mgsuvd add the board-specific part of the HDLC driver
authorGary Jennejohn <garyj@denx.de>
Sun, 9 Nov 2008 11:50:59 +0000 (12:50 +0100)
committerWolfgang Denk <wd@denx.de>
Sun, 7 Dec 2008 00:33:56 +0000 (01:33 +0100)
Signed-off-by: Gary Jennejohn <garyj@denx.de>
board/keymile/mgsuvd/Makefile
board/keymile/mgsuvd/mgsuvd_hdlc_enet.c [new file with mode: 0644]

index b2145f9ff7043831c4f543f8ef98e42ca7ee6d0b..2c5732d16b196f84fd13a63b67d463e9869c6a97 100644 (file)
@@ -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 (file)
index 0000000..9b93131
--- /dev/null
@@ -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 */