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

mtd: rawnand: sunxi: Migrate to ->exec_op()



And get rif of all legacy hooks and unused fields.

Signed-off-by: default avatarBoris Brezillon <boris.brezillon@bootlin.com>
Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
parent f5f88871
Loading
Loading
Loading
Loading
+174 −120
Original line number Diff line number Diff line
@@ -183,7 +183,6 @@ struct sunxi_nand_hw_ecc {
 * @mtd:		base MTD structure
 * @clk_rate:		clk_rate required for this NAND chip
 * @timing_cfg		TIMING_CFG register value for this NAND chip
 * @selected:		current active CS
 * @nsels:		number of CS lines required by the NAND chip
 * @sels:		array of CS lines descriptions
 */
@@ -193,11 +192,6 @@ struct sunxi_nand_chip {
	unsigned long clk_rate;
	u32 timing_cfg;
	u32 timing_ctl;
	int selected;
	int addr_cycles;
	u32 addr[2];
	int cmd_cycles;
	u8 cmd[2];
	int nsels;
	struct sunxi_nand_chip_sel sels[0];
};
@@ -386,26 +380,7 @@ static void sunxi_nfc_dma_op_cleanup(struct sunxi_nfc *nfc,
	       nfc->regs + NFC_REG_CTL);
}

static int sunxi_nfc_dev_ready(struct nand_chip *nand)
{
	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
	u32 mask;

	if (sunxi_nand->selected < 0)
		return 0;

	if (sunxi_nand->sels[sunxi_nand->selected].rb < 0) {
		dev_err(nfc->dev, "cannot check R/B NAND status!\n");
		return 0;
	}

	mask = NFC_RB_STATE(sunxi_nand->sels[sunxi_nand->selected].rb);

	return !!(readl(nfc->regs + NFC_REG_ST) & mask);
}

static void sunxi_nfc_select_chip(struct nand_chip *nand, int chip)
static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs)
{
	struct mtd_info *mtd = nand_to_mtd(nand);
	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
@@ -413,26 +388,16 @@ static void sunxi_nfc_select_chip(struct nand_chip *nand, int chip)
	struct sunxi_nand_chip_sel *sel;
	u32 ctl;

	if (chip > 0 && chip >= sunxi_nand->nsels)
		return;

	if (chip == sunxi_nand->selected)
	if (cs > 0 && cs >= sunxi_nand->nsels)
		return;

	ctl = readl(nfc->regs + NFC_REG_CTL) &
	      ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN);

	if (chip >= 0) {
		sel = &sunxi_nand->sels[chip];

		ctl |= NFC_CE_SEL(sel->cs) | NFC_EN |
		       NFC_PAGE_SHIFT(nand->page_shift);
		if (sel->rb < 0) {
			nand->legacy.dev_ready = NULL;
		} else {
			nand->legacy.dev_ready = sunxi_nfc_dev_ready;
	sel = &sunxi_nand->sels[cs];
	ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | NFC_PAGE_SHIFT(nand->page_shift);
	if (sel->rb >= 0)
		ctl |= NFC_RB_SEL(sel->rb);
		}

	writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);

@@ -440,13 +405,10 @@ static void sunxi_nfc_select_chip(struct nand_chip *nand, int chip)
		clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate);
		nfc->clk_rate = sunxi_nand->clk_rate;
	}
	}

	writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL);
	writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG);
	writel(ctl, nfc->regs + NFC_REG_CTL);

	sunxi_nand->selected = chip;
}

static void sunxi_nfc_read_buf(struct nand_chip *nand, uint8_t *buf, int len)
@@ -523,71 +485,6 @@ static void sunxi_nfc_write_buf(struct nand_chip *nand, const uint8_t *buf,
	}
}

static uint8_t sunxi_nfc_read_byte(struct nand_chip *nand)
{
	uint8_t ret = 0;

	sunxi_nfc_read_buf(nand, &ret, 1);

	return ret;
}

static void sunxi_nfc_cmd_ctrl(struct nand_chip *nand, int dat,
			       unsigned int ctrl)
{
	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
	int ret;

	if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) &&
	    !(ctrl & (NAND_CLE | NAND_ALE))) {
		u32 cmd = 0;

		if (!sunxi_nand->addr_cycles && !sunxi_nand->cmd_cycles)
			return;

		if (sunxi_nand->cmd_cycles--)
			cmd |= NFC_SEND_CMD1 | sunxi_nand->cmd[0];

		if (sunxi_nand->cmd_cycles--) {
			cmd |= NFC_SEND_CMD2;
			writel(sunxi_nand->cmd[1],
			       nfc->regs + NFC_REG_RCMD_SET);
		}

		sunxi_nand->cmd_cycles = 0;

		if (sunxi_nand->addr_cycles) {
			cmd |= NFC_SEND_ADR |
			       NFC_ADR_NUM(sunxi_nand->addr_cycles);
			writel(sunxi_nand->addr[0],
			       nfc->regs + NFC_REG_ADDR_LOW);
		}

		if (sunxi_nand->addr_cycles > 4)
			writel(sunxi_nand->addr[1],
			       nfc->regs + NFC_REG_ADDR_HIGH);

		ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
		if (ret)
			return;

		writel(cmd, nfc->regs + NFC_REG_CMD);
		sunxi_nand->addr[0] = 0;
		sunxi_nand->addr[1] = 0;
		sunxi_nand->addr_cycles = 0;
		sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
	}

	if (ctrl & NAND_CLE) {
		sunxi_nand->cmd[sunxi_nand->cmd_cycles++] = dat;
	} else if (ctrl & NAND_ALE) {
		sunxi_nand->addr[sunxi_nand->addr_cycles / 4] |=
				dat << ((sunxi_nand->addr_cycles % 4) * 8);
		sunxi_nand->addr_cycles++;
	}
}

/* These seed values have been extracted from Allwinner's BSP */
static const u16 sunxi_nfc_randomizer_page_seeds[] = {
	0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
@@ -1174,6 +1071,8 @@ static int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf,
	int ret, i, cur_off = 0;
	bool raw_mode = false;

	sunxi_nfc_select_chip(nand, nand->cur_cs);

	nand_read_page_op(nand, page, 0, NULL, 0);

	sunxi_nfc_hw_ecc_enable(nand);
@@ -1208,6 +1107,8 @@ static int sunxi_nfc_hw_ecc_read_page_dma(struct nand_chip *nand, u8 *buf,
{
	int ret;

	sunxi_nfc_select_chip(nand, nand->cur_cs);

	nand_read_page_op(nand, page, 0, NULL, 0);

	ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, oob_required, page,
@@ -1228,6 +1129,8 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand,
	int ret, i, cur_off = 0;
	unsigned int max_bitflips = 0;

	sunxi_nfc_select_chip(nand, nand->cur_cs);

	nand_read_page_op(nand, page, 0, NULL, 0);

	sunxi_nfc_hw_ecc_enable(nand);
@@ -1260,6 +1163,8 @@ static int sunxi_nfc_hw_ecc_read_subpage_dma(struct nand_chip *nand,
	int nchunks = DIV_ROUND_UP(data_offs + readlen, nand->ecc.size);
	int ret;

	sunxi_nfc_select_chip(nand, nand->cur_cs);

	nand_read_page_op(nand, page, 0, NULL, 0);

	ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, false, page, nchunks);
@@ -1279,6 +1184,8 @@ static int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand,
	struct nand_ecc_ctrl *ecc = &nand->ecc;
	int ret, i, cur_off = 0;

	sunxi_nfc_select_chip(nand, nand->cur_cs);

	nand_prog_page_begin_op(nand, page, 0, NULL, 0);

	sunxi_nfc_hw_ecc_enable(nand);
@@ -1314,6 +1221,8 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct nand_chip *nand,
	struct nand_ecc_ctrl *ecc = &nand->ecc;
	int ret, i, cur_off = 0;

	sunxi_nfc_select_chip(nand, nand->cur_cs);

	nand_prog_page_begin_op(nand, page, 0, NULL, 0);

	sunxi_nfc_hw_ecc_enable(nand);
@@ -1347,6 +1256,8 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
	struct scatterlist sg;
	int ret, i;

	sunxi_nfc_select_chip(nand, nand->cur_cs);

	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
	if (ret)
		return ret;
@@ -1820,9 +1731,159 @@ static int sunxi_nand_attach_chip(struct nand_chip *nand)
	return 0;
}

static int sunxi_nfc_exec_subop(struct nand_chip *nand,
				const struct nand_subop *subop)
{
	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
	u32 cmd = 0, extcmd = 0, cnt = 0, addrs[2] = { };
	unsigned int i, j, remaining, start;
	void *inbuf = NULL;
	int ret;

	for (i = 0; i < subop->ninstrs; i++) {
		const struct nand_op_instr *instr = &subop->instrs[i];

		switch (instr->type) {
		case NAND_OP_CMD_INSTR:
			if (cmd & NFC_SEND_CMD1) {
				if (WARN_ON(cmd & NFC_SEND_CMD2))
					return -EINVAL;

				cmd |= NFC_SEND_CMD2;
				extcmd |= instr->ctx.cmd.opcode;
			} else {
				cmd |= NFC_SEND_CMD1 |
				       NFC_CMD(instr->ctx.cmd.opcode);
			}
			break;

		case NAND_OP_ADDR_INSTR:
			remaining = nand_subop_get_num_addr_cyc(subop, i);
			start = nand_subop_get_addr_start_off(subop, i);
			for (j = 0; j < 8 && j + start < remaining; j++) {
				u32 addr = instr->ctx.addr.addrs[j + start];

				addrs[j / 4] |= addr << (j % 4) * 8;
			}

			if (j)
				cmd |= NFC_SEND_ADR | NFC_ADR_NUM(j);

			break;

		case NAND_OP_DATA_IN_INSTR:
		case NAND_OP_DATA_OUT_INSTR:
			start = nand_subop_get_data_start_off(subop, i);
			remaining = nand_subop_get_data_len(subop, i);
			cnt = min_t(u32, remaining, NFC_SRAM_SIZE);
			cmd |= NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;

			if (instr->type == NAND_OP_DATA_OUT_INSTR) {
				cmd |= NFC_ACCESS_DIR;
				memcpy_toio(nfc->regs + NFC_RAM0_BASE,
					    instr->ctx.data.buf.out + start,
					    cnt);
			} else {
				inbuf = instr->ctx.data.buf.in + start;
			}

			break;

		case NAND_OP_WAITRDY_INSTR:
			cmd |= NFC_WAIT_FLAG;
			break;
		}
	}

	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
	if (ret)
		return ret;

	if (cmd & NFC_SEND_ADR) {
		writel(addrs[0], nfc->regs + NFC_REG_ADDR_LOW);
		writel(addrs[1], nfc->regs + NFC_REG_ADDR_HIGH);
	}

	if (cmd & NFC_SEND_CMD2)
		writel(extcmd,
		       nfc->regs +
		       (cmd & NFC_ACCESS_DIR ?
			NFC_REG_WCMD_SET : NFC_REG_RCMD_SET));

	if (cmd & NFC_DATA_TRANS)
		writel(cnt, nfc->regs + NFC_REG_CNT);

	writel(cmd, nfc->regs + NFC_REG_CMD);

	ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG,
				    !(cmd & NFC_WAIT_FLAG) && cnt < 64,
				    0);
	if (ret)
		return ret;

	if (inbuf)
		memcpy_fromio(inbuf, nfc->regs + NFC_RAM0_BASE, cnt);

	return 0;
}

static int sunxi_nfc_soft_waitrdy(struct nand_chip *nand,
				  const struct nand_subop *subop)
{
	return nand_soft_waitrdy(nand,
				 subop->instrs[0].ctx.waitrdy.timeout_ms);
}

static const struct nand_op_parser sunxi_nfc_op_parser = NAND_OP_PARSER(
	NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
			       NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
			       NAND_OP_PARSER_PAT_WAITRDY_ELEM(true),
			       NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 1024)),
	NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
			       NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
			       NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, 1024),
			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
			       NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)),
);

static const struct nand_op_parser sunxi_nfc_norb_op_parser = NAND_OP_PARSER(
	NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
			       NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
			       NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 1024)),
	NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
			       NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
			       NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, 1024),
			       NAND_OP_PARSER_PAT_CMD_ELEM(true)),
	NAND_OP_PARSER_PATTERN(sunxi_nfc_soft_waitrdy,
			       NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
);

static int sunxi_nfc_exec_op(struct nand_chip *nand,
			     const struct nand_operation *op, bool check_only)
{
	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
	const struct nand_op_parser *parser;

	sunxi_nfc_select_chip(nand, op->cs);

	if (sunxi_nand->sels[op->cs].rb >= 0)
		parser = &sunxi_nfc_op_parser;
	else
		parser = &sunxi_nfc_norb_op_parser;

	return nand_op_parser_exec_op(nand, parser, op, check_only);
}

static const struct nand_controller_ops sunxi_nand_controller_ops = {
	.attach_chip = sunxi_nand_attach_chip,
	.setup_data_interface = sunxi_nfc_setup_data_interface,
	.exec_op = sunxi_nfc_exec_op,
};

static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
@@ -1853,7 +1914,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
	}

	sunxi_nand->nsels = nsels;
	sunxi_nand->selected = -1;

	for (i = 0; i < nsels; i++) {
		ret = of_property_read_u32_index(np, "reg", i, &tmp);
@@ -1886,7 +1946,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,

	nand = &sunxi_nand->nand;
	/* Default tR value specified in the ONFI spec (chapter 4.15.1) */
	nand->legacy.chip_delay = 200;
	nand->controller = &nfc->controller;
	nand->controller->ops = &sunxi_nand_controller_ops;

@@ -1896,11 +1955,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
	 */
	nand->ecc.mode = NAND_ECC_HW;
	nand_set_flash_node(nand, np);
	nand->legacy.select_chip = sunxi_nfc_select_chip;
	nand->legacy.cmd_ctrl = sunxi_nfc_cmd_ctrl;
	nand->legacy.read_buf = sunxi_nfc_read_buf;
	nand->legacy.write_buf = sunxi_nfc_write_buf;
	nand->legacy.read_byte = sunxi_nfc_read_byte;

	mtd = nand_to_mtd(nand);
	mtd->dev.parent = dev;