Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (63 commits) mtd: OneNAND: Allow setting of boundary information when built as module jffs2: leaking jffs2_summary in function jffs2_scan_medium mtd: nand: Fix memory leak on txx9ndfmc probe failure. mtd: orion_nand: use burst reads with double word accesses mtd/nand: s3c6400 support for s3c2410 driver [MTD] [NAND] S3C2410: Use DIV_ROUND_UP [MTD] [NAND] S3C2410: Deal with unaligned lengths in S3C2440 buffer read/write [MTD] [NAND] S3C2410: Allow the machine code to get the BBT table from NAND [MTD] [NAND] S3C2410: Added a kerneldoc for s3c2410_nand_set mtd: physmap_of: Add multiple regions and concatenation support mtd: nand: max_retries off by one in mxc_nand mtd: nand: s3c2410_nand_setrate(): use correct macros for 2412/2440 mtd: onenand: add bbt_wait & unlock_all as replaceable for some platform mtd: Flex-OneNAND support mtd: nand: add OMAP2/OMAP3 NAND driver mtd: maps: Blackfin async: fix memory leaks in probe/remove funcs mtd: uclinux: mark local stuff static mtd: uclinux: do not allow to be built as a module mtd: uclinux: allow systems to override map addr/size mtd: blackfin NFC: fix hang when using NAND on BF527-EZKITs ...
This commit is contained in:
Коммит
ac1b7c378e
|
@ -0,0 +1,125 @@
|
|||
What: /sys/class/mtd/
|
||||
Date: April 2009
|
||||
KernelVersion: 2.6.29
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
The mtd/ class subdirectory belongs to the MTD subsystem
|
||||
(MTD core).
|
||||
|
||||
What: /sys/class/mtd/mtdX/
|
||||
Date: April 2009
|
||||
KernelVersion: 2.6.29
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
The /sys/class/mtd/mtd{0,1,2,3,...} directories correspond
|
||||
to each /dev/mtdX character device. These may represent
|
||||
physical/simulated flash devices, partitions on a flash
|
||||
device, or concatenated flash devices. They exist regardless
|
||||
of whether CONFIG_MTD_CHAR is actually enabled.
|
||||
|
||||
What: /sys/class/mtd/mtdXro/
|
||||
Date: April 2009
|
||||
KernelVersion: 2.6.29
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
These directories provide the corresponding read-only device
|
||||
nodes for /sys/class/mtd/mtdX/ . They are only created
|
||||
(for the benefit of udev) if CONFIG_MTD_CHAR is enabled.
|
||||
|
||||
What: /sys/class/mtd/mtdX/dev
|
||||
Date: April 2009
|
||||
KernelVersion: 2.6.29
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
Major and minor numbers of the character device corresponding
|
||||
to this MTD device (in <major>:<minor> format). This is the
|
||||
read-write device so <minor> will be even.
|
||||
|
||||
What: /sys/class/mtd/mtdXro/dev
|
||||
Date: April 2009
|
||||
KernelVersion: 2.6.29
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
Major and minor numbers of the character device corresponding
|
||||
to the read-only variant of thie MTD device (in
|
||||
<major>:<minor> format). In this case <minor> will be odd.
|
||||
|
||||
What: /sys/class/mtd/mtdX/erasesize
|
||||
Date: April 2009
|
||||
KernelVersion: 2.6.29
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
"Major" erase size for the device. If numeraseregions is
|
||||
zero, this is the eraseblock size for the entire device.
|
||||
Otherwise, the MEMGETREGIONCOUNT/MEMGETREGIONINFO ioctls
|
||||
can be used to determine the actual eraseblock layout.
|
||||
|
||||
What: /sys/class/mtd/mtdX/flags
|
||||
Date: April 2009
|
||||
KernelVersion: 2.6.29
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
A hexadecimal value representing the device flags, ORed
|
||||
together:
|
||||
|
||||
0x0400: MTD_WRITEABLE - device is writable
|
||||
0x0800: MTD_BIT_WRITEABLE - single bits can be flipped
|
||||
0x1000: MTD_NO_ERASE - no erase necessary
|
||||
0x2000: MTD_POWERUP_LOCK - always locked after reset
|
||||
|
||||
What: /sys/class/mtd/mtdX/name
|
||||
Date: April 2009
|
||||
KernelVersion: 2.6.29
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
A human-readable ASCII name for the device or partition.
|
||||
This will match the name in /proc/mtd .
|
||||
|
||||
What: /sys/class/mtd/mtdX/numeraseregions
|
||||
Date: April 2009
|
||||
KernelVersion: 2.6.29
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
For devices that have variable eraseblock sizes, this
|
||||
provides the total number of erase regions. Otherwise,
|
||||
it will read back as zero.
|
||||
|
||||
What: /sys/class/mtd/mtdX/oobsize
|
||||
Date: April 2009
|
||||
KernelVersion: 2.6.29
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
Number of OOB bytes per page.
|
||||
|
||||
What: /sys/class/mtd/mtdX/size
|
||||
Date: April 2009
|
||||
KernelVersion: 2.6.29
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
Total size of the device/partition, in bytes.
|
||||
|
||||
What: /sys/class/mtd/mtdX/type
|
||||
Date: April 2009
|
||||
KernelVersion: 2.6.29
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
One of the following ASCII strings, representing the device
|
||||
type:
|
||||
|
||||
absent, ram, rom, nor, nand, dataflash, ubi, unknown
|
||||
|
||||
What: /sys/class/mtd/mtdX/writesize
|
||||
Date: April 2009
|
||||
KernelVersion: 2.6.29
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
Minimal writable flash unit size. This will always be
|
||||
a positive integer.
|
||||
|
||||
In the case of NOR flash it is 1 (even though individual
|
||||
bits can be cleared).
|
||||
|
||||
In the case of NAND flash it is one NAND page (or a
|
||||
half page, or a quarter page).
|
||||
|
||||
In the case of ECC NOR, it is the ECC block size.
|
|
@ -1431,6 +1431,16 @@ and is between 256 and 4096 characters. It is defined in the file
|
|||
mtdparts= [MTD]
|
||||
See drivers/mtd/cmdlinepart.c.
|
||||
|
||||
onenand.bdry= [HW,MTD] Flex-OneNAND Boundary Configuration
|
||||
|
||||
Format: [die0_boundary][,die0_lock][,die1_boundary][,die1_lock]
|
||||
|
||||
boundary - index of last SLC block on Flex-OneNAND.
|
||||
The remaining blocks are configured as MLC blocks.
|
||||
lock - Configure if Flex-OneNAND boundary should be locked.
|
||||
Once locked, the boundary cannot be changed.
|
||||
1 indicates lock status, 0 indicates unlock status.
|
||||
|
||||
mtdset= [ARM]
|
||||
ARM/S3C2412 JIVE boot control
|
||||
|
||||
|
|
|
@ -3252,7 +3252,6 @@ W: http://www.linux-mtd.infradead.org/doc/jffs2.html
|
|||
S: Maintained
|
||||
F: fs/jffs2/
|
||||
F: include/linux/jffs2.h
|
||||
F: include/mtd/jffs2-user.h
|
||||
|
||||
JOURNALLING LAYER FOR BLOCK DEVICES (JBD)
|
||||
P: Stephen Tweedie
|
||||
|
|
|
@ -68,10 +68,14 @@ struct davinci_nand_pdata { /* platform_data */
|
|||
|
||||
/* none == NAND_ECC_NONE (strongly *not* advised!!)
|
||||
* soft == NAND_ECC_SOFT
|
||||
* 1-bit == NAND_ECC_HW
|
||||
* 4-bit == NAND_ECC_HW_SYNDROME (not on all chips)
|
||||
* else == NAND_ECC_HW, according to ecc_bits
|
||||
*
|
||||
* All DaVinci-family chips support 1-bit hardware ECC.
|
||||
* Newer ones also support 4-bit ECC, but are awkward
|
||||
* using it with large page chips.
|
||||
*/
|
||||
nand_ecc_modes_t ecc_mode;
|
||||
u8 ecc_bits;
|
||||
|
||||
/* e.g. NAND_BUSWIDTH_16 or NAND_USE_FLASH_BBT */
|
||||
unsigned options;
|
||||
|
|
|
@ -10,19 +10,26 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/* struct s3c2410_nand_set
|
||||
/**
|
||||
* struct s3c2410_nand_set - define a set of one or more nand chips
|
||||
* @disable_ecc: Entirely disable ECC - Dangerous
|
||||
* @flash_bbt: Openmoko u-boot can create a Bad Block Table
|
||||
* Setting this flag will allow the kernel to
|
||||
* look for it at boot time and also skip the NAND
|
||||
* scan.
|
||||
* @nr_chips: Number of chips in this set
|
||||
* @nr_partitions: Number of partitions pointed to by @partitions
|
||||
* @name: Name of set (optional)
|
||||
* @nr_map: Map for low-layer logical to physical chip numbers (option)
|
||||
* @partitions: The mtd partition list
|
||||
*
|
||||
* define an set of one or more nand chips registered with an unique mtd
|
||||
*
|
||||
* nr_chips = number of chips in this set
|
||||
* nr_partitions = number of partitions pointed to be partitoons (or zero)
|
||||
* name = name of set (optional)
|
||||
* nr_map = map for low-layer logical to physical chip numbers (option)
|
||||
* partitions = mtd partition list
|
||||
*/
|
||||
|
||||
* define a set of one or more nand chips registered with an unique mtd. Also
|
||||
* allows to pass flag to the underlying NAND layer. 'disable_ecc' will trigger
|
||||
* a warning at boot time.
|
||||
*/
|
||||
struct s3c2410_nand_set {
|
||||
unsigned int disable_ecc : 1;
|
||||
unsigned int disable_ecc:1;
|
||||
unsigned int flash_bbt:1;
|
||||
|
||||
int nr_chips;
|
||||
int nr_partitions;
|
||||
|
@ -39,7 +46,7 @@ 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;
|
||||
unsigned int ignore_unset_ecc:1;
|
||||
|
||||
int nr_sets;
|
||||
struct s3c2410_nand_set *sets;
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#define MANUFACTURER_INTEL 0x0089
|
||||
#define I82802AB 0x00ad
|
||||
#define I82802AC 0x00ac
|
||||
#define PF38F4476 0x881c
|
||||
#define MANUFACTURER_ST 0x0020
|
||||
#define M50LPW080 0x002F
|
||||
#define M50FLW080A 0x0080
|
||||
|
@ -315,10 +316,20 @@ static struct cfi_fixup fixup_table[] = {
|
|||
{ 0, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
static void cfi_fixup_major_minor(struct cfi_private *cfi,
|
||||
struct cfi_pri_intelext *extp)
|
||||
{
|
||||
if (cfi->mfr == MANUFACTURER_INTEL &&
|
||||
cfi->id == PF38F4476 && extp->MinorVersion == '3')
|
||||
extp->MinorVersion = '1';
|
||||
}
|
||||
|
||||
static inline struct cfi_pri_intelext *
|
||||
read_pri_intelext(struct map_info *map, __u16 adr)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
struct cfi_pri_intelext *extp;
|
||||
unsigned int extra_size = 0;
|
||||
unsigned int extp_size = sizeof(*extp);
|
||||
|
||||
again:
|
||||
|
@ -326,6 +337,8 @@ read_pri_intelext(struct map_info *map, __u16 adr)
|
|||
if (!extp)
|
||||
return NULL;
|
||||
|
||||
cfi_fixup_major_minor(cfi, extp);
|
||||
|
||||
if (extp->MajorVersion != '1' ||
|
||||
(extp->MinorVersion < '0' || extp->MinorVersion > '5')) {
|
||||
printk(KERN_ERR " Unknown Intel/Sharp Extended Query "
|
||||
|
@ -340,19 +353,24 @@ read_pri_intelext(struct map_info *map, __u16 adr)
|
|||
extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask);
|
||||
extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr);
|
||||
|
||||
if (extp->MajorVersion == '1' && extp->MinorVersion >= '3') {
|
||||
unsigned int extra_size = 0;
|
||||
int nb_parts, i;
|
||||
if (extp->MinorVersion >= '0') {
|
||||
extra_size = 0;
|
||||
|
||||
/* Protection Register info */
|
||||
extra_size += (extp->NumProtectionFields - 1) *
|
||||
sizeof(struct cfi_intelext_otpinfo);
|
||||
}
|
||||
|
||||
if (extp->MinorVersion >= '1') {
|
||||
/* Burst Read info */
|
||||
extra_size += 2;
|
||||
if (extp_size < sizeof(*extp) + extra_size)
|
||||
goto need_more;
|
||||
extra_size += extp->extra[extra_size-1];
|
||||
extra_size += extp->extra[extra_size - 1];
|
||||
}
|
||||
|
||||
if (extp->MinorVersion >= '3') {
|
||||
int nb_parts, i;
|
||||
|
||||
/* Number of hardware-partitions */
|
||||
extra_size += 1;
|
||||
|
|
|
@ -166,6 +166,7 @@
|
|||
#define SST39LF040 0x00D7
|
||||
#define SST39SF010A 0x00B5
|
||||
#define SST39SF020A 0x00B6
|
||||
#define SST39SF040 0x00B7
|
||||
#define SST49LF004B 0x0060
|
||||
#define SST49LF040B 0x0050
|
||||
#define SST49LF008A 0x005a
|
||||
|
@ -1391,6 +1392,18 @@ static const struct amd_flash_info jedec_table[] = {
|
|||
.regions = {
|
||||
ERASEINFO(0x01000,64),
|
||||
}
|
||||
}, {
|
||||
.mfr_id = MANUFACTURER_SST,
|
||||
.dev_id = SST39SF040,
|
||||
.name = "SST 39SF040",
|
||||
.devtypes = CFI_DEVICETYPE_X8,
|
||||
.uaddr = MTD_UADDR_0x5555_0x2AAA,
|
||||
.dev_size = SIZE_512KiB,
|
||||
.cmd_set = P_ID_AMD_STD,
|
||||
.nr_regions = 1,
|
||||
.regions = {
|
||||
ERASEINFO(0x01000,128),
|
||||
}
|
||||
}, {
|
||||
.mfr_id = MANUFACTURER_SST,
|
||||
.dev_id = SST49LF040B,
|
||||
|
|
|
@ -500,6 +500,9 @@ static struct flash_info __devinitdata m25p_data [] = {
|
|||
{ "at26df161a", 0x1f4601, 0, 64 * 1024, 32, SECT_4K, },
|
||||
{ "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, },
|
||||
|
||||
/* Macronix */
|
||||
{ "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, },
|
||||
|
||||
/* Spansion -- single (large) sector size only, at least
|
||||
* for the chips listed here (without boot sectors).
|
||||
*/
|
||||
|
@ -528,6 +531,7 @@ static struct flash_info __devinitdata m25p_data [] = {
|
|||
{ "m25p64", 0x202017, 0, 64 * 1024, 128, },
|
||||
{ "m25p128", 0x202018, 0, 256 * 1024, 64, },
|
||||
|
||||
{ "m45pe10", 0x204011, 0, 64 * 1024, 2, },
|
||||
{ "m45pe80", 0x204014, 0, 64 * 1024, 16, },
|
||||
{ "m45pe16", 0x204015, 0, 64 * 1024, 32, },
|
||||
|
||||
|
|
|
@ -105,15 +105,6 @@ config MSP_FLASH_MAP_LIMIT
|
|||
default "0x02000000"
|
||||
depends on MSP_FLASH_MAP_LIMIT_32M
|
||||
|
||||
config MTD_PMC_MSP_RAMROOT
|
||||
tristate "Embedded RAM block device for root on PMC-Sierra MSP"
|
||||
depends on PMC_MSP_EMBEDDED_ROOTFS && \
|
||||
(MTD_BLOCK || MTD_BLOCK_RO) && \
|
||||
MTD_RAM
|
||||
help
|
||||
This provides support for the embedded root file system
|
||||
on PMC MSP devices. This memory is mapped as a MTD block device.
|
||||
|
||||
config MTD_SUN_UFLASH
|
||||
tristate "Sun Microsystems userflash support"
|
||||
depends on SPARC && MTD_CFI && PCI
|
||||
|
@ -270,7 +261,7 @@ config MTD_ALCHEMY
|
|||
|
||||
config MTD_DILNETPC
|
||||
tristate "CFI Flash device mapped on DIL/Net PC"
|
||||
depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT
|
||||
depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN
|
||||
help
|
||||
MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP".
|
||||
For details, see <http://www.ssv-embedded.de/ssv/pc104/p169.htm>
|
||||
|
@ -501,7 +492,7 @@ config MTD_BFIN_ASYNC
|
|||
If compiled as a module, it will be called bfin-async-flash.
|
||||
|
||||
config MTD_UCLINUX
|
||||
tristate "Generic uClinux RAM/ROM filesystem support"
|
||||
bool "Generic uClinux RAM/ROM filesystem support"
|
||||
depends on MTD_PARTITIONS && MTD_RAM && !MMU
|
||||
help
|
||||
Map driver to support image based filesystems for uClinux.
|
||||
|
|
|
@ -25,7 +25,6 @@ obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
|
|||
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
|
||||
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
|
||||
obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o
|
||||
obj-$(CONFIG_MTD_PMC_MSP_RAMROOT)+= pmcmsp-ramroot.o
|
||||
obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o
|
||||
obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o
|
||||
obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o
|
||||
|
|
|
@ -40,6 +40,9 @@ struct async_state {
|
|||
uint32_t flash_ambctl0, flash_ambctl1;
|
||||
uint32_t save_ambctl0, save_ambctl1;
|
||||
unsigned long irq_flags;
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
struct mtd_partition *parts;
|
||||
#endif
|
||||
};
|
||||
|
||||
static void switch_to_flash(struct async_state *state)
|
||||
|
@ -170,6 +173,7 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev)
|
|||
if (ret > 0) {
|
||||
pr_devinit(KERN_NOTICE DRIVER_NAME ": Using commandline partition definition\n");
|
||||
add_mtd_partitions(state->mtd, pdata->parts, ret);
|
||||
state->parts = pdata->parts;
|
||||
|
||||
} else if (pdata->nr_parts) {
|
||||
pr_devinit(KERN_NOTICE DRIVER_NAME ": Using board partition definition\n");
|
||||
|
@ -193,6 +197,7 @@ static int __devexit bfin_flash_remove(struct platform_device *pdev)
|
|||
gpio_free(state->enet_flash_pin);
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
del_mtd_partitions(state->mtd);
|
||||
kfree(state->parts);
|
||||
#endif
|
||||
map_destroy(state->mtd);
|
||||
kfree(state);
|
||||
|
|
|
@ -36,27 +36,33 @@
|
|||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/concat.h>
|
||||
|
||||
#include <asm/mach/flash.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#ifdef CONFIG_ARCH_P720T
|
||||
#define FLASH_BASE (0x04000000)
|
||||
#define FLASH_SIZE (64*1024*1024)
|
||||
#endif
|
||||
#define SUBDEV_NAME_SIZE (BUS_ID_SIZE + 2)
|
||||
|
||||
struct armflash_subdev_info {
|
||||
char name[SUBDEV_NAME_SIZE];
|
||||
struct mtd_info *mtd;
|
||||
struct map_info map;
|
||||
struct flash_platform_data *plat;
|
||||
};
|
||||
|
||||
struct armflash_info {
|
||||
struct flash_platform_data *plat;
|
||||
struct resource *res;
|
||||
struct mtd_partition *parts;
|
||||
struct mtd_info *mtd;
|
||||
struct map_info map;
|
||||
int nr_subdev;
|
||||
struct armflash_subdev_info subdev[0];
|
||||
};
|
||||
|
||||
static void armflash_set_vpp(struct map_info *map, int on)
|
||||
{
|
||||
struct armflash_info *info = container_of(map, struct armflash_info, map);
|
||||
struct armflash_subdev_info *info =
|
||||
container_of(map, struct armflash_subdev_info, map);
|
||||
|
||||
if (info->plat && info->plat->set_vpp)
|
||||
info->plat->set_vpp(on);
|
||||
|
@ -64,32 +70,17 @@ static void armflash_set_vpp(struct map_info *map, int on)
|
|||
|
||||
static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL };
|
||||
|
||||
static int armflash_probe(struct platform_device *dev)
|
||||
static int armflash_subdev_probe(struct armflash_subdev_info *subdev,
|
||||
struct resource *res)
|
||||
{
|
||||
struct flash_platform_data *plat = dev->dev.platform_data;
|
||||
struct resource *res = dev->resource;
|
||||
unsigned int size = res->end - res->start + 1;
|
||||
struct armflash_info *info;
|
||||
int err;
|
||||
struct flash_platform_data *plat = subdev->plat;
|
||||
resource_size_t size = res->end - res->start + 1;
|
||||
void __iomem *base;
|
||||
int err = 0;
|
||||
|
||||
info = kzalloc(sizeof(struct armflash_info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
info->plat = plat;
|
||||
if (plat && plat->init) {
|
||||
err = plat->init();
|
||||
if (err)
|
||||
goto no_resource;
|
||||
}
|
||||
|
||||
info->res = request_mem_region(res->start, size, "armflash");
|
||||
if (!info->res) {
|
||||
if (!request_mem_region(res->start, size, subdev->name)) {
|
||||
err = -EBUSY;
|
||||
goto no_resource;
|
||||
goto out;
|
||||
}
|
||||
|
||||
base = ioremap(res->start, size);
|
||||
|
@ -101,27 +92,132 @@ static int armflash_probe(struct platform_device *dev)
|
|||
/*
|
||||
* look for CFI based flash parts fitted to this board
|
||||
*/
|
||||
info->map.size = size;
|
||||
info->map.bankwidth = plat->width;
|
||||
info->map.phys = res->start;
|
||||
info->map.virt = base;
|
||||
info->map.name = dev_name(&dev->dev);
|
||||
info->map.set_vpp = armflash_set_vpp;
|
||||
subdev->map.size = size;
|
||||
subdev->map.bankwidth = plat->width;
|
||||
subdev->map.phys = res->start;
|
||||
subdev->map.virt = base;
|
||||
subdev->map.name = subdev->name;
|
||||
subdev->map.set_vpp = armflash_set_vpp;
|
||||
|
||||
simple_map_init(&info->map);
|
||||
simple_map_init(&subdev->map);
|
||||
|
||||
/*
|
||||
* Also, the CFI layer automatically works out what size
|
||||
* of chips we have, and does the necessary identification
|
||||
* for us automatically.
|
||||
*/
|
||||
info->mtd = do_map_probe(plat->map_name, &info->map);
|
||||
if (!info->mtd) {
|
||||
subdev->mtd = do_map_probe(plat->map_name, &subdev->map);
|
||||
if (!subdev->mtd) {
|
||||
err = -ENXIO;
|
||||
goto no_device;
|
||||
}
|
||||
|
||||
info->mtd->owner = THIS_MODULE;
|
||||
subdev->mtd->owner = THIS_MODULE;
|
||||
|
||||
/* Successful? */
|
||||
if (err == 0)
|
||||
return err;
|
||||
|
||||
if (subdev->mtd)
|
||||
map_destroy(subdev->mtd);
|
||||
no_device:
|
||||
iounmap(base);
|
||||
no_mem:
|
||||
release_mem_region(res->start, size);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void armflash_subdev_remove(struct armflash_subdev_info *subdev)
|
||||
{
|
||||
if (subdev->mtd)
|
||||
map_destroy(subdev->mtd);
|
||||
if (subdev->map.virt)
|
||||
iounmap(subdev->map.virt);
|
||||
release_mem_region(subdev->map.phys, subdev->map.size);
|
||||
}
|
||||
|
||||
static int armflash_probe(struct platform_device *dev)
|
||||
{
|
||||
struct flash_platform_data *plat = dev->dev.platform_data;
|
||||
unsigned int size;
|
||||
struct armflash_info *info;
|
||||
int i, nr, err;
|
||||
|
||||
/* Count the number of devices */
|
||||
for (nr = 0; ; nr++)
|
||||
if (!platform_get_resource(dev, IORESOURCE_MEM, nr))
|
||||
break;
|
||||
if (nr == 0) {
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size = sizeof(struct armflash_info) +
|
||||
sizeof(struct armflash_subdev_info) * nr;
|
||||
info = kzalloc(size, GFP_KERNEL);
|
||||
if (!info) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (plat && plat->init) {
|
||||
err = plat->init();
|
||||
if (err)
|
||||
goto no_resource;
|
||||
}
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
struct armflash_subdev_info *subdev = &info->subdev[i];
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(dev, IORESOURCE_MEM, i);
|
||||
if (!res)
|
||||
break;
|
||||
|
||||
if (nr == 1)
|
||||
/* No MTD concatenation, just use the default name */
|
||||
snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s",
|
||||
dev_name(&dev->dev));
|
||||
else
|
||||
snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s-%d",
|
||||
dev_name(&dev->dev), i);
|
||||
subdev->plat = plat;
|
||||
|
||||
err = armflash_subdev_probe(subdev, res);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
info->nr_subdev = i;
|
||||
|
||||
if (err)
|
||||
goto subdev_err;
|
||||
|
||||
if (info->nr_subdev == 1)
|
||||
info->mtd = info->subdev[0].mtd;
|
||||
else if (info->nr_subdev > 1) {
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
struct mtd_info *cdev[info->nr_subdev];
|
||||
|
||||
/*
|
||||
* We detected multiple devices. Concatenate them together.
|
||||
*/
|
||||
for (i = 0; i < info->nr_subdev; i++)
|
||||
cdev[i] = info->subdev[i].mtd;
|
||||
|
||||
info->mtd = mtd_concat_create(cdev, info->nr_subdev,
|
||||
dev_name(&dev->dev));
|
||||
if (info->mtd == NULL)
|
||||
err = -ENXIO;
|
||||
#else
|
||||
printk(KERN_ERR "armflash: multiple devices found but "
|
||||
"MTD concat support disabled.\n");
|
||||
err = -ENXIO;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (err < 0)
|
||||
goto cleanup;
|
||||
|
||||
err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0);
|
||||
if (err > 0) {
|
||||
|
@ -131,28 +227,30 @@ static int armflash_probe(struct platform_device *dev)
|
|||
"mtd partition registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
if (err == 0)
|
||||
if (err == 0) {
|
||||
platform_set_drvdata(dev, info);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we got an error, free all resources.
|
||||
* We got an error, free all resources.
|
||||
*/
|
||||
if (err < 0) {
|
||||
if (info->mtd) {
|
||||
del_mtd_partitions(info->mtd);
|
||||
map_destroy(info->mtd);
|
||||
}
|
||||
kfree(info->parts);
|
||||
|
||||
no_device:
|
||||
iounmap(base);
|
||||
no_mem:
|
||||
release_mem_region(res->start, size);
|
||||
no_resource:
|
||||
if (plat && plat->exit)
|
||||
plat->exit();
|
||||
kfree(info);
|
||||
cleanup:
|
||||
if (info->mtd) {
|
||||
del_mtd_partitions(info->mtd);
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
if (info->mtd != info->subdev[0].mtd)
|
||||
mtd_concat_destroy(info->mtd);
|
||||
#endif
|
||||
}
|
||||
kfree(info->parts);
|
||||
subdev_err:
|
||||
for (i = info->nr_subdev - 1; i >= 0; i--)
|
||||
armflash_subdev_remove(&info->subdev[i]);
|
||||
no_resource:
|
||||
if (plat && plat->exit)
|
||||
plat->exit();
|
||||
kfree(info);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
@ -160,22 +258,26 @@ static int armflash_probe(struct platform_device *dev)
|
|||
static int armflash_remove(struct platform_device *dev)
|
||||
{
|
||||
struct armflash_info *info = platform_get_drvdata(dev);
|
||||
struct flash_platform_data *plat = dev->dev.platform_data;
|
||||
int i;
|
||||
|
||||
platform_set_drvdata(dev, NULL);
|
||||
|
||||
if (info) {
|
||||
if (info->mtd) {
|
||||
del_mtd_partitions(info->mtd);
|
||||
map_destroy(info->mtd);
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
if (info->mtd != info->subdev[0].mtd)
|
||||
mtd_concat_destroy(info->mtd);
|
||||
#endif
|
||||
}
|
||||
kfree(info->parts);
|
||||
|
||||
iounmap(info->map.virt);
|
||||
release_resource(info->res);
|
||||
kfree(info->res);
|
||||
for (i = info->nr_subdev - 1; i >= 0; i--)
|
||||
armflash_subdev_remove(&info->subdev[i]);
|
||||
|
||||
if (info->plat && info->plat->exit)
|
||||
info->plat->exit();
|
||||
if (plat && plat->exit)
|
||||
plat->exit();
|
||||
|
||||
kfree(info);
|
||||
}
|
||||
|
|
|
@ -195,42 +195,6 @@ err_out:
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
struct physmap_flash_info *info = platform_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
|
||||
if (info->mtd[i]->suspend) {
|
||||
ret = info->mtd[i]->suspend(info->mtd[i]);
|
||||
if (ret)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
for (--i; i >= 0; --i)
|
||||
if (info->mtd[i]->suspend) {
|
||||
BUG_ON(!info->mtd[i]->resume);
|
||||
info->mtd[i]->resume(info->mtd[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int physmap_flash_resume(struct platform_device *dev)
|
||||
{
|
||||
struct physmap_flash_info *info = platform_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
|
||||
if (info->mtd[i]->resume)
|
||||
info->mtd[i]->resume(info->mtd[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void physmap_flash_shutdown(struct platform_device *dev)
|
||||
{
|
||||
struct physmap_flash_info *info = platform_get_drvdata(dev);
|
||||
|
@ -242,16 +206,12 @@ static void physmap_flash_shutdown(struct platform_device *dev)
|
|||
info->mtd[i]->resume(info->mtd[i]);
|
||||
}
|
||||
#else
|
||||
#define physmap_flash_suspend NULL
|
||||
#define physmap_flash_resume NULL
|
||||
#define physmap_flash_shutdown NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver physmap_flash_driver = {
|
||||
.probe = physmap_flash_probe,
|
||||
.remove = physmap_flash_remove,
|
||||
.suspend = physmap_flash_suspend,
|
||||
.resume = physmap_flash_resume,
|
||||
.shutdown = physmap_flash_shutdown,
|
||||
.driver = {
|
||||
.name = "physmap-flash",
|
||||
|
|
|
@ -20,16 +20,23 @@
|
|||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/concat.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
struct of_flash_list {
|
||||
struct mtd_info *mtd;
|
||||
struct map_info map;
|
||||
struct resource *res;
|
||||
};
|
||||
|
||||
struct of_flash {
|
||||
struct mtd_info *mtd;
|
||||
struct map_info map;
|
||||
struct resource *res;
|
||||
struct mtd_info *cmtd;
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
struct mtd_partition *parts;
|
||||
#endif
|
||||
int list_size; /* number of elements in of_flash_list */
|
||||
struct of_flash_list list[0];
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
|
@ -88,30 +95,44 @@ static int parse_obsolete_partitions(struct of_device *dev,
|
|||
static int of_flash_remove(struct of_device *dev)
|
||||
{
|
||||
struct of_flash *info;
|
||||
int i;
|
||||
|
||||
info = dev_get_drvdata(&dev->dev);
|
||||
if (!info)
|
||||
return 0;
|
||||
dev_set_drvdata(&dev->dev, NULL);
|
||||
|
||||
if (info->mtd) {
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
if (info->cmtd != info->list[0].mtd) {
|
||||
del_mtd_device(info->cmtd);
|
||||
mtd_concat_destroy(info->cmtd);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (info->cmtd) {
|
||||
if (OF_FLASH_PARTS(info)) {
|
||||
del_mtd_partitions(info->mtd);
|
||||
del_mtd_partitions(info->cmtd);
|
||||
kfree(OF_FLASH_PARTS(info));
|
||||
} else {
|
||||
del_mtd_device(info->mtd);
|
||||
del_mtd_device(info->cmtd);
|
||||
}
|
||||
map_destroy(info->mtd);
|
||||
}
|
||||
|
||||
if (info->map.virt)
|
||||
iounmap(info->map.virt);
|
||||
for (i = 0; i < info->list_size; i++) {
|
||||
if (info->list[i].mtd)
|
||||
map_destroy(info->list[i].mtd);
|
||||
|
||||
if (info->res) {
|
||||
release_resource(info->res);
|
||||
kfree(info->res);
|
||||
if (info->list[i].map.virt)
|
||||
iounmap(info->list[i].map.virt);
|
||||
|
||||
if (info->list[i].res) {
|
||||
release_resource(info->list[i].res);
|
||||
kfree(info->list[i].res);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -164,68 +185,130 @@ static int __devinit of_flash_probe(struct of_device *dev,
|
|||
const char *probe_type = match->data;
|
||||
const u32 *width;
|
||||
int err;
|
||||
int i;
|
||||
int count;
|
||||
const u32 *p;
|
||||
int reg_tuple_size;
|
||||
struct mtd_info **mtd_list = NULL;
|
||||
|
||||
err = -ENXIO;
|
||||
if (of_address_to_resource(dp, 0, &res)) {
|
||||
dev_err(&dev->dev, "Can't get IO address from device tree\n");
|
||||
reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
|
||||
|
||||
/*
|
||||
* Get number of "reg" tuples. Scan for MTD devices on area's
|
||||
* described by each "reg" region. This makes it possible (including
|
||||
* the concat support) to support the Intel P30 48F4400 chips which
|
||||
* consists internally of 2 non-identical NOR chips on one die.
|
||||
*/
|
||||
p = of_get_property(dp, "reg", &count);
|
||||
if (count % reg_tuple_size != 0) {
|
||||
dev_err(&dev->dev, "Malformed reg property on %s\n",
|
||||
dev->node->full_name);
|
||||
err = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
|
||||
(unsigned long long)res.start, (unsigned long long)res.end);
|
||||
count /= reg_tuple_size;
|
||||
|
||||
err = -ENOMEM;
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
info = kzalloc(sizeof(struct of_flash) +
|
||||
sizeof(struct of_flash_list) * count, GFP_KERNEL);
|
||||
if (!info)
|
||||
goto err_out;
|
||||
|
||||
mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL);
|
||||
if (!info)
|
||||
goto err_out;
|
||||
|
||||
dev_set_drvdata(&dev->dev, info);
|
||||
|
||||
err = -EBUSY;
|
||||
info->res = request_mem_region(res.start, res.end - res.start + 1,
|
||||
dev_name(&dev->dev));
|
||||
if (!info->res)
|
||||
goto err_out;
|
||||
for (i = 0; i < count; i++) {
|
||||
err = -ENXIO;
|
||||
if (of_address_to_resource(dp, i, &res)) {
|
||||
dev_err(&dev->dev, "Can't get IO address from device"
|
||||
" tree\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
err = -ENXIO;
|
||||
width = of_get_property(dp, "bank-width", NULL);
|
||||
if (!width) {
|
||||
dev_err(&dev->dev, "Can't get bank width from device tree\n");
|
||||
goto err_out;
|
||||
dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
|
||||
(unsigned long long)res.start,
|
||||
(unsigned long long)res.end);
|
||||
|
||||
err = -EBUSY;
|
||||
info->list[i].res = request_mem_region(res.start, res.end -
|
||||
res.start + 1,
|
||||
dev_name(&dev->dev));
|
||||
if (!info->list[i].res)
|
||||
goto err_out;
|
||||
|
||||
err = -ENXIO;
|
||||
width = of_get_property(dp, "bank-width", NULL);
|
||||
if (!width) {
|
||||
dev_err(&dev->dev, "Can't get bank width from device"
|
||||
" tree\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
info->list[i].map.name = dev_name(&dev->dev);
|
||||
info->list[i].map.phys = res.start;
|
||||
info->list[i].map.size = res.end - res.start + 1;
|
||||
info->list[i].map.bankwidth = *width;
|
||||
|
||||
err = -ENOMEM;
|
||||
info->list[i].map.virt = ioremap(info->list[i].map.phys,
|
||||
info->list[i].map.size);
|
||||
if (!info->list[i].map.virt) {
|
||||
dev_err(&dev->dev, "Failed to ioremap() flash"
|
||||
" region\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
simple_map_init(&info->list[i].map);
|
||||
|
||||
if (probe_type) {
|
||||
info->list[i].mtd = do_map_probe(probe_type,
|
||||
&info->list[i].map);
|
||||
} else {
|
||||
info->list[i].mtd = obsolete_probe(dev,
|
||||
&info->list[i].map);
|
||||
}
|
||||
mtd_list[i] = info->list[i].mtd;
|
||||
|
||||
err = -ENXIO;
|
||||
if (!info->list[i].mtd) {
|
||||
dev_err(&dev->dev, "do_map_probe() failed\n");
|
||||
goto err_out;
|
||||
} else {
|
||||
info->list_size++;
|
||||
}
|
||||
info->list[i].mtd->owner = THIS_MODULE;
|
||||
info->list[i].mtd->dev.parent = &dev->dev;
|
||||
}
|
||||
|
||||
info->map.name = dev_name(&dev->dev);
|
||||
info->map.phys = res.start;
|
||||
info->map.size = res.end - res.start + 1;
|
||||
info->map.bankwidth = *width;
|
||||
|
||||
err = -ENOMEM;
|
||||
info->map.virt = ioremap(info->map.phys, info->map.size);
|
||||
if (!info->map.virt) {
|
||||
dev_err(&dev->dev, "Failed to ioremap() flash region\n");
|
||||
goto err_out;
|
||||
err = 0;
|
||||
if (info->list_size == 1) {
|
||||
info->cmtd = info->list[0].mtd;
|
||||
} else if (info->list_size > 1) {
|
||||
/*
|
||||
* We detected multiple devices. Concatenate them together.
|
||||
*/
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
info->cmtd = mtd_concat_create(mtd_list, info->list_size,
|
||||
dev_name(&dev->dev));
|
||||
if (info->cmtd == NULL)
|
||||
err = -ENXIO;
|
||||
#else
|
||||
printk(KERN_ERR "physmap_of: multiple devices "
|
||||
"found but MTD concat support disabled.\n");
|
||||
err = -ENXIO;
|
||||
#endif
|
||||
}
|
||||
|
||||
simple_map_init(&info->map);
|
||||
|
||||
if (probe_type)
|
||||
info->mtd = do_map_probe(probe_type, &info->map);
|
||||
else
|
||||
info->mtd = obsolete_probe(dev, &info->map);
|
||||
|
||||
err = -ENXIO;
|
||||
if (!info->mtd) {
|
||||
dev_err(&dev->dev, "do_map_probe() failed\n");
|
||||
if (err)
|
||||
goto err_out;
|
||||
}
|
||||
info->mtd->owner = THIS_MODULE;
|
||||
info->mtd->dev.parent = &dev->dev;
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
/* First look for RedBoot table or partitions on the command
|
||||
* line, these take precedence over device tree information */
|
||||
err = parse_mtd_partitions(info->mtd, part_probe_types,
|
||||
&info->parts, 0);
|
||||
err = parse_mtd_partitions(info->cmtd, part_probe_types,
|
||||
&info->parts, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -244,15 +327,19 @@ static int __devinit of_flash_probe(struct of_device *dev,
|
|||
}
|
||||
|
||||
if (err > 0)
|
||||
add_mtd_partitions(info->mtd, info->parts, err);
|
||||
add_mtd_partitions(info->cmtd, info->parts, err);
|
||||
else
|
||||
#endif
|
||||
add_mtd_device(info->mtd);
|
||||
add_mtd_device(info->cmtd);
|
||||
|
||||
kfree(mtd_list);
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
kfree(mtd_list);
|
||||
of_flash_remove(dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* Mapping of the rootfs in a physical region of memory
|
||||
*
|
||||
* Copyright (C) 2005-2007 PMC-Sierra Inc.
|
||||
* Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* 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.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/root_dev.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <msp_prom.h>
|
||||
|
||||
static struct mtd_info *rr_mtd;
|
||||
|
||||
struct map_info rr_map = {
|
||||
.name = "ramroot",
|
||||
.bankwidth = 4,
|
||||
};
|
||||
|
||||
static int __init init_rrmap(void)
|
||||
{
|
||||
void *ramroot_start;
|
||||
unsigned long ramroot_size;
|
||||
|
||||
/* Check for supported rootfs types */
|
||||
if (get_ramroot(&ramroot_start, &ramroot_size)) {
|
||||
rr_map.phys = CPHYSADDR(ramroot_start);
|
||||
rr_map.size = ramroot_size;
|
||||
|
||||
printk(KERN_NOTICE
|
||||
"PMC embedded root device: 0x%08lx @ 0x%08lx\n",
|
||||
rr_map.size, (unsigned long)rr_map.phys);
|
||||
} else {
|
||||
printk(KERN_ERR
|
||||
"init_rrmap: no supported embedded rootfs detected!\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Map rootfs to I/O space for block device driver */
|
||||
rr_map.virt = ioremap(rr_map.phys, rr_map.size);
|
||||
if (!rr_map.virt) {
|
||||
printk(KERN_ERR "Failed to ioremap\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
simple_map_init(&rr_map);
|
||||
|
||||
rr_mtd = do_map_probe("map_ram", &rr_map);
|
||||
if (rr_mtd) {
|
||||
rr_mtd->owner = THIS_MODULE;
|
||||
|
||||
add_mtd_device(rr_mtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
iounmap(rr_map.virt);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static void __exit cleanup_rrmap(void)
|
||||
{
|
||||
del_mtd_device(rr_mtd);
|
||||
map_destroy(rr_mtd);
|
||||
|
||||
iounmap(rr_map.virt);
|
||||
rr_map.virt = NULL;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("PMC-Sierra, Inc");
|
||||
MODULE_DESCRIPTION("MTD map driver for embedded PMC-Sierra MSP filesystem");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(init_rrmap);
|
||||
module_exit(cleanup_rrmap);
|
|
@ -140,24 +140,6 @@ static int __devexit pxa2xx_flash_remove(struct platform_device *dev)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pxa2xx_flash_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (info->mtd && info->mtd->suspend)
|
||||
ret = info->mtd->suspend(info->mtd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pxa2xx_flash_resume(struct platform_device *dev)
|
||||
{
|
||||
struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
|
||||
|
||||
if (info->mtd && info->mtd->resume)
|
||||
info->mtd->resume(info->mtd);
|
||||
return 0;
|
||||
}
|
||||
static void pxa2xx_flash_shutdown(struct platform_device *dev)
|
||||
{
|
||||
struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
|
||||
|
@ -166,8 +148,6 @@ static void pxa2xx_flash_shutdown(struct platform_device *dev)
|
|||
info->mtd->resume(info->mtd);
|
||||
}
|
||||
#else
|
||||
#define pxa2xx_flash_suspend NULL
|
||||
#define pxa2xx_flash_resume NULL
|
||||
#define pxa2xx_flash_shutdown NULL
|
||||
#endif
|
||||
|
||||
|
@ -178,8 +158,6 @@ static struct platform_driver pxa2xx_flash_driver = {
|
|||
},
|
||||
.probe = pxa2xx_flash_probe,
|
||||
.remove = __devexit_p(pxa2xx_flash_remove),
|
||||
.suspend = pxa2xx_flash_suspend,
|
||||
.resume = pxa2xx_flash_resume,
|
||||
.shutdown = pxa2xx_flash_shutdown,
|
||||
};
|
||||
|
||||
|
|
|
@ -145,25 +145,6 @@ err_out:
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int rbtx4939_flash_suspend(struct platform_device *dev,
|
||||
pm_message_t state)
|
||||
{
|
||||
struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
|
||||
|
||||
if (info->mtd->suspend)
|
||||
return info->mtd->suspend(info->mtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rbtx4939_flash_resume(struct platform_device *dev)
|
||||
{
|
||||
struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
|
||||
|
||||
if (info->mtd->resume)
|
||||
info->mtd->resume(info->mtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rbtx4939_flash_shutdown(struct platform_device *dev)
|
||||
{
|
||||
struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
|
||||
|
@ -173,16 +154,12 @@ static void rbtx4939_flash_shutdown(struct platform_device *dev)
|
|||
info->mtd->resume(info->mtd);
|
||||
}
|
||||
#else
|
||||
#define rbtx4939_flash_suspend NULL
|
||||
#define rbtx4939_flash_resume NULL
|
||||
#define rbtx4939_flash_shutdown NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver rbtx4939_flash_driver = {
|
||||
.probe = rbtx4939_flash_probe,
|
||||
.remove = rbtx4939_flash_remove,
|
||||
.suspend = rbtx4939_flash_suspend,
|
||||
.resume = rbtx4939_flash_resume,
|
||||
.shutdown = rbtx4939_flash_shutdown,
|
||||
.driver = {
|
||||
.name = "rbtx4939-flash",
|
||||
|
|
|
@ -415,25 +415,6 @@ static int __exit sa1100_mtd_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int sa1100_mtd_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
struct sa_info *info = platform_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (info)
|
||||
ret = info->mtd->suspend(info->mtd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sa1100_mtd_resume(struct platform_device *dev)
|
||||
{
|
||||
struct sa_info *info = platform_get_drvdata(dev);
|
||||
if (info)
|
||||
info->mtd->resume(info->mtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sa1100_mtd_shutdown(struct platform_device *dev)
|
||||
{
|
||||
struct sa_info *info = platform_get_drvdata(dev);
|
||||
|
@ -441,16 +422,12 @@ static void sa1100_mtd_shutdown(struct platform_device *dev)
|
|||
info->mtd->resume(info->mtd);
|
||||
}
|
||||
#else
|
||||
#define sa1100_mtd_suspend NULL
|
||||
#define sa1100_mtd_resume NULL
|
||||
#define sa1100_mtd_shutdown NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver sa1100_mtd_driver = {
|
||||
.probe = sa1100_mtd_probe,
|
||||
.remove = __exit_p(sa1100_mtd_remove),
|
||||
.suspend = sa1100_mtd_suspend,
|
||||
.resume = sa1100_mtd_resume,
|
||||
.shutdown = sa1100_mtd_shutdown,
|
||||
.driver = {
|
||||
.name = "sa1100-mtd",
|
||||
|
|
|
@ -22,15 +22,19 @@
|
|||
|
||||
/****************************************************************************/
|
||||
|
||||
extern char _ebss;
|
||||
|
||||
struct map_info uclinux_ram_map = {
|
||||
.name = "RAM",
|
||||
.phys = (unsigned long)&_ebss,
|
||||
.size = 0,
|
||||
};
|
||||
|
||||
struct mtd_info *uclinux_ram_mtdinfo;
|
||||
static struct mtd_info *uclinux_ram_mtdinfo;
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
struct mtd_partition uclinux_romfs[] = {
|
||||
static struct mtd_partition uclinux_romfs[] = {
|
||||
{ .name = "ROMfs" }
|
||||
};
|
||||
|
||||
|
@ -38,7 +42,7 @@ struct mtd_partition uclinux_romfs[] = {
|
|||
|
||||
/****************************************************************************/
|
||||
|
||||
int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
static int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, void **virt, resource_size_t *phys)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
|
@ -55,12 +59,10 @@ static int __init uclinux_mtd_init(void)
|
|||
{
|
||||
struct mtd_info *mtd;
|
||||
struct map_info *mapp;
|
||||
extern char _ebss;
|
||||
unsigned long addr = (unsigned long) &_ebss;
|
||||
|
||||
mapp = &uclinux_ram_map;
|
||||
mapp->phys = addr;
|
||||
mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(addr + 8))));
|
||||
if (!mapp->size)
|
||||
mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8))));
|
||||
mapp->bankwidth = 4;
|
||||
|
||||
printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n",
|
||||
|
|
|
@ -291,7 +291,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
|
|||
gd->private_data = new;
|
||||
new->blkcore_priv = gd;
|
||||
gd->queue = tr->blkcore_priv->rq;
|
||||
gd->driverfs_dev = new->mtd->dev.parent;
|
||||
gd->driverfs_dev = &new->mtd->dev;
|
||||
|
||||
if (new->readonly)
|
||||
set_disk_ro(gd, 1);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/compatmac.h>
|
||||
|
@ -355,6 +356,100 @@ static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
|
|||
# define otp_select_filemode(f,m) -EOPNOTSUPP
|
||||
#endif
|
||||
|
||||
static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd,
|
||||
uint64_t start, uint32_t length, void __user *ptr,
|
||||
uint32_t __user *retp)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
uint32_t retlen;
|
||||
int ret = 0;
|
||||
|
||||
if (!(file->f_mode & FMODE_WRITE))
|
||||
return -EPERM;
|
||||
|
||||
if (length > 4096)
|
||||
return -EINVAL;
|
||||
|
||||
if (!mtd->write_oob)
|
||||
ret = -EOPNOTSUPP;
|
||||
else
|
||||
ret = access_ok(VERIFY_READ, ptr, length) ? 0 : EFAULT;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ops.ooblen = length;
|
||||
ops.ooboffs = start & (mtd->oobsize - 1);
|
||||
ops.datbuf = NULL;
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
|
||||
if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
|
||||
return -EINVAL;
|
||||
|
||||
ops.oobbuf = kmalloc(length, GFP_KERNEL);
|
||||
if (!ops.oobbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(ops.oobbuf, ptr, length)) {
|
||||
kfree(ops.oobbuf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
start &= ~((uint64_t)mtd->oobsize - 1);
|
||||
ret = mtd->write_oob(mtd, start, &ops);
|
||||
|
||||
if (ops.oobretlen > 0xFFFFFFFFU)
|
||||
ret = -EOVERFLOW;
|
||||
retlen = ops.oobretlen;
|
||||
if (copy_to_user(retp, &retlen, sizeof(length)))
|
||||
ret = -EFAULT;
|
||||
|
||||
kfree(ops.oobbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,
|
||||
uint32_t length, void __user *ptr, uint32_t __user *retp)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
int ret = 0;
|
||||
|
||||
if (length > 4096)
|
||||
return -EINVAL;
|
||||
|
||||
if (!mtd->read_oob)
|
||||
ret = -EOPNOTSUPP;
|
||||
else
|
||||
ret = access_ok(VERIFY_WRITE, ptr,
|
||||
length) ? 0 : -EFAULT;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ops.ooblen = length;
|
||||
ops.ooboffs = start & (mtd->oobsize - 1);
|
||||
ops.datbuf = NULL;
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
|
||||
if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
|
||||
return -EINVAL;
|
||||
|
||||
ops.oobbuf = kmalloc(length, GFP_KERNEL);
|
||||
if (!ops.oobbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
start &= ~((uint64_t)mtd->oobsize - 1);
|
||||
ret = mtd->read_oob(mtd, start, &ops);
|
||||
|
||||
if (put_user(ops.oobretlen, retp))
|
||||
ret = -EFAULT;
|
||||
else if (ops.oobretlen && copy_to_user(ptr, ops.oobbuf,
|
||||
ops.oobretlen))
|
||||
ret = -EFAULT;
|
||||
|
||||
kfree(ops.oobbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
u_int cmd, u_long arg)
|
||||
{
|
||||
|
@ -417,6 +512,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
|||
break;
|
||||
|
||||
case MEMERASE:
|
||||
case MEMERASE64:
|
||||
{
|
||||
struct erase_info *erase;
|
||||
|
||||
|
@ -427,20 +523,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
|||
if (!erase)
|
||||
ret = -ENOMEM;
|
||||
else {
|
||||
struct erase_info_user einfo;
|
||||
|
||||
wait_queue_head_t waitq;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
init_waitqueue_head(&waitq);
|
||||
|
||||
if (copy_from_user(&einfo, argp,
|
||||
sizeof(struct erase_info_user))) {
|
||||
kfree(erase);
|
||||
return -EFAULT;
|
||||
if (cmd == MEMERASE64) {
|
||||
struct erase_info_user64 einfo64;
|
||||
|
||||
if (copy_from_user(&einfo64, argp,
|
||||
sizeof(struct erase_info_user64))) {
|
||||
kfree(erase);
|
||||
return -EFAULT;
|
||||
}
|
||||
erase->addr = einfo64.start;
|
||||
erase->len = einfo64.length;
|
||||
} else {
|
||||
struct erase_info_user einfo32;
|
||||
|
||||
if (copy_from_user(&einfo32, argp,
|
||||
sizeof(struct erase_info_user))) {
|
||||
kfree(erase);
|
||||
return -EFAULT;
|
||||
}
|
||||
erase->addr = einfo32.start;
|
||||
erase->len = einfo32.length;
|
||||
}
|
||||
erase->addr = einfo.start;
|
||||
erase->len = einfo.length;
|
||||
erase->mtd = mtd;
|
||||
erase->callback = mtdchar_erase_callback;
|
||||
erase->priv = (unsigned long)&waitq;
|
||||
|
@ -474,100 +582,56 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
|||
case MEMWRITEOOB:
|
||||
{
|
||||
struct mtd_oob_buf buf;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_buf __user *user_buf = argp;
|
||||
uint32_t retlen;
|
||||
struct mtd_oob_buf __user *buf_user = argp;
|
||||
|
||||
if(!(file->f_mode & FMODE_WRITE))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
|
||||
return -EFAULT;
|
||||
|
||||
if (buf.length > 4096)
|
||||
return -EINVAL;
|
||||
|
||||
if (!mtd->write_oob)
|
||||
ret = -EOPNOTSUPP;
|
||||
else
|
||||
ret = access_ok(VERIFY_READ, buf.ptr,
|
||||
buf.length) ? 0 : EFAULT;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ops.ooblen = buf.length;
|
||||
ops.ooboffs = buf.start & (mtd->oobsize - 1);
|
||||
ops.datbuf = NULL;
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
|
||||
if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
|
||||
return -EINVAL;
|
||||
|
||||
ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
|
||||
if (!ops.oobbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) {
|
||||
kfree(ops.oobbuf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
buf.start &= ~(mtd->oobsize - 1);
|
||||
ret = mtd->write_oob(mtd, buf.start, &ops);
|
||||
|
||||
if (ops.oobretlen > 0xFFFFFFFFU)
|
||||
ret = -EOVERFLOW;
|
||||
retlen = ops.oobretlen;
|
||||
if (copy_to_user(&user_buf->length, &retlen, sizeof(buf.length)))
|
||||
/* NOTE: writes return length to buf_user->length */
|
||||
if (copy_from_user(&buf, argp, sizeof(buf)))
|
||||
ret = -EFAULT;
|
||||
|
||||
kfree(ops.oobbuf);
|
||||
else
|
||||
ret = mtd_do_writeoob(file, mtd, buf.start, buf.length,
|
||||
buf.ptr, &buf_user->length);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case MEMREADOOB:
|
||||
{
|
||||
struct mtd_oob_buf buf;
|
||||
struct mtd_oob_ops ops;
|
||||
struct mtd_oob_buf __user *buf_user = argp;
|
||||
|
||||
if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
|
||||
return -EFAULT;
|
||||
|
||||
if (buf.length > 4096)
|
||||
return -EINVAL;
|
||||
|
||||
if (!mtd->read_oob)
|
||||
ret = -EOPNOTSUPP;
|
||||
/* NOTE: writes return length to buf_user->start */
|
||||
if (copy_from_user(&buf, argp, sizeof(buf)))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = access_ok(VERIFY_WRITE, buf.ptr,
|
||||
buf.length) ? 0 : -EFAULT;
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = mtd_do_readoob(mtd, buf.start, buf.length,
|
||||
buf.ptr, &buf_user->start);
|
||||
break;
|
||||
}
|
||||
|
||||
ops.ooblen = buf.length;
|
||||
ops.ooboffs = buf.start & (mtd->oobsize - 1);
|
||||
ops.datbuf = NULL;
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
case MEMWRITEOOB64:
|
||||
{
|
||||
struct mtd_oob_buf64 buf;
|
||||
struct mtd_oob_buf64 __user *buf_user = argp;
|
||||
|
||||
if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
|
||||
return -EINVAL;
|
||||
|
||||
ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
|
||||
if (!ops.oobbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
buf.start &= ~(mtd->oobsize - 1);
|
||||
ret = mtd->read_oob(mtd, buf.start, &ops);
|
||||
|
||||
if (put_user(ops.oobretlen, (uint32_t __user *)argp))
|
||||
ret = -EFAULT;
|
||||
else if (ops.oobretlen && copy_to_user(buf.ptr, ops.oobbuf,
|
||||
ops.oobretlen))
|
||||
if (copy_from_user(&buf, argp, sizeof(buf)))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = mtd_do_writeoob(file, mtd, buf.start, buf.length,
|
||||
(void __user *)(uintptr_t)buf.usr_ptr,
|
||||
&buf_user->length);
|
||||
break;
|
||||
}
|
||||
|
||||
kfree(ops.oobbuf);
|
||||
case MEMREADOOB64:
|
||||
{
|
||||
struct mtd_oob_buf64 buf;
|
||||
struct mtd_oob_buf64 __user *buf_user = argp;
|
||||
|
||||
if (copy_from_user(&buf, argp, sizeof(buf)))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = mtd_do_readoob(mtd, buf.start, buf.length,
|
||||
(void __user *)(uintptr_t)buf.usr_ptr,
|
||||
&buf_user->length);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -758,6 +822,68 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
|||
return ret;
|
||||
} /* memory_ioctl */
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
||||
struct mtd_oob_buf32 {
|
||||
u_int32_t start;
|
||||
u_int32_t length;
|
||||
compat_caddr_t ptr; /* unsigned char* */
|
||||
};
|
||||
|
||||
#define MEMWRITEOOB32 _IOWR('M', 3, struct mtd_oob_buf32)
|
||||
#define MEMREADOOB32 _IOWR('M', 4, struct mtd_oob_buf32)
|
||||
|
||||
static long mtd_compat_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
struct mtd_file_info *mfi = file->private_data;
|
||||
struct mtd_info *mtd = mfi->mtd;
|
||||
void __user *argp = compat_ptr(arg);
|
||||
int ret = 0;
|
||||
|
||||
lock_kernel();
|
||||
|
||||
switch (cmd) {
|
||||
case MEMWRITEOOB32:
|
||||
{
|
||||
struct mtd_oob_buf32 buf;
|
||||
struct mtd_oob_buf32 __user *buf_user = argp;
|
||||
|
||||
if (copy_from_user(&buf, argp, sizeof(buf)))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = mtd_do_writeoob(file, mtd, buf.start,
|
||||
buf.length, compat_ptr(buf.ptr),
|
||||
&buf_user->length);
|
||||
break;
|
||||
}
|
||||
|
||||
case MEMREADOOB32:
|
||||
{
|
||||
struct mtd_oob_buf32 buf;
|
||||
struct mtd_oob_buf32 __user *buf_user = argp;
|
||||
|
||||
/* NOTE: writes return length to buf->start */
|
||||
if (copy_from_user(&buf, argp, sizeof(buf)))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = mtd_do_readoob(mtd, buf.start,
|
||||
buf.length, compat_ptr(buf.ptr),
|
||||
&buf_user->start);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = mtd_ioctl(inode, file, cmd, (unsigned long)argp);
|
||||
}
|
||||
|
||||
unlock_kernel();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
/*
|
||||
* try to determine where a shared mapping can be made
|
||||
* - only supported for NOMMU at the moment (MMU can't doesn't copy private
|
||||
|
@ -817,6 +943,9 @@ static const struct file_operations mtd_fops = {
|
|||
.read = mtd_read,
|
||||
.write = mtd_write,
|
||||
.ioctl = mtd_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = mtd_compat_ioctl,
|
||||
#endif
|
||||
.open = mtd_open,
|
||||
.release = mtd_close,
|
||||
.mmap = mtd_mmap,
|
||||
|
|
|
@ -23,8 +23,15 @@
|
|||
|
||||
#include "mtdcore.h"
|
||||
|
||||
static int mtd_cls_suspend(struct device *dev, pm_message_t state);
|
||||
static int mtd_cls_resume(struct device *dev);
|
||||
|
||||
static struct class *mtd_class;
|
||||
static struct class mtd_class = {
|
||||
.name = "mtd",
|
||||
.owner = THIS_MODULE,
|
||||
.suspend = mtd_cls_suspend,
|
||||
.resume = mtd_cls_resume,
|
||||
};
|
||||
|
||||
/* These are exported solely for the purpose of mtd_blkdevs.c. You
|
||||
should not use them for _anything_ else */
|
||||
|
@ -52,7 +59,26 @@ static void mtd_release(struct device *dev)
|
|||
|
||||
/* remove /dev/mtdXro node if needed */
|
||||
if (index)
|
||||
device_destroy(mtd_class, index + 1);
|
||||
device_destroy(&mtd_class, index + 1);
|
||||
}
|
||||
|
||||
static int mtd_cls_suspend(struct device *dev, pm_message_t state)
|
||||
{
|
||||
struct mtd_info *mtd = dev_to_mtd(dev);
|
||||
|
||||
if (mtd->suspend)
|
||||
return mtd->suspend(mtd);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtd_cls_resume(struct device *dev)
|
||||
{
|
||||
struct mtd_info *mtd = dev_to_mtd(dev);
|
||||
|
||||
if (mtd->resume)
|
||||
mtd->resume(mtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t mtd_type_show(struct device *dev,
|
||||
|
@ -269,7 +295,7 @@ int add_mtd_device(struct mtd_info *mtd)
|
|||
* physical device.
|
||||
*/
|
||||
mtd->dev.type = &mtd_devtype;
|
||||
mtd->dev.class = mtd_class;
|
||||
mtd->dev.class = &mtd_class;
|
||||
mtd->dev.devt = MTD_DEVT(i);
|
||||
dev_set_name(&mtd->dev, "mtd%d", i);
|
||||
if (device_register(&mtd->dev) != 0) {
|
||||
|
@ -278,7 +304,7 @@ int add_mtd_device(struct mtd_info *mtd)
|
|||
}
|
||||
|
||||
if (MTD_DEVT(i))
|
||||
device_create(mtd_class, mtd->dev.parent,
|
||||
device_create(&mtd_class, mtd->dev.parent,
|
||||
MTD_DEVT(i) + 1,
|
||||
NULL, "mtd%dro", i);
|
||||
|
||||
|
@ -604,11 +630,12 @@ done:
|
|||
|
||||
static int __init init_mtd(void)
|
||||
{
|
||||
mtd_class = class_create(THIS_MODULE, "mtd");
|
||||
int ret;
|
||||
ret = class_register(&mtd_class);
|
||||
|
||||
if (IS_ERR(mtd_class)) {
|
||||
pr_err("Error creating mtd class.\n");
|
||||
return PTR_ERR(mtd_class);
|
||||
if (ret) {
|
||||
pr_err("Error registering mtd class: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
#ifdef CONFIG_PROC_FS
|
||||
if ((proc_mtd = create_proc_entry( "mtd", 0, NULL )))
|
||||
|
@ -623,7 +650,7 @@ static void __exit cleanup_mtd(void)
|
|||
if (proc_mtd)
|
||||
remove_proc_entry( "mtd", NULL);
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
class_destroy(mtd_class);
|
||||
class_unregister(&mtd_class);
|
||||
}
|
||||
|
||||
module_init(init_mtd);
|
||||
|
|
|
@ -27,9 +27,7 @@ struct mtd_part {
|
|||
struct mtd_info mtd;
|
||||
struct mtd_info *master;
|
||||
uint64_t offset;
|
||||
int index;
|
||||
struct list_head list;
|
||||
int registered;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -321,8 +319,7 @@ int del_mtd_partitions(struct mtd_info *master)
|
|||
list_for_each_entry_safe(slave, next, &mtd_partitions, list)
|
||||
if (slave->master == master) {
|
||||
list_del(&slave->list);
|
||||
if (slave->registered)
|
||||
del_mtd_device(&slave->mtd);
|
||||
del_mtd_device(&slave->mtd);
|
||||
kfree(slave);
|
||||
}
|
||||
|
||||
|
@ -395,7 +392,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
|
|||
slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
|
||||
if (master->sync)
|
||||
slave->mtd.sync = part_sync;
|
||||
if (!partno && master->suspend && master->resume) {
|
||||
if (!partno && !master->dev.class && master->suspend && master->resume) {
|
||||
slave->mtd.suspend = part_suspend;
|
||||
slave->mtd.resume = part_resume;
|
||||
}
|
||||
|
@ -412,7 +409,6 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
|
|||
slave->mtd.erase = part_erase;
|
||||
slave->master = master;
|
||||
slave->offset = part->offset;
|
||||
slave->index = partno;
|
||||
|
||||
if (slave->offset == MTDPART_OFS_APPEND)
|
||||
slave->offset = cur_offset;
|
||||
|
@ -500,15 +496,9 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
|
|||
}
|
||||
|
||||
out_register:
|
||||
if (part->mtdp) {
|
||||
/* store the object pointer (caller may or may not register it*/
|
||||
*part->mtdp = &slave->mtd;
|
||||
slave->registered = 0;
|
||||
} else {
|
||||
/* register our partition */
|
||||
add_mtd_device(&slave->mtd);
|
||||
slave->registered = 1;
|
||||
}
|
||||
/* register our partition */
|
||||
add_mtd_device(&slave->mtd);
|
||||
|
||||
return slave;
|
||||
}
|
||||
|
||||
|
|
|
@ -74,6 +74,12 @@ config MTD_NAND_AMS_DELTA
|
|||
help
|
||||
Support for NAND flash on Amstrad E3 (Delta).
|
||||
|
||||
config MTD_NAND_OMAP2
|
||||
tristate "NAND Flash device on OMAP2 and OMAP3"
|
||||
depends on ARM && MTD_NAND && (ARCH_OMAP2 || ARCH_OMAP3)
|
||||
help
|
||||
Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms.
|
||||
|
||||
config MTD_NAND_TS7250
|
||||
tristate "NAND Flash device on TS-7250 board"
|
||||
depends on MACH_TS72XX
|
||||
|
@ -139,27 +145,27 @@ config MTD_NAND_PPCHAMELEONEVB
|
|||
This enables the NAND flash driver on the PPChameleon EVB Board.
|
||||
|
||||
config MTD_NAND_S3C2410
|
||||
tristate "NAND Flash support for S3C2410/S3C2440 SoC"
|
||||
depends on ARCH_S3C2410
|
||||
tristate "NAND Flash support for Samsung S3C SoCs"
|
||||
depends on ARCH_S3C2410 || ARCH_S3C64XX
|
||||
help
|
||||
This enables the NAND flash controller on the S3C2410 and S3C2440
|
||||
This enables the NAND flash controller on the S3C24xx and S3C64xx
|
||||
SoCs
|
||||
|
||||
No board specific support is done by this driver, each board
|
||||
must advertise a platform_device for the driver to attach.
|
||||
|
||||
config MTD_NAND_S3C2410_DEBUG
|
||||
bool "S3C2410 NAND driver debug"
|
||||
bool "Samsung S3C NAND driver debug"
|
||||
depends on MTD_NAND_S3C2410
|
||||
help
|
||||
Enable debugging of the S3C2410 NAND driver
|
||||
Enable debugging of the S3C NAND driver
|
||||
|
||||
config MTD_NAND_S3C2410_HWECC
|
||||
bool "S3C2410 NAND Hardware ECC"
|
||||
bool "Samsung S3C NAND Hardware ECC"
|
||||
depends on MTD_NAND_S3C2410
|
||||
help
|
||||
Enable the use of the S3C2410's internal ECC generator when
|
||||
using NAND. Early versions of the chip have had problems with
|
||||
Enable the use of the controller's internal ECC generator when
|
||||
using NAND. Early versions of the chips have had problems with
|
||||
incorrect ECC generation, and if using these, the default of
|
||||
software ECC is preferable.
|
||||
|
||||
|
@ -171,7 +177,7 @@ config MTD_NAND_NDFC
|
|||
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
|
||||
|
||||
config MTD_NAND_S3C2410_CLKSTOP
|
||||
bool "S3C2410 NAND IDLE clock stop"
|
||||
bool "Samsung S3C NAND IDLE clock stop"
|
||||
depends on MTD_NAND_S3C2410
|
||||
default n
|
||||
help
|
||||
|
|
|
@ -25,6 +25,7 @@ obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
|
|||
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
|
||||
obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o
|
||||
obj-$(CONFIG_MTD_NAND_OMAP2) += omap2.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
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
|
@ -47,6 +48,9 @@
|
|||
#define no_ecc 0
|
||||
#endif
|
||||
|
||||
static int on_flash_bbt = 0;
|
||||
module_param(on_flash_bbt, int, 0);
|
||||
|
||||
/* Register access macros */
|
||||
#define ecc_readl(add, reg) \
|
||||
__raw_readl(add + ATMEL_ECC_##reg)
|
||||
|
@ -459,12 +463,17 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
|
|||
|
||||
if (host->board->det_pin) {
|
||||
if (gpio_get_value(host->board->det_pin)) {
|
||||
printk("No SmartMedia card inserted.\n");
|
||||
printk(KERN_INFO "No SmartMedia card inserted.\n");
|
||||
res = ENXIO;
|
||||
goto err_no_card;
|
||||
}
|
||||
}
|
||||
|
||||
if (on_flash_bbt) {
|
||||
printk(KERN_INFO "atmel_nand: Use On Flash BBT\n");
|
||||
nand_chip->options |= NAND_USE_FLASH_BBT;
|
||||
}
|
||||
|
||||
/* first scan to find the device and get the page size */
|
||||
if (nand_scan_ident(mtd, 1)) {
|
||||
res = -ENXIO;
|
||||
|
|
|
@ -458,7 +458,7 @@ static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bf5xx_nand_dma_rw(struct mtd_info *mtd,
|
||||
static void bf5xx_nand_dma_rw(struct mtd_info *mtd,
|
||||
uint8_t *buf, int is_read)
|
||||
{
|
||||
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
|
||||
|
@ -496,11 +496,20 @@ static int bf5xx_nand_dma_rw(struct mtd_info *mtd,
|
|||
/* setup DMA register with Blackfin DMA API */
|
||||
set_dma_config(CH_NFC, 0x0);
|
||||
set_dma_start_addr(CH_NFC, (unsigned long) buf);
|
||||
|
||||
/* The DMAs have different size on BF52x and BF54x */
|
||||
#ifdef CONFIG_BF52x
|
||||
set_dma_x_count(CH_NFC, (page_size >> 1));
|
||||
set_dma_x_modify(CH_NFC, 2);
|
||||
val = DI_EN | WDSIZE_16;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BF54x
|
||||
set_dma_x_count(CH_NFC, (page_size >> 2));
|
||||
set_dma_x_modify(CH_NFC, 4);
|
||||
|
||||
/* setup write or read operation */
|
||||
val = DI_EN | WDSIZE_32;
|
||||
#endif
|
||||
/* setup write or read operation */
|
||||
if (is_read)
|
||||
val |= WNR;
|
||||
set_dma_config(CH_NFC, val);
|
||||
|
@ -512,8 +521,6 @@ static int bf5xx_nand_dma_rw(struct mtd_info *mtd,
|
|||
else
|
||||
bfin_write_NFC_PGCTL(0x2);
|
||||
wait_for_completion(&info->dma_completion);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd,
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
* and some flavors of secondary chipselect (e.g. based on A12) as used
|
||||
* with multichip packages.
|
||||
*
|
||||
* The 1-bit ECC hardware is supported, but not yet the newer 4-bit ECC
|
||||
* The 1-bit ECC hardware is supported, as well as the newer 4-bit ECC
|
||||
* available on chips like the DM355 and OMAP-L137 and needed with the
|
||||
* more error-prone MLC NAND chips.
|
||||
*
|
||||
|
@ -54,11 +54,14 @@
|
|||
struct davinci_nand_info {
|
||||
struct mtd_info mtd;
|
||||
struct nand_chip chip;
|
||||
struct nand_ecclayout ecclayout;
|
||||
|
||||
struct device *dev;
|
||||
struct clk *clk;
|
||||
bool partitioned;
|
||||
|
||||
bool is_readmode;
|
||||
|
||||
void __iomem *base;
|
||||
void __iomem *vaddr;
|
||||
|
||||
|
@ -73,6 +76,7 @@ struct davinci_nand_info {
|
|||
};
|
||||
|
||||
static DEFINE_SPINLOCK(davinci_nand_lock);
|
||||
static bool ecc4_busy;
|
||||
|
||||
#define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd)
|
||||
|
||||
|
@ -217,6 +221,192 @@ static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat,
|
|||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* 4-bit hardware ECC ... context maintained over entire AEMIF
|
||||
*
|
||||
* This is a syndrome engine, but we avoid NAND_ECC_HW_SYNDROME
|
||||
* since that forces use of a problematic "infix OOB" layout.
|
||||
* Among other things, it trashes manufacturer bad block markers.
|
||||
* Also, and specific to this hardware, it ECC-protects the "prepad"
|
||||
* in the OOB ... while having ECC protection for parts of OOB would
|
||||
* seem useful, the current MTD stack sometimes wants to update the
|
||||
* OOB without recomputing ECC.
|
||||
*/
|
||||
|
||||
static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
struct davinci_nand_info *info = to_davinci_nand(mtd);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&davinci_nand_lock, flags);
|
||||
|
||||
/* Start 4-bit ECC calculation for read/write */
|
||||
val = davinci_nand_readl(info, NANDFCR_OFFSET);
|
||||
val &= ~(0x03 << 4);
|
||||
val |= (info->core_chipsel << 4) | BIT(12);
|
||||
davinci_nand_writel(info, NANDFCR_OFFSET, val);
|
||||
|
||||
info->is_readmode = (mode == NAND_ECC_READ);
|
||||
|
||||
spin_unlock_irqrestore(&davinci_nand_lock, flags);
|
||||
}
|
||||
|
||||
/* Read raw ECC code after writing to NAND. */
|
||||
static void
|
||||
nand_davinci_readecc_4bit(struct davinci_nand_info *info, u32 code[4])
|
||||
{
|
||||
const u32 mask = 0x03ff03ff;
|
||||
|
||||
code[0] = davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET) & mask;
|
||||
code[1] = davinci_nand_readl(info, NAND_4BIT_ECC2_OFFSET) & mask;
|
||||
code[2] = davinci_nand_readl(info, NAND_4BIT_ECC3_OFFSET) & mask;
|
||||
code[3] = davinci_nand_readl(info, NAND_4BIT_ECC4_OFFSET) & mask;
|
||||
}
|
||||
|
||||
/* Terminate read ECC; or return ECC (as bytes) of data written to NAND. */
|
||||
static int nand_davinci_calculate_4bit(struct mtd_info *mtd,
|
||||
const u_char *dat, u_char *ecc_code)
|
||||
{
|
||||
struct davinci_nand_info *info = to_davinci_nand(mtd);
|
||||
u32 raw_ecc[4], *p;
|
||||
unsigned i;
|
||||
|
||||
/* After a read, terminate ECC calculation by a dummy read
|
||||
* of some 4-bit ECC register. ECC covers everything that
|
||||
* was read; correct() just uses the hardware state, so
|
||||
* ecc_code is not needed.
|
||||
*/
|
||||
if (info->is_readmode) {
|
||||
davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pack eight raw 10-bit ecc values into ten bytes, making
|
||||
* two passes which each convert four values (in upper and
|
||||
* lower halves of two 32-bit words) into five bytes. The
|
||||
* ROM boot loader uses this same packing scheme.
|
||||
*/
|
||||
nand_davinci_readecc_4bit(info, raw_ecc);
|
||||
for (i = 0, p = raw_ecc; i < 2; i++, p += 2) {
|
||||
*ecc_code++ = p[0] & 0xff;
|
||||
*ecc_code++ = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc);
|
||||
*ecc_code++ = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0);
|
||||
*ecc_code++ = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0);
|
||||
*ecc_code++ = (p[1] >> 18) & 0xff;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Correct up to 4 bits in data we just read, using state left in the
|
||||
* hardware plus the ecc_code computed when it was first written.
|
||||
*/
|
||||
static int nand_davinci_correct_4bit(struct mtd_info *mtd,
|
||||
u_char *data, u_char *ecc_code, u_char *null)
|
||||
{
|
||||
int i;
|
||||
struct davinci_nand_info *info = to_davinci_nand(mtd);
|
||||
unsigned short ecc10[8];
|
||||
unsigned short *ecc16;
|
||||
u32 syndrome[4];
|
||||
unsigned num_errors, corrected;
|
||||
|
||||
/* All bytes 0xff? It's an erased page; ignore its ECC. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (ecc_code[i] != 0xff)
|
||||
goto compare;
|
||||
}
|
||||
return 0;
|
||||
|
||||
compare:
|
||||
/* Unpack ten bytes into eight 10 bit values. We know we're
|
||||
* little-endian, and use type punning for less shifting/masking.
|
||||
*/
|
||||
if (WARN_ON(0x01 & (unsigned) ecc_code))
|
||||
return -EINVAL;
|
||||
ecc16 = (unsigned short *)ecc_code;
|
||||
|
||||
ecc10[0] = (ecc16[0] >> 0) & 0x3ff;
|
||||
ecc10[1] = ((ecc16[0] >> 10) & 0x3f) | ((ecc16[1] << 6) & 0x3c0);
|
||||
ecc10[2] = (ecc16[1] >> 4) & 0x3ff;
|
||||
ecc10[3] = ((ecc16[1] >> 14) & 0x3) | ((ecc16[2] << 2) & 0x3fc);
|
||||
ecc10[4] = (ecc16[2] >> 8) | ((ecc16[3] << 8) & 0x300);
|
||||
ecc10[5] = (ecc16[3] >> 2) & 0x3ff;
|
||||
ecc10[6] = ((ecc16[3] >> 12) & 0xf) | ((ecc16[4] << 4) & 0x3f0);
|
||||
ecc10[7] = (ecc16[4] >> 6) & 0x3ff;
|
||||
|
||||
/* Tell ECC controller about the expected ECC codes. */
|
||||
for (i = 7; i >= 0; i--)
|
||||
davinci_nand_writel(info, NAND_4BIT_ECC_LOAD_OFFSET, ecc10[i]);
|
||||
|
||||
/* Allow time for syndrome calculation ... then read it.
|
||||
* A syndrome of all zeroes 0 means no detected errors.
|
||||
*/
|
||||
davinci_nand_readl(info, NANDFSR_OFFSET);
|
||||
nand_davinci_readecc_4bit(info, syndrome);
|
||||
if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3]))
|
||||
return 0;
|
||||
|
||||
/* Start address calculation, and wait for it to complete.
|
||||
* We _could_ start reading more data while this is working,
|
||||
* to speed up the overall page read.
|
||||
*/
|
||||
davinci_nand_writel(info, NANDFCR_OFFSET,
|
||||
davinci_nand_readl(info, NANDFCR_OFFSET) | BIT(13));
|
||||
for (;;) {
|
||||
u32 fsr = davinci_nand_readl(info, NANDFSR_OFFSET);
|
||||
|
||||
switch ((fsr >> 8) & 0x0f) {
|
||||
case 0: /* no error, should not happen */
|
||||
return 0;
|
||||
case 1: /* five or more errors detected */
|
||||
return -EIO;
|
||||
case 2: /* error addresses computed */
|
||||
case 3:
|
||||
num_errors = 1 + ((fsr >> 16) & 0x03);
|
||||
goto correct;
|
||||
default: /* still working on it */
|
||||
cpu_relax();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
correct:
|
||||
/* correct each error */
|
||||
for (i = 0, corrected = 0; i < num_errors; i++) {
|
||||
int error_address, error_value;
|
||||
|
||||
if (i > 1) {
|
||||
error_address = davinci_nand_readl(info,
|
||||
NAND_ERR_ADD2_OFFSET);
|
||||
error_value = davinci_nand_readl(info,
|
||||
NAND_ERR_ERRVAL2_OFFSET);
|
||||
} else {
|
||||
error_address = davinci_nand_readl(info,
|
||||
NAND_ERR_ADD1_OFFSET);
|
||||
error_value = davinci_nand_readl(info,
|
||||
NAND_ERR_ERRVAL1_OFFSET);
|
||||
}
|
||||
|
||||
if (i & 1) {
|
||||
error_address >>= 16;
|
||||
error_value >>= 16;
|
||||
}
|
||||
error_address &= 0x3ff;
|
||||
error_address = (512 + 7) - error_address;
|
||||
|
||||
if (error_address < 512) {
|
||||
data[error_address] ^= error_value;
|
||||
corrected++;
|
||||
}
|
||||
}
|
||||
|
||||
return corrected;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's
|
||||
* how these chips are normally wired. This translates to both 8 and 16
|
||||
|
@ -294,6 +484,23 @@ static void __init nand_dm6446evm_flash_init(struct davinci_nand_info *info)
|
|||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/* An ECC layout for using 4-bit ECC with small-page flash, storing
|
||||
* ten ECC bytes plus the manufacturer's bad block marker byte, and
|
||||
* and not overlapping the default BBT markers.
|
||||
*/
|
||||
static struct nand_ecclayout hwecc4_small __initconst = {
|
||||
.eccbytes = 10,
|
||||
.eccpos = { 0, 1, 2, 3, 4,
|
||||
/* offset 5 holds the badblock marker */
|
||||
6, 7,
|
||||
13, 14, 15, },
|
||||
.oobfree = {
|
||||
{.offset = 8, .length = 5, },
|
||||
{.offset = 16, },
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static int __init nand_davinci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct davinci_nand_pdata *pdata = pdev->dev.platform_data;
|
||||
|
@ -306,6 +513,10 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
|||
uint32_t val;
|
||||
nand_ecc_modes_t ecc_mode;
|
||||
|
||||
/* insist on board-specific configuration */
|
||||
if (!pdata)
|
||||
return -ENODEV;
|
||||
|
||||
/* which external chipselect will we be managing? */
|
||||
if (pdev->id < 0 || pdev->id > 3)
|
||||
return -ENODEV;
|
||||
|
@ -351,7 +562,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
|||
info->chip.select_chip = nand_davinci_select_chip;
|
||||
|
||||
/* options such as NAND_USE_FLASH_BBT or 16-bit widths */
|
||||
info->chip.options = pdata ? pdata->options : 0;
|
||||
info->chip.options = pdata->options;
|
||||
|
||||
info->ioaddr = (uint32_t __force) vaddr;
|
||||
|
||||
|
@ -360,14 +571,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
|||
info->mask_chipsel = pdata->mask_chipsel;
|
||||
|
||||
/* use nandboot-capable ALE/CLE masks by default */
|
||||
if (pdata && pdata->mask_ale)
|
||||
info->mask_ale = pdata->mask_cle;
|
||||
else
|
||||
info->mask_ale = MASK_ALE;
|
||||
if (pdata && pdata->mask_cle)
|
||||
info->mask_cle = pdata->mask_cle;
|
||||
else
|
||||
info->mask_cle = MASK_CLE;
|
||||
info->mask_ale = pdata->mask_cle ? : MASK_ALE;
|
||||
info->mask_cle = pdata->mask_cle ? : MASK_CLE;
|
||||
|
||||
/* Set address of hardware control function */
|
||||
info->chip.cmd_ctrl = nand_davinci_hwcontrol;
|
||||
|
@ -377,30 +582,44 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
|||
info->chip.read_buf = nand_davinci_read_buf;
|
||||
info->chip.write_buf = nand_davinci_write_buf;
|
||||
|
||||
/* use board-specific ECC config; else, the best available */
|
||||
if (pdata)
|
||||
ecc_mode = pdata->ecc_mode;
|
||||
else
|
||||
ecc_mode = NAND_ECC_HW;
|
||||
/* Use board-specific ECC config */
|
||||
ecc_mode = pdata->ecc_mode;
|
||||
|
||||
ret = -EINVAL;
|
||||
switch (ecc_mode) {
|
||||
case NAND_ECC_NONE:
|
||||
case NAND_ECC_SOFT:
|
||||
pdata->ecc_bits = 0;
|
||||
break;
|
||||
case NAND_ECC_HW:
|
||||
info->chip.ecc.calculate = nand_davinci_calculate_1bit;
|
||||
info->chip.ecc.correct = nand_davinci_correct_1bit;
|
||||
info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
|
||||
info->chip.ecc.size = 512;
|
||||
info->chip.ecc.bytes = 3;
|
||||
break;
|
||||
case NAND_ECC_HW_SYNDROME:
|
||||
/* FIXME implement */
|
||||
info->chip.ecc.size = 512;
|
||||
info->chip.ecc.bytes = 10;
|
||||
if (pdata->ecc_bits == 4) {
|
||||
/* No sanity checks: CPUs must support this,
|
||||
* and the chips may not use NAND_BUSWIDTH_16.
|
||||
*/
|
||||
|
||||
dev_warn(&pdev->dev, "4-bit ECC nyet supported\n");
|
||||
/* FALL THROUGH */
|
||||
/* No sharing 4-bit hardware between chipselects yet */
|
||||
spin_lock_irq(&davinci_nand_lock);
|
||||
if (ecc4_busy)
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ecc4_busy = true;
|
||||
spin_unlock_irq(&davinci_nand_lock);
|
||||
|
||||
if (ret == -EBUSY)
|
||||
goto err_ecc;
|
||||
|
||||
info->chip.ecc.calculate = nand_davinci_calculate_4bit;
|
||||
info->chip.ecc.correct = nand_davinci_correct_4bit;
|
||||
info->chip.ecc.hwctl = nand_davinci_hwctl_4bit;
|
||||
info->chip.ecc.bytes = 10;
|
||||
} else {
|
||||
info->chip.ecc.calculate = nand_davinci_calculate_1bit;
|
||||
info->chip.ecc.correct = nand_davinci_correct_1bit;
|
||||
info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
|
||||
info->chip.ecc.bytes = 3;
|
||||
}
|
||||
info->chip.ecc.size = 512;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto err_ecc;
|
||||
|
@ -441,12 +660,56 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
|||
spin_unlock_irq(&davinci_nand_lock);
|
||||
|
||||
/* Scan to find existence of the device(s) */
|
||||
ret = nand_scan(&info->mtd, pdata->mask_chipsel ? 2 : 1);
|
||||
ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
|
||||
goto err_scan;
|
||||
}
|
||||
|
||||
/* Update ECC layout if needed ... for 1-bit HW ECC, the default
|
||||
* is OK, but it allocates 6 bytes when only 3 are needed (for
|
||||
* each 512 bytes). For the 4-bit HW ECC, that default is not
|
||||
* usable: 10 bytes are needed, not 6.
|
||||
*/
|
||||
if (pdata->ecc_bits == 4) {
|
||||
int chunks = info->mtd.writesize / 512;
|
||||
|
||||
if (!chunks || info->mtd.oobsize < 16) {
|
||||
dev_dbg(&pdev->dev, "too small\n");
|
||||
ret = -EINVAL;
|
||||
goto err_scan;
|
||||
}
|
||||
|
||||
/* For small page chips, preserve the manufacturer's
|
||||
* badblock marking data ... and make sure a flash BBT
|
||||
* table marker fits in the free bytes.
|
||||
*/
|
||||
if (chunks == 1) {
|
||||
info->ecclayout = hwecc4_small;
|
||||
info->ecclayout.oobfree[1].length =
|
||||
info->mtd.oobsize - 16;
|
||||
goto syndrome_done;
|
||||
}
|
||||
|
||||
/* For large page chips we'll be wanting to use a
|
||||
* not-yet-implemented mode that reads OOB data
|
||||
* before reading the body of the page, to avoid
|
||||
* the "infix OOB" model of NAND_ECC_HW_SYNDROME
|
||||
* (and preserve manufacturer badblock markings).
|
||||
*/
|
||||
dev_warn(&pdev->dev, "no 4-bit ECC support yet "
|
||||
"for large page NAND\n");
|
||||
ret = -EIO;
|
||||
goto err_scan;
|
||||
|
||||
syndrome_done:
|
||||
info->chip.ecc.layout = &info->ecclayout;
|
||||
}
|
||||
|
||||
ret = nand_scan_tail(&info->mtd);
|
||||
if (ret < 0)
|
||||
goto err_scan;
|
||||
|
||||
if (mtd_has_partitions()) {
|
||||
struct mtd_partition *mtd_parts = NULL;
|
||||
int mtd_parts_nb = 0;
|
||||
|
@ -455,22 +718,11 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
|||
static const char *probes[] __initconst =
|
||||
{ "cmdlinepart", NULL };
|
||||
|
||||
const char *master_name;
|
||||
|
||||
/* Set info->mtd.name = 0 temporarily */
|
||||
master_name = info->mtd.name;
|
||||
info->mtd.name = (char *)0;
|
||||
|
||||
/* info->mtd.name == 0, means: don't bother checking
|
||||
<mtd-id> */
|
||||
mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes,
|
||||
&mtd_parts, 0);
|
||||
|
||||
/* Restore info->mtd.name */
|
||||
info->mtd.name = master_name;
|
||||
}
|
||||
|
||||
if (mtd_parts_nb <= 0 && pdata) {
|
||||
if (mtd_parts_nb <= 0) {
|
||||
mtd_parts = pdata->parts;
|
||||
mtd_parts_nb = pdata->nr_parts;
|
||||
}
|
||||
|
@ -483,7 +735,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
|||
info->partitioned = true;
|
||||
}
|
||||
|
||||
} else if (pdata && pdata->nr_parts) {
|
||||
} else if (pdata->nr_parts) {
|
||||
dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n",
|
||||
pdata->nr_parts, info->mtd.name);
|
||||
}
|
||||
|
@ -509,6 +761,11 @@ err_scan:
|
|||
err_clk_enable:
|
||||
clk_put(info->clk);
|
||||
|
||||
spin_lock_irq(&davinci_nand_lock);
|
||||
if (ecc_mode == NAND_ECC_HW_SYNDROME)
|
||||
ecc4_busy = false;
|
||||
spin_unlock_irq(&davinci_nand_lock);
|
||||
|
||||
err_ecc:
|
||||
err_clk:
|
||||
err_ioremap:
|
||||
|
@ -532,6 +789,11 @@ static int __exit nand_davinci_remove(struct platform_device *pdev)
|
|||
else
|
||||
status = del_mtd_device(&info->mtd);
|
||||
|
||||
spin_lock_irq(&davinci_nand_lock);
|
||||
if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)
|
||||
ecc4_busy = false;
|
||||
spin_unlock_irq(&davinci_nand_lock);
|
||||
|
||||
iounmap(info->base);
|
||||
iounmap(info->vaddr);
|
||||
|
||||
|
|
|
@ -138,7 +138,14 @@ static struct nand_ecclayout nand_hw_eccoob_8 = {
|
|||
static struct nand_ecclayout nand_hw_eccoob_16 = {
|
||||
.eccbytes = 5,
|
||||
.eccpos = {6, 7, 8, 9, 10},
|
||||
.oobfree = {{0, 6}, {12, 4}, }
|
||||
.oobfree = {{0, 5}, {11, 5}, }
|
||||
};
|
||||
|
||||
static struct nand_ecclayout nand_hw_eccoob_64 = {
|
||||
.eccbytes = 20,
|
||||
.eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26,
|
||||
38, 39, 40, 41, 42, 54, 55, 56, 57, 58},
|
||||
.oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, }
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
|
@ -192,7 +199,7 @@ static void wait_op_done(struct mxc_nand_host *host, int max_retries,
|
|||
}
|
||||
udelay(1);
|
||||
}
|
||||
if (max_retries <= 0)
|
||||
if (max_retries < 0)
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n",
|
||||
__func__, param);
|
||||
}
|
||||
|
@ -795,9 +802,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
|
|||
send_addr(host, (page_addr & 0xff), false);
|
||||
|
||||
if (host->pagesize_2k) {
|
||||
send_addr(host, (page_addr >> 8) & 0xFF, false);
|
||||
if (mtd->size >= 0x40000000)
|
||||
if (mtd->size >= 0x10000000) {
|
||||
/* paddr_8 - paddr_15 */
|
||||
send_addr(host, (page_addr >> 8) & 0xff, false);
|
||||
send_addr(host, (page_addr >> 16) & 0xff, true);
|
||||
} else
|
||||
/* paddr_8 - paddr_15 */
|
||||
send_addr(host, (page_addr >> 8) & 0xff, true);
|
||||
} else {
|
||||
/* One more address cycle for higher density devices */
|
||||
if (mtd->size >= 0x4000000) {
|
||||
|
@ -923,7 +934,6 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
|||
this->ecc.mode = NAND_ECC_HW;
|
||||
this->ecc.size = 512;
|
||||
this->ecc.bytes = 3;
|
||||
this->ecc.layout = &nand_hw_eccoob_8;
|
||||
tmp = readw(host->regs + NFC_CONFIG1);
|
||||
tmp |= NFC_ECC_EN;
|
||||
writew(tmp, host->regs + NFC_CONFIG1);
|
||||
|
@ -957,12 +967,44 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
|||
this->ecc.layout = &nand_hw_eccoob_16;
|
||||
}
|
||||
|
||||
host->pagesize_2k = 0;
|
||||
/* first scan to find the device and get the page size */
|
||||
if (nand_scan_ident(mtd, 1)) {
|
||||
err = -ENXIO;
|
||||
goto escan;
|
||||
}
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan(mtd, 1)) {
|
||||
DEBUG(MTD_DEBUG_LEVEL0,
|
||||
"MXC_ND: Unable to find any NAND device.\n");
|
||||
host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0;
|
||||
|
||||
if (this->ecc.mode == NAND_ECC_HW) {
|
||||
switch (mtd->oobsize) {
|
||||
case 8:
|
||||
this->ecc.layout = &nand_hw_eccoob_8;
|
||||
break;
|
||||
case 16:
|
||||
this->ecc.layout = &nand_hw_eccoob_16;
|
||||
break;
|
||||
case 64:
|
||||
this->ecc.layout = &nand_hw_eccoob_64;
|
||||
break;
|
||||
default:
|
||||
/* page size not handled by HW ECC */
|
||||
/* switching back to soft ECC */
|
||||
this->ecc.size = 512;
|
||||
this->ecc.bytes = 3;
|
||||
this->ecc.layout = &nand_hw_eccoob_8;
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
this->ecc.calculate = NULL;
|
||||
this->ecc.correct = NULL;
|
||||
this->ecc.hwctl = NULL;
|
||||
tmp = readw(host->regs + NFC_CONFIG1);
|
||||
tmp &= ~NFC_ECC_EN;
|
||||
writew(tmp, host->regs + NFC_CONFIG1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* second phase scan */
|
||||
if (nand_scan_tail(mtd)) {
|
||||
err = -ENXIO;
|
||||
goto escan;
|
||||
}
|
||||
|
@ -985,7 +1027,7 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
escan:
|
||||
free_irq(host->irq, NULL);
|
||||
free_irq(host->irq, host);
|
||||
eirq:
|
||||
iounmap(host->regs);
|
||||
eres:
|
||||
|
@ -1005,7 +1047,7 @@ static int __devexit mxcnd_remove(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
nand_release(&host->mtd);
|
||||
free_irq(host->irq, NULL);
|
||||
free_irq(host->irq, host);
|
||||
iounmap(host->regs);
|
||||
kfree(host);
|
||||
|
||||
|
|
|
@ -2756,7 +2756,8 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|||
* the out of band area
|
||||
*/
|
||||
chip->ecc.layout->oobavail = 0;
|
||||
for (i = 0; chip->ecc.layout->oobfree[i].length; i++)
|
||||
for (i = 0; chip->ecc.layout->oobfree[i].length
|
||||
&& i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++)
|
||||
chip->ecc.layout->oobavail +=
|
||||
chip->ecc.layout->oobfree[i].length;
|
||||
mtd->oobavail = chip->ecc.layout->oobavail;
|
||||
|
|
|
@ -428,8 +428,8 @@ EXPORT_SYMBOL(nand_calculate_ecc);
|
|||
int nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
|
||||
unsigned char *read_ecc, unsigned char *calc_ecc)
|
||||
{
|
||||
unsigned char b0, b1, b2;
|
||||
unsigned char byte_addr, bit_addr;
|
||||
unsigned char b0, b1, b2, bit_addr;
|
||||
unsigned int byte_addr;
|
||||
/* 256 or 512 bytes/ecc */
|
||||
const uint32_t eccsize_mult =
|
||||
(((struct nand_chip *)mtd->priv)->ecc.size) >> 8;
|
||||
|
|
|
@ -0,0 +1,776 @@
|
|||
/*
|
||||
* Copyright © 2004 Texas Instruments, Jian Zhang <jzhang@ti.com>
|
||||
* Copyright © 2004 Micron Technology Inc.
|
||||
* Copyright © 2004 David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
|
||||
#include <mach/gpmc.h>
|
||||
#include <mach/nand.h>
|
||||
|
||||
#define GPMC_IRQ_STATUS 0x18
|
||||
#define GPMC_ECC_CONFIG 0x1F4
|
||||
#define GPMC_ECC_CONTROL 0x1F8
|
||||
#define GPMC_ECC_SIZE_CONFIG 0x1FC
|
||||
#define GPMC_ECC1_RESULT 0x200
|
||||
|
||||
#define DRIVER_NAME "omap2-nand"
|
||||
|
||||
/* size (4 KiB) for IO mapping */
|
||||
#define NAND_IO_SIZE SZ_4K
|
||||
|
||||
#define NAND_WP_OFF 0
|
||||
#define NAND_WP_BIT 0x00000010
|
||||
#define WR_RD_PIN_MONITORING 0x00600000
|
||||
|
||||
#define GPMC_BUF_FULL 0x00000001
|
||||
#define GPMC_BUF_EMPTY 0x00000000
|
||||
|
||||
#define NAND_Ecc_P1e (1 << 0)
|
||||
#define NAND_Ecc_P2e (1 << 1)
|
||||
#define NAND_Ecc_P4e (1 << 2)
|
||||
#define NAND_Ecc_P8e (1 << 3)
|
||||
#define NAND_Ecc_P16e (1 << 4)
|
||||
#define NAND_Ecc_P32e (1 << 5)
|
||||
#define NAND_Ecc_P64e (1 << 6)
|
||||
#define NAND_Ecc_P128e (1 << 7)
|
||||
#define NAND_Ecc_P256e (1 << 8)
|
||||
#define NAND_Ecc_P512e (1 << 9)
|
||||
#define NAND_Ecc_P1024e (1 << 10)
|
||||
#define NAND_Ecc_P2048e (1 << 11)
|
||||
|
||||
#define NAND_Ecc_P1o (1 << 16)
|
||||
#define NAND_Ecc_P2o (1 << 17)
|
||||
#define NAND_Ecc_P4o (1 << 18)
|
||||
#define NAND_Ecc_P8o (1 << 19)
|
||||
#define NAND_Ecc_P16o (1 << 20)
|
||||
#define NAND_Ecc_P32o (1 << 21)
|
||||
#define NAND_Ecc_P64o (1 << 22)
|
||||
#define NAND_Ecc_P128o (1 << 23)
|
||||
#define NAND_Ecc_P256o (1 << 24)
|
||||
#define NAND_Ecc_P512o (1 << 25)
|
||||
#define NAND_Ecc_P1024o (1 << 26)
|
||||
#define NAND_Ecc_P2048o (1 << 27)
|
||||
|
||||
#define TF(value) (value ? 1 : 0)
|
||||
|
||||
#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0)
|
||||
#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1)
|
||||
#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2)
|
||||
#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3)
|
||||
#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4)
|
||||
#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5)
|
||||
#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6)
|
||||
#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7)
|
||||
|
||||
#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0)
|
||||
#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1)
|
||||
#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2)
|
||||
#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3)
|
||||
#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4)
|
||||
#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5)
|
||||
#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6)
|
||||
#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7)
|
||||
|
||||
#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0)
|
||||
#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1)
|
||||
#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2)
|
||||
#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3)
|
||||
#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4)
|
||||
#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5)
|
||||
#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6)
|
||||
#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7)
|
||||
|
||||
#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0)
|
||||
#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1)
|
||||
#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2)
|
||||
#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3)
|
||||
#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4)
|
||||
#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5)
|
||||
#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6)
|
||||
#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7)
|
||||
|
||||
#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0)
|
||||
#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1)
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
static const char *part_probes[] = { "cmdlinepart", NULL };
|
||||
#endif
|
||||
|
||||
struct omap_nand_info {
|
||||
struct nand_hw_control controller;
|
||||
struct omap_nand_platform_data *pdata;
|
||||
struct mtd_info mtd;
|
||||
struct mtd_partition *parts;
|
||||
struct nand_chip nand;
|
||||
struct platform_device *pdev;
|
||||
|
||||
int gpmc_cs;
|
||||
unsigned long phys_base;
|
||||
void __iomem *gpmc_cs_baseaddr;
|
||||
void __iomem *gpmc_baseaddr;
|
||||
};
|
||||
|
||||
/**
|
||||
* omap_nand_wp - This function enable or disable the Write Protect feature
|
||||
* @mtd: MTD device structure
|
||||
* @mode: WP ON/OFF
|
||||
*/
|
||||
static void omap_nand_wp(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd,
|
||||
struct omap_nand_info, mtd);
|
||||
|
||||
unsigned long config = __raw_readl(info->gpmc_baseaddr + GPMC_CONFIG);
|
||||
|
||||
if (mode)
|
||||
config &= ~(NAND_WP_BIT); /* WP is ON */
|
||||
else
|
||||
config |= (NAND_WP_BIT); /* WP is OFF */
|
||||
|
||||
__raw_writel(config, (info->gpmc_baseaddr + GPMC_CONFIG));
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_hwcontrol - hardware specific access to control-lines
|
||||
* @mtd: MTD device structure
|
||||
* @cmd: command to device
|
||||
* @ctrl:
|
||||
* NAND_NCE: bit 0 -> don't care
|
||||
* NAND_CLE: bit 1 -> Command Latch
|
||||
* NAND_ALE: bit 2 -> Address Latch
|
||||
*
|
||||
* NOTE: boards may use different bits for these!!
|
||||
*/
|
||||
static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd,
|
||||
struct omap_nand_info, mtd);
|
||||
switch (ctrl) {
|
||||
case NAND_CTRL_CHANGE | NAND_CTRL_CLE:
|
||||
info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
|
||||
GPMC_CS_NAND_COMMAND;
|
||||
info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
|
||||
GPMC_CS_NAND_DATA;
|
||||
break;
|
||||
|
||||
case NAND_CTRL_CHANGE | NAND_CTRL_ALE:
|
||||
info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
|
||||
GPMC_CS_NAND_ADDRESS;
|
||||
info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
|
||||
GPMC_CS_NAND_DATA;
|
||||
break;
|
||||
|
||||
case NAND_CTRL_CHANGE | NAND_NCE:
|
||||
info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
|
||||
GPMC_CS_NAND_DATA;
|
||||
info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
|
||||
GPMC_CS_NAND_DATA;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
__raw_writeb(cmd, info->nand.IO_ADDR_W);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_read_buf16 - read data from NAND controller into buffer
|
||||
* @mtd: MTD device structure
|
||||
* @buf: buffer to store date
|
||||
* @len: number of bytes to read
|
||||
*/
|
||||
static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
|
||||
{
|
||||
struct nand_chip *nand = mtd->priv;
|
||||
|
||||
__raw_readsw(nand->IO_ADDR_R, buf, len / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_write_buf16 - write buffer to NAND controller
|
||||
* @mtd: MTD device structure
|
||||
* @buf: data buffer
|
||||
* @len: number of bytes to write
|
||||
*/
|
||||
static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd,
|
||||
struct omap_nand_info, mtd);
|
||||
u16 *p = (u16 *) buf;
|
||||
|
||||
/* FIXME try bursts of writesw() or DMA ... */
|
||||
len >>= 1;
|
||||
|
||||
while (len--) {
|
||||
writew(*p++, info->nand.IO_ADDR_W);
|
||||
|
||||
while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr +
|
||||
GPMC_STATUS) & GPMC_BUF_FULL))
|
||||
;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* omap_verify_buf - Verify chip data against buffer
|
||||
* @mtd: MTD device structure
|
||||
* @buf: buffer containing the data to compare
|
||||
* @len: number of bytes to compare
|
||||
*/
|
||||
static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
u16 *p = (u16 *) buf;
|
||||
|
||||
len >>= 1;
|
||||
while (len--) {
|
||||
if (*p++ != cpu_to_le16(readw(info->nand.IO_ADDR_R)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_OMAP_HWECC
|
||||
/**
|
||||
* omap_hwecc_init - Initialize the HW ECC for NAND flash in GPMC controller
|
||||
* @mtd: MTD device structure
|
||||
*/
|
||||
static void omap_hwecc_init(struct mtd_info *mtd)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
unsigned long val = 0x0;
|
||||
|
||||
/* Read from ECC Control Register */
|
||||
val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONTROL);
|
||||
/* Clear all ECC | Enable Reg1 */
|
||||
val = ((0x00000001<<8) | 0x00000001);
|
||||
__raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
|
||||
|
||||
/* Read from ECC Size Config Register */
|
||||
val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
|
||||
/* ECCSIZE1=512 | Select eccResultsize[0-3] */
|
||||
val = ((((chip->ecc.size >> 1) - 1) << 22) | (0x0000000F));
|
||||
__raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
|
||||
}
|
||||
|
||||
/**
|
||||
* gen_true_ecc - This function will generate true ECC value
|
||||
* @ecc_buf: buffer to store ecc code
|
||||
*
|
||||
* This generated true ECC value can be used when correcting
|
||||
* data read from NAND flash memory core
|
||||
*/
|
||||
static void gen_true_ecc(u8 *ecc_buf)
|
||||
{
|
||||
u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) |
|
||||
((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8);
|
||||
|
||||
ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) |
|
||||
P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp));
|
||||
ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) |
|
||||
P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp));
|
||||
ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) |
|
||||
P1e(tmp) | P2048o(tmp) | P2048e(tmp));
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data
|
||||
* @ecc_data1: ecc code from nand spare area
|
||||
* @ecc_data2: ecc code from hardware register obtained from hardware ecc
|
||||
* @page_data: page data
|
||||
*
|
||||
* This function compares two ECC's and indicates if there is an error.
|
||||
* If the error can be corrected it will be corrected to the buffer.
|
||||
*/
|
||||
static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
|
||||
u8 *ecc_data2, /* read from register */
|
||||
u8 *page_data)
|
||||
{
|
||||
uint i;
|
||||
u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8];
|
||||
u8 comp0_bit[8], comp1_bit[8], comp2_bit[8];
|
||||
u8 ecc_bit[24];
|
||||
u8 ecc_sum = 0;
|
||||
u8 find_bit = 0;
|
||||
uint find_byte = 0;
|
||||
int isEccFF;
|
||||
|
||||
isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF);
|
||||
|
||||
gen_true_ecc(ecc_data1);
|
||||
gen_true_ecc(ecc_data2);
|
||||
|
||||
for (i = 0; i <= 2; i++) {
|
||||
*(ecc_data1 + i) = ~(*(ecc_data1 + i));
|
||||
*(ecc_data2 + i) = ~(*(ecc_data2 + i));
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
tmp0_bit[i] = *ecc_data1 % 2;
|
||||
*ecc_data1 = *ecc_data1 / 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
tmp1_bit[i] = *(ecc_data1 + 1) % 2;
|
||||
*(ecc_data1 + 1) = *(ecc_data1 + 1) / 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
tmp2_bit[i] = *(ecc_data1 + 2) % 2;
|
||||
*(ecc_data1 + 2) = *(ecc_data1 + 2) / 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
comp0_bit[i] = *ecc_data2 % 2;
|
||||
*ecc_data2 = *ecc_data2 / 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
comp1_bit[i] = *(ecc_data2 + 1) % 2;
|
||||
*(ecc_data2 + 1) = *(ecc_data2 + 1) / 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
comp2_bit[i] = *(ecc_data2 + 2) % 2;
|
||||
*(ecc_data2 + 2) = *(ecc_data2 + 2) / 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2];
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i];
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i];
|
||||
|
||||
ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0];
|
||||
ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1];
|
||||
|
||||
for (i = 0; i < 24; i++)
|
||||
ecc_sum += ecc_bit[i];
|
||||
|
||||
switch (ecc_sum) {
|
||||
case 0:
|
||||
/* Not reached because this function is not called if
|
||||
* ECC values are equal
|
||||
*/
|
||||
return 0;
|
||||
|
||||
case 1:
|
||||
/* Uncorrectable error */
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n");
|
||||
return -1;
|
||||
|
||||
case 11:
|
||||
/* UN-Correctable error */
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR B\n");
|
||||
return -1;
|
||||
|
||||
case 12:
|
||||
/* Correctable error */
|
||||
find_byte = (ecc_bit[23] << 8) +
|
||||
(ecc_bit[21] << 7) +
|
||||
(ecc_bit[19] << 6) +
|
||||
(ecc_bit[17] << 5) +
|
||||
(ecc_bit[15] << 4) +
|
||||
(ecc_bit[13] << 3) +
|
||||
(ecc_bit[11] << 2) +
|
||||
(ecc_bit[9] << 1) +
|
||||
ecc_bit[7];
|
||||
|
||||
find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1];
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "Correcting single bit ECC error at "
|
||||
"offset: %d, bit: %d\n", find_byte, find_bit);
|
||||
|
||||
page_data[find_byte] ^= (1 << find_bit);
|
||||
|
||||
return 0;
|
||||
default:
|
||||
if (isEccFF) {
|
||||
if (ecc_data2[0] == 0 &&
|
||||
ecc_data2[1] == 0 &&
|
||||
ecc_data2[2] == 0)
|
||||
return 0;
|
||||
}
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "UNCORRECTED_ERROR default\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_correct_data - Compares the ECC read with HW generated ECC
|
||||
* @mtd: MTD device structure
|
||||
* @dat: page data
|
||||
* @read_ecc: ecc read from nand flash
|
||||
* @calc_ecc: ecc read from HW ECC registers
|
||||
*
|
||||
* Compares the ecc read from nand spare area with ECC registers values
|
||||
* and if ECC's mismached, it will call 'omap_compare_ecc' for error detection
|
||||
* and correction.
|
||||
*/
|
||||
static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
int blockCnt = 0, i = 0, ret = 0;
|
||||
|
||||
/* Ex NAND_ECC_HW12_2048 */
|
||||
if ((info->nand.ecc.mode == NAND_ECC_HW) &&
|
||||
(info->nand.ecc.size == 2048))
|
||||
blockCnt = 4;
|
||||
else
|
||||
blockCnt = 1;
|
||||
|
||||
for (i = 0; i < blockCnt; i++) {
|
||||
if (memcmp(read_ecc, calc_ecc, 3) != 0) {
|
||||
ret = omap_compare_ecc(read_ecc, calc_ecc, dat);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
read_ecc += 3;
|
||||
calc_ecc += 3;
|
||||
dat += 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_calcuate_ecc - Generate non-inverted ECC bytes.
|
||||
* @mtd: MTD device structure
|
||||
* @dat: The pointer to data on which ecc is computed
|
||||
* @ecc_code: The ecc_code buffer
|
||||
*
|
||||
* Using noninverted ECC can be considered ugly since writing a blank
|
||||
* page ie. padding will clear the ECC bytes. This is no problem as long
|
||||
* nobody is trying to write data on the seemingly unused page. Reading
|
||||
* an erased page will produce an ECC mismatch between generated and read
|
||||
* ECC bytes that has to be dealt with separately.
|
||||
*/
|
||||
static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
|
||||
u_char *ecc_code)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
unsigned long val = 0x0;
|
||||
unsigned long reg;
|
||||
|
||||
/* Start Reading from HW ECC1_Result = 0x200 */
|
||||
reg = (unsigned long)(info->gpmc_baseaddr + GPMC_ECC1_RESULT);
|
||||
val = __raw_readl(reg);
|
||||
*ecc_code++ = val; /* P128e, ..., P1e */
|
||||
*ecc_code++ = val >> 16; /* P128o, ..., P1o */
|
||||
/* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */
|
||||
*ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
|
||||
reg += 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_enable_hwecc - This function enables the hardware ecc functionality
|
||||
* @mtd: MTD device structure
|
||||
* @mode: Read/Write mode
|
||||
*/
|
||||
static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
|
||||
unsigned long val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONFIG);
|
||||
|
||||
switch (mode) {
|
||||
case NAND_ECC_READ:
|
||||
__raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
|
||||
/* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
|
||||
val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
|
||||
break;
|
||||
case NAND_ECC_READSYN:
|
||||
__raw_writel(0x100, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
|
||||
/* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
|
||||
val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
|
||||
break;
|
||||
case NAND_ECC_WRITE:
|
||||
__raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
|
||||
/* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
|
||||
val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
|
||||
break;
|
||||
default:
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "Error: Unrecognized Mode[%d]!\n",
|
||||
mode);
|
||||
break;
|
||||
}
|
||||
|
||||
__raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONFIG);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* omap_wait - wait until the command is done
|
||||
* @mtd: MTD device structure
|
||||
* @chip: NAND Chip structure
|
||||
*
|
||||
* Wait function is called during Program and erase operations and
|
||||
* the way it is called from MTD layer, we should wait till the NAND
|
||||
* chip is ready after the programming/erase operation has completed.
|
||||
*
|
||||
* Erase can take up to 400ms and program up to 20ms according to
|
||||
* general NAND and SmartMedia specs
|
||||
*/
|
||||
static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
unsigned long timeo = jiffies;
|
||||
int status, state = this->state;
|
||||
|
||||
if (state == FL_ERASING)
|
||||
timeo += (HZ * 400) / 1000;
|
||||
else
|
||||
timeo += (HZ * 20) / 1000;
|
||||
|
||||
this->IO_ADDR_W = (void *) info->gpmc_cs_baseaddr +
|
||||
GPMC_CS_NAND_COMMAND;
|
||||
this->IO_ADDR_R = (void *) info->gpmc_cs_baseaddr + GPMC_CS_NAND_DATA;
|
||||
|
||||
__raw_writeb(NAND_CMD_STATUS & 0xFF, this->IO_ADDR_W);
|
||||
|
||||
while (time_before(jiffies, timeo)) {
|
||||
status = __raw_readb(this->IO_ADDR_R);
|
||||
if (!(status & 0x40))
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_dev_ready - calls the platform specific dev_ready function
|
||||
* @mtd: MTD device structure
|
||||
*/
|
||||
static int omap_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
unsigned int val = __raw_readl(info->gpmc_baseaddr + GPMC_IRQ_STATUS);
|
||||
|
||||
if ((val & 0x100) == 0x100) {
|
||||
/* Clear IRQ Interrupt */
|
||||
val |= 0x100;
|
||||
val &= ~(0x0);
|
||||
__raw_writel(val, info->gpmc_baseaddr + GPMC_IRQ_STATUS);
|
||||
} else {
|
||||
unsigned int cnt = 0;
|
||||
while (cnt++ < 0x1FF) {
|
||||
if ((val & 0x100) == 0x100)
|
||||
return 0;
|
||||
val = __raw_readl(info->gpmc_baseaddr +
|
||||
GPMC_IRQ_STATUS);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __devinit omap_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_nand_info *info;
|
||||
struct omap_nand_platform_data *pdata;
|
||||
int err;
|
||||
unsigned long val;
|
||||
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (pdata == NULL) {
|
||||
dev_err(&pdev->dev, "platform data missing\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
spin_lock_init(&info->controller.lock);
|
||||
init_waitqueue_head(&info->controller.wq);
|
||||
|
||||
info->pdev = pdev;
|
||||
|
||||
info->gpmc_cs = pdata->cs;
|
||||
info->gpmc_baseaddr = pdata->gpmc_baseaddr;
|
||||
info->gpmc_cs_baseaddr = pdata->gpmc_cs_baseaddr;
|
||||
|
||||
info->mtd.priv = &info->nand;
|
||||
info->mtd.name = dev_name(&pdev->dev);
|
||||
info->mtd.owner = THIS_MODULE;
|
||||
|
||||
err = gpmc_cs_request(info->gpmc_cs, NAND_IO_SIZE, &info->phys_base);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "Cannot request GPMC CS\n");
|
||||
goto out_free_info;
|
||||
}
|
||||
|
||||
/* Enable RD PIN Monitoring Reg */
|
||||
if (pdata->dev_ready) {
|
||||
val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1);
|
||||
val |= WR_RD_PIN_MONITORING;
|
||||
gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG1, val);
|
||||
}
|
||||
|
||||
val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG7);
|
||||
val &= ~(0xf << 8);
|
||||
val |= (0xc & 0xf) << 8;
|
||||
gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG7, val);
|
||||
|
||||
/* NAND write protect off */
|
||||
omap_nand_wp(&info->mtd, NAND_WP_OFF);
|
||||
|
||||
if (!request_mem_region(info->phys_base, NAND_IO_SIZE,
|
||||
pdev->dev.driver->name)) {
|
||||
err = -EBUSY;
|
||||
goto out_free_cs;
|
||||
}
|
||||
|
||||
info->nand.IO_ADDR_R = ioremap(info->phys_base, NAND_IO_SIZE);
|
||||
if (!info->nand.IO_ADDR_R) {
|
||||
err = -ENOMEM;
|
||||
goto out_release_mem_region;
|
||||
}
|
||||
info->nand.controller = &info->controller;
|
||||
|
||||
info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
|
||||
info->nand.cmd_ctrl = omap_hwcontrol;
|
||||
|
||||
/* REVISIT: only supports 16-bit NAND flash */
|
||||
|
||||
info->nand.read_buf = omap_read_buf16;
|
||||
info->nand.write_buf = omap_write_buf16;
|
||||
info->nand.verify_buf = omap_verify_buf;
|
||||
|
||||
/*
|
||||
* If RDY/BSY line is connected to OMAP then use the omap ready
|
||||
* funcrtion and the generic nand_wait function which reads the status
|
||||
* register after monitoring the RDY/BSY line.Otherwise use a standard
|
||||
* chip delay which is slightly more than tR (AC Timing) of the NAND
|
||||
* device and read status register until you get a failure or success
|
||||
*/
|
||||
if (pdata->dev_ready) {
|
||||
info->nand.dev_ready = omap_dev_ready;
|
||||
info->nand.chip_delay = 0;
|
||||
} else {
|
||||
info->nand.waitfunc = omap_wait;
|
||||
info->nand.chip_delay = 50;
|
||||
}
|
||||
|
||||
info->nand.options |= NAND_SKIP_BBTSCAN;
|
||||
if ((gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1) & 0x3000)
|
||||
== 0x1000)
|
||||
info->nand.options |= NAND_BUSWIDTH_16;
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_OMAP_HWECC
|
||||
info->nand.ecc.bytes = 3;
|
||||
info->nand.ecc.size = 512;
|
||||
info->nand.ecc.calculate = omap_calculate_ecc;
|
||||
info->nand.ecc.hwctl = omap_enable_hwecc;
|
||||
info->nand.ecc.correct = omap_correct_data;
|
||||
info->nand.ecc.mode = NAND_ECC_HW;
|
||||
|
||||
/* init HW ECC */
|
||||
omap_hwecc_init(&info->mtd);
|
||||
#else
|
||||
info->nand.ecc.mode = NAND_ECC_SOFT;
|
||||
#endif
|
||||
|
||||
/* DIP switches on some boards change between 8 and 16 bit
|
||||
* bus widths for flash. Try the other width if the first try fails.
|
||||
*/
|
||||
if (nand_scan(&info->mtd, 1)) {
|
||||
info->nand.options ^= NAND_BUSWIDTH_16;
|
||||
if (nand_scan(&info->mtd, 1)) {
|
||||
err = -ENXIO;
|
||||
goto out_release_mem_region;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
|
||||
if (err > 0)
|
||||
add_mtd_partitions(&info->mtd, info->parts, err);
|
||||
else if (pdata->parts)
|
||||
add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
|
||||
else
|
||||
#endif
|
||||
add_mtd_device(&info->mtd);
|
||||
|
||||
platform_set_drvdata(pdev, &info->mtd);
|
||||
|
||||
return 0;
|
||||
|
||||
out_release_mem_region:
|
||||
release_mem_region(info->phys_base, NAND_IO_SIZE);
|
||||
out_free_cs:
|
||||
gpmc_cs_free(info->gpmc_cs);
|
||||
out_free_info:
|
||||
kfree(info);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int omap_nand_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mtd_info *mtd = platform_get_drvdata(pdev);
|
||||
struct omap_nand_info *info = mtd->priv;
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
/* Release NAND device, its internal structures and partitions */
|
||||
nand_release(&info->mtd);
|
||||
iounmap(info->nand.IO_ADDR_R);
|
||||
kfree(&info->mtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver omap_nand_driver = {
|
||||
.probe = omap_nand_probe,
|
||||
.remove = omap_nand_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init omap_nand_init(void)
|
||||
{
|
||||
printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME);
|
||||
return platform_driver_register(&omap_nand_driver);
|
||||
}
|
||||
|
||||
static void __exit omap_nand_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&omap_nand_driver);
|
||||
}
|
||||
|
||||
module_init(omap_nand_init);
|
||||
module_exit(omap_nand_exit);
|
||||
|
||||
MODULE_ALIAS(DRIVER_NAME);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards");
|
|
@ -47,6 +47,28 @@ static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl
|
|||
writeb(cmd, nc->IO_ADDR_W + offs);
|
||||
}
|
||||
|
||||
static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
void __iomem *io_base = chip->IO_ADDR_R;
|
||||
uint64_t *buf64;
|
||||
int i = 0;
|
||||
|
||||
while (len && (unsigned long)buf & 7) {
|
||||
*buf++ = readb(io_base);
|
||||
len--;
|
||||
}
|
||||
buf64 = (uint64_t *)buf;
|
||||
while (i < len/8) {
|
||||
uint64_t x;
|
||||
asm ("ldrd\t%0, [%1]" : "=r" (x) : "r" (io_base));
|
||||
buf64[i++] = x;
|
||||
}
|
||||
i *= 8;
|
||||
while (i < len)
|
||||
buf[i++] = readb(io_base);
|
||||
}
|
||||
|
||||
static int __init orion_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
|
@ -83,6 +105,7 @@ static int __init orion_nand_probe(struct platform_device *pdev)
|
|||
nc->priv = board;
|
||||
nc->IO_ADDR_R = nc->IO_ADDR_W = io_base;
|
||||
nc->cmd_ctrl = orion_nand_cmd_ctrl;
|
||||
nc->read_buf = orion_nand_read_buf;
|
||||
nc->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
if (board->chip_delay)
|
||||
|
|
|
@ -61,6 +61,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
|
|||
data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl;
|
||||
data->chip.dev_ready = pdata->ctrl.dev_ready;
|
||||
data->chip.select_chip = pdata->ctrl.select_chip;
|
||||
data->chip.write_buf = pdata->ctrl.write_buf;
|
||||
data->chip.read_buf = pdata->ctrl.read_buf;
|
||||
data->chip.chip_delay = pdata->chip.chip_delay;
|
||||
data->chip.options |= pdata->chip.options;
|
||||
|
||||
|
@ -70,6 +72,13 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
/* Handle any platform specific setup */
|
||||
if (pdata->ctrl.probe) {
|
||||
res = pdata->ctrl.probe(pdev);
|
||||
if (res)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Scan to find existance of the device */
|
||||
if (nand_scan(&data->mtd, 1)) {
|
||||
res = -ENXIO;
|
||||
|
@ -86,6 +95,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
if (pdata->chip.set_parts)
|
||||
pdata->chip.set_parts(data->mtd.size, &pdata->chip);
|
||||
if (pdata->chip.partitions) {
|
||||
data->parts = pdata->chip.partitions;
|
||||
res = add_mtd_partitions(&data->mtd, data->parts,
|
||||
|
@ -99,6 +110,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
|
|||
|
||||
nand_release(&data->mtd);
|
||||
out:
|
||||
if (pdata->ctrl.remove)
|
||||
pdata->ctrl.remove(pdev);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
iounmap(data->io_base);
|
||||
kfree(data);
|
||||
|
@ -111,15 +124,15 @@ out:
|
|||
static int __devexit plat_nand_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct plat_nand_data *data = platform_get_drvdata(pdev);
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
struct platform_nand_data *pdata = pdev->dev.platform_data;
|
||||
#endif
|
||||
|
||||
nand_release(&data->mtd);
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
if (data->parts && data->parts != pdata->chip.partitions)
|
||||
kfree(data->parts);
|
||||
#endif
|
||||
if (pdata->ctrl.remove)
|
||||
pdata->ctrl.remove(pdev);
|
||||
iounmap(data->io_base);
|
||||
kfree(data);
|
||||
|
||||
|
@ -128,7 +141,7 @@ static int __devexit plat_nand_remove(struct platform_device *pdev)
|
|||
|
||||
static struct platform_driver plat_nand_driver = {
|
||||
.probe = plat_nand_probe,
|
||||
.remove = plat_nand_remove,
|
||||
.remove = __devexit_p(plat_nand_remove),
|
||||
.driver = {
|
||||
.name = "gen_nand",
|
||||
.owner = THIS_MODULE,
|
||||
|
|
|
@ -74,6 +74,14 @@ static struct nand_ecclayout nand_hw_eccoob = {
|
|||
|
||||
struct s3c2410_nand_info;
|
||||
|
||||
/**
|
||||
* struct s3c2410_nand_mtd - driver MTD structure
|
||||
* @mtd: The MTD instance to pass to the MTD layer.
|
||||
* @chip: The NAND chip information.
|
||||
* @set: The platform information supplied for this set of NAND chips.
|
||||
* @info: Link back to the hardware information.
|
||||
* @scan_res: The result from calling nand_scan_ident().
|
||||
*/
|
||||
struct s3c2410_nand_mtd {
|
||||
struct mtd_info mtd;
|
||||
struct nand_chip chip;
|
||||
|
@ -90,6 +98,21 @@ enum s3c_cpu_type {
|
|||
|
||||
/* overview of the s3c2410 nand state */
|
||||
|
||||
/**
|
||||
* struct s3c2410_nand_info - NAND controller state.
|
||||
* @mtds: An array of MTD instances on this controoler.
|
||||
* @platform: The platform data for this board.
|
||||
* @device: The platform device we bound to.
|
||||
* @area: The IO area resource that came from request_mem_region().
|
||||
* @clk: The clock resource for this controller.
|
||||
* @regs: The area mapped for the hardware registers described by @area.
|
||||
* @sel_reg: Pointer to the register controlling the NAND selection.
|
||||
* @sel_bit: The bit in @sel_reg to select the NAND chip.
|
||||
* @mtd_count: The number of MTDs created from this controller.
|
||||
* @save_sel: The contents of @sel_reg to be saved over suspend.
|
||||
* @clk_rate: The clock rate from @clk.
|
||||
* @cpu_type: The exact type of this controller.
|
||||
*/
|
||||
struct s3c2410_nand_info {
|
||||
/* mtd info */
|
||||
struct nand_hw_control controller;
|
||||
|
@ -145,12 +168,19 @@ static inline int allow_clk_stop(struct s3c2410_nand_info *info)
|
|||
|
||||
#define NS_IN_KHZ 1000000
|
||||
|
||||
/**
|
||||
* s3c_nand_calc_rate - calculate timing data.
|
||||
* @wanted: The cycle time in nanoseconds.
|
||||
* @clk: The clock rate in kHz.
|
||||
* @max: The maximum divider value.
|
||||
*
|
||||
* Calculate the timing value from the given parameters.
|
||||
*/
|
||||
static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = (wanted * clk) / NS_IN_KHZ;
|
||||
result++;
|
||||
result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ);
|
||||
|
||||
pr_debug("result %d from %ld, %d\n", result, clk, wanted);
|
||||
|
||||
|
@ -169,13 +199,21 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
|
|||
|
||||
/* controller setup */
|
||||
|
||||
/**
|
||||
* s3c2410_nand_setrate - setup controller timing information.
|
||||
* @info: The controller instance.
|
||||
*
|
||||
* Given the information supplied by the platform, calculate and set
|
||||
* the necessary timing registers in the hardware to generate the
|
||||
* necessary timing cycles to the hardware.
|
||||
*/
|
||||
static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
|
||||
{
|
||||
struct s3c2410_platform_nand *plat = info->platform;
|
||||
int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
|
||||
int tacls, twrph0, twrph1;
|
||||
unsigned long clkrate = clk_get_rate(info->clk);
|
||||
unsigned long set, cfg, mask;
|
||||
unsigned long uninitialized_var(set), cfg, uninitialized_var(mask);
|
||||
unsigned long flags;
|
||||
|
||||
/* calculate the timing information for the controller */
|
||||
|
@ -215,9 +253,9 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
|
|||
|
||||
case TYPE_S3C2440:
|
||||
case TYPE_S3C2412:
|
||||
mask = (S3C2410_NFCONF_TACLS(tacls_max - 1) |
|
||||
S3C2410_NFCONF_TWRPH0(7) |
|
||||
S3C2410_NFCONF_TWRPH1(7));
|
||||
mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) |
|
||||
S3C2440_NFCONF_TWRPH0(7) |
|
||||
S3C2440_NFCONF_TWRPH1(7));
|
||||
|
||||
set = S3C2440_NFCONF_TACLS(tacls - 1);
|
||||
set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
|
||||
|
@ -225,14 +263,9 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
|
|||
break;
|
||||
|
||||
default:
|
||||
/* keep compiler happy */
|
||||
mask = 0;
|
||||
set = 0;
|
||||
BUG();
|
||||
}
|
||||
|
||||
dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
cfg = readl(info->regs + S3C2410_NFCONF);
|
||||
|
@ -242,9 +275,18 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
|
|||
|
||||
local_irq_restore(flags);
|
||||
|
||||
dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* s3c2410_nand_inithw - basic hardware initialisation
|
||||
* @info: The hardware state.
|
||||
*
|
||||
* Do the basic initialisation of the hardware, using s3c2410_nand_setrate()
|
||||
* to setup the hardware access speeds and set the controller to be enabled.
|
||||
*/
|
||||
static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
|
||||
{
|
||||
int ret;
|
||||
|
@ -268,8 +310,19 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* select chip */
|
||||
|
||||
/**
|
||||
* s3c2410_nand_select_chip - select the given nand chip
|
||||
* @mtd: The MTD instance for this chip.
|
||||
* @chip: The chip number.
|
||||
*
|
||||
* This is called by the MTD layer to either select a given chip for the
|
||||
* @mtd instance, or to indicate that the access has finished and the
|
||||
* chip can be de-selected.
|
||||
*
|
||||
* The routine ensures that the nFCE line is correctly setup, and any
|
||||
* platform specific selection code is called to route nFCE to the specific
|
||||
* chip.
|
||||
*/
|
||||
static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
struct s3c2410_nand_info *info;
|
||||
|
@ -530,7 +583,16 @@ static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
|||
static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
{
|
||||
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
readsl(info->regs + S3C2440_NFDATA, buf, len / 4);
|
||||
|
||||
readsl(info->regs + S3C2440_NFDATA, buf, len >> 2);
|
||||
|
||||
/* cleanup if we've got less than a word to do */
|
||||
if (len & 3) {
|
||||
buf += len & ~3;
|
||||
|
||||
for (; len & 3; len--)
|
||||
*buf++ = readb(info->regs + S3C2440_NFDATA);
|
||||
}
|
||||
}
|
||||
|
||||
static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
|
@ -542,7 +604,16 @@ static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int
|
|||
static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
{
|
||||
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
writesl(info->regs + S3C2440_NFDATA, buf, len / 4);
|
||||
|
||||
writesl(info->regs + S3C2440_NFDATA, buf, len >> 2);
|
||||
|
||||
/* cleanup any fractional write */
|
||||
if (len & 3) {
|
||||
buf += len & ~3;
|
||||
|
||||
for (; len & 3; len--, buf++)
|
||||
writeb(*buf, info->regs + S3C2440_NFDATA);
|
||||
}
|
||||
}
|
||||
|
||||
/* cpufreq driver support */
|
||||
|
@ -593,7 +664,7 @@ static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *inf
|
|||
|
||||
/* device management functions */
|
||||
|
||||
static int s3c2410_nand_remove(struct platform_device *pdev)
|
||||
static int s3c24xx_nand_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct s3c2410_nand_info *info = to_nand_info(pdev);
|
||||
|
||||
|
@ -645,17 +716,31 @@ static int s3c2410_nand_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
const char *part_probes[] = { "cmdlinepart", NULL };
|
||||
static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
|
||||
struct s3c2410_nand_mtd *mtd,
|
||||
struct s3c2410_nand_set *set)
|
||||
{
|
||||
struct mtd_partition *part_info;
|
||||
int nr_part = 0;
|
||||
|
||||
if (set == NULL)
|
||||
return add_mtd_device(&mtd->mtd);
|
||||
|
||||
if (set->nr_partitions > 0 && set->partitions != NULL) {
|
||||
return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);
|
||||
if (set->nr_partitions == 0) {
|
||||
mtd->mtd.name = set->name;
|
||||
nr_part = parse_mtd_partitions(&mtd->mtd, part_probes,
|
||||
&part_info, 0);
|
||||
} else {
|
||||
if (set->nr_partitions > 0 && set->partitions != NULL) {
|
||||
nr_part = set->nr_partitions;
|
||||
part_info = set->partitions;
|
||||
}
|
||||
}
|
||||
|
||||
if (nr_part > 0 && part_info)
|
||||
return add_mtd_partitions(&mtd->mtd, part_info, nr_part);
|
||||
|
||||
return add_mtd_device(&mtd->mtd);
|
||||
}
|
||||
#else
|
||||
|
@ -667,11 +752,16 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
|
|||
}
|
||||
#endif
|
||||
|
||||
/* s3c2410_nand_init_chip
|
||||
/**
|
||||
* s3c2410_nand_init_chip - initialise a single instance of an chip
|
||||
* @info: The base NAND controller the chip is on.
|
||||
* @nmtd: The new controller MTD instance to fill in.
|
||||
* @set: The information passed from the board specific platform data.
|
||||
*
|
||||
* init a single instance of an chip
|
||||
*/
|
||||
|
||||
* Initialise the given @nmtd from the information in @info and @set. This
|
||||
* readies the structure for use with the MTD layer functions by ensuring
|
||||
* all pointers are setup and the necessary control routines selected.
|
||||
*/
|
||||
static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
||||
struct s3c2410_nand_mtd *nmtd,
|
||||
struct s3c2410_nand_set *set)
|
||||
|
@ -757,14 +847,40 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
|||
|
||||
if (set->disable_ecc)
|
||||
chip->ecc.mode = NAND_ECC_NONE;
|
||||
|
||||
switch (chip->ecc.mode) {
|
||||
case NAND_ECC_NONE:
|
||||
dev_info(info->device, "NAND ECC disabled\n");
|
||||
break;
|
||||
case NAND_ECC_SOFT:
|
||||
dev_info(info->device, "NAND soft ECC\n");
|
||||
break;
|
||||
case NAND_ECC_HW:
|
||||
dev_info(info->device, "NAND hardware ECC\n");
|
||||
break;
|
||||
default:
|
||||
dev_info(info->device, "NAND ECC UNKNOWN\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* If you use u-boot BBT creation code, specifying this flag will
|
||||
* let the kernel fish out the BBT from the NAND, and also skip the
|
||||
* full NAND scan that can take 1/2s or so. Little things... */
|
||||
if (set->flash_bbt)
|
||||
chip->options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN;
|
||||
}
|
||||
|
||||
/* s3c2410_nand_update_chip
|
||||
/**
|
||||
* s3c2410_nand_update_chip - post probe update
|
||||
* @info: The controller instance.
|
||||
* @nmtd: The driver version of the MTD instance.
|
||||
*
|
||||
* post-probe chip update, to change any items, such as the
|
||||
* layout for large page nand
|
||||
*/
|
||||
|
||||
* This routine is called after the chip probe has succesfully completed
|
||||
* and the relevant per-chip information updated. This call ensure that
|
||||
* we update the internal state accordingly.
|
||||
*
|
||||
* The internal state is currently limited to the ECC state information.
|
||||
*/
|
||||
static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
|
||||
struct s3c2410_nand_mtd *nmtd)
|
||||
{
|
||||
|
@ -773,33 +889,33 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
|
|||
dev_dbg(info->device, "chip %p => page shift %d\n",
|
||||
chip, chip->page_shift);
|
||||
|
||||
if (hardware_ecc) {
|
||||
if (chip->ecc.mode != NAND_ECC_HW)
|
||||
return;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
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
|
||||
/* s3c24xx_nand_probe
|
||||
*
|
||||
* called by device layer when it finds a device matching
|
||||
* one our driver can handled. This code checks to see if
|
||||
* it can allocate all necessary resources then calls the
|
||||
* nand layer to look for devices
|
||||
*/
|
||||
|
||||
static int s3c24xx_nand_probe(struct platform_device *pdev,
|
||||
enum s3c_cpu_type cpu_type)
|
||||
static int s3c24xx_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
|
||||
enum s3c_cpu_type cpu_type;
|
||||
struct s3c2410_nand_info *info;
|
||||
struct s3c2410_nand_mtd *nmtd;
|
||||
struct s3c2410_nand_set *sets;
|
||||
|
@ -809,6 +925,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
|
|||
int nr_sets;
|
||||
int setno;
|
||||
|
||||
cpu_type = platform_get_device_id(pdev)->driver_data;
|
||||
|
||||
pr_debug("s3c2410_nand_probe(%p)\n", pdev);
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
|
@ -922,7 +1040,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
|
|||
return 0;
|
||||
|
||||
exit_error:
|
||||
s3c2410_nand_remove(pdev);
|
||||
s3c24xx_nand_remove(pdev);
|
||||
|
||||
if (err == 0)
|
||||
err = -EINVAL;
|
||||
|
@ -983,50 +1101,33 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
|
|||
|
||||
/* driver device registration */
|
||||
|
||||
static int s3c2410_nand_probe(struct platform_device *dev)
|
||||
{
|
||||
return s3c24xx_nand_probe(dev, TYPE_S3C2410);
|
||||
}
|
||||
|
||||
static int s3c2440_nand_probe(struct platform_device *dev)
|
||||
{
|
||||
return s3c24xx_nand_probe(dev, TYPE_S3C2440);
|
||||
}
|
||||
|
||||
static int s3c2412_nand_probe(struct platform_device *dev)
|
||||
{
|
||||
return s3c24xx_nand_probe(dev, TYPE_S3C2412);
|
||||
}
|
||||
|
||||
static struct platform_driver s3c2410_nand_driver = {
|
||||
.probe = s3c2410_nand_probe,
|
||||
.remove = s3c2410_nand_remove,
|
||||
.suspend = s3c24xx_nand_suspend,
|
||||
.resume = s3c24xx_nand_resume,
|
||||
.driver = {
|
||||
.name = "s3c2410-nand",
|
||||
.owner = THIS_MODULE,
|
||||
static struct platform_device_id s3c24xx_driver_ids[] = {
|
||||
{
|
||||
.name = "s3c2410-nand",
|
||||
.driver_data = TYPE_S3C2410,
|
||||
}, {
|
||||
.name = "s3c2440-nand",
|
||||
.driver_data = TYPE_S3C2440,
|
||||
}, {
|
||||
.name = "s3c2412-nand",
|
||||
.driver_data = TYPE_S3C2412,
|
||||
}, {
|
||||
.name = "s3c6400-nand",
|
||||
.driver_data = TYPE_S3C2412, /* compatible with 2412 */
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver s3c2440_nand_driver = {
|
||||
.probe = s3c2440_nand_probe,
|
||||
.remove = s3c2410_nand_remove,
|
||||
.suspend = s3c24xx_nand_suspend,
|
||||
.resume = s3c24xx_nand_resume,
|
||||
.driver = {
|
||||
.name = "s3c2440-nand",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
|
||||
|
||||
static struct platform_driver s3c2412_nand_driver = {
|
||||
.probe = s3c2412_nand_probe,
|
||||
.remove = s3c2410_nand_remove,
|
||||
static struct platform_driver s3c24xx_nand_driver = {
|
||||
.probe = s3c24xx_nand_probe,
|
||||
.remove = s3c24xx_nand_remove,
|
||||
.suspend = s3c24xx_nand_suspend,
|
||||
.resume = s3c24xx_nand_resume,
|
||||
.id_table = s3c24xx_driver_ids,
|
||||
.driver = {
|
||||
.name = "s3c2412-nand",
|
||||
.name = "s3c24xx-nand",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
@ -1035,16 +1136,12 @@ static int __init s3c2410_nand_init(void)
|
|||
{
|
||||
printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
|
||||
|
||||
platform_driver_register(&s3c2412_nand_driver);
|
||||
platform_driver_register(&s3c2440_nand_driver);
|
||||
return platform_driver_register(&s3c2410_nand_driver);
|
||||
return platform_driver_register(&s3c24xx_nand_driver);
|
||||
}
|
||||
|
||||
static void __exit s3c2410_nand_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&s3c2412_nand_driver);
|
||||
platform_driver_unregister(&s3c2440_nand_driver);
|
||||
platform_driver_unregister(&s3c2410_nand_driver);
|
||||
platform_driver_unregister(&s3c24xx_nand_driver);
|
||||
}
|
||||
|
||||
module_init(s3c2410_nand_init);
|
||||
|
@ -1053,6 +1150,3 @@ 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");
|
||||
|
|
|
@ -64,7 +64,7 @@ struct txx9ndfmc_priv {
|
|||
struct nand_chip chip;
|
||||
struct mtd_info mtd;
|
||||
int cs;
|
||||
char mtdname[BUS_ID_SIZE + 2];
|
||||
const char *mtdname;
|
||||
};
|
||||
|
||||
#define MAX_TXX9NDFMC_DEV 4
|
||||
|
@ -334,16 +334,23 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
|
|||
|
||||
if (plat->ch_mask != 1) {
|
||||
txx9_priv->cs = i;
|
||||
sprintf(txx9_priv->mtdname, "%s.%u",
|
||||
dev_name(&dev->dev), i);
|
||||
txx9_priv->mtdname = kasprintf(GFP_KERNEL, "%s.%u",
|
||||
dev_name(&dev->dev), i);
|
||||
} else {
|
||||
txx9_priv->cs = -1;
|
||||
strcpy(txx9_priv->mtdname, dev_name(&dev->dev));
|
||||
txx9_priv->mtdname = kstrdup(dev_name(&dev->dev),
|
||||
GFP_KERNEL);
|
||||
}
|
||||
if (!txx9_priv->mtdname) {
|
||||
kfree(txx9_priv);
|
||||
dev_err(&dev->dev, "Unable to allocate MTD name.\n");
|
||||
continue;
|
||||
}
|
||||
if (plat->wide_mask & (1 << i))
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
if (nand_scan(mtd, 1)) {
|
||||
kfree(txx9_priv->mtdname);
|
||||
kfree(txx9_priv);
|
||||
continue;
|
||||
}
|
||||
|
@ -385,6 +392,7 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev)
|
|||
kfree(drvdata->parts[i]);
|
||||
#endif
|
||||
del_mtd_device(mtd);
|
||||
kfree(txx9_priv->mtdname);
|
||||
kfree(txx9_priv);
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -565,7 +565,7 @@ int omap2_onenand_rephase(void)
|
|||
NULL, __adjust_timing);
|
||||
}
|
||||
|
||||
static void __devexit omap2_onenand_shutdown(struct platform_device *pdev)
|
||||
static void omap2_onenand_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct omap2_onenand *c = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
|
@ -777,7 +777,7 @@ static int __devexit omap2_onenand_remove(struct platform_device *pdev)
|
|||
|
||||
static struct platform_driver omap2_onenand_driver = {
|
||||
.probe = omap2_onenand_probe,
|
||||
.remove = omap2_onenand_remove,
|
||||
.remove = __devexit_p(omap2_onenand_remove),
|
||||
.shutdown = omap2_onenand_shutdown,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -63,6 +63,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
|||
loff_t from;
|
||||
size_t readlen, ooblen;
|
||||
struct mtd_oob_ops ops;
|
||||
int rgn;
|
||||
|
||||
printk(KERN_INFO "Scanning device for bad blocks\n");
|
||||
|
||||
|
@ -76,7 +77,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
|||
/* Note that numblocks is 2 * (real numblocks) here;
|
||||
* see i += 2 below as it makses shifting and masking less painful
|
||||
*/
|
||||
numblocks = mtd->size >> (bbm->bbt_erase_shift - 1);
|
||||
numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1);
|
||||
startblock = 0;
|
||||
from = 0;
|
||||
|
||||
|
@ -106,7 +107,12 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
|||
}
|
||||
}
|
||||
i += 2;
|
||||
from += (1 << bbm->bbt_erase_shift);
|
||||
|
||||
if (FLEXONENAND(this)) {
|
||||
rgn = flexonenand_region(mtd, from);
|
||||
from += mtd->eraseregions[rgn].erasesize;
|
||||
} else
|
||||
from += (1 << bbm->bbt_erase_shift);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -143,7 +149,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
|
|||
uint8_t res;
|
||||
|
||||
/* Get block number * 2 */
|
||||
block = (int) (offs >> (bbm->bbt_erase_shift - 1));
|
||||
block = (int) (onenand_block(this, offs) << 1);
|
||||
res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL2, "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n",
|
||||
|
@ -178,7 +184,7 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
|||
struct bbm_info *bbm = this->bbm;
|
||||
int len, ret = 0;
|
||||
|
||||
len = mtd->size >> (this->erase_shift + 2);
|
||||
len = this->chipsize >> (this->erase_shift + 2);
|
||||
/* Allocate memory (2bit per block) and clear the memory bad block table */
|
||||
bbm->bbt = kzalloc(len, GFP_KERNEL);
|
||||
if (!bbm->bbt) {
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
* Copyright © 2005-2007 Samsung Electronics
|
||||
* Kyungmin Park <kyungmin.park@samsung.com>
|
||||
*
|
||||
* Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
|
||||
* Flex-OneNAND simulator support
|
||||
* Copyright (C) Samsung Electronics, 2008
|
||||
*
|
||||
* 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.
|
||||
|
@ -24,16 +28,38 @@
|
|||
#ifndef CONFIG_ONENAND_SIM_MANUFACTURER
|
||||
#define CONFIG_ONENAND_SIM_MANUFACTURER 0xec
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ONENAND_SIM_DEVICE_ID
|
||||
#define CONFIG_ONENAND_SIM_DEVICE_ID 0x04
|
||||
#endif
|
||||
|
||||
#define CONFIG_FLEXONENAND ((CONFIG_ONENAND_SIM_DEVICE_ID >> 9) & 1)
|
||||
|
||||
#ifndef CONFIG_ONENAND_SIM_VERSION_ID
|
||||
#define CONFIG_ONENAND_SIM_VERSION_ID 0x1e
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ONENAND_SIM_TECHNOLOGY_ID
|
||||
#define CONFIG_ONENAND_SIM_TECHNOLOGY_ID CONFIG_FLEXONENAND
|
||||
#endif
|
||||
|
||||
/* Initial boundary values for Flex-OneNAND Simulator */
|
||||
#ifndef CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY
|
||||
#define CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY 0x01
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY
|
||||
#define CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY 0x01
|
||||
#endif
|
||||
|
||||
static int manuf_id = CONFIG_ONENAND_SIM_MANUFACTURER;
|
||||
static int device_id = CONFIG_ONENAND_SIM_DEVICE_ID;
|
||||
static int version_id = CONFIG_ONENAND_SIM_VERSION_ID;
|
||||
static int technology_id = CONFIG_ONENAND_SIM_TECHNOLOGY_ID;
|
||||
static int boundary[] = {
|
||||
CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY,
|
||||
CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY,
|
||||
};
|
||||
|
||||
struct onenand_flash {
|
||||
void __iomem *base;
|
||||
|
@ -57,12 +83,18 @@ struct onenand_flash {
|
|||
(writew(v, this->base + ONENAND_REG_WP_STATUS))
|
||||
|
||||
/* It has all 0xff chars */
|
||||
#define MAX_ONENAND_PAGESIZE (2048 + 64)
|
||||
#define MAX_ONENAND_PAGESIZE (4096 + 128)
|
||||
static unsigned char *ffchars;
|
||||
|
||||
#if CONFIG_FLEXONENAND
|
||||
#define PARTITION_NAME "Flex-OneNAND simulator partition"
|
||||
#else
|
||||
#define PARTITION_NAME "OneNAND simulator partition"
|
||||
#endif
|
||||
|
||||
static struct mtd_partition os_partitions[] = {
|
||||
{
|
||||
.name = "OneNAND simulator partition",
|
||||
.name = PARTITION_NAME,
|
||||
.offset = 0,
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
},
|
||||
|
@ -104,6 +136,7 @@ static void onenand_lock_handle(struct onenand_chip *this, int cmd)
|
|||
|
||||
switch (cmd) {
|
||||
case ONENAND_CMD_UNLOCK:
|
||||
case ONENAND_CMD_UNLOCK_ALL:
|
||||
if (block_lock_scheme)
|
||||
ONENAND_SET_WP_STATUS(ONENAND_WP_US, this);
|
||||
else
|
||||
|
@ -228,10 +261,12 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
|
|||
{
|
||||
struct mtd_info *mtd = &info->mtd;
|
||||
struct onenand_flash *flash = this->priv;
|
||||
int main_offset, spare_offset;
|
||||
int main_offset, spare_offset, die = 0;
|
||||
void __iomem *src;
|
||||
void __iomem *dest;
|
||||
unsigned int i;
|
||||
static int pi_operation;
|
||||
int erasesize, rgn;
|
||||
|
||||
if (dataram) {
|
||||
main_offset = mtd->writesize;
|
||||
|
@ -241,10 +276,27 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
|
|||
spare_offset = 0;
|
||||
}
|
||||
|
||||
if (pi_operation) {
|
||||
die = readw(this->base + ONENAND_REG_START_ADDRESS2);
|
||||
die >>= ONENAND_DDP_SHIFT;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case FLEXONENAND_CMD_PI_ACCESS:
|
||||
pi_operation = 1;
|
||||
break;
|
||||
|
||||
case ONENAND_CMD_RESET:
|
||||
pi_operation = 0;
|
||||
break;
|
||||
|
||||
case ONENAND_CMD_READ:
|
||||
src = ONENAND_CORE(flash) + offset;
|
||||
dest = ONENAND_MAIN_AREA(this, main_offset);
|
||||
if (pi_operation) {
|
||||
writew(boundary[die], this->base + ONENAND_DATARAM);
|
||||
break;
|
||||
}
|
||||
memcpy(dest, src, mtd->writesize);
|
||||
/* Fall through */
|
||||
|
||||
|
@ -257,6 +309,10 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
|
|||
case ONENAND_CMD_PROG:
|
||||
src = ONENAND_MAIN_AREA(this, main_offset);
|
||||
dest = ONENAND_CORE(flash) + offset;
|
||||
if (pi_operation) {
|
||||
boundary[die] = readw(this->base + ONENAND_DATARAM);
|
||||
break;
|
||||
}
|
||||
/* To handle partial write */
|
||||
for (i = 0; i < (1 << mtd->subpage_sft); i++) {
|
||||
int off = i * this->subpagesize;
|
||||
|
@ -284,9 +340,18 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
|
|||
break;
|
||||
|
||||
case ONENAND_CMD_ERASE:
|
||||
memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize);
|
||||
if (pi_operation)
|
||||
break;
|
||||
|
||||
if (FLEXONENAND(this)) {
|
||||
rgn = flexonenand_region(mtd, offset);
|
||||
erasesize = mtd->eraseregions[rgn].erasesize;
|
||||
} else
|
||||
erasesize = mtd->erasesize;
|
||||
|
||||
memset(ONENAND_CORE(flash) + offset, 0xff, erasesize);
|
||||
memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff,
|
||||
(mtd->erasesize >> 5));
|
||||
(erasesize >> 5));
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -339,7 +404,7 @@ static void onenand_command_handle(struct onenand_chip *this, int cmd)
|
|||
}
|
||||
|
||||
if (block != -1)
|
||||
offset += block << this->erase_shift;
|
||||
offset = onenand_addr(this, block);
|
||||
|
||||
if (page != -1)
|
||||
offset += page << this->page_shift;
|
||||
|
@ -390,6 +455,7 @@ static int __init flash_init(struct onenand_flash *flash)
|
|||
}
|
||||
|
||||
density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
|
||||
density &= ONENAND_DEVICE_DENSITY_MASK;
|
||||
size = ((16 << 20) << density);
|
||||
|
||||
ONENAND_CORE(flash) = vmalloc(size + (size >> 5));
|
||||
|
@ -405,8 +471,9 @@ static int __init flash_init(struct onenand_flash *flash)
|
|||
writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID);
|
||||
writew(device_id, flash->base + ONENAND_REG_DEVICE_ID);
|
||||
writew(version_id, flash->base + ONENAND_REG_VERSION_ID);
|
||||
writew(technology_id, flash->base + ONENAND_REG_TECHNOLOGY);
|
||||
|
||||
if (density < 2)
|
||||
if (density < 2 && (!CONFIG_FLEXONENAND))
|
||||
buffer_size = 0x0400; /* 1KiB page */
|
||||
else
|
||||
buffer_size = 0x0800; /* 2KiB page */
|
||||
|
|
|
@ -94,7 +94,6 @@
|
|||
#include <linux/atm_tcp.h>
|
||||
#include <linux/sonet.h>
|
||||
#include <linux/atm_suni.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
|
@ -1405,46 +1404,6 @@ static int ioc_settimeout(unsigned int fd, unsigned int cmd, unsigned long arg)
|
|||
#define HIDPGETCONNLIST _IOR('H', 210, int)
|
||||
#define HIDPGETCONNINFO _IOR('H', 211, int)
|
||||
|
||||
struct mtd_oob_buf32 {
|
||||
u_int32_t start;
|
||||
u_int32_t length;
|
||||
compat_caddr_t ptr; /* unsigned char* */
|
||||
};
|
||||
|
||||
#define MEMWRITEOOB32 _IOWR('M',3,struct mtd_oob_buf32)
|
||||
#define MEMREADOOB32 _IOWR('M',4,struct mtd_oob_buf32)
|
||||
|
||||
static int mtd_rw_oob(unsigned int fd, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct mtd_oob_buf __user *buf = compat_alloc_user_space(sizeof(*buf));
|
||||
struct mtd_oob_buf32 __user *buf32 = compat_ptr(arg);
|
||||
u32 data;
|
||||
char __user *datap;
|
||||
unsigned int real_cmd;
|
||||
int err;
|
||||
|
||||
real_cmd = (cmd == MEMREADOOB32) ?
|
||||
MEMREADOOB : MEMWRITEOOB;
|
||||
|
||||
if (copy_in_user(&buf->start, &buf32->start,
|
||||
2 * sizeof(u32)) ||
|
||||
get_user(data, &buf32->ptr))
|
||||
return -EFAULT;
|
||||
datap = compat_ptr(data);
|
||||
if (put_user(datap, &buf->ptr))
|
||||
return -EFAULT;
|
||||
|
||||
err = sys_ioctl(fd, real_cmd, (unsigned long) buf);
|
||||
|
||||
if (!err) {
|
||||
if (copy_in_user(&buf32->start, &buf->start,
|
||||
2 * sizeof(u32)))
|
||||
err = -EFAULT;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLOCK
|
||||
struct raw32_config_request
|
||||
{
|
||||
|
@ -2426,15 +2385,6 @@ COMPATIBLE_IOCTL(USBDEVFS_SUBMITURB32)
|
|||
COMPATIBLE_IOCTL(USBDEVFS_REAPURB32)
|
||||
COMPATIBLE_IOCTL(USBDEVFS_REAPURBNDELAY32)
|
||||
COMPATIBLE_IOCTL(USBDEVFS_CLEAR_HALT)
|
||||
/* MTD */
|
||||
COMPATIBLE_IOCTL(MEMGETINFO)
|
||||
COMPATIBLE_IOCTL(MEMERASE)
|
||||
COMPATIBLE_IOCTL(MEMLOCK)
|
||||
COMPATIBLE_IOCTL(MEMUNLOCK)
|
||||
COMPATIBLE_IOCTL(MEMGETREGIONCOUNT)
|
||||
COMPATIBLE_IOCTL(MEMGETREGIONINFO)
|
||||
COMPATIBLE_IOCTL(MEMGETBADBLOCK)
|
||||
COMPATIBLE_IOCTL(MEMSETBADBLOCK)
|
||||
/* NBD */
|
||||
ULONG_IOCTL(NBD_SET_SOCK)
|
||||
ULONG_IOCTL(NBD_SET_BLKSIZE)
|
||||
|
@ -2544,8 +2494,6 @@ COMPATIBLE_IOCTL(JSIOCGBUTTONS)
|
|||
COMPATIBLE_IOCTL(JSIOCGNAME(0))
|
||||
|
||||
/* now things that need handlers */
|
||||
HANDLE_IOCTL(MEMREADOOB32, mtd_rw_oob)
|
||||
HANDLE_IOCTL(MEMWRITEOOB32, mtd_rw_oob)
|
||||
#ifdef CONFIG_NET
|
||||
HANDLE_IOCTL(SIOCGIFNAME, dev_ifname32)
|
||||
HANDLE_IOCTL(SIOCGIFCONF, dev_ifconf)
|
||||
|
|
|
@ -196,7 +196,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
|
|||
if (c->nextblock) {
|
||||
ret = file_dirty(c, c->nextblock);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out;
|
||||
/* deleting summary information of the old nextblock */
|
||||
jffs2_sum_reset_collected(c->summary);
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
|
|||
} else {
|
||||
ret = file_dirty(c, jeb);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -563,6 +563,7 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
* @options: Option flags, e.g. 16bit buswidth
|
||||
* @ecclayout: ecc layout info structure
|
||||
* @part_probe_types: NULL-terminated array of probe types
|
||||
* @set_parts: platform specific function to set partitions
|
||||
* @priv: hardware controller specific settings
|
||||
*/
|
||||
struct platform_nand_chip {
|
||||
|
@ -574,26 +575,41 @@ struct platform_nand_chip {
|
|||
int chip_delay;
|
||||
unsigned int options;
|
||||
const char **part_probe_types;
|
||||
void (*set_parts)(uint64_t size,
|
||||
struct platform_nand_chip *chip);
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/* Keep gcc happy */
|
||||
struct platform_device;
|
||||
|
||||
/**
|
||||
* struct platform_nand_ctrl - controller level device structure
|
||||
* @probe: platform specific function to probe/setup hardware
|
||||
* @remove: platform specific function to remove/teardown hardware
|
||||
* @hwcontrol: platform specific hardware control structure
|
||||
* @dev_ready: platform specific function to read ready/busy pin
|
||||
* @select_chip: platform specific chip select function
|
||||
* @cmd_ctrl: platform specific function for controlling
|
||||
* ALE/CLE/nCE. Also used to write command and address
|
||||
* @write_buf: platform specific function for write buffer
|
||||
* @read_buf: platform specific function for read buffer
|
||||
* @priv: private data to transport driver specific settings
|
||||
*
|
||||
* All fields are optional and depend on the hardware driver requirements
|
||||
*/
|
||||
struct platform_nand_ctrl {
|
||||
int (*probe)(struct platform_device *pdev);
|
||||
void (*remove)(struct platform_device *pdev);
|
||||
void (*hwcontrol)(struct mtd_info *mtd, int cmd);
|
||||
int (*dev_ready)(struct mtd_info *mtd);
|
||||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||
void (*cmd_ctrl)(struct mtd_info *mtd, int dat,
|
||||
unsigned int ctrl);
|
||||
void (*write_buf)(struct mtd_info *mtd,
|
||||
const uint8_t *buf, int len);
|
||||
void (*read_buf)(struct mtd_info *mtd,
|
||||
uint8_t *buf, int len);
|
||||
void *priv;
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/mtd/onenand_regs.h>
|
||||
#include <linux/mtd/bbm.h>
|
||||
|
||||
#define MAX_DIES 2
|
||||
#define MAX_BUFFERRAM 2
|
||||
|
||||
/* Scan and identify a OneNAND device */
|
||||
|
@ -51,7 +52,12 @@ struct onenand_bufferram {
|
|||
/**
|
||||
* struct onenand_chip - OneNAND Private Flash Chip Data
|
||||
* @base: [BOARDSPECIFIC] address to access OneNAND
|
||||
* @dies: [INTERN][FLEX-ONENAND] number of dies on chip
|
||||
* @boundary: [INTERN][FLEX-ONENAND] Boundary of the dies
|
||||
* @diesize: [INTERN][FLEX-ONENAND] Size of the dies
|
||||
* @chipsize: [INTERN] the size of one chip for multichip arrays
|
||||
* FIXME For Flex-OneNAND, chipsize holds maximum possible
|
||||
* device size ie when all blocks are considered MLC
|
||||
* @device_id: [INTERN] device ID
|
||||
* @density_mask: chip density, used for DDP devices
|
||||
* @verstion_id: [INTERN] version ID
|
||||
|
@ -68,6 +74,8 @@ struct onenand_bufferram {
|
|||
* @command: [REPLACEABLE] hardware specific function for writing
|
||||
* commands to the chip
|
||||
* @wait: [REPLACEABLE] hardware specific function for wait on ready
|
||||
* @bbt_wait: [REPLACEABLE] hardware specific function for bbt wait on ready
|
||||
* @unlock_all: [REPLACEABLE] hardware specific function for unlock all
|
||||
* @read_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area
|
||||
* @write_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area
|
||||
* @read_word: [REPLACEABLE] hardware specific function for read
|
||||
|
@ -92,9 +100,13 @@ struct onenand_bufferram {
|
|||
*/
|
||||
struct onenand_chip {
|
||||
void __iomem *base;
|
||||
unsigned dies;
|
||||
unsigned boundary[MAX_DIES];
|
||||
loff_t diesize[MAX_DIES];
|
||||
unsigned int chipsize;
|
||||
unsigned int device_id;
|
||||
unsigned int version_id;
|
||||
unsigned int technology;
|
||||
unsigned int density_mask;
|
||||
unsigned int options;
|
||||
|
||||
|
@ -108,6 +120,8 @@ struct onenand_chip {
|
|||
|
||||
int (*command)(struct mtd_info *mtd, int cmd, loff_t address, size_t len);
|
||||
int (*wait)(struct mtd_info *mtd, int state);
|
||||
int (*bbt_wait)(struct mtd_info *mtd, int state);
|
||||
void (*unlock_all)(struct mtd_info *mtd);
|
||||
int (*read_bufferram)(struct mtd_info *mtd, int area,
|
||||
unsigned char *buffer, int offset, size_t count);
|
||||
int (*write_bufferram)(struct mtd_info *mtd, int area,
|
||||
|
@ -145,6 +159,8 @@ struct onenand_chip {
|
|||
#define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0)
|
||||
#define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1)
|
||||
|
||||
#define FLEXONENAND(this) \
|
||||
(this->device_id & DEVICE_IS_FLEXONENAND)
|
||||
#define ONENAND_GET_SYS_CFG1(this) \
|
||||
(this->read_word(this->base + ONENAND_REG_SYS_CFG1))
|
||||
#define ONENAND_SET_SYS_CFG1(v, this) \
|
||||
|
@ -153,6 +169,9 @@ struct onenand_chip {
|
|||
#define ONENAND_IS_DDP(this) \
|
||||
(this->device_id & ONENAND_DEVICE_IS_DDP)
|
||||
|
||||
#define ONENAND_IS_MLC(this) \
|
||||
(this->technology & ONENAND_TECHNOLOGY_IS_MLC)
|
||||
|
||||
#ifdef CONFIG_MTD_ONENAND_2X_PROGRAM
|
||||
#define ONENAND_IS_2PLANE(this) \
|
||||
(this->options & ONENAND_HAS_2PLANE)
|
||||
|
@ -169,6 +188,7 @@ struct onenand_chip {
|
|||
#define ONENAND_HAS_CONT_LOCK (0x0001)
|
||||
#define ONENAND_HAS_UNLOCK_ALL (0x0002)
|
||||
#define ONENAND_HAS_2PLANE (0x0004)
|
||||
#define ONENAND_SKIP_UNLOCK_CHECK (0x0100)
|
||||
#define ONENAND_PAGEBUF_ALLOC (0x1000)
|
||||
#define ONENAND_OOBBUF_ALLOC (0x2000)
|
||||
|
||||
|
@ -176,6 +196,7 @@ struct onenand_chip {
|
|||
* OneNAND Flash Manufacturer ID Codes
|
||||
*/
|
||||
#define ONENAND_MFR_SAMSUNG 0xec
|
||||
#define ONENAND_MFR_NUMONYX 0x20
|
||||
|
||||
/**
|
||||
* struct onenand_manufacturers - NAND Flash Manufacturer ID Structure
|
||||
|
@ -189,5 +210,8 @@ struct onenand_manufacturers {
|
|||
|
||||
int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
struct mtd_oob_ops *ops);
|
||||
unsigned onenand_block(struct onenand_chip *this, loff_t addr);
|
||||
loff_t onenand_addr(struct onenand_chip *this, int block);
|
||||
int flexonenand_region(struct mtd_info *mtd, loff_t addr);
|
||||
|
||||
#endif /* __LINUX_MTD_ONENAND_H */
|
||||
|
|
|
@ -67,6 +67,9 @@
|
|||
/*
|
||||
* Device ID Register F001h (R)
|
||||
*/
|
||||
#define DEVICE_IS_FLEXONENAND (1 << 9)
|
||||
#define FLEXONENAND_PI_MASK (0x3ff)
|
||||
#define FLEXONENAND_PI_UNLOCK_SHIFT (14)
|
||||
#define ONENAND_DEVICE_DENSITY_MASK (0xf)
|
||||
#define ONENAND_DEVICE_DENSITY_SHIFT (4)
|
||||
#define ONENAND_DEVICE_IS_DDP (1 << 3)
|
||||
|
@ -83,6 +86,11 @@
|
|||
*/
|
||||
#define ONENAND_VERSION_PROCESS_SHIFT (8)
|
||||
|
||||
/*
|
||||
* Technology Register F006h (R)
|
||||
*/
|
||||
#define ONENAND_TECHNOLOGY_IS_MLC (1 << 0)
|
||||
|
||||
/*
|
||||
* Start Address 1 F100h (R/W) & Start Address 2 F101h (R/W)
|
||||
*/
|
||||
|
@ -93,7 +101,8 @@
|
|||
/*
|
||||
* Start Address 8 F107h (R/W)
|
||||
*/
|
||||
#define ONENAND_FPA_MASK (0x3f)
|
||||
/* Note: It's actually 0x3f in case of SLC */
|
||||
#define ONENAND_FPA_MASK (0x7f)
|
||||
#define ONENAND_FPA_SHIFT (2)
|
||||
#define ONENAND_FSA_MASK (0x03)
|
||||
|
||||
|
@ -105,7 +114,8 @@
|
|||
#define ONENAND_BSA_BOOTRAM (0 << 2)
|
||||
#define ONENAND_BSA_DATARAM0 (2 << 2)
|
||||
#define ONENAND_BSA_DATARAM1 (3 << 2)
|
||||
#define ONENAND_BSC_MASK (0x03)
|
||||
/* Note: It's actually 0x03 in case of SLC */
|
||||
#define ONENAND_BSC_MASK (0x07)
|
||||
|
||||
/*
|
||||
* Command Register F220h (R/W)
|
||||
|
@ -124,9 +134,13 @@
|
|||
#define ONENAND_CMD_RESET (0xF0)
|
||||
#define ONENAND_CMD_OTP_ACCESS (0x65)
|
||||
#define ONENAND_CMD_READID (0x90)
|
||||
#define FLEXONENAND_CMD_PI_UPDATE (0x05)
|
||||
#define FLEXONENAND_CMD_PI_ACCESS (0x66)
|
||||
#define FLEXONENAND_CMD_RECOVER_LSB (0x05)
|
||||
|
||||
/* NOTE: Those are not *REAL* commands */
|
||||
#define ONENAND_CMD_BUFFERRAM (0x1978)
|
||||
#define FLEXONENAND_CMD_READ_PI (0x1985)
|
||||
|
||||
/*
|
||||
* System Configuration 1 Register F221h (R, R/W)
|
||||
|
@ -192,10 +206,12 @@
|
|||
#define ONENAND_ECC_1BIT_ALL (0x5555)
|
||||
#define ONENAND_ECC_2BIT (1 << 1)
|
||||
#define ONENAND_ECC_2BIT_ALL (0xAAAA)
|
||||
#define FLEXONENAND_UNCORRECTABLE_ERROR (0x1010)
|
||||
|
||||
/*
|
||||
* One-Time Programmable (OTP)
|
||||
*/
|
||||
#define FLEXONENAND_OTP_LOCK_OFFSET (2048)
|
||||
#define ONENAND_OTP_LOCK_OFFSET (14)
|
||||
|
||||
#endif /* __ONENAND_REG_H */
|
||||
|
|
|
@ -40,7 +40,6 @@ struct mtd_partition {
|
|||
uint64_t offset; /* offset within the master MTD space */
|
||||
uint32_t mask_flags; /* master MTD flags to mask out for this partition */
|
||||
struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/
|
||||
struct mtd_info **mtdp; /* pointer to store the MTD object */
|
||||
};
|
||||
|
||||
#define MTDPART_OFS_NXTBLK (-2)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
header-y += inftl-user.h
|
||||
header-y += jffs2-user.h
|
||||
header-y += mtd-abi.h
|
||||
header-y += mtd-user.h
|
||||
header-y += nftl-user.h
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* JFFS2 definitions for use in user space only
|
||||
*/
|
||||
|
||||
#ifndef __JFFS2_USER_H__
|
||||
#define __JFFS2_USER_H__
|
||||
|
||||
/* This file is blessed for inclusion by userspace */
|
||||
#include <linux/jffs2.h>
|
||||
#include <linux/types.h>
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
|
||||
#undef cpu_to_je16
|
||||
#undef cpu_to_je32
|
||||
#undef cpu_to_jemode
|
||||
#undef je16_to_cpu
|
||||
#undef je32_to_cpu
|
||||
#undef jemode_to_cpu
|
||||
|
||||
extern int target_endian;
|
||||
|
||||
#define t16(x) ({ __u16 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
|
||||
#define t32(x) ({ __u32 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
|
||||
|
||||
#define cpu_to_je16(x) ((jint16_t){t16(x)})
|
||||
#define cpu_to_je32(x) ((jint32_t){t32(x)})
|
||||
#define cpu_to_jemode(x) ((jmode_t){t32(x)})
|
||||
|
||||
#define je16_to_cpu(x) (t16((x).v16))
|
||||
#define je32_to_cpu(x) (t32((x).v32))
|
||||
#define jemode_to_cpu(x) (t32((x).m))
|
||||
|
||||
#endif /* __JFFS2_USER_H__ */
|
|
@ -12,12 +12,24 @@ struct erase_info_user {
|
|||
__u32 length;
|
||||
};
|
||||
|
||||
struct erase_info_user64 {
|
||||
__u64 start;
|
||||
__u64 length;
|
||||
};
|
||||
|
||||
struct mtd_oob_buf {
|
||||
__u32 start;
|
||||
__u32 length;
|
||||
unsigned char __user *ptr;
|
||||
};
|
||||
|
||||
struct mtd_oob_buf64 {
|
||||
__u64 start;
|
||||
__u32 pad;
|
||||
__u32 length;
|
||||
__u64 usr_ptr;
|
||||
};
|
||||
|
||||
#define MTD_ABSENT 0
|
||||
#define MTD_RAM 1
|
||||
#define MTD_ROM 2
|
||||
|
@ -95,6 +107,9 @@ struct otp_info {
|
|||
#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout)
|
||||
#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
|
||||
#define MTDFILEMODE _IO('M', 19)
|
||||
#define MEMERASE64 _IOW('M', 20, struct erase_info_user64)
|
||||
#define MEMWRITEOOB64 _IOWR('M', 21, struct mtd_oob_buf64)
|
||||
#define MEMREADOOB64 _IOWR('M', 22, struct mtd_oob_buf64)
|
||||
|
||||
/*
|
||||
* Obsolete legacy interface. Keep it in order not to break userspace
|
||||
|
|
Загрузка…
Ссылка в новой задаче