diff options
Diffstat (limited to 'board/keymile')
-rw-r--r-- | board/keymile/common/keymile_hdlc_enet.c | 620 | ||||
-rw-r--r-- | board/keymile/common/keymile_hdlc_enet.h | 129 | ||||
-rw-r--r-- | board/keymile/mgcoge/Makefile | 3 | ||||
-rw-r--r-- | board/keymile/mgcoge/mgcoge_hdlc_enet.c | 276 | ||||
-rw-r--r-- | board/keymile/mgsuvd/Makefile | 3 | ||||
-rw-r--r-- | board/keymile/mgsuvd/mgsuvd_hdlc_enet.c | 278 |
6 files changed, 1307 insertions, 2 deletions
diff --git a/board/keymile/common/keymile_hdlc_enet.c b/board/keymile/common/keymile_hdlc_enet.c new file mode 100644 index 0000000..141371b --- /dev/null +++ b/board/keymile/common/keymile_hdlc_enet.c @@ -0,0 +1,620 @@ +/* + * (C) Copyright 2008 + * Gary Jennejohn, DENX Software Engineering GmbH, garyj@denx.de. + * + * Based in part on cpu/mpc8260/ether_scc.c. + * + * 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 <malloc.h> +#include <net.h> + +#ifdef CONFIG_KEYMILE_HDLC_ENET +#ifdef TEST_IT +#include <command.h> +#endif + +#include "keymile_hdlc_enet.h" + +extern char keymile_slot; /* our slot number in the backplane */ + +/* Allow up to about 50 ms for sending */ +#define TOUT_LOOP 50000 + +/* + * Since, except during initialization, ethact is always HDLC ETHERNET + * while we're in the driver, just use serial_printf() everywhere for + * output. This avoids possible conflicts when netconsole is being + * used. + */ +#define dprintf(fmt, args...) serial_printf(fmt, ##args) + +/* Cannot use the storage from net.c because we allocate larger buffers */ +static volatile uchar MyPktBuf[HDLC_PKTBUFSRX * PKT_MAXBLR_SIZE + PKTALIGN]; +static volatile uchar *MyRxPackets[HDLC_PKTBUFSRX]; /* Receive packet */ + +static unsigned int keymile_rxIdx; /* index of the current RX buffer */ + +static IPaddr_t cachedNumbers[CACHEDNUMBERS]; /* 4 bytes per entry */ +void initCachedNumbers(int); + +/* + * SCC Ethernet Tx and Rx buffer descriptors allocated at the + * immr->udata_bd address on Dual-Port RAM + * Provide for Double Buffering + */ +typedef volatile struct CommonBufferDescriptor { + cbd_t txbd; /* Tx BD */ + cbd_t rxbd[HDLC_PKTBUFSRX]; /* Rx BD */ +} RTXBD; + +/* + * This must be extern because it is allocated in DPRAM using CPM-sepcific + * code. + */ +static RTXBD *rtx; + +static int keymile_hdlc_enet_send(struct eth_device *, volatile void *, int); +static int keymile_hdlc_enet_recv(struct eth_device *); +void keymile_hdlc_enet_init_bds(RTXBD *); +extern int keymile_hdlc_enet_init(struct eth_device *, bd_t *); +extern void keymile_hdlc_enet_halt(struct eth_device *); + +/* flags in the buffer descriptor not defined anywhere else */ +#define BD_SC_CT BD_SC_CD +#define BD_SC_CR 0x04 +#define BD_SC_DE 0x80 +#ifndef BD_SC_TC +#define BD_SC_TC ((ushort)0x0400) /* Transmit CRC */ +#endif +#define BD_SC_FIRST BD_SC_TC +#define BD_SC_STATS (BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_CR | BD_SC_CD \ + | BD_SC_OV | BD_SC_DE) + +#if defined(TEST_RX) || defined(TEST_TX) || defined(TEST_IT) +static void hexdump(unsigned char *buf, int len) +{ + int i; + const int bytesPerLine = 32; + + if (len > 4 * bytesPerLine) + len = 4 * bytesPerLine; + dprintf("\t address: %08x\n", (unsigned int)buf); + for (i = 0; i < len; i++) { + if (i % bytesPerLine == 0) + dprintf("%04x: ", (unsigned short)i); + dprintf("%02x ", buf[i]); + if ((i + 1) % bytesPerLine == 0) { + dprintf("\n"); + continue; + } + if ((i + 1) % 8 == 0) + printf(" "); + } + if (len % bytesPerLine) + dprintf("\n"); +} +#endif + +int keymile_hdlc_enet_initialize(bd_t *bis) +{ + struct eth_device *dev; + + dev = (struct eth_device *) malloc(sizeof *dev); + memset(dev, 0, sizeof *dev); +#ifdef TEST_IT + seth = dev; +#endif + + sprintf(dev->name, "HDLC ETHERNET"); + dev->init = keymile_hdlc_enet_init; + dev->halt = keymile_hdlc_enet_halt; + dev->send = keymile_hdlc_enet_send; + dev->recv = keymile_hdlc_enet_recv; + + eth_register(dev); + + return 1; +} + +/* + * This is called from the board-specific driver after rtx is allocated. + */ +void keymile_hdlc_enet_init_bds(RTXBD *board_rtx) +{ + volatile cbd_t *bdp; + int i; + + rtx = board_rtx; + keymile_rxIdx = 0; + /* + * Initialize the buffer descriptors. + */ + bdp = &rtx->txbd; + bdp->cbd_sc = 0; + bdp->cbd_bufaddr = 0; + bdp->cbd_sc = BD_SC_WRAP; + + /* + * Setup RX packet buffers, aligned correctly. + * Borrowed from net/net.c. + */ + MyRxPackets[0] = &MyPktBuf[0] + (PKTALIGN - 1); + MyRxPackets[0] -= (ulong)MyRxPackets[0] % PKTALIGN; + for (i = 1; i < HDLC_PKTBUFSRX; i++) + MyRxPackets[i] = MyRxPackets[0] + i * PKT_MAXBLR_SIZE; + + bdp = &rtx->rxbd[0]; + for (i = 0; i < HDLC_PKTBUFSRX; i++) { + bdp->cbd_sc = BD_SC_EMPTY; + /* Leave space at the start for INET header. */ + bdp->cbd_bufaddr = (unsigned int)(MyRxPackets[i] + + INET_HDR_ALIGN); + bdp++; + } + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; +} + +/* + * This returns the current port number for NETCONSOLE. If nc_port + * in netconsole.c weren't declared static we wouldn't need this. + */ +static short get_netcons_port(void) +{ + char *p; + short nc_port; + + nc_port = 6666; /* default */ + + p = getenv("ncip"); + if (p != NULL) { + p = strchr(p, ':'); + if (p != NULL) + nc_port = simple_strtoul(p + 1, NULL, 10); + } + + return htons(nc_port); +} + +/* + * Read the port numbers from the variables + */ +void initCachedNumbers(int verbose) +{ + char *str; + ushort port; + + /* already in network order */ + cachedNumbers[IP_ADDR] = getenv_IPaddr("ipaddr"); + /* already in network order */ + cachedNumbers[IP_SERVER] = getenv_IPaddr("serverip"); + str = getenv("tftpsrcp"); + if (str != NULL) { + /* avoid doing htons() again and again */ + port = htons((ushort)simple_strtol(str, NULL, 10)); + cachedNumbers[TFTP_SRC_PORT] = port; + } else + /* this can never be a valid port number */ + cachedNumbers[TFTP_SRC_PORT] = (ulong)-1; + str = getenv("tftpdstp"); + if (str != NULL) { + /* avoid doing htons() again and again */ + port = htons((ushort)simple_strtol(str, NULL, 10)); + cachedNumbers[TFTP_DST_PORT] = port; + } else + /* this is the default value */ + cachedNumbers[TFTP_DST_PORT] = htons(WELL_KNOWN_PORT); + /* already in network order */ + cachedNumbers[NETCONS_PORT] = get_netcons_port(); + if (verbose) { + dprintf("\nIP Number Initialization:\n"); + dprintf(" ip address %08lx\n", cachedNumbers[IP_ADDR]); + dprintf(" server ip address %08lx\n", + cachedNumbers[IP_SERVER]); + dprintf(" tftp client port %ld\n", + cachedNumbers[TFTP_SRC_PORT]); + dprintf(" tftp server port %ld\n", + cachedNumbers[TFTP_DST_PORT]); + dprintf(" netcons port %ld\n", + cachedNumbers[NETCONS_PORT]); + dprintf(" slot number (hex) %02x\n", keymile_slot); + } +} + +static void keymile_hdlc_enet_doarp(volatile void *packet, int len) +{ + ARP_t *arp; + IPaddr_t src_ip; /* U-Boot's IP */ + IPaddr_t dest_ip; /* the mgcoge's IP */ + unsigned char *packet_copy = malloc(len); + + /* + * Handling an ARP request means that a new transfer has started. + * Update our cached parameters now. + */ + initCachedNumbers(0); /* may reinit port numbers */ + + /* special handling required for ARP */ + arp = (ARP_t *)(packet + ETHER_HDR_SIZE); + /* + * XXXX + * This is pretty dirty! NetReceive only uses + * a few fields when handling an ARP reply, so + * we only modify those here. This could + * result in catastrophic failure at a later + * time if the handler is modified! + */ + arp->ar_op = htons(ARPOP_REPLY); + /* save his/our IP */ + src_ip = NetReadIP(&arp->ar_data[6]); + dest_ip = NetReadIP(&arp->ar_data[16]); + /* copy target IP to source IP */ + NetCopyIP(&arp->ar_data[6], &dest_ip); + /* copy our IP to the right place */ + NetCopyIP(&arp->ar_data[16], &src_ip); + /* always use 0x7f as the MAC for the coge */ + arp->ar_data[0] = HDLC_UACUA; + /* + * copy the packet + * if NetReceive wants to write to stdout, it may overwrite packet + * especially if stdout is set to nc! + * + * However, if the malloc() above fails then we can still try the + * original packet, rather than causing the transfer to fail. + */ + if (packet_copy != NULL) { + memcpy(packet_copy, (char *)packet, len); + NetReceive(packet_copy, len); + free(packet_copy); + } else + NetReceive(packet, len); +} + +/* + * NOTE all callers ignore the returned value! + * At the moment this only handles ARP Requests, TFTP and NETCONSOLE. + */ +static int keymile_hdlc_enet_send(struct eth_device *dev, volatile void *packet, + int len) +{ + int j; + uint data_addr; + int data_len; + struct icn_hdr header; + struct icn_frame *frame; + Ethernet_t *et; + ARP_t *arp; + IP_t *ip; + + if (len > (MAX_FRAME_LENGTH - sizeof(header))) + return -1; + + frame = NULL; + et = NULL; + arp = NULL; + ip = NULL; + + j = 0; + while ((rtx->txbd.cbd_sc & BD_SC_READY) && (j < TOUT_LOOP)) { + /* will also trigger Wd if needed, but maybe too often */ + udelay(1); + j++; + } + if (j >= TOUT_LOOP) { + dprintf("TX not ready sc %x\n", rtx->txbd.cbd_sc); + return -1; + } + + /* + * First check for an ARP Request since this requires special handling. + */ + if (len >= (ARP_HDR_SIZE + ETHER_HDR_SIZE)) { + et = (Ethernet_t *)packet; + arp = (ARP_t *)(((char *)et) + ETHER_HDR_SIZE); + /* ARP and REQUEST? */ + if (et->et_protlen == PROT_ARP && + arp->ar_op == htons(ARPOP_REQUEST)) { + /* just short-circuit the request on the U-Boot side */ + keymile_hdlc_enet_doarp(packet, len); + return 0; + } + } + + /* + * GJ - I suppose the assumption here that len will always be + * > INET_HDR_SIZE is alright as long as the network stack + * isn't changed. + * Do not send INET header. + */ + data_len = len + sizeof(header) - INET_HDR_SIZE; + frame = (struct icn_frame *) (((char *)packet) + INET_HDR_SIZE - + sizeof(header)); + +#ifdef TEST_TX + printf("frame: %08x, ", frame); + hexdump((unsigned char *)packet, data_len + INET_HDR_SIZE); +#endif + + data_addr = (uint)frame; + if (len >= (IP_HDR_SIZE + ETHER_HDR_SIZE)) + ip = (IP_t *)(packet + ETHER_HDR_SIZE); + /* Is it TFTP? TFTP always uses UDP and the cached dport */ + if (ip != NULL && ip->ip_p == IPPROTO_UDP && ip->udp_dst == + (ushort)cachedNumbers[TFTP_DST_PORT]) { + /* just in case the port wasn't set in the environment */ + if (cachedNumbers[TFTP_SRC_PORT] == (ulong)-1) + cachedNumbers[TFTP_SRC_PORT] = ip->udp_src; + frame->hdr.application = MGS_TFTP; + } + /* + * Is it NETCONSOLE? NETCONSOLE always uses UDP. + */ + else if (ip != NULL && ip->ip_p == IPPROTO_UDP + && ip->udp_dst == (ushort)cachedNumbers[NETCONS_PORT]) { + frame->hdr.application = MGS_NETCONS; + } else { + /* reject unknown packets */ + /* may do some check on frame->hdr.application */ + dprintf("Unknown packet type in %s, rejected\n", + __func__); + return -1; + } + /* + * Could extract the target's slot ID from its MAC here, + * but u-boot only wants to talk to the active server. + * + * avoid setting new source address when moving to another slot + */ + frame->hdr.src_addr = keymile_slot; + frame->hdr.dest_addr = HDLC_UACUA; +#ifdef TEST_TX + { + dprintf("TX: "); + hexdump((unsigned char *)data_addr, data_len); + } +#endif + + flush_cache(data_addr, data_len); + rtx->txbd.cbd_bufaddr = data_addr; + rtx->txbd.cbd_datlen = data_len; + rtx->txbd.cbd_sc |= (BD_SC_READY | BD_SC_TC | BD_SC_LAST | BD_SC_WRAP); + + while ((rtx->txbd.cbd_sc & BD_SC_READY) && (j < TOUT_LOOP)) { + /* will also trigger Wd if needed, but maybe too often */ + udelay(1); + j++; + } + if (j >= TOUT_LOOP) + dprintf("TX timeout\n"); +#ifdef ET_DEBUG + dprintf("cycles: %d status: %x\n", j, rtx->txbd.cbd_sc); +#endif + j = (rtx->txbd.cbd_sc & BD_SC_STATS); /* return only status bits */ + return j; +} + +/* + * During a receive, the RxIdx points to the current incoming buffer. + * When we update through the ring, if the next incoming buffer has + * not been given to the system, we just set the empty indicator, + * effectively tossing the packet. + */ +static int keymile_hdlc_enet_recv(struct eth_device *dev) +{ + int length; + unsigned char app; + struct icn_frame *fp; + Ethernet_t *ep; + IP_t *ip; + + for (;;) { + if (rtx->rxbd[keymile_rxIdx].cbd_sc & BD_SC_EMPTY) { + length = -1; + break; /* nothing received - leave for() loop */ + } + + length = rtx->rxbd[keymile_rxIdx].cbd_datlen; +#ifdef TEST_RX + dprintf("packet %d bytes long\n", length); +#endif + + /* + * BD_SC_BR -> LG bit + * BD_SC_FR -> NO bit + * BD_SC_PR -> AB bit + * BD_SC_NAK -> CR bit + * 0x80 -> DE bit + */ + if (rtx->rxbd[keymile_rxIdx].cbd_sc & BD_SC_STATS) { +#ifdef ET_DEBUG + dprintf("err: %x\n", rtx->rxbd[keymile_rxIdx].cbd_sc); +#endif + } else if (length > MAX_FRAME_LENGTH) { /* can't happen */ +#ifdef ET_DEBUG + dprintf("err: packet too big\n"); +#endif + } else { + fp = (struct icn_frame *)(MyRxPackets[keymile_rxIdx] + + INET_HDR_ALIGN - INET_HDR_SIZE); +#ifdef TEST_RX + dprintf("RX %d: ", keymile_rxIdx); + hexdump((unsigned char *)MyRxPackets[keymile_rxIdx], + INET_HDR_ALIGN + INET_HDR_SIZE + 4); +#endif + /* copy icn header to the beginning */ + memcpy(fp, ((char *)fp + INET_HDR_SIZE), + sizeof(struct icn_hdr)); + app = fp->hdr.application; + if (app == MGS_NETCONS || app == MGS_TFTP) { + struct icn_hdr *ih = &fp->hdr; + unsigned char icn_src_addr = ih->src_addr; + unsigned char icn_dest_addr = ih->dest_addr; + + /* + * expand header by INET_HDR_SIZE + */ + length += INET_HDR_SIZE; + /* initalize header */ + memset((char *)fp->data, 0x00, INET_HDR_SIZE); + ep = (Ethernet_t *)fp->data; + /* set MACs */ + ep->et_dest[0] = icn_dest_addr; + ep->et_src[0] = icn_src_addr; + ep->et_protlen = htons(PROT_IP); + /* set ip stuff */ + ip = (IP_t *)(fp->data + ETHER_HDR_SIZE); + /* set ip addresses */ + ip->ip_src = cachedNumbers[IP_SERVER]; + ip->ip_dst = cachedNumbers[IP_ADDR]; + /* ip length */ + ip->ip_len = htons(length - ETHER_HDR_SIZE - + REMOVE); + /* ip proto */ + ip->ip_p = IPPROTO_UDP; + switch (app) { + case MGS_TFTP: + /* swap src/dst port numbers */ + ip->udp_src = (ushort) + cachedNumbers[TFTP_DST_PORT]; + ip->udp_dst = (ushort) + cachedNumbers[TFTP_SRC_PORT]; + ip->udp_len = ip->ip_len - + IP_HDR_SIZE_NO_UDP; + ip->udp_xsum = 0; + break; + case MGS_NETCONS: + ip->udp_src = (ushort) + cachedNumbers[NETCONS_PORT]; + /* + * in drivers/net/netconsole.c src port + * equals dest port + */ + ip->udp_dst = ip->udp_src; + ip->udp_len = ip->ip_len - + IP_HDR_SIZE_NO_UDP; + ip->udp_xsum = 0; + break; + } + /* ip version */ + ip->ip_hl_v = (0x40) | (0x0f & + (IP_HDR_SIZE_NO_UDP / 4)); + ip->ip_tos = 0; + ip->ip_id = 0; + /* flags, fragment offset */ + ip->ip_off = htons(0x4000); + ip->ip_ttl = 255; /* time to live */ + /* have to fixup the checksum */ + ip->ip_sum = ~NetCksum((uchar *)ip, + IP_HDR_SIZE_NO_UDP / 2); + /* + * Pass the packet up to the protocol layers + * but remove dest_addr, src_addr, application + * and the CRC. + */ +#ifdef TEST_RX + hexdump((unsigned char *)fp->data, + INET_HDR_SIZE + 4); +#endif + NetReceive(fp->data, length - REMOVE); + } else { + /* + * the other application types are not yet + * supported by u-boot. + */ + /* normally drop it */ +#ifdef TEST_NO + /* send it anyway */ + fp = (struct icn_frame *) + (MyRxPackets[keymile_rxIdx] + + INET_HDR_ALIGN); + NetReceive(fp->data, length - REMOVE); +#endif + } + } + + /* Give the buffer back to the SCC. */ + rtx->rxbd[keymile_rxIdx].cbd_datlen = 0; + + /* wrap around buffer index when necessary */ + if ((keymile_rxIdx + 1) >= HDLC_PKTBUFSRX) { + rtx->rxbd[HDLC_PKTBUFSRX - 1].cbd_sc = + (BD_SC_WRAP | BD_SC_EMPTY); + keymile_rxIdx = 0; + } else { + rtx->rxbd[keymile_rxIdx].cbd_sc = BD_SC_EMPTY; + keymile_rxIdx++; + } + } + return length; +} + +#ifdef TEST_IT +/* simple send test routine */ +int hdlc_enet_stest(struct cmd_tbl_s *a, int b, int c, char **d) +{ + unsigned char pkt[2]; + int ret; + + dprintf("enter stest\n"); + /* may have to initialize things */ + if (seth->state != ETH_STATE_ACTIVE) { + /* the bd_t* is not used */ + if (seth->init(seth, NULL) >= 0) + seth->state = ETH_STATE_ACTIVE; + } + pkt[0] = 0xea; + pkt[1] = 0xae; + ret = keymile_hdlc_enet_send(seth, pkt, 2); + dprintf("return from send %x\n", ret); + dprintf("exit stest\n"); + return ret; +} +U_BOOT_CMD( + stest, 1, 1, hdlc_enet_stest, + "stest - simple send test for hdlc_enet\n", + "no arguments\n" +); +/* simple receive test routine */ +int hdlc_enet_rtest(struct cmd_tbl_s *a, int b, int c, char **d) +{ + int ret; + + dprintf("enter rtest\n"); + /* may have to initialize things */ + if (seth->state != ETH_STATE_ACTIVE) { + /* the bd_t* is not used */ + if (seth->init(seth, NULL) >= 0) + seth->state = ETH_STATE_ACTIVE; + } + ret = keymile_hdlc_enet_recv(seth); + dprintf("return from recv %x\n", ret); + dprintf("exit rtest\n"); + return ret; +} +U_BOOT_CMD( + rtest, 1, 1, hdlc_enet_rtest, + "rtest - simple receive test for hdlc_enet\n", + "no arguments\n" +); +#endif + +#endif /* CONFIG_KEYMILE_HDLC_ENET */ diff --git a/board/keymile/common/keymile_hdlc_enet.h b/board/keymile/common/keymile_hdlc_enet.h new file mode 100644 index 0000000..965bd5a --- /dev/null +++ b/board/keymile/common/keymile_hdlc_enet.h @@ -0,0 +1,129 @@ +/* + * (C) Copyright 2008 + * Gary Jennejohn, DENX Software Engineering GmbH, garyj@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 + */ + +#ifndef _KEYMILE_HDLC_ENET_H_ +#define _KEYMILE_HDLC_ENET_H_ + +/* Unfortuantely, we have do this to get the flag defines in the cbd_t */ +#ifdef CONFIG_MGSUVD +#include <commproc.h> +#endif +#ifdef CONFIG_MGCOGE +#include <mpc8260.h> +#include <asm/cpm_8260.h> +#endif + +/* + * Defines for the ICN protocol used for communication over HDLC + * on the backplane between MGSUVDs and MGCOGEs. + */ + +/* + * MAC which is reserved for communication (0x00 - 0xff in the last byte, + * which is the slot number) + */ + +/* + * A DLL frame looks like this: + * 8 bit | 8 bit | 8 bit | 8 bit | n * 8 bit| 16 bit| 8 bit + * opening| destination| source | application| data | FCS | closing + * flag | address | address| | | | flag + * (HW) (APP) (APP) (APP) (APP) (HW) (HW) + */ + +/* + * The opening flag, the FCS and the closing flag are set by the hardware so + * they are not reflected in this struct. + */ +struct icn_hdr { + unsigned char dest_addr; + unsigned char src_addr; + unsigned char application; +} __attribute__((packed)); + +#define ICNHDR_LEN (sizeof(struct icn_hdr)) +#define CRC_LEN (sizeof(short)) +/* bytes to remove from packet before sending it upstream */ +#define REMOVE (ICNHDR_LEN + CRC_LEN) + +struct icn_frame { + struct icn_hdr hdr; + unsigned char data[0]; /* a place holder */ +} __attribute__((packed)); + +/* Address field */ +#define HDLC_UUA 0x00 /* Unicast Unit Address */ +#define HDLC_UUA_MASK 0x3f /* the last 6 bits contain the slot number */ +#define SET_HDLC_UUA(x) ((HDLC_UUA | ((x) & HDLC_UUA_MASK))) +#define HDLC_UACUA 0x7f /* Unicast Active Control Unit Address */ +#define HDLC_BCAST 0xff /* broadcast */ + +/* Application field */ +#define MGS_UUSP 0x00 +#define MGS_UREP 0x01 +#define MGS_IUP 0x02 +#define MGS_UTA 0x03 +#define MGS_MDS 0x04 +#define MGS_ITIME 0x05 +/* added by DENX */ +#define MGS_NETCONS 0x06 /* netconsole */ +#define MGS_TFTP 0x07 + +/* Useful defines for buffer sizes, etc. */ +#define HDLC_PKTBUFSRX 32 +#define MAX_FRAME_LENGTH 1500 /* ethernet frame size */ + /* 14 + 28 */ +#define INET_HDR_SIZE (ETHER_HDR_SIZE + IP_HDR_SIZE) +#define INET_HDR_ALIGN (((INET_HDR_SIZE + PKTALIGN - 1) / PKTALIGN) * PKTALIGN) +/* INET_HDR_SIZE is stripped off */ +#define PKT_MAXBLR_SIZE (MAX_FRAME_LENGTH + INET_HDR_ALIGN) + +/* + * It is too slow to read always the port numbers and IP addresses from the + * string variables. + * cachedNumbers is meant to cache it. + * THIS IS ONLY A SPEED IMPROVEMENT! + */ +enum { + IP_ADDR = 0, /* getenv_IPaddr("serverip"); */ + IP_SERVER, /* getenv_IPaddr("ipaddr"); */ + TFTP_SRC_PORT, /* simple_strtol(getenv("tftpsrcp"), NULL, 10); */ + TFTP_DST_PORT, /* simple_strtol(getenv("tftpdstp"), NULL, 10); */ + NETCONS_PORT, /* simple_strtol(getenv("ncip"), NULL, 10); */ + CACHEDNUMBERS +}; + +#define WELL_KNOWN_PORT 69 /* Well known TFTP port # */ + +/* define this to create a test commend (htest) */ +#undef TEST_IT +#ifdef TEST_IT +/* have to save a copy of the eth_device for the test command's use */ +struct eth_device *seth; +#endif +/* define this for outputting of received packets */ +#undef TEST_RX +/* define this for outputting of packets being sent */ +#undef TEST_TX + +#endif /* _KEYMILE_HDLC_ENET_H_ */ diff --git a/board/keymile/mgcoge/Makefile b/board/keymile/mgcoge/Makefile index 0cad821..2774a70 100644 --- a/board/keymile/mgcoge/Makefile +++ b/board/keymile/mgcoge/Makefile @@ -28,7 +28,8 @@ endif LIB = $(obj)lib$(BOARD).a -COBJS := $(BOARD).o ../common/common.o +COBJS := $(BOARD).o ../common/common.o ../common/keymile_hdlc_enet.o \ + mgcoge_hdlc_enet.o SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS)) diff --git a/board/keymile/mgcoge/mgcoge_hdlc_enet.c b/board/keymile/mgcoge/mgcoge_hdlc_enet.c new file mode 100644 index 0000000..34f04f5 --- /dev/null +++ b/board/keymile/mgcoge/mgcoge_hdlc_enet.c @@ -0,0 +1,276 @@ +/* + * (C) Copyright 2008 + * Gary Jennejohn, DENX Software Engineering GmbH, garyj@denx.de. + * + * Based in part on cpu/mpc8260/ether_scc.c. + * + * 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 <malloc.h> +#include <net.h> + +#ifdef CONFIG_KEYMILE_HDLC_ENET + +#include "../common/keymile_hdlc_enet.h" + +char keymile_slot; /* our slot number in the backplane */ + +/* + * Since, except during initialization, ethact is always HDLC ETHERNET + * while we're in the driver, just use serial_printf() everywhere for + * output. This avoids possible conflicts when netconsole is being + * used. + */ +#define dprintf(fmt, args...) serial_printf(fmt, ##args) + +static int already_inited; + +/* + * SCC Ethernet Tx and Rx buffer descriptors allocated at the + * immr->udata_bd address on Dual-Port RAM + * Provide for Double Buffering + */ +typedef volatile struct CommonBufferDescriptor { + cbd_t txbd; /* Tx BD */ + cbd_t rxbd[HDLC_PKTBUFSRX]; /* Rx BD */ +} RTXBD; + +static RTXBD *rtx; + +int keymile_hdlc_enet_init(struct eth_device *, bd_t *); +void keymile_hdlc_enet_halt(struct eth_device *); +extern void keymile_hdlc_enet_init_bds(RTXBD *); +extern void initCachedNumbers(int); + +/* Use SCC1 */ +#define CPM_CR_SCC_PAGE CPM_CR_SCC1_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC1_SBLOCK +#define CMXSCR_MASK (CMXSCR_GR1|CMXSCR_SC1|\ + CMXSCR_RS1CS_MSK|CMXSCR_TS1CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS1CS_CLK11|CMXSCR_TS1CS_CLK11) +#define MGC_PROFF_HDLC PROFF_SCC1 +#define MGC_SCC_HDLC 0 /* Index, not number! */ + +int keymile_hdlc_enet_init(struct eth_device *dev, bd_t *bis) +{ + /* int i; */ + uint dpr; + /* volatile cbd_t *bdp; */ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile cpm8260_t *cp = &(im->im_cpm); + volatile scc_t *sccp; + volatile scc_hdlc_t *hpr; + volatile iop8260_t *iop; + + if (already_inited) + return 0; + + hpr = (scc_hdlc_t *)(&im->im_dprambase[MGC_PROFF_HDLC]); + sccp = (scc_t *)(&im->im_scc[MGC_SCC_HDLC]); + iop = &im->im_ioport; + + /* + * Disable receive and transmit just in case. + */ + sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* + * Avoid exhausting DPRAM, which would cause a panic. + */ + if (rtx == NULL) { + /* dpr is an offset into dpram */ + dpr = m8260_cpm_dpalloc(sizeof(RTXBD), 8); + rtx = (RTXBD *)&im->im_dprambase[dpr]; + } + + /* We need the slot number for addressing. */ + keymile_slot = *(char *)(CONFIG_SYS_SLOT_ID_BASE + + CONFIG_SYS_SLOT_ID_OFF) & CONFIG_SYS_SLOT_ID_MASK; + /* + * Be consistent with the Linux driver and set + * only enetaddr[0]. + * + * Always add 1 to the slot number so that + * there are no problems with an ethaddr which + * is all 0s. This should be acceptable because + * a board should never have a slot number of 255, + * which is the broadcast address. The HDLC addressing + * uses only the slot number. + */ + dev->enetaddr[0] = keymile_slot + 1; +#ifdef TEST_IT + dprintf("slot %d\n", keymile_slot); +#endif + + /* use pd30, pd31 pins for TXD1, RXD1 respectively */ + iop->iop_ppard |= (0x80000000 >> 30) | (0x80000000 >> 31); + iop->iop_pdird |= (0x80000000 >> 30); + iop->iop_psord |= (0x80000000 >> 30); + + /* use pc21 as CLK11 */ + iop->iop_pparc |= (0x80000000 >> 21); + iop->iop_pdirc &= ~(0x80000000 >> 21); + iop->iop_psorc &= ~(0x80000000 >> 21); + + /* use pc15 as CTS1 */ + iop->iop_pparc |= (0x80000000 >> 15); + iop->iop_pdirc &= ~(0x80000000 >> 15); + iop->iop_psorc &= ~(0x80000000 >> 15); + + /* + * SI clock routing + * use CLK11 + * this also connects SCC1 to NMSI + */ + im->im_cpmux.cmx_scr = (im->im_cpmux.cmx_scr & ~CMXSCR_MASK) | + CMXSCR_VALUE; + + /* keymile_rxIdx = 0; */ + + /* + * Initialize function code registers for big-endian. + */ + hpr->sh_genscc.scc_rfcr = CPMFCR_EB; + hpr->sh_genscc.scc_tfcr = CPMFCR_EB; + + /* + * Set maximum bytes per receive buffer. + */ + hpr->sh_genscc.scc_mrblr = MAX_FRAME_LENGTH; + + /* Setup CRC generator values for HDLC */ + hpr->sh_cmask = 0x0000F0B8; + hpr->sh_cpres = 0x0000FFFF; + + /* Initialize all error counters to 0 */ + hpr->sh_disfc = 0; + hpr->sh_crcec = 0; + hpr->sh_abtsc = 0; + hpr->sh_nmarc = 0; + hpr->sh_retrc = 0; + + /* Set maximum frame length size */ + hpr->sh_mflr = MAX_FRAME_LENGTH; + + /* set to 1 for per frame processing change later if needed */ + hpr->sh_rfthr = 1; + + hpr->sh_hmask = 0xff; + + hpr->sh_haddr2 = SET_HDLC_UUA(keymile_slot); + hpr->sh_haddr3 = hpr->sh_haddr2; + hpr->sh_haddr4 = hpr->sh_haddr2; + /* broadcast */ + hpr->sh_haddr1 = HDLC_BCAST; + + hpr->sh_genscc.scc_rbase = (unsigned int) &rtx->rxbd[0]; + hpr->sh_genscc.scc_tbase = (unsigned int) &rtx->txbd; + +#if 0 + /* + * Initialize the buffer descriptors. + */ + bdp = &rtx->txbd; + bdp->cbd_sc = 0; + bdp->cbd_bufaddr = 0; + bdp->cbd_sc = BD_SC_WRAP; + + /* + * Setup RX packet buffers, aligned correctly. + * Borrowed from net/net.c. + */ + MyRxPackets[0] = &MyPktBuf[0] + (PKTALIGN - 1); + MyRxPackets[0] -= (ulong)MyRxPackets[0] % PKTALIGN; + for (i = 1; i < HDLC_PKTBUFSRX; i++) + MyRxPackets[i] = MyRxPackets[0] + i * PKT_MAXBLR_SIZE; + + bdp = &rtx->rxbd[0]; + for (i = 0; i < HDLC_PKTBUFSRX; i++) { + bdp->cbd_sc = BD_SC_EMPTY; + /* Leave space at the start for INET header. */ + bdp->cbd_bufaddr = (unsigned int)(MyRxPackets[i] + + INET_HDR_ALIGN); + bdp++; + } + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; +#else + keymile_hdlc_enet_init_bds(rtx); +#endif + + /* Let's re-initialize the channel now. We have to do it later + * than the manual describes because we have just now finished + * the BD initialization. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_SCC_PAGE, CPM_CR_SCC_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + sccp->scc_gsmrl = SCC_GSMRL_MODE_HDLC; + /* CTSS=1 */ + sccp->scc_gsmrh = SCC_GSMRH_CTSS; + /* NOF=0, RTE=1, DRT=0, BUS=1 */ + sccp->scc_psmr = ((0x8000 >> 6) | (0x8000 >> 10)); + +/* loopback for local testing */ +#ifdef GJTEST + dprintf("LOOPBACK!\n"); + sccp->scc_gsmrl |= SCC_GSMRL_DIAG_LOOP; +#endif + + /* + * Disable all interrupts and clear all pending + * events. + */ + sccp->scc_sccm = 0; + sccp->scc_scce = 0xffff; + + /* + * And last, enable the transmit and receive processing. + */ + sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + dprintf("%s: HDLC ENET Version 0.3 on SCC%d\n", dev->name, + MGC_SCC_HDLC + 1); + + /* + * We may not get an ARP packet because ARP was already done on + * a different interface, so initialize the cached values now. + */ + initCachedNumbers(1); + + already_inited = 1; + + return 0; +} + +void keymile_hdlc_enet_halt(struct eth_device *dev) +{ +#if 0 /* just return, but keep this for reference */ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + + /* maybe should do a graceful stop here? */ + immr->im_scc[MGC_SCC_HDLC].scc_gsmrl &= + ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); +#endif +} + +#endif /* CONFIG_MGCOGE_HDLC_ENET */ diff --git a/board/keymile/mgsuvd/Makefile b/board/keymile/mgsuvd/Makefile index b2145f9..2c5732d 100644 --- a/board/keymile/mgsuvd/Makefile +++ b/board/keymile/mgsuvd/Makefile @@ -28,7 +28,8 @@ endif LIB = $(obj)lib$(BOARD).a -COBJS = $(BOARD).o ../common/common.o +COBJS = $(BOARD).o ../common/common.o ../common/keymile_hdlc_enet.o \ + mgsuvd_hdlc_enet.o SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS)) diff --git a/board/keymile/mgsuvd/mgsuvd_hdlc_enet.c b/board/keymile/mgsuvd/mgsuvd_hdlc_enet.c new file mode 100644 index 0000000..9b93131 --- /dev/null +++ b/board/keymile/mgsuvd/mgsuvd_hdlc_enet.c @@ -0,0 +1,278 @@ +/* + * (C) Copyright 2008 + * Gary Jennejohn, DENX Software Engineering GmbH, garyj@denx.de. + * + * Based in part on cpu/mpc8xx/scc.c. + * + * 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> /* commproc.h is included here */ +#include <malloc.h> +#include <net.h> + +#ifdef CONFIG_KEYMILE_HDLC_ENET + +#include "../common/keymile_hdlc_enet.h" + +char keymile_slot; /* our slot number in the backplane */ + +/* + * Since, except during initialization, ethact is always HDLC ETHERNET + * while we're in the driver, just use serial_printf() everywhere for + * output. This avoids possible conflicts when netconsole is being + * used. + */ +#define dprintf(fmt, args...) serial_printf(fmt, ##args) + +static int already_inited; + +/* + * SCC Ethernet Tx and Rx buffer descriptors allocated at the + * immr->udata_bd address on Dual-Port RAM + * Provide for Double Buffering + */ +typedef volatile struct CommonBufferDescriptor { + cbd_t txbd; /* Tx BD */ + cbd_t rxbd[HDLC_PKTBUFSRX]; /* Rx BD */ +} RTXBD; + +static RTXBD *rtx; + +int keymile_hdlc_enet_init(struct eth_device *, bd_t *); +void keymile_hdlc_enet_halt(struct eth_device *); +extern void keymile_hdlc_enet_init_bds(RTXBD *); +extern void initCachedNumbers(int); + +/* Use SCC4 */ +#define MGS_CPM_CR_HDLC CPM_CR_CH_SCC4 +#define MGS_PROFF_HDLC PROFF_SCC4 +#define MGS_SCC_HDLC 3 /* Index, not number! */ + +int keymile_hdlc_enet_init(struct eth_device *dev, bd_t *bis) +{ + /* int i; */ + /* volatile cbd_t *bdp; */ + volatile cpm8xx_t *cp; + volatile scc_t *sccp; + volatile hdlc_pram_t *hpr; + volatile iop8xx_t *iop; + + if (already_inited) + return 0; + + cp = (cpm8xx_t *)&(((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm); + hpr = (hdlc_pram_t *)(&cp->cp_dparam[MGS_PROFF_HDLC]); + sccp = (volatile scc_t *)(&cp->cp_scc[MGS_SCC_HDLC]); + iop = (iop8xx_t *)&(((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport); + + /* + * Disable receive and transmit just in case. + */ + sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + +#ifndef CONFIG_SYS_ALLOC_DPRAM +#error "CONFIG_SYS_ALLOC_DPRAM must be defined" +#else + /* + * Avoid exhausting DPRAM, which would cause a panic. + * Actually this isn't really necessary, but leave it here + * for safety's sake. + */ + if (rtx == NULL) { + rtx = (RTXBD *) (cp->cp_dpmem + + dpram_alloc_align(sizeof(RTXBD), 8)); + if (rtx == (RTXBD *)CPM_DP_NOSPACE) + return -1; + memset((void *)rtx, 0, sizeof(RTXBD)); + } +#endif /* !CONFIG_SYS_ALLOC_DPRAM */ + + /* We need the slot number for addressing. */ + keymile_slot = *(char *)(CONFIG_SYS_SLOT_ID_BASE + + CONFIG_SYS_SLOT_ID_OFF) & CONFIG_SYS_SLOT_ID_MASK; + /* + * Be consistent with the Linux driver and set + * only enetaddr[0]. + * + * Always add 1 to the slot number so that + * there are no problems with an ethaddr which + * is all 0s. This should be acceptable because + * a board should never have a slot number of 255, + * which is the broadcast address. The HDLC addressing + * uses only the slot number. + */ + dev->enetaddr[0] = keymile_slot + 1; + +#ifdef TEST_IT + dprintf("slot %d\n", keymile_slot); +#endif + + /* use pa8, pa9 pins for TXD4, RXD4 respectively */ + iop->iop_papar |= ((0x8000 >> 8) | (0x8000 >> 9)); + iop->iop_padir &= ~((0x8000 >> 8) | (0x8000 >> 9)); + iop->iop_paodr &= ~((0x8000 >> 8) | (0x8000 >> 9)); + + /* also use pa0 as CLK8 */ + iop->iop_papar |= 0x8000; + iop->iop_padir &= ~0x8000; + iop->iop_paodr &= ~0x8000; + + /* use pc5 as CTS4 */ + iop->iop_pcpar &= ~(0x8000 >> 5); + iop->iop_pcdir &= ~(0x8000 >> 5); + iop->iop_pcso |= (0x8000 >> 5); + + /* + * SI clock routing + * use CLK8 + * this also connects SCC4 to NMSI + */ + cp->cp_sicr = (cp->cp_sicr & ~0xff000000) | 0x3f000000; + + /* keymile_rxIdx = 0; */ + + /* + * Initialize function code registers for big-endian. + */ + hpr->rfcr = SCC_EB; + hpr->tfcr = SCC_EB; + + /* + * Set maximum bytes per receive buffer. + */ + hpr->mrblr = MAX_FRAME_LENGTH; + + /* Setup CRC generator values for HDLC */ + hpr->c_mask = 0x0000F0B8; + hpr->c_pres = 0x0000FFFF; + + /* Initialize all error counters to 0 */ + hpr->disfc = 0; + hpr->crcec = 0; + hpr->abtsc = 0; + hpr->nmarc = 0; + hpr->retrc = 0; + + /* Set maximum frame length size */ + hpr->mflr = MAX_FRAME_LENGTH; + + /* set to 1 for per frame processing change later if needed */ + hpr->rfthr = 1; + + hpr->hmask = 0xff; + + hpr->haddr2 = SET_HDLC_UUA(keymile_slot); + hpr->haddr3 = hpr->haddr2; + hpr->haddr4 = hpr->haddr2; + /* broadcast */ + hpr->haddr1 = HDLC_BCAST; + + hpr->rbase = (unsigned int) &rtx->rxbd[0]; + hpr->tbase = (unsigned int) &rtx->txbd; + +#if 0 + /* + * Initialize the buffer descriptors. + */ + bdp = &rtx->txbd; + bdp->cbd_sc = 0; + bdp->cbd_bufaddr = 0; + bdp->cbd_sc = BD_SC_WRAP; + + /* + * Setup RX packet buffers, aligned correctly. + * Borrowed from net/net.c. + */ + MyRxPackets[0] = &MyPktBuf[0] + (PKTALIGN - 1); + MyRxPackets[0] -= (ulong)MyRxPackets[0] % PKTALIGN; + for (i = 1; i < HDLC_PKTBUFSRX; i++) + MyRxPackets[i] = MyRxPackets[0] + i * PKT_MAXBLR_SIZE; + + bdp = &rtx->rxbd[0]; + for (i = 0; i < HDLC_PKTBUFSRX; i++) { + bdp->cbd_sc = BD_SC_EMPTY; + /* Leave space at the start for INET header. */ + bdp->cbd_bufaddr = (unsigned int)(MyRxPackets[i] + + INET_HDR_ALIGN); + bdp++; + } + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; +#else + keymile_hdlc_enet_init_bds(rtx); +#endif + + /* Let's re-initialize the channel now. We have to do it later + * than the manual describes because we have just now finished + * the BD initialization. + */ + cp->cp_cpcr = mk_cr_cmd(MGS_CPM_CR_HDLC, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + sccp->scc_gsmrl = SCC_GSMRL_MODE_HDLC; + /* CTSS=1 */ + sccp->scc_gsmrh = SCC_GSMRH_CTSS; + /* NOF=0, RTE=1, DRT=0, BUS=1 */ + sccp->scc_psmr = ((0x8000 >> 6) | (0x8000 >> 10)); + +/* loopback for local testing */ +#ifdef GJTEST + dprintf("LOOPBACK!\n"); + sccp->scc_gsmrl |= SCC_GSMRL_DIAG_LOOP; +#endif + + /* + * Disable all interrupts and clear all pending + * events. + */ + sccp->scc_sccm = 0; + sccp->scc_scce = 0xffff; + + /* + * And last, enable the transmit and receive processing. + */ + sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + dprintf("%s: HDLC ENET Version 0.3 on SCC%d\n", dev->name, + MGS_SCC_HDLC + 1); + + /* + * We may not get an ARP packet because ARP was already done on + * a different interface, so initialize the cached values now. + */ + initCachedNumbers(1); + + already_inited = 1; + + return 0; +} + +void keymile_hdlc_enet_halt(struct eth_device *dev) +{ +#if 0 /* just return, but keep this for reference */ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + + /* maybe should do a graceful stop here? */ + immr->im_cpm.cp_scc[MGS_SCC_HDLC].scc_gsmrl &= + ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); +#endif +} + +#endif /* CONFIG_KEYMILE_HDLC_ENET */ |