in nand/raw/gpmi-nand/gpmi-nand.c [2250:2421]
static int gpmi_nfc_exec_op(struct nand_chip *chip,
const struct nand_operation *op,
bool check_only)
{
const struct nand_op_instr *instr;
struct gpmi_nand_data *this = nand_get_controller_data(chip);
struct dma_async_tx_descriptor *desc = NULL;
int i, ret, buf_len = 0, nbufs = 0;
u8 cmd = 0;
void *buf_read = NULL;
const void *buf_write = NULL;
bool direct = false;
struct completion *dma_completion, *bch_completion;
unsigned long to;
if (check_only)
return 0;
this->ntransfers = 0;
for (i = 0; i < GPMI_MAX_TRANSFERS; i++)
this->transfers[i].direction = DMA_NONE;
ret = pm_runtime_get_sync(this->dev);
if (ret < 0) {
pm_runtime_put_noidle(this->dev);
return ret;
}
/*
* This driver currently supports only one NAND chip. Plus, dies share
* the same configuration. So once timings have been applied on the
* controller side, they will not change anymore. When the time will
* come, the check on must_apply_timings will have to be dropped.
*/
if (this->hw.must_apply_timings) {
this->hw.must_apply_timings = false;
ret = gpmi_nfc_apply_timings(this);
if (ret)
return ret;
}
dev_dbg(this->dev, "%s: %d instructions\n", __func__, op->ninstrs);
for (i = 0; i < op->ninstrs; i++) {
instr = &op->instrs[i];
nand_op_trace(" ", instr);
switch (instr->type) {
case NAND_OP_WAITRDY_INSTR:
desc = gpmi_chain_wait_ready(this);
break;
case NAND_OP_CMD_INSTR:
cmd = instr->ctx.cmd.opcode;
/*
* When this command has an address cycle chain it
* together with the address cycle
*/
if (i + 1 != op->ninstrs &&
op->instrs[i + 1].type == NAND_OP_ADDR_INSTR)
continue;
desc = gpmi_chain_command(this, cmd, NULL, 0);
break;
case NAND_OP_ADDR_INSTR:
desc = gpmi_chain_command(this, cmd, instr->ctx.addr.addrs,
instr->ctx.addr.naddrs);
break;
case NAND_OP_DATA_OUT_INSTR:
buf_write = instr->ctx.data.buf.out;
buf_len = instr->ctx.data.len;
nbufs++;
desc = gpmi_chain_data_write(this, buf_write, buf_len);
break;
case NAND_OP_DATA_IN_INSTR:
if (!instr->ctx.data.len)
break;
buf_read = instr->ctx.data.buf.in;
buf_len = instr->ctx.data.len;
nbufs++;
desc = gpmi_chain_data_read(this, buf_read, buf_len,
&direct);
break;
}
if (!desc) {
ret = -ENXIO;
goto unmap;
}
}
dev_dbg(this->dev, "%s setup done\n", __func__);
if (nbufs > 1) {
dev_err(this->dev, "Multiple data instructions not supported\n");
ret = -EINVAL;
goto unmap;
}
if (this->bch) {
writel(this->bch_flashlayout0,
this->resources.bch_regs + HW_BCH_FLASH0LAYOUT0);
writel(this->bch_flashlayout1,
this->resources.bch_regs + HW_BCH_FLASH0LAYOUT1);
}
desc->callback = dma_irq_callback;
desc->callback_param = this;
dma_completion = &this->dma_done;
bch_completion = NULL;
init_completion(dma_completion);
if (this->bch && buf_read) {
writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
this->resources.bch_regs + HW_BCH_CTRL_SET);
bch_completion = &this->bch_done;
init_completion(bch_completion);
}
dmaengine_submit(desc);
dma_async_issue_pending(get_dma_chan(this));
to = wait_for_completion_timeout(dma_completion, msecs_to_jiffies(1000));
if (!to) {
dev_err(this->dev, "DMA timeout, last DMA\n");
gpmi_dump_info(this);
ret = -ETIMEDOUT;
goto unmap;
}
if (this->bch && buf_read) {
to = wait_for_completion_timeout(bch_completion, msecs_to_jiffies(1000));
if (!to) {
dev_err(this->dev, "BCH timeout, last DMA\n");
gpmi_dump_info(this);
ret = -ETIMEDOUT;
goto unmap;
}
}
writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
this->resources.bch_regs + HW_BCH_CTRL_CLR);
gpmi_clear_bch(this);
ret = 0;
unmap:
for (i = 0; i < this->ntransfers; i++) {
struct gpmi_transfer *transfer = &this->transfers[i];
if (transfer->direction != DMA_NONE)
dma_unmap_sg(this->dev, &transfer->sgl, 1,
transfer->direction);
}
if (!ret && buf_read && !direct)
memcpy(buf_read, this->data_buffer_dma,
gpmi_raw_len_to_len(this, buf_len));
this->bch = false;
pm_runtime_mark_last_busy(this->dev);
pm_runtime_put_autosuspend(this->dev);
return ret;
}