10cefeebaSMichael Barkowski /* 20cefeebaSMichael Barkowski * Driver for ICPlus PHYs 30cefeebaSMichael Barkowski * 40cefeebaSMichael Barkowski * Copyright (c) 2007 Freescale Semiconductor, Inc. 50cefeebaSMichael Barkowski * 60cefeebaSMichael Barkowski * This program is free software; you can redistribute it and/or modify it 70cefeebaSMichael Barkowski * under the terms of the GNU General Public License as published by the 80cefeebaSMichael Barkowski * Free Software Foundation; either version 2 of the License, or (at your 90cefeebaSMichael Barkowski * option) any later version. 100cefeebaSMichael Barkowski * 110cefeebaSMichael Barkowski */ 120cefeebaSMichael Barkowski #include <linux/kernel.h> 130cefeebaSMichael Barkowski #include <linux/string.h> 140cefeebaSMichael Barkowski #include <linux/errno.h> 150cefeebaSMichael Barkowski #include <linux/unistd.h> 160cefeebaSMichael Barkowski #include <linux/interrupt.h> 170cefeebaSMichael Barkowski #include <linux/init.h> 180cefeebaSMichael Barkowski #include <linux/delay.h> 190cefeebaSMichael Barkowski #include <linux/netdevice.h> 200cefeebaSMichael Barkowski #include <linux/etherdevice.h> 210cefeebaSMichael Barkowski #include <linux/skbuff.h> 220cefeebaSMichael Barkowski #include <linux/spinlock.h> 230cefeebaSMichael Barkowski #include <linux/mm.h> 240cefeebaSMichael Barkowski #include <linux/module.h> 250cefeebaSMichael Barkowski #include <linux/mii.h> 260cefeebaSMichael Barkowski #include <linux/ethtool.h> 270cefeebaSMichael Barkowski #include <linux/phy.h> 280cefeebaSMichael Barkowski 290cefeebaSMichael Barkowski #include <asm/io.h> 300cefeebaSMichael Barkowski #include <asm/irq.h> 317c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 320cefeebaSMichael Barkowski 33e3e09f26SGiuseppe CAVALLARO MODULE_DESCRIPTION("ICPlus IP175C/IP101A/IP101G/IC1001 PHY drivers"); 340cefeebaSMichael Barkowski MODULE_AUTHOR("Michael Barkowski"); 350cefeebaSMichael Barkowski MODULE_LICENSE("GPL"); 360cefeebaSMichael Barkowski 37e3e09f26SGiuseppe CAVALLARO /* IP101A/G - IP1001 */ 389c9b1f24SGiuseppe CAVALLARO #define IP10XX_SPEC_CTRL_STATUS 16 /* Spec. Control Register */ 39b4a49631SStuart Menefy #define IP1001_RXPHASE_SEL (1<<0) /* Add delay on RX_CLK */ 40b4a49631SStuart Menefy #define IP1001_TXPHASE_SEL (1<<1) /* Add delay on TX_CLK */ 419c9b1f24SGiuseppe CAVALLARO #define IP1001_SPEC_CTRL_STATUS_2 20 /* IP1001 Spec. Control Reg 2 */ 429c9b1f24SGiuseppe CAVALLARO #define IP1001_APS_ON 11 /* IP1001 APS Mode bit */ 43e3e09f26SGiuseppe CAVALLARO #define IP101A_G_APS_ON 2 /* IP101A/G APS Mode bit */ 44996f7393SGiuseppe CAVALLARO #define IP101A_G_IRQ_CONF_STATUS 0x11 /* Conf Info IRQ & Status Reg */ 45ba2f55b0SHeiner Kallweit #define IP101A_G_IRQ_PIN_USED BIT(15) /* INTR pin used */ 46ba2f55b0SHeiner Kallweit #define IP101A_G_NO_IRQ BIT(11) /* IRQ's inactive */ 479c9b1f24SGiuseppe CAVALLARO 480cefeebaSMichael Barkowski static int ip175c_config_init(struct phy_device *phydev) 490cefeebaSMichael Barkowski { 500cefeebaSMichael Barkowski int err, i; 519ed66cb5SFlorian Fainelli static int full_reset_performed; 520cefeebaSMichael Barkowski 530cefeebaSMichael Barkowski if (full_reset_performed == 0) { 540cefeebaSMichael Barkowski 550cefeebaSMichael Barkowski /* master reset */ 56e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, 30, 0, 0x175c); 570cefeebaSMichael Barkowski if (err < 0) 580cefeebaSMichael Barkowski return err; 590cefeebaSMichael Barkowski 600cefeebaSMichael Barkowski /* ensure no bus delays overlap reset period */ 61e5a03bfdSAndrew Lunn err = mdiobus_read(phydev->mdio.bus, 30, 0); 620cefeebaSMichael Barkowski 630cefeebaSMichael Barkowski /* data sheet specifies reset period is 2 msec */ 640cefeebaSMichael Barkowski mdelay(2); 650cefeebaSMichael Barkowski 660cefeebaSMichael Barkowski /* enable IP175C mode */ 67e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, 29, 31, 0x175c); 680cefeebaSMichael Barkowski if (err < 0) 690cefeebaSMichael Barkowski return err; 700cefeebaSMichael Barkowski 710cefeebaSMichael Barkowski /* Set MII0 speed and duplex (in PHY mode) */ 72e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, 29, 22, 0x420); 730cefeebaSMichael Barkowski if (err < 0) 740cefeebaSMichael Barkowski return err; 750cefeebaSMichael Barkowski 760cefeebaSMichael Barkowski /* reset switch ports */ 770cefeebaSMichael Barkowski for (i = 0; i < 5; i++) { 78e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, i, 790cefeebaSMichael Barkowski MII_BMCR, BMCR_RESET); 800cefeebaSMichael Barkowski if (err < 0) 810cefeebaSMichael Barkowski return err; 820cefeebaSMichael Barkowski } 830cefeebaSMichael Barkowski 840cefeebaSMichael Barkowski for (i = 0; i < 5; i++) 85e5a03bfdSAndrew Lunn err = mdiobus_read(phydev->mdio.bus, i, MII_BMCR); 860cefeebaSMichael Barkowski 870cefeebaSMichael Barkowski mdelay(2); 880cefeebaSMichael Barkowski 890cefeebaSMichael Barkowski full_reset_performed = 1; 900cefeebaSMichael Barkowski } 910cefeebaSMichael Barkowski 92e5a03bfdSAndrew Lunn if (phydev->mdio.addr != 4) { 930cefeebaSMichael Barkowski phydev->state = PHY_RUNNING; 940cefeebaSMichael Barkowski phydev->speed = SPEED_100; 950cefeebaSMichael Barkowski phydev->duplex = DUPLEX_FULL; 960cefeebaSMichael Barkowski phydev->link = 1; 970cefeebaSMichael Barkowski netif_carrier_on(phydev->attached_dev); 980cefeebaSMichael Barkowski } 990cefeebaSMichael Barkowski 1000cefeebaSMichael Barkowski return 0; 1010cefeebaSMichael Barkowski } 1020cefeebaSMichael Barkowski 1039c9b1f24SGiuseppe CAVALLARO static int ip1xx_reset(struct phy_device *phydev) 104377ecca9SGiuseppe CAVALLARO { 105b8e3995aSDavid McKay int bmcr; 106377ecca9SGiuseppe CAVALLARO 107377ecca9SGiuseppe CAVALLARO /* Software Reset PHY */ 1089c9b1f24SGiuseppe CAVALLARO bmcr = phy_read(phydev, MII_BMCR); 109b8e3995aSDavid McKay if (bmcr < 0) 110b8e3995aSDavid McKay return bmcr; 1119c9b1f24SGiuseppe CAVALLARO bmcr |= BMCR_RESET; 112b8e3995aSDavid McKay bmcr = phy_write(phydev, MII_BMCR, bmcr); 113b8e3995aSDavid McKay if (bmcr < 0) 114b8e3995aSDavid McKay return bmcr; 115377ecca9SGiuseppe CAVALLARO 116377ecca9SGiuseppe CAVALLARO do { 1179c9b1f24SGiuseppe CAVALLARO bmcr = phy_read(phydev, MII_BMCR); 118b8e3995aSDavid McKay if (bmcr < 0) 119b8e3995aSDavid McKay return bmcr; 1209c9b1f24SGiuseppe CAVALLARO } while (bmcr & BMCR_RESET); 1219c9b1f24SGiuseppe CAVALLARO 122b8e3995aSDavid McKay return 0; 1239c9b1f24SGiuseppe CAVALLARO } 1249c9b1f24SGiuseppe CAVALLARO 1259c9b1f24SGiuseppe CAVALLARO static int ip1001_config_init(struct phy_device *phydev) 1269c9b1f24SGiuseppe CAVALLARO { 1279c9b1f24SGiuseppe CAVALLARO int c; 1289c9b1f24SGiuseppe CAVALLARO 1299c9b1f24SGiuseppe CAVALLARO c = ip1xx_reset(phydev); 1309c9b1f24SGiuseppe CAVALLARO if (c < 0) 1319c9b1f24SGiuseppe CAVALLARO return c; 1329c9b1f24SGiuseppe CAVALLARO 1339c9b1f24SGiuseppe CAVALLARO /* Enable Auto Power Saving mode */ 1349c9b1f24SGiuseppe CAVALLARO c = phy_read(phydev, IP1001_SPEC_CTRL_STATUS_2); 135b8e3995aSDavid McKay if (c < 0) 136b8e3995aSDavid McKay return c; 1379c9b1f24SGiuseppe CAVALLARO c |= IP1001_APS_ON; 138b8e3995aSDavid McKay c = phy_write(phydev, IP1001_SPEC_CTRL_STATUS_2, c); 1399c9b1f24SGiuseppe CAVALLARO if (c < 0) 1409c9b1f24SGiuseppe CAVALLARO return c; 141377ecca9SGiuseppe CAVALLARO 14232a64161SFlorian Fainelli if (phy_interface_is_rgmii(phydev)) { 143b4a49631SStuart Menefy 1449c9b1f24SGiuseppe CAVALLARO c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS); 145b8e3995aSDavid McKay if (c < 0) 146b8e3995aSDavid McKay return c; 147b8e3995aSDavid McKay 148b4a49631SStuart Menefy c &= ~(IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); 149b4a49631SStuart Menefy 150b4a49631SStuart Menefy if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 151b4a49631SStuart Menefy c |= (IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); 152b4a49631SStuart Menefy else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) 153b4a49631SStuart Menefy c |= IP1001_RXPHASE_SEL; 154b4a49631SStuart Menefy else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) 155b4a49631SStuart Menefy c |= IP1001_TXPHASE_SEL; 156b4a49631SStuart Menefy 157a4886d52SGiuseppe CAVALLARO c = phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c); 158b8e3995aSDavid McKay if (c < 0) 159b8e3995aSDavid McKay return c; 160a4886d52SGiuseppe CAVALLARO } 161377ecca9SGiuseppe CAVALLARO 162b8e3995aSDavid McKay return 0; 1639c9b1f24SGiuseppe CAVALLARO } 1649c9b1f24SGiuseppe CAVALLARO 1650cefeebaSMichael Barkowski static int ip175c_read_status(struct phy_device *phydev) 1660cefeebaSMichael Barkowski { 167e5a03bfdSAndrew Lunn if (phydev->mdio.addr == 4) /* WAN port */ 1680cefeebaSMichael Barkowski genphy_read_status(phydev); 1690cefeebaSMichael Barkowski else 1700cefeebaSMichael Barkowski /* Don't need to read status for switch ports */ 1710cefeebaSMichael Barkowski phydev->irq = PHY_IGNORE_INTERRUPT; 1720cefeebaSMichael Barkowski 1730cefeebaSMichael Barkowski return 0; 1740cefeebaSMichael Barkowski } 1750cefeebaSMichael Barkowski 1760cefeebaSMichael Barkowski static int ip175c_config_aneg(struct phy_device *phydev) 1770cefeebaSMichael Barkowski { 178e5a03bfdSAndrew Lunn if (phydev->mdio.addr == 4) /* WAN port */ 1790cefeebaSMichael Barkowski genphy_config_aneg(phydev); 1800cefeebaSMichael Barkowski 1810cefeebaSMichael Barkowski return 0; 1820cefeebaSMichael Barkowski } 1830cefeebaSMichael Barkowski 184*034289b2SMartin Blumenstingl static int ip101a_g_config_init(struct phy_device *phydev) 185*034289b2SMartin Blumenstingl { 186*034289b2SMartin Blumenstingl int c; 187*034289b2SMartin Blumenstingl 188*034289b2SMartin Blumenstingl c = ip1xx_reset(phydev); 189*034289b2SMartin Blumenstingl if (c < 0) 190*034289b2SMartin Blumenstingl return c; 191*034289b2SMartin Blumenstingl 192*034289b2SMartin Blumenstingl /* Enable Auto Power Saving mode */ 193*034289b2SMartin Blumenstingl c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS); 194*034289b2SMartin Blumenstingl c |= IP101A_G_APS_ON; 195*034289b2SMartin Blumenstingl 196*034289b2SMartin Blumenstingl return phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c); 197*034289b2SMartin Blumenstingl } 198*034289b2SMartin Blumenstingl 199ba2f55b0SHeiner Kallweit static int ip101a_g_config_intr(struct phy_device *phydev) 200ba2f55b0SHeiner Kallweit { 201ba2f55b0SHeiner Kallweit u16 val; 202ba2f55b0SHeiner Kallweit 203ba2f55b0SHeiner Kallweit if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 204ba2f55b0SHeiner Kallweit /* INTR pin used: Speed/link/duplex will cause an interrupt */ 205ba2f55b0SHeiner Kallweit val = IP101A_G_IRQ_PIN_USED; 206ba2f55b0SHeiner Kallweit else 207ba2f55b0SHeiner Kallweit val = IP101A_G_NO_IRQ; 208ba2f55b0SHeiner Kallweit 209ba2f55b0SHeiner Kallweit return phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, val); 210ba2f55b0SHeiner Kallweit } 211ba2f55b0SHeiner Kallweit 212996f7393SGiuseppe CAVALLARO static int ip101a_g_ack_interrupt(struct phy_device *phydev) 213996f7393SGiuseppe CAVALLARO { 214996f7393SGiuseppe CAVALLARO int err = phy_read(phydev, IP101A_G_IRQ_CONF_STATUS); 215996f7393SGiuseppe CAVALLARO if (err < 0) 216996f7393SGiuseppe CAVALLARO return err; 217996f7393SGiuseppe CAVALLARO 218996f7393SGiuseppe CAVALLARO return 0; 219996f7393SGiuseppe CAVALLARO } 220996f7393SGiuseppe CAVALLARO 221d5bf9071SChristian Hohnstaedt static struct phy_driver icplus_driver[] = { 222d5bf9071SChristian Hohnstaedt { 2230cefeebaSMichael Barkowski .phy_id = 0x02430d80, 2240cefeebaSMichael Barkowski .name = "ICPlus IP175C", 2250cefeebaSMichael Barkowski .phy_id_mask = 0x0ffffff0, 2260cefeebaSMichael Barkowski .features = PHY_BASIC_FEATURES, 2270cefeebaSMichael Barkowski .config_init = &ip175c_config_init, 2280cefeebaSMichael Barkowski .config_aneg = &ip175c_config_aneg, 2290cefeebaSMichael Barkowski .read_status = &ip175c_read_status, 230dab10863SGiuseppe Cavallaro .suspend = genphy_suspend, 231dab10863SGiuseppe Cavallaro .resume = genphy_resume, 232d5bf9071SChristian Hohnstaedt }, { 233377ecca9SGiuseppe CAVALLARO .phy_id = 0x02430d90, 234377ecca9SGiuseppe CAVALLARO .name = "ICPlus IP1001", 235377ecca9SGiuseppe CAVALLARO .phy_id_mask = 0x0ffffff0, 236529ed127STimur Tabi .features = PHY_GBIT_FEATURES, 237377ecca9SGiuseppe CAVALLARO .config_init = &ip1001_config_init, 238377ecca9SGiuseppe CAVALLARO .suspend = genphy_suspend, 239377ecca9SGiuseppe CAVALLARO .resume = genphy_resume, 240d5bf9071SChristian Hohnstaedt }, { 2419c9b1f24SGiuseppe CAVALLARO .phy_id = 0x02430c54, 242e3e09f26SGiuseppe CAVALLARO .name = "ICPlus IP101A/G", 2439c9b1f24SGiuseppe CAVALLARO .phy_id_mask = 0x0ffffff0, 244529ed127STimur Tabi .features = PHY_BASIC_FEATURES, 245ba2f55b0SHeiner Kallweit .config_intr = ip101a_g_config_intr, 246996f7393SGiuseppe CAVALLARO .ack_interrupt = ip101a_g_ack_interrupt, 247e3e09f26SGiuseppe CAVALLARO .config_init = &ip101a_g_config_init, 2489c9b1f24SGiuseppe CAVALLARO .suspend = genphy_suspend, 2499c9b1f24SGiuseppe CAVALLARO .resume = genphy_resume, 250d5bf9071SChristian Hohnstaedt } }; 2519c9b1f24SGiuseppe CAVALLARO 25250fd7150SJohan Hovold module_phy_driver(icplus_driver); 2534e4f10f6SDavid Woodhouse 254cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused icplus_tbl[] = { 2554e4f10f6SDavid Woodhouse { 0x02430d80, 0x0ffffff0 }, 256377ecca9SGiuseppe CAVALLARO { 0x02430d90, 0x0ffffff0 }, 257e3e09f26SGiuseppe CAVALLARO { 0x02430c54, 0x0ffffff0 }, 2584e4f10f6SDavid Woodhouse { } 2594e4f10f6SDavid Woodhouse }; 2604e4f10f6SDavid Woodhouse 2614e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, icplus_tbl); 262