/* * Driver for ICPlus PHYs * * Copyright (c) 2007 Freescale Semiconductor, Inc. * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_DESCRIPTION("ICPlus IP175C/IC1001 PHY drivers"); MODULE_AUTHOR("Michael Barkowski"); MODULE_LICENSE("GPL"); static int ip175c_config_init(struct phy_device *phydev) { int err, i; static int full_reset_performed = 0; if (full_reset_performed == 0) { /* master reset */ err = phydev->bus->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); /* data sheet specifies reset period is 2 msec */ mdelay(2); /* enable IP175C mode */ err = phydev->bus->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); 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); if (err < 0) return err; } for (i = 0; i < 5; i++) err = phydev->bus->read(phydev->bus, i, MII_BMCR); mdelay(2); full_reset_performed = 1; } if (phydev->addr != 4) { phydev->state = PHY_RUNNING; phydev->speed = SPEED_100; phydev->duplex = DUPLEX_FULL; phydev->link = 1; netif_carrier_on(phydev->attached_dev); } return 0; } static int ip1001_config_init(struct phy_device *phydev) { int err, value; /* Software Reset PHY */ value = phy_read(phydev, MII_BMCR); value |= BMCR_RESET; err = phy_write(phydev, MII_BMCR, value); if (err < 0) return err; do { value = phy_read(phydev, MII_BMCR); } while (value & BMCR_RESET); /* Additional delay (2ns) used to adjust RX clock phase * at GMII/ RGMII interface */ value = phy_read(phydev, 16); value |= 0x3; return phy_write(phydev, 16, value); } static int ip175c_read_status(struct phy_device *phydev) { if (phydev->addr == 4) /* WAN port */ genphy_read_status(phydev); else /* Don't need to read status for switch ports */ phydev->irq = PHY_IGNORE_INTERRUPT; return 0; } static int ip175c_config_aneg(struct phy_device *phydev) { if (phydev->addr == 4) /* WAN port */ genphy_config_aneg(phydev); return 0; } static struct phy_driver ip175c_driver = { .phy_id = 0x02430d80, .name = "ICPlus IP175C", .phy_id_mask = 0x0ffffff0, .features = PHY_BASIC_FEATURES, .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,}, }; static struct phy_driver ip1001_driver = { .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,}, }; static int __init icplus_init(void) { int ret = 0; ret = phy_driver_register(&ip1001_driver); if (ret < 0) return -ENODEV; return phy_driver_register(&ip175c_driver); } static void __exit icplus_exit(void) { phy_driver_unregister(&ip1001_driver); phy_driver_unregister(&ip175c_driver); } module_init(icplus_init); module_exit(icplus_exit); static struct mdio_device_id __maybe_unused icplus_tbl[] = { { 0x02430d80, 0x0ffffff0 }, { 0x02430d90, 0x0ffffff0 }, { } }; MODULE_DEVICE_TABLE(mdio, icplus_tbl);