mtd: rawnand: atmel: Convert the driver to exec_op()
authorBoris Brezillon <boris.brezillon@collabora.com>
Mon, 20 Jul 2020 13:13:55 +0000 (16:13 +0300)
committerMiquel Raynal <miquel.raynal@bootlin.com>
Wed, 30 Sep 2020 14:44:15 +0000 (16:44 +0200)
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
drivers/mtd/nand/raw/atmel/nand-controller.c

index c8305be..3847f4a 100644 (file)
@@ -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 = {