linux/drivers/net/phy/at803x.c
<<
>>
Prefs
   1/*
   2 * drivers/net/phy/at803x.c
   3 *
   4 * Driver for Atheros 803x PHY
   5 *
   6 * Author: Matus Ujhelyi <ujhelyi.m@gmail.com>
   7 *
   8 * This program is free software; you can redistribute  it and/or modify it
   9 * under  the terms of  the GNU General  Public License as published by the
  10 * Free Software Foundation;  either version 2 of the  License, or (at your
  11 * option) any later version.
  12 */
  13
  14#include <linux/phy.h>
  15#include <linux/module.h>
  16#include <linux/string.h>
  17#include <linux/netdevice.h>
  18#include <linux/etherdevice.h>
  19#include <linux/of_gpio.h>
  20#include <linux/gpio/consumer.h>
  21
  22#define AT803X_INTR_ENABLE                      0x12
  23#define AT803X_INTR_STATUS                      0x13
  24#define AT803X_SMART_SPEED                      0x14
  25#define AT803X_LED_CONTROL                      0x18
  26#define AT803X_WOL_ENABLE                       0x01
  27#define AT803X_DEVICE_ADDR                      0x03
  28#define AT803X_LOC_MAC_ADDR_0_15_OFFSET         0x804C
  29#define AT803X_LOC_MAC_ADDR_16_31_OFFSET        0x804B
  30#define AT803X_LOC_MAC_ADDR_32_47_OFFSET        0x804A
  31#define AT803X_MMD_ACCESS_CONTROL               0x0D
  32#define AT803X_MMD_ACCESS_CONTROL_DATA          0x0E
  33#define AT803X_FUNC_DATA                        0x4003
  34#define AT803X_INER                             0x0012
  35#define AT803X_INER_INIT                        0xec00
  36#define AT803X_INSR                             0x0013
  37#define AT803X_DEBUG_ADDR                       0x1D
  38#define AT803X_DEBUG_DATA                       0x1E
  39#define AT803X_DEBUG_SYSTEM_MODE_CTRL           0x05
  40#define AT803X_DEBUG_RGMII_TX_CLK_DLY           BIT(8)
  41
  42#define ATH8030_PHY_ID 0x004dd076
  43#define ATH8031_PHY_ID 0x004dd074
  44#define ATH8035_PHY_ID 0x004dd072
  45
  46MODULE_DESCRIPTION("Atheros 803x PHY driver");
  47MODULE_AUTHOR("Matus Ujhelyi");
  48MODULE_LICENSE("GPL");
  49
  50struct at803x_priv {
  51        bool phy_reset:1;
  52        struct gpio_desc *gpiod_reset;
  53};
  54
  55struct at803x_context {
  56        u16 bmcr;
  57        u16 advertise;
  58        u16 control1000;
  59        u16 int_enable;
  60        u16 smart_speed;
  61        u16 led_control;
  62};
  63
  64/* save relevant PHY registers to private copy */
  65static void at803x_context_save(struct phy_device *phydev,
  66                                struct at803x_context *context)
  67{
  68        context->bmcr = phy_read(phydev, MII_BMCR);
  69        context->advertise = phy_read(phydev, MII_ADVERTISE);
  70        context->control1000 = phy_read(phydev, MII_CTRL1000);
  71        context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE);
  72        context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED);
  73        context->led_control = phy_read(phydev, AT803X_LED_CONTROL);
  74}
  75
  76/* restore relevant PHY registers from private copy */
  77static void at803x_context_restore(struct phy_device *phydev,
  78                                   const struct at803x_context *context)
  79{
  80        phy_write(phydev, MII_BMCR, context->bmcr);
  81        phy_write(phydev, MII_ADVERTISE, context->advertise);
  82        phy_write(phydev, MII_CTRL1000, context->control1000);
  83        phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable);
  84        phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed);
  85        phy_write(phydev, AT803X_LED_CONTROL, context->led_control);
  86}
  87
  88static int at803x_set_wol(struct phy_device *phydev,
  89                          struct ethtool_wolinfo *wol)
  90{
  91        struct net_device *ndev = phydev->attached_dev;
  92        const u8 *mac;
  93        int ret;
  94        u32 value;
  95        unsigned int i, offsets[] = {
  96                AT803X_LOC_MAC_ADDR_32_47_OFFSET,
  97                AT803X_LOC_MAC_ADDR_16_31_OFFSET,
  98                AT803X_LOC_MAC_ADDR_0_15_OFFSET,
  99        };
 100
 101        if (!ndev)
 102                return -ENODEV;
 103
 104        if (wol->wolopts & WAKE_MAGIC) {
 105                mac = (const u8 *) ndev->dev_addr;
 106
 107                if (!is_valid_ether_addr(mac))
 108                        return -EFAULT;
 109
 110                for (i = 0; i < 3; i++) {
 111                        phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
 112                                  AT803X_DEVICE_ADDR);
 113                        phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
 114                                  offsets[i]);
 115                        phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
 116                                  AT803X_FUNC_DATA);
 117                        phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
 118                                  mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
 119                }
 120
 121                value = phy_read(phydev, AT803X_INTR_ENABLE);
 122                value |= AT803X_WOL_ENABLE;
 123                ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
 124                if (ret)
 125                        return ret;
 126                value = phy_read(phydev, AT803X_INTR_STATUS);
 127        } else {
 128                value = phy_read(phydev, AT803X_INTR_ENABLE);
 129                value &= (~AT803X_WOL_ENABLE);
 130                ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
 131                if (ret)
 132                        return ret;
 133                value = phy_read(phydev, AT803X_INTR_STATUS);
 134        }
 135
 136        return ret;
 137}
 138
 139static void at803x_get_wol(struct phy_device *phydev,
 140                           struct ethtool_wolinfo *wol)
 141{
 142        u32 value;
 143
 144        wol->supported = WAKE_MAGIC;
 145        wol->wolopts = 0;
 146
 147        value = phy_read(phydev, AT803X_INTR_ENABLE);
 148        if (value & AT803X_WOL_ENABLE)
 149                wol->wolopts |= WAKE_MAGIC;
 150}
 151
 152static int at803x_suspend(struct phy_device *phydev)
 153{
 154        int value;
 155        int wol_enabled;
 156
 157        mutex_lock(&phydev->lock);
 158
 159        value = phy_read(phydev, AT803X_INTR_ENABLE);
 160        wol_enabled = value & AT803X_WOL_ENABLE;
 161
 162        value = phy_read(phydev, MII_BMCR);
 163
 164        if (wol_enabled)
 165                value |= BMCR_ISOLATE;
 166        else
 167                value |= BMCR_PDOWN;
 168
 169        phy_write(phydev, MII_BMCR, value);
 170
 171        mutex_unlock(&phydev->lock);
 172
 173        return 0;
 174}
 175
 176static int at803x_resume(struct phy_device *phydev)
 177{
 178        int value;
 179
 180        mutex_lock(&phydev->lock);
 181
 182        value = phy_read(phydev, MII_BMCR);
 183        value &= ~(BMCR_PDOWN | BMCR_ISOLATE);
 184        phy_write(phydev, MII_BMCR, value);
 185
 186        mutex_unlock(&phydev->lock);
 187
 188        return 0;
 189}
 190
 191static int at803x_probe(struct phy_device *phydev)
 192{
 193        struct device *dev = &phydev->dev;
 194        struct at803x_priv *priv;
 195
 196        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 197        if (!priv)
 198                return -ENOMEM;
 199
 200        priv->gpiod_reset = devm_gpiod_get(dev, "reset");
 201        if (IS_ERR(priv->gpiod_reset))
 202                priv->gpiod_reset = NULL;
 203        else
 204                gpiod_direction_output(priv->gpiod_reset, 1);
 205
 206        phydev->priv = priv;
 207
 208        return 0;
 209}
 210
 211static int at803x_config_init(struct phy_device *phydev)
 212{
 213        int ret;
 214
 215        ret = genphy_config_init(phydev);
 216        if (ret < 0)
 217                return ret;
 218
 219        if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
 220                ret = phy_write(phydev, AT803X_DEBUG_ADDR,
 221                                AT803X_DEBUG_SYSTEM_MODE_CTRL);
 222                if (ret)
 223                        return ret;
 224                ret = phy_write(phydev, AT803X_DEBUG_DATA,
 225                                AT803X_DEBUG_RGMII_TX_CLK_DLY);
 226                if (ret)
 227                        return ret;
 228        }
 229
 230        return 0;
 231}
 232
 233static int at803x_ack_interrupt(struct phy_device *phydev)
 234{
 235        int err;
 236
 237        err = phy_read(phydev, AT803X_INSR);
 238
 239        return (err < 0) ? err : 0;
 240}
 241
 242static int at803x_config_intr(struct phy_device *phydev)
 243{
 244        int err;
 245        int value;
 246
 247        value = phy_read(phydev, AT803X_INER);
 248
 249        if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
 250                err = phy_write(phydev, AT803X_INER,
 251                                value | AT803X_INER_INIT);
 252        else
 253                err = phy_write(phydev, AT803X_INER, 0);
 254
 255        return err;
 256}
 257
 258static void at803x_link_change_notify(struct phy_device *phydev)
 259{
 260        struct at803x_priv *priv = phydev->priv;
 261
 262        /*
 263         * Conduct a hardware reset for AT8030 every time a link loss is
 264         * signalled. This is necessary to circumvent a hardware bug that
 265         * occurs when the cable is unplugged while TX packets are pending
 266         * in the FIFO. In such cases, the FIFO enters an error mode it
 267         * cannot recover from by software.
 268         */
 269        if (phydev->drv->phy_id == ATH8030_PHY_ID) {
 270                if (phydev->state == PHY_NOLINK) {
 271                        if (priv->gpiod_reset && !priv->phy_reset) {
 272                                struct at803x_context context;
 273
 274                                at803x_context_save(phydev, &context);
 275
 276                                gpiod_set_value(priv->gpiod_reset, 0);
 277                                msleep(1);
 278                                gpiod_set_value(priv->gpiod_reset, 1);
 279                                msleep(1);
 280
 281                                at803x_context_restore(phydev, &context);
 282
 283                                dev_dbg(&phydev->dev, "%s(): phy was reset\n",
 284                                        __func__);
 285                                priv->phy_reset = true;
 286                        }
 287                } else {
 288                        priv->phy_reset = false;
 289                }
 290        }
 291}
 292
 293static struct phy_driver at803x_driver[] = {
 294{
 295        /* ATHEROS 8035 */
 296        .phy_id                 = ATH8035_PHY_ID,
 297        .name                   = "Atheros 8035 ethernet",
 298        .phy_id_mask            = 0xffffffef,
 299        .probe                  = at803x_probe,
 300        .config_init            = at803x_config_init,
 301        .link_change_notify     = at803x_link_change_notify,
 302        .set_wol                = at803x_set_wol,
 303        .get_wol                = at803x_get_wol,
 304        .suspend                = at803x_suspend,
 305        .resume                 = at803x_resume,
 306        .features               = PHY_GBIT_FEATURES,
 307        .flags                  = PHY_HAS_INTERRUPT,
 308        .config_aneg            = genphy_config_aneg,
 309        .read_status            = genphy_read_status,
 310        .driver                 = {
 311                .owner = THIS_MODULE,
 312        },
 313}, {
 314        /* ATHEROS 8030 */
 315        .phy_id                 = ATH8030_PHY_ID,
 316        .name                   = "Atheros 8030 ethernet",
 317        .phy_id_mask            = 0xffffffef,
 318        .probe                  = at803x_probe,
 319        .config_init            = at803x_config_init,
 320        .link_change_notify     = at803x_link_change_notify,
 321        .set_wol                = at803x_set_wol,
 322        .get_wol                = at803x_get_wol,
 323        .suspend                = at803x_suspend,
 324        .resume                 = at803x_resume,
 325        .features               = PHY_GBIT_FEATURES,
 326        .flags                  = PHY_HAS_INTERRUPT,
 327        .config_aneg            = genphy_config_aneg,
 328        .read_status            = genphy_read_status,
 329        .driver                 = {
 330                .owner = THIS_MODULE,
 331        },
 332}, {
 333        /* ATHEROS 8031 */
 334        .phy_id                 = ATH8031_PHY_ID,
 335        .name                   = "Atheros 8031 ethernet",
 336        .phy_id_mask            = 0xffffffef,
 337        .probe                  = at803x_probe,
 338        .config_init            = at803x_config_init,
 339        .link_change_notify     = at803x_link_change_notify,
 340        .set_wol                = at803x_set_wol,
 341        .get_wol                = at803x_get_wol,
 342        .suspend                = at803x_suspend,
 343        .resume                 = at803x_resume,
 344        .features               = PHY_GBIT_FEATURES,
 345        .flags                  = PHY_HAS_INTERRUPT,
 346        .config_aneg            = genphy_config_aneg,
 347        .read_status            = genphy_read_status,
 348        .ack_interrupt          = &at803x_ack_interrupt,
 349        .config_intr            = &at803x_config_intr,
 350        .driver                 = {
 351                .owner = THIS_MODULE,
 352        },
 353} };
 354
 355static int __init atheros_init(void)
 356{
 357        return phy_drivers_register(at803x_driver,
 358                                    ARRAY_SIZE(at803x_driver));
 359}
 360
 361static void __exit atheros_exit(void)
 362{
 363        phy_drivers_unregister(at803x_driver, ARRAY_SIZE(at803x_driver));
 364}
 365
 366module_init(atheros_init);
 367module_exit(atheros_exit);
 368
 369static struct mdio_device_id __maybe_unused atheros_tbl[] = {
 370        { ATH8030_PHY_ID, 0xffffffef },
 371        { ATH8031_PHY_ID, 0xffffffef },
 372        { ATH8035_PHY_ID, 0xffffffef },
 373        { }
 374};
 375
 376MODULE_DEVICE_TABLE(mdio, atheros_tbl);
 377