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        struct gpio_desc *gpiod_reset;
 196
 197        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 198        if (!priv)
 199                return -ENOMEM;
 200
 201        gpiod_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
 202        if (IS_ERR(gpiod_reset))
 203                return PTR_ERR(gpiod_reset);
 204
 205        priv->gpiod_reset = gpiod_reset;
 206
 207        phydev->priv = priv;
 208
 209        return 0;
 210}
 211
 212static int at803x_config_init(struct phy_device *phydev)
 213{
 214        int ret;
 215
 216        ret = genphy_config_init(phydev);
 217        if (ret < 0)
 218                return ret;
 219
 220        if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
 221                ret = phy_write(phydev, AT803X_DEBUG_ADDR,
 222                                AT803X_DEBUG_SYSTEM_MODE_CTRL);
 223                if (ret)
 224                        return ret;
 225                ret = phy_write(phydev, AT803X_DEBUG_DATA,
 226                                AT803X_DEBUG_RGMII_TX_CLK_DLY);
 227                if (ret)
 228                        return ret;
 229        }
 230
 231        return 0;
 232}
 233
 234static int at803x_ack_interrupt(struct phy_device *phydev)
 235{
 236        int err;
 237
 238        err = phy_read(phydev, AT803X_INSR);
 239
 240        return (err < 0) ? err : 0;
 241}
 242
 243static int at803x_config_intr(struct phy_device *phydev)
 244{
 245        int err;
 246        int value;
 247
 248        value = phy_read(phydev, AT803X_INER);
 249
 250        if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
 251                err = phy_write(phydev, AT803X_INER,
 252                                value | AT803X_INER_INIT);
 253        else
 254                err = phy_write(phydev, AT803X_INER, 0);
 255
 256        return err;
 257}
 258
 259static void at803x_link_change_notify(struct phy_device *phydev)
 260{
 261        struct at803x_priv *priv = phydev->priv;
 262
 263        /*
 264         * Conduct a hardware reset for AT8030 every time a link loss is
 265         * signalled. This is necessary to circumvent a hardware bug that
 266         * occurs when the cable is unplugged while TX packets are pending
 267         * in the FIFO. In such cases, the FIFO enters an error mode it
 268         * cannot recover from by software.
 269         */
 270        if (phydev->drv->phy_id == ATH8030_PHY_ID) {
 271                if (phydev->state == PHY_NOLINK) {
 272                        if (priv->gpiod_reset && !priv->phy_reset) {
 273                                struct at803x_context context;
 274
 275                                at803x_context_save(phydev, &context);
 276
 277                                gpiod_set_value(priv->gpiod_reset, 0);
 278                                msleep(1);
 279                                gpiod_set_value(priv->gpiod_reset, 1);
 280                                msleep(1);
 281
 282                                at803x_context_restore(phydev, &context);
 283
 284                                dev_dbg(&phydev->dev, "%s(): phy was reset\n",
 285                                        __func__);
 286                                priv->phy_reset = true;
 287                        }
 288                } else {
 289                        priv->phy_reset = false;
 290                }
 291        }
 292}
 293
 294static struct phy_driver at803x_driver[] = {
 295{
 296        /* ATHEROS 8035 */
 297        .phy_id                 = ATH8035_PHY_ID,
 298        .name                   = "Atheros 8035 ethernet",
 299        .phy_id_mask            = 0xffffffef,
 300        .probe                  = at803x_probe,
 301        .config_init            = at803x_config_init,
 302        .link_change_notify     = at803x_link_change_notify,
 303        .set_wol                = at803x_set_wol,
 304        .get_wol                = at803x_get_wol,
 305        .suspend                = at803x_suspend,
 306        .resume                 = at803x_resume,
 307        .features               = PHY_GBIT_FEATURES,
 308        .flags                  = PHY_HAS_INTERRUPT,
 309        .config_aneg            = genphy_config_aneg,
 310        .read_status            = genphy_read_status,
 311        .driver                 = {
 312                .owner = THIS_MODULE,
 313        },
 314}, {
 315        /* ATHEROS 8030 */
 316        .phy_id                 = ATH8030_PHY_ID,
 317        .name                   = "Atheros 8030 ethernet",
 318        .phy_id_mask            = 0xffffffef,
 319        .probe                  = at803x_probe,
 320        .config_init            = at803x_config_init,
 321        .link_change_notify     = at803x_link_change_notify,
 322        .set_wol                = at803x_set_wol,
 323        .get_wol                = at803x_get_wol,
 324        .suspend                = at803x_suspend,
 325        .resume                 = at803x_resume,
 326        .features               = PHY_GBIT_FEATURES,
 327        .flags                  = PHY_HAS_INTERRUPT,
 328        .config_aneg            = genphy_config_aneg,
 329        .read_status            = genphy_read_status,
 330        .driver                 = {
 331                .owner = THIS_MODULE,
 332        },
 333}, {
 334        /* ATHEROS 8031 */
 335        .phy_id                 = ATH8031_PHY_ID,
 336        .name                   = "Atheros 8031 ethernet",
 337        .phy_id_mask            = 0xffffffef,
 338        .probe                  = at803x_probe,
 339        .config_init            = at803x_config_init,
 340        .link_change_notify     = at803x_link_change_notify,
 341        .set_wol                = at803x_set_wol,
 342        .get_wol                = at803x_get_wol,
 343        .suspend                = at803x_suspend,
 344        .resume                 = at803x_resume,
 345        .features               = PHY_GBIT_FEATURES,
 346        .flags                  = PHY_HAS_INTERRUPT,
 347        .config_aneg            = genphy_config_aneg,
 348        .read_status            = genphy_read_status,
 349        .ack_interrupt          = &at803x_ack_interrupt,
 350        .config_intr            = &at803x_config_intr,
 351        .driver                 = {
 352                .owner = THIS_MODULE,
 353        },
 354} };
 355
 356module_phy_driver(at803x_driver);
 357
 358static struct mdio_device_id __maybe_unused atheros_tbl[] = {
 359        { ATH8030_PHY_ID, 0xffffffef },
 360        { ATH8031_PHY_ID, 0xffffffef },
 361        { ATH8035_PHY_ID, 0xffffffef },
 362        { }
 363};
 364
 365MODULE_DEVICE_TABLE(mdio, atheros_tbl);
 366