]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/keymile/common/keymile_hdlc_enet.c
Move arch/ppc to arch/powerpc
[karo-tx-uboot.git] / board / keymile / common / keymile_hdlc_enet.c
1 /*
2  * (C) Copyright 2008
3  * Gary Jennejohn, DENX Software Engineering GmbH, garyj@denx.de.
4  *
5  * Based in part on arch/powerpc/cpu/mpc8260/ether_scc.c.
6  *
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
37 extern 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 /*
43  * Since, except during initialization, ethact is always HDLC ETHERNET
44  * 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 */
51 static volatile uchar MyPktBuf[HDLC_PKTBUFSRX * PKT_MAXBLR_SIZE + PKTALIGN];
52 static volatile uchar *MyRxPackets[HDLC_PKTBUFSRX]; /* Receive packet */
53
54 static unsigned int keymile_rxIdx;      /* index of the current RX buffer */
55
56 static IPaddr_t cachedNumbers[CACHEDNUMBERS]; /* 4 bytes per entry */
57 void 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   */
64 typedef 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  */
73 static RTXBD *rtx;
74
75 static int keymile_hdlc_enet_send(struct eth_device *, volatile void *, int);
76 static int keymile_hdlc_enet_recv(struct eth_device *);
77 void keymile_hdlc_enet_init_bds(RTXBD *);
78 extern int keymile_hdlc_enet_init(struct eth_device *, bd_t *);
79 extern 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)
93 static 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
117 int 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
127         sprintf(dev->name, "HDLC ETHERNET");
128         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  */
141 void 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  */
181 static 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  */
201 void 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
243 static 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  */
296 static 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  */
421 static 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 */
572 int 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 }
591 U_BOOT_CMD(
592         stest, 1, 1, hdlc_enet_stest,
593         "simple send test for hdlc_enet",
594         ""
595 );
596 /* simple receive test routine */
597 int 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 }
613 U_BOOT_CMD(
614         rtest, 1, 1, hdlc_enet_rtest,
615         "simple receive test for hdlc_enet",
616         ""
617 );
618 #endif
619
620 #endif /* CONFIG_KEYMILE_HDLC_ENET */