Linux Audio

Check our new training course

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