Branch data Line data Source code
1 : : /*
2 : :
3 : : mii.c: MII interface library
4 : :
5 : : Maintained by Jeff Garzik <jgarzik@pobox.com>
6 : : Copyright 2001,2002 Jeff Garzik
7 : :
8 : : Various code came from myson803.c and other files by
9 : : Donald Becker. Copyright:
10 : :
11 : : Written 1998-2002 by Donald Becker.
12 : :
13 : : This software may be used and distributed according
14 : : to the terms of the GNU General Public License (GPL),
15 : : incorporated herein by reference. Drivers based on
16 : : or derived from this code fall under the GPL and must
17 : : retain the authorship, copyright and license notice.
18 : : This file is not a complete program and may only be
19 : : used when the entire operating system is licensed
20 : : under the GPL.
21 : :
22 : : The author may be reached as becker@scyld.com, or C/O
23 : : Scyld Computing Corporation
24 : : 410 Severn Ave., Suite 210
25 : : Annapolis MD 21403
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/mii.h>
35 : :
36 : 0 : static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
37 : : {
38 : : int advert;
39 : :
40 : 0 : advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
41 : :
42 : 0 : return mii_lpa_to_ethtool_lpa_t(advert);
43 : : }
44 : :
45 : : /**
46 : : * mii_ethtool_gset - get settings that are specified in @ecmd
47 : : * @mii: MII interface
48 : : * @ecmd: requested ethtool_cmd
49 : : *
50 : : * The @ecmd parameter is expected to have been cleared before calling
51 : : * mii_ethtool_gset().
52 : : *
53 : : * Returns 0 for success, negative on error.
54 : : */
55 : 0 : int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
56 : : {
57 : 0 : struct net_device *dev = mii->dev;
58 : : u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
59 : : u32 nego;
60 : :
61 : 0 : ecmd->supported =
62 : : (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
63 : : SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
64 : : SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
65 [ # # ]: 0 : if (mii->supports_gmii)
66 : 0 : ecmd->supported |= SUPPORTED_1000baseT_Half |
67 : : SUPPORTED_1000baseT_Full;
68 : :
69 : : /* only supports twisted-pair */
70 : 0 : ecmd->port = PORT_MII;
71 : :
72 : : /* only supports internal transceiver */
73 : 0 : ecmd->transceiver = XCVR_INTERNAL;
74 : :
75 : : /* this isn't fully supported at higher layers */
76 : 0 : ecmd->phy_address = mii->phy_id;
77 : 0 : ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22;
78 : :
79 : 0 : ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
80 : :
81 : 0 : bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
82 : 0 : bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
83 [ # # ]: 0 : if (mii->supports_gmii) {
84 : 0 : ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
85 : 0 : stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
86 : : }
87 [ # # ]: 0 : if (bmcr & BMCR_ANENABLE) {
88 : 0 : ecmd->advertising |= ADVERTISED_Autoneg;
89 : 0 : ecmd->autoneg = AUTONEG_ENABLE;
90 : :
91 : 0 : ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE);
92 [ # # ]: 0 : if (mii->supports_gmii)
93 : 0 : ecmd->advertising |=
94 : : mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
95 : :
96 [ # # ]: 0 : if (bmsr & BMSR_ANEGCOMPLETE) {
97 : 0 : ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
98 : 0 : ecmd->lp_advertising |=
99 : : mii_stat1000_to_ethtool_lpa_t(stat1000);
100 : : } else {
101 : 0 : ecmd->lp_advertising = 0;
102 : : }
103 : :
104 : 0 : nego = ecmd->advertising & ecmd->lp_advertising;
105 : :
106 [ # # ]: 0 : if (nego & (ADVERTISED_1000baseT_Full |
107 : : ADVERTISED_1000baseT_Half)) {
108 : : ethtool_cmd_speed_set(ecmd, SPEED_1000);
109 : 0 : ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
110 [ # # ]: 0 : } else if (nego & (ADVERTISED_100baseT_Full |
111 : : ADVERTISED_100baseT_Half)) {
112 : : ethtool_cmd_speed_set(ecmd, SPEED_100);
113 : 0 : ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
114 : : } else {
115 : : ethtool_cmd_speed_set(ecmd, SPEED_10);
116 : 0 : ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
117 : : }
118 : : } else {
119 : 0 : ecmd->autoneg = AUTONEG_DISABLE;
120 : :
121 [ # # ][ # # ]: 0 : ethtool_cmd_speed_set(ecmd,
122 : 0 : ((bmcr & BMCR_SPEED1000 &&
123 : : (bmcr & BMCR_SPEED100) == 0) ?
124 : : SPEED_1000 :
125 : 0 : ((bmcr & BMCR_SPEED100) ?
126 : : SPEED_100 : SPEED_10)));
127 : 0 : ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
128 : : }
129 : :
130 : 0 : mii->full_duplex = ecmd->duplex;
131 : :
132 : : /* ignore maxtxpkt, maxrxpkt for now */
133 : :
134 : 0 : return 0;
135 : : }
136 : :
137 : : /**
138 : : * mii_ethtool_sset - set settings that are specified in @ecmd
139 : : * @mii: MII interface
140 : : * @ecmd: requested ethtool_cmd
141 : : *
142 : : * Returns 0 for success, negative on error.
143 : : */
144 : 0 : int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
145 : : {
146 : 0 : struct net_device *dev = mii->dev;
147 : : u32 speed = ethtool_cmd_speed(ecmd);
148 : :
149 [ # # ]: 0 : if (speed != SPEED_10 &&
150 [ # # ]: 0 : speed != SPEED_100 &&
151 : : speed != SPEED_1000)
152 : : return -EINVAL;
153 [ # # ]: 0 : if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
154 : : return -EINVAL;
155 [ # # ]: 0 : if (ecmd->port != PORT_MII)
156 : : return -EINVAL;
157 [ # # ]: 0 : if (ecmd->transceiver != XCVR_INTERNAL)
158 : : return -EINVAL;
159 [ # # ]: 0 : if (ecmd->phy_address != mii->phy_id)
160 : : return -EINVAL;
161 [ # # ]: 0 : if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
162 : : return -EINVAL;
163 [ # # ][ # # ]: 0 : if ((speed == SPEED_1000) && (!mii->supports_gmii))
164 : : return -EINVAL;
165 : :
166 : : /* ignore supported, maxtxpkt, maxrxpkt */
167 : :
168 [ # # ]: 0 : if (ecmd->autoneg == AUTONEG_ENABLE) {
169 : : u32 bmcr, advert, tmp;
170 : : u32 advert2 = 0, tmp2 = 0;
171 : :
172 [ # # ]: 0 : if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
173 : : ADVERTISED_10baseT_Full |
174 : : ADVERTISED_100baseT_Half |
175 : : ADVERTISED_100baseT_Full |
176 : : ADVERTISED_1000baseT_Half |
177 : : ADVERTISED_1000baseT_Full)) == 0)
178 : : return -EINVAL;
179 : :
180 : : /* advertise only what has been requested */
181 : 0 : advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
182 : 0 : tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
183 [ # # ]: 0 : if (mii->supports_gmii) {
184 : 0 : advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
185 : 0 : tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
186 : : }
187 : 0 : tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
188 : :
189 [ # # ]: 0 : if (mii->supports_gmii)
190 : 0 : tmp2 |=
191 : : ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
192 [ # # ]: 0 : if (advert != tmp) {
193 : 0 : mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
194 : 0 : mii->advertising = tmp;
195 : : }
196 [ # # ][ # # ]: 0 : if ((mii->supports_gmii) && (advert2 != tmp2))
197 : 0 : mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
198 : :
199 : : /* turn on autonegotiation, and force a renegotiate */
200 : 0 : bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
201 : 0 : bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
202 : 0 : mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
203 : :
204 : 0 : mii->force_media = 0;
205 : : } else {
206 : : u32 bmcr, tmp;
207 : :
208 : : /* turn off auto negotiation, set speed and duplexity */
209 : 0 : bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
210 : 0 : tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
211 : : BMCR_SPEED1000 | BMCR_FULLDPLX);
212 [ # # ]: 0 : if (speed == SPEED_1000)
213 : 0 : tmp |= BMCR_SPEED1000;
214 [ # # ]: 0 : else if (speed == SPEED_100)
215 : 0 : tmp |= BMCR_SPEED100;
216 [ # # ]: 0 : if (ecmd->duplex == DUPLEX_FULL) {
217 : 0 : tmp |= BMCR_FULLDPLX;
218 : 0 : mii->full_duplex = 1;
219 : : } else
220 : 0 : mii->full_duplex = 0;
221 [ # # ]: 0 : if (bmcr != tmp)
222 : 0 : mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
223 : :
224 : 0 : mii->force_media = 1;
225 : : }
226 : : return 0;
227 : : }
228 : :
229 : : /**
230 : : * mii_check_gmii_support - check if the MII supports Gb interfaces
231 : : * @mii: the MII interface
232 : : */
233 : 0 : int mii_check_gmii_support(struct mii_if_info *mii)
234 : : {
235 : : int reg;
236 : :
237 : 0 : reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
238 [ # # ]: 0 : if (reg & BMSR_ESTATEN) {
239 : 0 : reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
240 [ # # ]: 0 : if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
241 : : return 1;
242 : : }
243 : :
244 : : return 0;
245 : : }
246 : :
247 : : /**
248 : : * mii_link_ok - is link status up/ok
249 : : * @mii: the MII interface
250 : : *
251 : : * Returns 1 if the MII reports link status up/ok, 0 otherwise.
252 : : */
253 : 0 : int mii_link_ok (struct mii_if_info *mii)
254 : : {
255 : : /* first, a dummy read, needed to latch some MII phys */
256 : 0 : mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
257 [ # # ]: 0 : if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
258 : : return 1;
259 : 0 : return 0;
260 : : }
261 : :
262 : : /**
263 : : * mii_nway_restart - restart NWay (autonegotiation) for this interface
264 : : * @mii: the MII interface
265 : : *
266 : : * Returns 0 on success, negative on error.
267 : : */
268 : 0 : int mii_nway_restart (struct mii_if_info *mii)
269 : : {
270 : : int bmcr;
271 : : int r = -EINVAL;
272 : :
273 : : /* if autoneg is off, it's an error */
274 : 0 : bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
275 : :
276 [ # # ]: 0 : if (bmcr & BMCR_ANENABLE) {
277 : 0 : bmcr |= BMCR_ANRESTART;
278 : 0 : mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
279 : : r = 0;
280 : : }
281 : :
282 : 0 : return r;
283 : : }
284 : :
285 : : /**
286 : : * mii_check_link - check MII link status
287 : : * @mii: MII interface
288 : : *
289 : : * If the link status changed (previous != current), call
290 : : * netif_carrier_on() if current link status is Up or call
291 : : * netif_carrier_off() if current link status is Down.
292 : : */
293 : 0 : void mii_check_link (struct mii_if_info *mii)
294 : : {
295 : 0 : int cur_link = mii_link_ok(mii);
296 : 0 : int prev_link = netif_carrier_ok(mii->dev);
297 : :
298 [ # # ]: 0 : if (cur_link && !prev_link)
299 : 0 : netif_carrier_on(mii->dev);
300 [ # # ]: 0 : else if (prev_link && !cur_link)
301 : 0 : netif_carrier_off(mii->dev);
302 : 0 : }
303 : :
304 : : /**
305 : : * mii_check_media - check the MII interface for a duplex change
306 : : * @mii: the MII interface
307 : : * @ok_to_print: OK to print link up/down messages
308 : : * @init_media: OK to save duplex mode in @mii
309 : : *
310 : : * Returns 1 if the duplex mode changed, 0 if not.
311 : : * If the media type is forced, always returns 0.
312 : : */
313 : 0 : unsigned int mii_check_media (struct mii_if_info *mii,
314 : : unsigned int ok_to_print,
315 : : unsigned int init_media)
316 : : {
317 : : unsigned int old_carrier, new_carrier;
318 : : int advertise, lpa, media, duplex;
319 : : int lpa2 = 0;
320 : :
321 : : /* if forced media, go no further */
322 [ # # ]: 0 : if (mii->force_media)
323 : : return 0; /* duplex did not change */
324 : :
325 : : /* check current and old link status */
326 : 0 : old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
327 : 0 : new_carrier = (unsigned int) mii_link_ok(mii);
328 : :
329 : : /* if carrier state did not change, this is a "bounce",
330 : : * just exit as everything is already set correctly
331 : : */
332 [ # # ]: 0 : if ((!init_media) && (old_carrier == new_carrier))
333 : : return 0; /* duplex did not change */
334 : :
335 : : /* no carrier, nothing much to do */
336 [ # # ]: 0 : if (!new_carrier) {
337 : 0 : netif_carrier_off(mii->dev);
338 [ # # ]: 0 : if (ok_to_print)
339 : 0 : netdev_info(mii->dev, "link down\n");
340 : : return 0; /* duplex did not change */
341 : : }
342 : :
343 : : /*
344 : : * we have carrier, see who's on the other end
345 : : */
346 : 0 : netif_carrier_on(mii->dev);
347 : :
348 : : /* get MII advertise and LPA values */
349 [ # # ][ # # ]: 0 : if ((!init_media) && (mii->advertising))
350 : : advertise = mii->advertising;
351 : : else {
352 : 0 : advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
353 : 0 : mii->advertising = advertise;
354 : : }
355 : 0 : lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
356 [ # # ]: 0 : if (mii->supports_gmii)
357 : 0 : lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
358 : :
359 : : /* figure out media and duplex from advertise and LPA values */
360 : 0 : media = mii_nway_result(lpa & advertise);
361 : 0 : duplex = (media & ADVERTISE_FULL) ? 1 : 0;
362 [ # # ]: 0 : if (lpa2 & LPA_1000FULL)
363 : : duplex = 1;
364 : :
365 [ # # ]: 0 : if (ok_to_print)
366 [ # # ][ # # ]: 0 : netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
367 : 0 : lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
368 : 0 : media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
369 [ # # ]: 0 : 100 : 10,
370 : : duplex ? "full" : "half",
371 : : lpa);
372 : :
373 [ # # ][ # # ]: 0 : if ((init_media) || (mii->full_duplex != duplex)) {
374 : 0 : mii->full_duplex = duplex;
375 : 0 : return 1; /* duplex changed */
376 : : }
377 : :
378 : : return 0; /* duplex did not change */
379 : : }
380 : :
381 : : /**
382 : : * generic_mii_ioctl - main MII ioctl interface
383 : : * @mii_if: the MII interface
384 : : * @mii_data: MII ioctl data structure
385 : : * @cmd: MII ioctl command
386 : : * @duplex_chg_out: pointer to @duplex_changed status if there was no
387 : : * ioctl error
388 : : *
389 : : * Returns 0 on success, negative on error.
390 : : */
391 : 0 : int generic_mii_ioctl(struct mii_if_info *mii_if,
392 : : struct mii_ioctl_data *mii_data, int cmd,
393 : : unsigned int *duplex_chg_out)
394 : : {
395 : : int rc = 0;
396 : : unsigned int duplex_changed = 0;
397 : :
398 [ # # ]: 0 : if (duplex_chg_out)
399 : 0 : *duplex_chg_out = 0;
400 : :
401 : 0 : mii_data->phy_id &= mii_if->phy_id_mask;
402 : 0 : mii_data->reg_num &= mii_if->reg_num_mask;
403 : :
404 [ # # # # ]: 0 : switch(cmd) {
405 : : case SIOCGMIIPHY:
406 : 0 : mii_data->phy_id = mii_if->phy_id;
407 : : /* fall through */
408 : :
409 : : case SIOCGMIIREG:
410 : 0 : mii_data->val_out =
411 : 0 : mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
412 : : mii_data->reg_num);
413 : 0 : break;
414 : :
415 : : case SIOCSMIIREG: {
416 : 0 : u16 val = mii_data->val_in;
417 : :
418 [ # # ]: 0 : if (mii_data->phy_id == mii_if->phy_id) {
419 [ # # # ]: 0 : switch(mii_data->reg_num) {
420 : : case MII_BMCR: {
421 : : unsigned int new_duplex = 0;
422 [ # # ]: 0 : if (val & (BMCR_RESET|BMCR_ANENABLE))
423 : 0 : mii_if->force_media = 0;
424 : : else
425 : 0 : mii_if->force_media = 1;
426 [ # # ][ # # ]: 0 : if (mii_if->force_media &&
427 : 0 : (val & BMCR_FULLDPLX))
428 : : new_duplex = 1;
429 [ # # ]: 0 : if (mii_if->full_duplex != new_duplex) {
430 : : duplex_changed = 1;
431 : 0 : mii_if->full_duplex = new_duplex;
432 : : }
433 : : break;
434 : : }
435 : : case MII_ADVERTISE:
436 : 0 : mii_if->advertising = val;
437 : 0 : break;
438 : : default:
439 : : /* do nothing */
440 : : break;
441 : : }
442 : : }
443 : :
444 : 0 : mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
445 : 0 : mii_data->reg_num, val);
446 : 0 : break;
447 : : }
448 : :
449 : : default:
450 : : rc = -EOPNOTSUPP;
451 : : break;
452 : : }
453 : :
454 [ # # ][ # # ]: 0 : if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
455 : 0 : *duplex_chg_out = 1;
456 : :
457 : 0 : return rc;
458 : : }
459 : :
460 : : MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
461 : : MODULE_DESCRIPTION ("MII hardware support library");
462 : : MODULE_LICENSE("GPL");
463 : :
464 : : EXPORT_SYMBOL(mii_link_ok);
465 : : EXPORT_SYMBOL(mii_nway_restart);
466 : : EXPORT_SYMBOL(mii_ethtool_gset);
467 : : EXPORT_SYMBOL(mii_ethtool_sset);
468 : : EXPORT_SYMBOL(mii_check_link);
469 : : EXPORT_SYMBOL(mii_check_media);
470 : : EXPORT_SYMBOL(mii_check_gmii_support);
471 : : EXPORT_SYMBOL(generic_mii_ioctl);
472 : :
|