diff options
Diffstat (limited to 'drivers/net/phy/icplus.c')
| -rw-r--r-- | drivers/net/phy/icplus.c | 180 |
1 files changed, 162 insertions, 18 deletions
diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c index af3f1f2a9f8..97bf58bf493 100644 --- a/drivers/net/phy/icplus.c +++ b/drivers/net/phy/icplus.c @@ -13,7 +13,6 @@ #include <linux/string.h> #include <linux/errno.h> #include <linux/unistd.h> -#include <linux/slab.h> #include <linux/interrupt.h> #include <linux/init.h> #include <linux/delay.h> @@ -31,48 +30,59 @@ #include <asm/irq.h> #include <asm/uaccess.h> -MODULE_DESCRIPTION("ICPlus IP175C PHY driver"); +MODULE_DESCRIPTION("ICPlus IP175C/IP101A/IP101G/IC1001 PHY drivers"); MODULE_AUTHOR("Michael Barkowski"); MODULE_LICENSE("GPL"); +/* IP101A/G - IP1001 */ +#define IP10XX_SPEC_CTRL_STATUS 16 /* Spec. Control Register */ +#define IP1001_RXPHASE_SEL (1<<0) /* Add delay on RX_CLK */ +#define IP1001_TXPHASE_SEL (1<<1) /* Add delay on TX_CLK */ +#define IP1001_SPEC_CTRL_STATUS_2 20 /* IP1001 Spec. Control Reg 2 */ +#define IP1001_APS_ON 11 /* IP1001 APS Mode bit */ +#define IP101A_G_APS_ON 2 /* IP101A/G APS Mode bit */ +#define IP101A_G_IRQ_CONF_STATUS 0x11 /* Conf Info IRQ & Status Reg */ +#define IP101A_G_IRQ_PIN_USED (1<<15) /* INTR pin used */ +#define IP101A_G_IRQ_DEFAULT IP101A_G_IRQ_PIN_USED + static int ip175c_config_init(struct phy_device *phydev) { int err, i; - static int full_reset_performed = 0; + static int full_reset_performed; if (full_reset_performed == 0) { /* master reset */ - err = phydev->bus->write(phydev->bus, 30, 0, 0x175c); + err = mdiobus_write(phydev->bus, 30, 0, 0x175c); if (err < 0) return err; /* ensure no bus delays overlap reset period */ - err = phydev->bus->read(phydev->bus, 30, 0); + err = mdiobus_read(phydev->bus, 30, 0); /* data sheet specifies reset period is 2 msec */ mdelay(2); /* enable IP175C mode */ - err = phydev->bus->write(phydev->bus, 29, 31, 0x175c); + err = mdiobus_write(phydev->bus, 29, 31, 0x175c); if (err < 0) return err; /* Set MII0 speed and duplex (in PHY mode) */ - err = phydev->bus->write(phydev->bus, 29, 22, 0x420); + err = mdiobus_write(phydev->bus, 29, 22, 0x420); if (err < 0) return err; /* reset switch ports */ for (i = 0; i < 5; i++) { - err = phydev->bus->write(phydev->bus, i, - MII_BMCR, BMCR_RESET); + err = mdiobus_write(phydev->bus, i, + MII_BMCR, BMCR_RESET); if (err < 0) return err; } for (i = 0; i < 5; i++) - err = phydev->bus->read(phydev->bus, i, MII_BMCR); + err = mdiobus_read(phydev->bus, i, MII_BMCR); mdelay(2); @@ -90,6 +100,91 @@ static int ip175c_config_init(struct phy_device *phydev) return 0; } +static int ip1xx_reset(struct phy_device *phydev) +{ + int bmcr; + + /* Software Reset PHY */ + bmcr = phy_read(phydev, MII_BMCR); + if (bmcr < 0) + return bmcr; + bmcr |= BMCR_RESET; + bmcr = phy_write(phydev, MII_BMCR, bmcr); + if (bmcr < 0) + return bmcr; + + do { + bmcr = phy_read(phydev, MII_BMCR); + if (bmcr < 0) + return bmcr; + } while (bmcr & BMCR_RESET); + + return 0; +} + +static int ip1001_config_init(struct phy_device *phydev) +{ + int c; + + c = ip1xx_reset(phydev); + if (c < 0) + return c; + + /* Enable Auto Power Saving mode */ + c = phy_read(phydev, IP1001_SPEC_CTRL_STATUS_2); + if (c < 0) + return c; + c |= IP1001_APS_ON; + c = phy_write(phydev, IP1001_SPEC_CTRL_STATUS_2, c); + if (c < 0) + return c; + + if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) { + + c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS); + if (c < 0) + return c; + + c &= ~(IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) + c |= (IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); + else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) + c |= IP1001_RXPHASE_SEL; + else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) + c |= IP1001_TXPHASE_SEL; + + c = phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c); + if (c < 0) + return c; + } + + return 0; +} + +static int ip101a_g_config_init(struct phy_device *phydev) +{ + int c; + + c = ip1xx_reset(phydev); + if (c < 0) + return c; + + /* INTR pin used: speed/link/duplex will cause an interrupt */ + c = phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, IP101A_G_IRQ_DEFAULT); + if (c < 0) + return c; + + /* Enable Auto Power Saving mode */ + c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS); + c |= IP101A_G_APS_ON; + + return phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c); +} + static int ip175c_read_status(struct phy_device *phydev) { if (phydev->addr == 4) /* WAN port */ @@ -109,7 +204,17 @@ static int ip175c_config_aneg(struct phy_device *phydev) return 0; } -static struct phy_driver ip175c_driver = { +static int ip101a_g_ack_interrupt(struct phy_device *phydev) +{ + int err = phy_read(phydev, IP101A_G_IRQ_CONF_STATUS); + if (err < 0) + return err; + + return 0; +} + +static struct phy_driver icplus_driver[] = { +{ .phy_id = 0x02430d80, .name = "ICPlus IP175C", .phy_id_mask = 0x0ffffff0, @@ -117,18 +222,57 @@ static struct phy_driver ip175c_driver = { .config_init = &ip175c_config_init, .config_aneg = &ip175c_config_aneg, .read_status = &ip175c_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, -}; +}, { + .phy_id = 0x02430d90, + .name = "ICPlus IP1001", + .phy_id_mask = 0x0ffffff0, + .features = PHY_GBIT_FEATURES | SUPPORTED_Pause | + SUPPORTED_Asym_Pause, + .config_init = &ip1001_config_init, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, + .driver = { .owner = THIS_MODULE,}, +}, { + .phy_id = 0x02430c54, + .name = "ICPlus IP101A/G", + .phy_id_mask = 0x0ffffff0, + .features = PHY_BASIC_FEATURES | SUPPORTED_Pause | + SUPPORTED_Asym_Pause, + .flags = PHY_HAS_INTERRUPT, + .ack_interrupt = ip101a_g_ack_interrupt, + .config_init = &ip101a_g_config_init, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, + .driver = { .owner = THIS_MODULE,}, +} }; -static int __init ip175c_init(void) +static int __init icplus_init(void) { - return phy_driver_register(&ip175c_driver); + return phy_drivers_register(icplus_driver, + ARRAY_SIZE(icplus_driver)); } -static void __exit ip175c_exit(void) +static void __exit icplus_exit(void) { - phy_driver_unregister(&ip175c_driver); + phy_drivers_unregister(icplus_driver, + ARRAY_SIZE(icplus_driver)); } -module_init(ip175c_init); -module_exit(ip175c_exit); +module_init(icplus_init); +module_exit(icplus_exit); + +static struct mdio_device_id __maybe_unused icplus_tbl[] = { + { 0x02430d80, 0x0ffffff0 }, + { 0x02430d90, 0x0ffffff0 }, + { 0x02430c54, 0x0ffffff0 }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, icplus_tbl); |
