Index: sys/dev/usb/if_axe.c =================================================================== RCS file: /cvsroot/src/sys/dev/usb/if_axe.c,v retrieving revision 1.75 diff -u -p -r1.75 if_axe.c --- sys/dev/usb/if_axe.c 25 Nov 2016 12:56:29 -0000 1.75 +++ sys/dev/usb/if_axe.c 1 Dec 2016 21:21:29 -0000 @@ -1,5 +1,5 @@ /* $NetBSD: if_axe.c,v 1.75 2016/11/25 12:56:29 skrll Exp $ */ -/* $OpenBSD: if_axe.c,v 1.96 2010/01/09 05:33:08 jsg Exp $ */ +/* $OpenBSD: if_axe.c,v 1.137 2016/04/13 11:03:37 mpi Exp $ */ /* * Copyright (c) 2005, 2006, 2007 Jonathan Gray @@ -50,14 +50,8 @@ */ /* - * ASIX Electronics AX88172 USB 2.0 ethernet driver. Used in the - * LinkSys USB200M and various other adapters. - * - * Manuals available from: - * http://www.asix.com.tw/datasheet/mac/Ax88172.PDF - * Note: you need the manual for the AX88170 chip (USB 1.x ethernet - * controller) to find the definitions for the RX control register. - * http://www.asix.com.tw/datasheet/mac/Ax88170.PDF + * ASIX Electronics AX88172/AX88178/AX88778 USB 2.0 ethernet driver. + * Used in the LinkSys USB200M and various other adapters. * * Written by Bill Paul * Senior Engineer @@ -77,15 +71,19 @@ * to send any packets. * * Note that this device appears to only support loading the station - * address via autoload from the EEPROM (i.e. there's no way to manaully + * address via autoload from the EEPROM (i.e. there's no way to manually * set it). * * (Adam Weinberger wanted me to name this driver if_gir.c.) */ /* - * Ported to OpenBSD 3/28/2004 by Greg Taleck - * with bits and pieces from the aue and url drivers. + * Ax88178 and Ax88772 support backported from the OpenBSD driver. + * 2007/02/12, J.R. Oldroyd, fbsd@opal.com + * + * Manual here: + * http://www.asix.com.tw/FrootAttach/datasheet/AX88178_datasheet_Rev10.pdf + * http://www.asix.com.tw/FrootAttach/datasheet/AX88772_datasheet_Rev10.pdf */ #include @@ -127,15 +125,35 @@ __KERNEL_RCSID(0, "$NetBSD: if_axe.c,v 1 #include +/* + * AXE_178_MAX_FRAME_BURST + * max frame burst size for Ax88178 and Ax88772 + * 0 2048 bytes + * 1 4096 bytes + * 2 8192 bytes + * 3 16384 bytes + * use the largest your system can handle without USB stalling. + * + * NB: 88772 parts appear to generate lots of input errors with + * a 2K rx buffer and 8K is only slightly faster than 4K on an + * EHCI port on a T42 so change at your own risk. + */ +#define AXE_178_MAX_FRAME_BURST 1 + +#if 0 +#define AXE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) +#endif + #ifdef AXE_DEBUG #define DPRINTF(x) do { if (axedebug) printf x; } while (0) #define DPRINTFN(n,x) do { if (axedebug >= (n)) printf x; } while (0) -int axedebug = 0; +int axedebug = 10; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif + /* * Various supported device vendors/products. */ @@ -146,22 +164,23 @@ static const struct axe_type axe_devs[] { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88172}, 0 }, { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772}, AX772 }, { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772A}, AX772 }, - { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772B}, AX772 | AX772B }, - { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772B_1}, AX772 | AX772B }, + { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772B}, AX772B }, + { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772B_1}, AX772B }, { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88178}, AX178 }, { { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC210T}, 0 }, { { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D5055 }, AX178 }, { { USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB2AR}, 0}, - { { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_USB200MV2}, AX772 }, + { { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_USB200MV2}, AX772A }, { { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB2_TX }, 0}, { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100}, 0 }, { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100B1 }, AX772 }, { { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DUBE100B1 }, AX772 }, - { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100C1 }, AX772 | AX772B }, + { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100C1 }, AX772B }, { { USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_GWUSB2E}, 0 }, { { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_ETGUS2 }, AX178 }, { { USB_VENDOR_JVC, USB_PRODUCT_JVC_MP_PRX1}, 0 }, - { { USB_VENDOR_LENOVO, USB_PRODUCT_LENOVO_ETHERNET }, AX772 | AX772B }, + { { USB_VENDOR_LENOVO, USB_PRODUCT_LENOVO_ETHERNET }, AX772B }, + { { USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_HG20F9}, AX772B }, { { USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_USB200M}, 0 }, { { USB_VENDOR_LINKSYS4, USB_PRODUCT_LINKSYS4_USB1000 }, AX178 }, { { USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LAN_GTJU2}, AX178 }, @@ -171,12 +190,24 @@ static const struct axe_type axe_devs[] { { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120}, 0 }, { { USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01PLUS }, AX772 }, { { USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GU1000T }, AX178 }, - { { USB_VENDOR_SYSTEMTALKS, USB_PRODUCT_SYSTEMTALKS_SGCX2UL}, 0 }, { { USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_LN029}, 0 }, - { { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN028 }, AX178 } + { { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN028 }, AX178 }, + { { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN031 }, AX178 }, + { { USB_VENDOR_SYSTEMTALKS, USB_PRODUCT_SYSTEMTALKS_SGCX2UL}, 0 }, }; #define axe_lookup(v, p) ((const struct axe_type *)usb_lookup(axe_devs, v, p)) +static const struct ax88772b_mfb ax88772b_mfb_table[] = { + { 0x8000, 0x8001, 2048 }, + { 0x8100, 0x8147, 4096 }, + { 0x8200, 0x81EB, 6144 }, + { 0x8300, 0x83D7, 8192 }, + { 0x8400, 0x851E, 16384 }, + { 0x8500, 0x8666, 20480 }, + { 0x8600, 0x87AE, 24576 }, + { 0x8700, 0x8A3D, 32768 } +}; + int axe_match(device_t, cfdata_t, void *); void axe_attach(device_t, device_t, void *); int axe_detach(device_t, int); @@ -204,8 +235,8 @@ static void axe_miibus_writereg(device_t static void axe_miibus_statchg(struct ifnet *); static int axe_cmd(struct axe_softc *, int, int, int, void *); static void axe_reset(struct axe_softc *); -static int axe_ifmedia_upd(struct ifnet *); -static void axe_ifmedia_sts(struct ifnet *, struct ifmediareq *); +// static int axe_ifmedia_upd(struct ifnet *); +// static void axe_ifmedia_sts(struct ifnet *, struct ifmediareq *); static void axe_setmulti(struct axe_softc *); static void axe_lock_mii(struct axe_softc *); @@ -268,7 +299,11 @@ axe_miibus_readreg_locked(device_t dev, usbd_status err; uint16_t val; + +// DPRINTF(("axe_miibus_readreg: phy 0x%x reg 0x%x\n", phy, reg)); + axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); + err = axe_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, (void *)&val); axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); if (err) { @@ -277,18 +312,18 @@ axe_miibus_readreg_locked(device_t dev, } val = le16toh(val); - if (sc->axe_flags & AX772 && reg == MII_BMSR) { + if (AXE_IS_772(sc) && reg == MII_BMSR) { /* - * BMSR of AX88772 indicates it supports extended + * BMSR of AX88772 indicates that it supports extended * capability but the extended status register is - * reserverd for embedded ethernet PHY. So clear the + * reserved for embedded ethernet PHY. So clear the * extended capability bit of BMSR. */ val &= ~BMSR_EXTCAP; } - DPRINTF(("axe_miibus_readreg: phy 0x%x reg 0x%x val 0x%x\n", - phy, reg, val)); +// DPRINTF(("axe_miibus_readreg: phy 0x%x reg 0x%x val 0x%x\n", +// phy, reg, val)); return val; } @@ -354,13 +389,20 @@ axe_miibus_statchg(struct ifnet *ifp) struct mii_data *mii = &sc->axe_mii; int val, err; - if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) - val = AXE_MEDIA_FULL_DUPLEX; - else - val = 0; - - if (sc->axe_flags & AX178 || sc->axe_flags & AX772) { - val |= (AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC); + val = 0; + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { + val |= AXE_MEDIA_FULL_DUPLEX; + if (AXE_IS_178_FAMILY(sc)) { + if ((IFM_OPTIONS(mii->mii_media_active) & + IFM_ETH_TXPAUSE) != 0) + val |= AXE_178_MEDIA_TXFLOW_CONTROL_EN; + if ((IFM_OPTIONS(mii->mii_media_active) & + IFM_ETH_RXPAUSE) != 0) + val |= AXE_178_MEDIA_RXFLOW_CONTROL_EN; + } + } + if (AXE_IS_178_FAMILY(sc)) { + val |= AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC; if (sc->axe_flags & AX178) val |= AXE_178_MEDIA_ENCK; switch (IFM_SUBTYPE(mii->mii_media_active)) { @@ -386,6 +428,7 @@ axe_miibus_statchg(struct ifnet *ifp) } } +#if 0 /* * Set media options */ @@ -423,6 +466,7 @@ axe_ifmedia_sts(struct ifnet *ifp, struc ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; } +#endif static void axe_setmulti(struct axe_softc *sc) @@ -434,6 +478,8 @@ axe_setmulti(struct axe_softc *sc) uint16_t rxmode; uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + DPRINTFN(10,("%s: %s: enter\n", device_xname(sc->axe_dev),__func__)); + if (sc->axe_dying) return; @@ -441,11 +487,16 @@ axe_setmulti(struct axe_softc *sc) axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, (void *)&rxmode); rxmode = le16toh(rxmode); - rxmode &= ~(AXE_RXCMD_ALLMULTI | AXE_RXCMD_PROMISC); - - /* If we want promiscuous mode, set the allframes bit */ - if (ifp->if_flags & IFF_PROMISC) { - rxmode |= AXE_RXCMD_PROMISC; + rxmode &= + ~(AXE_RXCMD_ALLMULTI | AXE_RXCMD_PROMISC | + AXE_RXCMD_BROADCAST | AXE_RXCMD_MULTICAST); + + rxmode |= + (ifp->if_flags & IFF_BROADCAST) ? AXE_RXCMD_BROADCAST : 0; + + if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { + if (ifp->if_flags & IFF_PROMISC) + rxmode |= AXE_RXCMD_PROMISC; goto allmulti; } @@ -461,6 +512,8 @@ axe_setmulti(struct axe_softc *sc) ETHER_NEXT_MULTI(step, enm); } ifp->if_flags &= ~IFF_ALLMULTI; + rxmode |= AXE_RXCMD_MULTICAST; + axe_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, (void *)&hashtbl); axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); axe_unlock_mii(sc); @@ -473,18 +526,6 @@ axe_setmulti(struct axe_softc *sc) axe_unlock_mii(sc); } -static void -axe_reset(struct axe_softc *sc) -{ - - if (sc->axe_dying) - return; - /* XXX What to reset? */ - - /* Wait a little while for the chip to get its brains in order. */ - DELAY(1000); -} - static int axe_get_phyno(struct axe_softc *sc, int sel) { @@ -623,7 +664,7 @@ axe_ax88178_init(struct axe_softc *sc) axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL); usbd_delay_ms(sc->axe_udev, 150); - /* Enable MII/GMII/RGMII for external PHY */ + /* Enable MII/GMII/RGMII interface to work with external PHY. */ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0, NULL); usbd_delay_ms(sc->axe_udev, 10); axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); @@ -638,7 +679,8 @@ axe_ax88772_init(struct axe_softc *sc) if (sc->axe_phyno == AXE_772_PHY_NO_EPHY) { /* ask for the embedded PHY */ - axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL); + axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, + AXE_SW_PHY_SELECT_EMBEDDED, NULL); usbd_delay_ms(sc->axe_udev, 10); /* power down and reset state, pin reset state */ @@ -658,7 +700,8 @@ axe_ax88772_init(struct axe_softc *sc) AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL); } else { /* ask for external PHY */ - axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x00, NULL); + axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, AXE_SW_PHY_SELECT_EXT, + NULL); usbd_delay_ms(sc->axe_udev, 10); /* power down internal PHY */ @@ -670,6 +713,129 @@ axe_ax88772_init(struct axe_softc *sc) axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); } +/* 6.2.1.23 */ +static void +axe_ax88772_phywake(struct axe_softc *sc) +{ +printf("%s: sc->axe_phyno %d\n", __func__, sc->axe_phyno); + if (sc->axe_phyno == AXE_772_PHY_NO_EPHY) { + /* Manually select internal(embedded) PHY - MAC mode. */ + axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, + AXE_SW_PHY_SELECT_EMBEDDED, + NULL); + usbd_delay_ms(sc->axe_udev, hztoms(hz / 32)); + } else { + /* + * Manually select external PHY - MAC mode. + * Reverse MII/RMII is for AX88772A PHY mode. + */ + /* XXXNH Check */ + axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, AXE_SW_PHY_SELECT_SS_ENB | + AXE_SW_PHY_SELECT_EXT | AXE_SW_PHY_SELECT_SS_MII, NULL); + usbd_delay_ms(sc->axe_udev, hztoms(hz / 32)); + } + + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPPD | + AXE_SW_RESET_IPRL, NULL); + + /* T1 = min 500ns */ + usbd_delay_ms(sc->axe_udev, 150); + +#if 1 + /* 772B Appendix B says nothing about this */ + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); + usbd_delay_ms(sc->axe_udev, 150); +#endif + /* Take PHY out of power down. */ + if (sc->axe_phyno == AXE_772_PHY_NO_EPHY) { + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL, NULL); + } else { + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRTE, NULL); + } + usbd_delay_ms(sc->axe_udev, 600); + + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); + /* 772B T3 = 500ns */ + usbd_delay_ms(sc->axe_udev, hztoms(hz / 32)); + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL, NULL); + usbd_delay_ms(sc->axe_udev, hztoms(hz / 32)); +} + +static void +axe_ax88772a_init(struct axe_softc *sc) +{ + + /* Reload EEPROM. */ + AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM, hz / 32); + axe_ax88772_phywake(sc); + /* Stop MAC. */ + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); +} + +static void +axe_ax88772b_init(struct axe_softc *sc) +{ + uint16_t eeprom; + int i; + + /* Reload EEPROM. */ + AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM , hz / 32); +#if 1 + /* XXXNH unused */ + /* + * Save PHY power saving configuration(high byte) and + * clear EEPROM checksum value(low byte). + */ + axe_cmd(sc, AXE_CMD_SROM_READ, 0, AXE_EEPROM_772B_PHY_PWRCFG, &eeprom); + sc->sc_pwrcfg = le16toh(eeprom) & 0xFF00; +#endif + /* + * Auto-loaded default station address from internal ROM is + * 00:00:00:00:00:00 such that an explicit access to EEPROM + * is required to get real station address. + */ + uint8_t *eaddr = sc->axe_enaddr; + for (i = 0; i < ETHER_ADDR_LEN / 2; i++) { + axe_cmd(sc, AXE_CMD_SROM_READ, 0, AXE_EEPROM_772B_NODE_ID + i, + &eeprom); + eeprom = le16toh(eeprom); + *eaddr++ = (uint8_t)(eeprom & 0xFF); + *eaddr++ = (uint8_t)((eeprom >> 8) & 0xFF); + } + /* Wakeup PHY. */ + axe_ax88772_phywake(sc); + /* Stop MAC. */ + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); +} + +#undef AXE_GPIO_WRITE + +static void +axe_reset(struct axe_softc *sc) +{ + + if (sc->axe_dying) + return; +#if 1 + DELAY(1000); +#else + axe_lock_mii(sc); + + if (sc->axe_flags & AX178) { + axe_ax88178_init(sc); + } else if (sc->axe_flags & AX772) { + axe_ax88772_init(sc); + } else if (sc->axe_flags & AX772A) { + axe_ax88772a_init(sc); + } else if (sc->axe_flags & AX772B) { + axe_ax88772b_init(sc); + } + axe_unlock_mii(sc); +#endif +} + + + /* * Probe for a AX88172 chip. */ @@ -696,7 +862,6 @@ axe_attach(device_t parent, device_t sel usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; struct mii_data *mii; - uint8_t eaddr[ETHER_ADDR_LEN]; char *devinfop; const char *devname = device_xname(self); struct ifnet *ifp; @@ -736,12 +901,16 @@ axe_attach(device_t parent, device_t sel id = usbd_get_interface_descriptor(sc->axe_iface); /* decide on what our bufsize will be */ - if (sc->axe_flags & AX178 || sc->axe_flags & AX772) + if (AXE_IS_178_FAMILY(sc)) sc->axe_bufsz = (sc->axe_udev->ud_speed == USB_SPEED_HIGH) ? AXE_178_MAX_BUFSZ : AXE_178_MIN_BUFSZ; else sc->axe_bufsz = AXE_172_BUFSZ; + sc->axe_ed[AXE_ENDPT_RX] = -1; + sc->axe_ed[AXE_ENDPT_TX] = -1; + sc->axe_ed[AXE_ENDPT_INTR] = -1; + /* Find endpoints. */ for (i = 0; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(sc->axe_iface, i); @@ -749,14 +918,16 @@ axe_attach(device_t parent, device_t sel aprint_error_dev(self, "couldn't get ep %d\n", i); return; } - if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && - UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { + const uint8_t xt = UE_GET_XFERTYPE(ed->bmAttributes); + const uint8_t dir = UE_GET_DIR(ed->bEndpointAddress); + + if (dir == UE_DIR_IN && xt == UE_BULK && + sc->axe_ed[AXE_ENDPT_RX] == -1) { sc->axe_ed[AXE_ENDPT_RX] = ed->bEndpointAddress; - } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && - UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { + } else if (dir == UE_DIR_OUT && xt == UE_BULK && + sc->axe_ed[AXE_ENDPT_TX] == -1) { sc->axe_ed[AXE_ENDPT_TX] = ed->bEndpointAddress; - } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && - UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { + } else if (dir == UE_DIR_IN && xt == UE_INTERRUPT) { sc->axe_ed[AXE_ENDPT_INTR] = ed->bEndpointAddress; } } @@ -777,29 +948,39 @@ axe_attach(device_t parent, device_t sel sc->axe_phyno = 0; } - if (sc->axe_flags & AX178) + /* Initialize controller and get station address. */ + + if (sc->axe_flags & AX178) { axe_ax88178_init(sc); - else if (sc->axe_flags & AX772) + axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, sc->axe_enaddr); + } else if (sc->axe_flags & AX772) { axe_ax88772_init(sc); + axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, sc->axe_enaddr); + } else if (sc->axe_flags & AX772A) { + axe_ax88772a_init(sc); + axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, sc->axe_enaddr); + } else if (sc->axe_flags & AX772B) { + axe_ax88772b_init(sc); + } else + axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, sc->axe_enaddr); /* - * Get station address. + * Fetch IPG values. */ - if (sc->axe_flags & AX178 || sc->axe_flags & AX772) - axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, &eaddr); - else - axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, &eaddr); + if (sc->axe_flags & (AX772A | AX772B)) { + /* Set IPG values. */ + sc->axe_ipgs[0] = AXE_IPG0_DEFAULT; + sc->axe_ipgs[1] = AXE_IPG1_DEFAULT; + sc->axe_ipgs[2] = AXE_IPG2_DEFAULT; + } else + axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->axe_ipgs); - /* - * Load IPG values - */ - axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, (void *)&sc->axe_ipgs); axe_unlock_mii(sc); /* * An ASIX chip was detected. Inform the world. */ - aprint_normal_dev(self, "Ethernet address %s\n", ether_sprintf(eaddr)); + aprint_normal_dev(self, "Ethernet address %s\n", ether_sprintf(sc->axe_enaddr)); /* Initialize interface info.*/ ifp = &sc->sc_if; @@ -814,7 +995,30 @@ axe_attach(device_t parent, device_t sel IFQ_SET_READY(&ifp->if_snd); - sc->axe_ec.ec_capabilities = ETHERCAP_VLAN_MTU; + if (AXE_IS_178_FAMILY(sc)) + sc->axe_ec.ec_capabilities = ETHERCAP_VLAN_MTU; + if (sc->axe_flags & AX772B) { + ifp->if_capabilities = + IFCAP_CSUM_IPv4_Rx | + IFCAP_CSUM_TCPv4_Rx | IFCAP_CSUM_UDPv4_Rx | + IFCAP_CSUM_TCPv6_Rx | IFCAP_CSUM_UDPv6_Rx; + + /* + * Checksum offloading of AX88772B also works with VLAN + * tagged frames but there is no way to take advantage + * of the feature because vlan(4) assumes + * IFCAP_VLAN_HWTAGGING is prerequisite condition to + * support checksum offloading with VLAN. VLAN hardware + * tagging support of AX88772B is very limited so it's + * not possible to announce IFCAP_VLAN_HWTAGGING. + */ + } + u_int adv_pause; + if (sc->axe_flags & (AX772A | AX772B | AX178)) + adv_pause = MIIF_DOPAUSE; + else + adv_pause = 0; + adv_pause = 0; /* Initialize MII/media info. */ mii = &sc->axe_mii; @@ -825,15 +1029,10 @@ axe_attach(device_t parent, device_t sel mii->mii_flags = MIIF_AUTOTSLEEP; sc->axe_ec.ec_mii = mii; - if (sc->axe_flags & AXE_MII) - ifmedia_init(&mii->mii_media, 0, axe_ifmedia_upd, - axe_ifmedia_sts); - else - ifmedia_init(&mii->mii_media, 0, ether_mediachange, - ether_mediastatus); + ifmedia_init(&mii->mii_media, 0, ether_mediachange, ether_mediastatus); mii_attach(sc->axe_dev, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, - 0); + adv_pause); if (LIST_EMPTY(&mii->mii_phys)) { ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL); @@ -843,7 +1042,7 @@ axe_attach(device_t parent, device_t sel /* Attach the interface. */ if_attach(ifp); - ether_ifattach(ifp, eaddr); + ether_ifattach(ifp, sc->axe_enaddr); rnd_attach_source(&sc->rnd_source, device_xname(sc->axe_dev), RND_TYPE_NET, RND_FLAG_DEFAULT); @@ -876,6 +1075,13 @@ axe_detach(device_t self, int flags) sc->axe_dying = true; + if (sc->axe_ep[AXE_ENDPT_TX] != NULL) + usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_TX]); + if (sc->axe_ep[AXE_ENDPT_RX] != NULL) + usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_RX]); + if (sc->axe_ep[AXE_ENDPT_INTR] != NULL) + usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_INTR]); + /* * Remove any pending tasks. They cannot be executing because they run * in the same thread as detach. @@ -887,6 +1093,12 @@ axe_detach(device_t self, int flags) if (ifp->if_flags & IFF_RUNNING) axe_stop(ifp, 1); + + if (--sc->axe_refcnt >= 0) { + /* Wait for processes to go away. */ + usb_detach_waitold(sc->axe_dev); + } + callout_destroy(&sc->axe_stat_ch); mutex_destroy(&sc->axe_mii_lock); rnd_detach_source(&sc->rnd_source); @@ -904,10 +1116,6 @@ axe_detach(device_t self, int flags) sc->axe_attached = false; - if (--sc->axe_refcnt >= 0) { - /* Wait for processes to go away. */ - usb_detach_waitold(sc->axe_dev); - } splx(s); usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->axe_udev, sc->axe_dev); @@ -947,6 +1155,7 @@ axe_rx_list_init(struct axe_softc *sc) c->axe_sc = sc; c->axe_idx = i; if (c->axe_xfer == NULL) { + printf("%s: creating xfer on ep %x\n", __func__, sc->axe_ed[AXE_ENDPT_RX]); int err = usbd_create_xfer(sc->axe_ep[AXE_ENDPT_RX], sc->axe_bufsz, USBD_SHORT_XFER_OK, 0, &c->axe_xfer); if (err) @@ -973,6 +1182,7 @@ axe_tx_list_init(struct axe_softc *sc) c->axe_sc = sc; c->axe_idx = i; if (c->axe_xfer == NULL) { + printf("%s: creating xfer on ep %x\n", __func__, sc->axe_ed[AXE_ENDPT_TX]); int err = usbd_create_xfer(sc->axe_ep[AXE_ENDPT_TX], sc->axe_bufsz, USBD_FORCE_SHORT_XFER, 0, &c->axe_xfer); @@ -1018,9 +1228,10 @@ axe_rxeof(struct usbd_xfer *xfer, void * if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; - if (usbd_ratecheck(&sc->axe_rx_notice)) + if (usbd_ratecheck(&sc->axe_rx_notice)) { aprint_error_dev(sc->axe_dev, "usb errors on rx: %s\n", usbd_errstr(status)); + } if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->axe_ep[AXE_ENDPT_RX]); goto done; @@ -1029,13 +1240,23 @@ axe_rxeof(struct usbd_xfer *xfer, void * usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); do { - if (sc->axe_flags & AX178 || sc->axe_flags & AX772) { + pktlen = 0; + rxlen = 0; + if ((sc->axe_flags & AXSTD_FRAME) != 0) { if (total_len < sizeof(hdr)) { ifp->if_ierrors++; goto done; } memcpy(&hdr, buf, sizeof(hdr)); + + DPRINTFN(10,("%s: %s: total_len %#x len %x ilen %#x\n", + device_xname(sc->axe_dev), __func__, + total_len, + (le16toh(hdr.len) & AXE_RH1M_RXLEN_MASK), + (le16toh(hdr.ilen) & AXE_RH1M_RXLEN_MASK) + )); + total_len -= sizeof(hdr); buf += sizeof(hdr); @@ -1056,6 +1277,42 @@ axe_rxeof(struct usbd_xfer *xfer, void * total_len -= rxlen; } + } else if ((sc->axe_flags & AXCSUM_FRAME) != 0) { +#if 0 + if ((int)(pos + sizeof(csum_hdr)) > actlen) { + /* too little data */ + error = EINVAL; + break; + } + usbd_copy_out(pc, pos, &csum_hdr, sizeof(csum_hdr)); + + csum_hdr.len = le16toh(csum_hdr.len); + csum_hdr.ilen = le16toh(csum_hdr.ilen); + csum_hdr.cstatus = le16toh(csum_hdr.cstatus); + if ((AXE_CSUM_RXBYTES(csum_hdr.len) ^ + AXE_CSUM_RXBYTES(csum_hdr.ilen)) != + sc->sc_lenmask) { + /* we lost sync */ + error = EINVAL; + break; + } + /* + * Get total transferred frame length including + * checksum header. The length should be multiple + * of 4. + */ + len = sizeof(csum_hdr) + AXE_CSUM_RXBYTES(csum_hdr.len); + len = (len + 3) & ~3; + if (pos + len > actlen) { + /* invalid length */ + error = EINVAL; + break; + } + axe_rxeof(ue, pc, pos + sizeof(csum_hdr), + AXE_CSUM_RXBYTES(csum_hdr.len), &csum_hdr); + pos += len; +#endif + } else { /* AX172 */ pktlen = rxlen = total_len; total_len = 0; @@ -1114,14 +1371,13 @@ axe_rxeof(struct usbd_xfer *xfer, void * static void axe_txeof(struct usbd_xfer *xfer, void * priv, usbd_status status) { - struct axe_softc *sc; - struct axe_chain *c; - struct ifnet *ifp; + struct axe_chain *c = priv; + struct axe_softc *sc = c->axe_sc; + struct ifnet *ifp = &sc->sc_if; int s; - c = priv; - sc = c->axe_sc; - ifp = &sc->sc_if; + DPRINTFN(10,("%s: %s: enter\n", device_xname(sc->axe_dev),__func__)); + if (sc->axe_dying) return; @@ -1173,11 +1429,12 @@ static void axe_tick_task(void *xsc) { int s; - struct axe_softc *sc; + struct axe_softc *sc = xsc; struct ifnet *ifp; struct mii_data *mii; - sc = xsc; + DPRINTFN(10,("%s: %s: enter\n", device_xname(sc->axe_dev),__func__)); + if (sc == NULL) return; @@ -1224,7 +1481,7 @@ axe_encap(struct axe_softc *sc, struct m * Copy the mbuf data into a contiguous buffer, leaving two * bytes at the beginning to hold the frame length. */ - if (sc->axe_flags & AX178 || sc->axe_flags & AX772) { + if (AXE_IS_178_FAMILY(sc)) { boundary = (sc->axe_udev->ud_speed == USB_SPEED_HIGH) ? 512 : 64; hdr.len = htole16(m->m_pkthdr.len); @@ -1262,48 +1519,52 @@ axe_encap(struct axe_softc *sc, struct m return 0; } + static void -axe_start(struct ifnet *ifp) +axe_csum_cfg(struct axe_softc *sc) { - struct axe_softc *sc; - struct mbuf *m; - - sc = ifp->if_softc; - - if ((sc->axe_flags & AXE_MII) != 0 && sc->axe_link == 0) - return; - - if ((ifp->if_flags & (IFF_OACTIVE|IFF_RUNNING)) != IFF_RUNNING) - return; - - IFQ_POLL(&ifp->if_snd, m); - if (m == NULL) { - return; - } + struct ifnet *ifp = &sc->sc_if; + uint16_t csum1, csum2; - if (axe_encap(sc, m, 0)) { - ifp->if_flags |= IFF_OACTIVE; - return; + if ((sc->axe_flags & AX772B) != 0) { + csum1 = 0; + csum2 = 0; + if ((ifp->if_capenable & IFCAP_CSUM_IPv4_Tx) != 0) + csum1 |= AXE_TXCSUM_IP; + if ((ifp->if_capenable & IFCAP_CSUM_TCPv4_Tx) != 0) + csum1 |= AXE_TXCSUM_TCP; + if ((ifp->if_capenable & IFCAP_CSUM_UDPv4_Tx) != 0) + csum1 |= AXE_TXCSUM_UDP; + if ((ifp->if_capenable & IFCAP_CSUM_TCPv6_Tx) != 0) + csum1 |= AXE_TXCSUM_TCPV6; + if ((ifp->if_capenable & IFCAP_CSUM_UDPv6_Tx) != 0) + csum1 |= AXE_TXCSUM_UDPV6; + axe_cmd(sc, AXE_772B_CMD_WRITE_TXCSUM, csum2, csum1, NULL); + csum1 = 0; + csum2 = 0; + + if ((ifp->if_capenable & IFCAP_CSUM_IPv4_Rx) != 0) + csum1 |= AXE_RXCSUM_IP; + if ((ifp->if_capenable & IFCAP_CSUM_TCPv4_Rx) != 0) + csum1 |= AXE_RXCSUM_TCP; + if ((ifp->if_capenable & IFCAP_CSUM_UDPv4_Rx) != 0) + csum1 |= AXE_RXCSUM_UDP; + if ((ifp->if_capenable & IFCAP_CSUM_TCPv6_Rx) != 0) + csum1 |= AXE_RXCSUM_TCPV6; + if ((ifp->if_capenable & IFCAP_CSUM_UDPv6_Rx) != 0) + csum1 |= AXE_RXCSUM_UDPV6; + axe_cmd(sc, AXE_772B_CMD_WRITE_RXCSUM, csum2, csum1, NULL); } - IFQ_DEQUEUE(&ifp->if_snd, m); - - /* - * If there's a BPF listener, bounce a copy of this frame - * to him. - */ - bpf_mtap(ifp, m); - m_freem(m); - - ifp->if_flags |= IFF_OACTIVE; - - /* - * Set a timeout in case the chip goes out to lunch. - */ - ifp->if_timer = 5; - - return; } + +/* + * XXX axe_init + * XXX axe_init + * XXX axe_init + * XXX axe_init + * XXX axe_init + */ static int axe_init(struct ifnet *ifp) { @@ -1312,7 +1573,8 @@ axe_init(struct ifnet *ifp) usbd_status err; int rxmode; int i, s; - uint8_t eaddr[ETHER_ADDR_LEN]; + + DPRINTFN(10,("%s: %s: enter\n", device_xname(sc->axe_dev),__func__)); s = splnet(); @@ -1324,36 +1586,85 @@ axe_init(struct ifnet *ifp) */ axe_reset(sc); - /* Set MAC address */ - if (sc->axe_flags & AX178 || sc->axe_flags & AX772) { - memcpy(eaddr, CLLADDR(ifp->if_sadl), sizeof(eaddr)); - axe_lock_mii(sc); - axe_cmd(sc, AXE_178_CMD_WRITE_NODEID, 0, 0, eaddr); - axe_unlock_mii(sc); - } - - /* Set transmitter IPG values */ axe_lock_mii(sc); - if (sc->axe_flags & AX178 || sc->axe_flags & AX772) + +#if 0 + ret = asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_2 | + AX_GPIO_GPO2EN, 5, in_pm); +#endif + printf("%s: Ethernet address %s\n", __func__, ether_sprintf(sc->axe_enaddr)); + + /* Set MAC address and transmitter IPG values. */ + if (AXE_IS_178_FAMILY(sc)) { + axe_cmd(sc, AXE_178_CMD_WRITE_NODEID, 0, 0, sc->axe_enaddr); axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->axe_ipgs[2], (sc->axe_ipgs[1] << 8) | (sc->axe_ipgs[0]), NULL); - else { + } else { + axe_cmd(sc, AXE_172_CMD_WRITE_NODEID, 0, 0, sc->axe_enaddr); axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->axe_ipgs[0], NULL); axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->axe_ipgs[1], NULL); axe_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->axe_ipgs[2], NULL); } + if (AXE_IS_178_FAMILY(sc)) { + sc->axe_flags &= ~(AXSTD_FRAME | AXCSUM_FRAME); + if ((sc->axe_flags & AX772B) != 0 && + (ifp->if_capenable & AX_RXCSUM) != 0) { + sc->sc_lenmask = AXE_CSUM_HDR_LEN_MASK; + sc->axe_flags |= AXCSUM_FRAME; + } else { + sc->sc_lenmask = AXE_HDR_LEN_MASK; + sc->axe_flags |= AXSTD_FRAME; + } + } + + /* Configure TX/RX checksum offloading. */ + axe_csum_cfg(sc); + if (sc->axe_flags & AX772B) { + /* AX88772B uses different maximum frame burst configuration. */ + axe_cmd(sc, AXE_772B_CMD_RXCTL_WRITE_CFG, + ax88772b_mfb_table[AX88772B_MFB_16K].threshold, + ax88772b_mfb_table[AX88772B_MFB_16K].byte_cnt, NULL); + } /* Enable receiver, set RX mode */ - rxmode = AXE_RXCMD_BROADCAST | AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE; + rxmode = (AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE); + if (AXE_IS_178_FAMILY(sc)) { + if (sc->axe_flags & AX772B) { + /* + * Select RX header format type 1. Aligning IP + * header on 4 byte boundary is not needed when + * checksum offloading feature is not used + * because we always copy the received frame in + * RX handler. When RX checksum offloading is + * active, aligning IP header is required to + * reflect actual frame length including RX + * header size. + */ + rxmode |= AXE_772B_RXCMD_HDR_TYPE_1; + if (sc->axe_flags & AXCSUM_FRAME) + rxmode |= AXE_772B_RXCMD_IPHDR_ALIGN; + } else { + /* + * Default Rx buffer size is too small to get + * maximum performance. + */ + rxmode |= AXE_178_RXCMD_MFB_16384; + } + } + +/* XXXNH Done above??? */ +#if 1 if (sc->axe_flags & AX772B) - rxmode |= AXE_772B_RXCMD_RH1M; + rxmode |= AXE_772B_RXCMD_HDR_TYPE_1; else if (sc->axe_flags & AX178 || sc->axe_flags & AX772) { if (sc->axe_udev->ud_speed == USB_SPEED_HIGH) { /* Largest possible USB buffer size for AX88178 */ - rxmode |= AXE_178_RXCMD_MFB; + rxmode |= AXE_178_RXCMD_MFB_16384; } - } else + } else { rxmode |= AXE_172_RXCMD_UNICAST; + } +#endif /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) @@ -1362,6 +1673,8 @@ axe_init(struct ifnet *ifp) if (ifp->if_flags & IFF_BROADCAST) rxmode |= AXE_RXCMD_BROADCAST; + DPRINTF(("%s: rxmode 0x%#x\n", __func__, rxmode)); + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); axe_unlock_mii(sc); @@ -1418,6 +1731,45 @@ axe_init(struct ifnet *ifp) return 0; } +static void +axe_start(struct ifnet *ifp) +{ + struct axe_softc *sc; + struct mbuf *m; + + sc = ifp->if_softc; + + if ((ifp->if_flags & (IFF_OACTIVE|IFF_RUNNING)) != IFF_RUNNING) + return; + + IFQ_POLL(&ifp->if_snd, m); + if (m == NULL) { + return; + } + + if (axe_encap(sc, m, 0)) { + ifp->if_flags |= IFF_OACTIVE; + return; + } + IFQ_DEQUEUE(&ifp->if_snd, m); + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + bpf_mtap(ifp, m); + m_freem(m); + + ifp->if_flags |= IFF_OACTIVE; + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + + return; +} + static int axe_ioctl(struct ifnet *ifp, u_long cmd, void *data) { @@ -1498,8 +1850,6 @@ axe_stop(struct ifnet *ifp, int disable) usbd_status err; int i; - axe_reset(sc); - ifp->if_timer = 0; ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); @@ -1530,6 +1880,8 @@ axe_stop(struct ifnet *ifp, int disable) } } + axe_reset(sc); + /* Free RX resources. */ for (i = 0; i < AXE_RX_LIST_CNT; i++) { if (sc->axe_cdata.axe_rx_chain[i].axe_xfer != NULL) { Index: sys/dev/usb/if_axereg.h =================================================================== RCS file: /cvsroot/src/sys/dev/usb/if_axereg.h,v retrieving revision 1.18 diff -u -p -r1.18 if_axereg.h --- sys/dev/usb/if_axereg.h 23 Apr 2016 10:15:31 -0000 1.18 +++ sys/dev/usb/if_axereg.h 1 Dec 2016 21:21:29 -0000 @@ -57,10 +57,11 @@ #define AXE_CMD_CMD(x) ((x) & 0x00FF) #define AXE_172_CMD_READ_RXTX_SRAM 0x2002 -#define AXE_182_CMD_READ_RXTX_SRAM 0x6002 +#define AXE_182_CMD_READ_RXTX_SRAM 0x8002 #define AXE_172_CMD_WRITE_RX_SRAM 0x0103 -#define AXE_172_CMD_WRITE_TX_SRAM 0x0104 #define AXE_182_CMD_WRITE_RXTX_SRAM 0x8103 +#define AXE_172_CMD_WRITE_TX_SRAM 0x0104 + #define AXE_CMD_MII_OPMODE_SW 0x0106 #define AXE_CMD_MII_READ_REG 0x2007 #define AXE_CMD_MII_WRITE_REG 0x2108 @@ -95,6 +96,16 @@ #define AXE_CMD_SW_PHY_STATUS 0x0021 #define AXE_CMD_SW_PHY_SELECT 0x0122 +/* AX88772A and AX88772B only. */ +#define AXE_CMD_READ_VLAN_CTRL 0x4027 +#define AXE_CMD_WRITE_VLAN_CTRL 0x4028 + +#define AXE_772B_CMD_RXCTL_WRITE_CFG 0x012A +#define AXE_772B_CMD_READ_RXCSUM 0x002B +#define AXE_772B_CMD_WRITE_RXCSUM 0x012C +#define AXE_772B_CMD_READ_TXCSUM 0x002D +#define AXE_772B_CMD_WRITE_TXCSUM 0x012E + #define AXE_SW_RESET_CLEAR 0x00 #define AXE_SW_RESET_RR 0x01 #define AXE_SW_RESET_RT 0x02 @@ -110,8 +121,10 @@ #define AXE_178_MEDIA_GMII 0x0001 #define AXE_MEDIA_FULL_DUPLEX 0x0002 #define AXE_172_MEDIA_TX_ABORT_ALLOW 0x0004 -/* AX88178 documentation says to always write 1 to reserved bit... */ + +/* AX88178/88772 documentation says to always write 1 to bit 2 */ #define AXE_178_MEDIA_MAGIC 0x0004 +/* AX88772 documentation says to always write 0 to bit 3 */ #define AXE_178_MEDIA_ENCK 0x0008 #define AXE_172_MEDIA_FLOW_CONTROL_EN 0x0010 #define AXE_178_MEDIA_RXFLOW_CONTROL_EN 0x0010 @@ -123,6 +136,25 @@ #define AXE_178_MEDIA_SBP 0x0800 #define AXE_178_MEDIA_SUPERMAC 0x1000 +#define AXE_RXCMD_PROMISC 0x0001 +#define AXE_RXCMD_ALLMULTI 0x0002 +#define AXE_172_RXCMD_UNICAST 0x0004 +#define AXE_178_RXCMD_KEEP_INVALID_CRC 0x0004 +#define AXE_RXCMD_BROADCAST 0x0008 +#define AXE_RXCMD_MULTICAST 0x0010 +#define AXE_RXCMD_ACCEPT_RUNT 0x0040 /* AX88772B */ +#define AXE_RXCMD_ENABLE 0x0080 +#define AXE_178_RXCMD_MFB_MASK 0x0300 +#define AXE_178_RXCMD_MFB_2048 0x0000 +#define AXE_178_RXCMD_MFB_4096 0x0100 +#define AXE_178_RXCMD_MFB_8192 0x0200 +#define AXE_178_RXCMD_MFB_16384 0x0300 +#define AXE_772B_RXCMD_HDR_TYPE_0 0x0000 +#define AXE_772B_RXCMD_HDR_TYPE_1 0x0100 +#define AXE_772B_RXCMD_IPHDR_ALIGN 0x0200 +#define AXE_772B_RXCMD_ADD_CHKSUM 0x0400 +#define AXE_RXCMD_LOOPBACK 0x1000 /* AX88772A/AX88772B */ + #define AXE_PHY_SEL_PRI 1 #define AXE_PHY_SEL_SEC 0 #define AXE_PHY_TYPE_MASK 0xE0 @@ -141,6 +173,12 @@ #define AXE_772_PHY_NO_EPHY 0x10 /* Embedded 10/100 PHY of AX88772 */ + +/* 178, 772, 772A, 172A, 772B */ +#define AXE_IPG0_DEFAULT 0x15 +#define AXE_IPG1_DEFAULT 0x0c +#define AXE_IPG2_DEFAULT 0x12 + #define AXE_GPIO0_EN 0x01 #define AXE_GPIO0 0x02 #define AXE_GPIO1_EN 0x04 @@ -160,21 +198,75 @@ #define AXE_PHY_MODE_REALTEK_8251CL 0x0E #define AXE_PHY_MODE_ATTANSIC 0x40 -#define AXE_RXCMD_PROMISC 0x0001 -#define AXE_RXCMD_ALLMULTI 0x0002 -#define AXE_172_RXCMD_UNICAST 0x0004 -#define AXE_178_RXCMD_KEEP_INVALID_CRC 0x0004 -#define AXE_RXCMD_BROADCAST 0x0008 -#define AXE_RXCMD_MULTICAST 0x0010 -#define AXE_RXCMD_ENABLE 0x0080 -#define AXE_178_RXCMD_MFB 0x0300 - -#define AXE_NOPHY 0xE0 -#define AXE_INTPHY 0x10 - -#define AXE_772B_RXCMD_RH1M 0x0100 -#define AXE_772B_RXCMD_RH2M 0x0200 -#define AXE_772B_RXCMD_RH3M 0x0400 +/* AX88772A/AX88772B only. */ +#define AXE_SW_PHY_SELECT_EXT 0x0000 +#define AXE_SW_PHY_SELECT_EMBEDDED 0x0001 +#define AXE_SW_PHY_SELECT_AUTO 0x0002 +#define AXE_SW_PHY_SELECT_SS_MII 0x0004 +#define AXE_SW_PHY_SELECT_SS_RVRS_MII 0x0008 +#define AXE_SW_PHY_SELECT_SS_RVRS_RMII 0x000C +#define AXE_SW_PHY_SELECT_SS_ENB 0x0010 +#define AXE_SW_RESET_CLEAR 0x00 +#define AXE_SW_RESET_RR 0x01 +#define AXE_SW_RESET_RT 0x02 +#define AXE_SW_RESET_PRTE 0x04 /* not 772b */ +#define AXE_SW_RESET_PRL 0x08 /* not 772b */ +#define AXE_SW_RESET_BZ 0x10 +#define AXE_SW_RESET_IPRL 0x20 +#define AXE_SW_RESET_IPPD 0x40 +#define AXE_SW_RESET_IPOSC __BIT(7) +/* 772B only */ +#define AXE_SW_RESET_IPPSL_MASK __BITS(9,8) +#define AXE_SW_RESET_IPPSL_0 0 +#define AXE_SW_RESET_IPPSL_1 1 +#define AXE_SW_RESET_IPCOPS __BIT(10) +#define AXE_SW_RESET_IPCOPSC __BIT(11) +#define AXE_SW_RESET_AD __BIT(12) +#define AXE_SW_RESET_IPFPS __BIT(13) +#define AXE_SW_RESET_WOLLP __BIT(14) + +/* AX88772A/AX88772B VLAN control. */ +#define AXE_VLAN_CTRL_ENB 0x00001000 +#define AXE_VLAN_CTRL_STRIP 0x00002000 +#define AXE_VLAN_CTRL_VID1_MASK 0x00000FFF +#define AXE_VLAN_CTRL_VID2_MASK 0x0FFF0000 + +#define AXE_RXCSUM_IP 0x0001 +#define AXE_RXCSUM_IPVE 0x0002 +#define AXE_RXCSUM_IPV6E 0x0004 +#define AXE_RXCSUM_TCP 0x0008 +#define AXE_RXCSUM_UDP 0x0010 +#define AXE_RXCSUM_ICMP 0x0020 +#define AXE_RXCSUM_IGMP 0x0040 +#define AXE_RXCSUM_ICMP6 0x0080 +#define AXE_RXCSUM_TCPV6 0x0100 +#define AXE_RXCSUM_UDPV6 0x0200 +#define AXE_RXCSUM_ICMPV6 0x0400 +#define AXE_RXCSUM_IGMPV6 0x0800 +#define AXE_RXCSUM_ICMP6V6 0x1000 +#define AXE_RXCSUM_FOPC 0x8000 + +#define AXE_RXCSUM_64TE 0x0100 +#define AXE_RXCSUM_PPPOE 0x0200 +#define AXE_RXCSUM_RPCE 0x8000 + +#define AXE_TXCSUM_IP 0x0001 +#define AXE_TXCSUM_TCP 0x0002 +#define AXE_TXCSUM_UDP 0x0004 +#define AXE_TXCSUM_ICMP 0x0008 +#define AXE_TXCSUM_IGMP 0x0010 +#define AXE_TXCSUM_ICMP6 0x0020 +#define AXE_TXCSUM_TCPV6 0x0100 +#define AXE_TXCSUM_UDPV6 0x0200 +#define AXE_TXCSUM_ICMPV6 0x0400 +#define AXE_TXCSUM_IGMPV6 0x0800 +#define AXE_TXCSUM_ICMP6V6 0x1000 + +#define AXE_TXCSUM_64TE 0x0001 +#define AXE_TXCSUM_PPPOE 0x0002 + +#define AXE_NOPHY 0xE0 +#define AXE_INTPHY 0x10 #define AXE_RH1M_RXLEN_MASK 0x07ff @@ -197,23 +289,97 @@ #define AXE_CONFIG_NO 1 #define AXE_IFACE_IDX 0 +/* EEPROM Map. */ +#define AXE_EEPROM_772B_NODE_ID 0x04 +#define AXE_EEPROM_772B_PHY_PWRCFG 0x18 + +struct ax88772b_mfb { + int byte_cnt; + int threshold; + int size; +}; +#define AX88772B_MFB_2K 0 +#define AX88772B_MFB_4K 1 +#define AX88772B_MFB_6K 2 +#define AX88772B_MFB_8K 3 +#define AX88772B_MFB_16K 4 +#define AX88772B_MFB_20K 5 +#define AX88772B_MFB_24K 6 +#define AX88772B_MFB_32K 7 + +struct axe_sframe_hdr { + uint16_t len; +#define AXE_HDR_LEN_MASK 0xFFFF + uint16_t ilen; +} __packed; + +#define AXE_TX_CSUM_PSEUDO_HDR 0x4000 +#define AXE_TX_CSUM_DIS 0x8000 + /* - * The interrupt endpoint is currently unused - * by the ASIX part. + * When RX checksum offloading is enabled, AX88772B uses new RX header + * format and it's not compatible with previous RX header format. In + * addition, IP header align option should be enabled to get correct + * frame size including RX header. Total transferred size including + * the RX header is multiple of 4 and controller will pad necessary + * bytes if the length is not multiple of 4. + * This driver does not enable partial checksum feature which will + * compute 16bit checksum from 14th byte to the end of the frame. If + * this feature is enabled, computed checksum value is embedded into + * RX header which in turn means it uses different RX header format. */ -#define AXE_ENDPT_RX 0x0 -#define AXE_ENDPT_TX 0x1 -#define AXE_ENDPT_INTR 0x2 -#define AXE_ENDPT_MAX 0x3 +struct axe_csum_hdr { + uint16_t len; +#define AXE_CSUM_HDR_LEN_MASK 0x07FF +#define AXE_CSUM_HDR_CRC_ERR 0x1000 +#define AXE_CSUM_HDR_MII_ERR 0x2000 +#define AXE_CSUM_HDR_RUNT 0x4000 +#define AXE_CSUM_HDR_BMCAST 0x8000 + uint16_t ilen; + uint16_t cstatus; +#define AXE_CSUM_HDR_VLAN_MASK 0x0007 +#define AXE_CSUM_HDR_VLAN_STRIP 0x0008 +#define AXE_CSUM_HDR_VLAN_PRI_MASK 0x0070 +#define AXE_CSUM_HDR_L4_CSUM_ERR 0x0100 +#define AXE_CSUM_HDR_L3_CSUM_ERR 0x0200 +#define AXE_CSUM_HDR_L4_TYPE_UDP 0x0400 +#define AXE_CSUM_HDR_L4_TYPE_ICMP 0x0800 +#define AXE_CSUM_HDR_L4_TYPE_IGMP 0x0C00 +#define AXE_CSUM_HDR_L4_TYPE_TCP 0x1000 +#define AXE_CSUM_HDR_L4_TYPE_TCPV6 0x1400 +#define AXE_CSUM_HDR_L4_TYPE_MASK 0x1C00 +#define AXE_CSUM_HDR_L3_TYPE_IPV4 0x2000 +#define AXE_CSUM_HDR_L3_TYPE_IPV6 0x4000 + +#ifdef AXE_APPEND_PARTIAL_CSUM + /* + * These members present only when partial checksum + * offloading is enabled. The checksum value is simple + * 16bit sum of received frame starting at offset 14 of + * the frame to the end of the frame excluding FCS bytes. + */ + uint16_t csum_value; + uint16_t dummy; +#endif +} __packed; + +#define AXE_CSUM_RXBYTES(x) ((x) & AXE_CSUM_HDR_LEN_MASK) + +/* + * The interrupt and CBW endpoints are currently unused byt the driver. + */ +#define AXE_ENDPT_CTRL 0x0 +#define AXE_ENDPT_INTR 0x1 +#define AXE_ENDPT_RX 0x2 +#define AXE_ENDPT_TX 0x3 +#define AXx72A_ENDPT_RXCBW 0x4 /* AX88172A, and AX88772A */ +#define AXx72A_ENDPT_TXCBW 0x5 /* AX88172A, and AX88772A */ +#define AX772B_ENDPT_BOTM 0x5 /* AX88772B */ +#define AXE_ENDPT_MAX 0x6 struct axe_type { struct usb_devno axe_dev; uint16_t axe_flags; -#define AX178 0x0001 /* AX88178 */ -#define AX772 0x0002 /* AX88772 */ -#define AX772B 0x0004 /* AX88772B */ -#define AXE_ANY_PHY 0x1000 /* Chip lies about valid phys */ -#define AXE_MII 0x2000 /* Chip-specific MII handling */ }; struct axe_softc; @@ -235,11 +401,6 @@ struct axe_cdata { int axe_rx_prod; }; -struct axe_sframe_hdr { - uint16_t len; - uint16_t ilen; -} __packed; - struct axe_softc { device_t axe_dev; struct ethercom axe_ec; @@ -250,14 +411,23 @@ struct axe_softc { uint16_t axe_vendor; uint16_t axe_product; - uint16_t axe_flags; + uint32_t axe_flags; /* copied from axe_type */ +#define AX178 __BIT(0) /* AX88178 */ +#define AX772 __BIT(1) /* AX88772 */ +#define AX772A __BIT(2) /* AX88772A */ +#define AX772B __BIT(3) /* AX88772B */ +#define AXSTD_FRAME __BIT(12) +#define AXCSUM_FRAME __BIT(13) int axe_ed[AXE_ENDPT_MAX]; struct usbd_pipe * axe_ep[AXE_ENDPT_MAX]; int axe_if_flags; + int axe_phyno; struct axe_cdata axe_cdata; struct callout axe_stat_ch; + uint8_t axe_enaddr[ETHER_ADDR_LEN]; + int axe_refcnt; bool axe_dying; bool axe_attached; @@ -270,7 +440,9 @@ struct axe_softc { uint8_t axe_ipgs[3]; uint8_t axe_phyaddrs[2]; - int axe_phyno; + uint16_t sc_pwrcfg; + uint16_t sc_lenmask; + struct timeval axe_rx_notice; int axe_bufsz; @@ -278,3 +450,19 @@ struct axe_softc { }; #define ETHER_ALIGN 2 + +#define AXE_IS_178_FAMILY(sc) \ + ((sc)->axe_flags & (AX772 | AX772A | AX772B | AX178)) + +#define AXE_IS_772(sc) \ + ((sc)->axe_flags & (AX772 | AX772A | AX772B)) + +#define AX_RXCSUM \ + (IFCAP_CSUM_IPv4_Rx | \ + IFCAP_CSUM_TCPv4_Rx | IFCAP_CSUM_UDPv4_Rx | \ + IFCAP_CSUM_TCPv6_Rx | IFCAP_CSUM_UDPv6_Rx) + +#define AX_TXCSUM \ + (IFCAP_CSUM_IPv4_Tx | \ + IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_UDPv4_Tx | \ + IFCAP_CSUM_TCPv6_Tx | IFCAP_CSUM_UDPv6_Tx)