/* Flash mapping for Bifferboard, (c) bifferos@yahoo.co.uk * Works with 1, 4 and 8MB flash versions */ #include #include #include #include #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 "); MODULE_DESCRIPTION("MTD map driver for Bifferboard");