ed2644a57500c0673b40709d574e87df06fc8533
[pandora-kernel.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@smsc.com
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
26 #define MII_LAN83C185_ISF 29 /* Interrupt Source Flags */
27 #define MII_LAN83C185_IM  30 /* Interrupt Mask */
28 #define MII_LAN83C185_CTRL_STATUS 17 /* Mode/Status Register */
29
30 #define MII_LAN83C185_ISF_INT1 (1<<1) /* Auto-Negotiation Page Received */
31 #define MII_LAN83C185_ISF_INT2 (1<<2) /* Parallel Detection Fault */
32 #define MII_LAN83C185_ISF_INT3 (1<<3) /* Auto-Negotiation LP Ack */
33 #define MII_LAN83C185_ISF_INT4 (1<<4) /* Link Down */
34 #define MII_LAN83C185_ISF_INT5 (1<<5) /* Remote Fault Detected */
35 #define MII_LAN83C185_ISF_INT6 (1<<6) /* Auto-Negotiation complete */
36 #define MII_LAN83C185_ISF_INT7 (1<<7) /* ENERGYON */
37
38 #define MII_LAN83C185_ISF_INT_ALL (0x0e)
39
40 #define MII_LAN83C185_ISF_INT_PHYLIB_EVENTS \
41         (MII_LAN83C185_ISF_INT6 | MII_LAN83C185_ISF_INT4 | \
42          MII_LAN83C185_ISF_INT7)
43
44 #define MII_LAN83C185_EDPWRDOWN (1 << 13) /* EDPWRDOWN */
45
46 static int smsc_phy_config_intr(struct phy_device *phydev)
47 {
48         int rc = phy_write (phydev, MII_LAN83C185_IM,
49                         ((PHY_INTERRUPT_ENABLED == phydev->interrupts)
50                         ? MII_LAN83C185_ISF_INT_PHYLIB_EVENTS
51                         : 0));
52
53         return rc < 0 ? rc : 0;
54 }
55
56 static int smsc_phy_ack_interrupt(struct phy_device *phydev)
57 {
58         int rc = phy_read (phydev, MII_LAN83C185_ISF);
59
60         return rc < 0 ? rc : 0;
61 }
62
63 static int smsc_phy_config_init(struct phy_device *phydev)
64 {
65         int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
66         if (rc < 0)
67                 return rc;
68
69         /* Enable energy detect mode for this SMSC Transceivers */
70         rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
71                        rc | MII_LAN83C185_EDPWRDOWN);
72         if (rc < 0)
73                 return rc;
74
75         return smsc_phy_ack_interrupt (phydev);
76 }
77
78 static int lan911x_config_init(struct phy_device *phydev)
79 {
80         return smsc_phy_ack_interrupt(phydev);
81 }
82
83 static struct phy_driver lan83c185_driver = {
84         .phy_id         = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */
85         .phy_id_mask    = 0xfffffff0,
86         .name           = "SMSC LAN83C185",
87
88         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
89                                 | SUPPORTED_Asym_Pause),
90         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
91
92         /* basic functions */
93         .config_aneg    = genphy_config_aneg,
94         .read_status    = genphy_read_status,
95         .config_init    = smsc_phy_config_init,
96
97         /* IRQ related */
98         .ack_interrupt  = smsc_phy_ack_interrupt,
99         .config_intr    = smsc_phy_config_intr,
100
101         .suspend        = genphy_suspend,
102         .resume         = genphy_resume,
103
104         .driver         = { .owner = THIS_MODULE, }
105 };
106
107 static struct phy_driver lan8187_driver = {
108         .phy_id         = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */
109         .phy_id_mask    = 0xfffffff0,
110         .name           = "SMSC LAN8187",
111
112         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
113                                 | SUPPORTED_Asym_Pause),
114         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
115
116         /* basic functions */
117         .config_aneg    = genphy_config_aneg,
118         .read_status    = genphy_read_status,
119         .config_init    = smsc_phy_config_init,
120
121         /* IRQ related */
122         .ack_interrupt  = smsc_phy_ack_interrupt,
123         .config_intr    = smsc_phy_config_intr,
124
125         .suspend        = genphy_suspend,
126         .resume         = genphy_resume,
127
128         .driver         = { .owner = THIS_MODULE, }
129 };
130
131 static struct phy_driver lan8700_driver = {
132         .phy_id         = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */
133         .phy_id_mask    = 0xfffffff0,
134         .name           = "SMSC LAN8700",
135
136         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
137                                 | SUPPORTED_Asym_Pause),
138         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
139
140         /* basic functions */
141         .config_aneg    = genphy_config_aneg,
142         .read_status    = genphy_read_status,
143         .config_init    = smsc_phy_config_init,
144
145         /* IRQ related */
146         .ack_interrupt  = smsc_phy_ack_interrupt,
147         .config_intr    = smsc_phy_config_intr,
148
149         .suspend        = genphy_suspend,
150         .resume         = genphy_resume,
151
152         .driver         = { .owner = THIS_MODULE, }
153 };
154
155 static struct phy_driver lan911x_int_driver = {
156         .phy_id         = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */
157         .phy_id_mask    = 0xfffffff0,
158         .name           = "SMSC LAN911x Internal PHY",
159
160         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
161                                 | SUPPORTED_Asym_Pause),
162         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
163
164         /* basic functions */
165         .config_aneg    = genphy_config_aneg,
166         .read_status    = genphy_read_status,
167         .config_init    = lan911x_config_init,
168
169         /* IRQ related */
170         .ack_interrupt  = smsc_phy_ack_interrupt,
171         .config_intr    = smsc_phy_config_intr,
172
173         .suspend        = genphy_suspend,
174         .resume         = genphy_resume,
175
176         .driver         = { .owner = THIS_MODULE, }
177 };
178
179 static struct phy_driver lan8710_driver = {
180         .phy_id         = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */
181         .phy_id_mask    = 0xfffffff0,
182         .name           = "SMSC LAN8710/LAN8720",
183
184         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
185                                 | SUPPORTED_Asym_Pause),
186         .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
187
188         /* basic functions */
189         .config_aneg    = genphy_config_aneg,
190         .read_status    = genphy_read_status,
191         .config_init    = smsc_phy_config_init,
192
193         /* IRQ related */
194         .ack_interrupt  = smsc_phy_ack_interrupt,
195         .config_intr    = smsc_phy_config_intr,
196
197         .suspend        = genphy_suspend,
198         .resume         = genphy_resume,
199
200         .driver         = { .owner = THIS_MODULE, }
201 };
202
203 static int __init smsc_init(void)
204 {
205         int ret;
206
207         ret = phy_driver_register (&lan83c185_driver);
208         if (ret)
209                 goto err1;
210
211         ret = phy_driver_register (&lan8187_driver);
212         if (ret)
213                 goto err2;
214
215         ret = phy_driver_register (&lan8700_driver);
216         if (ret)
217                 goto err3;
218
219         ret = phy_driver_register (&lan911x_int_driver);
220         if (ret)
221                 goto err4;
222
223         ret = phy_driver_register (&lan8710_driver);
224         if (ret)
225                 goto err5;
226
227         return 0;
228
229 err5:
230         phy_driver_unregister (&lan911x_int_driver);
231 err4:
232         phy_driver_unregister (&lan8700_driver);
233 err3:
234         phy_driver_unregister (&lan8187_driver);
235 err2:
236         phy_driver_unregister (&lan83c185_driver);
237 err1:
238         return ret;
239 }
240
241 static void __exit smsc_exit(void)
242 {
243         phy_driver_unregister (&lan8710_driver);
244         phy_driver_unregister (&lan911x_int_driver);
245         phy_driver_unregister (&lan8700_driver);
246         phy_driver_unregister (&lan8187_driver);
247         phy_driver_unregister (&lan83c185_driver);
248 }
249
250 MODULE_DESCRIPTION("SMSC PHY driver");
251 MODULE_AUTHOR("Herbert Valerio Riedel");
252 MODULE_LICENSE("GPL");
253
254 module_init(smsc_init);
255 module_exit(smsc_exit);