From 8f7311e20b70f49422af14474ef59d2bde74ba1f Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 14 Oct 2007 00:07:43 +0000 Subject: [PATCH] converted atheros ethernet driver to phy layer SVN-Revision: 9298 --- target/linux/atheros/config-2.6.23 | 12 +- .../atheros/files/drivers/net/ar2313/ar2313.c | 323 +++++++----------- .../atheros/patches/130-ar2313_ethernet.patch | 36 +- 3 files changed, 156 insertions(+), 215 deletions(-) diff --git a/target/linux/atheros/config-2.6.23 b/target/linux/atheros/config-2.6.23 index 99bf419dbe..222a8db1e4 100644 --- a/target/linux/atheros/config-2.6.23 +++ b/target/linux/atheros/config-2.6.23 @@ -10,6 +10,7 @@ CONFIG_ATHEROS_AR5315=y # CONFIG_ATM is not set CONFIG_BASE_SMALL=0 CONFIG_BITREVERSE=y +# CONFIG_BROADCOM_PHY is not set # CONFIG_BT is not set CONFIG_CMDLINE="console=ttyS0,9600 rootfstype=squashfs,jffs2 init=/etc/preinit" CONFIG_CPU_BIG_ENDIAN=y @@ -45,16 +46,17 @@ CONFIG_CPU_SUPPORTS_HIGHMEM=y # CONFIG_DM9000 is not set CONFIG_DMA_NEED_PCI_MAP_STATE=y CONFIG_DMA_NONCOHERENT=y +# CONFIG_FIXED_PHY is not set CONFIG_FS_POSIX_ACL=y CONFIG_GENERIC_FIND_NEXT_BIT=y # CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set CONFIG_HAS_DMA=y CONFIG_HAS_IOMEM=y CONFIG_HAS_IOPORT=y -CONFIG_HAVE_STD_PC_SERIAL_PORT=y # CONFIG_HOSTAP is not set CONFIG_HW_RANDOM=y # CONFIG_I2C is not set +CONFIG_ICPLUS_PHY=y # CONFIG_IDE is not set # CONFIG_IEEE80211 is not set CONFIG_INITRAMFS_SOURCE="" @@ -127,15 +129,22 @@ CONFIG_MTD_REDBOOT_PARTS_READONLY=y # CONFIG_MTD_ROM is not set # CONFIG_MTD_SLRAM is not set CONFIG_MTD_SPIFLASH=y +CONFIG_NETDEV_1000=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_SCH_FIFO=y +CONFIG_NF_CT_PROTO_GRE=m +CONFIG_NF_NAT_PROTO_GRE=m # CONFIG_NO_IOPORT is not set # CONFIG_PAGE_SIZE_16KB is not set CONFIG_PAGE_SIZE_4KB=y # CONFIG_PAGE_SIZE_64KB is not set # CONFIG_PAGE_SIZE_8KB is not set +CONFIG_PHYLIB=y # CONFIG_PMC_MSP is not set # CONFIG_PMC_YOSEMITE is not set # CONFIG_PNX8550_JBS is not set # CONFIG_PNX8550_STB810 is not set +# CONFIG_QSEMI_PHY is not set # CONFIG_RTC is not set CONFIG_RWSEM_GENERIC_SPINLOCK=y CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y @@ -155,6 +164,7 @@ CONFIG_SERIAL_8250_RUNTIME_UARTS=1 # CONFIG_SIBYTE_RHONE is not set # CONFIG_SIBYTE_SENTOSA is not set # CONFIG_SIBYTE_SWARM is not set +# CONFIG_SMSC_PHY is not set # CONFIG_SOFT_WATCHDOG is not set # CONFIG_SPARSEMEM_STATIC is not set CONFIG_SYSVIPC_SYSCTL=y diff --git a/target/linux/atheros/files/drivers/net/ar2313/ar2313.c b/target/linux/atheros/files/drivers/net/ar2313/ar2313.c index 735ceebd93..1feca14c96 100644 --- a/target/linux/atheros/files/drivers/net/ar2313/ar2313.c +++ b/target/linux/atheros/files/drivers/net/ar2313/ar2313.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -144,16 +145,20 @@ MODULE_DESCRIPTION("AR2313 Ethernet driver"); #define virt_to_phys(x) ((u32)(x) & 0x1fffffff) // prototypes -static short armiiread(struct net_device *dev, short phy, short reg); -static void armiiwrite(struct net_device *dev, short phy, short reg, - short data); #ifdef TX_TIMEOUT static void ar2313_tx_timeout(struct net_device *dev); #endif static void ar2313_halt(struct net_device *dev); static void rx_tasklet_func(unsigned long data); +static void rx_tasklet_cleanup(struct net_device *dev); static void ar2313_multicast_list(struct net_device *dev); +static int mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum); +static int mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum, u16 value); +static int mdiobus_reset(struct mii_bus *bus); +static int mdiobus_probe (struct net_device *dev); +static void ar2313_adjust_link(struct net_device *dev); + #ifndef ERR #define ERR(fmt, args...) printk("%s: " fmt, __func__, ##args) #endif @@ -277,9 +282,28 @@ int __init ar2313_probe(struct platform_device *pdev) dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5], dev->irq); - /* start link poll timer */ - ar2313_setup_timer(dev); + sp->mii_bus.priv = dev; + sp->mii_bus.read = mdiobus_read; + sp->mii_bus.write = mdiobus_write; + sp->mii_bus.reset = mdiobus_reset; + sp->mii_bus.name = "ar2313_eth_mii"; + sp->mii_bus.id = 0; + sp->mii_bus.irq = kmalloc(sizeof(int), GFP_KERNEL); + *sp->mii_bus.irq = PHY_POLL; + + mdiobus_register(&sp->mii_bus); + if (mdiobus_probe(dev) != 0) { + printk(KERN_ERR "ar2313: mdiobus_probe failed"); + rx_tasklet_cleanup(dev); + ar2313_init_cleanup(dev); + unregister_netdev(dev); + kfree(dev); + } else { + /* start link poll timer */ + ar2313_setup_timer(dev); + } + return 0; } @@ -591,7 +615,7 @@ static void ar2313_check_link(struct net_device *dev) struct ar2313_private *sp = dev->priv; u16 phyData; - phyData = armiiread(dev, sp->phy, MII_BMSR); + phyData = mdiobus_read(&sp->mii_bus, sp->phy, MII_BMSR); if (sp->phyData != phyData) { if (phyData & BMSR_LSTATUS) { /* link is present, ready link partner ability to deterine @@ -600,10 +624,10 @@ static void ar2313_check_link(struct net_device *dev) u16 reg; sp->link = 1; - reg = armiiread(dev, sp->phy, MII_BMCR); + reg = mdiobus_read(&sp->mii_bus, sp->phy, MII_BMCR); if (reg & BMCR_ANENABLE) { /* auto neg enabled */ - reg = armiiread(dev, sp->phy, MII_LPA); + reg = mdiobus_read(&sp->mii_bus, sp->phy, MII_LPA); duplex = (reg & (LPA_100FULL | LPA_10FULL)) ? 1 : 0; } else { /* no auto neg, just read duplex config */ @@ -1217,194 +1241,19 @@ static int ar2313_start_xmit(struct sk_buff *skb, struct net_device *dev) return 0; } -static int netdev_get_ecmd(struct net_device *dev, - struct ethtool_cmd *ecmd) -{ - struct ar2313_private *np = dev->priv; - u32 tmp; - - ecmd->supported = - (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | - SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII); - - ecmd->port = PORT_TP; - /* only supports internal transceiver */ - ecmd->transceiver = XCVR_INTERNAL; - /* not sure what this is for */ - ecmd->phy_address = 1; - - ecmd->advertising = ADVERTISED_MII; - tmp = armiiread(dev, np->phy, MII_ADVERTISE); - if (tmp & ADVERTISE_10HALF) - ecmd->advertising |= ADVERTISED_10baseT_Half; - if (tmp & ADVERTISE_10FULL) - ecmd->advertising |= ADVERTISED_10baseT_Full; - if (tmp & ADVERTISE_100HALF) - ecmd->advertising |= ADVERTISED_100baseT_Half; - if (tmp & ADVERTISE_100FULL) - ecmd->advertising |= ADVERTISED_100baseT_Full; - - tmp = armiiread(dev, np->phy, MII_BMCR); - if (tmp & BMCR_ANENABLE) { - ecmd->advertising |= ADVERTISED_Autoneg; - ecmd->autoneg = AUTONEG_ENABLE; - } else { - ecmd->autoneg = AUTONEG_DISABLE; - } - - if (ecmd->autoneg == AUTONEG_ENABLE) { - tmp = armiiread(dev, np->phy, MII_LPA); - if (tmp & (LPA_100FULL | LPA_10FULL)) { - ecmd->duplex = DUPLEX_FULL; - } else { - ecmd->duplex = DUPLEX_HALF; - } - if (tmp & (LPA_100FULL | LPA_100HALF)) { - ecmd->speed = SPEED_100; - } else { - ecmd->speed = SPEED_10; - } - } else { - if (tmp & BMCR_FULLDPLX) { - ecmd->duplex = DUPLEX_FULL; - } else { - ecmd->duplex = DUPLEX_HALF; - } - if (tmp & BMCR_SPEED100) { - ecmd->speed = SPEED_100; - } else { - ecmd->speed = SPEED_10; - } - } - - /* ignore maxtxpkt, maxrxpkt for now */ - - return 0; -} - -static int netdev_set_ecmd(struct net_device *dev, - struct ethtool_cmd *ecmd) -{ - struct ar2313_private *np = dev->priv; - u32 tmp; - - if (ecmd->speed != SPEED_10 && ecmd->speed != SPEED_100) - return -EINVAL; - if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL) - return -EINVAL; - if (ecmd->port != PORT_TP) - return -EINVAL; - if (ecmd->transceiver != XCVR_INTERNAL) - return -EINVAL; - if (ecmd->autoneg != AUTONEG_DISABLE - && ecmd->autoneg != AUTONEG_ENABLE) - return -EINVAL; - /* ignore phy_address, maxtxpkt, maxrxpkt for now */ - - /* WHEW! now lets bang some bits */ - - tmp = armiiread(dev, np->phy, MII_BMCR); - if (ecmd->autoneg == AUTONEG_ENABLE) { - /* turn on autonegotiation */ - tmp |= BMCR_ANENABLE; - printk("%s: Enabling auto-neg\n", dev->name); - } else { - /* turn off auto negotiation, set speed and duplexity */ - tmp &= ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX); - if (ecmd->speed == SPEED_100) - tmp |= BMCR_SPEED100; - if (ecmd->duplex == DUPLEX_FULL) - tmp |= BMCR_FULLDPLX; - printk("%s: Hard coding %d/%s\n", dev->name, - (ecmd->speed == SPEED_100) ? 100 : 10, - (ecmd->duplex == DUPLEX_FULL) ? "full" : "half"); - } - armiiwrite(dev, np->phy, MII_BMCR, tmp); - np->phyData = 0; - return 0; -} - -static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) -{ - struct ar2313_private *np = dev->priv; - u32 cmd; - - if (get_user(cmd, (u32 *) useraddr)) - return -EFAULT; - - switch (cmd) { - /* get settings */ - case ETHTOOL_GSET:{ - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - spin_lock_irq(&np->lock); - netdev_get_ecmd(dev, &ecmd); - spin_unlock_irq(&np->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - /* set settings */ - case ETHTOOL_SSET:{ - struct ethtool_cmd ecmd; - int r; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - spin_lock_irq(&np->lock); - r = netdev_set_ecmd(dev, &ecmd); - spin_unlock_irq(&np->lock); - return r; - } - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST:{ - int tmp; - int r = -EINVAL; - /* if autoneg is off, it's an error */ - tmp = armiiread(dev, np->phy, MII_BMCR); - if (tmp & BMCR_ANENABLE) { - tmp |= (BMCR_ANRESTART); - armiiwrite(dev, np->phy, MII_BMCR, tmp); - r = 0; - } - return r; - } - /* get link status */ - case ETHTOOL_GLINK:{ - struct ethtool_value edata = { ETHTOOL_GLINK }; - edata.data = - (armiiread(dev, np->phy, MII_BMSR) & BMSR_LSTATUS) ? 1 : 0; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - } - - return -EOPNOTSUPP; -} - static int ar2313_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct mii_ioctl_data *data = (struct mii_ioctl_data *) &ifr->ifr_data; - + struct ar2313_private *sp = dev->priv; + int ret; + switch (cmd) { case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, (void *) ifr->ifr_data); - - case SIOCGMIIPHY: /* Get address of MII PHY in use. */ - data->phy_id = 1; - /* Fall Through */ - - case SIOCGMIIREG: /* Read MII PHY register. */ - data->val_out = armiiread(dev, data->phy_id & 0x1f, - data->reg_num & 0x1f); - return 0; - case SIOCSMIIREG: /* Write MII PHY register. */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - armiiwrite(dev, data->phy_id & 0x1f, - data->reg_num & 0x1f, data->val_in); - return 0; + spin_lock_irq(&sp->lock); + ret = phy_ethtool_ioctl(sp->phy_dev, (void *) ifr->ifr_data); + spin_unlock_irq(&sp->lock); + return ret; case SIOCSIFHWADDR: if (copy_from_user @@ -1417,7 +1266,12 @@ static int ar2313_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) (ifr->ifr_data, dev->dev_addr, sizeof(dev->dev_addr))) return -EFAULT; return 0; - + + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + return phy_mii_ioctl(sp->phy_dev, data, cmd); + default: break; } @@ -1432,26 +1286,101 @@ static struct net_device_stats *ar2313_get_stats(struct net_device *dev) } +static void ar2313_adjust_link(struct net_device *dev) +{ + printk(KERN_ERR " ar2313_adjust_link implementation missing\n"); +} + #define MII_ADDR(phy, reg) \ ((reg << MII_ADDR_REG_SHIFT) | (phy << MII_ADDR_PHY_SHIFT)) -static short armiiread(struct net_device *dev, short phy, short reg) +static int +mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum) { + struct net_device *const dev = bus->priv; struct ar2313_private *sp = (struct ar2313_private *) dev->priv; volatile ETHERNET_STRUCT *ethernet = sp->phy_regs; - ethernet->mii_addr = MII_ADDR(phy, reg); + ethernet->mii_addr = MII_ADDR(phy_addr, regnum); while (ethernet->mii_addr & MII_ADDR_BUSY); return (ethernet->mii_data >> MII_DATA_SHIFT); } -static void -armiiwrite(struct net_device *dev, short phy, short reg, short data) +static int +mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum, + u16 value) { + struct net_device *const dev = bus->priv; struct ar2313_private *sp = (struct ar2313_private *) dev->priv; volatile ETHERNET_STRUCT *ethernet = sp->phy_regs; while (ethernet->mii_addr & MII_ADDR_BUSY); - ethernet->mii_data = data << MII_DATA_SHIFT; - ethernet->mii_addr = MII_ADDR(phy, reg) | MII_ADDR_WRITE; + ethernet->mii_data = value << MII_DATA_SHIFT; + ethernet->mii_addr = MII_ADDR(phy_addr, regnum) | MII_ADDR_WRITE; + + return 0; } + +static int mdiobus_reset(struct mii_bus *bus) +{ + struct net_device *const dev = bus->priv; + + ar2313_reset_reg(dev); + + return 0; +} + +static int mdiobus_probe (struct net_device *dev) +{ + struct ar2313_private *const sp = (struct ar2313_private *) dev->priv; + struct phy_device *phydev = NULL; + int phy_addr; + + /* find the first (lowest address) PHY on the current MAC's MII bus */ + for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) + if (sp->mii_bus.phy_map[phy_addr]) { + phydev = sp->mii_bus.phy_map[phy_addr]; + break; /* break out with first one found */ + } + + if (!phydev) { + printk (KERN_ERR "ar2313:%s: no PHY found\n", dev->name); + return -1; + } + + /* now we are supposed to have a proper phydev, to attach to... */ + BUG_ON(!phydev); + BUG_ON(phydev->attached_dev); + + phydev = phy_connect(dev, phydev->dev.bus_id, &ar2313_adjust_link, 0, + PHY_INTERFACE_MODE_MII); + + if (IS_ERR(phydev)) { + printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); + return PTR_ERR(phydev); + } + + /* mask with MAC supported features */ + phydev->supported &= (SUPPORTED_10baseT_Half + | SUPPORTED_10baseT_Full + | SUPPORTED_100baseT_Half + | SUPPORTED_100baseT_Full + | SUPPORTED_Autoneg + /* | SUPPORTED_Pause | SUPPORTED_Asym_Pause */ + | SUPPORTED_MII + | SUPPORTED_TP); + + phydev->advertising = phydev->supported; + + //sp->old_link = 0; + //sp->old_speed = 0; + //sp->old_duplex = -1; + sp->phy_dev = phydev; + + printk(KERN_INFO "%s: attached PHY driver [%s] " + "(mii_bus:phy_addr=%s)\n", + dev->name, phydev->drv->name, phydev->dev.bus_id); + + return 0; +} + diff --git a/target/linux/atheros/patches/130-ar2313_ethernet.patch b/target/linux/atheros/patches/130-ar2313_ethernet.patch index 65fc51e7a8..721eba663f 100644 --- a/target/linux/atheros/patches/130-ar2313_ethernet.patch +++ b/target/linux/atheros/patches/130-ar2313_ethernet.patch @@ -1,9 +1,10 @@ -diff -urN linux.old/drivers/net/Kconfig linux.eth/drivers/net/Kconfig ---- linux.old/drivers/net/Kconfig 2006-12-14 23:53:29.000000000 +0100 -+++ linux.eth/drivers/net/Kconfig 2006-12-16 04:30:11.000000000 +0100 -@@ -324,6 +324,12 @@ - - source "drivers/net/arm/Kconfig" +Index: linux-2.6.23/drivers/net/Kconfig +=================================================================== +--- linux-2.6.23.orig/drivers/net/Kconfig 2007-10-14 00:55:22.000000000 +0200 ++++ linux-2.6.23/drivers/net/Kconfig 2007-10-14 00:56:32.000000000 +0200 +@@ -348,6 +348,12 @@ + AX88796 driver, using platform bus to provide + chip detection and resources +config AR2313 + tristate "AR2313 Ethernet support" @@ -13,15 +14,16 @@ diff -urN linux.old/drivers/net/Kconfig linux.eth/drivers/net/Kconfig + config MACE tristate "MACE (Power Mac ethernet) support" - depends on NET_ETHERNET && PPC_PMAC && PPC32 -diff -urN linux.old/drivers/net/Makefile linux.eth/drivers/net/Makefile ---- linux.old/drivers/net/Makefile 2006-12-14 23:53:29.000000000 +0100 -+++ linux.eth/drivers/net/Makefile 2006-12-16 04:30:11.000000000 +0100 -@@ -11,6 +11,7 @@ - obj-$(CONFIG_BONDING) += bonding/ - obj-$(CONFIG_ATL1) += atl1/ - obj-$(CONFIG_GIANFAR) += gianfar_driver.o + depends on PPC_PMAC && PPC32 +Index: linux-2.6.23/drivers/net/Makefile +=================================================================== +--- linux-2.6.23.orig/drivers/net/Makefile 2007-10-14 00:55:22.000000000 +0200 ++++ linux-2.6.23/drivers/net/Makefile 2007-10-14 00:56:55.000000000 +0200 +@@ -182,6 +182,7 @@ + obj-$(CONFIG_LGUEST_NET) += lguest_net.o + obj-$(CONFIG_MIPS_JAZZ_SONIC) += jazzsonic.o + obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o +obj-$(CONFIG_AR2313) += ar2313/ - - gianfar_driver-objs := gianfar.o \ - gianfar_ethtool.o \ + obj-$(CONFIG_MIPS_SIM_NET) += mipsnet.o + obj-$(CONFIG_SGI_IOC3_ETH) += ioc3-eth.o + obj-$(CONFIG_DECLANCE) += declance.o