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 1806d5f344SRussell King #define AT803X_SPECIFIC_STATUS 0x11 1906d5f344SRussell King #define AT803X_SS_SPEED_MASK (3 << 14) 2006d5f344SRussell King #define AT803X_SS_SPEED_1000 (2 << 14) 2106d5f344SRussell King #define AT803X_SS_SPEED_100 (1 << 14) 2206d5f344SRussell King #define AT803X_SS_SPEED_10 (0 << 14) 2306d5f344SRussell King #define AT803X_SS_DUPLEX BIT(13) 2406d5f344SRussell King #define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11) 2506d5f344SRussell King #define AT803X_SS_MDIX BIT(6) 2606d5f344SRussell King 270ca7111aSMatus Ujhelyi #define AT803X_INTR_ENABLE 0x12 28e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) 29e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) 30e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) 31e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) 32e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) 33e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) 34e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) 35e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) 36e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_WOL BIT(0) 37e6e4a556SMartin Blumenstingl 380ca7111aSMatus Ujhelyi #define AT803X_INTR_STATUS 0x13 39a46bd63bSMartin Blumenstingl 4013a56b44SDaniel Mack #define AT803X_SMART_SPEED 0x14 4113a56b44SDaniel Mack #define AT803X_LED_CONTROL 0x18 42a46bd63bSMartin Blumenstingl 430ca7111aSMatus Ujhelyi #define AT803X_DEVICE_ADDR 0x03 440ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C 450ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B 460ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A 47f62265b5SZefir Kurtisi #define AT803X_REG_CHIP_CONFIG 0x1f 48f62265b5SZefir Kurtisi #define AT803X_BT_BX_REG_SEL 0x8000 49a46bd63bSMartin Blumenstingl 501ca6d1b1SMugunthan V N #define AT803X_DEBUG_ADDR 0x1D 511ca6d1b1SMugunthan V N #define AT803X_DEBUG_DATA 0x1E 52a46bd63bSMartin Blumenstingl 53f62265b5SZefir Kurtisi #define AT803X_MODE_CFG_MASK 0x0F 54f62265b5SZefir Kurtisi #define AT803X_MODE_CFG_SGMII 0x01 55f62265b5SZefir Kurtisi 56f62265b5SZefir Kurtisi #define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ 57f62265b5SZefir Kurtisi #define AT803X_PSSR_MR_AN_COMPLETE 0x0200 58f62265b5SZefir Kurtisi 592e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_0 0x00 602e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) 61a46bd63bSMartin Blumenstingl 622e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_5 0x05 632e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) 640ca7111aSMatus Ujhelyi 65bd8ca17fSDaniel Mack #define ATH8030_PHY_ID 0x004dd076 66bd8ca17fSDaniel Mack #define ATH8031_PHY_ID 0x004dd074 67bd8ca17fSDaniel Mack #define ATH8035_PHY_ID 0x004dd072 6858effd71SFabio Estevam #define AT803X_PHY_ID_MASK 0xffffffef 69bd8ca17fSDaniel Mack 700ca7111aSMatus Ujhelyi MODULE_DESCRIPTION("Atheros 803x PHY driver"); 710ca7111aSMatus Ujhelyi MODULE_AUTHOR("Matus Ujhelyi"); 720ca7111aSMatus Ujhelyi MODULE_LICENSE("GPL"); 730ca7111aSMatus Ujhelyi 7413a56b44SDaniel Mack struct at803x_priv { 7513a56b44SDaniel Mack bool phy_reset:1; 7613a56b44SDaniel Mack }; 7713a56b44SDaniel Mack 7813a56b44SDaniel Mack struct at803x_context { 7913a56b44SDaniel Mack u16 bmcr; 8013a56b44SDaniel Mack u16 advertise; 8113a56b44SDaniel Mack u16 control1000; 8213a56b44SDaniel Mack u16 int_enable; 8313a56b44SDaniel Mack u16 smart_speed; 8413a56b44SDaniel Mack u16 led_control; 8513a56b44SDaniel Mack }; 8613a56b44SDaniel Mack 872e5f9f28SMartin Blumenstingl static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) 882e5f9f28SMartin Blumenstingl { 892e5f9f28SMartin Blumenstingl int ret; 902e5f9f28SMartin Blumenstingl 912e5f9f28SMartin Blumenstingl ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); 922e5f9f28SMartin Blumenstingl if (ret < 0) 932e5f9f28SMartin Blumenstingl return ret; 942e5f9f28SMartin Blumenstingl 952e5f9f28SMartin Blumenstingl return phy_read(phydev, AT803X_DEBUG_DATA); 962e5f9f28SMartin Blumenstingl } 972e5f9f28SMartin Blumenstingl 982e5f9f28SMartin Blumenstingl static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, 992e5f9f28SMartin Blumenstingl u16 clear, u16 set) 1002e5f9f28SMartin Blumenstingl { 1012e5f9f28SMartin Blumenstingl u16 val; 1022e5f9f28SMartin Blumenstingl int ret; 1032e5f9f28SMartin Blumenstingl 1042e5f9f28SMartin Blumenstingl ret = at803x_debug_reg_read(phydev, reg); 1052e5f9f28SMartin Blumenstingl if (ret < 0) 1062e5f9f28SMartin Blumenstingl return ret; 1072e5f9f28SMartin Blumenstingl 1082e5f9f28SMartin Blumenstingl val = ret & 0xffff; 1092e5f9f28SMartin Blumenstingl val &= ~clear; 1102e5f9f28SMartin Blumenstingl val |= set; 1112e5f9f28SMartin Blumenstingl 1122e5f9f28SMartin Blumenstingl return phy_write(phydev, AT803X_DEBUG_DATA, val); 1132e5f9f28SMartin Blumenstingl } 1142e5f9f28SMartin Blumenstingl 1156d4cd041SVinod Koul static int at803x_enable_rx_delay(struct phy_device *phydev) 1166d4cd041SVinod Koul { 1176d4cd041SVinod Koul return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0, 1186d4cd041SVinod Koul AT803X_DEBUG_RX_CLK_DLY_EN); 1196d4cd041SVinod Koul } 1206d4cd041SVinod Koul 1216d4cd041SVinod Koul static int at803x_enable_tx_delay(struct phy_device *phydev) 1226d4cd041SVinod Koul { 1236d4cd041SVinod Koul return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 0, 1246d4cd041SVinod Koul AT803X_DEBUG_TX_CLK_DLY_EN); 1256d4cd041SVinod Koul } 1266d4cd041SVinod Koul 12743f2ebd5SVinod Koul static int at803x_disable_rx_delay(struct phy_device *phydev) 1282e5f9f28SMartin Blumenstingl { 129cd28d1d6SVinod Koul return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 130cd28d1d6SVinod Koul AT803X_DEBUG_RX_CLK_DLY_EN, 0); 1312e5f9f28SMartin Blumenstingl } 1322e5f9f28SMartin Blumenstingl 13343f2ebd5SVinod Koul static int at803x_disable_tx_delay(struct phy_device *phydev) 1342e5f9f28SMartin Blumenstingl { 135cd28d1d6SVinod Koul return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 136cd28d1d6SVinod Koul AT803X_DEBUG_TX_CLK_DLY_EN, 0); 1372e5f9f28SMartin Blumenstingl } 1382e5f9f28SMartin Blumenstingl 13913a56b44SDaniel Mack /* save relevant PHY registers to private copy */ 14013a56b44SDaniel Mack static void at803x_context_save(struct phy_device *phydev, 14113a56b44SDaniel Mack struct at803x_context *context) 14213a56b44SDaniel Mack { 14313a56b44SDaniel Mack context->bmcr = phy_read(phydev, MII_BMCR); 14413a56b44SDaniel Mack context->advertise = phy_read(phydev, MII_ADVERTISE); 14513a56b44SDaniel Mack context->control1000 = phy_read(phydev, MII_CTRL1000); 14613a56b44SDaniel Mack context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE); 14713a56b44SDaniel Mack context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED); 14813a56b44SDaniel Mack context->led_control = phy_read(phydev, AT803X_LED_CONTROL); 14913a56b44SDaniel Mack } 15013a56b44SDaniel Mack 15113a56b44SDaniel Mack /* restore relevant PHY registers from private copy */ 15213a56b44SDaniel Mack static void at803x_context_restore(struct phy_device *phydev, 15313a56b44SDaniel Mack const struct at803x_context *context) 15413a56b44SDaniel Mack { 15513a56b44SDaniel Mack phy_write(phydev, MII_BMCR, context->bmcr); 15613a56b44SDaniel Mack phy_write(phydev, MII_ADVERTISE, context->advertise); 15713a56b44SDaniel Mack phy_write(phydev, MII_CTRL1000, context->control1000); 15813a56b44SDaniel Mack phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable); 15913a56b44SDaniel Mack phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed); 16013a56b44SDaniel Mack phy_write(phydev, AT803X_LED_CONTROL, context->led_control); 16113a56b44SDaniel Mack } 16213a56b44SDaniel Mack 163ea13c9eeSMugunthan V N static int at803x_set_wol(struct phy_device *phydev, 164ea13c9eeSMugunthan V N struct ethtool_wolinfo *wol) 1650ca7111aSMatus Ujhelyi { 1660ca7111aSMatus Ujhelyi struct net_device *ndev = phydev->attached_dev; 1670ca7111aSMatus Ujhelyi const u8 *mac; 168ea13c9eeSMugunthan V N int ret; 169ea13c9eeSMugunthan V N u32 value; 1700ca7111aSMatus Ujhelyi unsigned int i, offsets[] = { 1710ca7111aSMatus Ujhelyi AT803X_LOC_MAC_ADDR_32_47_OFFSET, 1720ca7111aSMatus Ujhelyi AT803X_LOC_MAC_ADDR_16_31_OFFSET, 1730ca7111aSMatus Ujhelyi AT803X_LOC_MAC_ADDR_0_15_OFFSET, 1740ca7111aSMatus Ujhelyi }; 1750ca7111aSMatus Ujhelyi 1760ca7111aSMatus Ujhelyi if (!ndev) 177ea13c9eeSMugunthan V N return -ENODEV; 1780ca7111aSMatus Ujhelyi 179ea13c9eeSMugunthan V N if (wol->wolopts & WAKE_MAGIC) { 1800ca7111aSMatus Ujhelyi mac = (const u8 *) ndev->dev_addr; 1810ca7111aSMatus Ujhelyi 1820ca7111aSMatus Ujhelyi if (!is_valid_ether_addr(mac)) 183fc755687SDan Murphy return -EINVAL; 1840ca7111aSMatus Ujhelyi 1850e021396SCarlo Caione for (i = 0; i < 3; i++) 1860e021396SCarlo Caione phy_write_mmd(phydev, AT803X_DEVICE_ADDR, offsets[i], 1870ca7111aSMatus Ujhelyi mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); 188ea13c9eeSMugunthan V N 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 } else { 196ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_ENABLE); 197e6e4a556SMartin Blumenstingl value &= (~AT803X_INTR_ENABLE_WOL); 198ea13c9eeSMugunthan V N ret = phy_write(phydev, AT803X_INTR_ENABLE, value); 199ea13c9eeSMugunthan V N if (ret) 200ea13c9eeSMugunthan V N return ret; 201ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_STATUS); 202ea13c9eeSMugunthan V N } 203ea13c9eeSMugunthan V N 204ea13c9eeSMugunthan V N return ret; 205ea13c9eeSMugunthan V N } 206ea13c9eeSMugunthan V N 207ea13c9eeSMugunthan V N static void at803x_get_wol(struct phy_device *phydev, 208ea13c9eeSMugunthan V N struct ethtool_wolinfo *wol) 209ea13c9eeSMugunthan V N { 210ea13c9eeSMugunthan V N u32 value; 211ea13c9eeSMugunthan V N 212ea13c9eeSMugunthan V N wol->supported = WAKE_MAGIC; 213ea13c9eeSMugunthan V N wol->wolopts = 0; 214ea13c9eeSMugunthan V N 215ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_ENABLE); 216e6e4a556SMartin Blumenstingl if (value & AT803X_INTR_ENABLE_WOL) 217ea13c9eeSMugunthan V N wol->wolopts |= WAKE_MAGIC; 2180ca7111aSMatus Ujhelyi } 2190ca7111aSMatus Ujhelyi 2206229ed1fSDaniel Mack static int at803x_suspend(struct phy_device *phydev) 2216229ed1fSDaniel Mack { 2226229ed1fSDaniel Mack int value; 2236229ed1fSDaniel Mack int wol_enabled; 2246229ed1fSDaniel Mack 2256229ed1fSDaniel Mack value = phy_read(phydev, AT803X_INTR_ENABLE); 226e6e4a556SMartin Blumenstingl wol_enabled = value & AT803X_INTR_ENABLE_WOL; 2276229ed1fSDaniel Mack 2286229ed1fSDaniel Mack if (wol_enabled) 229fea23fb5SRussell King value = BMCR_ISOLATE; 2306229ed1fSDaniel Mack else 231fea23fb5SRussell King value = BMCR_PDOWN; 2326229ed1fSDaniel Mack 233fea23fb5SRussell King phy_modify(phydev, MII_BMCR, 0, value); 2346229ed1fSDaniel Mack 2356229ed1fSDaniel Mack return 0; 2366229ed1fSDaniel Mack } 2376229ed1fSDaniel Mack 2386229ed1fSDaniel Mack static int at803x_resume(struct phy_device *phydev) 2396229ed1fSDaniel Mack { 240f102852fSRussell King return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); 2416229ed1fSDaniel Mack } 2426229ed1fSDaniel Mack 24313a56b44SDaniel Mack static int at803x_probe(struct phy_device *phydev) 24413a56b44SDaniel Mack { 245e5a03bfdSAndrew Lunn struct device *dev = &phydev->mdio.dev; 24613a56b44SDaniel Mack struct at803x_priv *priv; 24713a56b44SDaniel Mack 2488f2877caSFengguang Wu priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 24913a56b44SDaniel Mack if (!priv) 25013a56b44SDaniel Mack return -ENOMEM; 25113a56b44SDaniel Mack 25213a56b44SDaniel Mack phydev->priv = priv; 25313a56b44SDaniel Mack 25413a56b44SDaniel Mack return 0; 25513a56b44SDaniel Mack } 25613a56b44SDaniel Mack 2570ca7111aSMatus Ujhelyi static int at803x_config_init(struct phy_device *phydev) 2580ca7111aSMatus Ujhelyi { 2591ca6d1b1SMugunthan V N int ret; 2600ca7111aSMatus Ujhelyi 2616d4cd041SVinod Koul /* The RX and TX delay default is: 2626d4cd041SVinod Koul * after HW reset: RX delay enabled and TX delay disabled 2636d4cd041SVinod Koul * after SW reset: RX delay enabled, while TX delay retains the 2646d4cd041SVinod Koul * value before reset. 2656d4cd041SVinod Koul */ 266bb0ce4c1SAndré Draszik if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || 267bb0ce4c1SAndré Draszik phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) 268bb0ce4c1SAndré Draszik ret = at803x_enable_rx_delay(phydev); 269bb0ce4c1SAndré Draszik else 270cd28d1d6SVinod Koul ret = at803x_disable_rx_delay(phydev); 2712e5f9f28SMartin Blumenstingl if (ret < 0) 2721ca6d1b1SMugunthan V N return ret; 2736d4cd041SVinod Koul 2746d4cd041SVinod Koul if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || 275bb0ce4c1SAndré Draszik phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) 2766d4cd041SVinod Koul ret = at803x_enable_tx_delay(phydev); 277bb0ce4c1SAndré Draszik else 278bb0ce4c1SAndré Draszik ret = at803x_disable_tx_delay(phydev); 2796d4cd041SVinod Koul 2806d4cd041SVinod Koul return ret; 2810ca7111aSMatus Ujhelyi } 2820ca7111aSMatus Ujhelyi 28377a99394SZhao Qiang static int at803x_ack_interrupt(struct phy_device *phydev) 28477a99394SZhao Qiang { 28577a99394SZhao Qiang int err; 28677a99394SZhao Qiang 287a46bd63bSMartin Blumenstingl err = phy_read(phydev, AT803X_INTR_STATUS); 28877a99394SZhao Qiang 28977a99394SZhao Qiang return (err < 0) ? err : 0; 29077a99394SZhao Qiang } 29177a99394SZhao Qiang 29277a99394SZhao Qiang static int at803x_config_intr(struct phy_device *phydev) 29377a99394SZhao Qiang { 29477a99394SZhao Qiang int err; 29577a99394SZhao Qiang int value; 29677a99394SZhao Qiang 297a46bd63bSMartin Blumenstingl value = phy_read(phydev, AT803X_INTR_ENABLE); 29877a99394SZhao Qiang 299e6e4a556SMartin Blumenstingl if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 300e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_AUTONEG_ERR; 301e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_SPEED_CHANGED; 302e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; 303e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_LINK_FAIL; 304e6e4a556SMartin Blumenstingl value |= AT803X_INTR_ENABLE_LINK_SUCCESS; 305e6e4a556SMartin Blumenstingl 306e6e4a556SMartin Blumenstingl err = phy_write(phydev, AT803X_INTR_ENABLE, value); 307e6e4a556SMartin Blumenstingl } 30877a99394SZhao Qiang else 309a46bd63bSMartin Blumenstingl err = phy_write(phydev, AT803X_INTR_ENABLE, 0); 31077a99394SZhao Qiang 31177a99394SZhao Qiang return err; 31277a99394SZhao Qiang } 31377a99394SZhao Qiang 31413a56b44SDaniel Mack static void at803x_link_change_notify(struct phy_device *phydev) 31513a56b44SDaniel Mack { 31613a56b44SDaniel Mack /* 31713a56b44SDaniel Mack * Conduct a hardware reset for AT8030 every time a link loss is 31813a56b44SDaniel Mack * signalled. This is necessary to circumvent a hardware bug that 31913a56b44SDaniel Mack * occurs when the cable is unplugged while TX packets are pending 32013a56b44SDaniel Mack * in the FIFO. In such cases, the FIFO enters an error mode it 32113a56b44SDaniel Mack * cannot recover from by software. 32213a56b44SDaniel Mack */ 3236110ed2dSDavid Bauer if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) { 32413a56b44SDaniel Mack struct at803x_context context; 32513a56b44SDaniel Mack 32613a56b44SDaniel Mack at803x_context_save(phydev, &context); 32713a56b44SDaniel Mack 328bafbdd52SSergei Shtylyov phy_device_reset(phydev, 1); 32913a56b44SDaniel Mack msleep(1); 330bafbdd52SSergei Shtylyov phy_device_reset(phydev, 0); 331d57019d1SSergei Shtylyov msleep(1); 33213a56b44SDaniel Mack 33313a56b44SDaniel Mack at803x_context_restore(phydev, &context); 33413a56b44SDaniel Mack 3355c5f626bSHeiner Kallweit phydev_dbg(phydev, "%s(): phy was reset\n", __func__); 33613a56b44SDaniel Mack } 33713a56b44SDaniel Mack } 33813a56b44SDaniel Mack 339f62265b5SZefir Kurtisi static int at803x_aneg_done(struct phy_device *phydev) 340f62265b5SZefir Kurtisi { 341f62265b5SZefir Kurtisi int ccr; 342f62265b5SZefir Kurtisi 343f62265b5SZefir Kurtisi int aneg_done = genphy_aneg_done(phydev); 344f62265b5SZefir Kurtisi if (aneg_done != BMSR_ANEGCOMPLETE) 345f62265b5SZefir Kurtisi return aneg_done; 346f62265b5SZefir Kurtisi 347f62265b5SZefir Kurtisi /* 348f62265b5SZefir Kurtisi * in SGMII mode, if copper side autoneg is successful, 349f62265b5SZefir Kurtisi * also check SGMII side autoneg result 350f62265b5SZefir Kurtisi */ 351f62265b5SZefir Kurtisi ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); 352f62265b5SZefir Kurtisi if ((ccr & AT803X_MODE_CFG_MASK) != AT803X_MODE_CFG_SGMII) 353f62265b5SZefir Kurtisi return aneg_done; 354f62265b5SZefir Kurtisi 355f62265b5SZefir Kurtisi /* switch to SGMII/fiber page */ 356f62265b5SZefir Kurtisi phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL); 357f62265b5SZefir Kurtisi 358f62265b5SZefir Kurtisi /* check if the SGMII link is OK. */ 359f62265b5SZefir Kurtisi if (!(phy_read(phydev, AT803X_PSSR) & AT803X_PSSR_MR_AN_COMPLETE)) { 360ab2a605fSAndrew Lunn phydev_warn(phydev, "803x_aneg_done: SGMII link is not ok\n"); 361f62265b5SZefir Kurtisi aneg_done = 0; 362f62265b5SZefir Kurtisi } 363f62265b5SZefir Kurtisi /* switch back to copper page */ 364f62265b5SZefir Kurtisi phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL); 365f62265b5SZefir Kurtisi 366f62265b5SZefir Kurtisi return aneg_done; 367f62265b5SZefir Kurtisi } 368f62265b5SZefir Kurtisi 36906d5f344SRussell King static int at803x_read_status(struct phy_device *phydev) 37006d5f344SRussell King { 37106d5f344SRussell King int ss, err, old_link = phydev->link; 37206d5f344SRussell King 37306d5f344SRussell King /* Update the link, but return if there was an error */ 37406d5f344SRussell King err = genphy_update_link(phydev); 37506d5f344SRussell King if (err) 37606d5f344SRussell King return err; 37706d5f344SRussell King 37806d5f344SRussell King /* why bother the PHY if nothing can have changed */ 37906d5f344SRussell King if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) 38006d5f344SRussell King return 0; 38106d5f344SRussell King 38206d5f344SRussell King phydev->speed = SPEED_UNKNOWN; 38306d5f344SRussell King phydev->duplex = DUPLEX_UNKNOWN; 38406d5f344SRussell King phydev->pause = 0; 38506d5f344SRussell King phydev->asym_pause = 0; 38606d5f344SRussell King 38706d5f344SRussell King err = genphy_read_lpa(phydev); 38806d5f344SRussell King if (err < 0) 38906d5f344SRussell King return err; 39006d5f344SRussell King 39106d5f344SRussell King /* Read the AT8035 PHY-Specific Status register, which indicates the 39206d5f344SRussell King * speed and duplex that the PHY is actually using, irrespective of 39306d5f344SRussell King * whether we are in autoneg mode or not. 39406d5f344SRussell King */ 39506d5f344SRussell King ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); 39606d5f344SRussell King if (ss < 0) 39706d5f344SRussell King return ss; 39806d5f344SRussell King 39906d5f344SRussell King if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { 40006d5f344SRussell King switch (ss & AT803X_SS_SPEED_MASK) { 40106d5f344SRussell King case AT803X_SS_SPEED_10: 40206d5f344SRussell King phydev->speed = SPEED_10; 40306d5f344SRussell King break; 40406d5f344SRussell King case AT803X_SS_SPEED_100: 40506d5f344SRussell King phydev->speed = SPEED_100; 40606d5f344SRussell King break; 40706d5f344SRussell King case AT803X_SS_SPEED_1000: 40806d5f344SRussell King phydev->speed = SPEED_1000; 40906d5f344SRussell King break; 41006d5f344SRussell King } 41106d5f344SRussell King if (ss & AT803X_SS_DUPLEX) 41206d5f344SRussell King phydev->duplex = DUPLEX_FULL; 41306d5f344SRussell King else 41406d5f344SRussell King phydev->duplex = DUPLEX_HALF; 41506d5f344SRussell King if (ss & AT803X_SS_MDIX) 41606d5f344SRussell King phydev->mdix = ETH_TP_MDI_X; 41706d5f344SRussell King else 41806d5f344SRussell King phydev->mdix = ETH_TP_MDI; 41906d5f344SRussell King } 42006d5f344SRussell King 42106d5f344SRussell King if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) 42206d5f344SRussell King phy_resolve_aneg_pause(phydev); 42306d5f344SRussell King 42406d5f344SRussell King return 0; 42506d5f344SRussell King } 42606d5f344SRussell King 427317420abSMugunthan V N static struct phy_driver at803x_driver[] = { 428317420abSMugunthan V N { 4290ca7111aSMatus Ujhelyi /* ATHEROS 8035 */ 430bd8ca17fSDaniel Mack .phy_id = ATH8035_PHY_ID, 4310ca7111aSMatus Ujhelyi .name = "Atheros 8035 ethernet", 43258effd71SFabio Estevam .phy_id_mask = AT803X_PHY_ID_MASK, 43313a56b44SDaniel Mack .probe = at803x_probe, 4340ca7111aSMatus Ujhelyi .config_init = at803x_config_init, 435ea13c9eeSMugunthan V N .set_wol = at803x_set_wol, 436ea13c9eeSMugunthan V N .get_wol = at803x_get_wol, 4376229ed1fSDaniel Mack .suspend = at803x_suspend, 4386229ed1fSDaniel Mack .resume = at803x_resume, 439dcdecdcfSHeiner Kallweit /* PHY_GBIT_FEATURES */ 44006d5f344SRussell King .read_status = at803x_read_status, 4410eae5982SMåns Rullgård .ack_interrupt = at803x_ack_interrupt, 4420eae5982SMåns Rullgård .config_intr = at803x_config_intr, 443317420abSMugunthan V N }, { 4440ca7111aSMatus Ujhelyi /* ATHEROS 8030 */ 445bd8ca17fSDaniel Mack .phy_id = ATH8030_PHY_ID, 4460ca7111aSMatus Ujhelyi .name = "Atheros 8030 ethernet", 44758effd71SFabio Estevam .phy_id_mask = AT803X_PHY_ID_MASK, 44813a56b44SDaniel Mack .probe = at803x_probe, 4490ca7111aSMatus Ujhelyi .config_init = at803x_config_init, 45013a56b44SDaniel Mack .link_change_notify = at803x_link_change_notify, 451ea13c9eeSMugunthan V N .set_wol = at803x_set_wol, 452ea13c9eeSMugunthan V N .get_wol = at803x_get_wol, 4536229ed1fSDaniel Mack .suspend = at803x_suspend, 4546229ed1fSDaniel Mack .resume = at803x_resume, 455dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 4560eae5982SMåns Rullgård .ack_interrupt = at803x_ack_interrupt, 4570eae5982SMåns Rullgård .config_intr = at803x_config_intr, 45805d7cce8SMugunthan V N }, { 45905d7cce8SMugunthan V N /* ATHEROS 8031 */ 460bd8ca17fSDaniel Mack .phy_id = ATH8031_PHY_ID, 46105d7cce8SMugunthan V N .name = "Atheros 8031 ethernet", 46258effd71SFabio Estevam .phy_id_mask = AT803X_PHY_ID_MASK, 46313a56b44SDaniel Mack .probe = at803x_probe, 46405d7cce8SMugunthan V N .config_init = at803x_config_init, 46505d7cce8SMugunthan V N .set_wol = at803x_set_wol, 46605d7cce8SMugunthan V N .get_wol = at803x_get_wol, 4676229ed1fSDaniel Mack .suspend = at803x_suspend, 4686229ed1fSDaniel Mack .resume = at803x_resume, 469dcdecdcfSHeiner Kallweit /* PHY_GBIT_FEATURES */ 47006d5f344SRussell King .read_status = at803x_read_status, 471f62265b5SZefir Kurtisi .aneg_done = at803x_aneg_done, 47277a99394SZhao Qiang .ack_interrupt = &at803x_ack_interrupt, 47377a99394SZhao Qiang .config_intr = &at803x_config_intr, 474317420abSMugunthan V N } }; 4750ca7111aSMatus Ujhelyi 47650fd7150SJohan Hovold module_phy_driver(at803x_driver); 4770ca7111aSMatus Ujhelyi 4780ca7111aSMatus Ujhelyi static struct mdio_device_id __maybe_unused atheros_tbl[] = { 47958effd71SFabio Estevam { ATH8030_PHY_ID, AT803X_PHY_ID_MASK }, 48058effd71SFabio Estevam { ATH8031_PHY_ID, AT803X_PHY_ID_MASK }, 48158effd71SFabio Estevam { ATH8035_PHY_ID, AT803X_PHY_ID_MASK }, 4820ca7111aSMatus Ujhelyi { } 4830ca7111aSMatus Ujhelyi }; 4840ca7111aSMatus Ujhelyi 4850ca7111aSMatus Ujhelyi MODULE_DEVICE_TABLE(mdio, atheros_tbl); 486