mtd: rawnand: gpio: Implement exec_op()
authorBoris Brezillon <boris.brezillon@collabora.com>
Wed, 3 Jun 2020 15:07:45 +0000 (17:07 +0200)
committerMiquel Raynal <miquel.raynal@bootlin.com>
Fri, 26 Jun 2020 06:35:09 +0000 (08:35 +0200)
Implement exec_op() so we can get rid of the legacy interface
implementation.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/20200603150746.1423257-3-boris.brezillon@collabora.com
drivers/mtd/nand/raw/gpio.c

index 33828fb..1159980 100644 (file)
 #include <linux/mtd/nand-gpio.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/delay.h>
 
 struct gpiomtd {
        struct nand_controller  base;
+       void __iomem            *io;
        void __iomem            *io_sync;
        struct nand_chip        nand_chip;
        struct gpio_nand_platdata plat;
@@ -98,6 +100,99 @@ static int gpio_nand_devready(struct nand_chip *chip)
        return gpiod_get_value(gpiomtd->rdy);
 }
 
+static int gpio_nand_exec_instr(struct nand_chip *chip,
+                               const struct nand_op_instr *instr)
+{
+       struct gpiomtd *gpiomtd = gpio_nand_getpriv(nand_to_mtd(chip));
+       unsigned int i;
+
+       switch (instr->type) {
+       case NAND_OP_CMD_INSTR:
+               gpio_nand_dosync(gpiomtd);
+               gpiod_set_value(gpiomtd->cle, 1);
+               gpio_nand_dosync(gpiomtd);
+               writeb(instr->ctx.cmd.opcode, gpiomtd->io);
+               gpio_nand_dosync(gpiomtd);
+               gpiod_set_value(gpiomtd->cle, 0);
+               return 0;
+
+       case NAND_OP_ADDR_INSTR:
+               gpio_nand_dosync(gpiomtd);
+               gpiod_set_value(gpiomtd->ale, 1);
+               gpio_nand_dosync(gpiomtd);
+               for (i = 0; i < instr->ctx.addr.naddrs; i++)
+                       writeb(instr->ctx.addr.addrs[i], gpiomtd->io);
+               gpio_nand_dosync(gpiomtd);
+               gpiod_set_value(gpiomtd->ale, 0);
+               return 0;
+
+       case NAND_OP_DATA_IN_INSTR:
+               gpio_nand_dosync(gpiomtd);
+               if ((chip->options & NAND_BUSWIDTH_16) &&
+                   !instr->ctx.data.force_8bit)
+                       ioread16_rep(gpiomtd->io, instr->ctx.data.buf.in,
+                                    instr->ctx.data.len / 2);
+               else
+                       ioread8_rep(gpiomtd->io, instr->ctx.data.buf.in,
+                                   instr->ctx.data.len);
+               return 0;
+
+       case NAND_OP_DATA_OUT_INSTR:
+               gpio_nand_dosync(gpiomtd);
+               if ((chip->options & NAND_BUSWIDTH_16) &&
+                   !instr->ctx.data.force_8bit)
+                       iowrite16_rep(gpiomtd->io, instr->ctx.data.buf.out,
+                                     instr->ctx.data.len / 2);
+               else
+                       iowrite8_rep(gpiomtd->io, instr->ctx.data.buf.out,
+                                    instr->ctx.data.len);
+               return 0;
+
+       case NAND_OP_WAITRDY_INSTR:
+               if (!gpiomtd->rdy)
+                       return nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms);
+
+               return nand_gpio_waitrdy(chip, gpiomtd->rdy,
+                                        instr->ctx.waitrdy.timeout_ms);
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int gpio_nand_exec_op(struct nand_chip *chip,
+                            const struct nand_operation *op,
+                            bool check_only)
+{
+       struct gpiomtd *gpiomtd = gpio_nand_getpriv(nand_to_mtd(chip));
+       unsigned int i;
+       int ret = 0;
+
+       if (check_only)
+               return 0;
+
+       gpio_nand_dosync(gpiomtd);
+       gpiod_set_value(gpiomtd->nce, 0);
+       for (i = 0; i < op->ninstrs; i++) {
+               ret = gpio_nand_exec_instr(chip, &op->instrs[i]);
+               if (ret)
+                       break;
+
+               if (op->instrs[i].delay_ns)
+                       ndelay(op->instrs[i].delay_ns);
+       }
+       gpio_nand_dosync(gpiomtd);
+       gpiod_set_value(gpiomtd->nce, 1);
+
+       return ret;
+}
+
+static const struct nand_controller_ops gpio_nand_ops = {
+       .exec_op = gpio_nand_exec_op,
+};
+
 #ifdef CONFIG_OF
 static const struct of_device_id gpio_nand_id_table[] = {
        { .compatible = "gpio-control-nand" },
@@ -226,9 +321,9 @@ static int gpio_nand_probe(struct platform_device *pdev)
        chip = &gpiomtd->nand_chip;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       chip->legacy.IO_ADDR_R = devm_ioremap_resource(dev, res);
-       if (IS_ERR(chip->legacy.IO_ADDR_R))
-               return PTR_ERR(chip->legacy.IO_ADDR_R);
+       gpiomtd->io = devm_ioremap_resource(dev, res);
+       if (IS_ERR(gpiomtd->io))
+               return PTR_ERR(gpiomtd->io);
 
        res = gpio_nand_get_io_sync(pdev);
        if (res) {
@@ -275,7 +370,10 @@ static int gpio_nand_probe(struct platform_device *pdev)
                chip->legacy.dev_ready = gpio_nand_devready;
 
        nand_controller_init(&gpiomtd->base);
+       gpiomtd->base.ops = &gpio_nand_ops;
+
        nand_set_flash_node(chip, pdev->dev.of_node);
+       chip->legacy.IO_ADDR_R  = gpiomtd->io;
        chip->legacy.IO_ADDR_W  = chip->legacy.IO_ADDR_R;
        chip->ecc.mode          = NAND_ECC_SOFT;
        chip->ecc.algo          = NAND_ECC_HAMMING;