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 230ca7111aSMatus Ujhelyi #define AT803X_INTR_STATUS 0x13 2413a56b44SDaniel Mack #define AT803X_SMART_SPEED 0x14 2513a56b44SDaniel Mack #define AT803X_LED_CONTROL 0x18 260ca7111aSMatus Ujhelyi #define AT803X_WOL_ENABLE 0x01 270ca7111aSMatus Ujhelyi #define AT803X_DEVICE_ADDR 0x03 280ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C 290ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B 300ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A 310ca7111aSMatus Ujhelyi #define AT803X_MMD_ACCESS_CONTROL 0x0D 320ca7111aSMatus Ujhelyi #define AT803X_MMD_ACCESS_CONTROL_DATA 0x0E 330ca7111aSMatus Ujhelyi #define AT803X_FUNC_DATA 0x4003 3477a99394SZhao Qiang #define AT803X_INER 0x0012 3577a99394SZhao Qiang #define AT803X_INER_INIT 0xec00 3677a99394SZhao Qiang #define AT803X_INSR 0x0013 371ca6d1b1SMugunthan V N #define AT803X_DEBUG_ADDR 0x1D 381ca6d1b1SMugunthan V N #define AT803X_DEBUG_DATA 0x1E 391ca6d1b1SMugunthan V N #define AT803X_DEBUG_SYSTEM_MODE_CTRL 0x05 401ca6d1b1SMugunthan V N #define AT803X_DEBUG_RGMII_TX_CLK_DLY BIT(8) 410ca7111aSMatus Ujhelyi 42bd8ca17fSDaniel Mack #define ATH8030_PHY_ID 0x004dd076 43bd8ca17fSDaniel Mack #define ATH8031_PHY_ID 0x004dd074 44bd8ca17fSDaniel Mack #define ATH8035_PHY_ID 0x004dd072 45bd8ca17fSDaniel Mack 460ca7111aSMatus Ujhelyi MODULE_DESCRIPTION("Atheros 803x PHY driver"); 470ca7111aSMatus Ujhelyi MODULE_AUTHOR("Matus Ujhelyi"); 480ca7111aSMatus Ujhelyi MODULE_LICENSE("GPL"); 490ca7111aSMatus Ujhelyi 5013a56b44SDaniel Mack struct at803x_priv { 5113a56b44SDaniel Mack bool phy_reset:1; 5213a56b44SDaniel Mack struct gpio_desc *gpiod_reset; 5313a56b44SDaniel Mack }; 5413a56b44SDaniel Mack 5513a56b44SDaniel Mack struct at803x_context { 5613a56b44SDaniel Mack u16 bmcr; 5713a56b44SDaniel Mack u16 advertise; 5813a56b44SDaniel Mack u16 control1000; 5913a56b44SDaniel Mack u16 int_enable; 6013a56b44SDaniel Mack u16 smart_speed; 6113a56b44SDaniel Mack u16 led_control; 6213a56b44SDaniel Mack }; 6313a56b44SDaniel Mack 6413a56b44SDaniel Mack /* save relevant PHY registers to private copy */ 6513a56b44SDaniel Mack static void at803x_context_save(struct phy_device *phydev, 6613a56b44SDaniel Mack struct at803x_context *context) 6713a56b44SDaniel Mack { 6813a56b44SDaniel Mack context->bmcr = phy_read(phydev, MII_BMCR); 6913a56b44SDaniel Mack context->advertise = phy_read(phydev, MII_ADVERTISE); 7013a56b44SDaniel Mack context->control1000 = phy_read(phydev, MII_CTRL1000); 7113a56b44SDaniel Mack context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE); 7213a56b44SDaniel Mack context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED); 7313a56b44SDaniel Mack context->led_control = phy_read(phydev, AT803X_LED_CONTROL); 7413a56b44SDaniel Mack } 7513a56b44SDaniel Mack 7613a56b44SDaniel Mack /* restore relevant PHY registers from private copy */ 7713a56b44SDaniel Mack static void at803x_context_restore(struct phy_device *phydev, 7813a56b44SDaniel Mack const struct at803x_context *context) 7913a56b44SDaniel Mack { 8013a56b44SDaniel Mack phy_write(phydev, MII_BMCR, context->bmcr); 8113a56b44SDaniel Mack phy_write(phydev, MII_ADVERTISE, context->advertise); 8213a56b44SDaniel Mack phy_write(phydev, MII_CTRL1000, context->control1000); 8313a56b44SDaniel Mack phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable); 8413a56b44SDaniel Mack phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed); 8513a56b44SDaniel Mack phy_write(phydev, AT803X_LED_CONTROL, context->led_control); 8613a56b44SDaniel Mack } 8713a56b44SDaniel Mack 88ea13c9eeSMugunthan V N static int at803x_set_wol(struct phy_device *phydev, 89ea13c9eeSMugunthan V N struct ethtool_wolinfo *wol) 900ca7111aSMatus Ujhelyi { 910ca7111aSMatus Ujhelyi struct net_device *ndev = phydev->attached_dev; 920ca7111aSMatus Ujhelyi const u8 *mac; 93ea13c9eeSMugunthan V N int ret; 94ea13c9eeSMugunthan V N u32 value; 950ca7111aSMatus Ujhelyi unsigned int i, offsets[] = { 960ca7111aSMatus Ujhelyi AT803X_LOC_MAC_ADDR_32_47_OFFSET, 970ca7111aSMatus Ujhelyi AT803X_LOC_MAC_ADDR_16_31_OFFSET, 980ca7111aSMatus Ujhelyi AT803X_LOC_MAC_ADDR_0_15_OFFSET, 990ca7111aSMatus Ujhelyi }; 1000ca7111aSMatus Ujhelyi 1010ca7111aSMatus Ujhelyi if (!ndev) 102ea13c9eeSMugunthan V N return -ENODEV; 1030ca7111aSMatus Ujhelyi 104ea13c9eeSMugunthan V N if (wol->wolopts & WAKE_MAGIC) { 1050ca7111aSMatus Ujhelyi mac = (const u8 *) ndev->dev_addr; 1060ca7111aSMatus Ujhelyi 1070ca7111aSMatus Ujhelyi if (!is_valid_ether_addr(mac)) 108ea13c9eeSMugunthan V N return -EFAULT; 1090ca7111aSMatus Ujhelyi 1100ca7111aSMatus Ujhelyi for (i = 0; i < 3; i++) { 1110ca7111aSMatus Ujhelyi phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, 1120ca7111aSMatus Ujhelyi AT803X_DEVICE_ADDR); 1130ca7111aSMatus Ujhelyi phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, 1140ca7111aSMatus Ujhelyi offsets[i]); 1150ca7111aSMatus Ujhelyi phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, 1160ca7111aSMatus Ujhelyi AT803X_FUNC_DATA); 1170ca7111aSMatus Ujhelyi phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, 1180ca7111aSMatus Ujhelyi mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); 1190ca7111aSMatus Ujhelyi } 120ea13c9eeSMugunthan V N 121ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_ENABLE); 122ea13c9eeSMugunthan V N value |= AT803X_WOL_ENABLE; 123ea13c9eeSMugunthan V N ret = phy_write(phydev, AT803X_INTR_ENABLE, value); 124ea13c9eeSMugunthan V N if (ret) 125ea13c9eeSMugunthan V N return ret; 126ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_STATUS); 127ea13c9eeSMugunthan V N } else { 128ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_ENABLE); 129ea13c9eeSMugunthan V N value &= (~AT803X_WOL_ENABLE); 130ea13c9eeSMugunthan V N ret = phy_write(phydev, AT803X_INTR_ENABLE, value); 131ea13c9eeSMugunthan V N if (ret) 132ea13c9eeSMugunthan V N return ret; 133ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_STATUS); 134ea13c9eeSMugunthan V N } 135ea13c9eeSMugunthan V N 136ea13c9eeSMugunthan V N return ret; 137ea13c9eeSMugunthan V N } 138ea13c9eeSMugunthan V N 139ea13c9eeSMugunthan V N static void at803x_get_wol(struct phy_device *phydev, 140ea13c9eeSMugunthan V N struct ethtool_wolinfo *wol) 141ea13c9eeSMugunthan V N { 142ea13c9eeSMugunthan V N u32 value; 143ea13c9eeSMugunthan V N 144ea13c9eeSMugunthan V N wol->supported = WAKE_MAGIC; 145ea13c9eeSMugunthan V N wol->wolopts = 0; 146ea13c9eeSMugunthan V N 147ea13c9eeSMugunthan V N value = phy_read(phydev, AT803X_INTR_ENABLE); 148ea13c9eeSMugunthan V N if (value & AT803X_WOL_ENABLE) 149ea13c9eeSMugunthan V N wol->wolopts |= WAKE_MAGIC; 1500ca7111aSMatus Ujhelyi } 1510ca7111aSMatus Ujhelyi 1526229ed1fSDaniel Mack static int at803x_suspend(struct phy_device *phydev) 1536229ed1fSDaniel Mack { 1546229ed1fSDaniel Mack int value; 1556229ed1fSDaniel Mack int wol_enabled; 1566229ed1fSDaniel Mack 1576229ed1fSDaniel Mack mutex_lock(&phydev->lock); 1586229ed1fSDaniel Mack 1596229ed1fSDaniel Mack value = phy_read(phydev, AT803X_INTR_ENABLE); 1606229ed1fSDaniel Mack wol_enabled = value & AT803X_WOL_ENABLE; 1616229ed1fSDaniel Mack 1626229ed1fSDaniel Mack value = phy_read(phydev, MII_BMCR); 1636229ed1fSDaniel Mack 1646229ed1fSDaniel Mack if (wol_enabled) 1656229ed1fSDaniel Mack value |= BMCR_ISOLATE; 1666229ed1fSDaniel Mack else 1676229ed1fSDaniel Mack value |= BMCR_PDOWN; 1686229ed1fSDaniel Mack 1696229ed1fSDaniel Mack phy_write(phydev, MII_BMCR, value); 1706229ed1fSDaniel Mack 1716229ed1fSDaniel Mack mutex_unlock(&phydev->lock); 1726229ed1fSDaniel Mack 1736229ed1fSDaniel Mack return 0; 1746229ed1fSDaniel Mack } 1756229ed1fSDaniel Mack 1766229ed1fSDaniel Mack static int at803x_resume(struct phy_device *phydev) 1776229ed1fSDaniel Mack { 1786229ed1fSDaniel Mack int value; 1796229ed1fSDaniel Mack 1806229ed1fSDaniel Mack mutex_lock(&phydev->lock); 1816229ed1fSDaniel Mack 1826229ed1fSDaniel Mack value = phy_read(phydev, MII_BMCR); 1836229ed1fSDaniel Mack value &= ~(BMCR_PDOWN | BMCR_ISOLATE); 1846229ed1fSDaniel Mack phy_write(phydev, MII_BMCR, value); 1856229ed1fSDaniel Mack 1866229ed1fSDaniel Mack mutex_unlock(&phydev->lock); 1876229ed1fSDaniel Mack 1886229ed1fSDaniel Mack return 0; 1896229ed1fSDaniel Mack } 1906229ed1fSDaniel Mack 19113a56b44SDaniel Mack static int at803x_probe(struct phy_device *phydev) 19213a56b44SDaniel Mack { 19313a56b44SDaniel Mack struct device *dev = &phydev->dev; 19413a56b44SDaniel Mack struct at803x_priv *priv; 19513a56b44SDaniel Mack 1968f2877caSFengguang Wu priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 19713a56b44SDaniel Mack if (!priv) 19813a56b44SDaniel Mack return -ENOMEM; 19913a56b44SDaniel Mack 20013a56b44SDaniel Mack priv->gpiod_reset = devm_gpiod_get(dev, "reset"); 20113a56b44SDaniel Mack if (IS_ERR(priv->gpiod_reset)) 20213a56b44SDaniel Mack priv->gpiod_reset = NULL; 20313a56b44SDaniel Mack else 20413a56b44SDaniel Mack gpiod_direction_output(priv->gpiod_reset, 1); 20513a56b44SDaniel Mack 20613a56b44SDaniel Mack phydev->priv = priv; 20713a56b44SDaniel Mack 20813a56b44SDaniel Mack return 0; 20913a56b44SDaniel Mack } 21013a56b44SDaniel Mack 2110ca7111aSMatus Ujhelyi static int at803x_config_init(struct phy_device *phydev) 2120ca7111aSMatus Ujhelyi { 2131ca6d1b1SMugunthan V N int ret; 2140ca7111aSMatus Ujhelyi 2156ff01dbbSDaniel Mack ret = genphy_config_init(phydev); 2166ff01dbbSDaniel Mack if (ret < 0) 2176ff01dbbSDaniel Mack return ret; 2180ca7111aSMatus Ujhelyi 2191ca6d1b1SMugunthan V N if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { 2201ca6d1b1SMugunthan V N ret = phy_write(phydev, AT803X_DEBUG_ADDR, 2211ca6d1b1SMugunthan V N AT803X_DEBUG_SYSTEM_MODE_CTRL); 2221ca6d1b1SMugunthan V N if (ret) 2231ca6d1b1SMugunthan V N return ret; 2241ca6d1b1SMugunthan V N ret = phy_write(phydev, AT803X_DEBUG_DATA, 2251ca6d1b1SMugunthan V N AT803X_DEBUG_RGMII_TX_CLK_DLY); 2261ca6d1b1SMugunthan V N if (ret) 2271ca6d1b1SMugunthan V N return ret; 2281ca6d1b1SMugunthan V N } 2291ca6d1b1SMugunthan V N 2300ca7111aSMatus Ujhelyi return 0; 2310ca7111aSMatus Ujhelyi } 2320ca7111aSMatus Ujhelyi 23377a99394SZhao Qiang static int at803x_ack_interrupt(struct phy_device *phydev) 23477a99394SZhao Qiang { 23577a99394SZhao Qiang int err; 23677a99394SZhao Qiang 23777a99394SZhao Qiang err = phy_read(phydev, AT803X_INSR); 23877a99394SZhao Qiang 23977a99394SZhao Qiang return (err < 0) ? err : 0; 24077a99394SZhao Qiang } 24177a99394SZhao Qiang 24277a99394SZhao Qiang static int at803x_config_intr(struct phy_device *phydev) 24377a99394SZhao Qiang { 24477a99394SZhao Qiang int err; 24577a99394SZhao Qiang int value; 24677a99394SZhao Qiang 24777a99394SZhao Qiang value = phy_read(phydev, AT803X_INER); 24877a99394SZhao Qiang 24977a99394SZhao Qiang if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 25077a99394SZhao Qiang err = phy_write(phydev, AT803X_INER, 25177a99394SZhao Qiang value | AT803X_INER_INIT); 25277a99394SZhao Qiang else 25377a99394SZhao Qiang err = phy_write(phydev, AT803X_INER, 0); 25477a99394SZhao Qiang 25577a99394SZhao Qiang return err; 25677a99394SZhao Qiang } 25777a99394SZhao Qiang 25813a56b44SDaniel Mack static void at803x_link_change_notify(struct phy_device *phydev) 25913a56b44SDaniel Mack { 26013a56b44SDaniel Mack struct at803x_priv *priv = phydev->priv; 26113a56b44SDaniel Mack 26213a56b44SDaniel Mack /* 26313a56b44SDaniel Mack * Conduct a hardware reset for AT8030 every time a link loss is 26413a56b44SDaniel Mack * signalled. This is necessary to circumvent a hardware bug that 26513a56b44SDaniel Mack * occurs when the cable is unplugged while TX packets are pending 26613a56b44SDaniel Mack * in the FIFO. In such cases, the FIFO enters an error mode it 26713a56b44SDaniel Mack * cannot recover from by software. 26813a56b44SDaniel Mack */ 26913a56b44SDaniel Mack if (phydev->drv->phy_id == ATH8030_PHY_ID) { 27013a56b44SDaniel Mack if (phydev->state == PHY_NOLINK) { 27113a56b44SDaniel Mack if (priv->gpiod_reset && !priv->phy_reset) { 27213a56b44SDaniel Mack struct at803x_context context; 27313a56b44SDaniel Mack 27413a56b44SDaniel Mack at803x_context_save(phydev, &context); 27513a56b44SDaniel Mack 27613a56b44SDaniel Mack gpiod_set_value(priv->gpiod_reset, 0); 27713a56b44SDaniel Mack msleep(1); 27813a56b44SDaniel Mack gpiod_set_value(priv->gpiod_reset, 1); 27913a56b44SDaniel Mack msleep(1); 28013a56b44SDaniel Mack 28113a56b44SDaniel Mack at803x_context_restore(phydev, &context); 28213a56b44SDaniel Mack 28313a56b44SDaniel Mack dev_dbg(&phydev->dev, "%s(): phy was reset\n", 28413a56b44SDaniel Mack __func__); 28513a56b44SDaniel Mack priv->phy_reset = true; 28613a56b44SDaniel Mack } 28713a56b44SDaniel Mack } else { 28813a56b44SDaniel Mack priv->phy_reset = false; 28913a56b44SDaniel Mack } 29013a56b44SDaniel Mack } 29113a56b44SDaniel Mack } 29213a56b44SDaniel Mack 293317420abSMugunthan V N static struct phy_driver at803x_driver[] = { 294317420abSMugunthan V N { 2950ca7111aSMatus Ujhelyi /* ATHEROS 8035 */ 296bd8ca17fSDaniel Mack .phy_id = ATH8035_PHY_ID, 2970ca7111aSMatus Ujhelyi .name = "Atheros 8035 ethernet", 2980ca7111aSMatus Ujhelyi .phy_id_mask = 0xffffffef, 29913a56b44SDaniel Mack .probe = at803x_probe, 3000ca7111aSMatus Ujhelyi .config_init = at803x_config_init, 30113a56b44SDaniel Mack .link_change_notify = at803x_link_change_notify, 302ea13c9eeSMugunthan V N .set_wol = at803x_set_wol, 303ea13c9eeSMugunthan V N .get_wol = at803x_get_wol, 3046229ed1fSDaniel Mack .suspend = at803x_suspend, 3056229ed1fSDaniel Mack .resume = at803x_resume, 3060ca7111aSMatus Ujhelyi .features = PHY_GBIT_FEATURES, 3070ca7111aSMatus Ujhelyi .flags = PHY_HAS_INTERRUPT, 3080197ffedSDaniel Mack .config_aneg = genphy_config_aneg, 3090197ffedSDaniel Mack .read_status = genphy_read_status, 3100ca7111aSMatus Ujhelyi .driver = { 3110ca7111aSMatus Ujhelyi .owner = THIS_MODULE, 3120ca7111aSMatus Ujhelyi }, 313317420abSMugunthan V N }, { 3140ca7111aSMatus Ujhelyi /* ATHEROS 8030 */ 315bd8ca17fSDaniel Mack .phy_id = ATH8030_PHY_ID, 3160ca7111aSMatus Ujhelyi .name = "Atheros 8030 ethernet", 3170ca7111aSMatus Ujhelyi .phy_id_mask = 0xffffffef, 31813a56b44SDaniel Mack .probe = at803x_probe, 3190ca7111aSMatus Ujhelyi .config_init = at803x_config_init, 32013a56b44SDaniel Mack .link_change_notify = at803x_link_change_notify, 321ea13c9eeSMugunthan V N .set_wol = at803x_set_wol, 322ea13c9eeSMugunthan V N .get_wol = at803x_get_wol, 3236229ed1fSDaniel Mack .suspend = at803x_suspend, 3246229ed1fSDaniel Mack .resume = at803x_resume, 3250ca7111aSMatus Ujhelyi .features = PHY_GBIT_FEATURES, 3260ca7111aSMatus Ujhelyi .flags = PHY_HAS_INTERRUPT, 3270197ffedSDaniel Mack .config_aneg = genphy_config_aneg, 3280197ffedSDaniel Mack .read_status = genphy_read_status, 3290ca7111aSMatus Ujhelyi .driver = { 3300ca7111aSMatus Ujhelyi .owner = THIS_MODULE, 3310ca7111aSMatus Ujhelyi }, 33205d7cce8SMugunthan V N }, { 33305d7cce8SMugunthan V N /* ATHEROS 8031 */ 334bd8ca17fSDaniel Mack .phy_id = ATH8031_PHY_ID, 33505d7cce8SMugunthan V N .name = "Atheros 8031 ethernet", 33605d7cce8SMugunthan V N .phy_id_mask = 0xffffffef, 33713a56b44SDaniel Mack .probe = at803x_probe, 33805d7cce8SMugunthan V N .config_init = at803x_config_init, 33913a56b44SDaniel Mack .link_change_notify = at803x_link_change_notify, 34005d7cce8SMugunthan V N .set_wol = at803x_set_wol, 34105d7cce8SMugunthan V N .get_wol = at803x_get_wol, 3426229ed1fSDaniel Mack .suspend = at803x_suspend, 3436229ed1fSDaniel Mack .resume = at803x_resume, 34405d7cce8SMugunthan V N .features = PHY_GBIT_FEATURES, 34505d7cce8SMugunthan V N .flags = PHY_HAS_INTERRUPT, 3460197ffedSDaniel Mack .config_aneg = genphy_config_aneg, 3470197ffedSDaniel Mack .read_status = genphy_read_status, 34877a99394SZhao Qiang .ack_interrupt = &at803x_ack_interrupt, 34977a99394SZhao Qiang .config_intr = &at803x_config_intr, 35005d7cce8SMugunthan V N .driver = { 35105d7cce8SMugunthan V N .owner = THIS_MODULE, 35205d7cce8SMugunthan V N }, 353317420abSMugunthan V N } }; 3540ca7111aSMatus Ujhelyi 3550ca7111aSMatus Ujhelyi static int __init atheros_init(void) 3560ca7111aSMatus Ujhelyi { 357317420abSMugunthan V N return phy_drivers_register(at803x_driver, 358317420abSMugunthan V N ARRAY_SIZE(at803x_driver)); 3590ca7111aSMatus Ujhelyi } 3600ca7111aSMatus Ujhelyi 3610ca7111aSMatus Ujhelyi static void __exit atheros_exit(void) 3620ca7111aSMatus Ujhelyi { 3632ebb1582SShruti Kanetkar phy_drivers_unregister(at803x_driver, ARRAY_SIZE(at803x_driver)); 3640ca7111aSMatus Ujhelyi } 3650ca7111aSMatus Ujhelyi 3660ca7111aSMatus Ujhelyi module_init(atheros_init); 3670ca7111aSMatus Ujhelyi module_exit(atheros_exit); 3680ca7111aSMatus Ujhelyi 3690ca7111aSMatus Ujhelyi static struct mdio_device_id __maybe_unused atheros_tbl[] = { 370bd8ca17fSDaniel Mack { ATH8030_PHY_ID, 0xffffffef }, 371bd8ca17fSDaniel Mack { ATH8031_PHY_ID, 0xffffffef }, 372bd8ca17fSDaniel Mack { ATH8035_PHY_ID, 0xffffffef }, 3730ca7111aSMatus Ujhelyi { } 3740ca7111aSMatus Ujhelyi }; 3750ca7111aSMatus Ujhelyi 3760ca7111aSMatus Ujhelyi MODULE_DEVICE_TABLE(mdio, atheros_tbl); 377