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 rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, val); 193 } else { 194 rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, val); 195 if (rc) 196 return rc; 197 198 rc = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE); 199 } 200 201 return rc < 0 ? rc : 0; 202 } 203 204 static irqreturn_t lan87xx_handle_interrupt(struct phy_device *phydev) 205 { 206 int irq_status; 207 208 irq_status = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE); 209 if (irq_status < 0) { 210 phy_error(phydev); 211 return IRQ_NONE; 212 } 213 214 if (irq_status == 0) 215 return IRQ_NONE; 216 217 phy_trigger_machine(phydev); 218 219 return IRQ_HANDLED; 220 } 221 222 static int lan87xx_config_init(struct phy_device *phydev) 223 { 224 int rc = lan87xx_phy_init(phydev); 225 226 return rc < 0 ? rc : 0; 227 } 228 229 static struct phy_driver microchip_t1_phy_driver[] = { 230 { 231 .phy_id = 0x0007c150, 232 .phy_id_mask = 0xfffffff0, 233 .name = "Microchip LAN87xx T1", 234 235 .features = PHY_BASIC_T1_FEATURES, 236 237 .config_init = lan87xx_config_init, 238 239 .config_intr = lan87xx_phy_config_intr, 240 .handle_interrupt = lan87xx_handle_interrupt, 241 242 .suspend = genphy_suspend, 243 .resume = genphy_resume, 244 } 245 }; 246 247 module_phy_driver(microchip_t1_phy_driver); 248 249 static struct mdio_device_id __maybe_unused microchip_t1_tbl[] = { 250 { 0x0007c150, 0xfffffff0 }, 251 { } 252 }; 253 254 MODULE_DEVICE_TABLE(mdio, microchip_t1_tbl); 255 256 MODULE_AUTHOR(DRIVER_AUTHOR); 257 MODULE_DESCRIPTION(DRIVER_DESC); 258 MODULE_LICENSE("GPL"); 259