|
@@ -11,6 +11,7 @@
|
|
|
#include <linux/platform_device.h>
|
|
|
#include <linux/dma-mapping.h>
|
|
|
#include <linux/delay.h>
|
|
|
+#include <linux/interrupt.h>
|
|
|
#include <linux/jiffies.h>
|
|
|
#include <linux/sched.h>
|
|
|
#include <linux/mtd/mtd.h>
|
|
@@ -24,6 +25,7 @@
|
|
|
#include <plat/nand.h>
|
|
|
|
|
|
#define DRIVER_NAME "omap2-nand"
|
|
|
+#define OMAP_NAND_TIMEOUT_MS 5000
|
|
|
|
|
|
#define NAND_Ecc_P1e (1 << 0)
|
|
|
#define NAND_Ecc_P2e (1 << 1)
|
|
@@ -96,26 +98,19 @@
|
|
|
static const char *part_probes[] = { "cmdlinepart", NULL };
|
|
|
#endif
|
|
|
|
|
|
-#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH
|
|
|
-static int use_prefetch = 1;
|
|
|
-
|
|
|
-/* "modprobe ... use_prefetch=0" etc */
|
|
|
-module_param(use_prefetch, bool, 0);
|
|
|
-MODULE_PARM_DESC(use_prefetch, "enable/disable use of PREFETCH");
|
|
|
-
|
|
|
-#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA
|
|
|
-static int use_dma = 1;
|
|
|
+/* oob info generated runtime depending on ecc algorithm and layout selected */
|
|
|
+static struct nand_ecclayout omap_oobinfo;
|
|
|
+/* Define some generic bad / good block scan pattern which are used
|
|
|
+ * while scanning a device for factory marked good / bad blocks
|
|
|
+ */
|
|
|
+static uint8_t scan_ff_pattern[] = { 0xff };
|
|
|
+static struct nand_bbt_descr bb_descrip_flashbased = {
|
|
|
+ .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
|
|
|
+ .offs = 0,
|
|
|
+ .len = 1,
|
|
|
+ .pattern = scan_ff_pattern,
|
|
|
+};
|
|
|
|
|
|
-/* "modprobe ... use_dma=0" etc */
|
|
|
-module_param(use_dma, bool, 0);
|
|
|
-MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
|
|
|
-#else
|
|
|
-static const int use_dma;
|
|
|
-#endif
|
|
|
-#else
|
|
|
-const int use_prefetch;
|
|
|
-static const int use_dma;
|
|
|
-#endif
|
|
|
|
|
|
struct omap_nand_info {
|
|
|
struct nand_hw_control controller;
|
|
@@ -129,6 +124,13 @@ struct omap_nand_info {
|
|
|
unsigned long phys_base;
|
|
|
struct completion comp;
|
|
|
int dma_ch;
|
|
|
+ int gpmc_irq;
|
|
|
+ enum {
|
|
|
+ OMAP_NAND_IO_READ = 0, /* read */
|
|
|
+ OMAP_NAND_IO_WRITE, /* write */
|
|
|
+ } iomode;
|
|
|
+ u_char *buf;
|
|
|
+ int buf_len;
|
|
|
};
|
|
|
|
|
|
/**
|
|
@@ -256,7 +258,8 @@ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len)
|
|
|
}
|
|
|
|
|
|
/* configure and start prefetch transfer */
|
|
|
- ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x0);
|
|
|
+ ret = gpmc_prefetch_enable(info->gpmc_cs,
|
|
|
+ PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0);
|
|
|
if (ret) {
|
|
|
/* PFPW engine is busy, use cpu copy method */
|
|
|
if (info->nand.options & NAND_BUSWIDTH_16)
|
|
@@ -288,9 +291,10 @@ static void omap_write_buf_pref(struct mtd_info *mtd,
|
|
|
{
|
|
|
struct omap_nand_info *info = container_of(mtd,
|
|
|
struct omap_nand_info, mtd);
|
|
|
- uint32_t pref_count = 0, w_count = 0;
|
|
|
+ uint32_t w_count = 0;
|
|
|
int i = 0, ret = 0;
|
|
|
u16 *p;
|
|
|
+ unsigned long tim, limit;
|
|
|
|
|
|
/* take care of subpage writes */
|
|
|
if (len % 2 != 0) {
|
|
@@ -300,7 +304,8 @@ static void omap_write_buf_pref(struct mtd_info *mtd,
|
|
|
}
|
|
|
|
|
|
/* configure and start prefetch transfer */
|
|
|
- ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x1);
|
|
|
+ ret = gpmc_prefetch_enable(info->gpmc_cs,
|
|
|
+ PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1);
|
|
|
if (ret) {
|
|
|
/* PFPW engine is busy, use cpu copy method */
|
|
|
if (info->nand.options & NAND_BUSWIDTH_16)
|
|
@@ -316,15 +321,17 @@ static void omap_write_buf_pref(struct mtd_info *mtd,
|
|
|
iowrite16(*p++, info->nand.IO_ADDR_W);
|
|
|
}
|
|
|
/* wait for data to flushed-out before reset the prefetch */
|
|
|
- do {
|
|
|
- pref_count = gpmc_read_status(GPMC_PREFETCH_COUNT);
|
|
|
- } while (pref_count);
|
|
|
+ tim = 0;
|
|
|
+ limit = (loops_per_jiffy *
|
|
|
+ msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
|
|
|
+ while (gpmc_read_status(GPMC_PREFETCH_COUNT) && (tim++ < limit))
|
|
|
+ cpu_relax();
|
|
|
+
|
|
|
/* disable and stop the PFPW engine */
|
|
|
gpmc_prefetch_reset(info->gpmc_cs);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA
|
|
|
/*
|
|
|
* omap_nand_dma_cb: callback on the completion of dma transfer
|
|
|
* @lch: logical channel
|
|
@@ -348,14 +355,15 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
|
|
|
{
|
|
|
struct omap_nand_info *info = container_of(mtd,
|
|
|
struct omap_nand_info, mtd);
|
|
|
- uint32_t prefetch_status = 0;
|
|
|
enum dma_data_direction dir = is_write ? DMA_TO_DEVICE :
|
|
|
DMA_FROM_DEVICE;
|
|
|
dma_addr_t dma_addr;
|
|
|
int ret;
|
|
|
+ unsigned long tim, limit;
|
|
|
|
|
|
- /* The fifo depth is 64 bytes. We have a sync at each frame and frame
|
|
|
- * length is 64 bytes.
|
|
|
+ /* The fifo depth is 64 bytes max.
|
|
|
+ * But configure the FIFO-threahold to 32 to get a sync at each frame
|
|
|
+ * and frame length is 32 bytes.
|
|
|
*/
|
|
|
int buf_len = len >> 6;
|
|
|
|
|
@@ -396,9 +404,10 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
|
|
|
OMAP24XX_DMA_GPMC, OMAP_DMA_SRC_SYNC);
|
|
|
}
|
|
|
/* configure and start prefetch transfer */
|
|
|
- ret = gpmc_prefetch_enable(info->gpmc_cs, 0x1, len, is_write);
|
|
|
+ ret = gpmc_prefetch_enable(info->gpmc_cs,
|
|
|
+ PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write);
|
|
|
if (ret)
|
|
|
- /* PFPW engine is busy, use cpu copy methode */
|
|
|
+ /* PFPW engine is busy, use cpu copy method */
|
|
|
goto out_copy;
|
|
|
|
|
|
init_completion(&info->comp);
|
|
@@ -407,10 +416,11 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
|
|
|
|
|
|
/* setup and start DMA using dma_addr */
|
|
|
wait_for_completion(&info->comp);
|
|
|
+ tim = 0;
|
|
|
+ limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
|
|
|
+ while (gpmc_read_status(GPMC_PREFETCH_COUNT) && (tim++ < limit))
|
|
|
+ cpu_relax();
|
|
|
|
|
|
- do {
|
|
|
- prefetch_status = gpmc_read_status(GPMC_PREFETCH_COUNT);
|
|
|
- } while (prefetch_status);
|
|
|
/* disable and stop the PFPW engine */
|
|
|
gpmc_prefetch_reset(info->gpmc_cs);
|
|
|
|
|
@@ -426,14 +436,6 @@ out_copy:
|
|
|
: omap_write_buf8(mtd, (u_char *) addr, len);
|
|
|
return 0;
|
|
|
}
|
|
|
-#else
|
|
|
-static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) {}
|
|
|
-static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
|
|
|
- unsigned int len, int is_write)
|
|
|
-{
|
|
|
- return 0;
|
|
|
-}
|
|
|
-#endif
|
|
|
|
|
|
/**
|
|
|
* omap_read_buf_dma_pref - read data from NAND controller into buffer
|
|
@@ -466,6 +468,157 @@ static void omap_write_buf_dma_pref(struct mtd_info *mtd,
|
|
|
omap_nand_dma_transfer(mtd, (u_char *) buf, len, 0x1);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * omap_nand_irq - GMPC irq handler
|
|
|
+ * @this_irq: gpmc irq number
|
|
|
+ * @dev: omap_nand_info structure pointer is passed here
|
|
|
+ */
|
|
|
+static irqreturn_t omap_nand_irq(int this_irq, void *dev)
|
|
|
+{
|
|
|
+ struct omap_nand_info *info = (struct omap_nand_info *) dev;
|
|
|
+ u32 bytes;
|
|
|
+ u32 irq_stat;
|
|
|
+
|
|
|
+ irq_stat = gpmc_read_status(GPMC_GET_IRQ_STATUS);
|
|
|
+ bytes = gpmc_read_status(GPMC_PREFETCH_FIFO_CNT);
|
|
|
+ bytes = bytes & 0xFFFC; /* io in multiple of 4 bytes */
|
|
|
+ if (info->iomode == OMAP_NAND_IO_WRITE) { /* checks for write io */
|
|
|
+ if (irq_stat & 0x2)
|
|
|
+ goto done;
|
|
|
+
|
|
|
+ if (info->buf_len && (info->buf_len < bytes))
|
|
|
+ bytes = info->buf_len;
|
|
|
+ else if (!info->buf_len)
|
|
|
+ bytes = 0;
|
|
|
+ iowrite32_rep(info->nand.IO_ADDR_W,
|
|
|
+ (u32 *)info->buf, bytes >> 2);
|
|
|
+ info->buf = info->buf + bytes;
|
|
|
+ info->buf_len -= bytes;
|
|
|
+
|
|
|
+ } else {
|
|
|
+ ioread32_rep(info->nand.IO_ADDR_R,
|
|
|
+ (u32 *)info->buf, bytes >> 2);
|
|
|
+ info->buf = info->buf + bytes;
|
|
|
+
|
|
|
+ if (irq_stat & 0x2)
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ gpmc_cs_configure(info->gpmc_cs, GPMC_SET_IRQ_STATUS, irq_stat);
|
|
|
+
|
|
|
+ return IRQ_HANDLED;
|
|
|
+
|
|
|
+done:
|
|
|
+ complete(&info->comp);
|
|
|
+ /* disable irq */
|
|
|
+ gpmc_cs_configure(info->gpmc_cs, GPMC_ENABLE_IRQ, 0);
|
|
|
+
|
|
|
+ /* clear status */
|
|
|
+ gpmc_cs_configure(info->gpmc_cs, GPMC_SET_IRQ_STATUS, irq_stat);
|
|
|
+
|
|
|
+ return IRQ_HANDLED;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * omap_read_buf_irq_pref - read data from NAND controller into buffer
|
|
|
+ * @mtd: MTD device structure
|
|
|
+ * @buf: buffer to store date
|
|
|
+ * @len: number of bytes to read
|
|
|
+ */
|
|
|
+static void omap_read_buf_irq_pref(struct mtd_info *mtd, u_char *buf, int len)
|
|
|
+{
|
|
|
+ struct omap_nand_info *info = container_of(mtd,
|
|
|
+ struct omap_nand_info, mtd);
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (len <= mtd->oobsize) {
|
|
|
+ omap_read_buf_pref(mtd, buf, len);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ info->iomode = OMAP_NAND_IO_READ;
|
|
|
+ info->buf = buf;
|
|
|
+ init_completion(&info->comp);
|
|
|
+
|
|
|
+ /* configure and start prefetch transfer */
|
|
|
+ ret = gpmc_prefetch_enable(info->gpmc_cs,
|
|
|
+ PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0);
|
|
|
+ if (ret)
|
|
|
+ /* PFPW engine is busy, use cpu copy method */
|
|
|
+ goto out_copy;
|
|
|
+
|
|
|
+ info->buf_len = len;
|
|
|
+ /* enable irq */
|
|
|
+ gpmc_cs_configure(info->gpmc_cs, GPMC_ENABLE_IRQ,
|
|
|
+ (GPMC_IRQ_FIFOEVENTENABLE | GPMC_IRQ_COUNT_EVENT));
|
|
|
+
|
|
|
+ /* waiting for read to complete */
|
|
|
+ wait_for_completion(&info->comp);
|
|
|
+
|
|
|
+ /* disable and stop the PFPW engine */
|
|
|
+ gpmc_prefetch_reset(info->gpmc_cs);
|
|
|
+ return;
|
|
|
+
|
|
|
+out_copy:
|
|
|
+ if (info->nand.options & NAND_BUSWIDTH_16)
|
|
|
+ omap_read_buf16(mtd, buf, len);
|
|
|
+ else
|
|
|
+ omap_read_buf8(mtd, buf, len);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * omap_write_buf_irq_pref - write buffer to NAND controller
|
|
|
+ * @mtd: MTD device structure
|
|
|
+ * @buf: data buffer
|
|
|
+ * @len: number of bytes to write
|
|
|
+ */
|
|
|
+static void omap_write_buf_irq_pref(struct mtd_info *mtd,
|
|
|
+ const u_char *buf, int len)
|
|
|
+{
|
|
|
+ struct omap_nand_info *info = container_of(mtd,
|
|
|
+ struct omap_nand_info, mtd);
|
|
|
+ int ret = 0;
|
|
|
+ unsigned long tim, limit;
|
|
|
+
|
|
|
+ if (len <= mtd->oobsize) {
|
|
|
+ omap_write_buf_pref(mtd, buf, len);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ info->iomode = OMAP_NAND_IO_WRITE;
|
|
|
+ info->buf = (u_char *) buf;
|
|
|
+ init_completion(&info->comp);
|
|
|
+
|
|
|
+ /* configure and start prefetch transfer : size=24 */
|
|
|
+ ret = gpmc_prefetch_enable(info->gpmc_cs,
|
|
|
+ (PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1);
|
|
|
+ if (ret)
|
|
|
+ /* PFPW engine is busy, use cpu copy method */
|
|
|
+ goto out_copy;
|
|
|
+
|
|
|
+ info->buf_len = len;
|
|
|
+ /* enable irq */
|
|
|
+ gpmc_cs_configure(info->gpmc_cs, GPMC_ENABLE_IRQ,
|
|
|
+ (GPMC_IRQ_FIFOEVENTENABLE | GPMC_IRQ_COUNT_EVENT));
|
|
|
+
|
|
|
+ /* waiting for write to complete */
|
|
|
+ wait_for_completion(&info->comp);
|
|
|
+ /* wait for data to flushed-out before reset the prefetch */
|
|
|
+ tim = 0;
|
|
|
+ limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
|
|
|
+ while (gpmc_read_status(GPMC_PREFETCH_COUNT) && (tim++ < limit))
|
|
|
+ cpu_relax();
|
|
|
+
|
|
|
+ /* disable and stop the PFPW engine */
|
|
|
+ gpmc_prefetch_reset(info->gpmc_cs);
|
|
|
+ return;
|
|
|
+
|
|
|
+out_copy:
|
|
|
+ if (info->nand.options & NAND_BUSWIDTH_16)
|
|
|
+ omap_write_buf16(mtd, buf, len);
|
|
|
+ else
|
|
|
+ omap_write_buf8(mtd, buf, len);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* omap_verify_buf - Verify chip data against buffer
|
|
|
* @mtd: MTD device structure
|
|
@@ -487,8 +640,6 @@ static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-#ifdef CONFIG_MTD_NAND_OMAP_HWECC
|
|
|
-
|
|
|
/**
|
|
|
* gen_true_ecc - This function will generate true ECC value
|
|
|
* @ecc_buf: buffer to store ecc code
|
|
@@ -708,8 +859,6 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
|
|
|
gpmc_enable_hwecc(info->gpmc_cs, mode, dev_width, info->nand.ecc.size);
|
|
|
}
|
|
|
|
|
|
-#endif
|
|
|
-
|
|
|
/**
|
|
|
* omap_wait - wait until the command is done
|
|
|
* @mtd: MTD device structure
|
|
@@ -779,6 +928,7 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
|
|
|
struct omap_nand_info *info;
|
|
|
struct omap_nand_platform_data *pdata;
|
|
|
int err;
|
|
|
+ int i, offset;
|
|
|
|
|
|
pdata = pdev->dev.platform_data;
|
|
|
if (pdata == NULL) {
|
|
@@ -804,7 +954,7 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
|
|
|
info->mtd.name = dev_name(&pdev->dev);
|
|
|
info->mtd.owner = THIS_MODULE;
|
|
|
|
|
|
- info->nand.options |= pdata->devsize ? NAND_BUSWIDTH_16 : 0;
|
|
|
+ info->nand.options = pdata->devsize;
|
|
|
info->nand.options |= NAND_SKIP_BBTSCAN;
|
|
|
|
|
|
/* NAND write protect off */
|
|
@@ -842,28 +992,13 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
|
|
|
info->nand.chip_delay = 50;
|
|
|
}
|
|
|
|
|
|
- if (use_prefetch) {
|
|
|
-
|
|
|
+ switch (pdata->xfer_type) {
|
|
|
+ case NAND_OMAP_PREFETCH_POLLED:
|
|
|
info->nand.read_buf = omap_read_buf_pref;
|
|
|
info->nand.write_buf = omap_write_buf_pref;
|
|
|
- if (use_dma) {
|
|
|
- err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND",
|
|
|
- omap_nand_dma_cb, &info->comp, &info->dma_ch);
|
|
|
- if (err < 0) {
|
|
|
- info->dma_ch = -1;
|
|
|
- printk(KERN_WARNING "DMA request failed."
|
|
|
- " Non-dma data transfer mode\n");
|
|
|
- } else {
|
|
|
- omap_set_dma_dest_burst_mode(info->dma_ch,
|
|
|
- OMAP_DMA_DATA_BURST_16);
|
|
|
- omap_set_dma_src_burst_mode(info->dma_ch,
|
|
|
- OMAP_DMA_DATA_BURST_16);
|
|
|
-
|
|
|
- info->nand.read_buf = omap_read_buf_dma_pref;
|
|
|
- info->nand.write_buf = omap_write_buf_dma_pref;
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
+ break;
|
|
|
+
|
|
|
+ case NAND_OMAP_POLLED:
|
|
|
if (info->nand.options & NAND_BUSWIDTH_16) {
|
|
|
info->nand.read_buf = omap_read_buf16;
|
|
|
info->nand.write_buf = omap_write_buf16;
|
|
@@ -871,20 +1006,61 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
|
|
|
info->nand.read_buf = omap_read_buf8;
|
|
|
info->nand.write_buf = omap_write_buf8;
|
|
|
}
|
|
|
+ break;
|
|
|
+
|
|
|
+ case NAND_OMAP_PREFETCH_DMA:
|
|
|
+ err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND",
|
|
|
+ omap_nand_dma_cb, &info->comp, &info->dma_ch);
|
|
|
+ if (err < 0) {
|
|
|
+ info->dma_ch = -1;
|
|
|
+ dev_err(&pdev->dev, "DMA request failed!\n");
|
|
|
+ goto out_release_mem_region;
|
|
|
+ } else {
|
|
|
+ omap_set_dma_dest_burst_mode(info->dma_ch,
|
|
|
+ OMAP_DMA_DATA_BURST_16);
|
|
|
+ omap_set_dma_src_burst_mode(info->dma_ch,
|
|
|
+ OMAP_DMA_DATA_BURST_16);
|
|
|
+
|
|
|
+ info->nand.read_buf = omap_read_buf_dma_pref;
|
|
|
+ info->nand.write_buf = omap_write_buf_dma_pref;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case NAND_OMAP_PREFETCH_IRQ:
|
|
|
+ err = request_irq(pdata->gpmc_irq,
|
|
|
+ omap_nand_irq, IRQF_SHARED, "gpmc-nand", info);
|
|
|
+ if (err) {
|
|
|
+ dev_err(&pdev->dev, "requesting irq(%d) error:%d",
|
|
|
+ pdata->gpmc_irq, err);
|
|
|
+ goto out_release_mem_region;
|
|
|
+ } else {
|
|
|
+ info->gpmc_irq = pdata->gpmc_irq;
|
|
|
+ info->nand.read_buf = omap_read_buf_irq_pref;
|
|
|
+ info->nand.write_buf = omap_write_buf_irq_pref;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ dev_err(&pdev->dev,
|
|
|
+ "xfer_type(%d) not supported!\n", pdata->xfer_type);
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out_release_mem_region;
|
|
|
}
|
|
|
- info->nand.verify_buf = omap_verify_buf;
|
|
|
|
|
|
-#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;
|
|
|
+ info->nand.verify_buf = omap_verify_buf;
|
|
|
|
|
|
-#else
|
|
|
- info->nand.ecc.mode = NAND_ECC_SOFT;
|
|
|
-#endif
|
|
|
+ /* selsect the ecc type */
|
|
|
+ if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_DEFAULT)
|
|
|
+ info->nand.ecc.mode = NAND_ECC_SOFT;
|
|
|
+ else if ((pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW) ||
|
|
|
+ (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE)) {
|
|
|
+ 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;
|
|
|
+ }
|
|
|
|
|
|
/* DIP switches on some boards change between 8 and 16 bit
|
|
|
* bus widths for flash. Try the other width if the first try fails.
|
|
@@ -897,6 +1073,26 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /* rom code layout */
|
|
|
+ if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE) {
|
|
|
+
|
|
|
+ if (info->nand.options & NAND_BUSWIDTH_16)
|
|
|
+ offset = 2;
|
|
|
+ else {
|
|
|
+ offset = 1;
|
|
|
+ info->nand.badblock_pattern = &bb_descrip_flashbased;
|
|
|
+ }
|
|
|
+ omap_oobinfo.eccbytes = 3 * (info->mtd.oobsize/16);
|
|
|
+ for (i = 0; i < omap_oobinfo.eccbytes; i++)
|
|
|
+ omap_oobinfo.eccpos[i] = i+offset;
|
|
|
+
|
|
|
+ omap_oobinfo.oobfree->offset = offset + omap_oobinfo.eccbytes;
|
|
|
+ omap_oobinfo.oobfree->length = info->mtd.oobsize -
|
|
|
+ (offset + omap_oobinfo.eccbytes);
|
|
|
+
|
|
|
+ info->nand.ecc.layout = &omap_oobinfo;
|
|
|
+ }
|
|
|
+
|
|
|
#ifdef CONFIG_MTD_PARTITIONS
|
|
|
err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
|
|
|
if (err > 0)
|
|
@@ -926,9 +1122,12 @@ static int omap_nand_remove(struct platform_device *pdev)
|
|
|
mtd);
|
|
|
|
|
|
platform_set_drvdata(pdev, NULL);
|
|
|
- if (use_dma)
|
|
|
+ if (info->dma_ch != -1)
|
|
|
omap_free_dma(info->dma_ch);
|
|
|
|
|
|
+ if (info->gpmc_irq)
|
|
|
+ free_irq(info->gpmc_irq, info);
|
|
|
+
|
|
|
/* Release NAND device, its internal structures and partitions */
|
|
|
nand_release(&info->mtd);
|
|
|
iounmap(info->nand.IO_ADDR_R);
|
|
@@ -947,16 +1146,8 @@ static struct platform_driver omap_nand_driver = {
|
|
|
|
|
|
static int __init omap_nand_init(void)
|
|
|
{
|
|
|
- printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME);
|
|
|
+ pr_info("%s driver initializing\n", DRIVER_NAME);
|
|
|
|
|
|
- /* This check is required if driver is being
|
|
|
- * loaded run time as a module
|
|
|
- */
|
|
|
- if ((1 == use_dma) && (0 == use_prefetch)) {
|
|
|
- printk(KERN_INFO"Wrong parameters: 'use_dma' can not be 1 "
|
|
|
- "without use_prefetch'. Prefetch will not be"
|
|
|
- " used in either mode (mpu or dma)\n");
|
|
|
- }
|
|
|
return platform_driver_register(&omap_nand_driver);
|
|
|
}
|
|
|
|