Module Name: src Committed By: roy Date: Fri Nov 8 17:46:35 UTC 2013
Modified Files: src/sys/dev/usb: if_axe.c if_axereg.h Log Message: Add GPIO programming to support more PHY. Most of the work done by skrll@ taken from FreeBSD, but finished and tested by me using an AX88178 axe and a RTL8211 PHY. To generate a diff of this commit: cvs rdiff -u -r1.65 -r1.66 src/sys/dev/usb/if_axe.c cvs rdiff -u -r1.15 -r1.16 src/sys/dev/usb/if_axereg.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/usb/if_axe.c diff -u src/sys/dev/usb/if_axe.c:1.65 src/sys/dev/usb/if_axe.c:1.66 --- src/sys/dev/usb/if_axe.c:1.65 Thu Sep 12 21:03:11 2013 +++ src/sys/dev/usb/if_axe.c Fri Nov 8 17:46:35 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: if_axe.c,v 1.65 2013/09/12 21:03:11 martin Exp $ */ +/* $NetBSD: if_axe.c,v 1.66 2013/11/08 17:46:35 roy Exp $ */ /* $OpenBSD: if_axe.c,v 1.96 2010/01/09 05:33:08 jsg Exp $ */ /* @@ -89,7 +89,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_axe.c,v 1.65 2013/09/12 21:03:11 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_axe.c,v 1.66 2013/11/08 17:46:35 roy Exp $"); #ifdef _KERNEL_OPT #include "opt_inet.h" @@ -195,7 +195,9 @@ static int axe_ioctl(struct ifnet *, u_l static int axe_init(struct ifnet *); static void axe_stop(struct ifnet *, int); static void axe_watchdog(struct ifnet *); +static int axe_miibus_readreg_locked(device_t, int, int); static int axe_miibus_readreg(device_t, int, int); +static void axe_miibus_writereg_locked(device_t, int, int, int); static void axe_miibus_writereg(device_t, int, int, int); static void axe_miibus_statchg(struct ifnet *); static int axe_cmd(struct axe_softc *, int, int, int, void *); @@ -258,65 +260,68 @@ axe_cmd(struct axe_softc *sc, int cmd, i } static int -axe_miibus_readreg(device_t dev, int phy, int reg) +axe_miibus_readreg_locked(device_t dev, int phy, int reg) { struct axe_softc *sc = device_private(dev); usbd_status err; uint16_t val; - if (sc->axe_dying) { - DPRINTF(("axe: dying\n")); - return 0; + 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) { + aprint_error_dev(sc->axe_dev, "read PHY failed\n"); + return -1; } - /* - * The chip tells us the MII address of any supported - * PHYs attached to the chip, so only read from those. - * - * But if the chip lies about its PHYs, read from any. - */ - val = 0; + val = le16toh(val); + if (sc->axe_flags & AX772 && reg == MII_BMSR) { + /* + * BMSR of AX88772 indicates it supports extended + * capability but the extended status register is + * reserverd for embedded ethernet PHY. So clear the + * extended capability bit of BMSR. + */ + val &= ~BMSR_EXTCAP; + } - if ((phy == sc->axe_phyaddrs[0]) || (phy == sc->axe_phyaddrs[1]) || - (sc->axe_flags & AXE_ANY_PHY)) { - axe_lock_mii(sc); - 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); - axe_unlock_mii(sc); + DPRINTF(("axe_miibus_readreg: phy 0x%x reg 0x%x val 0x%x\n", + phy, reg, val)); - if (err) { - aprint_error_dev(sc->axe_dev, "read PHY failed\n"); - return -1; - } - DPRINTF(("axe_miibus_readreg: phy 0x%x reg 0x%x val 0x%x\n", - phy, reg, val)); + return val; +} - if (val && val != 0xffff) - sc->axe_phyaddrs[0] = phy; - } else { - DPRINTF(("axe_miibus_readreg: ignore read from phy 0x%x\n", - phy)); - } - return le16toh(val); +static int +axe_miibus_readreg(device_t dev, int phy, int reg) +{ + struct axe_softc *sc = device_private(dev); + int val; + + if (sc->axe_dying) + return 0; + + if (sc->axe_phyno != phy) + return 0; + + axe_lock_mii(sc); + val = axe_miibus_readreg_locked(dev, phy, reg); + axe_unlock_mii(sc); + + return val; } static void -axe_miibus_writereg(device_t dev, int phy, int reg, int aval) +axe_miibus_writereg_locked(device_t dev, int phy, int reg, int aval) { struct axe_softc *sc = device_private(dev); usbd_status err; uint16_t val; - if (sc->axe_dying) - return; - val = htole16(aval); - axe_lock_mii(sc); + axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); err = axe_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, (void *)&val); axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); - axe_unlock_mii(sc); if (err) { aprint_error_dev(sc->axe_dev, "write PHY failed\n"); @@ -325,6 +330,22 @@ axe_miibus_writereg(device_t dev, int ph } static void +axe_miibus_writereg(device_t dev, int phy, int reg, int aval) +{ + struct axe_softc *sc = device_private(dev); + + if (sc->axe_dying) + return; + + if (sc->axe_phyno != phy) + return; + + axe_lock_mii(sc); + axe_miibus_writereg_locked(dev, phy, reg, aval); + axe_unlock_mii(sc); +} + +static void axe_miibus_statchg(struct ifnet *ifp) { struct axe_softc *sc = ifp->if_softc; @@ -338,7 +359,8 @@ axe_miibus_statchg(struct ifnet *ifp) if (sc->axe_flags & AX178 || sc->axe_flags & AX772) { 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)) { case IFM_1000_T: val |= AXE_178_MEDIA_GMII | AXE_178_MEDIA_ENCK; @@ -461,13 +483,41 @@ axe_reset(struct axe_softc *sc) DELAY(1000); } +static int +axe_get_phyno(struct axe_softc *sc, int sel) +{ + int phyno; + + switch (AXE_PHY_TYPE(sc->axe_phyaddrs[sel])) { + case PHY_TYPE_100_HOME: + /* FALLTHROUGH */ + case PHY_TYPE_GIG: + phyno = AXE_PHY_NO(sc->axe_phyaddrs[sel]); + break; + case PHY_TYPE_SPECIAL: + /* FALLTHROUGH */ + case PHY_TYPE_RSVD: + /* FALLTHROUGH */ + case PHY_TYPE_NON_SUP: + /* FALLTHROUGH */ + default: + phyno = -1; + break; + } + + return phyno; +} + +#define AXE_GPIO_WRITE(x, y) do { \ + axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, (x), NULL); \ + usbd_delay_ms(sc->axe_udev, hztoms(y)); \ +} while (0) + static void axe_ax88178_init(struct axe_softc *sc) { -#ifdef AXE_DEBUG - int gpio0 = 0, phymode = 0; -#endif - uint16_t eeprom; + int gpio0, ledmode, phymode; + uint16_t eeprom, val; axe_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL); /* XXX magic */ @@ -479,35 +529,90 @@ axe_ax88178_init(struct axe_softc *sc) DPRINTF((" EEPROM is 0x%x\n", eeprom)); /* if EEPROM is invalid we have to use to GPIO0 */ -#ifdef AXE_DEBUG if (eeprom == 0xffff) { - phymode = 0; + phymode = AXE_PHY_MODE_MARVELL; gpio0 = 1; + ledmode = 0; } else { - phymode = eeprom & 7; + phymode = eeprom & 0x7f; gpio0 = (eeprom & 0x80) ? 0 : 1; + ledmode = eeprom >> 8; } -#endif DPRINTF(("use gpio0: %d, phymode %d\n", gpio0, phymode)); - axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x008c, NULL); - usbd_delay_ms(sc->axe_udev, 40); - if ((eeprom >> 8) != 1) { - axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL); - usbd_delay_ms(sc->axe_udev, 30); - - axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x001c, NULL); - usbd_delay_ms(sc->axe_udev, 300); - - axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL); - usbd_delay_ms(sc->axe_udev, 30); - } else { - DPRINTF(("axe gpio phymode == 1 path\n")); - axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x0004, NULL); - usbd_delay_ms(sc->axe_udev, 30); - axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x000c, NULL); - usbd_delay_ms(sc->axe_udev, 30); + /* Program GPIOs depending on PHY hardware. */ + switch (phymode) { + case AXE_PHY_MODE_MARVELL: + if (gpio0 == 1) { + AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO0_EN, + hz / 32); + AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2 | AXE_GPIO2_EN, + hz / 32); + AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2_EN, hz / 4); + AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2 | AXE_GPIO2_EN, + hz / 32); + } else { + AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | + AXE_GPIO1_EN, hz / 3); + if (ledmode == 1) { + AXE_GPIO_WRITE(AXE_GPIO1_EN, hz / 3); + AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN, + hz / 3); + } else { + AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | + AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); + AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | + AXE_GPIO2_EN, hz / 4); + AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | + AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); + } + } + break; + case AXE_PHY_MODE_CICADA: + case AXE_PHY_MODE_CICADA_V2: + case AXE_PHY_MODE_CICADA_V2_ASIX: + if (gpio0 == 1) + AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO0 | + AXE_GPIO0_EN, hz / 32); + else + AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | + AXE_GPIO1_EN, hz / 32); + break; + case AXE_PHY_MODE_AGERE: + AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | + AXE_GPIO1_EN, hz / 32); + AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 | + AXE_GPIO2_EN, hz / 32); + AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2_EN, hz / 4); + AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 | + AXE_GPIO2_EN, hz / 32); + break; + case AXE_PHY_MODE_REALTEK_8211CL: + case AXE_PHY_MODE_REALTEK_8211BN: + case AXE_PHY_MODE_REALTEK_8251CL: + val = gpio0 == 1 ? AXE_GPIO0 | AXE_GPIO0_EN : + AXE_GPIO1 | AXE_GPIO1_EN; + AXE_GPIO_WRITE(val, hz / 32); + AXE_GPIO_WRITE(val | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); + AXE_GPIO_WRITE(val | AXE_GPIO2_EN, hz / 4); + AXE_GPIO_WRITE(val | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); + if (phymode == AXE_PHY_MODE_REALTEK_8211CL) { + axe_miibus_writereg_locked(sc->axe_dev, + sc->axe_phyno, 0x1F, 0x0005); + axe_miibus_writereg_locked(sc->axe_dev, + sc->axe_phyno, 0x0C, 0x0000); + val = axe_miibus_readreg_locked(sc->axe_dev, + sc->axe_phyno, 0x0001); + axe_miibus_writereg_locked(sc->axe_dev, + sc->axe_phyno, 0x01, val | 0x0080); + axe_miibus_writereg_locked(sc->axe_dev, + sc->axe_phyno, 0x1F, 0x0000); + } + break; + default: + /* Unknown PHY model or no need to program GPIOs. */ + break; } /* soft reset */ @@ -529,7 +634,7 @@ axe_ax88772_init(struct axe_softc *sc) axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL); usbd_delay_ms(sc->axe_udev, 40); - if (sc->axe_phyaddrs[1] == AXE_INTPHY) { + 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); usbd_delay_ms(sc->axe_udev, 10); @@ -662,6 +767,13 @@ axe_attach(device_t parent, device_t sel DPRINTF((" phyaddrs[0]: %x phyaddrs[1]: %x\n", sc->axe_phyaddrs[0], sc->axe_phyaddrs[1])); + sc->axe_phyno = axe_get_phyno(sc, AXE_PHY_SEL_PRI); + if (sc->axe_phyno == -1) + sc->axe_phyno = axe_get_phyno(sc, AXE_PHY_SEL_SEC); + if (sc->axe_phyno == -1) { + DPRINTF((" no valid PHY address found, assuming PHY address 0\n")); + sc->axe_phyno = 0; + } if (sc->axe_flags & AX178) axe_ax88178_init(sc); @@ -1018,6 +1130,9 @@ axe_txeof(usbd_xfer_handle xfer, usbd_pr s = splnet(); + ifp->if_timer = 0; + ifp->if_flags &= ~IFF_OACTIVE; + if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { splx(s); @@ -1031,14 +1146,11 @@ axe_txeof(usbd_xfer_handle xfer, usbd_pr splx(s); return; } - - ifp->if_timer = 0; - ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_opackets++; if (!IFQ_IS_EMPTY(&ifp->if_snd)) axe_start(ifp); - ifp->if_opackets++; splx(s); } Index: src/sys/dev/usb/if_axereg.h diff -u src/sys/dev/usb/if_axereg.h:1.15 src/sys/dev/usb/if_axereg.h:1.16 --- src/sys/dev/usb/if_axereg.h:1.15 Sun Nov 25 22:22:39 2012 +++ src/sys/dev/usb/if_axereg.h Fri Nov 8 17:46:35 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: if_axereg.h,v 1.15 2012/11/25 22:22:39 christos Exp $ */ +/* $NetBSD: if_axereg.h,v 1.16 2013/11/08 17:46:35 roy Exp $ */ /* * Copyright (c) 1997, 1998, 1999, 2000-2003 @@ -122,6 +122,43 @@ #define AXE_178_MEDIA_SBP 0x0800 #define AXE_178_MEDIA_SUPERMAC 0x1000 +#define AXE_PHY_SEL_PRI 1 +#define AXE_PHY_SEL_SEC 0 +#define AXE_PHY_TYPE_MASK 0xE0 +#define AXE_PHY_TYPE_SHIFT 5 +#define AXE_PHY_TYPE(x) \ + (((x) & AXE_PHY_TYPE_MASK) >> AXE_PHY_TYPE_SHIFT) + +#define PHY_TYPE_100_HOME 0 /* 10/100 or 1M HOME PHY */ +#define PHY_TYPE_GIG 1 /* Gigabit PHY */ +#define PHY_TYPE_SPECIAL 4 /* Special case */ +#define PHY_TYPE_RSVD 5 /* Reserved */ +#define PHY_TYPE_NON_SUP 7 /* Non-supported PHY */ + +#define AXE_PHY_NO_MASK 0x1F +#define AXE_PHY_NO(x) ((x) & AXE_PHY_NO_MASK) + +#define AXE_772_PHY_NO_EPHY 0x10 /* Embedded 10/100 PHY of AX88772 */ + +#define AXE_GPIO0_EN 0x01 +#define AXE_GPIO0 0x02 +#define AXE_GPIO1_EN 0x04 +#define AXE_GPIO1 0x08 +#define AXE_GPIO2_EN 0x10 +#define AXE_GPIO2 0x20 +#define AXE_GPIO_RELOAD_EEPROM 0x80 + +#define AXE_PHY_MODE_MARVELL 0x00 +#define AXE_PHY_MODE_CICADA 0x01 +#define AXE_PHY_MODE_AGERE 0x02 +#define AXE_PHY_MODE_CICADA_V2 0x05 +#define AXE_PHY_MODE_AGERE_GMII 0x06 +#define AXE_PHY_MODE_CICADA_V2_ASIX 0x09 +#define AXE_PHY_MODE_REALTEK_8211CL 0x0C +#define AXE_PHY_MODE_REALTEK_8211BN 0x0D +#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 @@ -232,6 +269,7 @@ struct axe_softc { uint8_t axe_ipgs[3]; uint8_t axe_phyaddrs[2]; + int axe_phyno; struct timeval axe_rx_notice; int axe_bufsz;