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                value = phy_read_mmd(phydev, DP83811_DEVADDR,
 148                                     MII_DP83811_WOL_CFG);
 149                value &= ~DP83811_WOL_EN;
 150                phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
 151                              value);
 152        }
 153
 154        return 0;
 155}
 156
 157static void dp83811_get_wol(struct phy_device *phydev,
 158                            struct ethtool_wolinfo *wol)
 159{
 160        u16 sopass_val;
 161        int value;
 162
 163        wol->supported = (WAKE_MAGIC | WAKE_MAGICSECURE);
 164        wol->wolopts = 0;
 165
 166        value = phy_read_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG);
 167
 168        if (value & DP83811_WOL_MAGIC_EN)
 169                wol->wolopts |= WAKE_MAGIC;
 170
 171        if (value & DP83811_WOL_SECURE_ON) {
 172                sopass_val = phy_read_mmd(phydev, DP83811_DEVADDR,
 173                                          MII_DP83811_RXSOP1);
 174                wol->sopass[0] = (sopass_val & 0xff);
 175                wol->sopass[1] = (sopass_val >> 8);
 176
 177                sopass_val = phy_read_mmd(phydev, DP83811_DEVADDR,
 178                                          MII_DP83811_RXSOP2);
 179                wol->sopass[2] = (sopass_val & 0xff);
 180                wol->sopass[3] = (sopass_val >> 8);
 181
 182                sopass_val = phy_read_mmd(phydev, DP83811_DEVADDR,
 183                                          MII_DP83811_RXSOP3);
 184                wol->sopass[4] = (sopass_val & 0xff);
 185                wol->sopass[5] = (sopass_val >> 8);
 186
 187                wol->wolopts |= WAKE_MAGICSECURE;
 188        }
 189
 190        /* WoL is not enabled so set wolopts to 0 */
 191        if (!(value & DP83811_WOL_EN))
 192                wol->wolopts = 0;
 193}
 194
 195static int dp83811_config_intr(struct phy_device *phydev)
 196{
 197        int misr_status, err;
 198
 199        if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
 200                misr_status = phy_read(phydev, MII_DP83811_INT_STAT1);
 201                if (misr_status < 0)
 202                        return misr_status;
 203
 204                misr_status |= (DP83811_RX_ERR_HF_INT_EN |
 205                                DP83811_MS_TRAINING_INT_EN |
 206                                DP83811_ANEG_COMPLETE_INT_EN |
 207                                DP83811_ESD_EVENT_INT_EN |
 208                                DP83811_WOL_INT_EN |
 209                                DP83811_LINK_STAT_INT_EN |
 210                                DP83811_ENERGY_DET_INT_EN |
 211                                DP83811_LINK_QUAL_INT_EN);
 212
 213                err = phy_write(phydev, MII_DP83811_INT_STAT1, misr_status);
 214                if (err < 0)
 215                        return err;
 216
 217                misr_status = phy_read(phydev, MII_DP83811_INT_STAT2);
 218                if (misr_status < 0)
 219                        return misr_status;
 220
 221                misr_status |= (DP83811_JABBER_DET_INT_EN |
 222                                DP83811_POLARITY_INT_EN |
 223                                DP83811_SLEEP_MODE_INT_EN |
 224                                DP83811_OVERTEMP_INT_EN |
 225                                DP83811_OVERVOLTAGE_INT_EN |
 226                                DP83811_UNDERVOLTAGE_INT_EN);
 227
 228                err = phy_write(phydev, MII_DP83811_INT_STAT2, misr_status);
 229                if (err < 0)
 230                        return err;
 231
 232                misr_status = phy_read(phydev, MII_DP83811_INT_STAT3);
 233                if (misr_status < 0)
 234                        return misr_status;
 235
 236                misr_status |= (DP83811_LPS_INT_EN |
 237                                DP83811_NO_FRAME_INT_EN |
 238                                DP83811_POR_DONE_INT_EN);
 239
 240                err = phy_write(phydev, MII_DP83811_INT_STAT3, misr_status);
 241
 242        } else {
 243                err = phy_write(phydev, MII_DP83811_INT_STAT1, 0);
 244                if (err < 0)
 245                        return err;
 246
 247                err = phy_write(phydev, MII_DP83811_INT_STAT2, 0);
 248                if (err < 0)
 249                        return err;
 250
 251                err = phy_write(phydev, MII_DP83811_INT_STAT3, 0);
 252        }
 253
 254        return err;
 255}
 256
 257static int dp83811_config_aneg(struct phy_device *phydev)
 258{
 259        int value, err;
 260
 261        if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
 262                value = phy_read(phydev, MII_DP83811_SGMII_CTRL);
 263                if (phydev->autoneg == AUTONEG_ENABLE) {
 264                        err = phy_write(phydev, MII_DP83811_SGMII_CTRL,
 265                                        (DP83811_SGMII_AUTO_NEG_EN | value));
 266                        if (err < 0)
 267                                return err;
 268                } else {
 269                        err = phy_write(phydev, MII_DP83811_SGMII_CTRL,
 270                                        (~DP83811_SGMII_AUTO_NEG_EN & value));
 271                        if (err < 0)
 272                                return err;
 273                }
 274        }
 275
 276        return genphy_config_aneg(phydev);
 277}
 278
 279static int dp83811_config_init(struct phy_device *phydev)
 280{
 281        int value, err;
 282
 283        err = genphy_config_init(phydev);
 284        if (err < 0)
 285                return err;
 286
 287        value = phy_read(phydev, MII_DP83811_SGMII_CTRL);
 288        if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
 289                err = phy_write(phydev, MII_DP83811_SGMII_CTRL,
 290                                        (DP83811_SGMII_EN | value));
 291        } else {
 292                err = phy_write(phydev, MII_DP83811_SGMII_CTRL,
 293                                (~DP83811_SGMII_EN & value));
 294        }
 295
 296        if (err < 0)
 297
 298                return err;
 299
 300        value = DP83811_WOL_MAGIC_EN | DP83811_WOL_SECURE_ON | DP83811_WOL_EN;
 301
 302        return phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
 303              value);
 304}
 305
 306static int dp83811_phy_reset(struct phy_device *phydev)
 307{
 308        int err;
 309
 310        err = phy_write(phydev, MII_DP83811_RESET_CTRL, DP83811_HW_RESET);
 311        if (err < 0)
 312                return err;
 313
 314        return 0;
 315}
 316
 317static int dp83811_suspend(struct phy_device *phydev)
 318{
 319        int value;
 320
 321        value = phy_read_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG);
 322
 323        if (!(value & DP83811_WOL_EN))
 324                genphy_suspend(phydev);
 325
 326        return 0;
 327}
 328
 329static int dp83811_resume(struct phy_device *phydev)
 330{
 331        int value;
 332
 333        genphy_resume(phydev);
 334
 335        value = phy_read_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG);
 336
 337        phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG, value |
 338                      DP83811_WOL_CLR_INDICATION);
 339
 340        return 0;
 341}
 342
 343static struct phy_driver dp83811_driver[] = {
 344        {
 345                .phy_id = DP83TC811_PHY_ID,
 346                .phy_id_mask = 0xfffffff0,
 347                .name = "TI DP83TC811",
 348                .features = PHY_BASIC_FEATURES,
 349                .flags = PHY_HAS_INTERRUPT,
 350                .config_init = dp83811_config_init,
 351                .config_aneg = dp83811_config_aneg,
 352                .soft_reset = dp83811_phy_reset,
 353                .get_wol = dp83811_get_wol,
 354                .set_wol = dp83811_set_wol,
 355                .ack_interrupt = dp83811_ack_interrupt,
 356                .config_intr = dp83811_config_intr,
 357                .suspend = dp83811_suspend,
 358                .resume = dp83811_resume,
 359         },
 360};
 361module_phy_driver(dp83811_driver);
 362
 363static struct mdio_device_id __maybe_unused dp83811_tbl[] = {
 364        { DP83TC811_PHY_ID, 0xfffffff0 },
 365        { },
 366};
 367MODULE_DEVICE_TABLE(mdio, dp83811_tbl);
 368
 369MODULE_DESCRIPTION("Texas Instruments DP83TC811 PHY driver");
 370MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");
 371MODULE_LICENSE("GPL");
 372