Commit b4ed6328 authored by Boris Brezillon's avatar Boris Brezillon Committed by Miquel Raynal
Browse files

mtd: rawnand: cs553x: Implement exec_op()

parent ba03e483
Loading
Loading
Loading
Loading
+125 −1
Original line number Diff line number Diff line
@@ -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

@@ -165,6 +165,125 @@ static int cs553x_device_ready(struct nand_chip *this)
	return (foo & CS_NAND_STS_FLASH_RDY) && !(foo & CS_NAND_CTLR_BUSY);
}

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, cs553x->mmio, 0x800);
		buf += 0x800;
		len -= 0x800;
	}
	memcpy_fromio(buf, cs553x->mmio, 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(cs553x->mmio, buf, 0x800);
		buf += 0x800;
		len -= 0x800;
	}
	memcpy_toio(cs553x->mmio, buf, len);
}

static int cs553x_wait_ready(struct cs553x_nand_controller *cs553x,
			     unsigned int timeout_ms)
{
	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 int cs553x_exec_instr(struct cs553x_nand_controller *cs553x,
			     const struct nand_op_instr *instr)
{
	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;

	case NAND_OP_WAITRDY_INSTR:
		ret = cs553x_wait_ready(cs553x, instr->ctx.waitrdy.timeout_ms);
		break;
	}

	if (instr->delay_ns)
		ndelay(instr->delay_ns);

	return ret;
}

static int cs553x_exec_op(struct nand_chip *this,
			  const struct nand_operation *op,
			  bool check_only)
{
	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;
	}

	/* Re-assert the CE pin. */
	writeb(CS_NAND_CTL_CE, cs553x->mmio + MM_NAND_CTL);

	return ret;
}

static void cs_enable_hwecc(struct nand_chip *this, int mode)
{
	struct cs553x_nand_controller *cs553x = to_cs553x(this->controller);
@@ -188,6 +307,10 @@ static int cs_calculate_ecc(struct nand_chip *this, const u_char *dat,

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;
@@ -212,6 +335,7 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)

	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);