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:
Родитель
060c931c94
Коммит
03b3e0c27d
|
@ -202,6 +202,8 @@ struct atmel_nand_controller_ops {
|
|||
int (*ecc_init)(struct nand_chip *chip);
|
||||
int (*setup_interface)(struct atmel_nand *nand, int csline,
|
||||
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 {
|
||||
|
@ -259,6 +261,7 @@ struct atmel_hsmc_nand_controller {
|
|||
struct regmap *io;
|
||||
struct atmel_nfc_op op;
|
||||
struct completion complete;
|
||||
u32 cfg;
|
||||
int irq;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
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,
|
||||
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;
|
||||
int ret;
|
||||
|
||||
atmel_hsmc_nand_select_target(nand, chip->cur_cs);
|
||||
nc = to_hsmc_nand_controller(chip->controller);
|
||||
|
||||
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;
|
||||
int ret;
|
||||
|
||||
atmel_hsmc_nand_select_target(nand, chip->cur_cs);
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
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 = {
|
||||
.attach_chip = atmel_nand_attach_chip,
|
||||
.setup_interface = atmel_nand_setup_interface,
|
||||
.exec_op = atmel_nand_exec_op,
|
||||
};
|
||||
|
||||
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,
|
||||
.nand_init = atmel_hsmc_nand_init,
|
||||
.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 = {
|
||||
|
@ -2373,6 +2638,7 @@ static const struct atmel_nand_controller_ops at91rm9200_nc_ops = {
|
|||
.remove = atmel_smc_nand_controller_remove,
|
||||
.ecc_init = atmel_nand_ecc_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 = {
|
||||
|
@ -2388,6 +2654,7 @@ static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
|
|||
.ecc_init = atmel_nand_ecc_init,
|
||||
.nand_init = atmel_smc_nand_init,
|
||||
.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 = {
|
||||
|
|
Загрузка…
Ссылка в новой задаче