Hi, kind of embarassing, but i guess i originally went w/what was enough to get booting/building over eth, and i blindly followed freebsd driver more than i should of - as it is clearly nothing but copypaste with even comments included from linux.. yep.
so sxie has these bugs like trying to flush via rx filter control register etc., just like every other driver(free,net,linux) i've seen for allwinner emac still do. mostly fixed in the diff below, that is huge, i know, but anyone is free to pick what they want, if anything. :) i used UM10326.pdf by NXP for ie. the _softreset() below, more specifically "Chapter 14: LPC32x0 Ethernet Media Access Controller (MAC)". freebsd has driver for it too, but the "scatter gather dma engines" in it were different enough to what exists for sun4i emac, so i gave up trying to fit the pieces before getting hands dirty. -Artturi diff --git a/sys/arch/armv7/sunxi/sxie.c b/sys/arch/armv7/sunxi/sxie.c index 116fda5f8d7..e428425d7f8 100644 --- a/sys/arch/armv7/sunxi/sxie.c +++ b/sys/arch/armv7/sunxi/sxie.c @@ -51,105 +51,182 @@ #include <dev/ofw/openfirm.h> #include <dev/ofw/ofw_clock.h> #include <dev/ofw/ofw_pinctrl.h> +#include <dev/ofw/ofw_regulator.h> +#include <dev/ofw/ofw_misc.h> #include <dev/ofw/fdt.h> -/* configuration registers */ -#define SXIE_CR 0x0000 +#if defined(DEBUG) || defined(DIAGNOSTIC) +#define SXIE_CD (ifp->if_flags & IFF_DEBUG) +#define SXIEDBG(x) do { if (SXIE_CD) printf((x)); } while (0) +#else +#define SXIE_CD (0) +#define SXIEDBG(x) do { } while (0) +#endif + +/* control registers */ +#define SXIE_CMDR 0x0000 #define SXIE_TXMODE 0x0004 -#define SXIE_TXFLOW 0x0008 +#define SXIE_TXFLOW 0x0008 /* unused */ #define SXIE_TXCR0 0x000c #define SXIE_TXCR1 0x0010 #define SXIE_TXINS 0x0014 +/* + * XXX was wondering, what if these TXPKTLEN registers + * are actually part of memory mapped tx 'fake' descriptors, + * something like w/DMA but only via register map or something? + * should add some dbg and try to figure out:) + */ #define SXIE_TXPKTLEN0 0x0018 #define SXIE_TXPKTLEN1 0x001c -#define SXIE_TXSR 0x0020 +#define SXIE_TXSR 0x0020 /* unused */ #define SXIE_TXIO0 0x0024 -#define SXIE_TXIO1 0x0028 -#define SXIE_TXTSVL0 0x002c -#define SXIE_TXTSVH0 0x0030 -#define SXIE_TXTSVL1 0x0034 -#define SXIE_TXTSVH1 0x0038 -#define SXIE_RXCR 0x003c +#define SXIE_TXIO1 0x0028 /* unused */ +#define SXIE_TXTSVL0 0x002c /* unused */ +#define SXIE_TXTSVH0 0x0030 /* unused */ +#define SXIE_TXTSVL1 0x0034 /* unused */ +#define SXIE_TXTSVH1 0x0038 /* unused */ + +/* receive filter registers */ +#define SXIE_RXFILTER_CTRL 0x003c #define SXIE_RXHASH0 0x0040 #define SXIE_RXHASH1 0x0044 -#define SXIE_RXSR 0x0048 -#define SXIE_RXIO 0x004C + +/* dunno.. */ +#define SXIE_RXSR 0x0048 /* unused */ +#define SXIE_RXIO 0x004c #define SXIE_RXFBC 0x0050 + +/* module control registers */ #define SXIE_INTCR 0x0054 #define SXIE_INTSR 0x0058 -#define SXIE_MACCR0 0x005C -#define SXIE_MACCR1 0x0060 -#define SXIE_MACIPGT 0x0064 -#define SXIE_MACIPGR 0x0068 -#define SXIE_MACCLRT 0x006C -#define SXIE_MACMFL 0x0070 -#define SXIE_MACSUPP 0x0074 -#define SXIE_MACTEST 0x0078 -#define SXIE_MACMCFG 0x007C -#define SXIE_MACMCMD 0x0080 -#define SXIE_MACMADR 0x0084 -#define SXIE_MACMWTD 0x0088 -#define SXIE_MACMRDD 0x008C -#define SXIE_MACMIND 0x0090 -#define SXIE_MACSSRR 0x0094 -#define SXIE_MACA0 0x0098 -#define SXIE_MACA1 0x009c -#define SXIE_MACA2 0x00a0 + +/* ethernet MAC registers */ +#define SXIE_MAC0 0x005c +#define SXIE_MAC1 0x0060 +#define SXIE_MAC_IPGT 0x0064 +#define SXIE_MAC_IPGR 0x0068 +#define SXIE_MAC_CLRT 0x006c +#define SXIE_MAC_MAXF 0x0070 +#define SXIE_MAC_SUPP 0x0074 +#define SXIE_MAC_TEST 0x0078 /* unused */ +#define SXIE_MAC_MCFG 0x007c +#define SXIE_MAC_MCMD 0x0080 +#define SXIE_MAC_MADR 0x0084 +#define SXIE_MAC_MWTD 0x0088 +#define SXIE_MAC_MRDD 0x008c +#define SXIE_MAC_MIND 0x0090 +#define SXIE_MACSSRR 0x0094 /* unused */ +#define SXIE_MAC_SA0 0x0098 +#define SXIE_MAC_SA1 0x009c +#define SXIE_MACA2 0x00a0 /* unused */ /* i once spent hours on pretty defines, cvs up ate 'em. these shall do */ -#define SXIE_INTR_ENABLE 0x010f +#define SXIE_INTR_ENABLE 0x010f #define SXIE_INTR_DISABLE 0x0000 #define SXIE_INTR_CLEAR 0x0000 -#define SXIE_TX_FIFO0 0x0001 -#define SXIE_TX_FIFO1 0x0002 - #define SXIE_RX_ENABLE 0x0004 #define SXIE_TX_ENABLE 0x0003 -#define SXIE_RXTX_ENABLE 0x0007 +#define SXIE_RXTX_ENABLE 0x0007 #define SXIE_RXDRQM 0x0002 #define SXIE_RXTM 0x0004 -#define SXIE_RXFLUSH 0x0008 -#define SXIE_RXPA 0x0010 -#define SXIE_RXPCF 0x0020 -#define SXIE_RXPCRCE 0x0040 -#define SXIE_RXPLE 0x0080 -#define SXIE_RXPOR 0x0100 -#define SXIE_RXUCAD 0x10000 -#define SXIE_RXDAF 0x20000 -#define SXIE_RXMCO 0x100000 -#define SXIE_RXMHF 0x200000 -#define SXIE_RXBCO 0x400000 -#define SXIE_RXSAF 0x1000000 -#define SXIE_RXSAIF 0x2000000 - -#define SXIE_MACRXFC 0x0004 -#define SXIE_MACTXFC 0x0008 -#define SXIE_MACSOFTRESET 0x8000 - -#define SXIE_MACDUPLEX 0x0001 /* full = 1 */ -#define SXIE_MACFLC 0x0002 -#define SXIE_MACHF 0x0004 -#define SXIE_MACDCRC 0x0008 -#define SXIE_MACCRC 0x0010 -#define SXIE_MACPC 0x0020 -#define SXIE_MACVC 0x0040 -#define SXIE_MACADP 0x0080 -#define SXIE_MACPRE 0x0100 -#define SXIE_MACLPE 0x0200 -#define SXIE_MACNB 0x1000 -#define SXIE_MACBNB 0x2000 -#define SXIE_MACED 0x4000 - -#define SXIE_RX_ERRLENOOR 0x0040 -#define SXIE_RX_ERRLENCHK 0x0020 -#define SXIE_RX_ERRCRC 0x0010 -#define SXIE_RX_ERRRCV 0x0008 /* XXX receive code violation ? */ + + /* 0x00 */ +#define SXIE_CMDR_RXENABLE (1 << 0) +#define SXIE_CMDR_TXENABLE (1 << 1) +#define SXIE_CMDR_REGRESET (1 << 3) +#define SXIE_CMDR_TXRESET (1 << 4) +#define SXIE_CMDR_RXRESET (1 << 5) +#define SXIE_CMDR_PASSRUNTFRAME (1 << 6) +#define SXIE_CMDR_PASSRXFILTER (1 << 7) +#define SXIE_CMDR_TXFLOWCTL (1 << 8) +#define SXIE_CMDR_RMII (1 << 9) +#define SXIE_CMDR_FULLDUPLEX (1 << 10) + + /* 0x3c */ +#define SXIE_RXFILTER_PROMISC (1 << 4) +#define SXIE_RXFILTER_CONTROLFRAMES (1 << 5) +#define SXIE_RXFILTER_FRAMECRCERR (1 << 6) +#define SXIE_RXFILTER_FRAMELENERR (1 << 7) +#define SXIE_RXFILTER_FRAMELENOOR (1 << 8) +#define SXIE_RXFILTER_UNICAST (1 << 16) +#define SXIE_RXFILTER_DAFILTER (1 << 17) +#define SXIE_RXFILTER_MULTICAST (1 << 20) +#define SXIE_RXFILTER_MHASHFILTER (1 << 21) +#define SXIE_RXFILTER_BROADCAST (1 << 22) +#define SXIE_RXFILTER_SAFILTER (1 << 24) +#define SXIE_RXFILTER_SAINVFILTER (1 << 25) + + /* 0x5c */ +#define SXIE_MAC0_RXENABLE (1 << 0) +#define SXIE_MAC0_PASSALL (1 << 1) +#define SXIE_MAC0_RXFLOWCTRL (1 << 2) +#define SXIE_MAC0_TXFLOWCTRL (1 << 3) +#define SXIE_MAC0_LOOPBACK (1 << 4) +#define SXIE_MAC0_RESETTX (1 << 8) +#define SXIE_MAC0_RESETMCSTX (1 << 9) +#define SXIE_MAC0_RESETRX (1 << 10) +#define SXIE_MAC0_RESETMCSRX (1 << 11) +#define SXIE_MAC0_SOFTRESET (1 << 15) + /* 0x60 */ +#define SXIE_MAC1_FULLDUPLEX (1 << 0) +#define SXIE_MAC1_FRAMELENCHECK (1 << 1) +#define SXIE_MAC1_HUGEFRAME (1 << 2) +#define SXIE_MAC1_DELAYEDCRC (1 << 3) +#define SXIE_MAC1_CRC (1 << 4) +#define SXIE_MAC1_PADCRC (1 << 5) +#define SXIE_MAC1_VLANPAD (1 << 6) +#define SXIE_MAC1_AUTOPAD (1 << 7) +#define SXIE_MAC1_PUREPREAMBLE (1 << 8) +#define SXIE_MAC1_LONGPREAMBLE (1 << 9) +#define SXIE_MAC1_NOBACKOFF (1 << 12) +#define SXIE_MAC1_BACKPRESSURE (1 << 13) +#define SXIE_MAC1_EXCESSDEFER (1 << 14) + /* 0x64 */ +#define SXIE_MAC_BTB_IPG(x) ((x) & 0x7f) + /* 0x68 */ +#define SXIE_MAC_NBTB_IGP_P2(x) ((x) & 0x7f) +#define SXIE_MAC_NBTB_IGP_P1(x) (((x) & 0x7f) << 8) + /* 0x6c */ +#define SXIE_MAC_CLRT_RETRANSMAX 0x0f +#define SXIE_MAC_CLRT_COLLWIND(x) (((x) & 0x3f) << 8) + /* 0x70 */ +#define SXIE_MAC_MAXF_MAXFRAMELENMASK 0xffff + /* 0x74 */ +#define SXIE_MAC_SUPP_SPEED (1 << 8) + /* 0x78 */ +#define SXIE_MAC_TEST_SHORTCPAUSEQUANTA (1 << 0) +#define SXIE_MAC_TEST_TESTPAUSE (1 << 1) +#define SXIE_MAC_TEST_TESTBACKPRESSURE (1 << 2) + /* 0x7c */ +#define SXIE_MAC_MCFG_SCANINCR (1 << 0) +#define SXIE_MAC_MCFG_SUPPREAMBLE (1 << 1) +#define SXIE_MAC_MCFG_CLKSEL(_n) (((_n) & 0xf) << 2) +#define SXIE_MAC_MCFG_RESET_MII (1 << 15) + /* 0x80 */ +#define SXIE_MAC_MCMD_READ (1 << 0) +#define SXIE_MAC_MCMD_WRITE (0 << 0) +#define SXIE_MAC_MCMD_SCAN (1 << 1) + /* 0x90 */ +#define SXIE_MAC_MIND_BUSY (1 << 0) +#define SXIE_MAC_MIND_SCANNING (1 << 1) +#define SXIE_MAC_MIND_INVALID (1 << 2) +#define SXIE_MAC_MIND_MIIFAIL (1 << 3) + +#define SXIE_TX_FIFO0 0x0001 +#define SXIE_TX_FIFO1 0x0002 +#define SXIE_TX_FIFOS (SXIE_TX_FIFO0 | SXIE_TX_FIFO1) + +#define SXIE_SRST_RX (1 << 0) +#define SXIE_SRST_TX (1 << 1) +#define SXIE_SRST_FULL (1 << 2) + #define SXIE_RX_ERRMASK 0x0070 #define SXIE_MII_TIMEOUT 100 #define SXIE_MAX_RXD 8 +#define SXIE_MAX_TXD 2 #define SXIE_MAX_PKT_SIZE ETHER_MAX_DIX_LEN #define SXIE_ROUNDUP(size, unit) (((size) + (unit) - 1) & ~((unit) - 1)) @@ -158,20 +235,26 @@ struct sxie_softc { struct device sc_dev; struct arpcom sc_ac; struct mii_data sc_mii; + int sc_node; int sc_phyno; + int sc_dmanode; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; - bus_space_handle_t sc_sid_ioh; + bus_dma_tag_t sc_dmat; void *sc_ih; /* Interrupt handler */ - uint32_t intr_status; /* soft interrupt status */ - uint32_t pauseframe; - uint32_t txf_inuse; -}; -struct sxie_softc *sxie_sc; + struct mutex sc_miibus_lock; + struct timeout sc_tick; + + uint32_t sc_pauseframe; + uint32_t sc_tx_timer; + uint32_t sc_tx_inuse; +}; int sxie_match(struct device *, void *, void *); void sxie_attach(struct device *, struct device *, void *); +void sxie_get_lladdr(struct sxie_softc *); +void sxie_set_lladdr(struct sxie_softc *); void sxie_setup_interface(struct sxie_softc *, struct device *); void sxie_socware_init(struct sxie_softc *); int sxie_ioctl(struct ifnet *, u_long, caddr_t); @@ -180,7 +263,7 @@ void sxie_watchdog(struct ifnet *); void sxie_init(struct sxie_softc *); void sxie_stop(struct sxie_softc *); void sxie_reset(struct sxie_softc *); -void sxie_iff(struct sxie_softc *, struct ifnet *); +void sxie_iff(struct sxie_softc *); int sxie_intr(void *); void sxie_recv(struct sxie_softc *); int sxie_miibus_readreg(struct device *, int, int); @@ -188,6 +271,10 @@ void sxie_miibus_writereg(struct device *, int, int, int); void sxie_miibus_statchg(struct device *); int sxie_ifm_change(struct ifnet *); void sxie_ifm_status(struct ifnet *, struct ifmediareq *); +void sxie_tick(void *); + +static inline void + sxie_softreset(struct sxie_softc *, uint32_t); struct cfattach sxie_ca = { sizeof (struct sxie_softc), sxie_match, sxie_attach @@ -210,57 +297,81 @@ sxie_attach(struct device *parent, struct device *self, void *aux) { struct sxie_softc *sc = (struct sxie_softc *) self; struct fdt_attach_args *faa = aux; - struct mii_data *mii; - struct ifnet *ifp; + struct mii_data *mii = &sc->sc_mii; + struct ifnet *ifp = &sc->sc_ac.ac_if; + int phynode, physupply; int s; if (faa->fa_nreg < 1) return; - pinctrl_byname(faa->fa_node, "default"); - sc->sc_iot = faa->fa_iot; + sc->sc_dmat = faa->fa_dmat; + sc->sc_node = faa->fa_node; if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, - faa->fa_reg[0].size, 0, &sc->sc_ioh)) - panic("sxie_attach: bus_space_map ioh failed!"); + faa->fa_reg[0].size, 0, &sc->sc_ioh)) { + printf("%s: bus_space_map ioh failed!\n", + sc->sc_dev.dv_xname); + return; + } + + mtx_init(&sc->sc_miibus_lock, IPL_NET); + timeout_set(&sc->sc_tick, sxie_tick, sc); - if (bus_space_map(sc->sc_iot, SID_ADDR, SID_SIZE, 0, &sc->sc_sid_ioh)) - panic("sxie_attach: bus_space_map sid_ioh failed!"); + pinctrl_byname(faa->fa_node, "default"); clock_enable_all(faa->fa_node); - sxie_socware_init(sc); - sc->txf_inuse = 0; + /* Power up PHY. */ + physupply = OF_getpropint(faa->fa_node, "phy-supply", 0); + if (physupply) + regulator_enable(physupply); sc->sc_ih = arm_intr_establish_fdt(faa->fa_node, IPL_NET, sxie_intr, sc, sc->sc_dev.dv_xname); + if (sc->sc_ih == NULL) + goto failout; - s = splnet(); + /* Lookup PHY. */ + phynode = OF_getnodebyphandle(OF_getpropint(sc->sc_node, "phy", 0)); + if (phynode) + sc->sc_phyno = OF_getpropint(phynode, "reg", MII_PHY_ANY); + else + sc->sc_phyno = MII_PHY_ANY; + + /* + * try to get&set lladdr in order: + * a) devicetree + * b) current + * c) better than random (from SID rootkey registers) + * d) ether_fakeaddr + */ + sxie_get_lladdr(sc); printf(", address %s\n", ether_sprintf(sc->sc_ac.ac_enaddr)); - /* XXX verify flags & capabilities */ - ifp = &sc->sc_ac.ac_if; + s = splnet(); 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 = sxie_ioctl; ifp->if_start = sxie_start; ifp->if_watchdog = sxie_watchdog; + /* XXX can do CRC too */ ifp->if_capabilities = IFCAP_VLAN_MTU; /* XXX status check in recv? */ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); /* Initialize MII/media info. */ - mii = &sc->sc_mii; mii->mii_ifp = ifp; mii->mii_readreg = sxie_miibus_readreg; mii->mii_writereg = sxie_miibus_writereg; mii->mii_statchg = sxie_miibus_statchg; ifmedia_init(&mii->mii_media, 0, sxie_ifm_change, sxie_ifm_status); - mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0); + mii_attach(self, mii, 0xffffffff, sc->sc_phyno, MII_OFFSET_ANY, + MIIF_DOPAUSE); if (LIST_FIRST(&mii->mii_phys) == NULL) { ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL); @@ -272,56 +383,125 @@ sxie_attach(struct device *parent, struct device *self, void *aux) ether_ifattach(ifp); splx(s); - sxie_sc = sc; + return; +failout: + if (physupply) + regulator_disable(physupply); + clock_disable_all(faa->fa_node); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size); } void -sxie_socware_init(struct sxie_softc *sc) +sxie_softreset(struct sxie_softc *sc, uint32_t rx_tx_full) { - int have_mac = 0; - uint32_t reg; + uint32_t mac0_clr, mac0_set; + uint32_t cmdr_clr, cmdr_set; - /* MII clock cfg */ - SXICMS4(sc, SXIE_MACMCFG, 15 << 2, 13 << 2); + rx_tx_full &= 0x07; - SXIWRITE4(sc, SXIE_INTCR, SXIE_INTR_DISABLE); - SXISET4(sc, SXIE_INTSR, SXIE_INTR_CLEAR); + switch (rx_tx_full) { + case SXIE_SRST_RX: + mac0_clr = SXIE_MAC0_RXENABLE; + mac0_set = SXIE_MAC0_RESETMCSRX; + cmdr_clr = SXIE_CMDR_RXENABLE; + cmdr_set = SXIE_CMDR_RXRESET; + break; - /* - * If u-boot doesn't set emac, use the Security ID area - * to generate a consistent MAC address of. - */ - reg = SXIREAD4(sc, SXIE_MACA0); - if (reg != 0) { - sc->sc_ac.ac_enaddr[3] = reg >> 16 & 0xff; - sc->sc_ac.ac_enaddr[4] = reg >> 8 & 0xff; - sc->sc_ac.ac_enaddr[5] = reg & 0xff; - reg = SXIREAD4(sc, SXIE_MACA1); - sc->sc_ac.ac_enaddr[0] = reg >> 16 & 0xff; - sc->sc_ac.ac_enaddr[1] = reg >> 8 & 0xff; - sc->sc_ac.ac_enaddr[2] = reg & 0xff; - - have_mac = 1; + case SXIE_SRST_TX: + mac0_clr = /* XXX txflowcontrol? */0; + mac0_set = SXIE_MAC0_RESETMCSTX; + cmdr_clr = SXIE_CMDR_TXENABLE; + cmdr_set = SXIE_CMDR_TXRESET; + break; + + default: + case SXIE_SRST_FULL: + mac0_clr = SXIE_MAC0_RXENABLE; + mac0_set = SXIE_MAC0_SOFTRESET; + cmdr_clr = SXIE_CMDR_RXENABLE|SXIE_CMDR_TXENABLE; + cmdr_set = SXIE_CMDR_REGRESET; + break; } - reg = bus_space_read_4(sc->sc_iot, sc->sc_sid_ioh, 0x0); + /* disable, flush(reset), enable */ + SXICLR4(sc, SXIE_MAC0, mac0_clr); + SXICLR4(sc, SXIE_CMDR, cmdr_clr); - if (!have_mac && reg != 0) { - sc->sc_ac.ac_enaddr[0] = 0x02; - sc->sc_ac.ac_enaddr[1] = reg & 0xff; - reg = bus_space_read_4(sc->sc_iot, sc->sc_sid_ioh, 0x0c); - sc->sc_ac.ac_enaddr[2] = reg >> 24 & 0xff; - sc->sc_ac.ac_enaddr[3] = reg >> 16 & 0xff; - sc->sc_ac.ac_enaddr[4] = reg >> 8 & 0xff; - sc->sc_ac.ac_enaddr[5] = reg & 0xff; + SXISET4(sc, SXIE_MAC0, mac0_set); + SXISET4(sc, SXIE_CMDR, cmdr_set); + while (SXIREAD4(sc, SXIE_CMDR) & cmdr_set) + delay(10); - have_mac = 1; + if (rx_tx_full != SXIE_SRST_FULL) { + SXICLR4(sc, SXIE_MAC0, mac0_set); + + SXISET4(sc, SXIE_CMDR, cmdr_clr); + SXISET4(sc, SXIE_MAC0, mac0_clr); + } else { +#if 0 + sxie_setup_interface(sc); + SXICLR4(sc, SXIE_MAC0, mac0_set); +#else + return; +#endif } +} - if (!have_mac) - ether_fakeaddr(&sc->sc_ac.ac_if); +void +sxie_get_lladdr(struct sxie_softc *sc) +{ + uint8_t *enaddr = &sc->sc_ac.ac_enaddr[0]; + bus_space_handle_t sid_ioh; + uint32_t reg; + int eaplen; - sc->sc_phyno = 1; + eaplen = OF_getproplen(sc->sc_node, "local-mac-address"); + if (eaplen == ETHER_ADDR_LEN) { + OF_getprop(sc->sc_node, "local-mac-address", enaddr, eaplen); + return; + } else if ((reg = SXIREAD4(sc, SXIE_MAC_SA0)) != 0) { + enaddr[3] = reg >> 16 & 0xff; + enaddr[4] = reg >> 8 & 0xff; + enaddr[5] = reg & 0xff; + reg = SXIREAD4(sc, SXIE_MAC_SA1); + enaddr[0] = reg >> 16 & 0xff; + enaddr[1] = reg >> 8 & 0xff; + enaddr[2] = reg & 0xff; + return; + } else + if (bus_space_map(sc->sc_iot, SID_ADDR, SID_SIZE, 0, &sid_ioh) != 0) { + if ((reg = bus_space_read_4(sc->sc_iot, sid_ioh, 0x0)) != 0) { + /* + * If U-Boot doesn't set MAC, use the Security ID + * area to generate a consistent MAC address of. + */ + enaddr[0] = 0x02; + enaddr[1] = reg & 0xff; + reg = bus_space_read_4(sc->sc_iot, sid_ioh, 0x0c); + enaddr[2] = reg >> 24 & 0xff; + enaddr[3] = reg >> 16 & 0xff; + enaddr[4] = reg >> 8 & 0xff; + enaddr[5] = reg & 0xff; + } + bus_space_unmap(sc->sc_iot, sid_ioh, SID_SIZE); + if (reg != 0) + return; + } + ether_fakeaddr(&sc->sc_ac.ac_if); +} + +void +sxie_set_lladdr(struct sxie_softc *sc) +{ + /* set lladdr */ + SXIWRITE4(sc, SXIE_MAC_SA0, + sc->sc_ac.ac_enaddr[3] << 16 | + sc->sc_ac.ac_enaddr[4] << 8 | + sc->sc_ac.ac_enaddr[5]); + SXIWRITE4(sc, SXIE_MAC_SA1, + sc->sc_ac.ac_enaddr[0] << 16 | + sc->sc_ac.ac_enaddr[1] << 8 | + sc->sc_ac.ac_enaddr[2]); } void @@ -329,47 +509,46 @@ sxie_setup_interface(struct sxie_softc *sc, struct device *dev) { uint32_t clr_m, set_m; + /* MII host clock div */ + SXICMS4(sc, SXIE_MAC_MCFG, + SXIE_MAC_MCFG_CLKSEL(15), SXIE_MAC_MCFG_CLKSEL(13)); + /* configure TX */ SXICMS4(sc, SXIE_TXMODE, 3, 1); /* cpu mode */ - /* configure RX */ - clr_m = SXIE_RXDRQM | SXIE_RXTM | SXIE_RXPA | SXIE_RXPCF | - SXIE_RXPCRCE | SXIE_RXPLE | SXIE_RXMHF | SXIE_RXSAF | - SXIE_RXSAIF; - set_m = SXIE_RXPOR | SXIE_RXUCAD | SXIE_RXDAF | SXIE_RXBCO; - SXICMS4(sc, SXIE_RXCR, clr_m, set_m); + /* configure RX filtering */ + clr_m = SXIE_RXDRQM | SXIE_RXTM | + SXIE_RXFILTER_PROMISC | SXIE_RXFILTER_CONTROLFRAMES | + SXIE_RXFILTER_FRAMECRCERR | SXIE_RXFILTER_FRAMELENERR | + SXIE_RXFILTER_MHASHFILTER | SXIE_RXFILTER_SAFILTER | + SXIE_RXFILTER_SAINVFILTER; + set_m = SXIE_RXFILTER_FRAMELENOOR | SXIE_RXFILTER_DAFILTER | + SXIE_RXFILTER_BROADCAST; + SXICMS4(sc, SXIE_RXFILTER_CTRL, clr_m, set_m); /* configure MAC */ - SXISET4(sc, SXIE_MACCR0, SXIE_MACTXFC | SXIE_MACRXFC); - clr_m = SXIE_MACHF | SXIE_MACDCRC | SXIE_MACVC | SXIE_MACADP | - SXIE_MACPRE | SXIE_MACLPE | SXIE_MACNB | SXIE_MACBNB | - SXIE_MACED; - set_m = SXIE_MACFLC | SXIE_MACCRC | SXIE_MACPC; + SXISET4(sc, SXIE_MAC0, SXIE_MAC0_RXFLOWCTRL | SXIE_MAC0_TXFLOWCTRL); + clr_m = SXIE_MAC0_LOOPBACK | SXIE_MAC1_DELAYEDCRC | + SXIE_MAC1_VLANPAD | SXIE_MAC1_AUTOPAD | + SXIE_MAC1_PUREPREAMBLE | SXIE_MAC1_LONGPREAMBLE | + SXIE_MAC1_NOBACKOFF | SXIE_MAC1_BACKPRESSURE | + SXIE_MAC1_EXCESSDEFER; + set_m = SXIE_MAC1_FRAMELENCHECK | SXIE_MAC1_CRC | SXIE_MAC1_PADCRC; set_m |= sxie_miibus_readreg(dev, sc->sc_phyno, 0) >> 8 & 1; - SXICMS4(sc, SXIE_MACCR1, clr_m, set_m); + SXICMS4(sc, SXIE_MAC1, clr_m, set_m); /* XXX */ - SXIWRITE4(sc, SXIE_MACIPGT, 0x0015); - SXIWRITE4(sc, SXIE_MACIPGR, 0x0c12); + SXIWRITE4(sc, SXIE_MAC_IPGT, 0x0015); + SXIWRITE4(sc, SXIE_MAC_IPGR, 0x0c12); /* XXX set collision window */ - SXIWRITE4(sc, SXIE_MACCLRT, 0x370f); + SXIWRITE4(sc, SXIE_MAC_CLRT, 0x370f); /* set max frame length */ - SXIWRITE4(sc, SXIE_MACMFL, SXIE_MAX_PKT_SIZE); + SXIWRITE4(sc, SXIE_MAC_MAXF, SXIE_MAX_PKT_SIZE); /* set lladdr */ - SXIWRITE4(sc, SXIE_MACA0, - sc->sc_ac.ac_enaddr[3] << 16 | - sc->sc_ac.ac_enaddr[4] << 8 | - sc->sc_ac.ac_enaddr[5]); - SXIWRITE4(sc, SXIE_MACA1, - sc->sc_ac.ac_enaddr[0] << 16 | - sc->sc_ac.ac_enaddr[1] << 8 | - sc->sc_ac.ac_enaddr[2]); - - sxie_reset(sc); - /* XXX possibly missing delay in here. */ + sxie_set_lladdr(sc); } void @@ -379,43 +558,47 @@ sxie_init(struct sxie_softc *sc) struct device *dev = (struct device *)sc; int phyreg; - sxie_reset(sc); + sxie_softreset(sc, SXIE_SRST_FULL); SXIWRITE4(sc, SXIE_INTCR, SXIE_INTR_DISABLE); - SXISET4(sc, SXIE_INTSR, SXIE_INTR_CLEAR); - SXISET4(sc, SXIE_RXCR, SXIE_RXFLUSH); - - /* soft reset */ - SXICLR4(sc, SXIE_MACCR0, SXIE_MACSOFTRESET); - - /* zero rx counter */ - SXIWRITE4(sc, SXIE_RXFBC, 0); - sxie_setup_interface(sc, dev); + /* release from softreset */ + SXICLR4(sc, SXIE_MAC0, SXIE_MAC0_SOFTRESET); + /* power up PHY */ sxie_miibus_writereg(dev, sc->sc_phyno, 0, sxie_miibus_readreg(dev, sc->sc_phyno, 0) & ~(1 << 11)); delay(1000); phyreg = sxie_miibus_readreg(dev, sc->sc_phyno, 0); + sxie_iff(sc); + /* set duplex */ - SXICMS4(sc, SXIE_MACCR1, 1, phyreg >> 8 & 1); + SXICMS4(sc, SXIE_MAC1, 1, phyreg >> 8 & 1); /* set speed */ - SXICMS4(sc, SXIE_MACSUPP, 1 << 8, (phyreg >> 13 & 1) << 8); + SXICMS4(sc, SXIE_MAC_SUPP, 1 << 8, (phyreg >> 13 & 1) << 8); + + sc->sc_tx_timer = 0; + sc->sc_tx_inuse = 0; - SXISET4(sc, SXIE_CR, SXIE_RXTX_ENABLE); + SXISET4(sc, SXIE_CMDR, SXIE_RXTX_ENABLE); /* Indicate we are up and running. */ ifp->if_flags |= IFF_RUNNING; ifq_clr_oactive(&ifp->if_snd); + SXIWRITE4(sc, SXIE_INTCR, SXIE_INTR_DISABLE); + SXISET4(sc, SXIE_INTSR, SXIE_INTR_CLEAR); + SXISET4(sc, SXIE_INTCR, SXIE_INTR_ENABLE); sxie_start(ifp); + + timeout_add_sec(&sc->sc_tick, 1); } int @@ -439,11 +622,9 @@ sxie_intr(void *arg) } if (pending & (SXIE_TX_FIFO0 | SXIE_TX_FIFO1)) { - sc->txf_inuse &= ~pending; - if (sc->txf_inuse == 0) - ifp->if_timer = 0; - else - ifp->if_timer = 5; + sc->sc_tx_inuse &= ~(pending & SXIE_TX_FIFOS); + ifp->if_timer = (sc->sc_tx_inuse == 0) ? 0 : 1; + sc->sc_tx_timer = ifp->if_timer ? 5 : 0; if (ifq_is_oactive(&ifp->if_snd)) ifq_restart(&ifp->if_snd); @@ -454,6 +635,49 @@ sxie_intr(void *arg) return 1; } +void +sxie_tx_mbuf_cpu(struct sxie_softc *sc, uint32_t fifo, struct mbuf *m) +{ + uint32_t txbuf[SXIE_MAX_PKT_SIZE / sizeof(uint32_t)]; + + /* copy the actual packet to fifo XXX through 'align buffer' */ + m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)&txbuf[0]); + bus_space_write_multi_4(sc->sc_iot, sc->sc_ioh, + SXIE_TXIO0, (uint32_t *)&txbuf[0], + SXIE_ROUNDUP(m->m_pkthdr.len, 4) >> 2); + + /* transmit to PHY from fifo */ + SXISET4(sc, SXIE_TXCR0 + (fifo * 4), 1); +} + +void +sxie_tx_mbuf_dma(struct sxie_softc *sc, uint32_t fifo, struct mbuf *m) +{ + /* XXX not yet */ + sxie_tx_mbuf_cpu(sc, fifo, m); +} + +void +sxie_tx_mbuf(struct sxie_softc *sc, struct mbuf *m) +{ + uint32_t fifo = 1; + + /* select fifo */ + fifo <<= sc->sc_tx_inuse & SXIE_TX_FIFO0 ? 1 : 0; + sc->sc_tx_inuse |= fifo--; + SXIWRITE4(sc, SXIE_TXINS, fifo); + + /* set packet length */ + SXIWRITE4(sc, SXIE_TXPKTLEN0 + (fifo * 4), m->m_pkthdr.len); + + /* transfer to fifo */ + if (sc->sc_dmanode) + sxie_tx_mbuf_dma(sc, fifo, m); + else + sxie_tx_mbuf_cpu(sc, fifo, m); +} + + /* * XXX there's secondary tx fifo to be used. */ @@ -462,30 +686,16 @@ sxie_start(struct ifnet *ifp) { struct sxie_softc *sc = ifp->if_softc; struct mbuf *m; - struct mbuf *head; - uint8_t *td; - uint32_t fifo; - uint32_t txbuf[SXIE_MAX_PKT_SIZE / sizeof(uint32_t)]; /* XXX !!! */ if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd)) return; - - td = (uint8_t *)&txbuf[0]; - m = NULL; - head = NULL; - - for (;;) { - if (sc->txf_inuse == (SXIE_TX_FIFO0 | SXIE_TX_FIFO1)) { - ifq_set_oactive(&ifp->if_snd); - break; - } - - m = ifq_dequeue(&ifp->if_snd); - if (m == NULL) - break; + for (; sc->sc_tx_inuse < SXIE_MAX_TXD; m_freem(m)) { + if ((m = ifq_dequeue(&ifp->if_snd)) == NULL) + return; if (m->m_pkthdr.len > SXIE_MAX_PKT_SIZE) { + SXIEDBG(("sxie_start: packet too big\n")); m_freem(m); continue; } @@ -494,32 +704,12 @@ sxie_start(struct ifnet *ifp) if (ifp->if_bpf) bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); #endif - - /* select fifo */ - if (sc->txf_inuse & SXIE_TX_FIFO0) { - sc->txf_inuse |= SXIE_TX_FIFO1; - fifo = 1; - } else { - sc->txf_inuse |= SXIE_TX_FIFO0; - fifo = 0; - } - SXIWRITE4(sc, SXIE_TXINS, fifo); - - /* set packet length */ - SXIWRITE4(sc, SXIE_TXPKTLEN0 + (fifo * 4), m->m_pkthdr.len); - - /* copy the actual packet to fifo XXX through 'align buffer' */ - m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)td); - bus_space_write_multi_4(sc->sc_iot, sc->sc_ioh, - SXIE_TXIO0, - (uint32_t *)td, SXIE_ROUNDUP(m->m_pkthdr.len, 4) >> 2); - - /* transmit to PHY from fifo */ - SXISET4(sc, SXIE_TXCR0 + (fifo * 4), 1); - ifp->if_timer = 5; - - m_freem(m); + sxie_tx_mbuf(sc, m); + sc->sc_tx_timer = 5; + ifp->if_timer = 1; } + + ifq_set_oactive(&ifp->if_snd); } void @@ -527,35 +717,54 @@ sxie_stop(struct sxie_softc *sc) { struct ifnet *ifp = &sc->sc_ac.ac_if; - sxie_reset(sc); + timeout_del(&sc->sc_tick); + + SXICLR4(sc, SXIE_CMDR, SXIE_CMDR_RXENABLE | SXIE_CMDR_TXENABLE); + + sc->sc_pauseframe = 0; + sc->sc_tx_timer = 0; + sc->sc_tx_inuse = 0; ifp->if_flags &= ~IFF_RUNNING; ifp->if_timer = 0; ifq_clr_oactive(&ifp->if_snd); + + mii_down(&sc->sc_mii); } void sxie_reset(struct sxie_softc *sc) { +#if 0 /* reset the controller */ - SXIWRITE4(sc, SXIE_CR, 0); + SXIWRITE4(sc, SXIE_CMDR, 0); delay(200); - SXIWRITE4(sc, SXIE_CR, 1); + SXIWRITE4(sc, SXIE_CMDR, 1); delay(200); +#endif } void sxie_watchdog(struct ifnet *ifp) { struct sxie_softc *sc = ifp->if_softc; - if (sc->pauseframe) { - ifp->if_timer = 5; + + if (sc->sc_pauseframe) { + ifp->if_timer = 1; return; } - printf("%s: watchdog tx timeout\n", sc->sc_dev.dv_xname); - ifp->if_oerrors++; - sxie_init(sc); - sxie_start(ifp); + if (sc->sc_tx_timer > 0) { + if (--sc->sc_tx_timer == 0) { + if (ifp->if_flags & IFF_DEBUG) + printf("%s: watchdog tx timeout\n", + sc->sc_dev.dv_xname); + ifp->if_oerrors++; + sxie_init(sc); + sxie_start(ifp); + return; + } + ifp->if_timer = 1; + } } /* @@ -570,8 +779,7 @@ sxie_recv(struct sxie_softc *sc) struct mbuf *m; uint16_t pktstat; int16_t pktlen; - int rlen; - char rxbuf[SXIE_MAX_PKT_SIZE]; /* XXX !!! */ + char rxbuf[SXIE_MAX_PKT_SIZE]; trynext: fbc = SXIREAD4(sc, SXIE_RXFBC); if (!fbc) @@ -583,44 +791,34 @@ trynext: */ reg = SXIREAD4(sc, SXIE_RXIO); if (reg != 0x0143414d) { /* invalid packet */ - /* disable, flush, enable */ - SXICLR4(sc, SXIE_CR, SXIE_RX_ENABLE); - SXISET4(sc, SXIE_RXCR, SXIE_RXFLUSH); - while (SXIREAD4(sc, SXIE_RXCR) & SXIE_RXFLUSH); - SXISET4(sc, SXIE_CR, SXIE_RX_ENABLE); - - goto err_out; + sxie_softreset(sc, SXIE_SRST_RX); + ifp->if_ierrors++; + goto done; } reg = SXIREAD4(sc, SXIE_RXIO); pktstat = (uint16_t)reg >> 16; pktlen = (int16_t)reg; /* length of useful data */ - if (pktstat & SXIE_RX_ERRMASK || pktlen < ETHER_MIN_LEN) { + if (pktstat & SXIE_RX_ERRMASK || pktlen < ETHER_MIN_LEN || + pktlen > SXIE_MAX_PKT_SIZE) { ifp->if_ierrors++; goto trynext; } - if (pktlen > SXIE_MAX_PKT_SIZE) - pktlen = SXIE_MAX_PKT_SIZE; /* XXX is truncating ok? */ /* read the actual packet from fifo XXX through 'align buffer'.. */ - if (pktlen & 3) - rlen = SXIE_ROUNDUP(pktlen, 4); - else - rlen = pktlen; bus_space_read_multi_4(sc->sc_iot, sc->sc_ioh, - SXIE_RXIO, (uint32_t *)&rxbuf[0], rlen >> 2); + SXIE_RXIO, (uint32_t *)&rxbuf[0], + (pktlen & 3 ? SXIE_ROUNDUP(pktlen, 4) : pktlen) >> 2); m = m_devget(&rxbuf[0], pktlen, ETHER_ALIGN); if (m == NULL) { ifp->if_ierrors++; - goto err_out; + goto done; } ml_enqueue(&ml, m); goto trynext; -err_out: - ifp->if_ierrors++; done: if_input(ifp, &ml); } @@ -659,7 +857,7 @@ sxie_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) } if (error == ENETRESET) { if (ifp->if_flags & IFF_RUNNING) - sxie_iff(sc, ifp); + sxie_iff(sc); error = 0; } @@ -668,9 +866,49 @@ sxie_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) } void -sxie_iff(struct sxie_softc *sc, struct ifnet *ifp) +sxie_iff(struct sxie_softc *sc) { - /* XXX set interface features */ + struct arpcom *ac = &sc->sc_ac; + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct ether_multi *enm; + struct ether_multistep step; + uint64_t rxfhash = 0; + uint32_t h; + uint32_t rxfilt; + + rxfilt = SXIREAD4(sc, SXIE_RXFILTER_CTRL); + + ifp->if_flags &= ~IFF_ALLMULTI; + if ((ifp->if_flags & IFF_PROMISC) || ac->ac_multirangecnt > 0) { + ifp->if_flags |= IFF_ALLMULTI; + rxfhash = 0xffffffffffffffffLLU; + } else { + ETHER_FIRST_MULTI(step, ac, enm); + while (enm != NULL) { + h = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN); + + rxfhash |= 1LLU << (((uint8_t *)&h)[3] >> 2); + + ETHER_NEXT_MULTI(step, enm); + } + } + + rxfilt |= SXIE_RXFILTER_UNICAST | SXIE_RXFILTER_DAFILTER | + SXIE_RXFILTER_MULTICAST; + + if (ifp->if_flags & IFF_ALLMULTI) + rxfilt |= SXIE_RXFILTER_MHASHFILTER; + + if (ifp->if_flags & IFF_BROADCAST) + rxfilt |= SXIE_RXFILTER_BROADCAST; + + if (ifp->if_flags & IFF_PROMISC) + rxfilt |= SXIE_RXFILTER_PROMISC; + + SXIWRITE4(sc, SXIE_RXHASH0, (uint32_t)rxfhash); + SXIWRITE4(sc, SXIE_RXHASH1, (uint32_t)(rxfhash >> 32)); + + SXIWRITE4(sc, SXIE_RXFILTER_CTRL, rxfilt); } /* @@ -681,11 +919,13 @@ sxie_miibus_readreg(struct device *dev, int phy, int reg) { struct sxie_softc *sc = (struct sxie_softc *)dev; int timo = SXIE_MII_TIMEOUT; + int rv; - SXIWRITE4(sc, SXIE_MACMADR, phy << 8 | reg); + mtx_enter(&sc->sc_miibus_lock); - SXIWRITE4(sc, SXIE_MACMCMD, 1); - while (SXIREAD4(sc, SXIE_MACMIND) & 1 && --timo) + SXIWRITE4(sc, SXIE_MAC_MADR, phy << 8 | reg); + SXIWRITE4(sc, SXIE_MAC_MCMD, 1); + while (SXIREAD4(sc, SXIE_MAC_MIND) & 1 && --timo) delay(10); #ifdef DIAGNOSTIC if (!timo) @@ -693,9 +933,11 @@ sxie_miibus_readreg(struct device *dev, int phy, int reg) sc->sc_dev.dv_xname); #endif - SXIWRITE4(sc, SXIE_MACMCMD, 0); - - return SXIREAD4(sc, SXIE_MACMRDD) & 0xffff; + SXIWRITE4(sc, SXIE_MAC_MCMD, 0); + rv = SXIREAD4(sc, SXIE_MAC_MRDD) & 0xffff; + mtx_leave(&sc->sc_miibus_lock); + + return rv; } void @@ -704,38 +946,55 @@ sxie_miibus_writereg(struct device *dev, int phy, int reg, int val) struct sxie_softc *sc = (struct sxie_softc *)dev; int timo = SXIE_MII_TIMEOUT; - SXIWRITE4(sc, SXIE_MACMADR, phy << 8 | reg); + mtx_enter(&sc->sc_miibus_lock); - SXIWRITE4(sc, SXIE_MACMCMD, 1); - while (SXIREAD4(sc, SXIE_MACMIND) & 1 && --timo) + SXIWRITE4(sc, SXIE_MAC_MADR, phy << 8 | reg); + SXIWRITE4(sc, SXIE_MAC_MCMD, 1); + while (SXIREAD4(sc, SXIE_MAC_MIND) & 1 && --timo) delay(10); #ifdef DIAGNOSTIC if (!timo) - printf("%s: sxie_miibus_readreg timeout.\n", + printf("%s: sxie_miibus_writereg timeout.\n", sc->sc_dev.dv_xname); #endif - SXIWRITE4(sc, SXIE_MACMCMD, 0); + SXIWRITE4(sc, SXIE_MAC_MCMD, 0); + SXIWRITE4(sc, SXIE_MAC_MWTD, val); - SXIWRITE4(sc, SXIE_MACMWTD, val); + mtx_leave(&sc->sc_miibus_lock); } void sxie_miibus_statchg(struct device *dev) { - /* XXX */ -#if 0 struct sxie_softc *sc = (struct sxie_softc *)dev; + uint64_t mma = sc->sc_mii.mii_media_active; + uint64_t _valid = IFM_ACTIVE | IFM_AVALID; - switch (IFM_SUBTYPE(sc->sc_mii.mii_media_active)) { + if ((mma & _valid) != _valid) + return; /* no link */ + + switch (IFM_SUBTYPE(mma)) { case IFM_10_T: + SXICLR4(sc, SXIE_MAC_SUPP, SXIE_MAC_SUPP_SPEED); + break; case IFM_100_TX: - /*case IFM_1000_T: only on GMAC */ + SXISET4(sc, SXIE_MAC_SUPP, SXIE_MAC_SUPP_SPEED); break; default: break; } -#endif + + if (mma & IFM_FLOW) { + SXICMS4(sc, SXIE_MAC0, + SXIE_MAC0_TXFLOWCTRL | SXIE_MAC0_RXFLOWCTRL, + (mma & IFM_ETH_TXPAUSE ? SXIE_MAC0_TXFLOWCTRL : 0) | + (mma & IFM_ETH_RXPAUSE ? SXIE_MAC0_RXFLOWCTRL : 0)); + } else { + SXICLR4(sc, SXIE_MAC0, SXIE_MAC0_TXFLOWCTRL | SXIE_MAC0_RXFLOWCTRL); + } + + SXICMS4(sc, SXIE_MAC1, 1, mma & IFM_FDX ? 1 : 0); } int @@ -762,3 +1021,16 @@ sxie_ifm_status(struct ifnet *ifp, struct ifmediareq *ifmr) ifmr->ifm_active = sc->sc_mii.mii_media_active; ifmr->ifm_status = sc->sc_mii.mii_media_status; } + +void +sxie_tick(void *arg) +{ + struct sxie_softc *sc = arg; + int s; + + s = splnet(); + mii_tick(&sc->sc_mii); + splx(s); + + timeout_add_sec(&sc->sc_tick, 1); +}