diff options
author | Jonathan Gray <jsg@cvs.openbsd.org> | 2010-09-21 08:02:50 +0000 |
---|---|---|
committer | Jonathan Gray <jsg@cvs.openbsd.org> | 2010-09-21 08:02:50 +0000 |
commit | 3dcbcf7e8ac0ed244ef604f75f888a673e2beb8e (patch) | |
tree | 3c9bdbaa9325960566e6c7d3cf5fe10e523057de | |
parent | 98c4435e0aa31d9a6a112e607dd548e493feedad (diff) |
Better PHY probing adapted from FreeBSD by Laurent Ghigonis
fixes problems seen with Linksys USB300M.
-rw-r--r-- | sys/dev/usb/if_axe.c | 112 | ||||
-rw-r--r-- | sys/dev/usb/if_axereg.h | 22 |
2 files changed, 108 insertions, 26 deletions
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c index 9de60510f4c..7b2fe011e43 100644 --- a/sys/dev/usb/if_axe.c +++ b/sys/dev/usb/if_axe.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_axe.c,v 1.96 2010/01/09 05:33:08 jsg Exp $ */ +/* $OpenBSD: if_axe.c,v 1.97 2010/09/21 08:02:49 jsg Exp $ */ /* * Copyright (c) 2005, 2006, 2007 Jonathan Gray <jsg@openbsd.org> @@ -272,6 +272,7 @@ axe_miibus_readreg(struct device *dev, int phy, int reg) struct axe_softc *sc = (void *)dev; usbd_status err; uWord val; + int ival; if (sc->axe_dying) { DPRINTF(("axe: dying\n")); @@ -292,7 +293,7 @@ axe_miibus_readreg(struct device *dev, int phy, int reg) if (sc->axe_phyaddrs[1] != AXE_NOPHY && phy != sc->axe_phyaddrs[1]) return (0); #endif - if (sc->axe_phyaddrs[0] != 0xFF && sc->axe_phyaddrs[0] != phy) + if (sc->axe_phyno != phy) return (0); USETW(val, 0); @@ -310,10 +311,18 @@ axe_miibus_readreg(struct device *dev, int phy, int reg) DPRINTF(("axe_miibus_readreg: phy 0x%x reg 0x%x val 0x%x\n", phy, reg, UGETW(val))); - if (UGETW(val) && UGETW(val) != 0xffff) - sc->axe_phyaddrs[0] = phy; - - return (UGETW(val)); + ival = UGETW(val); + if ((sc->axe_flags & AX772) != 0 && reg == MII_BMSR) { + /* + * BMSR of AX88772 indicates that it supports extended + * capability but the extended status register is + * revered for embedded ethernet PHY. So clear the + * extended capability bit of BMSR. + */ + ival &= ~BMSR_EXTCAP; + } + + return (ival); } void @@ -325,6 +334,8 @@ axe_miibus_writereg(struct device *dev, int phy, int reg, int val) if (sc->axe_dying) return; + if (sc->axe_phyno != phy) + return; USETW(uval, val); @@ -345,6 +356,7 @@ axe_miibus_statchg(struct device *dev) { struct axe_softc *sc = (void *)dev; struct mii_data *mii = GET_MII(sc); + struct ifnet *ifp; int val, err; if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) @@ -352,8 +364,41 @@ axe_miibus_statchg(struct device *dev) else val = 0; + ifp = GET_IFP(sc); + if (mii == NULL || ifp == NULL || + (ifp->if_flags & IFF_RUNNING) == 0) + return; + + sc->axe_link = 0; + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID)) { + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_10_T: + case IFM_100_TX: + sc->axe_link++; + break; + case IFM_1000_T: + if ((sc->axe_flags & AX178) == 0) + break; + sc->axe_link++; + break; + default: + break; + } + } + + /* Lost link, do nothing. */ + if (sc->axe_link == 0) + return; + + val = 0; + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) + val |= AXE_MEDIA_FULL_DUPLEX; + 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: @@ -385,7 +430,6 @@ axe_ifmedia_upd(struct ifnet *ifp) struct axe_softc *sc = ifp->if_softc; struct mii_data *mii = GET_MII(sc); - sc->axe_link = 0; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) @@ -530,7 +574,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_PHY_NO_AX772_EPHY) { /* ask for the embedded PHY */ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL); usbd_delay_ms(sc->axe_udev, 10); @@ -564,6 +608,30 @@ axe_ax88772_init(struct axe_softc *sc) axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); } +static int +axe_get_phyno(struct axe_softc *sc, int sel) +{ + int phyno; + + phyno = -1; + switch (AXE_PHY_TYPE(sc->axe_phyaddrs[sel])) { + case PHY_TYPE_100_HOME: + 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: + break; + } + + return (phyno); +} + /* * Probe for a AX88172 chip. */ @@ -663,6 +731,16 @@ axe_attach(struct device *parent, struct device *self, void *aux) 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) { + printf(" no valid PHY address found, assuming PHY address 0\n"); + sc->axe_phyno = 0; + } + + DPRINTF((" get_phyno %d\n", sc->axe_phyno)); + if (sc->axe_flags & AX178) { axe_ax88178_init(sc); printf(" AX88178"); @@ -686,12 +764,6 @@ axe_attach(struct device *parent, struct device *self, void *aux) axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, (void *)&sc->axe_ipgs); /* - * Work around broken adapters that appear to lie about - * their PHY addresses. - */ - sc->axe_phyaddrs[0] = sc->axe_phyaddrs[1] = 0xFF; - - /* * An ASIX chip was detected. Inform the world. */ printf(", address %s\n", ether_sprintf(eaddr)); @@ -1122,15 +1194,8 @@ axe_tick_task(void *xsc) s = splnet(); mii_tick(mii); - if (!sc->axe_link && mii->mii_media_status & IFM_ACTIVE && - IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { - DPRINTF(("%s: %s: got link\n", - sc->axe_dev.dv_xname, __func__)); - sc->axe_link++; - if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) - axe_start(ifp); - } - + if (sc->axe_link == 0) + axe_miibus_statchg(&sc->axe_dev); timeout_add_sec(&sc->axe_stat_ch, 1); splx(s); @@ -1330,6 +1395,7 @@ axe_init(void *xsc) usbd_transfer(c->axe_xfer); } + sc->axe_link = 0; ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; diff --git a/sys/dev/usb/if_axereg.h b/sys/dev/usb/if_axereg.h index 5daf157e9e2..47e1a488f8b 100644 --- a/sys/dev/usb/if_axereg.h +++ b/sys/dev/usb/if_axereg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_axereg.h,v 1.18 2007/06/10 10:15:35 mbalmer Exp $ */ +/* $OpenBSD: if_axereg.h,v 1.19 2010/09/21 08:02:49 jsg Exp $ */ /* * Copyright (c) 1997, 1998, 1999, 2000-2003 @@ -131,8 +131,23 @@ #define AXE_RXCMD_ENABLE 0x0080 #define AXE_178_RXCMD_MFB 0x0300 -#define AXE_NOPHY 0xE0 -#define AXE_INTPHY 0x10 +#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_PHY_NO_AX772_EPHY 0x10 /* Embedded 10/100 PHY of AX88772 */ #define AXE_TIMEOUT 1000 #define AXE_172_BUFSZ 1536 @@ -222,6 +237,7 @@ struct axe_softc { int axe_link; unsigned char axe_ipgs[3]; unsigned char axe_phyaddrs[2]; + int axe_phyno; struct timeval axe_rx_notice; u_int axe_bufsz; }; |