Blame view

drivers/net/mii.c 18 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
  /*
  
  	mii.c: MII interface library
  
  	Maintained by Jeff Garzik <jgarzik@pobox.com>
  	Copyright 2001,2002 Jeff Garzik
  
  	Various code came from myson803.c and other files by
  	Donald Becker.  Copyright:
  
  		Written 1998-2002 by Donald Becker.
  
  		This software may be used and distributed according
  		to the terms of the GNU General Public License (GPL),
  		incorporated herein by reference.  Drivers based on
  		or derived from this code fall under the GPL and must
  		retain the authorship, copyright and license notice.
  		This file is not a complete program and may only be
  		used when the entire operating system is licensed
  		under the GPL.
  
  		The author may be reached as becker@scyld.com, or C/O
  		Scyld Computing Corporation
  		410 Severn Ave., Suite 210
  		Annapolis MD 21403
  
  
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/netdevice.h>
  #include <linux/ethtool.h>
9c4df53bc   Ben Hutchings   ethtool, mdio, mi...
34
  #include <linux/mii.h>
5974700c2   Ben Hutchings   mii: Rewrite mii_...
35
36
37
  
  static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
  {
5974700c2   Ben Hutchings   mii: Rewrite mii_...
38
39
40
  	int advert;
  
  	advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
28011cf19   Matt Carlson   net: Add ethtool ...
41

37f07023d   Matt Carlson   net: Change mii t...
42
  	return mii_lpa_to_ethtool_lpa_t(advert);
5974700c2   Ben Hutchings   mii: Rewrite mii_...
43
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44

32684ec61   Randy Dunlap   mii: add kernel-d...
45
46
47
48
49
  /**
   * mii_ethtool_gset - get settings that are specified in @ecmd
   * @mii: MII interface
   * @ecmd: requested ethtool_cmd
   *
8ae6daca8   David Decotigny   ethtool: Call eth...
50
51
52
   * The @ecmd parameter is expected to have been cleared before calling
   * mii_ethtool_gset().
   *
32684ec61   Randy Dunlap   mii: add kernel-d...
53
54
   * Returns 0 for success, negative on error.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
56
57
  int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
  {
  	struct net_device *dev = mii->dev;
5974700c2   Ben Hutchings   mii: Rewrite mii_...
58
59
  	u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
  	u32 nego;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  
  	ecmd->supported =
  	    (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
  	     SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
  	     SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
  	if (mii->supports_gmii)
  		ecmd->supported |= SUPPORTED_1000baseT_Half |
  			SUPPORTED_1000baseT_Full;
  
  	/* only supports twisted-pair */
  	ecmd->port = PORT_MII;
  
  	/* only supports internal transceiver */
  	ecmd->transceiver = XCVR_INTERNAL;
  
  	/* this isn't fully supported at higher layers */
  	ecmd->phy_address = mii->phy_id;
9c4df53bc   Ben Hutchings   ethtool, mdio, mi...
77
  	ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
  
  	ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
  
  	bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
5974700c2   Ben Hutchings   mii: Rewrite mii_...
82
  	bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
  	if (mii->supports_gmii) {
5974700c2   Ben Hutchings   mii: Rewrite mii_...
84
85
   		ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
  		stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
88
89
  	}
  	if (bmcr & BMCR_ANENABLE) {
  		ecmd->advertising |= ADVERTISED_Autoneg;
  		ecmd->autoneg = AUTONEG_ENABLE;
6aa20a223   Jeff Garzik   drivers/net: Trim...
90

5974700c2   Ben Hutchings   mii: Rewrite mii_...
91
  		ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE);
28011cf19   Matt Carlson   net: Add ethtool ...
92
  		if (mii->supports_gmii)
37f07023d   Matt Carlson   net: Change mii t...
93
94
  			ecmd->advertising |=
  					mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
5974700c2   Ben Hutchings   mii: Rewrite mii_...
95
96
97
  
  		if (bmsr & BMSR_ANEGCOMPLETE) {
  			ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
28011cf19   Matt Carlson   net: Add ethtool ...
98
  			ecmd->lp_advertising |=
37f07023d   Matt Carlson   net: Change mii t...
99
  					mii_stat1000_to_ethtool_lpa_t(stat1000);
5974700c2   Ben Hutchings   mii: Rewrite mii_...
100
101
102
103
104
105
106
107
  		} else {
  			ecmd->lp_advertising = 0;
  		}
  
  		nego = ecmd->advertising & ecmd->lp_advertising;
  
  		if (nego & (ADVERTISED_1000baseT_Full |
  			    ADVERTISED_1000baseT_Half)) {
707394972   David Decotigny   ethtool: cosmetic...
108
  			ethtool_cmd_speed_set(ecmd, SPEED_1000);
5974700c2   Ben Hutchings   mii: Rewrite mii_...
109
110
111
  			ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
  		} else if (nego & (ADVERTISED_100baseT_Full |
  				   ADVERTISED_100baseT_Half)) {
707394972   David Decotigny   ethtool: cosmetic...
112
  			ethtool_cmd_speed_set(ecmd, SPEED_100);
5974700c2   Ben Hutchings   mii: Rewrite mii_...
113
  			ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
  		} else {
707394972   David Decotigny   ethtool: cosmetic...
115
  			ethtool_cmd_speed_set(ecmd, SPEED_10);
5974700c2   Ben Hutchings   mii: Rewrite mii_...
116
  			ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
119
  		}
  	} else {
  		ecmd->autoneg = AUTONEG_DISABLE;
707394972   David Decotigny   ethtool: cosmetic...
120
121
122
123
124
125
  		ethtool_cmd_speed_set(ecmd,
  				      ((bmcr & BMCR_SPEED1000 &&
  					(bmcr & BMCR_SPEED100) == 0) ?
  				       SPEED_1000 :
  				       ((bmcr & BMCR_SPEED100) ?
  					SPEED_100 : SPEED_10)));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
  		ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
  	}
5974700c2   Ben Hutchings   mii: Rewrite mii_...
128
  	mii->full_duplex = ecmd->duplex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
132
  	/* ignore maxtxpkt, maxrxpkt for now */
  
  	return 0;
  }
32684ec61   Randy Dunlap   mii: add kernel-d...
133
  /**
bc8ee596a   Philippe Reynes   net: mii: add gen...
134
135
136
137
138
139
   * mii_ethtool_get_link_ksettings - get settings that are specified in @cmd
   * @mii: MII interface
   * @cmd: requested ethtool_link_ksettings
   *
   * The @cmd parameter is expected to have been cleared before calling
   * mii_ethtool_get_link_ksettings().
bc8ee596a   Philippe Reynes   net: mii: add gen...
140
   */
82c01a84d   yuval.shaia@oracle.com   net/{mii, smsc}: ...
141
142
  void mii_ethtool_get_link_ksettings(struct mii_if_info *mii,
  				    struct ethtool_link_ksettings *cmd)
bc8ee596a   Philippe Reynes   net: mii: add gen...
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
  {
  	struct net_device *dev = mii->dev;
  	u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
  	u32 nego, supported, advertising, lp_advertising;
  
  	supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
  		     SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
  		     SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
  	if (mii->supports_gmii)
  		supported |= SUPPORTED_1000baseT_Half |
  			SUPPORTED_1000baseT_Full;
  
  	/* only supports twisted-pair */
  	cmd->base.port = PORT_MII;
  
  	/* this isn't fully supported at higher layers */
  	cmd->base.phy_address = mii->phy_id;
  	cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22;
  
  	advertising = ADVERTISED_TP | ADVERTISED_MII;
  
  	bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
  	bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
  	if (mii->supports_gmii) {
  		ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
  		stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
  	}
  	if (bmcr & BMCR_ANENABLE) {
  		advertising |= ADVERTISED_Autoneg;
  		cmd->base.autoneg = AUTONEG_ENABLE;
  
  		advertising |= mii_get_an(mii, MII_ADVERTISE);
  		if (mii->supports_gmii)
  			advertising |= mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
  
  		if (bmsr & BMSR_ANEGCOMPLETE) {
  			lp_advertising = mii_get_an(mii, MII_LPA);
  			lp_advertising |=
  					mii_stat1000_to_ethtool_lpa_t(stat1000);
  		} else {
  			lp_advertising = 0;
  		}
  
  		nego = advertising & lp_advertising;
  
  		if (nego & (ADVERTISED_1000baseT_Full |
  			    ADVERTISED_1000baseT_Half)) {
  			cmd->base.speed = SPEED_1000;
  			cmd->base.duplex = !!(nego & ADVERTISED_1000baseT_Full);
  		} else if (nego & (ADVERTISED_100baseT_Full |
  				   ADVERTISED_100baseT_Half)) {
  			cmd->base.speed = SPEED_100;
  			cmd->base.duplex = !!(nego & ADVERTISED_100baseT_Full);
  		} else {
  			cmd->base.speed = SPEED_10;
  			cmd->base.duplex = !!(nego & ADVERTISED_10baseT_Full);
  		}
  	} else {
  		cmd->base.autoneg = AUTONEG_DISABLE;
  
  		cmd->base.speed = ((bmcr & BMCR_SPEED1000 &&
  				    (bmcr & BMCR_SPEED100) == 0) ?
  				   SPEED_1000 :
  				   ((bmcr & BMCR_SPEED100) ?
  				    SPEED_100 : SPEED_10));
  		cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ?
  			DUPLEX_FULL : DUPLEX_HALF;
dc0b2c9cb   Arnd Bergmann   net: mii: report ...
210
211
  
  		lp_advertising = 0;
bc8ee596a   Philippe Reynes   net: mii: add gen...
212
213
214
215
216
217
218
219
220
221
222
223
  	}
  
  	mii->full_duplex = cmd->base.duplex;
  
  	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
  						supported);
  	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
  						advertising);
  	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
  						lp_advertising);
  
  	/* ignore maxtxpkt, maxrxpkt for now */
bc8ee596a   Philippe Reynes   net: mii: add gen...
224
225
226
  }
  
  /**
32684ec61   Randy Dunlap   mii: add kernel-d...
227
228
229
230
231
232
   * mii_ethtool_sset - set settings that are specified in @ecmd
   * @mii: MII interface
   * @ecmd: requested ethtool_cmd
   *
   * Returns 0 for success, negative on error.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
234
235
  int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
  {
  	struct net_device *dev = mii->dev;
25db03388   David Decotigny   ethtool: Use full...
236
  	u32 speed = ethtool_cmd_speed(ecmd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237

25db03388   David Decotigny   ethtool: Use full...
238
239
240
  	if (speed != SPEED_10 &&
  	    speed != SPEED_100 &&
  	    speed != SPEED_1000)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
242
243
244
245
246
247
248
249
250
251
  		return -EINVAL;
  	if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
  		return -EINVAL;
  	if (ecmd->port != PORT_MII)
  		return -EINVAL;
  	if (ecmd->transceiver != XCVR_INTERNAL)
  		return -EINVAL;
  	if (ecmd->phy_address != mii->phy_id)
  		return -EINVAL;
  	if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
  		return -EINVAL;
25db03388   David Decotigny   ethtool: Use full...
252
  	if ((speed == SPEED_1000) && (!mii->supports_gmii))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
  		return -EINVAL;
6aa20a223   Jeff Garzik   drivers/net: Trim...
254

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
  	/* ignore supported, maxtxpkt, maxrxpkt */
6aa20a223   Jeff Garzik   drivers/net: Trim...
256

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
  	if (ecmd->autoneg == AUTONEG_ENABLE) {
  		u32 bmcr, advert, tmp;
  		u32 advert2 = 0, tmp2 = 0;
  
  		if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
  					  ADVERTISED_10baseT_Full |
  					  ADVERTISED_100baseT_Half |
  					  ADVERTISED_100baseT_Full |
  					  ADVERTISED_1000baseT_Half |
  					  ADVERTISED_1000baseT_Full)) == 0)
  			return -EINVAL;
  
  		/* advertise only what has been requested */
  		advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
  		tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
  		if (mii->supports_gmii) {
  			advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
  			tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
  		}
37f07023d   Matt Carlson   net: Change mii t...
276
  		tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
28011cf19   Matt Carlson   net: Add ethtool ...
277
278
  
  		if (mii->supports_gmii)
37f07023d   Matt Carlson   net: Change mii t...
279
280
  			tmp2 |=
  			      ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
283
284
285
286
  		if (advert != tmp) {
  			mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
  			mii->advertising = tmp;
  		}
  		if ((mii->supports_gmii) && (advert2 != tmp2))
  			mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
6aa20a223   Jeff Garzik   drivers/net: Trim...
287

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
289
290
291
292
293
294
295
296
297
298
  		/* turn on autonegotiation, and force a renegotiate */
  		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
  		bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
  		mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
  
  		mii->force_media = 0;
  	} else {
  		u32 bmcr, tmp;
  
  		/* turn off auto negotiation, set speed and duplexity */
  		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
6aa20a223   Jeff Garzik   drivers/net: Trim...
299
  		tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
  			       BMCR_SPEED1000 | BMCR_FULLDPLX);
25db03388   David Decotigny   ethtool: Use full...
301
  		if (speed == SPEED_1000)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
  			tmp |= BMCR_SPEED1000;
25db03388   David Decotigny   ethtool: Use full...
303
  		else if (speed == SPEED_100)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
305
306
307
308
309
310
311
312
313
314
315
316
  			tmp |= BMCR_SPEED100;
  		if (ecmd->duplex == DUPLEX_FULL) {
  			tmp |= BMCR_FULLDPLX;
  			mii->full_duplex = 1;
  		} else
  			mii->full_duplex = 0;
  		if (bmcr != tmp)
  			mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
  
  		mii->force_media = 1;
  	}
  	return 0;
  }
32684ec61   Randy Dunlap   mii: add kernel-d...
317
  /**
bc8ee596a   Philippe Reynes   net: mii: add gen...
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
   * mii_ethtool_set_link_ksettings - set settings that are specified in @cmd
   * @mii: MII interfaces
   * @cmd: requested ethtool_link_ksettings
   *
   * Returns 0 for success, negative on error.
   */
  int mii_ethtool_set_link_ksettings(struct mii_if_info *mii,
  				   const struct ethtool_link_ksettings *cmd)
  {
  	struct net_device *dev = mii->dev;
  	u32 speed = cmd->base.speed;
  
  	if (speed != SPEED_10 &&
  	    speed != SPEED_100 &&
  	    speed != SPEED_1000)
  		return -EINVAL;
  	if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL)
  		return -EINVAL;
  	if (cmd->base.port != PORT_MII)
  		return -EINVAL;
  	if (cmd->base.phy_address != mii->phy_id)
  		return -EINVAL;
  	if (cmd->base.autoneg != AUTONEG_DISABLE &&
  	    cmd->base.autoneg != AUTONEG_ENABLE)
  		return -EINVAL;
  	if ((speed == SPEED_1000) && (!mii->supports_gmii))
  		return -EINVAL;
  
  	/* ignore supported, maxtxpkt, maxrxpkt */
  
  	if (cmd->base.autoneg == AUTONEG_ENABLE) {
  		u32 bmcr, advert, tmp;
  		u32 advert2 = 0, tmp2 = 0;
  		u32 advertising;
  
  		ethtool_convert_link_mode_to_legacy_u32(
  			&advertising, cmd->link_modes.advertising);
  
  		if ((advertising & (ADVERTISED_10baseT_Half |
  				    ADVERTISED_10baseT_Full |
  				    ADVERTISED_100baseT_Half |
  				    ADVERTISED_100baseT_Full |
  				    ADVERTISED_1000baseT_Half |
  				    ADVERTISED_1000baseT_Full)) == 0)
  			return -EINVAL;
  
  		/* advertise only what has been requested */
  		advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
  		tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
  		if (mii->supports_gmii) {
  			advert2 = mii->mdio_read(dev, mii->phy_id,
  						 MII_CTRL1000);
  			tmp2 = advert2 &
  				~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
  		}
  		tmp |= ethtool_adv_to_mii_adv_t(advertising);
  
  		if (mii->supports_gmii)
  			tmp2 |= ethtool_adv_to_mii_ctrl1000_t(advertising);
  		if (advert != tmp) {
  			mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
  			mii->advertising = tmp;
  		}
  		if ((mii->supports_gmii) && (advert2 != tmp2))
  			mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
  
  		/* turn on autonegotiation, and force a renegotiate */
  		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
  		bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
  		mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
  
  		mii->force_media = 0;
  	} else {
  		u32 bmcr, tmp;
  
  		/* turn off auto negotiation, set speed and duplexity */
  		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
  		tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
  			       BMCR_SPEED1000 | BMCR_FULLDPLX);
  		if (speed == SPEED_1000)
  			tmp |= BMCR_SPEED1000;
  		else if (speed == SPEED_100)
  			tmp |= BMCR_SPEED100;
  		if (cmd->base.duplex == DUPLEX_FULL) {
  			tmp |= BMCR_FULLDPLX;
  			mii->full_duplex = 1;
  		} else {
  			mii->full_duplex = 0;
  		}
  		if (bmcr != tmp)
  			mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
  
  		mii->force_media = 1;
  	}
  	return 0;
  }
  
  /**
32684ec61   Randy Dunlap   mii: add kernel-d...
416
417
418
   * mii_check_gmii_support - check if the MII supports Gb interfaces
   * @mii: the MII interface
   */
43ec6e95e   Dale Farnsworth   [PATCH] mii: Add ...
419
420
421
422
423
424
425
426
427
428
429
430
431
  int mii_check_gmii_support(struct mii_if_info *mii)
  {
  	int reg;
  
  	reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
  	if (reg & BMSR_ESTATEN) {
  		reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
  		if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
  			return 1;
  	}
  
  	return 0;
  }
32684ec61   Randy Dunlap   mii: add kernel-d...
432
433
434
435
436
437
  /**
   * mii_link_ok - is link status up/ok
   * @mii: the MII interface
   *
   * Returns 1 if the MII reports link status up/ok, 0 otherwise.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
439
440
441
442
443
444
445
  int mii_link_ok (struct mii_if_info *mii)
  {
  	/* first, a dummy read, needed to latch some MII phys */
  	mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
  	if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
  		return 1;
  	return 0;
  }
32684ec61   Randy Dunlap   mii: add kernel-d...
446
447
448
449
450
451
  /**
   * mii_nway_restart - restart NWay (autonegotiation) for this interface
   * @mii: the MII interface
   *
   * Returns 0 on success, negative on error.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
  int mii_nway_restart (struct mii_if_info *mii)
  {
  	int bmcr;
  	int r = -EINVAL;
  
  	/* if autoneg is off, it's an error */
  	bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
  
  	if (bmcr & BMCR_ANENABLE) {
  		bmcr |= BMCR_ANRESTART;
  		mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
  		r = 0;
  	}
  
  	return r;
  }
32684ec61   Randy Dunlap   mii: add kernel-d...
468
469
470
471
472
473
474
475
  /**
   * mii_check_link - check MII link status
   * @mii: MII interface
   *
   * If the link status changed (previous != current), call
   * netif_carrier_on() if current link status is Up or call
   * netif_carrier_off() if current link status is Down.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
477
478
479
480
481
482
483
484
485
  void mii_check_link (struct mii_if_info *mii)
  {
  	int cur_link = mii_link_ok(mii);
  	int prev_link = netif_carrier_ok(mii->dev);
  
  	if (cur_link && !prev_link)
  		netif_carrier_on(mii->dev);
  	else if (prev_link && !cur_link)
  		netif_carrier_off(mii->dev);
  }
32684ec61   Randy Dunlap   mii: add kernel-d...
486
  /**
5bdc73800   Ben Hutchings   mii: Handle link ...
487
   * mii_check_media - check the MII interface for a carrier/speed/duplex change
32684ec61   Randy Dunlap   mii: add kernel-d...
488
489
490
491
492
493
494
   * @mii: the MII interface
   * @ok_to_print: OK to print link up/down messages
   * @init_media: OK to save duplex mode in @mii
   *
   * Returns 1 if the duplex mode changed, 0 if not.
   * If the media type is forced, always returns 0.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
497
498
499
500
501
  unsigned int mii_check_media (struct mii_if_info *mii,
  			      unsigned int ok_to_print,
  			      unsigned int init_media)
  {
  	unsigned int old_carrier, new_carrier;
  	int advertise, lpa, media, duplex;
  	int lpa2 = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
503
504
505
506
507
508
509
510
511
512
513
514
515
  	/* check current and old link status */
  	old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
  	new_carrier = (unsigned int) mii_link_ok(mii);
  
  	/* if carrier state did not change, this is a "bounce",
  	 * just exit as everything is already set correctly
  	 */
  	if ((!init_media) && (old_carrier == new_carrier))
  		return 0; /* duplex did not change */
  
  	/* no carrier, nothing much to do */
  	if (!new_carrier) {
  		netif_carrier_off(mii->dev);
  		if (ok_to_print)
967faf3b7   Joe Perches   mii: Convert prin...
516
517
  			netdev_info(mii->dev, "link down
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
519
520
521
522
523
524
  		return 0; /* duplex did not change */
  	}
  
  	/*
  	 * we have carrier, see who's on the other end
  	 */
  	netif_carrier_on(mii->dev);
5bdc73800   Ben Hutchings   mii: Handle link ...
525
526
527
528
529
530
  	if (mii->force_media) {
  		if (ok_to_print)
  			netdev_info(mii->dev, "link up
  ");
  		return 0; /* duplex did not change */
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
  	/* get MII advertise and LPA values */
  	if ((!init_media) && (mii->advertising))
  		advertise = mii->advertising;
  	else {
  		advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
  		mii->advertising = advertise;
  	}
  	lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
  	if (mii->supports_gmii)
  		lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
  
  	/* figure out media and duplex from advertise and LPA values */
  	media = mii_nway_result(lpa & advertise);
  	duplex = (media & ADVERTISE_FULL) ? 1 : 0;
  	if (lpa2 & LPA_1000FULL)
  		duplex = 1;
  
  	if (ok_to_print)
967faf3b7   Joe Perches   mii: Convert prin...
549
550
551
552
553
554
555
  		netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X
  ",
  			    lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
  			    media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
  			    100 : 10,
  			    duplex ? "full" : "half",
  			    lpa);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
557
558
559
560
561
562
563
  
  	if ((init_media) || (mii->full_duplex != duplex)) {
  		mii->full_duplex = duplex;
  		return 1; /* duplex changed */
  	}
  
  	return 0; /* duplex did not change */
  }
32684ec61   Randy Dunlap   mii: add kernel-d...
564
565
566
567
568
569
570
571
572
573
  /**
   * generic_mii_ioctl - main MII ioctl interface
   * @mii_if: the MII interface
   * @mii_data: MII ioctl data structure
   * @cmd: MII ioctl command
   * @duplex_chg_out: pointer to @duplex_changed status if there was no
   *	ioctl error
   *
   * Returns 0 on success, negative on error.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
  int generic_mii_ioctl(struct mii_if_info *mii_if,
  		      struct mii_ioctl_data *mii_data, int cmd,
  		      unsigned int *duplex_chg_out)
  {
  	int rc = 0;
  	unsigned int duplex_changed = 0;
  
  	if (duplex_chg_out)
  		*duplex_chg_out = 0;
  
  	mii_data->phy_id &= mii_if->phy_id_mask;
  	mii_data->reg_num &= mii_if->reg_num_mask;
  
  	switch(cmd) {
  	case SIOCGMIIPHY:
  		mii_data->phy_id = mii_if->phy_id;
  		/* fall through */
  
  	case SIOCGMIIREG:
  		mii_data->val_out =
  			mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
  					  mii_data->reg_num);
  		break;
  
  	case SIOCSMIIREG: {
  		u16 val = mii_data->val_in;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
  		if (mii_data->phy_id == mii_if->phy_id) {
  			switch(mii_data->reg_num) {
  			case MII_BMCR: {
  				unsigned int new_duplex = 0;
  				if (val & (BMCR_RESET|BMCR_ANENABLE))
  					mii_if->force_media = 0;
  				else
  					mii_if->force_media = 1;
  				if (mii_if->force_media &&
  				    (val & BMCR_FULLDPLX))
  					new_duplex = 1;
  				if (mii_if->full_duplex != new_duplex) {
  					duplex_changed = 1;
  					mii_if->full_duplex = new_duplex;
  				}
  				break;
  			}
  			case MII_ADVERTISE:
  				mii_if->advertising = val;
  				break;
  			default:
  				/* do nothing */
  				break;
  			}
  		}
  
  		mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
  				   mii_data->reg_num, val);
  		break;
  	}
  
  	default:
  		rc = -EOPNOTSUPP;
  		break;
  	}
  
  	if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
  		*duplex_chg_out = 1;
  
  	return rc;
  }
  
  MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
  MODULE_DESCRIPTION ("MII hardware support library");
  MODULE_LICENSE("GPL");
  
  EXPORT_SYMBOL(mii_link_ok);
  EXPORT_SYMBOL(mii_nway_restart);
  EXPORT_SYMBOL(mii_ethtool_gset);
bc8ee596a   Philippe Reynes   net: mii: add gen...
649
  EXPORT_SYMBOL(mii_ethtool_get_link_ksettings);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
650
  EXPORT_SYMBOL(mii_ethtool_sset);
bc8ee596a   Philippe Reynes   net: mii: add gen...
651
  EXPORT_SYMBOL(mii_ethtool_set_link_ksettings);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652
653
  EXPORT_SYMBOL(mii_check_link);
  EXPORT_SYMBOL(mii_check_media);
43ec6e95e   Dale Farnsworth   [PATCH] mii: Add ...
654
  EXPORT_SYMBOL(mii_check_gmii_support);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
  EXPORT_SYMBOL(generic_mii_ioctl);