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 lan911x_config_init(struct phy_device *phydev) 60 { 61 return smsc_phy_ack_interrupt(phydev); 62 } 63 64 static struct phy_driver smsc_phy_driver[] = { 65 { 66 .phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */ 67 .phy_id_mask = 0xfffffff0, 68 .name = "SMSC LAN83C185", 69 70 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 71 | SUPPORTED_Asym_Pause), 72 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 73 74 /* basic functions */ 75 .config_aneg = genphy_config_aneg, 76 .read_status = genphy_read_status, 77 .config_init = smsc_phy_config_init, 78 79 /* IRQ related */ 80 .ack_interrupt = smsc_phy_ack_interrupt, 81 .config_intr = smsc_phy_config_intr, 82 83 .suspend = genphy_suspend, 84 .resume = genphy_resume, 85 86 .driver = { .owner = THIS_MODULE, } 87 }, { 88 .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ 89 .phy_id_mask = 0xfffffff0, 90 .name = "SMSC LAN8187", 91 92 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 93 | SUPPORTED_Asym_Pause), 94 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 95 96 /* basic functions */ 97 .config_aneg = genphy_config_aneg, 98 .read_status = genphy_read_status, 99 .config_init = smsc_phy_config_init, 100 101 /* IRQ related */ 102 .ack_interrupt = smsc_phy_ack_interrupt, 103 .config_intr = smsc_phy_config_intr, 104 105 .suspend = genphy_suspend, 106 .resume = genphy_resume, 107 108 .driver = { .owner = THIS_MODULE, } 109 }, { 110 .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ 111 .phy_id_mask = 0xfffffff0, 112 .name = "SMSC LAN8700", 113 114 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 115 | SUPPORTED_Asym_Pause), 116 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 117 118 /* basic functions */ 119 .config_aneg = genphy_config_aneg, 120 .read_status = genphy_read_status, 121 .config_init = smsc_phy_config_init, 122 123 /* IRQ related */ 124 .ack_interrupt = smsc_phy_ack_interrupt, 125 .config_intr = smsc_phy_config_intr, 126 127 .suspend = genphy_suspend, 128 .resume = genphy_resume, 129 130 .driver = { .owner = THIS_MODULE, } 131 }, { 132 .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ 133 .phy_id_mask = 0xfffffff0, 134 .name = "SMSC LAN911x Internal PHY", 135 136 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 137 | SUPPORTED_Asym_Pause), 138 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 139 140 /* basic functions */ 141 .config_aneg = genphy_config_aneg, 142 .read_status = genphy_read_status, 143 .config_init = lan911x_config_init, 144 145 /* IRQ related */ 146 .ack_interrupt = smsc_phy_ack_interrupt, 147 .config_intr = smsc_phy_config_intr, 148 149 .suspend = genphy_suspend, 150 .resume = genphy_resume, 151 152 .driver = { .owner = THIS_MODULE, } 153 }, { 154 .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ 155 .phy_id_mask = 0xfffffff0, 156 .name = "SMSC LAN8710/LAN8720", 157 158 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 159 | SUPPORTED_Asym_Pause), 160 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 161 162 /* basic functions */ 163 .config_aneg = genphy_config_aneg, 164 .read_status = genphy_read_status, 165 .config_init = smsc_phy_config_init, 166 167 /* IRQ related */ 168 .ack_interrupt = smsc_phy_ack_interrupt, 169 .config_intr = smsc_phy_config_intr, 170 171 .suspend = genphy_suspend, 172 .resume = genphy_resume, 173 174 .driver = { .owner = THIS_MODULE, } 175 } }; 176 177 static int __init smsc_init(void) 178 { 179 return phy_drivers_register(smsc_phy_driver, 180 ARRAY_SIZE(smsc_phy_driver)); 181 } 182 183 static void __exit smsc_exit(void) 184 { 185 return phy_drivers_unregister(smsc_phy_driver, 186 ARRAY_SIZE(smsc_phy_driver)); 187 } 188 189 MODULE_DESCRIPTION("SMSC PHY driver"); 190 MODULE_AUTHOR("Herbert Valerio Riedel"); 191 MODULE_LICENSE("GPL"); 192 193 module_init(smsc_init); 194 module_exit(smsc_exit); 195 196 static struct mdio_device_id __maybe_unused smsc_tbl[] = { 197 { 0x0007c0a0, 0xfffffff0 }, 198 { 0x0007c0b0, 0xfffffff0 }, 199 { 0x0007c0c0, 0xfffffff0 }, 200 { 0x0007c0d0, 0xfffffff0 }, 201 { 0x0007c0f0, 0xfffffff0 }, 202 { } 203 }; 204 205 MODULE_DEVICE_TABLE(mdio, smsc_tbl); 206