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