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
  ...
This commit is contained in:
Linus Torvalds 2008-04-25 12:25:48 -07:00
commit 7e97b28309
89 changed files with 3339 additions and 496 deletions

View file

@ -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.

View file

@ -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

View file

@ -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
------

View file

@ -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

View file

@ -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.

151
drivers/mtd/ar7part.c Normal file
View file

@ -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");

View file

@ -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

View file

@ -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();
/*

View file

@ -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;

View file

@ -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 */

View file

@ -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

View file

@ -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",
@ -1411,6 +1414,18 @@ static const struct amd_flash_info jedec_table[] = {
ERASEINFO(0x1000,256),
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,
@ -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",

View file

@ -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;
}

View file

@ -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

View file

@ -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);
}

View file

@ -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;

View file

@ -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, },

View file

@ -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;

View file

@ -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)

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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");

View file

@ -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 );
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);
/* 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;
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 */
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;
}

View file

@ -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");

View file

@ -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");

View file

@ -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");

View file

@ -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");

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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)
{

View file

@ -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");

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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");

View file

@ -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);

View file

@ -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

View file

@ -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;

291
drivers/mtd/nand/fsl_upm.c Normal file
View file

@ -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");

View file

@ -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) {

View file

@ -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");

View file

@ -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");

View file

@ -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");

File diff suppressed because it is too large Load diff

View file

@ -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);

View file

@ -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");

View file

@ -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[]

View file

@ -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");

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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"

View file

@ -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);

View file

@ -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

View file

@ -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");

View file

@ -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,

View file

@ -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
@ -91,27 +92,6 @@ static int add_to_list(struct ubi_scan_info *si, int pnum, int ec,
return 0;
}
/**
* 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.
@ -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");

View file

@ -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;
};

View file

@ -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__ */

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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 */

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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 */

View file

@ -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)
{

View file

@ -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)

View file

@ -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;

View file

@ -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 */

View file

@ -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

View file

@ -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)

View file

@ -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);
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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 */

View file

@ -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;

View file

@ -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__ */

View file

@ -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

View file

@ -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 */

View file

@ -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;

View file

@ -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