#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/syslog.h>
#include <dev/ofw/openfirm.h>
#include <machine/bus.h>
#include <machine/octeon_model.h>
#include <machine/octeonvar.h>
#include <octeon/dev/iobusvar.h>
#include <octeon/dev/cn30xxasxvar.h>
#include <octeon/dev/cn30xxciureg.h>
#include <octeon/dev/cn30xxgmxreg.h>
#include <octeon/dev/cn30xxgmxvar.h>
#include <octeon/dev/cn30xxipdvar.h>
#include <octeon/dev/cn30xxpipvar.h>
#include <octeon/dev/cn30xxsmivar.h>
#define GMX_NCAM 8
#define _GMX_RD8(sc, off) \
bus_space_read_8((sc)->sc_port_gmx->sc_regt, (sc)->sc_port_gmx->sc_regh, (off))
#define _GMX_WR8(sc, off, v) \
bus_space_write_8((sc)->sc_port_gmx->sc_regt, (sc)->sc_port_gmx->sc_regh, (off), (v))
#define _GMX_PORT_RD8(sc, off) \
bus_space_read_8((sc)->sc_port_gmx->sc_regt, (sc)->sc_port_regh, (off))
#define _GMX_PORT_WR8(sc, off, v) \
bus_space_write_8((sc)->sc_port_gmx->sc_regt, (sc)->sc_port_regh, (off), (v))
#define AGL_GMX_RD8(sc, reg) \
bus_space_read_8((sc)->sc_port_gmx->sc_regt, \
(sc)->sc_port_gmx->sc_regh, (reg))
#define AGL_GMX_WR8(sc, reg, val) \
bus_space_write_8((sc)->sc_port_gmx->sc_regt, \
(sc)->sc_port_gmx->sc_regh, (reg), (val))
#define AGL_GMX_PORT_RD8(sc, reg) \
bus_space_read_8((sc)->sc_port_gmx->sc_regt, \
(sc)->sc_port_regh, (reg))
#define AGL_GMX_PORT_WR8(sc, reg, val) \
bus_space_write_8((sc)->sc_port_gmx->sc_regt, \
(sc)->sc_port_regh, (reg), (val))
#define PCS_READ_8(sc, reg) \
bus_space_read_8((sc)->sc_port_gmx->sc_regt, (sc)->sc_port_pcs_regh, \
(reg))
#define PCS_WRITE_8(sc, reg, val) \
bus_space_write_8((sc)->sc_port_gmx->sc_regt, (sc)->sc_port_pcs_regh, \
(reg), (val))
struct cn30xxgmx_port_ops {
int (*port_ops_enable)(struct cn30xxgmx_port_softc *, int);
int (*port_ops_speed)(struct cn30xxgmx_port_softc *);
int (*port_ops_timing)(struct cn30xxgmx_port_softc *);
};
int cn30xxgmx_match(struct device *, void *, void *);
void cn30xxgmx_attach(struct device *, struct device *, void *);
int cn30xxgmx_print(void *, const char *);
void cn30xxgmx_init(struct cn30xxgmx_softc *);
int cn30xxgmx_rx_frm_ctl_xable(struct cn30xxgmx_port_softc *,
uint64_t, int);
void cn30xxgmx_agl_init(struct cn30xxgmx_port_softc *);
int cn30xxgmx_agl_enable(struct cn30xxgmx_port_softc *, int);
int cn30xxgmx_agl_speed(struct cn30xxgmx_port_softc *);
int cn30xxgmx_agl_timing(struct cn30xxgmx_port_softc *);
int cn30xxgmx_rgmii_enable(struct cn30xxgmx_port_softc *, int);
int cn30xxgmx_rgmii_speed(struct cn30xxgmx_port_softc *);
int cn30xxgmx_rgmii_speed_newlink(struct cn30xxgmx_port_softc *,
uint64_t *);
int cn30xxgmx_rgmii_speed_speed(struct cn30xxgmx_port_softc *);
int cn30xxgmx_rgmii_timing(struct cn30xxgmx_port_softc *);
int cn30xxgmx_sgmii_enable(struct cn30xxgmx_port_softc *, int);
int cn30xxgmx_sgmii_speed(struct cn30xxgmx_port_softc *);
int cn30xxgmx_sgmii_timing(struct cn30xxgmx_port_softc *);
int cn30xxgmx_tx_ovr_bp_enable(struct cn30xxgmx_port_softc *, int);
int cn30xxgmx_rx_pause_enable(struct cn30xxgmx_port_softc *, int);
#ifdef OCTEON_ETH_DEBUG
int cn30xxgmx_rgmii_speed_newlink_log(struct cn30xxgmx_port_softc *,
uint64_t);
#endif
struct cn30xxgmx_port_ops cn30xxgmx_port_ops_agl = {
.port_ops_enable = cn30xxgmx_agl_enable,
.port_ops_speed = cn30xxgmx_agl_speed,
.port_ops_timing = cn30xxgmx_agl_timing,
};
struct cn30xxgmx_port_ops cn30xxgmx_port_ops_mii = {
};
struct cn30xxgmx_port_ops cn30xxgmx_port_ops_gmii = {
.port_ops_enable = cn30xxgmx_rgmii_enable,
.port_ops_speed = cn30xxgmx_rgmii_speed,
.port_ops_timing = cn30xxgmx_rgmii_timing,
};
struct cn30xxgmx_port_ops cn30xxgmx_port_ops_rgmii = {
.port_ops_enable = cn30xxgmx_rgmii_enable,
.port_ops_speed = cn30xxgmx_rgmii_speed,
.port_ops_timing = cn30xxgmx_rgmii_timing,
};
struct cn30xxgmx_port_ops cn30xxgmx_port_ops_sgmii = {
.port_ops_enable = cn30xxgmx_sgmii_enable,
.port_ops_speed = cn30xxgmx_sgmii_speed,
.port_ops_timing = cn30xxgmx_sgmii_timing,
};
struct cn30xxgmx_port_ops cn30xxgmx_port_ops_spi42 = {
};
struct cn30xxgmx_port_ops *cn30xxgmx_port_ops[] = {
[GMX_MII_PORT] = &cn30xxgmx_port_ops_mii,
[GMX_GMII_PORT] = &cn30xxgmx_port_ops_gmii,
[GMX_RGMII_PORT] = &cn30xxgmx_port_ops_rgmii,
[GMX_SGMII_PORT] = &cn30xxgmx_port_ops_sgmii,
[GMX_SPI42_PORT] = &cn30xxgmx_port_ops_spi42,
[GMX_AGL_PORT] = &cn30xxgmx_port_ops_agl,
};
const struct cfattach octgmx_ca = {sizeof(struct cn30xxgmx_softc),
cn30xxgmx_match, cn30xxgmx_attach, NULL, NULL};
struct cfdriver octgmx_cd = {NULL, "octgmx", DV_DULL};
int
cn30xxgmx_match(struct device *parent, void *match, void *aux)
{
struct cfdata *cf = (struct cfdata *)match;
struct iobus_attach_args *aa = aux;
if (strcmp(cf->cf_driver->cd_name, aa->aa_name) != 0)
return 0;
return 1;
}
int
cn30xxgmx_get_phy_phandle(int interface, int port)
{
char name[64];
int node;
int phandle = 0;
snprintf(name, sizeof(name),
"/soc/pip@11800a0000000/interface@%x/ethernet@%x",
interface, port);
node = OF_finddevice(name);
if (node != - 1)
phandle = OF_getpropint(node, "phy-handle", 0);
return phandle;
}
void
cn30xxgmx_attach(struct device *parent, struct device *self, void *aux)
{
struct cn30xxgmx_attach_args gmx_aa;
struct iobus_attach_args *aa = aux;
struct cn30xxgmx_port_softc *port_sc;
struct cn30xxgmx_softc *sc = (void *)self;
struct cn30xxsmi_softc *smi;
int i;
int phy_addr;
int port;
int status;
sc->sc_regt = aa->aa_bust;
sc->sc_unitno = aa->aa_unitno;
status = bus_space_map(sc->sc_regt, aa->aa_addr, GMX_BLOCK_SIZE,
0, &sc->sc_regh);
if (status != 0) {
printf(": can't map registers\n");
return;
}
cn30xxgmx_init(sc);
if (sc->sc_nports == 0) {
printf(": no active ports found\n");
goto error;
}
sc->sc_ports = mallocarray(sc->sc_nports, sizeof(*sc->sc_ports),
M_DEVBUF, M_NOWAIT | M_ZERO);
if (sc->sc_ports == NULL) {
printf(": out of memory\n");
goto error;
}
printf("\n");
for (i = 0; i < sc->sc_nports; i++) {
if (sc->sc_port_types[i] == GMX_AGL_PORT)
port = 24;
else
port = GMX_PORT_NUM(sc->sc_unitno, i);
if (cn30xxsmi_get_phy(cn30xxgmx_get_phy_phandle(sc->sc_unitno,
i), port, &smi, &phy_addr))
continue;
port_sc = &sc->sc_ports[i];
port_sc->sc_port_gmx = sc;
port_sc->sc_port_no = port;
port_sc->sc_port_type = sc->sc_port_types[i];
port_sc->sc_port_ops = cn30xxgmx_port_ops[port_sc->sc_port_type];
status = bus_space_map(sc->sc_regt,
aa->aa_addr + GMX0_BASE_PORT_SIZE * i,
GMX0_BASE_PORT_SIZE, 0, &port_sc->sc_port_regh);
if (status != 0) {
printf("%s port %d: can't map registers\n",
sc->sc_dev.dv_xname, port);
continue;
}
switch (port_sc->sc_port_type) {
case GMX_AGL_PORT:
cn30xxgmx_agl_init(port_sc);
break;
case GMX_MII_PORT:
case GMX_GMII_PORT:
case GMX_RGMII_PORT: {
struct cn30xxasx_attach_args asx_aa;
asx_aa.aa_port = i;
asx_aa.aa_regt = aa->aa_bust;
cn30xxasx_init(&asx_aa, &port_sc->sc_port_asx);
break;
}
case GMX_SGMII_PORT:
if (bus_space_map(sc->sc_regt,
PCS_BASE(sc->sc_unitno, i), PCS_SIZE, 0,
&port_sc->sc_port_pcs_regh)) {
printf("%s port %d: can't map PCS registers\n",
sc->sc_dev.dv_xname, port);
continue;
}
break;
default:
break;
}
(void)memset(&gmx_aa, 0, sizeof(gmx_aa));
gmx_aa.ga_regt = aa->aa_bust;
gmx_aa.ga_dmat = aa->aa_dmat;
gmx_aa.ga_addr = aa->aa_addr;
gmx_aa.ga_name = "cnmac";
gmx_aa.ga_portno = port_sc->sc_port_no;
gmx_aa.ga_port_type = sc->sc_port_types[i];
gmx_aa.ga_gmx = sc;
gmx_aa.ga_gmx_port = port_sc;
gmx_aa.ga_phy_addr = phy_addr;
gmx_aa.ga_smi = smi;
config_found(self, &gmx_aa, cn30xxgmx_print);
}
return;
error:
bus_space_unmap(sc->sc_regt, sc->sc_regh, GMX_BLOCK_SIZE);
}
int
cn30xxgmx_print(void *aux, const char *pnp)
{
struct cn30xxgmx_attach_args *ga = aux;
static const char *types[] = {
[GMX_AGL_PORT] = "AGL",
[GMX_MII_PORT] = "MII",
[GMX_GMII_PORT] = "GMII",
[GMX_RGMII_PORT] = "RGMII",
[GMX_SGMII_PORT] = "SGMII"
};
#if DEBUG
if (pnp)
printf("%s at %s", ga->ga_name, pnp);
#endif
printf(": port %d %s", ga->ga_portno, types[ga->ga_port_type]);
return UNCONF;
}
void
cn30xxgmx_init(struct cn30xxgmx_softc *sc)
{
uint64_t inf_mode;
int i, id;
id = octeon_get_chipid();
switch (octeon_model_family(id)) {
case OCTEON_MODEL_FAMILY_CN31XX:
inf_mode = bus_space_read_8(sc->sc_regt, sc->sc_regh,
GMX0_INF_MODE);
if ((inf_mode & INF_MODE_EN) == 0)
break;
if ((inf_mode & INF_MODE_TYPE) == 0) {
sc->sc_nports = 3;
sc->sc_port_types[0] = GMX_RGMII_PORT;
sc->sc_port_types[1] = GMX_RGMII_PORT;
sc->sc_port_types[2] = GMX_RGMII_PORT;
} else {
sc->sc_nports = 2;
sc->sc_port_types[0] = GMX_RGMII_PORT;
sc->sc_port_types[1] = GMX_GMII_PORT;
}
break;
case OCTEON_MODEL_FAMILY_CN30XX:
case OCTEON_MODEL_FAMILY_CN50XX:
inf_mode = bus_space_read_8(sc->sc_regt, sc->sc_regh,
GMX0_INF_MODE);
if ((inf_mode & INF_MODE_EN) == 0)
break;
if ((inf_mode & INF_MODE_P0MII) == 0)
sc->sc_port_types[0] = GMX_RGMII_PORT;
else
sc->sc_port_types[0] = GMX_MII_PORT;
if ((inf_mode & INF_MODE_TYPE) == 0) {
sc->sc_nports = 3;
sc->sc_port_types[1] = GMX_RGMII_PORT;
sc->sc_port_types[2] = GMX_RGMII_PORT;
} else {
sc->sc_nports = 2;
sc->sc_port_types[1] = GMX_GMII_PORT;
}
if ((octeon_model(id) != OCTEON_MODEL_CN3010) &&
(octeon_model(id) != OCTEON_MODEL_CN5010))
if (sc->sc_nports == 3)
sc->sc_nports = 2;
break;
case OCTEON_MODEL_FAMILY_CN61XX: {
uint64_t qlm_cfg;
inf_mode = bus_space_read_8(sc->sc_regt, sc->sc_regh,
GMX0_INF_MODE);
if ((inf_mode & INF_MODE_EN) == 0)
break;
if (sc->sc_unitno == 0)
qlm_cfg = octeon_xkphys_read_8(MIO_QLM_CFG(2));
else
qlm_cfg = octeon_xkphys_read_8(MIO_QLM_CFG(0));
if ((qlm_cfg & MIO_QLM_CFG_CFG) == 2) {
sc->sc_nports = 4;
for (i = 0; i < sc->sc_nports; i++)
sc->sc_port_types[i] = GMX_SGMII_PORT;
} else if ((qlm_cfg & MIO_QLM_CFG_CFG) == 3) {
printf(": XAUI interface is not supported");
} else {
}
break;
}
case OCTEON_MODEL_FAMILY_CN71XX:
if (sc->sc_unitno == 4) {
uint64_t val;
val = bus_space_read_8(sc->sc_regt, sc->sc_regh,
AGL_PRT_CTL(0));
if ((val & AGL_PRT_CTL_MODE_M) ==
AGL_PRT_CTL_MODE_RGMII) {
sc->sc_nports = 1;
sc->sc_port_types[0] = GMX_AGL_PORT;
}
break;
}
inf_mode = bus_space_read_8(sc->sc_regt, sc->sc_regh,
GMX0_INF_MODE);
if ((inf_mode & INF_MODE_EN) == 0)
break;
switch (inf_mode & INF_MODE_MODE) {
case INF_MODE_MODE_SGMII:
sc->sc_nports = 4;
for (i = 0; i < sc->sc_nports; i++)
sc->sc_port_types[i] = GMX_SGMII_PORT;
break;
#ifdef notyet
case INF_MODE_MODE_XAUI:
#endif
default:
break;
}
break;
case OCTEON_MODEL_FAMILY_CN38XX:
case OCTEON_MODEL_FAMILY_CN56XX:
case OCTEON_MODEL_FAMILY_CN58XX:
default:
printf(": unsupported octeon model: 0x%x", id);
break;
}
}
int
cn30xxgmx_link_enable(struct cn30xxgmx_port_softc *sc, int enable)
{
uint64_t prt_cfg;
cn30xxgmx_tx_int_enable(sc, enable);
cn30xxgmx_rx_int_enable(sc, enable);
prt_cfg = _GMX_PORT_RD8(sc, GMX0_PRT0_CFG);
if (enable) {
if (cn30xxgmx_link_status(sc)) {
SET(prt_cfg, PRTN_CFG_EN);
}
} else {
CLR(prt_cfg, PRTN_CFG_EN);
}
_GMX_PORT_WR8(sc, GMX0_PRT0_CFG, prt_cfg);
(void)_GMX_PORT_RD8(sc, GMX0_PRT0_CFG);
return 0;
}
void
cn30xxgmx_stats_init(struct cn30xxgmx_port_softc *sc)
{
_GMX_PORT_WR8(sc, GMX0_RX0_STATS_CTL, 1);
_GMX_PORT_WR8(sc, GMX0_TX0_STATS_CTL, 1);
_GMX_PORT_WR8(sc, GMX0_RX0_STATS_PKTS, 0);
_GMX_PORT_WR8(sc, GMX0_RX0_STATS_OCTS, 0);
_GMX_PORT_WR8(sc, GMX0_RX0_STATS_PKTS_CTL, 0);
_GMX_PORT_WR8(sc, GMX0_RX0_STATS_PKTS_DMAC, 0);
_GMX_PORT_WR8(sc, GMX0_RX0_STATS_PKTS_DRP, 0);
_GMX_PORT_WR8(sc, GMX0_RX0_STATS_PKTS_BAD, 0);
_GMX_PORT_WR8(sc, GMX0_TX0_STAT0, 0);
_GMX_PORT_WR8(sc, GMX0_TX0_STAT1, 0);
_GMX_PORT_WR8(sc, GMX0_TX0_STAT2, 0);
_GMX_PORT_WR8(sc, GMX0_TX0_STAT3, 0);
_GMX_PORT_WR8(sc, GMX0_TX0_STAT4, 0);
_GMX_PORT_WR8(sc, GMX0_TX0_STAT5, 0);
_GMX_PORT_WR8(sc, GMX0_TX0_STAT6, 0);
_GMX_PORT_WR8(sc, GMX0_TX0_STAT7, 0);
_GMX_PORT_WR8(sc, GMX0_TX0_STAT8, 0);
_GMX_PORT_WR8(sc, GMX0_TX0_STAT9, 0);
}
int
cn30xxgmx_tx_ovr_bp_enable(struct cn30xxgmx_port_softc *sc, int enable)
{
uint64_t ovr_bp;
int index = GMX_PORT_INDEX(sc->sc_port_no);
ovr_bp = _GMX_RD8(sc, GMX0_TX_OVR_BP);
if (enable) {
CLR(ovr_bp, (1 << index) << TX_OVR_BP_EN_SHIFT);
SET(ovr_bp, (1 << index) << TX_OVR_BP_BP_SHIFT);
SET(ovr_bp, (1 << index) << TX_OVR_BP_IGN_FULL_SHIFT);
} else {
SET(ovr_bp, (1 << index) << TX_OVR_BP_EN_SHIFT);
CLR(ovr_bp, (1 << index) << TX_OVR_BP_BP_SHIFT);
SET(ovr_bp, (1 << index) << TX_OVR_BP_IGN_FULL_SHIFT);
}
_GMX_WR8(sc, GMX0_TX_OVR_BP, ovr_bp);
return 0;
}
int
cn30xxgmx_rx_pause_enable(struct cn30xxgmx_port_softc *sc, int enable)
{
if (enable) {
cn30xxgmx_rx_frm_ctl_enable(sc, RXN_FRM_CTL_CTL_BCK);
} else {
cn30xxgmx_rx_frm_ctl_disable(sc, RXN_FRM_CTL_CTL_BCK);
}
return 0;
}
void
cn30xxgmx_tx_int_enable(struct cn30xxgmx_port_softc *sc, int enable)
{
uint64_t tx_int_xxx = 0;
SET(tx_int_xxx,
TX_INT_REG_LATE_COL |
TX_INT_REG_XSDEF |
TX_INT_REG_XSCOL |
TX_INT_REG_UNDFLW |
TX_INT_REG_PKO_NXA);
_GMX_WR8(sc, GMX0_TX_INT_REG, tx_int_xxx);
_GMX_WR8(sc, GMX0_TX_INT_EN, enable ? tx_int_xxx : 0);
}
void
cn30xxgmx_rx_int_enable(struct cn30xxgmx_port_softc *sc, int enable)
{
uint64_t rx_int_xxx = 0;
SET(rx_int_xxx, 0 |
RXN_INT_REG_PHY_DUPX |
RXN_INT_REG_PHY_SPD |
RXN_INT_REG_PHY_LINK |
RXN_INT_REG_IFGERR |
RXN_INT_REG_COLDET |
RXN_INT_REG_FALERR |
RXN_INT_REG_RSVERR |
RXN_INT_REG_PCTERR |
RXN_INT_REG_OVRERR |
RXN_INT_REG_NIBERR |
RXN_INT_REG_SKPERR |
RXN_INT_REG_RCVERR |
RXN_INT_REG_LENERR |
RXN_INT_REG_ALNERR |
RXN_INT_REG_FCSERR |
RXN_INT_REG_JABBER |
RXN_INT_REG_MAXERR |
RXN_INT_REG_CAREXT |
RXN_INT_REG_MINERR);
_GMX_PORT_WR8(sc, GMX0_RX0_INT_REG, rx_int_xxx);
_GMX_PORT_WR8(sc, GMX0_RX0_INT_EN, enable ? rx_int_xxx : 0);
}
int
cn30xxgmx_rx_frm_ctl_enable(struct cn30xxgmx_port_softc *sc,
uint64_t rx_frm_ctl)
{
struct ifnet *ifp = &sc->sc_port_ac->ac_if;
unsigned int maxlen;
maxlen = roundup(ifp->if_hardmtu + ETHER_HDR_LEN + ETHER_CRC_LEN +
ETHER_VLAN_ENCAP_LEN, 8);
_GMX_PORT_WR8(sc, GMX0_RX0_JABBER, maxlen);
return cn30xxgmx_rx_frm_ctl_xable(sc, rx_frm_ctl, 1);
}
int
cn30xxgmx_rx_frm_ctl_disable(struct cn30xxgmx_port_softc *sc,
uint64_t rx_frm_ctl)
{
return cn30xxgmx_rx_frm_ctl_xable(sc, rx_frm_ctl, 0);
}
int
cn30xxgmx_rx_frm_ctl_xable(struct cn30xxgmx_port_softc *sc,
uint64_t rx_frm_ctl, int enable)
{
uint64_t tmp;
tmp = _GMX_PORT_RD8(sc, GMX0_RX0_FRM_CTL);
if (enable)
SET(tmp, rx_frm_ctl);
else
CLR(tmp, rx_frm_ctl);
_GMX_PORT_WR8(sc, GMX0_RX0_FRM_CTL, tmp);
return 0;
}
int
cn30xxgmx_tx_thresh(struct cn30xxgmx_port_softc *sc, int cnt)
{
_GMX_PORT_WR8(sc, GMX0_TX0_THRESH, cnt);
return 0;
}
int
cn30xxgmx_set_filter(struct cn30xxgmx_port_softc *sc)
{
struct ifnet *ifp = &sc->sc_port_ac->ac_if;
struct arpcom *ac = sc->sc_port_ac;
struct ether_multi *enm;
struct ether_multistep step;
uint64_t cam[ETHER_ADDR_LEN];
uint64_t cam_en = 0;
uint64_t ctl = 0;
uint64_t mac;
int i, cidx;
SET(ctl, RXN_ADR_CTL_BCST);
if (ISSET(ifp->if_flags, IFF_PROMISC)) {
SET(ifp->if_flags, IFF_ALLMULTI);
SET(ctl, RXN_ADR_CTL_MCST_ACCEPT);
} else if (ac->ac_multirangecnt > 0 || ac->ac_multicnt >= GMX_NCAM) {
SET(ifp->if_flags, IFF_ALLMULTI);
SET(ctl, RXN_ADR_CTL_CAM_MODE);
SET(ctl, RXN_ADR_CTL_MCST_ACCEPT);
} else {
CLR(ifp->if_flags, IFF_ALLMULTI);
SET(ctl, RXN_ADR_CTL_CAM_MODE);
SET(ctl, RXN_ADR_CTL_MCST_AFCAM);
}
mac = 0;
for (i = 0; i < ETHER_ADDR_LEN; i++)
mac |= (uint64_t)ac->ac_enaddr[i] <<
((ETHER_ADDR_LEN - 1 - i) * 8);
memset(cam, 0, sizeof(cam));
cidx = 0;
if (!ISSET(ifp->if_flags, IFF_PROMISC)) {
for (i = 0; i < ETHER_ADDR_LEN; i++)
cam[i] |= (uint64_t)ac->ac_enaddr[i] << (cidx * 8);
cam_en |= 1U << cidx;
cidx++;
}
if (!ISSET(ifp->if_flags, IFF_ALLMULTI)) {
ETHER_FIRST_MULTI(step, ac, enm);
while (enm != NULL && cidx < GMX_NCAM) {
for (i = 0; i < ETHER_ADDR_LEN; i++)
cam[i] |= (uint64_t)enm->enm_addrlo[i] <<
(cidx * 8);
cam_en |= 1U << cidx;
cidx++;
ETHER_NEXT_MULTI(step, enm);
}
}
cn30xxgmx_link_enable(sc, 0);
_GMX_PORT_WR8(sc, GMX0_SMAC0, mac);
_GMX_PORT_WR8(sc, GMX0_RX0_ADR_CTL, ctl);
for (i = 0; i < ETHER_ADDR_LEN; i++)
_GMX_PORT_WR8(sc, GMX0_RX0_ADR_CAM(i), cam[i]);
_GMX_PORT_WR8(sc, GMX0_RX0_ADR_CAM_EN, cam_en);
(void)_GMX_PORT_RD8(sc, GMX0_RX0_ADR_CAM_EN);
cn30xxgmx_link_enable(sc, 1);
return 0;
}
int
cn30xxgmx_port_enable(struct cn30xxgmx_port_softc *sc, int enable)
{
(*sc->sc_port_ops->port_ops_enable)(sc, enable);
return 0;
}
int
cn30xxgmx_reset_speed(struct cn30xxgmx_port_softc *sc)
{
struct ifnet *ifp = &sc->sc_port_ac->ac_if;
if (ISSET(sc->sc_port_mii->mii_flags, MIIF_DOINGAUTO)) {
log(LOG_WARNING,
"%s: autonegotiation has not been completed yet\n",
ifp->if_xname);
return 1;
}
(*sc->sc_port_ops->port_ops_speed)(sc);
return 0;
}
int
cn30xxgmx_reset_timing(struct cn30xxgmx_port_softc *sc)
{
(*sc->sc_port_ops->port_ops_timing)(sc);
return 0;
}
int
cn30xxgmx_reset_flowctl(struct cn30xxgmx_port_softc *sc)
{
struct ifmedia_entry *ife = sc->sc_port_mii->mii_media.ifm_cur;
#ifdef GMX_802_3X_DISABLE_AUTONEG
if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
sc->sc_port_flowflags &= ~IFM_ETH_FMASK;
}
#else
if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO &&
(sc->sc_port_mii->mii_media_active & IFM_ETH_FMASK) !=
sc->sc_port_flowflags) {
sc->sc_port_flowflags =
sc->sc_port_mii->mii_media_active & IFM_ETH_FMASK;
sc->sc_port_mii->mii_media_active &= ~IFM_ETH_FMASK;
}
#endif
if (sc->sc_port_flowflags & IFM_ETH_TXPAUSE) {
cn30xxgmx_tx_ovr_bp_enable(sc, 1);
} else {
cn30xxgmx_tx_ovr_bp_enable(sc, 0);
}
if (sc->sc_port_flowflags & IFM_ETH_RXPAUSE) {
cn30xxgmx_rx_pause_enable(sc, 1);
} else {
cn30xxgmx_rx_pause_enable(sc, 0);
}
return 0;
}
void
cn30xxgmx_agl_init(struct cn30xxgmx_port_softc *sc)
{
uint64_t val;
int port = 0;
val = AGL_GMX_PORT_RD8(sc, AGL_GMX_PRT_CFG);
CLR(val, AGL_GMX_PRT_CFG_EN);
AGL_GMX_PORT_WR8(sc, AGL_GMX_PRT_CFG, val);
(void)AGL_GMX_PORT_RD8(sc, AGL_GMX_PRT_CFG);
val = AGL_GMX_RD8(sc, AGL_PRT_CTL(port));
CLR(val, AGL_PRT_CTL_CLKRST);
CLR(val, AGL_PRT_CTL_DLLRST);
CLR(val, AGL_PRT_CTL_CLKTX_BYP);
CLR(val, AGL_PRT_CTL_CLKTX_SET_M);
CLR(val, AGL_PRT_CTL_CLKRX_BYP);
CLR(val, AGL_PRT_CTL_CLKRX_SET_M);
CLR(val, AGL_PRT_CTL_REFCLK_SEL_M);
AGL_GMX_WR8(sc, AGL_PRT_CTL(port), val);
(void)AGL_GMX_RD8(sc, AGL_PRT_CTL(port));
delay(5);
val = AGL_GMX_RD8(sc, AGL_PRT_CTL(port));
CLR(val, AGL_PRT_CTL_DRV_BYP);
AGL_GMX_WR8(sc, AGL_PRT_CTL(port), val);
(void)AGL_GMX_RD8(sc, AGL_PRT_CTL(port));
val = AGL_GMX_RD8(sc, AGL_PRT_CTL(port));
SET(val, AGL_PRT_CTL_COMP);
AGL_GMX_WR8(sc, AGL_PRT_CTL(port), val);
(void)AGL_GMX_RD8(sc, AGL_PRT_CTL(port));
delay(20);
val = AGL_GMX_RX_FRM_CTL_PRE_ALIGN
| AGL_GMX_RX_FRM_CTL_PAD_LEN
| AGL_GMX_RX_FRM_CTL_VLAN_LEN
| AGL_GMX_RX_FRM_CTL_PRE_FREE
| AGL_GMX_RX_FRM_CTL_MCST
| AGL_GMX_RX_FRM_CTL_BCK
| AGL_GMX_RX_FRM_CTL_DRP
| AGL_GMX_RX_FRM_CTL_PRE_STRP
| AGL_GMX_RX_FRM_CTL_PRE_CHK;
AGL_GMX_PORT_WR8(sc, AGL_GMX_RX_FRM_CTL, val);
(void)AGL_GMX_PORT_RD8(sc, AGL_GMX_RX_FRM_CTL);
}
void
cn30xxgmx_agl_up(struct cn30xxgmx_port_softc *sc)
{
uint64_t val;
val = AGL_GMX_PORT_RD8(sc, AGL_GMX_PRT_CFG);
SET(val, AGL_GMX_PRT_CFG_RX_EN);
SET(val, AGL_GMX_PRT_CFG_TX_EN);
AGL_GMX_PORT_WR8(sc, AGL_GMX_PRT_CFG, val);
val = AGL_GMX_PORT_RD8(sc, AGL_GMX_PRT_CFG);
SET(val, AGL_GMX_PRT_CFG_EN);
AGL_GMX_PORT_WR8(sc, AGL_GMX_PRT_CFG, val);
(void)AGL_GMX_PORT_RD8(sc, AGL_GMX_PRT_CFG);
}
void
cn30xxgmx_agl_down(struct cn30xxgmx_port_softc *sc)
{
uint64_t val;
int timeout;
val = AGL_GMX_PORT_RD8(sc, AGL_GMX_PRT_CFG);
CLR(val, AGL_GMX_PRT_CFG_EN);
AGL_GMX_PORT_WR8(sc, AGL_GMX_PRT_CFG, val);
val = AGL_GMX_PORT_RD8(sc, AGL_GMX_PRT_CFG);
CLR(val, AGL_GMX_PRT_CFG_RX_EN);
CLR(val, AGL_GMX_PRT_CFG_TX_EN);
AGL_GMX_PORT_WR8(sc, AGL_GMX_PRT_CFG, val);
(void)AGL_GMX_PORT_RD8(sc, AGL_GMX_PRT_CFG);
for (timeout = 1000; timeout > 0; timeout--) {
const uint64_t idlemask = AGL_GMX_PRT_CFG_RX_IDLE |
AGL_GMX_PRT_CFG_TX_IDLE;
val = AGL_GMX_PORT_RD8(sc, AGL_GMX_PRT_CFG);
if ((val & idlemask) == idlemask)
break;
delay(1000);
}
}
int
cn30xxgmx_agl_enable(struct cn30xxgmx_port_softc *sc, int enable)
{
if (enable)
cn30xxgmx_agl_up(sc);
else
cn30xxgmx_agl_down(sc);
return 0;
}
int
cn30xxgmx_agl_speed(struct cn30xxgmx_port_softc *sc)
{
struct ifnet *ifp = &sc->sc_port_ac->ac_if;
uint64_t clk_cnt, prt_cfg, val;
unsigned int maxlen;
int port = 0;
cn30xxgmx_agl_down(sc);
prt_cfg = AGL_GMX_PORT_RD8(sc, AGL_GMX_PRT_CFG);
if (ISSET(sc->sc_port_mii->mii_media_active, IFM_FDX))
SET(prt_cfg, AGL_GMX_PRT_CFG_DUPLEX);
else
CLR(prt_cfg, AGL_GMX_PRT_CFG_DUPLEX);
switch (ifp->if_baudrate) {
case IF_Mbps(10):
CLR(prt_cfg, AGL_GMX_PRT_CFG_SPEED);
SET(prt_cfg, AGL_GMX_PRT_CFG_SPEED_MSB);
CLR(prt_cfg, AGL_GMX_PRT_CFG_SLOTTIME);
SET(prt_cfg, AGL_GMX_PRT_CFG_BURST);
clk_cnt = 50;
break;
case IF_Mbps(100):
CLR(prt_cfg, AGL_GMX_PRT_CFG_SPEED);
CLR(prt_cfg, AGL_GMX_PRT_CFG_SPEED_MSB);
CLR(prt_cfg, AGL_GMX_PRT_CFG_SLOTTIME);
SET(prt_cfg, AGL_GMX_PRT_CFG_BURST);
clk_cnt = 5;
break;
case IF_Gbps(1):
default:
SET(prt_cfg, AGL_GMX_PRT_CFG_SPEED);
CLR(prt_cfg, AGL_GMX_PRT_CFG_SPEED_MSB);
SET(prt_cfg, AGL_GMX_PRT_CFG_SLOTTIME);
if (ISSET(sc->sc_port_mii->mii_media_active, IFM_FDX))
SET(prt_cfg, AGL_GMX_PRT_CFG_BURST);
else
CLR(prt_cfg, AGL_GMX_PRT_CFG_BURST);
clk_cnt = 1;
break;
}
AGL_GMX_PORT_WR8(sc, AGL_GMX_PRT_CFG, prt_cfg);
(void)AGL_GMX_PORT_RD8(sc, AGL_GMX_PRT_CFG);
val = AGL_GMX_PORT_RD8(sc, AGL_GMX_TX_CLK);
CLR(val, AGL_GMX_TX_CLK_CLK_CNT_M);
SET(val, clk_cnt << AGL_GMX_TX_CLK_CLK_CNT_S);
AGL_GMX_PORT_WR8(sc, AGL_GMX_TX_CLK, val);
(void)AGL_GMX_PORT_RD8(sc, AGL_GMX_TX_CLK);
maxlen = roundup(ifp->if_hardmtu + ETHER_HDR_LEN + ETHER_CRC_LEN +
ETHER_VLAN_ENCAP_LEN, 8);
AGL_GMX_PORT_WR8(sc, AGL_GMX_RX_JABBER, maxlen);
AGL_GMX_PORT_WR8(sc, AGL_GMX_RX_FRM_MAX, maxlen);
(void)AGL_GMX_PORT_RD8(sc, AGL_GMX_RX_FRM_MAX);
cn30xxgmx_agl_up(sc);
val = AGL_GMX_RD8(sc, AGL_PRT_CTL(port));
SET(val, AGL_PRT_CTL_CLKRST);
AGL_GMX_WR8(sc, AGL_PRT_CTL(port), val);
val = AGL_GMX_RD8(sc, AGL_PRT_CTL(port));
SET(val, AGL_PRT_CTL_ENABLE);
AGL_GMX_WR8(sc, AGL_PRT_CTL(port), val);
(void)AGL_GMX_RD8(sc, AGL_PRT_CTL(port));
return 0;
}
int
cn30xxgmx_agl_timing(struct cn30xxgmx_port_softc *sc)
{
return 0;
}
int
cn30xxgmx_rgmii_enable(struct cn30xxgmx_port_softc *sc, int enable)
{
uint64_t mode;
mode = _GMX_RD8(sc, GMX0_INF_MODE);
if (ISSET(mode, INF_MODE_EN)) {
cn30xxasx_enable(sc->sc_port_asx, 1);
}
return 0;
}
int
cn30xxgmx_rgmii_speed(struct cn30xxgmx_port_softc *sc)
{
struct ifnet *ifp = &sc->sc_port_ac->ac_if;
uint64_t newlink;
int baudrate;
cn30xxgmx_link_enable(sc, 1);
cn30xxgmx_rgmii_speed_newlink(sc, &newlink);
if (sc->sc_link == newlink) {
return 0;
}
#ifdef OCTEON_ETH_DEBUG
cn30xxgmx_rgmii_speed_newlink_log(sc, newlink);
#endif
sc->sc_link = newlink;
switch (sc->sc_link & RXN_RX_INBND_SPEED) {
case RXN_RX_INBND_SPEED_2_5:
baudrate = IF_Mbps(10);
break;
case RXN_RX_INBND_SPEED_25:
baudrate = IF_Mbps(100);
break;
case RXN_RX_INBND_SPEED_125:
baudrate = IF_Gbps(1);
break;
default:
baudrate = IF_Gbps(1);
break;
}
ifp->if_baudrate = baudrate;
cn30xxgmx_link_enable(sc, 0);
delay((GMX_FRM_MAX_SIZ * 8) / (baudrate / 1000000));
cn30xxgmx_rgmii_speed_speed(sc);
cn30xxgmx_link_enable(sc, 1);
cn30xxasx_enable(sc->sc_port_asx, 1);
return 0;
}
int
cn30xxgmx_rgmii_speed_newlink(struct cn30xxgmx_port_softc *sc,
uint64_t *rnewlink)
{
uint64_t newlink;
newlink = _GMX_PORT_RD8(sc, GMX0_RX0_RX_INBND);
*rnewlink = newlink;
return 0;
}
#ifdef OCTEON_ETH_DEBUG
int
cn30xxgmx_rgmii_speed_newlink_log(struct cn30xxgmx_port_softc *sc,
uint64_t newlink)
{
struct ifnet *ifp = &sc->sc_port_ac->ac_if;
const char *status_str;
const char *speed_str;
const char *duplex_str;
int is_status_changed;
int is_speed_changed;
int is_linked;
char status_buf[80];
char speed_buf[80];
is_status_changed = (newlink & RXN_RX_INBND_STATUS) !=
(sc->sc_link & RXN_RX_INBND_STATUS);
is_speed_changed = (newlink & RXN_RX_INBND_SPEED) !=
(sc->sc_link & RXN_RX_INBND_SPEED);
is_linked = ISSET(newlink, RXN_RX_INBND_STATUS);
if (is_status_changed) {
if (is_linked)
status_str = "link up";
else
status_str = "link down";
} else {
if (is_linked) {
if (is_speed_changed)
status_str = "link change";
else
status_str = NULL;
} else {
status_str = NULL;
}
}
if (status_str != NULL) {
if ((is_speed_changed && is_linked) || is_linked) {
switch (newlink & RXN_RX_INBND_SPEED) {
case RXN_RX_INBND_SPEED_2_5:
speed_str = "10baseT";
break;
case RXN_RX_INBND_SPEED_25:
speed_str = "100baseTX";
break;
case RXN_RX_INBND_SPEED_125:
speed_str = "1000baseT";
break;
default:
panic("Unknown link speed");
break;
}
if (ISSET(newlink, RXN_RX_INBND_DUPLEX))
duplex_str = "-FDX";
else
duplex_str = "";
(void)snprintf(speed_buf, sizeof(speed_buf), "(%s%s)",
speed_str, duplex_str);
} else {
speed_buf[0] = '\0';
}
(void)snprintf(status_buf, sizeof(status_buf), "%s: %s%s%s\n",
ifp->if_xname, status_str, (is_speed_changed | is_linked) ? " " : "",
speed_buf);
log(LOG_CRIT, status_buf);
}
return 0;
}
#endif
int
cn30xxgmx_rgmii_speed_speed(struct cn30xxgmx_port_softc *sc)
{
uint64_t prt_cfg;
uint64_t tx_clk, tx_slot, tx_burst;
prt_cfg = _GMX_PORT_RD8(sc, GMX0_PRT0_CFG);
switch (sc->sc_link & RXN_RX_INBND_SPEED) {
case RXN_RX_INBND_SPEED_2_5:
tx_clk = 50;
tx_slot = 0x40;
tx_burst = 0;
CLR(prt_cfg, PRTN_CFG_SLOTTIME);
CLR(prt_cfg, PRTN_CFG_SPEED);
break;
case RXN_RX_INBND_SPEED_25:
tx_clk = 5;
tx_slot = 0x40;
tx_burst = 0;
CLR(prt_cfg, PRTN_CFG_SLOTTIME);
CLR(prt_cfg, PRTN_CFG_SPEED);
break;
case RXN_RX_INBND_SPEED_125:
tx_clk = 1;
tx_slot = 0x200;
tx_burst = 0x2000;
SET(prt_cfg, PRTN_CFG_SLOTTIME);
SET(prt_cfg, PRTN_CFG_SPEED);
break;
default:
tx_clk = 1;
tx_slot = 0x200;
tx_burst = 0x2000;
SET(prt_cfg, PRTN_CFG_SLOTTIME);
SET(prt_cfg, PRTN_CFG_SPEED);
break;
}
if (ISSET(sc->sc_link, RXN_RX_INBND_DUPLEX)) {
SET(prt_cfg, PRTN_CFG_DUPLEX);
} else {
CLR(prt_cfg, PRTN_CFG_DUPLEX);
}
_GMX_PORT_WR8(sc, GMX0_TX0_CLK, tx_clk);
_GMX_PORT_WR8(sc, GMX0_TX0_SLOT, tx_slot);
_GMX_PORT_WR8(sc, GMX0_TX0_BURST, tx_burst);
_GMX_PORT_WR8(sc, GMX0_PRT0_CFG, prt_cfg);
return 0;
}
int
cn30xxgmx_rgmii_timing(struct cn30xxgmx_port_softc *sc)
{
int clk_tx_setting;
int clk_rx_setting;
uint64_t rx_frm_ctl;
cn30xxgmx_tx_thresh(sc, 32);
rx_frm_ctl = 0 |
RXN_FRM_CTL_PRE_FREE |
RXN_FRM_CTL_CTL_SMAC |
RXN_FRM_CTL_CTL_MCST |
RXN_FRM_CTL_CTL_DRP |
RXN_FRM_CTL_PRE_STRP |
RXN_FRM_CTL_PRE_CHK;
cn30xxgmx_rx_frm_ctl_enable(sc, rx_frm_ctl);
switch (octeon_board) {
default:
clk_tx_setting = 24;
clk_rx_setting = 24;
break;
case BOARD_NETGEAR_UTM25:
if (sc->sc_port_no == 0) {
clk_tx_setting = 9;
clk_rx_setting = 9;
} else {
clk_tx_setting = 24;
clk_rx_setting = 24;
}
break;
case BOARD_UBIQUITI_E100:
case BOARD_UBIQUITI_E120:
clk_tx_setting = 16;
clk_rx_setting = 0;
break;
}
cn30xxasx_clk_set(sc->sc_port_asx, clk_tx_setting, clk_rx_setting);
return 0;
}
int
cn30xxgmx_sgmii_enable(struct cn30xxgmx_port_softc *sc, int enable)
{
uint64_t ctl_reg, status, timer_count;
uint64_t cpu_freq = octeon_boot_info->eclock / 1000000;
int done;
int i;
if (!enable)
return 0;
timer_count = PCS_READ_8(sc, PCS_LINK_TIMER_COUNT);
CLR(timer_count, PCS_LINK_TIMER_COUNT_MASK);
SET(timer_count, ((1600 * cpu_freq) >> 10) & PCS_LINK_TIMER_COUNT_MASK);
PCS_WRITE_8(sc, PCS_LINK_TIMER_COUNT, timer_count);
ctl_reg = PCS_READ_8(sc, PCS_MR_CONTROL);
SET(ctl_reg, PCS_MR_CONTROL_RESET);
PCS_WRITE_8(sc, PCS_MR_CONTROL, ctl_reg);
done = 0;
for (i = 0; i < 1000000; i++) {
ctl_reg = PCS_READ_8(sc, PCS_MR_CONTROL);
if (!ISSET(ctl_reg, PCS_MR_CONTROL_RESET)) {
done = 1;
break;
}
}
if (!done) {
printf("SGMII reset timeout on port %d\n", sc->sc_port_no);
return 1;
}
SET(ctl_reg, PCS_MR_CONTROL_AN_EN);
SET(ctl_reg, PCS_MR_CONTROL_RST_AN);
CLR(ctl_reg, PCS_MR_CONTROL_PWR_DN);
PCS_WRITE_8(sc, PCS_MR_CONTROL, ctl_reg);
done = 0;
for (i = 0; i < 1000000; i++) {
status = PCS_READ_8(sc, PCS_MR_STATUS);
if (ISSET(status, PCS_MR_STATUS_AN_CPT)) {
done = 1;
break;
}
}
if (!done) {
printf("SGMII autonegotiation timeout on port %d\n",
sc->sc_port_no);
return 1;
}
return 0;
}
int
cn30xxgmx_sgmii_speed(struct cn30xxgmx_port_softc *sc)
{
uint64_t misc_ctl, prt_cfg;
int tx_burst, tx_slot;
cn30xxgmx_link_enable(sc, 0);
prt_cfg = _GMX_PORT_RD8(sc, GMX0_PRT0_CFG);
if (ISSET(sc->sc_port_mii->mii_media_active, IFM_FDX))
SET(prt_cfg, PRTN_CFG_DUPLEX);
else
CLR(prt_cfg, PRTN_CFG_DUPLEX);
misc_ctl = PCS_READ_8(sc, PCS_MISC_CTL);
CLR(misc_ctl, PCS_MISC_CTL_SAMP_PT);
if (cn30xxgmx_link_status(sc))
CLR(misc_ctl, PCS_MISC_CTL_GMXENO);
else
SET(misc_ctl, PCS_MISC_CTL_GMXENO);
switch (sc->sc_port_ac->ac_if.if_baudrate) {
case IF_Mbps(10):
tx_slot = 0x40;
tx_burst = 0;
CLR(prt_cfg, PRTN_CFG_SPEED);
SET(prt_cfg, PRTN_CFG_SPEED_MSB);
CLR(prt_cfg, PRTN_CFG_SLOTTIME);
misc_ctl |= 25 & PCS_MISC_CTL_SAMP_PT;
break;
case IF_Mbps(100):
tx_slot = 0x40;
tx_burst = 0;
CLR(prt_cfg, PRTN_CFG_SPEED);
CLR(prt_cfg, PRTN_CFG_SPEED_MSB);
CLR(prt_cfg, PRTN_CFG_SLOTTIME);
misc_ctl |= 5 & PCS_MISC_CTL_SAMP_PT;
break;
case IF_Gbps(1):
default:
tx_slot = 0x200;
tx_burst = 0x2000;
SET(prt_cfg, PRTN_CFG_SPEED);
CLR(prt_cfg, PRTN_CFG_SPEED_MSB);
SET(prt_cfg, PRTN_CFG_SLOTTIME);
misc_ctl |= 1 & PCS_MISC_CTL_SAMP_PT;
break;
}
PCS_WRITE_8(sc, PCS_MISC_CTL, misc_ctl);
_GMX_PORT_WR8(sc, GMX0_TX0_SLOT, tx_slot);
_GMX_PORT_WR8(sc, GMX0_TX0_BURST, tx_burst);
_GMX_PORT_WR8(sc, GMX0_PRT0_CFG, prt_cfg);
cn30xxgmx_link_enable(sc, 1);
return 0;
}
int
cn30xxgmx_sgmii_timing(struct cn30xxgmx_port_softc *sc)
{
uint64_t rx_frm_ctl;
cn30xxgmx_tx_thresh(sc, 32);
rx_frm_ctl =
RXN_FRM_CTL_PRE_FREE |
RXN_FRM_CTL_CTL_SMAC |
RXN_FRM_CTL_CTL_MCST |
RXN_FRM_CTL_CTL_DRP |
RXN_FRM_CTL_PRE_STRP |
RXN_FRM_CTL_PRE_CHK;
cn30xxgmx_rx_frm_ctl_enable(sc, rx_frm_ctl);
return 0;
}
#if NKSTAT > 0
void
cn30xxgmx_kstat_read(struct cn30xxgmx_port_softc *sc, struct kstat_kv *kvs)
{
uint64_t val;
kstat_kv_u64(&kvs[cnmac_stat_rx_totp_gmx]) +=
(uint32_t)_GMX_PORT_RD8(sc, GMX0_RX0_STATS_PKTS);
kstat_kv_u64(&kvs[cnmac_stat_rx_toto_gmx]) +=
(uint32_t)_GMX_PORT_RD8(sc, GMX0_RX0_STATS_OCTS);
kstat_kv_u64(&kvs[cnmac_stat_rx_ctl]) +=
(uint32_t)_GMX_PORT_RD8(sc, GMX0_RX0_STATS_PKTS_CTL);
kstat_kv_u64(&kvs[cnmac_stat_rx_dmac]) +=
(uint32_t)_GMX_PORT_RD8(sc, GMX0_RX0_STATS_PKTS_DMAC);
kstat_kv_u64(&kvs[cnmac_stat_rx_drop]) +=
(uint32_t)_GMX_PORT_RD8(sc, GMX0_RX0_STATS_PKTS_DRP);
kstat_kv_u64(&kvs[cnmac_stat_rx_bad]) +=
(uint32_t)_GMX_PORT_RD8(sc, GMX0_RX0_STATS_PKTS_BAD);
val = _GMX_PORT_RD8(sc, GMX0_TX0_STAT0);
kstat_kv_u64(&kvs[cnmac_stat_tx_coll]) += (uint32_t)val;
kstat_kv_u64(&kvs[cnmac_stat_tx_defer]) += val >> 32;
val = _GMX_PORT_RD8(sc, GMX0_TX0_STAT1);
kstat_kv_u64(&kvs[cnmac_stat_tx_mcol]) += (uint32_t)val;
kstat_kv_u64(&kvs[cnmac_stat_tx_scol]) += val >> 32;
kstat_kv_u64(&kvs[cnmac_stat_tx_toto]) +=
(uint32_t)_GMX_PORT_RD8(sc, GMX0_TX0_STAT2);
kstat_kv_u64(&kvs[cnmac_stat_tx_totp]) +=
(uint32_t)_GMX_PORT_RD8(sc, GMX0_TX0_STAT3);
val = _GMX_PORT_RD8(sc, GMX0_TX0_STAT4);
kstat_kv_u64(&kvs[cnmac_stat_tx_hmin]) += (uint32_t)val;
kstat_kv_u64(&kvs[cnmac_stat_tx_h64]) += val >> 32;
val = _GMX_PORT_RD8(sc, GMX0_TX0_STAT5);
kstat_kv_u64(&kvs[cnmac_stat_tx_h127]) += (uint32_t)val;
kstat_kv_u64(&kvs[cnmac_stat_tx_h255]) += val >> 32;
val = _GMX_PORT_RD8(sc, GMX0_TX0_STAT6);
kstat_kv_u64(&kvs[cnmac_stat_tx_h511]) += (uint32_t)val;
kstat_kv_u64(&kvs[cnmac_stat_tx_h1023]) += val >> 32;
val = _GMX_PORT_RD8(sc, GMX0_TX0_STAT7);
kstat_kv_u64(&kvs[cnmac_stat_tx_h1518]) += (uint32_t)val;
kstat_kv_u64(&kvs[cnmac_stat_tx_hmax]) += val >> 32;
val = _GMX_PORT_RD8(sc, GMX0_TX0_STAT8);
kstat_kv_u64(&kvs[cnmac_stat_tx_bcast]) += (uint32_t)val;
kstat_kv_u64(&kvs[cnmac_stat_tx_mcast]) += val >> 32;
val = _GMX_PORT_RD8(sc, GMX0_TX0_STAT9);
kstat_kv_u64(&kvs[cnmac_stat_tx_ctl]) += (uint32_t)val;
kstat_kv_u64(&kvs[cnmac_stat_tx_uflow]) += val >> 32;
}
#endif