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

* git://git.infradead.org/mtd-2.6: (63 commits)
  mtd: OneNAND: Allow setting of boundary information when built as module
  jffs2: leaking jffs2_summary in function jffs2_scan_medium
  mtd: nand: Fix memory leak on txx9ndfmc probe failure.
  mtd: orion_nand: use burst reads with double word accesses
  mtd/nand: s3c6400 support for s3c2410 driver
  [MTD] [NAND] S3C2410: Use DIV_ROUND_UP
  [MTD] [NAND] S3C2410: Deal with unaligned lengths in S3C2440 buffer read/write
  [MTD] [NAND] S3C2410: Allow the machine code to get the BBT table from NAND
  [MTD] [NAND] S3C2410: Added a kerneldoc for s3c2410_nand_set
  mtd: physmap_of: Add multiple regions and concatenation support
  mtd: nand: max_retries off by one in mxc_nand
  mtd: nand: s3c2410_nand_setrate(): use correct macros for 2412/2440
  mtd: onenand: add bbt_wait & unlock_all as replaceable for some platform
  mtd: Flex-OneNAND support
  mtd: nand: add OMAP2/OMAP3 NAND driver
  mtd: maps: Blackfin async: fix memory leaks in probe/remove funcs
  mtd: uclinux: mark local stuff static
  mtd: uclinux: do not allow to be built as a module
  mtd: uclinux: allow systems to override map addr/size
  mtd: blackfin NFC: fix hang when using NAND on BF527-EZKITs
  ...
This commit is contained in:
Linus Torvalds 2009-06-22 16:56:22 -07:00
commit ac1b7c378e
49 changed files with 3139 additions and 833 deletions

View file

@ -0,0 +1,125 @@
What: /sys/class/mtd/
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
The mtd/ class subdirectory belongs to the MTD subsystem
(MTD core).
What: /sys/class/mtd/mtdX/
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
The /sys/class/mtd/mtd{0,1,2,3,...} directories correspond
to each /dev/mtdX character device. These may represent
physical/simulated flash devices, partitions on a flash
device, or concatenated flash devices. They exist regardless
of whether CONFIG_MTD_CHAR is actually enabled.
What: /sys/class/mtd/mtdXro/
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
These directories provide the corresponding read-only device
nodes for /sys/class/mtd/mtdX/ . They are only created
(for the benefit of udev) if CONFIG_MTD_CHAR is enabled.
What: /sys/class/mtd/mtdX/dev
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
Major and minor numbers of the character device corresponding
to this MTD device (in <major>:<minor> format). This is the
read-write device so <minor> will be even.
What: /sys/class/mtd/mtdXro/dev
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
Major and minor numbers of the character device corresponding
to the read-only variant of thie MTD device (in
<major>:<minor> format). In this case <minor> will be odd.
What: /sys/class/mtd/mtdX/erasesize
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
"Major" erase size for the device. If numeraseregions is
zero, this is the eraseblock size for the entire device.
Otherwise, the MEMGETREGIONCOUNT/MEMGETREGIONINFO ioctls
can be used to determine the actual eraseblock layout.
What: /sys/class/mtd/mtdX/flags
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
A hexadecimal value representing the device flags, ORed
together:
0x0400: MTD_WRITEABLE - device is writable
0x0800: MTD_BIT_WRITEABLE - single bits can be flipped
0x1000: MTD_NO_ERASE - no erase necessary
0x2000: MTD_POWERUP_LOCK - always locked after reset
What: /sys/class/mtd/mtdX/name
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
A human-readable ASCII name for the device or partition.
This will match the name in /proc/mtd .
What: /sys/class/mtd/mtdX/numeraseregions
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
For devices that have variable eraseblock sizes, this
provides the total number of erase regions. Otherwise,
it will read back as zero.
What: /sys/class/mtd/mtdX/oobsize
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
Number of OOB bytes per page.
What: /sys/class/mtd/mtdX/size
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
Total size of the device/partition, in bytes.
What: /sys/class/mtd/mtdX/type
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
One of the following ASCII strings, representing the device
type:
absent, ram, rom, nor, nand, dataflash, ubi, unknown
What: /sys/class/mtd/mtdX/writesize
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
Minimal writable flash unit size. This will always be
a positive integer.
In the case of NOR flash it is 1 (even though individual
bits can be cleared).
In the case of NAND flash it is one NAND page (or a
half page, or a quarter page).
In the case of ECC NOR, it is the ECC block size.

View file

@ -1431,6 +1431,16 @@ and is between 256 and 4096 characters. It is defined in the file
mtdparts= [MTD] mtdparts= [MTD]
See drivers/mtd/cmdlinepart.c. See drivers/mtd/cmdlinepart.c.
onenand.bdry= [HW,MTD] Flex-OneNAND Boundary Configuration
Format: [die0_boundary][,die0_lock][,die1_boundary][,die1_lock]
boundary - index of last SLC block on Flex-OneNAND.
The remaining blocks are configured as MLC blocks.
lock - Configure if Flex-OneNAND boundary should be locked.
Once locked, the boundary cannot be changed.
1 indicates lock status, 0 indicates unlock status.
mtdset= [ARM] mtdset= [ARM]
ARM/S3C2412 JIVE boot control ARM/S3C2412 JIVE boot control

View file

@ -3252,7 +3252,6 @@ W: http://www.linux-mtd.infradead.org/doc/jffs2.html
S: Maintained S: Maintained
F: fs/jffs2/ F: fs/jffs2/
F: include/linux/jffs2.h F: include/linux/jffs2.h
F: include/mtd/jffs2-user.h
JOURNALLING LAYER FOR BLOCK DEVICES (JBD) JOURNALLING LAYER FOR BLOCK DEVICES (JBD)
P: Stephen Tweedie P: Stephen Tweedie

View file

@ -68,10 +68,14 @@ struct davinci_nand_pdata { /* platform_data */
/* none == NAND_ECC_NONE (strongly *not* advised!!) /* none == NAND_ECC_NONE (strongly *not* advised!!)
* soft == NAND_ECC_SOFT * soft == NAND_ECC_SOFT
* 1-bit == NAND_ECC_HW * else == NAND_ECC_HW, according to ecc_bits
* 4-bit == NAND_ECC_HW_SYNDROME (not on all chips) *
* All DaVinci-family chips support 1-bit hardware ECC.
* Newer ones also support 4-bit ECC, but are awkward
* using it with large page chips.
*/ */
nand_ecc_modes_t ecc_mode; nand_ecc_modes_t ecc_mode;
u8 ecc_bits;
/* e.g. NAND_BUSWIDTH_16 or NAND_USE_FLASH_BBT */ /* e.g. NAND_BUSWIDTH_16 or NAND_USE_FLASH_BBT */
unsigned options; unsigned options;

View file

@ -10,19 +10,26 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
/* struct s3c2410_nand_set /**
* struct s3c2410_nand_set - define a set of one or more nand chips
* @disable_ecc: Entirely disable ECC - Dangerous
* @flash_bbt: Openmoko u-boot can create a Bad Block Table
* Setting this flag will allow the kernel to
* look for it at boot time and also skip the NAND
* scan.
* @nr_chips: Number of chips in this set
* @nr_partitions: Number of partitions pointed to by @partitions
* @name: Name of set (optional)
* @nr_map: Map for low-layer logical to physical chip numbers (option)
* @partitions: The mtd partition list
* *
* define an set of one or more nand chips registered with an unique mtd * define a set of one or more nand chips registered with an unique mtd. Also
* * allows to pass flag to the underlying NAND layer. 'disable_ecc' will trigger
* nr_chips = number of chips in this set * a warning at boot time.
* nr_partitions = number of partitions pointed to be partitoons (or zero) */
* name = name of set (optional)
* nr_map = map for low-layer logical to physical chip numbers (option)
* partitions = mtd partition list
*/
struct s3c2410_nand_set { struct s3c2410_nand_set {
unsigned int disable_ecc : 1; unsigned int disable_ecc:1;
unsigned int flash_bbt:1;
int nr_chips; int nr_chips;
int nr_partitions; int nr_partitions;
@ -39,7 +46,7 @@ struct s3c2410_platform_nand {
int twrph0; /* active time for nWE/nOE */ int twrph0; /* active time for nWE/nOE */
int twrph1; /* time for release CLE/ALE from nWE/nOE inactive */ int twrph1; /* time for release CLE/ALE from nWE/nOE inactive */
unsigned int ignore_unset_ecc : 1; unsigned int ignore_unset_ecc:1;
int nr_sets; int nr_sets;
struct s3c2410_nand_set *sets; struct s3c2410_nand_set *sets;

View file

@ -46,6 +46,7 @@
#define MANUFACTURER_INTEL 0x0089 #define MANUFACTURER_INTEL 0x0089
#define I82802AB 0x00ad #define I82802AB 0x00ad
#define I82802AC 0x00ac #define I82802AC 0x00ac
#define PF38F4476 0x881c
#define MANUFACTURER_ST 0x0020 #define MANUFACTURER_ST 0x0020
#define M50LPW080 0x002F #define M50LPW080 0x002F
#define M50FLW080A 0x0080 #define M50FLW080A 0x0080
@ -315,10 +316,20 @@ static struct cfi_fixup fixup_table[] = {
{ 0, 0, NULL, NULL } { 0, 0, NULL, NULL }
}; };
static void cfi_fixup_major_minor(struct cfi_private *cfi,
struct cfi_pri_intelext *extp)
{
if (cfi->mfr == MANUFACTURER_INTEL &&
cfi->id == PF38F4476 && extp->MinorVersion == '3')
extp->MinorVersion = '1';
}
static inline struct cfi_pri_intelext * static inline struct cfi_pri_intelext *
read_pri_intelext(struct map_info *map, __u16 adr) read_pri_intelext(struct map_info *map, __u16 adr)
{ {
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_pri_intelext *extp; struct cfi_pri_intelext *extp;
unsigned int extra_size = 0;
unsigned int extp_size = sizeof(*extp); unsigned int extp_size = sizeof(*extp);
again: again:
@ -326,6 +337,8 @@ read_pri_intelext(struct map_info *map, __u16 adr)
if (!extp) if (!extp)
return NULL; return NULL;
cfi_fixup_major_minor(cfi, extp);
if (extp->MajorVersion != '1' || if (extp->MajorVersion != '1' ||
(extp->MinorVersion < '0' || extp->MinorVersion > '5')) { (extp->MinorVersion < '0' || extp->MinorVersion > '5')) {
printk(KERN_ERR " Unknown Intel/Sharp Extended Query " printk(KERN_ERR " Unknown Intel/Sharp Extended Query "
@ -340,19 +353,24 @@ read_pri_intelext(struct map_info *map, __u16 adr)
extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask);
extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr);
if (extp->MajorVersion == '1' && extp->MinorVersion >= '3') { if (extp->MinorVersion >= '0') {
unsigned int extra_size = 0; extra_size = 0;
int nb_parts, i;
/* Protection Register info */ /* Protection Register info */
extra_size += (extp->NumProtectionFields - 1) * extra_size += (extp->NumProtectionFields - 1) *
sizeof(struct cfi_intelext_otpinfo); sizeof(struct cfi_intelext_otpinfo);
}
if (extp->MinorVersion >= '1') {
/* Burst Read info */ /* Burst Read info */
extra_size += 2; extra_size += 2;
if (extp_size < sizeof(*extp) + extra_size) if (extp_size < sizeof(*extp) + extra_size)
goto need_more; goto need_more;
extra_size += extp->extra[extra_size-1]; extra_size += extp->extra[extra_size - 1];
}
if (extp->MinorVersion >= '3') {
int nb_parts, i;
/* Number of hardware-partitions */ /* Number of hardware-partitions */
extra_size += 1; extra_size += 1;

View file

@ -166,6 +166,7 @@
#define SST39LF040 0x00D7 #define SST39LF040 0x00D7
#define SST39SF010A 0x00B5 #define SST39SF010A 0x00B5
#define SST39SF020A 0x00B6 #define SST39SF020A 0x00B6
#define SST39SF040 0x00B7
#define SST49LF004B 0x0060 #define SST49LF004B 0x0060
#define SST49LF040B 0x0050 #define SST49LF040B 0x0050
#define SST49LF008A 0x005a #define SST49LF008A 0x005a
@ -1391,6 +1392,18 @@ static const struct amd_flash_info jedec_table[] = {
.regions = { .regions = {
ERASEINFO(0x01000,64), ERASEINFO(0x01000,64),
} }
}, {
.mfr_id = MANUFACTURER_SST,
.dev_id = SST39SF040,
.name = "SST 39SF040",
.devtypes = CFI_DEVICETYPE_X8,
.uaddr = MTD_UADDR_0x5555_0x2AAA,
.dev_size = SIZE_512KiB,
.cmd_set = P_ID_AMD_STD,
.nr_regions = 1,
.regions = {
ERASEINFO(0x01000,128),
}
}, { }, {
.mfr_id = MANUFACTURER_SST, .mfr_id = MANUFACTURER_SST,
.dev_id = SST49LF040B, .dev_id = SST49LF040B,

View file

@ -500,6 +500,9 @@ static struct flash_info __devinitdata m25p_data [] = {
{ "at26df161a", 0x1f4601, 0, 64 * 1024, 32, SECT_4K, }, { "at26df161a", 0x1f4601, 0, 64 * 1024, 32, SECT_4K, },
{ "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, }, { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, },
/* Macronix */
{ "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, },
/* Spansion -- single (large) sector size only, at least /* Spansion -- single (large) sector size only, at least
* for the chips listed here (without boot sectors). * for the chips listed here (without boot sectors).
*/ */
@ -528,6 +531,7 @@ static struct flash_info __devinitdata m25p_data [] = {
{ "m25p64", 0x202017, 0, 64 * 1024, 128, }, { "m25p64", 0x202017, 0, 64 * 1024, 128, },
{ "m25p128", 0x202018, 0, 256 * 1024, 64, }, { "m25p128", 0x202018, 0, 256 * 1024, 64, },
{ "m45pe10", 0x204011, 0, 64 * 1024, 2, },
{ "m45pe80", 0x204014, 0, 64 * 1024, 16, }, { "m45pe80", 0x204014, 0, 64 * 1024, 16, },
{ "m45pe16", 0x204015, 0, 64 * 1024, 32, }, { "m45pe16", 0x204015, 0, 64 * 1024, 32, },

View file

@ -105,15 +105,6 @@ config MSP_FLASH_MAP_LIMIT
default "0x02000000" default "0x02000000"
depends on MSP_FLASH_MAP_LIMIT_32M depends on MSP_FLASH_MAP_LIMIT_32M
config MTD_PMC_MSP_RAMROOT
tristate "Embedded RAM block device for root on PMC-Sierra MSP"
depends on PMC_MSP_EMBEDDED_ROOTFS && \
(MTD_BLOCK || MTD_BLOCK_RO) && \
MTD_RAM
help
This provides support for the embedded root file system
on PMC MSP devices. This memory is mapped as a MTD block device.
config MTD_SUN_UFLASH config MTD_SUN_UFLASH
tristate "Sun Microsystems userflash support" tristate "Sun Microsystems userflash support"
depends on SPARC && MTD_CFI && PCI depends on SPARC && MTD_CFI && PCI
@ -270,7 +261,7 @@ config MTD_ALCHEMY
config MTD_DILNETPC config MTD_DILNETPC
tristate "CFI Flash device mapped on DIL/Net PC" tristate "CFI Flash device mapped on DIL/Net PC"
depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN
help help
MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP". MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP".
For details, see <http://www.ssv-embedded.de/ssv/pc104/p169.htm> For details, see <http://www.ssv-embedded.de/ssv/pc104/p169.htm>
@ -501,7 +492,7 @@ config MTD_BFIN_ASYNC
If compiled as a module, it will be called bfin-async-flash. If compiled as a module, it will be called bfin-async-flash.
config MTD_UCLINUX config MTD_UCLINUX
tristate "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 && !MMU
help help
Map driver to support image based filesystems for uClinux. Map driver to support image based filesystems for uClinux.

View file

@ -25,7 +25,6 @@ obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o
obj-$(CONFIG_MTD_PMC_MSP_RAMROOT)+= pmcmsp-ramroot.o
obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o
obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o
obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o

View file

@ -40,6 +40,9 @@ struct async_state {
uint32_t flash_ambctl0, flash_ambctl1; uint32_t flash_ambctl0, flash_ambctl1;
uint32_t save_ambctl0, save_ambctl1; uint32_t save_ambctl0, save_ambctl1;
unsigned long irq_flags; unsigned long irq_flags;
#ifdef CONFIG_MTD_PARTITIONS
struct mtd_partition *parts;
#endif
}; };
static void switch_to_flash(struct async_state *state) static void switch_to_flash(struct async_state *state)
@ -170,6 +173,7 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev)
if (ret > 0) { if (ret > 0) {
pr_devinit(KERN_NOTICE DRIVER_NAME ": Using commandline partition definition\n"); pr_devinit(KERN_NOTICE DRIVER_NAME ": Using commandline partition definition\n");
add_mtd_partitions(state->mtd, pdata->parts, ret); add_mtd_partitions(state->mtd, pdata->parts, ret);
state->parts = pdata->parts;
} else if (pdata->nr_parts) { } else if (pdata->nr_parts) {
pr_devinit(KERN_NOTICE DRIVER_NAME ": Using board partition definition\n"); pr_devinit(KERN_NOTICE DRIVER_NAME ": Using board partition definition\n");
@ -193,6 +197,7 @@ static int __devexit bfin_flash_remove(struct platform_device *pdev)
gpio_free(state->enet_flash_pin); gpio_free(state->enet_flash_pin);
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
del_mtd_partitions(state->mtd); del_mtd_partitions(state->mtd);
kfree(state->parts);
#endif #endif
map_destroy(state->mtd); map_destroy(state->mtd);
kfree(state); kfree(state);

View file

@ -36,27 +36,33 @@
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/map.h> #include <linux/mtd/map.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/mtd/concat.h>
#include <asm/mach/flash.h> #include <asm/mach/flash.h>
#include <mach/hardware.h> #include <mach/hardware.h>
#include <asm/system.h> #include <asm/system.h>
#ifdef CONFIG_ARCH_P720T #define SUBDEV_NAME_SIZE (BUS_ID_SIZE + 2)
#define FLASH_BASE (0x04000000)
#define FLASH_SIZE (64*1024*1024) struct armflash_subdev_info {
#endif char name[SUBDEV_NAME_SIZE];
struct mtd_info *mtd;
struct map_info map;
struct flash_platform_data *plat;
};
struct armflash_info { struct armflash_info {
struct flash_platform_data *plat;
struct resource *res; struct resource *res;
struct mtd_partition *parts; struct mtd_partition *parts;
struct mtd_info *mtd; struct mtd_info *mtd;
struct map_info map; int nr_subdev;
struct armflash_subdev_info subdev[0];
}; };
static void armflash_set_vpp(struct map_info *map, int on) static void armflash_set_vpp(struct map_info *map, int on)
{ {
struct armflash_info *info = container_of(map, struct armflash_info, map); struct armflash_subdev_info *info =
container_of(map, struct armflash_subdev_info, map);
if (info->plat && info->plat->set_vpp) if (info->plat && info->plat->set_vpp)
info->plat->set_vpp(on); info->plat->set_vpp(on);
@ -64,32 +70,17 @@ static void armflash_set_vpp(struct map_info *map, int on)
static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL }; static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL };
static int armflash_probe(struct platform_device *dev) static int armflash_subdev_probe(struct armflash_subdev_info *subdev,
struct resource *res)
{ {
struct flash_platform_data *plat = dev->dev.platform_data; struct flash_platform_data *plat = subdev->plat;
struct resource *res = dev->resource; resource_size_t size = res->end - res->start + 1;
unsigned int size = res->end - res->start + 1;
struct armflash_info *info;
int err;
void __iomem *base; void __iomem *base;
int err = 0;
info = kzalloc(sizeof(struct armflash_info), GFP_KERNEL); if (!request_mem_region(res->start, size, subdev->name)) {
if (!info) {
err = -ENOMEM;
goto out;
}
info->plat = plat;
if (plat && plat->init) {
err = plat->init();
if (err)
goto no_resource;
}
info->res = request_mem_region(res->start, size, "armflash");
if (!info->res) {
err = -EBUSY; err = -EBUSY;
goto no_resource; goto out;
} }
base = ioremap(res->start, size); base = ioremap(res->start, size);
@ -101,27 +92,132 @@ static int armflash_probe(struct platform_device *dev)
/* /*
* look for CFI based flash parts fitted to this board * look for CFI based flash parts fitted to this board
*/ */
info->map.size = size; subdev->map.size = size;
info->map.bankwidth = plat->width; subdev->map.bankwidth = plat->width;
info->map.phys = res->start; subdev->map.phys = res->start;
info->map.virt = base; subdev->map.virt = base;
info->map.name = dev_name(&dev->dev); subdev->map.name = subdev->name;
info->map.set_vpp = armflash_set_vpp; subdev->map.set_vpp = armflash_set_vpp;
simple_map_init(&info->map); simple_map_init(&subdev->map);
/* /*
* Also, the CFI layer automatically works out what size * Also, the CFI layer automatically works out what size
* of chips we have, and does the necessary identification * of chips we have, and does the necessary identification
* for us automatically. * for us automatically.
*/ */
info->mtd = do_map_probe(plat->map_name, &info->map); subdev->mtd = do_map_probe(plat->map_name, &subdev->map);
if (!info->mtd) { if (!subdev->mtd) {
err = -ENXIO; err = -ENXIO;
goto no_device; goto no_device;
} }
info->mtd->owner = THIS_MODULE; subdev->mtd->owner = THIS_MODULE;
/* Successful? */
if (err == 0)
return err;
if (subdev->mtd)
map_destroy(subdev->mtd);
no_device:
iounmap(base);
no_mem:
release_mem_region(res->start, size);
out:
return err;
}
static void armflash_subdev_remove(struct armflash_subdev_info *subdev)
{
if (subdev->mtd)
map_destroy(subdev->mtd);
if (subdev->map.virt)
iounmap(subdev->map.virt);
release_mem_region(subdev->map.phys, subdev->map.size);
}
static int armflash_probe(struct platform_device *dev)
{
struct flash_platform_data *plat = dev->dev.platform_data;
unsigned int size;
struct armflash_info *info;
int i, nr, err;
/* Count the number of devices */
for (nr = 0; ; nr++)
if (!platform_get_resource(dev, IORESOURCE_MEM, nr))
break;
if (nr == 0) {
err = -ENODEV;
goto out;
}
size = sizeof(struct armflash_info) +
sizeof(struct armflash_subdev_info) * nr;
info = kzalloc(size, GFP_KERNEL);
if (!info) {
err = -ENOMEM;
goto out;
}
if (plat && plat->init) {
err = plat->init();
if (err)
goto no_resource;
}
for (i = 0; i < nr; i++) {
struct armflash_subdev_info *subdev = &info->subdev[i];
struct resource *res;
res = platform_get_resource(dev, IORESOURCE_MEM, i);
if (!res)
break;
if (nr == 1)
/* No MTD concatenation, just use the default name */
snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s",
dev_name(&dev->dev));
else
snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s-%d",
dev_name(&dev->dev), i);
subdev->plat = plat;
err = armflash_subdev_probe(subdev, res);
if (err)
break;
}
info->nr_subdev = i;
if (err)
goto subdev_err;
if (info->nr_subdev == 1)
info->mtd = info->subdev[0].mtd;
else if (info->nr_subdev > 1) {
#ifdef CONFIG_MTD_CONCAT
struct mtd_info *cdev[info->nr_subdev];
/*
* We detected multiple devices. Concatenate them together.
*/
for (i = 0; i < info->nr_subdev; i++)
cdev[i] = info->subdev[i].mtd;
info->mtd = mtd_concat_create(cdev, info->nr_subdev,
dev_name(&dev->dev));
if (info->mtd == NULL)
err = -ENXIO;
#else
printk(KERN_ERR "armflash: multiple devices found but "
"MTD concat support disabled.\n");
err = -ENXIO;
#endif
}
if (err < 0)
goto cleanup;
err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0);
if (err > 0) { if (err > 0) {
@ -131,28 +227,30 @@ static int armflash_probe(struct platform_device *dev)
"mtd partition registration failed: %d\n", err); "mtd partition registration failed: %d\n", err);
} }
if (err == 0) if (err == 0) {
platform_set_drvdata(dev, info); platform_set_drvdata(dev, info);
return err;
}
/* /*
* If we got an error, free all resources. * We got an error, free all resources.
*/ */
if (err < 0) { cleanup:
if (info->mtd) { if (info->mtd) {
del_mtd_partitions(info->mtd); del_mtd_partitions(info->mtd);
map_destroy(info->mtd); #ifdef CONFIG_MTD_CONCAT
} if (info->mtd != info->subdev[0].mtd)
kfree(info->parts); mtd_concat_destroy(info->mtd);
#endif
no_device:
iounmap(base);
no_mem:
release_mem_region(res->start, size);
no_resource:
if (plat && plat->exit)
plat->exit();
kfree(info);
} }
kfree(info->parts);
subdev_err:
for (i = info->nr_subdev - 1; i >= 0; i--)
armflash_subdev_remove(&info->subdev[i]);
no_resource:
if (plat && plat->exit)
plat->exit();
kfree(info);
out: out:
return err; return err;
} }
@ -160,22 +258,26 @@ static int armflash_probe(struct platform_device *dev)
static int armflash_remove(struct platform_device *dev) static int armflash_remove(struct platform_device *dev)
{ {
struct armflash_info *info = platform_get_drvdata(dev); struct armflash_info *info = platform_get_drvdata(dev);
struct flash_platform_data *plat = dev->dev.platform_data;
int i;
platform_set_drvdata(dev, NULL); platform_set_drvdata(dev, NULL);
if (info) { if (info) {
if (info->mtd) { if (info->mtd) {
del_mtd_partitions(info->mtd); del_mtd_partitions(info->mtd);
map_destroy(info->mtd); #ifdef CONFIG_MTD_CONCAT
if (info->mtd != info->subdev[0].mtd)
mtd_concat_destroy(info->mtd);
#endif
} }
kfree(info->parts); kfree(info->parts);
iounmap(info->map.virt); for (i = info->nr_subdev - 1; i >= 0; i--)
release_resource(info->res); armflash_subdev_remove(&info->subdev[i]);
kfree(info->res);
if (info->plat && info->plat->exit) if (plat && plat->exit)
info->plat->exit(); plat->exit();
kfree(info); kfree(info);
} }

View file

@ -195,42 +195,6 @@ err_out:
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state)
{
struct physmap_flash_info *info = platform_get_drvdata(dev);
int ret = 0;
int i;
for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
if (info->mtd[i]->suspend) {
ret = info->mtd[i]->suspend(info->mtd[i]);
if (ret)
goto fail;
}
return 0;
fail:
for (--i; i >= 0; --i)
if (info->mtd[i]->suspend) {
BUG_ON(!info->mtd[i]->resume);
info->mtd[i]->resume(info->mtd[i]);
}
return ret;
}
static int physmap_flash_resume(struct platform_device *dev)
{
struct physmap_flash_info *info = platform_get_drvdata(dev);
int i;
for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
if (info->mtd[i]->resume)
info->mtd[i]->resume(info->mtd[i]);
return 0;
}
static void physmap_flash_shutdown(struct platform_device *dev) static void physmap_flash_shutdown(struct platform_device *dev)
{ {
struct physmap_flash_info *info = platform_get_drvdata(dev); struct physmap_flash_info *info = platform_get_drvdata(dev);
@ -242,16 +206,12 @@ static void physmap_flash_shutdown(struct platform_device *dev)
info->mtd[i]->resume(info->mtd[i]); info->mtd[i]->resume(info->mtd[i]);
} }
#else #else
#define physmap_flash_suspend NULL
#define physmap_flash_resume NULL
#define physmap_flash_shutdown NULL #define physmap_flash_shutdown NULL
#endif #endif
static struct platform_driver physmap_flash_driver = { static struct platform_driver physmap_flash_driver = {
.probe = physmap_flash_probe, .probe = physmap_flash_probe,
.remove = physmap_flash_remove, .remove = physmap_flash_remove,
.suspend = physmap_flash_suspend,
.resume = physmap_flash_resume,
.shutdown = physmap_flash_shutdown, .shutdown = physmap_flash_shutdown,
.driver = { .driver = {
.name = "physmap-flash", .name = "physmap-flash",

View file

@ -20,16 +20,23 @@
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/map.h> #include <linux/mtd/map.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/mtd/concat.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
struct of_flash_list {
struct mtd_info *mtd;
struct map_info map;
struct resource *res;
};
struct of_flash { struct of_flash {
struct mtd_info *mtd; struct mtd_info *cmtd;
struct map_info map;
struct resource *res;
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
struct mtd_partition *parts; struct mtd_partition *parts;
#endif #endif
int list_size; /* number of elements in of_flash_list */
struct of_flash_list list[0];
}; };
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
@ -88,30 +95,44 @@ static int parse_obsolete_partitions(struct of_device *dev,
static int of_flash_remove(struct of_device *dev) static int of_flash_remove(struct of_device *dev)
{ {
struct of_flash *info; struct of_flash *info;
int i;
info = dev_get_drvdata(&dev->dev); info = dev_get_drvdata(&dev->dev);
if (!info) if (!info)
return 0; return 0;
dev_set_drvdata(&dev->dev, NULL); dev_set_drvdata(&dev->dev, NULL);
if (info->mtd) { #ifdef CONFIG_MTD_CONCAT
if (info->cmtd != info->list[0].mtd) {
del_mtd_device(info->cmtd);
mtd_concat_destroy(info->cmtd);
}
#endif
if (info->cmtd) {
if (OF_FLASH_PARTS(info)) { if (OF_FLASH_PARTS(info)) {
del_mtd_partitions(info->mtd); del_mtd_partitions(info->cmtd);
kfree(OF_FLASH_PARTS(info)); kfree(OF_FLASH_PARTS(info));
} else { } else {
del_mtd_device(info->mtd); del_mtd_device(info->cmtd);
} }
map_destroy(info->mtd);
} }
if (info->map.virt) for (i = 0; i < info->list_size; i++) {
iounmap(info->map.virt); if (info->list[i].mtd)
map_destroy(info->list[i].mtd);
if (info->res) { if (info->list[i].map.virt)
release_resource(info->res); iounmap(info->list[i].map.virt);
kfree(info->res);
if (info->list[i].res) {
release_resource(info->list[i].res);
kfree(info->list[i].res);
}
} }
kfree(info);
return 0; return 0;
} }
@ -164,68 +185,130 @@ static int __devinit of_flash_probe(struct of_device *dev,
const char *probe_type = match->data; const char *probe_type = match->data;
const u32 *width; const u32 *width;
int err; int err;
int i;
int count;
const u32 *p;
int reg_tuple_size;
struct mtd_info **mtd_list = NULL;
err = -ENXIO; reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
if (of_address_to_resource(dp, 0, &res)) {
dev_err(&dev->dev, "Can't get IO address from device tree\n"); /*
* Get number of "reg" tuples. Scan for MTD devices on area's
* described by each "reg" region. This makes it possible (including
* the concat support) to support the Intel P30 48F4400 chips which
* consists internally of 2 non-identical NOR chips on one die.
*/
p = of_get_property(dp, "reg", &count);
if (count % reg_tuple_size != 0) {
dev_err(&dev->dev, "Malformed reg property on %s\n",
dev->node->full_name);
err = -EINVAL;
goto err_out; goto err_out;
} }
count /= reg_tuple_size;
dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
(unsigned long long)res.start, (unsigned long long)res.end);
err = -ENOMEM; err = -ENOMEM;
info = kzalloc(sizeof(*info), GFP_KERNEL); info = kzalloc(sizeof(struct of_flash) +
sizeof(struct of_flash_list) * count, GFP_KERNEL);
if (!info)
goto err_out;
mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL);
if (!info) if (!info)
goto err_out; goto err_out;
dev_set_drvdata(&dev->dev, info); dev_set_drvdata(&dev->dev, info);
err = -EBUSY; for (i = 0; i < count; i++) {
info->res = request_mem_region(res.start, res.end - res.start + 1, err = -ENXIO;
dev_name(&dev->dev)); if (of_address_to_resource(dp, i, &res)) {
if (!info->res) dev_err(&dev->dev, "Can't get IO address from device"
goto err_out; " tree\n");
goto err_out;
}
err = -ENXIO; dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
width = of_get_property(dp, "bank-width", NULL); (unsigned long long)res.start,
if (!width) { (unsigned long long)res.end);
dev_err(&dev->dev, "Can't get bank width from device tree\n");
goto err_out; err = -EBUSY;
info->list[i].res = request_mem_region(res.start, res.end -
res.start + 1,
dev_name(&dev->dev));
if (!info->list[i].res)
goto err_out;
err = -ENXIO;
width = of_get_property(dp, "bank-width", NULL);
if (!width) {
dev_err(&dev->dev, "Can't get bank width from device"
" tree\n");
goto err_out;
}
info->list[i].map.name = dev_name(&dev->dev);
info->list[i].map.phys = res.start;
info->list[i].map.size = res.end - res.start + 1;
info->list[i].map.bankwidth = *width;
err = -ENOMEM;
info->list[i].map.virt = ioremap(info->list[i].map.phys,
info->list[i].map.size);
if (!info->list[i].map.virt) {
dev_err(&dev->dev, "Failed to ioremap() flash"
" region\n");
goto err_out;
}
simple_map_init(&info->list[i].map);
if (probe_type) {
info->list[i].mtd = do_map_probe(probe_type,
&info->list[i].map);
} else {
info->list[i].mtd = obsolete_probe(dev,
&info->list[i].map);
}
mtd_list[i] = info->list[i].mtd;
err = -ENXIO;
if (!info->list[i].mtd) {
dev_err(&dev->dev, "do_map_probe() failed\n");
goto err_out;
} else {
info->list_size++;
}
info->list[i].mtd->owner = THIS_MODULE;
info->list[i].mtd->dev.parent = &dev->dev;
} }
info->map.name = dev_name(&dev->dev); err = 0;
info->map.phys = res.start; if (info->list_size == 1) {
info->map.size = res.end - res.start + 1; info->cmtd = info->list[0].mtd;
info->map.bankwidth = *width; } else if (info->list_size > 1) {
/*
err = -ENOMEM; * We detected multiple devices. Concatenate them together.
info->map.virt = ioremap(info->map.phys, info->map.size); */
if (!info->map.virt) { #ifdef CONFIG_MTD_CONCAT
dev_err(&dev->dev, "Failed to ioremap() flash region\n"); info->cmtd = mtd_concat_create(mtd_list, info->list_size,
goto err_out; dev_name(&dev->dev));
if (info->cmtd == NULL)
err = -ENXIO;
#else
printk(KERN_ERR "physmap_of: multiple devices "
"found but MTD concat support disabled.\n");
err = -ENXIO;
#endif
} }
if (err)
simple_map_init(&info->map);
if (probe_type)
info->mtd = do_map_probe(probe_type, &info->map);
else
info->mtd = obsolete_probe(dev, &info->map);
err = -ENXIO;
if (!info->mtd) {
dev_err(&dev->dev, "do_map_probe() failed\n");
goto err_out; goto err_out;
}
info->mtd->owner = THIS_MODULE;
info->mtd->dev.parent = &dev->dev;
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
/* First look for RedBoot table or partitions on the command /* First look for RedBoot table or partitions on the command
* line, these take precedence over device tree information */ * line, these take precedence over device tree information */
err = parse_mtd_partitions(info->mtd, part_probe_types, err = parse_mtd_partitions(info->cmtd, part_probe_types,
&info->parts, 0); &info->parts, 0);
if (err < 0) if (err < 0)
return err; return err;
@ -244,15 +327,19 @@ static int __devinit of_flash_probe(struct of_device *dev,
} }
if (err > 0) if (err > 0)
add_mtd_partitions(info->mtd, info->parts, err); add_mtd_partitions(info->cmtd, info->parts, err);
else else
#endif #endif
add_mtd_device(info->mtd); add_mtd_device(info->cmtd);
kfree(mtd_list);
return 0; return 0;
err_out: err_out:
kfree(mtd_list);
of_flash_remove(dev); of_flash_remove(dev);
return err; return err;
} }

View file

@ -1,104 +0,0 @@
/*
* Mapping of the rootfs in a physical region of memory
*
* Copyright (C) 2005-2007 PMC-Sierra Inc.
* Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.com
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/root_dev.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <asm/io.h>
#include <msp_prom.h>
static struct mtd_info *rr_mtd;
struct map_info rr_map = {
.name = "ramroot",
.bankwidth = 4,
};
static int __init init_rrmap(void)
{
void *ramroot_start;
unsigned long ramroot_size;
/* Check for supported rootfs types */
if (get_ramroot(&ramroot_start, &ramroot_size)) {
rr_map.phys = CPHYSADDR(ramroot_start);
rr_map.size = ramroot_size;
printk(KERN_NOTICE
"PMC embedded root device: 0x%08lx @ 0x%08lx\n",
rr_map.size, (unsigned long)rr_map.phys);
} else {
printk(KERN_ERR
"init_rrmap: no supported embedded rootfs detected!\n");
return -ENXIO;
}
/* Map rootfs to I/O space for block device driver */
rr_map.virt = ioremap(rr_map.phys, rr_map.size);
if (!rr_map.virt) {
printk(KERN_ERR "Failed to ioremap\n");
return -EIO;
}
simple_map_init(&rr_map);
rr_mtd = do_map_probe("map_ram", &rr_map);
if (rr_mtd) {
rr_mtd->owner = THIS_MODULE;
add_mtd_device(rr_mtd);
return 0;
}
iounmap(rr_map.virt);
return -ENXIO;
}
static void __exit cleanup_rrmap(void)
{
del_mtd_device(rr_mtd);
map_destroy(rr_mtd);
iounmap(rr_map.virt);
rr_map.virt = NULL;
}
MODULE_AUTHOR("PMC-Sierra, Inc");
MODULE_DESCRIPTION("MTD map driver for embedded PMC-Sierra MSP filesystem");
MODULE_LICENSE("GPL");
module_init(init_rrmap);
module_exit(cleanup_rrmap);

View file

@ -140,24 +140,6 @@ static int __devexit pxa2xx_flash_remove(struct platform_device *dev)
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int pxa2xx_flash_suspend(struct platform_device *dev, pm_message_t state)
{
struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
int ret = 0;
if (info->mtd && info->mtd->suspend)
ret = info->mtd->suspend(info->mtd);
return ret;
}
static int pxa2xx_flash_resume(struct platform_device *dev)
{
struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
if (info->mtd && info->mtd->resume)
info->mtd->resume(info->mtd);
return 0;
}
static void pxa2xx_flash_shutdown(struct platform_device *dev) static void pxa2xx_flash_shutdown(struct platform_device *dev)
{ {
struct pxa2xx_flash_info *info = platform_get_drvdata(dev); struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
@ -166,8 +148,6 @@ static void pxa2xx_flash_shutdown(struct platform_device *dev)
info->mtd->resume(info->mtd); info->mtd->resume(info->mtd);
} }
#else #else
#define pxa2xx_flash_suspend NULL
#define pxa2xx_flash_resume NULL
#define pxa2xx_flash_shutdown NULL #define pxa2xx_flash_shutdown NULL
#endif #endif
@ -178,8 +158,6 @@ static struct platform_driver pxa2xx_flash_driver = {
}, },
.probe = pxa2xx_flash_probe, .probe = pxa2xx_flash_probe,
.remove = __devexit_p(pxa2xx_flash_remove), .remove = __devexit_p(pxa2xx_flash_remove),
.suspend = pxa2xx_flash_suspend,
.resume = pxa2xx_flash_resume,
.shutdown = pxa2xx_flash_shutdown, .shutdown = pxa2xx_flash_shutdown,
}; };

View file

@ -145,25 +145,6 @@ err_out:
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int rbtx4939_flash_suspend(struct platform_device *dev,
pm_message_t state)
{
struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
if (info->mtd->suspend)
return info->mtd->suspend(info->mtd);
return 0;
}
static int rbtx4939_flash_resume(struct platform_device *dev)
{
struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
if (info->mtd->resume)
info->mtd->resume(info->mtd);
return 0;
}
static void rbtx4939_flash_shutdown(struct platform_device *dev) static void rbtx4939_flash_shutdown(struct platform_device *dev)
{ {
struct rbtx4939_flash_info *info = platform_get_drvdata(dev); struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
@ -173,16 +154,12 @@ static void rbtx4939_flash_shutdown(struct platform_device *dev)
info->mtd->resume(info->mtd); info->mtd->resume(info->mtd);
} }
#else #else
#define rbtx4939_flash_suspend NULL
#define rbtx4939_flash_resume NULL
#define rbtx4939_flash_shutdown NULL #define rbtx4939_flash_shutdown NULL
#endif #endif
static struct platform_driver rbtx4939_flash_driver = { static struct platform_driver rbtx4939_flash_driver = {
.probe = rbtx4939_flash_probe, .probe = rbtx4939_flash_probe,
.remove = rbtx4939_flash_remove, .remove = rbtx4939_flash_remove,
.suspend = rbtx4939_flash_suspend,
.resume = rbtx4939_flash_resume,
.shutdown = rbtx4939_flash_shutdown, .shutdown = rbtx4939_flash_shutdown,
.driver = { .driver = {
.name = "rbtx4939-flash", .name = "rbtx4939-flash",

View file

@ -415,25 +415,6 @@ static int __exit sa1100_mtd_remove(struct platform_device *pdev)
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int sa1100_mtd_suspend(struct platform_device *dev, pm_message_t state)
{
struct sa_info *info = platform_get_drvdata(dev);
int ret = 0;
if (info)
ret = info->mtd->suspend(info->mtd);
return ret;
}
static int sa1100_mtd_resume(struct platform_device *dev)
{
struct sa_info *info = platform_get_drvdata(dev);
if (info)
info->mtd->resume(info->mtd);
return 0;
}
static void sa1100_mtd_shutdown(struct platform_device *dev) static void sa1100_mtd_shutdown(struct platform_device *dev)
{ {
struct sa_info *info = platform_get_drvdata(dev); struct sa_info *info = platform_get_drvdata(dev);
@ -441,16 +422,12 @@ static void sa1100_mtd_shutdown(struct platform_device *dev)
info->mtd->resume(info->mtd); info->mtd->resume(info->mtd);
} }
#else #else
#define sa1100_mtd_suspend NULL
#define sa1100_mtd_resume NULL
#define sa1100_mtd_shutdown NULL #define sa1100_mtd_shutdown NULL
#endif #endif
static struct platform_driver sa1100_mtd_driver = { static struct platform_driver sa1100_mtd_driver = {
.probe = sa1100_mtd_probe, .probe = sa1100_mtd_probe,
.remove = __exit_p(sa1100_mtd_remove), .remove = __exit_p(sa1100_mtd_remove),
.suspend = sa1100_mtd_suspend,
.resume = sa1100_mtd_resume,
.shutdown = sa1100_mtd_shutdown, .shutdown = sa1100_mtd_shutdown,
.driver = { .driver = {
.name = "sa1100-mtd", .name = "sa1100-mtd",

View file

@ -22,15 +22,19 @@
/****************************************************************************/ /****************************************************************************/
extern char _ebss;
struct map_info uclinux_ram_map = { struct map_info uclinux_ram_map = {
.name = "RAM", .name = "RAM",
.phys = (unsigned long)&_ebss,
.size = 0,
}; };
struct mtd_info *uclinux_ram_mtdinfo; static struct mtd_info *uclinux_ram_mtdinfo;
/****************************************************************************/ /****************************************************************************/
struct mtd_partition uclinux_romfs[] = { static struct mtd_partition uclinux_romfs[] = {
{ .name = "ROMfs" } { .name = "ROMfs" }
}; };
@ -38,7 +42,7 @@ struct mtd_partition uclinux_romfs[] = {
/****************************************************************************/ /****************************************************************************/
int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, static int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys) size_t *retlen, void **virt, resource_size_t *phys)
{ {
struct map_info *map = mtd->priv; struct map_info *map = mtd->priv;
@ -55,12 +59,10 @@ static int __init uclinux_mtd_init(void)
{ {
struct mtd_info *mtd; struct mtd_info *mtd;
struct map_info *mapp; struct map_info *mapp;
extern char _ebss;
unsigned long addr = (unsigned long) &_ebss;
mapp = &uclinux_ram_map; mapp = &uclinux_ram_map;
mapp->phys = addr; if (!mapp->size)
mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(addr + 8)))); mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8))));
mapp->bankwidth = 4; mapp->bankwidth = 4;
printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n", printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n",

View file

@ -291,7 +291,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
gd->private_data = new; gd->private_data = new;
new->blkcore_priv = gd; new->blkcore_priv = gd;
gd->queue = tr->blkcore_priv->rq; gd->queue = tr->blkcore_priv->rq;
gd->driverfs_dev = new->mtd->dev.parent; gd->driverfs_dev = &new->mtd->dev;
if (new->readonly) if (new->readonly)
set_disk_ro(gd, 1); set_disk_ro(gd, 1);

View file

@ -14,6 +14,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/compat.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/compatmac.h> #include <linux/mtd/compatmac.h>
@ -355,6 +356,100 @@ static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
# define otp_select_filemode(f,m) -EOPNOTSUPP # define otp_select_filemode(f,m) -EOPNOTSUPP
#endif #endif
static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd,
uint64_t start, uint32_t length, void __user *ptr,
uint32_t __user *retp)
{
struct mtd_oob_ops ops;
uint32_t retlen;
int ret = 0;
if (!(file->f_mode & FMODE_WRITE))
return -EPERM;
if (length > 4096)
return -EINVAL;
if (!mtd->write_oob)
ret = -EOPNOTSUPP;
else
ret = access_ok(VERIFY_READ, ptr, length) ? 0 : EFAULT;
if (ret)
return ret;
ops.ooblen = length;
ops.ooboffs = start & (mtd->oobsize - 1);
ops.datbuf = NULL;
ops.mode = MTD_OOB_PLACE;
if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
return -EINVAL;
ops.oobbuf = kmalloc(length, GFP_KERNEL);
if (!ops.oobbuf)
return -ENOMEM;
if (copy_from_user(ops.oobbuf, ptr, length)) {
kfree(ops.oobbuf);
return -EFAULT;
}
start &= ~((uint64_t)mtd->oobsize - 1);
ret = mtd->write_oob(mtd, start, &ops);
if (ops.oobretlen > 0xFFFFFFFFU)
ret = -EOVERFLOW;
retlen = ops.oobretlen;
if (copy_to_user(retp, &retlen, sizeof(length)))
ret = -EFAULT;
kfree(ops.oobbuf);
return ret;
}
static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,
uint32_t length, void __user *ptr, uint32_t __user *retp)
{
struct mtd_oob_ops ops;
int ret = 0;
if (length > 4096)
return -EINVAL;
if (!mtd->read_oob)
ret = -EOPNOTSUPP;
else
ret = access_ok(VERIFY_WRITE, ptr,
length) ? 0 : -EFAULT;
if (ret)
return ret;
ops.ooblen = length;
ops.ooboffs = start & (mtd->oobsize - 1);
ops.datbuf = NULL;
ops.mode = MTD_OOB_PLACE;
if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
return -EINVAL;
ops.oobbuf = kmalloc(length, GFP_KERNEL);
if (!ops.oobbuf)
return -ENOMEM;
start &= ~((uint64_t)mtd->oobsize - 1);
ret = mtd->read_oob(mtd, start, &ops);
if (put_user(ops.oobretlen, retp))
ret = -EFAULT;
else if (ops.oobretlen && copy_to_user(ptr, ops.oobbuf,
ops.oobretlen))
ret = -EFAULT;
kfree(ops.oobbuf);
return ret;
}
static int mtd_ioctl(struct inode *inode, struct file *file, static int mtd_ioctl(struct inode *inode, struct file *file,
u_int cmd, u_long arg) u_int cmd, u_long arg)
{ {
@ -417,6 +512,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
break; break;
case MEMERASE: case MEMERASE:
case MEMERASE64:
{ {
struct erase_info *erase; struct erase_info *erase;
@ -427,20 +523,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
if (!erase) if (!erase)
ret = -ENOMEM; ret = -ENOMEM;
else { else {
struct erase_info_user einfo;
wait_queue_head_t waitq; wait_queue_head_t waitq;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
init_waitqueue_head(&waitq); init_waitqueue_head(&waitq);
if (copy_from_user(&einfo, argp, if (cmd == MEMERASE64) {
sizeof(struct erase_info_user))) { struct erase_info_user64 einfo64;
kfree(erase);
return -EFAULT; if (copy_from_user(&einfo64, argp,
sizeof(struct erase_info_user64))) {
kfree(erase);
return -EFAULT;
}
erase->addr = einfo64.start;
erase->len = einfo64.length;
} else {
struct erase_info_user einfo32;
if (copy_from_user(&einfo32, argp,
sizeof(struct erase_info_user))) {
kfree(erase);
return -EFAULT;
}
erase->addr = einfo32.start;
erase->len = einfo32.length;
} }
erase->addr = einfo.start;
erase->len = einfo.length;
erase->mtd = mtd; erase->mtd = mtd;
erase->callback = mtdchar_erase_callback; erase->callback = mtdchar_erase_callback;
erase->priv = (unsigned long)&waitq; erase->priv = (unsigned long)&waitq;
@ -474,100 +582,56 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
case MEMWRITEOOB: case MEMWRITEOOB:
{ {
struct mtd_oob_buf buf; struct mtd_oob_buf buf;
struct mtd_oob_ops ops; struct mtd_oob_buf __user *buf_user = argp;
struct mtd_oob_buf __user *user_buf = argp;
uint32_t retlen;
if(!(file->f_mode & FMODE_WRITE)) /* NOTE: writes return length to buf_user->length */
return -EPERM; if (copy_from_user(&buf, argp, sizeof(buf)))
if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
return -EFAULT;
if (buf.length > 4096)
return -EINVAL;
if (!mtd->write_oob)
ret = -EOPNOTSUPP;
else
ret = access_ok(VERIFY_READ, buf.ptr,
buf.length) ? 0 : EFAULT;
if (ret)
return ret;
ops.ooblen = buf.length;
ops.ooboffs = buf.start & (mtd->oobsize - 1);
ops.datbuf = NULL;
ops.mode = MTD_OOB_PLACE;
if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
return -EINVAL;
ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
if (!ops.oobbuf)
return -ENOMEM;
if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) {
kfree(ops.oobbuf);
return -EFAULT;
}
buf.start &= ~(mtd->oobsize - 1);
ret = mtd->write_oob(mtd, buf.start, &ops);
if (ops.oobretlen > 0xFFFFFFFFU)
ret = -EOVERFLOW;
retlen = ops.oobretlen;
if (copy_to_user(&user_buf->length, &retlen, sizeof(buf.length)))
ret = -EFAULT; ret = -EFAULT;
else
kfree(ops.oobbuf); ret = mtd_do_writeoob(file, mtd, buf.start, buf.length,
buf.ptr, &buf_user->length);
break; break;
} }
case MEMREADOOB: case MEMREADOOB:
{ {
struct mtd_oob_buf buf; struct mtd_oob_buf buf;
struct mtd_oob_ops ops; struct mtd_oob_buf __user *buf_user = argp;
if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) /* NOTE: writes return length to buf_user->start */
return -EFAULT; if (copy_from_user(&buf, argp, sizeof(buf)))
ret = -EFAULT;
if (buf.length > 4096)
return -EINVAL;
if (!mtd->read_oob)
ret = -EOPNOTSUPP;
else else
ret = access_ok(VERIFY_WRITE, buf.ptr, ret = mtd_do_readoob(mtd, buf.start, buf.length,
buf.length) ? 0 : -EFAULT; buf.ptr, &buf_user->start);
if (ret) break;
return ret; }
ops.ooblen = buf.length; case MEMWRITEOOB64:
ops.ooboffs = buf.start & (mtd->oobsize - 1); {
ops.datbuf = NULL; struct mtd_oob_buf64 buf;
ops.mode = MTD_OOB_PLACE; struct mtd_oob_buf64 __user *buf_user = argp;
if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) if (copy_from_user(&buf, argp, sizeof(buf)))
return -EINVAL;
ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
if (!ops.oobbuf)
return -ENOMEM;
buf.start &= ~(mtd->oobsize - 1);
ret = mtd->read_oob(mtd, buf.start, &ops);
if (put_user(ops.oobretlen, (uint32_t __user *)argp))
ret = -EFAULT;
else if (ops.oobretlen && copy_to_user(buf.ptr, ops.oobbuf,
ops.oobretlen))
ret = -EFAULT; ret = -EFAULT;
else
ret = mtd_do_writeoob(file, mtd, buf.start, buf.length,
(void __user *)(uintptr_t)buf.usr_ptr,
&buf_user->length);
break;
}
kfree(ops.oobbuf); case MEMREADOOB64:
{
struct mtd_oob_buf64 buf;
struct mtd_oob_buf64 __user *buf_user = argp;
if (copy_from_user(&buf, argp, sizeof(buf)))
ret = -EFAULT;
else
ret = mtd_do_readoob(mtd, buf.start, buf.length,
(void __user *)(uintptr_t)buf.usr_ptr,
&buf_user->length);
break; break;
} }
@ -758,6 +822,68 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
return ret; return ret;
} /* memory_ioctl */ } /* memory_ioctl */
#ifdef CONFIG_COMPAT
struct mtd_oob_buf32 {
u_int32_t start;
u_int32_t length;
compat_caddr_t ptr; /* unsigned char* */
};
#define MEMWRITEOOB32 _IOWR('M', 3, struct mtd_oob_buf32)
#define MEMREADOOB32 _IOWR('M', 4, struct mtd_oob_buf32)
static long mtd_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
void __user *argp = compat_ptr(arg);
int ret = 0;
lock_kernel();
switch (cmd) {
case MEMWRITEOOB32:
{
struct mtd_oob_buf32 buf;
struct mtd_oob_buf32 __user *buf_user = argp;
if (copy_from_user(&buf, argp, sizeof(buf)))
ret = -EFAULT;
else
ret = mtd_do_writeoob(file, mtd, buf.start,
buf.length, compat_ptr(buf.ptr),
&buf_user->length);
break;
}
case MEMREADOOB32:
{
struct mtd_oob_buf32 buf;
struct mtd_oob_buf32 __user *buf_user = argp;
/* NOTE: writes return length to buf->start */
if (copy_from_user(&buf, argp, sizeof(buf)))
ret = -EFAULT;
else
ret = mtd_do_readoob(mtd, buf.start,
buf.length, compat_ptr(buf.ptr),
&buf_user->start);
break;
}
default:
ret = mtd_ioctl(inode, file, cmd, (unsigned long)argp);
}
unlock_kernel();
return ret;
}
#endif /* CONFIG_COMPAT */
/* /*
* try to determine where a shared mapping can be made * try to determine where a shared mapping can be made
* - only supported for NOMMU at the moment (MMU can't doesn't copy private * - only supported for NOMMU at the moment (MMU can't doesn't copy private
@ -817,6 +943,9 @@ static const struct file_operations mtd_fops = {
.read = mtd_read, .read = mtd_read,
.write = mtd_write, .write = mtd_write,
.ioctl = mtd_ioctl, .ioctl = mtd_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = mtd_compat_ioctl,
#endif
.open = mtd_open, .open = mtd_open,
.release = mtd_close, .release = mtd_close,
.mmap = mtd_mmap, .mmap = mtd_mmap,

View file

@ -23,8 +23,15 @@
#include "mtdcore.h" #include "mtdcore.h"
static int mtd_cls_suspend(struct device *dev, pm_message_t state);
static int mtd_cls_resume(struct device *dev);
static struct class *mtd_class; static struct class mtd_class = {
.name = "mtd",
.owner = THIS_MODULE,
.suspend = mtd_cls_suspend,
.resume = mtd_cls_resume,
};
/* These are exported solely for the purpose of mtd_blkdevs.c. You /* These are exported solely for the purpose of mtd_blkdevs.c. You
should not use them for _anything_ else */ should not use them for _anything_ else */
@ -52,7 +59,26 @@ static void mtd_release(struct device *dev)
/* remove /dev/mtdXro node if needed */ /* remove /dev/mtdXro node if needed */
if (index) if (index)
device_destroy(mtd_class, index + 1); device_destroy(&mtd_class, index + 1);
}
static int mtd_cls_suspend(struct device *dev, pm_message_t state)
{
struct mtd_info *mtd = dev_to_mtd(dev);
if (mtd->suspend)
return mtd->suspend(mtd);
else
return 0;
}
static int mtd_cls_resume(struct device *dev)
{
struct mtd_info *mtd = dev_to_mtd(dev);
if (mtd->resume)
mtd->resume(mtd);
return 0;
} }
static ssize_t mtd_type_show(struct device *dev, static ssize_t mtd_type_show(struct device *dev,
@ -269,7 +295,7 @@ int add_mtd_device(struct mtd_info *mtd)
* physical device. * physical device.
*/ */
mtd->dev.type = &mtd_devtype; mtd->dev.type = &mtd_devtype;
mtd->dev.class = mtd_class; mtd->dev.class = &mtd_class;
mtd->dev.devt = MTD_DEVT(i); mtd->dev.devt = MTD_DEVT(i);
dev_set_name(&mtd->dev, "mtd%d", i); dev_set_name(&mtd->dev, "mtd%d", i);
if (device_register(&mtd->dev) != 0) { if (device_register(&mtd->dev) != 0) {
@ -278,7 +304,7 @@ int add_mtd_device(struct mtd_info *mtd)
} }
if (MTD_DEVT(i)) if (MTD_DEVT(i))
device_create(mtd_class, mtd->dev.parent, device_create(&mtd_class, mtd->dev.parent,
MTD_DEVT(i) + 1, MTD_DEVT(i) + 1,
NULL, "mtd%dro", i); NULL, "mtd%dro", i);
@ -604,11 +630,12 @@ done:
static int __init init_mtd(void) static int __init init_mtd(void)
{ {
mtd_class = class_create(THIS_MODULE, "mtd"); int ret;
ret = class_register(&mtd_class);
if (IS_ERR(mtd_class)) { if (ret) {
pr_err("Error creating mtd class.\n"); pr_err("Error registering mtd class: %d\n", ret);
return PTR_ERR(mtd_class); return ret;
} }
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) if ((proc_mtd = create_proc_entry( "mtd", 0, NULL )))
@ -623,7 +650,7 @@ static void __exit cleanup_mtd(void)
if (proc_mtd) if (proc_mtd)
remove_proc_entry( "mtd", NULL); remove_proc_entry( "mtd", NULL);
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */
class_destroy(mtd_class); class_unregister(&mtd_class);
} }
module_init(init_mtd); module_init(init_mtd);

View file

@ -27,9 +27,7 @@ struct mtd_part {
struct mtd_info mtd; struct mtd_info mtd;
struct mtd_info *master; struct mtd_info *master;
uint64_t offset; uint64_t offset;
int index;
struct list_head list; struct list_head list;
int registered;
}; };
/* /*
@ -321,8 +319,7 @@ int del_mtd_partitions(struct mtd_info *master)
list_for_each_entry_safe(slave, next, &mtd_partitions, list) list_for_each_entry_safe(slave, next, &mtd_partitions, list)
if (slave->master == master) { if (slave->master == master) {
list_del(&slave->list); list_del(&slave->list);
if (slave->registered) del_mtd_device(&slave->mtd);
del_mtd_device(&slave->mtd);
kfree(slave); kfree(slave);
} }
@ -395,7 +392,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
slave->mtd.get_fact_prot_info = part_get_fact_prot_info; slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
if (master->sync) if (master->sync)
slave->mtd.sync = part_sync; slave->mtd.sync = part_sync;
if (!partno && master->suspend && master->resume) { if (!partno && !master->dev.class && master->suspend && master->resume) {
slave->mtd.suspend = part_suspend; slave->mtd.suspend = part_suspend;
slave->mtd.resume = part_resume; slave->mtd.resume = part_resume;
} }
@ -412,7 +409,6 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
slave->mtd.erase = part_erase; slave->mtd.erase = part_erase;
slave->master = master; slave->master = master;
slave->offset = part->offset; slave->offset = part->offset;
slave->index = partno;
if (slave->offset == MTDPART_OFS_APPEND) if (slave->offset == MTDPART_OFS_APPEND)
slave->offset = cur_offset; slave->offset = cur_offset;
@ -500,15 +496,9 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
} }
out_register: out_register:
if (part->mtdp) { /* register our partition */
/* store the object pointer (caller may or may not register it*/ add_mtd_device(&slave->mtd);
*part->mtdp = &slave->mtd;
slave->registered = 0;
} else {
/* register our partition */
add_mtd_device(&slave->mtd);
slave->registered = 1;
}
return slave; return slave;
} }

View file

@ -74,6 +74,12 @@ config MTD_NAND_AMS_DELTA
help help
Support for NAND flash on Amstrad E3 (Delta). Support for NAND flash on Amstrad E3 (Delta).
config MTD_NAND_OMAP2
tristate "NAND Flash device on OMAP2 and OMAP3"
depends on ARM && MTD_NAND && (ARCH_OMAP2 || ARCH_OMAP3)
help
Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms.
config MTD_NAND_TS7250 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
@ -139,27 +145,27 @@ config MTD_NAND_PPCHAMELEONEVB
This enables the NAND flash driver on the PPChameleon EVB Board. This enables the NAND flash driver on the PPChameleon EVB Board.
config MTD_NAND_S3C2410 config MTD_NAND_S3C2410
tristate "NAND Flash support for S3C2410/S3C2440 SoC" tristate "NAND Flash support for Samsung S3C SoCs"
depends on ARCH_S3C2410 depends on ARCH_S3C2410 || ARCH_S3C64XX
help help
This enables the NAND flash controller on the S3C2410 and S3C2440 This enables the NAND flash controller on the S3C24xx and S3C64xx
SoCs SoCs
No board specific support is done by this driver, each board No board specific support is done by this driver, each board
must advertise a platform_device for the driver to attach. must advertise a platform_device for the driver to attach.
config MTD_NAND_S3C2410_DEBUG config MTD_NAND_S3C2410_DEBUG
bool "S3C2410 NAND driver debug" bool "Samsung S3C NAND driver debug"
depends on MTD_NAND_S3C2410 depends on MTD_NAND_S3C2410
help help
Enable debugging of the S3C2410 NAND driver Enable debugging of the S3C NAND driver
config MTD_NAND_S3C2410_HWECC config MTD_NAND_S3C2410_HWECC
bool "S3C2410 NAND Hardware ECC" bool "Samsung S3C NAND Hardware ECC"
depends on MTD_NAND_S3C2410 depends on MTD_NAND_S3C2410
help help
Enable the use of the S3C2410's internal ECC generator when Enable the use of the controller's internal ECC generator when
using NAND. Early versions of the chip have had problems with using NAND. Early versions of the chips have had problems with
incorrect ECC generation, and if using these, the default of incorrect ECC generation, and if using these, the default of
software ECC is preferable. software ECC is preferable.
@ -171,7 +177,7 @@ config MTD_NAND_NDFC
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
config MTD_NAND_S3C2410_CLKSTOP config MTD_NAND_S3C2410_CLKSTOP
bool "S3C2410 NAND IDLE clock stop" bool "Samsung S3C NAND IDLE clock stop"
depends on MTD_NAND_S3C2410 depends on MTD_NAND_S3C2410
default n default n
help help

View file

@ -25,6 +25,7 @@ obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o
obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o
obj-$(CONFIG_MTD_NAND_OMAP2) += omap2.o
obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o

View file

@ -24,6 +24,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h> #include <linux/mtd/nand.h>
@ -47,6 +48,9 @@
#define no_ecc 0 #define no_ecc 0
#endif #endif
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
/* Register access macros */ /* Register access macros */
#define ecc_readl(add, reg) \ #define ecc_readl(add, reg) \
__raw_readl(add + ATMEL_ECC_##reg) __raw_readl(add + ATMEL_ECC_##reg)
@ -459,12 +463,17 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
if (host->board->det_pin) { if (host->board->det_pin) {
if (gpio_get_value(host->board->det_pin)) { if (gpio_get_value(host->board->det_pin)) {
printk("No SmartMedia card inserted.\n"); printk(KERN_INFO "No SmartMedia card inserted.\n");
res = ENXIO; res = ENXIO;
goto err_no_card; goto err_no_card;
} }
} }
if (on_flash_bbt) {
printk(KERN_INFO "atmel_nand: Use On Flash BBT\n");
nand_chip->options |= NAND_USE_FLASH_BBT;
}
/* first scan to find the device and get the page size */ /* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1)) { if (nand_scan_ident(mtd, 1)) {
res = -ENXIO; res = -ENXIO;

View file

@ -458,7 +458,7 @@ static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int bf5xx_nand_dma_rw(struct mtd_info *mtd, static void bf5xx_nand_dma_rw(struct mtd_info *mtd,
uint8_t *buf, int is_read) uint8_t *buf, int is_read)
{ {
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
@ -496,11 +496,20 @@ static int bf5xx_nand_dma_rw(struct mtd_info *mtd,
/* setup DMA register with Blackfin DMA API */ /* setup DMA register with Blackfin DMA API */
set_dma_config(CH_NFC, 0x0); set_dma_config(CH_NFC, 0x0);
set_dma_start_addr(CH_NFC, (unsigned long) buf); set_dma_start_addr(CH_NFC, (unsigned long) buf);
/* The DMAs have different size on BF52x and BF54x */
#ifdef CONFIG_BF52x
set_dma_x_count(CH_NFC, (page_size >> 1));
set_dma_x_modify(CH_NFC, 2);
val = DI_EN | WDSIZE_16;
#endif
#ifdef CONFIG_BF54x
set_dma_x_count(CH_NFC, (page_size >> 2)); set_dma_x_count(CH_NFC, (page_size >> 2));
set_dma_x_modify(CH_NFC, 4); set_dma_x_modify(CH_NFC, 4);
/* setup write or read operation */
val = DI_EN | WDSIZE_32; val = DI_EN | WDSIZE_32;
#endif
/* setup write or read operation */
if (is_read) if (is_read)
val |= WNR; val |= WNR;
set_dma_config(CH_NFC, val); set_dma_config(CH_NFC, val);
@ -512,8 +521,6 @@ static int bf5xx_nand_dma_rw(struct mtd_info *mtd,
else else
bfin_write_NFC_PGCTL(0x2); bfin_write_NFC_PGCTL(0x2);
wait_for_completion(&info->dma_completion); wait_for_completion(&info->dma_completion);
return 0;
} }
static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd, static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd,

View file

@ -44,7 +44,7 @@
* and some flavors of secondary chipselect (e.g. based on A12) as used * and some flavors of secondary chipselect (e.g. based on A12) as used
* with multichip packages. * with multichip packages.
* *
* The 1-bit ECC hardware is supported, but not yet the newer 4-bit ECC * The 1-bit ECC hardware is supported, as well as the newer 4-bit ECC
* available on chips like the DM355 and OMAP-L137 and needed with the * available on chips like the DM355 and OMAP-L137 and needed with the
* more error-prone MLC NAND chips. * more error-prone MLC NAND chips.
* *
@ -54,11 +54,14 @@
struct davinci_nand_info { struct davinci_nand_info {
struct mtd_info mtd; struct mtd_info mtd;
struct nand_chip chip; struct nand_chip chip;
struct nand_ecclayout ecclayout;
struct device *dev; struct device *dev;
struct clk *clk; struct clk *clk;
bool partitioned; bool partitioned;
bool is_readmode;
void __iomem *base; void __iomem *base;
void __iomem *vaddr; void __iomem *vaddr;
@ -73,6 +76,7 @@ struct davinci_nand_info {
}; };
static DEFINE_SPINLOCK(davinci_nand_lock); static DEFINE_SPINLOCK(davinci_nand_lock);
static bool ecc4_busy;
#define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd) #define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd)
@ -217,6 +221,192 @@ static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat,
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
/*
* 4-bit hardware ECC ... context maintained over entire AEMIF
*
* This is a syndrome engine, but we avoid NAND_ECC_HW_SYNDROME
* since that forces use of a problematic "infix OOB" layout.
* Among other things, it trashes manufacturer bad block markers.
* Also, and specific to this hardware, it ECC-protects the "prepad"
* in the OOB ... while having ECC protection for parts of OOB would
* seem useful, the current MTD stack sometimes wants to update the
* OOB without recomputing ECC.
*/
static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode)
{
struct davinci_nand_info *info = to_davinci_nand(mtd);
unsigned long flags;
u32 val;
spin_lock_irqsave(&davinci_nand_lock, flags);
/* Start 4-bit ECC calculation for read/write */
val = davinci_nand_readl(info, NANDFCR_OFFSET);
val &= ~(0x03 << 4);
val |= (info->core_chipsel << 4) | BIT(12);
davinci_nand_writel(info, NANDFCR_OFFSET, val);
info->is_readmode = (mode == NAND_ECC_READ);
spin_unlock_irqrestore(&davinci_nand_lock, flags);
}
/* Read raw ECC code after writing to NAND. */
static void
nand_davinci_readecc_4bit(struct davinci_nand_info *info, u32 code[4])
{
const u32 mask = 0x03ff03ff;
code[0] = davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET) & mask;
code[1] = davinci_nand_readl(info, NAND_4BIT_ECC2_OFFSET) & mask;
code[2] = davinci_nand_readl(info, NAND_4BIT_ECC3_OFFSET) & mask;
code[3] = davinci_nand_readl(info, NAND_4BIT_ECC4_OFFSET) & mask;
}
/* Terminate read ECC; or return ECC (as bytes) of data written to NAND. */
static int nand_davinci_calculate_4bit(struct mtd_info *mtd,
const u_char *dat, u_char *ecc_code)
{
struct davinci_nand_info *info = to_davinci_nand(mtd);
u32 raw_ecc[4], *p;
unsigned i;
/* After a read, terminate ECC calculation by a dummy read
* of some 4-bit ECC register. ECC covers everything that
* was read; correct() just uses the hardware state, so
* ecc_code is not needed.
*/
if (info->is_readmode) {
davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET);
return 0;
}
/* Pack eight raw 10-bit ecc values into ten bytes, making
* two passes which each convert four values (in upper and
* lower halves of two 32-bit words) into five bytes. The
* ROM boot loader uses this same packing scheme.
*/
nand_davinci_readecc_4bit(info, raw_ecc);
for (i = 0, p = raw_ecc; i < 2; i++, p += 2) {
*ecc_code++ = p[0] & 0xff;
*ecc_code++ = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc);
*ecc_code++ = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0);
*ecc_code++ = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0);
*ecc_code++ = (p[1] >> 18) & 0xff;
}
return 0;
}
/* Correct up to 4 bits in data we just read, using state left in the
* hardware plus the ecc_code computed when it was first written.
*/
static int nand_davinci_correct_4bit(struct mtd_info *mtd,
u_char *data, u_char *ecc_code, u_char *null)
{
int i;
struct davinci_nand_info *info = to_davinci_nand(mtd);
unsigned short ecc10[8];
unsigned short *ecc16;
u32 syndrome[4];
unsigned num_errors, corrected;
/* All bytes 0xff? It's an erased page; ignore its ECC. */
for (i = 0; i < 10; i++) {
if (ecc_code[i] != 0xff)
goto compare;
}
return 0;
compare:
/* Unpack ten bytes into eight 10 bit values. We know we're
* little-endian, and use type punning for less shifting/masking.
*/
if (WARN_ON(0x01 & (unsigned) ecc_code))
return -EINVAL;
ecc16 = (unsigned short *)ecc_code;
ecc10[0] = (ecc16[0] >> 0) & 0x3ff;
ecc10[1] = ((ecc16[0] >> 10) & 0x3f) | ((ecc16[1] << 6) & 0x3c0);
ecc10[2] = (ecc16[1] >> 4) & 0x3ff;
ecc10[3] = ((ecc16[1] >> 14) & 0x3) | ((ecc16[2] << 2) & 0x3fc);
ecc10[4] = (ecc16[2] >> 8) | ((ecc16[3] << 8) & 0x300);
ecc10[5] = (ecc16[3] >> 2) & 0x3ff;
ecc10[6] = ((ecc16[3] >> 12) & 0xf) | ((ecc16[4] << 4) & 0x3f0);
ecc10[7] = (ecc16[4] >> 6) & 0x3ff;
/* Tell ECC controller about the expected ECC codes. */
for (i = 7; i >= 0; i--)
davinci_nand_writel(info, NAND_4BIT_ECC_LOAD_OFFSET, ecc10[i]);
/* Allow time for syndrome calculation ... then read it.
* A syndrome of all zeroes 0 means no detected errors.
*/
davinci_nand_readl(info, NANDFSR_OFFSET);
nand_davinci_readecc_4bit(info, syndrome);
if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3]))
return 0;
/* Start address calculation, and wait for it to complete.
* We _could_ start reading more data while this is working,
* to speed up the overall page read.
*/
davinci_nand_writel(info, NANDFCR_OFFSET,
davinci_nand_readl(info, NANDFCR_OFFSET) | BIT(13));
for (;;) {
u32 fsr = davinci_nand_readl(info, NANDFSR_OFFSET);
switch ((fsr >> 8) & 0x0f) {
case 0: /* no error, should not happen */
return 0;
case 1: /* five or more errors detected */
return -EIO;
case 2: /* error addresses computed */
case 3:
num_errors = 1 + ((fsr >> 16) & 0x03);
goto correct;
default: /* still working on it */
cpu_relax();
continue;
}
}
correct:
/* correct each error */
for (i = 0, corrected = 0; i < num_errors; i++) {
int error_address, error_value;
if (i > 1) {
error_address = davinci_nand_readl(info,
NAND_ERR_ADD2_OFFSET);
error_value = davinci_nand_readl(info,
NAND_ERR_ERRVAL2_OFFSET);
} else {
error_address = davinci_nand_readl(info,
NAND_ERR_ADD1_OFFSET);
error_value = davinci_nand_readl(info,
NAND_ERR_ERRVAL1_OFFSET);
}
if (i & 1) {
error_address >>= 16;
error_value >>= 16;
}
error_address &= 0x3ff;
error_address = (512 + 7) - error_address;
if (error_address < 512) {
data[error_address] ^= error_value;
corrected++;
}
}
return corrected;
}
/*----------------------------------------------------------------------*/
/* /*
* NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's * NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's
* how these chips are normally wired. This translates to both 8 and 16 * how these chips are normally wired. This translates to both 8 and 16
@ -294,6 +484,23 @@ static void __init nand_dm6446evm_flash_init(struct davinci_nand_info *info)
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
/* An ECC layout for using 4-bit ECC with small-page flash, storing
* ten ECC bytes plus the manufacturer's bad block marker byte, and
* and not overlapping the default BBT markers.
*/
static struct nand_ecclayout hwecc4_small __initconst = {
.eccbytes = 10,
.eccpos = { 0, 1, 2, 3, 4,
/* offset 5 holds the badblock marker */
6, 7,
13, 14, 15, },
.oobfree = {
{.offset = 8, .length = 5, },
{.offset = 16, },
},
};
static int __init nand_davinci_probe(struct platform_device *pdev) static int __init nand_davinci_probe(struct platform_device *pdev)
{ {
struct davinci_nand_pdata *pdata = pdev->dev.platform_data; struct davinci_nand_pdata *pdata = pdev->dev.platform_data;
@ -306,6 +513,10 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
uint32_t val; uint32_t val;
nand_ecc_modes_t ecc_mode; nand_ecc_modes_t ecc_mode;
/* insist on board-specific configuration */
if (!pdata)
return -ENODEV;
/* which external chipselect will we be managing? */ /* which external chipselect will we be managing? */
if (pdev->id < 0 || pdev->id > 3) if (pdev->id < 0 || pdev->id > 3)
return -ENODEV; return -ENODEV;
@ -351,7 +562,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
info->chip.select_chip = nand_davinci_select_chip; info->chip.select_chip = nand_davinci_select_chip;
/* options such as NAND_USE_FLASH_BBT or 16-bit widths */ /* options such as NAND_USE_FLASH_BBT or 16-bit widths */
info->chip.options = pdata ? pdata->options : 0; info->chip.options = pdata->options;
info->ioaddr = (uint32_t __force) vaddr; info->ioaddr = (uint32_t __force) vaddr;
@ -360,14 +571,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
info->mask_chipsel = pdata->mask_chipsel; info->mask_chipsel = pdata->mask_chipsel;
/* use nandboot-capable ALE/CLE masks by default */ /* use nandboot-capable ALE/CLE masks by default */
if (pdata && pdata->mask_ale) info->mask_ale = pdata->mask_cle ? : MASK_ALE;
info->mask_ale = pdata->mask_cle; info->mask_cle = pdata->mask_cle ? : MASK_CLE;
else
info->mask_ale = MASK_ALE;
if (pdata && pdata->mask_cle)
info->mask_cle = pdata->mask_cle;
else
info->mask_cle = MASK_CLE;
/* Set address of hardware control function */ /* Set address of hardware control function */
info->chip.cmd_ctrl = nand_davinci_hwcontrol; info->chip.cmd_ctrl = nand_davinci_hwcontrol;
@ -377,30 +582,44 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
info->chip.read_buf = nand_davinci_read_buf; info->chip.read_buf = nand_davinci_read_buf;
info->chip.write_buf = nand_davinci_write_buf; info->chip.write_buf = nand_davinci_write_buf;
/* use board-specific ECC config; else, the best available */ /* Use board-specific ECC config */
if (pdata) ecc_mode = pdata->ecc_mode;
ecc_mode = pdata->ecc_mode;
else
ecc_mode = NAND_ECC_HW;
ret = -EINVAL;
switch (ecc_mode) { switch (ecc_mode) {
case NAND_ECC_NONE: case NAND_ECC_NONE:
case NAND_ECC_SOFT: case NAND_ECC_SOFT:
pdata->ecc_bits = 0;
break; break;
case NAND_ECC_HW: case NAND_ECC_HW:
info->chip.ecc.calculate = nand_davinci_calculate_1bit; if (pdata->ecc_bits == 4) {
info->chip.ecc.correct = nand_davinci_correct_1bit; /* No sanity checks: CPUs must support this,
info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; * and the chips may not use NAND_BUSWIDTH_16.
info->chip.ecc.size = 512; */
info->chip.ecc.bytes = 3;
break;
case NAND_ECC_HW_SYNDROME:
/* FIXME implement */
info->chip.ecc.size = 512;
info->chip.ecc.bytes = 10;
dev_warn(&pdev->dev, "4-bit ECC nyet supported\n"); /* No sharing 4-bit hardware between chipselects yet */
/* FALL THROUGH */ spin_lock_irq(&davinci_nand_lock);
if (ecc4_busy)
ret = -EBUSY;
else
ecc4_busy = true;
spin_unlock_irq(&davinci_nand_lock);
if (ret == -EBUSY)
goto err_ecc;
info->chip.ecc.calculate = nand_davinci_calculate_4bit;
info->chip.ecc.correct = nand_davinci_correct_4bit;
info->chip.ecc.hwctl = nand_davinci_hwctl_4bit;
info->chip.ecc.bytes = 10;
} else {
info->chip.ecc.calculate = nand_davinci_calculate_1bit;
info->chip.ecc.correct = nand_davinci_correct_1bit;
info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
info->chip.ecc.bytes = 3;
}
info->chip.ecc.size = 512;
break;
default: default:
ret = -EINVAL; ret = -EINVAL;
goto err_ecc; goto err_ecc;
@ -441,12 +660,56 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
spin_unlock_irq(&davinci_nand_lock); spin_unlock_irq(&davinci_nand_lock);
/* Scan to find existence of the device(s) */ /* Scan to find existence of the device(s) */
ret = nand_scan(&info->mtd, pdata->mask_chipsel ? 2 : 1); ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1);
if (ret < 0) { if (ret < 0) {
dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
goto err_scan; goto err_scan;
} }
/* Update ECC layout if needed ... for 1-bit HW ECC, the default
* is OK, but it allocates 6 bytes when only 3 are needed (for
* each 512 bytes). For the 4-bit HW ECC, that default is not
* usable: 10 bytes are needed, not 6.
*/
if (pdata->ecc_bits == 4) {
int chunks = info->mtd.writesize / 512;
if (!chunks || info->mtd.oobsize < 16) {
dev_dbg(&pdev->dev, "too small\n");
ret = -EINVAL;
goto err_scan;
}
/* For small page chips, preserve the manufacturer's
* badblock marking data ... and make sure a flash BBT
* table marker fits in the free bytes.
*/
if (chunks == 1) {
info->ecclayout = hwecc4_small;
info->ecclayout.oobfree[1].length =
info->mtd.oobsize - 16;
goto syndrome_done;
}
/* For large page chips we'll be wanting to use a
* not-yet-implemented mode that reads OOB data
* before reading the body of the page, to avoid
* the "infix OOB" model of NAND_ECC_HW_SYNDROME
* (and preserve manufacturer badblock markings).
*/
dev_warn(&pdev->dev, "no 4-bit ECC support yet "
"for large page NAND\n");
ret = -EIO;
goto err_scan;
syndrome_done:
info->chip.ecc.layout = &info->ecclayout;
}
ret = nand_scan_tail(&info->mtd);
if (ret < 0)
goto err_scan;
if (mtd_has_partitions()) { if (mtd_has_partitions()) {
struct mtd_partition *mtd_parts = NULL; struct mtd_partition *mtd_parts = NULL;
int mtd_parts_nb = 0; int mtd_parts_nb = 0;
@ -455,22 +718,11 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
static const char *probes[] __initconst = static const char *probes[] __initconst =
{ "cmdlinepart", NULL }; { "cmdlinepart", NULL };
const char *master_name;
/* Set info->mtd.name = 0 temporarily */
master_name = info->mtd.name;
info->mtd.name = (char *)0;
/* info->mtd.name == 0, means: don't bother checking
<mtd-id> */
mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes, mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes,
&mtd_parts, 0); &mtd_parts, 0);
/* Restore info->mtd.name */
info->mtd.name = master_name;
} }
if (mtd_parts_nb <= 0 && pdata) { if (mtd_parts_nb <= 0) {
mtd_parts = pdata->parts; mtd_parts = pdata->parts;
mtd_parts_nb = pdata->nr_parts; mtd_parts_nb = pdata->nr_parts;
} }
@ -483,7 +735,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
info->partitioned = true; info->partitioned = true;
} }
} else if (pdata && pdata->nr_parts) { } else if (pdata->nr_parts) {
dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n", dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n",
pdata->nr_parts, info->mtd.name); pdata->nr_parts, info->mtd.name);
} }
@ -509,6 +761,11 @@ err_scan:
err_clk_enable: err_clk_enable:
clk_put(info->clk); clk_put(info->clk);
spin_lock_irq(&davinci_nand_lock);
if (ecc_mode == NAND_ECC_HW_SYNDROME)
ecc4_busy = false;
spin_unlock_irq(&davinci_nand_lock);
err_ecc: err_ecc:
err_clk: err_clk:
err_ioremap: err_ioremap:
@ -532,6 +789,11 @@ static int __exit nand_davinci_remove(struct platform_device *pdev)
else else
status = del_mtd_device(&info->mtd); status = del_mtd_device(&info->mtd);
spin_lock_irq(&davinci_nand_lock);
if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)
ecc4_busy = false;
spin_unlock_irq(&davinci_nand_lock);
iounmap(info->base); iounmap(info->base);
iounmap(info->vaddr); iounmap(info->vaddr);

View file

@ -138,7 +138,14 @@ static struct nand_ecclayout nand_hw_eccoob_8 = {
static struct nand_ecclayout nand_hw_eccoob_16 = { static struct nand_ecclayout nand_hw_eccoob_16 = {
.eccbytes = 5, .eccbytes = 5,
.eccpos = {6, 7, 8, 9, 10}, .eccpos = {6, 7, 8, 9, 10},
.oobfree = {{0, 6}, {12, 4}, } .oobfree = {{0, 5}, {11, 5}, }
};
static struct nand_ecclayout nand_hw_eccoob_64 = {
.eccbytes = 20,
.eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26,
38, 39, 40, 41, 42, 54, 55, 56, 57, 58},
.oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, }
}; };
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
@ -192,7 +199,7 @@ static void wait_op_done(struct mxc_nand_host *host, int max_retries,
} }
udelay(1); udelay(1);
} }
if (max_retries <= 0) if (max_retries < 0)
DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n",
__func__, param); __func__, param);
} }
@ -795,9 +802,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
send_addr(host, (page_addr & 0xff), false); send_addr(host, (page_addr & 0xff), false);
if (host->pagesize_2k) { if (host->pagesize_2k) {
send_addr(host, (page_addr >> 8) & 0xFF, false); if (mtd->size >= 0x10000000) {
if (mtd->size >= 0x40000000) /* paddr_8 - paddr_15 */
send_addr(host, (page_addr >> 8) & 0xff, false);
send_addr(host, (page_addr >> 16) & 0xff, true); send_addr(host, (page_addr >> 16) & 0xff, true);
} else
/* paddr_8 - paddr_15 */
send_addr(host, (page_addr >> 8) & 0xff, true);
} else { } else {
/* One more address cycle for higher density devices */ /* One more address cycle for higher density devices */
if (mtd->size >= 0x4000000) { if (mtd->size >= 0x4000000) {
@ -923,7 +934,6 @@ static int __init mxcnd_probe(struct platform_device *pdev)
this->ecc.mode = NAND_ECC_HW; this->ecc.mode = NAND_ECC_HW;
this->ecc.size = 512; this->ecc.size = 512;
this->ecc.bytes = 3; this->ecc.bytes = 3;
this->ecc.layout = &nand_hw_eccoob_8;
tmp = readw(host->regs + NFC_CONFIG1); tmp = readw(host->regs + NFC_CONFIG1);
tmp |= NFC_ECC_EN; tmp |= NFC_ECC_EN;
writew(tmp, host->regs + NFC_CONFIG1); writew(tmp, host->regs + NFC_CONFIG1);
@ -957,12 +967,44 @@ static int __init mxcnd_probe(struct platform_device *pdev)
this->ecc.layout = &nand_hw_eccoob_16; this->ecc.layout = &nand_hw_eccoob_16;
} }
host->pagesize_2k = 0; /* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1)) {
err = -ENXIO;
goto escan;
}
/* Scan to find existence of the device */ host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0;
if (nand_scan(mtd, 1)) {
DEBUG(MTD_DEBUG_LEVEL0, if (this->ecc.mode == NAND_ECC_HW) {
"MXC_ND: Unable to find any NAND device.\n"); switch (mtd->oobsize) {
case 8:
this->ecc.layout = &nand_hw_eccoob_8;
break;
case 16:
this->ecc.layout = &nand_hw_eccoob_16;
break;
case 64:
this->ecc.layout = &nand_hw_eccoob_64;
break;
default:
/* page size not handled by HW ECC */
/* switching back to soft ECC */
this->ecc.size = 512;
this->ecc.bytes = 3;
this->ecc.layout = &nand_hw_eccoob_8;
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.calculate = NULL;
this->ecc.correct = NULL;
this->ecc.hwctl = NULL;
tmp = readw(host->regs + NFC_CONFIG1);
tmp &= ~NFC_ECC_EN;
writew(tmp, host->regs + NFC_CONFIG1);
break;
}
}
/* second phase scan */
if (nand_scan_tail(mtd)) {
err = -ENXIO; err = -ENXIO;
goto escan; goto escan;
} }
@ -985,7 +1027,7 @@ static int __init mxcnd_probe(struct platform_device *pdev)
return 0; return 0;
escan: escan:
free_irq(host->irq, NULL); free_irq(host->irq, host);
eirq: eirq:
iounmap(host->regs); iounmap(host->regs);
eres: eres:
@ -1005,7 +1047,7 @@ static int __devexit mxcnd_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
nand_release(&host->mtd); nand_release(&host->mtd);
free_irq(host->irq, NULL); free_irq(host->irq, host);
iounmap(host->regs); iounmap(host->regs);
kfree(host); kfree(host);

View file

@ -2756,7 +2756,8 @@ int nand_scan_tail(struct mtd_info *mtd)
* the out of band area * the out of band area
*/ */
chip->ecc.layout->oobavail = 0; chip->ecc.layout->oobavail = 0;
for (i = 0; chip->ecc.layout->oobfree[i].length; i++) for (i = 0; chip->ecc.layout->oobfree[i].length
&& i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++)
chip->ecc.layout->oobavail += chip->ecc.layout->oobavail +=
chip->ecc.layout->oobfree[i].length; chip->ecc.layout->oobfree[i].length;
mtd->oobavail = chip->ecc.layout->oobavail; mtd->oobavail = chip->ecc.layout->oobavail;

View file

@ -428,8 +428,8 @@ EXPORT_SYMBOL(nand_calculate_ecc);
int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, int nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
unsigned char *read_ecc, unsigned char *calc_ecc) unsigned char *read_ecc, unsigned char *calc_ecc)
{ {
unsigned char b0, b1, b2; unsigned char b0, b1, b2, bit_addr;
unsigned char byte_addr, bit_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 =
(((struct nand_chip *)mtd->priv)->ecc.size) >> 8; (((struct nand_chip *)mtd->priv)->ecc.size) >> 8;

776
drivers/mtd/nand/omap2.c Normal file
View file

@ -0,0 +1,776 @@
/*
* Copyright © 2004 Texas Instruments, Jian Zhang <jzhang@ti.com>
* Copyright © 2004 Micron Technology Inc.
* Copyright © 2004 David Brownell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/io.h>
#include <asm/dma.h>
#include <mach/gpmc.h>
#include <mach/nand.h>
#define GPMC_IRQ_STATUS 0x18
#define GPMC_ECC_CONFIG 0x1F4
#define GPMC_ECC_CONTROL 0x1F8
#define GPMC_ECC_SIZE_CONFIG 0x1FC
#define GPMC_ECC1_RESULT 0x200
#define DRIVER_NAME "omap2-nand"
/* size (4 KiB) for IO mapping */
#define NAND_IO_SIZE SZ_4K
#define NAND_WP_OFF 0
#define NAND_WP_BIT 0x00000010
#define WR_RD_PIN_MONITORING 0x00600000
#define GPMC_BUF_FULL 0x00000001
#define GPMC_BUF_EMPTY 0x00000000
#define NAND_Ecc_P1e (1 << 0)
#define NAND_Ecc_P2e (1 << 1)
#define NAND_Ecc_P4e (1 << 2)
#define NAND_Ecc_P8e (1 << 3)
#define NAND_Ecc_P16e (1 << 4)
#define NAND_Ecc_P32e (1 << 5)
#define NAND_Ecc_P64e (1 << 6)
#define NAND_Ecc_P128e (1 << 7)
#define NAND_Ecc_P256e (1 << 8)
#define NAND_Ecc_P512e (1 << 9)
#define NAND_Ecc_P1024e (1 << 10)
#define NAND_Ecc_P2048e (1 << 11)
#define NAND_Ecc_P1o (1 << 16)
#define NAND_Ecc_P2o (1 << 17)
#define NAND_Ecc_P4o (1 << 18)
#define NAND_Ecc_P8o (1 << 19)
#define NAND_Ecc_P16o (1 << 20)
#define NAND_Ecc_P32o (1 << 21)
#define NAND_Ecc_P64o (1 << 22)
#define NAND_Ecc_P128o (1 << 23)
#define NAND_Ecc_P256o (1 << 24)
#define NAND_Ecc_P512o (1 << 25)
#define NAND_Ecc_P1024o (1 << 26)
#define NAND_Ecc_P2048o (1 << 27)
#define TF(value) (value ? 1 : 0)
#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0)
#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1)
#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2)
#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3)
#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4)
#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5)
#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6)
#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7)
#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0)
#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1)
#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2)
#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3)
#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4)
#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5)
#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6)
#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7)
#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0)
#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1)
#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2)
#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3)
#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4)
#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5)
#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6)
#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7)
#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0)
#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1)
#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2)
#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3)
#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4)
#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5)
#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6)
#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7)
#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0)
#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1)
#ifdef CONFIG_MTD_PARTITIONS
static const char *part_probes[] = { "cmdlinepart", NULL };
#endif
struct omap_nand_info {
struct nand_hw_control controller;
struct omap_nand_platform_data *pdata;
struct mtd_info mtd;
struct mtd_partition *parts;
struct nand_chip nand;
struct platform_device *pdev;
int gpmc_cs;
unsigned long phys_base;
void __iomem *gpmc_cs_baseaddr;
void __iomem *gpmc_baseaddr;
};
/**
* omap_nand_wp - This function enable or disable the Write Protect feature
* @mtd: MTD device structure
* @mode: WP ON/OFF
*/
static void omap_nand_wp(struct mtd_info *mtd, int mode)
{
struct omap_nand_info *info = container_of(mtd,
struct omap_nand_info, mtd);
unsigned long config = __raw_readl(info->gpmc_baseaddr + GPMC_CONFIG);
if (mode)
config &= ~(NAND_WP_BIT); /* WP is ON */
else
config |= (NAND_WP_BIT); /* WP is OFF */
__raw_writel(config, (info->gpmc_baseaddr + GPMC_CONFIG));
}
/**
* omap_hwcontrol - hardware specific access to control-lines
* @mtd: MTD device structure
* @cmd: command to device
* @ctrl:
* NAND_NCE: bit 0 -> don't care
* NAND_CLE: bit 1 -> Command Latch
* NAND_ALE: bit 2 -> Address Latch
*
* NOTE: boards may use different bits for these!!
*/
static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct omap_nand_info *info = container_of(mtd,
struct omap_nand_info, mtd);
switch (ctrl) {
case NAND_CTRL_CHANGE | NAND_CTRL_CLE:
info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
GPMC_CS_NAND_COMMAND;
info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
GPMC_CS_NAND_DATA;
break;
case NAND_CTRL_CHANGE | NAND_CTRL_ALE:
info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
GPMC_CS_NAND_ADDRESS;
info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
GPMC_CS_NAND_DATA;
break;
case NAND_CTRL_CHANGE | NAND_NCE:
info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
GPMC_CS_NAND_DATA;
info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
GPMC_CS_NAND_DATA;
break;
}
if (cmd != NAND_CMD_NONE)
__raw_writeb(cmd, info->nand.IO_ADDR_W);
}
/**
* omap_read_buf16 - read data from NAND controller into buffer
* @mtd: MTD device structure
* @buf: buffer to store date
* @len: number of bytes to read
*/
static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
{
struct nand_chip *nand = mtd->priv;
__raw_readsw(nand->IO_ADDR_R, buf, len / 2);
}
/**
* omap_write_buf16 - write buffer to NAND controller
* @mtd: MTD device structure
* @buf: data buffer
* @len: number of bytes to write
*/
static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
{
struct omap_nand_info *info = container_of(mtd,
struct omap_nand_info, mtd);
u16 *p = (u16 *) buf;
/* FIXME try bursts of writesw() or DMA ... */
len >>= 1;
while (len--) {
writew(*p++, info->nand.IO_ADDR_W);
while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr +
GPMC_STATUS) & GPMC_BUF_FULL))
;
}
}
/**
* omap_verify_buf - Verify chip data against buffer
* @mtd: MTD device structure
* @buf: buffer containing the data to compare
* @len: number of bytes to compare
*/
static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len)
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
u16 *p = (u16 *) buf;
len >>= 1;
while (len--) {
if (*p++ != cpu_to_le16(readw(info->nand.IO_ADDR_R)))
return -EFAULT;
}
return 0;
}
#ifdef CONFIG_MTD_NAND_OMAP_HWECC
/**
* omap_hwecc_init - Initialize the HW ECC for NAND flash in GPMC controller
* @mtd: MTD device structure
*/
static void omap_hwecc_init(struct mtd_info *mtd)
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
struct nand_chip *chip = mtd->priv;
unsigned long val = 0x0;
/* Read from ECC Control Register */
val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONTROL);
/* Clear all ECC | Enable Reg1 */
val = ((0x00000001<<8) | 0x00000001);
__raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
/* Read from ECC Size Config Register */
val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
/* ECCSIZE1=512 | Select eccResultsize[0-3] */
val = ((((chip->ecc.size >> 1) - 1) << 22) | (0x0000000F));
__raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
}
/**
* gen_true_ecc - This function will generate true ECC value
* @ecc_buf: buffer to store ecc code
*
* This generated true ECC value can be used when correcting
* data read from NAND flash memory core
*/
static void gen_true_ecc(u8 *ecc_buf)
{
u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) |
((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8);
ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) |
P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp));
ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) |
P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp));
ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) |
P1e(tmp) | P2048o(tmp) | P2048e(tmp));
}
/**
* omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data
* @ecc_data1: ecc code from nand spare area
* @ecc_data2: ecc code from hardware register obtained from hardware ecc
* @page_data: page data
*
* This function compares two ECC's and indicates if there is an error.
* If the error can be corrected it will be corrected to the buffer.
*/
static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
u8 *ecc_data2, /* read from register */
u8 *page_data)
{
uint i;
u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8];
u8 comp0_bit[8], comp1_bit[8], comp2_bit[8];
u8 ecc_bit[24];
u8 ecc_sum = 0;
u8 find_bit = 0;
uint find_byte = 0;
int isEccFF;
isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF);
gen_true_ecc(ecc_data1);
gen_true_ecc(ecc_data2);
for (i = 0; i <= 2; i++) {
*(ecc_data1 + i) = ~(*(ecc_data1 + i));
*(ecc_data2 + i) = ~(*(ecc_data2 + i));
}
for (i = 0; i < 8; i++) {
tmp0_bit[i] = *ecc_data1 % 2;
*ecc_data1 = *ecc_data1 / 2;
}
for (i = 0; i < 8; i++) {
tmp1_bit[i] = *(ecc_data1 + 1) % 2;
*(ecc_data1 + 1) = *(ecc_data1 + 1) / 2;
}
for (i = 0; i < 8; i++) {
tmp2_bit[i] = *(ecc_data1 + 2) % 2;
*(ecc_data1 + 2) = *(ecc_data1 + 2) / 2;
}
for (i = 0; i < 8; i++) {
comp0_bit[i] = *ecc_data2 % 2;
*ecc_data2 = *ecc_data2 / 2;
}
for (i = 0; i < 8; i++) {
comp1_bit[i] = *(ecc_data2 + 1) % 2;
*(ecc_data2 + 1) = *(ecc_data2 + 1) / 2;
}
for (i = 0; i < 8; i++) {
comp2_bit[i] = *(ecc_data2 + 2) % 2;
*(ecc_data2 + 2) = *(ecc_data2 + 2) / 2;
}
for (i = 0; i < 6; i++)
ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2];
for (i = 0; i < 8; i++)
ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i];
for (i = 0; i < 8; i++)
ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i];
ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0];
ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1];
for (i = 0; i < 24; i++)
ecc_sum += ecc_bit[i];
switch (ecc_sum) {
case 0:
/* Not reached because this function is not called if
* ECC values are equal
*/
return 0;
case 1:
/* Uncorrectable error */
DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n");
return -1;
case 11:
/* UN-Correctable error */
DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR B\n");
return -1;
case 12:
/* Correctable error */
find_byte = (ecc_bit[23] << 8) +
(ecc_bit[21] << 7) +
(ecc_bit[19] << 6) +
(ecc_bit[17] << 5) +
(ecc_bit[15] << 4) +
(ecc_bit[13] << 3) +
(ecc_bit[11] << 2) +
(ecc_bit[9] << 1) +
ecc_bit[7];
find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1];
DEBUG(MTD_DEBUG_LEVEL0, "Correcting single bit ECC error at "
"offset: %d, bit: %d\n", find_byte, find_bit);
page_data[find_byte] ^= (1 << find_bit);
return 0;
default:
if (isEccFF) {
if (ecc_data2[0] == 0 &&
ecc_data2[1] == 0 &&
ecc_data2[2] == 0)
return 0;
}
DEBUG(MTD_DEBUG_LEVEL0, "UNCORRECTED_ERROR default\n");
return -1;
}
}
/**
* omap_correct_data - Compares the ECC read with HW generated ECC
* @mtd: MTD device structure
* @dat: page data
* @read_ecc: ecc read from nand flash
* @calc_ecc: ecc read from HW ECC registers
*
* Compares the ecc read from nand spare area with ECC registers values
* and if ECC's mismached, it will call 'omap_compare_ecc' for error detection
* and correction.
*/
static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
int blockCnt = 0, i = 0, ret = 0;
/* Ex NAND_ECC_HW12_2048 */
if ((info->nand.ecc.mode == NAND_ECC_HW) &&
(info->nand.ecc.size == 2048))
blockCnt = 4;
else
blockCnt = 1;
for (i = 0; i < blockCnt; i++) {
if (memcmp(read_ecc, calc_ecc, 3) != 0) {
ret = omap_compare_ecc(read_ecc, calc_ecc, dat);
if (ret < 0)
return ret;
}
read_ecc += 3;
calc_ecc += 3;
dat += 512;
}
return 0;
}
/**
* omap_calcuate_ecc - Generate non-inverted ECC bytes.
* @mtd: MTD device structure
* @dat: The pointer to data on which ecc is computed
* @ecc_code: The ecc_code buffer
*
* Using noninverted ECC can be considered ugly since writing a blank
* page ie. padding will clear the ECC bytes. This is no problem as long
* nobody is trying to write data on the seemingly unused page. Reading
* an erased page will produce an ECC mismatch between generated and read
* ECC bytes that has to be dealt with separately.
*/
static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u_char *ecc_code)
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
unsigned long val = 0x0;
unsigned long reg;
/* Start Reading from HW ECC1_Result = 0x200 */
reg = (unsigned long)(info->gpmc_baseaddr + GPMC_ECC1_RESULT);
val = __raw_readl(reg);
*ecc_code++ = val; /* P128e, ..., P1e */
*ecc_code++ = val >> 16; /* P128o, ..., P1o */
/* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */
*ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
reg += 4;
return 0;
}
/**
* omap_enable_hwecc - This function enables the hardware ecc functionality
* @mtd: MTD device structure
* @mode: Read/Write mode
*/
static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
struct nand_chip *chip = mtd->priv;
unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
unsigned long val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONFIG);
switch (mode) {
case NAND_ECC_READ:
__raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
/* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
break;
case NAND_ECC_READSYN:
__raw_writel(0x100, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
/* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
break;
case NAND_ECC_WRITE:
__raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
/* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
break;
default:
DEBUG(MTD_DEBUG_LEVEL0, "Error: Unrecognized Mode[%d]!\n",
mode);
break;
}
__raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONFIG);
}
#endif
/**
* omap_wait - wait until the command is done
* @mtd: MTD device structure
* @chip: NAND Chip structure
*
* Wait function is called during Program and erase operations and
* the way it is called from MTD layer, we should wait till the NAND
* chip is ready after the programming/erase operation has completed.
*
* Erase can take up to 400ms and program up to 20ms according to
* general NAND and SmartMedia specs
*/
static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
{
struct nand_chip *this = mtd->priv;
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
unsigned long timeo = jiffies;
int status, state = this->state;
if (state == FL_ERASING)
timeo += (HZ * 400) / 1000;
else
timeo += (HZ * 20) / 1000;
this->IO_ADDR_W = (void *) info->gpmc_cs_baseaddr +
GPMC_CS_NAND_COMMAND;
this->IO_ADDR_R = (void *) info->gpmc_cs_baseaddr + GPMC_CS_NAND_DATA;
__raw_writeb(NAND_CMD_STATUS & 0xFF, this->IO_ADDR_W);
while (time_before(jiffies, timeo)) {
status = __raw_readb(this->IO_ADDR_R);
if (!(status & 0x40))
break;
}
return status;
}
/**
* omap_dev_ready - calls the platform specific dev_ready function
* @mtd: MTD device structure
*/
static int omap_dev_ready(struct mtd_info *mtd)
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
unsigned int val = __raw_readl(info->gpmc_baseaddr + GPMC_IRQ_STATUS);
if ((val & 0x100) == 0x100) {
/* Clear IRQ Interrupt */
val |= 0x100;
val &= ~(0x0);
__raw_writel(val, info->gpmc_baseaddr + GPMC_IRQ_STATUS);
} else {
unsigned int cnt = 0;
while (cnt++ < 0x1FF) {
if ((val & 0x100) == 0x100)
return 0;
val = __raw_readl(info->gpmc_baseaddr +
GPMC_IRQ_STATUS);
}
}
return 1;
}
static int __devinit omap_nand_probe(struct platform_device *pdev)
{
struct omap_nand_info *info;
struct omap_nand_platform_data *pdata;
int err;
unsigned long val;
pdata = pdev->dev.platform_data;
if (pdata == NULL) {
dev_err(&pdev->dev, "platform data missing\n");
return -ENODEV;
}
info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
platform_set_drvdata(pdev, info);
spin_lock_init(&info->controller.lock);
init_waitqueue_head(&info->controller.wq);
info->pdev = pdev;
info->gpmc_cs = pdata->cs;
info->gpmc_baseaddr = pdata->gpmc_baseaddr;
info->gpmc_cs_baseaddr = pdata->gpmc_cs_baseaddr;
info->mtd.priv = &info->nand;
info->mtd.name = dev_name(&pdev->dev);
info->mtd.owner = THIS_MODULE;
err = gpmc_cs_request(info->gpmc_cs, NAND_IO_SIZE, &info->phys_base);
if (err < 0) {
dev_err(&pdev->dev, "Cannot request GPMC CS\n");
goto out_free_info;
}
/* Enable RD PIN Monitoring Reg */
if (pdata->dev_ready) {
val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1);
val |= WR_RD_PIN_MONITORING;
gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG1, val);
}
val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG7);
val &= ~(0xf << 8);
val |= (0xc & 0xf) << 8;
gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG7, val);
/* NAND write protect off */
omap_nand_wp(&info->mtd, NAND_WP_OFF);
if (!request_mem_region(info->phys_base, NAND_IO_SIZE,
pdev->dev.driver->name)) {
err = -EBUSY;
goto out_free_cs;
}
info->nand.IO_ADDR_R = ioremap(info->phys_base, NAND_IO_SIZE);
if (!info->nand.IO_ADDR_R) {
err = -ENOMEM;
goto out_release_mem_region;
}
info->nand.controller = &info->controller;
info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
info->nand.cmd_ctrl = omap_hwcontrol;
/* REVISIT: only supports 16-bit NAND flash */
info->nand.read_buf = omap_read_buf16;
info->nand.write_buf = omap_write_buf16;
info->nand.verify_buf = omap_verify_buf;
/*
* If RDY/BSY line is connected to OMAP then use the omap ready
* funcrtion and the generic nand_wait function which reads the status
* register after monitoring the RDY/BSY line.Otherwise use a standard
* chip delay which is slightly more than tR (AC Timing) of the NAND
* device and read status register until you get a failure or success
*/
if (pdata->dev_ready) {
info->nand.dev_ready = omap_dev_ready;
info->nand.chip_delay = 0;
} else {
info->nand.waitfunc = omap_wait;
info->nand.chip_delay = 50;
}
info->nand.options |= NAND_SKIP_BBTSCAN;
if ((gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1) & 0x3000)
== 0x1000)
info->nand.options |= NAND_BUSWIDTH_16;
#ifdef CONFIG_MTD_NAND_OMAP_HWECC
info->nand.ecc.bytes = 3;
info->nand.ecc.size = 512;
info->nand.ecc.calculate = omap_calculate_ecc;
info->nand.ecc.hwctl = omap_enable_hwecc;
info->nand.ecc.correct = omap_correct_data;
info->nand.ecc.mode = NAND_ECC_HW;
/* init HW ECC */
omap_hwecc_init(&info->mtd);
#else
info->nand.ecc.mode = NAND_ECC_SOFT;
#endif
/* DIP switches on some boards change between 8 and 16 bit
* bus widths for flash. Try the other width if the first try fails.
*/
if (nand_scan(&info->mtd, 1)) {
info->nand.options ^= NAND_BUSWIDTH_16;
if (nand_scan(&info->mtd, 1)) {
err = -ENXIO;
goto out_release_mem_region;
}
}
#ifdef CONFIG_MTD_PARTITIONS
err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
if (err > 0)
add_mtd_partitions(&info->mtd, info->parts, err);
else if (pdata->parts)
add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
else
#endif
add_mtd_device(&info->mtd);
platform_set_drvdata(pdev, &info->mtd);
return 0;
out_release_mem_region:
release_mem_region(info->phys_base, NAND_IO_SIZE);
out_free_cs:
gpmc_cs_free(info->gpmc_cs);
out_free_info:
kfree(info);
return err;
}
static int omap_nand_remove(struct platform_device *pdev)
{
struct mtd_info *mtd = platform_get_drvdata(pdev);
struct omap_nand_info *info = mtd->priv;
platform_set_drvdata(pdev, NULL);
/* Release NAND device, its internal structures and partitions */
nand_release(&info->mtd);
iounmap(info->nand.IO_ADDR_R);
kfree(&info->mtd);
return 0;
}
static struct platform_driver omap_nand_driver = {
.probe = omap_nand_probe,
.remove = omap_nand_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
};
static int __init omap_nand_init(void)
{
printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME);
return platform_driver_register(&omap_nand_driver);
}
static void __exit omap_nand_exit(void)
{
platform_driver_unregister(&omap_nand_driver);
}
module_init(omap_nand_init);
module_exit(omap_nand_exit);
MODULE_ALIAS(DRIVER_NAME);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards");

View file

@ -47,6 +47,28 @@ static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl
writeb(cmd, nc->IO_ADDR_W + offs); writeb(cmd, nc->IO_ADDR_W + offs);
} }
static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
struct nand_chip *chip = mtd->priv;
void __iomem *io_base = chip->IO_ADDR_R;
uint64_t *buf64;
int i = 0;
while (len && (unsigned long)buf & 7) {
*buf++ = readb(io_base);
len--;
}
buf64 = (uint64_t *)buf;
while (i < len/8) {
uint64_t x;
asm ("ldrd\t%0, [%1]" : "=r" (x) : "r" (io_base));
buf64[i++] = x;
}
i *= 8;
while (i < len)
buf[i++] = readb(io_base);
}
static int __init orion_nand_probe(struct platform_device *pdev) static int __init orion_nand_probe(struct platform_device *pdev)
{ {
struct mtd_info *mtd; struct mtd_info *mtd;
@ -83,6 +105,7 @@ static int __init orion_nand_probe(struct platform_device *pdev)
nc->priv = board; nc->priv = board;
nc->IO_ADDR_R = nc->IO_ADDR_W = io_base; nc->IO_ADDR_R = nc->IO_ADDR_W = io_base;
nc->cmd_ctrl = orion_nand_cmd_ctrl; nc->cmd_ctrl = orion_nand_cmd_ctrl;
nc->read_buf = orion_nand_read_buf;
nc->ecc.mode = NAND_ECC_SOFT; nc->ecc.mode = NAND_ECC_SOFT;
if (board->chip_delay) if (board->chip_delay)

View file

@ -61,6 +61,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl; data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl;
data->chip.dev_ready = pdata->ctrl.dev_ready; data->chip.dev_ready = pdata->ctrl.dev_ready;
data->chip.select_chip = pdata->ctrl.select_chip; data->chip.select_chip = pdata->ctrl.select_chip;
data->chip.write_buf = pdata->ctrl.write_buf;
data->chip.read_buf = pdata->ctrl.read_buf;
data->chip.chip_delay = pdata->chip.chip_delay; data->chip.chip_delay = pdata->chip.chip_delay;
data->chip.options |= pdata->chip.options; data->chip.options |= pdata->chip.options;
@ -70,6 +72,13 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data); platform_set_drvdata(pdev, data);
/* Handle any platform specific setup */
if (pdata->ctrl.probe) {
res = pdata->ctrl.probe(pdev);
if (res)
goto out;
}
/* Scan to find existance of the device */ /* Scan to find existance of the device */
if (nand_scan(&data->mtd, 1)) { if (nand_scan(&data->mtd, 1)) {
res = -ENXIO; res = -ENXIO;
@ -86,6 +95,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
return 0; return 0;
} }
} }
if (pdata->chip.set_parts)
pdata->chip.set_parts(data->mtd.size, &pdata->chip);
if (pdata->chip.partitions) { if (pdata->chip.partitions) {
data->parts = pdata->chip.partitions; data->parts = pdata->chip.partitions;
res = add_mtd_partitions(&data->mtd, data->parts, res = add_mtd_partitions(&data->mtd, data->parts,
@ -99,6 +110,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
nand_release(&data->mtd); nand_release(&data->mtd);
out: out:
if (pdata->ctrl.remove)
pdata->ctrl.remove(pdev);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
iounmap(data->io_base); iounmap(data->io_base);
kfree(data); kfree(data);
@ -111,15 +124,15 @@ out:
static int __devexit plat_nand_remove(struct platform_device *pdev) static int __devexit plat_nand_remove(struct platform_device *pdev)
{ {
struct plat_nand_data *data = platform_get_drvdata(pdev); struct plat_nand_data *data = platform_get_drvdata(pdev);
#ifdef CONFIG_MTD_PARTITIONS
struct platform_nand_data *pdata = pdev->dev.platform_data; struct platform_nand_data *pdata = pdev->dev.platform_data;
#endif
nand_release(&data->mtd); nand_release(&data->mtd);
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
if (data->parts && data->parts != pdata->chip.partitions) if (data->parts && data->parts != pdata->chip.partitions)
kfree(data->parts); kfree(data->parts);
#endif #endif
if (pdata->ctrl.remove)
pdata->ctrl.remove(pdev);
iounmap(data->io_base); iounmap(data->io_base);
kfree(data); kfree(data);
@ -128,7 +141,7 @@ static int __devexit plat_nand_remove(struct platform_device *pdev)
static struct platform_driver plat_nand_driver = { static struct platform_driver plat_nand_driver = {
.probe = plat_nand_probe, .probe = plat_nand_probe,
.remove = plat_nand_remove, .remove = __devexit_p(plat_nand_remove),
.driver = { .driver = {
.name = "gen_nand", .name = "gen_nand",
.owner = THIS_MODULE, .owner = THIS_MODULE,

View file

@ -74,6 +74,14 @@ static struct nand_ecclayout nand_hw_eccoob = {
struct s3c2410_nand_info; struct s3c2410_nand_info;
/**
* struct s3c2410_nand_mtd - driver MTD structure
* @mtd: The MTD instance to pass to the MTD layer.
* @chip: The NAND chip information.
* @set: The platform information supplied for this set of NAND chips.
* @info: Link back to the hardware information.
* @scan_res: The result from calling nand_scan_ident().
*/
struct s3c2410_nand_mtd { struct s3c2410_nand_mtd {
struct mtd_info mtd; struct mtd_info mtd;
struct nand_chip chip; struct nand_chip chip;
@ -90,6 +98,21 @@ enum s3c_cpu_type {
/* overview of the s3c2410 nand state */ /* overview of the s3c2410 nand state */
/**
* struct s3c2410_nand_info - NAND controller state.
* @mtds: An array of MTD instances on this controoler.
* @platform: The platform data for this board.
* @device: The platform device we bound to.
* @area: The IO area resource that came from request_mem_region().
* @clk: The clock resource for this controller.
* @regs: The area mapped for the hardware registers described by @area.
* @sel_reg: Pointer to the register controlling the NAND selection.
* @sel_bit: The bit in @sel_reg to select the NAND chip.
* @mtd_count: The number of MTDs created from this controller.
* @save_sel: The contents of @sel_reg to be saved over suspend.
* @clk_rate: The clock rate from @clk.
* @cpu_type: The exact type of this controller.
*/
struct s3c2410_nand_info { struct s3c2410_nand_info {
/* mtd info */ /* mtd info */
struct nand_hw_control controller; struct nand_hw_control controller;
@ -145,12 +168,19 @@ static inline int allow_clk_stop(struct s3c2410_nand_info *info)
#define NS_IN_KHZ 1000000 #define NS_IN_KHZ 1000000
/**
* s3c_nand_calc_rate - calculate timing data.
* @wanted: The cycle time in nanoseconds.
* @clk: The clock rate in kHz.
* @max: The maximum divider value.
*
* Calculate the timing value from the given parameters.
*/
static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
{ {
int result; int result;
result = (wanted * clk) / NS_IN_KHZ; result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ);
result++;
pr_debug("result %d from %ld, %d\n", result, clk, wanted); pr_debug("result %d from %ld, %d\n", result, clk, wanted);
@ -169,13 +199,21 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
/* controller setup */ /* controller setup */
/**
* s3c2410_nand_setrate - setup controller timing information.
* @info: The controller instance.
*
* Given the information supplied by the platform, calculate and set
* the necessary timing registers in the hardware to generate the
* necessary timing cycles to the hardware.
*/
static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
{ {
struct s3c2410_platform_nand *plat = info->platform; struct s3c2410_platform_nand *plat = info->platform;
int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
int tacls, twrph0, twrph1; int tacls, twrph0, twrph1;
unsigned long clkrate = clk_get_rate(info->clk); unsigned long clkrate = clk_get_rate(info->clk);
unsigned long set, cfg, mask; unsigned long uninitialized_var(set), cfg, uninitialized_var(mask);
unsigned long flags; unsigned long flags;
/* calculate the timing information for the controller */ /* calculate the timing information for the controller */
@ -215,9 +253,9 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
case TYPE_S3C2440: case TYPE_S3C2440:
case TYPE_S3C2412: case TYPE_S3C2412:
mask = (S3C2410_NFCONF_TACLS(tacls_max - 1) | mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) |
S3C2410_NFCONF_TWRPH0(7) | S3C2440_NFCONF_TWRPH0(7) |
S3C2410_NFCONF_TWRPH1(7)); S3C2440_NFCONF_TWRPH1(7));
set = S3C2440_NFCONF_TACLS(tacls - 1); set = S3C2440_NFCONF_TACLS(tacls - 1);
set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
@ -225,14 +263,9 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
break; break;
default: default:
/* keep compiler happy */
mask = 0;
set = 0;
BUG(); BUG();
} }
dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
local_irq_save(flags); local_irq_save(flags);
cfg = readl(info->regs + S3C2410_NFCONF); cfg = readl(info->regs + S3C2410_NFCONF);
@ -242,9 +275,18 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
local_irq_restore(flags); local_irq_restore(flags);
dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
return 0; return 0;
} }
/**
* s3c2410_nand_inithw - basic hardware initialisation
* @info: The hardware state.
*
* Do the basic initialisation of the hardware, using s3c2410_nand_setrate()
* to setup the hardware access speeds and set the controller to be enabled.
*/
static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
{ {
int ret; int ret;
@ -268,8 +310,19 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
return 0; return 0;
} }
/* select chip */ /**
* s3c2410_nand_select_chip - select the given nand chip
* @mtd: The MTD instance for this chip.
* @chip: The chip number.
*
* This is called by the MTD layer to either select a given chip for the
* @mtd instance, or to indicate that the access has finished and the
* chip can be de-selected.
*
* The routine ensures that the nFCE line is correctly setup, and any
* platform specific selection code is called to route nFCE to the specific
* chip.
*/
static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
{ {
struct s3c2410_nand_info *info; struct s3c2410_nand_info *info;
@ -530,7 +583,16 @@ static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{ {
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
readsl(info->regs + S3C2440_NFDATA, buf, len / 4);
readsl(info->regs + S3C2440_NFDATA, buf, len >> 2);
/* cleanup if we've got less than a word to do */
if (len & 3) {
buf += len & ~3;
for (; len & 3; len--)
*buf++ = readb(info->regs + S3C2440_NFDATA);
}
} }
static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
@ -542,7 +604,16 @@ static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int
static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{ {
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
writesl(info->regs + S3C2440_NFDATA, buf, len / 4);
writesl(info->regs + S3C2440_NFDATA, buf, len >> 2);
/* cleanup any fractional write */
if (len & 3) {
buf += len & ~3;
for (; len & 3; len--, buf++)
writeb(*buf, info->regs + S3C2440_NFDATA);
}
} }
/* cpufreq driver support */ /* cpufreq driver support */
@ -593,7 +664,7 @@ static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *inf
/* device management functions */ /* device management functions */
static int s3c2410_nand_remove(struct platform_device *pdev) static int s3c24xx_nand_remove(struct platform_device *pdev)
{ {
struct s3c2410_nand_info *info = to_nand_info(pdev); struct s3c2410_nand_info *info = to_nand_info(pdev);
@ -645,17 +716,31 @@ static int s3c2410_nand_remove(struct platform_device *pdev)
} }
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
const char *part_probes[] = { "cmdlinepart", NULL };
static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
struct s3c2410_nand_mtd *mtd, struct s3c2410_nand_mtd *mtd,
struct s3c2410_nand_set *set) struct s3c2410_nand_set *set)
{ {
struct mtd_partition *part_info;
int nr_part = 0;
if (set == NULL) if (set == NULL)
return add_mtd_device(&mtd->mtd); return add_mtd_device(&mtd->mtd);
if (set->nr_partitions > 0 && set->partitions != NULL) { if (set->nr_partitions == 0) {
return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions); mtd->mtd.name = set->name;
nr_part = parse_mtd_partitions(&mtd->mtd, part_probes,
&part_info, 0);
} else {
if (set->nr_partitions > 0 && set->partitions != NULL) {
nr_part = set->nr_partitions;
part_info = set->partitions;
}
} }
if (nr_part > 0 && part_info)
return add_mtd_partitions(&mtd->mtd, part_info, nr_part);
return add_mtd_device(&mtd->mtd); return add_mtd_device(&mtd->mtd);
} }
#else #else
@ -667,11 +752,16 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
} }
#endif #endif
/* s3c2410_nand_init_chip /**
* s3c2410_nand_init_chip - initialise a single instance of an chip
* @info: The base NAND controller the chip is on.
* @nmtd: The new controller MTD instance to fill in.
* @set: The information passed from the board specific platform data.
* *
* init a single instance of an chip * Initialise the given @nmtd from the information in @info and @set. This
*/ * readies the structure for use with the MTD layer functions by ensuring
* all pointers are setup and the necessary control routines selected.
*/
static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
struct s3c2410_nand_mtd *nmtd, struct s3c2410_nand_mtd *nmtd,
struct s3c2410_nand_set *set) struct s3c2410_nand_set *set)
@ -757,14 +847,40 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
if (set->disable_ecc) if (set->disable_ecc)
chip->ecc.mode = NAND_ECC_NONE; chip->ecc.mode = NAND_ECC_NONE;
switch (chip->ecc.mode) {
case NAND_ECC_NONE:
dev_info(info->device, "NAND ECC disabled\n");
break;
case NAND_ECC_SOFT:
dev_info(info->device, "NAND soft ECC\n");
break;
case NAND_ECC_HW:
dev_info(info->device, "NAND hardware ECC\n");
break;
default:
dev_info(info->device, "NAND ECC UNKNOWN\n");
break;
}
/* If you use u-boot BBT creation code, specifying this flag will
* let the kernel fish out the BBT from the NAND, and also skip the
* full NAND scan that can take 1/2s or so. Little things... */
if (set->flash_bbt)
chip->options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN;
} }
/* s3c2410_nand_update_chip /**
* s3c2410_nand_update_chip - post probe update
* @info: The controller instance.
* @nmtd: The driver version of the MTD instance.
* *
* post-probe chip update, to change any items, such as the * This routine is called after the chip probe has succesfully completed
* layout for large page nand * and the relevant per-chip information updated. This call ensure that
*/ * we update the internal state accordingly.
*
* The internal state is currently limited to the ECC state information.
*/
static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
struct s3c2410_nand_mtd *nmtd) struct s3c2410_nand_mtd *nmtd)
{ {
@ -773,33 +889,33 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
dev_dbg(info->device, "chip %p => page shift %d\n", dev_dbg(info->device, "chip %p => page shift %d\n",
chip, chip->page_shift); chip, chip->page_shift);
if (hardware_ecc) { if (chip->ecc.mode != NAND_ECC_HW)
return;
/* change the behaviour depending on wether we are using /* change the behaviour depending on wether we are using
* the large or small page nand device */ * the large or small page nand device */
if (chip->page_shift > 10) { if (chip->page_shift > 10) {
chip->ecc.size = 256; chip->ecc.size = 256;
chip->ecc.bytes = 3; chip->ecc.bytes = 3;
} else { } else {
chip->ecc.size = 512; chip->ecc.size = 512;
chip->ecc.bytes = 3; chip->ecc.bytes = 3;
chip->ecc.layout = &nand_hw_eccoob; chip->ecc.layout = &nand_hw_eccoob;
}
} }
} }
/* s3c2410_nand_probe /* s3c24xx_nand_probe
* *
* called by device layer when it finds a device matching * called by device layer when it finds a device matching
* one our driver can handled. This code checks to see if * one our driver can handled. This code checks to see if
* it can allocate all necessary resources then calls the * it can allocate all necessary resources then calls the
* nand layer to look for devices * nand layer to look for devices
*/ */
static int s3c24xx_nand_probe(struct platform_device *pdev)
static int s3c24xx_nand_probe(struct platform_device *pdev,
enum s3c_cpu_type cpu_type)
{ {
struct s3c2410_platform_nand *plat = to_nand_plat(pdev); struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
enum s3c_cpu_type cpu_type;
struct s3c2410_nand_info *info; struct s3c2410_nand_info *info;
struct s3c2410_nand_mtd *nmtd; struct s3c2410_nand_mtd *nmtd;
struct s3c2410_nand_set *sets; struct s3c2410_nand_set *sets;
@ -809,6 +925,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
int nr_sets; int nr_sets;
int setno; int setno;
cpu_type = platform_get_device_id(pdev)->driver_data;
pr_debug("s3c2410_nand_probe(%p)\n", pdev); pr_debug("s3c2410_nand_probe(%p)\n", pdev);
info = kmalloc(sizeof(*info), GFP_KERNEL); info = kmalloc(sizeof(*info), GFP_KERNEL);
@ -922,7 +1040,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
return 0; return 0;
exit_error: exit_error:
s3c2410_nand_remove(pdev); s3c24xx_nand_remove(pdev);
if (err == 0) if (err == 0)
err = -EINVAL; err = -EINVAL;
@ -983,50 +1101,33 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
/* driver device registration */ /* driver device registration */
static int s3c2410_nand_probe(struct platform_device *dev) static struct platform_device_id s3c24xx_driver_ids[] = {
{ {
return s3c24xx_nand_probe(dev, TYPE_S3C2410); .name = "s3c2410-nand",
} .driver_data = TYPE_S3C2410,
}, {
static int s3c2440_nand_probe(struct platform_device *dev) .name = "s3c2440-nand",
{ .driver_data = TYPE_S3C2440,
return s3c24xx_nand_probe(dev, TYPE_S3C2440); }, {
} .name = "s3c2412-nand",
.driver_data = TYPE_S3C2412,
static int s3c2412_nand_probe(struct platform_device *dev) }, {
{ .name = "s3c6400-nand",
return s3c24xx_nand_probe(dev, TYPE_S3C2412); .driver_data = TYPE_S3C2412, /* compatible with 2412 */
}
static struct platform_driver s3c2410_nand_driver = {
.probe = s3c2410_nand_probe,
.remove = s3c2410_nand_remove,
.suspend = s3c24xx_nand_suspend,
.resume = s3c24xx_nand_resume,
.driver = {
.name = "s3c2410-nand",
.owner = THIS_MODULE,
}, },
{ }
}; };
static struct platform_driver s3c2440_nand_driver = { MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
.probe = s3c2440_nand_probe,
.remove = s3c2410_nand_remove,
.suspend = s3c24xx_nand_suspend,
.resume = s3c24xx_nand_resume,
.driver = {
.name = "s3c2440-nand",
.owner = THIS_MODULE,
},
};
static struct platform_driver s3c2412_nand_driver = { static struct platform_driver s3c24xx_nand_driver = {
.probe = s3c2412_nand_probe, .probe = s3c24xx_nand_probe,
.remove = s3c2410_nand_remove, .remove = s3c24xx_nand_remove,
.suspend = s3c24xx_nand_suspend, .suspend = s3c24xx_nand_suspend,
.resume = s3c24xx_nand_resume, .resume = s3c24xx_nand_resume,
.id_table = s3c24xx_driver_ids,
.driver = { .driver = {
.name = "s3c2412-nand", .name = "s3c24xx-nand",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
}; };
@ -1035,16 +1136,12 @@ static int __init s3c2410_nand_init(void)
{ {
printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n"); printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
platform_driver_register(&s3c2412_nand_driver); return platform_driver_register(&s3c24xx_nand_driver);
platform_driver_register(&s3c2440_nand_driver);
return platform_driver_register(&s3c2410_nand_driver);
} }
static void __exit s3c2410_nand_exit(void) static void __exit s3c2410_nand_exit(void)
{ {
platform_driver_unregister(&s3c2412_nand_driver); platform_driver_unregister(&s3c24xx_nand_driver);
platform_driver_unregister(&s3c2440_nand_driver);
platform_driver_unregister(&s3c2410_nand_driver);
} }
module_init(s3c2410_nand_init); module_init(s3c2410_nand_init);
@ -1053,6 +1150,3 @@ module_exit(s3c2410_nand_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); MODULE_DESCRIPTION("S3C24XX MTD NAND driver");
MODULE_ALIAS("platform:s3c2410-nand");
MODULE_ALIAS("platform:s3c2412-nand");
MODULE_ALIAS("platform:s3c2440-nand");

View file

@ -64,7 +64,7 @@ struct txx9ndfmc_priv {
struct nand_chip chip; struct nand_chip chip;
struct mtd_info mtd; struct mtd_info mtd;
int cs; int cs;
char mtdname[BUS_ID_SIZE + 2]; const char *mtdname;
}; };
#define MAX_TXX9NDFMC_DEV 4 #define MAX_TXX9NDFMC_DEV 4
@ -334,16 +334,23 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
if (plat->ch_mask != 1) { if (plat->ch_mask != 1) {
txx9_priv->cs = i; txx9_priv->cs = i;
sprintf(txx9_priv->mtdname, "%s.%u", txx9_priv->mtdname = kasprintf(GFP_KERNEL, "%s.%u",
dev_name(&dev->dev), i); dev_name(&dev->dev), i);
} else { } else {
txx9_priv->cs = -1; txx9_priv->cs = -1;
strcpy(txx9_priv->mtdname, dev_name(&dev->dev)); txx9_priv->mtdname = kstrdup(dev_name(&dev->dev),
GFP_KERNEL);
}
if (!txx9_priv->mtdname) {
kfree(txx9_priv);
dev_err(&dev->dev, "Unable to allocate MTD name.\n");
continue;
} }
if (plat->wide_mask & (1 << i)) if (plat->wide_mask & (1 << i))
chip->options |= NAND_BUSWIDTH_16; chip->options |= NAND_BUSWIDTH_16;
if (nand_scan(mtd, 1)) { if (nand_scan(mtd, 1)) {
kfree(txx9_priv->mtdname);
kfree(txx9_priv); kfree(txx9_priv);
continue; continue;
} }
@ -385,6 +392,7 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev)
kfree(drvdata->parts[i]); kfree(drvdata->parts[i]);
#endif #endif
del_mtd_device(mtd); del_mtd_device(mtd);
kfree(txx9_priv->mtdname);
kfree(txx9_priv); kfree(txx9_priv);
} }
return 0; return 0;

View file

@ -565,7 +565,7 @@ int omap2_onenand_rephase(void)
NULL, __adjust_timing); NULL, __adjust_timing);
} }
static void __devexit omap2_onenand_shutdown(struct platform_device *pdev) static void omap2_onenand_shutdown(struct platform_device *pdev)
{ {
struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); struct omap2_onenand *c = dev_get_drvdata(&pdev->dev);
@ -777,7 +777,7 @@ static int __devexit omap2_onenand_remove(struct platform_device *pdev)
static struct platform_driver omap2_onenand_driver = { static struct platform_driver omap2_onenand_driver = {
.probe = omap2_onenand_probe, .probe = omap2_onenand_probe,
.remove = omap2_onenand_remove, .remove = __devexit_p(omap2_onenand_remove),
.shutdown = omap2_onenand_shutdown, .shutdown = omap2_onenand_shutdown,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,

File diff suppressed because it is too large Load diff

View file

@ -63,6 +63,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
loff_t from; loff_t from;
size_t readlen, ooblen; size_t readlen, ooblen;
struct mtd_oob_ops ops; struct mtd_oob_ops ops;
int rgn;
printk(KERN_INFO "Scanning device for bad blocks\n"); printk(KERN_INFO "Scanning device for bad blocks\n");
@ -76,7 +77,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
/* Note that numblocks is 2 * (real numblocks) here; /* Note that numblocks is 2 * (real numblocks) here;
* see i += 2 below as it makses shifting and masking less painful * see i += 2 below as it makses shifting and masking less painful
*/ */
numblocks = mtd->size >> (bbm->bbt_erase_shift - 1); numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1);
startblock = 0; startblock = 0;
from = 0; from = 0;
@ -106,7 +107,12 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
} }
} }
i += 2; i += 2;
from += (1 << bbm->bbt_erase_shift);
if (FLEXONENAND(this)) {
rgn = flexonenand_region(mtd, from);
from += mtd->eraseregions[rgn].erasesize;
} else
from += (1 << bbm->bbt_erase_shift);
} }
return 0; return 0;
@ -143,7 +149,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
uint8_t res; uint8_t res;
/* Get block number * 2 */ /* Get block number * 2 */
block = (int) (offs >> (bbm->bbt_erase_shift - 1)); block = (int) (onenand_block(this, offs) << 1);
res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03; res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
DEBUG(MTD_DEBUG_LEVEL2, "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n", DEBUG(MTD_DEBUG_LEVEL2, "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n",
@ -178,7 +184,7 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
struct bbm_info *bbm = this->bbm; struct bbm_info *bbm = this->bbm;
int len, ret = 0; int len, ret = 0;
len = mtd->size >> (this->erase_shift + 2); len = this->chipsize >> (this->erase_shift + 2);
/* Allocate memory (2bit per block) and clear the memory bad block table */ /* Allocate memory (2bit per block) and clear the memory bad block table */
bbm->bbt = kzalloc(len, GFP_KERNEL); bbm->bbt = kzalloc(len, GFP_KERNEL);
if (!bbm->bbt) { if (!bbm->bbt) {

View file

@ -6,6 +6,10 @@
* Copyright © 2005-2007 Samsung Electronics * Copyright © 2005-2007 Samsung Electronics
* Kyungmin Park <kyungmin.park@samsung.com> * Kyungmin Park <kyungmin.park@samsung.com>
* *
* Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
* Flex-OneNAND simulator support
* Copyright (C) Samsung Electronics, 2008
*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
@ -24,16 +28,38 @@
#ifndef CONFIG_ONENAND_SIM_MANUFACTURER #ifndef CONFIG_ONENAND_SIM_MANUFACTURER
#define CONFIG_ONENAND_SIM_MANUFACTURER 0xec #define CONFIG_ONENAND_SIM_MANUFACTURER 0xec
#endif #endif
#ifndef CONFIG_ONENAND_SIM_DEVICE_ID #ifndef CONFIG_ONENAND_SIM_DEVICE_ID
#define CONFIG_ONENAND_SIM_DEVICE_ID 0x04 #define CONFIG_ONENAND_SIM_DEVICE_ID 0x04
#endif #endif
#define CONFIG_FLEXONENAND ((CONFIG_ONENAND_SIM_DEVICE_ID >> 9) & 1)
#ifndef CONFIG_ONENAND_SIM_VERSION_ID #ifndef CONFIG_ONENAND_SIM_VERSION_ID
#define CONFIG_ONENAND_SIM_VERSION_ID 0x1e #define CONFIG_ONENAND_SIM_VERSION_ID 0x1e
#endif #endif
#ifndef CONFIG_ONENAND_SIM_TECHNOLOGY_ID
#define CONFIG_ONENAND_SIM_TECHNOLOGY_ID CONFIG_FLEXONENAND
#endif
/* Initial boundary values for Flex-OneNAND Simulator */
#ifndef CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY
#define CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY 0x01
#endif
#ifndef CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY
#define CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY 0x01
#endif
static int manuf_id = CONFIG_ONENAND_SIM_MANUFACTURER; static int manuf_id = CONFIG_ONENAND_SIM_MANUFACTURER;
static int device_id = CONFIG_ONENAND_SIM_DEVICE_ID; static int device_id = CONFIG_ONENAND_SIM_DEVICE_ID;
static int version_id = CONFIG_ONENAND_SIM_VERSION_ID; static int version_id = CONFIG_ONENAND_SIM_VERSION_ID;
static int technology_id = CONFIG_ONENAND_SIM_TECHNOLOGY_ID;
static int boundary[] = {
CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY,
CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY,
};
struct onenand_flash { struct onenand_flash {
void __iomem *base; void __iomem *base;
@ -57,12 +83,18 @@ struct onenand_flash {
(writew(v, this->base + ONENAND_REG_WP_STATUS)) (writew(v, this->base + ONENAND_REG_WP_STATUS))
/* It has all 0xff chars */ /* It has all 0xff chars */
#define MAX_ONENAND_PAGESIZE (2048 + 64) #define MAX_ONENAND_PAGESIZE (4096 + 128)
static unsigned char *ffchars; static unsigned char *ffchars;
#if CONFIG_FLEXONENAND
#define PARTITION_NAME "Flex-OneNAND simulator partition"
#else
#define PARTITION_NAME "OneNAND simulator partition"
#endif
static struct mtd_partition os_partitions[] = { static struct mtd_partition os_partitions[] = {
{ {
.name = "OneNAND simulator partition", .name = PARTITION_NAME,
.offset = 0, .offset = 0,
.size = MTDPART_SIZ_FULL, .size = MTDPART_SIZ_FULL,
}, },
@ -104,6 +136,7 @@ static void onenand_lock_handle(struct onenand_chip *this, int cmd)
switch (cmd) { switch (cmd) {
case ONENAND_CMD_UNLOCK: case ONENAND_CMD_UNLOCK:
case ONENAND_CMD_UNLOCK_ALL:
if (block_lock_scheme) if (block_lock_scheme)
ONENAND_SET_WP_STATUS(ONENAND_WP_US, this); ONENAND_SET_WP_STATUS(ONENAND_WP_US, this);
else else
@ -228,10 +261,12 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
{ {
struct mtd_info *mtd = &info->mtd; struct mtd_info *mtd = &info->mtd;
struct onenand_flash *flash = this->priv; struct onenand_flash *flash = this->priv;
int main_offset, spare_offset; int main_offset, spare_offset, die = 0;
void __iomem *src; void __iomem *src;
void __iomem *dest; void __iomem *dest;
unsigned int i; unsigned int i;
static int pi_operation;
int erasesize, rgn;
if (dataram) { if (dataram) {
main_offset = mtd->writesize; main_offset = mtd->writesize;
@ -241,10 +276,27 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
spare_offset = 0; spare_offset = 0;
} }
if (pi_operation) {
die = readw(this->base + ONENAND_REG_START_ADDRESS2);
die >>= ONENAND_DDP_SHIFT;
}
switch (cmd) { switch (cmd) {
case FLEXONENAND_CMD_PI_ACCESS:
pi_operation = 1;
break;
case ONENAND_CMD_RESET:
pi_operation = 0;
break;
case ONENAND_CMD_READ: case ONENAND_CMD_READ:
src = ONENAND_CORE(flash) + offset; src = ONENAND_CORE(flash) + offset;
dest = ONENAND_MAIN_AREA(this, main_offset); dest = ONENAND_MAIN_AREA(this, main_offset);
if (pi_operation) {
writew(boundary[die], this->base + ONENAND_DATARAM);
break;
}
memcpy(dest, src, mtd->writesize); memcpy(dest, src, mtd->writesize);
/* Fall through */ /* Fall through */
@ -257,6 +309,10 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
case ONENAND_CMD_PROG: case ONENAND_CMD_PROG:
src = ONENAND_MAIN_AREA(this, main_offset); src = ONENAND_MAIN_AREA(this, main_offset);
dest = ONENAND_CORE(flash) + offset; dest = ONENAND_CORE(flash) + offset;
if (pi_operation) {
boundary[die] = readw(this->base + ONENAND_DATARAM);
break;
}
/* To handle partial write */ /* To handle partial write */
for (i = 0; i < (1 << mtd->subpage_sft); i++) { for (i = 0; i < (1 << mtd->subpage_sft); i++) {
int off = i * this->subpagesize; int off = i * this->subpagesize;
@ -284,9 +340,18 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
break; break;
case ONENAND_CMD_ERASE: case ONENAND_CMD_ERASE:
memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize); if (pi_operation)
break;
if (FLEXONENAND(this)) {
rgn = flexonenand_region(mtd, offset);
erasesize = mtd->eraseregions[rgn].erasesize;
} else
erasesize = mtd->erasesize;
memset(ONENAND_CORE(flash) + offset, 0xff, erasesize);
memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff, memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff,
(mtd->erasesize >> 5)); (erasesize >> 5));
break; break;
default: default:
@ -339,7 +404,7 @@ static void onenand_command_handle(struct onenand_chip *this, int cmd)
} }
if (block != -1) if (block != -1)
offset += block << this->erase_shift; offset = onenand_addr(this, block);
if (page != -1) if (page != -1)
offset += page << this->page_shift; offset += page << this->page_shift;
@ -390,6 +455,7 @@ static int __init flash_init(struct onenand_flash *flash)
} }
density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT; density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
density &= ONENAND_DEVICE_DENSITY_MASK;
size = ((16 << 20) << density); size = ((16 << 20) << density);
ONENAND_CORE(flash) = vmalloc(size + (size >> 5)); ONENAND_CORE(flash) = vmalloc(size + (size >> 5));
@ -405,8 +471,9 @@ static int __init flash_init(struct onenand_flash *flash)
writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID); writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID);
writew(device_id, flash->base + ONENAND_REG_DEVICE_ID); writew(device_id, flash->base + ONENAND_REG_DEVICE_ID);
writew(version_id, flash->base + ONENAND_REG_VERSION_ID); writew(version_id, flash->base + ONENAND_REG_VERSION_ID);
writew(technology_id, flash->base + ONENAND_REG_TECHNOLOGY);
if (density < 2) if (density < 2 && (!CONFIG_FLEXONENAND))
buffer_size = 0x0400; /* 1KiB page */ buffer_size = 0x0400; /* 1KiB page */
else else
buffer_size = 0x0800; /* 2KiB page */ buffer_size = 0x0800; /* 2KiB page */

View file

@ -94,7 +94,6 @@
#include <linux/atm_tcp.h> #include <linux/atm_tcp.h>
#include <linux/sonet.h> #include <linux/sonet.h>
#include <linux/atm_suni.h> #include <linux/atm_suni.h>
#include <linux/mtd/mtd.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/usbdevice_fs.h> #include <linux/usbdevice_fs.h>
@ -1405,46 +1404,6 @@ static int ioc_settimeout(unsigned int fd, unsigned int cmd, unsigned long arg)
#define HIDPGETCONNLIST _IOR('H', 210, int) #define HIDPGETCONNLIST _IOR('H', 210, int)
#define HIDPGETCONNINFO _IOR('H', 211, int) #define HIDPGETCONNINFO _IOR('H', 211, int)
struct mtd_oob_buf32 {
u_int32_t start;
u_int32_t length;
compat_caddr_t ptr; /* unsigned char* */
};
#define MEMWRITEOOB32 _IOWR('M',3,struct mtd_oob_buf32)
#define MEMREADOOB32 _IOWR('M',4,struct mtd_oob_buf32)
static int mtd_rw_oob(unsigned int fd, unsigned int cmd, unsigned long arg)
{
struct mtd_oob_buf __user *buf = compat_alloc_user_space(sizeof(*buf));
struct mtd_oob_buf32 __user *buf32 = compat_ptr(arg);
u32 data;
char __user *datap;
unsigned int real_cmd;
int err;
real_cmd = (cmd == MEMREADOOB32) ?
MEMREADOOB : MEMWRITEOOB;
if (copy_in_user(&buf->start, &buf32->start,
2 * sizeof(u32)) ||
get_user(data, &buf32->ptr))
return -EFAULT;
datap = compat_ptr(data);
if (put_user(datap, &buf->ptr))
return -EFAULT;
err = sys_ioctl(fd, real_cmd, (unsigned long) buf);
if (!err) {
if (copy_in_user(&buf32->start, &buf->start,
2 * sizeof(u32)))
err = -EFAULT;
}
return err;
}
#ifdef CONFIG_BLOCK #ifdef CONFIG_BLOCK
struct raw32_config_request struct raw32_config_request
{ {
@ -2426,15 +2385,6 @@ COMPATIBLE_IOCTL(USBDEVFS_SUBMITURB32)
COMPATIBLE_IOCTL(USBDEVFS_REAPURB32) COMPATIBLE_IOCTL(USBDEVFS_REAPURB32)
COMPATIBLE_IOCTL(USBDEVFS_REAPURBNDELAY32) COMPATIBLE_IOCTL(USBDEVFS_REAPURBNDELAY32)
COMPATIBLE_IOCTL(USBDEVFS_CLEAR_HALT) COMPATIBLE_IOCTL(USBDEVFS_CLEAR_HALT)
/* MTD */
COMPATIBLE_IOCTL(MEMGETINFO)
COMPATIBLE_IOCTL(MEMERASE)
COMPATIBLE_IOCTL(MEMLOCK)
COMPATIBLE_IOCTL(MEMUNLOCK)
COMPATIBLE_IOCTL(MEMGETREGIONCOUNT)
COMPATIBLE_IOCTL(MEMGETREGIONINFO)
COMPATIBLE_IOCTL(MEMGETBADBLOCK)
COMPATIBLE_IOCTL(MEMSETBADBLOCK)
/* NBD */ /* NBD */
ULONG_IOCTL(NBD_SET_SOCK) ULONG_IOCTL(NBD_SET_SOCK)
ULONG_IOCTL(NBD_SET_BLKSIZE) ULONG_IOCTL(NBD_SET_BLKSIZE)
@ -2544,8 +2494,6 @@ COMPATIBLE_IOCTL(JSIOCGBUTTONS)
COMPATIBLE_IOCTL(JSIOCGNAME(0)) COMPATIBLE_IOCTL(JSIOCGNAME(0))
/* now things that need handlers */ /* now things that need handlers */
HANDLE_IOCTL(MEMREADOOB32, mtd_rw_oob)
HANDLE_IOCTL(MEMWRITEOOB32, mtd_rw_oob)
#ifdef CONFIG_NET #ifdef CONFIG_NET
HANDLE_IOCTL(SIOCGIFNAME, dev_ifname32) HANDLE_IOCTL(SIOCGIFNAME, dev_ifname32)
HANDLE_IOCTL(SIOCGIFCONF, dev_ifconf) HANDLE_IOCTL(SIOCGIFCONF, dev_ifconf)

View file

@ -196,7 +196,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
if (c->nextblock) { if (c->nextblock) {
ret = file_dirty(c, c->nextblock); ret = file_dirty(c, c->nextblock);
if (ret) if (ret)
return ret; goto out;
/* deleting summary information of the old nextblock */ /* deleting summary information of the old nextblock */
jffs2_sum_reset_collected(c->summary); jffs2_sum_reset_collected(c->summary);
} }
@ -207,7 +207,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
} else { } else {
ret = file_dirty(c, jeb); ret = file_dirty(c, jeb);
if (ret) if (ret)
return ret; goto out;
} }
break; break;

View file

@ -563,6 +563,7 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
* @options: Option flags, e.g. 16bit buswidth * @options: Option flags, e.g. 16bit buswidth
* @ecclayout: ecc layout info structure * @ecclayout: ecc layout info structure
* @part_probe_types: NULL-terminated array of probe types * @part_probe_types: NULL-terminated array of probe types
* @set_parts: platform specific function to set partitions
* @priv: hardware controller specific settings * @priv: hardware controller specific settings
*/ */
struct platform_nand_chip { struct platform_nand_chip {
@ -574,26 +575,41 @@ struct platform_nand_chip {
int chip_delay; int chip_delay;
unsigned int options; unsigned int options;
const char **part_probe_types; const char **part_probe_types;
void (*set_parts)(uint64_t size,
struct platform_nand_chip *chip);
void *priv; void *priv;
}; };
/* Keep gcc happy */
struct platform_device;
/** /**
* struct platform_nand_ctrl - controller level device structure * struct platform_nand_ctrl - controller level device structure
* @probe: platform specific function to probe/setup hardware
* @remove: platform specific function to remove/teardown hardware
* @hwcontrol: platform specific hardware control structure * @hwcontrol: platform specific hardware control structure
* @dev_ready: platform specific function to read ready/busy pin * @dev_ready: platform specific function to read ready/busy pin
* @select_chip: platform specific chip select function * @select_chip: platform specific chip select function
* @cmd_ctrl: platform specific function for controlling * @cmd_ctrl: platform specific function for controlling
* ALE/CLE/nCE. Also used to write command and address * ALE/CLE/nCE. Also used to write command and address
* @write_buf: platform specific function for write buffer
* @read_buf: platform specific function for read buffer
* @priv: private data to transport driver specific settings * @priv: private data to transport driver specific settings
* *
* All fields are optional and depend on the hardware driver requirements * All fields are optional and depend on the hardware driver requirements
*/ */
struct platform_nand_ctrl { struct platform_nand_ctrl {
int (*probe)(struct platform_device *pdev);
void (*remove)(struct platform_device *pdev);
void (*hwcontrol)(struct mtd_info *mtd, int cmd); void (*hwcontrol)(struct mtd_info *mtd, int cmd);
int (*dev_ready)(struct mtd_info *mtd); int (*dev_ready)(struct mtd_info *mtd);
void (*select_chip)(struct mtd_info *mtd, int chip); void (*select_chip)(struct mtd_info *mtd, int chip);
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, void (*cmd_ctrl)(struct mtd_info *mtd, int dat,
unsigned int ctrl); unsigned int ctrl);
void (*write_buf)(struct mtd_info *mtd,
const uint8_t *buf, int len);
void (*read_buf)(struct mtd_info *mtd,
uint8_t *buf, int len);
void *priv; void *priv;
}; };

View file

@ -17,6 +17,7 @@
#include <linux/mtd/onenand_regs.h> #include <linux/mtd/onenand_regs.h>
#include <linux/mtd/bbm.h> #include <linux/mtd/bbm.h>
#define MAX_DIES 2
#define MAX_BUFFERRAM 2 #define MAX_BUFFERRAM 2
/* Scan and identify a OneNAND device */ /* Scan and identify a OneNAND device */
@ -51,7 +52,12 @@ struct onenand_bufferram {
/** /**
* struct onenand_chip - OneNAND Private Flash Chip Data * struct onenand_chip - OneNAND Private Flash Chip Data
* @base: [BOARDSPECIFIC] address to access OneNAND * @base: [BOARDSPECIFIC] address to access OneNAND
* @dies: [INTERN][FLEX-ONENAND] number of dies on chip
* @boundary: [INTERN][FLEX-ONENAND] Boundary of the dies
* @diesize: [INTERN][FLEX-ONENAND] Size of the dies
* @chipsize: [INTERN] the size of one chip for multichip arrays * @chipsize: [INTERN] the size of one chip for multichip arrays
* FIXME For Flex-OneNAND, chipsize holds maximum possible
* device size ie when all blocks are considered MLC
* @device_id: [INTERN] device ID * @device_id: [INTERN] device ID
* @density_mask: chip density, used for DDP devices * @density_mask: chip density, used for DDP devices
* @verstion_id: [INTERN] version ID * @verstion_id: [INTERN] version ID
@ -68,6 +74,8 @@ struct onenand_bufferram {
* @command: [REPLACEABLE] hardware specific function for writing * @command: [REPLACEABLE] hardware specific function for writing
* commands to the chip * commands to the chip
* @wait: [REPLACEABLE] hardware specific function for wait on ready * @wait: [REPLACEABLE] hardware specific function for wait on ready
* @bbt_wait: [REPLACEABLE] hardware specific function for bbt wait on ready
* @unlock_all: [REPLACEABLE] hardware specific function for unlock all
* @read_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area * @read_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area
* @write_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area * @write_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area
* @read_word: [REPLACEABLE] hardware specific function for read * @read_word: [REPLACEABLE] hardware specific function for read
@ -92,9 +100,13 @@ struct onenand_bufferram {
*/ */
struct onenand_chip { struct onenand_chip {
void __iomem *base; void __iomem *base;
unsigned dies;
unsigned boundary[MAX_DIES];
loff_t diesize[MAX_DIES];
unsigned int chipsize; unsigned int chipsize;
unsigned int device_id; unsigned int device_id;
unsigned int version_id; unsigned int version_id;
unsigned int technology;
unsigned int density_mask; unsigned int density_mask;
unsigned int options; unsigned int options;
@ -108,6 +120,8 @@ struct onenand_chip {
int (*command)(struct mtd_info *mtd, int cmd, loff_t address, size_t len); int (*command)(struct mtd_info *mtd, int cmd, loff_t address, size_t len);
int (*wait)(struct mtd_info *mtd, int state); int (*wait)(struct mtd_info *mtd, int state);
int (*bbt_wait)(struct mtd_info *mtd, int state);
void (*unlock_all)(struct mtd_info *mtd);
int (*read_bufferram)(struct mtd_info *mtd, int area, int (*read_bufferram)(struct mtd_info *mtd, int area,
unsigned char *buffer, int offset, size_t count); unsigned char *buffer, int offset, size_t count);
int (*write_bufferram)(struct mtd_info *mtd, int area, int (*write_bufferram)(struct mtd_info *mtd, int area,
@ -145,6 +159,8 @@ struct onenand_chip {
#define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0) #define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0)
#define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1) #define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1)
#define FLEXONENAND(this) \
(this->device_id & DEVICE_IS_FLEXONENAND)
#define ONENAND_GET_SYS_CFG1(this) \ #define ONENAND_GET_SYS_CFG1(this) \
(this->read_word(this->base + ONENAND_REG_SYS_CFG1)) (this->read_word(this->base + ONENAND_REG_SYS_CFG1))
#define ONENAND_SET_SYS_CFG1(v, this) \ #define ONENAND_SET_SYS_CFG1(v, this) \
@ -153,6 +169,9 @@ struct onenand_chip {
#define ONENAND_IS_DDP(this) \ #define ONENAND_IS_DDP(this) \
(this->device_id & ONENAND_DEVICE_IS_DDP) (this->device_id & ONENAND_DEVICE_IS_DDP)
#define ONENAND_IS_MLC(this) \
(this->technology & ONENAND_TECHNOLOGY_IS_MLC)
#ifdef CONFIG_MTD_ONENAND_2X_PROGRAM #ifdef CONFIG_MTD_ONENAND_2X_PROGRAM
#define ONENAND_IS_2PLANE(this) \ #define ONENAND_IS_2PLANE(this) \
(this->options & ONENAND_HAS_2PLANE) (this->options & ONENAND_HAS_2PLANE)
@ -169,6 +188,7 @@ struct onenand_chip {
#define ONENAND_HAS_CONT_LOCK (0x0001) #define ONENAND_HAS_CONT_LOCK (0x0001)
#define ONENAND_HAS_UNLOCK_ALL (0x0002) #define ONENAND_HAS_UNLOCK_ALL (0x0002)
#define ONENAND_HAS_2PLANE (0x0004) #define ONENAND_HAS_2PLANE (0x0004)
#define ONENAND_SKIP_UNLOCK_CHECK (0x0100)
#define ONENAND_PAGEBUF_ALLOC (0x1000) #define ONENAND_PAGEBUF_ALLOC (0x1000)
#define ONENAND_OOBBUF_ALLOC (0x2000) #define ONENAND_OOBBUF_ALLOC (0x2000)
@ -176,6 +196,7 @@ struct onenand_chip {
* OneNAND Flash Manufacturer ID Codes * OneNAND Flash Manufacturer ID Codes
*/ */
#define ONENAND_MFR_SAMSUNG 0xec #define ONENAND_MFR_SAMSUNG 0xec
#define ONENAND_MFR_NUMONYX 0x20
/** /**
* struct onenand_manufacturers - NAND Flash Manufacturer ID Structure * struct onenand_manufacturers - NAND Flash Manufacturer ID Structure
@ -189,5 +210,8 @@ struct onenand_manufacturers {
int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops); struct mtd_oob_ops *ops);
unsigned onenand_block(struct onenand_chip *this, loff_t addr);
loff_t onenand_addr(struct onenand_chip *this, int block);
int flexonenand_region(struct mtd_info *mtd, loff_t addr);
#endif /* __LINUX_MTD_ONENAND_H */ #endif /* __LINUX_MTD_ONENAND_H */

View file

@ -67,6 +67,9 @@
/* /*
* Device ID Register F001h (R) * Device ID Register F001h (R)
*/ */
#define DEVICE_IS_FLEXONENAND (1 << 9)
#define FLEXONENAND_PI_MASK (0x3ff)
#define FLEXONENAND_PI_UNLOCK_SHIFT (14)
#define ONENAND_DEVICE_DENSITY_MASK (0xf) #define ONENAND_DEVICE_DENSITY_MASK (0xf)
#define ONENAND_DEVICE_DENSITY_SHIFT (4) #define ONENAND_DEVICE_DENSITY_SHIFT (4)
#define ONENAND_DEVICE_IS_DDP (1 << 3) #define ONENAND_DEVICE_IS_DDP (1 << 3)
@ -83,6 +86,11 @@
*/ */
#define ONENAND_VERSION_PROCESS_SHIFT (8) #define ONENAND_VERSION_PROCESS_SHIFT (8)
/*
* Technology Register F006h (R)
*/
#define ONENAND_TECHNOLOGY_IS_MLC (1 << 0)
/* /*
* Start Address 1 F100h (R/W) & Start Address 2 F101h (R/W) * Start Address 1 F100h (R/W) & Start Address 2 F101h (R/W)
*/ */
@ -93,7 +101,8 @@
/* /*
* Start Address 8 F107h (R/W) * Start Address 8 F107h (R/W)
*/ */
#define ONENAND_FPA_MASK (0x3f) /* Note: It's actually 0x3f in case of SLC */
#define ONENAND_FPA_MASK (0x7f)
#define ONENAND_FPA_SHIFT (2) #define ONENAND_FPA_SHIFT (2)
#define ONENAND_FSA_MASK (0x03) #define ONENAND_FSA_MASK (0x03)
@ -105,7 +114,8 @@
#define ONENAND_BSA_BOOTRAM (0 << 2) #define ONENAND_BSA_BOOTRAM (0 << 2)
#define ONENAND_BSA_DATARAM0 (2 << 2) #define ONENAND_BSA_DATARAM0 (2 << 2)
#define ONENAND_BSA_DATARAM1 (3 << 2) #define ONENAND_BSA_DATARAM1 (3 << 2)
#define ONENAND_BSC_MASK (0x03) /* Note: It's actually 0x03 in case of SLC */
#define ONENAND_BSC_MASK (0x07)
/* /*
* Command Register F220h (R/W) * Command Register F220h (R/W)
@ -124,9 +134,13 @@
#define ONENAND_CMD_RESET (0xF0) #define ONENAND_CMD_RESET (0xF0)
#define ONENAND_CMD_OTP_ACCESS (0x65) #define ONENAND_CMD_OTP_ACCESS (0x65)
#define ONENAND_CMD_READID (0x90) #define ONENAND_CMD_READID (0x90)
#define FLEXONENAND_CMD_PI_UPDATE (0x05)
#define FLEXONENAND_CMD_PI_ACCESS (0x66)
#define FLEXONENAND_CMD_RECOVER_LSB (0x05)
/* NOTE: Those are not *REAL* commands */ /* NOTE: Those are not *REAL* commands */
#define ONENAND_CMD_BUFFERRAM (0x1978) #define ONENAND_CMD_BUFFERRAM (0x1978)
#define FLEXONENAND_CMD_READ_PI (0x1985)
/* /*
* System Configuration 1 Register F221h (R, R/W) * System Configuration 1 Register F221h (R, R/W)
@ -192,10 +206,12 @@
#define ONENAND_ECC_1BIT_ALL (0x5555) #define ONENAND_ECC_1BIT_ALL (0x5555)
#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)
/* /*
* One-Time Programmable (OTP) * One-Time Programmable (OTP)
*/ */
#define FLEXONENAND_OTP_LOCK_OFFSET (2048)
#define ONENAND_OTP_LOCK_OFFSET (14) #define ONENAND_OTP_LOCK_OFFSET (14)
#endif /* __ONENAND_REG_H */ #endif /* __ONENAND_REG_H */

View file

@ -40,7 +40,6 @@ struct mtd_partition {
uint64_t offset; /* offset within the master MTD space */ uint64_t offset; /* offset within the master MTD space */
uint32_t mask_flags; /* master MTD flags to mask out for this partition */ uint32_t mask_flags; /* master MTD flags to mask out for this partition */
struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/
struct mtd_info **mtdp; /* pointer to store the MTD object */
}; };
#define MTDPART_OFS_NXTBLK (-2) #define MTDPART_OFS_NXTBLK (-2)

View file

@ -1,5 +1,4 @@
header-y += inftl-user.h header-y += inftl-user.h
header-y += jffs2-user.h
header-y += mtd-abi.h header-y += mtd-abi.h
header-y += mtd-user.h header-y += mtd-user.h
header-y += nftl-user.h header-y += nftl-user.h

View file

@ -1,34 +0,0 @@
/*
* JFFS2 definitions for use in user space only
*/
#ifndef __JFFS2_USER_H__
#define __JFFS2_USER_H__
/* This file is blessed for inclusion by userspace */
#include <linux/jffs2.h>
#include <linux/types.h>
#include <endian.h>
#include <byteswap.h>
#undef cpu_to_je16
#undef cpu_to_je32
#undef cpu_to_jemode
#undef je16_to_cpu
#undef je32_to_cpu
#undef jemode_to_cpu
extern int target_endian;
#define t16(x) ({ __u16 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
#define t32(x) ({ __u32 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
#define cpu_to_je16(x) ((jint16_t){t16(x)})
#define cpu_to_je32(x) ((jint32_t){t32(x)})
#define cpu_to_jemode(x) ((jmode_t){t32(x)})
#define je16_to_cpu(x) (t16((x).v16))
#define je32_to_cpu(x) (t32((x).v32))
#define jemode_to_cpu(x) (t32((x).m))
#endif /* __JFFS2_USER_H__ */

View file

@ -12,12 +12,24 @@ struct erase_info_user {
__u32 length; __u32 length;
}; };
struct erase_info_user64 {
__u64 start;
__u64 length;
};
struct mtd_oob_buf { struct mtd_oob_buf {
__u32 start; __u32 start;
__u32 length; __u32 length;
unsigned char __user *ptr; unsigned char __user *ptr;
}; };
struct mtd_oob_buf64 {
__u64 start;
__u32 pad;
__u32 length;
__u64 usr_ptr;
};
#define MTD_ABSENT 0 #define MTD_ABSENT 0
#define MTD_RAM 1 #define MTD_RAM 1
#define MTD_ROM 2 #define MTD_ROM 2
@ -95,6 +107,9 @@ struct otp_info {
#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) #define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout)
#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) #define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
#define MTDFILEMODE _IO('M', 19) #define MTDFILEMODE _IO('M', 19)
#define MEMERASE64 _IOW('M', 20, struct erase_info_user64)
#define MEMWRITEOOB64 _IOWR('M', 21, struct mtd_oob_buf64)
#define MEMREADOOB64 _IOWR('M', 22, struct mtd_oob_buf64)
/* /*
* Obsolete legacy interface. Keep it in order not to break userspace * Obsolete legacy interface. Keep it in order not to break userspace