mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 05:54:26 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			244 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			244 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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");
 | |
| 
 |