eth.c 13.9 KB
Newer Older
W
wdenk 已提交
1
/*
W
wdenk 已提交
2
 * (C) Copyright 2001-2004
W
wdenk 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <command.h>
#include <net.h>
27
#include <miiphy.h>
W
wdenk 已提交
28

29
#if defined(CONFIG_CMD_NET) && defined(CONFIG_NET_MULTI)
W
wdenk 已提交
30 31 32 33 34

#ifdef CFG_GT_6426x
extern int gt6426x_eth_initialize(bd_t *bis);
#endif

W
wdenk 已提交
35 36
extern int au1x00_enet_initialize(bd_t*);
extern int dc21x4x_initialize(bd_t*);
37
extern int e1000_initialize(bd_t*);
W
wdenk 已提交
38
extern int eepro100_initialize(bd_t*);
W
wdenk 已提交
39
extern int eth_3com_initialize(bd_t*);
W
wdenk 已提交
40
extern int fec_initialize(bd_t*);
41
extern int inca_switch_initialize(bd_t*);
42
extern int mpc5xxx_fec_initialize(bd_t*);
43
extern int mpc512x_fec_initialize(bd_t*);
W
wdenk 已提交
44
extern int mpc8220_fec_initialize(bd_t*);
W
wdenk 已提交
45 46 47 48 49 50 51 52
extern int mv6436x_eth_initialize(bd_t *);
extern int mv6446x_eth_initialize(bd_t *);
extern int natsemi_initialize(bd_t*);
extern int ns8382x_initialize(bd_t*);
extern int pcnet_initialize(bd_t*);
extern int plb2800_eth_initialize(bd_t*);
extern int ppc_4xx_eth_initialize(bd_t *);
extern int rtl8139_initialize(bd_t*);
W
wdenk 已提交
53
extern int rtl8169_initialize(bd_t*);
W
wdenk 已提交
54
extern int scc_initialize(bd_t*);
W
wdenk 已提交
55
extern int skge_initialize(bd_t*);
56
extern int tsi108_eth_initialize(bd_t*);
57
extern int uli526x_initialize(bd_t *);
58
extern int tsec_initialize(bd_t*, int, char *);
W
Wolfgang Denk 已提交
59
extern int npe_initialize(bd_t *);
D
Dave Liu 已提交
60
extern int uec_initialize(int);
61
extern int bfin_EMAC_initialize(bd_t *);
H
Haavard Skinnemoen 已提交
62
extern int atstk1000_eth_initialize(bd_t *);
63
extern int atngw100_eth_initialize(bd_t *);
64
extern int mcffec_initialize(bd_t*);
65
extern int mcdmafec_initialize(bd_t*);
66
extern int at91cap9_eth_initialize(bd_t *);
W
wdenk 已提交
67

68 69 70 71 72 73 74 75 76 77 78
#ifdef CONFIG_API
extern void (*push_packet)(volatile void *, int);

static struct {
	uchar data[PKTSIZE];
	int length;
} eth_rcv_bufs[PKTBUFSRX];

static unsigned int eth_rcv_current = 0, eth_rcv_last = 0;
#endif

W
wdenk 已提交
79 80 81 82 83 84 85
static struct eth_device *eth_devices, *eth_current;

struct eth_device *eth_get_dev(void)
{
	return eth_current;
}

M
Marian Balakowicz 已提交
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
struct eth_device *eth_get_dev_by_name(char *devname)
{
	struct eth_device *dev, *target_dev;

	if (!eth_devices)
		return NULL;

	dev = eth_devices;
	target_dev = NULL;
	do {
		if (strcmp(devname, dev->name) == 0) {
			target_dev = dev;
			break;
		}
		dev = dev->next;
	} while (dev != eth_devices);

	return target_dev;
}

W
wdenk 已提交
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
int eth_get_dev_index (void)
{
	struct eth_device *dev;
	int num = 0;

	if (!eth_devices) {
		return (-1);
	}

	for (dev = eth_devices; dev; dev = dev->next) {
		if (dev == eth_current)
			break;
		++num;
	}

	if (dev) {
		return (num);
	}

	return (0);
}

int eth_register(struct eth_device* dev)
{
	struct eth_device *d;

	if (!eth_devices) {
		eth_current = eth_devices = dev;
134 135 136 137 138 139 140 141
#ifdef CONFIG_NET_MULTI
		/* update current ethernet name */
		{
			char *act = getenv("ethact");
			if (act == NULL || strcmp(act, eth_current->name) != 0)
				setenv("ethact", eth_current->name);
		}
#endif
W
wdenk 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154
	} else {
		for (d=eth_devices; d->next!=eth_devices; d=d->next);
		d->next = dev;
	}

	dev->state = ETH_STATE_INIT;
	dev->next  = eth_devices;

	return 0;
}

int eth_initialize(bd_t *bis)
{
155 156
	char enetvar[32];
	unsigned char env_enetaddr[6];
W
wdenk 已提交
157 158 159 160 161 162
	int i, eth_number = 0;
	char *tmp, *end;

	eth_devices = NULL;
	eth_current = NULL;

163
	show_boot_progress (64);
164
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
165 166 167
	miiphy_init();
#endif

168
#if defined(CONFIG_DB64360) || defined(CONFIG_CPCI750)
W
wdenk 已提交
169 170
	mv6436x_eth_initialize(bis);
#endif
171
#if defined(CONFIG_DB64460) || defined(CONFIG_P3Mx)
W
wdenk 已提交
172 173
	mv6446x_eth_initialize(bis);
#endif
W
Wolfgang Denk 已提交
174
#if defined(CONFIG_4xx) && !defined(CONFIG_IOP480) && !defined(CONFIG_AP1000)
W
wdenk 已提交
175
	ppc_4xx_eth_initialize(bis);
176
#endif
177 178 179
#ifdef CONFIG_INCA_IP_SWITCH
	inca_switch_initialize(bis);
#endif
W
wdenk 已提交
180 181 182
#ifdef CONFIG_PLB2800_ETHER
	plb2800_eth_initialize(bis);
#endif
183 184 185 186 187 188
#ifdef SCC_ENET
	scc_initialize(bis);
#endif
#if defined(CONFIG_MPC5xxx_FEC)
	mpc5xxx_fec_initialize(bis);
#endif
189 190 191
#if defined(CONFIG_MPC512x_FEC)
	mpc512x_fec_initialize (bis);
#endif
W
wdenk 已提交
192
#if defined(CONFIG_MPC8220_FEC)
W
wdenk 已提交
193 194
	mpc8220_fec_initialize(bis);
#endif
195 196 197
#if defined(CONFIG_SK98)
	skge_initialize(bis);
#endif
198 199
#if defined(CONFIG_TSEC1)
	tsec_initialize(bis, 0, CONFIG_TSEC1_NAME);
W
wdenk 已提交
200
#endif
201 202
#if defined(CONFIG_TSEC2)
	tsec_initialize(bis, 1, CONFIG_TSEC2_NAME);
W
wdenk 已提交
203 204
#endif
#if defined(CONFIG_MPC85XX_FEC)
205 206
	tsec_initialize(bis, 2, CONFIG_MPC85XX_FEC_NAME);
#else
207 208
#    if defined(CONFIG_TSEC3)
	tsec_initialize(bis, 2, CONFIG_TSEC3_NAME);
209
#    endif
210 211
#    if defined(CONFIG_TSEC4)
	tsec_initialize(bis, 3, CONFIG_TSEC4_NAME);
212
#    endif
213
#endif
D
Dave Liu 已提交
214 215 216 217 218 219
#if defined(CONFIG_UEC_ETH1)
	uec_initialize(0);
#endif
#if defined(CONFIG_UEC_ETH2)
	uec_initialize(1);
#endif
220 221 222
#if defined(CONFIG_UEC_ETH3)
	uec_initialize(2);
#endif
223 224 225
#if defined(CONFIG_UEC_ETH4)
	uec_initialize(3);
#endif
226

227 228 229
#if defined(FEC_ENET) || defined(CONFIG_ETHER_ON_FCC)
	fec_initialize(bis);
#endif
230 231 232
#if defined(CONFIG_AU1X00)
	au1x00_enet_initialize(bis);
#endif
W
Wolfgang Denk 已提交
233 234 235
#if defined(CONFIG_IXP4XX_NPE)
	npe_initialize(bis);
#endif
236 237 238
#ifdef CONFIG_E1000
	e1000_initialize(bis);
#endif
W
wdenk 已提交
239 240 241 242 243 244
#ifdef CONFIG_EEPRO100
	eepro100_initialize(bis);
#endif
#ifdef CONFIG_TULIP
	dc21x4x_initialize(bis);
#endif
W
wdenk 已提交
245 246 247
#ifdef CONFIG_3COM
	eth_3com_initialize(bis);
#endif
W
wdenk 已提交
248 249 250 251 252 253 254 255 256 257 258 259
#ifdef CONFIG_PCNET
	pcnet_initialize(bis);
#endif
#ifdef CFG_GT_6426x
	gt6426x_eth_initialize(bis);
#endif
#ifdef CONFIG_NATSEMI
	natsemi_initialize(bis);
#endif
#ifdef CONFIG_NS8382X
	ns8382x_initialize(bis);
#endif
260 261
#if defined(CONFIG_TSI108_ETH)
	tsi108_eth_initialize(bis);
W
wdenk 已提交
262
#endif
263 264 265
#if defined(CONFIG_ULI526X)
	uli526x_initialize(bis);
#endif
W
wdenk 已提交
266 267 268
#if defined(CONFIG_RTL8139)
	rtl8139_initialize(bis);
#endif
W
wdenk 已提交
269 270 271
#if defined(CONFIG_RTL8169)
	rtl8169_initialize(bis);
#endif
272 273 274
#if defined(CONFIG_BF537)
	bfin_EMAC_initialize(bis);
#endif
H
Haavard Skinnemoen 已提交
275 276 277
#if defined(CONFIG_ATSTK1000)
	atstk1000_eth_initialize(bis);
#endif
278 279 280
#if defined(CONFIG_ATNGW100)
	atngw100_eth_initialize(bis);
#endif
T
TsiChungLiew 已提交
281 282 283
#if defined(CONFIG_MCFFEC)
	mcffec_initialize(bis);
#endif
284 285 286
#if defined(CONFIG_FSLDMAFEC)
	mcdmafec_initialize(bis);
#endif
287 288 289
#if defined(CONFIG_AT91CAP9)
	at91cap9_eth_initialize(bis);
#endif
W
wdenk 已提交
290 291 292

	if (!eth_devices) {
		puts ("No ethernet found.\n");
293
		show_boot_progress (-64);
W
wdenk 已提交
294 295 296 297
	} else {
		struct eth_device *dev = eth_devices;
		char *ethprime = getenv ("ethprime");

298
		show_boot_progress (65);
W
wdenk 已提交
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
		do {
			if (eth_number)
				puts (", ");

			printf("%s", dev->name);

			if (ethprime && strcmp (dev->name, ethprime) == 0) {
				eth_current = dev;
				puts (" [PRIME]");
			}

			sprintf(enetvar, eth_number ? "eth%daddr" : "ethaddr", eth_number);
			tmp = getenv (enetvar);

			for (i=0; i<6; i++) {
				env_enetaddr[i] = tmp ? simple_strtoul(tmp, &end, 16) : 0;
				if (tmp)
					tmp = (*end) ? end+1 : end;
			}

			if (memcmp(env_enetaddr, "\0\0\0\0\0\0", 6)) {
				if (memcmp(dev->enetaddr, "\0\0\0\0\0\0", 6) &&
				    memcmp(dev->enetaddr, env_enetaddr, 6))
				{
323 324 325
					printf ("\nWarning: %s MAC addresses don't match:\n",
						dev->name);
					printf ("Address in SROM is         "
W
wdenk 已提交
326 327 328 329
					       "%02X:%02X:%02X:%02X:%02X:%02X\n",
					       dev->enetaddr[0], dev->enetaddr[1],
					       dev->enetaddr[2], dev->enetaddr[3],
					       dev->enetaddr[4], dev->enetaddr[5]);
330
					printf ("Address in environment is  "
W
wdenk 已提交
331 332 333 334 335 336 337 338 339 340 341 342 343
					       "%02X:%02X:%02X:%02X:%02X:%02X\n",
					       env_enetaddr[0], env_enetaddr[1],
					       env_enetaddr[2], env_enetaddr[3],
					       env_enetaddr[4], env_enetaddr[5]);
				}

				memcpy(dev->enetaddr, env_enetaddr, 6);
			}

			eth_number++;
			dev = dev->next;
		} while(dev != eth_devices);

344 345 346 347 348 349 350 351 352 353
#ifdef CONFIG_NET_MULTI
		/* update current ethernet name */
		if (eth_current) {
			char *act = getenv("ethact");
			if (act == NULL || strcmp(act, eth_current->name) != 0)
				setenv("ethact", eth_current->name);
		} else
			setenv("ethact", NULL);
#endif

W
wdenk 已提交
354 355 356 357 358 359 360 361 362 363 364 365
		putc ('\n');
	}

	return eth_number;
}

void eth_set_enetaddr(int num, char *addr) {
	struct eth_device *dev;
	unsigned char enetaddr[6];
	char *end;
	int i;

W
wdenk 已提交
366 367
	debug ("eth_set_enetaddr(num=%d, addr=%s)\n", num, addr);

W
wdenk 已提交
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
	if (!eth_devices)
		return;

	for (i=0; i<6; i++) {
		enetaddr[i] = addr ? simple_strtoul(addr, &end, 16) : 0;
		if (addr)
			addr = (*end) ? end+1 : end;
	}

	dev = eth_devices;
	while(num-- > 0) {
		dev = dev->next;

		if (dev == eth_devices)
			return;
	}

W
wdenk 已提交
385 386 387
	debug ( "Setting new HW address on %s\n"
		"New Address is             %02X:%02X:%02X:%02X:%02X:%02X\n",
		dev->name,
W
Wolfgang Denk 已提交
388 389 390
		enetaddr[0], enetaddr[1],
		enetaddr[2], enetaddr[3],
		enetaddr[4], enetaddr[5]);
W
wdenk 已提交
391 392 393

	memcpy(dev->enetaddr, enetaddr, 6);
}
D
David Updegraff 已提交
394 395 396
#ifdef CONFIG_MCAST_TFTP
/* Multicast.
 * mcast_addr: multicast ipaddr from which multicast Mac is made
397
 * join: 1=join, 0=leave.
D
David Updegraff 已提交
398 399 400 401
 */
int eth_mcast_join( IPaddr_t mcast_ip, u8 join)
{
 u8 mcast_mac[6];
402
	if (!eth_current || !eth_current->mcast)
D
David Updegraff 已提交
403 404 405 406 407 408 409 410 411 412
		return -1;
	mcast_mac[5] = htonl(mcast_ip) & 0xff;
	mcast_mac[4] = (htonl(mcast_ip)>>8) & 0xff;
	mcast_mac[3] = (htonl(mcast_ip)>>16) & 0x7f;
	mcast_mac[2] = 0x5e;
	mcast_mac[1] = 0x0;
	mcast_mac[0] = 0x1;
	return eth_current->mcast(eth_current, mcast_mac, join);
}

413 414
/* the 'way' for ethernet-CRC-32. Spliced in from Linux lib/crc32.c
 * and this is the ethernet-crc method needed for TSEC -- and perhaps
D
David Updegraff 已提交
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
 * some other adapter -- hash tables
 */
#define CRCPOLY_LE 0xedb88320
u32 ether_crc (size_t len, unsigned char const *p)
{
	int i;
	u32 crc;
	crc = ~0;
	while (len--) {
		crc ^= *p++;
		for (i = 0; i < 8; i++)
			crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
	}
	/* an reverse the bits, cuz of way they arrive -- last-first */
	crc = (crc >> 16) | (crc << 16);
	crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00);
	crc = (crc >> 4 & 0x0f0f0f0f) | (crc << 4 & 0xf0f0f0f0);
	crc = (crc >> 2 & 0x33333333) | (crc << 2 & 0xcccccccc);
	crc = (crc >> 1 & 0x55555555) | (crc << 1 & 0xaaaaaaaa);
	return crc;
}

#endif

W
wdenk 已提交
439 440 441 442 443

int eth_init(bd_t *bis)
{
	struct eth_device* old_current;

444 445
	if (!eth_current) {
		puts ("No ethernet found.\n");
446
		return -1;
447
	}
W
wdenk 已提交
448 449 450

	old_current = eth_current;
	do {
W
wdenk 已提交
451
		debug ("Trying %s\n", eth_current->name);
W
wdenk 已提交
452

B
Ben Warren 已提交
453
		if (eth_current->init(eth_current,bis) >= 0) {
W
wdenk 已提交
454 455
			eth_current->state = ETH_STATE_ACTIVE;

456
			return 0;
W
wdenk 已提交
457
		}
W
wdenk 已提交
458
		debug  ("FAIL\n");
W
wdenk 已提交
459 460 461 462

		eth_try_another(0);
	} while (old_current != eth_current);

463
	return -1;
W
wdenk 已提交
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
}

void eth_halt(void)
{
	if (!eth_current)
		return;

	eth_current->halt(eth_current);

	eth_current->state = ETH_STATE_PASSIVE;
}

int eth_send(volatile void *packet, int length)
{
	if (!eth_current)
		return -1;

	return eth_current->send(eth_current, packet, length);
}

int eth_rx(void)
{
	if (!eth_current)
		return -1;

	return eth_current->recv(eth_current);
}

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
#ifdef CONFIG_API
static void eth_save_packet(volatile void *packet, int length)
{
	volatile char *p = packet;
	int i;

	if ((eth_rcv_last+1) % PKTBUFSRX == eth_rcv_current)
		return;

	if (PKTSIZE < length)
		return;

	for (i = 0; i < length; i++)
		eth_rcv_bufs[eth_rcv_last].data[i] = p[i];

	eth_rcv_bufs[eth_rcv_last].length = length;
	eth_rcv_last = (eth_rcv_last + 1) % PKTBUFSRX;
}

int eth_receive(volatile void *packet, int length)
{
	volatile char *p = packet;
	void *pp = push_packet;
	int i;

	if (eth_rcv_current == eth_rcv_last) {
		push_packet = eth_save_packet;
		eth_rx();
		push_packet = pp;

		if (eth_rcv_current == eth_rcv_last)
			return -1;
	}

	if (length < eth_rcv_bufs[eth_rcv_current].length)
		return -1;

	length = eth_rcv_bufs[eth_rcv_current].length;

	for (i = 0; i < length; i++)
		p[i] = eth_rcv_bufs[eth_rcv_current].data[i];

	eth_rcv_current = (eth_rcv_current + 1) % PKTBUFSRX;
	return length;
}
#endif /* CONFIG_API */

W
wdenk 已提交
539 540 541
void eth_try_another(int first_restart)
{
	static struct eth_device *first_failed = NULL;
542 543 544 545 546 547 548 549 550
	char *ethrotate;

	/*
	 * Do not rotate between network interfaces when
	 * 'ethrotate' variable is set to 'no'.
	 */
	if (((ethrotate = getenv ("ethrotate")) != NULL) &&
	    (strcmp(ethrotate, "no") == 0))
		return;
W
wdenk 已提交
551 552 553 554

	if (!eth_current)
		return;

555
	if (first_restart) {
W
wdenk 已提交
556 557 558 559 560
		first_failed = eth_current;
	}

	eth_current = eth_current->next;

561 562 563 564 565 566 567 568 569
#ifdef CONFIG_NET_MULTI
	/* update current ethernet name */
	{
		char *act = getenv("ethact");
		if (act == NULL || strcmp(act, eth_current->name) != 0)
			setenv("ethact", eth_current->name);
	}
#endif

570
	if (first_failed == eth_current) {
W
wdenk 已提交
571 572 573 574
		NetRestartWrap = 1;
	}
}

575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
#ifdef CONFIG_NET_MULTI
void eth_set_current(void)
{
	char *act;
	struct eth_device* old_current;

	if (!eth_current)	/* XXX no current */
		return;

	act = getenv("ethact");
	if (act != NULL) {
		old_current = eth_current;
		do {
			if (strcmp(eth_current->name, act) == 0)
				return;
			eth_current = eth_current->next;
		} while (old_current != eth_current);
	}

	setenv("ethact", eth_current->name);
}
#endif

598 599 600 601
char *eth_get_name (void)
{
	return (eth_current ? eth_current->name : "unknown");
}
602
#elif defined(CONFIG_CMD_NET) && !defined(CONFIG_NET_MULTI)
M
Marian Balakowicz 已提交
603 604 605 606 607

extern int at91rm9200_miiphy_initialize(bd_t *bis);
extern int emac4xx_miiphy_initialize(bd_t *bis);
extern int mcf52x2_miiphy_initialize(bd_t *bis);
extern int ns7520_miiphy_initialize(bd_t *bis);
608 609
extern int dm644x_eth_miiphy_initialize(bd_t *bis);

M
Marian Balakowicz 已提交
610 611 612

int eth_initialize(bd_t *bis)
{
613
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
614 615 616
	miiphy_init();
#endif

M
Marian Balakowicz 已提交
617 618 619 620 621 622 623 624 625 626 627 628
#if defined(CONFIG_AT91RM9200)
	at91rm9200_miiphy_initialize(bis);
#endif
#if defined(CONFIG_4xx) && !defined(CONFIG_IOP480) \
	&& !defined(CONFIG_AP1000) && !defined(CONFIG_405)
	emac4xx_miiphy_initialize(bis);
#endif
#if defined(CONFIG_MCF52x2)
	mcf52x2_miiphy_initialize(bis);
#endif
#if defined(CONFIG_NETARM)
	ns7520_miiphy_initialize(bis);
629 630 631
#endif
#if defined(CONFIG_DRIVER_TI_EMAC)
	dm644x_eth_miiphy_initialize(bis);
M
Marian Balakowicz 已提交
632 633 634
#endif
	return 0;
}
W
wdenk 已提交
635
#endif