mtd: rawnand: atmel: Convert the driver to exec_op()
Both SMC and HSMC are converted to exec_op(). Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com> Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com> Tested-by: Tudor Ambarus <tudor.ambarus@microchip.com> Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Link: https://lore.kernel.org/linux-mtd/20200720131356.1579073-6-tudor.ambarus@microchip.com
This commit is contained in:
parent
060c931c94
commit
03b3e0c27d
|
@ -202,6 +202,8 @@ struct atmel_nand_controller_ops {
|
||||||
int (*ecc_init)(struct nand_chip *chip);
|
int (*ecc_init)(struct nand_chip *chip);
|
||||||
int (*setup_interface)(struct atmel_nand *nand, int csline,
|
int (*setup_interface)(struct atmel_nand *nand, int csline,
|
||||||
const struct nand_interface_config *conf);
|
const struct nand_interface_config *conf);
|
||||||
|
int (*exec_op)(struct atmel_nand *nand,
|
||||||
|
const struct nand_operation *op, bool check_only);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct atmel_nand_controller_caps {
|
struct atmel_nand_controller_caps {
|
||||||
|
@ -259,6 +261,7 @@ struct atmel_hsmc_nand_controller {
|
||||||
struct regmap *io;
|
struct regmap *io;
|
||||||
struct atmel_nfc_op op;
|
struct atmel_nfc_op op;
|
||||||
struct completion complete;
|
struct completion complete;
|
||||||
|
u32 cfg;
|
||||||
int irq;
|
int irq;
|
||||||
|
|
||||||
/* Only used when instantiating from legacy DT bindings. */
|
/* Only used when instantiating from legacy DT bindings. */
|
||||||
|
@ -638,6 +641,252 @@ static void atmel_nand_cmd_ctrl(struct nand_chip *chip, int cmd,
|
||||||
writeb(cmd, nand->activecs->io.virt + nc->caps->cle_offs);
|
writeb(cmd, nand->activecs->io.virt + nc->caps->cle_offs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void atmel_nand_data_in(struct atmel_nand *nand, void *buf,
|
||||||
|
unsigned int len, bool force_8bit)
|
||||||
|
{
|
||||||
|
struct atmel_nand_controller *nc;
|
||||||
|
|
||||||
|
nc = to_nand_controller(nand->base.controller);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the controller supports DMA, the buffer address is DMA-able and
|
||||||
|
* len is long enough to make DMA transfers profitable, let's trigger
|
||||||
|
* a DMA transfer. If it fails, fallback to PIO mode.
|
||||||
|
*/
|
||||||
|
if (nc->dmac && virt_addr_valid(buf) &&
|
||||||
|
len >= MIN_DMA_LEN && !force_8bit &&
|
||||||
|
!atmel_nand_dma_transfer(nc, buf, nand->activecs->io.dma, len,
|
||||||
|
DMA_FROM_DEVICE))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((nand->base.options & NAND_BUSWIDTH_16) && !force_8bit)
|
||||||
|
ioread16_rep(nand->activecs->io.virt, buf, len / 2);
|
||||||
|
else
|
||||||
|
ioread8_rep(nand->activecs->io.virt, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void atmel_nand_data_out(struct atmel_nand *nand, const void *buf,
|
||||||
|
unsigned int len, bool force_8bit)
|
||||||
|
{
|
||||||
|
struct atmel_nand_controller *nc;
|
||||||
|
|
||||||
|
nc = to_nand_controller(nand->base.controller);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the controller supports DMA, the buffer address is DMA-able and
|
||||||
|
* len is long enough to make DMA transfers profitable, let's trigger
|
||||||
|
* a DMA transfer. If it fails, fallback to PIO mode.
|
||||||
|
*/
|
||||||
|
if (nc->dmac && virt_addr_valid(buf) &&
|
||||||
|
len >= MIN_DMA_LEN && !force_8bit &&
|
||||||
|
!atmel_nand_dma_transfer(nc, (void *)buf, nand->activecs->io.dma,
|
||||||
|
len, DMA_TO_DEVICE))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((nand->base.options & NAND_BUSWIDTH_16) && !force_8bit)
|
||||||
|
iowrite16_rep(nand->activecs->io.virt, buf, len / 2);
|
||||||
|
else
|
||||||
|
iowrite8_rep(nand->activecs->io.virt, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int atmel_nand_waitrdy(struct atmel_nand *nand, unsigned int timeout_ms)
|
||||||
|
{
|
||||||
|
if (nand->activecs->rb.type == ATMEL_NAND_NO_RB)
|
||||||
|
return nand_soft_waitrdy(&nand->base, timeout_ms);
|
||||||
|
|
||||||
|
return nand_gpio_waitrdy(&nand->base, nand->activecs->rb.gpio,
|
||||||
|
timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int atmel_hsmc_nand_waitrdy(struct atmel_nand *nand,
|
||||||
|
unsigned int timeout_ms)
|
||||||
|
{
|
||||||
|
struct atmel_hsmc_nand_controller *nc;
|
||||||
|
u32 status, mask;
|
||||||
|
|
||||||
|
if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB)
|
||||||
|
return atmel_nand_waitrdy(nand, timeout_ms);
|
||||||
|
|
||||||
|
nc = to_hsmc_nand_controller(nand->base.controller);
|
||||||
|
mask = ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id);
|
||||||
|
return regmap_read_poll_timeout_atomic(nc->base.smc, ATMEL_HSMC_NFC_SR,
|
||||||
|
status, status & mask,
|
||||||
|
10, timeout_ms * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void atmel_nand_select_target(struct atmel_nand *nand,
|
||||||
|
unsigned int cs)
|
||||||
|
{
|
||||||
|
nand->activecs = &nand->cs[cs];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void atmel_hsmc_nand_select_target(struct atmel_nand *nand,
|
||||||
|
unsigned int cs)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = nand_to_mtd(&nand->base);
|
||||||
|
struct atmel_hsmc_nand_controller *nc;
|
||||||
|
u32 cfg = ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) |
|
||||||
|
ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) |
|
||||||
|
ATMEL_HSMC_NFC_CFG_RSPARE;
|
||||||
|
|
||||||
|
nand->activecs = &nand->cs[cs];
|
||||||
|
nc = to_hsmc_nand_controller(nand->base.controller);
|
||||||
|
if (nc->cfg == cfg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
regmap_update_bits(nc->base.smc, ATMEL_HSMC_NFC_CFG,
|
||||||
|
ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK |
|
||||||
|
ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK |
|
||||||
|
ATMEL_HSMC_NFC_CFG_RSPARE |
|
||||||
|
ATMEL_HSMC_NFC_CFG_WSPARE,
|
||||||
|
cfg);
|
||||||
|
nc->cfg = cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int atmel_smc_nand_exec_instr(struct atmel_nand *nand,
|
||||||
|
const struct nand_op_instr *instr)
|
||||||
|
{
|
||||||
|
struct atmel_nand_controller *nc;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
nc = to_nand_controller(nand->base.controller);
|
||||||
|
switch (instr->type) {
|
||||||
|
case NAND_OP_CMD_INSTR:
|
||||||
|
writeb(instr->ctx.cmd.opcode,
|
||||||
|
nand->activecs->io.virt + nc->caps->cle_offs);
|
||||||
|
return 0;
|
||||||
|
case NAND_OP_ADDR_INSTR:
|
||||||
|
for (i = 0; i < instr->ctx.addr.naddrs; i++)
|
||||||
|
writeb(instr->ctx.addr.addrs[i],
|
||||||
|
nand->activecs->io.virt + nc->caps->ale_offs);
|
||||||
|
return 0;
|
||||||
|
case NAND_OP_DATA_IN_INSTR:
|
||||||
|
atmel_nand_data_in(nand, instr->ctx.data.buf.in,
|
||||||
|
instr->ctx.data.len,
|
||||||
|
instr->ctx.data.force_8bit);
|
||||||
|
return 0;
|
||||||
|
case NAND_OP_DATA_OUT_INSTR:
|
||||||
|
atmel_nand_data_out(nand, instr->ctx.data.buf.out,
|
||||||
|
instr->ctx.data.len,
|
||||||
|
instr->ctx.data.force_8bit);
|
||||||
|
return 0;
|
||||||
|
case NAND_OP_WAITRDY_INSTR:
|
||||||
|
return atmel_nand_waitrdy(nand,
|
||||||
|
instr->ctx.waitrdy.timeout_ms);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int atmel_smc_nand_exec_op(struct atmel_nand *nand,
|
||||||
|
const struct nand_operation *op,
|
||||||
|
bool check_only)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (check_only)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
atmel_nand_select_target(nand, op->cs);
|
||||||
|
gpiod_set_value(nand->activecs->csgpio, 0);
|
||||||
|
for (i = 0; i < op->ninstrs; i++) {
|
||||||
|
ret = atmel_smc_nand_exec_instr(nand, &op->instrs[i]);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
gpiod_set_value(nand->activecs->csgpio, 1);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int atmel_hsmc_exec_cmd_addr(struct nand_chip *chip,
|
||||||
|
const struct nand_subop *subop)
|
||||||
|
{
|
||||||
|
struct atmel_nand *nand = to_atmel_nand(chip);
|
||||||
|
struct atmel_hsmc_nand_controller *nc;
|
||||||
|
unsigned int i, j;
|
||||||
|
|
||||||
|
nc = to_hsmc_nand_controller(chip->controller);
|
||||||
|
|
||||||
|
nc->op.cs = nand->activecs->id;
|
||||||
|
for (i = 0; i < subop->ninstrs; i++) {
|
||||||
|
const struct nand_op_instr *instr = &subop->instrs[i];
|
||||||
|
|
||||||
|
if (instr->type == NAND_OP_CMD_INSTR) {
|
||||||
|
nc->op.cmds[nc->op.ncmds++] = instr->ctx.cmd.opcode;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = nand_subop_get_addr_start_off(subop, i);
|
||||||
|
j < nand_subop_get_num_addr_cyc(subop, i); j++) {
|
||||||
|
nc->op.addrs[nc->op.naddrs] = instr->ctx.addr.addrs[j];
|
||||||
|
nc->op.naddrs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return atmel_nfc_exec_op(nc, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int atmel_hsmc_exec_rw(struct nand_chip *chip,
|
||||||
|
const struct nand_subop *subop)
|
||||||
|
{
|
||||||
|
const struct nand_op_instr *instr = subop->instrs;
|
||||||
|
struct atmel_nand *nand = to_atmel_nand(chip);
|
||||||
|
|
||||||
|
if (instr->type == NAND_OP_DATA_IN_INSTR)
|
||||||
|
atmel_nand_data_in(nand, instr->ctx.data.buf.in,
|
||||||
|
instr->ctx.data.len,
|
||||||
|
instr->ctx.data.force_8bit);
|
||||||
|
else
|
||||||
|
atmel_nand_data_out(nand, instr->ctx.data.buf.out,
|
||||||
|
instr->ctx.data.len,
|
||||||
|
instr->ctx.data.force_8bit);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int atmel_hsmc_exec_waitrdy(struct nand_chip *chip,
|
||||||
|
const struct nand_subop *subop)
|
||||||
|
{
|
||||||
|
const struct nand_op_instr *instr = subop->instrs;
|
||||||
|
struct atmel_nand *nand = to_atmel_nand(chip);
|
||||||
|
|
||||||
|
return atmel_hsmc_nand_waitrdy(nand, instr->ctx.waitrdy.timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct nand_op_parser atmel_hsmc_op_parser = NAND_OP_PARSER(
|
||||||
|
NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_cmd_addr,
|
||||||
|
NAND_OP_PARSER_PAT_CMD_ELEM(true),
|
||||||
|
NAND_OP_PARSER_PAT_ADDR_ELEM(true, 5),
|
||||||
|
NAND_OP_PARSER_PAT_CMD_ELEM(true)),
|
||||||
|
NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_rw,
|
||||||
|
NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 0)),
|
||||||
|
NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_rw,
|
||||||
|
NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, 0)),
|
||||||
|
NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_waitrdy,
|
||||||
|
NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
|
||||||
|
);
|
||||||
|
|
||||||
|
static int atmel_hsmc_nand_exec_op(struct atmel_nand *nand,
|
||||||
|
const struct nand_operation *op,
|
||||||
|
bool check_only)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (check_only)
|
||||||
|
return nand_op_parser_exec_op(&nand->base,
|
||||||
|
&atmel_hsmc_op_parser, op, true);
|
||||||
|
|
||||||
|
atmel_hsmc_nand_select_target(nand, op->cs);
|
||||||
|
ret = nand_op_parser_exec_op(&nand->base, &atmel_hsmc_op_parser, op,
|
||||||
|
false);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void atmel_nfc_copy_to_sram(struct nand_chip *chip, const u8 *buf,
|
static void atmel_nfc_copy_to_sram(struct nand_chip *chip, const u8 *buf,
|
||||||
bool oob_required)
|
bool oob_required)
|
||||||
{
|
{
|
||||||
|
@ -904,6 +1153,7 @@ static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip,
|
||||||
struct atmel_hsmc_nand_controller *nc;
|
struct atmel_hsmc_nand_controller *nc;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
atmel_hsmc_nand_select_target(nand, chip->cur_cs);
|
||||||
nc = to_hsmc_nand_controller(chip->controller);
|
nc = to_hsmc_nand_controller(chip->controller);
|
||||||
|
|
||||||
atmel_nfc_copy_to_sram(chip, buf, false);
|
atmel_nfc_copy_to_sram(chip, buf, false);
|
||||||
|
@ -964,6 +1214,7 @@ static int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf,
|
||||||
struct atmel_hsmc_nand_controller *nc;
|
struct atmel_hsmc_nand_controller *nc;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
atmel_hsmc_nand_select_target(nand, chip->cur_cs);
|
||||||
nc = to_hsmc_nand_controller(chip->controller);
|
nc = to_hsmc_nand_controller(chip->controller);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1450,6 +1701,18 @@ static int atmel_nand_setup_interface(struct nand_chip *chip, int csline,
|
||||||
return nc->caps->ops->setup_interface(nand, csline, conf);
|
return nc->caps->ops->setup_interface(nand, csline, conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int atmel_nand_exec_op(struct nand_chip *chip,
|
||||||
|
const struct nand_operation *op,
|
||||||
|
bool check_only)
|
||||||
|
{
|
||||||
|
struct atmel_nand *nand = to_atmel_nand(chip);
|
||||||
|
struct atmel_nand_controller *nc;
|
||||||
|
|
||||||
|
nc = to_nand_controller(nand->base.controller);
|
||||||
|
|
||||||
|
return nc->caps->ops->exec_op(nand, op, check_only);
|
||||||
|
}
|
||||||
|
|
||||||
static void atmel_nand_init(struct atmel_nand_controller *nc,
|
static void atmel_nand_init(struct atmel_nand_controller *nc,
|
||||||
struct atmel_nand *nand)
|
struct atmel_nand *nand)
|
||||||
{
|
{
|
||||||
|
@ -1940,6 +2203,7 @@ static int atmel_nand_attach_chip(struct nand_chip *chip)
|
||||||
static const struct nand_controller_ops atmel_nand_controller_ops = {
|
static const struct nand_controller_ops atmel_nand_controller_ops = {
|
||||||
.attach_chip = atmel_nand_attach_chip,
|
.attach_chip = atmel_nand_attach_chip,
|
||||||
.setup_interface = atmel_nand_setup_interface,
|
.setup_interface = atmel_nand_setup_interface,
|
||||||
|
.exec_op = atmel_nand_exec_op,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
|
static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
|
||||||
|
@ -2307,6 +2571,7 @@ static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = {
|
||||||
.ecc_init = atmel_hsmc_nand_ecc_init,
|
.ecc_init = atmel_hsmc_nand_ecc_init,
|
||||||
.nand_init = atmel_hsmc_nand_init,
|
.nand_init = atmel_hsmc_nand_init,
|
||||||
.setup_interface = atmel_hsmc_nand_setup_interface,
|
.setup_interface = atmel_hsmc_nand_setup_interface,
|
||||||
|
.exec_op = atmel_hsmc_nand_exec_op,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = {
|
static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = {
|
||||||
|
@ -2373,6 +2638,7 @@ static const struct atmel_nand_controller_ops at91rm9200_nc_ops = {
|
||||||
.remove = atmel_smc_nand_controller_remove,
|
.remove = atmel_smc_nand_controller_remove,
|
||||||
.ecc_init = atmel_nand_ecc_init,
|
.ecc_init = atmel_nand_ecc_init,
|
||||||
.nand_init = atmel_smc_nand_init,
|
.nand_init = atmel_smc_nand_init,
|
||||||
|
.exec_op = atmel_smc_nand_exec_op,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = {
|
static const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = {
|
||||||
|
@ -2388,6 +2654,7 @@ static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
|
||||||
.ecc_init = atmel_nand_ecc_init,
|
.ecc_init = atmel_nand_ecc_init,
|
||||||
.nand_init = atmel_smc_nand_init,
|
.nand_init = atmel_smc_nand_init,
|
||||||
.setup_interface = atmel_smc_nand_setup_interface,
|
.setup_interface = atmel_smc_nand_setup_interface,
|
||||||
|
.exec_op = atmel_smc_nand_exec_op,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = {
|
static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = {
|
||||||
|
|
Loading…
Reference in New Issue