summaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/omap2.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/omap2.c')
-rw-r--r--drivers/mtd/nand/omap2.c448
1 files changed, 298 insertions, 150 deletions
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index 0749ca1a1456..08e158895635 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -12,6 +12,7 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
@@ -28,6 +29,7 @@
#include <linux/mtd/nand_bch.h>
#include <linux/platform_data/elm.h>
+#include <linux/omap-gpmc.h>
#include <linux/platform_data/mtd-nand-omap2.h>
#define DRIVER_NAME "omap2-nand"
@@ -151,13 +153,17 @@ static struct nand_hw_control omap_gpmc_controller = {
};
struct omap_nand_info {
- struct omap_nand_platform_data *pdata;
struct nand_chip nand;
struct platform_device *pdev;
int gpmc_cs;
- unsigned long phys_base;
+ bool dev_ready;
+ enum nand_io xfer_type;
+ int devsize;
enum omap_ecc ecc_opt;
+ struct device_node *elm_of_node;
+
+ unsigned long phys_base;
struct completion comp;
struct dma_chan *dma;
int gpmc_irq_fifo;
@@ -168,12 +174,14 @@ struct omap_nand_info {
} iomode;
u_char *buf;
int buf_len;
+ /* Interface to GPMC */
struct gpmc_nand_regs reg;
- /* generated at runtime depending on ECC algorithm and layout selected */
- struct nand_ecclayout oobinfo;
+ struct gpmc_nand_ops *ops;
+ bool flash_bbt;
/* fields specific for BCHx_HW ECC scheme */
struct device *elm_dev;
- struct device_node *of_node;
+ /* NAND ready gpio */
+ struct gpio_desc *ready_gpiod;
};
static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
@@ -208,7 +216,7 @@ static int omap_prefetch_enable(int cs, int fifo_th, int dma_mode,
*/
val = ((cs << PREFETCH_CONFIG1_CS_SHIFT) |
PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH |
- (dma_mode << DMA_MPU_MODE_SHIFT) | (0x1 & is_write));
+ (dma_mode << DMA_MPU_MODE_SHIFT) | (is_write & 0x1));
writel(val, info->reg.gpmc_prefetch_config1);
/* Start the prefetch engine */
@@ -288,14 +296,13 @@ static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len)
{
struct omap_nand_info *info = mtd_to_omap(mtd);
u_char *p = (u_char *)buf;
- u32 status = 0;
+ bool status;
while (len--) {
iowrite8(*p++, info->nand.IO_ADDR_W);
/* wait until buffer is available for write */
do {
- status = readl(info->reg.gpmc_status) &
- STATUS_BUFF_EMPTY;
+ status = info->ops->nand_writebuffer_empty();
} while (!status);
}
}
@@ -323,7 +330,7 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
{
struct omap_nand_info *info = mtd_to_omap(mtd);
u16 *p = (u16 *) buf;
- u32 status = 0;
+ bool status;
/* FIXME try bursts of writesw() or DMA ... */
len >>= 1;
@@ -331,8 +338,7 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
iowrite16(*p++, info->nand.IO_ADDR_W);
/* wait until buffer is available for write */
do {
- status = readl(info->reg.gpmc_status) &
- STATUS_BUFF_EMPTY;
+ status = info->ops->nand_writebuffer_empty();
} while (!status);
}
}
@@ -467,17 +473,8 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
int ret;
u32 val;
- if (addr >= high_memory) {
- struct page *p1;
-
- if (((size_t)addr & PAGE_MASK) !=
- ((size_t)(addr + len - 1) & PAGE_MASK))
- goto out_copy;
- p1 = vmalloc_to_page(addr);
- if (!p1)
- goto out_copy;
- addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK);
- }
+ if (!virt_addr_valid(addr))
+ goto out_copy;
sg_init_one(&sg, addr, len);
n = dma_map_sg(info->dma->device->dev, &sg, 1, dir);
@@ -497,6 +494,11 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
tx->callback_param = &info->comp;
dmaengine_submit(tx);
+ init_completion(&info->comp);
+
+ /* setup and start DMA using dma_addr */
+ dma_async_issue_pending(info->dma);
+
/* configure and start prefetch transfer */
ret = omap_prefetch_enable(info->gpmc_cs,
PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write, info);
@@ -504,10 +506,6 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
/* PFPW engine is busy, use cpu copy method */
goto out_copy_unmap;
- init_completion(&info->comp);
- dma_async_issue_pending(info->dma);
-
- /* 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));
@@ -1017,21 +1015,16 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
}
/**
- * omap_dev_ready - calls the platform specific dev_ready function
+ * omap_dev_ready - checks the NAND Ready GPIO line
* @mtd: MTD device structure
+ *
+ * Returns true if ready and false if busy.
*/
static int omap_dev_ready(struct mtd_info *mtd)
{
- unsigned int val = 0;
struct omap_nand_info *info = mtd_to_omap(mtd);
- val = readl(info->reg.gpmc_status);
-
- if ((val & 0x100) == 0x100) {
- return 1;
- } else {
- return 0;
- }
+ return gpiod_get_value(info->ready_gpiod);
}
/**
@@ -1495,9 +1488,8 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required, int page)
{
- int i;
+ int ret;
uint8_t *ecc_calc = chip->buffers->ecccalc;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
/* Enable GPMC ecc engine */
chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
@@ -1508,8 +1500,10 @@ static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
/* Update ecc vector from GPMC result registers */
chip->ecc.calculate(mtd, buf, &ecc_calc[0]);
- for (i = 0; i < chip->ecc.total; i++)
- chip->oob_poi[eccpos[i]] = ecc_calc[i];
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
/* Write ecc vector to OOB area */
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -1536,10 +1530,7 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
{
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint8_t *ecc_code = chip->buffers->ecccode;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
- uint8_t *oob = &chip->oob_poi[eccpos[0]];
- uint32_t oob_pos = mtd->writesize + chip->ecc.layout->eccpos[0];
- int stat;
+ int stat, ret;
unsigned int max_bitflips = 0;
/* Enable GPMC ecc engine */
@@ -1549,13 +1540,18 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
chip->read_buf(mtd, buf, mtd->writesize);
/* Read oob bytes */
- chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, -1);
- chip->read_buf(mtd, oob, chip->ecc.total);
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+ mtd->writesize + BADBLOCK_MARKER_LENGTH, -1);
+ chip->read_buf(mtd, chip->oob_poi + BADBLOCK_MARKER_LENGTH,
+ chip->ecc.total);
/* Calculate ecc bytes */
chip->ecc.calculate(mtd, buf, ecc_calc);
- memcpy(ecc_code, &chip->oob_poi[eccpos[0]], chip->ecc.total);
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
stat = chip->ecc.correct(mtd, buf, ecc_code, ecc_calc);
@@ -1630,7 +1626,7 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info,
"CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
return false;
}
- if (ecc_needs_elm && !is_elm_present(info, pdata->elm_of_node)) {
+ if (ecc_needs_elm && !is_elm_present(info, info->elm_of_node)) {
dev_err(&info->pdev->dev, "ELM not available\n");
return false;
}
@@ -1638,43 +1634,227 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info,
return true;
}
+static const char * const nand_xfer_types[] = {
+ [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled",
+ [NAND_OMAP_POLLED] = "polled",
+ [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma",
+ [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq",
+};
+
+static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info)
+{
+ struct device_node *child = dev->of_node;
+ int i;
+ const char *s;
+ u32 cs;
+
+ if (of_property_read_u32(child, "reg", &cs) < 0) {
+ dev_err(dev, "reg not found in DT\n");
+ return -EINVAL;
+ }
+
+ info->gpmc_cs = cs;
+
+ /* detect availability of ELM module. Won't be present pre-OMAP4 */
+ info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
+ if (!info->elm_of_node)
+ dev_dbg(dev, "ti,elm-id not in DT\n");
+
+ /* select ecc-scheme for NAND */
+ if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
+ dev_err(dev, "ti,nand-ecc-opt not found\n");
+ return -EINVAL;
+ }
+
+ if (!strcmp(s, "sw")) {
+ info->ecc_opt = OMAP_ECC_HAM1_CODE_SW;
+ } else if (!strcmp(s, "ham1") ||
+ !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) {
+ info->ecc_opt = OMAP_ECC_HAM1_CODE_HW;
+ } else if (!strcmp(s, "bch4")) {
+ if (info->elm_of_node)
+ info->ecc_opt = OMAP_ECC_BCH4_CODE_HW;
+ else
+ info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW;
+ } else if (!strcmp(s, "bch8")) {
+ if (info->elm_of_node)
+ info->ecc_opt = OMAP_ECC_BCH8_CODE_HW;
+ else
+ info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
+ } else if (!strcmp(s, "bch16")) {
+ info->ecc_opt = OMAP_ECC_BCH16_CODE_HW;
+ } else {
+ dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n");
+ return -EINVAL;
+ }
+
+ /* select data transfer mode */
+ if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) {
+ for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) {
+ if (!strcasecmp(s, nand_xfer_types[i])) {
+ info->xfer_type = i;
+ return 0;
+ }
+ }
+
+ dev_err(dev, "unrecognized value for ti,nand-xfer-type\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int omap_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ struct nand_chip *chip = &info->nand;
+ int off = BADBLOCK_MARKER_LENGTH;
+
+ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
+ !(chip->options & NAND_BUSWIDTH_16))
+ off = 1;
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = off;
+ oobregion->length = chip->ecc.total;
+
+ return 0;
+}
+
+static int omap_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ struct nand_chip *chip = &info->nand;
+ int off = BADBLOCK_MARKER_LENGTH;
+
+ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
+ !(chip->options & NAND_BUSWIDTH_16))
+ off = 1;
+
+ if (section)
+ return -ERANGE;
+
+ off += chip->ecc.total;
+ if (off >= mtd->oobsize)
+ return -ERANGE;
+
+ oobregion->offset = off;
+ oobregion->length = mtd->oobsize - off;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops omap_ooblayout_ops = {
+ .ecc = omap_ooblayout_ecc,
+ .free = omap_ooblayout_free,
+};
+
+static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int off = BADBLOCK_MARKER_LENGTH;
+
+ if (section >= chip->ecc.steps)
+ return -ERANGE;
+
+ /*
+ * When SW correction is employed, one OMAP specific marker byte is
+ * reserved after each ECC step.
+ */
+ oobregion->offset = off + (section * (chip->ecc.bytes + 1));
+ oobregion->length = chip->ecc.bytes;
+
+ return 0;
+}
+
+static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int off = BADBLOCK_MARKER_LENGTH;
+
+ if (section)
+ return -ERANGE;
+
+ /*
+ * When SW correction is employed, one OMAP specific marker byte is
+ * reserved after each ECC step.
+ */
+ off += ((chip->ecc.bytes + 1) * chip->ecc.steps);
+ if (off >= mtd->oobsize)
+ return -ERANGE;
+
+ oobregion->offset = off;
+ oobregion->length = mtd->oobsize - off;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = {
+ .ecc = omap_sw_ooblayout_ecc,
+ .free = omap_sw_ooblayout_free,
+};
+
static int omap_nand_probe(struct platform_device *pdev)
{
struct omap_nand_info *info;
- struct omap_nand_platform_data *pdata;
+ struct omap_nand_platform_data *pdata = NULL;
struct mtd_info *mtd;
struct nand_chip *nand_chip;
- struct nand_ecclayout *ecclayout;
int err;
- int i;
dma_cap_mask_t mask;
unsigned sig;
- unsigned oob_index;
struct resource *res;
-
- pdata = dev_get_platdata(&pdev->dev);
- if (pdata == NULL) {
- dev_err(&pdev->dev, "platform data missing\n");
- return -ENODEV;
- }
+ struct device *dev = &pdev->dev;
+ int min_oobbytes = BADBLOCK_MARKER_LENGTH;
+ int oobbytes_per_step;
info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
GFP_KERNEL);
if (!info)
return -ENOMEM;
+ info->pdev = pdev;
+
+ if (dev->of_node) {
+ if (omap_get_dt_info(dev, info))
+ return -EINVAL;
+ } else {
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "platform data missing\n");
+ return -EINVAL;
+ }
+
+ info->gpmc_cs = pdata->cs;
+ info->reg = pdata->reg;
+ info->ecc_opt = pdata->ecc_opt;
+ if (pdata->dev_ready)
+ dev_info(&pdev->dev, "pdata->dev_ready is deprecated\n");
+
+ info->xfer_type = pdata->xfer_type;
+ info->devsize = pdata->devsize;
+ info->elm_of_node = pdata->elm_of_node;
+ info->flash_bbt = pdata->flash_bbt;
+ }
+
platform_set_drvdata(pdev, info);
+ info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs);
+ if (!info->ops) {
+ dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n");
+ return -ENODEV;
+ }
- info->pdev = pdev;
- info->gpmc_cs = pdata->cs;
- info->reg = pdata->reg;
- info->of_node = pdata->of_node;
- info->ecc_opt = pdata->ecc_opt;
nand_chip = &info->nand;
mtd = nand_to_mtd(nand_chip);
mtd->dev.parent = &pdev->dev;
nand_chip->ecc.priv = NULL;
- nand_set_flash_node(nand_chip, pdata->of_node);
+ nand_set_flash_node(nand_chip, dev->of_node);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
@@ -1688,6 +1868,13 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R;
nand_chip->cmd_ctrl = omap_hwcontrol;
+ info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb",
+ GPIOD_IN);
+ if (IS_ERR(info->ready_gpiod)) {
+ dev_err(dev, "failed to get ready gpio\n");
+ return PTR_ERR(info->ready_gpiod);
+ }
+
/*
* If RDY/BSY line is connected to OMAP then use the omap ready
* function and the generic nand_wait function which reads the status
@@ -1695,7 +1882,7 @@ static int omap_nand_probe(struct platform_device *pdev)
* 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) {
+ if (info->ready_gpiod) {
nand_chip->dev_ready = omap_dev_ready;
nand_chip->chip_delay = 0;
} else {
@@ -1703,21 +1890,25 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->chip_delay = 50;
}
- if (pdata->flash_bbt)
- nand_chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
- else
- nand_chip->options |= NAND_SKIP_BBTSCAN;
+ if (info->flash_bbt)
+ nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
/* scan NAND device connected to chip controller */
- nand_chip->options |= pdata->devsize & NAND_BUSWIDTH_16;
+ nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
if (nand_scan_ident(mtd, 1, NULL)) {
- dev_err(&info->pdev->dev, "scan failed, may be bus-width mismatch\n");
+ dev_err(&info->pdev->dev,
+ "scan failed, may be bus-width mismatch\n");
err = -ENXIO;
goto return_error;
}
+ if (nand_chip->bbt_options & NAND_BBT_USE_FLASH)
+ nand_chip->bbt_options |= NAND_BBT_NO_OOB;
+ else
+ nand_chip->options |= NAND_SKIP_BBTSCAN;
+
/* re-populate low-level callbacks based on xfer modes */
- switch (pdata->xfer_type) {
+ switch (info->xfer_type) {
case NAND_OMAP_PREFETCH_POLLED:
nand_chip->read_buf = omap_read_buf_pref;
nand_chip->write_buf = omap_write_buf_pref;
@@ -1797,7 +1988,7 @@ static int omap_nand_probe(struct platform_device *pdev)
default:
dev_err(&pdev->dev,
- "xfer_type(%d) not supported!\n", pdata->xfer_type);
+ "xfer_type(%d) not supported!\n", info->xfer_type);
err = -EINVAL;
goto return_error;
}
@@ -1809,16 +2000,15 @@ static int omap_nand_probe(struct platform_device *pdev)
/*
* Bail out earlier to let NAND_ECC_SOFT code create its own
- * ecclayout instead of using ours.
+ * ooblayout instead of using ours.
*/
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
nand_chip->ecc.mode = NAND_ECC_SOFT;
+ nand_chip->ecc.algo = NAND_ECC_HAMMING;
goto scan_tail;
}
/* populate MTD interface based on ECC scheme */
- ecclayout = &info->oobinfo;
- nand_chip->ecc.layout = ecclayout;
switch (info->ecc_opt) {
case OMAP_ECC_HAM1_CODE_HW:
pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
@@ -1829,19 +2019,12 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.calculate = omap_calculate_ecc;
nand_chip->ecc.hwctl = omap_enable_hwecc;
nand_chip->ecc.correct = omap_correct_data;
- /* define ECC layout */
- ecclayout->eccbytes = nand_chip->ecc.bytes *
- (mtd->writesize /
- nand_chip->ecc.size);
- if (nand_chip->options & NAND_BUSWIDTH_16)
- oob_index = BADBLOCK_MARKER_LENGTH;
- else
- oob_index = 1;
- for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
- ecclayout->eccpos[i] = oob_index;
- /* no reserved-marker in ecclayout for this ecc-scheme */
- ecclayout->oobfree->offset =
- ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
+ mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+ oobbytes_per_step = nand_chip->ecc.bytes;
+
+ if (!(nand_chip->options & NAND_BUSWIDTH_16))
+ min_oobbytes = 1;
+
break;
case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
@@ -1853,19 +2036,9 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = nand_bch_correct_data;
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
- /* define ECC layout */
- ecclayout->eccbytes = nand_chip->ecc.bytes *
- (mtd->writesize /
- nand_chip->ecc.size);
- oob_index = BADBLOCK_MARKER_LENGTH;
- for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) {
- ecclayout->eccpos[i] = oob_index;
- if (((i + 1) % nand_chip->ecc.bytes) == 0)
- oob_index++;
- }
- /* include reserved-marker in ecclayout->oobfree calculation */
- ecclayout->oobfree->offset = 1 +
- ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
+ mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
+ /* Reserve one byte for the OMAP marker */
+ oobbytes_per_step = nand_chip->ecc.bytes + 1;
/* software bch library is used for locating errors */
nand_chip->ecc.priv = nand_bch_init(mtd);
if (!nand_chip->ecc.priv) {
@@ -1887,16 +2060,8 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
nand_chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch;
- /* define ECC layout */
- ecclayout->eccbytes = nand_chip->ecc.bytes *
- (mtd->writesize /
- nand_chip->ecc.size);
- oob_index = BADBLOCK_MARKER_LENGTH;
- for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
- ecclayout->eccpos[i] = oob_index;
- /* reserved marker already included in ecclayout->eccbytes */
- ecclayout->oobfree->offset =
- ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
+ mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+ oobbytes_per_step = nand_chip->ecc.bytes;
err = elm_config(info->elm_dev, BCH4_ECC,
mtd->writesize / nand_chip->ecc.size,
@@ -1914,19 +2079,9 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = nand_bch_correct_data;
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
- /* define ECC layout */
- ecclayout->eccbytes = nand_chip->ecc.bytes *
- (mtd->writesize /
- nand_chip->ecc.size);
- oob_index = BADBLOCK_MARKER_LENGTH;
- for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) {
- ecclayout->eccpos[i] = oob_index;
- if (((i + 1) % nand_chip->ecc.bytes) == 0)
- oob_index++;
- }
- /* include reserved-marker in ecclayout->oobfree calculation */
- ecclayout->oobfree->offset = 1 +
- ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
+ mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
+ /* Reserve one byte for the OMAP marker */
+ oobbytes_per_step = nand_chip->ecc.bytes + 1;
/* software bch library is used for locating errors */
nand_chip->ecc.priv = nand_bch_init(mtd);
if (!nand_chip->ecc.priv) {
@@ -1948,6 +2103,8 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
nand_chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch;
+ mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+ oobbytes_per_step = nand_chip->ecc.bytes;
err = elm_config(info->elm_dev, BCH8_ECC,
mtd->writesize / nand_chip->ecc.size,
@@ -1955,16 +2112,6 @@ static int omap_nand_probe(struct platform_device *pdev)
if (err < 0)
goto return_error;
- /* define ECC layout */
- ecclayout->eccbytes = nand_chip->ecc.bytes *
- (mtd->writesize /
- nand_chip->ecc.size);
- oob_index = BADBLOCK_MARKER_LENGTH;
- for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
- ecclayout->eccpos[i] = oob_index;
- /* reserved marker already included in ecclayout->eccbytes */
- ecclayout->oobfree->offset =
- ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
break;
case OMAP_ECC_BCH16_CODE_HW:
@@ -1978,6 +2125,8 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
nand_chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch;
+ mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+ oobbytes_per_step = nand_chip->ecc.bytes;
err = elm_config(info->elm_dev, BCH16_ECC,
mtd->writesize / nand_chip->ecc.size,
@@ -1985,16 +2134,6 @@ static int omap_nand_probe(struct platform_device *pdev)
if (err < 0)
goto return_error;
- /* define ECC layout */
- ecclayout->eccbytes = nand_chip->ecc.bytes *
- (mtd->writesize /
- nand_chip->ecc.size);
- oob_index = BADBLOCK_MARKER_LENGTH;
- for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
- ecclayout->eccpos[i] = oob_index;
- /* reserved marker already included in ecclayout->eccbytes */
- ecclayout->oobfree->offset =
- ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
break;
default:
dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n");
@@ -2002,13 +2141,13 @@ static int omap_nand_probe(struct platform_device *pdev)
goto return_error;
}
- /* all OOB bytes from oobfree->offset till end off OOB are free */
- ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset;
/* check if NAND device's OOB is enough to store ECC signatures */
- if (mtd->oobsize < (ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH)) {
+ min_oobbytes += (oobbytes_per_step *
+ (mtd->writesize / nand_chip->ecc.size));
+ if (mtd->oobsize < min_oobbytes) {
dev_err(&info->pdev->dev,
"not enough OOB bytes required = %d, available=%d\n",
- ecclayout->eccbytes, mtd->oobsize);
+ min_oobbytes, mtd->oobsize);
err = -EINVAL;
goto return_error;
}
@@ -2020,7 +2159,10 @@ scan_tail:
goto return_error;
}
- mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
+ if (dev->of_node)
+ mtd_device_register(mtd, NULL, 0);
+ else
+ mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
platform_set_drvdata(pdev, mtd);
@@ -2051,11 +2193,17 @@ static int omap_nand_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id omap_nand_ids[] = {
+ { .compatible = "ti,omap2-nand", },
+ {},
+};
+
static struct platform_driver omap_nand_driver = {
.probe = omap_nand_probe,
.remove = omap_nand_remove,
.driver = {
.name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(omap_nand_ids),
},
};