U-Boot
Threads by month
- ----- 2025 -----
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2000 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
December 2008
- 172 participants
- 485 discussions

[U-Boot] [patch 2/2] Cleanup patch Fat on non standard sector size devices
by Remy Bohmer 07 Dec '08
by Remy Bohmer 07 Dec '08
07 Dec '08
The patch below is a patch originally posted by Igor Luri.
However, the patch posted on the mailinglist was not readable (base64)
and thus not mergable or testable. Also tabs/spaces were malformed.
So, I merged the whole patch by hand and posted it again.
Igor can you please check if I did this cleanup properly.
Notice that the code that is being modified here does not match _any_
coding rule standard and is actually a big mess. So, I did not focus
on the Coding Rules too much for this patch.
I will post a seperate patch to fix this for the complete files also.
----------------
There are USB devices with sector size different than 512 bytes. In
fact, I have one with 2048 bytes per sector:
scsi0 : SCSI emulation for USB Mass Storage devices
Vendor: ChipsBnk Model: Flash Disk Rev: 4.00
Type: Direct-Access ANSI SCSI revision: 02
Attached scsi removable disk sda at scsi0, channel 0, id 0, lun 0
SCSI device sda: 129124 2048-byte hdwr sectors (264 MB)
sda: Write Protect is off
Partition check:
sda: sda1 sda2 sda3 sda4
U-boot FAT filesystem code supposes that sector size is always 512
bytes, consecuently fatload and fatls commands ends up with a
segmentation fault.
Attached patches adds FAT support on devices with sector size up to 8192
bytes.
Signed-off-by: Igor Luri <iluri(a)aotek.es>
Signed-off-by: Remy Bohmer <linux(a)bohmer.net>
---
fs/fat/fat.c | 89 ++++++++++++++++++++++++++++++++++++++++------------------
include/fat.h | 18 +++--------
2 files changed, 68 insertions(+), 39 deletions(-)
Index: u-boot-usb.new/fs/fat/fat.c
===================================================================
--- u-boot-usb.new.orig/fs/fat/fat.c 2008-11-27 21:40:25.000000000 +0100
+++ u-boot-usb.new/fs/fat/fat.c 2008-11-27 22:10:40.000000000 +0100
@@ -30,6 +30,7 @@
#include <fat.h>
#include <asm/byteorder.h>
#include <part.h>
+#include <malloc.h>
/*
* Convert a string to lowercase.
@@ -67,12 +68,17 @@ int disk_read (__u32 startblock, __u32 g
int
fat_register_device(block_dev_desc_t *dev_desc, int part_no)
{
- unsigned char buffer[SECTOR_SIZE];
+ unsigned char *buffer;
disk_partition_t info;
if (!dev_desc->block_read)
return -1;
cur_dev = dev_desc;
+
+ buffer = malloc(cur_dev->blksz);
+ if (!buffer)
+ return -1;
+
/* check if we have a MBR (on floppies we have only a PBR) */
if (dev_desc->block_read (dev_desc->dev, 0, 1, (ulong *) buffer) != 1) {
printf ("** Can't read from device %d **\n", dev_desc->dev);
@@ -118,6 +124,7 @@ fat_register_device(block_dev_desc_t *de
cur_part = 1;
}
#endif
+ free(buffer);
return 0;
}
@@ -220,12 +227,12 @@ get_fatent(fsdata *mydata, __u32 entry)
/* Read a new block of FAT entries into the cache. */
if (bufnum != mydata->fatbufnum) {
- int getsize = FATBUFSIZE/FS_BLOCK_SIZE;
+ int getsize = FATBUFSIZE / mydata->sector_size;
__u8 *bufptr = mydata->fatbuf;
__u32 fatlength = mydata->fatlength;
__u32 startblock = bufnum * FATBUFBLOCKS;
- fatlength *= SECTOR_SIZE; /* We want it in bytes now */
+ fatlength *= mydata->sector_size; /* We want it in bytes now */
startblock += mydata->fat_sect; /* Offset from start of disk */
if (getsize > fatlength) getsize = fatlength;
@@ -300,20 +307,25 @@ get_cluster(fsdata *mydata, __u32 clustn
}
FAT_DPRINT("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
- if (disk_read(startsect, size/FS_BLOCK_SIZE , buffer) < 0) {
+ if (disk_read(startsect, size / mydata->sector_size, buffer) < 0) {
FAT_DPRINT("Error reading data\n");
return -1;
}
- if(size % FS_BLOCK_SIZE) {
- __u8 tmpbuf[FS_BLOCK_SIZE];
- idx= size/FS_BLOCK_SIZE;
+ if (size % mydata->sector_size) {
+ __u8 *tmpbuf;
+
+ tmpbuf = (__u8 *) malloc(mydata->sector_size);
+ if (!tmpbuf)
+ return -1;
+ idx = size / mydata->sector_size;
if (disk_read(startsect + idx, 1, tmpbuf) < 0) {
FAT_DPRINT("Error reading data\n");
return -1;
}
- buffer += idx*FS_BLOCK_SIZE;
+ buffer += idx * mydata->sector_size;
- memcpy(buffer, tmpbuf, size % FS_BLOCK_SIZE);
+ memcpy(buffer, tmpbuf, size % mydata->sector_size);
+ free(tmpbuf);
return 0;
}
@@ -331,7 +343,7 @@ get_contents(fsdata *mydata, dir_entry *
unsigned long maxsize)
{
unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
- unsigned int bytesperclust = mydata->clust_size * SECTOR_SIZE;
+ unsigned int bytesperclust = mydata->clust_size * mydata->sector_size;
__u32 curclust = START(dentptr);
__u32 endclust, newclust;
unsigned long actsize;
@@ -440,7 +452,8 @@ get_vfatname(fsdata *mydata, int curclus
{
dir_entry *realdent;
dir_slot *slotptr = (dir_slot*) retdent;
- __u8 *nextclust = cluster + mydata->clust_size * SECTOR_SIZE;
+ __u8 *nextclust = cluster +
+ mydata->clust_size * mydata->sector_size;
__u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
int idx = 0;
@@ -463,7 +476,7 @@ get_vfatname(fsdata *mydata, int curclus
return -1;
}
if (get_cluster(mydata, curclust, get_vfatname_block,
- mydata->clust_size * SECTOR_SIZE) != 0) {
+ mydata->clust_size * mydata->sector_size) != 0) {
FAT_DPRINT("Error: reading directory block\n");
return -1;
}
@@ -533,8 +546,8 @@ static dir_entry *get_dentfromdir (fsdat
dir_entry *dentptr;
int i;
- if (get_cluster (mydata, curclust, get_dentfromdir_block,
- mydata->clust_size * SECTOR_SIZE) != 0) {
+ if (get_cluster(mydata, curclust, get_dentfromdir_block,
+ mydata->clust_size * mydata->sector_size) != 0) {
FAT_DPRINT ("Error: reading directory block\n");
return NULL;
}
@@ -667,9 +680,16 @@ static dir_entry *get_dentfromdir (fsdat
static int
read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
{
- __u8 block[FS_BLOCK_SIZE];
+ __u8 *block;
volume_info *vistart;
+ if (!cur_dev)
+ return -1;
+
+ block = (__u8 *)malloc(cur_dev->blksz);
+ if (!block)
+ return -1;
+
if (disk_read(0, 1, block) < 0) {
FAT_DPRINT("Error: reading block\n");
return -1;
@@ -680,6 +700,7 @@ read_bootsectandvi(boot_sector *bs, volu
bs->fat_length = FAT2CPU16(bs->fat_length);
bs->secs_track = FAT2CPU16(bs->secs_track);
bs->heads = FAT2CPU16(bs->heads);
+ *(__u16 *)bs->sector_size = FAT2CPU16(*(__u16 *)bs->sector_size);
#if 0 /* UNUSED */
bs->hidden = FAT2CPU32(bs->hidden);
#endif
@@ -704,7 +725,7 @@ read_bootsectandvi(boot_sector *bs, volu
/* Terminate fs_type string. Writing past the end of vistart
is ok - it's just the buffer. */
vistart->fs_type[8] = '\0';
-
+ free(block);
if (*fatsize == 32) {
if (compare_sign(FAT32_SIGN, vistart->fs_type) == 0) {
return 0;
@@ -757,6 +778,7 @@ do_fat_read (const char *filename, void
mydata->fatlength = bs.fat_length;
}
mydata->fat_sect = bs.reserved;
+ mydata->sector_size = *(__u16 *)bs.sector_size;
cursect = mydata->rootdir_sect
= mydata->fat_sect + mydata->fatlength * bs.fats;
mydata->clust_size = bs.cluster_size;
@@ -766,17 +788,21 @@ do_fat_read (const char *filename, void
- (mydata->clust_size * 2);
} else {
rootdir_size = ((bs.dir_entries[1] * (int) 256 + bs.dir_entries[0])
- * sizeof (dir_entry)) / SECTOR_SIZE;
+ * sizeof (dir_entry)) / mydata->sector_size;
mydata->data_begin = mydata->rootdir_sect + rootdir_size
- (mydata->clust_size * 2);
}
mydata->fatbufnum = -1;
+ mydata->fatbuf = (__u8 *) malloc(FATBUFSIZE);
+ if (!mydata->fatbuf)
+ return -1;
FAT_DPRINT ("FAT%d, fatlength: %d\n", mydata->fatsize,
mydata->fatlength);
FAT_DPRINT ("Rootdir begins at sector: %d, offset: %x, size: %d\n"
"Data begins at: %d\n",
- mydata->rootdir_sect, mydata->rootdir_sect * SECTOR_SIZE,
+ mydata->rootdir_sect,
+ mydata->rootdir_sect * mydata->sector_size,
rootdir_size, mydata->data_begin);
FAT_DPRINT ("Cluster size: %d\n", mydata->clust_size);
@@ -787,8 +813,10 @@ do_fat_read (const char *filename, void
strcpy (fnamecopy, filename);
downcase (fnamecopy);
if (*fnamecopy == '\0') {
- if (!dols)
- return -1;
+ if (!dols) {
+ free(mydata->fatbuf);
+ return -1;
+ }
dols = LS_ROOT;
} else if ((idx = dirdelim (fnamecopy)) >= 0) {
isdir = 1;
@@ -805,8 +833,9 @@ do_fat_read (const char *filename, void
int i;
if (disk_read (cursect, mydata->clust_size, do_fat_read_block) < 0) {
- FAT_DPRINT ("Error: reading rootdir block\n");
- return -1;
+ FAT_DPRINT ("Error: reading rootdir block\n");
+ free(mydata->fatbuf);
+ return -1;
}
dentptr = (dir_entry *) do_fat_read_block;
for (i = 0; i < DIRENTSPERBLOCK; i++) {
@@ -856,6 +885,7 @@ do_fat_read (const char *filename, void
continue;
}
} else if (dentptr->name[0] == 0) {
+ free(mydata->fatbuf);
FAT_DPRINT ("RootDentname == NULL - %d\n", i);
if (dols == LS_ROOT) {
printf ("\n%d file(s), %d dir(s)\n\n", files, dirs);
@@ -906,9 +936,10 @@ do_fat_read (const char *filename, void
dentptr++;
continue;
}
- if (isdir && !(dentptr->attr & ATTR_DIR))
+ if (isdir && !(dentptr->attr & ATTR_DIR)) {
+ free(mydata->fatbuf);
return -1;
-
+ }
FAT_DPRINT ("RootName: %s", s_name);
FAT_DPRINT (", start: 0x%x", START (dentptr));
FAT_DPRINT (", size: 0x%x %s\n",
@@ -949,20 +980,24 @@ do_fat_read (const char *filename, void
if (get_dentfromdir (mydata, startsect, subname, dentptr,
isdir ? 0 : dols) == NULL) {
+ free(mydata->fatbuf);
if (dols && !isdir)
return 0;
return -1;
}
if (idx >= 0) {
- if (!(dentptr->attr & ATTR_DIR))
- return -1;
- subname = nextname;
+ if (!(dentptr->attr & ATTR_DIR)) {
+ free(mydata->fatbuf);
+ return -1;
+ }
+ subname = nextname;
}
}
ret = get_contents (mydata, dentptr, buffer, maxsize);
FAT_DPRINT ("Size: %d, got: %ld\n", FAT2CPU32 (dentptr->size), ret);
+ free(mydata->fatbuf);
return ret;
}
Index: u-boot-usb.new/include/fat.h
===================================================================
--- u-boot-usb.new.orig/include/fat.h 2008-11-27 21:40:17.000000000 +0100
+++ u-boot-usb.new/include/fat.h 2008-11-27 21:45:13.000000000 +0100
@@ -31,20 +31,13 @@
#define CONFIG_SUPPORT_VFAT
-#define SECTOR_SIZE FS_BLOCK_SIZE
-
-#define FS_BLOCK_SIZE 512
-
-#if FS_BLOCK_SIZE != SECTOR_SIZE
-#error FS_BLOCK_SIZE != SECTOR_SIZE - This code needs to be fixed!
-#endif
-
#define MAX_CLUSTSIZE 65536
-#define DIRENTSPERBLOCK (FS_BLOCK_SIZE/sizeof(dir_entry))
-#define DIRENTSPERCLUST ((mydata->clust_size*SECTOR_SIZE)/sizeof(dir_entry))
+#define DIRENTSPERBLOCK (mydata->sector_size / sizeof(dir_entry))
+#define DIRENTSPERCLUST ((mydata->clust_size * mydata->sector_size) / \
+ sizeof(dir_entry))
#define FATBUFBLOCKS 6
-#define FATBUFSIZE (FS_BLOCK_SIZE*FATBUFBLOCKS)
+#define FATBUFSIZE (mydata->sector_size * FATBUFBLOCKS)
#define FAT12BUFSIZE ((FATBUFSIZE*2)/3)
#define FAT16BUFSIZE (FATBUFSIZE/2)
#define FAT32BUFSIZE (FATBUFSIZE/4)
@@ -182,8 +175,9 @@ typedef struct dir_slot {
* (see FAT32 accesses)
*/
typedef struct {
- __u8 fatbuf[FATBUFSIZE]; /* Current FAT buffer */
+ __u8 *fatbuf; /* Current FAT buffer */
int fatsize; /* Size of FAT in bits */
+ __u16 sector_size; /* Sector size */
__u16 fatlength; /* Length of FAT in sectors */
__u16 fat_sect; /* Starting sector of the FAT */
__u16 rootdir_sect; /* Start sector of root directory */
--
2
2
has anyone used a PCIe based SATA card w/u-boot?
- k
2
1

[U-Boot] [PATCH] SPARC: Fixed compiler error introduced by commit c160a9544743e80e8889edb2275538e7764ce334
by Daniel Hellstrom 07 Dec '08
by Daniel Hellstrom 07 Dec '08
07 Dec '08
Hi Wolfgang,
This patch fix a build error for the SPARC platform. It was introduced by commit
c160a9544743e80e8889edb2275538e7764ce334.
I have updated u-boot-sparc.git testing branch.
Best Regards,
Daniel Hellstrom
Signed-off-by: Daniel Hellstrom <daniel(a)gaisler.com>
---
lib_sparc/bootm.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/lib_sparc/bootm.c b/lib_sparc/bootm.c
index 4975759..c62cf57 100644
--- a/lib_sparc/bootm.c
+++ b/lib_sparc/bootm.c
@@ -27,6 +27,7 @@
#include <asm/byteorder.h>
#include <asm/prom.h>
#include <asm/cache.h>
+#include <image.h>
#define PRINT_KERNEL_HEADER
@@ -178,7 +179,7 @@ int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t * images)
* From now on the only code in u-boot that will be
* executed is the PROM code.
*/
- kernel(kernel_arg_promvec, (void *)ep);
+ kernel(kernel_arg_promvec, (void *)images->ep);
/* It will never come to this... */
while (1) ;
--
1.5.4
2
1

07 Dec '08
Signed-off-by: Gary Jennejohn <garyj(a)denx.de>
---
board/keymile/mgsuvd/Makefile | 3 +-
board/keymile/mgsuvd/mgsuvd_hdlc_enet.c | 278 +++++++++++++++++++++++++++++++
2 files changed, 280 insertions(+), 1 deletions(-)
create mode 100644 board/keymile/mgsuvd/mgsuvd_hdlc_enet.c
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(a)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 */
--
1.5.4.3
---
Gary Jennejohn
*********************************************************************
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office(a)denx.de
*********************************************************************
2
1

07 Dec '08
Signed-off-by: Gary Jennejohn <garyj(a)denx.de>
---
board/keymile/mgcoge/Makefile | 3 +-
board/keymile/mgcoge/mgcoge_hdlc_enet.c | 276 +++++++++++++++++++++++++++++++
2 files changed, 278 insertions(+), 1 deletions(-)
create mode 100644 board/keymile/mgcoge/mgcoge_hdlc_enet.c
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(a)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 */
--
1.5.4.3
---
Gary Jennejohn
*********************************************************************
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office(a)denx.de
*********************************************************************
2
1

07 Dec '08
This implements the ICN protocol used across the backplane and is needed
by all the keymile boards.
Signed-off-by: Gary Jennejohn <garyj(a)denx.de>
---
checkpatch.pl reports an ERROR which is false.
board/keymile/common/keymile_hdlc_enet.c | 620 ++++++++++++++++++++++++++++++
board/keymile/common/keymile_hdlc_enet.h | 129 ++++++
2 files changed, 749 insertions(+), 0 deletions(-)
create mode 100644 board/keymile/common/keymile_hdlc_enet.c
create mode 100644 board/keymile/common/keymile_hdlc_enet.h
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(a)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(a)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_ */
--
1.5.4.3
---
Gary Jennejohn
*********************************************************************
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office(a)denx.de
*********************************************************************
2
1

[U-Boot] [PATCH] video: Add new driver for Silicon Motion SM501/SM502 Part 2/2
by Stefan Althoefer 07 Dec '08
by Stefan Althoefer 07 Dec '08
07 Dec '08
[PATCH] video: Add new driver for Silicon Motion SM501/SM502
This patch adds a new driver for SM501/SM502. Compared to the
existing driver it allows dynamic selection of resolution
(environment: videomode).
The drive is based on Vincent Sanders and Ben Dooks' linux
kernel driver.
Use CONFIG_VIDEO_SM501NEW to enable the driver.
This has been tested on Janz emPC-A400. On this platform
the SM501 is connected via PCI.
The patch is against "latest" u-boot git-repository
Please (still) be patient if style of submission or patches are
offending.
Signed-off-by: Stefan Althoefer <stefan.althoefer(a)web.de>
----
diff -uprN u-boot-orig//drivers/video/Makefile u-boot/drivers/video/Makefile
--- u-boot-orig//drivers/video/Makefile 2008-12-02 17:25:31.000000000 +0100
+++ u-boot/drivers/video/Makefile 2008-12-02 18:29:14.000000000 +0100
@@ -33,6 +33,7 @@ COBJS-$(CONFIG_VIDEO_MB862xx) += mb862xx
COBJS-$(CONFIG_VIDEO_SED13806) += sed13806.o
COBJS-$(CONFIG_SED156X) += sed156x.o
COBJS-$(CONFIG_VIDEO_SM501) += sm501.o
+COBJS-$(CONFIG_VIDEO_SM501NEW) += sm501new.o
COBJS-$(CONFIG_VIDEO_SMI_LYNXEM) += smiLynxEM.o
COBJS-y += videomodes.o
diff -uprN u-boot-orig//drivers/video/sm501new.c u-boot/drivers/video/sm501new.c
--- u-boot-orig//drivers/video/sm501new.c 1970-01-01 01:00:00.000000000 +0100
+++ u-boot/drivers/video/sm501new.c 2008-12-03 11:47:22.000000000 +0100
@@ -0,0 +1,1533 @@
+/* Large parts of this have been taken from the linux kernel source code
+ * with the following licencse:
+ *
+ * Copyright (c) 2006 Simtec Electronics
+ * Vincent Sanders <vince(a)simtec.co.uk>
+ * Ben Dooks <ben(a)simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Framebuffer driver for the Silicon Motion SM501
+ */
+
+/* Ported to u-boot:
+ *
+ * Copyright (c) 2008 StefanAlthoefer (as(a)janz.de)
+ */
+
+
+#include <common.h>
+#include <exports.h>
+#include <config.h>
+#include <watchdog.h>
+#include <command.h>
+#include <image.h>
+#include <asm/byteorder.h>
+#include <pci.h>
+#include <video_fb.h>
+#include <video.h>
+#include "videomodes.h"
+#include "sm501new-regs.h"
+#include "sm501new.h"
+
+
+#ifdef CONFIG_VIDEO_SM501NEW
+
+#undef DEBUG
+
+/* this should be in pci_ids.h */
+#define PCI_DEVICE_ID_SMI_501 0x0501
+
+#define BIG_ENDIAN_HOST
+#define VIDEO_MEM_SIZE (4*1024*1024)
+
+#define SM501_FRAMEBUFFER_ADDR 0
+
+#define HEAD_CRT 0
+#define HEAD_PANEL 1
+
+#if defined(BIG_ENDIAN_HOST)
+static inline unsigned int LONGSWAP(unsigned int x)
+{
+ return (
+ ((x<<24) & 0xff000000) |
+ ((x<< 8) & 0x00ff0000) |
+ ((x>> 8) & 0x0000ff00) |
+ ((x>>24) & 0x000000ff) );
+}
+static inline unsigned int readl(void *addr)
+{
+ return LONGSWAP((*(volatile unsigned int *)(addr)));
+}
+static inline void writel(unsigned int data, void *addr)
+{
+ /*printf("%p <- %x\n", addr, data); */
+ *(volatile unsigned int *)(addr) = LONGSWAP(data);
+}
+#else
+#define readl(addr) (*(volatile unsigned int*)(addr))
+#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b))
+#endif
+
+/*
+ * Export Graphic Device
+ */
+GraphicDevice smi;
+
+
+
+static struct pci_device_id supported[] = {
+ { PCI_VENDOR_ID_SMI, PCI_DEVICE_ID_SMI_501 },
+ { }
+};
+
+
+struct sm501_devdata {
+ struct sm501_platdata *platdata;
+ unsigned long pm_misc;
+ int unit_power[20];
+ unsigned int pdev_id;
+ unsigned int irq;
+ void *regs;
+ void *dc;
+ void *vmem;
+
+ GraphicDevice *gd;
+ struct ctfb_res_modes *mode;
+ int bits_per_pixel;
+ int xres_virtual;
+ int yres_virtual;
+};
+
+struct sm501_devdata smi_devd;
+
+
+static void mdelay(unsigned int delay)
+{
+ while( delay-- > 0 ){
+ udelay(1000);
+ }
+}
+
+#define MHZ (1000 * 1000)
+
+
+#ifdef DEBUG
+#define dev_dbg(XXX,...) printf(__VA_ARGS__)
+#define dev_info(XXX,...) printf(__VA_ARGS__)
+#define dev_err(XXX,...) printf(__VA_ARGS__)
+
+static const unsigned int misc_div[] = {
+ [0] = 1,
+ [1] = 2,
+ [2] = 4,
+ [3] = 8,
+ [4] = 16,
+ [5] = 32,
+ [6] = 64,
+ [7] = 128,
+ [8] = 3,
+ [9] = 6,
+ [10] = 12,
+ [11] = 24,
+ [12] = 48,
+ [13] = 96,
+ [14] = 192,
+ [15] = 384,
+};
+
+static const unsigned int px_div[] = {
+ [0] = 1,
+ [1] = 2,
+ [2] = 4,
+ [3] = 8,
+ [4] = 16,
+ [5] = 32,
+ [6] = 64,
+ [7] = 128,
+ [8] = 3,
+ [9] = 6,
+ [10] = 12,
+ [11] = 24,
+ [12] = 48,
+ [13] = 96,
+ [14] = 192,
+ [15] = 384,
+ [16] = 5,
+ [17] = 10,
+ [18] = 20,
+ [19] = 40,
+ [20] = 80,
+ [21] = 160,
+ [22] = 320,
+ [23] = 604,
+};
+
+static unsigned long decode_div(unsigned long pll2, unsigned long val,
+ unsigned int lshft, unsigned int selbit,
+ unsigned long mask, const unsigned int *dtab)
+{
+ if (val & selbit)
+ pll2 = 288 * MHZ;
+
+ return pll2 / dtab[(val >> lshft) & mask];
+}
+
+#define fmt_freq(x) ((x) / MHZ), ((x) % MHZ), (x)
+
+/* sm501_dump_clk
+ *
+ * Print out the current clock configuration for the device
+*/
+
+static void sm501_dump_clk(struct sm501_devdata *sm)
+{
+ unsigned long misct = readl(sm->regs + SM501_MISC_TIMING);
+ unsigned long pm0 = readl(sm->regs + SM501_POWER_MODE_0_CLOCK);
+ unsigned long pm1 = readl(sm->regs + SM501_POWER_MODE_1_CLOCK);
+ unsigned long pmc = readl(sm->regs + SM501_POWER_MODE_CONTROL);
+ unsigned long sdclk0, sdclk1;
+ unsigned long pll2 = 0;
+
+ switch (misct & 0x30) {
+ case 0x00:
+ pll2 = 336 * MHZ;
+ break;
+ case 0x10:
+ pll2 = 288 * MHZ;
+ break;
+ case 0x20:
+ pll2 = 240 * MHZ;
+ break;
+ case 0x30:
+ pll2 = 192 * MHZ;
+ break;
+ }
+
+ sdclk0 = (misct & (1<<12)) ? pll2 : 288 * MHZ;
+ sdclk0 /= misc_div[((misct >> 8) & 0xf)];
+
+ sdclk1 = (misct & (1<<20)) ? pll2 : 288 * MHZ;
+ sdclk1 /= misc_div[((misct >> 16) & 0xf)];
+
+ dev_dbg(sm->dev, "MISCT=%08lx, PM0=%08lx, PM1=%08lx\n",
+ misct, pm0, pm1);
+
+ dev_dbg(sm->dev, "PLL2 = %ld.%ld MHz (%ld), SDCLK0=%08lx, SDCLK1=%08lx\n",
+ fmt_freq(pll2), sdclk0, sdclk1);
+
+ dev_dbg(sm->dev, "SDRAM: PM0=%ld, PM1=%ld\n", sdclk0, sdclk1);
+
+ dev_dbg(sm->dev, "PM0[%c]: "
+ "P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), "
+ "M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n",
+ (pmc & 3 ) == 0 ? '*' : '-',
+ fmt_freq(decode_div(pll2, pm0, 24, 1<<29, 31, px_div)),
+ fmt_freq(decode_div(pll2, pm0, 16, 1<<20, 15, misc_div)),
+ fmt_freq(decode_div(pll2, pm0, 8, 1<<12, 15, misc_div)),
+ fmt_freq(decode_div(pll2, pm0, 0, 1<<4, 15, misc_div)));
+
+ dev_dbg(sm->dev, "PM1[%c]: "
+ "P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), "
+ "M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n",
+ (pmc & 3 ) == 1 ? '*' : '-',
+ fmt_freq(decode_div(pll2, pm1, 24, 1<<29, 31, px_div)),
+ fmt_freq(decode_div(pll2, pm1, 16, 1<<20, 15, misc_div)),
+ fmt_freq(decode_div(pll2, pm1, 8, 1<<12, 15, misc_div)),
+ fmt_freq(decode_div(pll2, pm1, 0, 1<<4, 15, misc_div)));
+}
+
+static void sm501_dump_regs(struct sm501_devdata *sm)
+{
+ void *regs = sm->regs;
+
+ dev_info(sm->dev, "System Control %08x\n",
+ readl(regs + SM501_SYSTEM_CONTROL));
+ dev_info(sm->dev, "Misc Control %08x\n",
+ readl(regs + SM501_MISC_CONTROL));
+ dev_info(sm->dev, "GPIO Control Low %08x\n",
+ readl(regs + SM501_GPIO31_0_CONTROL));
+ dev_info(sm->dev, "GPIO Control Hi %08x\n",
+ readl(regs + SM501_GPIO63_32_CONTROL));
+ dev_info(sm->dev, "DRAM Control %08x\n",
+ readl(regs + SM501_DRAM_CONTROL));
+ dev_info(sm->dev, "Arbitration Ctrl %08x\n",
+ readl(regs + SM501_ARBTRTN_CONTROL));
+ dev_info(sm->dev, "Misc Timing %08x\n",
+ readl(regs + SM501_MISC_TIMING));
+}
+
+static void sm501_dump_gate(struct sm501_devdata *sm)
+{
+ dev_info(sm->dev, "CurrentGate %08x\n",
+ readl(sm->regs + SM501_CURRENT_GATE));
+ dev_info(sm->dev, "CurrentClock %08x\n",
+ readl(sm->regs + SM501_CURRENT_CLOCK));
+ dev_info(sm->dev, "PowerModeControl %08x\n",
+ readl(sm->regs + SM501_POWER_MODE_CONTROL));
+}
+#else
+static inline void sm501_dump_gate(struct sm501_devdata *sm) { }
+static inline void sm501_dump_regs(struct sm501_devdata *sm) { }
+static inline void sm501_dump_clk(struct sm501_devdata *sm) { }
+
+#define dev_dbg(XXX,...)
+#define dev_info(XXX,...)
+#define dev_err(XXX,...)
+#endif
+
+/* sm501_sync_regs
+ *
+ * ensure the
+*/
+
+static void sm501_sync_regs(struct sm501_devdata *sm)
+{
+ readl(sm->regs);
+}
+
+static inline void sm501_mdelay(struct sm501_devdata *sm, unsigned int delay)
+{
+ mdelay(delay);
+}
+
+/* sm501_misc_control
+ *
+ * alters the miscellaneous control parameters
+*/
+
+int sm501_misc_control(struct sm501_devdata *sm,
+ unsigned long set, unsigned long clear)
+{
+ unsigned long misc;
+ unsigned long to;
+
+ misc = readl(sm->regs + SM501_MISC_CONTROL);
+ to = (misc & ~clear) | set;
+
+ if (to != misc) {
+ writel(to, sm->regs + SM501_MISC_CONTROL);
+ sm501_sync_regs(sm);
+
+ dev_dbg(sm->dev, "MISC_CONTROL %08lx\n", misc);
+ }
+
+ return to;
+}
+
+
+/* sm501_modify_reg
+ *
+ * Modify a register in the SM501 which may be shared with other
+ * drivers.
+*/
+
+unsigned long sm501_modify_reg(struct sm501_devdata *sm,
+ unsigned long reg,
+ unsigned long set,
+ unsigned long clear)
+{
+ unsigned long data;
+
+ data = readl(sm->regs + reg);
+ data |= set;
+ data &= ~clear;
+
+ writel(data, sm->regs + reg);
+ sm501_sync_regs(sm);
+
+ return data;
+}
+
+
+unsigned long sm501_gpio_get(struct sm501_devdata *sm,
+ unsigned long gpio)
+{
+ unsigned long result;
+ unsigned long reg;
+
+ reg = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW;
+ result = readl(sm->regs + reg);
+
+ result >>= (gpio & 31);
+ return result & 1UL;
+}
+
+
+void sm501_gpio_set(struct sm501_devdata *sm,
+ unsigned long gpio,
+ unsigned int to,
+ unsigned int dir)
+{
+ unsigned long bit = 1 << (gpio & 31);
+ unsigned long base;
+ unsigned long val;
+
+ base = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW;
+ base += SM501_GPIO;
+
+ val = readl(sm->regs + base) & ~bit;
+ if (to)
+ val |= bit;
+ writel(val, sm->regs + base);
+
+ val = readl(sm->regs + SM501_GPIO_DDR_LOW) & ~bit;
+ if (dir)
+ val |= bit;
+
+ writel(val, sm->regs + SM501_GPIO_DDR_LOW);
+ sm501_sync_regs(sm);
+}
+
+
+/* sm501_unit_power
+ *
+ * alters the power active gate to set specific units on or off
+ */
+
+int sm501_unit_power(struct sm501_devdata *sm, unsigned int unit, unsigned int to)
+{
+ unsigned long mode;
+ unsigned long gate;
+ unsigned long clock;
+
+ mode = readl(sm->regs + SM501_POWER_MODE_CONTROL);
+ gate = readl(sm->regs + SM501_CURRENT_GATE);
+ clock = readl(sm->regs + SM501_CURRENT_CLOCK);
+
+ mode &= 3; /* get current power mode */
+
+ if (unit >= ARRAY_SIZE(sm->unit_power)) {
+ dev_err(dev, "%s: bad unit %d\n", __FUNCTION__, unit);
+ goto already;
+ }
+
+ dev_dbg(sm->dev, "%s: unit %d, cur %d, to %d\n", __FUNCTION__, unit,
+ sm->unit_power[unit], to);
+
+ if (to == 0 && sm->unit_power[unit] == 0) {
+ dev_err(sm->dev, "unit %d is already shutdown\n", unit);
+ goto already;
+ }
+
+ sm->unit_power[unit] += to ? 1 : -1;
+ to = sm->unit_power[unit] ? 1 : 0;
+
+ if (to) {
+ if (gate & (1 << unit))
+ goto already;
+ gate |= (1 << unit);
+ } else {
+ if (!(gate & (1 << unit)))
+ goto already;
+ gate &= ~(1 << unit);
+ }
+
+ switch (mode) {
+ case 1:
+ writel(gate, sm->regs + SM501_POWER_MODE_0_GATE);
+ writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK);
+ mode = 0;
+ break;
+ case 2:
+ case 0:
+ writel(gate, sm->regs + SM501_POWER_MODE_1_GATE);
+ writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK);
+ mode = 1;
+ break;
+
+ default:
+ return -1;
+ }
+
+ writel(mode, sm->regs + SM501_POWER_MODE_CONTROL);
+ sm501_sync_regs(sm);
+
+ dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n",
+ gate, clock, mode);
+
+ sm501_mdelay(sm, 16);
+
+ already:
+ return gate;
+}
+
+
+/* Perform a rounded division. */
+static long sm501fb_round_div(long num, long denom)
+{
+ /* n / d + 1 / 2 = (2n + d) / 2d */
+ return (2 * num + denom) / (2 * denom);
+}
+
+/* clock value structure. */
+struct sm501_clock {
+ unsigned long mclk;
+ int divider;
+ int shift;
+};
+
+/* sm501_select_clock
+ *
+ * selects nearest discrete clock frequency the SM501 can achive
+ * the maximum divisor is 3 or 5
+ */
+static unsigned long sm501_select_clock(unsigned long freq,
+ struct sm501_clock *clock,
+ int max_div)
+{
+ unsigned long mclk;
+ int divider;
+ int shift;
+ long diff;
+ long best_diff = 999999999;
+
+ /* Try 288MHz and 336MHz clocks. */
+ for (mclk = 288000000; mclk <= 336000000; mclk += 48000000) {
+ /* try dividers 1 and 3 for CRT and for panel,
+ try divider 5 for panel only.*/
+
+ for (divider = 1; divider <= max_div; divider += 2) {
+ /* try all 8 shift values.*/
+ for (shift = 0; shift < 8; shift++) {
+ /* Calculate difference to requested clock */
+ diff = sm501fb_round_div(mclk, divider << shift) - freq;
+ if (diff < 0)
+ diff = -diff;
+
+ /* If it is less than the current, use it */
+ if (diff < best_diff) {
+ best_diff = diff;
+
+ clock->mclk = mclk;
+ clock->divider = divider;
+ clock->shift = shift;
+ }
+ }
+ }
+ }
+
+ /* Return best clock. */
+ return clock->mclk / (clock->divider << clock->shift);
+}
+
+/* sm501_set_clock
+ *
+ * set one of the four clock sources to the closest available frequency to
+ * the one specified
+*/
+
+unsigned long sm501_set_clock(struct sm501_devdata *sm,
+ int clksrc,
+ unsigned long req_freq)
+{
+ unsigned long mode = readl(sm->regs + SM501_POWER_MODE_CONTROL);
+ unsigned long gate = readl(sm->regs + SM501_CURRENT_GATE);
+ unsigned long clock = readl(sm->regs + SM501_CURRENT_CLOCK);
+ unsigned char reg;
+ unsigned long sm501_freq; /* the actual frequency acheived */
+
+ struct sm501_clock to;
+
+ /* find achivable discrete frequency and setup register value
+ * accordingly, V2XCLK, MCLK and M1XCLK are the same P2XCLK
+ * has an extra bit for the divider */
+
+ switch (clksrc) {
+ case SM501_CLOCK_P2XCLK:
+ /* This clock is divided in half so to achive the
+ * requested frequency the value must be multiplied by
+ * 2. This clock also has an additional pre divisor */
+
+ sm501_freq = (sm501_select_clock(2 * req_freq, &to, 5) / 2);
+ reg=to.shift & 0x07;/* bottom 3 bits are shift */
+ if (to.divider == 3)
+ reg |= 0x08; /* /3 divider required */
+ else if (to.divider == 5)
+ reg |= 0x10; /* /5 divider required */
+ if (to.mclk != 288000000)
+ reg |= 0x20; /* which mclk pll is source */
+ break;
+
+ case SM501_CLOCK_V2XCLK:
+ /* This clock is divided in half so to achive the
+ * requested frequency the value must be multiplied by 2. */
+
+ sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2);
+ reg=to.shift & 0x07; /* bottom 3 bits are shift */
+ if (to.divider == 3)
+ reg |= 0x08; /* /3 divider required */
+ if (to.mclk != 288000000)
+ reg |= 0x10; /* which mclk pll is source */
+ break;
+
+ case SM501_CLOCK_MCLK:
+ case SM501_CLOCK_M1XCLK:
+ /* These clocks are the same and not further divided */
+
+ sm501_freq = sm501_select_clock( req_freq, &to, 3);
+ reg=to.shift & 0x07; /* bottom 3 bits are shift */
+ if (to.divider == 3)
+ reg |= 0x08; /* /3 divider required */
+ if (to.mclk != 288000000)
+ reg |= 0x10; /* which mclk pll is source */
+ break;
+
+ default:
+ return 0; /* this is bad */
+ }
+
+ mode = readl(sm->regs + SM501_POWER_MODE_CONTROL);
+ gate = readl(sm->regs + SM501_CURRENT_GATE);
+ clock = readl(sm->regs + SM501_CURRENT_CLOCK);
+
+ clock = clock & ~(0xFF << clksrc);
+ clock |= reg<<clksrc;
+
+ mode &= 3; /* find current mode */
+
+ switch (mode) {
+ case 1:
+ writel(gate, sm->regs + SM501_POWER_MODE_0_GATE);
+ writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK);
+ mode = 0;
+ break;
+ case 2:
+ case 0:
+ writel(gate, sm->regs + SM501_POWER_MODE_1_GATE);
+ writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK);
+ mode = 1;
+ break;
+
+ default:
+ return -1;
+ }
+
+ writel(mode, sm->regs + SM501_POWER_MODE_CONTROL);
+ sm501_sync_regs(sm);
+
+ dev_info(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n",
+ gate, clock, mode);
+
+ sm501_mdelay(sm, 16);
+
+ sm501_dump_clk(sm);
+
+ return sm501_freq;
+}
+
+/* sm501_find_clock
+ *
+ * finds the closest available frequency for a given clock
+*/
+
+unsigned long sm501_find_clock(int clksrc,
+ unsigned long req_freq)
+{
+ unsigned long sm501_freq; /* the frequency achiveable by the 501 */
+ struct sm501_clock to;
+
+ switch (clksrc) {
+ case SM501_CLOCK_P2XCLK:
+ sm501_freq = (sm501_select_clock(2 * req_freq, &to, 5) / 2);
+ break;
+
+ case SM501_CLOCK_V2XCLK:
+ sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2);
+ break;
+
+ case SM501_CLOCK_MCLK:
+ case SM501_CLOCK_M1XCLK:
+ sm501_freq = sm501_select_clock(req_freq, &to, 3);
+ break;
+
+ default:
+ sm501_freq = 0; /* error */
+ }
+
+ return sm501_freq;
+}
+
+
+/* sm501_init_reg
+ *
+ * Helper function for the init code to setup a register
+ *
+ * clear the bits which are set in r->mask, and then set
+ * the bits set in r->set.
+*/
+
+static inline void sm501_init_reg(struct sm501_devdata *sm,
+ unsigned long reg,
+ struct sm501_reg_init *r)
+{
+ unsigned long tmp;
+
+ tmp = readl(sm->regs + reg);
+ tmp &= ~r->mask;
+ tmp |= r->set;
+ writel(tmp, sm->regs + reg);
+}
+
+/* sm501_init_regs
+ *
+ * Setup core register values
+*/
+
+static void sm501_init_regs(struct sm501_devdata *sm,
+ struct sm501_initdata *init)
+{
+ sm501_misc_control(sm,
+ init->misc_control.set,
+ init->misc_control.mask);
+
+ sm501_init_reg(sm, SM501_MISC_TIMING, &init->misc_timing);
+ sm501_init_reg(sm, SM501_GPIO31_0_CONTROL, &init->gpio_low);
+ sm501_init_reg(sm, SM501_GPIO63_32_CONTROL, &init->gpio_high);
+
+ if (init->m1xclk) {
+ dev_info(sm->dev, "setting M1XCLK to %ld\n", init->m1xclk);
+ sm501_set_clock(sm, SM501_CLOCK_M1XCLK, init->m1xclk);
+ }
+
+ if (init->mclk) {
+ dev_info(sm->dev, "setting MCLK to %ld\n", init->mclk);
+ sm501_set_clock(sm, SM501_CLOCK_MCLK, init->mclk);
+ }
+
+}
+
+/* Check the PLL sources for the M1CLK and M1XCLK
+ *
+ * If the M1CLK and M1XCLKs are not sourced from the same PLL, then
+ * there is a risk (see errata AB-5) that the SM501 will cease proper
+ * function. If this happens, then it is likely the SM501 will
+ * hang the system.
+*/
+
+static int sm501_check_clocks(struct sm501_devdata *sm)
+{
+ unsigned long pwrmode = readl(sm->regs + SM501_CURRENT_CLOCK);
+ unsigned long msrc = (pwrmode & SM501_POWERMODE_M_SRC);
+ unsigned long m1src = (pwrmode & SM501_POWERMODE_M1_SRC);
+
+ return ((msrc == 0 && m1src != 0) || (msrc != 0 && m1src == 0));
+}
+
+static unsigned int sm501_mem_local[] = {
+ [0] = 4*1024*1024,
+ [1] = 8*1024*1024,
+ [2] = 16*1024*1024,
+ [3] = 32*1024*1024,
+ [4] = 64*1024*1024,
+ [5] = 2*1024*1024,
+};
+
+/* sm501_init_dev
+ *
+ * Common init code for an SM501
+*/
+
+static int sm501_init_dev(struct sm501_devdata *sm)
+{
+ unsigned long mem_avail;
+ unsigned long dramctrl;
+ unsigned long devid;
+ int ret;
+
+ devid = readl(sm->regs + SM501_DEVICEID);
+
+ if ((devid & SM501_DEVICEID_IDMASK) != SM501_DEVICEID_SM501) {
+ dev_err(sm->dev, "incorrect device id %08lx\n", devid);
+ return -1;
+ }
+
+ dramctrl = readl(sm->regs + SM501_DRAM_CONTROL);
+ mem_avail = sm501_mem_local[(dramctrl >> 13) & 0x7];
+
+ dev_info(sm->dev, "SM501 At %p: Version %08lx, %ld Mb, IRQ %d\n",
+ sm->regs, devid, (unsigned long)mem_avail >> 20, sm->irq);
+
+ sm501_dump_gate(sm);
+ sm501_dump_clk(sm);
+
+ /* check to see if we have some device initialisation */
+
+ if (sm->platdata) {
+ struct sm501_platdata *pdata = sm->platdata;
+
+ if (pdata->init) {
+ sm501_init_regs(sm, sm->platdata->init);
+ }
+ }
+
+ ret = sm501_check_clocks(sm);
+ if (ret) {
+ dev_err(sm->dev, "M1X and M clocks sourced from different "
+ "PLLs\n");
+ return -1;
+ }
+
+
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+/* Initialisation data for PCI devices */
+
+
+static struct sm501_initdata sm501_pci_initdata = {
+ .gpio_high = {
+ .set = 0x3F000000, /* 24bit panel */
+ .mask = 0x0,
+ },
+ .misc_timing = {
+ .set = 0x010100, /* SDRAM timing */
+ .mask = 0x1F1F00,
+ },
+ .misc_control = {
+ .set = SM501_MISC_PNL_24BIT,
+ .mask = 0,
+ },
+
+ .devices = SM501_USE_ALL,
+
+ /* Errata AB-3 says that 72MHz is the fastest available
+ * for 33MHZ PCI with proper bus-mastering operation */
+
+ .mclk = 72 * MHZ,
+ .m1xclk = 144 * MHZ,
+};
+
+static struct sm501_platdata_fbsub sm501_pdata_fbsub = {
+ .flags = (SM501FB_FLAG_USE_INIT_MODE |
+ SM501FB_FLAG_USE_HWCURSOR |
+ SM501FB_FLAG_USE_HWACCEL |
+ SM501FB_FLAG_DISABLE_AT_EXIT),
+};
+
+static struct sm501_platdata_fb sm501_fb_pdata = {
+ .fb_route = SM501_FB_OWN,
+ .fb_crt = &sm501_pdata_fbsub,
+ .fb_pnl = &sm501_pdata_fbsub,
+};
+
+static struct sm501_platdata sm501_pci_platdata = {
+ .init = &sm501_pci_initdata,
+ .fb = &sm501_fb_pdata,
+};
+
+
+
+/* Helper functions */
+
+static inline int h_total(struct ctfb_res_modes *var)
+{
+ return var->xres + var->left_margin +
+ var->right_margin + var->hsync_len;
+}
+
+static inline int v_total(struct ctfb_res_modes *var)
+{
+ return var->yres + var->upper_margin +
+ var->lower_margin + var->vsync_len;
+}
+
+/* sm501fb_sync_regs()
+ *
+ * This call is mainly for PCI bus systems where we need to
+ * ensure that any writes to the bus are completed before the
+ * next phase, or after completing a function.
+*/
+
+static inline void sm501fb_sync_regs(struct sm501_devdata *sm)
+{
+ readl(sm->dc);
+}
+
+/* sm501fb_ps_to_hz
+ *
+ * Converts a period in picoseconds to Hz.
+ *
+ * Note, we try to keep this in Hz to minimise rounding with
+ * the limited PLL settings on the SM501.
+*/
+
+static unsigned long sm501fb_ps_to_hz(unsigned long psvalue)
+{
+ unsigned long numerator=1000000000ULL;
+
+ /* 10^12 / picosecond period gives frequency in Hz */
+ numerator /= psvalue;
+ numerator *= 1000;
+ return (unsigned long)numerator;
+}
+
+/* sm501fb_hz_to_ps is identical to the oposite transform */
+
+#define sm501fb_hz_to_ps(x) sm501fb_ps_to_hz(x)
+
+
+/* sm501fb_set_par_common
+ *
+ * set common registers for framebuffers
+*/
+
+static int sm501fb_set_par_common(struct sm501_devdata *sm, int head)
+{
+ struct ctfb_res_modes *var = sm->mode;
+ unsigned long pixclock; /* pixelclock in Hz */
+ unsigned long sm501pixclock; /* pixelclock the 501 can achive in Hz */
+ /*unsigned int mem_type;*/
+ unsigned int clock_type;
+ unsigned int head_addr;
+
+ dev_dbg(fbi->dev, "%s: %dx%d, bpp = %d, virtual %dx%d\n",
+ __func__, var->xres, var->yres, sm->bits_per_pixel,
+ sm->xres_virtual, sm->yres_virtual);
+
+ switch (head) {
+ case HEAD_CRT:
+ /*mem_type = SM501_MEMF_CRT;*/
+ clock_type = SM501_CLOCK_V2XCLK;
+ head_addr = SM501_DC_CRT_FB_ADDR;
+ break;
+
+ case HEAD_PANEL:
+ /*mem_type = SM501_MEMF_PANEL;*/
+ clock_type = SM501_CLOCK_P2XCLK;
+ head_addr = SM501_DC_PANEL_FB_ADDR;
+ break;
+
+ default:
+ /*mem_type = 0;*/ /* stop compiler warnings */
+ head_addr = 0;
+ clock_type = 0;
+ }
+
+#if 0
+ switch (sm->bits_per_pixel) {
+ case 8:
+ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ break;
+
+ case 16:
+ info->fix.visual = FB_VISUAL_DIRECTCOLOR;
+ break;
+
+ case 32:
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ break;
+ }
+
+ /* allocate fb memory within 501 */
+ info->fix.line_length = (var->xres_virtual * var->bits_per_pixel)/8;
+ info->fix.smem_len = info->fix.line_length * var->yres_virtual;
+
+ dev_dbg(fbi->dev, "%s: line length = %u\n", __func__,
+ info->fix.line_length);
+
+ if (sm501_alloc_mem(fbi, &par->screen, mem_type,
+ info->fix.smem_len)) {
+ dev_err(fbi->dev, "no memory available\n");
+ return -ENOMEM;
+ }
+
+ info->fix.smem_start = fbi->fbmem_res->start + par->screen.sm_addr;
+
+ info->screen_base = fbi->fbmem + par->screen.sm_addr;
+ info->screen_size = info->fix.smem_len;
+#endif
+
+ /* set start of framebuffer to the screen = start of video mem */
+
+ writel(SM501_FRAMEBUFFER_ADDR | SM501_ADDR_FLIP, sm->dc + head_addr);
+
+ /* program CRT clock */
+
+ pixclock = sm501fb_ps_to_hz(var->pixclock);
+
+ sm501pixclock = sm501_set_clock(sm, clock_type,
+ pixclock);
+
+ /* update fb layer with actual clock used */
+ var->pixclock = sm501fb_hz_to_ps(sm501pixclock);
+
+ dev_dbg(fbi->dev, "%s: pixclock(ps) = %u, pixclock(Hz) = %lu, "
+ "sm501pixclock = %lu, error = %ld%%\n",
+ __func__, var->pixclock, pixclock, sm501pixclock,
+ ((pixclock - sm501pixclock)*100)/pixclock);
+
+ return 0;
+}
+
+/* sm501fb_set_par_geometry
+ *
+ * set the geometry registers for specified framebuffer.
+*/
+
+static void sm501fb_set_par_geometry(struct sm501_devdata *sm, int head)
+{
+ struct ctfb_res_modes *var = sm->mode;
+ void *base = sm->dc;
+ unsigned long reg;
+
+ if (head == HEAD_CRT)
+ base += SM501_DC_CRT_H_TOT;
+ else
+ base += SM501_DC_PANEL_H_TOT;
+
+ /* set framebuffer width and display width */
+
+ reg = (sm->xres_virtual * sm->bits_per_pixel)/8;
+ reg |= ((var->xres * sm->bits_per_pixel)/8) << 16;
+
+ writel(reg, sm->dc + (head == HEAD_CRT ?
+ SM501_DC_CRT_FB_OFFSET : SM501_DC_PANEL_FB_OFFSET));
+
+ /* program horizontal total */
+
+ reg = (h_total(var) - 1) << 16;
+ reg |= (var->xres - 1);
+
+ writel(reg, base + SM501_OFF_DC_H_TOT);
+
+ /* program horizontal sync */
+
+ reg = var->hsync_len << 16;
+ reg |= var->xres + var->right_margin - 1;
+
+ writel(reg, base + SM501_OFF_DC_H_SYNC);
+
+ /* program vertical total */
+
+ reg = (v_total(var) - 1) << 16;
+ reg |= (var->yres - 1);
+
+ writel(reg, base + SM501_OFF_DC_V_TOT);
+
+ /* program vertical sync */
+ reg = var->vsync_len << 16;
+ reg |= var->yres + var->lower_margin - 1;
+
+ writel(reg, base + SM501_OFF_DC_V_SYNC);
+}
+
+/* sm501fb_pan_crt
+ *
+ * pan the CRT display output within an virtual framebuffer
+*/
+#if 0
+static int sm501fb_pan_crt(struct sm501_devdata *sm)
+{
+ struct ctfb_res_modes *var = sm->mode;
+ unsigned int bytes_pixel = sm->bits_per_pixel / 8;
+ unsigned long reg;
+ unsigned long xoffs;
+
+ xoffs = /*var->xoffset*/0 * bytes_pixel;
+
+ reg = readl(sm->dc + SM501_DC_CRT_CONTROL);
+
+ reg &= ~SM501_DC_CRT_CONTROL_PIXEL_MASK;
+ reg |= ((xoffs & 15) / bytes_pixel) << 4;
+ writel(reg, sm->dc + SM501_DC_CRT_CONTROL);
+
+ reg = (par->screen.sm_addr + xoffs +
+ /*var->yoffset*/0 * info->fix.line_length);
+ writel(reg | SM501_ADDR_FLIP, fbi->dc + SM501_DC_CRT_FB_ADDR);
+
+ sm501fb_sync_regs(sm);
+ return 0;
+}
+#endif
+
+/* sm501fb_pan_pnl
+ *
+ * pan the panel display output within an virtual framebuffer
+*/
+static int sm501fb_pan_pnl(struct sm501_devdata *sm)
+{
+ /*struct ctfb_res_modes *var = sm->mode;*/
+ unsigned long reg;
+
+ reg = /*var->xoffset*/0 | (sm->xres_virtual << 16);
+ writel(reg, sm->dc + SM501_DC_PANEL_FB_WIDTH);
+
+ reg = /*var->yoffset*/0 | (sm->yres_virtual << 16);
+ writel(reg, sm->dc + SM501_DC_PANEL_FB_HEIGHT);
+
+ sm501fb_sync_regs(sm);
+ return 0;
+}
+
+/* sm501fb_set_par_crt
+ *
+ * Set the CRT video mode from the fb_info structure
+*/
+
+static int sm501fb_set_par_crt(struct sm501_devdata *sm)
+{
+ struct ctfb_res_modes *var = sm->mode;
+ unsigned long control; /* control register */
+ int ret;
+
+ /* activate new configuration */
+
+ dev_dbg(fbi->dev, "%s(%p)\n", __func__, sm);
+
+ /* enable CRT DAC - note 0 is on!*/
+ sm501_misc_control(sm, 0, SM501_MISC_DAC_POWER);
+
+ control = readl(sm->dc + SM501_DC_CRT_CONTROL);
+ dev_dbg(fbi->dev, "old control is %08lx\n", control);
+
+ control &= (SM501_DC_CRT_CONTROL_PIXEL_MASK |
+ SM501_DC_CRT_CONTROL_GAMMA |
+ SM501_DC_CRT_CONTROL_BLANK |
+ SM501_DC_CRT_CONTROL_SEL |
+ SM501_DC_CRT_CONTROL_CP |
+ SM501_DC_CRT_CONTROL_TVP);
+
+ /* set the sync polarities before we check data source */
+
+ if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0)
+ control |= SM501_DC_CRT_CONTROL_HSP;
+
+ if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0)
+ control |= SM501_DC_CRT_CONTROL_VSP;
+
+ if ((control & SM501_DC_CRT_CONTROL_SEL) == 0) {
+ /* the head is displaying panel data... */
+
+ dev_dbg(fbi->dev, "%s CRT display panel data\n", __func__);
+ /*sm501_alloc_mem(fbi, &par->screen, SM501_MEMF_CRT, 0);*/
+ goto out_update;
+ }
+ dev_dbg(fbi->dev, "%s CRT not display panel data\n", __func__);
+
+ ret = sm501fb_set_par_common(sm, HEAD_CRT);
+ if (ret) {
+ dev_err(fbi->dev, "failed to set common parameters\n");
+ return ret;
+ }
+
+ /*sm501fb_pan_crt(var, info);*/
+ sm501fb_set_par_geometry(sm, HEAD_CRT);
+
+ control |= SM501_FIFO_3; /* fill if >3 free slots */
+
+ switch(sm->bits_per_pixel) {
+ case 8:
+ control |= SM501_DC_CRT_CONTROL_8BPP;
+ break;
+
+ case 16:
+ control |= SM501_DC_CRT_CONTROL_16BPP;
+ break;
+
+ case 32:
+ control |= SM501_DC_CRT_CONTROL_32BPP;
+ /*sm501fb_setup_gamma(fbi, SM501_DC_CRT_PALETTE);*/
+ break;
+
+ default:
+ dev_dbg(fbi->dev, "unkown pixel format\n");
+ }
+
+ control |= SM501_DC_CRT_CONTROL_SEL; /* CRT displays CRT data */
+ control |= SM501_DC_CRT_CONTROL_TE; /* enable CRT timing */
+ control |= SM501_DC_CRT_CONTROL_ENABLE; /* enable CRT plane */
+
+ out_update:
+ dev_dbg(fbi->dev, "new control is %08lx\n", control);
+
+ writel(control, sm->dc + SM501_DC_CRT_CONTROL);
+ sm501fb_sync_regs(sm);
+
+ return 0;
+}
+
+static void sm501fb_panel_power(struct sm501_devdata *sm, int to)
+{
+ unsigned long control;
+ void *ctrl_reg = sm->dc + SM501_DC_PANEL_CONTROL;
+
+ control = readl(ctrl_reg);
+
+ if (to && (control & SM501_DC_PANEL_CONTROL_VDD) == 0) {
+ /* enable panel power */
+
+ control |= SM501_DC_PANEL_CONTROL_VDD; /* FPVDDEN */
+ writel(control, ctrl_reg);
+ sm501fb_sync_regs(sm);
+ mdelay(10);
+
+ control |= SM501_DC_PANEL_CONTROL_DATA; /* DATA */
+ writel(control, ctrl_reg);
+ sm501fb_sync_regs(sm);
+ mdelay(10);
+
+ control |= SM501_DC_PANEL_CONTROL_BIAS; /* VBIASEN */
+ writel(control, ctrl_reg);
+ sm501fb_sync_regs(sm);
+ mdelay(10);
+
+ control |= SM501_DC_PANEL_CONTROL_FPEN;
+ writel(control, ctrl_reg);
+
+ } else if (!to && (control & SM501_DC_PANEL_CONTROL_VDD) != 0) {
+ /* disable panel power */
+
+ control &= ~SM501_DC_PANEL_CONTROL_FPEN;
+ writel(control, ctrl_reg);
+ sm501fb_sync_regs(sm);
+ mdelay(10);
+
+ control &= ~SM501_DC_PANEL_CONTROL_BIAS;
+ writel(control, ctrl_reg);
+ sm501fb_sync_regs(sm);
+ mdelay(10);
+
+ control &= ~SM501_DC_PANEL_CONTROL_DATA;
+ writel(control, ctrl_reg);
+ sm501fb_sync_regs(sm);
+ mdelay(10);
+
+ control &= ~SM501_DC_PANEL_CONTROL_VDD;
+ writel(control, ctrl_reg);
+ sm501fb_sync_regs(sm);
+ mdelay(10);
+ }
+
+ sm501fb_sync_regs(sm);
+}
+
+
+/* sm501fb_set_par_pnl
+ *
+ * Set the panel video mode from the fb_info structure
+*/
+
+static int sm501fb_set_par_pnl(struct sm501_devdata *sm)
+{
+ struct ctfb_res_modes *var = sm->mode;
+ unsigned long control;
+ unsigned long reg;
+ int ret;
+
+ dev_dbg(fbi->dev, "%s(%p)\n", __func__, sm);
+
+ /* activate this new configuration */
+
+ ret = sm501fb_set_par_common(sm, HEAD_PANEL);
+ if (ret)
+ return ret;
+
+ sm501fb_pan_pnl(sm);
+ sm501fb_set_par_geometry(sm, HEAD_PANEL);
+
+ /* update control register */
+
+ control = readl(sm->dc + SM501_DC_PANEL_CONTROL);
+ control &= (SM501_DC_PANEL_CONTROL_GAMMA |
+ SM501_DC_PANEL_CONTROL_VDD |
+ SM501_DC_PANEL_CONTROL_DATA |
+ SM501_DC_PANEL_CONTROL_BIAS |
+ SM501_DC_PANEL_CONTROL_FPEN |
+ SM501_DC_PANEL_CONTROL_CP |
+ SM501_DC_PANEL_CONTROL_CK |
+ SM501_DC_PANEL_CONTROL_HP |
+ SM501_DC_PANEL_CONTROL_VP |
+ SM501_DC_PANEL_CONTROL_HPD |
+ SM501_DC_PANEL_CONTROL_VPD);
+
+ control |= SM501_FIFO_3; /* fill if >3 free slots */
+
+ switch(sm->bits_per_pixel) {
+ case 8:
+ control |= SM501_DC_PANEL_CONTROL_8BPP;
+ break;
+
+ case 16:
+ control |= SM501_DC_PANEL_CONTROL_16BPP;
+ break;
+
+ case 32:
+ control |= SM501_DC_PANEL_CONTROL_32BPP;
+ /*sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE);*/
+ break;
+
+ default:
+ dev_dbg(fbi->dev, "unkown pixel format\n");
+ }
+
+ writel(0x0, sm->dc + SM501_DC_PANEL_PANNING_CONTROL);
+
+ /* panel plane top left and bottom right location */
+
+ writel(0x00, sm->dc + SM501_DC_PANEL_TL_LOC);
+
+ reg = var->xres - 1;
+ reg |= (var->yres - 1) << 16;
+
+ writel(reg, sm->dc + SM501_DC_PANEL_BR_LOC);
+
+ /* program panel control register */
+
+ control |= SM501_DC_PANEL_CONTROL_TE; /* enable PANEL timing */
+ control |= SM501_DC_PANEL_CONTROL_EN; /* enable PANEL gfx plane */
+
+ if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0)
+ control |= SM501_DC_PANEL_CONTROL_HSP;
+
+ if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0)
+ control |= SM501_DC_PANEL_CONTROL_VSP;
+
+ writel(control, sm->dc + SM501_DC_PANEL_CONTROL);
+ sm501fb_sync_regs(sm);
+
+ /* power the panel up */
+ sm501fb_panel_power(sm, 1);
+ return 0;
+}
+
+void video_set_lut(
+ unsigned int index, /* color number */
+ unsigned char r, /* red */
+ unsigned char g, /* green */
+ unsigned char b /* blue */
+ )
+{
+ /* FIXME: to be done */
+}
+
+/*******************************************************************************
+ *
+ * Init video chip with common Linux graphic modes (lilo)
+ */
+void *video_hw_init(void)
+{
+ GraphicDevice *pGD = (GraphicDevice *)&smi;
+ unsigned short device_id;
+ pci_dev_t devbusfn;
+ int videomode;
+ unsigned long t1, hsynch, vsynch;
+ unsigned int pci_mem_base, *vm;
+ unsigned int pci_reg_base;
+ char *penv;
+ int tmp, i, bits_per_pixel;
+ int vmem_size;
+ struct ctfb_res_modes *res_mode;
+ struct ctfb_res_modes var_mode;
+
+ /* Search for video chip */
+ printf("Video: ");
+
+ if ((devbusfn = pci_find_devices(supported, 0)) < 0)
+ {
+ printf ("Controller not found !\n");
+ return (NULL);
+ }
+
+ /* PCI setup */
+ pci_write_config_dword (devbusfn, PCI_COMMAND, (PCI_COMMAND_MEMORY | PCI_COMMAND_IO));
+ pci_read_config_word (devbusfn, PCI_DEVICE_ID, &device_id);
+ pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0, &pci_mem_base);
+ /*pci_mem_base = pci_mem_to_phys (devbusfn, pci_mem_base);*/
+ pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_1, &pci_reg_base);
+ /*pci_reg_base = pci_mem_to_phys (devbusfn, pci_reg_base);*/
+
+ dev_dbg(xxx, "mem_base=0x%x reg_base=0x%x\n", pci_mem_base, pci_reg_base);
+
+ tmp = 0;
+
+ videomode = CONFIG_SYS_DEFAULT_VIDEO_MODE;
+ /* get video mode via environment */
+ if ((penv = getenv ("videomode")) != NULL) {
+ /* deceide if it is a string */
+ if (penv[0] <= '9') {
+ videomode = (int) simple_strtoul (penv, NULL, 16);
+ tmp = 1;
+ }
+ } else {
+ tmp = 1;
+ }
+ if (tmp) {
+ /* parameter are vesa modes */
+ /* search params */
+ for (i = 0; i < VESA_MODES_COUNT; i++) {
+ if (vesa_modes[i].vesanr == videomode)
+ break;
+ }
+ if (i == VESA_MODES_COUNT) {
+ printf ("no VESA Mode found, switching to mode 0x%x ", CONFIG_SYS_DEFAULT_VIDEO_MODE);
+ i = 0;
+ }
+ res_mode =
+ (struct ctfb_res_modes *) &res_mode_init[vesa_modes[i].
+ resindex];
+ bits_per_pixel = vesa_modes[i].bits_per_pixel;
+ } else {
+
+ res_mode = (struct ctfb_res_modes *) &var_mode;
+ bits_per_pixel = video_get_params (res_mode, penv);
+ }
+
+ /* calculate hsynch and vsynch freq (info only) */
+ t1 = (res_mode->left_margin + res_mode->xres +
+ res_mode->right_margin + res_mode->hsync_len) / 8;
+ t1 *= 8;
+ t1 *= res_mode->pixclock;
+ t1 /= 1000;
+ hsynch = 1000000000L / t1;
+ t1 *=
+ (res_mode->upper_margin + res_mode->yres +
+ res_mode->lower_margin + res_mode->vsync_len);
+ t1 /= 1000;
+ vsynch = 1000000000L / t1;
+
+ /* fill in Graphic device struct */
+ sprintf (pGD->modeIdent, "%dx%dx%d %ldkHz %ldHz", res_mode->xres,
+ res_mode->yres, bits_per_pixel, (hsynch / 1000),
+ (vsynch / 1000));
+ printf ("%s\n", pGD->modeIdent);
+ pGD->winSizeX = res_mode->xres;
+ pGD->winSizeY = res_mode->yres;
+ pGD->plnSizeX = res_mode->xres;
+ pGD->plnSizeY = res_mode->yres;
+ switch (bits_per_pixel) {
+ case 8:
+ pGD->gdfBytesPP = 1;
+ pGD->gdfIndex = GDF__8BIT_INDEX;
+ break;
+ case 15:
+ pGD->gdfBytesPP = 2;
+ pGD->gdfIndex = GDF_15BIT_555RGB;
+ break;
+ case 16:
+ pGD->gdfBytesPP = 2;
+ pGD->gdfIndex = GDF_16BIT_565RGB;
+ break;
+ case 24:
+ pGD->gdfBytesPP = 4;
+ pGD->gdfIndex = GDF_32BIT_X888RGB;
+ break;
+ case 32:
+ pGD->gdfBytesPP = 4;
+ pGD->gdfIndex = GDF_32BIT_X888RGB;
+ break;
+ }
+
+ pGD->isaBase = 0;
+ pGD->pciBase = pci_mem_base;
+ pGD->dprBase = pci_reg_base;
+ pGD->vprBase = 0;
+ pGD->cprBase = 0;
+ pGD->frameAdrs = pci_mem_base;
+ pGD->memSize = VIDEO_MEM_SIZE;
+
+ smi_devd.platdata = &sm501_pci_platdata;
+ smi_devd.regs = (void *)pci_reg_base;
+ smi_devd.dc = (void *)pci_reg_base + 0x80000;
+ smi_devd.vmem = (void *)pci_mem_base;
+ smi_devd.gd = pGD;
+ smi_devd.mode = res_mode;
+ switch (bits_per_pixel) {
+ case 8: smi_devd.bits_per_pixel = 8; break;
+ case 16: smi_devd.bits_per_pixel = 16; break;
+ case 24: smi_devd.bits_per_pixel = 32; break;
+ default: smi_devd.bits_per_pixel = 32; break;
+ }
+ smi_devd.xres_virtual = pGD->plnSizeX;
+ smi_devd.yres_virtual = pGD->plnSizeY;
+
+ switch( (readl(smi_devd.regs + SM501_DRAM_CONTROL) >> 13) & 0x7 ){
+ case 0: vmem_size = 4*1024*1024; break;
+ case 1: vmem_size = 8*1024*1024; break;
+ case 2: vmem_size = 16*1024*1024; break;
+ case 3: vmem_size = 32*1024*1024; break;
+ case 4: vmem_size = 64*1024*1024; break;
+ case 5: vmem_size = 2*1024*1024; break;
+ default: vmem_size = 2*1024*1024; break;
+ }
+ printf("SM501: %d MB Video memory\n", vmem_size/(1024*1024));
+
+ /* Blank video memory */
+ vm = (unsigned int *)pGD->frameAdrs;
+ for(i=0; i < vmem_size/sizeof(int); i++){
+ *vm++ = 0;
+ }
+
+ sm501_init_dev(&smi_devd);
+
+ /* enable display controller */
+ sm501_unit_power(&smi_devd, SM501_GATE_DISPLAY, 1);
+
+#if 0
+ /* setup cursors */
+
+ sm501_init_cursor(info->fb[HEAD_CRT], SM501_DC_CRT_HWC_ADDR);
+ sm501_init_cursor(info->fb[HEAD_PANEL], SM501_DC_PANEL_HWC_ADDR);
+#endif
+
+ /*
+ * Panel setup
+ */
+ sm501fb_set_par_pnl(&smi_devd);
+
+ /*
+ * CRT setup (we want CRT display panel data)
+ */
+ sm501fb_set_par_crt(&smi_devd);
+
+ return ((void*)&smi);
+}
+
+
+#if 0
+int t_smi (int argc, char *argv[])
+{
+ int i;
+ ulong ret;
+ ulong t,t1;
+
+ app_startup(argv);
+
+ pci_init();
+ drv_video_init();
+
+#if 0
+ video_puts("loading ...\n");
+ video_puts("be patient\n");
+#endif
+
+#if 0
+ if (argc == 1) {
+ /* Print the ABI version */
+
+ printf("Video hw Init\n");
+ pci_init();
+ video_hw_init();
+
+ return 0;
+ } else {
+ printf("Video Init\n");
+ pci_init();
+ drv_video_init();
+ }
+#endif
+}
+#endif
+
+#endif
2
1

[U-Boot] [PATCH] drivers/usb: Move conditional compilation to Makefile
by Jean-Christophe PLAGNIOL-VILLARD 07 Dec '08
by Jean-Christophe PLAGNIOL-VILLARD 07 Dec '08
07 Dec '08
Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj(a)jcrosoft.com>
---
drivers/usb/Makefile | 19 +++++++++++++------
drivers/usb/isp116x-hcd.c | 4 ----
drivers/usb/sl811_usb.c | 3 ---
drivers/usb/usb_ohci.c | 4 ----
drivers/usb/usbdcore_ep0.c | 4 ----
drivers/usb/usbdcore_mpc8xx.c | 4 ----
drivers/usb/usbdcore_omap1510.c | 4 ----
7 files changed, 13 insertions(+), 29 deletions(-)
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index c67a490..856f51a 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -25,15 +25,22 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libusb.a
+# core
+COBJS-y += usbdcore.o
+COBJS-$(CONFIG_USB_OHCI_NEW) += usb_ohci.o
+
+# host
+COBJS-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
COBJS-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
+COBJS-$(CONFIG_USB_SL811HS) += sl811_usb.o
-COBJS-y += isp116x-hcd.o
-COBJS-y += sl811_usb.o
-COBJS-y += usb_ohci.o
-COBJS-y += usbdcore.o
+# device
+ifdef CONFIG_USB_DEVICE
COBJS-y += usbdcore_ep0.o
-COBJS-y += usbdcore_mpc8xx.o
-COBJS-y += usbdcore_omap1510.o
+COBJS-$(CONFIG_OMAP1510) += usbdcore_omap1510.o
+COBJS-$(CONFIG_OMAP1610) += usbdcore_omap1510.o
+COBJS-$(CONFIG_MPC885_FAMILY) += usbdcore_mpc8xx.o
+endif
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/usb/isp116x-hcd.c b/drivers/usb/isp116x-hcd.c
index cc46dfe..348e404 100644
--- a/drivers/usb/isp116x-hcd.c
+++ b/drivers/usb/isp116x-hcd.c
@@ -56,8 +56,6 @@
*/
#include <common.h>
-
-#ifdef CONFIG_USB_ISP116X_HCD
#include <asm/io.h>
#include <usb.h>
#include <malloc.h>
@@ -1441,5 +1439,3 @@ int usb_lowlevel_stop(void)
return 0;
}
-
-#endif /* CONFIG_USB_ISP116X_HCD */
diff --git a/drivers/usb/sl811_usb.c b/drivers/usb/sl811_usb.c
index 48f1ee9..a03e469 100644
--- a/drivers/usb/sl811_usb.c
+++ b/drivers/usb/sl811_usb.c
@@ -36,7 +36,6 @@
*/
#include <common.h>
-#ifdef CONFIG_USB_SL811HS
#include <mpc8xx.h>
#include <usb.h>
#include "sl811.h"
@@ -733,5 +732,3 @@ static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe,
return status == 0 ? len : status;
}
-
-#endif /* CONFIG_USB_SL811HS */
diff --git a/drivers/usb/usb_ohci.c b/drivers/usb/usb_ohci.c
index e03371c..d68fdcf 100644
--- a/drivers/usb/usb_ohci.c
+++ b/drivers/usb/usb_ohci.c
@@ -46,9 +46,6 @@
*/
#include <common.h>
-
-#ifdef CONFIG_USB_OHCI_NEW
-
#include <asm/byteorder.h>
#if defined(CONFIG_PCI_OHCI)
@@ -2016,4 +2013,3 @@ int usb_lowlevel_stop(void)
ohci_inited = 0;
return 0;
}
-#endif /* CONFIG_USB_OHCI_NEW */
diff --git a/drivers/usb/usbdcore_ep0.c b/drivers/usb/usbdcore_ep0.c
index cf3f382..f6e017d 100644
--- a/drivers/usb/usbdcore_ep0.c
+++ b/drivers/usb/usbdcore_ep0.c
@@ -51,8 +51,6 @@
*/
#include <common.h>
-
-#if defined(CONFIG_USB_DEVICE)
#include "usbdcore.h"
#if 0
@@ -597,5 +595,3 @@ int ep0_recv_setup (struct urb *urb)
}
return -1;
}
-
-#endif
diff --git a/drivers/usb/usbdcore_mpc8xx.c b/drivers/usb/usbdcore_mpc8xx.c
index 53bde0d..0e311ad 100644
--- a/drivers/usb/usbdcore_mpc8xx.c
+++ b/drivers/usb/usbdcore_mpc8xx.c
@@ -58,8 +58,6 @@
*/
#include <common.h>
#include <config.h>
-
-#if defined(CONFIG_MPC885_FAMILY) && defined(CONFIG_USB_DEVICE)
#include <commproc.h>
#include "usbdcore.h"
#include "usbdcore_mpc8xx.h"
@@ -1398,5 +1396,3 @@ static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment)
return retaddr;
}
-
-#endif /* CONFIG_MPC885_FAMILY && CONFIG_USB_DEVICE) */
diff --git a/drivers/usb/usbdcore_omap1510.c b/drivers/usb/usbdcore_omap1510.c
index 4e3239f..cb9dc44 100644
--- a/drivers/usb/usbdcore_omap1510.c
+++ b/drivers/usb/usbdcore_omap1510.c
@@ -27,9 +27,6 @@
*/
#include <common.h>
-
-#if ((defined(CONFIG_OMAP1510) || defined(CONFIG_OMAP1610)) && defined(CONFIG_USB_DEVICE))
-
#include <asm/io.h>
#ifdef CONFIG_OMAP_SX1
#include <i2c.h>
@@ -1566,4 +1563,3 @@ void udc_unset_nak (int epid)
{
/* TODO: implement this functionality in omap1510 */
}
-#endif
--
1.5.6.5
2
2

[U-Boot] [PATCH] common/cmd_ide.c: Corrected endian order printing for compact flash serial number.
by Richard Retanubun 07 Dec '08
by Richard Retanubun 07 Dec '08
07 Dec '08
Corrected endian order printing for compact flash serial number.
Signed-off-by: Richard Retanubun <RichardRetanubun(a)RuggedCom.com>
---
common/cmd_ide.c | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/common/cmd_ide.c b/common/cmd_ide.c
index 2564c2b..db05f76 100644
--- a/common/cmd_ide.c
+++ b/common/cmd_ide.c
@@ -1166,15 +1166,16 @@ static void ide_ident (block_dev_desc_t *dev_desc)
ident_cpy ((unsigned char*)dev_desc->product, iop->serial_no, sizeof(dev_desc->product));
#ifdef __LITTLE_ENDIAN
/*
- * firmware revision and model number have Big Endian Byte
- * order in Word. Convert both to little endian.
+ * firmware revision, model, and serial number have Big Endian Byte
+ * order in Word. Convert all three to little endian.
*
* See CF+ and CompactFlash Specification Revision 2.0:
- * 6.2.1.6: Identfy Drive, Table 39 for more details
+ * 6.2.1.6: Identify Drive, Table 39 for more details
*/
strswab (dev_desc->revision);
strswab (dev_desc->vendor);
+ strswab (dev_desc->product);
#endif /* __LITTLE_ENDIAN */
if ((iop->config & 0x0080)==0x0080)
--
1.5.5.GIT
2
2

07 Dec '08
See doc/README.iomux for a general description of what this does.
This is the first of two commits. The second commit touches net/eth.c
and has to go through the custodian, so I split it out for simplicity.
Tested with MAKEALL 8xx.
Signed-off-by: Gary Jennejohn <garyj(a)denx.de>
---
common/Makefile | 1 +
common/cmd_nvedit.c | 6 ++
common/console.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++
common/iomux.c | 133 +++++++++++++++++++++++++++++++++++++++
doc/README.iomux | 90 ++++++++++++++++++++++++++
include/common.h | 7 ++
include/iomux.h | 48 ++++++++++++++
7 files changed, 458 insertions(+), 0 deletions(-)
create mode 100644 common/iomux.c
create mode 100644 doc/README.iomux
create mode 100644 include/iomux.h
diff --git a/common/Makefile b/common/Makefile
index 8bddf8e..9138742 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -155,6 +155,7 @@ COBJS-$(CONFIG_LCD) += lcd.o
COBJS-$(CONFIG_LYNXKDI) += lynxkdi.o
COBJS-$(CONFIG_USB_KEYBOARD) += usb_kbd.o
COBJS-$(CONFIG_DDR_SPD) += ddr_spd.o
+COBJS-$(CONFIG_IO_MUX) += iomux.o
COBJS := $(sort $(COBJS-y))
SRCS := $(AOBJS:.o=.S) $(COBJS:.o=.c)
diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c
index 637d6c9..6fc9313 100644
--- a/common/cmd_nvedit.c
+++ b/common/cmd_nvedit.c
@@ -213,6 +213,11 @@ int _do_setenv (int flag, int argc, char *argv[])
return 1;
}
+#ifdef CONFIG_IO_MUX
+ i = iomux_doenv(console, argv[2]);
+ if (i)
+ return i;
+#else
/* Try assigning specified device */
if (console_assign (console, argv[2]) < 0)
return 1;
@@ -221,6 +226,7 @@ int _do_setenv (int flag, int argc, char *argv[])
if (serial_assign (argv[2]) < 0)
return 1;
#endif
+#endif /* CONFIG_IO_MUX */
}
/*
diff --git a/common/console.c b/common/console.c
index 56d9118..6158af9 100644
--- a/common/console.c
+++ b/common/console.c
@@ -93,6 +93,78 @@ static int console_setfile (int file, device_t * dev)
return error;
}
+#if defined(CONFIG_IO_MUX)
+/** Console I/O multiplexing *******************************************/
+
+static device_t *tstcdev;
+device_t *console_devices[MAX_FILES][MAX_CONSARGS];
+
+/*
+ * This depends on tstc() always being called before getc().
+ * No attempt is made to demultiplex multiple input sources.
+ */
+static int iomux_getc(void)
+{
+ unsigned char ret;
+
+ ret = tstcdev->getc();
+ tstcdev = NULL;
+ return ret;
+}
+
+static int iomux_tstc(int file)
+{
+ int i, ret;
+ device_t *dev;
+
+ disable_ctrlc(1);
+ for (i = 0; i < MAX_CONSARGS; i++) {
+ dev = console_devices[file][i];
+ if (dev == NULL)
+ break;
+ if (dev->tstc != NULL) {
+ ret = dev->tstc();
+ if (ret > 0) {
+ tstcdev = dev;
+ disable_ctrlc(0);
+ return ret;
+ }
+ }
+ }
+ disable_ctrlc(0);
+
+ return 0;
+}
+
+static void iomux_putc(int file, const char c)
+{
+ int i;
+ device_t *dev;
+
+ for (i = 0; i < MAX_CONSARGS; i++) {
+ dev = console_devices[file][i];
+ if (dev == NULL)
+ break;
+ if (dev->putc != NULL)
+ dev->putc(c);
+ }
+}
+
+static void iomux_puts(int file, const char *s)
+{
+ int i;
+ device_t *dev;
+
+ for (i = 0; i < MAX_CONSARGS; i++) {
+ dev = console_devices[file][i];
+ if (dev == NULL)
+ break;
+ if (dev->puts != NULL)
+ dev->puts(s);
+ }
+}
+#endif /* defined(CONFIG_IO_MUX) */
+
/** U-Boot INITIAL CONSOLE-NOT COMPATIBLE FUNCTIONS *************************/
void serial_printf (const char *fmt, ...)
@@ -115,7 +187,41 @@ void serial_printf (const char *fmt, ...)
int fgetc (int file)
{
if (file < MAX_FILES)
+#if defined(CONFIG_IO_MUX)
+ {
+ int cntr = 0;
+
+ /*
+ * Effectively poll for input wherever it may be available.
+ */
+ for (;;) {
+ /*
+ * Upper layer may have already called tstc() so
+ * check for that first.
+ */
+ if (tstcdev != NULL)
+ return iomux_getc();
+ iomux_tstc(file);
+ /*
+ * Even this is too slow for serial cut&paste due
+ * to the overhead of calling tstc() then getc().
+ * It gets worse if nc is set as a console because
+ * nc_tstc() is really slow.
+ *
+ * The idea behind this counter is to avoid calling
+ * the watchdog via udelay() too often.
+ * COUNT_TIL_UDELAY is defined in iomux.h and is just
+ * a guesstimate.
+ */
+ if (cntr++ > COUNT_TIL_UDELAY) {
+ udelay(1);
+ cntr = 0;
+ }
+ }
+ }
+#else
return stdio_devices[file]->getc ();
+#endif
return -1;
}
@@ -123,7 +229,11 @@ int fgetc (int file)
int ftstc (int file)
{
if (file < MAX_FILES)
+#if defined(CONFIG_IO_MUX)
+ return iomux_tstc(file);
+#else
return stdio_devices[file]->tstc ();
+#endif
return -1;
}
@@ -131,13 +241,21 @@ int ftstc (int file)
void fputc (int file, const char c)
{
if (file < MAX_FILES)
+#if defined(CONFIG_IO_MUX)
+ iomux_putc(file, c);
+#else
stdio_devices[file]->putc (c);
+#endif
}
void fputs (int file, const char *s)
{
if (file < MAX_FILES)
+#if defined(CONFIG_IO_MUX)
+ iomux_puts(file, s);
+#else
stdio_devices[file]->puts (s);
+#endif
}
void fprintf (int file, const char *fmt, ...)
@@ -407,6 +525,9 @@ int console_init_r (void)
#ifdef CFG_CONSOLE_ENV_OVERWRITE
int i;
#endif /* CFG_CONSOLE_ENV_OVERWRITE */
+#ifdef CONFIG_IO_MUX
+ int iomux_err = 0;
+#endif
/* set default handlers at first */
gd->jt[XF_getc] = serial_getc;
@@ -425,6 +546,14 @@ int console_init_r (void)
inputdev = search_device (DEV_FLAGS_INPUT, stdinname);
outputdev = search_device (DEV_FLAGS_OUTPUT, stdoutname);
errdev = search_device (DEV_FLAGS_OUTPUT, stderrname);
+#ifdef CONFIG_IO_MUX
+ iomux_err = iomux_doenv(stdin, stdinname);
+ iomux_err += iomux_doenv(stdout, stdoutname);
+ iomux_err += iomux_doenv(stderr, stderrname);
+ if (!iomux_err)
+ /* Successful, so skip all the code below. */
+ goto done;
+#endif
}
/* if the devices are overwritten or not found, use default device */
if (inputdev == NULL) {
@@ -439,15 +568,40 @@ int console_init_r (void)
/* Initializes output console first */
if (outputdev != NULL) {
console_setfile (stdout, outputdev);
+#ifdef CONFIG_IO_MUX
+ /* need to set a console if not done above. */
+ console_devices[stdout][0] = outputdev;
+#endif
}
if (errdev != NULL) {
console_setfile (stderr, errdev);
+#ifdef CONFIG_IO_MUX
+ /* need to set a console if not done above. */
+ console_devices[stderr][0] = errdev;
+#endif
}
if (inputdev != NULL) {
console_setfile (stdin, inputdev);
+#ifdef CONFIG_IO_MUX
+ /* need to set a console if not done above. */
+ console_devices[stdin][0] = inputdev;
+#endif
}
+#ifdef CONFIG_IO_MUX
+#ifdef CONFIG_NETCONSOLE
+ /*
+ * Must do this very late in net/eth.c because a network device may
+ * be set as a console at boot.
+ */
+ gd->flags |= 0;
+#else
+ gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
+#endif /* CONFIG_NETCONSOLE */
+done:
+#else
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
+#endif /* CONFIG_IO_MUX */
#ifndef CFG_CONSOLE_INFO_QUIET
/* Print information */
@@ -455,21 +609,33 @@ int console_init_r (void)
if (stdio_devices[stdin] == NULL) {
puts ("No input devices available!\n");
} else {
+#ifdef CONFIG_IO_MUX
+ iomux_printdevs(stdin);
+#else
printf ("%s\n", stdio_devices[stdin]->name);
+#endif
}
puts ("Out: ");
if (stdio_devices[stdout] == NULL) {
puts ("No output devices available!\n");
} else {
+#ifdef CONFIG_IO_MUX
+ iomux_printdevs(stdout);
+#else
printf ("%s\n", stdio_devices[stdout]->name);
+#endif
}
puts ("Err: ");
if (stdio_devices[stderr] == NULL) {
puts ("No error devices available!\n");
} else {
+#ifdef CONFIG_IO_MUX
+ iomux_printdevs(stderr);
+#else
printf ("%s\n", stdio_devices[stderr]->name);
+#endif
}
#endif /* CFG_CONSOLE_INFO_QUIET */
@@ -524,11 +690,18 @@ int console_init_r (void)
if (outputdev != NULL) {
console_setfile (stdout, outputdev);
console_setfile (stderr, outputdev);
+#ifdef CONFIG_IO_MUX
+ console_devices[stdout][0] = outputdev;
+ console_devices[stderr][0] = outputdev;
+#endif
}
/* Initializes input console */
if (inputdev != NULL) {
console_setfile (stdin, inputdev);
+#ifdef CONFIG_IO_MUX
+ console_devices[stdin][0] = inputdev;
+#endif
}
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
diff --git a/common/iomux.c b/common/iomux.c
new file mode 100644
index 0000000..d62f7e4
--- /dev/null
+++ b/common/iomux.c
@@ -0,0 +1,133 @@
+/*
+ * (C) Copyright 2008
+ * Gary Jennejohn, DENX Software Engineering GmbH, garyj(a)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
+ */
+
+#include <common.h>
+#include <serial.h>
+#include <malloc.h>
+
+#ifdef CONFIG_IO_MUX
+void iomux_printdevs(const int console)
+{
+ int i;
+ device_t *dev;
+
+ for (i = 0; i < MAX_CONSARGS; i++) {
+ dev = console_devices[console][i];
+ if (dev == NULL)
+ break;
+ serial_printf("%s ", dev->name);
+ }
+ serial_printf("\n");
+}
+
+int iomux_doenv(const int console, const char *arg)
+{
+ char *console_args, *temp, *start[MAX_CONSARGS];
+ int i, j, io_flag, cs_idx;
+ device_t *dev;
+ device_t *cons_set[MAX_CONSARGS];
+
+#ifdef CFG_CONSOLE_IS_IN_ENV
+ if (arg == NULL)
+ return 1;
+#endif
+
+ i = 0;
+ console_args = strdup(arg);
+ if (console_args == NULL)
+ return 1;
+ start[0] = console_args;
+ /*
+ * Check whether a comma separated list of devices was
+ * entered and count how many devices were entered.
+ * The array start[] has pointers to the beginning of
+ * each device name (up to MAX_CONSARGS devices).
+ */
+ for (;;) {
+ temp = strchr(start[i], ',');
+ if (temp != NULL) {
+ i++;
+ *temp = '\0';
+ if (i == MAX_CONSARGS)
+ break;
+ start[i] = temp + 1;
+ continue;
+ }
+ break;
+ }
+ if (i != MAX_CONSARGS)
+ i++;
+ cs_idx = 0;
+ memset((void *)cons_set, 0, CONSDEVS_LINE_SIZE);
+
+ switch (console) {
+ case stdin:
+ io_flag = DEV_FLAGS_INPUT;
+ break;
+ case stdout:
+ case stderr:
+ io_flag = DEV_FLAGS_OUTPUT;
+ break;
+ default:
+ return 1;
+ }
+
+ for (j = 0; j < i; j++) {
+ /*
+ * Check whether the device exists and is valid.
+ * console_assign() also calls search_device(),
+ * but I need the pointer to the device.
+ */
+ dev = search_device(io_flag, start[j]);
+ if (dev == NULL)
+ continue;
+ /*
+ * Try assigning the specified device.
+ * This could screw up the console settings for apps.
+ */
+ if (console_assign(console, start[j]) < 0)
+ continue;
+#ifdef CONFIG_SERIAL_MULTI
+ /*
+ * This was taken from common/cmd_nvedit.c.
+ * This will never work because serial_assign() returns
+ * 1 upon error, not -1.
+ * This would almost always return an error anyway because
+ * serial_assign() expects the name of a serial device, like
+ * serial_smc, but the user generally only wants to set serial.
+ */
+ if (serial_assign(start[j]) < 0)
+ continue;
+#endif
+ cons_set[cs_idx++] = dev;
+ }
+ free(console_args);
+ /* failed to set any console */
+ if (cs_idx == 0)
+ return 1;
+ else
+ memcpy(console_devices[console], cons_set,
+ CONSDEVS_LINE_SIZE);;
+ return 0;
+}
+#endif /* CONFIG_IO_MUX */
diff --git a/doc/README.iomux b/doc/README.iomux
new file mode 100644
index 0000000..222843c
--- /dev/null
+++ b/doc/README.iomux
@@ -0,0 +1,90 @@
+/*
+ * (C) Copyright 2008
+ * Gary Jennejohn, DENX Software Engineering GmbH <garyj(a)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
+ */
+
+U-Boot console multiplexing
+===========================
+
+HOW CONSOLE MULTIPLEXING WORKS
+------------------------------
+
+This functionality is controlled with CONFIG_IO_MUX in the board
+configuration file.
+
+Two new files, common/iomux.c and include/iomux.h, contain the heart
+of the environment setting implementation.
+
+The execution is then in common/cmd_nvedit.c and common/console.c.
+
+A user can use a comma-separated list of devices to set stdin, stdout
+and stderr. For example: setenv stdin serial,nc. NOTE: No spaces
+are allowed around the comma(s)!
+
+The length of the list is limited to MAX_CONSARGS entries, which is
+defined in include/iomux.h and presently set to 6.
+
+It should be possible to specify any device which console_assign()
+finds acceptable, but the code has only been tested with serial and
+nc.
+
+The major change in common/console.c was to modify fgetc() to call
+the iomux_tstc() routine in a for-loop. iomux_tstc() in turn calls
+the tstc() routine for every registered device, but exits immediately
+when one of them returns true. fgetc() then calls iomux_getc(),
+which calls the correpsonding getc() routine. fgetc() hangs in
+the for-loop until iomux_tstc() returns true and the input can be
+retrieved.
+
+Thus, a user can type into any device registered for stdin. No effort
+has been made to demulitplex simultaneous input from multiple stdin
+devices.
+
+fputc() and fputs() have been modified to call iomux_putc() and
+iomux_puts() respectively, which call the corresponding output
+routines for every registered device.
+
+Thus, a user can see the ouput for any device registered for stdout
+or stderr on all devices registered for stdout or stderr. As an
+example, if stdin=serial,nc and stdout=serial,nc then all output
+for serial, e.g. echos of input on serial, will appear on serial and nc.
+
+Just as with the old console code, this statement is still true:
+If not defined in the environment, the first input device is assigned
+to the 'stdin' file, the first output one to 'stdout' and 'stderr'.
+
+If CONFIG_CONSOLE_IS_IN_ENV is defined then multiple input/output
+devices will be set at boot time.
+
+CAVEATS
+-------
+
+Note that common/iomux.c calls console_assign() for every registered
+device as it is discovered. This means that the environment settings
+for application consoles will be set to the last device in the list.
+
+The overhead associated with calling tstc() and then getc() means that
+cut&paste will not work, even if serial is set as the only device for
+stdin, stdout and stderr.
+
+Using nc as a stdin device results in even more overhead because nc_tstc()
+is extremely slow. The slowdown is so extreme that it negatively impacts
+timers such as the autoboot countdown.
diff --git a/include/common.h b/include/common.h
index 33c6e10..8cc7e3a 100644
--- a/include/common.h
+++ b/include/common.h
@@ -675,6 +675,13 @@ void fputc(int file, const char c);
int ftstc(int file);
int fgetc(int file);
+/*
+ * CONSOLE multiplexing.
+ */
+#ifdef CONFIG_IO_MUX
+#include <iomux.h>
+#endif
+
int pcmcia_init (void);
#ifdef CONFIG_STATUS_LED
diff --git a/include/iomux.h b/include/iomux.h
new file mode 100644
index 0000000..1310a75
--- /dev/null
+++ b/include/iomux.h
@@ -0,0 +1,48 @@
+/*
+ * (C) Copyright 2008
+ * Gary Jennejohn, DENX Software Engineering GmbH, garyj(a)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 _IO_MUX_H
+#define _IO_MUX_H
+
+#include <devices.h>
+
+/*
+ * Stuff required to support console multiplexing.
+ */
+/* Only allow this many devices in the comma separated list of consoles */
+#define MAX_CONSARGS 6
+
+/*
+ * Avoid invoking WATCHDOG via udelayy() on every pass through the loop
+ * in fgetc(). Note this is all based on guesswork.
+ */
+#define COUNT_TIL_UDELAY 10000
+
+/*
+ * Pointers to devices used for each file type. Defined in console.c.
+ */
+extern device_t *console_devices[MAX_FILES][MAX_CONSARGS];
+#define CONSDEVS_LINE_SIZE (sizeof(device_t *) * MAX_CONSARGS)
+
+int iomux_doenv(const int, const char *);
+void iomux_printdevs(const int);
+device_t *search_device(int, char *);
+
+#endif /* _IO_MUX_H */
--
1.5.4.3
---
Gary Jennejohn
*********************************************************************
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office(a)denx.de
*********************************************************************
3
14