Linux Audio

Check our new training course

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