Index: linux-2.6.25.1/drivers/net/arm/ixp4xx_eth.c =================================================================== --- linux-2.6.25.1.orig/drivers/net/arm/ixp4xx_eth.c +++ linux-2.6.25.1/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,12 +317,13 @@ 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)) { @@ -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) { @@ -792,9 +850,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; } @@ -947,7 +1008,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; @@ -1107,10 +1169,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); @@ -1120,6 +1180,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; @@ -1192,20 +1288,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.25.1/include/asm-arm/arch-ixp4xx/platform.h =================================================================== --- linux-2.6.25.1.orig/include/asm-arm/arch-ixp4xx/platform.h +++ linux-2.6.25.1/include/asm-arm/arch-ixp4xx/platform.h @@ -95,12 +95,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 */