File Index
Files and directories
Sign in
FILE

mii.c

File preview with syntax highlighting.

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