|
25 | 25 | #include <linux/mtd/nand-gpio.h>
|
26 | 26 | #include <linux/of.h>
|
27 | 27 | #include <linux/of_address.h>
|
| 28 | +#include <linux/delay.h> |
28 | 29 |
|
29 | 30 | struct gpiomtd {
|
30 | 31 | struct nand_controller base;
|
| 32 | + void __iomem *io; |
31 | 33 | void __iomem *io_sync;
|
32 | 34 | struct nand_chip nand_chip;
|
33 | 35 | struct gpio_nand_platdata plat;
|
@@ -98,6 +100,99 @@ static int gpio_nand_devready(struct nand_chip *chip)
|
98 | 100 | return gpiod_get_value(gpiomtd->rdy);
|
99 | 101 | }
|
100 | 102 |
|
| 103 | +static int gpio_nand_exec_instr(struct nand_chip *chip, |
| 104 | + const struct nand_op_instr *instr) |
| 105 | +{ |
| 106 | + struct gpiomtd *gpiomtd = gpio_nand_getpriv(nand_to_mtd(chip)); |
| 107 | + unsigned int i; |
| 108 | + |
| 109 | + switch (instr->type) { |
| 110 | + case NAND_OP_CMD_INSTR: |
| 111 | + gpio_nand_dosync(gpiomtd); |
| 112 | + gpiod_set_value(gpiomtd->cle, 1); |
| 113 | + gpio_nand_dosync(gpiomtd); |
| 114 | + writeb(instr->ctx.cmd.opcode, gpiomtd->io); |
| 115 | + gpio_nand_dosync(gpiomtd); |
| 116 | + gpiod_set_value(gpiomtd->cle, 0); |
| 117 | + return 0; |
| 118 | + |
| 119 | + case NAND_OP_ADDR_INSTR: |
| 120 | + gpio_nand_dosync(gpiomtd); |
| 121 | + gpiod_set_value(gpiomtd->ale, 1); |
| 122 | + gpio_nand_dosync(gpiomtd); |
| 123 | + for (i = 0; i < instr->ctx.addr.naddrs; i++) |
| 124 | + writeb(instr->ctx.addr.addrs[i], gpiomtd->io); |
| 125 | + gpio_nand_dosync(gpiomtd); |
| 126 | + gpiod_set_value(gpiomtd->ale, 0); |
| 127 | + return 0; |
| 128 | + |
| 129 | + case NAND_OP_DATA_IN_INSTR: |
| 130 | + gpio_nand_dosync(gpiomtd); |
| 131 | + if ((chip->options & NAND_BUSWIDTH_16) && |
| 132 | + !instr->ctx.data.force_8bit) |
| 133 | + ioread16_rep(gpiomtd->io, instr->ctx.data.buf.in, |
| 134 | + instr->ctx.data.len / 2); |
| 135 | + else |
| 136 | + ioread8_rep(gpiomtd->io, instr->ctx.data.buf.in, |
| 137 | + instr->ctx.data.len); |
| 138 | + return 0; |
| 139 | + |
| 140 | + case NAND_OP_DATA_OUT_INSTR: |
| 141 | + gpio_nand_dosync(gpiomtd); |
| 142 | + if ((chip->options & NAND_BUSWIDTH_16) && |
| 143 | + !instr->ctx.data.force_8bit) |
| 144 | + iowrite16_rep(gpiomtd->io, instr->ctx.data.buf.out, |
| 145 | + instr->ctx.data.len / 2); |
| 146 | + else |
| 147 | + iowrite8_rep(gpiomtd->io, instr->ctx.data.buf.out, |
| 148 | + instr->ctx.data.len); |
| 149 | + return 0; |
| 150 | + |
| 151 | + case NAND_OP_WAITRDY_INSTR: |
| 152 | + if (!gpiomtd->rdy) |
| 153 | + return nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms); |
| 154 | + |
| 155 | + return nand_gpio_waitrdy(chip, gpiomtd->rdy, |
| 156 | + instr->ctx.waitrdy.timeout_ms); |
| 157 | + |
| 158 | + default: |
| 159 | + return -EINVAL; |
| 160 | + } |
| 161 | + |
| 162 | + return 0; |
| 163 | +} |
| 164 | + |
| 165 | +static int gpio_nand_exec_op(struct nand_chip *chip, |
| 166 | + const struct nand_operation *op, |
| 167 | + bool check_only) |
| 168 | +{ |
| 169 | + struct gpiomtd *gpiomtd = gpio_nand_getpriv(nand_to_mtd(chip)); |
| 170 | + unsigned int i; |
| 171 | + int ret = 0; |
| 172 | + |
| 173 | + if (check_only) |
| 174 | + return 0; |
| 175 | + |
| 176 | + gpio_nand_dosync(gpiomtd); |
| 177 | + gpiod_set_value(gpiomtd->nce, 0); |
| 178 | + for (i = 0; i < op->ninstrs; i++) { |
| 179 | + ret = gpio_nand_exec_instr(chip, &op->instrs[i]); |
| 180 | + if (ret) |
| 181 | + break; |
| 182 | + |
| 183 | + if (op->instrs[i].delay_ns) |
| 184 | + ndelay(op->instrs[i].delay_ns); |
| 185 | + } |
| 186 | + gpio_nand_dosync(gpiomtd); |
| 187 | + gpiod_set_value(gpiomtd->nce, 1); |
| 188 | + |
| 189 | + return ret; |
| 190 | +} |
| 191 | + |
| 192 | +static const struct nand_controller_ops gpio_nand_ops = { |
| 193 | + .exec_op = gpio_nand_exec_op, |
| 194 | +}; |
| 195 | + |
101 | 196 | #ifdef CONFIG_OF
|
102 | 197 | static const struct of_device_id gpio_nand_id_table[] = {
|
103 | 198 | { .compatible = "gpio-control-nand" },
|
@@ -226,9 +321,9 @@ static int gpio_nand_probe(struct platform_device *pdev)
|
226 | 321 | chip = &gpiomtd->nand_chip;
|
227 | 322 |
|
228 | 323 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
229 |
| - chip->legacy.IO_ADDR_R = devm_ioremap_resource(dev, res); |
230 |
| - if (IS_ERR(chip->legacy.IO_ADDR_R)) |
231 |
| - return PTR_ERR(chip->legacy.IO_ADDR_R); |
| 324 | + gpiomtd->io = devm_ioremap_resource(dev, res); |
| 325 | + if (IS_ERR(gpiomtd->io)) |
| 326 | + return PTR_ERR(gpiomtd->io); |
232 | 327 |
|
233 | 328 | res = gpio_nand_get_io_sync(pdev);
|
234 | 329 | if (res) {
|
@@ -275,7 +370,10 @@ static int gpio_nand_probe(struct platform_device *pdev)
|
275 | 370 | chip->legacy.dev_ready = gpio_nand_devready;
|
276 | 371 |
|
277 | 372 | nand_controller_init(&gpiomtd->base);
|
| 373 | + gpiomtd->base.ops = &gpio_nand_ops; |
| 374 | + |
278 | 375 | nand_set_flash_node(chip, pdev->dev.of_node);
|
| 376 | + chip->legacy.IO_ADDR_R = gpiomtd->io; |
279 | 377 | chip->legacy.IO_ADDR_W = chip->legacy.IO_ADDR_R;
|
280 | 378 | chip->ecc.mode = NAND_ECC_SOFT;
|
281 | 379 | chip->ecc.algo = NAND_ECC_HAMMING;
|
|
0 commit comments