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