1 /* 2 * drivers/net/phy/smsc.c 3 * 4 * Driver for SMSC PHYs 5 * 6 * Author: Herbert Valerio Riedel 7 * 8 * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org> 9 * 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License as published by the 12 * Free Software Foundation; either version 2 of the License, or (at your 13 * option) any later version. 14 * 15 * Support added for SMSC LAN8187 and LAN8700 by steve.glendinning@shawell.net 16 * 17 */ 18 19 #include <linux/kernel.h> 20 #include <linux/module.h> 21 #include <linux/mii.h> 22 #include <linux/ethtool.h> 23 #include <linux/phy.h> 24 #include <linux/netdevice.h> 25 #include <linux/smscphy.h> 26 27 static int smsc_phy_config_intr(struct phy_device *phydev) 28 { 29 int rc = phy_write (phydev, MII_LAN83C185_IM, 30 ((PHY_INTERRUPT_ENABLED == phydev->interrupts) 31 ? MII_LAN83C185_ISF_INT_PHYLIB_EVENTS 32 : 0)); 33 34 return rc < 0 ? rc : 0; 35 } 36 37 static int smsc_phy_ack_interrupt(struct phy_device *phydev) 38 { 39 int rc = phy_read (phydev, MII_LAN83C185_ISF); 40 41 return rc < 0 ? rc : 0; 42 } 43 44 static int smsc_phy_config_init(struct phy_device *phydev) 45 { 46 int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES); 47 if (rc < 0) 48 return rc; 49 50 /* If the SMSC PHY is in power down mode, then set it 51 * in all capable mode before using it. 52 */ 53 if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) { 54 int timeout = 50000; 55 56 /* set "all capable" mode and reset the phy */ 57 rc |= MII_LAN83C185_MODE_ALL; 58 phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc); 59 phy_write(phydev, MII_BMCR, BMCR_RESET); 60 61 /* wait end of reset (max 500 ms) */ 62 do { 63 udelay(10); 64 if (timeout-- == 0) 65 return -1; 66 rc = phy_read(phydev, MII_BMCR); 67 } while (rc & BMCR_RESET); 68 } 69 70 rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 71 if (rc < 0) 72 return rc; 73 74 /* Enable energy detect mode for this SMSC Transceivers */ 75 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, 76 rc | MII_LAN83C185_EDPWRDOWN); 77 if (rc < 0) 78 return rc; 79 80 return smsc_phy_ack_interrupt (phydev); 81 } 82 83 static int lan911x_config_init(struct phy_device *phydev) 84 { 85 return smsc_phy_ack_interrupt(phydev); 86 } 87 88 /* 89 * The LAN8710/LAN8720 requires a minimum of 2 link pulses within 64ms of each 90 * other in order to set the ENERGYON bit and exit EDPD mode. If a link partner 91 * does send the pulses within this interval, the PHY will remained powered 92 * down. 93 * 94 * This workaround will manually toggle the PHY on/off upon calls to read_status 95 * in order to generate link test pulses if the link is down. If a link partner 96 * is present, it will respond to the pulses, which will cause the ENERGYON bit 97 * to be set and will cause the EDPD mode to be exited. 98 */ 99 static int lan87xx_read_status(struct phy_device *phydev) 100 { 101 int err = genphy_read_status(phydev); 102 103 if (!phydev->link) { 104 /* Disable EDPD to wake up PHY */ 105 int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 106 if (rc < 0) 107 return rc; 108 109 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, 110 rc & ~MII_LAN83C185_EDPWRDOWN); 111 if (rc < 0) 112 return rc; 113 114 /* Sleep 64 ms to allow ~5 link test pulses to be sent */ 115 msleep(64); 116 117 /* Re-enable EDPD */ 118 rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 119 if (rc < 0) 120 return rc; 121 122 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, 123 rc | MII_LAN83C185_EDPWRDOWN); 124 if (rc < 0) 125 return rc; 126 } 127 128 return err; 129 } 130 131 static struct phy_driver smsc_phy_driver[] = { 132 { 133 .phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */ 134 .phy_id_mask = 0xfffffff0, 135 .name = "SMSC LAN83C185", 136 137 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 138 | SUPPORTED_Asym_Pause), 139 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 140 141 /* basic functions */ 142 .config_aneg = genphy_config_aneg, 143 .read_status = genphy_read_status, 144 .config_init = smsc_phy_config_init, 145 146 /* IRQ related */ 147 .ack_interrupt = smsc_phy_ack_interrupt, 148 .config_intr = smsc_phy_config_intr, 149 150 .suspend = genphy_suspend, 151 .resume = genphy_resume, 152 153 .driver = { .owner = THIS_MODULE, } 154 }, { 155 .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ 156 .phy_id_mask = 0xfffffff0, 157 .name = "SMSC LAN8187", 158 159 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 160 | SUPPORTED_Asym_Pause), 161 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 162 163 /* basic functions */ 164 .config_aneg = genphy_config_aneg, 165 .read_status = genphy_read_status, 166 .config_init = smsc_phy_config_init, 167 168 /* IRQ related */ 169 .ack_interrupt = smsc_phy_ack_interrupt, 170 .config_intr = smsc_phy_config_intr, 171 172 .suspend = genphy_suspend, 173 .resume = genphy_resume, 174 175 .driver = { .owner = THIS_MODULE, } 176 }, { 177 .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ 178 .phy_id_mask = 0xfffffff0, 179 .name = "SMSC LAN8700", 180 181 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 182 | SUPPORTED_Asym_Pause), 183 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 184 185 /* basic functions */ 186 .config_aneg = genphy_config_aneg, 187 .read_status = genphy_read_status, 188 .config_init = smsc_phy_config_init, 189 190 /* IRQ related */ 191 .ack_interrupt = smsc_phy_ack_interrupt, 192 .config_intr = smsc_phy_config_intr, 193 194 .suspend = genphy_suspend, 195 .resume = genphy_resume, 196 197 .driver = { .owner = THIS_MODULE, } 198 }, { 199 .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ 200 .phy_id_mask = 0xfffffff0, 201 .name = "SMSC LAN911x Internal PHY", 202 203 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 204 | SUPPORTED_Asym_Pause), 205 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 206 207 /* basic functions */ 208 .config_aneg = genphy_config_aneg, 209 .read_status = genphy_read_status, 210 .config_init = lan911x_config_init, 211 212 /* IRQ related */ 213 .ack_interrupt = smsc_phy_ack_interrupt, 214 .config_intr = smsc_phy_config_intr, 215 216 .suspend = genphy_suspend, 217 .resume = genphy_resume, 218 219 .driver = { .owner = THIS_MODULE, } 220 }, { 221 .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ 222 .phy_id_mask = 0xfffffff0, 223 .name = "SMSC LAN8710/LAN8720", 224 225 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 226 | SUPPORTED_Asym_Pause), 227 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 228 229 /* basic functions */ 230 .config_aneg = genphy_config_aneg, 231 .read_status = lan87xx_read_status, 232 .config_init = smsc_phy_config_init, 233 234 /* IRQ related */ 235 .ack_interrupt = smsc_phy_ack_interrupt, 236 .config_intr = smsc_phy_config_intr, 237 238 .suspend = genphy_suspend, 239 .resume = genphy_resume, 240 241 .driver = { .owner = THIS_MODULE, } 242 } }; 243 244 static int __init smsc_init(void) 245 { 246 return phy_drivers_register(smsc_phy_driver, 247 ARRAY_SIZE(smsc_phy_driver)); 248 } 249 250 static void __exit smsc_exit(void) 251 { 252 return phy_drivers_unregister(smsc_phy_driver, 253 ARRAY_SIZE(smsc_phy_driver)); 254 } 255 256 MODULE_DESCRIPTION("SMSC PHY driver"); 257 MODULE_AUTHOR("Herbert Valerio Riedel"); 258 MODULE_LICENSE("GPL"); 259 260 module_init(smsc_init); 261 module_exit(smsc_exit); 262 263 static struct mdio_device_id __maybe_unused smsc_tbl[] = { 264 { 0x0007c0a0, 0xfffffff0 }, 265 { 0x0007c0b0, 0xfffffff0 }, 266 { 0x0007c0c0, 0xfffffff0 }, 267 { 0x0007c0d0, 0xfffffff0 }, 268 { 0x0007c0f0, 0xfffffff0 }, 269 { } 270 }; 271 272 MODULE_DEVICE_TABLE(mdio, smsc_tbl); 273