Commit ac1b7c37 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 9e268beb c90173f0
What: /sys/class/mtd/
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
The mtd/ class subdirectory belongs to the MTD subsystem
(MTD core).
What: /sys/class/mtd/mtdX/
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
The /sys/class/mtd/mtd{0,1,2,3,...} directories correspond
to each /dev/mtdX character device. These may represent
physical/simulated flash devices, partitions on a flash
device, or concatenated flash devices. They exist regardless
of whether CONFIG_MTD_CHAR is actually enabled.
What: /sys/class/mtd/mtdXro/
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
These directories provide the corresponding read-only device
nodes for /sys/class/mtd/mtdX/ . They are only created
(for the benefit of udev) if CONFIG_MTD_CHAR is enabled.
What: /sys/class/mtd/mtdX/dev
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
Major and minor numbers of the character device corresponding
to this MTD device (in <major>:<minor> format). This is the
read-write device so <minor> will be even.
What: /sys/class/mtd/mtdXro/dev
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
Major and minor numbers of the character device corresponding
to the read-only variant of thie MTD device (in
<major>:<minor> format). In this case <minor> will be odd.
What: /sys/class/mtd/mtdX/erasesize
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
"Major" erase size for the device. If numeraseregions is
zero, this is the eraseblock size for the entire device.
Otherwise, the MEMGETREGIONCOUNT/MEMGETREGIONINFO ioctls
can be used to determine the actual eraseblock layout.
What: /sys/class/mtd/mtdX/flags
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
A hexadecimal value representing the device flags, ORed
together:
0x0400: MTD_WRITEABLE - device is writable
0x0800: MTD_BIT_WRITEABLE - single bits can be flipped
0x1000: MTD_NO_ERASE - no erase necessary
0x2000: MTD_POWERUP_LOCK - always locked after reset
What: /sys/class/mtd/mtdX/name
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
A human-readable ASCII name for the device or partition.
This will match the name in /proc/mtd .
What: /sys/class/mtd/mtdX/numeraseregions
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
For devices that have variable eraseblock sizes, this
provides the total number of erase regions. Otherwise,
it will read back as zero.
What: /sys/class/mtd/mtdX/oobsize
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
Number of OOB bytes per page.
What: /sys/class/mtd/mtdX/size
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
Total size of the device/partition, in bytes.
What: /sys/class/mtd/mtdX/type
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
One of the following ASCII strings, representing the device
type:
absent, ram, rom, nor, nand, dataflash, ubi, unknown
What: /sys/class/mtd/mtdX/writesize
Date: April 2009
KernelVersion: 2.6.29
Contact: linux-mtd@lists.infradead.org
Description:
Minimal writable flash unit size. This will always be
a positive integer.
In the case of NOR flash it is 1 (even though individual
bits can be cleared).
In the case of NAND flash it is one NAND page (or a
half page, or a quarter page).
In the case of ECC NOR, it is the ECC block size.
...@@ -1431,6 +1431,16 @@ and is between 256 and 4096 characters. It is defined in the file ...@@ -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
......
...@@ -3252,7 +3252,6 @@ W: http://www.linux-mtd.infradead.org/doc/jffs2.html ...@@ -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
......
...@@ -68,10 +68,14 @@ struct davinci_nand_pdata { /* platform_data */ ...@@ -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;
......
...@@ -10,19 +10,26 @@ ...@@ -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 { ...@@ -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;
......
...@@ -46,6 +46,7 @@ ...@@ -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[] = { ...@@ -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) ...@@ -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) ...@@ -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;
......
...@@ -166,6 +166,7 @@ ...@@ -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[] = { ...@@ -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,
......
...@@ -500,6 +500,9 @@ static struct flash_info __devinitdata m25p_data [] = { ...@@ -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 [] = { ...@@ -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, },
......
...@@ -105,15 +105,6 @@ config MSP_FLASH_MAP_LIMIT ...@@ -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 ...@@ -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 ...@@ -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.
......
...@@ -25,7 +25,6 @@ obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o ...@@ -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
......
...@@ -40,6 +40,9 @@ struct async_state { ...@@ -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) ...@@ -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) ...@@ -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);
......
...@@ -36,27 +36,33 @@ ...@@ -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)
#endif
struct armflash_info { struct armflash_subdev_info {
char name[SUBDEV_NAME_SIZE];
struct mtd_info *mtd;
struct map_info map;
struct flash_platform_data *plat; struct flash_platform_data *plat;
};
struct armflash_info {
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) ...@@ -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) ...@@ -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) ...@@ -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)
mtd_concat_destroy(info->mtd);
#endif
} }
kfree(info->parts); kfree(info->parts);
subdev_err:
no_device: for (i = info->nr_subdev - 1; i >= 0; i--)
iounmap(base); armflash_subdev_remove(&info->subdev[i]);
no_mem:
release_mem_region(res->start, size);
no_resource: no_resource:
if (plat && plat->exit) if (plat && plat->exit)
plat->exit(); plat->exit();
kfree(info); kfree(info);
}
out: out:
return err; return err;
} }
...@@ -160,22 +258,26 @@ static int armflash_probe(struct platform_device *dev) ...@@ -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);
} }
......
...@@ -195,42 +195,6 @@ static int physmap_flash_probe(struct platform_device *dev) ...@@ -195,42 +195,6 @@ static int physmap_flash_probe(struct platform_device *dev)
} }
#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) ...@@ -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",
......
...@@ -20,16 +20,23 @@ ...@@ -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 { struct of_flash_list {
struct mtd_info *mtd; struct mtd_info *mtd;
struct map_info map; struct map_info map;
struct resource *res; struct resource *res;
};
struct of_flash {
struct mtd_info *cmtd;
#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,29 +95,43 @@ static int parse_obsolete_partitions(struct of_device *dev, ...@@ -88,29 +95,43 @@ 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->list[i].map.virt)
iounmap(info->list[i].map.virt);
if (info->res) { if (info->list[i].res) {
release_resource(info->res); release_resource(info->list[i].res);
kfree(info->res); kfree(info->list[i].res);
} }
}
kfree(info);
return 0; return 0;
} }
...@@ -164,67 +185,129 @@ static int __devinit of_flash_probe(struct of_device *dev, ...@@ -164,67 +185,129 @@ 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;
err = -ENXIO; int count;
if (of_address_to_resource(dp, 0, &res)) { const u32 *p;
dev_err(&dev->dev, "Can't get IO address from device tree\n"); int reg_tuple_size;
struct mtd_info **mtd_list = NULL;
reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
/*
* Get number of "reg" tuples. Scan for MTD devices on area's
* described by each "reg" region. This makes it possible (including
* the concat support) to support the Intel P30 48F4400 chips which
* consists internally of 2 non-identical NOR chips on one die.
*/
p = of_get_property(dp, "reg", &count);
if (count % reg_tuple_size != 0) {
dev_err(&dev->dev, "Malformed reg property on %s\n",
dev->node->full_name);
err = -EINVAL;
goto err_out; 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);
for (i = 0; i < count; i++) {
err = -ENXIO;
if (of_address_to_resource(dp, i, &res)) {
dev_err(&dev->dev, "Can't get IO address from device"
" tree\n");
goto err_out;
}
dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
(unsigned long long)res.start,
(unsigned long long)res.end);
err = -EBUSY; err = -EBUSY;
info->res = request_mem_region(res.start, res.end - res.start + 1, info->list[i].res = request_mem_region(res.start, res.end -
res.start + 1,
dev_name(&dev->dev)); dev_name(&dev->dev));
if (!info->res) if (!info->list[i].res)
goto err_out; goto err_out;
err = -ENXIO; err = -ENXIO;
width = of_get_property(dp, "bank-width", NULL); width = of_get_property(dp, "bank-width", NULL);
if (!width) { if (!width) {
dev_err(&dev->dev, "Can't get bank width from device tree\n"); dev_err(&dev->dev, "Can't get bank width from device"
" tree\n");
goto err_out; goto err_out;
} }
info->map.name = dev_name(&dev->dev); info->list[i].map.name = dev_name(&dev->dev);
info->map.phys = res.start; info->list[i].map.phys = res.start;
info->map.size = res.end - res.start + 1; info->list[i].map.size = res.end - res.start + 1;
info->map.bankwidth = *width; info->list[i].map.bankwidth = *width;
err = -ENOMEM; err = -ENOMEM;
info->map.virt = ioremap(info->map.phys, info->map.size); info->list[i].map.virt = ioremap(info->list[i].map.phys,
if (!info->map.virt) { info->list[i].map.size);
dev_err(&dev->dev, "Failed to ioremap() flash region\n"); if (!info->list[i].map.virt) {
dev_err(&dev->dev, "Failed to ioremap() flash"
" region\n");
goto err_out; goto err_out;
} }
simple_map_init(&info->map); simple_map_init(&info->list[i].map);
if (probe_type) if (probe_type) {
info->mtd = do_map_probe(probe_type, &info->map); info->list[i].mtd = do_map_probe(probe_type,
else &info->list[i].map);
info->mtd = obsolete_probe(dev, &info->map); } else {
info->list[i].mtd = obsolete_probe(dev,
&info->list[i].map);
}
mtd_list[i] = info->list[i].mtd;
err = -ENXIO; err = -ENXIO;
if (!info->mtd) { if (!info->list[i].mtd) {
dev_err(&dev->dev, "do_map_probe() failed\n"); dev_err(&dev->dev, "do_map_probe() failed\n");
goto err_out; goto err_out;
} else {
info->list_size++;
}
info->list[i].mtd->owner = THIS_MODULE;
info->list[i].mtd->dev.parent = &dev->dev;
} }
info->mtd->owner = THIS_MODULE;
info->mtd->dev.parent = &dev->dev; err = 0;
if (info->list_size == 1) {
info->cmtd = info->list[0].mtd;
} else if (info->list_size > 1) {
/*
* We detected multiple devices. Concatenate them together.
*/
#ifdef CONFIG_MTD_CONCAT
info->cmtd = mtd_concat_create(mtd_list, info->list_size,
dev_name(&dev->dev));
if (info->cmtd == NULL)
err = -ENXIO;
#else
printk(KERN_ERR "physmap_of: multiple devices "
"found but MTD concat support disabled.\n");
err = -ENXIO;
#endif
}
if (err)
goto err_out;
#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, ...@@ -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;
} }
......
/*
* Mapping of the rootfs in a physical region of memory
*
* Copyright (C) 2005-2007 PMC-Sierra Inc.
* Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.com
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/root_dev.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <asm/io.h>
#include <msp_prom.h>
static struct mtd_info *rr_mtd;
struct map_info rr_map = {
.name = "ramroot",
.bankwidth = 4,
};
static int __init init_rrmap(void)
{
void *ramroot_start;
unsigned long ramroot_size;
/* Check for supported rootfs types */
if (get_ramroot(&ramroot_start, &ramroot_size)) {
rr_map.phys = CPHYSADDR(ramroot_start);
rr_map.size = ramroot_size;
printk(KERN_NOTICE
"PMC embedded root device: 0x%08lx @ 0x%08lx\n",
rr_map.size, (unsigned long)rr_map.phys);
} else {
printk(KERN_ERR
"init_rrmap: no supported embedded rootfs detected!\n");
return -ENXIO;
}
/* Map rootfs to I/O space for block device driver */
rr_map.virt = ioremap(rr_map.phys, rr_map.size);
if (!rr_map.virt) {
printk(KERN_ERR "Failed to ioremap\n");
return -EIO;
}
simple_map_init(&rr_map);
rr_mtd = do_map_probe("map_ram", &rr_map);
if (rr_mtd) {
rr_mtd->owner = THIS_MODULE;
add_mtd_device(rr_mtd);
return 0;
}
iounmap(rr_map.virt);
return -ENXIO;
}
static void __exit cleanup_rrmap(void)
{
del_mtd_device(rr_mtd);
map_destroy(rr_mtd);
iounmap(rr_map.virt);
rr_map.virt = NULL;
}
MODULE_AUTHOR("PMC-Sierra, Inc");
MODULE_DESCRIPTION("MTD map driver for embedded PMC-Sierra MSP filesystem");
MODULE_LICENSE("GPL");
module_init(init_rrmap);
module_exit(cleanup_rrmap);
...@@ -140,24 +140,6 @@ static int __devexit pxa2xx_flash_remove(struct platform_device *dev) ...@@ -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) ...@@ -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 = { ...@@ -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,
}; };
......
...@@ -145,25 +145,6 @@ static int rbtx4939_flash_probe(struct platform_device *dev) ...@@ -145,25 +145,6 @@ static int rbtx4939_flash_probe(struct platform_device *dev)
} }
#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) ...@@ -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",
......
...@@ -415,25 +415,6 @@ static int __exit sa1100_mtd_remove(struct platform_device *pdev) ...@@ -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) ...@@ -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",
......
...@@ -22,15 +22,19 @@ ...@@ -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[] = { ...@@ -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) ...@@ -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",
......
...@@ -291,7 +291,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) ...@@ -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);
......
...@@ -14,6 +14,7 @@ ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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) {
struct erase_info_user64 einfo64;
if (copy_from_user(&einfo64, argp,
sizeof(struct erase_info_user64))) {
kfree(erase);
return -EFAULT;
}
erase->addr = einfo64.start;
erase->len = einfo64.length;
} else {
struct erase_info_user einfo32;
if (copy_from_user(&einfo32, argp,
sizeof(struct erase_info_user))) { sizeof(struct erase_info_user))) {
kfree(erase); kfree(erase);
return -EFAULT; return -EFAULT;
} }
erase->addr = einfo.start; erase->addr = einfo32.start;
erase->len = einfo.length; erase->len = einfo32.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, ...@@ -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))
return -EPERM;
if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
return -EFAULT;
if (buf.length > 4096)
return -EINVAL;
if (!mtd->write_oob)
ret = -EOPNOTSUPP;
else
ret = access_ok(VERIFY_READ, buf.ptr,
buf.length) ? 0 : EFAULT;
if (ret) /* NOTE: writes return length to buf_user->length */
return ret; if (copy_from_user(&buf, argp, sizeof(buf)))
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)))
return -EFAULT;
if (buf.length > 4096)
return -EINVAL;
if (!mtd->read_oob) /* NOTE: writes return length to buf_user->start */
ret = -EOPNOTSUPP; if (copy_from_user(&buf, argp, sizeof(buf)))
ret = -EFAULT;
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;
ops.ooboffs = buf.start & (mtd->oobsize - 1);
ops.datbuf = NULL;
ops.mode = MTD_OOB_PLACE;
if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) case MEMWRITEOOB64:
return -EINVAL; {
struct mtd_oob_buf64 buf;
struct mtd_oob_buf64 __user *buf_user = argp;
ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); if (copy_from_user(&buf, argp, sizeof(buf)))
if (!ops.oobbuf) ret = -EFAULT;
return -ENOMEM; else
ret = mtd_do_writeoob(file, mtd, buf.start, buf.length,
(void __user *)(uintptr_t)buf.usr_ptr,
&buf_user->length);
break;
}
buf.start &= ~(mtd->oobsize - 1); case MEMREADOOB64:
ret = mtd->read_oob(mtd, buf.start, &ops); {
struct mtd_oob_buf64 buf;
struct mtd_oob_buf64 __user *buf_user = argp;
if (put_user(ops.oobretlen, (uint32_t __user *)argp)) if (copy_from_user(&buf, argp, sizeof(buf)))
ret = -EFAULT;
else if (ops.oobretlen && copy_to_user(buf.ptr, ops.oobbuf,
ops.oobretlen))
ret = -EFAULT; ret = -EFAULT;
else
kfree(ops.oobbuf); 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, ...@@ -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 = { ...@@ -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,
......
...@@ -23,8 +23,15 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ static int mtd_read_proc (char *page, char **start, off_t off, int count, ...@@ -604,11 +630,12 @@ static int mtd_read_proc (char *page, char **start, off_t off, int count,
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) ...@@ -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);
......
...@@ -27,9 +27,7 @@ struct mtd_part { ...@@ -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,7 +319,6 @@ int del_mtd_partitions(struct mtd_info *master) ...@@ -321,7 +319,6 @@ 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, ...@@ -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, ...@@ -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, ...@@ -500,15 +496,9 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
} }
out_register: out_register:
if (part->mtdp) {
/* store the object pointer (caller may or may not register it*/
*part->mtdp = &slave->mtd;
slave->registered = 0;
} else {
/* register our partition */ /* register our partition */
add_mtd_device(&slave->mtd); add_mtd_device(&slave->mtd);
slave->registered = 1;
}
return slave; return slave;
} }
......
...@@ -74,6 +74,12 @@ config MTD_NAND_AMS_DELTA ...@@ -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 ...@@ -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 ...@@ -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
......
...@@ -25,6 +25,7 @@ obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o ...@@ -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
......
...@@ -24,6 +24,7 @@ ...@@ -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 @@ ...@@ -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) ...@@ -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;
......
...@@ -458,7 +458,7 @@ static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id) ...@@ -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, ...@@ -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, ...@@ -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,
......
...@@ -44,7 +44,7 @@ ...@@ -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 @@ ...@@ -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 { ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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:
if (pdata->ecc_bits == 4) {
/* No sanity checks: CPUs must support this,
* and the chips may not use NAND_BUSWIDTH_16.
*/
/* No sharing 4-bit hardware between chipselects yet */
spin_lock_irq(&davinci_nand_lock);
if (ecc4_busy)
ret = -EBUSY;
else
ecc4_busy = true;
spin_unlock_irq(&davinci_nand_lock);
if (ret == -EBUSY)
goto err_ecc;
info->chip.ecc.calculate = nand_davinci_calculate_4bit;
info->chip.ecc.correct = nand_davinci_correct_4bit;
info->chip.ecc.hwctl = nand_davinci_hwctl_4bit;
info->chip.ecc.bytes = 10;
} else {
info->chip.ecc.calculate = nand_davinci_calculate_1bit; info->chip.ecc.calculate = nand_davinci_calculate_1bit;
info->chip.ecc.correct = nand_davinci_correct_1bit; info->chip.ecc.correct = nand_davinci_correct_1bit;
info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
info->chip.ecc.size = 512;
info->chip.ecc.bytes = 3; info->chip.ecc.bytes = 3;
break; }
case NAND_ECC_HW_SYNDROME:
/* FIXME implement */
info->chip.ecc.size = 512; info->chip.ecc.size = 512;
info->chip.ecc.bytes = 10; break;
dev_warn(&pdev->dev, "4-bit ECC nyet supported\n");
/* FALL THROUGH */
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) ...@@ -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) ...@@ -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) ...@@ -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 @@ static int __init nand_davinci_probe(struct platform_device *pdev) ...@@ -509,6 +761,11 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
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) ...@@ -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);
......
...@@ -138,7 +138,14 @@ static struct nand_ecclayout nand_hw_eccoob_8 = { ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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;
}
host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0;
/* Scan to find existence of the device */ if (this->ecc.mode == NAND_ECC_HW) {
if (nand_scan(mtd, 1)) { switch (mtd->oobsize) {
DEBUG(MTD_DEBUG_LEVEL0, case 8:
"MXC_ND: Unable to find any NAND device.\n"); 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) ...@@ -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) ...@@ -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);
......
...@@ -2756,7 +2756,8 @@ int nand_scan_tail(struct mtd_info *mtd) ...@@ -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;
......
...@@ -428,8 +428,8 @@ EXPORT_SYMBOL(nand_calculate_ecc); ...@@ -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;
......
/*
* Copyright © 2004 Texas Instruments, Jian Zhang <jzhang@ti.com>
* Copyright © 2004 Micron Technology Inc.
* Copyright © 2004 David Brownell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/io.h>
#include <asm/dma.h>
#include <mach/gpmc.h>
#include <mach/nand.h>
#define GPMC_IRQ_STATUS 0x18
#define GPMC_ECC_CONFIG 0x1F4
#define GPMC_ECC_CONTROL 0x1F8
#define GPMC_ECC_SIZE_CONFIG 0x1FC
#define GPMC_ECC1_RESULT 0x200
#define DRIVER_NAME "omap2-nand"
/* size (4 KiB) for IO mapping */
#define NAND_IO_SIZE SZ_4K
#define NAND_WP_OFF 0
#define NAND_WP_BIT 0x00000010
#define WR_RD_PIN_MONITORING 0x00600000
#define GPMC_BUF_FULL 0x00000001
#define GPMC_BUF_EMPTY 0x00000000
#define NAND_Ecc_P1e (1 << 0)
#define NAND_Ecc_P2e (1 << 1)
#define NAND_Ecc_P4e (1 << 2)
#define NAND_Ecc_P8e (1 << 3)
#define NAND_Ecc_P16e (1 << 4)
#define NAND_Ecc_P32e (1 << 5)
#define NAND_Ecc_P64e (1 << 6)
#define NAND_Ecc_P128e (1 << 7)
#define NAND_Ecc_P256e (1 << 8)
#define NAND_Ecc_P512e (1 << 9)
#define NAND_Ecc_P1024e (1 << 10)
#define NAND_Ecc_P2048e (1 << 11)
#define NAND_Ecc_P1o (1 << 16)
#define NAND_Ecc_P2o (1 << 17)
#define NAND_Ecc_P4o (1 << 18)
#define NAND_Ecc_P8o (1 << 19)
#define NAND_Ecc_P16o (1 << 20)
#define NAND_Ecc_P32o (1 << 21)
#define NAND_Ecc_P64o (1 << 22)
#define NAND_Ecc_P128o (1 << 23)
#define NAND_Ecc_P256o (1 << 24)
#define NAND_Ecc_P512o (1 << 25)
#define NAND_Ecc_P1024o (1 << 26)
#define NAND_Ecc_P2048o (1 << 27)
#define TF(value) (value ? 1 : 0)
#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0)
#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1)
#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2)
#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3)
#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4)
#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5)
#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6)
#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7)
#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0)
#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1)
#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2)
#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3)
#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4)
#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5)
#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6)
#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7)
#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0)
#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1)
#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2)
#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3)
#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4)
#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5)
#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6)
#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7)
#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0)
#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1)
#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2)
#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3)
#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4)
#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5)
#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6)
#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7)
#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0)
#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1)
#ifdef CONFIG_MTD_PARTITIONS
static const char *part_probes[] = { "cmdlinepart", NULL };
#endif
struct omap_nand_info {
struct nand_hw_control controller;
struct omap_nand_platform_data *pdata;
struct mtd_info mtd;
struct mtd_partition *parts;
struct nand_chip nand;
struct platform_device *pdev;
int gpmc_cs;
unsigned long phys_base;
void __iomem *gpmc_cs_baseaddr;
void __iomem *gpmc_baseaddr;
};
/**
* omap_nand_wp - This function enable or disable the Write Protect feature
* @mtd: MTD device structure
* @mode: WP ON/OFF
*/
static void omap_nand_wp(struct mtd_info *mtd, int mode)
{
struct omap_nand_info *info = container_of(mtd,
struct omap_nand_info, mtd);
unsigned long config = __raw_readl(info->gpmc_baseaddr + GPMC_CONFIG);
if (mode)
config &= ~(NAND_WP_BIT); /* WP is ON */
else
config |= (NAND_WP_BIT); /* WP is OFF */
__raw_writel(config, (info->gpmc_baseaddr + GPMC_CONFIG));
}
/**
* omap_hwcontrol - hardware specific access to control-lines
* @mtd: MTD device structure
* @cmd: command to device
* @ctrl:
* NAND_NCE: bit 0 -> don't care
* NAND_CLE: bit 1 -> Command Latch
* NAND_ALE: bit 2 -> Address Latch
*
* NOTE: boards may use different bits for these!!
*/
static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct omap_nand_info *info = container_of(mtd,
struct omap_nand_info, mtd);
switch (ctrl) {
case NAND_CTRL_CHANGE | NAND_CTRL_CLE:
info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
GPMC_CS_NAND_COMMAND;
info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
GPMC_CS_NAND_DATA;
break;
case NAND_CTRL_CHANGE | NAND_CTRL_ALE:
info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
GPMC_CS_NAND_ADDRESS;
info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
GPMC_CS_NAND_DATA;
break;
case NAND_CTRL_CHANGE | NAND_NCE:
info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
GPMC_CS_NAND_DATA;
info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
GPMC_CS_NAND_DATA;
break;
}
if (cmd != NAND_CMD_NONE)
__raw_writeb(cmd, info->nand.IO_ADDR_W);
}
/**
* omap_read_buf16 - read data from NAND controller into buffer
* @mtd: MTD device structure
* @buf: buffer to store date
* @len: number of bytes to read
*/
static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
{
struct nand_chip *nand = mtd->priv;
__raw_readsw(nand->IO_ADDR_R, buf, len / 2);
}
/**
* omap_write_buf16 - write buffer to NAND controller
* @mtd: MTD device structure
* @buf: data buffer
* @len: number of bytes to write
*/
static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
{
struct omap_nand_info *info = container_of(mtd,
struct omap_nand_info, mtd);
u16 *p = (u16 *) buf;
/* FIXME try bursts of writesw() or DMA ... */
len >>= 1;
while (len--) {
writew(*p++, info->nand.IO_ADDR_W);
while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr +
GPMC_STATUS) & GPMC_BUF_FULL))
;
}
}
/**
* omap_verify_buf - Verify chip data against buffer
* @mtd: MTD device structure
* @buf: buffer containing the data to compare
* @len: number of bytes to compare
*/
static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len)
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
u16 *p = (u16 *) buf;
len >>= 1;
while (len--) {
if (*p++ != cpu_to_le16(readw(info->nand.IO_ADDR_R)))
return -EFAULT;
}
return 0;
}
#ifdef CONFIG_MTD_NAND_OMAP_HWECC
/**
* omap_hwecc_init - Initialize the HW ECC for NAND flash in GPMC controller
* @mtd: MTD device structure
*/
static void omap_hwecc_init(struct mtd_info *mtd)
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
struct nand_chip *chip = mtd->priv;
unsigned long val = 0x0;
/* Read from ECC Control Register */
val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONTROL);
/* Clear all ECC | Enable Reg1 */
val = ((0x00000001<<8) | 0x00000001);
__raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
/* Read from ECC Size Config Register */
val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
/* ECCSIZE1=512 | Select eccResultsize[0-3] */
val = ((((chip->ecc.size >> 1) - 1) << 22) | (0x0000000F));
__raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
}
/**
* gen_true_ecc - This function will generate true ECC value
* @ecc_buf: buffer to store ecc code
*
* This generated true ECC value can be used when correcting
* data read from NAND flash memory core
*/
static void gen_true_ecc(u8 *ecc_buf)
{
u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) |
((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8);
ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) |
P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp));
ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) |
P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp));
ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) |
P1e(tmp) | P2048o(tmp) | P2048e(tmp));
}
/**
* omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data
* @ecc_data1: ecc code from nand spare area
* @ecc_data2: ecc code from hardware register obtained from hardware ecc
* @page_data: page data
*
* This function compares two ECC's and indicates if there is an error.
* If the error can be corrected it will be corrected to the buffer.
*/
static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
u8 *ecc_data2, /* read from register */
u8 *page_data)
{
uint i;
u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8];
u8 comp0_bit[8], comp1_bit[8], comp2_bit[8];
u8 ecc_bit[24];
u8 ecc_sum = 0;
u8 find_bit = 0;
uint find_byte = 0;
int isEccFF;
isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF);
gen_true_ecc(ecc_data1);
gen_true_ecc(ecc_data2);
for (i = 0; i <= 2; i++) {
*(ecc_data1 + i) = ~(*(ecc_data1 + i));
*(ecc_data2 + i) = ~(*(ecc_data2 + i));
}
for (i = 0; i < 8; i++) {
tmp0_bit[i] = *ecc_data1 % 2;
*ecc_data1 = *ecc_data1 / 2;
}
for (i = 0; i < 8; i++) {
tmp1_bit[i] = *(ecc_data1 + 1) % 2;
*(ecc_data1 + 1) = *(ecc_data1 + 1) / 2;
}
for (i = 0; i < 8; i++) {
tmp2_bit[i] = *(ecc_data1 + 2) % 2;
*(ecc_data1 + 2) = *(ecc_data1 + 2) / 2;
}
for (i = 0; i < 8; i++) {
comp0_bit[i] = *ecc_data2 % 2;
*ecc_data2 = *ecc_data2 / 2;
}
for (i = 0; i < 8; i++) {
comp1_bit[i] = *(ecc_data2 + 1) % 2;
*(ecc_data2 + 1) = *(ecc_data2 + 1) / 2;
}
for (i = 0; i < 8; i++) {
comp2_bit[i] = *(ecc_data2 + 2) % 2;
*(ecc_data2 + 2) = *(ecc_data2 + 2) / 2;
}
for (i = 0; i < 6; i++)
ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2];
for (i = 0; i < 8; i++)
ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i];
for (i = 0; i < 8; i++)
ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i];
ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0];
ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1];
for (i = 0; i < 24; i++)
ecc_sum += ecc_bit[i];
switch (ecc_sum) {
case 0:
/* Not reached because this function is not called if
* ECC values are equal
*/
return 0;
case 1:
/* Uncorrectable error */
DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n");
return -1;
case 11:
/* UN-Correctable error */
DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR B\n");
return -1;
case 12:
/* Correctable error */
find_byte = (ecc_bit[23] << 8) +
(ecc_bit[21] << 7) +
(ecc_bit[19] << 6) +
(ecc_bit[17] << 5) +
(ecc_bit[15] << 4) +
(ecc_bit[13] << 3) +
(ecc_bit[11] << 2) +
(ecc_bit[9] << 1) +
ecc_bit[7];
find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1];
DEBUG(MTD_DEBUG_LEVEL0, "Correcting single bit ECC error at "
"offset: %d, bit: %d\n", find_byte, find_bit);
page_data[find_byte] ^= (1 << find_bit);
return 0;
default:
if (isEccFF) {
if (ecc_data2[0] == 0 &&
ecc_data2[1] == 0 &&
ecc_data2[2] == 0)
return 0;
}
DEBUG(MTD_DEBUG_LEVEL0, "UNCORRECTED_ERROR default\n");
return -1;
}
}
/**
* omap_correct_data - Compares the ECC read with HW generated ECC
* @mtd: MTD device structure
* @dat: page data
* @read_ecc: ecc read from nand flash
* @calc_ecc: ecc read from HW ECC registers
*
* Compares the ecc read from nand spare area with ECC registers values
* and if ECC's mismached, it will call 'omap_compare_ecc' for error detection
* and correction.
*/
static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
int blockCnt = 0, i = 0, ret = 0;
/* Ex NAND_ECC_HW12_2048 */
if ((info->nand.ecc.mode == NAND_ECC_HW) &&
(info->nand.ecc.size == 2048))
blockCnt = 4;
else
blockCnt = 1;
for (i = 0; i < blockCnt; i++) {
if (memcmp(read_ecc, calc_ecc, 3) != 0) {
ret = omap_compare_ecc(read_ecc, calc_ecc, dat);
if (ret < 0)
return ret;
}
read_ecc += 3;
calc_ecc += 3;
dat += 512;
}
return 0;
}
/**
* omap_calcuate_ecc - Generate non-inverted ECC bytes.
* @mtd: MTD device structure
* @dat: The pointer to data on which ecc is computed
* @ecc_code: The ecc_code buffer
*
* Using noninverted ECC can be considered ugly since writing a blank
* page ie. padding will clear the ECC bytes. This is no problem as long
* nobody is trying to write data on the seemingly unused page. Reading
* an erased page will produce an ECC mismatch between generated and read
* ECC bytes that has to be dealt with separately.
*/
static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u_char *ecc_code)
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
unsigned long val = 0x0;
unsigned long reg;
/* Start Reading from HW ECC1_Result = 0x200 */
reg = (unsigned long)(info->gpmc_baseaddr + GPMC_ECC1_RESULT);
val = __raw_readl(reg);
*ecc_code++ = val; /* P128e, ..., P1e */
*ecc_code++ = val >> 16; /* P128o, ..., P1o */
/* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */
*ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
reg += 4;
return 0;
}
/**
* omap_enable_hwecc - This function enables the hardware ecc functionality
* @mtd: MTD device structure
* @mode: Read/Write mode
*/
static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
struct nand_chip *chip = mtd->priv;
unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
unsigned long val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONFIG);
switch (mode) {
case NAND_ECC_READ:
__raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
/* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
break;
case NAND_ECC_READSYN:
__raw_writel(0x100, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
/* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
break;
case NAND_ECC_WRITE:
__raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
/* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
break;
default:
DEBUG(MTD_DEBUG_LEVEL0, "Error: Unrecognized Mode[%d]!\n",
mode);
break;
}
__raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONFIG);
}
#endif
/**
* omap_wait - wait until the command is done
* @mtd: MTD device structure
* @chip: NAND Chip structure
*
* Wait function is called during Program and erase operations and
* the way it is called from MTD layer, we should wait till the NAND
* chip is ready after the programming/erase operation has completed.
*
* Erase can take up to 400ms and program up to 20ms according to
* general NAND and SmartMedia specs
*/
static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
{
struct nand_chip *this = mtd->priv;
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
unsigned long timeo = jiffies;
int status, state = this->state;
if (state == FL_ERASING)
timeo += (HZ * 400) / 1000;
else
timeo += (HZ * 20) / 1000;
this->IO_ADDR_W = (void *) info->gpmc_cs_baseaddr +
GPMC_CS_NAND_COMMAND;
this->IO_ADDR_R = (void *) info->gpmc_cs_baseaddr + GPMC_CS_NAND_DATA;
__raw_writeb(NAND_CMD_STATUS & 0xFF, this->IO_ADDR_W);
while (time_before(jiffies, timeo)) {
status = __raw_readb(this->IO_ADDR_R);
if (!(status & 0x40))
break;
}
return status;
}
/**
* omap_dev_ready - calls the platform specific dev_ready function
* @mtd: MTD device structure
*/
static int omap_dev_ready(struct mtd_info *mtd)
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
unsigned int val = __raw_readl(info->gpmc_baseaddr + GPMC_IRQ_STATUS);
if ((val & 0x100) == 0x100) {
/* Clear IRQ Interrupt */
val |= 0x100;
val &= ~(0x0);
__raw_writel(val, info->gpmc_baseaddr + GPMC_IRQ_STATUS);
} else {
unsigned int cnt = 0;
while (cnt++ < 0x1FF) {
if ((val & 0x100) == 0x100)
return 0;
val = __raw_readl(info->gpmc_baseaddr +
GPMC_IRQ_STATUS);
}
}
return 1;
}
static int __devinit omap_nand_probe(struct platform_device *pdev)
{
struct omap_nand_info *info;
struct omap_nand_platform_data *pdata;
int err;
unsigned long val;
pdata = pdev->dev.platform_data;
if (pdata == NULL) {
dev_err(&pdev->dev, "platform data missing\n");
return -ENODEV;
}
info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
platform_set_drvdata(pdev, info);
spin_lock_init(&info->controller.lock);
init_waitqueue_head(&info->controller.wq);
info->pdev = pdev;
info->gpmc_cs = pdata->cs;
info->gpmc_baseaddr = pdata->gpmc_baseaddr;
info->gpmc_cs_baseaddr = pdata->gpmc_cs_baseaddr;
info->mtd.priv = &info->nand;
info->mtd.name = dev_name(&pdev->dev);
info->mtd.owner = THIS_MODULE;
err = gpmc_cs_request(info->gpmc_cs, NAND_IO_SIZE, &info->phys_base);
if (err < 0) {
dev_err(&pdev->dev, "Cannot request GPMC CS\n");
goto out_free_info;
}
/* Enable RD PIN Monitoring Reg */
if (pdata->dev_ready) {
val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1);
val |= WR_RD_PIN_MONITORING;
gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG1, val);
}
val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG7);
val &= ~(0xf << 8);
val |= (0xc & 0xf) << 8;
gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG7, val);
/* NAND write protect off */
omap_nand_wp(&info->mtd, NAND_WP_OFF);
if (!request_mem_region(info->phys_base, NAND_IO_SIZE,
pdev->dev.driver->name)) {
err = -EBUSY;
goto out_free_cs;
}
info->nand.IO_ADDR_R = ioremap(info->phys_base, NAND_IO_SIZE);
if (!info->nand.IO_ADDR_R) {
err = -ENOMEM;
goto out_release_mem_region;
}
info->nand.controller = &info->controller;
info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
info->nand.cmd_ctrl = omap_hwcontrol;
/* REVISIT: only supports 16-bit NAND flash */
info->nand.read_buf = omap_read_buf16;
info->nand.write_buf = omap_write_buf16;
info->nand.verify_buf = omap_verify_buf;
/*
* If RDY/BSY line is connected to OMAP then use the omap ready
* funcrtion and the generic nand_wait function which reads the status
* register after monitoring the RDY/BSY line.Otherwise use a standard
* chip delay which is slightly more than tR (AC Timing) of the NAND
* device and read status register until you get a failure or success
*/
if (pdata->dev_ready) {
info->nand.dev_ready = omap_dev_ready;
info->nand.chip_delay = 0;
} else {
info->nand.waitfunc = omap_wait;
info->nand.chip_delay = 50;
}
info->nand.options |= NAND_SKIP_BBTSCAN;
if ((gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1) & 0x3000)
== 0x1000)
info->nand.options |= NAND_BUSWIDTH_16;
#ifdef CONFIG_MTD_NAND_OMAP_HWECC
info->nand.ecc.bytes = 3;
info->nand.ecc.size = 512;
info->nand.ecc.calculate = omap_calculate_ecc;
info->nand.ecc.hwctl = omap_enable_hwecc;
info->nand.ecc.correct = omap_correct_data;
info->nand.ecc.mode = NAND_ECC_HW;
/* init HW ECC */
omap_hwecc_init(&info->mtd);
#else
info->nand.ecc.mode = NAND_ECC_SOFT;
#endif
/* DIP switches on some boards change between 8 and 16 bit
* bus widths for flash. Try the other width if the first try fails.
*/
if (nand_scan(&info->mtd, 1)) {
info->nand.options ^= NAND_BUSWIDTH_16;
if (nand_scan(&info->mtd, 1)) {
err = -ENXIO;
goto out_release_mem_region;
}
}
#ifdef CONFIG_MTD_PARTITIONS
err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
if (err > 0)
add_mtd_partitions(&info->mtd, info->parts, err);
else if (pdata->parts)
add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
else
#endif
add_mtd_device(&info->mtd);
platform_set_drvdata(pdev, &info->mtd);
return 0;
out_release_mem_region:
release_mem_region(info->phys_base, NAND_IO_SIZE);
out_free_cs:
gpmc_cs_free(info->gpmc_cs);
out_free_info:
kfree(info);
return err;
}
static int omap_nand_remove(struct platform_device *pdev)
{
struct mtd_info *mtd = platform_get_drvdata(pdev);
struct omap_nand_info *info = mtd->priv;
platform_set_drvdata(pdev, NULL);
/* Release NAND device, its internal structures and partitions */
nand_release(&info->mtd);
iounmap(info->nand.IO_ADDR_R);
kfree(&info->mtd);
return 0;
}
static struct platform_driver omap_nand_driver = {
.probe = omap_nand_probe,
.remove = omap_nand_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
};
static int __init omap_nand_init(void)
{
printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME);
return platform_driver_register(&omap_nand_driver);
}
static void __exit omap_nand_exit(void)
{
platform_driver_unregister(&omap_nand_driver);
}
module_init(omap_nand_init);
module_exit(omap_nand_exit);
MODULE_ALIAS(DRIVER_NAME);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards");
...@@ -47,6 +47,28 @@ static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl ...@@ -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) ...@@ -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)
......
...@@ -61,6 +61,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) ...@@ -111,15 +124,15 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
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) ...@@ -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,
......
...@@ -74,6 +74,14 @@ static struct nand_ecclayout nand_hw_eccoob = { ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 ...@@ -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 ...@@ -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) ...@@ -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) {
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) { if (set->nr_partitions > 0 && set->partitions != NULL) {
return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions); 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, ...@@ -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, ...@@ -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,7 +889,9 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, ...@@ -773,7 +889,9 @@ 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 */
...@@ -785,21 +903,19 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, ...@@ -785,21 +903,19 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
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, ...@@ -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, ...@@ -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) ...@@ -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);
}
static int s3c2440_nand_probe(struct platform_device *dev)
{
return s3c24xx_nand_probe(dev, TYPE_S3C2440);
}
static int s3c2412_nand_probe(struct platform_device *dev)
{
return s3c24xx_nand_probe(dev, TYPE_S3C2412);
}
static struct platform_driver s3c2410_nand_driver = {
.probe = s3c2410_nand_probe,
.remove = s3c2410_nand_remove,
.suspend = s3c24xx_nand_suspend,
.resume = s3c24xx_nand_resume,
.driver = {
.name = "s3c2410-nand", .name = "s3c2410-nand",
.owner = THIS_MODULE, .driver_data = TYPE_S3C2410,
}, }, {
};
static struct platform_driver s3c2440_nand_driver = {
.probe = s3c2440_nand_probe,
.remove = s3c2410_nand_remove,
.suspend = s3c24xx_nand_suspend,
.resume = s3c24xx_nand_resume,
.driver = {
.name = "s3c2440-nand", .name = "s3c2440-nand",
.owner = THIS_MODULE, .driver_data = TYPE_S3C2440,
}, {
.name = "s3c2412-nand",
.driver_data = TYPE_S3C2412,
}, {
.name = "s3c6400-nand",
.driver_data = TYPE_S3C2412, /* compatible with 2412 */
}, },
{ }
}; };
static struct platform_driver s3c2412_nand_driver = { MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
.probe = s3c2412_nand_probe,
.remove = s3c2410_nand_remove, static struct platform_driver s3c24xx_nand_driver = {
.probe = s3c24xx_nand_probe,
.remove = s3c24xx_nand_remove,
.suspend = s3c24xx_nand_suspend, .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) ...@@ -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); ...@@ -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");
...@@ -64,7 +64,7 @@ struct txx9ndfmc_priv { ...@@ -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) ...@@ -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) ...@@ -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;
......
...@@ -565,7 +565,7 @@ int omap2_onenand_rephase(void) ...@@ -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) ...@@ -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,
......
...@@ -9,6 +9,10 @@ ...@@ -9,6 +9,10 @@
* auto-placement support, read-while load support, various fixes * auto-placement support, read-while load support, various fixes
* Copyright (C) Nokia Corporation, 2007 * Copyright (C) Nokia Corporation, 2007
* *
* Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
* Flex-OneNAND 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.
...@@ -16,6 +20,7 @@ ...@@ -16,6 +20,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/delay.h> #include <linux/delay.h>
...@@ -27,6 +32,38 @@ ...@@ -27,6 +32,38 @@
#include <asm/io.h> #include <asm/io.h>
/* Default Flex-OneNAND boundary and lock respectively */
static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 };
module_param_array(flex_bdry, int, NULL, 0400);
MODULE_PARM_DESC(flex_bdry, "SLC Boundary information for Flex-OneNAND"
"Syntax:flex_bdry=DIE_BDRY,LOCK,..."
"DIE_BDRY: SLC boundary of the die"
"LOCK: Locking information for SLC boundary"
" : 0->Set boundary in unlocked status"
" : 1->Set boundary in locked status");
/**
* onenand_oob_128 - oob info for Flex-Onenand with 4KB page
* For now, we expose only 64 out of 80 ecc bytes
*/
static struct nand_ecclayout onenand_oob_128 = {
.eccbytes = 64,
.eccpos = {
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
102, 103, 104, 105
},
.oobfree = {
{2, 4}, {18, 4}, {34, 4}, {50, 4},
{66, 4}, {82, 4}, {98, 4}, {114, 4}
}
};
/** /**
* onenand_oob_64 - oob info for large (2KB) page * onenand_oob_64 - oob info for large (2KB) page
*/ */
...@@ -65,6 +102,14 @@ static const unsigned char ffchars[] = { ...@@ -65,6 +102,14 @@ static const unsigned char ffchars[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */
}; };
/** /**
...@@ -170,6 +215,70 @@ static int onenand_buffer_address(int dataram1, int sectors, int count) ...@@ -170,6 +215,70 @@ static int onenand_buffer_address(int dataram1, int sectors, int count)
return ((bsa << ONENAND_BSA_SHIFT) | bsc); return ((bsa << ONENAND_BSA_SHIFT) | bsc);
} }
/**
* flexonenand_block- For given address return block number
* @param this - OneNAND device structure
* @param addr - Address for which block number is needed
*/
static unsigned flexonenand_block(struct onenand_chip *this, loff_t addr)
{
unsigned boundary, blk, die = 0;
if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) {
die = 1;
addr -= this->diesize[0];
}
boundary = this->boundary[die];
blk = addr >> (this->erase_shift - 1);
if (blk > boundary)
blk = (blk + boundary + 1) >> 1;
blk += die ? this->density_mask : 0;
return blk;
}
inline unsigned onenand_block(struct onenand_chip *this, loff_t addr)
{
if (!FLEXONENAND(this))
return addr >> this->erase_shift;
return flexonenand_block(this, addr);
}
/**
* flexonenand_addr - Return address of the block
* @this: OneNAND device structure
* @block: Block number on Flex-OneNAND
*
* Return address of the block
*/
static loff_t flexonenand_addr(struct onenand_chip *this, int block)
{
loff_t ofs = 0;
int die = 0, boundary;
if (ONENAND_IS_DDP(this) && block >= this->density_mask) {
block -= this->density_mask;
die = 1;
ofs = this->diesize[0];
}
boundary = this->boundary[die];
ofs += (loff_t)block << (this->erase_shift - 1);
if (block > (boundary + 1))
ofs += (loff_t)(block - boundary - 1) << (this->erase_shift - 1);
return ofs;
}
loff_t onenand_addr(struct onenand_chip *this, int block)
{
if (!FLEXONENAND(this))
return (loff_t)block << this->erase_shift;
return flexonenand_addr(this, block);
}
EXPORT_SYMBOL(onenand_addr);
/** /**
* onenand_get_density - [DEFAULT] Get OneNAND density * onenand_get_density - [DEFAULT] Get OneNAND density
* @param dev_id OneNAND device ID * @param dev_id OneNAND device ID
...@@ -182,6 +291,22 @@ static inline int onenand_get_density(int dev_id) ...@@ -182,6 +291,22 @@ static inline int onenand_get_density(int dev_id)
return (density & ONENAND_DEVICE_DENSITY_MASK); return (density & ONENAND_DEVICE_DENSITY_MASK);
} }
/**
* flexonenand_region - [Flex-OneNAND] Return erase region of addr
* @param mtd MTD device structure
* @param addr address whose erase region needs to be identified
*/
int flexonenand_region(struct mtd_info *mtd, loff_t addr)
{
int i;
for (i = 0; i < mtd->numeraseregions; i++)
if (addr < mtd->eraseregions[i].offset)
break;
return i - 1;
}
EXPORT_SYMBOL(flexonenand_region);
/** /**
* onenand_command - [DEFAULT] Send command to OneNAND device * onenand_command - [DEFAULT] Send command to OneNAND device
* @param mtd MTD device structure * @param mtd MTD device structure
...@@ -207,16 +332,28 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le ...@@ -207,16 +332,28 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
page = -1; page = -1;
break; break;
case FLEXONENAND_CMD_PI_ACCESS:
/* addr contains die index */
block = addr * this->density_mask;
page = -1;
break;
case ONENAND_CMD_ERASE: case ONENAND_CMD_ERASE:
case ONENAND_CMD_BUFFERRAM: case ONENAND_CMD_BUFFERRAM:
case ONENAND_CMD_OTP_ACCESS: case ONENAND_CMD_OTP_ACCESS:
block = (int) (addr >> this->erase_shift); block = onenand_block(this, addr);
page = -1; page = -1;
break; break;
case FLEXONENAND_CMD_READ_PI:
cmd = ONENAND_CMD_READ;
block = addr * this->density_mask;
page = 0;
break;
default: default:
block = (int) (addr >> this->erase_shift); block = onenand_block(this, addr);
page = (int) (addr >> this->page_shift); page = (int) (addr - onenand_addr(this, block)) >> this->page_shift;
if (ONENAND_IS_2PLANE(this)) { if (ONENAND_IS_2PLANE(this)) {
/* Make the even block number */ /* Make the even block number */
...@@ -236,7 +373,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le ...@@ -236,7 +373,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
value = onenand_bufferram_address(this, block); value = onenand_bufferram_address(this, block);
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
if (ONENAND_IS_2PLANE(this)) if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this))
/* It is always BufferRAM0 */ /* It is always BufferRAM0 */
ONENAND_SET_BUFFERRAM0(this); ONENAND_SET_BUFFERRAM0(this);
else else
...@@ -258,12 +395,17 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le ...@@ -258,12 +395,17 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
if (page != -1) { if (page != -1) {
/* Now we use page size operation */ /* Now we use page size operation */
int sectors = 4, count = 4; int sectors = 0, count = 0;
int dataram; int dataram;
switch (cmd) { switch (cmd) {
case FLEXONENAND_CMD_RECOVER_LSB:
case ONENAND_CMD_READ: case ONENAND_CMD_READ:
case ONENAND_CMD_READOOB: case ONENAND_CMD_READOOB:
if (ONENAND_IS_MLC(this))
/* It is always BufferRAM0 */
dataram = ONENAND_SET_BUFFERRAM0(this);
else
dataram = ONENAND_SET_NEXT_BUFFERRAM(this); dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
break; break;
...@@ -292,6 +434,30 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le ...@@ -292,6 +434,30 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
return 0; return 0;
} }
/**
* onenand_read_ecc - return ecc status
* @param this onenand chip structure
*/
static inline int onenand_read_ecc(struct onenand_chip *this)
{
int ecc, i, result = 0;
if (!FLEXONENAND(this))
return this->read_word(this->base + ONENAND_REG_ECC_STATUS);
for (i = 0; i < 4; i++) {
ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i);
if (likely(!ecc))
continue;
if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR)
return ONENAND_ECC_2BIT_ALL;
else
result = ONENAND_ECC_1BIT_ALL;
}
return result;
}
/** /**
* onenand_wait - [DEFAULT] wait until the command is done * onenand_wait - [DEFAULT] wait until the command is done
* @param mtd MTD device structure * @param mtd MTD device structure
...@@ -331,14 +497,14 @@ static int onenand_wait(struct mtd_info *mtd, int state) ...@@ -331,14 +497,14 @@ static int onenand_wait(struct mtd_info *mtd, int state)
* power off recovery (POR) test, it should read ECC status first * power off recovery (POR) test, it should read ECC status first
*/ */
if (interrupt & ONENAND_INT_READ) { if (interrupt & ONENAND_INT_READ) {
int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); int ecc = onenand_read_ecc(this);
if (ecc) { if (ecc) {
if (ecc & ONENAND_ECC_2BIT_ALL) { if (ecc & ONENAND_ECC_2BIT_ALL) {
printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc);
mtd->ecc_stats.failed++; mtd->ecc_stats.failed++;
return -EBADMSG; return -EBADMSG;
} else if (ecc & ONENAND_ECC_1BIT_ALL) { } else if (ecc & ONENAND_ECC_1BIT_ALL) {
printk(KERN_INFO "onenand_wait: correctable ECC error = 0x%04x\n", ecc); printk(KERN_DEBUG "onenand_wait: correctable ECC error = 0x%04x\n", ecc);
mtd->ecc_stats.corrected++; mtd->ecc_stats.corrected++;
} }
} }
...@@ -656,7 +822,7 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) ...@@ -656,7 +822,7 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
if (found && ONENAND_IS_DDP(this)) { if (found && ONENAND_IS_DDP(this)) {
/* Select DataRAM for DDP */ /* Select DataRAM for DDP */
int block = (int) (addr >> this->erase_shift); int block = onenand_block(this, addr);
int value = onenand_bufferram_address(this, block); int value = onenand_bufferram_address(this, block);
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
} }
...@@ -815,6 +981,149 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col ...@@ -815,6 +981,149 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col
return 0; return 0;
} }
/**
* onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data
* @param mtd MTD device structure
* @param addr address to recover
* @param status return value from onenand_wait / onenand_bbt_wait
*
* MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has
* lower page address and MSB page has higher page address in paired pages.
* If power off occurs during MSB page program, the paired LSB page data can
* become corrupt. LSB page recovery read is a way to read LSB page though page
* data are corrupted. When uncorrectable error occurs as a result of LSB page
* read after power up, issue LSB page recovery read.
*/
static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status)
{
struct onenand_chip *this = mtd->priv;
int i;
/* Recovery is only for Flex-OneNAND */
if (!FLEXONENAND(this))
return status;
/* check if we failed due to uncorrectable error */
if (status != -EBADMSG && status != ONENAND_BBT_READ_ECC_ERROR)
return status;
/* check if address lies in MLC region */
i = flexonenand_region(mtd, addr);
if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift))
return status;
/* We are attempting to reread, so decrement stats.failed
* which was incremented by onenand_wait due to read failure
*/
printk(KERN_INFO "onenand_recover_lsb: Attempting to recover from uncorrectable read\n");
mtd->ecc_stats.failed--;
/* Issue the LSB page recovery command */
this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize);
return this->wait(mtd, FL_READING);
}
/**
* onenand_mlc_read_ops_nolock - MLC OneNAND read main and/or out-of-band
* @param mtd MTD device structure
* @param from offset to read from
* @param ops: oob operation description structure
*
* MLC OneNAND / Flex-OneNAND has 4KB page size and 4KB dataram.
* So, read-while-load is not present.
*/
static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
struct onenand_chip *this = mtd->priv;
struct mtd_ecc_stats stats;
size_t len = ops->len;
size_t ooblen = ops->ooblen;
u_char *buf = ops->datbuf;
u_char *oobbuf = ops->oobbuf;
int read = 0, column, thislen;
int oobread = 0, oobcolumn, thisooblen, oobsize;
int ret = 0;
int writesize = this->writesize;
DEBUG(MTD_DEBUG_LEVEL3, "onenand_mlc_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
if (ops->mode == MTD_OOB_AUTO)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
oobcolumn = from & (mtd->oobsize - 1);
/* Do not allow reads past end of device */
if (from + len > mtd->size) {
printk(KERN_ERR "onenand_mlc_read_ops_nolock: Attempt read beyond end of device\n");
ops->retlen = 0;
ops->oobretlen = 0;
return -EINVAL;
}
stats = mtd->ecc_stats;
while (read < len) {
cond_resched();
thislen = min_t(int, writesize, len - read);
column = from & (writesize - 1);
if (column + thislen > writesize)
thislen = writesize - column;
if (!onenand_check_bufferram(mtd, from)) {
this->command(mtd, ONENAND_CMD_READ, from, writesize);
ret = this->wait(mtd, FL_READING);
if (unlikely(ret))
ret = onenand_recover_lsb(mtd, from, ret);
onenand_update_bufferram(mtd, from, !ret);
if (ret == -EBADMSG)
ret = 0;
}
this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
if (oobbuf) {
thisooblen = oobsize - oobcolumn;
thisooblen = min_t(int, thisooblen, ooblen - oobread);
if (ops->mode == MTD_OOB_AUTO)
onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
else
this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
oobread += thisooblen;
oobbuf += thisooblen;
oobcolumn = 0;
}
read += thislen;
if (read == len)
break;
from += thislen;
buf += thislen;
}
/*
* Return success, if no ECC failures, else -EBADMSG
* fs driver will take care of that, because
* retlen == desired len and result == -EBADMSG
*/
ops->retlen = read;
ops->oobretlen = oobread;
if (ret)
return ret;
if (mtd->ecc_stats.failed - stats.failed)
return -EBADMSG;
return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
}
/** /**
* onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band
* @param mtd MTD device structure * @param mtd MTD device structure
...@@ -962,7 +1271,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, ...@@ -962,7 +1271,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
size_t len = ops->ooblen; size_t len = ops->ooblen;
mtd_oob_mode_t mode = ops->mode; mtd_oob_mode_t mode = ops->mode;
u_char *buf = ops->oobbuf; u_char *buf = ops->oobbuf;
int ret = 0; int ret = 0, readcmd;
from += ops->ooboffs; from += ops->ooboffs;
...@@ -993,17 +1302,22 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, ...@@ -993,17 +1302,22 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
stats = mtd->ecc_stats; stats = mtd->ecc_stats;
readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
while (read < len) { while (read < len) {
cond_resched(); cond_resched();
thislen = oobsize - column; thislen = oobsize - column;
thislen = min_t(int, thislen, len); thislen = min_t(int, thislen, len);
this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); this->command(mtd, readcmd, from, mtd->oobsize);
onenand_update_bufferram(mtd, from, 0); onenand_update_bufferram(mtd, from, 0);
ret = this->wait(mtd, FL_READING); ret = this->wait(mtd, FL_READING);
if (unlikely(ret))
ret = onenand_recover_lsb(mtd, from, ret);
if (ret && ret != -EBADMSG) { if (ret && ret != -EBADMSG) {
printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret);
break; break;
...@@ -1053,6 +1367,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, ...@@ -1053,6 +1367,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf) size_t *retlen, u_char *buf)
{ {
struct onenand_chip *this = mtd->priv;
struct mtd_oob_ops ops = { struct mtd_oob_ops ops = {
.len = len, .len = len,
.ooblen = 0, .ooblen = 0,
...@@ -1062,7 +1377,9 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -1062,7 +1377,9 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
int ret; int ret;
onenand_get_device(mtd, FL_READING); onenand_get_device(mtd, FL_READING);
ret = onenand_read_ops_nolock(mtd, from, &ops); ret = ONENAND_IS_MLC(this) ?
onenand_mlc_read_ops_nolock(mtd, from, &ops) :
onenand_read_ops_nolock(mtd, from, &ops);
onenand_release_device(mtd); onenand_release_device(mtd);
*retlen = ops.retlen; *retlen = ops.retlen;
...@@ -1080,6 +1397,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -1080,6 +1397,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
static int onenand_read_oob(struct mtd_info *mtd, loff_t from, static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops) struct mtd_oob_ops *ops)
{ {
struct onenand_chip *this = mtd->priv;
int ret; int ret;
switch (ops->mode) { switch (ops->mode) {
...@@ -1094,7 +1412,9 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, ...@@ -1094,7 +1412,9 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
onenand_get_device(mtd, FL_READING); onenand_get_device(mtd, FL_READING);
if (ops->datbuf) if (ops->datbuf)
ret = onenand_read_ops_nolock(mtd, from, ops); ret = ONENAND_IS_MLC(this) ?
onenand_mlc_read_ops_nolock(mtd, from, ops) :
onenand_read_ops_nolock(mtd, from, ops);
else else
ret = onenand_read_oob_nolock(mtd, from, ops); ret = onenand_read_oob_nolock(mtd, from, ops);
onenand_release_device(mtd); onenand_release_device(mtd);
...@@ -1128,11 +1448,11 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state) ...@@ -1128,11 +1448,11 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state)
ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
if (interrupt & ONENAND_INT_READ) { if (interrupt & ONENAND_INT_READ) {
int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); int ecc = onenand_read_ecc(this);
if (ecc & ONENAND_ECC_2BIT_ALL) { if (ecc & ONENAND_ECC_2BIT_ALL) {
printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x" printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x"
", controller error 0x%04x\n", ecc, ctrl); ", controller error 0x%04x\n", ecc, ctrl);
return ONENAND_BBT_READ_ERROR; return ONENAND_BBT_READ_ECC_ERROR;
} }
} else { } else {
printk(KERN_ERR "onenand_bbt_wait: read timeout!" printk(KERN_ERR "onenand_bbt_wait: read timeout!"
...@@ -1163,7 +1483,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, ...@@ -1163,7 +1483,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
int read = 0, thislen, column; int read = 0, thislen, column;
int ret = 0; int ret = 0, readcmd;
size_t len = ops->ooblen; size_t len = ops->ooblen;
u_char *buf = ops->oobbuf; u_char *buf = ops->oobbuf;
...@@ -1183,17 +1503,22 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, ...@@ -1183,17 +1503,22 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
column = from & (mtd->oobsize - 1); column = from & (mtd->oobsize - 1);
readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
while (read < len) { while (read < len) {
cond_resched(); cond_resched();
thislen = mtd->oobsize - column; thislen = mtd->oobsize - column;
thislen = min_t(int, thislen, len); thislen = min_t(int, thislen, len);
this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); this->command(mtd, readcmd, from, mtd->oobsize);
onenand_update_bufferram(mtd, from, 0); onenand_update_bufferram(mtd, from, 0);
ret = onenand_bbt_wait(mtd, FL_READING); ret = this->bbt_wait(mtd, FL_READING);
if (unlikely(ret))
ret = onenand_recover_lsb(mtd, from, ret);
if (ret) if (ret)
break; break;
...@@ -1230,9 +1555,11 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to ...@@ -1230,9 +1555,11 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
u_char *oob_buf = this->oob_buf; u_char *oob_buf = this->oob_buf;
int status, i; int status, i, readcmd;
this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
this->command(mtd, readcmd, to, mtd->oobsize);
onenand_update_bufferram(mtd, to, 0); onenand_update_bufferram(mtd, to, 0);
status = this->wait(mtd, FL_READING); status = this->wait(mtd, FL_READING);
if (status) if (status)
...@@ -1633,7 +1960,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, ...@@ -1633,7 +1960,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
int column, ret = 0, oobsize; int column, ret = 0, oobsize;
int written = 0; int written = 0, oobcmd;
u_char *oobbuf; u_char *oobbuf;
size_t len = ops->ooblen; size_t len = ops->ooblen;
const u_char *buf = ops->oobbuf; const u_char *buf = ops->oobbuf;
...@@ -1675,6 +2002,8 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, ...@@ -1675,6 +2002,8 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
oobbuf = this->oob_buf; oobbuf = this->oob_buf;
oobcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB;
/* Loop until all data write */ /* Loop until all data write */
while (written < len) { while (written < len) {
int thislen = min_t(int, oobsize, len - written); int thislen = min_t(int, oobsize, len - written);
...@@ -1692,7 +2021,14 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, ...@@ -1692,7 +2021,14 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
memcpy(oobbuf + column, buf, thislen); memcpy(oobbuf + column, buf, thislen);
this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); if (ONENAND_IS_MLC(this)) {
/* Set main area of DataRAM to 0xff*/
memset(this->page_buf, 0xff, mtd->writesize);
this->write_bufferram(mtd, ONENAND_DATARAM,
this->page_buf, 0, mtd->writesize);
}
this->command(mtd, oobcmd, to, mtd->oobsize);
onenand_update_bufferram(mtd, to, 0); onenand_update_bufferram(mtd, to, 0);
if (ONENAND_IS_2PLANE(this)) { if (ONENAND_IS_2PLANE(this)) {
...@@ -1815,29 +2151,48 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) ...@@ -1815,29 +2151,48 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
unsigned int block_size; unsigned int block_size;
loff_t addr; loff_t addr = instr->addr;
int len; loff_t len = instr->len;
int ret = 0; int ret = 0, i;
struct mtd_erase_region_info *region = NULL;
loff_t region_end = 0;
DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%012llx, len = %llu\n", (unsigned long long) instr->addr, (unsigned long long) instr->len); DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%012llx, len = %llu\n", (unsigned long long) instr->addr, (unsigned long long) instr->len);
block_size = (1 << this->erase_shift); /* Do not allow erase past end of device */
if (unlikely((len + addr) > mtd->size)) {
printk(KERN_ERR "onenand_erase: Erase past end of device\n");
return -EINVAL;
}
/* Start address must align on block boundary */ if (FLEXONENAND(this)) {
if (unlikely(instr->addr & (block_size - 1))) { /* Find the eraseregion of this address */
i = flexonenand_region(mtd, addr);
region = &mtd->eraseregions[i];
block_size = region->erasesize;
region_end = region->offset + region->erasesize * region->numblocks;
/* Start address within region must align on block boundary.
* Erase region's start offset is always block start address.
*/
if (unlikely((addr - region->offset) & (block_size - 1))) {
printk(KERN_ERR "onenand_erase: Unaligned address\n"); printk(KERN_ERR "onenand_erase: Unaligned address\n");
return -EINVAL; return -EINVAL;
} }
} else {
block_size = 1 << this->erase_shift;
/* Length must align on block boundary */ /* Start address must align on block boundary */
if (unlikely(instr->len & (block_size - 1))) { if (unlikely(addr & (block_size - 1))) {
printk(KERN_ERR "onenand_erase: Length not block aligned\n"); printk(KERN_ERR "onenand_erase: Unaligned address\n");
return -EINVAL; return -EINVAL;
} }
}
/* Do not allow erase past end of device */ /* Length must align on block boundary */
if (unlikely((instr->len + instr->addr) > mtd->size)) { if (unlikely(len & (block_size - 1))) {
printk(KERN_ERR "onenand_erase: Erase past end of device\n"); printk(KERN_ERR "onenand_erase: Length not block aligned\n");
return -EINVAL; return -EINVAL;
} }
...@@ -1847,9 +2202,6 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) ...@@ -1847,9 +2202,6 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
onenand_get_device(mtd, FL_ERASING); onenand_get_device(mtd, FL_ERASING);
/* Loop throught the pages */ /* Loop throught the pages */
len = instr->len;
addr = instr->addr;
instr->state = MTD_ERASING; instr->state = MTD_ERASING;
while (len) { while (len) {
...@@ -1869,7 +2221,8 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) ...@@ -1869,7 +2221,8 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
ret = this->wait(mtd, FL_ERASING); ret = this->wait(mtd, FL_ERASING);
/* Check, if it is write protected */ /* Check, if it is write protected */
if (ret) { if (ret) {
printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); printk(KERN_ERR "onenand_erase: Failed erase, block %d\n",
onenand_block(this, addr));
instr->state = MTD_ERASE_FAILED; instr->state = MTD_ERASE_FAILED;
instr->fail_addr = addr; instr->fail_addr = addr;
goto erase_exit; goto erase_exit;
...@@ -1877,6 +2230,22 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) ...@@ -1877,6 +2230,22 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
len -= block_size; len -= block_size;
addr += block_size; addr += block_size;
if (addr == region_end) {
if (!len)
break;
region++;
block_size = region->erasesize;
region_end = region->offset + region->erasesize * region->numblocks;
if (len & (block_size - 1)) {
/* FIXME: This should be handled at MTD partitioning level. */
printk(KERN_ERR "onenand_erase: Unaligned address\n");
goto erase_exit;
}
}
} }
instr->state = MTD_ERASE_DONE; instr->state = MTD_ERASE_DONE;
...@@ -1955,12 +2324,16 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) ...@@ -1955,12 +2324,16 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
int block; int block;
/* Get block number */ /* Get block number */
block = ((int) ofs) >> bbm->bbt_erase_shift; block = onenand_block(this, ofs);
if (bbm->bbt) if (bbm->bbt)
bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
/* We write two bytes, so we dont have to mess with 16 bit access */ /* We write two bytes, so we dont have to mess with 16 bit access */
ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
/* FIXME : What to do when marking SLC block in partition
* with MLC erasesize? For now, it is not advisable to
* create partitions containing both SLC and MLC regions.
*/
return onenand_write_oob_nolock(mtd, ofs, &ops); return onenand_write_oob_nolock(mtd, ofs, &ops);
} }
...@@ -2005,8 +2378,8 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int ...@@ -2005,8 +2378,8 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int
int start, end, block, value, status; int start, end, block, value, status;
int wp_status_mask; int wp_status_mask;
start = ofs >> this->erase_shift; start = onenand_block(this, ofs);
end = len >> this->erase_shift; end = onenand_block(this, ofs + len) - 1;
if (cmd == ONENAND_CMD_LOCK) if (cmd == ONENAND_CMD_LOCK)
wp_status_mask = ONENAND_WP_LS; wp_status_mask = ONENAND_WP_LS;
...@@ -2018,7 +2391,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int ...@@ -2018,7 +2391,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int
/* Set start block address */ /* Set start block address */
this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
/* Set end block address */ /* Set end block address */
this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); this->write_word(end, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
/* Write lock command */ /* Write lock command */
this->command(mtd, cmd, 0, 0); this->command(mtd, cmd, 0, 0);
...@@ -2039,7 +2412,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int ...@@ -2039,7 +2412,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int
} }
/* Block lock scheme */ /* Block lock scheme */
for (block = start; block < start + end; block++) { for (block = start; block < end + 1; block++) {
/* Set block address */ /* Set block address */
value = onenand_block_address(this, block); value = onenand_block_address(this, block);
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
...@@ -2147,7 +2520,7 @@ static void onenand_unlock_all(struct mtd_info *mtd) ...@@ -2147,7 +2520,7 @@ static void onenand_unlock_all(struct mtd_info *mtd)
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
loff_t ofs = 0; loff_t ofs = 0;
size_t len = this->chipsize; loff_t len = mtd->size;
if (this->options & ONENAND_HAS_UNLOCK_ALL) { if (this->options & ONENAND_HAS_UNLOCK_ALL) {
/* Set start block address */ /* Set start block address */
...@@ -2163,12 +2536,16 @@ static void onenand_unlock_all(struct mtd_info *mtd) ...@@ -2163,12 +2536,16 @@ static void onenand_unlock_all(struct mtd_info *mtd)
& ONENAND_CTRL_ONGO) & ONENAND_CTRL_ONGO)
continue; continue;
/* Don't check lock status */
if (this->options & ONENAND_SKIP_UNLOCK_CHECK)
return;
/* Check lock status */ /* Check lock status */
if (onenand_check_lock_status(this)) if (onenand_check_lock_status(this))
return; return;
/* Workaround for all block unlock in DDP */ /* Workaround for all block unlock in DDP */
if (ONENAND_IS_DDP(this)) { if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) {
/* All blocks on another chip */ /* All blocks on another chip */
ofs = this->chipsize >> 1; ofs = this->chipsize >> 1;
len = this->chipsize >> 1; len = this->chipsize >> 1;
...@@ -2210,7 +2587,9 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -2210,7 +2587,9 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len,
this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
this->wait(mtd, FL_OTPING); this->wait(mtd, FL_OTPING);
ret = onenand_read_ops_nolock(mtd, from, &ops); ret = ONENAND_IS_MLC(this) ?
onenand_mlc_read_ops_nolock(mtd, from, &ops) :
onenand_read_ops_nolock(mtd, from, &ops);
/* Exit OTP access mode */ /* Exit OTP access mode */
this->command(mtd, ONENAND_CMD_RESET, 0, 0); this->command(mtd, ONENAND_CMD_RESET, 0, 0);
...@@ -2277,21 +2656,32 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -2277,21 +2656,32 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf) size_t *retlen, u_char *buf)
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
struct mtd_oob_ops ops = { struct mtd_oob_ops ops;
.mode = MTD_OOB_PLACE,
.ooblen = len,
.oobbuf = buf,
.ooboffs = 0,
};
int ret; int ret;
/* Enter OTP access mode */ /* Enter OTP access mode */
this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
this->wait(mtd, FL_OTPING); this->wait(mtd, FL_OTPING);
if (FLEXONENAND(this)) {
/*
* For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
* main area of page 49.
*/
ops.len = mtd->writesize;
ops.ooblen = 0;
ops.datbuf = buf;
ops.oobbuf = NULL;
ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops);
*retlen = ops.retlen;
} else {
ops.mode = MTD_OOB_PLACE;
ops.ooblen = len;
ops.oobbuf = buf;
ops.ooboffs = 0;
ret = onenand_write_oob_nolock(mtd, from, &ops); ret = onenand_write_oob_nolock(mtd, from, &ops);
*retlen = ops.oobretlen; *retlen = ops.oobretlen;
}
/* Exit OTP access mode */ /* Exit OTP access mode */
this->command(mtd, ONENAND_CMD_RESET, 0, 0); this->command(mtd, ONENAND_CMD_RESET, 0, 0);
...@@ -2475,27 +2865,34 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, ...@@ -2475,27 +2865,34 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len) size_t len)
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
u_char *oob_buf = this->oob_buf; u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf;
size_t retlen; size_t retlen;
int ret; int ret;
memset(oob_buf, 0xff, mtd->oobsize); memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
: mtd->oobsize);
/* /*
* Note: OTP lock operation * Note: OTP lock operation
* OTP block : 0xXXFC * OTP block : 0xXXFC
* 1st block : 0xXXF3 (If chip support) * 1st block : 0xXXF3 (If chip support)
* Both : 0xXXF0 (If chip support) * Both : 0xXXF0 (If chip support)
*/ */
oob_buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC; if (FLEXONENAND(this))
buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
else
buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC;
/* /*
* Write lock mark to 8th word of sector0 of page0 of the spare0. * Write lock mark to 8th word of sector0 of page0 of the spare0.
* We write 16 bytes spare area instead of 2 bytes. * We write 16 bytes spare area instead of 2 bytes.
* For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
* main area of page 49.
*/ */
from = 0; from = 0;
len = 16; len = FLEXONENAND(this) ? mtd->writesize : 16;
ret = onenand_otp_walk(mtd, from, len, &retlen, oob_buf, do_otp_lock, MTD_OTP_USER); ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);
return ret ? : retlen; return ret ? : retlen;
} }
...@@ -2542,6 +2939,14 @@ static void onenand_check_features(struct mtd_info *mtd) ...@@ -2542,6 +2939,14 @@ static void onenand_check_features(struct mtd_info *mtd)
break; break;
} }
if (ONENAND_IS_MLC(this))
this->options &= ~ONENAND_HAS_2PLANE;
if (FLEXONENAND(this)) {
this->options &= ~ONENAND_HAS_CONT_LOCK;
this->options |= ONENAND_HAS_UNLOCK_ALL;
}
if (this->options & ONENAND_HAS_CONT_LOCK) if (this->options & ONENAND_HAS_CONT_LOCK)
printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); printk(KERN_DEBUG "Lock scheme is Continuous Lock\n");
if (this->options & ONENAND_HAS_UNLOCK_ALL) if (this->options & ONENAND_HAS_UNLOCK_ALL)
...@@ -2559,14 +2964,16 @@ static void onenand_check_features(struct mtd_info *mtd) ...@@ -2559,14 +2964,16 @@ static void onenand_check_features(struct mtd_info *mtd)
*/ */
static void onenand_print_device_info(int device, int version) static void onenand_print_device_info(int device, int version)
{ {
int vcc, demuxed, ddp, density; int vcc, demuxed, ddp, density, flexonenand;
vcc = device & ONENAND_DEVICE_VCC_MASK; vcc = device & ONENAND_DEVICE_VCC_MASK;
demuxed = device & ONENAND_DEVICE_IS_DEMUX; demuxed = device & ONENAND_DEVICE_IS_DEMUX;
ddp = device & ONENAND_DEVICE_IS_DDP; ddp = device & ONENAND_DEVICE_IS_DDP;
density = onenand_get_density(device); density = onenand_get_density(device);
printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", flexonenand = device & DEVICE_IS_FLEXONENAND;
printk(KERN_INFO "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n",
demuxed ? "" : "Muxed ", demuxed ? "" : "Muxed ",
flexonenand ? "Flex-" : "",
ddp ? "(DDP)" : "", ddp ? "(DDP)" : "",
(16 << density), (16 << density),
vcc ? "2.65/3.3" : "1.8", vcc ? "2.65/3.3" : "1.8",
...@@ -2576,6 +2983,7 @@ static void onenand_print_device_info(int device, int version) ...@@ -2576,6 +2983,7 @@ static void onenand_print_device_info(int device, int version)
static const struct onenand_manufacturers onenand_manuf_ids[] = { static const struct onenand_manufacturers onenand_manuf_ids[] = {
{ONENAND_MFR_SAMSUNG, "Samsung"}, {ONENAND_MFR_SAMSUNG, "Samsung"},
{ONENAND_MFR_NUMONYX, "Numonyx"},
}; };
/** /**
...@@ -2604,6 +3012,261 @@ static int onenand_check_maf(int manuf) ...@@ -2604,6 +3012,261 @@ static int onenand_check_maf(int manuf)
return (i == size); return (i == size);
} }
/**
* flexonenand_get_boundary - Reads the SLC boundary
* @param onenand_info - onenand info structure
**/
static int flexonenand_get_boundary(struct mtd_info *mtd)
{
struct onenand_chip *this = mtd->priv;
unsigned die, bdry;
int ret, syscfg, locked;
/* Disable ECC */
syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1);
for (die = 0; die < this->dies; die++) {
this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
this->wait(mtd, FL_SYNCING);
this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
ret = this->wait(mtd, FL_READING);
bdry = this->read_word(this->base + ONENAND_DATARAM);
if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3)
locked = 0;
else
locked = 1;
this->boundary[die] = bdry & FLEXONENAND_PI_MASK;
this->command(mtd, ONENAND_CMD_RESET, 0, 0);
ret = this->wait(mtd, FL_RESETING);
printk(KERN_INFO "Die %d boundary: %d%s\n", die,
this->boundary[die], locked ? "(Locked)" : "(Unlocked)");
}
/* Enable ECC */
this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
return 0;
}
/**
* flexonenand_get_size - Fill up fields in onenand_chip and mtd_info
* boundary[], diesize[], mtd->size, mtd->erasesize
* @param mtd - MTD device structure
*/
static void flexonenand_get_size(struct mtd_info *mtd)
{
struct onenand_chip *this = mtd->priv;
int die, i, eraseshift, density;
int blksperdie, maxbdry;
loff_t ofs;
density = onenand_get_density(this->device_id);
blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift);
blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
maxbdry = blksperdie - 1;
eraseshift = this->erase_shift - 1;
mtd->numeraseregions = this->dies << 1;
/* This fills up the device boundary */
flexonenand_get_boundary(mtd);
die = ofs = 0;
i = -1;
for (; die < this->dies; die++) {
if (!die || this->boundary[die-1] != maxbdry) {
i++;
mtd->eraseregions[i].offset = ofs;
mtd->eraseregions[i].erasesize = 1 << eraseshift;
mtd->eraseregions[i].numblocks =
this->boundary[die] + 1;
ofs += mtd->eraseregions[i].numblocks << eraseshift;
eraseshift++;
} else {
mtd->numeraseregions -= 1;
mtd->eraseregions[i].numblocks +=
this->boundary[die] + 1;
ofs += (this->boundary[die] + 1) << (eraseshift - 1);
}
if (this->boundary[die] != maxbdry) {
i++;
mtd->eraseregions[i].offset = ofs;
mtd->eraseregions[i].erasesize = 1 << eraseshift;
mtd->eraseregions[i].numblocks = maxbdry ^
this->boundary[die];
ofs += mtd->eraseregions[i].numblocks << eraseshift;
eraseshift--;
} else
mtd->numeraseregions -= 1;
}
/* Expose MLC erase size except when all blocks are SLC */
mtd->erasesize = 1 << this->erase_shift;
if (mtd->numeraseregions == 1)
mtd->erasesize >>= 1;
printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions);
for (i = 0; i < mtd->numeraseregions; i++)
printk(KERN_INFO "[offset: 0x%08x, erasesize: 0x%05x,"
" numblocks: %04u]\n",
(unsigned int) mtd->eraseregions[i].offset,
mtd->eraseregions[i].erasesize,
mtd->eraseregions[i].numblocks);
for (die = 0, mtd->size = 0; die < this->dies; die++) {
this->diesize[die] = (loff_t)blksperdie << this->erase_shift;
this->diesize[die] -= (loff_t)(this->boundary[die] + 1)
<< (this->erase_shift - 1);
mtd->size += this->diesize[die];
}
}
/**
* flexonenand_check_blocks_erased - Check if blocks are erased
* @param mtd_info - mtd info structure
* @param start - first erase block to check
* @param end - last erase block to check
*
* Converting an unerased block from MLC to SLC
* causes byte values to change. Since both data and its ECC
* have changed, reads on the block give uncorrectable error.
* This might lead to the block being detected as bad.
*
* Avoid this by ensuring that the block to be converted is
* erased.
*/
static int flexonenand_check_blocks_erased(struct mtd_info *mtd, int start, int end)
{
struct onenand_chip *this = mtd->priv;
int i, ret;
int block;
struct mtd_oob_ops ops = {
.mode = MTD_OOB_PLACE,
.ooboffs = 0,
.ooblen = mtd->oobsize,
.datbuf = NULL,
.oobbuf = this->oob_buf,
};
loff_t addr;
printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end);
for (block = start; block <= end; block++) {
addr = flexonenand_addr(this, block);
if (onenand_block_isbad_nolock(mtd, addr, 0))
continue;
/*
* Since main area write results in ECC write to spare,
* it is sufficient to check only ECC bytes for change.
*/
ret = onenand_read_oob_nolock(mtd, addr, &ops);
if (ret)
return ret;
for (i = 0; i < mtd->oobsize; i++)
if (this->oob_buf[i] != 0xff)
break;
if (i != mtd->oobsize) {
printk(KERN_WARNING "Block %d not erased.\n", block);
return 1;
}
}
return 0;
}
/**
* flexonenand_set_boundary - Writes the SLC boundary
* @param mtd - mtd info structure
*/
int flexonenand_set_boundary(struct mtd_info *mtd, int die,
int boundary, int lock)
{
struct onenand_chip *this = mtd->priv;
int ret, density, blksperdie, old, new, thisboundary;
loff_t addr;
/* Change only once for SDP Flex-OneNAND */
if (die && (!ONENAND_IS_DDP(this)))
return 0;
/* boundary value of -1 indicates no required change */
if (boundary < 0 || boundary == this->boundary[die])
return 0;
density = onenand_get_density(this->device_id);
blksperdie = ((16 << density) << 20) >> this->erase_shift;
blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
if (boundary >= blksperdie) {
printk(KERN_ERR "flexonenand_set_boundary: Invalid boundary value. "
"Boundary not changed.\n");
return -EINVAL;
}
/* Check if converting blocks are erased */
old = this->boundary[die] + (die * this->density_mask);
new = boundary + (die * this->density_mask);
ret = flexonenand_check_blocks_erased(mtd, min(old, new) + 1, max(old, new));
if (ret) {
printk(KERN_ERR "flexonenand_set_boundary: Please erase blocks before boundary change\n");
return ret;
}
this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
this->wait(mtd, FL_SYNCING);
/* Check is boundary is locked */
this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
ret = this->wait(mtd, FL_READING);
thisboundary = this->read_word(this->base + ONENAND_DATARAM);
if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) {
printk(KERN_ERR "flexonenand_set_boundary: boundary locked\n");
ret = 1;
goto out;
}
printk(KERN_INFO "flexonenand_set_boundary: Changing die %d boundary: %d%s\n",
die, boundary, lock ? "(Locked)" : "(Unlocked)");
addr = die ? this->diesize[0] : 0;
boundary &= FLEXONENAND_PI_MASK;
boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT);
this->command(mtd, ONENAND_CMD_ERASE, addr, 0);
ret = this->wait(mtd, FL_ERASING);
if (ret) {
printk(KERN_ERR "flexonenand_set_boundary: Failed PI erase for Die %d\n", die);
goto out;
}
this->write_word(boundary, this->base + ONENAND_DATARAM);
this->command(mtd, ONENAND_CMD_PROG, addr, 0);
ret = this->wait(mtd, FL_WRITING);
if (ret) {
printk(KERN_ERR "flexonenand_set_boundary: Failed PI write for Die %d\n", die);
goto out;
}
this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0);
ret = this->wait(mtd, FL_WRITING);
out:
this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND);
this->wait(mtd, FL_RESETING);
if (!ret)
/* Recalculate device size on boundary change*/
flexonenand_get_size(mtd);
return ret;
}
/** /**
* onenand_probe - [OneNAND Interface] Probe the OneNAND device * onenand_probe - [OneNAND Interface] Probe the OneNAND device
* @param mtd MTD device structure * @param mtd MTD device structure
...@@ -2621,7 +3284,7 @@ static int onenand_probe(struct mtd_info *mtd) ...@@ -2621,7 +3284,7 @@ static int onenand_probe(struct mtd_info *mtd)
/* Save system configuration 1 */ /* Save system configuration 1 */
syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
/* Clear Sync. Burst Read mode to read BootRAM */ /* Clear Sync. Burst Read mode to read BootRAM */
this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1); this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ & ~ONENAND_SYS_CFG1_SYNC_WRITE), this->base + ONENAND_REG_SYS_CFG1);
/* Send the command for reading device ID from BootRAM */ /* Send the command for reading device ID from BootRAM */
this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);
...@@ -2646,6 +3309,7 @@ static int onenand_probe(struct mtd_info *mtd) ...@@ -2646,6 +3309,7 @@ static int onenand_probe(struct mtd_info *mtd)
maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY);
/* Check OneNAND device */ /* Check OneNAND device */
if (maf_id != bram_maf_id || dev_id != bram_dev_id) if (maf_id != bram_maf_id || dev_id != bram_dev_id)
...@@ -2657,28 +3321,54 @@ static int onenand_probe(struct mtd_info *mtd) ...@@ -2657,28 +3321,54 @@ static int onenand_probe(struct mtd_info *mtd)
this->version_id = ver_id; this->version_id = ver_id;
density = onenand_get_density(dev_id); density = onenand_get_density(dev_id);
if (FLEXONENAND(this)) {
this->dies = ONENAND_IS_DDP(this) ? 2 : 1;
/* Maximum possible erase regions */
mtd->numeraseregions = this->dies << 1;
mtd->eraseregions = kzalloc(sizeof(struct mtd_erase_region_info)
* (this->dies << 1), GFP_KERNEL);
if (!mtd->eraseregions)
return -ENOMEM;
}
/*
* For Flex-OneNAND, chipsize represents maximum possible device size.
* mtd->size represents the actual device size.
*/
this->chipsize = (16 << density) << 20; this->chipsize = (16 << density) << 20;
/* Set density mask. it is used for DDP */
if (ONENAND_IS_DDP(this))
this->density_mask = (1 << (density + 6));
else
this->density_mask = 0;
/* OneNAND page size & block size */ /* OneNAND page size & block size */
/* The data buffer size is equal to page size */ /* The data buffer size is equal to page size */
mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
/* We use the full BufferRAM */
if (ONENAND_IS_MLC(this))
mtd->writesize <<= 1;
mtd->oobsize = mtd->writesize >> 5; mtd->oobsize = mtd->writesize >> 5;
/* Pages per a block are always 64 in OneNAND */ /* Pages per a block are always 64 in OneNAND */
mtd->erasesize = mtd->writesize << 6; mtd->erasesize = mtd->writesize << 6;
/*
* Flex-OneNAND SLC area has 64 pages per block.
* Flex-OneNAND MLC area has 128 pages per block.
* Expose MLC erase size to find erase_shift and page_mask.
*/
if (FLEXONENAND(this))
mtd->erasesize <<= 1;
this->erase_shift = ffs(mtd->erasesize) - 1; this->erase_shift = ffs(mtd->erasesize) - 1;
this->page_shift = ffs(mtd->writesize) - 1; this->page_shift = ffs(mtd->writesize) - 1;
this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1;
/* Set density mask. it is used for DDP */
if (ONENAND_IS_DDP(this))
this->density_mask = this->chipsize >> (this->erase_shift + 1);
/* It's real page size */ /* It's real page size */
this->writesize = mtd->writesize; this->writesize = mtd->writesize;
/* REVIST: Multichip handling */ /* REVIST: Multichip handling */
if (FLEXONENAND(this))
flexonenand_get_size(mtd);
else
mtd->size = this->chipsize; mtd->size = this->chipsize;
/* Check OneNAND features */ /* Check OneNAND features */
...@@ -2734,7 +3424,7 @@ static void onenand_resume(struct mtd_info *mtd) ...@@ -2734,7 +3424,7 @@ static void onenand_resume(struct mtd_info *mtd)
*/ */
int onenand_scan(struct mtd_info *mtd, int maxchips) int onenand_scan(struct mtd_info *mtd, int maxchips)
{ {
int i; int i, ret;
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
if (!this->read_word) if (!this->read_word)
...@@ -2746,6 +3436,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) ...@@ -2746,6 +3436,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
this->command = onenand_command; this->command = onenand_command;
if (!this->wait) if (!this->wait)
onenand_setup_wait(mtd); onenand_setup_wait(mtd);
if (!this->bbt_wait)
this->bbt_wait = onenand_bbt_wait;
if (!this->unlock_all)
this->unlock_all = onenand_unlock_all;
if (!this->read_bufferram) if (!this->read_bufferram)
this->read_bufferram = onenand_read_bufferram; this->read_bufferram = onenand_read_bufferram;
...@@ -2796,6 +3490,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) ...@@ -2796,6 +3490,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
* Allow subpage writes up to oobsize. * Allow subpage writes up to oobsize.
*/ */
switch (mtd->oobsize) { switch (mtd->oobsize) {
case 128:
this->ecclayout = &onenand_oob_128;
mtd->subpage_sft = 0;
break;
case 64: case 64:
this->ecclayout = &onenand_oob_64; this->ecclayout = &onenand_oob_64;
mtd->subpage_sft = 2; mtd->subpage_sft = 2;
...@@ -2859,9 +3557,18 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) ...@@ -2859,9 +3557,18 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
mtd->owner = THIS_MODULE; mtd->owner = THIS_MODULE;
/* Unlock whole block */ /* Unlock whole block */
onenand_unlock_all(mtd); this->unlock_all(mtd);
ret = this->scan_bbt(mtd);
if ((!FLEXONENAND(this)) || ret)
return ret;
return this->scan_bbt(mtd); /* Change Flex-OneNAND boundaries if required */
for (i = 0; i < MAX_DIES; i++)
flexonenand_set_boundary(mtd, i, flex_bdry[2 * i],
flex_bdry[(2 * i) + 1]);
return 0;
} }
/** /**
...@@ -2890,6 +3597,7 @@ void onenand_release(struct mtd_info *mtd) ...@@ -2890,6 +3597,7 @@ void onenand_release(struct mtd_info *mtd)
kfree(this->page_buf); kfree(this->page_buf);
if (this->options & ONENAND_OOBBUF_ALLOC) if (this->options & ONENAND_OOBBUF_ALLOC)
kfree(this->oob_buf); kfree(this->oob_buf);
kfree(mtd->eraseregions);
} }
EXPORT_SYMBOL_GPL(onenand_scan); EXPORT_SYMBOL_GPL(onenand_scan);
......
...@@ -63,6 +63,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr ...@@ -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 ...@@ -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,6 +107,11 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr ...@@ -106,6 +107,11 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
} }
} }
i += 2; i += 2;
if (FLEXONENAND(this)) {
rgn = flexonenand_region(mtd, from);
from += mtd->eraseregions[rgn].erasesize;
} else
from += (1 << bbm->bbt_erase_shift); from += (1 << bbm->bbt_erase_shift);
} }
...@@ -143,7 +149,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) ...@@ -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) ...@@ -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) {
......
...@@ -6,6 +6,10 @@ ...@@ -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 @@ ...@@ -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 { ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 */
......
...@@ -94,7 +94,6 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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)
......
...@@ -196,7 +196,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) ...@@ -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) ...@@ -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;
......
...@@ -563,6 +563,7 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -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 { ...@@ -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;
}; };
......
...@@ -17,6 +17,7 @@ ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 */
...@@ -67,6 +67,9 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 */
...@@ -40,7 +40,6 @@ struct mtd_partition { ...@@ -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)
......
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
......
/*
* JFFS2 definitions for use in user space only
*/
#ifndef __JFFS2_USER_H__
#define __JFFS2_USER_H__
/* This file is blessed for inclusion by userspace */
#include <linux/jffs2.h>
#include <linux/types.h>
#include <endian.h>
#include <byteswap.h>
#undef cpu_to_je16
#undef cpu_to_je32
#undef cpu_to_jemode
#undef je16_to_cpu
#undef je32_to_cpu
#undef jemode_to_cpu
extern int target_endian;
#define t16(x) ({ __u16 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
#define t32(x) ({ __u32 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
#define cpu_to_je16(x) ((jint16_t){t16(x)})
#define cpu_to_je32(x) ((jint32_t){t32(x)})
#define cpu_to_jemode(x) ((jmode_t){t32(x)})
#define je16_to_cpu(x) (t16((x).v16))
#define je32_to_cpu(x) (t32((x).v32))
#define jemode_to_cpu(x) (t32((x).m))
#endif /* __JFFS2_USER_H__ */
...@@ -12,12 +12,24 @@ struct erase_info_user { ...@@ -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 { ...@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment