Merge branch 'siocghwtstamp' of git://git.kernel.org/pub/scm/linux/kernel/git/bwh...
[linux-drm-fsl-dcu.git] / drivers / net / mii.c
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 static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
37 {
38         int advert;
39
40         advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
41
42         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 int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
56 {
57         struct net_device *dev = mii->dev;
58         u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
59         u32 nego;
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;
77         ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22;
78
79         ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
80
81         bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
82         bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
83         if (mii->supports_gmii) {
84                 ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
85                 stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
86         }
87         if (bmcr & BMCR_ANENABLE) {
88                 ecmd->advertising |= ADVERTISED_Autoneg;
89                 ecmd->autoneg = AUTONEG_ENABLE;
90
91                 ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE);
92                 if (mii->supports_gmii)
93                         ecmd->advertising |=
94                                         mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
95
96                 if (bmsr & BMSR_ANEGCOMPLETE) {
97                         ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
98                         ecmd->lp_advertising |=
99                                         mii_stat1000_to_ethtool_lpa_t(stat1000);
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)) {
108                         ethtool_cmd_speed_set(ecmd, SPEED_1000);
109                         ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
110                 } else if (nego & (ADVERTISED_100baseT_Full |
111                                    ADVERTISED_100baseT_Half)) {
112                         ethtool_cmd_speed_set(ecmd, SPEED_100);
113                         ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
114                 } else {
115                         ethtool_cmd_speed_set(ecmd, SPEED_10);
116                         ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
117                 }
118         } else {
119                 ecmd->autoneg = AUTONEG_DISABLE;
120
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)));
127                 ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
128         }
129
130         mii->full_duplex = ecmd->duplex;
131
132         /* ignore maxtxpkt, maxrxpkt for now */
133
134         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 int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
145 {
146         struct net_device *dev = mii->dev;
147         u32 speed = ethtool_cmd_speed(ecmd);
148
149         if (speed != SPEED_10 &&
150             speed != SPEED_100 &&
151             speed != SPEED_1000)
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;
163         if ((speed == SPEED_1000) && (!mii->supports_gmii))
164                 return -EINVAL;
165
166         /* ignore supported, maxtxpkt, maxrxpkt */
167
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                 }
187                 tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
188
189                 if (mii->supports_gmii)
190                         tmp2 |=
191                               ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
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);
198
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);
210                 tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
211                                BMCR_SPEED1000 | BMCR_FULLDPLX);
212                 if (speed == SPEED_1000)
213                         tmp |= BMCR_SPEED1000;
214                 else if (speed == SPEED_100)
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
229 /**
230  * mii_check_gmii_support - check if the MII supports Gb interfaces
231  * @mii: the MII interface
232  */
233 int 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
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 int 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
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 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         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
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 void 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
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 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         if (mii->force_media)
323                 return 0; /* duplex did not change */
324
325         /* check current and old link status */
326         old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
327         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         if ((!init_media) && (old_carrier == new_carrier))
333                 return 0; /* duplex did not change */
334
335         /* no carrier, nothing much to do */
336         if (!new_carrier) {
337                 netif_carrier_off(mii->dev);
338                 if (ok_to_print)
339                         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         netif_carrier_on(mii->dev);
347
348         /* get MII advertise and LPA values */
349         if ((!init_media) && (mii->advertising))
350                 advertise = mii->advertising;
351         else {
352                 advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
353                 mii->advertising = advertise;
354         }
355         lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
356         if (mii->supports_gmii)
357                 lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
358
359         /* figure out media and duplex from advertise and LPA values */
360         media = mii_nway_result(lpa & advertise);
361         duplex = (media & ADVERTISE_FULL) ? 1 : 0;
362         if (lpa2 & LPA_1000FULL)
363                 duplex = 1;
364
365         if (ok_to_print)
366                 netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
367                             lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
368                             media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
369                             100 : 10,
370                             duplex ? "full" : "half",
371                             lpa);
372
373         if ((init_media) || (mii->full_duplex != duplex)) {
374                 mii->full_duplex = duplex;
375                 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 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         if (duplex_chg_out)
399                 *duplex_chg_out = 0;
400
401         mii_data->phy_id &= mii_if->phy_id_mask;
402         mii_data->reg_num &= mii_if->reg_num_mask;
403
404         switch(cmd) {
405         case SIOCGMIIPHY:
406                 mii_data->phy_id = mii_if->phy_id;
407                 /* fall through */
408
409         case SIOCGMIIREG:
410                 mii_data->val_out =
411                         mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
412                                           mii_data->reg_num);
413                 break;
414
415         case SIOCSMIIREG: {
416                 u16 val = mii_data->val_in;
417
418                 if (mii_data->phy_id == mii_if->phy_id) {
419                         switch(mii_data->reg_num) {
420                         case MII_BMCR: {
421                                 unsigned int new_duplex = 0;
422                                 if (val & (BMCR_RESET|BMCR_ANENABLE))
423                                         mii_if->force_media = 0;
424                                 else
425                                         mii_if->force_media = 1;
426                                 if (mii_if->force_media &&
427                                     (val & BMCR_FULLDPLX))
428                                         new_duplex = 1;
429                                 if (mii_if->full_duplex != new_duplex) {
430                                         duplex_changed = 1;
431                                         mii_if->full_duplex = new_duplex;
432                                 }
433                                 break;
434                         }
435                         case MII_ADVERTISE:
436                                 mii_if->advertising = val;
437                                 break;
438                         default:
439                                 /* do nothing */
440                                 break;
441                         }
442                 }
443
444                 mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
445                                    mii_data->reg_num, val);
446                 break;
447         }
448
449         default:
450                 rc = -EOPNOTSUPP;
451                 break;
452         }
453
454         if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
455                 *duplex_chg_out = 1;
456
457         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