1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * drivers/net/phy/smsc.c 4 * 5 * Driver for SMSC PHYs 6 * 7 * Author: Herbert Valerio Riedel 8 * 9 * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org> 10 * 11 * Support added for SMSC LAN8187 and LAN8700 by steve.glendinning@shawell.net 12 * 13 */ 14 15 #include <linux/kernel.h> 16 #include <linux/module.h> 17 #include <linux/mii.h> 18 #include <linux/ethtool.h> 19 #include <linux/of.h> 20 #include <linux/phy.h> 21 #include <linux/netdevice.h> 22 #include <linux/smscphy.h> 23 24 struct smsc_hw_stat { 25 const char *string; 26 u8 reg; 27 u8 bits; 28 }; 29 30 static struct smsc_hw_stat smsc_hw_stats[] = { 31 { "phy_symbol_errors", 26, 16}, 32 }; 33 34 struct smsc_phy_priv { 35 bool energy_enable; 36 }; 37 38 static int smsc_phy_config_intr(struct phy_device *phydev) 39 { 40 int rc = phy_write (phydev, MII_LAN83C185_IM, 41 ((PHY_INTERRUPT_ENABLED == phydev->interrupts) 42 ? MII_LAN83C185_ISF_INT_PHYLIB_EVENTS 43 : 0)); 44 45 return rc < 0 ? rc : 0; 46 } 47 48 static int smsc_phy_ack_interrupt(struct phy_device *phydev) 49 { 50 int rc = phy_read (phydev, MII_LAN83C185_ISF); 51 52 return rc < 0 ? rc : 0; 53 } 54 55 static int smsc_phy_config_init(struct phy_device *phydev) 56 { 57 struct smsc_phy_priv *priv = phydev->priv; 58 59 int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 60 61 if (rc < 0) 62 return rc; 63 64 if (priv->energy_enable) { 65 /* Enable energy detect mode for this SMSC Transceivers */ 66 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, 67 rc | MII_LAN83C185_EDPWRDOWN); 68 if (rc < 0) 69 return rc; 70 } 71 72 return smsc_phy_ack_interrupt(phydev); 73 } 74 75 static int smsc_phy_reset(struct phy_device *phydev) 76 { 77 int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES); 78 if (rc < 0) 79 return rc; 80 81 /* If the SMSC PHY is in power down mode, then set it 82 * in all capable mode before using it. 83 */ 84 if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) { 85 /* set "all capable" mode */ 86 rc |= MII_LAN83C185_MODE_ALL; 87 phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc); 88 } 89 90 /* reset the phy */ 91 return genphy_soft_reset(phydev); 92 } 93 94 static int lan911x_config_init(struct phy_device *phydev) 95 { 96 return smsc_phy_ack_interrupt(phydev); 97 } 98 99 /* 100 * The LAN87xx suffers from rare absence of the ENERGYON-bit when Ethernet cable 101 * plugs in while LAN87xx is in Energy Detect Power-Down mode. This leads to 102 * unstable detection of plugging in Ethernet cable. 103 * This workaround disables Energy Detect Power-Down mode and waiting for 104 * response on link pulses to detect presence of plugged Ethernet cable. 105 * The Energy Detect Power-Down mode is enabled again in the end of procedure to 106 * save approximately 220 mW of power if cable is unplugged. 107 */ 108 static int lan87xx_read_status(struct phy_device *phydev) 109 { 110 struct smsc_phy_priv *priv = phydev->priv; 111 112 int err = genphy_read_status(phydev); 113 114 if (!phydev->link && priv->energy_enable) { 115 /* Disable EDPD to wake up PHY */ 116 int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 117 if (rc < 0) 118 return rc; 119 120 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, 121 rc & ~MII_LAN83C185_EDPWRDOWN); 122 if (rc < 0) 123 return rc; 124 125 /* Wait max 640 ms to detect energy */ 126 phy_read_poll_timeout(phydev, MII_LAN83C185_CTRL_STATUS, rc, 127 rc & MII_LAN83C185_ENERGYON, 10000, 128 640000, true); 129 if (rc < 0) 130 return rc; 131 132 /* Re-enable EDPD */ 133 rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 134 if (rc < 0) 135 return rc; 136 137 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, 138 rc | MII_LAN83C185_EDPWRDOWN); 139 if (rc < 0) 140 return rc; 141 } 142 143 return err; 144 } 145 146 static int smsc_get_sset_count(struct phy_device *phydev) 147 { 148 return ARRAY_SIZE(smsc_hw_stats); 149 } 150 151 static void smsc_get_strings(struct phy_device *phydev, u8 *data) 152 { 153 int i; 154 155 for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++) { 156 strncpy(data + i * ETH_GSTRING_LEN, 157 smsc_hw_stats[i].string, ETH_GSTRING_LEN); 158 } 159 } 160 161 static u64 smsc_get_stat(struct phy_device *phydev, int i) 162 { 163 struct smsc_hw_stat stat = smsc_hw_stats[i]; 164 int val; 165 u64 ret; 166 167 val = phy_read(phydev, stat.reg); 168 if (val < 0) 169 ret = U64_MAX; 170 else 171 ret = val; 172 173 return ret; 174 } 175 176 static void smsc_get_stats(struct phy_device *phydev, 177 struct ethtool_stats *stats, u64 *data) 178 { 179 int i; 180 181 for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++) 182 data[i] = smsc_get_stat(phydev, i); 183 } 184 185 static int smsc_phy_probe(struct phy_device *phydev) 186 { 187 struct device *dev = &phydev->mdio.dev; 188 struct device_node *of_node = dev->of_node; 189 struct smsc_phy_priv *priv; 190 191 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 192 if (!priv) 193 return -ENOMEM; 194 195 priv->energy_enable = true; 196 197 if (of_property_read_bool(of_node, "smsc,disable-energy-detect")) 198 priv->energy_enable = false; 199 200 phydev->priv = priv; 201 202 return 0; 203 } 204 205 static struct phy_driver smsc_phy_driver[] = { 206 { 207 .phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */ 208 .phy_id_mask = 0xfffffff0, 209 .name = "SMSC LAN83C185", 210 211 /* PHY_BASIC_FEATURES */ 212 213 .probe = smsc_phy_probe, 214 215 /* basic functions */ 216 .config_init = smsc_phy_config_init, 217 .soft_reset = smsc_phy_reset, 218 219 /* IRQ related */ 220 .ack_interrupt = smsc_phy_ack_interrupt, 221 .config_intr = smsc_phy_config_intr, 222 223 .suspend = genphy_suspend, 224 .resume = genphy_resume, 225 }, { 226 .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ 227 .phy_id_mask = 0xfffffff0, 228 .name = "SMSC LAN8187", 229 230 /* PHY_BASIC_FEATURES */ 231 232 .probe = smsc_phy_probe, 233 234 /* basic functions */ 235 .config_init = smsc_phy_config_init, 236 .soft_reset = smsc_phy_reset, 237 238 /* IRQ related */ 239 .ack_interrupt = smsc_phy_ack_interrupt, 240 .config_intr = smsc_phy_config_intr, 241 242 /* Statistics */ 243 .get_sset_count = smsc_get_sset_count, 244 .get_strings = smsc_get_strings, 245 .get_stats = smsc_get_stats, 246 247 .suspend = genphy_suspend, 248 .resume = genphy_resume, 249 }, { 250 .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ 251 .phy_id_mask = 0xfffffff0, 252 .name = "SMSC LAN8700", 253 254 /* PHY_BASIC_FEATURES */ 255 256 .probe = smsc_phy_probe, 257 258 /* basic functions */ 259 .read_status = lan87xx_read_status, 260 .config_init = smsc_phy_config_init, 261 .soft_reset = smsc_phy_reset, 262 263 /* IRQ related */ 264 .ack_interrupt = smsc_phy_ack_interrupt, 265 .config_intr = smsc_phy_config_intr, 266 267 /* Statistics */ 268 .get_sset_count = smsc_get_sset_count, 269 .get_strings = smsc_get_strings, 270 .get_stats = smsc_get_stats, 271 272 .suspend = genphy_suspend, 273 .resume = genphy_resume, 274 }, { 275 .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ 276 .phy_id_mask = 0xfffffff0, 277 .name = "SMSC LAN911x Internal PHY", 278 279 /* PHY_BASIC_FEATURES */ 280 281 .probe = smsc_phy_probe, 282 283 /* basic functions */ 284 .config_init = lan911x_config_init, 285 286 /* IRQ related */ 287 .ack_interrupt = smsc_phy_ack_interrupt, 288 .config_intr = smsc_phy_config_intr, 289 290 .suspend = genphy_suspend, 291 .resume = genphy_resume, 292 }, { 293 .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ 294 .phy_id_mask = 0xfffffff0, 295 .name = "SMSC LAN8710/LAN8720", 296 297 /* PHY_BASIC_FEATURES */ 298 .flags = PHY_RST_AFTER_CLK_EN, 299 300 .probe = smsc_phy_probe, 301 302 /* basic functions */ 303 .read_status = lan87xx_read_status, 304 .config_init = smsc_phy_config_init, 305 .soft_reset = smsc_phy_reset, 306 307 /* IRQ related */ 308 .ack_interrupt = smsc_phy_ack_interrupt, 309 .config_intr = smsc_phy_config_intr, 310 311 /* Statistics */ 312 .get_sset_count = smsc_get_sset_count, 313 .get_strings = smsc_get_strings, 314 .get_stats = smsc_get_stats, 315 316 .suspend = genphy_suspend, 317 .resume = genphy_resume, 318 }, { 319 .phy_id = 0x0007c110, 320 .phy_id_mask = 0xfffffff0, 321 .name = "SMSC LAN8740", 322 323 /* PHY_BASIC_FEATURES */ 324 .flags = PHY_RST_AFTER_CLK_EN, 325 326 .probe = smsc_phy_probe, 327 328 /* basic functions */ 329 .read_status = lan87xx_read_status, 330 .config_init = smsc_phy_config_init, 331 .soft_reset = smsc_phy_reset, 332 333 /* IRQ related */ 334 .ack_interrupt = smsc_phy_ack_interrupt, 335 .config_intr = smsc_phy_config_intr, 336 337 /* Statistics */ 338 .get_sset_count = smsc_get_sset_count, 339 .get_strings = smsc_get_strings, 340 .get_stats = smsc_get_stats, 341 342 .suspend = genphy_suspend, 343 .resume = genphy_resume, 344 } }; 345 346 module_phy_driver(smsc_phy_driver); 347 348 MODULE_DESCRIPTION("SMSC PHY driver"); 349 MODULE_AUTHOR("Herbert Valerio Riedel"); 350 MODULE_LICENSE("GPL"); 351 352 static struct mdio_device_id __maybe_unused smsc_tbl[] = { 353 { 0x0007c0a0, 0xfffffff0 }, 354 { 0x0007c0b0, 0xfffffff0 }, 355 { 0x0007c0c0, 0xfffffff0 }, 356 { 0x0007c0d0, 0xfffffff0 }, 357 { 0x0007c0f0, 0xfffffff0 }, 358 { 0x0007c110, 0xfffffff0 }, 359 { } 360 }; 361 362 MODULE_DEVICE_TABLE(mdio, smsc_tbl); 363