2 * drivers/net/phy/smsc.c
6 * Author: Herbert Valerio Riedel
8 * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org>
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.
15 * Support added for SMSC LAN8187 and LAN8700 by steve.glendinning@shawell.net
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>
27 static int smsc_phy_config_intr(struct phy_device
*phydev
)
29 int rc
= phy_write (phydev
, MII_LAN83C185_IM
,
30 ((PHY_INTERRUPT_ENABLED
== phydev
->interrupts
)
31 ? MII_LAN83C185_ISF_INT_PHYLIB_EVENTS
34 return rc
< 0 ? rc
: 0;
37 static int smsc_phy_ack_interrupt(struct phy_device
*phydev
)
39 int rc
= phy_read (phydev
, MII_LAN83C185_ISF
);
41 return rc
< 0 ? rc
: 0;
44 static int smsc_phy_config_init(struct phy_device
*phydev
)
46 int rc
= phy_read(phydev
, MII_LAN83C185_CTRL_STATUS
);
51 /* Enable energy detect mode for this SMSC Transceivers */
52 rc
= phy_write(phydev
, MII_LAN83C185_CTRL_STATUS
,
53 rc
| MII_LAN83C185_EDPWRDOWN
);
57 return smsc_phy_ack_interrupt(phydev
);
60 static int smsc_phy_reset(struct phy_device
*phydev
)
62 int rc
= phy_read(phydev
, MII_LAN83C185_SPECIAL_MODES
);
66 /* If the SMSC PHY is in power down mode, then set it
67 * in all capable mode before using it.
69 if ((rc
& MII_LAN83C185_MODE_MASK
) == MII_LAN83C185_MODE_POWERDOWN
) {
72 /* set "all capable" mode and reset the phy */
73 rc
|= MII_LAN83C185_MODE_ALL
;
74 phy_write(phydev
, MII_LAN83C185_SPECIAL_MODES
, rc
);
75 phy_write(phydev
, MII_BMCR
, BMCR_RESET
);
77 /* wait end of reset (max 500 ms) */
82 rc
= phy_read(phydev
, MII_BMCR
);
83 } while (rc
& BMCR_RESET
);
88 static int lan911x_config_init(struct phy_device
*phydev
)
90 return smsc_phy_ack_interrupt(phydev
);
94 * The LAN8710/LAN8720 requires a minimum of 2 link pulses within 64ms of each
95 * other in order to set the ENERGYON bit and exit EDPD mode. If a link partner
96 * does send the pulses within this interval, the PHY will remained powered
99 * This workaround will manually toggle the PHY on/off upon calls to read_status
100 * in order to generate link test pulses if the link is down. If a link partner
101 * is present, it will respond to the pulses, which will cause the ENERGYON bit
102 * to be set and will cause the EDPD mode to be exited.
104 static int lan87xx_read_status(struct phy_device
*phydev
)
106 int err
= genphy_read_status(phydev
);
109 /* Disable EDPD to wake up PHY */
110 int rc
= phy_read(phydev
, MII_LAN83C185_CTRL_STATUS
);
114 rc
= phy_write(phydev
, MII_LAN83C185_CTRL_STATUS
,
115 rc
& ~MII_LAN83C185_EDPWRDOWN
);
119 /* Sleep 64 ms to allow ~5 link test pulses to be sent */
123 rc
= phy_read(phydev
, MII_LAN83C185_CTRL_STATUS
);
127 rc
= phy_write(phydev
, MII_LAN83C185_CTRL_STATUS
,
128 rc
| MII_LAN83C185_EDPWRDOWN
);
136 static struct phy_driver smsc_phy_driver
[] = {
138 .phy_id
= 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */
139 .phy_id_mask
= 0xfffffff0,
140 .name
= "SMSC LAN83C185",
142 .features
= (PHY_BASIC_FEATURES
| SUPPORTED_Pause
143 | SUPPORTED_Asym_Pause
),
144 .flags
= PHY_HAS_INTERRUPT
| PHY_HAS_MAGICANEG
,
146 /* basic functions */
147 .config_aneg
= genphy_config_aneg
,
148 .read_status
= genphy_read_status
,
149 .config_init
= smsc_phy_config_init
,
150 .soft_reset
= smsc_phy_reset
,
153 .ack_interrupt
= smsc_phy_ack_interrupt
,
154 .config_intr
= smsc_phy_config_intr
,
156 .suspend
= genphy_suspend
,
157 .resume
= genphy_resume
,
159 .driver
= { .owner
= THIS_MODULE
, }
161 .phy_id
= 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */
162 .phy_id_mask
= 0xfffffff0,
163 .name
= "SMSC LAN8187",
165 .features
= (PHY_BASIC_FEATURES
| SUPPORTED_Pause
166 | SUPPORTED_Asym_Pause
),
167 .flags
= PHY_HAS_INTERRUPT
| PHY_HAS_MAGICANEG
,
169 /* basic functions */
170 .config_aneg
= genphy_config_aneg
,
171 .read_status
= genphy_read_status
,
172 .config_init
= smsc_phy_config_init
,
173 .soft_reset
= smsc_phy_reset
,
176 .ack_interrupt
= smsc_phy_ack_interrupt
,
177 .config_intr
= smsc_phy_config_intr
,
179 .suspend
= genphy_suspend
,
180 .resume
= genphy_resume
,
182 .driver
= { .owner
= THIS_MODULE
, }
184 .phy_id
= 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */
185 .phy_id_mask
= 0xfffffff0,
186 .name
= "SMSC LAN8700",
188 .features
= (PHY_BASIC_FEATURES
| SUPPORTED_Pause
189 | SUPPORTED_Asym_Pause
),
190 .flags
= PHY_HAS_INTERRUPT
| PHY_HAS_MAGICANEG
,
192 /* basic functions */
193 .config_aneg
= genphy_config_aneg
,
194 .read_status
= genphy_read_status
,
195 .config_init
= smsc_phy_config_init
,
196 .soft_reset
= smsc_phy_reset
,
199 .ack_interrupt
= smsc_phy_ack_interrupt
,
200 .config_intr
= smsc_phy_config_intr
,
202 .suspend
= genphy_suspend
,
203 .resume
= genphy_resume
,
205 .driver
= { .owner
= THIS_MODULE
, }
207 .phy_id
= 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */
208 .phy_id_mask
= 0xfffffff0,
209 .name
= "SMSC LAN911x Internal PHY",
211 .features
= (PHY_BASIC_FEATURES
| SUPPORTED_Pause
212 | SUPPORTED_Asym_Pause
),
213 .flags
= PHY_HAS_INTERRUPT
| PHY_HAS_MAGICANEG
,
215 /* basic functions */
216 .config_aneg
= genphy_config_aneg
,
217 .read_status
= genphy_read_status
,
218 .config_init
= lan911x_config_init
,
221 .ack_interrupt
= smsc_phy_ack_interrupt
,
222 .config_intr
= smsc_phy_config_intr
,
224 .suspend
= genphy_suspend
,
225 .resume
= genphy_resume
,
227 .driver
= { .owner
= THIS_MODULE
, }
229 .phy_id
= 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */
230 .phy_id_mask
= 0xfffffff0,
231 .name
= "SMSC LAN8710/LAN8720",
233 .features
= (PHY_BASIC_FEATURES
| SUPPORTED_Pause
234 | SUPPORTED_Asym_Pause
),
235 .flags
= PHY_HAS_INTERRUPT
| PHY_HAS_MAGICANEG
,
237 /* basic functions */
238 .config_aneg
= genphy_config_aneg
,
239 .read_status
= lan87xx_read_status
,
240 .config_init
= smsc_phy_config_init
,
241 .soft_reset
= smsc_phy_reset
,
244 .ack_interrupt
= smsc_phy_ack_interrupt
,
245 .config_intr
= smsc_phy_config_intr
,
247 .suspend
= genphy_suspend
,
248 .resume
= genphy_resume
,
250 .driver
= { .owner
= THIS_MODULE
, }
253 module_phy_driver(smsc_phy_driver
);
255 MODULE_DESCRIPTION("SMSC PHY driver");
256 MODULE_AUTHOR("Herbert Valerio Riedel");
257 MODULE_LICENSE("GPL");
259 static struct mdio_device_id __maybe_unused smsc_tbl
[] = {
260 { 0x0007c0a0, 0xfffffff0 },
261 { 0x0007c0b0, 0xfffffff0 },
262 { 0x0007c0c0, 0xfffffff0 },
263 { 0x0007c0d0, 0xfffffff0 },
264 { 0x0007c0f0, 0xfffffff0 },
268 MODULE_DEVICE_TABLE(mdio
, smsc_tbl
);