diff options
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/Kconfig | 5 | ||||
-rw-r--r-- | drivers/net/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/phy/at803x.c | 176 | ||||
-rw-r--r-- | drivers/net/phy/davicom.c | 6 | ||||
-rw-r--r-- | drivers/net/phy/dp83640.c | 78 | ||||
-rw-r--r-- | drivers/net/phy/mdio-gpio.c | 25 | ||||
-rw-r--r-- | drivers/net/phy/mdio-mux-gpio.c | 6 | ||||
-rw-r--r-- | drivers/net/phy/mdio-mux-mmioreg.c | 6 | ||||
-rw-r--r-- | drivers/net/phy/mdio-octeon.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 | 95 | ||||
-rw-r--r-- | drivers/net/phy/spi_ks8995.c | 6 |
13 files changed, 417 insertions, 51 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 983bbf4d5ef..961f0b29391 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -15,6 +15,11 @@ if PHYLIB comment "MII PHY device drivers" +config AT803X_PHY + tristate "Drivers for Atheros AT803X PHYs" + ---help--- + Currently supports the AT8030 and AT8035 model + config AMD_PHY tristate "Drivers for the AMD PHYs" ---help--- diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 426674debae..9645e389a58 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_STE10XP) += ste10Xp.o obj-$(CONFIG_MICREL_PHY) += micrel.o obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o +obj-$(CONFIG_AT803X_PHY) += at803x.o obj-$(CONFIG_AMD_PHY) += amd.o obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c new file mode 100644 index 00000000000..45cbc10de01 --- /dev/null +++ b/drivers/net/phy/at803x.c @@ -0,0 +1,176 @@ +/* + * drivers/net/phy/at803x.c + * + * Driver for Atheros 803x PHY + * + * Author: Matus Ujhelyi <ujhelyi.m@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/phy.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> + +#define AT803X_INTR_ENABLE 0x12 +#define AT803X_INTR_STATUS 0x13 +#define AT803X_WOL_ENABLE 0x01 +#define AT803X_DEVICE_ADDR 0x03 +#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C +#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B +#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A +#define AT803X_MMD_ACCESS_CONTROL 0x0D +#define AT803X_MMD_ACCESS_CONTROL_DATA 0x0E +#define AT803X_FUNC_DATA 0x4003 + +MODULE_DESCRIPTION("Atheros 803x PHY driver"); +MODULE_AUTHOR("Matus Ujhelyi"); +MODULE_LICENSE("GPL"); + +static void at803x_set_wol_mac_addr(struct phy_device *phydev) +{ + struct net_device *ndev = phydev->attached_dev; + const u8 *mac; + unsigned int i, offsets[] = { + AT803X_LOC_MAC_ADDR_32_47_OFFSET, + AT803X_LOC_MAC_ADDR_16_31_OFFSET, + AT803X_LOC_MAC_ADDR_0_15_OFFSET, + }; + + if (!ndev) + return; + + mac = (const u8 *) ndev->dev_addr; + + if (!is_valid_ether_addr(mac)) + return; + + for (i = 0; i < 3; i++) { + phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, + AT803X_DEVICE_ADDR); + phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, + offsets[i]); + phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, + AT803X_FUNC_DATA); + phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, + mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); + } +} + +static int at803x_config_init(struct phy_device *phydev) +{ + int val; + u32 features; + int status; + + features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI | + SUPPORTED_FIBRE | SUPPORTED_BNC; + + val = phy_read(phydev, MII_BMSR); + if (val < 0) + return val; + + if (val & BMSR_ANEGCAPABLE) + features |= SUPPORTED_Autoneg; + if (val & BMSR_100FULL) + features |= SUPPORTED_100baseT_Full; + if (val & BMSR_100HALF) + features |= SUPPORTED_100baseT_Half; + if (val & BMSR_10FULL) + features |= SUPPORTED_10baseT_Full; + if (val & BMSR_10HALF) + features |= SUPPORTED_10baseT_Half; + + if (val & BMSR_ESTATEN) { + val = phy_read(phydev, MII_ESTATUS); + if (val < 0) + return val; + + if (val & ESTATUS_1000_TFULL) + features |= SUPPORTED_1000baseT_Full; + if (val & ESTATUS_1000_THALF) + features |= SUPPORTED_1000baseT_Half; + } + + phydev->supported = features; + phydev->advertising = features; + + /* enable WOL */ + at803x_set_wol_mac_addr(phydev); + status = phy_write(phydev, AT803X_INTR_ENABLE, AT803X_WOL_ENABLE); + status = phy_read(phydev, AT803X_INTR_STATUS); + + return 0; +} + +/* ATHEROS 8035 */ +static struct phy_driver at8035_driver = { + .phy_id = 0x004dd072, + .name = "Atheros 8035 ethernet", + .phy_id_mask = 0xffffffef, + .config_init = at803x_config_init, + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .driver = { + .owner = THIS_MODULE, + }, +}; + +/* ATHEROS 8030 */ +static struct phy_driver at8030_driver = { + .phy_id = 0x004dd076, + .name = "Atheros 8030 ethernet", + .phy_id_mask = 0xffffffef, + .config_init = at803x_config_init, + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .driver = { + .owner = THIS_MODULE, + }, +}; + +static int __init atheros_init(void) +{ + int ret; + + ret = phy_driver_register(&at8035_driver); + if (ret) + goto err1; + + ret = phy_driver_register(&at8030_driver); + if (ret) + goto err2; + + return 0; + +err2: + phy_driver_unregister(&at8035_driver); +err1: + return ret; +} + +static void __exit atheros_exit(void) +{ + phy_driver_unregister(&at8035_driver); + phy_driver_unregister(&at8030_driver); +} + +module_init(atheros_init); +module_exit(atheros_exit); + +static struct mdio_device_id __maybe_unused atheros_tbl[] = { + { 0x004dd076, 0xffffffef }, + { 0x004dd072, 0xffffffef }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, atheros_tbl); diff --git a/drivers/net/phy/davicom.c b/drivers/net/phy/davicom.c index 81c7bc010dd..383e8338ad8 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/dp83640.c b/drivers/net/phy/dp83640.c index 24e05c43bff..7490b6c866e 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -48,6 +48,21 @@ #define CAL_TRIGGER 7 #define PER_TRIGGER 6 +#define MII_DP83640_MICR 0x11 +#define MII_DP83640_MISR 0x12 + +#define MII_DP83640_MICR_OE 0x1 +#define MII_DP83640_MICR_IE 0x2 + +#define MII_DP83640_MISR_RHF_INT_EN 0x01 +#define MII_DP83640_MISR_FHF_INT_EN 0x02 +#define MII_DP83640_MISR_ANC_INT_EN 0x04 +#define MII_DP83640_MISR_DUP_INT_EN 0x08 +#define MII_DP83640_MISR_SPD_INT_EN 0x10 +#define MII_DP83640_MISR_LINK_INT_EN 0x20 +#define MII_DP83640_MISR_ED_INT_EN 0x40 +#define MII_DP83640_MISR_LQ_INT_EN 0x80 + /* phyter seems to miss the mark by 16 ns */ #define ADJTIME_FIX 16 @@ -1043,6 +1058,65 @@ static void dp83640_remove(struct phy_device *phydev) kfree(dp83640); } +static int dp83640_ack_interrupt(struct phy_device *phydev) +{ + int err = phy_read(phydev, MII_DP83640_MISR); + + if (err < 0) + return err; + + return 0; +} + +static int dp83640_config_intr(struct phy_device *phydev) +{ + int micr; + int misr; + int err; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { + misr = phy_read(phydev, MII_DP83640_MISR); + if (misr < 0) + return misr; + misr |= + (MII_DP83640_MISR_ANC_INT_EN | + MII_DP83640_MISR_DUP_INT_EN | + MII_DP83640_MISR_SPD_INT_EN | + MII_DP83640_MISR_LINK_INT_EN); + err = phy_write(phydev, MII_DP83640_MISR, misr); + if (err < 0) + return err; + + micr = phy_read(phydev, MII_DP83640_MICR); + if (micr < 0) + return micr; + micr |= + (MII_DP83640_MICR_OE | + MII_DP83640_MICR_IE); + return phy_write(phydev, MII_DP83640_MICR, micr); + } else { + micr = phy_read(phydev, MII_DP83640_MICR); + if (micr < 0) + return micr; + micr &= + ~(MII_DP83640_MICR_OE | + MII_DP83640_MICR_IE); + err = phy_write(phydev, MII_DP83640_MICR, micr); + if (err < 0) + return err; + + misr = phy_read(phydev, MII_DP83640_MISR); + if (misr < 0) + return misr; + misr &= + ~(MII_DP83640_MISR_ANC_INT_EN | + MII_DP83640_MISR_DUP_INT_EN | + MII_DP83640_MISR_SPD_INT_EN | + MII_DP83640_MISR_LINK_INT_EN); + return phy_write(phydev, MII_DP83640_MISR, misr); + } +} + static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr) { struct dp83640_private *dp83640 = phydev->priv; @@ -1253,11 +1327,13 @@ static struct phy_driver dp83640_driver = { .phy_id_mask = 0xfffffff0, .name = "NatSemi DP83640", .features = PHY_BASIC_FEATURES, - .flags = 0, + .flags = PHY_HAS_INTERRUPT, .probe = dp83640_probe, .remove = dp83640_remove, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, + .ack_interrupt = dp83640_ack_interrupt, + .config_intr = dp83640_config_intr, .ts_info = dp83640_ts_info, .hwtstamp = dp83640_hwtstamp, .rxtstamp = dp83640_rxtstamp, diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 899274f2f9b..27274986ab5 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -103,9 +103,9 @@ static struct mdiobb_ops mdio_gpio_ops = { .get_mdio_data = mdio_get, }; -static struct mii_bus * __devinit mdio_gpio_bus_init(struct device *dev, - struct mdio_gpio_platform_data *pdata, - int bus_id) +static struct mii_bus *mdio_gpio_bus_init(struct device *dev, + struct mdio_gpio_platform_data *pdata, + int bus_id) { struct mii_bus *new_bus; struct mdio_gpio_info *bitbang; @@ -173,7 +173,7 @@ static void mdio_gpio_bus_deinit(struct device *dev) kfree(bitbang); } -static void __devexit mdio_gpio_bus_destroy(struct device *dev) +static void mdio_gpio_bus_destroy(struct device *dev) { struct mii_bus *bus = dev_get_drvdata(dev); @@ -181,21 +181,24 @@ static void __devexit mdio_gpio_bus_destroy(struct device *dev) mdio_gpio_bus_deinit(dev); } -static int __devinit mdio_gpio_probe(struct platform_device *pdev) +static int mdio_gpio_probe(struct platform_device *pdev) { struct mdio_gpio_platform_data *pdata; struct mii_bus *new_bus; - int ret; + int ret, bus_id; - if (pdev->dev.of_node) + if (pdev->dev.of_node) { pdata = mdio_gpio_of_get_data(pdev); - else + bus_id = of_alias_get_id(pdev->dev.of_node, "mdio-gpio"); + } else { pdata = pdev->dev.platform_data; + bus_id = pdev->id; + } if (!pdata) return -ENODEV; - new_bus = mdio_gpio_bus_init(&pdev->dev, pdata, pdev->id); + new_bus = mdio_gpio_bus_init(&pdev->dev, pdata, bus_id); if (!new_bus) return -ENODEV; @@ -210,7 +213,7 @@ static int __devinit mdio_gpio_probe(struct platform_device *pdev) return ret; } -static int __devexit mdio_gpio_remove(struct platform_device *pdev) +static int mdio_gpio_remove(struct platform_device *pdev) { mdio_gpio_bus_destroy(&pdev->dev); @@ -224,7 +227,7 @@ static struct of_device_id mdio_gpio_of_match[] = { static struct platform_driver mdio_gpio_driver = { .probe = mdio_gpio_probe, - .remove = __devexit_p(mdio_gpio_remove), + .remove = mdio_gpio_remove, .driver = { .name = "mdio-gpio", .owner = THIS_MODULE, diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c index eefe49e8713..0c9accb1c14 100644 --- a/drivers/net/phy/mdio-mux-gpio.c +++ b/drivers/net/phy/mdio-mux-gpio.c @@ -49,7 +49,7 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child, return 0; } -static int __devinit mdio_mux_gpio_probe(struct platform_device *pdev) +static int mdio_mux_gpio_probe(struct platform_device *pdev) { enum of_gpio_flags f; struct mdio_mux_gpio_state *s; @@ -104,7 +104,7 @@ err: return r; } -static int __devexit mdio_mux_gpio_remove(struct platform_device *pdev) +static int mdio_mux_gpio_remove(struct platform_device *pdev) { struct mdio_mux_gpio_state *s = pdev->dev.platform_data; mdio_mux_uninit(s->mux_handle); @@ -130,7 +130,7 @@ static struct platform_driver mdio_mux_gpio_driver = { .of_match_table = mdio_mux_gpio_match, }, .probe = mdio_mux_gpio_probe, - .remove = __devexit_p(mdio_mux_gpio_remove), + .remove = mdio_mux_gpio_remove, }; module_platform_driver(mdio_mux_gpio_driver); diff --git a/drivers/net/phy/mdio-mux-mmioreg.c b/drivers/net/phy/mdio-mux-mmioreg.c index 9061ba622ac..9733bd239a8 100644 --- a/drivers/net/phy/mdio-mux-mmioreg.c +++ b/drivers/net/phy/mdio-mux-mmioreg.c @@ -67,7 +67,7 @@ static int mdio_mux_mmioreg_switch_fn(int current_child, int desired_child, return 0; } -static int __devinit mdio_mux_mmioreg_probe(struct platform_device *pdev) +static int mdio_mux_mmioreg_probe(struct platform_device *pdev) { struct device_node *np2, *np = pdev->dev.of_node; struct mdio_mux_mmioreg_state *s; @@ -137,7 +137,7 @@ static int __devinit mdio_mux_mmioreg_probe(struct platform_device *pdev) return 0; } -static int __devexit mdio_mux_mmioreg_remove(struct platform_device *pdev) +static int mdio_mux_mmioreg_remove(struct platform_device *pdev) { struct mdio_mux_mmioreg_state *s = dev_get_platdata(&pdev->dev); @@ -161,7 +161,7 @@ static struct platform_driver mdio_mux_mmioreg_driver = { .of_match_table = mdio_mux_mmioreg_match, }, .probe = mdio_mux_mmioreg_probe, - .remove = __devexit_p(mdio_mux_mmioreg_remove), + .remove = mdio_mux_mmioreg_remove, }; module_platform_driver(mdio_mux_mmioreg_driver); diff --git a/drivers/net/phy/mdio-octeon.c b/drivers/net/phy/mdio-octeon.c index d4015aa663e..09297fe05ae 100644 --- a/drivers/net/phy/mdio-octeon.c +++ b/drivers/net/phy/mdio-octeon.c @@ -96,7 +96,7 @@ static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id, return 0; } -static int __devinit octeon_mdiobus_probe(struct platform_device *pdev) +static int octeon_mdiobus_probe(struct platform_device *pdev) { struct octeon_mdiobus *bus; struct resource *res_mem; @@ -159,7 +159,7 @@ fail: return err; } -static int __devexit octeon_mdiobus_remove(struct platform_device *pdev) +static int octeon_mdiobus_remove(struct platform_device *pdev) { struct octeon_mdiobus *bus; union cvmx_smix_en smi_en; @@ -188,7 +188,7 @@ static struct platform_driver octeon_mdiobus_driver = { .of_match_table = octeon_mdiobus_match, }, .probe = octeon_mdiobus_probe, - .remove = __devexit_p(octeon_mdiobus_remove), + .remove = octeon_mdiobus_remove, }; void octeon_mdiobus_force_mod_depencency(void) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index c1ef3000ea6..044b5326459 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 2165d5fdb8c..b983596abcb 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 88e3991464e..11f34813e23 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -43,7 +43,31 @@ static int smsc_phy_ack_interrupt(struct phy_device *phydev) static int smsc_phy_config_init(struct phy_device *phydev) { - int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); + int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES); + if (rc < 0) + return rc; + + /* If the SMSC PHY is in power down mode, then set it + * in all capable mode before using it. + */ + if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) { + int timeout = 50000; + + /* set "all capable" mode and reset the phy */ + rc |= MII_LAN83C185_MODE_ALL; + phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc); + phy_write(phydev, MII_BMCR, BMCR_RESET); + + /* wait end of reset (max 500 ms) */ + do { + udelay(10); + if (timeout-- == 0) + return -1; + rc = phy_read(phydev, MII_BMCR); + } while (rc & BMCR_RESET); + } + + rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); if (rc < 0) return rc; @@ -56,35 +80,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 +228,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_init = smsc_phy_config_init, /* IRQ related */ .ack_interrupt = smsc_phy_ack_interrupt, diff --git a/drivers/net/phy/spi_ks8995.c b/drivers/net/phy/spi_ks8995.c index 1c3abce78b6..41eb8ffeb53 100644 --- a/drivers/net/phy/spi_ks8995.c +++ b/drivers/net/phy/spi_ks8995.c @@ -264,7 +264,7 @@ static struct bin_attribute ks8995_registers_attr = { /* ------------------------------------------------------------------------ */ -static int __devinit ks8995_probe(struct spi_device *spi) +static int ks8995_probe(struct spi_device *spi) { struct ks8995_switch *ks; struct ks8995_pdata *pdata; @@ -332,7 +332,7 @@ err_drvdata: return err; } -static int __devexit ks8995_remove(struct spi_device *spi) +static int ks8995_remove(struct spi_device *spi) { struct ks8995_data *ks8995; @@ -353,7 +353,7 @@ static struct spi_driver ks8995_driver = { .owner = THIS_MODULE, }, .probe = ks8995_probe, - .remove = __devexit_p(ks8995_remove), + .remove = ks8995_remove, }; static int __init ks8995_init(void) |