diff options
Diffstat (limited to 'drivers/net/phy/smsc.c')
-rw-r--r-- | drivers/net/phy/smsc.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c new file mode 100644 index 00000000..fc3e7e96 --- /dev/null +++ b/drivers/net/phy/smsc.c @@ -0,0 +1,247 @@ +/* + * drivers/net/phy/smsc.c + * + * Driver for SMSC PHYs + * + * Author: Herbert Valerio Riedel + * + * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org> + * + * 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. + * + * Support added for SMSC LAN8187 and LAN8700 by steve.glendinning@smsc.com + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mii.h> +#include <linux/ethtool.h> +#include <linux/phy.h> +#include <linux/netdevice.h> +#include <linux/smscphy.h> + +static int smsc_phy_config_intr(struct phy_device *phydev) +{ + int rc = phy_write (phydev, MII_LAN83C185_IM, + ((PHY_INTERRUPT_ENABLED == phydev->interrupts) + ? MII_LAN83C185_ISF_INT_PHYLIB_EVENTS + : 0)); + + return rc < 0 ? rc : 0; +} + +static int smsc_phy_ack_interrupt(struct phy_device *phydev) +{ + int rc = phy_read (phydev, MII_LAN83C185_ISF); + + return rc < 0 ? rc : 0; +} + +static int smsc_phy_config_init(struct phy_device *phydev) +{ + int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); + if (rc < 0) + return rc; + + /* Enable energy detect mode for this SMSC Transceivers */ + rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, + rc | MII_LAN83C185_EDPWRDOWN); + if (rc < 0) + return rc; + + return smsc_phy_ack_interrupt (phydev); +} + +static int lan911x_config_init(struct phy_device *phydev) +{ + return smsc_phy_ack_interrupt(phydev); +} + +static struct phy_driver lan83c185_driver = { + .phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */ + .phy_id_mask = 0xfffffff0, + .name = "SMSC LAN83C185", + + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + + /* basic functions */ + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .config_init = smsc_phy_config_init, + + /* IRQ related */ + .ack_interrupt = smsc_phy_ack_interrupt, + .config_intr = smsc_phy_config_intr, + + .suspend = genphy_suspend, + .resume = genphy_resume, + + .driver = { .owner = THIS_MODULE, } +}; + +static struct phy_driver lan8187_driver = { + .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ + .phy_id_mask = 0xfffffff0, + .name = "SMSC LAN8187", + + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + + /* basic functions */ + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .config_init = smsc_phy_config_init, + + /* IRQ related */ + .ack_interrupt = smsc_phy_ack_interrupt, + .config_intr = smsc_phy_config_intr, + + .suspend = genphy_suspend, + .resume = genphy_resume, + + .driver = { .owner = THIS_MODULE, } +}; + +static struct phy_driver lan8700_driver = { + .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ + .phy_id_mask = 0xfffffff0, + .name = "SMSC LAN8700", + + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + + /* basic functions */ + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .config_init = smsc_phy_config_init, + + /* IRQ related */ + .ack_interrupt = smsc_phy_ack_interrupt, + .config_intr = smsc_phy_config_intr, + + .suspend = genphy_suspend, + .resume = genphy_resume, + + .driver = { .owner = THIS_MODULE, } +}; + +static struct phy_driver lan911x_int_driver = { + .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ + .phy_id_mask = 0xfffffff0, + .name = "SMSC LAN911x Internal PHY", + + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + + /* basic functions */ + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .config_init = lan911x_config_init, + + /* IRQ related */ + .ack_interrupt = smsc_phy_ack_interrupt, + .config_intr = smsc_phy_config_intr, + + .suspend = genphy_suspend, + .resume = genphy_resume, + + .driver = { .owner = THIS_MODULE, } +}; + +static struct phy_driver lan8710_driver = { + .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ + .phy_id_mask = 0xfffffff0, + .name = "SMSC LAN8710/LAN8720", + + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + + /* basic functions */ + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .config_init = smsc_phy_config_init, + + /* IRQ related */ + .ack_interrupt = smsc_phy_ack_interrupt, + .config_intr = smsc_phy_config_intr, + + .suspend = genphy_suspend, + .resume = genphy_resume, + + .driver = { .owner = THIS_MODULE, } +}; + +static int __init smsc_init(void) +{ + int ret; + + ret = phy_driver_register (&lan83c185_driver); + if (ret) + goto err1; + + ret = phy_driver_register (&lan8187_driver); + if (ret) + goto err2; + + ret = phy_driver_register (&lan8700_driver); + if (ret) + goto err3; + + ret = phy_driver_register (&lan911x_int_driver); + if (ret) + goto err4; + + ret = phy_driver_register (&lan8710_driver); + if (ret) + goto err5; + + return 0; + +err5: + phy_driver_unregister (&lan911x_int_driver); +err4: + phy_driver_unregister (&lan8700_driver); +err3: + phy_driver_unregister (&lan8187_driver); +err2: + phy_driver_unregister (&lan83c185_driver); +err1: + return ret; +} + +static void __exit smsc_exit(void) +{ + phy_driver_unregister (&lan8710_driver); + phy_driver_unregister (&lan911x_int_driver); + phy_driver_unregister (&lan8700_driver); + phy_driver_unregister (&lan8187_driver); + phy_driver_unregister (&lan83c185_driver); +} + +MODULE_DESCRIPTION("SMSC PHY driver"); +MODULE_AUTHOR("Herbert Valerio Riedel"); +MODULE_LICENSE("GPL"); + +module_init(smsc_init); +module_exit(smsc_exit); + +static struct mdio_device_id __maybe_unused smsc_tbl[] = { + { 0x0007c0a0, 0xfffffff0 }, + { 0x0007c0b0, 0xfffffff0 }, + { 0x0007c0c0, 0xfffffff0 }, + { 0x0007c0d0, 0xfffffff0 }, + { 0x0007c0f0, 0xfffffff0 }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, smsc_tbl); |