From c264ce3344facff606749ea97408f0f46f91e88d Mon Sep 17 00:00:00 2001 From: juhosg Date: Mon, 28 Apr 2008 18:44:50 +0000 Subject: [PATCH] [ixp4xx] add switch support to the npe driver git-svn-id: svn://svn.openwrt.org/openwrt/trunk@10966 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- .../202-npe_driver_switch_support.patch | 252 ++++++++++++++++++ .../203-npe_driver_phy_reset_autoneg.patch | 44 +++ 2 files changed, 296 insertions(+) create mode 100644 target/linux/ixp4xx/patches-2.6.24/202-npe_driver_switch_support.patch create mode 100644 target/linux/ixp4xx/patches-2.6.24/203-npe_driver_phy_reset_autoneg.patch diff --git a/target/linux/ixp4xx/patches-2.6.24/202-npe_driver_switch_support.patch b/target/linux/ixp4xx/patches-2.6.24/202-npe_driver_switch_support.patch new file mode 100644 index 000000000..0163829b0 --- /dev/null +++ b/target/linux/ixp4xx/patches-2.6.24/202-npe_driver_switch_support.patch @@ -0,0 +1,252 @@ +Index: linux-2.6.24.2/drivers/net/arm/ixp4xx_eth.c +=================================================================== +--- linux-2.6.24.2.orig/drivers/net/arm/ixp4xx_eth.c ++++ linux-2.6.24.2/drivers/net/arm/ixp4xx_eth.c +@@ -165,14 +165,15 @@ struct port { + struct net_device *netdev; + struct napi_struct napi; + struct net_device_stats stat; +- struct mii_if_info mii; ++ struct mii_if_info mii[IXP4XX_ETH_PHY_MAX_ADDR]; + struct delayed_work mdio_thread; + struct eth_plat_info *plat; + buffer_t *rx_buff_tab[RX_DESCS], *tx_buff_tab[TX_DESCS]; + struct desc *desc_tab; /* coherent */ + u32 desc_tab_phys; + int id; /* logical port ID */ +- u16 mii_bmcr; ++ u16 mii_bmcr[IXP4XX_ETH_PHY_MAX_ADDR]; ++ int phy_count; + }; + + /* NPE message structure */ +@@ -316,13 +317,14 @@ static void mdio_write(struct net_device + spin_unlock_irqrestore(&mdio_lock, flags); + } + +-static void phy_reset(struct net_device *dev, int phy_id) ++static void phy_reset(struct net_device *dev, int idx) + { + struct port *port = netdev_priv(dev); ++ int phy_id = port->mii[idx].phy_id; + int cycles = 0; + +- mdio_write(dev, phy_id, MII_BMCR, port->mii_bmcr | BMCR_RESET); +- ++ mdio_write(dev, phy_id, MII_BMCR, port->mii_bmcr[idx] | BMCR_RESET); ++ + while (cycles < MAX_MII_RESET_RETRIES) { + if (!(mdio_read(dev, phy_id, MII_BMCR) & BMCR_RESET)) { + #if DEBUG_MDIO +@@ -335,12 +337,12 @@ static void phy_reset(struct net_device + cycles++; + } + +- printk(KERN_ERR "%s: MII reset failed\n", dev->name); ++ printk(KERN_ERR "%s: MII reset failed on PHY%2d\n", dev->name, phy_id); + } + +-static void eth_set_duplex(struct port *port) ++static void eth_set_duplex(struct port *port, int full_duplex) + { +- if (port->mii.full_duplex) ++ if (full_duplex) + __raw_writel(DEFAULT_TX_CNTRL0 & ~TX_CNTRL0_HALFDUPLEX, + &port->regs->tx_control[0]); + else +@@ -348,7 +350,7 @@ static void eth_set_duplex(struct port * + &port->regs->tx_control[0]); + } + +- ++#if 0 + static void phy_check_media(struct port *port, int init) + { + if (mii_check_media(&port->mii, 1, init)) +@@ -367,7 +369,63 @@ static void phy_check_media(struct port + } + } + } ++#else ++static void phy_update_link(struct net_device *dev, int link) ++{ ++ int prev_link = netif_carrier_ok(dev); ++ ++ if (!prev_link && link) { ++ printk(KERN_INFO "%s: link up\n", dev->name); ++ netif_carrier_on(dev); ++ } else if (prev_link && !link) { ++ printk(KERN_INFO "%s: link down\n", dev->name); ++ netif_carrier_off(dev); ++ } ++} ++ ++static void phy_check_media(struct port *port, int init) ++{ ++ struct net_device *dev = port->netdev; ++ ++ if (port->phy_count == 1) { ++ struct mii_if_info *mii = &port->mii[0]; ++ ++ if (mii_check_media(mii, 1, init)) ++ eth_set_duplex(port, mii->full_duplex); ++ ++ if (mii->force_media) /* mii_check_media() doesn't work */ ++ phy_update_link(dev, mii_link_ok(mii)); ++ } else { ++ int cur_link = 0; ++ int i; ++ ++ if (init) ++ eth_set_duplex(port, 1); ++ ++ for (i = 0; i < port->phy_count; i++) ++ cur_link |= mii_link_ok(&port->mii[i]); ++ ++ phy_update_link(dev, cur_link); ++ } ++} ++#endif + ++static void phy_power_down(struct net_device *dev, int idx) ++{ ++ struct port *port = netdev_priv(dev); ++ int phy_id = port->mii[idx].phy_id; ++ ++ port->mii_bmcr[idx] = mdio_read(dev, phy_id, MII_BMCR) & ++ ~(BMCR_RESET | BMCR_PDOWN); ++ mdio_write(dev, phy_id, MII_BMCR, port->mii_bmcr[idx] | BMCR_PDOWN); ++} ++ ++static void phy_power_up(struct net_device *dev, int idx) ++{ ++ struct port *port = netdev_priv(dev); ++ ++ mdio_write(dev, port->mii[idx].phy_id, MII_BMCR, port->mii_bmcr[idx]); ++} + + static void mdio_thread(struct work_struct *work) + { +@@ -790,9 +848,12 @@ static int eth_ioctl(struct net_device * + + if (!netif_running(dev)) + return -EINVAL; +- err = generic_mii_ioctl(&port->mii, if_mii(req), cmd, &duplex_chg); ++ if (port->phy_count != 1) ++ return -EOPNOTSUPP; ++ ++ err = generic_mii_ioctl(&port->mii[0], if_mii(req), cmd, &duplex_chg); + if (duplex_chg) +- eth_set_duplex(port); ++ eth_set_duplex(port, port->mii[0].full_duplex); + return err; + } + +@@ -944,7 +1005,8 @@ static int eth_open(struct net_device *d + } + } + +- mdio_write(dev, port->plat->phy, MII_BMCR, port->mii_bmcr); ++ for (i = 0; i < port->phy_count; i++) ++ phy_power_up(dev, i); + + memset(&msg, 0, sizeof(msg)); + msg.cmd = NPE_VLAN_SETRXQOSENTRY; +@@ -1103,10 +1165,8 @@ static int eth_close(struct net_device * + printk(KERN_CRIT "%s: unable to disable loopback\n", + dev->name); + +- port->mii_bmcr = mdio_read(dev, port->plat->phy, MII_BMCR) & +- ~(BMCR_RESET | BMCR_PDOWN); /* may have been altered */ +- mdio_write(dev, port->plat->phy, MII_BMCR, +- port->mii_bmcr | BMCR_PDOWN); ++ for (i = 0; i < port->phy_count; i++) ++ phy_power_down(dev, i); + + if (!ports_open) + qmgr_disable_irq(TXDONE_QUEUE); +@@ -1117,6 +1177,42 @@ static int eth_close(struct net_device * + return 0; + } + ++static void eth_add_phy(struct net_device *dev, int phy_id) ++{ ++ struct port *port = netdev_priv(dev); ++ int i; ++ ++ i = port->phy_count++; ++ ++ port->mii[i].dev = dev; ++ port->mii[i].mdio_read = mdio_read; ++ port->mii[i].mdio_write = mdio_write; ++ port->mii[i].phy_id = phy_id; ++ port->mii[i].phy_id_mask = 0x1F; ++ port->mii[i].reg_num_mask = 0x1F; ++ ++ printk(KERN_INFO "%s: MII PHY %i on %s\n", dev->name, phy_id, ++ npe_name(port->npe)); ++ ++ phy_reset(dev, i); ++ phy_power_down(dev, i); ++} ++ ++static void eth_init_mii(struct net_device *dev) ++{ ++ struct port *port = netdev_priv(dev); ++ ++ if (port->plat->phy < IXP4XX_ETH_PHY_MAX_ADDR) { ++ eth_add_phy(dev, port->plat->phy); ++ } else { ++ int i; ++ for (i = 0; i < IXP4XX_ETH_PHY_MAX_ADDR; i++) ++ if (port->plat->phy_mask & (1U << i)) ++ eth_add_phy(dev, i); ++ } ++ ++} ++ + static int __devinit eth_init_one(struct platform_device *pdev) + { + struct port *port; +@@ -1189,20 +1285,7 @@ static int __devinit eth_init_one(struct + __raw_writel(DEFAULT_CORE_CNTRL, &port->regs->core_control); + udelay(50); + +- port->mii.dev = dev; +- port->mii.mdio_read = mdio_read; +- port->mii.mdio_write = mdio_write; +- port->mii.phy_id = plat->phy; +- port->mii.phy_id_mask = 0x1F; +- port->mii.reg_num_mask = 0x1F; +- +- printk(KERN_INFO "%s: MII PHY %i on %s\n", dev->name, plat->phy, +- npe_name(port->npe)); +- +- phy_reset(dev, plat->phy); +- port->mii_bmcr = mdio_read(dev, plat->phy, MII_BMCR) & +- ~(BMCR_RESET | BMCR_PDOWN); +- mdio_write(dev, plat->phy, MII_BMCR, port->mii_bmcr | BMCR_PDOWN); ++ eth_init_mii(dev); + + INIT_DELAYED_WORK(&port->mdio_thread, mdio_thread); + return 0; +Index: linux-2.6.24.2/include/asm-arm/arch-ixp4xx/platform.h +=================================================================== +--- linux-2.6.24.2.orig/include/asm-arm/arch-ixp4xx/platform.h ++++ linux-2.6.24.2/include/asm-arm/arch-ixp4xx/platform.h +@@ -106,12 +106,15 @@ struct sys_timer; + #define IXP4XX_ETH_NPEB 0x10 + #define IXP4XX_ETH_NPEC 0x20 + ++#define IXP4XX_ETH_PHY_MAX_ADDR 32 ++ + /* Information about built-in Ethernet MAC interfaces */ + struct eth_plat_info { + u8 phy; /* MII PHY ID, 0 - 31 */ + u8 rxq; /* configurable, currently 0 - 31 only */ + u8 txreadyq; + u8 hwaddr[6]; ++ u32 phy_mask; + }; + + /* Information about built-in HSS (synchronous serial) interfaces */ diff --git a/target/linux/ixp4xx/patches-2.6.24/203-npe_driver_phy_reset_autoneg.patch b/target/linux/ixp4xx/patches-2.6.24/203-npe_driver_phy_reset_autoneg.patch new file mode 100644 index 000000000..6e1056e55 --- /dev/null +++ b/target/linux/ixp4xx/patches-2.6.24/203-npe_driver_phy_reset_autoneg.patch @@ -0,0 +1,44 @@ +Index: linux-2.6.24.2/drivers/net/arm/ixp4xx_eth.c +=================================================================== +--- linux-2.6.24.2.orig/drivers/net/arm/ixp4xx_eth.c ++++ linux-2.6.24.2/drivers/net/arm/ixp4xx_eth.c +@@ -322,8 +322,12 @@ static void phy_reset(struct net_device + struct port *port = netdev_priv(dev); + int phy_id = port->mii[idx].phy_id; + int cycles = 0; ++ u16 bmcr; + +- mdio_write(dev, phy_id, MII_BMCR, port->mii_bmcr[idx] | BMCR_RESET); ++ /* reset the PHY */ ++ bmcr = mdio_read(dev, phy_id, MII_BMCR); ++ bmcr |= BMCR_ANENABLE; ++ mdio_write(dev, phy_id, MII_BMCR, bmcr | BMCR_RESET); + + while (cycles < MAX_MII_RESET_RETRIES) { + if (!(mdio_read(dev, phy_id, MII_BMCR) & BMCR_RESET)) { +@@ -331,13 +335,23 @@ static void phy_reset(struct net_device + printk(KERN_DEBUG "%s: phy_reset() took %i cycles\n", + dev->name, cycles); + #endif +- return; ++ break; + } + udelay(1); + cycles++; + } + +- printk(KERN_ERR "%s: MII reset failed on PHY%2d\n", dev->name, phy_id); ++ if (cycles == MAX_MII_RESET_RETRIES) { ++ printk(KERN_ERR "%s: MII reset failed on PHY%2d\n", dev->name, ++ phy_id); ++ return; ++ } ++ ++ /* restart auto negotiation */ ++ bmcr = mdio_read(dev, phy_id, MII_BMCR); ++ bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART); ++ mdio_write(dev, phy_id, MII_BMCR, bmcr); ++ + } + + static void eth_set_duplex(struct port *port, int full_duplex) -- 2.20.1