diff options
Diffstat (limited to 'drivers/mtd/nand/raw/omap2.c')
| -rw-r--r-- | drivers/mtd/nand/raw/omap2.c | 572 |
1 files changed, 267 insertions, 305 deletions
diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index b1839eef5b65..39e297486721 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -19,10 +19,10 @@ #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/platform_data/elm.h> @@ -148,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; @@ -164,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; @@ -175,6 +175,11 @@ struct omap_nand_info { 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) @@ -182,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 @@ -241,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--; } @@ -412,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; @@ -451,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; @@ -521,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); } /* @@ -587,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; @@ -613,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; } @@ -637,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; @@ -652,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; } @@ -687,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; @@ -711,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); } /** @@ -911,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 @@ -982,50 +898,6 @@ 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; - - timeo += msecs_to_jiffies(400); - - 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 - * @chip: NAND chip object - * - * 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 * @chip: NAND chip object * @mode: Read/Write mode @@ -1543,8 +1415,8 @@ static int omap_write_page_bch(struct nand_chip *chip, const uint8_t *buf, chip->ecc.hwctl(chip, NAND_ECC_WRITE); /* Write data */ - chip->legacy.write_buf(chip, buf + (eccpg * info->eccpg_size), - info->eccpg_size); + info->data_out(chip, buf + (eccpg * info->eccpg_size), + info->eccpg_size, false); /* Update ecc vector from GPMC result registers */ ret = omap_calculate_ecc_bch_multi(mtd, @@ -1562,7 +1434,7 @@ static int omap_write_page_bch(struct nand_chip *chip, const uint8_t *buf, } /* 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); } @@ -1607,8 +1479,8 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset, chip->ecc.hwctl(chip, NAND_ECC_WRITE); /* Write data */ - chip->legacy.write_buf(chip, buf + (eccpg * info->eccpg_size), - info->eccpg_size); + 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; @@ -1641,7 +1513,7 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset, } /* 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); } @@ -1984,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: @@ -2017,15 +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) - 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); @@ -2037,8 +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) - 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); @@ -2049,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: @@ -2107,7 +1979,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip) err = rawnand_sw_bch_init(chip); if (err) { dev_err(dev, "Unable to use BCH library\n"); - return err; + goto err_put_elm_dev; } break; @@ -2144,7 +2016,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip) err = rawnand_sw_bch_init(chip); if (err) { dev_err(dev, "unable to use BCH library\n"); - return err; + goto err_put_elm_dev; } break; @@ -2182,7 +2054,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip) 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) { @@ -2201,7 +2074,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip) info->nsteps_per_eccpg, chip->ecc.size, chip->ecc.bytes); if (err < 0) - return err; + goto err_put_elm_dev; } /* Check if NAND device's OOB is enough to store ECC signatures */ @@ -2211,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; @@ -2219,6 +2202,8 @@ 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 */ @@ -2233,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); @@ -2265,11 +2251,11 @@ 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; if (!omap_gpmc_controller_initialized) { @@ -2280,9 +2266,6 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->controller = &omap_gpmc_controller; - nand_chip->legacy.IO_ADDR_W = nand_chip->legacy.IO_ADDR_R; - nand_chip->legacy.cmd_ctrl = omap_hwcontrol; - info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb", GPIOD_IN); if (IS_ERR(info->ready_gpiod)) { @@ -2290,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) @@ -2335,27 +2304,21 @@ return_error: 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); - int ret; rawnand_sw_bch_cleanup(nand_chip); if (info->dma) dma_release_channel(info->dma); - ret = mtd_device_unregister(mtd); - WARN_ON(ret); + WARN_ON(mtd_device_unregister(mtd)); nand_cleanup(nand_chip); - return ret; } -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 = { @@ -2363,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"); |
