root/drivers/net/phy/dp83822.c

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

DEFINITIONS

This source file includes following definitions.
  1. dp83822_ack_interrupt
  2. dp83822_set_wol
  3. dp83822_get_wol
  4. dp83822_config_intr
  5. dp83822_config_init
  6. dp83822_phy_reset
  7. dp83822_suspend
  8. dp83822_resume

   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 
  76 static 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 
  91 static 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 
 151 static 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 
 189 static 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 
 255 static int dp83822_config_init(struct phy_device *phydev)
 256 {
 257         int value;
 258 
 259         value = DP83822_WOL_MAGIC_EN | DP83822_WOL_SECURE_ON | DP83822_WOL_EN;
 260 
 261         return phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG,
 262               value);
 263 }
 264 
 265 static int dp83822_phy_reset(struct phy_device *phydev)
 266 {
 267         int err;
 268 
 269         err = phy_write(phydev, MII_DP83822_RESET_CTRL, DP83822_HW_RESET);
 270         if (err < 0)
 271                 return err;
 272 
 273         dp83822_config_init(phydev);
 274 
 275         return 0;
 276 }
 277 
 278 static int dp83822_suspend(struct phy_device *phydev)
 279 {
 280         int value;
 281 
 282         value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG);
 283 
 284         if (!(value & DP83822_WOL_EN))
 285                 genphy_suspend(phydev);
 286 
 287         return 0;
 288 }
 289 
 290 static int dp83822_resume(struct phy_device *phydev)
 291 {
 292         int value;
 293 
 294         genphy_resume(phydev);
 295 
 296         value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG);
 297 
 298         phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, value |
 299                       DP83822_WOL_CLR_INDICATION);
 300 
 301         return 0;
 302 }
 303 
 304 #define DP83822_PHY_DRIVER(_id, _name)                          \
 305         {                                                       \
 306                 PHY_ID_MATCH_MODEL(_id),                        \
 307                 .name           = (_name),                      \
 308                 /* PHY_BASIC_FEATURES */                        \
 309                 .soft_reset     = dp83822_phy_reset,            \
 310                 .config_init    = dp83822_config_init,          \
 311                 .get_wol = dp83822_get_wol,                     \
 312                 .set_wol = dp83822_set_wol,                     \
 313                 .ack_interrupt = dp83822_ack_interrupt,         \
 314                 .config_intr = dp83822_config_intr,             \
 315                 .suspend = dp83822_suspend,                     \
 316                 .resume = dp83822_resume,                       \
 317         }
 318 
 319 static struct phy_driver dp83822_driver[] = {
 320         DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822"),
 321         DP83822_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"),
 322 };
 323 module_phy_driver(dp83822_driver);
 324 
 325 static struct mdio_device_id __maybe_unused dp83822_tbl[] = {
 326         { DP83822_PHY_ID, 0xfffffff0 },
 327         { DP83825I_PHY_ID, 0xfffffff0 },
 328         { },
 329 };
 330 MODULE_DEVICE_TABLE(mdio, dp83822_tbl);
 331 
 332 MODULE_DESCRIPTION("Texas Instruments DP83822 PHY driver");
 333 MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");
 334 MODULE_LICENSE("GPL v2");

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