summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand/raw/cs553x_nand.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/raw/cs553x_nand.c')
-rw-r--r--drivers/mtd/nand/raw/cs553x_nand.c199
1 files changed, 138 insertions, 61 deletions
diff --git a/drivers/mtd/nand/raw/cs553x_nand.c b/drivers/mtd/nand/raw/cs553x_nand.c
index e2322cee3229..9472bf798ed5 100644
--- a/drivers/mtd/nand/raw/cs553x_nand.c
+++ b/drivers/mtd/nand/raw/cs553x_nand.c
@@ -21,9 +21,9 @@
#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
+#include <linux/iopoll.h>
#include <asm/msr.h>
-#include <asm/io.h>
#define NR_CS553X_CONTROLLERS 4
@@ -89,76 +89,151 @@
#define CS_NAND_ECC_CLRECC (1<<1)
#define CS_NAND_ECC_ENECC (1<<0)
-static void cs553x_read_buf(struct nand_chip *this, u_char *buf, int len)
+struct cs553x_nand_controller {
+ struct nand_controller base;
+ struct nand_chip chip;
+ void __iomem *mmio;
+};
+
+static struct cs553x_nand_controller *
+to_cs553x(struct nand_controller *controller)
+{
+ return container_of(controller, struct cs553x_nand_controller, base);
+}
+
+static int cs553x_write_ctrl_byte(struct cs553x_nand_controller *cs553x,
+ u32 ctl, u8 data)
{
+ u8 status;
+ int ret;
+
+ writeb(ctl, cs553x->mmio + MM_NAND_CTL);
+ writeb(data, cs553x->mmio + MM_NAND_IO);
+ ret = readb_poll_timeout_atomic(cs553x->mmio + MM_NAND_STS, status,
+ !(status & CS_NAND_CTLR_BUSY), 1,
+ 100000);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void cs553x_data_in(struct cs553x_nand_controller *cs553x, void *buf,
+ unsigned int len)
+{
+ writeb(0, cs553x->mmio + MM_NAND_CTL);
while (unlikely(len > 0x800)) {
- memcpy_fromio(buf, this->legacy.IO_ADDR_R, 0x800);
+ memcpy_fromio(buf, cs553x->mmio, 0x800);
buf += 0x800;
len -= 0x800;
}
- memcpy_fromio(buf, this->legacy.IO_ADDR_R, len);
+ memcpy_fromio(buf, cs553x->mmio, len);
}
-static void cs553x_write_buf(struct nand_chip *this, const u_char *buf, int len)
+static void cs553x_data_out(struct cs553x_nand_controller *cs553x,
+ const void *buf, unsigned int len)
{
+ writeb(0, cs553x->mmio + MM_NAND_CTL);
while (unlikely(len > 0x800)) {
- memcpy_toio(this->legacy.IO_ADDR_R, buf, 0x800);
+ memcpy_toio(cs553x->mmio, buf, 0x800);
buf += 0x800;
len -= 0x800;
}
- memcpy_toio(this->legacy.IO_ADDR_R, buf, len);
+ memcpy_toio(cs553x->mmio, buf, len);
}
-static unsigned char cs553x_read_byte(struct nand_chip *this)
+static int cs553x_wait_ready(struct cs553x_nand_controller *cs553x,
+ unsigned int timeout_ms)
{
- return readb(this->legacy.IO_ADDR_R);
+ u8 mask = CS_NAND_CTLR_BUSY | CS_NAND_STS_FLASH_RDY;
+ u8 status;
+
+ return readb_poll_timeout(cs553x->mmio + MM_NAND_STS, status,
+ (status & mask) == CS_NAND_STS_FLASH_RDY, 100,
+ timeout_ms * 1000);
}
-static void cs553x_write_byte(struct nand_chip *this, u_char byte)
+static int cs553x_exec_instr(struct cs553x_nand_controller *cs553x,
+ const struct nand_op_instr *instr)
{
- int i = 100000;
+ unsigned int i;
+ int ret = 0;
+
+ switch (instr->type) {
+ case NAND_OP_CMD_INSTR:
+ ret = cs553x_write_ctrl_byte(cs553x, CS_NAND_CTL_CLE,
+ instr->ctx.cmd.opcode);
+ break;
+
+ case NAND_OP_ADDR_INSTR:
+ for (i = 0; i < instr->ctx.addr.naddrs; i++) {
+ ret = cs553x_write_ctrl_byte(cs553x, CS_NAND_CTL_ALE,
+ instr->ctx.addr.addrs[i]);
+ if (ret)
+ break;
+ }
+ break;
+
+ case NAND_OP_DATA_IN_INSTR:
+ cs553x_data_in(cs553x, instr->ctx.data.buf.in,
+ instr->ctx.data.len);
+ break;
+
+ case NAND_OP_DATA_OUT_INSTR:
+ cs553x_data_out(cs553x, instr->ctx.data.buf.out,
+ instr->ctx.data.len);
+ break;
- while (i && readb(this->legacy.IO_ADDR_R + MM_NAND_STS) & CS_NAND_CTLR_BUSY) {
- udelay(1);
- i--;
+ case NAND_OP_WAITRDY_INSTR:
+ ret = cs553x_wait_ready(cs553x, instr->ctx.waitrdy.timeout_ms);
+ break;
}
- writeb(byte, this->legacy.IO_ADDR_W + 0x801);
+
+ if (instr->delay_ns)
+ ndelay(instr->delay_ns);
+
+ return ret;
}
-static void cs553x_hwcontrol(struct nand_chip *this, int cmd,
- unsigned int ctrl)
+static int cs553x_exec_op(struct nand_chip *this,
+ const struct nand_operation *op,
+ bool check_only)
{
- void __iomem *mmio_base = this->legacy.IO_ADDR_R;
- if (ctrl & NAND_CTRL_CHANGE) {
- unsigned char ctl = (ctrl & ~NAND_CTRL_CHANGE ) ^ 0x01;
- writeb(ctl, mmio_base + MM_NAND_CTL);
+ struct cs553x_nand_controller *cs553x = to_cs553x(this->controller);
+ unsigned int i;
+ int ret;
+
+ if (check_only)
+ return true;
+
+ /* De-assert the CE pin */
+ writeb(0, cs553x->mmio + MM_NAND_CTL);
+ for (i = 0; i < op->ninstrs; i++) {
+ ret = cs553x_exec_instr(cs553x, &op->instrs[i]);
+ if (ret)
+ break;
}
- if (cmd != NAND_CMD_NONE)
- cs553x_write_byte(this, cmd);
-}
-static int cs553x_device_ready(struct nand_chip *this)
-{
- void __iomem *mmio_base = this->legacy.IO_ADDR_R;
- unsigned char foo = readb(mmio_base + MM_NAND_STS);
+ /* Re-assert the CE pin. */
+ writeb(CS_NAND_CTL_CE, cs553x->mmio + MM_NAND_CTL);
- return (foo & CS_NAND_STS_FLASH_RDY) && !(foo & CS_NAND_CTLR_BUSY);
+ return ret;
}
static void cs_enable_hwecc(struct nand_chip *this, int mode)
{
- void __iomem *mmio_base = this->legacy.IO_ADDR_R;
+ struct cs553x_nand_controller *cs553x = to_cs553x(this->controller);
- writeb(0x07, mmio_base + MM_NAND_ECC_CTL);
+ writeb(0x07, cs553x->mmio + MM_NAND_ECC_CTL);
}
static int cs_calculate_ecc(struct nand_chip *this, const u_char *dat,
u_char *ecc_code)
{
+ struct cs553x_nand_controller *cs553x = to_cs553x(this->controller);
uint32_t ecc;
- void __iomem *mmio_base = this->legacy.IO_ADDR_R;
- ecc = readl(mmio_base + MM_NAND_STS);
+ ecc = readl(cs553x->mmio + MM_NAND_STS);
ecc_code[1] = ecc >> 8;
ecc_code[0] = ecc >> 16;
@@ -166,10 +241,15 @@ static int cs_calculate_ecc(struct nand_chip *this, const u_char *dat,
return 0;
}
-static struct mtd_info *cs553x_mtd[4];
+static struct cs553x_nand_controller *controllers[4];
+
+static const struct nand_controller_ops cs553x_nand_controller_ops = {
+ .exec_op = cs553x_exec_op,
+};
static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
{
+ struct cs553x_nand_controller *controller;
int err = 0;
struct nand_chip *this;
struct mtd_info *new_mtd;
@@ -183,33 +263,29 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
}
/* Allocate memory for MTD device structure and private data */
- this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
- if (!this) {
+ controller = kzalloc(sizeof(*controller), GFP_KERNEL);
+ if (!controller) {
err = -ENOMEM;
goto out;
}
+ this = &controller->chip;
+ nand_controller_init(&controller->base);
+ controller->base.ops = &cs553x_nand_controller_ops;
+ this->controller = &controller->base;
new_mtd = nand_to_mtd(this);
/* Link the private data with the MTD structure */
new_mtd->owner = THIS_MODULE;
/* map physical address */
- this->legacy.IO_ADDR_R = this->legacy.IO_ADDR_W = ioremap(adr, 4096);
- if (!this->legacy.IO_ADDR_R) {
+ controller->mmio = ioremap(adr, 4096);
+ if (!controller->mmio) {
pr_warn("ioremap cs553x NAND @0x%08lx failed\n", adr);
err = -EIO;
goto out_mtd;
}
- this->legacy.cmd_ctrl = cs553x_hwcontrol;
- this->legacy.dev_ready = cs553x_device_ready;
- this->legacy.read_byte = cs553x_read_byte;
- this->legacy.read_buf = cs553x_read_buf;
- this->legacy.write_buf = cs553x_write_buf;
-
- this->legacy.chip_delay = 0;
-
this->ecc.mode = NAND_ECC_HW;
this->ecc.size = 256;
this->ecc.bytes = 3;
@@ -232,15 +308,15 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
if (err)
goto out_free;
- cs553x_mtd[cs] = new_mtd;
+ controllers[cs] = controller;
goto out;
out_free:
kfree(new_mtd->name);
out_ior:
- iounmap(this->legacy.IO_ADDR_R);
+ iounmap(controller->mmio);
out_mtd:
- kfree(this);
+ kfree(controller);
out:
return err;
}
@@ -295,9 +371,10 @@ static int __init cs553x_init(void)
/* Register all devices together here. This means we can easily hack it to
do mtdconcat etc. if we want to. */
for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
- if (cs553x_mtd[i]) {
+ if (controllers[i]) {
/* If any devices registered, return success. Else the last error. */
- mtd_device_register(cs553x_mtd[i], NULL, 0);
+ mtd_device_register(nand_to_mtd(&controllers[i]->chip),
+ NULL, 0);
err = 0;
}
}
@@ -312,26 +389,26 @@ static void __exit cs553x_cleanup(void)
int i;
for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
- struct mtd_info *mtd = cs553x_mtd[i];
- struct nand_chip *this;
- void __iomem *mmio_base;
+ struct cs553x_nand_controller *controller = controllers[i];
+ struct nand_chip *this = &controller->chip;
+ struct mtd_info *mtd = nand_to_mtd(this);
+ int ret;
if (!mtd)
continue;
- this = mtd_to_nand(mtd);
- mmio_base = this->legacy.IO_ADDR_R;
-
/* Release resources, unregister device */
- nand_release(this);
+ ret = mtd_device_unregister(mtd);
+ WARN_ON(ret);
+ nand_cleanup(this);
kfree(mtd->name);
- cs553x_mtd[i] = NULL;
+ controllers[i] = NULL;
/* unmap physical address */
- iounmap(mmio_base);
+ iounmap(controller->mmio);
/* Free the MTD device structure */
- kfree(this);
+ kfree(controller);
}
}