Merge git://git.infradead.org/mtd-2.6

* git://git.infradead.org/mtd-2.6: (58 commits)
  mtd: jedec_probe: add PSD4256G6V id
  mtd: OneNand support for Nomadik 8815 SoC (on NHK8815 board)
  mtd: nand: driver for Nomadik 8815 SoC (on NHK8815 board)
  m25p80: Add Spansion S25FL129P serial flashes
  jffs2: Use SLAB_HWCACHE_ALIGN for jffs2_raw_{dirent,inode} slabs
  mtd: sh_flctl: register sh_flctl using platform_driver_probe()
  mtd: nand: txx9ndfmc: transfer 512 byte at a time if possible
  mtd: nand: fix tmio_nand ecc correction
  mtd: nand: add __nand_correct_data helper function
  mtd: cfi_cmdset_0002: add 0xFF intolerance for M29W128G
  mtd: inftl: fix fold chain block number
  mtd: jedec: fix compilation problem with I28F640C3B definition
  mtd: nand: fix ECC Correction bug for SMC ordering for NDFC driver
  mtd: ofpart: Check availability of reg property instead of name property
  driver/Makefile: Initialize "mtd" and "spi" before "net"
  mtd: omap: adding DMA mode support in nand prefetch/post-write
  mtd: omap: add support for nand prefetch-read and post-write
  mtd: add nand support for w90p910 (v2)
  mtd: maps: add mtd-ram support to physmap_of
  mtd: pxa3xx_nand: add single-bit error corrections reporting
  ...
This commit is contained in:
Linus Torvalds 2009-09-23 10:07:49 -07:00
commit a7c367b95a
64 changed files with 2798 additions and 254 deletions

View file

@ -1,18 +1,19 @@
CFI or JEDEC memory-mapped NOR flash CFI or JEDEC memory-mapped NOR flash, MTD-RAM (NVRAM...)
Flash chips (Memory Technology Devices) are often used for solid state Flash chips (Memory Technology Devices) are often used for solid state
file systems on embedded devices. file systems on embedded devices.
- compatible : should contain the specific model of flash chip(s) - compatible : should contain the specific model of mtd chip(s)
used, if known, followed by either "cfi-flash" or "jedec-flash" used, if known, followed by either "cfi-flash", "jedec-flash"
- reg : Address range(s) of the flash chip(s) or "mtd-ram".
- reg : Address range(s) of the mtd chip(s)
It's possible to (optionally) define multiple "reg" tuples so that It's possible to (optionally) define multiple "reg" tuples so that
non-identical NOR chips can be described in one flash node. non-identical chips can be described in one node.
- bank-width : Width (in bytes) of the flash bank. Equal to the - bank-width : Width (in bytes) of the bank. Equal to the
device width times the number of interleaved chips. device width times the number of interleaved chips.
- device-width : (optional) Width of a single flash chip. If - device-width : (optional) Width of a single mtd chip. If
omitted, assumed to be equal to 'bank-width'. omitted, assumed to be equal to 'bank-width'.
- #address-cells, #size-cells : Must be present if the flash has - #address-cells, #size-cells : Must be present if the device has
sub-nodes representing partitions (see below). In this case sub-nodes representing partitions (see below). In this case
both #address-cells and #size-cells must be equal to 1. both #address-cells and #size-cells must be equal to 1.
@ -22,24 +23,24 @@ are defined:
- vendor-id : Contains the flash chip's vendor id (1 byte). - vendor-id : Contains the flash chip's vendor id (1 byte).
- device-id : Contains the flash chip's device id (1 byte). - device-id : Contains the flash chip's device id (1 byte).
In addition to the information on the flash bank itself, the In addition to the information on the mtd bank itself, the
device tree may optionally contain additional information device tree may optionally contain additional information
describing partitions of the flash address space. This can be describing partitions of the address space. This can be
used on platforms which have strong conventions about which used on platforms which have strong conventions about which
portions of the flash are used for what purposes, but which don't portions of a flash are used for what purposes, but which don't
use an on-flash partition table such as RedBoot. use an on-flash partition table such as RedBoot.
Each partition is represented as a sub-node of the flash device. Each partition is represented as a sub-node of the mtd device.
Each node's name represents the name of the corresponding Each node's name represents the name of the corresponding
partition of the flash device. partition of the mtd device.
Flash partitions Flash partitions
- reg : The partition's offset and size within the flash bank. - reg : The partition's offset and size within the mtd bank.
- label : (optional) The label / name for this flash partition. - label : (optional) The label / name for this partition.
If omitted, the label is taken from the node name (excluding If omitted, the label is taken from the node name (excluding
the unit address). the unit address).
- read-only : (optional) This parameter, if present, is a hint to - read-only : (optional) This parameter, if present, is a hint to
Linux that this flash partition should only be mounted Linux that this partition should only be mounted
read-only. This is usually used for flash partitions read-only. This is usually used for flash partitions
containing early-boot firmware images or data which should not containing early-boot firmware images or data which should not
be clobbered. be clobbered.
@ -78,3 +79,12 @@ Here an example with multiple "reg" tuples:
reg = <0 0x04000000>; reg = <0 0x04000000>;
}; };
}; };
An example using SRAM:
sram@2,0 {
compatible = "samsung,k6f1616u6a", "mtd-ram";
reg = <2 0 0x00200000>;
bank-width = <2>;
};

View file

@ -498,7 +498,7 @@ CONFIG_MTD_CFI_I2=y
# CONFIG_MTD_DOC2001PLUS is not set # CONFIG_MTD_DOC2001PLUS is not set
CONFIG_MTD_NAND=y CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y CONFIG_MTD_NAND_VERIFY_WRITE=y
# CONFIG_MTD_NAND_ECC_SMC is not set CONFIG_MTD_NAND_ECC_SMC=y
# CONFIG_MTD_NAND_MUSEUM_IDS is not set # CONFIG_MTD_NAND_MUSEUM_IDS is not set
# CONFIG_MTD_NAND_GPIO is not set # CONFIG_MTD_NAND_GPIO is not set
CONFIG_MTD_NAND_IDS=y CONFIG_MTD_NAND_IDS=y

View file

@ -16,12 +16,164 @@
#include <linux/amba/bus.h> #include <linux/amba/bus.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/io.h>
#include <asm/sizes.h>
#include <asm/mach-types.h> #include <asm/mach-types.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/mach/irq.h> #include <asm/mach/irq.h>
#include <asm/mach/flash.h>
#include <mach/setup.h> #include <mach/setup.h>
#include <mach/nand.h>
#include <mach/fsmc.h>
#include "clock.h" #include "clock.h"
/* These adresses span 16MB, so use three individual pages */
static struct resource nhk8815_nand_resources[] = {
{
.name = "nand_addr",
.start = NAND_IO_ADDR,
.end = NAND_IO_ADDR + 0xfff,
.flags = IORESOURCE_MEM,
}, {
.name = "nand_cmd",
.start = NAND_IO_CMD,
.end = NAND_IO_CMD + 0xfff,
.flags = IORESOURCE_MEM,
}, {
.name = "nand_data",
.start = NAND_IO_DATA,
.end = NAND_IO_DATA + 0xfff,
.flags = IORESOURCE_MEM,
}
};
static int nhk8815_nand_init(void)
{
/* FSMC setup for nand chip select (8-bit nand in 8815NHK) */
writel(0x0000000E, FSMC_PCR(0));
writel(0x000D0A00, FSMC_PMEM(0));
writel(0x00100A00, FSMC_PATT(0));
/* enable access to the chip select area */
writel(readl(FSMC_PCR(0)) | 0x04, FSMC_PCR(0));
return 0;
}
/*
* These partitions are the same as those used in the 2.6.20 release
* shipped by the vendor; the first two partitions are mandated
* by the boot ROM, and the bootloader area is somehow oversized...
*/
static struct mtd_partition nhk8815_partitions[] = {
{
.name = "X-Loader(NAND)",
.offset = 0,
.size = SZ_256K,
}, {
.name = "MemInit(NAND)",
.offset = MTDPART_OFS_APPEND,
.size = SZ_256K,
}, {
.name = "BootLoader(NAND)",
.offset = MTDPART_OFS_APPEND,
.size = SZ_2M,
}, {
.name = "Kernel zImage(NAND)",
.offset = MTDPART_OFS_APPEND,
.size = 3 * SZ_1M,
}, {
.name = "Root Filesystem(NAND)",
.offset = MTDPART_OFS_APPEND,
.size = 22 * SZ_1M,
}, {
.name = "User Filesystem(NAND)",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
static struct nomadik_nand_platform_data nhk8815_nand_data = {
.parts = nhk8815_partitions,
.nparts = ARRAY_SIZE(nhk8815_partitions),
.options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING \
| NAND_NO_READRDY | NAND_NO_AUTOINCR,
.init = nhk8815_nand_init,
};
static struct platform_device nhk8815_nand_device = {
.name = "nomadik_nand",
.dev = {
.platform_data = &nhk8815_nand_data,
},
.resource = nhk8815_nand_resources,
.num_resources = ARRAY_SIZE(nhk8815_nand_resources),
};
/* These are the partitions for the OneNand device, different from above */
static struct mtd_partition nhk8815_onenand_partitions[] = {
{
.name = "X-Loader(OneNAND)",
.offset = 0,
.size = SZ_256K,
}, {
.name = "MemInit(OneNAND)",
.offset = MTDPART_OFS_APPEND,
.size = SZ_256K,
}, {
.name = "BootLoader(OneNAND)",
.offset = MTDPART_OFS_APPEND,
.size = SZ_2M-SZ_256K,
}, {
.name = "SysImage(OneNAND)",
.offset = MTDPART_OFS_APPEND,
.size = 4 * SZ_1M,
}, {
.name = "Root Filesystem(OneNAND)",
.offset = MTDPART_OFS_APPEND,
.size = 22 * SZ_1M,
}, {
.name = "User Filesystem(OneNAND)",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
static struct flash_platform_data nhk8815_onenand_data = {
.parts = nhk8815_onenand_partitions,
.nr_parts = ARRAY_SIZE(nhk8815_onenand_partitions),
};
static struct resource nhk8815_onenand_resource[] = {
{
.start = 0x30000000,
.end = 0x30000000 + SZ_128K - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device nhk8815_onenand_device = {
.name = "onenand",
.id = -1,
.dev = {
.platform_data = &nhk8815_onenand_data,
},
.resource = nhk8815_onenand_resource,
.num_resources = ARRAY_SIZE(nhk8815_onenand_resource),
};
static void __init nhk8815_onenand_init(void)
{
#ifdef CONFIG_ONENAND
/* Set up SMCS0 for OneNand */
writel(0x000030db, FSMC_BCR0);
writel(0x02100551, FSMC_BTR0);
#endif
}
#define __MEM_4K_RESOURCE(x) \ #define __MEM_4K_RESOURCE(x) \
.res = {.start = (x), .end = (x) + SZ_4K - 1, .flags = IORESOURCE_MEM} .res = {.start = (x), .end = (x) + SZ_4K - 1, .flags = IORESOURCE_MEM}
@ -81,6 +233,8 @@ static int __init nhk8815_eth_init(void)
device_initcall(nhk8815_eth_init); device_initcall(nhk8815_eth_init);
static struct platform_device *nhk8815_platform_devices[] __initdata = { static struct platform_device *nhk8815_platform_devices[] __initdata = {
&nhk8815_nand_device,
&nhk8815_onenand_device,
&nhk8815_eth_device, &nhk8815_eth_device,
/* will add more devices */ /* will add more devices */
}; };
@ -90,6 +244,7 @@ static void __init nhk8815_platform_init(void)
int i; int i;
cpu8815_platform_init(); cpu8815_platform_init();
nhk8815_onenand_init();
platform_add_devices(nhk8815_platform_devices, platform_add_devices(nhk8815_platform_devices,
ARRAY_SIZE(nhk8815_platform_devices)); ARRAY_SIZE(nhk8815_platform_devices));

View file

@ -0,0 +1,29 @@
/* Definitions for the Nomadik FSMC "Flexible Static Memory controller" */
#ifndef __ASM_ARCH_FSMC_H
#define __ASM_ARCH_FSMC_H
#include <mach/hardware.h>
/*
* Register list
*/
/* bus control reg. and bus timing reg. for CS0..CS3 */
#define FSMC_BCR(x) (NOMADIK_FSMC_VA + (x << 3))
#define FSMC_BTR(x) (NOMADIK_FSMC_VA + (x << 3) + 0x04)
/* PC-card and NAND:
* PCR = control register
* PMEM = memory timing
* PATT = attribute timing
* PIO = I/O timing
* PECCR = ECC result
*/
#define FSMC_PCR(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x00)
#define FSMC_PMEM(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x08)
#define FSMC_PATT(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x0c)
#define FSMC_PIO(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x10)
#define FSMC_PECCR(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x14)
#endif /* __ASM_ARCH_FSMC_H */

View file

@ -0,0 +1,16 @@
#ifndef __ASM_ARCH_NAND_H
#define __ASM_ARCH_NAND_H
struct nomadik_nand_platform_data {
struct mtd_partition *parts;
int nparts;
int options;
int (*init) (void);
int (*exit) (void);
};
#define NAND_IO_DATA 0x40000000
#define NAND_IO_CMD 0x40800000
#define NAND_IO_ADDR 0x41000000
#endif /* __ASM_ARCH_NAND_H */

View file

@ -87,7 +87,7 @@ static struct mtd_partition apollon_partitions[] = {
}, },
}; };
static struct flash_platform_data apollon_flash_data = { static struct onenand_platform_data apollon_flash_data = {
.parts = apollon_partitions, .parts = apollon_partitions,
.nr_parts = ARRAY_SIZE(apollon_partitions), .nr_parts = ARRAY_SIZE(apollon_partitions),
}; };
@ -99,7 +99,7 @@ static struct resource apollon_flash_resource[] = {
}; };
static struct platform_device apollon_onenand_device = { static struct platform_device apollon_onenand_device = {
.name = "onenand", .name = "onenand-flash",
.id = -1, .id = -1,
.dev = { .dev = {
.platform_data = &apollon_flash_data, .platform_data = &apollon_flash_data,

View file

@ -57,6 +57,11 @@
#define GPMC_CHUNK_SHIFT 24 /* 16 MB */ #define GPMC_CHUNK_SHIFT 24 /* 16 MB */
#define GPMC_SECTION_SHIFT 28 /* 128 MB */ #define GPMC_SECTION_SHIFT 28 /* 128 MB */
#define PREFETCH_FIFOTHRESHOLD (0x40 << 8)
#define CS_NUM_SHIFT 24
#define ENABLE_PREFETCH (0x1 << 7)
#define DMA_MPU_MODE 2
static struct resource gpmc_mem_root; static struct resource gpmc_mem_root;
static struct resource gpmc_cs_mem[GPMC_CS_NUM]; static struct resource gpmc_cs_mem[GPMC_CS_NUM];
static DEFINE_SPINLOCK(gpmc_mem_lock); static DEFINE_SPINLOCK(gpmc_mem_lock);
@ -386,6 +391,63 @@ void gpmc_cs_free(int cs)
} }
EXPORT_SYMBOL(gpmc_cs_free); EXPORT_SYMBOL(gpmc_cs_free);
/**
* gpmc_prefetch_enable - configures and starts prefetch transfer
* @cs: nand cs (chip select) number
* @dma_mode: dma mode enable (1) or disable (0)
* @u32_count: number of bytes to be transferred
* @is_write: prefetch read(0) or write post(1) mode
*/
int gpmc_prefetch_enable(int cs, int dma_mode,
unsigned int u32_count, int is_write)
{
uint32_t prefetch_config1;
if (!(gpmc_read_reg(GPMC_PREFETCH_CONTROL))) {
/* Set the amount of bytes to be prefetched */
gpmc_write_reg(GPMC_PREFETCH_CONFIG2, u32_count);
/* Set dma/mpu mode, the prefetch read / post write and
* enable the engine. Set which cs is has requested for.
*/
prefetch_config1 = ((cs << CS_NUM_SHIFT) |
PREFETCH_FIFOTHRESHOLD |
ENABLE_PREFETCH |
(dma_mode << DMA_MPU_MODE) |
(0x1 & is_write));
gpmc_write_reg(GPMC_PREFETCH_CONFIG1, prefetch_config1);
} else {
return -EBUSY;
}
/* Start the prefetch engine */
gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x1);
return 0;
}
EXPORT_SYMBOL(gpmc_prefetch_enable);
/**
* gpmc_prefetch_reset - disables and stops the prefetch engine
*/
void gpmc_prefetch_reset(void)
{
/* Stop the PFPW engine */
gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x0);
/* Reset/disable the PFPW engine */
gpmc_write_reg(GPMC_PREFETCH_CONFIG1, 0x0);
}
EXPORT_SYMBOL(gpmc_prefetch_reset);
/**
* gpmc_prefetch_status - reads prefetch status of engine
*/
int gpmc_prefetch_status(void)
{
return gpmc_read_reg(GPMC_PREFETCH_STATUS);
}
EXPORT_SYMBOL(gpmc_prefetch_status);
static void __init gpmc_mem_init(void) static void __init gpmc_mem_init(void)
{ {
int cs; int cs;
@ -452,6 +514,5 @@ void __init gpmc_init(void)
l &= 0x03 << 3; l &= 0x03 << 3;
l |= (0x02 << 3) | (1 << 0); l |= (0x02 << 3) | (1 << 0);
gpmc_write_reg(GPMC_SYSCONFIG, l); gpmc_write_reg(GPMC_SYSCONFIG, l);
gpmc_mem_init(); gpmc_mem_init();
} }

View file

@ -103,6 +103,10 @@ extern int gpmc_cs_request(int cs, unsigned long size, unsigned long *base);
extern void gpmc_cs_free(int cs); extern void gpmc_cs_free(int cs);
extern int gpmc_cs_set_reserved(int cs, int reserved); extern int gpmc_cs_set_reserved(int cs, int reserved);
extern int gpmc_cs_reserved(int cs); extern int gpmc_cs_reserved(int cs);
extern int gpmc_prefetch_enable(int cs, int dma_mode,
unsigned int u32_count, int is_write);
extern void gpmc_prefetch_reset(void);
extern int gpmc_prefetch_status(void);
extern void __init gpmc_init(void); extern void __init gpmc_init(void);
#endif #endif

View file

@ -43,6 +43,8 @@ obj-y += macintosh/
obj-$(CONFIG_IDE) += ide/ obj-$(CONFIG_IDE) += ide/
obj-$(CONFIG_SCSI) += scsi/ obj-$(CONFIG_SCSI) += scsi/
obj-$(CONFIG_ATA) += ata/ obj-$(CONFIG_ATA) += ata/
obj-$(CONFIG_MTD) += mtd/
obj-$(CONFIG_SPI) += spi/
obj-y += net/ obj-y += net/
obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_FUSION) += message/ obj-$(CONFIG_FUSION) += message/
@ -51,8 +53,6 @@ obj-y += ieee1394/
obj-$(CONFIG_UIO) += uio/ obj-$(CONFIG_UIO) += uio/
obj-y += cdrom/ obj-y += cdrom/
obj-y += auxdisplay/ obj-y += auxdisplay/
obj-$(CONFIG_MTD) += mtd/
obj-$(CONFIG_SPI) += spi/
obj-$(CONFIG_PCCARD) += pcmcia/ obj-$(CONFIG_PCCARD) += pcmcia/
obj-$(CONFIG_DIO) += dio/ obj-$(CONFIG_DIO) += dio/
obj-$(CONFIG_SBUS) += sbus/ obj-$(CONFIG_SBUS) += sbus/

View file

@ -25,6 +25,14 @@ config MTD_DEBUG_VERBOSE
help help
Determines the verbosity level of the MTD debugging messages. Determines the verbosity level of the MTD debugging messages.
config MTD_TESTS
tristate "MTD tests support"
depends on m
help
This option includes various MTD tests into compilation. The tests
should normally be compiled as kernel modules. The modules perform
various checks and verifications when loaded.
config MTD_CONCAT config MTD_CONCAT
tristate "MTD concatenating support" tristate "MTD concatenating support"
help help
@ -45,14 +53,6 @@ config MTD_PARTITIONS
devices. Partitioning on NFTL 'devices' is a different - that's the devices. Partitioning on NFTL 'devices' is a different - that's the
'normal' form of partitioning used on a block device. 'normal' form of partitioning used on a block device.
config MTD_TESTS
tristate "MTD tests support"
depends on m
help
This option includes various MTD tests into compilation. The tests
should normally be compiled as kernel modules. The modules perform
various checks and verifications when loaded.
config MTD_REDBOOT_PARTS config MTD_REDBOOT_PARTS
tristate "RedBoot partition table parsing" tristate "RedBoot partition table parsing"
depends on MTD_PARTITIONS depends on MTD_PARTITIONS

View file

@ -239,7 +239,7 @@ static int parse_afs_partitions(struct mtd_info *mtd,
parts[idx].offset = img_ptr; parts[idx].offset = img_ptr;
parts[idx].mask_flags = 0; parts[idx].mask_flags = 0;
printk(" mtd%d: at 0x%08x, %5dKB, %8u, %s\n", printk(" mtd%d: at 0x%08x, %5lluKiB, %8u, %s\n",
idx, img_ptr, parts[idx].size / 1024, idx, img_ptr, parts[idx].size / 1024,
iis.imageNumber, str); iis.imageNumber, str);

View file

@ -282,16 +282,6 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param)
} }
} }
static void fixup_M29W128G_write_buffer(struct mtd_info *mtd, void *param)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
if (cfi->cfiq->BufWriteTimeoutTyp) {
pr_warning("Don't use write buffer on ST flash M29W128G\n");
cfi->cfiq->BufWriteTimeoutTyp = 0;
}
}
static struct cfi_fixup cfi_fixup_table[] = { static struct cfi_fixup cfi_fixup_table[] = {
{ CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL },
#ifdef AMD_BOOTLOC_BUG #ifdef AMD_BOOTLOC_BUG
@ -308,7 +298,6 @@ static struct cfi_fixup cfi_fixup_table[] = {
{ CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, }, { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, },
{ CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, }, { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, },
{ CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, }, { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, },
{ CFI_MFR_ST, 0x227E, fixup_M29W128G_write_buffer, NULL, },
#if !FORCE_WORD_WRITE #if !FORCE_WORD_WRITE
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, },
#endif #endif

4
drivers/mtd/chips/cfi_util.c Normal file → Executable file
View file

@ -81,6 +81,10 @@ void __xipram cfi_qry_mode_off(uint32_t base, struct map_info *map,
{ {
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
/* M29W128G flashes require an additional reset command
when exit qry mode */
if ((cfi->mfr == CFI_MFR_ST) && (cfi->id == 0x227E || cfi->id == 0x7E))
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
} }
EXPORT_SYMBOL_GPL(cfi_qry_mode_off); EXPORT_SYMBOL_GPL(cfi_qry_mode_off);

View file

@ -111,6 +111,11 @@
#define I28F320B3B 0x8897 #define I28F320B3B 0x8897
#define I28F640B3T 0x8898 #define I28F640B3T 0x8898
#define I28F640B3B 0x8899 #define I28F640B3B 0x8899
#define I28F640C3B 0x88CD
#define I28F160F3T 0x88F3
#define I28F160F3B 0x88F4
#define I28F160C3T 0x88C2
#define I28F160C3B 0x88C3
#define I82802AB 0x00ad #define I82802AB 0x00ad
#define I82802AC 0x00ac #define I82802AC 0x00ac
@ -150,6 +155,7 @@
#define M50LPW080 0x002F #define M50LPW080 0x002F
#define M50FLW080A 0x0080 #define M50FLW080A 0x0080
#define M50FLW080B 0x0081 #define M50FLW080B 0x0081
#define PSD4256G6V 0x00e9
/* SST */ /* SST */
#define SST29EE020 0x0010 #define SST29EE020 0x0010
@ -201,6 +207,7 @@ enum uaddr {
MTD_UADDR_0x0555_0x02AA, MTD_UADDR_0x0555_0x02AA,
MTD_UADDR_0x0555_0x0AAA, MTD_UADDR_0x0555_0x0AAA,
MTD_UADDR_0x5555_0x2AAA, MTD_UADDR_0x5555_0x2AAA,
MTD_UADDR_0x0AAA_0x0554,
MTD_UADDR_0x0AAA_0x0555, MTD_UADDR_0x0AAA_0x0555,
MTD_UADDR_0xAAAA_0x5555, MTD_UADDR_0xAAAA_0x5555,
MTD_UADDR_DONT_CARE, /* Requires an arbitrary address */ MTD_UADDR_DONT_CARE, /* Requires an arbitrary address */
@ -245,6 +252,11 @@ static const struct unlock_addr unlock_addrs[] = {
.addr2 = 0x2aaa .addr2 = 0x2aaa
}, },
[MTD_UADDR_0x0AAA_0x0554] = {
.addr1 = 0x0AAA,
.addr2 = 0x0554
},
[MTD_UADDR_0x0AAA_0x0555] = { [MTD_UADDR_0x0AAA_0x0555] = {
.addr1 = 0x0AAA, .addr1 = 0x0AAA,
.addr2 = 0x0555 .addr2 = 0x0555
@ -1101,6 +1113,19 @@ static const struct amd_flash_info jedec_table[] = {
ERASEINFO(0x10000, 127), ERASEINFO(0x10000, 127),
ERASEINFO(0x02000, 8), ERASEINFO(0x02000, 8),
} }
}, {
.mfr_id = MANUFACTURER_INTEL,
.dev_id = I28F640C3B,
.name = "Intel 28F640C3B",
.devtypes = CFI_DEVICETYPE_X16,
.uaddr = MTD_UADDR_UNNECESSARY,
.dev_size = SIZE_8MiB,
.cmd_set = P_ID_INTEL_STD,
.nr_regions = 2,
.regions = {
ERASEINFO(0x02000, 8),
ERASEINFO(0x10000, 127),
}
}, { }, {
.mfr_id = MANUFACTURER_INTEL, .mfr_id = MANUFACTURER_INTEL,
.dev_id = I82802AB, .dev_id = I82802AB,
@ -1156,8 +1181,8 @@ static const struct amd_flash_info jedec_table[] = {
.mfr_id = MANUFACTURER_NEC, .mfr_id = MANUFACTURER_NEC,
.dev_id = UPD29F064115, .dev_id = UPD29F064115,
.name = "NEC uPD29F064115", .name = "NEC uPD29F064115",
.devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .devtypes = CFI_DEVICETYPE_X16,
.uaddr = MTD_UADDR_0x0555_0x02AA, /* ???? */ .uaddr = MTD_UADDR_0xAAAA_0x5555,
.dev_size = SIZE_8MiB, .dev_size = SIZE_8MiB,
.cmd_set = P_ID_AMD_STD, .cmd_set = P_ID_AMD_STD,
.nr_regions = 3, .nr_regions = 3,
@ -1725,6 +1750,18 @@ static const struct amd_flash_info jedec_table[] = {
ERASEINFO(0x10000,13), ERASEINFO(0x10000,13),
ERASEINFO(0x1000,16), ERASEINFO(0x1000,16),
} }
}, {
.mfr_id = 0xff00 | MANUFACTURER_ST,
.dev_id = 0xff00 | PSD4256G6V,
.name = "ST PSD4256G6V",
.devtypes = CFI_DEVICETYPE_X16,
.uaddr = MTD_UADDR_0x0AAA_0x0554,
.dev_size = SIZE_1MiB,
.cmd_set = P_ID_AMD_STD,
.nr_regions = 1,
.regions = {
ERASEINFO(0x10000,16),
}
}, { }, {
.mfr_id = MANUFACTURER_TOSHIBA, .mfr_id = MANUFACTURER_TOSHIBA,
.dev_id = TC58FVT160, .dev_id = TC58FVT160,

View file

@ -104,6 +104,16 @@ config M25PXX_USE_FAST_READ
help help
This option enables FAST_READ access supported by ST M25Pxx. This option enables FAST_READ access supported by ST M25Pxx.
config MTD_SST25L
tristate "Support SST25L (non JEDEC) SPI Flash chips"
depends on SPI_MASTER
help
This enables access to the non JEDEC SST25L SPI flash chips, used
for program and data storage.
Set up your spi devices with the right board-specific platform data,
if you want to specify device partitioning.
config MTD_SLRAM config MTD_SLRAM
tristate "Uncached system RAM" tristate "Uncached system RAM"
help help

View file

@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
obj-$(CONFIG_MTD_M25P80) += m25p80.o obj-$(CONFIG_MTD_M25P80) += m25p80.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o

View file

@ -393,7 +393,8 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
* erase range is aligned with the erase size which is in * erase range is aligned with the erase size which is in
* effect here. * effect here.
*/ */
if (instr->addr & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL); if (i < 0 || (instr->addr & (mtd->eraseregions[i].erasesize - 1)))
return -EINVAL;
/* Remember the erase region we start on */ /* Remember the erase region we start on */
first = i; first = i;
@ -409,7 +410,8 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
i--; i--;
/* is the end aligned on a block boundary? */ /* is the end aligned on a block boundary? */
if ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL); if (i < 0 || ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)))
return -EINVAL;
addr = instr->addr; addr = instr->addr;
len = instr->len; len = instr->len;

View file

@ -44,6 +44,11 @@
#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */
#define OPCODE_RDID 0x9f /* Read JEDEC ID */ #define OPCODE_RDID 0x9f /* Read JEDEC ID */
/* Used for SST flashes only. */
#define OPCODE_BP 0x02 /* Byte program */
#define OPCODE_WRDI 0x04 /* Write disable */
#define OPCODE_AAI_WP 0xad /* Auto address increment word program */
/* Status Register bits. */ /* Status Register bits. */
#define SR_WIP 1 /* Write in progress */ #define SR_WIP 1 /* Write in progress */
#define SR_WEL 2 /* Write enable latch */ #define SR_WEL 2 /* Write enable latch */
@ -132,6 +137,15 @@ static inline int write_enable(struct m25p *flash)
return spi_write_then_read(flash->spi, &code, 1, NULL, 0); return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
} }
/*
* Send write disble instruction to the chip.
*/
static inline int write_disable(struct m25p *flash)
{
u8 code = OPCODE_WRDI;
return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
}
/* /*
* Service routine to read status register until ready, or timeout occurs. * Service routine to read status register until ready, or timeout occurs.
@ -454,6 +468,111 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
return 0; return 0;
} }
static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct m25p *flash = mtd_to_m25p(mtd);
struct spi_transfer t[2];
struct spi_message m;
size_t actual;
int cmd_sz, ret;
if (retlen)
*retlen = 0;
/* sanity checks */
if (!len)
return 0;
if (to + len > flash->mtd.size)
return -EINVAL;
spi_message_init(&m);
memset(t, 0, (sizeof t));
t[0].tx_buf = flash->command;
t[0].len = CMD_SIZE;
spi_message_add_tail(&t[0], &m);
t[1].tx_buf = buf;
spi_message_add_tail(&t[1], &m);
mutex_lock(&flash->lock);
/* Wait until finished previous write command. */
ret = wait_till_ready(flash);
if (ret)
goto time_out;
write_enable(flash);
actual = to % 2;
/* Start write from odd address. */
if (actual) {
flash->command[0] = OPCODE_BP;
flash->command[1] = to >> 16;
flash->command[2] = to >> 8;
flash->command[3] = to;
/* write one byte. */
t[1].len = 1;
spi_sync(flash->spi, &m);
ret = wait_till_ready(flash);
if (ret)
goto time_out;
*retlen += m.actual_length - CMD_SIZE;
}
to += actual;
flash->command[0] = OPCODE_AAI_WP;
flash->command[1] = to >> 16;
flash->command[2] = to >> 8;
flash->command[3] = to;
/* Write out most of the data here. */
cmd_sz = CMD_SIZE;
for (; actual < len - 1; actual += 2) {
t[0].len = cmd_sz;
/* write two bytes. */
t[1].len = 2;
t[1].tx_buf = buf + actual;
spi_sync(flash->spi, &m);
ret = wait_till_ready(flash);
if (ret)
goto time_out;
*retlen += m.actual_length - cmd_sz;
cmd_sz = 1;
to += 2;
}
write_disable(flash);
ret = wait_till_ready(flash);
if (ret)
goto time_out;
/* Write out trailing byte if it exists. */
if (actual != len) {
write_enable(flash);
flash->command[0] = OPCODE_BP;
flash->command[1] = to >> 16;
flash->command[2] = to >> 8;
flash->command[3] = to;
t[0].len = CMD_SIZE;
t[1].len = 1;
t[1].tx_buf = buf + actual;
spi_sync(flash->spi, &m);
ret = wait_till_ready(flash);
if (ret)
goto time_out;
*retlen += m.actual_length - CMD_SIZE;
write_disable(flash);
}
time_out:
mutex_unlock(&flash->lock);
return ret;
}
/****************************************************************************/ /****************************************************************************/
@ -501,7 +620,10 @@ static struct flash_info __devinitdata m25p_data [] = {
{ "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, }, { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, },
/* Macronix */ /* Macronix */
{ "mx25l3205d", 0xc22016, 0, 64 * 1024, 64, },
{ "mx25l6405d", 0xc22017, 0, 64 * 1024, 128, },
{ "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, }, { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, },
{ "mx25l12855e", 0xc22618, 0, 64 * 1024, 256, },
/* Spansion -- single (large) sector size only, at least /* Spansion -- single (large) sector size only, at least
* for the chips listed here (without boot sectors). * for the chips listed here (without boot sectors).
@ -513,12 +635,18 @@ static struct flash_info __devinitdata m25p_data [] = {
{ "s25sl064a", 0x010216, 0, 64 * 1024, 128, }, { "s25sl064a", 0x010216, 0, 64 * 1024, 128, },
{ "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, }, { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, },
{ "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, }, { "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, },
{ "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, },
{ "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, },
/* SST -- large erase sizes are "overlays", "sectors" are 4K */ /* SST -- large erase sizes are "overlays", "sectors" are 4K */
{ "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SECT_4K, }, { "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SECT_4K, },
{ "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SECT_4K, }, { "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SECT_4K, },
{ "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SECT_4K, }, { "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SECT_4K, },
{ "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SECT_4K, }, { "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SECT_4K, },
{ "sst25wf512", 0xbf2501, 0, 64 * 1024, 1, SECT_4K, },
{ "sst25wf010", 0xbf2502, 0, 64 * 1024, 2, SECT_4K, },
{ "sst25wf020", 0xbf2503, 0, 64 * 1024, 4, SECT_4K, },
{ "sst25wf040", 0xbf2504, 0, 64 * 1024, 8, SECT_4K, },
/* ST Microelectronics -- newer production may have feature updates */ /* ST Microelectronics -- newer production may have feature updates */
{ "m25p05", 0x202010, 0, 32 * 1024, 2, }, { "m25p05", 0x202010, 0, 32 * 1024, 2, },
@ -667,6 +795,11 @@ static int __devinit m25p_probe(struct spi_device *spi)
flash->mtd.size = info->sector_size * info->n_sectors; flash->mtd.size = info->sector_size * info->n_sectors;
flash->mtd.erase = m25p80_erase; flash->mtd.erase = m25p80_erase;
flash->mtd.read = m25p80_read; flash->mtd.read = m25p80_read;
/* sst flash chips use AAI word program */
if (info->jedec_id >> 16 == 0xbf)
flash->mtd.write = sst_write;
else
flash->mtd.write = m25p80_write; flash->mtd.write = m25p80_write;
/* prefer "small sector" erase if possible */ /* prefer "small sector" erase if possible */

View file

@ -401,7 +401,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
(void) dataflash_waitready(priv->spi); (void) dataflash_waitready(priv->spi);
#ifdef CONFIG_MTD_DATAFLASH_VERIFY_WRITE #ifdef CONFIG_MTD_DATAFLASH_WRITE_VERIFY
/* (3) Compare to Buffer1 */ /* (3) Compare to Buffer1 */
addr = pageaddr << priv->page_offset; addr = pageaddr << priv->page_offset;
@ -430,7 +430,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
} else } else
status = 0; status = 0;
#endif /* CONFIG_MTD_DATAFLASH_VERIFY_WRITE */ #endif /* CONFIG_MTD_DATAFLASH_WRITE_VERIFY */
remaining = remaining - writelen; remaining = remaining - writelen;
pageaddr++; pageaddr++;

View file

@ -14,6 +14,9 @@
* Example: * Example:
* phram=swap,64Mi,128Mi phram=test,900Mi,1Mi * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi
*/ */
#define pr_fmt(fmt) "phram: " fmt
#include <asm/io.h> #include <asm/io.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
@ -23,8 +26,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args)
struct phram_mtd_list { struct phram_mtd_list {
struct mtd_info mtd; struct mtd_info mtd;
struct list_head list; struct list_head list;
@ -132,7 +133,7 @@ static int register_device(char *name, unsigned long start, unsigned long len)
ret = -EIO; ret = -EIO;
new->mtd.priv = ioremap(start, len); new->mtd.priv = ioremap(start, len);
if (!new->mtd.priv) { if (!new->mtd.priv) {
ERROR("ioremap failed\n"); pr_err("ioremap failed\n");
goto out1; goto out1;
} }
@ -152,7 +153,7 @@ static int register_device(char *name, unsigned long start, unsigned long len)
ret = -EAGAIN; ret = -EAGAIN;
if (add_mtd_device(&new->mtd)) { if (add_mtd_device(&new->mtd)) {
ERROR("Failed to register new device\n"); pr_err("Failed to register new device\n");
goto out2; goto out2;
} }
@ -227,8 +228,8 @@ static inline void kill_final_newline(char *str)
#define parse_err(fmt, args...) do { \ #define parse_err(fmt, args...) do { \
ERROR(fmt , ## args); \ pr_err(fmt , ## args); \
return 0; \ return 1; \
} while (0) } while (0)
static int phram_setup(const char *val, struct kernel_param *kp) static int phram_setup(const char *val, struct kernel_param *kp)
@ -256,12 +257,8 @@ static int phram_setup(const char *val, struct kernel_param *kp)
parse_err("not enough arguments\n"); parse_err("not enough arguments\n");
ret = parse_name(&name, token[0]); ret = parse_name(&name, token[0]);
if (ret == -ENOMEM)
parse_err("out of memory\n");
if (ret == -ENOSPC)
parse_err("name too long\n");
if (ret) if (ret)
return 0; return ret;
ret = parse_num32(&start, token[1]); ret = parse_num32(&start, token[1]);
if (ret) { if (ret) {
@ -275,9 +272,11 @@ static int phram_setup(const char *val, struct kernel_param *kp)
parse_err("illegal device length\n"); parse_err("illegal device length\n");
} }
register_device(name, start, len); ret = register_device(name, start, len);
if (!ret)
pr_info("%s device: %#x at %#x\n", name, len, start);
return 0; return ret;
} }
module_param_call(phram, phram_setup, NULL, NULL, 000); module_param_call(phram, phram_setup, NULL, NULL, 000);

View file

@ -341,7 +341,7 @@ static int __init init_slram(void)
#else #else
int count; int count;
for (count = 0; (map[count]) && (count < SLRAM_MAX_DEVICES_PARAMS); for (count = 0; count < SLRAM_MAX_DEVICES_PARAMS && map[count];
count++) { count++) {
} }

View file

@ -0,0 +1,512 @@
/*
* sst25l.c
*
* Driver for SST25L SPI Flash chips
*
* Copyright © 2009 Bluewater Systems Ltd
* Author: Andre Renaud <andre@bluewatersys.com>
* Author: Ryan Mallon <ryan@bluewatersys.com>
*
* Based on m25p80.c
*
* This code 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/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
/* Erases can take up to 3 seconds! */
#define MAX_READY_WAIT_JIFFIES msecs_to_jiffies(3000)
#define SST25L_CMD_WRSR 0x01 /* Write status register */
#define SST25L_CMD_WRDI 0x04 /* Write disable */
#define SST25L_CMD_RDSR 0x05 /* Read status register */
#define SST25L_CMD_WREN 0x06 /* Write enable */
#define SST25L_CMD_READ 0x03 /* High speed read */
#define SST25L_CMD_EWSR 0x50 /* Enable write status register */
#define SST25L_CMD_SECTOR_ERASE 0x20 /* Erase sector */
#define SST25L_CMD_READ_ID 0x90 /* Read device ID */
#define SST25L_CMD_AAI_PROGRAM 0xaf /* Auto address increment */
#define SST25L_STATUS_BUSY (1 << 0) /* Chip is busy */
#define SST25L_STATUS_WREN (1 << 1) /* Write enabled */
#define SST25L_STATUS_BP0 (1 << 2) /* Block protection 0 */
#define SST25L_STATUS_BP1 (1 << 3) /* Block protection 1 */
struct sst25l_flash {
struct spi_device *spi;
struct mutex lock;
struct mtd_info mtd;
int partitioned;
};
struct flash_info {
const char *name;
uint16_t device_id;
unsigned page_size;
unsigned nr_pages;
unsigned erase_size;
};
#define to_sst25l_flash(x) container_of(x, struct sst25l_flash, mtd)
static struct flash_info __initdata sst25l_flash_info[] = {
{"sst25lf020a", 0xbf43, 256, 1024, 4096},
{"sst25lf040a", 0xbf44, 256, 2048, 4096},
};
static int sst25l_status(struct sst25l_flash *flash, int *status)
{
unsigned char command, response;
int err;
command = SST25L_CMD_RDSR;
err = spi_write_then_read(flash->spi, &command, 1, &response, 1);
if (err < 0)
return err;
*status = response;
return 0;
}
static int sst25l_write_enable(struct sst25l_flash *flash, int enable)
{
unsigned char command[2];
int status, err;
command[0] = enable ? SST25L_CMD_WREN : SST25L_CMD_WRDI;
err = spi_write(flash->spi, command, 1);
if (err)
return err;
command[0] = SST25L_CMD_EWSR;
err = spi_write(flash->spi, command, 1);
if (err)
return err;
command[0] = SST25L_CMD_WRSR;
command[1] = enable ? 0 : SST25L_STATUS_BP0 | SST25L_STATUS_BP1;
err = spi_write(flash->spi, command, 2);
if (err)
return err;
if (enable) {
err = sst25l_status(flash, &status);
if (err)
return err;
if (!(status & SST25L_STATUS_WREN))
return -EROFS;
}
return 0;
}
static int sst25l_wait_till_ready(struct sst25l_flash *flash)
{
unsigned long deadline;
int status, err;
deadline = jiffies + MAX_READY_WAIT_JIFFIES;
do {
err = sst25l_status(flash, &status);
if (err)
return err;
if (!(status & SST25L_STATUS_BUSY))
return 0;
cond_resched();
} while (!time_after_eq(jiffies, deadline));
return -ETIMEDOUT;
}
static int sst25l_erase_sector(struct sst25l_flash *flash, uint32_t offset)
{
unsigned char command[4];
int err;
err = sst25l_write_enable(flash, 1);
if (err)
return err;
command[0] = SST25L_CMD_SECTOR_ERASE;
command[1] = offset >> 16;
command[2] = offset >> 8;
command[3] = offset;
err = spi_write(flash->spi, command, 4);
if (err)
return err;
err = sst25l_wait_till_ready(flash);
if (err)
return err;
return sst25l_write_enable(flash, 0);
}
static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct sst25l_flash *flash = to_sst25l_flash(mtd);
uint32_t addr, end;
int err;
/* Sanity checks */
if (instr->addr + instr->len > flash->mtd.size)
return -EINVAL;
if ((uint32_t)instr->len % mtd->erasesize)
return -EINVAL;
if ((uint32_t)instr->addr % mtd->erasesize)
return -EINVAL;
addr = instr->addr;
end = addr + instr->len;
mutex_lock(&flash->lock);
err = sst25l_wait_till_ready(flash);
if (err) {
mutex_unlock(&flash->lock);
return err;
}
while (addr < end) {
err = sst25l_erase_sector(flash, addr);
if (err) {
mutex_unlock(&flash->lock);
instr->state = MTD_ERASE_FAILED;
dev_err(&flash->spi->dev, "Erase failed\n");
return err;
}
addr += mtd->erasesize;
}
mutex_unlock(&flash->lock);
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return 0;
}
static int sst25l_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, unsigned char *buf)
{
struct sst25l_flash *flash = to_sst25l_flash(mtd);
struct spi_transfer transfer[2];
struct spi_message message;
unsigned char command[4];
int ret;
/* Sanity checking */
if (len == 0)
return 0;
if (from + len > flash->mtd.size)
return -EINVAL;
if (retlen)
*retlen = 0;
spi_message_init(&message);
memset(&transfer, 0, sizeof(transfer));
command[0] = SST25L_CMD_READ;
command[1] = from >> 16;
command[2] = from >> 8;
command[3] = from;
transfer[0].tx_buf = command;
transfer[0].len = sizeof(command);
spi_message_add_tail(&transfer[0], &message);
transfer[1].rx_buf = buf;
transfer[1].len = len;
spi_message_add_tail(&transfer[1], &message);
mutex_lock(&flash->lock);
/* Wait for previous write/erase to complete */
ret = sst25l_wait_till_ready(flash);
if (ret) {
mutex_unlock(&flash->lock);
return ret;
}
spi_sync(flash->spi, &message);
if (retlen && message.actual_length > sizeof(command))
*retlen += message.actual_length - sizeof(command);
mutex_unlock(&flash->lock);
return 0;
}
static int sst25l_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const unsigned char *buf)
{
struct sst25l_flash *flash = to_sst25l_flash(mtd);
int i, j, ret, bytes, copied = 0;
unsigned char command[5];
/* Sanity checks */
if (!len)
return 0;
if (to + len > flash->mtd.size)
return -EINVAL;
if ((uint32_t)to % mtd->writesize)
return -EINVAL;
mutex_lock(&flash->lock);
ret = sst25l_write_enable(flash, 1);
if (ret)
goto out;
for (i = 0; i < len; i += mtd->writesize) {
ret = sst25l_wait_till_ready(flash);
if (ret)
goto out;
/* Write the first byte of the page */
command[0] = SST25L_CMD_AAI_PROGRAM;
command[1] = (to + i) >> 16;
command[2] = (to + i) >> 8;
command[3] = (to + i);
command[4] = buf[i];
ret = spi_write(flash->spi, command, 5);
if (ret < 0)
goto out;
copied++;
/*
* Write the remaining bytes using auto address
* increment mode
*/
bytes = min_t(uint32_t, mtd->writesize, len - i);
for (j = 1; j < bytes; j++, copied++) {
ret = sst25l_wait_till_ready(flash);
if (ret)
goto out;
command[1] = buf[i + j];
ret = spi_write(flash->spi, command, 2);
if (ret)
goto out;
}
}
out:
ret = sst25l_write_enable(flash, 0);
if (retlen)
*retlen = copied;
mutex_unlock(&flash->lock);
return ret;
}
static struct flash_info *__init sst25l_match_device(struct spi_device *spi)
{
struct flash_info *flash_info = NULL;
unsigned char command[4], response;
int i, err;
uint16_t id;
command[0] = SST25L_CMD_READ_ID;
command[1] = 0;
command[2] = 0;
command[3] = 0;
err = spi_write_then_read(spi, command, sizeof(command), &response, 1);
if (err < 0) {
dev_err(&spi->dev, "error reading device id msb\n");
return NULL;
}
id = response << 8;
command[0] = SST25L_CMD_READ_ID;
command[1] = 0;
command[2] = 0;
command[3] = 1;
err = spi_write_then_read(spi, command, sizeof(command), &response, 1);
if (err < 0) {
dev_err(&spi->dev, "error reading device id lsb\n");
return NULL;
}
id |= response;
for (i = 0; i < ARRAY_SIZE(sst25l_flash_info); i++)
if (sst25l_flash_info[i].device_id == id)
flash_info = &sst25l_flash_info[i];
if (!flash_info)
dev_err(&spi->dev, "unknown id %.4x\n", id);
return flash_info;
}
static int __init sst25l_probe(struct spi_device *spi)
{
struct flash_info *flash_info;
struct sst25l_flash *flash;
struct flash_platform_data *data;
int ret, i;
flash_info = sst25l_match_device(spi);
if (!flash_info)
return -ENODEV;
flash = kzalloc(sizeof(struct sst25l_flash), GFP_KERNEL);
if (!flash)
return -ENOMEM;
flash->spi = spi;
mutex_init(&flash->lock);
dev_set_drvdata(&spi->dev, flash);
data = spi->dev.platform_data;
if (data && data->name)
flash->mtd.name = data->name;
else
flash->mtd.name = dev_name(&spi->dev);
flash->mtd.type = MTD_NORFLASH;
flash->mtd.flags = MTD_CAP_NORFLASH;
flash->mtd.erasesize = flash_info->erase_size;
flash->mtd.writesize = flash_info->page_size;
flash->mtd.size = flash_info->page_size * flash_info->nr_pages;
flash->mtd.erase = sst25l_erase;
flash->mtd.read = sst25l_read;
flash->mtd.write = sst25l_write;
dev_info(&spi->dev, "%s (%lld KiB)\n", flash_info->name,
(long long)flash->mtd.size >> 10);
DEBUG(MTD_DEBUG_LEVEL2,
"mtd .name = %s, .size = 0x%llx (%lldMiB) "
".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
flash->mtd.name,
(long long)flash->mtd.size, (long long)(flash->mtd.size >> 20),
flash->mtd.erasesize, flash->mtd.erasesize / 1024,
flash->mtd.numeraseregions);
if (flash->mtd.numeraseregions)
for (i = 0; i < flash->mtd.numeraseregions; i++)
DEBUG(MTD_DEBUG_LEVEL2,
"mtd.eraseregions[%d] = { .offset = 0x%llx, "
".erasesize = 0x%.8x (%uKiB), "
".numblocks = %d }\n",
i, (long long)flash->mtd.eraseregions[i].offset,
flash->mtd.eraseregions[i].erasesize,
flash->mtd.eraseregions[i].erasesize / 1024,
flash->mtd.eraseregions[i].numblocks);
if (mtd_has_partitions()) {
struct mtd_partition *parts = NULL;
int nr_parts = 0;
if (mtd_has_cmdlinepart()) {
static const char *part_probes[] =
{"cmdlinepart", NULL};
nr_parts = parse_mtd_partitions(&flash->mtd,
part_probes,
&parts, 0);
}
if (nr_parts <= 0 && data && data->parts) {
parts = data->parts;
nr_parts = data->nr_parts;
}
if (nr_parts > 0) {
for (i = 0; i < nr_parts; i++) {
DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = "
"{.name = %s, .offset = 0x%llx, "
".size = 0x%llx (%lldKiB) }\n",
i, parts[i].name,
(long long)parts[i].offset,
(long long)parts[i].size,
(long long)(parts[i].size >> 10));
}
flash->partitioned = 1;
return add_mtd_partitions(&flash->mtd,
parts, nr_parts);
}
} else if (data->nr_parts) {
dev_warn(&spi->dev, "ignoring %d default partitions on %s\n",
data->nr_parts, data->name);
}
ret = add_mtd_device(&flash->mtd);
if (ret == 1) {
kfree(flash);
dev_set_drvdata(&spi->dev, NULL);
return -ENODEV;
}
return 0;
}
static int __exit sst25l_remove(struct spi_device *spi)
{
struct sst25l_flash *flash = dev_get_drvdata(&spi->dev);
int ret;
if (mtd_has_partitions() && flash->partitioned)
ret = del_mtd_partitions(&flash->mtd);
else
ret = del_mtd_device(&flash->mtd);
if (ret == 0)
kfree(flash);
return ret;
}
static struct spi_driver sst25l_driver = {
.driver = {
.name = "sst25l",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = sst25l_probe,
.remove = __exit_p(sst25l_remove),
};
static int __init sst25l_init(void)
{
return spi_register_driver(&sst25l_driver);
}
static void __exit sst25l_exit(void)
{
spi_unregister_driver(&sst25l_driver);
}
module_init(sst25l_init);
module_exit(sst25l_exit);
MODULE_DESCRIPTION("MTD SPI driver for SST25L Flash chips");
MODULE_AUTHOR("Andre Renaud <andre@bluewatersys.com>, "
"Ryan Mallon <ryan@bluewatersys.com>");
MODULE_LICENSE("GPL");

2
drivers/mtd/inftlcore.c Normal file → Executable file
View file

@ -550,7 +550,7 @@ hitused:
* waiting to be picked up. We're going to have to fold * waiting to be picked up. We're going to have to fold
* a chain to make room. * a chain to make room.
*/ */
thisEUN = INFTL_makefreeblock(inftl, BLOCK_NIL); thisEUN = INFTL_makefreeblock(inftl, block);
/* /*
* Hopefully we free something, lets try again. * Hopefully we free something, lets try again.

View file

@ -484,9 +484,19 @@ config MTD_BFIN_ASYNC
If compiled as a module, it will be called bfin-async-flash. If compiled as a module, it will be called bfin-async-flash.
config MTD_GPIO_ADDR
tristate "GPIO-assisted Flash Chip Support"
depends on MTD_COMPLEX_MAPPINGS
select MTD_PARTITIONS
help
Map driver which allows flashes to be partially physically addressed
and assisted by GPIOs.
If compiled as a module, it will be called gpio-addr-flash.
config MTD_UCLINUX config MTD_UCLINUX
bool "Generic uClinux RAM/ROM filesystem support" bool "Generic uClinux RAM/ROM filesystem support"
depends on MTD_PARTITIONS && MTD_RAM && !MMU depends on MTD_PARTITIONS && MTD_RAM=y && !MMU
help help
Map driver to support image based filesystems for uClinux. Map driver to support image based filesystems for uClinux.

View file

@ -58,5 +58,4 @@ obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o
obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o
obj-$(CONFIG_MTD_VMU) += vmu-flash.o

View file

@ -0,0 +1,311 @@
/*
* drivers/mtd/maps/gpio-addr-flash.c
*
* Handle the case where a flash device is mostly addressed using physical
* line and supplemented by GPIOs. This way you can hook up say a 8MiB flash
* to a 2MiB memory range and use the GPIOs to select a particular range.
*
* Copyright © 2000 Nicolas Pitre <nico@cam.org>
* Copyright © 2005-2009 Analog Devices Inc.
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Licensed under the GPL-2 or later.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <asm/gpio.h>
#include <asm/io.h>
#define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); })
#define DRIVER_NAME "gpio-addr-flash"
#define PFX DRIVER_NAME ": "
/**
* struct async_state - keep GPIO flash state
* @mtd: MTD state for this mapping
* @map: MTD map state for this flash
* @gpio_count: number of GPIOs used to address
* @gpio_addrs: array of GPIOs to twiddle
* @gpio_values: cached GPIO values
* @win_size: dedicated memory size (if no GPIOs)
*/
struct async_state {
struct mtd_info *mtd;
struct map_info map;
size_t gpio_count;
unsigned *gpio_addrs;
int *gpio_values;
unsigned long win_size;
};
#define gf_map_info_to_state(mi) ((struct async_state *)(mi)->map_priv_1)
/**
* gf_set_gpios() - set GPIO address lines to access specified flash offset
* @state: GPIO flash state
* @ofs: desired offset to access
*
* Rather than call the GPIO framework every time, cache the last-programmed
* value. This speeds up sequential accesses (which are by far the most common
* type). We rely on the GPIO framework to treat non-zero value as high so
* that we don't have to normalize the bits.
*/
static void gf_set_gpios(struct async_state *state, unsigned long ofs)
{
size_t i = 0;
int value;
ofs /= state->win_size;
do {
value = ofs & (1 << i);
if (state->gpio_values[i] != value) {
gpio_set_value(state->gpio_addrs[i], value);
state->gpio_values[i] = value;
}
} while (++i < state->gpio_count);
}
/**
* gf_read() - read a word at the specified offset
* @map: MTD map state
* @ofs: desired offset to read
*/
static map_word gf_read(struct map_info *map, unsigned long ofs)
{
struct async_state *state = gf_map_info_to_state(map);
uint16_t word;
map_word test;
gf_set_gpios(state, ofs);
word = readw(map->virt + (ofs % state->win_size));
test.x[0] = word;
return test;
}
/**
* gf_copy_from() - copy a chunk of data from the flash
* @map: MTD map state
* @to: memory to copy to
* @from: flash offset to copy from
* @len: how much to copy
*
* We rely on the MTD layer to chunk up copies such that a single request here
* will not cross a window size. This allows us to only wiggle the GPIOs once
* before falling back to a normal memcpy. Reading the higher layer code shows
* that this is indeed the case, but add a BUG_ON() to future proof.
*/
static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
struct async_state *state = gf_map_info_to_state(map);
gf_set_gpios(state, from);
/* BUG if operation crosses the win_size */
BUG_ON(!((from + len) % state->win_size <= (from + len)));
/* operation does not cross the win_size, so one shot it */
memcpy_fromio(to, map->virt + (from % state->win_size), len);
}
/**
* gf_write() - write a word at the specified offset
* @map: MTD map state
* @ofs: desired offset to write
*/
static void gf_write(struct map_info *map, map_word d1, unsigned long ofs)
{
struct async_state *state = gf_map_info_to_state(map);
uint16_t d;
gf_set_gpios(state, ofs);
d = d1.x[0];
writew(d, map->virt + (ofs % state->win_size));
}
/**
* gf_copy_to() - copy a chunk of data to the flash
* @map: MTD map state
* @to: flash offset to copy to
* @from: memory to copy from
* @len: how much to copy
*
* See gf_copy_from() caveat.
*/
static void gf_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
struct async_state *state = gf_map_info_to_state(map);
gf_set_gpios(state, to);
/* BUG if operation crosses the win_size */
BUG_ON(!((to + len) % state->win_size <= (to + len)));
/* operation does not cross the win_size, so one shot it */
memcpy_toio(map->virt + (to % state->win_size), from, len);
}
#ifdef CONFIG_MTD_PARTITIONS
static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL };
#endif
/**
* gpio_flash_probe() - setup a mapping for a GPIO assisted flash
* @pdev: platform device
*
* The platform resource layout expected looks something like:
* struct mtd_partition partitions[] = { ... };
* struct physmap_flash_data flash_data = { ... };
* unsigned flash_gpios[] = { GPIO_XX, GPIO_XX, ... };
* struct resource flash_resource[] = {
* {
* .name = "cfi_probe",
* .start = 0x20000000,
* .end = 0x201fffff,
* .flags = IORESOURCE_MEM,
* }, {
* .start = (unsigned long)flash_gpios,
* .end = ARRAY_SIZE(flash_gpios),
* .flags = IORESOURCE_IRQ,
* }
* };
* struct platform_device flash_device = {
* .name = "gpio-addr-flash",
* .dev = { .platform_data = &flash_data, },
* .num_resources = ARRAY_SIZE(flash_resource),
* .resource = flash_resource,
* ...
* };
*/
static int __devinit gpio_flash_probe(struct platform_device *pdev)
{
int ret;
size_t i, arr_size;
struct physmap_flash_data *pdata;
struct resource *memory;
struct resource *gpios;
struct async_state *state;
pdata = pdev->dev.platform_data;
memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
gpios = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!memory || !gpios || !gpios->end)
return -EINVAL;
arr_size = sizeof(int) * gpios->end;
state = kzalloc(sizeof(*state) + arr_size, GFP_KERNEL);
if (!state)
return -ENOMEM;
state->gpio_count = gpios->end;
state->gpio_addrs = (void *)gpios->start;
state->gpio_values = (void *)(state + 1);
state->win_size = memory->end - memory->start + 1;
memset(state->gpio_values, 0xff, arr_size);
state->map.name = DRIVER_NAME;
state->map.read = gf_read;
state->map.copy_from = gf_copy_from;
state->map.write = gf_write;
state->map.copy_to = gf_copy_to;
state->map.bankwidth = pdata->width;
state->map.size = state->win_size * (1 << state->gpio_count);
state->map.virt = (void __iomem *)memory->start;
state->map.phys = NO_XIP;
state->map.map_priv_1 = (unsigned long)state;
platform_set_drvdata(pdev, state);
i = 0;
do {
if (gpio_request(state->gpio_addrs[i], DRIVER_NAME)) {
pr_devinit(KERN_ERR PFX "failed to request gpio %d\n",
state->gpio_addrs[i]);
while (i--)
gpio_free(state->gpio_addrs[i]);
kfree(state);
return -EBUSY;
}
gpio_direction_output(state->gpio_addrs[i], 0);
} while (++i < state->gpio_count);
pr_devinit(KERN_NOTICE PFX "probing %d-bit flash bus\n",
state->map.bankwidth * 8);
state->mtd = do_map_probe(memory->name, &state->map);
if (!state->mtd) {
for (i = 0; i < state->gpio_count; ++i)
gpio_free(state->gpio_addrs[i]);
kfree(state);
return -ENXIO;
}
#ifdef CONFIG_MTD_PARTITIONS
ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0);
if (ret > 0) {
pr_devinit(KERN_NOTICE PFX "Using commandline partition definition\n");
add_mtd_partitions(state->mtd, pdata->parts, ret);
kfree(pdata->parts);
} else if (pdata->nr_parts) {
pr_devinit(KERN_NOTICE PFX "Using board partition definition\n");
add_mtd_partitions(state->mtd, pdata->parts, pdata->nr_parts);
} else
#endif
{
pr_devinit(KERN_NOTICE PFX "no partition info available, registering whole flash at once\n");
add_mtd_device(state->mtd);
}
return 0;
}
static int __devexit gpio_flash_remove(struct platform_device *pdev)
{
struct async_state *state = platform_get_drvdata(pdev);
size_t i = 0;
do {
gpio_free(state->gpio_addrs[i]);
} while (++i < state->gpio_count);
#ifdef CONFIG_MTD_PARTITIONS
del_mtd_partitions(state->mtd);
#endif
map_destroy(state->mtd);
kfree(state);
return 0;
}
static struct platform_driver gpio_flash_driver = {
.probe = gpio_flash_probe,
.remove = __devexit_p(gpio_flash_remove),
.driver = {
.name = DRIVER_NAME,
},
};
static int __init gpio_flash_init(void)
{
return platform_driver_register(&gpio_flash_driver);
}
module_init(gpio_flash_init);
static void __exit gpio_flash_exit(void)
{
platform_driver_unregister(&gpio_flash_driver);
}
module_exit(gpio_flash_exit);
MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
MODULE_DESCRIPTION("MTD map driver for flashes addressed physically and with gpios");
MODULE_LICENSE("GPL");

View file

@ -190,6 +190,7 @@ static int __devinit of_flash_probe(struct of_device *dev,
const u32 *p; const u32 *p;
int reg_tuple_size; int reg_tuple_size;
struct mtd_info **mtd_list = NULL; struct mtd_info **mtd_list = NULL;
resource_size_t res_size;
reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32); reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
@ -204,7 +205,7 @@ static int __devinit of_flash_probe(struct of_device *dev,
dev_err(&dev->dev, "Malformed reg property on %s\n", dev_err(&dev->dev, "Malformed reg property on %s\n",
dev->node->full_name); dev->node->full_name);
err = -EINVAL; err = -EINVAL;
goto err_out; goto err_flash_remove;
} }
count /= reg_tuple_size; count /= reg_tuple_size;
@ -212,14 +213,14 @@ static int __devinit of_flash_probe(struct of_device *dev,
info = kzalloc(sizeof(struct of_flash) + info = kzalloc(sizeof(struct of_flash) +
sizeof(struct of_flash_list) * count, GFP_KERNEL); sizeof(struct of_flash_list) * count, GFP_KERNEL);
if (!info) if (!info)
goto err_out; goto err_flash_remove;
mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL);
if (!info)
goto err_out;
dev_set_drvdata(&dev->dev, info); dev_set_drvdata(&dev->dev, info);
mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL);
if (!mtd_list)
goto err_flash_remove;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
err = -ENXIO; err = -ENXIO;
if (of_address_to_resource(dp, i, &res)) { if (of_address_to_resource(dp, i, &res)) {
@ -233,8 +234,8 @@ static int __devinit of_flash_probe(struct of_device *dev,
(unsigned long long)res.end); (unsigned long long)res.end);
err = -EBUSY; err = -EBUSY;
info->list[i].res = request_mem_region(res.start, res.end - res_size = resource_size(&res);
res.start + 1, info->list[i].res = request_mem_region(res.start, res_size,
dev_name(&dev->dev)); dev_name(&dev->dev));
if (!info->list[i].res) if (!info->list[i].res)
goto err_out; goto err_out;
@ -249,7 +250,7 @@ static int __devinit of_flash_probe(struct of_device *dev,
info->list[i].map.name = dev_name(&dev->dev); info->list[i].map.name = dev_name(&dev->dev);
info->list[i].map.phys = res.start; info->list[i].map.phys = res.start;
info->list[i].map.size = res.end - res.start + 1; info->list[i].map.size = res_size;
info->list[i].map.bankwidth = *width; info->list[i].map.bankwidth = *width;
err = -ENOMEM; err = -ENOMEM;
@ -338,6 +339,7 @@ static int __devinit of_flash_probe(struct of_device *dev,
err_out: err_out:
kfree(mtd_list); kfree(mtd_list);
err_flash_remove:
of_flash_remove(dev); of_flash_remove(dev);
return err; return err;
@ -359,6 +361,10 @@ static struct of_device_id of_flash_match[] = {
.compatible = "jedec-flash", .compatible = "jedec-flash",
.data = (void *)"jedec_probe", .data = (void *)"jedec_probe",
}, },
{
.compatible = "mtd-ram",
.data = (void *)"map_ram",
},
{ {
.type = "rom", .type = "rom",
.compatible = "direct-mapped" .compatible = "direct-mapped"

View file

@ -175,7 +175,7 @@ static int platram_probe(struct platform_device *pdev)
/* setup map parameters */ /* setup map parameters */
info->map.phys = res->start; info->map.phys = res->start;
info->map.size = (res->end - res->start) + 1; info->map.size = resource_size(res);
info->map.name = pdata->mapname != NULL ? info->map.name = pdata->mapname != NULL ?
(char *)pdata->mapname : (char *)pdev->name; (char *)pdata->mapname : (char *)pdev->name;
info->map.bankwidth = pdata->bankwidth; info->map.bankwidth = pdata->bankwidth;

View file

@ -50,7 +50,7 @@ static int fcnt;
static int __init init_msp_flash(void) static int __init init_msp_flash(void)
{ {
int i, j; int i, j, ret = -ENOMEM;
int offset, coff; int offset, coff;
char *env; char *env;
int pcnt; int pcnt;
@ -75,14 +75,16 @@ static int __init init_msp_flash(void)
printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt); printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt);
msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL); msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL);
msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL); if (!msp_flash)
msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL);
if (!msp_flash || !msp_parts || !msp_maps) {
kfree(msp_maps);
kfree(msp_parts);
kfree(msp_flash);
return -ENOMEM; return -ENOMEM;
}
msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
if (!msp_parts)
goto free_msp_flash;
msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL);
if (!msp_maps)
goto free_msp_parts;
/* loop over the flash devices, initializing each */ /* loop over the flash devices, initializing each */
for (i = 0; i < fcnt; i++) { for (i = 0; i < fcnt; i++) {
@ -100,13 +102,18 @@ static int __init init_msp_flash(void)
msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition), msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition),
GFP_KERNEL); GFP_KERNEL);
if (!msp_parts[i])
goto cleanup_loop;
/* now initialize the devices proper */ /* now initialize the devices proper */
flash_name[5] = '0' + i; flash_name[5] = '0' + i;
env = prom_getenv(flash_name); env = prom_getenv(flash_name);
if (sscanf(env, "%x:%x", &addr, &size) < 2) if (sscanf(env, "%x:%x", &addr, &size) < 2) {
return -ENXIO; ret = -ENXIO;
kfree(msp_parts[i]);
goto cleanup_loop;
}
addr = CPHYSADDR(addr); addr = CPHYSADDR(addr);
printk(KERN_NOTICE printk(KERN_NOTICE
@ -122,13 +129,23 @@ static int __init init_msp_flash(void)
*/ */
if (size > CONFIG_MSP_FLASH_MAP_LIMIT) if (size > CONFIG_MSP_FLASH_MAP_LIMIT)
size = CONFIG_MSP_FLASH_MAP_LIMIT; size = CONFIG_MSP_FLASH_MAP_LIMIT;
msp_maps[i].virt = ioremap(addr, size);
msp_maps[i].bankwidth = 1;
msp_maps[i].name = strncpy(kmalloc(7, GFP_KERNEL),
flash_name, 7);
if (msp_maps[i].virt == NULL) msp_maps[i].virt = ioremap(addr, size);
return -ENXIO; if (msp_maps[i].virt == NULL) {
ret = -ENXIO;
kfree(msp_parts[i]);
goto cleanup_loop;
}
msp_maps[i].bankwidth = 1;
msp_maps[i].name = kmalloc(7, GFP_KERNEL);
if (!msp_maps[i].name) {
iounmap(msp_maps[i].virt);
kfree(msp_parts[i]);
goto cleanup_loop;
}
msp_maps[i].name = strncpy(msp_maps[i].name, flash_name, 7);
for (j = 0; j < pcnt; j++) { for (j = 0; j < pcnt; j++) {
part_name[5] = '0' + i; part_name[5] = '0' + i;
@ -136,8 +153,14 @@ static int __init init_msp_flash(void)
env = prom_getenv(part_name); env = prom_getenv(part_name);
if (sscanf(env, "%x:%x:%n", &offset, &size, &coff) < 2) if (sscanf(env, "%x:%x:%n", &offset, &size,
return -ENXIO; &coff) < 2) {
ret = -ENXIO;
kfree(msp_maps[i].name);
iounmap(msp_maps[i].virt);
kfree(msp_parts[i]);
goto cleanup_loop;
}
msp_parts[i][j].size = size; msp_parts[i][j].size = size;
msp_parts[i][j].offset = offset; msp_parts[i][j].offset = offset;
@ -152,18 +175,37 @@ static int __init init_msp_flash(void)
add_mtd_partitions(msp_flash[i], msp_parts[i], pcnt); add_mtd_partitions(msp_flash[i], msp_parts[i], pcnt);
} else { } else {
printk(KERN_ERR "map probe failed for flash\n"); printk(KERN_ERR "map probe failed for flash\n");
return -ENXIO; ret = -ENXIO;
kfree(msp_maps[i].name);
iounmap(msp_maps[i].virt);
kfree(msp_parts[i]);
goto cleanup_loop;
} }
} }
return 0; return 0;
cleanup_loop:
while (i--) {
del_mtd_partitions(msp_flash[i]);
map_destroy(msp_flash[i]);
kfree(msp_maps[i].name);
iounmap(msp_maps[i].virt);
kfree(msp_parts[i]);
}
kfree(msp_maps);
free_msp_parts:
kfree(msp_parts);
free_msp_flash:
kfree(msp_flash);
return ret;
} }
static void __exit cleanup_msp_flash(void) static void __exit cleanup_msp_flash(void)
{ {
int i; int i;
for (i = 0; i < sizeof(msp_flash) / sizeof(struct mtd_info **); i++) { for (i = 0; i < fcnt; i++) {
del_mtd_partitions(msp_flash[i]); del_mtd_partitions(msp_flash[i]);
map_destroy(msp_flash[i]); map_destroy(msp_flash[i]);
iounmap((void *)msp_maps[i].virt); iounmap((void *)msp_maps[i].virt);

View file

@ -89,7 +89,11 @@ static int __init uclinux_mtd_init(void)
mtd->priv = mapp; mtd->priv = mapp;
uclinux_ram_mtdinfo = mtd; uclinux_ram_mtdinfo = mtd;
#ifdef CONFIG_MTD_PARTITIONS
add_mtd_partitions(mtd, uclinux_romfs, NUM_PARTITIONS); add_mtd_partitions(mtd, uclinux_romfs, NUM_PARTITIONS);
#else
add_mtd_device(mtd);
#endif
return(0); return(0);
} }
@ -99,7 +103,11 @@ static int __init uclinux_mtd_init(void)
static void __exit uclinux_mtd_cleanup(void) static void __exit uclinux_mtd_cleanup(void)
{ {
if (uclinux_ram_mtdinfo) { if (uclinux_ram_mtdinfo) {
#ifdef CONFIG_MTD_PARTITIONS
del_mtd_partitions(uclinux_ram_mtdinfo); del_mtd_partitions(uclinux_ram_mtdinfo);
#else
del_mtd_device(uclinux_ram_mtdinfo);
#endif
map_destroy(uclinux_ram_mtdinfo); map_destroy(uclinux_ram_mtdinfo);
uclinux_ram_mtdinfo = NULL; uclinux_ram_mtdinfo = NULL;
} }

View file

@ -84,7 +84,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos,
remove_wait_queue(&wait_q, &wait); remove_wait_queue(&wait_q, &wait);
/* /*
* Next, writhe data to flash. * Next, write the data to flash.
*/ */
ret = mtd->write(mtd, pos, len, &retlen, buf); ret = mtd->write(mtd, pos, len, &retlen, buf);

View file

@ -427,7 +427,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
* to-be-erased area begins. Verify that the starting * to-be-erased area begins. Verify that the starting
* offset is aligned to this region's erase size: * offset is aligned to this region's erase size:
*/ */
if (instr->addr & (erase_regions[i].erasesize - 1)) if (i < 0 || instr->addr & (erase_regions[i].erasesize - 1))
return -EINVAL; return -EINVAL;
/* /*
@ -440,8 +440,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
/* /*
* check if the ending offset is aligned to this region's erase size * check if the ending offset is aligned to this region's erase size
*/ */
if ((instr->addr + instr->len) & (erase_regions[i].erasesize - if (i < 0 || ((instr->addr + instr->len) &
1)) (erase_regions[i].erasesize - 1)))
return -EINVAL; return -EINVAL;
} }

View file

@ -213,11 +213,11 @@ static struct attribute *mtd_attrs[] = {
NULL, NULL,
}; };
struct attribute_group mtd_group = { static struct attribute_group mtd_group = {
.attrs = mtd_attrs, .attrs = mtd_attrs,
}; };
const struct attribute_group *mtd_groups[] = { static const struct attribute_group *mtd_groups[] = {
&mtd_group, &mtd_group,
NULL, NULL,
}; };

View file

@ -453,6 +453,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
for (i = 0; i < max && regions[i].offset <= slave->offset; i++) for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
; ;
/* The loop searched for the region _behind_ the first one */ /* The loop searched for the region _behind_ the first one */
if (i > 0)
i--; i--;
/* Pick biggest erasesize */ /* Pick biggest erasesize */

View file

@ -80,6 +80,23 @@ config MTD_NAND_OMAP2
help help
Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms. Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms.
config MTD_NAND_OMAP_PREFETCH
bool "GPMC prefetch support for NAND Flash device"
depends on MTD_NAND && MTD_NAND_OMAP2
default y
help
The NAND device can be accessed for Read/Write using GPMC PREFETCH engine
to improve the performance.
config MTD_NAND_OMAP_PREFETCH_DMA
depends on MTD_NAND_OMAP_PREFETCH
bool "DMA mode"
default n
help
The GPMC PREFETCH engine can be configured eigther in MPU interrupt mode
or in DMA interrupt mode.
Say y for DMA mode or MPU mode will be used
config MTD_NAND_TS7250 config MTD_NAND_TS7250
tristate "NAND Flash device on TS-7250 board" tristate "NAND Flash device on TS-7250 board"
depends on MACH_TS72XX depends on MACH_TS72XX
@ -426,6 +443,12 @@ config MTD_NAND_MXC
This enables the driver for the NAND flash controller on the This enables the driver for the NAND flash controller on the
MXC processors. MXC processors.
config MTD_NAND_NOMADIK
tristate "ST Nomadik 8815 NAND support"
depends on ARCH_NOMADIK
help
Driver for the NAND flash controller on the Nomadik, with ECC.
config MTD_NAND_SH_FLCTL config MTD_NAND_SH_FLCTL
tristate "Support for NAND on Renesas SuperH FLCTL" tristate "Support for NAND on Renesas SuperH FLCTL"
depends on MTD_NAND && SUPERH && CPU_SUBTYPE_SH7723 depends on MTD_NAND && SUPERH && CPU_SUBTYPE_SH7723
@ -452,4 +475,11 @@ config MTD_NAND_SOCRATES
help help
Enables support for NAND Flash chips wired onto Socrates board. Enables support for NAND Flash chips wired onto Socrates board.
config MTD_NAND_W90P910
tristate "Support for NAND on w90p910 evaluation board."
depends on ARCH_W90X900 && MTD_PARTITIONS
help
This enables the driver for the NAND Flash on evaluation board based
on w90p910.
endif # MTD_NAND endif # MTD_NAND

View file

@ -40,5 +40,7 @@ obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o
obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o
nand-objs := nand_base.o nand_bbt.o nand-objs := nand_base.o nand_bbt.o

View file

@ -218,7 +218,7 @@ static int atmel_nand_calculate(struct mtd_info *mtd,
* buf: buffer to store read data * buf: buffer to store read data
*/ */
static int atmel_nand_read_page(struct mtd_info *mtd, static int atmel_nand_read_page(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf) struct nand_chip *chip, uint8_t *buf, int page)
{ {
int eccsize = chip->ecc.size; int eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes; int eccbytes = chip->ecc.bytes;

View file

@ -381,7 +381,7 @@ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
* we need a special oob layout and handling. * we need a special oob layout and handling.
*/ */
static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf) uint8_t *buf, int page)
{ {
struct cafe_priv *cafe = mtd->priv; struct cafe_priv *cafe = mtd->priv;

View file

@ -348,6 +348,12 @@ compare:
if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3])) if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3]))
return 0; return 0;
/*
* Clear any previous address calculation by doing a dummy read of an
* error address register.
*/
davinci_nand_readl(info, NAND_ERR_ADD1_OFFSET);
/* Start address calculation, and wait for it to complete. /* Start address calculation, and wait for it to complete.
* We _could_ start reading more data while this is working, * We _could_ start reading more data while this is working,
* to speed up the overall page read. * to speed up the overall page read.
@ -359,8 +365,10 @@ compare:
switch ((fsr >> 8) & 0x0f) { switch ((fsr >> 8) & 0x0f) {
case 0: /* no error, should not happen */ case 0: /* no error, should not happen */
davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET);
return 0; return 0;
case 1: /* five or more errors detected */ case 1: /* five or more errors detected */
davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET);
return -EIO; return -EIO;
case 2: /* error addresses computed */ case 2: /* error addresses computed */
case 3: case 3:
@ -500,6 +508,26 @@ static struct nand_ecclayout hwecc4_small __initconst = {
}, },
}; };
/* An ECC layout for using 4-bit ECC with large-page (2048bytes) flash,
* storing ten ECC bytes plus the manufacturer's bad block marker byte,
* and not overlapping the default BBT markers.
*/
static struct nand_ecclayout hwecc4_2048 __initconst = {
.eccbytes = 40,
.eccpos = {
/* at the end of spare sector */
24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
},
.oobfree = {
/* 2 bytes at offset 0 hold manufacturer badblock markers */
{.offset = 2, .length = 22, },
/* 5 bytes at offset 8 hold BBT markers */
/* 8 bytes at offset 16 hold JFFS2 clean markers */
},
};
static int __init nand_davinci_probe(struct platform_device *pdev) static int __init nand_davinci_probe(struct platform_device *pdev)
{ {
@ -690,15 +718,20 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
info->mtd.oobsize - 16; info->mtd.oobsize - 16;
goto syndrome_done; goto syndrome_done;
} }
if (chunks == 4) {
info->ecclayout = hwecc4_2048;
info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
goto syndrome_done;
}
/* For large page chips we'll be wanting to use a /* 4KiB page chips are not yet supported. The eccpos from
* not-yet-implemented mode that reads OOB data * nand_ecclayout cannot hold 80 bytes and change to eccpos[]
* before reading the body of the page, to avoid * breaks userspace ioctl interface with mtd-utils. Once we
* the "infix OOB" model of NAND_ECC_HW_SYNDROME * resolve this issue, NAND_ECC_HW_OOB_FIRST mode can be used
* (and preserve manufacturer badblock markings). * for the 4KiB page chips.
*/ */
dev_warn(&pdev->dev, "no 4-bit ECC support yet " dev_warn(&pdev->dev, "no 4-bit ECC support yet "
"for large page NAND\n"); "for 4KiB-page NAND\n");
ret = -EIO; ret = -EIO;
goto err_scan; goto err_scan;

View file

@ -739,7 +739,8 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
static int fsl_elbc_read_page(struct mtd_info *mtd, static int fsl_elbc_read_page(struct mtd_info *mtd,
struct nand_chip *chip, struct nand_chip *chip,
uint8_t *buf) uint8_t *buf,
int page)
{ {
fsl_elbc_read_buf(mtd, buf, mtd->writesize); fsl_elbc_read_buf(mtd, buf, mtd->writesize);
fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize);

View file

@ -857,6 +857,17 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
} }
} }
/* Define some generic bad / good block scan pattern which are used
* while scanning a device for factory marked good / bad blocks. */
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
static struct nand_bbt_descr smallpage_memorybased = {
.options = NAND_BBT_SCAN2NDPAGE,
.offs = 5,
.len = 1,
.pattern = scan_ff_pattern
};
static int __init mxcnd_probe(struct platform_device *pdev) static int __init mxcnd_probe(struct platform_device *pdev)
{ {
struct nand_chip *this; struct nand_chip *this;
@ -973,7 +984,10 @@ static int __init mxcnd_probe(struct platform_device *pdev)
goto escan; goto escan;
} }
host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0; if (mtd->writesize == 2048) {
host->pagesize_2k = 1;
this->badblock_pattern = &smallpage_memorybased;
}
if (this->ecc.mode == NAND_ECC_HW) { if (this->ecc.mode == NAND_ECC_HW) {
switch (mtd->oobsize) { switch (mtd->oobsize) {

View file

@ -688,8 +688,7 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
retry: retry:
spin_lock(lock); spin_lock(lock);
/* Hardware controller shared among independend devices */ /* Hardware controller shared among independent devices */
/* Hardware controller shared among independend devices */
if (!chip->controller->active) if (!chip->controller->active)
chip->controller->active = chip; chip->controller->active = chip;
@ -766,7 +765,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
* Not for syndrome calculating ecc controllers, which use a special oob layout * Not for syndrome calculating ecc controllers, which use a special oob layout
*/ */
static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf) uint8_t *buf, int page)
{ {
chip->read_buf(mtd, buf, mtd->writesize); chip->read_buf(mtd, buf, mtd->writesize);
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@ -782,7 +781,7 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
* We need a special oob layout and handling even when OOB isn't used. * We need a special oob layout and handling even when OOB isn't used.
*/ */
static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf) uint8_t *buf, int page)
{ {
int eccsize = chip->ecc.size; int eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes; int eccbytes = chip->ecc.bytes;
@ -821,7 +820,7 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *c
* @buf: buffer to store read data * @buf: buffer to store read data
*/ */
static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf) uint8_t *buf, int page)
{ {
int i, eccsize = chip->ecc.size; int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes; int eccbytes = chip->ecc.bytes;
@ -831,7 +830,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *ecc_code = chip->buffers->ecccode; uint8_t *ecc_code = chip->buffers->ecccode;
uint32_t *eccpos = chip->ecc.layout->eccpos; uint32_t *eccpos = chip->ecc.layout->eccpos;
chip->ecc.read_page_raw(mtd, chip, buf); chip->ecc.read_page_raw(mtd, chip, buf, page);
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(mtd, p, &ecc_calc[i]); chip->ecc.calculate(mtd, p, &ecc_calc[i]);
@ -944,7 +943,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3
* Not for syndrome calculating ecc controllers which need a special oob layout * Not for syndrome calculating ecc controllers which need a special oob layout
*/ */
static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf) uint8_t *buf, int page)
{ {
int i, eccsize = chip->ecc.size; int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes; int eccbytes = chip->ecc.bytes;
@ -979,6 +978,54 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
return 0; return 0;
} }
/**
* nand_read_page_hwecc_oob_first - [REPLACABLE] hw ecc, read oob first
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
*
* Hardware ECC for large page chips, require OOB to be read first.
* For this ECC mode, the write_page method is re-used from ECC_HW.
* These methods read/write ECC from the OOB area, unlike the
* ECC_HW_SYNDROME support with multiple ECC steps, follows the
* "infix ECC" scheme and reads/writes ECC from the data area, by
* overwriting the NAND manufacturer bad block markings.
*/
static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf, int page)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
uint8_t *ecc_code = chip->buffers->ecccode;
uint32_t *eccpos = chip->ecc.layout->eccpos;
uint8_t *ecc_calc = chip->buffers->ecccalc;
/* Read the OOB area first */
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
int stat;
chip->ecc.hwctl(mtd, NAND_ECC_READ);
chip->read_buf(mtd, p, eccsize);
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
if (stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
}
return 0;
}
/** /**
* nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read
* @mtd: mtd info structure * @mtd: mtd info structure
@ -989,7 +1036,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
* we need a special oob layout and handling. * we need a special oob layout and handling.
*/ */
static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf) uint8_t *buf, int page)
{ {
int i, eccsize = chip->ecc.size; int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes; int eccbytes = chip->ecc.bytes;
@ -1131,11 +1178,13 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
/* Now read the page into the buffer */ /* Now read the page into the buffer */
if (unlikely(ops->mode == MTD_OOB_RAW)) if (unlikely(ops->mode == MTD_OOB_RAW))
ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); ret = chip->ecc.read_page_raw(mtd, chip,
bufpoi, page);
else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);
else else
ret = chip->ecc.read_page(mtd, chip, bufpoi); ret = chip->ecc.read_page(mtd, chip, bufpoi,
page);
if (ret < 0) if (ret < 0)
break; break;
@ -1413,8 +1462,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
int len; int len;
uint8_t *buf = ops->oobbuf; uint8_t *buf = ops->oobbuf;
DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n",
(unsigned long long)from, readlen); __func__, (unsigned long long)from, readlen);
if (ops->mode == MTD_OOB_AUTO) if (ops->mode == MTD_OOB_AUTO)
len = chip->ecc.layout->oobavail; len = chip->ecc.layout->oobavail;
@ -1422,8 +1471,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
len = mtd->oobsize; len = mtd->oobsize;
if (unlikely(ops->ooboffs >= len)) { if (unlikely(ops->ooboffs >= len)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start read "
"Attempt to start read outside oob\n"); "outside oob\n", __func__);
return -EINVAL; return -EINVAL;
} }
@ -1431,8 +1480,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
if (unlikely(from >= mtd->size || if (unlikely(from >= mtd->size ||
ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -
(from >> chip->page_shift)) * len)) { (from >> chip->page_shift)) * len)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end "
"Attempt read beyond end of device\n"); "of device\n", __func__);
return -EINVAL; return -EINVAL;
} }
@ -1506,8 +1555,8 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
/* Do not allow reads past end of device */ /* Do not allow reads past end of device */
if (ops->datbuf && (from + ops->len) > mtd->size) { if (ops->datbuf && (from + ops->len) > mtd->size) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read "
"Attempt read beyond end of device\n"); "beyond end of device\n", __func__);
return -EINVAL; return -EINVAL;
} }
@ -1816,8 +1865,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
/* reject writes, which are not page aligned */ /* reject writes, which are not page aligned */
if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
printk(KERN_NOTICE "nand_write: " printk(KERN_NOTICE "%s: Attempt to write not "
"Attempt to write not page aligned data\n"); "page aligned data\n", __func__);
return -EINVAL; return -EINVAL;
} }
@ -1944,8 +1993,8 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
int chipnr, page, status, len; int chipnr, page, status, len;
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n",
(unsigned int)to, (int)ops->ooblen); __func__, (unsigned int)to, (int)ops->ooblen);
if (ops->mode == MTD_OOB_AUTO) if (ops->mode == MTD_OOB_AUTO)
len = chip->ecc.layout->oobavail; len = chip->ecc.layout->oobavail;
@ -1954,14 +2003,14 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
/* Do not allow write past end of page */ /* Do not allow write past end of page */
if ((ops->ooboffs + ops->ooblen) > len) { if ((ops->ooboffs + ops->ooblen) > len) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to write "
"Attempt to write past end of page\n"); "past end of page\n", __func__);
return -EINVAL; return -EINVAL;
} }
if (unlikely(ops->ooboffs >= len)) { if (unlikely(ops->ooboffs >= len)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: " DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start "
"Attempt to start write outside oob\n"); "write outside oob\n", __func__);
return -EINVAL; return -EINVAL;
} }
@ -1970,8 +2019,8 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
ops->ooboffs + ops->ooblen > ops->ooboffs + ops->ooblen >
((mtd->size >> chip->page_shift) - ((mtd->size >> chip->page_shift) -
(to >> chip->page_shift)) * len)) { (to >> chip->page_shift)) * len)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: " DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond "
"Attempt write beyond end of device\n"); "end of device\n", __func__);
return -EINVAL; return -EINVAL;
} }
@ -2026,8 +2075,8 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
/* Do not allow writes past end of device */ /* Do not allow writes past end of device */
if (ops->datbuf && (to + ops->len) > mtd->size) { if (ops->datbuf && (to + ops->len) > mtd->size) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond "
"Attempt write beyond end of device\n"); "end of device\n", __func__);
return -EINVAL; return -EINVAL;
} }
@ -2117,26 +2166,27 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
unsigned int bbt_masked_page = 0xffffffff; unsigned int bbt_masked_page = 0xffffffff;
loff_t len; loff_t len;
DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%012llx, len = %llu\n", DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n",
(unsigned long long)instr->addr, (unsigned long long)instr->len); __func__, (unsigned long long)instr->addr,
(unsigned long long)instr->len);
/* Start address must align on block boundary */ /* Start address must align on block boundary */
if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__);
return -EINVAL; return -EINVAL;
} }
/* Length must align on block boundary */ /* Length must align on block boundary */
if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { if (instr->len & ((1 << chip->phys_erase_shift) - 1)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n",
"Length not block aligned\n"); __func__);
return -EINVAL; return -EINVAL;
} }
/* Do not allow erase past end of device */ /* Do not allow erase past end of device */
if ((instr->len + instr->addr) > mtd->size) { if ((instr->len + instr->addr) > mtd->size) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " DEBUG(MTD_DEBUG_LEVEL0, "%s: Erase past end of device\n",
"Erase past end of device\n"); __func__);
return -EINVAL; return -EINVAL;
} }
@ -2157,8 +2207,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
/* Check, if it is write protected */ /* Check, if it is write protected */
if (nand_check_wp(mtd)) { if (nand_check_wp(mtd)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n",
"Device is write protected!!!\n"); __func__);
instr->state = MTD_ERASE_FAILED; instr->state = MTD_ERASE_FAILED;
goto erase_exit; goto erase_exit;
} }
@ -2183,8 +2233,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
*/ */
if (nand_block_checkbad(mtd, ((loff_t) page) << if (nand_block_checkbad(mtd, ((loff_t) page) <<
chip->page_shift, 0, allowbbt)) { chip->page_shift, 0, allowbbt)) {
printk(KERN_WARNING "nand_erase: attempt to erase a " printk(KERN_WARNING "%s: attempt to erase a bad block "
"bad block at page 0x%08x\n", page); "at page 0x%08x\n", __func__, page);
instr->state = MTD_ERASE_FAILED; instr->state = MTD_ERASE_FAILED;
goto erase_exit; goto erase_exit;
} }
@ -2211,8 +2261,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
/* See if block erase succeeded */ /* See if block erase succeeded */
if (status & NAND_STATUS_FAIL) { if (status & NAND_STATUS_FAIL) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " DEBUG(MTD_DEBUG_LEVEL0, "%s: Failed erase, "
"Failed erase, page 0x%08x\n", page); "page 0x%08x\n", __func__, page);
instr->state = MTD_ERASE_FAILED; instr->state = MTD_ERASE_FAILED;
instr->fail_addr = instr->fail_addr =
((loff_t)page << chip->page_shift); ((loff_t)page << chip->page_shift);
@ -2272,9 +2322,9 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
if (!rewrite_bbt[chipnr]) if (!rewrite_bbt[chipnr])
continue; continue;
/* update the BBT for chip */ /* update the BBT for chip */
DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " DEBUG(MTD_DEBUG_LEVEL0, "%s: nand_update_bbt "
"(%d:0x%0llx 0x%0x)\n", chipnr, rewrite_bbt[chipnr], "(%d:0x%0llx 0x%0x)\n", __func__, chipnr,
chip->bbt_td->pages[chipnr]); rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]);
nand_update_bbt(mtd, rewrite_bbt[chipnr]); nand_update_bbt(mtd, rewrite_bbt[chipnr]);
} }
@ -2292,7 +2342,7 @@ static void nand_sync(struct mtd_info *mtd)
{ {
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
DEBUG(MTD_DEBUG_LEVEL3, "nand_sync: called\n"); DEBUG(MTD_DEBUG_LEVEL3, "%s: called\n", __func__);
/* Grab the lock and see if the device is available */ /* Grab the lock and see if the device is available */
nand_get_device(chip, mtd, FL_SYNCING); nand_get_device(chip, mtd, FL_SYNCING);
@ -2356,8 +2406,8 @@ static void nand_resume(struct mtd_info *mtd)
if (chip->state == FL_PM_SUSPENDED) if (chip->state == FL_PM_SUSPENDED)
nand_release_device(mtd); nand_release_device(mtd);
else else
printk(KERN_ERR "nand_resume() called for a chip which is not " printk(KERN_ERR "%s called for a chip which is not "
"in suspended state\n"); "in suspended state\n", __func__);
} }
/* /*
@ -2671,6 +2721,17 @@ int nand_scan_tail(struct mtd_info *mtd)
*/ */
switch (chip->ecc.mode) { switch (chip->ecc.mode) {
case NAND_ECC_HW_OOB_FIRST:
/* Similar to NAND_ECC_HW, but a separate read_page handle */
if (!chip->ecc.calculate || !chip->ecc.correct ||
!chip->ecc.hwctl) {
printk(KERN_WARNING "No ECC functions supplied; "
"Hardware ECC not possible\n");
BUG();
}
if (!chip->ecc.read_page)
chip->ecc.read_page = nand_read_page_hwecc_oob_first;
case NAND_ECC_HW: case NAND_ECC_HW:
/* Use standard hwecc read page function ? */ /* Use standard hwecc read page function ? */
if (!chip->ecc.read_page) if (!chip->ecc.read_page)
@ -2693,7 +2754,7 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->ecc.read_page == nand_read_page_hwecc || chip->ecc.read_page == nand_read_page_hwecc ||
!chip->ecc.write_page || !chip->ecc.write_page ||
chip->ecc.write_page == nand_write_page_hwecc)) { chip->ecc.write_page == nand_write_page_hwecc)) {
printk(KERN_WARNING "No ECC functions supplied, " printk(KERN_WARNING "No ECC functions supplied; "
"Hardware ECC not possible\n"); "Hardware ECC not possible\n");
BUG(); BUG();
} }
@ -2728,6 +2789,7 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->ecc.write_page_raw = nand_write_page_raw; chip->ecc.write_page_raw = nand_write_page_raw;
chip->ecc.read_oob = nand_read_oob_std; chip->ecc.read_oob = nand_read_oob_std;
chip->ecc.write_oob = nand_write_oob_std; chip->ecc.write_oob = nand_write_oob_std;
if (!chip->ecc.size)
chip->ecc.size = 256; chip->ecc.size = 256;
chip->ecc.bytes = 3; chip->ecc.bytes = 3;
break; break;
@ -2858,7 +2920,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
/* Many callers got this wrong, so check for it for a while... */ /* Many callers got this wrong, so check for it for a while... */
if (!mtd->owner && caller_is_module()) { if (!mtd->owner && caller_is_module()) {
printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n"); printk(KERN_CRIT "%s called with NULL mtd->owner!\n",
__func__);
BUG(); BUG();
} }

View file

@ -417,22 +417,22 @@ int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
EXPORT_SYMBOL(nand_calculate_ecc); EXPORT_SYMBOL(nand_calculate_ecc);
/** /**
* nand_correct_data - [NAND Interface] Detect and correct bit error(s) * __nand_correct_data - [NAND Interface] Detect and correct bit error(s)
* @mtd: MTD block structure
* @buf: raw data read from the chip * @buf: raw data read from the chip
* @read_ecc: ECC from the chip * @read_ecc: ECC from the chip
* @calc_ecc: the ECC calculated from raw data * @calc_ecc: the ECC calculated from raw data
* @eccsize: data bytes per ecc step (256 or 512)
* *
* Detect and correct a 1 bit error for 256/512 byte block * Detect and correct a 1 bit error for eccsize byte block
*/ */
int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, int __nand_correct_data(unsigned char *buf,
unsigned char *read_ecc, unsigned char *calc_ecc) unsigned char *read_ecc, unsigned char *calc_ecc,
unsigned int eccsize)
{ {
unsigned char b0, b1, b2, bit_addr; unsigned char b0, b1, b2, bit_addr;
unsigned int byte_addr; unsigned int byte_addr;
/* 256 or 512 bytes/ecc */ /* 256 or 512 bytes/ecc */
const uint32_t eccsize_mult = const uint32_t eccsize_mult = eccsize >> 8;
(((struct nand_chip *)mtd->priv)->ecc.size) >> 8;
/* /*
* b0 to b2 indicate which bit is faulty (if any) * b0 to b2 indicate which bit is faulty (if any)
@ -495,6 +495,23 @@ int nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
printk(KERN_ERR "uncorrectable error : "); printk(KERN_ERR "uncorrectable error : ");
return -1; return -1;
} }
EXPORT_SYMBOL(__nand_correct_data);
/**
* nand_correct_data - [NAND Interface] Detect and correct bit error(s)
* @mtd: MTD block structure
* @buf: raw data read from the chip
* @read_ecc: ECC from the chip
* @calc_ecc: the ECC calculated from raw data
*
* Detect and correct a 1 bit error for 256/512 byte block
*/
int nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
unsigned char *read_ecc, unsigned char *calc_ecc)
{
return __nand_correct_data(buf, read_ecc, calc_ecc,
((struct nand_chip *)mtd->priv)->ecc.size);
}
EXPORT_SYMBOL(nand_correct_data); EXPORT_SYMBOL(nand_correct_data);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View file

@ -102,8 +102,8 @@ static int ndfc_calculate_ecc(struct mtd_info *mtd,
wmb(); wmb();
ecc = in_be32(ndfc->ndfcbase + NDFC_ECC); ecc = in_be32(ndfc->ndfcbase + NDFC_ECC);
/* The NDFC uses Smart Media (SMC) bytes order */ /* The NDFC uses Smart Media (SMC) bytes order */
ecc_code[0] = p[2]; ecc_code[0] = p[1];
ecc_code[1] = p[1]; ecc_code[1] = p[2];
ecc_code[2] = p[3]; ecc_code[2] = p[3];
return 0; return 0;

View file

@ -0,0 +1,250 @@
/*
* drivers/mtd/nand/nomadik_nand.c
*
* Overview:
* Driver for on-board NAND flash on Nomadik Platforms
*
* Copyright © 2007 STMicroelectronics Pvt. Ltd.
* Author: Sachin Verma <sachin.verma@st.com>
*
* Copyright © 2009 Alessandro Rubini
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/platform_device.h>
#include <linux/mtd/partitions.h>
#include <linux/io.h>
#include <mach/nand.h>
#include <mach/fsmc.h>
#include <mtd/mtd-abi.h>
struct nomadik_nand_host {
struct mtd_info mtd;
struct nand_chip nand;
void __iomem *data_va;
void __iomem *cmd_va;
void __iomem *addr_va;
struct nand_bbt_descr *bbt_desc;
};
static struct nand_ecclayout nomadik_ecc_layout = {
.eccbytes = 3 * 4,
.eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */
0x02, 0x03, 0x04,
0x12, 0x13, 0x14,
0x22, 0x23, 0x24,
0x32, 0x33, 0x34},
/* let's keep bytes 5,6,7 for us, just in case we change ECC algo */
.oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} },
};
static void nomadik_ecc_control(struct mtd_info *mtd, int mode)
{
/* No need to enable hw ecc, it's on by default */
}
static void nomadik_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *nand = mtd->priv;
struct nomadik_nand_host *host = nand->priv;
if (cmd == NAND_CMD_NONE)
return;
if (ctrl & NAND_CLE)
writeb(cmd, host->cmd_va);
else
writeb(cmd, host->addr_va);
}
static int nomadik_nand_probe(struct platform_device *pdev)
{
struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data;
struct nomadik_nand_host *host;
struct mtd_info *mtd;
struct nand_chip *nand;
struct resource *res;
int ret = 0;
/* Allocate memory for the device structure (and zero it) */
host = kzalloc(sizeof(struct nomadik_nand_host), GFP_KERNEL);
if (!host) {
dev_err(&pdev->dev, "Failed to allocate device structure.\n");
return -ENOMEM;
}
/* Call the client's init function, if any */
if (pdata->init)
ret = pdata->init();
if (ret < 0) {
dev_err(&pdev->dev, "Init function failed\n");
goto err;
}
/* ioremap three regions */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr");
if (!res) {
ret = -EIO;
goto err_unmap;
}
host->addr_va = ioremap(res->start, res->end - res->start + 1);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
if (!res) {
ret = -EIO;
goto err_unmap;
}
host->data_va = ioremap(res->start, res->end - res->start + 1);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd");
if (!res) {
ret = -EIO;
goto err_unmap;
}
host->cmd_va = ioremap(res->start, res->end - res->start + 1);
if (!host->addr_va || !host->data_va || !host->cmd_va) {
ret = -ENOMEM;
goto err_unmap;
}
/* Link all private pointers */
mtd = &host->mtd;
nand = &host->nand;
mtd->priv = nand;
nand->priv = host;
host->mtd.owner = THIS_MODULE;
nand->IO_ADDR_R = host->data_va;
nand->IO_ADDR_W = host->data_va;
nand->cmd_ctrl = nomadik_cmd_ctrl;
/*
* This stanza declares ECC_HW but uses soft routines. It's because
* HW claims to make the calculation but not the correction. However,
* I haven't managed to get the desired data out of it until now.
*/
nand->ecc.mode = NAND_ECC_SOFT;
nand->ecc.layout = &nomadik_ecc_layout;
nand->ecc.hwctl = nomadik_ecc_control;
nand->ecc.size = 512;
nand->ecc.bytes = 3;
nand->options = pdata->options;
/*
* Scan to find existance of the device
*/
if (nand_scan(&host->mtd, 1)) {
ret = -ENXIO;
goto err_unmap;
}
#ifdef CONFIG_MTD_PARTITIONS
add_mtd_partitions(&host->mtd, pdata->parts, pdata->nparts);
#else
pr_info("Registering %s as whole device\n", mtd->name);
add_mtd_device(mtd);
#endif
platform_set_drvdata(pdev, host);
return 0;
err_unmap:
if (host->cmd_va)
iounmap(host->cmd_va);
if (host->data_va)
iounmap(host->data_va);
if (host->addr_va)
iounmap(host->addr_va);
err:
kfree(host);
return ret;
}
/*
* Clean up routine
*/
static int nomadik_nand_remove(struct platform_device *pdev)
{
struct nomadik_nand_host *host = platform_get_drvdata(pdev);
struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data;
if (pdata->exit)
pdata->exit();
if (host) {
iounmap(host->cmd_va);
iounmap(host->data_va);
iounmap(host->addr_va);
kfree(host);
}
return 0;
}
static int nomadik_nand_suspend(struct device *dev)
{
struct nomadik_nand_host *host = dev_get_drvdata(dev);
int ret = 0;
if (host)
ret = host->mtd.suspend(&host->mtd);
return ret;
}
static int nomadik_nand_resume(struct device *dev)
{
struct nomadik_nand_host *host = dev_get_drvdata(dev);
if (host)
host->mtd.resume(&host->mtd);
return 0;
}
static struct dev_pm_ops nomadik_nand_pm_ops = {
.suspend = nomadik_nand_suspend,
.resume = nomadik_nand_resume,
};
static struct platform_driver nomadik_nand_driver = {
.probe = nomadik_nand_probe,
.remove = nomadik_nand_remove,
.driver = {
.owner = THIS_MODULE,
.name = "nomadik_nand",
.pm = &nomadik_nand_pm_ops,
},
};
static int __init nand_nomadik_init(void)
{
pr_info("Nomadik NAND driver\n");
return platform_driver_register(&nomadik_nand_driver);
}
static void __exit nand_nomadik_exit(void)
{
platform_driver_unregister(&nomadik_nand_driver);
}
module_init(nand_nomadik_init);
module_exit(nand_nomadik_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ST Microelectronics (sachin.verma@st.com)");
MODULE_DESCRIPTION("NAND driver for Nomadik Platform");

View file

@ -18,8 +18,7 @@
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/io.h> #include <linux/io.h>
#include <asm/dma.h> #include <mach/dma.h>
#include <mach/gpmc.h> #include <mach/gpmc.h>
#include <mach/nand.h> #include <mach/nand.h>
@ -112,6 +111,27 @@
static const char *part_probes[] = { "cmdlinepart", NULL }; static const char *part_probes[] = { "cmdlinepart", NULL };
#endif #endif
#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH
static int use_prefetch = 1;
/* "modprobe ... use_prefetch=0" etc */
module_param(use_prefetch, bool, 0);
MODULE_PARM_DESC(use_prefetch, "enable/disable use of PREFETCH");
#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA
static int use_dma = 1;
/* "modprobe ... use_dma=0" etc */
module_param(use_dma, bool, 0);
MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
#else
const int use_dma;
#endif
#else
const int use_prefetch;
const int use_dma;
#endif
struct omap_nand_info { struct omap_nand_info {
struct nand_hw_control controller; struct nand_hw_control controller;
struct omap_nand_platform_data *pdata; struct omap_nand_platform_data *pdata;
@ -124,6 +144,9 @@ struct omap_nand_info {
unsigned long phys_base; unsigned long phys_base;
void __iomem *gpmc_cs_baseaddr; void __iomem *gpmc_cs_baseaddr;
void __iomem *gpmc_baseaddr; void __iomem *gpmc_baseaddr;
void __iomem *nand_pref_fifo_add;
struct completion comp;
int dma_ch;
}; };
/** /**
@ -188,6 +211,38 @@ static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
__raw_writeb(cmd, info->nand.IO_ADDR_W); __raw_writeb(cmd, info->nand.IO_ADDR_W);
} }
/**
* omap_read_buf8 - 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_buf8(struct mtd_info *mtd, u_char *buf, int len)
{
struct nand_chip *nand = mtd->priv;
ioread8_rep(nand->IO_ADDR_R, buf, len);
}
/**
* omap_write_buf8 - write buffer to NAND controller
* @mtd: MTD device structure
* @buf: data buffer
* @len: number of bytes to write
*/
static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len)
{
struct omap_nand_info *info = container_of(mtd,
struct omap_nand_info, mtd);
u_char *p = (u_char *)buf;
while (len--) {
iowrite8(*p++, info->nand.IO_ADDR_W);
while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr +
GPMC_STATUS) & GPMC_BUF_FULL));
}
}
/** /**
* omap_read_buf16 - read data from NAND controller into buffer * omap_read_buf16 - read data from NAND controller into buffer
* @mtd: MTD device structure * @mtd: MTD device structure
@ -198,7 +253,7 @@ static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
{ {
struct nand_chip *nand = mtd->priv; struct nand_chip *nand = mtd->priv;
__raw_readsw(nand->IO_ADDR_R, buf, len / 2); ioread16_rep(nand->IO_ADDR_R, buf, len / 2);
} }
/** /**
@ -217,13 +272,242 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
len >>= 1; len >>= 1;
while (len--) { while (len--) {
writew(*p++, info->nand.IO_ADDR_W); iowrite16(*p++, info->nand.IO_ADDR_W);
while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr +
GPMC_STATUS) & GPMC_BUF_FULL)) GPMC_STATUS) & GPMC_BUF_FULL))
; ;
} }
} }
/**
* omap_read_buf_pref - 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_buf_pref(struct mtd_info *mtd, u_char *buf, int len)
{
struct omap_nand_info *info = container_of(mtd,
struct omap_nand_info, mtd);
uint32_t pfpw_status = 0, r_count = 0;
int ret = 0;
u32 *p = (u32 *)buf;
/* take care of subpage reads */
for (; len % 4 != 0; ) {
*buf++ = __raw_readb(info->nand.IO_ADDR_R);
len--;
}
p = (u32 *) buf;
/* configure and start prefetch transfer */
ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x0);
if (ret) {
/* PFPW engine is busy, use cpu copy method */
if (info->nand.options & NAND_BUSWIDTH_16)
omap_read_buf16(mtd, buf, len);
else
omap_read_buf8(mtd, buf, len);
} else {
do {
pfpw_status = gpmc_prefetch_status();
r_count = ((pfpw_status >> 24) & 0x7F) >> 2;
ioread32_rep(info->nand_pref_fifo_add, p, r_count);
p += r_count;
len -= r_count << 2;
} while (len);
/* disable and stop the PFPW engine */
gpmc_prefetch_reset();
}
}
/**
* omap_write_buf_pref - write buffer to NAND controller
* @mtd: MTD device structure
* @buf: data buffer
* @len: number of bytes to write
*/
static void omap_write_buf_pref(struct mtd_info *mtd,
const u_char *buf, int len)
{
struct omap_nand_info *info = container_of(mtd,
struct omap_nand_info, mtd);
uint32_t pfpw_status = 0, w_count = 0;
int i = 0, ret = 0;
u16 *p = (u16 *) buf;
/* take care of subpage writes */
if (len % 2 != 0) {
writeb(*buf, info->nand.IO_ADDR_R);
p = (u16 *)(buf + 1);
len--;
}
/* configure and start prefetch transfer */
ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x1);
if (ret) {
/* PFPW engine is busy, use cpu copy method */
if (info->nand.options & NAND_BUSWIDTH_16)
omap_write_buf16(mtd, buf, len);
else
omap_write_buf8(mtd, buf, len);
} else {
pfpw_status = gpmc_prefetch_status();
while (pfpw_status & 0x3FFF) {
w_count = ((pfpw_status >> 24) & 0x7F) >> 1;
for (i = 0; (i < w_count) && len; i++, len -= 2)
iowrite16(*p++, info->nand_pref_fifo_add);
pfpw_status = gpmc_prefetch_status();
}
/* disable and stop the PFPW engine */
gpmc_prefetch_reset();
}
}
#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA
/*
* omap_nand_dma_cb: callback on the completion of dma transfer
* @lch: logical channel
* @ch_satuts: channel status
* @data: pointer to completion data structure
*/
static void omap_nand_dma_cb(int lch, u16 ch_status, void *data)
{
complete((struct completion *) data);
}
/*
* omap_nand_dma_transfer: configer and start dma transfer
* @mtd: MTD device structure
* @addr: virtual address in RAM of source/destination
* @len: number of data bytes to be transferred
* @is_write: flag for read/write operation
*/
static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
unsigned int len, int is_write)
{
struct omap_nand_info *info = container_of(mtd,
struct omap_nand_info, mtd);
uint32_t prefetch_status = 0;
enum dma_data_direction dir = is_write ? DMA_TO_DEVICE :
DMA_FROM_DEVICE;
dma_addr_t dma_addr;
int ret;
/* The fifo depth is 64 bytes. We have a sync at each frame and frame
* length is 64 bytes.
*/
int buf_len = len >> 6;
if (addr >= high_memory) {
struct page *p1;
if (((size_t)addr & PAGE_MASK) !=
((size_t)(addr + len - 1) & PAGE_MASK))
goto out_copy;
p1 = vmalloc_to_page(addr);
if (!p1)
goto out_copy;
addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK);
}
dma_addr = dma_map_single(&info->pdev->dev, addr, len, dir);
if (dma_mapping_error(&info->pdev->dev, dma_addr)) {
dev_err(&info->pdev->dev,
"Couldn't DMA map a %d byte buffer\n", len);
goto out_copy;
}
if (is_write) {
omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
info->phys_base, 0, 0);
omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
dma_addr, 0, 0);
omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32,
0x10, buf_len, OMAP_DMA_SYNC_FRAME,
OMAP24XX_DMA_GPMC, OMAP_DMA_DST_SYNC);
} else {
omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
info->phys_base, 0, 0);
omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
dma_addr, 0, 0);
omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32,
0x10, buf_len, OMAP_DMA_SYNC_FRAME,
OMAP24XX_DMA_GPMC, OMAP_DMA_SRC_SYNC);
}
/* configure and start prefetch transfer */
ret = gpmc_prefetch_enable(info->gpmc_cs, 0x1, len, is_write);
if (ret)
/* PFPW engine is busy, use cpu copy methode */
goto out_copy;
init_completion(&info->comp);
omap_start_dma(info->dma_ch);
/* setup and start DMA using dma_addr */
wait_for_completion(&info->comp);
while (0x3fff & (prefetch_status = gpmc_prefetch_status()))
;
/* disable and stop the PFPW engine */
gpmc_prefetch_reset();
dma_unmap_single(&info->pdev->dev, dma_addr, len, dir);
return 0;
out_copy:
if (info->nand.options & NAND_BUSWIDTH_16)
is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len)
: omap_write_buf16(mtd, (u_char *) addr, len);
else
is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len)
: omap_write_buf8(mtd, (u_char *) addr, len);
return 0;
}
#else
static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) {}
static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
unsigned int len, int is_write)
{
return 0;
}
#endif
/**
* omap_read_buf_dma_pref - 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_buf_dma_pref(struct mtd_info *mtd, u_char *buf, int len)
{
if (len <= mtd->oobsize)
omap_read_buf_pref(mtd, buf, len);
else
/* start transfer in DMA mode */
omap_nand_dma_transfer(mtd, buf, len, 0x0);
}
/**
* omap_write_buf_dma_pref - write buffer to NAND controller
* @mtd: MTD device structure
* @buf: data buffer
* @len: number of bytes to write
*/
static void omap_write_buf_dma_pref(struct mtd_info *mtd,
const u_char *buf, int len)
{
if (len <= mtd->oobsize)
omap_write_buf_pref(mtd, buf, len);
else
/* start transfer in DMA mode */
omap_nand_dma_transfer(mtd, buf, len, 0x1);
}
/** /**
* omap_verify_buf - Verify chip data against buffer * omap_verify_buf - Verify chip data against buffer
* @mtd: MTD device structure * @mtd: MTD device structure
@ -658,17 +942,12 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
err = -ENOMEM; err = -ENOMEM;
goto out_release_mem_region; goto out_release_mem_region;
} }
info->nand.controller = &info->controller; info->nand.controller = &info->controller;
info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
info->nand.cmd_ctrl = omap_hwcontrol; 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 * If RDY/BSY line is connected to OMAP then use the omap ready
* funcrtion and the generic nand_wait function which reads the status * funcrtion and the generic nand_wait function which reads the status
@ -689,6 +968,40 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
== 0x1000) == 0x1000)
info->nand.options |= NAND_BUSWIDTH_16; info->nand.options |= NAND_BUSWIDTH_16;
if (use_prefetch) {
/* copy the virtual address of nand base for fifo access */
info->nand_pref_fifo_add = info->nand.IO_ADDR_R;
info->nand.read_buf = omap_read_buf_pref;
info->nand.write_buf = omap_write_buf_pref;
if (use_dma) {
err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND",
omap_nand_dma_cb, &info->comp, &info->dma_ch);
if (err < 0) {
info->dma_ch = -1;
printk(KERN_WARNING "DMA request failed."
" Non-dma data transfer mode\n");
} else {
omap_set_dma_dest_burst_mode(info->dma_ch,
OMAP_DMA_DATA_BURST_16);
omap_set_dma_src_burst_mode(info->dma_ch,
OMAP_DMA_DATA_BURST_16);
info->nand.read_buf = omap_read_buf_dma_pref;
info->nand.write_buf = omap_write_buf_dma_pref;
}
}
} else {
if (info->nand.options & NAND_BUSWIDTH_16) {
info->nand.read_buf = omap_read_buf16;
info->nand.write_buf = omap_write_buf16;
} else {
info->nand.read_buf = omap_read_buf8;
info->nand.write_buf = omap_write_buf8;
}
}
info->nand.verify_buf = omap_verify_buf;
#ifdef CONFIG_MTD_NAND_OMAP_HWECC #ifdef CONFIG_MTD_NAND_OMAP_HWECC
info->nand.ecc.bytes = 3; info->nand.ecc.bytes = 3;
info->nand.ecc.size = 512; info->nand.ecc.size = 512;
@ -744,9 +1057,12 @@ static int omap_nand_remove(struct platform_device *pdev)
struct omap_nand_info *info = mtd->priv; struct omap_nand_info *info = mtd->priv;
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
if (use_dma)
omap_free_dma(info->dma_ch);
/* Release NAND device, its internal structures and partitions */ /* Release NAND device, its internal structures and partitions */
nand_release(&info->mtd); nand_release(&info->mtd);
iounmap(info->nand.IO_ADDR_R); iounmap(info->nand_pref_fifo_add);
kfree(&info->mtd); kfree(&info->mtd);
return 0; return 0;
} }
@ -763,6 +1079,15 @@ static struct platform_driver omap_nand_driver = {
static int __init omap_nand_init(void) static int __init omap_nand_init(void)
{ {
printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME); printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME);
/* This check is required if driver is being
* loaded run time as a module
*/
if ((1 == use_dma) && (0 == use_prefetch)) {
printk(KERN_INFO"Wrong parameters: 'use_dma' can not be 1 "
"without use_prefetch'. Prefetch will not be"
" used in either mode (mpu or dma)\n");
}
return platform_driver_register(&omap_nand_driver); return platform_driver_register(&omap_nand_driver);
} }

View file

@ -171,7 +171,6 @@ static int __devexit orion_nand_remove(struct platform_device *pdev)
} }
static struct platform_driver orion_nand_driver = { static struct platform_driver orion_nand_driver = {
.probe = orion_nand_probe,
.remove = __devexit_p(orion_nand_remove), .remove = __devexit_p(orion_nand_remove),
.driver = { .driver = {
.name = "orion_nand", .name = "orion_nand",
@ -181,7 +180,7 @@ static struct platform_driver orion_nand_driver = {
static int __init orion_nand_init(void) static int __init orion_nand_init(void)
{ {
return platform_driver_register(&orion_nand_driver); return platform_driver_probe(&orion_nand_driver, orion_nand_probe);
} }
static void __exit orion_nand_exit(void) static void __exit orion_nand_exit(void)

View file

@ -102,6 +102,7 @@ enum {
ERR_SENDCMD = -2, ERR_SENDCMD = -2,
ERR_DBERR = -3, ERR_DBERR = -3,
ERR_BBERR = -4, ERR_BBERR = -4,
ERR_SBERR = -5,
}; };
enum { enum {
@ -564,11 +565,13 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
status = nand_readl(info, NDSR); status = nand_readl(info, NDSR);
if (status & (NDSR_RDDREQ | NDSR_DBERR)) { if (status & (NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR)) {
if (status & NDSR_DBERR) if (status & NDSR_DBERR)
info->retcode = ERR_DBERR; info->retcode = ERR_DBERR;
else if (status & NDSR_SBERR)
info->retcode = ERR_SBERR;
disable_int(info, NDSR_RDDREQ | NDSR_DBERR); disable_int(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR);
if (info->use_dma) { if (info->use_dma) {
info->state = STATE_DMA_READING; info->state = STATE_DMA_READING;
@ -670,7 +673,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr))
break; break;
pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR); pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR);
/* We only are OOB, so if the data has error, does not matter */ /* We only are OOB, so if the data has error, does not matter */
if (info->retcode == ERR_DBERR) if (info->retcode == ERR_DBERR)
@ -687,7 +690,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr))
break; break;
pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR); pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR);
if (info->retcode == ERR_DBERR) { if (info->retcode == ERR_DBERR) {
/* for blank page (all 0xff), HW will calculate its ECC as /* for blank page (all 0xff), HW will calculate its ECC as
@ -861,8 +864,12 @@ static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd,
* consider it as a ecc error which will tell the caller the * consider it as a ecc error which will tell the caller the
* read fail We have distinguish all the errors, but the * read fail We have distinguish all the errors, but the
* nand_read_ecc only check this function return value * nand_read_ecc only check this function return value
*
* Corrected (single-bit) errors must also be noted.
*/ */
if (info->retcode != ERR_NONE) if (info->retcode == ERR_SBERR)
return 1;
else if (info->retcode != ERR_NONE)
return -1; return -1;
return 0; return 0;

View file

@ -329,7 +329,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va
} }
static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf) uint8_t *buf, int page)
{ {
int i, eccsize = chip->ecc.size; int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes; int eccbytes = chip->ecc.bytes;
@ -857,7 +857,6 @@ static int __exit flctl_remove(struct platform_device *pdev)
} }
static struct platform_driver flctl_driver = { static struct platform_driver flctl_driver = {
.probe = flctl_probe,
.remove = flctl_remove, .remove = flctl_remove,
.driver = { .driver = {
.name = "sh_flctl", .name = "sh_flctl",
@ -867,7 +866,7 @@ static struct platform_driver flctl_driver = {
static int __init flctl_nand_init(void) static int __init flctl_nand_init(void)
{ {
return platform_driver_register(&flctl_driver); return platform_driver_probe(&flctl_driver, flctl_probe);
} }
static void __exit flctl_nand_cleanup(void) static void __exit flctl_nand_cleanup(void)

View file

@ -301,6 +301,21 @@ static int tmio_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
return 0; return 0;
} }
static int tmio_nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
unsigned char *read_ecc, unsigned char *calc_ecc)
{
int r0, r1;
/* assume ecc.size = 512 and ecc.bytes = 6 */
r0 = __nand_correct_data(buf, read_ecc, calc_ecc, 256);
if (r0 < 0)
return r0;
r1 = __nand_correct_data(buf + 256, read_ecc + 3, calc_ecc + 3, 256);
if (r1 < 0)
return r1;
return r0 + r1;
}
static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio)
{ {
struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data;
@ -424,7 +439,7 @@ static int tmio_probe(struct platform_device *dev)
nand_chip->ecc.bytes = 6; nand_chip->ecc.bytes = 6;
nand_chip->ecc.hwctl = tmio_nand_enable_hwecc; nand_chip->ecc.hwctl = tmio_nand_enable_hwecc;
nand_chip->ecc.calculate = tmio_nand_calculate_ecc; nand_chip->ecc.calculate = tmio_nand_calculate_ecc;
nand_chip->ecc.correct = nand_correct_data; nand_chip->ecc.correct = tmio_nand_correct_data;
if (data) if (data)
nand_chip->badblock_pattern = data->badblock_pattern; nand_chip->badblock_pattern = data->badblock_pattern;

View file

@ -189,18 +189,43 @@ static int txx9ndfmc_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
uint8_t *ecc_code) uint8_t *ecc_code)
{ {
struct platform_device *dev = mtd_to_platdev(mtd); struct platform_device *dev = mtd_to_platdev(mtd);
struct nand_chip *chip = mtd->priv;
int eccbytes;
u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
mcr &= ~TXX9_NDFMCR_ECC_ALL; mcr &= ~TXX9_NDFMCR_ECC_ALL;
txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);
txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR); txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR);
for (eccbytes = chip->ecc.bytes; eccbytes > 0; eccbytes -= 3) {
ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR); ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR);
ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR); ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR);
ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR); ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR);
ecc_code += 3;
}
txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);
return 0; return 0;
} }
static int txx9ndfmc_correct_data(struct mtd_info *mtd, unsigned char *buf,
unsigned char *read_ecc, unsigned char *calc_ecc)
{
struct nand_chip *chip = mtd->priv;
int eccsize;
int corrected = 0;
int stat;
for (eccsize = chip->ecc.size; eccsize > 0; eccsize -= 256) {
stat = __nand_correct_data(buf, read_ecc, calc_ecc, 256);
if (stat < 0)
return stat;
corrected += stat;
buf += 256;
read_ecc += 3;
calc_ecc += 3;
}
return corrected;
}
static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
{ {
struct platform_device *dev = mtd_to_platdev(mtd); struct platform_device *dev = mtd_to_platdev(mtd);
@ -244,6 +269,22 @@ static void txx9ndfmc_initialize(struct platform_device *dev)
#define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \ #define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \
DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000) DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000)
static int txx9ndfmc_nand_scan(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
int ret;
ret = nand_scan_ident(mtd, 1);
if (!ret) {
if (mtd->writesize >= 512) {
chip->ecc.size = mtd->writesize;
chip->ecc.bytes = 3 * (mtd->writesize / 256);
}
ret = nand_scan_tail(mtd);
}
return ret;
}
static int __init txx9ndfmc_probe(struct platform_device *dev) static int __init txx9ndfmc_probe(struct platform_device *dev)
{ {
struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
@ -321,9 +362,10 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
chip->cmd_ctrl = txx9ndfmc_cmd_ctrl; chip->cmd_ctrl = txx9ndfmc_cmd_ctrl;
chip->dev_ready = txx9ndfmc_dev_ready; chip->dev_ready = txx9ndfmc_dev_ready;
chip->ecc.calculate = txx9ndfmc_calculate_ecc; chip->ecc.calculate = txx9ndfmc_calculate_ecc;
chip->ecc.correct = nand_correct_data; chip->ecc.correct = txx9ndfmc_correct_data;
chip->ecc.hwctl = txx9ndfmc_enable_hwecc; chip->ecc.hwctl = txx9ndfmc_enable_hwecc;
chip->ecc.mode = NAND_ECC_HW; chip->ecc.mode = NAND_ECC_HW;
/* txx9ndfmc_nand_scan will overwrite ecc.size and ecc.bytes */
chip->ecc.size = 256; chip->ecc.size = 256;
chip->ecc.bytes = 3; chip->ecc.bytes = 3;
chip->chip_delay = 100; chip->chip_delay = 100;
@ -349,7 +391,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
if (plat->wide_mask & (1 << i)) if (plat->wide_mask & (1 << i))
chip->options |= NAND_BUSWIDTH_16; chip->options |= NAND_BUSWIDTH_16;
if (nand_scan(mtd, 1)) { if (txx9ndfmc_nand_scan(mtd)) {
kfree(txx9_priv->mtdname); kfree(txx9_priv->mtdname);
kfree(txx9_priv); kfree(txx9_priv);
continue; continue;

View file

@ -0,0 +1,382 @@
/*
* Copyright (c) 2009 Nuvoton technology corporation.
*
* Wan ZongShun <mcuos.com@gmail.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;version 2 of the License.
*
*/
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#define REG_FMICSR 0x00
#define REG_SMCSR 0xa0
#define REG_SMISR 0xac
#define REG_SMCMD 0xb0
#define REG_SMADDR 0xb4
#define REG_SMDATA 0xb8
#define RESET_FMI 0x01
#define NAND_EN 0x08
#define READYBUSY (0x01 << 18)
#define SWRST 0x01
#define PSIZE (0x01 << 3)
#define DMARWEN (0x03 << 1)
#define BUSWID (0x01 << 4)
#define ECC4EN (0x01 << 5)
#define WP (0x01 << 24)
#define NANDCS (0x01 << 25)
#define ENDADDR (0x01 << 31)
#define read_data_reg(dev) \
__raw_readl((dev)->reg + REG_SMDATA)
#define write_data_reg(dev, val) \
__raw_writel((val), (dev)->reg + REG_SMDATA)
#define write_cmd_reg(dev, val) \
__raw_writel((val), (dev)->reg + REG_SMCMD)
#define write_addr_reg(dev, val) \
__raw_writel((val), (dev)->reg + REG_SMADDR)
struct w90p910_nand {
struct mtd_info mtd;
struct nand_chip chip;
void __iomem *reg;
struct clk *clk;
spinlock_t lock;
};
static const struct mtd_partition partitions[] = {
{
.name = "NAND FS 0",
.offset = 0,
.size = 8 * 1024 * 1024
},
{
.name = "NAND FS 1",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL
}
};
static unsigned char w90p910_nand_read_byte(struct mtd_info *mtd)
{
unsigned char ret;
struct w90p910_nand *nand;
nand = container_of(mtd, struct w90p910_nand, mtd);
ret = (unsigned char)read_data_reg(nand);
return ret;
}
static void w90p910_nand_read_buf(struct mtd_info *mtd,
unsigned char *buf, int len)
{
int i;
struct w90p910_nand *nand;
nand = container_of(mtd, struct w90p910_nand, mtd);
for (i = 0; i < len; i++)
buf[i] = (unsigned char)read_data_reg(nand);
}
static void w90p910_nand_write_buf(struct mtd_info *mtd,
const unsigned char *buf, int len)
{
int i;
struct w90p910_nand *nand;
nand = container_of(mtd, struct w90p910_nand, mtd);
for (i = 0; i < len; i++)
write_data_reg(nand, buf[i]);
}
static int w90p910_verify_buf(struct mtd_info *mtd,
const unsigned char *buf, int len)
{
int i;
struct w90p910_nand *nand;
nand = container_of(mtd, struct w90p910_nand, mtd);
for (i = 0; i < len; i++) {
if (buf[i] != (unsigned char)read_data_reg(nand))
return -EFAULT;
}
return 0;
}
static int w90p910_check_rb(struct w90p910_nand *nand)
{
unsigned int val;
spin_lock(&nand->lock);
val = __raw_readl(REG_SMISR);
val &= READYBUSY;
spin_unlock(&nand->lock);
return val;
}
static int w90p910_nand_devready(struct mtd_info *mtd)
{
struct w90p910_nand *nand;
int ready;
nand = container_of(mtd, struct w90p910_nand, mtd);
ready = (w90p910_check_rb(nand)) ? 1 : 0;
return ready;
}
static void w90p910_nand_command_lp(struct mtd_info *mtd,
unsigned int command, int column, int page_addr)
{
register struct nand_chip *chip = mtd->priv;
struct w90p910_nand *nand;
nand = container_of(mtd, struct w90p910_nand, mtd);
if (command == NAND_CMD_READOOB) {
column += mtd->writesize;
command = NAND_CMD_READ0;
}
write_cmd_reg(nand, command & 0xff);
if (column != -1 || page_addr != -1) {
if (column != -1) {
if (chip->options & NAND_BUSWIDTH_16)
column >>= 1;
write_addr_reg(nand, column);
write_addr_reg(nand, column >> 8 | ENDADDR);
}
if (page_addr != -1) {
write_addr_reg(nand, page_addr);
if (chip->chipsize > (128 << 20)) {
write_addr_reg(nand, page_addr >> 8);
write_addr_reg(nand, page_addr >> 16 | ENDADDR);
} else {
write_addr_reg(nand, page_addr >> 8 | ENDADDR);
}
}
}
switch (command) {
case NAND_CMD_CACHEDPROG:
case NAND_CMD_PAGEPROG:
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
case NAND_CMD_SEQIN:
case NAND_CMD_RNDIN:
case NAND_CMD_STATUS:
case NAND_CMD_DEPLETE1:
return;
case NAND_CMD_STATUS_ERROR:
case NAND_CMD_STATUS_ERROR0:
case NAND_CMD_STATUS_ERROR1:
case NAND_CMD_STATUS_ERROR2:
case NAND_CMD_STATUS_ERROR3:
udelay(chip->chip_delay);
return;
case NAND_CMD_RESET:
if (chip->dev_ready)
break;
udelay(chip->chip_delay);
write_cmd_reg(nand, NAND_CMD_STATUS);
write_cmd_reg(nand, command);
while (!w90p910_check_rb(nand))
;
return;
case NAND_CMD_RNDOUT:
write_cmd_reg(nand, NAND_CMD_RNDOUTSTART);
return;
case NAND_CMD_READ0:
write_cmd_reg(nand, NAND_CMD_READSTART);
default:
if (!chip->dev_ready) {
udelay(chip->chip_delay);
return;
}
}
/* Apply this short delay always to ensure that we do wait tWB in
* any case on any machine. */
ndelay(100);
while (!chip->dev_ready(mtd))
;
}
static void w90p910_nand_enable(struct w90p910_nand *nand)
{
unsigned int val;
spin_lock(&nand->lock);
__raw_writel(RESET_FMI, (nand->reg + REG_FMICSR));
val = __raw_readl(nand->reg + REG_FMICSR);
if (!(val & NAND_EN))
__raw_writel(val | NAND_EN, REG_FMICSR);
val = __raw_readl(nand->reg + REG_SMCSR);
val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS);
val |= WP;
__raw_writel(val, nand->reg + REG_SMCSR);
spin_unlock(&nand->lock);
}
static int __devinit w90p910_nand_probe(struct platform_device *pdev)
{
struct w90p910_nand *w90p910_nand;
struct nand_chip *chip;
int retval;
struct resource *res;
retval = 0;
w90p910_nand = kzalloc(sizeof(struct w90p910_nand), GFP_KERNEL);
if (!w90p910_nand)
return -ENOMEM;
chip = &(w90p910_nand->chip);
w90p910_nand->mtd.priv = chip;
w90p910_nand->mtd.owner = THIS_MODULE;
spin_lock_init(&w90p910_nand->lock);
w90p910_nand->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(w90p910_nand->clk)) {
retval = -ENOENT;
goto fail1;
}
clk_enable(w90p910_nand->clk);
chip->cmdfunc = w90p910_nand_command_lp;
chip->dev_ready = w90p910_nand_devready;
chip->read_byte = w90p910_nand_read_byte;
chip->write_buf = w90p910_nand_write_buf;
chip->read_buf = w90p910_nand_read_buf;
chip->verify_buf = w90p910_verify_buf;
chip->chip_delay = 50;
chip->options = 0;
chip->ecc.mode = NAND_ECC_SOFT;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
retval = -ENXIO;
goto fail1;
}
if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
retval = -EBUSY;
goto fail1;
}
w90p910_nand->reg = ioremap(res->start, resource_size(res));
if (!w90p910_nand->reg) {
retval = -ENOMEM;
goto fail2;
}
w90p910_nand_enable(w90p910_nand);
if (nand_scan(&(w90p910_nand->mtd), 1)) {
retval = -ENXIO;
goto fail3;
}
add_mtd_partitions(&(w90p910_nand->mtd), partitions,
ARRAY_SIZE(partitions));
platform_set_drvdata(pdev, w90p910_nand);
return retval;
fail3: iounmap(w90p910_nand->reg);
fail2: release_mem_region(res->start, resource_size(res));
fail1: kfree(w90p910_nand);
return retval;
}
static int __devexit w90p910_nand_remove(struct platform_device *pdev)
{
struct w90p910_nand *w90p910_nand = platform_get_drvdata(pdev);
struct resource *res;
iounmap(w90p910_nand->reg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));
clk_disable(w90p910_nand->clk);
clk_put(w90p910_nand->clk);
kfree(w90p910_nand);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver w90p910_nand_driver = {
.probe = w90p910_nand_probe,
.remove = __devexit_p(w90p910_nand_remove),
.driver = {
.name = "w90p910-fmi",
.owner = THIS_MODULE,
},
};
static int __init w90p910_nand_init(void)
{
return platform_driver_register(&w90p910_nand_driver);
}
static void __exit w90p910_nand_exit(void)
{
platform_driver_unregister(&w90p910_nand_driver);
}
module_init(w90p910_nand_init);
module_exit(w90p910_nand_exit);
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
MODULE_DESCRIPTION("w90p910 nand driver!");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:w90p910-fmi");

View file

@ -46,21 +46,12 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
const u32 *reg; const u32 *reg;
int len; int len;
/* check if this is a partition node */ reg = of_get_property(pp, "reg", &len);
partname = of_get_property(pp, "name", &len); if (!reg) {
if (strcmp(partname, "partition") != 0) {
nr_parts--; nr_parts--;
continue; continue;
} }
reg = of_get_property(pp, "reg", &len);
if (!reg || (len != 2 * sizeof(u32))) {
of_node_put(pp);
dev_err(dev, "Invalid 'reg' on %s\n", node->full_name);
kfree(*pparts);
*pparts = NULL;
return -EINVAL;
}
(*pparts)[i].offset = reg[0]; (*pparts)[i].offset = reg[0];
(*pparts)[i].size = reg[1]; (*pparts)[i].size = reg[1];
@ -75,6 +66,14 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
i++; i++;
} }
if (!i) {
of_node_put(pp);
dev_err(dev, "No valid partition found on %s\n", node->full_name);
kfree(*pparts);
*pparts = NULL;
return -EINVAL;
}
return nr_parts; return nr_parts;
} }
EXPORT_SYMBOL(of_mtd_parse_partitions); EXPORT_SYMBOL(of_mtd_parse_partitions);

View file

@ -5,6 +5,7 @@
menuconfig MTD_ONENAND menuconfig MTD_ONENAND
tristate "OneNAND Device Support" tristate "OneNAND Device Support"
depends on MTD depends on MTD
select MTD_PARTITIONS
help help
This enables support for accessing all type of OneNAND flash This enables support for accessing all type of OneNAND flash
devices. For further information see devices. For further information see
@ -23,7 +24,6 @@ config MTD_ONENAND_VERIFY_WRITE
config MTD_ONENAND_GENERIC config MTD_ONENAND_GENERIC
tristate "OneNAND Flash device via platform device driver" tristate "OneNAND Flash device via platform device driver"
depends on ARM
help help
Support for OneNAND flash via platform device driver. Support for OneNAND flash via platform device driver.
@ -66,7 +66,6 @@ config MTD_ONENAND_2X_PROGRAM
config MTD_ONENAND_SIM config MTD_ONENAND_SIM
tristate "OneNAND simulator support" tristate "OneNAND simulator support"
depends on MTD_PARTITIONS
help help
The simulator may simulate various OneNAND flash chips for the The simulator may simulate various OneNAND flash chips for the
OneNAND MTD layer. OneNAND MTD layer.

View file

@ -19,12 +19,16 @@
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h> #include <linux/mtd/onenand.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/mach/flash.h>
#define DRIVER_NAME "onenand"
/*
* Note: Driver name and platform data format have been updated!
*
* This version of the driver is named "onenand-flash" and takes struct
* onenand_platform_data as platform data. The old ARM-specific version
* with the name "onenand" used to take struct flash_platform_data.
*/
#define DRIVER_NAME "onenand-flash"
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
static const char *part_probes[] = { "cmdlinepart", NULL, }; static const char *part_probes[] = { "cmdlinepart", NULL, };
@ -39,16 +43,16 @@ struct onenand_info {
static int __devinit generic_onenand_probe(struct platform_device *pdev) static int __devinit generic_onenand_probe(struct platform_device *pdev)
{ {
struct onenand_info *info; struct onenand_info *info;
struct flash_platform_data *pdata = pdev->dev.platform_data; struct onenand_platform_data *pdata = pdev->dev.platform_data;
struct resource *res = pdev->resource; struct resource *res = pdev->resource;
unsigned long size = res->end - res->start + 1; unsigned long size = resource_size(res);
int err; int err;
info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL); info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL);
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;
if (!request_mem_region(res->start, size, pdev->dev.driver->name)) { if (!request_mem_region(res->start, size, dev_name(&pdev->dev))) {
err = -EBUSY; err = -EBUSY;
goto out_free_info; goto out_free_info;
} }
@ -59,7 +63,7 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev)
goto out_release_mem_region; goto out_release_mem_region;
} }
info->onenand.mmcontrol = pdata->mmcontrol; info->onenand.mmcontrol = pdata ? pdata->mmcontrol : 0;
info->onenand.irq = platform_get_irq(pdev, 0); info->onenand.irq = platform_get_irq(pdev, 0);
info->mtd.name = dev_name(&pdev->dev); info->mtd.name = dev_name(&pdev->dev);
@ -75,7 +79,7 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev)
err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
if (err > 0) if (err > 0)
add_mtd_partitions(&info->mtd, info->parts, err); add_mtd_partitions(&info->mtd, info->parts, err);
else if (err <= 0 && pdata->parts) else if (err <= 0 && pdata && pdata->parts)
add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
else else
#endif #endif
@ -99,7 +103,7 @@ static int __devexit generic_onenand_remove(struct platform_device *pdev)
{ {
struct onenand_info *info = platform_get_drvdata(pdev); struct onenand_info *info = platform_get_drvdata(pdev);
struct resource *res = pdev->resource; struct resource *res = pdev->resource;
unsigned long size = res->end - res->start + 1; unsigned long size = resource_size(res);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);

View file

@ -1191,7 +1191,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
/* /*
* Chip boundary handling in DDP * Chip boundary handling in DDP
* Now we issued chip 1 read and pointed chip 1 * Now we issued chip 1 read and pointed chip 1
* bufferam so we have to point chip 0 bufferam. * bufferram so we have to point chip 0 bufferram.
*/ */
if (ONENAND_IS_DDP(this) && if (ONENAND_IS_DDP(this) &&
unlikely(from == (this->chipsize >> 1))) { unlikely(from == (this->chipsize >> 1))) {
@ -1867,8 +1867,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
ONENAND_SET_NEXT_BUFFERRAM(this); ONENAND_SET_NEXT_BUFFERRAM(this);
/* /*
* 2 PLANE, MLC, and Flex-OneNAND doesn't support * 2 PLANE, MLC, and Flex-OneNAND do not support
* write-while-programe feature. * write-while-program feature.
*/ */
if (!ONENAND_IS_2PLANE(this) && !first) { if (!ONENAND_IS_2PLANE(this) && !first) {
ONENAND_SET_PREV_BUFFERRAM(this); ONENAND_SET_PREV_BUFFERRAM(this);
@ -1879,7 +1879,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
if (ret) { if (ret) {
written -= prevlen; written -= prevlen;
printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); printk(KERN_ERR "onenand_write_ops_nolock: write failed %d\n", ret);
break; break;
} }
@ -1905,7 +1905,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
/* In partial page write we don't update bufferram */ /* In partial page write we don't update bufferram */
onenand_update_bufferram(mtd, to, !ret && !subpage); onenand_update_bufferram(mtd, to, !ret && !subpage);
if (ret) { if (ret) {
printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); printk(KERN_ERR "onenand_write_ops_nolock: write failed %d\n", ret);
break; break;
} }
@ -2201,7 +2201,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
/* Grab the lock and see if the device is available */ /* Grab the lock and see if the device is available */
onenand_get_device(mtd, FL_ERASING); onenand_get_device(mtd, FL_ERASING);
/* Loop throught the pages */ /* Loop through the blocks */
instr->state = MTD_ERASING; instr->state = MTD_ERASING;
while (len) { while (len) {
@ -2328,7 +2328,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
if (bbm->bbt) if (bbm->bbt)
bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
/* We write two bytes, so we dont have to mess with 16 bit access */ /* We write two bytes, so we don't have to mess with 16-bit access */
ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
/* FIXME : What to do when marking SLC block in partition /* FIXME : What to do when marking SLC block in partition
* with MLC erasesize? For now, it is not advisable to * with MLC erasesize? For now, it is not advisable to
@ -2557,7 +2557,7 @@ static void onenand_unlock_all(struct mtd_info *mtd)
#ifdef CONFIG_MTD_ONENAND_OTP #ifdef CONFIG_MTD_ONENAND_OTP
/* Interal OTP operation */ /* Internal OTP operation */
typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len, typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len,
size_t *retlen, u_char *buf); size_t *retlen, u_char *buf);
@ -2921,7 +2921,7 @@ static void onenand_check_features(struct mtd_info *mtd)
this->options |= ONENAND_HAS_2PLANE; this->options |= ONENAND_HAS_2PLANE;
case ONENAND_DEVICE_DENSITY_2Gb: case ONENAND_DEVICE_DENSITY_2Gb:
/* 2Gb DDP don't have 2 plane */ /* 2Gb DDP does not have 2 plane */
if (!ONENAND_IS_DDP(this)) if (!ONENAND_IS_DDP(this))
this->options |= ONENAND_HAS_2PLANE; this->options |= ONENAND_HAS_2PLANE;
this->options |= ONENAND_HAS_UNLOCK_ALL; this->options |= ONENAND_HAS_UNLOCK_ALL;
@ -3364,7 +3364,7 @@ static int onenand_probe(struct mtd_info *mtd)
/* It's real page size */ /* It's real page size */
this->writesize = mtd->writesize; this->writesize = mtd->writesize;
/* REVIST: Multichip handling */ /* REVISIT: Multichip handling */
if (FLEXONENAND(this)) if (FLEXONENAND(this))
flexonenand_get_size(mtd); flexonenand_get_size(mtd);

View file

@ -512,7 +512,7 @@ static int __init mtd_oobtest_init(void)
goto out; goto out;
addr0 = 0; addr0 = 0;
for (i = 0; bbt[i] && i < ebcnt; ++i) for (i = 0; i < ebcnt && bbt[i]; ++i)
addr0 += mtd->erasesize; addr0 += mtd->erasesize;
/* Attempt to write off end of OOB */ /* Attempt to write off end of OOB */

View file

@ -116,11 +116,11 @@ static int verify_eraseblock(int ebnum)
loff_t addr = ebnum * mtd->erasesize; loff_t addr = ebnum * mtd->erasesize;
addr0 = 0; addr0 = 0;
for (i = 0; bbt[i] && i < ebcnt; ++i) for (i = 0; i < ebcnt && bbt[i]; ++i)
addr0 += mtd->erasesize; addr0 += mtd->erasesize;
addrn = mtd->size; addrn = mtd->size;
for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i) for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
addrn -= mtd->erasesize; addrn -= mtd->erasesize;
set_random_data(writebuf, mtd->erasesize); set_random_data(writebuf, mtd->erasesize);
@ -219,11 +219,11 @@ static int crosstest(void)
memset(pp1, 0, pgsize * 4); memset(pp1, 0, pgsize * 4);
addr0 = 0; addr0 = 0;
for (i = 0; bbt[i] && i < ebcnt; ++i) for (i = 0; i < ebcnt && bbt[i]; ++i)
addr0 += mtd->erasesize; addr0 += mtd->erasesize;
addrn = mtd->size; addrn = mtd->size;
for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i) for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
addrn -= mtd->erasesize; addrn -= mtd->erasesize;
/* Read 2nd-to-last page to pp1 */ /* Read 2nd-to-last page to pp1 */
@ -317,7 +317,7 @@ static int erasecrosstest(void)
ebnum = 0; ebnum = 0;
addr0 = 0; addr0 = 0;
for (i = 0; bbt[i] && i < ebcnt; ++i) { for (i = 0; i < ebcnt && bbt[i]; ++i) {
addr0 += mtd->erasesize; addr0 += mtd->erasesize;
ebnum += 1; ebnum += 1;
} }
@ -413,7 +413,7 @@ static int erasetest(void)
ebnum = 0; ebnum = 0;
addr0 = 0; addr0 = 0;
for (i = 0; bbt[i] && i < ebcnt; ++i) { for (i = 0; i < ebcnt && bbt[i]; ++i) {
addr0 += mtd->erasesize; addr0 += mtd->erasesize;
ebnum += 1; ebnum += 1;
} }

View file

@ -15,6 +15,7 @@
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/kthread.h>
#include "nodelist.h" #include "nodelist.h"
@ -31,7 +32,7 @@ void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
/* This must only ever be called when no GC thread is currently running */ /* This must only ever be called when no GC thread is currently running */
int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
{ {
pid_t pid; struct task_struct *tsk;
int ret = 0; int ret = 0;
BUG_ON(c->gc_task); BUG_ON(c->gc_task);
@ -39,15 +40,16 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
init_completion(&c->gc_thread_start); init_completion(&c->gc_thread_start);
init_completion(&c->gc_thread_exit); init_completion(&c->gc_thread_exit);
pid = kernel_thread(jffs2_garbage_collect_thread, c, CLONE_FS|CLONE_FILES); tsk = kthread_run(jffs2_garbage_collect_thread, c, "jffs2_gcd_mtd%d", c->mtd->index);
if (pid < 0) { if (IS_ERR(tsk)) {
printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %d\n", -pid); printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %ld\n", -PTR_ERR(tsk));
complete(&c->gc_thread_exit); complete(&c->gc_thread_exit);
ret = pid; ret = PTR_ERR(tsk);
} else { } else {
/* Wait for it... */ /* Wait for it... */
D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", pid)); D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", tsk->pid));
wait_for_completion(&c->gc_thread_start); wait_for_completion(&c->gc_thread_start);
ret = tsk->pid;
} }
return ret; return ret;
@ -71,7 +73,6 @@ static int jffs2_garbage_collect_thread(void *_c)
{ {
struct jffs2_sb_info *c = _c; struct jffs2_sb_info *c = _c;
daemonize("jffs2_gcd_mtd%d", c->mtd->index);
allow_signal(SIGKILL); allow_signal(SIGKILL);
allow_signal(SIGSTOP); allow_signal(SIGSTOP);
allow_signal(SIGCONT); allow_signal(SIGCONT);
@ -107,6 +108,11 @@ static int jffs2_garbage_collect_thread(void *_c)
* the GC thread get there first. */ * the GC thread get there first. */
schedule_timeout_interruptible(msecs_to_jiffies(50)); schedule_timeout_interruptible(msecs_to_jiffies(50));
if (kthread_should_stop()) {
D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): kthread_stop() called.\n"));
goto die;
}
/* Put_super will send a SIGKILL and then wait on the sem. /* Put_super will send a SIGKILL and then wait on the sem.
*/ */
while (signal_pending(current) || freezing(current)) { while (signal_pending(current) || freezing(current)) {

View file

@ -39,13 +39,13 @@ int __init jffs2_create_slab_caches(void)
raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent",
sizeof(struct jffs2_raw_dirent), sizeof(struct jffs2_raw_dirent),
0, 0, NULL); 0, SLAB_HWCACHE_ALIGN, NULL);
if (!raw_dirent_slab) if (!raw_dirent_slab)
goto err; goto err;
raw_inode_slab = kmem_cache_create("jffs2_raw_inode", raw_inode_slab = kmem_cache_create("jffs2_raw_inode",
sizeof(struct jffs2_raw_inode), sizeof(struct jffs2_raw_inode),
0, 0, NULL); 0, SLAB_HWCACHE_ALIGN, NULL);
if (!raw_inode_slab) if (!raw_inode_slab)
goto err; goto err;

View file

@ -121,6 +121,7 @@ typedef enum {
NAND_ECC_SOFT, NAND_ECC_SOFT,
NAND_ECC_HW, NAND_ECC_HW,
NAND_ECC_HW_SYNDROME, NAND_ECC_HW_SYNDROME,
NAND_ECC_HW_OOB_FIRST,
} nand_ecc_modes_t; } nand_ecc_modes_t;
/* /*
@ -271,13 +272,13 @@ struct nand_ecc_ctrl {
uint8_t *calc_ecc); uint8_t *calc_ecc);
int (*read_page_raw)(struct mtd_info *mtd, int (*read_page_raw)(struct mtd_info *mtd,
struct nand_chip *chip, struct nand_chip *chip,
uint8_t *buf); uint8_t *buf, int page);
void (*write_page_raw)(struct mtd_info *mtd, void (*write_page_raw)(struct mtd_info *mtd,
struct nand_chip *chip, struct nand_chip *chip,
const uint8_t *buf); const uint8_t *buf);
int (*read_page)(struct mtd_info *mtd, int (*read_page)(struct mtd_info *mtd,
struct nand_chip *chip, struct nand_chip *chip,
uint8_t *buf); uint8_t *buf, int page);
int (*read_subpage)(struct mtd_info *mtd, int (*read_subpage)(struct mtd_info *mtd,
struct nand_chip *chip, struct nand_chip *chip,
uint32_t offs, uint32_t len, uint32_t offs, uint32_t len,

View file

@ -20,6 +20,12 @@ struct mtd_info;
*/ */
int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
/*
* Detect and correct a 1 bit error for eccsize byte block
*/
int __nand_correct_data(u_char *dat, u_char *read_ecc, u_char *calc_ecc,
unsigned int eccsize);
/* /*
* Detect and correct a 1 bit error for 256 byte block * Detect and correct a 1 bit error for 256 byte block
*/ */

View file

@ -214,4 +214,12 @@ unsigned onenand_block(struct onenand_chip *this, loff_t addr);
loff_t onenand_addr(struct onenand_chip *this, int block); loff_t onenand_addr(struct onenand_chip *this, int block);
int flexonenand_region(struct mtd_info *mtd, loff_t addr); int flexonenand_region(struct mtd_info *mtd, loff_t addr);
struct mtd_partition;
struct onenand_platform_data {
void (*mmcontrol)(struct mtd_info *mtd, int sync_read);
struct mtd_partition *parts;
unsigned int nr_parts;
};
#endif /* __LINUX_MTD_ONENAND_H */ #endif /* __LINUX_MTD_ONENAND_H */

View file

@ -207,6 +207,9 @@
#define ONENAND_ECC_2BIT (1 << 1) #define ONENAND_ECC_2BIT (1 << 1)
#define ONENAND_ECC_2BIT_ALL (0xAAAA) #define ONENAND_ECC_2BIT_ALL (0xAAAA)
#define FLEXONENAND_UNCORRECTABLE_ERROR (0x1010) #define FLEXONENAND_UNCORRECTABLE_ERROR (0x1010)
#define ONENAND_ECC_3BIT (1 << 2)
#define ONENAND_ECC_4BIT (1 << 3)
#define ONENAND_ECC_4BIT_UNCORRECTABLE (0x1010)
/* /*
* One-Time Programmable (OTP) * One-Time Programmable (OTP)