]> Git Repo - linux.git/blob - drivers/net/phy/smsc.c
tcp: add tp->tcp_mstamp field
[linux.git] / 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 <[email protected]>
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 [email protected]
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/of.h>
24 #include <linux/phy.h>
25 #include <linux/netdevice.h>
26 #include <linux/smscphy.h>
27
28 struct smsc_phy_priv {
29         bool energy_enable;
30 };
31
32 static int smsc_phy_config_intr(struct phy_device *phydev)
33 {
34         int rc = phy_write (phydev, MII_LAN83C185_IM,
35                         ((PHY_INTERRUPT_ENABLED == phydev->interrupts)
36                         ? MII_LAN83C185_ISF_INT_PHYLIB_EVENTS
37                         : 0));
38
39         return rc < 0 ? rc : 0;
40 }
41
42 static int smsc_phy_ack_interrupt(struct phy_device *phydev)
43 {
44         int rc = phy_read (phydev, MII_LAN83C185_ISF);
45
46         return rc < 0 ? rc : 0;
47 }
48
49 static int smsc_phy_config_init(struct phy_device *phydev)
50 {
51         struct smsc_phy_priv *priv = phydev->priv;
52
53         int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
54
55         if (rc < 0)
56                 return rc;
57
58         if (priv->energy_enable) {
59                 /* Enable energy detect mode for this SMSC Transceivers */
60                 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
61                                rc | MII_LAN83C185_EDPWRDOWN);
62                 if (rc < 0)
63                         return rc;
64         }
65
66         return smsc_phy_ack_interrupt(phydev);
67 }
68
69 static int smsc_phy_reset(struct phy_device *phydev)
70 {
71         int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES);
72         if (rc < 0)
73                 return rc;
74
75         /* If the SMSC PHY is in power down mode, then set it
76          * in all capable mode before using it.
77          */
78         if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) {
79                 /* set "all capable" mode */
80                 rc |= MII_LAN83C185_MODE_ALL;
81                 phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc);
82         }
83
84         /* reset the phy */
85         return genphy_soft_reset(phydev);
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 LAN87xx suffers from rare absence of the ENERGYON-bit when Ethernet cable
95  * plugs in while LAN87xx is in Energy Detect Power-Down mode. This leads to
96  * unstable detection of plugging in Ethernet cable.
97  * This workaround disables Energy Detect Power-Down mode and waiting for
98  * response on link pulses to detect presence of plugged Ethernet cable.
99  * The Energy Detect Power-Down mode is enabled again in the end of procedure to
100  * save approximately 220 mW of power if cable is unplugged.
101  */
102 static int lan87xx_read_status(struct phy_device *phydev)
103 {
104         struct smsc_phy_priv *priv = phydev->priv;
105
106         int err = genphy_read_status(phydev);
107
108         if (!phydev->link && priv->energy_enable) {
109                 int i;
110
111                 /* Disable EDPD to wake up PHY */
112                 int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
113                 if (rc < 0)
114                         return rc;
115
116                 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
117                                rc & ~MII_LAN83C185_EDPWRDOWN);
118                 if (rc < 0)
119                         return rc;
120
121                 /* Wait max 640 ms to detect energy */
122                 for (i = 0; i < 64; i++) {
123                         /* Sleep to allow link test pulses to be sent */
124                         msleep(10);
125                         rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
126                         if (rc < 0)
127                                 return rc;
128                         if (rc & MII_LAN83C185_ENERGYON)
129                                 break;
130                 }
131
132                 /* Re-enable EDPD */
133                 rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
134                 if (rc < 0)
135                         return rc;
136
137                 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
138                                rc | MII_LAN83C185_EDPWRDOWN);
139                 if (rc < 0)
140                         return rc;
141         }
142
143         return err;
144 }
145
146 static int smsc_phy_probe(struct phy_device *phydev)
147 {
148         struct device *dev = &phydev->mdio.dev;
149         struct device_node *of_node = dev->of_node;
150         struct smsc_phy_priv *priv;
151
152         priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
153         if (!priv)
154                 return -ENOMEM;
155
156         priv->energy_enable = true;
157
158         if (of_property_read_bool(of_node, "smsc,disable-energy-detect"))
159                 priv->energy_enable = false;
160
161         phydev->priv = priv;
162
163         return 0;
164 }
165
166 static struct phy_driver smsc_phy_driver[] = {
167 {
168         .phy_id         = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */
169         .phy_id_mask    = 0xfffffff0,
170         .name           = "SMSC LAN83C185",
171
172         .features       = PHY_BASIC_FEATURES,
173         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
174
175         .probe          = smsc_phy_probe,
176
177         /* basic functions */
178         .config_aneg    = genphy_config_aneg,
179         .read_status    = genphy_read_status,
180         .config_init    = smsc_phy_config_init,
181         .soft_reset     = smsc_phy_reset,
182
183         /* IRQ related */
184         .ack_interrupt  = smsc_phy_ack_interrupt,
185         .config_intr    = smsc_phy_config_intr,
186
187         .suspend        = genphy_suspend,
188         .resume         = genphy_resume,
189 }, {
190         .phy_id         = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */
191         .phy_id_mask    = 0xfffffff0,
192         .name           = "SMSC LAN8187",
193
194         .features       = PHY_BASIC_FEATURES,
195         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
196
197         .probe          = smsc_phy_probe,
198
199         /* basic functions */
200         .config_aneg    = genphy_config_aneg,
201         .read_status    = genphy_read_status,
202         .config_init    = smsc_phy_config_init,
203         .soft_reset     = smsc_phy_reset,
204
205         /* IRQ related */
206         .ack_interrupt  = smsc_phy_ack_interrupt,
207         .config_intr    = smsc_phy_config_intr,
208
209         .suspend        = genphy_suspend,
210         .resume         = genphy_resume,
211 }, {
212         .phy_id         = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */
213         .phy_id_mask    = 0xfffffff0,
214         .name           = "SMSC LAN8700",
215
216         .features       = PHY_BASIC_FEATURES,
217         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
218
219         .probe          = smsc_phy_probe,
220
221         /* basic functions */
222         .config_aneg    = genphy_config_aneg,
223         .read_status    = lan87xx_read_status,
224         .config_init    = smsc_phy_config_init,
225         .soft_reset     = smsc_phy_reset,
226
227         /* IRQ related */
228         .ack_interrupt  = smsc_phy_ack_interrupt,
229         .config_intr    = smsc_phy_config_intr,
230
231         .suspend        = genphy_suspend,
232         .resume         = genphy_resume,
233 }, {
234         .phy_id         = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */
235         .phy_id_mask    = 0xfffffff0,
236         .name           = "SMSC LAN911x Internal PHY",
237
238         .features       = PHY_BASIC_FEATURES,
239         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
240
241         .probe          = smsc_phy_probe,
242
243         /* basic functions */
244         .config_aneg    = genphy_config_aneg,
245         .read_status    = genphy_read_status,
246         .config_init    = lan911x_config_init,
247
248         /* IRQ related */
249         .ack_interrupt  = smsc_phy_ack_interrupt,
250         .config_intr    = smsc_phy_config_intr,
251
252         .suspend        = genphy_suspend,
253         .resume         = genphy_resume,
254 }, {
255         .phy_id         = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */
256         .phy_id_mask    = 0xfffffff0,
257         .name           = "SMSC LAN8710/LAN8720",
258
259         .features       = PHY_BASIC_FEATURES,
260         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
261
262         .probe          = smsc_phy_probe,
263
264         /* basic functions */
265         .config_aneg    = genphy_config_aneg,
266         .read_status    = lan87xx_read_status,
267         .config_init    = smsc_phy_config_init,
268         .soft_reset     = smsc_phy_reset,
269
270         /* IRQ related */
271         .ack_interrupt  = smsc_phy_ack_interrupt,
272         .config_intr    = smsc_phy_config_intr,
273
274         .suspend        = genphy_suspend,
275         .resume         = genphy_resume,
276 }, {
277         .phy_id         = 0x0007c110,
278         .phy_id_mask    = 0xfffffff0,
279         .name           = "SMSC LAN8740",
280
281         .features       = PHY_BASIC_FEATURES,
282         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
283
284         .probe          = smsc_phy_probe,
285
286         /* basic functions */
287         .config_aneg    = genphy_config_aneg,
288         .read_status    = lan87xx_read_status,
289         .config_init    = smsc_phy_config_init,
290         .soft_reset     = smsc_phy_reset,
291
292         /* IRQ related */
293         .ack_interrupt  = smsc_phy_ack_interrupt,
294         .config_intr    = smsc_phy_config_intr,
295
296         .suspend        = genphy_suspend,
297         .resume         = genphy_resume,
298 } };
299
300 module_phy_driver(smsc_phy_driver);
301
302 MODULE_DESCRIPTION("SMSC PHY driver");
303 MODULE_AUTHOR("Herbert Valerio Riedel");
304 MODULE_LICENSE("GPL");
305
306 static struct mdio_device_id __maybe_unused smsc_tbl[] = {
307         { 0x0007c0a0, 0xfffffff0 },
308         { 0x0007c0b0, 0xfffffff0 },
309         { 0x0007c0c0, 0xfffffff0 },
310         { 0x0007c0d0, 0xfffffff0 },
311         { 0x0007c0f0, 0xfffffff0 },
312         { 0x0007c110, 0xfffffff0 },
313         { }
314 };
315
316 MODULE_DEVICE_TABLE(mdio, smsc_tbl);
This page took 0.049648 seconds and 4 git commands to generate.