diff options
Diffstat (limited to 'drivers/mtd/nand/omap2.c')
-rw-r--r-- | drivers/mtd/nand/omap2.c | 448 |
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), }, }; |