Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.8-rc7 346 lines 9.3 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* Driver for the Texas Instruments DP83822, DP83825 and DP83826 PHYs. 3 * 4 * Copyright (C) 2017 Texas Instruments Inc. 5 */ 6 7#include <linux/ethtool.h> 8#include <linux/etherdevice.h> 9#include <linux/kernel.h> 10#include <linux/mii.h> 11#include <linux/module.h> 12#include <linux/of.h> 13#include <linux/phy.h> 14#include <linux/netdevice.h> 15 16#define DP83822_PHY_ID 0x2000a240 17#define DP83825S_PHY_ID 0x2000a140 18#define DP83825I_PHY_ID 0x2000a150 19#define DP83825CM_PHY_ID 0x2000a160 20#define DP83825CS_PHY_ID 0x2000a170 21#define DP83826C_PHY_ID 0x2000a130 22#define DP83826NC_PHY_ID 0x2000a110 23 24#define DP83822_DEVADDR 0x1f 25 26#define MII_DP83822_PHYSCR 0x11 27#define MII_DP83822_MISR1 0x12 28#define MII_DP83822_MISR2 0x13 29#define MII_DP83822_RESET_CTRL 0x1f 30 31#define DP83822_HW_RESET BIT(15) 32#define DP83822_SW_RESET BIT(14) 33 34/* PHYSCR Register Fields */ 35#define DP83822_PHYSCR_INT_OE BIT(0) /* Interrupt Output Enable */ 36#define DP83822_PHYSCR_INTEN BIT(1) /* Interrupt Enable */ 37 38/* MISR1 bits */ 39#define DP83822_RX_ERR_HF_INT_EN BIT(0) 40#define DP83822_FALSE_CARRIER_HF_INT_EN BIT(1) 41#define DP83822_ANEG_COMPLETE_INT_EN BIT(2) 42#define DP83822_DUP_MODE_CHANGE_INT_EN BIT(3) 43#define DP83822_SPEED_CHANGED_INT_EN BIT(4) 44#define DP83822_LINK_STAT_INT_EN BIT(5) 45#define DP83822_ENERGY_DET_INT_EN BIT(6) 46#define DP83822_LINK_QUAL_INT_EN BIT(7) 47 48/* MISR2 bits */ 49#define DP83822_JABBER_DET_INT_EN BIT(0) 50#define DP83822_WOL_PKT_INT_EN BIT(1) 51#define DP83822_SLEEP_MODE_INT_EN BIT(2) 52#define DP83822_MDI_XOVER_INT_EN BIT(3) 53#define DP83822_LB_FIFO_INT_EN BIT(4) 54#define DP83822_PAGE_RX_INT_EN BIT(5) 55#define DP83822_ANEG_ERR_INT_EN BIT(6) 56#define DP83822_EEE_ERROR_CHANGE_INT_EN BIT(7) 57 58/* INT_STAT1 bits */ 59#define DP83822_WOL_INT_EN BIT(4) 60#define DP83822_WOL_INT_STAT BIT(12) 61 62#define MII_DP83822_RXSOP1 0x04a5 63#define MII_DP83822_RXSOP2 0x04a6 64#define MII_DP83822_RXSOP3 0x04a7 65 66/* WoL Registers */ 67#define MII_DP83822_WOL_CFG 0x04a0 68#define MII_DP83822_WOL_STAT 0x04a1 69#define MII_DP83822_WOL_DA1 0x04a2 70#define MII_DP83822_WOL_DA2 0x04a3 71#define MII_DP83822_WOL_DA3 0x04a4 72 73/* WoL bits */ 74#define DP83822_WOL_MAGIC_EN BIT(0) 75#define DP83822_WOL_SECURE_ON BIT(5) 76#define DP83822_WOL_EN BIT(7) 77#define DP83822_WOL_INDICATION_SEL BIT(8) 78#define DP83822_WOL_CLR_INDICATION BIT(11) 79 80static int dp83822_ack_interrupt(struct phy_device *phydev) 81{ 82 int err; 83 84 err = phy_read(phydev, MII_DP83822_MISR1); 85 if (err < 0) 86 return err; 87 88 err = phy_read(phydev, MII_DP83822_MISR2); 89 if (err < 0) 90 return err; 91 92 return 0; 93} 94 95static int dp83822_set_wol(struct phy_device *phydev, 96 struct ethtool_wolinfo *wol) 97{ 98 struct net_device *ndev = phydev->attached_dev; 99 u16 value; 100 const u8 *mac; 101 102 if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) { 103 mac = (const u8 *)ndev->dev_addr; 104 105 if (!is_valid_ether_addr(mac)) 106 return -EINVAL; 107 108 /* MAC addresses start with byte 5, but stored in mac[0]. 109 * 822 PHYs store bytes 4|5, 2|3, 0|1 110 */ 111 phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA1, 112 (mac[1] << 8) | mac[0]); 113 phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA2, 114 (mac[3] << 8) | mac[2]); 115 phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA3, 116 (mac[5] << 8) | mac[4]); 117 118 value = phy_read_mmd(phydev, DP83822_DEVADDR, 119 MII_DP83822_WOL_CFG); 120 if (wol->wolopts & WAKE_MAGIC) 121 value |= DP83822_WOL_MAGIC_EN; 122 else 123 value &= ~DP83822_WOL_MAGIC_EN; 124 125 if (wol->wolopts & WAKE_MAGICSECURE) { 126 phy_write_mmd(phydev, DP83822_DEVADDR, 127 MII_DP83822_RXSOP1, 128 (wol->sopass[1] << 8) | wol->sopass[0]); 129 phy_write_mmd(phydev, DP83822_DEVADDR, 130 MII_DP83822_RXSOP2, 131 (wol->sopass[3] << 8) | wol->sopass[2]); 132 phy_write_mmd(phydev, DP83822_DEVADDR, 133 MII_DP83822_RXSOP3, 134 (wol->sopass[5] << 8) | wol->sopass[4]); 135 value |= DP83822_WOL_SECURE_ON; 136 } else { 137 value &= ~DP83822_WOL_SECURE_ON; 138 } 139 140 /* Clear any pending WoL interrupt */ 141 phy_read(phydev, MII_DP83822_MISR2); 142 143 value |= DP83822_WOL_EN | DP83822_WOL_INDICATION_SEL | 144 DP83822_WOL_CLR_INDICATION; 145 146 return phy_write_mmd(phydev, DP83822_DEVADDR, 147 MII_DP83822_WOL_CFG, value); 148 } else { 149 return phy_clear_bits_mmd(phydev, DP83822_DEVADDR, 150 MII_DP83822_WOL_CFG, DP83822_WOL_EN); 151 } 152} 153 154static void dp83822_get_wol(struct phy_device *phydev, 155 struct ethtool_wolinfo *wol) 156{ 157 int value; 158 u16 sopass_val; 159 160 wol->supported = (WAKE_MAGIC | WAKE_MAGICSECURE); 161 wol->wolopts = 0; 162 163 value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); 164 165 if (value & DP83822_WOL_MAGIC_EN) 166 wol->wolopts |= WAKE_MAGIC; 167 168 if (value & DP83822_WOL_SECURE_ON) { 169 sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR, 170 MII_DP83822_RXSOP1); 171 wol->sopass[0] = (sopass_val & 0xff); 172 wol->sopass[1] = (sopass_val >> 8); 173 174 sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR, 175 MII_DP83822_RXSOP2); 176 wol->sopass[2] = (sopass_val & 0xff); 177 wol->sopass[3] = (sopass_val >> 8); 178 179 sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR, 180 MII_DP83822_RXSOP3); 181 wol->sopass[4] = (sopass_val & 0xff); 182 wol->sopass[5] = (sopass_val >> 8); 183 184 wol->wolopts |= WAKE_MAGICSECURE; 185 } 186 187 /* WoL is not enabled so set wolopts to 0 */ 188 if (!(value & DP83822_WOL_EN)) 189 wol->wolopts = 0; 190} 191 192static int dp83822_config_intr(struct phy_device *phydev) 193{ 194 int misr_status; 195 int physcr_status; 196 int err; 197 198 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 199 misr_status = phy_read(phydev, MII_DP83822_MISR1); 200 if (misr_status < 0) 201 return misr_status; 202 203 misr_status |= (DP83822_RX_ERR_HF_INT_EN | 204 DP83822_FALSE_CARRIER_HF_INT_EN | 205 DP83822_ANEG_COMPLETE_INT_EN | 206 DP83822_DUP_MODE_CHANGE_INT_EN | 207 DP83822_SPEED_CHANGED_INT_EN | 208 DP83822_LINK_STAT_INT_EN | 209 DP83822_ENERGY_DET_INT_EN | 210 DP83822_LINK_QUAL_INT_EN); 211 212 err = phy_write(phydev, MII_DP83822_MISR1, misr_status); 213 if (err < 0) 214 return err; 215 216 misr_status = phy_read(phydev, MII_DP83822_MISR2); 217 if (misr_status < 0) 218 return misr_status; 219 220 misr_status |= (DP83822_JABBER_DET_INT_EN | 221 DP83822_WOL_PKT_INT_EN | 222 DP83822_SLEEP_MODE_INT_EN | 223 DP83822_MDI_XOVER_INT_EN | 224 DP83822_LB_FIFO_INT_EN | 225 DP83822_PAGE_RX_INT_EN | 226 DP83822_ANEG_ERR_INT_EN | 227 DP83822_EEE_ERROR_CHANGE_INT_EN); 228 229 err = phy_write(phydev, MII_DP83822_MISR2, misr_status); 230 if (err < 0) 231 return err; 232 233 physcr_status = phy_read(phydev, MII_DP83822_PHYSCR); 234 if (physcr_status < 0) 235 return physcr_status; 236 237 physcr_status |= DP83822_PHYSCR_INT_OE | DP83822_PHYSCR_INTEN; 238 239 } else { 240 err = phy_write(phydev, MII_DP83822_MISR1, 0); 241 if (err < 0) 242 return err; 243 244 err = phy_write(phydev, MII_DP83822_MISR1, 0); 245 if (err < 0) 246 return err; 247 248 physcr_status = phy_read(phydev, MII_DP83822_PHYSCR); 249 if (physcr_status < 0) 250 return physcr_status; 251 252 physcr_status &= ~DP83822_PHYSCR_INTEN; 253 } 254 255 return phy_write(phydev, MII_DP83822_PHYSCR, physcr_status); 256} 257 258static int dp83822_config_init(struct phy_device *phydev) 259{ 260 int value = DP83822_WOL_EN | DP83822_WOL_MAGIC_EN | 261 DP83822_WOL_SECURE_ON; 262 263 return phy_clear_bits_mmd(phydev, DP83822_DEVADDR, 264 MII_DP83822_WOL_CFG, value); 265} 266 267static int dp83822_phy_reset(struct phy_device *phydev) 268{ 269 int err; 270 271 err = phy_write(phydev, MII_DP83822_RESET_CTRL, DP83822_HW_RESET); 272 if (err < 0) 273 return err; 274 275 dp83822_config_init(phydev); 276 277 return 0; 278} 279 280static int dp83822_suspend(struct phy_device *phydev) 281{ 282 int value; 283 284 value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); 285 286 if (!(value & DP83822_WOL_EN)) 287 genphy_suspend(phydev); 288 289 return 0; 290} 291 292static int dp83822_resume(struct phy_device *phydev) 293{ 294 int value; 295 296 genphy_resume(phydev); 297 298 value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); 299 300 phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, value | 301 DP83822_WOL_CLR_INDICATION); 302 303 return 0; 304} 305 306#define DP83822_PHY_DRIVER(_id, _name) \ 307 { \ 308 PHY_ID_MATCH_MODEL(_id), \ 309 .name = (_name), \ 310 /* PHY_BASIC_FEATURES */ \ 311 .soft_reset = dp83822_phy_reset, \ 312 .config_init = dp83822_config_init, \ 313 .get_wol = dp83822_get_wol, \ 314 .set_wol = dp83822_set_wol, \ 315 .ack_interrupt = dp83822_ack_interrupt, \ 316 .config_intr = dp83822_config_intr, \ 317 .suspend = dp83822_suspend, \ 318 .resume = dp83822_resume, \ 319 } 320 321static struct phy_driver dp83822_driver[] = { 322 DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822"), 323 DP83822_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"), 324 DP83822_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"), 325 DP83822_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"), 326 DP83822_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"), 327 DP83822_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"), 328 DP83822_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"), 329}; 330module_phy_driver(dp83822_driver); 331 332static struct mdio_device_id __maybe_unused dp83822_tbl[] = { 333 { DP83822_PHY_ID, 0xfffffff0 }, 334 { DP83825I_PHY_ID, 0xfffffff0 }, 335 { DP83826C_PHY_ID, 0xfffffff0 }, 336 { DP83826NC_PHY_ID, 0xfffffff0 }, 337 { DP83825S_PHY_ID, 0xfffffff0 }, 338 { DP83825CM_PHY_ID, 0xfffffff0 }, 339 { DP83825CS_PHY_ID, 0xfffffff0 }, 340 { }, 341}; 342MODULE_DEVICE_TABLE(mdio, dp83822_tbl); 343 344MODULE_DESCRIPTION("Texas Instruments DP83822 PHY driver"); 345MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com"); 346MODULE_LICENSE("GPL v2");