eth.c 10.6 KB
Newer Older
W
wdenk 已提交
1
/*
2
 * (C) Copyright 2001-2010
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>
A
Andy Fleming 已提交
28
#include <phy.h>
W
wdenk 已提交
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
void eth_parse_enetaddr(const char *addr, uchar *enetaddr)
{
	char *end;
	int i;

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

int eth_getenv_enetaddr(char *name, uchar *enetaddr)
{
	eth_parse_enetaddr(getenv(name), enetaddr);
	return is_valid_ether_addr(enetaddr);
}

int eth_setenv_enetaddr(char *name, const uchar *enetaddr)
{
	char buf[20];

	sprintf(buf, "%pM", enetaddr);

	return setenv(name, buf);
}
56

57 58
int eth_getenv_enetaddr_by_index(const char *base_name, int index,
				 uchar *enetaddr)
59 60
{
	char enetvar[32];
61
	sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
62 63
	return eth_getenv_enetaddr(enetvar, enetaddr);
}
64

65 66 67 68 69 70 71 72
static int eth_mac_skip(int index)
{
	char enetvar[15];
	char *skip_state;
	sprintf(enetvar, index ? "eth%dmacskip" : "ethmacskip", index);
	return ((skip_state = getenv(enetvar)) != NULL);
}

73 74 75 76 77 78 79 80
/*
 * CPU and board-specific Ethernet initializations.  Aliased function
 * signals caller to move on
 */
static int __def_eth_init(bd_t *bis)
{
	return -1;
}
81 82
int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
83

W
wdenk 已提交
84 85
extern int mv6436x_eth_initialize(bd_t *);
extern int mv6446x_eth_initialize(bd_t *);
W
wdenk 已提交
86

87 88 89 90 91 92 93 94 95 96 97
#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 已提交
98 99 100 101 102 103 104
static struct eth_device *eth_devices, *eth_current;

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

105
struct eth_device *eth_get_dev_by_name(const char *devname)
M
Marian Balakowicz 已提交
106 107 108
{
	struct eth_device *dev, *target_dev;

109 110
	BUG_ON(devname == NULL);

M
Marian Balakowicz 已提交
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
	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;
}

A
Andy Fleming 已提交
127 128 129 130 131 132 133 134 135 136
struct eth_device *eth_get_dev_by_index(int index)
{
	struct eth_device *dev, *target_dev;

	if (!eth_devices)
		return NULL;

	dev = eth_devices;
	target_dev = NULL;
	do {
M
Michael Walle 已提交
137
		if (dev->index == index) {
A
Andy Fleming 已提交
138 139 140 141 142 143 144 145 146
			target_dev = dev;
			break;
		}
		dev = dev->next;
	} while (dev != eth_devices);

	return target_dev;
}

W
wdenk 已提交
147 148
int eth_get_dev_index (void)
{
M
Michael Walle 已提交
149 150
	if (!eth_current) {
		return -1;
W
wdenk 已提交
151 152
	}

M
Michael Walle 已提交
153
	return eth_current->index;
W
wdenk 已提交
154 155
}

156
static void eth_current_changed(void)
W
wdenk 已提交
157
{
M
Mike Frysinger 已提交
158 159 160 161 162
	char *act = getenv("ethact");
	/* update current ethernet name */
	if (eth_current) {
		if (act == NULL || strcmp(act, eth_current->name) != 0)
			setenv("ethact", eth_current->name);
163
	}
M
Mike Frysinger 已提交
164 165 166 167 168 169
	/*
	 * remove the variable completely if there is no active
	 * interface
	 */
	else if (act != NULL)
		setenv("ethact", NULL);
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
int eth_write_hwaddr(struct eth_device *dev, const char *base_name,
		   int eth_number)
{
	unsigned char env_enetaddr[6];
	int ret = 0;

	if (!eth_getenv_enetaddr_by_index(base_name, eth_number, env_enetaddr))
		return -1;

	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)) {
			printf("\nWarning: %s MAC addresses don't match:\n",
				dev->name);
			printf("Address in SROM is         %pM\n",
				dev->enetaddr);
			printf("Address in environment is  %pM\n",
				env_enetaddr);
		}

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

	if (dev->write_hwaddr &&
		!eth_mac_skip(eth_number) &&
		is_valid_ether_addr(dev->enetaddr)) {
		ret = dev->write_hwaddr(dev);
	}

	return ret;
}

204 205 206
int eth_register(struct eth_device *dev)
{
	struct eth_device *d;
M
Michael Walle 已提交
207
	static int index = 0;
208

209
	assert(strlen(dev->name) < NAMESIZE);
210

211 212 213
	if (!eth_devices) {
		eth_current = eth_devices = dev;
		eth_current_changed();
W
wdenk 已提交
214
	} else {
215 216
		for (d=eth_devices; d->next!=eth_devices; d=d->next)
			;
W
wdenk 已提交
217 218 219 220 221
		d->next = dev;
	}

	dev->state = ETH_STATE_INIT;
	dev->next  = eth_devices;
M
Michael Walle 已提交
222
	dev->index = index++;
W
wdenk 已提交
223 224 225 226 227 228

	return 0;
}

int eth_initialize(bd_t *bis)
{
M
Michael Walle 已提交
229
	int num_devices = 0;
W
wdenk 已提交
230 231 232
	eth_devices = NULL;
	eth_current = NULL;

233
	show_boot_progress (64);
234
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
235 236
	miiphy_init();
#endif
A
Andy Fleming 已提交
237 238 239 240 241

#ifdef CONFIG_PHYLIB
	phy_init();
#endif

B
Ben Warren 已提交
242 243 244 245 246 247 248 249 250 251 252 253
	/*
	 * If board-specific initialization exists, call it.
	 * If not, call a CPU-specific one
	 */
	if (board_eth_init != __def_eth_init) {
		if (board_eth_init(bis) < 0)
			printf("Board Net Initialization Failed\n");
	} else if (cpu_eth_init != __def_eth_init) {
		if (cpu_eth_init(bis) < 0)
			printf("CPU Net Initialization Failed\n");
	} else
		printf("Net Initialization Skipped\n");
254

255
#if defined(CONFIG_DB64360) || defined(CONFIG_CPCI750)
W
wdenk 已提交
256 257
	mv6436x_eth_initialize(bis);
#endif
258
#if defined(CONFIG_DB64460) || defined(CONFIG_P3Mx)
W
wdenk 已提交
259
	mv6446x_eth_initialize(bis);
260
#endif
W
wdenk 已提交
261 262
	if (!eth_devices) {
		puts ("No ethernet found.\n");
263
		show_boot_progress (-64);
W
wdenk 已提交
264 265 266 267
	} else {
		struct eth_device *dev = eth_devices;
		char *ethprime = getenv ("ethprime");

268
		show_boot_progress (65);
W
wdenk 已提交
269
		do {
M
Michael Walle 已提交
270
			if (dev->index)
W
wdenk 已提交
271 272 273 274 275 276 277 278 279
				puts (", ");

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

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

280 281 282
			if (strchr(dev->name, ' '))
				puts("\nWarning: eth device name has a space!\n");

M
Michael Walle 已提交
283
			if (eth_write_hwaddr(dev, "eth", dev->index))
284
				puts("\nWarning: failed to set MAC address\n");
W
wdenk 已提交
285 286

			dev = dev->next;
M
Michael Walle 已提交
287
			num_devices++;
W
wdenk 已提交
288 289
		} while(dev != eth_devices);

290
		eth_current_changed();
W
wdenk 已提交
291 292 293
		putc ('\n');
	}

M
Michael Walle 已提交
294
	return num_devices;
W
wdenk 已提交
295 296
}

D
David Updegraff 已提交
297 298 299
#ifdef CONFIG_MCAST_TFTP
/* Multicast.
 * mcast_addr: multicast ipaddr from which multicast Mac is made
300
 * join: 1=join, 0=leave.
D
David Updegraff 已提交
301 302 303 304
 */
int eth_mcast_join( IPaddr_t mcast_ip, u8 join)
{
 u8 mcast_mac[6];
305
	if (!eth_current || !eth_current->mcast)
D
David Updegraff 已提交
306 307 308 309 310 311 312 313 314 315
		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);
}

316 317
/* 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 已提交
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
 * 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 已提交
342 343 344

int eth_init(bd_t *bis)
{
345
	struct eth_device *old_current, *dev;
W
wdenk 已提交
346

347 348
	if (!eth_current) {
		puts ("No ethernet found.\n");
349
		return -1;
350
	}
W
wdenk 已提交
351

352 353 354 355 356
	/* Sync environment with network devices */
	dev = eth_devices;
	do {
		uchar env_enetaddr[6];

M
Michael Walle 已提交
357
		if (eth_getenv_enetaddr_by_index("eth", dev->index,
358
						 env_enetaddr))
359 360 361 362 363
			memcpy(dev->enetaddr, env_enetaddr, 6);

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

W
wdenk 已提交
364 365
	old_current = eth_current;
	do {
R
Robin Getz 已提交
366
		debug("Trying %s\n", eth_current->name);
W
wdenk 已提交
367

B
Ben Warren 已提交
368
		if (eth_current->init(eth_current,bis) >= 0) {
W
wdenk 已提交
369 370
			eth_current->state = ETH_STATE_ACTIVE;

371
			return 0;
W
wdenk 已提交
372
		}
R
Robin Getz 已提交
373
		debug("FAIL\n");
W
wdenk 已提交
374 375 376 377

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

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

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);
}

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
#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 已提交
454 455 456
void eth_try_another(int first_restart)
{
	static struct eth_device *first_failed = NULL;
R
Remy Bohmer 已提交
457
	char *ethrotate;
458 459 460 461 462 463 464 465

	/*
	 * 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 已提交
466 467 468 469

	if (!eth_current)
		return;

470
	if (first_restart) {
W
wdenk 已提交
471 472 473 474 475
		first_failed = eth_current;
	}

	eth_current = eth_current->next;

476
	eth_current_changed();
477

478
	if (first_failed == eth_current) {
W
wdenk 已提交
479 480 481 482
		NetRestartWrap = 1;
	}
}

483 484
void eth_set_current(void)
{
H
Heiko Schocher 已提交
485 486
	static char *act = NULL;
	static int  env_changed_id = 0;
487
	struct eth_device* old_current;
H
Heiko Schocher 已提交
488
	int	env_id;
489 490 491 492

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

H
Heiko Schocher 已提交
493 494 495 496 497
	env_id = get_env_id();
	if ((act == NULL) || (env_changed_id != env_id)) {
		act = getenv("ethact");
		env_changed_id = env_id;
	}
498 499 500 501 502 503 504 505 506
	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);
	}

507
	eth_current_changed();
508 509
}

510 511 512 513
char *eth_get_name (void)
{
	return (eth_current ? eth_current->name : "unknown");
}