diff options
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/davicom.c | 6 | ||||
-rw-r--r-- | drivers/net/phy/mdio_bus.c | 14 | ||||
-rw-r--r-- | drivers/net/phy/micrel.c | 44 | ||||
-rw-r--r-- | drivers/net/phy/smsc.c | 69 |
4 files changed, 107 insertions, 26 deletions
diff --git a/drivers/net/phy/davicom.c b/drivers/net/phy/davicom.c index 81c7bc010dd8..383e8338ad86 100644 --- a/drivers/net/phy/davicom.c +++ b/drivers/net/phy/davicom.c @@ -150,18 +150,24 @@ static struct phy_driver dm91xx_driver[] = { .name = "Davicom DM9161E", .phy_id_mask = 0x0ffffff0, .features = PHY_BASIC_FEATURES, + .flags = PHY_HAS_INTERRUPT, .config_init = dm9161_config_init, .config_aneg = dm9161_config_aneg, .read_status = genphy_read_status, + .ack_interrupt = dm9161_ack_interrupt, + .config_intr = dm9161_config_intr, .driver = { .owner = THIS_MODULE,}, }, { .phy_id = 0x0181b8a0, .name = "Davicom DM9161A", .phy_id_mask = 0x0ffffff0, .features = PHY_BASIC_FEATURES, + .flags = PHY_HAS_INTERRUPT, .config_init = dm9161_config_init, .config_aneg = dm9161_config_aneg, .read_status = genphy_read_status, + .ack_interrupt = dm9161_ack_interrupt, + .config_intr = dm9161_config_intr, .driver = { .owner = THIS_MODULE,}, }, { .phy_id = 0x00181b80, diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index c1ef3000ea60..044b5326459f 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -431,10 +431,24 @@ static struct dev_pm_ops mdio_bus_pm_ops = { #endif /* CONFIG_PM */ +static ssize_t +phy_id_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct phy_device *phydev = to_phy_device(dev); + + return sprintf(buf, "0x%.8lx\n", (unsigned long)phydev->phy_id); +} + +static struct device_attribute mdio_dev_attrs[] = { + __ATTR_RO(phy_id), + __ATTR_NULL +}; + struct bus_type mdio_bus_type = { .name = "mdio_bus", .match = mdio_bus_match, .pm = MDIO_BUS_PM_OPS, + .dev_attrs = mdio_dev_attrs, }; EXPORT_SYMBOL(mdio_bus_type); diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 2165d5fdb8c0..b983596abcbb 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -127,6 +127,39 @@ static int ks8051_config_init(struct phy_device *phydev) return 0; } +#define KSZ8873MLL_GLOBAL_CONTROL_4 0x06 +#define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX (1 << 6) +#define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED (1 << 4) +int ksz8873mll_read_status(struct phy_device *phydev) +{ + int regval; + + /* dummy read */ + regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); + + regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); + + if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX) + phydev->duplex = DUPLEX_HALF; + else + phydev->duplex = DUPLEX_FULL; + + if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_SPEED) + phydev->speed = SPEED_10; + else + phydev->speed = SPEED_100; + + phydev->link = 1; + phydev->pause = phydev->asym_pause = 0; + + return 0; +} + +static int ksz8873mll_config_aneg(struct phy_device *phydev) +{ + return 0; +} + static struct phy_driver ksphy_driver[] = { { .phy_id = PHY_ID_KS8737, @@ -204,6 +237,16 @@ static struct phy_driver ksphy_driver[] = { .ack_interrupt = kszphy_ack_interrupt, .config_intr = ksz9021_config_intr, .driver = { .owner = THIS_MODULE, }, +}, { + .phy_id = PHY_ID_KSZ8873MLL, + .phy_id_mask = 0x00fffff0, + .name = "Micrel KSZ8873MLL Switch", + .features = (SUPPORTED_Pause | SUPPORTED_Asym_Pause), + .flags = PHY_HAS_MAGICANEG, + .config_init = kszphy_config_init, + .config_aneg = ksz8873mll_config_aneg, + .read_status = ksz8873mll_read_status, + .driver = { .owner = THIS_MODULE, }, } }; static int __init ksphy_init(void) @@ -232,6 +275,7 @@ static struct mdio_device_id __maybe_unused micrel_tbl[] = { { PHY_ID_KSZ8021, 0x00ffffff }, { PHY_ID_KSZ8041, 0x00fffff0 }, { PHY_ID_KSZ8051, 0x00fffff0 }, + { PHY_ID_KSZ8873MLL, 0x00fffff0 }, { } }; diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 88e3991464e7..16dceed29d8c 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -56,35 +56,52 @@ static int smsc_phy_config_init(struct phy_device *phydev) return smsc_phy_ack_interrupt (phydev); } -static int lan87xx_config_init(struct phy_device *phydev) +static int lan911x_config_init(struct phy_device *phydev) { - /* - * Make sure the EDPWRDOWN bit is NOT set. Setting this bit on - * LAN8710/LAN8720 PHY causes the PHY to misbehave, likely due - * to a bug on the chip. - * - * When the system is powered on with the network cable being - * disconnected all the way until after ifconfig ethX up is - * issued for the LAN port with this PHY, connecting the cable - * afterwards does not cause LINK change detection, while the - * expected behavior is the Link UP being detected. - */ - int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); - if (rc < 0) - return rc; - - rc &= ~MII_LAN83C185_EDPWRDOWN; - - rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, rc); - if (rc < 0) - return rc; - return smsc_phy_ack_interrupt(phydev); } -static int lan911x_config_init(struct phy_device *phydev) +/* + * The LAN8710/LAN8720 requires a minimum of 2 link pulses within 64ms of each + * other in order to set the ENERGYON bit and exit EDPD mode. If a link partner + * does send the pulses within this interval, the PHY will remained powered + * down. + * + * This workaround will manually toggle the PHY on/off upon calls to read_status + * in order to generate link test pulses if the link is down. If a link partner + * is present, it will respond to the pulses, which will cause the ENERGYON bit + * to be set and will cause the EDPD mode to be exited. + */ +static int lan87xx_read_status(struct phy_device *phydev) { - return smsc_phy_ack_interrupt(phydev); + int err = genphy_read_status(phydev); + + if (!phydev->link) { + /* Disable EDPD to wake up PHY */ + int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); + if (rc < 0) + return rc; + + rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, + rc & ~MII_LAN83C185_EDPWRDOWN); + if (rc < 0) + return rc; + + /* Sleep 64 ms to allow ~5 link test pulses to be sent */ + msleep(64); + + /* Re-enable EDPD */ + rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); + if (rc < 0) + return rc; + + rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, + rc | MII_LAN83C185_EDPWRDOWN); + if (rc < 0) + return rc; + } + + return err; } static struct phy_driver smsc_phy_driver[] = { @@ -187,8 +204,8 @@ static struct phy_driver smsc_phy_driver[] = { /* basic functions */ .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, - .config_init = lan87xx_config_init, + .read_status = lan87xx_read_status, + .config_intr = smsc_phy_config_intr, /* IRQ related */ .ack_interrupt = smsc_phy_ack_interrupt, |