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:
Boris Brezillon 2020-07-20 16:13:55 +03:00 committed by Miquel Raynal
parent 060c931c94
commit 03b3e0c27d
1 changed files with 267 additions and 0 deletions

View File

@ -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 = {