linux/drivers/net/phy/dp83tc811.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Driver for the Texas Instruments DP83TC811 PHY
   4 *
   5 * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
   6 *
   7 */
   8
   9#include <linux/ethtool.h>
  10#include <linux/etherdevice.h>
  11#include <linux/kernel.h>
  12#include <linux/mii.h>
  13#include <linux/module.h>
  14#include <linux/of.h>
  15#include <linux/phy.h>
  16#include <linux/netdevice.h>
  17
  18#define DP83TC811_PHY_ID        0x2000a253
  19#define DP83811_DEVADDR         0x1f
  20
  21#define MII_DP83811_SGMII_CTRL  0x09
  22#define MII_DP83811_INT_STAT1   0x12
  23#define MII_DP83811_INT_STAT2   0x13
  24#define MII_DP83811_INT_STAT3   0x18
  25#define MII_DP83811_RESET_CTRL  0x1f
  26
  27#define DP83811_HW_RESET        BIT(15)
  28#define DP83811_SW_RESET        BIT(14)
  29
  30/* INT_STAT1 bits */
  31#define DP83811_RX_ERR_HF_INT_EN        BIT(0)
  32#define DP83811_MS_TRAINING_INT_EN      BIT(1)
  33#define DP83811_ANEG_COMPLETE_INT_EN    BIT(2)
  34#define DP83811_ESD_EVENT_INT_EN        BIT(3)
  35#define DP83811_WOL_INT_EN              BIT(4)
  36#define DP83811_LINK_STAT_INT_EN        BIT(5)
  37#define DP83811_ENERGY_DET_INT_EN       BIT(6)
  38#define DP83811_LINK_QUAL_INT_EN        BIT(7)
  39
  40/* INT_STAT2 bits */
  41#define DP83811_JABBER_DET_INT_EN       BIT(0)
  42#define DP83811_POLARITY_INT_EN         BIT(1)
  43#define DP83811_SLEEP_MODE_INT_EN       BIT(2)
  44#define DP83811_OVERTEMP_INT_EN         BIT(3)
  45#define DP83811_OVERVOLTAGE_INT_EN      BIT(6)
  46#define DP83811_UNDERVOLTAGE_INT_EN     BIT(7)
  47
  48/* INT_STAT3 bits */
  49#define DP83811_LPS_INT_EN      BIT(0)
  50#define DP83811_NO_FRAME_INT_EN BIT(3)
  51#define DP83811_POR_DONE_INT_EN BIT(4)
  52
  53#define MII_DP83811_RXSOP1      0x04a5
  54#define MII_DP83811_RXSOP2      0x04a6
  55#define MII_DP83811_RXSOP3      0x04a7
  56
  57/* WoL Registers */
  58#define MII_DP83811_WOL_CFG     0x04a0
  59#define MII_DP83811_WOL_STAT    0x04a1
  60#define MII_DP83811_WOL_DA1     0x04a2
  61#define MII_DP83811_WOL_DA2     0x04a3
  62#define MII_DP83811_WOL_DA3     0x04a4
  63
  64/* WoL bits */
  65#define DP83811_WOL_MAGIC_EN    BIT(0)
  66#define DP83811_WOL_SECURE_ON   BIT(5)
  67#define DP83811_WOL_EN          BIT(7)
  68#define DP83811_WOL_INDICATION_SEL BIT(8)
  69#define DP83811_WOL_CLR_INDICATION BIT(11)
  70
  71/* SGMII CTRL bits */
  72#define DP83811_TDR_AUTO                BIT(8)
  73#define DP83811_SGMII_EN                BIT(12)
  74#define DP83811_SGMII_AUTO_NEG_EN       BIT(13)
  75#define DP83811_SGMII_TX_ERR_DIS        BIT(14)
  76#define DP83811_SGMII_SOFT_RESET        BIT(15)
  77
  78static int dp83811_ack_interrupt(struct phy_device *phydev)
  79{
  80        int err;
  81
  82        err = phy_read(phydev, MII_DP83811_INT_STAT1);
  83        if (err < 0)
  84                return err;
  85
  86        err = phy_read(phydev, MII_DP83811_INT_STAT2);
  87        if (err < 0)
  88                return err;
  89
  90        err = phy_read(phydev, MII_DP83811_INT_STAT3);
  91        if (err < 0)
  92                return err;
  93
  94        return 0;
  95}
  96
  97static int dp83811_set_wol(struct phy_device *phydev,
  98                           struct ethtool_wolinfo *wol)
  99{
 100        struct net_device *ndev = phydev->attached_dev;
 101        const u8 *mac;
 102        u16 value;
 103
 104        if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
 105                mac = (const u8 *)ndev->dev_addr;
 106
 107                if (!is_valid_ether_addr(mac))
 108                        return -EINVAL;
 109
 110                /* MAC addresses start with byte 5, but stored in mac[0].
 111                 * 811 PHYs store bytes 4|5, 2|3, 0|1
 112                 */
 113                phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_DA1,
 114                              (mac[1] << 8) | mac[0]);
 115                phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_DA2,
 116                              (mac[3] << 8) | mac[2]);
 117                phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_DA3,
 118                              (mac[5] << 8) | mac[4]);
 119
 120                value = phy_read_mmd(phydev, DP83811_DEVADDR,
 121                                     MII_DP83811_WOL_CFG);
 122                if (wol->wolopts & WAKE_MAGIC)
 123                        value |= DP83811_WOL_MAGIC_EN;
 124                else
 125                        value &= ~DP83811_WOL_MAGIC_EN;
 126
 127                if (wol->wolopts & WAKE_MAGICSECURE) {
 128                        phy_write_mmd(phydev, DP83811_DEVADDR,
 129                                      MII_DP83811_RXSOP1,
 130                                      (wol->sopass[1] << 8) | wol->sopass[0]);
 131                        phy_write_mmd(phydev, DP83811_DEVADDR,
 132                                      MII_DP83811_RXSOP2,
 133                                      (wol->sopass[3] << 8) | wol->sopass[2]);
 134                        phy_write_mmd(phydev, DP83811_DEVADDR,
 135                                      MII_DP83811_RXSOP3,
 136                                      (wol->sopass[5] << 8) | wol->sopass[4]);
 137                        value |= DP83811_WOL_SECURE_ON;
 138                } else {
 139                        value &= ~DP83811_WOL_SECURE_ON;
 140                }
 141
 142                value |= (DP83811_WOL_EN | DP83811_WOL_INDICATION_SEL |
 143                          DP83811_WOL_CLR_INDICATION);
 144                phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
 145                              value);
 146        } else {
 147                phy_clear_bits_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
 148                                   DP83811_WOL_EN);
 149        }
 150
 151        return 0;
 152}
 153
 154static void dp83811_get_wol(struct phy_device *phydev,
 155                            struct ethtool_wolinfo *wol)
 156{
 157        u16 sopass_val;
 158        int value;
 159
 160        wol->supported = (WAKE_MAGIC | WAKE_MAGICSECURE);
 161        wol->wolopts = 0;
 162
 163        value = phy_read_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG);
 164
 165        if (value & DP83811_WOL_MAGIC_EN)
 166                wol->wolopts |= WAKE_MAGIC;
 167
 168        if (value & DP83811_WOL_SECURE_ON) {
 169                sopass_val = phy_read_mmd(phydev, DP83811_DEVADDR,
 170                                          MII_DP83811_RXSOP1);
 171                wol->sopass[0] = (sopass_val & 0xff);
 172                wol->sopass[1] = (sopass_val >> 8);
 173
 174                sopass_val = phy_read_mmd(phydev, DP83811_DEVADDR,
 175                                          MII_DP83811_RXSOP2);
 176                wol->sopass[2] = (sopass_val & 0xff);
 177                wol->sopass[3] = (sopass_val >> 8);
 178
 179                sopass_val = phy_read_mmd(phydev, DP83811_DEVADDR,
 180                                          MII_DP83811_RXSOP3);
 181                wol->sopass[4] = (sopass_val & 0xff);
 182                wol->sopass[5] = (sopass_val >> 8);
 183
 184                wol->wolopts |= WAKE_MAGICSECURE;
 185        }
 186
 187        /* WoL is not enabled so set wolopts to 0 */
 188        if (!(value & DP83811_WOL_EN))
 189                wol->wolopts = 0;
 190}
 191
 192static int dp83811_config_intr(struct phy_device *phydev)
 193{
 194        int misr_status, err;
 195
 196        if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
 197                misr_status = phy_read(phydev, MII_DP83811_INT_STAT1);
 198                if (misr_status < 0)
 199                        return misr_status;
 200
 201                misr_status |= (DP83811_RX_ERR_HF_INT_EN |
 202                                DP83811_MS_TRAINING_INT_EN |
 203                                DP83811_ANEG_COMPLETE_INT_EN |
 204                                DP83811_ESD_EVENT_INT_EN |
 205                                DP83811_WOL_INT_EN |
 206                                DP83811_LINK_STAT_INT_EN |
 207                                DP83811_ENERGY_DET_INT_EN |
 208                                DP83811_LINK_QUAL_INT_EN);
 209
 210                err = phy_write(phydev, MII_DP83811_INT_STAT1, misr_status);
 211                if (err < 0)
 212                        return err;
 213
 214                misr_status = phy_read(phydev, MII_DP83811_INT_STAT2);
 215                if (misr_status < 0)
 216                        return misr_status;
 217
 218                misr_status |= (DP83811_JABBER_DET_INT_EN |
 219                                DP83811_POLARITY_INT_EN |
 220                                DP83811_SLEEP_MODE_INT_EN |
 221                                DP83811_OVERTEMP_INT_EN |
 222                                DP83811_OVERVOLTAGE_INT_EN |
 223                                DP83811_UNDERVOLTAGE_INT_EN);
 224
 225                err = phy_write(phydev, MII_DP83811_INT_STAT2, misr_status);
 226                if (err < 0)
 227                        return err;
 228
 229                misr_status = phy_read(phydev, MII_DP83811_INT_STAT3);
 230                if (misr_status < 0)
 231                        return misr_status;
 232
 233                misr_status |= (DP83811_LPS_INT_EN |
 234                                DP83811_NO_FRAME_INT_EN |
 235                                DP83811_POR_DONE_INT_EN);
 236
 237                err = phy_write(phydev, MII_DP83811_INT_STAT3, misr_status);
 238
 239        } else {
 240                err = phy_write(phydev, MII_DP83811_INT_STAT1, 0);
 241                if (err < 0)
 242                        return err;
 243
 244                err = phy_write(phydev, MII_DP83811_INT_STAT2, 0);
 245                if (err < 0)
 246                        return err;
 247
 248                err = phy_write(phydev, MII_DP83811_INT_STAT3, 0);
 249        }
 250
 251        return err;
 252}
 253
 254static int dp83811_config_aneg(struct phy_device *phydev)
 255{
 256        int value, err;
 257
 258        if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
 259                value = phy_read(phydev, MII_DP83811_SGMII_CTRL);
 260                if (phydev->autoneg == AUTONEG_ENABLE) {
 261                        err = phy_write(phydev, MII_DP83811_SGMII_CTRL,
 262                                        (DP83811_SGMII_AUTO_NEG_EN | value));
 263                        if (err < 0)
 264                                return err;
 265                } else {
 266                        err = phy_write(phydev, MII_DP83811_SGMII_CTRL,
 267                                        (~DP83811_SGMII_AUTO_NEG_EN & value));
 268                        if (err < 0)
 269                                return err;
 270                }
 271        }
 272
 273        return genphy_config_aneg(phydev);
 274}
 275
 276static int dp83811_config_init(struct phy_device *phydev)
 277{
 278        int value, err;
 279
 280        err = genphy_config_init(phydev);
 281        if (err < 0)
 282                return err;
 283
 284        value = phy_read(phydev, MII_DP83811_SGMII_CTRL);
 285        if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
 286                err = phy_write(phydev, MII_DP83811_SGMII_CTRL,
 287                                        (DP83811_SGMII_EN | value));
 288        } else {
 289                err = phy_write(phydev, MII_DP83811_SGMII_CTRL,
 290                                (~DP83811_SGMII_EN & value));
 291        }
 292
 293        if (err < 0)
 294
 295                return err;
 296
 297        value = DP83811_WOL_MAGIC_EN | DP83811_WOL_SECURE_ON | DP83811_WOL_EN;
 298
 299        return phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
 300              value);
 301}
 302
 303static int dp83811_phy_reset(struct phy_device *phydev)
 304{
 305        int err;
 306
 307        err = phy_write(phydev, MII_DP83811_RESET_CTRL, DP83811_HW_RESET);
 308        if (err < 0)
 309                return err;
 310
 311        return 0;
 312}
 313
 314static int dp83811_suspend(struct phy_device *phydev)
 315{
 316        int value;
 317
 318        value = phy_read_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG);
 319
 320        if (!(value & DP83811_WOL_EN))
 321                genphy_suspend(phydev);
 322
 323        return 0;
 324}
 325
 326static int dp83811_resume(struct phy_device *phydev)
 327{
 328        genphy_resume(phydev);
 329
 330        phy_set_bits_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
 331                         DP83811_WOL_CLR_INDICATION);
 332
 333        return 0;
 334}
 335
 336static struct phy_driver dp83811_driver[] = {
 337        {
 338                .phy_id = DP83TC811_PHY_ID,
 339                .phy_id_mask = 0xfffffff0,
 340                .name = "TI DP83TC811",
 341                /* PHY_BASIC_FEATURES */
 342                .config_init = dp83811_config_init,
 343                .config_aneg = dp83811_config_aneg,
 344                .soft_reset = dp83811_phy_reset,
 345                .get_wol = dp83811_get_wol,
 346                .set_wol = dp83811_set_wol,
 347                .ack_interrupt = dp83811_ack_interrupt,
 348                .config_intr = dp83811_config_intr,
 349                .suspend = dp83811_suspend,
 350                .resume = dp83811_resume,
 351         },
 352};
 353module_phy_driver(dp83811_driver);
 354
 355static struct mdio_device_id __maybe_unused dp83811_tbl[] = {
 356        { DP83TC811_PHY_ID, 0xfffffff0 },
 357        { },
 358};
 359MODULE_DEVICE_TABLE(mdio, dp83811_tbl);
 360
 361MODULE_DESCRIPTION("Texas Instruments DP83TC811 PHY driver");
 362MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");
 363MODULE_LICENSE("GPL");
 364