Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / 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_CTRL_STATUS);
47
48         if (rc < 0)
49                 return rc;
50
51         /* Enable energy detect mode for this SMSC Transceivers */
52         rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
53                        rc | MII_LAN83C185_EDPWRDOWN);
54         if (rc < 0)
55                 return rc;
56
57         return smsc_phy_ack_interrupt(phydev);
58 }
59
60 static int smsc_phy_reset(struct phy_device *phydev)
61 {
62         int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES);
63         if (rc < 0)
64                 return rc;
65
66         /* If the SMSC PHY is in power down mode, then set it
67          * in all capable mode before using it.
68          */
69         if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) {
70                 int timeout = 50000;
71
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);
76
77                 /* wait end of reset (max 500 ms) */
78                 do {
79                         udelay(10);
80                         if (timeout-- == 0)
81                                 return -1;
82                         rc = phy_read(phydev, MII_BMCR);
83                 } while (rc & BMCR_RESET);
84         }
85         return 0;
86 }
87
88 static int lan911x_config_init(struct phy_device *phydev)
89 {
90         return smsc_phy_ack_interrupt(phydev);
91 }
92
93 /*
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
97  * down.
98  *
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.
103  */
104 static int lan87xx_read_status(struct phy_device *phydev)
105 {
106         int err = genphy_read_status(phydev);
107
108         if (!phydev->link) {
109                 /* Disable EDPD to wake up PHY */
110                 int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
111                 if (rc < 0)
112                         return rc;
113
114                 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
115                                rc & ~MII_LAN83C185_EDPWRDOWN);
116                 if (rc < 0)
117                         return rc;
118
119                 /* Sleep 64 ms to allow ~5 link test pulses to be sent */
120                 msleep(64);
121
122                 /* Re-enable EDPD */
123                 rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
124                 if (rc < 0)
125                         return rc;
126
127                 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
128                                rc | MII_LAN83C185_EDPWRDOWN);
129                 if (rc < 0)
130                         return rc;
131         }
132
133         return err;
134 }
135
136 static struct phy_driver smsc_phy_driver[] = {
137 {
138         .phy_id         = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */
139         .phy_id_mask    = 0xfffffff0,
140         .name           = "SMSC LAN83C185",
141
142         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
143                                 | SUPPORTED_Asym_Pause),
144         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
145
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,
151
152         /* IRQ related */
153         .ack_interrupt  = smsc_phy_ack_interrupt,
154         .config_intr    = smsc_phy_config_intr,
155
156         .suspend        = genphy_suspend,
157         .resume         = genphy_resume,
158
159         .driver         = { .owner = THIS_MODULE, }
160 }, {
161         .phy_id         = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */
162         .phy_id_mask    = 0xfffffff0,
163         .name           = "SMSC LAN8187",
164
165         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
166                                 | SUPPORTED_Asym_Pause),
167         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
168
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,
174
175         /* IRQ related */
176         .ack_interrupt  = smsc_phy_ack_interrupt,
177         .config_intr    = smsc_phy_config_intr,
178
179         .suspend        = genphy_suspend,
180         .resume         = genphy_resume,
181
182         .driver         = { .owner = THIS_MODULE, }
183 }, {
184         .phy_id         = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */
185         .phy_id_mask    = 0xfffffff0,
186         .name           = "SMSC LAN8700",
187
188         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
189                                 | SUPPORTED_Asym_Pause),
190         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
191
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,
197
198         /* IRQ related */
199         .ack_interrupt  = smsc_phy_ack_interrupt,
200         .config_intr    = smsc_phy_config_intr,
201
202         .suspend        = genphy_suspend,
203         .resume         = genphy_resume,
204
205         .driver         = { .owner = THIS_MODULE, }
206 }, {
207         .phy_id         = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */
208         .phy_id_mask    = 0xfffffff0,
209         .name           = "SMSC LAN911x Internal PHY",
210
211         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
212                                 | SUPPORTED_Asym_Pause),
213         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
214
215         /* basic functions */
216         .config_aneg    = genphy_config_aneg,
217         .read_status    = genphy_read_status,
218         .config_init    = lan911x_config_init,
219
220         /* IRQ related */
221         .ack_interrupt  = smsc_phy_ack_interrupt,
222         .config_intr    = smsc_phy_config_intr,
223
224         .suspend        = genphy_suspend,
225         .resume         = genphy_resume,
226
227         .driver         = { .owner = THIS_MODULE, }
228 }, {
229         .phy_id         = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */
230         .phy_id_mask    = 0xfffffff0,
231         .name           = "SMSC LAN8710/LAN8720",
232
233         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
234                                 | SUPPORTED_Asym_Pause),
235         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
236
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,
242
243         /* IRQ related */
244         .ack_interrupt  = smsc_phy_ack_interrupt,
245         .config_intr    = smsc_phy_config_intr,
246
247         .suspend        = genphy_suspend,
248         .resume         = genphy_resume,
249
250         .driver         = { .owner = THIS_MODULE, }
251 } };
252
253 module_phy_driver(smsc_phy_driver);
254
255 MODULE_DESCRIPTION("SMSC PHY driver");
256 MODULE_AUTHOR("Herbert Valerio Riedel");
257 MODULE_LICENSE("GPL");
258
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 },
265         { }
266 };
267
268 MODULE_DEVICE_TABLE(mdio, smsc_tbl);