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_CTRL_STATUS); 47 if (rc < 0) 48 return rc; 49 50 /* Enable energy detect mode for this SMSC Transceivers */ 51 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, 52 rc | MII_LAN83C185_EDPWRDOWN); 53 if (rc < 0) 54 return rc; 55 56 return smsc_phy_ack_interrupt (phydev); 57 } 58 59 static int lan87xx_config_init(struct phy_device *phydev) 60 { 61 /* 62 * Make sure the EDPWRDOWN bit is NOT set. Setting this bit on 63 * LAN8710/LAN8720 PHY causes the PHY to misbehave, likely due 64 * to a bug on the chip. 65 * 66 * When the system is powered on with the network cable being 67 * disconnected all the way until after ifconfig ethX up is 68 * issued for the LAN port with this PHY, connecting the cable 69 * afterwards does not cause LINK change detection, while the 70 * expected behavior is the Link UP being detected. 71 */ 72 int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 73 if (rc < 0) 74 return rc; 75 76 rc &= ~MII_LAN83C185_EDPWRDOWN; 77 78 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, rc); 79 if (rc < 0) 80 return rc; 81 82 return smsc_phy_ack_interrupt(phydev); 83 } 84 85 static int lan911x_config_init(struct phy_device *phydev) 86 { 87 return smsc_phy_ack_interrupt(phydev); 88 } 89 90 static struct phy_driver smsc_phy_driver[] = { 91 { 92 .phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */ 93 .phy_id_mask = 0xfffffff0, 94 .name = "SMSC LAN83C185", 95 96 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 97 | SUPPORTED_Asym_Pause), 98 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 99 100 /* basic functions */ 101 .config_aneg = genphy_config_aneg, 102 .read_status = genphy_read_status, 103 .config_init = smsc_phy_config_init, 104 105 /* IRQ related */ 106 .ack_interrupt = smsc_phy_ack_interrupt, 107 .config_intr = smsc_phy_config_intr, 108 109 .suspend = genphy_suspend, 110 .resume = genphy_resume, 111 112 .driver = { .owner = THIS_MODULE, } 113 }, { 114 .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ 115 .phy_id_mask = 0xfffffff0, 116 .name = "SMSC LAN8187", 117 118 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 119 | SUPPORTED_Asym_Pause), 120 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 121 122 /* basic functions */ 123 .config_aneg = genphy_config_aneg, 124 .read_status = genphy_read_status, 125 .config_init = smsc_phy_config_init, 126 127 /* IRQ related */ 128 .ack_interrupt = smsc_phy_ack_interrupt, 129 .config_intr = smsc_phy_config_intr, 130 131 .suspend = genphy_suspend, 132 .resume = genphy_resume, 133 134 .driver = { .owner = THIS_MODULE, } 135 }, { 136 .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ 137 .phy_id_mask = 0xfffffff0, 138 .name = "SMSC LAN8700", 139 140 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 141 | SUPPORTED_Asym_Pause), 142 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 143 144 /* basic functions */ 145 .config_aneg = genphy_config_aneg, 146 .read_status = genphy_read_status, 147 .config_init = smsc_phy_config_init, 148 149 /* IRQ related */ 150 .ack_interrupt = smsc_phy_ack_interrupt, 151 .config_intr = smsc_phy_config_intr, 152 153 .suspend = genphy_suspend, 154 .resume = genphy_resume, 155 156 .driver = { .owner = THIS_MODULE, } 157 }, { 158 .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ 159 .phy_id_mask = 0xfffffff0, 160 .name = "SMSC LAN911x Internal PHY", 161 162 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 163 | SUPPORTED_Asym_Pause), 164 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 165 166 /* basic functions */ 167 .config_aneg = genphy_config_aneg, 168 .read_status = genphy_read_status, 169 .config_init = lan911x_config_init, 170 171 /* IRQ related */ 172 .ack_interrupt = smsc_phy_ack_interrupt, 173 .config_intr = smsc_phy_config_intr, 174 175 .suspend = genphy_suspend, 176 .resume = genphy_resume, 177 178 .driver = { .owner = THIS_MODULE, } 179 }, { 180 .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ 181 .phy_id_mask = 0xfffffff0, 182 .name = "SMSC LAN8710/LAN8720", 183 184 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 185 | SUPPORTED_Asym_Pause), 186 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 187 188 /* basic functions */ 189 .config_aneg = genphy_config_aneg, 190 .read_status = genphy_read_status, 191 .config_init = lan87xx_config_init, 192 193 /* IRQ related */ 194 .ack_interrupt = smsc_phy_ack_interrupt, 195 .config_intr = smsc_phy_config_intr, 196 197 .suspend = genphy_suspend, 198 .resume = genphy_resume, 199 200 .driver = { .owner = THIS_MODULE, } 201 } }; 202 203 static int __init smsc_init(void) 204 { 205 return phy_drivers_register(smsc_phy_driver, 206 ARRAY_SIZE(smsc_phy_driver)); 207 } 208 209 static void __exit smsc_exit(void) 210 { 211 return phy_drivers_unregister(smsc_phy_driver, 212 ARRAY_SIZE(smsc_phy_driver)); 213 } 214 215 MODULE_DESCRIPTION("SMSC PHY driver"); 216 MODULE_AUTHOR("Herbert Valerio Riedel"); 217 MODULE_LICENSE("GPL"); 218 219 module_init(smsc_init); 220 module_exit(smsc_exit); 221 222 static struct mdio_device_id __maybe_unused smsc_tbl[] = { 223 { 0x0007c0a0, 0xfffffff0 }, 224 { 0x0007c0b0, 0xfffffff0 }, 225 { 0x0007c0c0, 0xfffffff0 }, 226 { 0x0007c0d0, 0xfffffff0 }, 227 { 0x0007c0f0, 0xfffffff0 }, 228 { } 229 }; 230 231 MODULE_DEVICE_TABLE(mdio, smsc_tbl); 232