From 7eafaed55f4b8599cfe55449a6ed88d3693954de Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Wed, 27 Jun 2007 00:56:40 +0200 Subject: [MTD] Only call mtd->sync() method in mtdchar close if opened for write. Signed-off-by: Joakim Tjernlund Signed-off-by: David Woodhouse --- drivers/mtd/mtdchar.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 8c86b802f2..942c88ec5b 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -135,7 +135,8 @@ static int mtd_close(struct inode *inode, struct file *file) DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); - if (mtd->sync) + /* Only sync if opened RW */ + if ((file->f_mode & 2) && mtd->sync) mtd->sync(mtd); put_mtd_device(mtd); -- cgit v1.2.2 From 4854b75c7795604ea6a0b6b7f549f145813343d0 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 28 Jun 2007 20:13:27 +0100 Subject: [MTD] Remove references to ROOT_DEV from map drivers. Signed-off-by: David Woodhouse --- drivers/mtd/maps/nettel.c | 4 ---- drivers/mtd/maps/pmcmsp-ramroot.c | 1 - 2 files changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 7b96cd02f8..88525622e9 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -421,10 +421,6 @@ int __init nettel_init(void) intel_mtd->owner = THIS_MODULE; -#ifndef CONFIG_BLK_DEV_INITRD - ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 1); -#endif - num_intel_partitions = sizeof(nettel_intel_partitions) / sizeof(nettel_intel_partitions[0]); diff --git a/drivers/mtd/maps/pmcmsp-ramroot.c b/drivers/mtd/maps/pmcmsp-ramroot.c index 18049bceba..30de5c0c09 100644 --- a/drivers/mtd/maps/pmcmsp-ramroot.c +++ b/drivers/mtd/maps/pmcmsp-ramroot.c @@ -79,7 +79,6 @@ static int __init init_rrmap(void) rr_mtd->owner = THIS_MODULE; add_mtd_device(rr_mtd); - ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, rr_mtd->index); return 0; } -- cgit v1.2.2 From 1100b47ea557e1cd6d9b71fe7ba1a189ed8bf276 Mon Sep 17 00:00:00 2001 From: Ivan Kuten Date: Thu, 24 May 2007 14:35:58 +0300 Subject: [MTD] [NAND] at91_nand rdy_pin fix The patch below fixes nand driver for AT91 boards which do not have NAND R/B signal connected to gpio (rdy_pin is not connected). Signed-off-by: Ivan Kuten Acked-by: Andrew Victor Signed-off-by: David Woodhouse --- drivers/mtd/nand/at91_nand.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/at91_nand.c b/drivers/mtd/nand/at91_nand.c index 512e999177..b2a5672df6 100644 --- a/drivers/mtd/nand/at91_nand.c +++ b/drivers/mtd/nand/at91_nand.c @@ -128,7 +128,10 @@ static int __init at91_nand_probe(struct platform_device *pdev) nand_chip->IO_ADDR_R = host->io_base; nand_chip->IO_ADDR_W = host->io_base; nand_chip->cmd_ctrl = at91_nand_cmd_ctrl; - nand_chip->dev_ready = at91_nand_device_ready; + + if (host->board->rdy_pin) + nand_chip->dev_ready = at91_nand_device_ready; + nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ nand_chip->chip_delay = 20; /* 20us command delay time */ -- cgit v1.2.2 From 4b23aff083649eafa141ed69ad0ff59ba639edf8 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Tue, 29 May 2007 13:31:42 +0100 Subject: [MTD] oops and panic message logging to MTD device Kernel oops and panic messages are invaluable when debugging crashes. These messages often don't make it to flash based logging methods (say a syslog on jffs2) due to the overheads involved in writing to flash. This patch allows you to turn an MTD partition into a circular log buffer where kernel oops and panic messages are written to. The messages are obtained by registering a console driver and checking oops_in_progress. Erases are performed in advance to maximise the chances of a saving messages. To activate it, add console=ttyMTDx to the kernel commandline (where x is the mtd device number to use). Signed-off-by: Richard Purdie Signed-off-by: David Woodhouse --- drivers/mtd/Kconfig | 8 ++ drivers/mtd/Makefile | 1 + drivers/mtd/mtdoops.c | 365 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 374 insertions(+) create mode 100644 drivers/mtd/mtdoops.c (limited to 'drivers') diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index fbec8cd55e..8848e8ac70 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -278,6 +278,14 @@ config SSFDC This enables read only access to SmartMedia formatted NAND flash. You can mount it with FAT file system. +config MTD_OOPS + tristate "Log panic/oops to an MTD buffer" + depends on MTD + help + This enables panic and oops messages to be logged to a circular + buffer in a flash partition where it can be read back at some + later point. + source "drivers/mtd/chips/Kconfig" source "drivers/mtd/maps/Kconfig" diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 451adcc52b..024d0e5e3e 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_NFTL) += nftl.o obj-$(CONFIG_INFTL) += inftl.o obj-$(CONFIG_RFD_FTL) += rfd_ftl.o obj-$(CONFIG_SSFDC) += ssfdc.o +obj-$(CONFIG_MTD_OOPS) += mtdoops.o nftl-objs := nftlcore.o nftlmount.o inftl-objs := inftlcore.o inftlmount.o diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c new file mode 100644 index 0000000000..cfc28ab4a3 --- /dev/null +++ b/drivers/mtd/mtdoops.c @@ -0,0 +1,365 @@ +/* + * MTD Oops/Panic logger + * + * Copyright (C) 2007 Nokia Corporation. All rights reserved. + * + * Author: Richard Purdie + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define OOPS_PAGE_SIZE 4096 + +static struct mtdoops_context { + int mtd_index; + struct work_struct work; + struct mtd_info *mtd; + int oops_pages; + int nextpage; + int nextcount; + + void *oops_buf; + int ready; + int writecount; +} oops_cxt; + +static void mtdoops_erase_callback(struct erase_info *done) +{ + wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; + wake_up(wait_q); +} + +static int mtdoops_erase_block(struct mtd_info *mtd, int offset) +{ + struct erase_info erase; + DECLARE_WAITQUEUE(wait, current); + wait_queue_head_t wait_q; + int ret; + + init_waitqueue_head(&wait_q); + erase.mtd = mtd; + erase.callback = mtdoops_erase_callback; + erase.addr = offset; + if (mtd->erasesize < OOPS_PAGE_SIZE) + erase.len = OOPS_PAGE_SIZE; + else + erase.len = mtd->erasesize; + erase.priv = (u_long)&wait_q; + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&wait_q, &wait); + + ret = mtd->erase(mtd, &erase); + if (ret) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&wait_q, &wait); + printk (KERN_WARNING "mtdoops: erase of region [0x%x, 0x%x] " + "on \"%s\" failed\n", + erase.addr, erase.len, mtd->name); + return ret; + } + + schedule(); /* Wait for erase to finish. */ + remove_wait_queue(&wait_q, &wait); + + return 0; +} + +static int mtdoops_inc_counter(struct mtdoops_context *cxt) +{ + struct mtd_info *mtd = cxt->mtd; + size_t retlen; + u32 count; + int ret; + + cxt->nextpage++; + if (cxt->nextpage > cxt->oops_pages) + cxt->nextpage = 0; + cxt->nextcount++; + if (cxt->nextcount == 0xffffffff) + cxt->nextcount = 0; + + ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4, + &retlen, (u_char *) &count); + if ((retlen != 4) || (ret < 0)) { + printk(KERN_ERR "mtdoops: Read failure at %d (%d of 4 read)" + ", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE, + retlen, ret); + return 1; + } + + /* See if we need to erase the next block */ + if (count != 0xffffffff) + return 1; + + printk(KERN_DEBUG "mtdoops: Ready %d, %d (no erase)\n", + cxt->nextpage, cxt->nextcount); + cxt->ready = 1; + return 0; +} + +static void mtdoops_prepare(struct mtdoops_context *cxt) +{ + struct mtd_info *mtd = cxt->mtd; + int i = 0, j, ret, mod; + + /* We were unregistered */ + if (!mtd) + return; + + mod = (cxt->nextpage * OOPS_PAGE_SIZE) % mtd->erasesize; + if (mod != 0) { + cxt->nextpage = cxt->nextpage + ((mtd->erasesize - mod) / OOPS_PAGE_SIZE); + if (cxt->nextpage > cxt->oops_pages) + cxt->nextpage = 0; + } + + while (mtd->block_isbad && + mtd->block_isbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE)) { +badblock: + printk(KERN_WARNING "mtdoops: Bad block at %08x\n", + cxt->nextpage * OOPS_PAGE_SIZE); + i++; + cxt->nextpage = cxt->nextpage + (mtd->erasesize / OOPS_PAGE_SIZE); + if (cxt->nextpage > cxt->oops_pages) + cxt->nextpage = 0; + if (i == (cxt->oops_pages / (mtd->erasesize / OOPS_PAGE_SIZE))) { + printk(KERN_ERR "mtdoops: All blocks bad!\n"); + return; + } + } + + for (j = 0, ret = -1; (j < 3) && (ret < 0); j++) + ret = mtdoops_erase_block(mtd, cxt->nextpage * OOPS_PAGE_SIZE); + + if (ret < 0) { + if (mtd->block_markbad) + mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE); + goto badblock; + } + + printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount); + + cxt->ready = 1; +} + +static void mtdoops_workfunc(struct work_struct *work) +{ + struct mtdoops_context *cxt = + container_of(work, struct mtdoops_context, work); + + mtdoops_prepare(cxt); +} + +static int find_next_position(struct mtdoops_context *cxt) +{ + struct mtd_info *mtd = cxt->mtd; + int page, maxpos = 0; + u32 count, maxcount = 0xffffffff; + size_t retlen; + + for (page = 0; page < cxt->oops_pages; page++) { + mtd->read(mtd, page * OOPS_PAGE_SIZE, 4, &retlen, (u_char *) &count); + if (count == 0xffffffff) + continue; + if (maxcount == 0xffffffff) { + maxcount = count; + maxpos = page; + } else if ((count < 0x40000000) && (maxcount > 0xc0000000)) { + maxcount = count; + maxpos = page; + } else if ((count > maxcount) && (count < 0xc0000000)) { + maxcount = count; + maxpos = page; + } else if ((count > maxcount) && (count > 0xc0000000) + && (maxcount > 0x80000000)) { + maxcount = count; + maxpos = page; + } + } + if (maxcount == 0xffffffff) { + cxt->nextpage = 0; + cxt->nextcount = 1; + cxt->ready = 1; + printk(KERN_DEBUG "mtdoops: Ready %d, %d (first init)\n", + cxt->nextpage, cxt->nextcount); + return 0; + } + + cxt->nextpage = maxpos; + cxt->nextcount = maxcount; + + return mtdoops_inc_counter(cxt); +} + + +static void mtdoops_notify_add(struct mtd_info *mtd) +{ + struct mtdoops_context *cxt = &oops_cxt; + int ret; + + if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) + return; + + if (mtd->size < (mtd->erasesize * 2)) { + printk(KERN_ERR "MTD partition %d not big enough for mtdoops\n", + mtd->index); + return; + } + + cxt->mtd = mtd; + cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE; + + ret = find_next_position(cxt); + if (ret == 1) + mtdoops_prepare(cxt); + + printk(KERN_DEBUG "mtdoops: Attached to MTD device %d\n", mtd->index); +} + +static void mtdoops_notify_remove(struct mtd_info *mtd) +{ + struct mtdoops_context *cxt = &oops_cxt; + + if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) + return; + + cxt->mtd = NULL; + flush_scheduled_work(); +} + + +static void +mtdoops_console_write(struct console *co, const char *s, unsigned int count) +{ + struct mtdoops_context *cxt = co->data; + struct mtd_info *mtd = cxt->mtd; + int i, ret; + + if (!cxt->ready || !mtd) + return; + + if (!oops_in_progress && cxt->writecount != 0) { + size_t retlen; + if (cxt->writecount < OOPS_PAGE_SIZE) + memset(cxt->oops_buf + cxt->writecount, 0xff, + OOPS_PAGE_SIZE - cxt->writecount); + + ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, + OOPS_PAGE_SIZE, &retlen, cxt->oops_buf); + cxt->ready = 0; + cxt->writecount = 0; + + if ((retlen != OOPS_PAGE_SIZE) || (ret < 0)) + printk(KERN_ERR "mtdoops: Write failure at %d (%d of %d" + " written), err %d.\n", + cxt->nextpage * OOPS_PAGE_SIZE, retlen, + OOPS_PAGE_SIZE, ret); + + ret = mtdoops_inc_counter(cxt); + if (ret == 1) + schedule_work(&cxt->work); + } + + if (!oops_in_progress) + return; + + if (cxt->writecount == 0) { + u32 *stamp = cxt->oops_buf; + *stamp = cxt->nextcount; + cxt->writecount = 4; + } + + if ((count + cxt->writecount) > OOPS_PAGE_SIZE) + count = OOPS_PAGE_SIZE - cxt->writecount; + + for (i = 0; i < count; i++, s++) + *((char *)(cxt->oops_buf) + cxt->writecount + i) = *s; + + cxt->writecount = cxt->writecount + count; +} + +static int __init mtdoops_console_setup(struct console *co, char *options) +{ + struct mtdoops_context *cxt = co->data; + + if (cxt->mtd_index != -1) + return -EBUSY; + if (co->index == -1) + return -EINVAL; + + cxt->mtd_index = co->index; + return 0; +} + +static struct mtd_notifier mtdoops_notifier = { + .add = mtdoops_notify_add, + .remove = mtdoops_notify_remove, +}; + +static struct console mtdoops_console = { + .name = "ttyMTD", + .write = mtdoops_console_write, + .setup = mtdoops_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &oops_cxt, +}; + +static int __init mtdoops_console_init(void) +{ + struct mtdoops_context *cxt = &oops_cxt; + + cxt->mtd_index = -1; + cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE); + + if (!cxt->oops_buf) { + printk(KERN_ERR "Failed to allocate oops buffer workspace\n"); + return -ENOMEM; + } + + INIT_WORK(&cxt->work, mtdoops_workfunc); + + register_console(&mtdoops_console); + register_mtd_user(&mtdoops_notifier); + return 0; +} + +static void __exit mtdoops_console_exit(void) +{ + struct mtdoops_context *cxt = &oops_cxt; + + unregister_mtd_user(&mtdoops_notifier); + unregister_console(&mtdoops_console); + vfree(cxt->oops_buf); +} + + +subsys_initcall(mtdoops_console_init); +module_exit(mtdoops_console_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Richard Purdie "); +MODULE_DESCRIPTION("MTD Oops/Panic console logger/driver"); -- cgit v1.2.2 From 8b099a390defd294a3832fe73626c25072967316 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 28 May 2007 19:17:54 +0100 Subject: [MTD] [NAND] nand_base.c: fix type of eccpos pointer The nand_base.c driver implicitly casts the uint32_t eccpos array to 'int *', which is not only not guaranteed to be the same sign as the source, but is not guaranteed to be the same size. Fix by changing nand_base.c to use uint32_t referencing the eccpos fields. Signed-off-by: Ben Dooks Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 7e68203fe1..25673eacdd 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -768,7 +768,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->ecc.layout->eccpos; + uint32_t *eccpos = chip->ecc.layout->eccpos; chip->ecc.read_page_raw(mtd, chip, buf); @@ -810,7 +810,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->ecc.layout->eccpos; + uint32_t *eccpos = chip->ecc.layout->eccpos; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_READ); @@ -1416,7 +1416,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->ecc.layout->eccpos; + uint32_t *eccpos = chip->ecc.layout->eccpos; /* Software ecc calculation */ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) @@ -1442,7 +1442,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->ecc.layout->eccpos; + uint32_t *eccpos = chip->ecc.layout->eccpos; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_WRITE); -- cgit v1.2.2 From 8f026f75accb8c9f1179a4b57a5019af36471046 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 28 May 2007 19:51:59 +0100 Subject: [MTD] mtd_blkdevs.c: do not export 'mtd_blktrans_ops' The mtd_blktrans_ops is not defined in any of the headers and is indeed not used elsewhere in the kernel, so mark it as static to reduce the warnings from sparse drivers/mtd/mtd_blkdevs.c:204:32: warning: symbol 'mtd_blktrans_ops' was not declared. Should it be static? Signed-off-by: Ben Dooks Signed-off-by: David Woodhouse --- drivers/mtd/mtd_blkdevs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 51bc7e2f1f..781660565c 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -201,7 +201,7 @@ static int blktrans_ioctl(struct inode *inode, struct file *file, } } -struct block_device_operations mtd_blktrans_ops = { +static struct block_device_operations mtd_blktrans_ops = { .owner = THIS_MODULE, .open = blktrans_open, .release = blktrans_release, -- cgit v1.2.2 From ce0f33adec9737bdfe59a10d03fed0c674136ffe Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 28 May 2007 19:59:00 +0100 Subject: [MTD] [NOR] cfi_cmdset_0002.c: fix 'cfi_amdstd_erase_varsize' to be static Make cfi_amdstd_erase_varsize static, as declared at the top of the file to ensure sparse does not print a warning for an undeclared function, as so: drivers/mtd/chips/cfi_cmdset_0002.c:1612:5: warning: symbol 'cfi_amdstd_erase_varsize' was not declared. Should it be static? Signed-off-by: Ben Dooks Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 1f64458404..389acc600f 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -1609,7 +1609,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, } -int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) +static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) { unsigned long ofs, len; int ret; -- cgit v1.2.2 From 029a9eb1f667ca71fe7eb3bf8962f01ac4d252a5 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 28 May 2007 20:11:37 +0100 Subject: [MTD] [NOR] cfi_cmdset_0001.c: sparse fixes Fix sparse warnings generated from cfi_cmdset_0001.c. drivers/mtd/chips/cfi_cmdset_0001.c:1783:5: warning: symbol 'cfi_intelext_erase_varsize' was not declared. Should it be static? drivers/mtd/chips/cfi_cmdset_0001.c:2258:43: warning: Using plain integer as NULL pointer Signed-off-by: Ben Dooks Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 2f19fa78d2..46d782f261 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -1780,7 +1780,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, return ret; } -int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) +static int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) { unsigned long ofs, len; int ret; @@ -2255,7 +2255,7 @@ static void cfi_intelext_save_locks(struct mtd_info *mtd) adr = region->offset + block * len; status = cfi_varsize_frob(mtd, - do_getlockstatus_oneblock, adr, len, 0); + do_getlockstatus_oneblock, adr, len, NULL); if (status) set_bit(block, region->lockmap); else -- cgit v1.2.2 From 356d70f19d949a11320ab626037b2d197a8e8b51 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 28 May 2007 20:28:34 +0100 Subject: [MTD] mtdcore.c: share syms with mtd_blkdev.c Fix the sparse warnings generated by the implicit dependency of mtd_blkdevs.c and mtd_core.c for the two symbols mtd_table and mtd_table_mutex. This is done by adding an local header file mtdcore.h to define these (including the warning about the non-proliferation of these symbols). Signed-off-by: Ben Dooks Signed-off-by: David Woodhouse --- drivers/mtd/mtd_blkdevs.c | 5 ++--- drivers/mtd/mtdcore.c | 2 ++ drivers/mtd/mtdcore.h | 11 +++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 drivers/mtd/mtdcore.h (limited to 'drivers') diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 781660565c..a8a1587082 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -23,10 +23,9 @@ #include #include -static LIST_HEAD(blktrans_majors); +#include "mtdcore.h" -extern struct mutex mtd_table_mutex; -extern struct mtd_info *mtd_table[]; +static LIST_HEAD(blktrans_majors); struct mtd_blkcore_priv { struct task_struct *thread; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index c153b64a83..6c2645e283 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -22,6 +22,8 @@ #include +#include "mtdcore.h" + /* These are exported solely for the purpose of mtd_blkdevs.c. You should not use them for _anything_ else */ DEFINE_MUTEX(mtd_table_mutex); diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h new file mode 100644 index 0000000000..a33251f4b8 --- /dev/null +++ b/drivers/mtd/mtdcore.h @@ -0,0 +1,11 @@ +/* linux/drivers/mtd/mtdcore.h + * + * Header file for driver private mtdcore exports + * + */ + +/* These are exported solely for the purpose of mtd_blkdevs.c. You + should not use them for _anything_ else */ + +extern struct mutex mtd_table_mutex; +extern struct mtd_info *mtd_table[MAX_MTD_DEVICES]; -- cgit v1.2.2 From 02d929187414f2095b30ebf4acee1ce6617add45 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Tue, 22 May 2007 11:23:45 +0200 Subject: [MTD] [NAND] Change NDFC driver to support 405 platforms too This patch adds 405 platform support to the 440 NDFC driver. The new AMCC 405EZ PPC is equipped with the same NDFC core as the 440EP(x) and other will follow soon. Signed-off-by: Stefan Roese Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 4 ++-- drivers/mtd/nand/ndfc.c | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index f1d60b6f04..d8b8fce511 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -134,10 +134,10 @@ config MTD_NAND_S3C2410_HWECC config MTD_NAND_NDFC tristate "NDFC NanD Flash Controller" - depends on 44x + depends on 4xx select MTD_NAND_ECC_SMC help - NDFC Nand Flash Controllers are integrated in EP44x SoCs + NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs config MTD_NAND_S3C2410_CLKSTOP bool "S3C2410 NAND IDLE clock stop" diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index fd7a8d5ba2..1c0e89f00e 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -24,7 +24,11 @@ #include #include +#ifdef CONFIG_40x +#include +#else #include +#endif struct ndfc_nand_mtd { struct mtd_info mtd; @@ -230,7 +234,11 @@ static int ndfc_nand_probe(struct platform_device *pdev) struct ndfc_controller *ndfc = &ndfc_ctrl; unsigned long long phys = settings->ndfc_erpn | res->start; +#ifndef CONFIG_PHYS_64BIT + ndfc->ndfcbase = ioremap((phys_addr_t)phys, res->end - res->start + 1); +#else ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1); +#endif if (!ndfc->ndfcbase) { printk(KERN_ERR "NDFC: ioremap failed\n"); return -EIO; -- cgit v1.2.2 From 7d5230ea3987ea3eaa03601fe429cb69f87de3e3 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sun, 24 Jun 2007 15:09:13 -0700 Subject: [MTD] m25p80 converted to mutex Convert semaphore usage in m25p80 driver to mutex; mention another kind of SPI flash chip that should be able to use this driver (given minor tweaks). Signed-off-by: David Brownell Signed-off-by: David Woodhouse --- drivers/mtd/devices/m25p80.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 78c2511ae9..7eaa61862a 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -19,16 +19,17 @@ #include #include #include -#include +#include + #include #include + #include #include -#include - /* NOTE: AT 25F and SST 25LF series are very similar, + * as are other newer Atmel dataflash chips (AT26), * but commands for sector erase and chip id differ... */ @@ -65,7 +66,7 @@ struct m25p { struct spi_device *spi; - struct semaphore lock; + struct mutex lock; struct mtd_info mtd; unsigned partitioned; u8 command[4]; @@ -201,13 +202,13 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) addr = instr->addr; len = instr->len; - down(&flash->lock); + mutex_lock(&flash->lock); /* now erase those sectors */ while (len) { if (erase_sector(flash, addr)) { instr->state = MTD_ERASE_FAILED; - up(&flash->lock); + mutex_unlock(&flash->lock); return -EIO; } @@ -215,7 +216,7 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) len -= mtd->erasesize; } - up(&flash->lock); + mutex_unlock(&flash->lock); instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); @@ -260,12 +261,12 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, if (retlen) *retlen = 0; - down(&flash->lock); + mutex_lock(&flash->lock); /* Wait till previous write/erase is done. */ if (wait_till_ready(flash)) { /* REVISIT status return?? */ - up(&flash->lock); + mutex_unlock(&flash->lock); return 1; } @@ -281,7 +282,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, *retlen = m.actual_length - sizeof(flash->command); - up(&flash->lock); + mutex_unlock(&flash->lock); return 0; } @@ -323,7 +324,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, t[1].tx_buf = buf; spi_message_add_tail(&t[1], &m); - down(&flash->lock); + mutex_lock(&flash->lock); /* Wait until finished previous write command. */ if (wait_till_ready(flash)) @@ -381,10 +382,10 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, if (retlen) *retlen += m.actual_length - sizeof(flash->command); - } - } + } + } - up(&flash->lock); + mutex_unlock(&flash->lock); return 0; } @@ -405,7 +406,7 @@ struct flash_info { }; static struct flash_info __devinitdata m25p_data [] = { - /* REVISIT: fill in JEDEC ids, for parts that have them */ + /* JEDEC id zero means "has no ID" */ { "m25p05", 0x05, 0x2010, 32 * 1024, 2 }, { "m25p10", 0x10, 0x2011, 32 * 1024, 4 }, { "m25p20", 0x11, 0x2012, 64 * 1024, 4 }, @@ -456,7 +457,7 @@ static int __devinit m25p_probe(struct spi_device *spi) return -ENOMEM; flash->spi = spi; - init_MUTEX(&flash->lock); + mutex_init(&flash->lock); dev_set_drvdata(&spi->dev, flash); if (data->name) -- cgit v1.2.2 From fa0a8c71f352d89c54f2d3a92f7a8a97cdb7d9a4 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sun, 24 Jun 2007 15:12:35 -0700 Subject: [MTD] m25p80 handles more chips, uses JEDEC ids and small eraseblocks Update chip ID tables in m25p80 to handle more SPI flash chips, matching datasheets. All of these can use the same core operations and are newer chips that support the JEDEC "read id" instruction: - Atmel AT25 and AT26 (seven chips) - Spansion S25SL (five chips) - SST 25VF (four chips) - ST M25, M45 (five more chips) - Winbond W25X series (seven chips) That JEDEC instruction is now used, either to support a sanity check on the platform data holding board configuration data, or to determine chip type when it's not included in platform data. In fact, boards that don't need a standard partition table may not need that platform data any more. For chips that support 4KiB erase units, use that smaller block size instead of the larger size (usually 64KiB); it's less wasteful. (Tested on W25X80.) Signed-off-by: David Brownell Signed-off-by: David Woodhouse --- drivers/mtd/devices/Kconfig | 17 +++- drivers/mtd/devices/m25p80.c | 234 +++++++++++++++++++++++++++++++++---------- 2 files changed, 194 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index ff642f8fbe..b4ea64dc93 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -69,12 +69,21 @@ config MTD_DATAFLASH26 If you have such a board and such a DataFlash, say 'Y'. config MTD_M25P80 - tristate "Support for M25 SPI Flash" + tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" depends on SPI_MASTER && EXPERIMENTAL help - This enables access to ST M25P80 and similar SPI flash chips, - used for program and data storage. Set up your spi devices - with the right board-specific platform data. + This enables access to most modern SPI flash chips, used for + program and data storage. Series supported include Atmel AT26DF, + Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X. Other chips + are supported as well. See the driver source for the current list, + or to add other chips. + + Note that the original DataFlash chips (AT45 series, not AT26DF), + need an entirely different driver. + + Set up your spi devices with the right board-specific platform data, + if you want to specify device partitioning or to use a device which + doesn't support the JEDEC ID instruction. config MTD_SLRAM tristate "Uncached system RAM" diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 7eaa61862a..6668a8c27c 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -1,5 +1,5 @@ /* - * MTD SPI driver for ST M25Pxx flash chips + * MTD SPI driver for ST M25Pxx (and similar) serial flash chips * * Author: Mike Lavender, mike@steroidmicros.com * @@ -28,25 +28,23 @@ #include -/* NOTE: AT 25F and SST 25LF series are very similar, - * as are other newer Atmel dataflash chips (AT26), - * but commands for sector erase and chip id differ... - */ - #define FLASH_PAGESIZE 256 /* Flash opcodes. */ -#define OPCODE_WREN 6 /* Write enable */ -#define OPCODE_RDSR 5 /* Read status register */ -#define OPCODE_READ 3 /* Read data bytes */ -#define OPCODE_PP 2 /* Page program */ -#define OPCODE_SE 0xd8 /* Sector erase */ -#define OPCODE_RES 0xab /* Read Electronic Signature */ +#define OPCODE_WREN 0x06 /* Write enable */ +#define OPCODE_RDSR 0x05 /* Read status register */ +#define OPCODE_READ 0x03 /* Read data bytes (low frequency) */ +#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ +#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ +#define OPCODE_BE_4K 0x20 /* Erase 4K block */ +#define OPCODE_BE_32K 0x52 /* Erase 32K block */ +#define OPCODE_SE 0xd8 /* Sector erase (usually 64K) */ #define OPCODE_RDID 0x9f /* Read JEDEC ID */ /* Status Register bits. */ #define SR_WIP 1 /* Write in progress */ #define SR_WEL 2 /* Write enable latch */ +/* meaning of other SR_* bits may differ between vendors */ #define SR_BP0 4 /* Block protect 0 */ #define SR_BP1 8 /* Block protect 1 */ #define SR_BP2 0x10 /* Block protect 2 */ @@ -68,7 +66,8 @@ struct m25p { struct spi_device *spi; struct mutex lock; struct mtd_info mtd; - unsigned partitioned; + unsigned partitioned:1; + u8 erase_opcode; u8 command[4]; }; @@ -151,8 +150,9 @@ static int wait_till_ready(struct m25p *flash) */ static int erase_sector(struct m25p *flash, u32 offset) { - DEBUG(MTD_DEBUG_LEVEL3, "%s: %s at 0x%08x\n", flash->spi->dev.bus_id, - __FUNCTION__, offset); + DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dK at 0x%08x\n", + flash->spi->dev.bus_id, __FUNCTION__, + flash->mtd.erasesize / 1024, offset); /* Wait until finished previous write command. */ if (wait_till_ready(flash)) @@ -162,7 +162,7 @@ static int erase_sector(struct m25p *flash, u32 offset) write_enable(flash); /* Set up command buffer. */ - flash->command[0] = OPCODE_SE; + flash->command[0] = flash->erase_opcode; flash->command[1] = offset >> 16; flash->command[2] = offset >> 8; flash->command[3] = offset; @@ -204,6 +204,10 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) mutex_lock(&flash->lock); + /* REVISIT in some cases we could speed up erasing large regions + * by using OPCODE_SE instead of OPCODE_BE_4K + */ + /* now erase those sectors */ while (len) { if (erase_sector(flash, addr)) { @@ -270,7 +274,10 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, return 1; } - /* NOTE: OPCODE_FAST_READ (if available) is faster... */ + /* FIXME switch to OPCODE_FAST_READ. It's required for higher + * clocks; and at this writing, every chip this driver handles + * supports that opcode. + */ /* Set up the write data buffer. */ flash->command[0] = OPCODE_READ; @@ -399,24 +406,118 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, struct flash_info { char *name; - u8 id; - u16 jedec_id; + + /* JEDEC id zero means "no ID" (most older chips); otherwise it has + * a high byte of zero plus three data bytes: the manufacturer id, + * then a two byte device id. + */ + u32 jedec_id; + + /* The size listed here is what works with OPCODE_SE, which isn't + * necessarily called a "sector" by the vendor. + */ unsigned sector_size; - unsigned n_sectors; + u16 n_sectors; + + u16 flags; +#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ }; + +/* NOTE: double check command sets and memory organization when you add + * more flash chips. This current list focusses on newer chips, which + * have been converging on command sets which including JEDEC ID. + */ static struct flash_info __devinitdata m25p_data [] = { - /* JEDEC id zero means "has no ID" */ - { "m25p05", 0x05, 0x2010, 32 * 1024, 2 }, - { "m25p10", 0x10, 0x2011, 32 * 1024, 4 }, - { "m25p20", 0x11, 0x2012, 64 * 1024, 4 }, - { "m25p40", 0x12, 0x2013, 64 * 1024, 8 }, - { "m25p80", 0x13, 0x0000, 64 * 1024, 16 }, - { "m25p16", 0x14, 0x2015, 64 * 1024, 32 }, - { "m25p32", 0x15, 0x2016, 64 * 1024, 64 }, - { "m25p64", 0x16, 0x2017, 64 * 1024, 128 }, + + /* Atmel -- some are (confusingly) marketed as "DataFlash" */ + { "at25fs010", 0x1f6601, 32 * 1024, 4, SECT_4K, }, + { "at25fs040", 0x1f6604, 64 * 1024, 8, SECT_4K, }, + + { "at25df041a", 0x1f4401, 64 * 1024, 8, SECT_4K, }, + + { "at26f004", 0x1f0400, 64 * 1024, 8, SECT_4K, }, + { "at26df081a", 0x1f4501, 64 * 1024, 16, SECT_4K, }, + { "at26df161a", 0x1f4601, 64 * 1024, 32, SECT_4K, }, + { "at26df321", 0x1f4701, 64 * 1024, 64, SECT_4K, }, + + /* Spansion -- single (large) sector size only, at least + * for the chips listed here (without boot sectors). + */ + { "s25sl004a", 0x010212, 64 * 1024, 8, }, + { "s25sl008a", 0x010213, 64 * 1024, 16, }, + { "s25sl016a", 0x010214, 64 * 1024, 32, }, + { "s25sl032a", 0x010215, 64 * 1024, 64, }, + { "s25sl064a", 0x010216, 64 * 1024, 128, }, + + /* SST -- large erase sizes are "overlays", "sectors" are 4K */ + { "sst25vf040b", 0xbf258d, 64 * 1024, 8, SECT_4K, }, + { "sst25vf080b", 0xbf258e, 64 * 1024, 16, SECT_4K, }, + { "sst25vf016b", 0xbf2541, 64 * 1024, 32, SECT_4K, }, + { "sst25vf032b", 0xbf254a, 64 * 1024, 64, SECT_4K, }, + + /* ST Microelectronics -- newer production may have feature updates */ + { "m25p05", 0x202010, 32 * 1024, 2, }, + { "m25p10", 0x202011, 32 * 1024, 4, }, + { "m25p20", 0x202012, 64 * 1024, 4, }, + { "m25p40", 0x202013, 64 * 1024, 8, }, + { "m25p80", 0, 64 * 1024, 16, }, + { "m25p16", 0x202015, 64 * 1024, 32, }, + { "m25p32", 0x202016, 64 * 1024, 64, }, + { "m25p64", 0x202017, 64 * 1024, 128, }, + { "m25p128", 0x202018, 256 * 1024, 64, }, + + { "m45pe80", 0x204014, 64 * 1024, 16, }, + { "m45pe16", 0x204015, 64 * 1024, 32, }, + + { "m25pe80", 0x208014, 64 * 1024, 16, }, + { "m25pe16", 0x208015, 64 * 1024, 32, SECT_4K, }, + + /* Winbond -- w25x "blocks" are 64K, "sectors" are 4K */ + { "w25x10", 0xef3011, 64 * 1024, 2, SECT_4K, }, + { "w25x20", 0xef3012, 64 * 1024, 4, SECT_4K, }, + { "w25x40", 0xef3013, 64 * 1024, 8, SECT_4K, }, + { "w25x80", 0xef3014, 64 * 1024, 16, SECT_4K, }, + { "w25x16", 0xef3015, 64 * 1024, 32, SECT_4K, }, + { "w25x32", 0xef3016, 64 * 1024, 64, SECT_4K, }, + { "w25x64", 0xef3017, 64 * 1024, 128, SECT_4K, }, }; +static struct flash_info *__devinit jedec_probe(struct spi_device *spi) +{ + int tmp; + u8 code = OPCODE_RDID; + u8 id[3]; + u32 jedec; + struct flash_info *info; + + /* JEDEC also defines an optional "extended device information" + * string for after vendor-specific data, after the three bytes + * we use here. Supporting some chips might require using it. + */ + tmp = spi_write_then_read(spi, &code, 1, id, 3); + if (tmp < 0) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", + spi->dev.bus_id, tmp); + return NULL; + } + jedec = id[0]; + jedec = jedec << 8; + jedec |= id[1]; + jedec = jedec << 8; + jedec |= id[2]; + + for (tmp = 0, info = m25p_data; + tmp < ARRAY_SIZE(m25p_data); + tmp++, info++) { + if (info->jedec_id == jedec) + return info; + } + dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec); + return NULL; +} + + /* * board specific setup should have ensured the SPI clock used here * matches what the READ command supports, at least until this driver @@ -430,27 +531,41 @@ static int __devinit m25p_probe(struct spi_device *spi) unsigned i; /* Platform data helps sort out which chip type we have, as - * well as how this board partitions it. + * well as how this board partitions it. If we don't have + * a chip ID, try the JEDEC id commands; they'll work for most + * newer chips, even if we don't recognize the particular chip. */ data = spi->dev.platform_data; - if (!data || !data->type) { - /* FIXME some chips can identify themselves with RES - * or JEDEC get-id commands. Try them ... - */ - DEBUG(MTD_DEBUG_LEVEL1, "%s: no chip id\n", - spi->dev.bus_id); - return -ENODEV; - } + if (data && data->type) { + for (i = 0, info = m25p_data; + i < ARRAY_SIZE(m25p_data); + i++, info++) { + if (strcmp(data->type, info->name) == 0) + break; + } - for (i = 0, info = m25p_data; i < ARRAY_SIZE(m25p_data); i++, info++) { - if (strcmp(data->type, info->name) == 0) - break; - } - if (i == ARRAY_SIZE(m25p_data)) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: unrecognized id %s\n", - spi->dev.bus_id, data->type); + /* unrecognized chip? */ + if (i == ARRAY_SIZE(m25p_data)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n", + spi->dev.bus_id, data->type); + info = NULL; + + /* recognized; is that chip really what's there? */ + } else if (info->jedec_id) { + struct flash_info *chip = jedec_probe(spi); + + if (!chip || chip != info) { + dev_warn(&spi->dev, "found %s, expected %s\n", + chip ? chip->name : "UNKNOWN", + info->name); + info = NULL; + } + } + } else + info = jedec_probe(spi); + + if (!info) return -ENODEV; - } flash = kzalloc(sizeof *flash, GFP_KERNEL); if (!flash) @@ -460,7 +575,7 @@ static int __devinit m25p_probe(struct spi_device *spi) mutex_init(&flash->lock); dev_set_drvdata(&spi->dev, flash); - if (data->name) + if (data && data->name) flash->mtd.name = data->name; else flash->mtd.name = spi->dev.bus_id; @@ -469,11 +584,19 @@ static int __devinit m25p_probe(struct spi_device *spi) 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; flash->mtd.erase = m25p80_erase; flash->mtd.read = m25p80_read; flash->mtd.write = m25p80_write; + /* prefer "small sector" erase if possible */ + if (info->flags & SECT_4K) { + flash->erase_opcode = OPCODE_BE_4K; + flash->mtd.erasesize = 4096; + } else { + flash->erase_opcode = OPCODE_SE; + flash->mtd.erasesize = info->sector_size; + } + dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name, flash->mtd.size / 1024); @@ -517,14 +640,14 @@ static int __devinit m25p_probe(struct spi_device *spi) } if (nr_parts > 0) { - for (i = 0; i < data->nr_parts; i++) { + for (i = 0; i < nr_parts; i++) { DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " "{.name = %s, .offset = 0x%.8x, " ".size = 0x%.8x (%uK) }\n", - i, data->parts[i].name, - data->parts[i].offset, - data->parts[i].size, - data->parts[i].size / 1024); + i, parts[i].name, + parts[i].offset, + parts[i].size, + parts[i].size / 1024); } flash->partitioned = 1; return add_mtd_partitions(&flash->mtd, parts, nr_parts); @@ -561,6 +684,11 @@ static struct spi_driver m25p80_driver = { }, .probe = m25p_probe, .remove = __devexit_p(m25p_remove), + + /* REVISIT: many of these chips have deep power-down modes, which + * should clearly be entered on suspend() to minimize power use. + * And also when they're otherwise idle... + */ }; -- cgit v1.2.2 From 02d087dbc8cfade63205ee78612c5fd3762afc03 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 28 Jun 2007 22:38:38 +0100 Subject: [MTD] m25p80: Use correct units for binary multiples Signed-off-by: David Woodhouse --- drivers/mtd/devices/m25p80.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 6668a8c27c..98df5bcc02 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -36,9 +36,9 @@ #define OPCODE_READ 0x03 /* Read data bytes (low frequency) */ #define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ #define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ -#define OPCODE_BE_4K 0x20 /* Erase 4K block */ -#define OPCODE_BE_32K 0x52 /* Erase 32K block */ -#define OPCODE_SE 0xd8 /* Sector erase (usually 64K) */ +#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ +#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ +#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ #define OPCODE_RDID 0x9f /* Read JEDEC ID */ /* Status Register bits. */ @@ -150,7 +150,7 @@ static int wait_till_ready(struct m25p *flash) */ static int erase_sector(struct m25p *flash, u32 offset) { - DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dK at 0x%08x\n", + DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n", flash->spi->dev.bus_id, __FUNCTION__, flash->mtd.erasesize / 1024, offset); @@ -473,7 +473,7 @@ static struct flash_info __devinitdata m25p_data [] = { { "m25pe80", 0x208014, 64 * 1024, 16, }, { "m25pe16", 0x208015, 64 * 1024, 32, SECT_4K, }, - /* Winbond -- w25x "blocks" are 64K, "sectors" are 4K */ + /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ { "w25x10", 0xef3011, 64 * 1024, 2, SECT_4K, }, { "w25x20", 0xef3012, 64 * 1024, 4, SECT_4K, }, { "w25x40", 0xef3013, 64 * 1024, 8, SECT_4K, }, @@ -601,8 +601,8 @@ static int __devinit m25p_probe(struct spi_device *spi) flash->mtd.size / 1024); DEBUG(MTD_DEBUG_LEVEL2, - "mtd .name = %s, .size = 0x%.8x (%uM) " - ".erasesize = 0x%.8x (%uK) .numeraseregions = %d\n", + "mtd .name = %s, .size = 0x%.8x (%uMiB) " + ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", flash->mtd.name, flash->mtd.size, flash->mtd.size / (1024*1024), flash->mtd.erasesize, flash->mtd.erasesize / 1024, @@ -612,7 +612,7 @@ static int __devinit m25p_probe(struct spi_device *spi) for (i = 0; i < flash->mtd.numeraseregions; i++) DEBUG(MTD_DEBUG_LEVEL2, "mtd.eraseregions[%d] = { .offset = 0x%.8x, " - ".erasesize = 0x%.8x (%uK), " + ".erasesize = 0x%.8x (%uKiB), " ".numblocks = %d }\n", i, flash->mtd.eraseregions[i].offset, flash->mtd.eraseregions[i].erasesize, @@ -643,7 +643,7 @@ static int __devinit m25p_probe(struct spi_device *spi) for (i = 0; i < nr_parts; i++) { DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " "{.name = %s, .offset = 0x%.8x, " - ".size = 0x%.8x (%uK) }\n", + ".size = 0x%.8x (%uKiB) }\n", i, parts[i].name, parts[i].offset, parts[i].size, -- cgit v1.2.2 From ec9ce52ef8f8496c83bc36a9b19f22e83575f890 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Thu, 28 Jun 2007 22:58:42 +0100 Subject: [MTD] Use mutex instead of semaphore in dataflash driver The MTD DataFlash driver uses a semaphore as mutex. Use the mutex API instead of the (binary) semaphore. Signed-off-by: Matthias Kaehlcke Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/devices/mtd_dataflash.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index a987e917f4..a5ed6d232c 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -89,7 +90,7 @@ struct dataflash { unsigned short page_offset; /* offset in flash address */ unsigned int page_size; /* of bytes per page */ - struct semaphore lock; + struct mutex lock; struct spi_device *spi; struct mtd_info mtd; @@ -167,7 +168,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) x.len = 4; spi_message_add_tail(&x, &msg); - down(&priv->lock); + mutex_lock(&priv->lock); while (instr->len > 0) { unsigned int pageaddr; int status; @@ -210,7 +211,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) instr->len -= priv->page_size; } } - up(&priv->lock); + mutex_unlock(&priv->lock); /* Inform MTD subsystem that erase is complete */ instr->state = MTD_ERASE_DONE; @@ -266,7 +267,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, x[1].len = len; spi_message_add_tail(&x[1], &msg); - down(&priv->lock); + mutex_lock(&priv->lock); /* Continuous read, max clock = f(car) which may be less than * the peak rate available. Some chips support commands with @@ -279,7 +280,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, /* plus 4 "don't care" bytes */ status = spi_sync(priv->spi, &msg); - up(&priv->lock); + mutex_unlock(&priv->lock); if (status >= 0) { *retlen = msg.actual_length - 8; @@ -336,7 +337,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, else writelen = len; - down(&priv->lock); + mutex_lock(&priv->lock); while (remaining > 0) { DEBUG(MTD_DEBUG_LEVEL3, "write @ %i:%i len=%i\n", pageaddr, offset, writelen); @@ -441,7 +442,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, else writelen = remaining; } - up(&priv->lock); + mutex_unlock(&priv->lock); return status; } @@ -463,7 +464,7 @@ add_dataflash(struct spi_device *spi, char *name, if (!priv) return -ENOMEM; - init_MUTEX(&priv->lock); + mutex_init(&priv->lock); priv->spi = spi; priv->page_size = pagesize; priv->page_offset = pageoffset; -- cgit v1.2.2 From 1da1caf8d80e953d0ff0c2131b04ccc609c3baa8 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 28 Jun 2007 23:00:09 +0100 Subject: [MTD] [NOR] Use NULL instead of 0 for pointer to shut sparse up. Use NULL instead of 0 for pointer: drivers/mtd/chips/cfi_cmdset_0001.c:2258:43: warning: Using plain integer as NULL pointer Other changes by inspection. Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 46d782f261..f838be2259 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -1930,7 +1930,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", __FUNCTION__, ofs, len); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, - ofs, len, 0); + ofs, len, NULL); #endif ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, @@ -1940,7 +1940,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) printk(KERN_DEBUG "%s: lock status after, ret=%d\n", __FUNCTION__, ret); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, - ofs, len, 0); + ofs, len, NULL); #endif return ret; @@ -1954,7 +1954,7 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", __FUNCTION__, ofs, len); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, - ofs, len, 0); + ofs, len, NULL); #endif ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, @@ -1964,7 +1964,7 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) printk(KERN_DEBUG "%s: lock status after, ret=%d\n", __FUNCTION__, ret); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, - ofs, len, 0); + ofs, len, NULL); #endif return ret; -- cgit v1.2.2 From 663d77a7ccfd407cf7491dbd53c7c17eef58c96a Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Thu, 28 Jun 2007 23:02:59 +0100 Subject: [MTD] [MAPS] Cleanup nettel map driver - make 2 needlessly global functions static - remove the unused nettel_eraseconfig() Signed-off-by: Adrian Bunk Cc: Greg Ungerer Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/maps/nettel.c | 61 ++--------------------------------------------- 1 file changed, 2 insertions(+), 59 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 88525622e9..0c9b305a72 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -158,68 +158,11 @@ static struct notifier_block nettel_notifier_block = { nettel_reboot_notifier, NULL, 0 }; -/* - * Erase the configuration file system. - * Used to support the software reset button. - */ -static void nettel_erasecallback(struct erase_info *done) -{ - wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; - wake_up(wait_q); -} - -static struct erase_info nettel_erase; - -int nettel_eraseconfig(void) -{ - struct mtd_info *mtd; - DECLARE_WAITQUEUE(wait, current); - wait_queue_head_t wait_q; - int ret; - - init_waitqueue_head(&wait_q); - mtd = get_mtd_device(NULL, 2); - if (!IS_ERR(mtd)) { - nettel_erase.mtd = mtd; - nettel_erase.callback = nettel_erasecallback; - nettel_erase.callback = NULL; - nettel_erase.addr = 0; - nettel_erase.len = mtd->size; - nettel_erase.priv = (u_long) &wait_q; - nettel_erase.priv = 0; - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&wait_q, &wait); - - ret = mtd->erase(mtd, &nettel_erase); - if (ret) { - set_current_state(TASK_RUNNING); - remove_wait_queue(&wait_q, &wait); - put_mtd_device(mtd); - return(ret); - } - - schedule(); /* Wait for erase to finish. */ - remove_wait_queue(&wait_q, &wait); - - put_mtd_device(mtd); - } - - return(0); -} - -#else - -int nettel_eraseconfig(void) -{ - return(0); -} - #endif /****************************************************************************/ -int __init nettel_init(void) +static int __init nettel_init(void) { volatile unsigned long *amdpar; unsigned long amdaddr, maxsize; @@ -473,7 +416,7 @@ out_unmap2: /****************************************************************************/ -void __exit nettel_cleanup(void) +static void __exit nettel_cleanup(void) { #ifdef CONFIG_MTD_CFI_INTELEXT unregister_reboot_notifier(&nettel_notifier_block); -- cgit v1.2.2 From 8f46c527acdb84f98d48809ba29173f515de1080 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 22 Jun 2007 01:52:08 +0200 Subject: [MTD] [NAND] cafe_nand.c: the OLPC laptop is not available for $100 The price might drop to $100 in a few years. But currently, a more reasonable name might be "$175 laptop". Let's simply call it "OLPC laptop" without any price tag. Signed-off-by: Adrian Bunk Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index d8b8fce511..df25cabb04 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -237,7 +237,7 @@ config MTD_NAND_CAFE select REED_SOLOMON select REED_SOLOMON_DEC16 help - Use NAND flash attached to the CAFÉ chip designed for the $100 + Use NAND flash attached to the CAFÉ chip designed for the OLPC laptop. config MTD_NAND_CS553X -- cgit v1.2.2 From 8e0aedc5bcb9da4f613af097295a02a57aa0d41c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 29 Jun 2007 14:18:22 +0100 Subject: [MTD] Use proper binary multiple prefixes in pmc551 driver Signed-off-by: David Woodhouse --- drivers/mtd/devices/pmc551.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index e8f686f7a3..d1cf66e0dc 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -30,8 +30,8 @@ * * Notes: * Due to what I assume is more buggy SROM, the 64M PMC551 I - * have available claims that all 4 of it's DRAM banks have 64M - * of ram configured (making a grand total of 256M onboard). + * have available claims that all 4 of its DRAM banks have 64MiB + * of ram configured (making a grand total of 256MiB onboard). * This is slightly annoying since the BAR0 size reflects the * aperture size, not the dram size, and the V370PDC supplies no * other method for memory size discovery. This problem is @@ -70,7 +70,7 @@ * made the memory unusable, added a fix to code to touch up * the DRAM some. * - * Bugs/FIXME's: + * Bugs/FIXMEs: * * MUST fix the init function to not spin on a register * waiting for it to set .. this does not safely handle busted * devices that never reset the register correctly which will @@ -562,10 +562,10 @@ static u32 fixup_pmc551(struct pci_dev *dev) /* * Some screen fun */ - printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at " + printk(KERN_DEBUG "pmc551: %d%sB (0x%x) of %sprefetchable memory at " "0x%llx\n", (size < 1024) ? size : (size < 1048576) ? size >> 10 : size >> 20, - (size < 1024) ? 'B' : (size < 1048576) ? 'K' : 'M', size, + (size < 1024) ? "" : (size < 1048576) ? "Ki" : "Mi", size, ((dcmd & (0x1 << 3)) == 0) ? "non-" : "", (unsigned long long)pci_resource_start(dev, 0)); @@ -656,7 +656,7 @@ static int asize = 0; #endif module_param(msize, int, 0); -MODULE_PARM_DESC(msize, "memory size in Megabytes [1 - 1024]"); +MODULE_PARM_DESC(msize, "memory size in MiB [1 - 1024]"); module_param(asize, int, 0); MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1-1024]"); @@ -799,8 +799,7 @@ static int __init init_pmc551(void) mtd->owner = THIS_MODULE; if (add_mtd_device(mtd)) { - printk(KERN_NOTICE "pmc551: Failed to register new " - "device\n"); + printk(KERN_NOTICE "pmc551: Failed to register new device\n"); pci_iounmap(PCI_Device, priv->start); kfree(mtd->priv); kfree(mtd); @@ -811,13 +810,13 @@ static int __init init_pmc551(void) pci_dev_get(PCI_Device); printk(KERN_NOTICE "Registered pmc551 memory device.\n"); - printk(KERN_NOTICE "Mapped %dM of memory from 0x%p to 0x%p\n", + printk(KERN_NOTICE "Mapped %dMiB of memory from 0x%p to 0x%p\n", priv->asize >> 20, priv->start, priv->start + priv->asize); - printk(KERN_NOTICE "Total memory is %d%c\n", + printk(KERN_NOTICE "Total memory is %d%sB\n", (length < 1024) ? length : (length < 1048576) ? length >> 10 : length >> 20, - (length < 1024) ? 'B' : (length < 1048576) ? 'K' : 'M'); + (length < 1024) ? "" : (length < 1048576) ? "Ki" : "Mi"); priv->nextpmc551 = pmc551list; pmc551list = mtd; found++; @@ -850,7 +849,7 @@ static void __exit cleanup_pmc551(void) pmc551list = priv->nextpmc551; if (priv->start) { - printk(KERN_DEBUG "pmc551: unmapping %dM starting at " + printk(KERN_DEBUG "pmc551: unmapping %dMiB starting at " "0x%p\n", priv->asize >> 20, priv->start); pci_iounmap(priv->dev, priv->start); } -- cgit v1.2.2 From a97145c22408865579be397460ca998301ab63a3 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sun, 3 Jun 2007 22:54:00 +0200 Subject: [MTD] remove dead MTD_PMC551_APERTURE_SIZE option The CONFIG_MTD_PMC551_APERTURE_SIZE option seems to never have existed, so there's no reason for carrying an #ifdef for it. Signed-off-by: Adrian Bunk Signed-off-by: David Woodhouse --- drivers/mtd/devices/pmc551.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index d1cf66e0dc..7060a0895c 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -649,11 +649,7 @@ MODULE_DESCRIPTION(PMC551_VERSION); * Stuff these outside the ifdef so as to not bust compiled in driver support */ static int msize = 0; -#if defined(CONFIG_MTD_PMC551_APERTURE_SIZE) -static int asize = CONFIG_MTD_PMC551_APERTURE_SIZE; -#else static int asize = 0; -#endif module_param(msize, int, 0); MODULE_PARM_DESC(msize, "memory size in MiB [1 - 1024]"); -- cgit v1.2.2 From ee9745fcf214272b7cdd9d320d044cf433ee958e Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Sat, 30 Jun 2007 13:57:49 +0900 Subject: [MTD] [OneNAND] 2X program support The 2X Program is an extension of Program Operation. Since the device is equipped with two DataRAMs, and two-plane NAND Flash memory array, these two component enables simultaneous program of 4KiB. Plane1 has only even blocks such as block0, block2, block4 while Plane2 has only odd blocks such as block1, block3, block5. So MTD regards it as 4KiB page size and 256KiB block size Now the following chips support it. (KFXXX16Q2M) Demux: KFG2G16Q2M, KFH4G16Q2M, KFW8G16Q2M, Mux: KFM2G16Q2M, KFN4G16Q2M, And more recent chips Signed-off-by: Kyungmin Park Signed-off-by: David Woodhouse --- drivers/mtd/onenand/Kconfig | 16 +++++ drivers/mtd/onenand/onenand_base.c | 139 ++++++++++++++++++++++++++++++------- 2 files changed, 128 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index c257d397d0..64ec034c43 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -40,4 +40,20 @@ config MTD_ONENAND_OTP OTP block is fully-guaranteed to be a valid block. +config MTD_ONENAND_2X_PROGRAM + bool "OneNAND 2X program support" + help + The 2X Program is an extension of Program Operation. + Since the device is equipped with two DataRAMs, and two-plane NAND + Flash memory array, these two component enables simultaneous program + of 4KiB. Plane1 has only even blocks such as block0, block2, block4 + while Plane2 has only odd blocks such as block1, block3, block5. + So MTD regards it as 4KiB page size and 256KiB block size + + Now the following chips support it. (KFXXX16Q2M) + Demux: KFG2G16Q2M, KFH4G16Q2M, KFW8G16Q2M, + Mux: KFM2G16Q2M, KFN4G16Q2M, + + And more recent chips + endif # MTD_ONENAND diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 0537fac8de..7d194cfdb8 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -206,6 +206,15 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le default: block = (int) (addr >> this->erase_shift); page = (int) (addr >> this->page_shift); + + if (ONENAND_IS_2PLANE(this)) { + /* Make the even block number */ + block &= ~1; + /* Is it the odd plane? */ + if (addr & this->writesize) + block++; + page >>= 1; + } page &= this->page_mask; break; } @@ -216,8 +225,12 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le value = onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); - /* Switch to the next data buffer */ - ONENAND_SET_NEXT_BUFFERRAM(this); + if (ONENAND_IS_2PLANE(this)) + /* It is always BufferRAM0 */ + ONENAND_SET_BUFFERRAM0(this); + else + /* Switch to the next data buffer */ + ONENAND_SET_NEXT_BUFFERRAM(this); return 0; } @@ -247,6 +260,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le break; default: + if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG) + cmd = ONENAND_CMD_2X_PROG; dataram = ONENAND_CURRENT_BUFFERRAM(this); break; } @@ -445,8 +460,9 @@ static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) struct onenand_chip *this = mtd->priv; if (ONENAND_CURRENT_BUFFERRAM(this)) { + /* Note: the 'this->writesize' is a real page size */ if (area == ONENAND_DATARAM) - return mtd->writesize; + return this->writesize; if (area == ONENAND_SPARERAM) return mtd->oobsize; } @@ -571,6 +587,30 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area, return 0; } +/** + * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode + * @param mtd MTD data structure + * @param addr address to check + * @return blockpage address + * + * Get blockpage address at 2x program mode + */ +static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr) +{ + struct onenand_chip *this = mtd->priv; + int blockpage, block, page; + + /* Calculate the even block number */ + block = (int) (addr >> this->erase_shift) & ~1; + /* Is it the odd plane? */ + if (addr & this->writesize) + block++; + page = (int) (addr >> (this->page_shift + 1)) & this->page_mask; + blockpage = (block << 7) | page; + + return blockpage; +} + /** * onenand_check_bufferram - [GENERIC] Check BufferRAM information * @param mtd MTD data structure @@ -585,7 +625,10 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) int blockpage, found = 0; unsigned int i; - blockpage = (int) (addr >> this->page_shift); + if (ONENAND_IS_2PLANE(this)) + blockpage = onenand_get_2x_blockpage(mtd, addr); + else + blockpage = (int) (addr >> this->page_shift); /* Is there valid data? */ i = ONENAND_CURRENT_BUFFERRAM(this); @@ -625,7 +668,10 @@ static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, int blockpage; unsigned int i; - blockpage = (int) (addr >> this->page_shift); + if (ONENAND_IS_2PLANE(this)) + blockpage = onenand_get_2x_blockpage(mtd, addr); + else + blockpage = (int) (addr >> this->page_shift); /* Invalidate another BufferRAM */ i = ONENAND_NEXT_BUFFERRAM(this); @@ -734,6 +780,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, int read = 0, column; int thislen; int ret = 0, boundary = 0; + int writesize = this->writesize; DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); @@ -754,22 +801,22 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, /* Do first load to bufferRAM */ if (read < len) { if (!onenand_check_bufferram(mtd, from)) { - this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize); + this->command(mtd, ONENAND_CMD_READ, from, writesize); ret = this->wait(mtd, FL_READING); onenand_update_bufferram(mtd, from, !ret); } } - thislen = min_t(int, mtd->writesize, len - read); - column = from & (mtd->writesize - 1); - if (column + thislen > mtd->writesize) - thislen = mtd->writesize - column; + thislen = min_t(int, writesize, len - read); + column = from & (writesize - 1); + if (column + thislen > writesize) + thislen = writesize - column; while (!ret) { /* If there is more to load then start next load */ from += thislen; if (read + thislen < len) { - this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize); + this->command(mtd, ONENAND_CMD_READ, from, writesize); /* * Chip boundary handling in DDP * Now we issued chip 1 read and pointed chip 1 @@ -794,7 +841,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); ONENAND_SET_NEXT_BUFFERRAM(this); buf += thislen; - thislen = min_t(int, mtd->writesize, len - read); + thislen = min_t(int, writesize, len - read); column = 0; cond_resched(); /* Now wait for load */ @@ -1079,7 +1126,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, /* Read more? */ if (read < len) { /* Update Page size */ - from += mtd->writesize; + from += this->writesize; column = 0; } } @@ -1135,12 +1182,12 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, int thislen, column; while (len != 0) { - thislen = min_t(int, mtd->writesize, len); - column = addr & (mtd->writesize - 1); - if (column + thislen > mtd->writesize) - thislen = mtd->writesize - column; + thislen = min_t(int, this->writesize, len); + column = addr & (this->writesize - 1); + if (column + thislen > this->writesize) + thislen = this->writesize - column; - this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); + this->command(mtd, ONENAND_CMD_READ, addr, this->writesize); onenand_update_bufferram(mtd, addr, 0); @@ -1236,6 +1283,10 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, /* In partial page write we don't update bufferram */ onenand_update_bufferram(mtd, to, !ret && !subpage); + if (ONENAND_IS_2PLANE(this)) { + ONENAND_SET_BUFFERRAM1(this); + onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage); + } if (ret) { printk(KERN_ERR "onenand_write: write filaed %d\n", ret); @@ -1384,6 +1435,10 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); + if (ONENAND_IS_2PLANE(this)) { + ONENAND_SET_BUFFERRAM1(this); + onenand_update_bufferram(mtd, to + this->writesize, 0); + } ret = this->wait(mtd, FL_WRITING); if (ret) { @@ -2107,6 +2162,7 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, * * Check and set OneNAND features * - lock scheme + * - two plane */ static void onenand_check_features(struct mtd_info *mtd) { @@ -2118,19 +2174,35 @@ static void onenand_check_features(struct mtd_info *mtd) process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; /* Lock scheme */ - if (density >= ONENAND_DEVICE_DENSITY_1Gb) { + switch (density) { + case ONENAND_DEVICE_DENSITY_4Gb: + this->options |= ONENAND_HAS_2PLANE; + + case ONENAND_DEVICE_DENSITY_2Gb: + /* 2Gb DDP don't have 2 plane */ + if (!ONENAND_IS_DDP(this)) + this->options |= ONENAND_HAS_2PLANE; + this->options |= ONENAND_HAS_UNLOCK_ALL; + + case ONENAND_DEVICE_DENSITY_1Gb: /* A-Die has all block unlock */ - if (process) { - printk(KERN_DEBUG "Chip support all block unlock\n"); + if (process) this->options |= ONENAND_HAS_UNLOCK_ALL; - } - } else { - /* Some OneNAND has continues lock scheme */ - if (!process) { - printk(KERN_DEBUG "Lock scheme is Continues Lock\n"); + break; + + default: + /* Some OneNAND has continuous lock scheme */ + if (!process) this->options |= ONENAND_HAS_CONT_LOCK; - } + break; } + + if (this->options & ONENAND_HAS_CONT_LOCK) + printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); + if (this->options & ONENAND_HAS_UNLOCK_ALL) + printk(KERN_DEBUG "Chip support all block unlock\n"); + if (this->options & ONENAND_HAS_2PLANE) + printk(KERN_DEBUG "Chip has 2 plane\n"); } /** @@ -2257,6 +2329,8 @@ static int onenand_probe(struct mtd_info *mtd) this->erase_shift = ffs(mtd->erasesize) - 1; this->page_shift = ffs(mtd->writesize) - 1; this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; + /* It's real page size */ + this->writesize = mtd->writesize; /* REVIST: Multichip handling */ @@ -2265,6 +2339,17 @@ static int onenand_probe(struct mtd_info *mtd) /* Check OneNAND features */ onenand_check_features(mtd); + /* + * We emulate the 4KiB page and 256KiB erase block size + * But oobsize is still 64 bytes. + * It is only valid if you turn on 2X program support, + * Otherwise it will be ignored by compiler. + */ + if (ONENAND_IS_2PLANE(this)) { + mtd->writesize <<= 1; + mtd->erasesize <<= 1; + } + return 0; } -- cgit v1.2.2 From 8dab169b8bdea3bcbc08b15fdbd9a21526fdbb77 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Sat, 30 Jun 2007 14:14:43 +0900 Subject: [MTD] OneNAND Simulator support This simulate various OneNAND flash chips for the MTD onenand layer. It's simple implementation, only basic operations. It don't support the recent changes in NANDSIM such as lazy block allocation, bitflip, and so on. Note: This passed nand-tests. Signed-off-by: Kyungmin Park Signed-off-by: David Woodhouse --- drivers/mtd/onenand/Kconfig | 7 + drivers/mtd/onenand/Makefile | 3 + drivers/mtd/onenand/onenand_sim.c | 495 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 505 insertions(+) create mode 100644 drivers/mtd/onenand/onenand_sim.c (limited to 'drivers') diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 64ec034c43..cb41cbca64 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -56,4 +56,11 @@ config MTD_ONENAND_2X_PROGRAM And more recent chips +config MTD_ONENAND_SIM + tristate "OneNAND simulator support" + depends on MTD_PARTITIONS + help + The simulator may simulate various OneNAND flash chips for the + OneNAND MTD layer. + endif # MTD_ONENAND diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile index 269cfe4673..4d2eacfd7e 100644 --- a/drivers/mtd/onenand/Makefile +++ b/drivers/mtd/onenand/Makefile @@ -8,4 +8,7 @@ obj-$(CONFIG_MTD_ONENAND) += onenand.o # Board specific. obj-$(CONFIG_MTD_ONENAND_GENERIC) += generic.o +# Simulator +obj-$(CONFIG_MTD_ONENAND_SIM) += onenand_sim.o + onenand-objs = onenand_base.o onenand_bbt.o diff --git a/drivers/mtd/onenand/onenand_sim.c b/drivers/mtd/onenand/onenand_sim.c new file mode 100644 index 0000000000..dfa39dfcc1 --- /dev/null +++ b/drivers/mtd/onenand/onenand_sim.c @@ -0,0 +1,495 @@ +/* + * linux/drivers/mtd/onenand/onenand_sim.c + * + * The OneNAND simulator + * + * Copyright(C) 2005-2007 Samsung Electronics + * Kyungmin Park + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef CONFIG_ONENAND_SIM_MANUFACTURER +#define CONFIG_ONENAND_SIM_MANUFACTURER 0xec +#endif +#ifndef CONFIG_ONENAND_SIM_DEVICE_ID +#define CONFIG_ONENAND_SIM_DEVICE_ID 0x04 +#endif +#ifndef CONFIG_ONENAND_SIM_VERSION_ID +#define CONFIG_ONENAND_SIM_VERSION_ID 0x1e +#endif + +static int manuf_id = CONFIG_ONENAND_SIM_MANUFACTURER; +static int device_id = CONFIG_ONENAND_SIM_DEVICE_ID; +static int version_id = CONFIG_ONENAND_SIM_VERSION_ID; + +struct onenand_flash { + void __iomem *base; + void __iomem *data; +}; + +#define ONENAND_CORE(flash) (flash->data) +#define ONENAND_CORE_SPARE(flash, this, offset) \ + ((flash->data) + (this->chipsize) + (offset >> 5)) + +#define ONENAND_MAIN_AREA(this, offset) \ + (this->base + ONENAND_DATARAM + offset) + +#define ONENAND_SPARE_AREA(this, offset) \ + (this->base + ONENAND_SPARERAM + offset) + +#define ONENAND_GET_WP_STATUS(this) \ + (readw(this->base + ONENAND_REG_WP_STATUS)) + +#define ONENAND_SET_WP_STATUS(v, this) \ + (writew(v, this->base + ONENAND_REG_WP_STATUS)) + +/* It has all 0xff chars */ +#define MAX_ONENAND_PAGESIZE (2048 + 64) +static unsigned char *ffchars; + +static struct mtd_partition os_partitions[] = { + { + .name = "OneNAND simulator partition", + .offset = 0, + .size = MTDPART_SIZ_FULL, + }, +}; + +/* + * OneNAND simulator mtd + */ +struct onenand_info { + struct mtd_info mtd; + struct mtd_partition *parts; + struct onenand_chip onenand; + struct onenand_flash flash; +}; + +struct onenand_info *info; + +#define DPRINTK(format, args...) \ +do { \ + printk("%s[%d]: " format "\n", __func__, __LINE__, ##args); \ +} while (0) + +/** + * onenand_lock_handle - Handle Lock scheme + * @param this OneNAND device structure + * @param cmd The command to be sent + * + * Send lock command to OneNAND device. + * The lock scheme is depends on chip type. + */ +static void onenand_lock_handle(struct onenand_chip *this, int cmd) +{ + int block_lock_scheme; + int status; + + status = ONENAND_GET_WP_STATUS(this); + block_lock_scheme = !(this->options & ONENAND_HAS_CONT_LOCK); + + switch (cmd) { + case ONENAND_CMD_UNLOCK: + if (block_lock_scheme) + ONENAND_SET_WP_STATUS(ONENAND_WP_US, this); + else + ONENAND_SET_WP_STATUS(status | ONENAND_WP_US, this); + break; + + case ONENAND_CMD_LOCK: + if (block_lock_scheme) + ONENAND_SET_WP_STATUS(ONENAND_WP_LS, this); + else + ONENAND_SET_WP_STATUS(status | ONENAND_WP_LS, this); + break; + + case ONENAND_CMD_LOCK_TIGHT: + if (block_lock_scheme) + ONENAND_SET_WP_STATUS(ONENAND_WP_LTS, this); + else + ONENAND_SET_WP_STATUS(status | ONENAND_WP_LTS, this); + break; + + default: + break; + } +} + +/** + * onenand_bootram_handle - Handle BootRAM area + * @param this OneNAND device structure + * @param cmd The command to be sent + * + * Emulate BootRAM area. It is possible to do basic operation using BootRAM. + */ +static void onenand_bootram_handle(struct onenand_chip *this, int cmd) +{ + switch (cmd) { + case ONENAND_CMD_READID: + writew(manuf_id, this->base); + writew(device_id, this->base + 2); + writew(version_id, this->base + 4); + break; + + default: + /* REVIST: Handle other commands */ + break; + } +} + +/** + * onenand_update_interrupt - Set interrupt register + * @param this OneNAND device structure + * @param cmd The command to be sent + * + * Update interrupt register. The status is depends on command. + */ +static void onenand_update_interrupt(struct onenand_chip *this, int cmd) +{ + int interrupt = ONENAND_INT_MASTER; + + switch (cmd) { + case ONENAND_CMD_READ: + case ONENAND_CMD_READOOB: + interrupt |= ONENAND_INT_READ; + break; + + case ONENAND_CMD_PROG: + case ONENAND_CMD_PROGOOB: + interrupt |= ONENAND_INT_WRITE; + break; + + case ONENAND_CMD_ERASE: + interrupt |= ONENAND_INT_ERASE; + break; + + case ONENAND_CMD_RESET: + interrupt |= ONENAND_INT_RESET; + break; + + default: + break; + } + + writew(interrupt, this->base + ONENAND_REG_INTERRUPT); +} + +/** + * onenand_check_overwrite - Check over-write if happend + * @param dest The destination pointer + * @param src The source pointer + * @param count The length to be check + * @return 0 on same, otherwise 1 + * + * Compare the source with destination + */ +static int onenand_check_overwrite(void *dest, void *src, size_t count) +{ + unsigned int *s = (unsigned int *) src; + unsigned int *d = (unsigned int *) dest; + int i; + + count >>= 2; + for (i = 0; i < count; i++) + if ((*s++ ^ *d++) != 0) + return 1; + + return 0; +} + +/** + * onenand_data_handle - Handle OneNAND Core and DataRAM + * @param this OneNAND device structure + * @param cmd The command to be sent + * @param dataram Which dataram used + * @param offset The offset to OneNAND Core + * + * Copy data from OneNAND Core to DataRAM (read) + * Copy data from DataRAM to OneNAND Core (write) + * Erase the OneNAND Core (erase) + */ +static void onenand_data_handle(struct onenand_chip *this, int cmd, + int dataram, unsigned int offset) +{ + struct mtd_info *mtd = &info->mtd; + struct onenand_flash *flash = this->priv; + int main_offset, spare_offset; + void __iomem *src; + void __iomem *dest; + unsigned int i; + + if (dataram) { + main_offset = mtd->writesize; + spare_offset = mtd->oobsize; + } else { + main_offset = 0; + spare_offset = 0; + } + + switch (cmd) { + case ONENAND_CMD_READ: + src = ONENAND_CORE(flash) + offset; + dest = ONENAND_MAIN_AREA(this, main_offset); + memcpy(dest, src, mtd->writesize); + /* Fall through */ + + case ONENAND_CMD_READOOB: + src = ONENAND_CORE_SPARE(flash, this, offset); + dest = ONENAND_SPARE_AREA(this, spare_offset); + memcpy(dest, src, mtd->oobsize); + break; + + case ONENAND_CMD_PROG: + src = ONENAND_MAIN_AREA(this, main_offset); + dest = ONENAND_CORE(flash) + offset; + /* To handle partial write */ + for (i = 0; i < (1 << mtd->subpage_sft); i++) { + int off = i * this->subpagesize; + if (!memcmp(src + off, ffchars, this->subpagesize)) + continue; + if (memcmp(dest + off, ffchars, this->subpagesize) && + onenand_check_overwrite(dest + off, src + off, this->subpagesize)) + printk(KERN_ERR "over-write happend at 0x%08x\n", offset); + memcpy(dest + off, src + off, this->subpagesize); + } + /* Fall through */ + + case ONENAND_CMD_PROGOOB: + src = ONENAND_SPARE_AREA(this, spare_offset); + /* Check all data is 0xff chars */ + if (!memcmp(src, ffchars, mtd->oobsize)) + break; + + dest = ONENAND_CORE_SPARE(flash, this, offset); + if (memcmp(dest, ffchars, mtd->oobsize) && + onenand_check_overwrite(dest, src, mtd->oobsize)) + printk(KERN_ERR "OOB: over-write happend at 0x%08x\n", + offset); + memcpy(dest, src, mtd->oobsize); + break; + + case ONENAND_CMD_ERASE: + memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize); + memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff, + (mtd->erasesize >> 5)); + break; + + default: + break; + } +} + +/** + * onenand_command_handle - Handle command + * @param this OneNAND device structure + * @param cmd The command to be sent + * + * Emulate OneNAND command. + */ +static void onenand_command_handle(struct onenand_chip *this, int cmd) +{ + unsigned long offset = 0; + int block = -1, page = -1, bufferram = -1; + int dataram = 0; + + switch (cmd) { + case ONENAND_CMD_UNLOCK: + case ONENAND_CMD_LOCK: + case ONENAND_CMD_LOCK_TIGHT: + case ONENAND_CMD_UNLOCK_ALL: + onenand_lock_handle(this, cmd); + break; + + case ONENAND_CMD_BUFFERRAM: + /* Do nothing */ + return; + + default: + block = (int) readw(this->base + ONENAND_REG_START_ADDRESS1); + if (block & (1 << ONENAND_DDP_SHIFT)) { + block &= ~(1 << ONENAND_DDP_SHIFT); + /* The half of chip block */ + block += this->chipsize >> (this->erase_shift + 1); + } + if (cmd == ONENAND_CMD_ERASE) + break; + + page = (int) readw(this->base + ONENAND_REG_START_ADDRESS8); + page = (page >> ONENAND_FPA_SHIFT); + bufferram = (int) readw(this->base + ONENAND_REG_START_BUFFER); + bufferram >>= ONENAND_BSA_SHIFT; + bufferram &= ONENAND_BSA_DATARAM1; + dataram = (bufferram == ONENAND_BSA_DATARAM1) ? 1 : 0; + break; + } + + if (block != -1) + offset += block << this->erase_shift; + + if (page != -1) + offset += page << this->page_shift; + + onenand_data_handle(this, cmd, dataram, offset); + + onenand_update_interrupt(this, cmd); +} + +/** + * onenand_writew - [OneNAND Interface] Emulate write operation + * @param value value to write + * @param addr address to write + * + * Write OneNAND register with value + */ +static void onenand_writew(unsigned short value, void __iomem * addr) +{ + struct onenand_chip *this = info->mtd.priv; + + /* BootRAM handling */ + if (addr < this->base + ONENAND_DATARAM) { + onenand_bootram_handle(this, value); + return; + } + /* Command handling */ + if (addr == this->base + ONENAND_REG_COMMAND) + onenand_command_handle(this, value); + + writew(value, addr); +} + +/** + * flash_init - Initialize OneNAND simulator + * @param flash OneNAND simulaotr data strucutres + * + * Initialize OneNAND simulator. + */ +static int __init flash_init(struct onenand_flash *flash) +{ + int density, size; + int buffer_size; + + flash->base = kzalloc(SZ_128K, GFP_KERNEL); + if (!flash->base) { + printk(KERN_ERR "Unalbe to allocate base address.\n"); + return -ENOMEM; + } + + density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT; + size = ((16 << 20) << density); + + ONENAND_CORE(flash) = vmalloc(size + (size >> 5)); + if (!ONENAND_CORE(flash)) { + printk(KERN_ERR "Unalbe to allocate nand core address.\n"); + kfree(flash->base); + return -ENOMEM; + } + + memset(ONENAND_CORE(flash), 0xff, size + (size >> 5)); + + /* Setup registers */ + writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID); + writew(device_id, flash->base + ONENAND_REG_DEVICE_ID); + writew(version_id, flash->base + ONENAND_REG_VERSION_ID); + + if (density < 2) + buffer_size = 0x0400; /* 1KB page */ + else + buffer_size = 0x0800; /* 2KB page */ + writew(buffer_size, flash->base + ONENAND_REG_DATA_BUFFER_SIZE); + + return 0; +} + +/** + * flash_exit - Clean up OneNAND simulator + * @param flash OneNAND simulaotr data strucutres + * + * Clean up OneNAND simulator. + */ +static void flash_exit(struct onenand_flash *flash) +{ + vfree(ONENAND_CORE(flash)); + kfree(flash->base); + kfree(flash); +} + +static int __init onenand_sim_init(void) +{ + /* Allocate all 0xff chars pointer */ + ffchars = kmalloc(MAX_ONENAND_PAGESIZE, GFP_KERNEL); + if (!ffchars) { + printk(KERN_ERR "Unable to allocate ff chars.\n"); + return -ENOMEM; + } + memset(ffchars, 0xff, MAX_ONENAND_PAGESIZE); + + /* Allocate OneNAND simulator mtd pointer */ + info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL); + if (!info) { + printk(KERN_ERR "Unable to allocate core structures.\n"); + kfree(ffchars); + return -ENOMEM; + } + + /* Override write_word function */ + info->onenand.write_word = onenand_writew; + + if (flash_init(&info->flash)) { + printk(KERN_ERR "Unable to allocat flash.\n"); + kfree(ffchars); + kfree(info); + return -ENOMEM; + } + + info->parts = os_partitions; + + info->onenand.base = info->flash.base; + info->onenand.priv = &info->flash; + + info->mtd.name = "OneNAND simulator"; + info->mtd.priv = &info->onenand; + info->mtd.owner = THIS_MODULE; + + if (onenand_scan(&info->mtd, 1)) { + flash_exit(&info->flash); + kfree(ffchars); + kfree(info); + return -ENXIO; + } + + add_mtd_partitions(&info->mtd, info->parts, ARRAY_SIZE(os_partitions)); + + return 0; +} + +static void __exit onenand_sim_exit(void) +{ + struct onenand_chip *this = info->mtd.priv; + struct onenand_flash *flash = this->priv; + + onenand_release(&info->mtd); + flash_exit(flash); + kfree(ffchars); + kfree(info); +} + +module_init(onenand_sim_init); +module_exit(onenand_sim_exit); + +MODULE_AUTHOR("Kyungmin Park "); +MODULE_DESCRIPTION("The OneNAND flash simulator"); +MODULE_LICENSE("GPL"); -- cgit v1.2.2 From 8f1a866fc6831f13593fae6194e3150d45976628 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Thu, 5 Jul 2007 02:18:34 +0200 Subject: [MTD] [CHIPS] fix tiny spelling error in comment in cfi_cmdset_0001.c Trivial fix of a spelling error in a comment in cfi_cmdset_0001.c s/ships/chips/ Signed-off-by: Jesper Juhl Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index f838be2259..39eff9ff57 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -526,7 +526,7 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, struct cfi_pri_intelext *extp = cfi->cmdset_priv; /* - * Probing of multi-partition flash ships. + * Probing of multi-partition flash chips. * * To support multiple partitions when available, we simply arrange * for each of them to have their own flchip structure even if they -- cgit v1.2.2 From c9856e39e0c3b4d260e4614b66b0a7e0e79ec0ac Mon Sep 17 00:00:00 2001 From: Philippe De Muyter Date: Thu, 5 Jul 2007 17:05:47 +0200 Subject: [MTD] [NOR] add FUJITSU MBM29F800BA and ST M29F800AB descriptions Add descriptions for Fujitsu MBM29F800BA and ST M29F800AB flash chips. Those chips are compatible (except for the ids) with the AMD AM29F800BB. Signed-off-by: Philippe De Muyter Signed-off-by: David Woodhouse --- drivers/mtd/chips/jedec_probe.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 58e561e876..593e9d6ebe 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -70,6 +70,7 @@ /* Fujitsu */ #define MBM29F040C 0x00A4 +#define MBM29F800BA 0x2258 #define MBM29LV650UE 0x22D7 #define MBM29LV320TE 0x22F6 #define MBM29LV320BE 0x22F9 @@ -129,6 +130,7 @@ #define LH28F640BF 0x00b0 /* ST - www.st.com */ +#define M29F800AB 0x0058 #define M29W800DT 0x00D7 #define M29W800DB 0x005B #define M29W160DT 0x22C4 @@ -644,6 +646,23 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x10000,8) } + }, { + .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29F800BA, + .name = "Fujitsu MBM29F800BA", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,15), + } }, { .mfr_id = MANUFACTURER_FUJITSU, .dev_id = MBM29LV650UE, @@ -1510,6 +1529,23 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,256) } + }, { + .mfr_id = MANUFACTURER_ST, + .dev_id = M29F800AB, + .name = "ST M29F800AB", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,15), + } }, { .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ .dev_id = M29W800DT, -- cgit v1.2.2 From 16adce7b6f4dab015d0b93274b41f8aae6fe07a5 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 10 Jul 2007 09:08:26 +0100 Subject: [MTD] [ONENAND] Make onenand_sim compile on non-ARM platforms. The whole point of a sim is that it should run almost anywhere. Gratuitously depending on '#define SZ_128K 131072' from an ARM-specific header isn't really a good idea. Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_sim.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/onenand_sim.c b/drivers/mtd/onenand/onenand_sim.c index dfa39dfcc1..b80667309b 100644 --- a/drivers/mtd/onenand/onenand_sim.c +++ b/drivers/mtd/onenand/onenand_sim.c @@ -3,7 +3,7 @@ * * The OneNAND simulator * - * Copyright(C) 2005-2007 Samsung Electronics + * Copyright © 2005-2007 Samsung Electronics * Kyungmin Park * * This program is free software; you can redistribute it and/or modify @@ -19,8 +19,7 @@ #include #include -#include -#include +#include #ifndef CONFIG_ONENAND_SIM_MANUFACTURER #define CONFIG_ONENAND_SIM_MANUFACTURER 0xec @@ -83,7 +82,8 @@ struct onenand_info *info; #define DPRINTK(format, args...) \ do { \ - printk("%s[%d]: " format "\n", __func__, __LINE__, ##args); \ + printk(KERN_DEBUG "%s[%d]: " format "\n", __func__, \ + __LINE__, ##args); \ } while (0) /** @@ -382,9 +382,9 @@ static int __init flash_init(struct onenand_flash *flash) int density, size; int buffer_size; - flash->base = kzalloc(SZ_128K, GFP_KERNEL); + flash->base = kzalloc(131072, GFP_KERNEL); if (!flash->base) { - printk(KERN_ERR "Unalbe to allocate base address.\n"); + printk(KERN_ERR "Unable to allocate base address.\n"); return -ENOMEM; } @@ -393,7 +393,7 @@ static int __init flash_init(struct onenand_flash *flash) ONENAND_CORE(flash) = vmalloc(size + (size >> 5)); if (!ONENAND_CORE(flash)) { - printk(KERN_ERR "Unalbe to allocate nand core address.\n"); + printk(KERN_ERR "Unable to allocate nand core address.\n"); kfree(flash->base); return -ENOMEM; } @@ -406,9 +406,9 @@ static int __init flash_init(struct onenand_flash *flash) writew(version_id, flash->base + ONENAND_REG_VERSION_ID); if (density < 2) - buffer_size = 0x0400; /* 1KB page */ + buffer_size = 0x0400; /* 1KiB page */ else - buffer_size = 0x0800; /* 2KB page */ + buffer_size = 0x0800; /* 2KiB page */ writew(buffer_size, flash->base + ONENAND_REG_DATA_BUFFER_SIZE); return 0; -- cgit v1.2.2 From a2e96b627b58efe2d618a415f34b9b9d9f829bd6 Mon Sep 17 00:00:00 2001 From: Yoichi Yuasa Date: Wed, 11 Jul 2007 11:36:56 +0900 Subject: [MTD] Remove Ocelot G support from DiskOnChip drivers. This patch has removed Ocelot G support from MTD. Ocelot G support has already removed since May 2007. Signed-off-by: Yoichi Yuasa Signed-off-by: David Woodhouse --- drivers/mtd/devices/docprobe.c | 2 -- drivers/mtd/nand/diskonchip.c | 2 -- 2 files changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c index b96ac8e119..d574916e78 100644 --- a/drivers/mtd/devices/docprobe.c +++ b/drivers/mtd/devices/docprobe.c @@ -84,8 +84,6 @@ static unsigned long __initdata doc_locations[] = { #elif defined(CONFIG_MOMENCO_OCELOT) 0x2f000000, 0xff000000, -#elif defined(CONFIG_MOMENCO_OCELOT_G) - 0xff000000, ##else #warning Unknown architecture for DiskOnChip. No default probe locations defined #endif diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 17c868034a..5f3049a424 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -59,8 +59,6 @@ static unsigned long __initdata doc_locations[] = { #elif defined(CONFIG_MOMENCO_OCELOT) 0x2f000000, 0xff000000, -#elif defined(CONFIG_MOMENCO_OCELOT_G) - 0xff000000, #else #warning Unknown architecture for DiskOnChip. No default probe locations defined #endif -- cgit v1.2.2 From 8691a729a2a3d739ee40a577053157393450aabd Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Tue, 10 Jul 2007 20:33:54 +0100 Subject: [MTD] Add sync/unblank function to mtdoops mtdoops wasn't ensuring data was flushed to flash in crash situations after recent changes in mainline kernels as tracking the oops_in_progress variable was no longer enough. We can use the "unblank" console call as a sync call to tell us to write out the buffer though. Therefore add a sync function to mtdoops and call this when console unblank events occur. Signed-off-by: Richard Purdie Signed-off-by: David Woodhouse --- drivers/mtd/mtdoops.c | 57 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index cfc28ab4a3..62ee2043d0 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -250,40 +250,50 @@ static void mtdoops_notify_remove(struct mtd_info *mtd) flush_scheduled_work(); } - -static void -mtdoops_console_write(struct console *co, const char *s, unsigned int count) +static void mtdoops_console_sync(void) { - struct mtdoops_context *cxt = co->data; + struct mtdoops_context *cxt = &oops_cxt; struct mtd_info *mtd = cxt->mtd; - int i, ret; + size_t retlen; + int ret; if (!cxt->ready || !mtd) return; - if (!oops_in_progress && cxt->writecount != 0) { - size_t retlen; - if (cxt->writecount < OOPS_PAGE_SIZE) - memset(cxt->oops_buf + cxt->writecount, 0xff, + if (cxt->writecount == 0) + return; + + if (cxt->writecount < OOPS_PAGE_SIZE) + memset(cxt->oops_buf + cxt->writecount, 0xff, OOPS_PAGE_SIZE - cxt->writecount); - ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, + ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, OOPS_PAGE_SIZE, &retlen, cxt->oops_buf); - cxt->ready = 0; - cxt->writecount = 0; - - if ((retlen != OOPS_PAGE_SIZE) || (ret < 0)) - printk(KERN_ERR "mtdoops: Write failure at %d (%d of %d" - " written), err %d.\n", - cxt->nextpage * OOPS_PAGE_SIZE, retlen, - OOPS_PAGE_SIZE, ret); - - ret = mtdoops_inc_counter(cxt); - if (ret == 1) - schedule_work(&cxt->work); + cxt->ready = 0; + cxt->writecount = 0; + + if ((retlen != OOPS_PAGE_SIZE) || (ret < 0)) + printk(KERN_ERR "mtdoops: Write failure at %d (%d of %d written), err %d.\n", + cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); + + ret = mtdoops_inc_counter(cxt); + if (ret == 1) + schedule_work(&cxt->work); +} + +static void +mtdoops_console_write(struct console *co, const char *s, unsigned int count) +{ + struct mtdoops_context *cxt = co->data; + struct mtd_info *mtd = cxt->mtd; + int i; + + if (!oops_in_progress) { + mtdoops_console_sync(); + return; } - if (!oops_in_progress) + if (!cxt->ready || !mtd) return; if (cxt->writecount == 0) { @@ -323,6 +333,7 @@ static struct console mtdoops_console = { .name = "ttyMTD", .write = mtdoops_console_write, .setup = mtdoops_console_setup, + .unblank = mtdoops_console_sync, .flags = CON_PRINTBUFFER, .index = -1, .data = &oops_cxt, -- cgit v1.2.2 From 30eb0db07d67b9211da7f506220184df827e425d Mon Sep 17 00:00:00 2001 From: "Steven J. Hill" Date: Wed, 18 Jul 2007 23:29:46 -0500 Subject: [MTD] [NAND] Add NAND manufacturer AMD. This patch adds the manufacturer ID for AMD flash. Signed-off-by: Steven J. Hill Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_ids.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 2fc674a190..a3e3ab0185 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -141,6 +141,7 @@ struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_STMICRO, "ST Micro"}, {NAND_MFR_HYNIX, "Hynix"}, {NAND_MFR_MICRON, "Micron"}, + {NAND_MFR_AMD, "AMD"}, {0x0, "Unknown"} }; -- cgit v1.2.2 From 4cfff0db3a60a775c29abd9cbc300fc0f616904b Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sat, 21 Jul 2007 17:02:36 +0200 Subject: [MTD] Clean up duplicate includes in drivers/mtd/ This patch cleans up duplicate includes in drivers/mtd/ Signed-off-by: Jesper Juhl Signed-off-by: David Woodhouse --- drivers/mtd/chips/jedec_probe.c | 1 - drivers/mtd/nand/excite_nandflash.c | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 593e9d6ebe..a67b23b87f 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c index 7e9afc4c77..bed87290de 100644 --- a/drivers/mtd/nand/excite_nandflash.c +++ b/drivers/mtd/nand/excite_nandflash.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include -- cgit v1.2.2 From 1050643431c74d29ac972141cb24df1d9aca65cd Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Thu, 19 Jul 2007 16:45:18 -0400 Subject: [MTD] Fix potential leak in rfd_ftl_add_mtd This fixes a leak in the !mtd->erasesize error path (Coverity 1765). Signed-off-by: Florin Malita Signed-off-by: David Woodhouse --- drivers/mtd/rfd_ftl.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index d4b1ba8f23..006c03aacb 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -779,6 +779,7 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) else { if (!mtd->erasesize) { printk(KERN_WARNING PREFIX "please provide block_size"); + kfree(part); return; } else -- cgit v1.2.2 From 030f9e13bec9aaae1def86c23963a1a825ccdab5 Mon Sep 17 00:00:00 2001 From: "akpm@linux-foundation.org" Date: Fri, 20 Jul 2007 11:56:19 -0700 Subject: [MTD] Remove embedded return in RFD FTL. embedded returns are evil. Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/rfd_ftl.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index 006c03aacb..823fba4e6d 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -779,10 +779,8 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) else { if (!mtd->erasesize) { printk(KERN_WARNING PREFIX "please provide block_size"); - kfree(part); - return; - } - else + goto out; + } else part->block_size = mtd->erasesize; } @@ -804,7 +802,7 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) if (!add_mtd_blktrans_dev((void*)part)) return; } - +out: kfree(part); } -- cgit v1.2.2 From 4a32cfa0f5c3f9e8a0da610002e4555173226650 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Wed, 18 Jul 2007 14:56:11 +0200 Subject: [MTD] [NAND] Fix refactoring of EDB7312 hwcontrol function. The patch ensures that the current code (kernel 2.6.22) uses the bits like the code prior to the refactoring. The variable "bits" is employed in a useful way now. Signed-off-by: Roland Stigge Signed-off-by: David Woodhouse --- drivers/mtd/nand/edb7312.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c index 1daf8231aa..0146cdc480 100644 --- a/drivers/mtd/nand/edb7312.c +++ b/drivers/mtd/nand/edb7312.c @@ -74,7 +74,7 @@ static struct mtd_partition partition_info[] = { /* * hardware specific access to control-lines * - * NAND_NCE: bit 0 -> bit 7 + * NAND_NCE: bit 0 -> bit 6 (bit 7 = 1) * NAND_CLE: bit 1 -> bit 4 * NAND_ALE: bit 2 -> bit 5 */ @@ -83,12 +83,12 @@ static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) struct nand_chip *chip = mtd->priv; if (ctrl & NAND_CTRL_CHANGE) { - unsigned char bits; + unsigned char bits = 0x80; - bits = (ctrl & (NAND_CLE | NAND_ALE)) << 3; - bits = (ctrl & NAND_NCE) << 7; + bits |= (ctrl & (NAND_CLE | NAND_ALE)) << 3; + bits |= (ctrl & NAND_NCE) ? 0x00 : 0x40; - clps_writeb((clps_readb(ep7312_pxdr) & 0xB0) | 0x10, + clps_writeb((clps_readb(ep7312_pxdr) & 0xF0) | bits, ep7312_pxdr); } if (cmd != NAND_CMD_NONE) -- cgit v1.2.2 From e733450b675dfc07940c21b4832207c79059246f Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Wed, 11 Jul 2007 17:04:02 -0400 Subject: [MTD] Remove useless references to MIPS_HYDROGEN3 and MIPS_MIRAGE_WHY. Signed-off-by: Robert P. J. Day Acked-by: Ralf Baechle Signed-off-by: David Woodhouse --- drivers/mtd/maps/alchemy-flash.c | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c index 84fbe0e8c4..82811bcb04 100644 --- a/drivers/mtd/maps/alchemy-flash.c +++ b/drivers/mtd/maps/alchemy-flash.c @@ -75,13 +75,6 @@ #define BOARD_FLASH_WIDTH 2 /* 16-bits */ #endif -#ifdef CONFIG_MIPS_HYDROGEN3 -#define BOARD_MAP_NAME "Hydrogen3 Flash" -#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#define USE_LOCAL_ACCESSORS /* why? */ -#endif - #ifdef CONFIG_MIPS_BOSPORUS #define BOARD_MAP_NAME "Bosporus Flash" #define BOARD_FLASH_SIZE 0x01000000 /* 16MB */ @@ -130,13 +123,6 @@ int __init alchemy_mtd_init(void) window_addr = 0x20000000 - BOARD_FLASH_SIZE; window_size = BOARD_FLASH_SIZE; -#ifdef CONFIG_MIPS_MIRAGE_WHY - /* Boot ROM flash bank only; no user bank */ - window_addr = 0x1C000000; - window_size = 0x04000000; - /* USERFS from 0x1C00 0000 to 0x1FC00000 */ - alchemy_partitions[0].size = 0x03C00000; -#endif /* * Static partition definition selection -- cgit v1.2.2 From 0bf9733d0d65ebb413d62204ad8e328e0a0b9407 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 23 Jul 2007 13:07:06 +0100 Subject: [MTD] Fix do_div() type warning in mtdconcat It expects a uint64_t; give it one. Signed-off-by: David Woodhouse --- drivers/mtd/mtdconcat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 41844ea024..96be7ef62f 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -178,7 +178,7 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs, /* Check alignment */ if (mtd->writesize > 1) { - loff_t __to = to; + uint64_t __to = to; if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize)) return -EINVAL; } -- cgit v1.2.2 From c0b8ba7bfe7b4c1f11b3356b65520405b005bd33 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 23 Jul 2007 16:06:50 +0300 Subject: [MTD] [NAND] fix race in nand_base.c When we mark block bad we have to get chip because this involves writing to the page's OOB. We hit this bug in UBI - we observed random obscure crashes when it marks block bad from the background thread and there is some parallel task which utilizes flash. This patch also adds a TODO note about BBT table protection which it seems does not exist. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 25673eacdd..24ac6778b1 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -24,6 +24,7 @@ * if we have HW ecc support. * The AG-AND chips have nice features for speed improvement, * which are not supported yet. Read / program 4 pages in one go. + * BBT table is not serialized, has to be fixed * * 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 @@ -360,6 +361,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) /* We write two bytes, so we dont have to mess with 16 bit * access */ + nand_get_device(chip, mtd, FL_WRITING); ofs += mtd->oobsize; chip->ops.len = chip->ops.ooblen = 2; chip->ops.datbuf = NULL; @@ -367,9 +369,11 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) chip->ops.ooboffs = chip->badblockpos & ~0x01; ret = nand_do_write_oob(mtd, ofs, &chip->ops); + nand_release_device(mtd); } if (!ret) mtd->ecc_stats.badblocks++; + return ret; } -- cgit v1.2.2 From 8f0f23f97adbd833000e25e41c1310e49ace9ba8 Mon Sep 17 00:00:00 2001 From: Mariusz Kozlowski Date: Fri, 27 Jul 2007 17:29:32 +0200 Subject: [MTD] remove redundant/dead code from physmap_of.c This patch removes redundant memset() and dead return line from of_physmap_probe(). No functional change. Signed-off-by: Mariusz Kozlowski Signed-off-by: David Woodhouse --- drivers/mtd/maps/physmap_of.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index bbb42c35b6..fbd6139687 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -141,7 +141,6 @@ static int __devinit of_physmap_probe(struct of_device *dev, const struct of_dev err = -ENOMEM; goto err_out; } - memset(info, 0, sizeof(*info)); dev_set_drvdata(&dev->dev, info); @@ -213,10 +212,6 @@ static int __devinit of_physmap_probe(struct of_device *dev, const struct of_dev err_out: of_physmap_remove(dev); return err; - - return 0; - - } static struct of_device_id of_physmap_match[] = { -- cgit v1.2.2 From d67d1d7fc344e44b51296327a0b09a9419ca307f Mon Sep 17 00:00:00 2001 From: Mariusz Kozlowski Date: Tue, 31 Jul 2007 20:03:14 +0200 Subject: [MTD] drivers/mtd/inftlmount.c: kmalloc + memset conversion to kcalloc Signed-off-by: Mariusz Kozlowski Signed-off-by: David Woodhouse --- drivers/mtd/inftlmount.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index ecac0e438f..b8917beeb6 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -580,14 +580,13 @@ int INFTL_mount(struct INFTLrecord *s) logical_block = block = BLOCK_NIL; /* Temporary buffer to store ANAC numbers. */ - ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL); + ANACtable = kcalloc(s->nb_blocks, sizeof(u8), GFP_KERNEL); if (!ANACtable) { printk(KERN_WARNING "INFTL: allocation of ANACtable " "failed (%zd bytes)\n", s->nb_blocks * sizeof(u8)); return -ENOMEM; } - memset(ANACtable, 0, s->nb_blocks); /* * First pass is to explore each physical unit, and construct the -- cgit v1.2.2 From 4fb4caa639a27a3cb0b3bfbf2041524af43efff9 Mon Sep 17 00:00:00 2001 From: Mariusz Kozlowski Date: Tue, 31 Jul 2007 23:49:06 +0200 Subject: [MTD] pmcmsp-flash.c: kmalloc + memset conversion to k[cz]alloc Signed-off-by: Mariusz Kozlowski Signed-off-by: David Woodhouse --- drivers/mtd/maps/pmcmsp-flash.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c index 7e0377ec1c..02bde8c982 100644 --- a/drivers/mtd/maps/pmcmsp-flash.c +++ b/drivers/mtd/maps/pmcmsp-flash.c @@ -73,13 +73,16 @@ int __init init_msp_flash(void) return -ENXIO; printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt); - msp_flash = (struct mtd_info **)kmalloc( - fcnt * sizeof(struct map_info *), GFP_KERNEL); - msp_parts = (struct mtd_partition **)kmalloc( - fcnt * sizeof(struct mtd_partition *), GFP_KERNEL); - msp_maps = (struct map_info *)kmalloc( - fcnt * sizeof(struct mtd_info), GFP_KERNEL); - memset(msp_maps, 0, fcnt * sizeof(struct mtd_info)); + + msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL); + msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL); + msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL); + if (!msp_flash || !msp_parts || !msp_maps) { + kfree(msp_maps); + kfree(msp_parts); + kfree(msp_flash); + return -ENOMEM; + } /* loop over the flash devices, initializing each */ for (i = 0; i < fcnt; i++) { @@ -95,9 +98,8 @@ int __init init_msp_flash(void) continue; } - msp_parts[i] = (struct mtd_partition *)kmalloc( - pcnt * sizeof(struct mtd_partition), GFP_KERNEL); - memset(msp_parts[i], 0, pcnt * sizeof(struct mtd_partition)); + msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition), + GFP_KERNEL); /* now initialize the devices proper */ flash_name[5] = '0' + i; -- cgit v1.2.2 From d9b0744d6c75f7b268b728cdd1843f31bdeba311 Mon Sep 17 00:00:00 2001 From: Mariusz Kozlowski Date: Wed, 1 Aug 2007 00:02:10 +0200 Subject: [UBI] drivers/mtd/ubi/scan.c: kmalloc + memset conversion to kzalloc To be able to convert kmalloc + memset(..., 1, ...) to kzalloc this patch reverses the logic around 'buf'. Signed-off-by: Mariusz Kozlowski Signed-off-by: David Woodhouse --- drivers/mtd/ubi/scan.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 94ee549344..29c41eeb09 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -1314,11 +1314,10 @@ static int paranoid_check_si(const struct ubi_device *ubi, * Make sure that all the physical eraseblocks are in one of the lists * or trees. */ - buf = kmalloc(ubi->peb_count, GFP_KERNEL); + buf = kzalloc(ubi->peb_count, GFP_KERNEL); if (!buf) return -ENOMEM; - memset(buf, 1, ubi->peb_count); for (pnum = 0; pnum < ubi->peb_count; pnum++) { err = ubi_io_is_bad(ubi, pnum); if (err < 0) { @@ -1326,28 +1325,28 @@ static int paranoid_check_si(const struct ubi_device *ubi, return err; } else if (err) - buf[pnum] = 0; + buf[pnum] = 1; } ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) - buf[seb->pnum] = 0; + buf[seb->pnum] = 1; list_for_each_entry(seb, &si->free, u.list) - buf[seb->pnum] = 0; + buf[seb->pnum] = 1; list_for_each_entry(seb, &si->corr, u.list) - buf[seb->pnum] = 0; + buf[seb->pnum] = 1; list_for_each_entry(seb, &si->erase, u.list) - buf[seb->pnum] = 0; + buf[seb->pnum] = 1; list_for_each_entry(seb, &si->alien, u.list) - buf[seb->pnum] = 0; + buf[seb->pnum] = 1; err = 0; for (pnum = 0; pnum < ubi->peb_count; pnum++) - if (buf[pnum]) { + if (!buf[pnum]) { ubi_err("PEB %d is not referred", pnum); err = 1; } -- cgit v1.2.2 From 73ff007537e591e53d070aafbfd90369c3580332 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sun, 29 Jul 2007 16:58:12 +0200 Subject: [MTD] [ONENAND] onenand_sim.c: make struct info static This patch makes the needlessly global struct info static. Signed-off-by: Adrian Bunk Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_sim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/onenand_sim.c b/drivers/mtd/onenand/onenand_sim.c index b80667309b..0d89ad5776 100644 --- a/drivers/mtd/onenand/onenand_sim.c +++ b/drivers/mtd/onenand/onenand_sim.c @@ -78,7 +78,7 @@ struct onenand_info { struct onenand_flash flash; }; -struct onenand_info *info; +static struct onenand_info *info; #define DPRINTK(format, args...) \ do { \ -- cgit v1.2.2 From 8b2b403ce0f1a816b7a6a4f47c8798003b26c07a Mon Sep 17 00:00:00 2001 From: maximilian attems Date: Sat, 28 Jul 2007 13:07:16 +0200 Subject: [MTD] [NAND] nand_base.c fix broken link replace with working link from nand Kconfig help text fixes bugzilla 7815 Signed-off-by: maximilian attems Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 24ac6778b1..d569121205 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -7,7 +7,7 @@ * Basic support for AG-AND chips is provided. * * Additional technical information is available on - * http://www.linux-mtd.infradead.org/tech/nand.html + * http://www.linux-mtd.infradead.org/doc/nand.html * * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * 2002-2006 Thomas Gleixner (tglx@linutronix.de) -- cgit v1.2.2 From 471f717a48d25afcb9428c9523cd0557738b7115 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 3 Aug 2007 23:02:02 +0100 Subject: Revert "[MTD] Driver for AT26Fxxx dataflash devices" This reverts commit 340ea370c2ce89d1c15fbf785460f2f74314ce58. It's not needed given the other m25p80 patch (which now handles at26 "dataflash" as well as most other standard SPI flash chips), and requires a controller driver that won't be merged upstream (supplanted by drivers/spi/atmel_spi.c) ... the submitter of that at91_dataflash26.c driver concurred. Requested by David Brownell Signed-off-by: David Woodhouse --- drivers/mtd/devices/Kconfig | 8 - drivers/mtd/devices/Makefile | 1 - drivers/mtd/devices/at91_dataflash26.c | 485 --------------------------------- 3 files changed, 494 deletions(-) delete mode 100644 drivers/mtd/devices/at91_dataflash26.c (limited to 'drivers') diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index b4ea64dc93..811d56fd89 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -60,14 +60,6 @@ config MTD_DATAFLASH Sometimes DataFlash chips are packaged inside MMC-format cards; at this writing, the MMC stack won't handle those. -config MTD_DATAFLASH26 - tristate "AT91RM9200 DataFlash AT26xxx" - depends on MTD && ARCH_AT91RM9200 && AT91_SPI - help - This enables access to the DataFlash chip (AT26xxx) on an - AT91RM9200-based board. - If you have such a board and such a DataFlash, say 'Y'. - config MTD_M25P80 tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" depends on SPI_MASTER && EXPERIMENTAL diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 8ab568b3f5..0f788d5c4b 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -16,5 +16,4 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o -obj-$(CONFIG_MTD_DATAFLASH26) += at91_dataflash26.o obj-$(CONFIG_MTD_M25P80) += m25p80.o diff --git a/drivers/mtd/devices/at91_dataflash26.c b/drivers/mtd/devices/at91_dataflash26.c deleted file mode 100644 index 64ce37f986..0000000000 --- a/drivers/mtd/devices/at91_dataflash26.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder) - * This is a largely modified version of at91_dataflash.c that - * supports AT26xxx dataflash chips. The original driver supports - * AT45xxx chips. - * - * Note: This driver was only tested with an AT26F004. It should be - * easy to make it work with other AT26xxx dataflash devices, though. - * - * Copyright (C) 2007 Hans J. Koch - * original Copyright (C) SAN People (Pty) Ltd - * - * 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. -*/ - -#include -#include -#include -#include - -#include - -#define DATAFLASH_MAX_DEVICES 4 /* max number of dataflash devices */ - -#define MANUFACTURER_ID_ATMEL 0x1F - -/* command codes */ - -#define AT26_OP_READ_STATUS 0x05 -#define AT26_OP_READ_DEV_ID 0x9F -#define AT26_OP_ERASE_PAGE_4K 0x20 -#define AT26_OP_READ_ARRAY_FAST 0x0B -#define AT26_OP_SEQUENTIAL_WRITE 0xAF -#define AT26_OP_WRITE_ENABLE 0x06 -#define AT26_OP_WRITE_DISABLE 0x04 -#define AT26_OP_SECTOR_PROTECT 0x36 -#define AT26_OP_SECTOR_UNPROTECT 0x39 - -/* status register bits */ - -#define AT26_STATUS_BUSY 0x01 -#define AT26_STATUS_WRITE_ENABLE 0x02 - -struct dataflash_local -{ - int spi; /* SPI chip-select number */ - unsigned int page_size; /* number of bytes per page */ -}; - - -/* Detected DataFlash devices */ -static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES]; -static int nr_devices = 0; - -/* Allocate a single SPI transfer descriptor. We're assuming that if multiple - SPI transfers occur at the same time, spi_access_bus() will serialize them. - If this is not valid, then either (i) each dataflash 'priv' structure - needs it's own transfer descriptor, (ii) we lock this one, or (iii) use - another mechanism. */ -static struct spi_transfer_list* spi_transfer_desc; - -/* - * Perform a SPI transfer to access the DataFlash device. - */ -static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len, - char* txnext, int txnext_len, char* rxnext, int rxnext_len) -{ - struct spi_transfer_list* list = spi_transfer_desc; - - list->tx[0] = tx; list->txlen[0] = tx_len; - list->rx[0] = rx; list->rxlen[0] = rx_len; - - list->tx[1] = txnext; list->txlen[1] = txnext_len; - list->rx[1] = rxnext; list->rxlen[1] = rxnext_len; - - list->nr_transfers = nr; - /* Note: spi_transfer() always returns 0, there are no error checks */ - return spi_transfer(list); -} - -/* - * Return the status of the DataFlash device. - */ -static unsigned char at91_dataflash26_status(void) -{ - unsigned char command[2]; - - command[0] = AT26_OP_READ_STATUS; - command[1] = 0; - - do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0); - - return command[1]; -} - -/* - * Poll the DataFlash device until it is READY. - */ -static unsigned char at91_dataflash26_waitready(void) -{ - unsigned char status; - - while (1) { - status = at91_dataflash26_status(); - if (!(status & AT26_STATUS_BUSY)) - return status; - } -} - -/* - * Enable/disable write access - */ - static void at91_dataflash26_write_enable(int enable) -{ - unsigned char cmd[2]; - - DEBUG(MTD_DEBUG_LEVEL3, "write_enable: enable=%i\n", enable); - - if (enable) - cmd[0] = AT26_OP_WRITE_ENABLE; - else - cmd[0] = AT26_OP_WRITE_DISABLE; - cmd[1] = 0; - - do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0); -} - -/* - * Protect/unprotect sector - */ - static void at91_dataflash26_sector_protect(loff_t addr, int protect) -{ - unsigned char cmd[4]; - - DEBUG(MTD_DEBUG_LEVEL3, "sector_protect: addr=0x%06x prot=%d\n", - addr, protect); - - if (protect) - cmd[0] = AT26_OP_SECTOR_PROTECT; - else - cmd[0] = AT26_OP_SECTOR_UNPROTECT; - cmd[1] = (addr & 0x00FF0000) >> 16; - cmd[2] = (addr & 0x0000FF00) >> 8; - cmd[3] = (addr & 0x000000FF); - - do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0); -} - -/* - * Erase blocks of flash. - */ -static int at91_dataflash26_erase(struct mtd_info *mtd, - struct erase_info *instr) -{ - struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; - unsigned char cmd[4]; - - DEBUG(MTD_DEBUG_LEVEL1, "dataflash_erase: addr=0x%06x len=%i\n", - instr->addr, instr->len); - - /* Sanity checks */ - if (priv->page_size != 4096) - return -EINVAL; /* Can't handle other sizes at the moment */ - - if ( ((instr->len % mtd->erasesize) != 0) - || ((instr->len % priv->page_size) != 0) - || ((instr->addr % priv->page_size) != 0) - || ((instr->addr + instr->len) > mtd->size)) - return -EINVAL; - - spi_access_bus(priv->spi); - - while (instr->len > 0) { - at91_dataflash26_write_enable(1); - at91_dataflash26_sector_protect(instr->addr, 0); - at91_dataflash26_write_enable(1); - cmd[0] = AT26_OP_ERASE_PAGE_4K; - cmd[1] = (instr->addr & 0x00FF0000) >> 16; - cmd[2] = (instr->addr & 0x0000FF00) >> 8; - cmd[3] = (instr->addr & 0x000000FF); - - DEBUG(MTD_DEBUG_LEVEL3, "ERASE: (0x%02x) 0x%02x 0x%02x" - "0x%02x\n", - cmd[0], cmd[1], cmd[2], cmd[3]); - - do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0); - at91_dataflash26_waitready(); - - instr->addr += priv->page_size; /* next page */ - instr->len -= priv->page_size; - } - - at91_dataflash26_write_enable(0); - spi_release_bus(priv->spi); - - /* Inform MTD subsystem that erase is complete */ - instr->state = MTD_ERASE_DONE; - if (instr->callback) - instr->callback(instr); - - return 0; -} - -/* - * Read from the DataFlash device. - * from : Start offset in flash device - * len : Number of bytes to read - * retlen : Number of bytes actually read - * buf : Buffer that will receive data - */ -static int at91_dataflash26_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; - unsigned char cmd[5]; - - DEBUG(MTD_DEBUG_LEVEL1, "dataflash_read: %lli .. %lli\n", - from, from+len); - - *retlen = 0; - - /* Sanity checks */ - if (!len) - return 0; - if (from + len > mtd->size) - return -EINVAL; - - cmd[0] = AT26_OP_READ_ARRAY_FAST; - cmd[1] = (from & 0x00FF0000) >> 16; - cmd[2] = (from & 0x0000FF00) >> 8; - cmd[3] = (from & 0x000000FF); - /* cmd[4] is a "Don't care" byte */ - - DEBUG(MTD_DEBUG_LEVEL3, "READ: (0x%02x) 0x%02x 0x%02x 0x%02x\n", - cmd[0], cmd[1], cmd[2], cmd[3]); - - spi_access_bus(priv->spi); - do_spi_transfer(2, cmd, 5, cmd, 5, buf, len, buf, len); - spi_release_bus(priv->spi); - - *retlen = len; - return 0; -} - -/* - * Write to the DataFlash device. - * to : Start offset in flash device - * len : Number of bytes to write - * retlen : Number of bytes actually written - * buf : Buffer containing the data - */ -static int at91_dataflash26_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; - unsigned int addr, buf_index = 0; - int ret = -EIO, sector, last_sector; - unsigned char status, cmd[5]; - - DEBUG(MTD_DEBUG_LEVEL1, "dataflash_write: %lli .. %lli\n", to, to+len); - - *retlen = 0; - - /* Sanity checks */ - if (!len) - return 0; - if (to + len > mtd->size) - return -EINVAL; - - spi_access_bus(priv->spi); - - addr = to; - last_sector = -1; - - while (buf_index < len) { - sector = addr / priv->page_size; - /* Write first byte if a new sector begins */ - if (sector != last_sector) { - at91_dataflash26_write_enable(1); - at91_dataflash26_sector_protect(addr, 0); - at91_dataflash26_write_enable(1); - - /* Program first byte of a new sector */ - cmd[0] = AT26_OP_SEQUENTIAL_WRITE; - cmd[1] = (addr & 0x00FF0000) >> 16; - cmd[2] = (addr & 0x0000FF00) >> 8; - cmd[3] = (addr & 0x000000FF); - cmd[4] = buf[buf_index++]; - do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0); - status = at91_dataflash26_waitready(); - addr++; - /* On write errors, the chip resets the write enable - flag. This also happens after the last byte of a - sector is successfully programmed. */ - if ( ( !(status & AT26_STATUS_WRITE_ENABLE)) - && ((addr % priv->page_size) != 0) ) { - DEBUG(MTD_DEBUG_LEVEL1, - "write error1: addr=0x%06x, " - "status=0x%02x\n", addr, status); - goto write_err; - } - (*retlen)++; - last_sector = sector; - } - - /* Write subsequent bytes in the same sector */ - cmd[0] = AT26_OP_SEQUENTIAL_WRITE; - cmd[1] = buf[buf_index++]; - do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0); - status = at91_dataflash26_waitready(); - addr++; - - if ( ( !(status & AT26_STATUS_WRITE_ENABLE)) - && ((addr % priv->page_size) != 0) ) { - DEBUG(MTD_DEBUG_LEVEL1, "write error2: addr=0x%06x, " - "status=0x%02x\n", addr, status); - goto write_err; - } - - (*retlen)++; - } - - ret = 0; - at91_dataflash26_write_enable(0); -write_err: - spi_release_bus(priv->spi); - return ret; -} - -/* - * Initialize and register DataFlash device with MTD subsystem. - */ -static int __init add_dataflash(int channel, char *name, int nr_pages, - int pagesize) -{ - struct mtd_info *device; - struct dataflash_local *priv; - - if (nr_devices >= DATAFLASH_MAX_DEVICES) { - printk(KERN_ERR "at91_dataflash26: Too many devices " - "detected\n"); - return 0; - } - - device = kzalloc(sizeof(struct mtd_info) + strlen(name) + 8, - GFP_KERNEL); - if (!device) - return -ENOMEM; - - device->name = (char *)&device[1]; - sprintf(device->name, "%s.spi%d", name, channel); - device->size = nr_pages * pagesize; - device->erasesize = pagesize; - device->owner = THIS_MODULE; - device->type = MTD_DATAFLASH; - device->flags = MTD_CAP_NORFLASH; - device->erase = at91_dataflash26_erase; - device->read = at91_dataflash26_read; - device->write = at91_dataflash26_write; - - priv = (struct dataflash_local *)kzalloc(sizeof(struct dataflash_local), - GFP_KERNEL); - if (!priv) { - kfree(device); - return -ENOMEM; - } - - priv->spi = channel; - priv->page_size = pagesize; - device->priv = priv; - - mtd_devices[nr_devices] = device; - nr_devices++; - printk(KERN_INFO "at91_dataflash26: %s detected [spi%i] (%i bytes)\n", - name, channel, device->size); - - return add_mtd_device(device); -} - -/* - * Detect and initialize DataFlash device connected to specified SPI channel. - * - */ - -struct dataflash26_types { - unsigned char id0; - unsigned char id1; - char *name; - int pagesize; - int nr_pages; -}; - -struct dataflash26_types df26_types[] = { - { - .id0 = 0x04, - .id1 = 0x00, - .name = "AT26F004", - .pagesize = 4096, - .nr_pages = 128, - }, - { - .id0 = 0x45, - .id1 = 0x01, - .name = "AT26DF081A", /* Not tested ! */ - .pagesize = 4096, - .nr_pages = 256, - }, -}; - -static int __init at91_dataflash26_detect(int channel) -{ - unsigned char status, cmd[5]; - int i; - - spi_access_bus(channel); - status = at91_dataflash26_status(); - - if (status == 0 || status == 0xff) { - printk(KERN_ERR "at91_dataflash26_detect: status error %d\n", - status); - spi_release_bus(channel); - return -ENODEV; - } - - cmd[0] = AT26_OP_READ_DEV_ID; - do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0); - spi_release_bus(channel); - - if (cmd[1] != MANUFACTURER_ID_ATMEL) - return -ENODEV; - - for (i = 0; i < ARRAY_SIZE(df26_types); i++) { - if ( cmd[2] == df26_types[i].id0 - && cmd[3] == df26_types[i].id1) - return add_dataflash(channel, - df26_types[i].name, - df26_types[i].nr_pages, - df26_types[i].pagesize); - } - - printk(KERN_ERR "at91_dataflash26_detect: Unsupported device " - "(0x%02x/0x%02x)\n", cmd[2], cmd[3]); - return -ENODEV; -} - -static int __init at91_dataflash26_init(void) -{ - spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list), - GFP_KERNEL); - if (!spi_transfer_desc) - return -ENOMEM; - - /* DataFlash (SPI chip select 0) */ - at91_dataflash26_detect(0); - -#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD - /* DataFlash card (SPI chip select 3) */ - at91_dataflash26_detect(3); -#endif - return 0; -} - -static void __exit at91_dataflash26_exit(void) -{ - int i; - - for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) { - if (mtd_devices[i]) { - del_mtd_device(mtd_devices[i]); - kfree(mtd_devices[i]->priv); - kfree(mtd_devices[i]); - } - } - nr_devices = 0; - kfree(spi_transfer_desc); -} - -module_init(at91_dataflash26_init); -module_exit(at91_dataflash26_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Hans J. Koch"); -MODULE_DESCRIPTION("DataFlash AT26xxx driver for Atmel AT91RM9200"); -- cgit v1.2.2 From 68d09b1b6780415d82160f6b6d88e82bd724e691 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 10 Aug 2007 14:01:31 -0700 Subject: [MTD] mtdoops printk warning fixes drivers/mtd/mtdoops.c: In function 'mtdoops_inc_counter': drivers/mtd/mtdoops.c:109: warning: format '%d' expects type 'int', but argument 3 has type 'size_t' drivers/mtd/mtdoops.c: In function 'mtdoops_console_sync': drivers/mtd/mtdoops.c:277: warning: format '%d' expects type 'int', but argument 3 has type 'size_t' someone buy Dave an x86_64 box. Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/mtdoops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 62ee2043d0..f8af627f0b 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -104,7 +104,7 @@ static int mtdoops_inc_counter(struct mtdoops_context *cxt) ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4, &retlen, (u_char *) &count); if ((retlen != 4) || (ret < 0)) { - printk(KERN_ERR "mtdoops: Read failure at %d (%d of 4 read)" + printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)" ", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE, retlen, ret); return 1; @@ -273,7 +273,7 @@ static void mtdoops_console_sync(void) cxt->writecount = 0; if ((retlen != OOPS_PAGE_SIZE) || (ret < 0)) - printk(KERN_ERR "mtdoops: Write failure at %d (%d of %d written), err %d.\n", + printk(KERN_ERR "mtdoops: Write failure at %d (%td of %d written), err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); ret = mtdoops_inc_counter(cxt); -- cgit v1.2.2 From 3feb0ff1607ab3b7dcd36b2347f00c41774464c8 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Wed, 22 Aug 2007 14:30:47 -0500 Subject: [MTD] 4xx: Don't build arch/ppc dependent drivers in arch/powerpc These drivers are specific to 4xx support in arch/ppc at the moment. Make sure they don't get built on arch/powerpc. Signed-off-by: Josh Boyer Signed-off-by: Kumar Gala --- drivers/mtd/maps/Kconfig | 4 ++-- drivers/mtd/nand/Kconfig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index cc6c734424..3a6566adcd 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -354,7 +354,7 @@ config MTD_CFI_FLAGADM config MTD_WALNUT tristate "Flash device mapped on IBM 405GP Walnut" - depends on MTD_JEDECPROBE && WALNUT + depends on MTD_JEDECPROBE && WALNUT && !PPC_MERGE help This enables access routines for the flash chips on the IBM 405GP Walnut board. If you have one of these boards and would like to @@ -370,7 +370,7 @@ config MTD_EBONY config MTD_OCOTEA tristate "Flash devices mapped on IBM 440GX Ocotea" - depends on MTD_CFI && OCOTEA + depends on MTD_CFI && OCOTEA && !PPC_MERGE help This enables access routines for the flash chips on the IBM 440GX Ocotea board. If you have one of these boards and would like to diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index f1d60b6f04..a783d62f48 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -134,7 +134,7 @@ config MTD_NAND_S3C2410_HWECC config MTD_NAND_NDFC tristate "NDFC NanD Flash Controller" - depends on 44x + depends on 44x && !PPC_MERGE select MTD_NAND_ECC_SMC help NDFC Nand Flash Controllers are integrated in EP44x SoCs -- cgit v1.2.2 From 34a27ffdf59f80fe165f5c56a0be57d55b6da63d Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Wed, 22 Aug 2007 22:53:39 -0500 Subject: [MTD] Remove dead maps The tqm834x map Kconfig options depends on TQM834x which does not exist anywhere else in the kernel. The pq2fads map Kconfig/makefile support was removed a while ago but the actual file persisted. Signed-off-by: Kumar Gala --- drivers/mtd/maps/Kconfig | 8 -- drivers/mtd/maps/Makefile | 1 - drivers/mtd/maps/pq2fads.c | 88 -------------- drivers/mtd/maps/tqm834x.c | 286 --------------------------------------------- 4 files changed, 383 deletions(-) delete mode 100644 drivers/mtd/maps/pq2fads.c delete mode 100644 drivers/mtd/maps/tqm834x.c (limited to 'drivers') diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 3a6566adcd..5850ccd0dc 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -384,14 +384,6 @@ config MTD_REDWOOD Redwood board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. -config MTD_TQM834x - tristate "Flash device mapped on TQ Components TQM834x Boards" - depends on MTD_CFI && TQM834x - help - This enables access routines for the flash chips on the - TQ Components TQM834x boards. If you have one of these boards - and would like to use the flash chips on it, say 'Y'. - config MTD_OCELOT tristate "Momenco Ocelot boot flash device" depends on MOMENCO_OCELOT diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 970b189271..b9019b1b19 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -70,4 +70,3 @@ obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o obj-$(CONFIG_MTD_MTX1) += mtx-1_flash.o -obj-$(CONFIG_MTD_TQM834x) += tqm834x.o diff --git a/drivers/mtd/maps/pq2fads.c b/drivers/mtd/maps/pq2fads.c deleted file mode 100644 index fb78d87cc1..0000000000 --- a/drivers/mtd/maps/pq2fads.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * drivers/mtd/maps/pq2fads.c - * - * Mapping for the flash SIMM on 8272ADS and PQ2FADS board - * - * Author: Vitaly Bordug - * - * 2005 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - NOTE: bank width and interleave relative to the installed flash - should have been chosen within MTD_CFI_GEOMETRY options. - */ -#define PQ2FADS_BANK_WIDTH 4 - -static struct mtd_partition pq2fads_partitions[] = { - { -#ifdef CONFIG_ADS8272 - .name = "HRCW", - .size = 0x40000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "User FS", - .size = 0x5c0000, - .offset = 0x40000, -#else - .name = "User FS", - .size = 0x600000, - .offset = 0, -#endif - }, { - .name = "uImage", - .size = 0x100000, - .offset = 0x600000, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "bootloader", - .size = 0x40000, - .offset = 0x700000, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "bootloader env", - .size = 0x40000, - .offset = 0x740000, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - } -}; - - -/* pointer to MPC885ADS board info data */ -extern unsigned char __res[]; - -static int __init init_pq2fads_mtd(void) -{ - bd_t *bd = (bd_t *)__res; - physmap_configure(bd->bi_flashstart, bd->bi_flashsize, PQ2FADS_BANK_WIDTH, NULL); - - physmap_set_partitions(pq2fads_partitions, - sizeof (pq2fads_partitions) / - sizeof (pq2fads_partitions[0])); - return 0; -} - -static void __exit cleanup_pq2fads_mtd(void) -{ -} - -module_init(init_pq2fads_mtd); -module_exit(cleanup_pq2fads_mtd); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("MTD map and partitions for MPC8272ADS boards"); diff --git a/drivers/mtd/maps/tqm834x.c b/drivers/mtd/maps/tqm834x.c deleted file mode 100644 index 9adc970e55..0000000000 --- a/drivers/mtd/maps/tqm834x.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * drivers/mtd/maps/tqm834x.c - * - * MTD mapping driver for TQM834x boards - * - * Copyright 2005 Wolfgang Denk, DENX Software Engineering, . - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define FLASH_BANK_MAX 2 - -extern unsigned char __res[]; - -/* trivial struct to describe partition information */ -struct mtd_part_def -{ - int nums; - unsigned char *type; - struct mtd_partition* mtd_part; -}; - -static struct mtd_info* mtd_banks[FLASH_BANK_MAX]; -static struct map_info* map_banks[FLASH_BANK_MAX]; -static struct mtd_part_def part_banks[FLASH_BANK_MAX]; - -static unsigned long num_banks; -static unsigned long start_scan_addr; - -#ifdef CONFIG_MTD_PARTITIONS -/* - * The following defines the partition layout of TQM834x boards. - * - * See include/linux/mtd/partitions.h for definition of the - * mtd_partition structure. - * - * Assume minimal initial size of 4 MiB per bank, will be updated - * later in init_tqm834x_mtd() routine. - */ - -/* Partition definition for the first flash bank which is always present. */ -static struct mtd_partition tqm834x_partitions_bank1[] = { - { - .name = "u-boot", /* u-boot firmware */ - .offset = 0x00000000, - .size = 0x00040000, /* 256 KiB */ - /*mask_flags: MTD_WRITEABLE, * force read-only */ - }, - { - .name = "env", /* u-boot environment */ - .offset = 0x00040000, - .size = 0x00020000, /* 128 KiB */ - /*mask_flags: MTD_WRITEABLE, * force read-only */ - }, - { - .name = "kernel", /* linux kernel image */ - .offset = 0x00060000, - .size = 0x00100000, /* 1 MiB */ - /*mask_flags: MTD_WRITEABLE, * force read-only */ - }, - { - .name = "initrd", /* ramdisk image */ - .offset = 0x00160000, - .size = 0x00200000, /* 2 MiB */ - }, - { - .name = "user", /* user data */ - .offset = 0x00360000, - .size = 0x000a0000, /* remaining space */ - /* NOTE: this parttion size is re-calcated in */ - /* init_tqm834x_mtd() to cover actual remaining space. */ - }, -}; - -/* Partition definition for the second flash bank which may be present on some - * TQM834x boards. - */ -static struct mtd_partition tqm834x_partitions_bank2[] = { - { - .name = "jffs2", /* jffs2 filesystem */ - .offset = 0x00000000, - .size = 0x00400000, /* whole device */ - /* NOTE: this parttion size is re-calcated in */ - /* init_tqm834x_mtd() to cover actual device size. */ - }, -}; - -#endif /* CONFIG_MTD_PARTITIONS */ - -static int __init init_tqm834x_mtd(void) -{ - int idx = 0, ret = 0; - unsigned long flash_addr, flash_size, mtd_size = 0; - - /* pointer to TQM834x board info data */ - bd_t *bd = (bd_t *)__res; -#ifdef CONFIG_MTD_CMDLINE_PARTS - int n; - char mtdid[4]; - const char *part_probes[] = { "cmdlinepart", NULL }; -#endif - - flash_addr = bd->bi_flashstart; - flash_size = bd->bi_flashsize; - - /* request maximum flash size address space */ - start_scan_addr = (unsigned long)ioremap(flash_addr, flash_size); - if (!start_scan_addr) { - printk("%s: Failed to ioremap address: 0x%lx\n", - __FUNCTION__, flash_addr); - return -EIO; - } - - for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { - if (mtd_size >= flash_size) - break; - - pr_debug("%s: chip probing count %d\n", __FUNCTION__, idx); - - map_banks[idx] = kzalloc(sizeof(struct map_info), GFP_KERNEL); - if (map_banks[idx] == NULL) { - ret = -ENOMEM; - goto error_mem; - } - map_banks[idx]->name = kzalloc(16, GFP_KERNEL); - if (map_banks[idx]->name == NULL) { - ret = -ENOMEM; - goto error_mem; - } - - sprintf(map_banks[idx]->name, "TQM834x-%d", idx); - map_banks[idx]->size = flash_size; - map_banks[idx]->bankwidth = 4; - - simple_map_init(map_banks[idx]); - - map_banks[idx]->virt = (void __iomem *) - (start_scan_addr + ((idx > 0) ? - (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0)); - map_banks[idx]->phys = - flash_addr + ((idx > 0) ? - (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0); - - /* start to probe flash chips */ - mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]); - if (mtd_banks[idx]) { - mtd_banks[idx]->owner = THIS_MODULE; - mtd_size += mtd_banks[idx]->size; - num_banks++; - pr_debug("%s: bank %ld, name: %s, size: %d bytes \n", - __FUNCTION__, num_banks, - mtd_banks[idx]->name, mtd_banks[idx]->size); - } - } - - /* no supported flash chips found */ - if (!num_banks) { - printk("TQM834x: No supported flash chips found!\n"); - ret = -ENXIO; - goto error_mem; - } - -#ifdef CONFIG_MTD_PARTITIONS - /* - * Select static partition definitions - */ - n = ARRAY_SIZE(tqm834x_partitions_bank1); - part_banks[0].mtd_part = tqm834x_partitions_bank1; - part_banks[0].type = "static image bank1"; - part_banks[0].nums = n; - - /* update last partition size to cover actual remaining space */ - tqm834x_partitions_bank1[n - 1].size = - mtd_banks[0]->size - - tqm834x_partitions_bank1[n - 1].offset; - - /* check if we have second bank? */ - if (num_banks == 2) { - n = ARRAY_SIZE(tqm834x_partitions_bank2); - part_banks[1].mtd_part = tqm834x_partitions_bank2; - part_banks[1].type = "static image bank2"; - part_banks[1].nums = n; - - /* update last partition size to cover actual remaining space */ - tqm834x_partitions_bank2[n - 1].size = - mtd_banks[1]->size - - tqm834x_partitions_bank2[n - 1].offset; - } - - for(idx = 0; idx < num_banks ; idx++) { -#ifdef CONFIG_MTD_CMDLINE_PARTS - sprintf(mtdid, "%d", idx); - n = parse_mtd_partitions(mtd_banks[idx], - part_probes, - &part_banks[idx].mtd_part, - 0); - pr_debug("%s: %d command line partitions on bank %s\n", - __FUNCTION__, n, mtdid); - if (n > 0) { - part_banks[idx].type = "command line"; - part_banks[idx].nums = n; - } -#endif /* CONFIG_MTD_CMDLINE_PARTS */ - if (part_banks[idx].nums == 0) { - printk(KERN_NOTICE - "TQM834x flash bank %d: no partition info " - "available, registering whole device\n", idx); - add_mtd_device(mtd_banks[idx]); - } else { - printk(KERN_NOTICE - "TQM834x flash bank %d: Using %s partition " - "definition\n", idx, part_banks[idx].type); - add_mtd_partitions(mtd_banks[idx], - part_banks[idx].mtd_part, - part_banks[idx].nums); - } - } -#else /* ! CONFIG_MTD_PARTITIONS */ - printk(KERN_NOTICE "TQM834x flash: registering %d flash banks " - "at once\n", num_banks); - - for(idx = 0 ; idx < num_banks ; idx++) - add_mtd_device(mtd_banks[idx]); - -#endif /* CONFIG_MTD_PARTITIONS */ - - return 0; -error_mem: - for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { - if (map_banks[idx] != NULL) { - if (map_banks[idx]->name != NULL) { - kfree(map_banks[idx]->name); - map_banks[idx]->name = NULL; - } - kfree(map_banks[idx]); - map_banks[idx] = NULL; - } - } - - iounmap((void *)start_scan_addr); - - return ret; -} - -static void __exit cleanup_tqm834x_mtd(void) -{ - unsigned int idx = 0; - for(idx = 0 ; idx < num_banks ; idx++) { - /* destroy mtd_info previously allocated */ - if (mtd_banks[idx]) { - del_mtd_partitions(mtd_banks[idx]); - map_destroy(mtd_banks[idx]); - } - - /* release map_info not used anymore */ - kfree(map_banks[idx]->name); - kfree(map_banks[idx]); - } - - if (start_scan_addr) { - iounmap((void *)start_scan_addr); - start_scan_addr = 0; - } -} - -module_init(init_tqm834x_mtd); -module_exit(cleanup_tqm834x_mtd); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Wolfgang Denk "); -MODULE_DESCRIPTION("MTD map driver for TQM834x boards"); -- cgit v1.2.2 From e208520ed664db0f7584048ae09e5d2afda43714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Engel?= Date: Wed, 29 Aug 2007 17:57:11 +0200 Subject: [MTD] [NAND] Driver for Olympus MAUSB-10 and Fujifilm DPC-R1 card readers Unlike most stuff on the market the chip inside these two allows raw flash access and doesn't implement and FTL, leaving that functionality to the device driver. Raw flash access in a cheap USB cardreader! An MTD test device one can attach to a PC! What a deal! The command set of the chip is not documented, so information was obtained from the existing mass-storage driver (drivers/usb/storage/alauda.c), its documentation (http://alauda.sourceforge.net/wikka.php?wakka=BulkCommandReference), additional reverse engineering and comparison with a vendor driver for a related chip (http://www.ratocsystems.com/english/download/driver/linux/sma03u.html). Signed-off-by: Joern Engel Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 6 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/alauda.c | 742 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 749 insertions(+) create mode 100644 drivers/mtd/nand/alauda.c (limited to 'drivers') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 511448ff0c..d3ec309f24 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -280,5 +280,11 @@ config MTD_NAND_PLATFORM devices. You will need to provide platform-specific functions via platform_data. +config MTD_ALAUDA + tristate "MTD driver for Olympus MAUSB-10 and Fijufilm DPC-R1" + depends on MTD_NAND && USB + help + These two (and possibly other) Alauda-based cardreaders for + SmartMedia and xD allow raw flash access. endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index edba1db14b..74125611f3 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -27,5 +27,6 @@ obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o +obj-$(CONFIG_MTD_ALAUDA) += alauda.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c new file mode 100644 index 0000000000..2c29854b42 --- /dev/null +++ b/drivers/mtd/nand/alauda.c @@ -0,0 +1,742 @@ +/* + * MTD driver for Alauda chips + * + * Copyright (C) 2007 Joern Engel + * + * Based on drivers/usb/usb-skeleton.c which is: + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * and on drivers/usb/storage/alauda.c, which is: + * (c) 2005 Daniel Drake + * + * Idea and initial work by Arnd Bergmann + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Control commands */ +#define ALAUDA_GET_XD_MEDIA_STATUS 0x08 +#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a +#define ALAUDA_GET_XD_MEDIA_SIG 0x86 + +/* Common prefix */ +#define ALAUDA_BULK_CMD 0x40 + +/* The two ports */ +#define ALAUDA_PORT_XD 0x00 +#define ALAUDA_PORT_SM 0x01 + +/* Bulk commands */ +#define ALAUDA_BULK_READ_PAGE 0x84 +#define ALAUDA_BULK_READ_OOB 0x85 /* don't use, there's a chip bug */ +#define ALAUDA_BULK_READ_BLOCK 0x94 +#define ALAUDA_BULK_ERASE_BLOCK 0xa3 +#define ALAUDA_BULK_WRITE_PAGE 0xa4 +#define ALAUDA_BULK_WRITE_BLOCK 0xb4 +#define ALAUDA_BULK_RESET_MEDIA 0xe0 + +/* Address shifting */ +#define PBA_LO(pba) ((pba & 0xF) << 5) +#define PBA_HI(pba) (pba >> 3) +#define PBA_ZONE(pba) (pba >> 11) + +#define TIMEOUT HZ + +static struct usb_device_id alauda_table [] = { + { USB_DEVICE(0x0584, 0x0008) }, /* Fujifilm DPC-R1 */ + { USB_DEVICE(0x07b4, 0x010a) }, /* Olympus MAUSB-10 */ + { } +}; +MODULE_DEVICE_TABLE(usb, alauda_table); + +struct alauda_card { + u8 id; /* id byte */ + u8 chipshift; /* 1<id; card++) + if (card->id == id) + return card; + return NULL; +} + +static void alauda_delete(struct kref *kref) +{ + struct alauda *al = container_of(kref, struct alauda, kref); + + if (al->mtd) { + del_mtd_device(al->mtd); + kfree(al->mtd); + } + usb_put_dev(al->dev); + kfree(al); +} + +static int alauda_get_media_status(struct alauda *al, void *buf) +{ + int ret; + + mutex_lock(&al->card_mutex); + ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0), + ALAUDA_GET_XD_MEDIA_STATUS, 0xc0, 0, 1, buf, 2, HZ); + mutex_unlock(&al->card_mutex); + return ret; +} + +static int alauda_ack_media(struct alauda *al) +{ + int ret; + + mutex_lock(&al->card_mutex); + ret = usb_control_msg(al->dev, usb_sndctrlpipe(al->dev, 0), + ALAUDA_ACK_XD_MEDIA_CHANGE, 0x40, 0, 1, NULL, 0, HZ); + mutex_unlock(&al->card_mutex); + return ret; +} + +static int alauda_get_media_signatures(struct alauda *al, void *buf) +{ + int ret; + + mutex_lock(&al->card_mutex); + ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0), + ALAUDA_GET_XD_MEDIA_SIG, 0xc0, 0, 0, buf, 4, HZ); + mutex_unlock(&al->card_mutex); + return ret; +} + +static void alauda_reset(struct alauda *al) +{ + u8 command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_RESET_MEDIA, 0, 0, + 0, 0, 0, 0, al->port + }; + mutex_lock(&al->card_mutex); + usb_bulk_msg(al->dev, al->bulk_out, command, 9, NULL, HZ); + mutex_unlock(&al->card_mutex); +} + +static void correct_data(void *buf, void *read_ecc, + int *corrected, int *uncorrected) +{ + u8 calc_ecc[3]; + int err; + + nand_calculate_ecc(NULL, buf, calc_ecc); + err = nand_correct_data(NULL, buf, read_ecc, calc_ecc); + if (err) { + if (err > 0) + (*corrected)++; + else + (*uncorrected)++; + } +} + +struct alauda_sg_request { + struct urb *urb[3]; + struct completion comp; +}; + +static void alauda_complete(struct urb *urb) +{ + struct completion *comp = urb->context; + + if (comp) + complete(comp); +} + +static int __alauda_read_page(struct mtd_info *mtd, loff_t from, void *buf, + void *oob) +{ + struct alauda_sg_request sg; + struct alauda *al = mtd->priv; + u32 pba = from >> al->card->blockshift; + u32 page = (from >> al->card->pageshift) & al->pagemask; + u8 command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_READ_PAGE, PBA_HI(pba), + PBA_ZONE(pba), 0, PBA_LO(pba) + page, 1, 0, al->port + }; + int i, err; + + for (i=0; i<3; i++) + sg.urb[i] = NULL; + + err = -ENOMEM; + for (i=0; i<3; i++) { + sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); + if (!sg.urb[i]) + goto out; + } + init_completion(&sg.comp); + usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, + alauda_complete, NULL); + usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, mtd->writesize, + alauda_complete, NULL); + usb_fill_bulk_urb(sg.urb[2], al->dev, al->bulk_in, oob, 16, + alauda_complete, &sg.comp); + + mutex_lock(&al->card_mutex); + for (i=0; i<3; i++) { + err = usb_submit_urb(sg.urb[i], GFP_NOIO); + if (err) + goto cancel; + } + if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { + err = -ETIMEDOUT; +cancel: + for (i=0; i<3; i++) { + usb_kill_urb(sg.urb[i]); + } + } + mutex_unlock(&al->card_mutex); + +out: + usb_free_urb(sg.urb[0]); + usb_free_urb(sg.urb[1]); + usb_free_urb(sg.urb[2]); + return err; +} + +static int alauda_read_page(struct mtd_info *mtd, loff_t from, + void *buf, u8 *oob, int *corrected, int *uncorrected) +{ + int err; + + err = __alauda_read_page(mtd, from, buf, oob); + if (err) + return err; + correct_data(buf, oob+13, corrected, uncorrected); + correct_data(buf+256, oob+8, corrected, uncorrected); + return 0; +} + +static int alauda_write_page(struct mtd_info *mtd, loff_t to, void *buf, + void *oob) +{ + struct alauda_sg_request sg; + struct alauda *al = mtd->priv; + u32 pba = to >> al->card->blockshift; + u32 page = (to >> al->card->pageshift) & al->pagemask; + u8 command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_PAGE, PBA_HI(pba), + PBA_ZONE(pba), 0, PBA_LO(pba) + page, 32, 0, al->port + }; + int i, err; + + for (i=0; i<3; i++) + sg.urb[i] = NULL; + + err = -ENOMEM; + for (i=0; i<3; i++) { + sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); + if (!sg.urb[i]) + goto out; + } + init_completion(&sg.comp); + usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, + alauda_complete, NULL); + usb_fill_bulk_urb(sg.urb[1], al->dev, al->write_out, buf,mtd->writesize, + alauda_complete, NULL); + usb_fill_bulk_urb(sg.urb[2], al->dev, al->write_out, oob, 16, + alauda_complete, &sg.comp); + + mutex_lock(&al->card_mutex); + for (i=0; i<3; i++) { + err = usb_submit_urb(sg.urb[i], GFP_NOIO); + if (err) + goto cancel; + } + if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { + err = -ETIMEDOUT; +cancel: + for (i=0; i<3; i++) { + usb_kill_urb(sg.urb[i]); + } + } + mutex_unlock(&al->card_mutex); + +out: + usb_free_urb(sg.urb[0]); + usb_free_urb(sg.urb[1]); + usb_free_urb(sg.urb[2]); + return err; +} + +static int alauda_erase_block(struct mtd_info *mtd, loff_t ofs) +{ + struct alauda_sg_request sg; + struct alauda *al = mtd->priv; + u32 pba = ofs >> al->card->blockshift; + u8 command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba), + PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, al->port + }; + u8 buf[2]; + int i, err; + + for (i=0; i<2; i++) + sg.urb[i] = NULL; + + err = -ENOMEM; + for (i=0; i<2; i++) { + sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); + if (!sg.urb[i]) + goto out; + } + init_completion(&sg.comp); + usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, + alauda_complete, NULL); + usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, 2, + alauda_complete, &sg.comp); + + mutex_lock(&al->card_mutex); + for (i=0; i<2; i++) { + err = usb_submit_urb(sg.urb[i], GFP_NOIO); + if (err) + goto cancel; + } + if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { + err = -ETIMEDOUT; +cancel: + for (i=0; i<2; i++) { + usb_kill_urb(sg.urb[i]); + } + } + mutex_unlock(&al->card_mutex); + +out: + usb_free_urb(sg.urb[0]); + usb_free_urb(sg.urb[1]); + return err; +} + +static int alauda_read_oob(struct mtd_info *mtd, loff_t from, void *oob) +{ + static u8 ignore_buf[512]; /* write only */ + + return __alauda_read_page(mtd, from, ignore_buf, oob); +} + +static int popcount8(u8 c) +{ + int ret = 0; + + for ( ; c; c>>=1) + ret += c & 1; + return ret; +} + +static int alauda_isbad(struct mtd_info *mtd, loff_t ofs) +{ + u8 oob[16]; + int err; + + err = alauda_read_oob(mtd, ofs, oob); + if (err) + return err; + + /* A block is marked bad if two or more bits are zero */ + return popcount8(oob[5]) >= 7 ? 0 : 1; +} + +static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct alauda *al = mtd->priv; + void *bounce_buf; + int err, corrected=0, uncorrected=0; + + bounce_buf = kmalloc(mtd->writesize, GFP_KERNEL); + if (!bounce_buf) + return -ENOMEM; + + *retlen = len; + while (len) { + u8 oob[16]; + u32 byte = from & al->bytemask; + size_t cplen = min(len, mtd->writesize - byte); + + err = alauda_read_page(mtd, from, bounce_buf, oob, + &corrected, &uncorrected); + if (err) + goto out; + + memcpy(buf, bounce_buf + byte, cplen); + buf += cplen; + from += cplen; + len -= cplen; + } + err = 0; + if (corrected) + err = -EUCLEAN; + if (uncorrected) + err = -EBADMSG; +out: + kfree(bounce_buf); + return err; +} + +static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct alauda *al = mtd->priv; + int err, corrected=0, uncorrected=0; + + if ((from & al->bytemask) || (len & al->bytemask)) + return alauda_bounce_read(mtd, from, len, retlen, buf); + + *retlen = len; + while (len) { + u8 oob[16]; + + err = alauda_read_page(mtd, from, buf, oob, + &corrected, &uncorrected); + if (err) + return err; + + buf += mtd->writesize; + from += mtd->writesize; + len -= mtd->writesize; + } + err = 0; + if (corrected) + err = -EUCLEAN; + if (uncorrected) + err = -EBADMSG; + return err; +} + +static int alauda_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct alauda *al = mtd->priv; + int err; + + if ((to & al->bytemask) || (len & al->bytemask)) + return -EINVAL; + + *retlen = len; + while (len) { + u32 page = (to >> al->card->pageshift) & al->pagemask; + u8 oob[16] = { 'h', 'e', 'l', 'l', 'o', 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + /* don't write to bad blocks */ + if (page == 0) { + err = alauda_isbad(mtd, to); + if (err) { + return -EIO; + } + } + nand_calculate_ecc(mtd, buf, &oob[13]); + nand_calculate_ecc(mtd, buf+256, &oob[8]); + + err = alauda_write_page(mtd, to, (void*)buf, oob); + if (err) + return err; + + buf += mtd->writesize; + to += mtd->writesize; + len -= mtd->writesize; + } + return 0; +} + +static int __alauda_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct alauda *al = mtd->priv; + u32 ofs = instr->addr; + u32 len = instr->len; + int err; + + if ((ofs & al->blockmask) || (len & al->blockmask)) + return -EINVAL; + + while (len) { + /* don't erase bad blocks */ + err = alauda_isbad(mtd, ofs); + if (err > 0) + err = -EIO; + if (err < 0) + return err; + + err = alauda_erase_block(mtd, ofs); + if (err < 0) + return err; + + ofs += mtd->erasesize; + len -= mtd->erasesize; + } + return 0; +} + +static int alauda_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + int err; + + err = __alauda_erase(mtd, instr); + instr->state = err ? MTD_ERASE_FAILED : MTD_ERASE_DONE; + mtd_erase_callback(instr); + return err; +} + +static int alauda_init_media(struct alauda *al) +{ + u8 buf[4], *b0=buf, *b1=buf+1; + struct alauda_card *card; + struct mtd_info *mtd; + int err; + + mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); + if (!mtd) + return -ENOMEM; + + for (;;) { + err = alauda_get_media_status(al, buf); + if (err < 0) + goto error; + if (*b0 & 0x10) + break; + msleep(20); + } + + err = alauda_ack_media(al); + if (err) + goto error; + + msleep(10); + + err = alauda_get_media_status(al, buf); + if (err < 0) + goto error; + + if (*b0 != 0x14) { + /* media not ready */ + err = -EIO; + goto error; + } + err = alauda_get_media_signatures(al, buf); + if (err < 0) + goto error; + + card = get_card(*b1); + if (!card) { + printk(KERN_ERR"Alauda: unknown card id %02x\n", *b1); + err = -EIO; + goto error; + } + printk(KERN_INFO"pagesize=%x\nerasesize=%x\nsize=%xMiB\n", + 1<pageshift, 1<blockshift, + 1<<(card->chipshift-20)); + al->card = card; + al->pagemask = (1 << (card->blockshift - card->pageshift)) - 1; + al->bytemask = (1 << card->pageshift) - 1; + al->blockmask = (1 << card->blockshift) - 1; + + mtd->name = "alauda"; + mtd->size = 1<chipshift; + mtd->erasesize = 1<blockshift; + mtd->writesize = 1<pageshift; + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + mtd->read = alauda_read; + mtd->write = alauda_write; + mtd->erase = alauda_erase; + mtd->block_isbad = alauda_isbad; + mtd->priv = al; + mtd->owner = THIS_MODULE; + + err = add_mtd_device(mtd); + if (err) { + err = -ENFILE; + goto error; + } + + al->mtd = mtd; + alauda_reset(al); /* no clue whether this is necessary */ + return 0; +error: + kfree(mtd); + return err; +} + +static int alauda_check_media(struct alauda *al) +{ + u8 buf[2], *b0 = buf, *b1 = buf+1; + int err; + + err = alauda_get_media_status(al, buf); + if (err < 0) + return err; + + if ((*b1 & 0x01) == 0) { + /* door open */ + return -EIO; + } + if ((*b0 & 0x80) || ((*b0 & 0x1F) == 0x10)) { + /* no media ? */ + return -EIO; + } + if (*b0 & 0x08) { + /* media change ? */ + return alauda_init_media(al); + } + return 0; +} + +static int alauda_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct alauda *al; + struct usb_host_interface *iface; + struct usb_endpoint_descriptor *ep, + *ep_in=NULL, *ep_out=NULL, *ep_wr=NULL; + int i, err = -ENOMEM; + + al = kzalloc(2*sizeof(*al), GFP_KERNEL); + if (!al) + goto error; + + kref_init(&al->kref); + usb_set_intfdata(interface, al); + + al->dev = usb_get_dev(interface_to_usbdev(interface)); + al->interface = interface; + + iface = interface->cur_altsetting; + for (i = 0; i < iface->desc.bNumEndpoints; ++i) { + ep = &iface->endpoint[i].desc; + + if (usb_endpoint_is_bulk_in(ep)) { + ep_in = ep; + } else if (usb_endpoint_is_bulk_out(ep)) { + if (i==0) + ep_wr = ep; + else + ep_out = ep; + } + } + err = -EIO; + if (!ep_wr || !ep_in || !ep_out) + goto error; + + al->write_out = usb_sndbulkpipe(al->dev, + ep_wr->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + al->bulk_in = usb_rcvbulkpipe(al->dev, + ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + al->bulk_out = usb_sndbulkpipe(al->dev, + ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + + /* second device is identical up to now */ + memcpy(al+1, al, sizeof(*al)); + + mutex_init(&al[0].card_mutex); + mutex_init(&al[1].card_mutex); + + al[0].port = ALAUDA_PORT_XD; + al[1].port = ALAUDA_PORT_SM; + + info("alauda probed"); + alauda_check_media(al); + alauda_check_media(al+1); + + return 0; + +error: + if (al) + kref_put(&al->kref, alauda_delete); + return err; +} + +static void alauda_disconnect(struct usb_interface *interface) +{ + struct alauda *al; + + al = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + /* FIXME: prevent more I/O from starting */ + + /* decrement our usage count */ + if (al) + kref_put(&al->kref, alauda_delete); + + info("alauda gone"); +} + +static struct usb_driver alauda_driver = { + .name = "alauda", + .probe = alauda_probe, + .disconnect = alauda_disconnect, + .id_table = alauda_table, +}; + +static int __init alauda_init(void) +{ + return usb_register(&alauda_driver); +} + +static void __exit alauda_exit(void) +{ + usb_deregister(&alauda_driver); +} + +module_init(alauda_init); +module_exit(alauda_exit); + +MODULE_LICENSE("GPL"); -- cgit v1.2.2 From 12f77c9eed0d2a9f598500d9c1e3dd48883f1d0c Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Thu, 30 Aug 2007 09:36:05 +0900 Subject: [MTD] [OneNAND] Use mtd_oob_ops at oob functions To enable the main read/write at oob ops Next time we will commit the main read/write support for yaffs2 Signed-off-by: Kyungmin Park Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 51 +++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 7d194cfdb8..45bc1433b2 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -921,17 +921,22 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col * * OneNAND read out-of-band data from the spare area */ -static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf, mtd_oob_mode_t mode) +static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) { struct onenand_chip *this = mtd->priv; int read = 0, thislen, column, oobsize; + size_t len = ops->ooblen; + mtd_oob_mode_t mode = ops->mode; + u_char *buf = ops->oobbuf; int ret = 0; + from += ops->ooboffs; + DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); /* Initialize return length value */ - *retlen = 0; + ops->oobretlen = 0; if (mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; @@ -997,7 +1002,7 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); - *retlen = read; + ops->oobretlen = read; return ret; } @@ -1019,8 +1024,7 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, default: return -EINVAL; } - return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf, ops->mode); + return onenand_do_read_oob(mtd, from, ops); } /** @@ -1370,18 +1374,23 @@ static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, * * OneNAND write out-of-band */ -static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf, mtd_oob_mode_t mode) +static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) { struct onenand_chip *this = mtd->priv; int column, ret = 0, oobsize; int written = 0; u_char *oobbuf; + size_t len = ops->ooblen; + const u_char *buf = ops->oobbuf; + mtd_oob_mode_t mode = ops->mode; + + to += ops->ooboffs; DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ - *retlen = 0; + ops->oobretlen = 0; if (mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; @@ -1464,7 +1473,7 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); - *retlen = written; + ops->oobretlen = written; return ret; } @@ -1487,8 +1496,7 @@ static int onenand_write_oob(struct mtd_info *mtd, loff_t to, default: return -EINVAL; } - return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf, ops->mode); + return onenand_do_write_oob(mtd, to, ops); } /** @@ -1646,7 +1654,12 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) struct onenand_chip *this = mtd->priv; struct bbm_info *bbm = this->bbm; u_char buf[2] = {0, 0}; - size_t retlen; + struct mtd_oob_ops ops = { + .mode = MTD_OOB_PLACE, + .ooblen = 2, + .oobbuf = buf, + .ooboffs = 0, + }; int block; /* Get block number */ @@ -1656,7 +1669,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 onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE); + return onenand_do_write_oob(mtd, ofs, &ops); } /** @@ -1945,13 +1958,21 @@ 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; + struct mtd_oob_ops ops = { + .mode = MTD_OOB_PLACE, + .ooblen = len, + .oobbuf = buf, + .ooboffs = 0, + }; int ret; /* Enter OTP access mode */ this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); - ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE); + ret = onenand_do_write_oob(mtd, from, &ops); + + *retlen = ops.oobretlen; /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); -- cgit v1.2.2 From d15057b7034d9fdc4259b66a0367c9d8ffcf0620 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Thu, 6 Sep 2007 10:06:12 +0900 Subject: [MTD] [OneNAND] main read/write ops support for yaffs2 Now we can use yaffs2 on OneNAND Signed-off-by: Kyungmin Park Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 349 +++++++++++++++++++++++++------------ 1 file changed, 233 insertions(+), 116 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 45bc1433b2..40d8d6ff62 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -763,31 +763,83 @@ static void onenand_release_device(struct mtd_info *mtd) } /** - * onenand_read - [MTD Interface] Read data from flash + * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer + * @param mtd MTD device structure + * @param buf destination address + * @param column oob offset to read from + * @param thislen oob length to read + */ +static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column, + int thislen) +{ + struct onenand_chip *this = mtd->priv; + struct nand_oobfree *free; + int readcol = column; + int readend = column + thislen; + int lastgap = 0; + unsigned int i; + uint8_t *oob_buf = this->oob_buf; + + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { + if (readcol >= lastgap) + readcol += free->offset - lastgap; + if (readend >= lastgap) + readend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { + int free_end = free->offset + free->length; + if (free->offset < readend && free_end > readcol) { + int st = max_t(int,free->offset,readcol); + int ed = min_t(int,free_end,readend); + int n = ed - st; + memcpy(buf, oob_buf + st, n); + buf += n; + } else if (column == 0) + break; + } + return 0; +} + +/** + * onenand_read_ops - [OneNAND Interface] OneNAND read main and/or out-of-band * @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 ops: oob operation description structure * - * Read with ecc -*/ -static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) + * OneNAND read main and/or out-of-band data + */ +static int onenand_read_ops(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) { struct onenand_chip *this = mtd->priv; struct mtd_ecc_stats stats; - int read = 0, column; - int thislen; + size_t len = ops->len; + size_t ooblen = ops->ooblen; + u_char *buf = ops->datbuf; + u_char *oobbuf = ops->oobbuf; + int read = 0, column, thislen; + int oobread = 0, oobcolumn, thisooblen, oobsize; int ret = 0, boundary = 0; int writesize = this->writesize; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + + if (ops->mode == MTD_OOB_AUTO) + oobsize = this->ecclayout->oobavail; + else + oobsize = mtd->oobsize; + + oobcolumn = from & (mtd->oobsize - 1); /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { - printk(KERN_ERR "onenand_read: Attempt read beyond end of device\n"); - *retlen = 0; + printk(KERN_ERR "onenand_read_ops: Attempt read beyond end of device\n"); + ops->retlen = 0; + ops->oobretlen = 0; return -EINVAL; } @@ -832,6 +884,21 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, } /* While load is going, read from last bufferRAM */ this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); + + /* Read oob area if needed */ + if (oobbuf) { + thisooblen = oobsize - oobcolumn; + thisooblen = min_t(int, thisooblen, ooblen - oobread); + + if (ops->mode == MTD_OOB_AUTO) + onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); + else + this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); + oobread += thisooblen; + oobbuf += thisooblen; + oobcolumn = 0; + } + /* See if we are done */ read += thislen; if (read == len) @@ -857,7 +924,8 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, * fs driver will take care of that, because * retlen == desired len and result == -EBADMSG */ - *retlen = read; + ops->retlen = read; + ops->oobretlen = oobread; if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; @@ -868,56 +936,11 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; } -/** - * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer - * @param mtd MTD device structure - * @param buf destination address - * @param column oob offset to read from - * @param thislen oob length to read - */ -static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column, - int thislen) -{ - struct onenand_chip *this = mtd->priv; - struct nand_oobfree *free; - int readcol = column; - int readend = column + thislen; - int lastgap = 0; - unsigned int i; - uint8_t *oob_buf = this->oob_buf; - - free = this->ecclayout->oobfree; - for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { - if (readcol >= lastgap) - readcol += free->offset - lastgap; - if (readend >= lastgap) - readend += free->offset - lastgap; - lastgap = free->offset + free->length; - } - this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); - free = this->ecclayout->oobfree; - for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { - int free_end = free->offset + free->length; - if (free->offset < readend && free_end > readcol) { - int st = max_t(int,free->offset,readcol); - int ed = min_t(int,free_end,readend); - int n = ed - st; - memcpy(buf, oob_buf + st, n); - buf += n; - } else if (column == 0) - break; - } - return 0; -} - /** * 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 - * @param retlen pointer to variable to store the number of read bytes - * @param buf the databuffer to put data - * @param mode operation mode + * @param ops: oob operation description structure * * OneNAND read out-of-band data from the spare area */ @@ -1007,10 +1030,39 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, } /** - * onenand_read_oob - [MTD Interface] NAND write data and/or out-of-band + * 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 + * + * Read with ecc +*/ +static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct mtd_oob_ops ops = { + .len = len, + .ooblen = 0, + .datbuf = buf, + .oobbuf = NULL, + }; + int ret; + + ret = onenand_read_ops(mtd, from, &ops); + *retlen = ops.retlen; + + return ret; +} + +/** + * onenand_read_oob - [MTD Interface] Read main and/or out-of-band * @param mtd: MTD device structure * @param from: offset to read from * @param ops: oob operation description structure + + * Read main and/or out-of-band */ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) @@ -1024,6 +1076,10 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, default: return -EINVAL; } + + if (ops->datbuf) + return onenand_read_ops(mtd, from, ops); + return onenand_do_read_oob(mtd, from, ops); } @@ -1148,7 +1204,6 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, * @param mtd MTD device structure * @param buf the databuffer to verify * @param to offset to read from - * */ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) { @@ -1176,7 +1231,6 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to * @param buf the databuffer to verify * @param addr offset to read from * @param len number of bytes to read and compare - * */ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) { @@ -1222,27 +1276,72 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, #define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0) /** - * onenand_write - [MTD Interface] write buffer to FLASH + * onenand_fill_auto_oob - [Internal] oob auto-placement transfer + * @param mtd MTD device structure + * @param oob_buf oob buffer + * @param buf source address + * @param column oob offset to write to + * @param thislen oob length to write + */ +static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, + const u_char *buf, int column, int thislen) +{ + struct onenand_chip *this = mtd->priv; + struct nand_oobfree *free; + int writecol = column; + int writeend = column + thislen; + int lastgap = 0; + unsigned int i; + + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { + if (writecol >= lastgap) + writecol += free->offset - lastgap; + if (writeend >= lastgap) + writeend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { + int free_end = free->offset + free->length; + if (free->offset < writeend && free_end > writecol) { + int st = max_t(int,free->offset,writecol); + int ed = min_t(int,free_end,writeend); + int n = ed - st; + memcpy(oob_buf + st, buf, n); + buf += n; + } else if (column == 0) + break; + } + return 0; +} + +/** + * onenand_write_ops - [OneNAND Interface] write main and/or out-of-band * @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 ops oob operation description structure * - * Write with ECC + * Write main and/or oob with ECC */ -static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int onenand_write_ops(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) { struct onenand_chip *this = mtd->priv; - int written = 0; + int written = 0, column, thislen, subpage; + int oobwritten = 0, oobcolumn, thisooblen, oobsize; + size_t len = ops->len; + size_t ooblen = ops->ooblen; + const u_char *buf = ops->datbuf; + const u_char *oob = ops->oobbuf; + u_char *oobbuf; int ret = 0; - int column, subpage; 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; + ops->retlen = 0; + ops->oobretlen = 0; /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { @@ -1256,6 +1355,13 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, return -EINVAL; } + if (ops->mode == MTD_OOB_AUTO) + oobsize = this->ecclayout->oobavail; + else + oobsize = mtd->oobsize; + + oobcolumn = to & (mtd->oobsize - 1); + column = to & (mtd->writesize - 1); /* Grab the lock and see if the device is available */ @@ -1263,9 +1369,11 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, /* Loop until all data write */ while (written < len) { - int thislen = min_t(int, mtd->writesize - column, len - written); u_char *wbuf = (u_char *) buf; + thislen = min_t(int, mtd->writesize - column, len - written); + thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten); + cond_resched(); this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); @@ -1279,7 +1387,25 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, } this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); - this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); + + if (oob) { + oobbuf = this->oob_buf; + + /* We send data to spare ram with oobsize + * to prevent byte access */ + memset(oobbuf, 0xff, mtd->oobsize); + if (ops->mode == MTD_OOB_AUTO) + onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen); + else + memcpy(oobbuf + oobcolumn, oob, thisooblen); + + oobwritten += thisooblen; + oob += thisooblen; + oobcolumn = 0; + } else + oobbuf = (u_char *) ffchars; + + this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); @@ -1317,51 +1443,11 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); - *retlen = written; + ops->retlen = written; return ret; } -/** - * onenand_fill_auto_oob - [Internal] oob auto-placement transfer - * @param mtd MTD device structure - * @param oob_buf oob buffer - * @param buf source address - * @param column oob offset to write to - * @param thislen oob length to write - */ -static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, - const u_char *buf, int column, int thislen) -{ - struct onenand_chip *this = mtd->priv; - struct nand_oobfree *free; - int writecol = column; - int writeend = column + thislen; - int lastgap = 0; - unsigned int i; - - free = this->ecclayout->oobfree; - for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { - if (writecol >= lastgap) - writecol += free->offset - lastgap; - if (writeend >= lastgap) - writeend += free->offset - lastgap; - lastgap = free->offset + free->length; - } - free = this->ecclayout->oobfree; - for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { - int free_end = free->offset + free->length; - if (free->offset < writeend && free_end > writecol) { - int st = max_t(int,free->offset,writecol); - int ed = min_t(int,free_end,writeend); - int n = ed - st; - memcpy(oob_buf + st, buf, n); - buf += n; - } else if (column == 0) - break; - } - return 0; -} /** * onenand_do_write_oob - [Internal] OneNAND write out-of-band @@ -1478,6 +1564,33 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, return ret; } +/** + * 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 + * + * Write with ECC + */ +static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct mtd_oob_ops ops = { + .len = len, + .ooblen = 0, + .datbuf = (u_char *) buf, + .oobbuf = NULL, + }; + int ret; + + ret = onenand_write_ops(mtd, to, &ops); + *retlen = ops.retlen; + + return ret; +} + /** * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band * @param mtd: MTD device structure @@ -1496,6 +1609,10 @@ static int onenand_write_oob(struct mtd_info *mtd, loff_t to, default: return -EINVAL; } + + if (ops->datbuf) + return onenand_write_ops(mtd, to, ops); + return onenand_do_write_oob(mtd, to, ops); } -- cgit v1.2.2 From 98b830d26095007aeb04041147b93d2b74e0a0c0 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 28 Aug 2007 20:33:32 +0300 Subject: [MTD] [NAND] nandsim: avoid deadlocking FS Make nandsim use GFP_NOFS when allocating memory, because it might be used by a file-system (e.g. UBIFS2) which means, if we are short of memory, we may deadlock. Indee, UBIFS is holding a lock, writes to the media, reaches this place in NANDsim, kmalloc does not find the requested amount of RAM, calls memory shrinker, which decides to writeback inodes, calls FS, and it deadlocks on the lock which is already being held. Below is the UBIFS backtrace which demonstrates that: [] __mutex_lock_slowpath+0xc8/0x2e6 [] mutex_lock+0x1c/0x1f [] reserve_space+0x3d/0xa9 [ubifs] [] make_one_reservation+0x2b/0x86 [ubifs] [] ubifs_jrn_write_block+0xda/0x12f [ubifs] [] ubifs_writepage+0x11d/0x1ec [ubifs] [] shrink_inactive_list+0x7fa/0x969 [] shrink_zone+0xae/0x10c [] try_to_free_pages+0x159/0x251 [] __alloc_pages+0x125/0x2f0 [] cache_alloc_refill+0x380/0x6ba [] __kmalloc+0x14f/0x157 [] do_state_action+0xab7/0xc74 [nandsim] [] switch_state+0x225/0x402 [nandsim] [] ns_hwcontrol+0x3e2/0x620 [nandsim] [] nand_command+0x2e/0x1a5 [nand] [] nand_write_page+0x4a/0x9a [nand] [] nand_do_write_ops+0x1cf/0x343 [nand] [] nand_write+0x88/0xa6 [nand] [] part_write+0x72/0x8b [mtd] [] ubi_io_write+0x189/0x29c [ubi] [] ubi_eba_write_leb+0xb6/0x699 [ubi] [] ubi_leb_write+0xe4/0xe9 [ubi] [] ubifs_wbuf_write_nolock+0x333/0x4c9 [ubifs] [] write_node+0x74/0x8e [ubifs] [] ubifs_jrn_write_block+0x100/0x12f [ubifs] [] ubifs_writepage+0x11d/0x1ec [ubifs] [] __writepage+0xb/0x26 [] write_cache_pages+0x203/0x2d9 [] generic_writepages+0x23/0x2d [] do_writepages+0x37/0x39 [] __writeback_single_inode+0x96/0x399 [] sync_sb_inodes+0x1a3/0x274 [] writeback_inodes+0xa6/0xd8 [] background_writeout+0x86/0x9e [] pdflush+0xfb/0x1b6 [] kthread+0x37/0x59 [] kernel_thread_helper+0x7/0x14 The deadlock is funny because it starts in pdflush/writeback, and comes back to writeback, then deadlocks. It seems we should look carefully for other places in UBI and MTD and use GFP_NOFS instead of GFP_KERNEL. Caught-by: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nandsim.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 205df0f771..a7574807dc 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -1272,7 +1272,13 @@ static int prog_page(struct nandsim *ns, int num) mypage = NS_GET_PAGE(ns); if (mypage->byte == NULL) { NS_DBG("prog_page: allocating page %d\n", ns->regs.row); - mypage->byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); + /* + * 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. + */ + mypage->byte = kmalloc(ns->geom.pgszoob, GFP_NOFS); if (mypage->byte == NULL) { NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row); return -1; -- cgit v1.2.2 From 5041f1f1b753031731bc404c906817323a9c280b Mon Sep 17 00:00:00 2001 From: Yoichi Yuasa Date: Sun, 9 Sep 2007 23:35:25 +0900 Subject: [MTD] Remove Momenco Ocelot NOR flash support This patch has removed Momenco Ocelot support from MTD. Ocelot support has already removed. Signed-off-by: Yoichi Yuasa Signed-off-by: David Woodhouse --- drivers/mtd/maps/Kconfig | 8 --- drivers/mtd/maps/Makefile | 1 - drivers/mtd/maps/ocelot.c | 175 ---------------------------------------------- 3 files changed, 184 deletions(-) delete mode 100644 drivers/mtd/maps/ocelot.c (limited to 'drivers') diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 5850ccd0dc..1c9716381f 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -384,14 +384,6 @@ config MTD_REDWOOD Redwood board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. -config MTD_OCELOT - tristate "Momenco Ocelot boot flash device" - depends on MOMENCO_OCELOT - help - This enables access routines for the boot flash device and for the - NVRAM on the Momenco Ocelot board. If you have one of these boards - and would like access to either of these, say 'Y'. - config MTD_SOLUTIONENGINE tristate "CFI Flash device mapped on Hitachi SolutionEngine" depends on SUPERH && MTD_CFI && MTD_REDBOOT_PARTS diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index b9019b1b19..53fa2b55b4 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -43,7 +43,6 @@ obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o obj-$(CONFIG_MTD_VMAX) += vmax301.o obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o -obj-$(CONFIG_MTD_OCELOT) += ocelot.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_PCI) += pci.o obj-$(CONFIG_MTD_ALCHEMY) += alchemy-flash.o diff --git a/drivers/mtd/maps/ocelot.c b/drivers/mtd/maps/ocelot.c deleted file mode 100644 index 6977963d78..0000000000 --- a/drivers/mtd/maps/ocelot.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * $Id: ocelot.c,v 1.17 2005/11/07 11:14:27 gleixner Exp $ - * - * Flash on Momenco Ocelot - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define OCELOT_PLD 0x2c000000 -#define FLASH_WINDOW_ADDR 0x2fc00000 -#define FLASH_WINDOW_SIZE 0x00080000 -#define FLASH_BUSWIDTH 1 -#define NVRAM_WINDOW_ADDR 0x2c800000 -#define NVRAM_WINDOW_SIZE 0x00007FF0 -#define NVRAM_BUSWIDTH 1 - -static unsigned int cacheflush = 0; - -static struct mtd_info *flash_mtd; -static struct mtd_info *nvram_mtd; - -static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) -{ - struct map_info *map = mtd->priv; - size_t done = 0; - - /* If we use memcpy, it does word-wide writes. Even though we told the - GT64120A that it's an 8-bit wide region, word-wide writes don't work. - We end up just writing the first byte of the four to all four bytes. - So we have this loop instead */ - *retlen = len; - while(len) { - __raw_writeb(*(unsigned char *) from, map->virt + to); - from++; - to++; - len--; - } -} - -static struct mtd_partition *parsed_parts; - -struct map_info ocelot_flash_map = { - .name = "Ocelot boot flash", - .size = FLASH_WINDOW_SIZE, - .bankwidth = FLASH_BUSWIDTH, - .phys = FLASH_WINDOW_ADDR, -}; - -struct map_info ocelot_nvram_map = { - .name = "Ocelot NVRAM", - .size = NVRAM_WINDOW_SIZE, - .bankwidth = NVRAM_BUSWIDTH, - .phys = NVRAM_WINDOW_ADDR, -}; - -static const char *probes[] = { "RedBoot", NULL }; - -static int __init init_ocelot_maps(void) -{ - void *pld; - int nr_parts; - unsigned char brd_status; - - printk(KERN_INFO "Momenco Ocelot MTD mappings: Flash 0x%x at 0x%x, NVRAM 0x%x at 0x%x\n", - FLASH_WINDOW_SIZE, FLASH_WINDOW_ADDR, NVRAM_WINDOW_SIZE, NVRAM_WINDOW_ADDR); - - /* First check whether the flash jumper is present */ - pld = ioremap(OCELOT_PLD, 0x10); - if (!pld) { - printk(KERN_NOTICE "Failed to ioremap Ocelot PLD\n"); - return -EIO; - } - brd_status = readb(pld+4); - iounmap(pld); - - /* Now ioremap the NVRAM space */ - ocelot_nvram_map.virt = ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE); - if (!ocelot_nvram_map.virt) { - printk(KERN_NOTICE "Failed to ioremap Ocelot NVRAM space\n"); - return -EIO; - } - - simple_map_init(&ocelot_nvram_map); - - /* And do the RAM probe on it to get an MTD device */ - nvram_mtd = do_map_probe("map_ram", &ocelot_nvram_map); - if (!nvram_mtd) { - printk("NVRAM probe failed\n"); - goto fail_1; - } - nvram_mtd->owner = THIS_MODULE; - nvram_mtd->erasesize = 16; - /* Override the write() method */ - nvram_mtd->write = ocelot_ram_write; - - /* Now map the flash space */ - ocelot_flash_map.virt = ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE); - if (!ocelot_flash_map.virt) { - printk(KERN_NOTICE "Failed to ioremap Ocelot flash space\n"); - goto fail_2; - } - /* Now the cached version */ - ocelot_flash_map.cached = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0); - - simple_map_init(&ocelot_flash_map); - - /* Only probe for flash if the write jumper is present */ - if (brd_status & 0x40) { - flash_mtd = do_map_probe("jedec", &ocelot_flash_map); - } else { - printk(KERN_NOTICE "Ocelot flash write jumper not present. Treating as ROM\n"); - } - /* If that failed or the jumper's absent, pretend it's ROM */ - if (!flash_mtd) { - flash_mtd = do_map_probe("map_rom", &ocelot_flash_map); - /* If we're treating it as ROM, set the erase size */ - if (flash_mtd) - flash_mtd->erasesize = 0x10000; - } - if (!flash_mtd) - goto fail3; - - add_mtd_device(nvram_mtd); - - flash_mtd->owner = THIS_MODULE; - nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0); - - if (nr_parts > 0) - add_mtd_partitions(flash_mtd, parsed_parts, nr_parts); - else - add_mtd_device(flash_mtd); - - return 0; - - fail3: - iounmap((void *)ocelot_flash_map.virt); - if (ocelot_flash_map.cached) - iounmap((void *)ocelot_flash_map.cached); - fail_2: - map_destroy(nvram_mtd); - fail_1: - iounmap((void *)ocelot_nvram_map.virt); - - return -ENXIO; -} - -static void __exit cleanup_ocelot_maps(void) -{ - del_mtd_device(nvram_mtd); - map_destroy(nvram_mtd); - iounmap((void *)ocelot_nvram_map.virt); - - if (parsed_parts) - del_mtd_partitions(flash_mtd); - else - del_mtd_device(flash_mtd); - map_destroy(flash_mtd); - iounmap((void *)ocelot_flash_map.virt); - if (ocelot_flash_map.cached) - iounmap((void *)ocelot_flash_map.cached); -} - -module_init(init_ocelot_maps); -module_exit(cleanup_ocelot_maps); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Red Hat, Inc. - David Woodhouse "); -MODULE_DESCRIPTION("MTD map driver for Momenco Ocelot board"); -- cgit v1.2.2 From e644f7d6289456657996df4192de76c5d0a9f9c7 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 7 Nov 2005 21:47:48 +0000 Subject: [MTD] MAPS: Merge Lubbock and Mainstone drivers into common PXA2xx driver Replace Lubbock and Mainstone board drivers with common PXA2xx driver, convert to platform driver (corresponding platform device changes merged to kernel.org for 2.6.15), add power management callbacks. Signed-off-by: Todd Poynor Signed-off-by: Nicolas Pitre Signed-off-by: David Woodhouse --- drivers/mtd/maps/Kconfig | 16 +-- drivers/mtd/maps/Makefile | 3 +- drivers/mtd/maps/lubbock-flash.c | 170 ------------------------------- drivers/mtd/maps/mainstone-flash.c | 181 --------------------------------- drivers/mtd/maps/pxa2xx-flash.c | 200 +++++++++++++++++++++++++++++++++++++ 5 files changed, 205 insertions(+), 365 deletions(-) delete mode 100644 drivers/mtd/maps/lubbock-flash.c delete mode 100644 drivers/mtd/maps/mainstone-flash.c create mode 100644 drivers/mtd/maps/pxa2xx-flash.c (limited to 'drivers') diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 1c9716381f..7559950e42 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -163,20 +163,12 @@ config MTD_SBC_GXX More info at . -config MTD_LUBBOCK - tristate "CFI Flash device mapped on Intel Lubbock XScale eval board" - depends on ARCH_LUBBOCK && MTD_CFI_INTELEXT && MTD_PARTITIONS - help - This provides a driver for the on-board flash of the Intel - 'Lubbock' XScale evaluation board. - -config MTD_MAINSTONE - tristate "CFI Flash device mapped on Intel Mainstone XScale eval board" - depends on MACH_MAINSTONE && MTD_CFI_INTELEXT +config MTD_PXA2XX + tristate "CFI Flash device mapped on Intel XScale PXA2xx based boards" + depends on (PXA25x || PXA27x) && MTD_CFI_INTELEXT select MTD_PARTITIONS help - This provides a driver for the on-board flash of the Intel - 'Mainstone PXA27x evaluation board. + This provides a driver for the NOR flash attached to a PXA2xx chip. config MTD_OCTAGON tristate "JEDEC Flash device mapped on Octagon 5066 SBC" diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 53fa2b55b4..1313eee61a 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -20,8 +20,7 @@ obj-$(CONFIG_MTD_ESB2ROM) += esb2rom.o obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o -obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o -obj-$(CONFIG_MTD_MAINSTONE) += mainstone-flash.o +obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o obj-$(CONFIG_MTD_MBX860) += mbx860.o obj-$(CONFIG_MTD_CEIVA) += ceiva.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o diff --git a/drivers/mtd/maps/lubbock-flash.c b/drivers/mtd/maps/lubbock-flash.c deleted file mode 100644 index 1aa0447c5e..0000000000 --- a/drivers/mtd/maps/lubbock-flash.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * $Id: lubbock-flash.c,v 1.21 2005/11/07 11:14:27 gleixner Exp $ - * - * Map driver for the Lubbock developer platform. - * - * Author: Nicolas Pitre - * Copyright: (C) 2001 MontaVista Software Inc. - * - * 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. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - - -#define ROM_ADDR 0x00000000 -#define FLASH_ADDR 0x04000000 - -#define WINDOW_SIZE 64*1024*1024 - -static void lubbock_map_inval_cache(struct map_info *map, unsigned long from, ssize_t len) -{ - consistent_sync((char *)map->cached + from, len, DMA_FROM_DEVICE); -} - -static struct map_info lubbock_maps[2] = { { - .size = WINDOW_SIZE, - .phys = 0x00000000, - .inval_cache = lubbock_map_inval_cache, -}, { - .size = WINDOW_SIZE, - .phys = 0x04000000, - .inval_cache = lubbock_map_inval_cache, -} }; - -static struct mtd_partition lubbock_partitions[] = { - { - .name = "Bootloader", - .size = 0x00040000, - .offset = 0, - .mask_flags = MTD_WRITEABLE /* force read-only */ - },{ - .name = "Kernel", - .size = 0x00100000, - .offset = 0x00040000, - },{ - .name = "Filesystem", - .size = MTDPART_SIZ_FULL, - .offset = 0x00140000 - } -}; - -static struct mtd_info *mymtds[2]; -static struct mtd_partition *parsed_parts[2]; -static int nr_parsed_parts[2]; - -static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; - -static int __init init_lubbock(void) -{ - int flashboot = (LUB_CONF_SWITCHES & 1); - int ret = 0, i; - - lubbock_maps[0].bankwidth = lubbock_maps[1].bankwidth = - (BOOT_DEF & 1) ? 2 : 4; - - /* Compensate for the nROMBT switch which swaps the flash banks */ - printk(KERN_NOTICE "Lubbock configured to boot from %s (bank %d)\n", - flashboot?"Flash":"ROM", flashboot); - - lubbock_maps[flashboot^1].name = "Lubbock Application Flash"; - lubbock_maps[flashboot].name = "Lubbock Boot ROM"; - - for (i = 0; i < 2; i++) { - lubbock_maps[i].virt = ioremap(lubbock_maps[i].phys, WINDOW_SIZE); - if (!lubbock_maps[i].virt) { - printk(KERN_WARNING "Failed to ioremap %s\n", lubbock_maps[i].name); - if (!ret) - ret = -ENOMEM; - continue; - } - lubbock_maps[i].cached = ioremap_cached(lubbock_maps[i].phys, WINDOW_SIZE); - if (!lubbock_maps[i].cached) - printk(KERN_WARNING "Failed to ioremap cached %s\n", lubbock_maps[i].name); - simple_map_init(&lubbock_maps[i]); - - printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit bankwidth)\n", - lubbock_maps[i].name, lubbock_maps[i].phys, - lubbock_maps[i].bankwidth * 8); - - mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]); - - if (!mymtds[i]) { - iounmap((void *)lubbock_maps[i].virt); - if (lubbock_maps[i].cached) - iounmap(lubbock_maps[i].cached); - if (!ret) - ret = -EIO; - continue; - } - mymtds[i]->owner = THIS_MODULE; - - ret = parse_mtd_partitions(mymtds[i], probes, - &parsed_parts[i], 0); - - if (ret > 0) - nr_parsed_parts[i] = ret; - } - - if (!mymtds[0] && !mymtds[1]) - return ret; - - for (i = 0; i < 2; i++) { - if (!mymtds[i]) { - printk(KERN_WARNING "%s is absent. Skipping\n", lubbock_maps[i].name); - } else if (nr_parsed_parts[i]) { - add_mtd_partitions(mymtds[i], parsed_parts[i], nr_parsed_parts[i]); - } else if (!i) { - printk("Using static partitions on %s\n", lubbock_maps[i].name); - add_mtd_partitions(mymtds[i], lubbock_partitions, ARRAY_SIZE(lubbock_partitions)); - } else { - printk("Registering %s as whole device\n", lubbock_maps[i].name); - add_mtd_device(mymtds[i]); - } - } - return 0; -} - -static void __exit cleanup_lubbock(void) -{ - int i; - for (i = 0; i < 2; i++) { - if (!mymtds[i]) - continue; - - if (nr_parsed_parts[i] || !i) - del_mtd_partitions(mymtds[i]); - else - del_mtd_device(mymtds[i]); - - map_destroy(mymtds[i]); - iounmap((void *)lubbock_maps[i].virt); - if (lubbock_maps[i].cached) - iounmap(lubbock_maps[i].cached); - - kfree(parsed_parts[i]); - } -} - -module_init(init_lubbock); -module_exit(cleanup_lubbock); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Nicolas Pitre "); -MODULE_DESCRIPTION("MTD map driver for Intel Lubbock"); diff --git a/drivers/mtd/maps/mainstone-flash.c b/drivers/mtd/maps/mainstone-flash.c deleted file mode 100644 index eaa4bbb868..0000000000 --- a/drivers/mtd/maps/mainstone-flash.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * $Id: $ - * - * Map driver for the Mainstone developer platform. - * - * Author: Nicolas Pitre - * Copyright: (C) 2001 MontaVista Software Inc. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - - -#define ROM_ADDR 0x00000000 -#define FLASH_ADDR 0x04000000 - -#define WINDOW_SIZE 0x04000000 - -static void mainstone_map_inval_cache(struct map_info *map, unsigned long from, - ssize_t len) -{ - consistent_sync((char *)map->cached + from, len, DMA_FROM_DEVICE); -} - -static struct map_info mainstone_maps[2] = { { - .size = WINDOW_SIZE, - .phys = PXA_CS0_PHYS, - .inval_cache = mainstone_map_inval_cache, -}, { - .size = WINDOW_SIZE, - .phys = PXA_CS1_PHYS, - .inval_cache = mainstone_map_inval_cache, -} }; - -static struct mtd_partition mainstone_partitions[] = { - { - .name = "Bootloader", - .size = 0x00040000, - .offset = 0, - .mask_flags = MTD_WRITEABLE /* force read-only */ - },{ - .name = "Kernel", - .size = 0x00400000, - .offset = 0x00040000, - },{ - .name = "Filesystem", - .size = MTDPART_SIZ_FULL, - .offset = 0x00440000 - } -}; - -static struct mtd_info *mymtds[2]; -static struct mtd_partition *parsed_parts[2]; -static int nr_parsed_parts[2]; - -static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; - -static int __init init_mainstone(void) -{ - int SW7 = 0; /* FIXME: get from SCR (Mst doc section 3.2.1.1) */ - int ret = 0, i; - - mainstone_maps[0].bankwidth = (BOOT_DEF & 1) ? 2 : 4; - mainstone_maps[1].bankwidth = 4; - - /* Compensate for SW7 which swaps the flash banks */ - mainstone_maps[SW7].name = "processor flash"; - mainstone_maps[SW7 ^ 1].name = "main board flash"; - - printk(KERN_NOTICE "Mainstone configured to boot from %s\n", - mainstone_maps[0].name); - - for (i = 0; i < 2; i++) { - mainstone_maps[i].virt = ioremap(mainstone_maps[i].phys, - WINDOW_SIZE); - if (!mainstone_maps[i].virt) { - printk(KERN_WARNING "Failed to ioremap %s\n", - mainstone_maps[i].name); - if (!ret) - ret = -ENOMEM; - continue; - } - mainstone_maps[i].cached = - ioremap_cached(mainstone_maps[i].phys, WINDOW_SIZE); - if (!mainstone_maps[i].cached) - printk(KERN_WARNING "Failed to ioremap cached %s\n", - mainstone_maps[i].name); - simple_map_init(&mainstone_maps[i]); - - printk(KERN_NOTICE - "Probing %s at physical address 0x%08lx" - " (%d-bit bankwidth)\n", - mainstone_maps[i].name, mainstone_maps[i].phys, - mainstone_maps[i].bankwidth * 8); - - mymtds[i] = do_map_probe("cfi_probe", &mainstone_maps[i]); - - if (!mymtds[i]) { - iounmap((void *)mainstone_maps[i].virt); - if (mainstone_maps[i].cached) - iounmap(mainstone_maps[i].cached); - if (!ret) - ret = -EIO; - continue; - } - mymtds[i]->owner = THIS_MODULE; - - ret = parse_mtd_partitions(mymtds[i], probes, - &parsed_parts[i], 0); - - if (ret > 0) - nr_parsed_parts[i] = ret; - } - - if (!mymtds[0] && !mymtds[1]) - return ret; - - for (i = 0; i < 2; i++) { - if (!mymtds[i]) { - printk(KERN_WARNING "%s is absent. Skipping\n", - mainstone_maps[i].name); - } else if (nr_parsed_parts[i]) { - add_mtd_partitions(mymtds[i], parsed_parts[i], - nr_parsed_parts[i]); - } else if (!i) { - printk("Using static partitions on %s\n", - mainstone_maps[i].name); - add_mtd_partitions(mymtds[i], mainstone_partitions, - ARRAY_SIZE(mainstone_partitions)); - } else { - printk("Registering %s as whole device\n", - mainstone_maps[i].name); - add_mtd_device(mymtds[i]); - } - } - return 0; -} - -static void __exit cleanup_mainstone(void) -{ - int i; - for (i = 0; i < 2; i++) { - if (!mymtds[i]) - continue; - - if (nr_parsed_parts[i] || !i) - del_mtd_partitions(mymtds[i]); - else - del_mtd_device(mymtds[i]); - - map_destroy(mymtds[i]); - iounmap((void *)mainstone_maps[i].virt); - if (mainstone_maps[i].cached) - iounmap(mainstone_maps[i].cached); - kfree(parsed_parts[i]); - } -} - -module_init(init_mainstone); -module_exit(cleanup_mainstone); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Nicolas Pitre "); -MODULE_DESCRIPTION("MTD map driver for Intel Mainstone"); diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c new file mode 100644 index 0000000000..cb933ac475 --- /dev/null +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -0,0 +1,200 @@ +/* + * Map driver for Intel XScale PXA2xx platforms. + * + * Author: Nicolas Pitre + * Copyright: (C) 2001 MontaVista Software Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +static void pxa2xx_map_inval_cache(struct map_info *map, unsigned long from, + ssize_t len) +{ + consistent_sync((char *)map->cached + from, len, DMA_FROM_DEVICE); +} + +struct pxa2xx_flash_info { + struct mtd_partition *parts; + int nr_parts; + struct mtd_info *mtd; + struct map_info map; +}; + + +static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + + +static int __init pxa2xx_flash_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct flash_platform_data *flash = pdev->dev.platform_data; + struct pxa2xx_flash_info *info; + struct mtd_partition *parts; + struct resource *res; + int ret = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + info = kmalloc(sizeof(struct pxa2xx_flash_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + memset(info, 0, sizeof(struct pxa2xx_flash_info)); + info->map.name = (char *) flash->name; + info->map.bankwidth = flash->width; + info->map.phys = res->start; + info->map.size = res->end - res->start + 1; + info->parts = flash->parts; + info->nr_parts = flash->nr_parts; + + info->map.virt = ioremap(info->map.phys, info->map.size); + if (!info->map.virt) { + printk(KERN_WARNING "Failed to ioremap %s\n", + info->map.name); + return -ENOMEM; + } + info->map.cached = + ioremap_cached(info->map.phys, info->map.size); + if (!info->map.cached) + printk(KERN_WARNING "Failed to ioremap cached %s\n", + info->map.name); + info->map.inval_cache = pxa2xx_map_inval_cache; + simple_map_init(&info->map); + + printk(KERN_NOTICE + "Probing %s at physical address 0x%08lx" + " (%d-bit bankwidth)\n", + info->map.name, (unsigned long)info->map.phys, + info->map.bankwidth * 8); + + info->mtd = do_map_probe(flash->map_name, &info->map); + + if (!info->mtd) { + iounmap((void *)info->map.virt); + if (info->map.cached) + iounmap(info->map.cached); + return -EIO; + } + info->mtd->owner = THIS_MODULE; + +#ifdef CONFIG_MTD_PARTITIONS + ret = parse_mtd_partitions(info->mtd, probes, &parts, 0); + + if (ret > 0) { + info->nr_parts = ret; + info->parts = parts; + } +#endif + + if (info->nr_parts) { + add_mtd_partitions(info->mtd, info->parts, + info->nr_parts); + } else { + printk("Registering %s as whole device\n", + info->map.name); + add_mtd_device(info->mtd); + } + + dev_set_drvdata(dev, info); + return 0; +} + +static int __exit pxa2xx_flash_remove(struct device *dev) +{ + struct pxa2xx_flash_info *info = dev_get_drvdata(dev); + + dev_set_drvdata(dev, NULL); + +#ifdef CONFIG_MTD_PARTITIONS + if (info->nr_parts) + del_mtd_partitions(info->mtd); + else +#endif + del_mtd_device(info->mtd); + + map_destroy(info->mtd); + iounmap(info->map.virt); + if (info->map.cached) + iounmap(info->map.cached); + kfree(info->parts); + kfree(info); + return 0; +} + +#ifdef CONFIG_PM +static int pxa2xx_flash_suspend(struct device *dev, pm_message_t state) +{ + struct pxa2xx_flash_info *info = dev_get_drvdata(dev); + int ret = 0; + + if (info->mtd && info->mtd->suspend) + ret = info->mtd->suspend(info->mtd); + return ret; +} + +static int pxa2xx_flash_resume(struct device *dev) +{ + struct pxa2xx_flash_info *info = dev_get_drvdata(dev); + + if (info->mtd && info->mtd->resume) + info->mtd->resume(info->mtd); + return 0; +} +static void pxa2xx_flash_shutdown(struct device *dev) +{ + struct pxa2xx_flash_info *info = dev_get_drvdata(dev); + + if (info && info->mtd->suspend(info->mtd) == 0) + info->mtd->resume(info->mtd); +} +#else +#define pxa2xx_flash_suspend NULL +#define pxa2xx_flash_resume NULL +#define pxa2xx_flash_shutdown NULL +#endif + +static struct device_driver pxa2xx_flash_driver = { + .name = "pxa2xx-flash", + .bus = &platform_bus_type, + .probe = pxa2xx_flash_probe, + .remove = __exit_p(pxa2xx_flash_remove), + .suspend = pxa2xx_flash_suspend, + .resume = pxa2xx_flash_resume, + .shutdown = pxa2xx_flash_shutdown, +}; + +static int __init init_pxa2xx_flash(void) +{ + return driver_register(&pxa2xx_flash_driver); +} + +static void __exit cleanup_pxa2xx_flash(void) +{ + driver_unregister(&pxa2xx_flash_driver); +} + +module_init(init_pxa2xx_flash); +module_exit(cleanup_pxa2xx_flash); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nicolas Pitre "); +MODULE_DESCRIPTION("MTD map driver for Intel XScale PXA2xx"); -- cgit v1.2.2 From 097f2576eb7dbc8cd5f610847f229f4fea305b80 Mon Sep 17 00:00:00 2001 From: Andy Lowe Date: Fri, 12 Jan 2007 18:05:10 -0500 Subject: [MTD] fix CFI point method for discontiguous maps The CFI probe routine is capable of detecting flash banks consisting of identical chips mapped to physically discontiguous addresses. (One common way this can occur is if a flash bank is populated with chips of less capacity than the hardware was designed to support.) The CFI point() routine currently ignores any such gaps. This patch fixes the CFI point() routine so that it truncates any request that would span a gap. Signed-off-by: Andy Lowe Signed-off-by: Nicolas Pitre Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 39eff9ff57..c655e971c1 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -1166,28 +1166,34 @@ static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, si { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - unsigned long ofs; + unsigned long ofs, last_end = 0; int chipnum; int ret = 0; if (!map->virt || (from + len > mtd->size)) return -EINVAL; - *mtdbuf = (void *)map->virt + from; - *retlen = 0; - /* Now lock the chip(s) to POINT state */ /* ofs: offset within the first chip that the first read should start */ chipnum = (from >> cfi->chipshift); ofs = from - (chipnum << cfi->chipshift); + *mtdbuf = (void *)map->virt + cfi->chips[chipnum].start + ofs; + *retlen = 0; + while (len) { unsigned long thislen; if (chipnum >= cfi->numchips) break; + /* We cannot point across chips that are virtually disjoint */ + if (!last_end) + last_end = cfi->chips[chipnum].start; + else if (cfi->chips[chipnum].start != last_end) + break; + if ((len + ofs -1) >> cfi->chipshift) thislen = (1<chipshift) - ofs; else @@ -1201,6 +1207,7 @@ static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, si len -= thislen; ofs = 0; + last_end += 1 << cfi->chipshift; chipnum++; } return 0; -- cgit v1.2.2 From 0bac5111cc00b70460dd8ba8340522e1f0d79f05 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 23 Sep 2007 18:51:25 +0100 Subject: [MTD] map driver for NOR flash on the Intel Vermilion Range chipset The Vermilion Range Expansion Bus supports four chip selects, each of which has 64MiB of address space. The 2nd BAR of the Expansion Bus PCI Device is a 256MiB memory region containing the address spaces for all four of the chip selects, with start addresses hardcoded on 64MiB boundaries. This map driver only supports NOR flash on chip select 0. The buswidth (either 8 bits or 16 bits) is determined by reading the Expansion Bus Timing and Control Register for Chip Select 0 (EXP_TIMING_CS0). Signed-off-by: Andy Lowe Signed-off-by: David Woodhouse --- drivers/mtd/maps/Kconfig | 7 + drivers/mtd/maps/Makefile | 1 + drivers/mtd/maps/intel_vr_nor.c | 298 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 306 insertions(+) create mode 100644 drivers/mtd/maps/intel_vr_nor.c (limited to 'drivers') diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 7559950e42..c2624b2655 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -581,6 +581,13 @@ config MTD_SHARP_SL help This enables access to the flash chip on the Sharp SL Series of PDAs. +config MTD_INTEL_VR_NOR + tristate "NOR flash on Intel Vermilion Range Expansion Bus CS0" + depends on PCI + help + Map driver for a NOR flash bank located on the Expansion Bus of the + Intel Vermilion Range chipset. + config MTD_PLATRAM tristate "Map driver for platform device RAM (mtd-ram)" select MTD_RAM diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 1313eee61a..316382a140 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -68,3 +68,4 @@ obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o obj-$(CONFIG_MTD_MTX1) += mtx-1_flash.o +obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c new file mode 100644 index 0000000000..1e7814ae21 --- /dev/null +++ b/drivers/mtd/maps/intel_vr_nor.c @@ -0,0 +1,298 @@ +/* + * drivers/mtd/maps/intel_vr_nor.c + * + * An MTD map driver for a NOR flash bank on the Expansion Bus of the Intel + * Vermilion Range chipset. + * + * The Vermilion Range Expansion Bus supports four chip selects, each of which + * has 64MiB of address space. The 2nd BAR of the Expansion Bus PCI Device + * is a 256MiB memory region containing the address spaces for all four of the + * chip selects, with start addresses hardcoded on 64MiB boundaries. + * + * This map driver only supports NOR flash on chip select 0. The buswidth + * (either 8 bits or 16 bits) is determined by reading the Expansion Bus Timing + * and Control Register for Chip Select 0 (EXP_TIMING_CS0). This driver does + * not modify the value in the EXP_TIMING_CS0 register except to enable writing + * and disable boot acceleration. The timing parameters in the register are + * assumed to have been properly initialized by the BIOS. The reset default + * timing parameters are maximally conservative (slow), so access to the flash + * will be slower than it should be if the BIOS has not initialized the timing + * parameters. + * + * Author: Andy Lowe + * + * 2006 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "vr_nor" + +struct vr_nor_mtd { + void __iomem *csr_base; + struct map_info map; + struct mtd_info *info; + int nr_parts; + struct pci_dev *dev; +}; + +/* Expansion Bus Configuration and Status Registers are in BAR 0 */ +#define EXP_CSR_MBAR 0 +/* Expansion Bus Memory Window is BAR 1 */ +#define EXP_WIN_MBAR 1 +/* Maximum address space for Chip Select 0 is 64MiB */ +#define CS0_SIZE 0x04000000 +/* Chip Select 0 is at offset 0 in the Memory Window */ +#define CS0_START 0x0 +/* Chip Select 0 Timing Register is at offset 0 in CSR */ +#define EXP_TIMING_CS0 0x00 +#define TIMING_CS_EN (1 << 31) /* Chip Select Enable */ +#define TIMING_BOOT_ACCEL_DIS (1 << 8) /* Boot Acceleration Disable */ +#define TIMING_WR_EN (1 << 1) /* Write Enable */ +#define TIMING_BYTE_EN (1 << 0) /* 8-bit vs 16-bit bus */ +#define TIMING_MASK 0x3FFF0000 + +static void __devexit vr_nor_destroy_partitions(struct vr_nor_mtd *p) +{ + if (p->nr_parts > 0) { +#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE) + del_mtd_partitions(p->info); +#endif + } else + del_mtd_device(p->info); +} + +static int __devinit vr_nor_init_partitions(struct vr_nor_mtd *p) +{ + int err = 0; +#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE) + struct mtd_partition *parts; + static const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + + /* register the flash bank */ +#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE) + /* partition the flash bank */ + p->nr_parts = parse_mtd_partitions(p->info, part_probes, &parts, 0); + if (p->nr_parts > 0) + err = add_mtd_partitions(p->info, parts, p->nr_parts); +#endif + if (p->nr_parts <= 0) + err = add_mtd_device(p->info); + + return err; +} + +static void __devexit vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p) +{ + map_destroy(p->info); +} + +static int __devinit vr_nor_mtd_setup(struct vr_nor_mtd *p) +{ + static const char *probe_types[] = + { "cfi_probe", "jedec_probe", NULL }; + const char **type; + + for (type = probe_types; !p->info && *type; type++) + p->info = do_map_probe(*type, &p->map); + if (!p->info) + return -ENODEV; + + p->info->owner = THIS_MODULE; + + return 0; +} + +static void __devexit vr_nor_destroy_maps(struct vr_nor_mtd *p) +{ + unsigned int exp_timing_cs0; + + /* write-protect the flash bank */ + exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0); + exp_timing_cs0 &= ~TIMING_WR_EN; + writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0); + + /* unmap the flash window */ + iounmap(p->map.virt); + + /* unmap the csr window */ + iounmap(p->csr_base); +} + +/* + * Initialize the map_info structure and map the flash. + * Returns 0 on success, nonzero otherwise. + */ +static int __devinit vr_nor_init_maps(struct vr_nor_mtd *p) +{ + unsigned long csr_phys, csr_len; + unsigned long win_phys, win_len; + unsigned int exp_timing_cs0; + int err; + + csr_phys = pci_resource_start(p->dev, EXP_CSR_MBAR); + csr_len = pci_resource_len(p->dev, EXP_CSR_MBAR); + win_phys = pci_resource_start(p->dev, EXP_WIN_MBAR); + win_len = pci_resource_len(p->dev, EXP_WIN_MBAR); + + if (!csr_phys || !csr_len || !win_phys || !win_len) + return -ENODEV; + + if (win_len < (CS0_START + CS0_SIZE)) + return -ENXIO; + + p->csr_base = ioremap_nocache(csr_phys, csr_len); + if (!p->csr_base) + return -ENOMEM; + + exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0); + if (!(exp_timing_cs0 & TIMING_CS_EN)) { + dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 " + "is disabled.\n"); + err = -ENODEV; + goto release; + } + if ((exp_timing_cs0 & TIMING_MASK) == TIMING_MASK) { + dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 " + "is configured for maximally slow access times.\n"); + } + p->map.name = DRV_NAME; + p->map.bankwidth = (exp_timing_cs0 & TIMING_BYTE_EN) ? 1 : 2; + p->map.phys = win_phys + CS0_START; + p->map.size = CS0_SIZE; + p->map.virt = ioremap_nocache(p->map.phys, p->map.size); + if (!p->map.virt) { + err = -ENOMEM; + goto release; + } + simple_map_init(&p->map); + + /* Enable writes to flash bank */ + exp_timing_cs0 |= TIMING_BOOT_ACCEL_DIS | TIMING_WR_EN; + writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0); + + return 0; + + release: + iounmap(p->csr_base); + return err; +} + +static struct pci_device_id vr_nor_pci_ids[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x500D)}, + {0,} +}; + +static void __devexit vr_nor_pci_remove(struct pci_dev *dev) +{ + struct vr_nor_mtd *p = pci_get_drvdata(dev); + + pci_set_drvdata(dev, NULL); + vr_nor_destroy_partitions(p); + vr_nor_destroy_mtd_setup(p); + vr_nor_destroy_maps(p); + kfree(p); + pci_release_regions(dev); + pci_disable_device(dev); +} + +static int __devinit +vr_nor_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct vr_nor_mtd *p = NULL; + unsigned int exp_timing_cs0; + int err; + + err = pci_enable_device(dev); + if (err) + goto out; + + err = pci_request_regions(dev, DRV_NAME); + if (err) + goto disable_dev; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + err = -ENOMEM; + if (!p) + goto release; + + p->dev = dev; + + err = vr_nor_init_maps(p); + if (err) + goto release; + + err = vr_nor_mtd_setup(p); + if (err) + goto destroy_maps; + + err = vr_nor_init_partitions(p); + if (err) + goto destroy_mtd_setup; + + pci_set_drvdata(dev, p); + + return 0; + + destroy_mtd_setup: + map_destroy(p->info); + + destroy_maps: + /* write-protect the flash bank */ + exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0); + exp_timing_cs0 &= ~TIMING_WR_EN; + writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0); + + /* unmap the flash window */ + iounmap(p->map.virt); + + /* unmap the csr window */ + iounmap(p->csr_base); + + release: + kfree(p); + pci_release_regions(dev); + + disable_dev: + pci_disable_device(dev); + + out: + return err; +} + +static struct pci_driver vr_nor_pci_driver = { + .name = DRV_NAME, + .probe = vr_nor_pci_probe, + .remove = __devexit_p(vr_nor_pci_remove), + .id_table = vr_nor_pci_ids, +}; + +static int __init vr_nor_mtd_init(void) +{ + return pci_register_driver(&vr_nor_pci_driver); +} + +static void __exit vr_nor_mtd_exit(void) +{ + pci_unregister_driver(&vr_nor_pci_driver); +} + +module_init(vr_nor_mtd_init); +module_exit(vr_nor_mtd_exit); + +MODULE_AUTHOR("Andy Lowe"); +MODULE_DESCRIPTION("MTD map driver for NOR flash on Intel Vermilion Range"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, vr_nor_pci_ids); -- cgit v1.2.2 From 49dc08eeda707f59019814fe07a2b17979348002 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 21 Sep 2007 19:35:21 +0300 Subject: [MTD] [OneNAND] fix numerous races This patch make the OneNAND driver much less racy. It fixes our "onenand_wait: read timeout!" heisenbugs. The reason of these bugs was that the driver did not lock the chip when accessing OTP, and it screwed up OneNAND state when the OTP was read while JFFS2 was doing FS checking. This patch also fixes other races I spotted: 1. BBT was not protected 2. Access to ecc_stats was not protected Now the chip is locked when BBT is accessed. To fix all of these I basically split all interface functions on 'function()' and 'function_nolock()' parts. I tested this patch on N800 hardware - it fixes our problems. But I tested a little different version because our OneNAND codebase is slightly out-of-date. But it should be OK. This patch also includes the prin fixes I posted before. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 170 +++++++++++++++++++++---------------- 1 file changed, 95 insertions(+), 75 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 40d8d6ff62..85a97198be 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -333,12 +333,14 @@ static int onenand_wait(struct mtd_info *mtd, int state) if (interrupt & ONENAND_INT_READ) { int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); if (ecc) { - printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); if (ecc & ONENAND_ECC_2BIT_ALL) { + printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); mtd->ecc_stats.failed++; return ecc; - } else if (ecc & ONENAND_ECC_1BIT_ALL) + } else if (ecc & ONENAND_ECC_1BIT_ALL) { + printk(KERN_INFO "onenand_wait: correctable ECC error = 0x%04x\n", ecc); mtd->ecc_stats.corrected++; + } } } else if (state == FL_READING) { printk(KERN_ERR "onenand_wait: read timeout! ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); @@ -805,14 +807,14 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col } /** - * onenand_read_ops - [OneNAND Interface] OneNAND read main and/or out-of-band + * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band * @param mtd MTD device structure * @param from offset to read from * @param ops: oob operation description structure * * OneNAND read main and/or out-of-band data */ -static int onenand_read_ops(struct mtd_info *mtd, loff_t from, +static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { struct onenand_chip *this = mtd->priv; @@ -826,7 +828,7 @@ static int onenand_read_ops(struct mtd_info *mtd, loff_t from, int ret = 0, boundary = 0; int writesize = this->writesize; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); if (ops->mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; @@ -837,15 +839,12 @@ static int onenand_read_ops(struct mtd_info *mtd, loff_t from, /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { - printk(KERN_ERR "onenand_read_ops: Attempt read beyond end of device\n"); + printk(KERN_ERR "onenand_read_ops_nolock: Attempt read beyond end of device\n"); ops->retlen = 0; ops->oobretlen = 0; return -EINVAL; } - /* Grab the lock and see if the device is available */ - onenand_get_device(mtd, FL_READING); - stats = mtd->ecc_stats; /* Read-while-load method */ @@ -916,9 +915,6 @@ static int onenand_read_ops(struct mtd_info *mtd, loff_t from, onenand_update_bufferram(mtd, from, !ret); } - /* Deselect and wake up anyone waiting on the device */ - onenand_release_device(mtd); - /* * Return success, if no ECC failures, else -EBADMSG * fs driver will take care of that, because @@ -937,14 +933,14 @@ static int onenand_read_ops(struct mtd_info *mtd, loff_t from, } /** - * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band + * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band * @param mtd MTD device structure * @param from offset to read from * @param ops: oob operation description structure * * OneNAND read out-of-band data from the spare area */ -static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, +static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { struct onenand_chip *this = mtd->priv; @@ -956,7 +952,7 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, from += ops->ooboffs; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); /* Initialize return length value */ ops->oobretlen = 0; @@ -969,7 +965,7 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, column = from & (mtd->oobsize - 1); if (unlikely(column >= oobsize)) { - printk(KERN_ERR "onenand_read_oob: Attempted to start read outside oob\n"); + printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n"); return -EINVAL; } @@ -977,13 +973,10 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, if (unlikely(from >= mtd->size || column + len > ((mtd->size >> this->page_shift) - (from >> this->page_shift)) * oobsize)) { - printk(KERN_ERR "onenand_read_oob: Attempted to read beyond end of device\n"); + printk(KERN_ERR "onenand_read_oob_nolock: Attempted to read beyond end of device\n"); return -EINVAL; } - /* Grab the lock and see if the device is available */ - onenand_get_device(mtd, FL_READING); - while (read < len) { cond_resched(); @@ -1003,7 +996,7 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); if (ret) { - printk(KERN_ERR "onenand_read_oob: read failed = 0x%x\n", ret); + printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); break; } @@ -1022,9 +1015,6 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, } } - /* Deselect and wake up anyone waiting on the device */ - onenand_release_device(mtd); - ops->oobretlen = read; return ret; } @@ -1050,9 +1040,11 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, }; int ret; - ret = onenand_read_ops(mtd, from, &ops); - *retlen = ops.retlen; + onenand_get_device(mtd, FL_READING); + ret = onenand_read_ops_nolock(mtd, from, &ops); + onenand_release_device(mtd); + *retlen = ops.retlen; return ret; } @@ -1067,6 +1059,8 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, static int onenand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { + int ret; + switch (ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_AUTO: @@ -1077,10 +1071,14 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, return -EINVAL; } + onenand_get_device(mtd, FL_READING); if (ops->datbuf) - return onenand_read_ops(mtd, from, ops); + ret = onenand_read_ops_nolock(mtd, from, ops); + else + ret = onenand_read_oob_nolock(mtd, from, ops); + onenand_release_device(mtd); - return onenand_do_read_oob(mtd, from, ops); + return ret; } /** @@ -1317,14 +1315,14 @@ static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, } /** - * onenand_write_ops - [OneNAND Interface] write main and/or out-of-band + * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band * @param mtd MTD device structure * @param to offset to write to * @param ops oob operation description structure * * Write main and/or oob with ECC */ -static int onenand_write_ops(struct mtd_info *mtd, loff_t to, +static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { struct onenand_chip *this = mtd->priv; @@ -1337,7 +1335,7 @@ static int onenand_write_ops(struct mtd_info *mtd, loff_t to, u_char *oobbuf; int ret = 0; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_write: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ ops->retlen = 0; @@ -1345,13 +1343,13 @@ static int onenand_write_ops(struct mtd_info *mtd, loff_t to, /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { - printk(KERN_ERR "onenand_write: Attempt write to past end of device\n"); + printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n"); return -EINVAL; } /* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { - printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n"); + printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n"); return -EINVAL; } @@ -1364,9 +1362,6 @@ static int onenand_write_ops(struct mtd_info *mtd, loff_t to, column = to & (mtd->writesize - 1); - /* Grab the lock and see if the device is available */ - onenand_get_device(mtd, FL_WRITING); - /* Loop until all data write */ while (written < len) { u_char *wbuf = (u_char *) buf; @@ -1419,14 +1414,14 @@ static int onenand_write_ops(struct mtd_info *mtd, loff_t to, } if (ret) { - printk(KERN_ERR "onenand_write: write filaed %d\n", ret); + printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); break; } /* Only check verify write turn on */ ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen); if (ret) { - printk(KERN_ERR "onenand_write: verify failed %d\n", ret); + printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); break; } @@ -1450,7 +1445,7 @@ static int onenand_write_ops(struct mtd_info *mtd, loff_t to, /** - * onenand_do_write_oob - [Internal] OneNAND write out-of-band + * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write @@ -1460,8 +1455,8 @@ static int onenand_write_ops(struct mtd_info *mtd, loff_t to, * * OneNAND write out-of-band */ -static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) +static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) { struct onenand_chip *this = mtd->priv; int column, ret = 0, oobsize; @@ -1473,7 +1468,7 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, to += ops->ooboffs; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ ops->oobretlen = 0; @@ -1486,13 +1481,13 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, column = to & (mtd->oobsize - 1); if (unlikely(column >= oobsize)) { - printk(KERN_ERR "onenand_write_oob: Attempted to start write outside oob\n"); + printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n"); return -EINVAL; } /* For compatibility with NAND: Do not allow write past end of page */ if (unlikely(column + len > oobsize)) { - printk(KERN_ERR "onenand_write_oob: " + printk(KERN_ERR "onenand_write_oob_nolock: " "Attempt to write past end of page\n"); return -EINVAL; } @@ -1501,13 +1496,10 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, if (unlikely(to >= mtd->size || column + len > ((mtd->size >> this->page_shift) - (to >> this->page_shift)) * oobsize)) { - printk(KERN_ERR "onenand_write_oob: Attempted to write past end of device\n"); + printk(KERN_ERR "onenand_write_oob_nolock: Attempted to write past end of device\n"); return -EINVAL; } - /* Grab the lock and see if the device is available */ - onenand_get_device(mtd, FL_WRITING); - oobbuf = this->oob_buf; /* Loop until all data write */ @@ -1537,13 +1529,13 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, ret = this->wait(mtd, FL_WRITING); if (ret) { - printk(KERN_ERR "onenand_write_oob: write failed %d\n", ret); + printk(KERN_ERR "onenand_write_oob_nolock: write failed %d\n", ret); break; } ret = onenand_verify_oob(mtd, oobbuf, to); if (ret) { - printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret); + printk(KERN_ERR "onenand_write_oob_nolock: verify failed %d\n", ret); break; } @@ -1556,9 +1548,6 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, column = 0; } - /* Deselect and wake up anyone waiting on the device */ - onenand_release_device(mtd); - ops->oobretlen = written; return ret; @@ -1585,9 +1574,11 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, }; int ret; - ret = onenand_write_ops(mtd, to, &ops); - *retlen = ops.retlen; + onenand_get_device(mtd, FL_WRITING); + ret = onenand_write_ops_nolock(mtd, to, &ops); + onenand_release_device(mtd); + *retlen = ops.retlen; return ret; } @@ -1600,6 +1591,8 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, static int onenand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { + int ret; + switch (ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_AUTO: @@ -1610,23 +1603,26 @@ static int onenand_write_oob(struct mtd_info *mtd, loff_t to, return -EINVAL; } + onenand_get_device(mtd, FL_WRITING); if (ops->datbuf) - return onenand_write_ops(mtd, to, ops); + ret = onenand_write_ops_nolock(mtd, to, ops); + else + ret = onenand_write_oob_nolock(mtd, to, ops); + onenand_release_device(mtd); - return onenand_do_write_oob(mtd, to, ops); + return ret; } /** - * onenand_block_checkbad - [GENERIC] Check if a block is marked bad + * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad * @param mtd MTD device structure * @param ofs offset from device start - * @param getchip 0, if the chip is already selected * @param allowbbt 1, if its allowed to access the bbt area * * Check, if the block is bad. Either by reading the bad block table or * calling of the scan function. */ -static int onenand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) +static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt) { struct onenand_chip *this = mtd->priv; struct bbm_info *bbm = this->bbm; @@ -1687,7 +1683,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) cond_resched(); /* Check if we have a bad block, we do not erase bad blocks */ - if (onenand_block_checkbad(mtd, addr, 0, 0)) { + if (onenand_block_isbad_nolock(mtd, addr, 0)) { printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%08x\n", (unsigned int) addr); instr->state = MTD_ERASE_FAILED; goto erase_exit; @@ -1751,11 +1747,16 @@ static void onenand_sync(struct mtd_info *mtd) */ static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) { + int ret; + /* Check for invalid offset */ if (ofs > mtd->size) return -EINVAL; - return onenand_block_checkbad(mtd, ofs, 1, 0); + onenand_get_device(mtd, FL_READING); + ret = onenand_block_isbad_nolock(mtd, ofs, 0); + onenand_release_device(mtd); + return ret; } /** @@ -1786,7 +1787,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 onenand_do_write_oob(mtd, ofs, &ops); + return onenand_write_oob_nolock(mtd, ofs, &ops); } /** @@ -1809,7 +1810,10 @@ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) return ret; } - return this->block_markbad(mtd, ofs); + onenand_get_device(mtd, FL_WRITING); + ret = this->block_markbad(mtd, ofs); + onenand_release_device(mtd); + return ret; } /** @@ -2008,13 +2012,19 @@ 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; + struct mtd_oob_ops ops = { + .len = len, + .ooblen = 0, + .datbuf = buf, + .oobbuf = NULL, + }; 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); + ret = onenand_read_ops_nolock(mtd, from, &ops); /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); @@ -2026,19 +2036,20 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len, /** * do_otp_write - [DEFAULT] Write OTP block area * @param mtd MTD device structure - * @param from The offset to write + * @param to 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, +static int do_otp_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, u_char *buf) { struct onenand_chip *this = mtd->priv; unsigned char *pbuf = buf; int ret; + struct mtd_oob_ops ops; /* Force buffer page aligned */ if (len < mtd->writesize) { @@ -2052,7 +2063,12 @@ static int do_otp_write(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(mtd, from, len, retlen, pbuf); + ops.len = len; + ops.ooblen = 0; + ops.databuf = pbuf; + ops.oobbuf = NULL; + ret = onenand_write_ops_nolock(mtd, to, &ops); + *retlen = ops.retlen; /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); @@ -2087,7 +2103,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 = onenand_do_write_oob(mtd, from, &ops); + ret = onenand_write_oob_nolock(mtd, from, &ops); *retlen = ops.oobretlen; @@ -2136,13 +2152,16 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, if (((mtd->writesize * otp_pages) - (from + len)) < 0) return 0; + onenand_get_device(mtd, FL_OTPING); 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; + if (len <= 0) { + ret = -ENOSPC; + break; + } otpinfo = (struct otp_info *) buf; otpinfo->start = from; @@ -2162,13 +2181,14 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, len -= size; *retlen += size; - if (ret < 0) - return ret; + if (ret) + break; } otp_pages--; } + onenand_release_device(mtd); - return 0; + return ret; } /** @@ -2364,7 +2384,7 @@ static void onenand_print_device_info(int device, int version) (16 << density), vcc ? "2.65/3.3" : "1.8", device); - printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version); + printk(KERN_INFO "OneNAND version = 0x%04x\n", version); } static const struct onenand_manufacturers onenand_manuf_ids[] = { -- cgit v1.2.2 From b7a701851842e6a3d33ac1b0f80379664c633d49 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 24 Jul 2007 13:37:27 +0100 Subject: [MTD] [NAND] s3c2410: fix arch moves Fixup the includes which have been moved around when changing the s3c24xx arch support. Signed-off-by: Ben Dooks Signed-off-by: David Woodhouse --- drivers/mtd/nand/s3c2410.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 5fac4c421a..b79a9cf2d1 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -60,8 +60,8 @@ #include -#include -#include +#include +#include #ifdef CONFIG_MTD_NAND_S3C2410_HWECC static int hardware_ecc = 1; -- cgit v1.2.2 From 06ed24e5773346eb13579a4ae9599502bc78e4f7 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 6 Oct 2007 14:44:12 -0400 Subject: =?UTF-8?q?[MTD]=20[NAND]=20Fix=20PCI=20ident=20table=20for=20CAF?= =?UTF-8?q?=C3=89=20NAND=20controller.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was only the very early prototypes which made the mistake of using the same device ident for all three functions on the device -- don't bother trying to express that in the PCI match table, since the tools don't cope. We can check in the probe routine instead, just in case. Also remember to terminate the table. Signed-off-by: David Woodhouse --- drivers/mtd/nand/cafe_nand.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index cff969d05d..0b3f840b6c 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -623,6 +623,11 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, uint32_t ctrl; int err = 0; + /* Very old versions shared the same PCI ident for all three + functions on the chip. Verify the class too... */ + if ((pdev->class >> 8) != PCI_CLASS_MEMORY_FLASH) + return -ENODEV; + err = pci_enable_device(pdev); if (err) return err; @@ -816,7 +821,8 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev) } static struct pci_device_id cafe_nand_tbl[] = { - { 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MEMORY_FLASH << 8, 0xFFFF0 } + { 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID }, + { } }; MODULE_DEVICE_TABLE(pci, cafe_nand_tbl); -- cgit v1.2.2 From 1fcf8ce51e7acc4b66904c4e159690c9467606b5 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 6 Oct 2007 14:59:32 -0400 Subject: =?UTF-8?q?[MTD]=20[NAND]=20Resume=20method=20for=20CAF=C3=89=20NA?= =?UTF-8?q?ND=20controller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Originally from Marcelo; modified to put the original timing registers back instead of 0xFFFFFFFF. Signed-off-by: David Woodhouse --- drivers/mtd/nand/cafe_nand.c | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 0b3f840b6c..e2832d0b98 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -827,15 +827,51 @@ static struct pci_device_id cafe_nand_tbl[] = { MODULE_DEVICE_TABLE(pci, cafe_nand_tbl); +static int cafe_nand_resume(struct pci_dev *pdev) +{ + uint32_t ctrl; + struct mtd_info *mtd = pci_get_drvdata(pdev); + struct cafe_priv *cafe = mtd->priv; + + /* Start off by resetting the NAND controller completely */ + cafe_writel(cafe, 1, NAND_RESET); + cafe_writel(cafe, 0, NAND_RESET); + cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); + + /* Restore timing configuration */ + cafe_writel(cafe, timing[0], NAND_TIMING1); + cafe_writel(cafe, timing[1], NAND_TIMING2); + cafe_writel(cafe, timing[2], NAND_TIMING3); + + /* Disable master reset, enable NAND clock */ + ctrl = cafe_readl(cafe, GLOBAL_CTRL); + ctrl &= 0xffffeff0; + ctrl |= 0x00007000; + cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL); + cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL); + cafe_writel(cafe, 0, NAND_DMA_CTRL); + cafe_writel(cafe, 0x7006, GLOBAL_CTRL); + cafe_writel(cafe, 0x700a, GLOBAL_CTRL); + + /* Set up DMA address */ + cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0); + if (sizeof(cafe->dmaaddr) > 4) + /* Shift in two parts to shut the compiler up */ + cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1); + else + cafe_writel(cafe, 0, NAND_DMA_ADDR1); + + /* Enable NAND IRQ in global IRQ mask register */ + cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); + return 0; +} + static struct pci_driver cafe_nand_pci_driver = { .name = "CAFÉ NAND", .id_table = cafe_nand_tbl, .probe = cafe_nand_probe, .remove = __devexit_p(cafe_nand_remove), -#ifdef CONFIG_PMx - .suspend = cafe_nand_suspend, .resume = cafe_nand_resume, -#endif }; static int cafe_nand_init(void) -- cgit v1.2.2 From 49defc015ff58fda46a3afa3462dfdfa69bc8401 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 6 Oct 2007 15:01:59 -0400 Subject: [MTD] [NAND] Avoid deadlock in erase callback; release chip lock first. When the erase callback performs some other action on the flash, it's highly likely to deadlock unless we actually release the chip lock before calling it. Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index d569121205..b4e0e77238 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2069,13 +2069,14 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, erase_exit: ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; - /* Do call back function */ - if (!ret) - mtd_erase_callback(instr); /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); + /* Do call back function */ + if (!ret) + mtd_erase_callback(instr); + /* * If BBT requires refresh and erase was successful, rewrite any * selected bad block tables -- cgit v1.2.2 From 1437085c3780f064a06df662195a2695e7d75c09 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Wed, 10 Oct 2007 13:48:14 +0900 Subject: [MTD] [OneNAND] Fix typo related with recent commit Signed-off-by: Kyungmin Park Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 85a97198be..b2c40f67db 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -2065,7 +2065,7 @@ static int do_otp_write(struct mtd_info *mtd, loff_t to, size_t len, ops.len = len; ops.ooblen = 0; - ops.databuf = pbuf; + ops.datbuf = pbuf; ops.oobbuf = NULL; ret = onenand_write_ops_nolock(mtd, to, &ops); *retlen = ops.retlen; -- cgit v1.2.2 From f96880d1e859e3937eb691da8293700b8eec17b3 Mon Sep 17 00:00:00 2001 From: "akpm@linux-foundation.org" Date: Thu, 11 Oct 2007 00:02:06 -0700 Subject: [MTD] [NAND] Fix compiler warning in Alauda driver drivers/mtd/nand/alauda.c: In function 'alauda_bounce_read': drivers/mtd/nand/alauda.c:412: warning: comparison of distinct pointer types lacks a cast Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/nand/alauda.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index 2c29854b42..257937cd99 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c @@ -408,7 +408,7 @@ static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len, *retlen = len; while (len) { u8 oob[16]; - u32 byte = from & al->bytemask; + size_t byte = from & al->bytemask; size_t cplen = min(len, mtd->writesize - byte); err = alauda_read_page(mtd, from, bounce_buf, oob, -- cgit v1.2.2 From c4a9f88daf6c382fedde4cdddef0b30f1d0a20db Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Tue, 2 Oct 2007 13:56:04 -0700 Subject: [MTD] [NOR] fix ctrl-alt-del can't reboot for intel flash bug When we press ctrl-alt-del,kernel_restart_prepare will invoke cfi_intelext_reboot which will set flash to read array mode, but later when device_shutdown is invoked which may put current work queue to sleep and other process may be scheduled to running and programming flash in not FL_READY mode again. So we can't boot up if this flash is used for bootloader. Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index c655e971c1..3aa3dca56a 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -653,7 +653,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr resettime: timeo = jiffies + HZ; retry: - if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) { + if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE || mode == FL_SHUTDOWN)) { /* * OK. We have possibility for contension on the write/erase * operations which are global to the real chip and not per @@ -798,6 +798,9 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr if (mode == FL_READY && chip->oldstate == FL_READY) return 0; + case FL_SHUTDOWN: + /* The machine is rebooting now,so no one can get chip anymore */ + return -EIO; default: sleep: set_current_state(TASK_UNINTERRUPTIBLE); @@ -2409,10 +2412,10 @@ static int cfi_intelext_reset(struct mtd_info *mtd) and switch to array mode so any bootloader in flash is accessible for soft reboot. */ spin_lock(chip->mutex); - ret = get_chip(map, chip, chip->start, FL_SYNCING); + ret = get_chip(map, chip, chip->start, FL_SHUTDOWN); if (!ret) { map_write(map, CMD(0xff), chip->start); - chip->state = FL_READY; + chip->state = FL_SHUTDOWN; } spin_unlock(chip->mutex); } -- cgit v1.2.2 From b37bde147890c8fea8369a5a4e230dabdea4ebfb Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Tue, 2 Oct 2007 13:56:05 -0700 Subject: [MTD] [NAND] Blackfin on-chip NAND Flash Controller driver This is the driver for latest Blackfin on-chip nand flash controller - use nand_chip and mtd_info common nand driver interface - provide both PIO and dma operation - compiled with ezkit bf548 configuration - use hardware 1-bit ECC - tested with YAFFS2 and can mount YAFFS2 filesystem as rootfs ChangeLog from try#1 - use hweight32() instead of count_bits() - replace bf54x with bf5xx and BF54X with BF5XX - compare against plat->page_size in 2 cases when enable hardware ECC ChangeLog from try#2 - passed nand_test suites - use cpu_relax() instead of busy wait loop - some coding style issue pointed out by Andrew Signed-off-by: Bryan Wu Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 19 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/bf5xx_nand.c | 788 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 808 insertions(+) create mode 100644 drivers/mtd/nand/bf5xx_nand.c (limited to 'drivers') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index d3ec309f24..8f9c3baeb3 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -91,6 +91,25 @@ config MTD_NAND_AU1550 This enables the driver for the NAND flash controller on the AMD/Alchemy 1550 SOC. +config MTD_NAND_BF5XX + tristate "Blackfin on-chip NAND Flash Controller driver" + depends on BF54x && MTD_NAND + help + This enables the Blackfin on-chip NAND flash controller + + No board specific support is done by this driver, each board + must advertise a platform_device for the driver to attach. + + This driver can also be built as a module. If so, the module + will be called bf5xx-nand. + +config MTD_NAND_BF5XX_HWECC + bool "BF5XX NAND Hardware ECC" + depends on MTD_NAND_BF5XX + help + Enable the use of the BF5XX's internal ECC generator when + using NAND. + config MTD_NAND_RTC_FROM4 tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)" depends on SH_SOLUTION_ENGINE diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 74125611f3..3ad6c0165d 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_MTD_NAND_TOTO) += toto.o obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o +obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c new file mode 100644 index 0000000000..1657ecd748 --- /dev/null +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -0,0 +1,788 @@ +/* linux/drivers/mtd/nand/bf5xx_nand.c + * + * Copyright 2006-2007 Analog Devices Inc. + * http://blackfin.uclinux.org/ + * Bryan Wu + * + * Blackfin BF5xx on-chip NAND flash controler driver + * + * Derived from drivers/mtd/nand/s3c2410.c + * Copyright (c) 2007 Ben Dooks + * + * Derived from drivers/mtd/nand/cafe.c + * Copyright © 2006 Red Hat, Inc. + * Copyright © 2006 David Woodhouse + * + * Changelog: + * 12-Jun-2007 Bryan Wu: Initial version + * 18-Jul-2007 Bryan Wu: + * - ECC_HW and ECC_SW supported + * - DMA supported in ECC_HW + * - YAFFS tested as rootfs in both ECC_HW and ECC_SW + * + * TODO: + * Enable JFFS2 over NAND as rootfs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DRV_NAME "bf5xx-nand" +#define DRV_VERSION "1.2" +#define DRV_AUTHOR "Bryan Wu " +#define DRV_DESC "BF5xx on-chip NAND FLash Controller Driver" + +#ifdef CONFIG_MTD_NAND_BF5XX_HWECC +static int hardware_ecc = 1; +#else +static int hardware_ecc; +#endif + +static unsigned short bfin_nfc_pin_req[] = {P_NAND_CE, P_NAND_RB, 0}; + +/* + * Data structures for bf5xx nand flash controller driver + */ + +/* bf5xx nand info */ +struct bf5xx_nand_info { + /* mtd info */ + struct nand_hw_control controller; + struct mtd_info mtd; + struct nand_chip chip; + + /* platform info */ + struct bf5xx_nand_platform *platform; + + /* device info */ + struct device *device; + + /* DMA stuff */ + struct completion dma_completion; +}; + +/* + * Conversion functions + */ +static struct bf5xx_nand_info *mtd_to_nand_info(struct mtd_info *mtd) +{ + return container_of(mtd, struct bf5xx_nand_info, mtd); +} + +static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev) +{ + return platform_get_drvdata(pdev); +} + +static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev) +{ + return pdev->dev.platform_data; +} + +/* + * struct nand_chip interface function pointers + */ + +/* + * bf5xx_nand_hwcontrol + * + * Issue command and address cycles to the chip + */ +static void bf5xx_nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + if (cmd == NAND_CMD_NONE) + return; + + while (bfin_read_NFC_STAT() & WB_FULL) + cpu_relax(); + + if (ctrl & NAND_CLE) + bfin_write_NFC_CMD(cmd); + else + bfin_write_NFC_ADDR(cmd); + SSYNC(); +} + +/* + * bf5xx_nand_devready() + * + * returns 0 if the nand is busy, 1 if it is ready + */ +static int bf5xx_nand_devready(struct mtd_info *mtd) +{ + unsigned short val = bfin_read_NFC_IRQSTAT(); + + if ((val & NBUSYIRQ) == NBUSYIRQ) + return 1; + else + return 0; +} + +/* + * ECC functions + * These allow the bf5xx to use the controller's ECC + * generator block to ECC the data as it passes through + */ + +/* + * ECC error correction function + */ +static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); + u32 syndrome[5]; + u32 calced, stored; + int i; + unsigned short failing_bit, failing_byte; + u_char data; + + calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16); + stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16); + + syndrome[0] = (calced ^ stored); + + /* + * syndrome 0: all zero + * No error in data + * No action + */ + if (!syndrome[0] || !calced || !stored) + return 0; + + /* + * sysdrome 0: only one bit is one + * ECC data was incorrect + * No action + */ + if (hweight32(syndrome[0]) == 1) { + dev_err(info->device, "ECC data was incorrect!\n"); + return 1; + } + + syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF); + syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF); + syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF); + syndrome[4] = syndrome[2] ^ syndrome[3]; + + for (i = 0; i < 5; i++) + dev_info(info->device, "syndrome[%d] 0x%08x\n", i, syndrome[i]); + + dev_info(info->device, + "calced[0x%08x], stored[0x%08x]\n", + calced, stored); + + /* + * sysdrome 0: exactly 11 bits are one, each parity + * and parity' pair is 1 & 0 or 0 & 1. + * 1-bit correctable error + * Correct the error + */ + if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) { + dev_info(info->device, + "1-bit correctable error, correct it.\n"); + dev_info(info->device, + "syndrome[1] 0x%08x\n", syndrome[1]); + + failing_bit = syndrome[1] & 0x7; + failing_byte = syndrome[1] >> 0x3; + data = *(dat + failing_byte); + data = data ^ (0x1 << failing_bit); + *(dat + failing_byte) = data; + + return 0; + } + + /* + * sysdrome 0: random data + * More than 1-bit error, non-correctable error + * Discard data, mark bad block + */ + dev_err(info->device, + "More than 1-bit error, non-correctable error.\n"); + dev_err(info->device, + "Please discard data, mark bad block\n"); + + return 1; +} + +static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); + struct bf5xx_nand_platform *plat = info->platform; + unsigned short page_size = (plat->page_size ? 512 : 256); + int ret; + + ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc); + + /* If page size is 512, correct second 256 bytes */ + if (page_size == 512) { + dat += 256; + read_ecc += 8; + calc_ecc += 8; + ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc); + } + + return ret; +} + +static void bf5xx_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + return; +} + +static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_code) +{ + struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); + struct bf5xx_nand_platform *plat = info->platform; + u16 page_size = (plat->page_size ? 512 : 256); + u16 ecc0, ecc1; + u32 code[2]; + u8 *p; + int bytes = 3, i; + + /* first 4 bytes ECC code for 256 page size */ + ecc0 = bfin_read_NFC_ECC0(); + ecc1 = bfin_read_NFC_ECC1(); + + code[0] = (ecc0 & 0x3FF) | ((ecc1 & 0x3FF) << 11); + + dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]); + + /* second 4 bytes ECC code for 512 page size */ + if (page_size == 512) { + ecc0 = bfin_read_NFC_ECC2(); + ecc1 = bfin_read_NFC_ECC3(); + code[1] = (ecc0 & 0x3FF) | ((ecc1 & 0x3FF) << 11); + bytes = 6; + dev_dbg(info->device, "returning ecc 0x%08x\n", code[1]); + } + + p = (u8 *)code; + for (i = 0; i < bytes; i++) + ecc_code[i] = p[i]; + + return 0; +} + +/* + * PIO mode for buffer writing and reading + */ +static void bf5xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + unsigned short val; + + /* + * Data reads are requested by first writing to NFC_DATA_RD + * and then reading back from NFC_READ. + */ + for (i = 0; i < len; i++) { + while (bfin_read_NFC_STAT() & WB_FULL) + cpu_relax(); + + /* Contents do not matter */ + bfin_write_NFC_DATA_RD(0x0000); + SSYNC(); + + while ((bfin_read_NFC_IRQSTAT() & RD_RDY) != RD_RDY) + cpu_relax(); + + buf[i] = bfin_read_NFC_READ(); + + val = bfin_read_NFC_IRQSTAT(); + val |= RD_RDY; + bfin_write_NFC_IRQSTAT(val); + SSYNC(); + } +} + +static uint8_t bf5xx_nand_read_byte(struct mtd_info *mtd) +{ + uint8_t val; + + bf5xx_nand_read_buf(mtd, &val, 1); + + return val; +} + +static void bf5xx_nand_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) { + while (bfin_read_NFC_STAT() & WB_FULL) + cpu_relax(); + + bfin_write_NFC_DATA_WR(buf[i]); + SSYNC(); + } +} + +static void bf5xx_nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + u16 *p = (u16 *) buf; + len >>= 1; + + /* + * Data reads are requested by first writing to NFC_DATA_RD + * and then reading back from NFC_READ. + */ + bfin_write_NFC_DATA_RD(0x5555); + + SSYNC(); + + for (i = 0; i < len; i++) + p[i] = bfin_read_NFC_READ(); +} + +static void bf5xx_nand_write_buf16(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + int i; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + bfin_write_NFC_DATA_WR(p[i]); + + SSYNC(); +} + +/* + * DMA functions for buffer writing and reading + */ +static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id) +{ + struct bf5xx_nand_info *info = dev_id; + + clear_dma_irqstat(CH_NFC); + disable_dma(CH_NFC); + complete(&info->dma_completion); + + return IRQ_HANDLED; +} + +static int bf5xx_nand_dma_rw(struct mtd_info *mtd, + uint8_t *buf, int is_read) +{ + struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); + struct bf5xx_nand_platform *plat = info->platform; + unsigned short page_size = (plat->page_size ? 512 : 256); + unsigned short val; + + dev_dbg(info->device, " mtd->%p, buf->%p, is_read %d\n", + mtd, buf, is_read); + + /* + * Before starting a dma transfer, be sure to invalidate/flush + * the cache over the address range of your DMA buffer to + * prevent cache coherency problems. Otherwise very subtle bugs + * can be introduced to your driver. + */ + if (is_read) + invalidate_dcache_range((unsigned int)buf, + (unsigned int)(buf + page_size)); + else + flush_dcache_range((unsigned int)buf, + (unsigned int)(buf + page_size)); + + /* + * This register must be written before each page is + * transferred to generate the correct ECC register + * values. + */ + bfin_write_NFC_RST(0x1); + SSYNC(); + + disable_dma(CH_NFC); + clear_dma_irqstat(CH_NFC); + + /* setup DMA register with Blackfin DMA API */ + set_dma_config(CH_NFC, 0x0); + set_dma_start_addr(CH_NFC, (unsigned long) buf); + set_dma_x_count(CH_NFC, (page_size >> 2)); + set_dma_x_modify(CH_NFC, 4); + + /* setup write or read operation */ + val = DI_EN | WDSIZE_32; + if (is_read) + val |= WNR; + set_dma_config(CH_NFC, val); + enable_dma(CH_NFC); + + /* Start PAGE read/write operation */ + if (is_read) + bfin_write_NFC_PGCTL(0x1); + else + bfin_write_NFC_PGCTL(0x2); + wait_for_completion(&info->dma_completion); + + return 0; +} + +static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd, + uint8_t *buf, int len) +{ + struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); + struct bf5xx_nand_platform *plat = info->platform; + unsigned short page_size = (plat->page_size ? 512 : 256); + + dev_dbg(info->device, "mtd->%p, buf->%p, int %d\n", mtd, buf, len); + + if (len == page_size) + bf5xx_nand_dma_rw(mtd, buf, 1); + else + bf5xx_nand_read_buf(mtd, buf, len); +} + +static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); + struct bf5xx_nand_platform *plat = info->platform; + unsigned short page_size = (plat->page_size ? 512 : 256); + + dev_dbg(info->device, "mtd->%p, buf->%p, len %d\n", mtd, buf, len); + + if (len == page_size) + bf5xx_nand_dma_rw(mtd, (uint8_t *)buf, 0); + else + bf5xx_nand_write_buf(mtd, buf, len); +} + +/* + * System initialization functions + */ + +static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) +{ + int ret; + unsigned short val; + + /* Do not use dma */ + if (!hardware_ecc) + return 0; + + init_completion(&info->dma_completion); + + /* Setup DMAC1 channel mux for NFC which shared with SDH */ + val = bfin_read_DMAC1_PERIMUX(); + val &= 0xFFFE; + bfin_write_DMAC1_PERIMUX(val); + SSYNC(); + + /* Request NFC DMA channel */ + ret = request_dma(CH_NFC, "BF5XX NFC driver"); + if (ret < 0) { + dev_err(info->device, " unable to get DMA channel\n"); + return ret; + } + + set_dma_callback(CH_NFC, (void *) bf5xx_nand_dma_irq, (void *) info); + + /* Turn off the DMA channel first */ + disable_dma(CH_NFC); + return 0; +} + +/* + * BF5XX NFC hardware initialization + * - pin mux setup + * - clear interrupt status + */ +static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info) +{ + int err = 0; + unsigned short val; + struct bf5xx_nand_platform *plat = info->platform; + + /* setup NFC_CTL register */ + dev_info(info->device, + "page_size=%d, data_width=%d, wr_dly=%d, rd_dly=%d\n", + (plat->page_size ? 512 : 256), + (plat->data_width ? 16 : 8), + plat->wr_dly, plat->rd_dly); + + val = (plat->page_size << NFC_PG_SIZE_OFFSET) | + (plat->data_width << NFC_NWIDTH_OFFSET) | + (plat->rd_dly << NFC_RDDLY_OFFSET) | + (plat->rd_dly << NFC_WRDLY_OFFSET); + dev_dbg(info->device, "NFC_CTL is 0x%04x\n", val); + + bfin_write_NFC_CTL(val); + SSYNC(); + + /* clear interrupt status */ + bfin_write_NFC_IRQMASK(0x0); + SSYNC(); + val = bfin_read_NFC_IRQSTAT(); + bfin_write_NFC_IRQSTAT(val); + SSYNC(); + + if (peripheral_request_list(bfin_nfc_pin_req, DRV_NAME)) { + printk(KERN_ERR DRV_NAME + ": Requesting Peripherals failed\n"); + return -EFAULT; + } + + /* DMA initialization */ + if (bf5xx_nand_dma_init(info)) + err = -ENXIO; + + return err; +} + +/* + * Device management interface + */ +static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info) +{ + struct mtd_info *mtd = &info->mtd; + +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts = info->platform->partitions; + int nr = info->platform->nr_partitions; + + return add_mtd_partitions(mtd, parts, nr); +#else + return add_mtd_device(mtd); +#endif +} + +static int bf5xx_nand_remove(struct platform_device *pdev) +{ + struct bf5xx_nand_info *info = to_nand_info(pdev); + struct mtd_info *mtd = NULL; + + platform_set_drvdata(pdev, NULL); + + /* first thing we need to do is release all our mtds + * and their partitions, then go through freeing the + * resources used + */ + mtd = &info->mtd; + if (mtd) { + nand_release(mtd); + kfree(mtd); + } + + peripheral_free_list(bfin_nfc_pin_req); + + /* free the common resources */ + kfree(info); + + return 0; +} + +/* + * bf5xx_nand_probe + * + * called by device layer when it finds a device matching + * one our driver can handled. This code checks to see if + * it can allocate all necessary resources then calls the + * nand layer to look for devices + */ +static int bf5xx_nand_probe(struct platform_device *pdev) +{ + struct bf5xx_nand_platform *plat = to_nand_plat(pdev); + struct bf5xx_nand_info *info = NULL; + struct nand_chip *chip = NULL; + struct mtd_info *mtd = NULL; + int err = 0; + + dev_dbg(&pdev->dev, "(%p)\n", pdev); + + if (!plat) { + dev_err(&pdev->dev, "no platform specific information\n"); + goto exit_error; + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) { + dev_err(&pdev->dev, "no memory for flash info\n"); + err = -ENOMEM; + goto exit_error; + } + + platform_set_drvdata(pdev, info); + + spin_lock_init(&info->controller.lock); + init_waitqueue_head(&info->controller.wq); + + info->device = &pdev->dev; + info->platform = plat; + + /* initialise chip data struct */ + chip = &info->chip; + + if (plat->data_width) + chip->options |= NAND_BUSWIDTH_16; + + chip->options |= NAND_CACHEPRG | NAND_SKIP_BBTSCAN; + + chip->read_buf = (plat->data_width) ? + bf5xx_nand_read_buf16 : bf5xx_nand_read_buf; + chip->write_buf = (plat->data_width) ? + bf5xx_nand_write_buf16 : bf5xx_nand_write_buf; + + chip->read_byte = bf5xx_nand_read_byte; + + chip->cmd_ctrl = bf5xx_nand_hwcontrol; + chip->dev_ready = bf5xx_nand_devready; + + chip->priv = &info->mtd; + chip->controller = &info->controller; + + chip->IO_ADDR_R = (void __iomem *) NFC_READ; + chip->IO_ADDR_W = (void __iomem *) NFC_DATA_WR; + + chip->chip_delay = 0; + + /* initialise mtd info data struct */ + mtd = &info->mtd; + mtd->priv = chip; + mtd->owner = THIS_MODULE; + + /* initialise the hardware */ + err = bf5xx_nand_hw_init(info); + if (err != 0) + goto exit_error; + + /* setup hardware ECC data struct */ + if (hardware_ecc) { + if (plat->page_size == NFC_PG_SIZE_256) { + chip->ecc.bytes = 3; + chip->ecc.size = 256; + } else if (plat->page_size == NFC_PG_SIZE_512) { + chip->ecc.bytes = 6; + chip->ecc.size = 512; + } + + chip->read_buf = bf5xx_nand_dma_read_buf; + chip->write_buf = bf5xx_nand_dma_write_buf; + chip->ecc.calculate = bf5xx_nand_calculate_ecc; + chip->ecc.correct = bf5xx_nand_correct_data; + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.hwctl = bf5xx_nand_enable_hwecc; + } else { + chip->ecc.mode = NAND_ECC_SOFT; + } + + /* scan hardware nand chip and setup mtd info data struct */ + if (nand_scan(mtd, 1)) { + err = -ENXIO; + goto exit_error; + } + + /* add NAND partition */ + bf5xx_nand_add_partition(info); + + dev_dbg(&pdev->dev, "initialised ok\n"); + return 0; + +exit_error: + bf5xx_nand_remove(pdev); + + if (err == 0) + err = -EINVAL; + return err; +} + +/* PM Support */ +#ifdef CONFIG_PM + +static int bf5xx_nand_suspend(struct platform_device *dev, pm_message_t pm) +{ + struct bf5xx_nand_info *info = platform_get_drvdata(dev); + + return 0; +} + +static int bf5xx_nand_resume(struct platform_device *dev) +{ + struct bf5xx_nand_info *info = platform_get_drvdata(dev); + + if (info) + bf5xx_nand_hw_init(info); + + return 0; +} + +#else +#define bf5xx_nand_suspend NULL +#define bf5xx_nand_resume NULL +#endif + +/* driver device registration */ +static struct platform_driver bf5xx_nand_driver = { + .probe = bf5xx_nand_probe, + .remove = bf5xx_nand_remove, + .suspend = bf5xx_nand_suspend, + .resume = bf5xx_nand_resume, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init bf5xx_nand_init(void) +{ + printk(KERN_INFO "%s, Version %s (c) 2007 Analog Devices, Inc.\n", + DRV_DESC, DRV_VERSION); + + return platform_driver_register(&bf5xx_nand_driver); +} + +static void __exit bf5xx_nand_exit(void) +{ + platform_driver_unregister(&bf5xx_nand_driver); +} + +module_init(bf5xx_nand_init); +module_exit(bf5xx_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRV_AUTHOR); +MODULE_DESCRIPTION(DRV_DESC); -- cgit v1.2.2