3e3d482c98
Drop the own PHY polling function and switch to using the kernel PHY state machine. This change allows driver to work correctly with devices that do not support PHY behaviour but whose driver could emulate autonegotiation completion (e.g. MV88E6060 and IP17xx switches). NB: earlier this driver rely on flaws in PHY core code and could use PHY device without really starting it. But now (at least in kernel 4.9) this trick no more work and network interface could stuck in not-running state. Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
111 lines
2.5 KiB
Diff
111 lines
2.5 KiB
Diff
--- 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);
|
|
@@ -326,8 +371,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;
|
|
}
|
|
@@ -870,7 +917,8 @@ static int ar231x_open(struct net_device
|
|
|
|
sp->eth_regs->mac_control |= MAC_CONTROL_RE;
|
|
|
|
- phy_start(sp->phy_dev);
|
|
+ if (sp->phy_dev)
|
|
+ phy_start(sp->phy_dev);
|
|
|
|
return 0;
|
|
}
|
|
@@ -951,7 +999,8 @@ static int ar231x_close(struct net_devic
|
|
|
|
#endif
|
|
|
|
- phy_stop(sp->phy_dev);
|
|
+ if (sp->phy_dev)
|
|
+ phy_stop(sp->phy_dev);
|
|
|
|
return 0;
|
|
}
|
|
@@ -995,6 +1044,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:
|