14857393dSBoris Brezillon // SPDX-License-Identifier: GPL-2.0
293db446aSBoris Brezillon /*
393db446aSBoris Brezillon * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
493db446aSBoris Brezillon *
593db446aSBoris Brezillon * Derived from drivers/mtd/nand/toto.c (removed in v2.6.28)
693db446aSBoris Brezillon * Copyright (c) 2003 Texas Instruments
793db446aSBoris Brezillon * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
893db446aSBoris Brezillon *
993db446aSBoris Brezillon * Converted to platform driver by Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
1093db446aSBoris Brezillon * Partially stolen from plat_nand.c
1193db446aSBoris Brezillon *
1293db446aSBoris Brezillon * Overview:
1393db446aSBoris Brezillon * This is a device driver for the NAND flash device found on the
1493db446aSBoris Brezillon * Amstrad E3 (Delta).
1593db446aSBoris Brezillon */
1693db446aSBoris Brezillon
1793db446aSBoris Brezillon #include <linux/slab.h>
1893db446aSBoris Brezillon #include <linux/module.h>
1993db446aSBoris Brezillon #include <linux/delay.h>
20f1a97e0bSJanusz Krzysztofik #include <linux/gpio/consumer.h>
2193db446aSBoris Brezillon #include <linux/mtd/mtd.h>
221698ea32SJanusz Krzysztofik #include <linux/mtd/nand-gpio.h>
2393db446aSBoris Brezillon #include <linux/mtd/rawnand.h>
2493db446aSBoris Brezillon #include <linux/mtd/partitions.h>
25*c2fc6b69SRob Herring #include <linux/of.h>
267416bd35SJanusz Krzysztofik #include <linux/platform_device.h>
27fbb080a1SBoris Brezillon #include <linux/sizes.h>
2893db446aSBoris Brezillon
2993db446aSBoris Brezillon /*
3093db446aSBoris Brezillon * MTD structure for E3 (Delta)
3193db446aSBoris Brezillon */
3216d00cd6SJanusz Krzysztofik struct gpio_nand {
339fd6bcffSBoris Brezillon struct nand_controller base;
342b44af3aSJanusz Krzysztofik struct nand_chip nand_chip;
352b44af3aSJanusz Krzysztofik struct gpio_desc *gpiod_rdy;
362b44af3aSJanusz Krzysztofik struct gpio_desc *gpiod_nce;
372b44af3aSJanusz Krzysztofik struct gpio_desc *gpiod_nre;
382b44af3aSJanusz Krzysztofik struct gpio_desc *gpiod_nwp;
392b44af3aSJanusz Krzysztofik struct gpio_desc *gpiod_nwe;
402b44af3aSJanusz Krzysztofik struct gpio_desc *gpiod_ale;
412b44af3aSJanusz Krzysztofik struct gpio_desc *gpiod_cle;
427416bd35SJanusz Krzysztofik struct gpio_descs *data_gpiods;
439c076d7eSJanusz Krzysztofik bool data_in;
44ccada49bSJanusz Krzysztofik unsigned int tRP;
45ccada49bSJanusz Krzysztofik unsigned int tWP;
4616d00cd6SJanusz Krzysztofik u8 (*io_read)(struct gpio_nand *this);
4716d00cd6SJanusz Krzysztofik void (*io_write)(struct gpio_nand *this, u8 byte);
482b44af3aSJanusz Krzysztofik };
4993db446aSBoris Brezillon
gpio_nand_write_commit(struct gpio_nand * priv)5016d00cd6SJanusz Krzysztofik static void gpio_nand_write_commit(struct gpio_nand *priv)
5193db446aSBoris Brezillon {
522b44af3aSJanusz Krzysztofik gpiod_set_value(priv->gpiod_nwe, 1);
53ccada49bSJanusz Krzysztofik ndelay(priv->tWP);
54241008edSJanusz Krzysztofik gpiod_set_value(priv->gpiod_nwe, 0);
5593db446aSBoris Brezillon }
5693db446aSBoris Brezillon
gpio_nand_io_write(struct gpio_nand * priv,u8 byte)5716d00cd6SJanusz Krzysztofik static void gpio_nand_io_write(struct gpio_nand *priv, u8 byte)
587416bd35SJanusz Krzysztofik {
597416bd35SJanusz Krzysztofik struct gpio_descs *data_gpiods = priv->data_gpiods;
607416bd35SJanusz Krzysztofik DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, };
617416bd35SJanusz Krzysztofik
627416bd35SJanusz Krzysztofik gpiod_set_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
637416bd35SJanusz Krzysztofik data_gpiods->info, values);
647416bd35SJanusz Krzysztofik
6516d00cd6SJanusz Krzysztofik gpio_nand_write_commit(priv);
667416bd35SJanusz Krzysztofik }
677416bd35SJanusz Krzysztofik
gpio_nand_dir_output(struct gpio_nand * priv,u8 byte)6816d00cd6SJanusz Krzysztofik static void gpio_nand_dir_output(struct gpio_nand *priv, u8 byte)
697416bd35SJanusz Krzysztofik {
707416bd35SJanusz Krzysztofik struct gpio_descs *data_gpiods = priv->data_gpiods;
717416bd35SJanusz Krzysztofik DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, };
727416bd35SJanusz Krzysztofik int i;
737416bd35SJanusz Krzysztofik
747416bd35SJanusz Krzysztofik for (i = 0; i < data_gpiods->ndescs; i++)
757416bd35SJanusz Krzysztofik gpiod_direction_output_raw(data_gpiods->desc[i],
767416bd35SJanusz Krzysztofik test_bit(i, values));
777416bd35SJanusz Krzysztofik
7816d00cd6SJanusz Krzysztofik gpio_nand_write_commit(priv);
797416bd35SJanusz Krzysztofik
807416bd35SJanusz Krzysztofik priv->data_in = false;
817416bd35SJanusz Krzysztofik }
827416bd35SJanusz Krzysztofik
gpio_nand_io_read(struct gpio_nand * priv)8316d00cd6SJanusz Krzysztofik static u8 gpio_nand_io_read(struct gpio_nand *priv)
8493db446aSBoris Brezillon {
85d54445d6SBoris Brezillon u8 res;
867416bd35SJanusz Krzysztofik struct gpio_descs *data_gpiods = priv->data_gpiods;
877416bd35SJanusz Krzysztofik DECLARE_BITMAP(values, BITS_PER_TYPE(res)) = { 0, };
8893db446aSBoris Brezillon
89241008edSJanusz Krzysztofik gpiod_set_value(priv->gpiod_nre, 1);
90ccada49bSJanusz Krzysztofik ndelay(priv->tRP);
917416bd35SJanusz Krzysztofik
927416bd35SJanusz Krzysztofik gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
937416bd35SJanusz Krzysztofik data_gpiods->info, values);
947416bd35SJanusz Krzysztofik
95241008edSJanusz Krzysztofik gpiod_set_value(priv->gpiod_nre, 0);
9693db446aSBoris Brezillon
977416bd35SJanusz Krzysztofik res = values[0];
9893db446aSBoris Brezillon return res;
9993db446aSBoris Brezillon }
10093db446aSBoris Brezillon
gpio_nand_dir_input(struct gpio_nand * priv)10116d00cd6SJanusz Krzysztofik static void gpio_nand_dir_input(struct gpio_nand *priv)
1029c076d7eSJanusz Krzysztofik {
1037416bd35SJanusz Krzysztofik struct gpio_descs *data_gpiods = priv->data_gpiods;
1047416bd35SJanusz Krzysztofik int i;
1057416bd35SJanusz Krzysztofik
1067416bd35SJanusz Krzysztofik for (i = 0; i < data_gpiods->ndescs; i++)
1077416bd35SJanusz Krzysztofik gpiod_direction_input(data_gpiods->desc[i]);
1087416bd35SJanusz Krzysztofik
1097416bd35SJanusz Krzysztofik priv->data_in = true;
1109c076d7eSJanusz Krzysztofik }
1119c076d7eSJanusz Krzysztofik
gpio_nand_write_buf(struct gpio_nand * priv,const u8 * buf,int len)11216d00cd6SJanusz Krzysztofik static void gpio_nand_write_buf(struct gpio_nand *priv, const u8 *buf, int len)
11393db446aSBoris Brezillon {
1147416bd35SJanusz Krzysztofik int i = 0;
11593db446aSBoris Brezillon
1167416bd35SJanusz Krzysztofik if (len > 0 && priv->data_in)
11716d00cd6SJanusz Krzysztofik gpio_nand_dir_output(priv, buf[i++]);
1189c076d7eSJanusz Krzysztofik
1197416bd35SJanusz Krzysztofik while (i < len)
1202b1dcee3SJanusz Krzysztofik priv->io_write(priv, buf[i++]);
12193db446aSBoris Brezillon }
12293db446aSBoris Brezillon
gpio_nand_read_buf(struct gpio_nand * priv,u8 * buf,int len)12316d00cd6SJanusz Krzysztofik static void gpio_nand_read_buf(struct gpio_nand *priv, u8 *buf, int len)
12493db446aSBoris Brezillon {
12593db446aSBoris Brezillon int i;
12693db446aSBoris Brezillon
1272b1dcee3SJanusz Krzysztofik if (priv->data_gpiods && !priv->data_in)
12816d00cd6SJanusz Krzysztofik gpio_nand_dir_input(priv);
1299c076d7eSJanusz Krzysztofik
13093db446aSBoris Brezillon for (i = 0; i < len; i++)
1312b1dcee3SJanusz Krzysztofik buf[i] = priv->io_read(priv);
1329c076d7eSJanusz Krzysztofik }
1339c076d7eSJanusz Krzysztofik
gpio_nand_ctrl_cs(struct gpio_nand * priv,bool assert)13416d00cd6SJanusz Krzysztofik static void gpio_nand_ctrl_cs(struct gpio_nand *priv, bool assert)
13593db446aSBoris Brezillon {
136241008edSJanusz Krzysztofik gpiod_set_value(priv->gpiod_nce, assert);
13793db446aSBoris Brezillon }
13893db446aSBoris Brezillon
gpio_nand_exec_op(struct nand_chip * this,const struct nand_operation * op,bool check_only)13916d00cd6SJanusz Krzysztofik static int gpio_nand_exec_op(struct nand_chip *this,
140861fbd6eSJanusz Krzysztofik const struct nand_operation *op, bool check_only)
14193db446aSBoris Brezillon {
14216d00cd6SJanusz Krzysztofik struct gpio_nand *priv = nand_get_controller_data(this);
143861fbd6eSJanusz Krzysztofik const struct nand_op_instr *instr;
144861fbd6eSJanusz Krzysztofik int ret = 0;
1452b44af3aSJanusz Krzysztofik
146861fbd6eSJanusz Krzysztofik if (check_only)
147861fbd6eSJanusz Krzysztofik return 0;
148861fbd6eSJanusz Krzysztofik
14916d00cd6SJanusz Krzysztofik gpio_nand_ctrl_cs(priv, 1);
1501770022fSBoris Brezillon
151861fbd6eSJanusz Krzysztofik for (instr = op->instrs; instr < op->instrs + op->ninstrs; instr++) {
152861fbd6eSJanusz Krzysztofik switch (instr->type) {
153861fbd6eSJanusz Krzysztofik case NAND_OP_CMD_INSTR:
154861fbd6eSJanusz Krzysztofik gpiod_set_value(priv->gpiod_cle, 1);
15516d00cd6SJanusz Krzysztofik gpio_nand_write_buf(priv, &instr->ctx.cmd.opcode, 1);
156861fbd6eSJanusz Krzysztofik gpiod_set_value(priv->gpiod_cle, 0);
157861fbd6eSJanusz Krzysztofik break;
158861fbd6eSJanusz Krzysztofik
159861fbd6eSJanusz Krzysztofik case NAND_OP_ADDR_INSTR:
160861fbd6eSJanusz Krzysztofik gpiod_set_value(priv->gpiod_ale, 1);
16116d00cd6SJanusz Krzysztofik gpio_nand_write_buf(priv, instr->ctx.addr.addrs,
162861fbd6eSJanusz Krzysztofik instr->ctx.addr.naddrs);
163861fbd6eSJanusz Krzysztofik gpiod_set_value(priv->gpiod_ale, 0);
164861fbd6eSJanusz Krzysztofik break;
165861fbd6eSJanusz Krzysztofik
166861fbd6eSJanusz Krzysztofik case NAND_OP_DATA_IN_INSTR:
16716d00cd6SJanusz Krzysztofik gpio_nand_read_buf(priv, instr->ctx.data.buf.in,
168861fbd6eSJanusz Krzysztofik instr->ctx.data.len);
169861fbd6eSJanusz Krzysztofik break;
170861fbd6eSJanusz Krzysztofik
171861fbd6eSJanusz Krzysztofik case NAND_OP_DATA_OUT_INSTR:
17216d00cd6SJanusz Krzysztofik gpio_nand_write_buf(priv, instr->ctx.data.buf.out,
173861fbd6eSJanusz Krzysztofik instr->ctx.data.len);
174861fbd6eSJanusz Krzysztofik break;
175861fbd6eSJanusz Krzysztofik
176861fbd6eSJanusz Krzysztofik case NAND_OP_WAITRDY_INSTR:
177861fbd6eSJanusz Krzysztofik ret = priv->gpiod_rdy ?
178861fbd6eSJanusz Krzysztofik nand_gpio_waitrdy(this, priv->gpiod_rdy,
179861fbd6eSJanusz Krzysztofik instr->ctx.waitrdy.timeout_ms) :
180861fbd6eSJanusz Krzysztofik nand_soft_waitrdy(this,
181861fbd6eSJanusz Krzysztofik instr->ctx.waitrdy.timeout_ms);
182861fbd6eSJanusz Krzysztofik break;
183861fbd6eSJanusz Krzysztofik }
184861fbd6eSJanusz Krzysztofik
185861fbd6eSJanusz Krzysztofik if (ret)
186861fbd6eSJanusz Krzysztofik break;
187861fbd6eSJanusz Krzysztofik }
188861fbd6eSJanusz Krzysztofik
18916d00cd6SJanusz Krzysztofik gpio_nand_ctrl_cs(priv, 0);
1901770022fSBoris Brezillon
191861fbd6eSJanusz Krzysztofik return ret;
19293db446aSBoris Brezillon }
19393db446aSBoris Brezillon
gpio_nand_setup_interface(struct nand_chip * this,int csline,const struct nand_interface_config * cf)1944c46667bSMiquel Raynal static int gpio_nand_setup_interface(struct nand_chip *this, int csline,
1954c46667bSMiquel Raynal const struct nand_interface_config *cf)
196ccada49bSJanusz Krzysztofik {
19716d00cd6SJanusz Krzysztofik struct gpio_nand *priv = nand_get_controller_data(this);
198ccada49bSJanusz Krzysztofik const struct nand_sdr_timings *sdr = nand_get_sdr_timings(cf);
199ccada49bSJanusz Krzysztofik struct device *dev = &nand_to_mtd(this)->dev;
200ccada49bSJanusz Krzysztofik
201ccada49bSJanusz Krzysztofik if (IS_ERR(sdr))
202ccada49bSJanusz Krzysztofik return PTR_ERR(sdr);
203ccada49bSJanusz Krzysztofik
204ccada49bSJanusz Krzysztofik if (csline == NAND_DATA_IFACE_CHECK_ONLY)
205ccada49bSJanusz Krzysztofik return 0;
206ccada49bSJanusz Krzysztofik
207586a746bSJanusz Krzysztofik if (priv->gpiod_nre) {
208ccada49bSJanusz Krzysztofik priv->tRP = DIV_ROUND_UP(sdr->tRP_min, 1000);
209ccada49bSJanusz Krzysztofik dev_dbg(dev, "using %u ns read pulse width\n", priv->tRP);
210586a746bSJanusz Krzysztofik }
211ccada49bSJanusz Krzysztofik
212ccada49bSJanusz Krzysztofik priv->tWP = DIV_ROUND_UP(sdr->tWP_min, 1000);
213ccada49bSJanusz Krzysztofik dev_dbg(dev, "using %u ns write pulse width\n", priv->tWP);
214ccada49bSJanusz Krzysztofik
215ccada49bSJanusz Krzysztofik return 0;
216ccada49bSJanusz Krzysztofik }
217ccada49bSJanusz Krzysztofik
gpio_nand_attach_chip(struct nand_chip * chip)21859d93473SMiquel Raynal static int gpio_nand_attach_chip(struct nand_chip *chip)
21959d93473SMiquel Raynal {
220d707bb74SMiquel Raynal if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT &&
221d707bb74SMiquel Raynal chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN)
22259d93473SMiquel Raynal chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
22359d93473SMiquel Raynal
22459d93473SMiquel Raynal return 0;
22559d93473SMiquel Raynal }
22659d93473SMiquel Raynal
22716d00cd6SJanusz Krzysztofik static const struct nand_controller_ops gpio_nand_ops = {
22816d00cd6SJanusz Krzysztofik .exec_op = gpio_nand_exec_op,
22959d93473SMiquel Raynal .attach_chip = gpio_nand_attach_chip,
2304c46667bSMiquel Raynal .setup_interface = gpio_nand_setup_interface,
231f2abfeb2SBoris Brezillon };
232f2abfeb2SBoris Brezillon
23393db446aSBoris Brezillon /*
23493db446aSBoris Brezillon * Main initialization routine
23593db446aSBoris Brezillon */
gpio_nand_probe(struct platform_device * pdev)23616d00cd6SJanusz Krzysztofik static int gpio_nand_probe(struct platform_device *pdev)
23793db446aSBoris Brezillon {
2381698ea32SJanusz Krzysztofik struct gpio_nand_platdata *pdata = dev_get_platdata(&pdev->dev);
239d7ffe387SJanusz Krzysztofik const struct mtd_partition *partitions = NULL;
240d7ffe387SJanusz Krzysztofik int num_partitions = 0;
24116d00cd6SJanusz Krzysztofik struct gpio_nand *priv;
24293db446aSBoris Brezillon struct nand_chip *this;
2432b44af3aSJanusz Krzysztofik struct mtd_info *mtd;
24416d00cd6SJanusz Krzysztofik int (*probe)(struct platform_device *pdev, struct gpio_nand *priv);
24593db446aSBoris Brezillon int err = 0;
24693db446aSBoris Brezillon
2471698ea32SJanusz Krzysztofik if (pdata) {
2481698ea32SJanusz Krzysztofik partitions = pdata->parts;
2491698ea32SJanusz Krzysztofik num_partitions = pdata->num_parts;
2501698ea32SJanusz Krzysztofik }
2511698ea32SJanusz Krzysztofik
25293db446aSBoris Brezillon /* Allocate memory for MTD device structure and private data */
25316d00cd6SJanusz Krzysztofik priv = devm_kzalloc(&pdev->dev, sizeof(struct gpio_nand),
2542b44af3aSJanusz Krzysztofik GFP_KERNEL);
255d54445d6SBoris Brezillon if (!priv)
2562b44af3aSJanusz Krzysztofik return -ENOMEM;
257d54445d6SBoris Brezillon
2582b44af3aSJanusz Krzysztofik this = &priv->nand_chip;
25993db446aSBoris Brezillon
2602b44af3aSJanusz Krzysztofik mtd = nand_to_mtd(this);
2612b44af3aSJanusz Krzysztofik mtd->dev.parent = &pdev->dev;
26293db446aSBoris Brezillon
2632b44af3aSJanusz Krzysztofik nand_set_controller_data(this, priv);
2642cef3d4cSJanusz Krzysztofik nand_set_flash_node(this, pdev->dev.of_node);
26593db446aSBoris Brezillon
2662b44af3aSJanusz Krzysztofik priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
2672b44af3aSJanusz Krzysztofik if (IS_ERR(priv->gpiod_rdy)) {
2682b44af3aSJanusz Krzysztofik err = PTR_ERR(priv->gpiod_rdy);
269f1a97e0bSJanusz Krzysztofik dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
2707416bd35SJanusz Krzysztofik return err;
27193db446aSBoris Brezillon }
272f1a97e0bSJanusz Krzysztofik
2732b44af3aSJanusz Krzysztofik platform_set_drvdata(pdev, priv);
27493db446aSBoris Brezillon
27591a1abfbSJanusz Krzysztofik /* Set chip enabled but write protected */
276ea5ea9faSJanusz Krzysztofik priv->gpiod_nwp = devm_gpiod_get_optional(&pdev->dev, "nwp",
277ea5ea9faSJanusz Krzysztofik GPIOD_OUT_HIGH);
2782b44af3aSJanusz Krzysztofik if (IS_ERR(priv->gpiod_nwp)) {
2792b44af3aSJanusz Krzysztofik err = PTR_ERR(priv->gpiod_nwp);
280f1a97e0bSJanusz Krzysztofik dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
2817416bd35SJanusz Krzysztofik return err;
282f1a97e0bSJanusz Krzysztofik }
283f1a97e0bSJanusz Krzysztofik
284ea5ea9faSJanusz Krzysztofik priv->gpiod_nce = devm_gpiod_get_optional(&pdev->dev, "nce",
285ea5ea9faSJanusz Krzysztofik GPIOD_OUT_LOW);
2862b44af3aSJanusz Krzysztofik if (IS_ERR(priv->gpiod_nce)) {
2872b44af3aSJanusz Krzysztofik err = PTR_ERR(priv->gpiod_nce);
288f1a97e0bSJanusz Krzysztofik dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
2897416bd35SJanusz Krzysztofik return err;
290f1a97e0bSJanusz Krzysztofik }
291f1a97e0bSJanusz Krzysztofik
292586a746bSJanusz Krzysztofik priv->gpiod_nre = devm_gpiod_get_optional(&pdev->dev, "nre",
293586a746bSJanusz Krzysztofik GPIOD_OUT_LOW);
2942b44af3aSJanusz Krzysztofik if (IS_ERR(priv->gpiod_nre)) {
2952b44af3aSJanusz Krzysztofik err = PTR_ERR(priv->gpiod_nre);
296f1a97e0bSJanusz Krzysztofik dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
2977416bd35SJanusz Krzysztofik return err;
298f1a97e0bSJanusz Krzysztofik }
299f1a97e0bSJanusz Krzysztofik
3002b1dcee3SJanusz Krzysztofik priv->gpiod_nwe = devm_gpiod_get_optional(&pdev->dev, "nwe",
3012b1dcee3SJanusz Krzysztofik GPIOD_OUT_LOW);
3022b44af3aSJanusz Krzysztofik if (IS_ERR(priv->gpiod_nwe)) {
3032b44af3aSJanusz Krzysztofik err = PTR_ERR(priv->gpiod_nwe);
304f1a97e0bSJanusz Krzysztofik dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
3057416bd35SJanusz Krzysztofik return err;
306f1a97e0bSJanusz Krzysztofik }
307f1a97e0bSJanusz Krzysztofik
3082b44af3aSJanusz Krzysztofik priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
3092b44af3aSJanusz Krzysztofik if (IS_ERR(priv->gpiod_ale)) {
3102b44af3aSJanusz Krzysztofik err = PTR_ERR(priv->gpiod_ale);
311f1a97e0bSJanusz Krzysztofik dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
3127416bd35SJanusz Krzysztofik return err;
313f1a97e0bSJanusz Krzysztofik }
314f1a97e0bSJanusz Krzysztofik
3152b44af3aSJanusz Krzysztofik priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
3162b44af3aSJanusz Krzysztofik if (IS_ERR(priv->gpiod_cle)) {
3172b44af3aSJanusz Krzysztofik err = PTR_ERR(priv->gpiod_cle);
318f1a97e0bSJanusz Krzysztofik dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
3197416bd35SJanusz Krzysztofik return err;
320f1a97e0bSJanusz Krzysztofik }
32193db446aSBoris Brezillon
32297738613SJanusz Krzysztofik /* Request array of data pins, initialize them as input */
3232b1dcee3SJanusz Krzysztofik priv->data_gpiods = devm_gpiod_get_array_optional(&pdev->dev, "data",
3242b1dcee3SJanusz Krzysztofik GPIOD_IN);
325edfd8d9cSJanusz Krzysztofik if (IS_ERR(priv->data_gpiods)) {
326edfd8d9cSJanusz Krzysztofik err = PTR_ERR(priv->data_gpiods);
32797738613SJanusz Krzysztofik dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
3287416bd35SJanusz Krzysztofik return err;
32997738613SJanusz Krzysztofik }
3302b1dcee3SJanusz Krzysztofik if (priv->data_gpiods) {
3312b1dcee3SJanusz Krzysztofik if (!priv->gpiod_nwe) {
3322b1dcee3SJanusz Krzysztofik dev_err(&pdev->dev,
3332b1dcee3SJanusz Krzysztofik "mandatory NWE pin not provided by platform\n");
3342b1dcee3SJanusz Krzysztofik return -ENODEV;
3352b1dcee3SJanusz Krzysztofik }
3362b1dcee3SJanusz Krzysztofik
33716d00cd6SJanusz Krzysztofik priv->io_read = gpio_nand_io_read;
33816d00cd6SJanusz Krzysztofik priv->io_write = gpio_nand_io_write;
33997738613SJanusz Krzysztofik priv->data_in = true;
3402b1dcee3SJanusz Krzysztofik }
3419c076d7eSJanusz Krzysztofik
342d1b1a8f7SJanusz Krzysztofik if (pdev->id_entry)
343d1b1a8f7SJanusz Krzysztofik probe = (void *) pdev->id_entry->driver_data;
344d1b1a8f7SJanusz Krzysztofik else
345d1b1a8f7SJanusz Krzysztofik probe = of_device_get_match_data(&pdev->dev);
346d1b1a8f7SJanusz Krzysztofik if (probe)
347d1b1a8f7SJanusz Krzysztofik err = probe(pdev, priv);
348d1b1a8f7SJanusz Krzysztofik if (err)
349d1b1a8f7SJanusz Krzysztofik return err;
350d1b1a8f7SJanusz Krzysztofik
3512b1dcee3SJanusz Krzysztofik if (!priv->io_read || !priv->io_write) {
3522b1dcee3SJanusz Krzysztofik dev_err(&pdev->dev, "incomplete device configuration\n");
3532b1dcee3SJanusz Krzysztofik return -ENODEV;
3542b1dcee3SJanusz Krzysztofik }
3552b1dcee3SJanusz Krzysztofik
35616d00cd6SJanusz Krzysztofik /* Initialize the NAND controller object embedded in gpio_nand. */
35716d00cd6SJanusz Krzysztofik priv->base.ops = &gpio_nand_ops;
3589fd6bcffSBoris Brezillon nand_controller_init(&priv->base);
3599fd6bcffSBoris Brezillon this->controller = &priv->base;
3609fd6bcffSBoris Brezillon
36191a1abfbSJanusz Krzysztofik /*
36291a1abfbSJanusz Krzysztofik * FIXME: We should release write protection only after nand_scan() to
36391a1abfbSJanusz Krzysztofik * be on the safe side but we can't do that until we have a generic way
36491a1abfbSJanusz Krzysztofik * to assert/deassert WP from the core. Even if the core shouldn't
36591a1abfbSJanusz Krzysztofik * write things in the nand_scan() path, it should have control on this
36691a1abfbSJanusz Krzysztofik * pin just in case we ever need to disable write protection during
36791a1abfbSJanusz Krzysztofik * chip detection/initialization.
36891a1abfbSJanusz Krzysztofik */
36991a1abfbSJanusz Krzysztofik /* Release write protection */
370241008edSJanusz Krzysztofik gpiod_set_value(priv->gpiod_nwp, 0);
37191a1abfbSJanusz Krzysztofik
372d707bb74SMiquel Raynal /*
373d707bb74SMiquel Raynal * This driver assumes that the default ECC engine should be TYPE_SOFT.
374d707bb74SMiquel Raynal * Set ->engine_type before registering the NAND devices in order to
375d707bb74SMiquel Raynal * provide a driver specific default value.
376d707bb74SMiquel Raynal */
377d707bb74SMiquel Raynal this->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
378d707bb74SMiquel Raynal
37993db446aSBoris Brezillon /* Scan to find existence of the device */
38000ad378fSBoris Brezillon err = nand_scan(this, 1);
38193db446aSBoris Brezillon if (err)
3827416bd35SJanusz Krzysztofik return err;
38393db446aSBoris Brezillon
38493db446aSBoris Brezillon /* Register the partitions */
3851698ea32SJanusz Krzysztofik err = mtd_device_register(mtd, partitions, num_partitions);
386876ba603SBoris Brezillon if (err)
387876ba603SBoris Brezillon goto err_nand_cleanup;
38893db446aSBoris Brezillon
3898bbc3c08SBoris Brezillon return 0;
39093db446aSBoris Brezillon
391876ba603SBoris Brezillon err_nand_cleanup:
392876ba603SBoris Brezillon nand_cleanup(this);
393876ba603SBoris Brezillon
39493db446aSBoris Brezillon return err;
39593db446aSBoris Brezillon }
39693db446aSBoris Brezillon
39793db446aSBoris Brezillon /*
39893db446aSBoris Brezillon * Clean up routine
39993db446aSBoris Brezillon */
gpio_nand_remove(struct platform_device * pdev)400ec185b18SUwe Kleine-König static void gpio_nand_remove(struct platform_device *pdev)
40193db446aSBoris Brezillon {
40216d00cd6SJanusz Krzysztofik struct gpio_nand *priv = platform_get_drvdata(pdev);
4032b44af3aSJanusz Krzysztofik struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
40408f25cd7SMiquel Raynal int ret;
40593db446aSBoris Brezillon
40691a1abfbSJanusz Krzysztofik /* Apply write protection */
407241008edSJanusz Krzysztofik gpiod_set_value(priv->gpiod_nwp, 1);
40891a1abfbSJanusz Krzysztofik
4097416bd35SJanusz Krzysztofik /* Unregister device */
41008f25cd7SMiquel Raynal ret = mtd_device_unregister(mtd);
41108f25cd7SMiquel Raynal WARN_ON(ret);
41208f25cd7SMiquel Raynal nand_cleanup(mtd_to_nand(mtd));
41393db446aSBoris Brezillon }
41493db446aSBoris Brezillon
4156d111787SJanusz Krzysztofik #ifdef CONFIG_OF
4167c2f66a9SJanusz Krzysztofik static const struct of_device_id gpio_nand_of_id_table[] = {
4177c2f66a9SJanusz Krzysztofik {
4187c2f66a9SJanusz Krzysztofik /* sentinel */
4197c2f66a9SJanusz Krzysztofik },
4207c2f66a9SJanusz Krzysztofik };
4217c2f66a9SJanusz Krzysztofik MODULE_DEVICE_TABLE(of, gpio_nand_of_id_table);
4226d111787SJanusz Krzysztofik #endif
4237c2f66a9SJanusz Krzysztofik
4247c2f66a9SJanusz Krzysztofik static const struct platform_device_id gpio_nand_plat_id_table[] = {
4257c2f66a9SJanusz Krzysztofik {
4267c2f66a9SJanusz Krzysztofik .name = "ams-delta-nand",
4277c2f66a9SJanusz Krzysztofik }, {
4287c2f66a9SJanusz Krzysztofik /* sentinel */
4297c2f66a9SJanusz Krzysztofik },
4307c2f66a9SJanusz Krzysztofik };
4317c2f66a9SJanusz Krzysztofik MODULE_DEVICE_TABLE(platform, gpio_nand_plat_id_table);
4327c2f66a9SJanusz Krzysztofik
43316d00cd6SJanusz Krzysztofik static struct platform_driver gpio_nand_driver = {
43416d00cd6SJanusz Krzysztofik .probe = gpio_nand_probe,
435ec185b18SUwe Kleine-König .remove_new = gpio_nand_remove,
4367c2f66a9SJanusz Krzysztofik .id_table = gpio_nand_plat_id_table,
43793db446aSBoris Brezillon .driver = {
43893db446aSBoris Brezillon .name = "ams-delta-nand",
4397c2f66a9SJanusz Krzysztofik .of_match_table = of_match_ptr(gpio_nand_of_id_table),
44093db446aSBoris Brezillon },
44193db446aSBoris Brezillon };
44293db446aSBoris Brezillon
44316d00cd6SJanusz Krzysztofik module_platform_driver(gpio_nand_driver);
44493db446aSBoris Brezillon
4454857393dSBoris Brezillon MODULE_LICENSE("GPL v2");
44693db446aSBoris Brezillon MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
44793db446aSBoris Brezillon MODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)");
448