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