]> Pileus Git - ~andy/linux/blob - drivers/net/phy/smsc.c
Linux 3.14
[~andy/linux] / drivers / net / phy / smsc.c
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);