3 mii.c: MII interface library
5 Maintained by Jeff Garzik <jgarzik@pobox.com>
6 Copyright 2001,2002 Jeff Garzik
8 Various code came from myson803.c and other files by
9 Donald Becker. Copyright:
11 Written 1998-2002 by Donald Becker.
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
22 The author may be reached as becker@scyld.com, or C/O
23 Scyld Computing Corporation
24 410 Severn Ave., Suite 210
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>
36 int mii_ethtool_gset(struct mii_if_info
*mii
, struct ethtool_cmd
*ecmd
)
38 struct net_device
*dev
= mii
->dev
;
39 u32 advert
, bmcr
, lpa
, nego
;
42 (SUPPORTED_10baseT_Half
| SUPPORTED_10baseT_Full
|
43 SUPPORTED_100baseT_Half
| SUPPORTED_100baseT_Full
|
44 SUPPORTED_Autoneg
| SUPPORTED_TP
| SUPPORTED_MII
);
46 /* only supports twisted-pair */
47 ecmd
->port
= PORT_MII
;
49 /* only supports internal transceiver */
50 ecmd
->transceiver
= XCVR_INTERNAL
;
52 /* this isn't fully supported at higher layers */
53 ecmd
->phy_address
= mii
->phy_id
;
55 ecmd
->advertising
= ADVERTISED_TP
| ADVERTISED_MII
;
56 advert
= mii
->mdio_read(dev
, mii
->phy_id
, MII_ADVERTISE
);
57 if (advert
& ADVERTISE_10HALF
)
58 ecmd
->advertising
|= ADVERTISED_10baseT_Half
;
59 if (advert
& ADVERTISE_10FULL
)
60 ecmd
->advertising
|= ADVERTISED_10baseT_Full
;
61 if (advert
& ADVERTISE_100HALF
)
62 ecmd
->advertising
|= ADVERTISED_100baseT_Half
;
63 if (advert
& ADVERTISE_100FULL
)
64 ecmd
->advertising
|= ADVERTISED_100baseT_Full
;
66 bmcr
= mii
->mdio_read(dev
, mii
->phy_id
, MII_BMCR
);
67 lpa
= mii
->mdio_read(dev
, mii
->phy_id
, MII_LPA
);
68 if (bmcr
& BMCR_ANENABLE
) {
69 ecmd
->advertising
|= ADVERTISED_Autoneg
;
70 ecmd
->autoneg
= AUTONEG_ENABLE
;
72 nego
= mii_nway_result(advert
& lpa
);
73 if (nego
== LPA_100FULL
|| nego
== LPA_100HALF
)
74 ecmd
->speed
= SPEED_100
;
76 ecmd
->speed
= SPEED_10
;
77 if (nego
== LPA_100FULL
|| nego
== LPA_10FULL
) {
78 ecmd
->duplex
= DUPLEX_FULL
;
81 ecmd
->duplex
= DUPLEX_HALF
;
85 ecmd
->autoneg
= AUTONEG_DISABLE
;
87 ecmd
->speed
= (bmcr
& BMCR_SPEED100
) ? SPEED_100
: SPEED_10
;
88 ecmd
->duplex
= (bmcr
& BMCR_FULLDPLX
) ? DUPLEX_FULL
: DUPLEX_HALF
;
91 /* ignore maxtxpkt, maxrxpkt for now */
96 int mii_ethtool_sset(struct mii_if_info
*mii
, struct ethtool_cmd
*ecmd
)
98 struct net_device
*dev
= mii
->dev
;
100 if (ecmd
->speed
!= SPEED_10
&& ecmd
->speed
!= SPEED_100
)
102 if (ecmd
->duplex
!= DUPLEX_HALF
&& ecmd
->duplex
!= DUPLEX_FULL
)
104 if (ecmd
->port
!= PORT_MII
)
106 if (ecmd
->transceiver
!= XCVR_INTERNAL
)
108 if (ecmd
->phy_address
!= mii
->phy_id
)
110 if (ecmd
->autoneg
!= AUTONEG_DISABLE
&& ecmd
->autoneg
!= AUTONEG_ENABLE
)
113 /* ignore supported, maxtxpkt, maxrxpkt */
115 if (ecmd
->autoneg
== AUTONEG_ENABLE
) {
116 u32 bmcr
, advert
, tmp
;
118 if ((ecmd
->advertising
& (ADVERTISED_10baseT_Half
|
119 ADVERTISED_10baseT_Full
|
120 ADVERTISED_100baseT_Half
|
121 ADVERTISED_100baseT_Full
)) == 0)
124 /* advertise only what has been requested */
125 advert
= mii
->mdio_read(dev
, mii
->phy_id
, MII_ADVERTISE
);
126 tmp
= advert
& ~(ADVERTISE_ALL
| ADVERTISE_100BASE4
);
127 if (ecmd
->advertising
& ADVERTISED_10baseT_Half
)
128 tmp
|= ADVERTISE_10HALF
;
129 if (ecmd
->advertising
& ADVERTISED_10baseT_Full
)
130 tmp
|= ADVERTISE_10FULL
;
131 if (ecmd
->advertising
& ADVERTISED_100baseT_Half
)
132 tmp
|= ADVERTISE_100HALF
;
133 if (ecmd
->advertising
& ADVERTISED_100baseT_Full
)
134 tmp
|= ADVERTISE_100FULL
;
136 mii
->mdio_write(dev
, mii
->phy_id
, MII_ADVERTISE
, tmp
);
137 mii
->advertising
= tmp
;
140 /* turn on autonegotiation, and force a renegotiate */
141 bmcr
= mii
->mdio_read(dev
, mii
->phy_id
, MII_BMCR
);
142 bmcr
|= (BMCR_ANENABLE
| BMCR_ANRESTART
);
143 mii
->mdio_write(dev
, mii
->phy_id
, MII_BMCR
, bmcr
);
145 mii
->force_media
= 0;
149 /* turn off auto negotiation, set speed and duplexity */
150 bmcr
= mii
->mdio_read(dev
, mii
->phy_id
, MII_BMCR
);
151 tmp
= bmcr
& ~(BMCR_ANENABLE
| BMCR_SPEED100
| BMCR_FULLDPLX
);
152 if (ecmd
->speed
== SPEED_100
)
153 tmp
|= BMCR_SPEED100
;
154 if (ecmd
->duplex
== DUPLEX_FULL
) {
155 tmp
|= BMCR_FULLDPLX
;
156 mii
->full_duplex
= 1;
158 mii
->full_duplex
= 0;
160 mii
->mdio_write(dev
, mii
->phy_id
, MII_BMCR
, tmp
);
162 mii
->force_media
= 1;
167 int mii_link_ok (struct mii_if_info
*mii
)
169 /* first, a dummy read, needed to latch some MII phys */
170 mii
->mdio_read(mii
->dev
, mii
->phy_id
, MII_BMSR
);
171 if (mii
->mdio_read(mii
->dev
, mii
->phy_id
, MII_BMSR
) & BMSR_LSTATUS
)
176 int mii_nway_restart (struct mii_if_info
*mii
)
181 /* if autoneg is off, it's an error */
182 bmcr
= mii
->mdio_read(mii
->dev
, mii
->phy_id
, MII_BMCR
);
184 if (bmcr
& BMCR_ANENABLE
) {
185 bmcr
|= BMCR_ANRESTART
;
186 mii
->mdio_write(mii
->dev
, mii
->phy_id
, MII_BMCR
, bmcr
);
193 void mii_check_link (struct mii_if_info
*mii
)
195 int cur_link
= mii_link_ok(mii
);
196 int prev_link
= netif_carrier_ok(mii
->dev
);
198 if (cur_link
&& !prev_link
)
199 netif_carrier_on(mii
->dev
);
200 else if (prev_link
&& !cur_link
)
201 netif_carrier_off(mii
->dev
);
204 unsigned int mii_check_media (struct mii_if_info
*mii
,
205 unsigned int ok_to_print
,
206 unsigned int init_media
)
208 unsigned int old_carrier
, new_carrier
;
209 int advertise
, lpa
, media
, duplex
;
211 /* if forced media, go no further */
212 if (mii
->force_media
)
213 return 0; /* duplex did not change */
215 /* check current and old link status */
216 old_carrier
= netif_carrier_ok(mii
->dev
) ? 1 : 0;
217 new_carrier
= (unsigned int) mii_link_ok(mii
);
219 /* if carrier state did not change, this is a "bounce",
220 * just exit as everything is already set correctly
222 if ((!init_media
) && (old_carrier
== new_carrier
))
223 return 0; /* duplex did not change */
225 /* no carrier, nothing much to do */
227 netif_carrier_off(mii
->dev
);
229 printk(KERN_INFO
"%s: link down\n", mii
->dev
->name
);
230 return 0; /* duplex did not change */
234 * we have carrier, see who's on the other end
236 netif_carrier_on(mii
->dev
);
238 /* get MII advertise and LPA values */
239 if ((!init_media
) && (mii
->advertising
))
240 advertise
= mii
->advertising
;
242 advertise
= mii
->mdio_read(mii
->dev
, mii
->phy_id
, MII_ADVERTISE
);
243 mii
->advertising
= advertise
;
245 lpa
= mii
->mdio_read(mii
->dev
, mii
->phy_id
, MII_LPA
);
247 /* figure out media and duplex from advertise and LPA values */
248 media
= mii_nway_result(lpa
& advertise
);
249 duplex
= (media
& ADVERTISE_FULL
) ? 1 : 0;
252 printk(KERN_INFO
"%s: link up, %sMbps, %s-duplex, lpa 0x%04X\n",
254 media
& (ADVERTISE_100FULL
| ADVERTISE_100HALF
) ?
256 duplex
? "full" : "half",
259 if ((init_media
) || (mii
->full_duplex
!= duplex
)) {
260 mii
->full_duplex
= duplex
;
261 return 1; /* duplex changed */
264 return 0; /* duplex did not change */
267 int generic_mii_ioctl(struct mii_if_info
*mii_if
,
268 struct mii_ioctl_data
*mii_data
, int cmd
,
269 unsigned int *duplex_chg_out
)
272 unsigned int duplex_changed
= 0;
277 mii_data
->phy_id
&= mii_if
->phy_id_mask
;
278 mii_data
->reg_num
&= mii_if
->reg_num_mask
;
282 mii_data
->phy_id
= mii_if
->phy_id
;
287 mii_if
->mdio_read(mii_if
->dev
, mii_data
->phy_id
,
292 u16 val
= mii_data
->val_in
;
294 if (!capable(CAP_NET_ADMIN
))
297 if (mii_data
->phy_id
== mii_if
->phy_id
) {
298 switch(mii_data
->reg_num
) {
300 unsigned int new_duplex
= 0;
301 if (val
& (BMCR_RESET
|BMCR_ANENABLE
))
302 mii_if
->force_media
= 0;
304 mii_if
->force_media
= 1;
305 if (mii_if
->force_media
&&
306 (val
& BMCR_FULLDPLX
))
308 if (mii_if
->full_duplex
!= new_duplex
) {
310 mii_if
->full_duplex
= new_duplex
;
315 mii_if
->advertising
= val
;
323 mii_if
->mdio_write(mii_if
->dev
, mii_data
->phy_id
,
324 mii_data
->reg_num
, val
);
333 if ((rc
== 0) && (duplex_chg_out
) && (duplex_changed
))
339 MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
340 MODULE_DESCRIPTION ("MII hardware support library");
341 MODULE_LICENSE("GPL");
343 EXPORT_SYMBOL(mii_link_ok
);
344 EXPORT_SYMBOL(mii_nway_restart
);
345 EXPORT_SYMBOL(mii_ethtool_gset
);
346 EXPORT_SYMBOL(mii_ethtool_sset
);
347 EXPORT_SYMBOL(mii_check_link
);
348 EXPORT_SYMBOL(mii_check_media
);
349 EXPORT_SYMBOL(generic_mii_ioctl
);