diff options
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r-- | drivers/mtd/nand/fsl_elbc_nand.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/fsl_upm.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 25 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_bbt.c | 31 | ||||
-rw-r--r-- | drivers/mtd/nand/nandsim.c | 338 | ||||
-rw-r--r-- | drivers/mtd/nand/plat_nand.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/sharpsl.c | 247 | ||||
-rw-r--r-- | drivers/mtd/nand/tmio_nand.c | 2 |
8 files changed, 453 insertions, 198 deletions
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 4aa5bd6158da..65929db29446 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -777,7 +777,9 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) /* Fill in fsl_elbc_mtd structure */ priv->mtd.priv = chip; priv->mtd.owner = THIS_MODULE; - priv->fmr = 0; /* rest filled in later */ + + /* Set the ECCM according to the settings in bootloader.*/ + priv->fmr = in_be32(&lbc->fmr) & FMR_ECCM; /* fill in nand_chip structure */ /* set up function call table */ diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index a83192f80eba..7815a404a632 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -222,7 +222,7 @@ static int __devinit fun_probe(struct of_device *ofdev, fun->rnb_gpio = of_get_gpio(ofdev->node, 0); if (fun->rnb_gpio >= 0) { - ret = gpio_request(fun->rnb_gpio, ofdev->dev.bus_id); + ret = gpio_request(fun->rnb_gpio, dev_name(&ofdev->dev)); if (ret) { dev_err(&ofdev->dev, "can't request RNB gpio\n"); goto err2; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 0a9c9cd33f96..0c3afccde8a2 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2014,13 +2014,14 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int allowbbt) { - int page, len, status, pages_per_block, ret, chipnr; + int page, status, pages_per_block, ret, chipnr; struct nand_chip *chip = mtd->priv; - int rewrite_bbt[NAND_MAX_CHIPS]={0}; + loff_t rewrite_bbt[NAND_MAX_CHIPS]={0}; unsigned int bbt_masked_page = 0xffffffff; + loff_t len; - DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", - (unsigned int)instr->addr, (unsigned int)instr->len); + DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%012llx, len = %llu\n", + (unsigned long long)instr->addr, (unsigned long long)instr->len); /* Start address must align on block boundary */ if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { @@ -2116,7 +2117,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; - instr->fail_addr = (page << chip->page_shift); + instr->fail_addr = + ((loff_t)page << chip->page_shift); goto erase_exit; } @@ -2126,7 +2128,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, */ if (bbt_masked_page != 0xffffffff && (page & BBT_PAGE_MASK) == bbt_masked_page) - rewrite_bbt[chipnr] = (page << chip->page_shift); + rewrite_bbt[chipnr] = + ((loff_t)page << chip->page_shift); /* Increment page address and decrement length */ len -= (1 << chip->phys_erase_shift); @@ -2173,7 +2176,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, continue; /* update the BBT for chip */ DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " - "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr], + "(%d:0x%0llx 0x%0x)\n", chipnr, rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]); nand_update_bbt(mtd, rewrite_bbt[chipnr]); } @@ -2365,7 +2368,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (!mtd->name) mtd->name = type->name; - chip->chipsize = type->chipsize << 20; + chip->chipsize = (uint64_t)type->chipsize << 20; /* Newer devices have all the information in additional id bytes */ if (!type->pagesize) { @@ -2423,7 +2426,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->bbt_erase_shift = chip->phys_erase_shift = ffs(mtd->erasesize) - 1; - chip->chip_shift = ffs(chip->chipsize) - 1; + if (chip->chipsize & 0xffffffff) + chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; + else + chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 32 - 1; /* Set the bad block position */ chip->badblockpos = mtd->writesize > 512 ? @@ -2517,7 +2523,6 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) /** * nand_scan_tail - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure - * @maxchips: Number of chips to scan for * * This is the second phase of the normal nand_scan() function. It * fills out all the uninitialized function pointers with the defaults diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 0b1c48595f12..55c23e5cd210 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -171,16 +171,16 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, if (tmp == msk) continue; if (reserved_block_code && (tmp == reserved_block_code)) { - printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", - ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%012llx\n", + (loff_t)((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 * message to MTD_DEBUG_LEVEL0 */ - printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n", - ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%012llx\n", + (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); /* Factory marked bad or worn out ? */ if (tmp == 0) this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); @@ -284,7 +284,7 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, /* Read the primary version, if available */ if (td->options & NAND_BBT_VERSION) { - scan_read_raw(mtd, buf, td->pages[0] << this->page_shift, + scan_read_raw(mtd, buf, (loff_t)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", @@ -293,7 +293,7 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, /* Read the mirror version, if available */ if (md && (md->options & NAND_BBT_VERSION)) { - scan_read_raw(mtd, buf, md->pages[0] << this->page_shift, + scan_read_raw(mtd, buf, (loff_t)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", @@ -411,7 +411,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, numblocks = this->chipsize >> (this->bbt_erase_shift - 1); startblock = chip * numblocks; numblocks += startblock; - from = startblock << (this->bbt_erase_shift - 1); + from = (loff_t)startblock << (this->bbt_erase_shift - 1); } for (i = startblock; i < numblocks;) { @@ -428,8 +428,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, if (ret) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); - printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", - i >> 1, (unsigned int)from); + printk(KERN_WARNING "Bad eraseblock %d at 0x%012llx\n", + i >> 1, (unsigned long long)from); mtd->ecc_stats.badblocks++; } @@ -495,7 +495,7 @@ 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; - loff_t offs = actblock << this->bbt_erase_shift; + loff_t offs = (loff_t)actblock << this->bbt_erase_shift; /* Read first page */ scan_read_raw(mtd, buf, offs, mtd->writesize); @@ -719,7 +719,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, memset(&einfo, 0, sizeof(einfo)); einfo.mtd = mtd; - einfo.addr = (unsigned long)to; + einfo.addr = to; einfo.len = 1 << this->bbt_erase_shift; res = nand_erase_nand(mtd, &einfo, 1); if (res < 0) @@ -729,8 +729,8 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, 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]); + printk(KERN_DEBUG "Bad block table written to 0x%012llx, version " + "0x%02X\n", (unsigned long long)to, td->version[chip]); /* Mark it as used */ td->pages[chip] = page; @@ -910,7 +910,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) newval = oldval | (0x2 << (block & 0x06)); this->bbt[(block >> 3)] = newval; if ((oldval != newval) && td->reserved_block_code) - nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1)); + nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1)); continue; } update = 0; @@ -931,7 +931,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) new ones have been marked, then we need to update the stored bbts. This should only happen once. */ if (update && td->reserved_block_code) - nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1)); + nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1)); } } @@ -1027,7 +1027,6 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs) if (!this->bbt || !td) return -EINVAL; - len = mtd->size >> (this->bbt_erase_shift + 2); /* Allocate a temporary buffer for one eraseblock incl. oob */ len = (1 << this->bbt_erase_shift); len += (len >> this->page_shift) * mtd->oobsize; diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index ae7c57781a68..23b53fc405f5 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -38,6 +38,8 @@ #include <linux/delay.h> #include <linux/list.h> #include <linux/random.h> +#include <linux/fs.h> +#include <linux/pagemap.h> /* Default simulator parameters values */ #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ @@ -100,6 +102,7 @@ static unsigned int bitflips = 0; static char *gravepages = NULL; static unsigned int rptwear = 0; static unsigned int overridesize = 0; +static char *cache_file = NULL; module_param(first_id_byte, uint, 0400); module_param(second_id_byte, uint, 0400); @@ -122,12 +125,13 @@ module_param(bitflips, uint, 0400); module_param(gravepages, charp, 0400); module_param(rptwear, uint, 0400); module_param(overridesize, uint, 0400); +module_param(cache_file, charp, 0400); MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)"); MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)"); MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command"); MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command"); -MODULE_PARM_DESC(access_delay, "Initial page access delay (microiseconds)"); +MODULE_PARM_DESC(access_delay, "Initial page access delay (microseconds)"); MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds"); MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)"); MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanodeconds)"); @@ -153,6 +157,7 @@ MODULE_PARM_DESC(rptwear, "Number of erases inbetween reporting wear, if MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. " "The size is specified in erase blocks and as the exponent of a power of two" " e.g. 5 means a size of 32 erase blocks"); +MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory"); /* The largest possible page size */ #define NS_LARGEST_PAGE_SIZE 2048 @@ -266,6 +271,9 @@ MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the I */ #define NS_MAX_PREVSTATES 1 +/* Maximum page cache pages needed to read or write a NAND page to the cache_file */ +#define NS_MAX_HELD_PAGES 16 + /* * A union to represent flash memory contents and flash buffer. */ @@ -295,6 +303,9 @@ struct nandsim { /* The simulated NAND flash pages array */ union ns_mem *pages; + /* Slab allocator for nand pages */ + struct kmem_cache *nand_pages_slab; + /* Internal buffer of page + OOB size bytes */ union ns_mem buf; @@ -335,6 +346,13 @@ struct nandsim { int ale; /* address Latch Enable */ int wp; /* write Protect */ } lines; + + /* Fields needed when using a cache file */ + struct file *cfile; /* Open file */ + unsigned char *pages_written; /* Which pages have been written */ + void *file_buf; + struct page *held_pages[NS_MAX_HELD_PAGES]; + int held_cnt; }; /* @@ -420,25 +438,69 @@ static struct mtd_info *nsmtd; static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE]; /* - * Allocate array of page pointers and initialize the array to NULL - * pointers. + * Allocate array of page pointers, create slab allocation for an array + * and initialize the array by NULL pointers. * * RETURNS: 0 if success, -ENOMEM if memory alloc fails. */ static int alloc_device(struct nandsim *ns) { - int i; + struct file *cfile; + int i, err; + + if (cache_file) { + cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600); + if (IS_ERR(cfile)) + return PTR_ERR(cfile); + if (!cfile->f_op || (!cfile->f_op->read && !cfile->f_op->aio_read)) { + NS_ERR("alloc_device: cache file not readable\n"); + err = -EINVAL; + goto err_close; + } + if (!cfile->f_op->write && !cfile->f_op->aio_write) { + NS_ERR("alloc_device: cache file not writeable\n"); + err = -EINVAL; + goto err_close; + } + ns->pages_written = vmalloc(ns->geom.pgnum); + if (!ns->pages_written) { + NS_ERR("alloc_device: unable to allocate pages written array\n"); + err = -ENOMEM; + goto err_close; + } + ns->file_buf = kmalloc(ns->geom.pgszoob, GFP_KERNEL); + if (!ns->file_buf) { + NS_ERR("alloc_device: unable to allocate file buf\n"); + err = -ENOMEM; + goto err_free; + } + ns->cfile = cfile; + memset(ns->pages_written, 0, ns->geom.pgnum); + return 0; + } ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem)); if (!ns->pages) { - NS_ERR("alloc_map: unable to allocate page array\n"); + NS_ERR("alloc_device: unable to allocate page array\n"); return -ENOMEM; } for (i = 0; i < ns->geom.pgnum; i++) { ns->pages[i].byte = NULL; } + ns->nand_pages_slab = kmem_cache_create("nandsim", + ns->geom.pgszoob, 0, 0, NULL); + if (!ns->nand_pages_slab) { + NS_ERR("cache_create: unable to create kmem_cache\n"); + return -ENOMEM; + } return 0; + +err_free: + vfree(ns->pages_written); +err_close: + filp_close(cfile, NULL); + return err; } /* @@ -448,11 +510,20 @@ static void free_device(struct nandsim *ns) { int i; + if (ns->cfile) { + kfree(ns->file_buf); + vfree(ns->pages_written); + filp_close(ns->cfile, NULL); + return; + } + if (ns->pages) { for (i = 0; i < ns->geom.pgnum; i++) { if (ns->pages[i].byte) - kfree(ns->pages[i].byte); + kmem_cache_free(ns->nand_pages_slab, + ns->pages[i].byte); } + kmem_cache_destroy(ns->nand_pages_slab); vfree(ns->pages); } } @@ -464,7 +535,7 @@ static char *get_partition_name(int i) return kstrdup(buf, GFP_KERNEL); } -static u_int64_t divide(u_int64_t n, u_int32_t d) +static uint64_t divide(uint64_t n, uint32_t d) { do_div(n, d); return n; @@ -480,8 +551,8 @@ static int init_nandsim(struct mtd_info *mtd) struct nand_chip *chip = (struct nand_chip *)mtd->priv; struct nandsim *ns = (struct nandsim *)(chip->priv); int i, ret = 0; - u_int64_t remains; - u_int64_t next_offset; + uint64_t remains; + uint64_t next_offset; if (NS_IS_INITIALIZED(ns)) { NS_ERR("init_nandsim: nandsim is already initialized\n"); @@ -548,7 +619,7 @@ static int init_nandsim(struct mtd_info *mtd) remains = ns->geom.totsz; next_offset = 0; for (i = 0; i < parts_num; ++i) { - u_int64_t part_sz = (u_int64_t)parts[i] * ns->geom.secsz; + uint64_t part_sz = (uint64_t)parts[i] * ns->geom.secsz; if (!part_sz || part_sz > remains) { NS_ERR("bad partition size.\n"); @@ -1211,6 +1282,97 @@ static int find_operation(struct nandsim *ns, uint32_t flag) return -1; } +static void put_pages(struct nandsim *ns) +{ + int i; + + for (i = 0; i < ns->held_cnt; i++) + page_cache_release(ns->held_pages[i]); +} + +/* Get page cache pages in advance to provide NOFS memory allocation */ +static int get_pages(struct nandsim *ns, struct file *file, size_t count, loff_t pos) +{ + pgoff_t index, start_index, end_index; + struct page *page; + struct address_space *mapping = file->f_mapping; + + start_index = pos >> PAGE_CACHE_SHIFT; + end_index = (pos + count - 1) >> PAGE_CACHE_SHIFT; + if (end_index - start_index + 1 > NS_MAX_HELD_PAGES) + return -EINVAL; + ns->held_cnt = 0; + for (index = start_index; index <= end_index; index++) { + page = find_get_page(mapping, index); + if (page == NULL) { + page = find_or_create_page(mapping, index, GFP_NOFS); + if (page == NULL) { + write_inode_now(mapping->host, 1); + page = find_or_create_page(mapping, index, GFP_NOFS); + } + if (page == NULL) { + put_pages(ns); + return -ENOMEM; + } + unlock_page(page); + } + ns->held_pages[ns->held_cnt++] = page; + } + return 0; +} + +static int set_memalloc(void) +{ + if (current->flags & PF_MEMALLOC) + return 0; + current->flags |= PF_MEMALLOC; + return 1; +} + +static void clear_memalloc(int memalloc) +{ + if (memalloc) + current->flags &= ~PF_MEMALLOC; +} + +static ssize_t read_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t *pos) +{ + mm_segment_t old_fs; + ssize_t tx; + int err, memalloc; + + err = get_pages(ns, file, count, *pos); + if (err) + return err; + old_fs = get_fs(); + set_fs(get_ds()); + memalloc = set_memalloc(); + tx = vfs_read(file, (char __user *)buf, count, pos); + clear_memalloc(memalloc); + set_fs(old_fs); + put_pages(ns); + return tx; +} + +static ssize_t write_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t *pos) +{ + mm_segment_t old_fs; + ssize_t tx; + int err, memalloc; + + err = get_pages(ns, file, count, *pos); + if (err) + return err; + old_fs = get_fs(); + set_fs(get_ds()); + memalloc = set_memalloc(); + tx = vfs_write(file, (char __user *)buf, count, pos); + clear_memalloc(memalloc); + set_fs(old_fs); + put_pages(ns); + return tx; +} + /* * Returns a pointer to the current page. */ @@ -1227,6 +1389,38 @@ static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns) return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off; } +int do_read_error(struct nandsim *ns, int num) +{ + unsigned int page_no = ns->regs.row; + + if (read_error(page_no)) { + int i; + memset(ns->buf.byte, 0xFF, num); + for (i = 0; i < num; ++i) + ns->buf.byte[i] = random32(); + NS_WARN("simulating read error in page %u\n", page_no); + return 1; + } + return 0; +} + +void do_bit_flips(struct nandsim *ns, int num) +{ + if (bitflips && random32() < (1 << 22)) { + int flips = 1; + if (bitflips > 1) + flips = (random32() % (int) bitflips) + 1; + while (flips--) { + int pos = random32() % (num * 8); + ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); + NS_WARN("read_page: flipping bit %d in page %d " + "reading from %d ecc: corrected=%u failed=%u\n", + pos, ns->regs.row, ns->regs.column + ns->regs.off, + nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed); + } + } +} + /* * Fill the NAND buffer with data read from the specified page. */ @@ -1234,36 +1428,40 @@ static void read_page(struct nandsim *ns, int num) { union ns_mem *mypage; + if (ns->cfile) { + if (!ns->pages_written[ns->regs.row]) { + NS_DBG("read_page: page %d not written\n", ns->regs.row); + memset(ns->buf.byte, 0xFF, num); + } else { + loff_t pos; + ssize_t tx; + + NS_DBG("read_page: page %d written, reading from %d\n", + ns->regs.row, ns->regs.column + ns->regs.off); + if (do_read_error(ns, num)) + return; + pos = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off; + tx = read_file(ns, ns->cfile, ns->buf.byte, num, &pos); + if (tx != num) { + NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); + return; + } + do_bit_flips(ns, num); + } + return; + } + mypage = NS_GET_PAGE(ns); if (mypage->byte == NULL) { NS_DBG("read_page: page %d not allocated\n", ns->regs.row); memset(ns->buf.byte, 0xFF, num); } else { - unsigned int page_no = ns->regs.row; NS_DBG("read_page: page %d allocated, reading from %d\n", ns->regs.row, ns->regs.column + ns->regs.off); - if (read_error(page_no)) { - int i; - memset(ns->buf.byte, 0xFF, num); - for (i = 0; i < num; ++i) - ns->buf.byte[i] = random32(); - NS_WARN("simulating read error in page %u\n", page_no); + if (do_read_error(ns, num)) return; - } memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num); - if (bitflips && random32() < (1 << 22)) { - int flips = 1; - if (bitflips > 1) - flips = (random32() % (int) bitflips) + 1; - while (flips--) { - int pos = random32() % (num * 8); - ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); - NS_WARN("read_page: flipping bit %d in page %d " - "reading from %d ecc: corrected=%u failed=%u\n", - pos, ns->regs.row, ns->regs.column + ns->regs.off, - nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed); - } - } + do_bit_flips(ns, num); } } @@ -1275,11 +1473,20 @@ static void erase_sector(struct nandsim *ns) union ns_mem *mypage; int i; + if (ns->cfile) { + for (i = 0; i < ns->geom.pgsec; i++) + if (ns->pages_written[ns->regs.row + i]) { + NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i); + ns->pages_written[ns->regs.row + i] = 0; + } + return; + } + mypage = NS_GET_PAGE(ns); for (i = 0; i < ns->geom.pgsec; i++) { if (mypage->byte != NULL) { NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i); - kfree(mypage->byte); + kmem_cache_free(ns->nand_pages_slab, mypage->byte); mypage->byte = NULL; } mypage++; @@ -1295,16 +1502,57 @@ static int prog_page(struct nandsim *ns, int num) union ns_mem *mypage; u_char *pg_off; + if (ns->cfile) { + loff_t off, pos; + ssize_t tx; + int all; + + NS_DBG("prog_page: writing page %d\n", ns->regs.row); + pg_off = ns->file_buf + ns->regs.column + ns->regs.off; + off = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off; + if (!ns->pages_written[ns->regs.row]) { + all = 1; + memset(ns->file_buf, 0xff, ns->geom.pgszoob); + } else { + all = 0; + pos = off; + tx = read_file(ns, ns->cfile, pg_off, num, &pos); + if (tx != num) { + NS_ERR("prog_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); + return -1; + } + } + for (i = 0; i < num; i++) + pg_off[i] &= ns->buf.byte[i]; + if (all) { + pos = (loff_t)ns->regs.row * ns->geom.pgszoob; + tx = write_file(ns, ns->cfile, ns->file_buf, ns->geom.pgszoob, &pos); + if (tx != ns->geom.pgszoob) { + NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); + return -1; + } + ns->pages_written[ns->regs.row] = 1; + } else { + pos = off; + tx = write_file(ns, ns->cfile, pg_off, num, &pos); + if (tx != num) { + NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); + return -1; + } + } + return 0; + } + mypage = NS_GET_PAGE(ns); if (mypage->byte == NULL) { NS_DBG("prog_page: allocating page %d\n", ns->regs.row); /* * We allocate memory with GFP_NOFS because a flash FS may * utilize this. If it is holding an FS lock, then gets here, - * then kmalloc runs writeback which goes to the FS again - * and deadlocks. This was seen in practice. + * then kernel memory alloc runs writeback which goes to the FS + * again and deadlocks. This was seen in practice. */ - mypage->byte = kmalloc(ns->geom.pgszoob, GFP_NOFS); + mypage->byte = kmem_cache_alloc(ns->nand_pages_slab, GFP_NOFS); if (mypage->byte == NULL) { NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row); return -1; @@ -1736,13 +1984,17 @@ static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte) /* Check if chip is expecting command */ if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) { - /* - * We are in situation when something else (not command) - * was expected but command was input. In this case ignore - * previous command(s)/state(s) and accept the last one. - */ - NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, " - "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate)); + /* Do not warn if only 2 id bytes are read */ + if (!(ns->regs.command == NAND_CMD_READID && + NS_STATE(ns->state) == STATE_DATAOUT_ID && ns->regs.count == 2)) { + /* + * We are in situation when something else (not command) + * was expected but command was input. In this case ignore + * previous command(s)/state(s) and accept the last one. + */ + NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, " + "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate)); + } switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); } @@ -2044,7 +2296,7 @@ static int __init ns_init_module(void) } if (overridesize) { - u_int64_t new_size = (u_int64_t)nsmtd->erasesize << overridesize; + uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize; if (new_size >> overridesize != nsmtd->erasesize) { NS_ERR("overridesize is too big\n"); goto err_exit; diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index f674c5427b17..75f9f4874ecf 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -54,7 +54,7 @@ static int __init plat_nand_probe(struct platform_device *pdev) data->chip.priv = &data; data->mtd.priv = &data->chip; data->mtd.owner = THIS_MODULE; - data->mtd.name = pdev->dev.bus_id; + data->mtd.name = dev_name(&pdev->dev); data->chip.IO_ADDR_R = data->io_base; data->chip.IO_ADDR_W = data->io_base; diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 30a518e211bd..54ec7542a7b7 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -2,6 +2,7 @@ * drivers/mtd/nand/sharpsl.c * * Copyright (C) 2004 Richard Purdie + * Copyright (C) 2008 Dmitry Baryshkov * * Based on Sharp's NAND driver sharp_sl.c * @@ -19,22 +20,31 @@ #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/sharpsl.h> #include <linux/interrupt.h> +#include <linux/platform_device.h> + #include <asm/io.h> #include <mach/hardware.h> #include <asm/mach-types.h> -static void __iomem *sharpsl_io_base; -static int sharpsl_phys_base = 0x0C000000; +struct sharpsl_nand { + struct mtd_info mtd; + struct nand_chip chip; + + void __iomem *io; +}; + +#define mtd_to_sharpsl(_mtd) container_of(_mtd, struct sharpsl_nand, mtd) /* register offset */ -#define ECCLPLB sharpsl_io_base+0x00 /* line parity 7 - 0 bit */ -#define ECCLPUB sharpsl_io_base+0x04 /* line parity 15 - 8 bit */ -#define ECCCP sharpsl_io_base+0x08 /* column parity 5 - 0 bit */ -#define ECCCNTR sharpsl_io_base+0x0C /* ECC byte counter */ -#define ECCCLRR sharpsl_io_base+0x10 /* cleare ECC */ -#define FLASHIO sharpsl_io_base+0x14 /* Flash I/O */ -#define FLASHCTL sharpsl_io_base+0x18 /* Flash Control */ +#define ECCLPLB 0x00 /* line parity 7 - 0 bit */ +#define ECCLPUB 0x04 /* line parity 15 - 8 bit */ +#define ECCCP 0x08 /* column parity 5 - 0 bit */ +#define ECCCNTR 0x0C /* ECC byte counter */ +#define ECCCLRR 0x10 /* cleare ECC */ +#define FLASHIO 0x14 /* Flash I/O */ +#define FLASHCTL 0x18 /* Flash Control */ /* Flash control bit */ #define FLRYBY (1 << 5) @@ -45,35 +55,6 @@ static int sharpsl_phys_base = 0x0C000000; #define FLCE0 (1 << 0) /* - * MTD structure for SharpSL - */ -static struct mtd_info *sharpsl_mtd = NULL; - -/* - * Define partitions for flash device - */ -#define DEFAULT_NUM_PARTITIONS 3 - -static int nr_partitions; -static struct mtd_partition sharpsl_nand_default_partition_info[] = { - { - .name = "System Area", - .offset = 0, - .size = 7 * 1024 * 1024, - }, - { - .name = "Root Filesystem", - .offset = 7 * 1024 * 1024, - .size = 30 * 1024 * 1024, - }, - { - .name = "Home Filesystem", - .offset = MTDPART_OFS_APPEND, - .size = MTDPART_SIZ_FULL, - }, -}; - -/* * hardware specific access to control-lines * ctrl: * NAND_CNE: bit 0 -> ! bit 0 & 4 @@ -84,6 +65,7 @@ static struct mtd_partition sharpsl_nand_default_partition_info[] = { static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { + struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); struct nand_chip *chip = mtd->priv; if (ctrl & NAND_CTRL_CHANGE) { @@ -93,103 +75,97 @@ static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd, bits ^= 0x11; - writeb((readb(FLASHCTL) & ~0x17) | bits, FLASHCTL); + writeb((readb(sharpsl->io + FLASHCTL) & ~0x17) | bits, sharpsl->io + FLASHCTL); } if (cmd != NAND_CMD_NONE) writeb(cmd, chip->IO_ADDR_W); } -static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; - -static struct nand_bbt_descr sharpsl_bbt = { - .options = 0, - .offs = 4, - .len = 2, - .pattern = scan_ff_pattern -}; - -static struct nand_bbt_descr sharpsl_akita_bbt = { - .options = 0, - .offs = 4, - .len = 1, - .pattern = scan_ff_pattern -}; - -static struct nand_ecclayout akita_oobinfo = { - .eccbytes = 24, - .eccpos = { - 0x5, 0x1, 0x2, 0x3, 0x6, 0x7, 0x15, 0x11, - 0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23, - 0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37}, - .oobfree = {{0x08, 0x09}} -}; - static int sharpsl_nand_dev_ready(struct mtd_info *mtd) { - return !((readb(FLASHCTL) & FLRYBY) == 0); + struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); + return !((readb(sharpsl->io + FLASHCTL) & FLRYBY) == 0); } static void sharpsl_nand_enable_hwecc(struct mtd_info *mtd, int mode) { - writeb(0, ECCCLRR); + struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); + writeb(0, sharpsl->io + ECCCLRR); } static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code) { - ecc_code[0] = ~readb(ECCLPUB); - ecc_code[1] = ~readb(ECCLPLB); - ecc_code[2] = (~readb(ECCCP) << 2) | 0x03; - return readb(ECCCNTR) != 0; + struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); + ecc_code[0] = ~readb(sharpsl->io + ECCLPUB); + ecc_code[1] = ~readb(sharpsl->io + ECCLPLB); + ecc_code[2] = (~readb(sharpsl->io + ECCCP) << 2) | 0x03; + return readb(sharpsl->io + ECCCNTR) != 0; } #ifdef CONFIG_MTD_PARTITIONS -const char *part_probes[] = { "cmdlinepart", NULL }; +static const char *part_probes[] = { "cmdlinepart", NULL }; #endif /* * Main initialization routine */ -static int __init sharpsl_nand_init(void) +static int __devinit sharpsl_nand_probe(struct platform_device *pdev) { struct nand_chip *this; +#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *sharpsl_partition_info; + int nr_partitions; +#endif + struct resource *r; int err = 0; + struct sharpsl_nand *sharpsl; + struct sharpsl_nand_platform_data *data = pdev->dev.platform_data; + + if (!data) { + dev_err(&pdev->dev, "no platform data!\n"); + return -EINVAL; + } /* Allocate memory for MTD device structure and private data */ - sharpsl_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); - if (!sharpsl_mtd) { + sharpsl = kzalloc(sizeof(struct sharpsl_nand), GFP_KERNEL); + if (!sharpsl) { printk("Unable to allocate SharpSL NAND MTD device structure.\n"); return -ENOMEM; } + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no io memory resource defined!\n"); + err = -ENODEV; + goto err_get_res; + } + /* map physical address */ - sharpsl_io_base = ioremap(sharpsl_phys_base, 0x1000); - if (!sharpsl_io_base) { + sharpsl->io = ioremap(r->start, resource_size(r)); + if (!sharpsl->io) { printk("ioremap to access Sharp SL NAND chip failed\n"); - kfree(sharpsl_mtd); - return -EIO; + err = -EIO; + goto err_ioremap; } /* Get pointer to private data */ - this = (struct nand_chip *)(&sharpsl_mtd[1]); - - /* Initialize structures */ - memset(sharpsl_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); + this = (struct nand_chip *)(&sharpsl->chip); /* Link the private data with the MTD structure */ - sharpsl_mtd->priv = this; - sharpsl_mtd->owner = THIS_MODULE; + sharpsl->mtd.priv = this; + sharpsl->mtd.owner = THIS_MODULE; + + platform_set_drvdata(pdev, sharpsl); /* * PXA initialize */ - writeb(readb(FLASHCTL) | FLWP, FLASHCTL); + writeb(readb(sharpsl->io + FLASHCTL) | FLWP, sharpsl->io + FLASHCTL); /* Set address of NAND IO lines */ - this->IO_ADDR_R = FLASHIO; - this->IO_ADDR_W = FLASHIO; + this->IO_ADDR_R = sharpsl->io + FLASHIO; + this->IO_ADDR_W = sharpsl->io + FLASHIO; /* Set address of hardware control function */ this->cmd_ctrl = sharpsl_nand_hwcontrol; this->dev_ready = sharpsl_nand_dev_ready; @@ -199,68 +175,89 @@ static int __init sharpsl_nand_init(void) 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->ecc.layout = &akita_oobinfo; - } + this->badblock_pattern = data->badblock_pattern; + this->ecc.layout = data->ecc_layout; 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); - if (err) { - iounmap(sharpsl_io_base); - kfree(sharpsl_mtd); - return err; - } + err = nand_scan(&sharpsl->mtd, 1); + if (err) + goto err_scan; /* Register the partitions */ - sharpsl_mtd->name = "sharpsl-nand"; - nr_partitions = parse_mtd_partitions(sharpsl_mtd, part_probes, &sharpsl_partition_info, 0); - + sharpsl->mtd.name = "sharpsl-nand"; +#ifdef CONFIG_MTD_PARTITIONS + nr_partitions = parse_mtd_partitions(&sharpsl->mtd, part_probes, &sharpsl_partition_info, 0); if (nr_partitions <= 0) { - nr_partitions = DEFAULT_NUM_PARTITIONS; - sharpsl_partition_info = sharpsl_nand_default_partition_info; - if (machine_is_poodle()) { - sharpsl_partition_info[1].size = 22 * 1024 * 1024; - } else if (machine_is_corgi() || machine_is_shepherd()) { - sharpsl_partition_info[1].size = 25 * 1024 * 1024; - } else if (machine_is_husky()) { - sharpsl_partition_info[1].size = 53 * 1024 * 1024; - } else if (machine_is_spitz()) { - sharpsl_partition_info[1].size = 5 * 1024 * 1024; - } else if (machine_is_akita()) { - sharpsl_partition_info[1].size = 58 * 1024 * 1024; - } else if (machine_is_borzoi()) { - sharpsl_partition_info[1].size = 32 * 1024 * 1024; - } + nr_partitions = data->nr_partitions; + sharpsl_partition_info = data->partitions; } - add_mtd_partitions(sharpsl_mtd, sharpsl_partition_info, nr_partitions); + if (nr_partitions > 0) + err = add_mtd_partitions(&sharpsl->mtd, sharpsl_partition_info, nr_partitions); + else +#endif + err = add_mtd_device(&sharpsl->mtd); + if (err) + goto err_add; /* Return happy */ return 0; -} -module_init(sharpsl_nand_init); +err_add: + nand_release(&sharpsl->mtd); + +err_scan: + platform_set_drvdata(pdev, NULL); + iounmap(sharpsl->io); +err_ioremap: +err_get_res: + kfree(sharpsl); + return err; +} /* * Clean up routine */ -static void __exit sharpsl_nand_cleanup(void) +static int __devexit sharpsl_nand_remove(struct platform_device *pdev) { + struct sharpsl_nand *sharpsl = platform_get_drvdata(pdev); + /* Release resources, unregister device */ - nand_release(sharpsl_mtd); + nand_release(&sharpsl->mtd); - iounmap(sharpsl_io_base); + platform_set_drvdata(pdev, NULL); + + iounmap(sharpsl->io); /* Free the MTD device structure */ - kfree(sharpsl_mtd); + kfree(sharpsl); + + return 0; +} + +static struct platform_driver sharpsl_nand_driver = { + .driver = { + .name = "sharpsl-nand", + .owner = THIS_MODULE, + }, + .probe = sharpsl_nand_probe, + .remove = __devexit_p(sharpsl_nand_remove), +}; + +static int __init sharpsl_nand_init(void) +{ + return platform_driver_register(&sharpsl_nand_driver); } +module_init(sharpsl_nand_init); -module_exit(sharpsl_nand_cleanup); +static void __exit sharpsl_nand_exit(void) +{ + platform_driver_unregister(&sharpsl_nand_driver); +} +module_exit(sharpsl_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index edb1e322113d..daa6a4c3b8ce 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -433,7 +433,7 @@ static int tmio_probe(struct platform_device *dev) nand_chip->chip_delay = 15; retval = request_irq(irq, &tmio_irq, - IRQF_DISABLED, dev->dev.bus_id, tmio); + IRQF_DISABLED, dev_name(&dev->dev), tmio); if (retval) { dev_err(&dev->dev, "request_irq error %d\n", retval); goto err_irq; |