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 carrier/speed/duplex change
306 * @mii: the MII interface
307 * @ok_to_print: OK to print link up/down messages
308 * @init_media: OK to save duplex mode in @mii
309 *
310 * Returns 1 if the duplex mode changed, 0 if not.
311 * If the media type is forced, always returns 0.
312 */
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	/* check current and old link status */
322	old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
323	new_carrier = (unsigned int) mii_link_ok(mii);
324
325	/* if carrier state did not change, this is a "bounce",
326	 * just exit as everything is already set correctly
327	 */
328	if ((!init_media) && (old_carrier == new_carrier))
329		return 0; /* duplex did not change */
330
331	/* no carrier, nothing much to do */
332	if (!new_carrier) {
333		netif_carrier_off(mii->dev);
334		if (ok_to_print)
335			netdev_info(mii->dev, "link down\n");
336		return 0; /* duplex did not change */
337	}
338
339	/*
340	 * we have carrier, see who's on the other end
341	 */
342	netif_carrier_on(mii->dev);
343
344	if (mii->force_media) {
345		if (ok_to_print)
346			netdev_info(mii->dev, "link up\n");
347		return 0; /* duplex did not change */
348	}
349
350	/* get MII advertise and LPA values */
351	if ((!init_media) && (mii->advertising))
352		advertise = mii->advertising;
353	else {
354		advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
355		mii->advertising = advertise;
356	}
357	lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
358	if (mii->supports_gmii)
359		lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
360
361	/* figure out media and duplex from advertise and LPA values */
362	media = mii_nway_result(lpa & advertise);
363	duplex = (media & ADVERTISE_FULL) ? 1 : 0;
364	if (lpa2 & LPA_1000FULL)
365		duplex = 1;
366
367	if (ok_to_print)
368		netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
369			    lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
370			    media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
371			    100 : 10,
372			    duplex ? "full" : "half",
373			    lpa);
374
375	if ((init_media) || (mii->full_duplex != duplex)) {
376		mii->full_duplex = duplex;
377		return 1; /* duplex changed */
378	}
379
380	return 0; /* duplex did not change */
381}
382
383/**
384 * generic_mii_ioctl - main MII ioctl interface
385 * @mii_if: the MII interface
386 * @mii_data: MII ioctl data structure
387 * @cmd: MII ioctl command
388 * @duplex_chg_out: pointer to @duplex_changed status if there was no
389 *	ioctl error
390 *
391 * Returns 0 on success, negative on error.
392 */
393int generic_mii_ioctl(struct mii_if_info *mii_if,
394		      struct mii_ioctl_data *mii_data, int cmd,
395		      unsigned int *duplex_chg_out)
396{
397	int rc = 0;
398	unsigned int duplex_changed = 0;
399
400	if (duplex_chg_out)
401		*duplex_chg_out = 0;
402
403	mii_data->phy_id &= mii_if->phy_id_mask;
404	mii_data->reg_num &= mii_if->reg_num_mask;
405
406	switch(cmd) {
407	case SIOCGMIIPHY:
408		mii_data->phy_id = mii_if->phy_id;
409		/* fall through */
410
411	case SIOCGMIIREG:
412		mii_data->val_out =
413			mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
414					  mii_data->reg_num);
415		break;
416
417	case SIOCSMIIREG: {
418		u16 val = mii_data->val_in;
419
420		if (mii_data->phy_id == mii_if->phy_id) {
421			switch(mii_data->reg_num) {
422			case MII_BMCR: {
423				unsigned int new_duplex = 0;
424				if (val & (BMCR_RESET|BMCR_ANENABLE))
425					mii_if->force_media = 0;
426				else
427					mii_if->force_media = 1;
428				if (mii_if->force_media &&
429				    (val & BMCR_FULLDPLX))
430					new_duplex = 1;
431				if (mii_if->full_duplex != new_duplex) {
432					duplex_changed = 1;
433					mii_if->full_duplex = new_duplex;
434				}
435				break;
436			}
437			case MII_ADVERTISE:
438				mii_if->advertising = val;
439				break;
440			default:
441				/* do nothing */
442				break;
443			}
444		}
445
446		mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
447				   mii_data->reg_num, val);
448		break;
449	}
450
451	default:
452		rc = -EOPNOTSUPP;
453		break;
454	}
455
456	if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
457		*duplex_chg_out = 1;
458
459	return rc;
460}
461
462MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
463MODULE_DESCRIPTION ("MII hardware support library");
464MODULE_LICENSE("GPL");
465
466EXPORT_SYMBOL(mii_link_ok);
467EXPORT_SYMBOL(mii_nway_restart);
468EXPORT_SYMBOL(mii_ethtool_gset);
469EXPORT_SYMBOL(mii_ethtool_sset);
470EXPORT_SYMBOL(mii_check_link);
471EXPORT_SYMBOL(mii_check_media);
472EXPORT_SYMBOL(mii_check_gmii_support);
473EXPORT_SYMBOL(generic_mii_ioctl);
474
475