mirror of
https://github.com/opnsense/src.git
synced 2026-04-25 16:18:54 -04:00
Mainly focus on files that use BSD 2-Clause license, however the tool I was using misidentified many licenses so this was mostly a manual - error prone - task. The Software Package Data Exchange (SPDX) group provides a specification to make it easier for automated tools to detect and summarize well known opensource licenses. We are gradually adopting the specification, noting that the tags are considered only advisory and do not, in any way, superceed or replace the license texts.
1543 lines
41 KiB
C
1543 lines
41 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* Copyright (c) 2003-2012 Broadcom Corporation
|
|
* All Rights Reserved
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
#include <sys/endian.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/param.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/limits.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/module.h>
|
|
#include <sys/socket.h>
|
|
#define __RMAN_RESOURCE_VISIBLE
|
|
#include <sys/rman.h>
|
|
#include <sys/taskqueue.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_var.h>
|
|
#include <net/if_arp.h>
|
|
#include <net/ethernet.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/if_media.h>
|
|
#include <net/bpf.h>
|
|
#include <net/if_types.h>
|
|
#include <net/if_vlan_var.h>
|
|
|
|
#include <dev/pci/pcivar.h>
|
|
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip.h>
|
|
|
|
#include <vm/vm.h>
|
|
#include <vm/pmap.h>
|
|
#include <vm/uma.h>
|
|
|
|
#include <machine/reg.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/mips_opcode.h>
|
|
#include <machine/asm.h>
|
|
#include <machine/cpuregs.h>
|
|
|
|
#include <machine/intr_machdep.h>
|
|
#include <machine/clock.h> /* for DELAY */
|
|
#include <machine/bus.h>
|
|
#include <machine/resource.h>
|
|
#include <mips/nlm/hal/haldefs.h>
|
|
#include <mips/nlm/hal/iomap.h>
|
|
#include <mips/nlm/hal/mips-extns.h>
|
|
#include <mips/nlm/hal/cop2.h>
|
|
#include <mips/nlm/hal/fmn.h>
|
|
#include <mips/nlm/hal/sys.h>
|
|
#include <mips/nlm/hal/nae.h>
|
|
#include <mips/nlm/hal/mdio.h>
|
|
#include <mips/nlm/hal/sgmii.h>
|
|
#include <mips/nlm/hal/xaui.h>
|
|
#include <mips/nlm/hal/poe.h>
|
|
#include <ucore_app_bin.h>
|
|
#include <mips/nlm/hal/ucore_loader.h>
|
|
#include <mips/nlm/xlp.h>
|
|
#include <mips/nlm/board.h>
|
|
#include <mips/nlm/msgring.h>
|
|
|
|
#include <dev/mii/mii.h>
|
|
#include <dev/mii/miivar.h>
|
|
#include "miidevs.h"
|
|
#include <dev/mii/brgphyreg.h>
|
|
#include "miibus_if.h"
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <mips/nlm/dev/net/xlpge.h>
|
|
|
|
/*#define XLP_DRIVER_LOOPBACK*/
|
|
|
|
static struct nae_port_config nae_port_config[64];
|
|
|
|
int poe_cl_tbl[MAX_POE_CLASSES] = {
|
|
0x0, 0x249249,
|
|
0x492492, 0x6db6db,
|
|
0x924924, 0xb6db6d,
|
|
0xdb6db6, 0xffffff
|
|
};
|
|
|
|
/* #define DUMP_PACKET */
|
|
|
|
static uint64_t
|
|
nlm_paddr_ld(uint64_t paddr)
|
|
{
|
|
uint64_t xkaddr = 0x9800000000000000 | paddr;
|
|
|
|
return (nlm_load_dword_daddr(xkaddr));
|
|
}
|
|
|
|
struct nlm_xlp_portdata ifp_ports[64];
|
|
static uma_zone_t nl_tx_desc_zone;
|
|
|
|
/* This implementation will register the following tree of device
|
|
* registration:
|
|
* pcibus
|
|
* |
|
|
* xlpnae (1 instance - virtual entity)
|
|
* |
|
|
* xlpge
|
|
* (18 sgmii / 4 xaui / 2 interlaken instances)
|
|
* |
|
|
* miibus
|
|
*/
|
|
|
|
static int nlm_xlpnae_probe(device_t);
|
|
static int nlm_xlpnae_attach(device_t);
|
|
static int nlm_xlpnae_detach(device_t);
|
|
static int nlm_xlpnae_suspend(device_t);
|
|
static int nlm_xlpnae_resume(device_t);
|
|
static int nlm_xlpnae_shutdown(device_t);
|
|
|
|
static device_method_t nlm_xlpnae_methods[] = {
|
|
/* Methods from the device interface */
|
|
DEVMETHOD(device_probe, nlm_xlpnae_probe),
|
|
DEVMETHOD(device_attach, nlm_xlpnae_attach),
|
|
DEVMETHOD(device_detach, nlm_xlpnae_detach),
|
|
DEVMETHOD(device_suspend, nlm_xlpnae_suspend),
|
|
DEVMETHOD(device_resume, nlm_xlpnae_resume),
|
|
DEVMETHOD(device_shutdown, nlm_xlpnae_shutdown),
|
|
|
|
DEVMETHOD(bus_driver_added, bus_generic_driver_added),
|
|
|
|
DEVMETHOD_END
|
|
};
|
|
|
|
static driver_t nlm_xlpnae_driver = {
|
|
"xlpnae",
|
|
nlm_xlpnae_methods,
|
|
sizeof(struct nlm_xlpnae_softc)
|
|
};
|
|
|
|
static devclass_t nlm_xlpnae_devclass;
|
|
|
|
static int nlm_xlpge_probe(device_t);
|
|
static int nlm_xlpge_attach(device_t);
|
|
static int nlm_xlpge_detach(device_t);
|
|
static int nlm_xlpge_suspend(device_t);
|
|
static int nlm_xlpge_resume(device_t);
|
|
static int nlm_xlpge_shutdown(device_t);
|
|
|
|
/* mii override functions */
|
|
static int nlm_xlpge_mii_read(device_t, int, int);
|
|
static int nlm_xlpge_mii_write(device_t, int, int, int);
|
|
static void nlm_xlpge_mii_statchg(device_t);
|
|
|
|
static device_method_t nlm_xlpge_methods[] = {
|
|
/* Methods from the device interface */
|
|
DEVMETHOD(device_probe, nlm_xlpge_probe),
|
|
DEVMETHOD(device_attach, nlm_xlpge_attach),
|
|
DEVMETHOD(device_detach, nlm_xlpge_detach),
|
|
DEVMETHOD(device_suspend, nlm_xlpge_suspend),
|
|
DEVMETHOD(device_resume, nlm_xlpge_resume),
|
|
DEVMETHOD(device_shutdown, nlm_xlpge_shutdown),
|
|
|
|
/* Methods from the nexus bus needed for explicitly
|
|
* probing children when driver is loaded as a kernel module
|
|
*/
|
|
DEVMETHOD(miibus_readreg, nlm_xlpge_mii_read),
|
|
DEVMETHOD(miibus_writereg, nlm_xlpge_mii_write),
|
|
DEVMETHOD(miibus_statchg, nlm_xlpge_mii_statchg),
|
|
|
|
/* Terminate method list */
|
|
DEVMETHOD_END
|
|
};
|
|
|
|
static driver_t nlm_xlpge_driver = {
|
|
"xlpge",
|
|
nlm_xlpge_methods,
|
|
sizeof(struct nlm_xlpge_softc)
|
|
};
|
|
|
|
static devclass_t nlm_xlpge_devclass;
|
|
|
|
DRIVER_MODULE(xlpnae, pci, nlm_xlpnae_driver, nlm_xlpnae_devclass, 0, 0);
|
|
DRIVER_MODULE(xlpge, xlpnae, nlm_xlpge_driver, nlm_xlpge_devclass, 0, 0);
|
|
DRIVER_MODULE(miibus, xlpge, miibus_driver, miibus_devclass, 0, 0);
|
|
|
|
MODULE_DEPEND(pci, xlpnae, 1, 1, 1);
|
|
MODULE_DEPEND(xlpnae, xlpge, 1, 1, 1);
|
|
MODULE_DEPEND(xlpge, ether, 1, 1, 1);
|
|
MODULE_DEPEND(xlpge, miibus, 1, 1, 1);
|
|
|
|
#define SGMII_RCV_CONTEXT_WIDTH 8
|
|
|
|
/* prototypes */
|
|
static void nlm_xlpge_msgring_handler(int vc, int size,
|
|
int code, int srcid, struct nlm_fmn_msg *msg, void *data);
|
|
static void nlm_xlpge_submit_rx_free_desc(struct nlm_xlpge_softc *sc, int num);
|
|
static void nlm_xlpge_init(void *addr);
|
|
static void nlm_xlpge_port_disable(struct nlm_xlpge_softc *sc);
|
|
static void nlm_xlpge_port_enable(struct nlm_xlpge_softc *sc);
|
|
|
|
/* globals */
|
|
int dbg_on = 1;
|
|
int cntx2port[524];
|
|
|
|
static __inline void
|
|
atomic_incr_long(unsigned long *addr)
|
|
{
|
|
atomic_add_long(addr, 1);
|
|
}
|
|
|
|
/*
|
|
* xlpnae driver implementation
|
|
*/
|
|
static int
|
|
nlm_xlpnae_probe(device_t dev)
|
|
{
|
|
if (pci_get_vendor(dev) != PCI_VENDOR_NETLOGIC ||
|
|
pci_get_device(dev) != PCI_DEVICE_ID_NLM_NAE)
|
|
return (ENXIO);
|
|
|
|
return (BUS_PROBE_DEFAULT);
|
|
}
|
|
|
|
static void
|
|
nlm_xlpnae_print_frin_desc_carving(struct nlm_xlpnae_softc *sc)
|
|
{
|
|
int intf;
|
|
uint32_t value;
|
|
int start, size;
|
|
|
|
/* XXXJC: use max_ports instead of 20 ? */
|
|
for (intf = 0; intf < 20; intf++) {
|
|
nlm_write_nae_reg(sc->base, NAE_FREE_IN_FIFO_CFG,
|
|
(0x80000000 | intf));
|
|
value = nlm_read_nae_reg(sc->base, NAE_FREE_IN_FIFO_CFG);
|
|
size = 2 * ((value >> 20) & 0x3ff);
|
|
start = 2 * ((value >> 8) & 0x1ff);
|
|
}
|
|
}
|
|
|
|
static void
|
|
nlm_config_egress(struct nlm_xlpnae_softc *sc, int nblock,
|
|
int context_base, int hwport, int max_channels)
|
|
{
|
|
int offset, num_channels;
|
|
uint32_t data;
|
|
|
|
num_channels = sc->portcfg[hwport].num_channels;
|
|
|
|
data = (2048 << 12) | (hwport << 4) | 1;
|
|
nlm_write_nae_reg(sc->base, NAE_TX_IF_BURSTMAX_CMD, data);
|
|
|
|
data = ((context_base + num_channels - 1) << 22) |
|
|
(context_base << 12) | (hwport << 4) | 1;
|
|
nlm_write_nae_reg(sc->base, NAE_TX_DDR_ACTVLIST_CMD, data);
|
|
|
|
config_egress_fifo_carvings(sc->base, hwport,
|
|
context_base, num_channels, max_channels, sc->portcfg);
|
|
config_egress_fifo_credits(sc->base, hwport,
|
|
context_base, num_channels, max_channels, sc->portcfg);
|
|
|
|
data = nlm_read_nae_reg(sc->base, NAE_DMA_TX_CREDIT_TH);
|
|
data |= (1 << 25) | (1 << 24);
|
|
nlm_write_nae_reg(sc->base, NAE_DMA_TX_CREDIT_TH, data);
|
|
|
|
for (offset = 0; offset < num_channels; offset++) {
|
|
nlm_write_nae_reg(sc->base, NAE_TX_SCHED_MAP_CMD1,
|
|
NAE_DRR_QUANTA);
|
|
data = (hwport << 15) | ((context_base + offset) << 5);
|
|
if (sc->cmplx_type[nblock] == ILC)
|
|
data |= (offset << 20);
|
|
nlm_write_nae_reg(sc->base, NAE_TX_SCHED_MAP_CMD0, data | 1);
|
|
nlm_write_nae_reg(sc->base, NAE_TX_SCHED_MAP_CMD0, data);
|
|
}
|
|
}
|
|
|
|
static int
|
|
xlpnae_get_maxchannels(struct nlm_xlpnae_softc *sc)
|
|
{
|
|
int maxchans = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < sc->max_ports; i++) {
|
|
if (sc->portcfg[i].type == UNKNOWN)
|
|
continue;
|
|
maxchans += sc->portcfg[i].num_channels;
|
|
}
|
|
|
|
return (maxchans);
|
|
}
|
|
|
|
static void
|
|
nlm_setup_interface(struct nlm_xlpnae_softc *sc, int nblock,
|
|
int port, uint32_t cur_flow_base, uint32_t flow_mask,
|
|
int max_channels, int context)
|
|
{
|
|
uint64_t nae_base = sc->base;
|
|
int mtu = 1536; /* XXXJC: don't hard code */
|
|
uint32_t ucore_mask;
|
|
|
|
if (sc->cmplx_type[nblock] == XAUIC)
|
|
nlm_config_xaui(nae_base, nblock, mtu,
|
|
mtu, sc->portcfg[port].vlan_pri_en);
|
|
nlm_config_freein_fifo_uniq_cfg(nae_base,
|
|
port, sc->portcfg[port].free_desc_sizes);
|
|
nlm_config_ucore_iface_mask_cfg(nae_base,
|
|
port, sc->portcfg[port].ucore_mask);
|
|
|
|
nlm_program_flow_cfg(nae_base, port, cur_flow_base, flow_mask);
|
|
|
|
if (sc->cmplx_type[nblock] == SGMIIC)
|
|
nlm_configure_sgmii_interface(nae_base, nblock, port, mtu, 0);
|
|
|
|
nlm_config_egress(sc, nblock, context, port, max_channels);
|
|
|
|
nlm_nae_init_netior(nae_base, sc->nblocks);
|
|
nlm_nae_open_if(nae_base, nblock, sc->cmplx_type[nblock], port,
|
|
sc->portcfg[port].free_desc_sizes);
|
|
|
|
/* XXXJC: check mask calculation */
|
|
ucore_mask = (1 << sc->nucores) - 1;
|
|
nlm_nae_init_ucore(nae_base, port, ucore_mask);
|
|
}
|
|
|
|
static void
|
|
nlm_setup_interfaces(struct nlm_xlpnae_softc *sc)
|
|
{
|
|
uint64_t nae_base;
|
|
uint32_t cur_slot, cur_slot_base;
|
|
uint32_t cur_flow_base, port, flow_mask;
|
|
int max_channels;
|
|
int i, context;
|
|
|
|
cur_slot = 0;
|
|
cur_slot_base = 0;
|
|
cur_flow_base = 0;
|
|
nae_base = sc->base;
|
|
flow_mask = nlm_get_flow_mask(sc->total_num_ports);
|
|
/* calculate max_channels */
|
|
max_channels = xlpnae_get_maxchannels(sc);
|
|
|
|
port = 0;
|
|
context = 0;
|
|
for (i = 0; i < sc->max_ports; i++) {
|
|
if (sc->portcfg[i].type == UNKNOWN)
|
|
continue;
|
|
nlm_setup_interface(sc, sc->portcfg[i].block, i, cur_flow_base,
|
|
flow_mask, max_channels, context);
|
|
cur_flow_base += sc->per_port_num_flows;
|
|
context += sc->portcfg[i].num_channels;
|
|
}
|
|
}
|
|
|
|
static void
|
|
nlm_xlpnae_init(int node, struct nlm_xlpnae_softc *sc)
|
|
{
|
|
uint64_t nae_base;
|
|
uint32_t ucoremask = 0;
|
|
uint32_t val;
|
|
int i;
|
|
|
|
nae_base = sc->base;
|
|
|
|
nlm_nae_flush_free_fifo(nae_base, sc->nblocks);
|
|
nlm_deflate_frin_fifo_carving(nae_base, sc->max_ports);
|
|
nlm_reset_nae(node);
|
|
|
|
for (i = 0; i < sc->nucores; i++) /* XXXJC: code repeated below */
|
|
ucoremask |= (0x1 << i);
|
|
printf("Loading 0x%x ucores with microcode\n", ucoremask);
|
|
nlm_ucore_load_all(nae_base, ucoremask, 1);
|
|
|
|
val = nlm_set_device_frequency(node, DFS_DEVICE_NAE, sc->freq);
|
|
printf("Setup NAE frequency to %dMHz\n", val);
|
|
|
|
nlm_mdio_reset_all(nae_base);
|
|
|
|
printf("Initialze SGMII PCS for blocks 0x%x\n", sc->sgmiimask);
|
|
nlm_sgmii_pcs_init(nae_base, sc->sgmiimask);
|
|
|
|
printf("Initialze XAUI PCS for blocks 0x%x\n", sc->xauimask);
|
|
nlm_xaui_pcs_init(nae_base, sc->xauimask);
|
|
|
|
/* clear NETIOR soft reset */
|
|
nlm_write_nae_reg(nae_base, NAE_LANE_CFG_SOFTRESET, 0x0);
|
|
|
|
/* Disable RX enable bit in RX_CONFIG */
|
|
val = nlm_read_nae_reg(nae_base, NAE_RX_CONFIG);
|
|
val &= 0xfffffffe;
|
|
nlm_write_nae_reg(nae_base, NAE_RX_CONFIG, val);
|
|
|
|
if (nlm_is_xlp8xx_ax() == 0) {
|
|
val = nlm_read_nae_reg(nae_base, NAE_TX_CONFIG);
|
|
val &= ~(1 << 3);
|
|
nlm_write_nae_reg(nae_base, NAE_TX_CONFIG, val);
|
|
}
|
|
|
|
nlm_setup_poe_class_config(nae_base, MAX_POE_CLASSES,
|
|
sc->ncontexts, poe_cl_tbl);
|
|
|
|
nlm_setup_vfbid_mapping(nae_base);
|
|
|
|
nlm_setup_flow_crc_poly(nae_base, sc->flow_crc_poly);
|
|
|
|
nlm_setup_rx_cal_cfg(nae_base, sc->max_ports, sc->portcfg);
|
|
/* note: xlp8xx Ax does not have Tx Calendering */
|
|
if (!nlm_is_xlp8xx_ax())
|
|
nlm_setup_tx_cal_cfg(nae_base, sc->max_ports, sc->portcfg);
|
|
|
|
nlm_setup_interfaces(sc);
|
|
nlm_config_poe(sc->poe_base, sc->poedv_base);
|
|
|
|
if (sc->hw_parser_en)
|
|
nlm_enable_hardware_parser(nae_base);
|
|
|
|
if (sc->prepad_en)
|
|
nlm_prepad_enable(nae_base, sc->prepad_size);
|
|
|
|
if (sc->ieee_1588_en)
|
|
nlm_setup_1588_timer(sc->base, sc->portcfg);
|
|
}
|
|
|
|
static void
|
|
nlm_xlpnae_update_pde(void *dummy __unused)
|
|
{
|
|
struct nlm_xlpnae_softc *sc;
|
|
uint32_t dv[NUM_WORDS_PER_DV];
|
|
device_t dev;
|
|
int vec;
|
|
|
|
dev = devclass_get_device(devclass_find("xlpnae"), 0);
|
|
sc = device_get_softc(dev);
|
|
|
|
nlm_write_poe_reg(sc->poe_base, POE_DISTR_EN, 0);
|
|
for (vec = 0; vec < NUM_DIST_VEC; vec++) {
|
|
if (nlm_get_poe_distvec(vec, dv) != 0)
|
|
continue;
|
|
|
|
nlm_write_poe_distvec(sc->poedv_base, vec, dv);
|
|
}
|
|
nlm_write_poe_reg(sc->poe_base, POE_DISTR_EN, 1);
|
|
}
|
|
|
|
SYSINIT(nlm_xlpnae_update_pde, SI_SUB_SMP, SI_ORDER_ANY,
|
|
nlm_xlpnae_update_pde, NULL);
|
|
|
|
/* configuration common for sgmii, xaui, ilaken goes here */
|
|
static void
|
|
nlm_setup_portcfg(struct nlm_xlpnae_softc *sc, struct xlp_nae_ivars *naep,
|
|
int block, int port)
|
|
{
|
|
int i;
|
|
uint32_t ucore_mask = 0;
|
|
struct xlp_block_ivars *bp;
|
|
struct xlp_port_ivars *p;
|
|
|
|
bp = &(naep->block_ivars[block]);
|
|
p = &(bp->port_ivars[port & 0x3]);
|
|
|
|
sc->portcfg[port].node = p->node;
|
|
sc->portcfg[port].block = p->block;
|
|
sc->portcfg[port].port = p->port;
|
|
sc->portcfg[port].type = p->type;
|
|
sc->portcfg[port].mdio_bus = p->mdio_bus;
|
|
sc->portcfg[port].phy_addr = p->phy_addr;
|
|
sc->portcfg[port].loopback_mode = p->loopback_mode;
|
|
sc->portcfg[port].num_channels = p->num_channels;
|
|
if (p->free_desc_sizes != MCLBYTES) {
|
|
printf("[%d, %d] Error: free_desc_sizes %d != %d\n",
|
|
block, port, p->free_desc_sizes, MCLBYTES);
|
|
return;
|
|
}
|
|
sc->portcfg[port].free_desc_sizes = p->free_desc_sizes;
|
|
for (i = 0; i < sc->nucores; i++) /* XXXJC: configure this */
|
|
ucore_mask |= (0x1 << i);
|
|
sc->portcfg[port].ucore_mask = ucore_mask;
|
|
sc->portcfg[port].vlan_pri_en = p->vlan_pri_en;
|
|
sc->portcfg[port].num_free_descs = p->num_free_descs;
|
|
sc->portcfg[port].iface_fifo_size = p->iface_fifo_size;
|
|
sc->portcfg[port].rxbuf_size = p->rxbuf_size;
|
|
sc->portcfg[port].rx_slots_reqd = p->rx_slots_reqd;
|
|
sc->portcfg[port].tx_slots_reqd = p->tx_slots_reqd;
|
|
sc->portcfg[port].pseq_fifo_size = p->pseq_fifo_size;
|
|
|
|
sc->portcfg[port].stg2_fifo_size = p->stg2_fifo_size;
|
|
sc->portcfg[port].eh_fifo_size = p->eh_fifo_size;
|
|
sc->portcfg[port].frout_fifo_size = p->frout_fifo_size;
|
|
sc->portcfg[port].ms_fifo_size = p->ms_fifo_size;
|
|
sc->portcfg[port].pkt_fifo_size = p->pkt_fifo_size;
|
|
sc->portcfg[port].pktlen_fifo_size = p->pktlen_fifo_size;
|
|
sc->portcfg[port].max_stg2_offset = p->max_stg2_offset;
|
|
sc->portcfg[port].max_eh_offset = p->max_eh_offset;
|
|
sc->portcfg[port].max_frout_offset = p->max_frout_offset;
|
|
sc->portcfg[port].max_ms_offset = p->max_ms_offset;
|
|
sc->portcfg[port].max_pmem_offset = p->max_pmem_offset;
|
|
sc->portcfg[port].stg1_2_credit = p->stg1_2_credit;
|
|
sc->portcfg[port].stg2_eh_credit = p->stg2_eh_credit;
|
|
sc->portcfg[port].stg2_frout_credit = p->stg2_frout_credit;
|
|
sc->portcfg[port].stg2_ms_credit = p->stg2_ms_credit;
|
|
sc->portcfg[port].ieee1588_inc_intg = p->ieee1588_inc_intg;
|
|
sc->portcfg[port].ieee1588_inc_den = p->ieee1588_inc_den;
|
|
sc->portcfg[port].ieee1588_inc_num = p->ieee1588_inc_num;
|
|
sc->portcfg[port].ieee1588_userval = p->ieee1588_userval;
|
|
sc->portcfg[port].ieee1588_ptpoff = p->ieee1588_ptpoff;
|
|
sc->portcfg[port].ieee1588_tmr1 = p->ieee1588_tmr1;
|
|
sc->portcfg[port].ieee1588_tmr2 = p->ieee1588_tmr2;
|
|
sc->portcfg[port].ieee1588_tmr3 = p->ieee1588_tmr3;
|
|
|
|
sc->total_free_desc += sc->portcfg[port].free_desc_sizes;
|
|
sc->total_num_ports++;
|
|
}
|
|
|
|
static int
|
|
nlm_xlpnae_attach(device_t dev)
|
|
{
|
|
struct xlp_nae_ivars *nae_ivars;
|
|
struct nlm_xlpnae_softc *sc;
|
|
device_t tmpd;
|
|
uint32_t dv[NUM_WORDS_PER_DV];
|
|
int port, i, j, nchan, nblock, node, qstart, qnum;
|
|
int offset, context, txq_base, rxvcbase;
|
|
uint64_t poe_pcibase, nae_pcibase;
|
|
|
|
node = pci_get_slot(dev) / 8;
|
|
nae_ivars = &xlp_board_info.nodes[node].nae_ivars;
|
|
|
|
sc = device_get_softc(dev);
|
|
sc->xlpnae_dev = dev;
|
|
sc->node = nae_ivars->node;
|
|
sc->base = nlm_get_nae_regbase(sc->node);
|
|
sc->poe_base = nlm_get_poe_regbase(sc->node);
|
|
sc->poedv_base = nlm_get_poedv_regbase(sc->node);
|
|
sc->portcfg = nae_port_config;
|
|
sc->blockmask = nae_ivars->blockmask;
|
|
sc->ilmask = nae_ivars->ilmask;
|
|
sc->xauimask = nae_ivars->xauimask;
|
|
sc->sgmiimask = nae_ivars->sgmiimask;
|
|
sc->nblocks = nae_ivars->nblocks;
|
|
sc->freq = nae_ivars->freq;
|
|
|
|
/* flow table generation is done by CRC16 polynomial */
|
|
sc->flow_crc_poly = nae_ivars->flow_crc_poly;
|
|
|
|
sc->hw_parser_en = nae_ivars->hw_parser_en;
|
|
sc->prepad_en = nae_ivars->prepad_en;
|
|
sc->prepad_size = nae_ivars->prepad_size;
|
|
sc->ieee_1588_en = nae_ivars->ieee_1588_en;
|
|
|
|
nae_pcibase = nlm_get_nae_pcibase(sc->node);
|
|
sc->ncontexts = nlm_read_reg(nae_pcibase, XLP_PCI_DEVINFO_REG5);
|
|
sc->nucores = nlm_num_uengines(nae_pcibase);
|
|
|
|
for (nblock = 0; nblock < sc->nblocks; nblock++) {
|
|
sc->cmplx_type[nblock] = nae_ivars->block_ivars[nblock].type;
|
|
sc->portmask[nblock] = nae_ivars->block_ivars[nblock].portmask;
|
|
}
|
|
|
|
for (i = 0; i < sc->ncontexts; i++)
|
|
cntx2port[i] = 18; /* 18 is an invalid port */
|
|
|
|
if (sc->nblocks == 5)
|
|
sc->max_ports = 18; /* 8xx has a block 4 with 2 ports */
|
|
else
|
|
sc->max_ports = sc->nblocks * PORTS_PER_CMPLX;
|
|
|
|
for (i = 0; i < sc->max_ports; i++)
|
|
sc->portcfg[i].type = UNKNOWN; /* Port Not Present */
|
|
/*
|
|
* Now setup all internal fifo carvings based on
|
|
* total number of ports in the system
|
|
*/
|
|
sc->total_free_desc = 0;
|
|
sc->total_num_ports = 0;
|
|
port = 0;
|
|
context = 0;
|
|
txq_base = nlm_qidstart(nae_pcibase);
|
|
rxvcbase = txq_base + sc->ncontexts;
|
|
for (i = 0; i < sc->nblocks; i++) {
|
|
uint32_t portmask;
|
|
|
|
if ((nae_ivars->blockmask & (1 << i)) == 0) {
|
|
port += 4;
|
|
continue;
|
|
}
|
|
portmask = nae_ivars->block_ivars[i].portmask;
|
|
for (j = 0; j < PORTS_PER_CMPLX; j++, port++) {
|
|
if ((portmask & (1 << j)) == 0)
|
|
continue;
|
|
nlm_setup_portcfg(sc, nae_ivars, i, port);
|
|
nchan = sc->portcfg[port].num_channels;
|
|
for (offset = 0; offset < nchan; offset++)
|
|
cntx2port[context + offset] = port;
|
|
sc->portcfg[port].txq = txq_base + context;
|
|
sc->portcfg[port].rxfreeq = rxvcbase + port;
|
|
context += nchan;
|
|
}
|
|
}
|
|
|
|
poe_pcibase = nlm_get_poe_pcibase(sc->node);
|
|
sc->per_port_num_flows =
|
|
nlm_poe_max_flows(poe_pcibase) / sc->total_num_ports;
|
|
|
|
/* zone for P2P descriptors */
|
|
nl_tx_desc_zone = uma_zcreate("NL Tx Desc",
|
|
sizeof(struct xlpge_tx_desc), NULL, NULL, NULL, NULL,
|
|
NAE_CACHELINE_SIZE, 0);
|
|
|
|
/* NAE FMN messages have CMS src station id's in the
|
|
* range of qstart to qnum.
|
|
*/
|
|
qstart = nlm_qidstart(nae_pcibase);
|
|
qnum = nlm_qnum(nae_pcibase);
|
|
if (register_msgring_handler(qstart, qstart + qnum - 1,
|
|
nlm_xlpge_msgring_handler, sc)) {
|
|
panic("Couldn't register NAE msgring handler\n");
|
|
}
|
|
|
|
/* POE FMN messages have CMS src station id's in the
|
|
* range of qstart to qnum.
|
|
*/
|
|
qstart = nlm_qidstart(poe_pcibase);
|
|
qnum = nlm_qnum(poe_pcibase);
|
|
if (register_msgring_handler(qstart, qstart + qnum - 1,
|
|
nlm_xlpge_msgring_handler, sc)) {
|
|
panic("Couldn't register POE msgring handler\n");
|
|
}
|
|
|
|
nlm_xlpnae_init(node, sc);
|
|
|
|
for (i = 0; i < sc->max_ports; i++) {
|
|
char desc[32];
|
|
int block, port;
|
|
|
|
if (sc->portcfg[i].type == UNKNOWN)
|
|
continue;
|
|
block = sc->portcfg[i].block;
|
|
port = sc->portcfg[i].port;
|
|
tmpd = device_add_child(dev, "xlpge", i);
|
|
device_set_ivars(tmpd,
|
|
&(nae_ivars->block_ivars[block].port_ivars[port]));
|
|
sprintf(desc, "XLP NAE Port %d,%d", block, port);
|
|
device_set_desc_copy(tmpd, desc);
|
|
}
|
|
nlm_setup_iface_fifo_cfg(sc->base, sc->max_ports, sc->portcfg);
|
|
nlm_setup_rx_base_config(sc->base, sc->max_ports, sc->portcfg);
|
|
nlm_setup_rx_buf_config(sc->base, sc->max_ports, sc->portcfg);
|
|
nlm_setup_freein_fifo_cfg(sc->base, sc->portcfg);
|
|
nlm_program_nae_parser_seq_fifo(sc->base, sc->max_ports, sc->portcfg);
|
|
|
|
nlm_xlpnae_print_frin_desc_carving(sc);
|
|
bus_generic_probe(dev);
|
|
bus_generic_attach(dev);
|
|
|
|
/*
|
|
* Enable only boot cpu at this point, full distribution comes
|
|
* only after SMP is started
|
|
*/
|
|
nlm_write_poe_reg(sc->poe_base, POE_DISTR_EN, 0);
|
|
nlm_calc_poe_distvec(0x1, 0, 0, 0, 0x1 << XLPGE_RX_VC, dv);
|
|
nlm_write_poe_distvec(sc->poedv_base, 0, dv);
|
|
nlm_write_poe_reg(sc->poe_base, POE_DISTR_EN, 1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
nlm_xlpnae_detach(device_t dev)
|
|
{
|
|
/* TODO - free zone here */
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
nlm_xlpnae_suspend(device_t dev)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
nlm_xlpnae_resume(device_t dev)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
nlm_xlpnae_shutdown(device_t dev)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* xlpge driver implementation
|
|
*/
|
|
|
|
static void
|
|
nlm_xlpge_mac_set_rx_mode(struct nlm_xlpge_softc *sc)
|
|
{
|
|
if (sc->if_flags & IFF_PROMISC) {
|
|
if (sc->type == SGMIIC)
|
|
nlm_nae_setup_rx_mode_sgmii(sc->base_addr,
|
|
sc->block, sc->port, sc->type, 1 /* broadcast */,
|
|
1/* multicast */, 0 /* pause */, 1 /* promisc */);
|
|
else
|
|
nlm_nae_setup_rx_mode_xaui(sc->base_addr,
|
|
sc->block, sc->port, sc->type, 1 /* broadcast */,
|
|
1/* multicast */, 0 /* pause */, 1 /* promisc */);
|
|
} else {
|
|
if (sc->type == SGMIIC)
|
|
nlm_nae_setup_rx_mode_sgmii(sc->base_addr,
|
|
sc->block, sc->port, sc->type, 1 /* broadcast */,
|
|
1/* multicast */, 0 /* pause */, 0 /* promisc */);
|
|
else
|
|
nlm_nae_setup_rx_mode_xaui(sc->base_addr,
|
|
sc->block, sc->port, sc->type, 1 /* broadcast */,
|
|
1/* multicast */, 0 /* pause */, 0 /* promisc */);
|
|
}
|
|
}
|
|
|
|
static int
|
|
nlm_xlpge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
|
|
{
|
|
struct mii_data *mii;
|
|
struct nlm_xlpge_softc *sc;
|
|
struct ifreq *ifr;
|
|
int error;
|
|
|
|
sc = ifp->if_softc;
|
|
error = 0;
|
|
ifr = (struct ifreq *)data;
|
|
|
|
switch (command) {
|
|
case SIOCSIFFLAGS:
|
|
XLPGE_LOCK(sc);
|
|
sc->if_flags = ifp->if_flags;
|
|
if (ifp->if_flags & IFF_UP) {
|
|
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
|
|
nlm_xlpge_init(sc);
|
|
else
|
|
nlm_xlpge_port_enable(sc);
|
|
nlm_xlpge_mac_set_rx_mode(sc);
|
|
sc->link = NLM_LINK_UP;
|
|
} else {
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
|
|
nlm_xlpge_port_disable(sc);
|
|
sc->link = NLM_LINK_DOWN;
|
|
}
|
|
XLPGE_UNLOCK(sc);
|
|
error = 0;
|
|
break;
|
|
case SIOCGIFMEDIA:
|
|
case SIOCSIFMEDIA:
|
|
if (sc->mii_bus != NULL) {
|
|
mii = device_get_softc(sc->mii_bus);
|
|
error = ifmedia_ioctl(ifp, ifr, &mii->mii_media,
|
|
command);
|
|
}
|
|
break;
|
|
default:
|
|
error = ether_ioctl(ifp, command, data);
|
|
break;
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
xlpge_tx(struct ifnet *ifp, struct mbuf *mbuf_chain)
|
|
{
|
|
struct nlm_fmn_msg msg;
|
|
struct xlpge_tx_desc *p2p;
|
|
struct nlm_xlpge_softc *sc;
|
|
struct mbuf *m;
|
|
vm_paddr_t paddr;
|
|
int fbid, dst, pos, err;
|
|
int ret = 0, tx_msgstatus, retries;
|
|
|
|
err = 0;
|
|
if (mbuf_chain == NULL)
|
|
return (0);
|
|
|
|
sc = ifp->if_softc;
|
|
p2p = NULL;
|
|
if (!(ifp->if_drv_flags & IFF_DRV_RUNNING) ||
|
|
ifp->if_drv_flags & IFF_DRV_OACTIVE) {
|
|
err = ENXIO;
|
|
goto fail;
|
|
}
|
|
|
|
/* free a few in coming messages on the fb vc */
|
|
xlp_handle_msg_vc(1 << XLPGE_FB_VC, 2);
|
|
|
|
/* vfb id table is setup to map cpu to vc 3 of the cpu */
|
|
fbid = nlm_cpuid();
|
|
dst = sc->txq;
|
|
|
|
pos = 0;
|
|
p2p = uma_zalloc(nl_tx_desc_zone, M_NOWAIT);
|
|
if (p2p == NULL) {
|
|
printf("alloc fail\n");
|
|
err = ENOBUFS;
|
|
goto fail;
|
|
}
|
|
|
|
for (m = mbuf_chain; m != NULL; m = m->m_next) {
|
|
vm_offset_t buf = (vm_offset_t) m->m_data;
|
|
int len = m->m_len;
|
|
int frag_sz;
|
|
uint64_t desc;
|
|
|
|
/*printf("m_data = %p len %d\n", m->m_data, len); */
|
|
while (len) {
|
|
if (pos == XLP_NTXFRAGS - 3) {
|
|
device_printf(sc->xlpge_dev,
|
|
"packet defrag %d\n",
|
|
m_length(mbuf_chain, NULL));
|
|
err = ENOBUFS; /* TODO fix error */
|
|
goto fail;
|
|
}
|
|
paddr = vtophys(buf);
|
|
frag_sz = PAGE_SIZE - (buf & PAGE_MASK);
|
|
if (len < frag_sz)
|
|
frag_sz = len;
|
|
desc = nae_tx_desc(P2D_NEOP, 0, 127,
|
|
frag_sz, paddr);
|
|
p2p->frag[pos] = htobe64(desc);
|
|
pos++;
|
|
len -= frag_sz;
|
|
buf += frag_sz;
|
|
}
|
|
}
|
|
|
|
KASSERT(pos != 0, ("Zero-length mbuf chain?\n"));
|
|
|
|
/* Make the last one P2D EOP */
|
|
p2p->frag[pos-1] |= htobe64((uint64_t)P2D_EOP << 62);
|
|
|
|
/* stash useful pointers in the desc */
|
|
p2p->frag[XLP_NTXFRAGS-3] = 0xf00bad;
|
|
p2p->frag[XLP_NTXFRAGS-2] = (uintptr_t)p2p;
|
|
p2p->frag[XLP_NTXFRAGS-1] = (uintptr_t)mbuf_chain;
|
|
|
|
paddr = vtophys(p2p);
|
|
msg.msg[0] = nae_tx_desc(P2P, 0, fbid, pos, paddr);
|
|
|
|
for (retries = 16; retries > 0; retries--) {
|
|
ret = nlm_fmn_msgsend(dst, 1, FMN_SWCODE_NAE, &msg);
|
|
if (ret == 0)
|
|
return (0);
|
|
}
|
|
|
|
fail:
|
|
if (ret != 0) {
|
|
tx_msgstatus = nlm_read_c2_txmsgstatus();
|
|
if ((tx_msgstatus >> 24) & 0x1)
|
|
device_printf(sc->xlpge_dev, "Transmit queue full - ");
|
|
if ((tx_msgstatus >> 3) & 0x1)
|
|
device_printf(sc->xlpge_dev, "ECC error - ");
|
|
if ((tx_msgstatus >> 2) & 0x1)
|
|
device_printf(sc->xlpge_dev, "Pending Sync - ");
|
|
if ((tx_msgstatus >> 1) & 0x1)
|
|
device_printf(sc->xlpge_dev,
|
|
"Insufficient input queue credits - ");
|
|
if (tx_msgstatus & 0x1)
|
|
device_printf(sc->xlpge_dev,
|
|
"Insufficient output queue credits - ");
|
|
}
|
|
device_printf(sc->xlpge_dev, "Send failed! err = %d\n", err);
|
|
if (p2p)
|
|
uma_zfree(nl_tx_desc_zone, p2p);
|
|
m_freem(mbuf_chain);
|
|
if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
|
|
return (err);
|
|
}
|
|
|
|
|
|
static int
|
|
nlm_xlpge_gmac_config_speed(struct nlm_xlpge_softc *sc)
|
|
{
|
|
struct mii_data *mii;
|
|
|
|
if (sc->type == XAUIC || sc->type == ILC)
|
|
return (0);
|
|
|
|
if (sc->mii_bus) {
|
|
mii = device_get_softc(sc->mii_bus);
|
|
mii_pollstat(mii);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
nlm_xlpge_port_disable(struct nlm_xlpge_softc *sc)
|
|
{
|
|
struct ifnet *ifp;
|
|
|
|
ifp = sc->xlpge_if;
|
|
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
|
|
|
|
callout_stop(&sc->xlpge_callout);
|
|
nlm_mac_disable(sc->base_addr, sc->block, sc->type, sc->port);
|
|
}
|
|
|
|
static void
|
|
nlm_mii_pollstat(void *arg)
|
|
{
|
|
struct nlm_xlpge_softc *sc = (struct nlm_xlpge_softc *)arg;
|
|
struct mii_data *mii = NULL;
|
|
|
|
if (sc->mii_bus) {
|
|
mii = device_get_softc(sc->mii_bus);
|
|
|
|
KASSERT(mii != NULL, ("mii ptr is NULL"));
|
|
|
|
mii_pollstat(mii);
|
|
|
|
callout_reset(&sc->xlpge_callout, hz,
|
|
nlm_mii_pollstat, sc);
|
|
}
|
|
}
|
|
|
|
static void
|
|
nlm_xlpge_port_enable(struct nlm_xlpge_softc *sc)
|
|
{
|
|
if ((sc->type != SGMIIC) && (sc->type != XAUIC))
|
|
return;
|
|
nlm_mac_enable(sc->base_addr, sc->block, sc->type, sc->port);
|
|
nlm_mii_pollstat((void *)sc);
|
|
}
|
|
|
|
static void
|
|
nlm_xlpge_init(void *addr)
|
|
{
|
|
struct nlm_xlpge_softc *sc;
|
|
struct ifnet *ifp;
|
|
struct mii_data *mii = NULL;
|
|
|
|
sc = (struct nlm_xlpge_softc *)addr;
|
|
ifp = sc->xlpge_if;
|
|
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
|
|
return;
|
|
|
|
if (sc->mii_bus) {
|
|
mii = device_get_softc(sc->mii_bus);
|
|
mii_mediachg(mii);
|
|
}
|
|
|
|
nlm_xlpge_gmac_config_speed(sc);
|
|
ifp->if_drv_flags |= IFF_DRV_RUNNING;
|
|
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
|
|
nlm_xlpge_port_enable(sc);
|
|
|
|
/* start the callout */
|
|
callout_reset(&sc->xlpge_callout, hz, nlm_mii_pollstat, sc);
|
|
}
|
|
|
|
/*
|
|
* Read the MAC address from FDT or board eeprom.
|
|
*/
|
|
static void
|
|
xlpge_read_mac_addr(struct nlm_xlpge_softc *sc)
|
|
{
|
|
|
|
xlpge_get_macaddr(sc->dev_addr);
|
|
/* last octet is port specific */
|
|
sc->dev_addr[5] += (sc->block * 4) + sc->port;
|
|
|
|
if (sc->type == SGMIIC)
|
|
nlm_nae_setup_mac_addr_sgmii(sc->base_addr, sc->block,
|
|
sc->port, sc->type, sc->dev_addr);
|
|
else if (sc->type == XAUIC)
|
|
nlm_nae_setup_mac_addr_xaui(sc->base_addr, sc->block,
|
|
sc->port, sc->type, sc->dev_addr);
|
|
}
|
|
|
|
|
|
static int
|
|
xlpge_mediachange(struct ifnet *ifp)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
xlpge_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
|
|
{
|
|
struct nlm_xlpge_softc *sc;
|
|
struct mii_data *md;
|
|
|
|
md = NULL;
|
|
sc = ifp->if_softc;
|
|
|
|
if (sc->mii_bus)
|
|
md = device_get_softc(sc->mii_bus);
|
|
|
|
ifmr->ifm_status = IFM_AVALID;
|
|
ifmr->ifm_active = IFM_ETHER;
|
|
|
|
if (sc->link == NLM_LINK_DOWN)
|
|
return;
|
|
|
|
if (md != NULL)
|
|
ifmr->ifm_active = md->mii_media.ifm_cur->ifm_media;
|
|
ifmr->ifm_status |= IFM_ACTIVE;
|
|
}
|
|
|
|
static int
|
|
nlm_xlpge_ifinit(struct nlm_xlpge_softc *sc)
|
|
{
|
|
struct ifnet *ifp;
|
|
device_t dev;
|
|
int port = sc->block * 4 + sc->port;
|
|
|
|
dev = sc->xlpge_dev;
|
|
ifp = sc->xlpge_if = if_alloc(IFT_ETHER);
|
|
/*(sc->network_sc)->ifp_ports[port].xlpge_if = ifp;*/
|
|
ifp_ports[port].xlpge_if = ifp;
|
|
|
|
if (ifp == NULL) {
|
|
device_printf(dev, "cannot if_alloc()\n");
|
|
return (ENOSPC);
|
|
}
|
|
ifp->if_softc = sc;
|
|
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
sc->if_flags = ifp->if_flags;
|
|
/*ifp->if_capabilities = IFCAP_TXCSUM | IFCAP_VLAN_HWTAGGING;*/
|
|
ifp->if_capabilities = 0;
|
|
ifp->if_capenable = ifp->if_capabilities;
|
|
ifp->if_ioctl = nlm_xlpge_ioctl;
|
|
ifp->if_init = nlm_xlpge_init ;
|
|
ifp->if_hwassist = 0;
|
|
ifp->if_snd.ifq_drv_maxlen = NLM_XLPGE_TXQ_SIZE; /* TODO: make this a sysint */
|
|
IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen);
|
|
IFQ_SET_READY(&ifp->if_snd);
|
|
|
|
ifmedia_init(&sc->xlpge_mii.mii_media, 0, xlpge_mediachange,
|
|
xlpge_mediastatus);
|
|
ifmedia_add(&sc->xlpge_mii.mii_media, IFM_ETHER | IFM_AUTO, 0, NULL);
|
|
ifmedia_set(&sc->xlpge_mii.mii_media, IFM_ETHER | IFM_AUTO);
|
|
sc->xlpge_mii.mii_media.ifm_media =
|
|
sc->xlpge_mii.mii_media.ifm_cur->ifm_media;
|
|
xlpge_read_mac_addr(sc);
|
|
|
|
ether_ifattach(ifp, sc->dev_addr);
|
|
|
|
/* override if_transmit : per ifnet(9), do it after if_attach */
|
|
ifp->if_transmit = xlpge_tx;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
nlm_xlpge_probe(device_t dev)
|
|
{
|
|
return (BUS_PROBE_DEFAULT);
|
|
}
|
|
|
|
static void *
|
|
get_buf(void)
|
|
{
|
|
struct mbuf *m_new;
|
|
uint64_t *md;
|
|
#ifdef INVARIANTS
|
|
vm_paddr_t temp1, temp2;
|
|
#endif
|
|
|
|
if ((m_new = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR)) == NULL)
|
|
return (NULL);
|
|
m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
|
|
KASSERT(((uintptr_t)m_new->m_data & (NAE_CACHELINE_SIZE - 1)) == 0,
|
|
("m_new->m_data is not cacheline aligned"));
|
|
md = (uint64_t *)m_new->m_data;
|
|
md[0] = (intptr_t)m_new; /* Back Ptr */
|
|
md[1] = 0xf00bad;
|
|
m_adj(m_new, NAE_CACHELINE_SIZE);
|
|
|
|
#ifdef INVARIANTS
|
|
temp1 = vtophys((vm_offset_t) m_new->m_data);
|
|
temp2 = vtophys((vm_offset_t) m_new->m_data + 1536);
|
|
KASSERT((temp1 + 1536) == temp2,
|
|
("Alloced buffer is not contiguous"));
|
|
#endif
|
|
return ((void *)m_new->m_data);
|
|
}
|
|
|
|
static void
|
|
nlm_xlpge_mii_init(device_t dev, struct nlm_xlpge_softc *sc)
|
|
{
|
|
int error;
|
|
|
|
error = mii_attach(dev, &sc->mii_bus, sc->xlpge_if,
|
|
xlpge_mediachange, xlpge_mediastatus,
|
|
BMSR_DEFCAPMASK, sc->phy_addr, MII_OFFSET_ANY, 0);
|
|
|
|
if (error) {
|
|
device_printf(dev, "attaching PHYs failed\n");
|
|
sc->mii_bus = NULL;
|
|
}
|
|
|
|
if (sc->mii_bus != NULL) {
|
|
/* enable MDIO interrupts in the PHY */
|
|
/* XXXJC: TODO */
|
|
}
|
|
}
|
|
|
|
static int
|
|
xlpge_stats_sysctl(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
struct nlm_xlpge_softc *sc;
|
|
uint32_t val;
|
|
int reg, field;
|
|
|
|
sc = arg1;
|
|
field = arg2;
|
|
reg = SGMII_STATS_MLR(sc->block, sc->port) + field;
|
|
val = nlm_read_nae_reg(sc->base_addr, reg);
|
|
return (sysctl_handle_int(oidp, &val, 0, req));
|
|
}
|
|
|
|
static void
|
|
nlm_xlpge_setup_stats_sysctl(device_t dev, struct nlm_xlpge_softc *sc)
|
|
{
|
|
struct sysctl_ctx_list *ctx;
|
|
struct sysctl_oid_list *child;
|
|
struct sysctl_oid *tree;
|
|
|
|
ctx = device_get_sysctl_ctx(dev);
|
|
tree = device_get_sysctl_tree(dev);
|
|
child = SYSCTL_CHILDREN(tree);
|
|
|
|
#define XLPGE_STAT(name, offset, desc) \
|
|
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, name, \
|
|
CTLTYPE_UINT | CTLFLAG_RD, sc, offset, \
|
|
xlpge_stats_sysctl, "IU", desc)
|
|
|
|
XLPGE_STAT("tr127", nlm_sgmii_stats_tr127, "TxRx 64 - 127 Bytes");
|
|
XLPGE_STAT("tr255", nlm_sgmii_stats_tr255, "TxRx 128 - 255 Bytes");
|
|
XLPGE_STAT("tr511", nlm_sgmii_stats_tr511, "TxRx 256 - 511 Bytes");
|
|
XLPGE_STAT("tr1k", nlm_sgmii_stats_tr1k, "TxRx 512 - 1023 Bytes");
|
|
XLPGE_STAT("trmax", nlm_sgmii_stats_trmax, "TxRx 1024 - 1518 Bytes");
|
|
XLPGE_STAT("trmgv", nlm_sgmii_stats_trmgv, "TxRx 1519 - 1522 Bytes");
|
|
|
|
XLPGE_STAT("rbyt", nlm_sgmii_stats_rbyt, "Rx Bytes");
|
|
XLPGE_STAT("rpkt", nlm_sgmii_stats_rpkt, "Rx Packets");
|
|
XLPGE_STAT("rfcs", nlm_sgmii_stats_rfcs, "Rx FCS Error");
|
|
XLPGE_STAT("rmca", nlm_sgmii_stats_rmca, "Rx Multicast Packets");
|
|
XLPGE_STAT("rbca", nlm_sgmii_stats_rbca, "Rx Broadcast Packets");
|
|
XLPGE_STAT("rxcf", nlm_sgmii_stats_rxcf, "Rx Control Frames");
|
|
XLPGE_STAT("rxpf", nlm_sgmii_stats_rxpf, "Rx Pause Frames");
|
|
XLPGE_STAT("rxuo", nlm_sgmii_stats_rxuo, "Rx Unknown Opcode");
|
|
XLPGE_STAT("raln", nlm_sgmii_stats_raln, "Rx Alignment Errors");
|
|
XLPGE_STAT("rflr", nlm_sgmii_stats_rflr, "Rx Framelength Errors");
|
|
XLPGE_STAT("rcde", nlm_sgmii_stats_rcde, "Rx Code Errors");
|
|
XLPGE_STAT("rcse", nlm_sgmii_stats_rcse, "Rx Carrier Sense Errors");
|
|
XLPGE_STAT("rund", nlm_sgmii_stats_rund, "Rx Undersize Packet Errors");
|
|
XLPGE_STAT("rovr", nlm_sgmii_stats_rovr, "Rx Oversize Packet Errors");
|
|
XLPGE_STAT("rfrg", nlm_sgmii_stats_rfrg, "Rx Fragments");
|
|
XLPGE_STAT("rjbr", nlm_sgmii_stats_rjbr, "Rx Jabber");
|
|
|
|
XLPGE_STAT("tbyt", nlm_sgmii_stats_tbyt, "Tx Bytes");
|
|
XLPGE_STAT("tpkt", nlm_sgmii_stats_tpkt, "Tx Packets");
|
|
XLPGE_STAT("tmca", nlm_sgmii_stats_tmca, "Tx Multicast Packets");
|
|
XLPGE_STAT("tbca", nlm_sgmii_stats_tbca, "Tx Broadcast Packets");
|
|
XLPGE_STAT("txpf", nlm_sgmii_stats_txpf, "Tx Pause Frame");
|
|
XLPGE_STAT("tdfr", nlm_sgmii_stats_tdfr, "Tx Deferral Packets");
|
|
XLPGE_STAT("tedf", nlm_sgmii_stats_tedf, "Tx Excessive Deferral Pkts");
|
|
XLPGE_STAT("tscl", nlm_sgmii_stats_tscl, "Tx Single Collisions");
|
|
XLPGE_STAT("tmcl", nlm_sgmii_stats_tmcl, "Tx Multiple Collisions");
|
|
XLPGE_STAT("tlcl", nlm_sgmii_stats_tlcl, "Tx Late Collision Pkts");
|
|
XLPGE_STAT("txcl", nlm_sgmii_stats_txcl, "Tx Excessive Collisions");
|
|
XLPGE_STAT("tncl", nlm_sgmii_stats_tncl, "Tx Total Collisions");
|
|
XLPGE_STAT("tjbr", nlm_sgmii_stats_tjbr, "Tx Jabber Frames");
|
|
XLPGE_STAT("tfcs", nlm_sgmii_stats_tfcs, "Tx FCS Errors");
|
|
XLPGE_STAT("txcf", nlm_sgmii_stats_txcf, "Tx Control Frames");
|
|
XLPGE_STAT("tovr", nlm_sgmii_stats_tovr, "Tx Oversize Frames");
|
|
XLPGE_STAT("tund", nlm_sgmii_stats_tund, "Tx Undersize Frames");
|
|
XLPGE_STAT("tfrg", nlm_sgmii_stats_tfrg, "Tx Fragments");
|
|
#undef XLPGE_STAT
|
|
}
|
|
|
|
static int
|
|
nlm_xlpge_attach(device_t dev)
|
|
{
|
|
struct xlp_port_ivars *pv;
|
|
struct nlm_xlpge_softc *sc;
|
|
int port;
|
|
|
|
pv = device_get_ivars(dev);
|
|
sc = device_get_softc(dev);
|
|
sc->xlpge_dev = dev;
|
|
sc->mii_bus = NULL;
|
|
sc->block = pv->block;
|
|
sc->node = pv->node;
|
|
sc->port = pv->port;
|
|
sc->type = pv->type;
|
|
sc->xlpge_if = NULL;
|
|
sc->phy_addr = pv->phy_addr;
|
|
sc->mdio_bus = pv->mdio_bus;
|
|
sc->portcfg = nae_port_config;
|
|
sc->hw_parser_en = pv->hw_parser_en;
|
|
|
|
/* default settings */
|
|
sc->speed = NLM_SGMII_SPEED_10;
|
|
sc->duplexity = NLM_SGMII_DUPLEX_FULL;
|
|
sc->link = NLM_LINK_DOWN;
|
|
sc->flowctrl = NLM_FLOWCTRL_DISABLED;
|
|
|
|
sc->network_sc = device_get_softc(device_get_parent(dev));
|
|
sc->base_addr = sc->network_sc->base;
|
|
sc->prepad_en = sc->network_sc->prepad_en;
|
|
sc->prepad_size = sc->network_sc->prepad_size;
|
|
|
|
callout_init(&sc->xlpge_callout, 1);
|
|
|
|
XLPGE_LOCK_INIT(sc, device_get_nameunit(dev));
|
|
|
|
port = (sc->block*4)+sc->port;
|
|
sc->nfree_desc = nae_port_config[port].num_free_descs;
|
|
sc->txq = nae_port_config[port].txq;
|
|
sc->rxfreeq = nae_port_config[port].rxfreeq;
|
|
|
|
nlm_xlpge_submit_rx_free_desc(sc, sc->nfree_desc);
|
|
if (sc->hw_parser_en)
|
|
nlm_enable_hardware_parser_per_port(sc->base_addr,
|
|
sc->block, sc->port);
|
|
|
|
nlm_xlpge_ifinit(sc);
|
|
ifp_ports[port].xlpge_sc = sc;
|
|
nlm_xlpge_mii_init(dev, sc);
|
|
|
|
nlm_xlpge_setup_stats_sysctl(dev, sc);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
nlm_xlpge_detach(device_t dev)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
nlm_xlpge_suspend(device_t dev)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
nlm_xlpge_resume(device_t dev)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
nlm_xlpge_shutdown(device_t dev)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* miibus function with custom implementation
|
|
*/
|
|
static int
|
|
nlm_xlpge_mii_read(device_t dev, int phyaddr, int regidx)
|
|
{
|
|
struct nlm_xlpge_softc *sc;
|
|
int val;
|
|
|
|
sc = device_get_softc(dev);
|
|
if (sc->type == SGMIIC)
|
|
val = nlm_gmac_mdio_read(sc->base_addr, sc->mdio_bus,
|
|
BLOCK_7, LANE_CFG, phyaddr, regidx);
|
|
else
|
|
val = 0xffff;
|
|
|
|
return (val);
|
|
}
|
|
|
|
static int
|
|
nlm_xlpge_mii_write(device_t dev, int phyaddr, int regidx, int val)
|
|
{
|
|
struct nlm_xlpge_softc *sc;
|
|
|
|
sc = device_get_softc(dev);
|
|
if (sc->type == SGMIIC)
|
|
nlm_gmac_mdio_write(sc->base_addr, sc->mdio_bus, BLOCK_7,
|
|
LANE_CFG, phyaddr, regidx, val);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
nlm_xlpge_mii_statchg(device_t dev)
|
|
{
|
|
struct nlm_xlpge_softc *sc;
|
|
struct mii_data *mii;
|
|
char *speed, *duplexity;
|
|
|
|
sc = device_get_softc(dev);
|
|
if (sc->mii_bus == NULL)
|
|
return;
|
|
|
|
mii = device_get_softc(sc->mii_bus);
|
|
if (mii->mii_media_status & IFM_ACTIVE) {
|
|
if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) {
|
|
sc->speed = NLM_SGMII_SPEED_10;
|
|
speed = "10Mbps";
|
|
} else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) {
|
|
sc->speed = NLM_SGMII_SPEED_100;
|
|
speed = "100Mbps";
|
|
} else { /* default to 1G */
|
|
sc->speed = NLM_SGMII_SPEED_1000;
|
|
speed = "1Gbps";
|
|
}
|
|
|
|
if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) {
|
|
sc->duplexity = NLM_SGMII_DUPLEX_FULL;
|
|
duplexity = "full";
|
|
} else {
|
|
sc->duplexity = NLM_SGMII_DUPLEX_HALF;
|
|
duplexity = "half";
|
|
}
|
|
|
|
printf("Port [%d, %d] setup with speed=%s duplex=%s\n",
|
|
sc->block, sc->port, speed, duplexity);
|
|
|
|
nlm_nae_setup_mac(sc->base_addr, sc->block, sc->port, 0, 1, 1,
|
|
sc->speed, sc->duplexity);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* xlpge support function implementations
|
|
*/
|
|
static void
|
|
nlm_xlpge_release_mbuf(uint64_t paddr)
|
|
{
|
|
uint64_t mag, desc, mbuf;
|
|
|
|
paddr += (XLP_NTXFRAGS - 3) * sizeof(uint64_t);
|
|
mag = nlm_paddr_ld(paddr);
|
|
desc = nlm_paddr_ld(paddr + sizeof(uint64_t));
|
|
mbuf = nlm_paddr_ld(paddr + 2 * sizeof(uint64_t));
|
|
|
|
if (mag != 0xf00bad) {
|
|
/* somebody else packet Error - FIXME in intialization */
|
|
printf("cpu %d: ERR Tx packet paddr %jx, mag %jx, desc %jx mbuf %jx\n",
|
|
nlm_cpuid(), (uintmax_t)paddr, (uintmax_t)mag,
|
|
(intmax_t)desc, (uintmax_t)mbuf);
|
|
return;
|
|
}
|
|
m_freem((struct mbuf *)(uintptr_t)mbuf);
|
|
uma_zfree(nl_tx_desc_zone, (void *)(uintptr_t)desc);
|
|
}
|
|
|
|
static void
|
|
nlm_xlpge_rx(struct nlm_xlpge_softc *sc, int port, vm_paddr_t paddr, int len)
|
|
{
|
|
struct ifnet *ifp;
|
|
struct mbuf *m;
|
|
vm_offset_t temp;
|
|
unsigned long mag;
|
|
int prepad_size;
|
|
|
|
ifp = sc->xlpge_if;
|
|
temp = nlm_paddr_ld(paddr - NAE_CACHELINE_SIZE);
|
|
mag = nlm_paddr_ld(paddr - NAE_CACHELINE_SIZE + sizeof(uint64_t));
|
|
|
|
m = (struct mbuf *)(intptr_t)temp;
|
|
if (mag != 0xf00bad) {
|
|
/* somebody else packet Error - FIXME in intialization */
|
|
printf("cpu %d: ERR Rx packet paddr %jx, temp %p, mag %lx\n",
|
|
nlm_cpuid(), (uintmax_t)paddr, (void *)temp, mag);
|
|
return;
|
|
}
|
|
|
|
m->m_pkthdr.rcvif = ifp;
|
|
|
|
#ifdef DUMP_PACKET
|
|
{
|
|
int i = 0, j = 64;
|
|
unsigned char *buf = (char *)m->m_data;
|
|
printf("(cpu_%d: nlge_rx, !RX_COPY) Rx Packet: length=%d\n",
|
|
nlm_cpuid(), len);
|
|
if (len < j)
|
|
j = len;
|
|
if (sc->prepad_en)
|
|
j += ((sc->prepad_size + 1) * 16);
|
|
for (i = 0; i < j; i++) {
|
|
if (i && (i % 16) == 0)
|
|
printf("\n");
|
|
printf("%02x ", buf[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
|
|
if (sc->prepad_en) {
|
|
prepad_size = ((sc->prepad_size + 1) * 16);
|
|
m->m_data += prepad_size;
|
|
m->m_pkthdr.len = m->m_len = (len - prepad_size);
|
|
} else
|
|
m->m_pkthdr.len = m->m_len = len;
|
|
|
|
if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
|
|
#ifdef XLP_DRIVER_LOOPBACK
|
|
if (port == 16 || port == 17)
|
|
(*ifp->if_input)(ifp, m);
|
|
else
|
|
xlpge_tx(ifp, m);
|
|
#else
|
|
(*ifp->if_input)(ifp, m);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
nlm_xlpge_submit_rx_free_desc(struct nlm_xlpge_softc *sc, int num)
|
|
{
|
|
int i, size, ret, n;
|
|
struct nlm_fmn_msg msg;
|
|
void *ptr;
|
|
|
|
for(i = 0; i < num; i++) {
|
|
memset(&msg, 0, sizeof(msg));
|
|
ptr = get_buf();
|
|
if (!ptr) {
|
|
device_printf(sc->xlpge_dev, "Cannot allocate mbuf\n");
|
|
break;
|
|
}
|
|
|
|
msg.msg[0] = vtophys(ptr);
|
|
if (msg.msg[0] == 0) {
|
|
printf("Bad ptr for %p\n", ptr);
|
|
break;
|
|
}
|
|
size = 1;
|
|
|
|
n = 0;
|
|
while (1) {
|
|
/* on success returns 1, else 0 */
|
|
ret = nlm_fmn_msgsend(sc->rxfreeq, size, 0, &msg);
|
|
if (ret == 0)
|
|
break;
|
|
if (n++ > 10000) {
|
|
printf("Too many credit fails for send free desc\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nlm_xlpge_msgring_handler(int vc, int size, int code, int src_id,
|
|
struct nlm_fmn_msg *msg, void *data)
|
|
{
|
|
uint64_t phys_addr;
|
|
struct nlm_xlpnae_softc *sc;
|
|
struct nlm_xlpge_softc *xlpge_sc;
|
|
struct ifnet *ifp;
|
|
uint32_t context;
|
|
uint32_t port = 0;
|
|
uint32_t length;
|
|
|
|
sc = (struct nlm_xlpnae_softc *)data;
|
|
KASSERT(sc != NULL, ("Null sc in msgring handler"));
|
|
|
|
if (size == 1) { /* process transmit complete */
|
|
phys_addr = msg->msg[0] & 0xffffffffffULL;
|
|
|
|
/* context is SGMII_RCV_CONTEXT_NUM + three bit vlan type
|
|
* or vlan priority
|
|
*/
|
|
context = (msg->msg[0] >> 40) & 0x3fff;
|
|
port = cntx2port[context];
|
|
|
|
if (port >= XLP_MAX_PORTS) {
|
|
printf("%s:%d Bad port %d (context=%d)\n",
|
|
__func__, __LINE__, port, context);
|
|
return;
|
|
}
|
|
ifp = ifp_ports[port].xlpge_if;
|
|
xlpge_sc = ifp_ports[port].xlpge_sc;
|
|
|
|
nlm_xlpge_release_mbuf(phys_addr);
|
|
|
|
if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
|
|
|
|
} else if (size > 1) { /* Recieve packet */
|
|
phys_addr = msg->msg[1] & 0xffffffffc0ULL;
|
|
length = (msg->msg[1] >> 40) & 0x3fff;
|
|
length -= MAC_CRC_LEN;
|
|
|
|
/* context is SGMII_RCV_CONTEXT_NUM + three bit vlan type
|
|
* or vlan priority
|
|
*/
|
|
context = (msg->msg[1] >> 54) & 0x3ff;
|
|
port = cntx2port[context];
|
|
|
|
if (port >= XLP_MAX_PORTS) {
|
|
printf("%s:%d Bad port %d (context=%d)\n",
|
|
__func__, __LINE__, port, context);
|
|
return;
|
|
}
|
|
|
|
ifp = ifp_ports[port].xlpge_if;
|
|
xlpge_sc = ifp_ports[port].xlpge_sc;
|
|
|
|
nlm_xlpge_rx(xlpge_sc, port, phys_addr, length);
|
|
/* return back a free descriptor to NA */
|
|
nlm_xlpge_submit_rx_free_desc(xlpge_sc, 1);
|
|
}
|
|
}
|