Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (63 commits) mtd: OneNAND: Allow setting of boundary information when built as module jffs2: leaking jffs2_summary in function jffs2_scan_medium mtd: nand: Fix memory leak on txx9ndfmc probe failure. mtd: orion_nand: use burst reads with double word accesses mtd/nand: s3c6400 support for s3c2410 driver [MTD] [NAND] S3C2410: Use DIV_ROUND_UP [MTD] [NAND] S3C2410: Deal with unaligned lengths in S3C2440 buffer read/write [MTD] [NAND] S3C2410: Allow the machine code to get the BBT table from NAND [MTD] [NAND] S3C2410: Added a kerneldoc for s3c2410_nand_set mtd: physmap_of: Add multiple regions and concatenation support mtd: nand: max_retries off by one in mxc_nand mtd: nand: s3c2410_nand_setrate(): use correct macros for 2412/2440 mtd: onenand: add bbt_wait & unlock_all as replaceable for some platform mtd: Flex-OneNAND support mtd: nand: add OMAP2/OMAP3 NAND driver mtd: maps: Blackfin async: fix memory leaks in probe/remove funcs mtd: uclinux: mark local stuff static mtd: uclinux: do not allow to be built as a module mtd: uclinux: allow systems to override map addr/size mtd: blackfin NFC: fix hang when using NAND on BF527-EZKITs ...
This commit is contained in:
@@ -74,6 +74,12 @@ config MTD_NAND_AMS_DELTA
|
||||
help
|
||||
Support for NAND flash on Amstrad E3 (Delta).
|
||||
|
||||
config MTD_NAND_OMAP2
|
||||
tristate "NAND Flash device on OMAP2 and OMAP3"
|
||||
depends on ARM && MTD_NAND && (ARCH_OMAP2 || ARCH_OMAP3)
|
||||
help
|
||||
Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms.
|
||||
|
||||
config MTD_NAND_TS7250
|
||||
tristate "NAND Flash device on TS-7250 board"
|
||||
depends on MACH_TS72XX
|
||||
@@ -139,27 +145,27 @@ config MTD_NAND_PPCHAMELEONEVB
|
||||
This enables the NAND flash driver on the PPChameleon EVB Board.
|
||||
|
||||
config MTD_NAND_S3C2410
|
||||
tristate "NAND Flash support for S3C2410/S3C2440 SoC"
|
||||
depends on ARCH_S3C2410
|
||||
tristate "NAND Flash support for Samsung S3C SoCs"
|
||||
depends on ARCH_S3C2410 || ARCH_S3C64XX
|
||||
help
|
||||
This enables the NAND flash controller on the S3C2410 and S3C2440
|
||||
This enables the NAND flash controller on the S3C24xx and S3C64xx
|
||||
SoCs
|
||||
|
||||
No board specific support is done by this driver, each board
|
||||
must advertise a platform_device for the driver to attach.
|
||||
|
||||
config MTD_NAND_S3C2410_DEBUG
|
||||
bool "S3C2410 NAND driver debug"
|
||||
bool "Samsung S3C NAND driver debug"
|
||||
depends on MTD_NAND_S3C2410
|
||||
help
|
||||
Enable debugging of the S3C2410 NAND driver
|
||||
Enable debugging of the S3C NAND driver
|
||||
|
||||
config MTD_NAND_S3C2410_HWECC
|
||||
bool "S3C2410 NAND Hardware ECC"
|
||||
bool "Samsung S3C NAND Hardware ECC"
|
||||
depends on MTD_NAND_S3C2410
|
||||
help
|
||||
Enable the use of the S3C2410's internal ECC generator when
|
||||
using NAND. Early versions of the chip have had problems with
|
||||
Enable the use of the controller's internal ECC generator when
|
||||
using NAND. Early versions of the chips have had problems with
|
||||
incorrect ECC generation, and if using these, the default of
|
||||
software ECC is preferable.
|
||||
|
||||
@@ -171,7 +177,7 @@ config MTD_NAND_NDFC
|
||||
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
|
||||
|
||||
config MTD_NAND_S3C2410_CLKSTOP
|
||||
bool "S3C2410 NAND IDLE clock stop"
|
||||
bool "Samsung S3C NAND IDLE clock stop"
|
||||
depends on MTD_NAND_S3C2410
|
||||
default n
|
||||
help
|
||||
|
@@ -25,6 +25,7 @@ obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
|
||||
obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o
|
||||
obj-$(CONFIG_MTD_NAND_OMAP2) += omap2.o
|
||||
obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
|
||||
obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o
|
||||
|
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
@@ -47,6 +48,9 @@
|
||||
#define no_ecc 0
|
||||
#endif
|
||||
|
||||
static int on_flash_bbt = 0;
|
||||
module_param(on_flash_bbt, int, 0);
|
||||
|
||||
/* Register access macros */
|
||||
#define ecc_readl(add, reg) \
|
||||
__raw_readl(add + ATMEL_ECC_##reg)
|
||||
@@ -459,12 +463,17 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
|
||||
|
||||
if (host->board->det_pin) {
|
||||
if (gpio_get_value(host->board->det_pin)) {
|
||||
printk("No SmartMedia card inserted.\n");
|
||||
printk(KERN_INFO "No SmartMedia card inserted.\n");
|
||||
res = ENXIO;
|
||||
goto err_no_card;
|
||||
}
|
||||
}
|
||||
|
||||
if (on_flash_bbt) {
|
||||
printk(KERN_INFO "atmel_nand: Use On Flash BBT\n");
|
||||
nand_chip->options |= NAND_USE_FLASH_BBT;
|
||||
}
|
||||
|
||||
/* first scan to find the device and get the page size */
|
||||
if (nand_scan_ident(mtd, 1)) {
|
||||
res = -ENXIO;
|
||||
|
@@ -458,7 +458,7 @@ static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bf5xx_nand_dma_rw(struct mtd_info *mtd,
|
||||
static void bf5xx_nand_dma_rw(struct mtd_info *mtd,
|
||||
uint8_t *buf, int is_read)
|
||||
{
|
||||
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
|
||||
@@ -496,11 +496,20 @@ static int bf5xx_nand_dma_rw(struct mtd_info *mtd,
|
||||
/* setup DMA register with Blackfin DMA API */
|
||||
set_dma_config(CH_NFC, 0x0);
|
||||
set_dma_start_addr(CH_NFC, (unsigned long) buf);
|
||||
|
||||
/* The DMAs have different size on BF52x and BF54x */
|
||||
#ifdef CONFIG_BF52x
|
||||
set_dma_x_count(CH_NFC, (page_size >> 1));
|
||||
set_dma_x_modify(CH_NFC, 2);
|
||||
val = DI_EN | WDSIZE_16;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BF54x
|
||||
set_dma_x_count(CH_NFC, (page_size >> 2));
|
||||
set_dma_x_modify(CH_NFC, 4);
|
||||
|
||||
/* setup write or read operation */
|
||||
val = DI_EN | WDSIZE_32;
|
||||
#endif
|
||||
/* setup write or read operation */
|
||||
if (is_read)
|
||||
val |= WNR;
|
||||
set_dma_config(CH_NFC, val);
|
||||
@@ -512,8 +521,6 @@ static int bf5xx_nand_dma_rw(struct mtd_info *mtd,
|
||||
else
|
||||
bfin_write_NFC_PGCTL(0x2);
|
||||
wait_for_completion(&info->dma_completion);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd,
|
||||
|
@@ -44,7 +44,7 @@
|
||||
* and some flavors of secondary chipselect (e.g. based on A12) as used
|
||||
* with multichip packages.
|
||||
*
|
||||
* The 1-bit ECC hardware is supported, but not yet the newer 4-bit ECC
|
||||
* The 1-bit ECC hardware is supported, as well as the newer 4-bit ECC
|
||||
* available on chips like the DM355 and OMAP-L137 and needed with the
|
||||
* more error-prone MLC NAND chips.
|
||||
*
|
||||
@@ -54,11 +54,14 @@
|
||||
struct davinci_nand_info {
|
||||
struct mtd_info mtd;
|
||||
struct nand_chip chip;
|
||||
struct nand_ecclayout ecclayout;
|
||||
|
||||
struct device *dev;
|
||||
struct clk *clk;
|
||||
bool partitioned;
|
||||
|
||||
bool is_readmode;
|
||||
|
||||
void __iomem *base;
|
||||
void __iomem *vaddr;
|
||||
|
||||
@@ -73,6 +76,7 @@ struct davinci_nand_info {
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(davinci_nand_lock);
|
||||
static bool ecc4_busy;
|
||||
|
||||
#define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd)
|
||||
|
||||
@@ -217,6 +221,192 @@ static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat,
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* 4-bit hardware ECC ... context maintained over entire AEMIF
|
||||
*
|
||||
* This is a syndrome engine, but we avoid NAND_ECC_HW_SYNDROME
|
||||
* since that forces use of a problematic "infix OOB" layout.
|
||||
* Among other things, it trashes manufacturer bad block markers.
|
||||
* Also, and specific to this hardware, it ECC-protects the "prepad"
|
||||
* in the OOB ... while having ECC protection for parts of OOB would
|
||||
* seem useful, the current MTD stack sometimes wants to update the
|
||||
* OOB without recomputing ECC.
|
||||
*/
|
||||
|
||||
static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
struct davinci_nand_info *info = to_davinci_nand(mtd);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&davinci_nand_lock, flags);
|
||||
|
||||
/* Start 4-bit ECC calculation for read/write */
|
||||
val = davinci_nand_readl(info, NANDFCR_OFFSET);
|
||||
val &= ~(0x03 << 4);
|
||||
val |= (info->core_chipsel << 4) | BIT(12);
|
||||
davinci_nand_writel(info, NANDFCR_OFFSET, val);
|
||||
|
||||
info->is_readmode = (mode == NAND_ECC_READ);
|
||||
|
||||
spin_unlock_irqrestore(&davinci_nand_lock, flags);
|
||||
}
|
||||
|
||||
/* Read raw ECC code after writing to NAND. */
|
||||
static void
|
||||
nand_davinci_readecc_4bit(struct davinci_nand_info *info, u32 code[4])
|
||||
{
|
||||
const u32 mask = 0x03ff03ff;
|
||||
|
||||
code[0] = davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET) & mask;
|
||||
code[1] = davinci_nand_readl(info, NAND_4BIT_ECC2_OFFSET) & mask;
|
||||
code[2] = davinci_nand_readl(info, NAND_4BIT_ECC3_OFFSET) & mask;
|
||||
code[3] = davinci_nand_readl(info, NAND_4BIT_ECC4_OFFSET) & mask;
|
||||
}
|
||||
|
||||
/* Terminate read ECC; or return ECC (as bytes) of data written to NAND. */
|
||||
static int nand_davinci_calculate_4bit(struct mtd_info *mtd,
|
||||
const u_char *dat, u_char *ecc_code)
|
||||
{
|
||||
struct davinci_nand_info *info = to_davinci_nand(mtd);
|
||||
u32 raw_ecc[4], *p;
|
||||
unsigned i;
|
||||
|
||||
/* After a read, terminate ECC calculation by a dummy read
|
||||
* of some 4-bit ECC register. ECC covers everything that
|
||||
* was read; correct() just uses the hardware state, so
|
||||
* ecc_code is not needed.
|
||||
*/
|
||||
if (info->is_readmode) {
|
||||
davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pack eight raw 10-bit ecc values into ten bytes, making
|
||||
* two passes which each convert four values (in upper and
|
||||
* lower halves of two 32-bit words) into five bytes. The
|
||||
* ROM boot loader uses this same packing scheme.
|
||||
*/
|
||||
nand_davinci_readecc_4bit(info, raw_ecc);
|
||||
for (i = 0, p = raw_ecc; i < 2; i++, p += 2) {
|
||||
*ecc_code++ = p[0] & 0xff;
|
||||
*ecc_code++ = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc);
|
||||
*ecc_code++ = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0);
|
||||
*ecc_code++ = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0);
|
||||
*ecc_code++ = (p[1] >> 18) & 0xff;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Correct up to 4 bits in data we just read, using state left in the
|
||||
* hardware plus the ecc_code computed when it was first written.
|
||||
*/
|
||||
static int nand_davinci_correct_4bit(struct mtd_info *mtd,
|
||||
u_char *data, u_char *ecc_code, u_char *null)
|
||||
{
|
||||
int i;
|
||||
struct davinci_nand_info *info = to_davinci_nand(mtd);
|
||||
unsigned short ecc10[8];
|
||||
unsigned short *ecc16;
|
||||
u32 syndrome[4];
|
||||
unsigned num_errors, corrected;
|
||||
|
||||
/* All bytes 0xff? It's an erased page; ignore its ECC. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (ecc_code[i] != 0xff)
|
||||
goto compare;
|
||||
}
|
||||
return 0;
|
||||
|
||||
compare:
|
||||
/* Unpack ten bytes into eight 10 bit values. We know we're
|
||||
* little-endian, and use type punning for less shifting/masking.
|
||||
*/
|
||||
if (WARN_ON(0x01 & (unsigned) ecc_code))
|
||||
return -EINVAL;
|
||||
ecc16 = (unsigned short *)ecc_code;
|
||||
|
||||
ecc10[0] = (ecc16[0] >> 0) & 0x3ff;
|
||||
ecc10[1] = ((ecc16[0] >> 10) & 0x3f) | ((ecc16[1] << 6) & 0x3c0);
|
||||
ecc10[2] = (ecc16[1] >> 4) & 0x3ff;
|
||||
ecc10[3] = ((ecc16[1] >> 14) & 0x3) | ((ecc16[2] << 2) & 0x3fc);
|
||||
ecc10[4] = (ecc16[2] >> 8) | ((ecc16[3] << 8) & 0x300);
|
||||
ecc10[5] = (ecc16[3] >> 2) & 0x3ff;
|
||||
ecc10[6] = ((ecc16[3] >> 12) & 0xf) | ((ecc16[4] << 4) & 0x3f0);
|
||||
ecc10[7] = (ecc16[4] >> 6) & 0x3ff;
|
||||
|
||||
/* Tell ECC controller about the expected ECC codes. */
|
||||
for (i = 7; i >= 0; i--)
|
||||
davinci_nand_writel(info, NAND_4BIT_ECC_LOAD_OFFSET, ecc10[i]);
|
||||
|
||||
/* Allow time for syndrome calculation ... then read it.
|
||||
* A syndrome of all zeroes 0 means no detected errors.
|
||||
*/
|
||||
davinci_nand_readl(info, NANDFSR_OFFSET);
|
||||
nand_davinci_readecc_4bit(info, syndrome);
|
||||
if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3]))
|
||||
return 0;
|
||||
|
||||
/* Start address calculation, and wait for it to complete.
|
||||
* We _could_ start reading more data while this is working,
|
||||
* to speed up the overall page read.
|
||||
*/
|
||||
davinci_nand_writel(info, NANDFCR_OFFSET,
|
||||
davinci_nand_readl(info, NANDFCR_OFFSET) | BIT(13));
|
||||
for (;;) {
|
||||
u32 fsr = davinci_nand_readl(info, NANDFSR_OFFSET);
|
||||
|
||||
switch ((fsr >> 8) & 0x0f) {
|
||||
case 0: /* no error, should not happen */
|
||||
return 0;
|
||||
case 1: /* five or more errors detected */
|
||||
return -EIO;
|
||||
case 2: /* error addresses computed */
|
||||
case 3:
|
||||
num_errors = 1 + ((fsr >> 16) & 0x03);
|
||||
goto correct;
|
||||
default: /* still working on it */
|
||||
cpu_relax();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
correct:
|
||||
/* correct each error */
|
||||
for (i = 0, corrected = 0; i < num_errors; i++) {
|
||||
int error_address, error_value;
|
||||
|
||||
if (i > 1) {
|
||||
error_address = davinci_nand_readl(info,
|
||||
NAND_ERR_ADD2_OFFSET);
|
||||
error_value = davinci_nand_readl(info,
|
||||
NAND_ERR_ERRVAL2_OFFSET);
|
||||
} else {
|
||||
error_address = davinci_nand_readl(info,
|
||||
NAND_ERR_ADD1_OFFSET);
|
||||
error_value = davinci_nand_readl(info,
|
||||
NAND_ERR_ERRVAL1_OFFSET);
|
||||
}
|
||||
|
||||
if (i & 1) {
|
||||
error_address >>= 16;
|
||||
error_value >>= 16;
|
||||
}
|
||||
error_address &= 0x3ff;
|
||||
error_address = (512 + 7) - error_address;
|
||||
|
||||
if (error_address < 512) {
|
||||
data[error_address] ^= error_value;
|
||||
corrected++;
|
||||
}
|
||||
}
|
||||
|
||||
return corrected;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's
|
||||
* how these chips are normally wired. This translates to both 8 and 16
|
||||
@@ -294,6 +484,23 @@ static void __init nand_dm6446evm_flash_init(struct davinci_nand_info *info)
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/* An ECC layout for using 4-bit ECC with small-page flash, storing
|
||||
* ten ECC bytes plus the manufacturer's bad block marker byte, and
|
||||
* and not overlapping the default BBT markers.
|
||||
*/
|
||||
static struct nand_ecclayout hwecc4_small __initconst = {
|
||||
.eccbytes = 10,
|
||||
.eccpos = { 0, 1, 2, 3, 4,
|
||||
/* offset 5 holds the badblock marker */
|
||||
6, 7,
|
||||
13, 14, 15, },
|
||||
.oobfree = {
|
||||
{.offset = 8, .length = 5, },
|
||||
{.offset = 16, },
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static int __init nand_davinci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct davinci_nand_pdata *pdata = pdev->dev.platform_data;
|
||||
@@ -306,6 +513,10 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
||||
uint32_t val;
|
||||
nand_ecc_modes_t ecc_mode;
|
||||
|
||||
/* insist on board-specific configuration */
|
||||
if (!pdata)
|
||||
return -ENODEV;
|
||||
|
||||
/* which external chipselect will we be managing? */
|
||||
if (pdev->id < 0 || pdev->id > 3)
|
||||
return -ENODEV;
|
||||
@@ -351,7 +562,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
||||
info->chip.select_chip = nand_davinci_select_chip;
|
||||
|
||||
/* options such as NAND_USE_FLASH_BBT or 16-bit widths */
|
||||
info->chip.options = pdata ? pdata->options : 0;
|
||||
info->chip.options = pdata->options;
|
||||
|
||||
info->ioaddr = (uint32_t __force) vaddr;
|
||||
|
||||
@@ -360,14 +571,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
||||
info->mask_chipsel = pdata->mask_chipsel;
|
||||
|
||||
/* use nandboot-capable ALE/CLE masks by default */
|
||||
if (pdata && pdata->mask_ale)
|
||||
info->mask_ale = pdata->mask_cle;
|
||||
else
|
||||
info->mask_ale = MASK_ALE;
|
||||
if (pdata && pdata->mask_cle)
|
||||
info->mask_cle = pdata->mask_cle;
|
||||
else
|
||||
info->mask_cle = MASK_CLE;
|
||||
info->mask_ale = pdata->mask_cle ? : MASK_ALE;
|
||||
info->mask_cle = pdata->mask_cle ? : MASK_CLE;
|
||||
|
||||
/* Set address of hardware control function */
|
||||
info->chip.cmd_ctrl = nand_davinci_hwcontrol;
|
||||
@@ -377,30 +582,44 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
||||
info->chip.read_buf = nand_davinci_read_buf;
|
||||
info->chip.write_buf = nand_davinci_write_buf;
|
||||
|
||||
/* use board-specific ECC config; else, the best available */
|
||||
if (pdata)
|
||||
ecc_mode = pdata->ecc_mode;
|
||||
else
|
||||
ecc_mode = NAND_ECC_HW;
|
||||
/* Use board-specific ECC config */
|
||||
ecc_mode = pdata->ecc_mode;
|
||||
|
||||
ret = -EINVAL;
|
||||
switch (ecc_mode) {
|
||||
case NAND_ECC_NONE:
|
||||
case NAND_ECC_SOFT:
|
||||
pdata->ecc_bits = 0;
|
||||
break;
|
||||
case NAND_ECC_HW:
|
||||
info->chip.ecc.calculate = nand_davinci_calculate_1bit;
|
||||
info->chip.ecc.correct = nand_davinci_correct_1bit;
|
||||
info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
|
||||
info->chip.ecc.size = 512;
|
||||
info->chip.ecc.bytes = 3;
|
||||
break;
|
||||
case NAND_ECC_HW_SYNDROME:
|
||||
/* FIXME implement */
|
||||
info->chip.ecc.size = 512;
|
||||
info->chip.ecc.bytes = 10;
|
||||
if (pdata->ecc_bits == 4) {
|
||||
/* No sanity checks: CPUs must support this,
|
||||
* and the chips may not use NAND_BUSWIDTH_16.
|
||||
*/
|
||||
|
||||
dev_warn(&pdev->dev, "4-bit ECC nyet supported\n");
|
||||
/* FALL THROUGH */
|
||||
/* No sharing 4-bit hardware between chipselects yet */
|
||||
spin_lock_irq(&davinci_nand_lock);
|
||||
if (ecc4_busy)
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ecc4_busy = true;
|
||||
spin_unlock_irq(&davinci_nand_lock);
|
||||
|
||||
if (ret == -EBUSY)
|
||||
goto err_ecc;
|
||||
|
||||
info->chip.ecc.calculate = nand_davinci_calculate_4bit;
|
||||
info->chip.ecc.correct = nand_davinci_correct_4bit;
|
||||
info->chip.ecc.hwctl = nand_davinci_hwctl_4bit;
|
||||
info->chip.ecc.bytes = 10;
|
||||
} else {
|
||||
info->chip.ecc.calculate = nand_davinci_calculate_1bit;
|
||||
info->chip.ecc.correct = nand_davinci_correct_1bit;
|
||||
info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
|
||||
info->chip.ecc.bytes = 3;
|
||||
}
|
||||
info->chip.ecc.size = 512;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto err_ecc;
|
||||
@@ -441,12 +660,56 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
||||
spin_unlock_irq(&davinci_nand_lock);
|
||||
|
||||
/* Scan to find existence of the device(s) */
|
||||
ret = nand_scan(&info->mtd, pdata->mask_chipsel ? 2 : 1);
|
||||
ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
|
||||
goto err_scan;
|
||||
}
|
||||
|
||||
/* Update ECC layout if needed ... for 1-bit HW ECC, the default
|
||||
* is OK, but it allocates 6 bytes when only 3 are needed (for
|
||||
* each 512 bytes). For the 4-bit HW ECC, that default is not
|
||||
* usable: 10 bytes are needed, not 6.
|
||||
*/
|
||||
if (pdata->ecc_bits == 4) {
|
||||
int chunks = info->mtd.writesize / 512;
|
||||
|
||||
if (!chunks || info->mtd.oobsize < 16) {
|
||||
dev_dbg(&pdev->dev, "too small\n");
|
||||
ret = -EINVAL;
|
||||
goto err_scan;
|
||||
}
|
||||
|
||||
/* For small page chips, preserve the manufacturer's
|
||||
* badblock marking data ... and make sure a flash BBT
|
||||
* table marker fits in the free bytes.
|
||||
*/
|
||||
if (chunks == 1) {
|
||||
info->ecclayout = hwecc4_small;
|
||||
info->ecclayout.oobfree[1].length =
|
||||
info->mtd.oobsize - 16;
|
||||
goto syndrome_done;
|
||||
}
|
||||
|
||||
/* For large page chips we'll be wanting to use a
|
||||
* not-yet-implemented mode that reads OOB data
|
||||
* before reading the body of the page, to avoid
|
||||
* the "infix OOB" model of NAND_ECC_HW_SYNDROME
|
||||
* (and preserve manufacturer badblock markings).
|
||||
*/
|
||||
dev_warn(&pdev->dev, "no 4-bit ECC support yet "
|
||||
"for large page NAND\n");
|
||||
ret = -EIO;
|
||||
goto err_scan;
|
||||
|
||||
syndrome_done:
|
||||
info->chip.ecc.layout = &info->ecclayout;
|
||||
}
|
||||
|
||||
ret = nand_scan_tail(&info->mtd);
|
||||
if (ret < 0)
|
||||
goto err_scan;
|
||||
|
||||
if (mtd_has_partitions()) {
|
||||
struct mtd_partition *mtd_parts = NULL;
|
||||
int mtd_parts_nb = 0;
|
||||
@@ -455,22 +718,11 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
||||
static const char *probes[] __initconst =
|
||||
{ "cmdlinepart", NULL };
|
||||
|
||||
const char *master_name;
|
||||
|
||||
/* Set info->mtd.name = 0 temporarily */
|
||||
master_name = info->mtd.name;
|
||||
info->mtd.name = (char *)0;
|
||||
|
||||
/* info->mtd.name == 0, means: don't bother checking
|
||||
<mtd-id> */
|
||||
mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes,
|
||||
&mtd_parts, 0);
|
||||
|
||||
/* Restore info->mtd.name */
|
||||
info->mtd.name = master_name;
|
||||
}
|
||||
|
||||
if (mtd_parts_nb <= 0 && pdata) {
|
||||
if (mtd_parts_nb <= 0) {
|
||||
mtd_parts = pdata->parts;
|
||||
mtd_parts_nb = pdata->nr_parts;
|
||||
}
|
||||
@@ -483,7 +735,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
||||
info->partitioned = true;
|
||||
}
|
||||
|
||||
} else if (pdata && pdata->nr_parts) {
|
||||
} else if (pdata->nr_parts) {
|
||||
dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n",
|
||||
pdata->nr_parts, info->mtd.name);
|
||||
}
|
||||
@@ -509,6 +761,11 @@ err_scan:
|
||||
err_clk_enable:
|
||||
clk_put(info->clk);
|
||||
|
||||
spin_lock_irq(&davinci_nand_lock);
|
||||
if (ecc_mode == NAND_ECC_HW_SYNDROME)
|
||||
ecc4_busy = false;
|
||||
spin_unlock_irq(&davinci_nand_lock);
|
||||
|
||||
err_ecc:
|
||||
err_clk:
|
||||
err_ioremap:
|
||||
@@ -532,6 +789,11 @@ static int __exit nand_davinci_remove(struct platform_device *pdev)
|
||||
else
|
||||
status = del_mtd_device(&info->mtd);
|
||||
|
||||
spin_lock_irq(&davinci_nand_lock);
|
||||
if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)
|
||||
ecc4_busy = false;
|
||||
spin_unlock_irq(&davinci_nand_lock);
|
||||
|
||||
iounmap(info->base);
|
||||
iounmap(info->vaddr);
|
||||
|
||||
|
@@ -138,7 +138,14 @@ static struct nand_ecclayout nand_hw_eccoob_8 = {
|
||||
static struct nand_ecclayout nand_hw_eccoob_16 = {
|
||||
.eccbytes = 5,
|
||||
.eccpos = {6, 7, 8, 9, 10},
|
||||
.oobfree = {{0, 6}, {12, 4}, }
|
||||
.oobfree = {{0, 5}, {11, 5}, }
|
||||
};
|
||||
|
||||
static struct nand_ecclayout nand_hw_eccoob_64 = {
|
||||
.eccbytes = 20,
|
||||
.eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26,
|
||||
38, 39, 40, 41, 42, 54, 55, 56, 57, 58},
|
||||
.oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, }
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
@@ -192,7 +199,7 @@ static void wait_op_done(struct mxc_nand_host *host, int max_retries,
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
if (max_retries <= 0)
|
||||
if (max_retries < 0)
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n",
|
||||
__func__, param);
|
||||
}
|
||||
@@ -795,9 +802,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
|
||||
send_addr(host, (page_addr & 0xff), false);
|
||||
|
||||
if (host->pagesize_2k) {
|
||||
send_addr(host, (page_addr >> 8) & 0xFF, false);
|
||||
if (mtd->size >= 0x40000000)
|
||||
if (mtd->size >= 0x10000000) {
|
||||
/* paddr_8 - paddr_15 */
|
||||
send_addr(host, (page_addr >> 8) & 0xff, false);
|
||||
send_addr(host, (page_addr >> 16) & 0xff, true);
|
||||
} else
|
||||
/* paddr_8 - paddr_15 */
|
||||
send_addr(host, (page_addr >> 8) & 0xff, true);
|
||||
} else {
|
||||
/* One more address cycle for higher density devices */
|
||||
if (mtd->size >= 0x4000000) {
|
||||
@@ -923,7 +934,6 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
||||
this->ecc.mode = NAND_ECC_HW;
|
||||
this->ecc.size = 512;
|
||||
this->ecc.bytes = 3;
|
||||
this->ecc.layout = &nand_hw_eccoob_8;
|
||||
tmp = readw(host->regs + NFC_CONFIG1);
|
||||
tmp |= NFC_ECC_EN;
|
||||
writew(tmp, host->regs + NFC_CONFIG1);
|
||||
@@ -957,12 +967,44 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
||||
this->ecc.layout = &nand_hw_eccoob_16;
|
||||
}
|
||||
|
||||
host->pagesize_2k = 0;
|
||||
/* first scan to find the device and get the page size */
|
||||
if (nand_scan_ident(mtd, 1)) {
|
||||
err = -ENXIO;
|
||||
goto escan;
|
||||
}
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan(mtd, 1)) {
|
||||
DEBUG(MTD_DEBUG_LEVEL0,
|
||||
"MXC_ND: Unable to find any NAND device.\n");
|
||||
host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0;
|
||||
|
||||
if (this->ecc.mode == NAND_ECC_HW) {
|
||||
switch (mtd->oobsize) {
|
||||
case 8:
|
||||
this->ecc.layout = &nand_hw_eccoob_8;
|
||||
break;
|
||||
case 16:
|
||||
this->ecc.layout = &nand_hw_eccoob_16;
|
||||
break;
|
||||
case 64:
|
||||
this->ecc.layout = &nand_hw_eccoob_64;
|
||||
break;
|
||||
default:
|
||||
/* page size not handled by HW ECC */
|
||||
/* switching back to soft ECC */
|
||||
this->ecc.size = 512;
|
||||
this->ecc.bytes = 3;
|
||||
this->ecc.layout = &nand_hw_eccoob_8;
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
this->ecc.calculate = NULL;
|
||||
this->ecc.correct = NULL;
|
||||
this->ecc.hwctl = NULL;
|
||||
tmp = readw(host->regs + NFC_CONFIG1);
|
||||
tmp &= ~NFC_ECC_EN;
|
||||
writew(tmp, host->regs + NFC_CONFIG1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* second phase scan */
|
||||
if (nand_scan_tail(mtd)) {
|
||||
err = -ENXIO;
|
||||
goto escan;
|
||||
}
|
||||
@@ -985,7 +1027,7 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
escan:
|
||||
free_irq(host->irq, NULL);
|
||||
free_irq(host->irq, host);
|
||||
eirq:
|
||||
iounmap(host->regs);
|
||||
eres:
|
||||
@@ -1005,7 +1047,7 @@ static int __devexit mxcnd_remove(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
nand_release(&host->mtd);
|
||||
free_irq(host->irq, NULL);
|
||||
free_irq(host->irq, host);
|
||||
iounmap(host->regs);
|
||||
kfree(host);
|
||||
|
||||
|
@@ -2756,7 +2756,8 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
* the out of band area
|
||||
*/
|
||||
chip->ecc.layout->oobavail = 0;
|
||||
for (i = 0; chip->ecc.layout->oobfree[i].length; i++)
|
||||
for (i = 0; chip->ecc.layout->oobfree[i].length
|
||||
&& i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++)
|
||||
chip->ecc.layout->oobavail +=
|
||||
chip->ecc.layout->oobfree[i].length;
|
||||
mtd->oobavail = chip->ecc.layout->oobavail;
|
||||
|
@@ -428,8 +428,8 @@ EXPORT_SYMBOL(nand_calculate_ecc);
|
||||
int nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
|
||||
unsigned char *read_ecc, unsigned char *calc_ecc)
|
||||
{
|
||||
unsigned char b0, b1, b2;
|
||||
unsigned char byte_addr, bit_addr;
|
||||
unsigned char b0, b1, b2, bit_addr;
|
||||
unsigned int byte_addr;
|
||||
/* 256 or 512 bytes/ecc */
|
||||
const uint32_t eccsize_mult =
|
||||
(((struct nand_chip *)mtd->priv)->ecc.size) >> 8;
|
||||
|
776
drivers/mtd/nand/omap2.c
Normal file
776
drivers/mtd/nand/omap2.c
Normal file
@@ -0,0 +1,776 @@
|
||||
/*
|
||||
* Copyright © 2004 Texas Instruments, Jian Zhang <jzhang@ti.com>
|
||||
* Copyright © 2004 Micron Technology Inc.
|
||||
* Copyright © 2004 David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
|
||||
#include <mach/gpmc.h>
|
||||
#include <mach/nand.h>
|
||||
|
||||
#define GPMC_IRQ_STATUS 0x18
|
||||
#define GPMC_ECC_CONFIG 0x1F4
|
||||
#define GPMC_ECC_CONTROL 0x1F8
|
||||
#define GPMC_ECC_SIZE_CONFIG 0x1FC
|
||||
#define GPMC_ECC1_RESULT 0x200
|
||||
|
||||
#define DRIVER_NAME "omap2-nand"
|
||||
|
||||
/* size (4 KiB) for IO mapping */
|
||||
#define NAND_IO_SIZE SZ_4K
|
||||
|
||||
#define NAND_WP_OFF 0
|
||||
#define NAND_WP_BIT 0x00000010
|
||||
#define WR_RD_PIN_MONITORING 0x00600000
|
||||
|
||||
#define GPMC_BUF_FULL 0x00000001
|
||||
#define GPMC_BUF_EMPTY 0x00000000
|
||||
|
||||
#define NAND_Ecc_P1e (1 << 0)
|
||||
#define NAND_Ecc_P2e (1 << 1)
|
||||
#define NAND_Ecc_P4e (1 << 2)
|
||||
#define NAND_Ecc_P8e (1 << 3)
|
||||
#define NAND_Ecc_P16e (1 << 4)
|
||||
#define NAND_Ecc_P32e (1 << 5)
|
||||
#define NAND_Ecc_P64e (1 << 6)
|
||||
#define NAND_Ecc_P128e (1 << 7)
|
||||
#define NAND_Ecc_P256e (1 << 8)
|
||||
#define NAND_Ecc_P512e (1 << 9)
|
||||
#define NAND_Ecc_P1024e (1 << 10)
|
||||
#define NAND_Ecc_P2048e (1 << 11)
|
||||
|
||||
#define NAND_Ecc_P1o (1 << 16)
|
||||
#define NAND_Ecc_P2o (1 << 17)
|
||||
#define NAND_Ecc_P4o (1 << 18)
|
||||
#define NAND_Ecc_P8o (1 << 19)
|
||||
#define NAND_Ecc_P16o (1 << 20)
|
||||
#define NAND_Ecc_P32o (1 << 21)
|
||||
#define NAND_Ecc_P64o (1 << 22)
|
||||
#define NAND_Ecc_P128o (1 << 23)
|
||||
#define NAND_Ecc_P256o (1 << 24)
|
||||
#define NAND_Ecc_P512o (1 << 25)
|
||||
#define NAND_Ecc_P1024o (1 << 26)
|
||||
#define NAND_Ecc_P2048o (1 << 27)
|
||||
|
||||
#define TF(value) (value ? 1 : 0)
|
||||
|
||||
#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0)
|
||||
#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1)
|
||||
#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2)
|
||||
#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3)
|
||||
#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4)
|
||||
#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5)
|
||||
#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6)
|
||||
#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7)
|
||||
|
||||
#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0)
|
||||
#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1)
|
||||
#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2)
|
||||
#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3)
|
||||
#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4)
|
||||
#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5)
|
||||
#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6)
|
||||
#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7)
|
||||
|
||||
#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0)
|
||||
#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1)
|
||||
#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2)
|
||||
#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3)
|
||||
#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4)
|
||||
#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5)
|
||||
#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6)
|
||||
#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7)
|
||||
|
||||
#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0)
|
||||
#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1)
|
||||
#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2)
|
||||
#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3)
|
||||
#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4)
|
||||
#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5)
|
||||
#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6)
|
||||
#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7)
|
||||
|
||||
#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0)
|
||||
#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1)
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
static const char *part_probes[] = { "cmdlinepart", NULL };
|
||||
#endif
|
||||
|
||||
struct omap_nand_info {
|
||||
struct nand_hw_control controller;
|
||||
struct omap_nand_platform_data *pdata;
|
||||
struct mtd_info mtd;
|
||||
struct mtd_partition *parts;
|
||||
struct nand_chip nand;
|
||||
struct platform_device *pdev;
|
||||
|
||||
int gpmc_cs;
|
||||
unsigned long phys_base;
|
||||
void __iomem *gpmc_cs_baseaddr;
|
||||
void __iomem *gpmc_baseaddr;
|
||||
};
|
||||
|
||||
/**
|
||||
* omap_nand_wp - This function enable or disable the Write Protect feature
|
||||
* @mtd: MTD device structure
|
||||
* @mode: WP ON/OFF
|
||||
*/
|
||||
static void omap_nand_wp(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd,
|
||||
struct omap_nand_info, mtd);
|
||||
|
||||
unsigned long config = __raw_readl(info->gpmc_baseaddr + GPMC_CONFIG);
|
||||
|
||||
if (mode)
|
||||
config &= ~(NAND_WP_BIT); /* WP is ON */
|
||||
else
|
||||
config |= (NAND_WP_BIT); /* WP is OFF */
|
||||
|
||||
__raw_writel(config, (info->gpmc_baseaddr + GPMC_CONFIG));
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_hwcontrol - hardware specific access to control-lines
|
||||
* @mtd: MTD device structure
|
||||
* @cmd: command to device
|
||||
* @ctrl:
|
||||
* NAND_NCE: bit 0 -> don't care
|
||||
* NAND_CLE: bit 1 -> Command Latch
|
||||
* NAND_ALE: bit 2 -> Address Latch
|
||||
*
|
||||
* NOTE: boards may use different bits for these!!
|
||||
*/
|
||||
static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd,
|
||||
struct omap_nand_info, mtd);
|
||||
switch (ctrl) {
|
||||
case NAND_CTRL_CHANGE | NAND_CTRL_CLE:
|
||||
info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
|
||||
GPMC_CS_NAND_COMMAND;
|
||||
info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
|
||||
GPMC_CS_NAND_DATA;
|
||||
break;
|
||||
|
||||
case NAND_CTRL_CHANGE | NAND_CTRL_ALE:
|
||||
info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
|
||||
GPMC_CS_NAND_ADDRESS;
|
||||
info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
|
||||
GPMC_CS_NAND_DATA;
|
||||
break;
|
||||
|
||||
case NAND_CTRL_CHANGE | NAND_NCE:
|
||||
info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
|
||||
GPMC_CS_NAND_DATA;
|
||||
info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
|
||||
GPMC_CS_NAND_DATA;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
__raw_writeb(cmd, info->nand.IO_ADDR_W);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_read_buf16 - read data from NAND controller into buffer
|
||||
* @mtd: MTD device structure
|
||||
* @buf: buffer to store date
|
||||
* @len: number of bytes to read
|
||||
*/
|
||||
static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
|
||||
{
|
||||
struct nand_chip *nand = mtd->priv;
|
||||
|
||||
__raw_readsw(nand->IO_ADDR_R, buf, len / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_write_buf16 - write buffer to NAND controller
|
||||
* @mtd: MTD device structure
|
||||
* @buf: data buffer
|
||||
* @len: number of bytes to write
|
||||
*/
|
||||
static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd,
|
||||
struct omap_nand_info, mtd);
|
||||
u16 *p = (u16 *) buf;
|
||||
|
||||
/* FIXME try bursts of writesw() or DMA ... */
|
||||
len >>= 1;
|
||||
|
||||
while (len--) {
|
||||
writew(*p++, info->nand.IO_ADDR_W);
|
||||
|
||||
while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr +
|
||||
GPMC_STATUS) & GPMC_BUF_FULL))
|
||||
;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* omap_verify_buf - Verify chip data against buffer
|
||||
* @mtd: MTD device structure
|
||||
* @buf: buffer containing the data to compare
|
||||
* @len: number of bytes to compare
|
||||
*/
|
||||
static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
u16 *p = (u16 *) buf;
|
||||
|
||||
len >>= 1;
|
||||
while (len--) {
|
||||
if (*p++ != cpu_to_le16(readw(info->nand.IO_ADDR_R)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_OMAP_HWECC
|
||||
/**
|
||||
* omap_hwecc_init - Initialize the HW ECC for NAND flash in GPMC controller
|
||||
* @mtd: MTD device structure
|
||||
*/
|
||||
static void omap_hwecc_init(struct mtd_info *mtd)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
unsigned long val = 0x0;
|
||||
|
||||
/* Read from ECC Control Register */
|
||||
val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONTROL);
|
||||
/* Clear all ECC | Enable Reg1 */
|
||||
val = ((0x00000001<<8) | 0x00000001);
|
||||
__raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
|
||||
|
||||
/* Read from ECC Size Config Register */
|
||||
val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
|
||||
/* ECCSIZE1=512 | Select eccResultsize[0-3] */
|
||||
val = ((((chip->ecc.size >> 1) - 1) << 22) | (0x0000000F));
|
||||
__raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
|
||||
}
|
||||
|
||||
/**
|
||||
* gen_true_ecc - This function will generate true ECC value
|
||||
* @ecc_buf: buffer to store ecc code
|
||||
*
|
||||
* This generated true ECC value can be used when correcting
|
||||
* data read from NAND flash memory core
|
||||
*/
|
||||
static void gen_true_ecc(u8 *ecc_buf)
|
||||
{
|
||||
u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) |
|
||||
((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8);
|
||||
|
||||
ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) |
|
||||
P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp));
|
||||
ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) |
|
||||
P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp));
|
||||
ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) |
|
||||
P1e(tmp) | P2048o(tmp) | P2048e(tmp));
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data
|
||||
* @ecc_data1: ecc code from nand spare area
|
||||
* @ecc_data2: ecc code from hardware register obtained from hardware ecc
|
||||
* @page_data: page data
|
||||
*
|
||||
* This function compares two ECC's and indicates if there is an error.
|
||||
* If the error can be corrected it will be corrected to the buffer.
|
||||
*/
|
||||
static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
|
||||
u8 *ecc_data2, /* read from register */
|
||||
u8 *page_data)
|
||||
{
|
||||
uint i;
|
||||
u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8];
|
||||
u8 comp0_bit[8], comp1_bit[8], comp2_bit[8];
|
||||
u8 ecc_bit[24];
|
||||
u8 ecc_sum = 0;
|
||||
u8 find_bit = 0;
|
||||
uint find_byte = 0;
|
||||
int isEccFF;
|
||||
|
||||
isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF);
|
||||
|
||||
gen_true_ecc(ecc_data1);
|
||||
gen_true_ecc(ecc_data2);
|
||||
|
||||
for (i = 0; i <= 2; i++) {
|
||||
*(ecc_data1 + i) = ~(*(ecc_data1 + i));
|
||||
*(ecc_data2 + i) = ~(*(ecc_data2 + i));
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
tmp0_bit[i] = *ecc_data1 % 2;
|
||||
*ecc_data1 = *ecc_data1 / 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
tmp1_bit[i] = *(ecc_data1 + 1) % 2;
|
||||
*(ecc_data1 + 1) = *(ecc_data1 + 1) / 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
tmp2_bit[i] = *(ecc_data1 + 2) % 2;
|
||||
*(ecc_data1 + 2) = *(ecc_data1 + 2) / 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
comp0_bit[i] = *ecc_data2 % 2;
|
||||
*ecc_data2 = *ecc_data2 / 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
comp1_bit[i] = *(ecc_data2 + 1) % 2;
|
||||
*(ecc_data2 + 1) = *(ecc_data2 + 1) / 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
comp2_bit[i] = *(ecc_data2 + 2) % 2;
|
||||
*(ecc_data2 + 2) = *(ecc_data2 + 2) / 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2];
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i];
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i];
|
||||
|
||||
ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0];
|
||||
ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1];
|
||||
|
||||
for (i = 0; i < 24; i++)
|
||||
ecc_sum += ecc_bit[i];
|
||||
|
||||
switch (ecc_sum) {
|
||||
case 0:
|
||||
/* Not reached because this function is not called if
|
||||
* ECC values are equal
|
||||
*/
|
||||
return 0;
|
||||
|
||||
case 1:
|
||||
/* Uncorrectable error */
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n");
|
||||
return -1;
|
||||
|
||||
case 11:
|
||||
/* UN-Correctable error */
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR B\n");
|
||||
return -1;
|
||||
|
||||
case 12:
|
||||
/* Correctable error */
|
||||
find_byte = (ecc_bit[23] << 8) +
|
||||
(ecc_bit[21] << 7) +
|
||||
(ecc_bit[19] << 6) +
|
||||
(ecc_bit[17] << 5) +
|
||||
(ecc_bit[15] << 4) +
|
||||
(ecc_bit[13] << 3) +
|
||||
(ecc_bit[11] << 2) +
|
||||
(ecc_bit[9] << 1) +
|
||||
ecc_bit[7];
|
||||
|
||||
find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1];
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "Correcting single bit ECC error at "
|
||||
"offset: %d, bit: %d\n", find_byte, find_bit);
|
||||
|
||||
page_data[find_byte] ^= (1 << find_bit);
|
||||
|
||||
return 0;
|
||||
default:
|
||||
if (isEccFF) {
|
||||
if (ecc_data2[0] == 0 &&
|
||||
ecc_data2[1] == 0 &&
|
||||
ecc_data2[2] == 0)
|
||||
return 0;
|
||||
}
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "UNCORRECTED_ERROR default\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_correct_data - Compares the ECC read with HW generated ECC
|
||||
* @mtd: MTD device structure
|
||||
* @dat: page data
|
||||
* @read_ecc: ecc read from nand flash
|
||||
* @calc_ecc: ecc read from HW ECC registers
|
||||
*
|
||||
* Compares the ecc read from nand spare area with ECC registers values
|
||||
* and if ECC's mismached, it will call 'omap_compare_ecc' for error detection
|
||||
* and correction.
|
||||
*/
|
||||
static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
int blockCnt = 0, i = 0, ret = 0;
|
||||
|
||||
/* Ex NAND_ECC_HW12_2048 */
|
||||
if ((info->nand.ecc.mode == NAND_ECC_HW) &&
|
||||
(info->nand.ecc.size == 2048))
|
||||
blockCnt = 4;
|
||||
else
|
||||
blockCnt = 1;
|
||||
|
||||
for (i = 0; i < blockCnt; i++) {
|
||||
if (memcmp(read_ecc, calc_ecc, 3) != 0) {
|
||||
ret = omap_compare_ecc(read_ecc, calc_ecc, dat);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
read_ecc += 3;
|
||||
calc_ecc += 3;
|
||||
dat += 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_calcuate_ecc - Generate non-inverted ECC bytes.
|
||||
* @mtd: MTD device structure
|
||||
* @dat: The pointer to data on which ecc is computed
|
||||
* @ecc_code: The ecc_code buffer
|
||||
*
|
||||
* Using noninverted ECC can be considered ugly since writing a blank
|
||||
* page ie. padding will clear the ECC bytes. This is no problem as long
|
||||
* nobody is trying to write data on the seemingly unused page. Reading
|
||||
* an erased page will produce an ECC mismatch between generated and read
|
||||
* ECC bytes that has to be dealt with separately.
|
||||
*/
|
||||
static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
|
||||
u_char *ecc_code)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
unsigned long val = 0x0;
|
||||
unsigned long reg;
|
||||
|
||||
/* Start Reading from HW ECC1_Result = 0x200 */
|
||||
reg = (unsigned long)(info->gpmc_baseaddr + GPMC_ECC1_RESULT);
|
||||
val = __raw_readl(reg);
|
||||
*ecc_code++ = val; /* P128e, ..., P1e */
|
||||
*ecc_code++ = val >> 16; /* P128o, ..., P1o */
|
||||
/* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */
|
||||
*ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
|
||||
reg += 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_enable_hwecc - This function enables the hardware ecc functionality
|
||||
* @mtd: MTD device structure
|
||||
* @mode: Read/Write mode
|
||||
*/
|
||||
static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
|
||||
unsigned long val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONFIG);
|
||||
|
||||
switch (mode) {
|
||||
case NAND_ECC_READ:
|
||||
__raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
|
||||
/* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
|
||||
val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
|
||||
break;
|
||||
case NAND_ECC_READSYN:
|
||||
__raw_writel(0x100, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
|
||||
/* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
|
||||
val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
|
||||
break;
|
||||
case NAND_ECC_WRITE:
|
||||
__raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
|
||||
/* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
|
||||
val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
|
||||
break;
|
||||
default:
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "Error: Unrecognized Mode[%d]!\n",
|
||||
mode);
|
||||
break;
|
||||
}
|
||||
|
||||
__raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONFIG);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* omap_wait - wait until the command is done
|
||||
* @mtd: MTD device structure
|
||||
* @chip: NAND Chip structure
|
||||
*
|
||||
* Wait function is called during Program and erase operations and
|
||||
* the way it is called from MTD layer, we should wait till the NAND
|
||||
* chip is ready after the programming/erase operation has completed.
|
||||
*
|
||||
* Erase can take up to 400ms and program up to 20ms according to
|
||||
* general NAND and SmartMedia specs
|
||||
*/
|
||||
static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
unsigned long timeo = jiffies;
|
||||
int status, state = this->state;
|
||||
|
||||
if (state == FL_ERASING)
|
||||
timeo += (HZ * 400) / 1000;
|
||||
else
|
||||
timeo += (HZ * 20) / 1000;
|
||||
|
||||
this->IO_ADDR_W = (void *) info->gpmc_cs_baseaddr +
|
||||
GPMC_CS_NAND_COMMAND;
|
||||
this->IO_ADDR_R = (void *) info->gpmc_cs_baseaddr + GPMC_CS_NAND_DATA;
|
||||
|
||||
__raw_writeb(NAND_CMD_STATUS & 0xFF, this->IO_ADDR_W);
|
||||
|
||||
while (time_before(jiffies, timeo)) {
|
||||
status = __raw_readb(this->IO_ADDR_R);
|
||||
if (!(status & 0x40))
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_dev_ready - calls the platform specific dev_ready function
|
||||
* @mtd: MTD device structure
|
||||
*/
|
||||
static int omap_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
unsigned int val = __raw_readl(info->gpmc_baseaddr + GPMC_IRQ_STATUS);
|
||||
|
||||
if ((val & 0x100) == 0x100) {
|
||||
/* Clear IRQ Interrupt */
|
||||
val |= 0x100;
|
||||
val &= ~(0x0);
|
||||
__raw_writel(val, info->gpmc_baseaddr + GPMC_IRQ_STATUS);
|
||||
} else {
|
||||
unsigned int cnt = 0;
|
||||
while (cnt++ < 0x1FF) {
|
||||
if ((val & 0x100) == 0x100)
|
||||
return 0;
|
||||
val = __raw_readl(info->gpmc_baseaddr +
|
||||
GPMC_IRQ_STATUS);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __devinit omap_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_nand_info *info;
|
||||
struct omap_nand_platform_data *pdata;
|
||||
int err;
|
||||
unsigned long val;
|
||||
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (pdata == NULL) {
|
||||
dev_err(&pdev->dev, "platform data missing\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
spin_lock_init(&info->controller.lock);
|
||||
init_waitqueue_head(&info->controller.wq);
|
||||
|
||||
info->pdev = pdev;
|
||||
|
||||
info->gpmc_cs = pdata->cs;
|
||||
info->gpmc_baseaddr = pdata->gpmc_baseaddr;
|
||||
info->gpmc_cs_baseaddr = pdata->gpmc_cs_baseaddr;
|
||||
|
||||
info->mtd.priv = &info->nand;
|
||||
info->mtd.name = dev_name(&pdev->dev);
|
||||
info->mtd.owner = THIS_MODULE;
|
||||
|
||||
err = gpmc_cs_request(info->gpmc_cs, NAND_IO_SIZE, &info->phys_base);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "Cannot request GPMC CS\n");
|
||||
goto out_free_info;
|
||||
}
|
||||
|
||||
/* Enable RD PIN Monitoring Reg */
|
||||
if (pdata->dev_ready) {
|
||||
val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1);
|
||||
val |= WR_RD_PIN_MONITORING;
|
||||
gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG1, val);
|
||||
}
|
||||
|
||||
val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG7);
|
||||
val &= ~(0xf << 8);
|
||||
val |= (0xc & 0xf) << 8;
|
||||
gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG7, val);
|
||||
|
||||
/* NAND write protect off */
|
||||
omap_nand_wp(&info->mtd, NAND_WP_OFF);
|
||||
|
||||
if (!request_mem_region(info->phys_base, NAND_IO_SIZE,
|
||||
pdev->dev.driver->name)) {
|
||||
err = -EBUSY;
|
||||
goto out_free_cs;
|
||||
}
|
||||
|
||||
info->nand.IO_ADDR_R = ioremap(info->phys_base, NAND_IO_SIZE);
|
||||
if (!info->nand.IO_ADDR_R) {
|
||||
err = -ENOMEM;
|
||||
goto out_release_mem_region;
|
||||
}
|
||||
info->nand.controller = &info->controller;
|
||||
|
||||
info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
|
||||
info->nand.cmd_ctrl = omap_hwcontrol;
|
||||
|
||||
/* REVISIT: only supports 16-bit NAND flash */
|
||||
|
||||
info->nand.read_buf = omap_read_buf16;
|
||||
info->nand.write_buf = omap_write_buf16;
|
||||
info->nand.verify_buf = omap_verify_buf;
|
||||
|
||||
/*
|
||||
* If RDY/BSY line is connected to OMAP then use the omap ready
|
||||
* funcrtion and the generic nand_wait function which reads the status
|
||||
* register after monitoring the RDY/BSY line.Otherwise use a standard
|
||||
* chip delay which is slightly more than tR (AC Timing) of the NAND
|
||||
* device and read status register until you get a failure or success
|
||||
*/
|
||||
if (pdata->dev_ready) {
|
||||
info->nand.dev_ready = omap_dev_ready;
|
||||
info->nand.chip_delay = 0;
|
||||
} else {
|
||||
info->nand.waitfunc = omap_wait;
|
||||
info->nand.chip_delay = 50;
|
||||
}
|
||||
|
||||
info->nand.options |= NAND_SKIP_BBTSCAN;
|
||||
if ((gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1) & 0x3000)
|
||||
== 0x1000)
|
||||
info->nand.options |= NAND_BUSWIDTH_16;
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_OMAP_HWECC
|
||||
info->nand.ecc.bytes = 3;
|
||||
info->nand.ecc.size = 512;
|
||||
info->nand.ecc.calculate = omap_calculate_ecc;
|
||||
info->nand.ecc.hwctl = omap_enable_hwecc;
|
||||
info->nand.ecc.correct = omap_correct_data;
|
||||
info->nand.ecc.mode = NAND_ECC_HW;
|
||||
|
||||
/* init HW ECC */
|
||||
omap_hwecc_init(&info->mtd);
|
||||
#else
|
||||
info->nand.ecc.mode = NAND_ECC_SOFT;
|
||||
#endif
|
||||
|
||||
/* DIP switches on some boards change between 8 and 16 bit
|
||||
* bus widths for flash. Try the other width if the first try fails.
|
||||
*/
|
||||
if (nand_scan(&info->mtd, 1)) {
|
||||
info->nand.options ^= NAND_BUSWIDTH_16;
|
||||
if (nand_scan(&info->mtd, 1)) {
|
||||
err = -ENXIO;
|
||||
goto out_release_mem_region;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
|
||||
if (err > 0)
|
||||
add_mtd_partitions(&info->mtd, info->parts, err);
|
||||
else if (pdata->parts)
|
||||
add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
|
||||
else
|
||||
#endif
|
||||
add_mtd_device(&info->mtd);
|
||||
|
||||
platform_set_drvdata(pdev, &info->mtd);
|
||||
|
||||
return 0;
|
||||
|
||||
out_release_mem_region:
|
||||
release_mem_region(info->phys_base, NAND_IO_SIZE);
|
||||
out_free_cs:
|
||||
gpmc_cs_free(info->gpmc_cs);
|
||||
out_free_info:
|
||||
kfree(info);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int omap_nand_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mtd_info *mtd = platform_get_drvdata(pdev);
|
||||
struct omap_nand_info *info = mtd->priv;
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
/* Release NAND device, its internal structures and partitions */
|
||||
nand_release(&info->mtd);
|
||||
iounmap(info->nand.IO_ADDR_R);
|
||||
kfree(&info->mtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver omap_nand_driver = {
|
||||
.probe = omap_nand_probe,
|
||||
.remove = omap_nand_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init omap_nand_init(void)
|
||||
{
|
||||
printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME);
|
||||
return platform_driver_register(&omap_nand_driver);
|
||||
}
|
||||
|
||||
static void __exit omap_nand_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&omap_nand_driver);
|
||||
}
|
||||
|
||||
module_init(omap_nand_init);
|
||||
module_exit(omap_nand_exit);
|
||||
|
||||
MODULE_ALIAS(DRIVER_NAME);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards");
|
@@ -47,6 +47,28 @@ static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl
|
||||
writeb(cmd, nc->IO_ADDR_W + offs);
|
||||
}
|
||||
|
||||
static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
void __iomem *io_base = chip->IO_ADDR_R;
|
||||
uint64_t *buf64;
|
||||
int i = 0;
|
||||
|
||||
while (len && (unsigned long)buf & 7) {
|
||||
*buf++ = readb(io_base);
|
||||
len--;
|
||||
}
|
||||
buf64 = (uint64_t *)buf;
|
||||
while (i < len/8) {
|
||||
uint64_t x;
|
||||
asm ("ldrd\t%0, [%1]" : "=r" (x) : "r" (io_base));
|
||||
buf64[i++] = x;
|
||||
}
|
||||
i *= 8;
|
||||
while (i < len)
|
||||
buf[i++] = readb(io_base);
|
||||
}
|
||||
|
||||
static int __init orion_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
@@ -83,6 +105,7 @@ static int __init orion_nand_probe(struct platform_device *pdev)
|
||||
nc->priv = board;
|
||||
nc->IO_ADDR_R = nc->IO_ADDR_W = io_base;
|
||||
nc->cmd_ctrl = orion_nand_cmd_ctrl;
|
||||
nc->read_buf = orion_nand_read_buf;
|
||||
nc->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
if (board->chip_delay)
|
||||
|
@@ -61,6 +61,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
|
||||
data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl;
|
||||
data->chip.dev_ready = pdata->ctrl.dev_ready;
|
||||
data->chip.select_chip = pdata->ctrl.select_chip;
|
||||
data->chip.write_buf = pdata->ctrl.write_buf;
|
||||
data->chip.read_buf = pdata->ctrl.read_buf;
|
||||
data->chip.chip_delay = pdata->chip.chip_delay;
|
||||
data->chip.options |= pdata->chip.options;
|
||||
|
||||
@@ -70,6 +72,13 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
/* Handle any platform specific setup */
|
||||
if (pdata->ctrl.probe) {
|
||||
res = pdata->ctrl.probe(pdev);
|
||||
if (res)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Scan to find existance of the device */
|
||||
if (nand_scan(&data->mtd, 1)) {
|
||||
res = -ENXIO;
|
||||
@@ -86,6 +95,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (pdata->chip.set_parts)
|
||||
pdata->chip.set_parts(data->mtd.size, &pdata->chip);
|
||||
if (pdata->chip.partitions) {
|
||||
data->parts = pdata->chip.partitions;
|
||||
res = add_mtd_partitions(&data->mtd, data->parts,
|
||||
@@ -99,6 +110,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
|
||||
|
||||
nand_release(&data->mtd);
|
||||
out:
|
||||
if (pdata->ctrl.remove)
|
||||
pdata->ctrl.remove(pdev);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
iounmap(data->io_base);
|
||||
kfree(data);
|
||||
@@ -111,15 +124,15 @@ out:
|
||||
static int __devexit plat_nand_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct plat_nand_data *data = platform_get_drvdata(pdev);
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
struct platform_nand_data *pdata = pdev->dev.platform_data;
|
||||
#endif
|
||||
|
||||
nand_release(&data->mtd);
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
if (data->parts && data->parts != pdata->chip.partitions)
|
||||
kfree(data->parts);
|
||||
#endif
|
||||
if (pdata->ctrl.remove)
|
||||
pdata->ctrl.remove(pdev);
|
||||
iounmap(data->io_base);
|
||||
kfree(data);
|
||||
|
||||
@@ -128,7 +141,7 @@ static int __devexit plat_nand_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver plat_nand_driver = {
|
||||
.probe = plat_nand_probe,
|
||||
.remove = plat_nand_remove,
|
||||
.remove = __devexit_p(plat_nand_remove),
|
||||
.driver = {
|
||||
.name = "gen_nand",
|
||||
.owner = THIS_MODULE,
|
||||
|
@@ -74,6 +74,14 @@ static struct nand_ecclayout nand_hw_eccoob = {
|
||||
|
||||
struct s3c2410_nand_info;
|
||||
|
||||
/**
|
||||
* struct s3c2410_nand_mtd - driver MTD structure
|
||||
* @mtd: The MTD instance to pass to the MTD layer.
|
||||
* @chip: The NAND chip information.
|
||||
* @set: The platform information supplied for this set of NAND chips.
|
||||
* @info: Link back to the hardware information.
|
||||
* @scan_res: The result from calling nand_scan_ident().
|
||||
*/
|
||||
struct s3c2410_nand_mtd {
|
||||
struct mtd_info mtd;
|
||||
struct nand_chip chip;
|
||||
@@ -90,6 +98,21 @@ enum s3c_cpu_type {
|
||||
|
||||
/* overview of the s3c2410 nand state */
|
||||
|
||||
/**
|
||||
* struct s3c2410_nand_info - NAND controller state.
|
||||
* @mtds: An array of MTD instances on this controoler.
|
||||
* @platform: The platform data for this board.
|
||||
* @device: The platform device we bound to.
|
||||
* @area: The IO area resource that came from request_mem_region().
|
||||
* @clk: The clock resource for this controller.
|
||||
* @regs: The area mapped for the hardware registers described by @area.
|
||||
* @sel_reg: Pointer to the register controlling the NAND selection.
|
||||
* @sel_bit: The bit in @sel_reg to select the NAND chip.
|
||||
* @mtd_count: The number of MTDs created from this controller.
|
||||
* @save_sel: The contents of @sel_reg to be saved over suspend.
|
||||
* @clk_rate: The clock rate from @clk.
|
||||
* @cpu_type: The exact type of this controller.
|
||||
*/
|
||||
struct s3c2410_nand_info {
|
||||
/* mtd info */
|
||||
struct nand_hw_control controller;
|
||||
@@ -145,12 +168,19 @@ static inline int allow_clk_stop(struct s3c2410_nand_info *info)
|
||||
|
||||
#define NS_IN_KHZ 1000000
|
||||
|
||||
/**
|
||||
* s3c_nand_calc_rate - calculate timing data.
|
||||
* @wanted: The cycle time in nanoseconds.
|
||||
* @clk: The clock rate in kHz.
|
||||
* @max: The maximum divider value.
|
||||
*
|
||||
* Calculate the timing value from the given parameters.
|
||||
*/
|
||||
static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = (wanted * clk) / NS_IN_KHZ;
|
||||
result++;
|
||||
result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ);
|
||||
|
||||
pr_debug("result %d from %ld, %d\n", result, clk, wanted);
|
||||
|
||||
@@ -169,13 +199,21 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
|
||||
|
||||
/* controller setup */
|
||||
|
||||
/**
|
||||
* s3c2410_nand_setrate - setup controller timing information.
|
||||
* @info: The controller instance.
|
||||
*
|
||||
* Given the information supplied by the platform, calculate and set
|
||||
* the necessary timing registers in the hardware to generate the
|
||||
* necessary timing cycles to the hardware.
|
||||
*/
|
||||
static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
|
||||
{
|
||||
struct s3c2410_platform_nand *plat = info->platform;
|
||||
int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
|
||||
int tacls, twrph0, twrph1;
|
||||
unsigned long clkrate = clk_get_rate(info->clk);
|
||||
unsigned long set, cfg, mask;
|
||||
unsigned long uninitialized_var(set), cfg, uninitialized_var(mask);
|
||||
unsigned long flags;
|
||||
|
||||
/* calculate the timing information for the controller */
|
||||
@@ -215,9 +253,9 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
|
||||
|
||||
case TYPE_S3C2440:
|
||||
case TYPE_S3C2412:
|
||||
mask = (S3C2410_NFCONF_TACLS(tacls_max - 1) |
|
||||
S3C2410_NFCONF_TWRPH0(7) |
|
||||
S3C2410_NFCONF_TWRPH1(7));
|
||||
mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) |
|
||||
S3C2440_NFCONF_TWRPH0(7) |
|
||||
S3C2440_NFCONF_TWRPH1(7));
|
||||
|
||||
set = S3C2440_NFCONF_TACLS(tacls - 1);
|
||||
set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
|
||||
@@ -225,14 +263,9 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
|
||||
break;
|
||||
|
||||
default:
|
||||
/* keep compiler happy */
|
||||
mask = 0;
|
||||
set = 0;
|
||||
BUG();
|
||||
}
|
||||
|
||||
dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
cfg = readl(info->regs + S3C2410_NFCONF);
|
||||
@@ -242,9 +275,18 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* s3c2410_nand_inithw - basic hardware initialisation
|
||||
* @info: The hardware state.
|
||||
*
|
||||
* Do the basic initialisation of the hardware, using s3c2410_nand_setrate()
|
||||
* to setup the hardware access speeds and set the controller to be enabled.
|
||||
*/
|
||||
static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
|
||||
{
|
||||
int ret;
|
||||
@@ -268,8 +310,19 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* select chip */
|
||||
|
||||
/**
|
||||
* s3c2410_nand_select_chip - select the given nand chip
|
||||
* @mtd: The MTD instance for this chip.
|
||||
* @chip: The chip number.
|
||||
*
|
||||
* This is called by the MTD layer to either select a given chip for the
|
||||
* @mtd instance, or to indicate that the access has finished and the
|
||||
* chip can be de-selected.
|
||||
*
|
||||
* The routine ensures that the nFCE line is correctly setup, and any
|
||||
* platform specific selection code is called to route nFCE to the specific
|
||||
* chip.
|
||||
*/
|
||||
static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
struct s3c2410_nand_info *info;
|
||||
@@ -530,7 +583,16 @@ static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
{
|
||||
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
readsl(info->regs + S3C2440_NFDATA, buf, len / 4);
|
||||
|
||||
readsl(info->regs + S3C2440_NFDATA, buf, len >> 2);
|
||||
|
||||
/* cleanup if we've got less than a word to do */
|
||||
if (len & 3) {
|
||||
buf += len & ~3;
|
||||
|
||||
for (; len & 3; len--)
|
||||
*buf++ = readb(info->regs + S3C2440_NFDATA);
|
||||
}
|
||||
}
|
||||
|
||||
static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
@@ -542,7 +604,16 @@ static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int
|
||||
static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
{
|
||||
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
writesl(info->regs + S3C2440_NFDATA, buf, len / 4);
|
||||
|
||||
writesl(info->regs + S3C2440_NFDATA, buf, len >> 2);
|
||||
|
||||
/* cleanup any fractional write */
|
||||
if (len & 3) {
|
||||
buf += len & ~3;
|
||||
|
||||
for (; len & 3; len--, buf++)
|
||||
writeb(*buf, info->regs + S3C2440_NFDATA);
|
||||
}
|
||||
}
|
||||
|
||||
/* cpufreq driver support */
|
||||
@@ -593,7 +664,7 @@ static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *inf
|
||||
|
||||
/* device management functions */
|
||||
|
||||
static int s3c2410_nand_remove(struct platform_device *pdev)
|
||||
static int s3c24xx_nand_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct s3c2410_nand_info *info = to_nand_info(pdev);
|
||||
|
||||
@@ -645,17 +716,31 @@ static int s3c2410_nand_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
const char *part_probes[] = { "cmdlinepart", NULL };
|
||||
static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
|
||||
struct s3c2410_nand_mtd *mtd,
|
||||
struct s3c2410_nand_set *set)
|
||||
{
|
||||
struct mtd_partition *part_info;
|
||||
int nr_part = 0;
|
||||
|
||||
if (set == NULL)
|
||||
return add_mtd_device(&mtd->mtd);
|
||||
|
||||
if (set->nr_partitions > 0 && set->partitions != NULL) {
|
||||
return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);
|
||||
if (set->nr_partitions == 0) {
|
||||
mtd->mtd.name = set->name;
|
||||
nr_part = parse_mtd_partitions(&mtd->mtd, part_probes,
|
||||
&part_info, 0);
|
||||
} else {
|
||||
if (set->nr_partitions > 0 && set->partitions != NULL) {
|
||||
nr_part = set->nr_partitions;
|
||||
part_info = set->partitions;
|
||||
}
|
||||
}
|
||||
|
||||
if (nr_part > 0 && part_info)
|
||||
return add_mtd_partitions(&mtd->mtd, part_info, nr_part);
|
||||
|
||||
return add_mtd_device(&mtd->mtd);
|
||||
}
|
||||
#else
|
||||
@@ -667,11 +752,16 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
|
||||
}
|
||||
#endif
|
||||
|
||||
/* s3c2410_nand_init_chip
|
||||
/**
|
||||
* s3c2410_nand_init_chip - initialise a single instance of an chip
|
||||
* @info: The base NAND controller the chip is on.
|
||||
* @nmtd: The new controller MTD instance to fill in.
|
||||
* @set: The information passed from the board specific platform data.
|
||||
*
|
||||
* init a single instance of an chip
|
||||
*/
|
||||
|
||||
* Initialise the given @nmtd from the information in @info and @set. This
|
||||
* readies the structure for use with the MTD layer functions by ensuring
|
||||
* all pointers are setup and the necessary control routines selected.
|
||||
*/
|
||||
static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
||||
struct s3c2410_nand_mtd *nmtd,
|
||||
struct s3c2410_nand_set *set)
|
||||
@@ -757,14 +847,40 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
||||
|
||||
if (set->disable_ecc)
|
||||
chip->ecc.mode = NAND_ECC_NONE;
|
||||
|
||||
switch (chip->ecc.mode) {
|
||||
case NAND_ECC_NONE:
|
||||
dev_info(info->device, "NAND ECC disabled\n");
|
||||
break;
|
||||
case NAND_ECC_SOFT:
|
||||
dev_info(info->device, "NAND soft ECC\n");
|
||||
break;
|
||||
case NAND_ECC_HW:
|
||||
dev_info(info->device, "NAND hardware ECC\n");
|
||||
break;
|
||||
default:
|
||||
dev_info(info->device, "NAND ECC UNKNOWN\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* If you use u-boot BBT creation code, specifying this flag will
|
||||
* let the kernel fish out the BBT from the NAND, and also skip the
|
||||
* full NAND scan that can take 1/2s or so. Little things... */
|
||||
if (set->flash_bbt)
|
||||
chip->options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN;
|
||||
}
|
||||
|
||||
/* s3c2410_nand_update_chip
|
||||
/**
|
||||
* s3c2410_nand_update_chip - post probe update
|
||||
* @info: The controller instance.
|
||||
* @nmtd: The driver version of the MTD instance.
|
||||
*
|
||||
* post-probe chip update, to change any items, such as the
|
||||
* layout for large page nand
|
||||
*/
|
||||
|
||||
* This routine is called after the chip probe has succesfully completed
|
||||
* and the relevant per-chip information updated. This call ensure that
|
||||
* we update the internal state accordingly.
|
||||
*
|
||||
* The internal state is currently limited to the ECC state information.
|
||||
*/
|
||||
static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
|
||||
struct s3c2410_nand_mtd *nmtd)
|
||||
{
|
||||
@@ -773,33 +889,33 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
|
||||
dev_dbg(info->device, "chip %p => page shift %d\n",
|
||||
chip, chip->page_shift);
|
||||
|
||||
if (hardware_ecc) {
|
||||
if (chip->ecc.mode != NAND_ECC_HW)
|
||||
return;
|
||||
|
||||
/* change the behaviour depending on wether we are using
|
||||
* the large or small page nand device */
|
||||
|
||||
if (chip->page_shift > 10) {
|
||||
chip->ecc.size = 256;
|
||||
chip->ecc.bytes = 3;
|
||||
} else {
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.layout = &nand_hw_eccoob;
|
||||
}
|
||||
if (chip->page_shift > 10) {
|
||||
chip->ecc.size = 256;
|
||||
chip->ecc.bytes = 3;
|
||||
} else {
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.layout = &nand_hw_eccoob;
|
||||
}
|
||||
}
|
||||
|
||||
/* s3c2410_nand_probe
|
||||
/* s3c24xx_nand_probe
|
||||
*
|
||||
* called by device layer when it finds a device matching
|
||||
* one our driver can handled. This code checks to see if
|
||||
* it can allocate all necessary resources then calls the
|
||||
* nand layer to look for devices
|
||||
*/
|
||||
|
||||
static int s3c24xx_nand_probe(struct platform_device *pdev,
|
||||
enum s3c_cpu_type cpu_type)
|
||||
static int s3c24xx_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
|
||||
enum s3c_cpu_type cpu_type;
|
||||
struct s3c2410_nand_info *info;
|
||||
struct s3c2410_nand_mtd *nmtd;
|
||||
struct s3c2410_nand_set *sets;
|
||||
@@ -809,6 +925,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
|
||||
int nr_sets;
|
||||
int setno;
|
||||
|
||||
cpu_type = platform_get_device_id(pdev)->driver_data;
|
||||
|
||||
pr_debug("s3c2410_nand_probe(%p)\n", pdev);
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
@@ -922,7 +1040,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
|
||||
return 0;
|
||||
|
||||
exit_error:
|
||||
s3c2410_nand_remove(pdev);
|
||||
s3c24xx_nand_remove(pdev);
|
||||
|
||||
if (err == 0)
|
||||
err = -EINVAL;
|
||||
@@ -983,50 +1101,33 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
|
||||
|
||||
/* driver device registration */
|
||||
|
||||
static int s3c2410_nand_probe(struct platform_device *dev)
|
||||
{
|
||||
return s3c24xx_nand_probe(dev, TYPE_S3C2410);
|
||||
}
|
||||
|
||||
static int s3c2440_nand_probe(struct platform_device *dev)
|
||||
{
|
||||
return s3c24xx_nand_probe(dev, TYPE_S3C2440);
|
||||
}
|
||||
|
||||
static int s3c2412_nand_probe(struct platform_device *dev)
|
||||
{
|
||||
return s3c24xx_nand_probe(dev, TYPE_S3C2412);
|
||||
}
|
||||
|
||||
static struct platform_driver s3c2410_nand_driver = {
|
||||
.probe = s3c2410_nand_probe,
|
||||
.remove = s3c2410_nand_remove,
|
||||
.suspend = s3c24xx_nand_suspend,
|
||||
.resume = s3c24xx_nand_resume,
|
||||
.driver = {
|
||||
.name = "s3c2410-nand",
|
||||
.owner = THIS_MODULE,
|
||||
static struct platform_device_id s3c24xx_driver_ids[] = {
|
||||
{
|
||||
.name = "s3c2410-nand",
|
||||
.driver_data = TYPE_S3C2410,
|
||||
}, {
|
||||
.name = "s3c2440-nand",
|
||||
.driver_data = TYPE_S3C2440,
|
||||
}, {
|
||||
.name = "s3c2412-nand",
|
||||
.driver_data = TYPE_S3C2412,
|
||||
}, {
|
||||
.name = "s3c6400-nand",
|
||||
.driver_data = TYPE_S3C2412, /* compatible with 2412 */
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver s3c2440_nand_driver = {
|
||||
.probe = s3c2440_nand_probe,
|
||||
.remove = s3c2410_nand_remove,
|
||||
.suspend = s3c24xx_nand_suspend,
|
||||
.resume = s3c24xx_nand_resume,
|
||||
.driver = {
|
||||
.name = "s3c2440-nand",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
|
||||
|
||||
static struct platform_driver s3c2412_nand_driver = {
|
||||
.probe = s3c2412_nand_probe,
|
||||
.remove = s3c2410_nand_remove,
|
||||
static struct platform_driver s3c24xx_nand_driver = {
|
||||
.probe = s3c24xx_nand_probe,
|
||||
.remove = s3c24xx_nand_remove,
|
||||
.suspend = s3c24xx_nand_suspend,
|
||||
.resume = s3c24xx_nand_resume,
|
||||
.id_table = s3c24xx_driver_ids,
|
||||
.driver = {
|
||||
.name = "s3c2412-nand",
|
||||
.name = "s3c24xx-nand",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
@@ -1035,16 +1136,12 @@ static int __init s3c2410_nand_init(void)
|
||||
{
|
||||
printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
|
||||
|
||||
platform_driver_register(&s3c2412_nand_driver);
|
||||
platform_driver_register(&s3c2440_nand_driver);
|
||||
return platform_driver_register(&s3c2410_nand_driver);
|
||||
return platform_driver_register(&s3c24xx_nand_driver);
|
||||
}
|
||||
|
||||
static void __exit s3c2410_nand_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&s3c2412_nand_driver);
|
||||
platform_driver_unregister(&s3c2440_nand_driver);
|
||||
platform_driver_unregister(&s3c2410_nand_driver);
|
||||
platform_driver_unregister(&s3c24xx_nand_driver);
|
||||
}
|
||||
|
||||
module_init(s3c2410_nand_init);
|
||||
@@ -1053,6 +1150,3 @@ module_exit(s3c2410_nand_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||
MODULE_DESCRIPTION("S3C24XX MTD NAND driver");
|
||||
MODULE_ALIAS("platform:s3c2410-nand");
|
||||
MODULE_ALIAS("platform:s3c2412-nand");
|
||||
MODULE_ALIAS("platform:s3c2440-nand");
|
||||
|
@@ -64,7 +64,7 @@ struct txx9ndfmc_priv {
|
||||
struct nand_chip chip;
|
||||
struct mtd_info mtd;
|
||||
int cs;
|
||||
char mtdname[BUS_ID_SIZE + 2];
|
||||
const char *mtdname;
|
||||
};
|
||||
|
||||
#define MAX_TXX9NDFMC_DEV 4
|
||||
@@ -334,16 +334,23 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
|
||||
|
||||
if (plat->ch_mask != 1) {
|
||||
txx9_priv->cs = i;
|
||||
sprintf(txx9_priv->mtdname, "%s.%u",
|
||||
dev_name(&dev->dev), i);
|
||||
txx9_priv->mtdname = kasprintf(GFP_KERNEL, "%s.%u",
|
||||
dev_name(&dev->dev), i);
|
||||
} else {
|
||||
txx9_priv->cs = -1;
|
||||
strcpy(txx9_priv->mtdname, dev_name(&dev->dev));
|
||||
txx9_priv->mtdname = kstrdup(dev_name(&dev->dev),
|
||||
GFP_KERNEL);
|
||||
}
|
||||
if (!txx9_priv->mtdname) {
|
||||
kfree(txx9_priv);
|
||||
dev_err(&dev->dev, "Unable to allocate MTD name.\n");
|
||||
continue;
|
||||
}
|
||||
if (plat->wide_mask & (1 << i))
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
if (nand_scan(mtd, 1)) {
|
||||
kfree(txx9_priv->mtdname);
|
||||
kfree(txx9_priv);
|
||||
continue;
|
||||
}
|
||||
@@ -385,6 +392,7 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev)
|
||||
kfree(drvdata->parts[i]);
|
||||
#endif
|
||||
del_mtd_device(mtd);
|
||||
kfree(txx9_priv->mtdname);
|
||||
kfree(txx9_priv);
|
||||
}
|
||||
return 0;
|
||||
|
Reference in New Issue
Block a user