linux/drivers/net/phy/dp83822.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Driver for the Texas Instruments DP83822, DP83825 and DP83826 PHYs.
   3 *
   4 * Copyright (C) 2017 Texas Instruments Inc.
   5 */
   6
   7#include <linux/ethtool.h>
   8#include <linux/etherdevice.h>
   9#include <linux/kernel.h>
  10#include <linux/mii.h>
  11#include <linux/module.h>
  12#include <linux/of.h>
  13#include <linux/phy.h>
  14#include <linux/netdevice.h>
  15
  16#define DP83822_PHY_ID          0x2000a240
  17#define DP83825S_PHY_ID         0x2000a140
  18#define DP83825I_PHY_ID         0x2000a150
  19#define DP83825CM_PHY_ID        0x2000a160
  20#define DP83825CS_PHY_ID        0x2000a170
  21#define DP83826C_PHY_ID         0x2000a130
  22#define DP83826NC_PHY_ID        0x2000a110
  23
  24#define DP83822_DEVADDR         0x1f
  25
  26#define MII_DP83822_PHYSCR      0x11
  27#define MII_DP83822_MISR1       0x12
  28#define MII_DP83822_MISR2       0x13
  29#define MII_DP83822_RESET_CTRL  0x1f
  30
  31#define DP83822_HW_RESET        BIT(15)
  32#define DP83822_SW_RESET        BIT(14)
  33
  34/* PHYSCR Register Fields */
  35#define DP83822_PHYSCR_INT_OE           BIT(0) /* Interrupt Output Enable */
  36#define DP83822_PHYSCR_INTEN            BIT(1) /* Interrupt Enable */
  37
  38/* MISR1 bits */
  39#define DP83822_RX_ERR_HF_INT_EN        BIT(0)
  40#define DP83822_FALSE_CARRIER_HF_INT_EN BIT(1)
  41#define DP83822_ANEG_COMPLETE_INT_EN    BIT(2)
  42#define DP83822_DUP_MODE_CHANGE_INT_EN  BIT(3)
  43#define DP83822_SPEED_CHANGED_INT_EN    BIT(4)
  44#define DP83822_LINK_STAT_INT_EN        BIT(5)
  45#define DP83822_ENERGY_DET_INT_EN       BIT(6)
  46#define DP83822_LINK_QUAL_INT_EN        BIT(7)
  47
  48/* MISR2 bits */
  49#define DP83822_JABBER_DET_INT_EN       BIT(0)
  50#define DP83822_WOL_PKT_INT_EN          BIT(1)
  51#define DP83822_SLEEP_MODE_INT_EN       BIT(2)
  52#define DP83822_MDI_XOVER_INT_EN        BIT(3)
  53#define DP83822_LB_FIFO_INT_EN          BIT(4)
  54#define DP83822_PAGE_RX_INT_EN          BIT(5)
  55#define DP83822_ANEG_ERR_INT_EN         BIT(6)
  56#define DP83822_EEE_ERROR_CHANGE_INT_EN BIT(7)
  57
  58/* INT_STAT1 bits */
  59#define DP83822_WOL_INT_EN      BIT(4)
  60#define DP83822_WOL_INT_STAT    BIT(12)
  61
  62#define MII_DP83822_RXSOP1      0x04a5
  63#define MII_DP83822_RXSOP2      0x04a6
  64#define MII_DP83822_RXSOP3      0x04a7
  65
  66/* WoL Registers */
  67#define MII_DP83822_WOL_CFG     0x04a0
  68#define MII_DP83822_WOL_STAT    0x04a1
  69#define MII_DP83822_WOL_DA1     0x04a2
  70#define MII_DP83822_WOL_DA2     0x04a3
  71#define MII_DP83822_WOL_DA3     0x04a4
  72
  73/* WoL bits */
  74#define DP83822_WOL_MAGIC_EN    BIT(0)
  75#define DP83822_WOL_SECURE_ON   BIT(5)
  76#define DP83822_WOL_EN          BIT(7)
  77#define DP83822_WOL_INDICATION_SEL BIT(8)
  78#define DP83822_WOL_CLR_INDICATION BIT(11)
  79
  80static int dp83822_ack_interrupt(struct phy_device *phydev)
  81{
  82        int err;
  83
  84        err = phy_read(phydev, MII_DP83822_MISR1);
  85        if (err < 0)
  86                return err;
  87
  88        err = phy_read(phydev, MII_DP83822_MISR2);
  89        if (err < 0)
  90                return err;
  91
  92        return 0;
  93}
  94
  95static int dp83822_set_wol(struct phy_device *phydev,
  96                           struct ethtool_wolinfo *wol)
  97{
  98        struct net_device *ndev = phydev->attached_dev;
  99        u16 value;
 100        const u8 *mac;
 101
 102        if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
 103                mac = (const u8 *)ndev->dev_addr;
 104
 105                if (!is_valid_ether_addr(mac))
 106                        return -EINVAL;
 107
 108                /* MAC addresses start with byte 5, but stored in mac[0].
 109                 * 822 PHYs store bytes 4|5, 2|3, 0|1
 110                 */
 111                phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA1,
 112                              (mac[1] << 8) | mac[0]);
 113                phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA2,
 114                              (mac[3] << 8) | mac[2]);
 115                phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA3,
 116                              (mac[5] << 8) | mac[4]);
 117
 118                value = phy_read_mmd(phydev, DP83822_DEVADDR,
 119                                     MII_DP83822_WOL_CFG);
 120                if (wol->wolopts & WAKE_MAGIC)
 121                        value |= DP83822_WOL_MAGIC_EN;
 122                else
 123                        value &= ~DP83822_WOL_MAGIC_EN;
 124
 125                if (wol->wolopts & WAKE_MAGICSECURE) {
 126                        phy_write_mmd(phydev, DP83822_DEVADDR,
 127                                      MII_DP83822_RXSOP1,
 128                                      (wol->sopass[1] << 8) | wol->sopass[0]);
 129                        phy_write_mmd(phydev, DP83822_DEVADDR,
 130                                      MII_DP83822_RXSOP2,
 131                                      (wol->sopass[3] << 8) | wol->sopass[2]);
 132                        phy_write_mmd(phydev, DP83822_DEVADDR,
 133                                      MII_DP83822_RXSOP3,
 134                                      (wol->sopass[5] << 8) | wol->sopass[4]);
 135                        value |= DP83822_WOL_SECURE_ON;
 136                } else {
 137                        value &= ~DP83822_WOL_SECURE_ON;
 138                }
 139
 140                value |= (DP83822_WOL_EN | DP83822_WOL_INDICATION_SEL |
 141                          DP83822_WOL_CLR_INDICATION);
 142                phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG,
 143                              value);
 144        } else {
 145                value = phy_read_mmd(phydev, DP83822_DEVADDR,
 146                                     MII_DP83822_WOL_CFG);
 147                value &= ~DP83822_WOL_EN;
 148                phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG,
 149                              value);
 150        }
 151
 152        return 0;
 153}
 154
 155static void dp83822_get_wol(struct phy_device *phydev,
 156                            struct ethtool_wolinfo *wol)
 157{
 158        int value;
 159        u16 sopass_val;
 160
 161        wol->supported = (WAKE_MAGIC | WAKE_MAGICSECURE);
 162        wol->wolopts = 0;
 163
 164        value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG);
 165
 166        if (value & DP83822_WOL_MAGIC_EN)
 167                wol->wolopts |= WAKE_MAGIC;
 168
 169        if (value & DP83822_WOL_SECURE_ON) {
 170                sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR,
 171                                          MII_DP83822_RXSOP1);
 172                wol->sopass[0] = (sopass_val & 0xff);
 173                wol->sopass[1] = (sopass_val >> 8);
 174
 175                sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR,
 176                                          MII_DP83822_RXSOP2);
 177                wol->sopass[2] = (sopass_val & 0xff);
 178                wol->sopass[3] = (sopass_val >> 8);
 179
 180                sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR,
 181                                          MII_DP83822_RXSOP3);
 182                wol->sopass[4] = (sopass_val & 0xff);
 183                wol->sopass[5] = (sopass_val >> 8);
 184
 185                wol->wolopts |= WAKE_MAGICSECURE;
 186        }
 187
 188        /* WoL is not enabled so set wolopts to 0 */
 189        if (!(value & DP83822_WOL_EN))
 190                wol->wolopts = 0;
 191}
 192
 193static int dp83822_config_intr(struct phy_device *phydev)
 194{
 195        int misr_status;
 196        int physcr_status;
 197        int err;
 198
 199        if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
 200                misr_status = phy_read(phydev, MII_DP83822_MISR1);
 201                if (misr_status < 0)
 202                        return misr_status;
 203
 204                misr_status |= (DP83822_RX_ERR_HF_INT_EN |
 205                                DP83822_FALSE_CARRIER_HF_INT_EN |
 206                                DP83822_ANEG_COMPLETE_INT_EN |
 207                                DP83822_DUP_MODE_CHANGE_INT_EN |
 208                                DP83822_SPEED_CHANGED_INT_EN |
 209                                DP83822_LINK_STAT_INT_EN |
 210                                DP83822_ENERGY_DET_INT_EN |
 211                                DP83822_LINK_QUAL_INT_EN);
 212
 213                err = phy_write(phydev, MII_DP83822_MISR1, misr_status);
 214                if (err < 0)
 215                        return err;
 216
 217                misr_status = phy_read(phydev, MII_DP83822_MISR2);
 218                if (misr_status < 0)
 219                        return misr_status;
 220
 221                misr_status |= (DP83822_JABBER_DET_INT_EN |
 222                                DP83822_WOL_PKT_INT_EN |
 223                                DP83822_SLEEP_MODE_INT_EN |
 224                                DP83822_MDI_XOVER_INT_EN |
 225                                DP83822_LB_FIFO_INT_EN |
 226                                DP83822_PAGE_RX_INT_EN |
 227                                DP83822_ANEG_ERR_INT_EN |
 228                                DP83822_EEE_ERROR_CHANGE_INT_EN);
 229
 230                err = phy_write(phydev, MII_DP83822_MISR2, misr_status);
 231                if (err < 0)
 232                        return err;
 233
 234                physcr_status = phy_read(phydev, MII_DP83822_PHYSCR);
 235                if (physcr_status < 0)
 236                        return physcr_status;
 237
 238                physcr_status |= DP83822_PHYSCR_INT_OE | DP83822_PHYSCR_INTEN;
 239
 240        } else {
 241                err = phy_write(phydev, MII_DP83822_MISR1, 0);
 242                if (err < 0)
 243                        return err;
 244
 245                err = phy_write(phydev, MII_DP83822_MISR1, 0);
 246                if (err < 0)
 247                        return err;
 248
 249                physcr_status = phy_read(phydev, MII_DP83822_PHYSCR);
 250                if (physcr_status < 0)
 251                        return physcr_status;
 252
 253                physcr_status &= ~DP83822_PHYSCR_INTEN;
 254        }
 255
 256        return phy_write(phydev, MII_DP83822_PHYSCR, physcr_status);
 257}
 258
 259static int dp83822_config_init(struct phy_device *phydev)
 260{
 261        int value;
 262
 263        value = DP83822_WOL_MAGIC_EN | DP83822_WOL_SECURE_ON | DP83822_WOL_EN;
 264
 265        return phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG,
 266              value);
 267}
 268
 269static int dp83822_phy_reset(struct phy_device *phydev)
 270{
 271        int err;
 272
 273        err = phy_write(phydev, MII_DP83822_RESET_CTRL, DP83822_HW_RESET);
 274        if (err < 0)
 275                return err;
 276
 277        dp83822_config_init(phydev);
 278
 279        return 0;
 280}
 281
 282static int dp83822_suspend(struct phy_device *phydev)
 283{
 284        int value;
 285
 286        value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG);
 287
 288        if (!(value & DP83822_WOL_EN))
 289                genphy_suspend(phydev);
 290
 291        return 0;
 292}
 293
 294static int dp83822_resume(struct phy_device *phydev)
 295{
 296        int value;
 297
 298        genphy_resume(phydev);
 299
 300        value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG);
 301
 302        phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, value |
 303                      DP83822_WOL_CLR_INDICATION);
 304
 305        return 0;
 306}
 307
 308#define DP83822_PHY_DRIVER(_id, _name)                          \
 309        {                                                       \
 310                PHY_ID_MATCH_MODEL(_id),                        \
 311                .name           = (_name),                      \
 312                /* PHY_BASIC_FEATURES */                        \
 313                .soft_reset     = dp83822_phy_reset,            \
 314                .config_init    = dp83822_config_init,          \
 315                .get_wol = dp83822_get_wol,                     \
 316                .set_wol = dp83822_set_wol,                     \
 317                .ack_interrupt = dp83822_ack_interrupt,         \
 318                .config_intr = dp83822_config_intr,             \
 319                .suspend = dp83822_suspend,                     \
 320                .resume = dp83822_resume,                       \
 321        }
 322
 323static struct phy_driver dp83822_driver[] = {
 324        DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822"),
 325        DP83822_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"),
 326        DP83822_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"),
 327        DP83822_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"),
 328        DP83822_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"),
 329        DP83822_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"),
 330        DP83822_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"),
 331};
 332module_phy_driver(dp83822_driver);
 333
 334static struct mdio_device_id __maybe_unused dp83822_tbl[] = {
 335        { DP83822_PHY_ID, 0xfffffff0 },
 336        { DP83825I_PHY_ID, 0xfffffff0 },
 337        { DP83826C_PHY_ID, 0xfffffff0 },
 338        { DP83826NC_PHY_ID, 0xfffffff0 },
 339        { DP83825S_PHY_ID, 0xfffffff0 },
 340        { DP83825CM_PHY_ID, 0xfffffff0 },
 341        { DP83825CS_PHY_ID, 0xfffffff0 },
 342        { },
 343};
 344MODULE_DEVICE_TABLE(mdio, dp83822_tbl);
 345
 346MODULE_DESCRIPTION("Texas Instruments DP83822 PHY driver");
 347MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");
 348MODULE_LICENSE("GPL v2");
 349