diff options
Diffstat (limited to 'drivers/mtd/nand/raw/omap2.c')
| -rw-r--r-- | drivers/mtd/nand/raw/omap2.c | 904 |
1 files changed, 459 insertions, 445 deletions
diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index 68e8b9f7f372..39e297486721 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * 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> @@ -18,15 +15,15 @@ #include <linux/jiffies.h> #include <linux/sched.h> #include <linux/mtd/mtd.h> +#include <linux/mtd/nand-ecc-sw-bch.h> #include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/omap-dma.h> -#include <linux/io.h> +#include <linux/iopoll.h> #include <linux/slab.h> #include <linux/of.h> -#include <linux/of_device.h> +#include <linux/of_platform.h> -#include <linux/mtd/nand_bch.h> #include <linux/platform_data/elm.h> #include <linux/omap-gpmc.h> @@ -134,7 +131,7 @@ #define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */ #define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */ -#define BADBLOCK_MARKER_LENGTH 2 +#define BBM_LEN 2 static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55, 0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78, @@ -151,7 +148,6 @@ struct omap_nand_info { int gpmc_cs; bool dev_ready; enum nand_io xfer_type; - int devsize; enum omap_ecc ecc_opt; struct device_node *elm_of_node; @@ -167,6 +163,7 @@ struct omap_nand_info { u_char *buf; int buf_len; /* Interface to GPMC */ + void __iomem *fifo; struct gpmc_nand_regs reg; struct gpmc_nand_ops *ops; bool flash_bbt; @@ -174,6 +171,15 @@ struct omap_nand_info { struct device *elm_dev; /* NAND ready gpio */ struct gpio_desc *ready_gpiod; + unsigned int neccpg; + unsigned int nsteps_per_eccpg; + unsigned int eccpg_size; + unsigned int eccpg_bytes; + void (*data_in)(struct nand_chip *chip, void *buf, + unsigned int len, bool force_8bit); + void (*data_out)(struct nand_chip *chip, + const void *buf, unsigned int len, + bool force_8bit); }; static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) @@ -181,6 +187,13 @@ static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) return container_of(mtd_to_nand(mtd), struct omap_nand_info, nand); } +static void omap_nand_data_in(struct nand_chip *chip, void *buf, + unsigned int len, bool force_8bit); + +static void omap_nand_data_out(struct nand_chip *chip, + const void *buf, unsigned int len, + bool force_8bit); + /** * omap_prefetch_enable - configures and starts prefetch transfer * @cs: cs (chip select) number @@ -188,6 +201,7 @@ static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) * @dma_mode: dma mode enable (1) or disable (0) * @u32_count: number of bytes to be transferred * @is_write: prefetch read(0) or write post(1) mode + * @info: NAND device structure containing platform data */ static int omap_prefetch_enable(int cs, int fifo_th, int dma_mode, unsigned int u32_count, int is_write, struct omap_nand_info *info) @@ -217,7 +231,7 @@ static int omap_prefetch_enable(int cs, int fifo_th, int dma_mode, return 0; } -/** +/* * omap_prefetch_reset - disables and stops the prefetch engine */ static int omap_prefetch_reset(int cs, struct omap_nand_info *info) @@ -239,169 +253,78 @@ static int omap_prefetch_reset(int cs, struct omap_nand_info *info) } /** - * omap_hwcontrol - hardware specific access to control-lines - * @chip: NAND chip object - * @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!! + * omap_nand_data_in_pref - NAND data in using prefetch engine + * @chip: NAND chip + * @buf: output buffer where NAND data is placed into + * @len: length of transfer + * @force_8bit: force 8-bit transfers */ -static void omap_hwcontrol(struct nand_chip *chip, int cmd, unsigned int ctrl) +static void omap_nand_data_in_pref(struct nand_chip *chip, void *buf, + unsigned int len, bool force_8bit) { struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); - - if (cmd != NAND_CMD_NONE) { - if (ctrl & NAND_CLE) - writeb(cmd, info->reg.gpmc_nand_command); - - else if (ctrl & NAND_ALE) - writeb(cmd, info->reg.gpmc_nand_address); - - else /* NAND_NCE */ - writeb(cmd, info->reg.gpmc_nand_data); - } -} - -/** - * omap_read_buf8 - 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_buf8(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - - ioread8_rep(nand->legacy.IO_ADDR_R, buf, len); -} - -/** - * omap_write_buf8 - write buffer to NAND controller - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - */ -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; - bool status; - - while (len--) { - iowrite8(*p++, info->nand.legacy.IO_ADDR_W); - /* wait until buffer is available for write */ - do { - status = info->ops->nand_writebuffer_empty(); - } while (!status); - } -} - -/** - * 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_to_nand(mtd); - - ioread16_rep(nand->legacy.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 = mtd_to_omap(mtd); - u16 *p = (u16 *) buf; - bool status; - /* FIXME try bursts of writesw() or DMA ... */ - len >>= 1; - - while (len--) { - iowrite16(*p++, info->nand.legacy.IO_ADDR_W); - /* wait until buffer is available for write */ - do { - status = info->ops->nand_writebuffer_empty(); - } while (!status); - } -} - -/** - * omap_read_buf_pref - read data from NAND controller into buffer - * @chip: NAND chip object - * @buf: buffer to store date - * @len: number of bytes to read - */ -static void omap_read_buf_pref(struct nand_chip *chip, u_char *buf, int len) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct omap_nand_info *info = mtd_to_omap(mtd); uint32_t r_count = 0; int ret = 0; u32 *p = (u32 *)buf; + unsigned int pref_len; - /* take care of subpage reads */ - if (len % 4) { - if (info->nand.options & NAND_BUSWIDTH_16) - omap_read_buf16(mtd, buf, len % 4); - else - omap_read_buf8(mtd, buf, len % 4); - p = (u32 *) (buf + len % 4); - len -= len % 4; + if (force_8bit) { + omap_nand_data_in(chip, buf, len, force_8bit); + return; } + /* read 32-bit words using prefetch and remaining bytes normally */ + /* configure and start prefetch transfer */ + pref_len = len - (len & 3); ret = omap_prefetch_enable(info->gpmc_cs, - PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0, info); + PREFETCH_FIFOTHRESHOLD_MAX, 0x0, pref_len, 0x0, info); if (ret) { - /* PFPW engine is busy, use cpu copy method */ - if (info->nand.options & NAND_BUSWIDTH_16) - omap_read_buf16(mtd, (u_char *)p, len); - else - omap_read_buf8(mtd, (u_char *)p, len); + /* prefetch engine is busy, use CPU copy method */ + omap_nand_data_in(chip, buf, len, false); } else { do { r_count = readl(info->reg.gpmc_prefetch_status); r_count = PREFETCH_STATUS_FIFO_CNT(r_count); r_count = r_count >> 2; - ioread32_rep(info->nand.legacy.IO_ADDR_R, p, r_count); + ioread32_rep(info->fifo, p, r_count); p += r_count; - len -= r_count << 2; - } while (len); - /* disable and stop the PFPW engine */ + pref_len -= r_count << 2; + } while (pref_len); + /* disable and stop the Prefetch engine */ omap_prefetch_reset(info->gpmc_cs, info); + /* fetch any remaining bytes */ + if (len & 3) + omap_nand_data_in(chip, p, len & 3, false); } } /** - * omap_write_buf_pref - write buffer to NAND controller - * @chip: NAND chip object - * @buf: data buffer - * @len: number of bytes to write + * omap_nand_data_out_pref - NAND data out using Write Posting engine + * @chip: NAND chip + * @buf: input buffer that is sent to NAND + * @len: length of transfer + * @force_8bit: force 8-bit transfers */ -static void omap_write_buf_pref(struct nand_chip *chip, const u_char *buf, - int len) +static void omap_nand_data_out_pref(struct nand_chip *chip, + const void *buf, unsigned int len, + bool force_8bit) { - struct mtd_info *mtd = nand_to_mtd(chip); - struct omap_nand_info *info = mtd_to_omap(mtd); + struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); uint32_t w_count = 0; int i = 0, ret = 0; u16 *p = (u16 *)buf; unsigned long tim, limit; u32 val; + if (force_8bit) { + omap_nand_data_out(chip, buf, len, force_8bit); + return; + } + /* take care of subpage writes */ if (len % 2 != 0) { - writeb(*buf, info->nand.legacy.IO_ADDR_W); + writeb(*(u8 *)buf, info->fifo); p = (u16 *)(buf + 1); len--; } @@ -410,18 +333,15 @@ static void omap_write_buf_pref(struct nand_chip *chip, const u_char *buf, ret = omap_prefetch_enable(info->gpmc_cs, PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1, info); if (ret) { - /* PFPW engine is busy, use cpu copy method */ - if (info->nand.options & NAND_BUSWIDTH_16) - omap_write_buf16(mtd, (u_char *)p, len); - else - omap_write_buf8(mtd, (u_char *)p, len); + /* write posting engine is busy, use CPU copy method */ + omap_nand_data_out(chip, buf, len, false); } else { while (len) { w_count = readl(info->reg.gpmc_prefetch_status); w_count = PREFETCH_STATUS_FIFO_CNT(w_count); w_count = w_count >> 1; for (i = 0; (i < w_count) && len; i++, len -= 2) - iowrite16(*p++, info->nand.legacy.IO_ADDR_W); + iowrite16(*p++, info->fifo); } /* wait for data to flushed-out before reset the prefetch */ tim = 0; @@ -449,15 +369,16 @@ static void omap_nand_dma_callback(void *data) /* * omap_nand_dma_transfer: configure and start dma transfer - * @mtd: MTD device structure + * @chip: nand chip structure * @addr: virtual address in RAM of source/destination * @len: number of data bytes to be transferred * @is_write: flag for read/write operation */ -static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, - unsigned int len, int is_write) +static inline int omap_nand_dma_transfer(struct nand_chip *chip, + const void *addr, unsigned int len, + int is_write) { - struct omap_nand_info *info = mtd_to_omap(mtd); + struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); struct dma_async_tx_descriptor *tx; enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; @@ -519,49 +440,59 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, out_copy_unmap: dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); out_copy: - if (info->nand.options & NAND_BUSWIDTH_16) - is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len) - : omap_write_buf16(mtd, (u_char *) addr, len); - else - is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len) - : omap_write_buf8(mtd, (u_char *) addr, len); + is_write == 0 ? omap_nand_data_in(chip, (void *)addr, len, false) + : omap_nand_data_out(chip, addr, len, false); + return 0; } /** - * omap_read_buf_dma_pref - read data from NAND controller into buffer - * @chip: NAND chip object - * @buf: buffer to store date - * @len: number of bytes to read + * omap_nand_data_in_dma_pref - NAND data in using DMA and Prefetch + * @chip: NAND chip + * @buf: output buffer where NAND data is placed into + * @len: length of transfer + * @force_8bit: force 8-bit transfers */ -static void omap_read_buf_dma_pref(struct nand_chip *chip, u_char *buf, - int len) +static void omap_nand_data_in_dma_pref(struct nand_chip *chip, void *buf, + unsigned int len, bool force_8bit) { struct mtd_info *mtd = nand_to_mtd(chip); + if (force_8bit) { + omap_nand_data_in(chip, buf, len, force_8bit); + return; + } + if (len <= mtd->oobsize) - omap_read_buf_pref(chip, buf, len); + omap_nand_data_in_pref(chip, buf, len, false); else /* start transfer in DMA mode */ - omap_nand_dma_transfer(mtd, buf, len, 0x0); + omap_nand_dma_transfer(chip, buf, len, 0x0); } /** - * omap_write_buf_dma_pref - write buffer to NAND controller - * @chip: NAND chip object - * @buf: data buffer - * @len: number of bytes to write + * omap_nand_data_out_dma_pref - NAND data out using DMA and write posting + * @chip: NAND chip + * @buf: input buffer that is sent to NAND + * @len: length of transfer + * @force_8bit: force 8-bit transfers */ -static void omap_write_buf_dma_pref(struct nand_chip *chip, const u_char *buf, - int len) +static void omap_nand_data_out_dma_pref(struct nand_chip *chip, + const void *buf, unsigned int len, + bool force_8bit) { struct mtd_info *mtd = nand_to_mtd(chip); + if (force_8bit) { + omap_nand_data_out(chip, buf, len, force_8bit); + return; + } + if (len <= mtd->oobsize) - omap_write_buf_pref(chip, buf, len); + omap_nand_data_out_pref(chip, buf, len, false); else /* start transfer in DMA mode */ - omap_nand_dma_transfer(mtd, (u_char *)buf, len, 0x1); + omap_nand_dma_transfer(chip, buf, len, 0x1); } /* @@ -585,13 +516,13 @@ static irqreturn_t omap_nand_irq(int this_irq, void *dev) bytes = info->buf_len; else if (!info->buf_len) bytes = 0; - iowrite32_rep(info->nand.legacy.IO_ADDR_W, (u32 *)info->buf, + iowrite32_rep(info->fifo, (u32 *)info->buf, bytes >> 2); info->buf = info->buf + bytes; info->buf_len -= bytes; } else { - ioread32_rep(info->nand.legacy.IO_ADDR_R, (u32 *)info->buf, + ioread32_rep(info->fifo, (u32 *)info->buf, bytes >> 2); info->buf = info->buf + bytes; @@ -611,20 +542,17 @@ done: } /* - * omap_read_buf_irq_pref - read data from NAND controller into buffer - * @chip: NAND chip object - * @buf: buffer to store date - * @len: number of bytes to read + * omap_nand_data_in_irq_pref - NAND data in using Prefetch and IRQ */ -static void omap_read_buf_irq_pref(struct nand_chip *chip, u_char *buf, - int len) +static void omap_nand_data_in_irq_pref(struct nand_chip *chip, void *buf, + unsigned int len, bool force_8bit) { - struct mtd_info *mtd = nand_to_mtd(chip); - struct omap_nand_info *info = mtd_to_omap(mtd); + struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); + struct mtd_info *mtd = nand_to_mtd(&info->nand); int ret = 0; - if (len <= mtd->oobsize) { - omap_read_buf_pref(chip, buf, len); + if (len <= mtd->oobsize || force_8bit) { + omap_nand_data_in(chip, buf, len, force_8bit); return; } @@ -635,9 +563,11 @@ static void omap_read_buf_irq_pref(struct nand_chip *chip, u_char *buf, /* configure and start prefetch transfer */ ret = omap_prefetch_enable(info->gpmc_cs, PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0, info); - if (ret) + if (ret) { /* PFPW engine is busy, use cpu copy method */ - goto out_copy; + omap_nand_data_in(chip, buf, len, false); + return; + } info->buf_len = len; @@ -650,31 +580,23 @@ static void omap_read_buf_irq_pref(struct nand_chip *chip, u_char *buf, /* disable and stop the PFPW engine */ omap_prefetch_reset(info->gpmc_cs, info); 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 - * @chip: NAND chip object - * @buf: data buffer - * @len: number of bytes to write + * omap_nand_data_out_irq_pref - NAND out using write posting and IRQ */ -static void omap_write_buf_irq_pref(struct nand_chip *chip, const u_char *buf, - int len) +static void omap_nand_data_out_irq_pref(struct nand_chip *chip, + const void *buf, unsigned int len, + bool force_8bit) { - struct mtd_info *mtd = nand_to_mtd(chip); - struct omap_nand_info *info = mtd_to_omap(mtd); + struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); + struct mtd_info *mtd = nand_to_mtd(&info->nand); int ret = 0; unsigned long tim, limit; u32 val; - if (len <= mtd->oobsize) { - omap_write_buf_pref(chip, buf, len); + if (len <= mtd->oobsize || force_8bit) { + omap_nand_data_out(chip, buf, len, force_8bit); return; } @@ -685,9 +607,11 @@ static void omap_write_buf_irq_pref(struct nand_chip *chip, const u_char *buf, /* configure and start prefetch transfer : size=24 */ ret = omap_prefetch_enable(info->gpmc_cs, (PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1, info); - if (ret) + if (ret) { /* PFPW engine is busy, use cpu copy method */ - goto out_copy; + omap_nand_data_out(chip, buf, len, false); + return; + } info->buf_len = len; @@ -709,12 +633,6 @@ static void omap_write_buf_irq_pref(struct nand_chip *chip, const u_char *buf, /* disable and stop the PFPW engine */ omap_prefetch_reset(info->gpmc_cs, info); return; - -out_copy: - if (info->nand.options & NAND_BUSWIDTH_16) - omap_write_buf16(mtd, buf, len); - else - omap_write_buf8(mtd, buf, len); } /** @@ -887,8 +805,8 @@ static int omap_correct_data(struct nand_chip *chip, u_char *dat, int stat = 0; /* Ex NAND_ECC_HW12_2048 */ - if ((info->nand.ecc.mode == NAND_ECC_HW) && - (info->nand.ecc.size == 2048)) + if (info->nand.ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST && + info->nand.ecc.size == 2048) blockCnt = 4; else blockCnt = 1; @@ -909,7 +827,7 @@ static int omap_correct_data(struct nand_chip *chip, u_char *dat, } /** - * omap_calcuate_ecc - Generate non-inverted ECC bytes. + * omap_calculate_ecc - Generate non-inverted ECC bytes. * @chip: NAND chip object * @dat: The pointer to data on which ecc is computed * @ecc_code: The ecc_code buffer @@ -942,7 +860,7 @@ static int omap_calculate_ecc(struct nand_chip *chip, const u_char *dat, /** * omap_enable_hwecc - This function enables the hardware ecc functionality - * @mtd: MTD device structure + * @chip: NAND chip object * @mode: Read/Write mode */ static void omap_enable_hwecc(struct nand_chip *chip, int mode) @@ -980,55 +898,8 @@ static void omap_enable_hwecc(struct nand_chip *chip, int mode) } /** - * omap_wait - wait until the command is done - * @this: 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 nand_chip *this) -{ - struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(this)); - unsigned long timeo = jiffies; - int status, state = this->state; - - if (state == FL_ERASING) - timeo += msecs_to_jiffies(400); - else - timeo += msecs_to_jiffies(20); - - writeb(NAND_CMD_STATUS & 0xFF, info->reg.gpmc_nand_command); - while (time_before(jiffies, timeo)) { - status = readb(info->reg.gpmc_nand_data); - if (status & NAND_STATUS_READY) - break; - cond_resched(); - } - - status = readb(info->reg.gpmc_nand_data); - return status; -} - -/** - * 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 nand_chip *chip) -{ - struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); - - return gpiod_get_value(info->ready_gpiod); -} - -/** * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation - * @mtd: MTD device structure + * @chip: NAND chip object * @mode: Read/Write mode * * When using BCH with SW correction (i.e. no ELM), sector size is set @@ -1137,7 +1008,7 @@ static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2, * _omap_calculate_ecc_bch - Generate ECC bytes for one sector * @mtd: MTD device structure * @dat: The pointer to data on which ecc is computed - * @ecc_code: The ecc_code buffer + * @ecc_calc: The ecc_code buffer * @i: The sector number (for a multi sector page) * * Support calculating of BCH4/8/16 ECC vectors for one sector @@ -1265,7 +1136,7 @@ static int _omap_calculate_ecc_bch(struct mtd_info *mtd, * omap_calculate_ecc_bch_sw - ECC generator for sector for SW based correction * @chip: NAND chip object * @dat: The pointer to data on which ecc is computed - * @ecc_code: The ecc_code buffer + * @ecc_calc: Buffer storing the calculated ECC bytes * * Support calculating of BCH4/8/16 ECC vectors for one sector. This is used * when SW based correction is required as ECC is required for one sector @@ -1281,7 +1152,7 @@ static int omap_calculate_ecc_bch_sw(struct nand_chip *chip, * omap_calculate_ecc_bch_multi - Generate ECC for multiple sectors * @mtd: MTD device structure * @dat: The pointer to data on which ecc is computed - * @ecc_code: The ecc_code buffer + * @ecc_calc: Buffer storing the calculated ECC bytes * * Support calculating of BCH4/8/16 ecc vectors for the entire page in one go. */ @@ -1360,7 +1231,7 @@ static int omap_elm_correct_data(struct nand_chip *chip, u_char *data, { struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); struct nand_ecc_ctrl *ecc = &info->nand.ecc; - int eccsteps = info->nand.ecc.steps; + int eccsteps = info->nsteps_per_eccpg; int i , j, stat = 0; int eccflag, actual_eccbytes; struct elm_errorvec err_vec[ERROR_VECTOR_MAX]; @@ -1507,7 +1378,7 @@ static int omap_elm_correct_data(struct nand_chip *chip, u_char *data, } /* Update number of correctable errors */ - stat += err_vec[i].error_count; + stat = max_t(unsigned int, stat, err_vec[i].error_count); /* Update page data with sector size */ data += ecc->size; @@ -1530,27 +1401,40 @@ static int omap_write_page_bch(struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { struct mtd_info *mtd = nand_to_mtd(chip); - int ret; + struct omap_nand_info *info = mtd_to_omap(mtd); uint8_t *ecc_calc = chip->ecc.calc_buf; + unsigned int eccpg; + int ret; - nand_prog_page_begin_op(chip, page, 0, NULL, 0); + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (ret) + return ret; - /* Enable GPMC ecc engine */ - chip->ecc.hwctl(chip, NAND_ECC_WRITE); + for (eccpg = 0; eccpg < info->neccpg; eccpg++) { + /* Enable GPMC ecc engine */ + chip->ecc.hwctl(chip, NAND_ECC_WRITE); - /* Write data */ - chip->legacy.write_buf(chip, buf, mtd->writesize); + /* Write data */ + info->data_out(chip, buf + (eccpg * info->eccpg_size), + info->eccpg_size, false); - /* Update ecc vector from GPMC result registers */ - omap_calculate_ecc_bch_multi(mtd, buf, &ecc_calc[0]); + /* Update ecc vector from GPMC result registers */ + ret = omap_calculate_ecc_bch_multi(mtd, + buf + (eccpg * info->eccpg_size), + ecc_calc); + if (ret) + return ret; - ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, - chip->ecc.total); - if (ret) - return ret; + ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, + chip->oob_poi, + eccpg * info->eccpg_bytes, + info->eccpg_bytes); + if (ret) + return ret; + } /* Write ecc vector to OOB area */ - chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize); + info->data_out(chip, chip->oob_poi, mtd->oobsize, false); return nand_prog_page_end_op(chip); } @@ -1571,12 +1455,13 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset, int oob_required, int page) { struct mtd_info *mtd = nand_to_mtd(chip); + struct omap_nand_info *info = mtd_to_omap(mtd); u8 *ecc_calc = chip->ecc.calc_buf; int ecc_size = chip->ecc.size; int ecc_bytes = chip->ecc.bytes; - int ecc_steps = chip->ecc.steps; u32 start_step = offset / ecc_size; u32 end_step = (offset + data_len - 1) / ecc_size; + unsigned int eccpg; int step, ret = 0; /* @@ -1585,38 +1470,50 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset, * ECC is calculated for all subpages but we choose * only what we want. */ - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - /* Enable GPMC ECC engine */ - chip->ecc.hwctl(chip, NAND_ECC_WRITE); - - /* Write data */ - chip->legacy.write_buf(chip, buf, mtd->writesize); + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (ret) + return ret; - for (step = 0; step < ecc_steps; step++) { - /* mask ECC of un-touched subpages by padding 0xFF */ - if (step < start_step || step > end_step) - memset(ecc_calc, 0xff, ecc_bytes); - else - ret = _omap_calculate_ecc_bch(mtd, buf, ecc_calc, step); + for (eccpg = 0; eccpg < info->neccpg; eccpg++) { + /* Enable GPMC ECC engine */ + chip->ecc.hwctl(chip, NAND_ECC_WRITE); + + /* Write data */ + info->data_out(chip, buf + (eccpg * info->eccpg_size), + info->eccpg_size, false); + + for (step = 0; step < info->nsteps_per_eccpg; step++) { + unsigned int base_step = eccpg * info->nsteps_per_eccpg; + const u8 *bufoffs = buf + (eccpg * info->eccpg_size); + + /* Mask ECC of un-touched subpages with 0xFFs */ + if ((step + base_step) < start_step || + (step + base_step) > end_step) + memset(ecc_calc + (step * ecc_bytes), 0xff, + ecc_bytes); + else + ret = _omap_calculate_ecc_bch(mtd, + bufoffs + (step * ecc_size), + ecc_calc + (step * ecc_bytes), + step); + + if (ret) + return ret; + } + /* + * Copy the calculated ECC for the whole page including the + * masked values (0xFF) corresponding to unwritten subpages. + */ + ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, + eccpg * info->eccpg_bytes, + info->eccpg_bytes); if (ret) return ret; - - buf += ecc_size; - ecc_calc += ecc_bytes; } - /* copy calculated ECC for whole page to chip->buffer->oob */ - /* this include masked-value(0xFF) for unwritten subpages */ - ecc_calc = chip->ecc.calc_buf; - ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, - chip->ecc.total); - if (ret) - return ret; - /* write OOB buffer to NAND device */ - chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize); + info->data_out(chip, chip->oob_poi, mtd->oobsize, false); return nand_prog_page_end_op(chip); } @@ -1639,40 +1536,60 @@ static int omap_read_page_bch(struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { struct mtd_info *mtd = nand_to_mtd(chip); + struct omap_nand_info *info = mtd_to_omap(mtd); uint8_t *ecc_calc = chip->ecc.calc_buf; uint8_t *ecc_code = chip->ecc.code_buf; + unsigned int max_bitflips = 0, eccpg; int stat, ret; - unsigned int max_bitflips = 0; - - nand_read_page_op(chip, page, 0, NULL, 0); - /* Enable GPMC ecc engine */ - chip->ecc.hwctl(chip, NAND_ECC_READ); + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + return ret; - /* Read data */ - chip->legacy.read_buf(chip, buf, mtd->writesize); + for (eccpg = 0; eccpg < info->neccpg; eccpg++) { + /* Enable GPMC ecc engine */ + chip->ecc.hwctl(chip, NAND_ECC_READ); - /* Read oob bytes */ - nand_change_read_column_op(chip, - mtd->writesize + BADBLOCK_MARKER_LENGTH, - chip->oob_poi + BADBLOCK_MARKER_LENGTH, - chip->ecc.total, false); + /* Read data */ + ret = nand_change_read_column_op(chip, eccpg * info->eccpg_size, + buf + (eccpg * info->eccpg_size), + info->eccpg_size, false); + if (ret) + return ret; - /* Calculate ecc bytes */ - omap_calculate_ecc_bch_multi(mtd, buf, ecc_calc); + /* Read oob bytes */ + ret = nand_change_read_column_op(chip, + mtd->writesize + BBM_LEN + + (eccpg * info->eccpg_bytes), + chip->oob_poi + BBM_LEN + + (eccpg * info->eccpg_bytes), + info->eccpg_bytes, false); + if (ret) + return ret; - ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, - chip->ecc.total); - if (ret) - return ret; + /* Calculate ecc bytes */ + ret = omap_calculate_ecc_bch_multi(mtd, + buf + (eccpg * info->eccpg_size), + ecc_calc); + if (ret) + return ret; - stat = chip->ecc.correct(chip, buf, ecc_code, ecc_calc); + ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, + chip->oob_poi, + eccpg * info->eccpg_bytes, + info->eccpg_bytes); + if (ret) + return ret; - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); + stat = chip->ecc.correct(chip, + buf + (eccpg * info->eccpg_size), + ecc_code, ecc_calc); + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } return max_bitflips; @@ -1680,7 +1597,8 @@ static int omap_read_page_bch(struct nand_chip *chip, uint8_t *buf, /** * is_elm_present - checks for presence of ELM module by scanning DT nodes - * @omap_nand_info: NAND device structure containing platform data + * @info: NAND device structure containing platform data + * @elm_node: ELM's DT node */ static bool is_elm_present(struct omap_nand_info *info, struct device_node *elm_node) @@ -1728,9 +1646,9 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info) break; } - if (ecc_needs_bch && !IS_ENABLED(CONFIG_MTD_NAND_ECC_BCH)) { + if (ecc_needs_bch && !IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH)) { dev_err(&info->pdev->dev, - "CONFIG_MTD_NAND_ECC_BCH not enabled\n"); + "CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n"); return false; } if (ecc_needs_omap_bch && !IS_ENABLED(CONFIG_MTD_NAND_OMAP_BCH)) { @@ -1824,7 +1742,7 @@ static int omap_ooblayout_ecc(struct mtd_info *mtd, int section, { struct omap_nand_info *info = mtd_to_omap(mtd); struct nand_chip *chip = &info->nand; - int off = BADBLOCK_MARKER_LENGTH; + int off = BBM_LEN; if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW && !(chip->options & NAND_BUSWIDTH_16)) @@ -1844,7 +1762,7 @@ static int omap_ooblayout_free(struct mtd_info *mtd, int section, { struct omap_nand_info *info = mtd_to_omap(mtd); struct nand_chip *chip = &info->nand; - int off = BADBLOCK_MARKER_LENGTH; + int off = BBM_LEN; if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW && !(chip->options & NAND_BUSWIDTH_16)) @@ -1871,18 +1789,20 @@ static const struct mtd_ooblayout_ops omap_ooblayout_ops = { 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; + struct nand_device *nand = mtd_to_nanddev(mtd); + unsigned int nsteps = nanddev_get_ecc_nsteps(nand); + unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand); + int off = BBM_LEN; - if (section >= chip->ecc.steps) + if (section >= nsteps) 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; + oobregion->offset = off + (section * (ecc_bytes + 1)); + oobregion->length = ecc_bytes; return 0; } @@ -1890,8 +1810,10 @@ static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section, 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; + struct nand_device *nand = mtd_to_nanddev(mtd); + unsigned int nsteps = nanddev_get_ecc_nsteps(nand); + unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand); + int off = BBM_LEN; if (section) return -ERANGE; @@ -1900,7 +1822,7 @@ static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section, * When SW correction is employed, one OMAP specific marker byte is * reserved after each ECC step. */ - off += ((chip->ecc.bytes + 1) * chip->ecc.steps); + off += ((ecc_bytes + 1) * nsteps); if (off >= mtd->oobsize) return -ERANGE; @@ -1920,7 +1842,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip) struct mtd_info *mtd = nand_to_mtd(chip); struct omap_nand_info *info = mtd_to_omap(mtd); struct device *dev = &info->pdev->dev; - int min_oobbytes = BADBLOCK_MARKER_LENGTH; + int min_oobbytes = BBM_LEN; + int elm_bch_strength = -1; int oobbytes_per_step; dma_cap_mask_t mask; int err; @@ -1933,8 +1856,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip) /* Re-populate low-level callbacks based on xfer modes */ switch (info->xfer_type) { case NAND_OMAP_PREFETCH_POLLED: - chip->legacy.read_buf = omap_read_buf_pref; - chip->legacy.write_buf = omap_write_buf_pref; + info->data_in = omap_nand_data_in_pref; + info->data_out = omap_nand_data_out_pref; break; case NAND_OMAP_POLLED: @@ -1966,17 +1889,16 @@ static int omap_nand_attach_chip(struct nand_chip *chip) err); return err; } - chip->legacy.read_buf = omap_read_buf_dma_pref; - chip->legacy.write_buf = omap_write_buf_dma_pref; + + info->data_in = omap_nand_data_in_dma_pref; + info->data_out = omap_nand_data_out_dma_pref; } break; case NAND_OMAP_PREFETCH_IRQ: info->gpmc_irq_fifo = platform_get_irq(info->pdev, 0); - if (info->gpmc_irq_fifo <= 0) { - dev_err(dev, "Error getting fifo IRQ\n"); - return -ENODEV; - } + if (info->gpmc_irq_fifo < 0) + return info->gpmc_irq_fifo; err = devm_request_irq(dev, info->gpmc_irq_fifo, omap_nand_irq, IRQF_SHARED, "gpmc-nand-fifo", info); @@ -1988,10 +1910,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip) } info->gpmc_irq_count = platform_get_irq(info->pdev, 1); - if (info->gpmc_irq_count <= 0) { - dev_err(dev, "Error getting IRQ count\n"); - return -ENODEV; - } + if (info->gpmc_irq_count < 0) + return info->gpmc_irq_count; err = devm_request_irq(dev, info->gpmc_irq_count, omap_nand_irq, IRQF_SHARED, "gpmc-nand-count", info); @@ -2002,9 +1922,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip) return err; } - chip->legacy.read_buf = omap_read_buf_irq_pref; - chip->legacy.write_buf = omap_write_buf_irq_pref; - + info->data_in = omap_nand_data_in_irq_pref; + info->data_out = omap_nand_data_out_irq_pref; break; default: @@ -2016,12 +1935,12 @@ static int omap_nand_attach_chip(struct nand_chip *chip) return -EINVAL; /* - * Bail out earlier to let NAND_ECC_SOFT code create its own + * Bail out earlier to let NAND_ECC_ENGINE_TYPE_SOFT code create its own * ooblayout instead of using ours. */ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) { - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; return 0; } @@ -2029,7 +1948,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip) switch (info->ecc_opt) { case OMAP_ECC_HAM1_CODE_HW: dev_info(dev, "nand: using OMAP_ECC_HAM1_CODE_HW\n"); - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.bytes = 3; chip->ecc.size = 512; chip->ecc.strength = 1; @@ -2046,27 +1965,27 @@ static int omap_nand_attach_chip(struct nand_chip *chip) case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n"); - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.size = 512; chip->ecc.bytes = 7; chip->ecc.strength = 4; chip->ecc.hwctl = omap_enable_hwecc_bch; - chip->ecc.correct = nand_bch_correct_data; + chip->ecc.correct = rawnand_sw_bch_correct; chip->ecc.calculate = omap_calculate_ecc_bch_sw; mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); /* Reserve one byte for the OMAP marker */ oobbytes_per_step = chip->ecc.bytes + 1; /* Software BCH library is used for locating errors */ - chip->ecc.priv = nand_bch_init(mtd); - if (!chip->ecc.priv) { + err = rawnand_sw_bch_init(chip); + if (err) { dev_err(dev, "Unable to use BCH library\n"); - return -EINVAL; + goto err_put_elm_dev; } break; case OMAP_ECC_BCH4_CODE_HW: pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n"); - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.size = 512; /* 14th bit is kept reserved for ROM-code compatibility */ chip->ecc.bytes = 7 + 1; @@ -2078,37 +1997,32 @@ static int omap_nand_attach_chip(struct nand_chip *chip) chip->ecc.write_subpage = omap_write_subpage_bch; mtd_set_ooblayout(mtd, &omap_ooblayout_ops); oobbytes_per_step = chip->ecc.bytes; - - err = elm_config(info->elm_dev, BCH4_ECC, - mtd->writesize / chip->ecc.size, - chip->ecc.size, chip->ecc.bytes); - if (err < 0) - return err; + elm_bch_strength = BCH4_ECC; break; case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.size = 512; chip->ecc.bytes = 13; chip->ecc.strength = 8; chip->ecc.hwctl = omap_enable_hwecc_bch; - chip->ecc.correct = nand_bch_correct_data; + chip->ecc.correct = rawnand_sw_bch_correct; chip->ecc.calculate = omap_calculate_ecc_bch_sw; mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); /* Reserve one byte for the OMAP marker */ oobbytes_per_step = chip->ecc.bytes + 1; /* Software BCH library is used for locating errors */ - chip->ecc.priv = nand_bch_init(mtd); - if (!chip->ecc.priv) { + err = rawnand_sw_bch_init(chip); + if (err) { dev_err(dev, "unable to use BCH library\n"); - return -EINVAL; + goto err_put_elm_dev; } break; case OMAP_ECC_BCH8_CODE_HW: pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n"); - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.size = 512; /* 14th bit is kept reserved for ROM-code compatibility */ chip->ecc.bytes = 13 + 1; @@ -2120,18 +2034,12 @@ static int omap_nand_attach_chip(struct nand_chip *chip) chip->ecc.write_subpage = omap_write_subpage_bch; mtd_set_ooblayout(mtd, &omap_ooblayout_ops); oobbytes_per_step = chip->ecc.bytes; - - err = elm_config(info->elm_dev, BCH8_ECC, - mtd->writesize / chip->ecc.size, - chip->ecc.size, chip->ecc.bytes); - if (err < 0) - return err; - + elm_bch_strength = BCH8_ECC; break; case OMAP_ECC_BCH16_CODE_HW: pr_info("Using OMAP_ECC_BCH16_CODE_HW ECC scheme\n"); - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.size = 512; chip->ecc.bytes = 26; chip->ecc.strength = 16; @@ -2142,17 +2050,31 @@ static int omap_nand_attach_chip(struct nand_chip *chip) chip->ecc.write_subpage = omap_write_subpage_bch; mtd_set_ooblayout(mtd, &omap_ooblayout_ops); oobbytes_per_step = chip->ecc.bytes; - - err = elm_config(info->elm_dev, BCH16_ECC, - mtd->writesize / chip->ecc.size, - chip->ecc.size, chip->ecc.bytes); - if (err < 0) - return err; - + elm_bch_strength = BCH16_ECC; break; default: dev_err(dev, "Invalid or unsupported ECC scheme\n"); - return -EINVAL; + err = -EINVAL; + goto err_put_elm_dev; + } + + if (elm_bch_strength >= 0) { + chip->ecc.steps = mtd->writesize / chip->ecc.size; + info->neccpg = chip->ecc.steps / ERROR_VECTOR_MAX; + if (info->neccpg) { + info->nsteps_per_eccpg = ERROR_VECTOR_MAX; + } else { + info->neccpg = 1; + info->nsteps_per_eccpg = chip->ecc.steps; + } + info->eccpg_size = info->nsteps_per_eccpg * chip->ecc.size; + info->eccpg_bytes = info->nsteps_per_eccpg * chip->ecc.bytes; + + err = elm_config(info->elm_dev, elm_bch_strength, + info->nsteps_per_eccpg, chip->ecc.size, + chip->ecc.bytes); + if (err < 0) + goto err_put_elm_dev; } /* Check if NAND device's OOB is enough to store ECC signatures */ @@ -2162,7 +2084,117 @@ static int omap_nand_attach_chip(struct nand_chip *chip) dev_err(dev, "Not enough OOB bytes: required = %d, available=%d\n", min_oobbytes, mtd->oobsize); - return -EINVAL; + err = -EINVAL; + goto err_put_elm_dev; + } + + return 0; + +err_put_elm_dev: + put_device(info->elm_dev); + + return err; +} + +static void omap_nand_detach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct omap_nand_info *info = mtd_to_omap(mtd); + + put_device(info->elm_dev); +} + +static void omap_nand_data_in(struct nand_chip *chip, void *buf, + unsigned int len, bool force_8bit) +{ + struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); + u32 alignment = ((uintptr_t)buf | len) & 3; + + if (force_8bit || (alignment & 1)) + ioread8_rep(info->fifo, buf, len); + else if (alignment & 3) + ioread16_rep(info->fifo, buf, len >> 1); + else + ioread32_rep(info->fifo, buf, len >> 2); +} + +static void omap_nand_data_out(struct nand_chip *chip, + const void *buf, unsigned int len, + bool force_8bit) +{ + struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); + u32 alignment = ((uintptr_t)buf | len) & 3; + + if (force_8bit || (alignment & 1)) + iowrite8_rep(info->fifo, buf, len); + else if (alignment & 3) + iowrite16_rep(info->fifo, buf, len >> 1); + else + iowrite32_rep(info->fifo, buf, len >> 2); +} + +static int omap_nand_exec_instr(struct nand_chip *chip, + const struct nand_op_instr *instr) +{ + struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); + unsigned int i; + int ret; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + iowrite8(instr->ctx.cmd.opcode, + info->reg.gpmc_nand_command); + break; + + case NAND_OP_ADDR_INSTR: + for (i = 0; i < instr->ctx.addr.naddrs; i++) { + iowrite8(instr->ctx.addr.addrs[i], + info->reg.gpmc_nand_address); + } + break; + + case NAND_OP_DATA_IN_INSTR: + info->data_in(chip, instr->ctx.data.buf.in, + instr->ctx.data.len, + instr->ctx.data.force_8bit); + break; + + case NAND_OP_DATA_OUT_INSTR: + info->data_out(chip, instr->ctx.data.buf.out, + instr->ctx.data.len, + instr->ctx.data.force_8bit); + break; + + case NAND_OP_WAITRDY_INSTR: + ret = info->ready_gpiod ? + nand_gpio_waitrdy(chip, info->ready_gpiod, instr->ctx.waitrdy.timeout_ms) : + nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms); + if (ret) + return ret; + break; + } + + if (instr->delay_ns) + ndelay(instr->delay_ns); + + return 0; +} + +static int omap_nand_exec_op(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only) +{ + unsigned int i; + + if (check_only) + return 0; + + for (i = 0; i < op->ninstrs; i++) { + int ret; + + ret = omap_nand_exec_instr(chip, &op->instrs[i]); + if (ret) + return ret; } return 0; @@ -2170,14 +2202,13 @@ static int omap_nand_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops omap_nand_controller_ops = { .attach_chip = omap_nand_attach_chip, + .detach_chip = omap_nand_detach_chip, + .exec_op = omap_nand_exec_op, }; /* Shared among all NAND instances to synchronize access to the ECC Engine */ -static struct nand_controller omap_gpmc_controller = { - .lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock), - .wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq), - .ops = &omap_nand_controller_ops, -}; +static struct nand_controller omap_gpmc_controller; +static bool omap_gpmc_controller_initialized; static int omap_nand_probe(struct platform_device *pdev) { @@ -2187,6 +2218,7 @@ static int omap_nand_probe(struct platform_device *pdev) int err; struct resource *res; struct device *dev = &pdev->dev; + void __iomem *vaddr; info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), GFP_KERNEL); @@ -2208,7 +2240,6 @@ static int omap_nand_probe(struct platform_device *pdev) 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, dev->of_node); if (!mtd->name) { @@ -2220,17 +2251,20 @@ static int omap_nand_probe(struct platform_device *pdev) } } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nand_chip->legacy.IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(nand_chip->legacy.IO_ADDR_R)) - return PTR_ERR(nand_chip->legacy.IO_ADDR_R); + vaddr = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(vaddr)) + return PTR_ERR(vaddr); + info->fifo = vaddr; info->phys_base = res->start; - nand_chip->controller = &omap_gpmc_controller; + if (!omap_gpmc_controller_initialized) { + omap_gpmc_controller.ops = &omap_nand_controller_ops; + nand_controller_init(&omap_gpmc_controller); + omap_gpmc_controller_initialized = true; + } - nand_chip->legacy.IO_ADDR_W = nand_chip->legacy.IO_ADDR_R; - nand_chip->legacy.cmd_ctrl = omap_hwcontrol; + nand_chip->controller = &omap_gpmc_controller; info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb", GPIOD_IN); @@ -2239,26 +2273,12 @@ static int omap_nand_probe(struct platform_device *pdev) 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 - * 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 (info->ready_gpiod) { - nand_chip->legacy.dev_ready = omap_dev_ready; - nand_chip->legacy.chip_delay = 0; - } else { - nand_chip->legacy.waitfunc = omap_wait; - nand_chip->legacy.chip_delay = 50; - } - if (info->flash_bbt) nand_chip->bbt_options |= NAND_BBT_USE_FLASH; - /* scan NAND device connected to chip controller */ - nand_chip->options |= info->devsize & NAND_BUSWIDTH_16; + /* default operations */ + info->data_in = omap_nand_data_in; + info->data_out = omap_nand_data_out; err = nand_scan(nand_chip, 1); if (err) @@ -2278,32 +2298,27 @@ cleanup_nand: return_error: if (!IS_ERR_OR_NULL(info->dma)) dma_release_channel(info->dma); - if (nand_chip->ecc.priv) { - nand_bch_free(nand_chip->ecc.priv); - nand_chip->ecc.priv = NULL; - } + + rawnand_sw_bch_cleanup(nand_chip); + return err; } -static int omap_nand_remove(struct platform_device *pdev) +static void omap_nand_remove(struct platform_device *pdev) { struct mtd_info *mtd = platform_get_drvdata(pdev); struct nand_chip *nand_chip = mtd_to_nand(mtd); struct omap_nand_info *info = mtd_to_omap(mtd); - if (nand_chip->ecc.priv) { - nand_bch_free(nand_chip->ecc.priv); - nand_chip->ecc.priv = NULL; - } + + rawnand_sw_bch_cleanup(nand_chip); + if (info->dma) dma_release_channel(info->dma); - nand_release(nand_chip); - return 0; + WARN_ON(mtd_device_unregister(mtd)); + nand_cleanup(nand_chip); } -static const struct of_device_id omap_nand_ids[] = { - { .compatible = "ti,omap2-nand", }, - {}, -}; +/* omap_nand_ids defined in linux/platform_data/mtd-nand-omap2.h */ MODULE_DEVICE_TABLE(of, omap_nand_ids); static struct platform_driver omap_nand_driver = { @@ -2311,12 +2326,11 @@ static struct platform_driver omap_nand_driver = { .remove = omap_nand_remove, .driver = { .name = DRIVER_NAME, - .of_match_table = of_match_ptr(omap_nand_ids), + .of_match_table = omap_nand_ids, }, }; module_platform_driver(omap_nand_driver); -MODULE_ALIAS("platform:" DRIVER_NAME); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards"); |
