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:
commit
7e97b28309
|
@ -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.
|
|
@ -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
|
||||
|
|
@ -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
|
||||
------
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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");
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, },
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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[]
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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__ */
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
164
fs/jffs2/debug.c
164
fs/jffs2/debug.c
|
@ -62,9 +62,9 @@ __jffs2_dbg_acct_sanity_check(struct jffs2_sb_info *c,
|
|||
void
|
||||
__jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f)
|
||||
{
|
||||
down(&f->sem);
|
||||
mutex_lock(&f->sem);
|
||||
__jffs2_dbg_fragtree_paranoia_check_nolock(f);
|
||||
up(&f->sem);
|
||||
mutex_unlock(&f->sem);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -153,6 +153,139 @@ __jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c,
|
|||
kfree(buf);
|
||||
}
|
||||
|
||||
void __jffs2_dbg_superblock_counts(struct jffs2_sb_info *c)
|
||||
{
|
||||
struct jffs2_eraseblock *jeb;
|
||||
uint32_t free = 0, dirty = 0, used = 0, wasted = 0,
|
||||
erasing = 0, bad = 0, unchecked = 0;
|
||||
int nr_counted = 0;
|
||||
int dump = 0;
|
||||
|
||||
if (c->gcblock) {
|
||||
nr_counted++;
|
||||
free += c->gcblock->free_size;
|
||||
dirty += c->gcblock->dirty_size;
|
||||
used += c->gcblock->used_size;
|
||||
wasted += c->gcblock->wasted_size;
|
||||
unchecked += c->gcblock->unchecked_size;
|
||||
}
|
||||
if (c->nextblock) {
|
||||
nr_counted++;
|
||||
free += c->nextblock->free_size;
|
||||
dirty += c->nextblock->dirty_size;
|
||||
used += c->nextblock->used_size;
|
||||
wasted += c->nextblock->wasted_size;
|
||||
unchecked += c->nextblock->unchecked_size;
|
||||
}
|
||||
list_for_each_entry(jeb, &c->clean_list, list) {
|
||||
nr_counted++;
|
||||
free += jeb->free_size;
|
||||
dirty += jeb->dirty_size;
|
||||
used += jeb->used_size;
|
||||
wasted += jeb->wasted_size;
|
||||
unchecked += jeb->unchecked_size;
|
||||
}
|
||||
list_for_each_entry(jeb, &c->very_dirty_list, list) {
|
||||
nr_counted++;
|
||||
free += jeb->free_size;
|
||||
dirty += jeb->dirty_size;
|
||||
used += jeb->used_size;
|
||||
wasted += jeb->wasted_size;
|
||||
unchecked += jeb->unchecked_size;
|
||||
}
|
||||
list_for_each_entry(jeb, &c->dirty_list, list) {
|
||||
nr_counted++;
|
||||
free += jeb->free_size;
|
||||
dirty += jeb->dirty_size;
|
||||
used += jeb->used_size;
|
||||
wasted += jeb->wasted_size;
|
||||
unchecked += jeb->unchecked_size;
|
||||
}
|
||||
list_for_each_entry(jeb, &c->erasable_list, list) {
|
||||
nr_counted++;
|
||||
free += jeb->free_size;
|
||||
dirty += jeb->dirty_size;
|
||||
used += jeb->used_size;
|
||||
wasted += jeb->wasted_size;
|
||||
unchecked += jeb->unchecked_size;
|
||||
}
|
||||
list_for_each_entry(jeb, &c->erasable_pending_wbuf_list, list) {
|
||||
nr_counted++;
|
||||
free += jeb->free_size;
|
||||
dirty += jeb->dirty_size;
|
||||
used += jeb->used_size;
|
||||
wasted += jeb->wasted_size;
|
||||
unchecked += jeb->unchecked_size;
|
||||
}
|
||||
list_for_each_entry(jeb, &c->erase_pending_list, list) {
|
||||
nr_counted++;
|
||||
free += jeb->free_size;
|
||||
dirty += jeb->dirty_size;
|
||||
used += jeb->used_size;
|
||||
wasted += jeb->wasted_size;
|
||||
unchecked += jeb->unchecked_size;
|
||||
}
|
||||
list_for_each_entry(jeb, &c->free_list, list) {
|
||||
nr_counted++;
|
||||
free += jeb->free_size;
|
||||
dirty += jeb->dirty_size;
|
||||
used += jeb->used_size;
|
||||
wasted += jeb->wasted_size;
|
||||
unchecked += jeb->unchecked_size;
|
||||
}
|
||||
list_for_each_entry(jeb, &c->bad_used_list, list) {
|
||||
nr_counted++;
|
||||
free += jeb->free_size;
|
||||
dirty += jeb->dirty_size;
|
||||
used += jeb->used_size;
|
||||
wasted += jeb->wasted_size;
|
||||
unchecked += jeb->unchecked_size;
|
||||
}
|
||||
|
||||
list_for_each_entry(jeb, &c->erasing_list, list) {
|
||||
nr_counted++;
|
||||
erasing += c->sector_size;
|
||||
}
|
||||
list_for_each_entry(jeb, &c->erase_checking_list, list) {
|
||||
nr_counted++;
|
||||
erasing += c->sector_size;
|
||||
}
|
||||
list_for_each_entry(jeb, &c->erase_complete_list, list) {
|
||||
nr_counted++;
|
||||
erasing += c->sector_size;
|
||||
}
|
||||
list_for_each_entry(jeb, &c->bad_list, list) {
|
||||
nr_counted++;
|
||||
bad += c->sector_size;
|
||||
}
|
||||
|
||||
#define check(sz) \
|
||||
if (sz != c->sz##_size) { \
|
||||
printk(KERN_WARNING #sz "_size mismatch counted 0x%x, c->" #sz "_size 0x%x\n", \
|
||||
sz, c->sz##_size); \
|
||||
dump = 1; \
|
||||
}
|
||||
check(free);
|
||||
check(dirty);
|
||||
check(used);
|
||||
check(wasted);
|
||||
check(unchecked);
|
||||
check(bad);
|
||||
check(erasing);
|
||||
#undef check
|
||||
|
||||
if (nr_counted != c->nr_blocks) {
|
||||
printk(KERN_WARNING "%s counted only 0x%x blocks of 0x%x. Where are the others?\n",
|
||||
__func__, nr_counted, c->nr_blocks);
|
||||
dump = 1;
|
||||
}
|
||||
|
||||
if (dump) {
|
||||
__jffs2_dbg_dump_block_lists_nolock(c);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the space accounting and node_ref list correctness for the JFFS2 erasable block 'jeb'.
|
||||
*/
|
||||
|
@ -229,6 +362,9 @@ __jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c,
|
|||
}
|
||||
#endif
|
||||
|
||||
if (!(c->flags & (JFFS2_SB_FLAG_BUILDING|JFFS2_SB_FLAG_SCANNING)))
|
||||
__jffs2_dbg_superblock_counts(c);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
|
@ -268,7 +404,10 @@ __jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c,
|
|||
|
||||
printk(JFFS2_DBG);
|
||||
for (ref = jeb->first_node; ; ref = ref_next(ref)) {
|
||||
printk("%#08x(%#x)", ref_offset(ref), ref->__totlen);
|
||||
printk("%#08x", ref_offset(ref));
|
||||
#ifdef TEST_TOTLEN
|
||||
printk("(%x)", ref->__totlen);
|
||||
#endif
|
||||
if (ref_next(ref))
|
||||
printk("->");
|
||||
else
|
||||
|
@ -447,6 +586,21 @@ __jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c)
|
|||
}
|
||||
}
|
||||
}
|
||||
if (list_empty(&c->erase_checking_list)) {
|
||||
printk(JFFS2_DBG "erase_checking_list: empty\n");
|
||||
} else {
|
||||
struct list_head *this;
|
||||
|
||||
list_for_each(this, &c->erase_checking_list) {
|
||||
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
|
||||
|
||||
if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
|
||||
printk(JFFS2_DBG "erase_checking_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n",
|
||||
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size,
|
||||
jeb->unchecked_size, jeb->free_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (list_empty(&c->erase_pending_list)) {
|
||||
printk(JFFS2_DBG "erase_pending_list: empty\n");
|
||||
|
@ -532,9 +686,9 @@ __jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c)
|
|||
void
|
||||
__jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f)
|
||||
{
|
||||
down(&f->sem);
|
||||
mutex_lock(&f->sem);
|
||||
jffs2_dbg_dump_fragtree_nolock(f);
|
||||
up(&f->sem);
|
||||
mutex_unlock(&f->sem);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue