1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+ 20ca7111aSMatus Ujhelyi /* 30ca7111aSMatus Ujhelyi * drivers/net/phy/at803x.c 40ca7111aSMatus Ujhelyi * 50ca7111aSMatus Ujhelyi * Driver for Atheros 803x PHY 60ca7111aSMatus Ujhelyi * 70ca7111aSMatus Ujhelyi * Author: Matus Ujhelyi <ujhelyi.m@gmail.com> 80ca7111aSMatus Ujhelyi */ 90ca7111aSMatus Ujhelyi 100ca7111aSMatus Ujhelyi #include <linux/phy.h> 110ca7111aSMatus Ujhelyi #include <linux/module.h> 120ca7111aSMatus Ujhelyi #include <linux/string.h> 130ca7111aSMatus Ujhelyi #include <linux/netdevice.h> 140ca7111aSMatus Ujhelyi #include <linux/etherdevice.h> 1513a56b44SDaniel Mack #include <linux/of_gpio.h> 1613a56b44SDaniel Mack #include <linux/gpio/consumer.h> 170ca7111aSMatus Ujhelyi 180ca7111aSMatus Ujhelyi #define AT803X_INTR_ENABLE 0x12 19e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) 20e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) 21e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) 22e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) 23e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) 24e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) 25e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) 26e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) 27e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_WOL BIT(0) 28e6e4a556SMartin Blumenstingl 290ca7111aSMatus Ujhelyi #define AT803X_INTR_STATUS 0x13 30a46bd63bSMartin Blumenstingl 3113a56b44SDaniel Mack #define AT803X_SMART_SPEED 0x14 3213a56b44SDaniel Mack #define AT803X_LED_CONTROL 0x18 33a46bd63bSMartin Blumenstingl 340ca7111aSMatus Ujhelyi #define AT803X_DEVICE_ADDR 0x03 350ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C 360ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B 370ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A 38f62265b5SZefir Kurtisi #define AT803X_REG_CHIP_CONFIG 0x1f 39f62265b5SZefir Kurtisi #define AT803X_BT_BX_REG_SEL 0x8000 40a46bd63bSMartin Blumenstingl 411ca6d1b1SMugunthan V N #define AT803X_DEBUG_ADDR 0x1D 421ca6d1b1SMugunthan V N #define AT803X_DEBUG_DATA 0x1E 43a46bd63bSMartin Blumenstingl 44f62265b5SZefir Kurtisi #define AT803X_MODE_CFG_MASK 0x0F 45f62265b5SZefir Kurtisi #define AT803X_MODE_CFG_SGMII 0x01 46f62265b5SZefir Kurtisi 47f62265b5SZefir Kurtisi #define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ 48f62265b5SZefir Kurtisi #define AT803X_PSSR_MR_AN_COMPLETE 0x0200 49f62265b5SZefir Kurtisi 502e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_0 0x00 512e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) 52a46bd63bSMartin Blumenstingl 532e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_5 0x05 542e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) 550ca7111aSMatus Ujhelyi 56bd8ca17fSDaniel Mack #define ATH8030_PHY_ID 0x004dd076 57bd8ca17fSDaniel Mack #define ATH8031_PHY_ID 0x004dd074 58bd8ca17fSDaniel Mack #define ATH8035_PHY_ID 0x004dd072 5958effd71SFabio Estevam #define AT803X_PHY_ID_MASK 0xffffffef 60bd8ca17fSDaniel Mack 610ca7111aSMatus Ujhelyi MODULE_DESCRIPTION("Atheros 803x PHY driver"); 620ca7111aSMatus Ujhelyi MODULE_AUTHOR("Matus Ujhelyi"); 630ca7111aSMatus Ujhelyi MODULE_LICENSE("GPL"); 640ca7111aSMatus Ujhelyi 6513a56b44SDaniel Mack struct at803x_priv { 6613a56b44SDaniel Mack bool phy_reset:1; 6713a56b44SDaniel Mack }; 6813a56b44SDaniel Mack 6913a56b44SDaniel Mack struct at803x_context { 7013a56b44SDaniel Mack u16 bmcr; 7113a56b44SDaniel Mack u16 advertise; 7213a56b44SDaniel Mack u16 control1000; 7313a56b44SDaniel Mack u16 int_enable; 7413a56b44SDaniel Mack u16 smart_speed; 7513a56b44SDaniel Mack u16 led_control; 7613a56b44SDaniel Mack }; 7713a56b44SDaniel Mack 782e5f9f28SMartin Blumenstingl static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) 792e5f9f28SMartin Blumenstingl { 802e5f9f28SMartin Blumenstingl int ret; 812e5f9f28SMartin Blumenstingl 822e5f9f28SMartin Blumenstingl ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); 832e5f9f28SMartin Blumenstingl if (ret < 0) 842e5f9f28SMartin Blumenstingl return ret; 852e5f9f28SMartin Blumenstingl 862e5f9f28SMartin Blumenstingl return phy_read(phydev, AT803X_DEBUG_DATA); 872e5f9f28SMartin Blumenstingl } 882e5f9f28SMartin Blumenstingl 892e5f9f28SMartin Blumenstingl static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, 902e5f9f28SMartin Blumenstingl u16 clear, u16 set) 912e5f9f28SMartin Blumenstingl { 922e5f9f28SMartin Blumenstingl u16 val; 932e5f9f28SMartin Blumenstingl int ret; 942e5f9f28SMartin Blumenstingl 952e5f9f28SMartin Blumenstingl ret = at803x_debug_reg_read(phydev, reg); 962e5f9f28SMartin Blumenstingl if (ret < 0) 972e5f9f28SMartin Blumenstingl return ret; 982e5f9f28SMartin Blumenstingl 992e5f9f28SMartin Blumenstingl val = ret & 0xffff; 1002e5f9f28SMartin Blumenstingl val &= ~clear; 1012e5f9f28SMartin Blumenstingl val |= set; 1022e5f9f28SMartin Blumenstingl 1032e5f9f28SMartin Blumenstingl return phy_write(phydev, AT803X_DEBUG_DATA, val); 1042e5f9f28SMartin Blumenstingl } 1052e5f9f28SMartin Blumenstingl 1066d4cd041SVinod Koul static int at803x_enable_rx_delay(struct phy_device *phydev) 1076d4cd041SVinod Koul { 1086d4cd041SVinod Koul return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0, 1096d4cd041SVinod Koul AT803X_DEBUG_RX_CLK_DLY_EN); 1106d4cd041SVinod Koul } 1116d4cd041SVinod Koul 1126d4cd041SVinod Koul static int at803x_enable_tx_delay(struct phy_device *phydev) 1136d4cd041SVinod Koul { 1146d4cd041SVinod Koul return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 0, 1156d4cd041SVinod Koul AT803X_DEBUG_TX_CLK_DLY_EN); 1166d4cd041SVinod Koul } 1176d4cd041SVinod Koul 11843f2ebd5SVinod Koul static int at803x_disable_rx_delay(struct phy_device *phydev) 1192e5f9f28SMartin Blumenstingl { 120cd28d1d6SVinod Koul return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 121cd28d1d6SVinod Koul AT803X_DEBUG_RX_CLK_DLY_EN, 0); 1222e5f9f28SMartin Blumenstingl } 1232e5f9f28SMartin Blumenstingl 12443f2ebd5SVinod Koul static int at803x_disable_tx_delay(struct phy_device *phydev) 1252e5f9f28SMartin Blumenstingl { 126cd28d1d6SVinod Koul return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 127cd28d1d6SVinod Koul AT803X_DEBUG_TX_CLK_DLY_EN, 0); 1282e5f9f28SMartin Blumenstingl } 1292e5f9f28SMartin Blumenstingl 13013a56b44SDaniel Mack /* save relevant PHY registers to private copy */ 13113a56b44SDaniel Mack static void at803x_context_save(struct phy_device *phydev, 13213a56b44SDaniel Mack struct at803x_context *context) 13313a56b44SDaniel Mack { 13413a56b44SDaniel Mack context->bmcr = phy_read(phydev, MII_BMCR); 13513a56b44SDaniel Mack context->advertise = phy_read(phydev, MII_ADVERTISE); 13613a56b44SDaniel Mack context->control1000 = phy_read(phydev, MII_CTRL1000); 13713a56b44SDaniel Mack context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE); 13813a56b44SDaniel Mack context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED); 13913a56b44SDaniel Mack context->led_control = phy_read(phydev, AT803X_LED_CONTROL); 14013a56b44SDaniel Mack } 14113a56b44SDaniel Mack 14213a56b44SDaniel Mack /* restore relevant PHY registers from private copy */ 14313a56b44SDaniel Mack static void at803x_context_restore(struct phy_device *phydev, 14413a56b44SDaniel Mack const struct at803x_context *context) 14513a56b44SDaniel Mack { 14613a56b44SDaniel Mack phy_write(phydev, MII_BMCR, context->bmcr); 14713a56b44SDaniel Mack phy_write(phydev, MII_ADVERTISE, context->advertise); 14813a56b44SDaniel Mack phy_write(phydev, MII_CTRL1000, context->control1000); 14913a56b44SDaniel Mack phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable); 15013a56b44SDaniel Mack phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed); 15113a56b44SDaniel Mack phy_write(phydev, AT803X_LED_CONTROL, context->led_control); 15213a56b44SDaniel Mack } 15313a56b44SDaniel Mack 154ea13c9eeSMugunthan V N static int at803x_set_wol(struct phy_device *phydev, 155ea13c9eeSMugunthan V N struct ethtool_wolinfo *wol) 1560ca7111aSMatus Ujhelyi { 1570ca7111aSMatus Ujhelyi struct net_device *ndev = phydev->attached_dev; 1580ca7111aSMatus Ujhelyi const u8 *mac; 159ea13c9eeSMugunthan V N int ret; 160ea13c9eeSMugunthan V N u32 value; 1610ca7111aSMatus Ujhelyi unsigned int i, offsets[] = { 1620ca7111aSMatus Ujhelyi AT803X_LOC_MAC_ADDR_32_47_OFFSET, 1630ca7111aSMatus Ujhelyi AT803X_LOC_MAC_ADDR_16_31_OFFSET, 1640ca7111aSMatus Ujhelyi AT803X_LOC_MAC_ADDR_0_15_OFFSET, 1650ca7111aSMatus Ujhelyi }; 1660ca7111aSMatus Ujhelyi 1670ca7111aSMatus Ujhelyi if (!ndev) 168ea13c9eeSMugunthan V N return -ENODEV; 1690ca7111aSMatus Ujhelyi 170ea13c9eeSMugunthan V N if (wol->wolopts & WAKE_MAGIC) { 1710ca7111aSMatus Ujhelyi mac = (const u8 *) ndev->dev_addr; 1720ca7111aSMatus Ujhelyi 1730ca7111aSMatus Ujhelyi if (!is_valid_ether_addr(mac)) 174fc755687SDan Murphy return -EINVAL; 1750ca7111aSMatus Ujhelyi 1760e021396SCarlo Caione for (i = 0; i < 3; i++) 1770e021396SCarlo Caione phy_write_mmd(phydev, AT803X_DEVICE_ADDR, offsets[i], 1780ca7111aSMatus Ujhelyi mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); 179ea13c9eeSMugunthan V N 180ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_ENABLE); 181e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_WOL; 182ea13c9eeSMugunthan V N ret = phy_write(phydev, AT803X_INTR_ENABLE, value); 183ea13c9eeSMugunthan V N if (ret) 184ea13c9eeSMugunthan V N return ret; 185ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_STATUS); 186ea13c9eeSMugunthan V N } else { 187ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_ENABLE); 188e6e4a556SMartin Blumenstingl value &= (~AT803X_INTR_ENABLE_WOL); 189ea13c9eeSMugunthan V N ret = phy_write(phydev, AT803X_INTR_ENABLE, value); 190ea13c9eeSMugunthan V N if (ret) 191ea13c9eeSMugunthan V N return ret; 192ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_STATUS); 193ea13c9eeSMugunthan V N } 194ea13c9eeSMugunthan V N 195ea13c9eeSMugunthan V N return ret; 196ea13c9eeSMugunthan V N } 197ea13c9eeSMugunthan V N 198ea13c9eeSMugunthan V N static void at803x_get_wol(struct phy_device *phydev, 199ea13c9eeSMugunthan V N struct ethtool_wolinfo *wol) 200ea13c9eeSMugunthan V N { 201ea13c9eeSMugunthan V N u32 value; 202ea13c9eeSMugunthan V N 203ea13c9eeSMugunthan V N wol->supported = WAKE_MAGIC; 204ea13c9eeSMugunthan V N wol->wolopts = 0; 205ea13c9eeSMugunthan V N 206ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_ENABLE); 207e6e4a556SMartin Blumenstingl if (value & AT803X_INTR_ENABLE_WOL) 208ea13c9eeSMugunthan V N wol->wolopts |= WAKE_MAGIC; 2090ca7111aSMatus Ujhelyi } 2100ca7111aSMatus Ujhelyi 2116229ed1fSDaniel Mack static int at803x_suspend(struct phy_device *phydev) 2126229ed1fSDaniel Mack { 2136229ed1fSDaniel Mack int value; 2146229ed1fSDaniel Mack int wol_enabled; 2156229ed1fSDaniel Mack 2166229ed1fSDaniel Mack value = phy_read(phydev, AT803X_INTR_ENABLE); 217e6e4a556SMartin Blumenstingl wol_enabled = value & AT803X_INTR_ENABLE_WOL; 2186229ed1fSDaniel Mack 2196229ed1fSDaniel Mack if (wol_enabled) 220fea23fb5SRussell King value = BMCR_ISOLATE; 2216229ed1fSDaniel Mack else 222fea23fb5SRussell King value = BMCR_PDOWN; 2236229ed1fSDaniel Mack 224fea23fb5SRussell King phy_modify(phydev, MII_BMCR, 0, value); 2256229ed1fSDaniel Mack 2266229ed1fSDaniel Mack return 0; 2276229ed1fSDaniel Mack } 2286229ed1fSDaniel Mack 2296229ed1fSDaniel Mack static int at803x_resume(struct phy_device *phydev) 2306229ed1fSDaniel Mack { 231f102852fSRussell King return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); 2326229ed1fSDaniel Mack } 2336229ed1fSDaniel Mack 23413a56b44SDaniel Mack static int at803x_probe(struct phy_device *phydev) 23513a56b44SDaniel Mack { 236e5a03bfdSAndrew Lunn struct device *dev = &phydev->mdio.dev; 23713a56b44SDaniel Mack struct at803x_priv *priv; 23813a56b44SDaniel Mack 2398f2877caSFengguang Wu priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 24013a56b44SDaniel Mack if (!priv) 24113a56b44SDaniel Mack return -ENOMEM; 24213a56b44SDaniel Mack 24313a56b44SDaniel Mack phydev->priv = priv; 24413a56b44SDaniel Mack 24513a56b44SDaniel Mack return 0; 24613a56b44SDaniel Mack } 24713a56b44SDaniel Mack 2480ca7111aSMatus Ujhelyi static int at803x_config_init(struct phy_device *phydev) 2490ca7111aSMatus Ujhelyi { 2501ca6d1b1SMugunthan V N int ret; 2510ca7111aSMatus Ujhelyi 2526ff01dbbSDaniel Mack ret = genphy_config_init(phydev); 2536ff01dbbSDaniel Mack if (ret < 0) 2546ff01dbbSDaniel Mack return ret; 2550ca7111aSMatus Ujhelyi 2566d4cd041SVinod Koul /* The RX and TX delay default is: 2576d4cd041SVinod Koul * after HW reset: RX delay enabled and TX delay disabled 2586d4cd041SVinod Koul * after SW reset: RX delay enabled, while TX delay retains the 2596d4cd041SVinod Koul * value before reset. 2606d4cd041SVinod Koul * 2616d4cd041SVinod Koul * So let's first disable the RX and TX delays in PHY and enable 2626d4cd041SVinod Koul * them based on the mode selected (this also takes care of RGMII 2636d4cd041SVinod Koul * mode where we expect delays to be disabled) 2646d4cd041SVinod Koul */ 2656d4cd041SVinod Koul 266cd28d1d6SVinod Koul ret = at803x_disable_rx_delay(phydev); 2672e5f9f28SMartin Blumenstingl if (ret < 0) 2681ca6d1b1SMugunthan V N return ret; 269cd28d1d6SVinod Koul ret = at803x_disable_tx_delay(phydev); 2702e5f9f28SMartin Blumenstingl if (ret < 0) 2711ca6d1b1SMugunthan V N return ret; 2726d4cd041SVinod Koul 2736d4cd041SVinod Koul if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || 2746d4cd041SVinod Koul phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { 2756d4cd041SVinod Koul /* If RGMII_ID or RGMII_RXID are specified enable RX delay, 2766d4cd041SVinod Koul * otherwise keep it disabled 2776d4cd041SVinod Koul */ 2786d4cd041SVinod Koul ret = at803x_enable_rx_delay(phydev); 2796d4cd041SVinod Koul if (ret < 0) 2806d4cd041SVinod Koul return ret; 2811ca6d1b1SMugunthan V N } 2821ca6d1b1SMugunthan V N 2836d4cd041SVinod Koul if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || 2846d4cd041SVinod Koul phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { 2856d4cd041SVinod Koul /* If RGMII_ID or RGMII_TXID are specified enable TX delay, 2866d4cd041SVinod Koul * otherwise keep it disabled 2876d4cd041SVinod Koul */ 2886d4cd041SVinod Koul ret = at803x_enable_tx_delay(phydev); 2896d4cd041SVinod Koul } 2906d4cd041SVinod Koul 2916d4cd041SVinod Koul return ret; 2920ca7111aSMatus Ujhelyi } 2930ca7111aSMatus Ujhelyi 29477a99394SZhao Qiang static int at803x_ack_interrupt(struct phy_device *phydev) 29577a99394SZhao Qiang { 29677a99394SZhao Qiang int err; 29777a99394SZhao Qiang 298a46bd63bSMartin Blumenstingl err = phy_read(phydev, AT803X_INTR_STATUS); 29977a99394SZhao Qiang 30077a99394SZhao Qiang return (err < 0) ? err : 0; 30177a99394SZhao Qiang } 30277a99394SZhao Qiang 30377a99394SZhao Qiang static int at803x_config_intr(struct phy_device *phydev) 30477a99394SZhao Qiang { 30577a99394SZhao Qiang int err; 30677a99394SZhao Qiang int value; 30777a99394SZhao Qiang 308a46bd63bSMartin Blumenstingl value = phy_read(phydev, AT803X_INTR_ENABLE); 30977a99394SZhao Qiang 310e6e4a556SMartin Blumenstingl if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 311e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_AUTONEG_ERR; 312e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_SPEED_CHANGED; 313e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; 314e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_LINK_FAIL; 315e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_LINK_SUCCESS; 316e6e4a556SMartin Blumenstingl 317e6e4a556SMartin Blumenstingl err = phy_write(phydev, AT803X_INTR_ENABLE, value); 318e6e4a556SMartin Blumenstingl } 31977a99394SZhao Qiang else 320a46bd63bSMartin Blumenstingl err = phy_write(phydev, AT803X_INTR_ENABLE, 0); 32177a99394SZhao Qiang 32277a99394SZhao Qiang return err; 32377a99394SZhao Qiang } 32477a99394SZhao Qiang 32513a56b44SDaniel Mack static void at803x_link_change_notify(struct phy_device *phydev) 32613a56b44SDaniel Mack { 32713a56b44SDaniel Mack /* 32813a56b44SDaniel Mack * Conduct a hardware reset for AT8030 every time a link loss is 32913a56b44SDaniel Mack * signalled. This is necessary to circumvent a hardware bug that 33013a56b44SDaniel Mack * occurs when the cable is unplugged while TX packets are pending 33113a56b44SDaniel Mack * in the FIFO. In such cases, the FIFO enters an error mode it 33213a56b44SDaniel Mack * cannot recover from by software. 33313a56b44SDaniel Mack */ 3346110ed2dSDavid Bauer if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) { 33513a56b44SDaniel Mack struct at803x_context context; 33613a56b44SDaniel Mack 33713a56b44SDaniel Mack at803x_context_save(phydev, &context); 33813a56b44SDaniel Mack 339bafbdd52SSergei Shtylyov phy_device_reset(phydev, 1); 34013a56b44SDaniel Mack msleep(1); 341bafbdd52SSergei Shtylyov phy_device_reset(phydev, 0); 342d57019d1SSergei Shtylyov msleep(1); 34313a56b44SDaniel Mack 34413a56b44SDaniel Mack at803x_context_restore(phydev, &context); 34513a56b44SDaniel Mack 3465c5f626bSHeiner Kallweit phydev_dbg(phydev, "%s(): phy was reset\n", __func__); 34713a56b44SDaniel Mack } 34813a56b44SDaniel Mack } 34913a56b44SDaniel Mack 350f62265b5SZefir Kurtisi static int at803x_aneg_done(struct phy_device *phydev) 351f62265b5SZefir Kurtisi { 352f62265b5SZefir Kurtisi int ccr; 353f62265b5SZefir Kurtisi 354f62265b5SZefir Kurtisi int aneg_done = genphy_aneg_done(phydev); 355f62265b5SZefir Kurtisi if (aneg_done != BMSR_ANEGCOMPLETE) 356f62265b5SZefir Kurtisi return aneg_done; 357f62265b5SZefir Kurtisi 358f62265b5SZefir Kurtisi /* 359f62265b5SZefir Kurtisi * in SGMII mode, if copper side autoneg is successful, 360f62265b5SZefir Kurtisi * also check SGMII side autoneg result 361f62265b5SZefir Kurtisi */ 362f62265b5SZefir Kurtisi ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); 363f62265b5SZefir Kurtisi if ((ccr & AT803X_MODE_CFG_MASK) != AT803X_MODE_CFG_SGMII) 364f62265b5SZefir Kurtisi return aneg_done; 365f62265b5SZefir Kurtisi 366f62265b5SZefir Kurtisi /* switch to SGMII/fiber page */ 367f62265b5SZefir Kurtisi phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL); 368f62265b5SZefir Kurtisi 369f62265b5SZefir Kurtisi /* check if the SGMII link is OK. */ 370f62265b5SZefir Kurtisi if (!(phy_read(phydev, AT803X_PSSR) & AT803X_PSSR_MR_AN_COMPLETE)) { 371ab2a605fSAndrew Lunn phydev_warn(phydev, "803x_aneg_done: SGMII link is not ok\n"); 372f62265b5SZefir Kurtisi aneg_done = 0; 373f62265b5SZefir Kurtisi } 374f62265b5SZefir Kurtisi /* switch back to copper page */ 375f62265b5SZefir Kurtisi phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL); 376f62265b5SZefir Kurtisi 377f62265b5SZefir Kurtisi return aneg_done; 378f62265b5SZefir Kurtisi } 379f62265b5SZefir Kurtisi 380317420abSMugunthan V N static struct phy_driver at803x_driver[] = { 381317420abSMugunthan V N { 3820ca7111aSMatus Ujhelyi /* ATHEROS 8035 */ 383bd8ca17fSDaniel Mack .phy_id = ATH8035_PHY_ID, 3840ca7111aSMatus Ujhelyi .name = "Atheros 8035 ethernet", 38558effd71SFabio Estevam .phy_id_mask = AT803X_PHY_ID_MASK, 38613a56b44SDaniel Mack .probe = at803x_probe, 3870ca7111aSMatus Ujhelyi .config_init = at803x_config_init, 388ea13c9eeSMugunthan V N .set_wol = at803x_set_wol, 389ea13c9eeSMugunthan V N .get_wol = at803x_get_wol, 3906229ed1fSDaniel Mack .suspend = at803x_suspend, 3916229ed1fSDaniel Mack .resume = at803x_resume, 392dcdecdcfSHeiner Kallweit /* PHY_GBIT_FEATURES */ 3930eae5982SMåns Rullgård .ack_interrupt = at803x_ack_interrupt, 3940eae5982SMåns Rullgård .config_intr = at803x_config_intr, 395317420abSMugunthan V N }, { 3960ca7111aSMatus Ujhelyi /* ATHEROS 8030 */ 397bd8ca17fSDaniel Mack .phy_id = ATH8030_PHY_ID, 3980ca7111aSMatus Ujhelyi .name = "Atheros 8030 ethernet", 39958effd71SFabio Estevam .phy_id_mask = AT803X_PHY_ID_MASK, 40013a56b44SDaniel Mack .probe = at803x_probe, 4010ca7111aSMatus Ujhelyi .config_init = at803x_config_init, 40213a56b44SDaniel Mack .link_change_notify = at803x_link_change_notify, 403ea13c9eeSMugunthan V N .set_wol = at803x_set_wol, 404ea13c9eeSMugunthan V N .get_wol = at803x_get_wol, 4056229ed1fSDaniel Mack .suspend = at803x_suspend, 4066229ed1fSDaniel Mack .resume = at803x_resume, 407dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 4080eae5982SMåns Rullgård .ack_interrupt = at803x_ack_interrupt, 4090eae5982SMåns Rullgård .config_intr = at803x_config_intr, 41005d7cce8SMugunthan V N }, { 41105d7cce8SMugunthan V N /* ATHEROS 8031 */ 412bd8ca17fSDaniel Mack .phy_id = ATH8031_PHY_ID, 41305d7cce8SMugunthan V N .name = "Atheros 8031 ethernet", 41458effd71SFabio Estevam .phy_id_mask = AT803X_PHY_ID_MASK, 41513a56b44SDaniel Mack .probe = at803x_probe, 41605d7cce8SMugunthan V N .config_init = at803x_config_init, 41705d7cce8SMugunthan V N .set_wol = at803x_set_wol, 41805d7cce8SMugunthan V N .get_wol = at803x_get_wol, 4196229ed1fSDaniel Mack .suspend = at803x_suspend, 4206229ed1fSDaniel Mack .resume = at803x_resume, 421dcdecdcfSHeiner Kallweit /* PHY_GBIT_FEATURES */ 422f62265b5SZefir Kurtisi .aneg_done = at803x_aneg_done, 42377a99394SZhao Qiang .ack_interrupt = &at803x_ack_interrupt, 42477a99394SZhao Qiang .config_intr = &at803x_config_intr, 425317420abSMugunthan V N } }; 4260ca7111aSMatus Ujhelyi 42750fd7150SJohan Hovold module_phy_driver(at803x_driver); 4280ca7111aSMatus Ujhelyi 4290ca7111aSMatus Ujhelyi static struct mdio_device_id __maybe_unused atheros_tbl[] = { 43058effd71SFabio Estevam { ATH8030_PHY_ID, AT803X_PHY_ID_MASK }, 43158effd71SFabio Estevam { ATH8031_PHY_ID, AT803X_PHY_ID_MASK }, 43258effd71SFabio Estevam { ATH8035_PHY_ID, AT803X_PHY_ID_MASK }, 4330ca7111aSMatus Ujhelyi { } 4340ca7111aSMatus Ujhelyi }; 4350ca7111aSMatus Ujhelyi 4360ca7111aSMatus Ujhelyi MODULE_DEVICE_TABLE(mdio, atheros_tbl); 437