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_config_aneg(struct phy_device *phydev)
|
47
|
{
|
48
|
int err;
|
49
|
printk(KERN_ALERT "SMSC_config_aneg\n");
|
50
|
phydev->duplex = DUPLEX_FULL;
|
51
|
phydev->speed = SPEED_100;
|
52
|
phydev->autoneg = AUTONEG_DISABLE;
|
53
|
phydev->interface == PHY_INTERFACE_MODE_RGMII_ID;
|
54
|
err = phy_write(phydev, 0, 0x2000);
|
55
|
if(err<0) return err;
|
56
|
return 0;
|
57
|
}
|
58
|
|
59
|
static int smsc_phy_config_intr(struct phy_device *phydev)
|
60
|
{
|
61
|
int rc = phy_write (phydev, MII_LAN83C185_IM,
|
62
|
((PHY_INTERRUPT_ENABLED == phydev->interrupts)
|
63
|
? MII_LAN83C185_ISF_INT_PHYLIB_EVENTS : 0));
|
64
|
/*int rd = phy_write (phydev, 0, 0x2000); // override auto mdx for now. force 100M*/
|
65
|
return rc < 0 ? rc : 0;
|
66
|
}
|
67
|
|
68
|
static int smsc_phy_ack_interrupt(struct phy_device *phydev)
|
69
|
{
|
70
|
int rc = phy_read (phydev, MII_LAN83C185_ISF);
|
71
|
|
72
|
return rc < 0 ? rc : 0;
|
73
|
}
|
74
|
|
75
|
static int smsc_phy_config_init(struct phy_device *phydev)
|
76
|
{
|
77
|
int phyregloop;
|
78
|
int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
|
79
|
if (rc < 0)
|
80
|
return rc;
|
81
|
|
82
|
/* Enable energy detect mode for this SMSC Transceivers */
|
83
|
//c = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
|
84
|
// rc | MII_LAN83C185_EDPWRDOWN);
|
85
|
//if (rc < 0)
|
86
|
// return rc;
|
87
|
printk(KERN_ALERT "smsc_phy_config_init\n");
|
88
|
int rd = phy_write (phydev, 0, 0x2000); // override auto mdx for now. force 100M
|
89
|
//
|
90
|
// dump the phy registers
|
91
|
for(phyregloop = 0; phyregloop<6; phyregloop++)
|
92
|
{
|
93
|
rc = phy_read(phydev, phyregloop);
|
94
|
printk(KERN_ALERT "..... smsc PHY REGS: %d: VAL: %x \n", phyregloop, rc);
|
95
|
}
|
96
|
return smsc_phy_ack_interrupt (phydev);
|
97
|
}
|
98
|
|
99
|
static int lan911x_config_init(struct phy_device *phydev)
|
100
|
{
|
101
|
return smsc_phy_ack_interrupt(phydev);
|
102
|
}
|
103
|
|
104
|
static struct phy_driver lan83c185_driver = {
|
105
|
.phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */
|
106
|
.phy_id_mask = 0xfffffff0,
|
107
|
.name = "SMSC LAN83C185",
|
108
|
|
109
|
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
|
110
|
| SUPPORTED_Asym_Pause),
|
111
|
.flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
|
112
|
|
113
|
/* basic functions */
|
114
|
.config_aneg = genphy_config_aneg,
|
115
|
.read_status = genphy_read_status,
|
116
|
.config_init = smsc_phy_config_init,
|
117
|
|
118
|
/* IRQ related */
|
119
|
.ack_interrupt = smsc_phy_ack_interrupt,
|
120
|
.config_intr = smsc_phy_config_intr,
|
121
|
|
122
|
.suspend = genphy_suspend,
|
123
|
.resume = genphy_resume,
|
124
|
|
125
|
.driver = { .owner = THIS_MODULE, }
|
126
|
};
|
127
|
|
128
|
static struct phy_driver lan8187_driver = {
|
129
|
.phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */
|
130
|
.phy_id_mask = 0xfffffff0,
|
131
|
.name = "SMSC LAN8187",
|
132
|
|
133
|
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
|
134
|
| SUPPORTED_Asym_Pause),
|
135
|
.flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
|
136
|
|
137
|
/* basic functions */
|
138
|
.config_aneg = genphy_config_aneg,
|
139
|
.read_status = genphy_read_status,
|
140
|
.config_init = smsc_phy_config_init,
|
141
|
|
142
|
/* IRQ related */
|
143
|
.ack_interrupt = smsc_phy_ack_interrupt,
|
144
|
.config_intr = smsc_phy_config_intr,
|
145
|
|
146
|
.suspend = genphy_suspend,
|
147
|
.resume = genphy_resume,
|
148
|
|
149
|
.driver = { .owner = THIS_MODULE, }
|
150
|
};
|
151
|
|
152
|
static struct phy_driver lan8700_driver = {
|
153
|
.phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */
|
154
|
.phy_id_mask = 0xfffffff0,
|
155
|
.name = "SMSC LAN8700",
|
156
|
|
157
|
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
|
158
|
| SUPPORTED_Asym_Pause),
|
159
|
.flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
|
160
|
|
161
|
/* basic functions */
|
162
|
.config_aneg = genphy_config_aneg,
|
163
|
.read_status = genphy_read_status,
|
164
|
.config_init = smsc_phy_config_init,
|
165
|
|
166
|
/* IRQ related */
|
167
|
.ack_interrupt = smsc_phy_ack_interrupt,
|
168
|
.config_intr = smsc_phy_config_intr,
|
169
|
|
170
|
.suspend = genphy_suspend,
|
171
|
.resume = genphy_resume,
|
172
|
|
173
|
.driver = { .owner = THIS_MODULE, }
|
174
|
};
|
175
|
static struct phy_driver lan8820_driver = {
|
176
|
.phy_id = 0x0007c0e0, /* OUI=0x00800f, Model#=0x0c */
|
177
|
.phy_id_mask = 0xfffffff0,
|
178
|
.name = "SMSC LAN8820",
|
179
|
|
180
|
.features = PHY_BASIC_FEATURES,
|
181
|
.flags = PHY_POLL,
|
182
|
|
183
|
/* basic functions */
|
184
|
//.speed = SPEED_100,
|
185
|
//.autoneg = AUTONEG_DISABLE,
|
186
|
.config_aneg = &SMSC_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
|
|
200
|
static struct phy_driver lan911x_int_driver = {
|
201
|
.phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */
|
202
|
.phy_id_mask = 0xfffffff0,
|
203
|
.name = "SMSC LAN911x Internal PHY",
|
204
|
|
205
|
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
|
206
|
| SUPPORTED_Asym_Pause),
|
207
|
.flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
|
208
|
|
209
|
/* basic functions */
|
210
|
.config_aneg = genphy_config_aneg,
|
211
|
.read_status = genphy_read_status,
|
212
|
.config_init = lan911x_config_init,
|
213
|
|
214
|
/* IRQ related */
|
215
|
.ack_interrupt = smsc_phy_ack_interrupt,
|
216
|
.config_intr = smsc_phy_config_intr,
|
217
|
|
218
|
.suspend = genphy_suspend,
|
219
|
.resume = genphy_resume,
|
220
|
|
221
|
.driver = { .owner = THIS_MODULE, }
|
222
|
};
|
223
|
|
224
|
static struct phy_driver lan8710_driver = {
|
225
|
.phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */
|
226
|
.phy_id_mask = 0xfffffff0,
|
227
|
.name = "SMSC LAN8710/LAN8720",
|
228
|
|
229
|
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
|
230
|
| SUPPORTED_Asym_Pause),
|
231
|
.flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
|
232
|
|
233
|
/* basic functions */
|
234
|
.config_aneg = genphy_config_aneg,
|
235
|
.read_status = genphy_read_status,
|
236
|
.config_init = smsc_phy_config_init,
|
237
|
|
238
|
/* IRQ related */
|
239
|
.ack_interrupt = smsc_phy_ack_interrupt,
|
240
|
.config_intr = smsc_phy_config_intr,
|
241
|
|
242
|
.suspend = genphy_suspend,
|
243
|
.resume = genphy_resume,
|
244
|
|
245
|
.driver = { .owner = THIS_MODULE, }
|
246
|
};
|
247
|
|
248
|
static int __init smsc_init(void)
|
249
|
{
|
250
|
int ret;
|
251
|
printk(KERN_ALERT "INIT SMSC PHY.\n");
|
252
|
|
253
|
ret = phy_driver_register (&lan83c185_driver);
|
254
|
if (ret)
|
255
|
goto err1;
|
256
|
|
257
|
ret = phy_driver_register (&lan8187_driver);
|
258
|
if (ret)
|
259
|
goto err2;
|
260
|
|
261
|
ret = phy_driver_register (&lan8700_driver);
|
262
|
if (ret)
|
263
|
goto err3;
|
264
|
ret = phy_driver_register (&lan8820_driver);
|
265
|
printk(KERN_ALERT "SMSC lan8820_driver status: %x\n", ret);
|
266
|
if (ret)
|
267
|
goto err6;
|
268
|
|
269
|
ret = phy_driver_register (&lan911x_int_driver);
|
270
|
if (ret)
|
271
|
goto err4;
|
272
|
|
273
|
ret = phy_driver_register (&lan8710_driver);
|
274
|
if (ret)
|
275
|
goto err5;
|
276
|
|
277
|
return 0;
|
278
|
err6:
|
279
|
phy_driver_unregister (&lan8820_driver);
|
280
|
err5:
|
281
|
phy_driver_unregister (&lan911x_int_driver);
|
282
|
err4:
|
283
|
phy_driver_unregister (&lan8700_driver);
|
284
|
err3:
|
285
|
phy_driver_unregister (&lan8187_driver);
|
286
|
err2:
|
287
|
phy_driver_unregister (&lan83c185_driver);
|
288
|
err1:
|
289
|
return ret;
|
290
|
}
|
291
|
|
292
|
static void __exit smsc_exit(void)
|
293
|
{
|
294
|
phy_driver_unregister (&lan8710_driver);
|
295
|
phy_driver_unregister (&lan8820_driver);
|
296
|
phy_driver_unregister (&lan911x_int_driver);
|
297
|
phy_driver_unregister (&lan8700_driver);
|
298
|
phy_driver_unregister (&lan8187_driver);
|
299
|
phy_driver_unregister (&lan83c185_driver);
|
300
|
}
|
301
|
|
302
|
MODULE_DESCRIPTION("SMSC PHY driver");
|
303
|
MODULE_AUTHOR("Herbert Valerio Riedel");
|
304
|
MODULE_LICENSE("GPL");
|
305
|
|
306
|
module_init(smsc_init);
|
307
|
module_exit(smsc_exit);
|
308
|
|
309
|
static struct mdio_device_id __maybe_unused smsc_tbl[] = {
|
310
|
{ 0x0007c0a0, 0xfffffff0 },
|
311
|
{ 0x0007c0b0, 0xfffffff0 },
|
312
|
{ 0x0007c0c0, 0xfffffff0 },
|
313
|
{ 0x0007c0d0, 0xfffffff0 },
|
314
|
{ 0x0007c0e0, 0xfffffff0 },
|
315
|
{ 0x0007c0f0, 0xfffffff0 },
|
316
|
{ }
|
317
|
};
|
318
|
|
319
|
MODULE_DEVICE_TABLE(mdio, smsc_tbl);
|