1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30#include <linux/kernel.h>
31#include <linux/module.h>
32#include <linux/netdevice.h>
33#include <linux/ethtool.h>
34#include <linux/mdio.h>
35
36static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
37{
38 u32 result = 0;
39 int advert;
40
41 advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
42 if (advert & LPA_LPACK)
43 result |= ADVERTISED_Autoneg;
44 if (advert & ADVERTISE_10HALF)
45 result |= ADVERTISED_10baseT_Half;
46 if (advert & ADVERTISE_10FULL)
47 result |= ADVERTISED_10baseT_Full;
48 if (advert & ADVERTISE_100HALF)
49 result |= ADVERTISED_100baseT_Half;
50 if (advert & ADVERTISE_100FULL)
51 result |= ADVERTISED_100baseT_Full;
52 if (advert & ADVERTISE_PAUSE_CAP)
53 result |= ADVERTISED_Pause;
54 if (advert & ADVERTISE_PAUSE_ASYM)
55 result |= ADVERTISED_Asym_Pause;
56
57 return result;
58}
59
60
61
62
63
64
65
66
67
68
69
70int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
71{
72 struct net_device *dev = mii->dev;
73 u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
74 u32 nego;
75
76 ecmd->supported =
77 (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
78 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
79 SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
80 if (mii->supports_gmii)
81 ecmd->supported |= SUPPORTED_1000baseT_Half |
82 SUPPORTED_1000baseT_Full;
83
84
85 ecmd->port = PORT_MII;
86
87
88 ecmd->transceiver = XCVR_INTERNAL;
89
90
91 ecmd->phy_address = mii->phy_id;
92 ecmd->mdio_support = MDIO_SUPPORTS_C22;
93
94 ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
95
96 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
97 bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
98 if (mii->supports_gmii) {
99 ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
100 stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
101 }
102 if (bmcr & BMCR_ANENABLE) {
103 ecmd->advertising |= ADVERTISED_Autoneg;
104 ecmd->autoneg = AUTONEG_ENABLE;
105
106 ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE);
107 if (ctrl1000 & ADVERTISE_1000HALF)
108 ecmd->advertising |= ADVERTISED_1000baseT_Half;
109 if (ctrl1000 & ADVERTISE_1000FULL)
110 ecmd->advertising |= ADVERTISED_1000baseT_Full;
111
112 if (bmsr & BMSR_ANEGCOMPLETE) {
113 ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
114 if (stat1000 & LPA_1000HALF)
115 ecmd->lp_advertising |=
116 ADVERTISED_1000baseT_Half;
117 if (stat1000 & LPA_1000FULL)
118 ecmd->lp_advertising |=
119 ADVERTISED_1000baseT_Full;
120 } else {
121 ecmd->lp_advertising = 0;
122 }
123
124 nego = ecmd->advertising & ecmd->lp_advertising;
125
126 if (nego & (ADVERTISED_1000baseT_Full |
127 ADVERTISED_1000baseT_Half)) {
128 ethtool_cmd_speed_set(ecmd, SPEED_1000);
129 ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
130 } else if (nego & (ADVERTISED_100baseT_Full |
131 ADVERTISED_100baseT_Half)) {
132 ethtool_cmd_speed_set(ecmd, SPEED_100);
133 ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
134 } else {
135 ethtool_cmd_speed_set(ecmd, SPEED_10);
136 ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
137 }
138 } else {
139 ecmd->autoneg = AUTONEG_DISABLE;
140
141 ethtool_cmd_speed_set(ecmd,
142 ((bmcr & BMCR_SPEED1000 &&
143 (bmcr & BMCR_SPEED100) == 0) ?
144 SPEED_1000 :
145 ((bmcr & BMCR_SPEED100) ?
146 SPEED_100 : SPEED_10)));
147 ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
148 }
149
150 mii->full_duplex = ecmd->duplex;
151
152
153
154 return 0;
155}
156
157
158
159
160
161
162
163
164int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
165{
166 struct net_device *dev = mii->dev;
167 u32 speed = ethtool_cmd_speed(ecmd);
168
169 if (speed != SPEED_10 &&
170 speed != SPEED_100 &&
171 speed != SPEED_1000)
172 return -EINVAL;
173 if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
174 return -EINVAL;
175 if (ecmd->port != PORT_MII)
176 return -EINVAL;
177 if (ecmd->transceiver != XCVR_INTERNAL)
178 return -EINVAL;
179 if (ecmd->phy_address != mii->phy_id)
180 return -EINVAL;
181 if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
182 return -EINVAL;
183 if ((speed == SPEED_1000) && (!mii->supports_gmii))
184 return -EINVAL;
185
186
187
188 if (ecmd->autoneg == AUTONEG_ENABLE) {
189 u32 bmcr, advert, tmp;
190 u32 advert2 = 0, tmp2 = 0;
191
192 if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
193 ADVERTISED_10baseT_Full |
194 ADVERTISED_100baseT_Half |
195 ADVERTISED_100baseT_Full |
196 ADVERTISED_1000baseT_Half |
197 ADVERTISED_1000baseT_Full)) == 0)
198 return -EINVAL;
199
200
201 advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
202 tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
203 if (mii->supports_gmii) {
204 advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
205 tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
206 }
207 if (ecmd->advertising & ADVERTISED_10baseT_Half)
208 tmp |= ADVERTISE_10HALF;
209 if (ecmd->advertising & ADVERTISED_10baseT_Full)
210 tmp |= ADVERTISE_10FULL;
211 if (ecmd->advertising & ADVERTISED_100baseT_Half)
212 tmp |= ADVERTISE_100HALF;
213 if (ecmd->advertising & ADVERTISED_100baseT_Full)
214 tmp |= ADVERTISE_100FULL;
215 if (mii->supports_gmii) {
216 if (ecmd->advertising & ADVERTISED_1000baseT_Half)
217 tmp2 |= ADVERTISE_1000HALF;
218 if (ecmd->advertising & ADVERTISED_1000baseT_Full)
219 tmp2 |= ADVERTISE_1000FULL;
220 }
221 if (advert != tmp) {
222 mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
223 mii->advertising = tmp;
224 }
225 if ((mii->supports_gmii) && (advert2 != tmp2))
226 mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
227
228
229 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
230 bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
231 mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
232
233 mii->force_media = 0;
234 } else {
235 u32 bmcr, tmp;
236
237
238 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
239 tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
240 BMCR_SPEED1000 | BMCR_FULLDPLX);
241 if (speed == SPEED_1000)
242 tmp |= BMCR_SPEED1000;
243 else if (speed == SPEED_100)
244 tmp |= BMCR_SPEED100;
245 if (ecmd->duplex == DUPLEX_FULL) {
246 tmp |= BMCR_FULLDPLX;
247 mii->full_duplex = 1;
248 } else
249 mii->full_duplex = 0;
250 if (bmcr != tmp)
251 mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
252
253 mii->force_media = 1;
254 }
255 return 0;
256}
257
258
259
260
261
262int mii_check_gmii_support(struct mii_if_info *mii)
263{
264 int reg;
265
266 reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
267 if (reg & BMSR_ESTATEN) {
268 reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
269 if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
270 return 1;
271 }
272
273 return 0;
274}
275
276
277
278
279
280
281
282int mii_link_ok (struct mii_if_info *mii)
283{
284
285 mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
286 if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
287 return 1;
288 return 0;
289}
290
291
292
293
294
295
296
297int mii_nway_restart (struct mii_if_info *mii)
298{
299 int bmcr;
300 int r = -EINVAL;
301
302
303 bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
304
305 if (bmcr & BMCR_ANENABLE) {
306 bmcr |= BMCR_ANRESTART;
307 mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
308 r = 0;
309 }
310
311 return r;
312}
313
314
315
316
317
318
319
320
321
322void mii_check_link (struct mii_if_info *mii)
323{
324 int cur_link = mii_link_ok(mii);
325 int prev_link = netif_carrier_ok(mii->dev);
326
327 if (cur_link && !prev_link)
328 netif_carrier_on(mii->dev);
329 else if (prev_link && !cur_link)
330 netif_carrier_off(mii->dev);
331}
332
333
334
335
336
337
338
339
340
341
342unsigned int mii_check_media (struct mii_if_info *mii,
343 unsigned int ok_to_print,
344 unsigned int init_media)
345{
346 unsigned int old_carrier, new_carrier;
347 int advertise, lpa, media, duplex;
348 int lpa2 = 0;
349
350
351 if (mii->force_media)
352 return 0;
353
354
355 old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
356 new_carrier = (unsigned int) mii_link_ok(mii);
357
358
359
360
361 if ((!init_media) && (old_carrier == new_carrier))
362 return 0;
363
364
365 if (!new_carrier) {
366 netif_carrier_off(mii->dev);
367 if (ok_to_print)
368 netdev_info(mii->dev, "link down\n");
369 return 0;
370 }
371
372
373
374
375 netif_carrier_on(mii->dev);
376
377
378 if ((!init_media) && (mii->advertising))
379 advertise = mii->advertising;
380 else {
381 advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
382 mii->advertising = advertise;
383 }
384 lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
385 if (mii->supports_gmii)
386 lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
387
388
389 media = mii_nway_result(lpa & advertise);
390 duplex = (media & ADVERTISE_FULL) ? 1 : 0;
391 if (lpa2 & LPA_1000FULL)
392 duplex = 1;
393
394 if (ok_to_print)
395 netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
396 lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
397 media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
398 100 : 10,
399 duplex ? "full" : "half",
400 lpa);
401
402 if ((init_media) || (mii->full_duplex != duplex)) {
403 mii->full_duplex = duplex;
404 return 1;
405 }
406
407 return 0;
408}
409
410
411
412
413
414
415
416
417
418
419
420int generic_mii_ioctl(struct mii_if_info *mii_if,
421 struct mii_ioctl_data *mii_data, int cmd,
422 unsigned int *duplex_chg_out)
423{
424 int rc = 0;
425 unsigned int duplex_changed = 0;
426
427 if (duplex_chg_out)
428 *duplex_chg_out = 0;
429
430 mii_data->phy_id &= mii_if->phy_id_mask;
431 mii_data->reg_num &= mii_if->reg_num_mask;
432
433 switch(cmd) {
434 case SIOCGMIIPHY:
435 mii_data->phy_id = mii_if->phy_id;
436
437
438 case SIOCGMIIREG:
439 mii_data->val_out =
440 mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
441 mii_data->reg_num);
442 break;
443
444 case SIOCSMIIREG: {
445 u16 val = mii_data->val_in;
446
447 if (mii_data->phy_id == mii_if->phy_id) {
448 switch(mii_data->reg_num) {
449 case MII_BMCR: {
450 unsigned int new_duplex = 0;
451 if (val & (BMCR_RESET|BMCR_ANENABLE))
452 mii_if->force_media = 0;
453 else
454 mii_if->force_media = 1;
455 if (mii_if->force_media &&
456 (val & BMCR_FULLDPLX))
457 new_duplex = 1;
458 if (mii_if->full_duplex != new_duplex) {
459 duplex_changed = 1;
460 mii_if->full_duplex = new_duplex;
461 }
462 break;
463 }
464 case MII_ADVERTISE:
465 mii_if->advertising = val;
466 break;
467 default:
468
469 break;
470 }
471 }
472
473 mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
474 mii_data->reg_num, val);
475 break;
476 }
477
478 default:
479 rc = -EOPNOTSUPP;
480 break;
481 }
482
483 if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
484 *duplex_chg_out = 1;
485
486 return rc;
487}
488
489MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
490MODULE_DESCRIPTION ("MII hardware support library");
491MODULE_LICENSE("GPL");
492
493EXPORT_SYMBOL(mii_link_ok);
494EXPORT_SYMBOL(mii_nway_restart);
495EXPORT_SYMBOL(mii_ethtool_gset);
496EXPORT_SYMBOL(mii_ethtool_sset);
497EXPORT_SYMBOL(mii_check_link);
498EXPORT_SYMBOL(mii_check_media);
499EXPORT_SYMBOL(mii_check_gmii_support);
500EXPORT_SYMBOL(generic_mii_ioctl);
501
502