#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sockio.h>
#include <sys/queue.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/evcount.h>
#include <sys/socket.h>
#include <sys/timeout.h>
#include <sys/mbuf.h>
#include <machine/intr.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include "bpfilter.h"
#include <net/if.h>
#include <net/if_media.h>
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include <dev/mii/miidevs.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_clock.h>
#include <dev/ofw/ofw_gpio.h>
#include <dev/ofw/ofw_pinctrl.h>
#include <dev/ofw/fdt.h>
#define ENET_EIR 0x004
#define ENET_EIMR 0x008
#define ENET_RDAR 0x010
#define ENET_TDAR 0x014
#define ENET_ECR 0x024
#define ENET_MMFR 0x040
#define ENET_MSCR 0x044
#define ENET_MIBC 0x064
#define ENET_RCR 0x084
#define ENET_TCR 0x0C4
#define ENET_PALR 0x0E4
#define ENET_PAUR 0x0E8
#define ENET_OPD 0x0EC
#define ENET_IAUR 0x118
#define ENET_IALR 0x11C
#define ENET_GAUR 0x120
#define ENET_GALR 0x124
#define ENET_TFWR 0x144
#define ENET_RDSR 0x180
#define ENET_TDSR 0x184
#define ENET_MRBR 0x188
#define ENET_RSFL 0x190
#define ENET_RSEM 0x194
#define ENET_RAEM 0x198
#define ENET_RAFL 0x19C
#define ENET_TSEM 0x1A0
#define ENET_TAEM 0x1A4
#define ENET_TAFL 0x1A8
#define ENET_TIPG 0x1AC
#define ENET_FTRL 0x1B0
#define ENET_TACC 0x1C0
#define ENET_RACC 0x1C4
#define ENET_RDAR_RDAR (1 << 24)
#define ENET_TDAR_TDAR (1 << 24)
#define ENET_ECR_RESET (1 << 0)
#define ENET_ECR_ETHEREN (1 << 1)
#define ENET_ECR_EN1588 (1 << 4)
#define ENET_ECR_SPEED (1 << 5)
#define ENET_ECR_DBSWP (1 << 8)
#define ENET_MMFR_TA (2 << 16)
#define ENET_MMFR_RA_SHIFT 18
#define ENET_MMFR_PA_SHIFT 23
#define ENET_MMFR_OP_WR (1 << 28)
#define ENET_MMFR_OP_RD (2 << 28)
#define ENET_MMFR_ST (1 << 30)
#define ENET_RCR_MII_MODE (1 << 2)
#define ENET_RCR_PROM (1 << 3)
#define ENET_RCR_FCE (1 << 5)
#define ENET_RCR_RGMII_MODE (1 << 6)
#define ENET_RCR_RMII_10T (1 << 9)
#define ENET_RCR_MAX_FL(x) (((x) & 0x3fff) << 16)
#define ENET_TCR_FDEN (1 << 2)
#define ENET_EIR_MII (1 << 23)
#define ENET_EIR_RXF (1 << 25)
#define ENET_EIR_TXF (1 << 27)
#define ENET_TFWR_STRFWD (1 << 8)
#define ENET_RACC_SHIFT16 (1 << 7)
#define ENET_ATCR 0x400
#define ENET_ATVR 0x404
#define ENET_ATOFF 0x408
#define ENET_ATPER 0x40C
#define ENET_ATCOR 0x410
#define ENET_ATINC 0x414
#define ENET_ATSTMP 0x418
#define ENET_TGSR 0x604
#define ENET_TCSR0 0x608
#define ENET_TCCR0 0x60C
#define ENET_TCSR1 0x610
#define ENET_TCCR1 0x614
#define ENET_TCSR2 0x618
#define ENET_TCCR2 0x61C
#define ENET_TCSR3 0x620
#define ENET_TCCR3 0x624
#define ENET_MII_CLK 2500000
#define ENET_ALIGNMENT 16
#define HREAD4(sc, reg) \
(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
#define HWRITE4(sc, reg, val) \
bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
#define HSET4(sc, reg, bits) \
HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
#define HCLR4(sc, reg, bits) \
HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
#define ENET_MAX_BUF_SIZE 1522
#define ENET_MAX_PKT_SIZE 1536
#define ENET_ROUNDUP(size, unit) (((size) + (unit) - 1) & ~((unit) - 1))
#define ENET_RXD_EMPTY (1 << 15)
#define ENET_RXD_WRAP (1 << 13)
#define ENET_RXD_INTR (1 << 12)
#define ENET_RXD_LAST (1 << 11)
#define ENET_RXD_MISS (1 << 8)
#define ENET_RXD_BC (1 << 7)
#define ENET_RXD_MC (1 << 6)
#define ENET_RXD_LG (1 << 5)
#define ENET_RXD_NO (1 << 4)
#define ENET_RXD_CR (1 << 2)
#define ENET_RXD_OV (1 << 1)
#define ENET_RXD_TR (1 << 0)
#define ENET_TXD_READY (1 << 15)
#define ENET_TXD_WRAP (1 << 13)
#define ENET_TXD_INTR (1 << 12)
#define ENET_TXD_LAST (1 << 11)
#define ENET_TXD_TC (1 << 10)
#define ENET_TXD_ABC (1 << 9)
#define ENET_TXD_STATUS_MASK 0x3ff
#ifdef ENET_ENHANCED_BD
#define ENET_RXD_INT (1 << 23)
#define ENET_TXD_INT (1 << 30)
#endif
struct fec_buf {
bus_dmamap_t fb_map;
struct mbuf *fb_m;
struct mbuf *fb_m0;
};
#define ENET_NTXDESC 256
#define ENET_NTXSEGS 16
#define ENET_NRXDESC 256
struct fec_dmamem {
bus_dmamap_t fdm_map;
bus_dma_segment_t fdm_seg;
size_t fdm_size;
caddr_t fdm_kva;
};
#define ENET_DMA_MAP(_fdm) ((_fdm)->fdm_map)
#define ENET_DMA_LEN(_fdm) ((_fdm)->fdm_size)
#define ENET_DMA_DVA(_fdm) ((_fdm)->fdm_map->dm_segs[0].ds_addr)
#define ENET_DMA_KVA(_fdm) ((void *)(_fdm)->fdm_kva)
struct fec_desc {
uint16_t fd_len;
uint16_t fd_status;
uint32_t fd_addr;
#ifdef ENET_ENHANCED_BD
uint32_t fd_enhanced_status;
uint32_t fd_reserved0;
uint32_t fd_update_done;
uint32_t fd_timestamp;
uint32_t fd_reserved1[2];
#endif
};
struct fec_softc {
struct device sc_dev;
struct arpcom sc_ac;
struct mii_data sc_mii;
int sc_node;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
void *sc_ih[3];
bus_dma_tag_t sc_dmat;
struct fec_dmamem *sc_txring;
struct fec_buf *sc_txbuf;
struct fec_desc *sc_txdesc;
int sc_tx_prod;
int sc_tx_cnt;
int sc_tx_cons;
int sc_tx_bounce;
struct fec_dmamem *sc_rxring;
struct fec_buf *sc_rxbuf;
struct fec_desc *sc_rxdesc;
int sc_rx_prod;
struct if_rxring sc_rx_ring;
int sc_rx_cons;
struct timeout sc_tick;
uint32_t sc_phy_speed;
};
struct fec_softc *fec_sc;
int fec_match(struct device *, void *, void *);
void fec_attach(struct device *, struct device *, void *);
void fec_phy_init(struct fec_softc *, struct mii_softc *);
int fec_ioctl(struct ifnet *, u_long, caddr_t);
void fec_start(struct ifnet *);
int fec_encap(struct fec_softc *, struct mbuf *, int *);
void fec_init_txd(struct fec_softc *);
void fec_init_rxd(struct fec_softc *);
void fec_init(struct fec_softc *);
void fec_stop(struct fec_softc *);
void fec_iff(struct fec_softc *);
int fec_intr(void *);
void fec_tx_proc(struct fec_softc *);
void fec_rx_proc(struct fec_softc *);
void fec_tick(void *);
int fec_miibus_readreg(struct device *, int, int);
void fec_miibus_writereg(struct device *, int, int, int);
void fec_miibus_statchg(struct device *);
int fec_ifmedia_upd(struct ifnet *);
void fec_ifmedia_sts(struct ifnet *, struct ifmediareq *);
struct fec_dmamem *fec_dmamem_alloc(struct fec_softc *, bus_size_t, bus_size_t);
void fec_dmamem_free(struct fec_softc *, struct fec_dmamem *);
struct mbuf *fec_alloc_mbuf(struct fec_softc *, bus_dmamap_t);
void fec_fill_rx_ring(struct fec_softc *);
const struct cfattach fec_ca = {
sizeof (struct fec_softc), fec_match, fec_attach
};
struct cfdriver fec_cd = {
NULL, "fec", DV_IFNET
};
int
fec_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return (OF_is_compatible(faa->fa_node, "fsl,imx6q-fec") ||
OF_is_compatible(faa->fa_node, "fsl,imx6sx-fec") ||
OF_is_compatible(faa->fa_node, "fsl,imx8mq-fec"));
}
void
fec_attach(struct device *parent, struct device *self, void *aux)
{
struct fec_softc *sc = (struct fec_softc *) self;
struct fdt_attach_args *faa = aux;
struct fec_buf *txb, *rxb;
struct mii_data *mii;
struct mii_softc *child;
struct ifnet *ifp;
uint32_t phy_reset_gpio[3];
uint32_t phy_reset_duration;
int i, s;
if (faa->fa_nreg < 1)
return;
sc->sc_node = faa->fa_node;
sc->sc_iot = faa->fa_iot;
if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
faa->fa_reg[0].size, 0, &sc->sc_ioh))
panic("fec_attach: bus_space_map failed!");
sc->sc_dmat = faa->fa_dmat;
pinctrl_byname(faa->fa_node, "default");
clock_enable_all(faa->fa_node);
if (OF_getpropintarray(faa->fa_node, "phy-reset-gpios", phy_reset_gpio,
sizeof(phy_reset_gpio)) == sizeof(phy_reset_gpio)) {
phy_reset_duration = OF_getpropint(faa->fa_node,
"phy-reset-duration", 1);
if (phy_reset_duration > 1000)
phy_reset_duration = 1;
phy_reset_gpio[2] = GPIO_ACTIVE_LOW;
gpio_controller_config_pin(phy_reset_gpio, GPIO_CONFIG_OUTPUT);
gpio_controller_set_pin(phy_reset_gpio, 1);
delay((phy_reset_duration + 1) * 1000);
gpio_controller_set_pin(phy_reset_gpio, 0);
delay(1000);
}
printf("\n");
OF_getprop(faa->fa_node, "local-mac-address", sc->sc_ac.ac_enaddr,
sizeof(sc->sc_ac.ac_enaddr));
HSET4(sc, ENET_ECR, ENET_ECR_RESET);
while (HREAD4(sc, ENET_ECR) & ENET_ECR_ETHEREN)
continue;
HWRITE4(sc, ENET_EIMR, 0);
HWRITE4(sc, ENET_EIR, 0xffffffff);
sc->sc_ih[0] = fdt_intr_establish_idx(faa->fa_node, 0, IPL_NET,
fec_intr, sc, sc->sc_dev.dv_xname);
sc->sc_ih[1] = fdt_intr_establish_idx(faa->fa_node, 1, IPL_NET,
fec_intr, sc, sc->sc_dev.dv_xname);
sc->sc_ih[2] = fdt_intr_establish_idx(faa->fa_node, 2, IPL_NET,
fec_intr, sc, sc->sc_dev.dv_xname);
if (OF_is_compatible(faa->fa_node, "fsl,imx6q-fec"))
sc->sc_tx_bounce = 1;
sc->sc_txring = fec_dmamem_alloc(sc,
ENET_NTXDESC * sizeof(struct fec_desc), 64);
if (sc->sc_txring == NULL) {
printf("%s: could not allocate Tx descriptor ring\n",
sc->sc_dev.dv_xname);
goto bad;
}
sc->sc_txdesc = ENET_DMA_KVA(sc->sc_txring);
sc->sc_txbuf = malloc(sizeof(struct fec_buf) * ENET_NTXDESC,
M_DEVBUF, M_WAITOK);
for (i = 0; i < ENET_NTXDESC; i++) {
txb = &sc->sc_txbuf[i];
bus_dmamap_create(sc->sc_dmat, MCLBYTES, ENET_NTXSEGS,
MCLBYTES, 0, BUS_DMA_WAITOK, &txb->fb_map);
txb->fb_m = txb->fb_m0 = NULL;
}
sc->sc_rxring = fec_dmamem_alloc(sc,
ENET_NRXDESC * sizeof(struct fec_desc), 64);
if (sc->sc_rxring == NULL) {
printf("%s: could not allocate Rx descriptor ring\n",
sc->sc_dev.dv_xname);
for (i = 0; i < ENET_NTXDESC; i++) {
txb = &sc->sc_txbuf[i];
bus_dmamap_destroy(sc->sc_dmat, txb->fb_map);
}
free(sc->sc_txbuf, M_DEVBUF,
sizeof(struct fec_buf) * ENET_NTXDESC);
fec_dmamem_free(sc, sc->sc_txring);
goto bad;
}
sc->sc_rxdesc = ENET_DMA_KVA(sc->sc_rxring);
sc->sc_rxbuf = malloc(sizeof(struct fec_buf) * ENET_NRXDESC,
M_DEVBUF, M_WAITOK);
for (i = 0; i < ENET_NRXDESC; i++) {
rxb = &sc->sc_rxbuf[i];
bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
MCLBYTES, 0, BUS_DMA_WAITOK, &rxb->fb_map);
rxb->fb_m = NULL;
}
s = splnet();
ifp = &sc->sc_ac.ac_if;
ifp->if_softc = sc;
strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_ioctl = fec_ioctl;
ifp->if_start = fec_start;
ifp->if_capabilities = IFCAP_VLAN_MTU;
printf("%s: address %s\n", sc->sc_dev.dv_xname,
ether_sprintf(sc->sc_ac.ac_enaddr));
sc->sc_phy_speed = clock_get_frequency(sc->sc_node, "ipg");
sc->sc_phy_speed = (sc->sc_phy_speed + (ENET_MII_CLK - 1)) / ENET_MII_CLK;
sc->sc_phy_speed = (sc->sc_phy_speed / 2) - 1;
HWRITE4(sc, ENET_MSCR, (sc->sc_phy_speed << 1) | 0x100);
mii = &sc->sc_mii;
mii->mii_ifp = ifp;
mii->mii_readreg = fec_miibus_readreg;
mii->mii_writereg = fec_miibus_writereg;
mii->mii_statchg = fec_miibus_statchg;
ifmedia_init(&mii->mii_media, 0, fec_ifmedia_upd, fec_ifmedia_sts);
mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0);
child = LIST_FIRST(&mii->mii_phys);
if (child)
fec_phy_init(sc, child);
if (LIST_FIRST(&mii->mii_phys) == NULL) {
ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE);
} else
ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
if_attach(ifp);
ether_ifattach(ifp);
splx(s);
timeout_set(&sc->sc_tick, fec_tick, sc);
fec_sc = sc;
return;
bad:
bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
}
void
fec_phy_init(struct fec_softc *sc, struct mii_softc *child)
{
struct device *dev = (struct device *)sc;
int phy = child->mii_phy;
uint32_t reg;
if (child->mii_oui == MII_OUI_ATHEROS &&
child->mii_model == MII_MODEL_ATHEROS_AR8035) {
fec_miibus_writereg(dev, phy, 0x0d, 0x0003);
fec_miibus_writereg(dev, phy, 0x0e, 0x805d);
fec_miibus_writereg(dev, phy, 0x0d, 0x4003);
reg = fec_miibus_readreg(dev, phy, 0x0e);
fec_miibus_writereg(dev, phy, 0x0e, reg & ~0x0100);
fec_miibus_writereg(dev, phy, 0x0d, 0x0007);
fec_miibus_writereg(dev, phy, 0x0e, 0x8016);
fec_miibus_writereg(dev, phy, 0x0d, 0x4007);
reg = fec_miibus_readreg(dev, phy, 0x0e) & 0xffe3;
fec_miibus_writereg(dev, phy, 0x0e, reg | 0x18);
fec_miibus_writereg(dev, phy, 0x1d, 0x0005);
reg = fec_miibus_readreg(dev, phy, 0x1e);
fec_miibus_writereg(dev, phy, 0x1e, reg | 0x0100);
PHY_RESET(child);
}
if (child->mii_oui == MII_OUI_MICREL &&
child->mii_model == MII_MODEL_MICREL_KSZ9021) {
uint32_t rxc, rxdv, txc, txen;
uint32_t rxd0, rxd1, rxd2, rxd3;
uint32_t txd0, txd1, txd2, txd3;
uint32_t val, phy;
int node;
node = sc->sc_node;
phy = OF_getpropint(sc->sc_node, "phy-handle", 0);
if (phy)
node = OF_getnodebyphandle(phy);
rxc = OF_getpropint(node, "rxc-skew-ps", 1400) / 200;
rxdv = OF_getpropint(node, "rxdv-skew-ps", 1400) / 200;
txc = OF_getpropint(node, "txc-skew-ps", 1400) / 200;
txen = OF_getpropint(node, "txen-skew-ps", 1400) / 200;
rxd0 = OF_getpropint(node, "rxd0-skew-ps", 1400) / 200;
rxd1 = OF_getpropint(node, "rxd1-skew-ps", 1400) / 200;
rxd2 = OF_getpropint(node, "rxd2-skew-ps", 1400) / 200;
rxd3 = OF_getpropint(node, "rxd3-skew-ps", 1400) / 200;
txd0 = OF_getpropint(node, "txd0-skew-ps", 1400) / 200;
txd1 = OF_getpropint(node, "txd1-skew-ps", 1400) / 200;
txd2 = OF_getpropint(node, "txd2-skew-ps", 1400) / 200;
txd3 = OF_getpropint(node, "txd3-skew-ps", 1400) / 200;
val = ((rxc & 0xf) << 12) | ((rxdv & 0xf) << 8) |
((txc & 0xf) << 4) | ((txen & 0xf) << 0);
fec_miibus_writereg(dev, phy, 0x0b, 0x8104);
fec_miibus_writereg(dev, phy, 0x0c, val);
val = ((rxd3 & 0xf) << 12) | ((rxd2 & 0xf) << 8) |
((rxd1 & 0xf) << 4) | ((rxd0 & 0xf) << 0);
fec_miibus_writereg(dev, phy, 0x0b, 0x8105);
fec_miibus_writereg(dev, phy, 0x0c, val);
val = ((txd3 & 0xf) << 12) | ((txd2 & 0xf) << 8) |
((txd1 & 0xf) << 4) | ((txd0 & 0xf) << 0);
fec_miibus_writereg(dev, phy, 0x0b, 0x8106);
fec_miibus_writereg(dev, phy, 0x0c, val);
}
if (child->mii_oui == MII_OUI_MICREL &&
child->mii_model == MII_MODEL_MICREL_KSZ9031) {
uint32_t rxc, rxdv, txc, txen;
uint32_t rxd0, rxd1, rxd2, rxd3;
uint32_t txd0, txd1, txd2, txd3;
uint32_t val, phy;
int node;
node = sc->sc_node;
phy = OF_getpropint(sc->sc_node, "phy-handle", 0);
if (phy)
node = OF_getnodebyphandle(phy);
rxc = OF_getpropint(node, "rxc-skew-ps", 900) / 60;
rxdv = OF_getpropint(node, "rxdv-skew-ps", 420) / 60;
txc = OF_getpropint(node, "txc-skew-ps", 900) / 60;
txen = OF_getpropint(node, "txen-skew-ps", 420) / 60;
rxd0 = OF_getpropint(node, "rxd0-skew-ps", 420) / 60;
rxd1 = OF_getpropint(node, "rxd1-skew-ps", 420) / 60;
rxd2 = OF_getpropint(node, "rxd2-skew-ps", 420) / 60;
rxd3 = OF_getpropint(node, "rxd3-skew-ps", 420) / 60;
txd0 = OF_getpropint(node, "txd0-skew-ps", 420) / 60;
txd1 = OF_getpropint(node, "txd1-skew-ps", 420) / 60;
txd2 = OF_getpropint(node, "txd2-skew-ps", 420) / 60;
txd3 = OF_getpropint(node, "txd3-skew-ps", 420) / 60;
val = ((rxdv & 0xf) << 4) || ((txen & 0xf) << 0);
fec_miibus_writereg(dev, phy, 0x0d, 0x0002);
fec_miibus_writereg(dev, phy, 0x0e, 0x0004);
fec_miibus_writereg(dev, phy, 0x0d, 0x4002);
fec_miibus_writereg(dev, phy, 0x0e, val);
val = ((rxd3 & 0xf) << 12) | ((rxd2 & 0xf) << 8) |
((rxd1 & 0xf) << 4) | ((rxd0 & 0xf) << 0);
fec_miibus_writereg(dev, phy, 0x0d, 0x0002);
fec_miibus_writereg(dev, phy, 0x0e, 0x0005);
fec_miibus_writereg(dev, phy, 0x0d, 0x4002);
fec_miibus_writereg(dev, phy, 0x0e, val);
val = ((txd3 & 0xf) << 12) | ((txd2 & 0xf) << 8) |
((txd1 & 0xf) << 4) | ((txd0 & 0xf) << 0);
fec_miibus_writereg(dev, phy, 0x0d, 0x0002);
fec_miibus_writereg(dev, phy, 0x0e, 0x0006);
fec_miibus_writereg(dev, phy, 0x0d, 0x4002);
fec_miibus_writereg(dev, phy, 0x0e, val);
val = ((txc & 0x1f) << 5) || ((rxc & 0x1f) << 0);
fec_miibus_writereg(dev, phy, 0x0d, 0x0002);
fec_miibus_writereg(dev, phy, 0x0e, 0x0008);
fec_miibus_writereg(dev, phy, 0x0d, 0x4002);
fec_miibus_writereg(dev, phy, 0x0e, val);
}
}
void
fec_init_rxd(struct fec_softc *sc)
{
struct fec_desc *rxd;
sc->sc_rx_prod = sc->sc_rx_cons = 0;
memset(sc->sc_rxdesc, 0, ENET_DMA_LEN(sc->sc_rxring));
rxd = &sc->sc_rxdesc[ENET_NRXDESC - 1];
rxd->fd_status = ENET_RXD_WRAP;
}
void
fec_init_txd(struct fec_softc *sc)
{
struct fec_desc *txd;
sc->sc_tx_prod = sc->sc_tx_cons = 0;
sc->sc_tx_cnt = 0;
memset(sc->sc_txdesc, 0, ENET_DMA_LEN(sc->sc_txring));
txd = &sc->sc_txdesc[ENET_NTXDESC - 1];
txd->fd_status = ENET_TXD_WRAP;
}
void
fec_init(struct fec_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
int speed = 0;
HSET4(sc, ENET_ECR, ENET_ECR_RESET);
while (HREAD4(sc, ENET_ECR) & ENET_ECR_ETHEREN)
continue;
HWRITE4(sc, ENET_PALR,
(sc->sc_ac.ac_enaddr[0] << 24) |
(sc->sc_ac.ac_enaddr[1] << 16) |
(sc->sc_ac.ac_enaddr[2] << 8) |
sc->sc_ac.ac_enaddr[3]);
HWRITE4(sc, ENET_PAUR,
(sc->sc_ac.ac_enaddr[4] << 24) |
(sc->sc_ac.ac_enaddr[5] << 16));
HWRITE4(sc, ENET_EIR, 0xffffffff);
HWRITE4(sc, ENET_MRBR, ENET_MAX_PKT_SIZE);
fec_init_txd(sc);
fec_init_rxd(sc);
if_rxr_init(&sc->sc_rx_ring, 2, ENET_NRXDESC);
fec_fill_rx_ring(sc);
bus_dmamap_sync(sc->sc_dmat, ENET_DMA_MAP(sc->sc_txring),
0, ENET_DMA_LEN(sc->sc_txring), BUS_DMASYNC_PREWRITE);
bus_dmamap_sync(sc->sc_dmat, ENET_DMA_MAP(sc->sc_rxring),
0, ENET_DMA_LEN(sc->sc_rxring), BUS_DMASYNC_PREWRITE);
HWRITE4(sc, ENET_TDSR, ENET_DMA_DVA(sc->sc_txring));
HWRITE4(sc, ENET_RDSR, ENET_DMA_DVA(sc->sc_rxring));
HWRITE4(sc, ENET_TCR, ENET_TCR_FDEN);
HWRITE4(sc, ENET_RCR,
ENET_RCR_MAX_FL(1522) | ENET_RCR_RGMII_MODE | ENET_RCR_MII_MODE |
ENET_RCR_FCE);
HWRITE4(sc, ENET_MSCR, (sc->sc_phy_speed << 1) | 0x100);
HWRITE4(sc, ENET_RACC, ENET_RACC_SHIFT16);
HWRITE4(sc, ENET_FTRL, ENET_MAX_BUF_SIZE);
HWRITE4(sc, ENET_RSEM, 0x84);
HWRITE4(sc, ENET_RSFL, 16);
HWRITE4(sc, ENET_RAEM, 8);
HWRITE4(sc, ENET_RAFL, 8);
HWRITE4(sc, ENET_OPD, 0xFFF0);
HWRITE4(sc, ENET_TFWR, ENET_TFWR_STRFWD);
switch (IFM_SUBTYPE(sc->sc_mii.mii_media_active)) {
case IFM_1000_T:
speed |= ENET_ECR_SPEED;
break;
default:
speed &= ~ENET_ECR_SPEED;
}
HWRITE4(sc, ENET_ECR, ENET_ECR_ETHEREN | speed | ENET_ECR_DBSWP);
#ifdef ENET_ENHANCED_BD
HSET4(sc, ENET_ECR, ENET_ECR_EN1588);
#endif
HWRITE4(sc, ENET_RDAR, ENET_RDAR_RDAR);
fec_iff(sc);
timeout_add_sec(&sc->sc_tick, 1);
ifp->if_flags |= IFF_RUNNING;
ifq_clr_oactive(&ifp->if_snd);
HWRITE4(sc, ENET_EIMR, ENET_EIR_TXF | ENET_EIR_RXF);
fec_start(ifp);
}
void
fec_stop(struct fec_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct fec_buf *txb, *rxb;
int i;
ifp->if_flags &= ~IFF_RUNNING;
ifp->if_timer = 0;
ifq_clr_oactive(&ifp->if_snd);
timeout_del(&sc->sc_tick);
HSET4(sc, ENET_ECR, ENET_ECR_RESET);
while (HREAD4(sc, ENET_ECR) & ENET_ECR_ETHEREN)
continue;
HWRITE4(sc, ENET_MSCR, (sc->sc_phy_speed << 1) | 0x100);
for (i = 0; i < ENET_NTXDESC; i++) {
txb = &sc->sc_txbuf[i];
if (txb->fb_m == NULL)
continue;
bus_dmamap_sync(sc->sc_dmat, txb->fb_map, 0,
txb->fb_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_dmat, txb->fb_map);
m_freem(txb->fb_m);
m_freem(txb->fb_m0);
txb->fb_m = txb->fb_m0 = NULL;
}
for (i = 0; i < ENET_NRXDESC; i++) {
rxb = &sc->sc_rxbuf[i];
if (rxb->fb_m == NULL)
continue;
bus_dmamap_sync(sc->sc_dmat, rxb->fb_map, 0,
rxb->fb_map->dm_mapsize, BUS_DMASYNC_POSTREAD);
bus_dmamap_unload(sc->sc_dmat, rxb->fb_map);
if_rxr_put(&sc->sc_rx_ring, 1);
rxb->fb_m = NULL;
}
}
void
fec_iff(struct fec_softc *sc)
{
struct arpcom *ac = &sc->sc_ac;
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct ether_multi *enm;
struct ether_multistep step;
uint64_t ghash = 0, ihash = 0;
uint32_t h;
ifp->if_flags &= ~IFF_ALLMULTI;
if (ifp->if_flags & IFF_PROMISC) {
ifp->if_flags |= IFF_ALLMULTI;
ihash = 0xffffffffffffffffLLU;
} else if (ac->ac_multirangecnt > 0) {
ifp->if_flags |= IFF_ALLMULTI;
ghash = 0xffffffffffffffffLLU;
} else {
ETHER_FIRST_MULTI(step, ac, enm);
while (enm != NULL) {
h = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN);
ghash |= 1LLU << (((uint8_t *)&h)[3] >> 2);
ETHER_NEXT_MULTI(step, enm);
}
}
HWRITE4(sc, ENET_GAUR, (uint32_t)(ghash >> 32));
HWRITE4(sc, ENET_GALR, (uint32_t)ghash);
HWRITE4(sc, ENET_IAUR, (uint32_t)(ihash >> 32));
HWRITE4(sc, ENET_IALR, (uint32_t)ihash);
}
int
fec_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct fec_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
int s, error = 0;
s = splnet();
switch (cmd) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
if (!(ifp->if_flags & IFF_RUNNING))
fec_init(sc);
break;
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
if (ifp->if_flags & IFF_RUNNING)
error = ENETRESET;
else
fec_init(sc);
} else {
if (ifp->if_flags & IFF_RUNNING)
fec_stop(sc);
}
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd);
break;
default:
error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
}
if (error == ENETRESET) {
if (ifp->if_flags & IFF_RUNNING)
fec_iff(sc);
error = 0;
}
splx(s);
return(error);
}
void
fec_start(struct ifnet *ifp)
{
struct fec_softc *sc = ifp->if_softc;
struct mbuf *m = NULL;
int error, idx;
if (!(ifp->if_flags & IFF_RUNNING))
return;
if (ifq_is_oactive(&ifp->if_snd))
return;
if (ifq_empty(&ifp->if_snd))
return;
idx = sc->sc_tx_prod;
while ((sc->sc_txdesc[idx].fd_status & ENET_TXD_READY) == 0) {
m = ifq_deq_begin(&ifp->if_snd);
if (m == NULL)
break;
error = fec_encap(sc, m, &idx);
if (error == ENOBUFS) {
ifq_deq_rollback(&ifp->if_snd, m);
ifq_set_oactive(&ifp->if_snd);
break;
}
if (error == EFBIG) {
ifq_deq_commit(&ifp->if_snd, m);
m_freem(m);
ifp->if_oerrors++;
continue;
}
ifq_deq_commit(&ifp->if_snd, m);
#if NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
#endif
}
if (sc->sc_tx_prod != idx) {
sc->sc_tx_prod = idx;
ifp->if_timer = 5;
}
}
int
fec_encap(struct fec_softc *sc, struct mbuf *m0, int *idx)
{
struct fec_desc *txd, *txd_start;
bus_dmamap_t map;
struct mbuf *m;
int cur, frag, i;
int ret;
m = m0;
cur = frag = *idx;
map = sc->sc_txbuf[cur].fb_map;
if (sc->sc_tx_bounce) {
m = m_dup_pkt(m0, 0, M_DONTWAIT);
if (m == NULL) {
ret = ENOBUFS;
goto fail;
}
}
if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT)) {
if (m_defrag(m, M_DONTWAIT)) {
ret = EFBIG;
goto fail;
}
if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT)) {
ret = EFBIG;
goto fail;
}
}
if (map->dm_nsegs > (ENET_NTXDESC - sc->sc_tx_cnt - 2)) {
bus_dmamap_unload(sc->sc_dmat, map);
ret = ENOBUFS;
goto fail;
}
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
BUS_DMASYNC_PREWRITE);
txd = txd_start = &sc->sc_txdesc[frag];
for (i = 0; i < map->dm_nsegs; i++) {
txd->fd_addr = map->dm_segs[i].ds_addr;
txd->fd_len = map->dm_segs[i].ds_len;
txd->fd_status &= ENET_TXD_WRAP;
if (i == (map->dm_nsegs - 1))
txd->fd_status |= ENET_TXD_LAST | ENET_TXD_TC;
if (i != 0)
txd->fd_status |= ENET_TXD_READY;
bus_dmamap_sync(sc->sc_dmat, ENET_DMA_MAP(sc->sc_txring),
frag * sizeof(*txd), sizeof(*txd), BUS_DMASYNC_PREWRITE);
cur = frag;
if (frag == (ENET_NTXDESC - 1)) {
txd = &sc->sc_txdesc[0];
frag = 0;
} else {
txd++;
frag++;
}
KASSERT(frag != sc->sc_tx_cons);
}
txd_start->fd_status |= ENET_TXD_READY;
bus_dmamap_sync(sc->sc_dmat, ENET_DMA_MAP(sc->sc_txring),
*idx * sizeof(*txd), sizeof(*txd), BUS_DMASYNC_PREWRITE);
HWRITE4(sc, ENET_TDAR, ENET_TDAR_TDAR);
KASSERT(sc->sc_txbuf[cur].fb_m == NULL);
KASSERT(sc->sc_txbuf[cur].fb_m0 == NULL);
sc->sc_txbuf[*idx].fb_map = sc->sc_txbuf[cur].fb_map;
sc->sc_txbuf[cur].fb_map = map;
sc->sc_txbuf[cur].fb_m = m;
if (m != m0)
sc->sc_txbuf[cur].fb_m0 = m0;
sc->sc_tx_cnt += map->dm_nsegs;
*idx = frag;
return (0);
fail:
if (m != m0)
m_freem(m);
return (ret);
}
int
fec_intr(void *arg)
{
struct fec_softc *sc = arg;
struct ifnet *ifp = &sc->sc_ac.ac_if;
u_int32_t status;
status = HREAD4(sc, ENET_EIR);
status &= (ENET_EIR_RXF | ENET_EIR_TXF);
HWRITE4(sc, ENET_EIR, status);
if (ISSET(status, ENET_EIR_RXF))
fec_rx_proc(sc);
if (ISSET(status, ENET_EIR_TXF))
fec_tx_proc(sc);
if (ifp->if_flags & IFF_RUNNING && !ifq_empty(&ifp->if_snd))
fec_start(ifp);
return 1;
}
void
fec_tx_proc(struct fec_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct fec_desc *txd;
struct fec_buf *txb;
int idx;
bus_dmamap_sync(sc->sc_dmat, ENET_DMA_MAP(sc->sc_txring), 0,
ENET_DMA_LEN(sc->sc_txring),
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
while (sc->sc_tx_cnt > 0) {
idx = sc->sc_tx_cons;
KASSERT(idx < ENET_NTXDESC);
txd = &sc->sc_txdesc[idx];
if (txd->fd_status & ENET_TXD_READY)
break;
txb = &sc->sc_txbuf[idx];
if (txb->fb_m) {
bus_dmamap_sync(sc->sc_dmat, txb->fb_map, 0,
txb->fb_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_dmat, txb->fb_map);
m_freem(txb->fb_m);
m_freem(txb->fb_m0);
txb->fb_m = txb->fb_m0 = NULL;
}
ifq_clr_oactive(&ifp->if_snd);
sc->sc_tx_cnt--;
if (sc->sc_tx_cons == (ENET_NTXDESC - 1))
sc->sc_tx_cons = 0;
else
sc->sc_tx_cons++;
txd->fd_status &= ENET_TXD_WRAP;
}
if (sc->sc_tx_cnt == 0)
ifp->if_timer = 0;
else
HWRITE4(sc, ENET_TDAR, ENET_TDAR_TDAR);
}
void
fec_rx_proc(struct fec_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct fec_desc *rxd;
struct fec_buf *rxb;
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
struct mbuf *m;
int idx, len;
if ((ifp->if_flags & IFF_RUNNING) == 0)
return;
bus_dmamap_sync(sc->sc_dmat, ENET_DMA_MAP(sc->sc_rxring), 0,
ENET_DMA_LEN(sc->sc_rxring),
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
while (if_rxr_inuse(&sc->sc_rx_ring) > 0) {
idx = sc->sc_rx_cons;
KASSERT(idx < ENET_NRXDESC);
rxd = &sc->sc_rxdesc[idx];
if (rxd->fd_status & ENET_RXD_EMPTY)
break;
len = rxd->fd_len;
rxb = &sc->sc_rxbuf[idx];
KASSERT(rxb->fb_m);
bus_dmamap_sync(sc->sc_dmat, rxb->fb_map, 0,
len, BUS_DMASYNC_POSTREAD);
bus_dmamap_unload(sc->sc_dmat, rxb->fb_map);
len -= ETHER_CRC_LEN;
KASSERT(len > 0);
m = rxb->fb_m;
rxb->fb_m = NULL;
m_adj(m, ETHER_ALIGN);
m->m_pkthdr.len = m->m_len = len;
ml_enqueue(&ml, m);
if_rxr_put(&sc->sc_rx_ring, 1);
if (sc->sc_rx_cons == (ENET_NRXDESC - 1))
sc->sc_rx_cons = 0;
else
sc->sc_rx_cons++;
}
if (ifiq_input(&ifp->if_rcv, &ml))
if_rxr_livelocked(&sc->sc_rx_ring);
fec_fill_rx_ring(sc);
bus_dmamap_sync(sc->sc_dmat, ENET_DMA_MAP(sc->sc_rxring), 0,
ENET_DMA_LEN(sc->sc_rxring),
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
HWRITE4(sc, ENET_RDAR, ENET_RDAR_RDAR);
}
void
fec_tick(void *arg)
{
struct fec_softc *sc = arg;
int s;
s = splnet();
mii_tick(&sc->sc_mii);
splx(s);
timeout_add_sec(&sc->sc_tick, 1);
}
int
fec_miibus_readreg(struct device *dev, int phy, int reg)
{
int r = 0;
struct fec_softc *sc = (struct fec_softc *)dev;
HWRITE4(sc, ENET_EIR, ENET_EIR_MII);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, ENET_MMFR,
ENET_MMFR_ST | ENET_MMFR_OP_RD | ENET_MMFR_TA |
phy << ENET_MMFR_PA_SHIFT | reg << ENET_MMFR_RA_SHIFT);
while(!(HREAD4(sc, ENET_EIR) & ENET_EIR_MII));
r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, ENET_MMFR);
return (r & 0xffff);
}
void
fec_miibus_writereg(struct device *dev, int phy, int reg, int val)
{
struct fec_softc *sc = (struct fec_softc *)dev;
HWRITE4(sc, ENET_EIR, ENET_EIR_MII);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, ENET_MMFR,
ENET_MMFR_ST | ENET_MMFR_OP_WR | ENET_MMFR_TA |
phy << ENET_MMFR_PA_SHIFT | reg << ENET_MMFR_RA_SHIFT |
(val & 0xffff));
while(!(HREAD4(sc, ENET_EIR) & ENET_EIR_MII));
return;
}
void
fec_miibus_statchg(struct device *dev)
{
struct fec_softc *sc = (struct fec_softc *)dev;
uint32_t ecr, rcr;
ecr = HREAD4(sc, ENET_ECR) & ~ENET_ECR_SPEED;
rcr = HREAD4(sc, ENET_RCR) & ~ENET_RCR_RMII_10T;
switch (IFM_SUBTYPE(sc->sc_mii.mii_media_active)) {
case IFM_1000_T:
ecr |= ENET_ECR_SPEED;
break;
case IFM_100_TX:
break;
case IFM_10_T:
rcr |= ENET_RCR_RMII_10T;
break;
}
HWRITE4(sc, ENET_ECR, ecr);
HWRITE4(sc, ENET_RCR, rcr);
return;
}
int
fec_ifmedia_upd(struct ifnet *ifp)
{
struct fec_softc *sc = ifp->if_softc;
struct mii_data *mii = &sc->sc_mii;
int err;
if (mii->mii_instance) {
struct mii_softc *miisc;
LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
mii_phy_reset(miisc);
}
err = mii_mediachg(mii);
return (err);
}
void
fec_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct fec_softc *sc = ifp->if_softc;
struct mii_data *mii = &sc->sc_mii;
mii_pollstat(mii);
ifmr->ifm_active = mii->mii_media_active;
ifmr->ifm_status = mii->mii_media_status;
}
struct fec_dmamem *
fec_dmamem_alloc(struct fec_softc *sc, bus_size_t size, bus_size_t align)
{
struct fec_dmamem *fdm;
int nsegs;
fdm = malloc(sizeof(*fdm), M_DEVBUF, M_WAITOK | M_ZERO);
fdm->fdm_size = size;
if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &fdm->fdm_map) != 0)
goto fdmfree;
if (bus_dmamem_alloc(sc->sc_dmat, size, align, 0, &fdm->fdm_seg, 1,
&nsegs, BUS_DMA_WAITOK) != 0)
goto destroy;
if (bus_dmamem_map(sc->sc_dmat, &fdm->fdm_seg, nsegs, size,
&fdm->fdm_kva, BUS_DMA_WAITOK | BUS_DMA_COHERENT) != 0)
goto free;
if (bus_dmamap_load(sc->sc_dmat, fdm->fdm_map, fdm->fdm_kva, size,
NULL, BUS_DMA_WAITOK) != 0)
goto unmap;
return (fdm);
unmap:
bus_dmamem_unmap(sc->sc_dmat, fdm->fdm_kva, size);
free:
bus_dmamem_free(sc->sc_dmat, &fdm->fdm_seg, 1);
destroy:
bus_dmamap_destroy(sc->sc_dmat, fdm->fdm_map);
fdmfree:
free(fdm, M_DEVBUF, sizeof(*fdm));
return (NULL);
}
void
fec_dmamem_free(struct fec_softc *sc, struct fec_dmamem *fdm)
{
bus_dmamem_unmap(sc->sc_dmat, fdm->fdm_kva, fdm->fdm_size);
bus_dmamem_free(sc->sc_dmat, &fdm->fdm_seg, 1);
bus_dmamap_destroy(sc->sc_dmat, fdm->fdm_map);
free(fdm, M_DEVBUF, sizeof(*fdm));
}
struct mbuf *
fec_alloc_mbuf(struct fec_softc *sc, bus_dmamap_t map)
{
struct mbuf *m = NULL;
m = MCLGETL(NULL, M_DONTWAIT, MCLBYTES);
if (!m)
return (NULL);
m->m_len = m->m_pkthdr.len = MCLBYTES;
if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT) != 0) {
printf("%s: could not load mbuf DMA map",
sc->sc_dev.dv_xname);
m_freem(m);
return (NULL);
}
bus_dmamap_sync(sc->sc_dmat, map, 0,
m->m_pkthdr.len, BUS_DMASYNC_PREREAD);
return (m);
}
void
fec_fill_rx_ring(struct fec_softc *sc)
{
struct fec_desc *rxd;
struct fec_buf *rxb;
u_int slots;
for (slots = if_rxr_get(&sc->sc_rx_ring, ENET_NRXDESC);
slots > 0; slots--) {
rxb = &sc->sc_rxbuf[sc->sc_rx_prod];
rxb->fb_m = fec_alloc_mbuf(sc, rxb->fb_map);
if (rxb->fb_m == NULL)
break;
rxd = &sc->sc_rxdesc[sc->sc_rx_prod];
rxd->fd_len = rxb->fb_map->dm_segs[0].ds_len - 1;
rxd->fd_addr = rxb->fb_map->dm_segs[0].ds_addr;
rxd->fd_status &= ENET_RXD_WRAP;
rxd->fd_status |= ENET_RXD_EMPTY;
if (sc->sc_rx_prod == (ENET_NRXDESC - 1))
sc->sc_rx_prod = 0;
else
sc->sc_rx_prod++;
}
if_rxr_put(&sc->sc_rx_ring, slots);
}