1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+ 20cefeebaSMichael Barkowski /* 30cefeebaSMichael Barkowski * Driver for ICPlus PHYs 40cefeebaSMichael Barkowski * 50cefeebaSMichael Barkowski * Copyright (c) 2007 Freescale Semiconductor, Inc. 60cefeebaSMichael Barkowski */ 70cefeebaSMichael Barkowski #include <linux/kernel.h> 80cefeebaSMichael Barkowski #include <linux/string.h> 90cefeebaSMichael Barkowski #include <linux/errno.h> 100cefeebaSMichael Barkowski #include <linux/unistd.h> 110cefeebaSMichael Barkowski #include <linux/interrupt.h> 120cefeebaSMichael Barkowski #include <linux/init.h> 130cefeebaSMichael Barkowski #include <linux/delay.h> 140cefeebaSMichael Barkowski #include <linux/netdevice.h> 150cefeebaSMichael Barkowski #include <linux/etherdevice.h> 160cefeebaSMichael Barkowski #include <linux/skbuff.h> 170cefeebaSMichael Barkowski #include <linux/spinlock.h> 180cefeebaSMichael Barkowski #include <linux/mm.h> 190cefeebaSMichael Barkowski #include <linux/module.h> 200cefeebaSMichael Barkowski #include <linux/mii.h> 210cefeebaSMichael Barkowski #include <linux/ethtool.h> 220cefeebaSMichael Barkowski #include <linux/phy.h> 23f2f1a847SMartin Blumenstingl #include <linux/property.h> 240cefeebaSMichael Barkowski 250cefeebaSMichael Barkowski #include <asm/io.h> 260cefeebaSMichael Barkowski #include <asm/irq.h> 277c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 280cefeebaSMichael Barkowski 29e3e09f26SGiuseppe CAVALLARO MODULE_DESCRIPTION("ICPlus IP175C/IP101A/IP101G/IC1001 PHY drivers"); 300cefeebaSMichael Barkowski MODULE_AUTHOR("Michael Barkowski"); 310cefeebaSMichael Barkowski MODULE_LICENSE("GPL"); 320cefeebaSMichael Barkowski 33e3e09f26SGiuseppe CAVALLARO /* IP101A/G - IP1001 */ 349c9b1f24SGiuseppe CAVALLARO #define IP10XX_SPEC_CTRL_STATUS 16 /* Spec. Control Register */ 35ee336140SMartin Blumenstingl #define IP1001_RXPHASE_SEL BIT(0) /* Add delay on RX_CLK */ 36ee336140SMartin Blumenstingl #define IP1001_TXPHASE_SEL BIT(1) /* Add delay on TX_CLK */ 379c9b1f24SGiuseppe CAVALLARO #define IP1001_SPEC_CTRL_STATUS_2 20 /* IP1001 Spec. Control Reg 2 */ 389c9b1f24SGiuseppe CAVALLARO #define IP1001_APS_ON 11 /* IP1001 APS Mode bit */ 39ee336140SMartin Blumenstingl #define IP101A_G_APS_ON BIT(1) /* IP101A/G APS Mode bit */ 40996f7393SGiuseppe CAVALLARO #define IP101A_G_IRQ_CONF_STATUS 0x11 /* Conf Info IRQ & Status Reg */ 41ba2f55b0SHeiner Kallweit #define IP101A_G_IRQ_PIN_USED BIT(15) /* INTR pin used */ 42a872c388SMartin Blumenstingl #define IP101A_G_IRQ_ALL_MASK BIT(11) /* IRQ's inactive */ 43f7e290fbSMartin Blumenstingl #define IP101A_G_IRQ_SPEED_CHANGE BIT(2) 44f7e290fbSMartin Blumenstingl #define IP101A_G_IRQ_DUPLEX_CHANGE BIT(1) 45f7e290fbSMartin Blumenstingl #define IP101A_G_IRQ_LINK_CHANGE BIT(0) 469c9b1f24SGiuseppe CAVALLARO 47675115bfSMichael Walle #define IP101G_PAGE_CONTROL 0x14 48675115bfSMichael Walle #define IP101G_PAGE_CONTROL_MASK GENMASK(4, 0) 49f2f1a847SMartin Blumenstingl #define IP101G_DIGITAL_IO_SPEC_CTRL 0x1d 50f2f1a847SMartin Blumenstingl #define IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32 BIT(2) 51f2f1a847SMartin Blumenstingl 522ad4758cSMichael Walle #define IP175C_PHY_ID 0x02430d80 532ad4758cSMichael Walle #define IP1001_PHY_ID 0x02430d90 542ad4758cSMichael Walle #define IP101A_PHY_ID 0x02430c54 552ad4758cSMichael Walle 56f2f1a847SMartin Blumenstingl /* The 32-pin IP101GR package can re-configure the mode of the RXER/INTR_32 pin 57f2f1a847SMartin Blumenstingl * (pin number 21). The hardware default is RXER (receive error) mode. But it 58f2f1a847SMartin Blumenstingl * can be configured to interrupt mode manually. 59f2f1a847SMartin Blumenstingl */ 60f2f1a847SMartin Blumenstingl enum ip101gr_sel_intr32 { 61f2f1a847SMartin Blumenstingl IP101GR_SEL_INTR32_KEEP, 62f2f1a847SMartin Blumenstingl IP101GR_SEL_INTR32_INTR, 63f2f1a847SMartin Blumenstingl IP101GR_SEL_INTR32_RXER, 64f2f1a847SMartin Blumenstingl }; 65f2f1a847SMartin Blumenstingl 66f2f1a847SMartin Blumenstingl struct ip101a_g_phy_priv { 67f2f1a847SMartin Blumenstingl enum ip101gr_sel_intr32 sel_intr32; 68f2f1a847SMartin Blumenstingl }; 69f2f1a847SMartin Blumenstingl 700cefeebaSMichael Barkowski static int ip175c_config_init(struct phy_device *phydev) 710cefeebaSMichael Barkowski { 720cefeebaSMichael Barkowski int err, i; 739ed66cb5SFlorian Fainelli static int full_reset_performed; 740cefeebaSMichael Barkowski 750cefeebaSMichael Barkowski if (full_reset_performed == 0) { 760cefeebaSMichael Barkowski 770cefeebaSMichael Barkowski /* master reset */ 78e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, 30, 0, 0x175c); 790cefeebaSMichael Barkowski if (err < 0) 800cefeebaSMichael Barkowski return err; 810cefeebaSMichael Barkowski 820cefeebaSMichael Barkowski /* ensure no bus delays overlap reset period */ 83e5a03bfdSAndrew Lunn err = mdiobus_read(phydev->mdio.bus, 30, 0); 840cefeebaSMichael Barkowski 850cefeebaSMichael Barkowski /* data sheet specifies reset period is 2 msec */ 860cefeebaSMichael Barkowski mdelay(2); 870cefeebaSMichael Barkowski 880cefeebaSMichael Barkowski /* enable IP175C mode */ 89e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, 29, 31, 0x175c); 900cefeebaSMichael Barkowski if (err < 0) 910cefeebaSMichael Barkowski return err; 920cefeebaSMichael Barkowski 930cefeebaSMichael Barkowski /* Set MII0 speed and duplex (in PHY mode) */ 94e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, 29, 22, 0x420); 950cefeebaSMichael Barkowski if (err < 0) 960cefeebaSMichael Barkowski return err; 970cefeebaSMichael Barkowski 980cefeebaSMichael Barkowski /* reset switch ports */ 990cefeebaSMichael Barkowski for (i = 0; i < 5; i++) { 100e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, i, 1010cefeebaSMichael Barkowski MII_BMCR, BMCR_RESET); 1020cefeebaSMichael Barkowski if (err < 0) 1030cefeebaSMichael Barkowski return err; 1040cefeebaSMichael Barkowski } 1050cefeebaSMichael Barkowski 1060cefeebaSMichael Barkowski for (i = 0; i < 5; i++) 107e5a03bfdSAndrew Lunn err = mdiobus_read(phydev->mdio.bus, i, MII_BMCR); 1080cefeebaSMichael Barkowski 1090cefeebaSMichael Barkowski mdelay(2); 1100cefeebaSMichael Barkowski 1110cefeebaSMichael Barkowski full_reset_performed = 1; 1120cefeebaSMichael Barkowski } 1130cefeebaSMichael Barkowski 114e5a03bfdSAndrew Lunn if (phydev->mdio.addr != 4) { 1150cefeebaSMichael Barkowski phydev->state = PHY_RUNNING; 1160cefeebaSMichael Barkowski phydev->speed = SPEED_100; 1170cefeebaSMichael Barkowski phydev->duplex = DUPLEX_FULL; 1180cefeebaSMichael Barkowski phydev->link = 1; 1190cefeebaSMichael Barkowski netif_carrier_on(phydev->attached_dev); 1200cefeebaSMichael Barkowski } 1210cefeebaSMichael Barkowski 1220cefeebaSMichael Barkowski return 0; 1230cefeebaSMichael Barkowski } 1240cefeebaSMichael Barkowski 1259c9b1f24SGiuseppe CAVALLARO static int ip1001_config_init(struct phy_device *phydev) 1269c9b1f24SGiuseppe CAVALLARO { 1279c9b1f24SGiuseppe CAVALLARO int c; 1289c9b1f24SGiuseppe CAVALLARO 1299c9b1f24SGiuseppe CAVALLARO /* Enable Auto Power Saving mode */ 1309c9b1f24SGiuseppe CAVALLARO c = phy_read(phydev, IP1001_SPEC_CTRL_STATUS_2); 131b8e3995aSDavid McKay if (c < 0) 132b8e3995aSDavid McKay return c; 1339c9b1f24SGiuseppe CAVALLARO c |= IP1001_APS_ON; 134b8e3995aSDavid McKay c = phy_write(phydev, IP1001_SPEC_CTRL_STATUS_2, c); 1359c9b1f24SGiuseppe CAVALLARO if (c < 0) 1369c9b1f24SGiuseppe CAVALLARO return c; 137377ecca9SGiuseppe CAVALLARO 13832a64161SFlorian Fainelli if (phy_interface_is_rgmii(phydev)) { 139b4a49631SStuart Menefy 1409c9b1f24SGiuseppe CAVALLARO c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS); 141b8e3995aSDavid McKay if (c < 0) 142b8e3995aSDavid McKay return c; 143b8e3995aSDavid McKay 144b4a49631SStuart Menefy c &= ~(IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); 145b4a49631SStuart Menefy 146b4a49631SStuart Menefy if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 147b4a49631SStuart Menefy c |= (IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); 148b4a49631SStuart Menefy else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) 149b4a49631SStuart Menefy c |= IP1001_RXPHASE_SEL; 150b4a49631SStuart Menefy else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) 151b4a49631SStuart Menefy c |= IP1001_TXPHASE_SEL; 152b4a49631SStuart Menefy 153a4886d52SGiuseppe CAVALLARO c = phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c); 154b8e3995aSDavid McKay if (c < 0) 155b8e3995aSDavid McKay return c; 156a4886d52SGiuseppe CAVALLARO } 157377ecca9SGiuseppe CAVALLARO 158b8e3995aSDavid McKay return 0; 1599c9b1f24SGiuseppe CAVALLARO } 1609c9b1f24SGiuseppe CAVALLARO 1610cefeebaSMichael Barkowski static int ip175c_read_status(struct phy_device *phydev) 1620cefeebaSMichael Barkowski { 163e5a03bfdSAndrew Lunn if (phydev->mdio.addr == 4) /* WAN port */ 1640cefeebaSMichael Barkowski genphy_read_status(phydev); 1650cefeebaSMichael Barkowski else 1660cefeebaSMichael Barkowski /* Don't need to read status for switch ports */ 1670cefeebaSMichael Barkowski phydev->irq = PHY_IGNORE_INTERRUPT; 1680cefeebaSMichael Barkowski 1690cefeebaSMichael Barkowski return 0; 1700cefeebaSMichael Barkowski } 1710cefeebaSMichael Barkowski 1720cefeebaSMichael Barkowski static int ip175c_config_aneg(struct phy_device *phydev) 1730cefeebaSMichael Barkowski { 174e5a03bfdSAndrew Lunn if (phydev->mdio.addr == 4) /* WAN port */ 1750cefeebaSMichael Barkowski genphy_config_aneg(phydev); 1760cefeebaSMichael Barkowski 1770cefeebaSMichael Barkowski return 0; 1780cefeebaSMichael Barkowski } 1790cefeebaSMichael Barkowski 180f2f1a847SMartin Blumenstingl static int ip101a_g_probe(struct phy_device *phydev) 181f2f1a847SMartin Blumenstingl { 182f2f1a847SMartin Blumenstingl struct device *dev = &phydev->mdio.dev; 183f2f1a847SMartin Blumenstingl struct ip101a_g_phy_priv *priv; 184f2f1a847SMartin Blumenstingl 185f2f1a847SMartin Blumenstingl priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 186f2f1a847SMartin Blumenstingl if (!priv) 187f2f1a847SMartin Blumenstingl return -ENOMEM; 188f2f1a847SMartin Blumenstingl 189f2f1a847SMartin Blumenstingl /* Both functions (RX error and interrupt status) are sharing the same 190f2f1a847SMartin Blumenstingl * pin on the 32-pin IP101GR, so this is an exclusive choice. 191f2f1a847SMartin Blumenstingl */ 192f2f1a847SMartin Blumenstingl if (device_property_read_bool(dev, "icplus,select-rx-error") && 193f2f1a847SMartin Blumenstingl device_property_read_bool(dev, "icplus,select-interrupt")) { 194f2f1a847SMartin Blumenstingl dev_err(dev, 195f2f1a847SMartin Blumenstingl "RXER and INTR mode cannot be selected together\n"); 196f2f1a847SMartin Blumenstingl return -EINVAL; 197f2f1a847SMartin Blumenstingl } 198f2f1a847SMartin Blumenstingl 199f2f1a847SMartin Blumenstingl if (device_property_read_bool(dev, "icplus,select-rx-error")) 200f2f1a847SMartin Blumenstingl priv->sel_intr32 = IP101GR_SEL_INTR32_RXER; 201f2f1a847SMartin Blumenstingl else if (device_property_read_bool(dev, "icplus,select-interrupt")) 202f2f1a847SMartin Blumenstingl priv->sel_intr32 = IP101GR_SEL_INTR32_INTR; 203f2f1a847SMartin Blumenstingl else 204f2f1a847SMartin Blumenstingl priv->sel_intr32 = IP101GR_SEL_INTR32_KEEP; 205f2f1a847SMartin Blumenstingl 206f2f1a847SMartin Blumenstingl phydev->priv = priv; 207f2f1a847SMartin Blumenstingl 208f2f1a847SMartin Blumenstingl return 0; 209f2f1a847SMartin Blumenstingl } 210f2f1a847SMartin Blumenstingl 211*eeac7d43SMichael Walle static int ip101a_g_config_intr_pin(struct phy_device *phydev) 212034289b2SMartin Blumenstingl { 213f2f1a847SMartin Blumenstingl struct ip101a_g_phy_priv *priv = phydev->priv; 214*eeac7d43SMichael Walle int err; 215034289b2SMartin Blumenstingl 216f2f1a847SMartin Blumenstingl /* configure the RXER/INTR_32 pin of the 32-pin IP101GR if needed: */ 217f2f1a847SMartin Blumenstingl switch (priv->sel_intr32) { 218f2f1a847SMartin Blumenstingl case IP101GR_SEL_INTR32_RXER: 219f2f1a847SMartin Blumenstingl err = phy_modify(phydev, IP101G_DIGITAL_IO_SPEC_CTRL, 220f2f1a847SMartin Blumenstingl IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32, 0); 221f2f1a847SMartin Blumenstingl if (err < 0) 222f2f1a847SMartin Blumenstingl return err; 223f2f1a847SMartin Blumenstingl break; 224f2f1a847SMartin Blumenstingl 225f2f1a847SMartin Blumenstingl case IP101GR_SEL_INTR32_INTR: 226f2f1a847SMartin Blumenstingl err = phy_modify(phydev, IP101G_DIGITAL_IO_SPEC_CTRL, 227f2f1a847SMartin Blumenstingl IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32, 228f2f1a847SMartin Blumenstingl IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32); 229f2f1a847SMartin Blumenstingl if (err < 0) 230f2f1a847SMartin Blumenstingl return err; 231f2f1a847SMartin Blumenstingl break; 232f2f1a847SMartin Blumenstingl 233f2f1a847SMartin Blumenstingl default: 234f2f1a847SMartin Blumenstingl /* Don't touch IP101G_DIGITAL_IO_SPEC_CTRL because it's not 235f2f1a847SMartin Blumenstingl * documented on IP101A and it's not clear whether this would 236f2f1a847SMartin Blumenstingl * cause problems. 237f2f1a847SMartin Blumenstingl * For the 32-pin IP101GR we simply keep the SEL_INTR32 238f2f1a847SMartin Blumenstingl * configuration as set by the bootloader when not configured 239f2f1a847SMartin Blumenstingl * to one of the special functions. 240f2f1a847SMartin Blumenstingl */ 241f2f1a847SMartin Blumenstingl break; 242f2f1a847SMartin Blumenstingl } 243f2f1a847SMartin Blumenstingl 244*eeac7d43SMichael Walle return 0; 245*eeac7d43SMichael Walle } 246034289b2SMartin Blumenstingl 247*eeac7d43SMichael Walle static int ip101a_config_init(struct phy_device *phydev) 248*eeac7d43SMichael Walle { 249*eeac7d43SMichael Walle int ret; 250*eeac7d43SMichael Walle 251*eeac7d43SMichael Walle /* Enable Auto Power Saving mode */ 252*eeac7d43SMichael Walle ret = phy_set_bits(phydev, IP10XX_SPEC_CTRL_STATUS, IP101A_G_APS_ON); 253*eeac7d43SMichael Walle if (ret) 254*eeac7d43SMichael Walle return ret; 255*eeac7d43SMichael Walle 256*eeac7d43SMichael Walle return ip101a_g_config_intr_pin(phydev); 257*eeac7d43SMichael Walle } 258*eeac7d43SMichael Walle 259*eeac7d43SMichael Walle static int ip101g_config_init(struct phy_device *phydev) 260*eeac7d43SMichael Walle { 261*eeac7d43SMichael Walle return ip101a_g_config_intr_pin(phydev); 262034289b2SMartin Blumenstingl } 263034289b2SMartin Blumenstingl 26412ae7ba3SIoana Ciornei static int ip101a_g_ack_interrupt(struct phy_device *phydev) 26512ae7ba3SIoana Ciornei { 26612ae7ba3SIoana Ciornei int err = phy_read(phydev, IP101A_G_IRQ_CONF_STATUS); 26712ae7ba3SIoana Ciornei 26812ae7ba3SIoana Ciornei if (err < 0) 26912ae7ba3SIoana Ciornei return err; 27012ae7ba3SIoana Ciornei 27112ae7ba3SIoana Ciornei return 0; 27212ae7ba3SIoana Ciornei } 27312ae7ba3SIoana Ciornei 274ba2f55b0SHeiner Kallweit static int ip101a_g_config_intr(struct phy_device *phydev) 275ba2f55b0SHeiner Kallweit { 276ba2f55b0SHeiner Kallweit u16 val; 27712ae7ba3SIoana Ciornei int err; 278ba2f55b0SHeiner Kallweit 27912ae7ba3SIoana Ciornei if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 28012ae7ba3SIoana Ciornei err = ip101a_g_ack_interrupt(phydev); 28112ae7ba3SIoana Ciornei if (err) 28212ae7ba3SIoana Ciornei return err; 28312ae7ba3SIoana Ciornei 284ba2f55b0SHeiner Kallweit /* INTR pin used: Speed/link/duplex will cause an interrupt */ 285ba2f55b0SHeiner Kallweit val = IP101A_G_IRQ_PIN_USED; 28612ae7ba3SIoana Ciornei err = phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, val); 28712ae7ba3SIoana Ciornei } else { 288a872c388SMartin Blumenstingl val = IP101A_G_IRQ_ALL_MASK; 28912ae7ba3SIoana Ciornei err = phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, val); 29012ae7ba3SIoana Ciornei if (err) 29112ae7ba3SIoana Ciornei return err; 292ba2f55b0SHeiner Kallweit 29312ae7ba3SIoana Ciornei err = ip101a_g_ack_interrupt(phydev); 29412ae7ba3SIoana Ciornei } 29512ae7ba3SIoana Ciornei 29612ae7ba3SIoana Ciornei return err; 297ba2f55b0SHeiner Kallweit } 298ba2f55b0SHeiner Kallweit 29925497b7fSIoana Ciornei static irqreturn_t ip101a_g_handle_interrupt(struct phy_device *phydev) 300f7e290fbSMartin Blumenstingl { 30125497b7fSIoana Ciornei int irq_status; 302f7e290fbSMartin Blumenstingl 30325497b7fSIoana Ciornei irq_status = phy_read(phydev, IP101A_G_IRQ_CONF_STATUS); 30425497b7fSIoana Ciornei if (irq_status < 0) { 30525497b7fSIoana Ciornei phy_error(phydev); 30625497b7fSIoana Ciornei return IRQ_NONE; 30725497b7fSIoana Ciornei } 308f7e290fbSMartin Blumenstingl 30925497b7fSIoana Ciornei if (!(irq_status & (IP101A_G_IRQ_SPEED_CHANGE | 310f7e290fbSMartin Blumenstingl IP101A_G_IRQ_DUPLEX_CHANGE | 31125497b7fSIoana Ciornei IP101A_G_IRQ_LINK_CHANGE))) 31225497b7fSIoana Ciornei return IRQ_NONE; 31325497b7fSIoana Ciornei 31425497b7fSIoana Ciornei phy_trigger_machine(phydev); 31525497b7fSIoana Ciornei 31625497b7fSIoana Ciornei return IRQ_HANDLED; 317f7e290fbSMartin Blumenstingl } 318f7e290fbSMartin Blumenstingl 319675115bfSMichael Walle static int ip101a_g_has_page_register(struct phy_device *phydev) 320675115bfSMichael Walle { 321675115bfSMichael Walle int oldval, val, ret; 322675115bfSMichael Walle 323675115bfSMichael Walle oldval = phy_read(phydev, IP101G_PAGE_CONTROL); 324675115bfSMichael Walle if (oldval < 0) 325675115bfSMichael Walle return oldval; 326675115bfSMichael Walle 327675115bfSMichael Walle ret = phy_write(phydev, IP101G_PAGE_CONTROL, 0xffff); 328675115bfSMichael Walle if (ret) 329675115bfSMichael Walle return ret; 330675115bfSMichael Walle 331675115bfSMichael Walle val = phy_read(phydev, IP101G_PAGE_CONTROL); 332675115bfSMichael Walle if (val < 0) 333675115bfSMichael Walle return val; 334675115bfSMichael Walle 335675115bfSMichael Walle ret = phy_write(phydev, IP101G_PAGE_CONTROL, oldval); 336675115bfSMichael Walle if (ret) 337675115bfSMichael Walle return ret; 338675115bfSMichael Walle 339675115bfSMichael Walle return val == IP101G_PAGE_CONTROL_MASK; 340675115bfSMichael Walle } 341675115bfSMichael Walle 342675115bfSMichael Walle static int ip101a_g_match_phy_device(struct phy_device *phydev, bool ip101a) 343675115bfSMichael Walle { 344675115bfSMichael Walle int ret; 345675115bfSMichael Walle 346675115bfSMichael Walle if (phydev->phy_id != IP101A_PHY_ID) 347675115bfSMichael Walle return 0; 348675115bfSMichael Walle 349675115bfSMichael Walle /* The IP101A and the IP101G share the same PHY identifier.The IP101G 350675115bfSMichael Walle * seems to be a successor of the IP101A and implements more functions. 351675115bfSMichael Walle * Amongst other things there is a page select register, which is not 352675115bfSMichael Walle * available on the IP101A. Use this to distinguish these two. 353675115bfSMichael Walle */ 354675115bfSMichael Walle ret = ip101a_g_has_page_register(phydev); 355675115bfSMichael Walle if (ret < 0) 356675115bfSMichael Walle return ret; 357675115bfSMichael Walle 358675115bfSMichael Walle return ip101a == !ret; 359675115bfSMichael Walle } 360675115bfSMichael Walle 361675115bfSMichael Walle static int ip101a_match_phy_device(struct phy_device *phydev) 362675115bfSMichael Walle { 363675115bfSMichael Walle return ip101a_g_match_phy_device(phydev, true); 364675115bfSMichael Walle } 365675115bfSMichael Walle 366675115bfSMichael Walle static int ip101g_match_phy_device(struct phy_device *phydev) 367675115bfSMichael Walle { 368675115bfSMichael Walle return ip101a_g_match_phy_device(phydev, false); 369675115bfSMichael Walle } 370675115bfSMichael Walle 371d5bf9071SChristian Hohnstaedt static struct phy_driver icplus_driver[] = { 372d5bf9071SChristian Hohnstaedt { 3732ad4758cSMichael Walle PHY_ID_MATCH_MODEL(IP175C_PHY_ID), 3740cefeebaSMichael Barkowski .name = "ICPlus IP175C", 375dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 3768edf206cSMichael Walle .config_init = ip175c_config_init, 3778edf206cSMichael Walle .config_aneg = ip175c_config_aneg, 3788edf206cSMichael Walle .read_status = ip175c_read_status, 379dab10863SGiuseppe Cavallaro .suspend = genphy_suspend, 380dab10863SGiuseppe Cavallaro .resume = genphy_resume, 381d5bf9071SChristian Hohnstaedt }, { 3822ad4758cSMichael Walle PHY_ID_MATCH_MODEL(IP1001_PHY_ID), 383377ecca9SGiuseppe CAVALLARO .name = "ICPlus IP1001", 384dcdecdcfSHeiner Kallweit /* PHY_GBIT_FEATURES */ 3858edf206cSMichael Walle .config_init = ip1001_config_init, 386df22de9aSMichael Walle .soft_reset = genphy_soft_reset, 387377ecca9SGiuseppe CAVALLARO .suspend = genphy_suspend, 388377ecca9SGiuseppe CAVALLARO .resume = genphy_resume, 389d5bf9071SChristian Hohnstaedt }, { 390675115bfSMichael Walle .name = "ICPlus IP101A", 391675115bfSMichael Walle .match_phy_device = ip101a_match_phy_device, 392675115bfSMichael Walle .probe = ip101a_g_probe, 393675115bfSMichael Walle .config_intr = ip101a_g_config_intr, 394675115bfSMichael Walle .handle_interrupt = ip101a_g_handle_interrupt, 395*eeac7d43SMichael Walle .config_init = ip101a_config_init, 396675115bfSMichael Walle .soft_reset = genphy_soft_reset, 397675115bfSMichael Walle .suspend = genphy_suspend, 398675115bfSMichael Walle .resume = genphy_resume, 399675115bfSMichael Walle }, { 400675115bfSMichael Walle .name = "ICPlus IP101G", 401675115bfSMichael Walle .match_phy_device = ip101g_match_phy_device, 402f2f1a847SMartin Blumenstingl .probe = ip101a_g_probe, 403ba2f55b0SHeiner Kallweit .config_intr = ip101a_g_config_intr, 40425497b7fSIoana Ciornei .handle_interrupt = ip101a_g_handle_interrupt, 405*eeac7d43SMichael Walle .config_init = ip101g_config_init, 406df22de9aSMichael Walle .soft_reset = genphy_soft_reset, 4079c9b1f24SGiuseppe CAVALLARO .suspend = genphy_suspend, 4089c9b1f24SGiuseppe CAVALLARO .resume = genphy_resume, 409d5bf9071SChristian Hohnstaedt } }; 4109c9b1f24SGiuseppe CAVALLARO 41150fd7150SJohan Hovold module_phy_driver(icplus_driver); 4124e4f10f6SDavid Woodhouse 413cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused icplus_tbl[] = { 4142ad4758cSMichael Walle { PHY_ID_MATCH_MODEL(IP175C_PHY_ID) }, 4152ad4758cSMichael Walle { PHY_ID_MATCH_MODEL(IP1001_PHY_ID) }, 4167360a4deSMichael Walle { PHY_ID_MATCH_EXACT(IP101A_PHY_ID) }, 4174e4f10f6SDavid Woodhouse { } 4184e4f10f6SDavid Woodhouse }; 4194e4f10f6SDavid Woodhouse 4204e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, icplus_tbl); 421