From a6c591eda3078f92e7a3ff3db55f6841e4819fb5 Mon Sep 17 00:00:00 2001 From: Jörn Engel Date: Thu, 13 Apr 2006 18:54:34 +0200 Subject: Remove unchecked MTD flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several flags are set by some devices, but never checked. Remove them. Signed-off-by: Jörn Engel Signed-off-by: David Woodhouse --- include/mtd/mtd-abi.h | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index b5994ea56a5a..cc7be33b4225 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -35,20 +35,14 @@ struct mtd_oob_buf { #define MTD_CLEAR_BITS 1 // Bits can be cleared (flash) #define MTD_SET_BITS 2 // Bits can be set -#define MTD_ERASEABLE 4 // Has an erase function -#define MTD_WRITEB_WRITEABLE 8 // Direct IO is possible -#define MTD_VOLATILE 16 // Set for RAMs -#define MTD_XIP 32 // eXecute-In-Place possible -#define MTD_OOB 64 // Out-of-band data (NAND flash) #define MTD_ECC 128 // Device capable of automatic ECC -#define MTD_NO_VIRTBLOCKS 256 // Virtual blocks not allowed #define MTD_PROGRAM_REGIONS 512 // Configurable Programming Regions // Some common devices / combinations of capabilities #define MTD_CAP_ROM 0 -#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE) -#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE) -#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB) +#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS) +#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS) +#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS) #define MTD_WRITEABLE (MTD_CLEAR_BITS|MTD_SET_BITS) -- cgit v1.2.3 From cd2866faaa0efd9af18fe4a86d129cbd99240796 Mon Sep 17 00:00:00 2001 From: Jörn Engel Date: Thu, 13 Apr 2006 18:55:09 +0200 Subject: Remove unused MTD types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three types are never set or checked for. Remove. Signed-off-by: Jörn Engel Signed-off-by: David Woodhouse --- include/mtd/mtd-abi.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index cc7be33b4225..fa2524157846 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -28,10 +28,7 @@ struct mtd_oob_buf { #define MTD_ROM 2 #define MTD_NORFLASH 3 #define MTD_NANDFLASH 4 -#define MTD_PEROM 5 #define MTD_DATAFLASH 6 -#define MTD_OTHER 14 -#define MTD_UNKNOWN 15 #define MTD_CLEAR_BITS 1 // Bits can be cleared (flash) #define MTD_SET_BITS 2 // Bits can be set -- cgit v1.2.3 From cbb9a56177b16294ed347ba7fcb1c66c8adb5dc4 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 3 May 2006 13:07:27 +0100 Subject: Move jffs2_fs_i.h and jffs2_fs_sb.h from include/linux/ to fs/jffs2/ Signed-off-by: David Woodhouse --- fs/jffs2/compr.h | 4 +- fs/jffs2/dir.c | 4 +- fs/jffs2/jffs2_fs_i.h | 50 ++++++++++++++++++ fs/jffs2/jffs2_fs_sb.h | 122 ++++++++++++++++++++++++++++++++++++++++++++ fs/jffs2/nodelist.h | 4 +- include/linux/jffs2_fs_i.h | 50 ------------------ include/linux/jffs2_fs_sb.h | 122 -------------------------------------------- 7 files changed, 178 insertions(+), 178 deletions(-) create mode 100644 fs/jffs2/jffs2_fs_i.h create mode 100644 fs/jffs2/jffs2_fs_sb.h delete mode 100644 include/linux/jffs2_fs_i.h delete mode 100644 include/linux/jffs2_fs_sb.h (limited to 'include') diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h index a77e830d85c5..509b8b1c0811 100644 --- a/fs/jffs2/compr.h +++ b/fs/jffs2/compr.h @@ -23,8 +23,8 @@ #include #include #include -#include -#include +#include "jffs2_fs_i.h" +#include "jffs2_fs_sb.h" #include "nodelist.h" #define JFFS2_RUBINMIPS_PRIORITY 10 diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index f92840a3a52f..1c8e8c0f6cea 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -17,8 +17,8 @@ #include #include #include -#include -#include +#include "jffs2_fs_i.h" +#include "jffs2_fs_sb.h" #include #include "nodelist.h" diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h new file mode 100644 index 000000000000..ad565bf9dcc1 --- /dev/null +++ b/fs/jffs2/jffs2_fs_i.h @@ -0,0 +1,50 @@ +/* $Id: jffs2_fs_i.h,v 1.19 2005/11/07 11:14:52 gleixner Exp $ */ + +#ifndef _JFFS2_FS_I +#define _JFFS2_FS_I + +#include +#include +#include + +struct jffs2_inode_info { + /* We need an internal mutex similar to inode->i_mutex. + Unfortunately, we can't used the existing one, because + either the GC would deadlock, or we'd have to release it + before letting GC proceed. Or we'd have to put ugliness + into the GC code so it didn't attempt to obtain the i_mutex + for the inode(s) which are already locked */ + struct semaphore sem; + + /* The highest (datanode) version number used for this ino */ + uint32_t highest_version; + + /* List of data fragments which make up the file */ + struct rb_root fragtree; + + /* There may be one datanode which isn't referenced by any of the + above fragments, if it contains a metadata update but no actual + data - or if this is a directory inode */ + /* This also holds the _only_ dnode for symlinks/device nodes, + etc. */ + struct jffs2_full_dnode *metadata; + + /* Directory entries */ + struct jffs2_full_dirent *dents; + + /* The target path if this is the inode of a symlink */ + unsigned char *target; + + /* Some stuff we just have to keep in-core at all times, for each inode. */ + struct jffs2_inode_cache *inocache; + + uint16_t flags; + uint8_t usercompr; +#if !defined (__ECOS) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) + struct inode vfs_inode; +#endif +#endif +}; + +#endif /* _JFFS2_FS_I */ diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h new file mode 100644 index 000000000000..4bcfb5570221 --- /dev/null +++ b/fs/jffs2/jffs2_fs_sb.h @@ -0,0 +1,122 @@ +/* $Id: jffs2_fs_sb.h,v 1.54 2005/09/21 13:37:34 dedekind Exp $ */ + +#ifndef _JFFS2_FS_SB +#define _JFFS2_FS_SB + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JFFS2_SB_FLAG_RO 1 +#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */ +#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */ + +struct jffs2_inodirty; + +/* A struct for the overall file system control. Pointers to + jffs2_sb_info structs are named `c' in the source code. + Nee jffs_control +*/ +struct jffs2_sb_info { + struct mtd_info *mtd; + + uint32_t highest_ino; + uint32_t checked_ino; + + unsigned int flags; + + struct task_struct *gc_task; /* GC task struct */ + struct completion gc_thread_start; /* GC thread start completion */ + struct completion gc_thread_exit; /* GC thread exit completion port */ + + struct semaphore alloc_sem; /* Used to protect all the following + fields, and also to protect against + out-of-order writing of nodes. And GC. */ + uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER + (i.e. zero for OOB CLEANMARKER */ + + uint32_t flash_size; + uint32_t used_size; + uint32_t dirty_size; + uint32_t wasted_size; + uint32_t free_size; + uint32_t erasing_size; + uint32_t bad_size; + uint32_t sector_size; + uint32_t unchecked_size; + + uint32_t nr_free_blocks; + uint32_t nr_erasing_blocks; + + /* Number of free blocks there must be before we... */ + uint8_t resv_blocks_write; /* ... allow a normal filesystem write */ + uint8_t resv_blocks_deletion; /* ... allow a normal filesystem deletion */ + uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */ + uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */ + uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */ + + uint32_t nospc_dirty_size; + + uint32_t nr_blocks; + struct jffs2_eraseblock *blocks; /* The whole array of blocks. Used for getting blocks + * from the offset (blocks[ofs / sector_size]) */ + struct jffs2_eraseblock *nextblock; /* The block we're currently filling */ + + struct jffs2_eraseblock *gcblock; /* The block we're currently garbage-collecting */ + + struct list_head clean_list; /* Blocks 100% full of clean data */ + struct list_head very_dirty_list; /* Blocks with lots of dirty space */ + struct list_head dirty_list; /* Blocks with some dirty space */ + struct list_head erasable_list; /* Blocks which are completely dirty, and need erasing */ + struct list_head erasable_pending_wbuf_list; /* Blocks which need erasing but only after the current wbuf is flushed */ + struct list_head erasing_list; /* Blocks which are currently erasing */ + struct list_head erase_pending_list; /* Blocks which need erasing now */ + struct list_head erase_complete_list; /* Blocks which are erased and need the clean marker written to them */ + struct list_head free_list; /* Blocks which are free and ready to be used */ + struct list_head bad_list; /* Bad blocks. */ + struct list_head bad_used_list; /* Bad blocks with valid data in. */ + + spinlock_t erase_completion_lock; /* Protect free_list and erasing_list + against erase completion handler */ + wait_queue_head_t erase_wait; /* For waiting for erases to complete */ + + wait_queue_head_t inocache_wq; + struct jffs2_inode_cache **inocache_list; + spinlock_t inocache_lock; + + /* Sem to allow jffs2_garbage_collect_deletion_dirent to + drop the erase_completion_lock while it's holding a pointer + to an obsoleted node. I don't like this. Alternatives welcomed. */ + struct semaphore erase_free_sem; + + uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ + +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + /* Write-behind buffer for NAND flash */ + unsigned char *wbuf; + uint32_t wbuf_ofs; + uint32_t wbuf_len; + struct jffs2_inodirty *wbuf_inodes; + + struct rw_semaphore wbuf_sem; /* Protects the write buffer */ + + /* Information about out-of-band area usage... */ + struct nand_oobinfo *oobinfo; + uint32_t badblock_pos; + uint32_t fsdata_pos; + uint32_t fsdata_len; +#endif + + struct jffs2_summary *summary; /* Summary information */ + + /* OS-private pointer for getting back to master superblock info */ + void *os_priv; +}; + +#endif /* _JFFS2_FB_SB */ diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index 23a67bb3052f..f6645afe88e4 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -18,8 +18,8 @@ #include #include #include -#include -#include +#include "jffs2_fs_sb.h" +#include "jffs2_fs_i.h" #include "summary.h" #ifdef __ECOS diff --git a/include/linux/jffs2_fs_i.h b/include/linux/jffs2_fs_i.h deleted file mode 100644 index ad565bf9dcc1..000000000000 --- a/include/linux/jffs2_fs_i.h +++ /dev/null @@ -1,50 +0,0 @@ -/* $Id: jffs2_fs_i.h,v 1.19 2005/11/07 11:14:52 gleixner Exp $ */ - -#ifndef _JFFS2_FS_I -#define _JFFS2_FS_I - -#include -#include -#include - -struct jffs2_inode_info { - /* We need an internal mutex similar to inode->i_mutex. - Unfortunately, we can't used the existing one, because - either the GC would deadlock, or we'd have to release it - before letting GC proceed. Or we'd have to put ugliness - into the GC code so it didn't attempt to obtain the i_mutex - for the inode(s) which are already locked */ - struct semaphore sem; - - /* The highest (datanode) version number used for this ino */ - uint32_t highest_version; - - /* List of data fragments which make up the file */ - struct rb_root fragtree; - - /* There may be one datanode which isn't referenced by any of the - above fragments, if it contains a metadata update but no actual - data - or if this is a directory inode */ - /* This also holds the _only_ dnode for symlinks/device nodes, - etc. */ - struct jffs2_full_dnode *metadata; - - /* Directory entries */ - struct jffs2_full_dirent *dents; - - /* The target path if this is the inode of a symlink */ - unsigned char *target; - - /* Some stuff we just have to keep in-core at all times, for each inode. */ - struct jffs2_inode_cache *inocache; - - uint16_t flags; - uint8_t usercompr; -#if !defined (__ECOS) -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) - struct inode vfs_inode; -#endif -#endif -}; - -#endif /* _JFFS2_FS_I */ diff --git a/include/linux/jffs2_fs_sb.h b/include/linux/jffs2_fs_sb.h deleted file mode 100644 index 4bcfb5570221..000000000000 --- a/include/linux/jffs2_fs_sb.h +++ /dev/null @@ -1,122 +0,0 @@ -/* $Id: jffs2_fs_sb.h,v 1.54 2005/09/21 13:37:34 dedekind Exp $ */ - -#ifndef _JFFS2_FS_SB -#define _JFFS2_FS_SB - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define JFFS2_SB_FLAG_RO 1 -#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */ -#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */ - -struct jffs2_inodirty; - -/* A struct for the overall file system control. Pointers to - jffs2_sb_info structs are named `c' in the source code. - Nee jffs_control -*/ -struct jffs2_sb_info { - struct mtd_info *mtd; - - uint32_t highest_ino; - uint32_t checked_ino; - - unsigned int flags; - - struct task_struct *gc_task; /* GC task struct */ - struct completion gc_thread_start; /* GC thread start completion */ - struct completion gc_thread_exit; /* GC thread exit completion port */ - - struct semaphore alloc_sem; /* Used to protect all the following - fields, and also to protect against - out-of-order writing of nodes. And GC. */ - uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER - (i.e. zero for OOB CLEANMARKER */ - - uint32_t flash_size; - uint32_t used_size; - uint32_t dirty_size; - uint32_t wasted_size; - uint32_t free_size; - uint32_t erasing_size; - uint32_t bad_size; - uint32_t sector_size; - uint32_t unchecked_size; - - uint32_t nr_free_blocks; - uint32_t nr_erasing_blocks; - - /* Number of free blocks there must be before we... */ - uint8_t resv_blocks_write; /* ... allow a normal filesystem write */ - uint8_t resv_blocks_deletion; /* ... allow a normal filesystem deletion */ - uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */ - uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */ - uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */ - - uint32_t nospc_dirty_size; - - uint32_t nr_blocks; - struct jffs2_eraseblock *blocks; /* The whole array of blocks. Used for getting blocks - * from the offset (blocks[ofs / sector_size]) */ - struct jffs2_eraseblock *nextblock; /* The block we're currently filling */ - - struct jffs2_eraseblock *gcblock; /* The block we're currently garbage-collecting */ - - struct list_head clean_list; /* Blocks 100% full of clean data */ - struct list_head very_dirty_list; /* Blocks with lots of dirty space */ - struct list_head dirty_list; /* Blocks with some dirty space */ - struct list_head erasable_list; /* Blocks which are completely dirty, and need erasing */ - struct list_head erasable_pending_wbuf_list; /* Blocks which need erasing but only after the current wbuf is flushed */ - struct list_head erasing_list; /* Blocks which are currently erasing */ - struct list_head erase_pending_list; /* Blocks which need erasing now */ - struct list_head erase_complete_list; /* Blocks which are erased and need the clean marker written to them */ - struct list_head free_list; /* Blocks which are free and ready to be used */ - struct list_head bad_list; /* Bad blocks. */ - struct list_head bad_used_list; /* Bad blocks with valid data in. */ - - spinlock_t erase_completion_lock; /* Protect free_list and erasing_list - against erase completion handler */ - wait_queue_head_t erase_wait; /* For waiting for erases to complete */ - - wait_queue_head_t inocache_wq; - struct jffs2_inode_cache **inocache_list; - spinlock_t inocache_lock; - - /* Sem to allow jffs2_garbage_collect_deletion_dirent to - drop the erase_completion_lock while it's holding a pointer - to an obsoleted node. I don't like this. Alternatives welcomed. */ - struct semaphore erase_free_sem; - - uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ - -#ifdef CONFIG_JFFS2_FS_WRITEBUFFER - /* Write-behind buffer for NAND flash */ - unsigned char *wbuf; - uint32_t wbuf_ofs; - uint32_t wbuf_len; - struct jffs2_inodirty *wbuf_inodes; - - struct rw_semaphore wbuf_sem; /* Protects the write buffer */ - - /* Information about out-of-band area usage... */ - struct nand_oobinfo *oobinfo; - uint32_t badblock_pos; - uint32_t fsdata_pos; - uint32_t fsdata_len; -#endif - - struct jffs2_summary *summary; /* Summary information */ - - /* OS-private pointer for getting back to master superblock info */ - void *os_priv; -}; - -#endif /* _JFFS2_FB_SB */ -- cgit v1.2.3 From 73566edf9b91dd085ddb12033d0ea7288979dd10 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Sun, 7 May 2006 17:16:36 +0100 Subject: [MTD] Convert physmap to platform driver After dwmw2 let me know it ought to be done, I rewrote the physmap map driver to be a platform driver. I know zilch about the driver model, so I probably botched it in some way, but I've done some tests on an ixp23xx board which uses physmap, and it all seems to work. In order to not break existing physmap users, I've added some compat code that will instantiate a platform device iff CONFIG_MTD_PHYSMAP_LEN is defined and != 0. Also, I've changed the default value for CONFIG_MTD_PHYSMAP_LEN to zero, so that people who inadvertently compile in physmap (or new, platform-style, users of physmap) don't get burned. This works pretty well -- the new physmap driver is a drop-in replacement for the old one, and works on said ixp23xx board without any code changes needed. (This should hold as long as users don't touch 'physmap_map' directly.) Once all physmap users have been converted to instantiate their own platform devices, the compat code can go. (Or we decide that we can change all the in-tree users at the same time, and never merge the compat code.) Signed-off-by: Lennert Buytenhek Signed-off-by: David Woodhouse --- drivers/mtd/maps/Kconfig | 2 +- drivers/mtd/maps/physmap.c | 256 ++++++++++++++++++++++++++++++++------------ include/linux/mtd/physmap.h | 21 ++-- 3 files changed, 196 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 80d6810e88ed..b2becd7d4337 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -37,7 +37,7 @@ config MTD_PHYSMAP_START config MTD_PHYSMAP_LEN hex "Physical length of flash mapping" depends on MTD_PHYSMAP - default "0x4000000" + default "0" help This is the total length of the mapping of the flash chips on your particular board. If there is space, or aliases, in the diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index f49ebc3c4606..76ce9bd943aa 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -14,112 +14,230 @@ #include #include #include -#include +#include +#include #include #include #include #include #include +#include +#include -static struct mtd_info *mymtd; - -struct map_info physmap_map = { - .name = "phys_mapped_flash", - .phys = CONFIG_MTD_PHYSMAP_START, - .size = CONFIG_MTD_PHYSMAP_LEN, - .bankwidth = CONFIG_MTD_PHYSMAP_BANKWIDTH, +struct physmap_flash_info { + struct mtd_info *mtd; + struct map_info map; + struct resource *res; +#ifdef CONFIG_MTD_PARTITIONS + int nr_parts; + struct mtd_partition *parts; +#endif }; + +static int physmap_flash_remove(struct platform_device *dev) +{ + struct physmap_flash_info *info; + struct physmap_flash_data *physmap_data; + + info = platform_get_drvdata(dev); + if (info == NULL) + return 0; + platform_set_drvdata(dev, NULL); + + physmap_data = dev->dev.platform_data; + + if (info->mtd != NULL) { #ifdef CONFIG_MTD_PARTITIONS -static struct mtd_partition *mtd_parts; -static int mtd_parts_nb; + if (info->nr_parts) { + del_mtd_partitions(info->mtd); + kfree(info->parts); + } else if (physmap_data->nr_parts) { + del_mtd_partitions(info->mtd); + } else { + del_mtd_device(info->mtd); + } +#else + del_mtd_device(info->mtd); +#endif + map_destroy(info->mtd); + } -static int num_physmap_partitions; -static struct mtd_partition *physmap_partitions; + if (info->map.virt != NULL) + iounmap((void *)info->map.virt); -static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL}; + if (info->res != NULL) { + release_resource(info->res); + kfree(info->res); + } -void physmap_set_partitions(struct mtd_partition *parts, int num_parts) -{ - physmap_partitions=parts; - num_physmap_partitions=num_parts; + return 0; } -#endif /* CONFIG_MTD_PARTITIONS */ -static int __init init_physmap(void) +static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; +#endif + +static int physmap_flash_probe(struct platform_device *dev) { - static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; - const char **type; + struct physmap_flash_data *physmap_data; + struct physmap_flash_info *info; + const char **probe_type; + int err; + + physmap_data = dev->dev.platform_data; + if (physmap_data == NULL) + return -ENODEV; + + printk(KERN_NOTICE "physmap platform flash device: %.8lx at %.8lx\n", + dev->resource->end - dev->resource->start + 1, + dev->resource->start); + + info = kmalloc(sizeof(struct physmap_flash_info), GFP_KERNEL); + if (info == NULL) { + err = -ENOMEM; + goto err_out; + } + memset(info, 0, sizeof(*info)); - printk(KERN_NOTICE "physmap flash device: %lx at %lx\n", physmap_map.size, physmap_map.phys); - physmap_map.virt = ioremap(physmap_map.phys, physmap_map.size); + platform_set_drvdata(dev, info); - if (!physmap_map.virt) { - printk("Failed to ioremap\n"); - return -EIO; + info->res = request_mem_region(dev->resource->start, + dev->resource->end - dev->resource->start + 1, + dev->dev.bus_id); + if (info->res == NULL) { + dev_err(&dev->dev, "Could not reserve memory region\n"); + err = -ENOMEM; + goto err_out; } - simple_map_init(&physmap_map); + info->map.name = dev->dev.bus_id; + info->map.phys = dev->resource->start; + info->map.size = dev->resource->end - dev->resource->start + 1; + info->map.bankwidth = physmap_data->width; + info->map.set_vpp = physmap_data->set_vpp; + + info->map.virt = ioremap(info->map.phys, info->map.size); + if (info->map.virt == NULL) { + dev_err(&dev->dev, "Failed to ioremap flash region\n"); + err = EIO; + goto err_out; + } - mymtd = NULL; - type = rom_probe_types; - for(; !mymtd && *type; type++) { - mymtd = do_map_probe(*type, &physmap_map); + simple_map_init(&info->map); + + probe_type = rom_probe_types; + for (; info->mtd == NULL && *probe_type != NULL; probe_type++) + info->mtd = do_map_probe(*probe_type, &info->map); + if (info->mtd == NULL) { + dev_err(&dev->dev, "map_probe failed\n"); + err = -ENXIO; + goto err_out; } - if (mymtd) { - mymtd->owner = THIS_MODULE; + info->mtd->owner = THIS_MODULE; #ifdef CONFIG_MTD_PARTITIONS - mtd_parts_nb = parse_mtd_partitions(mymtd, part_probes, - &mtd_parts, 0); + err = parse_mtd_partitions(info->mtd, part_probe_types, &info->parts, 0); + if (err > 0) { + add_mtd_partitions(info->mtd, info->parts, err); + return 0; + } - if (mtd_parts_nb > 0) - { - add_mtd_partitions (mymtd, mtd_parts, mtd_parts_nb); - return 0; - } + if (physmap_data->nr_parts) { + printk(KERN_NOTICE "Using physmap partition information\n"); + add_mtd_partitions(info->mtd, physmap_data->parts, + physmap_data->nr_parts); + return 0; + } +#endif + + add_mtd_device(info->mtd); + return 0; + +err_out: + physmap_flash_remove(dev); + return err; +} + +static struct platform_driver physmap_flash_driver = { + .probe = physmap_flash_probe, + .remove = physmap_flash_remove, + .driver = { + .name = "physmap-flash", + }, +}; - if (num_physmap_partitions != 0) - { - printk(KERN_NOTICE - "Using physmap partition definition\n"); - add_mtd_partitions (mymtd, physmap_partitions, num_physmap_partitions); - return 0; - } +#ifdef CONFIG_MTD_PHYSMAP_LEN +#if CONFIG_MTD_PHYSMAP_LEN != 0 +#warning using PHYSMAP compat code +#define PHYSMAP_COMPAT +#endif #endif - add_mtd_device(mymtd); - return 0; - } +#ifdef PHYSMAP_COMPAT +static struct physmap_flash_data physmap_flash_data = { + .width = CONFIG_MTD_PHYSMAP_BANKWIDTH, +}; - iounmap(physmap_map.virt); - return -ENXIO; -} +static struct resource physmap_flash_resource = { + .start = CONFIG_MTD_PHYSMAP_START, + .end = CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN, + .flags = IORESOURCE_MEM, +}; -static void __exit cleanup_physmap(void) +static struct platform_device physmap_flash = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &physmap_flash_data, + }, + .num_resources = 1, + .resource = &physmap_flash_resource, +}; + +void physmap_configure(unsigned long addr, unsigned long size, + int bankwidth, void (*set_vpp)(struct map_info *, int)) { + physmap_flash_resource.start = addr; + physmap_flash_resource.end = addr + size - 1; + physmap_flash_data.width = bankwidth; + physmap_flash_data.set_vpp = set_vpp; +} + #ifdef CONFIG_MTD_PARTITIONS - if (mtd_parts_nb) { - del_mtd_partitions(mymtd); - kfree(mtd_parts); - } else if (num_physmap_partitions) { - del_mtd_partitions(mymtd); - } else { - del_mtd_device(mymtd); - } -#else - del_mtd_device(mymtd); +void physmap_set_partitions(struct mtd_partition *parts, int num_parts) +{ + physmap_flash_data.nr_parts = num_parts; + physmap_flash_data.parts = parts; +} +#endif #endif - map_destroy(mymtd); - iounmap(physmap_map.virt); - physmap_map.virt = NULL; +static int __init physmap_init(void) +{ + int err; + + err = platform_driver_register(&physmap_flash_driver); +#ifdef PHYSMAP_COMPAT + if (err == 0) + platform_device_register(&physmap_flash); +#endif + + return err; } -module_init(init_physmap); -module_exit(cleanup_physmap); +static void __exit physmap_exit(void) +{ +#ifdef PHYSMAP_COMPAT + platform_device_unregister(&physmap_flash); +#endif + platform_driver_unregister(&physmap_flash_driver); +} +module_init(physmap_init); +module_exit(physmap_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse "); diff --git a/include/linux/mtd/physmap.h b/include/linux/mtd/physmap.h index c7b8bcdef013..50f954461aa8 100644 --- a/include/linux/mtd/physmap.h +++ b/include/linux/mtd/physmap.h @@ -24,22 +24,18 @@ #include #include -/* - * The map_info for physmap. Board can override size, buswidth, phys, - * (*set_vpp)(), etc in their initial setup routine. - */ -extern struct map_info physmap_map; +struct physmap_flash_data { + unsigned int width; + void (*set_vpp)(struct map_info *, int); + unsigned int nr_parts; + struct mtd_partition *parts; +}; /* * Board needs to specify the exact mapping during their setup time. */ -static inline void physmap_configure(unsigned long addr, unsigned long size, int bankwidth, void (*set_vpp)(struct map_info *, int) ) -{ - physmap_map.phys = addr; - physmap_map.size = size; - physmap_map.bankwidth = bankwidth; - physmap_map.set_vpp = set_vpp; -} +void physmap_configure(unsigned long addr, unsigned long size, + int bankwidth, void (*set_vpp)(struct map_info *, int) ); #if defined(CONFIG_MTD_PARTITIONS) @@ -58,4 +54,3 @@ void physmap_set_partitions(struct mtd_partition *parts, int num_parts); #endif /* defined(CONFIG_MTD) */ #endif /* __LINUX_MTD_PHYSMAP__ */ - -- cgit v1.2.3 From 6f18a022fb311f07f3b32f2c0e1b5c9477dc4439 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 8 May 2006 22:40:05 +0100 Subject: Finally remove the obnoxious inter_module_xxx() This was already a bad plan when I argued against adding it in the first place. Good riddance. Signed-off-by: David Woodhouse --- include/linux/module.h | 9 --- init/Kconfig | 3 - kernel/Makefile | 1 - kernel/intermodule.c | 184 ------------------------------------------------- 4 files changed, 197 deletions(-) delete mode 100644 kernel/intermodule.c (limited to 'include') diff --git a/include/linux/module.h b/include/linux/module.h index eaec13ddd667..b0d44134f3c4 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -557,13 +557,4 @@ static inline void module_remove_driver(struct device_driver *driver) #define __MODULE_STRING(x) __stringify(x) -/* Use symbol_get and symbol_put instead. You'll thank me. */ -#define HAVE_INTER_MODULE -extern void __deprecated inter_module_register(const char *, - struct module *, const void *); -extern void __deprecated inter_module_unregister(const char *); -extern const void * __deprecated inter_module_get_request(const char *, - const char *); -extern void __deprecated inter_module_put(const char *); - #endif /* _LINUX_MODULE_H */ diff --git a/init/Kconfig b/init/Kconfig index 3b36a1d53656..a7697787946a 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -389,9 +389,6 @@ config SLOB default !SLAB bool -config OBSOLETE_INTERMODULE - tristate - menu "Loadable module support" config MODULES diff --git a/kernel/Makefile b/kernel/Makefile index 58908f9d156a..f6ef00f4f90f 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -20,7 +20,6 @@ obj-$(CONFIG_SMP) += cpu.o spinlock.o obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += module.o -obj-$(CONFIG_OBSOLETE_INTERMODULE) += intermodule.o obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_PM) += power/ obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o diff --git a/kernel/intermodule.c b/kernel/intermodule.c deleted file mode 100644 index 55b1e5b85db9..000000000000 --- a/kernel/intermodule.c +++ /dev/null @@ -1,184 +0,0 @@ -/* Deprecated, do not use. Moved from module.c to here. --RR */ - -/* Written by Keith Owens Oct 2000 */ -#include -#include -#include -#include -#include - -/* inter_module functions are always available, even when the kernel is - * compiled without modules. Consumers of inter_module_xxx routines - * will always work, even when both are built into the kernel, this - * approach removes lots of #ifdefs in mainline code. - */ - -static struct list_head ime_list = LIST_HEAD_INIT(ime_list); -static DEFINE_SPINLOCK(ime_lock); -static int kmalloc_failed; - -struct inter_module_entry { - struct list_head list; - const char *im_name; - struct module *owner; - const void *userdata; -}; - -/** - * inter_module_register - register a new set of inter module data. - * @im_name: an arbitrary string to identify the data, must be unique - * @owner: module that is registering the data, always use THIS_MODULE - * @userdata: pointer to arbitrary userdata to be registered - * - * Description: Check that the im_name has not already been registered, - * complain if it has. For new data, add it to the inter_module_entry - * list. - */ -void inter_module_register(const char *im_name, struct module *owner, const void *userdata) -{ - struct list_head *tmp; - struct inter_module_entry *ime, *ime_new; - - if (!(ime_new = kzalloc(sizeof(*ime), GFP_KERNEL))) { - /* Overloaded kernel, not fatal */ - printk(KERN_ERR - "Aiee, inter_module_register: cannot kmalloc entry for '%s'\n", - im_name); - kmalloc_failed = 1; - return; - } - ime_new->im_name = im_name; - ime_new->owner = owner; - ime_new->userdata = userdata; - - spin_lock(&ime_lock); - list_for_each(tmp, &ime_list) { - ime = list_entry(tmp, struct inter_module_entry, list); - if (strcmp(ime->im_name, im_name) == 0) { - spin_unlock(&ime_lock); - kfree(ime_new); - /* Program logic error, fatal */ - printk(KERN_ERR "inter_module_register: duplicate im_name '%s'", im_name); - BUG(); - } - } - list_add(&(ime_new->list), &ime_list); - spin_unlock(&ime_lock); -} - -/** - * inter_module_unregister - unregister a set of inter module data. - * @im_name: an arbitrary string to identify the data, must be unique - * - * Description: Check that the im_name has been registered, complain if - * it has not. For existing data, remove it from the - * inter_module_entry list. - */ -void inter_module_unregister(const char *im_name) -{ - struct list_head *tmp; - struct inter_module_entry *ime; - - spin_lock(&ime_lock); - list_for_each(tmp, &ime_list) { - ime = list_entry(tmp, struct inter_module_entry, list); - if (strcmp(ime->im_name, im_name) == 0) { - list_del(&(ime->list)); - spin_unlock(&ime_lock); - kfree(ime); - return; - } - } - spin_unlock(&ime_lock); - if (kmalloc_failed) { - printk(KERN_ERR - "inter_module_unregister: no entry for '%s', " - "probably caused by previous kmalloc failure\n", - im_name); - return; - } - else { - /* Program logic error, fatal */ - printk(KERN_ERR "inter_module_unregister: no entry for '%s'", im_name); - BUG(); - } -} - -/** - * inter_module_get - return arbitrary userdata from another module. - * @im_name: an arbitrary string to identify the data, must be unique - * - * Description: If the im_name has not been registered, return NULL. - * Try to increment the use count on the owning module, if that fails - * then return NULL. Otherwise return the userdata. - */ -static const void *inter_module_get(const char *im_name) -{ - struct list_head *tmp; - struct inter_module_entry *ime; - const void *result = NULL; - - spin_lock(&ime_lock); - list_for_each(tmp, &ime_list) { - ime = list_entry(tmp, struct inter_module_entry, list); - if (strcmp(ime->im_name, im_name) == 0) { - if (try_module_get(ime->owner)) - result = ime->userdata; - break; - } - } - spin_unlock(&ime_lock); - return(result); -} - -/** - * inter_module_get_request - im get with automatic request_module. - * @im_name: an arbitrary string to identify the data, must be unique - * @modname: module that is expected to register im_name - * - * Description: If inter_module_get fails, do request_module then retry. - */ -const void *inter_module_get_request(const char *im_name, const char *modname) -{ - const void *result = inter_module_get(im_name); - if (!result) { - request_module("%s", modname); - result = inter_module_get(im_name); - } - return(result); -} - -/** - * inter_module_put - release use of data from another module. - * @im_name: an arbitrary string to identify the data, must be unique - * - * Description: If the im_name has not been registered, complain, - * otherwise decrement the use count on the owning module. - */ -void inter_module_put(const char *im_name) -{ - struct list_head *tmp; - struct inter_module_entry *ime; - - spin_lock(&ime_lock); - list_for_each(tmp, &ime_list) { - ime = list_entry(tmp, struct inter_module_entry, list); - if (strcmp(ime->im_name, im_name) == 0) { - if (ime->owner) - module_put(ime->owner); - spin_unlock(&ime_lock); - return; - } - } - spin_unlock(&ime_lock); - printk(KERN_ERR "inter_module_put: no entry for '%s'", im_name); - BUG(); -} - -EXPORT_SYMBOL(inter_module_register); -EXPORT_SYMBOL(inter_module_unregister); -EXPORT_SYMBOL(inter_module_get_request); -EXPORT_SYMBOL(inter_module_put); - -MODULE_LICENSE("GPL"); - -- cgit v1.2.3 From 9c01f87db183403a4f603fe5180c57b82b54b4a1 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Fri, 12 May 2006 17:02:31 +0300 Subject: OneNAND: handle byte access on BufferRAM Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/mtd/onenand.h | 3 +++ 2 files changed, 41 insertions(+) (limited to 'include') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index d6c13f7ae5a1..1439c9fa1d23 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -373,6 +373,17 @@ static int onenand_read_bufferram(struct mtd_info *mtd, int area, bufferram += onenand_bufferram_offset(mtd, area); + if (ONENAND_CHECK_BYTE_ACCESS(count)) { + unsigned short word; + + /* Align with word(16-bit) size */ + count--; + + /* Read word and save byte */ + word = this->read_word(bufferram + offset + count); + buffer[count] = (word & 0xff); + } + memcpy(buffer, bufferram + offset, count); return 0; @@ -400,6 +411,17 @@ static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area, this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ); + if (ONENAND_CHECK_BYTE_ACCESS(count)) { + unsigned short word; + + /* Align with word(16-bit) size */ + count--; + + /* Read word and save byte */ + word = this->read_word(bufferram + offset + count); + buffer[count] = (word & 0xff); + } + memcpy(buffer, bufferram + offset, count); this->mmcontrol(mtd, 0); @@ -427,6 +449,22 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area, bufferram += onenand_bufferram_offset(mtd, area); + if (ONENAND_CHECK_BYTE_ACCESS(count)) { + unsigned short word; + int byte_offset; + + /* Align with word(16-bit) size */ + count--; + + /* Calculate byte access offset */ + byte_offset = offset + count; + + /* Read word and save byte */ + word = this->read_word(bufferram + byte_offset); + word = (word & ~0xff) | buffer[count]; + this->write_word(word, bufferram + byte_offset); + } + memcpy(bufferram + offset, buffer, count); return 0; diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 7419b5fab133..22322c8a7729 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -130,6 +130,9 @@ struct onenand_chip { #define ONENAND_SET_SYS_CFG1(v, this) \ (this->write_word(v, this->base + ONENAND_REG_SYS_CFG1)) +/* Check byte access in OneNAND */ +#define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1) + /* * Options bits */ -- cgit v1.2.3 From 493c646077ef0b8668ed71b8057f81cb7454af87 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Fri, 12 May 2006 17:03:07 +0300 Subject: OneNAND: One-Time Programmable (OTP) support One Block of the NAND Flash Array memory is reserved as a One-Time Programmable Block memory area. Also, 1st Block of NAND Flash Array can be used as OTP. The OTP block can be read, programmed and locked using the same operations as any other NAND Flash Array memory block. OTP block cannot be erased. OTP block is fully-guaranteed to be a valid block. Signed-off-by: Kyungmin Park --- drivers/mtd/mtdchar.c | 2 +- drivers/mtd/onenand/Kconfig | 14 ++ drivers/mtd/onenand/onenand_base.c | 313 ++++++++++++++++++++++++++++++++++++- include/linux/mtd/onenand.h | 2 + include/linux/mtd/onenand_regs.h | 8 + 5 files changed, 335 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 6b83aee8abb8..7a7df851c993 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -552,7 +552,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, break; } -#ifdef CONFIG_MTD_OTP +#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP) case OTPSELECT: { int mode; diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 126ff6bf63d5..5930a03736d7 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -29,6 +29,20 @@ config MTD_ONENAND_GENERIC help Support for OneNAND flash via platform device driver. +config MTD_ONENAND_OTP + bool "OneNAND OTP Support" + depends on MTD_ONENAND + help + One Block of the NAND Flash Array memory is reserved as + a One-Time Programmable Block memory area. + Also, 1st Block of NAND Flash Array can be used as OTP. + + The OTP block can be read, programmed and locked using the same + operations as any other NAND Flash Array memory block. + OTP block cannot be erased. + + OTP block is fully-guaranteed to be a valid block. + config MTD_ONENAND_SYNC_READ bool "OneNAND Sync. Burst Read Support" depends on ARCH_OMAP diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 7c7dc0ae5a19..163c81135447 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -191,7 +191,7 @@ static int onenand_buffer_address(int dataram1, int sectors, int count) static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len) { struct onenand_chip *this = mtd->priv; - int value, readcmd = 0; + int value, readcmd = 0, block_cmd = 0; int block, page; /* Now we use page size operation */ int sectors = 4, count = 4; @@ -207,6 +207,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le case ONENAND_CMD_ERASE: case ONENAND_CMD_BUFFERRAM: + case ONENAND_CMD_OTP_ACCESS: + block_cmd = 1; block = (int) (addr >> this->erase_shift); page = -1; break; @@ -235,7 +237,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le value = onenand_block_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); - if (cmd == ONENAND_CMD_ERASE) { + if (cmd == block_cmd) { /* Select DataRAM for DDP */ value = onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); @@ -1412,6 +1414,304 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) return 0; } +#ifdef CONFIG_MTD_ONENAND_OTP + +/* Interal OTP operation */ +typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len, + size_t *retlen, u_char *buf); + +/** + * do_otp_read - [DEFAULT] Read OTP block area + * @param mtd MTD device structure + * @param from The offset to read + * @param len number of bytes to read + * @param retlen pointer to variable to store the number of readbytes + * @param buf the databuffer to put/get data + * + * Read OTP block area. + */ +static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct onenand_chip *this = mtd->priv; + int ret; + + /* Enter OTP access mode */ + this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); + this->wait(mtd, FL_OTPING); + + ret = mtd->read(mtd, from, len, retlen, buf); + + /* Exit OTP access mode */ + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + this->wait(mtd, FL_RESETING); + + return ret; +} + +/** + * do_otp_write - [DEFAULT] Write OTP block area + * @param mtd MTD device structure + * @param from The offset to write + * @param len number of bytes to write + * @param retlen pointer to variable to store the number of write bytes + * @param buf the databuffer to put/get data + * + * Write OTP block area. + */ +static int do_otp_write(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct onenand_chip *this = mtd->priv; + unsigned char *pbuf = buf; + int ret; + + /* Force buffer page aligned */ + if (len < mtd->oobblock) { + memcpy(this->page_buf, buf, len); + memset(this->page_buf + len, 0xff, mtd->oobblock - len); + pbuf = this->page_buf; + len = mtd->oobblock; + } + + /* Enter OTP access mode */ + this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); + this->wait(mtd, FL_OTPING); + + ret = mtd->write(mtd, from, len, retlen, pbuf); + + /* Exit OTP access mode */ + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + this->wait(mtd, FL_RESETING); + + return ret; +} + +/** + * do_otp_lock - [DEFAULT] Lock OTP block area + * @param mtd MTD device structure + * @param from The offset to lock + * @param len number of bytes to lock + * @param retlen pointer to variable to store the number of lock bytes + * @param buf the databuffer to put/get data + * + * Lock OTP block area. + */ +static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct onenand_chip *this = mtd->priv; + int ret; + + /* Enter OTP access mode */ + this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); + this->wait(mtd, FL_OTPING); + + ret = mtd->write_oob(mtd, from, len, retlen, buf); + + /* Exit OTP access mode */ + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + this->wait(mtd, FL_RESETING); + + return ret; +} + +/** + * onenand_otp_walk - [DEFAULT] Handle OTP operation + * @param mtd MTD device structure + * @param from The offset to read/write + * @param len number of bytes to read/write + * @param retlen pointer to variable to store the number of read bytes + * @param buf the databuffer to put/get data + * @param action do given action + * @param mode specify user and factory + * + * Handle OTP operation. + */ +static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf, + otp_op_t action, int mode) +{ + struct onenand_chip *this = mtd->priv; + int otp_pages; + int density; + int ret = 0; + + *retlen = 0; + + density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT; + if (density < ONENAND_DEVICE_DENSITY_512Mb) + otp_pages = 20; + else + otp_pages = 10; + + if (mode == MTD_OTP_FACTORY) { + from += mtd->oobblock * otp_pages; + otp_pages = 64 - otp_pages; + } + + /* Check User/Factory boundary */ + if (((mtd->oobblock * otp_pages) - (from + len)) < 0) + return 0; + + while (len > 0 && otp_pages > 0) { + if (!action) { /* OTP Info functions */ + struct otp_info *otpinfo; + + len -= sizeof(struct otp_info); + if (len <= 0) + return -ENOSPC; + + otpinfo = (struct otp_info *) buf; + otpinfo->start = from; + otpinfo->length = mtd->oobblock; + otpinfo->locked = 0; + + from += mtd->oobblock; + buf += sizeof(struct otp_info); + *retlen += sizeof(struct otp_info); + } else { + size_t tmp_retlen; + int size = len; + + ret = action(mtd, from, len, &tmp_retlen, buf); + + buf += size; + len -= size; + *retlen += size; + + if (ret < 0) + return ret; + } + otp_pages--; + } + + return 0; +} + +/** + * onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info + * @param mtd MTD device structure + * @param buf the databuffer to put/get data + * @param len number of bytes to read + * + * Read factory OTP info. + */ +static int onenand_get_fact_prot_info(struct mtd_info *mtd, + struct otp_info *buf, size_t len) +{ + size_t retlen; + int ret; + + ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_FACTORY); + + return ret ? : retlen; +} + +/** + * onenand_read_fact_prot_reg - [MTD Interface] Read factory OTP area + * @param mtd MTD device structure + * @param from The offset to read + * @param len number of bytes to read + * @param retlen pointer to variable to store the number of read bytes + * @param buf the databuffer to put/get data + * + * Read factory OTP area. + */ +static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_FACTORY); +} + +/** + * onenand_get_user_prot_info - [MTD Interface] Read user OTP info + * @param mtd MTD device structure + * @param buf the databuffer to put/get data + * @param len number of bytes to read + * + * Read user OTP info. + */ +static int onenand_get_user_prot_info(struct mtd_info *mtd, + struct otp_info *buf, size_t len) +{ + size_t retlen; + int ret; + + ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_USER); + + return ret ? : retlen; +} + +/** + * onenand_read_user_prot_reg - [MTD Interface] Read user OTP area + * @param mtd MTD device structure + * @param from The offset to read + * @param len number of bytes to read + * @param retlen pointer to variable to store the number of read bytes + * @param buf the databuffer to put/get data + * + * Read user OTP area. + */ +static int onenand_read_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_USER); +} + +/** + * onenand_write_user_prot_reg - [MTD Interface] Write user OTP area + * @param mtd MTD device structure + * @param from The offset to write + * @param len number of bytes to write + * @param retlen pointer to variable to store the number of write bytes + * @param buf the databuffer to put/get data + * + * Write user OTP area. + */ +static int onenand_write_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_write, MTD_OTP_USER); +} + +/** + * onenand_lock_user_prot_reg - [MTD Interface] Lock user OTP area + * @param mtd MTD device structure + * @param from The offset to lock + * @param len number of bytes to unlock + * + * Write lock mark on spare area in page 0 in OTP block + */ +static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len) +{ + unsigned char oob_buf[64]; + size_t retlen; + int ret; + + memset(oob_buf, 0xff, mtd->oobsize); + /* + * Note: OTP lock operation + * OTP block : 0xXXFC + * 1st block : 0xXXF3 (If chip support) + * Both : 0xXXF0 (If chip support) + */ + oob_buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC; + + /* + * Write lock mark to 8th word of sector0 of page0 of the spare0. + * We write 16 bytes spare area instead of 2 bytes. + */ + from = 0; + len = 16; + + ret = onenand_otp_walk(mtd, from, len, &retlen, oob_buf, do_otp_lock, MTD_OTP_USER); + + return ret ? : retlen; +} +#endif /* CONFIG_MTD_ONENAND_OTP */ + /** * onenand_print_device_info - Print device ID * @param device device ID @@ -1563,7 +1863,6 @@ static void onenand_resume(struct mtd_info *mtd) "in suspended state\n"); } - /** * onenand_scan - [OneNAND Interface] Scan for the OneNAND device * @param mtd MTD device structure @@ -1655,6 +1954,14 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) mtd->write_ecc = onenand_write_ecc; mtd->read_oob = onenand_read_oob; mtd->write_oob = onenand_write_oob; +#ifdef CONFIG_MTD_ONENAND_OTP + mtd->get_fact_prot_info = onenand_get_fact_prot_info; + mtd->read_fact_prot_reg = onenand_read_fact_prot_reg; + mtd->get_user_prot_info = onenand_get_user_prot_info; + mtd->read_user_prot_reg = onenand_read_user_prot_reg; + mtd->write_user_prot_reg = onenand_write_user_prot_reg; + mtd->lock_user_prot_reg = onenand_lock_user_prot_reg; +#endif mtd->readv = NULL; mtd->readv_ecc = NULL; mtd->writev = onenand_writev; diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 22322c8a7729..3f5919f2e9da 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -35,6 +35,8 @@ typedef enum { FL_SYNCING, FL_UNLOCKING, FL_LOCKING, + FL_RESETING, + FL_OTPING, FL_PM_SUSPENDED, } onenand_state_t; diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index d7832ef8ed63..4a72818d2545 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -112,6 +112,7 @@ #define ONENAND_CMD_LOCK_TIGHT (0x2C) #define ONENAND_CMD_ERASE (0x94) #define ONENAND_CMD_RESET (0xF0) +#define ONENAND_CMD_OTP_ACCESS (0x65) #define ONENAND_CMD_READID (0x90) /* NOTE: Those are not *REAL* commands */ @@ -152,6 +153,8 @@ #define ONENAND_CTRL_ERASE (1 << 11) #define ONENAND_CTRL_ERROR (1 << 10) #define ONENAND_CTRL_RSTB (1 << 7) +#define ONENAND_CTRL_OTP_L (1 << 6) +#define ONENAND_CTRL_OTP_BL (1 << 5) /* * Interrupt Status Register F241h (R) @@ -177,4 +180,9 @@ #define ONENAND_ECC_2BIT (1 << 1) #define ONENAND_ECC_2BIT_ALL (0xAAAA) +/* + * One-Time Programmable (OTP) + */ +#define ONENAND_OTP_LOCK_OFFSET (14) + #endif /* __ONENAND_REG_H */ -- cgit v1.2.3 From aa98d7cf59b5b0764d3502662053489585faf2fe Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Sat, 13 May 2006 15:09:47 +0900 Subject: [JFFS2][XATTR] XATTR support on JFFS2 (version. 5) This attached patches provide xattr support including POSIX-ACL and SELinux support on JFFS2 (version.5). There are some significant differences from previous version posted at last December. The biggest change is addition of EBS(Erase Block Summary) support. Currently, both kernel and usermode utility (sumtool) can recognize xattr nodes which have JFFS2_NODETYPE_XATTR/_XREF nodetype. In addition, some bugs are fixed. - A potential race condition was fixed. - Unexpected fail when updating a xattr by same name/value pair was fixed. - A bug when removing xattr name/value pair was fixed. The fundamental structures (such as using two new nodetypes and exclusion mechanism by rwsem) are unchanged. But most of implementation were reviewed and updated if necessary. Espacially, we had to change several internal implementations related to load_xattr_datum() to avoid a potential race condition. [1/2] xattr_on_jffs2.kernel.version-5.patch [2/2] xattr_on_jffs2.utils.version-5.patch Signed-off-by: KaiGai Kohei Signed-off-by: David Woodhouse --- fs/Kconfig | 38 ++ fs/jffs2/Makefile | 3 + fs/jffs2/acl.c | 483 ++++++++++++++++++ fs/jffs2/acl.h | 46 ++ fs/jffs2/build.c | 2 + fs/jffs2/debug.h | 6 + fs/jffs2/dir.c | 62 ++- fs/jffs2/file.c | 7 +- fs/jffs2/fs.c | 11 +- fs/jffs2/gc.c | 16 +- fs/jffs2/jffs2_fs_i.h | 5 + fs/jffs2/jffs2_fs_sb.h | 10 + fs/jffs2/malloc.c | 68 ++- fs/jffs2/nodelist.c | 1 + fs/jffs2/nodelist.h | 21 +- fs/jffs2/os-linux.h | 4 + fs/jffs2/readinode.c | 1 + fs/jffs2/scan.c | 168 ++++++ fs/jffs2/security.c | 82 +++ fs/jffs2/summary.c | 191 +++++++ fs/jffs2/summary.h | 42 ++ fs/jffs2/super.c | 6 +- fs/jffs2/symlink.c | 7 +- fs/jffs2/write.c | 2 +- fs/jffs2/xattr.c | 1271 ++++++++++++++++++++++++++++++++++++++++++++++ fs/jffs2/xattr.h | 120 +++++ fs/jffs2/xattr_trusted.c | 51 ++ fs/jffs2/xattr_user.c | 51 ++ include/linux/jffs2.h | 40 ++ 29 files changed, 2800 insertions(+), 15 deletions(-) create mode 100644 fs/jffs2/acl.c create mode 100644 fs/jffs2/acl.h create mode 100644 fs/jffs2/security.c create mode 100644 fs/jffs2/xattr.c create mode 100644 fs/jffs2/xattr.h create mode 100644 fs/jffs2/xattr_trusted.c create mode 100644 fs/jffs2/xattr_user.c (limited to 'include') diff --git a/fs/Kconfig b/fs/Kconfig index f9b5842c8d2d..2496ccbe2604 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -1075,6 +1075,44 @@ config JFFS2_FS_DEBUG If reporting bugs, please try to have available a full dump of the messages at debug level 1 while the misbehaviour was occurring. +config JFFS2_FS_XATTR + bool "JFFS2 XATTR support" + depends on JFFS2_FS + default n + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page, or visit + for details). + + If unsure, say N. + +config JFFS2_FS_POSIX_ACL + bool "JFFS2 POSIX Access Control Lists" + depends on JFFS2_FS_XATTR + default y + select FS_POSIX_ACL + help + Posix Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website . + + If you don't know what Access Control Lists are, say N + +config JFFS2_FS_SECURITY + bool "JFFS2 Security Labels" + depends on JFFS2_FS_XATTR + default y + help + Security labels support alternative access control models + implemented by security modules like SELinux. This option + enables an extended attribute handler for file security + labels in the jffs2 filesystem. + + If you are not using a security module that requires using + extended attributes for file security labels, say N. + config JFFS2_FS_WRITEBUFFER bool "JFFS2 write-buffering support" depends on JFFS2_FS diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile index 77dc5561a04e..7f28ee0bd132 100644 --- a/fs/jffs2/Makefile +++ b/fs/jffs2/Makefile @@ -12,6 +12,9 @@ jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o jffs2-y += super.o debug.o jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o +jffs2-$(CONFIG_JFFS2_FS_XATTR) += xattr.o xattr_trusted.o xattr_user.o +jffs2-$(CONFIG_JFFS2_FS_SECURITY) += security.o +jffs2-$(CONFIG_JFFS2_FS_POSIX_ACL) += acl.o jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c new file mode 100644 index 000000000000..080bb51e4b65 --- /dev/null +++ b/fs/jffs2/acl.c @@ -0,0 +1,483 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/acl.c + * POSIX ACL support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei + * Copyright (C) 2006 NEC Corporation + * + * For licensing information, see the file 'LICENCE' in the jffs2 directory. + *-------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nodelist.h" + +static size_t jffs2_acl_size(int count) +{ + if (count <= 4) { + return sizeof(jffs2_acl_header) + + count * sizeof(jffs2_acl_entry_short); + } else { + return sizeof(jffs2_acl_header) + + 4 * sizeof(jffs2_acl_entry_short) + + (count - 4) * sizeof(jffs2_acl_entry); + } +} + +static int jffs2_acl_count(size_t size) +{ + size_t s; + + size -= sizeof(jffs2_acl_header); + s = size - 4 * sizeof(jffs2_acl_entry_short); + if (s < 0) { + if (size % sizeof(jffs2_acl_entry_short)) + return -1; + return size / sizeof(jffs2_acl_entry_short); + } else { + if (s % sizeof(jffs2_acl_entry)) + return -1; + return s / sizeof(jffs2_acl_entry) + 4; + } +} + +static struct posix_acl *jffs2_acl_from_medium(const void *value, size_t size) +{ + const char *end = (char *)value + size; + struct posix_acl *acl; + uint32_t ver; + int i, count; + + if (!value) + return NULL; + if (size < sizeof(jffs2_acl_header)) + return ERR_PTR(-EINVAL); + ver = je32_to_cpu(((jffs2_acl_header *)value)->a_version); + if (ver != JFFS2_ACL_VERSION) { + JFFS2_WARNING("Invalid ACL version. (=%u)\n", ver); + return ERR_PTR(-EINVAL); + } + + value = (char *)value + sizeof(jffs2_acl_header); + count = jffs2_acl_count(size); + if (count < 0) + return ERR_PTR(-EINVAL); + if (count == 0) + return NULL; + + acl = posix_acl_alloc(count, GFP_KERNEL); + if (!acl) + return ERR_PTR(-ENOMEM); + + for (i=0; i < count; i++) { + jffs2_acl_entry *entry = (jffs2_acl_entry *)value; + if ((char *)value + sizeof(jffs2_acl_entry_short) > end) + goto fail; + acl->a_entries[i].e_tag = je16_to_cpu(entry->e_tag); + acl->a_entries[i].e_perm = je16_to_cpu(entry->e_perm); + switch (acl->a_entries[i].e_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + value = (char *)value + sizeof(jffs2_acl_entry_short); + acl->a_entries[i].e_id = ACL_UNDEFINED_ID; + break; + + case ACL_USER: + case ACL_GROUP: + value = (char *)value + sizeof(jffs2_acl_entry); + if ((char *)value > end) + goto fail; + acl->a_entries[i].e_id = je32_to_cpu(entry->e_id); + break; + + default: + goto fail; + } + } + if (value != end) + goto fail; + return acl; + fail: + posix_acl_release(acl); + return ERR_PTR(-EINVAL); +} + +static void *jffs2_acl_to_medium(const struct posix_acl *acl, size_t *size) +{ + jffs2_acl_header *jffs2_acl; + char *e; + size_t i; + + *size = jffs2_acl_size(acl->a_count); + jffs2_acl = (jffs2_acl_header *)kmalloc(sizeof(jffs2_acl_header) + + acl->a_count * sizeof(jffs2_acl_entry), + GFP_KERNEL); + if (!jffs2_acl) + return ERR_PTR(-ENOMEM); + jffs2_acl->a_version = cpu_to_je32(JFFS2_ACL_VERSION); + e = (char *)jffs2_acl + sizeof(jffs2_acl_header); + for (i=0; i < acl->a_count; i++) { + jffs2_acl_entry *entry = (jffs2_acl_entry *)e; + entry->e_tag = cpu_to_je16(acl->a_entries[i].e_tag); + entry->e_perm = cpu_to_je16(acl->a_entries[i].e_perm); + switch(acl->a_entries[i].e_tag) { + case ACL_USER: + case ACL_GROUP: + entry->e_id = cpu_to_je32(acl->a_entries[i].e_id); + e += sizeof(jffs2_acl_entry); + break; + + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + e += sizeof(jffs2_acl_entry_short); + break; + + default: + goto fail; + } + } + return (char *)jffs2_acl; + fail: + kfree(jffs2_acl); + return ERR_PTR(-EINVAL); +} + +static struct posix_acl *jffs2_iget_acl(struct inode *inode, struct posix_acl **i_acl) +{ + struct posix_acl *acl = JFFS2_ACL_NOT_CACHED; + + spin_lock(&inode->i_lock); + if (*i_acl != JFFS2_ACL_NOT_CACHED) + acl = posix_acl_dup(*i_acl); + spin_unlock(&inode->i_lock); + return acl; +} + +static void jffs2_iset_acl(struct inode *inode, struct posix_acl **i_acl, struct posix_acl *acl) +{ + spin_lock(&inode->i_lock); + if (*i_acl != JFFS2_ACL_NOT_CACHED) + posix_acl_release(*i_acl); + *i_acl = posix_acl_dup(acl); + spin_unlock(&inode->i_lock); +} + +static struct posix_acl *jffs2_get_acl(struct inode *inode, int type) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct posix_acl *acl; + char *value = NULL; + int rc, xprefix; + + switch (type) { + case ACL_TYPE_ACCESS: + acl = jffs2_iget_acl(inode, &f->i_acl_access); + if (acl != JFFS2_ACL_NOT_CACHED) + return acl; + xprefix = JFFS2_XPREFIX_ACL_ACCESS; + break; + case ACL_TYPE_DEFAULT: + acl = jffs2_iget_acl(inode, &f->i_acl_default); + if (acl != JFFS2_ACL_NOT_CACHED) + return acl; + xprefix = JFFS2_XPREFIX_ACL_DEFAULT; + break; + default: + return ERR_PTR(-EINVAL); + } + rc = do_jffs2_getxattr(inode, xprefix, "", NULL, 0); + if (rc > 0) { + value = kmalloc(rc, GFP_KERNEL); + if (!value) + return ERR_PTR(-ENOMEM); + rc = do_jffs2_getxattr(inode, xprefix, "", value, rc); + } + if (rc > 0) { + acl = jffs2_acl_from_medium(value, rc); + } else if (rc == -ENODATA || rc == -ENOSYS) { + acl = NULL; + } else { + acl = ERR_PTR(rc); + } + if (value) + kfree(value); + if (!IS_ERR(acl)) { + switch (type) { + case ACL_TYPE_ACCESS: + jffs2_iset_acl(inode, &f->i_acl_access, acl); + break; + case ACL_TYPE_DEFAULT: + jffs2_iset_acl(inode, &f->i_acl_default, acl); + break; + } + } + return acl; +} + +static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + size_t size = 0; + char *value = NULL; + int rc, xprefix; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + + switch (type) { + case ACL_TYPE_ACCESS: + xprefix = JFFS2_XPREFIX_ACL_ACCESS; + if (acl) { + mode_t mode = inode->i_mode; + rc = posix_acl_equiv_mode(acl, &mode); + if (rc < 0) + return rc; + if (inode->i_mode != mode) { + inode->i_mode = mode; + jffs2_dirty_inode(inode); + } + if (rc == 0) + acl = NULL; + } + break; + case ACL_TYPE_DEFAULT: + xprefix = JFFS2_XPREFIX_ACL_DEFAULT; + if (!S_ISDIR(inode->i_mode)) + return acl ? -EACCES : 0; + break; + default: + return -EINVAL; + } + if (acl) { + value = jffs2_acl_to_medium(acl, &size); + if (IS_ERR(value)) + return PTR_ERR(value); + } + + rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0); + if (value) + kfree(value); + if (!rc) { + switch(type) { + case ACL_TYPE_ACCESS: + jffs2_iset_acl(inode, &f->i_acl_access, acl); + break; + case ACL_TYPE_DEFAULT: + jffs2_iset_acl(inode, &f->i_acl_default, acl); + break; + } + } + return rc; +} + +static int jffs2_check_acl(struct inode *inode, int mask) +{ + struct posix_acl *acl; + int rc; + + acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl) { + rc = posix_acl_permission(inode, acl, mask); + posix_acl_release(acl); + return rc; + } + return -EAGAIN; +} + +int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + return generic_permission(inode, mask, jffs2_check_acl); +} + +int jffs2_init_acl(struct inode *inode, struct inode *dir) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct posix_acl *acl = NULL, *clone; + mode_t mode; + int rc = 0; + + f->i_acl_access = JFFS2_ACL_NOT_CACHED; + f->i_acl_default = JFFS2_ACL_NOT_CACHED; + if (!S_ISLNK(inode->i_mode)) { + acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (!acl) + inode->i_mode &= ~current->fs->umask; + } + if (acl) { + if (S_ISDIR(inode->i_mode)) { + rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl); + if (rc) + goto cleanup; + } + clone = posix_acl_clone(acl, GFP_KERNEL); + rc = -ENOMEM; + if (!clone) + goto cleanup; + mode = inode->i_mode; + rc = posix_acl_create_masq(clone, &mode); + if (rc >= 0) { + inode->i_mode = mode; + if (rc > 0) + rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone); + } + posix_acl_release(clone); + } + cleanup: + posix_acl_release(acl); + return rc; +} + +void jffs2_clear_acl(struct inode *inode) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + + if (f->i_acl_access && f->i_acl_access != JFFS2_ACL_NOT_CACHED) { + posix_acl_release(f->i_acl_access); + f->i_acl_access = JFFS2_ACL_NOT_CACHED; + } + if (f->i_acl_default && f->i_acl_default != JFFS2_ACL_NOT_CACHED) { + posix_acl_release(f->i_acl_default); + f->i_acl_default = JFFS2_ACL_NOT_CACHED; + } +} + +int jffs2_acl_chmod(struct inode *inode) +{ + struct posix_acl *acl, *clone; + int rc; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(acl) || !acl) + return PTR_ERR(acl); + clone = posix_acl_clone(acl, GFP_KERNEL); + posix_acl_release(acl); + if (!clone) + return -ENOMEM; + rc = posix_acl_chmod_masq(clone, inode->i_mode); + if (!rc) + rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone); + posix_acl_release(clone); + return rc; +} + +static size_t jffs2_acl_access_listxattr(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + const int retlen = sizeof(POSIX_ACL_XATTR_ACCESS); + + if (list && retlen <= list_size) + strcpy(list, POSIX_ACL_XATTR_ACCESS); + return retlen; +} + +static size_t jffs2_acl_default_listxattr(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + const int retlen = sizeof(POSIX_ACL_XATTR_DEFAULT); + + if (list && retlen <= list_size) + strcpy(list, POSIX_ACL_XATTR_DEFAULT); + return retlen; +} + +static int jffs2_acl_getxattr(struct inode *inode, int type, void *buffer, size_t size) +{ + struct posix_acl *acl; + int rc; + + acl = jffs2_get_acl(inode, type); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (!acl) + return -ENODATA; + rc = posix_acl_to_xattr(acl, buffer, size); + posix_acl_release(acl); + + return rc; +} + +static int jffs2_acl_access_getxattr(struct inode *inode, const char *name, void *buffer, size_t size) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_getxattr(inode, ACL_TYPE_ACCESS, buffer, size); +} + +static int jffs2_acl_default_getxattr(struct inode *inode, const char *name, void *buffer, size_t size) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_getxattr(inode, ACL_TYPE_DEFAULT, buffer, size); +} + +static int jffs2_acl_setxattr(struct inode *inode, int type, const void *value, size_t size) +{ + struct posix_acl *acl; + int rc; + + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + + if (value) { + acl = posix_acl_from_xattr(value, size); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl) { + rc = posix_acl_valid(acl); + if (rc) + goto out; + } + } else { + acl = NULL; + } + rc = jffs2_set_acl(inode, type, acl); + out: + posix_acl_release(acl); + return rc; +} + +static int jffs2_acl_access_setxattr(struct inode *inode, const char *name, + const void *buffer, size_t size, int flags) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_setxattr(inode, ACL_TYPE_ACCESS, buffer, size); +} + +static int jffs2_acl_default_setxattr(struct inode *inode, const char *name, + const void *buffer, size_t size, int flags) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_setxattr(inode, ACL_TYPE_DEFAULT, buffer, size); +} + +struct xattr_handler jffs2_acl_access_xattr_handler = { + .prefix = POSIX_ACL_XATTR_ACCESS, + .list = jffs2_acl_access_listxattr, + .get = jffs2_acl_access_getxattr, + .set = jffs2_acl_access_setxattr, +}; + +struct xattr_handler jffs2_acl_default_xattr_handler = { + .prefix = POSIX_ACL_XATTR_DEFAULT, + .list = jffs2_acl_default_listxattr, + .get = jffs2_acl_default_getxattr, + .set = jffs2_acl_default_setxattr, +}; diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h new file mode 100644 index 000000000000..c98610b4e81c --- /dev/null +++ b/fs/jffs2/acl.h @@ -0,0 +1,46 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/acl.h + * POSIX ACL support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei + * Copyright (C) 2006 NEC Corporation + * + * For licensing information, see the file 'LICENCE' in the jffs2 directory. + *-------------------------------------------------------------------------*/ +typedef struct { + jint16_t e_tag; + jint16_t e_perm; + jint32_t e_id; +} jffs2_acl_entry; + +typedef struct { + jint16_t e_tag; + jint16_t e_perm; +} jffs2_acl_entry_short; + +typedef struct { + jint32_t a_version; +} jffs2_acl_header; + +#ifdef __KERNEL__ +#ifdef CONFIG_JFFS2_FS_POSIX_ACL + +#define JFFS2_ACL_NOT_CACHED ((void *)-1) + +extern int jffs2_permission(struct inode *, int, struct nameidata *); +extern int jffs2_acl_chmod(struct inode *); +extern int jffs2_init_acl(struct inode *, struct inode *); +extern void jffs2_clear_acl(struct inode *); + +extern struct xattr_handler jffs2_acl_access_xattr_handler; +extern struct xattr_handler jffs2_acl_default_xattr_handler; + +#else + +#define jffs2_permission NULL +#define jffs2_acl_chmod(inode) (0) +#define jffs2_init_acl(inode,dir) (0) +#define jffs2_clear_acl(inode) + +#endif /* CONFIG_JFFS2_FS_POSIX_ACL */ +#endif /* __KERNEL__ */ diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 70f7a896c04a..02826967ab58 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -160,6 +160,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c) ic->scan_dents = NULL; cond_resched(); } + jffs2_build_xattr_subsystem(c); c->flags &= ~JFFS2_SB_FLAG_BUILDING; dbg_fsbuild("FS build complete\n"); @@ -178,6 +179,7 @@ exit: jffs2_free_full_dirent(fd); } } + jffs2_clear_xattr_subsystem(c); } return ret; diff --git a/fs/jffs2/debug.h b/fs/jffs2/debug.h index 162af6dfe292..5fa494a792b2 100644 --- a/fs/jffs2/debug.h +++ b/fs/jffs2/debug.h @@ -171,6 +171,12 @@ #define dbg_memalloc(fmt, ...) #endif +/* Watch the XATTR subsystem */ +#ifdef JFFS2_DBG_XATTR_MESSAGES +#define dbg_xattr(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define dbg_xattr(fmt, ...) +#endif /* "Sanity" checks */ void diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 1c8e8c0f6cea..f1b18b99a3cd 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -57,7 +57,12 @@ struct inode_operations jffs2_dir_inode_operations = .rmdir = jffs2_rmdir, .mknod = jffs2_mknod, .rename = jffs2_rename, + .permission = jffs2_permission, .setattr = jffs2_setattr, + .setxattr = jffs2_setxattr, + .getxattr = jffs2_getxattr, + .listxattr = jffs2_listxattr, + .removexattr = jffs2_removexattr }; /***********************************************************************/ @@ -209,12 +214,15 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, ret = jffs2_do_create(c, dir_f, f, ri, dentry->d_name.name, dentry->d_name.len); - if (ret) { - make_bad_inode(inode); - iput(inode); - jffs2_free_raw_inode(ri); - return ret; - } + if (ret) + goto fail; + + ret = jffs2_init_security(inode, dir_i); + if (ret) + goto fail; + ret = jffs2_init_acl(inode, dir_i); + if (ret) + goto fail; dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime)); @@ -224,6 +232,12 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n", inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages)); return 0; + + fail: + make_bad_inode(inode); + iput(inode); + jffs2_free_raw_inode(ri); + return ret; } /***********************************************************************/ @@ -374,6 +388,18 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char up(&f->sem); jffs2_complete_reservation(c); + + ret = jffs2_init_security(inode, dir_i); + if (ret) { + jffs2_clear_inode(inode); + return ret; + } + ret = jffs2_init_acl(inode, dir_i); + if (ret) { + jffs2_clear_inode(inode); + return ret; + } + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { @@ -504,6 +530,18 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) up(&f->sem); jffs2_complete_reservation(c); + + ret = jffs2_init_security(inode, dir_i); + if (ret) { + jffs2_clear_inode(inode); + return ret; + } + ret = jffs2_init_acl(inode, dir_i); + if (ret) { + jffs2_clear_inode(inode); + return ret; + } + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { @@ -660,6 +698,18 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de up(&f->sem); jffs2_complete_reservation(c); + + ret = jffs2_init_security(inode, dir_i); + if (ret) { + jffs2_clear_inode(inode); + return ret; + } + ret = jffs2_init_acl(inode, dir_i); + if (ret) { + jffs2_clear_inode(inode); + return ret; + } + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 9f4171213e58..e92187f34d5f 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -54,7 +54,12 @@ const struct file_operations jffs2_file_operations = struct inode_operations jffs2_file_inode_operations = { - .setattr = jffs2_setattr + .permission = jffs2_permission, + .setattr = jffs2_setattr, + .setxattr = jffs2_setxattr, + .getxattr = jffs2_getxattr, + .listxattr = jffs2_listxattr, + .removexattr = jffs2_removexattr }; struct address_space_operations jffs2_file_address_operations = diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index ea1f37d4fc58..4607cdc4c46d 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -185,7 +185,12 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) int jffs2_setattr(struct dentry *dentry, struct iattr *iattr) { - return jffs2_do_setattr(dentry->d_inode, iattr); + int rc; + + rc = jffs2_do_setattr(dentry->d_inode, iattr); + if (!rc && (iattr->ia_valid & ATTR_MODE)) + rc = jffs2_acl_chmod(dentry->d_inode); + return rc; } int jffs2_statfs(struct super_block *sb, struct kstatfs *buf) @@ -224,6 +229,7 @@ void jffs2_clear_inode (struct inode *inode) D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode)); + jffs2_xattr_delete_inode(c, f->inocache); jffs2_do_clear_inode(c, f); } @@ -497,6 +503,8 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) } memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); + jffs2_init_xattr_subsystem(c); + if ((ret = jffs2_do_mount_fs(c))) goto out_inohash; @@ -531,6 +539,7 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) else kfree(c->blocks); out_inohash: + jffs2_clear_xattr_subsystem(c); kfree(c->inocache_list); out_wbuf: jffs2_flash_cleanup(c); diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 967fb2cf8e21..4ea1b7f0ae78 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -125,6 +125,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) struct jffs2_eraseblock *jeb; struct jffs2_raw_node_ref *raw; int ret = 0, inum, nlink; + int xattr = 0; if (down_interruptible(&c->alloc_sem)) return -EINTR; @@ -138,7 +139,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) the node CRCs etc. Do it now. */ /* checked_ino is protected by the alloc_sem */ - if (c->checked_ino > c->highest_ino) { + if (c->checked_ino > c->highest_ino && xattr) { printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n", c->unchecked_size); jffs2_dbg_dump_block_lists_nolock(c); @@ -148,6 +149,9 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) spin_unlock(&c->erase_completion_lock); + if (!xattr) + xattr = jffs2_verify_xattr(c); + spin_lock(&c->inocache_lock); ic = jffs2_get_ino_cache(c, c->checked_ino++); @@ -262,6 +266,16 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) ic = jffs2_raw_ref_to_ic(raw); + /* When 'ic' refers xattr_datum/xattr_ref, this node is GCed as xattr. + We can decide whether this node is inode or xattr by ic->class. + ret = 0 : ic is xattr_datum/xattr_ref, and GC was SUCCESSED. + ret < 0 : ic is xattr_datum/xattr_ref, but GC was FAILED. + ret > 0 : ic is NOT xattr_datum/xattr_ref. + */ + ret = jffs2_garbage_collect_xattr(c, ic); + if (ret <= 0) + goto release_sem; + /* We need to hold the inocache. Either the erase_completion_lock or the inocache_lock are sufficient; we trade down since the inocache_lock causes less contention. */ diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h index ad565bf9dcc1..2e0cc8e00b85 100644 --- a/fs/jffs2/jffs2_fs_i.h +++ b/fs/jffs2/jffs2_fs_i.h @@ -5,6 +5,7 @@ #include #include +#include #include struct jffs2_inode_info { @@ -45,6 +46,10 @@ struct jffs2_inode_info { struct inode vfs_inode; #endif #endif +#ifdef CONFIG_JFFS2_FS_POSIX_ACL + struct posix_acl *i_acl_access; + struct posix_acl *i_acl_default; +#endif }; #endif /* _JFFS2_FS_I */ diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index 4bcfb5570221..3b4e0edd6dbb 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -115,6 +115,16 @@ struct jffs2_sb_info { struct jffs2_summary *summary; /* Summary information */ +#ifdef CONFIG_JFFS2_FS_XATTR +#define XATTRINDEX_HASHSIZE (57) + uint32_t highest_xid; + struct list_head xattrindex[XATTRINDEX_HASHSIZE]; + struct list_head xattr_temp; + struct list_head xattr_unchecked; + struct rw_semaphore xattr_sem; + uint32_t xdatum_mem_usage; + uint32_t xdatum_mem_threshold; +#endif /* OS-private pointer for getting back to master superblock info */ void *os_priv; }; diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c index 036cbd11c004..3d5b7ecfbf8d 100644 --- a/fs/jffs2/malloc.c +++ b/fs/jffs2/malloc.c @@ -26,6 +26,10 @@ static kmem_cache_t *tmp_dnode_info_slab; static kmem_cache_t *raw_node_ref_slab; static kmem_cache_t *node_frag_slab; static kmem_cache_t *inode_cache_slab; +#ifdef CONFIG_JFFS2_FS_XATTR +static kmem_cache_t *xattr_datum_cache; +static kmem_cache_t *xattr_ref_cache; +#endif int __init jffs2_create_slab_caches(void) { @@ -68,8 +72,24 @@ int __init jffs2_create_slab_caches(void) inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), 0, 0, NULL, NULL); - if (inode_cache_slab) - return 0; + if (!inode_cache_slab) + goto err; + +#ifdef CONFIG_JFFS2_FS_XATTR + xattr_datum_cache = kmem_cache_create("jffs2_xattr_datum", + sizeof(struct jffs2_xattr_datum), + 0, 0, NULL, NULL); + if (!xattr_datum_cache) + goto err; + + xattr_ref_cache = kmem_cache_create("jffs2_xattr_ref", + sizeof(struct jffs2_xattr_ref), + 0, 0, NULL, NULL); + if (!xattr_ref_cache) + goto err; +#endif + + return 0; err: jffs2_destroy_slab_caches(); return -ENOMEM; @@ -91,6 +111,12 @@ void jffs2_destroy_slab_caches(void) kmem_cache_destroy(node_frag_slab); if(inode_cache_slab) kmem_cache_destroy(inode_cache_slab); +#ifdef CONFIG_JFFS2_FS_XATTR + if (xattr_datum_cache) + kmem_cache_destroy(xattr_datum_cache); + if (xattr_ref_cache) + kmem_cache_destroy(xattr_ref_cache); +#endif } struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize) @@ -205,3 +231,41 @@ void jffs2_free_inode_cache(struct jffs2_inode_cache *x) dbg_memalloc("%p\n", x); kmem_cache_free(inode_cache_slab, x); } + +#ifdef CONFIG_JFFS2_FS_XATTR +struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void) +{ + struct jffs2_xattr_datum *xd; + xd = kmem_cache_alloc(xattr_datum_cache, GFP_KERNEL); + dbg_memalloc("%p\n", xd); + + memset(xd, 0, sizeof(struct jffs2_xattr_datum)); + xd->class = RAWNODE_CLASS_XATTR_DATUM; + INIT_LIST_HEAD(&xd->xindex); + return xd; +} + +void jffs2_free_xattr_datum(struct jffs2_xattr_datum *xd) +{ + dbg_memalloc("%p\n", xd); + kmem_cache_free(xattr_datum_cache, xd); +} + +struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void) +{ + struct jffs2_xattr_ref *ref; + ref = kmem_cache_alloc(xattr_ref_cache, GFP_KERNEL); + dbg_memalloc("%p\n", ref); + + memset(ref, 0, sizeof(struct jffs2_xattr_ref)); + ref->class = RAWNODE_CLASS_XATTR_REF; + INIT_LIST_HEAD(&ref->ilist); + return ref; +} + +void jffs2_free_xattr_ref(struct jffs2_xattr_ref *ref) +{ + dbg_memalloc("%p\n", ref); + kmem_cache_free(xattr_ref_cache, ref); +} +#endif diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c index d4d0c41490cd..9c575733659b 100644 --- a/fs/jffs2/nodelist.c +++ b/fs/jffs2/nodelist.c @@ -938,6 +938,7 @@ void jffs2_free_ino_caches(struct jffs2_sb_info *c) this = c->inocache_list[i]; while (this) { next = this->next; + jffs2_xattr_free_inode(c, this); jffs2_free_inode_cache(this); this = next; } diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index f6645afe88e4..6f6279cf4909 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -20,6 +20,8 @@ #include #include "jffs2_fs_sb.h" #include "jffs2_fs_i.h" +#include "xattr.h" +#include "acl.h" #include "summary.h" #ifdef __ECOS @@ -107,11 +109,16 @@ struct jffs2_inode_cache { temporary lists of dirents, and later must be set to NULL to mark the end of the raw_node_ref->next_in_ino chain. */ + u8 class; /* It's used for identification */ + u8 flags; + uint16_t state; struct jffs2_inode_cache *next; struct jffs2_raw_node_ref *nodes; uint32_t ino; int nlink; - int state; +#ifdef CONFIG_JFFS2_FS_XATTR + struct list_head ilist; +#endif }; /* Inode states for 'state' above. We need the 'GC' state to prevent @@ -125,6 +132,12 @@ struct jffs2_inode_cache { #define INO_STATE_READING 5 /* In read_inode() */ #define INO_STATE_CLEARING 6 /* In clear_inode() */ +#define INO_FLAGS_XATTR_CHECKED 0x01 /* has no duplicate xattr_ref */ + +#define RAWNODE_CLASS_INODE_CACHE 0 +#define RAWNODE_CLASS_XATTR_DATUM 1 +#define RAWNODE_CLASS_XATTR_REF 2 + #define INOCACHE_HASHSIZE 128 /* @@ -374,6 +387,12 @@ struct jffs2_node_frag *jffs2_alloc_node_frag(void); void jffs2_free_node_frag(struct jffs2_node_frag *); struct jffs2_inode_cache *jffs2_alloc_inode_cache(void); void jffs2_free_inode_cache(struct jffs2_inode_cache *); +#ifdef CONFIG_JFFS2_FS_XATTR +struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void); +void jffs2_free_xattr_datum(struct jffs2_xattr_datum *); +struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void); +void jffs2_free_xattr_ref(struct jffs2_xattr_ref *); +#endif /* gc.c */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c); diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index d307cf548625..9936ae23f8dc 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -60,6 +60,10 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) f->target = NULL; f->flags = 0; f->usercompr = 0; +#ifdef CONFIG_JFFS2_FS_POSIX_ACL + f->i_acl_access = JFFS2_ACL_NOT_CACHED; + f->i_acl_default = JFFS2_ACL_NOT_CACHED; +#endif } diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index e1acce8fb2bf..61ccdf4f1042 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -902,6 +902,7 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, f->inocache->ino = f->inocache->nlink = 1; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; f->inocache->state = INO_STATE_READING; + init_xattr_inode_cache(f->inocache); jffs2_add_ino_cache(c, f->inocache); } if (!f->inocache) { diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index cf55b221fc2b..f09689e320fe 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -306,6 +306,136 @@ int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *je return BLK_STATE_ALLDIRTY; } +#ifdef CONFIG_JFFS2_FS_XATTR +static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_xattr *rx, uint32_t ofs, + struct jffs2_summary *s) +{ + struct jffs2_xattr_datum *xd; + struct jffs2_raw_node_ref *raw; + uint32_t totlen, crc; + + crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr) - 4); + if (crc != je32_to_cpu(rx->node_crc)) { + if (je32_to_cpu(rx->node_crc) != 0xffffffff) + JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", + ofs, je32_to_cpu(rx->node_crc), crc); + DIRTY_SPACE(je32_to_cpu(rx->totlen)); + return 0; + } + + totlen = PAD(sizeof(*rx) + rx->name_len + 1 + je16_to_cpu(rx->value_len)); + if (totlen != je32_to_cpu(rx->totlen)) { + JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%u\n", + ofs, je32_to_cpu(rx->totlen), totlen); + DIRTY_SPACE(je32_to_cpu(rx->totlen)); + return 0; + } + + raw = jffs2_alloc_raw_node_ref(); + if (!raw) + return -ENOMEM; + + xd = jffs2_setup_xattr_datum(c, je32_to_cpu(rx->xid), je32_to_cpu(rx->version)); + if (IS_ERR(xd)) { + jffs2_free_raw_node_ref(raw); + if (PTR_ERR(xd) == -EEXIST) { + DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen))); + return 0; + } + return PTR_ERR(xd); + } + xd->xprefix = rx->xprefix; + xd->name_len = rx->name_len; + xd->value_len = je16_to_cpu(rx->value_len); + xd->data_crc = je32_to_cpu(rx->data_crc); + xd->node = raw; + + raw->__totlen = totlen; + raw->flash_offset = ofs | REF_PRISTINE; + raw->next_phys = NULL; + raw->next_in_ino = (void *)xd; + if (!jeb->first_node) + jeb->first_node = raw; + if (jeb->last_node) + jeb->last_node->next_phys = raw; + jeb->last_node = raw; + + USED_SPACE(PAD(je32_to_cpu(rx->totlen))); + if (jffs2_sum_active()) + jffs2_sum_add_xattr_mem(s, rx, ofs - jeb->offset); + dbg_xattr("scaning xdatum at %#08x (xid=%u, version=%u)\n", + ofs, xd->xid, xd->version); + return 0; +} + +static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_xref *rr, uint32_t ofs, + struct jffs2_summary *s) +{ + struct jffs2_xattr_ref *ref; + struct jffs2_raw_node_ref *raw; + uint32_t crc; + + crc = crc32(0, rr, sizeof(*rr) - 4); + if (crc != je32_to_cpu(rr->node_crc)) { + if (je32_to_cpu(rr->node_crc) != 0xffffffff) + JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", + ofs, je32_to_cpu(rr->node_crc), crc); + DIRTY_SPACE(PAD(je32_to_cpu(rr->totlen))); + return 0; + } + + if (PAD(sizeof(struct jffs2_raw_xref)) != je32_to_cpu(rr->totlen)) { + JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%u\n", + ofs, je32_to_cpu(rr->totlen), + PAD(sizeof(struct jffs2_raw_xref))); + DIRTY_SPACE(je32_to_cpu(rr->totlen)); + return 0; + } + + ref = jffs2_alloc_xattr_ref(); + if (!ref) + return -ENOMEM; + + raw = jffs2_alloc_raw_node_ref(); + if (!raw) { + jffs2_free_xattr_ref(ref); + return -ENOMEM; + } + + /* BEFORE jffs2_build_xattr_subsystem() called, + * ref->xid is used to store 32bit xid, xd is not used + * ref->ino is used to store 32bit inode-number, ic is not used + * Thoes variables are declared as union, thus using those + * are exclusive. In a similar way, ref->ilist is temporarily + * used to chain all xattr_ref object. It's re-chained to + * jffs2_inode_cache in jffs2_build_xattr_subsystem() correctly. + */ + ref->node = raw; + ref->ino = je32_to_cpu(rr->ino); + ref->xid = je32_to_cpu(rr->xid); + list_add_tail(&ref->ilist, &c->xattr_temp); + + raw->__totlen = PAD(je32_to_cpu(rr->totlen)); + raw->flash_offset = ofs | REF_PRISTINE; + raw->next_phys = NULL; + raw->next_in_ino = (void *)ref; + if (!jeb->first_node) + jeb->first_node = raw; + if (jeb->last_node) + jeb->last_node->next_phys = raw; + jeb->last_node = raw; + + USED_SPACE(PAD(je32_to_cpu(rr->totlen))); + if (jffs2_sum_active()) + jffs2_sum_add_xref_mem(s, rr, ofs - jeb->offset); + dbg_xattr("scan xref at %#08x (xid=%u, ino=%u)\n", + ofs, ref->xid, ref->ino); + return 0; +} +#endif + static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) { struct jffs2_unknown_node *node; @@ -614,6 +744,43 @@ scan_more: ofs += PAD(je32_to_cpu(node->totlen)); break; +#ifdef CONFIG_JFFS2_FS_XATTR + case JFFS2_NODETYPE_XATTR: + if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) { + buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); + D1(printk(KERN_DEBUG "Fewer than %d bytes (xattr node)" + " left to end of buf. Reading 0x%x at 0x%08x\n", + je32_to_cpu(node->totlen), buf_len, ofs)); + err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); + if (err) + return err; + buf_ofs = ofs; + node = (void *)buf; + } + err = jffs2_scan_xattr_node(c, jeb, (void *)node, ofs, s); + if (err) + return err; + ofs += PAD(je32_to_cpu(node->totlen)); + break; + case JFFS2_NODETYPE_XREF: + if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) { + buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); + D1(printk(KERN_DEBUG "Fewer than %d bytes (xref node)" + " left to end of buf. Reading 0x%x at 0x%08x\n", + je32_to_cpu(node->totlen), buf_len, ofs)); + err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); + if (err) + return err; + buf_ofs = ofs; + node = (void *)buf; + } + err = jffs2_scan_xref_node(c, jeb, (void *)node, ofs, s); + if (err) + return err; + ofs += PAD(je32_to_cpu(node->totlen)); + break; +#endif /* CONFIG_JFFS2_FS_XATTR */ + case JFFS2_NODETYPE_CLEANMARKER: D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs)); if (je32_to_cpu(node->totlen) != c->cleanmarker_size) { @@ -721,6 +888,7 @@ struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uin ic->ino = ino; ic->nodes = (void *)ic; + init_xattr_inode_cache(ic); jffs2_add_ino_cache(c, ic); if (ino == 1) ic->nlink = 1; diff --git a/fs/jffs2/security.c b/fs/jffs2/security.c new file mode 100644 index 000000000000..4b6c3b22524f --- /dev/null +++ b/fs/jffs2/security.c @@ -0,0 +1,82 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/security.c + * Security Labels support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei + * Copyright (C) 2006 NEC Corporation + * + * For licensing information, see the file 'LICENCE' in the jffs2 directory. + *-------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nodelist.h" + +/* ---- Initial Security Label Attachment -------------- */ +int jffs2_init_security(struct inode *inode, struct inode *dir) +{ + int rc; + size_t len; + void *value; + char *name; + + rc = security_inode_init_security(inode, dir, &name, &value, &len); + if (rc) { + if (rc == -EOPNOTSUPP) + return 0; + return rc; + } + rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0); + + kfree(name); + kfree(value); + return rc; +} + +/* ---- XATTR Handler for "security.*" ----------------- */ +static int jffs2_security_getxattr(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (!strcmp(name, "")) + return -EINVAL; + + return do_jffs2_getxattr(inode, JFFS2_XPREFIX_SECURITY, name, buffer, size); +} + +static int jffs2_security_setxattr(struct inode *inode, const char *name, const void *buffer, + size_t size, int flags) +{ + if (!strcmp(name, "")) + return -EINVAL; + + return do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, buffer, size, flags); +} + +static size_t jffs2_security_listxattr(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + size_t retlen = XATTR_SECURITY_PREFIX_LEN + name_len + 1; + + if (list && retlen <= list_size) { + strcpy(list, XATTR_SECURITY_PREFIX); + strcpy(list + XATTR_SECURITY_PREFIX_LEN, name); + } + + return retlen; +} + +struct xattr_handler jffs2_security_xattr_handler = { + .prefix = XATTR_SECURITY_PREFIX, + .list = jffs2_security_listxattr, + .set = jffs2_security_setxattr, + .get = jffs2_security_getxattr +}; diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c index 7b0ed77a4c35..5d9ec8e36528 100644 --- a/fs/jffs2/summary.c +++ b/fs/jffs2/summary.c @@ -5,6 +5,7 @@ * Zoltan Sogor , * Patrik Kluba , * University of Szeged, Hungary + * 2005 KaiGai Kohei * * For licensing information, see the file 'LICENCE' in this directory. * @@ -81,6 +82,19 @@ static int jffs2_sum_add_mem(struct jffs2_summary *s, union jffs2_sum_mem *item) dbg_summary("dirent (%u) added to summary\n", je32_to_cpu(item->d.ino)); break; +#ifdef CONFIG_JFFS2_FS_XATTR + case JFFS2_NODETYPE_XATTR: + s->sum_size += JFFS2_SUMMARY_XATTR_SIZE; + s->sum_num++; + dbg_summary("xattr (xid=%u, version=%u) added to summary\n", + je32_to_cpu(item->x.xid), je32_to_cpu(item->x.version)); + break; + case JFFS2_NODETYPE_XREF: + s->sum_size += JFFS2_SUMMARY_XREF_SIZE; + s->sum_num++; + dbg_summary("xref added to summary\n"); + break; +#endif default: JFFS2_WARNING("UNKNOWN node type %u\n", je16_to_cpu(item->u.nodetype)); @@ -141,6 +155,40 @@ int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *r return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp); } +#ifdef CONFIG_JFFS2_FS_XATTR +int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs) +{ + struct jffs2_sum_xattr_mem *temp; + + temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL); + if (!temp) + return -ENOMEM; + + temp->nodetype = rx->nodetype; + temp->xid = rx->xid; + temp->version = rx->version; + temp->offset = cpu_to_je32(ofs); + temp->totlen = rx->totlen; + temp->next = NULL; + + return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp); +} + +int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs) +{ + struct jffs2_sum_xref_mem *temp; + + temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL); + if (!temp) + return -ENOMEM; + + temp->nodetype = rr->nodetype; + temp->offset = cpu_to_je32(ofs); + temp->next = NULL; + + return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp); +} +#endif /* Cleanup every collected summary information */ static void jffs2_sum_clean_collected(struct jffs2_summary *s) @@ -259,7 +307,40 @@ int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs, return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp); } +#ifdef CONFIG_JFFS2_FS_XATTR + case JFFS2_NODETYPE_XATTR: { + struct jffs2_sum_xattr_mem *temp; + if (je32_to_cpu(node->x.version) == 0xffffffff) + return 0; + temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL); + if (!temp) + goto no_mem; + temp->nodetype = node->x.nodetype; + temp->xid = node->x.xid; + temp->version = node->x.version; + temp->totlen = node->x.totlen; + temp->offset = cpu_to_je32(ofs); + temp->next = NULL; + + return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp); + } + case JFFS2_NODETYPE_XREF: { + struct jffs2_sum_xref_mem *temp; + + if (je32_to_cpu(node->r.ino) == 0xffffffff + && je32_to_cpu(node->r.xid) == 0xffffffff) + return 0; + temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL); + if (!temp) + goto no_mem; + temp->nodetype = node->r.nodetype; + temp->offset = cpu_to_je32(ofs); + temp->next = NULL; + + return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp); + } +#endif case JFFS2_NODETYPE_PADDING: dbg_summary("node PADDING\n"); c->summary->sum_padded += je32_to_cpu(node->u.totlen); @@ -408,8 +489,94 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras break; } +#ifdef CONFIG_JFFS2_FS_XATTR + case JFFS2_NODETYPE_XATTR: { + struct jffs2_xattr_datum *xd; + struct jffs2_sum_xattr_flash *spx; + uint32_t ofs; + + spx = (struct jffs2_sum_xattr_flash *)sp; + ofs = jeb->offset + je32_to_cpu(spx->offset); + dbg_summary("xattr at %#08x (xid=%u, version=%u)\n", ofs, + je32_to_cpu(spx->xid), je32_to_cpu(spx->version)); + raw = jffs2_alloc_raw_node_ref(); + if (!raw) { + JFFS2_NOTICE("allocation of node reference failed\n"); + kfree(summary); + return -ENOMEM; + } + xd = jffs2_setup_xattr_datum(c, je32_to_cpu(spx->xid), + je32_to_cpu(spx->version)); + if (IS_ERR(xd)) { + JFFS2_NOTICE("allocation of xattr_datum failed\n"); + jffs2_free_raw_node_ref(raw); + kfree(summary); + return PTR_ERR(xd); + } + xd->node = raw; + raw->flash_offset = ofs | REF_UNCHECKED; + raw->__totlen = PAD(je32_to_cpu(spx->totlen)); + raw->next_phys = NULL; + raw->next_in_ino = (void *)xd; + if (!jeb->first_node) + jeb->first_node = raw; + if (jeb->last_node) + jeb->last_node->next_phys = raw; + jeb->last_node = raw; + + *pseudo_random += je32_to_cpu(spx->xid); + UNCHECKED_SPACE(je32_to_cpu(spx->totlen)); + sp += JFFS2_SUMMARY_XATTR_SIZE; + + break; + } + case JFFS2_NODETYPE_XREF: { + struct jffs2_xattr_ref *ref; + struct jffs2_sum_xref_flash *spr; + uint32_t ofs; + + spr = (struct jffs2_sum_xref_flash *)sp; + ofs = jeb->offset + je32_to_cpu(spr->offset); + dbg_summary("xref at %#08x (xid=%u, ino=%u)\n", ofs, + je32_to_cpu(spr->xid), je32_to_cpu(spr->ino)); + raw = jffs2_alloc_raw_node_ref(); + if (!raw) { + JFFS2_NOTICE("allocation of node reference failed\n"); + kfree(summary); + return -ENOMEM; + } + ref = jffs2_alloc_xattr_ref(); + if (!ref) { + JFFS2_NOTICE("allocation of xattr_datum failed\n"); + jffs2_free_raw_node_ref(raw); + kfree(summary); + return -ENOMEM; + } + ref->ino = 0xfffffffe; + ref->xid = 0xfffffffd; + ref->node = raw; + list_add_tail(&ref->ilist, &c->xattr_temp); + + raw->__totlen = PAD(sizeof(struct jffs2_raw_xref)); + raw->flash_offset = ofs | REF_UNCHECKED; + raw->next_phys = NULL; + raw->next_in_ino = (void *)ref; + if (!jeb->first_node) + jeb->first_node = raw; + if (jeb->last_node) + jeb->last_node->next_phys = raw; + jeb->last_node = raw; + + UNCHECKED_SPACE(PAD(sizeof(struct jffs2_raw_xref))); + *pseudo_random += ofs; + sp += JFFS2_SUMMARY_XREF_SIZE; + + break; + } +#endif default : { +printk("nodetype = %#04x\n",je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)); JFFS2_WARNING("Unsupported node type found in summary! Exiting..."); kfree(summary); return -EIO; @@ -617,7 +784,31 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock break; } +#ifdef CONFIG_JFFS2_FS_XATTR + case JFFS2_NODETYPE_XATTR: { + struct jffs2_sum_xattr_flash *sxattr_ptr = wpage; + + temp = c->summary->sum_list_head; + sxattr_ptr->nodetype = temp->x.nodetype; + sxattr_ptr->xid = temp->x.xid; + sxattr_ptr->version = temp->x.version; + sxattr_ptr->offset = temp->x.offset; + sxattr_ptr->totlen = temp->x.totlen; + + wpage += JFFS2_SUMMARY_XATTR_SIZE; + break; + } + case JFFS2_NODETYPE_XREF: { + struct jffs2_sum_xref_flash *sxref_ptr = wpage; + + temp = c->summary->sum_list_head; + sxref_ptr->nodetype = temp->r.nodetype; + sxref_ptr->offset = temp->r.offset; + wpage += JFFS2_SUMMARY_XREF_SIZE; + break; + } +#endif default : { BUG(); /* unknown node in summary information */ } diff --git a/fs/jffs2/summary.h b/fs/jffs2/summary.h index b7a678be1709..a3b66c18aae9 100644 --- a/fs/jffs2/summary.h +++ b/fs/jffs2/summary.h @@ -45,6 +45,8 @@ #define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff #define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash)) #define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x)) +#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash)) +#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash)) /* Summary structures used on flash */ @@ -75,11 +77,28 @@ struct jffs2_sum_dirent_flash uint8_t name[0]; /* dirent name */ } __attribute__((packed)); +struct jffs2_sum_xattr_flash +{ + jint16_t nodetype; /* == JFFS2_NODETYPE_XATR */ + jint32_t xid; /* xattr identifier */ + jint32_t version; /* version number */ + jint32_t offset; /* offset on jeb */ + jint32_t totlen; /* node length */ +} __attribute__((packed)); + +struct jffs2_sum_xref_flash +{ + jint16_t nodetype; /* == JFFS2_NODETYPE_XREF */ + jint32_t offset; /* offset on jeb */ +} __attribute__((packed)); + union jffs2_sum_flash { struct jffs2_sum_unknown_flash u; struct jffs2_sum_inode_flash i; struct jffs2_sum_dirent_flash d; + struct jffs2_sum_xattr_flash x; + struct jffs2_sum_xref_flash r; }; /* Summary structures used in the memory */ @@ -114,11 +133,30 @@ struct jffs2_sum_dirent_mem uint8_t name[0]; /* dirent name */ } __attribute__((packed)); +struct jffs2_sum_xattr_mem +{ + union jffs2_sum_mem *next; + jint16_t nodetype; + jint32_t xid; + jint32_t version; + jint32_t offset; + jint32_t totlen; +} __attribute__((packed)); + +struct jffs2_sum_xref_mem +{ + union jffs2_sum_mem *next; + jint16_t nodetype; + jint32_t offset; +} __attribute__((packed)); + union jffs2_sum_mem { struct jffs2_sum_unknown_mem u; struct jffs2_sum_inode_mem i; struct jffs2_sum_dirent_mem d; + struct jffs2_sum_xattr_mem x; + struct jffs2_sum_xref_mem r; }; /* Summary related information stored in superblock */ @@ -159,6 +197,8 @@ int jffs2_sum_write_sumnode(struct jffs2_sb_info *c); int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size); int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri, uint32_t ofs); int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd, uint32_t ofs); +int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs); +int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs); int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t ofs, uint32_t *pseudo_random); @@ -176,6 +216,8 @@ int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb #define jffs2_sum_add_padding_mem(a,b) #define jffs2_sum_add_inode_mem(a,b,c) #define jffs2_sum_add_dirent_mem(a,b,c) +#define jffs2_sum_add_xattr_mem(a,b,c) +#define jffs2_sum_add_xref_mem(a,b,c) #define jffs2_sum_scan_sumnode(a,b,c,d) (0) #endif /* CONFIG_JFFS2_SUMMARY */ diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index ffd8e84b22cc..c8b539ee7d80 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -151,7 +151,10 @@ static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type, sb->s_op = &jffs2_super_operations; sb->s_flags = flags | MS_NOATIME; - + sb->s_xattr = jffs2_xattr_handlers; +#ifdef CONFIG_JFFS2_FS_POSIX_ACL + sb->s_flags |= MS_POSIXACL; +#endif ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); if (ret) { @@ -293,6 +296,7 @@ static void jffs2_put_super (struct super_block *sb) kfree(c->blocks); jffs2_flash_cleanup(c); kfree(c->inocache_list); + jffs2_clear_xattr_subsystem(c); if (c->mtd->sync) c->mtd->sync(c->mtd); diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index d55754fe8925..fc211b6e9b03 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -24,7 +24,12 @@ struct inode_operations jffs2_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = jffs2_follow_link, - .setattr = jffs2_setattr + .permission = jffs2_permission, + .setattr = jffs2_setattr, + .setxattr = jffs2_setxattr, + .getxattr = jffs2_getxattr, + .listxattr = jffs2_listxattr, + .removexattr = jffs2_removexattr }; static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd) diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 1342f0158e9b..d5c78195f3b8 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -36,7 +36,7 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint f->inocache->nlink = 1; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; f->inocache->state = INO_STATE_PRESENT; - + init_xattr_inode_cache(f->inocache); jffs2_add_ino_cache(c, f->inocache); D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino)); diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c new file mode 100644 index 000000000000..c9a185c54ce7 --- /dev/null +++ b/fs/jffs2/xattr.c @@ -0,0 +1,1271 @@ +/* ------------------------------------------------------------------------- + * File: fs/jffs2/xattr.c + * XATTR support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei + * Copyright (C) 2006 NEC Corporation + * + * For licensing information, see the file 'LICENCE' in the jffs2 directory. + * ------------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nodelist.h" +/* -------- xdatum related functions ---------------- + * xattr_datum_hashkey(xprefix, xname, xvalue, xsize) + * is used to calcurate xdatum hashkey. The reminder of hashkey into XATTRINDEX_HASHSIZE is + * the index of the xattr name/value pair cache (c->xattrindex). + * unload_xattr_datum(c, xd) + * is used to release xattr name/value pair and detach from c->xattrindex. + * reclaim_xattr_datum(c) + * is used to reclaim xattr name/value pairs on the xattr name/value pair cache when + * memory usage by cache is over c->xdatum_mem_threshold. Currentry, this threshold + * is hard coded as 32KiB. + * delete_xattr_datum_node(c, xd) + * is used to delete a jffs2 node is dominated by xdatum. When EBS(Erase Block Summary) is + * enabled, it overwrites the obsolete node by myself. + * delete_xattr_datum(c, xd) + * is used to delete jffs2_xattr_datum object. It must be called with 0-value of reference + * counter. (It means how many jffs2_xattr_ref object refers this xdatum.) + * do_verify_xattr_datum(c, xd) + * is used to load the xdatum informations without name/value pair from the medium. + * It's necessary once, because those informations are not collected during mounting + * process when EBS is enabled. + * 0 will be returned, if success. An negative return value means recoverable error, and + * positive return value means unrecoverable error. Thus, caller must remove this xdatum + * and xref when it returned positive value. + * do_load_xattr_datum(c, xd) + * is used to load name/value pair from the medium. + * The meanings of return value is same as do_verify_xattr_datum(). + * load_xattr_datum(c, xd) + * is used to be as a wrapper of do_verify_xattr_datum() and do_load_xattr_datum(). + * If xd need to call do_verify_xattr_datum() at first, it's called before calling + * do_load_xattr_datum(). The meanings of return value is same as do_verify_xattr_datum(). + * save_xattr_datum(c, xd, phys_ofs) + * is used to write xdatum to medium. xd->version will be incremented. + * create_xattr_datum(c, xprefix, xname, xvalue, xsize, phys_ofs) + * is used to create new xdatum and write to medium. + * -------------------------------------------------- */ + +static uint32_t xattr_datum_hashkey(int xprefix, const char *xname, const char *xvalue, int xsize) +{ + int name_len = strlen(xname); + + return crc32(xprefix, xname, name_len) ^ crc32(xprefix, xvalue, xsize); +} + +static void unload_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem) */ + D1(dbg_xattr("%s: xid=%u, version=%u\n", __FUNCTION__, xd->xid, xd->version)); + if (xd->xname) { + c->xdatum_mem_usage -= (xd->name_len + 1 + xd->value_len); + kfree(xd->xname); + } + + list_del_init(&xd->xindex); + xd->hashkey = 0; + xd->xname = NULL; + xd->xvalue = NULL; +} + +static void reclaim_xattr_datum(struct jffs2_sb_info *c) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_xattr_datum *xd, *_xd; + uint32_t target, before; + static int index = 0; + int count; + + if (c->xdatum_mem_threshold > c->xdatum_mem_usage) + return; + + before = c->xdatum_mem_usage; + target = c->xdatum_mem_usage * 4 / 5; /* 20% reduction */ + for (count = 0; count < XATTRINDEX_HASHSIZE; count++) { + list_for_each_entry_safe(xd, _xd, &c->xattrindex[index], xindex) { + if (xd->flags & JFFS2_XFLAGS_HOT) { + xd->flags &= ~JFFS2_XFLAGS_HOT; + } else if (!(xd->flags & JFFS2_XFLAGS_BIND)) { + unload_xattr_datum(c, xd); + } + if (c->xdatum_mem_usage <= target) + goto out; + } + index = (index+1) % XATTRINDEX_HASHSIZE; + } + out: + JFFS2_NOTICE("xdatum_mem_usage from %u byte to %u byte (%u byte reclaimed)\n", + before, c->xdatum_mem_usage, before - c->xdatum_mem_usage); +} + +static void delete_xattr_datum_node(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_raw_xattr rx; + uint32_t length; + int rc; + + if (!xd->node) { + JFFS2_WARNING("xdatum (xid=%u) is removed twice.\n", xd->xid); + return; + } + if (jffs2_sum_active()) { + memset(&rx, 0xff, sizeof(struct jffs2_raw_xattr)); + rc = jffs2_flash_read(c, ref_offset(xd->node), + sizeof(struct jffs2_unknown_node), + &length, (char *)&rx); + if (rc || length != sizeof(struct jffs2_unknown_node)) { + JFFS2_ERROR("jffs2_flash_read()=%d, req=%u, read=%u at %#08x\n", + rc, sizeof(struct jffs2_unknown_node), + length, ref_offset(xd->node)); + } + rc = jffs2_flash_write(c, ref_offset(xd->node), sizeof(rx), + &length, (char *)&rx); + if (rc || length != sizeof(struct jffs2_raw_xattr)) { + JFFS2_ERROR("jffs2_flash_write()=%d, req=%u, wrote=%u ar %#08x\n", + rc, sizeof(rx), length, ref_offset(xd->node)); + } + } + spin_lock(&c->erase_completion_lock); + xd->node->next_in_ino = NULL; + spin_unlock(&c->erase_completion_lock); + jffs2_mark_node_obsolete(c, xd->node); + xd->node = NULL; +} + +static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem) */ + BUG_ON(xd->refcnt); + + unload_xattr_datum(c, xd); + if (xd->node) { + delete_xattr_datum_node(c, xd); + xd->node = NULL; + } + jffs2_free_xattr_datum(xd); +} + +static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_eraseblock *jeb; + struct jffs2_raw_xattr rx; + size_t readlen; + uint32_t crc, totlen; + int rc; + + BUG_ON(!xd->node); + BUG_ON(ref_flags(xd->node) != REF_UNCHECKED); + + rc = jffs2_flash_read(c, ref_offset(xd->node), sizeof(rx), &readlen, (char *)&rx); + if (rc || readlen != sizeof(rx)) { + JFFS2_WARNING("jffs2_flash_read()=%d, req=%u, read=%u at %#08x\n", + rc, sizeof(rx), readlen, ref_offset(xd->node)); + return rc ? rc : -EIO; + } + crc = crc32(0, &rx, sizeof(rx) - 4); + if (crc != je32_to_cpu(rx.node_crc)) { + if (je32_to_cpu(rx.node_crc) != 0xffffffff) + JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", + ref_offset(xd->node), je32_to_cpu(rx.hdr_crc), crc); + return EIO; + } + totlen = PAD(sizeof(rx) + rx.name_len + 1 + je16_to_cpu(rx.value_len)); + if (je16_to_cpu(rx.magic) != JFFS2_MAGIC_BITMASK + || je16_to_cpu(rx.nodetype) != JFFS2_NODETYPE_XATTR + || je32_to_cpu(rx.totlen) != totlen + || je32_to_cpu(rx.xid) != xd->xid + || je32_to_cpu(rx.version) != xd->version) { + JFFS2_ERROR("inconsistent xdatum at %#08x, magic=%#04x/%#04x, " + "nodetype=%#04x/%#04x, totlen=%u/%u, xid=%u/%u, version=%u/%u\n", + ref_offset(xd->node), je16_to_cpu(rx.magic), JFFS2_MAGIC_BITMASK, + je16_to_cpu(rx.nodetype), JFFS2_NODETYPE_XATTR, + je32_to_cpu(rx.totlen), totlen, + je32_to_cpu(rx.xid), xd->xid, + je32_to_cpu(rx.version), xd->version); + return EIO; + } + xd->xprefix = rx.xprefix; + xd->name_len = rx.name_len; + xd->value_len = je16_to_cpu(rx.value_len); + xd->data_crc = je32_to_cpu(rx.data_crc); + + /* This JFFS2_NODETYPE_XATTR node is checked */ + jeb = &c->blocks[ref_offset(xd->node) / c->sector_size]; + totlen = PAD(je32_to_cpu(rx.totlen)); + + spin_lock(&c->erase_completion_lock); + c->unchecked_size -= totlen; c->used_size += totlen; + jeb->unchecked_size -= totlen; jeb->used_size += totlen; + xd->node->flash_offset = ref_offset(xd->node) | REF_PRISTINE; + spin_unlock(&c->erase_completion_lock); + + /* unchecked xdatum is chained with c->xattr_unchecked */ + list_del_init(&xd->xindex); + + dbg_xattr("success on verfying xdatum (xid=%u, version=%u)\n", + xd->xid, xd->version); + + return 0; +} + +static int do_load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem) */ + char *data; + size_t readlen; + uint32_t crc, length; + int i, ret, retry = 0; + + BUG_ON(!xd->node); + BUG_ON(ref_flags(xd->node) != REF_PRISTINE); + BUG_ON(!list_empty(&xd->xindex)); + retry: + length = xd->name_len + 1 + xd->value_len; + data = kmalloc(length, GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = jffs2_flash_read(c, ref_offset(xd->node)+sizeof(struct jffs2_raw_xattr), + length, &readlen, data); + + if (ret || length!=readlen) { + JFFS2_WARNING("jffs2_flash_read() returned %d, request=%d, readlen=%d, at %#08x\n", + ret, length, readlen, ref_offset(xd->node)); + kfree(data); + return ret ? ret : -EIO; + } + + data[xd->name_len] = '\0'; + crc = crc32(0, data, length); + if (crc != xd->data_crc) { + JFFS2_WARNING("node CRC failed (JFFS2_NODETYPE_XREF)" + " at %#08x, read: 0x%08x calculated: 0x%08x\n", + ref_offset(xd->node), xd->data_crc, crc); + kfree(data); + return EIO; + } + + xd->flags |= JFFS2_XFLAGS_HOT; + xd->xname = data; + xd->xvalue = data + xd->name_len+1; + + c->xdatum_mem_usage += length; + + xd->hashkey = xattr_datum_hashkey(xd->xprefix, xd->xname, xd->xvalue, xd->value_len); + i = xd->hashkey % XATTRINDEX_HASHSIZE; + list_add(&xd->xindex, &c->xattrindex[i]); + if (!retry) { + retry = 1; + reclaim_xattr_datum(c); + if (!xd->xname) + goto retry; + } + + dbg_xattr("success on loading xdatum (xid=%u, xprefix=%u, xname='%s')\n", + xd->xid, xd->xprefix, xd->xname); + + return 0; +} + +static int load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem); + * rc < 0 : recoverable error, try again + * rc = 0 : success + * rc > 0 : Unrecoverable error, this node should be deleted. + */ + int rc = 0; + BUG_ON(xd->xname); + if (!xd->node) + return EIO; + if (unlikely(ref_flags(xd->node) != REF_PRISTINE)) { + rc = do_verify_xattr_datum(c, xd); + if (rc > 0) { + list_del_init(&xd->xindex); + delete_xattr_datum_node(c, xd); + } + } + if (!rc) + rc = do_load_xattr_datum(c, xd); + return rc; +} + +static int save_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd, uint32_t phys_ofs) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_raw_xattr rx; + struct jffs2_raw_node_ref *raw; + struct kvec vecs[2]; + uint32_t length; + int rc, totlen; + + BUG_ON(!xd->xname); + + vecs[0].iov_base = ℞ + vecs[0].iov_len = PAD(sizeof(rx)); + vecs[1].iov_base = xd->xname; + vecs[1].iov_len = xd->name_len + 1 + xd->value_len; + totlen = vecs[0].iov_len + vecs[1].iov_len; + + raw = jffs2_alloc_raw_node_ref(); + if (!raw) + return -ENOMEM; + raw->flash_offset = phys_ofs; + raw->__totlen = PAD(totlen); + raw->next_phys = NULL; + raw->next_in_ino = (void *)xd; + + /* Setup raw-xattr */ + rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR); + rx.totlen = cpu_to_je32(PAD(totlen)); + rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); + + rx.xid = cpu_to_je32(xd->xid); + rx.version = cpu_to_je32(++xd->version); + rx.xprefix = xd->xprefix; + rx.name_len = xd->name_len; + rx.value_len = cpu_to_je16(xd->value_len); + rx.data_crc = cpu_to_je32(crc32(0, vecs[1].iov_base, vecs[1].iov_len)); + rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_raw_xattr) - 4)); + + rc = jffs2_flash_writev(c, vecs, 2, phys_ofs, &length, 0); + if (rc || totlen != length) { + JFFS2_WARNING("jffs2_flash_writev()=%d, req=%u, wrote=%u, at %#08x\n", + rc, totlen, length, phys_ofs); + rc = rc ? rc : -EIO; + if (length) { + raw->flash_offset |= REF_OBSOLETE; + raw->next_in_ino = NULL; + jffs2_add_physical_node_ref(c, raw); + jffs2_mark_node_obsolete(c, raw); + } else { + jffs2_free_raw_node_ref(raw); + } + return rc; + } + BUG_ON(raw->__totlen < sizeof(struct jffs2_raw_xattr)); + /* success */ + raw->flash_offset |= REF_PRISTINE; + jffs2_add_physical_node_ref(c, raw); + if (xd->node) + delete_xattr_datum_node(c, xd); + xd->node = raw; + + dbg_xattr("success on saving xdatum (xid=%u, version=%u, xprefix=%u, xname='%s')\n", + xd->xid, xd->version, xd->xprefix, xd->xname); + + return 0; +} + +static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c, + int xprefix, const char *xname, + const char *xvalue, int xsize, + uint32_t phys_ofs) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_xattr_datum *xd; + uint32_t hashkey, name_len; + char *data; + int i, rc; + + /* Search xattr_datum has same xname/xvalue by index */ + hashkey = xattr_datum_hashkey(xprefix, xname, xvalue, xsize); + i = hashkey % XATTRINDEX_HASHSIZE; + list_for_each_entry(xd, &c->xattrindex[i], xindex) { + if (xd->hashkey==hashkey + && xd->xprefix==xprefix + && xd->value_len==xsize + && !strcmp(xd->xname, xname) + && !memcmp(xd->xvalue, xvalue, xsize)) { + xd->refcnt++; + return xd; + } + } + + /* Not found, Create NEW XATTR-Cache */ + name_len = strlen(xname); + + xd = jffs2_alloc_xattr_datum(); + if (!xd) + return ERR_PTR(-ENOMEM); + + data = kmalloc(name_len + 1 + xsize, GFP_KERNEL); + if (!data) { + jffs2_free_xattr_datum(xd); + return ERR_PTR(-ENOMEM); + } + strcpy(data, xname); + memcpy(data + name_len + 1, xvalue, xsize); + + xd->refcnt = 1; + xd->xid = ++c->highest_xid; + xd->flags |= JFFS2_XFLAGS_HOT; + xd->xprefix = xprefix; + + xd->hashkey = hashkey; + xd->xname = data; + xd->xvalue = data + name_len + 1; + xd->name_len = name_len; + xd->value_len = xsize; + xd->data_crc = crc32(0, data, xd->name_len + 1 + xd->value_len); + + rc = save_xattr_datum(c, xd, phys_ofs); + if (rc) { + kfree(xd->xname); + jffs2_free_xattr_datum(xd); + return ERR_PTR(rc); + } + + /* Insert Hash Index */ + i = hashkey % XATTRINDEX_HASHSIZE; + list_add(&xd->xindex, &c->xattrindex[i]); + + c->xdatum_mem_usage += (xd->name_len + 1 + xd->value_len); + reclaim_xattr_datum(c); + + return xd; +} + +/* -------- xdatum related functions ---------------- + * verify_xattr_ref(c, ref) + * is used to load xref information from medium. Because summary data does not + * contain xid/ino, it's necessary to verify once while mounting process. + * delete_xattr_ref_node(c, ref) + * is used to delete a jffs2 node is dominated by xref. When EBS is enabled, + * it overwrites the obsolete node by myself. + * delete_xattr_ref(c, ref) + * is used to delete jffs2_xattr_ref object. If the reference counter of xdatum + * is refered by this xref become 0, delete_xattr_datum() is called later. + * save_xattr_ref(c, ref, phys_ofs) + * is used to write xref to medium. + * create_xattr_ref(c, ic, xd, phys_ofs) + * is used to create a new xref and write to medium. + * jffs2_xattr_delete_inode(c, ic) + * is called to remove xrefs related to obsolete inode when inode is unlinked. + * jffs2_xattr_free_inode(c, ic) + * is called to release xattr related objects when unmounting. + * check_xattr_ref_ilist(c, ic) + * is used to confirm inode does not have duplicate xattr name/value pair. + * -------------------------------------------------- */ +static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) +{ + struct jffs2_eraseblock *jeb; + struct jffs2_raw_xref rr; + size_t readlen; + uint32_t crc, totlen; + int rc; + + BUG_ON(ref_flags(ref->node) != REF_UNCHECKED); + + rc = jffs2_flash_read(c, ref_offset(ref->node), sizeof(rr), &readlen, (char *)&rr); + if (rc || sizeof(rr) != readlen) { + JFFS2_WARNING("jffs2_flash_read()=%d, req=%u, read=%u, at %#08x\n", + rc, sizeof(rr), readlen, ref_offset(ref->node)); + return rc ? rc : -EIO; + } + /* obsolete node */ + crc = crc32(0, &rr, sizeof(rr) - 4); + if (crc != je32_to_cpu(rr.node_crc)) { + if (je32_to_cpu(rr.node_crc) != 0xffffffff) + JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", + ref_offset(ref->node), je32_to_cpu(rr.node_crc), crc); + return EIO; + } + if (je16_to_cpu(rr.magic) != JFFS2_MAGIC_BITMASK + || je16_to_cpu(rr.nodetype) != JFFS2_NODETYPE_XREF + || je32_to_cpu(rr.totlen) != PAD(sizeof(rr))) { + JFFS2_ERROR("inconsistent xref at %#08x, magic=%#04x/%#04x, " + "nodetype=%#04x/%#04x, totlen=%u/%u\n", + ref_offset(ref->node), je16_to_cpu(rr.magic), JFFS2_MAGIC_BITMASK, + je16_to_cpu(rr.nodetype), JFFS2_NODETYPE_XREF, + je32_to_cpu(rr.totlen), PAD(sizeof(rr))); + return EIO; + } + ref->ino = je32_to_cpu(rr.ino); + ref->xid = je32_to_cpu(rr.xid); + + /* fixup superblock/eraseblock info */ + jeb = &c->blocks[ref_offset(ref->node) / c->sector_size]; + totlen = PAD(sizeof(rr)); + + spin_lock(&c->erase_completion_lock); + c->unchecked_size -= totlen; c->used_size += totlen; + jeb->unchecked_size -= totlen; jeb->used_size += totlen; + ref->node->flash_offset = ref_offset(ref->node) | REF_PRISTINE; + spin_unlock(&c->erase_completion_lock); + + dbg_xattr("success on verifying xref (ino=%u, xid=%u) at %#08x\n", + ref->ino, ref->xid, ref_offset(ref->node)); + return 0; +} + +static void delete_xattr_ref_node(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) +{ + struct jffs2_raw_xref rr; + uint32_t length; + int rc; + + if (jffs2_sum_active()) { + memset(&rr, 0xff, sizeof(rr)); + rc = jffs2_flash_read(c, ref_offset(ref->node), + sizeof(struct jffs2_unknown_node), + &length, (char *)&rr); + if (rc || length != sizeof(struct jffs2_unknown_node)) { + JFFS2_ERROR("jffs2_flash_read()=%d, req=%u, read=%u at %#08x\n", + rc, sizeof(struct jffs2_unknown_node), + length, ref_offset(ref->node)); + } + rc = jffs2_flash_write(c, ref_offset(ref->node), sizeof(rr), + &length, (char *)&rr); + if (rc || length != sizeof(struct jffs2_raw_xref)) { + JFFS2_ERROR("jffs2_flash_write()=%d, req=%u, wrote=%u at %#08x\n", + rc, sizeof(rr), length, ref_offset(ref->node)); + } + } + spin_lock(&c->erase_completion_lock); + ref->node->next_in_ino = NULL; + spin_unlock(&c->erase_completion_lock); + jffs2_mark_node_obsolete(c, ref->node); + ref->node = NULL; +} + +static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_xattr_datum *xd; + + BUG_ON(!ref->node); + delete_xattr_ref_node(c, ref); + + list_del(&ref->ilist); + xd = ref->xd; + xd->refcnt--; + if (!xd->refcnt) + delete_xattr_datum(c, xd); + jffs2_free_xattr_ref(ref); +} + +static int save_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref, uint32_t phys_ofs) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_raw_node_ref *raw; + struct jffs2_raw_xref rr; + uint32_t length; + int ret; + + raw = jffs2_alloc_raw_node_ref(); + if (!raw) + return -ENOMEM; + raw->flash_offset = phys_ofs; + raw->__totlen = PAD(sizeof(rr)); + raw->next_phys = NULL; + raw->next_in_ino = (void *)ref; + + rr.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rr.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF); + rr.totlen = cpu_to_je32(PAD(sizeof(rr))); + rr.hdr_crc = cpu_to_je32(crc32(0, &rr, sizeof(struct jffs2_unknown_node) - 4)); + + rr.ino = cpu_to_je32(ref->ic->ino); + rr.xid = cpu_to_je32(ref->xd->xid); + rr.node_crc = cpu_to_je32(crc32(0, &rr, sizeof(rr) - 4)); + + ret = jffs2_flash_write(c, phys_ofs, sizeof(rr), &length, (char *)&rr); + if (ret || sizeof(rr) != length) { + JFFS2_WARNING("jffs2_flash_write() returned %d, request=%u, retlen=%u, at %#08x\n", + ret, sizeof(rr), length, phys_ofs); + ret = ret ? ret : -EIO; + if (length) { + raw->flash_offset |= REF_OBSOLETE; + raw->next_in_ino = NULL; + jffs2_add_physical_node_ref(c, raw); + jffs2_mark_node_obsolete(c, raw); + } else { + jffs2_free_raw_node_ref(raw); + } + return ret; + } + raw->flash_offset |= REF_PRISTINE; + + jffs2_add_physical_node_ref(c, raw); + if (ref->node) + delete_xattr_ref_node(c, ref); + ref->node = raw; + + dbg_xattr("success on saving xref (ino=%u, xid=%u)\n", ref->ic->ino, ref->xd->xid); + + return 0; +} + +static struct jffs2_xattr_ref *create_xattr_ref(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, + struct jffs2_xattr_datum *xd, uint32_t phys_ofs) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_xattr_ref *ref; + int ret; + + ref = jffs2_alloc_xattr_ref(); + if (!ref) + return ERR_PTR(-ENOMEM); + ref->ic = ic; + ref->xd = xd; + + ret = save_xattr_ref(c, ref, phys_ofs); + if (ret) { + jffs2_free_xattr_ref(ref); + return ERR_PTR(ret); + } + + /* Chain to inode */ + list_add(&ref->ilist, &ic->ilist); + + return ref; /* success */ +} + +void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + /* It's called from jffs2_clear_inode() on inode removing. + When an inode with XATTR is removed, those XATTRs must be removed. */ + struct jffs2_xattr_ref *ref, *_ref; + + if (!ic || ic->nlink > 0) + return; + + down_write(&c->xattr_sem); + list_for_each_entry_safe(ref, _ref, &ic->ilist, ilist) + delete_xattr_ref(c, ref); + up_write(&c->xattr_sem); +} + +void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + /* It's called from jffs2_free_ino_caches() until unmounting FS. */ + struct jffs2_xattr_datum *xd; + struct jffs2_xattr_ref *ref, *_ref; + + down_write(&c->xattr_sem); + list_for_each_entry_safe(ref, _ref, &ic->ilist, ilist) { + list_del(&ref->ilist); + xd = ref->xd; + xd->refcnt--; + if (!xd->refcnt) { + unload_xattr_datum(c, xd); + jffs2_free_xattr_datum(xd); + } + jffs2_free_xattr_ref(ref); + } + up_write(&c->xattr_sem); +} + +static int check_xattr_ref_ilist(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + /* success of check_xattr_ref_ilist() means taht inode (ic) dose not have + * duplicate name/value pairs. If duplicate name/value pair would be found, + * one will be removed. + */ + struct jffs2_xattr_ref *ref, *cmp; + int rc = 0; + + if (likely(ic->flags & INO_FLAGS_XATTR_CHECKED)) + return 0; + down_write(&c->xattr_sem); + retry: + rc = 0; + list_for_each_entry(ref, &ic->ilist, ilist) { + if (!ref->xd->xname) { + rc = load_xattr_datum(c, ref->xd); + if (unlikely(rc > 0)) { + delete_xattr_ref(c, ref); + goto retry; + } else if (unlikely(rc < 0)) + goto out; + } + cmp = ref; + list_for_each_entry_continue(cmp, &ic->ilist, ilist) { + if (!cmp->xd->xname) { + ref->xd->flags |= JFFS2_XFLAGS_BIND; + rc = load_xattr_datum(c, cmp->xd); + ref->xd->flags &= ~JFFS2_XFLAGS_BIND; + if (unlikely(rc > 0)) { + delete_xattr_ref(c, cmp); + goto retry; + } else if (unlikely(rc < 0)) + goto out; + } + if (ref->xd->xprefix == cmp->xd->xprefix + && !strcmp(ref->xd->xname, cmp->xd->xname)) { + delete_xattr_ref(c, cmp); + goto retry; + } + } + } + ic->flags |= INO_FLAGS_XATTR_CHECKED; + out: + up_write(&c->xattr_sem); + + return rc; +} + +/* -------- xattr subsystem functions --------------- + * jffs2_init_xattr_subsystem(c) + * is used to initialize semaphore and list_head, and some variables. + * jffs2_find_xattr_datum(c, xid) + * is used to lookup xdatum while scanning process. + * jffs2_clear_xattr_subsystem(c) + * is used to release any xattr related objects. + * jffs2_build_xattr_subsystem(c) + * is used to associate xdatum and xref while super block building process. + * jffs2_setup_xattr_datum(c, xid, version) + * is used to insert xdatum while scanning process. + * -------------------------------------------------- */ +void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c) +{ + int i; + + for (i=0; i < XATTRINDEX_HASHSIZE; i++) + INIT_LIST_HEAD(&c->xattrindex[i]); + INIT_LIST_HEAD(&c->xattr_temp); + INIT_LIST_HEAD(&c->xattr_unchecked); + + init_rwsem(&c->xattr_sem); + c->xdatum_mem_usage = 0; + c->xdatum_mem_threshold = 32 * 1024; /* Default 32KB */ +} + +static struct jffs2_xattr_datum *jffs2_find_xattr_datum(struct jffs2_sb_info *c, uint32_t xid) +{ + struct jffs2_xattr_datum *xd; + int i = xid % XATTRINDEX_HASHSIZE; + + /* It's only used in scanning/building process. */ + BUG_ON(!(c->flags & (JFFS2_SB_FLAG_SCANNING|JFFS2_SB_FLAG_BUILDING))); + + list_for_each_entry(xd, &c->xattrindex[i], xindex) { + if (xd->xid==xid) + return xd; + } + return NULL; +} + +void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c) +{ + struct jffs2_xattr_datum *xd, *_xd; + struct jffs2_xattr_ref *ref, *_ref; + int i; + + list_for_each_entry_safe(ref, _ref, &c->xattr_temp, ilist) + jffs2_free_xattr_ref(ref); + + for (i=0; i < XATTRINDEX_HASHSIZE; i++) { + list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) { + list_del(&xd->xindex); + if (xd->xname) + kfree(xd->xname); + jffs2_free_xattr_datum(xd); + } + } +} + +void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) +{ + struct jffs2_xattr_ref *ref, *_ref; + struct jffs2_xattr_datum *xd, *_xd; + struct jffs2_inode_cache *ic; + int i, xdatum_count =0, xdatum_unchecked_count = 0, xref_count = 0; + + BUG_ON(!(c->flags & JFFS2_SB_FLAG_BUILDING)); + + /* Phase.1 */ + list_for_each_entry_safe(ref, _ref, &c->xattr_temp, ilist) { + list_del_init(&ref->ilist); + /* checking REF_UNCHECKED nodes */ + if (ref_flags(ref->node) != REF_PRISTINE) { + if (verify_xattr_ref(c, ref)) { + delete_xattr_ref_node(c, ref); + jffs2_free_xattr_ref(ref); + continue; + } + } + /* At this point, ref->xid and ref->ino contain XID and inode number. + ref->xd and ref->ic are not valid yet. */ + xd = jffs2_find_xattr_datum(c, ref->xid); + ic = jffs2_get_ino_cache(c, ref->ino); + if (!xd || !ic) { + if (ref_flags(ref->node) != REF_UNCHECKED) + JFFS2_WARNING("xref(ino=%u, xid=%u) is orphan. \n", + ref->ino, ref->xid); + delete_xattr_ref_node(c, ref); + jffs2_free_xattr_ref(ref); + continue; + } + ref->xd = xd; + ref->ic = ic; + xd->refcnt++; + list_add_tail(&ref->ilist, &ic->ilist); + xref_count++; + } + /* After this, ref->xid/ino are NEVER used. */ + + /* Phase.2 */ + for (i=0; i < XATTRINDEX_HASHSIZE; i++) { + list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) { + list_del_init(&xd->xindex); + if (!xd->refcnt) { + if (ref_flags(xd->node) != REF_UNCHECKED) + JFFS2_WARNING("orphan xdatum(xid=%u, version=%u) at %#08x\n", + xd->xid, xd->version, ref_offset(xd->node)); + delete_xattr_datum(c, xd); + continue; + } + if (ref_flags(xd->node) != REF_PRISTINE) { + dbg_xattr("unchecked xdatum(xid=%u) at %#08x\n", + xd->xid, ref_offset(xd->node)); + list_add(&xd->xindex, &c->xattr_unchecked); + xdatum_unchecked_count++; + } + xdatum_count++; + } + } + /* build complete */ + JFFS2_NOTICE("complete building xattr subsystem, %u of xdatum (%u unchecked) and " + "%u of xref found.\n", xdatum_count, xdatum_unchecked_count, xref_count); +} + +struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c, + uint32_t xid, uint32_t version) +{ + struct jffs2_xattr_datum *xd, *_xd; + + _xd = jffs2_find_xattr_datum(c, xid); + if (_xd) { + dbg_xattr("duplicate xdatum (xid=%u, version=%u/%u) at %#08x\n", + xid, version, _xd->version, ref_offset(_xd->node)); + if (version < _xd->version) + return ERR_PTR(-EEXIST); + } + xd = jffs2_alloc_xattr_datum(); + if (!xd) + return ERR_PTR(-ENOMEM); + xd->xid = xid; + xd->version = version; + if (xd->xid > c->highest_xid) + c->highest_xid = xd->xid; + list_add_tail(&xd->xindex, &c->xattrindex[xid % XATTRINDEX_HASHSIZE]); + + if (_xd) { + list_del_init(&_xd->xindex); + delete_xattr_datum_node(c, _xd); + jffs2_free_xattr_datum(_xd); + } + return xd; +} + +/* -------- xattr subsystem functions --------------- + * xprefix_to_handler(xprefix) + * is used to translate xprefix into xattr_handler. + * jffs2_listxattr(dentry, buffer, size) + * is an implementation of listxattr handler on jffs2. + * do_jffs2_getxattr(inode, xprefix, xname, buffer, size) + * is an implementation of getxattr handler on jffs2. + * do_jffs2_setxattr(inode, xprefix, xname, buffer, size, flags) + * is an implementation of setxattr handler on jffs2. + * -------------------------------------------------- */ +struct xattr_handler *jffs2_xattr_handlers[] = { + &jffs2_user_xattr_handler, +#ifdef CONFIG_JFFS2_FS_SECURITY + &jffs2_security_xattr_handler, +#endif +#ifdef CONFIG_JFFS2_FS_POSIX_ACL + &jffs2_acl_access_xattr_handler, + &jffs2_acl_default_xattr_handler, +#endif + &jffs2_trusted_xattr_handler, + NULL +}; + +static struct xattr_handler *xprefix_to_handler(int xprefix) { + struct xattr_handler *ret; + + switch (xprefix) { + case JFFS2_XPREFIX_USER: + ret = &jffs2_user_xattr_handler; + break; +#ifdef CONFIG_JFFS2_FS_SECURITY + case JFFS2_XPREFIX_SECURITY: + ret = &jffs2_security_xattr_handler; + break; +#endif +#ifdef CONFIG_JFFS2_FS_POSIX_ACL + case JFFS2_XPREFIX_ACL_ACCESS: + ret = &jffs2_acl_access_xattr_handler; + break; + case JFFS2_XPREFIX_ACL_DEFAULT: + ret = &jffs2_acl_default_xattr_handler; + break; +#endif + case JFFS2_XPREFIX_TRUSTED: + ret = &jffs2_trusted_xattr_handler; + break; + default: + ret = NULL; + break; + } + return ret; +} + +ssize_t jffs2_listxattr(struct dentry *dentry, char *buffer, size_t size) +{ + struct inode *inode = dentry->d_inode; + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_inode_cache *ic = f->inocache; + struct jffs2_xattr_ref *ref; + struct jffs2_xattr_datum *xd; + struct xattr_handler *xhandle; + ssize_t len, rc; + int retry = 0; + + rc = check_xattr_ref_ilist(c, ic); + if (unlikely(rc)) + return rc; + + down_read(&c->xattr_sem); + retry: + len = 0; + list_for_each_entry(ref, &ic->ilist, ilist) { + BUG_ON(ref->ic != ic); + xd = ref->xd; + if (!xd->xname) { + /* xdatum is unchached */ + if (!retry) { + retry = 1; + up_read(&c->xattr_sem); + down_write(&c->xattr_sem); + goto retry; + } else { + rc = load_xattr_datum(c, xd); + if (unlikely(rc > 0)) { + delete_xattr_ref(c, ref); + goto retry; + } else if (unlikely(rc < 0)) + goto out; + } + } + xhandle = xprefix_to_handler(xd->xprefix); + if (!xhandle) + continue; + if (buffer) { + rc = xhandle->list(inode, buffer+len, size-len, xd->xname, xd->name_len); + } else { + rc = xhandle->list(inode, NULL, 0, xd->xname, xd->name_len); + } + if (rc < 0) + goto out; + len += rc; + } + rc = len; + out: + if (!retry) { + up_read(&c->xattr_sem); + } else { + up_write(&c->xattr_sem); + } + return rc; +} + +int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname, + char *buffer, size_t size) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_inode_cache *ic = f->inocache; + struct jffs2_xattr_datum *xd; + struct jffs2_xattr_ref *ref; + int rc, retry = 0; + + rc = check_xattr_ref_ilist(c, ic); + if (unlikely(rc)) + return rc; + + down_read(&c->xattr_sem); + retry: + list_for_each_entry(ref, &ic->ilist, ilist) { + BUG_ON(ref->ic!=ic); + + xd = ref->xd; + if (xd->xprefix != xprefix) + continue; + if (!xd->xname) { + /* xdatum is unchached */ + if (!retry) { + retry = 1; + up_read(&c->xattr_sem); + down_write(&c->xattr_sem); + goto retry; + } else { + rc = load_xattr_datum(c, xd); + if (unlikely(rc > 0)) { + delete_xattr_ref(c, ref); + goto retry; + } else if (unlikely(rc < 0)) { + goto out; + } + } + } + if (!strcmp(xname, xd->xname)) { + rc = xd->value_len; + if (buffer) { + if (size < rc) { + rc = -ERANGE; + } else { + memcpy(buffer, xd->xvalue, rc); + } + } + goto out; + } + } + rc = -ENODATA; + out: + if (!retry) { + up_read(&c->xattr_sem); + } else { + up_write(&c->xattr_sem); + } + return rc; +} + +int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, + const char *buffer, size_t size, int flags) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_inode_cache *ic = f->inocache; + struct jffs2_xattr_datum *xd; + struct jffs2_xattr_ref *ref, *newref; + uint32_t phys_ofs, length, request; + int rc; + + rc = check_xattr_ref_ilist(c, ic); + if (unlikely(rc)) + return rc; + + request = PAD(sizeof(struct jffs2_raw_xattr) + strlen(xname) + 1 + size); + rc = jffs2_reserve_space(c, request, &phys_ofs, &length, + ALLOC_NORMAL, JFFS2_SUMMARY_XATTR_SIZE); + if (rc) { + JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request); + return rc; + } + + /* Find existing xattr */ + down_write(&c->xattr_sem); + retry: + list_for_each_entry(ref, &ic->ilist, ilist) { + xd = ref->xd; + if (xd->xprefix != xprefix) + continue; + if (!xd->xname) { + rc = load_xattr_datum(c, xd); + if (unlikely(rc > 0)) { + delete_xattr_ref(c, ref); + goto retry; + } else if (unlikely(rc < 0)) + goto out; + } + if (!strcmp(xd->xname, xname)) { + if (flags & XATTR_CREATE) { + rc = -EEXIST; + goto out; + } + if (!buffer) { + delete_xattr_ref(c, ref); + rc = 0; + goto out; + } + goto found; + } + } + /* not found */ + ref = NULL; + if (flags & XATTR_REPLACE) { + rc = -ENODATA; + goto out; + } + if (!buffer) { + rc = -EINVAL; + goto out; + } + found: + xd = create_xattr_datum(c, xprefix, xname, buffer, size, phys_ofs); + if (IS_ERR(xd)) { + rc = PTR_ERR(xd); + goto out; + } + up_write(&c->xattr_sem); + jffs2_complete_reservation(c); + + /* create xattr_ref */ + request = PAD(sizeof(struct jffs2_raw_xref)); + rc = jffs2_reserve_space(c, request, &phys_ofs, &length, + ALLOC_NORMAL, JFFS2_SUMMARY_XREF_SIZE); + if (rc) { + JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request); + down_write(&c->xattr_sem); + xd->refcnt--; + if (!xd->refcnt) + delete_xattr_datum(c, xd); + up_write(&c->xattr_sem); + return rc; + } + down_write(&c->xattr_sem); + newref = create_xattr_ref(c, ic, xd, phys_ofs); + if (IS_ERR(newref)) { + rc = PTR_ERR(newref); + xd->refcnt--; + if (!xd->refcnt) + delete_xattr_datum(c, xd); + } else if (ref) { + /* If replaced xattr_ref exists */ + delete_xattr_ref(c, ref); + } + out: + up_write(&c->xattr_sem); + jffs2_complete_reservation(c); + return rc; +} + +/* -------- garbage collector functions ------------- + * jffs2_garbage_collect_xattr_datum(c, xd) + * is used to move xdatum into new node. + * jffs2_garbage_collect_xattr_ref(c, ref) + * is used to move xref into new node. + * jffs2_garbage_collect_xattr(c, ic) + * is used to call appropriate garbage collector function, if argument + * pointer (ic) is the reference of xdatum/xref. + * jffs2_verify_xattr(c) + * is used to call do_verify_xattr_datum() before garbage collecting. + * -------------------------------------------------- */ +static int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, + struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem), and called from GC thread */ + uint32_t phys_ofs, totlen, length, old_ofs; + int rc; + + BUG_ON(!xd->node); + + old_ofs = ref_offset(xd->node); + totlen = ref_totlen(c, c->gcblock, xd->node); + if (totlen < sizeof(struct jffs2_raw_xattr)) + return -EINVAL; + + if (!xd->xname) { + rc = load_xattr_datum(c, xd); + if (unlikely(rc > 0)) { + delete_xattr_datum_node(c, xd); + return 0; + } else if (unlikely(rc < 0)) + return -EINVAL; + } + rc = jffs2_reserve_space_gc(c, totlen, &phys_ofs, &length, JFFS2_SUMMARY_XATTR_SIZE); + if (rc || length < totlen) { + JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, totlen); + return rc ? rc : -EBADFD; + } + rc = save_xattr_datum(c, xd, phys_ofs); + if (!rc) + dbg_xattr("xdatum (xid=%u, version=%u) GC'ed from %#08x to %08x\n", + xd->xid, xd->version, old_ofs, ref_offset(xd->node)); + return rc; +} + + +static int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, + struct jffs2_xattr_ref *ref) +{ + /* must be called under down(alloc_sem) */ + uint32_t phys_ofs, totlen, length, old_ofs; + int rc; + + BUG_ON(!ref->node); + + old_ofs = ref_offset(ref->node); + totlen = ref_totlen(c, c->gcblock, ref->node); + if (totlen != sizeof(struct jffs2_raw_xref)) + return -EINVAL; + rc = jffs2_reserve_space_gc(c, totlen, &phys_ofs, &length, JFFS2_SUMMARY_XREF_SIZE); + if (rc || length < totlen) { + JFFS2_WARNING("%s: jffs2_reserve_space() = %d, request = %u\n", + __FUNCTION__, rc, totlen); + return rc ? rc : -EBADFD; + } + rc = save_xattr_ref(c, ref, phys_ofs); + if (!rc) + dbg_xattr("xref (ino=%u, xid=%u) GC'ed from %#08x to %08x\n", + ref->ic->ino, ref->xd->xid, old_ofs, ref_offset(ref->node)); + return rc; +} + +int jffs2_garbage_collect_xattr(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + struct jffs2_xattr_datum *xd; + struct jffs2_xattr_ref *ref; + int ret; + + switch (ic->class) { + case RAWNODE_CLASS_XATTR_DATUM: + spin_unlock(&c->erase_completion_lock); + + down_write(&c->xattr_sem); + xd = (struct jffs2_xattr_datum *)ic; + ret = xd ? jffs2_garbage_collect_xattr_datum(c, xd) : 0; + up_write(&c->xattr_sem); + break; + case RAWNODE_CLASS_XATTR_REF: + spin_unlock(&c->erase_completion_lock); + + down_write(&c->xattr_sem); + ref = (struct jffs2_xattr_ref *)ic; + ret = ref ? jffs2_garbage_collect_xattr_ref(c, ref) : 0; + up_write(&c->xattr_sem); + break; + default: + /* This node is not xattr_datum/xattr_ref */ + ret = 1; + break; + } + return ret; +} + +int jffs2_verify_xattr(struct jffs2_sb_info *c) +{ + struct jffs2_xattr_datum *xd, *_xd; + int rc; + + down_write(&c->xattr_sem); + list_for_each_entry_safe(xd, _xd, &c->xattr_unchecked, xindex) { + rc = do_verify_xattr_datum(c, xd); + if (rc == 0) { + list_del_init(&xd->xindex); + break; + } else if (rc > 0) { + list_del_init(&xd->xindex); + delete_xattr_datum_node(c, xd); + } + } + up_write(&c->xattr_sem); + + return list_empty(&c->xattr_unchecked) ? 1 : 0; +} diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h new file mode 100644 index 000000000000..d157ad641ed4 --- /dev/null +++ b/fs/jffs2/xattr.h @@ -0,0 +1,120 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/xattr.c + * XATTR support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei + * Copyright (C) 2006 NEC Corporation + * + * For licensing information, see the file 'LICENCE' in the jffs2 directory. + *-------------------------------------------------------------------------*/ + +#ifndef _JFFS2_FS_XATTR_H_ +#define _JFFS2_FS_XATTR_H_ + +#include + +#define JFFS2_XFLAGS_HOT (0x01) /* This datum is HOT */ +#define JFFS2_XFLAGS_BIND (0x02) /* This datum is not reclaimed */ + +struct jffs2_xattr_datum +{ + void *always_null; + u8 class; + u8 flags; + u16 xprefix; /* see JFFS2_XATTR_PREFIX_* */ + + struct jffs2_raw_node_ref *node; + struct list_head xindex; /* chained from c->xattrindex[n] */ + uint32_t refcnt; /* # of xattr_ref refers this */ + uint32_t xid; + uint32_t version; + + uint32_t data_crc; + uint32_t hashkey; + char *xname; /* XATTR name without prefix */ + uint32_t name_len; /* length of xname */ + char *xvalue; /* XATTR value */ + uint32_t value_len; /* length of xvalue */ +}; + +struct jffs2_inode_cache; /* forward refence */ +struct jffs2_xattr_ref +{ + void *always_null; + u8 class; + u8 flags; /* Currently unused */ + u16 unused; + + struct jffs2_raw_node_ref *node; + union { + struct jffs2_inode_cache *ic; /* reference to jffs2_inode_cache */ + uint32_t ino; /* only used in scanning/building */ + }; + union { + struct jffs2_xattr_datum *xd; /* reference to jffs2_xattr_datum */ + uint32_t xid; /* only used in sccanning/building */ + }; + struct list_head ilist; /* chained from ic->ilist */ +}; + +#ifdef CONFIG_JFFS2_FS_XATTR + +extern void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c); +extern void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c); +extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c); + +extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c, + uint32_t xid, uint32_t version); + +extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); +extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); + +extern int jffs2_garbage_collect_xattr(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); +extern int jffs2_verify_xattr(struct jffs2_sb_info *c); + +extern int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname, + char *buffer, size_t size); +extern int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, + const char *buffer, size_t size, int flags); + +extern struct xattr_handler *jffs2_xattr_handlers[]; +extern struct xattr_handler jffs2_user_xattr_handler; +extern struct xattr_handler jffs2_trusted_xattr_handler; + +extern ssize_t jffs2_listxattr(struct dentry *, char *, size_t); +#define jffs2_getxattr generic_getxattr +#define jffs2_setxattr generic_setxattr +#define jffs2_removexattr generic_removexattr + +/*---- Any inline initialize functions ----*/ +#define init_xattr_inode_cache(x) INIT_LIST_HEAD(&((x)->ilist)) + +#else + +#define jffs2_init_xattr_subsystem(c) +#define jffs2_build_xattr_subsystem(c) +#define jffs2_clear_xattr_subsystem(c) + +#define jffs2_xattr_delete_inode(c, ic) +#define jffs2_xattr_free_inode(c, ic) +#define jffs2_garbage_collect_xattr(c, ic) (1) +#define jffs2_verify_xattr(c) (1) + +#define jffs2_xattr_handlers NULL +#define jffs2_listxattr NULL +#define jffs2_getxattr NULL +#define jffs2_setxattr NULL +#define jffs2_removexattr NULL + +#define init_xattr_inode_cache(x) + +#endif /* CONFIG_JFFS2_FS_XATTR */ + +#ifdef CONFIG_JFFS2_FS_SECURITY +extern int jffs2_init_security(struct inode *inode, struct inode *dir); +extern struct xattr_handler jffs2_security_xattr_handler; +#else +#define jffs2_init_security(inode,dir) (0) +#endif /* CONFIG_JFFS2_FS_SECURITY */ + +#endif /* _JFFS2_FS_XATTR_H_ */ diff --git a/fs/jffs2/xattr_trusted.c b/fs/jffs2/xattr_trusted.c new file mode 100644 index 000000000000..a018c9c31a60 --- /dev/null +++ b/fs/jffs2/xattr_trusted.c @@ -0,0 +1,51 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/xattr_trusted.c + * XATTR support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei + * Copyright (C) 2006 NEC Corporation + * + * For licensing information, see the file 'LICENCE' in the jffs2 directory. + *-------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include "nodelist.h" + +static int jffs2_trusted_getxattr(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (!strcmp(name, "")) + return -EINVAL; + return do_jffs2_getxattr(inode, JFFS2_XPREFIX_TRUSTED, name, buffer, size); +} + +static int jffs2_trusted_setxattr(struct inode *inode, const char *name, const void *buffer, + size_t size, int flags) +{ + if (!strcmp(name, "")) + return -EINVAL; + return do_jffs2_setxattr(inode, JFFS2_XPREFIX_TRUSTED, name, buffer, size, flags); +} + +static size_t jffs2_trusted_listxattr(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + size_t retlen = XATTR_TRUSTED_PREFIX_LEN + name_len + 1; + + if (list && retlen<=list_size) { + strcpy(list, XATTR_TRUSTED_PREFIX); + strcpy(list + XATTR_TRUSTED_PREFIX_LEN, name); + } + + return retlen; +} + +struct xattr_handler jffs2_trusted_xattr_handler = { + .prefix = XATTR_TRUSTED_PREFIX, + .list = jffs2_trusted_listxattr, + .set = jffs2_trusted_setxattr, + .get = jffs2_trusted_getxattr +}; diff --git a/fs/jffs2/xattr_user.c b/fs/jffs2/xattr_user.c new file mode 100644 index 000000000000..d8c13636ea4c --- /dev/null +++ b/fs/jffs2/xattr_user.c @@ -0,0 +1,51 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/xattr_user.c + * XATTR support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei + * Copyright (C) 2006 NEC Corporation + * + * For licensing information, see the file 'LICENCE' in the jffs2 directory. + *-------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include "nodelist.h" + +static int jffs2_user_getxattr(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (!strcmp(name, "")) + return -EINVAL; + return do_jffs2_getxattr(inode, JFFS2_XPREFIX_USER, name, buffer, size); +} + +static int jffs2_user_setxattr(struct inode *inode, const char *name, const void *buffer, + size_t size, int flags) +{ + if (!strcmp(name, "")) + return -EINVAL; + return do_jffs2_setxattr(inode, JFFS2_XPREFIX_USER, name, buffer, size, flags); +} + +static size_t jffs2_user_listxattr(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + size_t retlen = XATTR_USER_PREFIX_LEN + name_len + 1; + + if (list && retlen <= list_size) { + strcpy(list, XATTR_USER_PREFIX); + strcpy(list + XATTR_USER_PREFIX_LEN, name); + } + + return retlen; +} + +struct xattr_handler jffs2_user_xattr_handler = { + .prefix = XATTR_USER_PREFIX, + .list = jffs2_user_listxattr, + .set = jffs2_user_setxattr, + .get = jffs2_user_getxattr +}; diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index cf792bb3c726..2cac60e55322 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -65,6 +65,18 @@ #define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6) +#define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8) +#define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9) + +/* XATTR Related */ +#define JFFS2_XPREFIX_USER 1 /* for "user." */ +#define JFFS2_XPREFIX_SECURITY 2 /* for "security." */ +#define JFFS2_XPREFIX_ACL_ACCESS 3 /* for "system.posix_acl_access" */ +#define JFFS2_XPREFIX_ACL_DEFAULT 4 /* for "system.posix_acl_default" */ +#define JFFS2_XPREFIX_TRUSTED 5 /* for "trusted.*" */ + +#define JFFS2_ACL_VERSION 0x0001 + // Maybe later... //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4) @@ -151,6 +163,32 @@ struct jffs2_raw_inode uint8_t data[0]; } __attribute__((packed)); +struct jffs2_raw_xattr { + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_XATTR */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t xid; /* XATTR identifier number */ + jint32_t version; + uint8_t xprefix; + uint8_t name_len; + jint16_t value_len; + jint32_t data_crc; + jint32_t node_crc; + uint8_t data[0]; +} __attribute__((packed)); + +struct jffs2_raw_xref +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_XREF */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t ino; /* inode number */ + jint32_t xid; /* XATTR identifier number */ + jint32_t node_crc; +} __attribute__((packed)); + struct jffs2_raw_summary { jint16_t magic; @@ -169,6 +207,8 @@ union jffs2_node_union { struct jffs2_raw_inode i; struct jffs2_raw_dirent d; + struct jffs2_raw_xattr x; + struct jffs2_raw_xref r; struct jffs2_raw_summary s; struct jffs2_unknown_node u; }; -- cgit v1.2.3 From 151e76590f66f5406eb2e1f4270c5323f385d2e8 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 14 May 2006 01:51:54 +0100 Subject: [MTD] Fix legacy character sets throughout drivers/mtd, include/linux/mtd Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 2 +- drivers/mtd/chips/cfi_probe.c | 8 ++++---- drivers/mtd/devices/block2mtd.c | 2 +- drivers/mtd/devices/phram.c | 6 +++--- drivers/mtd/maps/cfi_flagadm.c | 4 ++-- drivers/mtd/maps/dbox2-flash.c | 2 +- drivers/mtd/maps/mtx-1_flash.c | 2 +- drivers/mtd/nand/edb7312.c | 2 +- drivers/mtd/nand/h1910.c | 2 +- drivers/mtd/nand/ts7250.c | 2 +- include/linux/mtd/mtd.h | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index fe00af3f9195..898c321ab86d 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -1475,7 +1475,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, ENABLE_VPP(map); xip_disable(map, chip, cmd_adr); - /* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set + /* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set [...], the device will not accept any more Write to Buffer commands". So we must check here and reset those bits if they're set. Otherwise we're just pissing in the wind */ diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index e636aa86bc24..4bf9f8cac0dd 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -349,12 +349,12 @@ static void print_cfi_ident(struct cfi_ident *cfip) else printk("No Vpp line\n"); - printk("Typical byte/word write timeout: %d µs\n", 1<WordWriteTimeoutTyp); - printk("Maximum byte/word write timeout: %d µs\n", (1<WordWriteTimeoutMax) * (1<WordWriteTimeoutTyp)); + printk("Typical byte/word write timeout: %d µs\n", 1<WordWriteTimeoutTyp); + printk("Maximum byte/word write timeout: %d µs\n", (1<WordWriteTimeoutMax) * (1<WordWriteTimeoutTyp)); if (cfip->BufWriteTimeoutTyp || cfip->BufWriteTimeoutMax) { - printk("Typical full buffer write timeout: %d µs\n", 1<BufWriteTimeoutTyp); - printk("Maximum full buffer write timeout: %d µs\n", (1<BufWriteTimeoutMax) * (1<BufWriteTimeoutTyp)); + printk("Typical full buffer write timeout: %d µs\n", 1<BufWriteTimeoutTyp); + printk("Maximum full buffer write timeout: %d µs\n", (1<BufWriteTimeoutMax) * (1<BufWriteTimeoutTyp)); } else printk("Full buffer write not supported\n"); diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 45606921364b..8ca04f4e03f0 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -4,7 +4,7 @@ * block2mtd.c - create an mtd from a block device * * Copyright (C) 2001,2002 Simon Evans - * Copyright (C) 2004-2006 Jörn Engel + * Copyright (C) 2004-2006 Jörn Engel * * Licence: GPL */ diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 68d39cc9df71..e09e416667d3 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -1,8 +1,8 @@ /** * $Id: phram.c,v 1.16 2005/11/07 11:14:25 gleixner Exp $ * - * Copyright (c) ???? Jochen Schäuble - * Copyright (c) 2003-2004 Jörn Engel + * Copyright (c) ???? Jochen Schäuble + * Copyright (c) 2003-2004 Jörn Engel * * Usage: * @@ -300,5 +300,5 @@ module_init(init_phram); module_exit(cleanup_phram); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jörn Engel "); +MODULE_AUTHOR("Jörn Engel "); MODULE_DESCRIPTION("MTD driver for physical RAM"); diff --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c index fd0f0d3187de..92b5d883d7b0 100644 --- a/drivers/mtd/maps/cfi_flagadm.c +++ b/drivers/mtd/maps/cfi_flagadm.c @@ -1,5 +1,5 @@ /* - * Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson + * Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson * * $Id: cfi_flagadm.c,v 1.15 2005/11/07 11:14:26 gleixner Exp $ * @@ -135,5 +135,5 @@ module_exit(cleanup_flagadm); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Kári Davíðsson "); +MODULE_AUTHOR("Kári Davíðsson "); MODULE_DESCRIPTION("MTD map driver for Flaga digital module"); diff --git a/drivers/mtd/maps/dbox2-flash.c b/drivers/mtd/maps/dbox2-flash.c index 652813cd6c2d..85c2a9e22b1e 100644 --- a/drivers/mtd/maps/dbox2-flash.c +++ b/drivers/mtd/maps/dbox2-flash.c @@ -122,5 +122,5 @@ module_exit(cleanup_dbox2_flash); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Kári Davíðsson , Bastian Blank , Alexander Wild "); +MODULE_AUTHOR("Kári Davíðsson , Bastian Blank , Alexander Wild "); MODULE_DESCRIPTION("MTD map driver for D-Box 2 board"); diff --git a/drivers/mtd/maps/mtx-1_flash.c b/drivers/mtd/maps/mtx-1_flash.c index d1e66e186746..5c25d4e552c6 100644 --- a/drivers/mtd/maps/mtx-1_flash.c +++ b/drivers/mtd/maps/mtx-1_flash.c @@ -4,7 +4,7 @@ * $Id: mtx-1_flash.c,v 1.2 2005/11/07 11:14:27 gleixner Exp $ * * (C) 2005 Bruno Randolf - * (C) 2005 Jörn Engel + * (C) 2005 Jörn Engel * */ diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c index ad4488abfb0b..8e56570af91f 100644 --- a/drivers/mtd/nand/edb7312.c +++ b/drivers/mtd/nand/edb7312.c @@ -1,7 +1,7 @@ /* * drivers/mtd/nand/edb7312.c * - * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) + * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) * * Derived from drivers/mtd/nand/autcpu12.c * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c index b47a15c23d1c..9848eb09b884 100644 --- a/drivers/mtd/nand/h1910.c +++ b/drivers/mtd/nand/h1910.c @@ -4,7 +4,7 @@ * Copyright (C) 2003 Joshua Wise (joshua@joshuawise.com) * * Derived from drivers/mtd/nand/edb7312.c - * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) + * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * * $Id: h1910.c,v 1.6 2005/11/07 11:14:30 gleixner Exp $ diff --git a/drivers/mtd/nand/ts7250.c b/drivers/mtd/nand/ts7250.c index 756ef64b0efd..622db3127f7c 100644 --- a/drivers/mtd/nand/ts7250.c +++ b/drivers/mtd/nand/ts7250.c @@ -4,7 +4,7 @@ * Copyright (C) 2004 Technologic Systems (support@embeddedARM.com) * * Derived from drivers/mtd/nand/edb7312.c - * Copyright (C) 2004 Marius Gröger (mag@sysgo.de) + * Copyright (C) 2004 Marius Gröger (mag@sysgo.de) * * Derived from drivers/mtd/nand/autcpu12.c * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index b6f2fdae65c6..73620ef83364 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -61,7 +61,7 @@ struct mtd_info { u_int32_t flags; u_int32_t size; // Total size of the MTD - /* "Major" erase size for the device. Naïve users may take this + /* "Major" erase size for the device. Naïve users may take this * to be the only erase size available, or may use the more detailed * information below if they desire */ -- cgit v1.2.3 From 0d4e30d26a279f1b6a008a233a6835ad2af571e4 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 14 May 2006 12:25:19 +0100 Subject: [MTD] Clean up to fix modular build ... and also fix the multiple inclusion guard so it actually _works_ Signed-off-by: David Woodhouse --- include/linux/mtd/physmap.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/mtd/physmap.h b/include/linux/mtd/physmap.h index 50f954461aa8..86831e3594f6 100644 --- a/include/linux/mtd/physmap.h +++ b/include/linux/mtd/physmap.h @@ -15,10 +15,7 @@ */ #ifndef __LINUX_MTD_PHYSMAP__ - -#include - -#if defined(CONFIG_MTD_PHYSMAP) +#define __LINUX_MTD_PHYSMAP__ #include #include @@ -37,7 +34,7 @@ struct physmap_flash_data { void physmap_configure(unsigned long addr, unsigned long size, int bankwidth, void (*set_vpp)(struct map_info *, int) ); -#if defined(CONFIG_MTD_PARTITIONS) +#ifdef CONFIG_MTD_PARTITIONS /* * Machines that wish to do flash partition may want to call this function in @@ -51,6 +48,5 @@ void physmap_configure(unsigned long addr, unsigned long size, void physmap_set_partitions(struct mtd_partition *parts, int num_parts); #endif /* defined(CONFIG_MTD_PARTITIONS) */ -#endif /* defined(CONFIG_MTD) */ #endif /* __LINUX_MTD_PHYSMAP__ */ -- cgit v1.2.3 From 3e68fbb59b3d4e6b47b65e9928b5929e02179759 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 15 May 2006 00:49:43 +0100 Subject: [JFFS2] Don't pack on-medium structures, because GCC emits crappy code If we use __attribute__((packed)), GCC will _also_ assume that the structures aren't sensibly aligned, and it'll emit code to cope with that instead of straight word load/save. This can be _very_ suboptimal on architectures like ARM. Ideally, we want an attribute which just tells GCC not to do any padding, without the alignment side-effects. In the absense of that, we'll just drop the 'packed' attribute and hope that everything stays as it was (which to be fair is fairly much what we expect). And add some paranoia checks in the initialisation code, which should be optimised away completely in the normal case. Signed-off-by: David Woodhouse --- fs/jffs2/super.c | 14 +++++++++++++- include/linux/jffs2.h | 14 +++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index ffd8e84b22cc..5f73de586928 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -320,6 +320,18 @@ static int __init init_jffs2_fs(void) { int ret; + /* Paranoia checks for on-medium structures. If we ask GCC + to pack them with __attribute__((packed)) then it _also_ + assumes that they're not aligned -- so it emits crappy + code on some architectures. Ideally we want an attribute + which means just 'no padding', without the alignment + thing. But GCC doesn't have that -- we have to just + hope the structs are the right sizes, instead. */ + BUG_ON(sizeof(struct jffs2_unknown_node) != 12); + BUG_ON(sizeof(struct jffs2_raw_dirent) != 40); + BUG_ON(sizeof(struct jffs2_raw_inode) != 68); + BUG_ON(sizeof(struct jffs2_raw_summary) != 32); + printk(KERN_INFO "JFFS2 version 2.2." #ifdef CONFIG_JFFS2_FS_WRITEBUFFER " (NAND)" @@ -327,7 +339,7 @@ static int __init init_jffs2_fs(void) #ifdef CONFIG_JFFS2_SUMMARY " (SUMMARY) " #endif - " (C) 2001-2003 Red Hat, Inc.\n"); + " (C) 2001-2006 Red Hat, Inc.\n"); jffs2_inode_cachep = kmem_cache_create("jffs2_i", sizeof(struct jffs2_inode_info), diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index cf792bb3c726..228ad72f7dd8 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -82,15 +82,15 @@ typedef struct { uint32_t v32; -} __attribute__((packed)) jint32_t; +} jint32_t; typedef struct { uint32_t m; -} __attribute__((packed)) jmode_t; +} jmode_t; typedef struct { uint16_t v16; -} __attribute__((packed)) jint16_t; +} jint16_t; struct jffs2_unknown_node { @@ -99,7 +99,7 @@ struct jffs2_unknown_node jint16_t nodetype; jint32_t totlen; /* So we can skip over nodes we don't grok */ jint32_t hdr_crc; -} __attribute__((packed)); +}; struct jffs2_raw_dirent { @@ -117,7 +117,7 @@ struct jffs2_raw_dirent jint32_t node_crc; jint32_t name_crc; uint8_t name[0]; -} __attribute__((packed)); +}; /* The JFFS2 raw inode structure: Used for storage on physical media. */ /* The uid, gid, atime, mtime and ctime members could be longer, but @@ -149,7 +149,7 @@ struct jffs2_raw_inode jint32_t data_crc; /* CRC for the (compressed) data. */ jint32_t node_crc; /* CRC for the raw inode (excluding data) */ uint8_t data[0]; -} __attribute__((packed)); +}; struct jffs2_raw_summary { @@ -163,7 +163,7 @@ struct jffs2_raw_summary jint32_t sum_crc; /* summary information crc */ jint32_t node_crc; /* node crc */ jint32_t sum[0]; /* inode summary info */ -} __attribute__((packed)); +}; union jffs2_node_union { -- cgit v1.2.3 From ba9627b85fcb5ed67285ca0711f0f4d1e965746e Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 16 May 2006 23:03:08 +0100 Subject: [JFFS2] Repack some on-medium structures. ARM is weirder than I thought. We have to pack at least the jint16_t structure, because otherwise it'll be four bytes in size. Thankfully, we can do that and _not_ pack the actual node structures, and the compiler still doesn't emit stupid code. Signed-off-by: David Woodhouse --- include/linux/jffs2.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index 228ad72f7dd8..a26fbd498c79 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -82,15 +82,15 @@ typedef struct { uint32_t v32; -} jint32_t; +} __attribute__((packed)) jint32_t; typedef struct { uint32_t m; -} jmode_t; +} __attribute__((packed)) jmode_t; typedef struct { uint16_t v16; -} jint16_t; +} __attribute__((packed)) jint16_t; struct jffs2_unknown_node { -- cgit v1.2.3 From aef9ab47841af45888d950baa6448072cc70bdd5 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 19 May 2006 00:28:49 +0100 Subject: [JFFS2] Support new device nodes Device node major/minor numbers are just stored in the payload of a single data node. Just extend that to 4 bytes and use new_encode_dev() for it. We only use the 4-byte format if we _need_ to, if !old_valid_dev(foo). This preserves backwards compatibility with older code as much as possible. If we do make devices with major or minor numbers above 255, and then mount the file system with the old code, it'll just read the first two bytes and get the numbers wrong. If it comes to garbage-collect it, it'll then write back those wrong numbers. But that's about the best we can expect. Signed-off-by: David Woodhouse --- fs/jffs2/dir.c | 12 +++++------- fs/jffs2/fs.c | 25 ++++++++++++++++++------- fs/jffs2/gc.c | 7 ++----- fs/jffs2/nodelist.h | 11 +++++++++++ fs/jffs2/os-linux.h | 4 +--- include/linux/jffs2.h | 6 ++++++ 6 files changed, 43 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 1c8e8c0f6cea..a6c11cef1b73 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -591,12 +591,12 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; int namelen; - jint16_t dev; + union jffs2_device_node dev; int devlen = 0; uint32_t alloclen, phys_ofs; int ret; - if (!old_valid_dev(rdev)) + if (!new_valid_dev(rdev)) return -EINVAL; ri = jffs2_alloc_raw_inode(); @@ -605,17 +605,15 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de c = JFFS2_SB_INFO(dir_i->i_sb); - if (S_ISBLK(mode) || S_ISCHR(mode)) { - dev = cpu_to_je16(old_encode_dev(rdev)); - devlen = sizeof(dev); - } + if (S_ISBLK(mode) || S_ISCHR(mode)) + devlen = jffs2_encode_dev(&dev, rdev); /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ namelen = dentry->d_name.len; ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &phys_ofs, &alloclen, - ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); + ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index ea1f37d4fc58..24cb4c688efc 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -33,7 +33,7 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode *ri; - unsigned short dev; + union jffs2_device_node dev; unsigned char *mdata = NULL; int mdatalen = 0; unsigned int ivalid; @@ -51,9 +51,8 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) it out again with the appropriate data attached */ if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { /* For these, we don't actually need to read the old node */ - dev = old_encode_dev(inode->i_rdev); + mdatalen = jffs2_encode_dev(&dev, inode->i_rdev); mdata = (char *)&dev; - mdatalen = sizeof(dev); D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen)); } else if (S_ISLNK(inode->i_mode)) { down(&f->sem); @@ -232,6 +231,8 @@ void jffs2_read_inode (struct inode *inode) struct jffs2_inode_info *f; struct jffs2_sb_info *c; struct jffs2_raw_inode latest_node; + union jffs2_device_node jdev; + dev_t rdev = 0; int ret; D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino)); @@ -263,7 +264,6 @@ void jffs2_read_inode (struct inode *inode) inode->i_blocks = (inode->i_size + 511) >> 9; switch (inode->i_mode & S_IFMT) { - jint16_t rdev; case S_IFLNK: inode->i_op = &jffs2_symlink_inode_operations; @@ -297,8 +297,16 @@ void jffs2_read_inode (struct inode *inode) case S_IFBLK: case S_IFCHR: /* Read the device numbers from the media */ + if (f->metadata->size != sizeof(jdev.old) && + f->metadata->size != sizeof(jdev.new)) { + printk(KERN_NOTICE "Device node has strange size %d\n", f->metadata->size); + up(&f->sem); + jffs2_do_clear_inode(c, f); + make_bad_inode(inode); + return; + } D1(printk(KERN_DEBUG "Reading device numbers from flash\n")); - if (jffs2_read_dnode(c, f, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) { + if (jffs2_read_dnode(c, f, f->metadata, (char *)&jdev, 0, f->metadata->size) < 0) { /* Eep */ printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); up(&f->sem); @@ -306,12 +314,15 @@ void jffs2_read_inode (struct inode *inode) make_bad_inode(inode); return; } + if (f->metadata->size == sizeof(jdev.old)) + rdev = old_decode_dev(je16_to_cpu(jdev.old)); + else + rdev = new_decode_dev(je32_to_cpu(jdev.new)); case S_IFSOCK: case S_IFIFO: inode->i_op = &jffs2_file_inode_operations; - init_special_inode(inode, inode->i_mode, - old_decode_dev((je16_to_cpu(rdev)))); + init_special_inode(inode, inode->i_mode, rdev); break; default: diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 967fb2cf8e21..77d30707de56 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -679,7 +679,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_ struct jffs2_full_dnode *new_fn; struct jffs2_raw_inode ri; struct jffs2_node_frag *last_frag; - jint16_t dev; + union jffs2_device_node dev; char *mdata = NULL, mdatalen = 0; uint32_t alloclen, phys_ofs, ilen; int ret; @@ -687,11 +687,8 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_ if (S_ISBLK(JFFS2_F_I_MODE(f)) || S_ISCHR(JFFS2_F_I_MODE(f)) ) { /* For these, we don't actually need to read the old node */ - /* FIXME: for minor or major > 255. */ - dev = cpu_to_je16(((JFFS2_F_I_RDEV_MAJ(f) << 8) | - JFFS2_F_I_RDEV_MIN(f))); + mdatalen = jffs2_encode_dev(&dev, JFFS2_F_I_RDEV(f)); mdata = (char *)&dev; - mdatalen = sizeof(dev); D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen)); } else if (S_ISLNK(JFFS2_F_I_MODE(f))) { mdatalen = fn->size; diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index f6645afe88e4..24e0f28a8bac 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -268,6 +268,17 @@ static inline uint32_t ref_totlen(struct jffs2_sb_info *c, #define PAD(x) (((x)+3)&~3) +static inline int jffs2_encode_dev(union jffs2_device_node *jdev, dev_t rdev) +{ + if (old_valid_dev(rdev)) { + jdev->old = cpu_to_je16(old_encode_dev(rdev)); + return sizeof(jdev->old); + } else { + jdev->new = cpu_to_je32(new_encode_dev(rdev)); + return sizeof(jdev->new); + } +} + static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw) { while(raw->next_in_ino) { diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index d307cf548625..a10eb03ac95b 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -31,9 +31,7 @@ struct kvec; #define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode) #define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid) #define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid) - -#define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f))) -#define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f))) +#define JFFS2_F_I_RDEV(f) (OFNI_EDONI_2SFFJ(f)->i_rdev) #define ITIME(sec) ((struct timespec){sec, 0}) #define I_SEC(tv) ((tv).tv_sec) diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index a26fbd498c79..007d76d290cb 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -173,4 +173,10 @@ union jffs2_node_union struct jffs2_unknown_node u; }; +/* Data payload for device nodes. */ +union jffs2_device_node { + jint16_t old; + jint32_t new; +}; + #endif /* __LINUX_JFFS2_H__ */ -- cgit v1.2.3 From 8ca9ed5db3aea8d27989c239e8a2f79b839f1e99 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Mon, 22 May 2006 23:17:23 +0200 Subject: [MTD] Use single flag to mark writeable devices. Two flags exist to decide whether a device is writeable or not. None of those two flags is checked for independently, so they are clearly redundant, if not an invitation to bugs. This patch removed both of them, replacing them with a single new flag. Signed-off-by: Joern Engel --- include/mtd/mtd-abi.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index fa2524157846..e4d61f33d5bb 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -30,17 +30,15 @@ struct mtd_oob_buf { #define MTD_NANDFLASH 4 #define MTD_DATAFLASH 6 -#define MTD_CLEAR_BITS 1 // Bits can be cleared (flash) -#define MTD_SET_BITS 2 // Bits can be set #define MTD_ECC 128 // Device capable of automatic ECC #define MTD_PROGRAM_REGIONS 512 // Configurable Programming Regions +#define MTD_WRITEABLE 0x400 /* Device is writeable */ // Some common devices / combinations of capabilities #define MTD_CAP_ROM 0 -#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS) -#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS) -#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS) -#define MTD_WRITEABLE (MTD_CLEAR_BITS|MTD_SET_BITS) +#define MTD_CAP_RAM (MTD_WRITEABLE) +#define MTD_CAP_NORFLASH (MTD_WRITEABLE) +#define MTD_CAP_NANDFLASH (MTD_WRITEABLE) // Types of automatic ECC/Checksum available -- cgit v1.2.3 From 28318776a80bc3261f9af91ef79e6e38bb9f5bec Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Mon, 22 May 2006 23:18:05 +0200 Subject: [MTD] Introduce writesize At least two flashes exists that have the concept of a minimum write unit, similar to NAND pages, but no other NAND characteristics. Therefore, rename the minimum write unit to "writesize" for all flashes, including NAND. Signed-off-by: Joern Engel --- drivers/mtd/chips/cfi_cmdset_0001.c | 4 +-- drivers/mtd/devices/doc2000.c | 2 +- drivers/mtd/devices/doc2001.c | 2 +- drivers/mtd/devices/doc2001plus.c | 2 +- drivers/mtd/mtdconcat.c | 10 +++--- drivers/mtd/mtdpart.c | 2 +- drivers/mtd/nand/au1550nd.c | 4 +-- drivers/mtd/nand/diskonchip.c | 16 ++++----- drivers/mtd/nand/nand_base.c | 64 ++++++++++++++++----------------- drivers/mtd/nand/nand_bbt.c | 30 ++++++++-------- drivers/mtd/nand/nandsim.c | 2 +- drivers/mtd/nand/rtc_from4.c | 2 +- drivers/mtd/onenand/onenand_base.c | 72 ++++++++++++++++++------------------- drivers/mtd/onenand/onenand_bbt.c | 4 +-- fs/jffs2/wbuf.c | 6 ++-- include/linux/mtd/mtd.h | 7 ++-- include/mtd/mtd-abi.h | 2 +- 17 files changed, 117 insertions(+), 114 deletions(-) (limited to 'include') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index d0d5e521b564..35c3689bc5c1 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -545,12 +545,12 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, if (extp->MinorVersion >= '4') { struct cfi_intelext_programming_regioninfo *prinfo; prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs]; - MTD_PROGREGION_SIZE(mtd) = cfi->interleave << prinfo->ProgRegShift; + mtd->writesize = cfi->interleave << prinfo->ProgRegShift; MTD_PROGREGION_CTRLMODE_VALID(mtd) = cfi->interleave * prinfo->ControlValid; MTD_PROGREGION_CTRLMODE_INVALID(mtd) = cfi->interleave * prinfo->ControlInvalid; mtd->flags |= MTD_PROGRAM_REGIONS; printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n", - map->name, MTD_PROGREGION_SIZE(mtd), + map->name, mtd->writesize, MTD_PROGREGION_CTRLMODE_VALID(mtd), MTD_PROGREGION_CTRLMODE_INVALID(mtd)); } diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index 40cc20f6d164..423a34f4638c 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -579,7 +579,7 @@ void DoC2k_init(struct mtd_info *mtd) mtd->ecctype = MTD_ECC_RS_DiskOnChip; mtd->size = 0; mtd->erasesize = 0; - mtd->oobblock = 512; + mtd->writesize = 512; mtd->oobsize = 16; mtd->owner = THIS_MODULE; mtd->erase = doc_erase; diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index 1670eb8b9755..e6eaef28a2b0 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -361,7 +361,7 @@ void DoCMil_init(struct mtd_info *mtd) /* FIXME: erase size is not always 8KiB */ mtd->erasesize = 0x2000; - mtd->oobblock = 512; + mtd->writesize = 512; mtd->oobsize = 16; mtd->owner = THIS_MODULE; mtd->erase = doc_erase; diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index 0dc5d108f7b5..8422c5e92d27 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -483,7 +483,7 @@ void DoCMilPlus_init(struct mtd_info *mtd) mtd->size = 0; mtd->erasesize = 0; - mtd->oobblock = 512; + mtd->writesize = 512; mtd->oobsize = 16; mtd->owner = THIS_MODULE; mtd->erase = doc_erase; diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 3c61a980c56c..a5e8373349a5 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -278,9 +278,9 @@ concat_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, return -EINVAL; /* Check alignment */ - if (mtd->oobblock > 1) { + if (mtd->writesize > 1) { loff_t __to = to; - if (do_div(__to, mtd->oobblock) || (total_len % mtd->oobblock)) + if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize)) return -EINVAL; } @@ -334,7 +334,7 @@ concat_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, *retlen += retsize; total_len -= wsize; if (concat->mtd.type == MTD_NANDFLASH && eccbuf) - eccbuf += mtd->oobavail * (wsize / mtd->oobblock); + eccbuf += mtd->oobavail * (wsize / mtd->writesize); if (total_len == 0) break; @@ -833,7 +833,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c concat->mtd.flags = subdev[0]->flags; concat->mtd.size = subdev[0]->size; concat->mtd.erasesize = subdev[0]->erasesize; - concat->mtd.oobblock = subdev[0]->oobblock; + concat->mtd.writesize = subdev[0]->writesize; concat->mtd.oobsize = subdev[0]->oobsize; concat->mtd.ecctype = subdev[0]->ecctype; concat->mtd.eccsize = subdev[0]->eccsize; @@ -881,7 +881,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c subdev[i]->flags & MTD_WRITEABLE; } concat->mtd.size += subdev[i]->size; - if (concat->mtd.oobblock != subdev[i]->oobblock || + if (concat->mtd.writesize != subdev[i]->writesize || concat->mtd.oobsize != subdev[i]->oobsize || concat->mtd.ecctype != subdev[i]->ecctype || concat->mtd.eccsize != subdev[i]->eccsize || diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 29ed5abe70c4..082662f90481 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -398,7 +398,7 @@ int add_mtd_partitions(struct mtd_info *master, slave->mtd.type = master->type; slave->mtd.flags = master->flags & ~parts[i].mask_flags; slave->mtd.size = parts[i].size; - slave->mtd.oobblock = master->oobblock; + slave->mtd.writesize = master->writesize; slave->mtd.oobsize = master->oobsize; slave->mtd.oobavail = master->oobavail; slave->mtd.ecctype = master->ecctype; diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index d9a0143e1d3a..4253b9309789 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -356,9 +356,9 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i if (command == NAND_CMD_SEQIN) { int readcmd; - if (column >= mtd->oobblock) { + if (column >= mtd->writesize) { /* OOB area */ - column -= mtd->oobblock; + column -= mtd->writesize; readcmd = NAND_CMD_READOOB; } else if (column < 256) { /* First 256 bytes --> READ0 */ diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index a2391c66a63f..d160930276d6 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -761,9 +761,9 @@ static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int colu if (command == NAND_CMD_SEQIN) { int readcmd; - if (column >= mtd->oobblock) { + if (column >= mtd->writesize) { /* OOB area */ - column -= mtd->oobblock; + column -= mtd->writesize; readcmd = NAND_CMD_READOOB; } else if (column < 256) { /* First 256 bytes --> READ0 */ @@ -1093,8 +1093,8 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const ch size_t retlen; for (offs = 0; offs < mtd->size; offs += mtd->erasesize) { - ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf); - if (retlen != mtd->oobblock) + ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf); + if (retlen != mtd->writesize) continue; if (ret) { printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", offs); @@ -1118,8 +1118,8 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const ch /* Only one mediaheader was found. We want buf to contain a mediaheader on return, so we'll have to re-read the one we found. */ offs = doc->mh0_page << this->page_shift; - ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf); - if (retlen != mtd->oobblock) { + ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf); + if (retlen != mtd->writesize) { /* Insanity. Give up. */ printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n"); return 0; @@ -1139,7 +1139,7 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio unsigned blocks, maxblocks; int offs, numheaders; - buf = kmalloc(mtd->oobblock, GFP_KERNEL); + buf = kmalloc(mtd->writesize, GFP_KERNEL); if (!buf) { printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); return 0; @@ -1247,7 +1247,7 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partiti if (inftl_bbt_write) end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift); - buf = kmalloc(mtd->oobblock, GFP_KERNEL); + buf = kmalloc(mtd->writesize, GFP_KERNEL); if (!buf) { printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); return 0; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 08dffb7a9389..055f6608a2ec 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -560,9 +560,9 @@ static void nand_command(struct mtd_info *mtd, unsigned command, int column, int if (command == NAND_CMD_SEQIN) { int readcmd; - if (column >= mtd->oobblock) { + if (column >= mtd->writesize) { /* OOB area */ - column -= mtd->oobblock; + column -= mtd->writesize; readcmd = NAND_CMD_READOOB; } else if (column < 256) { /* First 256 bytes --> READ0 */ @@ -658,7 +658,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned command, int column, /* Emulate NAND_CMD_READOOB */ if (command == NAND_CMD_READOOB) { - column += mtd->oobblock; + column += mtd->writesize; command = NAND_CMD_READ0; } @@ -889,7 +889,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this, int pag /* No ecc, write all */ case NAND_ECC_NONE: printk(KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); - this->write_buf(mtd, this->data_poi, mtd->oobblock); + this->write_buf(mtd, this->data_poi, mtd->writesize); break; /* Software ecc 3/256, write all */ @@ -900,7 +900,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this, int pag oob_buf[oob_config[eccidx]] = ecc_code[i]; datidx += this->eccsize; } - this->write_buf(mtd, this->data_poi, mtd->oobblock); + this->write_buf(mtd, this->data_poi, mtd->writesize); break; default: eccbytes = this->eccbytes; @@ -1161,9 +1161,9 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, page = realpage & this->pagemask; /* Get raw starting column */ - col = from & (mtd->oobblock - 1); + col = from & (mtd->writesize - 1); - end = mtd->oobblock; + end = mtd->writesize; ecc = this->eccsize; eccbytes = this->eccbytes; @@ -1321,7 +1321,7 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, buf[read++] = data_poi[j]; this->pagebuf = realpage; } else - read += mtd->oobblock; + read += mtd->writesize; /* Apply delay or wait for ready/busy pin * Do this before the AUTOINCR check, so no problems @@ -1479,7 +1479,7 @@ int nand_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, s int chip = (int)(from >> this->chip_shift); int sndcmd = 1; int cnt = 0; - int pagesize = mtd->oobblock + mtd->oobsize; + int pagesize = mtd->writesize + mtd->oobsize; int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; /* Do not allow reads past end of device */ @@ -1581,7 +1581,7 @@ static u_char *nand_prepare_oobbuf(struct mtd_info *mtd, u_char *fsbuf, struct n return this->oob_buf; } -#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0 +#define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0 /** * nand_write - [MTD Interface] compability function for nand_write_ecc @@ -1694,7 +1694,7 @@ static int nand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, /* Next oob page */ oob += mtd->oobsize; /* Update written bytes count */ - written += mtd->oobblock; + written += mtd->writesize; if (written == len) goto cmp; @@ -1805,7 +1805,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, size_t *r if (NAND_MUST_PAD(this)) { /* Write out desired data */ - this->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask); + this->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page & this->pagemask); /* prepad 0xff for partial programming */ this->write_buf(mtd, ffchars, column); /* write data */ @@ -1814,7 +1814,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, size_t *r this->write_buf(mtd, ffchars, mtd->oobsize - (len + column)); } else { /* Write out desired data */ - this->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask); + this->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + column, page & this->pagemask); /* write data */ this->write_buf(mtd, buf, len); } @@ -1947,7 +1947,7 @@ static int nand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, unsign /* If the given tuple is >= pagesize then * write it out from the iov */ - if ((vecs->iov_len - len) >= mtd->oobblock) { + if ((vecs->iov_len - len) >= mtd->writesize) { /* Calc number of pages we can write * out of this iov in one go */ numpages = (vecs->iov_len - len) >> this->page_shift; @@ -1967,8 +1967,8 @@ static int nand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, unsign &oobbuf[oob], oobsel, i != numpages); if (ret) goto out; - this->data_poi += mtd->oobblock; - len += mtd->oobblock; + this->data_poi += mtd->writesize; + len += mtd->writesize; oob += mtd->oobsize; page++; } @@ -1983,7 +1983,7 @@ static int nand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, unsign * tuple until we have a full page to write */ int cnt = 0; - while (cnt < mtd->oobblock) { + while (cnt < mtd->writesize) { 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 */ @@ -2009,7 +2009,7 @@ static int nand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, unsign if (ret) goto out; - written += mtd->oobblock * numpages; + written += mtd->writesize * numpages; /* All done ? */ if (!count) break; @@ -2411,10 +2411,10 @@ int nand_scan(struct mtd_info *mtd, int maxchips) /* The 4th id byte is the important one */ extid = this->read_byte(mtd); /* Calc pagesize */ - mtd->oobblock = 1024 << (extid & 0x3); + mtd->writesize = 1024 << (extid & 0x3); extid >>= 2; /* Calc oobsize */ - mtd->oobsize = (8 << (extid & 0x01)) * (mtd->oobblock >> 9); + mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); extid >>= 2; /* Calc blocksize. Blocksize is multiples of 64KiB */ mtd->erasesize = (64 * 1024) << (extid & 0x03); @@ -2426,8 +2426,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips) /* 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; + mtd->writesize = nand_flash_ids[i].pagesize; + mtd->oobsize = mtd->writesize / 32; busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16; } @@ -2451,12 +2451,12 @@ int nand_scan(struct mtd_info *mtd, int maxchips) } /* Calculate the address shift from the page size */ - this->page_shift = ffs(mtd->oobblock) - 1; + this->page_shift = ffs(mtd->writesize) - 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; + this->badblockpos = mtd->writesize > 512 ? NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; /* Get chip options, preserve non chip based options */ this->options &= ~NAND_CHIPOPTIONS_MSK; @@ -2476,7 +2476,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) this->erase_cmd = single_erase_cmd; /* Do not replace user supplied command function ! */ - if (mtd->oobblock > 512 && this->cmdfunc == nand_command) + if (mtd->writesize > 512 && this->cmdfunc == nand_command) this->cmdfunc = nand_command_lp; printk(KERN_INFO "NAND device: Manufacturer ID:" @@ -2519,7 +2519,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) if (!this->data_buf) { size_t len; - len = mtd->oobblock + mtd->oobsize; + len = mtd->writesize + mtd->oobsize; this->data_buf = kmalloc(len, GFP_KERNEL); if (!this->data_buf) { if (this->options & NAND_OOBBUF_ALLOC) @@ -2575,9 +2575,9 @@ int nand_scan(struct mtd_info *mtd, int maxchips) switch (this->eccmode) { case NAND_ECC_HW12_2048: - if (mtd->oobblock < 2048) { + if (mtd->writesize < 2048) { printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", - mtd->oobblock); + mtd->writesize); this->eccmode = NAND_ECC_SOFT; this->calculate_ecc = nand_calculate_ecc; this->correct_data = nand_correct_data; @@ -2588,7 +2588,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: case NAND_ECC_HW8_512: - if (mtd->oobblock == 256) { + if (mtd->writesize == 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; @@ -2638,16 +2638,16 @@ int nand_scan(struct mtd_info *mtd, int maxchips) /* 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; + this->eccsteps = mtd->writesize / 2048; break; case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: case NAND_ECC_HW8_512: - this->eccsteps = mtd->oobblock / 512; + this->eccsteps = mtd->writesize / 512; break; case NAND_ECC_HW3_256: case NAND_ECC_SOFT: - this->eccsteps = mtd->oobblock / 256; + this->eccsteps = mtd->writesize / 256; break; case NAND_ECC_NONE: diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 9adc6d62332a..fbccb2a25186 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -247,15 +247,15 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des /* Read the primary version, if available */ if (td->options & NAND_BBT_VERSION) { - nand_read_raw(mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); - td->version[0] = buf[mtd->oobblock + td->veroffs]; + nand_read_raw(mtd, buf, td->pages[0] << this->page_shift, mtd->writesize, mtd->oobsize); + td->version[0] = buf[mtd->writesize + td->veroffs]; printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]); } /* Read the mirror version, if available */ if (md && (md->options & NAND_BBT_VERSION)) { - nand_read_raw(mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); - md->version[0] = buf[mtd->oobblock + md->veroffs]; + nand_read_raw(mtd, buf, md->pages[0] << this->page_shift, mtd->writesize, mtd->oobsize); + md->version[0] = buf[mtd->writesize + md->veroffs]; printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]); } @@ -298,8 +298,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr readlen = bd->len; } else { /* Full page content should be read */ - scanlen = mtd->oobblock + mtd->oobsize; - readlen = len * mtd->oobblock; + scanlen = mtd->writesize + mtd->oobsize; + readlen = len * mtd->writesize; ooblen = len * mtd->oobsize; } @@ -334,7 +334,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr /* Read the full oob until read_oob is fixed to * handle single byte reads for 16 bit buswidth */ - ret = mtd->read_oob(mtd, from + j * mtd->oobblock, mtd->oobsize, &retlen, buf); + ret = mtd->read_oob(mtd, from + j * mtd->writesize, mtd->oobsize, &retlen, buf); if (ret) return ret; @@ -345,7 +345,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr break; } } else { - if (check_pattern(&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { + if (check_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", i >> 1, (unsigned int)from); @@ -381,7 +381,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr struct nand_chip *this = mtd->priv; int i, chips; int bits, startblock, block, dir; - int scanlen = mtd->oobblock + mtd->oobsize; + int scanlen = mtd->writesize + mtd->oobsize; int bbtblocks; /* Search direction top -> down ? */ @@ -414,11 +414,11 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr for (block = 0; block < td->maxblocks; block++) { int actblock = startblock + dir * block; /* Read first page */ - nand_read_raw(mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize); - if (!check_pattern(buf, scanlen, mtd->oobblock, td)) { + nand_read_raw(mtd, buf, actblock << this->bbt_erase_shift, mtd->writesize, mtd->oobsize); + if (!check_pattern(buf, scanlen, mtd->writesize, td)) { td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift); if (td->options & NAND_BBT_VERSION) { - td->version[i] = buf[mtd->oobblock + td->veroffs]; + td->version[i] = buf[mtd->writesize + td->veroffs]; } break; } @@ -586,7 +586,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, /* Calc length */ len = (size_t) (numblocks >> sft); /* Make it page aligned ! */ - len = (len + (mtd->oobblock - 1)) & ~(mtd->oobblock - 1); + len = (len + (mtd->writesize - 1)) & ~(mtd->writesize - 1); /* Preset the buffer with 0xff */ memset(buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize); offs = 0; @@ -1063,13 +1063,13 @@ int nand_default_bbt(struct mtd_info *mtd) this->bbt_md = &bbt_mirror_descr; } if (!this->badblock_pattern) { - this->badblock_pattern = (mtd->oobblock > 512) ? &largepage_flashbased : &smallpage_flashbased; + this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased; } } else { this->bbt_td = NULL; this->bbt_md = NULL; if (!this->badblock_pattern) { - this->badblock_pattern = (mtd->oobblock > 512) ? + this->badblock_pattern = (mtd->writesize > 512) ? &largepage_memorybased : &smallpage_memorybased; } } diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 6903f5b903c6..8674f1e9d3c6 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -369,7 +369,7 @@ init_nandsim(struct mtd_info *mtd) /* Initialize the NAND flash parameters */ ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8; ns->geom.totsz = mtd->size; - ns->geom.pgsz = mtd->oobblock; + ns->geom.pgsz = mtd->writesize; ns->geom.oobsz = mtd->oobsize; ns->geom.secsz = mtd->erasesize; ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz; diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index bc9d849fbd5d..64ccf4c9613f 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -487,7 +487,7 @@ static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int s if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { er_stat |= 1 << 1; /* err_ecc_not_avail */ } else { - len = mtd->oobblock; + len = mtd->writesize; buf = kmalloc(len, GFP_KERNEL); if (!buf) { printk(KERN_ERR "rtc_from4_errstat: Out of memory!\n"); diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index fe5b48997275..198bb8562d93 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -354,7 +354,7 @@ static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) if (ONENAND_CURRENT_BUFFERRAM(this)) { if (area == ONENAND_DATARAM) - return mtd->oobblock; + return mtd->writesize; if (area == ONENAND_SPARERAM) return mtd->oobsize; } @@ -632,14 +632,14 @@ static int onenand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, /* TODO handling oob */ while (read < len) { - thislen = min_t(int, mtd->oobblock, len - read); + thislen = min_t(int, mtd->writesize, len - read); - column = from & (mtd->oobblock - 1); - if (column + thislen > mtd->oobblock) - thislen = mtd->oobblock - column; + column = from & (mtd->writesize - 1); + if (column + thislen > mtd->writesize) + thislen = mtd->writesize - column; if (!onenand_check_bufferram(mtd, from)) { - this->command(mtd, ONENAND_CMD_READ, from, mtd->oobblock); + this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize); ret = this->wait(mtd, FL_READING); /* First copy data and check return value for ECC handling */ @@ -752,7 +752,7 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, /* Read more? */ if (read < len) { /* Page size */ - from += mtd->oobblock; + from += mtd->writesize; column = 0; } } @@ -809,7 +809,7 @@ static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr) void __iomem *dataram0, *dataram1; int ret = 0; - this->command(mtd, ONENAND_CMD_READ, addr, mtd->oobblock); + this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); ret = this->wait(mtd, FL_READING); if (ret) @@ -819,9 +819,9 @@ static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr) /* Check, if the two dataram areas are same */ dataram0 = this->base + ONENAND_DATARAM; - dataram1 = dataram0 + mtd->oobblock; + dataram1 = dataram0 + mtd->writesize; - if (memcmp(dataram0, dataram1, mtd->oobblock)) + if (memcmp(dataram0, dataram1, mtd->writesize)) return -EBADMSG; return 0; @@ -831,7 +831,7 @@ static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr) #define onenand_verify_oob(...) (0) #endif -#define NOTALIGNED(x) ((x & (mtd->oobblock - 1)) != 0) +#define NOTALIGNED(x) ((x & (mtd->writesize - 1)) != 0) /** * onenand_write_ecc - [MTD Interface] OneNAND write with ECC @@ -875,14 +875,14 @@ static int onenand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, /* Loop until all data write */ while (written < len) { - int thislen = min_t(int, mtd->oobblock, len - written); + int thislen = min_t(int, mtd->writesize, len - written); - this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobblock); + this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->writesize); this->write_bufferram(mtd, ONENAND_DATARAM, buf, 0, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); - this->command(mtd, ONENAND_CMD_PROG, to, mtd->oobblock); + this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); onenand_update_bufferram(mtd, to, 1); @@ -1070,10 +1070,10 @@ static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, * If the given tuple is >= pagesize then * write it out from the iov */ - if ((vecs->iov_len - len) >= mtd->oobblock) { + if ((vecs->iov_len - len) >= mtd->writesize) { pbuf = vecs->iov_base + len; - len += mtd->oobblock; + len += mtd->writesize; /* Check, if we have to switch to the next tuple */ if (len >= (int) vecs->iov_len) { @@ -1083,8 +1083,8 @@ static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, } } else { int cnt = 0, thislen; - while (cnt < mtd->oobblock) { - thislen = min_t(int, mtd->oobblock - cnt, vecs->iov_len - len); + while (cnt < mtd->writesize) { + thislen = min_t(int, mtd->writesize - cnt, vecs->iov_len - len); memcpy(this->page_buf + cnt, vecs->iov_base + len, thislen); cnt += thislen; len += thislen; @@ -1098,12 +1098,12 @@ static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, } } - this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobblock); + this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->writesize); - this->write_bufferram(mtd, ONENAND_DATARAM, pbuf, 0, mtd->oobblock); + this->write_bufferram(mtd, ONENAND_DATARAM, pbuf, 0, mtd->writesize); this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); - this->command(mtd, ONENAND_CMD_PROG, to, mtd->oobblock); + this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); onenand_update_bufferram(mtd, to, 1); @@ -1121,9 +1121,9 @@ static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, goto out; } - written += mtd->oobblock; + written += mtd->writesize; - to += mtd->oobblock; + to += mtd->writesize; } out: @@ -1467,11 +1467,11 @@ static int do_otp_write(struct mtd_info *mtd, loff_t from, size_t len, int ret; /* Force buffer page aligned */ - if (len < mtd->oobblock) { + if (len < mtd->writesize) { memcpy(this->page_buf, buf, len); - memset(this->page_buf + len, 0xff, mtd->oobblock - len); + memset(this->page_buf + len, 0xff, mtd->writesize - len); pbuf = this->page_buf; - len = mtd->oobblock; + len = mtd->writesize; } /* Enter OTP access mode */ @@ -1546,12 +1546,12 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, otp_pages = 10; if (mode == MTD_OTP_FACTORY) { - from += mtd->oobblock * otp_pages; + from += mtd->writesize * otp_pages; otp_pages = 64 - otp_pages; } /* Check User/Factory boundary */ - if (((mtd->oobblock * otp_pages) - (from + len)) < 0) + if (((mtd->writesize * otp_pages) - (from + len)) < 0) return 0; while (len > 0 && otp_pages > 0) { @@ -1564,10 +1564,10 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, otpinfo = (struct otp_info *) buf; otpinfo->start = from; - otpinfo->length = mtd->oobblock; + otpinfo->length = mtd->writesize; otpinfo->locked = 0; - from += mtd->oobblock; + from += mtd->writesize; buf += sizeof(struct otp_info); *retlen += sizeof(struct otp_info); } else { @@ -1811,15 +1811,15 @@ static int onenand_probe(struct mtd_info *mtd) /* OneNAND page size & block size */ /* The data buffer size is equal to page size */ - mtd->oobblock = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); - mtd->oobsize = mtd->oobblock >> 5; + mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); + mtd->oobsize = mtd->writesize >> 5; /* Pagers per block is always 64 in OneNAND */ - mtd->erasesize = mtd->oobblock << 6; + mtd->erasesize = mtd->writesize << 6; this->erase_shift = ffs(mtd->erasesize) - 1; - this->page_shift = ffs(mtd->oobblock) - 1; + this->page_shift = ffs(mtd->writesize) - 1; this->ppb_shift = (this->erase_shift - this->page_shift); - this->page_mask = (mtd->erasesize / mtd->oobblock) - 1; + this->page_mask = (mtd->erasesize / mtd->writesize) - 1; /* REVIST: Multichip handling */ @@ -1909,7 +1909,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) /* Allocate buffers, if necessary */ if (!this->page_buf) { size_t len; - len = mtd->oobblock + mtd->oobsize; + len = mtd->writesize + mtd->oobsize; this->page_buf = kmalloc(len, GFP_KERNEL); if (!this->page_buf) { printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n"); diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 4510d3361eaa..aafd7c2f7802 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -87,13 +87,13 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr /* No need to read pages fully, * just read required OOB bytes */ - ret = mtd->read_oob(mtd, from + j * mtd->oobblock + bd->offs, + ret = mtd->read_oob(mtd, from + j * mtd->writesize + bd->offs, readlen, &retlen, &buf[0]); if (ret) return ret; - if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { + if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", i >> 1, (unsigned int) from); diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 62f685faeba8..355226d8ce29 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -1173,7 +1173,7 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) /* Initialise write buffer */ init_rwsem(&c->wbuf_sem); - c->wbuf_pagesize = c->mtd->oobblock; + c->wbuf_pagesize = c->mtd->writesize; c->wbuf_ofs = 0xFFFFFFFF; c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); @@ -1266,11 +1266,11 @@ void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) { int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c) { /* Cleanmarker currently occupies a whole programming region */ - c->cleanmarker_size = MTD_PROGREGION_SIZE(c->mtd); + c->cleanmarker_size = c->mtd->writesize; /* Initialize write buffer */ init_rwsem(&c->wbuf_sem); - c->wbuf_pagesize = MTD_PROGREGION_SIZE(c->mtd); + c->wbuf_pagesize = c->mtd->writesize; c->wbuf_ofs = 0xFFFFFFFF; c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 73620ef83364..d48c7492392b 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -66,8 +66,12 @@ struct mtd_info { * information below if they desire */ u_int32_t erasesize; + /* Smallest availlable size for writing to the device. For NAND, + * this is the page size, for some NOR chips, the size of ECC + * covered blocks. + */ + u_int32_t writesize; - u_int32_t oobblock; // Size of OOB blocks (e.g. 512) u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) u_int32_t ecctype; u_int32_t eccsize; @@ -79,7 +83,6 @@ struct mtd_info { * MTD_PROGRAM_REGIONS flag is set. * (Maybe we should have an union for those?) */ -#define MTD_PROGREGION_SIZE(mtd) (mtd)->oobblock #define MTD_PROGREGION_CTRLMODE_VALID(mtd) (mtd)->oobsize #define MTD_PROGREGION_CTRLMODE_INVALID(mtd) (mtd)->ecctype diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index e4d61f33d5bb..520a3b483100 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -63,7 +63,7 @@ struct mtd_info_user { uint32_t flags; uint32_t size; // Total size of the MTD uint32_t erasesize; - uint32_t oobblock; // Size of OOB blocks (e.g. 512) + uint32_t writesize; uint32_t oobsize; // Amount of OOB data per block (e.g. 16) uint32_t ecctype; uint32_t eccsize; -- cgit v1.2.3 From 5fa433942ba4e399f7e28764c9db4ade89e91d40 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Mon, 22 May 2006 23:18:29 +0200 Subject: [MTD] Introduce MTD_BIT_WRITEABLE o Add a flag MTD_BIT_WRITEABLE for devices that allow single bits to be cleared. o Replace MTD_PROGRAM_REGIONS with a cleared MTD_BIT_WRITEABLE flag for STMicro and Intel Sibley flashes with internal ECC. Those flashes disallow clearing of single bits, unlike regular NOR flashes, so the new flag models their behaviour better. o Remove MTD_ECC. After the STMicro/Sibley merge, this flag is only set and never checked. Signed-off-by: Joern Engel --- drivers/mtd/chips/cfi_cmdset_0001.c | 2 +- drivers/mtd/chips/cfi_cmdset_0020.c | 3 +-- drivers/mtd/nand/nand_base.c | 2 +- drivers/mtd/onenand/onenand_base.c | 2 +- fs/jffs2/os-linux.h | 6 ++---- include/mtd/mtd-abi.h | 7 +++---- 6 files changed, 9 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 35c3689bc5c1..0d435814aaa1 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -548,7 +548,7 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, mtd->writesize = cfi->interleave << prinfo->ProgRegShift; MTD_PROGREGION_CTRLMODE_VALID(mtd) = cfi->interleave * prinfo->ControlValid; MTD_PROGREGION_CTRLMODE_INVALID(mtd) = cfi->interleave * prinfo->ControlInvalid; - mtd->flags |= MTD_PROGRAM_REGIONS; + mtd->flags &= ~MTD_BIT_WRITEABLE; printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n", map->name, mtd->writesize, MTD_PROGREGION_CTRLMODE_VALID(mtd), diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index d745285fccf8..fae70a5db540 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -238,8 +238,7 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map) mtd->unlock = cfi_staa_unlock; mtd->suspend = cfi_staa_suspend; mtd->resume = cfi_staa_resume; - mtd->flags = MTD_CAP_NORFLASH; - mtd->flags |= MTD_PROGRAM_REGIONS; /* FIXME: Not all STMicro flashes have this */ + mtd->flags = MTD_CAP_NORFLASH & ~MTD_BIT_WRITEABLE; mtd->writesize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */ map->fldrv = &cfi_staa_chipdrv; __module_get(THIS_MODULE); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 055f6608a2ec..cd90a46bf56a 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2668,7 +2668,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; - mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; + mtd->flags = MTD_CAP_NANDFLASH; mtd->ecctype = MTD_ECC_SW; mtd->erase = nand_erase; mtd->point = NULL; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 198bb8562d93..4c2c61d54b3a 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1943,7 +1943,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; - mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; + mtd->flags = MTD_CAP_NANDFLASH; mtd->ecctype = MTD_ECC_SW; mtd->erase = onenand_erase; mtd->point = NULL; diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index 1a6eb955e0be..743c9e52152d 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -105,9 +105,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) #ifdef CONFIG_JFFS2_SUMMARY #define jffs2_can_mark_obsolete(c) (0) #else -#define jffs2_can_mark_obsolete(c) \ - ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & (MTD_PROGRAM_REGIONS))) || \ - c->mtd->type == MTD_RAM) +#define jffs2_can_mark_obsolete(c) (c->mtd->flags & (MTD_BIT_WRITEABLE)) #endif #define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH) @@ -135,7 +133,7 @@ void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c); int jffs2_dataflash_setup(struct jffs2_sb_info *c); void jffs2_dataflash_cleanup(struct jffs2_sb_info *c); -#define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_PROGRAM_REGIONS)) +#define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && ! (c->mtd->flags & MTD_BIT_WRITEABLE)) int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c); void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c); diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index 520a3b483100..1e09e4c8f485 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -30,14 +30,13 @@ struct mtd_oob_buf { #define MTD_NANDFLASH 4 #define MTD_DATAFLASH 6 -#define MTD_ECC 128 // Device capable of automatic ECC -#define MTD_PROGRAM_REGIONS 512 // Configurable Programming Regions #define MTD_WRITEABLE 0x400 /* Device is writeable */ +#define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ // Some common devices / combinations of capabilities #define MTD_CAP_ROM 0 -#define MTD_CAP_RAM (MTD_WRITEABLE) -#define MTD_CAP_NORFLASH (MTD_WRITEABLE) +#define MTD_CAP_RAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE) +#define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE) #define MTD_CAP_NANDFLASH (MTD_WRITEABLE) -- cgit v1.2.3 From a36ed2995c56d4f858ecb524a78837473e7115ae Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 23 May 2006 11:37:03 +0200 Subject: [MTD] Simplify NAND locking Replace the chip lock by a the controller lock. For simple drivers a dummy controller structure is created by the scan code. This simplifies the locking algorithm in nand_get/release_chip(). Signed-off-by: Thomas Gleixner --- drivers/mtd/nand/nand_base.c | 81 +++++++++++++++++++++++--------------------- include/linux/mtd/nand.h | 7 ++-- 2 files changed, 47 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 08dffb7a9389..7933ca273c95 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -172,20 +172,12 @@ static void nand_release_device(struct mtd_info *mtd) /* De-select the NAND device */ this->select_chip(mtd, -1); - if (this->controller) { - /* Release the controller and the chip */ - spin_lock(&this->controller->lock); - this->controller->active = NULL; - this->state = FL_READY; - wake_up(&this->controller->wq); - spin_unlock(&this->controller->lock); - } else { - /* Release the chip */ - spin_lock(&this->chip_lock); - this->state = FL_READY; - wake_up(&this->wq); - spin_unlock(&this->chip_lock); - } + /* Release the controller and the chip */ + spin_lock(&this->controller->lock); + this->controller->active = NULL; + this->state = FL_READY; + wake_up(&this->controller->wq); + spin_unlock(&this->controller->lock); } /** @@ -765,25 +757,18 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned command, int column, */ static int nand_get_device(struct nand_chip *this, struct mtd_info *mtd, int new_state) { - struct nand_chip *active; - spinlock_t *lock; - wait_queue_head_t *wq; + spinlock_t *lock = &this->controller->lock; + wait_queue_head_t *wq = &this->controller->wq; DECLARE_WAITQUEUE(wait, current); - - lock = (this->controller) ? &this->controller->lock : &this->chip_lock; - wq = (this->controller) ? &this->controller->wq : &this->wq; retry: - active = this; spin_lock(lock); /* Hardware controller shared among independend devices */ - if (this->controller) { - if (this->controller->active) - active = this->controller->active; - else - this->controller->active = this; - } - if (active == this && this->state == FL_READY) { + /* Hardware controller shared among independend devices */ + if (!this->controller->active) + this->controller->active = this; + + if (this->controller->active == this && this->state == FL_READY) { this->state = new_state; spin_unlock(lock); return 0; @@ -2312,6 +2297,22 @@ static void nand_resume(struct mtd_info *mtd) } +/* + * Free allocated data structures + */ +static void nand_free_kmem(struct nand_chip *this) +{ + /* Buffer allocated by nand_scan ? */ + if (this->options & NAND_OOBBUF_ALLOC) + kfree(this->oob_buf); + /* Buffer allocated by nand_scan ? */ + if (this->options & NAND_DATABUF_ALLOC) + kfree(this->data_buf); + /* Controller allocated by nand_scan ? */ + if (this->options & NAND_CONTROLLER_ALLOC) + kfree(this->controller); +} + /* module_text_address() isn't exported, and it's mostly a pointless test if this is a module _anyway_ -- they'd have to try _really_ hard to call us from in-kernel code if the core NAND support is modular. */ @@ -2522,9 +2523,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips) 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"); + nand_free_kmem(this); return -ENOMEM; } this->options |= NAND_DATABUF_ALLOC; @@ -2657,8 +2657,17 @@ int nand_scan(struct mtd_info *mtd, int maxchips) /* Initialize state, waitqueue and spinlock */ this->state = FL_READY; - init_waitqueue_head(&this->wq); - spin_lock_init(&this->chip_lock); + if (!this->controller) { + this->controller = kzalloc(sizeof(struct nand_hw_control), + GFP_KERNEL); + if (!this->controller) { + nand_free_kmem(this); + return -ENOMEM; + } + this->options |= NAND_CONTROLLER_ALLOC; + } + init_waitqueue_head(&this->controller->wq); + spin_lock_init(&this->controller->lock); /* De-select the device */ this->select_chip(mtd, -1); @@ -2718,12 +2727,8 @@ void nand_release(struct mtd_info *mtd) /* Free bad block table memory */ kfree(this->bbt); - /* Buffer allocated by nand_scan ? */ - if (this->options & NAND_OOBBUF_ALLOC) - kfree(this->oob_buf); - /* Buffer allocated by nand_scan ? */ - if (this->options & NAND_DATABUF_ALLOC) - kfree(this->data_buf); + /* Free buffers */ + nand_free_kmem(this); } EXPORT_SYMBOL_GPL(nand_scan); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index da5e67b3fc70..b8792be3c4e0 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -227,6 +227,8 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_ #define NAND_SKIP_BBTSCAN 0x00040000 /* Options set by nand scan */ +/* Nand scan has allocated controller struct */ +#define NAND_CONTROLLER_ALLOC 0x20000000 /* Nand scan has allocated oob_buf */ #define NAND_OOBBUF_ALLOC 0x40000000 /* Nand scan has allocated data_buf */ @@ -294,7 +296,6 @@ struct nand_hw_control { * @eccbytes: [INTERN] number of ecc bytes per ecc-calculation step * @eccsteps: [INTERN] number of ecc calculation steps per page * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) - * @chip_lock: [INTERN] spinlock used to protect access to this structure and the chip * @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress * @state: [INTERN] the current state of the NAND device * @page_shift: [INTERN] number of address bits in a page (column address bits) @@ -317,7 +318,8 @@ struct nand_hw_control { * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup * @bbt_md: [REPLACEABLE] bad block table mirror descriptor * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan - * @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices + * @controller: [REPLACEABLE] a pointer to a hardware controller structure + * which is shared among multiple independend devices * @priv: [OPTIONAL] pointer to private chip date * @errstat: [OPTIONAL] hardware specific function to perform additional error status checks * (determine if errors are correctable) @@ -352,7 +354,6 @@ struct nand_chip { int eccbytes; int eccsteps; int chip_delay; - spinlock_t chip_lock; wait_queue_head_t wq; nand_state_t state; int page_shift; -- cgit v1.2.3 From 41796c2ea9b74cdf3bc2c368193d15b8ae8950ca Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 23 May 2006 11:38:59 +0200 Subject: [MTD] Add platform support for NAND Add the data structures necessary to provide platform device support for NAND Signed-off-by: Thomas Gleixner --- include/linux/mtd/nand.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'include') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index b8792be3c4e0..05c6ecc07036 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -510,4 +510,51 @@ extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, #define NAND_SMALL_BADBLOCK_POS 5 #define NAND_LARGE_BADBLOCK_POS 0 +/** + * struct platform_nand_chip - chip level device structure + * + * @nr_chips: max. number of chips to scan for + * @chip_offs: chip number offset + * @nr_partitions: number of partitions pointed to be partitoons (or zero) + * @partitions: mtd partition list + * @chip_delay: R/B delay value in us + * @options: Option flags, e.g. 16bit buswidth + * @priv: hardware controller specific settings + */ +struct platform_nand_chip { + int nr_chips; + int chip_offset; + int nr_partitions; + struct mtd_partition *partitions; + int chip_delay; + unsigned int options; + void *priv; +}; + +/** + * struct platform_nand_ctrl - controller level device structure + * + * @hwcontrol: platform specific hardware control structure + * @dev_ready: platform specific function to read ready/busy pin + * @select_chip: platform specific chip select function + * @priv_data: private data to transport driver specific settings + * + * All fields are optional and depend on the hardware driver requirements + */ +struct platform_nand_ctrl { + void (*hwcontrol)(struct mtd_info *mtd, int cmd); + int (*dev_ready)(struct mtd_info *mtd); + void (*select_chip)(struct mtd_info *mtd, int chip); + void *priv; +}; + +/* Some helpers to access the data structures */ +static inline +struct platform_nand_chip *get_platform_nandchip(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + return chip->priv; +} + #endif /* __LINUX_MTD_NAND_H */ -- cgit v1.2.3 From ce4c61f184864991881ec789f7524f4b332eaafc Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 23 May 2006 11:43:28 +0200 Subject: [MTD] Add support for NDFC NAND controller NDFC NAND Flash controller is embedded in PPC EP44x SoCs. Add platform driver based support. Signed-off-by: Thomas Gleixner --- drivers/mtd/nand/Kconfig | 6 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/ndfc.c | 317 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/ndfc.h | 66 ++++++++++ 4 files changed, 390 insertions(+) create mode 100644 drivers/mtd/nand/ndfc.c create mode 100644 include/linux/mtd/ndfc.h (limited to 'include') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 4d235b91267d..c2cb87fc4cb8 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -129,6 +129,12 @@ config MTD_NAND_S3C2410_HWECC currently not be able to switch to software, as there is no implementation for ECC method used by the S3C2410 +config MTD_NAND_NDFC + tristate "NDFC NanD Flash Controller" + depends on MTD_NAND && 44x + help + NDFC Nand Flash Controllers are integrated in EP44x SoCs + config MTD_NAND_DISKONCHIP tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)" depends on MTD_NAND && EXPERIMENTAL diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 33475087dbff..f74759351c91 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -21,5 +21,6 @@ obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o +obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o nand-objs = nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c new file mode 100644 index 000000000000..22fd682b70ca --- /dev/null +++ b/drivers/mtd/nand/ndfc.c @@ -0,0 +1,317 @@ +/* + * drivers/mtd/ndfc.c + * + * Overview: + * Platform independend driver for NDFC (NanD Flash Controller) + * integrated into EP440 cores + * + * Author: Thomas Gleixner + * + * Copyright 2006 IBM + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct ndfc_nand_mtd { + struct mtd_info mtd; + struct nand_chip chip; + struct platform_nand_chip *pl_chip; +}; + +static struct ndfc_nand_mtd ndfc_mtd[NDFC_MAX_BANKS]; + +struct ndfc_controller { + void __iomem *ndfcbase; + struct nand_hw_control ndfc_control; + atomic_t childs_active; +}; + +static struct ndfc_controller ndfc_ctrl; + +static void ndfc_select_chip(struct mtd_info *mtd, int chip) +{ + uint32_t ccr; + struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *nandchip = mtd->priv; + struct ndfc_nand_mtd *nandmtd = nandchip->priv; + struct platform_nand_chip *pchip = nandmtd->pl_chip; + + ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR); + if (chip >= 0) { + ccr &= ~NDFC_CCR_BS_MASK; + ccr |= NDFC_CCR_BS(chip + pchip->chip_offset); + } else + ccr |= NDFC_CCR_RESET_CE; + writel(ccr, ndfc->ndfcbase + NDFC_CCR); +} + +static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *chip = mtd->priv; + + switch (cmd) { + case NAND_CTL_SETCLE: + chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_CMD; + break; + case NAND_CTL_SETALE: + chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_ALE; + break; + default: + chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; + break; + } +} + +static int ndfc_ready(struct mtd_info *mtd) +{ + struct ndfc_controller *ndfc = &ndfc_ctrl; + + return __raw_readl(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; +} + +static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) +{ + uint32_t ccr; + struct ndfc_controller *ndfc = &ndfc_ctrl; + + ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR); + ccr |= NDFC_CCR_RESET_ECC; + __raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR); + wmb(); +} + +static int ndfc_calculate_ecc(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_code) +{ + struct ndfc_controller *ndfc = &ndfc_ctrl; + uint32_t ecc; + uint8_t *p = (uint8_t *)&ecc; + + wmb(); + ecc = __raw_readl(ndfc->ndfcbase + NDFC_ECC); + ecc_code[0] = p[1]; + ecc_code[1] = p[2]; + ecc_code[2] = p[3]; + + return 0; +} + +/* + * Speedups for buffer read/write/verify + * + * NDFC allows 32bit read/write of data. So we can speed up the buffer + * functions. No further checking, as nand_base will always read/write + * page aligned. + */ +static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct ndfc_controller *ndfc = &ndfc_ctrl; + uint32_t *p = (uint32_t *) buf; + + for(;len > 0; len -= 4) + *p++ = __raw_readl(ndfc->ndfcbase + NDFC_DATA); +} + +static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct ndfc_controller *ndfc = &ndfc_ctrl; + uint32_t *p = (uint32_t *) buf; + + for(;len > 0; len -= 4) + __raw_writel(*p++, ndfc->ndfcbase + NDFC_DATA); +} + +static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct ndfc_controller *ndfc = &ndfc_ctrl; + uint32_t *p = (uint32_t *) buf; + + for(;len > 0; len -= 4) + if (*p++ != __raw_readl(ndfc->ndfcbase + NDFC_DATA)) + return -EFAULT; + return 0; +} + +/* + * Initialize chip structure + */ +static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) +{ + struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *chip = &mtd->chip; + + chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA; + chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; + chip->hwcontrol = ndfc_hwcontrol; + chip->dev_ready = ndfc_ready; + chip->select_chip = ndfc_select_chip; + chip->chip_delay = 50; + chip->priv = mtd; + chip->options = mtd->pl_chip->options; + chip->controller = &ndfc->ndfc_control; + chip->read_buf = ndfc_read_buf; + chip->write_buf = ndfc_write_buf; + chip->verify_buf = ndfc_verify_buf; + chip->correct_data = nand_correct_data; + chip->enable_hwecc = ndfc_enable_hwecc; + chip->calculate_ecc = ndfc_calculate_ecc; + chip->eccmode = NAND_ECC_HW3_256; + chip->autooob = mtd->pl_chip->autooob; + mtd->mtd.priv = chip; + mtd->mtd.owner = THIS_MODULE; +} + +static int ndfc_chip_probe(struct platform_device *pdev) +{ + int rc; + struct platform_nand_chip *nc = pdev->dev.platform_data; + struct ndfc_chip_settings *settings = nc->priv; + struct ndfc_controller *ndfc = &ndfc_ctrl; + struct ndfc_nand_mtd *nandmtd; + + if (nc->chip_offset >= NDFC_MAX_BANKS || nc->nr_chips > NDFC_MAX_BANKS) + return -EINVAL; + + /* Set the bank settings */ + __raw_writel(settings->bank_settings, + ndfc->ndfcbase + NDFC_BCFG0 + (nc->chip_offset << 2)); + + nandmtd = &ndfc_mtd[pdev->id]; + if (nandmtd->pl_chip) + return -EBUSY; + + nandmtd->pl_chip = nc; + ndfc_chip_init(nandmtd); + + /* Scan for chips */ + if (nand_scan(&nandmtd->mtd, nc->nr_chips)) { + nandmtd->pl_chip = NULL; + return -ENODEV; + } + +#ifdef CONFIG_MTD_PARTITIONS + printk("Number of partitions %d\n", nc->nr_partitions); + if (nc->nr_partitions) { + struct mtd_info *mtd_ubi; + nc->partitions[NAND_PARTS_CONTENT_IDX].mtdp = &mtd_ubi; + + add_mtd_device(&nandmtd->mtd); /* for testing */ + add_mtd_partitions(&nandmtd->mtd, + nc->partitions, + nc->nr_partitions); + + add_mtd_device(mtd_ubi); + + } else +#else + add_mtd_device(&nandmtd->mtd); +#endif + + atomic_inc(&ndfc->childs_active); + return 0; +} + +static int ndfc_chip_remove(struct platform_device *pdev) +{ + return 0; +} + +static int ndfc_nand_probe(struct platform_device *pdev) +{ + struct platform_nand_ctrl *nc = pdev->dev.platform_data; + struct ndfc_controller_settings *settings = nc->priv; + struct resource *res = pdev->resource; + struct ndfc_controller *ndfc = &ndfc_ctrl; + unsigned long long phys = NDFC_PHYSADDR_OFFS | res->start; + + ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1); + if (!ndfc->ndfcbase) { + printk(KERN_ERR "NDFC: ioremap failed\n"); + return -EIO; + } + + __raw_writel(settings->ccr_settings, ndfc->ndfcbase + NDFC_CCR); + + spin_lock_init(&ndfc->ndfc_control.lock); + init_waitqueue_head(&ndfc->ndfc_control.wq); + + platform_set_drvdata(pdev, ndfc); + + printk("NDFC NAND Driver initialized. Chip-Rev: 0x%08x\n", + __raw_readl(ndfc->ndfcbase + NDFC_REVID)); + + return 0; +} + +static int ndfc_nand_remove(struct platform_device *pdev) +{ + struct ndfc_controller *ndfc = platform_get_drvdata(pdev); + + if (atomic_read(&ndfc->childs_active)) + return -EBUSY; + + if (ndfc) { + platform_set_drvdata(pdev, NULL); + iounmap(ndfc_ctrl.ndfcbase); + ndfc_ctrl.ndfcbase = NULL; + } + return 0; +} + +/* driver device registration */ + +static struct platform_driver ndfc_chip_driver = { + .probe = ndfc_chip_probe, + .remove = ndfc_chip_remove, + .driver = { + .name = "ndfc-chip", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver ndfc_nand_driver = { + .probe = ndfc_nand_probe, + .remove = ndfc_nand_remove, + .driver = { + .name = "ndfc-nand", + .owner = THIS_MODULE, + }, +}; + +static int __init ndfc_nand_init(void) +{ + int ret = platform_driver_register(&ndfc_nand_driver); + + if (!ret) + ret = platform_driver_register(&ndfc_chip_driver); + return ret; +} + +static void __exit ndfc_nand_exit(void) +{ + platform_driver_unregister(&ndfc_chip_driver); + platform_driver_unregister(&ndfc_nand_driver); +} + +module_init(ndfc_nand_init); +module_exit(ndfc_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Thomas Gleixner "); +MODULE_DESCRIPTION("Platform driver for NDFC"); diff --git a/include/linux/mtd/ndfc.h b/include/linux/mtd/ndfc.h new file mode 100644 index 000000000000..31d61f07d768 --- /dev/null +++ b/include/linux/mtd/ndfc.h @@ -0,0 +1,66 @@ +/* + * linux/include/linux/mtd/ndfc.h + * + * Copyright (c) 2006 Thomas Gleixner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Info: + * Contains defines, datastructures for ndfc nand controller + * + */ +#ifndef __LINUX_MTD_NDFC_H +#define __LINUX_MTD_NDFC_H + +/* NDFC Register definitions */ +#define NDFC_CMD 0x00 +#define NDFC_ALE 0x04 +#define NDFC_DATA 0x08 +#define NDFC_ECC 0x10 +#define NDFC_BCFG0 0x30 +#define NDFC_BCFG1 0x34 +#define NDFC_BCFG2 0x38 +#define NDFC_BCFG3 0x3c +#define NDFC_CCR 0x40 +#define NDFC_STAT 0x44 +#define NDFC_HWCTL 0x48 +#define NDFC_REVID 0x50 + +#define NDFC_STAT_IS_READY 0x01000000 + +#define NDFC_CCR_RESET_CE 0x80000000 /* CE Reset */ +#define NDFC_CCR_RESET_ECC 0x40000000 /* ECC Reset */ +#define NDFC_CCR_RIE 0x20000000 /* Interrupt Enable on Device Rdy */ +#define NDFC_CCR_REN 0x10000000 /* Enable wait for Rdy in LinearR */ +#define NDFC_CCR_ROMEN 0x08000000 /* Enable ROM In LinearR */ +#define NDFC_CCR_ARE 0x04000000 /* Auto-Read Enable */ +#define NDFC_CCR_BS(x) (((x) & 0x3) << 24) /* Select Bank on CE[x] */ +#define NDFC_CCR_BS_MASK 0x03000000 /* Select Bank */ +#define NDFC_CCR_ARAC0 0x00000000 /* 3 Addr, 1 Col 2 Row 512b page */ +#define NDFC_CCR_ARAC1 0x00001000 /* 4 Addr, 1 Col 3 Row 512b page */ +#define NDFC_CCR_ARAC2 0x00002000 /* 4 Addr, 2 Col 2 Row 2K page */ +#define NDFC_CCR_ARAC3 0x00003000 /* 5 Addr, 2 Col 3 Row 2K page */ +#define NDFC_CCR_ARAC_MASK 0x00003000 /* Auto-Read mode Addr Cycles */ +#define NDFC_CCR_RPG 0x0000C000 /* Auto-Read Page */ +#define NDFC_CCR_EBCC 0x00000004 /* EBC Configuration Completed */ +#define NDFC_CCR_DHC 0x00000002 /* Direct Hardware Control Enable */ + +#define NDFC_BxCFG_EN 0x80000000 /* Bank Enable */ +#define NDFC_BxCFG_CED 0x40000000 /* nCE Style */ +#define NDFC_BxCFG_SZ_MASK 0x08000000 /* Bank Size */ +#define NDFC_BxCFG_SZ_8BIT 0x00000000 /* 8bit */ +#define NDFC_BxCFG_SZ_16BIT 0x08000000 /* 16bit */ + +#define NDFC_MAX_BANKS 4 + +struct ndfc_controller_settings { + uint32_t ccr_settings; +}; + +struct ndfc_chip_settings { + uint32_t bank_settings; +}; + +#endif -- cgit v1.2.3 From 2c0a2bed9276ebbec5794edc07f66e21e9a1735c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 23 May 2006 11:50:56 +0200 Subject: [MTD] NAND whitespace and formatting cleanup Signed-off-by: Thomas Gleixner --- drivers/mtd/nand/nand_base.c | 104 ++++++++++++++++++++++++++----------------- include/linux/mtd/nand.h | 103 ++++++++++++++---------------------------- 2 files changed, 96 insertions(+), 111 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 7933ca273c95..6ef1893996ce 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -10,7 +10,7 @@ * http://www.linux-mtd.infradead.org/tech/nand.html * * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) - * 2002 Thomas Gleixner (tglx@linutronix.de) + * 2002 Thomas Gleixner (tglx@linutronix.de) * * 02-08-2004 tglx: support for strange chips, which cannot auto increment * pages on read / read_oob @@ -25,26 +25,30 @@ * 05-19-2004 tglx: Basic support for Renesas AG-AND chips * * 09-24-2004 tglx: add support for hardware controllers (e.g. ECC) shared - * among multiple independend devices. Suggestions and initial patch - * from Ben Dooks - * - * 12-05-2004 dmarlin: add workaround for Renesas AG-AND chips "disturb" issue. - * Basically, any block not rewritten may lose data when surrounding blocks - * are rewritten many times. JFFS2 ensures this doesn't happen for blocks - * it uses, but the Bad Block Table(s) may not be rewritten. To ensure they - * do not lose data, force them to be rewritten when some of the surrounding - * blocks are erased. Rather than tracking a specific nearby block (which - * could itself go bad), use a page address 'mask' to select several blocks - * in the same area, and rewrite the BBT when any of them are erased. - * - * 01-03-2005 dmarlin: added support for the device recovery command sequence for Renesas - * AG-AND chips. If there was a sudden loss of power during an erase operation, - * a "device recovery" operation must be performed when power is restored - * to ensure correct operation. - * - * 01-20-2005 dmarlin: added support for optional hardware specific callback routine to - * perform extra error status checks on erase and write failures. This required - * adding a wrapper function for nand_read_ecc. + * among multiple independend devices. Suggestions and initial + * patch from Ben Dooks + * + * 12-05-2004 dmarlin: add workaround for Renesas AG-AND chips "disturb" + * issue. Basically, any block not rewritten may lose data when + * surrounding blocks are rewritten many times. JFFS2 ensures + * this doesn't happen for blocks it uses, but the Bad Block + * Table(s) may not be rewritten. To ensure they do not lose + * data, force them to be rewritten when some of the surrounding + * blocks are erased. Rather than tracking a specific nearby + * block (which could itself go bad), use a page address 'mask' to + * select several blocks in the same area, and rewrite the BBT + * when any of them are erased. + * + * 01-03-2005 dmarlin: added support for the device recovery command sequence + * for Renesas AG-AND chips. If there was a sudden loss of power + * during an erase operation, a "device recovery" operation must + * be performed when power is restored to ensure correct + * operation. + * + * 01-20-2005 dmarlin: added support for optional hardware specific callback + * routine to perform extra error status checks on erase and write + * failures. This required adding a wrapper function for + * nand_read_ecc. * * 08-20-2005 vwool: suspend/resume added * @@ -132,32 +136,43 @@ static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len); static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len); static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len); -static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); +static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf); static int 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 nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); -static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); + size_t *retlen, u_char *buf, u_char *eccbuf, + struct nand_oobinfo *oobsel); +static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf); +static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf); static int 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 nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); -static int nand_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); + size_t *retlen, const u_char *buf, u_char *eccbuf, + struct nand_oobinfo *oobsel); +static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf); +static int nand_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen); static int 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); + unsigned long count, loff_t to, size_t *retlen, + u_char *eccbuf, struct nand_oobinfo *oobsel); static int nand_erase(struct mtd_info *mtd, struct erase_info *instr); static void nand_sync(struct mtd_info *mtd); /* Some internal functions */ -static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this, int page, u_char * oob_buf, +static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this, + int page, u_char * oob_buf, struct nand_oobinfo *oobsel, int mode); #ifdef CONFIG_MTD_NAND_VERIFY_WRITE -static int nand_verify_pages(struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, - u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode); +static int nand_verify_pages(struct mtd_info *mtd, struct nand_chip *this, + int page, int numpages, u_char *oob_buf, + struct nand_oobinfo *oobsel, int chipnr, + int oobmode); #else #define nand_verify_pages(...) (0) #endif -static int nand_get_device(struct nand_chip *this, struct mtd_info *mtd, int new_state); +static int nand_get_device(struct nand_chip *this, struct mtd_info *mtd, + int new_state); /** * nand_release_device - [GENERIC] release chip @@ -424,14 +439,16 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) page = (int)ofs; if (this->options & NAND_BUSWIDTH_16) { - this->cmdfunc(mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask); + this->cmdfunc(mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, + page & this->pagemask); bad = cpu_to_le16(this->read_word(mtd)); if (this->badblockpos & 0x1) bad >>= 8; if ((bad & 0xFF) != 0xff) res = 1; } else { - this->cmdfunc(mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask); + this->cmdfunc(mtd, NAND_CMD_READOOB, this->badblockpos, + page & this->pagemask); if (this->read_byte(mtd) != 0xff) res = 1; } @@ -498,7 +515,8 @@ static int nand_check_wp(struct mtd_info *mtd) * Check, if the block is bad. Either by reading the bad block table or * calling of the scan function. */ -static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) +static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, + int allowbbt) { struct nand_chip *this = mtd->priv; @@ -540,7 +558,8 @@ static void nand_wait_ready(struct mtd_info *mtd) * Send command to NAND device. This function is used for small page * devices (256/512 Bytes per page) */ -static void nand_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) +static void nand_command(struct mtd_info *mtd, unsigned command, int column, + int page_addr) { register struct nand_chip *this = mtd->priv; @@ -755,7 +774,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned command, int column, * * Get the device and lock it for exclusive access */ -static int nand_get_device(struct nand_chip *this, struct mtd_info *mtd, int new_state) +static int +nand_get_device(struct nand_chip *this, struct mtd_info *mtd, int new_state) { spinlock_t *lock = &this->controller->lock; wait_queue_head_t *wq = &this->controller->wq; @@ -942,7 +962,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this, int pag * nand_verify_pages - [GENERIC] verify the chip contents after a write * @mtd: MTD device structure * @this: NAND chip structure - * @page: startpage inside the chip, must be called with (page & this->pagemask) + * @page: startpage inside the chip, must be called with (page & this->pagemask) * @numpages: number of pages to verify * @oob_buf: out of band data buffer * @oobsel: out of band selecttion structre @@ -2293,8 +2313,8 @@ static void nand_resume(struct mtd_info *mtd) if (this->state == FL_PM_SUSPENDED) nand_release_device(mtd); else - printk(KERN_ERR "resume() called for the chip which is not in suspended state\n"); - + printk(KERN_ERR "nand_resume() called for a chip which is not " + "in suspended state\n"); } /* diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 05c6ecc07036..014ceefbec0e 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -11,47 +11,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * Info: - * Contains standard defines and IDs for NAND flash devices + * Info: + * Contains standard defines and IDs for NAND flash devices * - * Changelog: - * 01-31-2000 DMW Created - * 09-18-2000 SJH Moved structure out of the Disk-On-Chip drivers - * so it can be used by other NAND flash device - * drivers. I also changed the copyright since none - * of the original contents of this file are specific - * to DoC devices. David can whack me with a baseball - * bat later if I did something naughty. - * 10-11-2000 SJH Added private NAND flash structure for driver - * 10-24-2000 SJH Added prototype for 'nand_scan' function - * 10-29-2001 TG changed nand_chip structure to support - * hardwarespecific function for accessing control lines - * 02-21-2002 TG added support for different read/write adress and - * ready/busy line access function - * 02-26-2002 TG added chip_delay to nand_chip structure to optimize - * command delay times for different chips - * 04-28-2002 TG OOB config defines moved from nand.c to avoid duplicate - * defines in jffs2/wbuf.c - * 08-07-2002 TG forced bad block location to byte 5 of OOB, even if - * CONFIG_MTD_NAND_ECC_JFFS2 is not set - * 08-10-2002 TG extensions to nand_chip structure to support HW-ECC - * - * 08-29-2002 tglx nand_chip structure: data_poi for selecting - * internal / fs-driver buffer - * support for 6byte/512byte hardware ECC - * read_ecc, write_ecc extended for different oob-layout - * oob layout selections: NAND_NONE_OOB, NAND_JFFS2_OOB, - * NAND_YAFFS_OOB - * 11-25-2002 tglx Added Manufacturer code FUJITSU, NATIONAL - * Split manufacturer and device ID structures - * - * 02-08-2004 tglx added option field to nand structure for chip anomalities - * 05-25-2004 tglx added bad block table support, ST-MICRO manufacturer id - * update of nand_chip structure description - * 01-17-2005 dmarlin added extended commands for AG-AND device and added option - * for BBT_AUTO_REFRESH. - * 01-20-2005 dmarlin added optional pointer to hardware specific callback for - * extra error status checks. + * Changelog: + * See git changelog. */ #ifndef __LINUX_MTD_NAND_H #define __LINUX_MTD_NAND_H @@ -68,7 +32,8 @@ extern int nand_scan (struct mtd_info *mtd, int max_chips); extern void nand_release (struct mtd_info *mtd); /* Read raw data from the device without ECC */ -extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen); +extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, + size_t len, size_t ooblen); /* The maximum number of NAND chips in an array */ @@ -84,7 +49,7 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_ * Constants for hardware specific CLE/ALE/NCE function */ /* Select the chip by setting nCE to low */ -#define NAND_CTL_SETNCE 1 +#define NAND_CTL_SETNCE 1 /* Deselect the chip by setting nCE to high */ #define NAND_CTL_CLRNCE 2 /* Select the command latch by setting CLE to high */ @@ -285,19 +250,19 @@ struct nand_hw_control { * is read from the chip status register * @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready - * @calculate_ecc: [REPLACEABLE] function for ecc calculation or readback from ecc hardware + * @calculate_ecc: [REPLACEABLE] function for ecc calculation or readback from ecc hardware * @correct_data: [REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw) * @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only * be provided if a hardware ECC is available * @erase_cmd: [INTERN] erase command write function, selectable due to AND support * @scan_bbt: [REPLACEABLE] function to scan bad block table * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines - * @eccsize: [INTERN] databytes used per ecc-calculation - * @eccbytes: [INTERN] number of ecc bytes per ecc-calculation step + * @eccsize: [INTERN] databytes used per ecc-calculation + * @eccbytes: [INTERN] number of ecc bytes per ecc-calculation step * @eccsteps: [INTERN] number of ecc calculation steps per page * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) * @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress - * @state: [INTERN] the current state of the NAND device + * @state: [INTERN] the current state of the NAND device * @page_shift: [INTERN] number of address bits in a page (column address bits) * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry @@ -327,7 +292,7 @@ struct nand_hw_control { struct nand_chip { void __iomem *IO_ADDR_R; - void __iomem *IO_ADDR_W; + void __iomem *IO_ADDR_W; u_char (*read_byte)(struct mtd_info *mtd); void (*write_byte)(struct mtd_info *mtd, u_char byte); @@ -340,12 +305,12 @@ struct nand_chip { void (*select_chip)(struct mtd_info *mtd, int chip); int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); - void (*hwcontrol)(struct mtd_info *mtd, int cmd); - int (*dev_ready)(struct mtd_info *mtd); - void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); - int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state); + void (*hwcontrol)(struct mtd_info *mtd, int cmd); + int (*dev_ready)(struct mtd_info *mtd); + void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); + int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state); int (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); - int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); + int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); void (*enable_hwecc)(struct mtd_info *mtd, int mode); void (*erase_cmd)(struct mtd_info *mtd, int page); int (*scan_bbt)(struct mtd_info *mtd); @@ -353,14 +318,14 @@ struct nand_chip { int eccsize; int eccbytes; int eccsteps; - int chip_delay; + int chip_delay; wait_queue_head_t wq; - nand_state_t state; - int page_shift; + nand_state_t state; + int page_shift; int phys_erase_shift; int bbt_erase_shift; int chip_shift; - u_char *data_buf; + u_char *data_buf; u_char *oob_buf; int oobdirty; u_char *data_poi; @@ -389,19 +354,19 @@ struct nand_chip { #define NAND_MFR_NATIONAL 0x8f #define NAND_MFR_RENESAS 0x07 #define NAND_MFR_STMICRO 0x20 -#define NAND_MFR_HYNIX 0xad +#define NAND_MFR_HYNIX 0xad /** * struct nand_flash_dev - NAND Flash Device ID Structure * - * @name: Identify the device type - * @id: device ID code - * @pagesize: Pagesize in bytes. Either 256 or 512 or 0 + * @name: Identify the device type + * @id: device ID code + * @pagesize: Pagesize in bytes. Either 256 or 512 or 0 * If the pagesize is 0, then the real pagesize * and the eraseize are determined from the * extended id bytes in the chip - * @erasesize: Size of an erase block in the flash device. - * @chipsize: Total chipsize in Mega Bytes + * @erasesize: Size of an erase block in the flash device. + * @chipsize: Total chipsize in Mega Bytes * @options: Bitfield to store chip relevant options */ struct nand_flash_dev { @@ -416,7 +381,7 @@ struct nand_flash_dev { /** * struct nand_manufacturers - NAND Flash Manufacturer ID Structure * @name: Manufacturer name - * @id: manufacturer ID code of device. + * @id: manufacturer ID code of device. */ struct nand_manufacturers { int id; @@ -456,7 +421,7 @@ struct nand_bbt_descr { int veroffs; uint8_t version[NAND_MAX_CHIPS]; int len; - int maxblocks; + int maxblocks; int reserved_block_code; uint8_t *pattern; }; @@ -501,8 +466,8 @@ extern int nand_default_bbt (struct mtd_info *mtd); extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt); extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt); extern int nand_do_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 flags); + size_t * retlen, u_char * buf, u_char * oob_buf, + struct nand_oobinfo *oobsel, int flags); /* * Constants for oob configuration @@ -526,7 +491,7 @@ struct platform_nand_chip { int chip_offset; int nr_partitions; struct mtd_partition *partitions; - int chip_delay; + int chip_delay; unsigned int options; void *priv; }; @@ -542,8 +507,8 @@ struct platform_nand_chip { * All fields are optional and depend on the hardware driver requirements */ struct platform_nand_ctrl { - void (*hwcontrol)(struct mtd_info *mtd, int cmd); - int (*dev_ready)(struct mtd_info *mtd); + void (*hwcontrol)(struct mtd_info *mtd, int cmd); + int (*dev_ready)(struct mtd_info *mtd); void (*select_chip)(struct mtd_info *mtd, int chip); void *priv; }; -- cgit v1.2.3 From 58dd8f2bfdcad1b219a4a92a2aadd8ea8c819f79 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 23 May 2006 11:52:35 +0200 Subject: [MTD] NAND consolidate data types The NAND driver used a mix of unsigned char, u_char amd uint8_t data types. Consolidate to uint8_t usage Signed-off-by: Thomas Gleixner --- drivers/mtd/nand/nand_base.c | 106 +++++++++++++++++++++---------------------- include/linux/mtd/nand.h | 22 ++++----- 2 files changed, 64 insertions(+), 64 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 6ef1893996ce..afa77d1ed900 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -118,7 +118,7 @@ static struct nand_oobinfo nand_oob_64 = { }; /* This is used for padding purposes in nand_write_oob */ -static u_char ffchars[] = { +static uint8_t ffchars[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, @@ -132,39 +132,39 @@ static u_char ffchars[] = { /* * NAND low-level MTD interface functions */ -static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len); -static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len); -static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len); +static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len); +static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len); +static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len); static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf); + size_t *retlen, uint8_t *buf); static int nand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf, u_char *eccbuf, + size_t *retlen, uint8_t *buf, uint8_t *eccbuf, struct nand_oobinfo *oobsel); static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf); + size_t *retlen, uint8_t *buf); static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf); + size_t *retlen, const uint8_t *buf); static int nand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf, u_char *eccbuf, + size_t *retlen, const uint8_t *buf, uint8_t *eccbuf, struct nand_oobinfo *oobsel); static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf); + size_t *retlen, const uint8_t *buf); static int nand_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); static int 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); + uint8_t *eccbuf, struct nand_oobinfo *oobsel); static int nand_erase(struct mtd_info *mtd, struct erase_info *instr); static void nand_sync(struct mtd_info *mtd); /* Some internal functions */ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this, - int page, u_char * oob_buf, + int page, uint8_t * oob_buf, struct nand_oobinfo *oobsel, int mode); #ifdef CONFIG_MTD_NAND_VERIFY_WRITE static int nand_verify_pages(struct mtd_info *mtd, struct nand_chip *this, - int page, int numpages, u_char *oob_buf, + int page, int numpages, uint8_t *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode); #else @@ -201,7 +201,7 @@ static void nand_release_device(struct mtd_info *mtd) * * Default read function for 8bit buswith */ -static u_char nand_read_byte(struct mtd_info *mtd) +static uint8_t nand_read_byte(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; return readb(this->IO_ADDR_R); @@ -214,7 +214,7 @@ static u_char nand_read_byte(struct mtd_info *mtd) * * Default write function for 8it buswith */ -static void nand_write_byte(struct mtd_info *mtd, u_char byte) +static void nand_write_byte(struct mtd_info *mtd, uint8_t byte) { struct nand_chip *this = mtd->priv; writeb(byte, this->IO_ADDR_W); @@ -227,10 +227,10 @@ static void nand_write_byte(struct mtd_info *mtd, u_char byte) * Default read function for 16bit buswith with * endianess conversion */ -static u_char nand_read_byte16(struct mtd_info *mtd) +static uint8_t nand_read_byte16(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - return (u_char) cpu_to_le16(readw(this->IO_ADDR_R)); + return (uint8_t) cpu_to_le16(readw(this->IO_ADDR_R)); } /** @@ -241,7 +241,7 @@ static u_char nand_read_byte16(struct mtd_info *mtd) * Default write function for 16bit buswith with * endianess conversion */ -static void nand_write_byte16(struct mtd_info *mtd, u_char byte) +static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte) { struct nand_chip *this = mtd->priv; writew(le16_to_cpu((u16) byte), this->IO_ADDR_W); @@ -305,7 +305,7 @@ static void nand_select_chip(struct mtd_info *mtd, int chip) * * Default write function for 8bit buswith */ -static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { int i; struct nand_chip *this = mtd->priv; @@ -322,7 +322,7 @@ static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) * * Default read function for 8bit buswith */ -static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { int i; struct nand_chip *this = mtd->priv; @@ -339,7 +339,7 @@ static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) * * Default verify function for 8bit buswith */ -static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { int i; struct nand_chip *this = mtd->priv; @@ -359,7 +359,7 @@ static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) * * Default write function for 16bit buswith */ -static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) +static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) { int i; struct nand_chip *this = mtd->priv; @@ -379,7 +379,7 @@ static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) * * Default read function for 16bit buswith */ -static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len) +static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) { int i; struct nand_chip *this = mtd->priv; @@ -398,7 +398,7 @@ static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len) * * Default verify function for 16bit buswith */ -static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) +static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) { int i; struct nand_chip *this = mtd->priv; @@ -472,7 +472,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *this = mtd->priv; - u_char buf[2] = { 0, 0 }; + uint8_t buf[2] = { 0, 0 }; size_t retlen; int block; @@ -600,11 +600,11 @@ static void nand_command(struct mtd_info *mtd, unsigned command, int column, this->write_byte(mtd, column); } if (page_addr != -1) { - this->write_byte(mtd, (unsigned char)(page_addr & 0xff)); - this->write_byte(mtd, (unsigned char)((page_addr >> 8) & 0xff)); + this->write_byte(mtd, (uint8_t)(page_addr & 0xff)); + this->write_byte(mtd, (uint8_t)((page_addr >> 8) & 0xff)); /* One more address cycle for devices > 32MiB */ if (this->chipsize > (32 << 20)) - this->write_byte(mtd, (unsigned char)((page_addr >> 16) & 0x0f)); + this->write_byte(mtd, (uint8_t)((page_addr >> 16) & 0x0f)); } /* Latch in address */ this->hwcontrol(mtd, NAND_CTL_CLRALE); @@ -692,11 +692,11 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned command, int column, this->write_byte(mtd, column >> 8); } if (page_addr != -1) { - this->write_byte(mtd, (unsigned char)(page_addr & 0xff)); - this->write_byte(mtd, (unsigned char)((page_addr >> 8) & 0xff)); + this->write_byte(mtd, (uint8_t)(page_addr & 0xff)); + this->write_byte(mtd, (uint8_t)((page_addr >> 8) & 0xff)); /* One more address cycle for devices > 128MiB */ if (this->chipsize > (128 << 20)) - this->write_byte(mtd, (unsigned char)((page_addr >> 16) & 0xff)); + this->write_byte(mtd, (uint8_t)((page_addr >> 16) & 0xff)); } /* Latch in address */ this->hwcontrol(mtd, NAND_CTL_CLRALE); @@ -874,10 +874,10 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) * Cached programming is not supported yet. */ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this, int page, - u_char *oob_buf, struct nand_oobinfo *oobsel, int cached) + uint8_t *oob_buf, struct nand_oobinfo *oobsel, int cached) { int i, status; - u_char ecc_code[32]; + uint8_t ecc_code[32]; int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; int *oob_config = oobsel->eccpos; int datidx = 0, eccidx = 0, eccsteps = this->eccsteps; @@ -978,12 +978,12 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this, int pag * it early in the page write stage. Better to write no data than invalid data. */ static int nand_verify_pages(struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, - u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode) + uint8_t *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode) { int i, j, datidx = 0, oobofs = 0, res = -EIO; int eccsteps = this->eccsteps; int hweccbytes; - u_char oobdata[64]; + uint8_t oobdata[64]; hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0; @@ -1078,7 +1078,7 @@ static int nand_verify_pages(struct mtd_info *mtd, struct nand_chip *this, int p * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL * and flags = 0xff */ -static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf) { return nand_do_read_ecc(mtd, from, len, retlen, buf, NULL, &mtd->oobinfo, 0xff); } @@ -1096,7 +1096,7 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retl * This function simply calls nand_do_read_ecc with flags = 0xff */ static int 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) + size_t *retlen, uint8_t *buf, uint8_t *oob_buf, struct nand_oobinfo *oobsel) { /* use userspace supplied oobinfo, if zero */ if (oobsel == NULL) @@ -1121,15 +1121,15 @@ static int nand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, * NAND read with ECC */ int nand_do_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 flags) + size_t *retlen, uint8_t *buf, uint8_t *oob_buf, struct nand_oobinfo *oobsel, int flags) { int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1; int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; struct nand_chip *this = mtd->priv; - u_char *data_poi, *oob_data = oob_buf; - u_char ecc_calc[32]; - u_char ecc_code[32]; + uint8_t *data_poi, *oob_data = oob_buf; + uint8_t ecc_calc[32]; + uint8_t ecc_code[32]; int eccmode, eccsteps; int *oob_config, datidx; int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; @@ -1383,7 +1383,7 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, * * NAND read out-of-band data from the spare area */ -static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf) { int i, col, page, chipnr; struct nand_chip *this = mtd->priv; @@ -1550,7 +1550,7 @@ int nand_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, s * forces the 0xff fill before using the buffer again. * */ -static u_char *nand_prepare_oobbuf(struct mtd_info *mtd, u_char *fsbuf, struct nand_oobinfo *oobsel, +static uint8_t *nand_prepare_oobbuf(struct mtd_info *mtd, uint8_t *fsbuf, struct nand_oobinfo *oobsel, int autoplace, int numpages) { struct nand_chip *this = mtd->priv; @@ -1599,7 +1599,7 @@ static u_char *nand_prepare_oobbuf(struct mtd_info *mtd, u_char *fsbuf, struct n * This function simply calls nand_write_ecc with oob buffer and oobsel = NULL * */ -static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf) { return (nand_write_ecc(mtd, to, len, retlen, buf, NULL, NULL)); } @@ -1617,13 +1617,13 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retle * NAND write with ECC */ static int nand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf, u_char *eccbuf, + size_t *retlen, const uint8_t *buf, uint8_t *eccbuf, struct nand_oobinfo *oobsel) { int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr; int autoplace = 0, numpages, totalpages; struct nand_chip *this = mtd->priv; - u_char *oobbuf, *bufstart; + uint8_t *oobbuf, *bufstart; int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); DEBUG(MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int)to, (int)len); @@ -1680,12 +1680,12 @@ static int nand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, /* Calc number of pages we can write in one go */ numpages = min(ppblock - (startpage & (ppblock - 1)), totalpages); oobbuf = nand_prepare_oobbuf(mtd, eccbuf, oobsel, autoplace, numpages); - bufstart = (u_char *) buf; + bufstart = (uint8_t *) buf; /* Loop until all data is written */ while (written < len) { - this->data_poi = (u_char *) &buf[written]; + this->data_poi = (uint8_t *) &buf[written]; /* Write one page. If this is the last page to write * or the last page in this block, then use the * real pageprogram command, else select cached programming @@ -1764,7 +1764,7 @@ static int nand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, * * NAND write out-of-band */ -static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf) { int column, page, status, ret = -EIO, chipnr; struct nand_chip *this = mtd->priv; @@ -1884,13 +1884,13 @@ static int nand_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned l * NAND write with iovec with ecc */ static int 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) + loff_t to, size_t *retlen, uint8_t *eccbuf, struct nand_oobinfo *oobsel) { int i, page, len, total_len, ret = -EIO, written = 0, chipnr; int oob, numpages, autoplace = 0, startpage; struct nand_chip *this = mtd->priv; int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); - u_char *oobbuf, *bufstart; + uint8_t *oobbuf, *bufstart; /* Preset written len for early exit */ *retlen = 0; @@ -1959,7 +1959,7 @@ static int nand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, unsign /* Do not cross block boundaries */ numpages = min(ppblock - (startpage & (ppblock - 1)), numpages); oobbuf = nand_prepare_oobbuf(mtd, NULL, oobsel, autoplace, numpages); - bufstart = (u_char *) vecs->iov_base; + bufstart = (uint8_t *) vecs->iov_base; bufstart += len; this->data_poi = bufstart; oob = 0; @@ -1990,7 +1990,7 @@ static int nand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, unsign int cnt = 0; while (cnt < mtd->oobblock) { if (vecs->iov_base != NULL && vecs->iov_len) - this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++]; + this->data_buf[cnt++] = ((uint8_t *) vecs->iov_base)[len++]; /* Check, if we have to switch to the next tuple */ if (len >= (int)vecs->iov_len) { vecs++; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 014ceefbec0e..601c5c703a05 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -294,14 +294,14 @@ struct nand_chip { void __iomem *IO_ADDR_R; void __iomem *IO_ADDR_W; - u_char (*read_byte)(struct mtd_info *mtd); - void (*write_byte)(struct mtd_info *mtd, u_char byte); + uint8_t (*read_byte)(struct mtd_info *mtd); + void (*write_byte)(struct mtd_info *mtd, uint8_t byte); u16 (*read_word)(struct mtd_info *mtd); void (*write_word)(struct mtd_info *mtd, u16 word); - void (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len); - void (*read_buf)(struct mtd_info *mtd, u_char *buf, int len); - int (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len); + void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); + void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); + int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); void (*select_chip)(struct mtd_info *mtd, int chip); int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); @@ -309,8 +309,8 @@ struct nand_chip { int (*dev_ready)(struct mtd_info *mtd); void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state); - int (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); - int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); + int (*calculate_ecc)(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code); + int (*correct_data)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc); void (*enable_hwecc)(struct mtd_info *mtd, int mode); void (*erase_cmd)(struct mtd_info *mtd, int page); int (*scan_bbt)(struct mtd_info *mtd); @@ -325,10 +325,10 @@ struct nand_chip { int phys_erase_shift; int bbt_erase_shift; int chip_shift; - u_char *data_buf; - u_char *oob_buf; + uint8_t *data_buf; + uint8_t *oob_buf; int oobdirty; - u_char *data_poi; + uint8_t *data_poi; unsigned int options; int badblockpos; int numchips; @@ -466,7 +466,7 @@ extern int nand_default_bbt (struct mtd_info *mtd); extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt); extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt); extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, - size_t * retlen, u_char * buf, u_char * oob_buf, + size_t * retlen, uint8_t * buf, uint8_t * oob_buf, struct nand_oobinfo *oobsel, int flags); /* -- cgit v1.2.3 From 6dfc6d250d0b7ebaa6423c44dcd09fcfe68deabd Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 23 May 2006 12:00:46 +0200 Subject: [MTD] NAND modularize ECC First step of modularizing ECC support. - Move ECC related functionality into a seperate embedded data structure - Get rid of the hardware dependend constants to simplify new ECC models Signed-off-by: Thomas Gleixner --- drivers/mtd/nand/ams-delta.c | 2 +- drivers/mtd/nand/au1550nd.c | 2 +- drivers/mtd/nand/autcpu12.c | 2 +- drivers/mtd/nand/cs553x_nand.c | 12 ++-- drivers/mtd/nand/diskonchip.c | 10 +-- drivers/mtd/nand/h1910.c | 2 +- drivers/mtd/nand/nand_base.c | 146 +++++++++++++------------------------- drivers/mtd/nand/nandsim.c | 2 +- drivers/mtd/nand/ndfc.c | 10 +-- drivers/mtd/nand/ppchameleonevb.c | 4 +- drivers/mtd/nand/rtc_from4.c | 12 ++-- drivers/mtd/nand/s3c2410.c | 16 +++-- drivers/mtd/nand/sharpsl.c | 10 +-- drivers/mtd/nand/toto.c | 2 +- drivers/mtd/nand/ts7250.c | 2 +- include/linux/mtd/nand.h | 63 ++++++++-------- 16 files changed, 131 insertions(+), 166 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index 5a349eb316f5..aeaf2dece095 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -192,7 +192,7 @@ static int __init ams_delta_init(void) } /* 25 us command delay time */ this->chip_delay = 30; - this->eccmode = NAND_ECC_SOFT; + this->ecc.mode = NAND_ECC_SOFT; /* Set chip enabled, but */ ams_delta_latch2_write(NAND_MASK, AMS_DELTA_LATCH2_NAND_NRE | diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index d9a0143e1d3a..d7f04abfe18e 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -578,7 +578,7 @@ static int __init au1xxx_nand_init(void) /* 30 us command delay time */ this->chip_delay = 30; - this->eccmode = NAND_ECC_SOFT; + this->ecc.mode = NAND_ECC_SOFT; this->options = NAND_NO_AUTOINCR; diff --git a/drivers/mtd/nand/autcpu12.c b/drivers/mtd/nand/autcpu12.c index 43b296040d7f..dbb1b6267ade 100644 --- a/drivers/mtd/nand/autcpu12.c +++ b/drivers/mtd/nand/autcpu12.c @@ -163,7 +163,7 @@ static int __init autcpu12_init(void) this->dev_ready = autcpu12_device_ready; /* 20 us command delay time */ this->chip_delay = 20; - this->eccmode = NAND_ECC_SOFT; + this->ecc.mode = NAND_ECC_SOFT; /* Enable the following for a flash based bad block table */ /* diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index bf251253ea1f..064f3feadf53 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -242,11 +242,13 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) this->chip_delay = 0; - this->eccmode = NAND_ECC_HW3_256; - this->enable_hwecc = cs_enable_hwecc; - this->calculate_ecc = cs_calculate_ecc; - this->correct_data = nand_correct_data; - + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 256; + this->ecc.bytes = 3; + this->ecc.hwctl = cs_enable_hwecc; + this->ecc.calculate = cs_calculate_ecc; + this->ecc.correct = nand_correct_data; + /* Enable the following for a flash based bad block table */ this->options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR; diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index a2391c66a63f..128c937af32f 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -1674,12 +1674,14 @@ static int __init doc_probe(unsigned long physadr) nand->dev_ready = doc200x_dev_ready; nand->waitfunc = doc200x_wait; nand->block_bad = doc200x_block_bad; - nand->enable_hwecc = doc200x_enable_hwecc; - nand->calculate_ecc = doc200x_calculate_ecc; - nand->correct_data = doc200x_correct_data; + nand->ecc.hwctl = doc200x_enable_hwecc; + nand->ecc.calculate = doc200x_calculate_ecc; + nand->ecc.correct = doc200x_correct_data; nand->autooob = &doc200x_oobinfo; - nand->eccmode = NAND_ECC_HW6_512; + nand->ecc.mode = NAND_ECC_HW_SYNDROME; + nand->ecc.size = 512; + nand->ecc.bytes = 6; nand->options = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME; doc->physadr = physadr; diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c index 9848eb09b884..06e91fa11b34 100644 --- a/drivers/mtd/nand/h1910.c +++ b/drivers/mtd/nand/h1910.c @@ -149,7 +149,7 @@ static int __init h1910_init(void) this->dev_ready = NULL; /* unknown whether that was correct or not so we will just do it like this */ /* 15 us command delay time */ this->chip_delay = 50; - this->eccmode = NAND_ECC_SOFT; + this->ecc.mode = NAND_ECC_SOFT; this->options = NAND_NO_AUTOINCR; /* Scan to find existence of the device */ diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 37db98a58c34..98792ec4c2dc 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -879,9 +879,9 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this, int pag { int i, status; uint8_t ecc_code[32]; - int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; + int eccmode = oobsel->useecc ? this->ecc.mode : NAND_ECC_NONE; int *oob_config = oobsel->eccpos; - int datidx = 0, eccidx = 0, eccsteps = this->eccsteps; + int datidx = 0, eccidx = 0, eccsteps = this->ecc.steps; int eccbytes = 0; /* FIXME: Enable cached programming */ @@ -901,20 +901,20 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this, int pag /* Software ecc 3/256, write all */ case NAND_ECC_SOFT: for (; eccsteps; eccsteps--) { - this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); + this->ecc.calculate(mtd, &this->data_poi[datidx], ecc_code); for (i = 0; i < 3; i++, eccidx++) oob_buf[oob_config[eccidx]] = ecc_code[i]; - datidx += this->eccsize; + datidx += this->ecc.size; } this->write_buf(mtd, this->data_poi, mtd->oobblock); break; default: - eccbytes = this->eccbytes; + eccbytes = this->ecc.bytes; for (; eccsteps; eccsteps--) { /* enable hardware ecc logic for write */ - this->enable_hwecc(mtd, NAND_ECC_WRITE); - this->write_buf(mtd, &this->data_poi[datidx], this->eccsize); - this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); + this->ecc.hwctl(mtd, NAND_ECC_WRITE); + this->write_buf(mtd, &this->data_poi[datidx], this->ecc.size); + this->ecc.calculate(mtd, &this->data_poi[datidx], ecc_code); for (i = 0; i < eccbytes; i++, eccidx++) oob_buf[oob_config[eccidx]] = ecc_code[i]; /* If the hardware ecc provides syndromes then @@ -922,7 +922,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this, int pag * the data bytes (words) */ if (this->options & NAND_HWECC_SYNDROME) this->write_buf(mtd, ecc_code, eccbytes); - datidx += this->eccsize; + datidx += this->ecc.size; } break; } @@ -1155,7 +1155,7 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) oobsel = this->autooob; - eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; + eccmode = oobsel->useecc ? this->ecc.mode : NAND_ECC_NONE; oob_config = oobsel->eccpos; /* Select the NAND device */ @@ -1170,8 +1170,8 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, col = from & (mtd->oobblock - 1); end = mtd->oobblock; - ecc = this->eccsize; - eccbytes = this->eccbytes; + ecc = this->ecc.size; + eccbytes = this->ecc.bytes; if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME)) compareecc = 0; @@ -1216,7 +1216,7 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, oobsel->useecc == MTD_NANDECC_AUTOPL_USR) oob_data = &this->data_buf[end]; - eccsteps = this->eccsteps; + eccsteps = this->ecc.steps; switch (eccmode) { case NAND_ECC_NONE:{ @@ -1234,12 +1234,12 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ this->read_buf(mtd, data_poi, end); for (i = 0, datidx = 0; eccsteps; eccsteps--, i += 3, datidx += ecc) - this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); + this->ecc.calculate(mtd, &data_poi[datidx], &ecc_calc[i]); break; default: for (i = 0, datidx = 0; eccsteps; eccsteps--, i += eccbytes, datidx += ecc) { - this->enable_hwecc(mtd, NAND_ECC_READ); + this->ecc.hwctl(mtd, NAND_ECC_READ); this->read_buf(mtd, &data_poi[datidx], ecc); /* HW ecc with syndrome calculation must read the @@ -1247,19 +1247,19 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, if (!compareecc) { /* Some hw ecc generators need to know when the * syndrome is read from flash */ - this->enable_hwecc(mtd, NAND_ECC_READSYN); + this->ecc.hwctl(mtd, NAND_ECC_READSYN); this->read_buf(mtd, &oob_data[i], eccbytes); /* We calc error correction directly, it checks the hw * generator for an error, reads back the syndrome and * does the error correction on the fly */ - ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]); + ecc_status = this->ecc.correct(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]); if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { DEBUG(MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); ecc_failed++; } } else { - this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); + this->ecc.calculate(mtd, &data_poi[datidx], &ecc_calc[i]); } } break; @@ -1277,8 +1277,8 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, ecc_code[j] = oob_data[oob_config[j]]; /* correct data, if necessary */ - for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) { - ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]); + for (i = 0, j = 0, datidx = 0; i < this->ecc.steps; i++, datidx += ecc) { + ecc_status = this->ecc.correct(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]); /* Get next chunk of ecc bytes */ j += eccbytes; @@ -1315,7 +1315,7 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, break; case MTD_NANDECC_PLACE: /* YAFFS1 legacy mode */ - oob_data += this->eccsteps * sizeof(int); + oob_data += this->ecc.steps * sizeof(int); default: oob_data += mtd->oobsize; } @@ -2648,99 +2648,49 @@ int nand_scan(struct mtd_info *mtd, int maxchips) * 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; - 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; + switch (this->ecc.mode) { + case NAND_ECC_HW: + case NAND_ECC_HW_SYNDROME: + if (!this->ecc.calculate || !this->ecc.correct || + !this->ecc.hwctl) { + printk(KERN_WARNING "No ECC functions supplied, " + "Hardware ECC not possible\n"); + BUG(); + } + if (mtd->oobblock >= this->ecc.size) + break; + printk(KERN_WARNING "%d byte HW ECC not possible on " + "%d byte page size, fallback to SW ECC\n", + this->ecc.size, mtd->oobblock); + this->ecc.mode = NAND_ECC_SOFT; - case NAND_ECC_HW3_256: + case NAND_ECC_SOFT: + this->ecc.calculate = nand_calculate_ecc; + this->ecc.correct = nand_correct_data; + this->ecc.size = 256; + this->ecc.bytes = 3; 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; + this->ecc.size = mtd->oobblock; + this->ecc.bytes = 0; 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"); + this->ecc.mode); BUG(); } - mtd->eccsize = this->eccsize; - /* * Set the number of read / write steps for one page depending on ECC * mode */ - 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; + this->ecc.steps = mtd->oobblock / this->ecc.size; + if(this->ecc.steps * this->ecc.size != mtd->oobblock) { + printk(KERN_WARNING "Invalid ecc parameters\n"); + BUG(); } /* Initialize state, waitqueue and spinlock */ diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 6903f5b903c6..9008bc5493fb 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -1523,7 +1523,7 @@ static int __init ns_init_module(void) chip->verify_buf = ns_nand_verify_buf; chip->write_word = ns_nand_write_word; chip->read_word = ns_nand_read_word; - chip->eccmode = NAND_ECC_SOFT; + chip->ecc.mode = NAND_ECC_SOFT; chip->options |= NAND_SKIP_BBTSCAN; /* diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 22fd682b70ca..e2dc81de106a 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -168,10 +168,12 @@ static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) chip->read_buf = ndfc_read_buf; chip->write_buf = ndfc_write_buf; chip->verify_buf = ndfc_verify_buf; - chip->correct_data = nand_correct_data; - chip->enable_hwecc = ndfc_enable_hwecc; - chip->calculate_ecc = ndfc_calculate_ecc; - chip->eccmode = NAND_ECC_HW3_256; + chip->ecc.correct = nand_correct_data; + chip->ecc.hwctl = ndfc_enable_hwecc; + chip->ecc.calculate = ndfc_calculate_ecc; + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = 256; + chip->ecc.bytes = 3; chip->autooob = mtd->pl_chip->autooob; mtd->mtd.priv = chip; mtd->mtd.owner = THIS_MODULE; diff --git a/drivers/mtd/nand/ppchameleonevb.c b/drivers/mtd/nand/ppchameleonevb.c index 5d4d16fb1df6..9fab0998524d 100644 --- a/drivers/mtd/nand/ppchameleonevb.c +++ b/drivers/mtd/nand/ppchameleonevb.c @@ -257,7 +257,7 @@ static int __init ppchameleonevb_init(void) #endif this->chip_delay = NAND_BIG_DELAY_US; /* ECC mode */ - this->eccmode = NAND_ECC_SOFT; + this->ecc.mode = NAND_ECC_SOFT; /* Scan to find existence of the device (it could not be mounted) */ if (nand_scan(ppchameleon_mtd, 1)) { @@ -358,7 +358,7 @@ static int __init ppchameleonevb_init(void) this->chip_delay = NAND_SMALL_DELAY_US; /* ECC mode */ - this->eccmode = NAND_ECC_SOFT; + this->ecc.mode = NAND_ECC_SOFT; /* Scan to find existence of the device */ if (nand_scan(ppchameleonevb_mtd, 1)) { diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index bc9d849fbd5d..a2122fe4101a 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -570,19 +570,21 @@ static int __init rtc_from4_init(void) #ifdef RTC_FROM4_HWECC printk(KERN_INFO "rtc_from4_init: using hardware ECC detection.\n"); - this->eccmode = NAND_ECC_HW8_512; + this->ecc.mode = NAND_ECC_HW_SYNDROME; + this->ecc.size = 512; + this->ecc.bytes = 8; this->options |= NAND_HWECC_SYNDROME; /* return the status of extra status and ECC checks */ this->errstat = rtc_from4_errstat; /* set the nand_oobinfo to support FPGA H/W error detection */ this->autooob = &rtc_from4_nand_oobinfo; - this->enable_hwecc = rtc_from4_enable_hwecc; - this->calculate_ecc = rtc_from4_calculate_ecc; - this->correct_data = rtc_from4_correct_data; + this->ecc.hwctl = rtc_from4_enable_hwecc; + this->ecc.calculate = rtc_from4_calculate_ecc; + this->ecc.correct = rtc_from4_correct_data; #else printk(KERN_INFO "rtc_from4_init: using software ECC detection.\n"); - this->eccmode = NAND_ECC_SOFT; + this->ecc.mode = NAND_ECC_SOFT; #endif /* set the bad block tables to support debugging */ diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index f8002596de8b..608340a25278 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -520,18 +520,20 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, nmtd->set = set; if (hardware_ecc) { - chip->correct_data = s3c2410_nand_correct_data; - chip->enable_hwecc = s3c2410_nand_enable_hwecc; - chip->calculate_ecc = s3c2410_nand_calculate_ecc; - chip->eccmode = NAND_ECC_HW3_512; + chip->ecc.correct = s3c2410_nand_correct_data; + chip->ecc.hwctl = s3c2410_nand_enable_hwecc; + chip->ecc.calculate = s3c2410_nand_calculate_ecc; + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = 512; + chip->ecc.bytes = 3; chip->autooob = &nand_hw_eccoob; if (info->is_s3c2440) { - chip->enable_hwecc = s3c2440_nand_enable_hwecc; - chip->calculate_ecc = s3c2440_nand_calculate_ecc; + chip->ecc.hwctl = s3c2440_nand_enable_hwecc; + chip->ecc.calculate = s3c2440_nand_calculate_ecc; } } else { - chip->eccmode = NAND_ECC_SOFT; + chip->ecc.mode = NAND_ECC_SOFT; } } diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 60e10c0d6980..5554d0b97c8c 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -201,15 +201,17 @@ static int __init sharpsl_nand_init(void) /* 15 us command delay time */ this->chip_delay = 15; /* set eccmode using hardware ECC */ - this->eccmode = NAND_ECC_HW3_256; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 256; + this->ecc.bytes = 3; this->badblock_pattern = &sharpsl_bbt; if (machine_is_akita() || machine_is_borzoi()) { this->badblock_pattern = &sharpsl_akita_bbt; this->autooob = &akita_oobinfo; } - this->enable_hwecc = sharpsl_nand_enable_hwecc; - this->calculate_ecc = sharpsl_nand_calculate_ecc; - this->correct_data = nand_correct_data; + this->ecc.hwctl = sharpsl_nand_enable_hwecc; + this->ecc.calculate = sharpsl_nand_calculate_ecc; + this->ecc.correct = nand_correct_data; /* Scan to find existence of the device */ err = nand_scan(sharpsl_mtd, 1); diff --git a/drivers/mtd/nand/toto.c b/drivers/mtd/nand/toto.c index c51c89559514..50aa6a46911f 100644 --- a/drivers/mtd/nand/toto.c +++ b/drivers/mtd/nand/toto.c @@ -146,7 +146,7 @@ static int __init toto_init(void) this->dev_ready = NULL; /* 25 us command delay time */ this->chip_delay = 30; - this->eccmode = NAND_ECC_SOFT; + this->ecc.mode = NAND_ECC_SOFT; /* Scan to find existance of the device */ if (nand_scan(toto_mtd, 1)) { diff --git a/drivers/mtd/nand/ts7250.c b/drivers/mtd/nand/ts7250.c index 622db3127f7c..70bce1b0326c 100644 --- a/drivers/mtd/nand/ts7250.c +++ b/drivers/mtd/nand/ts7250.c @@ -155,7 +155,7 @@ static int __init ts7250_init(void) this->hwcontrol = ts7250_hwcontrol; this->dev_ready = ts7250_device_ready; this->chip_delay = 15; - this->eccmode = NAND_ECC_SOFT; + this->ecc.mode = NAND_ECC_SOFT; printk("Searching for NAND flash...\n"); /* Scan to find existence of the device */ diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 601c5c703a05..460525841a27 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -113,21 +113,12 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, /* * Constants for ECC_MODES */ - -/* No ECC. Usage is not recommended ! */ -#define NAND_ECC_NONE 0 -/* Software ECC 3 byte ECC per 256 Byte data */ -#define NAND_ECC_SOFT 1 -/* Hardware ECC 3 byte ECC per 256 Byte data */ -#define NAND_ECC_HW3_256 2 -/* Hardware ECC 3 byte ECC per 512 Byte data */ -#define NAND_ECC_HW3_512 3 -/* Hardware ECC 3 byte ECC per 512 Byte data */ -#define NAND_ECC_HW6_512 4 -/* Hardware ECC 8 byte ECC per 512 Byte data */ -#define NAND_ECC_HW8_512 6 -/* Hardware ECC 12 byte ECC per 2048 Byte data */ -#define NAND_ECC_HW12_2048 7 +typedef enum { + NAND_ECC_NONE, + NAND_ECC_SOFT, + NAND_ECC_HW, + NAND_ECC_HW_SYNDROME, +} nand_ecc_modes_t; /* * Constants for Hardware ECC @@ -230,6 +221,31 @@ struct nand_hw_control { wait_queue_head_t wq; }; +/** + * struct nand_ecc_ctrl - Control structure for ecc + * @mode: ecc mode + * @steps: number of ecc steps per page + * @size: data bytes per ecc step + * @bytes: ecc bytes per step + * @hwctl: function to control hardware ecc generator. Must only + * be provided if an hardware ECC is available + * @calculate: function for ecc calculation or readback from ecc hardware + * @correct: function for ecc correction, matching to ecc generator (sw/hw) + */ +struct nand_ecc_ctrl { + nand_ecc_modes_t mode; + int steps; + int size; + int bytes; + int (*hwctl)(struct mtd_info *mtd, int mode); + int (*calculate)(struct mtd_info *mtd, + const uint8_t *dat, + uint8_t *ecc_code); + int (*correct)(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, + uint8_t *calc_ecc); +}; + /** * struct nand_chip - NAND Private Flash Chip Data * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device @@ -250,16 +266,9 @@ struct nand_hw_control { * is read from the chip status register * @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready - * @calculate_ecc: [REPLACEABLE] function for ecc calculation or readback from ecc hardware - * @correct_data: [REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw) - * @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only - * be provided if a hardware ECC is available + * @ecc: [BOARDSPECIFIC] ecc control ctructure * @erase_cmd: [INTERN] erase command write function, selectable due to AND support * @scan_bbt: [REPLACEABLE] function to scan bad block table - * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines - * @eccsize: [INTERN] databytes used per ecc-calculation - * @eccbytes: [INTERN] number of ecc bytes per ecc-calculation step - * @eccsteps: [INTERN] number of ecc calculation steps per page * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) * @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress * @state: [INTERN] the current state of the NAND device @@ -309,15 +318,9 @@ struct nand_chip { int (*dev_ready)(struct mtd_info *mtd); void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state); - int (*calculate_ecc)(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code); - int (*correct_data)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc); - void (*enable_hwecc)(struct mtd_info *mtd, int mode); void (*erase_cmd)(struct mtd_info *mtd, int page); int (*scan_bbt)(struct mtd_info *mtd); - int eccmode; - int eccsize; - int eccbytes; - int eccsteps; + struct nand_ecc_ctrl ecc; int chip_delay; wait_queue_head_t wq; nand_state_t state; -- cgit v1.2.3 From 9a57d470fd4a77b9732fee97bed29c565c730af0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 23 May 2006 15:58:23 +0200 Subject: [MTD] NAND ECC hwctl function has no return value Fix the broken prototype Signed-off-by: Thomas Gleixner --- include/linux/mtd/nand.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 460525841a27..6931376ed68d 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -237,7 +237,7 @@ struct nand_ecc_ctrl { int steps; int size; int bytes; - int (*hwctl)(struct mtd_info *mtd, int mode); + void (*hwctl)(struct mtd_info *mtd, int mode); int (*calculate)(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code); -- cgit v1.2.3 From 9d8522df37f91621a70c5c0dbbf5bf2220b16798 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 23 May 2006 16:06:03 +0200 Subject: [MTD] Remove nand writev support NAND writev(_ecc) support is not longer necessary. Remove it. Signed-off-by: Thomas Gleixner --- drivers/mtd/devices/doc2000.c | 64 ------------- drivers/mtd/mtdconcat.c | 20 +--- drivers/mtd/mtdpart.c | 23 +---- drivers/mtd/nand/nand_base.c | 188 ------------------------------------- drivers/mtd/onenand/onenand_base.c | 140 --------------------------- include/linux/mtd/mtd.h | 2 - 6 files changed, 3 insertions(+), 434 deletions(-) (limited to 'include') diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index 423a34f4638c..6f32942fdf77 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -59,9 +59,6 @@ static int doc_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 doc_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 doc_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 doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf); static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, @@ -589,7 +586,6 @@ void DoC2k_init(struct mtd_info *mtd) mtd->write = doc_write; mtd->read_ecc = doc_read_ecc; mtd->write_ecc = doc_write_ecc; - mtd->writev_ecc = doc_writev_ecc; mtd->read_oob = doc_read_oob; mtd->write_oob = doc_write_oob; mtd->sync = NULL; @@ -965,66 +961,6 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, return 0; } -static int doc_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 char static_buf[512]; - static DEFINE_MUTEX(writev_buf_mutex); - - size_t totretlen = 0; - size_t thisvecofs = 0; - int ret= 0; - - mutex_lock(&writev_buf_mutex); - - while(count) { - size_t thislen, thisretlen; - unsigned char *buf; - - buf = vecs->iov_base + thisvecofs; - thislen = vecs->iov_len - thisvecofs; - - - if (thislen >= 512) { - thislen = thislen & ~(512-1); - thisvecofs += thislen; - } else { - /* Not enough to fill a page. Copy into buf */ - memcpy(static_buf, buf, thislen); - buf = &static_buf[thislen]; - - while(count && thislen < 512) { - vecs++; - count--; - thisvecofs = min((512-thislen), vecs->iov_len); - memcpy(buf, vecs->iov_base, thisvecofs); - thislen += thisvecofs; - buf += thisvecofs; - } - buf = static_buf; - } - if (count && thisvecofs == vecs->iov_len) { - thisvecofs = 0; - vecs++; - count--; - } - ret = doc_write_ecc(mtd, to, thislen, &thisretlen, buf, eccbuf, oobsel); - - totretlen += thisretlen; - - if (ret || thisretlen != thislen) - break; - - to += thislen; - } - - mutex_unlock(&writev_buf_mutex); - *retlen = totretlen; - return ret; -} - - static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t * retlen, u_char * buf) { diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index a5e8373349a5..a6fcee2713b0 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -253,9 +253,8 @@ concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, } static int -concat_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) +concat_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t * retlen) { struct mtd_concat *concat = CONCAT(mtd); struct kvec *vecs_copy; @@ -315,10 +314,6 @@ concat_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, if (!(subdev->flags & MTD_WRITEABLE)) err = -EROFS; - else if (eccbuf) - err = subdev->writev_ecc(subdev, &vecs_copy[entry_low], - entry_high - entry_low + 1, to, &retsize, - eccbuf, oobsel); else err = subdev->writev(subdev, &vecs_copy[entry_low], entry_high - entry_low + 1, to, &retsize); @@ -333,8 +328,6 @@ concat_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, *retlen += retsize; total_len -= wsize; - if (concat->mtd.type == MTD_NANDFLASH && eccbuf) - eccbuf += mtd->oobavail * (wsize / mtd->writesize); if (total_len == 0) break; @@ -347,13 +340,6 @@ concat_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, return err; } -static int -concat_writev(struct mtd_info *mtd, const struct kvec *vecs, - unsigned long count, loff_t to, size_t * retlen) -{ - return concat_writev_ecc(mtd, vecs, count, to, retlen, NULL, NULL); -} - static int concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) @@ -843,8 +829,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c concat->mtd.write_ecc = concat_write_ecc; if (subdev[0]->writev) concat->mtd.writev = concat_writev; - if (subdev[0]->writev_ecc) - concat->mtd.writev_ecc = concat_writev_ecc; if (subdev[0]->read_oob) concat->mtd.read_oob = concat_read_oob; if (subdev[0]->write_oob) diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 082662f90481..ae675608fa91 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -208,13 +208,8 @@ static int part_writev (struct mtd_info *mtd, const struct kvec *vecs, struct mtd_part *part = PART(mtd); if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - if (part->master->writev_ecc == NULL) - return part->master->writev (part->master, vecs, count, + return part->master->writev (part->master, vecs, count, to + part->offset, retlen); - else - return part->master->writev_ecc (part->master, vecs, count, - to + part->offset, retlen, - NULL, &mtd->oobinfo); } static int part_readv (struct mtd_info *mtd, struct kvec *vecs, @@ -230,20 +225,6 @@ static int part_readv (struct mtd_info *mtd, struct kvec *vecs, NULL, &mtd->oobinfo); } -static int part_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) -{ - struct mtd_part *part = PART(mtd); - if (!(mtd->flags & MTD_WRITEABLE)) - return -EROFS; - if (oobsel == NULL) - oobsel = &mtd->oobinfo; - return part->master->writev_ecc (part->master, vecs, count, - to + part->offset, retlen, - eccbuf, oobsel); -} - static int part_readv_ecc (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel) @@ -446,8 +427,6 @@ int add_mtd_partitions(struct mtd_info *master, slave->mtd.writev = part_writev; if (master->readv) slave->mtd.readv = part_readv; - if (master->writev_ecc) - slave->mtd.writev_ecc = part_writev_ecc; if (master->readv_ecc) slave->mtd.readv_ecc = part_readv_ecc; if (master->lock) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 778535006c83..9aaeb3aa9d4d 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -151,11 +151,6 @@ static int nand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, struct nand_oobinfo *oobsel); static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf); -static int nand_writev(struct mtd_info *mtd, const struct kvec *vecs, - unsigned long count, loff_t to, size_t *retlen); -static int nand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, - unsigned long count, loff_t to, size_t *retlen, - uint8_t *eccbuf, struct nand_oobinfo *oobsel); static int nand_erase(struct mtd_info *mtd, struct erase_info *instr); static void nand_sync(struct mtd_info *mtd); @@ -1856,187 +1851,6 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, size_t *r return ret; } -/** - * nand_writev - [MTD Interface] compabilty function for nand_writev_ecc - * @mtd: MTD device structure - * @vecs: the iovectors to write - * @count: number of vectors - * @to: offset to write to - * @retlen: pointer to variable to store the number of written bytes - * - * NAND write with kvec. This just calls the ecc function - */ -static int nand_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, - loff_t to, size_t *retlen) -{ - return (nand_writev_ecc(mtd, vecs, count, to, retlen, NULL, NULL)); -} - -/** - * nand_writev_ecc - [MTD Interface] write with iovec with ecc - * @mtd: MTD device structure - * @vecs: the iovectors to write - * @count: number of vectors - * @to: offset to write to - * @retlen: pointer to variable to store the number of written bytes - * @eccbuf: filesystem supplied oob data buffer - * @oobsel: oob selection structure - * - * NAND write with iovec with ecc - */ -static int nand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, - loff_t to, size_t *retlen, uint8_t *eccbuf, struct nand_oobinfo *oobsel) -{ - int i, page, len, total_len, ret = -EIO, written = 0, chipnr; - int oob, numpages, autoplace = 0, startpage; - struct nand_chip *this = mtd->priv; - int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); - uint8_t *oobbuf, *bufstart; - - /* Preset written len for early exit */ - *retlen = 0; - - /* 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; - } - - /* Grab the lock and see if the device is available */ - nand_get_device(this, mtd, FL_WRITING); - - /* Get the current chip-nr */ - chipnr = (int)(to >> this->chip_shift); - /* Select the NAND device */ - this->select_chip(mtd, chipnr); - - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) - goto out; - - /* if oobsel is NULL, use chip defaults */ - if (oobsel == NULL) - oobsel = &mtd->oobinfo; - - /* Autoplace of oob data ? Use the default placement scheme */ - if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { - oobsel = this->autooob; - autoplace = 1; - } - if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) - autoplace = 1; - - /* Setup start page */ - page = (int)(to >> this->page_shift); - /* Invalidate the page cache, if we write to the cached page */ - if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift)) - this->pagebuf = -1; - - startpage = page & this->pagemask; - - /* Loop until all kvec' data has been written */ - len = 0; - while (count) { - /* If the given tuple is >= pagesize then - * write it out from the iov - */ - if ((vecs->iov_len - len) >= mtd->writesize) { - /* Calc number of pages we can write - * out of this iov in one go */ - numpages = (vecs->iov_len - len) >> this->page_shift; - /* Do not cross block boundaries */ - numpages = min(ppblock - (startpage & (ppblock - 1)), numpages); - oobbuf = nand_prepare_oobbuf(mtd, NULL, oobsel, autoplace, numpages); - bufstart = (uint8_t *) vecs->iov_base; - bufstart += len; - this->data_poi = bufstart; - oob = 0; - for (i = 1; i <= numpages; i++) { - /* Write one page. If this is the last page to write - * then use the real pageprogram command, else select - * cached programming if supported by the chip. - */ - ret = nand_write_page(mtd, this, page & this->pagemask, - &oobbuf[oob], oobsel, i != numpages); - if (ret) - goto out; - this->data_poi += mtd->writesize; - len += mtd->writesize; - oob += mtd->oobsize; - page++; - } - /* Check, if we have to switch to the next tuple */ - if (len >= (int)vecs->iov_len) { - vecs++; - len = 0; - count--; - } - } else { - /* We must use the internal buffer, read data out of each - * tuple until we have a full page to write - */ - int cnt = 0; - while (cnt < mtd->writesize) { - if (vecs->iov_base != NULL && vecs->iov_len) - this->data_buf[cnt++] = ((uint8_t *) 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->pagebuf = page; - this->data_poi = this->data_buf; - bufstart = this->data_poi; - numpages = 1; - oobbuf = nand_prepare_oobbuf(mtd, NULL, oobsel, autoplace, numpages); - ret = nand_write_page(mtd, this, page & this->pagemask, oobbuf, oobsel, 0); - if (ret) - goto out; - page++; - } - - this->data_poi = bufstart; - ret = nand_verify_pages(mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0); - if (ret) - goto out; - - written += mtd->writesize * numpages; - /* All done ? */ - if (!count) - break; - - startpage = page & this->pagemask; - /* Check, if we cross a chip boundary */ - if (!startpage) { - chipnr++; - this->select_chip(mtd, -1); - this->select_chip(mtd, chipnr); - } - } - ret = 0; - out: - /* Deselect and wake up anyone waiting on the device */ - nand_release_device(mtd); - - *retlen = written; - return ret; -} - /** * single_erease_cmd - [GENERIC] NAND standard block erase command function * @mtd: MTD device structure @@ -2718,8 +2532,6 @@ int nand_scan(struct mtd_info *mtd, int maxchips) mtd->read_oob = nand_read_oob; mtd->write_oob = nand_write_oob; mtd->readv = NULL; - mtd->writev = nand_writev; - mtd->writev_ecc = nand_writev_ecc; mtd->sync = nand_sync; mtd->lock = NULL; mtd->unlock = NULL; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 4c2c61d54b3a..8e875fa140a8 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1013,144 +1013,6 @@ out: return ret; } -/** - * onenand_writev_ecc - [MTD Interface] write with iovec with ecc - * @param mtd MTD device structure - * @param vecs the iovectors to write - * @param count number of vectors - * @param to offset to write to - * @param retlen pointer to variable to store the number of written bytes - * @param eccbuf filesystem supplied oob data buffer - * @param oobsel oob selection structure - * - * OneNAND write with iovec with ecc - */ -static int onenand_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) -{ - struct onenand_chip *this = mtd->priv; - unsigned char *pbuf; - size_t total_len, len; - int i, written = 0; - int ret = 0; - - /* Preset written len for early exit */ - *retlen = 0; - - /* Calculate total length of data */ - total_len = 0; - for (i = 0; i < count; i++) - total_len += vecs[i].iov_len; - - DEBUG(MTD_DEBUG_LEVEL3, "onenand_writev_ecc: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count); - - /* Do not allow write past end of the device */ - if (unlikely((to + total_len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: Attempted write past end of device\n"); - return -EINVAL; - } - - /* Reject writes, which are not page aligned */ - if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(total_len))) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: Attempt to write not page aligned data\n"); - return -EINVAL; - } - - /* Grab the lock and see if the device is available */ - onenand_get_device(mtd, FL_WRITING); - - /* TODO handling oob */ - - /* Loop until all keve's data has been written */ - len = 0; - while (count) { - pbuf = this->page_buf; - /* - * If the given tuple is >= pagesize then - * write it out from the iov - */ - if ((vecs->iov_len - len) >= mtd->writesize) { - pbuf = vecs->iov_base + len; - - len += mtd->writesize; - - /* Check, if we have to switch to the next tuple */ - if (len >= (int) vecs->iov_len) { - vecs++; - len = 0; - count--; - } - } else { - int cnt = 0, thislen; - while (cnt < mtd->writesize) { - thislen = min_t(int, mtd->writesize - cnt, vecs->iov_len - len); - memcpy(this->page_buf + cnt, vecs->iov_base + len, thislen); - cnt += thislen; - len += thislen; - - /* Check, if we have to switch to the next tuple */ - if (len >= (int) vecs->iov_len) { - vecs++; - len = 0; - count--; - } - } - } - - this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->writesize); - - this->write_bufferram(mtd, ONENAND_DATARAM, pbuf, 0, mtd->writesize); - this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); - - this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); - - onenand_update_bufferram(mtd, to, 1); - - ret = this->wait(mtd, FL_WRITING); - if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: write failed %d\n", ret); - goto out; - } - - - /* Only check verify write turn on */ - ret = onenand_verify_page(mtd, (u_char *) pbuf, to); - if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: verify failed %d\n", ret); - goto out; - } - - written += mtd->writesize; - - to += mtd->writesize; - } - -out: - /* Deselect and wakt up anyone waiting on the device */ - onenand_release_device(mtd); - - *retlen = written; - - return 0; -} - -/** - * onenand_writev - [MTD Interface] compabilty function for onenand_writev_ecc - * @param mtd MTD device structure - * @param vecs the iovectors to write - * @param count number of vectors - * @param to offset to write to - * @param retlen pointer to variable to store the number of written bytes - * - * OneNAND write with kvec. This just calls the ecc function - */ -static int onenand_writev(struct mtd_info *mtd, const struct kvec *vecs, - unsigned long count, loff_t to, size_t *retlen) -{ - return onenand_writev_ecc(mtd, vecs, count, to, retlen, NULL, NULL); -} - /** * onenand_block_checkbad - [GENERIC] Check if a block is marked bad * @param mtd MTD device structure @@ -1964,8 +1826,6 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) #endif mtd->readv = NULL; mtd->readv_ecc = NULL; - mtd->writev = onenand_writev; - mtd->writev_ecc = onenand_writev_ecc; mtd->sync = onenand_sync; mtd->lock = NULL; mtd->unlock = onenand_unlock; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index d48c7492392b..dba25da84aed 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -142,8 +142,6 @@ struct mtd_info { int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); - int (*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); /* Sync */ void (*sync) (struct mtd_info *mtd); -- cgit v1.2.3 From 2528e8cdf376d7da24647c442ec1e88c360d76ca Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 23 May 2006 16:10:00 +0200 Subject: [MTD] Remove readv/readv_ecc These functions were never implemented and added only bloat to partition and concat code. Signed-off-by: Thomas Gleixner --- drivers/mtd/mtdpart.c | 29 ----------------------------- drivers/mtd/nand/nand_base.c | 1 - drivers/mtd/onenand/onenand_base.c | 2 -- include/linux/mtd/mtd.h | 6 +----- 4 files changed, 1 insertion(+), 37 deletions(-) (limited to 'include') diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index ae675608fa91..f418920320d2 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -212,31 +212,6 @@ static int part_writev (struct mtd_info *mtd, const struct kvec *vecs, to + part->offset, retlen); } -static int part_readv (struct mtd_info *mtd, struct kvec *vecs, - unsigned long count, loff_t from, size_t *retlen) -{ - struct mtd_part *part = PART(mtd); - if (part->master->readv_ecc == NULL) - return part->master->readv (part->master, vecs, count, - from + part->offset, retlen); - else - return part->master->readv_ecc (part->master, vecs, count, - from + part->offset, retlen, - NULL, &mtd->oobinfo); -} - -static int part_readv_ecc (struct mtd_info *mtd, struct kvec *vecs, - unsigned long count, loff_t from, size_t *retlen, - u_char *eccbuf, struct nand_oobinfo *oobsel) -{ - struct mtd_part *part = PART(mtd); - if (oobsel == NULL) - oobsel = &mtd->oobinfo; - return part->master->readv_ecc (part->master, vecs, count, - from + part->offset, retlen, - eccbuf, oobsel); -} - static int part_erase (struct mtd_info *mtd, struct erase_info *instr) { struct mtd_part *part = PART(mtd); @@ -425,10 +400,6 @@ int add_mtd_partitions(struct mtd_info *master, } if (master->writev) slave->mtd.writev = part_writev; - if (master->readv) - slave->mtd.readv = part_readv; - if (master->readv_ecc) - slave->mtd.readv_ecc = part_readv_ecc; if (master->lock) slave->mtd.lock = part_lock; if (master->unlock) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 9aaeb3aa9d4d..da2f4d16e506 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2531,7 +2531,6 @@ int nand_scan(struct mtd_info *mtd, int maxchips) mtd->write_ecc = nand_write_ecc; mtd->read_oob = nand_read_oob; mtd->write_oob = nand_write_oob; - mtd->readv = NULL; mtd->sync = nand_sync; mtd->lock = NULL; mtd->unlock = NULL; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 8e875fa140a8..3a3fe1d8fcdd 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1824,8 +1824,6 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) mtd->write_user_prot_reg = onenand_write_user_prot_reg; mtd->lock_user_prot_reg = onenand_lock_user_prot_reg; #endif - mtd->readv = NULL; - mtd->readv_ecc = NULL; mtd->sync = onenand_sync; mtd->lock = NULL; mtd->unlock = onenand_unlock; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index dba25da84aed..af89e529b8d2 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -133,14 +133,10 @@ struct mtd_info { int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len); - /* kvec-based read/write methods. We need these especially for NAND flash, - with its limited number of write cycles per erase. + /* kvec-based read/write methods. NB: The 'count' parameter is the number of _vectors_, each of which contains an (ofs, len) tuple. */ - int (*readv) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen); - int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, - size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); /* Sync */ -- cgit v1.2.3 From 9223a456da8ed357bf7e0b128c853e2c8bd54614 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 23 May 2006 17:21:03 +0200 Subject: [MTD] Remove read/write _ecc variants MTD clients are agnostic of FLASH which needs ECC suppport. Remove the functions and fixup the callers. Signed-off-by: Thomas Gleixner --- drivers/mtd/devices/doc2000.c | 2 - drivers/mtd/devices/doc2001.c | 2 - drivers/mtd/devices/doc2001plus.c | 2 - drivers/mtd/inftlcore.c | 63 +++++++++---------- drivers/mtd/inftlmount.c | 12 ++-- drivers/mtd/mtdconcat.c | 116 ----------------------------------- drivers/mtd/mtdpart.c | 54 ++-------------- drivers/mtd/nand/nand_base.c | 122 ++++++++++++++++++------------------- drivers/mtd/nand/nand_bbt.c | 104 ++++++++++++++++++------------- drivers/mtd/nftlcore.c | 15 ++--- drivers/mtd/nftlmount.c | 12 ++-- drivers/mtd/onenand/onenand_base.c | 75 ++++++----------------- fs/jffs2/wbuf.c | 28 +++------ include/linux/mtd/mtd.h | 3 - include/linux/mtd/nand.h | 3 + 15 files changed, 209 insertions(+), 404 deletions(-) (limited to 'include') diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index 6f32942fdf77..d9ba1ee658f6 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -584,8 +584,6 @@ void DoC2k_init(struct mtd_info *mtd) mtd->unpoint = NULL; mtd->read = doc_read; mtd->write = doc_write; - mtd->read_ecc = doc_read_ecc; - mtd->write_ecc = doc_write_ecc; mtd->read_oob = doc_read_oob; mtd->write_oob = doc_write_oob; mtd->sync = NULL; diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index e6eaef28a2b0..579c0b570ae5 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -369,8 +369,6 @@ void DoCMil_init(struct mtd_info *mtd) mtd->unpoint = NULL; mtd->read = doc_read; mtd->write = doc_write; - mtd->read_ecc = doc_read_ecc; - mtd->write_ecc = doc_write_ecc; mtd->read_oob = doc_read_oob; mtd->write_oob = doc_write_oob; mtd->sync = NULL; diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index 8422c5e92d27..1ee0c0dcb53b 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -491,8 +491,6 @@ void DoCMilPlus_init(struct mtd_info *mtd) mtd->unpoint = NULL; mtd->read = doc_read; mtd->write = doc_write; - mtd->read_ecc = doc_read_ecc; - mtd->write_ecc = doc_write_ecc; mtd->read_oob = doc_read_oob; mtd->write_oob = doc_write_oob; mtd->sync = NULL; diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index a3b92479719d..ddd12993780d 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -79,14 +80,12 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) inftl->mbd.devnum = -1; inftl->mbd.blksize = 512; inftl->mbd.tr = tr; - memcpy(&inftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo)); - inftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY; - if (INFTL_mount(inftl) < 0) { + if (INFTL_mount(inftl) < 0) { printk(KERN_WARNING "INFTL: could not mount device\n"); kfree(inftl); return; - } + } /* OK, it's a new one. Set up all the data structures. */ @@ -221,7 +220,7 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned * Scan to find the Erase Unit which holds the actual data for each * 512-byte block within the Chain. */ - silly = MAX_LOOPS; + silly = MAX_LOOPS; while (thisEUN < inftl->nb_blocks) { for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) { if ((BlockMap[block] != 0xffff) || BlockDeleted[block]) @@ -232,7 +231,7 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned (char *)&oob) < 0) status = SECTOR_IGNORE; else - status = oob.b.Status | oob.b.Status1; + status = oob.b.Status | oob.b.Status1; switch(status) { case SECTOR_FREE: @@ -282,29 +281,30 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned continue; } - /* + /* * Copy only in non free block (free blocks can only * happen in case of media errors or deleted blocks). */ - if (BlockMap[block] == BLOCK_NIL) - continue; + if (BlockMap[block] == BLOCK_NIL) + continue; - ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize * + ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, &retlen, movebuf); - if (ret < 0) { + if (ret < 0) { ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, &retlen, movebuf); if (ret != -EIO) - DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went " - "away on retry?\n"); - } - memset(&oob, 0xff, sizeof(struct inftl_oob)); - oob.b.Status = oob.b.Status1 = SECTOR_USED; - MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + - (block * SECTORSIZE), SECTORSIZE, &retlen, - movebuf, (char *)&oob, &inftl->oobinfo); + DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went " + "away on retry?\n"); + } + memset(&oob, 0xff, sizeof(struct inftl_oob)); + oob.b.Status = oob.b.Status1 = SECTOR_USED; + + nand_write_raw(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + + (block * SECTORSIZE), SECTORSIZE, &retlen, + movebuf, (char *)&oob); } /* @@ -329,17 +329,17 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned if (thisEUN == targetEUN) break; - if (INFTL_formatblock(inftl, thisEUN) < 0) { + if (INFTL_formatblock(inftl, thisEUN) < 0) { /* * Could not erase : mark block as reserved. */ inftl->PUtable[thisEUN] = BLOCK_RESERVED; - } else { + } else { /* Correctly erased : mark it as free */ inftl->PUtable[thisEUN] = BLOCK_FREE; inftl->PUtable[prevEUN] = BLOCK_NIL; inftl->numfreeEUNs++; - } + } } return targetEUN; @@ -437,7 +437,7 @@ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block) MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + blockofs, 8, &retlen, (char *)&bci); - status = bci.Status | bci.Status1; + status = bci.Status | bci.Status1; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in " "EUN %d is %x\n", block , writeEUN, status); @@ -670,12 +670,12 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n", thisEUN, thisVUC); - if (INFTL_formatblock(inftl, thisEUN) < 0) { + if (INFTL_formatblock(inftl, thisEUN) < 0) { /* * Could not erase : mark block as reserved. */ inftl->PUtable[thisEUN] = BLOCK_RESERVED; - } else { + } else { /* Correctly erased : mark it as free */ inftl->PUtable[thisEUN] = BLOCK_FREE; inftl->numfreeEUNs++; @@ -784,9 +784,10 @@ static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, memset(&oob, 0xff, sizeof(struct inftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; - MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + - blockofs, SECTORSIZE, &retlen, (char *)buffer, - (char *)&oob, &inftl->oobinfo); + + nand_write_raw(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + + blockofs, SECTORSIZE, &retlen, (char *)buffer, + (char *)&oob); /* * need to write SECTOR_USED flags since they are not written * in mtd_writeecc @@ -804,9 +805,9 @@ static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, struct INFTLrecord *inftl = (void *)mbd; unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)]; unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); - unsigned int status; + unsigned int status; int silly = MAX_LOOPS; - struct inftl_bci bci; + struct inftl_bci bci; size_t retlen; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=%p,block=%ld," @@ -850,7 +851,7 @@ foundit: /* The requested block is not on the media, return all 0x00 */ memset(buffer, 0, SECTORSIZE); } else { - size_t retlen; + size_t retlen; loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; if (MTD_READ(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen, buffer)) diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 43fdc9433882..f89a03795e76 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -350,21 +350,21 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, int len, int check_oob) { u8 buf[SECTORSIZE + inftl->mbd.mtd->oobsize]; + struct mtd_info *mtd = inftl->mbd.mtd; size_t retlen; int i; - DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=%p," - "address=0x%x,len=%d,check_oob=%d)\n", inftl, - address, len, check_oob); - for (i = 0; i < len; i += SECTORSIZE) { - if (MTD_READECC(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &inftl->oobinfo) < 0) + if (mtd->read(mtd, address, SECTORSIZE, &retlen, buf)) return -1; if (memcmpb(buf, 0xff, SECTORSIZE) != 0) return -1; if (check_oob) { - if (memcmpb(buf + SECTORSIZE, 0xff, inftl->mbd.mtd->oobsize) != 0) + if(mtd->read_oob(mtd, address, mtd->oobsize, + &retlen, &buf[SECTORSIZE]) < 0) + return -1; + if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0) return -1; } address += SECTORSIZE; diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index a6fcee2713b0..6d52137988fa 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -142,116 +142,6 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len, return err; } -static int -concat_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) -{ - struct mtd_concat *concat = CONCAT(mtd); - int err = -EINVAL; - int i; - - *retlen = 0; - - for (i = 0; i < concat->num_subdev; i++) { - struct mtd_info *subdev = concat->subdev[i]; - size_t size, retsize; - - if (from >= subdev->size) { - /* Not destined for this subdev */ - size = 0; - from -= subdev->size; - continue; - } - - if (from + len > subdev->size) - /* First part goes into this subdev */ - size = subdev->size - from; - else - /* Entire transaction goes into this subdev */ - size = len; - - if (subdev->read_ecc) - err = subdev->read_ecc(subdev, from, size, - &retsize, buf, eccbuf, oobsel); - else - err = -EINVAL; - - if (err) - break; - - *retlen += retsize; - len -= size; - if (len == 0) - break; - - err = -EINVAL; - buf += size; - if (eccbuf) { - eccbuf += subdev->oobsize; - /* in nand.c at least, eccbufs are - tagged with 2 (int)eccstatus'; we - must account for these */ - eccbuf += 2 * (sizeof (int)); - } - from = 0; - } - return err; -} - -static int -concat_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) -{ - struct mtd_concat *concat = CONCAT(mtd); - int err = -EINVAL; - int i; - - if (!(mtd->flags & MTD_WRITEABLE)) - return -EROFS; - - *retlen = 0; - - for (i = 0; i < concat->num_subdev; i++) { - struct mtd_info *subdev = concat->subdev[i]; - size_t size, retsize; - - if (to >= subdev->size) { - size = 0; - to -= subdev->size; - continue; - } - if (to + len > subdev->size) - size = subdev->size - to; - else - size = len; - - if (!(subdev->flags & MTD_WRITEABLE)) - err = -EROFS; - else if (subdev->write_ecc) - err = subdev->write_ecc(subdev, to, size, - &retsize, buf, eccbuf, oobsel); - else - err = -EINVAL; - - if (err) - break; - - *retlen += retsize; - len -= size; - if (len == 0) - break; - - err = -EINVAL; - buf += size; - if (eccbuf) - eccbuf += subdev->oobsize; - to = 0; - } - return err; -} - static int concat_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t * retlen) @@ -823,10 +713,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c concat->mtd.oobsize = subdev[0]->oobsize; concat->mtd.ecctype = subdev[0]->ecctype; concat->mtd.eccsize = subdev[0]->eccsize; - if (subdev[0]->read_ecc) - concat->mtd.read_ecc = concat_read_ecc; - if (subdev[0]->write_ecc) - concat->mtd.write_ecc = concat_write_ecc; if (subdev[0]->writev) concat->mtd.writev = concat_writev; if (subdev[0]->read_oob) @@ -869,8 +755,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c concat->mtd.oobsize != subdev[i]->oobsize || concat->mtd.ecctype != subdev[i]->ecctype || concat->mtd.eccsize != subdev[i]->eccsize || - !concat->mtd.read_ecc != !subdev[i]->read_ecc || - !concat->mtd.write_ecc != !subdev[i]->write_ecc || !concat->mtd.read_oob != !subdev[i]->read_oob || !concat->mtd.write_oob != !subdev[i]->write_oob) { kfree(concat); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index f418920320d2..a93550ce7978 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -55,12 +55,8 @@ static int part_read (struct mtd_info *mtd, loff_t from, size_t len, len = 0; else if (from + len > mtd->size) len = mtd->size - from; - if (part->master->read_ecc == NULL) - return part->master->read (part->master, from + part->offset, - len, retlen, buf); - else - return part->master->read_ecc (part->master, from + part->offset, - len, retlen, buf, NULL, &mtd->oobinfo); + return part->master->read (part->master, from + part->offset, + len, retlen, buf); } static int part_point (struct mtd_info *mtd, loff_t from, size_t len, @@ -74,6 +70,7 @@ static int part_point (struct mtd_info *mtd, loff_t from, size_t len, return part->master->point (part->master, from + part->offset, len, retlen, buf); } + static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) { struct mtd_part *part = PART(mtd); @@ -81,21 +78,6 @@ static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_ part->master->unpoint (part->master, addr, from + part->offset, len); } - -static int part_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) -{ - struct mtd_part *part = PART(mtd); - if (oobsel == NULL) - oobsel = &mtd->oobinfo; - if (from >= mtd->size) - len = 0; - else if (from + len > mtd->size) - len = mtd->size - from; - return part->master->read_ecc (part->master, from + part->offset, - len, retlen, buf, eccbuf, oobsel); -} - static int part_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { @@ -148,30 +130,8 @@ static int part_write (struct mtd_info *mtd, loff_t to, size_t len, len = 0; else if (to + len > mtd->size) len = mtd->size - to; - if (part->master->write_ecc == NULL) - return part->master->write (part->master, to + part->offset, - len, retlen, buf); - else - return part->master->write_ecc (part->master, to + part->offset, - len, retlen, buf, NULL, &mtd->oobinfo); - -} - -static int part_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) -{ - struct mtd_part *part = PART(mtd); - if (!(mtd->flags & MTD_WRITEABLE)) - return -EROFS; - if (oobsel == NULL) - oobsel = &mtd->oobinfo; - if (to >= mtd->size) - len = 0; - else if (to + len > mtd->size) - len = mtd->size - to; - return part->master->write_ecc (part->master, to + part->offset, - len, retlen, buf, eccbuf, oobsel); + return part->master->write (part->master, to + part->offset, + len, retlen, buf); } static int part_write_oob (struct mtd_info *mtd, loff_t to, size_t len, @@ -372,10 +332,6 @@ int add_mtd_partitions(struct mtd_info *master, slave->mtd.unpoint = part_unpoint; } - if (master->read_ecc) - slave->mtd.read_ecc = part_read_ecc; - if (master->write_ecc) - slave->mtd.write_ecc = part_write_ecc; if (master->read_oob) slave->mtd.read_oob = part_read_oob; if (master->write_oob) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index da2f4d16e506..d796eb508b4f 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -139,16 +139,10 @@ static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len); static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf); -static int nand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, uint8_t *buf, uint8_t *eccbuf, - struct nand_oobinfo *oobsel); static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf); static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf); -static int nand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf, uint8_t *eccbuf, - struct nand_oobinfo *oobsel); static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf); static int nand_erase(struct mtd_info *mtd, struct erase_info *instr); @@ -1079,27 +1073,6 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retl return nand_do_read_ecc(mtd, from, len, retlen, buf, NULL, &mtd->oobinfo, 0xff); } -/** - * nand_read_ecc - [MTD Interface] MTD compability function for nand_do_read_ecc - * @mtd: MTD device structure - * @from: offset to read from - * @len: number of bytes to read - * @retlen: pointer to variable to store the number of read bytes - * @buf: the databuffer to put data - * @oob_buf: filesystem supplied oob data buffer - * @oobsel: oob selection structure - * - * This function simply calls nand_do_read_ecc with flags = 0xff - */ -static int nand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, uint8_t *buf, uint8_t *oob_buf, struct nand_oobinfo *oobsel) -{ - /* use userspace supplied oobinfo, if zero */ - if (oobsel == NULL) - oobsel = &mtd->oobinfo; - return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff); -} - /** * nand_do_read_ecc - [MTD Interface] Read data with ECC * @mtd: MTD device structure @@ -1523,6 +1496,55 @@ int nand_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, s return 0; } +/** + * nand_write_raw - [GENERIC] Write raw data including oob + * @mtd: MTD device structure + * @buf: source buffer + * @to: offset to write to + * @len: number of bytes to write + * @buf: source buffer + * @oob: oob buffer + * + * Write raw data including oob + */ +int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, + uint8_t *buf, uint8_t *oob) +{ + struct nand_chip *this = mtd->priv; + int page = (int)(to >> this->page_shift); + int chip = (int)(to >> this->chip_shift); + int ret; + + *retlen = 0; + + /* Do not allow writes past end of device */ + if ((to + len) > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt write " + "beyond end of device\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device(this, mtd, FL_WRITING); + + this->select_chip(mtd, chip); + this->data_poi = buf; + + while (len != *retlen) { + ret = nand_write_page(mtd, this, page, oob, &mtd->oobinfo, 0); + if (ret) + return ret; + page++; + *retlen += mtd->writesize; + this->data_poi += mtd->writesize; + oob += mtd->oobsize; + } + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + return 0; +} + /** * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer * @mtd: MTD device structure @@ -1585,57 +1607,39 @@ static uint8_t *nand_prepare_oobbuf(struct mtd_info *mtd, uint8_t *fsbuf, struct #define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0 /** - * nand_write - [MTD Interface] compability function for nand_write_ecc - * @mtd: MTD device structure - * @to: offset to write to - * @len: number of bytes to write - * @retlen: pointer to variable to store the number of written bytes - * @buf: the data to write - * - * This function simply calls nand_write_ecc with oob buffer and oobsel = NULL - * -*/ -static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf) -{ - return (nand_write_ecc(mtd, to, len, retlen, buf, NULL, NULL)); -} - -/** - * nand_write_ecc - [MTD Interface] NAND write with ECC + * nand_write - [MTD Interface] NAND write with ECC * @mtd: MTD device structure * @to: offset to write to * @len: number of bytes to write * @retlen: pointer to variable to store the number of written bytes * @buf: the data to write - * @eccbuf: filesystem supplied oob data buffer - * @oobsel: oob selection structure * * NAND write with ECC */ -static int nand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf, uint8_t *eccbuf, - struct nand_oobinfo *oobsel) +static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) { int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr; int autoplace = 0, numpages, totalpages; struct nand_chip *this = mtd->priv; - uint8_t *oobbuf, *bufstart; + uint8_t *oobbuf, *bufstart, *eccbuf = NULL; int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); + struct nand_oobinfo *oobsel = &mtd->oobinfo; - DEBUG(MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int)to, (int)len); + DEBUG(MTD_DEBUG_LEVEL3, "nand_write: to = 0x%08x, len = %i\n", (unsigned int)to, (int)len); /* Initialize retlen, in case of early exit */ *retlen = 0; /* 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"); + DEBUG(MTD_DEBUG_LEVEL0, "nand_write: 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"); + printk(KERN_NOTICE "nand_write: Attempt to write not page aligned data\n"); return -EINVAL; } @@ -1651,10 +1655,6 @@ static int nand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, if (nand_check_wp(mtd)) goto out; - /* if oobsel is NULL, use chip defaults */ - if (oobsel == NULL) - oobsel = &mtd->oobinfo; - /* Autoplace of oob data ? Use the default placement scheme */ if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { oobsel = this->autooob; @@ -1689,7 +1689,7 @@ static int nand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, */ ret = nand_write_page(mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0)); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", ret); + DEBUG(MTD_DEBUG_LEVEL0, "nand_write: write_page failed %d\n", ret); goto out; } /* Next oob page */ @@ -1712,7 +1712,7 @@ static int nand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ret = nand_verify_pages(mtd, this, startpage, page - startpage, oobbuf, oobsel, chipnr, (eccbuf != NULL)); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret); + DEBUG(MTD_DEBUG_LEVEL0, "nand_write: verify_pages failed %d\n", ret); goto out; } *retlen = written; @@ -1741,7 +1741,7 @@ static int nand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, if (!ret) *retlen = written; else - DEBUG(MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret); + DEBUG(MTD_DEBUG_LEVEL0, "nand_write: verify_pages failed %d\n", ret); out: /* Deselect and wake up anyone waiting on the device */ @@ -2527,8 +2527,6 @@ int nand_scan(struct mtd_info *mtd, int maxchips) mtd->unpoint = NULL; mtd->read = nand_read; mtd->write = nand_write; - mtd->read_ecc = nand_read_ecc; - mtd->write_ecc = nand_write_ecc; mtd->read_oob = nand_read_oob; mtd->write_oob = nand_write_oob; mtd->sync = nand_sync; diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index fbccb2a25186..ecaaca18d1e0 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -156,7 +156,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, while (totlen) { len = min(totlen, (size_t) (1 << this->bbt_erase_shift)); - res = mtd->read_ecc(mtd, from, len, &retlen, buf, NULL, this->autooob); + res = mtd->read(mtd, from, len, &retlen, buf); if (res < 0) { if (retlen != len) { printk(KERN_INFO "nand_bbt: Error reading bad block table\n"); @@ -471,17 +471,17 @@ static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt * */ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, - struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel) + struct nand_bbt_descr *td, struct nand_bbt_descr *md, + int chipsel) { struct nand_chip *this = mtd->priv; - struct nand_oobinfo oobinfo; struct erase_info einfo; int i, j, res, chip = 0; int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; - int nrchips, bbtoffs, pageoffs; + int nrchips, bbtoffs, pageoffs, ooboffs; uint8_t msk[4]; uint8_t rcode = td->reserved_block_code; - size_t retlen, len = 0; + size_t retlen, len = 0, ooblen; loff_t to; if (!rcode) @@ -526,12 +526,14 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, for (i = 0; i < td->maxblocks; i++) { int block = startblock + dir * i; /* Check, if the block is bad */ - switch ((this->bbt[block >> 2] >> (2 * (block & 0x03))) & 0x03) { + switch ((this->bbt[block >> 2] >> + (2 * (block & 0x03))) & 0x03) { case 0x01: case 0x03: continue; } - page = block << (this->bbt_erase_shift - this->page_shift); + page = block << + (this->bbt_erase_shift - this->page_shift); /* Check, if the block is used by the mirror table */ if (!md || md->pages[chip] != page) goto write; @@ -542,11 +544,20 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, /* Set up shift count and masks for the flash table */ bits = td->options & NAND_BBT_NRBITS_MSK; + msk[2] = ~rcode; switch (bits) { - case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break; - case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break; - case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break; - case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break; + case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; + msk[3] = 0x01; + break; + case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; + msk[3] = 0x03; + break; + case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; + msk[3] = 0x0f; + break; + case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; + msk[3] = 0xff; + break; default: return -EINVAL; } @@ -554,49 +565,55 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, to = ((loff_t) page) << this->page_shift; - memcpy(&oobinfo, this->autooob, sizeof(oobinfo)); - oobinfo.useecc = MTD_NANDECC_PLACEONLY; - /* Must we save the block contents ? */ if (td->options & NAND_BBT_SAVECONTENT) { /* Make it block aligned */ to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1)); len = 1 << this->bbt_erase_shift; - res = mtd->read_ecc(mtd, to, len, &retlen, buf, &buf[len], &oobinfo); + res = mtd->read(mtd, to, len, &retlen, buf); if (res < 0) { if (retlen != len) { - printk(KERN_INFO - "nand_bbt: Error reading block for writing the bad block table\n"); + printk(KERN_INFO "nand_bbt: Error " + "reading block for writing " + "the bad block table\n"); return res; } - printk(KERN_WARNING "nand_bbt: ECC error while reading block for writing bad block table\n"); + printk(KERN_WARNING "nand_bbt: ECC error " + "while reading block for writing " + "bad block table\n"); } + /* Read oob data */ + ooblen = (len >> this->page_shift) * mtd->oobsize; + res = mtd->read_oob(mtd, to + mtd->writesize, ooblen, + &retlen, &buf[len]); + if (res < 0 || retlen != ooblen) + goto outerr; + /* Calc the byte offset in the buffer */ pageoffs = page - (int)(to >> this->page_shift); offs = pageoffs << this->page_shift; /* Preset the bbt area with 0xff */ memset(&buf[offs], 0xff, (size_t) (numblocks >> sft)); - /* Preset the bbt's oob area with 0xff */ - memset(&buf[len + pageoffs * mtd->oobsize], 0xff, - ((len >> this->page_shift) - pageoffs) * mtd->oobsize); - if (td->options & NAND_BBT_VERSION) { - buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip]; - } + ooboffs = len + (pageoffs * mtd->oobsize); + } else { /* Calc length */ len = (size_t) (numblocks >> sft); /* Make it page aligned ! */ - len = (len + (mtd->writesize - 1)) & ~(mtd->writesize - 1); + len = (len + (mtd->writesize - 1)) & + ~(mtd->writesize - 1); /* Preset the buffer with 0xff */ - memset(buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize); + memset(buf, 0xff, len + + (len >> this->page_shift)* mtd->oobsize); offs = 0; + ooboffs = len; /* Pattern is located in oob area of first page */ - memcpy(&buf[len + td->offs], td->pattern, td->len); - if (td->options & NAND_BBT_VERSION) { - buf[len + td->veroffs] = td->version[chip]; - } + memcpy(&buf[ooboffs + td->offs], td->pattern, td->len); } + if (td->options & NAND_BBT_VERSION) + buf[ooboffs + td->veroffs] = td->version[chip]; + /* walk through the memory table */ for (i = 0; i < numblocks;) { uint8_t dat; @@ -604,7 +621,8 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, for (j = 0; j < 4; j++, i++) { int sftcnt = (i << (3 - sft)) & sftmsk; /* Do not store the reserved bbt blocks ! */ - buf[offs + (i >> sft)] &= ~(msk[dat & 0x03] << sftcnt); + buf[offs + (i >> sft)] &= + ~(msk[dat & 0x03] << sftcnt); dat >>= 2; } } @@ -614,23 +632,25 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, einfo.addr = (unsigned long)to; einfo.len = 1 << this->bbt_erase_shift; res = nand_erase_nand(mtd, &einfo, 1); - if (res < 0) { - printk(KERN_WARNING "nand_bbt: Error during block erase: %d\n", res); - return res; - } + if (res < 0) + goto outerr; - res = mtd->write_ecc(mtd, to, len, &retlen, buf, &buf[len], &oobinfo); - if (res < 0) { - printk(KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res); - return res; - } - printk(KERN_DEBUG "Bad block table written to 0x%08x, version 0x%02X\n", - (unsigned int)to, td->version[chip]); + res = nand_write_raw(mtd, to, len, &retlen, buf, &buf[len]); + if (res < 0) + goto outerr; + + printk(KERN_DEBUG "Bad block table written to 0x%08x, version " + "0x%02X\n", (unsigned int)to, td->version[chip]); /* Mark it as used */ td->pages[chip] = page; } return 0; + + outerr: + printk(KERN_WARNING + "nand_bbt: Error while writing bad block table %d\n", res); + return res; } /** diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index d7cd5fa16ba4..dd03349946c2 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -70,8 +70,6 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) nftl->mbd.devnum = -1; nftl->mbd.blksize = 512; nftl->mbd.tr = tr; - memcpy(&nftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo)); - nftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY; if (NFTL_mount(nftl) < 0) { printk(KERN_WARNING "NFTL: could not mount device\n"); @@ -369,8 +367,11 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p } memset(&oob, 0xff, sizeof(struct nftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; - MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512), - 512, &retlen, movebuf, (char *)&oob, &nftl->oobinfo); + + nand_write_raw(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + + (block * 512), 512, &retlen, movebuf, + (char *)&oob); + } /* add the header so that it is now a valid chain */ @@ -639,10 +640,10 @@ static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, memset(&oob, 0xff, sizeof(struct nftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; - MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, - 512, &retlen, (char *)buffer, (char *)&oob, &nftl->oobinfo); - /* need to write SECTOR_USED flags since they are not written in mtd_writeecc */ + nand_write_raw(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + + blockofs, 512, &retlen, (char *)buffer, + (char *)&oob); return 0; } #endif /* CONFIG_NFTL_RW */ diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 3b104ebb219a..90e5e7e97fdc 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -268,18 +268,22 @@ static int memcmpb(void *a, int c, int n) static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len, int check_oob) { - int i; - size_t retlen; u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize]; + struct mtd_info *mtd = nftl->mbd.mtd; + size_t retlen; + int i; for (i = 0; i < len; i += SECTORSIZE) { - if (MTD_READECC(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &nftl->oobinfo) < 0) + if (mtd->read(mtd, address, SECTORSIZE, &retlen, buf)) return -1; if (memcmpb(buf, 0xff, SECTORSIZE) != 0) return -1; if (check_oob) { - if (memcmpb(buf + SECTORSIZE, 0xff, nftl->mbd.mtd->oobsize) != 0) + if(mtd->read_oob(mtd, address, mtd->oobsize, + &retlen, &buf[SECTORSIZE]) < 0) + return -1; + if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0) return -1; } address += SECTORSIZE; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 3a3fe1d8fcdd..7a2419186ff4 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -597,31 +597,28 @@ static void onenand_release_device(struct mtd_info *mtd) } /** - * onenand_read_ecc - [MTD Interface] Read data with ECC + * onenand_read - [MTD Interface] Read data from flash * @param mtd MTD device structure * @param from offset to read from * @param len number of bytes to read * @param retlen pointer to variable to store the number of read bytes * @param buf the databuffer to put data - * @param oob_buf filesystem supplied oob data buffer - * @param oobsel oob selection structure * - * OneNAND read with ECC - */ -static int onenand_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) + * Read with ecc +*/ +static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) { struct onenand_chip *this = mtd->priv; int read = 0, column; int thislen; int ret = 0; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: 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, "onenand_read_ecc: Attempt read beyond end of device\n"); + DEBUG(MTD_DEBUG_LEVEL0, "onenand_read: Attempt read beyond end of device\n"); *retlen = 0; return -EINVAL; } @@ -654,7 +651,7 @@ static int onenand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, break; if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_ecc: read failed = %d\n", ret); + DEBUG(MTD_DEBUG_LEVEL0, "onenand_read: read failed = %d\n", ret); goto out; } @@ -675,22 +672,6 @@ out: return ret; } -/** - * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc - * @param mtd MTD device structure - * @param from offset to read from - * @param len number of bytes to read - * @param retlen pointer to variable to store the number of read bytes - * @param buf the databuffer to put data - * - * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL -*/ -static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - return onenand_read_ecc(mtd, from, len, retlen, buf, NULL, NULL); -} - /** * onenand_read_oob - [MTD Interface] OneNAND read out-of-band * @param mtd MTD device structure @@ -834,39 +815,36 @@ static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr) #define NOTALIGNED(x) ((x & (mtd->writesize - 1)) != 0) /** - * onenand_write_ecc - [MTD Interface] OneNAND write with ECC + * onenand_write - [MTD Interface] write buffer to FLASH * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write * @param retlen pointer to variable to store the number of written bytes * @param buf the data to write - * @param eccbuf filesystem supplied oob data buffer - * @param oobsel oob selection structure * - * OneNAND write with ECC + * Write with ECC */ -static int onenand_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 onenand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) { struct onenand_chip *this = mtd->priv; int written = 0; int ret = 0; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + DEBUG(MTD_DEBUG_LEVEL3, "onenand_write: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ *retlen = 0; /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: Attempt write to past end of device\n"); + DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt write to past end of device\n"); return -EINVAL; } /* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: Attempt to write not page aligned data\n"); + DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt to write not page aligned data\n"); return -EINVAL; } @@ -888,7 +866,7 @@ static int onenand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ret = this->wait(mtd, FL_WRITING); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: write filaed %d\n", ret); + DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret); goto out; } @@ -897,7 +875,7 @@ static int onenand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, /* Only check verify write turn on */ ret = onenand_verify_page(mtd, (u_char *) buf, to); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: verify failed %d\n", ret); + DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret); goto out; } @@ -917,23 +895,6 @@ out: return ret; } -/** - * onenand_write - [MTD Interface] compability function for onenand_write_ecc - * @param mtd MTD device structure - * @param to offset to write to - * @param len number of bytes to write - * @param retlen pointer to variable to store the number of written bytes - * @param buf the data to write - * - * This function simply calls onenand_write_ecc - * with oob buffer and oobsel = NULL - */ -static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - return onenand_write_ecc(mtd, to, len, retlen, buf, NULL, NULL); -} - /** * onenand_write_oob - [MTD Interface] OneNAND write out-of-band * @param mtd MTD device structure @@ -1812,8 +1773,6 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) mtd->unpoint = NULL; mtd->read = onenand_read; mtd->write = onenand_write; - mtd->read_ecc = onenand_read_ecc; - mtd->write_ecc = onenand_write_ecc; mtd->read_oob = onenand_read_oob; mtd->write_oob = onenand_write_oob; #ifdef CONFIG_MTD_ONENAND_OTP diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 916c87d3393b..76d4c361ef1f 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -233,10 +233,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) } /* Do the read... */ - if (jffs2_cleanmarker_oob(c)) - ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo); - else - ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf); + ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf); if (ret == -EBADMSG && retlen == c->wbuf_ofs - start) { /* ECC recovered */ @@ -290,16 +287,13 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) if (breakme++ == 20) { printk(KERN_NOTICE "Faking write error at 0x%08x\n", ofs); breakme = 0; - c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, - brokenbuf, NULL, c->oobinfo); + c->mtd->write(c->mtd, ofs, towrite, &retlen, + brokenbuf); ret = -EIO; } else #endif - if (jffs2_cleanmarker_oob(c)) - ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, - rewrite_buf, NULL, c->oobinfo); - else - ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf); + ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, + rewrite_buf); if (ret || retlen != towrite) { /* Argh. We tried. Really we did. */ @@ -457,15 +451,12 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) if (breakme++ == 20) { printk(KERN_NOTICE "Faking write error at 0x%08x\n", c->wbuf_ofs); breakme = 0; - c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, - &retlen, brokenbuf, NULL, c->oobinfo); + c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, + brokenbuf); ret = -EIO; } else #endif - if (jffs2_cleanmarker_oob(c)) - ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo); - else ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf); if (ret || retlen != c->wbuf_pagesize) { @@ -800,10 +791,7 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re /* Read flash */ down_read(&c->wbuf_sem); - if (jffs2_cleanmarker_oob(c)) - ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo); - else - ret = c->mtd->read(c->mtd, ofs, len, retlen, buf); + ret = c->mtd->read(c->mtd, ofs, len, retlen, buf); if ( (ret == -EBADMSG) && (*retlen == len) ) { printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n", diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index af89e529b8d2..b8ad634391db 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -115,9 +115,6 @@ struct mtd_info { int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); - int (*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); - int (*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 (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 6931376ed68d..8362b466df3a 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -36,6 +36,9 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen); +extern int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, uint8_t *buf, uint8_t *oob); + /* The maximum number of NAND chips in an array */ #define NAND_MAX_CHIPS 8 -- cgit v1.2.3 From 7abd3ef9875eb2afcdcd4f450680298a2983a55e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 23 May 2006 23:25:53 +0200 Subject: [MTD] Refactor NAND hwcontrol to cmd_ctrl The hwcontrol function enforced a step by step state machine for any kind of hardware chip access. Let the hardware driver know which control bits are set and inform it about a change of the control lines. Let the hardware driver write out the command and address bytes directly. This gives a peformance advantage for address bus controlled chips and simplifies the quirks in the hardware drivers. Signed-off-by: Thomas Gleixner --- drivers/mtd/nand/ams-delta.c | 56 +++++++++---------- drivers/mtd/nand/au1550nd.c | 25 ++++++--- drivers/mtd/nand/autcpu12.c | 77 +++++++++++++++---------- drivers/mtd/nand/cs553x_nand.c | 32 +++-------- drivers/mtd/nand/diskonchip.c | 77 ++++++++++--------------- drivers/mtd/nand/edb7312.c | 42 ++++++-------- drivers/mtd/nand/h1910.c | 40 ++++--------- drivers/mtd/nand/nand_base.c | 115 ++++++++++++++++++-------------------- drivers/mtd/nand/nandsim.c | 76 +++++-------------------- drivers/mtd/nand/ndfc.c | 23 +++----- drivers/mtd/nand/ppchameleonevb.c | 102 ++++++++++++++++++--------------- drivers/mtd/nand/rtc_from4.c | 34 ++++------- drivers/mtd/nand/s3c2410.c | 64 +++++++-------------- drivers/mtd/nand/sharpsl.c | 41 ++++++-------- drivers/mtd/nand/spia.c | 27 +++++---- drivers/mtd/nand/toto.c | 65 ++++++++++----------- drivers/mtd/nand/ts7250.c | 44 +++++++-------- include/linux/mtd/nand.h | 33 +++++------ 18 files changed, 430 insertions(+), 543 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index aeaf2dece095..c0e96860686e 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -34,13 +34,6 @@ static struct mtd_info *ams_delta_mtd = NULL; #define NAND_MASK (AMS_DELTA_LATCH2_NAND_NRE | AMS_DELTA_LATCH2_NAND_NWE | AMS_DELTA_LATCH2_NAND_CLE | AMS_DELTA_LATCH2_NAND_ALE | AMS_DELTA_LATCH2_NAND_NCE | AMS_DELTA_LATCH2_NAND_NWP) -#define T_NAND_CTL_CLRALE(iob) ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_ALE, 0) -#define T_NAND_CTL_SETALE(iob) ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_ALE, AMS_DELTA_LATCH2_NAND_ALE) -#define T_NAND_CTL_CLRCLE(iob) ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_CLE, 0) -#define T_NAND_CTL_SETCLE(iob) ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_CLE, AMS_DELTA_LATCH2_NAND_CLE) -#define T_NAND_CTL_SETNCE(iob) ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NCE, 0) -#define T_NAND_CTL_CLRNCE(iob) ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NCE, AMS_DELTA_LATCH2_NAND_NCE) - /* * Define partitions for flash devices */ @@ -66,25 +59,6 @@ static struct mtd_partition partition_info[] = { .size = 3 * SZ_256K }, }; -/* - * hardware specific access to control-lines -*/ - -static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd) -{ - switch (cmd) { - - case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break; - case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break; - - case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break; - case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break; - - case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break; - case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break; - } -} - static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte) { struct nand_chip *this = mtd->priv; @@ -141,6 +115,32 @@ static int ams_delta_verify_buf(struct mtd_info *mtd, const u_char *buf, return 0; } +/* + * Command control function + * + * ctrl: + * NAND_NCE: bit 0 -> bit 2 + * NAND_CLE: bit 1 -> bit 7 + * NAND_ALE: bit 2 -> bit 6 + */ +static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + + if (ctrl & NAND_CTRL_CHANGE) { + unsigned long bits; + + bits = (~ctrl & NAND_NCE) << 2; + bits |= (ctrl & NAND_CLE) << 7; + bits |= (ctrl & NAND_ALE) << 6; + + ams_delta_latch2_write(0xC2, bits); + } + + if (cmd != NAND_CMD_NONE) + ams_delta_write_byte(mtd, cmd); +} + static int ams_delta_nand_ready(struct mtd_info *mtd) { return omap_get_gpio_datain(AMS_DELTA_GPIO_PIN_NAND_RB); @@ -183,7 +183,7 @@ static int __init ams_delta_init(void) this->write_buf = ams_delta_write_buf; this->read_buf = ams_delta_read_buf; this->verify_buf = ams_delta_verify_buf; - this->hwcontrol = ams_delta_hwcontrol; + this->cmd_ctrl = ams_delta_hwcontrol; if (!omap_request_gpio(AMS_DELTA_GPIO_PIN_NAND_RB)) { this->dev_ready = ams_delta_nand_ready; } else { @@ -200,7 +200,7 @@ static int __init ams_delta_init(void) AMS_DELTA_LATCH2_NAND_NCE | AMS_DELTA_LATCH2_NAND_NWP); - /* Scan to find existance of the device */ + /* Scan to find existance of the device */ if (nand_scan(ams_delta_mtd, 1)) { err = -ENXIO; goto out_mtd; diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 29dde7dcafa1..275453ea7a71 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -269,6 +269,18 @@ static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) return 0; } +/* Select the chip by setting nCE to low */ +#define NAND_CTL_SETNCE 1 +/* Deselect the chip by setting nCE to high */ +#define NAND_CTL_CLRNCE 2 +/* Select the command latch by setting CLE to high */ +#define NAND_CTL_SETCLE 3 +/* Deselect the command latch by setting CLE to low */ +#define NAND_CTL_CLRCLE 4 +/* Select the address latch by setting ALE to high */ +#define NAND_CTL_SETALE 5 +/* Deselect the address latch by setting ALE to low */ +#define NAND_CTL_CLRALE 6 static void au1550_hwcontrol(struct mtd_info *mtd, int cmd) { @@ -349,7 +361,7 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i ulong flags; /* Begin command latch cycle */ - this->hwcontrol(mtd, NAND_CTL_SETCLE); + au1550_hwcontrol(mtd, NAND_CTL_SETCLE); /* * Write out the command to the device. */ @@ -372,10 +384,10 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i this->write_byte(mtd, command); /* Set ALE and clear CLE to start address cycle */ - this->hwcontrol(mtd, NAND_CTL_CLRCLE); + au1550_hwcontrol(mtd, NAND_CTL_CLRCLE); if (column != -1 || page_addr != -1) { - this->hwcontrol(mtd, NAND_CTL_SETALE); + au1550_hwcontrol(mtd, NAND_CTL_SETALE); /* Serially input address */ if (column != -1) { @@ -400,7 +412,7 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i */ ce_override = 1; local_irq_save(flags); - this->hwcontrol(mtd, NAND_CTL_SETNCE); + au1550_hwcontrol(mtd, NAND_CTL_SETNCE); } this->write_byte(mtd, (u8)(page_addr >> 8)); @@ -410,7 +422,7 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i this->write_byte(mtd, (u8)((page_addr >> 16) & 0x0f)); } /* Latch in address */ - this->hwcontrol(mtd, NAND_CTL_CLRALE); + au1550_hwcontrol(mtd, NAND_CTL_CLRALE); } /* @@ -443,7 +455,7 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i udelay(1); /* Release -CE and re-enable interrupts. */ - this->hwcontrol(mtd, NAND_CTL_CLRNCE); + au1550_hwcontrol(mtd, NAND_CTL_CLRNCE); local_irq_restore(flags); return; } @@ -571,7 +583,6 @@ static int __init au1xxx_nand_init(void) nand_width = au_readl(MEM_STCFG3) & (1 << 22); /* Set address of hardware control function */ - this->hwcontrol = au1550_hwcontrol; this->dev_ready = au1550_device_ready; this->select_chip = au1550_select_chip; this->cmdfunc = au1550_command; diff --git a/drivers/mtd/nand/autcpu12.c b/drivers/mtd/nand/autcpu12.c index dbb1b6267ade..fe94ae9ae1f2 100644 --- a/drivers/mtd/nand/autcpu12.c +++ b/drivers/mtd/nand/autcpu12.c @@ -4,7 +4,7 @@ * Copyright (c) 2002 Thomas Gleixner * * Derived from drivers/mtd/spia.c - * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * * $Id: autcpu12.c,v 1.23 2005/11/07 11:14:30 gleixner Exp $ * @@ -42,11 +42,6 @@ * MTD structure for AUTCPU12 board */ static struct mtd_info *autcpu12_mtd = NULL; - -static int autcpu12_io_base = CS89712_VIRT_BASE; -static int autcpu12_fio_pbase = AUTCPU12_PHYS_SMC; -static int autcpu12_fio_ctrl = AUTCPU12_SMC_SELECT_OFFSET; -static int autcpu12_pedr = AUTCPU12_SMC_PORT_OFFSET; static void __iomem *autcpu12_fio_base; /* @@ -94,31 +89,42 @@ static struct mtd_partition partition_info128k[] = { #define NUM_PARTITIONS128K 2 /* * hardware specific access to control-lines -*/ - -static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd) + * + * ALE bit 4 autcpu12_pedr + * CLE bit 5 autcpu12_pedr + * NCE bit 0 fio_ctrl + * + */ +static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) { - switch (cmd) { + struct nand_chip *chip = mtd->priv; - case NAND_CTL_SETCLE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) |= AUTCPU12_SMC_CLE; break; - case NAND_CTL_CLRCLE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) &= ~AUTCPU12_SMC_CLE; break; + if (ctrl & NAND_CTRL_CHANGE) { + void __iomem *addr + unsigned char bits; - case NAND_CTL_SETALE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) |= AUTCPU12_SMC_ALE; break; - case NAND_CTL_CLRALE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) &= ~AUTCPU12_SMC_ALE; break; + addr = CS89712_VIRT_BASE + AUTCPU12_SMC_PORT_OFFSET; + bits = (ctrl & NAND_CLE) << 4; + bits |= (ctrl & NAND_ALE) << 2; + writeb((readb(addr) & ~0x30) | bits, addr); - case NAND_CTL_SETNCE: (*(volatile unsigned char *) (autcpu12_fio_base + autcpu12_fio_ctrl)) = 0x01; break; - case NAND_CTL_CLRNCE: (*(volatile unsigned char *) (autcpu12_fio_base + autcpu12_fio_ctrl)) = 0x00; break; + addr = autcpu12_fio_base + AUTCPU12_SMC_SELECT_OFFSET; + writeb((readb(addr) & ~0x1) | (ctrl & NAND_NCE), addr); } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); } /* -* read device ready pin -*/ + * read device ready pin + */ int autcpu12_device_ready(struct mtd_info *mtd) { + void __iomem *addr = CS89712_VIRT_BASE + AUTCPU12_SMC_PORT_OFFSET; - return ((*(volatile unsigned char *)(autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0; - + return readb(addr) & AUTCPU12_SMC_RDY; } /* @@ -130,7 +136,8 @@ static int __init autcpu12_init(void) int err = 0; /* Allocate memory for MTD device structure and private data */ - autcpu12_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); + autcpu12_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), + GFP_KERNEL); if (!autcpu12_mtd) { printk("Unable to allocate AUTCPU12 NAND MTD device structure.\n"); err = -ENOMEM; @@ -138,7 +145,7 @@ static int __init autcpu12_init(void) } /* map physical adress */ - autcpu12_fio_base = ioremap(autcpu12_fio_pbase, SZ_1K); + autcpu12_fio_base = ioremap(AUTCPU12_PHYS_SMC, SZ_1K); if (!autcpu12_fio_base) { printk("Ioremap autcpu12 SmartMedia Card failed\n"); err = -EIO; @@ -159,7 +166,7 @@ static int __init autcpu12_init(void) /* Set address of NAND IO lines */ this->IO_ADDR_R = autcpu12_fio_base; this->IO_ADDR_W = autcpu12_fio_base; - this->hwcontrol = autcpu12_hwcontrol; + this->cmd_ctrl = autcpu12_hwcontrol; this->dev_ready = autcpu12_device_ready; /* 20 us command delay time */ this->chip_delay = 20; @@ -179,10 +186,22 @@ static int __init autcpu12_init(void) /* Register the partitions */ switch (autcpu12_mtd->size) { - case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break; - case SZ_32M: add_mtd_partitions(autcpu12_mtd, partition_info32k, NUM_PARTITIONS32K); break; - case SZ_64M: add_mtd_partitions(autcpu12_mtd, partition_info64k, NUM_PARTITIONS64K); break; - case SZ_128M: add_mtd_partitions(autcpu12_mtd, partition_info128k, NUM_PARTITIONS128K); break; + case SZ_16M: + add_mtd_partitions(autcpu12_mtd, partition_info16k, + NUM_PARTITIONS16K); + break; + case SZ_32M: + add_mtd_partitions(autcpu12_mtd, partition_info32k, + NUM_PARTITIONS32K); + break; + case SZ_64M: + add_mtd_partitions(autcpu12_mtd, partition_info64k, + NUM_PARTITIONS64K); + break; + case SZ_128M: + add_mtd_partitions(autcpu12_mtd, partition_info128k, + NUM_PARTITIONS128K); + break; default: printk("Unsupported SmartMedia device\n"); err = -ENXIO; @@ -191,7 +210,7 @@ static int __init autcpu12_init(void) goto out; out_ior: - iounmap((void *)autcpu12_fio_base); + iounmap(autcpu12_fio_base); out_mtd: kfree(autcpu12_mtd); out: @@ -209,7 +228,7 @@ static void __exit autcpu12_cleanup(void) nand_release(autcpu12_mtd); /* unmap physical adress */ - iounmap((void *)autcpu12_fio_base); + iounmap(autcpu12_fio_base); /* Free the MTD device structure */ kfree(autcpu12_mtd); diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 064f3feadf53..cd3d7eb132f9 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -131,33 +131,17 @@ static void cs553x_write_byte(struct mtd_info *mtd, u_char byte) writeb(byte, this->IO_ADDR_W + 0x801); } -static void cs553x_hwcontrol(struct mtd_info *mtd, int cmd) +static void cs553x_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) { struct nand_chip *this = mtd->priv; void __iomem *mmio_base = this->IO_ADDR_R; - unsigned char ctl; - - switch (cmd) { - case NAND_CTL_SETCLE: - ctl = CS_NAND_CTL_CLE; - break; - - case NAND_CTL_CLRCLE: - case NAND_CTL_CLRALE: - case NAND_CTL_SETNCE: - ctl = 0; - break; - - case NAND_CTL_SETALE: - ctl = CS_NAND_CTL_ALE; - break; - - default: - case NAND_CTL_CLRNCE: - ctl = CS_NAND_CTL_CE; - break; + if (ctrl & NAND_CTRL_CHANGE) { + unsigned char ctl = (ctrl & ~NAND_CTRL_CHANGE ) ^ 0x01; + writeb(ctl, mmio_base + MM_NAND_CTL); } - writeb(ctl, mmio_base + MM_NAND_CTL); + if (cmd != NAND_CMD_NONE) + cs553x_write_byte(mtd, cmd); } static int cs553x_device_ready(struct mtd_info *mtd) @@ -233,7 +217,7 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) goto out_mtd; } - this->hwcontrol = cs553x_hwcontrol; + this->cmd_ctrl = cs553x_hwcontrol; this->dev_ready = cs553x_device_ready; this->read_byte = cs553x_read_byte; this->write_byte = cs553x_write_byte; diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index f77298f3af60..e4bb6b429f87 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -95,7 +95,8 @@ static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 }; #define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil) #define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k) -static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd); +static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int bitmask); static void doc200x_select_chip(struct mtd_info *mtd, int chip); static int debug = 0; @@ -402,12 +403,10 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) uint16_t ret; doc200x_select_chip(mtd, nr); - doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); - this->write_byte(mtd, NAND_CMD_READID); - doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); - doc200x_hwcontrol(mtd, NAND_CTL_SETALE); - this->write_byte(mtd, 0); - doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); + doc200x_hwcontrol(mtd, NAND_CMD_READID, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); /* We cant' use dev_ready here, but at least we wait for the * command to complete @@ -425,12 +424,11 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) } ident; void __iomem *docptr = doc->virtadr; - doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); - doc2000_write_byte(mtd, NAND_CMD_READID); - doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); - doc200x_hwcontrol(mtd, NAND_CTL_SETALE); - doc2000_write_byte(mtd, 0); - doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); + doc200x_hwcontrol(mtd, NAND_CMD_READID, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); udelay(50); @@ -690,54 +688,37 @@ static void doc200x_select_chip(struct mtd_info *mtd, int chip) chip -= (floor * doc->chips_per_floor); /* 11.4.4 -- deassert CE before changing chip */ - doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE); + doc200x_hwcontrol(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); WriteDOC(floor, docptr, FloorSelect); WriteDOC(chip, docptr, CDSNDeviceSelect); - doc200x_hwcontrol(mtd, NAND_CTL_SETNCE); + doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); doc->curchip = chip; doc->curfloor = floor; } -static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd) +#define CDSN_CTRL_MSK (CDSN_CTRL_CE | CDSN_CTRL_CLE | CDSN_CTRL_ALE) + +static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; - switch (cmd) { - case NAND_CTL_SETNCE: - doc->CDSNControl |= CDSN_CTRL_CE; - break; - case NAND_CTL_CLRNCE: - doc->CDSNControl &= ~CDSN_CTRL_CE; - break; - case NAND_CTL_SETCLE: - doc->CDSNControl |= CDSN_CTRL_CLE; - break; - case NAND_CTL_CLRCLE: - doc->CDSNControl &= ~CDSN_CTRL_CLE; - break; - case NAND_CTL_SETALE: - doc->CDSNControl |= CDSN_CTRL_ALE; - break; - case NAND_CTL_CLRALE: - doc->CDSNControl &= ~CDSN_CTRL_ALE; - break; - case NAND_CTL_SETWP: - doc->CDSNControl |= CDSN_CTRL_WP; - break; - case NAND_CTL_CLRWP: - doc->CDSNControl &= ~CDSN_CTRL_WP; - break; + if (ctrl & NAND_CTRL_CHANGE) { + doc->CDSNControl &= ~CDSN_CTRL_MSK; + doc->CDSNControl |= ctrl & CDSN_CTRL_MSK; + if (debug) + printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl); + WriteDOC(doc->CDSNControl, docptr, CDSNControl); + /* 11.4.3 -- 4 NOPs after CSDNControl write */ + DoC_Delay(doc, 4); } - if (debug) - printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl); - WriteDOC(doc->CDSNControl, docptr, CDSNControl); - /* 11.4.3 -- 4 NOPs after CSDNControl write */ - DoC_Delay(doc, 4); + if (cmd != NAND_CMD_NONE) + this->write_byte(mtd, cmd); } static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) @@ -1510,7 +1491,7 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd) this->read_buf = doc2001plus_readbuf; this->verify_buf = doc2001plus_verifybuf; this->scan_bbt = inftl_scan_bbt; - this->hwcontrol = NULL; + this->cmd_ctrl = NULL; this->select_chip = doc2001plus_select_chip; this->cmdfunc = doc2001plus_command; this->ecc.hwctl = doc2001plus_enable_hwecc; @@ -1670,7 +1651,7 @@ static int __init doc_probe(unsigned long physadr) nand->priv = doc; nand->select_chip = doc200x_select_chip; - nand->hwcontrol = doc200x_hwcontrol; + nand->cmd_ctrl = doc200x_hwcontrol; nand->dev_ready = doc200x_dev_ready; nand->waitfunc = doc200x_wait; nand->block_bad = doc200x_block_bad; diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c index 8e56570af91f..ba5a2174a408 100644 --- a/drivers/mtd/nand/edb7312.c +++ b/drivers/mtd/nand/edb7312.c @@ -73,32 +73,26 @@ static struct mtd_partition partition_info[] = { /* * hardware specific access to control-lines + * + * NAND_NCE: bit 0 -> bit 7 + * NAND_CLE: bit 1 -> bit 4 + * NAND_ALE: bit 2 -> bit 5 */ -static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd) +static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { - switch (cmd) { - - case NAND_CTL_SETCLE: - clps_writeb(clps_readb(ep7312_pxdr) | 0x10, ep7312_pxdr); - break; - case NAND_CTL_CLRCLE: - clps_writeb(clps_readb(ep7312_pxdr) & ~0x10, ep7312_pxdr); - break; - - case NAND_CTL_SETALE: - clps_writeb(clps_readb(ep7312_pxdr) | 0x20, ep7312_pxdr); - break; - case NAND_CTL_CLRALE: - clps_writeb(clps_readb(ep7312_pxdr) & ~0x20, ep7312_pxdr); - break; - - case NAND_CTL_SETNCE: - clps_writeb((clps_readb(ep7312_pxdr) | 0x80) & ~0x40, ep7312_pxdr); - break; - case NAND_CTL_CLRNCE: - clps_writeb((clps_readb(ep7312_pxdr) | 0x80) | 0x40, ep7312_pxdr); - break; + struct nand_chip *chip = mtd->priv; + + if (ctrl & NAND_CTRL_CHANGE) { + unsigned char bits; + + bits = (ctrl & (NAND_CLE | NAND_ALE)) << 3; + bits = (ctrl & NAND_NCE) << 7; + + clps_writeb((clps_readb(ep7312_pxdr) & 0xB0) | 0x10, + ep7312_pxdr); } + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); } /* @@ -159,7 +153,7 @@ static int __init ep7312_init(void) /* insert callbacks */ this->IO_ADDR_R = ep7312_fio_base; this->IO_ADDR_W = ep7312_fio_base; - this->hwcontrol = ep7312_hwcontrol; + this->cmd_ctrl = ep7312_hwcontrol; this->dev_ready = ep7312_device_ready; /* 15 us command delay time */ this->chip_delay = 15; diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c index 06e91fa11b34..2d585d2d090c 100644 --- a/drivers/mtd/nand/h1910.c +++ b/drivers/mtd/nand/h1910.c @@ -56,36 +56,18 @@ static struct mtd_partition partition_info[] = { /* * hardware specific access to control-lines + * + * NAND_NCE: bit 0 - don't care + * NAND_CLE: bit 1 - address bit 2 + * NAND_ALE: bit 2 - address bit 3 */ -static void h1910_hwcontrol(struct mtd_info *mtd, int cmd) +static void h1910_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) { - struct nand_chip *this = (struct nand_chip *)(mtd->priv); - - switch (cmd) { - - case NAND_CTL_SETCLE: - this->IO_ADDR_R |= (1 << 2); - this->IO_ADDR_W |= (1 << 2); - break; - case NAND_CTL_CLRCLE: - this->IO_ADDR_R &= ~(1 << 2); - this->IO_ADDR_W &= ~(1 << 2); - break; - - case NAND_CTL_SETALE: - this->IO_ADDR_R |= (1 << 3); - this->IO_ADDR_W |= (1 << 3); - break; - case NAND_CTL_CLRALE: - this->IO_ADDR_R &= ~(1 << 3); - this->IO_ADDR_W &= ~(1 << 3); - break; - - case NAND_CTL_SETNCE: - break; - case NAND_CTL_CLRNCE: - break; - } + struct nand_chip *chip = mtd->priv; + + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W | ((ctrl & 0x6) << 1)); } /* @@ -145,7 +127,7 @@ static int __init h1910_init(void) /* insert callbacks */ this->IO_ADDR_R = nandaddr; this->IO_ADDR_W = nandaddr; - this->hwcontrol = h1910_hwcontrol; + this->cmd_ctrl = h1910_hwcontrol; this->dev_ready = NULL; /* unknown whether that was correct or not so we will just do it like this */ /* 15 us command delay time */ this->chip_delay = 50; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index aa2e14538bf4..f6997fb77b91 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -276,10 +276,10 @@ static void nand_select_chip(struct mtd_info *mtd, int chip) struct nand_chip *this = mtd->priv; switch (chip) { case -1: - this->hwcontrol(mtd, NAND_CTL_CLRNCE); + this->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); break; case 0: - this->hwcontrol(mtd, NAND_CTL_SETNCE); + this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); break; default: @@ -548,13 +548,12 @@ static void nand_wait_ready(struct mtd_info *mtd) * Send command to NAND device. This function is used for small page * devices (256/512 Bytes per page) */ -static void nand_command(struct mtd_info *mtd, unsigned command, int column, - int page_addr) +static void nand_command(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) { register struct nand_chip *this = mtd->priv; + int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE; - /* Begin command latch cycle */ - this->hwcontrol(mtd, NAND_CTL_SETCLE); /* * Write out the command to the device. */ @@ -572,33 +571,32 @@ static void nand_command(struct mtd_info *mtd, unsigned command, int column, column -= 256; readcmd = NAND_CMD_READ1; } - this->write_byte(mtd, readcmd); + this->cmd_ctrl(mtd, readcmd, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; } - this->write_byte(mtd, command); + this->cmd_ctrl(mtd, command, ctrl); - /* Set ALE and clear CLE to start address cycle */ - this->hwcontrol(mtd, NAND_CTL_CLRCLE); - - if (column != -1 || page_addr != -1) { - this->hwcontrol(mtd, NAND_CTL_SETALE); - - /* Serially input address */ - if (column != -1) { - /* Adjust columns for 16 bit buswidth */ - if (this->options & NAND_BUSWIDTH_16) - column >>= 1; - this->write_byte(mtd, column); - } - if (page_addr != -1) { - this->write_byte(mtd, (uint8_t)(page_addr & 0xff)); - this->write_byte(mtd, (uint8_t)((page_addr >> 8) & 0xff)); - /* One more address cycle for devices > 32MiB */ - if (this->chipsize > (32 << 20)) - this->write_byte(mtd, (uint8_t)((page_addr >> 16) & 0x0f)); - } - /* Latch in address */ - this->hwcontrol(mtd, NAND_CTL_CLRALE); + /* + * Address cycle, when necessary + */ + ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE; + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (this->options & NAND_BUSWIDTH_16) + column >>= 1; + this->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + } + if (page_addr != -1) { + this->cmd_ctrl(mtd, page_addr, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + this->cmd_ctrl(mtd, page_addr >> 8, ctrl); + /* One more address cycle for devices > 32MiB */ + if (this->chipsize > (32 << 20)) + this->cmd_ctrl(mtd, page_addr >> 16, ctrl); } + this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); /* * program and erase have their own busy handlers @@ -611,15 +609,16 @@ static void nand_command(struct mtd_info *mtd, unsigned command, int column, case NAND_CMD_ERASE2: case NAND_CMD_SEQIN: case NAND_CMD_STATUS: + this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE); return; case NAND_CMD_RESET: if (this->dev_ready) break; udelay(this->chip_delay); - this->hwcontrol(mtd, NAND_CTL_SETCLE); - this->write_byte(mtd, NAND_CMD_STATUS); - this->hwcontrol(mtd, NAND_CTL_CLRCLE); + this->cmd_ctrl(mtd, NAND_CMD_STATUS, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE); while (!(this->read_byte(mtd) & NAND_STATUS_READY)) ; return; @@ -648,12 +647,13 @@ static void nand_command(struct mtd_info *mtd, unsigned command, int column, * @column: the column address for this command, -1 if none * @page_addr: the page address for this command, -1 if none * - * Send command to NAND device. This is the version for the new large page devices - * We dont have the separate regions as we have in the small page devices. - * We must emulate NAND_CMD_READOOB to keep the code compatible. + * Send command to NAND device. This is the version for the new large page + * devices We dont have the separate regions as we have in the small page + * devices. We must emulate NAND_CMD_READOOB to keep the code compatible. * */ -static void nand_command_lp(struct mtd_info *mtd, unsigned command, int column, int page_addr) +static void nand_command_lp(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) { register struct nand_chip *this = mtd->priv; @@ -663,34 +663,33 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned command, int column, command = NAND_CMD_READ0; } - /* Begin command latch cycle */ - this->hwcontrol(mtd, NAND_CTL_SETCLE); - /* Write out the command to the device. */ - this->write_byte(mtd, (command & 0xff)); - /* End command latch cycle */ - this->hwcontrol(mtd, NAND_CTL_CLRCLE); + /* Command latch cycle */ + this->cmd_ctrl(mtd, command & 0xff, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); if (column != -1 || page_addr != -1) { - this->hwcontrol(mtd, NAND_CTL_SETALE); + int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ if (this->options & NAND_BUSWIDTH_16) column >>= 1; - this->write_byte(mtd, column & 0xff); - this->write_byte(mtd, column >> 8); + this->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + this->cmd_ctrl(mtd, column >> 8, ctrl); } if (page_addr != -1) { - this->write_byte(mtd, (uint8_t)(page_addr & 0xff)); - this->write_byte(mtd, (uint8_t)((page_addr >> 8) & 0xff)); + this->cmd_ctrl(mtd, page_addr, ctrl); + this->cmd_ctrl(mtd, page_addr >> 8, + NAND_NCE | NAND_ALE); /* One more address cycle for devices > 128MiB */ if (this->chipsize > (128 << 20)) - this->write_byte(mtd, (uint8_t)((page_addr >> 16) & 0xff)); + this->cmd_ctrl(mtd, page_addr >> 16, + NAND_NCE | NAND_ALE); } - /* Latch in address */ - this->hwcontrol(mtd, NAND_CTL_CLRALE); } + this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); /* * program and erase have their own busy handlers @@ -722,20 +721,14 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned command, int column, if (this->dev_ready) break; udelay(this->chip_delay); - this->hwcontrol(mtd, NAND_CTL_SETCLE); - this->write_byte(mtd, NAND_CMD_STATUS); - this->hwcontrol(mtd, NAND_CTL_CLRCLE); + this->cmd_ctrl(mtd, NAND_CMD_STATUS, NAND_NCE | NAND_CLE); + this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE); while (!(this->read_byte(mtd) & NAND_STATUS_READY)) ; return; case NAND_CMD_READ0: - /* Begin command latch cycle */ - this->hwcontrol(mtd, NAND_CTL_SETCLE); - /* Write out the start read command */ - this->write_byte(mtd, NAND_CMD_READSTART); - /* End command latch cycle */ - this->hwcontrol(mtd, NAND_CTL_CLRCLE); - /* Fall through into ready check */ + this->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_NCE | NAND_CLE); + this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE); /* This applies to read commands */ default: diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 22af9b29d2bf..ecf727b32dec 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -1071,68 +1071,6 @@ switch_state(struct nandsim *ns) } } -static void -ns_hwcontrol(struct mtd_info *mtd, int cmd) -{ - struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; - - switch (cmd) { - - /* set CLE line high */ - case NAND_CTL_SETCLE: - NS_DBG("ns_hwcontrol: start command latch cycles\n"); - ns->lines.cle = 1; - break; - - /* set CLE line low */ - case NAND_CTL_CLRCLE: - NS_DBG("ns_hwcontrol: stop command latch cycles\n"); - ns->lines.cle = 0; - break; - - /* set ALE line high */ - case NAND_CTL_SETALE: - NS_DBG("ns_hwcontrol: start address latch cycles\n"); - ns->lines.ale = 1; - break; - - /* set ALE line low */ - case NAND_CTL_CLRALE: - NS_DBG("ns_hwcontrol: stop address latch cycles\n"); - ns->lines.ale = 0; - break; - - /* set WP line high */ - case NAND_CTL_SETWP: - NS_DBG("ns_hwcontrol: enable write protection\n"); - ns->lines.wp = 1; - break; - - /* set WP line low */ - case NAND_CTL_CLRWP: - NS_DBG("ns_hwcontrol: disable write protection\n"); - ns->lines.wp = 0; - break; - - /* set CE line low */ - case NAND_CTL_SETNCE: - NS_DBG("ns_hwcontrol: enable chip\n"); - ns->lines.ce = 1; - break; - - /* set CE line high */ - case NAND_CTL_CLRNCE: - NS_DBG("ns_hwcontrol: disable chip\n"); - ns->lines.ce = 0; - break; - - default: - NS_ERR("hwcontrol: unknown command\n"); - } - - return; -} - static u_char ns_nand_read_byte(struct mtd_info *mtd) { @@ -1359,6 +1297,18 @@ ns_nand_write_byte(struct mtd_info *mtd, u_char byte) return; } +static void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask) +{ + struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv; + + ns->lines.cle = bitmask & NAND_CLE ? 1 : 0; + ns->lines.ale = bitmask & NAND_ALE ? 1 : 0; + ns->lines.ce = bitmask & NAND_NCE ? 1 : 0; + + if (cmd != NAND_CMD_NONE) + ns_nand_write_byte(mtd, cmd); +} + static int ns_device_ready(struct mtd_info *mtd) { @@ -1514,7 +1464,7 @@ static int __init ns_init_module(void) /* * Register simulator's callbacks. */ - chip->hwcontrol = ns_hwcontrol; + chip->cmd_ctrl = ns_hwcontrol; chip->read_byte = ns_nand_read_byte; chip->dev_ready = ns_device_ready; chip->write_byte = ns_nand_write_byte; diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index e2dc81de106a..481541a683ca 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -60,22 +60,17 @@ static void ndfc_select_chip(struct mtd_info *mtd, int chip) writel(ccr, ndfc->ndfcbase + NDFC_CCR); } -static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd) +static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { - struct ndfc_controller *ndfc = &ndfc_ctrl; struct nand_chip *chip = mtd->priv; - switch (cmd) { - case NAND_CTL_SETCLE: - chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_CMD; - break; - case NAND_CTL_SETALE: - chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_ALE; - break; - default: - chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; - break; - } + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + writel(cmd & 0xFF, chip->IO_ADDR_W + NDFC_CMD); + else + writel(cmd & 0xFF, chip->IO_ADDR_W + NDFC_ALE); } static int ndfc_ready(struct mtd_info *mtd) @@ -158,7 +153,7 @@ static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA; chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; - chip->hwcontrol = ndfc_hwcontrol; + chip->cmd_ctrl = ndfc_hwcontrol; chip->dev_ready = ndfc_ready; chip->select_chip = ndfc_select_chip; chip->chip_delay = 50; diff --git a/drivers/mtd/nand/ppchameleonevb.c b/drivers/mtd/nand/ppchameleonevb.c index 9fab0998524d..22fa65c12ab9 100644 --- a/drivers/mtd/nand/ppchameleonevb.c +++ b/drivers/mtd/nand/ppchameleonevb.c @@ -108,54 +108,68 @@ extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partitio /* * hardware specific access to control-lines */ -static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd) +static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd, + unsigned int ctrl) { - switch (cmd) { - - case NAND_CTL_SETCLE: - MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR); - break; - case NAND_CTL_CLRCLE: - MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR); - break; - case NAND_CTL_SETALE: - MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR); - break; - case NAND_CTL_CLRALE: - MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR); - break; - case NAND_CTL_SETNCE: - MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND0_PADDR); - break; - case NAND_CTL_CLRNCE: - MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND0_PADDR); - break; + struct nand_chip *chip = mtd->priv; + + if (ctrl & NAND_CTRL_CHANGE) { +#error Missing headerfiles. No way to fix this. -tglx + switch (cmd) { + case NAND_CTL_SETCLE: + MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR); + break; + case NAND_CTL_CLRCLE: + MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR); + break; + case NAND_CTL_SETALE: + MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR); + break; + case NAND_CTL_CLRALE: + MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR); + break; + case NAND_CTL_SETNCE: + MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND0_PADDR); + break; + case NAND_CTL_CLRNCE: + MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND0_PADDR); + break; + } } + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); } -static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd) +static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd, + unsigned int ctrl) { - switch (cmd) { - - case NAND_CTL_SETCLE: - MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR); - break; - case NAND_CTL_CLRCLE: - MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR); - break; - case NAND_CTL_SETALE: - MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR); - break; - case NAND_CTL_CLRALE: - MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR); - break; - case NAND_CTL_SETNCE: - MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR); - break; - case NAND_CTL_CLRNCE: - MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR); - break; + struct nand_chip *chip = mtd->priv; + + if (ctrl & NAND_CTRL_CHANGE) { +#error Missing headerfiles. No way to fix this. -tglx + switch (cmd) { + case NAND_CTL_SETCLE: + MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR); + break; + case NAND_CTL_CLRCLE: + MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR); + break; + case NAND_CTL_SETALE: + MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR); + break; + case NAND_CTL_CLRALE: + MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR); + break; + case NAND_CTL_SETNCE: + MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR); + break; + case NAND_CTL_CLRNCE: + MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR); + break; + } } + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); } #ifdef USE_READY_BUSY_PIN @@ -251,7 +265,7 @@ static int __init ppchameleonevb_init(void) /* insert callbacks */ this->IO_ADDR_R = ppchameleon_fio_base; this->IO_ADDR_W = ppchameleon_fio_base; - this->hwcontrol = ppchameleon_hwcontrol; + this->cmd_ctrl = ppchameleon_hwcontrol; #ifdef USE_READY_BUSY_PIN this->dev_ready = ppchameleon_device_ready; #endif @@ -351,7 +365,7 @@ static int __init ppchameleonevb_init(void) /* insert callbacks */ this->IO_ADDR_R = ppchameleonevb_fio_base; this->IO_ADDR_W = ppchameleonevb_fio_base; - this->hwcontrol = ppchameleonevb_hwcontrol; + this->cmd_ctrl = ppchameleonevb_hwcontrol; #ifdef USE_READY_BUSY_PIN this->dev_ready = ppchameleonevb_device_ready; #endif diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index f8e631c89a60..6c97bfaea19a 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -208,32 +208,18 @@ static uint8_t revbits[256] = { * Address lines (A24-A22), so no action is required here. * */ -static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd) +static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) { - struct nand_chip *this = (struct nand_chip *)(mtd->priv); + struct nand_chip *chip = (mtd->priv); - switch (cmd) { + if (cmd == NAND_CMD_NONE) + return; - case NAND_CTL_SETCLE: - this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_CLE); - break; - case NAND_CTL_CLRCLE: - this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_CLE); - break; - - case NAND_CTL_SETALE: - this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_ALE); - break; - case NAND_CTL_CLRALE: - this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_ALE); - break; - - case NAND_CTL_SETNCE: - break; - case NAND_CTL_CLRNCE: - break; - - } + if (ctrl & NAND_CLE) + writeb(cmd, chip->IO_ADDR_W | RTC_FROM4_CLE); + else + writeb(cmd, chip->IO_ADDR_W | RTC_FROM4_ALE); } /* @@ -559,7 +545,7 @@ static int __init rtc_from4_init(void) this->IO_ADDR_R = rtc_from4_fio_base; this->IO_ADDR_W = rtc_from4_fio_base; /* Set address of hardware control function */ - this->hwcontrol = rtc_from4_hwcontrol; + this->cmd_ctrl = rtc_from4_hwcontrol; /* Set address of chip select function */ this->select_chip = rtc_from4_nand_select_chip; /* command delay time (in us) */ diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 608340a25278..215227d1a65c 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -256,60 +256,36 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) * */ -static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd) +static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigend int ctrl) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); struct nand_chip *chip = mtd->priv; - switch (cmd) { - case NAND_CTL_SETNCE: - case NAND_CTL_CLRNCE: - printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__); - break; - - case NAND_CTL_SETCLE: - chip->IO_ADDR_W = info->regs + S3C2410_NFCMD; - break; - - case NAND_CTL_SETALE: - chip->IO_ADDR_W = info->regs + S3C2410_NFADDR; - break; - - /* NAND_CTL_CLRCLE: */ - /* NAND_CTL_CLRALE: */ - default: - chip->IO_ADDR_W = info->regs + S3C2410_NFDATA; - break; - } + if (cmd == NAND_CMD_NONE) + return; + + if (cmd & NAND_CLE) + writeb(cmd, info->regs + S3C2410_NFCMD); + else + writeb(cmd, info->regs + S3C2410_NFADDR); } /* command and control functions */ -static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd) +static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigend int ctrl) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); struct nand_chip *chip = mtd->priv; - switch (cmd) { - case NAND_CTL_SETNCE: - case NAND_CTL_CLRNCE: - printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__); - break; - - case NAND_CTL_SETCLE: - chip->IO_ADDR_W = info->regs + S3C2440_NFCMD; - break; - - case NAND_CTL_SETALE: - chip->IO_ADDR_W = info->regs + S3C2440_NFADDR; - break; - - /* NAND_CTL_CLRCLE: */ - /* NAND_CTL_CLRALE: */ - default: - chip->IO_ADDR_W = info->regs + S3C2440_NFDATA; - break; - } + if (cmd == NAND_CMD_NONE) + return; + + if (cmd & NAND_CLE) + writeb(cmd, info->regs + S3C2440_NFCMD); + else + writeb(cmd, info->regs + S3C2440_NFADDR); } /* s3c2410_nand_devready() @@ -498,7 +474,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->IO_ADDR_R = info->regs + S3C2410_NFDATA; chip->IO_ADDR_W = info->regs + S3C2410_NFDATA; - chip->hwcontrol = s3c2410_nand_hwcontrol; + chip->cmd_ctrl = s3c2410_nand_hwcontrol; chip->dev_ready = s3c2410_nand_devready; chip->write_buf = s3c2410_nand_write_buf; chip->read_buf = s3c2410_nand_read_buf; @@ -511,7 +487,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, if (info->is_s3c2440) { chip->IO_ADDR_R = info->regs + S3C2440_NFDATA; chip->IO_ADDR_W = info->regs + S3C2440_NFDATA; - chip->hwcontrol = s3c2440_nand_hwcontrol; + chip->cmd_ctrl = s3c2440_nand_hwcontrol; } nmtd->info = info; diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 5554d0b97c8c..45a1da724bff 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -77,31 +77,26 @@ static struct mtd_partition sharpsl_nand_default_partition_info[] = { /* * hardware specific access to control-lines + * ctrl: + * NAND_CNE: bit 0 -> bit 0 & 4 + * NAND_CLE: bit 1 -> bit 1 + * NAND_ALE: bit 2 -> bit 2 + * */ -static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd) +static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) { - switch (cmd) { - case NAND_CTL_SETCLE: - writeb(readb(FLASHCTL) | FLCLE, FLASHCTL); - break; - case NAND_CTL_CLRCLE: - writeb(readb(FLASHCTL) & ~FLCLE, FLASHCTL); - break; - - case NAND_CTL_SETALE: - writeb(readb(FLASHCTL) | FLALE, FLASHCTL); - break; - case NAND_CTL_CLRALE: - writeb(readb(FLASHCTL) & ~FLALE, FLASHCTL); - break; - - case NAND_CTL_SETNCE: - writeb(readb(FLASHCTL) & ~(FLCE0 | FLCE1), FLASHCTL); - break; - case NAND_CTL_CLRNCE: - writeb(readb(FLASHCTL) | (FLCE0 | FLCE1), FLASHCTL); - break; + struct nand_chip *chip = mtd->priv; + + if (ctrl & NAND_CTRL_CHANGE) { + unsigned char bits = ctrl & 0x07; + + bits |= (ctrl & 0x01) << 4; + writeb((readb(FLASHCTL) & 0x17) | bits, FLASHCTL); } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); } static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; @@ -196,7 +191,7 @@ static int __init sharpsl_nand_init(void) this->IO_ADDR_R = FLASHIO; this->IO_ADDR_W = FLASHIO; /* Set address of hardware control function */ - this->hwcontrol = sharpsl_nand_hwcontrol; + this->cmd_ctrl = sharpsl_nand_hwcontrol; this->dev_ready = sharpsl_nand_dev_ready; /* 15 us command delay time */ this->chip_delay = 15; diff --git a/drivers/mtd/nand/spia.c b/drivers/mtd/nand/spia.c index 9737f1d67c3c..1f6d429b1583 100644 --- a/drivers/mtd/nand/spia.c +++ b/drivers/mtd/nand/spia.c @@ -82,20 +82,27 @@ static const struct mtd_partition partition_info[] = { /* * hardware specific access to control-lines -*/ + * + * ctrl: + * NAND_CNE: bit 0 -> bit 2 + * NAND_CLE: bit 1 -> bit 0 + * NAND_ALE: bit 2 -> bit 1 + */ static void spia_hwcontrol(struct mtd_info *mtd, int cmd) { - switch (cmd) { + struct nand_chip *chip = mtd->priv; - case NAND_CTL_SETCLE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |= 0x01; break; - case NAND_CTL_CLRCLE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x01; break; + if (ctrl & NAND_CTRL_CHANGE) { + void __iomem *addr = spia_io_base + spia_pedr; + unsigned char bits; - case NAND_CTL_SETALE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |= 0x02; break; - case NAND_CTL_CLRALE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x02; break; - - case NAND_CTL_SETNCE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x04; break; - case NAND_CTL_CLRNCE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |= 0x04; break; + bits = (ctrl & NAND_CNE) << 2; + bits |= (ctrl & NAND_CLE | NAND_ALE) >> 1; + writeb((readb(addr) & ~0x7) | bits, addr); } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); } /* @@ -133,7 +140,7 @@ static int __init spia_init(void) this->IO_ADDR_R = (void __iomem *)spia_fio_base; this->IO_ADDR_W = (void __iomem *)spia_fio_base; /* Set address of hardware control function */ - this->hwcontrol = spia_hwcontrol; + this->cmd_ctrl = spia_hwcontrol; /* 15 us command delay time */ this->chip_delay = 15; diff --git a/drivers/mtd/nand/toto.c b/drivers/mtd/nand/toto.c index 50aa6a46911f..a9cf0190c27a 100644 --- a/drivers/mtd/nand/toto.c +++ b/drivers/mtd/nand/toto.c @@ -32,6 +32,8 @@ #include #include +#define CONFIG_NAND_WORKAROUND 1 + /* * MTD structure for TOTO board */ @@ -39,25 +41,6 @@ static struct mtd_info *toto_mtd = NULL; static unsigned long toto_io_base = OMAP_FLASH_1_BASE; -#define CONFIG_NAND_WORKAROUND 1 - -#define NAND_NCE 0x4000 -#define NAND_CLE 0x1000 -#define NAND_ALE 0x0002 -#define NAND_MASK (NAND_CLE | NAND_ALE | NAND_NCE) - -#define T_NAND_CTL_CLRALE(iob) gpiosetout(NAND_ALE, 0) -#define T_NAND_CTL_SETALE(iob) gpiosetout(NAND_ALE, NAND_ALE) -#ifdef CONFIG_NAND_WORKAROUND /* "some" dev boards busted, blue wired to rts2 :( */ -#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0); rts2setout(2, 2) -#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE); rts2setout(2, 0) -#else -#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0) -#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE) -#endif -#define T_NAND_CTL_SETNCE(iob) gpiosetout(NAND_NCE, 0) -#define T_NAND_CTL_CLRNCE(iob) gpiosetout(NAND_NCE, NAND_NCE) - /* * Define partitions for flash devices */ @@ -91,25 +74,43 @@ static struct mtd_partition partition_info32M[] = { #define NUM_PARTITIONS32M 3 #define NUM_PARTITIONS64M 4 + /* * hardware specific access to control-lines -*/ - -static void toto_hwcontrol(struct mtd_info *mtd, int cmd) + * + * ctrl: + * NAND_NCE: bit 0 -> bit 14 (0x4000) + * NAND_CLE: bit 1 -> bit 12 (0x1000) + * NAND_ALE: bit 2 -> bit 1 (0x0002) + */ +static void toto_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) { + struct nand_chip *chip = mtd->priv; + + if (ctrl & NAND_CTRL_CHANGE) { + unsigned long bits; - udelay(1); /* hopefully enough time for tc make proceding write to clear */ - switch (cmd) { - case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break; - case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break; + /* hopefully enough time for tc make proceding write to clear */ + udelay(1); - case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break; - case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break; + bits = (~ctrl & NAND_NCE) << 14; + bits |= (ctrl & NAND_CLE) << 12; + bits |= (ctrl & NAND_ALE) >> 1; - case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break; - case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break; +#warning Wild guess as gpiosetout() is nowhere defined in the kernel source - tglx + gpiosetout(0x5002, bits); + +#ifdef CONFIG_NAND_WORKAROUND + /* "some" dev boards busted, blue wired to rts2 :( */ + rts2setout(2, (ctrl & NAND_CLE) << 1); +#endif + /* allow time to ensure gpio state to over take memory write */ + udelay(1); } - udelay(1); /* allow time to ensure gpio state to over take memory write */ + + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); } /* @@ -142,7 +143,7 @@ static int __init toto_init(void) /* Set address of NAND IO lines */ this->IO_ADDR_R = toto_io_base; this->IO_ADDR_W = toto_io_base; - this->hwcontrol = toto_hwcontrol; + this->cmd_ctrl = toto_hwcontrol; this->dev_ready = NULL; /* 25 us command delay time */ this->chip_delay = 30; diff --git a/drivers/mtd/nand/ts7250.c b/drivers/mtd/nand/ts7250.c index 70bce1b0326c..a0b4b1edcb0d 100644 --- a/drivers/mtd/nand/ts7250.c +++ b/drivers/mtd/nand/ts7250.c @@ -83,31 +83,29 @@ static struct mtd_partition partition_info128[] = { /* * hardware specific access to control-lines + * + * ctrl: + * NAND_NCE: bit 0 -> bit 2 + * NAND_CLE: bit 1 -> bit 1 + * NAND_ALE: bit 2 -> bit 0 */ -static void ts7250_hwcontrol(struct mtd_info *mtd, int cmd) +static void ts7250_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { - unsigned long ctrl = TS72XX_NAND_CONTROL_VIRT_BASE; - - switch (cmd) { - case NAND_CTL_SETCLE: - __raw_writeb(__raw_readb(ctrl) | 0x2, ctrl); - break; - case NAND_CTL_CLRCLE: - __raw_writeb(__raw_readb(ctrl) & ~0x2, ctrl); - break; - case NAND_CTL_SETALE: - __raw_writeb(__raw_readb(ctrl) | 0x1, ctrl); - break; - case NAND_CTL_CLRALE: - __raw_writeb(__raw_readb(ctrl) & ~0x1, ctrl); - break; - case NAND_CTL_SETNCE: - __raw_writeb(__raw_readb(ctrl) | 0x4, ctrl); - break; - case NAND_CTL_CLRNCE: - __raw_writeb(__raw_readb(ctrl) & ~0x4, ctrl); - break; + struct nand_chip *chip = mtd->priv; + + if (ctrl & NAND_CTRL_CHANGE) { + unsigned long addr = TS72XX_NAND_CONTROL_VIRT_BASE; + unsigned char bits; + + bits = (ctrl & NAND_CNE) << 2; + bits |= ctrl & NAND_CLE; + bits |= (ctrl & NAND_ALE) >> 2; + + __raw_writeb((__raw_readb(addr) & ~0x7) | bits, addr); } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); } /* @@ -152,7 +150,7 @@ static int __init ts7250_init(void) /* insert callbacks */ this->IO_ADDR_R = (void *)TS72XX_NAND_DATA_VIRT_BASE; this->IO_ADDR_W = (void *)TS72XX_NAND_DATA_VIRT_BASE; - this->hwcontrol = ts7250_hwcontrol; + this->cmd_ctrl = ts7250_hwcontrol; this->dev_ready = ts7250_device_ready; this->chip_delay = 15; this->ecc.mode = NAND_ECC_SOFT; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 8362b466df3a..e9a935263151 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -50,23 +50,20 @@ extern int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, /* * Constants for hardware specific CLE/ALE/NCE function -*/ + * + * These are bits which can be or'ed to set/clear multiple + * bits in one go. + */ /* Select the chip by setting nCE to low */ -#define NAND_CTL_SETNCE 1 -/* Deselect the chip by setting nCE to high */ -#define NAND_CTL_CLRNCE 2 +#define NAND_NCE 0x01 /* Select the command latch by setting CLE to high */ -#define NAND_CTL_SETCLE 3 -/* Deselect the command latch by setting CLE to low */ -#define NAND_CTL_CLRCLE 4 +#define NAND_CLE 0x02 /* Select the address latch by setting ALE to high */ -#define NAND_CTL_SETALE 5 -/* Deselect the address latch by setting ALE to low */ -#define NAND_CTL_CLRALE 6 -/* Set write protection by setting WP to high. Not used! */ -#define NAND_CTL_SETWP 7 -/* Clear write protection by setting WP to low. Not used! */ -#define NAND_CTL_CLRWP 8 +#define NAND_ALE 0x04 + +#define NAND_CTRL_CLE (NAND_NCE | NAND_CLE) +#define NAND_CTRL_ALE (NAND_NCE | NAND_ALE) +#define NAND_CTRL_CHANGE 0x80 /* * Standard NAND flash commands @@ -106,6 +103,8 @@ extern int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, #define NAND_CMD_STATUS_RESET 0x7f #define NAND_CMD_STATUS_CLEAR 0xff +#define NAND_CMD_NONE -1 + /* Status bits */ #define NAND_STATUS_FAIL 0x01 #define NAND_STATUS_FAIL_N1 0x02 @@ -263,7 +262,8 @@ struct nand_ecc_ctrl { * @select_chip: [REPLACEABLE] select chip nr * @block_bad: [REPLACEABLE] check, if the block is bad * @block_markbad: [REPLACEABLE] mark the block bad - * @hwcontrol: [BOARDSPECIFIC] hardwarespecific function for accesing control-lines + * @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific funtion for controlling + * ALE/CLE/nCE. Also used to write command and address * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line * If set to NULL no access to ready/busy is available and the ready/busy information * is read from the chip status register @@ -317,7 +317,8 @@ struct nand_chip { void (*select_chip)(struct mtd_info *mtd, int chip); int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); - void (*hwcontrol)(struct mtd_info *mtd, int cmd); + void (*cmd_ctrl)(struct mtd_info *mtd, int dat, + unsigned int ctrl); int (*dev_ready)(struct mtd_info *mtd); void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state); -- cgit v1.2.3 From cad74f2c380411ae7bee997f3ba18834cfe313a2 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 23 May 2006 23:28:48 +0200 Subject: [MTD] NAND remove write_byte/word function from nand_chip The previous change of the command / hardware control allows to remove the write_byte/word functions completely, as their only user were nand_command and nand_command_lp. Signed-off-by: Thomas Gleixner --- drivers/mtd/nand/ams-delta.c | 1 - drivers/mtd/nand/au1550nd.c | 31 ++++++++--------------------- drivers/mtd/nand/cs553x_nand.c | 1 - drivers/mtd/nand/diskonchip.c | 11 ++++++----- drivers/mtd/nand/nand_base.c | 45 ------------------------------------------ drivers/mtd/nand/nandsim.c | 13 ------------ include/linux/mtd/nand.h | 5 ----- 7 files changed, 14 insertions(+), 93 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index c0e96860686e..d7897dc6b3c8 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -179,7 +179,6 @@ static int __init ams_delta_init(void) this->IO_ADDR_R = (OMAP_MPUIO_BASE + OMAP_MPUIO_INPUT_LATCH); this->IO_ADDR_W = (OMAP_MPUIO_BASE + OMAP_MPUIO_OUTPUT); this->read_byte = ams_delta_read_byte; - this->write_byte = ams_delta_write_byte; this->write_buf = ams_delta_write_buf; this->read_buf = ams_delta_read_buf; this->verify_buf = ams_delta_verify_buf; diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 275453ea7a71..31228334da12 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -40,6 +40,7 @@ static struct mtd_info *au1550_mtd = NULL; static void __iomem *p_nand; static int nand_width = 1; /* default x8 */ +static void (*au1550_write_byte)(struct mtd_info *, u_char); /* * Define partitions for flash device @@ -128,21 +129,6 @@ static u16 au_read_word(struct mtd_info *mtd) return ret; } -/** - * au_write_word - write one word to the chip - * @mtd: MTD device structure - * @word: data word to write - * - * write function for 16bit buswith without - * endianess conversion - */ -static void au_write_word(struct mtd_info *mtd, u16 word) -{ - struct nand_chip *this = mtd->priv; - writew(word, this->IO_ADDR_W); - au_sync(); -} - /** * au_write_buf - write buffer to chip * @mtd: MTD device structure @@ -379,9 +365,9 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i column -= 256; readcmd = NAND_CMD_READ1; } - this->write_byte(mtd, readcmd); + au1550_write_byte(mtd, readcmd); } - this->write_byte(mtd, command); + au1550_write_byte(mtd, command); /* Set ALE and clear CLE to start address cycle */ au1550_hwcontrol(mtd, NAND_CTL_CLRCLE); @@ -394,10 +380,10 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i /* Adjust columns for 16 bit buswidth */ if (this->options & NAND_BUSWIDTH_16) column >>= 1; - this->write_byte(mtd, column); + au1550_write_byte(mtd, column); } if (page_addr != -1) { - this->write_byte(mtd, (u8)(page_addr & 0xff)); + au1550_write_byte(mtd, (u8)(page_addr & 0xff)); if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || @@ -415,11 +401,11 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i au1550_hwcontrol(mtd, NAND_CTL_SETNCE); } - this->write_byte(mtd, (u8)(page_addr >> 8)); + au1550_write_byte(mtd, (u8)(page_addr >> 8)); /* One more address cycle for devices > 32MiB */ if (this->chipsize > (32 << 20)) - this->write_byte(mtd, (u8)((page_addr >> 16) & 0x0f)); + au1550_write_byte(mtd, (u8)((page_addr >> 16) & 0x0f)); } /* Latch in address */ au1550_hwcontrol(mtd, NAND_CTL_CLRALE); @@ -597,8 +583,7 @@ static int __init au1xxx_nand_init(void) this->options |= NAND_BUSWIDTH_16; this->read_byte = (!nand_width) ? au_read_byte16 : au_read_byte; - this->write_byte = (!nand_width) ? au_write_byte16 : au_write_byte; - this->write_word = au_write_word; + au1550_write_byte = (!nand_width) ? au_write_byte16 : au_write_byte; this->read_word = au_read_word; this->write_buf = (!nand_width) ? au_write_buf16 : au_write_buf; this->read_buf = (!nand_width) ? au_read_buf16 : au_read_buf; diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index cd3d7eb132f9..1e0348ae325f 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -220,7 +220,6 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) this->cmd_ctrl = cs553x_hwcontrol; this->dev_ready = cs553x_device_ready; this->read_byte = cs553x_read_byte; - this->write_byte = cs553x_write_byte; this->read_buf = cs553x_read_buf; this->write_buf = cs553x_write_buf; diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index e4bb6b429f87..2ec9080e2b14 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -717,8 +717,12 @@ static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, /* 11.4.3 -- 4 NOPs after CSDNControl write */ DoC_Delay(doc, 4); } - if (cmd != NAND_CMD_NONE) - this->write_byte(mtd, cmd); + if (cmd != NAND_CMD_NONE) { + if (DoC_is_2000(doc)) + doc2000_write_byte(mtd, cmd); + else + doc2001_write_byte(mtd, cmd); + } } static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) @@ -1435,7 +1439,6 @@ static inline int __init doc2000_init(struct mtd_info *mtd) struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; - this->write_byte = doc2000_write_byte; this->read_byte = doc2000_read_byte; this->write_buf = doc2000_writebuf; this->read_buf = doc2000_readbuf; @@ -1453,7 +1456,6 @@ static inline int __init doc2001_init(struct mtd_info *mtd) struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; - this->write_byte = doc2001_write_byte; this->read_byte = doc2001_read_byte; this->write_buf = doc2001_writebuf; this->read_buf = doc2001_readbuf; @@ -1485,7 +1487,6 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd) struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; - this->write_byte = NULL; this->read_byte = doc2001plus_read_byte; this->write_buf = doc2001plus_writebuf; this->read_buf = doc2001plus_readbuf; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index f6997fb77b91..4f387c8388d7 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -197,19 +197,6 @@ static uint8_t nand_read_byte(struct mtd_info *mtd) return readb(this->IO_ADDR_R); } -/** - * nand_write_byte - [DEFAULT] write one byte to the chip - * @mtd: MTD device structure - * @byte: pointer to data byte to write - * - * Default write function for 8it buswith - */ -static void nand_write_byte(struct mtd_info *mtd, uint8_t byte) -{ - struct nand_chip *this = mtd->priv; - writeb(byte, this->IO_ADDR_W); -} - /** * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip * @mtd: MTD device structure @@ -223,20 +210,6 @@ static uint8_t nand_read_byte16(struct mtd_info *mtd) return (uint8_t) cpu_to_le16(readw(this->IO_ADDR_R)); } -/** - * nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip - * @mtd: MTD device structure - * @byte: pointer to data byte to write - * - * Default write function for 16bit buswith with - * endianess conversion - */ -static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte) -{ - struct nand_chip *this = mtd->priv; - writew(le16_to_cpu((u16) byte), this->IO_ADDR_W); -} - /** * nand_read_word - [DEFAULT] read one word from the chip * @mtd: MTD device structure @@ -250,20 +223,6 @@ static u16 nand_read_word(struct mtd_info *mtd) return readw(this->IO_ADDR_R); } -/** - * nand_write_word - [DEFAULT] write one word to the chip - * @mtd: MTD device structure - * @word: data word to write - * - * Default write function for 16bit buswith without - * endianess conversion - */ -static void nand_write_word(struct mtd_info *mtd, u16 word) -{ - struct nand_chip *this = mtd->priv; - writew(word, this->IO_ADDR_W); -} - /** * nand_select_chip - [DEFAULT] control CE line * @mtd: MTD device structure @@ -2200,12 +2159,8 @@ static void nand_set_defaults(struct nand_chip *this, int busw) if (!this->select_chip) this->select_chip = nand_select_chip; - if (!this->write_byte) - this->write_byte = busw ? nand_write_byte16 : nand_write_byte; if (!this->read_byte) this->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) diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index ecf727b32dec..ebd64abc8be8 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -1326,17 +1326,6 @@ ns_nand_read_word(struct mtd_info *mtd) return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8); } -static void -ns_nand_write_word(struct mtd_info *mtd, uint16_t word) -{ - struct nand_chip *chip = (struct nand_chip *)mtd->priv; - - NS_DBG("write_word\n"); - - chip->write_byte(mtd, word & 0xFF); - chip->write_byte(mtd, word >> 8); -} - static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) { @@ -1467,11 +1456,9 @@ static int __init ns_init_module(void) chip->cmd_ctrl = ns_hwcontrol; chip->read_byte = ns_nand_read_byte; chip->dev_ready = ns_device_ready; - chip->write_byte = ns_nand_write_byte; chip->write_buf = ns_nand_write_buf; chip->read_buf = ns_nand_read_buf; chip->verify_buf = ns_nand_verify_buf; - chip->write_word = ns_nand_write_word; chip->read_word = ns_nand_read_word; chip->ecc.mode = NAND_ECC_SOFT; chip->options |= NAND_SKIP_BBTSCAN; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index e9a935263151..2c0fb6380461 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -253,9 +253,7 @@ struct nand_ecc_ctrl { * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device * @read_byte: [REPLACEABLE] read one byte from the chip - * @write_byte: [REPLACEABLE] write one byte to the chip * @read_word: [REPLACEABLE] read one word from the chip - * @write_word: [REPLACEABLE] write one word to the chip * @write_buf: [REPLACEABLE] write data from the buffer to the chip * @read_buf: [REPLACEABLE] read data from the chip into the buffer * @verify_buf: [REPLACEABLE] verify buffer contents against the chip data @@ -307,10 +305,7 @@ struct nand_chip { void __iomem *IO_ADDR_W; uint8_t (*read_byte)(struct mtd_info *mtd); - void (*write_byte)(struct mtd_info *mtd, uint8_t byte); u16 (*read_word)(struct mtd_info *mtd); - void (*write_word)(struct mtd_info *mtd, u16 word); - void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); -- cgit v1.2.3 From 7a30601b3ac7b02440ffa629fd3d2cca71c1bcd8 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 May 2006 09:50:16 +0200 Subject: [MTD] NAND Introduce NAND_NO_READRDY option The nand driver has a superflous read ready / command delay in the read functions. This was added to handle chips which have an automatic read forward. Newer chips do not have this functionality anymore. Add this option to avoid the delay / I/O operation. Mark all large page chips with the new option flag. Signed-off-by: Thomas Gleixner --- drivers/mtd/nand/nand_ids.c | 165 +++++++++++++++++++++++--------------------- include/linux/mtd/nand.h | 4 ++ 2 files changed, 92 insertions(+), 77 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index a9d52fc6e5d7..2e2cdf2fc91d 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -18,99 +18,110 @@ * Name. ID code, pagesize, chipsize in MegaByte, eraseblock size, * options * -* Pagesize; 0, 256, 512 -* 0 get this information from the extended chip ID +* Pagesize; 0, 256, 512 +* 0 get this information from the extended chip ID + 256 256 Byte page size * 512 512 Byte page size */ struct nand_flash_dev nand_flash_ids[] = { - {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, - {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, - {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, - {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, - {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, - {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, - {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, - {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, - {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, - {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0}, - - {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0}, - {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, - {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, - {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, - - {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, - {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, - {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, - - {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0}, - {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0}, - {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, - - {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0}, - {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}, - {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, - - {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, - {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0}, - {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, - {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16}, - - {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, - - /* These are the new chips with large page size. The pagesize - * and the erasesize is determined from the extended id bytes - */ + {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, + {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, + {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, + {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, + {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0}, + + {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0}, + {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, + {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, + {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, + + {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, + {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, + {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0}, + {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0}, + {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0}, + {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}, + {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, + {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0}, + {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, + {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, + + /* + * These are the new chips with large page size. The pagesize and the + * erasesize is determined from the extended id bytes + */ +#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR) +#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) + /*512 Megabit */ - {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, - {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS}, + {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS}, + {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16}, + {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16}, /* 1 Gigabit */ - {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, - {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS}, + {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS}, + {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16}, + {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16}, /* 2 Gigabit */ - {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, - {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS}, + {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS}, + {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16}, + {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16}, /* 4 Gigabit */ - {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, - {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS}, + {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS}, + {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16}, + {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16}, /* 8 Gigabit */ - {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, - {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS}, + {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS}, + {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16}, + {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16}, /* 16 Gigabit */ - {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, - {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, - {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, - - /* Renesas AND 1 Gigabit. Those chips do not support extended id and have a strange page/block layout ! - * The chosen minimum erasesize is 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page planes - * 1 block = 2 pages, but due to plane arrangement the blocks 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 - * Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go - * There are more speed improvements for reads and writes possible, but not implemented now + {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS}, + {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS}, + {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16}, + {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16}, + + /* + * Renesas AND 1 Gigabit. Those chips do not support extended id and + * have a strange page/block layout ! The chosen minimum erasesize is + * 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page + * planes 1 block = 2 pages, but due to plane arrangement the blocks + * 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 Anyway JFFS2 would + * increase the eraseblock size so we chose a combined one which can be + * erased in one go There are more speed improvements for reads and + * writes possible, but not implemented now */ - {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH}, + {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, + NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY | + BBT_AUTO_REFRESH + }, {NULL,} }; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 2c0fb6380461..2fd85d55803d 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -159,6 +159,10 @@ typedef enum { * bits from adjacent blocks from 'leaking' in altering data. * This happens with the Renesas AG-AND chips, possibly others. */ #define BBT_AUTO_REFRESH 0x00000080 +/* Chip does not require ready check on read. True + * for all large page devices, as they do not support + * autoincrement.*/ +#define NAND_NO_READRDY 0x00000100 /* Options valid for Samsung large page devices */ #define NAND_SAMSUNG_LP_OPTIONS \ -- cgit v1.2.3 From 7fac464868ec5d80019fa549b8b4516dd1dc9d5c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 May 2006 09:57:31 +0200 Subject: [MTD] Add ECC statistics to struct mtd_info FLASH - especially NAND FLASH - will become less reliable and bit flips more likely. Add an ECC statistics struct to struct mtd_info to keep track of this. Signed-off-by: Thomas Gleixner --- include/linux/mtd/mtd.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include') diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index b8ad634391db..41a984dcb139 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -56,6 +56,17 @@ struct mtd_erase_region_info { u_int32_t numblocks; /* Number of blocks of erasesize in this region */ }; +/** + * struct mtd_ecc_stats - error correction status + * + * @corrected: number of corrected bits + * @failed: number of uncorrectable errors + */ +struct mtd_ecc_stats { + unsigned long corrected; + unsigned long failed; +}; + struct mtd_info { u_char type; u_int32_t flags; @@ -153,6 +164,9 @@ struct mtd_info { struct notifier_block reboot_notifier; /* default mode before reboot */ + /* ECC status information */ + struct mtd_ecc_stats ecc_stats; + void *priv; struct module *owner; -- cgit v1.2.3 From 9577f44a899cf4acb9e381c8946307b72153cd15 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 May 2006 10:04:31 +0200 Subject: [MTD] NAND Add read/write function pointers to struct nand_ecc_ctrl Add read/write function pointers to struct nand_ecc_ctrl to prepare the modulaization of nand_read/write functions. The current implementation handles every type of ecc mode software/hardware and all kinds of strange ecc placement schemes in one switch/if construct. Thats too complex to maintain and too inflexible to expand. Modularization will also shorten the code pathes of the read/write functions. Signed-off-by: Thomas Gleixner --- include/linux/mtd/nand.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 2fd85d55803d..daacde5132fe 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -233,16 +233,23 @@ struct nand_hw_control { * @steps: number of ecc steps per page * @size: data bytes per ecc step * @bytes: ecc bytes per step + * @total: total number of ecc bytes per page + * @prepad: padding information for syndrome based ecc generators + * @postpad: padding information for syndrome based ecc generators * @hwctl: function to control hardware ecc generator. Must only * be provided if an hardware ECC is available * @calculate: function for ecc calculation or readback from ecc hardware * @correct: function for ecc correction, matching to ecc generator (sw/hw) + * @write_page: function to write a page according to the ecc generator requirements */ struct nand_ecc_ctrl { nand_ecc_modes_t mode; int steps; int size; int bytes; + int total; + int prepad; + int postpad; void (*hwctl)(struct mtd_info *mtd, int mode); int (*calculate)(struct mtd_info *mtd, const uint8_t *dat, @@ -250,6 +257,12 @@ struct nand_ecc_ctrl { int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc); + int (*read_page)(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf); + int (*write_page)(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, int cached); }; /** -- cgit v1.2.3 From f5bbdacc41939f89d8ccb18dd79cd9b21c0cb75d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 May 2006 10:07:16 +0200 Subject: [MTD] NAND Modularize read function Split the core of the read function out and implement seperate handling functions for software and hardware ECC. Signed-off-by: Thomas Gleixner --- drivers/mtd/nand/diskonchip.c | 4 +- drivers/mtd/nand/nand_base.c | 448 +++++++++++++++++++++--------------------- drivers/mtd/nand/rtc_from4.c | 62 +++--- include/linux/mtd/nand.h | 16 +- 4 files changed, 275 insertions(+), 255 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 2ec9080e2b14..83af6f05cd00 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -968,12 +968,14 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsign return 0; } -static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) +static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *isnull) { int i, ret = 0; struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; + uint8_t calc_ecc[6]; volatile u_char dummy; int emptymatch = 1; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 49bca242610b..21fce2bce4b2 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -976,256 +976,224 @@ static int nand_verify_pages(struct mtd_info *mtd, struct nand_chip *chip, int p #endif /** - * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc - * @mtd: MTD device structure - * @from: offset to read from - * @len: number of bytes to read - * @retlen: pointer to variable to store the number of read bytes - * @buf: the databuffer to put data - * - * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL - * and flags = 0xff + * nand_read_page_swecc - {REPLACABLE] software ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data */ -static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf) +static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) { - return nand_do_read_ecc(mtd, from, len, retlen, buf, NULL, &mtd->oobinfo, 0xff); + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->oob_buf + mtd->oobsize; + uint8_t *ecc_code = ecc_calc + mtd->oobsize; + int *eccpos = chip->autooob->eccpos; + + chip->read_buf(mtd, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_buf, mtd->oobsize); + + if (chip->ecc.mode == NAND_ECC_NONE) + return 0; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_buf[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat == -1) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; } /** - * nand_do_read_ecc - [MTD Interface] Read data with ECC - * @mtd: MTD device structure - * @from: offset to read from - * @len: number of bytes to read - * @retlen: pointer to variable to store the number of read bytes - * @buf: the databuffer to put data - * @oob_buf: filesystem supplied oob data buffer (can be NULL) - * @oobsel: oob selection structure - * @flags: flag to indicate if nand_get_device/nand_release_device should be preformed - * and how many corrected error bits are acceptable: - * bits 0..7 - number of tolerable errors - * bit 8 - 0 == do not get/release chip, 1 == get/release chip + * nand_read_page_hwecc - {REPLACABLE] hardware ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data * - * NAND read with ECC + * Not for syndrome calculating ecc controllers which need a special oob layout */ -int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, uint8_t *buf, uint8_t *oob_buf, struct nand_oobinfo *oobsel, int flags) +static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) { - - int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1; - int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; - struct nand_chip *chip = mtd->priv; - uint8_t *data_poi, *oob_data = oob_buf; - uint8_t ecc_calc[32]; - uint8_t ecc_code[32]; - int eccmode, eccsteps; - int *oob_config, datidx; - int blockcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; - int eccbytes; - int compareecc = 1; - int oobreadlen; - - 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; + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->oob_buf + mtd->oobsize; + uint8_t *ecc_code = ecc_calc + mtd->oobsize; + int *eccpos = chip->autooob->eccpos; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); } + chip->read_buf(mtd, chip->oob_buf, mtd->oobsize); - /* Grab the lock and see if the device is available */ - if (flags & NAND_GET_DEVICE) - nand_get_device(chip, mtd, FL_READING); + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_buf[eccpos[i]]; - /* Autoplace of oob data ? Use the default placement scheme */ - if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) - oobsel = chip->autooob; + eccsteps = chip->ecc.steps; + p = buf; - eccmode = oobsel->useecc ? chip->ecc.mode : NAND_ECC_NONE; - oob_config = oobsel->eccpos; + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; - /* Select the NAND device */ - chipnr = (int)(from >> chip->chip_shift); - chip->select_chip(mtd, chipnr); - - /* First we calculate the starting page */ - realpage = (int)(from >> chip->page_shift); - page = realpage & chip->pagemask; + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat == -1) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} - /* Get raw starting column */ - col = from & (mtd->writesize - 1); +/** + * nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * + * The hw generator calculates the error syndrome automatically. Therefor + * we need a special oob layout and . + */ +static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *oob = chip->oob_buf; - end = mtd->writesize; - ecc = chip->ecc.size; - eccbytes = chip->ecc.bytes; + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; - if ((eccmode == NAND_ECC_NONE) || (chip->options & NAND_HWECC_SYNDROME)) - compareecc = 0; + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); - oobreadlen = mtd->oobsize; - if (chip->options & NAND_HWECC_SYNDROME) - oobreadlen -= oobsel->eccbytes; + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } - /* Loop until all data read */ - while (read < len) { + chip->ecc.hwctl(mtd, NAND_ECC_READSYN); + chip->read_buf(mtd, oob, eccbytes); + stat = chip->ecc.correct(mtd, p, oob, NULL); - int aligned = (!col && (len - read) >= end); - /* - * 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 (aligned) - data_poi = &buf[read]; + if (stat == -1) + mtd->ecc_stats.failed++; else - data_poi = chip->data_buf; + mtd->ecc_stats.corrected += stat; - /* Check, if we have this page in the buffer - * - * FIXME: Make it work when we must provide oob data too, - * check the usage of data_buf oob field - */ - if (realpage == chip->pagebuf && !oob_buf) { - /* aligned read ? */ - if (aligned) - memcpy(data_poi, chip->data_buf, end); - goto readdata; - } + oob += eccbytes; - /* Check, if we must send the read command */ - if (sndcmd) { - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); - sndcmd = 0; + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; } + } - /* get oob area, if we have no oob buffer from fs-driver */ - if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE || - oobsel->useecc == MTD_NANDECC_AUTOPL_USR) - oob_data = &chip->data_buf[end]; - - eccsteps = chip->ecc.steps; - - switch (eccmode) { - case NAND_ECC_NONE:{ - /* No ECC, Read in a page */ - static unsigned long lastwhinge = 0; - if ((lastwhinge / HZ) != (jiffies / HZ)) { - printk(KERN_WARNING - "Reading data from NAND FLASH without ECC is not recommended\n"); - lastwhinge = jiffies; - } - chip->read_buf(mtd, data_poi, end); - break; - } + /* Calculate remaining oob bytes */ + i = oob - chip->oob_buf; + if (i) + chip->read_buf(mtd, oob, i); - case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ - chip->read_buf(mtd, data_poi, end); - for (i = 0, datidx = 0; eccsteps; eccsteps--, i += 3, datidx += ecc) - chip->ecc.calculate(mtd, &data_poi[datidx], &ecc_calc[i]); - break; + return 0; +} - default: - for (i = 0, datidx = 0; eccsteps; eccsteps--, i += eccbytes, datidx += ecc) { - chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, &data_poi[datidx], ecc); - - /* HW ecc with syndrome calculation must read the - * syndrome from flash immidiately after the data */ - if (!compareecc) { - /* Some hw ecc generators need to know when the - * syndrome is read from flash */ - chip->ecc.hwctl(mtd, NAND_ECC_READSYN); - chip->read_buf(mtd, &oob_data[i], eccbytes); - /* We calc error correction directly, it checks the hw - * generator for an error, reads back the syndrome and - * does the error correction on the fly */ - ecc_status = chip->ecc.correct(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]); - if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_ecc: " - "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); - ecc_failed++; - } - } else { - chip->ecc.calculate(mtd, &data_poi[datidx], &ecc_calc[i]); - } - } - break; - } +/** + * nand_do_read - [Internal] Read data with ECC + * + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * + * Internal function. Called with chip held. + */ +int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, uint8_t *buf) +{ + int chipnr, page, realpage, col, bytes, aligned; + struct nand_chip *chip = mtd->priv; + struct mtd_ecc_stats stats; + int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + int sndcmd = 1; + int ret = 0; + uint32_t readlen = len; + uint8_t *bufpoi; - /* read oobdata */ - chip->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen); + stats = mtd->ecc_stats; - /* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */ - if (!compareecc) - goto readoob; + chipnr = (int)(from >> chip->chip_shift); + chip->select_chip(mtd, chipnr); - /* Pick the ECC bytes out of the oob data */ - for (j = 0; j < oobsel->eccbytes; j++) - ecc_code[j] = oob_data[oob_config[j]]; + realpage = (int)(from >> chip->page_shift); + page = realpage & chip->pagemask; - /* correct data, if necessary */ - for (i = 0, j = 0, datidx = 0; i < chip->ecc.steps; i++, datidx += ecc) { - ecc_status = chip->ecc.correct(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]); + col = (int)(from & (mtd->writesize - 1)); - /* Get next chunk of ecc bytes */ - j += eccbytes; + while(1) { + bytes = min(mtd->writesize - col, readlen); + aligned = (bytes == mtd->writesize); - /* Check, if we have a fs supplied oob-buffer, - * This is the legacy mode. Used by YAFFS1 - * Should go away some day - */ - if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) { - int *p = (int *)(&oob_data[mtd->oobsize]); - p[i] = ecc_status; - } + /* Is the current page in the buffer ? */ + if (realpage != chip->pagebuf) { + bufpoi = aligned ? buf : chip->data_buf; - if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); - ecc_failed++; + if (likely(sndcmd)) { + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + sndcmd = 0; } - } - readoob: - /* check, if we have a fs supplied oob-buffer */ - if (oob_buf) { - /* without autoplace. Legacy mode used by YAFFS1 */ - switch (oobsel->useecc) { - case MTD_NANDECC_AUTOPLACE: - case MTD_NANDECC_AUTOPL_USR: - /* Walk through the autoplace chunks */ - for (i = 0; oobsel->oobfree[i][1]; i++) { - int from = oobsel->oobfree[i][0]; - int num = oobsel->oobfree[i][1]; - memcpy(&oob_buf[oob], &oob_data[from], num); - oob += num; - } + /* Now read the page into the buffer */ + ret = chip->ecc.read_page(mtd, chip, bufpoi); + if (ret < 0) break; - case MTD_NANDECC_PLACE: - /* YAFFS1 legacy mode */ - oob_data += chip->ecc.steps * sizeof(int); - default: - oob_data += mtd->oobsize; + + /* Transfer not aligned data */ + if (!aligned) { + chip->pagebuf = realpage; + memcpy(buf, chip->data_buf + col, bytes); + } + + if (!(chip->options & NAND_NO_READRDY)) { + /* + * Apply delay or wait for ready/busy pin. Do + * this before the AUTOINCR check, so no + * problems arise if a chip which does auto + * increment is marked as NOAUTOINCR by the + * board driver. + */ + if (!chip->dev_ready) + udelay(chip->chip_delay); + else + nand_wait_ready(mtd); } - } - readdata: - /* Partial page read, transfer data into fs buffer */ - if (!aligned) { - for (j = col; j < end && read < len; j++) - buf[read++] = data_poi[j]; - chip->pagebuf = realpage; } else - read += mtd->writesize; + memcpy(buf, chip->data_buf + col, bytes); - /* Apply delay or wait for ready/busy pin - * Do this before the AUTOINCR check, so no problems - * arise if a chip which does auto increment - * is marked as NOAUTOINCR by the board driver. - */ - if (!chip->dev_ready) - udelay(chip->chip_delay); - else - nand_wait_ready(mtd); + buf += bytes; + readlen -= bytes; - if (read == len) + if (!readlen) break; /* For subsequent reads align to page boundary. */ @@ -1240,24 +1208,51 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); } + /* Check, if the chip supports auto page increment * or if we have hit a block boundary. */ - if (!NAND_CANAUTOINCR(chip) || !(page & blockcheck)) + if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) sndcmd = 1; } - /* Deselect and wake up anyone waiting on the device */ - if (flags & NAND_GET_DEVICE) - nand_release_device(mtd); + *retlen = len - (size_t) readlen; - /* - * Return success, if no ECC failures, else -EBADMSG - * fs driver will take care of that, because - * retlen == desired len and result == -EBADMSG - */ - *retlen = read; - return ecc_failed ? -EBADMSG : 0; + if (ret) + return ret; + + return mtd->ecc_stats.failed - stats.failed ? -EBADMSG : 0; +} + +/** + * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * + * Get hold of the chip and call nand_do_read + */ +static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, uint8_t *buf) +{ + int ret; + + *retlen = 0; + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) + return -EINVAL; + if (!len) + return 0; + + nand_get_device(mtd->priv, mtd, FL_READING); + + ret = nand_do_read(mtd, from, len, retlen, buf); + + nand_release_device(mtd); + + return ret; } /** @@ -2417,6 +2412,10 @@ int nand_scan(struct mtd_info *mtd, int maxchips) */ switch (chip->ecc.mode) { case NAND_ECC_HW: + /* Use standard hwecc read page function ? */ + if (!chip->ecc.read_page) + chip->ecc.read_page = nand_read_page_hwecc; + case NAND_ECC_HW_SYNDROME: if (!chip->ecc.calculate || !chip->ecc.correct || !chip->ecc.hwctl) { @@ -2424,6 +2423,10 @@ int nand_scan(struct mtd_info *mtd, int maxchips) "Hardware ECC not possible\n"); BUG(); } + /* Use standard syndrome read page function ? */ + if (!chip->ecc.read_page) + chip->ecc.read_page = nand_read_page_syndrome; + if (mtd->writesize >= chip->ecc.size) break; printk(KERN_WARNING "%d byte HW ECC not possible on " @@ -2434,6 +2437,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) case NAND_ECC_SOFT: chip->ecc.calculate = nand_calculate_ecc; chip->ecc.correct = nand_correct_data; + chip->ecc.read_page = nand_read_page_swecc; chip->ecc.size = 256; chip->ecc.bytes = 3; break; @@ -2441,6 +2445,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) case NAND_ECC_NONE: printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " "This is not recommended !!\n"); + chip->ecc.read_page = nand_read_page_swecc; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; break; @@ -2459,6 +2464,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) printk(KERN_WARNING "Invalid ecc parameters\n"); BUG(); } + chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; /* Initialize state */ chip->state = FL_READY; diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index 6c97bfaea19a..b7083104a05b 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -444,7 +444,8 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha * note: see pages 34..37 of data sheet for details. * */ -static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page) +static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, + int state, int status, int page) { int er_stat = 0; int rtn, retlen; @@ -455,39 +456,50 @@ static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int s this->cmdfunc(mtd, NAND_CMD_STATUS_CLEAR, -1, -1); if (state == FL_ERASING) { + for (i = 0; i < 4; i++) { - if (status & 1 << (i + 1)) { - this->cmdfunc(mtd, (NAND_CMD_STATUS_ERROR + i + 1), -1, -1); - rtn = this->read_byte(mtd); - this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1); - if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { - er_stat |= 1 << (i + 1); /* err_ecc_not_avail */ - } - } + if (!(status & 1 << (i + 1))) + continue; + this->cmdfunc(mtd, (NAND_CMD_STATUS_ERROR + i + 1), + -1, -1); + rtn = this->read_byte(mtd); + this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1); + + /* err_ecc_not_avail */ + if (!(rtn & ERR_STAT_ECC_AVAILABLE)) + er_stat |= 1 << (i + 1); } + } else if (state == FL_WRITING) { + + unsigned long corrected = mtd->ecc_stats.corrected; + /* single bank write logic */ this->cmdfunc(mtd, NAND_CMD_STATUS_ERROR, -1, -1); rtn = this->read_byte(mtd); this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1); + if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { - er_stat |= 1 << 1; /* err_ecc_not_avail */ - } else { - len = mtd->writesize; - buf = kmalloc(len, GFP_KERNEL); - if (!buf) { - printk(KERN_ERR "rtc_from4_errstat: Out of memory!\n"); - er_stat = 1; /* if we can't check, assume failed */ - } else { - /* recovery read */ - /* page read */ - rtn = nand_do_read_ecc(mtd, page, len, &retlen, buf, NULL, this->autooob, 1); - if (rtn) { /* if read failed or > 1-bit error corrected */ - er_stat |= 1 << 1; /* ECC read failed */ - } - kfree(buf); - } + /* err_ecc_not_avail */ + er_stat |= 1 << 1; + goto out; } + + len = mtd->writesize; + buf = kmalloc(len, GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "rtc_from4_errstat: Out of memory!\n"); + er_stat = 1; + goto out; + } + + /* recovery read */ + rtn = nand_do_read(mtd, page, len, &retlen, buf); + + /* if read failed or > 1-bit error corrected */ + if (rtn || (mtd->ecc_stats.corrected - corrected) > 1) { + er_stat |= 1 << 1; + kfree(buf); } rtn = status; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index daacde5132fe..00916498ea55 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -479,14 +479,14 @@ struct nand_bbt_descr { /* The maximum number of blocks to scan for a bbt */ #define NAND_BBT_SCAN_MAXBLOCKS 4 -extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd); -extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs); -extern int nand_default_bbt (struct mtd_info *mtd); -extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt); -extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt); -extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, - size_t * retlen, uint8_t * buf, uint8_t * oob_buf, - struct nand_oobinfo *oobsel, int flags); +extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd); +extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs); +extern int nand_default_bbt(struct mtd_info *mtd); +extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt); +extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, + int allowbbt); +extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, uint8_t * buf); /* * Constants for oob configuration -- cgit v1.2.3 From f75e5097ef298c5a0aa106faa211d1afdc92dc3d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 26 May 2006 18:52:08 +0200 Subject: [MTD] NAND modularize write function Modularize the write function and reorganaize the internal buffer management. Remove obsolete chip options and fixup all affected users. Signed-off-by: Thomas Gleixner --- drivers/mtd/nand/diskonchip.c | 2 +- drivers/mtd/nand/edb7312.c | 3 - drivers/mtd/nand/nand_base.c | 775 ++++++++++++++---------------------------- drivers/mtd/nand/nand_bbt.c | 2 +- drivers/mtd/nand/rtc_from4.c | 1 - drivers/mtd/nand/toto.c | 2 - include/linux/mtd/nand.h | 71 ++-- 7 files changed, 308 insertions(+), 548 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 83af6f05cd00..82262a4a4208 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -1666,7 +1666,7 @@ static int __init doc_probe(unsigned long physadr) nand->ecc.mode = NAND_ECC_HW_SYNDROME; nand->ecc.size = 512; nand->ecc.bytes = 6; - nand->options = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME; + nand->options = NAND_USE_FLASH_BBT; doc->physadr = physadr; doc->virtadr = virtadr; diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c index ba5a2174a408..516c0e5e564c 100644 --- a/drivers/mtd/nand/edb7312.c +++ b/drivers/mtd/nand/edb7312.c @@ -198,9 +198,6 @@ static void __exit ep7312_cleanup(void) /* Release resources, unregister device */ nand_release(ap7312_mtd); - /* Free internal data buffer */ - kfree(this->data_buf); - /* Free the MTD device structure */ kfree(ep7312_mtd); } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 2b29b47e2af4..cead9fc4f99f 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -88,37 +88,8 @@ static uint8_t ffchars[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; -/* - * NAND low-level MTD interface functions - */ -static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len); -static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len); -static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len); - -static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, uint8_t *buf); -static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, uint8_t *buf); -static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf); static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf); -static int nand_erase(struct mtd_info *mtd, struct erase_info *instr); -static void nand_sync(struct mtd_info *mtd); - -/* Some internal functions */ -static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - int page, uint8_t * oob_buf, - struct nand_oobinfo *oobsel, int mode); -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE -static int nand_verify_pages(struct mtd_info *mtd, struct nand_chip *chip, - int page, int numpages, uint8_t *oob_buf, - struct nand_oobinfo *oobsel, int chipnr, - int oobmode); -#else -#define nand_verify_pages(...) (0) -#endif - static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state); @@ -262,7 +233,6 @@ static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) for (i = 0; i < len; i++) if (buf[i] != readb(chip->IO_ADDR_R)) return -EFAULT; - return 0; } @@ -766,215 +736,6 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip, int state) return status; } -/** - * nand_write_page - [GENERIC] write one page - * @mtd: MTD device structure - * @this: NAND chip structure - * @page: startpage inside the chip, must be called with (page & chip->pagemask) - * @oob_buf: out of band data buffer - * @oobsel: out of band selecttion structre - * @cached: 1 = enable cached programming if supported by chip - * - * 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 :) - * - * Cached programming is not supported yet. - */ -static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, int page, - uint8_t *oob_buf, struct nand_oobinfo *oobsel, int cached) -{ - int i, status; - uint8_t ecc_code[32]; - int eccmode = oobsel->useecc ? chip->ecc.mode : NAND_ECC_NONE; - int *oob_config = oobsel->eccpos; - int datidx = 0, eccidx = 0, eccsteps = chip->ecc.steps; - int eccbytes = 0; - - /* FIXME: Enable cached programming */ - cached = 0; - - /* Send command to begin auto page programming */ - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - - /* Write out complete page of data, take care of eccmode */ - switch (eccmode) { - /* No ecc, write all */ - case NAND_ECC_NONE: - printk(KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); - chip->write_buf(mtd, chip->data_poi, mtd->writesize); - break; - - /* Software ecc 3/256, write all */ - case NAND_ECC_SOFT: - for (; eccsteps; eccsteps--) { - chip->ecc.calculate(mtd, &chip->data_poi[datidx], ecc_code); - for (i = 0; i < 3; i++, eccidx++) - oob_buf[oob_config[eccidx]] = ecc_code[i]; - datidx += chip->ecc.size; - } - chip->write_buf(mtd, chip->data_poi, mtd->writesize); - break; - default: - eccbytes = chip->ecc.bytes; - for (; eccsteps; eccsteps--) { - /* enable hardware ecc logic for write */ - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - chip->write_buf(mtd, &chip->data_poi[datidx], chip->ecc.size); - chip->ecc.calculate(mtd, &chip->data_poi[datidx], ecc_code); - for (i = 0; i < eccbytes; i++, eccidx++) - oob_buf[oob_config[eccidx]] = ecc_code[i]; - /* If the hardware ecc provides syndromes then - * the ecc code must be written immidiately after - * the data bytes (words) */ - if (chip->options & NAND_HWECC_SYNDROME) - chip->write_buf(mtd, ecc_code, eccbytes); - datidx += chip->ecc.size; - } - break; - } - - /* Write out OOB data */ - if (chip->options & NAND_HWECC_SYNDROME) - chip->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes); - else - chip->write_buf(mtd, oob_buf, mtd->oobsize); - - /* Send command to actually program the data */ - chip->cmdfunc(mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1); - - if (!cached) { - /* call wait ready function */ - status = chip->waitfunc(mtd, chip, FL_WRITING); - - /* See if operation failed and additional status checks are available */ - if ((status & NAND_STATUS_FAIL) && (chip->errstat)) { - status = chip->errstat(mtd, chip, FL_WRITING, status, page); - } - - /* See if device thinks it succeeded */ - if (status & NAND_STATUS_FAIL) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); - return -EIO; - } - } else { - /* FIXME: Implement cached programming ! */ - /* wait until cache is ready */ - // status = chip->waitfunc (mtd, this, FL_CACHEDRPG); - } - return 0; -} - -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE -/** - * nand_verify_pages - [GENERIC] verify the chip contents after a write - * @mtd: MTD device structure - * @this: NAND chip structure - * @page: startpage inside the chip, must be called with (page & chip->pagemask) - * @numpages: number of pages to verify - * @oob_buf: out of band data buffer - * @oobsel: out of band selecttion structre - * @chipnr: number of the current chip - * @oobmode: 1 = full buffer verify, 0 = ecc only - * - * 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. - */ -static int nand_verify_pages(struct mtd_info *mtd, struct nand_chip *chip, int page, int numpages, - uint8_t *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode) -{ - int i, j, datidx = 0, oobofs = 0, res = -EIO; - int eccsteps = chip->ecc.steps; - int hweccbytes; - uint8_t oobdata[64]; - - hweccbytes = (chip->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0; - - /* Send command to read back the first page */ - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); - - for (;;) { - for (j = 0; j < eccsteps; j++) { - /* Loop through and verify the data */ - if (chip->verify_buf(mtd, &chip->data_poi[datidx], mtd->eccsize)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); - goto out; - } - datidx += mtd->eccsize; - /* Have we a hw generator layout ? */ - if (!hweccbytes) - continue; - if (chip->verify_buf(mtd, &chip->oob_buf[oobofs], hweccbytes)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); - goto out; - } - oobofs += hweccbytes; - } - - /* check, if we must compare all data or if we just have to - * compare the ecc bytes - */ - if (oobmode) { - if (chip->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); - goto out; - } - } else { - /* Read always, else autoincrement fails */ - chip->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps); - - if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) { - int ecccnt = oobsel->eccbytes; - - for (i = 0; i < ecccnt; i++) { - int idx = oobsel->eccpos[i]; - if (oobdata[idx] != oob_buf[oobofs + idx]) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Failed ECC write verify, page 0x%08x, %6i bytes were succesful\n", - __FUNCTION__, page, i); - goto out; - } - } - } - } - oobofs += mtd->oobsize - hweccbytes * eccsteps; - page++; - numpages--; - - /* Apply delay or wait for ready/busy pin - * Do this before the AUTOINCR check, so no problems - * arise if a chip which does auto increment - * is marked as NOAUTOINCR by the board driver. - * Do this also before returning, so the chip is - * ready for the next command. - */ - if (!chip->dev_ready) - udelay(chip->chip_delay); - else - nand_wait_ready(mtd); - - /* All done, return happy */ - if (!numpages) - return 0; - - /* Check, if the chip supports auto page increment */ - if (!NAND_CANAUTOINCR(chip)) - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); - } - /* - * Terminate the read command. We come here in case of an error - * So we must issue a reset command. - */ - out: - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - return res; -} -#endif - /** * nand_read_page_swecc - {REPLACABLE] software ecc based page read function * @mtd: mtd info structure @@ -988,12 +749,12 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; - uint8_t *ecc_calc = chip->oob_buf + mtd->oobsize; - uint8_t *ecc_code = ecc_calc + mtd->oobsize; + uint8_t *ecc_calc = chip->buffers.ecccalc; + uint8_t *ecc_code = chip->buffers.ecccode; int *eccpos = chip->autooob->eccpos; chip->read_buf(mtd, buf, mtd->writesize); - chip->read_buf(mtd, chip->oob_buf, mtd->oobsize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); if (chip->ecc.mode == NAND_ECC_NONE) return 0; @@ -1002,7 +763,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, chip->ecc.calculate(mtd, p, &ecc_calc[i]); for (i = 0; i < chip->ecc.total; i++) - ecc_code[i] = chip->oob_buf[eccpos[i]]; + ecc_code[i] = chip->oob_poi[eccpos[i]]; eccsteps = chip->ecc.steps; p = buf; @@ -1034,8 +795,8 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; - uint8_t *ecc_calc = chip->oob_buf + mtd->oobsize; - uint8_t *ecc_code = ecc_calc + mtd->oobsize; + uint8_t *ecc_calc = chip->buffers.ecccalc; + uint8_t *ecc_code = chip->buffers.ecccode; int *eccpos = chip->autooob->eccpos; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { @@ -1043,10 +804,10 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, chip->read_buf(mtd, p, eccsize); chip->ecc.calculate(mtd, p, &ecc_calc[i]); } - chip->read_buf(mtd, chip->oob_buf, mtd->oobsize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); for (i = 0; i < chip->ecc.total; i++) - ecc_code[i] = chip->oob_buf[eccpos[i]]; + ecc_code[i] = chip->oob_poi[eccpos[i]]; eccsteps = chip->ecc.steps; p = buf; @@ -1070,7 +831,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * @buf: buffer to store read data * * The hw generator calculates the error syndrome automatically. Therefor - * we need a special oob layout and . + * we need a special oob layout and handling. */ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf) @@ -1079,7 +840,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; - uint8_t *oob = chip->oob_buf; + uint8_t *oob = chip->oob_poi; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; @@ -1110,7 +871,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, } /* Calculate remaining oob bytes */ - i = oob - chip->oob_buf; + i = oob - chip->oob_poi; if (i) chip->read_buf(mtd, oob, i); @@ -1149,6 +910,7 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, page = realpage & chip->pagemask; col = (int)(from & (mtd->writesize - 1)); + chip->oob_poi = chip->buffers.oobrbuf; while(1) { bytes = min(mtd->writesize - col, readlen); @@ -1156,7 +918,7 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, /* Is the current page in the buffer ? */ if (realpage != chip->pagebuf) { - bufpoi = aligned ? buf : chip->data_buf; + bufpoi = aligned ? buf : chip->buffers.databuf; if (likely(sndcmd)) { chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); @@ -1171,7 +933,7 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, /* Transfer not aligned data */ if (!aligned) { chip->pagebuf = realpage; - memcpy(buf, chip->data_buf + col, bytes); + memcpy(buf, chip->buffers.databuf + col, bytes); } if (!(chip->options & NAND_NO_READRDY)) { @@ -1188,7 +950,7 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, nand_wait_ready(mtd); } } else - memcpy(buf, chip->data_buf + col, bytes); + memcpy(buf, chip->buffers.databuf + col, bytes); buf += bytes; readlen -= bytes; @@ -1392,10 +1154,11 @@ int nand_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, blockcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; while (len) { - if (sndcmd) + if (likely(sndcmd)) { chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page & chip->pagemask); - sndcmd = 0; + sndcmd = 0; + } chip->read_buf(mtd, &buf[cnt], pagesize); @@ -1403,10 +1166,12 @@ int nand_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, cnt += pagesize; page++; - if (!chip->dev_ready) - udelay(chip->chip_delay); - else - nand_wait_ready(mtd); + if (!(chip->options & NAND_NO_READRDY)) { + if (!chip->dev_ready) + udelay(chip->chip_delay); + else + nand_wait_ready(mtd); + } /* * Check, if the chip supports auto page increment or if we @@ -1422,112 +1187,156 @@ int nand_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, } /** - * nand_write_raw - [GENERIC] Write raw data including oob - * @mtd: MTD device structure - * @buf: source buffer - * @to: offset to write to - * @len: number of bytes to write - * @buf: source buffer - * @oob: oob buffer - * - * Write raw data including oob + * nand_write_page_swecc - {REPLACABLE] software ecc based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer */ -int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, - uint8_t *buf, uint8_t *oob) +static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) { - struct nand_chip *chip = mtd->priv; - int page = (int)(to >> chip->page_shift); - int chipnr = (int)(to >> chip->chip_shift); - int ret; + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->buffers.ecccalc; + const uint8_t *p = buf; + int *eccpos = chip->autooob->eccpos; - *retlen = 0; + if (chip->ecc.mode != NAND_ECC_NONE) { + /* Software ecc calculation */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); - /* Do not allow writes past end of device */ - if ((to + len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt write " - "beyond end of device\n"); - return -EINVAL; + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; } - /* Grab the lock and see if the device is available */ - nand_get_device(chip, mtd, FL_WRITING); + chip->write_buf(mtd, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +} - chip->select_chip(mtd, chipnr); - chip->data_poi = buf; +/** + * nand_write_page_hwecc - {REPLACABLE] hardware ecc based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + */ +static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->buffers.ecccalc; + const uint8_t *p = buf; + int *eccpos = chip->autooob->eccpos; - while (len != *retlen) { - ret = nand_write_page(mtd, chip, page, oob, &mtd->oobinfo, 0); - if (ret) - return ret; - page++; - *retlen += mtd->writesize; - chip->data_poi += mtd->writesize; - oob += mtd->oobsize; + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, mtd->writesize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); } - /* Deselect and wake up anyone waiting on the device */ - nand_release_device(mtd); - return 0; + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); } -EXPORT_SYMBOL_GPL(nand_write_raw); /** - * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer - * @mtd: MTD device structure - * @fsbuf: buffer given by fs driver - * @oobsel: out of band selection structre - * @autoplace: 1 = place given buffer into the oob bytes - * @numpages: number of pages to prepare - * - * Return: - * 1. Filesystem buffer available and autoplacement is off, - * return filesystem buffer - * 2. No filesystem buffer or autoplace is off, return internal - * buffer - * 3. Filesystem buffer is given and autoplace selected - * put data from fs buffer into internal buffer and - * retrun internal buffer - * - * Note: The internal buffer is filled with 0xff. This must - * be done only once, when no autoplacement happens - * Autoplacement sets the buffer dirty flag, which - * forces the 0xff fill before using the buffer again. + * nand_write_page_syndrome - {REPLACABLE] hardware ecc syndrom based page write + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer * -*/ -static uint8_t *nand_prepare_oobbuf(struct mtd_info *mtd, uint8_t *fsbuf, struct nand_oobinfo *oobsel, - int autoplace, int numpages) + * The hw generator calculates the error syndrome automatically. Therefor + * we need a special oob layout and handling. + */ +static void nand_write_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) { - struct nand_chip *chip = mtd->priv; - int i, len, ofs; + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + const uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; - /* Zero copy fs supplied buffer */ - if (fsbuf && !autoplace) - return fsbuf; + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - /* Check, if the buffer must be filled with ff again */ - if (chip->oobdirty) { - memset(chip->oob_buf, 0xff, mtd->oobsize << (chip->phys_erase_shift - chip->page_shift)); - chip->oobdirty = 0; - } + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); - /* If we have no autoplacement or no fs buffer use the internal one */ - if (!autoplace || !fsbuf) - return chip->oob_buf; - - /* Walk through the pages and place the data */ - chip->oobdirty = 1; - ofs = 0; - while (numpages--) { - for (i = 0, len = 0; len < mtd->oobavail; i++) { - int to = ofs + oobsel->oobfree[i][0]; - int num = oobsel->oobfree[i][1]; - memcpy(&chip->oob_buf[to], fsbuf, num); - len += num; - fsbuf += num; + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->ecc.calculate(mtd, p, oob); + chip->write_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; } - ofs += mtd->oobavail; } - return chip->oob_buf; + + /* Calculate remaining oob bytes */ + i = oob - chip->oob_poi; + if (i) + chip->write_buf(mtd, oob, i); +} + +/** + * nand_write_page - [INTERNAL] write one page + * @mtd: MTD device structure + * @chip: NAND chip descriptor + * @buf: the data to write + * @page: page number to write + * @cached: cached programming + */ +static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int page, int cached) +{ + int status; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + + chip->ecc.write_page(mtd, chip, buf); + + /* + * Cached progamming disabled for now, Not sure if its worth the + * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) + */ + cached = 0; + + if (!cached || !(chip->options & NAND_CACHEPRG)) { + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip, FL_WRITING); + /* + * See if operation failed and additional status checks are + * available + */ + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_WRITING, status, + page); + + if (status & NAND_STATUS_FAIL) + return -EIO; + } else { + chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); + status = chip->waitfunc(mtd, chip, FL_WRITING); + } + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* Send command to read back the data */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + if (chip->verify_buf(mtd, buf, mtd->writesize)) + return -EIO; +#endif + return 0; } #define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0 @@ -1545,137 +1354,128 @@ static uint8_t *nand_prepare_oobbuf(struct mtd_info *mtd, uint8_t *fsbuf, struct static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf) { - int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr; - int autoplace = 0, numpages, totalpages; + int chipnr, realpage, page, blockmask; struct nand_chip *chip = mtd->priv; - uint8_t *oobbuf, *bufstart, *eccbuf = NULL; - int ppblock = (1 << (chip->phys_erase_shift - chip->page_shift)); - struct nand_oobinfo *oobsel = &mtd->oobinfo; - - DEBUG(MTD_DEBUG_LEVEL3, "nand_write: to = 0x%08x, len = %i\n", (unsigned int)to, (int)len); + uint32_t writelen = len; + int bytes = mtd->writesize; + int ret = -EIO; - /* Initialize retlen, in case of early exit */ *retlen = 0; /* Do not allow write past end of device */ if ((to + len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_write: Attempt to write past end of page\n"); + DEBUG(MTD_DEBUG_LEVEL0, "nand_write: " + "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: Attempt to write not page aligned data\n"); + printk(KERN_NOTICE "nand_write: " + "Attempt to write not page aligned data\n"); return -EINVAL; } - /* Grab the lock and see if the device is available */ - nand_get_device(chip, mtd, FL_WRITING); + if (!len) + return 0; - /* Calculate chipnr */ - chipnr = (int)(to >> chip->chip_shift); - /* Select the NAND device */ - chip->select_chip(mtd, chipnr); + nand_get_device(chip, mtd, FL_WRITING); /* Check, if it is write protected */ if (nand_check_wp(mtd)) goto out; - /* Autoplace of oob data ? Use the default placement scheme */ - if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { - oobsel = chip->autooob; - autoplace = 1; - } - if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) - autoplace = 1; + chipnr = (int)(to >> chip->chip_shift); + chip->select_chip(mtd, chipnr); - /* Setup variables and oob buffer */ - totalpages = len >> chip->page_shift; - page = (int)(to >> chip->page_shift); - /* Invalidate the page cache, if we write to the cached page */ - if (page <= chip->pagebuf && chip->pagebuf < (page + totalpages)) + realpage = (int)(to >> chip->page_shift); + page = realpage & chip->pagemask; + blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + + /* Invalidate the page cache, when we write to the cached page */ + if (to <= (chip->pagebuf << chip->page_shift) && + (chip->pagebuf << chip->page_shift) < (to + len)) chip->pagebuf = -1; - /* Set it relative to chip */ - page &= chip->pagemask; - startpage = page; - /* Calc number of pages we can write in one go */ - numpages = min(ppblock - (startpage & (ppblock - 1)), totalpages); - oobbuf = nand_prepare_oobbuf(mtd, eccbuf, oobsel, autoplace, numpages); - bufstart = (uint8_t *) buf; - - /* Loop until all data is written */ - while (written < len) { - - chip->data_poi = (uint8_t *) &buf[written]; - /* Write one page. If this is the last page to write - * or the last page in this block, then use the - * real pageprogram command, else select cached programming - * if supported by the chip. - */ - ret = nand_write_page(mtd, chip, page, &oobbuf[oob], oobsel, (--numpages > 0)); - if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_write: write_page failed %d\n", ret); - goto out; - } - /* Next oob page */ - oob += mtd->oobsize; - /* Update written bytes count */ - written += mtd->writesize; - if (written == len) - goto cmp; + chip->oob_poi = chip->buffers.oobwbuf; - /* Increment page address */ - page++; + while(1) { + int cached = writelen > bytes && page != blockmask; - /* Have we hit a block boundary ? Then we have to verify and - * if verify is ok, we have to setup the oob buffer for - * the next pages. - */ - if (!(page & (ppblock - 1))) { - int ofs; - chip->data_poi = bufstart; - ret = nand_verify_pages(mtd, chip, startpage, page - startpage, - oobbuf, oobsel, chipnr, (eccbuf != NULL)); - if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_write: verify_pages failed %d\n", ret); - goto out; - } - *retlen = written; - - ofs = autoplace ? mtd->oobavail : mtd->oobsize; - if (eccbuf) - eccbuf += (page - startpage) * ofs; - totalpages -= page - startpage; - numpages = min(totalpages, ppblock); - page &= chip->pagemask; - startpage = page; - oobbuf = nand_prepare_oobbuf(mtd, eccbuf, oobsel, autoplace, numpages); - oob = 0; - /* Check, if we cross a chip boundary */ - if (!page) { - chipnr++; - chip->select_chip(mtd, -1); - chip->select_chip(mtd, chipnr); - } + ret = nand_write_page(mtd, chip, buf, page, cached); + if (ret) + break; + + writelen -= bytes; + if (!writelen) + break; + + buf += bytes; + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); } } - /* Verify the remaining pages */ - cmp: - chip->data_poi = bufstart; - ret = nand_verify_pages(mtd, chip, startpage, totalpages, oobbuf, oobsel, chipnr, (eccbuf != NULL)); - if (!ret) - *retlen = written; - else - DEBUG(MTD_DEBUG_LEVEL0, "nand_write: verify_pages failed %d\n", ret); - out: - /* Deselect and wake up anyone waiting on the device */ + *retlen = len - writelen; nand_release_device(mtd); - return ret; } +/** + * nand_write_raw - [GENERIC] Write raw data including oob + * @mtd: MTD device structure + * @buf: source buffer + * @to: offset to write to + * @len: number of bytes to write + * @buf: source buffer + * @oob: oob buffer + * + * Write raw data including oob + */ +int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, + const uint8_t *buf, uint8_t *oob) +{ + struct nand_chip *chip = mtd->priv; + int page = (int)(to >> chip->page_shift); + int chipnr = (int)(to >> chip->chip_shift); + int ret; + + *retlen = 0; + + /* Do not allow writes past end of device */ + if ((to + len) > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt write " + "beyond end of device\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device(chip, mtd, FL_WRITING); + + chip->select_chip(mtd, chipnr); + chip->oob_poi = oob; + + while (len != *retlen) { + ret = nand_write_page(mtd, chip, buf, page, 0); + if (ret) + return ret; + page++; + *retlen += mtd->writesize; + buf += mtd->writesize; + chip->oob_poi += mtd->oobsize; + } + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + return 0; +} +EXPORT_SYMBOL_GPL(nand_write_raw); /** * nand_write_oob - [MTD Interface] NAND write out-of-band @@ -2081,64 +1881,6 @@ static void nand_resume(struct mtd_info *mtd) "in suspended state\n"); } -/* - * Free allocated data structures - */ -static void nand_free_kmem(struct nand_chip *chip) -{ - /* Buffer allocated by nand_scan ? */ - if (chip->options & NAND_OOBBUF_ALLOC) - kfree(chip->oob_buf); - /* Buffer allocated by nand_scan ? */ - if (chip->options & NAND_DATABUF_ALLOC) - kfree(chip->data_buf); - /* Controller allocated by nand_scan ? */ - if (chip->options & NAND_CONTROLLER_ALLOC) - kfree(chip->controller); -} - -/* - * Allocate buffers and data structures - */ -static int nand_allocate_kmem(struct mtd_info *mtd, struct nand_chip *chip) -{ - size_t len; - - if (!chip->oob_buf) { - len = mtd->oobsize << - (chip->phys_erase_shift - chip->page_shift); - chip->oob_buf = kmalloc(len, GFP_KERNEL); - if (!chip->oob_buf) - goto outerr; - chip->options |= NAND_OOBBUF_ALLOC; - } - - if (!chip->data_buf) { - len = mtd->writesize + mtd->oobsize; - chip->data_buf = kmalloc(len, GFP_KERNEL); - if (!chip->data_buf) - goto outerr; - chip->options |= NAND_DATABUF_ALLOC; - } - - if (!chip->controller) { - chip->controller = kzalloc(sizeof(struct nand_hw_control), - GFP_KERNEL); - if (!chip->controller) - goto outerr; - - spin_lock_init(&chip->controller->lock); - init_waitqueue_head(&chip->controller->wq); - chip->options |= NAND_CONTROLLER_ALLOC; - } - return 0; - - outerr: - printk(KERN_ERR "nand_scan(): Cannot allocate buffers\n"); - nand_free_kmem(chip); - return -ENOMEM; -} - /* * Set default functions */ @@ -2174,6 +1916,13 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; if (!chip->scan_bbt) chip->scan_bbt = nand_default_bbt; + + if (!chip->controller) { + chip->controller = &chip->hwcontrol; + spin_lock_init(&chip->controller->lock); + init_waitqueue_head(&chip->controller->wq); + } + } /* @@ -2321,8 +2070,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, * This fills out all the uninitialized function pointers * with the defaults. * The flash ID is read and the mtd/chip structures are - * filled with the appropriate values. Buffers are allocated if - * they are not provided by the board driver + * filled with the appropriate values. * The mtd->owner field must be set to the module of the caller * */ @@ -2369,13 +2117,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips) chip->numchips = i; mtd->size = i * chip->chipsize; - /* Allocate buffers and data structures */ - if (nand_allocate_kmem(mtd, chip)) - return -ENOMEM; - - /* Preset the internal oob buffer */ - memset(chip->oob_buf, 0xff, - mtd->oobsize << (chip->phys_erase_shift - chip->page_shift)); + /* Preset the internal oob write buffer */ + memset(chip->buffers.oobwbuf, 0xff, mtd->oobsize); /* * If no default placement scheme is given, select an appropriate one @@ -2415,6 +2158,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips) /* Use standard hwecc read page function ? */ if (!chip->ecc.read_page) chip->ecc.read_page = nand_read_page_hwecc; + if (!chip->ecc.write_page) + chip->ecc.write_page = nand_write_page_hwecc; case NAND_ECC_HW_SYNDROME: if (!chip->ecc.calculate || !chip->ecc.correct || @@ -2423,9 +2168,11 @@ int nand_scan(struct mtd_info *mtd, int maxchips) "Hardware ECC not possible\n"); BUG(); } - /* Use standard syndrome read page function ? */ + /* Use standard syndrome read/write page function ? */ if (!chip->ecc.read_page) chip->ecc.read_page = nand_read_page_syndrome; + if (!chip->ecc.write_page) + chip->ecc.write_page = nand_write_page_syndrome; if (mtd->writesize >= chip->ecc.size) break; @@ -2438,6 +2185,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) chip->ecc.calculate = nand_calculate_ecc; chip->ecc.correct = nand_correct_data; chip->ecc.read_page = nand_read_page_swecc; + chip->ecc.write_page = nand_write_page_swecc; chip->ecc.size = 256; chip->ecc.bytes = 3; break; @@ -2446,6 +2194,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " "This is not recommended !!\n"); chip->ecc.read_page = nand_read_page_swecc; + chip->ecc.write_page = nand_write_page_swecc; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; break; @@ -2522,8 +2271,6 @@ void nand_release(struct mtd_info *mtd) /* Free bad block table memory */ kfree(chip->bbt); - /* Free buffers */ - nand_free_kmem(chip); } EXPORT_SYMBOL_GPL(nand_scan); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index ecaaca18d1e0..40f99304df76 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -666,7 +666,7 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b struct nand_chip *this = mtd->priv; bd->options &= ~NAND_BBT_SCANEMPTY; - return create_bbt(mtd, this->data_buf, bd, -1); + return create_bbt(mtd, this->buffers.databuf, bd, -1); } /** diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index b7083104a05b..de6de91fbad9 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -571,7 +571,6 @@ static int __init rtc_from4_init(void) this->ecc.mode = NAND_ECC_HW_SYNDROME; this->ecc.size = 512; this->ecc.bytes = 8; - this->options |= NAND_HWECC_SYNDROME; /* return the status of extra status and ECC checks */ this->errstat = rtc_from4_errstat; /* set the nand_oobinfo to support FPGA H/W error detection */ diff --git a/drivers/mtd/nand/toto.c b/drivers/mtd/nand/toto.c index a9cf0190c27a..f9e2d4a0ab8c 100644 --- a/drivers/mtd/nand/toto.c +++ b/drivers/mtd/nand/toto.c @@ -175,8 +175,6 @@ static int __init toto_init(void) goto out; - out_buf: - kfree(this->data_buf); out_mtd: kfree(toto_mtd); out: diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 00916498ea55..1a749ba6130f 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -37,7 +37,7 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, extern int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, uint8_t *buf, uint8_t *oob); + size_t *retlen, const uint8_t *buf, uint8_t *oob); /* The maximum number of NAND chips in an array */ #define NAND_MAX_CHIPS 8 @@ -47,6 +47,7 @@ extern int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, * adjust this accordingly. */ #define NAND_MAX_OOBSIZE 64 +#define NAND_MAX_PAGESIZE 2048 /* * Constants for hardware specific CLE/ALE/NCE function @@ -181,20 +182,12 @@ typedef enum { /* Use a flash based bad block table. This option is passed to the * default bad block table function. */ #define NAND_USE_FLASH_BBT 0x00010000 -/* The hw ecc generator provides a syndrome instead a ecc value on read - * This can only work if we have the ecc bytes directly behind the - * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */ -#define NAND_HWECC_SYNDROME 0x00020000 /* This option skips the bbt scan during initialization. */ -#define NAND_SKIP_BBTSCAN 0x00040000 +#define NAND_SKIP_BBTSCAN 0x00020000 /* Options set by nand scan */ /* Nand scan has allocated controller struct */ -#define NAND_CONTROLLER_ALLOC 0x20000000 -/* Nand scan has allocated oob_buf */ -#define NAND_OOBBUF_ALLOC 0x40000000 -/* Nand scan has allocated data_buf */ -#define NAND_DATABUF_ALLOC 0x80000000 +#define NAND_CONTROLLER_ALLOC 0x80000000 /* @@ -240,6 +233,7 @@ struct nand_hw_control { * be provided if an hardware ECC is available * @calculate: function for ecc calculation or readback from ecc hardware * @correct: function for ecc correction, matching to ecc generator (sw/hw) + * @read_page: function to read a page according to the ecc generator requirements * @write_page: function to write a page according to the ecc generator requirements */ struct nand_ecc_ctrl { @@ -260,9 +254,28 @@ struct nand_ecc_ctrl { int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf); - int (*write_page)(struct mtd_info *mtd, + void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int cached); + const uint8_t *buf); +}; + +/** + * struct nand_buffers - buffer structure for read/write + * @ecccalc: buffer for calculated ecc + * @ecccode: buffer for ecc read from flash + * @oobwbuf: buffer for write oob data + * @databuf: buffer for data - dynamically sized + * @oobrbuf: buffer to read oob data + * + * Do not change the order of buffers. databuf and oobrbuf must be in + * consecutive order. + */ +struct nand_buffers { + uint8_t ecccalc[NAND_MAX_OOBSIZE]; + uint8_t ecccode[NAND_MAX_OOBSIZE]; + uint8_t oobwbuf[NAND_MAX_OOBSIZE]; + uint8_t databuf[NAND_MAX_PAGESIZE]; + uint8_t oobrbuf[NAND_MAX_OOBSIZE]; }; /** @@ -294,8 +307,8 @@ struct nand_ecc_ctrl { * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry * @chip_shift: [INTERN] number of address bits in one chip - * @data_buf: [INTERN] internal buffer for one page + oob - * @oob_buf: [INTERN] oob buffer for one eraseblock + * @datbuf: [INTERN] internal buffer for one page + oob + * @oobbuf: [INTERN] oob buffer for one eraseblock * @oobdirty: [INTERN] indicates that oob_buf must be reinitialized * @data_poi: [INTERN] pointer to a data buffer * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about @@ -336,32 +349,38 @@ struct nand_chip { int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state); void (*erase_cmd)(struct mtd_info *mtd, int page); int (*scan_bbt)(struct mtd_info *mtd); - struct nand_ecc_ctrl ecc; + int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); + int chip_delay; - wait_queue_head_t wq; - nand_state_t state; + unsigned int options; + int page_shift; int phys_erase_shift; int bbt_erase_shift; int chip_shift; - uint8_t *data_buf; - uint8_t *oob_buf; - int oobdirty; - uint8_t *data_poi; - unsigned int options; - int badblockpos; int numchips; unsigned long chipsize; int pagemask; int pagebuf; + int badblockpos; + + nand_state_t state; + + uint8_t *oob_poi; + struct nand_hw_control *controller; struct nand_oobinfo *autooob; + + struct nand_ecc_ctrl ecc; + struct nand_buffers buffers; + struct nand_hw_control hwcontrol; + uint8_t *bbt; struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_md; + struct nand_bbt_descr *badblock_pattern; - struct nand_hw_control *controller; + void *priv; - int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); }; /* -- cgit v1.2.3 From 8be834f76291fdcc0614cb84926c6910b9f2ecbc Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 May 2006 20:05:26 +0200 Subject: [MTD] NAND Fix platform structure and NDFC driver The platform structure was lacking an oobinfo field. The NDFC driver had some remains from another tree. Signed-off-by: Thomas Gleixner --- drivers/mtd/nand/ndfc.c | 6 ++---- include/linux/mtd/nand.h | 4 +++- include/linux/mtd/ndfc.h | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 4d70dd16cf5d..5790d630faed 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -169,14 +168,13 @@ static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) chip->ecc.mode = NAND_ECC_HW; chip->ecc.size = 256; chip->ecc.bytes = 3; - chip->autooob = mtd->pl_chip->autooob; + chip->autooob = mtd->pl_chip->oobinfo; mtd->mtd.priv = chip; mtd->mtd.owner = THIS_MODULE; } static int ndfc_chip_probe(struct platform_device *pdev) { - int rc; struct platform_nand_chip *nc = pdev->dev.platform_data; struct ndfc_chip_settings *settings = nc->priv; struct ndfc_controller *ndfc = &ndfc_ctrl; @@ -235,7 +233,7 @@ static int ndfc_nand_probe(struct platform_device *pdev) struct ndfc_controller_settings *settings = nc->priv; struct resource *res = pdev->resource; struct ndfc_controller *ndfc = &ndfc_ctrl; - unsigned long long phys = NDFC_PHYSADDR_OFFS | res->start; + unsigned long long phys = setting->erpn | res->start; ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1); if (!ndfc->ndfcbase) { diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 1a749ba6130f..fd46bcf52281 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -518,10 +518,11 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, * * @nr_chips: max. number of chips to scan for * @chip_offs: chip number offset - * @nr_partitions: number of partitions pointed to be partitoons (or zero) + * @nr_partitions: number of partitions pointed to by partitions (or zero) * @partitions: mtd partition list * @chip_delay: R/B delay value in us * @options: Option flags, e.g. 16bit buswidth + * @oobinfo: oob info structure (ecc placement) * @priv: hardware controller specific settings */ struct platform_nand_chip { @@ -529,6 +530,7 @@ struct platform_nand_chip { int chip_offset; int nr_partitions; struct mtd_partition *partitions; + struct nand_oobinfo *oobinfo; int chip_delay; unsigned int options; void *priv; diff --git a/include/linux/mtd/ndfc.h b/include/linux/mtd/ndfc.h index 31d61f07d768..d0558a982628 100644 --- a/include/linux/mtd/ndfc.h +++ b/include/linux/mtd/ndfc.h @@ -56,7 +56,8 @@ #define NDFC_MAX_BANKS 4 struct ndfc_controller_settings { - uint32_t ccr_settings; + uint32_t ccr_settings; + uint64_t ndfc_erpn; }; struct ndfc_chip_settings { -- cgit v1.2.3 From ff268fb8791cf18df536113355d7184007c269d9 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 May 2006 20:36:12 +0200 Subject: [MTD] NAND Consolidate oobinfo handling The info structure for out of band data was copied into the mtd structure. Make it a pointer and remove the ability to set it from userspace. The position of ecc bytes is defined by the hardware and should not be changed by software. Signed-off-by: Thomas Gleixner --- drivers/mtd/mtdchar.c | 10 ++-------- drivers/mtd/mtdconcat.c | 4 +--- drivers/mtd/mtdpart.c | 4 +--- drivers/mtd/nand/nand_base.c | 10 +--------- drivers/mtd/onenand/onenand_base.c | 2 +- fs/jffs2/wbuf.c | 2 +- include/linux/mtd/mtd.h | 5 ++--- 7 files changed, 9 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 7a7df851c993..608f7af679cb 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -512,16 +512,10 @@ static int mtd_ioctl(struct inode *inode, struct file *file, break; } - case MEMSETOOBSEL: - { - if (copy_from_user(&mtd->oobinfo, argp, sizeof(struct nand_oobinfo))) - return -EFAULT; - break; - } - case MEMGETOOBSEL: { - if (copy_to_user(argp, &(mtd->oobinfo), sizeof(struct nand_oobinfo))) + if (copy_to_user(argp, mtd->oobinfo, + sizeof(struct nand_oobinfo))) return -EFAULT; break; } diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 6d52137988fa..699fce7770de 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -766,9 +766,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c } - if(concat->mtd.type == MTD_NANDFLASH) - memcpy(&concat->mtd.oobinfo, &subdev[0]->oobinfo, - sizeof(struct nand_oobinfo)); + concat->mtd.oobinfo = subdev[0]->oobinfo; concat->num_subdev = num_devs; concat->mtd.name = name; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index a93550ce7978..b6b218952d49 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -316,7 +316,6 @@ int add_mtd_partitions(struct mtd_info *master, slave->mtd.size = parts[i].size; slave->mtd.writesize = master->writesize; slave->mtd.oobsize = master->oobsize; - slave->mtd.oobavail = master->oobavail; slave->mtd.ecctype = master->ecctype; slave->mtd.eccsize = master->eccsize; @@ -435,8 +434,7 @@ int add_mtd_partitions(struct mtd_info *master, parts[i].name); } - /* copy oobinfo from master */ - memcpy(&slave->mtd.oobinfo, &master->oobinfo, sizeof(slave->mtd.oobinfo)); + slave->mtd.oobinfo = master->oobinfo; if(parts[i].mtdp) { /* store the object pointer (caller may or may not register it */ diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 023224dd12eb..20f79fec73b5 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2142,14 +2142,6 @@ int nand_scan(struct mtd_info *mtd, int maxchips) } } - /* - * The number of bytes available for the filesystem to place fs - * dependend oob data - */ - mtd->oobavail = 0; - for (i = 0; chip->autooob->oobfree[i][1]; i++) - mtd->oobavail += chip->autooob->oobfree[i][1]; - /* * check ECC mode, default to software if 3byte/512byte hardware ECC is * selected and we have 256 byte pagesize fallback to software ECC @@ -2245,7 +2237,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) mtd->block_markbad = nand_block_markbad; /* and make the autooob the default one */ - memcpy(&mtd->oobinfo, chip->autooob, sizeof(mtd->oobinfo)); + mtd->oobinfo = chip->autooob; /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 7a2419186ff4..b24bfa6e202c 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1762,7 +1762,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) break; } - memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo)); + mtd->oobinfo = this->autooob; /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 717fa2f52ac2..dc275cedfe4a 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -1151,7 +1151,7 @@ static struct nand_oobinfo jffs2_oobinfo_docecc = { static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) { - struct nand_oobinfo *oinfo = &c->mtd->oobinfo; + struct nand_oobinfo *oinfo = c->mtd->oobinfo; /* Do this only, if we have an oob buffer */ if (!c->mtd->oobsize) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 41a984dcb139..8429da51bb09 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -101,9 +101,8 @@ struct mtd_info { char *name; int index; - // oobinfo is a nand_oobinfo structure, which can be set by iotcl (MEMSETOOBINFO) - struct nand_oobinfo oobinfo; - u_int32_t oobavail; // Number of bytes in OOB area available for fs + /* oobinfo structure pointer - read only ! */ + struct nand_oobinfo *oobinfo; /* Data for variable erase regions. If numeraseregions is zero, * it means that the whole device has erasesize as given above. -- cgit v1.2.3 From 5bd34c091a044d130601370c370f84b1c59f1627 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 27 May 2006 22:16:10 +0200 Subject: [MTD] NAND Replace oobinfo by ecclayout The nand_oobinfo structure is not fitting the newer error correction demands anymore. Replace it by struct nand_ecclayout and fixup the users all over the place. Keep the nand_oobinfo based ioctl for user space compability reasons. Signed-off-by: Thomas Gleixner --- drivers/mtd/mtdchar.c | 26 +++++++++++++++++-- drivers/mtd/mtdconcat.c | 2 +- drivers/mtd/mtdpart.c | 2 +- drivers/mtd/nand/diskonchip.c | 5 ++-- drivers/mtd/nand/nand_base.c | 52 ++++++++++++++++++++++++-------------- drivers/mtd/nand/ndfc.c | 2 +- drivers/mtd/nand/rtc_from4.c | 5 ++-- drivers/mtd/nand/s3c2410.c | 5 ++-- drivers/mtd/nand/sharpsl.c | 5 ++-- drivers/mtd/onenand/onenand_base.c | 14 +++++----- fs/jffs2/jffs2_fs_sb.h | 2 +- fs/jffs2/wbuf.c | 51 ++++++++++++------------------------- include/linux/mtd/inftl.h | 2 +- include/linux/mtd/mtd.h | 4 +-- include/linux/mtd/nand.h | 9 ++++--- include/linux/mtd/nftl.h | 2 +- include/linux/mtd/onenand.h | 6 ++--- include/linux/mtd/partitions.h | 2 +- include/mtd/mtd-abi.h | 36 +++++++++++++++++++++----- include/mtd/mtd-user.h | 1 + 20 files changed, 134 insertions(+), 99 deletions(-) (limited to 'include') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 608f7af679cb..e75ec5fe7760 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -512,14 +512,36 @@ static int mtd_ioctl(struct inode *inode, struct file *file, break; } + /* Legacy interface */ case MEMGETOOBSEL: { - if (copy_to_user(argp, mtd->oobinfo, - sizeof(struct nand_oobinfo))) + struct nand_oobinfo oi; + + if (!mtd->ecclayout) + return -EOPNOTSUPP; + if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos)) + return -EINVAL; + + oi.useecc = MTD_NANDECC_AUTOPLACE; + memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos)); + memcpy(&oi.oobfree, mtd->ecclayout->oobfree, + sizeof(oi.oobfree)); + + if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo))) return -EFAULT; break; } + case ECCGETLAYOUT: + + if (!mtd->ecclayout) + return -EOPNOTSUPP; + + if (copy_to_user(argp, &mtd->ecclayout, + sizeof(struct nand_ecclayout))) + return -EFAULT; + break; + case MEMGETBADBLOCK: { loff_t offs; diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 699fce7770de..ec15abcdbdfa 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -766,7 +766,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c } - concat->mtd.oobinfo = subdev[0]->oobinfo; + concat->mtd.ecclayout = subdev[0]->ecclayout; concat->num_subdev = num_devs; concat->mtd.name = name; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index b6b218952d49..6d7639b98eab 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -434,7 +434,7 @@ int add_mtd_partitions(struct mtd_info *master, parts[i].name); } - slave->mtd.oobinfo = master->oobinfo; + slave->mtd.ecclayout = master->ecclayout; if(parts[i].mtdp) { /* store the object pointer (caller may or may not register it */ diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 82262a4a4208..463e12ced1b3 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -1058,8 +1058,7 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, * safer. The only problem with it is that any code that parses oobfree must * be able to handle out-of-order segments. */ -static struct nand_oobinfo doc200x_oobinfo = { - .useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout doc200x_oobinfo = { .eccbytes = 6, .eccpos = {0, 1, 2, 3, 4, 5}, .oobfree = {{8, 8}, {6, 2}} @@ -1662,7 +1661,7 @@ static int __init doc_probe(unsigned long physadr) nand->ecc.calculate = doc200x_calculate_ecc; nand->ecc.correct = doc200x_correct_data; - nand->autooob = &doc200x_oobinfo; + nand->ecc.layout = &doc200x_oobinfo; nand->ecc.mode = NAND_ECC_HW_SYNDROME; nand->ecc.size = 512; nand->ecc.bytes = 6; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 20f79fec73b5..e922b829c4be 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -52,28 +52,33 @@ #endif /* Define default oob placement schemes for large and small page devices */ -static struct nand_oobinfo nand_oob_8 = { - .useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout nand_oob_8 = { .eccbytes = 3, .eccpos = {0, 1, 2}, - .oobfree = {{3, 2}, {6, 2}} + .oobfree = { + {.offset = 3, + .length = 2}, + {.offset = 6, + .length = 2}} }; -static struct nand_oobinfo nand_oob_16 = { - .useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout nand_oob_16 = { .eccbytes = 6, .eccpos = {0, 1, 2, 3, 6, 7}, - .oobfree = {{8, 8}} + .oobfree = { + {.offset = 8, + . length = 8}} }; -static struct nand_oobinfo nand_oob_64 = { - .useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout nand_oob_64 = { .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}} + .oobfree = { + {.offset = 2, + .length = 38}} }; /* This is used for padding purposes in nand_write_oob */ @@ -749,7 +754,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *p = buf; uint8_t *ecc_calc = chip->buffers.ecccalc; uint8_t *ecc_code = chip->buffers.ecccode; - int *eccpos = chip->autooob->eccpos; + int *eccpos = chip->ecc.layout->eccpos; chip->read_buf(mtd, buf, mtd->writesize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -795,7 +800,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *p = buf; uint8_t *ecc_calc = chip->buffers.ecccalc; uint8_t *ecc_code = chip->buffers.ecccode; - int *eccpos = chip->autooob->eccpos; + int *eccpos = chip->ecc.layout->eccpos; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_READ); @@ -1198,7 +1203,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, int eccsteps = chip->ecc.steps; uint8_t *ecc_calc = chip->buffers.ecccalc; const uint8_t *p = buf; - int *eccpos = chip->autooob->eccpos; + int *eccpos = chip->ecc.layout->eccpos; if (chip->ecc.mode != NAND_ECC_NONE) { /* Software ecc calculation */ @@ -1227,7 +1232,7 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int eccsteps = chip->ecc.steps; uint8_t *ecc_calc = chip->buffers.ecccalc; const uint8_t *p = buf; - int *eccpos = chip->autooob->eccpos; + int *eccpos = chip->ecc.layout->eccpos; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_WRITE); @@ -2124,16 +2129,16 @@ int nand_scan(struct mtd_info *mtd, int maxchips) /* * If no default placement scheme is given, select an appropriate one */ - if (!chip->autooob) { + if (!chip->ecc.layout) { switch (mtd->oobsize) { case 8: - chip->autooob = &nand_oob_8; + chip->ecc.layout = &nand_oob_8; break; case 16: - chip->autooob = &nand_oob_16; + chip->ecc.layout = &nand_oob_16; break; case 64: - chip->autooob = &nand_oob_64; + chip->ecc.layout = &nand_oob_64; break; default: printk(KERN_WARNING "No oob scheme defined for " @@ -2197,6 +2202,15 @@ int nand_scan(struct mtd_info *mtd, int maxchips) BUG(); } + /* + * The number of bytes available for a client to place data into + * the out of band area + */ + chip->ecc.layout->oobavail = 0; + for (i = 0; chip->ecc.layout->oobfree[i].length; i++) + chip->ecc.layout->oobavail += + chip->ecc.layout->oobfree[i].length; + /* * Set the number of read / write steps for one page depending on ECC * mode @@ -2236,8 +2250,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips) mtd->block_isbad = nand_block_isbad; mtd->block_markbad = nand_block_markbad; - /* and make the autooob the default one */ - mtd->oobinfo = chip->autooob; + /* propagate ecc.layout to mtd_info */ + mtd->ecclayout = chip->ecc.layout; /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 5790d630faed..551702ddcacb 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -168,7 +168,7 @@ static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) chip->ecc.mode = NAND_ECC_HW; chip->ecc.size = 256; chip->ecc.bytes = 3; - chip->autooob = mtd->pl_chip->oobinfo; + chip->ecclayout = mtd->pl_chip->ecclayout; mtd->mtd.priv = chip; mtd->mtd.owner = THIS_MODULE; } diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index de6de91fbad9..f8c49645324d 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -142,8 +142,7 @@ static struct rs_control *rs_decoder; /* * hardware specific Out Of Band information */ -static struct nand_oobinfo rtc_from4_nand_oobinfo = { - .useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout rtc_from4_nand_oobinfo = { .eccbytes = 32, .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, @@ -574,7 +573,7 @@ static int __init rtc_from4_init(void) /* return the status of extra status and ECC checks */ this->errstat = rtc_from4_errstat; /* set the nand_oobinfo to support FPGA H/W error detection */ - this->autooob = &rtc_from4_nand_oobinfo; + this->ecc.layout = &rtc_from4_nand_oobinfo; this->ecc.hwctl = rtc_from4_enable_hwecc; this->ecc.calculate = rtc_from4_calculate_ecc; this->ecc.correct = rtc_from4_correct_data; diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 215227d1a65c..8429793a6288 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -76,8 +76,7 @@ static int hardware_ecc = 0; /* new oob placement block for use with hardware ecc generation */ -static struct nand_oobinfo nand_hw_eccoob = { - .useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout nand_hw_eccoob = { .eccbytes = 3, .eccpos = {0, 1, 2}, .oobfree = {{8, 8}} @@ -502,7 +501,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->ecc.mode = NAND_ECC_HW; chip->ecc.size = 512; chip->ecc.bytes = 3; - chip->autooob = &nand_hw_eccoob; + chip->ecc.layout = &nand_hw_eccoob; if (info->is_s3c2440) { chip->ecc.hwctl = s3c2440_nand_enable_hwecc; diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 45a1da724bff..21743658d150 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -115,8 +115,7 @@ static struct nand_bbt_descr sharpsl_akita_bbt = { .pattern = scan_ff_pattern }; -static struct nand_oobinfo akita_oobinfo = { - .useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout akita_oobinfo = { .eccbytes = 24, .eccpos = { 0x5, 0x1, 0x2, 0x3, 0x6, 0x7, 0x15, 0x11, @@ -202,7 +201,7 @@ static int __init sharpsl_nand_init(void) this->badblock_pattern = &sharpsl_bbt; if (machine_is_akita() || machine_is_borzoi()) { this->badblock_pattern = &sharpsl_akita_bbt; - this->autooob = &akita_oobinfo; + this->ecc.layout = &akita_oobinfo; } this->ecc.hwctl = sharpsl_nand_enable_hwecc; this->ecc.calculate = sharpsl_nand_calculate_ecc; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index b24bfa6e202c..a0d3f011c0f2 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -23,8 +23,7 @@ /** * onenand_oob_64 - oob info for large (2KB) page */ -static struct nand_oobinfo onenand_oob_64 = { - .useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout onenand_oob_64 = { .eccbytes = 20, .eccpos = { 8, 9, 10, 11, 12, @@ -41,8 +40,7 @@ static struct nand_oobinfo onenand_oob_64 = { /** * onenand_oob_32 - oob info for middle (1KB) page */ -static struct nand_oobinfo onenand_oob_32 = { - .useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout onenand_oob_32 = { .eccbytes = 10, .eccpos = { 8, 9, 10, 11, 12, @@ -1747,22 +1745,22 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) switch (mtd->oobsize) { case 64: - this->autooob = &onenand_oob_64; + this->ecclayout = &onenand_oob_64; break; case 32: - this->autooob = &onenand_oob_32; + this->ecclayout = &onenand_oob_32; break; default: printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n", mtd->oobsize); /* To prevent kernel oops */ - this->autooob = &onenand_oob_32; + this->ecclayout = &onenand_oob_32; break; } - mtd->oobinfo = this->autooob; + mtd->ecclayout = this->ecclayout; /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index 272fbea55192..506690cc9a78 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -107,7 +107,7 @@ struct jffs2_sb_info { struct rw_semaphore wbuf_sem; /* Protects the write buffer */ /* Information about out-of-band area usage... */ - struct nand_oobinfo *oobinfo; + struct nand_ecclayout *ecclayout; uint32_t badblock_pos; uint32_t fsdata_pos; uint32_t fsdata_len; diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index dc275cedfe4a..c6a62e162963 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -1140,18 +1140,9 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock * return 1; } -#define NAND_JFFS2_OOB16_FSDALEN 8 - -static struct nand_oobinfo jffs2_oobinfo_docecc = { - .useecc = MTD_NANDECC_PLACE, - .eccbytes = 6, - .eccpos = {0,1,2,3,4,5} -}; - - static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) { - struct nand_oobinfo *oinfo = c->mtd->oobinfo; + struct nand_ecclayout *oinfo = c->mtd->ecclayout; /* Do this only, if we have an oob buffer */ if (!c->mtd->oobsize) @@ -1161,33 +1152,23 @@ static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) c->cleanmarker_size = 0; /* Should we use autoplacement ? */ - if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) { - D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n")); - /* Get the position of the free bytes */ - if (!oinfo->oobfree[0][1]) { - printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n"); - return -ENOSPC; - } - c->fsdata_pos = oinfo->oobfree[0][0]; - c->fsdata_len = oinfo->oobfree[0][1]; - if (c->fsdata_len > 8) - c->fsdata_len = 8; - } else { - /* This is just a legacy fallback and should go away soon */ - switch(c->mtd->ecctype) { - case MTD_ECC_RS_DiskOnChip: - printk(KERN_WARNING "JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it!\n"); - c->oobinfo = &jffs2_oobinfo_docecc; - c->fsdata_pos = 6; - c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN; - c->badblock_pos = 15; - break; + if (!oinfo) { + D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n")); + return -EINVAL; + } - default: - D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n")); - return -EINVAL; - } + D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n")); + /* Get the position of the free bytes */ + if (!oinfo->oobfree[0].length) { + printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep." + " Autoplacement selected and no empty space in oob\n"); + return -ENOSPC; } + c->fsdata_pos = oinfo->oobfree[0].offset; + c->fsdata_len = oinfo->oobfree[0].length; + if (c->fsdata_len > 8) + c->fsdata_len = 8; + return 0; } diff --git a/include/linux/mtd/inftl.h b/include/linux/mtd/inftl.h index d7eaa40e5ab0..6977780e548f 100644 --- a/include/linux/mtd/inftl.h +++ b/include/linux/mtd/inftl.h @@ -46,7 +46,7 @@ struct INFTLrecord { unsigned int nb_blocks; /* number of physical blocks */ unsigned int nb_boot_blocks; /* number of blocks used by the bios */ struct erase_info instr; - struct nand_oobinfo oobinfo; + struct nand_ecclayout oobinfo; }; int INFTL_mount(struct INFTLrecord *s); diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 8429da51bb09..48a9df21ab11 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -101,8 +101,8 @@ struct mtd_info { char *name; int index; - /* oobinfo structure pointer - read only ! */ - struct nand_oobinfo *oobinfo; + /* ecc layout structure pointer - read only ! */ + struct nand_ecclayout *ecclayout; /* Data for variable erase regions. If numeraseregions is zero, * it means that the whole device has erasesize as given above. diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index fd46bcf52281..dc2bf1bcf42b 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -244,6 +244,7 @@ struct nand_ecc_ctrl { int total; int prepad; int postpad; + struct nand_ecclayout *layout; void (*hwctl)(struct mtd_info *mtd, int mode); int (*calculate)(struct mtd_info *mtd, const uint8_t *dat, @@ -318,7 +319,7 @@ struct nand_buffers { * @chipsize: [INTERN] the size of one chip for multichip arrays * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 * @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf - * @autooob: [REPLACEABLE] the default (auto)placement scheme + * @ecclayout: [REPLACEABLE] the default ecc placement scheme * @bbt: [INTERN] bad block table pointer * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup * @bbt_md: [REPLACEABLE] bad block table mirror descriptor @@ -368,7 +369,7 @@ struct nand_chip { uint8_t *oob_poi; struct nand_hw_control *controller; - struct nand_oobinfo *autooob; + struct nand_ecclayout *ecclayout; struct nand_ecc_ctrl ecc; struct nand_buffers buffers; @@ -522,7 +523,7 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, * @partitions: mtd partition list * @chip_delay: R/B delay value in us * @options: Option flags, e.g. 16bit buswidth - * @oobinfo: oob info structure (ecc placement) + * @ecclayout: ecc layout info structure * @priv: hardware controller specific settings */ struct platform_nand_chip { @@ -530,7 +531,7 @@ struct platform_nand_chip { int chip_offset; int nr_partitions; struct mtd_partition *partitions; - struct nand_oobinfo *oobinfo; + struct nand_ecclayout *ecclayout; int chip_delay; unsigned int options; void *priv; diff --git a/include/linux/mtd/nftl.h b/include/linux/mtd/nftl.h index d35d2c21ff3e..bcf2fb3fa4a7 100644 --- a/include/linux/mtd/nftl.h +++ b/include/linux/mtd/nftl.h @@ -37,7 +37,7 @@ struct NFTLrecord { unsigned int nb_blocks; /* number of physical blocks */ unsigned int nb_boot_blocks; /* number of blocks used by the bios */ struct erase_info instr; - struct nand_oobinfo oobinfo; + struct nand_ecclayout oobinfo; }; int NFTL_mount(struct NFTLrecord *s); diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 3f5919f2e9da..9ce9a48db444 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -77,7 +77,7 @@ struct onenand_bufferram { * @param chip_lock [INTERN] spinlock used to protect access to this structure and the chip * @param wq [INTERN] wait queue to sleep on if a OneNAND operation is in progress * @param state [INTERN] the current state of the OneNAND device - * @param autooob [REPLACEABLE] the default (auto)placement scheme + * @param ecclayout [REPLACEABLE] the default ecc placement scheme * @param bbm [REPLACEABLE] pointer to Bad Block Management * @param priv [OPTIONAL] pointer to private chip date */ @@ -113,9 +113,9 @@ struct onenand_chip { onenand_state_t state; unsigned char *page_buf; - struct nand_oobinfo *autooob; + struct nand_ecclayout *ecclayout; - void *bbm; + void *bbm; void *priv; }; diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index b03f512d51b9..da6b3d6f12a7 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -41,7 +41,7 @@ struct mtd_partition { u_int32_t size; /* partition size */ u_int32_t offset; /* offset within the master MTD space */ u_int32_t mask_flags; /* master MTD flags to mask out for this partition */ - struct nand_oobinfo *oobsel; /* out of band layout for this partition (NAND only)*/ + struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ struct mtd_info **mtdp; /* pointer to store the MTD object */ }; diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index 1e09e4c8f485..54c673f9648d 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -82,12 +82,12 @@ struct otp_info { uint32_t locked; }; -#define MEMGETINFO _IOR('M', 1, struct mtd_info_user) -#define MEMERASE _IOW('M', 2, struct erase_info_user) -#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) -#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf) -#define MEMLOCK _IOW('M', 5, struct erase_info_user) -#define MEMUNLOCK _IOW('M', 6, struct erase_info_user) +#define MEMGETINFO _IOR('M', 1, struct mtd_info_user) +#define MEMERASE _IOW('M', 2, struct erase_info_user) +#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) +#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf) +#define MEMLOCK _IOW('M', 5, struct erase_info_user) +#define MEMUNLOCK _IOW('M', 6, struct erase_info_user) #define MEMGETREGIONCOUNT _IOR('M', 7, int) #define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user) #define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo) @@ -97,8 +97,13 @@ struct otp_info { #define OTPSELECT _IOR('M', 13, int) #define OTPGETREGIONCOUNT _IOW('M', 14, int) #define OTPGETREGIONINFO _IOW('M', 15, struct otp_info) -#define OTPLOCK _IOR('M', 16, struct otp_info) +#define OTPLOCK _IOR('M', 16, struct otp_info) +#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) +/* + * Obsolete legacy interface. Keep it in order not to break userspace + * interfaces + */ struct nand_oobinfo { uint32_t useecc; uint32_t eccbytes; @@ -106,4 +111,21 @@ struct nand_oobinfo { uint32_t eccpos[32]; }; +struct nand_oobfree { + uint32_t offset; + uint32_t length; +}; + +#define MTD_MAX_OOBFREE_ENTRIES 8 +/* + * ECC layout control structure. Exported to userspace for + * diagnosis and to allow creation of raw images + */ +struct nand_ecclayout { + uint32_t eccbytes; + uint32_t eccpos[64]; + uint32_t oobavail; + struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; +}; + #endif /* __MTD_ABI_H__ */ diff --git a/include/mtd/mtd-user.h b/include/mtd/mtd-user.h index 1c13fc7161fe..713f34d3e62e 100644 --- a/include/mtd/mtd-user.h +++ b/include/mtd/mtd-user.h @@ -16,5 +16,6 @@ typedef struct mtd_info_user mtd_info_t; typedef struct erase_info_user erase_info_t; typedef struct region_info_user region_info_t; typedef struct nand_oobinfo nand_oobinfo_t; +typedef struct nand_ecclayout nand_ecclayout_t; #endif /* __MTD_USER_H__ */ -- cgit v1.2.3 From f4a43cfcecfcaeeaa40a9dbc1d1378298c22446e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 28 May 2006 11:01:53 +0200 Subject: [MTD] Remove silly MTD_WRITE/READ macros Most of those macros are unused and the used ones just obfuscate the code. Remove them and fixup all users. Signed-off-by: Thomas Gleixner --- drivers/mtd/inftlcore.c | 63 ++++++++++---------- drivers/mtd/inftlmount.c | 43 ++++++++------ drivers/mtd/maps/nettel.c | 2 +- drivers/mtd/mtdblock.c | 13 +++-- drivers/mtd/mtdchar.c | 4 +- drivers/mtd/nftlcore.c | 144 ++++++++++++++++++++++++---------------------- drivers/mtd/nftlmount.c | 74 ++++++++++++++---------- fs/jffs/intrep.c | 15 ++--- include/linux/mtd/mtd.h | 16 +----- 9 files changed, 195 insertions(+), 179 deletions(-) (limited to 'include') diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index ddd12993780d..3396f0e1ac5f 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -197,10 +197,11 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned u16 BlockMap[MAX_SECTORS_PER_UNIT]; unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; unsigned int thisEUN, prevEUN, status; + struct mtd_info *mtd = inftl->mbd.mtd; int block, silly; unsigned int targetEUN; struct inftl_oob oob; - size_t retlen; + size_t retlen; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d," "pending=%d)\n", inftl, thisVUC, pendingblock); @@ -226,9 +227,9 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned if ((BlockMap[block] != 0xffff) || BlockDeleted[block]) continue; - if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) - + (block * SECTORSIZE), 16 , &retlen, - (char *)&oob) < 0) + if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) + + (block * SECTORSIZE), 16 , &retlen, + (char *)&oob) < 0) status = SECTOR_IGNORE; else status = oob.b.Status | oob.b.Status1; @@ -288,13 +289,14 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned if (BlockMap[block] == BLOCK_NIL) continue; - ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize * - BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, - &retlen, movebuf); + ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) + + (block * SECTORSIZE), SECTORSIZE, &retlen, + movebuf); if (ret < 0) { - ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize * - BlockMap[block]) + (block * SECTORSIZE), - SECTORSIZE, &retlen, movebuf); + ret = mtd->read(mtd, + (inftl->EraseSize * BlockMap[block]) + + (block * SECTORSIZE), SECTORSIZE, + &retlen, movebuf); if (ret != -EIO) DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went " "away on retry?\n"); @@ -415,6 +417,7 @@ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block) unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE); unsigned int thisEUN, writeEUN, prev_block, status; unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1); + struct mtd_info *mtd = inftl->mbd.mtd; struct inftl_oob oob; struct inftl_bci bci; unsigned char anac, nacs, parity; @@ -434,8 +437,8 @@ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block) silly = MAX_LOOPS; while (thisEUN <= inftl->lastEUN) { - MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + - blockofs, 8, &retlen, (char *)&bci); + mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) + + blockofs, 8, &retlen, (char *)&bci); status = bci.Status | bci.Status1; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in " @@ -522,8 +525,8 @@ hitused: nacs = 0; thisEUN = inftl->VUtable[thisVUC]; if (thisEUN != BLOCK_NIL) { - MTD_READOOB(inftl->mbd.mtd, thisEUN * inftl->EraseSize - + 8, 8, &retlen, (char *)&oob.u); + mtd->read_oob(mtd, thisEUN * inftl->EraseSize + + 8, 8, &retlen, (char *)&oob.u); anac = oob.u.a.ANAC + 1; nacs = oob.u.a.NACs + 1; } @@ -544,8 +547,8 @@ hitused: oob.u.a.parityPerField = parity; oob.u.a.discarded = 0xaa; - MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 8, 8, - &retlen, (char *)&oob.u); + mtd->write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8, + &retlen, (char *)&oob.u); /* Also back up header... */ oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC); @@ -555,8 +558,8 @@ hitused: oob.u.b.parityPerField = parity; oob.u.b.discarded = 0xaa; - MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + - SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u); + mtd->write_oob(mtd, writeEUN * inftl->EraseSize + + SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u); inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC]; inftl->VUtable[thisVUC] = writeEUN; @@ -576,6 +579,7 @@ hitused: */ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) { + struct mtd_info *mtd = inftl->mbd.mtd; unsigned char BlockUsed[MAX_SECTORS_PER_UNIT]; unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; unsigned int thisEUN, status; @@ -606,9 +610,9 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) if (BlockUsed[block] || BlockDeleted[block]) continue; - if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) - + (block * SECTORSIZE), 8 , &retlen, - (char *)&bci) < 0) + if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) + + (block * SECTORSIZE), 8 , &retlen, + (char *)&bci) < 0) status = SECTOR_IGNORE; else status = bci.Status | bci.Status1; @@ -697,6 +701,7 @@ static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block) { unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)]; unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); + struct mtd_info *mtd = inftl->mbd.mtd; unsigned int status; int silly = MAX_LOOPS; size_t retlen; @@ -706,8 +711,8 @@ static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block) "block=%d)\n", inftl, block); while (thisEUN < inftl->nb_blocks) { - if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + - blockofs, 8, &retlen, (char *)&bci) < 0) + if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) + + blockofs, 8, &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; else status = bci.Status | bci.Status1; @@ -741,10 +746,10 @@ foundit: if (thisEUN != BLOCK_NIL) { loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; - if (MTD_READOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0) + if (mtd->read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) return -EIO; bci.Status = bci.Status1 = SECTOR_DELETED; - if (MTD_WRITEOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0) + if (mtd->write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) return -EIO; INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE)); } @@ -805,6 +810,7 @@ static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, struct INFTLrecord *inftl = (void *)mbd; unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)]; unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); + struct mtd_info *mtd = inftl->mbd.mtd; unsigned int status; int silly = MAX_LOOPS; struct inftl_bci bci; @@ -814,8 +820,8 @@ static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, "buffer=%p)\n", inftl, block, buffer); while (thisEUN < inftl->nb_blocks) { - if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + - blockofs, 8, &retlen, (char *)&bci) < 0) + if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) + + blockofs, 8, &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; else status = bci.Status | bci.Status1; @@ -853,8 +859,7 @@ foundit: } else { size_t retlen; loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; - if (MTD_READ(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen, - buffer)) + if (mtd->read(mtd, ptr, SECTORSIZE, &retlen, buffer)) return -EIO; } return 0; diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index f89a03795e76..b4cda7d0a52d 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -57,6 +57,7 @@ static int find_boot_record(struct INFTLrecord *inftl) unsigned int i, block; u8 buf[SECTORSIZE]; struct INFTLMediaHeader *mh = &inftl->MediaHdr; + struct mtd_info *mtd = inftl->mbd.mtd; struct INFTLPartition *ip; size_t retlen; @@ -80,8 +81,8 @@ static int find_boot_record(struct INFTLrecord *inftl) * Check for BNAND header first. Then whinge if it's found * but later checks fail. */ - ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize, - SECTORSIZE, &retlen, buf); + ret = mtd->read(mtd, block * inftl->EraseSize, + SECTORSIZE, &retlen, buf); /* We ignore ret in case the ECC of the MediaHeader is invalid (which is apparently acceptable) */ if (retlen != SECTORSIZE) { @@ -106,8 +107,9 @@ static int find_boot_record(struct INFTLrecord *inftl) } /* To be safer with BIOS, also use erase mark as discriminant */ - if ((ret = MTD_READOOB(inftl->mbd.mtd, block * inftl->EraseSize + - SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0)) { + if ((ret = mtd->read_oob(mtd, block * inftl->EraseSize + + SECTORSIZE + 8, 8, &retlen, + (char *)&h1) < 0)) { printk(KERN_WARNING "INFTL: ANAND header found at " "0x%x in mtd%d, but OOB data read failed " "(err %d)\n", block * inftl->EraseSize, @@ -123,8 +125,8 @@ static int find_boot_record(struct INFTLrecord *inftl) memcpy(mh, buf, sizeof(struct INFTLMediaHeader)); /* Read the spare media header at offset 4096 */ - MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize + 4096, - SECTORSIZE, &retlen, buf); + mtd->read(mtd, block * inftl->EraseSize + 4096, + SECTORSIZE, &retlen, buf); if (retlen != SECTORSIZE) { printk(KERN_WARNING "INFTL: Unable to read spare " "Media Header\n"); @@ -233,7 +235,7 @@ static int find_boot_record(struct INFTLrecord *inftl) */ instr->addr = ip->Reserved0 * inftl->EraseSize; instr->len = inftl->EraseSize; - MTD_ERASE(inftl->mbd.mtd, instr); + mtd->erase(mtd, instr); } if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) { printk(KERN_WARNING "INFTL: Media Header " @@ -387,6 +389,7 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block) size_t retlen; struct inftl_unittail uci; struct erase_info *instr = &inftl->instr; + struct mtd_info *mtd = inftl->mbd.mtd; int physblock; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=%p," @@ -404,8 +407,9 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block) /* Erase one physical eraseblock at a time, even though the NAND api allows us to group them. This way we if we have a failure, we can mark only the failed block in the bbt. */ - for (physblock = 0; physblock < inftl->EraseSize; physblock += instr->len, instr->addr += instr->len) { - MTD_ERASE(inftl->mbd.mtd, instr); + for (physblock = 0; physblock < inftl->EraseSize; + physblock += instr->len, instr->addr += instr->len) { + mtd->erase(inftl->mbd.mtd, instr); if (instr->state == MTD_ERASE_FAILED) { printk(KERN_WARNING "INFTL: error while formatting block %d\n", @@ -414,10 +418,10 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block) } /* - * Check the "freeness" of Erase Unit before updating metadata. - * FixMe: is this check really necessary? Since we have check the - * return code after the erase operation. - */ + * Check the "freeness" of Erase Unit before updating metadata. + * FixMe: is this check really necessary? Since we have check + * the return code after the erase operation. + */ if (check_free_sectors(inftl, instr->addr, instr->len, 1) != 0) goto fail; } @@ -429,8 +433,7 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block) uci.Reserved[2] = 0; uci.Reserved[3] = 0; instr->addr = block * inftl->EraseSize + SECTORSIZE * 2; - if (MTD_WRITEOOB(inftl->mbd.mtd, instr->addr + - 8, 8, &retlen, (char *)&uci) < 0) + if (mtd->write_oob(mtd, instr->addr + 8, 8, &retlen, (char *)&uci) < 0) goto fail; return 0; fail: @@ -549,6 +552,7 @@ void INFTL_dumpVUchains(struct INFTLrecord *s) int INFTL_mount(struct INFTLrecord *s) { + struct mtd_info *mtd = s->mbd.mtd; unsigned int block, first_block, prev_block, last_block; unsigned int first_logical_block, logical_block, erase_mark; int chain_length, do_format_chain; @@ -607,10 +611,11 @@ int INFTL_mount(struct INFTLrecord *s) break; } - if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, - 8, &retlen, (char *)&h0) < 0 || - MTD_READOOB(s->mbd.mtd, block * s->EraseSize + - 2 * SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) { + if (mtd->read_oob(mtd, block * s->EraseSize + 8, + 8, &retlen, (char *)&h0) < 0 || + mtd->read_oob(mtd, block * s->EraseSize + + 2 * SECTORSIZE + 8, 8, &retlen, + (char *)&h1) < 0) { /* Should never happen? */ do_format_chain++; break; diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 20771b2a05e1..0994b5b2e331 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -190,7 +190,7 @@ int nettel_eraseconfig(void) set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&wait_q, &wait); - ret = MTD_ERASE(mtd, &nettel_erase); + ret = mtd->erase(mtd, &nettel_erase); if (ret) { set_current_state(TASK_RUNNING); remove_wait_queue(&wait_q, &wait); diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 8e50170137e0..9b0bc20e4d8d 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -71,7 +71,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&wait_q, &wait); - ret = MTD_ERASE(mtd, &erase); + ret = mtd->erase(mtd, &erase); if (ret) { set_current_state(TASK_RUNNING); remove_wait_queue(&wait_q, &wait); @@ -88,7 +88,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, * Next, writhe data to flash. */ - ret = MTD_WRITE (mtd, pos, len, &retlen, buf); + ret = mtd->write(mtd, pos, len, &retlen, buf); if (ret) return ret; if (retlen != len) @@ -138,7 +138,7 @@ static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos, mtd->name, pos, len); if (!sect_size) - return MTD_WRITE (mtd, pos, len, &retlen, buf); + return mtd->write(mtd, pos, len, &retlen, buf); while (len > 0) { unsigned long sect_start = (pos/sect_size)*sect_size; @@ -170,7 +170,8 @@ static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos, mtdblk->cache_offset != sect_start) { /* fill the cache with the current sector */ mtdblk->cache_state = STATE_EMPTY; - ret = MTD_READ(mtd, sect_start, sect_size, &retlen, mtdblk->cache_data); + ret = mtd->read(mtd, sect_start, sect_size, + &retlen, mtdblk->cache_data); if (ret) return ret; if (retlen != sect_size) @@ -207,7 +208,7 @@ static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos, mtd->name, pos, len); if (!sect_size) - return MTD_READ (mtd, pos, len, &retlen, buf); + return mtd->read(mtd, pos, len, &retlen, buf); while (len > 0) { unsigned long sect_start = (pos/sect_size)*sect_size; @@ -226,7 +227,7 @@ static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos, mtdblk->cache_offset == sect_start) { memcpy (buf, mtdblk->cache_data + offset, size); } else { - ret = MTD_READ (mtd, pos, size, &retlen, buf); + ret = mtd->read(mtd, pos, size, &retlen, buf); if (ret) return ret; if (retlen != size) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index e75ec5fe7760..b45e7747daa3 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -194,7 +194,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); break; default: - ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); + ret = mtd->read(mtd, *ppos, len, &retlen, kbuf); } /* Nand returns -EBADMSG on ecc errors, but it returns * the data. For our userspace tools it is important @@ -205,7 +205,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t if (!ret || (ret == -EBADMSG)) { *ppos += retlen; if (copy_to_user(buf, kbuf, retlen)) { - kfree(kbuf); + kfree(kbuf); return -EFAULT; } else diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index dd03349946c2..359533b33d9b 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -183,6 +183,7 @@ static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate ) static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock ) { + struct mtd_info *mtd = nftl->mbd.mtd; u16 BlockMap[MAX_SECTORS_PER_UNIT]; unsigned char BlockLastState[MAX_SECTORS_PER_UNIT]; unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT]; @@ -192,7 +193,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p unsigned int targetEUN; struct nftl_oob oob; int inplace = 1; - size_t retlen; + size_t retlen; memset(BlockMap, 0xff, sizeof(BlockMap)); memset(BlockFreeFound, 0, sizeof(BlockFreeFound)); @@ -208,21 +209,21 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p /* Scan to find the Erase Unit which holds the actual data for each 512-byte block within the Chain. */ - silly = MAX_LOOPS; + silly = MAX_LOOPS; targetEUN = BLOCK_NIL; while (thisEUN <= nftl->lastEUN ) { - unsigned int status, foldmark; + unsigned int status, foldmark; targetEUN = thisEUN; for (block = 0; block < nftl->EraseSize / 512; block ++) { - MTD_READOOB(nftl->mbd.mtd, - (thisEUN * nftl->EraseSize) + (block * 512), - 16 , &retlen, (char *)&oob); + mtd->read_oob(mtd, (thisEUN * nftl->EraseSize) + + (block * 512), 16 , &retlen, + (char *)&oob); if (block == 2) { - foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1; - if (foldmark == FOLD_MARK_IN_PROGRESS) { - DEBUG(MTD_DEBUG_LEVEL1, - "Write Inhibited on EUN %d\n", thisEUN); + foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1; + if (foldmark == FOLD_MARK_IN_PROGRESS) { + DEBUG(MTD_DEBUG_LEVEL1, + "Write Inhibited on EUN %d\n", thisEUN); inplace = 0; } else { /* There's no other reason not to do inplace, @@ -231,7 +232,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p inplace = 1; } } - status = oob.b.Status | oob.b.Status1; + status = oob.b.Status | oob.b.Status1; BlockLastState[block] = status; switch(status) { @@ -326,15 +327,15 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p return BLOCK_NIL; } } else { - /* We put a fold mark in the chain we are folding only if - we fold in place to help the mount check code. If we do - not fold in place, it is possible to find the valid - chain by selecting the longer one */ - oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS); - oob.u.c.unused = 0xffffffff; - MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, - 8, &retlen, (char *)&oob.u); - } + /* We put a fold mark in the chain we are folding only if we + fold in place to help the mount check code. If we do not fold in + place, it is possible to find the valid chain by selecting the + longer one */ + oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS); + oob.u.c.unused = 0xffffffff; + mtd->write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, + 8, &retlen, (char *)&oob.u); + } /* OK. We now know the location of every block in the Virtual Unit Chain, and the Erase Unit into which we are supposed to be copying. @@ -351,20 +352,20 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p continue; } - /* copy only in non free block (free blocks can only + /* copy only in non free block (free blocks can only happen in case of media errors or deleted blocks) */ - if (BlockMap[block] == BLOCK_NIL) - continue; - - ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), - 512, &retlen, movebuf); - if (ret < 0) { - ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) - + (block * 512), 512, &retlen, - movebuf); - if (ret != -EIO) - printk("Error went away on retry.\n"); - } + if (BlockMap[block] == BLOCK_NIL) + continue; + + ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), + 512, &retlen, movebuf); + if (ret < 0) { + ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + + (block * 512), 512, &retlen, + movebuf); + if (ret != -EIO) + printk("Error went away on retry.\n"); + } memset(&oob, 0xff, sizeof(struct nftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; @@ -374,13 +375,12 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p } - /* add the header so that it is now a valid chain */ - oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum - = cpu_to_le16(thisVUC); - oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; + /* add the header so that it is now a valid chain */ + oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); + oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; - MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 8, - 8, &retlen, (char *)&oob.u); + mtd->write_oob(mtd, (nftl->EraseSize * targetEUN) + 8, + 8, &retlen, (char *)&oob.u); /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */ @@ -397,18 +397,18 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) { unsigned int EUNtmp; - EUNtmp = nftl->ReplUnitTable[thisEUN]; + EUNtmp = nftl->ReplUnitTable[thisEUN]; - if (NFTL_formatblock(nftl, thisEUN) < 0) { + if (NFTL_formatblock(nftl, thisEUN) < 0) { /* could not erase : mark block as reserved */ nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED; - } else { + } else { /* correctly erased : mark it as free */ nftl->ReplUnitTable[thisEUN] = BLOCK_FREE; nftl->numfreeEUNs++; - } - thisEUN = EUNtmp; + } + thisEUN = EUNtmp; } /* Make this the new start of chain for thisVUC */ @@ -474,6 +474,7 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) { u16 lastEUN; u16 thisVUC = block / (nftl->EraseSize / 512); + struct mtd_info *mtd = nftl->mbd.mtd; unsigned int writeEUN; unsigned long blockofs = (block * 512) & (nftl->EraseSize -1); size_t retlen; @@ -490,21 +491,22 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) */ lastEUN = BLOCK_NIL; writeEUN = nftl->EUNtable[thisVUC]; - silly = MAX_LOOPS; + silly = MAX_LOOPS; while (writeEUN <= nftl->lastEUN) { struct nftl_bci bci; size_t retlen; - unsigned int status; + unsigned int status; lastEUN = writeEUN; - MTD_READOOB(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, - 8, &retlen, (char *)&bci); + mtd->read_oob(mtd, + (writeEUN * nftl->EraseSize) + blockofs, + 8, &retlen, (char *)&bci); DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n", block , writeEUN, le16_to_cpu(bci.Status)); - status = bci.Status | bci.Status1; + status = bci.Status | bci.Status1; switch(status) { case SECTOR_FREE: return writeEUN; @@ -575,10 +577,10 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) /* We've found a free block. Insert it into the chain. */ if (lastEUN != BLOCK_NIL) { - thisVUC |= 0x8000; /* It's a replacement block */ + thisVUC |= 0x8000; /* It's a replacement block */ } else { - /* The first block in a new chain */ - nftl->EUNtable[thisVUC] = writeEUN; + /* The first block in a new chain */ + nftl->EUNtable[thisVUC] = writeEUN; } /* set up the actual EUN we're writing into */ @@ -586,29 +588,29 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) nftl->ReplUnitTable[writeEUN] = BLOCK_NIL; /* ... and on the flash itself */ - MTD_READOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8, - &retlen, (char *)&oob.u); + mtd->read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, + &retlen, (char *)&oob.u); oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); - MTD_WRITEOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8, - &retlen, (char *)&oob.u); + mtd->write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, + &retlen, (char *)&oob.u); - /* we link the new block to the chain only after the + /* we link the new block to the chain only after the block is ready. It avoids the case where the chain could point to a free block */ - if (lastEUN != BLOCK_NIL) { + if (lastEUN != BLOCK_NIL) { /* Both in our cache... */ nftl->ReplUnitTable[lastEUN] = writeEUN; /* ... and on the flash itself */ - MTD_READOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8, - 8, &retlen, (char *)&oob.u); + mtd->read_oob(mtd, (lastEUN * nftl->EraseSize) + 8, + 8, &retlen, (char *)&oob.u); oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = cpu_to_le16(writeEUN); - MTD_WRITEOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8, - 8, &retlen, (char *)&oob.u); + mtd->write_oob(mtd, (lastEUN * nftl->EraseSize) + 8, + 8, &retlen, (char *)&oob.u); } return writeEUN; @@ -652,20 +654,22 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, char *buffer) { struct NFTLrecord *nftl = (void *)mbd; + struct mtd_info *mtd = nftl->mbd.mtd; u16 lastgoodEUN; u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)]; unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); - unsigned int status; + unsigned int status; int silly = MAX_LOOPS; - size_t retlen; - struct nftl_bci bci; + size_t retlen; + struct nftl_bci bci; lastgoodEUN = BLOCK_NIL; - if (thisEUN != BLOCK_NIL) { + if (thisEUN != BLOCK_NIL) { while (thisEUN < nftl->nb_blocks) { - if (MTD_READOOB(nftl->mbd.mtd, (thisEUN * nftl->EraseSize) + blockofs, - 8, &retlen, (char *)&bci) < 0) + if (mtd->read_oob(mtd, (thisEUN * nftl->EraseSize) + + blockofs, 8, &retlen, + (char *)&bci) < 0) status = SECTOR_IGNORE; else status = bci.Status | bci.Status1; @@ -695,7 +699,7 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, } thisEUN = nftl->ReplUnitTable[thisEUN]; } - } + } the_end: if (lastgoodEUN == BLOCK_NIL) { @@ -704,7 +708,7 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, } else { loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; size_t retlen; - if (MTD_READ(nftl->mbd.mtd, ptr, 512, &retlen, buffer)) + if (mtd->read(mtd, ptr, 512, &retlen, buffer)) return -EIO; } return 0; diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 90e5e7e97fdc..521b07cd2326 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -45,6 +45,7 @@ static int find_boot_record(struct NFTLrecord *nftl) size_t retlen; u8 buf[SECTORSIZE]; struct NFTLMediaHeader *mh = &nftl->MediaHdr; + struct mtd_info *mtd = nftl->mbd.mtd; unsigned int i; /* Assume logical EraseSize == physical erasesize for starting the scan. @@ -65,7 +66,8 @@ static int find_boot_record(struct NFTLrecord *nftl) /* Check for ANAND header first. Then can whinge if it's found but later checks fail */ - ret = MTD_READ(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf); + ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE, + &retlen, buf); /* We ignore ret in case the ECC of the MediaHeader is invalid (which is apparently acceptable) */ if (retlen != SECTORSIZE) { @@ -90,8 +92,9 @@ static int find_boot_record(struct NFTLrecord *nftl) } /* To be safer with BIOS, also use erase mark as discriminant */ - if ((ret = MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, - 8, &retlen, (char *)&h1) < 0)) { + if ((ret = mtd->read_oob(mtd, block * nftl->EraseSize + + SECTORSIZE + 8, 8, &retlen, + (char *)&h1) < 0)) { printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n", block * nftl->EraseSize, nftl->mbd.mtd->index, ret); continue; @@ -109,8 +112,8 @@ static int find_boot_record(struct NFTLrecord *nftl) } /* Finally reread to check ECC */ - if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, - &retlen, buf, (char *)&oob, NULL) < 0)) { + if ((ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE, + &retlen, buf) < 0)) { printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n", block * nftl->EraseSize, nftl->mbd.mtd->index, ret); continue; @@ -228,9 +231,9 @@ device is already correct. The new DiskOnChip driver already scanned the bad block table. Just query it. if ((i & (SECTORSIZE - 1)) == 0) { /* read one sector for every SECTORSIZE of blocks */ - if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize + - i + SECTORSIZE, SECTORSIZE, &retlen, buf, - (char *)&oob, NULL)) < 0) { + if ((ret = mtd->read(nftl->mbd.mtd, block * nftl->EraseSize + + i + SECTORSIZE, SECTORSIZE, &retlen, + buf)) < 0) { printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n", ret); kfree(nftl->ReplUnitTable); @@ -305,10 +308,11 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) unsigned int nb_erases, erase_mark; struct nftl_uci1 uci; struct erase_info *instr = &nftl->instr; + struct mtd_info *mtd = nftl->mbd.mtd; /* Read the Unit Control Information #1 for Wear-Leveling */ - if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, - 8, &retlen, (char *)&uci) < 0) + if (mtd->read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, + 8, &retlen, (char *)&uci) < 0) goto default_uci1; erase_mark = le16_to_cpu ((uci.EraseMark | uci.EraseMark1)); @@ -325,7 +329,7 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) instr->mtd = nftl->mbd.mtd; instr->addr = block * nftl->EraseSize; instr->len = nftl->EraseSize; - MTD_ERASE(nftl->mbd.mtd, instr); + mtd->erase(mtd, instr); if (instr->state == MTD_ERASE_FAILED) { printk("Error while formatting block %d\n", block); @@ -347,8 +351,8 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) goto fail; uci.WearInfo = le32_to_cpu(nb_erases); - if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, - &retlen, (char *)&uci) < 0) + if (mtd->write_oob(mtd, block * nftl->EraseSize + SECTORSIZE + + 8, 8, &retlen, (char *)&uci) < 0) goto fail; return 0; fail: @@ -369,6 +373,7 @@ fail: * case. */ static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_block) { + struct mtd_info *mtd = nftl->mbd.mtd; unsigned int block, i, status; struct nftl_bci bci; int sectors_per_block; @@ -378,8 +383,9 @@ static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_b block = first_block; for (;;) { for (i = 0; i < sectors_per_block; i++) { - if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i * SECTORSIZE, - 8, &retlen, (char *)&bci) < 0) + if (mtd->read_oob(mtd, + block * nftl->EraseSize + i * SECTORSIZE, + 8, &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; else status = bci.Status | bci.Status1; @@ -398,9 +404,10 @@ static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_b /* sector not free actually : mark it as SECTOR_IGNORE */ bci.Status = SECTOR_IGNORE; bci.Status1 = SECTOR_IGNORE; - MTD_WRITEOOB(nftl->mbd.mtd, - block * nftl->EraseSize + i * SECTORSIZE, - 8, &retlen, (char *)&bci); + mtd->write_oob(mtd, block * + nftl->EraseSize + + i * SECTORSIZE, 8, + &retlen, (char *)&bci); } break; default: @@ -485,13 +492,14 @@ static void format_chain(struct NFTLrecord *nftl, unsigned int first_block) * 1. */ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) { + struct mtd_info *mtd = nftl->mbd.mtd; struct nftl_uci1 h1; unsigned int erase_mark; size_t retlen; /* check erase mark. */ - if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, - &retlen, (char *)&h1) < 0) + if (mtd->read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&h1) < 0) return -1; erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1)); @@ -505,8 +513,9 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) h1.EraseMark = cpu_to_le16(ERASE_MARK); h1.EraseMark1 = cpu_to_le16(ERASE_MARK); h1.WearInfo = cpu_to_le32(0); - if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, - &retlen, (char *)&h1) < 0) + if (mtd->write_oob(mtd, + block * nftl->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&h1) < 0) return -1; } else { #if 0 @@ -517,8 +526,8 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) SECTORSIZE, 0) != 0) return -1; - if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i, - 16, &retlen, buf) < 0) + if (mtd->read_oob(mtd, block * nftl->EraseSize + i, + 16, &retlen, buf) < 0) return -1; if (i == SECTORSIZE) { /* skip erase mark */ @@ -544,11 +553,12 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) */ static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block) { + struct mtd_info *mtd = nftl->mbd.mtd; struct nftl_uci2 uci; size_t retlen; - if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, - 8, &retlen, (char *)&uci) < 0) + if (mtd->read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, + 8, &retlen, (char *)&uci) < 0) return 0; return le16_to_cpu((uci.FoldMark | uci.FoldMark1)); @@ -562,6 +572,7 @@ int NFTL_mount(struct NFTLrecord *s) int chain_length, do_format_chain; struct nftl_uci0 h0; struct nftl_uci1 h1; + struct mtd_info *mtd = s->mbd.mtd; size_t retlen; /* search for NFTL MediaHeader and Spare NFTL Media Header */ @@ -586,10 +597,13 @@ int NFTL_mount(struct NFTLrecord *s) for (;;) { /* read the block header. If error, we format the chain */ - if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, 8, - &retlen, (char *)&h0) < 0 || - MTD_READOOB(s->mbd.mtd, block * s->EraseSize + SECTORSIZE + 8, 8, - &retlen, (char *)&h1) < 0) { + if (mtd->read_oob(mtd, + block * s->EraseSize + 8, 8, + &retlen, (char *)&h0) < 0 || + mtd->read_oob(mtd, + block * s->EraseSize + + SECTORSIZE + 8, 8, + &retlen, (char *)&h1) < 0) { s->ReplUnitTable[block] = BLOCK_NIL; do_format_chain = 1; break; diff --git a/fs/jffs/intrep.c b/fs/jffs/intrep.c index 0ef207dfaf6f..5371a403130a 100644 --- a/fs/jffs/intrep.c +++ b/fs/jffs/intrep.c @@ -247,7 +247,7 @@ flash_safe_read(struct mtd_info *mtd, loff_t from, D3(printk(KERN_NOTICE "flash_safe_read(%p, %08x, %p, %08x)\n", mtd, (unsigned int) from, buf, count)); - res = MTD_READ(mtd, from, count, &retlen, buf); + res = mtd->read(mtd, from, count, &retlen, buf); if (retlen != count) { panic("Didn't read all bytes in flash_safe_read(). Returned %d\n", res); } @@ -262,7 +262,7 @@ flash_read_u32(struct mtd_info *mtd, loff_t from) __u32 ret; int res; - res = MTD_READ(mtd, from, 4, &retlen, (unsigned char *)&ret); + res = mtd->read(mtd, from, 4, &retlen, (unsigned char *)&ret); if (retlen != 4) { printk("Didn't read all bytes in flash_read_u32(). Returned %d\n", res); return 0; @@ -282,7 +282,7 @@ flash_safe_write(struct mtd_info *mtd, loff_t to, D3(printk(KERN_NOTICE "flash_safe_write(%p, %08x, %p, %08x)\n", mtd, (unsigned int) to, buf, count)); - res = MTD_WRITE(mtd, to, count, &retlen, buf); + res = mtd->write(mtd, to, count, &retlen, buf); if (retlen != count) { printk("Didn't write all bytes in flash_safe_write(). Returned %d\n", res); } @@ -300,9 +300,9 @@ flash_safe_writev(struct mtd_info *mtd, const struct kvec *vecs, D3(printk(KERN_NOTICE "flash_safe_writev(%p, %08x, %p)\n", mtd, (unsigned int) to, vecs)); - + if (mtd->writev) { - res = MTD_WRITEV(mtd, vecs, iovec_cnt, to, &retlen); + res = mtd->writev(mtd, vecs, iovec_cnt, to, &retlen); return res ? res : retlen; } /* Not implemented writev. Repeatedly use write - on the not so @@ -312,7 +312,8 @@ flash_safe_writev(struct mtd_info *mtd, const struct kvec *vecs, retlen=0; for (i=0; !res && iwrite(mtd, to, vecs[i].iov_len, &retlen_a, + vecs[i].iov_base); if (retlen_a != vecs[i].iov_len) { printk("Didn't write all bytes in flash_safe_writev(). Returned %d\n", res); if (i != iovec_cnt-1) @@ -393,7 +394,7 @@ flash_erase_region(struct mtd_info *mtd, loff_t start, set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&wait_q, &wait); - if (MTD_ERASE(mtd, erase) < 0) { + if (mtd->erase(mtd, erase) < 0) { set_current_state(TASK_RUNNING); remove_wait_queue(&wait_q, &wait); kfree(erase); diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 48a9df21ab11..4970c2e96fbf 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -199,20 +199,6 @@ int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen); -#define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args) -#define MTD_POINT(mtd, a,b,c,d) (*(mtd->point))(mtd, a,b,c, (u_char **)(d)) -#define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg) -#define MTD_READ(mtd, args...) (*(mtd->read))(mtd, args) -#define MTD_WRITE(mtd, args...) (*(mtd->write))(mtd, args) -#define MTD_READV(mtd, args...) (*(mtd->readv))(mtd, args) -#define MTD_WRITEV(mtd, args...) (*(mtd->writev))(mtd, args) -#define MTD_READECC(mtd, args...) (*(mtd->read_ecc))(mtd, args) -#define MTD_WRITEECC(mtd, args...) (*(mtd->write_ecc))(mtd, args) -#define MTD_READOOB(mtd, args...) (*(mtd->read_oob))(mtd, args) -#define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args) -#define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd); } while (0) - - #ifdef CONFIG_MTD_PARTITIONS void mtd_erase_callback(struct erase_info *instr); #else @@ -233,7 +219,7 @@ static inline void mtd_erase_callback(struct erase_info *instr) #ifdef CONFIG_MTD_DEBUG #define DEBUG(n, args...) \ - do { \ + do { \ if (n <= CONFIG_MTD_DEBUG_VERBOSE) \ printk(KERN_INFO args); \ } while(0) -- cgit v1.2.3 From 8593fbc68b0df1168995de76d1af38eb62fd6b62 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 29 May 2006 03:26:58 +0200 Subject: [MTD] Rework the out of band handling completely Hopefully the last iteration on this! The handling of out of band data on NAND was accompanied by tons of fruitless discussions and halfarsed patches to make it work for a particular problem. Sufficiently annoyed by I all those "I know it better" mails and the resonable amount of discarded "it solves my problem" patches, I finally decided to go for the big rework. After removing the _ecc variants of mtd read/write functions the solution to satisfy the various requirements was to refactor the read/write _oob functions in mtd. The major change is that read/write_oob now takes a pointer to an operation descriptor structure "struct mtd_oob_ops".instead of having a function with at least seven arguments. read/write_oob which should probably renamed to a more descriptive name, can do the following tasks: - read/write out of band data - read/write data content and out of band data - read/write raw data content and out of band data (ecc disabled) struct mtd_oob_ops has a mode field, which determines the oob handling mode. Aside of the MTD_OOB_RAW mode, which is intended to be especially for diagnostic purposes and some internal functions e.g. bad block table creation, the other two modes are for mtd clients: MTD_OOB_PLACE puts/gets the given oob data exactly to/from the place which is described by the ooboffs and ooblen fields of the mtd_oob_ops strcuture. It's up to the caller to make sure that the byte positions are not used by the ECC placement algorithms. MTD_OOB_AUTO puts/gets the given oob data automaticaly to/from the places in the out of band area which are described by the oobfree tuples in the ecclayout data structre which is associated to the devicee. The decision whether data plus oob or oob only handling is done depends on the setting of the datbuf member of the data structure. When datbuf == NULL then the internal read/write_oob functions are selected, otherwise the read/write data routines are invoked. Tested on a few platforms with all variants. Please be aware of possible regressions for your particular device / application scenario Disclaimer: Any whining will be ignored from those who just contributed "hot air blurb" and never sat down to tackle the underlying problem of the mess in the NAND driver grown over time and the big chunk of work to fix up the existing users. The problem was not the holiness of the existing MTD interfaces. The problems was the lack of time to go for the big overhaul. It's easy to add more mess to the existing one, but it takes alot of effort to go for a real solution. Improvements and bugfixes are welcome! Signed-off-by: Thomas Gleixner --- drivers/mtd/devices/doc2000.c | 39 ++- drivers/mtd/devices/doc2001.c | 34 ++- drivers/mtd/devices/doc2001plus.c | 34 ++- drivers/mtd/inftlcore.c | 111 ++++++-- drivers/mtd/inftlmount.c | 27 +- drivers/mtd/mtdchar.c | 59 ++-- drivers/mtd/mtdconcat.c | 90 +++--- drivers/mtd/mtdpart.c | 29 +- drivers/mtd/nand/nand_base.c | 542 ++++++++++++++++++++++--------------- drivers/mtd/nand/nand_bbt.c | 188 +++++++++---- drivers/mtd/nftlcore.c | 92 +++++-- drivers/mtd/nftlmount.c | 29 +- drivers/mtd/onenand/onenand_base.c | 46 +++- drivers/mtd/onenand/onenand_bbt.c | 7 +- fs/jffs2/jffs2_fs_sb.h | 1 + fs/jffs2/wbuf.c | 230 ++++++++-------- include/linux/mtd/mtd.h | 50 +++- include/linux/mtd/nand.h | 10 +- 18 files changed, 1028 insertions(+), 590 deletions(-) (limited to 'include') diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index d9ba1ee658f6..c54e40464d82 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -59,10 +59,10 @@ static int doc_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 doc_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 doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, u_char *buf); -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, const u_char *buf); +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops); +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops); static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); @@ -959,12 +959,18 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, return 0; } -static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t * retlen, u_char * buf) +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops) { struct DiskOnChip *this = mtd->priv; int len256 = 0, ret; struct Nand *mychip; + uint8_t *buf = ops->oobbuf; + size_t len = ops->len; + + BUG_ON(ops->mode != MTD_OOB_PLACE); + + ofs += ops->ooboffs; mutex_lock(&this->lock); @@ -1005,7 +1011,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, DoC_ReadBuf(this, &buf[len256], len - len256); - *retlen = len; + ops->retlen = len; /* Reading the full OOB data drops us off of the end of the page, * causing the flash device to go into busy mode, so we need * to wait until ready 11.4.1 and Toshiba TC58256FT docs */ @@ -1120,17 +1126,20 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, } -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t * retlen, const u_char * buf) +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops) { - struct DiskOnChip *this = mtd->priv; - int ret; + struct DiskOnChip *this = mtd->priv; + int ret; - mutex_lock(&this->lock); - ret = doc_write_oob_nolock(mtd, ofs, len, retlen, buf); + BUG_ON(ops->mode != MTD_OOB_PLACE); + + mutex_lock(&this->lock); + ret = doc_write_oob_nolock(mtd, ofs + ops->ooboffs, ops->len, + &ops->retlen, ops->oobbuf); - mutex_unlock(&this->lock); - return ret; + mutex_unlock(&this->lock); + return ret; } static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index 579c0b570ae5..0cf022a69e65 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -43,10 +43,10 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, static int doc_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 doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, u_char *buf); -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, const u_char *buf); +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops); +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops); static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); static struct mtd_info *docmillist = NULL; @@ -662,8 +662,8 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, return ret; } -static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, u_char *buf) +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops) { #ifndef USE_MEMCPY int i; @@ -672,6 +672,12 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, struct DiskOnChip *this = mtd->priv; void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; + uint8_t *buf = ops->oobbuf; + size_t len = ops->len; + + BUG_ON(ops->mode != MTD_OOB_PLACE); + + ofs += ops->ooboffs; /* Find the chip which is to be used and select it */ if (this->curfloor != mychip->floor) { @@ -708,13 +714,13 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, #endif buf[len - 1] = ReadDOC(docptr, LastDataRead); - *retlen = len; + ops->retlen = len; return 0; } -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, const u_char *buf) +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops) { #ifndef USE_MEMCPY int i; @@ -724,6 +730,12 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, struct DiskOnChip *this = mtd->priv; void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; + uint8_t *buf = ops->oobbuf; + size_t len = ops->len; + + BUG_ON(ops->mode != MTD_OOB_PLACE); + + ofs += ops->ooboffs; /* Find the chip which is to be used and select it */ if (this->curfloor != mychip->floor) { @@ -775,12 +787,12 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, if (ReadDOC(docptr, Mil_CDSN_IO) & 1) { printk("Error programming oob data\n"); /* FIXME: implement Bad Block Replacement (in nftl.c ??) */ - *retlen = 0; + ops->retlen = 0; ret = -EIO; } dummy = ReadDOC(docptr, LastDataRead); - *retlen = len; + ops->retlen = len; return ret; } diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index 1ee0c0dcb53b..66cb1e50469a 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -47,10 +47,10 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, static int doc_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 doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, u_char *buf); -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, const u_char *buf); +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops); +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops); static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); static struct mtd_info *docmilpluslist = NULL; @@ -868,14 +868,20 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, return ret; } -static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, u_char *buf) +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops) { loff_t fofs, base; struct DiskOnChip *this = mtd->priv; void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; size_t i, size, got, want; + uint8_t *buf = ops->oobbuf; + size_t len = ops->len; + + BUG_ON(ops->mode != MTD_OOB_PLACE); + + ofs += ops->ooboffs; DoC_CheckASIC(docptr); @@ -941,12 +947,12 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, /* Disable flash internally */ WriteDOC(0, docptr, Mplus_FlashSelect); - *retlen = len; + ops->retlen = len; return 0; } -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, const u_char *buf) +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops) { volatile char dummy; loff_t fofs, base; @@ -955,6 +961,12 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, struct Nand *mychip = &this->chips[ofs >> this->chipshift]; size_t i, size, got, want; int ret = 0; + uint8_t *buf = ops->oobbuf; + size_t len = ops->len; + + BUG_ON(ops->mode != MTD_OOB_PLACE); + + ofs += ops->ooboffs; DoC_CheckASIC(docptr); @@ -1030,7 +1042,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, printk("MTD: Error 0x%x programming oob at 0x%x\n", dummy, (int)ofs); /* FIXME: implement Bad Block Replacement */ - *retlen = 0; + ops->retlen = 0; ret = -EIO; } dummy = ReadDOC(docptr, Mplus_LastDataRead); @@ -1043,7 +1055,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, /* Disable flash internally */ WriteDOC(0, docptr, Mplus_FlashSelect); - *retlen = len; + ops->retlen = len; return ret; } diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index 3396f0e1ac5f..efb1a95aa0a0 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -150,6 +150,69 @@ static void inftl_remove_dev(struct mtd_blktrans_dev *dev) * Actual INFTL access routines. */ +/* + * Read oob data from flash + */ +int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf) +{ + struct mtd_oob_ops ops; + int res; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = offs & (mtd->writesize - 1); + ops.ooblen = len; + ops.oobbuf = buf; + ops.datbuf = NULL; + ops.len = len; + + res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + *retlen = ops.retlen; + return res; +} + +/* + * Write oob data to flash + */ +int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf) +{ + struct mtd_oob_ops ops; + int res; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = offs & (mtd->writesize - 1); + ops.ooblen = len; + ops.oobbuf = buf; + ops.datbuf = NULL; + ops.len = len; + + res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + *retlen = ops.retlen; + return res; +} + +/* + * Write data and oob to flash + */ +static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf, uint8_t *oob) +{ + struct mtd_oob_ops ops; + int res; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = offs; + ops.ooblen = mtd->oobsize; + ops.oobbuf = oob; + ops.datbuf = buf; + ops.len = len; + + res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + *retlen = ops.retlen; + return res; +} + /* * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition. * This function is used when the give Virtual Unit Chain. @@ -227,9 +290,9 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned if ((BlockMap[block] != 0xffff) || BlockDeleted[block]) continue; - if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) - + (block * SECTORSIZE), 16 , &retlen, - (char *)&oob) < 0) + if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + + (block * SECTORSIZE), 16, &retlen, + (char *)&oob) < 0) status = SECTOR_IGNORE; else status = oob.b.Status | oob.b.Status1; @@ -304,9 +367,9 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned memset(&oob, 0xff, sizeof(struct inftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; - nand_write_raw(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + - (block * SECTORSIZE), SECTORSIZE, &retlen, - movebuf, (char *)&oob); + inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + + (block * SECTORSIZE), SECTORSIZE, &retlen, + movebuf, (char *)&oob); } /* @@ -437,8 +500,8 @@ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block) silly = MAX_LOOPS; while (thisEUN <= inftl->lastEUN) { - mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) + - blockofs, 8, &retlen, (char *)&bci); + inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + + blockofs, 8, &retlen, (char *)&bci); status = bci.Status | bci.Status1; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in " @@ -525,8 +588,8 @@ hitused: nacs = 0; thisEUN = inftl->VUtable[thisVUC]; if (thisEUN != BLOCK_NIL) { - mtd->read_oob(mtd, thisEUN * inftl->EraseSize - + 8, 8, &retlen, (char *)&oob.u); + inftl_read_oob(mtd, thisEUN * inftl->EraseSize + + 8, 8, &retlen, (char *)&oob.u); anac = oob.u.a.ANAC + 1; nacs = oob.u.a.NACs + 1; } @@ -547,8 +610,8 @@ hitused: oob.u.a.parityPerField = parity; oob.u.a.discarded = 0xaa; - mtd->write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8, - &retlen, (char *)&oob.u); + inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8, + &retlen, (char *)&oob.u); /* Also back up header... */ oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC); @@ -558,8 +621,8 @@ hitused: oob.u.b.parityPerField = parity; oob.u.b.discarded = 0xaa; - mtd->write_oob(mtd, writeEUN * inftl->EraseSize + - SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u); + inftl_write_oob(mtd, writeEUN * inftl->EraseSize + + SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u); inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC]; inftl->VUtable[thisVUC] = writeEUN; @@ -610,8 +673,8 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) if (BlockUsed[block] || BlockDeleted[block]) continue; - if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) - + (block * SECTORSIZE), 8 , &retlen, + if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + + (block * SECTORSIZE), 8 , &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; else @@ -711,8 +774,8 @@ static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block) "block=%d)\n", inftl, block); while (thisEUN < inftl->nb_blocks) { - if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) + - blockofs, 8, &retlen, (char *)&bci) < 0) + if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + + blockofs, 8, &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; else status = bci.Status | bci.Status1; @@ -746,10 +809,10 @@ foundit: if (thisEUN != BLOCK_NIL) { loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; - if (mtd->read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) + if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) return -EIO; bci.Status = bci.Status1 = SECTOR_DELETED; - if (mtd->write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) + if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) return -EIO; INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE)); } @@ -790,9 +853,9 @@ static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, memset(&oob, 0xff, sizeof(struct inftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; - nand_write_raw(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + - blockofs, SECTORSIZE, &retlen, (char *)buffer, - (char *)&oob); + inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + + blockofs, SECTORSIZE, &retlen, (char *)buffer, + (char *)&oob); /* * need to write SECTOR_USED flags since they are not written * in mtd_writeecc @@ -820,7 +883,7 @@ static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, "buffer=%p)\n", inftl, block, buffer); while (thisEUN < inftl->nb_blocks) { - if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) + + if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + blockofs, 8, &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; else diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index b4cda7d0a52d..8f6006f1a519 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -43,6 +43,11 @@ char inftlmountrev[]="$Revision: 1.18 $"; +extern int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); +extern int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); + /* * find_boot_record: Find the INFTL Media Header and its Spare copy which * contains the various device information of the INFTL partition and @@ -107,9 +112,9 @@ static int find_boot_record(struct INFTLrecord *inftl) } /* To be safer with BIOS, also use erase mark as discriminant */ - if ((ret = mtd->read_oob(mtd, block * inftl->EraseSize + - SECTORSIZE + 8, 8, &retlen, - (char *)&h1) < 0)) { + if ((ret = inftl_read_oob(mtd, block * inftl->EraseSize + + SECTORSIZE + 8, 8, &retlen, + (char *)&h1) < 0)) { printk(KERN_WARNING "INFTL: ANAND header found at " "0x%x in mtd%d, but OOB data read failed " "(err %d)\n", block * inftl->EraseSize, @@ -363,8 +368,8 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, return -1; if (check_oob) { - if(mtd->read_oob(mtd, address, mtd->oobsize, - &retlen, &buf[SECTORSIZE]) < 0) + if(inftl_read_oob(mtd, address, mtd->oobsize, + &retlen, &buf[SECTORSIZE]) < 0) return -1; if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0) return -1; @@ -433,7 +438,7 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block) uci.Reserved[2] = 0; uci.Reserved[3] = 0; instr->addr = block * inftl->EraseSize + SECTORSIZE * 2; - if (mtd->write_oob(mtd, instr->addr + 8, 8, &retlen, (char *)&uci) < 0) + if (inftl_write_oob(mtd, instr->addr + 8, 8, &retlen, (char *)&uci) < 0) goto fail; return 0; fail: @@ -611,11 +616,11 @@ int INFTL_mount(struct INFTLrecord *s) break; } - if (mtd->read_oob(mtd, block * s->EraseSize + 8, - 8, &retlen, (char *)&h0) < 0 || - mtd->read_oob(mtd, block * s->EraseSize + - 2 * SECTORSIZE + 8, 8, &retlen, - (char *)&h1) < 0) { + if (inftl_read_oob(mtd, block * s->EraseSize + 8, + 8, &retlen, (char *)&h0) < 0 || + inftl_read_oob(mtd, block * s->EraseSize + + 2 * SECTORSIZE + 8, 8, &retlen, + (char *)&h1) < 0) { /* Should never happen? */ do_format_chain++; break; diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index b45e7747daa3..7522fc3a2827 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -408,8 +408,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, case MEMWRITEOOB: { struct mtd_oob_buf buf; - void *databuf; - ssize_t retlen; + struct mtd_oob_ops ops; if(!(file->f_mode & 2)) return -EPERM; @@ -417,7 +416,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) return -EFAULT; - if (buf.length > 0x4096) + if (buf.length > 4096) return -EINVAL; if (!mtd->write_oob) @@ -429,21 +428,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file, if (ret) return ret; - databuf = kmalloc(buf.length, GFP_KERNEL); - if (!databuf) + ops.len = buf.length; + ops.ooblen = mtd->oobsize; + ops.ooboffs = buf.start & (mtd->oobsize - 1); + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + if (ops.ooboffs && ops.len > (ops.ooblen - ops.ooboffs)) + return -EINVAL; + + ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); + if (!ops.oobbuf) return -ENOMEM; - if (copy_from_user(databuf, buf.ptr, buf.length)) { - kfree(databuf); + if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) { + kfree(ops.oobbuf); return -EFAULT; } - ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf); + buf.start &= ~(mtd->oobsize - 1); + ret = mtd->write_oob(mtd, buf.start, &ops); - if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t))) + if (copy_to_user(argp + sizeof(uint32_t), &ops.retlen, + sizeof(uint32_t))) ret = -EFAULT; - kfree(databuf); + kfree(ops.oobbuf); break; } @@ -451,13 +461,12 @@ static int mtd_ioctl(struct inode *inode, struct file *file, case MEMREADOOB: { struct mtd_oob_buf buf; - void *databuf; - ssize_t retlen; + struct mtd_oob_ops ops; if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) return -EFAULT; - if (buf.length > 0x4096) + if (buf.length > 4096) return -EINVAL; if (!mtd->read_oob) @@ -465,22 +474,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file, else ret = access_ok(VERIFY_WRITE, buf.ptr, buf.length) ? 0 : -EFAULT; - if (ret) return ret; - databuf = kmalloc(buf.length, GFP_KERNEL); - if (!databuf) + ops.len = buf.length; + ops.ooblen = mtd->oobsize; + ops.ooboffs = buf.start & (mtd->oobsize - 1); + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + if (ops.ooboffs && ops.len > (ops.ooblen - ops.ooboffs)) + return -EINVAL; + + ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); + if (!ops.oobbuf) return -ENOMEM; - ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); + buf.start &= ~(mtd->oobsize - 1); + ret = mtd->read_oob(mtd, buf.start, &ops); - if (put_user(retlen, (uint32_t __user *)argp)) + if (put_user(ops.retlen, (uint32_t __user *)argp)) ret = -EFAULT; - else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) + else if (ops.retlen && copy_to_user(buf.ptr, ops.oobbuf, + ops.retlen)) ret = -EFAULT; - kfree(databuf); + kfree(ops.oobbuf); break; } diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index ec15abcdbdfa..38151b8e6631 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -231,101 +231,85 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs, } static int -concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t * retlen, u_char * buf) +concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { struct mtd_concat *concat = CONCAT(mtd); - int err = -EINVAL; - int i; + struct mtd_oob_ops devops = *ops; + int i, err; - *retlen = 0; + ops->retlen = 0; for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; - size_t size, retsize; if (from >= subdev->size) { - /* Not destined for this subdev */ - size = 0; from -= subdev->size; continue; } - if (from + len > subdev->size) - /* First part goes into this subdev */ - size = subdev->size - from; - else - /* Entire transaction goes into this subdev */ - size = len; - if (subdev->read_oob) - err = subdev->read_oob(subdev, from, size, - &retsize, buf); - else - err = -EINVAL; + /* partial read ? */ + if (from + devops.len > subdev->size) + devops.len = subdev->size - from; + err = subdev->read_oob(subdev, from, &devops); + ops->retlen += devops.retlen; if (err) - break; + return err; - *retlen += retsize; - len -= size; - if (len == 0) - break; + devops.len = ops->len - ops->retlen; + if (!devops.len) + return 0; + + if (devops.datbuf) + devops.datbuf += devops.retlen; + if (devops.oobbuf) + devops.oobbuf += devops.ooblen; - err = -EINVAL; - buf += size; from = 0; } - return err; + return -EINVAL; } static int -concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len, - size_t * retlen, const u_char * buf) +concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { struct mtd_concat *concat = CONCAT(mtd); - int err = -EINVAL; - int i; + struct mtd_oob_ops devops = *ops; + int i, err; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - *retlen = 0; + ops->retlen = 0; for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; - size_t size, retsize; if (to >= subdev->size) { - size = 0; to -= subdev->size; continue; } - if (to + len > subdev->size) - size = subdev->size - to; - else - size = len; - if (!(subdev->flags & MTD_WRITEABLE)) - err = -EROFS; - else if (subdev->write_oob) - err = subdev->write_oob(subdev, to, size, &retsize, - buf); - else - err = -EINVAL; + /* partial write ? */ + if (to + devops.len > subdev->size) + devops.len = subdev->size - to; + err = subdev->write_oob(subdev, to, &devops); + ops->retlen += devops.retlen; if (err) - break; + return err; - *retlen += retsize; - len -= size; - if (len == 0) - break; + devops.len = ops->len - ops->retlen; + if (!devops.len) + return 0; - err = -EINVAL; - buf += size; + if (devops.datbuf) + devops.datbuf += devops.retlen; + if (devops.oobbuf) + devops.oobbuf += devops.ooblen; to = 0; } - return err; + return -EINVAL; } static void concat_erase_callback(struct erase_info *instr) diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 6d7639b98eab..f22aeccf01e7 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -78,16 +78,16 @@ static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_ part->master->unpoint (part->master, addr, from + part->offset, len); } -static int part_read_oob (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int part_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) { struct mtd_part *part = PART(mtd); + if (from >= mtd->size) - len = 0; - else if (from + len > mtd->size) - len = mtd->size - from; - return part->master->read_oob (part->master, from + part->offset, - len, retlen, buf); + return -EINVAL; + if (from + ops->len > mtd->size) + return -EINVAL; + return part->master->read_oob(part->master, from + part->offset, ops); } static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, @@ -134,18 +134,19 @@ static int part_write (struct mtd_info *mtd, loff_t to, size_t len, len, retlen, buf); } -static int part_write_oob (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int part_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) { struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; + if (to >= mtd->size) - len = 0; - else if (to + len > mtd->size) - len = mtd->size - to; - return part->master->write_oob (part->master, to + part->offset, - len, retlen, buf); + return -EINVAL; + if (to + ops->len > mtd->size) + return -EINVAL; + return part->master->write_oob(part->master, to + part->offset, ops); } static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index e922b829c4be..b8e6e1579cf1 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -81,23 +81,12 @@ static struct nand_ecclayout nand_oob_64 = { .length = 38}} }; -/* This is used for padding purposes in nand_write_oob */ -static uint8_t ffchars[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -}; - -static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf); static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state); +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); + /* * For devices which display every fart in the system on a seperate LED. Is * compiled away when LED support is disabled. @@ -358,7 +347,6 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; uint8_t buf[2] = { 0, 0 }; - size_t retlen; int block; /* Get block number */ @@ -371,8 +359,13 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) return nand_update_bbt(mtd, ofs); /* We write two bytes, so we dont have to mess with 16 bit access */ - ofs += mtd->oobsize + (chip->badblockpos & ~0x01); - return nand_write_oob(mtd, ofs, 2, &retlen, buf); + ofs += mtd->oobsize; + chip->ops.len = 2; + chip->ops.datbuf = NULL; + chip->ops.oobbuf = buf; + chip->ops.ooboffs = chip->badblockpos & ~0x01; + + return nand_do_write_oob(mtd, ofs, &chip->ops); } /** @@ -739,6 +732,20 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip, int state) return status; } +/** + * nand_read_page_raw - [Intern] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + */ +static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + chip->read_buf(mtd, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return 0; +} + /** * nand_read_page_swecc - {REPLACABLE] software ecc based page read function * @mtd: mtd info structure @@ -756,11 +763,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_code = chip->buffers.ecccode; int *eccpos = chip->ecc.layout->eccpos; - chip->read_buf(mtd, buf, mtd->writesize); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - if (chip->ecc.mode == NAND_ECC_NONE) - return 0; + nand_read_page_raw(mtd, chip, buf); for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) chip->ecc.calculate(mtd, p, &ecc_calc[i]); @@ -882,18 +885,50 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, } /** - * nand_do_read - [Internal] Read data with ECC + * nand_transfer_oob - [Internal] Transfer oob to client buffer + * @chip: nand chip structure + * @ops: oob ops structure + */ +static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops) +{ + size_t len = ops->ooblen; + + switch(ops->mode) { + + case MTD_OOB_PLACE: + case MTD_OOB_RAW: + memcpy(oob, chip->oob_poi + ops->ooboffs, len); + return oob + len; + + case MTD_OOB_AUTO: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + size_t bytes; + + for(; free->length && len; free++, len -= bytes) { + bytes = min(len, free->length); + + memcpy(oob, chip->oob_poi + free->offset, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} + +/** + * nand_do_read_ops - [Internal] Read data with ECC * * @mtd: MTD device structure * @from: offset to read from - * @len: number of bytes to read - * @retlen: pointer to variable to store the number of read bytes - * @buf: the databuffer to put data * * Internal function. Called with chip held. */ -int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, uint8_t *buf) +static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) { int chipnr, page, realpage, col, bytes, aligned; struct nand_chip *chip = mtd->priv; @@ -901,8 +936,8 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; int sndcmd = 1; int ret = 0; - uint32_t readlen = len; - uint8_t *bufpoi; + uint32_t readlen = ops->len; + uint8_t *bufpoi, *oob, *buf; stats = mtd->ecc_stats; @@ -915,12 +950,15 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, col = (int)(from & (mtd->writesize - 1)); chip->oob_poi = chip->buffers.oobrbuf; + buf = ops->datbuf; + oob = ops->oobbuf; + while(1) { bytes = min(mtd->writesize - col, readlen); aligned = (bytes == mtd->writesize); /* Is the current page in the buffer ? */ - if (realpage != chip->pagebuf) { + if (realpage != chip->pagebuf || oob) { bufpoi = aligned ? buf : chip->buffers.databuf; if (likely(sndcmd)) { @@ -939,6 +977,16 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, memcpy(buf, chip->buffers.databuf + col, bytes); } + buf += bytes; + + if (unlikely(oob)) { + /* Raw mode does data:oob:data:oob */ + if (ops->mode != MTD_OOB_RAW) + oob = nand_transfer_oob(chip, oob, ops); + else + buf = nand_transfer_oob(chip, buf, ops); + } + if (!(chip->options & NAND_NO_READRDY)) { /* * Apply delay or wait for ready/busy pin. Do @@ -952,10 +1000,11 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, else nand_wait_ready(mtd); } - } else + } else { memcpy(buf, chip->buffers.databuf + col, bytes); + buf += bytes; + } - buf += bytes; readlen -= bytes; if (!readlen) @@ -981,7 +1030,7 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, sndcmd = 1; } - *retlen = len - (size_t) readlen; + ops->retlen = ops->len - (size_t) readlen; if (ret) return ret; @@ -1002,57 +1051,49 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf) { + struct nand_chip *chip = mtd->priv; int ret; - *retlen = 0; /* Do not allow reads past end of device */ if ((from + len) > mtd->size) return -EINVAL; if (!len) return 0; - nand_get_device(mtd->priv, mtd, FL_READING); + nand_get_device(chip, mtd, FL_READING); - ret = nand_do_read(mtd, from, len, retlen, buf); + chip->ops.len = len; + chip->ops.datbuf = buf; + chip->ops.oobbuf = NULL; + + ret = nand_do_read_ops(mtd, from, &chip->ops); nand_release_device(mtd); + *retlen = chip->ops.retlen; return ret; } /** - * nand_read_oob - [MTD Interface] NAND read out-of-band + * nand_do_read_oob - [Intern] NAND read out-of-band * @mtd: MTD device structure * @from: offset to read from - * @len: number of bytes to read - * @retlen: pointer to variable to store the number of read bytes - * @buf: the databuffer to put data + * @ops: oob operations description structure * * NAND read out-of-band data from the spare area */ -static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, uint8_t *buf) +static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) { int col, page, realpage, chipnr, sndcmd = 1; struct nand_chip *chip = mtd->priv; int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; - int readlen = len; + int direct, bytes, readlen = ops->len; + uint8_t *bufpoi, *buf = ops->oobbuf; DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int)from, (int)len); - /* Initialize return length value */ - *retlen = 0; - - /* Do not allow reads past end of device */ - if ((from + len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt read beyond end of device\n"); - return -EINVAL; - } - - nand_get_device(chip, mtd, FL_READING); - chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -1060,20 +1101,31 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; - /* Mask to get column */ - col = from & (mtd->oobsize - 1); + if (ops->mode != MTD_OOB_AUTO) { + col = ops->ooboffs; + direct = 1; + } else { + col = 0; + direct = 0; + } while(1) { - int bytes = min((int)(mtd->oobsize - col), readlen); + bytes = direct ? ops->ooblen : mtd->oobsize; + bufpoi = direct ? buf : chip->buffers.oobrbuf; if (likely(sndcmd)) { chip->cmdfunc(mtd, NAND_CMD_READOOB, col, page); sndcmd = 0; } - chip->read_buf(mtd, buf, bytes); + chip->read_buf(mtd, bufpoi, bytes); - readlen -= bytes; + if (unlikely(!direct)) + buf = nand_transfer_oob(chip, buf, ops); + else + buf += ops->ooblen; + + readlen -= ops->ooblen; if (!readlen) break; @@ -1090,10 +1142,6 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, nand_wait_ready(mtd); } - buf += bytes; - bytes = mtd->oobsize; - col = 0; - /* Increment page address */ realpage++; @@ -1112,81 +1160,76 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, sndcmd = 1; } - /* Deselect and wake up anyone waiting on the device */ - nand_release_device(mtd); - - *retlen = len; + ops->retlen = ops->len; return 0; } /** - * nand_read_raw - [GENERIC] Read raw data including oob into buffer + * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band * @mtd: MTD device structure - * @buf: temporary buffer * @from: offset to read from - * @len: number of bytes to read - * @ooblen: number of oob data bytes to read + * @ops: oob operation description structure * - * Read raw data including oob into buffer + * NAND read data and/or out-of-band data */ -int nand_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, - size_t ooblen) +static int nand_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) { + int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) = NULL; struct nand_chip *chip = mtd->priv; - int page = (int)(from >> chip->page_shift); - int chipnr = (int)(from >> chip->chip_shift); - int sndcmd = 1; - int cnt = 0; - int pagesize = mtd->writesize + mtd->oobsize; - int blockcheck; + int ret = -ENOTSUPP; + + ops->retlen = 0; /* Do not allow reads past end of device */ - if ((from + len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_raw: " + if ((from + ops->len) > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " "Attempt read beyond end of device\n"); return -EINVAL; } - /* Grab the lock and see if the device is available */ nand_get_device(chip, mtd, FL_READING); - chip->select_chip(mtd, chipnr); - - /* Add requested oob length */ - len += ooblen; - blockcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + switch(ops->mode) { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + break; - while (len) { - if (likely(sndcmd)) { - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, - page & chip->pagemask); - sndcmd = 0; - } + case MTD_OOB_RAW: + /* Replace the read_page algorithm temporary */ + read_page = chip->ecc.read_page; + chip->ecc.read_page = nand_read_page_raw; + break; - chip->read_buf(mtd, &buf[cnt], pagesize); + default: + goto out; + } - len -= pagesize; - cnt += pagesize; - page++; + if (!ops->datbuf) + ret = nand_do_read_oob(mtd, from, ops); + else + ret = nand_do_read_ops(mtd, from, ops); - if (!(chip->options & NAND_NO_READRDY)) { - if (!chip->dev_ready) - udelay(chip->chip_delay); - else - nand_wait_ready(mtd); - } + if (unlikely(ops->mode == MTD_OOB_RAW)) + chip->ecc.read_page = read_page; + out: + nand_release_device(mtd); + return ret; +} - /* - * Check, if the chip supports auto page increment or if we - * cross a block boundary. - */ - if (!NAND_CANAUTOINCR(chip) || !(page & blockcheck)) - sndcmd = 1; - } - /* Deselect and wake up anyone waiting on the device */ - nand_release_device(mtd); - return 0; +/** + * nand_write_page_raw - [Intern] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + */ +static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + chip->write_buf(mtd, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); } /** @@ -1205,17 +1248,14 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *p = buf; int *eccpos = chip->ecc.layout->eccpos; - if (chip->ecc.mode != NAND_ECC_NONE) { - /* Software ecc calculation */ - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + /* Software ecc calculation */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); - for (i = 0; i < chip->ecc.total; i++) - chip->oob_poi[eccpos[i]] = ecc_calc[i]; - } + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; - chip->write_buf(mtd, buf, mtd->writesize); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_write_page_raw(mtd, chip, buf); } /** @@ -1342,51 +1382,77 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, return 0; } +/** + * nand_fill_oob - [Internal] Transfer client buffer to oob + * @chip: nand chip structure + * @oob: oob data buffer + * @ops: oob ops structure + */ +static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops) +{ + size_t len = ops->ooblen; + + switch(ops->mode) { + + case MTD_OOB_PLACE: + case MTD_OOB_RAW: + memcpy(chip->oob_poi + ops->ooboffs, oob, len); + return oob + len; + + case MTD_OOB_AUTO: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + size_t bytes; + + for(; free->length && len; free++, len -= bytes) { + bytes = min(len, free->length); + memcpy(chip->oob_poi + free->offset, oob, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} + #define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0 /** - * nand_write - [MTD Interface] NAND write with ECC + * nand_do_write_ops - [Internal] NAND write with ECC * @mtd: MTD device structure * @to: offset to write to - * @len: number of bytes to write - * @retlen: pointer to variable to store the number of written bytes - * @buf: the data to write + * @ops: oob operations description structure * * NAND write with ECC */ -static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf) +static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) { int chipnr, realpage, page, blockmask; struct nand_chip *chip = mtd->priv; - uint32_t writelen = len; + uint32_t writelen = ops->len; + uint8_t *oob = ops->oobbuf; + uint8_t *buf = ops->datbuf; int bytes = mtd->writesize; - int ret = -EIO; + int ret; - *retlen = 0; - - /* Do not allow write past end of device */ - if ((to + len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_write: " - "Attempt to write past end of page\n"); - return -EINVAL; - } + ops->retlen = 0; /* reject writes, which are not page aligned */ - if (NOTALIGNED(to) || NOTALIGNED(len)) { + if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { printk(KERN_NOTICE "nand_write: " "Attempt to write not page aligned data\n"); return -EINVAL; } - if (!len) + if (!writelen) return 0; - nand_get_device(chip, mtd, FL_WRITING); - /* Check, if it is write protected */ if (nand_check_wp(mtd)) - goto out; + return -EIO; chipnr = (int)(to >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -1397,7 +1463,7 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, /* Invalidate the page cache, when we write to the cached page */ if (to <= (chip->pagebuf << chip->page_shift) && - (chip->pagebuf << chip->page_shift) < (to + len)) + (chip->pagebuf << chip->page_shift) < (to + ops->len)) chip->pagebuf = -1; chip->oob_poi = chip->buffers.oobwbuf; @@ -1405,6 +1471,9 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, while(1) { int cached = writelen > bytes && page != blockmask; + if (unlikely(oob)) + oob = nand_fill_oob(chip, oob, ops); + ret = nand_write_page(mtd, chip, buf, page, cached); if (ret) break; @@ -1424,94 +1493,74 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, chip->select_chip(mtd, chipnr); } } - out: - *retlen = len - writelen; - nand_release_device(mtd); + + if (unlikely(oob)) + memset(chip->oob_poi, 0xff, mtd->oobsize); + + ops->retlen = ops->len - writelen; return ret; } /** - * nand_write_raw - [GENERIC] Write raw data including oob + * nand_write - [MTD Interface] NAND write with ECC * @mtd: MTD device structure - * @buf: source buffer * @to: offset to write to * @len: number of bytes to write - * @buf: source buffer - * @oob: oob buffer + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write * - * Write raw data including oob + * NAND write with ECC */ -int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, - const uint8_t *buf, uint8_t *oob) +static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) { struct nand_chip *chip = mtd->priv; - int page = (int)(to >> chip->page_shift); - int chipnr = (int)(to >> chip->chip_shift); int ret; - *retlen = 0; - - /* Do not allow writes past end of device */ - if ((to + len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt write " - "beyond end of device\n"); + /* Do not allow reads past end of device */ + if ((to + len) > mtd->size) return -EINVAL; - } + if (!len) + return 0; - /* Grab the lock and see if the device is available */ - nand_get_device(chip, mtd, FL_WRITING); + nand_get_device(chip, mtd, FL_READING); - chip->select_chip(mtd, chipnr); - chip->oob_poi = oob; + chip->ops.len = len; + chip->ops.datbuf = (uint8_t *)buf; + chip->ops.oobbuf = NULL; - while (len != *retlen) { - ret = nand_write_page(mtd, chip, buf, page, 0); - if (ret) - return ret; - page++; - *retlen += mtd->writesize; - buf += mtd->writesize; - chip->oob_poi += mtd->oobsize; - } + ret = nand_do_write_ops(mtd, to, &chip->ops); - /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); - return 0; + + *retlen = chip->ops.retlen; + return ret; } -EXPORT_SYMBOL_GPL(nand_write_raw); /** - * nand_write_oob - [MTD Interface] NAND write out-of-band + * nand_do_write_oob - [MTD Interface] NAND write out-of-band * @mtd: MTD device structure * @to: offset to write to - * @len: number of bytes to write - * @retlen: pointer to variable to store the number of written bytes - * @buf: the data to write + * @ops: oob operation description structure * * NAND write out-of-band */ -static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf) +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) { - int column, page, status, ret = -EIO, chipnr; + int chipnr, page, status; struct nand_chip *chip = mtd->priv; DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", - (unsigned int)to, (int)len); - - /* Initialize return length value */ - *retlen = 0; + (unsigned int)to, (int)ops->len); /* Do not allow write past end of page */ - column = to & (mtd->oobsize - 1); - if ((column + len) > mtd->oobsize) { + if ((ops->ooboffs + ops->len) > mtd->oobsize) { DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " "Attempt to write past end of page\n"); return -EINVAL; } - nand_get_device(chip, mtd, FL_WRITING); - chipnr = (int)(to >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -1528,26 +1577,27 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, /* Check, if it is write protected */ if (nand_check_wp(mtd)) - goto out; + return -EROFS; /* Invalidate the page cache, if we write to the cached page */ if (page == chip->pagebuf) chip->pagebuf = -1; - if (NAND_MUST_PAD(chip)) { + if (ops->mode == MTD_OOB_AUTO || NAND_MUST_PAD(chip)) { + chip->oob_poi = chip->buffers.oobwbuf; + memset(chip->oob_poi, 0xff, mtd->oobsize); + nand_fill_oob(chip, ops->oobbuf, ops); chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page & chip->pagemask); - /* prepad 0xff for partial programming */ - chip->write_buf(mtd, ffchars, column); - /* write data */ - chip->write_buf(mtd, buf, len); - /* postpad 0xff for partial programming */ - chip->write_buf(mtd, ffchars, mtd->oobsize - (len + column)); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + memset(chip->oob_poi, 0xff, mtd->oobsize); } else { - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + column, + chip->cmdfunc(mtd, NAND_CMD_SEQIN, + mtd->writesize + ops->ooboffs, page & chip->pagemask); - chip->write_buf(mtd, buf, len); + chip->write_buf(mtd, ops->oobbuf, ops->len); } + /* Send command to program the OOB data */ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); @@ -1557,27 +1607,75 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, if (status & NAND_STATUS_FAIL) { DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page); - ret = -EIO; - goto out; + return -EIO; } - *retlen = len; + ops->retlen = ops->len; #ifdef CONFIG_MTD_NAND_VERIFY_WRITE - /* Send command to read back the data */ - chip->cmdfunc(mtd, NAND_CMD_READOOB, column, page & chip->pagemask); + if (ops->mode != MTD_OOB_AUTO) { + /* Send command to read back the data */ + chip->cmdfunc(mtd, NAND_CMD_READOOB, ops->ooboffs, + page & chip->pagemask); - if (chip->verify_buf(mtd, buf, len)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " - "Failed write verify, page 0x%08x\n", page); - ret = -EIO; - goto out; + if (chip->verify_buf(mtd, ops->oobbuf, ops->len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " + "Failed write verify, page 0x%08x\n", page); + return -EIO; + } } #endif - ret = 0; + return 0; +} + +/** + * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure + */ +static int nand_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) = NULL; + struct nand_chip *chip = mtd->priv; + int ret = -ENOTSUPP; + + ops->retlen = 0; + + /* Do not allow writes past end of device */ + if ((to + ops->len) > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt read beyond end of device\n"); + return -EINVAL; + } + + nand_get_device(chip, mtd, FL_READING); + + switch(ops->mode) { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + break; + + case MTD_OOB_RAW: + /* Replace the write_page algorithm temporary */ + write_page = chip->ecc.write_page; + chip->ecc.write_page = nand_write_page_raw; + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_write_oob(mtd, to, ops); + else + ret = nand_do_write_ops(mtd, to, ops); + + if (unlikely(ops->mode == MTD_OOB_RAW)) + chip->ecc.write_page = write_page; out: - /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); - return ret; } @@ -2191,8 +2289,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips) case NAND_ECC_NONE: printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " "This is not recommended !!\n"); - chip->ecc.read_page = nand_read_page_swecc; - chip->ecc.write_page = nand_write_page_swecc; + chip->ecc.read_page = nand_read_page_raw; + chip->ecc.write_page = nand_write_page_raw; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; break; diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 40f99304df76..480c3cbf9bf9 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -230,6 +230,42 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc return 0; } +/* + * Scan read raw data from flash + */ +static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + size_t len) +{ + struct mtd_oob_ops ops; + + ops.mode = MTD_OOB_RAW; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.datbuf = buf; + ops.len = len; + + return mtd->read_oob(mtd, offs, &ops); +} + +/* + * Scan write data with oob to flash + */ +static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, + uint8_t *buf, uint8_t *oob) +{ + struct mtd_oob_ops ops; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.datbuf = buf; + ops.oobbuf = oob; + ops.len = len; + + return mtd->write_oob(mtd, offs, &ops); +} + /** * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page * @mtd: MTD device structure @@ -241,27 +277,85 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc * We assume that the bbt bits are in consecutive order. * */ -static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md) +static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md) { struct nand_chip *this = mtd->priv; /* Read the primary version, if available */ if (td->options & NAND_BBT_VERSION) { - nand_read_raw(mtd, buf, td->pages[0] << this->page_shift, mtd->writesize, mtd->oobsize); + scan_read_raw(mtd, buf, td->pages[0] << this->page_shift, + mtd->writesize); td->version[0] = buf[mtd->writesize + td->veroffs]; - printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]); + printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", + td->pages[0], td->version[0]); } /* Read the mirror version, if available */ if (md && (md->options & NAND_BBT_VERSION)) { - nand_read_raw(mtd, buf, md->pages[0] << this->page_shift, mtd->writesize, mtd->oobsize); + scan_read_raw(mtd, buf, md->pages[0] << this->page_shift, + mtd->writesize); md->version[0] = buf[mtd->writesize + md->veroffs]; - printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]); + printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", + md->pages[0], md->version[0]); } - return 1; } +/* + * Scan a given block full + */ +static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, + loff_t offs, uint8_t *buf, size_t readlen, + int scanlen, int len) +{ + int ret, j; + + ret = scan_read_raw(mtd, buf, offs, readlen); + if (ret) + return ret; + + for (j = 0; j < len; j++, buf += scanlen) { + if (check_pattern(buf, scanlen, mtd->writesize, bd)) + return 1; + } + return 0; +} + +/* + * Scan a given block partially + */ +static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, + loff_t offs, uint8_t *buf, int len) +{ + struct mtd_oob_ops ops; + int j, ret; + + ops.len = mtd->oobsize; + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + for (j = 0; j < len; j++) { + /* + * Read the full oob until read_oob is fixed to + * handle single byte reads for 16 bit + * buswidth + */ + ret = mtd->read_oob(mtd, offs, &ops); + if (ret) + return ret; + + if (check_short_pattern(buf, bd)) + return 1; + + offs += mtd->writesize; + } + return 0; +} + /** * create_bbt - [GENERIC] Create a bad block table by scanning the device * @mtd: MTD device structure @@ -273,13 +367,14 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des * Create a bad block table by scanning the device * for the given good/bad block identify pattern */ -static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) +static int create_bbt(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *bd, int chip) { struct nand_chip *this = mtd->priv; - int i, j, numblocks, len, scanlen; + int i, numblocks, len, scanlen; int startblock; loff_t from; - size_t readlen, ooblen; + size_t readlen; printk(KERN_INFO "Scanning device for bad blocks\n"); @@ -294,18 +389,17 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr if (!(bd->options & NAND_BBT_SCANEMPTY)) { /* We need only read few bytes from the OOB area */ - scanlen = ooblen = 0; + scanlen = 0; readlen = bd->len; } else { /* Full page content should be read */ scanlen = mtd->writesize + mtd->oobsize; readlen = len * mtd->writesize; - ooblen = len * mtd->oobsize; } if (chip == -1) { - /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it - * makes shifting and masking less painful */ + /* Note that numblocks is 2 * (real numblocks) here, see i+=2 + * below as it makes shifting and masking less painful */ numblocks = mtd->size >> (this->bbt_erase_shift - 1); startblock = 0; from = 0; @@ -324,35 +418,21 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr for (i = startblock; i < numblocks;) { int ret; - if (bd->options & NAND_BBT_SCANEMPTY) - if ((ret = nand_read_raw(mtd, buf, from, readlen, ooblen))) - return ret; - - for (j = 0; j < len; j++) { - if (!(bd->options & NAND_BBT_SCANEMPTY)) { - size_t retlen; - - /* Read the full oob until read_oob is fixed to - * handle single byte reads for 16 bit buswidth */ - ret = mtd->read_oob(mtd, from + j * mtd->writesize, mtd->oobsize, &retlen, buf); - if (ret) - return ret; - - if (check_short_pattern(buf, bd)) { - this->bbt[i >> 3] |= 0x03 << (i & 0x6); - printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", - i >> 1, (unsigned int)from); - break; - } - } else { - if (check_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { - this->bbt[i >> 3] |= 0x03 << (i & 0x6); - printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", - i >> 1, (unsigned int)from); - break; - } - } + if (bd->options & NAND_BBT_SCANALLPAGES) + ret = scan_block_full(mtd, bd, from, buf, readlen, + scanlen, len); + else + ret = scan_block_fast(mtd, bd, from, buf, len); + + if (ret < 0) + return ret; + + if (ret) { + this->bbt[i >> 3] |= 0x03 << (i & 0x6); + printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", + i >> 1, (unsigned int)from); } + i += 2; from += (1 << this->bbt_erase_shift); } @@ -383,6 +463,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr int bits, startblock, block, dir; int scanlen = mtd->writesize + mtd->oobsize; int bbtblocks; + int blocktopage = this->bbt_erase_shift - this->page_shift; /* Search direction top -> down ? */ if (td->options & NAND_BBT_LASTBLOCK) { @@ -412,11 +493,14 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr td->pages[i] = -1; /* Scan the maximum number of blocks */ for (block = 0; block < td->maxblocks; block++) { + int actblock = startblock + dir * block; + loff_t offs = actblock << this->bbt_erase_shift; + /* Read first page */ - nand_read_raw(mtd, buf, actblock << this->bbt_erase_shift, mtd->writesize, mtd->oobsize); + scan_read_raw(mtd, buf, offs, mtd->writesize); if (!check_pattern(buf, scanlen, mtd->writesize, td)) { - td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift); + td->pages[i] = actblock << blocktopage; if (td->options & NAND_BBT_VERSION) { td->version[i] = buf[mtd->writesize + td->veroffs]; } @@ -481,8 +565,14 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, int nrchips, bbtoffs, pageoffs, ooboffs; uint8_t msk[4]; uint8_t rcode = td->reserved_block_code; - size_t retlen, len = 0, ooblen; + size_t retlen, len = 0; loff_t to; + struct mtd_oob_ops ops; + + ops.ooblen = mtd->oobsize; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; if (!rcode) rcode = 0xff; @@ -583,10 +673,10 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, "bad block table\n"); } /* Read oob data */ - ooblen = (len >> this->page_shift) * mtd->oobsize; - res = mtd->read_oob(mtd, to + mtd->writesize, ooblen, - &retlen, &buf[len]); - if (res < 0 || retlen != ooblen) + ops.len = (len >> this->page_shift) * mtd->oobsize; + ops.oobbuf = &buf[len]; + res = mtd->read_oob(mtd, to + mtd->writesize, &ops); + if (res < 0 || ops.retlen != ops.len) goto outerr; /* Calc the byte offset in the buffer */ @@ -635,7 +725,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, if (res < 0) goto outerr; - res = nand_write_raw(mtd, to, len, &retlen, buf, &buf[len]); + res = scan_write_bbt(mtd, to, len, buf, &buf[len]); if (res < 0) goto outerr; diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 359533b33d9b..f6ffe7949b26 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -134,6 +134,69 @@ static void nftl_remove_dev(struct mtd_blktrans_dev *dev) kfree(nftl); } +/* + * Read oob data from flash + */ +int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf) +{ + struct mtd_oob_ops ops; + int res; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = offs & (mtd->writesize - 1); + ops.ooblen = len; + ops.oobbuf = buf; + ops.datbuf = NULL; + ops.len = len; + + res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + *retlen = ops.retlen; + return res; +} + +/* + * Write oob data to flash + */ +int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf) +{ + struct mtd_oob_ops ops; + int res; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = offs & (mtd->writesize - 1); + ops.ooblen = len; + ops.oobbuf = buf; + ops.datbuf = NULL; + ops.len = len; + + res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + *retlen = ops.retlen; + return res; +} + +/* + * Write data and oob to flash + */ +static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf, uint8_t *oob) +{ + struct mtd_oob_ops ops; + int res; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = offs; + ops.ooblen = mtd->oobsize; + ops.oobbuf = oob; + ops.datbuf = buf; + ops.len = len; + + res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + *retlen = ops.retlen; + return res; +} + #ifdef CONFIG_NFTL_RW /* Actual NFTL access routines */ @@ -216,7 +279,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p targetEUN = thisEUN; for (block = 0; block < nftl->EraseSize / 512; block ++) { - mtd->read_oob(mtd, (thisEUN * nftl->EraseSize) + + nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) + (block * 512), 16 , &retlen, (char *)&oob); if (block == 2) { @@ -333,7 +396,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p longer one */ oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS); oob.u.c.unused = 0xffffffff; - mtd->write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, + nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, 8, &retlen, (char *)&oob.u); } @@ -369,17 +432,15 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p memset(&oob, 0xff, sizeof(struct nftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; - nand_write_raw(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + - (block * 512), 512, &retlen, movebuf, - (char *)&oob); - + nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + + (block * 512), 512, &retlen, movebuf, (char *)&oob); } /* add the header so that it is now a valid chain */ oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; - mtd->write_oob(mtd, (nftl->EraseSize * targetEUN) + 8, + nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8, 8, &retlen, (char *)&oob.u); /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */ @@ -499,7 +560,7 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) lastEUN = writeEUN; - mtd->read_oob(mtd, + nftl_read_oob(mtd, (writeEUN * nftl->EraseSize) + blockofs, 8, &retlen, (char *)&bci); @@ -588,12 +649,12 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) nftl->ReplUnitTable[writeEUN] = BLOCK_NIL; /* ... and on the flash itself */ - mtd->read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, + nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, &retlen, (char *)&oob.u); oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); - mtd->write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, + nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, &retlen, (char *)&oob.u); /* we link the new block to the chain only after the @@ -603,13 +664,13 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) /* Both in our cache... */ nftl->ReplUnitTable[lastEUN] = writeEUN; /* ... and on the flash itself */ - mtd->read_oob(mtd, (lastEUN * nftl->EraseSize) + 8, + nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8, 8, &retlen, (char *)&oob.u); oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = cpu_to_le16(writeEUN); - mtd->write_oob(mtd, (lastEUN * nftl->EraseSize) + 8, + nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8, 8, &retlen, (char *)&oob.u); } @@ -643,9 +704,8 @@ static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, memset(&oob, 0xff, sizeof(struct nftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; - nand_write_raw(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + - blockofs, 512, &retlen, (char *)buffer, - (char *)&oob); + nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, + 512, &retlen, (char *)buffer, (char *)&oob); return 0; } #endif /* CONFIG_NFTL_RW */ @@ -667,7 +727,7 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, if (thisEUN != BLOCK_NIL) { while (thisEUN < nftl->nb_blocks) { - if (mtd->read_oob(mtd, (thisEUN * nftl->EraseSize) + + if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) + blockofs, 8, &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 521b07cd2326..067262ee8df0 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -33,6 +33,11 @@ char nftlmountrev[]="$Revision: 1.41 $"; +extern int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); +extern int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); + /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the * various device information of the NFTL partition and Bad Unit Table. Update * the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[] @@ -92,7 +97,7 @@ static int find_boot_record(struct NFTLrecord *nftl) } /* To be safer with BIOS, also use erase mark as discriminant */ - if ((ret = mtd->read_oob(mtd, block * nftl->EraseSize + + if ((ret = nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0)) { printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n", @@ -283,7 +288,7 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int return -1; if (check_oob) { - if(mtd->read_oob(mtd, address, mtd->oobsize, + if(nftl_read_oob(mtd, address, mtd->oobsize, &retlen, &buf[SECTORSIZE]) < 0) return -1; if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0) @@ -311,7 +316,7 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) struct mtd_info *mtd = nftl->mbd.mtd; /* Read the Unit Control Information #1 for Wear-Leveling */ - if (mtd->read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, + if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&uci) < 0) goto default_uci1; @@ -351,7 +356,7 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) goto fail; uci.WearInfo = le32_to_cpu(nb_erases); - if (mtd->write_oob(mtd, block * nftl->EraseSize + SECTORSIZE + + if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&uci) < 0) goto fail; return 0; @@ -383,7 +388,7 @@ static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_b block = first_block; for (;;) { for (i = 0; i < sectors_per_block; i++) { - if (mtd->read_oob(mtd, + if (nftl_read_oob(mtd, block * nftl->EraseSize + i * SECTORSIZE, 8, &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; @@ -404,7 +409,7 @@ static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_b /* sector not free actually : mark it as SECTOR_IGNORE */ bci.Status = SECTOR_IGNORE; bci.Status1 = SECTOR_IGNORE; - mtd->write_oob(mtd, block * + nftl_write_oob(mtd, block * nftl->EraseSize + i * SECTORSIZE, 8, &retlen, (char *)&bci); @@ -498,7 +503,7 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) size_t retlen; /* check erase mark. */ - if (mtd->read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) return -1; @@ -513,7 +518,7 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) h1.EraseMark = cpu_to_le16(ERASE_MARK); h1.EraseMark1 = cpu_to_le16(ERASE_MARK); h1.WearInfo = cpu_to_le32(0); - if (mtd->write_oob(mtd, + if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) return -1; @@ -526,7 +531,7 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) SECTORSIZE, 0) != 0) return -1; - if (mtd->read_oob(mtd, block * nftl->EraseSize + i, + if (nftl_read_oob(mtd, block * nftl->EraseSize + i, 16, &retlen, buf) < 0) return -1; if (i == SECTORSIZE) { @@ -557,7 +562,7 @@ static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block) struct nftl_uci2 uci; size_t retlen; - if (mtd->read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, + if (nftl_read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, 8, &retlen, (char *)&uci) < 0) return 0; @@ -597,10 +602,10 @@ int NFTL_mount(struct NFTLrecord *s) for (;;) { /* read the block header. If error, we format the chain */ - if (mtd->read_oob(mtd, + if (nftl_read_oob(mtd, block * s->EraseSize + 8, 8, &retlen, (char *)&h0) < 0 || - mtd->read_oob(mtd, + nftl_read_oob(mtd, block * s->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) { diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index a0d3f011c0f2..84ec40d25438 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -671,7 +671,7 @@ out: } /** - * onenand_read_oob - [MTD Interface] OneNAND read out-of-band + * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band * @param mtd MTD device structure * @param from offset to read from * @param len number of bytes to read @@ -680,8 +680,8 @@ out: * * OneNAND read out-of-band data from the spare area */ -static int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) { struct onenand_chip *this = mtd->priv; int read = 0, thislen, column; @@ -744,6 +744,21 @@ out: return ret; } +/** + * onenand_read_oob - [MTD Interface] NAND write data and/or out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure + */ +static int onenand_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + BUG_ON(ops->mode != MTD_OOB_PLACE); + + return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->len, + &ops->retlen, ops->oobbuf); +} + #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE /** * onenand_verify_oob - [GENERIC] verify the oob contents after a write @@ -894,7 +909,7 @@ out: } /** - * onenand_write_oob - [MTD Interface] OneNAND write out-of-band + * onenand_do_write_oob - [Internal] OneNAND write out-of-band * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write @@ -903,8 +918,8 @@ out: * * OneNAND write out-of-band */ -static int onenand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) { struct onenand_chip *this = mtd->priv; int column, ret = 0; @@ -972,6 +987,21 @@ out: return ret; } +/** + * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure + */ +static int onenand_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + BUG_ON(ops->mode != MTD_OOB_PLACE); + + return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->len, + &ops->retlen, ops->oobbuf); +} + /** * onenand_block_checkbad - [GENERIC] Check if a block is marked bad * @param mtd MTD device structure @@ -1138,7 +1168,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) /* We write two bytes, so we dont have to mess with 16 bit access */ ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); - return mtd->write_oob(mtd, ofs , 2, &retlen, buf); + return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf); } /** @@ -1328,7 +1358,7 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); - ret = mtd->write_oob(mtd, from, len, retlen, buf); + ret = onenand_do_write_oob(mtd, from, len, retlen, buf); /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index aafd7c2f7802..1b00dac3d7d6 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -17,6 +17,9 @@ #include #include +extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf); + /** * check_short_pattern - [GENERIC] check if a pattern is in the buffer * @param buf the buffer to search @@ -87,8 +90,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr /* No need to read pages fully, * just read required OOB bytes */ - ret = mtd->read_oob(mtd, from + j * mtd->writesize + bd->offs, - readlen, &retlen, &buf[0]); + ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs, + readlen, &retlen, &buf[0]); if (ret) return ret; diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index 506690cc9a78..935fec1b1201 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -100,6 +100,7 @@ struct jffs2_sb_info { #ifdef CONFIG_JFFS2_FS_WRITEBUFFER /* Write-behind buffer for NAND flash */ unsigned char *wbuf; + unsigned char *oobbuf; uint32_t wbuf_ofs; uint32_t wbuf_len; struct jffs2_inodirty *wbuf_inodes; diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index c6a62e162963..1195d06d4373 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -955,158 +955,159 @@ exit: return ret; } +#define NR_OOB_SCAN_PAGES 4 + /* - * Check, if the out of band area is empty + * Check, if the out of band area is empty */ -int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode) +int jffs2_check_oob_empty(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, int mode) { - unsigned char *buf; - int ret = 0; - int i,len,page; - size_t retlen; - int oob_size; - - /* allocate a buffer for all oob data in this sector */ - oob_size = c->mtd->oobsize; - len = 4 * oob_size; - buf = kmalloc(len, GFP_KERNEL); - if (!buf) { - printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n"); - return -ENOMEM; - } - /* - * if mode = 0, we scan for a total empty oob area, else we have - * to take care of the cleanmarker in the first page of the block - */ - ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf); + int i, page, ret; + int oobsize = c->mtd->oobsize; + struct mtd_oob_ops ops; + + ops.len = NR_OOB_SCAN_PAGES * oobsize; + ops.ooblen = oobsize; + ops.oobbuf = c->oobbuf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); if (ret) { - D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); - goto out; + D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " + "failed %d for block at %08x\n", ret, jeb->offset)); + return ret; } - if (retlen < len) { - D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read " - "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset)); - ret = -EIO; - goto out; + if (ops.retlen < ops.len) { + D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " + "returned short read (%zd bytes not %d) for block " + "at %08x\n", ops.retlen, ops.len, jeb->offset)); + return -EIO; } /* Special check for first page */ - for(i = 0; i < oob_size ; i++) { + for(i = 0; i < oobsize ; i++) { /* Yeah, we know about the cleanmarker. */ if (mode && i >= c->fsdata_pos && i < c->fsdata_pos + c->fsdata_len) continue; - if (buf[i] != 0xFF) { - D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n", - buf[i], i, jeb->offset)); - ret = 1; - goto out; + if (ops.oobbuf[i] != 0xFF) { + D2(printk(KERN_DEBUG "Found %02x at %x in OOB for " + "%08x\n", ops.oobbuf[i], i, jeb->offset)); + return 1; } } /* we know, we are aligned :) */ - for (page = oob_size; page < len; page += sizeof(long)) { - unsigned long dat = *(unsigned long *)(&buf[page]); - if(dat != -1) { - ret = 1; - goto out; - } + for (page = oobsize; page < ops.len; page += sizeof(long)) { + long dat = *(long *)(&ops.oobbuf[page]); + if(dat != -1) + return 1; } - -out: - kfree(buf); - - return ret; + return 0; } /* -* Scan for a valid cleanmarker and for bad blocks -* For virtual blocks (concatenated physical blocks) check the cleanmarker -* only in the first page of the first physical block, but scan for bad blocks in all -* physical blocks -*/ -int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) + * Scan for a valid cleanmarker and for bad blocks + */ +int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) { struct jffs2_unknown_node n; - unsigned char buf[2 * NAND_MAX_OOBSIZE]; - unsigned char *p; - int ret, i, cnt, retval = 0; - size_t retlen, offset; - int oob_size; - - offset = jeb->offset; - oob_size = c->mtd->oobsize; - - /* Loop through the physical blocks */ - for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) { - /* Check first if the block is bad. */ - if (c->mtd->block_isbad (c->mtd, offset)) { - D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset)); - return 2; - } - /* - * We read oob data from page 0 and 1 of the block. - * page 0 contains cleanmarker and badblock info - * page 1 contains failure count of this block - */ - ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf); + struct mtd_oob_ops ops; + int oobsize = c->mtd->oobsize; + unsigned char *p,*b; + int i, ret; + size_t offset = jeb->offset; + + /* Check first if the block is bad. */ + if (c->mtd->block_isbad(c->mtd, offset)) { + D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker()" + ": Bad block at %08x\n", jeb->offset)); + return 2; + } - if (ret) { - D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); - return ret; - } - if (retlen < (oob_size << 1)) { - D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset)); - return -EIO; - } + ops.len = oobsize; + ops.ooblen = oobsize; + ops.oobbuf = c->oobbuf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; - /* Check cleanmarker only on the first physical block */ - if (!cnt) { - n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); - n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); - n.totlen = cpu_to_je32 (8); - p = (unsigned char *) &n; + ret = c->mtd->read_oob(c->mtd, offset, &ops); + if (ret) { + D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " + "Read OOB failed %d for block at %08x\n", + ret, jeb->offset)); + return ret; + } - for (i = 0; i < c->fsdata_len; i++) { - if (buf[c->fsdata_pos + i] != p[i]) { - retval = 1; - } - } - D1(if (retval == 1) { - printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset); - printk(KERN_WARNING "OOB at %08zx was ", offset); - for (i=0; i < oob_size; i++) { - printk("%02x ", buf[i]); - } - printk("\n"); - }) - } - offset += c->mtd->erasesize; + if (ops.retlen < ops.len) { + D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): " + "Read OOB return short read (%zd bytes not %d) " + "for block at %08x\n", ops.retlen, ops.len, + jeb->offset)); + return -EIO; } - return retval; + + n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); + n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); + n.totlen = cpu_to_je32 (8); + p = (unsigned char *) &n; + b = c->oobbuf + c->fsdata_pos; + + for (i = c->fsdata_len; i; i--) { + if (*b++ != *p++) + ret = 1; + } + + D1(if (ret == 1) { + printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " + "Cleanmarker node not detected in block at %08x\n", + offset); + printk(KERN_WARNING "OOB at %08zx was ", offset); + for (i=0; i < oobsize; i++) + printk("%02x ", c->oobbuf[i]); + printk("\n"); + }); + return ret; } -int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) { - struct jffs2_unknown_node n; - int ret; - size_t retlen; + struct jffs2_unknown_node n; + int ret; + struct mtd_oob_ops ops; n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); n.totlen = cpu_to_je32(8); - ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n); + ops.len = c->fsdata_len; + ops.ooblen = c->fsdata_len;; + ops.oobbuf = (uint8_t *)&n; + ops.ooboffs = c->fsdata_pos; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops); if (ret) { - D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); + D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " + "Write failed for block at %08x: error %d\n", + jeb->offset, ret)); return ret; } - if (retlen != c->fsdata_len) { - D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len)); - return ret; + if (ops.retlen != ops.len) { + D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " + "Short write for block at %08x: %zd not %d\n", + jeb->offset, ops.retlen, ops.len)); + return -EIO; } return 0; } @@ -1185,6 +1186,10 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) if (!c->wbuf) return -ENOMEM; + c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->mtd->oobsize, GFP_KERNEL); + if (!c->oobbuf) + return -ENOMEM; + res = jffs2_nand_set_oobinfo(c); #ifdef BREAKME @@ -1202,6 +1207,7 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) { kfree(c->wbuf); + kfree(c->oobbuf); } int jffs2_dataflash_setup(struct jffs2_sb_info *c) { diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 4970c2e96fbf..e75bb584e80b 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -67,6 +67,50 @@ struct mtd_ecc_stats { unsigned long failed; }; +/* + * oob operation modes + * + * MTD_OOB_PLACE: oob data are placed at the given offset + * MTD_OOB_AUTO: oob data are automatically placed at the free areas + * which are defined by the ecclayout + * MTD_OOB_RAW: mode to read raw data+oob in one chunk. The oob data + * is inserted into the data. Thats a raw image of the + * flash contents. + */ +typedef enum { + MTD_OOB_PLACE, + MTD_OOB_AUTO, + MTD_OOB_RAW, +} mtd_oob_mode_t; + +/** + * struct mtd_oob_ops - oob operation operands + * @mode: operation mode + * + * @len: number of bytes to write/read. When a data buffer is given + * (datbuf != NULL) this is the number of data bytes. When + + no data buffer is available this is the number of oob bytes. + * + * @retlen: number of bytes written/read. When a data buffer is given + * (datbuf != NULL) this is the number of data bytes. When + + no data buffer is available this is the number of oob bytes. + * + * @ooblen: number of oob bytes per page + * @ooboffs: offset of oob data in the oob area (only relevant when + * mode = MTD_OOB_PLACE) + * @datbuf: data buffer - if NULL only oob data are read/written + * @oobbuf: oob data buffer + */ +struct mtd_oob_ops { + mtd_oob_mode_t mode; + size_t len; + size_t retlen; + size_t ooblen; + uint32_t ooboffs; + uint8_t *datbuf; + uint8_t *oobbuf; +}; + struct mtd_info { u_char type; u_int32_t flags; @@ -125,8 +169,10 @@ struct mtd_info { int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); - int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); - int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); + int (*read_oob) (struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops); + int (*write_oob) (struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); /* * Methods to access the protection register area, present in some diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index dc2bf1bcf42b..bf2ce68901f5 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -31,14 +31,6 @@ extern int nand_scan (struct mtd_info *mtd, int max_chips); /* Free resources held by the NAND device */ extern void nand_release (struct mtd_info *mtd); -/* Read raw data from the device without ECC */ -extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, - size_t len, size_t ooblen); - - -extern int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf, uint8_t *oob); - /* The maximum number of NAND chips in an array */ #define NAND_MAX_CHIPS 8 @@ -375,6 +367,8 @@ struct nand_chip { struct nand_buffers buffers; struct nand_hw_control hwcontrol; + struct mtd_oob_ops ops; + uint8_t *bbt; struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_md; -- cgit v1.2.3 From f1a28c02843efcfcc41982149880bac3ac180234 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 30 May 2006 00:37:34 +0200 Subject: [MTD] NAND Expose the new raw mode function and status info to userspace The raw read/write access to NAND (without ECC) has been changed in the NAND rework. Expose the new way - setting the file mode via ioctl - to userspace. Also allow to read out the ecc statistics information so userspace tools can see that bitflips happened and whether errors where correctable or not. Also expose the number of bad blocks for the partition, so nandwrite can check if the data fits into the parition before writing to it. Signed-off-by: Thomas Gleixner --- drivers/mtd/mtdchar.c | 200 ++++++++++++++++++++++++++++++------------- drivers/mtd/mtdconcat.c | 51 +++++++---- drivers/mtd/mtdpart.c | 39 ++++++++- drivers/mtd/nand/nand_base.c | 26 +++--- drivers/mtd/nand/nand_bbt.c | 3 + include/linux/mtd/mtd.h | 11 --- include/mtd/mtd-abi.h | 27 ++++++ 7 files changed, 259 insertions(+), 98 deletions(-) (limited to 'include') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index a48210d58b92..fdc535b22e39 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -49,24 +49,18 @@ static struct mtd_notifier notifier = { }; /* - * We use file->private_data to store a pointer to the MTDdevice. - * Since alighment is at least 32 bits, we have 2 bits free for OTP - * modes as well. + * Data structure to hold the pointer to the mtd device as well + * as mode information ofr various use cases. */ - -#define TO_MTD(file) (struct mtd_info *)((long)((file)->private_data) & ~3L) - -#define MTD_MODE_OTP_FACT 1 -#define MTD_MODE_OTP_USER 2 -#define MTD_MODE(file) ((long)((file)->private_data) & 3) - -#define SET_MTD_MODE(file, mode) \ - do { long __p = (long)((file)->private_data); \ - (file)->private_data = (void *)((__p & ~3L) | mode); } while (0) +struct mtd_file_info { + struct mtd_info *mtd; + enum mtd_file_modes mode; +}; static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) { - struct mtd_info *mtd = TO_MTD(file); + struct mtd_file_info *mfi = file->private_data; + struct mtd_info *mtd = mfi->mtd; switch (orig) { case 0: @@ -97,6 +91,7 @@ static int mtd_open(struct inode *inode, struct file *file) int minor = iminor(inode); int devnum = minor >> 1; struct mtd_info *mtd; + struct mtd_file_info *mfi; DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); @@ -117,14 +112,20 @@ static int mtd_open(struct inode *inode, struct file *file) return -ENODEV; } - file->private_data = mtd; - /* You can't open it RW if it's not a writeable device */ if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) { put_mtd_device(mtd); return -EACCES; } + mfi = kzalloc(sizeof(*mfi), GFP_KERNEL); + if (!mfi) { + put_mtd_device(mtd); + return -ENOMEM; + } + mfi->mtd = mtd; + file->private_data = mfi; + return 0; } /* mtd_open */ @@ -132,16 +133,17 @@ static int mtd_open(struct inode *inode, struct file *file) static int mtd_close(struct inode *inode, struct file *file) { - struct mtd_info *mtd; + struct mtd_file_info *mfi = file->private_data; + struct mtd_info *mtd = mfi->mtd; DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); - mtd = TO_MTD(file); - if (mtd->sync) mtd->sync(mtd); put_mtd_device(mtd); + file->private_data = NULL; + kfree(mfi); return 0; } /* mtd_close */ @@ -153,7 +155,8 @@ static int mtd_close(struct inode *inode, struct file *file) static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) { - struct mtd_info *mtd = TO_MTD(file); + struct mtd_file_info *mfi = file->private_data; + struct mtd_info *mtd = mfi->mtd; size_t retlen=0; size_t total_retlen=0; int ret=0; @@ -186,13 +189,26 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t else len = count; - switch (MTD_MODE(file)) { - case MTD_MODE_OTP_FACT: + switch (mfi->mode) { + case MTD_MODE_OTP_FACTORY: ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); break; case MTD_MODE_OTP_USER: ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); break; + case MTD_MODE_RAW: + { + struct mtd_oob_ops ops; + + ops.mode = MTD_OOB_RAW; + ops.datbuf = kbuf; + ops.oobbuf = NULL; + ops.len = len; + + ret = mtd->read_oob(mtd, *ppos, &ops); + retlen = ops.retlen; + break; + } default: ret = mtd->read(mtd, *ppos, len, &retlen, kbuf); } @@ -232,7 +248,8 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) { - struct mtd_info *mtd = TO_MTD(file); + struct mtd_file_info *mfi = file->private_data; + struct mtd_info *mtd = mfi->mtd; char *kbuf; size_t retlen; size_t total_retlen=0; @@ -270,8 +287,8 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count return -EFAULT; } - switch (MTD_MODE(file)) { - case MTD_MODE_OTP_FACT: + switch (mfi->mode) { + case MTD_MODE_OTP_FACTORY: ret = -EROFS; break; case MTD_MODE_OTP_USER: @@ -281,6 +298,21 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count } ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); break; + + case MTD_MODE_RAW: + { + struct mtd_oob_ops ops; + + ops.mode = MTD_OOB_RAW; + ops.datbuf = kbuf; + ops.oobbuf = NULL; + ops.len = len; + + ret = mtd->write_oob(mtd, *ppos, &ops); + retlen = ops.retlen; + break; + } + default: ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); } @@ -310,10 +342,41 @@ static void mtdchar_erase_callback (struct erase_info *instr) wake_up((wait_queue_head_t *)instr->priv); } +#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP) +static int otp_select_filemode(struct mtd_file_info *mfi, int mode) +{ + struct mtd_info *mtd = mfi->mtd; + int ret = 0; + + switch (mode) { + case MTD_OTP_FACTORY: + if (!mtd->read_fact_prot_reg) + ret = -EOPNOTSUPP; + else + mfi->mode = MTD_MODE_OTP_FACTORY; + break; + case MTD_OTP_USER: + if (!mtd->read_fact_prot_reg) + ret = -EOPNOTSUPP; + else + mfi->mode = MTD_MODE_OTP_USER; + break; + default: + ret = -EINVAL; + case MTD_OTP_OFF: + break; + } + return ret; +} +#else +# define otp_select_filemode(f,m) -EOPNOTSUPP +#endif + static int mtd_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) { - struct mtd_info *mtd = TO_MTD(file); + struct mtd_file_info *mfi = file->private_data; + struct mtd_info *mtd = mfi->mtd; void __user *argp = (void __user *)arg; int ret = 0; u_long size; @@ -554,16 +617,6 @@ static int mtd_ioctl(struct inode *inode, struct file *file, break; } - case ECCGETLAYOUT: - - if (!mtd->ecclayout) - return -EOPNOTSUPP; - - if (copy_to_user(argp, &mtd->ecclayout, - sizeof(struct nand_ecclayout))) - return -EFAULT; - break; - case MEMGETBADBLOCK: { loff_t offs; @@ -596,25 +649,11 @@ static int mtd_ioctl(struct inode *inode, struct file *file, int mode; if (copy_from_user(&mode, argp, sizeof(int))) return -EFAULT; - SET_MTD_MODE(file, 0); - switch (mode) { - case MTD_OTP_FACTORY: - if (!mtd->read_fact_prot_reg) - ret = -EOPNOTSUPP; - else - SET_MTD_MODE(file, MTD_MODE_OTP_FACT); - break; - case MTD_OTP_USER: - if (!mtd->read_fact_prot_reg) - ret = -EOPNOTSUPP; - else - SET_MTD_MODE(file, MTD_MODE_OTP_USER); - break; - default: - ret = -EINVAL; - case MTD_OTP_OFF: - break; - } + + mfi->mode = MTD_MODE_NORMAL; + + ret = otp_select_filemode(mfi, mode); + file->f_pos = 0; break; } @@ -626,8 +665,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file, if (!buf) return -ENOMEM; ret = -EOPNOTSUPP; - switch (MTD_MODE(file)) { - case MTD_MODE_OTP_FACT: + switch (mfi->mode) { + case MTD_MODE_OTP_FACTORY: if (mtd->get_fact_prot_info) ret = mtd->get_fact_prot_info(mtd, buf, 4096); break; @@ -635,6 +674,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file, if (mtd->get_user_prot_info) ret = mtd->get_user_prot_info(mtd, buf, 4096); break; + default: + break; } if (ret >= 0) { if (cmd == OTPGETREGIONCOUNT) { @@ -653,7 +694,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, { struct otp_info info; - if (MTD_MODE(file) != MTD_MODE_OTP_USER) + if (mfi->mode != MTD_MODE_OTP_USER) return -EINVAL; if (copy_from_user(&info, argp, sizeof(info))) return -EFAULT; @@ -664,6 +705,49 @@ static int mtd_ioctl(struct inode *inode, struct file *file, } #endif + case ECCGETLAYOUT: + { + if (!mtd->ecclayout) + return -EOPNOTSUPP; + + if (copy_to_user(argp, &mtd->ecclayout, + sizeof(struct nand_ecclayout))) + return -EFAULT; + break; + } + + case ECCGETSTATS: + { + if (copy_to_user(argp, &mtd->ecc_stats, + sizeof(struct mtd_ecc_stats))) + return -EFAULT; + break; + } + + case MTDFILEMODE: + { + mfi->mode = 0; + + switch(arg) { + case MTD_MODE_OTP_FACTORY: + case MTD_MODE_OTP_USER: + ret = otp_select_filemode(mfi, arg); + break; + + case MTD_MODE_RAW: + if (!mtd->read_oob || !mtd->write_oob) + return -EOPNOTSUPP; + mfi->mode = arg; + + case MTD_MODE_NORMAL: + break; + default: + ret = -EINVAL; + } + file->f_pos = 0; + break; + } + default: ret = -ENOTTY; } diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 3c8d5e6fa010..1fea631b5852 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -56,7 +56,7 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) { struct mtd_concat *concat = CONCAT(mtd); - int ret = 0, err = -EINVAL; + int ret = 0, err; int i; *retlen = 0; @@ -80,28 +80,29 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len, err = subdev->read(subdev, from, size, &retsize, buf); - if (err && (err != -EBADMSG) && (err != -EUCLEAN)) - break; - /* Save information about bitflips! */ - if (err) { - if (err == -EBADMSG) - ret = err; - else if (!ret) + if (unlikely(err)) { + if (err == -EBADMSG) { + mtd->ecc_stats.failed++; ret = err; - err = 0; + } else if (err == -EUCLEAN) { + mtd->ecc_stats.corrected++; + /* Do not overwrite -EBADMSG !! */ + if (!ret) + ret = err; + } else + return err; } *retlen += retsize; len -= size; if (len == 0) - break; + return ret; - err = -EINVAL; buf += size; from = 0; } - return err ? err : ret; + return -EINVAL; } static int @@ -244,7 +245,7 @@ concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { struct mtd_concat *concat = CONCAT(mtd); struct mtd_oob_ops devops = *ops; - int i, err; + int i, err, ret = 0; ops->retlen = 0; @@ -262,12 +263,24 @@ concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) err = subdev->read_oob(subdev, from, &devops); ops->retlen += devops.retlen; - if (err) - return err; + + /* Save information about bitflips! */ + if (unlikely(err)) { + if (err == -EBADMSG) { + mtd->ecc_stats.failed++; + ret = err; + } else if (err == -EUCLEAN) { + mtd->ecc_stats.corrected++; + /* Do not overwrite -EBADMSG !! */ + if (!ret) + ret = err; + } else + return err; + } devops.len = ops->len - ops->retlen; if (!devops.len) - return 0; + return ret; if (devops.datbuf) devops.datbuf += devops.retlen; @@ -655,6 +668,8 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs) } err = subdev->block_markbad(subdev, ofs); + if (!err) + mtd->ecc_stats.badblocks++; break; } @@ -717,6 +732,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c if (subdev[0]->block_markbad) concat->mtd.block_markbad = concat_block_markbad; + concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks; + concat->subdev[0] = subdev[0]; for (i = 1; i < num_devs; i++) { @@ -744,6 +761,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c subdev[i]->flags & MTD_WRITEABLE; } concat->mtd.size += subdev[i]->size; + concat->mtd.ecc_stats.badblocks += + subdev[i]->ecc_stats.badblocks; if (concat->mtd.writesize != subdev[i]->writesize || concat->mtd.oobsize != subdev[i]->oobsize || concat->mtd.ecctype != subdev[i]->ecctype || diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index f22aeccf01e7..77a7123a5c56 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -51,12 +51,21 @@ static int part_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); + int res; + if (from >= mtd->size) len = 0; else if (from + len > mtd->size) len = mtd->size - from; - return part->master->read (part->master, from + part->offset, + res = part->master->read (part->master, from + part->offset, len, retlen, buf); + if (unlikely(res)) { + if (res == -EUCLEAN) + mtd->ecc_stats.corrected++; + if (res == -EBADMSG) + mtd->ecc_stats.failed++; + } + return res; } static int part_point (struct mtd_info *mtd, loff_t from, size_t len, @@ -82,12 +91,21 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { struct mtd_part *part = PART(mtd); + int res; if (from >= mtd->size) return -EINVAL; if (from + ops->len > mtd->size) return -EINVAL; - return part->master->read_oob(part->master, from + part->offset, ops); + res = part->master->read_oob(part->master, from + part->offset, ops); + + if (unlikely(res)) { + if (res == -EUCLEAN) + mtd->ecc_stats.corrected++; + if (res == -EBADMSG) + mtd->ecc_stats.failed++; + } + return res; } static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, @@ -246,12 +264,17 @@ static int part_block_isbad (struct mtd_info *mtd, loff_t ofs) static int part_block_markbad (struct mtd_info *mtd, loff_t ofs) { struct mtd_part *part = PART(mtd); + int res; + if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; if (ofs >= mtd->size) return -EINVAL; ofs += part->offset; - return part->master->block_markbad(part->master, ofs); + res = part->master->block_markbad(part->master, ofs); + if (!res) + mtd->ecc_stats.badblocks++; + return res; } /* @@ -436,6 +459,16 @@ int add_mtd_partitions(struct mtd_info *master, } slave->mtd.ecclayout = master->ecclayout; + if (master->block_isbad) { + uint32_t offs = 0; + + while(offs < slave->mtd.size) { + if (master->block_isbad(master, + offs + slave->offset)) + slave->mtd.ecc_stats.badblocks++; + offs += slave->mtd.erasesize; + } + } if(parts[i].mtdp) { /* store the object pointer (caller may or may not register it */ diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 7a3a44907715..ea6d2c334aed 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -347,7 +347,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; uint8_t buf[2] = { 0, 0 }; - int block; + int block, ret; /* Get block number */ block = ((int)ofs) >> chip->bbt_erase_shift; @@ -356,16 +356,22 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) /* Do we have a flash based bad block table ? */ if (chip->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; - chip->ops.len = 2; - chip->ops.datbuf = NULL; - chip->ops.oobbuf = buf; - chip->ops.ooboffs = chip->badblockpos & ~0x01; + ret = nand_update_bbt(mtd, ofs); + else { + /* We write two bytes, so we dont have to mess with 16 bit + * access + */ + ofs += mtd->oobsize; + chip->ops.len = 2; + chip->ops.datbuf = NULL; + chip->ops.oobbuf = buf; + chip->ops.ooboffs = chip->badblockpos & ~0x01; - return nand_do_write_oob(mtd, ofs, &chip->ops); + ret = nand_do_write_oob(mtd, ofs, &chip->ops); + } + if (!ret) + mtd->ecc_stats.badblocks++; + return ret; } /** diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 480c3cbf9bf9..a612c4ea8194 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -176,6 +176,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); + mtd->ecc_stats.bbtblocks++; continue; } /* Leave it for now, if its matured we can move this @@ -187,6 +188,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); else this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); + mtd->ecc_stats.badblocks++; } } totlen -= len; @@ -431,6 +433,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, this->bbt[i >> 3] |= 0x03 << (i & 0x6); printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", i >> 1, (unsigned int)from); + mtd->ecc_stats.badblocks++; } i += 2; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index e75bb584e80b..9536567d041b 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -56,17 +56,6 @@ struct mtd_erase_region_info { u_int32_t numblocks; /* Number of blocks of erasesize in this region */ }; -/** - * struct mtd_ecc_stats - error correction status - * - * @corrected: number of corrected bits - * @failed: number of uncorrectable errors - */ -struct mtd_ecc_stats { - unsigned long corrected; - unsigned long failed; -}; - /* * oob operation modes * diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index 54c673f9648d..c11a589bdedf 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -99,6 +99,8 @@ struct otp_info { #define OTPGETREGIONINFO _IOW('M', 15, struct otp_info) #define OTPLOCK _IOR('M', 16, struct otp_info) #define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) +#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) +#define MTDFILEMODE _IO('M', 19) /* * Obsolete legacy interface. Keep it in order not to break userspace @@ -128,4 +130,29 @@ struct nand_ecclayout { struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; }; +/** + * struct mtd_ecc_stats - error correction status + * + * @corrected: number of corrected bits + * @failed: number of uncorrectable errors + * @badblocks: number of bad blocks in this partition + * @bbtblocks: number of blocks reserved for bad block tables + */ +struct mtd_ecc_stats { + uint32_t corrected; + uint32_t failed; + uint32_t badblocks; + uint32_t bbtblocks; +}; + +/* + * Read/write file modes for access to MTD + */ +enum mtd_file_modes { + MTD_MODE_NORMAL = MTD_OTP_OFF, + MTD_MODE_OTP_FACTORY = MTD_OTP_FACTORY, + MTD_MODE_OTP_USER = MTD_OTP_USER, + MTD_MODE_RAW, +}; + #endif /* __MTD_ABI_H__ */ -- cgit v1.2.3 From e369d62e92d526a7ed641e2f0b2978fb0ce366c5 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 30 May 2006 14:25:17 +0200 Subject: [MTD] replace MTD_ROM with MTD_GENERIC_TYPE No mtd user should ever check for the device type. Instead, device features should be checked by the flags - if at all. As a first step towards type removal, change MTD_ROM into MTD_GENERIC_TYPE. Signed-off-by: Joern Engel --- drivers/mtd/chips/map_rom.c | 6 ++---- include/mtd/mtd-abi.h | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index 624c12c232c8..d59ac0136510 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -40,15 +40,13 @@ static struct mtd_info *map_rom_probe(struct map_info *map) map->fldrv = &maprom_chipdrv; mtd->priv = map; mtd->name = map->name; - mtd->type = MTD_ROM; + mtd->type = MTD_GENERIC_TYPE; mtd->size = map->size; mtd->read = maprom_read; mtd->write = maprom_write; mtd->sync = maprom_nop; mtd->flags = MTD_CAP_ROM; - mtd->erasesize = 131072; - while(mtd->size & (mtd->erasesize - 1)) - mtd->erasesize >>= 1; + mtd->erasesize = map->size; __module_get(THIS_MODULE); return mtd; diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index c11a589bdedf..1ce365bd31d2 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -25,10 +25,10 @@ struct mtd_oob_buf { #define MTD_ABSENT 0 #define MTD_RAM 1 -#define MTD_ROM 2 #define MTD_NORFLASH 3 #define MTD_NANDFLASH 4 #define MTD_DATAFLASH 6 +#define MTD_GENERIC_TYPE 7 #define MTD_WRITEABLE 0x400 /* Device is writeable */ #define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ -- cgit v1.2.3 From 92cbfdcc3661d7670b01b92b89811cd3a2412297 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 30 May 2006 14:25:24 +0200 Subject: [MTD] replace MTD_RAM with MTD_GENERIC_TYPE Ram devices get the extra capability of MTD_NO_ERASE - not requiring an explicit erase before writing to it. Currently only mtdblock uses this capability. Rest of the patch is a simple text replacement. Signed-off-by: Joern Engel --- drivers/mtd/chips/map_ram.c | 2 +- drivers/mtd/devices/block2mtd.c | 2 +- drivers/mtd/devices/ms02-nv.c | 2 +- drivers/mtd/devices/mtdram.c | 2 +- drivers/mtd/devices/phram.c | 2 +- drivers/mtd/devices/pmc551.c | 2 +- drivers/mtd/devices/slram.c | 2 +- drivers/mtd/mtdblock.c | 2 +- include/mtd/mtd-abi.h | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index 763925747db6..9b7c49930a76 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -64,7 +64,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map) map->fldrv = &mapram_chipdrv; mtd->priv = map; mtd->name = map->name; - mtd->type = MTD_RAM; + mtd->type = MTD_GENERIC_TYPE; mtd->size = map->size; mtd->erase = mapram_erase; mtd->read = mapram_read; diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 0d98c223c5fc..1157182c2c8c 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -324,7 +324,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; dev->mtd.erasesize = erase_size; - dev->mtd.type = MTD_RAM; + dev->mtd.type = MTD_GENERIC_TYPE; dev->mtd.flags = MTD_CAP_RAM; dev->mtd.erase = block2mtd_erase; dev->mtd.write = block2mtd_write; diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c index 4ab7670770e4..a6466f923e7e 100644 --- a/drivers/mtd/devices/ms02-nv.c +++ b/drivers/mtd/devices/ms02-nv.c @@ -218,7 +218,7 @@ static int __init ms02nv_init_one(ulong addr) fixsize = (size - (fixaddr - addr)) & ~(PAGE_SIZE - 1); mp->uaddr = phys_to_virt(fixaddr); - mtd->type = MTD_RAM; + mtd->type = MTD_GENERIC_TYPE; mtd->flags = MTD_CAP_RAM; mtd->size = fixsize; mtd->name = (char *)ms02nv_name; diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index 1443117fd8f4..f284c9670be8 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -103,7 +103,7 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, /* Setup the MTD structure */ mtd->name = name; - mtd->type = MTD_RAM; + mtd->type = MTD_GENERIC_TYPE; mtd->flags = MTD_CAP_RAM; mtd->size = size; mtd->erasesize = MTDRAM_ERASE_SIZE; diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index e09e416667d3..e6daa74d5fc6 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -149,7 +149,7 @@ static int register_device(char *name, unsigned long start, unsigned long len) new->mtd.read = phram_read; new->mtd.write = phram_write; new->mtd.owner = THIS_MODULE; - new->mtd.type = MTD_RAM; + new->mtd.type = MTD_GENERIC_TYPE; new->mtd.erasesize = PAGE_SIZE; ret = -EAGAIN; diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index 666cce1bf60c..aa9f6c993125 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -775,7 +775,7 @@ static int __init init_pmc551(void) mtd->write = pmc551_write; mtd->point = pmc551_point; mtd->unpoint = pmc551_unpoint; - mtd->type = MTD_RAM; + mtd->type = MTD_GENERIC_TYPE; mtd->name = "PMC551 RAM board"; mtd->erasesize = 0x10000; mtd->owner = THIS_MODULE; diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index b3f665e3c38b..fdb7a78b5304 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -207,7 +207,7 @@ static int register_device(char *name, unsigned long start, unsigned long length (*curmtd)->mtdinfo->read = slram_read; (*curmtd)->mtdinfo->write = slram_write; (*curmtd)->mtdinfo->owner = THIS_MODULE; - (*curmtd)->mtdinfo->type = MTD_RAM; + (*curmtd)->mtdinfo->type = MTD_GENERIC_TYPE; (*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ; if (add_mtd_device((*curmtd)->mtdinfo)) { diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 9b0bc20e4d8d..e5c78463ebfd 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -289,7 +289,7 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd) mutex_init(&mtdblk->cache_mutex); mtdblk->cache_state = STATE_EMPTY; - if (mtdblk->mtd->type != MTD_RAM && mtdblk->mtd->erasesize) { + if ( !(mtdblk->mtd->flags & MTD_NO_ERASE) && mtdblk->mtd->erasesize) { mtdblk->cache_size = mtdblk->mtd->erasesize; mtdblk->cache_data = NULL; } diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index 1ce365bd31d2..7ccadb1eaf7d 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -24,7 +24,6 @@ struct mtd_oob_buf { }; #define MTD_ABSENT 0 -#define MTD_RAM 1 #define MTD_NORFLASH 3 #define MTD_NANDFLASH 4 #define MTD_DATAFLASH 6 @@ -32,10 +31,11 @@ struct mtd_oob_buf { #define MTD_WRITEABLE 0x400 /* Device is writeable */ #define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ +#define MTD_NO_ERASE 0x1000 /* No erase necessary */ // Some common devices / combinations of capabilities #define MTD_CAP_ROM 0 -#define MTD_CAP_RAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE) +#define MTD_CAP_RAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE) #define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE) #define MTD_CAP_NANDFLASH (MTD_WRITEABLE) -- cgit v1.2.3 From 783ed81ff39d3f938a6b2efd09fbad96e41e5c1f Mon Sep 17 00:00:00 2001 From: "Artem B. Bityutskiy" Date: Wed, 14 Jun 2006 19:53:44 +0400 Subject: [MTD] assume mtd->writesize is 1 for NOR flashes Signed-off-by: Artem B. Bityitskiy --- drivers/mtd/chips/amd_flash.c | 1 + drivers/mtd/chips/cfi_cmdset_0002.c | 1 + drivers/mtd/chips/sharp.c | 1 + drivers/mtd/devices/lart.c | 1 + drivers/mtd/devices/m25p80.c | 1 + drivers/mtd/devices/mtdram.c | 1 + drivers/mtd/mtdcore.c | 1 + include/linux/mtd/mtd.h | 9 ++++++--- 8 files changed, 13 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/mtd/chips/amd_flash.c b/drivers/mtd/chips/amd_flash.c index 9e466509a23d..16eaca69fb5a 100644 --- a/drivers/mtd/chips/amd_flash.c +++ b/drivers/mtd/chips/amd_flash.c @@ -730,6 +730,7 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) offset += dev_size; } mtd->type = MTD_NORFLASH; + mtd->writesize = 1; mtd->flags = MTD_CAP_NORFLASH; mtd->name = map->name; mtd->erase = amd_flash_erase; diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 3aeb0c79e714..1e01ad38b26e 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -236,6 +236,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) mtd->resume = cfi_amdstd_resume; mtd->flags = MTD_CAP_NORFLASH; mtd->name = map->name; + mtd->writesize = 1; if (cfi->cfi_mode==CFI_MODE_CFI){ unsigned char bootloc; diff --git a/drivers/mtd/chips/sharp.c b/drivers/mtd/chips/sharp.c index 3cc0b23c5865..967abbecdff9 100644 --- a/drivers/mtd/chips/sharp.c +++ b/drivers/mtd/chips/sharp.c @@ -140,6 +140,7 @@ static struct mtd_info *sharp_probe(struct map_info *map) mtd->suspend = sharp_suspend; mtd->resume = sharp_resume; mtd->flags = MTD_CAP_NORFLASH; + mtd->writesize = 1; mtd->name = map->name; memset(sharp, 0, sizeof(*sharp)); diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 29b0ddaa324e..4ea50a1dda85 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -635,6 +635,7 @@ int __init lart_flash_init (void) printk ("%s: This looks like a LART board to me.\n",module_name); mtd.name = module_name; mtd.type = MTD_NORFLASH; + mtd.writesize = 1; mtd.flags = MTD_CAP_NORFLASH; mtd.size = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM + FLASH_BLOCKSIZE_MAIN * FLASH_NUMBLOCKS_16m_MAIN; mtd.erasesize = FLASH_BLOCKSIZE_MAIN; diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 04e65d5dae00..a8466141e914 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -465,6 +465,7 @@ static int __devinit m25p_probe(struct spi_device *spi) flash->mtd.name = spi->dev.bus_id; flash->mtd.type = MTD_NORFLASH; + flash->mtd.writesize = 1; flash->mtd.flags = MTD_CAP_NORFLASH; flash->mtd.size = info->sector_size * info->n_sectors; flash->mtd.erasesize = info->sector_size; diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index f284c9670be8..8ab4b931215f 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -106,6 +106,7 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, mtd->type = MTD_GENERIC_TYPE; mtd->flags = MTD_CAP_RAM; mtd->size = size; + mtd->writesize = 1; mtd->erasesize = MTDRAM_ERASE_SIZE; mtd->priv = mapped_address; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 49bc9fdcb88b..16a952dd486a 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -47,6 +47,7 @@ int add_mtd_device(struct mtd_info *mtd) { int i; + BUG_ON(mtd->writesize == 0); mutex_lock(&mtd_table_mutex); for (i=0; i < MAX_MTD_DEVICES; i++) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 9536567d041b..e1d2a3d56546 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -110,9 +110,12 @@ struct mtd_info { * information below if they desire */ u_int32_t erasesize; - /* Smallest availlable size for writing to the device. For NAND, - * this is the page size, for some NOR chips, the size of ECC - * covered blocks. + /* Minimal writable flash unit size. In case of NOR flash it is 1 (even + * though individual bits can be cleared), in case of NAND flash it is + * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR + * it is of ECC block size, etc. It is illegal to have writesize = 0. + * Any driver registering a struct mtd_info must ensure a writesize of + * 1 or larger. */ u_int32_t writesize; -- cgit v1.2.3 From 21c8db9eff95260e543535dfc6f27164c4c0c0ff Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 14 Jun 2006 21:39:48 +0100 Subject: [MTD] Restore MTD_ROM and MTD_RAM types Let's not attempt the abolition of mtd->type until/unless it's properly thought through. And certainly, let's not do it by halves. Signed-off-by: David Woodhouse --- drivers/mtd/chips/map_ram.c | 2 +- drivers/mtd/chips/map_rom.c | 2 +- drivers/mtd/devices/block2mtd.c | 2 +- drivers/mtd/devices/ms02-nv.c | 2 +- drivers/mtd/devices/mtdram.c | 2 +- drivers/mtd/devices/phram.c | 2 +- drivers/mtd/devices/pmc551.c | 2 +- drivers/mtd/devices/slram.c | 2 +- include/mtd/mtd-abi.h | 3 ++- 9 files changed, 10 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index 9b7c49930a76..763925747db6 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -64,7 +64,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map) map->fldrv = &mapram_chipdrv; mtd->priv = map; mtd->name = map->name; - mtd->type = MTD_GENERIC_TYPE; + mtd->type = MTD_RAM; mtd->size = map->size; mtd->erase = mapram_erase; mtd->read = mapram_read; diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index d59ac0136510..bc6ee9ef8a31 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -40,7 +40,7 @@ static struct mtd_info *map_rom_probe(struct map_info *map) map->fldrv = &maprom_chipdrv; mtd->priv = map; mtd->name = map->name; - mtd->type = MTD_GENERIC_TYPE; + mtd->type = MTD_ROM; mtd->size = map->size; mtd->read = maprom_read; mtd->write = maprom_write; diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 1157182c2c8c..0d98c223c5fc 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -324,7 +324,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; dev->mtd.erasesize = erase_size; - dev->mtd.type = MTD_GENERIC_TYPE; + dev->mtd.type = MTD_RAM; dev->mtd.flags = MTD_CAP_RAM; dev->mtd.erase = block2mtd_erase; dev->mtd.write = block2mtd_write; diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c index a6466f923e7e..4ab7670770e4 100644 --- a/drivers/mtd/devices/ms02-nv.c +++ b/drivers/mtd/devices/ms02-nv.c @@ -218,7 +218,7 @@ static int __init ms02nv_init_one(ulong addr) fixsize = (size - (fixaddr - addr)) & ~(PAGE_SIZE - 1); mp->uaddr = phys_to_virt(fixaddr); - mtd->type = MTD_GENERIC_TYPE; + mtd->type = MTD_RAM; mtd->flags = MTD_CAP_RAM; mtd->size = fixsize; mtd->name = (char *)ms02nv_name; diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index 8ab4b931215f..b4438eacfd80 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -103,7 +103,7 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, /* Setup the MTD structure */ mtd->name = name; - mtd->type = MTD_GENERIC_TYPE; + mtd->type = MTD_RAM; mtd->flags = MTD_CAP_RAM; mtd->size = size; mtd->writesize = 1; diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index e6daa74d5fc6..e09e416667d3 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -149,7 +149,7 @@ static int register_device(char *name, unsigned long start, unsigned long len) new->mtd.read = phram_read; new->mtd.write = phram_write; new->mtd.owner = THIS_MODULE; - new->mtd.type = MTD_GENERIC_TYPE; + new->mtd.type = MTD_RAM; new->mtd.erasesize = PAGE_SIZE; ret = -EAGAIN; diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index aa9f6c993125..666cce1bf60c 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -775,7 +775,7 @@ static int __init init_pmc551(void) mtd->write = pmc551_write; mtd->point = pmc551_point; mtd->unpoint = pmc551_unpoint; - mtd->type = MTD_GENERIC_TYPE; + mtd->type = MTD_RAM; mtd->name = "PMC551 RAM board"; mtd->erasesize = 0x10000; mtd->owner = THIS_MODULE; diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index fdb7a78b5304..b3f665e3c38b 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -207,7 +207,7 @@ static int register_device(char *name, unsigned long start, unsigned long length (*curmtd)->mtdinfo->read = slram_read; (*curmtd)->mtdinfo->write = slram_write; (*curmtd)->mtdinfo->owner = THIS_MODULE; - (*curmtd)->mtdinfo->type = MTD_GENERIC_TYPE; + (*curmtd)->mtdinfo->type = MTD_RAM; (*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ; if (add_mtd_device((*curmtd)->mtdinfo)) { diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index 7ccadb1eaf7d..ee2afbaefe1b 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -24,10 +24,11 @@ struct mtd_oob_buf { }; #define MTD_ABSENT 0 +#define MTD_RAM 1 +#define MTD_ROM 2 #define MTD_NORFLASH 3 #define MTD_NANDFLASH 4 #define MTD_DATAFLASH 6 -#define MTD_GENERIC_TYPE 7 #define MTD_WRITEABLE 0x400 /* Device is writeable */ #define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ -- cgit v1.2.3 From 7bc3312bef4d6f220812500c0de7868fb7625a41 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 20 Jun 2006 20:05:05 +0200 Subject: [MTD] NAND: Fix breakage all over the place Following problems are addressed: - wrong status caused early break out of nand_wait() - removed the bogus status check in nand_wait() which is a relict of the abandoned support for interrupted erase. - status check moved to the correct place in read_oob - oob support for syndrom based ecc with strange layouts - use given offset in the AUTOOOB based oob operations Partially based on a patch from Vitaly Vool Thanks to Savin Zlobec for tracking down the status problem. Signed-off-by: Thomas Gleixner --- drivers/mtd/mtdchar.c | 8 +- drivers/mtd/nand/diskonchip.c | 2 +- drivers/mtd/nand/nand_base.c | 297 ++++++++++++++++++++++++++++++------------ include/linux/mtd/nand.h | 12 +- 4 files changed, 231 insertions(+), 88 deletions(-) (limited to 'include') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 5dd0b8d72c8b..aa18d45b264b 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -504,12 +504,12 @@ static int mtd_ioctl(struct inode *inode, struct file *file, return ret; ops.len = buf.length; - ops.ooblen = mtd->oobsize; + ops.ooblen = buf.length; ops.ooboffs = buf.start & (mtd->oobsize - 1); ops.datbuf = NULL; ops.mode = MTD_OOB_PLACE; - if (ops.ooboffs && ops.len > (ops.ooblen - ops.ooboffs)) + if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs)) return -EINVAL; ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); @@ -553,12 +553,12 @@ static int mtd_ioctl(struct inode *inode, struct file *file, return ret; ops.len = buf.length; - ops.ooblen = mtd->oobsize; + ops.ooblen = buf.length; ops.ooboffs = buf.start & (mtd->oobsize - 1); ops.datbuf = NULL; ops.mode = MTD_OOB_PLACE; - if (ops.ooboffs && ops.len > (ops.ooblen - ops.ooboffs)) + if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs)) return -EINVAL; ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 463e12ced1b3..6107f532855b 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -464,7 +464,7 @@ static void __init doc2000_count_chips(struct mtd_info *mtd) printk(KERN_DEBUG "Detected %d chips per floor.\n", i); } -static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state) +static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this) { struct doc_priv *doc = this->priv; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index e74678e928cf..27083ed0a017 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -501,7 +501,6 @@ static void nand_command(struct mtd_info *mtd, unsigned int command, case NAND_CMD_ERASE2: case NAND_CMD_SEQIN: case NAND_CMD_STATUS: - chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE); return; case NAND_CMD_RESET: @@ -595,6 +594,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: case NAND_CMD_STATUS: case NAND_CMD_DEPLETE1: return; @@ -621,6 +621,14 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ; return; + case NAND_CMD_RNDOUT: + /* No ready / busy check necessary */ + chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + return; + case NAND_CMD_READ0: chip->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); @@ -689,18 +697,17 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) * nand_wait - [DEFAULT] wait until the command is done * @mtd: MTD device structure * @this: NAND chip structure - * @state: state to select the max. timeout value * * 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 nand_wait(struct mtd_info *mtd, struct nand_chip *chip, int state) +static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) { unsigned long timeo = jiffies; - int status; + int status, state = chip->state; if (state == FL_ERASING) timeo += (HZ * 400) / 1000; @@ -719,10 +726,6 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip, int state) chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); while (time_before(jiffies, timeo)) { - /* Check, if we were interrupted */ - if (chip->state != state) - return 0; - if (chip->dev_ready) { if (chip->dev_ready(mtd)) break; @@ -909,12 +912,25 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, case MTD_OOB_AUTO: { struct nand_oobfree *free = chip->ecc.layout->oobfree; - size_t bytes; + uint32_t boffs = 0, roffs = ops->ooboffs; + size_t bytes = 0; for(; free->length && len; free++, len -= bytes) { - bytes = min_t(size_t, len, free->length); - - memcpy(oob, chip->oob_poi + free->offset, bytes); + /* Read request not from offset 0 ? */ + if (unlikely(roffs)) { + if (roffs >= free->length) { + roffs -= free->length; + continue; + } + boffs = free->offset + roffs; + bytes = min_t(size_t, len, + (free->length - roffs)); + roffs = 0; + } else { + bytes = min_t(size_t, len, free->length); + boffs = free->offset; + } + memcpy(oob, chip->oob_poi + boffs, bytes); oob += bytes; } return oob; @@ -1083,6 +1099,145 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, return ret; } +/** + * nand_read_oob_std - [REPLACABLE] the most common OOB data read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + * @sndcmd: flag whether to issue read command or not + */ +static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return sndcmd; +} + +/** + * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC + * with syndromes + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + * @sndcmd: flag whether to issue read command or not + */ +static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + uint8_t *buf = chip->oob_poi; + int length = mtd->oobsize; + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size; + uint8_t *bufpoi = buf; + int i, toread, sndrnd = 0, pos; + + chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); + for (i = 0; i < chip->ecc.steps; i++) { + if (sndrnd) { + pos = eccsize + i * (eccsize + chunk); + if (mtd->writesize > 512) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1); + else + chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page); + } else + sndrnd = 1; + toread = min_t(int, length, chunk); + chip->read_buf(mtd, bufpoi, toread); + bufpoi += toread; + length -= toread; + } + if (length > 0) + chip->read_buf(mtd, bufpoi, length); + + return 1; +} + +/** + * nand_write_oob_std - [REPLACABLE] the most common OOB data write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int status = 0; + const uint8_t *buf = chip->oob_poi; + int length = mtd->oobsize; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + chip->write_buf(mtd, buf, length); + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + + return status; +} + +/** + * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC + * with syndrome - only for large page flash ! + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +static int nand_write_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size, length = mtd->oobsize; + int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps; + const uint8_t *bufpoi = chip->oob_poi; + + /* + * data-ecc-data-ecc ... ecc-oob + * or + * data-pad-ecc-pad-data-pad .... ecc-pad-oob + */ + if (!chip->ecc.prepad && !chip->ecc.postpad) { + pos = steps * (eccsize + chunk); + steps = 0; + } else + pos = eccsize + chunk; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); + for (i = 0; i < steps; i++) { + if (sndcmd) { + if (mtd->writesize <= 512) { + uint32_t fill = 0xFFFFFFFF; + + len = eccsize; + while (len > 0) { + int num = min_t(int, len, 4); + chip->write_buf(mtd, (uint8_t *)&fill, + num); + len -= num; + } + } else { + pos = eccsize + i * (eccsize + chunk); + chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1); + } + } else + sndcmd = 1; + len = min_t(int, length, chunk); + chip->write_buf(mtd, bufpoi, len); + bufpoi += len; + length -= len; + } + if (length > 0) + chip->write_buf(mtd, bufpoi, length); + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + /** * nand_do_read_oob - [Intern] NAND read out-of-band * @mtd: MTD device structure @@ -1094,11 +1249,11 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - int col, page, realpage, chipnr, sndcmd = 1; + int page, realpage, chipnr, sndcmd = 1; struct nand_chip *chip = mtd->priv; int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; - int direct, bytes, readlen = ops->len; - uint8_t *bufpoi, *buf = ops->oobbuf; + int readlen = ops->len; + uint8_t *buf = ops->oobbuf; DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", (unsigned long long)from, readlen); @@ -1110,29 +1265,11 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; - if (ops->mode != MTD_OOB_AUTO) { - col = ops->ooboffs; - direct = 1; - } else { - col = 0; - direct = 0; - } + chip->oob_poi = chip->buffers.oobrbuf; while(1) { - bytes = direct ? ops->ooblen : mtd->oobsize; - bufpoi = direct ? buf : chip->buffers.oobrbuf; - - if (likely(sndcmd)) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, col, page); - sndcmd = 0; - } - - chip->read_buf(mtd, bufpoi, bytes); - - if (unlikely(!direct)) - buf = nand_transfer_oob(chip, buf, ops); - else - buf += ops->ooblen; + sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); + buf = nand_transfer_oob(chip, buf, ops); readlen -= ops->ooblen; if (!readlen) @@ -1365,7 +1502,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, if (!cached || !(chip->options & NAND_CACHEPRG)) { chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip, FL_WRITING); + status = chip->waitfunc(mtd, chip); /* * See if operation failed and additional status checks are * available @@ -1378,7 +1515,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, return -EIO; } else { chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); - status = chip->waitfunc(mtd, chip, FL_WRITING); + status = chip->waitfunc(mtd, chip); } #ifdef CONFIG_MTD_NAND_VERIFY_WRITE @@ -1411,11 +1548,25 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, case MTD_OOB_AUTO: { struct nand_oobfree *free = chip->ecc.layout->oobfree; - size_t bytes; + uint32_t boffs = 0, woffs = ops->ooboffs; + size_t bytes = 0; for(; free->length && len; free++, len -= bytes) { - bytes = min_t(size_t, len, free->length); - memcpy(chip->oob_poi + free->offset, oob, bytes); + /* Write request not from offset 0 ? */ + if (unlikely(woffs)) { + if (woffs >= free->length) { + woffs -= free->length; + continue; + } + boffs = free->offset + woffs; + bytes = min_t(size_t, len, + (free->length - woffs)); + woffs = 0; + } else { + bytes = min_t(size_t, len, free->length); + boffs = free->offset; + } + memcpy(chip->oob_poi + woffs, oob, bytes); oob += bytes; } return oob; @@ -1532,7 +1683,7 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, if (!len) return 0; - nand_get_device(chip, mtd, FL_READING); + nand_get_device(chip, mtd, FL_WRITING); chip->ops.len = len; chip->ops.datbuf = (uint8_t *)buf; @@ -1592,48 +1743,18 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, if (page == chip->pagebuf) chip->pagebuf = -1; - if (ops->mode == MTD_OOB_AUTO || NAND_MUST_PAD(chip)) { - chip->oob_poi = chip->buffers.oobwbuf; - memset(chip->oob_poi, 0xff, mtd->oobsize); - nand_fill_oob(chip, ops->oobbuf, ops); - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, - page & chip->pagemask); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - memset(chip->oob_poi, 0xff, mtd->oobsize); - } else { - chip->cmdfunc(mtd, NAND_CMD_SEQIN, - mtd->writesize + ops->ooboffs, - page & chip->pagemask); - chip->write_buf(mtd, ops->oobbuf, ops->len); - } - - /* Send command to program the OOB data */ - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + chip->oob_poi = chip->buffers.oobwbuf; + memset(chip->oob_poi, 0xff, mtd->oobsize); + nand_fill_oob(chip, ops->oobbuf, ops); + status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); + memset(chip->oob_poi, 0xff, mtd->oobsize); - status = chip->waitfunc(mtd, chip, FL_WRITING); + if (status) + return status; - /* See if device thinks it succeeded */ - if (status & NAND_STATUS_FAIL) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " - "Failed write, page 0x%08x\n", page); - return -EIO; - } ops->retlen = ops->len; -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE - if (ops->mode != MTD_OOB_AUTO) { - /* Send command to read back the data */ - chip->cmdfunc(mtd, NAND_CMD_READOOB, ops->ooboffs, - page & chip->pagemask); - - if (chip->verify_buf(mtd, ops->oobbuf, ops->len)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " - "Failed write verify, page 0x%08x\n", page); - return -EIO; - } - } -#endif - return 0; + return 0; } /** @@ -1659,7 +1780,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, return -EINVAL; } - nand_get_device(chip, mtd, FL_READING); + nand_get_device(chip, mtd, FL_WRITING); switch(ops->mode) { case MTD_OOB_PLACE: @@ -1833,7 +1954,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, chip->erase_cmd(mtd, page & chip->pagemask); - status = chip->waitfunc(mtd, chip, FL_ERASING); + status = chip->waitfunc(mtd, chip); /* * See if operation failed and additional status checks are @@ -2265,6 +2386,10 @@ int nand_scan(struct mtd_info *mtd, int maxchips) chip->ecc.read_page = nand_read_page_hwecc; if (!chip->ecc.write_page) chip->ecc.write_page = nand_write_page_hwecc; + if (!chip->ecc.read_oob) + chip->ecc.read_oob = nand_read_oob_std; + if (!chip->ecc.write_oob) + chip->ecc.write_oob = nand_write_oob_std; case NAND_ECC_HW_SYNDROME: if (!chip->ecc.calculate || !chip->ecc.correct || @@ -2278,6 +2403,10 @@ int nand_scan(struct mtd_info *mtd, int maxchips) chip->ecc.read_page = nand_read_page_syndrome; if (!chip->ecc.write_page) chip->ecc.write_page = nand_write_page_syndrome; + if (!chip->ecc.read_oob) + chip->ecc.read_oob = nand_read_oob_syndrome; + if (!chip->ecc.write_oob) + chip->ecc.write_oob = nand_write_oob_syndrome; if (mtd->writesize >= chip->ecc.size) break; @@ -2291,6 +2420,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips) chip->ecc.correct = nand_correct_data; chip->ecc.read_page = nand_read_page_swecc; chip->ecc.write_page = nand_write_page_swecc; + chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.write_oob = nand_write_oob_std; chip->ecc.size = 256; chip->ecc.bytes = 3; break; @@ -2300,6 +2431,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips) "This is not recommended !!\n"); chip->ecc.read_page = nand_read_page_raw; chip->ecc.write_page = nand_write_page_raw; + chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.write_oob = nand_write_oob_std; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; break; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index bf2ce68901f5..a30969eb9afe 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -63,18 +63,21 @@ extern void nand_release (struct mtd_info *mtd); */ #define NAND_CMD_READ0 0 #define NAND_CMD_READ1 1 +#define NAND_CMD_RNDOUT 5 #define NAND_CMD_PAGEPROG 0x10 #define NAND_CMD_READOOB 0x50 #define NAND_CMD_ERASE1 0x60 #define NAND_CMD_STATUS 0x70 #define NAND_CMD_STATUS_MULTI 0x71 #define NAND_CMD_SEQIN 0x80 +#define NAND_CMD_RNDIN 0x85 #define NAND_CMD_READID 0x90 #define NAND_CMD_ERASE2 0xd0 #define NAND_CMD_RESET 0xff /* Extended commands for large page devices */ #define NAND_CMD_READSTART 0x30 +#define NAND_CMD_RNDOUTSTART 0xE0 #define NAND_CMD_CACHEDPROG 0x15 /* Extended commands for AG-AND device */ @@ -250,6 +253,13 @@ struct nand_ecc_ctrl { void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf); + int (*read_oob)(struct mtd_info *mtd, + struct nand_chip *chip, + int page, + int sndcmd); + int (*write_oob)(struct mtd_info *mtd, + struct nand_chip *chip, + int page); }; /** @@ -339,7 +349,7 @@ struct nand_chip { unsigned int ctrl); int (*dev_ready)(struct mtd_info *mtd); void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); - int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state); + int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this); void (*erase_cmd)(struct mtd_info *mtd, int page); int (*scan_bbt)(struct mtd_info *mtd); int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); -- cgit v1.2.3