xref: /openbmc/linux/drivers/mtd/nand/raw/ams-delta.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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