10ca7111aSMatus Ujhelyi /* 20ca7111aSMatus Ujhelyi * drivers/net/phy/at803x.c 30ca7111aSMatus Ujhelyi * 40ca7111aSMatus Ujhelyi * Driver for Atheros 803x PHY 50ca7111aSMatus Ujhelyi * 60ca7111aSMatus Ujhelyi * Author: Matus Ujhelyi <ujhelyi.m@gmail.com> 70ca7111aSMatus Ujhelyi * 80ca7111aSMatus Ujhelyi * This program is free software; you can redistribute it and/or modify it 90ca7111aSMatus Ujhelyi * under the terms of the GNU General Public License as published by the 100ca7111aSMatus Ujhelyi * Free Software Foundation; either version 2 of the License, or (at your 110ca7111aSMatus Ujhelyi * option) any later version. 120ca7111aSMatus Ujhelyi */ 130ca7111aSMatus Ujhelyi 140ca7111aSMatus Ujhelyi #include <linux/phy.h> 150ca7111aSMatus Ujhelyi #include <linux/module.h> 160ca7111aSMatus Ujhelyi #include <linux/string.h> 170ca7111aSMatus Ujhelyi #include <linux/netdevice.h> 180ca7111aSMatus Ujhelyi #include <linux/etherdevice.h> 1913a56b44SDaniel Mack #include <linux/of_gpio.h> 2013a56b44SDaniel Mack #include <linux/gpio/consumer.h> 210ca7111aSMatus Ujhelyi 220ca7111aSMatus Ujhelyi #define AT803X_INTR_ENABLE 0x12 23e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) 24e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) 25e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) 26e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) 27e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) 28e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) 29e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) 30e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) 31e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_WOL BIT(0) 32e6e4a556SMartin Blumenstingl 330ca7111aSMatus Ujhelyi #define AT803X_INTR_STATUS 0x13 34a46bd63bSMartin Blumenstingl 3513a56b44SDaniel Mack #define AT803X_SMART_SPEED 0x14 3613a56b44SDaniel Mack #define AT803X_LED_CONTROL 0x18 37a46bd63bSMartin Blumenstingl 380ca7111aSMatus Ujhelyi #define AT803X_DEVICE_ADDR 0x03 390ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C 400ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B 410ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A 420ca7111aSMatus Ujhelyi #define AT803X_MMD_ACCESS_CONTROL 0x0D 430ca7111aSMatus Ujhelyi #define AT803X_MMD_ACCESS_CONTROL_DATA 0x0E 440ca7111aSMatus Ujhelyi #define AT803X_FUNC_DATA 0x4003 45a46bd63bSMartin Blumenstingl 461ca6d1b1SMugunthan V N #define AT803X_DEBUG_ADDR 0x1D 471ca6d1b1SMugunthan V N #define AT803X_DEBUG_DATA 0x1E 48a46bd63bSMartin Blumenstingl 492e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_0 0x00 502e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) 51a46bd63bSMartin Blumenstingl 522e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_5 0x05 532e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) 540ca7111aSMatus Ujhelyi 5598267311SZefir Kurtisi #define AT803X_REG_CHIP_CONFIG 0x1f 5698267311SZefir Kurtisi #define AT803X_BT_BX_REG_SEL 0x8000 5798267311SZefir Kurtisi 58bd8ca17fSDaniel Mack #define ATH8030_PHY_ID 0x004dd076 59bd8ca17fSDaniel Mack #define ATH8031_PHY_ID 0x004dd074 60bd8ca17fSDaniel Mack #define ATH8035_PHY_ID 0x004dd072 61bd8ca17fSDaniel Mack 620ca7111aSMatus Ujhelyi MODULE_DESCRIPTION("Atheros 803x PHY driver"); 630ca7111aSMatus Ujhelyi MODULE_AUTHOR("Matus Ujhelyi"); 640ca7111aSMatus Ujhelyi MODULE_LICENSE("GPL"); 650ca7111aSMatus Ujhelyi 6613a56b44SDaniel Mack struct at803x_priv { 6713a56b44SDaniel Mack bool phy_reset:1; 6813a56b44SDaniel Mack struct gpio_desc *gpiod_reset; 6913a56b44SDaniel Mack }; 7013a56b44SDaniel Mack 7113a56b44SDaniel Mack struct at803x_context { 7213a56b44SDaniel Mack u16 bmcr; 7313a56b44SDaniel Mack u16 advertise; 7413a56b44SDaniel Mack u16 control1000; 7513a56b44SDaniel Mack u16 int_enable; 7613a56b44SDaniel Mack u16 smart_speed; 7713a56b44SDaniel Mack u16 led_control; 7813a56b44SDaniel Mack }; 7913a56b44SDaniel Mack 802e5f9f28SMartin Blumenstingl static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) 812e5f9f28SMartin Blumenstingl { 822e5f9f28SMartin Blumenstingl int ret; 832e5f9f28SMartin Blumenstingl 842e5f9f28SMartin Blumenstingl ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); 852e5f9f28SMartin Blumenstingl if (ret < 0) 862e5f9f28SMartin Blumenstingl return ret; 872e5f9f28SMartin Blumenstingl 882e5f9f28SMartin Blumenstingl return phy_read(phydev, AT803X_DEBUG_DATA); 892e5f9f28SMartin Blumenstingl } 902e5f9f28SMartin Blumenstingl 912e5f9f28SMartin Blumenstingl static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, 922e5f9f28SMartin Blumenstingl u16 clear, u16 set) 932e5f9f28SMartin Blumenstingl { 942e5f9f28SMartin Blumenstingl u16 val; 952e5f9f28SMartin Blumenstingl int ret; 962e5f9f28SMartin Blumenstingl 972e5f9f28SMartin Blumenstingl ret = at803x_debug_reg_read(phydev, reg); 982e5f9f28SMartin Blumenstingl if (ret < 0) 992e5f9f28SMartin Blumenstingl return ret; 1002e5f9f28SMartin Blumenstingl 1012e5f9f28SMartin Blumenstingl val = ret & 0xffff; 1022e5f9f28SMartin Blumenstingl val &= ~clear; 1032e5f9f28SMartin Blumenstingl val |= set; 1042e5f9f28SMartin Blumenstingl 1052e5f9f28SMartin Blumenstingl return phy_write(phydev, AT803X_DEBUG_DATA, val); 1062e5f9f28SMartin Blumenstingl } 1072e5f9f28SMartin Blumenstingl 1082e5f9f28SMartin Blumenstingl static inline int at803x_enable_rx_delay(struct phy_device *phydev) 1092e5f9f28SMartin Blumenstingl { 1102e5f9f28SMartin Blumenstingl return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0, 1112e5f9f28SMartin Blumenstingl AT803X_DEBUG_RX_CLK_DLY_EN); 1122e5f9f28SMartin Blumenstingl } 1132e5f9f28SMartin Blumenstingl 1142e5f9f28SMartin Blumenstingl static inline int at803x_enable_tx_delay(struct phy_device *phydev) 1152e5f9f28SMartin Blumenstingl { 1162e5f9f28SMartin Blumenstingl return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 0, 1172e5f9f28SMartin Blumenstingl AT803X_DEBUG_TX_CLK_DLY_EN); 1182e5f9f28SMartin Blumenstingl } 1192e5f9f28SMartin Blumenstingl 12013a56b44SDaniel Mack /* save relevant PHY registers to private copy */ 12113a56b44SDaniel Mack static void at803x_context_save(struct phy_device *phydev, 12213a56b44SDaniel Mack struct at803x_context *context) 12313a56b44SDaniel Mack { 12413a56b44SDaniel Mack context->bmcr = phy_read(phydev, MII_BMCR); 12513a56b44SDaniel Mack context->advertise = phy_read(phydev, MII_ADVERTISE); 12613a56b44SDaniel Mack context->control1000 = phy_read(phydev, MII_CTRL1000); 12713a56b44SDaniel Mack context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE); 12813a56b44SDaniel Mack context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED); 12913a56b44SDaniel Mack context->led_control = phy_read(phydev, AT803X_LED_CONTROL); 13013a56b44SDaniel Mack } 13113a56b44SDaniel Mack 13213a56b44SDaniel Mack /* restore relevant PHY registers from private copy */ 13313a56b44SDaniel Mack static void at803x_context_restore(struct phy_device *phydev, 13413a56b44SDaniel Mack const struct at803x_context *context) 13513a56b44SDaniel Mack { 13613a56b44SDaniel Mack phy_write(phydev, MII_BMCR, context->bmcr); 13713a56b44SDaniel Mack phy_write(phydev, MII_ADVERTISE, context->advertise); 13813a56b44SDaniel Mack phy_write(phydev, MII_CTRL1000, context->control1000); 13913a56b44SDaniel Mack phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable); 14013a56b44SDaniel Mack phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed); 14113a56b44SDaniel Mack phy_write(phydev, AT803X_LED_CONTROL, context->led_control); 14213a56b44SDaniel Mack } 14313a56b44SDaniel Mack 144ea13c9eeSMugunthan V N static int at803x_set_wol(struct phy_device *phydev, 145ea13c9eeSMugunthan V N struct ethtool_wolinfo *wol) 1460ca7111aSMatus Ujhelyi { 1470ca7111aSMatus Ujhelyi struct net_device *ndev = phydev->attached_dev; 1480ca7111aSMatus Ujhelyi const u8 *mac; 149ea13c9eeSMugunthan V N int ret; 150ea13c9eeSMugunthan V N u32 value; 1510ca7111aSMatus Ujhelyi unsigned int i, offsets[] = { 1520ca7111aSMatus Ujhelyi AT803X_LOC_MAC_ADDR_32_47_OFFSET, 1530ca7111aSMatus Ujhelyi AT803X_LOC_MAC_ADDR_16_31_OFFSET, 1540ca7111aSMatus Ujhelyi AT803X_LOC_MAC_ADDR_0_15_OFFSET, 1550ca7111aSMatus Ujhelyi }; 1560ca7111aSMatus Ujhelyi 1570ca7111aSMatus Ujhelyi if (!ndev) 158ea13c9eeSMugunthan V N return -ENODEV; 1590ca7111aSMatus Ujhelyi 160ea13c9eeSMugunthan V N if (wol->wolopts & WAKE_MAGIC) { 1610ca7111aSMatus Ujhelyi mac = (const u8 *) ndev->dev_addr; 1620ca7111aSMatus Ujhelyi 1630ca7111aSMatus Ujhelyi if (!is_valid_ether_addr(mac)) 164ea13c9eeSMugunthan V N return -EFAULT; 1650ca7111aSMatus Ujhelyi 1660ca7111aSMatus Ujhelyi for (i = 0; i < 3; i++) { 1670ca7111aSMatus Ujhelyi phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, 1680ca7111aSMatus Ujhelyi AT803X_DEVICE_ADDR); 1690ca7111aSMatus Ujhelyi phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, 1700ca7111aSMatus Ujhelyi offsets[i]); 1710ca7111aSMatus Ujhelyi phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, 1720ca7111aSMatus Ujhelyi AT803X_FUNC_DATA); 1730ca7111aSMatus Ujhelyi phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, 1740ca7111aSMatus Ujhelyi mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); 1750ca7111aSMatus Ujhelyi } 176ea13c9eeSMugunthan V N 177ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_ENABLE); 178e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_WOL; 179ea13c9eeSMugunthan V N ret = phy_write(phydev, AT803X_INTR_ENABLE, value); 180ea13c9eeSMugunthan V N if (ret) 181ea13c9eeSMugunthan V N return ret; 182ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_STATUS); 183ea13c9eeSMugunthan V N } else { 184ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_ENABLE); 185e6e4a556SMartin Blumenstingl value &= (~AT803X_INTR_ENABLE_WOL); 186ea13c9eeSMugunthan V N ret = phy_write(phydev, AT803X_INTR_ENABLE, value); 187ea13c9eeSMugunthan V N if (ret) 188ea13c9eeSMugunthan V N return ret; 189ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_STATUS); 190ea13c9eeSMugunthan V N } 191ea13c9eeSMugunthan V N 192ea13c9eeSMugunthan V N return ret; 193ea13c9eeSMugunthan V N } 194ea13c9eeSMugunthan V N 195ea13c9eeSMugunthan V N static void at803x_get_wol(struct phy_device *phydev, 196ea13c9eeSMugunthan V N struct ethtool_wolinfo *wol) 197ea13c9eeSMugunthan V N { 198ea13c9eeSMugunthan V N u32 value; 199ea13c9eeSMugunthan V N 200ea13c9eeSMugunthan V N wol->supported = WAKE_MAGIC; 201ea13c9eeSMugunthan V N wol->wolopts = 0; 202ea13c9eeSMugunthan V N 203ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_ENABLE); 204e6e4a556SMartin Blumenstingl if (value & AT803X_INTR_ENABLE_WOL) 205ea13c9eeSMugunthan V N wol->wolopts |= WAKE_MAGIC; 2060ca7111aSMatus Ujhelyi } 2070ca7111aSMatus Ujhelyi 2086229ed1fSDaniel Mack static int at803x_suspend(struct phy_device *phydev) 2096229ed1fSDaniel Mack { 2106229ed1fSDaniel Mack int value; 2116229ed1fSDaniel Mack int wol_enabled; 21298267311SZefir Kurtisi int ccr; 2136229ed1fSDaniel Mack 2146229ed1fSDaniel Mack mutex_lock(&phydev->lock); 2156229ed1fSDaniel Mack 2166229ed1fSDaniel Mack value = phy_read(phydev, AT803X_INTR_ENABLE); 217e6e4a556SMartin Blumenstingl wol_enabled = value & AT803X_INTR_ENABLE_WOL; 2186229ed1fSDaniel Mack 2196229ed1fSDaniel Mack value = phy_read(phydev, MII_BMCR); 2206229ed1fSDaniel Mack 2216229ed1fSDaniel Mack if (wol_enabled) 2226229ed1fSDaniel Mack value |= BMCR_ISOLATE; 2236229ed1fSDaniel Mack else 2246229ed1fSDaniel Mack value |= BMCR_PDOWN; 2256229ed1fSDaniel Mack 2266229ed1fSDaniel Mack phy_write(phydev, MII_BMCR, value); 2276229ed1fSDaniel Mack 22898267311SZefir Kurtisi if (phydev->interface != PHY_INTERFACE_MODE_SGMII) 22998267311SZefir Kurtisi goto done; 23098267311SZefir Kurtisi 23198267311SZefir Kurtisi /* also power-down SGMII interface */ 23298267311SZefir Kurtisi ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); 23398267311SZefir Kurtisi phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL); 23498267311SZefir Kurtisi phy_write(phydev, MII_BMCR, phy_read(phydev, MII_BMCR) | BMCR_PDOWN); 23598267311SZefir Kurtisi phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL); 23698267311SZefir Kurtisi 23798267311SZefir Kurtisi done: 2386229ed1fSDaniel Mack mutex_unlock(&phydev->lock); 2396229ed1fSDaniel Mack 2406229ed1fSDaniel Mack return 0; 2416229ed1fSDaniel Mack } 2426229ed1fSDaniel Mack 2436229ed1fSDaniel Mack static int at803x_resume(struct phy_device *phydev) 2446229ed1fSDaniel Mack { 2456229ed1fSDaniel Mack int value; 24698267311SZefir Kurtisi int ccr; 2476229ed1fSDaniel Mack 2486229ed1fSDaniel Mack mutex_lock(&phydev->lock); 2496229ed1fSDaniel Mack 2506229ed1fSDaniel Mack value = phy_read(phydev, MII_BMCR); 2516229ed1fSDaniel Mack value &= ~(BMCR_PDOWN | BMCR_ISOLATE); 2526229ed1fSDaniel Mack phy_write(phydev, MII_BMCR, value); 2536229ed1fSDaniel Mack 25498267311SZefir Kurtisi if (phydev->interface != PHY_INTERFACE_MODE_SGMII) 25598267311SZefir Kurtisi goto done; 25698267311SZefir Kurtisi 25798267311SZefir Kurtisi /* also power-up SGMII interface */ 25898267311SZefir Kurtisi ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); 25998267311SZefir Kurtisi phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL); 26098267311SZefir Kurtisi value = phy_read(phydev, MII_BMCR) & ~(BMCR_PDOWN | BMCR_ISOLATE); 26198267311SZefir Kurtisi phy_write(phydev, MII_BMCR, value); 26298267311SZefir Kurtisi phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL); 26398267311SZefir Kurtisi 26498267311SZefir Kurtisi done: 2656229ed1fSDaniel Mack mutex_unlock(&phydev->lock); 2666229ed1fSDaniel Mack 2676229ed1fSDaniel Mack return 0; 2686229ed1fSDaniel Mack } 2696229ed1fSDaniel Mack 27013a56b44SDaniel Mack static int at803x_probe(struct phy_device *phydev) 27113a56b44SDaniel Mack { 272e5a03bfdSAndrew Lunn struct device *dev = &phydev->mdio.dev; 27313a56b44SDaniel Mack struct at803x_priv *priv; 274687908c2SUwe Kleine-König struct gpio_desc *gpiod_reset; 27513a56b44SDaniel Mack 2768f2877caSFengguang Wu priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 27713a56b44SDaniel Mack if (!priv) 27813a56b44SDaniel Mack return -ENOMEM; 27913a56b44SDaniel Mack 2809eb13f65SSebastian Frias if (phydev->drv->phy_id != ATH8030_PHY_ID) 2819eb13f65SSebastian Frias goto does_not_require_reset_workaround; 2829eb13f65SSebastian Frias 283d57019d1SSergei Shtylyov gpiod_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 284687908c2SUwe Kleine-König if (IS_ERR(gpiod_reset)) 285687908c2SUwe Kleine-König return PTR_ERR(gpiod_reset); 286687908c2SUwe Kleine-König 287687908c2SUwe Kleine-König priv->gpiod_reset = gpiod_reset; 28813a56b44SDaniel Mack 2899eb13f65SSebastian Frias does_not_require_reset_workaround: 29013a56b44SDaniel Mack phydev->priv = priv; 29113a56b44SDaniel Mack 29213a56b44SDaniel Mack return 0; 29313a56b44SDaniel Mack } 29413a56b44SDaniel Mack 2950ca7111aSMatus Ujhelyi static int at803x_config_init(struct phy_device *phydev) 2960ca7111aSMatus Ujhelyi { 2971ca6d1b1SMugunthan V N int ret; 2980ca7111aSMatus Ujhelyi 2996ff01dbbSDaniel Mack ret = genphy_config_init(phydev); 3006ff01dbbSDaniel Mack if (ret < 0) 3016ff01dbbSDaniel Mack return ret; 3020ca7111aSMatus Ujhelyi 3032e5f9f28SMartin Blumenstingl if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID || 3042e5f9f28SMartin Blumenstingl phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { 3052e5f9f28SMartin Blumenstingl ret = at803x_enable_rx_delay(phydev); 3062e5f9f28SMartin Blumenstingl if (ret < 0) 3071ca6d1b1SMugunthan V N return ret; 3082e5f9f28SMartin Blumenstingl } 3092e5f9f28SMartin Blumenstingl 3102e5f9f28SMartin Blumenstingl if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID || 3112e5f9f28SMartin Blumenstingl phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { 3122e5f9f28SMartin Blumenstingl ret = at803x_enable_tx_delay(phydev); 3132e5f9f28SMartin Blumenstingl if (ret < 0) 3141ca6d1b1SMugunthan V N return ret; 3151ca6d1b1SMugunthan V N } 3161ca6d1b1SMugunthan V N 3170ca7111aSMatus Ujhelyi return 0; 3180ca7111aSMatus Ujhelyi } 3190ca7111aSMatus Ujhelyi 32077a99394SZhao Qiang static int at803x_ack_interrupt(struct phy_device *phydev) 32177a99394SZhao Qiang { 32277a99394SZhao Qiang int err; 32377a99394SZhao Qiang 324a46bd63bSMartin Blumenstingl err = phy_read(phydev, AT803X_INTR_STATUS); 32577a99394SZhao Qiang 32677a99394SZhao Qiang return (err < 0) ? err : 0; 32777a99394SZhao Qiang } 32877a99394SZhao Qiang 32977a99394SZhao Qiang static int at803x_config_intr(struct phy_device *phydev) 33077a99394SZhao Qiang { 33177a99394SZhao Qiang int err; 33277a99394SZhao Qiang int value; 33377a99394SZhao Qiang 334a46bd63bSMartin Blumenstingl value = phy_read(phydev, AT803X_INTR_ENABLE); 33577a99394SZhao Qiang 336e6e4a556SMartin Blumenstingl if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 337e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_AUTONEG_ERR; 338e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_SPEED_CHANGED; 339e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; 340e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_LINK_FAIL; 341e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_LINK_SUCCESS; 342e6e4a556SMartin Blumenstingl 343e6e4a556SMartin Blumenstingl err = phy_write(phydev, AT803X_INTR_ENABLE, value); 344e6e4a556SMartin Blumenstingl } 34577a99394SZhao Qiang else 346a46bd63bSMartin Blumenstingl err = phy_write(phydev, AT803X_INTR_ENABLE, 0); 34777a99394SZhao Qiang 34877a99394SZhao Qiang return err; 34977a99394SZhao Qiang } 35077a99394SZhao Qiang 35113a56b44SDaniel Mack static void at803x_link_change_notify(struct phy_device *phydev) 35213a56b44SDaniel Mack { 35313a56b44SDaniel Mack struct at803x_priv *priv = phydev->priv; 35413a56b44SDaniel Mack 35513a56b44SDaniel Mack /* 35613a56b44SDaniel Mack * Conduct a hardware reset for AT8030 every time a link loss is 35713a56b44SDaniel Mack * signalled. This is necessary to circumvent a hardware bug that 35813a56b44SDaniel Mack * occurs when the cable is unplugged while TX packets are pending 35913a56b44SDaniel Mack * in the FIFO. In such cases, the FIFO enters an error mode it 36013a56b44SDaniel Mack * cannot recover from by software. 36113a56b44SDaniel Mack */ 36213a56b44SDaniel Mack if (phydev->drv->phy_id == ATH8030_PHY_ID) { 36313a56b44SDaniel Mack if (phydev->state == PHY_NOLINK) { 36413a56b44SDaniel Mack if (priv->gpiod_reset && !priv->phy_reset) { 36513a56b44SDaniel Mack struct at803x_context context; 36613a56b44SDaniel Mack 36713a56b44SDaniel Mack at803x_context_save(phydev, &context); 36813a56b44SDaniel Mack 36913a56b44SDaniel Mack gpiod_set_value(priv->gpiod_reset, 1); 37013a56b44SDaniel Mack msleep(1); 371d57019d1SSergei Shtylyov gpiod_set_value(priv->gpiod_reset, 0); 372d57019d1SSergei Shtylyov msleep(1); 37313a56b44SDaniel Mack 37413a56b44SDaniel Mack at803x_context_restore(phydev, &context); 37513a56b44SDaniel Mack 37672ba48beSAndrew Lunn phydev_dbg(phydev, "%s(): phy was reset\n", 37713a56b44SDaniel Mack __func__); 37813a56b44SDaniel Mack priv->phy_reset = true; 37913a56b44SDaniel Mack } 38013a56b44SDaniel Mack } else { 38113a56b44SDaniel Mack priv->phy_reset = false; 38213a56b44SDaniel Mack } 38313a56b44SDaniel Mack } 38413a56b44SDaniel Mack } 38513a56b44SDaniel Mack 386317420abSMugunthan V N static struct phy_driver at803x_driver[] = { 387317420abSMugunthan V N { 3880ca7111aSMatus Ujhelyi /* ATHEROS 8035 */ 389bd8ca17fSDaniel Mack .phy_id = ATH8035_PHY_ID, 3900ca7111aSMatus Ujhelyi .name = "Atheros 8035 ethernet", 3910ca7111aSMatus Ujhelyi .phy_id_mask = 0xffffffef, 39213a56b44SDaniel Mack .probe = at803x_probe, 3930ca7111aSMatus Ujhelyi .config_init = at803x_config_init, 39413a56b44SDaniel Mack .link_change_notify = at803x_link_change_notify, 395ea13c9eeSMugunthan V N .set_wol = at803x_set_wol, 396ea13c9eeSMugunthan V N .get_wol = at803x_get_wol, 3976229ed1fSDaniel Mack .suspend = at803x_suspend, 3986229ed1fSDaniel Mack .resume = at803x_resume, 3990ca7111aSMatus Ujhelyi .features = PHY_GBIT_FEATURES, 4000ca7111aSMatus Ujhelyi .flags = PHY_HAS_INTERRUPT, 4010197ffedSDaniel Mack .config_aneg = genphy_config_aneg, 4020197ffedSDaniel Mack .read_status = genphy_read_status, 4030eae5982SMåns Rullgård .ack_interrupt = at803x_ack_interrupt, 4040eae5982SMåns Rullgård .config_intr = at803x_config_intr, 405317420abSMugunthan V N }, { 4060ca7111aSMatus Ujhelyi /* ATHEROS 8030 */ 407bd8ca17fSDaniel Mack .phy_id = ATH8030_PHY_ID, 4080ca7111aSMatus Ujhelyi .name = "Atheros 8030 ethernet", 4090ca7111aSMatus Ujhelyi .phy_id_mask = 0xffffffef, 41013a56b44SDaniel Mack .probe = at803x_probe, 4110ca7111aSMatus Ujhelyi .config_init = at803x_config_init, 41213a56b44SDaniel Mack .link_change_notify = at803x_link_change_notify, 413ea13c9eeSMugunthan V N .set_wol = at803x_set_wol, 414ea13c9eeSMugunthan V N .get_wol = at803x_get_wol, 4156229ed1fSDaniel Mack .suspend = at803x_suspend, 4166229ed1fSDaniel Mack .resume = at803x_resume, 417e15bb4c6SMartin Blumenstingl .features = PHY_BASIC_FEATURES, 4180ca7111aSMatus Ujhelyi .flags = PHY_HAS_INTERRUPT, 4190197ffedSDaniel Mack .config_aneg = genphy_config_aneg, 4200197ffedSDaniel Mack .read_status = genphy_read_status, 4210eae5982SMåns Rullgård .ack_interrupt = at803x_ack_interrupt, 4220eae5982SMåns Rullgård .config_intr = at803x_config_intr, 42305d7cce8SMugunthan V N }, { 42405d7cce8SMugunthan V N /* ATHEROS 8031 */ 425bd8ca17fSDaniel Mack .phy_id = ATH8031_PHY_ID, 42605d7cce8SMugunthan V N .name = "Atheros 8031 ethernet", 42705d7cce8SMugunthan V N .phy_id_mask = 0xffffffef, 42813a56b44SDaniel Mack .probe = at803x_probe, 42905d7cce8SMugunthan V N .config_init = at803x_config_init, 43013a56b44SDaniel Mack .link_change_notify = at803x_link_change_notify, 43105d7cce8SMugunthan V N .set_wol = at803x_set_wol, 43205d7cce8SMugunthan V N .get_wol = at803x_get_wol, 4336229ed1fSDaniel Mack .suspend = at803x_suspend, 4346229ed1fSDaniel Mack .resume = at803x_resume, 43505d7cce8SMugunthan V N .features = PHY_GBIT_FEATURES, 43605d7cce8SMugunthan V N .flags = PHY_HAS_INTERRUPT, 4370197ffedSDaniel Mack .config_aneg = genphy_config_aneg, 4380197ffedSDaniel Mack .read_status = genphy_read_status, 43977a99394SZhao Qiang .ack_interrupt = &at803x_ack_interrupt, 44077a99394SZhao Qiang .config_intr = &at803x_config_intr, 441317420abSMugunthan V N } }; 4420ca7111aSMatus Ujhelyi 44350fd7150SJohan Hovold module_phy_driver(at803x_driver); 4440ca7111aSMatus Ujhelyi 4450ca7111aSMatus Ujhelyi static struct mdio_device_id __maybe_unused atheros_tbl[] = { 446bd8ca17fSDaniel Mack { ATH8030_PHY_ID, 0xffffffef }, 447bd8ca17fSDaniel Mack { ATH8031_PHY_ID, 0xffffffef }, 448bd8ca17fSDaniel Mack { ATH8035_PHY_ID, 0xffffffef }, 4490ca7111aSMatus Ujhelyi { } 4500ca7111aSMatus Ujhelyi }; 4510ca7111aSMatus Ujhelyi 4520ca7111aSMatus Ujhelyi MODULE_DEVICE_TABLE(mdio, atheros_tbl); 453