diff options
Diffstat (limited to 'target/linux/rdc/files/drivers/mtd/maps')
-rw-r--r-- | target/linux/rdc/files/drivers/mtd/maps/bifferboard-flash.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/target/linux/rdc/files/drivers/mtd/maps/bifferboard-flash.c b/target/linux/rdc/files/drivers/mtd/maps/bifferboard-flash.c new file mode 100644 index 000000000..e732334e9 --- /dev/null +++ b/target/linux/rdc/files/drivers/mtd/maps/bifferboard-flash.c @@ -0,0 +1,243 @@ +/* Flash mapping for Bifferboard, (c) bifferos@yahoo.co.uk + * Works with 1, 4 and 8MB flash versions + */ + +#include <linux/delay.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> + +#define DRV "bifferboard-flash: " + +static struct mtd_info *bb_mtd = NULL; + +#define SIZE_BIFFBOOT 0x10000 +#define SIZE_SECTOR 0x10000 +#define SIZE_CONFIG 0x02000 +#define SIZE_1MB 0x00100000 +#define SIZE_4MB 0x00400000 +#define SIZE_8MB 0x00800000 + +#define BASE_1MB 0xfff00000 +#define BASE_4MB 0xffc00000 +#define BASE_8MB 0xff800000 + +#define OFFS_KERNEL 0x6000 +#define OFFS_CONFIG 0x4000 + +/* + * Flash detection code. This is needed because the cfi_probe doesn't probe + * for the size correctly. You actually have to know the flash size before you + * call it, or so it seems. It does work correctly if you map the entire flash + * chip, but unsure of the implications for mapping more mem than exists. + */ + +static unsigned char ReadFlash(void* base, u32 addr) +{ + unsigned char val = *(volatile unsigned char *)(base+addr); + udelay(1); + return val; +} + +static void WriteFlash(void* base, u32 addr, unsigned short data) +{ + *(volatile unsigned short *)(base+addr) = data; +} + +static DEFINE_SPINLOCK(flash_lock); + +static u32 bb_detect(void) +{ + u32 ret = 0; + ulong flags; + + /* for detection, map in just the 1MB device, 1st 64k is enough */ + void* base = ioremap_nocache(BASE_1MB, 0x10000); + + if (!base) + { + pr_err(DRV "Failed to map flash for probing\n"); + return 0; + } + + spin_lock_irqsave(&flash_lock, flags); + + /* put flash in auto-detect mode */ + WriteFlash(base, 0xaaaa, 0xaaaa); + WriteFlash(base, 0x5554, 0x5555); + WriteFlash(base, 0xaaaa, 0x9090); + + /* Read the auto-config data - 4 values in total */ + ret = ReadFlash(base, 0x0000); ret <<= 8; + ret |= ReadFlash(base, 0x0200); ret <<= 8; + ret |= ReadFlash(base, 0x0003); ret <<= 8; + ret |= ReadFlash(base, 0x0002); + + /* exit the autodetect state */ + WriteFlash(base, 0x0000, 0xf0f0); + + spin_unlock_irqrestore(&flash_lock, flags); + + /* unmap it, it'll be re-mapped based on the detection */ + iounmap(base); + + return ret; +} + + +static struct map_info bb_map; + +/* Update the map, using the detected flash signature. */ +static int bb_adjust_map(unsigned long sig, struct map_info* m) +{ + m->bankwidth = 2; + switch (sig) + { + case 0x7f1c225b : + m->name = "ENLV800B"; + m->phys = BASE_1MB; + m->size = SIZE_1MB; + break; + case 0x7f1c22f9 : + m->name = "ENLV320B"; + m->phys = BASE_4MB; + m->size = SIZE_4MB; + break; + case 0x7f1c22cb : + m->name = "ENLV640B"; + m->phys = BASE_8MB; + m->size = SIZE_8MB; + break; + default: + return -ENXIO; + } + return 0; +} + + +/* adjust partition sizes, prior to adding the partition + * Values not specified by defines here are subject to replacement based on + * the real flash size. (0x00000000 being the exception, of course) */ + +static struct mtd_partition bb_parts[] = +{ +/* 0 */ { name: "kernel", offset: 0x00000000, size: 0x000fa000 }, +/* 1 */ { name: "rootfs", offset: MTDPART_OFS_APPEND, size: 0x002F0000 }, +/* 2 */ { name: "biffboot", offset: MTDPART_OFS_APPEND, size: MTDPART_SIZ_FULL, mask_flags: MTD_WRITEABLE }, +}; + + +/* returns the count of partitions to be used */ +static ulong bb_adjust_partitions(struct map_info* m) +{ + /* Read the kernel offset value, 1036 bytes into the config block. */ + u16 km_units = *((u16*)(m->virt + OFFS_CONFIG + 1036)); + u32 kernelmax = 0x200000; /* Biffboot 2.7 or earlier default */ + + /* special-case 1MB devices, because there are fewer partitions */ + if (m->size == SIZE_1MB) + { + bb_parts[0].size = SIZE_1MB - SIZE_BIFFBOOT; /* kernel */ + bb_parts[1].name = "biffboot"; /* biffboot */ + bb_parts[1].offset = MTDPART_OFS_APPEND; /* biffboot */ + bb_parts[1].size = MTDPART_SIZ_FULL; /* biffboot */ + bb_parts[1].mask_flags = MTD_WRITEABLE; + return 2; /* because there's no rootfs now */ + } + + /* sanity check */ + if (km_units > ((m->size-SIZE_BIFFBOOT)/SIZE_SECTOR)) + { + pr_err(DRV "config block has invalid kernelmax\n"); + return 0; + } + + kernelmax = km_units * SIZE_SECTOR; + + /* Kernel */ + bb_parts[0].size = kernelmax; + + /* rootfs */ + bb_parts[1].size = m->size - kernelmax - SIZE_BIFFBOOT; + + return 3; /* 3 partitions */ +} + + +static int __init init_bb_map(void) +{ + int ret; + ulong signature = bb_detect(); + ulong part_len; + + if (!signature) + { + pr_err(DRV "Undetected flash chip"); + return -ENXIO; + } + + ret = bb_adjust_map(signature, &bb_map); + + if (ret) + { + pr_err(DRV "Unrecognised flash chip (signature: 0x%lx)\n", signature); + return ret; + } + + bb_map.virt = ioremap_nocache(bb_map.phys, bb_map.size); + if (!bb_map.virt) + { + pr_err(DRV "ioremap\n"); + return -EIO; + } + + bb_mtd = do_map_probe("cfi_probe", &bb_map); + if (!bb_mtd) + { + /* cfi_probe fails here for 1MB devices */ + pr_err(DRV "cfi_probe\n"); + ret = -ENXIO; + goto err; + } + + part_len = bb_adjust_partitions(&bb_map); + if (!part_len) + { + pr_err(DRV "no partitions established"); + ret = -ENXIO; + goto err2; + } + + bb_mtd->owner = THIS_MODULE; + ret = add_mtd_partitions(bb_mtd, bb_parts, part_len); + if (ret) + { + pr_err(DRV "add_mtd_partitions\n"); + ret = -ENXIO; + goto err2; + } + + return 0; + +err2: + map_destroy(bb_mtd); +err: + iounmap(bb_map.virt); + + return ret; +} + +static void __exit exit_bb_map(void) +{ + del_mtd_partitions(bb_mtd); + map_destroy(bb_mtd); + iounmap(bb_map.virt); +} + +module_init(init_bb_map); +module_exit(exit_bb_map); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Bifferos <bifferos@yahoo.co.uk>"); +MODULE_DESCRIPTION("MTD map driver for Bifferboard"); + |