summaryrefslogtreecommitdiffstats
path: root/drivers/net/tulip/media.c
blob: 4bd13922875d029c9a0e5b155f235df2c5d620c1 (plain)
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
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
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
/*
	drivers/net/tulip/media.c

	Copyright 2000,2001  The Linux Kernel Team
	Written/copyright 1994-2001 by Donald Becker.

	This software may be used and distributed according to the terms
	of the GNU General Public License, incorporated herein by reference.

	Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html}
	for more information on this driver.

	Please submit bugs to http://bugzilla.kernel.org/ .
*/

#include <linux/kernel.h>
#include <linux/mii.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include "tulip.h"


/* The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
   met by back-to-back PCI I/O cycles, but we insert a delay to avoid
   "overclocking" issues or future 66Mhz PCI. */
#define mdio_delay() ioread32(mdio_addr)

/* Read and write the MII registers using software-generated serial
   MDIO protocol.  It is just different enough from the EEPROM protocol
   to not share code.  The maxium data clock rate is 2.5 Mhz. */
#define MDIO_SHIFT_CLK		0x10000
#define MDIO_DATA_WRITE0	0x00000
#define MDIO_DATA_WRITE1	0x20000
#define MDIO_ENB		0x00000 /* Ignore the 0x02000 databook setting. */
#define MDIO_ENB_IN		0x40000
#define MDIO_DATA_READ		0x80000

static const unsigned char comet_miireg2offset[32] = {
	0xB4, 0xB8, 0xBC, 0xC0,  0xC4, 0xC8, 0xCC, 0,  0,0,0,0,  0,0,0,0,
	0,0xD0,0,0,  0,0,0,0,  0,0,0,0, 0, 0xD4, 0xD8, 0xDC, };


/* MII transceiver control section.
   Read and write the MII registers using software-generated serial
   MDIO protocol.
   See IEEE 802.3-2002.pdf (Section 2, Chapter "22.2.4 Management functions")
   or DP83840A data sheet for more details.
   */

int tulip_mdio_read(struct net_device *dev, int phy_id, int location)
{
	struct tulip_private *tp = netdev_priv(dev);
	int i;
	int read_cmd = (0xf6 << 10) | ((phy_id & 0x1f) << 5) | location;
	int retval = 0;
	void __iomem *ioaddr = tp->base_addr;
	void __iomem *mdio_addr = ioaddr + CSR9;
	unsigned long flags;

	if (location & ~0x1f)
		return 0xffff;

	if (tp->chip_id == COMET  &&  phy_id == 30) {
		if (comet_miireg2offset[location])
			return ioread32(ioaddr + comet_miireg2offset[location]);
		return 0xffff;
	}

	spin_lock_irqsave(&tp->mii_lock, flags);
	if (tp->chip_id == LC82C168) {
		iowrite32(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0);
		ioread32(ioaddr + 0xA0);
		ioread32(ioaddr + 0xA0);
		for (i = 1000; i >= 0; --i) {
			barrier();
			if ( ! ((retval = ioread32(ioaddr + 0xA0)) & 0x80000000))
				break;
		}
		spin_unlock_irqrestore(&tp->mii_lock, flags);
		return retval & 0xffff;
	}

	/* Establish sync by sending at least 32 logic ones. */
	for (i = 32; i >= 0; i--) {
		iowrite32(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
		mdio_delay();
		iowrite32(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
		mdio_delay();
	}
	/* Shift the read command bits out. */
	for (i = 15; i >= 0; i--) {
		int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;

		iowrite32(MDIO_ENB | dataval, mdio_addr);
		mdio_delay();
		iowrite32(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
		mdio_delay();
	}
	/* Read the two transition, 16 data, and wire-idle bits. */
	for (i = 19; i > 0; i--) {
		iowrite32(MDIO_ENB_IN, mdio_addr);
		mdio_delay();
		retval = (retval << 1) | ((ioread32(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
		iowrite32(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
		mdio_delay();
	}

	spin_unlock_irqrestore(&tp->mii_lock, flags);
	return (retval>>1) & 0xffff;
}

void tulip_mdio_write(struct net_device *dev, int phy_id, int location, int val)
{
	struct tulip_private *tp = netdev_priv(dev);
	int i;
	int cmd = (0x5002 << 16) | ((phy_id & 0x1f) << 23) | (location<<18) | (val & 0xffff);
	void __iomem *ioaddr = tp->base_addr;
	void __iomem *mdio_addr = ioaddr + CSR9;
	unsigned long flags;

	if (location & ~0x1f)
		return;

	if (tp->chip_id == COMET && phy_id == 30) {
		if (comet_miireg2offset[location])
			iowrite32(val, ioaddr + comet_miireg2offset[location]);
		return;
	}

	spin_lock_irqsave(&tp->mii_lock, flags);
	if (tp->chip_id == LC82C168) {
		iowrite32(cmd, ioaddr + 0xA0);
		for (i = 1000; i >= 0; --i) {
			barrier();
			if ( ! (ioread32(ioaddr + 0xA0) & 0x80000000))
				break;
		}
		spin_unlock_irqrestore(&tp->mii_lock, flags);
		return;
	}

	/* Establish sync by sending 32 logic ones. */
	for (i = 32; i >= 0; i--) {
		iowrite32(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
		mdio_delay();
		iowrite32(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
		mdio_delay();
	}
	/* Shift the command bits out. */
	for (i = 31; i >= 0; i--) {
		int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
		iowrite32(MDIO_ENB | dataval, mdio_addr);
		mdio_delay();
		iowrite32(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
		mdio_delay();
	}
	/* Clear out extra bits. */
	for (i = 2; i > 0; i--) {
		iowrite32(MDIO_ENB_IN, mdio_addr);
		mdio_delay();
		iowrite32(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
		mdio_delay();
	}

	spin_unlock_irqrestore(&tp->mii_lock, flags);
}


/* Set up the transceiver control registers for the selected media type. */
void tulip_select_media(struct net_device *dev, int startup)
{
	struct tulip_private *tp = netdev_priv(dev);
	void __iomem *ioaddr = tp->base_addr;
	struct mediatable *mtable = tp->mtable;
	u32 new_csr6;
	int i;

	if (mtable) {
		struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
		unsigned char *p = mleaf->leafdata;
		switch (mleaf->type) {
		case 0:					/* 21140 non-MII xcvr. */
			if (tulip_debug > 1)
				netdev_dbg(dev, "Using a 21140 non-MII transceiver with control setting %02x\n",
					   p[1]);
			dev->if_port = p[0];
			if (startup)
				iowrite32(mtable->csr12dir | 0x100, ioaddr + CSR12);
			iowrite32(p[1], ioaddr + CSR12);
			new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
			break;
		case 2: case 4: {
			u16 setup[5];
			u32 csr13val, csr14val, csr15dir, csr15val;
			for (i = 0; i < 5; i++)
				setup[i] = get_u16(&p[i*2 + 1]);

			dev->if_port = p[0] & MEDIA_MASK;
			if (tulip_media_cap[dev->if_port] & MediaAlwaysFD)
				tp->full_duplex = 1;

			if (startup && mtable->has_reset) {
				struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset];
				unsigned char *rst = rleaf->leafdata;
				if (tulip_debug > 1)
					netdev_dbg(dev, "Resetting the transceiver\n");
				for (i = 0; i < rst[0]; i++)
					iowrite32(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15);
			}
			if (tulip_debug > 1)
				netdev_dbg(dev, "21143 non-MII %s transceiver control %04x/%04x\n",
					   medianame[dev->if_port],
					   setup[0], setup[1]);
			if (p[0] & 0x40) {	/* SIA (CSR13-15) setup values are provided. */
				csr13val = setup[0];
				csr14val = setup[1];
				csr15dir = (setup[3]<<16) | setup[2];
				csr15val = (setup[4]<<16) | setup[2];
				iowrite32(0, ioaddr + CSR13);
				iowrite32(csr14val, ioaddr + CSR14);
				iowrite32(csr15dir, ioaddr + CSR15);	/* Direction */
				iowrite32(csr15val, ioaddr + CSR15);	/* Data */
				iowrite32(csr13val, ioaddr + CSR13);
			} else {
				csr13val = 1;
				csr14val = 0;
				csr15dir = (setup[0]<<16) | 0x0008;
				csr15val = (setup[1]<<16) | 0x0008;
				if (dev->if_port <= 4)
					csr14val = t21142_csr14[dev->if_port];
				if (startup) {
					iowrite32(0, ioaddr + CSR13);
					iowrite32(csr14val, ioaddr + CSR14);
				}
				iowrite32(csr15dir, ioaddr + CSR15);	/* Direction */
				iowrite32(csr15val, ioaddr + CSR15);	/* Data */
				if (startup) iowrite32(csr13val, ioaddr + CSR13);
			}
			if (tulip_debug > 1)
				netdev_dbg(dev, "Setting CSR15 to %08x/%08x\n",
					   csr15dir, csr15val);
			if (mleaf->type == 4)
				new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18);
			else
				new_csr6 = 0x82420000;
			break;
		}
		case 1: case 3: {
			int phy_num = p[0];
			int init_length = p[1];
			u16 *misc_info, tmp_info;

			dev->if_port = 11;
			new_csr6 = 0x020E0000;
			if (mleaf->type == 3) {	/* 21142 */
				u16 *init_sequence = (u16*)(p+2);
				u16 *reset_sequence = &((u16*)(p+3))[init_length];
				int reset_length = p[2 + init_length*2];
				misc_info = reset_sequence + reset_length;
				if (startup) {
					int timeout = 10;	/* max 1 ms */
					for (i = 0; i < reset_length; i++)
						iowrite32(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15);

					/* flush posted writes */
					ioread32(ioaddr + CSR15);

					/* Sect 3.10.3 in DP83840A.pdf (p39) */
					udelay(500);

					/* Section 4.2 in DP83840A.pdf (p43) */
					/* and IEEE 802.3 "22.2.4.1.1 Reset" */
					while (timeout-- &&
						(tulip_mdio_read (dev, phy_num, MII_BMCR) & BMCR_RESET))
						udelay(100);
				}
				for (i = 0; i < init_length; i++)
					iowrite32(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15);

				ioread32(ioaddr + CSR15);	/* flush posted writes */
			} else {
				u8 *init_sequence = p + 2;
				u8 *reset_sequence = p + 3 + init_length;
				int reset_length = p[2 + init_length];
				misc_info = (u16*)(reset_sequence + reset_length);
				if (startup) {
					int timeout = 10;	/* max 1 ms */
					iowrite32(mtable->csr12dir | 0x100, ioaddr + CSR12);
					for (i = 0; i < reset_length; i++)
						iowrite32(reset_sequence[i], ioaddr + CSR12);

					/* flush posted writes */
					ioread32(ioaddr + CSR12);

					/* Sect 3.10.3 in DP83840A.pdf (p39) */
					udelay(500);

					/* Section 4.2 in DP83840A.pdf (p43) */
					/* and IEEE 802.3 "22.2.4.1.1 Reset" */
					while (timeout-- &&
						(tulip_mdio_read (dev, phy_num, MII_BMCR) & BMCR_RESET))
						udelay(100);
				}
				for (i = 0; i < init_length; i++)
					iowrite32(init_sequence[i], ioaddr + CSR12);

				ioread32(ioaddr + CSR12);	/* flush posted writes */
			}

			tmp_info = get_u16(&misc_info[1]);
			if (tmp_info)
				tp->advertising[phy_num] = tmp_info | 1;
			if (tmp_info && startup < 2) {
				if (tp->mii_advertise == 0)
					tp->mii_advertise = tp->advertising[phy_num];
				if (tulip_debug > 1)
					netdev_dbg(dev, " Advertising %04x on MII %d\n",
						   tp->mii_advertise,
						   tp->phys[phy_num]);
				tulip_mdio_write(dev, tp->phys[phy_num], 4, tp->mii_advertise);
			}
			break;
		}
		case 5: case 6: {
			u16 setup[5];

			new_csr6 = 0; /* FIXME */

			for (i = 0; i < 5; i++)
				setup[i] = get_u16(&p[i*2 + 1]);

			if (startup && mtable->has_reset) {
				struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset];
				unsigned char *rst = rleaf->leafdata;
				if (tulip_debug > 1)
					netdev_dbg(dev, "Resetting the transceiver\n");
				for (i = 0; i < rst[0]; i++)
					iowrite32(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15);
			}

			break;
		}
		default:
			netdev_dbg(dev, " Invalid media table selection %d\n",
				   mleaf->type);
			new_csr6 = 0x020E0000;
		}
		if (tulip_debug > 1)
			netdev_dbg(dev, "Using media type %s, CSR12 is %02x\n",
				   medianame[dev->if_port],
				   ioread32(ioaddr + CSR12) & 0xff);
	} else if (tp->chip_id == LC82C168) {
		if (startup && ! tp->medialock)
			dev->if_port = tp->mii_cnt ? 11 : 0;
		if (tulip_debug > 1)
			netdev_dbg(dev, "PNIC PHY status is %3.3x, media %s\n",
				   ioread32(ioaddr + 0xB8),
				   medianame[dev->if_port]);
		if (tp->mii_cnt) {
			new_csr6 = 0x810C0000;
			iowrite32(0x0001, ioaddr + CSR15);
			iowrite32(0x0201B07A, ioaddr + 0xB8);
		} else if (startup) {
			/* Start with 10mbps to do autonegotiation. */
			iowrite32(0x32, ioaddr + CSR12);
			new_csr6 = 0x00420000;
			iowrite32(0x0001B078, ioaddr + 0xB8);
			iowrite32(0x0201B078, ioaddr + 0xB8);
		} else if (dev->if_port == 3  ||  dev->if_port == 5) {
			iowrite32(0x33, ioaddr + CSR12);
			new_csr6 = 0x01860000;
			/* Trigger autonegotiation. */
			iowrite32(startup ? 0x0201F868 : 0x0001F868, ioaddr + 0xB8);
		} else {
			iowrite32(0x32, ioaddr + CSR12);
			new_csr6 = 0x00420000;
			iowrite32(0x1F078, ioaddr + 0xB8);
		}
	} else {					/* Unknown chip type with no media table. */
		if (tp->default_port == 0)
			dev->if_port = tp->mii_cnt ? 11 : 3;
		if (tulip_media_cap[dev->if_port] & MediaIsMII) {
			new_csr6 = 0x020E0000;
		} else if (tulip_media_cap[dev->if_port] & MediaIsFx) {
			new_csr6 = 0x02860000;
		} else
			new_csr6 = 0x03860000;
		if (tulip_debug > 1)
			netdev_dbg(dev, "No media description table, assuming %s transceiver, CSR12 %02x\n",
				   medianame[dev->if_port],
				   ioread32(ioaddr + CSR12));
	}

	tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0);

	mdelay(1);
}

/*
  Check the MII negotiated duplex and change the CSR6 setting if
  required.
  Return 0 if everything is OK.
  Return < 0 if the transceiver is missing or has no link beat.
  */
int tulip_check_duplex(struct net_device *dev)
{
	struct tulip_private *tp = netdev_priv(dev);
	unsigned int bmsr, lpa, negotiated, new_csr6;

	bmsr = tulip_mdio_read(dev, tp->phys[0], MII_BMSR);
	lpa = tulip_mdio_read(dev, tp->phys[0], MII_LPA);
	if (tulip_debug > 1)
		dev_info(&dev->dev, "MII status %04x, Link partner report %04x\n",
			 bmsr, lpa);
	if (bmsr == 0xffff)
		return -2;
	if ((bmsr & BMSR_LSTATUS) == 0) {
		int new_bmsr = tulip_mdio_read(dev, tp->phys[0], MII_BMSR);
		if ((new_bmsr & BMSR_LSTATUS) == 0) {
			if (tulip_debug  > 1)
				dev_info(&dev->dev,
					 "No link beat on the MII interface, status %04x\n",
					 new_bmsr);
			return -1;
		}
	}
	negotiated = lpa & tp->advertising[0];
	tp->full_duplex = mii_duplex(tp->full_duplex_lock, negotiated);

	new_csr6 = tp->csr6;

	if (negotiated & LPA_100) new_csr6 &= ~TxThreshold;
	else			  new_csr6 |= TxThreshold;
	if (tp->full_duplex) new_csr6 |= FullDuplex;
	else		     new_csr6 &= ~FullDuplex;

	if (new_csr6 != tp->csr6) {
		tp->csr6 = new_csr6;
		tulip_restart_rxtx(tp);

		if (tulip_debug > 0)
			dev_info(&dev->dev,
				 "Setting %s-duplex based on MII#%d link partner capability of %04x\n",
				 tp->full_duplex ? "full" : "half",
				 tp->phys[0], lpa);
		return 1;
	}

	return 0;
}

void __devinit tulip_find_mii (struct net_device *dev, int board_idx)
{
	struct tulip_private *tp = netdev_priv(dev);
	int phyn, phy_idx = 0;
	int mii_reg0;
	int mii_advert;
	unsigned int to_advert, new_bmcr, ane_switch;

	/* Find the connected MII xcvrs.
	   Doing this in open() would allow detecting external xcvrs later,
	   but takes much time. */
	for (phyn = 1; phyn <= 32 && phy_idx < sizeof (tp->phys); phyn++) {
		int phy = phyn & 0x1f;
		int mii_status = tulip_mdio_read (dev, phy, MII_BMSR);
		if ((mii_status & 0x8301) == 0x8001 ||
		    ((mii_status & BMSR_100BASE4) == 0 &&
		     (mii_status & 0x7800) != 0)) {
			/* preserve Becker logic, gain indentation level */
		} else {
			continue;
		}

		mii_reg0 = tulip_mdio_read (dev, phy, MII_BMCR);
		mii_advert = tulip_mdio_read (dev, phy, MII_ADVERTISE);
		ane_switch = 0;

		/* if not advertising at all, gen an
		 * advertising value from the capability
		 * bits in BMSR
		 */
		if ((mii_advert & ADVERTISE_ALL) == 0) {
			unsigned int tmpadv = tulip_mdio_read (dev, phy, MII_BMSR);
			mii_advert = ((tmpadv >> 6) & 0x3e0) | 1;
		}

		if (tp->mii_advertise) {
			tp->advertising[phy_idx] =
			to_advert = tp->mii_advertise;
		} else if (tp->advertising[phy_idx]) {
			to_advert = tp->advertising[phy_idx];
		} else {
			tp->advertising[phy_idx] =
			tp->mii_advertise =
			to_advert = mii_advert;
		}

		tp->phys[phy_idx++] = phy;

		pr_info("tulip%d:  MII transceiver #%d config %04x status %04x advertising %04x\n",
			board_idx, phy, mii_reg0, mii_status, mii_advert);

		/* Fixup for DLink with miswired PHY. */
		if (mii_advert != to_advert) {
			pr_debug("tulip%d:  Advertising %04x on PHY %d, previously advertising %04x\n",
				 board_idx, to_advert, phy, mii_advert);
			tulip_mdio_write (dev, phy, 4, to_advert);
		}

		/* Enable autonegotiation: some boards default to off. */
		if (tp->default_port == 0) {
			new_bmcr = mii_reg0 | BMCR_ANENABLE;
			if (new_bmcr != mii_reg0) {
				new_bmcr |= BMCR_ANRESTART;
				ane_switch = 1;
			}
		}
		/* ...or disable nway, if forcing media */
		else {
			new_bmcr = mii_reg0 & ~BMCR_ANENABLE;
			if (new_bmcr != mii_reg0)
				ane_switch = 1;
		}

		/* clear out bits we never want at this point */
		new_bmcr &= ~(BMCR_CTST | BMCR_FULLDPLX | BMCR_ISOLATE |
			      BMCR_PDOWN | BMCR_SPEED100 | BMCR_LOOPBACK |
			      BMCR_RESET);

		if (tp->full_duplex)
			new_bmcr |= BMCR_FULLDPLX;
		if (tulip_media_cap[tp->default_port] & MediaIs100)
			new_bmcr |= BMCR_SPEED100;

		if (new_bmcr != mii_reg0) {
			/* some phys need the ANE switch to
			 * happen before forced media settings
			 * will "take."  However, we write the
			 * same value twice in order not to
			 * confuse the sane phys.
			 */
			if (ane_switch) {
				tulip_mdio_write (dev, phy, MII_BMCR, new_bmcr);
				udelay (10);
			}
			tulip_mdio_write (dev, phy, MII_BMCR, new_bmcr);
		}
	}
	tp->mii_cnt = phy_idx;
	if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) {
		pr_info("tulip%d: ***WARNING***: No MII transceiver found!\n",
			board_idx);
		tp->phys[0] = 1;
	}
}