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 52*f9bc51e6SMichael Walle #define IP101G_DEFAULT_PAGE 16 53*f9bc51e6SMichael Walle 542ad4758cSMichael Walle #define IP175C_PHY_ID 0x02430d80 552ad4758cSMichael Walle #define IP1001_PHY_ID 0x02430d90 562ad4758cSMichael Walle #define IP101A_PHY_ID 0x02430c54 572ad4758cSMichael Walle 58f2f1a847SMartin Blumenstingl /* The 32-pin IP101GR package can re-configure the mode of the RXER/INTR_32 pin 59f2f1a847SMartin Blumenstingl * (pin number 21). The hardware default is RXER (receive error) mode. But it 60f2f1a847SMartin Blumenstingl * can be configured to interrupt mode manually. 61f2f1a847SMartin Blumenstingl */ 62f2f1a847SMartin Blumenstingl enum ip101gr_sel_intr32 { 63f2f1a847SMartin Blumenstingl IP101GR_SEL_INTR32_KEEP, 64f2f1a847SMartin Blumenstingl IP101GR_SEL_INTR32_INTR, 65f2f1a847SMartin Blumenstingl IP101GR_SEL_INTR32_RXER, 66f2f1a847SMartin Blumenstingl }; 67f2f1a847SMartin Blumenstingl 68f2f1a847SMartin Blumenstingl struct ip101a_g_phy_priv { 69f2f1a847SMartin Blumenstingl enum ip101gr_sel_intr32 sel_intr32; 70f2f1a847SMartin Blumenstingl }; 71f2f1a847SMartin Blumenstingl 720cefeebaSMichael Barkowski static int ip175c_config_init(struct phy_device *phydev) 730cefeebaSMichael Barkowski { 740cefeebaSMichael Barkowski int err, i; 759ed66cb5SFlorian Fainelli static int full_reset_performed; 760cefeebaSMichael Barkowski 770cefeebaSMichael Barkowski if (full_reset_performed == 0) { 780cefeebaSMichael Barkowski 790cefeebaSMichael Barkowski /* master reset */ 80e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, 30, 0, 0x175c); 810cefeebaSMichael Barkowski if (err < 0) 820cefeebaSMichael Barkowski return err; 830cefeebaSMichael Barkowski 840cefeebaSMichael Barkowski /* ensure no bus delays overlap reset period */ 85e5a03bfdSAndrew Lunn err = mdiobus_read(phydev->mdio.bus, 30, 0); 860cefeebaSMichael Barkowski 870cefeebaSMichael Barkowski /* data sheet specifies reset period is 2 msec */ 880cefeebaSMichael Barkowski mdelay(2); 890cefeebaSMichael Barkowski 900cefeebaSMichael Barkowski /* enable IP175C mode */ 91e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, 29, 31, 0x175c); 920cefeebaSMichael Barkowski if (err < 0) 930cefeebaSMichael Barkowski return err; 940cefeebaSMichael Barkowski 950cefeebaSMichael Barkowski /* Set MII0 speed and duplex (in PHY mode) */ 96e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, 29, 22, 0x420); 970cefeebaSMichael Barkowski if (err < 0) 980cefeebaSMichael Barkowski return err; 990cefeebaSMichael Barkowski 1000cefeebaSMichael Barkowski /* reset switch ports */ 1010cefeebaSMichael Barkowski for (i = 0; i < 5; i++) { 102e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, i, 1030cefeebaSMichael Barkowski MII_BMCR, BMCR_RESET); 1040cefeebaSMichael Barkowski if (err < 0) 1050cefeebaSMichael Barkowski return err; 1060cefeebaSMichael Barkowski } 1070cefeebaSMichael Barkowski 1080cefeebaSMichael Barkowski for (i = 0; i < 5; i++) 109e5a03bfdSAndrew Lunn err = mdiobus_read(phydev->mdio.bus, i, MII_BMCR); 1100cefeebaSMichael Barkowski 1110cefeebaSMichael Barkowski mdelay(2); 1120cefeebaSMichael Barkowski 1130cefeebaSMichael Barkowski full_reset_performed = 1; 1140cefeebaSMichael Barkowski } 1150cefeebaSMichael Barkowski 116e5a03bfdSAndrew Lunn if (phydev->mdio.addr != 4) { 1170cefeebaSMichael Barkowski phydev->state = PHY_RUNNING; 1180cefeebaSMichael Barkowski phydev->speed = SPEED_100; 1190cefeebaSMichael Barkowski phydev->duplex = DUPLEX_FULL; 1200cefeebaSMichael Barkowski phydev->link = 1; 1210cefeebaSMichael Barkowski netif_carrier_on(phydev->attached_dev); 1220cefeebaSMichael Barkowski } 1230cefeebaSMichael Barkowski 1240cefeebaSMichael Barkowski return 0; 1250cefeebaSMichael Barkowski } 1260cefeebaSMichael Barkowski 1279c9b1f24SGiuseppe CAVALLARO static int ip1001_config_init(struct phy_device *phydev) 1289c9b1f24SGiuseppe CAVALLARO { 1299c9b1f24SGiuseppe CAVALLARO int c; 1309c9b1f24SGiuseppe CAVALLARO 1319c9b1f24SGiuseppe CAVALLARO /* Enable Auto Power Saving mode */ 1329c9b1f24SGiuseppe CAVALLARO c = phy_read(phydev, IP1001_SPEC_CTRL_STATUS_2); 133b8e3995aSDavid McKay if (c < 0) 134b8e3995aSDavid McKay return c; 1359c9b1f24SGiuseppe CAVALLARO c |= IP1001_APS_ON; 136b8e3995aSDavid McKay c = phy_write(phydev, IP1001_SPEC_CTRL_STATUS_2, c); 1379c9b1f24SGiuseppe CAVALLARO if (c < 0) 1389c9b1f24SGiuseppe CAVALLARO return c; 139377ecca9SGiuseppe CAVALLARO 14032a64161SFlorian Fainelli if (phy_interface_is_rgmii(phydev)) { 141b4a49631SStuart Menefy 1429c9b1f24SGiuseppe CAVALLARO c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS); 143b8e3995aSDavid McKay if (c < 0) 144b8e3995aSDavid McKay return c; 145b8e3995aSDavid McKay 146b4a49631SStuart Menefy c &= ~(IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); 147b4a49631SStuart Menefy 148b4a49631SStuart Menefy if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 149b4a49631SStuart Menefy c |= (IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); 150b4a49631SStuart Menefy else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) 151b4a49631SStuart Menefy c |= IP1001_RXPHASE_SEL; 152b4a49631SStuart Menefy else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) 153b4a49631SStuart Menefy c |= IP1001_TXPHASE_SEL; 154b4a49631SStuart Menefy 155a4886d52SGiuseppe CAVALLARO c = phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c); 156b8e3995aSDavid McKay if (c < 0) 157b8e3995aSDavid McKay return c; 158a4886d52SGiuseppe CAVALLARO } 159377ecca9SGiuseppe CAVALLARO 160b8e3995aSDavid McKay return 0; 1619c9b1f24SGiuseppe CAVALLARO } 1629c9b1f24SGiuseppe CAVALLARO 1630cefeebaSMichael Barkowski static int ip175c_read_status(struct phy_device *phydev) 1640cefeebaSMichael Barkowski { 165e5a03bfdSAndrew Lunn if (phydev->mdio.addr == 4) /* WAN port */ 1660cefeebaSMichael Barkowski genphy_read_status(phydev); 1670cefeebaSMichael Barkowski else 1680cefeebaSMichael Barkowski /* Don't need to read status for switch ports */ 1690cefeebaSMichael Barkowski phydev->irq = PHY_IGNORE_INTERRUPT; 1700cefeebaSMichael Barkowski 1710cefeebaSMichael Barkowski return 0; 1720cefeebaSMichael Barkowski } 1730cefeebaSMichael Barkowski 1740cefeebaSMichael Barkowski static int ip175c_config_aneg(struct phy_device *phydev) 1750cefeebaSMichael Barkowski { 176e5a03bfdSAndrew Lunn if (phydev->mdio.addr == 4) /* WAN port */ 1770cefeebaSMichael Barkowski genphy_config_aneg(phydev); 1780cefeebaSMichael Barkowski 1790cefeebaSMichael Barkowski return 0; 1800cefeebaSMichael Barkowski } 1810cefeebaSMichael Barkowski 182f2f1a847SMartin Blumenstingl static int ip101a_g_probe(struct phy_device *phydev) 183f2f1a847SMartin Blumenstingl { 184f2f1a847SMartin Blumenstingl struct device *dev = &phydev->mdio.dev; 185f2f1a847SMartin Blumenstingl struct ip101a_g_phy_priv *priv; 186f2f1a847SMartin Blumenstingl 187f2f1a847SMartin Blumenstingl priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 188f2f1a847SMartin Blumenstingl if (!priv) 189f2f1a847SMartin Blumenstingl return -ENOMEM; 190f2f1a847SMartin Blumenstingl 191f2f1a847SMartin Blumenstingl /* Both functions (RX error and interrupt status) are sharing the same 192f2f1a847SMartin Blumenstingl * pin on the 32-pin IP101GR, so this is an exclusive choice. 193f2f1a847SMartin Blumenstingl */ 194f2f1a847SMartin Blumenstingl if (device_property_read_bool(dev, "icplus,select-rx-error") && 195f2f1a847SMartin Blumenstingl device_property_read_bool(dev, "icplus,select-interrupt")) { 196f2f1a847SMartin Blumenstingl dev_err(dev, 197f2f1a847SMartin Blumenstingl "RXER and INTR mode cannot be selected together\n"); 198f2f1a847SMartin Blumenstingl return -EINVAL; 199f2f1a847SMartin Blumenstingl } 200f2f1a847SMartin Blumenstingl 201f2f1a847SMartin Blumenstingl if (device_property_read_bool(dev, "icplus,select-rx-error")) 202f2f1a847SMartin Blumenstingl priv->sel_intr32 = IP101GR_SEL_INTR32_RXER; 203f2f1a847SMartin Blumenstingl else if (device_property_read_bool(dev, "icplus,select-interrupt")) 204f2f1a847SMartin Blumenstingl priv->sel_intr32 = IP101GR_SEL_INTR32_INTR; 205f2f1a847SMartin Blumenstingl else 206f2f1a847SMartin Blumenstingl priv->sel_intr32 = IP101GR_SEL_INTR32_KEEP; 207f2f1a847SMartin Blumenstingl 208f2f1a847SMartin Blumenstingl phydev->priv = priv; 209f2f1a847SMartin Blumenstingl 210f2f1a847SMartin Blumenstingl return 0; 211f2f1a847SMartin Blumenstingl } 212f2f1a847SMartin Blumenstingl 213eeac7d43SMichael Walle static int ip101a_g_config_intr_pin(struct phy_device *phydev) 214034289b2SMartin Blumenstingl { 215f2f1a847SMartin Blumenstingl struct ip101a_g_phy_priv *priv = phydev->priv; 216*f9bc51e6SMichael Walle int oldpage, err = 0; 217*f9bc51e6SMichael Walle 218*f9bc51e6SMichael Walle oldpage = phy_select_page(phydev, IP101G_DEFAULT_PAGE); 219*f9bc51e6SMichael Walle if (oldpage < 0) 220*f9bc51e6SMichael Walle return oldpage; 221034289b2SMartin Blumenstingl 222f2f1a847SMartin Blumenstingl /* configure the RXER/INTR_32 pin of the 32-pin IP101GR if needed: */ 223f2f1a847SMartin Blumenstingl switch (priv->sel_intr32) { 224f2f1a847SMartin Blumenstingl case IP101GR_SEL_INTR32_RXER: 225*f9bc51e6SMichael Walle err = __phy_modify(phydev, IP101G_DIGITAL_IO_SPEC_CTRL, 226f2f1a847SMartin Blumenstingl IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32, 0); 227f2f1a847SMartin Blumenstingl if (err < 0) 228*f9bc51e6SMichael Walle goto out; 229f2f1a847SMartin Blumenstingl break; 230f2f1a847SMartin Blumenstingl 231f2f1a847SMartin Blumenstingl case IP101GR_SEL_INTR32_INTR: 232*f9bc51e6SMichael Walle err = __phy_modify(phydev, IP101G_DIGITAL_IO_SPEC_CTRL, 233f2f1a847SMartin Blumenstingl IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32, 234f2f1a847SMartin Blumenstingl IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32); 235f2f1a847SMartin Blumenstingl if (err < 0) 236*f9bc51e6SMichael Walle goto out; 237f2f1a847SMartin Blumenstingl break; 238f2f1a847SMartin Blumenstingl 239f2f1a847SMartin Blumenstingl default: 240f2f1a847SMartin Blumenstingl /* Don't touch IP101G_DIGITAL_IO_SPEC_CTRL because it's not 241f2f1a847SMartin Blumenstingl * documented on IP101A and it's not clear whether this would 242f2f1a847SMartin Blumenstingl * cause problems. 243f2f1a847SMartin Blumenstingl * For the 32-pin IP101GR we simply keep the SEL_INTR32 244f2f1a847SMartin Blumenstingl * configuration as set by the bootloader when not configured 245f2f1a847SMartin Blumenstingl * to one of the special functions. 246f2f1a847SMartin Blumenstingl */ 247f2f1a847SMartin Blumenstingl break; 248f2f1a847SMartin Blumenstingl } 249f2f1a847SMartin Blumenstingl 250*f9bc51e6SMichael Walle out: 251*f9bc51e6SMichael Walle return phy_restore_page(phydev, oldpage, err); 252eeac7d43SMichael Walle } 253034289b2SMartin Blumenstingl 254eeac7d43SMichael Walle static int ip101a_config_init(struct phy_device *phydev) 255eeac7d43SMichael Walle { 256eeac7d43SMichael Walle int ret; 257eeac7d43SMichael Walle 258eeac7d43SMichael Walle /* Enable Auto Power Saving mode */ 259eeac7d43SMichael Walle ret = phy_set_bits(phydev, IP10XX_SPEC_CTRL_STATUS, IP101A_G_APS_ON); 260eeac7d43SMichael Walle if (ret) 261eeac7d43SMichael Walle return ret; 262eeac7d43SMichael Walle 263eeac7d43SMichael Walle return ip101a_g_config_intr_pin(phydev); 264eeac7d43SMichael Walle } 265eeac7d43SMichael Walle 266eeac7d43SMichael Walle static int ip101g_config_init(struct phy_device *phydev) 267eeac7d43SMichael Walle { 268eeac7d43SMichael Walle return ip101a_g_config_intr_pin(phydev); 269034289b2SMartin Blumenstingl } 270034289b2SMartin Blumenstingl 27112ae7ba3SIoana Ciornei static int ip101a_g_ack_interrupt(struct phy_device *phydev) 27212ae7ba3SIoana Ciornei { 273*f9bc51e6SMichael Walle int err; 27412ae7ba3SIoana Ciornei 275*f9bc51e6SMichael Walle err = phy_read_paged(phydev, IP101G_DEFAULT_PAGE, 276*f9bc51e6SMichael Walle IP101A_G_IRQ_CONF_STATUS); 27712ae7ba3SIoana Ciornei if (err < 0) 27812ae7ba3SIoana Ciornei return err; 27912ae7ba3SIoana Ciornei 28012ae7ba3SIoana Ciornei return 0; 28112ae7ba3SIoana Ciornei } 28212ae7ba3SIoana Ciornei 283ba2f55b0SHeiner Kallweit static int ip101a_g_config_intr(struct phy_device *phydev) 284ba2f55b0SHeiner Kallweit { 285ba2f55b0SHeiner Kallweit u16 val; 28612ae7ba3SIoana Ciornei int err; 287ba2f55b0SHeiner Kallweit 28812ae7ba3SIoana Ciornei if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 28912ae7ba3SIoana Ciornei err = ip101a_g_ack_interrupt(phydev); 29012ae7ba3SIoana Ciornei if (err) 29112ae7ba3SIoana Ciornei return err; 29212ae7ba3SIoana Ciornei 293ba2f55b0SHeiner Kallweit /* INTR pin used: Speed/link/duplex will cause an interrupt */ 294ba2f55b0SHeiner Kallweit val = IP101A_G_IRQ_PIN_USED; 295*f9bc51e6SMichael Walle err = phy_write_paged(phydev, IP101G_DEFAULT_PAGE, 296*f9bc51e6SMichael Walle IP101A_G_IRQ_CONF_STATUS, val); 29712ae7ba3SIoana Ciornei } else { 298a872c388SMartin Blumenstingl val = IP101A_G_IRQ_ALL_MASK; 299*f9bc51e6SMichael Walle err = phy_write_paged(phydev, IP101G_DEFAULT_PAGE, 300*f9bc51e6SMichael Walle IP101A_G_IRQ_CONF_STATUS, val); 30112ae7ba3SIoana Ciornei if (err) 30212ae7ba3SIoana Ciornei return err; 303ba2f55b0SHeiner Kallweit 30412ae7ba3SIoana Ciornei err = ip101a_g_ack_interrupt(phydev); 30512ae7ba3SIoana Ciornei } 30612ae7ba3SIoana Ciornei 30712ae7ba3SIoana Ciornei return err; 308ba2f55b0SHeiner Kallweit } 309ba2f55b0SHeiner Kallweit 31025497b7fSIoana Ciornei static irqreturn_t ip101a_g_handle_interrupt(struct phy_device *phydev) 311f7e290fbSMartin Blumenstingl { 31225497b7fSIoana Ciornei int irq_status; 313f7e290fbSMartin Blumenstingl 314*f9bc51e6SMichael Walle irq_status = phy_read_paged(phydev, IP101G_DEFAULT_PAGE, 315*f9bc51e6SMichael Walle IP101A_G_IRQ_CONF_STATUS); 31625497b7fSIoana Ciornei if (irq_status < 0) { 31725497b7fSIoana Ciornei phy_error(phydev); 31825497b7fSIoana Ciornei return IRQ_NONE; 31925497b7fSIoana Ciornei } 320f7e290fbSMartin Blumenstingl 32125497b7fSIoana Ciornei if (!(irq_status & (IP101A_G_IRQ_SPEED_CHANGE | 322f7e290fbSMartin Blumenstingl IP101A_G_IRQ_DUPLEX_CHANGE | 32325497b7fSIoana Ciornei IP101A_G_IRQ_LINK_CHANGE))) 32425497b7fSIoana Ciornei return IRQ_NONE; 32525497b7fSIoana Ciornei 32625497b7fSIoana Ciornei phy_trigger_machine(phydev); 32725497b7fSIoana Ciornei 32825497b7fSIoana Ciornei return IRQ_HANDLED; 329f7e290fbSMartin Blumenstingl } 330f7e290fbSMartin Blumenstingl 331*f9bc51e6SMichael Walle /* The IP101A doesn't really have a page register. We just pretend to have one 332*f9bc51e6SMichael Walle * so we can use the paged versions of the callbacks of the IP101G. 333*f9bc51e6SMichael Walle */ 334*f9bc51e6SMichael Walle static int ip101a_read_page(struct phy_device *phydev) 335*f9bc51e6SMichael Walle { 336*f9bc51e6SMichael Walle return IP101G_DEFAULT_PAGE; 337*f9bc51e6SMichael Walle } 338*f9bc51e6SMichael Walle 339*f9bc51e6SMichael Walle static int ip101a_write_page(struct phy_device *phydev, int page) 340*f9bc51e6SMichael Walle { 341*f9bc51e6SMichael Walle WARN_ONCE(page != IP101G_DEFAULT_PAGE, "wrong page selected\n"); 342*f9bc51e6SMichael Walle 343*f9bc51e6SMichael Walle return 0; 344*f9bc51e6SMichael Walle } 345*f9bc51e6SMichael Walle 346*f9bc51e6SMichael Walle static int ip101g_read_page(struct phy_device *phydev) 347*f9bc51e6SMichael Walle { 348*f9bc51e6SMichael Walle return __phy_read(phydev, IP101G_PAGE_CONTROL); 349*f9bc51e6SMichael Walle } 350*f9bc51e6SMichael Walle 351*f9bc51e6SMichael Walle static int ip101g_write_page(struct phy_device *phydev, int page) 352*f9bc51e6SMichael Walle { 353*f9bc51e6SMichael Walle return __phy_write(phydev, IP101G_PAGE_CONTROL, page); 354*f9bc51e6SMichael Walle } 355*f9bc51e6SMichael Walle 356675115bfSMichael Walle static int ip101a_g_has_page_register(struct phy_device *phydev) 357675115bfSMichael Walle { 358675115bfSMichael Walle int oldval, val, ret; 359675115bfSMichael Walle 360675115bfSMichael Walle oldval = phy_read(phydev, IP101G_PAGE_CONTROL); 361675115bfSMichael Walle if (oldval < 0) 362675115bfSMichael Walle return oldval; 363675115bfSMichael Walle 364675115bfSMichael Walle ret = phy_write(phydev, IP101G_PAGE_CONTROL, 0xffff); 365675115bfSMichael Walle if (ret) 366675115bfSMichael Walle return ret; 367675115bfSMichael Walle 368675115bfSMichael Walle val = phy_read(phydev, IP101G_PAGE_CONTROL); 369675115bfSMichael Walle if (val < 0) 370675115bfSMichael Walle return val; 371675115bfSMichael Walle 372675115bfSMichael Walle ret = phy_write(phydev, IP101G_PAGE_CONTROL, oldval); 373675115bfSMichael Walle if (ret) 374675115bfSMichael Walle return ret; 375675115bfSMichael Walle 376675115bfSMichael Walle return val == IP101G_PAGE_CONTROL_MASK; 377675115bfSMichael Walle } 378675115bfSMichael Walle 379675115bfSMichael Walle static int ip101a_g_match_phy_device(struct phy_device *phydev, bool ip101a) 380675115bfSMichael Walle { 381675115bfSMichael Walle int ret; 382675115bfSMichael Walle 383675115bfSMichael Walle if (phydev->phy_id != IP101A_PHY_ID) 384675115bfSMichael Walle return 0; 385675115bfSMichael Walle 386675115bfSMichael Walle /* The IP101A and the IP101G share the same PHY identifier.The IP101G 387675115bfSMichael Walle * seems to be a successor of the IP101A and implements more functions. 388675115bfSMichael Walle * Amongst other things there is a page select register, which is not 389675115bfSMichael Walle * available on the IP101A. Use this to distinguish these two. 390675115bfSMichael Walle */ 391675115bfSMichael Walle ret = ip101a_g_has_page_register(phydev); 392675115bfSMichael Walle if (ret < 0) 393675115bfSMichael Walle return ret; 394675115bfSMichael Walle 395675115bfSMichael Walle return ip101a == !ret; 396675115bfSMichael Walle } 397675115bfSMichael Walle 398675115bfSMichael Walle static int ip101a_match_phy_device(struct phy_device *phydev) 399675115bfSMichael Walle { 400675115bfSMichael Walle return ip101a_g_match_phy_device(phydev, true); 401675115bfSMichael Walle } 402675115bfSMichael Walle 403675115bfSMichael Walle static int ip101g_match_phy_device(struct phy_device *phydev) 404675115bfSMichael Walle { 405675115bfSMichael Walle return ip101a_g_match_phy_device(phydev, false); 406675115bfSMichael Walle } 407675115bfSMichael Walle 408d5bf9071SChristian Hohnstaedt static struct phy_driver icplus_driver[] = { 409d5bf9071SChristian Hohnstaedt { 4102ad4758cSMichael Walle PHY_ID_MATCH_MODEL(IP175C_PHY_ID), 4110cefeebaSMichael Barkowski .name = "ICPlus IP175C", 412dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 4138edf206cSMichael Walle .config_init = ip175c_config_init, 4148edf206cSMichael Walle .config_aneg = ip175c_config_aneg, 4158edf206cSMichael Walle .read_status = ip175c_read_status, 416dab10863SGiuseppe Cavallaro .suspend = genphy_suspend, 417dab10863SGiuseppe Cavallaro .resume = genphy_resume, 418d5bf9071SChristian Hohnstaedt }, { 4192ad4758cSMichael Walle PHY_ID_MATCH_MODEL(IP1001_PHY_ID), 420377ecca9SGiuseppe CAVALLARO .name = "ICPlus IP1001", 421dcdecdcfSHeiner Kallweit /* PHY_GBIT_FEATURES */ 4228edf206cSMichael Walle .config_init = ip1001_config_init, 423df22de9aSMichael Walle .soft_reset = genphy_soft_reset, 424377ecca9SGiuseppe CAVALLARO .suspend = genphy_suspend, 425377ecca9SGiuseppe CAVALLARO .resume = genphy_resume, 426d5bf9071SChristian Hohnstaedt }, { 427675115bfSMichael Walle .name = "ICPlus IP101A", 428675115bfSMichael Walle .match_phy_device = ip101a_match_phy_device, 429675115bfSMichael Walle .probe = ip101a_g_probe, 430*f9bc51e6SMichael Walle .read_page = ip101a_read_page, 431*f9bc51e6SMichael Walle .write_page = ip101a_write_page, 432675115bfSMichael Walle .config_intr = ip101a_g_config_intr, 433675115bfSMichael Walle .handle_interrupt = ip101a_g_handle_interrupt, 434eeac7d43SMichael Walle .config_init = ip101a_config_init, 435675115bfSMichael Walle .soft_reset = genphy_soft_reset, 436675115bfSMichael Walle .suspend = genphy_suspend, 437675115bfSMichael Walle .resume = genphy_resume, 438675115bfSMichael Walle }, { 439675115bfSMichael Walle .name = "ICPlus IP101G", 440675115bfSMichael Walle .match_phy_device = ip101g_match_phy_device, 441f2f1a847SMartin Blumenstingl .probe = ip101a_g_probe, 442*f9bc51e6SMichael Walle .read_page = ip101g_read_page, 443*f9bc51e6SMichael Walle .write_page = ip101g_write_page, 444ba2f55b0SHeiner Kallweit .config_intr = ip101a_g_config_intr, 44525497b7fSIoana Ciornei .handle_interrupt = ip101a_g_handle_interrupt, 446eeac7d43SMichael Walle .config_init = ip101g_config_init, 447df22de9aSMichael Walle .soft_reset = genphy_soft_reset, 4489c9b1f24SGiuseppe CAVALLARO .suspend = genphy_suspend, 4499c9b1f24SGiuseppe CAVALLARO .resume = genphy_resume, 450d5bf9071SChristian Hohnstaedt } }; 4519c9b1f24SGiuseppe CAVALLARO 45250fd7150SJohan Hovold module_phy_driver(icplus_driver); 4534e4f10f6SDavid Woodhouse 454cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused icplus_tbl[] = { 4552ad4758cSMichael Walle { PHY_ID_MATCH_MODEL(IP175C_PHY_ID) }, 4562ad4758cSMichael Walle { PHY_ID_MATCH_MODEL(IP1001_PHY_ID) }, 4577360a4deSMichael Walle { PHY_ID_MATCH_EXACT(IP101A_PHY_ID) }, 4584e4f10f6SDavid Woodhouse { } 4594e4f10f6SDavid Woodhouse }; 4604e4f10f6SDavid Woodhouse 4614e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, icplus_tbl); 462