root/drivers/net/phy/microchip_t1.c

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

DEFINITIONS

This source file includes following definitions.
  1. access_ereg
  2. access_ereg_modify_changed
  3. lan87xx_phy_init
  4. lan87xx_phy_config_intr
  5. lan87xx_phy_ack_interrupt
  6. lan87xx_config_init

   1 // SPDX-License-Identifier: GPL-2.0
   2 // Copyright (C) 2018 Microchip Technology
   3 
   4 #include <linux/kernel.h>
   5 #include <linux/module.h>
   6 #include <linux/delay.h>
   7 #include <linux/mii.h>
   8 #include <linux/phy.h>
   9 
  10 /* External Register Control Register */
  11 #define LAN87XX_EXT_REG_CTL                     (0x14)
  12 #define LAN87XX_EXT_REG_CTL_RD_CTL              (0x1000)
  13 #define LAN87XX_EXT_REG_CTL_WR_CTL              (0x0800)
  14 
  15 /* External Register Read Data Register */
  16 #define LAN87XX_EXT_REG_RD_DATA                 (0x15)
  17 
  18 /* External Register Write Data Register */
  19 #define LAN87XX_EXT_REG_WR_DATA                 (0x16)
  20 
  21 /* Interrupt Source Register */
  22 #define LAN87XX_INTERRUPT_SOURCE                (0x18)
  23 
  24 /* Interrupt Mask Register */
  25 #define LAN87XX_INTERRUPT_MASK                  (0x19)
  26 #define LAN87XX_MASK_LINK_UP                    (0x0004)
  27 #define LAN87XX_MASK_LINK_DOWN                  (0x0002)
  28 
  29 /* phyaccess nested types */
  30 #define PHYACC_ATTR_MODE_READ           0
  31 #define PHYACC_ATTR_MODE_WRITE          1
  32 #define PHYACC_ATTR_MODE_MODIFY         2
  33 
  34 #define PHYACC_ATTR_BANK_SMI            0
  35 #define PHYACC_ATTR_BANK_MISC           1
  36 #define PHYACC_ATTR_BANK_PCS            2
  37 #define PHYACC_ATTR_BANK_AFE            3
  38 #define PHYACC_ATTR_BANK_MAX            7
  39 
  40 #define DRIVER_AUTHOR   "Nisar Sayed <nisar.sayed@microchip.com>"
  41 #define DRIVER_DESC     "Microchip LAN87XX T1 PHY driver"
  42 
  43 struct access_ereg_val {
  44         u8  mode;
  45         u8  bank;
  46         u8  offset;
  47         u16 val;
  48         u16 mask;
  49 };
  50 
  51 static int access_ereg(struct phy_device *phydev, u8 mode, u8 bank,
  52                        u8 offset, u16 val)
  53 {
  54         u16 ereg = 0;
  55         int rc = 0;
  56 
  57         if (mode > PHYACC_ATTR_MODE_WRITE || bank > PHYACC_ATTR_BANK_MAX)
  58                 return -EINVAL;
  59 
  60         if (bank == PHYACC_ATTR_BANK_SMI) {
  61                 if (mode == PHYACC_ATTR_MODE_WRITE)
  62                         rc = phy_write(phydev, offset, val);
  63                 else
  64                         rc = phy_read(phydev, offset);
  65                 return rc;
  66         }
  67 
  68         if (mode == PHYACC_ATTR_MODE_WRITE) {
  69                 ereg = LAN87XX_EXT_REG_CTL_WR_CTL;
  70                 rc = phy_write(phydev, LAN87XX_EXT_REG_WR_DATA, val);
  71                 if (rc < 0)
  72                         return rc;
  73         } else {
  74                 ereg = LAN87XX_EXT_REG_CTL_RD_CTL;
  75         }
  76 
  77         ereg |= (bank << 8) | offset;
  78 
  79         rc = phy_write(phydev, LAN87XX_EXT_REG_CTL, ereg);
  80         if (rc < 0)
  81                 return rc;
  82 
  83         if (mode == PHYACC_ATTR_MODE_READ)
  84                 rc = phy_read(phydev, LAN87XX_EXT_REG_RD_DATA);
  85 
  86         return rc;
  87 }
  88 
  89 static int access_ereg_modify_changed(struct phy_device *phydev,
  90                                       u8 bank, u8 offset, u16 val, u16 mask)
  91 {
  92         int new = 0, rc = 0;
  93 
  94         if (bank > PHYACC_ATTR_BANK_MAX)
  95                 return -EINVAL;
  96 
  97         rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, bank, offset, val);
  98         if (rc < 0)
  99                 return rc;
 100 
 101         new = val | (rc & (mask ^ 0xFFFF));
 102         rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE, bank, offset, new);
 103 
 104         return rc;
 105 }
 106 
 107 static int lan87xx_phy_init(struct phy_device *phydev)
 108 {
 109         static const struct access_ereg_val init[] = {
 110                 /* TX Amplitude = 5 */
 111                 {PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_AFE, 0x0B,
 112                  0x000A, 0x001E},
 113                 /* Clear SMI interrupts */
 114                 {PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI, 0x18,
 115                  0, 0},
 116                 /* Clear MISC interrupts */
 117                 {PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_MISC, 0x08,
 118                  0, 0},
 119                 /* Turn on TC10 Ring Oscillator (ROSC) */
 120                 {PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_MISC, 0x20,
 121                  0x0020, 0x0020},
 122                 /* WUR Detect Length to 1.2uS, LPC Detect Length to 1.09uS */
 123                 {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_PCS, 0x20,
 124                  0x283C, 0},
 125                 /* Wake_In Debounce Length to 39uS, Wake_Out Length to 79uS */
 126                 {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x21,
 127                  0x274F, 0},
 128                 /* Enable Auto Wake Forward to Wake_Out, ROSC on, Sleep,
 129                  * and Wake_In to wake PHY
 130                  */
 131                 {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x20,
 132                  0x80A7, 0},
 133                 /* Enable WUP Auto Fwd, Enable Wake on MDI, Wakeup Debouncer
 134                  * to 128 uS
 135                  */
 136                 {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x24,
 137                  0xF110, 0},
 138                 /* Enable HW Init */
 139                 {PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_SMI, 0x1A,
 140                  0x0100, 0x0100},
 141         };
 142         int rc, i;
 143 
 144         /* Start manual initialization procedures in Managed Mode */
 145         rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI,
 146                                         0x1a, 0x0000, 0x0100);
 147         if (rc < 0)
 148                 return rc;
 149 
 150         /* Soft Reset the SMI block */
 151         rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI,
 152                                         0x00, 0x8000, 0x8000);
 153         if (rc < 0)
 154                 return rc;
 155 
 156         /* Check to see if the self-clearing bit is cleared */
 157         usleep_range(1000, 2000);
 158         rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
 159                          PHYACC_ATTR_BANK_SMI, 0x00, 0);
 160         if (rc < 0)
 161                 return rc;
 162         if ((rc & 0x8000) != 0)
 163                 return -ETIMEDOUT;
 164 
 165         /* PHY Initialization */
 166         for (i = 0; i < ARRAY_SIZE(init); i++) {
 167                 if (init[i].mode == PHYACC_ATTR_MODE_MODIFY) {
 168                         rc = access_ereg_modify_changed(phydev, init[i].bank,
 169                                                         init[i].offset,
 170                                                         init[i].val,
 171                                                         init[i].mask);
 172                 } else {
 173                         rc = access_ereg(phydev, init[i].mode, init[i].bank,
 174                                          init[i].offset, init[i].val);
 175                 }
 176                 if (rc < 0)
 177                         return rc;
 178         }
 179 
 180         return 0;
 181 }
 182 
 183 static int lan87xx_phy_config_intr(struct phy_device *phydev)
 184 {
 185         int rc, val = 0;
 186 
 187         if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
 188                 /* unmask all source and clear them before enable */
 189                 rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, 0x7FFF);
 190                 rc = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE);
 191                 val = LAN87XX_MASK_LINK_UP | LAN87XX_MASK_LINK_DOWN;
 192         }
 193 
 194         rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, val);
 195 
 196         return rc < 0 ? rc : 0;
 197 }
 198 
 199 static int lan87xx_phy_ack_interrupt(struct phy_device *phydev)
 200 {
 201         int rc = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE);
 202 
 203         return rc < 0 ? rc : 0;
 204 }
 205 
 206 static int lan87xx_config_init(struct phy_device *phydev)
 207 {
 208         int rc = lan87xx_phy_init(phydev);
 209 
 210         return rc < 0 ? rc : 0;
 211 }
 212 
 213 static struct phy_driver microchip_t1_phy_driver[] = {
 214         {
 215                 .phy_id         = 0x0007c150,
 216                 .phy_id_mask    = 0xfffffff0,
 217                 .name           = "Microchip LAN87xx T1",
 218 
 219                 .features       = PHY_BASIC_T1_FEATURES,
 220 
 221                 .config_init    = lan87xx_config_init,
 222                 .config_aneg    = genphy_config_aneg,
 223 
 224                 .ack_interrupt  = lan87xx_phy_ack_interrupt,
 225                 .config_intr    = lan87xx_phy_config_intr,
 226 
 227                 .suspend        = genphy_suspend,
 228                 .resume         = genphy_resume,
 229         }
 230 };
 231 
 232 module_phy_driver(microchip_t1_phy_driver);
 233 
 234 static struct mdio_device_id __maybe_unused microchip_t1_tbl[] = {
 235         { 0x0007c150, 0xfffffff0 },
 236         { }
 237 };
 238 
 239 MODULE_DEVICE_TABLE(mdio, microchip_t1_tbl);
 240 
 241 MODULE_AUTHOR(DRIVER_AUTHOR);
 242 MODULE_DESCRIPTION(DRIVER_DESC);
 243 MODULE_LICENSE("GPL");

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