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.
This commit is contained in:
parent
30bd626232
commit
7d670da32a
@ -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_long, void *);
|
||||
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,70 +260,89 @@ axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf)
|
||||
}
|
||||
|
||||
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"));
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
DPRINTF(("axe_miibus_readreg: phy 0x%x reg 0x%x val 0x%x\n",
|
||||
phy, reg, val));
|
||||
|
||||
return 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_locked(device_t dev, int phy, int reg, int aval)
|
||||
{
|
||||
struct axe_softc *sc = device_private(dev);
|
||||
usbd_status err;
|
||||
uint16_t val;
|
||||
|
||||
val = htole16(aval);
|
||||
|
||||
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);
|
||||
|
||||
if (err) {
|
||||
aprint_error_dev(sc->axe_dev, "write PHY failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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);
|
||||
|
||||
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));
|
||||
|
||||
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 void
|
||||
axe_miibus_writereg(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");
|
||||
if (sc->axe_phyno != phy)
|
||||
return;
|
||||
}
|
||||
|
||||
axe_lock_mii(sc);
|
||||
axe_miibus_writereg_locked(dev, phy, reg, aval);
|
||||
axe_unlock_mii(sc);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -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 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) {
|
||||
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_private_handle priv, usbd_status status)
|
||||
|
||||
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_private_handle priv, usbd_status status)
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user