]>
git.ipfire.org Git - people/ms/u-boot.git/blob - board/keymile/common/keymile_hdlc_enet.c
3 * Gary Jennejohn, DENX Software Engineering GmbH, garyj@denx.de.
5 * Based in part on arch/powerpc/cpu/mpc8260/ether_scc.c.
7 * See file CREDITS for list of people who contributed to this
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.
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.
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,
30 #ifdef CONFIG_KEYMILE_HDLC_ENET
35 #include "keymile_hdlc_enet.h"
37 extern char keymile_slot
; /* our slot number in the backplane */
39 /* Allow up to about 50 ms for sending */
40 #define TOUT_LOOP 50000
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
48 #define dprintf(fmt, args...) serial_printf(fmt, ##args)
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 */
54 static unsigned int keymile_rxIdx
; /* index of the current RX buffer */
56 static IPaddr_t cachedNumbers
[CACHEDNUMBERS
]; /* 4 bytes per entry */
57 void initCachedNumbers(int);
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
64 typedef volatile struct CommonBufferDescriptor
{
65 cbd_t txbd
; /* Tx BD */
66 cbd_t rxbd
[HDLC_PKTBUFSRX
]; /* Rx BD */
70 * This must be extern because it is allocated in DPRAM using CPM-sepcific
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
*);
81 /* flags in the buffer descriptor not defined anywhere else */
82 #define BD_SC_CT BD_SC_CD
86 #define BD_SC_TC ((ushort)0x0400) /* Transmit CRC */
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)
92 #if defined(TEST_RX) || defined(TEST_TX) || defined(TEST_IT)
93 static void hexdump(unsigned char *buf
, int len
)
96 const int bytesPerLine
= 32;
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) {
109 if ((i
+ 1) % 8 == 0)
112 if (len
% bytesPerLine
)
117 int keymile_hdlc_enet_initialize(bd_t
*bis
)
119 struct eth_device
*dev
;
121 dev
= (struct eth_device
*) malloc(sizeof *dev
);
122 memset(dev
, 0, sizeof *dev
);
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
;
139 * This is called from the board-specific driver after rtx is allocated.
141 void keymile_hdlc_enet_init_bds(RTXBD
*board_rtx
)
149 * Initialize the buffer descriptors.
153 bdp
->cbd_bufaddr
= 0;
154 bdp
->cbd_sc
= BD_SC_WRAP
;
157 * Setup RX packet buffers, aligned correctly.
158 * Borrowed from net/net.c.
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
;
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
] +
174 bdp
->cbd_sc
|= BD_SC_WRAP
;
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.
181 static short get_netcons_port(void)
186 nc_port
= 6666; /* default */
192 nc_port
= simple_strtoul(p
+ 1, NULL
, 10);
195 return htons(nc_port
);
199 * Read the port numbers from the variables
201 void initCachedNumbers(int verbose
)
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");
212 /* avoid doing htons() again and again */
213 port
= htons((ushort
)simple_strtol(str
, NULL
, 10));
214 cachedNumbers
[TFTP_SRC_PORT
] = port
;
216 /* this can never be a valid port number */
217 cachedNumbers
[TFTP_SRC_PORT
] = (ulong
)-1;
218 str
= getenv("tftpdstp");
220 /* avoid doing htons() again and again */
221 port
= htons((ushort
)simple_strtol(str
, NULL
, 10));
222 cachedNumbers
[TFTP_DST_PORT
] = port
;
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();
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
);
243 static void keymile_hdlc_enet_doarp(volatile void *packet
, int len
)
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
);
251 * Handling an ARP request means that a new transfer has started.
252 * Update our cached parameters now.
254 initCachedNumbers(0); /* may reinit port numbers */
256 /* special handling required for ARP */
257 arp
= (ARP_t
*)(packet
+ ETHER_HDR_SIZE
);
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!
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
;
278 * if NetReceive wants to write to stdout, it may overwrite packet
279 * especially if stdout is set to nc!
281 * However, if the malloc() above fails then we can still try the
282 * original packet, rather than causing the transfer to fail.
284 if (packet_copy
!= NULL
) {
285 memcpy(packet_copy
, (char *)packet
, len
);
286 NetReceive(packet_copy
, len
);
289 NetReceive(packet
, len
);
293 * NOTE all callers ignore the returned value!
294 * At the moment this only handles ARP Requests, TFTP and NETCONSOLE.
296 static int keymile_hdlc_enet_send(struct eth_device
*dev
, volatile void *packet
,
302 struct icn_hdr header
;
303 struct icn_frame
*frame
;
308 if (len
> (MAX_FRAME_LENGTH
- sizeof(header
)))
317 while ((rtx
->txbd
.cbd_sc
& BD_SC_READY
) && (j
< TOUT_LOOP
)) {
318 /* will also trigger Wd if needed, but maybe too often */
322 if (j
>= TOUT_LOOP
) {
323 dprintf("TX not ready sc %x\n", rtx
->txbd
.cbd_sc
);
328 * First check for an ARP Request since this requires special handling.
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
);
343 * GJ - I suppose the assumption here that len will always be
344 * > INET_HDR_SIZE is alright as long as the network stack
346 * Do not send INET header.
348 data_len
= len
+ sizeof(header
) - INET_HDR_SIZE
;
349 frame
= (struct icn_frame
*) (((char *)packet
) + INET_HDR_SIZE
-
353 printf("frame: %08x, ", frame
);
354 hexdump((unsigned char *)packet
, data_len
+ INET_HDR_SIZE
);
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
;
369 * Is it NETCONSOLE? NETCONSOLE always uses UDP.
371 else if (ip
!= NULL
&& ip
->ip_p
== IPPROTO_UDP
372 && ip
->udp_dst
== (ushort
)cachedNumbers
[NETCONS_PORT
]) {
373 frame
->hdr
.application
= MGS_NETCONS
;
375 /* reject unknown packets */
376 /* may do some check on frame->hdr.application */
377 dprintf("Unknown packet type in %s, rejected\n",
382 * Could extract the target's slot ID from its MAC here,
383 * but u-boot only wants to talk to the active server.
385 * avoid setting new source address when moving to another slot
387 frame
->hdr
.src_addr
= keymile_slot
;
388 frame
->hdr
.dest_addr
= HDLC_UACUA
;
392 hexdump((unsigned char *)data_addr
, data_len
);
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
);
401 while ((rtx
->txbd
.cbd_sc
& BD_SC_READY
) && (j
< TOUT_LOOP
)) {
402 /* will also trigger Wd if needed, but maybe too often */
407 dprintf("TX timeout\n");
409 dprintf("cycles: %d status: %x\n", j
, rtx
->txbd
.cbd_sc
);
411 j
= (rtx
->txbd
.cbd_sc
& BD_SC_STATS
); /* return only status bits */
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.
421 static int keymile_hdlc_enet_recv(struct eth_device
*dev
)
425 struct icn_frame
*fp
;
430 if (rtx
->rxbd
[keymile_rxIdx
].cbd_sc
& BD_SC_EMPTY
) {
432 break; /* nothing received - leave for() loop */
435 length
= rtx
->rxbd
[keymile_rxIdx
].cbd_datlen
;
437 dprintf("packet %d bytes long\n", length
);
444 * BD_SC_NAK -> CR bit
447 if (rtx
->rxbd
[keymile_rxIdx
].cbd_sc
& BD_SC_STATS
) {
449 dprintf("err: %x\n", rtx
->rxbd
[keymile_rxIdx
].cbd_sc
);
451 } else if (length
> MAX_FRAME_LENGTH
) { /* can't happen */
453 dprintf("err: packet too big\n");
456 fp
= (struct icn_frame
*)(MyRxPackets
[keymile_rxIdx
] +
457 INET_HDR_ALIGN
- INET_HDR_SIZE
);
459 dprintf("RX %d: ", keymile_rxIdx
);
460 hexdump((unsigned char *)MyRxPackets
[keymile_rxIdx
],
461 INET_HDR_ALIGN
+ INET_HDR_SIZE
+ 4);
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
;
473 * expand header by INET_HDR_SIZE
475 length
+= INET_HDR_SIZE
;
476 /* initalize header */
477 memset((char *)fp
->data
, 0x00, INET_HDR_SIZE
);
478 ep
= (Ethernet_t
*)fp
->data
;
480 ep
->et_dest
[0] = icn_dest_addr
;
481 ep
->et_src
[0] = icn_src_addr
;
482 ep
->et_protlen
= htons(PROT_IP
);
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
];
489 ip
->ip_len
= htons(length
- ETHER_HDR_SIZE
-
492 ip
->ip_p
= IPPROTO_UDP
;
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
-
505 ip
->udp_src
= (ushort
)
506 cachedNumbers
[NETCONS_PORT
];
508 * in drivers/net/netconsole.c src port
511 ip
->udp_dst
= ip
->udp_src
;
512 ip
->udp_len
= ip
->ip_len
-
518 ip
->ip_hl_v
= (0x40) | (0x0f &
519 (IP_HDR_SIZE_NO_UDP
/ 4));
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);
529 * Pass the packet up to the protocol layers
530 * but remove dest_addr, src_addr, application
534 hexdump((unsigned char *)fp
->data
,
537 NetReceive(fp
->data
, length
- REMOVE
);
540 * the other application types are not yet
541 * supported by u-boot.
543 /* normally drop it */
546 fp
= (struct icn_frame
*)
547 (MyRxPackets
[keymile_rxIdx
] +
549 NetReceive(fp
->data
, length
- REMOVE
);
554 /* Give the buffer back to the SCC. */
555 rtx
->rxbd
[keymile_rxIdx
].cbd_datlen
= 0;
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
);
563 rtx
->rxbd
[keymile_rxIdx
].cbd_sc
= BD_SC_EMPTY
;
571 /* simple send test routine */
572 int hdlc_enet_stest(struct cmd_tbl_s
*a
, int b
, int c
, char **d
)
574 unsigned char pkt
[2];
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
;
586 ret
= keymile_hdlc_enet_send(seth
, pkt
, 2);
587 dprintf("return from send %x\n", ret
);
588 dprintf("exit stest\n");
592 stest
, 1, 1, hdlc_enet_stest
,
593 "simple send test for hdlc_enet",
596 /* simple receive test routine */
597 int hdlc_enet_rtest(struct cmd_tbl_s
*a
, int b
, int c
, char **d
)
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
;
608 ret
= keymile_hdlc_enet_recv(seth
);
609 dprintf("return from recv %x\n", ret
);
610 dprintf("exit rtest\n");
614 rtest
, 1, 1, hdlc_enet_rtest
,
615 "simple receive test for hdlc_enet",
620 #endif /* CONFIG_KEYMILE_HDLC_ENET */