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 52f9bc51e6SMichael Walle #define IP101G_DEFAULT_PAGE 16 53f9bc51e6SMichael Walle 54*a0750d42SMichael Walle #define IP101G_P1_CNT_CTRL 17 55*a0750d42SMichael Walle #define CNT_CTRL_RX_EN BIT(13) 56*a0750d42SMichael Walle #define IP101G_P8_CNT_CTRL 17 57*a0750d42SMichael Walle #define CNT_CTRL_RDCLR_EN BIT(15) 58*a0750d42SMichael Walle #define IP101G_CNT_REG 18 59*a0750d42SMichael Walle 602ad4758cSMichael Walle #define IP175C_PHY_ID 0x02430d80 612ad4758cSMichael Walle #define IP1001_PHY_ID 0x02430d90 622ad4758cSMichael Walle #define IP101A_PHY_ID 0x02430c54 632ad4758cSMichael Walle 64f2f1a847SMartin Blumenstingl /* The 32-pin IP101GR package can re-configure the mode of the RXER/INTR_32 pin 65f2f1a847SMartin Blumenstingl * (pin number 21). The hardware default is RXER (receive error) mode. But it 66f2f1a847SMartin Blumenstingl * can be configured to interrupt mode manually. 67f2f1a847SMartin Blumenstingl */ 68f2f1a847SMartin Blumenstingl enum ip101gr_sel_intr32 { 69f2f1a847SMartin Blumenstingl IP101GR_SEL_INTR32_KEEP, 70f2f1a847SMartin Blumenstingl IP101GR_SEL_INTR32_INTR, 71f2f1a847SMartin Blumenstingl IP101GR_SEL_INTR32_RXER, 72f2f1a847SMartin Blumenstingl }; 73f2f1a847SMartin Blumenstingl 74*a0750d42SMichael Walle struct ip101g_hw_stat { 75*a0750d42SMichael Walle const char *name; 76*a0750d42SMichael Walle int page; 77*a0750d42SMichael Walle }; 78*a0750d42SMichael Walle 79*a0750d42SMichael Walle static struct ip101g_hw_stat ip101g_hw_stats[] = { 80*a0750d42SMichael Walle { "phy_crc_errors", 1 }, 81*a0750d42SMichael Walle { "phy_symbol_errors", 11, }, 82*a0750d42SMichael Walle }; 83*a0750d42SMichael Walle 84f2f1a847SMartin Blumenstingl struct ip101a_g_phy_priv { 85f2f1a847SMartin Blumenstingl enum ip101gr_sel_intr32 sel_intr32; 86*a0750d42SMichael Walle u64 stats[ARRAY_SIZE(ip101g_hw_stats)]; 87f2f1a847SMartin Blumenstingl }; 88f2f1a847SMartin Blumenstingl 890cefeebaSMichael Barkowski static int ip175c_config_init(struct phy_device *phydev) 900cefeebaSMichael Barkowski { 910cefeebaSMichael Barkowski int err, i; 929ed66cb5SFlorian Fainelli static int full_reset_performed; 930cefeebaSMichael Barkowski 940cefeebaSMichael Barkowski if (full_reset_performed == 0) { 950cefeebaSMichael Barkowski 960cefeebaSMichael Barkowski /* master reset */ 97e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, 30, 0, 0x175c); 980cefeebaSMichael Barkowski if (err < 0) 990cefeebaSMichael Barkowski return err; 1000cefeebaSMichael Barkowski 1010cefeebaSMichael Barkowski /* ensure no bus delays overlap reset period */ 102e5a03bfdSAndrew Lunn err = mdiobus_read(phydev->mdio.bus, 30, 0); 1030cefeebaSMichael Barkowski 1040cefeebaSMichael Barkowski /* data sheet specifies reset period is 2 msec */ 1050cefeebaSMichael Barkowski mdelay(2); 1060cefeebaSMichael Barkowski 1070cefeebaSMichael Barkowski /* enable IP175C mode */ 108e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, 29, 31, 0x175c); 1090cefeebaSMichael Barkowski if (err < 0) 1100cefeebaSMichael Barkowski return err; 1110cefeebaSMichael Barkowski 1120cefeebaSMichael Barkowski /* Set MII0 speed and duplex (in PHY mode) */ 113e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, 29, 22, 0x420); 1140cefeebaSMichael Barkowski if (err < 0) 1150cefeebaSMichael Barkowski return err; 1160cefeebaSMichael Barkowski 1170cefeebaSMichael Barkowski /* reset switch ports */ 1180cefeebaSMichael Barkowski for (i = 0; i < 5; i++) { 119e5a03bfdSAndrew Lunn err = mdiobus_write(phydev->mdio.bus, i, 1200cefeebaSMichael Barkowski MII_BMCR, BMCR_RESET); 1210cefeebaSMichael Barkowski if (err < 0) 1220cefeebaSMichael Barkowski return err; 1230cefeebaSMichael Barkowski } 1240cefeebaSMichael Barkowski 1250cefeebaSMichael Barkowski for (i = 0; i < 5; i++) 126e5a03bfdSAndrew Lunn err = mdiobus_read(phydev->mdio.bus, i, MII_BMCR); 1270cefeebaSMichael Barkowski 1280cefeebaSMichael Barkowski mdelay(2); 1290cefeebaSMichael Barkowski 1300cefeebaSMichael Barkowski full_reset_performed = 1; 1310cefeebaSMichael Barkowski } 1320cefeebaSMichael Barkowski 133e5a03bfdSAndrew Lunn if (phydev->mdio.addr != 4) { 1340cefeebaSMichael Barkowski phydev->state = PHY_RUNNING; 1350cefeebaSMichael Barkowski phydev->speed = SPEED_100; 1360cefeebaSMichael Barkowski phydev->duplex = DUPLEX_FULL; 1370cefeebaSMichael Barkowski phydev->link = 1; 1380cefeebaSMichael Barkowski netif_carrier_on(phydev->attached_dev); 1390cefeebaSMichael Barkowski } 1400cefeebaSMichael Barkowski 1410cefeebaSMichael Barkowski return 0; 1420cefeebaSMichael Barkowski } 1430cefeebaSMichael Barkowski 1449c9b1f24SGiuseppe CAVALLARO static int ip1001_config_init(struct phy_device *phydev) 1459c9b1f24SGiuseppe CAVALLARO { 1469c9b1f24SGiuseppe CAVALLARO int c; 1479c9b1f24SGiuseppe CAVALLARO 1489c9b1f24SGiuseppe CAVALLARO /* Enable Auto Power Saving mode */ 1499c9b1f24SGiuseppe CAVALLARO c = phy_read(phydev, IP1001_SPEC_CTRL_STATUS_2); 150b8e3995aSDavid McKay if (c < 0) 151b8e3995aSDavid McKay return c; 1529c9b1f24SGiuseppe CAVALLARO c |= IP1001_APS_ON; 153b8e3995aSDavid McKay c = phy_write(phydev, IP1001_SPEC_CTRL_STATUS_2, c); 1549c9b1f24SGiuseppe CAVALLARO if (c < 0) 1559c9b1f24SGiuseppe CAVALLARO return c; 156377ecca9SGiuseppe CAVALLARO 15732a64161SFlorian Fainelli if (phy_interface_is_rgmii(phydev)) { 158b4a49631SStuart Menefy 1599c9b1f24SGiuseppe CAVALLARO c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS); 160b8e3995aSDavid McKay if (c < 0) 161b8e3995aSDavid McKay return c; 162b8e3995aSDavid McKay 163b4a49631SStuart Menefy c &= ~(IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); 164b4a49631SStuart Menefy 165b4a49631SStuart Menefy if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 166b4a49631SStuart Menefy c |= (IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); 167b4a49631SStuart Menefy else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) 168b4a49631SStuart Menefy c |= IP1001_RXPHASE_SEL; 169b4a49631SStuart Menefy else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) 170b4a49631SStuart Menefy c |= IP1001_TXPHASE_SEL; 171b4a49631SStuart Menefy 172a4886d52SGiuseppe CAVALLARO c = phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c); 173b8e3995aSDavid McKay if (c < 0) 174b8e3995aSDavid McKay return c; 175a4886d52SGiuseppe CAVALLARO } 176377ecca9SGiuseppe CAVALLARO 177b8e3995aSDavid McKay return 0; 1789c9b1f24SGiuseppe CAVALLARO } 1799c9b1f24SGiuseppe CAVALLARO 1800cefeebaSMichael Barkowski static int ip175c_read_status(struct phy_device *phydev) 1810cefeebaSMichael Barkowski { 182e5a03bfdSAndrew Lunn if (phydev->mdio.addr == 4) /* WAN port */ 1830cefeebaSMichael Barkowski genphy_read_status(phydev); 1840cefeebaSMichael Barkowski else 1850cefeebaSMichael Barkowski /* Don't need to read status for switch ports */ 1860cefeebaSMichael Barkowski phydev->irq = PHY_IGNORE_INTERRUPT; 1870cefeebaSMichael Barkowski 1880cefeebaSMichael Barkowski return 0; 1890cefeebaSMichael Barkowski } 1900cefeebaSMichael Barkowski 1910cefeebaSMichael Barkowski static int ip175c_config_aneg(struct phy_device *phydev) 1920cefeebaSMichael Barkowski { 193e5a03bfdSAndrew Lunn if (phydev->mdio.addr == 4) /* WAN port */ 1940cefeebaSMichael Barkowski genphy_config_aneg(phydev); 1950cefeebaSMichael Barkowski 1960cefeebaSMichael Barkowski return 0; 1970cefeebaSMichael Barkowski } 1980cefeebaSMichael Barkowski 199f2f1a847SMartin Blumenstingl static int ip101a_g_probe(struct phy_device *phydev) 200f2f1a847SMartin Blumenstingl { 201f2f1a847SMartin Blumenstingl struct device *dev = &phydev->mdio.dev; 202f2f1a847SMartin Blumenstingl struct ip101a_g_phy_priv *priv; 203f2f1a847SMartin Blumenstingl 204f2f1a847SMartin Blumenstingl priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 205f2f1a847SMartin Blumenstingl if (!priv) 206f2f1a847SMartin Blumenstingl return -ENOMEM; 207f2f1a847SMartin Blumenstingl 208f2f1a847SMartin Blumenstingl /* Both functions (RX error and interrupt status) are sharing the same 209f2f1a847SMartin Blumenstingl * pin on the 32-pin IP101GR, so this is an exclusive choice. 210f2f1a847SMartin Blumenstingl */ 211f2f1a847SMartin Blumenstingl if (device_property_read_bool(dev, "icplus,select-rx-error") && 212f2f1a847SMartin Blumenstingl device_property_read_bool(dev, "icplus,select-interrupt")) { 213f2f1a847SMartin Blumenstingl dev_err(dev, 214f2f1a847SMartin Blumenstingl "RXER and INTR mode cannot be selected together\n"); 215f2f1a847SMartin Blumenstingl return -EINVAL; 216f2f1a847SMartin Blumenstingl } 217f2f1a847SMartin Blumenstingl 218f2f1a847SMartin Blumenstingl if (device_property_read_bool(dev, "icplus,select-rx-error")) 219f2f1a847SMartin Blumenstingl priv->sel_intr32 = IP101GR_SEL_INTR32_RXER; 220f2f1a847SMartin Blumenstingl else if (device_property_read_bool(dev, "icplus,select-interrupt")) 221f2f1a847SMartin Blumenstingl priv->sel_intr32 = IP101GR_SEL_INTR32_INTR; 222f2f1a847SMartin Blumenstingl else 223f2f1a847SMartin Blumenstingl priv->sel_intr32 = IP101GR_SEL_INTR32_KEEP; 224f2f1a847SMartin Blumenstingl 225f2f1a847SMartin Blumenstingl phydev->priv = priv; 226f2f1a847SMartin Blumenstingl 227f2f1a847SMartin Blumenstingl return 0; 228f2f1a847SMartin Blumenstingl } 229f2f1a847SMartin Blumenstingl 230eeac7d43SMichael Walle static int ip101a_g_config_intr_pin(struct phy_device *phydev) 231034289b2SMartin Blumenstingl { 232f2f1a847SMartin Blumenstingl struct ip101a_g_phy_priv *priv = phydev->priv; 233f9bc51e6SMichael Walle int oldpage, err = 0; 234f9bc51e6SMichael Walle 235f9bc51e6SMichael Walle oldpage = phy_select_page(phydev, IP101G_DEFAULT_PAGE); 236f9bc51e6SMichael Walle if (oldpage < 0) 237f9bc51e6SMichael Walle return oldpage; 238034289b2SMartin Blumenstingl 239f2f1a847SMartin Blumenstingl /* configure the RXER/INTR_32 pin of the 32-pin IP101GR if needed: */ 240f2f1a847SMartin Blumenstingl switch (priv->sel_intr32) { 241f2f1a847SMartin Blumenstingl case IP101GR_SEL_INTR32_RXER: 242f9bc51e6SMichael Walle err = __phy_modify(phydev, IP101G_DIGITAL_IO_SPEC_CTRL, 243f2f1a847SMartin Blumenstingl IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32, 0); 244f2f1a847SMartin Blumenstingl if (err < 0) 245f9bc51e6SMichael Walle goto out; 246f2f1a847SMartin Blumenstingl break; 247f2f1a847SMartin Blumenstingl 248f2f1a847SMartin Blumenstingl case IP101GR_SEL_INTR32_INTR: 249f9bc51e6SMichael Walle err = __phy_modify(phydev, IP101G_DIGITAL_IO_SPEC_CTRL, 250f2f1a847SMartin Blumenstingl IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32, 251f2f1a847SMartin Blumenstingl IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32); 252f2f1a847SMartin Blumenstingl if (err < 0) 253f9bc51e6SMichael Walle goto out; 254f2f1a847SMartin Blumenstingl break; 255f2f1a847SMartin Blumenstingl 256f2f1a847SMartin Blumenstingl default: 257f2f1a847SMartin Blumenstingl /* Don't touch IP101G_DIGITAL_IO_SPEC_CTRL because it's not 258f2f1a847SMartin Blumenstingl * documented on IP101A and it's not clear whether this would 259f2f1a847SMartin Blumenstingl * cause problems. 260f2f1a847SMartin Blumenstingl * For the 32-pin IP101GR we simply keep the SEL_INTR32 261f2f1a847SMartin Blumenstingl * configuration as set by the bootloader when not configured 262f2f1a847SMartin Blumenstingl * to one of the special functions. 263f2f1a847SMartin Blumenstingl */ 264f2f1a847SMartin Blumenstingl break; 265f2f1a847SMartin Blumenstingl } 266f2f1a847SMartin Blumenstingl 267f9bc51e6SMichael Walle out: 268f9bc51e6SMichael Walle return phy_restore_page(phydev, oldpage, err); 269eeac7d43SMichael Walle } 270034289b2SMartin Blumenstingl 271eeac7d43SMichael Walle static int ip101a_config_init(struct phy_device *phydev) 272eeac7d43SMichael Walle { 273eeac7d43SMichael Walle int ret; 274eeac7d43SMichael Walle 275eeac7d43SMichael Walle /* Enable Auto Power Saving mode */ 276eeac7d43SMichael Walle ret = phy_set_bits(phydev, IP10XX_SPEC_CTRL_STATUS, IP101A_G_APS_ON); 277eeac7d43SMichael Walle if (ret) 278eeac7d43SMichael Walle return ret; 279eeac7d43SMichael Walle 280eeac7d43SMichael Walle return ip101a_g_config_intr_pin(phydev); 281eeac7d43SMichael Walle } 282eeac7d43SMichael Walle 283eeac7d43SMichael Walle static int ip101g_config_init(struct phy_device *phydev) 284eeac7d43SMichael Walle { 285*a0750d42SMichael Walle int ret; 286*a0750d42SMichael Walle 287*a0750d42SMichael Walle /* Enable the PHY counters */ 288*a0750d42SMichael Walle ret = phy_modify_paged(phydev, 1, IP101G_P1_CNT_CTRL, 289*a0750d42SMichael Walle CNT_CTRL_RX_EN, CNT_CTRL_RX_EN); 290*a0750d42SMichael Walle if (ret) 291*a0750d42SMichael Walle return ret; 292*a0750d42SMichael Walle 293*a0750d42SMichael Walle /* Clear error counters on read */ 294*a0750d42SMichael Walle ret = phy_modify_paged(phydev, 8, IP101G_P8_CNT_CTRL, 295*a0750d42SMichael Walle CNT_CTRL_RDCLR_EN, CNT_CTRL_RDCLR_EN); 296*a0750d42SMichael Walle if (ret) 297*a0750d42SMichael Walle return ret; 298*a0750d42SMichael Walle 299eeac7d43SMichael Walle return ip101a_g_config_intr_pin(phydev); 300034289b2SMartin Blumenstingl } 301034289b2SMartin Blumenstingl 30212ae7ba3SIoana Ciornei static int ip101a_g_ack_interrupt(struct phy_device *phydev) 30312ae7ba3SIoana Ciornei { 304f9bc51e6SMichael Walle int err; 30512ae7ba3SIoana Ciornei 306f9bc51e6SMichael Walle err = phy_read_paged(phydev, IP101G_DEFAULT_PAGE, 307f9bc51e6SMichael Walle IP101A_G_IRQ_CONF_STATUS); 30812ae7ba3SIoana Ciornei if (err < 0) 30912ae7ba3SIoana Ciornei return err; 31012ae7ba3SIoana Ciornei 31112ae7ba3SIoana Ciornei return 0; 31212ae7ba3SIoana Ciornei } 31312ae7ba3SIoana Ciornei 314ba2f55b0SHeiner Kallweit static int ip101a_g_config_intr(struct phy_device *phydev) 315ba2f55b0SHeiner Kallweit { 316ba2f55b0SHeiner Kallweit u16 val; 31712ae7ba3SIoana Ciornei int err; 318ba2f55b0SHeiner Kallweit 31912ae7ba3SIoana Ciornei if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 32012ae7ba3SIoana Ciornei err = ip101a_g_ack_interrupt(phydev); 32112ae7ba3SIoana Ciornei if (err) 32212ae7ba3SIoana Ciornei return err; 32312ae7ba3SIoana Ciornei 324ba2f55b0SHeiner Kallweit /* INTR pin used: Speed/link/duplex will cause an interrupt */ 325ba2f55b0SHeiner Kallweit val = IP101A_G_IRQ_PIN_USED; 326f9bc51e6SMichael Walle err = phy_write_paged(phydev, IP101G_DEFAULT_PAGE, 327f9bc51e6SMichael Walle IP101A_G_IRQ_CONF_STATUS, val); 32812ae7ba3SIoana Ciornei } else { 329a872c388SMartin Blumenstingl val = IP101A_G_IRQ_ALL_MASK; 330f9bc51e6SMichael Walle err = phy_write_paged(phydev, IP101G_DEFAULT_PAGE, 331f9bc51e6SMichael Walle IP101A_G_IRQ_CONF_STATUS, val); 33212ae7ba3SIoana Ciornei if (err) 33312ae7ba3SIoana Ciornei return err; 334ba2f55b0SHeiner Kallweit 33512ae7ba3SIoana Ciornei err = ip101a_g_ack_interrupt(phydev); 33612ae7ba3SIoana Ciornei } 33712ae7ba3SIoana Ciornei 33812ae7ba3SIoana Ciornei return err; 339ba2f55b0SHeiner Kallweit } 340ba2f55b0SHeiner Kallweit 34125497b7fSIoana Ciornei static irqreturn_t ip101a_g_handle_interrupt(struct phy_device *phydev) 342f7e290fbSMartin Blumenstingl { 34325497b7fSIoana Ciornei int irq_status; 344f7e290fbSMartin Blumenstingl 345f9bc51e6SMichael Walle irq_status = phy_read_paged(phydev, IP101G_DEFAULT_PAGE, 346f9bc51e6SMichael Walle IP101A_G_IRQ_CONF_STATUS); 34725497b7fSIoana Ciornei if (irq_status < 0) { 34825497b7fSIoana Ciornei phy_error(phydev); 34925497b7fSIoana Ciornei return IRQ_NONE; 35025497b7fSIoana Ciornei } 351f7e290fbSMartin Blumenstingl 35225497b7fSIoana Ciornei if (!(irq_status & (IP101A_G_IRQ_SPEED_CHANGE | 353f7e290fbSMartin Blumenstingl IP101A_G_IRQ_DUPLEX_CHANGE | 35425497b7fSIoana Ciornei IP101A_G_IRQ_LINK_CHANGE))) 35525497b7fSIoana Ciornei return IRQ_NONE; 35625497b7fSIoana Ciornei 35725497b7fSIoana Ciornei phy_trigger_machine(phydev); 35825497b7fSIoana Ciornei 35925497b7fSIoana Ciornei return IRQ_HANDLED; 360f7e290fbSMartin Blumenstingl } 361f7e290fbSMartin Blumenstingl 362f9bc51e6SMichael Walle /* The IP101A doesn't really have a page register. We just pretend to have one 363f9bc51e6SMichael Walle * so we can use the paged versions of the callbacks of the IP101G. 364f9bc51e6SMichael Walle */ 365f9bc51e6SMichael Walle static int ip101a_read_page(struct phy_device *phydev) 366f9bc51e6SMichael Walle { 367f9bc51e6SMichael Walle return IP101G_DEFAULT_PAGE; 368f9bc51e6SMichael Walle } 369f9bc51e6SMichael Walle 370f9bc51e6SMichael Walle static int ip101a_write_page(struct phy_device *phydev, int page) 371f9bc51e6SMichael Walle { 372f9bc51e6SMichael Walle WARN_ONCE(page != IP101G_DEFAULT_PAGE, "wrong page selected\n"); 373f9bc51e6SMichael Walle 374f9bc51e6SMichael Walle return 0; 375f9bc51e6SMichael Walle } 376f9bc51e6SMichael Walle 377f9bc51e6SMichael Walle static int ip101g_read_page(struct phy_device *phydev) 378f9bc51e6SMichael Walle { 379f9bc51e6SMichael Walle return __phy_read(phydev, IP101G_PAGE_CONTROL); 380f9bc51e6SMichael Walle } 381f9bc51e6SMichael Walle 382f9bc51e6SMichael Walle static int ip101g_write_page(struct phy_device *phydev, int page) 383f9bc51e6SMichael Walle { 384f9bc51e6SMichael Walle return __phy_write(phydev, IP101G_PAGE_CONTROL, page); 385f9bc51e6SMichael Walle } 386f9bc51e6SMichael Walle 387675115bfSMichael Walle static int ip101a_g_has_page_register(struct phy_device *phydev) 388675115bfSMichael Walle { 389675115bfSMichael Walle int oldval, val, ret; 390675115bfSMichael Walle 391675115bfSMichael Walle oldval = phy_read(phydev, IP101G_PAGE_CONTROL); 392675115bfSMichael Walle if (oldval < 0) 393675115bfSMichael Walle return oldval; 394675115bfSMichael Walle 395675115bfSMichael Walle ret = phy_write(phydev, IP101G_PAGE_CONTROL, 0xffff); 396675115bfSMichael Walle if (ret) 397675115bfSMichael Walle return ret; 398675115bfSMichael Walle 399675115bfSMichael Walle val = phy_read(phydev, IP101G_PAGE_CONTROL); 400675115bfSMichael Walle if (val < 0) 401675115bfSMichael Walle return val; 402675115bfSMichael Walle 403675115bfSMichael Walle ret = phy_write(phydev, IP101G_PAGE_CONTROL, oldval); 404675115bfSMichael Walle if (ret) 405675115bfSMichael Walle return ret; 406675115bfSMichael Walle 407675115bfSMichael Walle return val == IP101G_PAGE_CONTROL_MASK; 408675115bfSMichael Walle } 409675115bfSMichael Walle 410675115bfSMichael Walle static int ip101a_g_match_phy_device(struct phy_device *phydev, bool ip101a) 411675115bfSMichael Walle { 412675115bfSMichael Walle int ret; 413675115bfSMichael Walle 414675115bfSMichael Walle if (phydev->phy_id != IP101A_PHY_ID) 415675115bfSMichael Walle return 0; 416675115bfSMichael Walle 417675115bfSMichael Walle /* The IP101A and the IP101G share the same PHY identifier.The IP101G 418675115bfSMichael Walle * seems to be a successor of the IP101A and implements more functions. 419675115bfSMichael Walle * Amongst other things there is a page select register, which is not 420675115bfSMichael Walle * available on the IP101A. Use this to distinguish these two. 421675115bfSMichael Walle */ 422675115bfSMichael Walle ret = ip101a_g_has_page_register(phydev); 423675115bfSMichael Walle if (ret < 0) 424675115bfSMichael Walle return ret; 425675115bfSMichael Walle 426675115bfSMichael Walle return ip101a == !ret; 427675115bfSMichael Walle } 428675115bfSMichael Walle 429675115bfSMichael Walle static int ip101a_match_phy_device(struct phy_device *phydev) 430675115bfSMichael Walle { 431675115bfSMichael Walle return ip101a_g_match_phy_device(phydev, true); 432675115bfSMichael Walle } 433675115bfSMichael Walle 434675115bfSMichael Walle static int ip101g_match_phy_device(struct phy_device *phydev) 435675115bfSMichael Walle { 436675115bfSMichael Walle return ip101a_g_match_phy_device(phydev, false); 437675115bfSMichael Walle } 438675115bfSMichael Walle 439*a0750d42SMichael Walle static int ip101g_get_sset_count(struct phy_device *phydev) 440*a0750d42SMichael Walle { 441*a0750d42SMichael Walle return ARRAY_SIZE(ip101g_hw_stats); 442*a0750d42SMichael Walle } 443*a0750d42SMichael Walle 444*a0750d42SMichael Walle static void ip101g_get_strings(struct phy_device *phydev, u8 *data) 445*a0750d42SMichael Walle { 446*a0750d42SMichael Walle int i; 447*a0750d42SMichael Walle 448*a0750d42SMichael Walle for (i = 0; i < ARRAY_SIZE(ip101g_hw_stats); i++) 449*a0750d42SMichael Walle strscpy(data + i * ETH_GSTRING_LEN, 450*a0750d42SMichael Walle ip101g_hw_stats[i].name, ETH_GSTRING_LEN); 451*a0750d42SMichael Walle } 452*a0750d42SMichael Walle 453*a0750d42SMichael Walle static u64 ip101g_get_stat(struct phy_device *phydev, int i) 454*a0750d42SMichael Walle { 455*a0750d42SMichael Walle struct ip101g_hw_stat stat = ip101g_hw_stats[i]; 456*a0750d42SMichael Walle struct ip101a_g_phy_priv *priv = phydev->priv; 457*a0750d42SMichael Walle int val; 458*a0750d42SMichael Walle u64 ret; 459*a0750d42SMichael Walle 460*a0750d42SMichael Walle val = phy_read_paged(phydev, stat.page, IP101G_CNT_REG); 461*a0750d42SMichael Walle if (val < 0) { 462*a0750d42SMichael Walle ret = U64_MAX; 463*a0750d42SMichael Walle } else { 464*a0750d42SMichael Walle priv->stats[i] += val; 465*a0750d42SMichael Walle ret = priv->stats[i]; 466*a0750d42SMichael Walle } 467*a0750d42SMichael Walle 468*a0750d42SMichael Walle return ret; 469*a0750d42SMichael Walle } 470*a0750d42SMichael Walle 471*a0750d42SMichael Walle static void ip101g_get_stats(struct phy_device *phydev, 472*a0750d42SMichael Walle struct ethtool_stats *stats, u64 *data) 473*a0750d42SMichael Walle { 474*a0750d42SMichael Walle int i; 475*a0750d42SMichael Walle 476*a0750d42SMichael Walle for (i = 0; i < ARRAY_SIZE(ip101g_hw_stats); i++) 477*a0750d42SMichael Walle data[i] = ip101g_get_stat(phydev, i); 478*a0750d42SMichael Walle } 479*a0750d42SMichael Walle 480d5bf9071SChristian Hohnstaedt static struct phy_driver icplus_driver[] = { 481d5bf9071SChristian Hohnstaedt { 4822ad4758cSMichael Walle PHY_ID_MATCH_MODEL(IP175C_PHY_ID), 4830cefeebaSMichael Barkowski .name = "ICPlus IP175C", 484dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 4858edf206cSMichael Walle .config_init = ip175c_config_init, 4868edf206cSMichael Walle .config_aneg = ip175c_config_aneg, 4878edf206cSMichael Walle .read_status = ip175c_read_status, 488dab10863SGiuseppe Cavallaro .suspend = genphy_suspend, 489dab10863SGiuseppe Cavallaro .resume = genphy_resume, 490d5bf9071SChristian Hohnstaedt }, { 4912ad4758cSMichael Walle PHY_ID_MATCH_MODEL(IP1001_PHY_ID), 492377ecca9SGiuseppe CAVALLARO .name = "ICPlus IP1001", 493dcdecdcfSHeiner Kallweit /* PHY_GBIT_FEATURES */ 4948edf206cSMichael Walle .config_init = ip1001_config_init, 495df22de9aSMichael Walle .soft_reset = genphy_soft_reset, 496377ecca9SGiuseppe CAVALLARO .suspend = genphy_suspend, 497377ecca9SGiuseppe CAVALLARO .resume = genphy_resume, 498d5bf9071SChristian Hohnstaedt }, { 499675115bfSMichael Walle .name = "ICPlus IP101A", 500675115bfSMichael Walle .match_phy_device = ip101a_match_phy_device, 501675115bfSMichael Walle .probe = ip101a_g_probe, 502f9bc51e6SMichael Walle .read_page = ip101a_read_page, 503f9bc51e6SMichael Walle .write_page = ip101a_write_page, 504675115bfSMichael Walle .config_intr = ip101a_g_config_intr, 505675115bfSMichael Walle .handle_interrupt = ip101a_g_handle_interrupt, 506eeac7d43SMichael Walle .config_init = ip101a_config_init, 507675115bfSMichael Walle .soft_reset = genphy_soft_reset, 508675115bfSMichael Walle .suspend = genphy_suspend, 509675115bfSMichael Walle .resume = genphy_resume, 510675115bfSMichael Walle }, { 511675115bfSMichael Walle .name = "ICPlus IP101G", 512675115bfSMichael Walle .match_phy_device = ip101g_match_phy_device, 513f2f1a847SMartin Blumenstingl .probe = ip101a_g_probe, 514f9bc51e6SMichael Walle .read_page = ip101g_read_page, 515f9bc51e6SMichael Walle .write_page = ip101g_write_page, 516ba2f55b0SHeiner Kallweit .config_intr = ip101a_g_config_intr, 51725497b7fSIoana Ciornei .handle_interrupt = ip101a_g_handle_interrupt, 518eeac7d43SMichael Walle .config_init = ip101g_config_init, 519df22de9aSMichael Walle .soft_reset = genphy_soft_reset, 520*a0750d42SMichael Walle .get_sset_count = ip101g_get_sset_count, 521*a0750d42SMichael Walle .get_strings = ip101g_get_strings, 522*a0750d42SMichael Walle .get_stats = ip101g_get_stats, 5239c9b1f24SGiuseppe CAVALLARO .suspend = genphy_suspend, 5249c9b1f24SGiuseppe CAVALLARO .resume = genphy_resume, 525d5bf9071SChristian Hohnstaedt } }; 5269c9b1f24SGiuseppe CAVALLARO 52750fd7150SJohan Hovold module_phy_driver(icplus_driver); 5284e4f10f6SDavid Woodhouse 529cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused icplus_tbl[] = { 5302ad4758cSMichael Walle { PHY_ID_MATCH_MODEL(IP175C_PHY_ID) }, 5312ad4758cSMichael Walle { PHY_ID_MATCH_MODEL(IP1001_PHY_ID) }, 5327360a4deSMichael Walle { PHY_ID_MATCH_EXACT(IP101A_PHY_ID) }, 5334e4f10f6SDavid Woodhouse { } 5344e4f10f6SDavid Woodhouse }; 5354e4f10f6SDavid Woodhouse 5364e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, icplus_tbl); 537