xref: /linux/drivers/mtd/nand/raw/plat_nand.c (revision cdd38c5f1ce4398ec58fec95904b75824daab7b5)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2711fdf62SVitaly Wool /*
3711fdf62SVitaly Wool  * Generic NAND driver
4711fdf62SVitaly Wool  *
5711fdf62SVitaly Wool  * Author: Vitaly Wool <vitalywool@gmail.com>
6711fdf62SVitaly Wool  */
7711fdf62SVitaly Wool 
8ffdac7cdSJingoo Han #include <linux/err.h>
9711fdf62SVitaly Wool #include <linux/io.h>
10711fdf62SVitaly Wool #include <linux/module.h>
11711fdf62SVitaly Wool #include <linux/platform_device.h>
12711fdf62SVitaly Wool #include <linux/slab.h>
13711fdf62SVitaly Wool #include <linux/mtd/mtd.h>
14c7921bb3SBoris Brezillon #include <linux/mtd/platnand.h>
15711fdf62SVitaly Wool 
16711fdf62SVitaly Wool struct plat_nand_data {
17612e048eSMiquel Raynal 	struct nand_controller	controller;
18711fdf62SVitaly Wool 	struct nand_chip	chip;
19711fdf62SVitaly Wool 	void __iomem		*io_base;
20711fdf62SVitaly Wool };
21711fdf62SVitaly Wool 
22612e048eSMiquel Raynal static int plat_nand_attach_chip(struct nand_chip *chip)
23612e048eSMiquel Raynal {
24612e048eSMiquel Raynal 	chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
25*148b4f16SMiquel Raynal 
26*148b4f16SMiquel Raynal 	if (chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN)
27612e048eSMiquel Raynal 		chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
28612e048eSMiquel Raynal 
29612e048eSMiquel Raynal 	return 0;
30612e048eSMiquel Raynal }
31612e048eSMiquel Raynal 
32612e048eSMiquel Raynal static const struct nand_controller_ops plat_nand_ops = {
33612e048eSMiquel Raynal 	.attach_chip = plat_nand_attach_chip,
34612e048eSMiquel Raynal };
35612e048eSMiquel Raynal 
36711fdf62SVitaly Wool /*
37711fdf62SVitaly Wool  * Probe for the NAND device.
38711fdf62SVitaly Wool  */
3906f25510SBill Pemberton static int plat_nand_probe(struct platform_device *pdev)
40711fdf62SVitaly Wool {
41453810b7SJingoo Han 	struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev);
42711fdf62SVitaly Wool 	struct plat_nand_data *data;
43a0260d21SBoris BREZILLON 	struct mtd_info *mtd;
442d098a72SH Hartley Sweeten 	struct resource *res;
45f2e5a244SH Hartley Sweeten 	const char **part_types;
462d098a72SH Hartley Sweeten 	int err = 0;
472d098a72SH Hartley Sweeten 
48da3888cbSJohn Crispin 	if (!pdata) {
49da3888cbSJohn Crispin 		dev_err(&pdev->dev, "platform_nand_data is missing\n");
50da3888cbSJohn Crispin 		return -EINVAL;
51da3888cbSJohn Crispin 	}
52da3888cbSJohn Crispin 
5301cd2abaSMarek Vasut 	if (pdata->chip.nr_chips < 1) {
5401cd2abaSMarek Vasut 		dev_err(&pdev->dev, "invalid number of chips specified\n");
5501cd2abaSMarek Vasut 		return -EINVAL;
5601cd2abaSMarek Vasut 	}
5701cd2abaSMarek Vasut 
58711fdf62SVitaly Wool 	/* Allocate memory for the device structure (and zero it) */
59ffdac7cdSJingoo Han 	data = devm_kzalloc(&pdev->dev, sizeof(struct plat_nand_data),
60ffdac7cdSJingoo Han 			    GFP_KERNEL);
61a01eb204SJingoo Han 	if (!data)
62711fdf62SVitaly Wool 		return -ENOMEM;
63711fdf62SVitaly Wool 
64612e048eSMiquel Raynal 	data->controller.ops = &plat_nand_ops;
65612e048eSMiquel Raynal 	nand_controller_init(&data->controller);
66612e048eSMiquel Raynal 	data->chip.controller = &data->controller;
67612e048eSMiquel Raynal 
68840f53c3SWei Yongjun 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
69ffdac7cdSJingoo Han 	data->io_base = devm_ioremap_resource(&pdev->dev, res);
70ffdac7cdSJingoo Han 	if (IS_ERR(data->io_base))
71ffdac7cdSJingoo Han 		return PTR_ERR(data->io_base);
72711fdf62SVitaly Wool 
73a61ae81aSBrian Norris 	nand_set_flash_node(&data->chip, pdev->dev.of_node);
74a0260d21SBoris BREZILLON 	mtd = nand_to_mtd(&data->chip);
75a0260d21SBoris BREZILLON 	mtd->dev.parent = &pdev->dev;
76711fdf62SVitaly Wool 
7782fc5099SBoris Brezillon 	data->chip.legacy.IO_ADDR_R = data->io_base;
7882fc5099SBoris Brezillon 	data->chip.legacy.IO_ADDR_W = data->io_base;
79bf6065c6SBoris Brezillon 	data->chip.legacy.cmd_ctrl = pdata->ctrl.cmd_ctrl;
808395b753SBoris Brezillon 	data->chip.legacy.dev_ready = pdata->ctrl.dev_ready;
817d6c37e9SBoris Brezillon 	data->chip.legacy.select_chip = pdata->ctrl.select_chip;
82716bbbabSBoris Brezillon 	data->chip.legacy.write_buf = pdata->ctrl.write_buf;
83716bbbabSBoris Brezillon 	data->chip.legacy.read_buf = pdata->ctrl.read_buf;
843cece3abSBoris Brezillon 	data->chip.legacy.chip_delay = pdata->chip.chip_delay;
85711fdf62SVitaly Wool 	data->chip.options |= pdata->chip.options;
86a40f7341SBrian Norris 	data->chip.bbt_options |= pdata->chip.bbt_options;
87711fdf62SVitaly Wool 
88711fdf62SVitaly Wool 	platform_set_drvdata(pdev, data);
89711fdf62SVitaly Wool 
90bf95efd4SH Hartley Sweeten 	/* Handle any platform specific setup */
91bf95efd4SH Hartley Sweeten 	if (pdata->ctrl.probe) {
922d098a72SH Hartley Sweeten 		err = pdata->ctrl.probe(pdev);
932d098a72SH Hartley Sweeten 		if (err)
94bf95efd4SH Hartley Sweeten 			goto out;
95bf95efd4SH Hartley Sweeten 	}
96bf95efd4SH Hartley Sweeten 
9725985edcSLucas De Marchi 	/* Scan to find existence of the device */
9800ad378fSBoris Brezillon 	err = nand_scan(&data->chip, pdata->chip.nr_chips);
99ce2eaca7SMasahiro Yamada 	if (err)
100711fdf62SVitaly Wool 		goto out;
101711fdf62SVitaly Wool 
1028bf57b0dSBrian Norris 	part_types = pdata->chip.part_probe_types;
103f2e5a244SH Hartley Sweeten 
104a0260d21SBoris BREZILLON 	err = mtd_device_parse_register(mtd, part_types, NULL,
10542d7fbe2SArtem Bityutskiy 					pdata->chip.partitions,
10642d7fbe2SArtem Bityutskiy 					pdata->chip.nr_partitions);
107711fdf62SVitaly Wool 
1082d098a72SH Hartley Sweeten 	if (!err)
1092d098a72SH Hartley Sweeten 		return err;
110711fdf62SVitaly Wool 
1115284024bSMiquel Raynal 	nand_cleanup(&data->chip);
112711fdf62SVitaly Wool out:
113bf95efd4SH Hartley Sweeten 	if (pdata->ctrl.remove)
114bf95efd4SH Hartley Sweeten 		pdata->ctrl.remove(pdev);
1152d098a72SH Hartley Sweeten 	return err;
116711fdf62SVitaly Wool }
117711fdf62SVitaly Wool 
118711fdf62SVitaly Wool /*
119711fdf62SVitaly Wool  * Remove a NAND device.
120711fdf62SVitaly Wool  */
121810b7e06SBill Pemberton static int plat_nand_remove(struct platform_device *pdev)
122711fdf62SVitaly Wool {
123711fdf62SVitaly Wool 	struct plat_nand_data *data = platform_get_drvdata(pdev);
124453810b7SJingoo Han 	struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev);
125d1aae005SMiquel Raynal 	struct nand_chip *chip = &data->chip;
126d1aae005SMiquel Raynal 	int ret;
127711fdf62SVitaly Wool 
128d1aae005SMiquel Raynal 	ret = mtd_device_unregister(nand_to_mtd(chip));
129d1aae005SMiquel Raynal 	WARN_ON(ret);
130d1aae005SMiquel Raynal 	nand_cleanup(chip);
131bf95efd4SH Hartley Sweeten 	if (pdata->ctrl.remove)
132bf95efd4SH Hartley Sweeten 		pdata->ctrl.remove(pdev);
133711fdf62SVitaly Wool 
134711fdf62SVitaly Wool 	return 0;
135711fdf62SVitaly Wool }
136711fdf62SVitaly Wool 
137a4f20351SJohn Crispin static const struct of_device_id plat_nand_match[] = {
138a4f20351SJohn Crispin 	{ .compatible = "gen_nand" },
139a4f20351SJohn Crispin 	{},
140a4f20351SJohn Crispin };
141a4f20351SJohn Crispin MODULE_DEVICE_TABLE(of, plat_nand_match);
142a4f20351SJohn Crispin 
143711fdf62SVitaly Wool static struct platform_driver plat_nand_driver = {
144711fdf62SVitaly Wool 	.probe	= plat_nand_probe,
1455153b88cSBill Pemberton 	.remove	= plat_nand_remove,
146711fdf62SVitaly Wool 	.driver	= {
147711fdf62SVitaly Wool 		.name		= "gen_nand",
148a4f20351SJohn Crispin 		.of_match_table = plat_nand_match,
149711fdf62SVitaly Wool 	},
150711fdf62SVitaly Wool };
151711fdf62SVitaly Wool 
152f99640deSAxel Lin module_platform_driver(plat_nand_driver);
153711fdf62SVitaly Wool 
154711fdf62SVitaly Wool MODULE_LICENSE("GPL");
155711fdf62SVitaly Wool MODULE_AUTHOR("Vitaly Wool");
156711fdf62SVitaly Wool MODULE_DESCRIPTION("Simple generic NAND driver");
1571ff18422SKay Sievers MODULE_ALIAS("platform:gen_nand");
158