diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-25 12:25:48 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-25 12:25:48 -0700 |
commit | 7e97b28309edc5669ced2bb1371704e8f8d084e3 (patch) | |
tree | 9605694b00b4233a730ad86ea6d395cf59efc905 | |
parent | 5421d059068341b3f21df1a21c2ba9175e55ebef (diff) | |
parent | 3887ed5231fb6f339f36c3a0297c996cd1a1dad9 (diff) | |
download | linux-7e97b28309edc5669ced2bb1371704e8f8d084e3.tar.gz linux-7e97b28309edc5669ced2bb1371704e8f8d084e3.tar.bz2 linux-7e97b28309edc5669ced2bb1371704e8f8d084e3.zip |
Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (82 commits)
[MTD] m25p80: Add Support for ATMEL AT25DF641 64-Megabit SPI Flash
[MTD] m25p80: add FAST_READ access support to M25Pxx
[MTD] [NAND] bf5xx_nand: Avoid crash if bfin_mac is installed.
[MTD] [NAND] at91_nand: control NCE signal
[MTD] [NAND] AT91 hardware ECC compile fix for at91sam9263 / at91sam9260
[MTD] [NAND] Hardware ECC controller on at91sam9263 / at91sam9260
[JFFS2] Introduce dbg_readinode2 log level, use it to shut read_dnode() up
[JFFS2] Fix jffs2_reserve_space() when all blocks are pending erasure.
[JFFS2] Add erase_checking_list to hold blocks being marked.
UBI: add a message
[JFFS2] Return values of jffs2_block_check_erase error paths
[MTD] Clean up AR7 partition map support
[MTD] [NOR] Fix Intel CFI driver for collie flash
[JFFS2] Finally remove redundant ref->__totlen field.
[JFFS2] Honour TEST_TOTLEN macro in debugging code. ref->__totlen is going!
[JFFS2] Add paranoia debugging for superblock counts
[JFFS2] Fix free space leak with in-band cleanmarkers
[JFFS2] Self-sufficient #includes in jffs2_fs_i.h: include <linux/mutex.h>
[MTD] [NAND] Verify probe by retrying to checking the results match
[MTD] [NAND] S3C2410 Allow ECC disable to be specified by the board
...
89 files changed, 3342 insertions, 499 deletions
diff --git a/Documentation/ABI/stable/sysfs-class-ubi b/Documentation/ABI/stable/sysfs-class-ubi new file mode 100644 index 000000000000..18d471d9faea --- /dev/null +++ b/Documentation/ABI/stable/sysfs-class-ubi @@ -0,0 +1,212 @@ +What: /sys/class/ubi/ +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + The ubi/ class sub-directory belongs to the UBI subsystem and + provides general UBI information, per-UBI device information + and per-UBI volume information. + +What: /sys/class/ubi/version +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + This file contains version of the latest supported UBI on-media + format. Currently it is 1, and there is no plan to change this. + However, if in the future UBI needs on-flash format changes + which cannot be done in a compatible manner, a new format + version will be added. So this is a mechanism for possible + future backward-compatible (but forward-incompatible) + improvements. + +What: /sys/class/ubiX/ +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + The /sys/class/ubi0, /sys/class/ubi1, etc directories describe + UBI devices (UBI device 0, 1, etc). They contain general UBI + device information and per UBI volume information (each UBI + device may have many UBI volumes) + +What: /sys/class/ubi/ubiX/avail_eraseblocks +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Amount of available logical eraseblock. For example, one may + create a new UBI volume which has this amount of logical + eraseblocks. + +What: /sys/class/ubi/ubiX/bad_peb_count +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Count of bad physical eraseblocks on the underlying MTD device. + +What: /sys/class/ubi/ubiX/bgt_enabled +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Contains ASCII "0\n" if the UBI background thread is disabled, + and ASCII "1\n" if it is enabled. + +What: /sys/class/ubi/ubiX/dev +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Major and minor numbers of the character device corresponding + to this UBI device (in <major>:<minor> format). + +What: /sys/class/ubi/ubiX/eraseblock_size +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Maximum logical eraseblock size this UBI device may provide. UBI + volumes may have smaller logical eraseblock size because of their + alignment. + +What: /sys/class/ubi/ubiX/max_ec +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Maximum physical eraseblock erase counter value. + +What: /sys/class/ubi/ubiX/max_vol_count +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Maximum number of volumes which this UBI device may have. + +What: /sys/class/ubi/ubiX/min_io_size +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Minimum input/output unit size. All the I/O may only be done + in fractions of the contained number. + +What: /sys/class/ubi/ubiX/mtd_num +Date: January 2008 +KernelVersion: 2.6.25 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Number of the underlying MTD device. + +What: /sys/class/ubi/ubiX/reserved_for_bad +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Number of physical eraseblocks reserved for bad block handling. + +What: /sys/class/ubi/ubiX/total_eraseblocks +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Total number of good (not marked as bad) physical eraseblocks on + the underlying MTD device. + +What: /sys/class/ubi/ubiX/volumes_count +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Count of volumes on this UBI device. + +What: /sys/class/ubi/ubiX/ubiX_Y/ +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + The /sys/class/ubi/ubiX/ubiX_0/, /sys/class/ubi/ubiX/ubiX_1/, + etc directories describe UBI volumes on UBI device X (volumes + 0, 1, etc). + +What: /sys/class/ubi/ubiX/ubiX_Y/alignment +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Volume alignment - the value the logical eraseblock size of + this volume has to be aligned on. For example, 2048 means that + logical eraseblock size is multiple of 2048. In other words, + volume logical eraseblock size is UBI device logical eraseblock + size aligned to the alignment value. + +What: /sys/class/ubi/ubiX/ubiX_Y/corrupted +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Contains ASCII "0\n" if the UBI volume is OK, and ASCII "1\n" + if it is corrupted (e.g., due to an interrupted volume update). + +What: /sys/class/ubi/ubiX/ubiX_Y/data_bytes +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + The amount of data this volume contains. This value makes sense + only for static volumes, and for dynamic volume it equivalent + to the total volume size in bytes. + +What: /sys/class/ubi/ubiX/ubiX_Y/dev +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Major and minor numbers of the character device corresponding + to this UBI volume (in <major>:<minor> format). + +What: /sys/class/ubi/ubiX/ubiX_Y/name +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Volume name. + +What: /sys/class/ubi/ubiX/ubiX_Y/reserved_ebs +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Count of physical eraseblock reserved for this volume. + Equivalent to the volume size in logical eraseblocks. + +What: /sys/class/ubi/ubiX/ubiX_Y/type +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Volume type. Contains ASCII "dynamic\n" for dynamic volumes and + "static\n" for static volumes. + +What: /sys/class/ubi/ubiX/ubiX_Y/upd_marker +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Contains ASCII "0\n" if the update marker is not set for this + volume, and "1\n" if it is set. The update marker is set when + volume update starts, and cleaned when it ends. So the presence + of the update marker indicates that the volume is being updated + at the moment of the update was interrupted. The later may be + checked using the "corrupted" sysfs file. + +What: /sys/class/ubi/ubiX/ubiX_Y/usable_eb_size +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Logical eraseblock size of this volume. Equivalent to logical + eraseblock size of the device aligned on the volume alignment + value. diff --git a/Documentation/arm/Samsung-S3C24XX/NAND.txt b/Documentation/arm/Samsung-S3C24XX/NAND.txt new file mode 100644 index 000000000000..bc478a3409b8 --- /dev/null +++ b/Documentation/arm/Samsung-S3C24XX/NAND.txt @@ -0,0 +1,30 @@ + S3C24XX NAND Support + ==================== + +Introduction +------------ + +Small Page NAND +--------------- + +The driver uses a 512 byte (1 page) ECC code for this setup. The +ECC code is not directly compatible with the default kernel ECC +code, so the driver enforces its own OOB layout and ECC parameters + +Large Page NAND +--------------- + +The driver is capable of handling NAND flash with a 2KiB page +size, with support for hardware ECC generation and correction. + +Unlike the 512byte page mode, the driver generates ECC data for +each 256 byte block in an 2KiB page. This means that more than +one error in a page can be rectified. It also means that the +OOB layout remains the default kernel layout for these flashes. + + +Document Author +--------------- + +Ben Dooks, Copyright 2007 Simtec Electronics + diff --git a/Documentation/arm/Samsung-S3C24XX/Overview.txt b/Documentation/arm/Samsung-S3C24XX/Overview.txt index c31b76fa66c4..d04e1e30c47f 100644 --- a/Documentation/arm/Samsung-S3C24XX/Overview.txt +++ b/Documentation/arm/Samsung-S3C24XX/Overview.txt @@ -156,6 +156,8 @@ NAND controller. If there are any problems the latest linux-mtd code can be found from http://www.linux-mtd.infradead.org/ + For more information see Documentation/arm/Samsung-S3C24XX/NAND.txt + Serial ------ diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index e8503341e3b1..eed06d068fd1 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -158,6 +158,12 @@ config MTD_OF_PARTS the partition map from the children of the flash node, as described in Documentation/powerpc/booting-without-of.txt. +config MTD_AR7_PARTS + tristate "TI AR7 partitioning support" + depends on MTD_PARTITIONS + ---help--- + TI AR7 partitioning support + comment "User Modules And Translation Layers" config MTD_CHAR diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 538e33d11d46..4b77335715f0 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o +obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o # 'Users' - code which presents functionality to userspace. diff --git a/drivers/mtd/ar7part.c b/drivers/mtd/ar7part.c new file mode 100644 index 000000000000..ecf170b55c32 --- /dev/null +++ b/drivers/mtd/ar7part.c @@ -0,0 +1,151 @@ +/* + * Copyright © 2007 Eugene Konev <ejka@openwrt.org> + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * TI AR7 flash partition table. + * Based on ar7 map by Felix Fietkau <nbd@openwrt.org> + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/bootmem.h> +#include <linux/magic.h> + +#define AR7_PARTS 4 +#define ROOT_OFFSET 0xe0000 + +#define LOADER_MAGIC1 le32_to_cpu(0xfeedfa42) +#define LOADER_MAGIC2 le32_to_cpu(0xfeed1281) + +#ifndef SQUASHFS_MAGIC +#define SQUASHFS_MAGIC 0x73717368 +#endif + +struct ar7_bin_rec { + unsigned int checksum; + unsigned int length; + unsigned int address; +}; + +static struct mtd_partition ar7_parts[AR7_PARTS]; + +static int create_mtd_partitions(struct mtd_info *master, + struct mtd_partition **pparts, + unsigned long origin) +{ + struct ar7_bin_rec header; + unsigned int offset; + size_t len; + unsigned int pre_size = master->erasesize, post_size = 0; + unsigned int root_offset = ROOT_OFFSET; + + int retries = 10; + + ar7_parts[0].name = "loader"; + ar7_parts[0].offset = 0; + ar7_parts[0].size = master->erasesize; + ar7_parts[0].mask_flags = MTD_WRITEABLE; + + ar7_parts[1].name = "config"; + ar7_parts[1].offset = 0; + ar7_parts[1].size = master->erasesize; + ar7_parts[1].mask_flags = 0; + + do { /* Try 10 blocks starting from master->erasesize */ + offset = pre_size; + master->read(master, offset, + sizeof(header), &len, (uint8_t *)&header); + if (!strncmp((char *)&header, "TIENV0.8", 8)) + ar7_parts[1].offset = pre_size; + if (header.checksum == LOADER_MAGIC1) + break; + if (header.checksum == LOADER_MAGIC2) + break; + pre_size += master->erasesize; + } while (retries--); + + pre_size = offset; + + if (!ar7_parts[1].offset) { + ar7_parts[1].offset = master->size - master->erasesize; + post_size = master->erasesize; + } + + switch (header.checksum) { + case LOADER_MAGIC1: + while (header.length) { + offset += sizeof(header) + header.length; + master->read(master, offset, sizeof(header), + &len, (uint8_t *)&header); + } + root_offset = offset + sizeof(header) + 4; + break; + case LOADER_MAGIC2: + while (header.length) { + offset += sizeof(header) + header.length; + master->read(master, offset, sizeof(header), + &len, (uint8_t *)&header); + } + root_offset = offset + sizeof(header) + 4 + 0xff; + root_offset &= ~(uint32_t)0xff; + break; + default: + printk(KERN_WARNING "Unknown magic: %08x\n", header.checksum); + break; + } + + master->read(master, root_offset, + sizeof(header), &len, (u8 *)&header); + if (header.checksum != SQUASHFS_MAGIC) { + root_offset += master->erasesize - 1; + root_offset &= ~(master->erasesize - 1); + } + + ar7_parts[2].name = "linux"; + ar7_parts[2].offset = pre_size; + ar7_parts[2].size = master->size - pre_size - post_size; + ar7_parts[2].mask_flags = 0; + + ar7_parts[3].name = "rootfs"; + ar7_parts[3].offset = root_offset; + ar7_parts[3].size = master->size - root_offset - post_size; + ar7_parts[3].mask_flags = 0; + + *pparts = ar7_parts; + return AR7_PARTS; +} + +static struct mtd_part_parser ar7_parser = { + .owner = THIS_MODULE, + .parse_fn = create_mtd_partitions, + .name = "ar7part", +}; + +static int __init ar7_parser_init(void) +{ + return register_mtd_parser(&ar7_parser); +} + +module_init(ar7_parser_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR( "Felix Fietkau <nbd@openwrt.org>, " + "Eugene Konev <ejka@openwrt.org>"); +MODULE_DESCRIPTION("MTD partitioning for TI AR7"); diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 0080452531d6..e812df607a5c 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -384,7 +384,7 @@ read_pri_intelext(struct map_info *map, __u16 adr) if (extp_size > 4096) { printk(KERN_ERR "%s: cfi_pri_intelext is too fat\n", - __FUNCTION__); + __func__); return NULL; } goto again; @@ -619,6 +619,9 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, sizeof(struct cfi_intelext_blockinfo); } + if (!numparts) + numparts = 1; + /* Programming Region info */ if (extp->MinorVersion >= '4') { struct cfi_intelext_programming_regioninfo *prinfo; @@ -641,7 +644,7 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, if ((1 << partshift) < mtd->erasesize) { printk( KERN_ERR "%s: bad number of hw partitions (%d)\n", - __FUNCTION__, numparts); + __func__, numparts); return -EINVAL; } @@ -1071,10 +1074,10 @@ static int __xipram xip_wait_for_operation( chip->state = newstate; map_write(map, CMD(0xff), adr); (void) map_read(map, adr); - asm volatile (".rep 8; nop; .endr"); + xip_iprefetch(); local_irq_enable(); spin_unlock(chip->mutex); - asm volatile (".rep 8; nop; .endr"); + xip_iprefetch(); cond_resched(); /* @@ -2013,7 +2016,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", - __FUNCTION__, ofs, len); + __func__, ofs, len); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, NULL); #endif @@ -2023,7 +2026,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status after, ret=%d\n", - __FUNCTION__, ret); + __func__, ret); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, NULL); #endif @@ -2037,7 +2040,7 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", - __FUNCTION__, ofs, len); + __func__, ofs, len); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, NULL); #endif @@ -2047,7 +2050,7 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status after, ret=%d\n", - __FUNCTION__, ret); + __func__, ret); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, NULL); #endif diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 458d477614d6..f7fcc6389533 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -220,6 +220,28 @@ static void fixup_use_atmel_lock(struct mtd_info *mtd, void *param) mtd->flags |= MTD_POWERUP_LOCK; } +static void fixup_s29gl064n_sectors(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + if ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0x003f) { + cfi->cfiq->EraseRegionInfo[0] |= 0x0040; + pr_warning("%s: Bad S29GL064N CFI data, adjust from 64 to 128 sectors\n", mtd->name); + } +} + +static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + if ((cfi->cfiq->EraseRegionInfo[1] & 0xffff) == 0x007e) { + cfi->cfiq->EraseRegionInfo[1] &= ~0x0040; + pr_warning("%s: Bad S29GL032N CFI data, adjust from 127 to 63 sectors\n", mtd->name); + } +} + static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, #ifdef AMD_BOOTLOC_BUG @@ -231,6 +253,10 @@ static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_AMD, 0x0056, fixup_use_secsi, NULL, }, { CFI_MFR_AMD, 0x005C, fixup_use_secsi, NULL, }, { CFI_MFR_AMD, 0x005F, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x0c01, fixup_s29gl064n_sectors, NULL, }, + { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, }, + { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, }, + { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, }, #if !FORCE_WORD_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, #endif @@ -723,10 +749,10 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, chip->erase_suspended = 1; map_write(map, CMD(0xf0), adr); (void) map_read(map, adr); - asm volatile (".rep 8; nop; .endr"); + xip_iprefetch(); local_irq_enable(); spin_unlock(chip->mutex); - asm volatile (".rep 8; nop; .endr"); + xip_iprefetch(); cond_resched(); /* diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index 492e2ab27420..1b720cc571f3 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -445,7 +445,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, retry: #ifdef DEBUG_CFI_FEATURES - printk("%s: chip->state[%d]\n", __FUNCTION__, chip->state); + printk("%s: chip->state[%d]\n", __func__, chip->state); #endif spin_lock_bh(chip->mutex); @@ -463,7 +463,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; #ifdef DEBUG_CFI_FEATURES - printk("%s: 1 status[%x]\n", __FUNCTION__, map_read(map, cmd_adr)); + printk("%s: 1 status[%x]\n", __func__, map_read(map, cmd_adr)); #endif case FL_STATUS: @@ -591,7 +591,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, /* check for errors: 'lock bit', 'VPP', 'dead cell'/'unerased cell' or 'incorrect cmd' -- saw */ if (map_word_bitsset(map, status, CMD(0x3a))) { #ifdef DEBUG_CFI_FEATURES - printk("%s: 2 status[%lx]\n", __FUNCTION__, status.x[0]); + printk("%s: 2 status[%lx]\n", __func__, status.x[0]); #endif /* clear status */ map_write(map, CMD(0x50), cmd_adr); @@ -625,9 +625,9 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to, ofs = to - (chipnum << cfi->chipshift); #ifdef DEBUG_CFI_FEATURES - printk("%s: map_bankwidth(map)[%x]\n", __FUNCTION__, map_bankwidth(map)); - printk("%s: chipnum[%x] wbufsize[%x]\n", __FUNCTION__, chipnum, wbufsize); - printk("%s: ofs[%x] len[%x]\n", __FUNCTION__, ofs, len); + printk("%s: map_bankwidth(map)[%x]\n", __func__, map_bankwidth(map)); + printk("%s: chipnum[%x] wbufsize[%x]\n", __func__, chipnum, wbufsize); + printk("%s: ofs[%x] len[%x]\n", __func__, ofs, len); #endif /* Write buffer is worth it only if more than one word to write... */ @@ -893,7 +893,8 @@ retry: return ret; } -int cfi_staa_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) +static int cfi_staa_erase_varsize(struct mtd_info *mtd, + struct erase_info *instr) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; unsigned long adr, len; diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index f651b6ef1c5d..a4463a91ce31 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -39,7 +39,7 @@ struct mtd_info *cfi_probe(struct map_info *map); #define xip_allowed(base, map) \ do { \ (void) map_read(map, base); \ - asm volatile (".rep 8; nop; .endr"); \ + xip_iprefetch(); \ local_irq_enable(); \ } while (0) @@ -232,6 +232,11 @@ static int __xipram cfi_chip_setup(struct map_info *map, cfi->mfr = cfi_read_query16(map, base); cfi->id = cfi_read_query16(map, base + ofs_factor); + /* Get AMD/Spansion extended JEDEC ID */ + if (cfi->mfr == CFI_MFR_AMD && (cfi->id & 0xff) == 0x7e) + cfi->id = cfi_read_query(map, base + 0xe * ofs_factor) << 8 | + cfi_read_query(map, base + 0xf * ofs_factor); + /* Put it back into Read Mode */ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); /* ... even if it's an Intel chip */ diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index 2e51496c248e..72e0022a47bf 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -65,7 +65,7 @@ __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* n #ifdef CONFIG_MTD_XIP (void) map_read(map, base); - asm volatile (".rep 8; nop; .endr"); + xip_iprefetch(); local_irq_enable(); #endif diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 4be51a86a85c..aa07575eb288 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -132,6 +132,8 @@ #define M29F800AB 0x0058 #define M29W800DT 0x00D7 #define M29W800DB 0x005B +#define M29W400DT 0x00EE +#define M29W400DB 0x00EF #define M29W160DT 0x22C4 #define M29W160DB 0x2249 #define M29W040B 0x00E3 @@ -160,6 +162,7 @@ #define SST49LF030A 0x001C #define SST49LF040A 0x0051 #define SST49LF080A 0x005B +#define SST36VF3203 0x7354 /* Toshiba */ #define TC58FVT160 0x00C2 @@ -1113,7 +1116,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x10000,8), } - }, { + }, { .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29F016, .name = "Macronix MX29F016", @@ -1125,7 +1128,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x10000,32), } - }, { + }, { .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29F004T, .name = "Macronix MX29F004T", @@ -1140,7 +1143,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,2), ERASEINFO(0x04000,1), } - }, { + }, { .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29F004B, .name = "Macronix MX29F004B", @@ -1218,7 +1221,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x40000,16), } - }, { + }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39LF512, .name = "SST 39LF512", @@ -1230,7 +1233,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x01000,16), } - }, { + }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39LF010, .name = "SST 39LF010", @@ -1242,7 +1245,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x01000,32), } - }, { + }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST29EE020, .name = "SST 29EE020", @@ -1276,7 +1279,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x01000,64), } - }, { + }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39LF040, .name = "SST 39LF040", @@ -1288,7 +1291,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x01000,128), } - }, { + }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39SF010A, .name = "SST 39SF010A", @@ -1300,7 +1303,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x01000,32), } - }, { + }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39SF020A, .name = "SST 39SF020A", @@ -1412,6 +1415,18 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,256) } }, { + .mfr_id = MANUFACTURER_SST, + .dev_id = SST36VF3203, + .name = "SST 36VF3203", + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + .dev_size = SIZE_4MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, + .regions = { + ERASEINFO(0x10000,64), + } + }, { .mfr_id = MANUFACTURER_ST, .dev_id = M29F800AB, .name = "ST M29F800AB", @@ -1426,7 +1441,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x08000,1), ERASEINFO(0x10000,15), } - }, { + }, { .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ .dev_id = M29W800DT, .name = "ST M29W800DT", @@ -1456,6 +1471,36 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x08000,1), ERASEINFO(0x10000,15) } + }, { + .mfr_id = MANUFACTURER_ST, + .dev_id = M29W400DT, + .name = "ST M29W400DT", + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, + .regions = { + ERASEINFO(0x04000,7), + ERASEINFO(0x02000,1), + ERASEINFO(0x08000,2), + ERASEINFO(0x10000,1) + } + }, { + .mfr_id = MANUFACTURER_ST, + .dev_id = M29W400DB, + .name = "ST M29W400DB", + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,7) + } }, { .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ .dev_id = M29W160DT, @@ -1486,7 +1531,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x08000,1), ERASEINFO(0x10000,31) } - }, { + }, { .mfr_id = MANUFACTURER_ST, .dev_id = M29W040B, .name = "ST M29W040B", @@ -1498,7 +1543,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x10000,8), } - }, { + }, { .mfr_id = MANUFACTURER_ST, .dev_id = M50FW040, .name = "ST M50FW040", @@ -1510,7 +1555,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x10000,8), } - }, { + }, { .mfr_id = MANUFACTURER_ST, .dev_id = M50FW080, .name = "ST M50FW080", @@ -1522,7 +1567,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x10000,16), } - }, { + }, { .mfr_id = MANUFACTURER_ST, .dev_id = M50FW016, .name = "ST M50FW016", diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index b44292abd9f7..e472a0e9de9d 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -119,7 +119,8 @@ static struct mtd_partition * newpart(char *s, char *p; name = ++s; - if ((p = strchr(name, delim)) == 0) + p = strchr(name, delim); + if (!p) { printk(KERN_ERR ERRP "no closing %c found in partition name\n", delim); return NULL; @@ -159,9 +160,10 @@ static struct mtd_partition * newpart(char *s, return NULL; } /* more partitions follow, parse them */ - if ((parts = newpart(s + 1, &s, num_parts, - this_part + 1, &extra_mem, extra_mem_size)) == 0) - return NULL; + parts = newpart(s + 1, &s, num_parts, this_part + 1, + &extra_mem, extra_mem_size); + if (!parts) + return NULL; } else { /* this is the last partition: allocate space for all */ @@ -308,9 +310,6 @@ static int parse_cmdline_partitions(struct mtd_info *master, struct cmdline_mtd_partition *part; char *mtd_id = master->name; - if(!cmdline) - return -EINVAL; - /* parse command line */ if (!cmdline_parsed) mtdpart_setup_real(cmdline); @@ -341,7 +340,7 @@ static int parse_cmdline_partitions(struct mtd_info *master, return part->num_parts; } } - return -EINVAL; + return 0; } diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 811d56fd890f..35ed1103dbb2 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -77,6 +77,13 @@ config MTD_M25P80 if you want to specify device partitioning or to use a device which doesn't support the JEDEC ID instruction. +config M25PXX_USE_FAST_READ + bool "Use FAST_READ OPCode allowing SPI CLK <= 50MHz" + depends on MTD_M25P80 + default y + help + This option enables FAST_READ access supported by ST M25Pxx. + config MTD_SLRAM tristate "Uncached system RAM" help diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index ad1880c67518..519d942e7940 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -305,7 +305,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) } list_add(&dev->list, &blkmtd_device_list); INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index, - dev->mtd.name + strlen("blkmtd: "), + dev->mtd.name + strlen("block2mtd: "), dev->mtd.erasesize >> 10, dev->mtd.erasesize); return dev; @@ -366,9 +366,9 @@ static inline void kill_final_newline(char *str) } -#define parse_err(fmt, args...) do { \ - ERROR("block2mtd: " fmt "\n", ## args); \ - return 0; \ +#define parse_err(fmt, args...) do { \ + ERROR(fmt, ## args); \ + return 0; \ } while (0) #ifndef MODULE @@ -473,7 +473,7 @@ static void __devexit block2mtd_exit(void) block2mtd_sync(&dev->mtd); del_mtd_device(&dev->mtd); INFO("mtd%d: [%s] removed", dev->mtd.index, - dev->mtd.name + strlen("blkmtd: ")); + dev->mtd.name + strlen("block2mtd: ")); list_del(&dev->list); block2mtd_free_device(dev); } diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 99fd210feaec..1d324e5c412d 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -275,7 +275,7 @@ static __u8 read8 (__u32 offset) { volatile __u8 *data = (__u8 *) (FLASH_OFFSET + offset); #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(): 0x%.8x -> 0x%.2x\n",__FUNCTION__,offset,*data); + printk (KERN_DEBUG "%s(): 0x%.8x -> 0x%.2x\n", __func__, offset, *data); #endif return (*data); } @@ -284,7 +284,7 @@ static __u32 read32 (__u32 offset) { volatile __u32 *data = (__u32 *) (FLASH_OFFSET + offset); #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(): 0x%.8x -> 0x%.8x\n",__FUNCTION__,offset,*data); + printk (KERN_DEBUG "%s(): 0x%.8x -> 0x%.8x\n", __func__, offset, *data); #endif return (*data); } @@ -294,7 +294,7 @@ static void write32 (__u32 x,__u32 offset) volatile __u32 *data = (__u32 *) (FLASH_OFFSET + offset); *data = x; #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(): 0x%.8x <- 0x%.8x\n",__FUNCTION__,offset,*data); + printk (KERN_DEBUG "%s(): 0x%.8x <- 0x%.8x\n", __func__, offset, *data); #endif } @@ -337,7 +337,7 @@ static inline int erase_block (__u32 offset) __u32 status; #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(): 0x%.8x\n",__FUNCTION__,offset); + printk (KERN_DEBUG "%s(): 0x%.8x\n", __func__, offset); #endif /* erase and confirm */ @@ -371,7 +371,7 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) int i,first; #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(addr = 0x%.8x, len = %d)\n",__FUNCTION__,instr->addr,instr->len); + printk (KERN_DEBUG "%s(addr = 0x%.8x, len = %d)\n", __func__, instr->addr, instr->len); #endif /* sanity checks */ @@ -442,7 +442,7 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) static int flash_read (struct mtd_info *mtd,loff_t from,size_t len,size_t *retlen,u_char *buf) { #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(from = 0x%.8x, len = %d)\n",__FUNCTION__,(__u32) from,len); + printk (KERN_DEBUG "%s(from = 0x%.8x, len = %d)\n", __func__, (__u32)from, len); #endif /* sanity checks */ @@ -488,7 +488,7 @@ static inline int write_dword (__u32 offset,__u32 x) __u32 status; #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(): 0x%.8x <- 0x%.8x\n",__FUNCTION__,offset,x); + printk (KERN_DEBUG "%s(): 0x%.8x <- 0x%.8x\n", __func__, offset, x); #endif /* setup writing */ @@ -524,7 +524,7 @@ static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen int i,n; #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(to = 0x%.8x, len = %d)\n",__FUNCTION__,(__u32) to,len); + printk (KERN_DEBUG "%s(to = 0x%.8x, len = %d)\n", __func__, (__u32)to, len); #endif *retlen = 0; diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 98df5bcc02f3..25efd331ef28 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -33,7 +33,7 @@ /* Flash opcodes. */ #define OPCODE_WREN 0x06 /* Write enable */ #define OPCODE_RDSR 0x05 /* Read status register */ -#define OPCODE_READ 0x03 /* Read data bytes (low frequency) */ +#define OPCODE_NORM_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 4KiB block */ @@ -52,7 +52,15 @@ /* Define max times to check status register before we give up. */ #define MAX_READY_WAIT_COUNT 100000 +#define CMD_SIZE 4 +#ifdef CONFIG_M25PXX_USE_FAST_READ +#define OPCODE_READ OPCODE_FAST_READ +#define FAST_READ_DUMMY_BYTE 1 +#else +#define OPCODE_READ OPCODE_NORM_READ +#define FAST_READ_DUMMY_BYTE 0 +#endif #ifdef CONFIG_MTD_PARTITIONS #define mtd_has_partitions() (1) @@ -68,7 +76,7 @@ struct m25p { struct mtd_info mtd; unsigned partitioned:1; u8 erase_opcode; - u8 command[4]; + u8 command[CMD_SIZE + FAST_READ_DUMMY_BYTE]; }; static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) @@ -151,7 +159,7 @@ static int wait_till_ready(struct m25p *flash) static int erase_sector(struct m25p *flash, u32 offset) { DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n", - flash->spi->dev.bus_id, __FUNCTION__, + flash->spi->dev.bus_id, __func__, flash->mtd.erasesize / 1024, offset); /* Wait until finished previous write command. */ @@ -167,7 +175,7 @@ static int erase_sector(struct m25p *flash, u32 offset) flash->command[2] = offset >> 8; flash->command[3] = offset; - spi_write(flash->spi, flash->command, sizeof(flash->command)); + spi_write(flash->spi, flash->command, CMD_SIZE); return 0; } @@ -188,7 +196,7 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) u32 addr,len; DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n", - flash->spi->dev.bus_id, __FUNCTION__, "at", + flash->spi->dev.bus_id, __func__, "at", (u32)instr->addr, instr->len); /* sanity checks */ @@ -240,7 +248,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, struct spi_message m; DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", - flash->spi->dev.bus_id, __FUNCTION__, "from", + flash->spi->dev.bus_id, __func__, "from", (u32)from, len); /* sanity checks */ @@ -253,8 +261,12 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, spi_message_init(&m); memset(t, 0, (sizeof t)); + /* NOTE: + * OPCODE_FAST_READ (if available) is faster. + * Should add 1 byte DUMMY_BYTE. + */ t[0].tx_buf = flash->command; - t[0].len = sizeof(flash->command); + t[0].len = CMD_SIZE + FAST_READ_DUMMY_BYTE; spi_message_add_tail(&t[0], &m); t[1].rx_buf = buf; @@ -287,7 +299,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, spi_sync(flash->spi, &m); - *retlen = m.actual_length - sizeof(flash->command); + *retlen = m.actual_length - CMD_SIZE - FAST_READ_DUMMY_BYTE; mutex_unlock(&flash->lock); @@ -308,7 +320,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, struct spi_message m; DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", - flash->spi->dev.bus_id, __FUNCTION__, "to", + flash->spi->dev.bus_id, __func__, "to", (u32)to, len); if (retlen) @@ -325,7 +337,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, memset(t, 0, (sizeof t)); t[0].tx_buf = flash->command; - t[0].len = sizeof(flash->command); + t[0].len = CMD_SIZE; spi_message_add_tail(&t[0], &m); t[1].tx_buf = buf; @@ -354,7 +366,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, spi_sync(flash->spi, &m); - *retlen = m.actual_length - sizeof(flash->command); + *retlen = m.actual_length - CMD_SIZE; } else { u32 i; @@ -364,7 +376,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, t[1].len = page_size; spi_sync(flash->spi, &m); - *retlen = m.actual_length - sizeof(flash->command); + *retlen = m.actual_length - CMD_SIZE; /* write everything in PAGESIZE chunks */ for (i = page_size; i < len; i += page_size) { @@ -387,8 +399,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, spi_sync(flash->spi, &m); if (retlen) - *retlen += m.actual_length - - sizeof(flash->command); + *retlen += m.actual_length - CMD_SIZE; } } @@ -435,6 +446,7 @@ static struct flash_info __devinitdata m25p_data [] = { { "at25fs040", 0x1f6604, 64 * 1024, 8, SECT_4K, }, { "at25df041a", 0x1f4401, 64 * 1024, 8, SECT_4K, }, + { "at25df641", 0x1f4800, 64 * 1024, 128, SECT_4K, }, { "at26f004", 0x1f0400, 64 * 1024, 8, SECT_4K, }, { "at26df081a", 0x1f4501, 64 * 1024, 16, SECT_4K, }, diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index e427c82d5f4c..bf485ff49457 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/mtd/compatmac.h> #include <linux/mtd/mtd.h> +#include <linux/mtd/mtdram.h> static unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE; static unsigned long erase_size = CONFIG_MTDRAM_ERASE_SIZE; diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 180298b92a7a..5f960182da95 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -282,7 +282,7 @@ static int phram_setup(const char *val, struct kernel_param *kp) } module_param_call(phram, phram_setup, NULL, NULL, 000); -MODULE_PARM_DESC(phram,"Memory region to map. \"map=<name>,<start>,<length>\""); +MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\""); static int __init init_phram(void) diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index c815d0f38577..4a79b187b568 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -136,8 +136,6 @@ typedef struct partition_t { #endif } partition_t; -void ftl_freepart(partition_t *part); - /* Partition state flags */ #define FTL_FORMATTED 0x01 @@ -1014,7 +1012,7 @@ static int ftl_writesect(struct mtd_blktrans_dev *dev, /*====================================================================*/ -void ftl_freepart(partition_t *part) +static void ftl_freepart(partition_t *part) { vfree(part->VirtualBlockMap); part->VirtualBlockMap = NULL; @@ -1069,7 +1067,7 @@ static void ftl_remove_dev(struct mtd_blktrans_dev *dev) kfree(dev); } -struct mtd_blktrans_ops ftl_tr = { +static struct mtd_blktrans_ops ftl_tr = { .name = "ftl", .major = FTL_MAJOR, .part_bits = PART_BITS, diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index b8917beeb650..c551d2f0779c 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -41,11 +41,6 @@ char inftlmountrev[]="$Revision: 1.18 $"; -extern int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, - size_t *retlen, uint8_t *buf); -extern int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, - size_t *retlen, uint8_t *buf); - /* * find_boot_record: Find the INFTL Media Header and its Spare copy which * contains the various device information of the INFTL partition and diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 12c253664eb2..1bd69aa9e22a 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -21,6 +21,9 @@ config MTD_PHYSMAP particular board as well as the bus width, either statically with config options or at run-time. + To compile this driver as a module, choose M here: the + module will be called physmap. + config MTD_PHYSMAP_START hex "Physical start address of flash mapping" depends on MTD_PHYSMAP diff --git a/drivers/mtd/maps/bast-flash.c b/drivers/mtd/maps/bast-flash.c index fc3b2672d1e2..1f492062f8ca 100644 --- a/drivers/mtd/maps/bast-flash.c +++ b/drivers/mtd/maps/bast-flash.c @@ -137,7 +137,7 @@ static int bast_flash_probe(struct platform_device *pdev) if (info->map.size > AREA_MAXSIZE) info->map.size = AREA_MAXSIZE; - pr_debug("%s: area %08lx, size %ld\n", __FUNCTION__, + pr_debug("%s: area %08lx, size %ld\n", __func__, info->map.phys, info->map.size); info->area = request_mem_region(res->start, info->map.size, @@ -149,7 +149,7 @@ static int bast_flash_probe(struct platform_device *pdev) } info->map.virt = ioremap(res->start, info->map.size); - pr_debug("%s: virt at %08x\n", __FUNCTION__, (int)info->map.virt); + pr_debug("%s: virt at %08x\n", __func__, (int)info->map.virt); if (info->map.virt == 0) { printk(KERN_ERR PFX "failed to ioremap() region\n"); @@ -223,3 +223,4 @@ module_exit(bast_flash_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_DESCRIPTION("BAST MTD Map driver"); +MODULE_ALIAS("platform:bast-nor"); diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 688ef495888a..59d8fb49270a 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c @@ -28,6 +28,9 @@ #define ROM_PROBE_STEP_SIZE (64*1024) +#define DEV_CK804 1 +#define DEV_MCP55 2 + struct ck804xrom_window { void __iomem *virt; unsigned long phys; @@ -45,8 +48,9 @@ struct ck804xrom_map_info { char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; }; - -/* The 2 bits controlling the window size are often set to allow reading +/* + * The following applies to ck804 only: + * The 2 bits controlling the window size are often set to allow reading * the BIOS, but too small to allow writing, since the lock registers are * 4MiB lower in the address space than the data. * @@ -58,10 +62,17 @@ struct ck804xrom_map_info { * If only the 7 Bit is set, it is a 4MiB window. Otherwise, a * 64KiB window. * + * The following applies to mcp55 only: + * The 15 bits controlling the window size are distributed as follows: + * byte @0x88: bit 0..7 + * byte @0x8c: bit 8..15 + * word @0x90: bit 16..30 + * If all bits are enabled, we have a 16? MiB window + * Please set win_size_bits to 0x7fffffff if you actually want to do something */ static uint win_size_bits = 0; module_param(win_size_bits, uint, 0); -MODULE_PARM_DESC(win_size_bits, "ROM window size bits override for 0x88 byte, normally set by BIOS."); +MODULE_PARM_DESC(win_size_bits, "ROM window size bits override, normally set by BIOS."); static struct ck804xrom_window ck804xrom_window = { .maps = LIST_HEAD_INIT(ck804xrom_window.maps), @@ -102,10 +113,11 @@ static void ck804xrom_cleanup(struct ck804xrom_window *window) static int __devinit ck804xrom_init_one (struct pci_dev *pdev, - const struct pci_device_id *ent) + const struct pci_device_id *ent) { static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; u8 byte; + u16 word; struct ck804xrom_window *window = &ck804xrom_window; struct ck804xrom_map_info *map = NULL; unsigned long map_top; @@ -113,26 +125,42 @@ static int __devinit ck804xrom_init_one (struct pci_dev *pdev, /* Remember the pci dev I find the window in */ window->pdev = pci_dev_get(pdev); - /* Enable the selected rom window. This is often incorrectly - * set up by the BIOS, and the 4MiB offset for the lock registers - * requires the full 5MiB of window space. - * - * This 'write, then read' approach leaves the bits for - * other uses of the hardware info. - */ - pci_read_config_byte(pdev, 0x88, &byte); - pci_write_config_byte(pdev, 0x88, byte | win_size_bits ); - - - /* Assume the rom window is properly setup, and find it's size */ - pci_read_config_byte(pdev, 0x88, &byte); - - if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) - window->phys = 0xffb00000; /* 5MiB */ - else if ((byte & (1<<7)) == (1<<7)) - window->phys = 0xffc00000; /* 4MiB */ - else - window->phys = 0xffff0000; /* 64KiB */ + switch (ent->driver_data) { + case DEV_CK804: + /* Enable the selected rom window. This is often incorrectly + * set up by the BIOS, and the 4MiB offset for the lock registers + * requires the full 5MiB of window space. + * + * This 'write, then read' approach leaves the bits for + * other uses of the hardware info. + */ + pci_read_config_byte(pdev, 0x88, &byte); + pci_write_config_byte(pdev, 0x88, byte | win_size_bits ); + + /* Assume the rom window is properly setup, and find it's size */ + pci_read_config_byte(pdev, 0x88, &byte); + + if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) + window->phys = 0xffb00000; /* 5MiB */ + else if ((byte & (1<<7)) == (1<<7)) + window->phys = 0xffc00000; /* 4MiB */ + else + window->phys = 0xffff0000; /* 64KiB */ + break; + + case DEV_MCP55: + pci_read_config_byte(pdev, 0x88, &byte); + pci_write_config_byte(pdev, 0x88, byte | (win_size_bits & 0xff)); + + pci_read_config_byte(pdev, 0x8c, &byte); + pci_write_config_byte(pdev, 0x8c, byte | ((win_size_bits & 0xff00) >> 8)); + + pci_read_config_word(pdev, 0x90, &word); + pci_write_config_word(pdev, 0x90, word | ((win_size_bits & 0x7fff0000) >> 16)); + + window->phys = 0xff000000; /* 16MiB, hardcoded for now */ + break; + } window->size = 0xffffffffUL - window->phys + 1UL; @@ -303,8 +331,15 @@ static void __devexit ck804xrom_remove_one (struct pci_dev *pdev) } static struct pci_device_id ck804xrom_pci_tbl[] = { - { PCI_VENDOR_ID_NVIDIA, 0x0051, - PCI_ANY_ID, PCI_ANY_ID, }, /* nvidia ck804 */ + { PCI_VENDOR_ID_NVIDIA, 0x0051, PCI_ANY_ID, PCI_ANY_ID, DEV_CK804 }, + { PCI_VENDOR_ID_NVIDIA, 0x0360, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, + { PCI_VENDOR_ID_NVIDIA, 0x0361, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, + { PCI_VENDOR_ID_NVIDIA, 0x0362, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, + { PCI_VENDOR_ID_NVIDIA, 0x0363, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, + { PCI_VENDOR_ID_NVIDIA, 0x0364, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, + { PCI_VENDOR_ID_NVIDIA, 0x0365, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, + { PCI_VENDOR_ID_NVIDIA, 0x0366, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, + { PCI_VENDOR_ID_NVIDIA, 0x0367, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, { 0, } }; @@ -332,7 +367,7 @@ static int __init init_ck804xrom(void) break; } if (pdev) { - retVal = ck804xrom_init_one(pdev, &ck804xrom_pci_tbl[0]); + retVal = ck804xrom_init_one(pdev, id); pci_dev_put(pdev); return retVal; } diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index 6946d802e6f6..325c8880c437 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c @@ -190,6 +190,7 @@ static struct platform_driver armflash_driver = { .remove = armflash_remove, .driver = { .name = "armflash", + .owner = THIS_MODULE, }, }; @@ -209,3 +210,4 @@ module_exit(armflash_exit); MODULE_AUTHOR("ARM Ltd"); MODULE_DESCRIPTION("ARM Integrator CFI map driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:armflash"); diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c index c26488a1793a..c8396b8574c4 100644 --- a/drivers/mtd/maps/ixp2000.c +++ b/drivers/mtd/maps/ixp2000.c @@ -253,6 +253,7 @@ static struct platform_driver ixp2000_flash_driver = { .remove = ixp2000_flash_remove, .driver = { .name = "IXP2000-Flash", + .owner = THIS_MODULE, }, }; @@ -270,4 +271,4 @@ module_init(ixp2000_flash_init); module_exit(ixp2000_flash_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>"); - +MODULE_ALIAS("platform:IXP2000-Flash"); diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 7a828e3e6446..01f19a4714b5 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -275,6 +275,7 @@ static struct platform_driver ixp4xx_flash_driver = { .remove = ixp4xx_flash_remove, .driver = { .name = "IXP4XX-Flash", + .owner = THIS_MODULE, }, }; @@ -295,3 +296,4 @@ module_exit(ixp4xx_flash_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("MTD map driver for Intel IXP4xx systems"); MODULE_AUTHOR("Deepak Saxena"); +MODULE_ALIAS("platform:IXP4XX-Flash"); diff --git a/drivers/mtd/maps/omap_nor.c b/drivers/mtd/maps/omap_nor.c index e8d9ae535673..240b0e2d095d 100644 --- a/drivers/mtd/maps/omap_nor.c +++ b/drivers/mtd/maps/omap_nor.c @@ -70,7 +70,7 @@ static void omap_set_vpp(struct map_info *map, int enable) } } -static int __devinit omapflash_probe(struct platform_device *pdev) +static int __init omapflash_probe(struct platform_device *pdev) { int err; struct omapflash_info *info; @@ -130,7 +130,7 @@ out_free_info: return err; } -static int __devexit omapflash_remove(struct platform_device *pdev) +static int __exit omapflash_remove(struct platform_device *pdev) { struct omapflash_info *info = platform_get_drvdata(pdev); @@ -152,16 +152,16 @@ static int __devexit omapflash_remove(struct platform_device *pdev) } static struct platform_driver omapflash_driver = { - .probe = omapflash_probe, - .remove = __devexit_p(omapflash_remove), + .remove = __exit_p(omapflash_remove), .driver = { .name = "omapflash", + .owner = THIS_MODULE, }, }; static int __init omapflash_init(void) { - return platform_driver_register(&omapflash_driver); + return platform_driver_probe(&omapflash_driver, omapflash_probe); } static void __exit omapflash_exit(void) @@ -174,4 +174,4 @@ module_exit(omapflash_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("MTD NOR map driver for TI OMAP boards"); - +MODULE_ALIAS("platform:omapflash"); diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index eaeb56a4070a..1912d968718b 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -33,7 +33,7 @@ MODULE_PARM_DESC(debug, "Set Debug Level 0=quiet, 5=noisy"); #undef DEBUG #define DEBUG(n, format, arg...) \ if (n <= debug) { \ - printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __FUNCTION__ , ## arg); \ + printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __func__ , ## arg); \ } #else diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index bc4649a17b9d..183255fcfdcb 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -242,6 +242,7 @@ static struct platform_driver physmap_flash_driver = { .shutdown = physmap_flash_shutdown, .driver = { .name = "physmap-flash", + .owner = THIS_MODULE, }, }; @@ -319,3 +320,10 @@ module_exit(physmap_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); MODULE_DESCRIPTION("Generic configurable MTD map driver"); + +/* legacy platform drivers can't hotplug or coldplg */ +#ifndef PHYSMAP_COMPAT +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:physmap-flash"); +#endif + diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 894c0b271289..f0b10ca05029 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -47,6 +47,7 @@ struct platram_info { struct mtd_info *mtd; struct map_info map; struct mtd_partition *partitions; + bool free_partitions; struct resource *area; struct platdata_mtd_ram *pdata; }; @@ -98,7 +99,8 @@ static int platram_remove(struct platform_device *pdev) #ifdef CONFIG_MTD_PARTITIONS if (info->partitions) { del_mtd_partitions(info->mtd); - kfree(info->partitions); + if (info->free_partitions) + kfree(info->partitions); } #endif del_mtd_device(info->mtd); @@ -176,7 +178,8 @@ static int platram_probe(struct platform_device *pdev) info->map.phys = res->start; info->map.size = (res->end - res->start) + 1; - info->map.name = pdata->mapname != NULL ? pdata->mapname : (char *)pdev->name; + info->map.name = pdata->mapname != NULL ? + (char *)pdata->mapname : (char *)pdev->name; info->map.bankwidth = pdata->bankwidth; /* register our usage of the memory area */ @@ -203,9 +206,19 @@ static int platram_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "initialised map, probing for mtd\n"); - /* probe for the right mtd map driver */ + /* probe for the right mtd map driver + * supplied by the platform_data struct */ + + if (pdata->map_probes != 0) { + const char **map_probes = pdata->map_probes; + + for ( ; !info->mtd && *map_probes; map_probes++) + info->mtd = do_map_probe(*map_probes , &info->map); + } + /* fallback to map_ram */ + else + info->mtd = do_map_probe("map_ram", &info->map); - info->mtd = do_map_probe("map_ram" , &info->map); if (info->mtd == NULL) { dev_err(&pdev->dev, "failed to probe for map_ram\n"); err = -ENOMEM; @@ -220,19 +233,21 @@ static int platram_probe(struct platform_device *pdev) * to add this device whole */ #ifdef CONFIG_MTD_PARTITIONS - if (pdata->nr_partitions > 0) { - const char **probes = { NULL }; - - if (pdata->probes) - probes = (const char **)pdata->probes; - - err = parse_mtd_partitions(info->mtd, probes, + if (!pdata->nr_partitions) { + /* try to probe using the supplied probe type */ + if (pdata->probes) { + err = parse_mtd_partitions(info->mtd, pdata->probes, &info->partitions, 0); - if (err > 0) { - err = add_mtd_partitions(info->mtd, info->partitions, - err); + info->free_partitions = 1; + if (err > 0) + err = add_mtd_partitions(info->mtd, + info->partitions, err); } } + /* use the static mapping */ + else + err = add_mtd_partitions(info->mtd, pdata->partitions, + pdata->nr_partitions); #endif /* CONFIG_MTD_PARTITIONS */ if (add_mtd_device(info->mtd)) { @@ -240,7 +255,9 @@ static int platram_probe(struct platform_device *pdev) err = -ENOMEM; } - dev_info(&pdev->dev, "registered mtd device\n"); + if (!err) + dev_info(&pdev->dev, "registered mtd device\n"); + return err; exit_free: @@ -251,6 +268,9 @@ static int platram_probe(struct platform_device *pdev) /* device driver info */ +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:mtd-ram"); + static struct platform_driver platram_driver = { .probe = platram_probe, .remove = platram_remove, diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c index 02bde8c982ec..f43ba2815cbb 100644 --- a/drivers/mtd/maps/pmcmsp-flash.c +++ b/drivers/mtd/maps/pmcmsp-flash.c @@ -46,7 +46,7 @@ static struct mtd_partition **msp_parts; static struct map_info *msp_maps; static int fcnt; -#define DEBUG_MARKER printk(KERN_NOTICE "%s[%d]\n",__FUNCTION__,__LINE__) +#define DEBUG_MARKER printk(KERN_NOTICE "%s[%d]\n", __func__, __LINE__) int __init init_msp_flash(void) { diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index f904e6bd02e0..c7d5a52a2d55 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -456,6 +456,7 @@ static struct platform_driver sa1100_mtd_driver = { .shutdown = sa1100_mtd_shutdown, .driver = { .name = "flash", + .owner = THIS_MODULE, }, }; @@ -475,3 +476,4 @@ module_exit(sa1100_mtd_exit); MODULE_AUTHOR("Nicolas Pitre"); MODULE_DESCRIPTION("SA1100 CFI map driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:flash"); diff --git a/drivers/mtd/maps/sharpsl-flash.c b/drivers/mtd/maps/sharpsl-flash.c index 12fe53c0d2fc..917dc778f24e 100644 --- a/drivers/mtd/maps/sharpsl-flash.c +++ b/drivers/mtd/maps/sharpsl-flash.c @@ -92,7 +92,7 @@ int __init init_sharpsl(void) parts = sharpsl_partitions; nb_parts = ARRAY_SIZE(sharpsl_partitions); - printk(KERN_NOTICE "Using %s partision definition\n", part_type); + printk(KERN_NOTICE "Using %s partition definition\n", part_type); add_mtd_partitions(mymtd, parts, nb_parts); return 0; diff --git a/drivers/mtd/maps/tqm8xxl.c b/drivers/mtd/maps/tqm8xxl.c index 37e4ded9b600..521734057314 100644 --- a/drivers/mtd/maps/tqm8xxl.c +++ b/drivers/mtd/maps/tqm8xxl.c @@ -124,7 +124,7 @@ int __init init_tqm_mtd(void) //request maximum flash size address space start_scan_addr = ioremap(flash_addr, flash_size); if (!start_scan_addr) { - printk(KERN_WARNING "%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr); + printk(KERN_WARNING "%s:Failed to ioremap address:0x%x\n", __func__, flash_addr); return -EIO; } @@ -132,7 +132,7 @@ int __init init_tqm_mtd(void) if(mtd_size >= flash_size) break; - printk(KERN_INFO "%s: chip probing count %d\n", __FUNCTION__, idx); + printk(KERN_INFO "%s: chip probing count %d\n", __func__, idx); map_banks[idx] = kzalloc(sizeof(struct map_info), GFP_KERNEL); if(map_banks[idx] == NULL) { @@ -178,7 +178,7 @@ int __init init_tqm_mtd(void) mtd_size += mtd_banks[idx]->size; num_banks++; - printk(KERN_INFO "%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks, + printk(KERN_INFO "%s: bank%d, name:%s, size:%dbytes \n", __func__, num_banks, mtd_banks[idx]->name, mtd_banks[idx]->size); } } diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index d3cf05012b46..5a680e1e61f1 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -35,7 +35,7 @@ #define OOPS_PAGE_SIZE 4096 -struct mtdoops_context { +static struct mtdoops_context { int mtd_index; struct work_struct work_erase; struct work_struct work_write; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 959fb86cda01..5076faf9ca66 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -278,6 +278,54 @@ config MTD_NAND_AT91 help Enables support for NAND Flash / Smart Media Card interface on Atmel AT91 processors. +choice + prompt "ECC management for NAND Flash / SmartMedia on AT91" + depends on MTD_NAND_AT91 + +config MTD_NAND_AT91_ECC_HW + bool "Hardware ECC" + depends on ARCH_AT91SAM9263 || ARCH_AT91SAM9260 + help + Uses hardware ECC provided by the at91sam9260/at91sam9263 chip + instead of software ECC. + The hardware ECC controller is capable of single bit error + correction and 2-bit random detection per page. + + NB : hardware and software ECC schemes are incompatible. + If you switch from one to another, you'll have to erase your + mtd partition. + + If unsure, say Y + +config MTD_NAND_AT91_ECC_SOFT + bool "Software ECC" + help + Uses software ECC. + + NB : hardware and software ECC schemes are incompatible. + If you switch from one to another, you'll have to erase your + mtd partition. + +config MTD_NAND_AT91_ECC_NONE + bool "No ECC (testing only, DANGEROUS)" + depends on DEBUG_KERNEL + help + No ECC will be used. + It's not a good idea and it should be reserved for testing + purpose only. + + If unsure, say N + + endchoice + +endchoice + +config MTD_NAND_PXA3xx + bool "Support for NAND flash devices on PXA3xx" + depends on MTD_NAND && PXA3xx + help + This enables the driver for the NAND flash device found on + PXA3xx processors config MTD_NAND_CM_X270 tristate "Support for NAND Flash on CM-X270 modules" @@ -330,4 +378,12 @@ config MTD_NAND_FSL_ELBC Enabling this option will enable you to use this to control external NAND devices. +config MTD_NAND_FSL_UPM + tristate "Support for NAND on Freescale UPM" + depends on MTD_NAND && OF_GPIO && (PPC_83xx || PPC_85xx) + select FSL_LBC + help + Enables support for NAND Flash chips wired onto Freescale PowerPC + processor localbus with User-Programmable Machine support. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 80d575eeee96..a6e74a46992a 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -27,10 +27,12 @@ obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o 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_PXA3xx) += pxa3xx_nand.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o obj-$(CONFIG_MTD_ALAUDA) += alauda.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o +obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/at91_nand.c b/drivers/mtd/nand/at91_nand.c index c9fb2acf4056..414ceaecdb3a 100644 --- a/drivers/mtd/nand/at91_nand.c +++ b/drivers/mtd/nand/at91_nand.c @@ -9,6 +9,15 @@ * Derived from drivers/mtd/spia.c * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) * + * + * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 + * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright (C) 2007 + * + * Derived from Das U-Boot source code + * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) + * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas + * + * * 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. @@ -29,11 +38,59 @@ #include <asm/arch/board.h> #include <asm/arch/gpio.h> +#ifdef CONFIG_MTD_NAND_AT91_ECC_HW +#define hard_ecc 1 +#else +#define hard_ecc 0 +#endif + +#ifdef CONFIG_MTD_NAND_AT91_ECC_NONE +#define no_ecc 1 +#else +#define no_ecc 0 +#endif + +/* Register access macros */ +#define ecc_readl(add, reg) \ + __raw_readl(add + AT91_ECC_##reg) +#define ecc_writel(add, reg, value) \ + __raw_writel((value), add + AT91_ECC_##reg) + +#include <asm/arch/at91_ecc.h> /* AT91SAM9260/3 ECC registers */ + +/* oob layout for large page size + * bad block info is on bytes 0 and 1 + * the bytes have to be consecutives to avoid + * several NAND_CMD_RNDOUT during read + */ +static struct nand_ecclayout at91_oobinfo_large = { + .eccbytes = 4, + .eccpos = {60, 61, 62, 63}, + .oobfree = { + {2, 58} + }, +}; + +/* oob layout for small page size + * bad block info is on bytes 4 and 5 + * the bytes have to be consecutives to avoid + * several NAND_CMD_RNDOUT during read + */ +static struct nand_ecclayout at91_oobinfo_small = { + .eccbytes = 4, + .eccpos = {0, 1, 2, 3}, + .oobfree = { + {6, 10} + }, +}; + struct at91_nand_host { struct nand_chip nand_chip; struct mtd_info mtd; void __iomem *io_base; struct at91_nand_data *board; + struct device *dev; + void __iomem *ecc; }; /* @@ -44,6 +101,12 @@ static void at91_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) struct nand_chip *nand_chip = mtd->priv; struct at91_nand_host *host = nand_chip->priv; + if (host->board->enable_pin && (ctrl & NAND_CTRL_CHANGE)) { + if (ctrl & NAND_NCE) + at91_set_gpio_value(host->board->enable_pin, 0); + else + at91_set_gpio_value(host->board->enable_pin, 1); + } if (cmd == NAND_CMD_NONE) return; @@ -82,8 +145,217 @@ static void at91_nand_disable(struct at91_nand_host *host) at91_set_gpio_value(host->board->enable_pin, 1); } +/* + * write oob for small pages + */ +static int at91_nand_write_oob_512(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size, length = mtd->oobsize; + int len, pos, status = 0; + const uint8_t *bufpoi = chip->oob_poi; + + pos = eccsize + chunk; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); + len = min_t(int, length, chunk); + chip->write_buf(mtd, bufpoi, len); + bufpoi += len; + length -= len; + if (length > 0) + chip->write_buf(mtd, bufpoi, length); + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; + +} + +/* + * read oob for small pages + */ +static int at91_nand_read_oob_512(struct mtd_info *mtd, + struct nand_chip *chip, int page, int sndcmd) +{ + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return sndcmd; +} + +/* + * Calculate HW ECC + * + * function called after a write + * + * mtd: MTD block structure + * dat: raw data (unused) + * ecc_code: buffer for ECC + */ +static int at91_nand_calculate(struct mtd_info *mtd, + const u_char *dat, unsigned char *ecc_code) +{ + struct nand_chip *nand_chip = mtd->priv; + struct at91_nand_host *host = nand_chip->priv; + uint32_t *eccpos = nand_chip->ecc.layout->eccpos; + unsigned int ecc_value; + + /* get the first 2 ECC bytes */ + ecc_value = ecc_readl(host->ecc, PR); + + ecc_code[eccpos[0]] = ecc_value & 0xFF; + ecc_code[eccpos[1]] = (ecc_value >> 8) & 0xFF; + + /* get the last 2 ECC bytes */ + ecc_value = ecc_readl(host->ecc, NPR) & AT91_ECC_NPARITY; + + ecc_code[eccpos[2]] = ecc_value & 0xFF; + ecc_code[eccpos[3]] = (ecc_value >> 8) & 0xFF; + + return 0; +} + +/* + * HW ECC read page function + * + * mtd: mtd info structure + * chip: nand chip info structure + * buf: buffer to store read data + */ +static int at91_nand_read_page(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + uint8_t *ecc_pos; + int stat; + + /* read the page */ + chip->read_buf(mtd, p, eccsize); + + /* move to ECC position if needed */ + if (eccpos[0] != 0) { + /* This only works on large pages + * because the ECC controller waits for + * NAND_CMD_RNDOUTSTART after the + * NAND_CMD_RNDOUT. + * anyway, for small pages, the eccpos[0] == 0 + */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + mtd->writesize + eccpos[0], -1); + } + + /* the ECC controller needs to read the ECC just after the data */ + ecc_pos = oob + eccpos[0]; + chip->read_buf(mtd, ecc_pos, eccbytes); + + /* check if there's an error */ + stat = chip->ecc.correct(mtd, p, oob, NULL); + + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + + /* get back to oob start (end of page) */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); + + /* read the oob */ + chip->read_buf(mtd, oob, mtd->oobsize); + + return 0; +} + +/* + * HW ECC Correction + * + * function called after a read + * + * mtd: MTD block structure + * dat: raw data read from the chip + * read_ecc: ECC from the chip (unused) + * isnull: unused + * + * Detect and correct a 1 bit error for a page + */ +static int at91_nand_correct(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *isnull) +{ + struct nand_chip *nand_chip = mtd->priv; + struct at91_nand_host *host = nand_chip->priv; + unsigned int ecc_status; + unsigned int ecc_word, ecc_bit; + + /* get the status from the Status Register */ + ecc_status = ecc_readl(host->ecc, SR); + + /* if there's no error */ + if (likely(!(ecc_status & AT91_ECC_RECERR))) + return 0; + + /* get error bit offset (4 bits) */ + ecc_bit = ecc_readl(host->ecc, PR) & AT91_ECC_BITADDR; + /* get word address (12 bits) */ + ecc_word = ecc_readl(host->ecc, PR) & AT91_ECC_WORDADDR; + ecc_word >>= 4; + + /* if there are multiple errors */ + if (ecc_status & AT91_ECC_MULERR) { + /* check if it is a freshly erased block + * (filled with 0xff) */ + if ((ecc_bit == AT91_ECC_BITADDR) + && (ecc_word == (AT91_ECC_WORDADDR >> 4))) { + /* the block has just been erased, return OK */ + return 0; + } + /* it doesn't seems to be a freshly + * erased block. + * We can't correct so many errors */ + dev_dbg(host->dev, "at91_nand : multiple errors detected." + " Unable to correct.\n"); + return -EIO; + } + + /* if there's a single bit error : we can correct it */ + if (ecc_status & AT91_ECC_ECCERR) { + /* there's nothing much to do here. + * the bit error is on the ECC itself. + */ + dev_dbg(host->dev, "at91_nand : one bit error on ECC code." + " Nothing to correct\n"); + return 0; + } + + dev_dbg(host->dev, "at91_nand : one bit error on data." + " (word offset in the page :" + " 0x%x bit offset : 0x%x)\n", + ecc_word, ecc_bit); + /* correct the error */ + if (nand_chip->options & NAND_BUSWIDTH_16) { + /* 16 bits words */ + ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit); + } else { + /* 8 bits words */ + dat[ecc_word] ^= (1 << ecc_bit); + } + dev_dbg(host->dev, "at91_nand : error corrected\n"); + return 1; +} + +/* + * Enable HW ECC : unsused + */ +static void at91_nand_hwctl(struct mtd_info *mtd, int mode) { ; } + #ifdef CONFIG_MTD_PARTITIONS -const char *part_probes[] = { "cmdlinepart", NULL }; +static const char *part_probes[] = { "cmdlinepart", NULL }; #endif /* @@ -94,6 +366,8 @@ static int __init at91_nand_probe(struct platform_device *pdev) struct at91_nand_host *host; struct mtd_info *mtd; struct nand_chip *nand_chip; + struct resource *regs; + struct resource *mem; int res; #ifdef CONFIG_MTD_PARTITIONS @@ -108,8 +382,13 @@ static int __init at91_nand_probe(struct platform_device *pdev) return -ENOMEM; } - host->io_base = ioremap(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + printk(KERN_ERR "at91_nand: can't get I/O resource mem\n"); + return -ENXIO; + } + + host->io_base = ioremap(mem->start, mem->end - mem->start + 1); if (host->io_base == NULL) { printk(KERN_ERR "at91_nand: ioremap failed\n"); kfree(host); @@ -119,6 +398,7 @@ static int __init at91_nand_probe(struct platform_device *pdev) mtd = &host->mtd; nand_chip = &host->nand_chip; host->board = pdev->dev.platform_data; + host->dev = &pdev->dev; nand_chip->priv = host; /* link the private data structures */ mtd->priv = nand_chip; @@ -132,7 +412,32 @@ static int __init at91_nand_probe(struct platform_device *pdev) if (host->board->rdy_pin) nand_chip->dev_ready = at91_nand_device_ready; + regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!regs && hard_ecc) { + printk(KERN_ERR "at91_nand: can't get I/O resource " + "regs\nFalling back on software ECC\n"); + } + nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ + if (no_ecc) + nand_chip->ecc.mode = NAND_ECC_NONE; + if (hard_ecc && regs) { + host->ecc = ioremap(regs->start, regs->end - regs->start + 1); + if (host->ecc == NULL) { + printk(KERN_ERR "at91_nand: ioremap failed\n"); + res = -EIO; + goto err_ecc_ioremap; + } + nand_chip->ecc.mode = NAND_ECC_HW_SYNDROME; + nand_chip->ecc.calculate = at91_nand_calculate; + nand_chip->ecc.correct = at91_nand_correct; + nand_chip->ecc.hwctl = at91_nand_hwctl; + nand_chip->ecc.read_page = at91_nand_read_page; + nand_chip->ecc.bytes = 4; + nand_chip->ecc.prepad = 0; + nand_chip->ecc.postpad = 0; + } + nand_chip->chip_delay = 20; /* 20us command delay time */ if (host->board->bus_width_16) /* 16-bit bus width */ @@ -149,8 +454,53 @@ static int __init at91_nand_probe(struct platform_device *pdev) } } - /* Scan to find existance of the device */ - if (nand_scan(mtd, 1)) { + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, 1)) { + res = -ENXIO; + goto out; + } + + if (nand_chip->ecc.mode == NAND_ECC_HW_SYNDROME) { + /* ECC is calculated for the whole page (1 step) */ + nand_chip->ecc.size = mtd->writesize; + + /* set ECC page size and oob layout */ + switch (mtd->writesize) { + case 512: + nand_chip->ecc.layout = &at91_oobinfo_small; + nand_chip->ecc.read_oob = at91_nand_read_oob_512; + nand_chip->ecc.write_oob = at91_nand_write_oob_512; + ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_528); + break; + case 1024: + nand_chip->ecc.layout = &at91_oobinfo_large; + ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_1056); + break; + case 2048: + nand_chip->ecc.layout = &at91_oobinfo_large; + ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_2112); + break; + case 4096: + nand_chip->ecc.layout = &at91_oobinfo_large; + ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_4224); + break; + default: + /* page size not handled by HW ECC */ + /* switching back to soft ECC */ + nand_chip->ecc.mode = NAND_ECC_SOFT; + nand_chip->ecc.calculate = NULL; + nand_chip->ecc.correct = NULL; + nand_chip->ecc.hwctl = NULL; + nand_chip->ecc.read_page = NULL; + nand_chip->ecc.postpad = 0; + nand_chip->ecc.prepad = 0; + nand_chip->ecc.bytes = 0; + break; + } + } + + /* second phase scan */ + if (nand_scan_tail(mtd)) { res = -ENXIO; goto out; } @@ -179,9 +529,15 @@ static int __init at91_nand_probe(struct platform_device *pdev) if (!res) return res; +#ifdef CONFIG_MTD_PARTITIONS release: +#endif nand_release(mtd); + out: + iounmap(host->ecc); + +err_ecc_ioremap: at91_nand_disable(host); platform_set_drvdata(pdev, NULL); iounmap(host->io_base); @@ -202,6 +558,7 @@ static int __devexit at91_nand_remove(struct platform_device *pdev) at91_nand_disable(host); iounmap(host->io_base); + iounmap(host->ecc); kfree(host); return 0; @@ -233,4 +590,5 @@ module_exit(at91_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Rick Bronson"); -MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200"); +MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200 / AT91SAM9"); +MODULE_ALIAS("platform:at91_nand"); diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 747042ab094a..e87a57297328 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -1,6 +1,6 @@ /* linux/drivers/mtd/nand/bf5xx_nand.c * - * Copyright 2006-2007 Analog Devices Inc. + * Copyright 2006-2008 Analog Devices Inc. * http://blackfin.uclinux.org/ * Bryan Wu <bryan.wu@analog.com> * @@ -74,7 +74,7 @@ static int hardware_ecc = 1; static int hardware_ecc; #endif -static unsigned short bfin_nfc_pin_req[] = +static const unsigned short bfin_nfc_pin_req[] = {P_NAND_CE, P_NAND_RB, P_NAND_D0, @@ -581,12 +581,6 @@ static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info) 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; @@ -654,6 +648,12 @@ static int bf5xx_nand_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "(%p)\n", pdev); + if (peripheral_request_list(bfin_nfc_pin_req, DRV_NAME)) { + printk(KERN_ERR DRV_NAME + ": Requesting Peripherals failed\n"); + return -EFAULT; + } + if (!plat) { dev_err(&pdev->dev, "no platform specific information\n"); goto exit_error; @@ -803,3 +803,4 @@ module_exit(bf5xx_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR(DRV_AUTHOR); MODULE_DESCRIPTION(DRV_DESC); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 8dab69657b19..3370a800fd36 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -279,7 +279,7 @@ static int is_geode(void) #ifdef CONFIG_MTD_PARTITIONS -const char *part_probes[] = { "cmdlinepart", NULL }; +static const char *part_probes[] = { "cmdlinepart", NULL }; #endif diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 378b7aa63812..4b69aacdf5ca 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -184,11 +184,11 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) in_be32(&lbc->fbar), in_be32(&lbc->fpar), in_be32(&lbc->fbcr), priv->bank); + ctrl->irq_status = 0; /* execute special operation */ out_be32(&lbc->lsor, priv->bank); /* wait for FCM complete flag or timeout */ - ctrl->irq_status = 0; wait_event_timeout(ctrl->irq_wait, ctrl->irq_status, FCM_TIMEOUT_MSECS * HZ/1000); ctrl->status = ctrl->irq_status; @@ -346,19 +346,20 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, ctrl->column = column; ctrl->oob = 0; - fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) | - (NAND_CMD_SEQIN << FCR_CMD2_SHIFT); - if (priv->page_size) { + fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) | + (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT); + out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) | (FIR_OP_CA << FIR_OP1_SHIFT) | (FIR_OP_PA << FIR_OP2_SHIFT) | (FIR_OP_WB << FIR_OP3_SHIFT) | (FIR_OP_CW1 << FIR_OP4_SHIFT)); - - fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; } else { + fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) | + (NAND_CMD_SEQIN << FCR_CMD2_SHIFT); + out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) | (FIR_OP_CM2 << FIR_OP1_SHIFT) | @@ -480,7 +481,7 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) struct fsl_elbc_ctrl *ctrl = priv->ctrl; unsigned int bufsize = mtd->writesize + mtd->oobsize; - if (len < 0) { + if (len <= 0) { dev_err(ctrl->dev, "write_buf of %d bytes", len); ctrl->status = 0; return; @@ -495,6 +496,15 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) } memcpy_toio(&ctrl->addr[ctrl->index], buf, len); + /* + * This is workaround for the weird elbc hangs during nand write, + * Scott Wood says: "...perhaps difference in how long it takes a + * write to make it through the localbus compared to a write to IMMR + * is causing problems, and sync isn't helping for some reason." + * Reading back the last byte helps though. + */ + in_8(&ctrl->addr[ctrl->index] + len - 1); + ctrl->index += len; } @@ -666,7 +676,7 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) /* adjust Option Register and ECC to match Flash page size */ if (mtd->writesize == 512) { priv->page_size = 0; - clrbits32(&lbc->bank[priv->bank].or, ~OR_FCM_PGS); + clrbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); } else if (mtd->writesize == 2048) { priv->page_size = 1; setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); @@ -687,11 +697,6 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) return -1; } - /* The default u-boot configuration on MPC8313ERDB causes errors; - * more delay is needed. This should be safe for other boards - * as well. - */ - setbits32(&lbc->bank[priv->bank].or, 0x70); return 0; } @@ -779,6 +784,8 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) nand_release(&priv->mtd); + kfree(priv->mtd.name); + if (priv->vbase) iounmap(priv->vbase); @@ -839,6 +846,12 @@ static int fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, goto err; } + priv->mtd.name = kasprintf(GFP_KERNEL, "%x.flash", res.start); + if (!priv->mtd.name) { + ret = -ENOMEM; + goto err; + } + ret = fsl_elbc_chip_init(priv); if (ret) goto err; diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c new file mode 100644 index 000000000000..1ebfd87f00b4 --- /dev/null +++ b/drivers/mtd/nand/fsl_upm.c @@ -0,0 +1,291 @@ +/* + * Freescale UPM NAND driver. + * + * Copyright © 2007-2008 MontaVista Software, Inc. + * + * Author: Anton Vorontsov <avorontsov@ru.mvista.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/mtd.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/io.h> +#include <asm/fsl_lbc.h> + +struct fsl_upm_nand { + struct device *dev; + struct mtd_info mtd; + struct nand_chip chip; + int last_ctrl; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; +#endif + + struct fsl_upm upm; + uint8_t upm_addr_offset; + uint8_t upm_cmd_offset; + void __iomem *io_base; + int rnb_gpio; + const uint32_t *wait_pattern; + const uint32_t *wait_write; + int chip_delay; +}; + +#define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd) + +static int fun_chip_ready(struct mtd_info *mtd) +{ + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + + if (gpio_get_value(fun->rnb_gpio)) + return 1; + + dev_vdbg(fun->dev, "busy\n"); + return 0; +} + +static void fun_wait_rnb(struct fsl_upm_nand *fun) +{ + int cnt = 1000000; + + if (fun->rnb_gpio >= 0) { + while (--cnt && !fun_chip_ready(&fun->mtd)) + cpu_relax(); + } + + if (!cnt) + dev_err(fun->dev, "tired waiting for RNB\n"); +} + +static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + + if (!(ctrl & fun->last_ctrl)) { + fsl_upm_end_pattern(&fun->upm); + + if (cmd == NAND_CMD_NONE) + return; + + fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE); + } + + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_ALE) + fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset); + else if (ctrl & NAND_CLE) + fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); + } + + fsl_upm_run_pattern(&fun->upm, fun->io_base, cmd); + + if (fun->wait_pattern) + fun_wait_rnb(fun); +} + +static uint8_t fun_read_byte(struct mtd_info *mtd) +{ + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + + return in_8(fun->chip.IO_ADDR_R); +} + +static void fun_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + int i; + + for (i = 0; i < len; i++) + buf[i] = in_8(fun->chip.IO_ADDR_R); +} + +static void fun_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + int i; + + for (i = 0; i < len; i++) { + out_8(fun->chip.IO_ADDR_W, buf[i]); + if (fun->wait_write) + fun_wait_rnb(fun); + } +} + +static int __devinit fun_chip_init(struct fsl_upm_nand *fun) +{ + int ret; +#ifdef CONFIG_MTD_PARTITIONS + static const char *part_types[] = { "cmdlinepart", NULL, }; +#endif + + fun->chip.IO_ADDR_R = fun->io_base; + fun->chip.IO_ADDR_W = fun->io_base; + fun->chip.cmd_ctrl = fun_cmd_ctrl; + fun->chip.chip_delay = fun->chip_delay; + fun->chip.read_byte = fun_read_byte; + fun->chip.read_buf = fun_read_buf; + fun->chip.write_buf = fun_write_buf; + fun->chip.ecc.mode = NAND_ECC_SOFT; + + if (fun->rnb_gpio >= 0) + fun->chip.dev_ready = fun_chip_ready; + + fun->mtd.priv = &fun->chip; + fun->mtd.owner = THIS_MODULE; + + ret = nand_scan(&fun->mtd, 1); + if (ret) + return ret; + + fun->mtd.name = fun->dev->bus_id; + +#ifdef CONFIG_MTD_PARTITIONS + ret = parse_mtd_partitions(&fun->mtd, part_types, &fun->parts, 0); + if (ret > 0) + return add_mtd_partitions(&fun->mtd, fun->parts, ret); +#endif + return add_mtd_device(&fun->mtd); +} + +static int __devinit fun_probe(struct of_device *ofdev, + const struct of_device_id *ofid) +{ + struct fsl_upm_nand *fun; + struct resource io_res; + const uint32_t *prop; + int ret; + int size; + + fun = kzalloc(sizeof(*fun), GFP_KERNEL); + if (!fun) + return -ENOMEM; + + ret = of_address_to_resource(ofdev->node, 0, &io_res); + if (ret) { + dev_err(&ofdev->dev, "can't get IO base\n"); + goto err1; + } + + ret = fsl_upm_find(io_res.start, &fun->upm); + if (ret) { + dev_err(&ofdev->dev, "can't find UPM\n"); + goto err1; + } + + prop = of_get_property(ofdev->node, "fsl,upm-addr-offset", &size); + if (!prop || size != sizeof(uint32_t)) { + dev_err(&ofdev->dev, "can't get UPM address offset\n"); + ret = -EINVAL; + goto err2; + } + fun->upm_addr_offset = *prop; + + prop = of_get_property(ofdev->node, "fsl,upm-cmd-offset", &size); + if (!prop || size != sizeof(uint32_t)) { + dev_err(&ofdev->dev, "can't get UPM command offset\n"); + ret = -EINVAL; + goto err2; + } + fun->upm_cmd_offset = *prop; + + fun->rnb_gpio = of_get_gpio(ofdev->node, 0); + if (fun->rnb_gpio >= 0) { + ret = gpio_request(fun->rnb_gpio, ofdev->dev.bus_id); + if (ret) { + dev_err(&ofdev->dev, "can't request RNB gpio\n"); + goto err2; + } + gpio_direction_input(fun->rnb_gpio); + } else if (fun->rnb_gpio == -EINVAL) { + dev_err(&ofdev->dev, "specified RNB gpio is invalid\n"); + goto err2; + } + + fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start, + io_res.end - io_res.start + 1); + if (!fun->io_base) { + ret = -ENOMEM; + goto err2; + } + + fun->dev = &ofdev->dev; + fun->last_ctrl = NAND_CLE; + fun->wait_pattern = of_get_property(ofdev->node, "fsl,wait-pattern", + NULL); + fun->wait_write = of_get_property(ofdev->node, "fsl,wait-write", NULL); + + prop = of_get_property(ofdev->node, "chip-delay", NULL); + if (prop) + fun->chip_delay = *prop; + else + fun->chip_delay = 50; + + ret = fun_chip_init(fun); + if (ret) + goto err2; + + dev_set_drvdata(&ofdev->dev, fun); + + return 0; +err2: + if (fun->rnb_gpio >= 0) + gpio_free(fun->rnb_gpio); +err1: + kfree(fun); + + return ret; +} + +static int __devexit fun_remove(struct of_device *ofdev) +{ + struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev); + + nand_release(&fun->mtd); + + if (fun->rnb_gpio >= 0) + gpio_free(fun->rnb_gpio); + + kfree(fun); + + return 0; +} + +static struct of_device_id of_fun_match[] = { + { .compatible = "fsl,upm-nand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_fun_match); + +static struct of_platform_driver of_fun_driver = { + .name = "fsl,upm-nand", + .match_table = of_fun_match, + .probe = fun_probe, + .remove = __devexit_p(fun_remove), +}; + +static int __init fun_module_init(void) +{ + return of_register_platform_driver(&of_fun_driver); +} +module_init(fun_module_init); + +static void __exit fun_module_exit(void) +{ + of_unregister_platform_driver(&of_fun_driver); +} +module_exit(fun_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); +MODULE_DESCRIPTION("Driver for NAND chips working through Freescale " + "LocalBus User-Programmable Machine"); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 7acb1a0e7409..ba1bdf787323 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2229,6 +2229,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, { struct nand_flash_dev *type = NULL; int i, dev_id, maf_idx; + int tmp_id, tmp_manf; /* Select the device */ chip->select_chip(mtd, 0); @@ -2240,6 +2241,26 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, *maf_id = chip->read_byte(mtd); dev_id = chip->read_byte(mtd); + /* Try again to make sure, as some systems the bus-hold or other + * interface concerns can cause random data which looks like a + * possibly credible NAND flash to appear. If the two results do + * not match, ignore the device completely. + */ + + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + + tmp_manf = chip->read_byte(mtd); + tmp_id = chip->read_byte(mtd); + + if (tmp_manf != *maf_id || tmp_id != dev_id) { + printk(KERN_INFO "%s: second ID read did not match " + "%02x,%02x against %02x,%02x\n", __func__, + *maf_id, dev_id, tmp_manf, tmp_id); + return ERR_PTR(-ENODEV); + } + /* Lookup the flash id */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (dev_id == nand_flash_ids[i].id) { diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 1c0e89f00e8d..955959eb02d4 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -317,3 +317,5 @@ module_exit(ndfc_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); MODULE_DESCRIPTION("Platform driver for NDFC"); +MODULE_ALIAS("platform:ndfc-chip"); +MODULE_ALIAS("platform:ndfc-nand"); diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index ec5ad28b237e..59e05a1c50cf 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -169,3 +169,4 @@ module_exit(orion_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Tzachi Perelstein"); MODULE_DESCRIPTION("NAND glue for Orion platforms"); +MODULE_ALIAS("platform:orion_nand"); diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index f6d5c2adc4fd..f674c5427b17 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -54,6 +54,7 @@ static int __init plat_nand_probe(struct platform_device *pdev) data->chip.priv = &data; data->mtd.priv = &data->chip; data->mtd.owner = THIS_MODULE; + data->mtd.name = pdev->dev.bus_id; data->chip.IO_ADDR_R = data->io_base; data->chip.IO_ADDR_W = data->io_base; @@ -150,3 +151,4 @@ module_exit(plat_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vitaly Wool"); MODULE_DESCRIPTION("Simple generic NAND driver"); +MODULE_ALIAS("platform:gen_nand"); diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c new file mode 100644 index 000000000000..fceb468ccdec --- /dev/null +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -0,0 +1,1249 @@ +/* + * drivers/mtd/nand/pxa3xx_nand.c + * + * Copyright © 2005 Intel Corporation + * Copyright © 2006 Marvell International 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 <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <asm/dma.h> + +#include <asm/arch/pxa-regs.h> +#include <asm/arch/pxa3xx_nand.h> + +#define CHIP_DELAY_TIMEOUT (2 * HZ/10) + +/* registers and bit definitions */ +#define NDCR (0x00) /* Control register */ +#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */ +#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */ +#define NDSR (0x14) /* Status Register */ +#define NDPCR (0x18) /* Page Count Register */ +#define NDBDR0 (0x1C) /* Bad Block Register 0 */ +#define NDBDR1 (0x20) /* Bad Block Register 1 */ +#define NDDB (0x40) /* Data Buffer */ +#define NDCB0 (0x48) /* Command Buffer0 */ +#define NDCB1 (0x4C) /* Command Buffer1 */ +#define NDCB2 (0x50) /* Command Buffer2 */ + +#define NDCR_SPARE_EN (0x1 << 31) +#define NDCR_ECC_EN (0x1 << 30) +#define NDCR_DMA_EN (0x1 << 29) +#define NDCR_ND_RUN (0x1 << 28) +#define NDCR_DWIDTH_C (0x1 << 27) +#define NDCR_DWIDTH_M (0x1 << 26) +#define NDCR_PAGE_SZ (0x1 << 24) +#define NDCR_NCSX (0x1 << 23) +#define NDCR_ND_MODE (0x3 << 21) +#define NDCR_NAND_MODE (0x0) +#define NDCR_CLR_PG_CNT (0x1 << 20) +#define NDCR_CLR_ECC (0x1 << 19) +#define NDCR_RD_ID_CNT_MASK (0x7 << 16) +#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK) + +#define NDCR_RA_START (0x1 << 15) +#define NDCR_PG_PER_BLK (0x1 << 14) +#define NDCR_ND_ARB_EN (0x1 << 12) + +#define NDSR_MASK (0xfff) +#define NDSR_RDY (0x1 << 11) +#define NDSR_CS0_PAGED (0x1 << 10) +#define NDSR_CS1_PAGED (0x1 << 9) +#define NDSR_CS0_CMDD (0x1 << 8) +#define NDSR_CS1_CMDD (0x1 << 7) +#define NDSR_CS0_BBD (0x1 << 6) +#define NDSR_CS1_BBD (0x1 << 5) +#define NDSR_DBERR (0x1 << 4) +#define NDSR_SBERR (0x1 << 3) +#define NDSR_WRDREQ (0x1 << 2) +#define NDSR_RDDREQ (0x1 << 1) +#define NDSR_WRCMDREQ (0x1) + +#define NDCB0_AUTO_RS (0x1 << 25) +#define NDCB0_CSEL (0x1 << 24) +#define NDCB0_CMD_TYPE_MASK (0x7 << 21) +#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK) +#define NDCB0_NC (0x1 << 20) +#define NDCB0_DBC (0x1 << 19) +#define NDCB0_ADDR_CYC_MASK (0x7 << 16) +#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK) +#define NDCB0_CMD2_MASK (0xff << 8) +#define NDCB0_CMD1_MASK (0xff) +#define NDCB0_ADDR_CYC_SHIFT (16) + +/* dma-able I/O address for the NAND data and commands */ +#define NDCB0_DMA_ADDR (0x43100048) +#define NDDB_DMA_ADDR (0x43100040) + +/* macros for registers read/write */ +#define nand_writel(info, off, val) \ + __raw_writel((val), (info)->mmio_base + (off)) + +#define nand_readl(info, off) \ + __raw_readl((info)->mmio_base + (off)) + +/* error code and state */ +enum { + ERR_NONE = 0, + ERR_DMABUSERR = -1, + ERR_SENDCMD = -2, + ERR_DBERR = -3, + ERR_BBERR = -4, +}; + +enum { + STATE_READY = 0, + STATE_CMD_HANDLE, + STATE_DMA_READING, + STATE_DMA_WRITING, + STATE_DMA_DONE, + STATE_PIO_READING, + STATE_PIO_WRITING, +}; + +struct pxa3xx_nand_timing { + unsigned int tCH; /* Enable signal hold time */ + unsigned int tCS; /* Enable signal setup time */ + unsigned int tWH; /* ND_nWE high duration */ + unsigned int tWP; /* ND_nWE pulse time */ + unsigned int tRH; /* ND_nRE high duration */ + unsigned int tRP; /* ND_nRE pulse width */ + unsigned int tR; /* ND_nWE high to ND_nRE low for read */ + unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */ + unsigned int tAR; /* ND_ALE low to ND_nRE low delay */ +}; + +struct pxa3xx_nand_cmdset { + uint16_t read1; + uint16_t read2; + uint16_t program; + uint16_t read_status; + uint16_t read_id; + uint16_t erase; + uint16_t reset; + uint16_t lock; + uint16_t unlock; + uint16_t lock_status; +}; + +struct pxa3xx_nand_flash { + struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ + struct pxa3xx_nand_cmdset *cmdset; + + uint32_t page_per_block;/* Pages per block (PG_PER_BLK) */ + uint32_t page_size; /* Page size in bytes (PAGE_SZ) */ + uint32_t flash_width; /* Width of Flash memory (DWIDTH_M) */ + uint32_t dfc_width; /* Width of flash controller(DWIDTH_C) */ + uint32_t num_blocks; /* Number of physical blocks in Flash */ + uint32_t chip_id; + + /* NOTE: these are automatically calculated, do not define */ + size_t oob_size; + size_t read_id_bytes; + + unsigned int col_addr_cycles; + unsigned int row_addr_cycles; +}; + +struct pxa3xx_nand_info { + struct nand_chip nand_chip; + + struct platform_device *pdev; + struct pxa3xx_nand_flash *flash_info; + + struct clk *clk; + void __iomem *mmio_base; + + unsigned int buf_start; + unsigned int buf_count; + + /* DMA information */ + int drcmr_dat; + int drcmr_cmd; + + unsigned char *data_buff; + dma_addr_t data_buff_phys; + size_t data_buff_size; + int data_dma_ch; + struct pxa_dma_desc *data_desc; + dma_addr_t data_desc_addr; + + uint32_t reg_ndcr; + + /* saved column/page_addr during CMD_SEQIN */ + int seqin_column; + int seqin_page_addr; + + /* relate to the command */ + unsigned int state; + + int use_ecc; /* use HW ECC ? */ + int use_dma; /* use DMA ? */ + + size_t data_size; /* data size in FIFO */ + int retcode; + struct completion cmd_complete; + + /* generated NDCBx register values */ + uint32_t ndcb0; + uint32_t ndcb1; + uint32_t ndcb2; +}; + +static int use_dma = 1; +module_param(use_dma, bool, 0444); +MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW"); + +static struct pxa3xx_nand_cmdset smallpage_cmdset = { + .read1 = 0x0000, + .read2 = 0x0050, + .program = 0x1080, + .read_status = 0x0070, + .read_id = 0x0090, + .erase = 0xD060, + .reset = 0x00FF, + .lock = 0x002A, + .unlock = 0x2423, + .lock_status = 0x007A, +}; + +static struct pxa3xx_nand_cmdset largepage_cmdset = { + .read1 = 0x3000, + .read2 = 0x0050, + .program = 0x1080, + .read_status = 0x0070, + .read_id = 0x0090, + .erase = 0xD060, + .reset = 0x00FF, + .lock = 0x002A, + .unlock = 0x2423, + .lock_status = 0x007A, +}; + +static struct pxa3xx_nand_timing samsung512MbX16_timing = { + .tCH = 10, + .tCS = 0, + .tWH = 20, + .tWP = 40, + .tRH = 30, + .tRP = 40, + .tR = 11123, + .tWHR = 110, + .tAR = 10, +}; + +static struct pxa3xx_nand_flash samsung512MbX16 = { + .timing = &samsung512MbX16_timing, + .cmdset = &smallpage_cmdset, + .page_per_block = 32, + .page_size = 512, + .flash_width = 16, + .dfc_width = 16, + .num_blocks = 4096, + .chip_id = 0x46ec, +}; + +static struct pxa3xx_nand_timing micron_timing = { + .tCH = 10, + .tCS = 25, + .tWH = 15, + .tWP = 25, + .tRH = 15, + .tRP = 25, + .tR = 25000, + .tWHR = 60, + .tAR = 10, +}; + +static struct pxa3xx_nand_flash micron1GbX8 = { + .timing = µn_timing, + .cmdset = &largepage_cmdset, + .page_per_block = 64, + .page_size = 2048, + .flash_width = 8, + .dfc_width = 8, + .num_blocks = 1024, + .chip_id = 0xa12c, +}; + +static struct pxa3xx_nand_flash micron1GbX16 = { + .timing = µn_timing, + .cmdset = &largepage_cmdset, + .page_per_block = 64, + .page_size = 2048, + .flash_width = 16, + .dfc_width = 16, + .num_blocks = 1024, + .chip_id = 0xb12c, +}; + +static struct pxa3xx_nand_flash *builtin_flash_types[] = { + &samsung512MbX16, + µn1GbX8, + µn1GbX16, +}; + +#define NDTR0_tCH(c) (min((c), 7) << 19) +#define NDTR0_tCS(c) (min((c), 7) << 16) +#define NDTR0_tWH(c) (min((c), 7) << 11) +#define NDTR0_tWP(c) (min((c), 7) << 8) +#define NDTR0_tRH(c) (min((c), 7) << 3) +#define NDTR0_tRP(c) (min((c), 7) << 0) + +#define NDTR1_tR(c) (min((c), 65535) << 16) +#define NDTR1_tWHR(c) (min((c), 15) << 4) +#define NDTR1_tAR(c) (min((c), 15) << 0) + +/* convert nano-seconds to nand flash controller clock cycles */ +#define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) + 1) + +static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, + struct pxa3xx_nand_timing *t) +{ + unsigned long nand_clk = clk_get_rate(info->clk); + uint32_t ndtr0, ndtr1; + + ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) | + NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) | + NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) | + NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) | + NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) | + NDTR0_tRP(ns2cycle(t->tRP, nand_clk)); + + ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) | + NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) | + NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); + + nand_writel(info, NDTR0CS0, ndtr0); + nand_writel(info, NDTR1CS0, ndtr1); +} + +#define WAIT_EVENT_TIMEOUT 10 + +static int wait_for_event(struct pxa3xx_nand_info *info, uint32_t event) +{ + int timeout = WAIT_EVENT_TIMEOUT; + uint32_t ndsr; + + while (timeout--) { + ndsr = nand_readl(info, NDSR) & NDSR_MASK; + if (ndsr & event) { + nand_writel(info, NDSR, ndsr); + return 0; + } + udelay(10); + } + + return -ETIMEDOUT; +} + +static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, + uint16_t cmd, int column, int page_addr) +{ + struct pxa3xx_nand_flash *f = info->flash_info; + struct pxa3xx_nand_cmdset *cmdset = f->cmdset; + + /* calculate data size */ + switch (f->page_size) { + case 2048: + info->data_size = (info->use_ecc) ? 2088 : 2112; + break; + case 512: + info->data_size = (info->use_ecc) ? 520 : 528; + break; + default: + return -EINVAL; + } + + /* generate values for NDCBx registers */ + info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); + info->ndcb1 = 0; + info->ndcb2 = 0; + info->ndcb0 |= NDCB0_ADDR_CYC(f->row_addr_cycles + f->col_addr_cycles); + + if (f->col_addr_cycles == 2) { + /* large block, 2 cycles for column address + * row address starts from 3rd cycle + */ + info->ndcb1 |= (page_addr << 16) | (column & 0xffff); + if (f->row_addr_cycles == 3) + info->ndcb2 = (page_addr >> 16) & 0xff; + } else + /* small block, 1 cycles for column address + * row address starts from 2nd cycle + */ + info->ndcb1 = (page_addr << 8) | (column & 0xff); + + if (cmd == cmdset->program) + info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS; + + return 0; +} + +static int prepare_erase_cmd(struct pxa3xx_nand_info *info, + uint16_t cmd, int page_addr) +{ + info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); + info->ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS | NDCB0_ADDR_CYC(3); + info->ndcb1 = page_addr; + info->ndcb2 = 0; + return 0; +} + +static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) +{ + struct pxa3xx_nand_cmdset *cmdset = info->flash_info->cmdset; + + info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); + info->ndcb1 = 0; + info->ndcb2 = 0; + + if (cmd == cmdset->read_id) { + info->ndcb0 |= NDCB0_CMD_TYPE(3); + info->data_size = 8; + } else if (cmd == cmdset->read_status) { + info->ndcb0 |= NDCB0_CMD_TYPE(4); + info->data_size = 8; + } else if (cmd == cmdset->reset || cmd == cmdset->lock || + cmd == cmdset->unlock) { + info->ndcb0 |= NDCB0_CMD_TYPE(5); + } else + return -EINVAL; + + return 0; +} + +static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) +{ + uint32_t ndcr; + + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr & ~int_mask); +} + +static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) +{ + uint32_t ndcr; + + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr | int_mask); +} + +/* NOTE: it is a must to set ND_RUN firstly, then write command buffer + * otherwise, it does not work + */ +static int write_cmd(struct pxa3xx_nand_info *info) +{ + uint32_t ndcr; + + /* clear status bits and run */ + nand_writel(info, NDSR, NDSR_MASK); + + ndcr = info->reg_ndcr; + + ndcr |= info->use_ecc ? NDCR_ECC_EN : 0; + ndcr |= info->use_dma ? NDCR_DMA_EN : 0; + ndcr |= NDCR_ND_RUN; + + nand_writel(info, NDCR, ndcr); + + if (wait_for_event(info, NDSR_WRCMDREQ)) { + printk(KERN_ERR "timed out writing command\n"); + return -ETIMEDOUT; + } + + nand_writel(info, NDCB0, info->ndcb0); + nand_writel(info, NDCB0, info->ndcb1); + nand_writel(info, NDCB0, info->ndcb2); + return 0; +} + +static int handle_data_pio(struct pxa3xx_nand_info *info) +{ + int ret, timeout = CHIP_DELAY_TIMEOUT; + + switch (info->state) { + case STATE_PIO_WRITING: + __raw_writesl(info->mmio_base + NDDB, info->data_buff, + info->data_size << 2); + + enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); + + ret = wait_for_completion_timeout(&info->cmd_complete, timeout); + if (!ret) { + printk(KERN_ERR "program command time out\n"); + return -1; + } + break; + case STATE_PIO_READING: + __raw_readsl(info->mmio_base + NDDB, info->data_buff, + info->data_size << 2); + break; + default: + printk(KERN_ERR "%s: invalid state %d\n", __func__, + info->state); + return -EINVAL; + } + + info->state = STATE_READY; + return 0; +} + +static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out) +{ + struct pxa_dma_desc *desc = info->data_desc; + int dma_len = ALIGN(info->data_size, 32); + + desc->ddadr = DDADR_STOP; + desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len; + + if (dir_out) { + desc->dsadr = info->data_buff_phys; + desc->dtadr = NDDB_DMA_ADDR; + desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG; + } else { + desc->dtadr = info->data_buff_phys; + desc->dsadr = NDDB_DMA_ADDR; + desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC; + } + + DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch; + DDADR(info->data_dma_ch) = info->data_desc_addr; + DCSR(info->data_dma_ch) |= DCSR_RUN; +} + +static void pxa3xx_nand_data_dma_irq(int channel, void *data) +{ + struct pxa3xx_nand_info *info = data; + uint32_t dcsr; + + dcsr = DCSR(channel); + DCSR(channel) = dcsr; + + if (dcsr & DCSR_BUSERR) { + info->retcode = ERR_DMABUSERR; + complete(&info->cmd_complete); + } + + if (info->state == STATE_DMA_WRITING) { + info->state = STATE_DMA_DONE; + enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); + } else { + info->state = STATE_READY; + complete(&info->cmd_complete); + } +} + +static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) +{ + struct pxa3xx_nand_info *info = devid; + unsigned int status; + + status = nand_readl(info, NDSR); + + if (status & (NDSR_RDDREQ | NDSR_DBERR)) { + if (status & NDSR_DBERR) + info->retcode = ERR_DBERR; + + disable_int(info, NDSR_RDDREQ | NDSR_DBERR); + + if (info->use_dma) { + info->state = STATE_DMA_READING; + start_data_dma(info, 0); + } else { + info->state = STATE_PIO_READING; + complete(&info->cmd_complete); + } + } else if (status & NDSR_WRDREQ) { + disable_int(info, NDSR_WRDREQ); + if (info->use_dma) { + info->state = STATE_DMA_WRITING; + start_data_dma(info, 1); + } else { + info->state = STATE_PIO_WRITING; + complete(&info->cmd_complete); + } + } else if (status & (NDSR_CS0_BBD | NDSR_CS0_CMDD)) { + if (status & NDSR_CS0_BBD) + info->retcode = ERR_BBERR; + + disable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); + info->state = STATE_READY; + complete(&info->cmd_complete); + } + nand_writel(info, NDSR, status); + return IRQ_HANDLED; +} + +static int pxa3xx_nand_do_cmd(struct pxa3xx_nand_info *info, uint32_t event) +{ + uint32_t ndcr; + int ret, timeout = CHIP_DELAY_TIMEOUT; + + if (write_cmd(info)) { + info->retcode = ERR_SENDCMD; + goto fail_stop; + } + + info->state = STATE_CMD_HANDLE; + + enable_int(info, event); + + ret = wait_for_completion_timeout(&info->cmd_complete, timeout); + if (!ret) { + printk(KERN_ERR "command execution timed out\n"); + info->retcode = ERR_SENDCMD; + goto fail_stop; + } + + if (info->use_dma == 0 && info->data_size > 0) + if (handle_data_pio(info)) + goto fail_stop; + + return 0; + +fail_stop: + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); + udelay(10); + return -ETIMEDOUT; +} + +static int pxa3xx_nand_dev_ready(struct mtd_info *mtd) +{ + struct pxa3xx_nand_info *info = mtd->priv; + return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0; +} + +static inline int is_buf_blank(uint8_t *buf, size_t len) +{ + for (; len > 0; len--) + if (*buf++ != 0xff) + return 0; + return 1; +} + +static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand_flash *flash_info = info->flash_info; + struct pxa3xx_nand_cmdset *cmdset = flash_info->cmdset; + int ret; + + info->use_dma = (use_dma) ? 1 : 0; + info->use_ecc = 0; + info->data_size = 0; + info->state = STATE_READY; + + init_completion(&info->cmd_complete); + + switch (command) { + case NAND_CMD_READOOB: + /* disable HW ECC to get all the OOB data */ + info->buf_count = mtd->writesize + mtd->oobsize; + info->buf_start = mtd->writesize + column; + + if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) + break; + + pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR); + + /* We only are OOB, so if the data has error, does not matter */ + if (info->retcode == ERR_DBERR) + info->retcode = ERR_NONE; + break; + + case NAND_CMD_READ0: + info->use_ecc = 1; + info->retcode = ERR_NONE; + info->buf_start = column; + info->buf_count = mtd->writesize + mtd->oobsize; + memset(info->data_buff, 0xFF, info->buf_count); + + if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) + break; + + pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR); + + if (info->retcode == ERR_DBERR) { + /* for blank page (all 0xff), HW will calculate its ECC as + * 0, which is different from the ECC information within + * OOB, ignore such double bit errors + */ + if (is_buf_blank(info->data_buff, mtd->writesize)) + info->retcode = ERR_NONE; + } + break; + case NAND_CMD_SEQIN: + info->buf_start = column; + info->buf_count = mtd->writesize + mtd->oobsize; + memset(info->data_buff, 0xff, info->buf_count); + + /* save column/page_addr for next CMD_PAGEPROG */ + info->seqin_column = column; + info->seqin_page_addr = page_addr; + break; + case NAND_CMD_PAGEPROG: + info->use_ecc = (info->seqin_column >= mtd->writesize) ? 0 : 1; + + if (prepare_read_prog_cmd(info, cmdset->program, + info->seqin_column, info->seqin_page_addr)) + break; + + pxa3xx_nand_do_cmd(info, NDSR_WRDREQ); + break; + case NAND_CMD_ERASE1: + if (prepare_erase_cmd(info, cmdset->erase, page_addr)) + break; + + pxa3xx_nand_do_cmd(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); + break; + case NAND_CMD_ERASE2: + break; + case NAND_CMD_READID: + case NAND_CMD_STATUS: + info->use_dma = 0; /* force PIO read */ + info->buf_start = 0; + info->buf_count = (command == NAND_CMD_READID) ? + flash_info->read_id_bytes : 1; + + if (prepare_other_cmd(info, (command == NAND_CMD_READID) ? + cmdset->read_id : cmdset->read_status)) + break; + + pxa3xx_nand_do_cmd(info, NDSR_RDDREQ); + break; + case NAND_CMD_RESET: + if (prepare_other_cmd(info, cmdset->reset)) + break; + + ret = pxa3xx_nand_do_cmd(info, NDSR_CS0_CMDD); + if (ret == 0) { + int timeout = 2; + uint32_t ndcr; + + while (timeout--) { + if (nand_readl(info, NDSR) & NDSR_RDY) + break; + msleep(10); + } + + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); + } + break; + default: + printk(KERN_ERR "non-supported command.\n"); + break; + } + + if (info->retcode == ERR_DBERR) { + printk(KERN_ERR "double bit error @ page %08x\n", page_addr); + info->retcode = ERR_NONE; + } +} + +static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd) +{ + struct pxa3xx_nand_info *info = mtd->priv; + char retval = 0xFF; + + if (info->buf_start < info->buf_count) + /* Has just send a new command? */ + retval = info->data_buff[info->buf_start++]; + + return retval; +} + +static u16 pxa3xx_nand_read_word(struct mtd_info *mtd) +{ + struct pxa3xx_nand_info *info = mtd->priv; + u16 retval = 0xFFFF; + + if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) { + retval = *((u16 *)(info->data_buff+info->buf_start)); + info->buf_start += 2; + } + return retval; +} + +static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct pxa3xx_nand_info *info = mtd->priv; + int real_len = min_t(size_t, len, info->buf_count - info->buf_start); + + memcpy(buf, info->data_buff + info->buf_start, real_len); + info->buf_start += real_len; +} + +static void pxa3xx_nand_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + struct pxa3xx_nand_info *info = mtd->priv; + int real_len = min_t(size_t, len, info->buf_count - info->buf_start); + + memcpy(info->data_buff + info->buf_start, buf, real_len); + info->buf_start += real_len; +} + +static int pxa3xx_nand_verify_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + return 0; +} + +static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip) +{ + return; +} + +static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) +{ + struct pxa3xx_nand_info *info = mtd->priv; + + /* pxa3xx_nand_send_command has waited for command complete */ + if (this->state == FL_WRITING || this->state == FL_ERASING) { + if (info->retcode == ERR_NONE) + return 0; + else { + /* + * any error make it return 0x01 which will tell + * the caller the erase and write fail + */ + return 0x01; + } + } + + return 0; +} + +static void pxa3xx_nand_ecc_hwctl(struct mtd_info *mtd, int mode) +{ + return; +} + +static int pxa3xx_nand_ecc_calculate(struct mtd_info *mtd, + const uint8_t *dat, uint8_t *ecc_code) +{ + return 0; +} + +static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd, + uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc) +{ + struct pxa3xx_nand_info *info = mtd->priv; + /* + * Any error include ERR_SEND_CMD, ERR_DBERR, ERR_BUSERR, we + * consider it as a ecc error which will tell the caller the + * read fail We have distinguish all the errors, but the + * nand_read_ecc only check this function return value + */ + if (info->retcode != ERR_NONE) + return -1; + + return 0; +} + +static int __readid(struct pxa3xx_nand_info *info, uint32_t *id) +{ + struct pxa3xx_nand_flash *f = info->flash_info; + struct pxa3xx_nand_cmdset *cmdset = f->cmdset; + uint32_t ndcr; + uint8_t id_buff[8]; + + if (prepare_other_cmd(info, cmdset->read_id)) { + printk(KERN_ERR "failed to prepare command\n"); + return -EINVAL; + } + + /* Send command */ + if (write_cmd(info)) + goto fail_timeout; + + /* Wait for CMDDM(command done successfully) */ + if (wait_for_event(info, NDSR_RDDREQ)) + goto fail_timeout; + + __raw_readsl(info->mmio_base + NDDB, id_buff, 2); + *id = id_buff[0] | (id_buff[1] << 8); + return 0; + +fail_timeout: + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); + udelay(10); + return -ETIMEDOUT; +} + +static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, + struct pxa3xx_nand_flash *f) +{ + struct platform_device *pdev = info->pdev; + struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; + uint32_t ndcr = 0x00000FFF; /* disable all interrupts */ + + if (f->page_size != 2048 && f->page_size != 512) + return -EINVAL; + + if (f->flash_width != 16 && f->flash_width != 8) + return -EINVAL; + + /* calculate flash information */ + f->oob_size = (f->page_size == 2048) ? 64 : 16; + f->read_id_bytes = (f->page_size == 2048) ? 4 : 2; + + /* calculate addressing information */ + f->col_addr_cycles = (f->page_size == 2048) ? 2 : 1; + + if (f->num_blocks * f->page_per_block > 65536) + f->row_addr_cycles = 3; + else + f->row_addr_cycles = 2; + + ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; + ndcr |= (f->col_addr_cycles == 2) ? NDCR_RA_START : 0; + ndcr |= (f->page_per_block == 64) ? NDCR_PG_PER_BLK : 0; + ndcr |= (f->page_size == 2048) ? NDCR_PAGE_SZ : 0; + ndcr |= (f->flash_width == 16) ? NDCR_DWIDTH_M : 0; + ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0; + + ndcr |= NDCR_RD_ID_CNT(f->read_id_bytes); + ndcr |= NDCR_SPARE_EN; /* enable spare by default */ + + info->reg_ndcr = ndcr; + + pxa3xx_nand_set_timing(info, f->timing); + info->flash_info = f; + return 0; +} + +static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_flash *f; + uint32_t id; + int i; + + for (i = 0; i < ARRAY_SIZE(builtin_flash_types); i++) { + + f = builtin_flash_types[i]; + + if (pxa3xx_nand_config_flash(info, f)) + continue; + + if (__readid(info, &id)) + continue; + + if (id == f->chip_id) + return 0; + } + + return -ENODEV; +} + +/* the maximum possible buffer size for large page with OOB data + * is: 2048 + 64 = 2112 bytes, allocate a page here for both the + * data buffer and the DMA descriptor + */ +#define MAX_BUFF_SIZE PAGE_SIZE + +static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) +{ + struct platform_device *pdev = info->pdev; + int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc); + + if (use_dma == 0) { + info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL); + if (info->data_buff == NULL) + return -ENOMEM; + return 0; + } + + info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE, + &info->data_buff_phys, GFP_KERNEL); + if (info->data_buff == NULL) { + dev_err(&pdev->dev, "failed to allocate dma buffer\n"); + return -ENOMEM; + } + + info->data_buff_size = MAX_BUFF_SIZE; + info->data_desc = (void *)info->data_buff + data_desc_offset; + info->data_desc_addr = info->data_buff_phys + data_desc_offset; + + info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW, + pxa3xx_nand_data_dma_irq, info); + if (info->data_dma_ch < 0) { + dev_err(&pdev->dev, "failed to request data dma\n"); + dma_free_coherent(&pdev->dev, info->data_buff_size, + info->data_buff, info->data_buff_phys); + return info->data_dma_ch; + } + + return 0; +} + +static struct nand_ecclayout hw_smallpage_ecclayout = { + .eccbytes = 6, + .eccpos = {8, 9, 10, 11, 12, 13 }, + .oobfree = { {2, 6} } +}; + +static struct nand_ecclayout hw_largepage_ecclayout = { + .eccbytes = 24, + .eccpos = { + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { {2, 38} } +}; + +static void pxa3xx_nand_init_mtd(struct mtd_info *mtd, + struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_flash *f = info->flash_info; + struct nand_chip *this = &info->nand_chip; + + this->options = (f->flash_width == 16) ? NAND_BUSWIDTH_16: 0; + + this->waitfunc = pxa3xx_nand_waitfunc; + this->select_chip = pxa3xx_nand_select_chip; + this->dev_ready = pxa3xx_nand_dev_ready; + this->cmdfunc = pxa3xx_nand_cmdfunc; + this->read_word = pxa3xx_nand_read_word; + this->read_byte = pxa3xx_nand_read_byte; + this->read_buf = pxa3xx_nand_read_buf; + this->write_buf = pxa3xx_nand_write_buf; + this->verify_buf = pxa3xx_nand_verify_buf; + + this->ecc.mode = NAND_ECC_HW; + this->ecc.hwctl = pxa3xx_nand_ecc_hwctl; + this->ecc.calculate = pxa3xx_nand_ecc_calculate; + this->ecc.correct = pxa3xx_nand_ecc_correct; + this->ecc.size = f->page_size; + + if (f->page_size == 2048) + this->ecc.layout = &hw_largepage_ecclayout; + else + this->ecc.layout = &hw_smallpage_ecclayout; + + this->chip_delay = 25; +} + +static int pxa3xx_nand_probe(struct platform_device *pdev) +{ + struct pxa3xx_nand_platform_data *pdata; + struct pxa3xx_nand_info *info; + struct nand_chip *this; + struct mtd_info *mtd; + struct resource *r; + int ret = 0, irq; + + pdata = pdev->dev.platform_data; + + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -ENODEV; + } + + mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info), + GFP_KERNEL); + if (!mtd) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + info = (struct pxa3xx_nand_info *)(&mtd[1]); + info->pdev = pdev; + + this = &info->nand_chip; + mtd->priv = info; + + info->clk = clk_get(&pdev->dev, "NANDCLK"); + if (IS_ERR(info->clk)) { + dev_err(&pdev->dev, "failed to get nand clock\n"); + ret = PTR_ERR(info->clk); + goto fail_free_mtd; + } + clk_enable(info->clk); + + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (r == NULL) { + dev_err(&pdev->dev, "no resource defined for data DMA\n"); + ret = -ENXIO; + goto fail_put_clk; + } + info->drcmr_dat = r->start; + + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (r == NULL) { + dev_err(&pdev->dev, "no resource defined for command DMA\n"); + ret = -ENXIO; + goto fail_put_clk; + } + info->drcmr_cmd = r->start; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no IRQ resource defined\n"); + ret = -ENXIO; + goto fail_put_clk; + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(&pdev->dev, "no IO memory resource defined\n"); + ret = -ENODEV; + goto fail_put_clk; + } + + r = request_mem_region(r->start, r->end - r->start + 1, pdev->name); + if (r == NULL) { + dev_err(&pdev->dev, "failed to request memory resource\n"); + ret = -EBUSY; + goto fail_put_clk; + } + + info->mmio_base = ioremap(r->start, r->end - r->start + 1); + if (info->mmio_base == NULL) { + dev_err(&pdev->dev, "ioremap() failed\n"); + ret = -ENODEV; + goto fail_free_res; + } + + ret = pxa3xx_nand_init_buff(info); + if (ret) + goto fail_free_io; + + ret = request_irq(IRQ_NAND, pxa3xx_nand_irq, IRQF_DISABLED, + pdev->name, info); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto fail_free_buf; + } + + ret = pxa3xx_nand_detect_flash(info); + if (ret) { + dev_err(&pdev->dev, "failed to detect flash\n"); + ret = -ENODEV; + goto fail_free_irq; + } + + pxa3xx_nand_init_mtd(mtd, info); + + platform_set_drvdata(pdev, mtd); + + if (nand_scan(mtd, 1)) { + dev_err(&pdev->dev, "failed to scan nand\n"); + ret = -ENXIO; + goto fail_free_irq; + } + + return add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); + +fail_free_irq: + free_irq(IRQ_NAND, info); +fail_free_buf: + if (use_dma) { + pxa_free_dma(info->data_dma_ch); + dma_free_coherent(&pdev->dev, info->data_buff_size, + info->data_buff, info->data_buff_phys); + } else + kfree(info->data_buff); +fail_free_io: + iounmap(info->mmio_base); +fail_free_res: + release_mem_region(r->start, r->end - r->start + 1); +fail_put_clk: + clk_disable(info->clk); + clk_put(info->clk); +fail_free_mtd: + kfree(mtd); + return ret; +} + +static int pxa3xx_nand_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct pxa3xx_nand_info *info = mtd->priv; + + platform_set_drvdata(pdev, NULL); + + del_mtd_device(mtd); + del_mtd_partitions(mtd); + free_irq(IRQ_NAND, info); + if (use_dma) { + pxa_free_dma(info->data_dma_ch); + dma_free_writecombine(&pdev->dev, info->data_buff_size, + info->data_buff, info->data_buff_phys); + } else + kfree(info->data_buff); + kfree(mtd); + return 0; +} + +#ifdef CONFIG_PM +static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev); + struct pxa3xx_nand_info *info = mtd->priv; + + if (info->state != STATE_READY) { + dev_err(&pdev->dev, "driver busy, state = %d\n", info->state); + return -EAGAIN; + } + + return 0; +} + +static int pxa3xx_nand_resume(struct platform_device *pdev) +{ + struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev); + struct pxa3xx_nand_info *info = mtd->priv; + + clk_enable(info->clk); + + return pxa3xx_nand_config_flash(info); +} +#else +#define pxa3xx_nand_suspend NULL +#define pxa3xx_nand_resume NULL +#endif + +static struct platform_driver pxa3xx_nand_driver = { + .driver = { + .name = "pxa3xx-nand", + }, + .probe = pxa3xx_nand_probe, + .remove = pxa3xx_nand_remove, + .suspend = pxa3xx_nand_suspend, + .resume = pxa3xx_nand_resume, +}; + +static int __init pxa3xx_nand_init(void) +{ + return platform_driver_register(&pxa3xx_nand_driver); +} +module_init(pxa3xx_nand_init); + +static void __exit pxa3xx_nand_exit(void) +{ + platform_driver_unregister(&pxa3xx_nand_driver); +} +module_exit(pxa3xx_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PXA3xx NAND controller driver"); diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index 0f6ac250f434..26f88215bc47 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -478,6 +478,7 @@ static int __init rtc_from4_init(void) struct nand_chip *this; unsigned short bcr1, bcr2, wcr2; int i; + int ret; /* Allocate memory for MTD device structure and private data */ rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); @@ -537,6 +538,22 @@ static int __init rtc_from4_init(void) this->ecc.hwctl = rtc_from4_enable_hwecc; this->ecc.calculate = rtc_from4_calculate_ecc; this->ecc.correct = rtc_from4_correct_data; + + /* We could create the decoder on demand, if memory is a concern. + * This way we have it handy, if an error happens + * + * Symbolsize is 10 (bits) + * Primitve polynomial is x^10+x^3+1 + * first consecutive root is 0 + * primitve element to generate roots = 1 + * generator polinomial degree = 6 + */ + rs_decoder = init_rs(10, 0x409, 0, 1, 6); + if (!rs_decoder) { + printk(KERN_ERR "Could not create a RS decoder\n"); + ret = -ENOMEM; + goto err_1; + } #else printk(KERN_INFO "rtc_from4_init: using software ECC detection.\n"); @@ -549,8 +566,8 @@ static int __init rtc_from4_init(void) /* Scan to find existence of the device */ if (nand_scan(rtc_from4_mtd, RTC_FROM4_MAX_CHIPS)) { - kfree(rtc_from4_mtd); - return -ENXIO; + ret = -ENXIO; + goto err_2; } /* Perform 'device recovery' for each chip in case there was a power loss. */ @@ -566,28 +583,19 @@ static int __init rtc_from4_init(void) #endif /* Register the partitions */ - add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS); + ret = add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS); + if (ret) + goto err_3; -#ifdef RTC_FROM4_HWECC - /* We could create the decoder on demand, if memory is a concern. - * This way we have it handy, if an error happens - * - * Symbolsize is 10 (bits) - * Primitve polynomial is x^10+x^3+1 - * first consecutive root is 0 - * primitve element to generate roots = 1 - * generator polinomial degree = 6 - */ - rs_decoder = init_rs(10, 0x409, 0, 1, 6); - if (!rs_decoder) { - printk(KERN_ERR "Could not create a RS decoder\n"); - nand_release(rtc_from4_mtd); - kfree(rtc_from4_mtd); - return -ENOMEM; - } -#endif /* Return happy */ return 0; +err_3: + nand_release(rtc_from4_mtd); +err_2: + free_rs(rs_decoder); +err_1: + kfree(rtc_from4_mtd); + return ret; } module_init(rtc_from4_init); diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 9260ad947524..b34a460ab679 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -119,8 +119,7 @@ struct s3c2410_nand_info { void __iomem *sel_reg; int sel_bit; int mtd_count; - - unsigned long save_nfconf; + unsigned long save_sel; enum s3c_cpu_type cpu_type; }; @@ -358,6 +357,14 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, if (diff0 == 0 && diff1 == 0 && diff2 == 0) return 0; /* ECC is ok */ + /* sometimes people do not think about using the ECC, so check + * to see if we have an 0xff,0xff,0xff read ECC and then ignore + * the error, on the assumption that this is an un-eccd page. + */ + if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff + && info->platform->ignore_unset_ecc) + return 0; + /* Can we correct this ECC (ie, one row and column change). * Note, this is similar to the 256 error code on smartmedia */ @@ -473,7 +480,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u ecc_code[1] = ecc >> 8; ecc_code[2] = ecc >> 16; - pr_debug("%s: returning ecc %06lx\n", __func__, ecc); + pr_debug("%s: returning ecc %06lx\n", __func__, ecc & 0xffffff); return 0; } @@ -644,9 +651,6 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->ecc.calculate = s3c2410_nand_calculate_ecc; chip->ecc.correct = s3c2410_nand_correct_data; chip->ecc.mode = NAND_ECC_HW; - chip->ecc.size = 512; - chip->ecc.bytes = 3; - chip->ecc.layout = &nand_hw_eccoob; switch (info->cpu_type) { case TYPE_S3C2410: @@ -668,6 +672,40 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, } else { chip->ecc.mode = NAND_ECC_SOFT; } + + if (set->ecc_layout != NULL) + chip->ecc.layout = set->ecc_layout; + + if (set->disable_ecc) + chip->ecc.mode = NAND_ECC_NONE; +} + +/* s3c2410_nand_update_chip + * + * post-probe chip update, to change any items, such as the + * layout for large page nand + */ + +static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, + struct s3c2410_nand_mtd *nmtd) +{ + struct nand_chip *chip = &nmtd->chip; + + printk("%s: chip %p: %d\n", __func__, chip, chip->page_shift); + + if (hardware_ecc) { + /* change the behaviour depending on wether we are using + * the large or small page nand device */ + + if (chip->page_shift > 10) { + chip->ecc.size = 256; + chip->ecc.bytes = 3; + } else { + chip->ecc.size = 512; + chip->ecc.bytes = 3; + chip->ecc.layout = &nand_hw_eccoob; + } + } } /* s3c2410_nand_probe @@ -776,9 +814,12 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, s3c2410_nand_init_chip(info, nmtd, sets); - nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1); + nmtd->scan_res = nand_scan_ident(&nmtd->mtd, + (sets) ? sets->nr_chips : 1); if (nmtd->scan_res == 0) { + s3c2410_nand_update_chip(info, nmtd); + nand_scan_tail(&nmtd->mtd); s3c2410_nand_add_partition(info, nmtd, sets); } @@ -810,15 +851,14 @@ static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) struct s3c2410_nand_info *info = platform_get_drvdata(dev); if (info) { - info->save_nfconf = readl(info->regs + S3C2410_NFCONF); + info->save_sel = readl(info->sel_reg); /* For the moment, we must ensure nFCE is high during * the time we are suspended. This really should be * handled by suspending the MTDs we are using, but * that is currently not the case. */ - writel(info->save_nfconf | info->sel_bit, - info->regs + S3C2410_NFCONF); + writel(info->save_sel | info->sel_bit, info->sel_reg); if (!allow_clk_stop(info)) clk_disable(info->clk); @@ -830,7 +870,7 @@ static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) static int s3c24xx_nand_resume(struct platform_device *dev) { struct s3c2410_nand_info *info = platform_get_drvdata(dev); - unsigned long nfconf; + unsigned long sel; if (info) { clk_enable(info->clk); @@ -838,10 +878,10 @@ static int s3c24xx_nand_resume(struct platform_device *dev) /* Restore the state of the nFCE line. */ - nfconf = readl(info->regs + S3C2410_NFCONF); - nfconf &= ~info->sel_bit; - nfconf |= info->save_nfconf & info->sel_bit; - writel(nfconf, info->regs + S3C2410_NFCONF); + sel = readl(info->sel_reg); + sel &= ~info->sel_bit; + sel |= info->save_sel & info->sel_bit; + writel(sel, info->sel_reg); if (allow_clk_stop(info)) clk_disable(info->clk); @@ -927,3 +967,6 @@ module_exit(s3c2410_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); +MODULE_ALIAS("platform:s3c2410-nand"); +MODULE_ALIAS("platform:s3c2412-nand"); +MODULE_ALIAS("platform:s3c2440-nand"); diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 0513cbc8834d..345e6eff89ce 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -33,11 +33,6 @@ char nftlmountrev[]="$Revision: 1.41 $"; -extern int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, - size_t *retlen, uint8_t *buf); -extern int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, - size_t *retlen, uint8_t *buf); - /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the * various device information of the NFTL partition and Bad Unit Table. Update * the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[] diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index f86e06934cd8..4f80c2fd89af 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c @@ -72,3 +72,5 @@ int __devinit of_mtd_parse_partitions(struct device *dev, return nr_parts; } EXPORT_SYMBOL(of_mtd_parse_partitions); + +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 8d7d21be1541..5d7965f7e9ce 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -329,6 +329,21 @@ static int onenand_wait(struct mtd_info *mtd, int state) printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl); if (ctrl & ONENAND_CTRL_LOCK) printk(KERN_ERR "onenand_wait: it's locked error.\n"); + if (state == FL_READING) { + /* + * A power loss while writing can result in a page + * becoming unreadable. When the device is mounted + * again, reading that page gives controller errors. + * Upper level software like JFFS2 treat -EIO as fatal, + * refusing to mount at all. That means it is necessary + * to treat the error as an ECC error to allow recovery. + * Note that typically in this case, the eraseblock can + * still be erased and rewritten i.e. it has not become + * a bad block. + */ + mtd->ecc_stats.failed++; + return -EBADMSG; + } return -EIO; } @@ -1336,7 +1351,7 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len, } /* Reject writes, which are not page aligned */ - if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { + if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { printk(KERN_ERR "onenand_panic_write: Attempt to write not page aligned data\n"); return -EINVAL; } @@ -1466,7 +1481,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, } /* Reject writes, which are not page aligned */ - if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { + if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n"); return -EINVAL; } @@ -2052,7 +2067,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) * * Check lock status */ -static void onenand_check_lock_status(struct onenand_chip *this) +static int onenand_check_lock_status(struct onenand_chip *this) { unsigned int value, block, status; unsigned int end; @@ -2070,9 +2085,13 @@ static void onenand_check_lock_status(struct onenand_chip *this) /* Check lock status */ status = this->read_word(this->base + ONENAND_REG_WP_STATUS); - if (!(status & ONENAND_WP_US)) + if (!(status & ONENAND_WP_US)) { printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); + return 0; + } } + + return 1; } /** @@ -2081,9 +2100,11 @@ static void onenand_check_lock_status(struct onenand_chip *this) * * Unlock all blocks */ -static int onenand_unlock_all(struct mtd_info *mtd) +static void onenand_unlock_all(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; + loff_t ofs = 0; + size_t len = this->chipsize; if (this->options & ONENAND_HAS_UNLOCK_ALL) { /* Set start block address */ @@ -2099,23 +2120,19 @@ static int onenand_unlock_all(struct mtd_info *mtd) & ONENAND_CTRL_ONGO) continue; + /* Check lock status */ + if (onenand_check_lock_status(this)) + return; + /* Workaround for all block unlock in DDP */ if (ONENAND_IS_DDP(this)) { - /* 1st block on another chip */ - loff_t ofs = this->chipsize >> 1; - size_t len = mtd->erasesize; - - onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); + /* All blocks on another chip */ + ofs = this->chipsize >> 1; + len = this->chipsize >> 1; } - - onenand_check_lock_status(this); - - return 0; } - onenand_do_lock_cmd(mtd, 0x0, this->chipsize, ONENAND_CMD_UNLOCK); - - return 0; + onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); } #ifdef CONFIG_MTD_ONENAND_OTP diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index aecdd50a1781..2f53b51c6805 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -17,9 +17,6 @@ #include <linux/mtd/onenand.h> #include <linux/mtd/compatmac.h> -extern int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops); - /** * check_short_pattern - [GENERIC] check if a pattern is in the buffer * @param buf the buffer to search diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index 823fba4e6d2f..c84e45465499 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -823,7 +823,7 @@ static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev) kfree(part); } -struct mtd_blktrans_ops rfd_ftl_tr = { +static struct mtd_blktrans_ops rfd_ftl_tr = { .name = "rfd", .major = RFD_FTL_MAJOR, .part_bits = PART_BITS, diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig index b9daf159a4a7..3f063108e95f 100644 --- a/drivers/mtd/ubi/Kconfig +++ b/drivers/mtd/ubi/Kconfig @@ -24,8 +24,13 @@ config MTD_UBI_WL_THRESHOLD erase counter value and the lowest erase counter value of eraseblocks of UBI devices. When this threshold is exceeded, UBI starts performing wear leveling by means of moving data from eraseblock with low erase - counter to eraseblocks with high erase counter. Leave the default - value if unsure. + counter to eraseblocks with high erase counter. + + The default value should be OK for SLC NAND flashes, NOR flashes and + other flashes which have eraseblock life-cycle 100000 or more. + However, in case of MLC NAND flashes which typically have eraseblock + life-cycle less then 10000, the threshold should be lessened (e.g., + to 128 or 256, although it does not have to be power of 2). config MTD_UBI_BEB_RESERVE int "Percentage of reserved eraseblocks for bad eraseblocks handling" diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 275960462970..961416ac0616 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -606,8 +606,16 @@ static int io_init(struct ubi_device *ubi) ubi->ro_mode = 1; } - dbg_msg("leb_size %d", ubi->leb_size); - dbg_msg("ro_mode %d", ubi->ro_mode); + ubi_msg("physical eraseblock size: %d bytes (%d KiB)", + ubi->peb_size, ubi->peb_size >> 10); + ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size); + ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size); + if (ubi->hdrs_min_io_size != ubi->min_io_size) + ubi_msg("sub-page size: %d", + ubi->hdrs_min_io_size); + ubi_msg("VID header offset: %d (aligned %d)", + ubi->vid_hdr_offset, ubi->vid_hdr_aloffset); + ubi_msg("data offset: %d", ubi->leb_start); /* * Note, ideally, we have to initialize ubi->bad_peb_count here. But @@ -755,8 +763,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) mutex_init(&ubi->volumes_mutex); spin_lock_init(&ubi->volumes_lock); - dbg_msg("attaching mtd%d to ubi%d: VID header offset %d", - mtd->index, ubi_num, vid_hdr_offset); + ubi_msg("attaching mtd%d to ubi%d", mtd->index, ubi_num); err = io_init(ubi); if (err) @@ -804,15 +811,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi_num); ubi_msg("MTD device name: \"%s\"", mtd->name); ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20); - ubi_msg("physical eraseblock size: %d bytes (%d KiB)", - ubi->peb_size, ubi->peb_size >> 10); - ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size); ubi_msg("number of good PEBs: %d", ubi->good_peb_count); ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count); - ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size); - ubi_msg("VID header offset: %d (aligned %d)", - ubi->vid_hdr_offset, ubi->vid_hdr_aloffset); - ubi_msg("data offset: %d", ubi->leb_start); ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots); ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD); ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT); @@ -950,8 +950,7 @@ static int __init ubi_init(void) BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64); if (mtd_devs > UBI_MAX_DEVICES) { - printk(KERN_ERR "UBI error: too many MTD devices, " - "maximum is %d\n", UBI_MAX_DEVICES); + ubi_err("too many MTD devices, maximum is %d", UBI_MAX_DEVICES); return -EINVAL; } @@ -959,25 +958,25 @@ static int __init ubi_init(void) ubi_class = class_create(THIS_MODULE, UBI_NAME_STR); if (IS_ERR(ubi_class)) { err = PTR_ERR(ubi_class); - printk(KERN_ERR "UBI error: cannot create UBI class\n"); + ubi_err("cannot create UBI class"); goto out; } err = class_create_file(ubi_class, &ubi_version); if (err) { - printk(KERN_ERR "UBI error: cannot create sysfs file\n"); + ubi_err("cannot create sysfs file"); goto out_class; } err = misc_register(&ubi_ctrl_cdev); if (err) { - printk(KERN_ERR "UBI error: cannot register device\n"); + ubi_err("cannot register device"); goto out_version; } ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab", - sizeof(struct ubi_wl_entry), - 0, 0, NULL); + sizeof(struct ubi_wl_entry), + 0, 0, NULL); if (!ubi_wl_entry_slab) goto out_dev_unreg; @@ -1000,8 +999,7 @@ static int __init ubi_init(void) mutex_unlock(&ubi_devices_mutex); if (err < 0) { put_mtd_device(mtd); - printk(KERN_ERR "UBI error: cannot attach mtd%d\n", - mtd->index); + ubi_err("cannot attach mtd%d", mtd->index); goto out_detach; } } @@ -1023,7 +1021,7 @@ out_version: out_class: class_destroy(ubi_class); out: - printk(KERN_ERR "UBI error: cannot initialize UBI, error %d\n", err); + ubi_err("UBI error: cannot initialize UBI, error %d", err); return err; } module_init(ubi_init); diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index 51c40b17f1ec..8ea99d8c9e1f 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h @@ -41,7 +41,7 @@ /* Generic debugging message */ #define dbg_msg(fmt, ...) \ printk(KERN_DEBUG "UBI DBG (pid %d): %s: " fmt "\n", \ - current->pid, __FUNCTION__, ##__VA_ARGS__) + current->pid, __func__, ##__VA_ARGS__) #define ubi_dbg_dump_stack() dump_stack() @@ -99,8 +99,10 @@ void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req); #ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD /* Initialization and build messages */ #define dbg_bld(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) +#define UBI_IO_DEBUG 1 #else #define dbg_bld(fmt, ...) ({}) +#define UBI_IO_DEBUG 0 #endif #ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c index d397219238d3..e909b390069a 100644 --- a/drivers/mtd/ubi/gluebi.c +++ b/drivers/mtd/ubi/gluebi.c @@ -291,11 +291,12 @@ int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol) /* * In case of dynamic volume, MTD device size is just volume size. In * case of a static volume the size is equivalent to the amount of data - * bytes, which is zero at this moment and will be changed after volume - * update. + * bytes. */ if (vol->vol_type == UBI_DYNAMIC_VOLUME) mtd->size = vol->usable_leb_size * vol->reserved_pebs; + else + mtd->size = vol->used_bytes; if (add_mtd_device(mtd)) { ubi_err("cannot not add MTD device\n"); diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index db3efdef2433..4ac11df7b048 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -631,6 +631,8 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, dbg_io("read EC header from PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + if (UBI_IO_DEBUG) + verbose = 1; err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE); if (err) { @@ -904,6 +906,8 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, dbg_io("read VID header from PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + if (UBI_IO_DEBUG) + verbose = 1; p = (char *)vid_hdr - ubi->vid_hdr_shift; err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 05aa3e7daba1..96d410e106ab 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -42,6 +42,7 @@ #include <linux/err.h> #include <linux/crc32.h> +#include <asm/div64.h> #include "ubi.h" #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID @@ -92,27 +93,6 @@ static int add_to_list(struct ubi_scan_info *si, int pnum, int ec, } /** - * commit_to_mean_value - commit intermediate results to the final mean erase - * counter value. - * @si: scanning information - * - * This is a helper function which calculates partial mean erase counter mean - * value and adds it to the resulting mean value. As we can work only in - * integer arithmetic and we want to calculate the mean value of erase counter - * accurately, we first sum erase counter values in @si->ec_sum variable and - * count these components in @si->ec_count. If this temporary @si->ec_sum is - * going to overflow, we calculate the partial mean value - * (@si->ec_sum/@si->ec_count) and add it to @si->mean_ec. - */ -static void commit_to_mean_value(struct ubi_scan_info *si) -{ - si->ec_sum /= si->ec_count; - if (si->ec_sum % si->ec_count >= si->ec_count / 2) - si->mean_ec += 1; - si->mean_ec += si->ec_sum; -} - -/** * validate_vid_hdr - check that volume identifier header is correct and * consistent. * @vid_hdr: the volume identifier header to check @@ -901,15 +881,8 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum adjust_mean_ec: if (!ec_corr) { - if (si->ec_sum + ec < ec) { - commit_to_mean_value(si); - si->ec_sum = 0; - si->ec_count = 0; - } else { - si->ec_sum += ec; - si->ec_count += 1; - } - + si->ec_sum += ec; + si->ec_count += 1; if (ec > si->max_ec) si->max_ec = ec; if (ec < si->min_ec) @@ -965,9 +938,11 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) dbg_msg("scanning is finished"); - /* Finish mean erase counter calculations */ - if (si->ec_count) - commit_to_mean_value(si); + /* Calculate mean erase counter */ + if (si->ec_count) { + do_div(si->ec_sum, si->ec_count); + si->mean_ec = si->ec_sum; + } if (si->is_empty) ubi_msg("empty MTD device detected"); diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h index 46d444af471a..966b9b682a42 100644 --- a/drivers/mtd/ubi/scan.h +++ b/drivers/mtd/ubi/scan.h @@ -124,7 +124,7 @@ struct ubi_scan_info { int max_ec; unsigned long long max_sqnum; int mean_ec; - int ec_sum; + uint64_t ec_sum; int ec_count; }; diff --git a/include/mtd/ubi-header.h b/drivers/mtd/ubi/ubi-media.h index 292f916ea564..c3185d9fd048 100644 --- a/include/mtd/ubi-header.h +++ b/drivers/mtd/ubi/ubi-media.h @@ -24,11 +24,11 @@ /* * This file defines the layout of UBI headers and all the other UBI on-flash - * data structures. May be included by user-space. + * data structures. */ -#ifndef __UBI_HEADER_H__ -#define __UBI_HEADER_H__ +#ifndef __UBI_MEDIA_H__ +#define __UBI_MEDIA_H__ #include <asm/byteorder.h> @@ -369,4 +369,4 @@ struct ubi_vtbl_record { __be32 crc; } __attribute__ ((packed)); -#endif /* !__UBI_HEADER_H__ */ +#endif /* !__UBI_MEDIA_H__ */ diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index a548c1d28fa8..67dcbd11c15c 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -37,10 +37,9 @@ #include <linux/string.h> #include <linux/vmalloc.h> #include <linux/mtd/mtd.h> - -#include <mtd/ubi-header.h> #include <linux/mtd/ubi.h> +#include "ubi-media.h" #include "scan.h" #include "debug.h" @@ -54,10 +53,10 @@ #define ubi_msg(fmt, ...) printk(KERN_NOTICE "UBI: " fmt "\n", ##__VA_ARGS__) /* UBI warning messages */ #define ubi_warn(fmt, ...) printk(KERN_WARNING "UBI warning: %s: " fmt "\n", \ - __FUNCTION__, ##__VA_ARGS__) + __func__, ##__VA_ARGS__) /* UBI error messages */ #define ubi_err(fmt, ...) printk(KERN_ERR "UBI error: %s: " fmt "\n", \ - __FUNCTION__, ##__VA_ARGS__) + __func__, ##__VA_ARGS__) /* Lowest number PEBs reserved for bad PEB handling */ #define MIN_RESEVED_PEBS 2 diff --git a/fs/jffs2/README.Locking b/fs/jffs2/README.Locking index d14d5a4dc5ac..3ea36554107f 100644 --- a/fs/jffs2/README.Locking +++ b/fs/jffs2/README.Locking @@ -14,7 +14,7 @@ be fairly close. alloc_sem --------- -The alloc_sem is a per-filesystem semaphore, used primarily to ensure +The alloc_sem is a per-filesystem mutex, used primarily to ensure contiguous allocation of space on the medium. It is automatically obtained during space allocations (jffs2_reserve_space()) and freed upon write completion (jffs2_complete_reservation()). Note that @@ -41,10 +41,10 @@ if the wbuf is currently holding any data is permitted, though. Ordering constraints: See f->sem. - File Semaphore f->sem + File Mutex f->sem --------------------- -This is the JFFS2-internal equivalent of the inode semaphore i->i_sem. +This is the JFFS2-internal equivalent of the inode mutex i->i_sem. It protects the contents of the jffs2_inode_info private inode data, including the linked list of node fragments (but see the notes below on erase_completion_lock), etc. @@ -60,14 +60,14 @@ lead to deadlock, unless we played games with unlocking the i_sem before calling the space allocation functions. Instead of playing such games, we just have an extra internal -semaphore, which is obtained by the garbage collection code and also +mutex, which is obtained by the garbage collection code and also by the normal file system code _after_ allocation of space. Ordering constraints: 1. Never attempt to allocate space or lock alloc_sem with any f->sem held. - 2. Never attempt to lock two file semaphores in one thread. + 2. Never attempt to lock two file mutexes in one thread. No ordering rules have been made for doing so. @@ -86,8 +86,8 @@ a simple spin_lock() rather than spin_lock_bh(). Note that the per-inode list of physical nodes (f->nodes) is a special case. Any changes to _valid_ nodes (i.e. ->flash_offset & 1 == 0) in -the list are protected by the file semaphore f->sem. But the erase -code may remove _obsolete_ nodes from the list while holding only the +the list are protected by the file mutex f->sem. But the erase code +may remove _obsolete_ nodes from the list while holding only the erase_completion_lock. So you can walk the list only while holding the erase_completion_lock, and can drop the lock temporarily mid-walk as long as the pointer you're holding is to a _valid_ node, not an @@ -124,10 +124,10 @@ Ordering constraints: erase_free_sem -------------- -This semaphore is only used by the erase code which frees obsolete -node references and the jffs2_garbage_collect_deletion_dirent() -function. The latter function on NAND flash must read _obsolete_ nodes -to determine whether the 'deletion dirent' under consideration can be +This mutex is only used by the erase code which frees obsolete node +references and the jffs2_garbage_collect_deletion_dirent() function. +The latter function on NAND flash must read _obsolete_ nodes to +determine whether the 'deletion dirent' under consideration can be discarded or whether it is still required to show that an inode has been unlinked. Because reading from the flash may sleep, the erase_completion_lock cannot be held, so an alternative, more diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 722a6b682951..d58f845ccb85 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -345,6 +345,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c) INIT_LIST_HEAD(&c->dirty_list); INIT_LIST_HEAD(&c->erasable_list); INIT_LIST_HEAD(&c->erasing_list); + INIT_LIST_HEAD(&c->erase_checking_list); INIT_LIST_HEAD(&c->erase_pending_list); INIT_LIST_HEAD(&c->erasable_pending_wbuf_list); INIT_LIST_HEAD(&c->erase_complete_list); diff --git a/fs/jffs2/debug.c b/fs/jffs2/debug.c index 3a32c64ed497..5544d31c066b 100644 --- a/fs/jffs2/debug.c +++ b/fs/jffs2/debug.c @@ -62,9 +62,9 @@ __jffs2_dbg_acct_sanity_check(struct jffs2_sb_info *c, void __jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f) { - down(&f->sem); + mutex_lock(&f->sem); __jffs2_dbg_fragtree_paranoia_check_nolock(f); - up(&f->sem); + mutex_unlock(&f->sem); } void @@ -153,6 +153,139 @@ __jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c, kfree(buf); } +void __jffs2_dbg_superblock_counts(struct jffs2_sb_info *c) +{ + struct jffs2_eraseblock *jeb; + uint32_t free = 0, dirty = 0, used = 0, wasted = 0, + erasing = 0, bad = 0, unchecked = 0; + int nr_counted = 0; + int dump = 0; + + if (c->gcblock) { + nr_counted++; + free += c->gcblock->free_size; + dirty += c->gcblock->dirty_size; + used += c->gcblock->used_size; + wasted += c->gcblock->wasted_size; + unchecked += c->gcblock->unchecked_size; + } + if (c->nextblock) { + nr_counted++; + free += c->nextblock->free_size; + dirty += c->nextblock->dirty_size; + used += c->nextblock->used_size; + wasted += c->nextblock->wasted_size; + unchecked += c->nextblock->unchecked_size; + } + list_for_each_entry(jeb, &c->clean_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->very_dirty_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->dirty_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->erasable_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->erasable_pending_wbuf_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->erase_pending_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->free_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->bad_used_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + + list_for_each_entry(jeb, &c->erasing_list, list) { + nr_counted++; + erasing += c->sector_size; + } + list_for_each_entry(jeb, &c->erase_checking_list, list) { + nr_counted++; + erasing += c->sector_size; + } + list_for_each_entry(jeb, &c->erase_complete_list, list) { + nr_counted++; + erasing += c->sector_size; + } + list_for_each_entry(jeb, &c->bad_list, list) { + nr_counted++; + bad += c->sector_size; + } + +#define check(sz) \ + if (sz != c->sz##_size) { \ + printk(KERN_WARNING #sz "_size mismatch counted 0x%x, c->" #sz "_size 0x%x\n", \ + sz, c->sz##_size); \ + dump = 1; \ + } + check(free); + check(dirty); + check(used); + check(wasted); + check(unchecked); + check(bad); + check(erasing); +#undef check + + if (nr_counted != c->nr_blocks) { + printk(KERN_WARNING "%s counted only 0x%x blocks of 0x%x. Where are the others?\n", + __func__, nr_counted, c->nr_blocks); + dump = 1; + } + + if (dump) { + __jffs2_dbg_dump_block_lists_nolock(c); + BUG(); + } +} + /* * Check the space accounting and node_ref list correctness for the JFFS2 erasable block 'jeb'. */ @@ -229,6 +362,9 @@ __jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c, } #endif + if (!(c->flags & (JFFS2_SB_FLAG_BUILDING|JFFS2_SB_FLAG_SCANNING))) + __jffs2_dbg_superblock_counts(c); + return; error: @@ -268,7 +404,10 @@ __jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c, printk(JFFS2_DBG); for (ref = jeb->first_node; ; ref = ref_next(ref)) { - printk("%#08x(%#x)", ref_offset(ref), ref->__totlen); + printk("%#08x", ref_offset(ref)); +#ifdef TEST_TOTLEN + printk("(%x)", ref->__totlen); +#endif if (ref_next(ref)) printk("->"); else @@ -447,6 +586,21 @@ __jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c) } } } + if (list_empty(&c->erase_checking_list)) { + printk(JFFS2_DBG "erase_checking_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->erase_checking_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG "erase_checking_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, + jeb->unchecked_size, jeb->free_size); + } + } + } if (list_empty(&c->erase_pending_list)) { printk(JFFS2_DBG "erase_pending_list: empty\n"); @@ -532,9 +686,9 @@ __jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c) void __jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f) { - down(&f->sem); + mutex_lock(&f->sem); jffs2_dbg_dump_fragtree_nolock(f); - up(&f->sem); + mutex_unlock(&f->sem); } void diff --git a/fs/jffs2/debug.h b/fs/jffs2/debug.h index 4130adabd76e..9645275023e6 100644 --- a/fs/jffs2/debug.h +++ b/fs/jffs2/debug.h @@ -38,6 +38,7 @@ #if CONFIG_JFFS2_FS_DEBUG > 1 #define JFFS2_DBG_FRAGTREE2_MESSAGES +#define JFFS2_DBG_READINODE2_MESSAGES #define JFFS2_DBG_MEMALLOC_MESSAGES #endif @@ -115,6 +116,11 @@ #else #define dbg_readinode(fmt, ...) #endif +#ifdef JFFS2_DBG_READINODE2_MESSAGES +#define dbg_readinode2(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define dbg_readinode2(fmt, ...) +#endif /* Fragtree build debugging messages */ #ifdef JFFS2_DBG_FRAGTREE_MESSAGES diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index f948f7e6ec82..c63e7a96af0d 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -86,7 +86,7 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, dir_f = JFFS2_INODE_INFO(dir_i); c = JFFS2_SB_INFO(dir_i->i_sb); - down(&dir_f->sem); + mutex_lock(&dir_f->sem); /* NB: The 2.2 backport will need to explicitly check for '.' and '..' here */ for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= target->d_name.hash; fd_list = fd_list->next) { @@ -99,7 +99,7 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, } if (fd) ino = fd->ino; - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); if (ino) { inode = jffs2_iget(dir_i->i_sb, ino); if (IS_ERR(inode)) { @@ -146,7 +146,7 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir) } curofs=1; - down(&f->sem); + mutex_lock(&f->sem); for (fd = f->dents; fd; fd = fd->next) { curofs++; @@ -166,7 +166,7 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir) break; offset++; } - up(&f->sem); + mutex_unlock(&f->sem); out: filp->f_pos = offset; return 0; @@ -275,9 +275,9 @@ static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct de ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len, now); if (!ret) { - down(&f->sem); + mutex_lock(&f->sem); old_dentry->d_inode->i_nlink = ++f->inocache->nlink; - up(&f->sem); + mutex_unlock(&f->sem); d_instantiate(dentry, old_dentry->d_inode); dir_i->i_mtime = dir_i->i_ctime = ITIME(now); atomic_inc(&old_dentry->d_inode->i_count); @@ -351,7 +351,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return PTR_ERR(fn); @@ -361,7 +361,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char f->target = kmalloc(targetlen + 1, GFP_KERNEL); if (!f->target) { printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return -ENOMEM; @@ -374,7 +374,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char obsoleted by the first data write */ f->metadata = fn; - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); @@ -406,7 +406,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char } dir_f = JFFS2_INODE_INFO(dir_i); - down(&dir_f->sem); + mutex_lock(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); @@ -429,7 +429,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_clear_inode(inode); return PTR_ERR(fd); } @@ -442,7 +442,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); @@ -507,7 +507,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return PTR_ERR(fn); @@ -516,7 +516,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) obsoleted by the first data write */ f->metadata = fn; - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); @@ -548,7 +548,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) } dir_f = JFFS2_INODE_INFO(dir_i); - down(&dir_f->sem); + mutex_lock(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); @@ -571,7 +571,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_clear_inode(inode); return PTR_ERR(fd); } @@ -585,7 +585,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); @@ -673,7 +673,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return PTR_ERR(fn); @@ -682,7 +682,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de obsoleted by the first data write */ f->metadata = fn; - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); @@ -714,7 +714,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de } dir_f = JFFS2_INODE_INFO(dir_i); - down(&dir_f->sem); + mutex_lock(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); @@ -740,7 +740,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_clear_inode(inode); return PTR_ERR(fd); } @@ -753,7 +753,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); @@ -780,14 +780,14 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, if (S_ISDIR(new_dentry->d_inode->i_mode)) { struct jffs2_full_dirent *fd; - down(&victim_f->sem); + mutex_lock(&victim_f->sem); for (fd = victim_f->dents; fd; fd = fd->next) { if (fd->ino) { - up(&victim_f->sem); + mutex_unlock(&victim_f->sem); return -ENOTEMPTY; } } - up(&victim_f->sem); + mutex_unlock(&victim_f->sem); } } @@ -816,9 +816,9 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, /* Don't oops if the victim was a dirent pointing to an inode which didn't exist. */ if (victim_f->inocache) { - down(&victim_f->sem); + mutex_lock(&victim_f->sem); victim_f->inocache->nlink--; - up(&victim_f->sem); + mutex_unlock(&victim_f->sem); } } @@ -836,11 +836,11 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, if (ret) { /* Oh shit. We really ought to make a single node which can do both atomically */ struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); - down(&f->sem); + mutex_lock(&f->sem); inc_nlink(old_dentry->d_inode); if (f->inocache) f->inocache->nlink++; - up(&f->sem); + mutex_unlock(&f->sem); printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret); /* Might as well let the VFS know */ diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index a1db9180633f..25a640e566d3 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -50,14 +50,14 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL); if (!instr) { printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); return; } @@ -84,14 +84,14 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, if (ret == -ENOMEM || ret == -EAGAIN) { /* Erase failed immediately. Refile it on the list */ D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret)); - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); return; } @@ -107,7 +107,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) { struct jffs2_eraseblock *jeb; - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); @@ -116,9 +116,9 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) if (!list_empty(&c->erase_complete_list)) { jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); - list_del(&jeb->list); + list_move(&jeb->list, &c->erase_checking_list); spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); jffs2_mark_erased_block(c, jeb); if (!--count) { @@ -139,7 +139,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) jffs2_free_jeb_node_refs(c, jeb); list_add(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); jffs2_erase_block(c, jeb); @@ -149,12 +149,12 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) /* Be nice */ yield(); - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); } spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); done: D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); } @@ -162,11 +162,11 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset)); - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move_tail(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); /* Ensure that kupdated calls us again to mark them clean */ jffs2_erase_pending_trigger(c); } @@ -180,26 +180,26 @@ static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock failed too many times. */ if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) { /* We'd like to give this block another try. */ - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); return; } } - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); c->erasing_size -= c->sector_size; c->bad_size += c->sector_size; list_move(&jeb->list, &c->bad_list); c->nr_erasing_blocks--; spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); wake_up(&c->erase_wait); } @@ -350,9 +350,11 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl break; } while(--retlen); c->mtd->unpoint(c->mtd, ebuf, jeb->offset, c->sector_size); - if (retlen) + if (retlen) { printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08tx\n", *wordebuf, jeb->offset + c->sector_size-retlen*sizeof(*wordebuf)); + return -EIO; + } return 0; } do_flash_read: @@ -373,10 +375,12 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf); if (ret) { printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret); + ret = -EIO; goto fail; } if (retlen != readlen) { printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen); + ret = -EIO; goto fail; } for (i=0; i<readlen; i += sizeof(unsigned long)) { @@ -385,6 +389,7 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl if (*datum + 1) { *bad_offset += i; printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", *datum, *bad_offset); + ret = -EIO; goto fail; } } @@ -419,9 +424,6 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb if (jffs2_write_nand_cleanmarker(c, jeb)) goto filebad; } - - /* Everything else got zeroed before the erase */ - jeb->free_size = c->sector_size; } else { struct kvec vecs[1]; @@ -449,48 +451,50 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb goto filebad; } - - /* Everything else got zeroed before the erase */ - jeb->free_size = c->sector_size; - /* FIXME Special case for cleanmarker in empty block */ - jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL); } + /* Everything else got zeroed before the erase */ + jeb->free_size = c->sector_size; - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); + c->erasing_size -= c->sector_size; - c->free_size += jeb->free_size; - c->used_size += jeb->used_size; + c->free_size += c->sector_size; - jffs2_dbg_acct_sanity_check_nolock(c,jeb); - jffs2_dbg_acct_paranoia_check_nolock(c, jeb); + /* Account for cleanmarker now, if it's in-band */ + if (c->cleanmarker_size && !jffs2_cleanmarker_oob(c)) + jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL); - list_add_tail(&jeb->list, &c->free_list); + list_move_tail(&jeb->list, &c->free_list); c->nr_erasing_blocks--; c->nr_free_blocks++; + + jffs2_dbg_acct_sanity_check_nolock(c, jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); + spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); wake_up(&c->erase_wait); return; filebad: - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); /* Stick it on a list (any list) so erase_failed can take it right off again. Silly, but shouldn't happen often. */ - list_add(&jeb->list, &c->erasing_list); + list_move(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); jffs2_erase_failed(c, jeb, bad_offset); return; refile: /* Stick it back on the list from whence it came and come back later */ jffs2_erase_pending_trigger(c); - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); - list_add(&jeb->list, &c->erase_complete_list); + list_move(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); return; } diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index dcc2734e0b5d..5e920343b2c5 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -115,9 +115,9 @@ static int jffs2_readpage (struct file *filp, struct page *pg) struct jffs2_inode_info *f = JFFS2_INODE_INFO(pg->mapping->host); int ret; - down(&f->sem); + mutex_lock(&f->sem); ret = jffs2_do_readpage_unlock(pg->mapping->host, pg); - up(&f->sem); + mutex_unlock(&f->sem); return ret; } @@ -154,7 +154,7 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, if (ret) goto out_page; - down(&f->sem); + mutex_lock(&f->sem); memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -181,7 +181,7 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, if (IS_ERR(fn)) { ret = PTR_ERR(fn); jffs2_complete_reservation(c); - up(&f->sem); + mutex_unlock(&f->sem); goto out_page; } ret = jffs2_add_full_dnode_to_inode(c, f, fn); @@ -195,12 +195,12 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); jffs2_complete_reservation(c); - up(&f->sem); + mutex_unlock(&f->sem); goto out_page; } jffs2_complete_reservation(c); inode->i_size = pageofs; - up(&f->sem); + mutex_unlock(&f->sem); } /* @@ -209,9 +209,9 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, * case of a short-copy. */ if (!PageUptodate(pg)) { - down(&f->sem); + mutex_lock(&f->sem); ret = jffs2_do_readpage_nolock(inode, pg); - up(&f->sem); + mutex_unlock(&f->sem); if (ret) goto out_page; } diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index e26ea78c7892..3eb1c84b0a33 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -36,6 +36,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) unsigned int ivalid; uint32_t alloclen; int ret; + int alloc_type = ALLOC_NORMAL; D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino)); @@ -50,20 +51,20 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) mdata = (char *)&dev; D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen)); } else if (S_ISLNK(inode->i_mode)) { - down(&f->sem); + mutex_lock(&f->sem); mdatalen = f->metadata->size; mdata = kmalloc(f->metadata->size, GFP_USER); if (!mdata) { - up(&f->sem); + mutex_unlock(&f->sem); return -ENOMEM; } ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen); if (ret) { - up(&f->sem); + mutex_unlock(&f->sem); kfree(mdata); return ret; } - up(&f->sem); + mutex_unlock(&f->sem); D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen)); } @@ -82,7 +83,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) kfree(mdata); return ret; } - down(&f->sem); + mutex_lock(&f->sem); ivalid = iattr->ia_valid; ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -115,6 +116,10 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) ri->compr = JFFS2_COMPR_ZERO; ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size); ri->offset = cpu_to_je32(inode->i_size); + } else if (ivalid & ATTR_SIZE && !iattr->ia_size) { + /* For truncate-to-zero, treat it as deletion because + it'll always be obsoleting all previous nodes */ + alloc_type = ALLOC_DELETION; } ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); if (mdatalen) @@ -122,14 +127,14 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) else ri->data_crc = cpu_to_je32(0); - new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, ALLOC_NORMAL); + new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, alloc_type); if (S_ISLNK(inode->i_mode)) kfree(mdata); if (IS_ERR(new_metadata)) { jffs2_complete_reservation(c); jffs2_free_raw_inode(ri); - up(&f->sem); + mutex_unlock(&f->sem); return PTR_ERR(new_metadata); } /* It worked. Update the inode */ @@ -149,6 +154,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { jffs2_add_full_dnode_to_inode(c, f, new_metadata); inode->i_size = iattr->ia_size; + inode->i_blocks = (inode->i_size + 511) >> 9; f->metadata = NULL; } else { f->metadata = new_metadata; @@ -159,7 +165,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) } jffs2_free_raw_inode(ri); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); /* We have to do the vmtruncate() without f->sem held, since @@ -167,8 +173,10 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) We are protected from a simultaneous write() extending i_size back past iattr->ia_size, because do_truncate() holds the generic inode semaphore. */ - if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) - vmtruncate(inode, iattr->ia_size); + if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) { + vmtruncate(inode, iattr->ia_size); + inode->i_blocks = (inode->i_size + 511) >> 9; + } return 0; } @@ -248,12 +256,12 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) c = JFFS2_SB_INFO(inode->i_sb); jffs2_init_inode_info(f); - down(&f->sem); + mutex_lock(&f->sem); ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); if (ret) { - up(&f->sem); + mutex_unlock(&f->sem); iget_failed(inode); return ERR_PTR(ret); } @@ -330,7 +338,7 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino); } - up(&f->sem); + mutex_unlock(&f->sem); D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); unlock_new_inode(inode); @@ -339,7 +347,7 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) error_io: ret = -EIO; error: - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); iget_failed(inode); return ERR_PTR(ret); @@ -380,9 +388,9 @@ int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) Flush the writebuffer, if neccecary, else we loose it */ if (!(sb->s_flags & MS_RDONLY)) { jffs2_stop_garbage_collect_thread(c); - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); jffs2_flush_wbuf_pad(c); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); } if (!(*flags & MS_RDONLY)) @@ -429,7 +437,7 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i f = JFFS2_INODE_INFO(inode); jffs2_init_inode_info(f); - down(&f->sem); + mutex_lock(&f->sem); memset(ri, 0, sizeof(*ri)); /* Set OS-specific defaults for new inodes */ diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 32ff0373aa04..bad005664e30 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -126,7 +126,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) int ret = 0, inum, nlink; int xattr = 0; - if (down_interruptible(&c->alloc_sem)) + if (mutex_lock_interruptible(&c->alloc_sem)) return -EINTR; for (;;) { @@ -143,7 +143,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) c->unchecked_size); jffs2_dbg_dump_block_lists_nolock(c); spin_unlock(&c->erase_completion_lock); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return -ENOSPC; } @@ -190,7 +190,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) made no progress in this case, but that should be OK */ c->checked_ino--; - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); return 0; @@ -210,7 +210,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino); jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return ret; } @@ -221,9 +221,15 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) jeb = jffs2_find_gc_block(c); if (!jeb) { - D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n")); + /* Couldn't find a free block. But maybe we can just erase one and make 'progress'? */ + if (!list_empty(&c->erase_pending_list)) { + spin_unlock(&c->erase_completion_lock); + mutex_unlock(&c->alloc_sem); + return -EAGAIN; + } + D1(printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n")); spin_unlock(&c->erase_completion_lock); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return -EIO; } @@ -232,7 +238,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) printk(KERN_DEBUG "Nextblock at %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size)); if (!jeb->used_size) { - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); goto eraseit; } @@ -248,7 +254,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size); jeb->gc_node = raw; spin_unlock(&c->erase_completion_lock); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); BUG(); } } @@ -266,7 +272,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) /* Just mark it obsolete */ jffs2_mark_node_obsolete(c, raw); } - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); goto eraseit_lock; } @@ -334,7 +340,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) */ printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n", ic->ino, ic->state); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); spin_unlock(&c->inocache_lock); BUG(); @@ -345,7 +351,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) the alloc_sem() (for marking nodes invalid) so we must drop the alloc_sem before sleeping. */ - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n", ic->ino, ic->state)); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); @@ -416,7 +422,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) ret = -ENOSPC; } release_sem: - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); eraseit_lock: /* If we've finished this block, start it erasing */ @@ -445,7 +451,7 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_era uint32_t start = 0, end = 0, nrfrags = 0; int ret = 0; - down(&f->sem); + mutex_lock(&f->sem); /* Now we have the lock for this inode. Check that it's still the one at the head of the list. */ @@ -525,7 +531,7 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_era } } upnout: - up(&f->sem); + mutex_unlock(&f->sem); return ret; } @@ -846,7 +852,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct /* Prevent the erase code from nicking the obsolete node refs while we're looking at them. I really don't like this extra lock but can't see any alternative. Suggestions on a postcard to... */ - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) { @@ -899,7 +905,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct /* OK. The name really does match. There really is still an older node on the flash which our deletion dirent obsoletes. So we have to write out a new deletion dirent to replace it */ - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n", ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino))); @@ -908,7 +914,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct return jffs2_garbage_collect_dirent(c, jeb, f, fd); } - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); kfree(rd); } @@ -1081,7 +1087,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras return 0; } -static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *orig_jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end) { diff --git a/fs/jffs2/ioctl.c b/fs/jffs2/ioctl.c index f4d525b0ea53..e2177210f621 100644 --- a/fs/jffs2/ioctl.c +++ b/fs/jffs2/ioctl.c @@ -10,6 +10,7 @@ */ #include <linux/fs.h> +#include "nodelist.h" int jffs2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h index a841f4973a74..31559f45fdde 100644 --- a/fs/jffs2/jffs2_fs_i.h +++ b/fs/jffs2/jffs2_fs_i.h @@ -15,7 +15,7 @@ #include <linux/version.h> #include <linux/rbtree.h> #include <linux/posix_acl.h> -#include <linux/semaphore.h> +#include <linux/mutex.h> struct jffs2_inode_info { /* We need an internal mutex similar to inode->i_mutex. @@ -24,7 +24,7 @@ struct jffs2_inode_info { before letting GC proceed. Or we'd have to put ugliness into the GC code so it didn't attempt to obtain the i_mutex for the inode(s) which are already locked */ - struct semaphore sem; + struct mutex sem; /* The highest (datanode) version number used for this ino */ uint32_t highest_version; diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index 18fca2b9e531..85ef6dbb1be7 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -16,7 +16,7 @@ #include <linux/spinlock.h> #include <linux/workqueue.h> #include <linux/completion.h> -#include <linux/semaphore.h> +#include <linux/mutex.h> #include <linux/timer.h> #include <linux/wait.h> #include <linux/list.h> @@ -44,7 +44,7 @@ struct jffs2_sb_info { struct completion gc_thread_start; /* GC thread start completion */ struct completion gc_thread_exit; /* GC thread exit completion port */ - struct semaphore alloc_sem; /* Used to protect all the following + struct mutex alloc_sem; /* Used to protect all the following fields, and also to protect against out-of-order writing of nodes. And GC. */ uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER @@ -87,6 +87,7 @@ struct jffs2_sb_info { struct list_head erasable_list; /* Blocks which are completely dirty, and need erasing */ struct list_head erasable_pending_wbuf_list; /* Blocks which need erasing but only after the current wbuf is flushed */ struct list_head erasing_list; /* Blocks which are currently erasing */ + struct list_head erase_checking_list; /* Blocks which are being checked and marked */ struct list_head erase_pending_list; /* Blocks which need erasing now */ struct list_head erase_complete_list; /* Blocks which are erased and need the clean marker written to them */ struct list_head free_list; /* Blocks which are free and ready to be used */ @@ -104,7 +105,7 @@ struct jffs2_sb_info { /* Sem to allow jffs2_garbage_collect_deletion_dirent to drop the erase_completion_lock while it's holding a pointer to an obsoleted node. I don't like this. Alternatives welcomed. */ - struct semaphore erase_free_sem; + struct mutex erase_free_sem; uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index ec1aae9e695e..8219df6eb6d8 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -87,7 +87,7 @@ struct jffs2_raw_node_ref xattr_ref or xattr_datum instead. The common part of those structures has NULL in the first word. See jffs2_raw_ref_to_ic() below */ uint32_t flash_offset; -#define TEST_TOTLEN +#undef TEST_TOTLEN #ifdef TEST_TOTLEN uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */ #endif diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index a0313fa8748e..9df8f3ef20df 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -48,7 +48,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, minsize = PAD(minsize); D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize)); - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n")); @@ -57,7 +57,6 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, /* this needs a little more thought (true <tglx> :)) */ while(ret == -EAGAIN) { while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { - int ret; uint32_t dirty, avail; /* calculate real dirty size @@ -82,7 +81,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, dirty, c->unchecked_size, c->sector_size)); spin_unlock(&c->erase_completion_lock); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return -ENOSPC; } @@ -105,11 +104,11 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n", avail, blocksneeded * c->sector_size)); spin_unlock(&c->erase_completion_lock); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return -ENOSPC; } - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n", c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size, @@ -117,7 +116,10 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, spin_unlock(&c->erase_completion_lock); ret = jffs2_garbage_collect_pass(c); - if (ret) + + if (ret == -EAGAIN) + jffs2_erase_pending_blocks(c, 1); + else if (ret) return ret; cond_resched(); @@ -125,7 +127,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, if (signal_pending(current)) return -EINTR; - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); spin_lock(&c->erase_completion_lock); } @@ -138,7 +140,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, if (!ret) ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1); if (ret) - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return ret; } @@ -463,7 +465,7 @@ void jffs2_complete_reservation(struct jffs2_sb_info *c) { D1(printk(KERN_DEBUG "jffs2_complete_reservation()\n")); jffs2_garbage_collect_trigger(c); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); } static inline int on_list(struct list_head *obj, struct list_head *head) @@ -512,7 +514,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref any jffs2_raw_node_refs. So we don't need to stop erases from happening, or protect against people holding an obsolete jffs2_raw_node_ref without the erase_completion_lock. */ - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); } spin_lock(&c->erase_completion_lock); @@ -715,7 +717,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref } out_erase_sem: - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); } int jffs2_thread_should_wake(struct jffs2_sb_info *c) diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index e512a93d6249..4cb4d76de07f 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -825,8 +825,9 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref else // normal case... tn->fn->size = je32_to_cpu(rd->dsize); - dbg_readinode("dnode @%08x: ver %u, offset %#04x, dsize %#04x, csize %#04x\n", - ref_offset(ref), je32_to_cpu(rd->version), je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize), csize); + dbg_readinode2("dnode @%08x: ver %u, offset %#04x, dsize %#04x, csize %#04x\n", + ref_offset(ref), je32_to_cpu(rd->version), + je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize), csize); ret = jffs2_add_tn_to_tree(c, rii, tn); @@ -836,13 +837,13 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref jffs2_free_tmp_dnode_info(tn); return ret; } -#ifdef JFFS2_DBG_READINODE_MESSAGES - dbg_readinode("After adding ver %d:\n", je32_to_cpu(rd->version)); +#ifdef JFFS2_DBG_READINODE2_MESSAGES + dbg_readinode2("After adding ver %d:\n", je32_to_cpu(rd->version)); tn = tn_first(&rii->tn_root); while (tn) { - dbg_readinode("%p: v %d r 0x%x-0x%x ov %d\n", - tn, tn->version, tn->fn->ofs, - tn->fn->ofs+tn->fn->size, tn->overlapped); + dbg_readinode2("%p: v %d r 0x%x-0x%x ov %d\n", + tn, tn->version, tn->fn->ofs, + tn->fn->ofs+tn->fn->size, tn->overlapped); tn = tn_next(tn); } #endif @@ -1193,7 +1194,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n", ret, retlen, sizeof(*latest_node)); /* FIXME: If this fails, there seems to be a memory leak. Find it. */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return ret?ret:-EIO; } @@ -1202,7 +1203,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, if (crc != je32_to_cpu(latest_node->node_crc)) { JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(rii.latest_ref)); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } @@ -1242,7 +1243,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, f->target = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL); if (!f->target) { JFFS2_ERROR("can't allocate %d bytes of memory for the symlink target path cache\n", je32_to_cpu(latest_node->csize)); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -ENOMEM; } @@ -1255,7 +1256,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, ret = -EIO; kfree(f->target); f->target = NULL; - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -ret; } @@ -1273,14 +1274,14 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, if (f->metadata) { JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } if (!frag_first(&f->fragtree)) { JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } @@ -1289,7 +1290,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, JFFS2_ERROR("Argh. Special inode #%u with mode 0x%x had more than one node\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } @@ -1379,12 +1380,13 @@ int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *i if (!f) return -ENOMEM; - init_MUTEX_LOCKED(&f->sem); + mutex_init(&f->sem); + mutex_lock(&f->sem); f->inocache = ic; ret = jffs2_do_read_inode_internal(c, f, &n); if (!ret) { - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); } kfree (f); @@ -1398,7 +1400,7 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) jffs2_clear_acl(f); jffs2_xattr_delete_inode(c, f->inocache); - down(&f->sem); + mutex_lock(&f->sem); deleted = f->inocache && !f->inocache->nlink; if (f->inocache && f->inocache->state != INO_STATE_CHECKING) @@ -1430,5 +1432,5 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) jffs2_del_ino_cache(c, f->inocache); } - up(&f->sem); + mutex_unlock(&f->sem); } diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 4677355996cc..f3353df178e7 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -47,7 +47,7 @@ static void jffs2_i_init_once(struct kmem_cache *cachep, void *foo) { struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo; - init_MUTEX(&ei->sem); + mutex_init(&ei->sem); inode_init_once(&ei->vfs_inode); } @@ -55,9 +55,9 @@ static int jffs2_sync_fs(struct super_block *sb, int wait) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); jffs2_flush_wbuf_pad(c); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return 0; } @@ -95,8 +95,8 @@ static int jffs2_fill_super(struct super_block *sb, void *data, int silent) /* Initialize JFFS2 superblock locks, the further initialization will * be done later */ - init_MUTEX(&c->alloc_sem); - init_MUTEX(&c->erase_free_sem); + mutex_init(&c->alloc_sem); + mutex_init(&c->erase_free_sem); init_waitqueue_head(&c->erase_wait); init_waitqueue_head(&c->inocache_wq); spin_lock_init(&c->erase_completion_lock); @@ -125,9 +125,9 @@ static void jffs2_put_super (struct super_block *sb) D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n")); - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); jffs2_flush_wbuf_pad(c); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); jffs2_sum_exit(c); diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index d1d4f27464ba..8de52b607678 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -578,8 +578,8 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) if (!jffs2_is_writebuffered(c)) return 0; - if (!down_trylock(&c->alloc_sem)) { - up(&c->alloc_sem); + if (mutex_trylock(&c->alloc_sem)) { + mutex_unlock(&c->alloc_sem); printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n"); BUG(); } @@ -702,10 +702,10 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) if (!c->wbuf) return 0; - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); if (!jffs2_wbuf_pending_for_ino(c, ino)) { D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino)); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return 0; } @@ -725,14 +725,14 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) } else while (old_wbuf_len && old_wbuf_ofs == c->wbuf_ofs) { - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n")); ret = jffs2_garbage_collect_pass(c); if (ret) { /* GC failed. Flush it with padding instead */ - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); down_write(&c->wbuf_sem); ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); /* retry flushing wbuf in case jffs2_wbuf_recover @@ -742,12 +742,12 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) up_write(&c->wbuf_sem); break; } - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); } D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n")); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return ret; } @@ -1236,12 +1236,24 @@ int jffs2_dataflash_setup(struct jffs2_sb_info *c) { if (!c->wbuf) return -ENOMEM; +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf_verify) { + kfree(c->oobbuf); + kfree(c->wbuf); + return -ENOMEM; + } +#endif + printk(KERN_INFO "JFFS2 write-buffering enabled buffer (%d) erasesize (%d)\n", c->wbuf_pagesize, c->sector_size); return 0; } void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) { +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + kfree(c->wbuf_verify); +#endif kfree(c->wbuf); } diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 776f13cbf2b5..665fce9797d3 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -137,12 +137,12 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2 JFFS2_SUMMARY_INODE_SIZE); } else { /* Locking pain */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &dummy, alloc_mode, JFFS2_SUMMARY_INODE_SIZE); - down(&f->sem); + mutex_lock(&f->sem); } if (!ret) { @@ -285,12 +285,12 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff JFFS2_SUMMARY_DIRENT_SIZE(namelen)); } else { /* Locking pain */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &dummy, alloc_mode, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); - down(&f->sem); + mutex_lock(&f->sem); } if (!ret) { @@ -353,7 +353,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret)); break; } - down(&f->sem); + mutex_lock(&f->sem); datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1))); cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen); @@ -381,7 +381,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, if (IS_ERR(fn)) { ret = PTR_ERR(fn); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); if (!retried) { /* Write error to be retried */ @@ -403,11 +403,11 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); break; } - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); if (!datalen) { printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n"); @@ -439,7 +439,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str JFFS2_SUMMARY_INODE_SIZE); D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen)); if (ret) { - up(&f->sem); + mutex_unlock(&f->sem); return ret; } @@ -454,7 +454,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str if (IS_ERR(fn)) { D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n")); /* Eeek. Wave bye bye */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); return PTR_ERR(fn); } @@ -463,7 +463,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str */ f->metadata = fn; - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = jffs2_init_security(&f->vfs_inode, &dir_f->vfs_inode); @@ -489,7 +489,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str return -ENOMEM; } - down(&dir_f->sem); + mutex_lock(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); @@ -513,7 +513,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ jffs2_complete_reservation(c); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); return PTR_ERR(fd); } @@ -522,7 +522,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str jffs2_add_fd_to_list(c, fd, &dir_f->dents); jffs2_complete_reservation(c); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); return 0; } @@ -551,7 +551,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, return ret; } - down(&dir_f->sem); + mutex_lock(&dir_f->sem); /* Build a deletion node */ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -574,21 +574,21 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, if (IS_ERR(fd)) { jffs2_complete_reservation(c); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); return PTR_ERR(fd); } /* File it. This will mark the old one obsolete. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); } else { - struct jffs2_full_dirent *fd = dir_f->dents; uint32_t nhash = full_name_hash(name, namelen); + fd = dir_f->dents; /* We don't actually want to reserve any space, but we do want to be holding the alloc_sem when we write to flash */ - down(&c->alloc_sem); - down(&dir_f->sem); + mutex_lock(&c->alloc_sem); + mutex_lock(&dir_f->sem); for (fd = dir_f->dents; fd; fd = fd->next) { if (fd->nhash == nhash && @@ -607,7 +607,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, break; } } - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); } /* dead_f is NULL if this was a rename not a real unlink */ @@ -615,7 +615,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, pointing to an inode which didn't exist. */ if (dead_f && dead_f->inocache) { - down(&dead_f->sem); + mutex_lock(&dead_f->sem); if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) { while (dead_f->dents) { @@ -639,7 +639,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, dead_f->inocache->nlink--; /* NB: Caller must set inode nlink if appropriate */ - up(&dead_f->sem); + mutex_unlock(&dead_f->sem); } jffs2_complete_reservation(c); @@ -666,7 +666,7 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint return ret; } - down(&dir_f->sem); + mutex_lock(&dir_f->sem); /* Build a deletion node */ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -691,7 +691,7 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint if (IS_ERR(fd)) { jffs2_complete_reservation(c); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); return PTR_ERR(fd); } @@ -699,7 +699,7 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint jffs2_add_fd_to_list(c, fd, &dir_f->dents); jffs2_complete_reservation(c); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); return 0; } diff --git a/include/asm-arm/arch-pxa/pxa3xx_nand.h b/include/asm-arm/arch-pxa/pxa3xx_nand.h new file mode 100644 index 000000000000..81a8937486cb --- /dev/null +++ b/include/asm-arm/arch-pxa/pxa3xx_nand.h @@ -0,0 +1,18 @@ +#ifndef __ASM_ARCH_PXA3XX_NAND_H +#define __ASM_ARCH_PXA3XX_NAND_H + +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> + +struct pxa3xx_nand_platform_data { + + /* the data flash bus is shared between the Static Memory + * Controller and the Data Flash Controller, the arbiter + * controls the ownership of the bus + */ + int enable_arbiter; + + struct mtd_partition *parts; + unsigned int nr_parts; +}; +#endif /* __ASM_ARCH_PXA3XX_NAND_H */ diff --git a/include/asm-arm/plat-s3c/nand.h b/include/asm-arm/plat-s3c/nand.h index 8816f7f9cee1..ad6bbe90616e 100644 --- a/include/asm-arm/plat-s3c/nand.h +++ b/include/asm-arm/plat-s3c/nand.h @@ -22,11 +22,14 @@ */ struct s3c2410_nand_set { + unsigned int disable_ecc : 1; + int nr_chips; int nr_partitions; char *name; int *nr_map; struct mtd_partition *partitions; + struct nand_ecclayout *ecc_layout; }; struct s3c2410_platform_nand { @@ -36,6 +39,8 @@ struct s3c2410_platform_nand { int twrph0; /* active time for nWE/nOE */ int twrph1; /* time for release CLE/ALE from nWE/nOE inactive */ + unsigned int ignore_unset_ecc : 1; + int nr_sets; struct s3c2410_nand_set *sets; diff --git a/include/linux/mtd/inftl.h b/include/linux/mtd/inftl.h index 6977780e548f..85fd041d44ad 100644 --- a/include/linux/mtd/inftl.h +++ b/include/linux/mtd/inftl.h @@ -57,6 +57,11 @@ extern char inftlmountrev[]; void INFTL_dumptables(struct INFTLrecord *s); void INFTL_dumpVUchains(struct INFTLrecord *s); +int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); +int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); + #endif /* __KERNEL__ */ #endif /* __MTD_INFTL_H__ */ diff --git a/include/linux/mtd/nftl.h b/include/linux/mtd/nftl.h index bcf2fb3fa4a7..001eec50cac6 100644 --- a/include/linux/mtd/nftl.h +++ b/include/linux/mtd/nftl.h @@ -43,6 +43,11 @@ struct NFTLrecord { int NFTL_mount(struct NFTLrecord *s); int NFTL_formatblock(struct NFTLrecord *s, int block); +int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); +int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); + #ifndef NFTL_MAJOR #define NFTL_MAJOR 93 #endif diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index fd0a260e070b..9aa2a9149b58 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -187,4 +187,7 @@ struct onenand_manufacturers { char *name; }; +int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops); + #endif /* __LINUX_MTD_ONENAND_H */ diff --git a/include/linux/mtd/plat-ram.h b/include/linux/mtd/plat-ram.h index 9667863bd7e3..0e37ad07bce2 100644 --- a/include/linux/mtd/plat-ram.h +++ b/include/linux/mtd/plat-ram.h @@ -21,8 +21,9 @@ #define PLATRAM_RW (1) struct platdata_mtd_ram { - char *mapname; - char **probes; + const char *mapname; + const char **map_probes; + const char **probes; struct mtd_partition *partitions; int nr_partitions; int bankwidth; diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild index 4d46b3bdebd8..8eb018f96002 100644 --- a/include/mtd/Kbuild +++ b/include/mtd/Kbuild @@ -3,5 +3,4 @@ header-y += jffs2-user.h header-y += mtd-abi.h header-y += mtd-user.h header-y += nftl-user.h -header-y += ubi-header.h header-y += ubi-user.h |