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 : 0 : 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 : 0 : u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
59 : 0 : 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 : 0 : 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 : 0 : ethtool_cmd_speed_set(ecmd, SPEED_100);
113 : 0 : ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
114 : : } else {
115 : 0 : 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 : 0 : 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_get_link_ksettings - get settings that are specified in @cmd
139 : : * @mii: MII interface
140 : : * @cmd: requested ethtool_link_ksettings
141 : : *
142 : : * The @cmd parameter is expected to have been cleared before calling
143 : : * mii_ethtool_get_link_ksettings().
144 : : */
145 : 0 : void mii_ethtool_get_link_ksettings(struct mii_if_info *mii,
146 : : struct ethtool_link_ksettings *cmd)
147 : : {
148 : 0 : struct net_device *dev = mii->dev;
149 : 0 : u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
150 : 0 : u32 nego, supported, advertising, lp_advertising;
151 : :
152 : 0 : supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
153 : : SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
154 : : SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
155 [ # # ]: 0 : if (mii->supports_gmii)
156 : 0 : supported |= SUPPORTED_1000baseT_Half |
157 : : SUPPORTED_1000baseT_Full;
158 : :
159 : : /* only supports twisted-pair */
160 : 0 : cmd->base.port = PORT_MII;
161 : :
162 : : /* this isn't fully supported at higher layers */
163 : 0 : cmd->base.phy_address = mii->phy_id;
164 : 0 : cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22;
165 : :
166 : 0 : advertising = ADVERTISED_TP | ADVERTISED_MII;
167 : :
168 : 0 : bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
169 : 0 : bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
170 [ # # ]: 0 : if (mii->supports_gmii) {
171 : 0 : ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
172 : 0 : stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
173 : : }
174 [ # # ]: 0 : if (bmcr & BMCR_ANENABLE) {
175 : 0 : advertising |= ADVERTISED_Autoneg;
176 : 0 : cmd->base.autoneg = AUTONEG_ENABLE;
177 : :
178 : 0 : advertising |= mii_get_an(mii, MII_ADVERTISE);
179 [ # # ]: 0 : if (mii->supports_gmii)
180 [ # # ]: 0 : advertising |= mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
181 : :
182 [ # # ]: 0 : if (bmsr & BMSR_ANEGCOMPLETE) {
183 : 0 : lp_advertising = mii_get_an(mii, MII_LPA);
184 [ # # ]: 0 : lp_advertising |=
185 : : mii_stat1000_to_ethtool_lpa_t(stat1000);
186 : : } else {
187 : : lp_advertising = 0;
188 : : }
189 : :
190 : 0 : nego = advertising & lp_advertising;
191 : :
192 [ # # ]: 0 : if (nego & (ADVERTISED_1000baseT_Full |
193 : : ADVERTISED_1000baseT_Half)) {
194 : 0 : cmd->base.speed = SPEED_1000;
195 : 0 : cmd->base.duplex = !!(nego & ADVERTISED_1000baseT_Full);
196 [ # # ]: 0 : } else if (nego & (ADVERTISED_100baseT_Full |
197 : : ADVERTISED_100baseT_Half)) {
198 : 0 : cmd->base.speed = SPEED_100;
199 : 0 : cmd->base.duplex = !!(nego & ADVERTISED_100baseT_Full);
200 : : } else {
201 : 0 : cmd->base.speed = SPEED_10;
202 : 0 : cmd->base.duplex = !!(nego & ADVERTISED_10baseT_Full);
203 : : }
204 : : } else {
205 : 0 : cmd->base.autoneg = AUTONEG_DISABLE;
206 : :
207 : 0 : cmd->base.speed = ((bmcr & BMCR_SPEED1000 &&
208 : : (bmcr & BMCR_SPEED100) == 0) ?
209 [ # # ]: 0 : SPEED_1000 :
210 [ # # ]: 0 : ((bmcr & BMCR_SPEED100) ?
211 : : SPEED_100 : SPEED_10));
212 : 0 : cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ?
213 : 0 : DUPLEX_FULL : DUPLEX_HALF;
214 : :
215 : 0 : lp_advertising = 0;
216 : : }
217 : :
218 : 0 : mii->full_duplex = cmd->base.duplex;
219 : :
220 : 0 : ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
221 : : supported);
222 : 0 : ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
223 : : advertising);
224 : 0 : ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
225 : : lp_advertising);
226 : :
227 : : /* ignore maxtxpkt, maxrxpkt for now */
228 : 0 : }
229 : :
230 : : /**
231 : : * mii_ethtool_sset - set settings that are specified in @ecmd
232 : : * @mii: MII interface
233 : : * @ecmd: requested ethtool_cmd
234 : : *
235 : : * Returns 0 for success, negative on error.
236 : : */
237 : 0 : int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
238 : : {
239 : 0 : struct net_device *dev = mii->dev;
240 [ # # ]: 0 : u32 speed = ethtool_cmd_speed(ecmd);
241 : :
242 : 0 : if (speed != SPEED_10 &&
243 [ # # # # ]: 0 : speed != SPEED_100 &&
244 : : speed != SPEED_1000)
245 : : return -EINVAL;
246 [ # # ]: 0 : if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
247 : : return -EINVAL;
248 [ # # ]: 0 : if (ecmd->port != PORT_MII)
249 : : return -EINVAL;
250 [ # # ]: 0 : if (ecmd->transceiver != XCVR_INTERNAL)
251 : : return -EINVAL;
252 [ # # ]: 0 : if (ecmd->phy_address != mii->phy_id)
253 : : return -EINVAL;
254 [ # # ]: 0 : if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
255 : : return -EINVAL;
256 [ # # # # ]: 0 : if ((speed == SPEED_1000) && (!mii->supports_gmii))
257 : : return -EINVAL;
258 : :
259 : : /* ignore supported, maxtxpkt, maxrxpkt */
260 : :
261 [ # # ]: 0 : if (ecmd->autoneg == AUTONEG_ENABLE) {
262 : 0 : u32 bmcr, advert, tmp;
263 : 0 : u32 advert2 = 0, tmp2 = 0;
264 : :
265 [ # # ]: 0 : if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
266 : : ADVERTISED_10baseT_Full |
267 : : ADVERTISED_100baseT_Half |
268 : : ADVERTISED_100baseT_Full |
269 : : ADVERTISED_1000baseT_Half |
270 : : ADVERTISED_1000baseT_Full)) == 0)
271 : : return -EINVAL;
272 : :
273 : : /* advertise only what has been requested */
274 : 0 : advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
275 : 0 : tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
276 [ # # ]: 0 : if (mii->supports_gmii) {
277 : 0 : advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
278 : 0 : tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
279 : : }
280 : 0 : tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
281 : :
282 [ # # ]: 0 : if (mii->supports_gmii)
283 [ # # ]: 0 : tmp2 |=
284 : : ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
285 [ # # ]: 0 : if (advert != tmp) {
286 : 0 : mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
287 : 0 : mii->advertising = tmp;
288 : : }
289 [ # # # # ]: 0 : if ((mii->supports_gmii) && (advert2 != tmp2))
290 : 0 : mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
291 : :
292 : : /* turn on autonegotiation, and force a renegotiate */
293 : 0 : bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
294 : 0 : bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
295 : 0 : mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
296 : :
297 : 0 : mii->force_media = 0;
298 : : } else {
299 : 0 : u32 bmcr, tmp;
300 : :
301 : : /* turn off auto negotiation, set speed and duplexity */
302 : 0 : bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
303 : 0 : tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
304 : : BMCR_SPEED1000 | BMCR_FULLDPLX);
305 [ # # ]: 0 : if (speed == SPEED_1000)
306 : 0 : tmp |= BMCR_SPEED1000;
307 [ # # ]: 0 : else if (speed == SPEED_100)
308 : 0 : tmp |= BMCR_SPEED100;
309 [ # # ]: 0 : if (ecmd->duplex == DUPLEX_FULL) {
310 : 0 : tmp |= BMCR_FULLDPLX;
311 : 0 : mii->full_duplex = 1;
312 : : } else
313 : 0 : mii->full_duplex = 0;
314 [ # # ]: 0 : if (bmcr != tmp)
315 : 0 : mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
316 : :
317 : 0 : mii->force_media = 1;
318 : : }
319 : : return 0;
320 : : }
321 : :
322 : : /**
323 : : * mii_ethtool_set_link_ksettings - set settings that are specified in @cmd
324 : : * @mii: MII interfaces
325 : : * @cmd: requested ethtool_link_ksettings
326 : : *
327 : : * Returns 0 for success, negative on error.
328 : : */
329 : 0 : int mii_ethtool_set_link_ksettings(struct mii_if_info *mii,
330 : : const struct ethtool_link_ksettings *cmd)
331 : : {
332 : 0 : struct net_device *dev = mii->dev;
333 : 0 : u32 speed = cmd->base.speed;
334 : :
335 : 0 : if (speed != SPEED_10 &&
336 [ # # # # ]: 0 : speed != SPEED_100 &&
337 : : speed != SPEED_1000)
338 : : return -EINVAL;
339 [ # # ]: 0 : if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL)
340 : : return -EINVAL;
341 [ # # ]: 0 : if (cmd->base.port != PORT_MII)
342 : : return -EINVAL;
343 [ # # ]: 0 : if (cmd->base.phy_address != mii->phy_id)
344 : : return -EINVAL;
345 [ # # ]: 0 : if (cmd->base.autoneg != AUTONEG_DISABLE &&
346 : : cmd->base.autoneg != AUTONEG_ENABLE)
347 : : return -EINVAL;
348 [ # # # # ]: 0 : if ((speed == SPEED_1000) && (!mii->supports_gmii))
349 : : return -EINVAL;
350 : :
351 : : /* ignore supported, maxtxpkt, maxrxpkt */
352 : :
353 [ # # ]: 0 : if (cmd->base.autoneg == AUTONEG_ENABLE) {
354 : 0 : u32 bmcr, advert, tmp;
355 : 0 : u32 advert2 = 0, tmp2 = 0;
356 : 0 : u32 advertising;
357 : :
358 : 0 : ethtool_convert_link_mode_to_legacy_u32(
359 : 0 : &advertising, cmd->link_modes.advertising);
360 : :
361 [ # # ]: 0 : if ((advertising & (ADVERTISED_10baseT_Half |
362 : : ADVERTISED_10baseT_Full |
363 : : ADVERTISED_100baseT_Half |
364 : : ADVERTISED_100baseT_Full |
365 : : ADVERTISED_1000baseT_Half |
366 : : ADVERTISED_1000baseT_Full)) == 0)
367 : 0 : return -EINVAL;
368 : :
369 : : /* advertise only what has been requested */
370 : 0 : advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
371 : 0 : tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
372 [ # # ]: 0 : if (mii->supports_gmii) {
373 : 0 : advert2 = mii->mdio_read(dev, mii->phy_id,
374 : : MII_CTRL1000);
375 : 0 : tmp2 = advert2 &
376 : : ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
377 : : }
378 : 0 : tmp |= ethtool_adv_to_mii_adv_t(advertising);
379 : :
380 [ # # ]: 0 : if (mii->supports_gmii)
381 [ # # ]: 0 : tmp2 |= ethtool_adv_to_mii_ctrl1000_t(advertising);
382 [ # # ]: 0 : if (advert != tmp) {
383 : 0 : mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
384 : 0 : mii->advertising = tmp;
385 : : }
386 [ # # # # ]: 0 : if ((mii->supports_gmii) && (advert2 != tmp2))
387 : 0 : mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
388 : :
389 : : /* turn on autonegotiation, and force a renegotiate */
390 : 0 : bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
391 : 0 : bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
392 : 0 : mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
393 : :
394 : 0 : mii->force_media = 0;
395 : : } else {
396 : 0 : u32 bmcr, tmp;
397 : :
398 : : /* turn off auto negotiation, set speed and duplexity */
399 : 0 : bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
400 : 0 : tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
401 : : BMCR_SPEED1000 | BMCR_FULLDPLX);
402 [ # # ]: 0 : if (speed == SPEED_1000)
403 : 0 : tmp |= BMCR_SPEED1000;
404 [ # # ]: 0 : else if (speed == SPEED_100)
405 : 0 : tmp |= BMCR_SPEED100;
406 [ # # ]: 0 : if (cmd->base.duplex == DUPLEX_FULL) {
407 : 0 : tmp |= BMCR_FULLDPLX;
408 : 0 : mii->full_duplex = 1;
409 : : } else {
410 : 0 : mii->full_duplex = 0;
411 : : }
412 [ # # ]: 0 : if (bmcr != tmp)
413 : 0 : mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
414 : :
415 : 0 : mii->force_media = 1;
416 : : }
417 : : return 0;
418 : : }
419 : :
420 : : /**
421 : : * mii_check_gmii_support - check if the MII supports Gb interfaces
422 : : * @mii: the MII interface
423 : : */
424 : 0 : int mii_check_gmii_support(struct mii_if_info *mii)
425 : : {
426 : 0 : int reg;
427 : :
428 : 0 : reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
429 [ # # ]: 0 : if (reg & BMSR_ESTATEN) {
430 : 0 : reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
431 [ # # ]: 0 : if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
432 : 0 : return 1;
433 : : }
434 : :
435 : : return 0;
436 : : }
437 : :
438 : : /**
439 : : * mii_link_ok - is link status up/ok
440 : : * @mii: the MII interface
441 : : *
442 : : * Returns 1 if the MII reports link status up/ok, 0 otherwise.
443 : : */
444 : 3 : int mii_link_ok (struct mii_if_info *mii)
445 : : {
446 : : /* first, a dummy read, needed to latch some MII phys */
447 : 3 : mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
448 [ + - ]: 3 : if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
449 : 3 : return 1;
450 : : return 0;
451 : : }
452 : :
453 : : /**
454 : : * mii_nway_restart - restart NWay (autonegotiation) for this interface
455 : : * @mii: the MII interface
456 : : *
457 : : * Returns 0 on success, negative on error.
458 : : */
459 : 0 : int mii_nway_restart (struct mii_if_info *mii)
460 : : {
461 : 0 : int bmcr;
462 : 0 : int r = -EINVAL;
463 : :
464 : : /* if autoneg is off, it's an error */
465 : 0 : bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
466 : :
467 [ # # ]: 0 : if (bmcr & BMCR_ANENABLE) {
468 : 0 : bmcr |= BMCR_ANRESTART;
469 : 0 : mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
470 : 0 : r = 0;
471 : : }
472 : :
473 : 0 : return r;
474 : : }
475 : :
476 : : /**
477 : : * mii_check_link - check MII link status
478 : : * @mii: MII interface
479 : : *
480 : : * If the link status changed (previous != current), call
481 : : * netif_carrier_on() if current link status is Up or call
482 : : * netif_carrier_off() if current link status is Down.
483 : : */
484 : 0 : void mii_check_link (struct mii_if_info *mii)
485 : : {
486 : 0 : int cur_link = mii_link_ok(mii);
487 : 0 : int prev_link = netif_carrier_ok(mii->dev);
488 : :
489 [ # # ]: 0 : if (cur_link && !prev_link)
490 : 0 : netif_carrier_on(mii->dev);
491 [ # # ]: 0 : else if (prev_link && !cur_link)
492 : 0 : netif_carrier_off(mii->dev);
493 : 0 : }
494 : :
495 : : /**
496 : : * mii_check_media - check the MII interface for a carrier/speed/duplex change
497 : : * @mii: the MII interface
498 : : * @ok_to_print: OK to print link up/down messages
499 : : * @init_media: OK to save duplex mode in @mii
500 : : *
501 : : * Returns 1 if the duplex mode changed, 0 if not.
502 : : * If the media type is forced, always returns 0.
503 : : */
504 : 3 : unsigned int mii_check_media (struct mii_if_info *mii,
505 : : unsigned int ok_to_print,
506 : : unsigned int init_media)
507 : : {
508 : 3 : unsigned int old_carrier, new_carrier;
509 : 3 : int advertise, lpa, media, duplex;
510 : 3 : int lpa2 = 0;
511 : :
512 : : /* check current and old link status */
513 : 3 : old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
514 : 3 : new_carrier = (unsigned int) mii_link_ok(mii);
515 : :
516 : : /* if carrier state did not change, this is a "bounce",
517 : : * just exit as everything is already set correctly
518 : : */
519 [ + - ]: 3 : if ((!init_media) && (old_carrier == new_carrier))
520 : : return 0; /* duplex did not change */
521 : :
522 : : /* no carrier, nothing much to do */
523 [ - + ]: 3 : if (!new_carrier) {
524 : 0 : netif_carrier_off(mii->dev);
525 [ # # ]: 0 : if (ok_to_print)
526 : 0 : netdev_info(mii->dev, "link down\n");
527 : 0 : return 0; /* duplex did not change */
528 : : }
529 : :
530 : : /*
531 : : * we have carrier, see who's on the other end
532 : : */
533 : 3 : netif_carrier_on(mii->dev);
534 : :
535 [ - + ]: 3 : if (mii->force_media) {
536 [ # # ]: 0 : if (ok_to_print)
537 : 0 : netdev_info(mii->dev, "link up\n");
538 : 0 : return 0; /* duplex did not change */
539 : : }
540 : :
541 : : /* get MII advertise and LPA values */
542 [ - + - - ]: 3 : if ((!init_media) && (mii->advertising))
543 : : advertise = mii->advertising;
544 : : else {
545 : 3 : advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
546 : 3 : mii->advertising = advertise;
547 : : }
548 : 3 : lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
549 [ - + ]: 3 : if (mii->supports_gmii)
550 : 0 : lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
551 : :
552 : : /* figure out media and duplex from advertise and LPA values */
553 [ + - ]: 3 : media = mii_nway_result(lpa & advertise);
554 : 3 : duplex = (media & ADVERTISE_FULL) ? 1 : 0;
555 [ - + ]: 3 : if (lpa2 & LPA_1000FULL)
556 : 0 : duplex = 1;
557 : :
558 [ + - ]: 3 : if (ok_to_print)
559 [ + - ]: 6 : netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
560 [ + - ]: 3 : lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
561 : 3 : media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
562 [ + - ]: 3 : 100 : 10,
563 : : duplex ? "full" : "half",
564 : : lpa);
565 : :
566 [ - + - - ]: 3 : if ((init_media) || (mii->full_duplex != duplex)) {
567 : 3 : mii->full_duplex = duplex;
568 : 3 : return 1; /* duplex changed */
569 : : }
570 : :
571 : : return 0; /* duplex did not change */
572 : : }
573 : :
574 : : /**
575 : : * generic_mii_ioctl - main MII ioctl interface
576 : : * @mii_if: the MII interface
577 : : * @mii_data: MII ioctl data structure
578 : : * @cmd: MII ioctl command
579 : : * @duplex_chg_out: pointer to @duplex_changed status if there was no
580 : : * ioctl error
581 : : *
582 : : * Returns 0 on success, negative on error.
583 : : */
584 : 0 : int generic_mii_ioctl(struct mii_if_info *mii_if,
585 : : struct mii_ioctl_data *mii_data, int cmd,
586 : : unsigned int *duplex_chg_out)
587 : : {
588 : 0 : int rc = 0;
589 : 0 : unsigned int duplex_changed = 0;
590 : :
591 [ # # ]: 0 : if (duplex_chg_out)
592 : 0 : *duplex_chg_out = 0;
593 : :
594 : 0 : mii_data->phy_id &= mii_if->phy_id_mask;
595 : 0 : mii_data->reg_num &= mii_if->reg_num_mask;
596 : :
597 [ # # # # ]: 0 : switch(cmd) {
598 : 0 : case SIOCGMIIPHY:
599 : 0 : mii_data->phy_id = mii_if->phy_id;
600 : : /* fall through */
601 : :
602 : 0 : case SIOCGMIIREG:
603 : 0 : mii_data->val_out =
604 : 0 : mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
605 : : mii_data->reg_num);
606 : 0 : break;
607 : :
608 : 0 : case SIOCSMIIREG: {
609 : 0 : u16 val = mii_data->val_in;
610 : :
611 [ # # ]: 0 : if (mii_data->phy_id == mii_if->phy_id) {
612 [ # # # ]: 0 : switch(mii_data->reg_num) {
613 : 0 : case MII_BMCR: {
614 : 0 : unsigned int new_duplex = 0;
615 [ # # ]: 0 : if (val & (BMCR_RESET|BMCR_ANENABLE))
616 : 0 : mii_if->force_media = 0;
617 : : else
618 : 0 : mii_if->force_media = 1;
619 [ # # # # ]: 0 : if (mii_if->force_media &&
620 : : (val & BMCR_FULLDPLX))
621 : 0 : new_duplex = 1;
622 [ # # ]: 0 : if (mii_if->full_duplex != new_duplex) {
623 : 0 : duplex_changed = 1;
624 : 0 : mii_if->full_duplex = new_duplex;
625 : : }
626 : : break;
627 : : }
628 : 0 : case MII_ADVERTISE:
629 : 0 : mii_if->advertising = val;
630 : 0 : break;
631 : : default:
632 : : /* do nothing */
633 : : break;
634 : : }
635 : 0 : }
636 : :
637 : 0 : mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
638 : 0 : mii_data->reg_num, val);
639 : 0 : break;
640 : : }
641 : :
642 : : default:
643 : : rc = -EOPNOTSUPP;
644 : : break;
645 : : }
646 : :
647 [ # # # # ]: 0 : if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
648 : 0 : *duplex_chg_out = 1;
649 : :
650 : 0 : return rc;
651 : : }
652 : :
653 : : MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
654 : : MODULE_DESCRIPTION ("MII hardware support library");
655 : : MODULE_LICENSE("GPL");
656 : :
657 : : EXPORT_SYMBOL(mii_link_ok);
658 : : EXPORT_SYMBOL(mii_nway_restart);
659 : : EXPORT_SYMBOL(mii_ethtool_gset);
660 : : EXPORT_SYMBOL(mii_ethtool_get_link_ksettings);
661 : : EXPORT_SYMBOL(mii_ethtool_sset);
662 : : EXPORT_SYMBOL(mii_ethtool_set_link_ksettings);
663 : : EXPORT_SYMBOL(mii_check_link);
664 : : EXPORT_SYMBOL(mii_check_media);
665 : : EXPORT_SYMBOL(mii_check_gmii_support);
666 : : EXPORT_SYMBOL(generic_mii_ioctl);
667 : :
|