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