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 and the timeout is not 126 * an actual error. 127 */ 128 read_poll_timeout(phy_read, rc, 129 rc & MII_LAN83C185_ENERGYON || rc < 0, 130 10000, 640000, true, phydev, 131 MII_LAN83C185_CTRL_STATUS); 132 if (rc < 0) 133 return rc; 134 135 /* Re-enable EDPD */ 136 rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 137 if (rc < 0) 138 return rc; 139 140 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, 141 rc | MII_LAN83C185_EDPWRDOWN); 142 if (rc < 0) 143 return rc; 144 } 145 146 return err; 147 } 148 149 static int smsc_get_sset_count(struct phy_device *phydev) 150 { 151 return ARRAY_SIZE(smsc_hw_stats); 152 } 153 154 static void smsc_get_strings(struct phy_device *phydev, u8 *data) 155 { 156 int i; 157 158 for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++) { 159 strncpy(data + i * ETH_GSTRING_LEN, 160 smsc_hw_stats[i].string, ETH_GSTRING_LEN); 161 } 162 } 163 164 static u64 smsc_get_stat(struct phy_device *phydev, int i) 165 { 166 struct smsc_hw_stat stat = smsc_hw_stats[i]; 167 int val; 168 u64 ret; 169 170 val = phy_read(phydev, stat.reg); 171 if (val < 0) 172 ret = U64_MAX; 173 else 174 ret = val; 175 176 return ret; 177 } 178 179 static void smsc_get_stats(struct phy_device *phydev, 180 struct ethtool_stats *stats, u64 *data) 181 { 182 int i; 183 184 for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++) 185 data[i] = smsc_get_stat(phydev, i); 186 } 187 188 static int smsc_phy_probe(struct phy_device *phydev) 189 { 190 struct device *dev = &phydev->mdio.dev; 191 struct device_node *of_node = dev->of_node; 192 struct smsc_phy_priv *priv; 193 194 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 195 if (!priv) 196 return -ENOMEM; 197 198 priv->energy_enable = true; 199 200 if (of_property_read_bool(of_node, "smsc,disable-energy-detect")) 201 priv->energy_enable = false; 202 203 phydev->priv = priv; 204 205 return 0; 206 } 207 208 static struct phy_driver smsc_phy_driver[] = { 209 { 210 .phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */ 211 .phy_id_mask = 0xfffffff0, 212 .name = "SMSC LAN83C185", 213 214 /* PHY_BASIC_FEATURES */ 215 216 .probe = smsc_phy_probe, 217 218 /* basic functions */ 219 .config_init = smsc_phy_config_init, 220 .soft_reset = smsc_phy_reset, 221 222 /* IRQ related */ 223 .ack_interrupt = smsc_phy_ack_interrupt, 224 .config_intr = smsc_phy_config_intr, 225 226 .suspend = genphy_suspend, 227 .resume = genphy_resume, 228 }, { 229 .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ 230 .phy_id_mask = 0xfffffff0, 231 .name = "SMSC LAN8187", 232 233 /* PHY_BASIC_FEATURES */ 234 235 .probe = smsc_phy_probe, 236 237 /* basic functions */ 238 .config_init = smsc_phy_config_init, 239 .soft_reset = smsc_phy_reset, 240 241 /* IRQ related */ 242 .ack_interrupt = smsc_phy_ack_interrupt, 243 .config_intr = smsc_phy_config_intr, 244 245 /* Statistics */ 246 .get_sset_count = smsc_get_sset_count, 247 .get_strings = smsc_get_strings, 248 .get_stats = smsc_get_stats, 249 250 .suspend = genphy_suspend, 251 .resume = genphy_resume, 252 }, { 253 .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ 254 .phy_id_mask = 0xfffffff0, 255 .name = "SMSC LAN8700", 256 257 /* PHY_BASIC_FEATURES */ 258 259 .probe = smsc_phy_probe, 260 261 /* basic functions */ 262 .read_status = lan87xx_read_status, 263 .config_init = smsc_phy_config_init, 264 .soft_reset = smsc_phy_reset, 265 266 /* IRQ related */ 267 .ack_interrupt = smsc_phy_ack_interrupt, 268 .config_intr = smsc_phy_config_intr, 269 270 /* Statistics */ 271 .get_sset_count = smsc_get_sset_count, 272 .get_strings = smsc_get_strings, 273 .get_stats = smsc_get_stats, 274 275 .suspend = genphy_suspend, 276 .resume = genphy_resume, 277 }, { 278 .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ 279 .phy_id_mask = 0xfffffff0, 280 .name = "SMSC LAN911x Internal PHY", 281 282 /* PHY_BASIC_FEATURES */ 283 284 .probe = smsc_phy_probe, 285 286 /* basic functions */ 287 .config_init = lan911x_config_init, 288 289 /* IRQ related */ 290 .ack_interrupt = smsc_phy_ack_interrupt, 291 .config_intr = smsc_phy_config_intr, 292 293 .suspend = genphy_suspend, 294 .resume = genphy_resume, 295 }, { 296 .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ 297 .phy_id_mask = 0xfffffff0, 298 .name = "SMSC LAN8710/LAN8720", 299 300 /* PHY_BASIC_FEATURES */ 301 .flags = PHY_RST_AFTER_CLK_EN, 302 303 .probe = smsc_phy_probe, 304 305 /* basic functions */ 306 .read_status = lan87xx_read_status, 307 .config_init = smsc_phy_config_init, 308 .soft_reset = smsc_phy_reset, 309 310 /* IRQ related */ 311 .ack_interrupt = smsc_phy_ack_interrupt, 312 .config_intr = smsc_phy_config_intr, 313 314 /* Statistics */ 315 .get_sset_count = smsc_get_sset_count, 316 .get_strings = smsc_get_strings, 317 .get_stats = smsc_get_stats, 318 319 .suspend = genphy_suspend, 320 .resume = genphy_resume, 321 }, { 322 .phy_id = 0x0007c110, 323 .phy_id_mask = 0xfffffff0, 324 .name = "SMSC LAN8740", 325 326 /* PHY_BASIC_FEATURES */ 327 .flags = PHY_RST_AFTER_CLK_EN, 328 329 .probe = smsc_phy_probe, 330 331 /* basic functions */ 332 .read_status = lan87xx_read_status, 333 .config_init = smsc_phy_config_init, 334 .soft_reset = smsc_phy_reset, 335 336 /* IRQ related */ 337 .ack_interrupt = smsc_phy_ack_interrupt, 338 .config_intr = smsc_phy_config_intr, 339 340 /* Statistics */ 341 .get_sset_count = smsc_get_sset_count, 342 .get_strings = smsc_get_strings, 343 .get_stats = smsc_get_stats, 344 345 .suspend = genphy_suspend, 346 .resume = genphy_resume, 347 } }; 348 349 module_phy_driver(smsc_phy_driver); 350 351 MODULE_DESCRIPTION("SMSC PHY driver"); 352 MODULE_AUTHOR("Herbert Valerio Riedel"); 353 MODULE_LICENSE("GPL"); 354 355 static struct mdio_device_id __maybe_unused smsc_tbl[] = { 356 { 0x0007c0a0, 0xfffffff0 }, 357 { 0x0007c0b0, 0xfffffff0 }, 358 { 0x0007c0c0, 0xfffffff0 }, 359 { 0x0007c0d0, 0xfffffff0 }, 360 { 0x0007c0f0, 0xfffffff0 }, 361 { 0x0007c110, 0xfffffff0 }, 362 { } 363 }; 364 365 MODULE_DEVICE_TABLE(mdio, smsc_tbl); 366