--- a/drivers/mtd/chips/Kconfig +++ b/drivers/mtd/chips/Kconfig @@ -220,6 +220,13 @@ config MTD_ROM This option enables basic support for ROM chips accessed through a bus mapping driver. +config MTD_SERIAL + tristate "Support for Serial chips in bus mapping" + depends on MTD + help + This option enables basic support for Serial chips accessed through + a bus mapping driver. + config MTD_ABSENT tristate "Support for absent chips in bus mapping" help --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -39,10 +39,15 @@ #include #include +//****** Storlink SoC ****** #define AMD_BOOTLOC_BUG -#define FORCE_WORD_WRITE 0 - -#define MAX_WORD_RETRIES 3 +//#define FORCE_WORD_WRITE 0 +#define FORCE_WORD_WRITE 1 +#define FORCE_FAST_PROG 0 + +//#define MAX_WORD_RETRIES 3 +#define MAX_WORD_RETRIES 3 // CONFIG_MTD_CFI_AMDSTD_RETRY +//************************** #define MANUFACTURER_AMD 0x0001 #define MANUFACTURER_ATMEL 0x001F @@ -322,6 +327,13 @@ struct mtd_info *cfi_cmdset_0002(struct #endif bootloc = extp->TopBottom; +//****** Storlink SoC ****** + if(bootloc == 5) + { + bootloc = 3; + extp->TopBottom = 3; + } +//************************** if ((bootloc != 2) && (bootloc != 3)) { printk(KERN_WARNING "%s: CFI does not contain boot " "bank location. Assuming top.\n", map->name); @@ -340,6 +352,9 @@ struct mtd_info *cfi_cmdset_0002(struct cfi->cfiq->EraseRegionInfo[j] = swap; } } +#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1 + cfi->device_type = CFI_DEVICETYPE_X8; +#endif /* Set the default CFI lock/unlock addresses */ cfi->addr_unlock1 = 0x555; cfi->addr_unlock2 = 0x2aa; @@ -461,6 +476,7 @@ static int __xipram chip_ready(struct ma map_word d, t; d = map_read(map, addr); + udelay(20); //Storlink SoC t = map_read(map, addr); return map_word_equal(map, d, t); @@ -626,7 +642,9 @@ static void put_chip(struct map_info *ma default: printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate); } +//****** Storlink SoC ****** wake_up(&chip->wq); +//************************** } #ifdef CONFIG_MTD_XIP @@ -940,7 +958,9 @@ static inline int do_read_secsi_onechip( cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); +//****** Storlink SoC ****** wake_up(&chip->wq); +//************************** spin_unlock(chip->mutex); return 0; @@ -1005,7 +1025,10 @@ static int __xipram do_write_oneword(str */ unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; int ret = 0; - map_word oldd; +//****** Storlink SoC ****** +// map_word oldd; + map_word oldd, tmp; +//************************** int retry_cnt = 0; adr += chip->start; @@ -1037,9 +1060,15 @@ static int __xipram do_write_oneword(str ENABLE_VPP(map); xip_disable(map, chip, adr); retry: +//****** Storlink SoC ****** +#if FORCE_FAST_PROG /* Unlock bypass */ + cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); +#else cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); +#endif +//************************** map_write(map, datum, adr); chip->state = FL_WRITING; @@ -1072,7 +1101,13 @@ static int __xipram do_write_oneword(str } if (chip_ready(map, adr)) - break; + { + tmp = map_read(map, adr); + if(map_word_equal(map, tmp, datum)) +// goto op_done; + break; + + } /* Latency issues. Drop the lock, wait a while and retry */ UDELAY(map, chip, adr, 1); @@ -1084,8 +1119,17 @@ static int __xipram do_write_oneword(str /* FIXME - should have reset delay before continuing */ if (++retry_cnt <= MAX_WORD_RETRIES) + { +//****** Storlink SoC ****** +#if FORCE_FAST_PROG + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + //udelay(1); +#endif + udelay(1); goto retry; - + } ret = -EIO; } xip_enable(map, chip, adr); @@ -1171,7 +1215,14 @@ static int cfi_amdstd_write_words(struct return 0; } } - +//****** Storlink SoC ****** + map_write( map, CMD(0xF0), chipstart ); +#if FORCE_FAST_PROG + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, cfi->device_type, NULL); +#endif +//************************** /* We are now aligned, write as much as possible */ while(len >= map_bankwidth(map)) { map_word datum; @@ -1181,7 +1232,15 @@ static int cfi_amdstd_write_words(struct ret = do_write_oneword(map, &cfi->chips[chipnum], ofs, datum); if (ret) + { +//****** Storlink SoC ****** +#if FORCE_FAST_PROG + /* Get out of unlock bypass mode */ + cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); +#endif return ret; + } ofs += map_bankwidth(map); buf += map_bankwidth(map); @@ -1189,19 +1248,38 @@ static int cfi_amdstd_write_words(struct len -= map_bankwidth(map); if (ofs >> cfi->chipshift) { +//****** Storlink SoC ****** +#if FORCE_FAST_PROG + /* Get out of unlock bypass mode */ + cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); +#endif chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; chipstart = cfi->chips[chipnum].start; +#if FORCE_FAST_PROG + /* Go into unlock bypass mode for next set of chips */ + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, cfi->device_type, NULL); +#endif } } +#if FORCE_FAST_PROG + /* Get out of unlock bypass mode */ + cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); +#endif + /* Write the trailing bytes if any */ if (len & (map_bankwidth(map)-1)) { map_word tmp_buf; retry1: + spin_lock(cfi->chips[chipnum].mutex); if (cfi->chips[chipnum].state != FL_READY) { @@ -1221,7 +1299,11 @@ static int cfi_amdstd_write_words(struct #endif goto retry1; } - +#if FORCE_FAST_PROG + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, cfi->device_type, NULL); +#endif tmp_buf = map_read(map, ofs + chipstart); spin_unlock(cfi->chips[chipnum].mutex); @@ -1231,11 +1313,23 @@ static int cfi_amdstd_write_words(struct ret = do_write_oneword(map, &cfi->chips[chipnum], ofs, tmp_buf); if (ret) + { +#if FORCE_FAST_PROG + /* Get out of unlock bypass mode */ + cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); +#endif return ret; - + } +#if FORCE_FAST_PROG + /* Get out of unlock bypass mode */ + cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); +#endif (*retlen) += len; } + map_write( map, CMD(0xF0), chipstart ); return 0; } @@ -1275,6 +1369,7 @@ static int __xipram do_write_buffer(stru ENABLE_VPP(map); xip_disable(map, chip, cmd_adr); + map_write( map, CMD(0xF0), chip->start ); //Storlink cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); //cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); @@ -1535,6 +1630,9 @@ static int __xipram do_erase_oneblock(st DECLARE_WAITQUEUE(wait, current); int ret = 0; +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_lock(); // sl2312 share pin lock +#endif adr += chip->start; spin_lock(chip->mutex); @@ -1613,6 +1711,9 @@ static int __xipram do_erase_oneblock(st chip->state = FL_READY; put_chip(map, chip, adr); spin_unlock(chip->mutex); +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return ret; } --- /dev/null +++ b/drivers/mtd/chips/map_serial.c @@ -0,0 +1,188 @@ +/* + * Common code to handle map devices which are simple ROM + * (C) 2000 Red Hat. GPL'd. + * $Id: map_serial.c,v 1.3 2006/06/05 02:34:54 middle Exp $ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include //add +#include +#include + +static int mapserial_erase(struct mtd_info *mtd, struct erase_info *instr); +static int mapserial_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int mapserial_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +static void mapserial_nop (struct mtd_info *); +struct mtd_info *map_serial_probe(struct map_info *map); + +extern int m25p80_sector_erase(__u32 address, __u32 schip_en); + +static struct mtd_chip_driver mapserial_chipdrv = { + probe: map_serial_probe, + name: "map_serial", + module: THIS_MODULE +}; + +struct mtd_info *map_serial_probe(struct map_info *map) +{ + struct mtd_info *mtd; + + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + if (!mtd) + return NULL; + + memset(mtd, 0, sizeof(*mtd)); + + map->fldrv = &mapserial_chipdrv; + mtd->priv = map; + mtd->name = map->name; + mtd->type = MTD_OTHER; + mtd->erase = mapserial_erase; + mtd->size = map->size; + mtd->read = mapserial_read; + mtd->write = mapserial_write; + mtd->sync = mapserial_nop; + mtd->flags = (MTD_WRITEABLE|MTD_ERASEABLE); +// mtd->erasesize = 512; // page size; +#ifdef CONFIG_MTD_SL2312_SERIAL_ST + mtd->erasesize = M25P80_SECTOR_SIZE; // block size; +#else + mtd->erasesize = 0x1000; // block size; +#endif + + __module_get(THIS_MODULE); + //MOD_INC_USE_COUNT; + return mtd; +} + +#define FLASH_ACCESS_OFFSET 0x00000010 +#define FLASH_ADDRESS_OFFSET 0x00000014 +#define FLASH_WRITE_DATA_OFFSET 0x00000018 +#define FLASH_READ_DATA_OFFSET 0x00000018 + +static __u32 readflash_ctrl_reg(__u32 ofs) +{ + __u32 *base; + + base = (__u32 *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE + ofs)); + return __raw_readl(base); +} + +static void writeflash_ctrl_reg(__u32 data, __u32 ofs) +{ + __u32 *base; + + base = (__u32 *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE + ofs)); + __raw_writel(data, base); +} + +static int mapserial_erase_block(struct map_info *map,unsigned int block) +{ + + __u32 address; +#ifdef CONFIG_MTD_SL2312_SERIAL_ST + + if(!m25p80_sector_erase(block, 0)) + return (MTD_ERASE_DONE); +#else + __u32 opcode; + __u32 count=0; +// __u8 status; + + // printk("mapserial_erase_block : erase block %d \n",block); +// opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS | cmd; + opcode = 0x80000000 | 0x0200 | 0x50; + address = (block << 13); + writeflash_ctrl_reg(address,FLASH_ADDRESS_OFFSET); + writeflash_ctrl_reg(opcode,FLASH_ACCESS_OFFSET); + opcode=readflash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(opcode&0x80000000) + { + opcode = readflash_ctrl_reg(FLASH_ACCESS_OFFSET); + count++; + if (count > 10000) + { + return (MTD_ERASE_FAILED); + } + } + return (MTD_ERASE_DONE); +#endif +} + +static int mapserial_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct map_info *map = (struct map_info *)mtd->priv; + unsigned int addr; + int len; + unsigned int block; + unsigned int ret=0; + + addr = instr->addr; + len = instr->len; + while (len > 0) + { + block = addr / mtd->erasesize; +#ifdef CONFIG_MTD_SL2312_SERIAL_ST + ret = mapserial_erase_block(map,addr); +#else + ret = mapserial_erase_block(map,block); +#endif + addr = addr + mtd->erasesize; + len = len - mtd->erasesize; + } + return (ret); +} + +static int mapserial_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = (struct map_info *)mtd->priv; +// printk("mapserial_read : \n"); + map->copy_from(map, buf, from, len); + *retlen = len; + return 0; +} + +static void mapserial_nop(struct mtd_info *mtd) +{ + /* Nothing to see here */ +} + +static int mapserial_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ + struct map_info *map = (struct map_info *)mtd->priv; +// printk("mapserial_write : buf %x to %x len %x \n",(int)buf, (int)to, (int)len); + //map->copy_to(map, buf, to, len); + map->copy_to(map, to, buf, len); + *retlen = len; + return 0; +} + +int __init map_serial_init(void) +{ + register_mtd_chip_driver(&mapserial_chipdrv); + return 0; +} + +static void __exit map_serial_exit(void) +{ + unregister_mtd_chip_driver(&mapserial_chipdrv); +} + +module_init(map_serial_init); +module_exit(map_serial_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse "); +MODULE_DESCRIPTION("MTD chip driver for ROM chips"); --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -614,5 +614,30 @@ config MTD_PLATRAM This selection automatically selects the map_ram driver. +#*************************************************************************************** +# Storlink parallel/Serial Flash configuration +#*************************************************************************************** +config MTD_SL2312_CFI + tristate "CFI Flash device mapped on SL2312" + depends on MTD_CFI + help + Map driver for SL2312 demo board. + +config MTD_SL2312_SERIAL_ATMEL + tristate "ATMEL Serial Flash device mapped on SL2312" + depends on MTD_PARTITIONS && ARCH_SL2312 + help + Map driver for SL2312 demo board. + +config MTD_SL2312_SERIAL_ST + tristate "ST Serial Flash device mapped on SL2312" + depends on MTD_PARTITIONS && ARCH_SL2312 + help + Map driver for SL2312 demo board. + +config SL2312_SHARE_PIN + tristate "Parallel Flash share pin on SL2312 ASIC" + depends on SL3516_ASIC + endmenu --- /dev/null +++ b/drivers/mtd/maps/sl2312-flash-atmel.c @@ -0,0 +1,554 @@ +/* + * $Id: sl2312-flash-atmel.c,v 1.2 2006/06/05 02:35:57 middle Exp $ + * + * Flash and EPROM on Hitachi Solution Engine and similar boards. + * + * (C) 2001 Red Hat, Inc. + * + * GPL'd + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include //add + + +#define g_page_addr AT45DB321_PAGE_SHIFT //321 : shift 10 ; 642 : shift 11 +#define g_chipen SERIAL_FLASH_CHIP0_EN //atmel + +extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); + +void address_to_page(__u32 address, __u16 *page, __u16 *offset) +{ + *page = address / SPAGE_SIZE; + *offset = address % SPAGE_SIZE; +} + +static __u32 read_flash_ctrl_reg(__u32 ofs) +{ + __u32 *base; + + base = (__u32 *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE + ofs)); + return __raw_readl(base); +} + +static void write_flash_ctrl_reg(__u32 ofs,__u32 data) +{ + __u32 *base; + + base = (__u32 *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE + ofs)); + __raw_writel(data, base); +} + +void atmel_read_status(__u8 cmd, __u8 *data) +{ + __u32 opcode; + __u32 value; + + opcode = 0x80000000 | FLASH_ACCESS_ACTION_OPCODE_DATA | cmd | g_chipen; + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(opcode&0x80000000) + { + opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + flash_delay(); + schedule(); + } + + value=read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET); + *data = value & 0xff; +} + +void main_memory_page_read(__u8 cmd, __u16 page, __u16 offset, __u8 *data) +{ + __u32 opcode; + __u32 address; + __u32 value; + + opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS_4X_DATA | cmd | g_chipen; + address = (page << g_page_addr) + offset; + write_flash_ctrl_reg(FLASH_ADDRESS_OFFSET, address); + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(opcode&0x80000000) + { + opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + flash_delay(); + schedule(); + } + + value=read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET); + *data = value & 0xff; +} + +void buffer_to_main_memory(__u8 cmd, __u16 page) +{ + __u32 opcode; + __u32 address; + __u8 status; + + opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS | cmd | g_chipen; + address = (page << g_page_addr); + write_flash_ctrl_reg(FLASH_ADDRESS_OFFSET, address); + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(opcode&0x80000000) + { + opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + flash_delay(); + schedule(); + } + atmel_read_status(READ_STATUS_SPI, &status); + while(!(status&0x80)) + { + atmel_read_status(READ_STATUS_SPI, &status); + flash_delay(); + schedule(); + } + +} + + +void atmel_flash_read_page(__u32 address, __u8 *buffer, __u32 len) +{ + __u8 byte; + __u16 page, offset; + __u16 i; + + address_to_page(address, &page, &offset); + + for(i=0; i> 8; +//printk("sl2312_write32:: address = %08x data = %c \n",ofs,byte); + } +#else + write_flash_ctrl_reg(ofs, d); +#endif +} + +void sl2312_write8(struct map_info *map, __u8 d, unsigned long ofs) +{ + __u16 page, offset; + + address_to_page(ofs, &page, &offset); + main_memory_page_program(MAIN_MEMORY_PROGRAM_BUFFER1, page, offset, d); +//printk("sl2312_write8:: address = %08x data = %c \n",ofs,d); + +} + +void sl2312_copy_from(struct map_info *map, void *buf, unsigned long ofs, ssize_t len) +{ + __u32 size; + __u8 *buffer; + __u32 length;//i, j, + + //printk("sl2312_copy_from:: address = %08x datalen = %d \n",ofs,len); + + length = len; + buffer = (__u8 *)buf; + while(len) + { + size = SPAGE_SIZE - (ofs%SPAGE_SIZE); + if(size > len) + size = len; + atmel_flash_read_page(ofs, buffer, size); + buffer+=size; + ofs+=size; + len -= size; + } + +#if 0 + buffer = (__u8 *)buf; + for(i=0; i len) + size = len; + atmel_flash_program_page(ofs, buffer, size); + buffer+=size; + ofs+=size; + len-=size; + } + + +} + + +static struct mtd_info *serial_mtd; + +static struct mtd_partition *parsed_parts; + +static struct map_info sl2312_serial_map = { +// name: "SL2312 serial flash", +// size: 4194304, //0x400000, +// //buswidth: 4, +// bankwidth: 4, +// phys: SL2312_FLASH_BASE, +//#ifdef CONFIG_MTD_COMPLEX_MAPPINGS +// //read32: sl2312_read32, +// //read8: sl2312_read8, +// copy_from: sl2312_copy_from, +// //write8: sl2312_write8, +// //write32: sl2312_write32, +// read: sl2312_read32, +// write: sl2312_write32, +// copy_to: sl2312_copy_to +//#endif + .name = "SL2312 serial flash", + .size = 4194304, //0x400000, + //buswidth: 4, + .bankwidth = 4, + .phys = SL2312_FLASH_BASE, +#ifdef CONFIG_MTD_COMPLEX_MAPPINGS + //read32: sl2312_read32, + //read8: sl2312_read8, + .copy_from = sl2312_copy_from, + //write8: sl2312_write8, + //write32: sl2312_write32, + .read = sl2312_read32, + .write = sl2312_write32, + .copy_to = sl2312_copy_to +#endif +}; + + + +static struct mtd_partition sl2312_partitions[] = { + + + ///* boot code */ + //{ name: "bootloader", offset: 0x00000000, size: 0x20000, }, + ///* kernel image */ + //{ name: "kerel image", offset: 0x000020000, size: 0x2E0000 }, + ///* All else is writable (e.g. JFFS) */ + //{ name: "user data", offset: 0x00300000, size: 0x00100000, }, + /* boot code */ + { .name = "bootloader", .offset = 0x00000000, .size = 0x20000, }, + /* kernel image */ + { .name = "kerel image", .offset = 0x000020000, .size = 0xE0000 }, + /* All else is writable (e.g. JFFS) */ + { .name = "user data", .offset = 0x00100000, .size = 0x00300000, }, + + +}; + + + +static int __init init_sl2312_maps(void) +{ + int nr_parts = 0; + struct mtd_partition *parts; + + serial_mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!serial_mtd) + return NULL; + + memset(serial_mtd, 0, sizeof(struct mtd_info)); + //sl2312flash_map.virt = (unsigned long)ioremap(SL2312_FLASH_BASE, FLASH_SIZE); + //sl2312_serial_map.map_priv_1 = (unsigned long)ioremap(SL2312_FLASH_BASE, SFLASH_SIZE);//(unsigned long)FLASH_VBASE; + sl2312_serial_map.virt = (unsigned long)ioremap(SL2312_FLASH_BASE, SFLASH_SIZE);//(unsigned long)ioremap(FLASH_START, SFLASH_SIZE); + if (!sl2312_serial_map.virt) { + printk(" failed to ioremap \n"); + return -EIO; + } + serial_mtd = do_map_probe("map_serial", &sl2312_serial_map); + if (serial_mtd) { + //serial_mtd->module = THIS_MODULE; + serial_mtd->owner = THIS_MODULE; + + } + +#ifdef CONFIG_MTD_REDBOOT_PARTS + nr_parts = parse_redboot_partitions(serial_mtd, &parsed_parts); + if (nr_parts > 0) + printk(KERN_NOTICE "Found RedBoot partition table.\n"); + else if (nr_parts < 0) + printk(KERN_NOTICE "Error looking for RedBoot partitions.\n"); +#else + parsed_parts = sl2312_partitions; + parts = sl2312_partitions; + nr_parts = sizeof(sl2312_partitions)/sizeof(*parts); + nr_parts = sizeof(sl2312_partitions)/sizeof(*parsed_parts); +#endif /* CONFIG_MTD_REDBOOT_PARTS */ + + if (nr_parts > 0) + add_mtd_partitions(serial_mtd, parsed_parts, nr_parts); + else + add_mtd_device(serial_mtd); + + return 0; +} + +static void __exit cleanup_sl2312_maps(void) +{ + if (parsed_parts) + del_mtd_partitions(serial_mtd); + else + del_mtd_device(serial_mtd); + + map_destroy(serial_mtd); + + +} + +module_init(init_sl2312_maps); +module_exit(cleanup_sl2312_maps); + + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Plus Chen "); +MODULE_DESCRIPTION("MTD map driver for Storlink Sword boards"); + --- /dev/null +++ b/drivers/mtd/maps/sl2312-flash-cfi.c @@ -0,0 +1,370 @@ +/*====================================================================== + + 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 +======================================================================*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include "sl2312_flashmap.h" + + +//extern int parse_afs_partitions(struct mtd_info *, struct mtd_partition **); + +/* the base address of FLASH control register */ +#define FLASH_CONTROL_BASE_ADDR (IO_ADDRESS(SL2312_FLASH_CTRL_BASE)) +#define SL2312_GLOBAL_BASE_ADDR (IO_ADDRESS(SL2312_GLOBAL_BASE)) + +/* define read/write register utility */ +#define FLASH_READ_REG(offset) (__raw_readl(offset+FLASH_CONTROL_BASE_ADDR)) +#define FLASH_WRITE_REG(offset,val) (__raw_writel(val,offset+FLASH_CONTROL_BASE_ADDR)) + +/* the offset of FLASH control register */ +enum EMAC_REGISTER { + FLASH_ID = 0x0000, + FLASH_STATUS = 0x0008, + FLASH_TYPE = 0x000c, + FLASH_ACCESS = 0x0020, + FLASH_ADDRESS = 0x0024, + FLASH_DATA = 0x0028, + FLASH_TIMING = 0x002c, +}; + +//#define FLASH_BASE FLASH_CONTROL_BASE_ADDR +//#define FLASH_SIZE 0x00800000 //INTEGRATOR_FLASH_SIZE + +//#define FLASH_PART_SIZE 8388608 + +static unsigned int flash_indirect_access = 0; + +#ifdef CONFIG_SL2312_SHARE_PIN +static unsigned int chip_en = 0x00000000; + +void sl2312flash_enable_parallel_flash(void) +{ + unsigned int reg_val; + + reg_val = readl(SL2312_GLOBAL_BASE_ADDR + 0x30); + reg_val = reg_val & 0xfffffffd; + writel(reg_val,SL2312_GLOBAL_BASE_ADDR + 0x30); + return; +} + +void sl2312flash_disable_parallel_flash(void) +{ + unsigned int reg_val; + + reg_val = readl(SL2312_GLOBAL_BASE_ADDR + 0x30); + reg_val = reg_val | 0x00000002; + writel(reg_val,SL2312_GLOBAL_BASE_ADDR + 0x30); + return; +} +#endif + + +static struct map_info sl2312flash_map = +{ + name: "SL2312 CFI Flash", + size: FLASH_SIZE, + bankwidth: 2, + //bankwidth: 1, //for 8 bits width + phys: SL2312_FLASH_BASE, +}; + +static struct mtd_info *mtd; +#if 0 +static struct mtd_partition sl2312_partitions[] = { + /* boot code */ + { + name: "bootloader", + offset: 0x00000000, + size: 0x20000, +// mask_flags: MTD_WRITEABLE, + }, + /* kernel image */ + { + name: "kerel image", + offset: 0x00020000, + size: 0x2E0000 + }, + /* All else is writable (e.g. JFFS) */ + { + name: "user data", + offset: 0x00300000, + size: 0x00100000, + } +}; +#endif + + + +static int __init sl2312flash_init(void) +{ + struct mtd_partition *parts; + int nr_parts = 0; + int ret; +#ifndef CONFIG_SL2312_SHARE_PIN + unsigned int reg_val; +#endif + + printk("SL2312 MTD Driver Init.......\n"); + +#ifndef CONFIG_SL2312_SHARE_PIN + /* enable flash */ + reg_val = readl(SL2312_GLOBAL_BASE_ADDR + 0x30); + reg_val = reg_val & 0xfffffffd; + writel(reg_val,SL2312_GLOBAL_BASE_ADDR + 0x30); +#else + sl2312flash_enable_parallel_flash(); /* enable Parallel FLASH */ +#endif + FLASH_WRITE_REG(FLASH_ACCESS,0x00004000); /* parallel flash direct access mode */ + ret = FLASH_READ_REG(FLASH_ACCESS); + if (ret == 0x00004000) + { + flash_indirect_access = 0; /* parallel flash direct access */ + } + else + { + flash_indirect_access = 1; /* parallel flash indirect access */ + } + + /* + * Also, the CFI layer automatically works out what size + * of chips we have, and does the necessary identification + * for us automatically. + */ +#ifdef CONFIG_GEMINI_IPI + sl2312flash_map.virt = FLASH_VBASE;//(unsigned int *)ioremap(SL2312_FLASH_BASE, FLASH_SIZE); +#else + sl2312flash_map.virt = (unsigned int *)ioremap(SL2312_FLASH_BASE, FLASH_SIZE); +#endif + //printk("sl2312flash_map.virt = %08x\n",(unsigned int)sl2312flash_map.virt); + +// simple_map_init(&sl2312flash_map); + + mtd = do_map_probe("cfi_probe", &sl2312flash_map); + if (!mtd) + { +#ifdef CONFIG_SL2312_SHARE_PIN + sl2312flash_disable_parallel_flash(); /* disable Parallel FLASH */ +#endif + return -ENXIO; + } + mtd->owner = THIS_MODULE; +// mtd->erase = flash_erase; +// mtd->read = flash_read; +// mtd->write = flash_write; + + parts = sl2312_partitions; + nr_parts = sizeof(sl2312_partitions)/sizeof(*parts); + ret = add_mtd_partitions(mtd, parts, nr_parts); + /*If we got an error, free all resources.*/ + if (ret < 0) { + del_mtd_partitions(mtd); + map_destroy(mtd); + } +#ifdef CONFIG_SL2312_SHARE_PIN + sl2312flash_disable_parallel_flash(); /* disable Parallel FLASH */ +#endif + printk("SL2312 MTD Driver Init Success ......\n"); + return ret; +} + +static void __exit sl2312flash_exit(void) +{ + if (mtd) { + del_mtd_partitions(mtd); + map_destroy(mtd); + } + + if (sl2312flash_map.virt) { + iounmap((void *)sl2312flash_map.virt); + sl2312flash_map.virt = 0; + } +} + +char chrtohex(char c) +{ + char val; + if ((c >= '0') && (c <= '9')) + { + val = c - '0'; + return val; + } + else if ((c >= 'a') && (c <= 'f')) + { + val = 10 + (c - 'a'); + return val; + } + else if ((c >= 'A') && (c <= 'F')) + { + val = 10 + (c - 'A'); + return val; + } + printk("<1>Error number\n"); + return 0; +} + + +int get_vlaninfo(vlaninfo* vlan) +{ + vctl_mheader head; + vctl_entry entry; + struct mtd_info *mymtd=NULL; + int i, j, loc = 0; + char *payload=0, *tmp1, *tmp2, tmp3[9]; + size_t retlen; + + #ifdef CONFIG_SL2312_SHARE_PIN + sl2312flash_enable_parallel_flash(); + #endif + for(i=0;iname: %s\n", mymtd->name); + if(mymtd && !strcmp(mymtd->name,"VCTL")) + { + // printk("%s\n", mymtd->name); + break; + } + } + if( i >= MAX_MTD_DEVICES) + { + printk("Can't find version control\n"); + #ifdef CONFIG_SL2312_SHARE_PIN + sl2312flash_disable_parallel_flash(); + #endif + return 0; + } + + if (!mymtd | !mymtd->read) + { + printk("<1>Can't read Version Configuration\n"); + #ifdef CONFIG_SL2312_SHARE_PIN + sl2312flash_disable_parallel_flash(); + #endif + return 0; + } + + mymtd->read(mymtd, 0, VCTL_HEAD_SIZE, &retlen, (u_char*)&head); + // printk("entry header: %c%c%c%c\n", head.header[0], head.header[1], head.header[2], head.header[3]); + // printk("entry number: %x\n", head.entry_num); + if ( strncmp(head.header, "FLFM", 4) ) + { + printk("VCTL is a erase block\n"); + #ifdef CONFIG_SL2312_SHARE_PIN + sl2312flash_disable_parallel_flash(); + #endif + return 0; + } + loc += retlen; + for (i = 0; i < head.entry_num; i++) + { + mymtd->read(mymtd, loc, VCTL_ENTRY_LEN, &retlen, (u_char*)&entry); + // printk("type: %x\n", entry.type); + // printk("size: %x\n", entry.size); + strncpy(tmp3, entry.header, 4); + if (entry.type == VCT_VLAN) + { + for (j = 0; j < 6 ; j++) + { + vlan[0].mac[j] = 0; + vlan[1].mac[j] = 0; + } + vlan[0].vlanid = 1; + vlan[1].vlanid = 2; + vlan[0].vlanmap = 0x7F; + vlan[1].vlanmap = 0x80; + + payload = (char *)kmalloc(entry.size - VCTL_ENTRY_LEN, GFP_KERNEL); + loc += VCTL_ENTRY_LEN; + mymtd->read(mymtd, loc, entry.size - VCTL_ENTRY_LEN, &retlen, payload); + // printk("%s\n", payload); + tmp1 = strstr(payload, "MAC1:"); + tmp2 = strstr(payload, "MAC2:"); + if(!tmp1||!tmp2){ + kfree(payload); + #ifdef CONFIG_SL2312_SHARE_PIN + sl2312flash_disable_parallel_flash(); + #endif + printk("Error VCTL format!!\n"); + return 0; + } + tmp1 += 7; + tmp2 += 7; + + + for (j = 0; j < 6; j++) + { + vlan[0].mac[j] = chrtohex(tmp1[2*j])*16 + chrtohex(tmp1[(2*j)+1]); + vlan[1].mac[j] = chrtohex(tmp2[2*j])*16 + chrtohex(tmp2[(2*j)+1]); + } + tmp1 = strstr(payload, "ID1:"); + tmp2 = strstr(payload, "ID2:"); + tmp1 += 4; + tmp2 += 4; + vlan[0].vlanid = tmp1[0] - '0'; + vlan[1].vlanid = tmp2[0] - '0'; + tmp1 = strstr(payload, "MAP1:"); + tmp2 = strstr(payload, "MAP2:"); + tmp1 += 7; + tmp2 += 7; + vlan[0].vlanmap = chrtohex(tmp1[0]) * 16 + chrtohex(tmp1[1]); + vlan[1].vlanmap = chrtohex(tmp2[0]) * 16 + chrtohex(tmp2[1]); + // printk("Vlan1 id:%x map:%02x mac:%x%x%x%x%x%x\n", vlan[0].vlanid, vlan[0].vlanmap, vlan[0].mac[0], vlan[0].mac[1], vlan[0].mac[2], vlan[0].mac[3], vlan[0].mac[4], vlan[0].mac[5]); + // printk("Vlan2 id:%x map:%02x mac:%x%x%x%x%x%x\n", vlan[1].vlanid, vlan[1].vlanmap, vlan[1].mac[0], vlan[1].mac[1], vlan[1].mac[2], vlan[1].mac[3], vlan[1].mac[4], vlan[1].mac[5]); + break; + } + loc += entry.size; + } + if ( entry.type == VCT_VLAN ) + { + #ifdef CONFIG_SL2312_SHARE_PIN + sl2312flash_disable_parallel_flash(); + #endif + kfree(payload); + return 1; + } + if (i >= head.entry_num) + printk("Can't find vlan information\n"); + #ifdef CONFIG_SL2312_SHARE_PIN + sl2312flash_disable_parallel_flash(); + #endif + return 0; +} + +EXPORT_SYMBOL(get_vlaninfo); + + +module_init(sl2312flash_init); +module_exit(sl2312flash_exit); + +MODULE_AUTHOR("Storlink Ltd"); +MODULE_DESCRIPTION("CFI map driver"); +MODULE_LICENSE("GPL"); --- /dev/null +++ b/drivers/mtd/maps/sl2312-flash-m25p80.c @@ -0,0 +1,498 @@ +/* + * $Id: sl2312-flash-m25p80.c,v 1.2 2006/06/02 08:46:02 middle Exp $ + * + * Flash and EPROM on Hitachi Solution Engine and similar boards. + * + * (C) 2001 Red Hat, Inc. + * + * GPL'd + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include //add +#define g_chipen SERIAL_FLASH_CHIP0_EN //ST + +//static int m25p80_page_program(__u32 address, __u8 data, __u32 schip_en); +static void m25p80_write_cmd(__u8 cmd, __u32 schip_en); +extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); + + +static __u32 read_flash_ctrl_reg(__u32 ofs) +{ + __u32 *base; + + base = (__u32 *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE + ofs)); + return __raw_readl(base); +} + +static void write_flash_ctrl_reg(__u32 ofs,__u32 data) +{ + __u32 *base; + + base = (__u32 *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE + ofs)); + __raw_writel(data, base); +} + +static void m25p80_read(__u32 address, __u8 *data, __u32 schip_en) +{ + __u32 opcode,status; + __u32 value; + + //opcode = 0x80000000 | FLASH_ACCESS_ACTION_OPCODE_DATA | M25P80_READ; + opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS_DATA | M25P80_READ; + write_flash_ctrl_reg(FLASH_ADDRESS_OFFSET, address); + + opcode|=g_chipen; + + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + status=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(status&0x80000000) + { + status=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + flash_delay(); + schedule(); + } + + value=read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET); + *data = value & 0xff; +} + +static int m25p80_page_program(__u32 address, __u8 *data, __u32 schip_en) +{ + __u32 opcode; + __u32 status; + __u32 tmp; + int res = FLASH_ERR_OK; + //volatile FLASH_DATA_T* data_ptr = (volatile FLASH_DATA_T*) data; + opcode = 0x80000000 | FLASH_ACCESS_ACTION_OPCODE_DATA | M25P80_READ_STATUS; + + opcode|=g_chipen; + + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(tmp&0x80000000) + { + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + flash_delay(); + schedule(); + } + //middle delay_ms(130); + status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET); + if((status&0x02)==0x02) + { + //middle delay_ms(100); + m25p80_write_cmd(M25P80_WRITE_DISABLE, schip_en); + } + + + m25p80_write_cmd(M25P80_WRITE_ENABLE, schip_en); + ////middle delay_ms(10); + opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS_DATA | M25P80_PAGE_PROGRAM; + write_flash_ctrl_reg(FLASH_ADDRESS_OFFSET, address); + write_flash_ctrl_reg(FLASH_WRITE_DATA_OFFSET, *data); + + //status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET); + //while(status!=data) + //{ + // status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET); + // //middle delay_ms(10); + //} + + opcode|=g_chipen; + + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(tmp&0x80000000) + { + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + flash_delay(); + schedule(); + } + //opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + + opcode = 0x80000000 | FLASH_ACCESS_ACTION_OPCODE_DATA | M25P80_READ_STATUS; + + opcode|=g_chipen; + + + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(tmp&0x80000000) + { + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + flash_delay(); + schedule(); + } + status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET); + //while(status&0xfd) + while(status&0x01) + { + //if((status&0x9c)!=0) + // printf(" m25p80_page_program Protect Status = %x\n",status); + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(tmp&0x80000000) + { + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + flash_delay(); + schedule(); + } + status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET); + flash_delay(); + schedule(); + //middle delay_ms(50); + } + //printf("status = %x, data = %x\n",status,data); + if((status&0x02)==0x02) + { + //middle delay_ms(100); + m25p80_write_cmd(M25P80_WRITE_DISABLE, schip_en); + } + //};//while (len > 0) + return res; +} + +void m25p80_copy_from(struct map_info *map, void *buf, unsigned long ofs, ssize_t len) +{ +// __u32 size; + __u8 *buffer; + __u32 length;//i, j, + + length = len; + buffer = (__u8 *)buf; + while(len) + { + m25p80_read(ofs, buffer, g_chipen); + buffer++; + ofs++; + len --; + } ; + +} + +__u32 m25p80_read32(struct map_info *map, unsigned long ofs) +{ + + return read_flash_ctrl_reg(ofs); + + +} + +void m25p80_write32(struct map_info *map, __u32 d, unsigned long ofs) +{ + + write_flash_ctrl_reg(ofs, d); + +} + +void m25p80_copy_to(struct map_info *map, unsigned long ofs, void *buf, ssize_t len) +{ + __u32 size, i, ret; + + while(len > 0) + { + if(len >= M25P80_PAGE_SIZE) + size = M25P80_PAGE_SIZE; + else + size = len; + + for(i=0;im25p80_sector_erase"); + if(address >= FLASH_START) + address-=FLASH_START; + + m25p80_write_cmd(M25P80_WRITE_ENABLE, schip_en); + //printf("\n m25p80_sector_erase : after we-en"); + opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS | M25P80_SECTOR_ERASE; + write_flash_ctrl_reg(FLASH_ADDRESS_OFFSET, address); + #ifdef MIDWAY_DIAG + opcode|=schip_en; + #endif + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(tmp&0x80000000) + { + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + flash_delay(); + schedule(); + } + + opcode = 0x80000000 | FLASH_ACCESS_ACTION_OPCODE_DATA | M25P80_READ_STATUS; + #ifdef MIDWAY_DIAG + opcode|=schip_en; + #endif + + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(tmp&0x80000000) + { + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + flash_delay(); + schedule(); + } + status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET); + //while(status&0xfd) + while(status&0x01) + { + //if((status&0x9c)!=0) + // printf(" m25p80_sector_erase Protect Status = %x\n",status); + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(tmp&0x80000000) + { + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + flash_delay(); + schedule(); + } + status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET); + flash_delay(); + schedule(); + //middle delay_ms(50); + } + if((status&0x02)==0x02) + { + //middle delay_ms(100); + m25p80_write_cmd(M25P80_WRITE_DISABLE, schip_en); + } + //printf("\n<--m25p80_sector_erase"); + return res; +} + +static void m25p80_write_cmd(__u8 cmd, __u32 schip_en) +{ + __u32 opcode,tmp; + __u32 status; + + + + + opcode = 0x80000000 | FLASH_ACCESS_ACTION_OPCODE | cmd; + + opcode|=g_chipen; + + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(tmp&0x80000000) + { + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + flash_delay(); + schedule(); + } + ////// + opcode = 0x80000000 | FLASH_ACCESS_ACTION_OPCODE_DATA | M25P80_READ_STATUS; + + opcode|=g_chipen; + + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(tmp&0x80000000) + { + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + flash_delay(); + schedule(); + } + //middle delay_ms(130); + status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET); + //printf("\ncmd =%x status = %x",cmd,status); + if(cmd==M25P80_WRITE_ENABLE) + { + //printf("\n**-->enable** status = %x",status); + //middle delay_ms(100); + while((status&0x03) != 2) + { + //if((status&0x9c)!=0) + // printf(" M25P80_WRITE_ENABLE Protect Status = %x\n",status); + + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(tmp&0x80000000) + { + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + //flash_delay(); + } + status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET); + //printf("\n**enable** status = %x",status); + flash_delay(); + schedule(); + //middle delay_ms(100); + } + } + else if(cmd==M25P80_WRITE_DISABLE) + { + //while((status&0x03) == 2) + // printf("\n**disable** status = %x",status); + //middle delay_ms(100); + while((status&0x03) != 0) + { + //m25p80_write_status((status&0xfd),schip_en); + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(tmp&0x80000000) + { + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + flash_delay(); + schedule(); + } + status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET); + //printf("\n**disable** status = %x",status); + flash_delay(); + schedule(); + //middle delay_ms(50); + } + } + else + { + //while((status&0x01) !=0) + while((status&0x01) !=0) + { + write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode); + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + while(tmp&0x80000000) + { + tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET); + flash_delay(); + schedule(); + } + status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET); + flash_delay(); + schedule(); + //middle delay_ms(50); + } + } + ////// + + //printf("\n<-- status = %x",status); +} + +static int __init init_sl2312_m25p80(void) +{ + int nr_parts = 0; + struct mtd_partition *parts; + + serial_mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!serial_mtd) + return NULL; + + memset(serial_mtd, 0, sizeof(struct mtd_info)); + m25p80_map.virt = (unsigned long)ioremap(SL2312_FLASH_BASE, SFLASH_SIZE);//(unsigned long)ioremap(FLASH_START, SFLASH_SIZE); + if (!m25p80_map.virt) { + printk(" failed to ioremap \n"); + return -EIO; + } + serial_mtd = do_map_probe("map_serial", &m25p80_map); + if (serial_mtd) { + serial_mtd->owner = THIS_MODULE; + + } + +#ifdef CONFIG_MTD_REDBOOT_PARTS + nr_parts = parse_redboot_partitions(serial_mtd, &parsed_parts); + if (nr_parts > 0) + printk(KERN_NOTICE "Found RedBoot partition table.\n"); + else if (nr_parts < 0) + printk(KERN_NOTICE "Error looking for RedBoot partitions.\n"); +#else + parsed_parts = m25p80_partitions; + parts = m25p80_partitions; + nr_parts = sizeof(m25p80_partitions)/sizeof(*parts); + nr_parts = sizeof(m25p80_partitions)/sizeof(*parsed_parts); +#endif /* CONFIG_MTD_REDBOOT_PARTS */ + + if (nr_parts > 0) + add_mtd_partitions(serial_mtd, parsed_parts, nr_parts); + else + add_mtd_device(serial_mtd); + + return 0; +} + +static void __exit cleanup_sl2312_m25p80(void) +{ + if (parsed_parts) + del_mtd_partitions(serial_mtd); + else + del_mtd_device(serial_mtd); + + map_destroy(serial_mtd); + + +} + +module_init(init_sl2312_m25p80); +module_exit(cleanup_sl2312_m25p80); + + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Plus Chen "); +MODULE_DESCRIPTION("MTD map driver for Storlink Sword boards"); + --- /dev/null +++ b/drivers/mtd/maps/sl2312_flashmap.h @@ -0,0 +1,20 @@ +/* + * Please note that the name are used in mkflash script. Therefore + * don't change them. If you want to add different partitions, you + * will need to modify mkflash script as well so that the end image + * is what you include here! + * + * Also, the 7th item is always the size, so please don't add extra + * spaces in the name or other items. + * + * - Alan + */ + +static struct mtd_partition sl2312_partitions[] = { + { name: "RedBoot", offset: 0x00000000, size: 0x00020000, }, + { name: "kernel", offset: 0x00020000, size: 0x00100000, }, + { name: "rootfs", offset: 0x00120000, size: 0x006A0000, }, + { name: "VCTL", offset: 0x007C0000, size: 0x00010000, }, + { name: "cfg", offset: 0x007D0000, size: 0x00020000, }, + { name: "FIS directory", offset: 0x007F0000, size: 0x00010000, } +}; --- /dev/null +++ b/drivers/mtd/maps/sl2312_flashmap.h.16MB @@ -0,0 +1,21 @@ +/* + * Please note that the name are used in mkflash script. Therefore + * don't change them. If you want to add different partitions, you + * will need to modify mkflash script as well so that the end image + * is what you include here! + * + * Also, the 7th item is always the size, so please don't add extra + * spaces in the name or other items. + * + * - Alan + */ + +static struct mtd_partition sl2312_partitions[] = { + { name: "RedBoot", offset: 0x00000000, size: 0x00020000, }, + { name: "Kernel", offset: 0x00020000, size: 0x00300000, }, + { name: "Ramdisk", offset: 0x00320000, size: 0x00600000, }, + { name: "Application", offset: 0x00920000, size: 0x00600000, }, + { name: "VCTL", offset: 0x00F20000, size: 0x00020000, }, + { name: "CurConf", offset: 0x00F40000, size: 0x000A0000, }, + { name: "FIS directory", offset: 0x00FE0000, size: 0x00020000, } +}; --- /dev/null +++ b/drivers/mtd/maps/sl2312_flashmap.h.8MB @@ -0,0 +1,21 @@ +/* + * Please note that the name are used in mkflash script. Therefore + * don't change them. If you want to add different partitions, you + * will need to modify mkflash script as well so that the end image + * is what you include here! + * + * Also, the 7th item is always the size, so please don't add extra + * spaces in the name or other items. + * + * - Alan + */ + +static struct mtd_partition sl2312_partitions[] = { + { name: "RedBoot", offset: 0x00000000, size: 0x00020000, }, + { name: "Kernel", offset: 0x00020000, size: 0x00200000, }, + { name: "Ramdisk", offset: 0x00220000, size: 0x00280000, }, + { name: "Application", offset: 0x004A0000, size: 0x00300000, }, + { name: "VCTL", offset: 0x007A0000, size: 0x00020000, }, + { name: "CurConf", offset: 0x007C0000, size: 0x00020000, }, + { name: "FIS directory", offset: 0x007E0000, size: 0x00020000, } +}; --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -59,6 +59,77 @@ struct mtd_file_info { enum mtd_file_modes mode; }; +/*********************************************************************** +/* Storlink SoC -- flash +/***********************************************************************/ +#ifdef CONFIG_SL2312_SHARE_PIN +unsigned int share_pin_flag=0; // bit0:FLASH, bit1:UART, bit2:EMAC, bit3-4:IDE +unsigned int check_sleep_flag=0; // bit0:FLASH, bit1:IDE +static spinlock_t sl2312_flash_lock = SPIN_LOCK_UNLOCKED; +EXPORT_SYMBOL(share_pin_flag); +int dbg=0; +DECLARE_WAIT_QUEUE_HEAD(wq); +extern struct wait_queue_head_t *flash_wait; +unsigned int flash_req=0; +void mtd_lock() +{ + struct task_struct *tsk = current; + unsigned int value ; + unsigned long flags; + flash_req = 1; + DECLARE_WAITQUEUE(wait, tsk); + add_wait_queue(&wq, &wait); + for(;;) + { + set_task_state(tsk, TASK_INTERRUPTIBLE); + spin_lock_irqsave(&sl2312_flash_lock,flags); + if((share_pin_flag&0x1E)){//||(check_sleep_flag&0x00000002)) { + spin_unlock_irqrestore(&sl2312_flash_lock, flags); + check_sleep_flag |= 0x00000001; + if(dbg) + printk("mtd yield %x %x\n",share_pin_flag,check_sleep_flag); + wake_up_interruptible(&flash_wait); + schedule(); + } + else { + check_sleep_flag &= ~0x01; + share_pin_flag |= 0x00000001 ; // set share pin flag + spin_unlock_irqrestore(&sl2312_flash_lock, flags); + value = readl(IO_ADDRESS((SL2312_GLOBAL_BASE+GLOBAL_MISC_REG))); + value = value & (~PFLASH_SHARE_BIT) ; + writel(value,IO_ADDRESS((SL2312_GLOBAL_BASE+GLOBAL_MISC_REG))); + if(dbg) + printk("mtd Go %x %x\n",share_pin_flag,check_sleep_flag); + tsk->state = TASK_RUNNING; + remove_wait_queue(&wq, &wait); + return ; + } + } +} + +void mtd_unlock() +{ + unsigned int value ; + unsigned long flags; + + spin_lock_irqsave(&sl2312_flash_lock,flags); // Disable IRQ + value = readl(IO_ADDRESS((SL2312_GLOBAL_BASE+GLOBAL_MISC_REG))); + value = value | PFLASH_SHARE_BIT ; // Disable Flash PADs + writel(value,IO_ADDRESS((SL2312_GLOBAL_BASE+GLOBAL_MISC_REG))); + share_pin_flag &= ~(0x00000001); // clear share pin flag + check_sleep_flag &= ~0x00000001; + spin_unlock_irqrestore(&sl2312_flash_lock, flags); // Restore IRQ + if (check_sleep_flag & 0x00000002) + { + check_sleep_flag &= ~(0x00000002); + wake_up_interruptible(&flash_wait); + } + DEBUG(MTD_DEBUG_LEVEL0, "Flash Unlock...\n"); + flash_req = 0; +} +#endif +/***********************************************************************/ + static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) { struct mtd_file_info *mfi = file->private_data; @@ -162,13 +233,21 @@ static ssize_t mtd_read(struct file *fil int len; char *kbuf; +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_lock(); // sl2312 share pin lock +#endif + DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); if (*ppos + count > mtd->size) count = mtd->size - *ppos; - if (!count) + if (!count){ +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return 0; + } /* FIXME: Use kiovec in 2.5 to lock down the user's buffers and pass them directly to the MTD functions */ @@ -178,8 +257,12 @@ static ssize_t mtd_read(struct file *fil else kbuf=kmalloc(count, GFP_KERNEL); - if (!kbuf) + if (!kbuf) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -ENOMEM; + } while (count) { @@ -224,6 +307,9 @@ static ssize_t mtd_read(struct file *fil *ppos += retlen; if (copy_to_user(buf, kbuf, retlen)) { kfree(kbuf); +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; } else @@ -235,13 +321,19 @@ static ssize_t mtd_read(struct file *fil count = 0; } else { - kfree(kbuf); + kfree(kbuf); +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return ret; } } kfree(kbuf); +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return total_retlen; } /* mtd_read */ @@ -255,24 +347,40 @@ static ssize_t mtd_write(struct file *fi int ret=0; int len; +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_lock(); // sl2312 share pin lock +#endif + DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); - if (*ppos == mtd->size) + if (*ppos == mtd->size){ +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -ENOSPC; + } if (*ppos + count > mtd->size) count = mtd->size - *ppos; - if (!count) + if (!count){ +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return 0; + } if (count > MAX_KMALLOC_SIZE) kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL); else kbuf=kmalloc(count, GFP_KERNEL); - if (!kbuf) + if (!kbuf) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -ENOMEM; + } while (count) { @@ -283,6 +391,9 @@ static ssize_t mtd_write(struct file *fi if (copy_from_user(kbuf, buf, len)) { kfree(kbuf); +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; } @@ -323,11 +434,17 @@ static ssize_t mtd_write(struct file *fi } else { kfree(kbuf); +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return ret; } } kfree(kbuf); +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return total_retlen; } /* mtd_write */ @@ -381,36 +498,67 @@ static int mtd_ioctl(struct inode *inode u_long size; struct mtd_info_user info; +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_lock(); // sl2312 share pin lock +#endif + DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n"); size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; if (cmd & IOC_IN) { if (!access_ok(VERIFY_READ, argp, size)) + { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; + } } if (cmd & IOC_OUT) { if (!access_ok(VERIFY_WRITE, argp, size)) + { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; + } } switch (cmd) { case MEMGETREGIONCOUNT: if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int))) + { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; + } break; case MEMGETREGIONINFO: { struct region_info_user ur; - if (copy_from_user(&ur, argp, sizeof(struct region_info_user))) + if (copy_from_user(&ur, argp, sizeof(struct region_info_user))) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; + } - if (ur.regionindex >= mtd->numeraseregions) + if (ur.regionindex >= mtd->numeraseregions) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EINVAL; + } if (copy_to_user(argp, &(mtd->eraseregions[ur.regionindex]), - sizeof(struct mtd_erase_region_info))) + sizeof(struct mtd_erase_region_info))) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; + } break; } @@ -433,7 +581,12 @@ static int mtd_ioctl(struct inode *inode struct erase_info *erase; if(!(file->f_mode & 2)) + { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EPERM; + } erase=kzalloc(sizeof(struct erase_info),GFP_KERNEL); if (!erase) @@ -447,6 +600,9 @@ static int mtd_ioctl(struct inode *inode if (copy_from_user(&erase->addr, argp, sizeof(struct erase_info_user))) { kfree(erase); +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; } erase->mtd = mtd; @@ -484,14 +640,26 @@ static int mtd_ioctl(struct inode *inode struct mtd_oob_buf buf; struct mtd_oob_ops ops; - if(!(file->f_mode & 2)) + if(!(file->f_mode & 2)) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EPERM; + } - if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) + if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; + } - if (buf.length > 4096) + if (buf.length > 4096) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EINVAL; + } if (!mtd->write_oob) ret = -EOPNOTSUPP; @@ -499,8 +667,12 @@ static int mtd_ioctl(struct inode *inode ret = access_ok(VERIFY_READ, buf.ptr, buf.length) ? 0 : EFAULT; - if (ret) + if (ret) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return ret; + } ops.ooblen = buf.length; ops.ooboffs = buf.start & (mtd->oobsize - 1); @@ -536,19 +708,35 @@ static int mtd_ioctl(struct inode *inode struct mtd_oob_buf buf; struct mtd_oob_ops ops; - if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) + if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; + } - if (buf.length > 4096) + if (buf.length > 4096) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EINVAL; + } - if (!mtd->read_oob) + if (!mtd->read_oob) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif ret = -EOPNOTSUPP; + } else ret = access_ok(VERIFY_WRITE, buf.ptr, buf.length) ? 0 : -EFAULT; - if (ret) + if (ret) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return ret; + } ops.ooblen = buf.length; ops.ooboffs = buf.start & (mtd->oobsize - 1); @@ -580,7 +768,12 @@ static int mtd_ioctl(struct inode *inode struct erase_info_user info; if (copy_from_user(&info, argp, sizeof(info))) + { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; + } if (!mtd->lock) ret = -EOPNOTSUPP; @@ -594,7 +787,12 @@ static int mtd_ioctl(struct inode *inode struct erase_info_user info; if (copy_from_user(&info, argp, sizeof(info))) + { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; + } if (!mtd->unlock) ret = -EOPNOTSUPP; @@ -629,11 +827,21 @@ static int mtd_ioctl(struct inode *inode loff_t offs; if (copy_from_user(&offs, argp, sizeof(loff_t))) + { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; + } if (!mtd->block_isbad) ret = -EOPNOTSUPP; else + { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return mtd->block_isbad(mtd, offs); + } break; } @@ -642,11 +850,21 @@ static int mtd_ioctl(struct inode *inode loff_t offs; if (copy_from_user(&offs, argp, sizeof(loff_t))) + { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; + } if (!mtd->block_markbad) ret = -EOPNOTSUPP; else + { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return mtd->block_markbad(mtd, offs); + } break; } @@ -654,8 +872,12 @@ static int mtd_ioctl(struct inode *inode case OTPSELECT: { int mode; - if (copy_from_user(&mode, argp, sizeof(int))) + if (copy_from_user(&mode, argp, sizeof(int))) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; + } mfi->mode = MTD_MODE_NORMAL; @@ -670,7 +892,12 @@ static int mtd_ioctl(struct inode *inode { struct otp_info *buf = kmalloc(4096, GFP_KERNEL); if (!buf) + { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -ENOMEM; + } ret = -EOPNOTSUPP; switch (mfi->mode) { case MTD_MODE_OTP_FACTORY: @@ -701,12 +928,24 @@ static int mtd_ioctl(struct inode *inode { struct otp_info info; - if (mfi->mode != MTD_MODE_OTP_USER) + if (mfi->mode != MTD_MODE_OTP_USER) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EINVAL; - if (copy_from_user(&info, argp, sizeof(info))) + } + if (copy_from_user(&info, argp, sizeof(info))) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EFAULT; - if (!mtd->lock_user_prot_reg) + } + if (!mtd->lock_user_prot_reg) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EOPNOTSUPP; + } ret = mtd->lock_user_prot_reg(mtd, info.start, info.length); break; } @@ -742,8 +981,12 @@ static int mtd_ioctl(struct inode *inode break; case MTD_MODE_RAW: - if (!mtd->read_oob || !mtd->write_oob) + if (!mtd->read_oob || !mtd->write_oob) { +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif return -EOPNOTSUPP; + } mfi->mode = arg; case MTD_MODE_NORMAL: @@ -766,6 +1009,10 @@ static int mtd_ioctl(struct inode *inode ret = -ENOTTY; } +#ifdef CONFIG_SL2312_SHARE_PIN + mtd_unlock(); // sl2312 share pin lock +#endif + return ret; } /* memory_ioctl */ --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -44,6 +44,13 @@ config MTD_NAND_AUTCPU12 This enables the driver for the autronix autcpu12 board to access the SmartMediaCard. +config MTD_NAND_SL2312 + tristate "NAND Flash device on Storlink board" + depends on ARM && MTD_NAND && ARCH_SL2312 + help + This enables the driver for the Storlink board to + access the nand device. + config MTD_NAND_EDB7312 tristate "Support for Cirrus Logic EBD7312 evaluation board" depends on ARCH_EDB7312 --- /dev/null +++ b/drivers/mtd/nand/sl2312-flash-nand.c @@ -0,0 +1,2287 @@ +/* + * drivers/mtd/sl2312.c + * + * $Id: sl2312-flash-nand.c,v 1.5 2006/06/15 07:02:29 middle Exp $ + * + * Copyright (C) 2001 Toshiba Corporation + * + * 2003 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sl2312-flash-nand.h" + + +#include +#include +#include +#include +#include +#include + + +/* + * NAND low-level MTD interface functions + */ +static void sl2312_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len); +static void sl2312_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len); +static int sl2312_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len); + +static int sl2312_nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); +static int sl2312_nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); +static int sl2312_nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); +static int sl2312_nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf); +static int sl2312_nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); +static int sl2312_nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf); +static int sl2312_nand_writev (struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t * retlen); +static int sl2312_nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); +static int sl2312_nand_erase (struct mtd_info *mtd, struct erase_info *instr, int allowbbt); +static void sl2312_nand_sync (struct mtd_info *mtd); +static int sl2312_nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel); +static int sl2312_nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt); +static int sl2312_nand_erase_block(struct mtd_info *mtd, int page); + +/* + * MTD structure for sl2312 NDFMC + */ +static struct mtd_info *sl2312_mtd = NULL; +static int nand_page=0,nand_col=0; + +/* Define default oob placement schemes for large and small page devices */ +static struct nand_oobinfo nand_oob_8 = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 3, + .eccpos = {0, 1, 2}, + .oobfree = { {3, 2}, {6, 2} } +}; + +static struct nand_oobinfo nand_oob_16 = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 6, + .eccpos = {0, 1, 2, 3, 6, 7}, + .oobfree = { {8, 8} } +}; + +static struct nand_oobinfo nand_oob_64 = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 24, + .eccpos = { + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { {2, 38} } +}; + + +/* + * Define partitions for flash device + */ +/* the base address of FLASH control register */ +#define FLASH_CONTROL_BASE_ADDR (IO_ADDRESS(SL2312_FLASH_CTRL_BASE)) +#define SL2312_GLOBAL_BASE_ADDR (IO_ADDRESS(SL2312_GLOBAL_BASE)) +//#define SL2312_FLASH_BASE_ADDR (IO_ADDRESS(SL2312_FLASH_BASE)) +#define SL2312_FLASH_BASE_ADDR FLASH_VADDR(SL2312_FLASH_BASE) +static unsigned int CHIP_EN; +/* define read/write register utility */ +//#define FLASH_READ_REG(offset) (__raw_readl(offset+FLASH_CONTROL_BASE_ADDR)) +//#define FLASH_WRITE_REG(offset,val) (__raw_writel(val,offset+FLASH_CONTROL_BASE_ADDR)) +//#define FLASH_READ_DATA(offset) (__raw_readb(offset+SL2312_FLASH_BASE_ADDR)) +//#define FLASH_WRITE_DATA(offset,val) (__raw_writeb(val,offset+SL2312_FLASH_BASE_ADDR)) + +unsigned int FLASH_READ_REG(unsigned int addr) +{ + unsigned int *base; + unsigned int data; + + base = (unsigned int *)(FLASH_CONTROL_BASE_ADDR + addr); + data = *base; + return (data); +} + +void FLASH_WRITE_REG(unsigned int addr,unsigned int data) +{ + unsigned int *base; + + base = (unsigned int *)(FLASH_CONTROL_BASE_ADDR + addr); + *base = data; + return; +} + +unsigned int FLASH_READ_DATA(unsigned int addr) +{ + unsigned char *base; + unsigned int data; + + base = (unsigned char *)(SL2312_FLASH_BASE_ADDR + addr); + data = *base; + return (data); +} + +void FLASH_WRITE_DATA(unsigned int addr,unsigned int data) +{ + unsigned char *base; + + base = (unsigned char *)(SL2312_FLASH_BASE_ADDR + addr); + *base = data; + return; +} + +/* the offset of FLASH control register */ +enum NFLASH_REGISTER { + NFLASH_ID = 0x0000, + NFLASH_STATUS = 0x0008, + NFLASH_TYPE = 0x000c, + NFLASH_ACCESS = 0x0030, + NFLASH_COUNT = 0x0034, + NFLASH_CMD_ADDR = 0x0038, + NFLASH_ADDRESS = 0x003C, + NFLASH_DATA = 0x0040, + NFLASH_TIMING = 0x004C, + NFLASH_ECC_STATUS = 0x0050, + NFLASH_ECC_CONTROL = 0x0054, + NFLASH_ECC_OOB = 0x005c, + NFLASH_ECC_CODE_GEN0 = 0x0060, + NFLASH_ECC_CODE_GEN1 = 0x0064, + NFLASH_ECC_CODE_GEN2 = 0x0068, + NFLASH_ECC_CODE_GEN3 = 0x006C, + NFLASH_FIFO_CONTROL = 0x0070, + NFLASH_FIFO_STATUS = 0x0074, + NFLASH_FIFO_ADDRESS = 0x0078, + NFLASH_FIFO_DATA = 0x007c, +}; + + + +//#define FLASH_BASE FLASH_CONTROL_BASE_ADDR +//#define FLASH_SIZE 0x00800000 //INTEGRATOR_FLASH_SIZE + +//#define FLASH_PART_SIZE 8388608 + +//static unsigned int flash_indirect_access = 0; + + +#ifdef CONFIG_SL2312_SHARE_PIN +void sl2312flash_enable_nand_flash(void) +{ + unsigned int reg_val; + + reg_val = readl(SL2312_GLOBAL_BASE_ADDR + 0x30); + reg_val = reg_val & 0xfffffffb; + writel(reg_val,SL2312_GLOBAL_BASE_ADDR + 0x30); + return; +} + +void sl2312flash_disable_nand_flash(void) +{ + unsigned int reg_val; + + reg_val = readl(SL2312_GLOBAL_BASE_ADDR + 0x30); + reg_val = reg_val | 0x00000004; + writel(reg_val,SL2312_GLOBAL_BASE_ADDR + 0x30); + return; +} +#endif + +extern struct nand_oobinfo jffs2_oobinfo; +/* + * Define partitions for flash devices + */ + +static struct mtd_partition sl2312_partitions[] = { + { name: "RedBoot", offset: 0x00000000, size: 0x0020000, }, + { name: "Kernel", offset: 0x00020000, size: 0x00200000, }, + { name: "Ramdisk", offset: 0x00220000, size: 0x00280000, }, + { name: "Application", offset: 0x004A0000, size: 0x00320000, }, + { name: "VCTL", offset: 0x007C0000, size: 0x20000, }, + { name: "CurConf", offset: 0x007E0000, size: 0x20000, }, + { name: "FIS directory", offset: 0x007e0000, size: 0x00020000, } + +}; + + +/* + * hardware specific access to control-lines +*/ +static void sl2312_hwcontrol(struct mtd_info *mtd, int cmd) +{ + + return ; +} + +static int sl2312_nand_scan_bbt(struct mtd_info *mtd) +{ + return 0; +} + +/** + * nand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +static int sl2312_nand_block_isbad (struct mtd_info *mtd, loff_t ofs) +{ + /* Check for invalid offset */ + if (ofs > mtd->size) + return -EINVAL; + + return sl2312_nand_block_checkbad (mtd, ofs, 1, 0); +} + +/** + * nand_block_checkbad - [GENERIC] Check if a block is marked bad + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected + * @allowbbt: 1, if its allowed to access the bbt area + * + * Check, if the block is bad. Either by reading the bad block table or + * calling of the scan function. + */ + +static int sl2312_nand_erase_block(struct mtd_info *mtd, int page) +{ + int opcode; + /* Send commands to erase a page */ + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x00000000); //set 31b = 0 + + if(mtd->oobblock > 528) + FLASH_WRITE_REG(NFLASH_COUNT, 0x7f0fff21); // 3 address & 2 command + else + FLASH_WRITE_REG(NFLASH_COUNT, 0x7f0fff11); // 2 address & 2 command + + FLASH_WRITE_REG(NFLASH_CMD_ADDR, 0x0000d060); // write read id command + FLASH_WRITE_REG(NFLASH_ADDRESS, page); //write address 0x00 + + + + /* read maker code */ + opcode = 0x80003000|DWIDTH|CHIP_EN; //set start bit & 8bits write command + FLASH_WRITE_REG(NFLASH_ACCESS, opcode); + + while(opcode&0x80000000) //polling flash access 31b + { + opcode=FLASH_READ_REG(NFLASH_ACCESS); + //sl2312_flash_delay(); + schedule(); + //cond_resched(); + } +} + +void sl2312_flash_delay(void) +{ + int i; + + for(i=0; i<50; i++) + i=i; +} + +static int sl2312_nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) +{ + struct nand_chip *this = mtd->priv; + + if (!this->bbt) + return this->block_bad(mtd, ofs, getchip); + + /* Return info from the table */ + return nand_isbad_bbt (mtd, ofs, allowbbt); +} + +/** + * nand_block_markbad - [MTD Interface] Mark the block at the given offset as bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +static int sl2312_nand_block_markbad (struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *this = mtd->priv; + int ret; + + if ((ret = sl2312_nand_block_isbad(mtd, ofs))) { + /* If it was bad already, return success and do nothing. */ + if (ret > 0) + return 0; + return ret; + } + + return this->block_markbad(mtd, ofs); +} + +/* + * Get chip for selected access + */ +static inline void sl2312_nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state, int *erase_state) +{ + + DECLARE_WAITQUEUE (wait, current); + + /* + * Grab the lock and see if the device is available + * For erasing, we keep the spinlock until the + * erase command is written. + */ +retry: + spin_lock_bh (&this->chip_lock); + + if (this->state == FL_READY) { + this->state = new_state; + if (new_state != FL_ERASING) + spin_unlock_bh (&this->chip_lock); + return; + } + + if (this->state == FL_ERASING) { + if (new_state != FL_ERASING) { + this->state = new_state; + spin_unlock_bh (&this->chip_lock); + this->select_chip(mtd, 0); /* select in any case */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + return; + } + } + + set_current_state (TASK_UNINTERRUPTIBLE); + add_wait_queue (&this->wq, &wait); + spin_unlock_bh (&this->chip_lock); + schedule (); + remove_wait_queue (&this->wq, &wait); + goto retry; +} + +/* +* read device ready pin +*/ +static int sl2312_device_ready(struct mtd_info *mtd) +{ + int ready; + + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x00000000); //set 31b = 0 + FLASH_WRITE_REG(NFLASH_COUNT, 0x7f000070); //set only command no address and two data + + FLASH_WRITE_REG(NFLASH_CMD_ADDR, 0x00000070); //write read status command + + + ready = 0x80002000|DWIDTH|CHIP_EN; //set start bit & 8bits read command + FLASH_WRITE_REG(NFLASH_ACCESS, ready); + + while(ready&0x80000000) //polling flash access 31b + { + ready=FLASH_READ_REG(NFLASH_ACCESS); + //sl2312_flash_delay(); + schedule(); + } + FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT); + ready=FLASH_READ_REG(NFLASH_DATA)&0xff; + return ready; +} +void sl2312_enable_hwecc(struct mtd_info *mtd, int mode) +{ + /* reset first */ + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x80000001); //set 31b = 0 + +} + + +void sl2312_device_setup(void) +{ + +} +static u_char sl2312_nand_read_byte(struct mtd_info *mtd) +{ + + unsigned int data=0, page=0, col=0, tmp, i; + + printk ("**************************sl2312_nand_read_byte !! \n"); + //page = FLASH_READ_REG(NFLASH_ADDRESS)&0xffffff00; + //col = FLASH_READ_REG(NFLASH_ADDRESS)&0x000000ff; + page = nand_page; + col = nand_col; + for(i=0;i<(mtd->oobblock+mtd->oobsize);i++) + { + if(i==col) + data = FLASH_READ_DATA(page*mtd->oobblock +i); + else + tmp = FLASH_READ_DATA(page*mtd->oobblock +i); + } + return data&0xff; +} + +static void sl2312_nand_write_byte(struct mtd_info *mtd, u_char byte) +{ + //struct nand_chip *this = mtd->priv; + unsigned int page=0, col=0, i; + u_char *databuf,oobbuf[mtd->oobsize]; + size_t retlen; + retlen=0; + printk ("********************sl2312_nand_write_byte !! \n"); + page = nand_page; + col = nand_col; + databuf = kmalloc (mtd->oobsize+mtd->oobblock,GFP_KERNEL); + + if (!databuf) { + printk ("sl2312_nand_write_byte : Unable to allocate SL2312 NAND MTD device structure.\n"); + + } + + for(i=0;i<(mtd->oobblock+mtd->oobsize);i++) + databuf[i] = FLASH_READ_DATA(page*mtd->oobblock +i); + + databuf[col] = byte; + sl2312_nand_write_ecc (mtd, page, mtd->oobblock, &retlen, databuf, oobbuf, NULL); + +} + +static void sl2312_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i, page=0,col=0; + struct nand_chip *this = mtd->priv; + u_char *databuf, *oobbuf; + size_t retlen; + retlen=0; + + + printk ("***********************sl2312_nand_write_buf !! \n"); + databuf = &(this->data_buf[0]); + oobbuf = &(this->data_buf[mtd->oobblock]); + for (i = 0; i < mtd->oobsize; i++) + oobbuf[i] = 0xff; + + if(len < mtd->oobblock) + { + //addr = FLASH_READ_REG(NFLASH_ADDRESS); + //page = FLASH_READ_REG(NFLASH_ADDRESS)&0xffffff00; + //col = FLASH_READ_REG(NFLASH_ADDRESS)&0x000000ff; + page = nand_page; + col = nand_col; + + sl2312_nand_read_ecc (mtd, page, mtd->oobblock , &retlen, databuf, oobbuf, NULL); + + for(i=col;ioobblock, &retlen, databuf, oobbuf, NULL); + + } + +} + +static void sl2312_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i, page=0,col=0,addr=0,tmp=0; + //struct nand_chip *this = mtd->priv; + printk ("********************sl2312_nand_read_buf !! \n"); + if(len < mtd->oobblock) + { + //addr = FLASH_READ_REG(NFLASH_ADDRESS); + //page = FLASH_READ_REG(NFLASH_ADDRESS)&0xffffff00; + //col = FLASH_READ_REG(NFLASH_ADDRESS)&0x000000ff; + page = nand_page; + col = nand_col; + for (i=col; i<((mtd->oobblock+mtd->oobsize)-col); i++) + { + if(ipriv; + u_char *datatmp, *oobtmp; + size_t retlen; + retlen=0; + + datatmp = kmalloc (mtd->oobblock,GFP_KERNEL); + oobtmp = kmalloc (mtd->oobsize,GFP_KERNEL); + + if ((!datatmp)||(!oobtmp)) { + printk ("sl2312_nand_verify_buf : Unable to allocate SL2312 NAND MTD device structure.\n"); + + } + //page = nand_page; + for(i=0;ioobblock;i++) + datatmp[i] = FLASH_READ_DATA(nand_page*mtd->oobblock +i); + /* read oobdata */ + for (i = 0; i < mtd->oobsize; i++) + oobtmp[i] = FLASH_READ_DATA(nand_page*mtd->oobblock + mtd->oobblock + i); + + if(len==mtd->oobblock) + { + for (i=0; i page: %x, byte: %x \n",nand_page,i); + return i; + } + } + } + else if(len == mtd->oobsize) + { + for (i=0; i page: %x, byte: %x \n",nand_page,i); + return i; + } + } + } + else + { + printk (KERN_WARNING "sl2312_nand_verify_buf : verify length not match 0x%08x\n", len); + kfree(datatmp); + kfree(oobtmp); + return -1; + } + + kfree(datatmp); + kfree(oobtmp); + return 0; +} + +/* + * Send command to NAND device + */ +static void sl2312_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + register struct nand_chip *this = mtd->priv; + int opcode; + + + /* + * program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + case NAND_CMD_READ0: + + /* + * Write out the command to the device. + */ + if (column != -1 || page_addr != -1) { + + /* Serially input address */ + if (column != -1) + //FLASH_WRITE_REG(NFLASH_ADDRESS,column); + nand_col=column; + + opcode = FLASH_READ_REG(NFLASH_ADDRESS); + + if (page_addr != -1) + //FLASH_WRITE_REG(NFLASH_ADDRESS,opcode|(page_addr<<8)); + nand_page = page_addr; + + } + return; + + case NAND_CMD_RESET: + if (this->dev_ready) + break; + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x00000000); //set 31b = 0 + FLASH_WRITE_REG(NFLASH_COUNT, 0x7f0fff70); //set only command and no other data + FLASH_WRITE_REG(NFLASH_CMD_ADDR, NAND_CMD_RESET); //write reset command + + opcode = 0x80002000|DWIDTH|CHIP_EN; //set start bit & 8bits read command + FLASH_WRITE_REG(NFLASH_ACCESS, opcode); + + while(opcode&0x80000000) //polling flash access 31b + { + opcode=FLASH_READ_REG(NFLASH_ACCESS); + //sl2312_flash_delay(); + schedule(); + } + while ( !(sl2312_device_ready(mtd) & 0x40)); + { + FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT); + //sl2312_flash_delay(); + schedule(); + return; + } + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!this->dev_ready) { + udelay (this->chip_delay); + FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT); + return; + } + } + + /* wait until command is processed */ + while (!this->dev_ready(mtd)); + +} +/*Add function*/ +static void nand_read_id(int chip_no, unsigned char *id) +{ + unsigned int opcode, i; + + if(chip_no==0) + CHIP_EN = NFLASH_CHIP0_EN; + else + CHIP_EN = NFLASH_CHIP1_EN; + + opcode = FLASH_READ_REG(NFLASH_TYPE); + + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x00000000); //set 31b = 0 + if((opcode&0x00000300)<=0x00000100) + FLASH_WRITE_REG(NFLASH_COUNT, 0x7f000100); //set only command & address and two data + else + FLASH_WRITE_REG(NFLASH_COUNT, 0x7f000300); //set only command & address and 4 data + + FLASH_WRITE_REG(NFLASH_CMD_ADDR, 0x00000090); //write read id command + FLASH_WRITE_REG(NFLASH_ADDRESS, 0x00000000); //write address 0x00 + + /* read maker code */ + opcode = 0x80002000|DWIDTH|CHIP_EN;//|chip0_en; //set start bit & 8bits read command + FLASH_WRITE_REG(NFLASH_ACCESS, opcode); + opcode=FLASH_READ_REG(NFLASH_ACCESS); + while(opcode&0x80000000) //polling flash access 31b + { + opcode=FLASH_READ_REG(NFLASH_ACCESS); + //sl2312_flash_delay(); + schedule(); + } + + opcode = FLASH_READ_REG(NFLASH_DATA); + if(DWIDTH==NFLASH_WiDTH16) + { + id[0] = opcode&0xff; + id[1] = (opcode&0xff00)>>8; + } + else + { + id[0] = opcode&0xff; + opcode = 0x80002000|DWIDTH|CHIP_EN;//|chip0_en; //set start bit & 8bits read command + FLASH_WRITE_REG(NFLASH_ACCESS, opcode); + opcode=FLASH_READ_REG(NFLASH_ACCESS); + while(opcode&0x80000000) //polling flash access 31b + { + opcode=FLASH_READ_REG(NFLASH_ACCESS); + //sl2312_flash_delay(); + schedule(); + } + opcode = FLASH_READ_REG(NFLASH_DATA); + id[1] = (opcode&0xff00)>>8; + + opcode=FLASH_READ_REG(NFLASH_TYPE); + if((opcode&0x300)>0x100) + { + for(i=0;i<2;i++) + { + //data cycle 3 & 4 ->not use + opcode = 0x80002000|DWIDTH|CHIP_EN;//set start bit & 8bits read command + FLASH_WRITE_REG(NFLASH_ACCESS, opcode); + opcode=FLASH_READ_REG(NFLASH_ACCESS); + while(opcode&0x80000000) //polling flash access 31b + { + opcode=FLASH_READ_REG(NFLASH_ACCESS); + //sl2312_flash_delay(); + schedule(); + } + + opcode=FLASH_READ_REG(NFLASH_DATA); + id[2+i] = (opcode&(0xff0000<>(8*(2+i)); + } + } + } + FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT); +} + +/* + * NAND erase a block + */ +static int sl2312_nand_erase (struct mtd_info *mtd, struct erase_info *instr, int allowbbt) +{ + int page, len, status, pages_per_block, ret, chipnr; + struct nand_chip *this = mtd->priv; + + DEBUG (MTD_DEBUG_LEVEL3, + "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); + + /* Start address must align on block boundary */ + if (instr->addr & ((1 << this->phys_erase_shift) - 1)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); + return -EINVAL; + } + + /* Length must align on block boundary */ + if (instr->len & ((1 << this->phys_erase_shift) - 1)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n"); + return -EINVAL; + } + + /* Do not allow erase past end of device */ + if ((instr->len + instr->addr) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n"); + return -EINVAL; + } + + instr->fail_addr = 0xffffffff; + + /* Grab the lock and see if the device is available */ + sl2312_nand_get_chip (this, mtd, FL_ERASING, NULL); + + /* Shift to get first page */ + page = (int) (instr->addr >> this->page_shift); + chipnr = (int) (instr->addr >> this->chip_shift); + + /* Calculate pages in each block */ + pages_per_block = 1 << (this->phys_erase_shift - this->page_shift); + + /* Select the NAND device */ + //this->select_chip(mtd, chipnr); + this->select_chip(mtd, 0); + + /* Check the WP bit */ + /* Check, if it is write protected */ + status = sl2312_device_ready(mtd); + if (!(status & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n"); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* Loop through the pages */ + len = instr->len; + + instr->state = MTD_ERASING; + + while (len) { + /* Check if we have a bad block, we do not erase bad blocks ! */ + if (this->block_bad(mtd, ((loff_t) page) << this->page_shift, 0)) { + printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page); + //instr->state = MTD_ERASE_FAILED; + //goto erase_exit; + } + + /* Invalidate the page cache, if we erase the block which contains + the current cached page */ + if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block)) + this->pagebuf = -1; + ///////// + + ///* Send commands to erase a page */ + //FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x00000000); //set 31b = 0 + // + //if(mtd->oobblock > 528) + // FLASH_WRITE_REG(NFLASH_COUNT, 0x7f0fff21); // 3 address & 2 command + //else + // FLASH_WRITE_REG(NFLASH_COUNT, 0x7f0fff11); // 2 address & 2 command + // + //FLASH_WRITE_REG(NFLASH_CMD_ADDR, 0x0000d060); // write read id command + //FLASH_WRITE_REG(NFLASH_ADDRESS, page); //write address 0x00 + // + // + // + ///* read maker code */ + //opcode = 0x80003000|DWIDTH|CHIP_EN; //set start bit & 8bits write command + //FLASH_WRITE_REG(NFLASH_ACCESS, opcode); + // + //while(opcode&0x80000000) //polling flash access 31b + //{ + // opcode=FLASH_READ_REG(NFLASH_ACCESS); + // //sl2312_flash_delay(); + // schedule(); + // //cond_resched(); + //} + sl2312_nand_erase_block(mtd, page); + ////////////// + status = this->waitfunc (mtd, this, FL_ERASING); + /* See if block erase succeeded */ + if (status & 0x01) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = (page << this->page_shift); + goto erase_exit; + } + + /* Increment page address and decrement length */ + len -= (1 << this->phys_erase_shift); + page += pages_per_block; + + /* Check, if we cross a chip boundary */ + if (len && !(page & this->pagemask)) { + chipnr++; + this->select_chip(mtd, 0); + this->select_chip(mtd, 0); + } + //sl2312_flash_delay(); + schedule(); + //cond_resched(); + } + instr->state = MTD_ERASE_DONE; + +erase_exit: + /* De-select the NAND device */ + this->select_chip(mtd, 0); + spin_unlock_bh (&this->chip_lock); + + ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;; + /* Do call back function */ + if (!ret && instr->callback) + instr->callback (instr); + + /* The device is ready */ + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + spin_unlock_bh (&this->chip_lock); + FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT); + /* Return more or less happy */ + return ret; +} + +static void sl2312_nand_select_chip(struct mtd_info *mtd, int chip) +{ + //struct nand_chip *this = mtd->priv; + + switch(chip) { + case -1: + CHIP_EN = NFLASH_CHIP0_EN; + break; + case 0: + CHIP_EN = NFLASH_CHIP0_EN; + break; + case 1: + CHIP_EN = NFLASH_CHIP1_EN; + break; + default: + CHIP_EN = NFLASH_CHIP0_EN; + break; + } +} + +/** + * nand_default_block_markbad - [DEFAULT] mark a block bad + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This is the default implementation, which can be overridden by + * a hardware specific driver. +*/ +static int sl2312_nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *this = mtd->priv; + u_char buf[2] = {0, 0}; + size_t retlen; + int block; + + /* Get block number */ + block = ((int) ofs) >> this->bbt_erase_shift; + this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + + /* Do we have a flash based bad block table ? */ + if (this->options & NAND_USE_FLASH_BBT) + return nand_update_bbt (mtd, ofs); + + /* We write two bytes, so we dont have to mess with 16 bit access */ + ofs += mtd->oobsize + (this->badblockpos & ~0x01); + return sl2312_nand_write_oob (mtd, ofs , 2, &retlen, buf); +} + +/* Appropriate chip should already be selected */ +static int sl2312_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)//(struct mtd_info *mtd, unsigned long page, ) +{ + u_char *buf, *oobbuf; + size_t retlen; + unsigned long page, chipnr; + struct nand_chip *this = mtd->priv; + + if (getchip) { + page = (int)(ofs >> this->page_shift); + chipnr = (int)(ofs >> this->chip_shift); + + /* Grab the lock and see if the device is available */ + sl2312_nand_get_chip (this, mtd, FL_READING, NULL); + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + } else + page = (int) ofs; + + buf = kmalloc (mtd->oobblock,GFP_KERNEL); + oobbuf = kmalloc (mtd->oobsize,GFP_KERNEL); + + if ((!buf)||(!oobbuf)) { + printk ("sl2312_nand_block_bad : Unable to allocate SL2312 NAND MTD device structure.\n"); + + } + + sl2312_nand_read_ecc (mtd, page, mtd->oobblock , &retlen, buf, oobbuf, NULL); + + + if(((mtd->oobblock < 528)&&(oobbuf[5] != 0xff))||((mtd->oobblock > 528)&&(oobbuf[0] != 0xff))) + { + kfree(buf); + kfree(oobbuf); + return 1; + } + + kfree(buf); + kfree(oobbuf); + return 0; +} + +/* +* Use NAND read ECC +*/ +static int sl2312_nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) +{ + return sl2312_nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL); +} + +/* + * NAND read with ECC + */ +static int sl2312_nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel) +{ + int j, col, page, opcode, i; + int end=0;//, ecc=0;//, end_page=0; + int erase_state = 0; + int read = 0, oob = 0, ecc_failed = 0;//, ecc_status = 0 + struct nand_chip *this = mtd->priv; + u_char *data_poi, *oob_data = oob_buf; + //u_char ecc_calc[6]; + //u_char ecc_code[6]; + int eccmode; + int *oob_config; + + + + // use chip default if zero + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + + eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; + oob_config = oobsel->eccpos; + + DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n"); + *retlen = 0; + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + sl2312_nand_get_chip (this, mtd ,FL_READING, &erase_state); + + /* Select the NAND device */ + this->select_chip(mtd, 0); + + /* First we calculate the starting page */ + page = from >> this->page_shift; + + //end_page = mtd->oobblock + mtd->oobsize; + end = mtd->oobblock; + //ecc = mtd->eccsize; + /* Get raw starting column */ + col = (from & (mtd->oobblock - 1)); + + + /* Send the read command */ + //this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); + + /* Loop until all data read */ + FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT); + while (read < len) { + + //udelay(1200); + /* If we have consequent page reads, apply delay or wait for ready/busy pin */ + if (read) { + if (!this->dev_ready) + udelay (this->chip_delay); + else + while (!this->dev_ready(mtd)); + } + + /* + * If the read is not page aligned, we have to read into data buffer + * due to ecc, else we read into return buffer direct + */ + if (!col && (len - read) >= end) + data_poi = &buf[read]; + else + data_poi = this->data_buf; + + /* get oob area, if we have no oob buffer from fs-driver */ + if (!oob_buf) { + oob_data = &this->data_buf[end]; + oob = 0; + } + + j = 0; + switch (eccmode) { + case NAND_ECC_NONE: { /* No ECC, Read in a page */ + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x0); //set 31b = 0 + break; + } + + case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ + break; + + case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data: Read in first 256 byte, get ecc, */ + break; + + case NAND_ECC_HW3_512: + case NAND_ECC_HW6_512: /* Hardware ECC 3/6 byte / 512 byte data : Read in a page */ + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x80000001); //set 31b = 0 + break; + + default: + printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x0); + //BUG(); + }//end switch + + for(i=0;ioobblock +i); + } + /* read oobdata */ + for (i = 0; i < mtd->oobsize; i++) + { + //udelay(7); + oob_data[oob + i] = FLASH_READ_DATA(page*mtd->oobblock +end+i); + } + + /* Skip ECC, if not active */ + if (eccmode == NAND_ECC_NONE) + goto readdata; + + // compare ecc and correct data + + opcode=FLASH_READ_REG(NFLASH_ECC_STATUS); + while(!(opcode&0x80000000)) //polling flash access 31b + { + opcode=FLASH_READ_REG(NFLASH_ECC_STATUS); + //sl2312_flash_delay(); + schedule(); + } + for(j=0;j<(end/512);j++) + {//for 2k page + + opcode = 0x00000000|oob_data[mtd->oobsize-3-4*j]<<16|oob_data[mtd->oobsize-2-4*j]<<8|oob_data[mtd->oobsize-1-4*j]; + + //opcode=FLASH_READ_REG(NFLASH_ECC_CODE_GEN0+(j*4)); + + FLASH_WRITE_REG(NFLASH_ECC_OOB, opcode); + opcode = 0x00000000|(j<<8); //select ECC code generation 0 + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, opcode); //??? + + opcode=FLASH_READ_REG(NFLASH_ECC_STATUS); + if((opcode&0x00000003)==0x03) + { + printk (KERN_WARNING "\nPageRead Uncorrectable error !!\n"); + ecc_failed++; + } + else if((opcode&0x00000003)==0x01) + { + printk (KERN_WARNING "\nPageRead One bit data error !!"); + // correct data + if((data_poi[(opcode&0xff80)>>7]>>((opcode&0x38)>>3))%1) + data_poi[(opcode&0xff80)>>7] &= ~(1<<((opcode&0x38)>>3)); + else + data_poi[(opcode&0xff80)>>7] |= (1<<((opcode&0x38)>>3)); + + } + else if((opcode&0x00000003)==0x02) + { + printk (KERN_WARNING "\nPageRead One bit ECC error !!\n"); + } + else if((opcode&0x00000003)==0x00) + { + + } + + }//for 2k page +readdata: + if (col || (len - read) < end) { + for (j = col; j < end && read < len; j++) + buf[read++] = data_poi[j]; + } else + read += mtd->oobblock; + /* For subsequent reads align to page boundary. */ + col = 0; + /* Increment page address */ + page++; + schedule(); + } + /* De-select the NAND device */ + //this->select_chip(mtd, -1); + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x0); //set 31b = 0 + FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_INDIRECT); + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + + /* + * Return success, if no ECC failures, else -EIO + * fs driver will take care of that, because + * retlen == desired len and result == -EIO + */ + *retlen = read; + return ecc_failed ? -EIO : 0; +} + +/* + * Wait for command done. This applies to erase and program only + * Erase can take up to 400ms and program up to 20ms according to + * general NAND and SmartMedia specs + * +*/ +static int sl2312_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this, int state) +{ + unsigned long timeo = jiffies; + int status, opcode; + + if (state == FL_ERASING) + timeo += (HZ * 400) / 1000; + else + timeo += (HZ * 20) / 1000; + + spin_lock_bh (&this->chip_lock); + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x00000000); //set 31b = 0 + FLASH_WRITE_REG(NFLASH_COUNT, 0x007f000070); //set only command no address and two data + + FLASH_WRITE_REG(NFLASH_CMD_ADDR, 0x00000070); //write read status command + + + opcode = 0x80002000|DWIDTH|CHIP_EN; //set start bit & 8bits read command + FLASH_WRITE_REG(NFLASH_ACCESS, opcode); + + while(opcode&0x80000000) //polling flash access 31b + { + opcode=FLASH_READ_REG(NFLASH_ACCESS); + //sl2312_flash_delay(); + schedule(); + } + + while (time_before(jiffies, timeo)) { + /* Check, if we were interrupted */ + if (this->state != state) { + spin_unlock_bh (&this->chip_lock); + FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT); + return 0; + } + if (this->dev_ready) { + if (this->dev_ready(mtd)) + break; + } + if (FLASH_READ_REG(NFLASH_DATA) & 0x40) + break; + + spin_unlock_bh (&this->chip_lock); + yield (); + spin_lock_bh (&this->chip_lock); + } + status = FLASH_READ_REG(NFLASH_DATA)&0xff; + spin_unlock_bh (&this->chip_lock); + FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT); + return status; +} + +static int sl2312_nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) +{ + int i, col, page, j=0; + //int erase_state = 0; + struct nand_chip *this = mtd->priv; + u_char *databuf, *oobbuf; + + databuf = &this->data_buf[0]; + oobbuf = &this->data_buf[mtd->oobblock]; + for (i = 0; i < mtd->oobsize; i++) + oobbuf[i] = 0xff; + + DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + + /* Shift to get page */ + page = ((int) from) >> this->page_shift; + + /* Mask to get column */ + col = from & (mtd->oobsize-1); //0x0f; + + /* Initialize return length value */ + *retlen = 0; + sl2312_nand_read_ecc (mtd, page, mtd->oobblock , retlen, databuf, oobbuf, NULL); + for(i=col,j=0;ioobsize||i<(col+len);i++,j++) + buf[j] = oobbuf[i]; + + *retlen = j ; + return 0; +} + +#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0 +/* +* Use NAND write ECC +*/ +static int sl2312_nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) +{ + return (sl2312_nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL)); +} + +/* + * NAND write with ECC + */ +static int sl2312_nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) +{ + int page, ret = 0, oob = 0, written = 0; + struct nand_chip *this = mtd->priv; + + DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + + + /* Do not allow write past end of device */ + if ((to + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n"); + return -EINVAL; + } + + /* reject writes, which are not page aligned */ + if (NOTALIGNED (to) || NOTALIGNED(len)) { + printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); + return -EINVAL; + } + + // if oobsel is NULL, use chip defaults + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + + /* Shift to get page */ + page = ((int) to) >> this->page_shift; + + /* Grab the lock and see if the device is available */ + sl2312_nand_get_chip (this, mtd, FL_WRITING, NULL); + + /* Select the NAND device */ + this->select_chip(mtd, 0); + + /* Check the WP bit */ + if (!(sl2312_device_ready(mtd) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Device is write protected!!!\n"); + ret = -EIO; + goto out; + } + + /* Loop until all data is written */ + while (written < len) { + //udelay(100); + int cnt = mtd->oobblock; + this->data_poi = (u_char*) &buf[written]; + /* We use the same function for write and writev */ + if (eccbuf) { + ret = sl2312_nand_write_page (mtd, this, page, &eccbuf[oob], oobsel); + oob += mtd->oobsize; + } else + ret = sl2312_nand_write_page (mtd, this, page, NULL, oobsel); + + if (ret) + goto out; + + /* Update written bytes count */ + written += cnt; + /* Increment page address */ + page++; + } + +out: + /* De-select the NAND device */ + //this->select_chip(mtd, -1); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + + *retlen = written; + return ret; +} + +/* + * Nand_page_program function is used for write and writev ! + * This function will always program a full page of data + * If you call it with a non page aligned buffer, you're lost :) + */ +static int sl2312_nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel) +{ + int i, j, status, opcode; + u_char ecc_code[16], *oob_data; + int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; + //int *oob_config = oobsel->eccpos; + + /* pad oob area, if we have no oob buffer from fs-driver */ + if (!oob_buf) { + oob_data = &this->data_buf[mtd->oobblock]; + for (i = 0; i < mtd->oobsize; i++) + oob_data[i] = 0xff; + } else + oob_data = oob_buf; + + /* Send command to begin auto page programming */ + + memset(oob_data,0xff,mtd->oobsize); + /* Write out complete page of data, take care of eccmode */ + switch (eccmode) { + /* No ecc and software ecc 3/256, write all */ + case NAND_ECC_NONE: + printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x0); //set 31b = 0 + break; + case NAND_ECC_SOFT: + break; + + /* Hardware ecc 3 byte / 256 data, write first half, get ecc, then second, if 512 byte pagesize */ + case NAND_ECC_HW3_256: + break; + + /* Hardware ecc 3 byte / 512 byte data, write full page */ + case NAND_ECC_HW3_512: + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x80000001); //set 31b = 0 + + /* Hardware ecc 6 byte / 512 byte data, write full page */ + case NAND_ECC_HW6_512: + break; + + default: + printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x0); //set 31b = 0 + //BUG(); + } + + FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT); + + for(i=0;ioobblock;i++) + { + //udelay(5); + FLASH_WRITE_DATA((page*mtd->oobblock)+i,this->data_poi[i]); + } + /////////////// + if(eccmode!=NAND_ECC_NONE) + { + opcode=FLASH_READ_REG(NFLASH_ECC_STATUS); + while(!(opcode&0x80000000)) //polling flash access 31b + { + opcode=FLASH_READ_REG(NFLASH_ECC_STATUS); + //sl2312_flash_delay(); + schedule(); + } + + + for(i=0;i<(mtd->oobblock/512);i++) + { + opcode=FLASH_READ_REG(NFLASH_ECC_CODE_GEN0+(i*4)); + + for(j=3;j>0;j--) + oob_data[(mtd->oobsize-j-(i*4))] = (opcode<<((4-j)*8)) >>24; + + for(j=0;j<4;j++) + { + ecc_code[15-i*4] = opcode; + ecc_code[15-i*4-1] = opcode>>8; + ecc_code[15-i*4-2] = opcode>>16; + } + } + + //disable ecc + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x00000000); + + /* Write out OOB data */ + for(i=0;ioobsize;i++) + { + //udelay(5); + FLASH_WRITE_DATA((page*mtd->oobblock)+mtd->oobblock+i,oob_data[i]); + } + } + else + { + for(i=0;ioobsize;i++) + { + //udelay(5); + FLASH_WRITE_DATA((page*mtd->oobblock)+mtd->oobblock+i,0xff); + } + } + + + /* call wait ready function */ + status = this->waitfunc (mtd, this, FL_WRITING); + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x0); //set 31b = 0 + /* See if device thinks it succeeded */ + if (status & 0x01) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); + FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x0); //set 31b = 0 + return -EIO; + } + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* + * The NAND device assumes that it is always writing to + * a cleanly erased page. Hence, it performs its internal + * write verification only on bits that transitioned from + * 1 to 0. The device does NOT verify the whole page on a + * byte by byte basis. It is possible that the page was + * not completely erased or the page is becoming unusable + * due to wear. The read with ECC would catch the error + * later when the ECC page check fails, but we would rather + * catch it early in the page write stage. Better to write + * no data than invalid data. + */ + + /* Send command to read back the page */ + this->cmdfunc (mtd, NAND_CMD_READ0, 0, page); + /* Loop through and verify the data */ + if (this->verify_buf(mtd, this->data_poi, mtd->oobblock)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + return -EIO; + } + + /* check, if we have a fs-supplied oob-buffer */ + if (oob_buf) { + if (this->verify_buf(mtd, oob_data, mtd->oobsize)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + return -EIO; + } + } else { + if (eccmode != NAND_ECC_NONE) { + int ecc_bytes = 0; + + switch (this->eccmode) { + case NAND_ECC_SOFT: + case NAND_ECC_HW3_256: ecc_bytes = (mtd->oobblock == 512) ? 6 : 3; break; + case NAND_ECC_HW3_512: ecc_bytes = 3; break; + case NAND_ECC_HW6_512: ecc_bytes = 6; break; + } + + + + for(i=0;i < (mtd->oobblock+mtd->oobsize);i++) + { + if(i>=mtd->oobblock) + oob_data[i-mtd->oobblock] = FLASH_READ_DATA((page*mtd->oobblock) +i); + else + oob_data[0] = FLASH_READ_DATA((page*mtd->oobblock) +i); + } + + if(this->eccmode == NAND_ECC_HW3_512) + { + for(i=0;i<(mtd->oobblock/512);i++) + { + for(j=0;j<3;j++) + { + if (oob_data[mtd->oobsize-1-j-4*i] != ecc_code[15-j-4*i]) { + DEBUG (MTD_DEBUG_LEVEL0, + "%s: Failed ECC write " + "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i); + return -EIO; + } + } + } + } + }//eccmode != NAND_ECC_NONE + } + /* + * Terminate the read command. This is faster than sending a reset command or + * applying a 20us delay before issuing the next programm sequence. + * This is not a problem for all chips, but I have found a bunch of them. + */ + //this->select_chip(mtd, -1); + //this->select_chip(mtd, 0); +#endif + + return 0; +} + +/* + * NAND write with iovec + */ +static int sl2312_nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, + loff_t to, size_t * retlen) +{ + return (sl2312_nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, 0)); +} + +static int sl2312_nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, + loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel) +{ + int i, page, len, total_len, ret = 0, written = 0; + struct nand_chip *this = mtd->priv; + + /* Calculate total length of data */ + total_len = 0; + for (i = 0; i < count; i++) + total_len += (int) vecs[i].iov_len; + + DEBUG (MTD_DEBUG_LEVEL3, + "nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count); + + /* Do not allow write past end of page */ + if ((to + total_len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n"); + return -EINVAL; + } + + /* reject writes, which are not page aligned */ + if (NOTALIGNED (to) || NOTALIGNED(total_len)) { + printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); + return -EINVAL; + } + + // if oobsel is NULL, use chip defaults + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + + /* Shift to get page */ + page = ((int) to) >> this->page_shift; + + /* Grab the lock and see if the device is available */ + sl2312_nand_get_chip (this, mtd, FL_WRITING, NULL); + + /* Select the NAND device */ + this->select_chip(mtd, 0); + + /* Check the WP bit */ + if (!(sl2312_device_ready(mtd) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, "sl2312_nand_writev_ecc: Device is write protected!!!\n"); + ret = -EIO; + goto out; + } + + /* Loop until all iovecs' data has been written */ + len = 0; + while (count) { + /* + * Check, if the tuple gives us not enough data for a + * full page write. Then we can use the iov direct, + * else we have to copy into data_buf. + */ + if ((vecs->iov_len - len) >= mtd->oobblock) { + this->data_poi = (u_char *) vecs->iov_base; + this->data_poi += len; + len += mtd->oobblock; + /* Check, if we have to switch to the next tuple */ + if (len >= (int) vecs->iov_len) { + vecs++; + len = 0; + count--; + } + } else { + /* + * Read data out of each tuple until we have a full page + * to write or we've read all the tuples. + */ + int cnt = 0; + while ((cnt < mtd->oobblock) && count) { + if (vecs->iov_base != NULL && vecs->iov_len) { + this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++]; + } + /* Check, if we have to switch to the next tuple */ + if (len >= (int) vecs->iov_len) { + vecs++; + len = 0; + count--; + } + } + this->data_poi = this->data_buf; + } + + /* We use the same function for write and writev !) */ + ret = sl2312_nand_write_page (mtd, this, page, NULL, oobsel); + if (ret) + goto out; + + /* Update written bytes count */ + written += mtd->oobblock;; + + /* Increment page address */ + page++; + } + +out: + /* De-select the NAND device */ + //this->select_chip(mtd, -1); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + + *retlen = written; + return ret; +} + +/* +static u_char ffchars[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; +*/ +/* + * NAND write out-of-band + */ +static int sl2312_nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) +{ + int column, page, status, ret = 0, j=0; + struct nand_chip *this = mtd->priv; + u_char *databuf, *oobbuf; + + + databuf = &this->data_buf[0]; + oobbuf = &this->data_buf[mtd->oobblock]; + for (j = 0; j < mtd->oobsize; j++) + oobbuf[j] = 0xff; +//#ifdef CONFIG_MTD_NAND_VERIFY_WRITE +// int i; +//#endif + + DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + + /* Shift to get page */ + page = ((int) to) >> this->page_shift; + + /* Mask to get column */ + column = to & 0x1f; + + /* Initialize return length value */ + *retlen = 0; + + /* Do not allow write past end of page */ + if ((column + len) > mtd->oobsize) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + sl2312_nand_get_chip (this, mtd, FL_WRITING, NULL); + + /* Select the NAND device */ + this->select_chip(mtd, 0); + + /* Reset the chip. Some chips (like the Toshiba TC5832DC found + in one of my DiskOnChip 2000 test units) will clear the whole + data page too if we don't do this. I have no clue why, but + I seem to have 'fixed' it in the doc2000 driver in + August 1999. dwmw2. */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Check the WP bit */ + if (!(sl2312_device_ready(mtd) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Device is write protected!!!\n"); + ret = -EIO; + goto out; + } + /* Write out desired data */ + this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page); + + sl2312_nand_read_ecc (mtd, page, mtd->oobblock , retlen, databuf, oobbuf, NULL); + + for(j=column;j<(column+len);j++) + oobbuf[j] = buf[j-column]; + sl2312_nand_write_ecc (mtd, page, mtd->oobblock, retlen, databuf, oobbuf, NULL); + + status = this->waitfunc (mtd, this, FL_WRITING); + + /* See if device thinks it succeeded */ + if (status & 0x01) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page); + ret = -EIO; + goto out; + } + /* Return happy */ + *retlen = len; + + +out: + /* De-select the NAND device */ + //this->select_chip(mtd, -1); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + + return ret; +} + +/* + * NAND sync + */ +static void sl2312_nand_sync (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + DECLARE_WAITQUEUE (wait, current); + + DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n"); + +retry: + /* Grab the spinlock */ + spin_lock_bh (&this->chip_lock); + + /* See what's going on */ + switch (this->state) { + case FL_READY: + case FL_SYNCING: + this->state = FL_SYNCING; + spin_unlock_bh (&this->chip_lock); + break; + + default: + /* Not an idle state */ + add_wait_queue (&this->wq, &wait); + spin_unlock_bh (&this->chip_lock); + schedule (); + + remove_wait_queue (&this->wq, &wait); + goto retry; + } + + /* Lock the device */ + spin_lock_bh (&this->chip_lock); + + /* Set the device to be ready again */ + if (this->state == FL_SYNCING) { + this->state = FL_READY; + wake_up (&this->wq); + } + + /* Unlock the device */ + spin_unlock_bh (&this->chip_lock); +} + + +/* + * Scan for the NAND device + */ +int sl2312_nand_scan (struct mtd_info *mtd, int maxchips) +{ + int i, j, nand_maf_id, nand_dev_id, busw; + struct nand_chip *this = mtd->priv; + unsigned char id[4]; + + /* Get buswidth to select the correct functions*/ + busw = this->options & NAND_BUSWIDTH_16; + + /* check for proper chip_delay setup, set 20us if not */ + if (!this->chip_delay) + this->chip_delay = 20; + + /* check, if a user supplied command function given */ + if (this->cmdfunc == NULL) + this->cmdfunc = sl2312_nand_command; + + /* check, if a user supplied wait function given */ + if (this->waitfunc == NULL) + this->waitfunc = sl2312_nand_waitfunc; + + if (!this->select_chip) + this->select_chip = sl2312_nand_select_chip; + if (!this->write_byte) + this->write_byte = sl2312_nand_write_byte; //busw ? nand_write_byte16 : nand_write_byte; + if (!this->read_byte) + this->read_byte = sl2312_nand_read_byte; //busw ? nand_read_byte16 : nand_read_byte; +// if (!this->write_word) +// this->write_word = nand_write_word; +// if (!this->read_word) +// this->read_word = nand_read_word; +// if (!this->block_bad) + this->block_bad = sl2312_nand_block_bad; //nand_block_bad; + if (!this->block_markbad) + this->block_markbad = sl2312_nand_default_block_markbad; + if (!this->write_buf) + this->write_buf = sl2312_nand_write_buf; //busw ? nand_write_buf16 : nand_write_buf; + if (!this->read_buf) + this->read_buf = sl2312_nand_read_buf; //busw ? nand_read_buf16 : nand_read_buf; + if (!this->verify_buf) + this->verify_buf = sl2312_nand_verify_buf; //busw ? nand_verify_buf16 : nand_verify_buf; + if (!this->scan_bbt) + this->scan_bbt = sl2312_nand_scan_bbt; + + /* Select the device */ + this->select_chip(mtd, 0); + + /* Read manufacturer and device IDs */ + nand_read_id(0,id); + + nand_maf_id = id[0]; + nand_dev_id = id[1]; + + /* Print and store flash device information */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + + if (nand_dev_id != nand_flash_ids[i].id) + continue; + + if (!mtd->name) mtd->name = nand_flash_ids[i].name; + this->chipsize = nand_flash_ids[i].chipsize << 20; + + /* New devices have all the information in additional id bytes */ + if (!nand_flash_ids[i].pagesize) { + int extid; + + /* The 4th id byte is the important one */ + extid = id[3]; + /* Calc pagesize */ + mtd->oobblock = 1024 << (extid & 0x3); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + + } else { + /* Old devices have this data hardcoded in the + * device id table */ + mtd->erasesize = nand_flash_ids[i].erasesize; + mtd->oobblock = nand_flash_ids[i].pagesize; + mtd->oobsize = mtd->oobblock / 32; + busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16; + } + + /* Check, if buswidth is correct. Hardware drivers should set + * this correct ! */ + if (busw != (this->options & NAND_BUSWIDTH_16)) { + printk (KERN_INFO "NAND device: Manufacturer ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, + nand_manuf_ids[i].name , mtd->name); + printk (KERN_WARNING + "NAND bus width %d instead %d bit\n", + (this->options & NAND_BUSWIDTH_16) ? 16 : 8, + busw ? 16 : 8); + this->select_chip(mtd, -1); + return 1; + } + + /* Calculate the address shift from the page size */ + this->page_shift = ffs(mtd->oobblock) - 1; + this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1; + this->chip_shift = ffs(this->chipsize) - 1; + + /* Set the bad block position */ + this->badblockpos = mtd->oobblock > 512 ? + NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; + + /* Get chip options, preserve non chip based options */ + this->options &= ~NAND_CHIPOPTIONS_MSK; + this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK; + /* Set this as a default. Board drivers can override it, if neccecary */ + this->options |= NAND_NO_AUTOINCR; + /* Check if this is a not a samsung device. Do not clear the options + * for chips which are not having an extended id. + */ + if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize) + this->options &= ~NAND_SAMSUNG_LP_OPTIONS; + + /* Check for AND chips with 4 page planes */ + // if (this->options & NAND_4PAGE_ARRAY) + // this->erase_cmd = multi_erase_cmd; + // else + // this->erase_cmd = single_erase_cmd; + + /* Do not replace user supplied command function ! */ + // if (mtd->oobblock > 512 && this->cmdfunc == nand_command) + // this->cmdfunc = nand_command_lp; + + /* Try to identify manufacturer */ + for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { + if (nand_manuf_ids[j].id == nand_maf_id) + break; + } + printk (KERN_INFO "NAND device: Manufacturer ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, + nand_manuf_ids[j].name , nand_flash_ids[i].name); + break; + } + ///////////////////////////// + + for (i=1; i < maxchips; i++) { + this->select_chip(mtd, i); + + /* Send the command for reading device ID */ + nand_read_id(1,id); + + /* Read manufacturer and device IDs */ + if (nand_maf_id != id[0] || + nand_dev_id != id[1]) + break; + } + if (i > 1) + printk(KERN_INFO "%d NAND chips detected\n", i); + + /* Allocate buffers, if neccecary */ + if (!this->oob_buf) { + size_t len; + len = mtd->oobsize << (this->phys_erase_shift - this->page_shift); + this->oob_buf = kmalloc (len, GFP_KERNEL); + if (!this->oob_buf) { + printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n"); + return -ENOMEM; + } + this->options |= NAND_OOBBUF_ALLOC; + } + + if (!this->data_buf) { + size_t len; + len = mtd->oobblock + mtd->oobsize; + this->data_buf = kmalloc (len, GFP_KERNEL); + if (!this->data_buf) { + if (this->options & NAND_OOBBUF_ALLOC) + kfree (this->oob_buf); + printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n"); + return -ENOMEM; + } + this->options |= NAND_DATABUF_ALLOC; + } + + /* Store the number of chips and calc total size for mtd */ + this->numchips = i; + mtd->size = i * this->chipsize; + /* Convert chipsize to number of pages per chip -1. */ + this->pagemask = (this->chipsize >> this->page_shift) - 1; + /* Preset the internal oob buffer */ + memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift)); + + /* If no default placement scheme is given, select an + * appropriate one */ + if (!this->autooob) { + /* Select the appropriate default oob placement scheme for + * placement agnostic filesystems */ + switch (mtd->oobsize) { + case 8: + this->autooob = &nand_oob_8; + break; + case 16: + this->autooob = &nand_oob_16; + break; + case 64: + this->autooob = &nand_oob_64; + break; + default: + printk (KERN_WARNING "No oob scheme defined for oobsize %d\n", + mtd->oobsize); + BUG(); + } + } + + /* The number of bytes available for the filesystem to place fs dependend + * oob data */ + if (this->options & NAND_BUSWIDTH_16) { + mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2); + if (this->autooob->eccbytes & 0x01) + mtd->oobavail--; + } else + mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1); + + + /* + * check ECC mode, default to software + * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize + * fallback to software ECC + */ + this->eccsize = 256; /* set default eccsize */ + this->eccbytes = 3; + + switch (this->eccmode) { + case NAND_ECC_HW12_2048: + if (mtd->oobblock < 2048) { + printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", + mtd->oobblock); + this->eccmode = NAND_ECC_SOFT; + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + } else + this->eccsize = 2048; + break; + + case NAND_ECC_HW3_512: + case NAND_ECC_HW6_512: + case NAND_ECC_HW8_512: + if (mtd->oobblock == 256) { + printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n"); + this->eccmode = NAND_ECC_SOFT; + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + } else + this->eccsize = 512; /* set eccsize to 512 */ + break; + + case NAND_ECC_HW3_256: + break; + + case NAND_ECC_NONE: + printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); + this->eccmode = NAND_ECC_NONE; + break; + + case NAND_ECC_SOFT: + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + break; + + default: + printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); + BUG(); + } + + /* Check hardware ecc function availability and adjust number of ecc bytes per + * calculation step + */ + switch (this->eccmode) { + case NAND_ECC_HW12_2048: + this->eccbytes += 4; + case NAND_ECC_HW8_512: + this->eccbytes += 2; + case NAND_ECC_HW6_512: + this->eccbytes += 3; +// case NAND_ECC_HW3_512: + case NAND_ECC_HW3_256: + if (this->calculate_ecc && this->correct_data && this->enable_hwecc) + break; + printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); + BUG(); + } + + mtd->eccsize = this->eccsize; + + /* Set the number of read / write steps for one page to ensure ECC generation */ + switch (this->eccmode) { + case NAND_ECC_HW12_2048: + this->eccsteps = mtd->oobblock / 2048; + break; + case NAND_ECC_HW3_512: + case NAND_ECC_HW6_512: + case NAND_ECC_HW8_512: + this->eccsteps = mtd->oobblock / 512; + break; + case NAND_ECC_HW3_256: + case NAND_ECC_SOFT: + this->eccsteps = mtd->oobblock / 256; + break; + + case NAND_ECC_NONE: + this->eccsteps = 1; + break; + } + + /* Initialize state, waitqueue and spinlock */ + this->state = FL_READY; + init_waitqueue_head (&this->wq); + spin_lock_init (&this->chip_lock); + + /* De-select the device */ + this->select_chip(mtd, 0); + + /* Print warning message for no device */ + if (!mtd->size) { + printk (KERN_WARNING "No NAND device found!!!\n"); + return 1; + } + + /* Fill in remaining MTD driver data */ + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; + mtd->ecctype = MTD_ECC_SW; + mtd->erase = sl2312_nand_erase; + mtd->point = NULL; + mtd->unpoint = NULL; + mtd->read = sl2312_nand_read; + mtd->write = sl2312_nand_write; + mtd->read_ecc = sl2312_nand_read_ecc; + mtd->write_ecc = sl2312_nand_write_ecc; + mtd->read_oob = sl2312_nand_read_oob; + mtd->write_oob = sl2312_nand_write_oob; + mtd->readv = NULL; + mtd->writev = sl2312_nand_writev; + mtd->writev_ecc = sl2312_nand_writev_ecc; + mtd->sync = sl2312_nand_sync; + mtd->lock = NULL; + mtd->unlock = NULL; + mtd->suspend = NULL; + mtd->resume = NULL; + mtd->block_isbad = sl2312_nand_block_isbad; + mtd->block_markbad = sl2312_nand_block_markbad; + + /* and make the autooob the default one */ + memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo)); + + mtd->owner = THIS_MODULE; + + /* Build bad block table */ + return this->scan_bbt (mtd); +} + +/*End Add function*/ + +/* + * Main initialization routine + */ +extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); + +int __init sl2312_mtd_init (void) +{ + struct nand_chip *this; + int err = 0; + struct mtd_partition *parts; + int nr_parts = 0; + int ret, data, *base; + + printk("NAND MTD Driver Start Init ......\n"); + + base = (unsigned int *)(IO_ADDRESS(SL2312_GLOBAL_BASE) + 0x30); + data = *base; + data&=0xffffffeb; + data|=0x3; //disable p & s flash + *base = data; + + /* Allocate memory for MTD device structure and private data */ + sl2312_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); + if (!sl2312_mtd) { + printk ("Unable to allocate SL2312 NAND MTD device structure.\n"); + err = -ENOMEM; + goto out; + } + + // sl2312_device_setup(); + + /* io is indirect via a register so don't need to ioremap address */ + + /* Get pointer to private data */ + this = (struct nand_chip *) (&sl2312_mtd[1]); + + /* Initialize structures */ + memset((char *) sl2312_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + sl2312_mtd->priv = this; + sl2312_mtd->name = "sl2312-nand"; + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = (void __iomem *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE+NFLASH_DATA)); //(unsigned long)&(sl2312_ndfmcptr->dtr); + this->IO_ADDR_W = (void __iomem *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE+NFLASH_DATA)); //(unsigned long)&(sl2312_ndfmcptr->dtr); + this->read_byte = sl2312_nand_read_byte; + this->write_byte = sl2312_nand_write_byte; + this->write_buf = sl2312_nand_write_buf; + this->read_buf = sl2312_nand_read_buf; + this->verify_buf = sl2312_nand_verify_buf; + this->select_chip = sl2312_nand_select_chip; + this->block_bad = sl2312_nand_block_bad; + this->hwcontrol = sl2312_hwcontrol; + this->dev_ready = sl2312_device_ready; + this->cmdfunc = sl2312_nand_command; + this->waitfunc = sl2312_nand_waitfunc; + //this->calculate_ecc = sl2312_readecc; + this->enable_hwecc = sl2312_enable_hwecc; + this->eccmode = NAND_ECC_HW3_512; + /*this->eccsize = 512; */ + /* 20 us command delay time */ + this->chip_delay = 20; + + this->correct_data = nand_correct_data; +// this->scan_bbt = sl2312_nand_scan_bbt; + + /* Allocate memory for internal data buffer */ + this->data_buf = kmalloc (sizeof(u_char) * (sl2312_mtd->oobblock + sl2312_mtd->oobsize), GFP_KERNEL); + if (!this->data_buf) { + printk ("Unable to allocate NAND data buffer.\n"); + err = -ENOMEM; + goto out_ior; + } + + /* Scan to find existance of the device */ + if (sl2312_nand_scan(sl2312_mtd, 1)) { + err = -ENXIO; + goto out_ior; + } + + /* Register the partitions */ + parts = sl2312_partitions; + nr_parts = sizeof(sl2312_partitions)/sizeof(*parts); + + ret = add_mtd_partitions(sl2312_mtd, sl2312_partitions, nr_parts); + /*If we got an error, free all resources.*/ + if (ret < 0) { + del_mtd_partitions(sl2312_mtd); + map_destroy(sl2312_mtd); + } + goto out; + +//out_buf: +// kfree (this->data_buf); +out_ior: +out: + printk("NAND MTD Driver Init Success ......\n"); + return err; +} + +module_init(sl2312_mtd_init); + +/* + * Clean up routine + */ +#ifdef MODULE +static void __exit sl2312_cleanup (void) +{ + struct nand_chip *this = (struct nand_chip *) &sl2312_mtd[1]; + + /* Unregister partitions */ + del_mtd_partitions(sl2312_mtd); + + /* Unregister the device */ + del_mtd_device (sl2312_mtd); + + /* Free internal data buffers */ + kfree (this->data_buf); + + /* Free the MTD device structure */ + kfree (sl2312_mtd); +} +module_exit(sl2312_cleanup); +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alice Hennessy "); +MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBsl2312"); --- /dev/null +++ b/drivers/mtd/nand/sl2312-flash-nand.h @@ -0,0 +1,24 @@ +#ifndef SL2312_FLASH_NAND_H +#define SL2312_FLASH_NAND_H + +#include +#include + +/*Add function*/ +static void nand_read_id(int chip_no,unsigned char *id); + + + +#define NFLASH_WiDTH8 0x00000000 +#define NFLASH_WiDTH16 0x00000400 +#define NFLASH_WiDTH32 0x00000800 +#define NFLASH_CHIP0_EN 0x00000000 // 16th bit = 0 +#define NFLASH_CHIP1_EN 0x00010000 // 16th bit = 1 +#define NFLASH_DIRECT 0x00004000 +#define NFLASH_INDIRECT 0x00000000 + + +#define DWIDTH NFLASH_WiDTH8 + + +#endif /* SL2312_FLASH_NAND_H */ --- /dev/null +++ b/include/linux/mtd/kvctl.h @@ -0,0 +1,40 @@ +#ifndef KVCTL_H +#define KVCTL_H + +#define VCTL_HEAD_SIZE 8 +#define VCTL_ENTRY_LEN 20 + +typedef struct +{ + char header[4]; + unsigned int entry_num; +} vctl_mheader; + +typedef struct +{ + char header[4]; + unsigned int size; + unsigned int type; + char majorver[4]; + char minorver[4]; + unsigned char *payload; +} vctl_entry; + +typedef struct +{ + unsigned char mac[6]; + unsigned char vlanid; + unsigned char vlanmap; +} vlaninfo; + +#define VCT_VENDORSPEC 0 +#define VCT_BOOTLOADER 1 +#define VCT_KERNEL 2 +#define VCT_VERCTL 3 +#define VCT_CURRCONF 4 +#define VCT_DEFAULTCONF 5 +#define VCT_ROOTFS 6 +#define VCT_APP 7 +#define VCT_VLAN 8 + +#endif --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -71,3 +71,7 @@ obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o obj-$(CONFIG_MTD_MTX1) += mtx-1_flash.o obj-$(CONFIG_MTD_TQM834x) += tqm834x.o +###### for Storlink Soc ####### +obj-$(CONFIG_MTD_SL2312_CFI) += sl2312-flash-cfi.o +obj-$(CONFIG_MTD_SL2312_SERIAL_ATMEL) += sl2312-flash-atmel.o +obj-$(CONFIG_MTD_SL2312_SERIAL_ST) += sl2312-flash-m25p80.o