patch-2.4.6 linux/drivers/mtd/maps/sc520cdp.c

Next file: linux/drivers/mtd/maps/sun_uflash.c
Previous file: linux/drivers/mtd/maps/sbc_gxx.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.5/linux/drivers/mtd/maps/sc520cdp.c linux/drivers/mtd/maps/sc520cdp.c
@@ -0,0 +1,348 @@
+/* sc520cdp.c -- MTD map driver for AMD SC520 Customer Development Platform
+ *
+ * Copyright (C) 2001 Sysgo Real-Time Solutions GmbH
+ *
+ * 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
+ *
+ * $Id: sc520cdp.c,v 1.7 2001/06/02 14:52:23 dwmw2 Exp $
+ *
+ *
+ * The SC520CDP is an evaluation board for the Elan SC520 processor available
+ * from AMD. It has two banks of 32-bit Flash ROM, each 8 Megabytes in size,
+ * and up to 512 KiB of 8-bit DIL Flash ROM.
+ * For details see http://www.amd.com/products/epd/desiging/evalboards/18.elansc520/520_cdp_brief/index.html
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+
+
+/*
+** The Embedded Systems BIOS decodes the first FLASH starting at
+** 0x8400000. This is a *terrible* place for it because accessing
+** the flash at this location causes the A22 address line to be high
+** (that's what 0x8400000 binary's ought to be). But this is the highest
+** order address line on the raw flash devices themselves!!
+** This causes the top HALF of the flash to be accessed first. Beyond
+** the physical limits of the flash, the flash chip aliases over (to
+** 0x880000 which causes the bottom half to be accessed. This splits the
+** flash into two and inverts it! If you then try to access this from another
+** program that does NOT do this insanity, then you *will* access the
+** first half of the flash, but not find what you expect there. That
+** stuff is in the *second* half! Similarly, the address used by the
+** BIOS for the second FLASH bank is also quite a bad choice.
+** If REPROGRAM_PAR is defined below (the default), then this driver will
+** choose more useful addresses for the FLASH banks by reprogramming the
+** responsible PARxx registers in the SC520's MMCR region. This will
+** cause the settings to be incompatible with the BIOS's settings, which
+** shouldn't be a problem since you are running Linux, (i.e. the BIOS is
+** not much use anyway). However, if you need to be compatible with
+** the BIOS for some reason, just undefine REPROGRAM_PAR.
+*/
+#define REPROGRAM_PAR
+
+
+
+#ifdef REPROGRAM_PAR
+
+/* These are the addresses we want.. */
+#define WINDOW_ADDR_0	0x08800000
+#define WINDOW_ADDR_1	0x09000000
+#define WINDOW_ADDR_2	0x09800000
+
+/* .. and these are the addresses the BIOS gives us */
+#define WINDOW_ADDR_0_BIOS	0x08400000
+#define WINDOW_ADDR_1_BIOS	0x08c00000
+#define WINDOW_ADDR_2_BIOS	0x09400000
+
+#else
+
+#define WINDOW_ADDR_0	0x08400000
+#define WINDOW_ADDR_1	0x08C00000
+#define WINDOW_ADDR_2	0x09400000
+
+#endif
+
+#define WINDOW_SIZE_0	0x00800000
+#define WINDOW_SIZE_1	0x00800000
+#define WINDOW_SIZE_2	0x00080000
+
+static __u8 sc520cdp_read8(struct map_info *map, unsigned long ofs)
+{
+	return readb(map->map_priv_1 + ofs);
+}
+
+static __u16 sc520cdp_read16(struct map_info *map, unsigned long ofs)
+{
+	return readw(map->map_priv_1 + ofs);
+}
+
+static __u32 sc520cdp_read32(struct map_info *map, unsigned long ofs)
+{
+	return readl(map->map_priv_1 + ofs);
+}
+
+static void sc520cdp_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+{
+	memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+}
+
+static void sc520cdp_write8(struct map_info *map, __u8 d, unsigned long adr)
+{
+	writeb(d, map->map_priv_1 + adr);
+}
+
+static void sc520cdp_write16(struct map_info *map, __u16 d, unsigned long adr)
+{
+	writew(d, map->map_priv_1 + adr);
+}
+
+static void sc520cdp_write32(struct map_info *map, __u32 d, unsigned long adr)
+{
+	writel(d, map->map_priv_1 + adr);
+}
+
+static void sc520cdp_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+{
+	memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+}
+
+static struct map_info sc520cdp_map[] = {
+	{
+		name: "SC520CDP Flash Bank #0",
+		size: WINDOW_SIZE_0,
+		buswidth: 4,
+		read8: sc520cdp_read8,
+		read16: sc520cdp_read16,
+		read32: sc520cdp_read32,
+		copy_from: sc520cdp_copy_from,
+		write8: sc520cdp_write8,
+		write16: sc520cdp_write16,
+		write32: sc520cdp_write32,
+		copy_to: sc520cdp_copy_to,
+		map_priv_2: WINDOW_ADDR_0
+	},
+	{
+		name: "SC520CDP Flash Bank #1",
+		size: WINDOW_SIZE_1,
+		buswidth: 4,
+		read8: sc520cdp_read8,
+		read16: sc520cdp_read16,
+		read32: sc520cdp_read32,
+		copy_from: sc520cdp_copy_from,
+		write8: sc520cdp_write8,
+		write16: sc520cdp_write16,
+		write32: sc520cdp_write32,
+		copy_to: sc520cdp_copy_to,
+		map_priv_2: WINDOW_ADDR_1
+	},
+	{
+		name: "SC520CDP DIL Flash",
+		size: WINDOW_SIZE_2,
+		buswidth: 1,
+		read8: sc520cdp_read8,
+		read16: sc520cdp_read16,
+		read32: sc520cdp_read32,
+		copy_from: sc520cdp_copy_from,
+		write8: sc520cdp_write8,
+		write16: sc520cdp_write16,
+		write32: sc520cdp_write32,
+		copy_to: sc520cdp_copy_to,
+		map_priv_2: WINDOW_ADDR_2
+	},
+};
+
+#define NUM_FLASH_BANKS	(sizeof(sc520cdp_map)/sizeof(struct map_info))
+
+static struct mtd_info *mymtd[NUM_FLASH_BANKS];
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define init_sc520cdp init_module
+#define cleanup_sc520cdp cleanup_module
+#endif
+
+#ifdef REPROGRAM_PAR
+
+/*
+** The SC520 MMCR (memory mapped control register) region resides
+** at 0xFFFEF000. The 16 Programmable Address Region (PAR) registers
+** are at offset 0x88 in the MMCR:
+*/
+#define SC520_MMCR_BASE		0xFFFEF000
+#define SC520_MMCR_EXTENT	0x1000
+#define SC520_PAR(x)		((0x88/sizeof(unsigned long)) + (x))
+#define NUM_SC520_PAR		16	/* total number of PAR registers */
+
+/*
+** The highest three bits in a PAR register determine what target
+** device is controlled by this PAR. Here, only ROMCS? and BOOTCS
+** devices are of interest.
+*/
+#define SC520_PAR_BOOTCS	(0x4<<29)
+#define SC520_PAR_ROMCS0	(0x5<<29)
+#define SC520_PAR_ROMCS1	(0x6<<29)
+#define SC520_PAR_TRGDEV	(0x7<<29)
+
+/*
+** Bits 28 thru 26 determine some attributes for the
+** region controlled by the PAR. (We only use non-cacheable)
+*/
+#define SC520_PAR_WRPROT	(1<<26)	/* write protected       */
+#define SC520_PAR_NOCACHE	(1<<27)	/* non-cacheable         */
+#define SC520_PAR_NOEXEC	(1<<28)	/* code execution denied */
+
+
+/*
+** Bit 25 determines the granularity: 4K or 64K
+*/
+#define SC520_PAR_PG_SIZ4	(0<<25)
+#define SC520_PAR_PG_SIZ64	(1<<25)
+
+/*
+** Build a value to be written into a PAR register.
+** We only need ROM entries, 64K page size:
+*/
+#define SC520_PAR_ENTRY(trgdev, address, size) \
+	((trgdev) | SC520_PAR_NOCACHE | SC520_PAR_PG_SIZ64 | \
+	(address) >> 16 | (((size) >> 16) - 1) << 14)
+
+struct sc520_par_table
+{
+	unsigned long trgdev;
+	unsigned long new_par;
+	unsigned long default_address;
+};
+
+static struct sc520_par_table par_table[NUM_FLASH_BANKS] =
+{
+	{	/* Flash Bank #0: selected by ROMCS0 */
+		SC520_PAR_ROMCS0,
+		SC520_PAR_ENTRY(SC520_PAR_ROMCS0, WINDOW_ADDR_0, WINDOW_SIZE_0),
+		WINDOW_ADDR_0_BIOS
+	},
+	{	/* Flash Bank #1: selected by ROMCS1 */
+		SC520_PAR_ROMCS1,
+		SC520_PAR_ENTRY(SC520_PAR_ROMCS1, WINDOW_ADDR_1, WINDOW_SIZE_1),
+		WINDOW_ADDR_1_BIOS
+	},
+	{	/* DIL (BIOS) Flash: selected by BOOTCS */
+		SC520_PAR_BOOTCS,
+		SC520_PAR_ENTRY(SC520_PAR_BOOTCS, WINDOW_ADDR_2, WINDOW_SIZE_2),
+		WINDOW_ADDR_2_BIOS
+	}
+};
+
+
+static void sc520cdp_setup_par(void)
+{
+	volatile unsigned long *mmcr;
+	unsigned long mmcr_val;
+	int i, j;
+
+	/* map in SC520's MMCR area */
+	mmcr = (unsigned long *)ioremap_nocache(SC520_MMCR_BASE, SC520_MMCR_EXTENT);
+	if(!mmcr) { /* ioremap_nocache failed: skip the PAR reprogramming */
+		/* force map_priv_2 fields to BIOS defaults: */
+		for(i = 0; i < NUM_FLASH_BANKS; i++)
+			sc520cdp_map[i].map_priv_2 = par_table[i].default_address;
+		return;
+	}
+
+	/*
+	** Find the PARxx registers that are reponsible for activating
+	** ROMCS0, ROMCS1 and BOOTCS. Reprogram each of these with a
+	** new value from the table.
+	*/
+	for(i = 0; i < NUM_FLASH_BANKS; i++) {		/* for each par_table entry  */
+		for(j = 0; j < NUM_SC520_PAR; j++) {	/* for each PAR register     */
+			mmcr_val = mmcr[SC520_PAR(j)];
+			/* if target device field matches, reprogram the PAR */
+			if((mmcr_val & SC520_PAR_TRGDEV) == par_table[i].trgdev)
+			{
+				mmcr[SC520_PAR(j)] = par_table[i].new_par;
+				break;
+			}
+		}
+		if(j == NUM_SC520_PAR)
+		{	/* no matching PAR found: try default BIOS address */
+			printk(KERN_NOTICE "Could not find PAR responsible for %s\n",
+				sc520cdp_map[i].name);
+			printk(KERN_NOTICE "Trying default address 0x%lx\n",
+				par_table[i].default_address);
+			sc520cdp_map[i].map_priv_2 = par_table[i].default_address;
+		}
+	}
+	iounmap((void *)mmcr);
+}
+#endif
+
+
+static int __init init_sc520cdp(void)
+{
+	int i, devices_found = 0;
+	
+#ifdef REPROGRAM_PAR
+	/* reprogram PAR registers so flash appears at the desired addresses */
+	sc520cdp_setup_par();
+#endif
+
+	for (i = 0; i < NUM_FLASH_BANKS; i++) {
+		printk(KERN_NOTICE "SC520 CDP flash device: %lx at %lx\n", sc520cdp_map[i].size, sc520cdp_map[i].map_priv_2);
+		sc520cdp_map[i].map_priv_1 = (unsigned long)ioremap_nocache(sc520cdp_map[i].map_priv_2, sc520cdp_map[i].size);
+
+		if (!sc520cdp_map[i].map_priv_1) {
+			printk("Failed to ioremap_nocache\n");
+			return -EIO;
+		}
+		mymtd[i] = do_map_probe("cfi", &sc520cdp_map[i]);
+		if(!mymtd[i])
+			mymtd[i] = do_map_probe("jedec", &sc520cdp_map[i]);
+		if(!mymtd[i])
+			mymtd[i] = do_map_probe("rom", &sc520cdp_map[i]);
+
+		if (mymtd[i]) {
+			mymtd[i]->module = THIS_MODULE;
+			add_mtd_device(mymtd[i]);
+			++devices_found;
+		}
+		else {
+			iounmap((void *)sc520cdp_map[i].map_priv_1);
+		}
+	}
+	return(devices_found ? 0 : -ENXIO);
+}
+
+static void __exit cleanup_sc520cdp(void)
+{
+	int i;
+	
+	for (i = 0; i < NUM_FLASH_BANKS; i++) {
+		if (mymtd[i]) {
+			del_mtd_device(mymtd[i]);
+			map_destroy(mymtd[i]);
+		}
+		if (sc520cdp_map[i].map_priv_1) {
+			iounmap((void *)sc520cdp_map[i].map_priv_1);
+			sc520cdp_map[i].map_priv_1 = 0;
+		}
+	}
+}
+
+module_init(init_sc520cdp);
+module_exit(cleanup_sc520cdp);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)