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;
 

Reply via email to