247 lines
6.3 KiB
Diff
247 lines
6.3 KiB
Diff
--- a/drivers/net/arm/ixp4xx_eth.c
|
|
+++ b/drivers/net/arm/ixp4xx_eth.c
|
|
@@ -165,14 +165,15 @@
|
|
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 @@
|
|
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 @@
|
|
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 @@
|
|
&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 @@
|
|
}
|
|
}
|
|
}
|
|
+#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 @@
|
|
|
|
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 @@
|
|
}
|
|
}
|
|
|
|
- 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 @@
|
|
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 @@
|
|
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 @@
|
|
__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;
|
|
--- a/include/asm-arm/arch-ixp4xx/platform.h
|
|
+++ b/include/asm-arm/arch-ixp4xx/platform.h
|
|
@@ -95,12 +95,15 @@
|
|
#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 */
|