blob: a545211d1942538cb20aa54719a48c45f42e24ad [file] [log] [blame]
Gary Jennejohnad8678a2008-11-09 12:36:15 +01001/*
2 * (C) Copyright 2008
3 * Gary Jennejohn, DENX Software Engineering GmbH, garyj@denx.de.
4 *
Stefan Roese88fbf932010-04-15 16:07:28 +02005 * Based in part on arch/powerpc/cpu/mpc8260/ether_scc.c.
Gary Jennejohnad8678a2008-11-09 12:36:15 +01006 *
7 * See file CREDITS for list of people who contributed to this
8 * project.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of
13 * the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23 * MA 02111-1307 USA
24 */
25
26#include <common.h>
27#include <malloc.h>
28#include <net.h>
29
30#ifdef CONFIG_KEYMILE_HDLC_ENET
31#ifdef TEST_IT
32#include <command.h>
33#endif
34
35#include "keymile_hdlc_enet.h"
36
37extern char keymile_slot; /* our slot number in the backplane */
38
39/* Allow up to about 50 ms for sending */
40#define TOUT_LOOP 50000
41
42/*
Heiko Schocherc5e84052010-07-20 17:45:02 +020043 * Since, except during initialization, ethact is always HDLC
Gary Jennejohnad8678a2008-11-09 12:36:15 +010044 * while we're in the driver, just use serial_printf() everywhere for
45 * output. This avoids possible conflicts when netconsole is being
46 * used.
47 */
48#define dprintf(fmt, args...) serial_printf(fmt, ##args)
49
50/* Cannot use the storage from net.c because we allocate larger buffers */
51static volatile uchar MyPktBuf[HDLC_PKTBUFSRX * PKT_MAXBLR_SIZE + PKTALIGN];
52static volatile uchar *MyRxPackets[HDLC_PKTBUFSRX]; /* Receive packet */
53
54static unsigned int keymile_rxIdx; /* index of the current RX buffer */
55
56static IPaddr_t cachedNumbers[CACHEDNUMBERS]; /* 4 bytes per entry */
57void initCachedNumbers(int);
58
59/*
60 * SCC Ethernet Tx and Rx buffer descriptors allocated at the
61 * immr->udata_bd address on Dual-Port RAM
62 * Provide for Double Buffering
63 */
64typedef volatile struct CommonBufferDescriptor {
65 cbd_t txbd; /* Tx BD */
66 cbd_t rxbd[HDLC_PKTBUFSRX]; /* Rx BD */
67} RTXBD;
68
69/*
70 * This must be extern because it is allocated in DPRAM using CPM-sepcific
71 * code.
72 */
73static RTXBD *rtx;
74
75static int keymile_hdlc_enet_send(struct eth_device *, volatile void *, int);
76static int keymile_hdlc_enet_recv(struct eth_device *);
77void keymile_hdlc_enet_init_bds(RTXBD *);
78extern int keymile_hdlc_enet_init(struct eth_device *, bd_t *);
79extern void keymile_hdlc_enet_halt(struct eth_device *);
80
81/* flags in the buffer descriptor not defined anywhere else */
82#define BD_SC_CT BD_SC_CD
83#define BD_SC_CR 0x04
84#define BD_SC_DE 0x80
85#ifndef BD_SC_TC
86#define BD_SC_TC ((ushort)0x0400) /* Transmit CRC */
87#endif
88#define BD_SC_FIRST BD_SC_TC
89#define BD_SC_STATS (BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_CR | BD_SC_CD \
90 | BD_SC_OV | BD_SC_DE)
91
92#if defined(TEST_RX) || defined(TEST_TX) || defined(TEST_IT)
93static void hexdump(unsigned char *buf, int len)
94{
95 int i;
96 const int bytesPerLine = 32;
97
98 if (len > 4 * bytesPerLine)
99 len = 4 * bytesPerLine;
100 dprintf("\t address: %08x\n", (unsigned int)buf);
101 for (i = 0; i < len; i++) {
102 if (i % bytesPerLine == 0)
103 dprintf("%04x: ", (unsigned short)i);
104 dprintf("%02x ", buf[i]);
105 if ((i + 1) % bytesPerLine == 0) {
106 dprintf("\n");
107 continue;
108 }
109 if ((i + 1) % 8 == 0)
110 printf(" ");
111 }
112 if (len % bytesPerLine)
113 dprintf("\n");
114}
115#endif
116
117int keymile_hdlc_enet_initialize(bd_t *bis)
118{
119 struct eth_device *dev;
120
121 dev = (struct eth_device *) malloc(sizeof *dev);
122 memset(dev, 0, sizeof *dev);
123#ifdef TEST_IT
124 seth = dev;
125#endif
126
Heiko Schocherc5e84052010-07-20 17:45:02 +0200127 sprintf(dev->name, "HDLC");
Gary Jennejohnad8678a2008-11-09 12:36:15 +0100128 dev->init = keymile_hdlc_enet_init;
129 dev->halt = keymile_hdlc_enet_halt;
130 dev->send = keymile_hdlc_enet_send;
131 dev->recv = keymile_hdlc_enet_recv;
132
133 eth_register(dev);
134
135 return 1;
136}
137
138/*
139 * This is called from the board-specific driver after rtx is allocated.
140 */
141void keymile_hdlc_enet_init_bds(RTXBD *board_rtx)
142{
143 volatile cbd_t *bdp;
144 int i;
145
146 rtx = board_rtx;
147 keymile_rxIdx = 0;
148 /*
149 * Initialize the buffer descriptors.
150 */
151 bdp = &rtx->txbd;
152 bdp->cbd_sc = 0;
153 bdp->cbd_bufaddr = 0;
154 bdp->cbd_sc = BD_SC_WRAP;
155
156 /*
157 * Setup RX packet buffers, aligned correctly.
158 * Borrowed from net/net.c.
159 */
160 MyRxPackets[0] = &MyPktBuf[0] + (PKTALIGN - 1);
161 MyRxPackets[0] -= (ulong)MyRxPackets[0] % PKTALIGN;
162 for (i = 1; i < HDLC_PKTBUFSRX; i++)
163 MyRxPackets[i] = MyRxPackets[0] + i * PKT_MAXBLR_SIZE;
164
165 bdp = &rtx->rxbd[0];
166 for (i = 0; i < HDLC_PKTBUFSRX; i++) {
167 bdp->cbd_sc = BD_SC_EMPTY;
168 /* Leave space at the start for INET header. */
169 bdp->cbd_bufaddr = (unsigned int)(MyRxPackets[i] +
170 INET_HDR_ALIGN);
171 bdp++;
172 }
173 bdp--;
174 bdp->cbd_sc |= BD_SC_WRAP;
175}
176
177/*
178 * This returns the current port number for NETCONSOLE. If nc_port
179 * in netconsole.c weren't declared static we wouldn't need this.
180 */
181static short get_netcons_port(void)
182{
183 char *p;
184 short nc_port;
185
186 nc_port = 6666; /* default */
187
188 p = getenv("ncip");
189 if (p != NULL) {
190 p = strchr(p, ':');
191 if (p != NULL)
192 nc_port = simple_strtoul(p + 1, NULL, 10);
193 }
194
195 return htons(nc_port);
196}
197
198/*
199 * Read the port numbers from the variables
200 */
201void initCachedNumbers(int verbose)
202{
203 char *str;
204 ushort port;
205
206 /* already in network order */
207 cachedNumbers[IP_ADDR] = getenv_IPaddr("ipaddr");
208 /* already in network order */
209 cachedNumbers[IP_SERVER] = getenv_IPaddr("serverip");
210 str = getenv("tftpsrcp");
211 if (str != NULL) {
212 /* avoid doing htons() again and again */
213 port = htons((ushort)simple_strtol(str, NULL, 10));
214 cachedNumbers[TFTP_SRC_PORT] = port;
215 } else
216 /* this can never be a valid port number */
217 cachedNumbers[TFTP_SRC_PORT] = (ulong)-1;
218 str = getenv("tftpdstp");
219 if (str != NULL) {
220 /* avoid doing htons() again and again */
221 port = htons((ushort)simple_strtol(str, NULL, 10));
222 cachedNumbers[TFTP_DST_PORT] = port;
223 } else
224 /* this is the default value */
225 cachedNumbers[TFTP_DST_PORT] = htons(WELL_KNOWN_PORT);
226 /* already in network order */
227 cachedNumbers[NETCONS_PORT] = get_netcons_port();
228 if (verbose) {
229 dprintf("\nIP Number Initialization:\n");
230 dprintf(" ip address %08lx\n", cachedNumbers[IP_ADDR]);
231 dprintf(" server ip address %08lx\n",
232 cachedNumbers[IP_SERVER]);
233 dprintf(" tftp client port %ld\n",
234 cachedNumbers[TFTP_SRC_PORT]);
235 dprintf(" tftp server port %ld\n",
236 cachedNumbers[TFTP_DST_PORT]);
237 dprintf(" netcons port %ld\n",
238 cachedNumbers[NETCONS_PORT]);
239 dprintf(" slot number (hex) %02x\n", keymile_slot);
240 }
241}
242
243static void keymile_hdlc_enet_doarp(volatile void *packet, int len)
244{
245 ARP_t *arp;
246 IPaddr_t src_ip; /* U-Boot's IP */
247 IPaddr_t dest_ip; /* the mgcoge's IP */
248 unsigned char *packet_copy = malloc(len);
249
250 /*
251 * Handling an ARP request means that a new transfer has started.
252 * Update our cached parameters now.
253 */
254 initCachedNumbers(0); /* may reinit port numbers */
255
256 /* special handling required for ARP */
257 arp = (ARP_t *)(packet + ETHER_HDR_SIZE);
258 /*
259 * XXXX
260 * This is pretty dirty! NetReceive only uses
261 * a few fields when handling an ARP reply, so
262 * we only modify those here. This could
263 * result in catastrophic failure at a later
264 * time if the handler is modified!
265 */
266 arp->ar_op = htons(ARPOP_REPLY);
267 /* save his/our IP */
268 src_ip = NetReadIP(&arp->ar_data[6]);
269 dest_ip = NetReadIP(&arp->ar_data[16]);
270 /* copy target IP to source IP */
271 NetCopyIP(&arp->ar_data[6], &dest_ip);
272 /* copy our IP to the right place */
273 NetCopyIP(&arp->ar_data[16], &src_ip);
274 /* always use 0x7f as the MAC for the coge */
275 arp->ar_data[0] = HDLC_UACUA;
276 /*
277 * copy the packet
278 * if NetReceive wants to write to stdout, it may overwrite packet
279 * especially if stdout is set to nc!
280 *
281 * However, if the malloc() above fails then we can still try the
282 * original packet, rather than causing the transfer to fail.
283 */
284 if (packet_copy != NULL) {
285 memcpy(packet_copy, (char *)packet, len);
286 NetReceive(packet_copy, len);
287 free(packet_copy);
288 } else
289 NetReceive(packet, len);
290}
291
292/*
293 * NOTE all callers ignore the returned value!
294 * At the moment this only handles ARP Requests, TFTP and NETCONSOLE.
295 */
296static int keymile_hdlc_enet_send(struct eth_device *dev, volatile void *packet,
297 int len)
298{
299 int j;
300 uint data_addr;
301 int data_len;
302 struct icn_hdr header;
303 struct icn_frame *frame;
304 Ethernet_t *et;
305 ARP_t *arp;
306 IP_t *ip;
307
308 if (len > (MAX_FRAME_LENGTH - sizeof(header)))
309 return -1;
310
311 frame = NULL;
312 et = NULL;
313 arp = NULL;
314 ip = NULL;
315
316 j = 0;
317 while ((rtx->txbd.cbd_sc & BD_SC_READY) && (j < TOUT_LOOP)) {
318 /* will also trigger Wd if needed, but maybe too often */
319 udelay(1);
320 j++;
321 }
322 if (j >= TOUT_LOOP) {
323 dprintf("TX not ready sc %x\n", rtx->txbd.cbd_sc);
324 return -1;
325 }
326
327 /*
328 * First check for an ARP Request since this requires special handling.
329 */
330 if (len >= (ARP_HDR_SIZE + ETHER_HDR_SIZE)) {
331 et = (Ethernet_t *)packet;
332 arp = (ARP_t *)(((char *)et) + ETHER_HDR_SIZE);
333 /* ARP and REQUEST? */
334 if (et->et_protlen == PROT_ARP &&
335 arp->ar_op == htons(ARPOP_REQUEST)) {
336 /* just short-circuit the request on the U-Boot side */
337 keymile_hdlc_enet_doarp(packet, len);
338 return 0;
339 }
340 }
341
342 /*
343 * GJ - I suppose the assumption here that len will always be
344 * > INET_HDR_SIZE is alright as long as the network stack
345 * isn't changed.
346 * Do not send INET header.
347 */
348 data_len = len + sizeof(header) - INET_HDR_SIZE;
349 frame = (struct icn_frame *) (((char *)packet) + INET_HDR_SIZE -
350 sizeof(header));
351
352#ifdef TEST_TX
353 printf("frame: %08x, ", frame);
354 hexdump((unsigned char *)packet, data_len + INET_HDR_SIZE);
355#endif
356
357 data_addr = (uint)frame;
358 if (len >= (IP_HDR_SIZE + ETHER_HDR_SIZE))
359 ip = (IP_t *)(packet + ETHER_HDR_SIZE);
360 /* Is it TFTP? TFTP always uses UDP and the cached dport */
361 if (ip != NULL && ip->ip_p == IPPROTO_UDP && ip->udp_dst ==
362 (ushort)cachedNumbers[TFTP_DST_PORT]) {
363 /* just in case the port wasn't set in the environment */
364 if (cachedNumbers[TFTP_SRC_PORT] == (ulong)-1)
365 cachedNumbers[TFTP_SRC_PORT] = ip->udp_src;
366 frame->hdr.application = MGS_TFTP;
367 }
368 /*
369 * Is it NETCONSOLE? NETCONSOLE always uses UDP.
370 */
371 else if (ip != NULL && ip->ip_p == IPPROTO_UDP
372 && ip->udp_dst == (ushort)cachedNumbers[NETCONS_PORT]) {
373 frame->hdr.application = MGS_NETCONS;
374 } else {
375 /* reject unknown packets */
376 /* may do some check on frame->hdr.application */
377 dprintf("Unknown packet type in %s, rejected\n",
378 __func__);
379 return -1;
380 }
381 /*
382 * Could extract the target's slot ID from its MAC here,
383 * but u-boot only wants to talk to the active server.
384 *
385 * avoid setting new source address when moving to another slot
386 */
387 frame->hdr.src_addr = keymile_slot;
388 frame->hdr.dest_addr = HDLC_UACUA;
389#ifdef TEST_TX
390 {
391 dprintf("TX: ");
392 hexdump((unsigned char *)data_addr, data_len);
393 }
394#endif
395
396 flush_cache(data_addr, data_len);
397 rtx->txbd.cbd_bufaddr = data_addr;
398 rtx->txbd.cbd_datlen = data_len;
399 rtx->txbd.cbd_sc |= (BD_SC_READY | BD_SC_TC | BD_SC_LAST | BD_SC_WRAP);
400
401 while ((rtx->txbd.cbd_sc & BD_SC_READY) && (j < TOUT_LOOP)) {
402 /* will also trigger Wd if needed, but maybe too often */
403 udelay(1);
404 j++;
405 }
406 if (j >= TOUT_LOOP)
407 dprintf("TX timeout\n");
408#ifdef ET_DEBUG
409 dprintf("cycles: %d status: %x\n", j, rtx->txbd.cbd_sc);
410#endif
411 j = (rtx->txbd.cbd_sc & BD_SC_STATS); /* return only status bits */
412 return j;
413}
414
415/*
416 * During a receive, the RxIdx points to the current incoming buffer.
417 * When we update through the ring, if the next incoming buffer has
418 * not been given to the system, we just set the empty indicator,
419 * effectively tossing the packet.
420 */
421static int keymile_hdlc_enet_recv(struct eth_device *dev)
422{
423 int length;
424 unsigned char app;
425 struct icn_frame *fp;
426 Ethernet_t *ep;
427 IP_t *ip;
428
429 for (;;) {
430 if (rtx->rxbd[keymile_rxIdx].cbd_sc & BD_SC_EMPTY) {
431 length = -1;
432 break; /* nothing received - leave for() loop */
433 }
434
435 length = rtx->rxbd[keymile_rxIdx].cbd_datlen;
436#ifdef TEST_RX
437 dprintf("packet %d bytes long\n", length);
438#endif
439
440 /*
441 * BD_SC_BR -> LG bit
442 * BD_SC_FR -> NO bit
443 * BD_SC_PR -> AB bit
444 * BD_SC_NAK -> CR bit
445 * 0x80 -> DE bit
446 */
447 if (rtx->rxbd[keymile_rxIdx].cbd_sc & BD_SC_STATS) {
448#ifdef ET_DEBUG
449 dprintf("err: %x\n", rtx->rxbd[keymile_rxIdx].cbd_sc);
450#endif
451 } else if (length > MAX_FRAME_LENGTH) { /* can't happen */
452#ifdef ET_DEBUG
453 dprintf("err: packet too big\n");
454#endif
455 } else {
456 fp = (struct icn_frame *)(MyRxPackets[keymile_rxIdx] +
457 INET_HDR_ALIGN - INET_HDR_SIZE);
458#ifdef TEST_RX
459 dprintf("RX %d: ", keymile_rxIdx);
460 hexdump((unsigned char *)MyRxPackets[keymile_rxIdx],
461 INET_HDR_ALIGN + INET_HDR_SIZE + 4);
462#endif
463 /* copy icn header to the beginning */
464 memcpy(fp, ((char *)fp + INET_HDR_SIZE),
465 sizeof(struct icn_hdr));
466 app = fp->hdr.application;
467 if (app == MGS_NETCONS || app == MGS_TFTP) {
468 struct icn_hdr *ih = &fp->hdr;
469 unsigned char icn_src_addr = ih->src_addr;
470 unsigned char icn_dest_addr = ih->dest_addr;
471
472 /*
473 * expand header by INET_HDR_SIZE
474 */
475 length += INET_HDR_SIZE;
476 /* initalize header */
477 memset((char *)fp->data, 0x00, INET_HDR_SIZE);
478 ep = (Ethernet_t *)fp->data;
479 /* set MACs */
480 ep->et_dest[0] = icn_dest_addr;
481 ep->et_src[0] = icn_src_addr;
482 ep->et_protlen = htons(PROT_IP);
483 /* set ip stuff */
484 ip = (IP_t *)(fp->data + ETHER_HDR_SIZE);
485 /* set ip addresses */
486 ip->ip_src = cachedNumbers[IP_SERVER];
487 ip->ip_dst = cachedNumbers[IP_ADDR];
488 /* ip length */
489 ip->ip_len = htons(length - ETHER_HDR_SIZE -
490 REMOVE);
491 /* ip proto */
492 ip->ip_p = IPPROTO_UDP;
493 switch (app) {
494 case MGS_TFTP:
495 /* swap src/dst port numbers */
496 ip->udp_src = (ushort)
497 cachedNumbers[TFTP_DST_PORT];
498 ip->udp_dst = (ushort)
499 cachedNumbers[TFTP_SRC_PORT];
500 ip->udp_len = ip->ip_len -
501 IP_HDR_SIZE_NO_UDP;
502 ip->udp_xsum = 0;
503 break;
504 case MGS_NETCONS:
505 ip->udp_src = (ushort)
506 cachedNumbers[NETCONS_PORT];
507 /*
508 * in drivers/net/netconsole.c src port
509 * equals dest port
510 */
511 ip->udp_dst = ip->udp_src;
512 ip->udp_len = ip->ip_len -
513 IP_HDR_SIZE_NO_UDP;
514 ip->udp_xsum = 0;
515 break;
516 }
517 /* ip version */
518 ip->ip_hl_v = (0x40) | (0x0f &
519 (IP_HDR_SIZE_NO_UDP / 4));
520 ip->ip_tos = 0;
521 ip->ip_id = 0;
522 /* flags, fragment offset */
523 ip->ip_off = htons(0x4000);
524 ip->ip_ttl = 255; /* time to live */
525 /* have to fixup the checksum */
526 ip->ip_sum = ~NetCksum((uchar *)ip,
527 IP_HDR_SIZE_NO_UDP / 2);
528 /*
529 * Pass the packet up to the protocol layers
530 * but remove dest_addr, src_addr, application
531 * and the CRC.
532 */
533#ifdef TEST_RX
534 hexdump((unsigned char *)fp->data,
535 INET_HDR_SIZE + 4);
536#endif
537 NetReceive(fp->data, length - REMOVE);
538 } else {
539 /*
540 * the other application types are not yet
541 * supported by u-boot.
542 */
543 /* normally drop it */
544#ifdef TEST_NO
545 /* send it anyway */
546 fp = (struct icn_frame *)
547 (MyRxPackets[keymile_rxIdx] +
548 INET_HDR_ALIGN);
549 NetReceive(fp->data, length - REMOVE);
550#endif
551 }
552 }
553
554 /* Give the buffer back to the SCC. */
555 rtx->rxbd[keymile_rxIdx].cbd_datlen = 0;
556
557 /* wrap around buffer index when necessary */
558 if ((keymile_rxIdx + 1) >= HDLC_PKTBUFSRX) {
559 rtx->rxbd[HDLC_PKTBUFSRX - 1].cbd_sc =
560 (BD_SC_WRAP | BD_SC_EMPTY);
561 keymile_rxIdx = 0;
562 } else {
563 rtx->rxbd[keymile_rxIdx].cbd_sc = BD_SC_EMPTY;
564 keymile_rxIdx++;
565 }
566 }
567 return length;
568}
569
570#ifdef TEST_IT
571/* simple send test routine */
572int hdlc_enet_stest(struct cmd_tbl_s *a, int b, int c, char **d)
573{
574 unsigned char pkt[2];
575 int ret;
576
577 dprintf("enter stest\n");
578 /* may have to initialize things */
579 if (seth->state != ETH_STATE_ACTIVE) {
580 /* the bd_t* is not used */
581 if (seth->init(seth, NULL) >= 0)
582 seth->state = ETH_STATE_ACTIVE;
583 }
584 pkt[0] = 0xea;
585 pkt[1] = 0xae;
586 ret = keymile_hdlc_enet_send(seth, pkt, 2);
587 dprintf("return from send %x\n", ret);
588 dprintf("exit stest\n");
589 return ret;
590}
591U_BOOT_CMD(
592 stest, 1, 1, hdlc_enet_stest,
Peter Tyserdfb72b82009-01-27 18:03:12 -0600593 "simple send test for hdlc_enet",
Wolfgang Denkc54781c2009-05-24 17:06:54 +0200594 ""
Gary Jennejohnad8678a2008-11-09 12:36:15 +0100595);
596/* simple receive test routine */
597int hdlc_enet_rtest(struct cmd_tbl_s *a, int b, int c, char **d)
598{
599 int ret;
600
601 dprintf("enter rtest\n");
602 /* may have to initialize things */
603 if (seth->state != ETH_STATE_ACTIVE) {
604 /* the bd_t* is not used */
605 if (seth->init(seth, NULL) >= 0)
606 seth->state = ETH_STATE_ACTIVE;
607 }
608 ret = keymile_hdlc_enet_recv(seth);
609 dprintf("return from recv %x\n", ret);
610 dprintf("exit rtest\n");
611 return ret;
612}
613U_BOOT_CMD(
614 rtest, 1, 1, hdlc_enet_rtest,
Peter Tyserdfb72b82009-01-27 18:03:12 -0600615 "simple receive test for hdlc_enet",
Wolfgang Denkc54781c2009-05-24 17:06:54 +0200616 ""
Gary Jennejohnad8678a2008-11-09 12:36:15 +0100617);
618#endif
619
620#endif /* CONFIG_KEYMILE_HDLC_ENET */