Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[platform/kernel/linux-rpi.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 carrier/speed/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         /* 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)
335                         netdev_info(mii->dev, "link down\n");
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
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
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)
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);
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
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  */
393 int 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
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
462 MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
463 MODULE_DESCRIPTION ("MII hardware support library");
464 MODULE_LICENSE("GPL");
465
466 EXPORT_SYMBOL(mii_link_ok);
467 EXPORT_SYMBOL(mii_nway_restart);
468 EXPORT_SYMBOL(mii_ethtool_gset);
469 EXPORT_SYMBOL(mii_ethtool_sset);
470 EXPORT_SYMBOL(mii_check_link);
471 EXPORT_SYMBOL(mii_check_media);
472 EXPORT_SYMBOL(mii_check_gmii_support);
473 EXPORT_SYMBOL(generic_mii_ioctl);
474