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"); 244