static int rnandc_exec_op()

in nand/raw/renesas-nand-controller.c [682:887]


static int rnandc_exec_op(struct nand_chip *chip,
			  const struct nand_operation *op, bool check_only)
{
	struct rnandc *rnandc = to_rnandc(chip->controller);
	const struct nand_op_instr *instr = NULL;
	struct rnandc_op rop = {
		.command = COMMAND_INPUT_SEL_AHBS,
		.gen_seq_ctrl = GEN_SEQ_IMD_SEQ,
	};
	unsigned int cmd_phase = 0, addr_phase = 0, data_phase = 0,
		delay_phase = 0, delays = 0;
	unsigned int op_id, col_addrs, row_addrs, naddrs, remainder, words, i;
	const u8 *addrs;
	u32 last_bytes;
	int ret;

	if (!check_only)
		rnandc_select_target(chip, op->cs);

	for (op_id = 0; op_id < op->ninstrs; op_id++) {
		instr = &op->instrs[op_id];

		nand_op_trace("  ", instr);

		switch (instr->type) {
		case NAND_OP_CMD_INSTR:
			switch (cmd_phase++) {
			case 0:
				rop.command |= COMMAND_0(instr->ctx.cmd.opcode);
				rop.gen_seq_ctrl |= GEN_SEQ_CMD0_EN;
				break;
			case 1:
				rop.gen_seq_ctrl |= GEN_SEQ_COMMAND_3(instr->ctx.cmd.opcode);
				rop.gen_seq_ctrl |= GEN_SEQ_CMD3_EN;
				if (addr_phase == 0)
					addr_phase = 1;
				break;
			case 2:
				rop.command |= COMMAND_2(instr->ctx.cmd.opcode);
				rop.gen_seq_ctrl |= GEN_SEQ_CMD2_EN;
				if (addr_phase <= 1)
					addr_phase = 2;
				break;
			case 3:
				rop.command |= COMMAND_1(instr->ctx.cmd.opcode);
				rop.gen_seq_ctrl |= GEN_SEQ_CMD1_EN;
				if (addr_phase <= 1)
					addr_phase = 2;
				if (delay_phase == 0)
					delay_phase = 1;
				if (data_phase == 0)
					data_phase = 1;
				break;
			default:
				return -EOPNOTSUPP;
			}
			break;

		case NAND_OP_ADDR_INSTR:
			addrs = instr->ctx.addr.addrs;
			naddrs = instr->ctx.addr.naddrs;
			if (naddrs > 5)
				return -EOPNOTSUPP;

			col_addrs = min(2U, naddrs);
			row_addrs = naddrs > 2 ? naddrs - col_addrs : 0;

			switch (addr_phase++) {
			case 0:
				for (i = 0; i < col_addrs; i++)
					rop.addr0_col |= addrs[i] << (i * 8);
				rop.gen_seq_ctrl |= GEN_SEQ_COL_A0(col_addrs);

				for (i = 0; i < row_addrs; i++)
					rop.addr0_row |= addrs[2 + i] << (i * 8);
				rop.gen_seq_ctrl |= GEN_SEQ_ROW_A0(row_addrs);

				if (cmd_phase == 0)
					cmd_phase = 1;
				break;
			case 1:
				for (i = 0; i < col_addrs; i++)
					rop.addr1_col |= addrs[i] << (i * 8);
				rop.gen_seq_ctrl |= GEN_SEQ_COL_A1(col_addrs);

				for (i = 0; i < row_addrs; i++)
					rop.addr1_row |= addrs[2 + i] << (i * 8);
				rop.gen_seq_ctrl |= GEN_SEQ_ROW_A1(row_addrs);

				if (cmd_phase <= 1)
					cmd_phase = 2;
				break;
			default:
				return -EOPNOTSUPP;
			}
			break;

		case NAND_OP_DATA_IN_INSTR:
			rop.read = true;
			fallthrough;
		case NAND_OP_DATA_OUT_INSTR:
			rop.gen_seq_ctrl |= GEN_SEQ_DATA_EN;
			rop.buf = instr->ctx.data.buf.in;
			rop.len = instr->ctx.data.len;
			rop.command |= COMMAND_FIFO_SEL;

			switch (data_phase++) {
			case 0:
				if (cmd_phase <= 2)
					cmd_phase = 3;
				if (addr_phase <= 1)
					addr_phase = 2;
				if (delay_phase == 0)
					delay_phase = 1;
				break;
			default:
				return -EOPNOTSUPP;
			}
			break;

		case NAND_OP_WAITRDY_INSTR:
			switch (delay_phase++) {
			case 0:
				rop.gen_seq_ctrl |= GEN_SEQ_DELAY0_EN;

				if (cmd_phase <= 2)
					cmd_phase = 3;
				break;
			case 1:
				rop.gen_seq_ctrl |= GEN_SEQ_DELAY1_EN;

				if (cmd_phase <= 3)
					cmd_phase = 4;
				if (data_phase == 0)
					data_phase = 1;
				break;
			default:
				return -EOPNOTSUPP;
			}
			break;
		}
	}

	/*
	 * Sequence 19 is generic and dedicated to write operations.
	 * Sequence 18 is also generic and works for all other operations.
	 */
	if (rop.buf && !rop.read)
		rop.command |= COMMAND_SEQ_GEN_OUT;
	else
		rop.command |= COMMAND_SEQ_GEN_IN;

	if (delays > 1) {
		dev_err(rnandc->dev, "Cannot handle more than one wait delay\n");
		return -EOPNOTSUPP;
	}

	if (check_only)
		return 0;

	rnandc_trigger_op(rnandc, &rop);

	words = rop.len / sizeof(u32);
	remainder = rop.len % sizeof(u32);
	if (rop.buf && rop.read) {
		while (!FIFO_STATE_C_EMPTY(readl(rnandc->regs + FIFO_STATE_REG)))
			cpu_relax();

		while (FIFO_STATE_R_EMPTY(readl(rnandc->regs + FIFO_STATE_REG)))
			cpu_relax();

		ioread32_rep(rnandc->regs + FIFO_DATA_REG, rop.buf, words);
		if (remainder) {
			last_bytes = readl_relaxed(rnandc->regs + FIFO_DATA_REG);
			memcpy(rop.buf + (words * sizeof(u32)), &last_bytes,
			       remainder);
		}

		if (!FIFO_STATE_R_EMPTY(readl(rnandc->regs + FIFO_STATE_REG))) {
			dev_warn(rnandc->dev,
				 "Clearing residual data in the read FIFO\n");
			rnandc_clear_fifo(rnandc);
		}
	} else if (rop.len && !rop.read) {
		while (FIFO_STATE_W_FULL(readl(rnandc->regs + FIFO_STATE_REG)))
			cpu_relax();

		iowrite32_rep(rnandc->regs + FIFO_DATA_REG, rop.buf,
			      DIV_ROUND_UP(rop.len, 4));

		if (remainder) {
			last_bytes = 0;
			memcpy(&last_bytes, rop.buf + (words * sizeof(u32)), remainder);
			writel_relaxed(last_bytes, rnandc->regs + FIFO_DATA_REG);
		}

		while (!FIFO_STATE_W_EMPTY(readl(rnandc->regs + FIFO_STATE_REG)))
			cpu_relax();
	}

	ret = rnandc_wait_end_of_op(rnandc, chip);
	if (ret)
		return ret;

	return 0;
}