summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Gray <jsg@cvs.openbsd.org>2010-09-21 08:02:50 +0000
committerJonathan Gray <jsg@cvs.openbsd.org>2010-09-21 08:02:50 +0000
commit3dcbcf7e8ac0ed244ef604f75f888a673e2beb8e (patch)
tree3c9bdbaa9325960566e6c7d3cf5fe10e523057de
parent98c4435e0aa31d9a6a112e607dd548e493feedad (diff)
Better PHY probing adapted from FreeBSD by Laurent Ghigonis
fixes problems seen with Linksys USB300M.
-rw-r--r--sys/dev/usb/if_axe.c112
-rw-r--r--sys/dev/usb/if_axereg.h22
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;
};