--- a/drivers/net/ethernet/atheros/ar231x/ar231x.c +++ b/drivers/net/ethernet/atheros/ar231x/ar231x.c @@ -135,6 +135,7 @@ static int ar231x_mdiobus_write(struct m static int ar231x_mdiobus_reset(struct mii_bus *bus); static int ar231x_mdiobus_probe(struct net_device *dev); static void ar231x_adjust_link(struct net_device *dev); +static bool no_phy; #ifndef ERR #define ERR(fmt, args...) printk("%s: " fmt, __func__, ##args) @@ -167,6 +168,32 @@ static const struct net_device_ops ar231 #endif }; +static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id) +{ + int phy_reg; + + /** + * Grab the bits from PHYIR1, and put them + * in the upper half. + */ + phy_reg = mdiobus_read(bus, addr, MII_PHYSID1); + + if (phy_reg < 0) + return -EIO; + + *phy_id = (phy_reg & 0xffff) << 16; + + /* Grab the bits from PHYIR2, and put them in the lower half */ + phy_reg = mdiobus_read(bus, addr, MII_PHYSID2); + + if (phy_reg < 0) + return -EIO; + + *phy_id |= (phy_reg & 0xffff); + + return 0; +} + static int ar231x_probe(struct platform_device *pdev) { struct net_device *dev; @@ -273,6 +300,24 @@ static int ar231x_probe(struct platform_ mdiobus_register(sp->mii_bus); + /** + * Workaround for Micrel switch, which is only available on + * one PHY and cannot be configured through MDIO. + */ + if (!no_phy) { + u32 phy_id = 0; + + get_phy_id(sp->mii_bus, 1, &phy_id); + if (phy_id == 0x00221450) + no_phy = true; + } + if (no_phy) { + sp->link = 1; + netif_carrier_on(dev); + return 0; + } + no_phy = true; + if (ar231x_mdiobus_probe(dev) != 0) { printk(KERN_ERR "%s: mdiobus_probe failed\n", dev->name); rx_tasklet_cleanup(dev); @@ -329,8 +374,10 @@ static int ar231x_remove(struct platform rx_tasklet_cleanup(dev); ar231x_init_cleanup(dev); unregister_netdev(dev); - mdiobus_unregister(sp->mii_bus); - mdiobus_free(sp->mii_bus); + if (sp->mii_bus) { + mdiobus_unregister(sp->mii_bus); + mdiobus_free(sp->mii_bus); + } kfree(dev); return 0; } @@ -1079,6 +1126,9 @@ static int ar231x_ioctl(struct net_devic { struct ar231x_private *sp = netdev_priv(dev); + if (!sp->phy_dev) + return -ENODEV; + switch (cmd) { case SIOCGMIIPHY: case SIOCGMIIREG: