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
- Lines: 349
- Date:
Tue Jun 12 10:30:27 2001
- Orig file:
v2.4.5/linux/drivers/mtd/maps/sc520cdp.c
- Orig date:
Wed Dec 31 16:00:00 1969
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)