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 45f62265b5SZefir Kurtisi #define AT803X_REG_CHIP_CONFIG 0x1f 46f62265b5SZefir Kurtisi #define AT803X_BT_BX_REG_SEL 0x8000 47a46bd63bSMartin Blumenstingl 481ca6d1b1SMugunthan V N #define AT803X_DEBUG_ADDR 0x1D 491ca6d1b1SMugunthan V N #define AT803X_DEBUG_DATA 0x1E 50a46bd63bSMartin Blumenstingl 51f62265b5SZefir Kurtisi #define AT803X_MODE_CFG_MASK 0x0F 52f62265b5SZefir Kurtisi #define AT803X_MODE_CFG_SGMII 0x01 53f62265b5SZefir Kurtisi 54f62265b5SZefir Kurtisi #define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ 55f62265b5SZefir Kurtisi #define AT803X_PSSR_MR_AN_COMPLETE 0x0200 56f62265b5SZefir Kurtisi 572e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_0 0x00 582e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) 59a46bd63bSMartin Blumenstingl 602e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_5 0x05 612e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) 620ca7111aSMatus Ujhelyi 63bd8ca17fSDaniel Mack #define ATH8030_PHY_ID 0x004dd076 64bd8ca17fSDaniel Mack #define ATH8031_PHY_ID 0x004dd074 65bd8ca17fSDaniel Mack #define ATH8035_PHY_ID 0x004dd072 6658effd71SFabio Estevam #define AT803X_PHY_ID_MASK 0xffffffef 67bd8ca17fSDaniel Mack 680ca7111aSMatus Ujhelyi MODULE_DESCRIPTION("Atheros 803x PHY driver"); 690ca7111aSMatus Ujhelyi MODULE_AUTHOR("Matus Ujhelyi"); 700ca7111aSMatus Ujhelyi MODULE_LICENSE("GPL"); 710ca7111aSMatus Ujhelyi 7213a56b44SDaniel Mack struct at803x_priv { 7313a56b44SDaniel Mack bool phy_reset:1; 7413a56b44SDaniel Mack }; 7513a56b44SDaniel Mack 7613a56b44SDaniel Mack struct at803x_context { 7713a56b44SDaniel Mack u16 bmcr; 7813a56b44SDaniel Mack u16 advertise; 7913a56b44SDaniel Mack u16 control1000; 8013a56b44SDaniel Mack u16 int_enable; 8113a56b44SDaniel Mack u16 smart_speed; 8213a56b44SDaniel Mack u16 led_control; 8313a56b44SDaniel Mack }; 8413a56b44SDaniel Mack 852e5f9f28SMartin Blumenstingl static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) 862e5f9f28SMartin Blumenstingl { 872e5f9f28SMartin Blumenstingl int ret; 882e5f9f28SMartin Blumenstingl 892e5f9f28SMartin Blumenstingl ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); 902e5f9f28SMartin Blumenstingl if (ret < 0) 912e5f9f28SMartin Blumenstingl return ret; 922e5f9f28SMartin Blumenstingl 932e5f9f28SMartin Blumenstingl return phy_read(phydev, AT803X_DEBUG_DATA); 942e5f9f28SMartin Blumenstingl } 952e5f9f28SMartin Blumenstingl 962e5f9f28SMartin Blumenstingl static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, 972e5f9f28SMartin Blumenstingl u16 clear, u16 set) 982e5f9f28SMartin Blumenstingl { 992e5f9f28SMartin Blumenstingl u16 val; 1002e5f9f28SMartin Blumenstingl int ret; 1012e5f9f28SMartin Blumenstingl 1022e5f9f28SMartin Blumenstingl ret = at803x_debug_reg_read(phydev, reg); 1032e5f9f28SMartin Blumenstingl if (ret < 0) 1042e5f9f28SMartin Blumenstingl return ret; 1052e5f9f28SMartin Blumenstingl 1062e5f9f28SMartin Blumenstingl val = ret & 0xffff; 1072e5f9f28SMartin Blumenstingl val &= ~clear; 1082e5f9f28SMartin Blumenstingl val |= set; 1092e5f9f28SMartin Blumenstingl 1102e5f9f28SMartin Blumenstingl return phy_write(phydev, AT803X_DEBUG_DATA, val); 1112e5f9f28SMartin Blumenstingl } 1122e5f9f28SMartin Blumenstingl 113cd28d1d6SVinod Koul static inline int at803x_disable_rx_delay(struct phy_device *phydev) 1142e5f9f28SMartin Blumenstingl { 115cd28d1d6SVinod Koul return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 116cd28d1d6SVinod Koul AT803X_DEBUG_RX_CLK_DLY_EN, 0); 1172e5f9f28SMartin Blumenstingl } 1182e5f9f28SMartin Blumenstingl 119cd28d1d6SVinod Koul static inline int at803x_disable_tx_delay(struct phy_device *phydev) 1202e5f9f28SMartin Blumenstingl { 121cd28d1d6SVinod Koul return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 122cd28d1d6SVinod Koul AT803X_DEBUG_TX_CLK_DLY_EN, 0); 1232e5f9f28SMartin Blumenstingl } 1242e5f9f28SMartin Blumenstingl 12513a56b44SDaniel Mack /* save relevant PHY registers to private copy */ 12613a56b44SDaniel Mack static void at803x_context_save(struct phy_device *phydev, 12713a56b44SDaniel Mack struct at803x_context *context) 12813a56b44SDaniel Mack { 12913a56b44SDaniel Mack context->bmcr = phy_read(phydev, MII_BMCR); 13013a56b44SDaniel Mack context->advertise = phy_read(phydev, MII_ADVERTISE); 13113a56b44SDaniel Mack context->control1000 = phy_read(phydev, MII_CTRL1000); 13213a56b44SDaniel Mack context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE); 13313a56b44SDaniel Mack context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED); 13413a56b44SDaniel Mack context->led_control = phy_read(phydev, AT803X_LED_CONTROL); 13513a56b44SDaniel Mack } 13613a56b44SDaniel Mack 13713a56b44SDaniel Mack /* restore relevant PHY registers from private copy */ 13813a56b44SDaniel Mack static void at803x_context_restore(struct phy_device *phydev, 13913a56b44SDaniel Mack const struct at803x_context *context) 14013a56b44SDaniel Mack { 14113a56b44SDaniel Mack phy_write(phydev, MII_BMCR, context->bmcr); 14213a56b44SDaniel Mack phy_write(phydev, MII_ADVERTISE, context->advertise); 14313a56b44SDaniel Mack phy_write(phydev, MII_CTRL1000, context->control1000); 14413a56b44SDaniel Mack phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable); 14513a56b44SDaniel Mack phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed); 14613a56b44SDaniel Mack phy_write(phydev, AT803X_LED_CONTROL, context->led_control); 14713a56b44SDaniel Mack } 14813a56b44SDaniel Mack 149ea13c9eeSMugunthan V N static int at803x_set_wol(struct phy_device *phydev, 150ea13c9eeSMugunthan V N struct ethtool_wolinfo *wol) 1510ca7111aSMatus Ujhelyi { 1520ca7111aSMatus Ujhelyi struct net_device *ndev = phydev->attached_dev; 1530ca7111aSMatus Ujhelyi const u8 *mac; 154ea13c9eeSMugunthan V N int ret; 155ea13c9eeSMugunthan V N u32 value; 1560ca7111aSMatus Ujhelyi unsigned int i, offsets[] = { 1570ca7111aSMatus Ujhelyi AT803X_LOC_MAC_ADDR_32_47_OFFSET, 1580ca7111aSMatus Ujhelyi AT803X_LOC_MAC_ADDR_16_31_OFFSET, 1590ca7111aSMatus Ujhelyi AT803X_LOC_MAC_ADDR_0_15_OFFSET, 1600ca7111aSMatus Ujhelyi }; 1610ca7111aSMatus Ujhelyi 1620ca7111aSMatus Ujhelyi if (!ndev) 163ea13c9eeSMugunthan V N return -ENODEV; 1640ca7111aSMatus Ujhelyi 165ea13c9eeSMugunthan V N if (wol->wolopts & WAKE_MAGIC) { 1660ca7111aSMatus Ujhelyi mac = (const u8 *) ndev->dev_addr; 1670ca7111aSMatus Ujhelyi 1680ca7111aSMatus Ujhelyi if (!is_valid_ether_addr(mac)) 169fc755687SDan Murphy return -EINVAL; 1700ca7111aSMatus Ujhelyi 1710ca7111aSMatus Ujhelyi for (i = 0; i < 3; i++) { 1720ca7111aSMatus Ujhelyi phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, 1730ca7111aSMatus Ujhelyi AT803X_DEVICE_ADDR); 1740ca7111aSMatus Ujhelyi phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, 1750ca7111aSMatus Ujhelyi offsets[i]); 1760ca7111aSMatus Ujhelyi phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, 1770ca7111aSMatus Ujhelyi AT803X_FUNC_DATA); 1780ca7111aSMatus Ujhelyi phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, 1790ca7111aSMatus Ujhelyi mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); 1800ca7111aSMatus Ujhelyi } 181ea13c9eeSMugunthan V N 182ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_ENABLE); 183e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_WOL; 184ea13c9eeSMugunthan V N ret = phy_write(phydev, AT803X_INTR_ENABLE, value); 185ea13c9eeSMugunthan V N if (ret) 186ea13c9eeSMugunthan V N return ret; 187ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_STATUS); 188ea13c9eeSMugunthan V N } else { 189ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_ENABLE); 190e6e4a556SMartin Blumenstingl value &= (~AT803X_INTR_ENABLE_WOL); 191ea13c9eeSMugunthan V N ret = phy_write(phydev, AT803X_INTR_ENABLE, value); 192ea13c9eeSMugunthan V N if (ret) 193ea13c9eeSMugunthan V N return ret; 194ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_STATUS); 195ea13c9eeSMugunthan V N } 196ea13c9eeSMugunthan V N 197ea13c9eeSMugunthan V N return ret; 198ea13c9eeSMugunthan V N } 199ea13c9eeSMugunthan V N 200ea13c9eeSMugunthan V N static void at803x_get_wol(struct phy_device *phydev, 201ea13c9eeSMugunthan V N struct ethtool_wolinfo *wol) 202ea13c9eeSMugunthan V N { 203ea13c9eeSMugunthan V N u32 value; 204ea13c9eeSMugunthan V N 205ea13c9eeSMugunthan V N wol->supported = WAKE_MAGIC; 206ea13c9eeSMugunthan V N wol->wolopts = 0; 207ea13c9eeSMugunthan V N 208ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_ENABLE); 209e6e4a556SMartin Blumenstingl if (value & AT803X_INTR_ENABLE_WOL) 210ea13c9eeSMugunthan V N wol->wolopts |= WAKE_MAGIC; 2110ca7111aSMatus Ujhelyi } 2120ca7111aSMatus Ujhelyi 2136229ed1fSDaniel Mack static int at803x_suspend(struct phy_device *phydev) 2146229ed1fSDaniel Mack { 2156229ed1fSDaniel Mack int value; 2166229ed1fSDaniel Mack int wol_enabled; 2176229ed1fSDaniel Mack 2186229ed1fSDaniel Mack value = phy_read(phydev, AT803X_INTR_ENABLE); 219e6e4a556SMartin Blumenstingl wol_enabled = value & AT803X_INTR_ENABLE_WOL; 2206229ed1fSDaniel Mack 2216229ed1fSDaniel Mack if (wol_enabled) 222fea23fb5SRussell King value = BMCR_ISOLATE; 2236229ed1fSDaniel Mack else 224fea23fb5SRussell King value = BMCR_PDOWN; 2256229ed1fSDaniel Mack 226fea23fb5SRussell King phy_modify(phydev, MII_BMCR, 0, value); 2276229ed1fSDaniel Mack 2286229ed1fSDaniel Mack return 0; 2296229ed1fSDaniel Mack } 2306229ed1fSDaniel Mack 2316229ed1fSDaniel Mack static int at803x_resume(struct phy_device *phydev) 2326229ed1fSDaniel Mack { 233f102852fSRussell King return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); 2346229ed1fSDaniel Mack } 2356229ed1fSDaniel Mack 23613a56b44SDaniel Mack static int at803x_probe(struct phy_device *phydev) 23713a56b44SDaniel Mack { 238e5a03bfdSAndrew Lunn struct device *dev = &phydev->mdio.dev; 23913a56b44SDaniel Mack struct at803x_priv *priv; 24013a56b44SDaniel Mack 2418f2877caSFengguang Wu priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 24213a56b44SDaniel Mack if (!priv) 24313a56b44SDaniel Mack return -ENOMEM; 24413a56b44SDaniel Mack 24513a56b44SDaniel Mack phydev->priv = priv; 24613a56b44SDaniel Mack 24713a56b44SDaniel Mack return 0; 24813a56b44SDaniel Mack } 24913a56b44SDaniel Mack 2500ca7111aSMatus Ujhelyi static int at803x_config_init(struct phy_device *phydev) 2510ca7111aSMatus Ujhelyi { 2521ca6d1b1SMugunthan V N int ret; 2530ca7111aSMatus Ujhelyi 2546ff01dbbSDaniel Mack ret = genphy_config_init(phydev); 2556ff01dbbSDaniel Mack if (ret < 0) 2566ff01dbbSDaniel Mack return ret; 2570ca7111aSMatus Ujhelyi 2582e5f9f28SMartin Blumenstingl if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID || 259cd28d1d6SVinod Koul phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || 260cd28d1d6SVinod Koul phydev->interface == PHY_INTERFACE_MODE_RGMII) { 261cd28d1d6SVinod Koul ret = at803x_disable_rx_delay(phydev); 2622e5f9f28SMartin Blumenstingl if (ret < 0) 2631ca6d1b1SMugunthan V N return ret; 2642e5f9f28SMartin Blumenstingl } 2652e5f9f28SMartin Blumenstingl 2662e5f9f28SMartin Blumenstingl if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID || 267cd28d1d6SVinod Koul phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || 268cd28d1d6SVinod Koul phydev->interface == PHY_INTERFACE_MODE_RGMII) { 269cd28d1d6SVinod Koul ret = at803x_disable_tx_delay(phydev); 2702e5f9f28SMartin Blumenstingl if (ret < 0) 2711ca6d1b1SMugunthan V N return ret; 2721ca6d1b1SMugunthan V N } 2731ca6d1b1SMugunthan V N 2740ca7111aSMatus Ujhelyi return 0; 2750ca7111aSMatus Ujhelyi } 2760ca7111aSMatus Ujhelyi 27777a99394SZhao Qiang static int at803x_ack_interrupt(struct phy_device *phydev) 27877a99394SZhao Qiang { 27977a99394SZhao Qiang int err; 28077a99394SZhao Qiang 281a46bd63bSMartin Blumenstingl err = phy_read(phydev, AT803X_INTR_STATUS); 28277a99394SZhao Qiang 28377a99394SZhao Qiang return (err < 0) ? err : 0; 28477a99394SZhao Qiang } 28577a99394SZhao Qiang 28677a99394SZhao Qiang static int at803x_config_intr(struct phy_device *phydev) 28777a99394SZhao Qiang { 28877a99394SZhao Qiang int err; 28977a99394SZhao Qiang int value; 29077a99394SZhao Qiang 291a46bd63bSMartin Blumenstingl value = phy_read(phydev, AT803X_INTR_ENABLE); 29277a99394SZhao Qiang 293e6e4a556SMartin Blumenstingl if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 294e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_AUTONEG_ERR; 295e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_SPEED_CHANGED; 296e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; 297e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_LINK_FAIL; 298e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_LINK_SUCCESS; 299e6e4a556SMartin Blumenstingl 300e6e4a556SMartin Blumenstingl err = phy_write(phydev, AT803X_INTR_ENABLE, value); 301e6e4a556SMartin Blumenstingl } 30277a99394SZhao Qiang else 303a46bd63bSMartin Blumenstingl err = phy_write(phydev, AT803X_INTR_ENABLE, 0); 30477a99394SZhao Qiang 30577a99394SZhao Qiang return err; 30677a99394SZhao Qiang } 30777a99394SZhao Qiang 30813a56b44SDaniel Mack static void at803x_link_change_notify(struct phy_device *phydev) 30913a56b44SDaniel Mack { 31013a56b44SDaniel Mack struct at803x_priv *priv = phydev->priv; 31113a56b44SDaniel Mack 31213a56b44SDaniel Mack /* 31313a56b44SDaniel Mack * Conduct a hardware reset for AT8030 every time a link loss is 31413a56b44SDaniel Mack * signalled. This is necessary to circumvent a hardware bug that 31513a56b44SDaniel Mack * occurs when the cable is unplugged while TX packets are pending 31613a56b44SDaniel Mack * in the FIFO. In such cases, the FIFO enters an error mode it 31713a56b44SDaniel Mack * cannot recover from by software. 31813a56b44SDaniel Mack */ 31913a56b44SDaniel Mack if (phydev->state == PHY_NOLINK) { 320bafbdd52SSergei Shtylyov if (phydev->mdio.reset && !priv->phy_reset) { 32113a56b44SDaniel Mack struct at803x_context context; 32213a56b44SDaniel Mack 32313a56b44SDaniel Mack at803x_context_save(phydev, &context); 32413a56b44SDaniel Mack 325bafbdd52SSergei Shtylyov phy_device_reset(phydev, 1); 32613a56b44SDaniel Mack msleep(1); 327bafbdd52SSergei Shtylyov phy_device_reset(phydev, 0); 328d57019d1SSergei Shtylyov msleep(1); 32913a56b44SDaniel Mack 33013a56b44SDaniel Mack at803x_context_restore(phydev, &context); 33113a56b44SDaniel Mack 33272ba48beSAndrew Lunn phydev_dbg(phydev, "%s(): phy was reset\n", 33313a56b44SDaniel Mack __func__); 33413a56b44SDaniel Mack priv->phy_reset = true; 33513a56b44SDaniel Mack } 33613a56b44SDaniel Mack } else { 33713a56b44SDaniel Mack priv->phy_reset = false; 33813a56b44SDaniel Mack } 33913a56b44SDaniel Mack } 34013a56b44SDaniel Mack 341f62265b5SZefir Kurtisi static int at803x_aneg_done(struct phy_device *phydev) 342f62265b5SZefir Kurtisi { 343f62265b5SZefir Kurtisi int ccr; 344f62265b5SZefir Kurtisi 345f62265b5SZefir Kurtisi int aneg_done = genphy_aneg_done(phydev); 346f62265b5SZefir Kurtisi if (aneg_done != BMSR_ANEGCOMPLETE) 347f62265b5SZefir Kurtisi return aneg_done; 348f62265b5SZefir Kurtisi 349f62265b5SZefir Kurtisi /* 350f62265b5SZefir Kurtisi * in SGMII mode, if copper side autoneg is successful, 351f62265b5SZefir Kurtisi * also check SGMII side autoneg result 352f62265b5SZefir Kurtisi */ 353f62265b5SZefir Kurtisi ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); 354f62265b5SZefir Kurtisi if ((ccr & AT803X_MODE_CFG_MASK) != AT803X_MODE_CFG_SGMII) 355f62265b5SZefir Kurtisi return aneg_done; 356f62265b5SZefir Kurtisi 357f62265b5SZefir Kurtisi /* switch to SGMII/fiber page */ 358f62265b5SZefir Kurtisi phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL); 359f62265b5SZefir Kurtisi 360f62265b5SZefir Kurtisi /* check if the SGMII link is OK. */ 361f62265b5SZefir Kurtisi if (!(phy_read(phydev, AT803X_PSSR) & AT803X_PSSR_MR_AN_COMPLETE)) { 362ab2a605fSAndrew Lunn phydev_warn(phydev, "803x_aneg_done: SGMII link is not ok\n"); 363f62265b5SZefir Kurtisi aneg_done = 0; 364f62265b5SZefir Kurtisi } 365f62265b5SZefir Kurtisi /* switch back to copper page */ 366f62265b5SZefir Kurtisi phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL); 367f62265b5SZefir Kurtisi 368f62265b5SZefir Kurtisi return aneg_done; 369f62265b5SZefir Kurtisi } 370f62265b5SZefir Kurtisi 371317420abSMugunthan V N static struct phy_driver at803x_driver[] = { 372317420abSMugunthan V N { 3730ca7111aSMatus Ujhelyi /* ATHEROS 8035 */ 374bd8ca17fSDaniel Mack .phy_id = ATH8035_PHY_ID, 3750ca7111aSMatus Ujhelyi .name = "Atheros 8035 ethernet", 37658effd71SFabio Estevam .phy_id_mask = AT803X_PHY_ID_MASK, 37713a56b44SDaniel Mack .probe = at803x_probe, 3780ca7111aSMatus Ujhelyi .config_init = at803x_config_init, 379ea13c9eeSMugunthan V N .set_wol = at803x_set_wol, 380ea13c9eeSMugunthan V N .get_wol = at803x_get_wol, 3816229ed1fSDaniel Mack .suspend = at803x_suspend, 3826229ed1fSDaniel Mack .resume = at803x_resume, 3830ca7111aSMatus Ujhelyi .features = PHY_GBIT_FEATURES, 3840eae5982SMåns Rullgård .ack_interrupt = at803x_ack_interrupt, 3850eae5982SMåns Rullgård .config_intr = at803x_config_intr, 386317420abSMugunthan V N }, { 3870ca7111aSMatus Ujhelyi /* ATHEROS 8030 */ 388bd8ca17fSDaniel Mack .phy_id = ATH8030_PHY_ID, 3890ca7111aSMatus Ujhelyi .name = "Atheros 8030 ethernet", 39058effd71SFabio Estevam .phy_id_mask = AT803X_PHY_ID_MASK, 39113a56b44SDaniel Mack .probe = at803x_probe, 3920ca7111aSMatus Ujhelyi .config_init = at803x_config_init, 39313a56b44SDaniel Mack .link_change_notify = at803x_link_change_notify, 394ea13c9eeSMugunthan V N .set_wol = at803x_set_wol, 395ea13c9eeSMugunthan V N .get_wol = at803x_get_wol, 3966229ed1fSDaniel Mack .suspend = at803x_suspend, 3976229ed1fSDaniel Mack .resume = at803x_resume, 398e15bb4c6SMartin Blumenstingl .features = PHY_BASIC_FEATURES, 3990eae5982SMåns Rullgård .ack_interrupt = at803x_ack_interrupt, 4000eae5982SMåns Rullgård .config_intr = at803x_config_intr, 40105d7cce8SMugunthan V N }, { 40205d7cce8SMugunthan V N /* ATHEROS 8031 */ 403bd8ca17fSDaniel Mack .phy_id = ATH8031_PHY_ID, 40405d7cce8SMugunthan V N .name = "Atheros 8031 ethernet", 40558effd71SFabio Estevam .phy_id_mask = AT803X_PHY_ID_MASK, 40613a56b44SDaniel Mack .probe = at803x_probe, 40705d7cce8SMugunthan V N .config_init = at803x_config_init, 40805d7cce8SMugunthan V N .set_wol = at803x_set_wol, 40905d7cce8SMugunthan V N .get_wol = at803x_get_wol, 4106229ed1fSDaniel Mack .suspend = at803x_suspend, 4116229ed1fSDaniel Mack .resume = at803x_resume, 41205d7cce8SMugunthan V N .features = PHY_GBIT_FEATURES, 413f62265b5SZefir Kurtisi .aneg_done = at803x_aneg_done, 41477a99394SZhao Qiang .ack_interrupt = &at803x_ack_interrupt, 41577a99394SZhao Qiang .config_intr = &at803x_config_intr, 416317420abSMugunthan V N } }; 4170ca7111aSMatus Ujhelyi 41850fd7150SJohan Hovold module_phy_driver(at803x_driver); 4190ca7111aSMatus Ujhelyi 4200ca7111aSMatus Ujhelyi static struct mdio_device_id __maybe_unused atheros_tbl[] = { 42158effd71SFabio Estevam { ATH8030_PHY_ID, AT803X_PHY_ID_MASK }, 42258effd71SFabio Estevam { ATH8031_PHY_ID, AT803X_PHY_ID_MASK }, 42358effd71SFabio Estevam { ATH8035_PHY_ID, AT803X_PHY_ID_MASK }, 4240ca7111aSMatus Ujhelyi { } 4250ca7111aSMatus Ujhelyi }; 4260ca7111aSMatus Ujhelyi 4270ca7111aSMatus Ujhelyi MODULE_DEVICE_TABLE(mdio, atheros_tbl); 428