mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-07 22:08:33 -04:00
mtd: rawnand: omap2: move to exec_op interface
Stop using legacy interface and move to the exec_op interface. Signed-off-by: Roger Quadros <rogerq@kernel.org> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Link: https://lore.kernel.org/linux-mtd/20211209090458.24830-4-rogerq@kernel.org
This commit is contained in:
committed by
Miquel Raynal
parent
35da0c4545
commit
a9e849efca
@@ -19,7 +19,7 @@
|
||||
#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>
|
||||
@@ -164,6 +164,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 +176,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 +188,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 +254,70 @@ 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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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 +326,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 +362,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 +433,41 @@ 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
|
||||
*/
|
||||
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 (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
|
||||
*/
|
||||
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 (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 +491,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 +517,17 @@ static irqreturn_t omap_nand_irq(int this_irq, void *dev)
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 +538,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 +555,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 +582,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 +608,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -981,50 +872,6 @@ static void omap_enable_hwecc(struct nand_chip *chip, int mode)
|
||||
writel(val, info->reg.gpmc_ecc_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -1543,8 +1390,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 +1409,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 +1454,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 +1488,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 +1831,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,8 +1864,9 @@ 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;
|
||||
|
||||
@@ -2049,9 +1897,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:
|
||||
@@ -2217,8 +2064,105 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops omap_nand_controller_ops = {
|
||||
.attach_chip = omap_nand_attach_chip,
|
||||
.exec_op = omap_nand_exec_op,
|
||||
};
|
||||
|
||||
/* Shared among all NAND instances to synchronize access to the ECC Engine */
|
||||
@@ -2233,6 +2177,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);
|
||||
@@ -2266,10 +2211,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_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(vaddr))
|
||||
return PTR_ERR(vaddr);
|
||||
|
||||
info->fifo = vaddr;
|
||||
info->phys_base = res->start;
|
||||
|
||||
if (!omap_gpmc_controller_initialized) {
|
||||
@@ -2280,9 +2226,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,27 +2233,16 @@ 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)
|
||||
goto return_error;
|
||||
|
||||
Reference in New Issue
Block a user