xref: /linux/drivers/mtd/nand/raw/ams-delta.c (revision 95d002e0a34cb0f238abb39987f9980f325d8332)
14857393dSBoris Brezillon // SPDX-License-Identifier: GPL-2.0
23d12c0c7SJonathan McDowell /*
33d12c0c7SJonathan McDowell  *  Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
43d12c0c7SJonathan McDowell  *
5187c5448SBoris Brezillon  *  Derived from drivers/mtd/nand/toto.c (removed in v2.6.28)
67b6afee7SBoris Brezillon  *    Copyright (c) 2003 Texas Instruments
77b6afee7SBoris Brezillon  *    Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
87b6afee7SBoris Brezillon  *
97e95d1f1SJanusz Krzysztofik  *  Converted to platform driver by Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
1093cbd6f3SBoris Brezillon  *  Partially stolen from plat_nand.c
113d12c0c7SJonathan McDowell  *
123d12c0c7SJonathan McDowell  *  Overview:
133d12c0c7SJonathan McDowell  *   This is a device driver for the NAND flash device found on the
143d12c0c7SJonathan McDowell  *   Amstrad E3 (Delta).
153d12c0c7SJonathan McDowell  */
163d12c0c7SJonathan McDowell 
173d12c0c7SJonathan McDowell #include <linux/slab.h>
183d12c0c7SJonathan McDowell #include <linux/module.h>
193d12c0c7SJonathan McDowell #include <linux/delay.h>
20f1a97e0bSJanusz Krzysztofik #include <linux/gpio/consumer.h>
213d12c0c7SJonathan McDowell #include <linux/mtd/mtd.h>
22d4092d76SBoris Brezillon #include <linux/mtd/rawnand.h>
233d12c0c7SJonathan McDowell #include <linux/mtd/partitions.h>
24*7416bd35SJanusz Krzysztofik #include <linux/platform_device.h>
25fbb080a1SBoris Brezillon #include <linux/sizes.h>
263d12c0c7SJonathan McDowell 
273d12c0c7SJonathan McDowell /*
283d12c0c7SJonathan McDowell  * MTD structure for E3 (Delta)
293d12c0c7SJonathan McDowell  */
302b44af3aSJanusz Krzysztofik struct ams_delta_nand {
319fd6bcffSBoris Brezillon 	struct nand_controller	base;
322b44af3aSJanusz Krzysztofik 	struct nand_chip	nand_chip;
332b44af3aSJanusz Krzysztofik 	struct gpio_desc	*gpiod_rdy;
342b44af3aSJanusz Krzysztofik 	struct gpio_desc	*gpiod_nce;
352b44af3aSJanusz Krzysztofik 	struct gpio_desc	*gpiod_nre;
362b44af3aSJanusz Krzysztofik 	struct gpio_desc	*gpiod_nwp;
372b44af3aSJanusz Krzysztofik 	struct gpio_desc	*gpiod_nwe;
382b44af3aSJanusz Krzysztofik 	struct gpio_desc	*gpiod_ale;
392b44af3aSJanusz Krzysztofik 	struct gpio_desc	*gpiod_cle;
40*7416bd35SJanusz Krzysztofik 	struct gpio_descs	*data_gpiods;
419c076d7eSJanusz Krzysztofik 	bool			data_in;
422b44af3aSJanusz Krzysztofik };
433d12c0c7SJonathan McDowell 
443d12c0c7SJonathan McDowell /*
453d12c0c7SJonathan McDowell  * Define partitions for flash devices
463d12c0c7SJonathan McDowell  */
473d12c0c7SJonathan McDowell 
48d4906688SArvind Yadav static const struct mtd_partition partition_info[] = {
493d12c0c7SJonathan McDowell 	{ .name		= "Kernel",
503d12c0c7SJonathan McDowell 	  .offset	= 0,
513d12c0c7SJonathan McDowell 	  .size		= 3 * SZ_1M + SZ_512K },
523d12c0c7SJonathan McDowell 	{ .name		= "u-boot",
533d12c0c7SJonathan McDowell 	  .offset	= 3 * SZ_1M + SZ_512K,
543d12c0c7SJonathan McDowell 	  .size		= SZ_256K },
553d12c0c7SJonathan McDowell 	{ .name		= "u-boot params",
563d12c0c7SJonathan McDowell 	  .offset	= 3 * SZ_1M + SZ_512K + SZ_256K,
573d12c0c7SJonathan McDowell 	  .size		= SZ_256K },
583d12c0c7SJonathan McDowell 	{ .name		= "Amstrad LDR",
593d12c0c7SJonathan McDowell 	  .offset	= 4 * SZ_1M,
603d12c0c7SJonathan McDowell 	  .size		= SZ_256K },
613d12c0c7SJonathan McDowell 	{ .name		= "File system",
623d12c0c7SJonathan McDowell 	  .offset	= 4 * SZ_1M + 1 * SZ_256K,
633d12c0c7SJonathan McDowell 	  .size		= 27 * SZ_1M },
643d12c0c7SJonathan McDowell 	{ .name		= "PBL reserved",
653d12c0c7SJonathan McDowell 	  .offset	= 32 * SZ_1M - 3 * SZ_256K,
663d12c0c7SJonathan McDowell 	  .size		=  3 * SZ_256K },
673d12c0c7SJonathan McDowell };
683d12c0c7SJonathan McDowell 
69*7416bd35SJanusz Krzysztofik static void ams_delta_write_commit(struct ams_delta_nand *priv)
703d12c0c7SJonathan McDowell {
712b44af3aSJanusz Krzysztofik 	gpiod_set_value(priv->gpiod_nwe, 0);
723d12c0c7SJonathan McDowell 	ndelay(40);
732b44af3aSJanusz Krzysztofik 	gpiod_set_value(priv->gpiod_nwe, 1);
743d12c0c7SJonathan McDowell }
753d12c0c7SJonathan McDowell 
76*7416bd35SJanusz Krzysztofik static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte)
77*7416bd35SJanusz Krzysztofik {
78*7416bd35SJanusz Krzysztofik 	struct gpio_descs *data_gpiods = priv->data_gpiods;
79*7416bd35SJanusz Krzysztofik 	DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, };
80*7416bd35SJanusz Krzysztofik 
81*7416bd35SJanusz Krzysztofik 	gpiod_set_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
82*7416bd35SJanusz Krzysztofik 				  data_gpiods->info, values);
83*7416bd35SJanusz Krzysztofik 
84*7416bd35SJanusz Krzysztofik 	ams_delta_write_commit(priv);
85*7416bd35SJanusz Krzysztofik }
86*7416bd35SJanusz Krzysztofik 
87*7416bd35SJanusz Krzysztofik static void ams_delta_dir_output(struct ams_delta_nand *priv, u8 byte)
88*7416bd35SJanusz Krzysztofik {
89*7416bd35SJanusz Krzysztofik 	struct gpio_descs *data_gpiods = priv->data_gpiods;
90*7416bd35SJanusz Krzysztofik 	DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, };
91*7416bd35SJanusz Krzysztofik 	int i;
92*7416bd35SJanusz Krzysztofik 
93*7416bd35SJanusz Krzysztofik 	for (i = 0; i < data_gpiods->ndescs; i++)
94*7416bd35SJanusz Krzysztofik 		gpiod_direction_output_raw(data_gpiods->desc[i],
95*7416bd35SJanusz Krzysztofik 					   test_bit(i, values));
96*7416bd35SJanusz Krzysztofik 
97*7416bd35SJanusz Krzysztofik 	ams_delta_write_commit(priv);
98*7416bd35SJanusz Krzysztofik 
99*7416bd35SJanusz Krzysztofik 	priv->data_in = false;
100*7416bd35SJanusz Krzysztofik }
101*7416bd35SJanusz Krzysztofik 
102d54445d6SBoris Brezillon static u8 ams_delta_io_read(struct ams_delta_nand *priv)
1033d12c0c7SJonathan McDowell {
104d54445d6SBoris Brezillon 	u8 res;
105*7416bd35SJanusz Krzysztofik 	struct gpio_descs *data_gpiods = priv->data_gpiods;
106*7416bd35SJanusz Krzysztofik 	DECLARE_BITMAP(values, BITS_PER_TYPE(res)) = { 0, };
1073d12c0c7SJonathan McDowell 
1082b44af3aSJanusz Krzysztofik 	gpiod_set_value(priv->gpiod_nre, 0);
1093d12c0c7SJonathan McDowell 	ndelay(40);
110*7416bd35SJanusz Krzysztofik 
111*7416bd35SJanusz Krzysztofik 	gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
112*7416bd35SJanusz Krzysztofik 				  data_gpiods->info, values);
113*7416bd35SJanusz Krzysztofik 
1142b44af3aSJanusz Krzysztofik 	gpiod_set_value(priv->gpiod_nre, 1);
1153d12c0c7SJonathan McDowell 
116*7416bd35SJanusz Krzysztofik 	res = values[0];
1173d12c0c7SJonathan McDowell 	return res;
1183d12c0c7SJonathan McDowell }
1193d12c0c7SJonathan McDowell 
120*7416bd35SJanusz Krzysztofik static void ams_delta_dir_input(struct ams_delta_nand *priv)
1219c076d7eSJanusz Krzysztofik {
122*7416bd35SJanusz Krzysztofik 	struct gpio_descs *data_gpiods = priv->data_gpiods;
123*7416bd35SJanusz Krzysztofik 	int i;
124*7416bd35SJanusz Krzysztofik 
125*7416bd35SJanusz Krzysztofik 	for (i = 0; i < data_gpiods->ndescs; i++)
126*7416bd35SJanusz Krzysztofik 		gpiod_direction_input(data_gpiods->desc[i]);
127*7416bd35SJanusz Krzysztofik 
128*7416bd35SJanusz Krzysztofik 	priv->data_in = true;
1299c076d7eSJanusz Krzysztofik }
1309c076d7eSJanusz Krzysztofik 
131d54445d6SBoris Brezillon static void ams_delta_write_buf(struct ams_delta_nand *priv, const u8 *buf,
1323d12c0c7SJonathan McDowell 				int len)
1333d12c0c7SJonathan McDowell {
134*7416bd35SJanusz Krzysztofik 	int i = 0;
1353d12c0c7SJonathan McDowell 
136*7416bd35SJanusz Krzysztofik 	if (len > 0 && priv->data_in)
137*7416bd35SJanusz Krzysztofik 		ams_delta_dir_output(priv, buf[i++]);
1389c076d7eSJanusz Krzysztofik 
139*7416bd35SJanusz Krzysztofik 	while (i < len)
140*7416bd35SJanusz Krzysztofik 		ams_delta_io_write(priv, buf[i++]);
1413d12c0c7SJonathan McDowell }
1423d12c0c7SJonathan McDowell 
143d54445d6SBoris Brezillon static void ams_delta_read_buf(struct ams_delta_nand *priv, u8 *buf, int len)
1443d12c0c7SJonathan McDowell {
1453d12c0c7SJonathan McDowell 	int i;
1463d12c0c7SJonathan McDowell 
1479c076d7eSJanusz Krzysztofik 	if (!priv->data_in)
148*7416bd35SJanusz Krzysztofik 		ams_delta_dir_input(priv);
1499c076d7eSJanusz Krzysztofik 
1503d12c0c7SJonathan McDowell 	for (i = 0; i < len; i++)
1519c076d7eSJanusz Krzysztofik 		buf[i] = ams_delta_io_read(priv);
1529c076d7eSJanusz Krzysztofik }
1539c076d7eSJanusz Krzysztofik 
1541770022fSBoris Brezillon static void ams_delta_ctrl_cs(struct ams_delta_nand *priv, bool assert)
1557abd3ef9SThomas Gleixner {
1561770022fSBoris Brezillon 	gpiod_set_value(priv->gpiod_nce, assert ? 0 : 1);
1577abd3ef9SThomas Gleixner }
1587abd3ef9SThomas Gleixner 
159861fbd6eSJanusz Krzysztofik static int ams_delta_exec_op(struct nand_chip *this,
160861fbd6eSJanusz Krzysztofik 			     const struct nand_operation *op, bool check_only)
1613d12c0c7SJonathan McDowell {
1622b44af3aSJanusz Krzysztofik 	struct ams_delta_nand *priv = nand_get_controller_data(this);
163861fbd6eSJanusz Krzysztofik 	const struct nand_op_instr *instr;
164861fbd6eSJanusz Krzysztofik 	int ret = 0;
1652b44af3aSJanusz Krzysztofik 
166861fbd6eSJanusz Krzysztofik 	if (check_only)
167861fbd6eSJanusz Krzysztofik 		return 0;
168861fbd6eSJanusz Krzysztofik 
1691770022fSBoris Brezillon 	ams_delta_ctrl_cs(priv, 1);
1701770022fSBoris Brezillon 
171861fbd6eSJanusz Krzysztofik 	for (instr = op->instrs; instr < op->instrs + op->ninstrs; instr++) {
172861fbd6eSJanusz Krzysztofik 		switch (instr->type) {
173861fbd6eSJanusz Krzysztofik 		case NAND_OP_CMD_INSTR:
174861fbd6eSJanusz Krzysztofik 			gpiod_set_value(priv->gpiod_cle, 1);
175861fbd6eSJanusz Krzysztofik 			ams_delta_write_buf(priv, &instr->ctx.cmd.opcode, 1);
176861fbd6eSJanusz Krzysztofik 			gpiod_set_value(priv->gpiod_cle, 0);
177861fbd6eSJanusz Krzysztofik 			break;
178861fbd6eSJanusz Krzysztofik 
179861fbd6eSJanusz Krzysztofik 		case NAND_OP_ADDR_INSTR:
180861fbd6eSJanusz Krzysztofik 			gpiod_set_value(priv->gpiod_ale, 1);
181861fbd6eSJanusz Krzysztofik 			ams_delta_write_buf(priv, instr->ctx.addr.addrs,
182861fbd6eSJanusz Krzysztofik 					    instr->ctx.addr.naddrs);
183861fbd6eSJanusz Krzysztofik 			gpiod_set_value(priv->gpiod_ale, 0);
184861fbd6eSJanusz Krzysztofik 			break;
185861fbd6eSJanusz Krzysztofik 
186861fbd6eSJanusz Krzysztofik 		case NAND_OP_DATA_IN_INSTR:
187861fbd6eSJanusz Krzysztofik 			ams_delta_read_buf(priv, instr->ctx.data.buf.in,
188861fbd6eSJanusz Krzysztofik 					   instr->ctx.data.len);
189861fbd6eSJanusz Krzysztofik 			break;
190861fbd6eSJanusz Krzysztofik 
191861fbd6eSJanusz Krzysztofik 		case NAND_OP_DATA_OUT_INSTR:
192861fbd6eSJanusz Krzysztofik 			ams_delta_write_buf(priv, instr->ctx.data.buf.out,
193861fbd6eSJanusz Krzysztofik 					    instr->ctx.data.len);
194861fbd6eSJanusz Krzysztofik 			break;
195861fbd6eSJanusz Krzysztofik 
196861fbd6eSJanusz Krzysztofik 		case NAND_OP_WAITRDY_INSTR:
197861fbd6eSJanusz Krzysztofik 			ret = priv->gpiod_rdy ?
198861fbd6eSJanusz Krzysztofik 			      nand_gpio_waitrdy(this, priv->gpiod_rdy,
199861fbd6eSJanusz Krzysztofik 						instr->ctx.waitrdy.timeout_ms) :
200861fbd6eSJanusz Krzysztofik 			      nand_soft_waitrdy(this,
201861fbd6eSJanusz Krzysztofik 						instr->ctx.waitrdy.timeout_ms);
202861fbd6eSJanusz Krzysztofik 			break;
203861fbd6eSJanusz Krzysztofik 		}
204861fbd6eSJanusz Krzysztofik 
205861fbd6eSJanusz Krzysztofik 		if (ret)
206861fbd6eSJanusz Krzysztofik 			break;
207861fbd6eSJanusz Krzysztofik 	}
208861fbd6eSJanusz Krzysztofik 
2091770022fSBoris Brezillon 	ams_delta_ctrl_cs(priv, 0);
2101770022fSBoris Brezillon 
211861fbd6eSJanusz Krzysztofik 	return ret;
2123d12c0c7SJonathan McDowell }
2133d12c0c7SJonathan McDowell 
214f2abfeb2SBoris Brezillon static const struct nand_controller_ops ams_delta_ops = {
215f2abfeb2SBoris Brezillon 	.exec_op = ams_delta_exec_op,
216f2abfeb2SBoris Brezillon };
217f2abfeb2SBoris Brezillon 
2183d12c0c7SJonathan McDowell /*
2193d12c0c7SJonathan McDowell  * Main initialization routine
2203d12c0c7SJonathan McDowell  */
22106f25510SBill Pemberton static int ams_delta_init(struct platform_device *pdev)
2223d12c0c7SJonathan McDowell {
2232b44af3aSJanusz Krzysztofik 	struct ams_delta_nand *priv;
2243d12c0c7SJonathan McDowell 	struct nand_chip *this;
2252b44af3aSJanusz Krzysztofik 	struct mtd_info *mtd;
22697738613SJanusz Krzysztofik 	struct gpio_descs *data_gpiods;
2273d12c0c7SJonathan McDowell 	int err = 0;
2283d12c0c7SJonathan McDowell 
2293d12c0c7SJonathan McDowell 	/* Allocate memory for MTD device structure and private data */
2302b44af3aSJanusz Krzysztofik 	priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
2312b44af3aSJanusz Krzysztofik 			    GFP_KERNEL);
232d54445d6SBoris Brezillon 	if (!priv)
2332b44af3aSJanusz Krzysztofik 		return -ENOMEM;
234d54445d6SBoris Brezillon 
2352b44af3aSJanusz Krzysztofik 	this = &priv->nand_chip;
2363d12c0c7SJonathan McDowell 
2372b44af3aSJanusz Krzysztofik 	mtd = nand_to_mtd(this);
2382b44af3aSJanusz Krzysztofik 	mtd->dev.parent = &pdev->dev;
2393d12c0c7SJonathan McDowell 
2402b44af3aSJanusz Krzysztofik 	nand_set_controller_data(this, priv);
241eaca491fSJanusz Krzysztofik 
2422b44af3aSJanusz Krzysztofik 	priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
2432b44af3aSJanusz Krzysztofik 	if (IS_ERR(priv->gpiod_rdy)) {
2442b44af3aSJanusz Krzysztofik 		err = PTR_ERR(priv->gpiod_rdy);
245f1a97e0bSJanusz Krzysztofik 		dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
246*7416bd35SJanusz Krzysztofik 		return err;
2473d12c0c7SJonathan McDowell 	}
248f1a97e0bSJanusz Krzysztofik 
2496dfc6d25SThomas Gleixner 	this->ecc.mode = NAND_ECC_SOFT;
250e58dd3c3SRafał Miłecki 	this->ecc.algo = NAND_ECC_HAMMING;
2513d12c0c7SJonathan McDowell 
2522b44af3aSJanusz Krzysztofik 	platform_set_drvdata(pdev, priv);
253eaca491fSJanusz Krzysztofik 
2543d12c0c7SJonathan McDowell 	/* Set chip enabled, but  */
2552b44af3aSJanusz Krzysztofik 	priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
2562b44af3aSJanusz Krzysztofik 	if (IS_ERR(priv->gpiod_nwp)) {
2572b44af3aSJanusz Krzysztofik 		err = PTR_ERR(priv->gpiod_nwp);
258f1a97e0bSJanusz Krzysztofik 		dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
259*7416bd35SJanusz Krzysztofik 		return err;
260f1a97e0bSJanusz Krzysztofik 	}
261f1a97e0bSJanusz Krzysztofik 
2622b44af3aSJanusz Krzysztofik 	priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
2632b44af3aSJanusz Krzysztofik 	if (IS_ERR(priv->gpiod_nce)) {
2642b44af3aSJanusz Krzysztofik 		err = PTR_ERR(priv->gpiod_nce);
265f1a97e0bSJanusz Krzysztofik 		dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
266*7416bd35SJanusz Krzysztofik 		return err;
267f1a97e0bSJanusz Krzysztofik 	}
268f1a97e0bSJanusz Krzysztofik 
2692b44af3aSJanusz Krzysztofik 	priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
2702b44af3aSJanusz Krzysztofik 	if (IS_ERR(priv->gpiod_nre)) {
2712b44af3aSJanusz Krzysztofik 		err = PTR_ERR(priv->gpiod_nre);
272f1a97e0bSJanusz Krzysztofik 		dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
273*7416bd35SJanusz Krzysztofik 		return err;
274f1a97e0bSJanusz Krzysztofik 	}
275f1a97e0bSJanusz Krzysztofik 
2762b44af3aSJanusz Krzysztofik 	priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
2772b44af3aSJanusz Krzysztofik 	if (IS_ERR(priv->gpiod_nwe)) {
2782b44af3aSJanusz Krzysztofik 		err = PTR_ERR(priv->gpiod_nwe);
279f1a97e0bSJanusz Krzysztofik 		dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
280*7416bd35SJanusz Krzysztofik 		return err;
281f1a97e0bSJanusz Krzysztofik 	}
282f1a97e0bSJanusz Krzysztofik 
2832b44af3aSJanusz Krzysztofik 	priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
2842b44af3aSJanusz Krzysztofik 	if (IS_ERR(priv->gpiod_ale)) {
2852b44af3aSJanusz Krzysztofik 		err = PTR_ERR(priv->gpiod_ale);
286f1a97e0bSJanusz Krzysztofik 		dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
287*7416bd35SJanusz Krzysztofik 		return err;
288f1a97e0bSJanusz Krzysztofik 	}
289f1a97e0bSJanusz Krzysztofik 
2902b44af3aSJanusz Krzysztofik 	priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
2912b44af3aSJanusz Krzysztofik 	if (IS_ERR(priv->gpiod_cle)) {
2922b44af3aSJanusz Krzysztofik 		err = PTR_ERR(priv->gpiod_cle);
293f1a97e0bSJanusz Krzysztofik 		dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
294*7416bd35SJanusz Krzysztofik 		return err;
295f1a97e0bSJanusz Krzysztofik 	}
2963d12c0c7SJonathan McDowell 
29797738613SJanusz Krzysztofik 	/* Request array of data pins, initialize them as input */
29897738613SJanusz Krzysztofik 	data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN);
29997738613SJanusz Krzysztofik 	if (IS_ERR(data_gpiods)) {
30097738613SJanusz Krzysztofik 		err = PTR_ERR(data_gpiods);
30197738613SJanusz Krzysztofik 		dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
302*7416bd35SJanusz Krzysztofik 		return err;
30397738613SJanusz Krzysztofik 	}
304*7416bd35SJanusz Krzysztofik 	priv->data_gpiods = data_gpiods;
30597738613SJanusz Krzysztofik 	priv->data_in = true;
3069c076d7eSJanusz Krzysztofik 
3079fd6bcffSBoris Brezillon 	/* Initialize the NAND controller object embedded in ams_delta_nand. */
308f2abfeb2SBoris Brezillon 	priv->base.ops = &ams_delta_ops;
3099fd6bcffSBoris Brezillon 	nand_controller_init(&priv->base);
3109fd6bcffSBoris Brezillon 	this->controller = &priv->base;
3119fd6bcffSBoris Brezillon 
31225985edcSLucas De Marchi 	/* Scan to find existence of the device */
31300ad378fSBoris Brezillon 	err = nand_scan(this, 1);
3140d0aa866SMasahiro Yamada 	if (err)
315*7416bd35SJanusz Krzysztofik 		return err;
3163d12c0c7SJonathan McDowell 
3173d12c0c7SJonathan McDowell 	/* Register the partitions */
318876ba603SBoris Brezillon 	err = mtd_device_register(mtd, partition_info,
319876ba603SBoris Brezillon 				  ARRAY_SIZE(partition_info));
320876ba603SBoris Brezillon 	if (err)
321876ba603SBoris Brezillon 		goto err_nand_cleanup;
3223d12c0c7SJonathan McDowell 
3238bbc3c08SBoris Brezillon 	return 0;
3243d12c0c7SJonathan McDowell 
325876ba603SBoris Brezillon err_nand_cleanup:
326876ba603SBoris Brezillon 	nand_cleanup(this);
327876ba603SBoris Brezillon 
3283d12c0c7SJonathan McDowell 	return err;
3293d12c0c7SJonathan McDowell }
3303d12c0c7SJonathan McDowell 
3313d12c0c7SJonathan McDowell /*
3323d12c0c7SJonathan McDowell  * Clean up routine
3333d12c0c7SJonathan McDowell  */
334810b7e06SBill Pemberton static int ams_delta_cleanup(struct platform_device *pdev)
3353d12c0c7SJonathan McDowell {
3362b44af3aSJanusz Krzysztofik 	struct ams_delta_nand *priv = platform_get_drvdata(pdev);
3372b44af3aSJanusz Krzysztofik 	struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
338eaca491fSJanusz Krzysztofik 
339*7416bd35SJanusz Krzysztofik 	/* Unregister device */
3402b44af3aSJanusz Krzysztofik 	nand_release(mtd_to_nand(mtd));
3413d12c0c7SJonathan McDowell 
3427e95d1f1SJanusz Krzysztofik 	return 0;
3433d12c0c7SJonathan McDowell }
3447e95d1f1SJanusz Krzysztofik 
3457e95d1f1SJanusz Krzysztofik static struct platform_driver ams_delta_nand_driver = {
3467e95d1f1SJanusz Krzysztofik 	.probe		= ams_delta_init,
3475153b88cSBill Pemberton 	.remove		= ams_delta_cleanup,
3487e95d1f1SJanusz Krzysztofik 	.driver		= {
3497e95d1f1SJanusz Krzysztofik 		.name	= "ams-delta-nand",
3507e95d1f1SJanusz Krzysztofik 	},
3517e95d1f1SJanusz Krzysztofik };
3527e95d1f1SJanusz Krzysztofik 
353f99640deSAxel Lin module_platform_driver(ams_delta_nand_driver);
3543d12c0c7SJonathan McDowell 
3554857393dSBoris Brezillon MODULE_LICENSE("GPL v2");
3563d12c0c7SJonathan McDowell MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
3573d12c0c7SJonathan McDowell MODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)");
358