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");
 | 
						|
 |