root/drivers/net/phy/dp83tc811.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dp83811_ack_interrupt
  2. dp83811_set_wol
  3. dp83811_get_wol
  4. dp83811_config_intr
  5. dp83811_config_aneg
  6. dp83811_config_init
  7. dp83811_phy_reset
  8. dp83811_suspend
  9. dp83811_resume

   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 
  78 static 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 
  97 static 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 
 154 static 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 
 192 static 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 
 254 static 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 
 276 static int dp83811_config_init(struct phy_device *phydev)
 277 {
 278         int value, err;
 279 
 280         value = phy_read(phydev, MII_DP83811_SGMII_CTRL);
 281         if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
 282                 err = phy_write(phydev, MII_DP83811_SGMII_CTRL,
 283                                         (DP83811_SGMII_EN | value));
 284         } else {
 285                 err = phy_write(phydev, MII_DP83811_SGMII_CTRL,
 286                                 (~DP83811_SGMII_EN & value));
 287         }
 288 
 289         if (err < 0)
 290 
 291                 return err;
 292 
 293         value = DP83811_WOL_MAGIC_EN | DP83811_WOL_SECURE_ON | DP83811_WOL_EN;
 294 
 295         return phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
 296               value);
 297 }
 298 
 299 static int dp83811_phy_reset(struct phy_device *phydev)
 300 {
 301         int err;
 302 
 303         err = phy_write(phydev, MII_DP83811_RESET_CTRL, DP83811_HW_RESET);
 304         if (err < 0)
 305                 return err;
 306 
 307         return 0;
 308 }
 309 
 310 static int dp83811_suspend(struct phy_device *phydev)
 311 {
 312         int value;
 313 
 314         value = phy_read_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG);
 315 
 316         if (!(value & DP83811_WOL_EN))
 317                 genphy_suspend(phydev);
 318 
 319         return 0;
 320 }
 321 
 322 static int dp83811_resume(struct phy_device *phydev)
 323 {
 324         genphy_resume(phydev);
 325 
 326         phy_set_bits_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
 327                          DP83811_WOL_CLR_INDICATION);
 328 
 329         return 0;
 330 }
 331 
 332 static struct phy_driver dp83811_driver[] = {
 333         {
 334                 .phy_id = DP83TC811_PHY_ID,
 335                 .phy_id_mask = 0xfffffff0,
 336                 .name = "TI DP83TC811",
 337                 /* PHY_BASIC_FEATURES */
 338                 .config_init = dp83811_config_init,
 339                 .config_aneg = dp83811_config_aneg,
 340                 .soft_reset = dp83811_phy_reset,
 341                 .get_wol = dp83811_get_wol,
 342                 .set_wol = dp83811_set_wol,
 343                 .ack_interrupt = dp83811_ack_interrupt,
 344                 .config_intr = dp83811_config_intr,
 345                 .suspend = dp83811_suspend,
 346                 .resume = dp83811_resume,
 347          },
 348 };
 349 module_phy_driver(dp83811_driver);
 350 
 351 static struct mdio_device_id __maybe_unused dp83811_tbl[] = {
 352         { DP83TC811_PHY_ID, 0xfffffff0 },
 353         { },
 354 };
 355 MODULE_DEVICE_TABLE(mdio, dp83811_tbl);
 356 
 357 MODULE_DESCRIPTION("Texas Instruments DP83TC811 PHY driver");
 358 MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");
 359 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */