mmc: sdhci-pltfm: use devm_ioremap_resource()
[linux-2.6-block.git] / drivers / net / mii.c
CommitLineData
1da177e4
LT
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>
9c4df53b 34#include <linux/mii.h>
5974700c
BH
35
36static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
37{
5974700c
BH
38 int advert;
39
40 advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
28011cf1 41
37f07023 42 return mii_lpa_to_ethtool_lpa_t(advert);
5974700c 43}
1da177e4 44
32684ec6
RD
45/**
46 * mii_ethtool_gset - get settings that are specified in @ecmd
47 * @mii: MII interface
48 * @ecmd: requested ethtool_cmd
49 *
8ae6daca
DD
50 * The @ecmd parameter is expected to have been cleared before calling
51 * mii_ethtool_gset().
52 *
32684ec6
RD
53 * Returns 0 for success, negative on error.
54 */
1da177e4
LT
55int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
56{
57 struct net_device *dev = mii->dev;
5974700c
BH
58 u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
59 u32 nego;
1da177e4
LT
60
61 ecmd->supported =
62 (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
63 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
64 SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
65 if (mii->supports_gmii)
66 ecmd->supported |= SUPPORTED_1000baseT_Half |
67 SUPPORTED_1000baseT_Full;
68
69 /* only supports twisted-pair */
70 ecmd->port = PORT_MII;
71
72 /* only supports internal transceiver */
73 ecmd->transceiver = XCVR_INTERNAL;
74
75 /* this isn't fully supported at higher layers */
76 ecmd->phy_address = mii->phy_id;
9c4df53b 77 ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22;
1da177e4
LT
78
79 ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
1da177e4
LT
80
81 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
5974700c 82 bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
1da177e4 83 if (mii->supports_gmii) {
5974700c
BH
84 ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
85 stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
1da177e4
LT
86 }
87 if (bmcr & BMCR_ANENABLE) {
88 ecmd->advertising |= ADVERTISED_Autoneg;
89 ecmd->autoneg = AUTONEG_ENABLE;
6aa20a22 90
5974700c 91 ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE);
28011cf1 92 if (mii->supports_gmii)
37f07023
MC
93 ecmd->advertising |=
94 mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
5974700c
BH
95
96 if (bmsr & BMSR_ANEGCOMPLETE) {
97 ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
28011cf1 98 ecmd->lp_advertising |=
37f07023 99 mii_stat1000_to_ethtool_lpa_t(stat1000);
5974700c
BH
100 } else {
101 ecmd->lp_advertising = 0;
102 }
103
104 nego = ecmd->advertising & ecmd->lp_advertising;
105
106 if (nego & (ADVERTISED_1000baseT_Full |
107 ADVERTISED_1000baseT_Half)) {
70739497 108 ethtool_cmd_speed_set(ecmd, SPEED_1000);
5974700c
BH
109 ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
110 } else if (nego & (ADVERTISED_100baseT_Full |
111 ADVERTISED_100baseT_Half)) {
70739497 112 ethtool_cmd_speed_set(ecmd, SPEED_100);
5974700c 113 ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
1da177e4 114 } else {
70739497 115 ethtool_cmd_speed_set(ecmd, SPEED_10);
5974700c 116 ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
1da177e4
LT
117 }
118 } else {
119 ecmd->autoneg = AUTONEG_DISABLE;
120
70739497
DD
121 ethtool_cmd_speed_set(ecmd,
122 ((bmcr & BMCR_SPEED1000 &&
123 (bmcr & BMCR_SPEED100) == 0) ?
124 SPEED_1000 :
125 ((bmcr & BMCR_SPEED100) ?
126 SPEED_100 : SPEED_10)));
1da177e4
LT
127 ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
128 }
129
5974700c
BH
130 mii->full_duplex = ecmd->duplex;
131
1da177e4
LT
132 /* ignore maxtxpkt, maxrxpkt for now */
133
134 return 0;
135}
136
32684ec6
RD
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 */
1da177e4
LT
144int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
145{
146 struct net_device *dev = mii->dev;
25db0338 147 u32 speed = ethtool_cmd_speed(ecmd);
1da177e4 148
25db0338
DD
149 if (speed != SPEED_10 &&
150 speed != SPEED_100 &&
151 speed != SPEED_1000)
1da177e4
LT
152 return -EINVAL;
153 if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
154 return -EINVAL;
155 if (ecmd->port != PORT_MII)
156 return -EINVAL;
157 if (ecmd->transceiver != XCVR_INTERNAL)
158 return -EINVAL;
159 if (ecmd->phy_address != mii->phy_id)
160 return -EINVAL;
161 if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
162 return -EINVAL;
25db0338 163 if ((speed == SPEED_1000) && (!mii->supports_gmii))
1da177e4 164 return -EINVAL;
6aa20a22 165
1da177e4 166 /* ignore supported, maxtxpkt, maxrxpkt */
6aa20a22 167
1da177e4
LT
168 if (ecmd->autoneg == AUTONEG_ENABLE) {
169 u32 bmcr, advert, tmp;
170 u32 advert2 = 0, tmp2 = 0;
171
172 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 advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
182 tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
183 if (mii->supports_gmii) {
184 advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
185 tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
186 }
37f07023 187 tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
28011cf1
MC
188
189 if (mii->supports_gmii)
37f07023
MC
190 tmp2 |=
191 ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
1da177e4
LT
192 if (advert != tmp) {
193 mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
194 mii->advertising = tmp;
195 }
196 if ((mii->supports_gmii) && (advert2 != tmp2))
197 mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
6aa20a22 198
1da177e4
LT
199 /* turn on autonegotiation, and force a renegotiate */
200 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
201 bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
202 mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
203
204 mii->force_media = 0;
205 } else {
206 u32 bmcr, tmp;
207
208 /* turn off auto negotiation, set speed and duplexity */
209 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
6aa20a22 210 tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
1da177e4 211 BMCR_SPEED1000 | BMCR_FULLDPLX);
25db0338 212 if (speed == SPEED_1000)
1da177e4 213 tmp |= BMCR_SPEED1000;
25db0338 214 else if (speed == SPEED_100)
1da177e4
LT
215 tmp |= BMCR_SPEED100;
216 if (ecmd->duplex == DUPLEX_FULL) {
217 tmp |= BMCR_FULLDPLX;
218 mii->full_duplex = 1;
219 } else
220 mii->full_duplex = 0;
221 if (bmcr != tmp)
222 mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
223
224 mii->force_media = 1;
225 }
226 return 0;
227}
228
32684ec6
RD
229/**
230 * mii_check_gmii_support - check if the MII supports Gb interfaces
231 * @mii: the MII interface
232 */
43ec6e95
DF
233int mii_check_gmii_support(struct mii_if_info *mii)
234{
235 int reg;
236
237 reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
238 if (reg & BMSR_ESTATEN) {
239 reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
240 if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
241 return 1;
242 }
243
244 return 0;
245}
246
32684ec6
RD
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 */
1da177e4
LT
253int mii_link_ok (struct mii_if_info *mii)
254{
255 /* first, a dummy read, needed to latch some MII phys */
256 mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
257 if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
258 return 1;
259 return 0;
260}
261
32684ec6
RD
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 */
1da177e4
LT
268int 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 bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
275
276 if (bmcr & BMCR_ANENABLE) {
277 bmcr |= BMCR_ANRESTART;
278 mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
279 r = 0;
280 }
281
282 return r;
283}
284
32684ec6
RD
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 */
1da177e4
LT
293void mii_check_link (struct mii_if_info *mii)
294{
295 int cur_link = mii_link_ok(mii);
296 int prev_link = netif_carrier_ok(mii->dev);
297
298 if (cur_link && !prev_link)
299 netif_carrier_on(mii->dev);
300 else if (prev_link && !cur_link)
301 netif_carrier_off(mii->dev);
302}
303
32684ec6 304/**
5bdc7380 305 * mii_check_media - check the MII interface for a carrier/speed/duplex change
32684ec6
RD
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 */
1da177e4
LT
313unsigned 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
1da177e4
LT
321 /* check current and old link status */
322 old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
323 new_carrier = (unsigned int) mii_link_ok(mii);
324
325 /* if carrier state did not change, this is a "bounce",
326 * just exit as everything is already set correctly
327 */
328 if ((!init_media) && (old_carrier == new_carrier))
329 return 0; /* duplex did not change */
330
331 /* no carrier, nothing much to do */
332 if (!new_carrier) {
333 netif_carrier_off(mii->dev);
334 if (ok_to_print)
967faf3b 335 netdev_info(mii->dev, "link down\n");
1da177e4
LT
336 return 0; /* duplex did not change */
337 }
338
339 /*
340 * we have carrier, see who's on the other end
341 */
342 netif_carrier_on(mii->dev);
343
5bdc7380
BH
344 if (mii->force_media) {
345 if (ok_to_print)
346 netdev_info(mii->dev, "link up\n");
347 return 0; /* duplex did not change */
348 }
349
1da177e4
LT
350 /* get MII advertise and LPA values */
351 if ((!init_media) && (mii->advertising))
352 advertise = mii->advertising;
353 else {
354 advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
355 mii->advertising = advertise;
356 }
357 lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
358 if (mii->supports_gmii)
359 lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
360
361 /* figure out media and duplex from advertise and LPA values */
362 media = mii_nway_result(lpa & advertise);
363 duplex = (media & ADVERTISE_FULL) ? 1 : 0;
364 if (lpa2 & LPA_1000FULL)
365 duplex = 1;
366
367 if (ok_to_print)
967faf3b
JP
368 netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
369 lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
370 media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
371 100 : 10,
372 duplex ? "full" : "half",
373 lpa);
1da177e4
LT
374
375 if ((init_media) || (mii->full_duplex != duplex)) {
376 mii->full_duplex = duplex;
377 return 1; /* duplex changed */
378 }
379
380 return 0; /* duplex did not change */
381}
382
32684ec6
RD
383/**
384 * generic_mii_ioctl - main MII ioctl interface
385 * @mii_if: the MII interface
386 * @mii_data: MII ioctl data structure
387 * @cmd: MII ioctl command
388 * @duplex_chg_out: pointer to @duplex_changed status if there was no
389 * ioctl error
390 *
391 * Returns 0 on success, negative on error.
392 */
1da177e4
LT
393int generic_mii_ioctl(struct mii_if_info *mii_if,
394 struct mii_ioctl_data *mii_data, int cmd,
395 unsigned int *duplex_chg_out)
396{
397 int rc = 0;
398 unsigned int duplex_changed = 0;
399
400 if (duplex_chg_out)
401 *duplex_chg_out = 0;
402
403 mii_data->phy_id &= mii_if->phy_id_mask;
404 mii_data->reg_num &= mii_if->reg_num_mask;
405
406 switch(cmd) {
407 case SIOCGMIIPHY:
408 mii_data->phy_id = mii_if->phy_id;
409 /* fall through */
410
411 case SIOCGMIIREG:
412 mii_data->val_out =
413 mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
414 mii_data->reg_num);
415 break;
416
417 case SIOCSMIIREG: {
418 u16 val = mii_data->val_in;
419
1da177e4
LT
420 if (mii_data->phy_id == mii_if->phy_id) {
421 switch(mii_data->reg_num) {
422 case MII_BMCR: {
423 unsigned int new_duplex = 0;
424 if (val & (BMCR_RESET|BMCR_ANENABLE))
425 mii_if->force_media = 0;
426 else
427 mii_if->force_media = 1;
428 if (mii_if->force_media &&
429 (val & BMCR_FULLDPLX))
430 new_duplex = 1;
431 if (mii_if->full_duplex != new_duplex) {
432 duplex_changed = 1;
433 mii_if->full_duplex = new_duplex;
434 }
435 break;
436 }
437 case MII_ADVERTISE:
438 mii_if->advertising = val;
439 break;
440 default:
441 /* do nothing */
442 break;
443 }
444 }
445
446 mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
447 mii_data->reg_num, val);
448 break;
449 }
450
451 default:
452 rc = -EOPNOTSUPP;
453 break;
454 }
455
456 if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
457 *duplex_chg_out = 1;
458
459 return rc;
460}
461
462MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
463MODULE_DESCRIPTION ("MII hardware support library");
464MODULE_LICENSE("GPL");
465
466EXPORT_SYMBOL(mii_link_ok);
467EXPORT_SYMBOL(mii_nway_restart);
468EXPORT_SYMBOL(mii_ethtool_gset);
469EXPORT_SYMBOL(mii_ethtool_sset);
470EXPORT_SYMBOL(mii_check_link);
471EXPORT_SYMBOL(mii_check_media);
43ec6e95 472EXPORT_SYMBOL(mii_check_gmii_support);
1da177e4
LT
473EXPORT_SYMBOL(generic_mii_ioctl);
474