xref: /linux/drivers/mtd/nand/raw/plat_nand.c (revision d2912cb15bdda8ba4a5dd73396ad62641af2f520)
1*d2912cb1SThomas 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 {
17711fdf62SVitaly Wool 	struct nand_chip	chip;
18711fdf62SVitaly Wool 	void __iomem		*io_base;
19711fdf62SVitaly Wool };
20711fdf62SVitaly Wool 
21711fdf62SVitaly Wool /*
22711fdf62SVitaly Wool  * Probe for the NAND device.
23711fdf62SVitaly Wool  */
2406f25510SBill Pemberton static int plat_nand_probe(struct platform_device *pdev)
25711fdf62SVitaly Wool {
26453810b7SJingoo Han 	struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev);
27711fdf62SVitaly Wool 	struct plat_nand_data *data;
28a0260d21SBoris BREZILLON 	struct mtd_info *mtd;
292d098a72SH Hartley Sweeten 	struct resource *res;
30f2e5a244SH Hartley Sweeten 	const char **part_types;
312d098a72SH Hartley Sweeten 	int err = 0;
322d098a72SH Hartley Sweeten 
33da3888cbSJohn Crispin 	if (!pdata) {
34da3888cbSJohn Crispin 		dev_err(&pdev->dev, "platform_nand_data is missing\n");
35da3888cbSJohn Crispin 		return -EINVAL;
36da3888cbSJohn Crispin 	}
37da3888cbSJohn Crispin 
3801cd2abaSMarek Vasut 	if (pdata->chip.nr_chips < 1) {
3901cd2abaSMarek Vasut 		dev_err(&pdev->dev, "invalid number of chips specified\n");
4001cd2abaSMarek Vasut 		return -EINVAL;
4101cd2abaSMarek Vasut 	}
4201cd2abaSMarek Vasut 
43711fdf62SVitaly Wool 	/* Allocate memory for the device structure (and zero it) */
44ffdac7cdSJingoo Han 	data = devm_kzalloc(&pdev->dev, sizeof(struct plat_nand_data),
45ffdac7cdSJingoo Han 			    GFP_KERNEL);
46a01eb204SJingoo Han 	if (!data)
47711fdf62SVitaly Wool 		return -ENOMEM;
48711fdf62SVitaly Wool 
49840f53c3SWei Yongjun 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
50ffdac7cdSJingoo Han 	data->io_base = devm_ioremap_resource(&pdev->dev, res);
51ffdac7cdSJingoo Han 	if (IS_ERR(data->io_base))
52ffdac7cdSJingoo Han 		return PTR_ERR(data->io_base);
53711fdf62SVitaly Wool 
54a61ae81aSBrian Norris 	nand_set_flash_node(&data->chip, pdev->dev.of_node);
55a0260d21SBoris BREZILLON 	mtd = nand_to_mtd(&data->chip);
56a0260d21SBoris BREZILLON 	mtd->dev.parent = &pdev->dev;
57711fdf62SVitaly Wool 
5882fc5099SBoris Brezillon 	data->chip.legacy.IO_ADDR_R = data->io_base;
5982fc5099SBoris Brezillon 	data->chip.legacy.IO_ADDR_W = data->io_base;
60bf6065c6SBoris Brezillon 	data->chip.legacy.cmd_ctrl = pdata->ctrl.cmd_ctrl;
618395b753SBoris Brezillon 	data->chip.legacy.dev_ready = pdata->ctrl.dev_ready;
627d6c37e9SBoris Brezillon 	data->chip.legacy.select_chip = pdata->ctrl.select_chip;
63716bbbabSBoris Brezillon 	data->chip.legacy.write_buf = pdata->ctrl.write_buf;
64716bbbabSBoris Brezillon 	data->chip.legacy.read_buf = pdata->ctrl.read_buf;
653cece3abSBoris Brezillon 	data->chip.legacy.chip_delay = pdata->chip.chip_delay;
66711fdf62SVitaly Wool 	data->chip.options |= pdata->chip.options;
67a40f7341SBrian Norris 	data->chip.bbt_options |= pdata->chip.bbt_options;
68711fdf62SVitaly Wool 
69711fdf62SVitaly Wool 	data->chip.ecc.mode = NAND_ECC_SOFT;
7041ccb49eSRafał Miłecki 	data->chip.ecc.algo = NAND_ECC_HAMMING;
71711fdf62SVitaly Wool 
72711fdf62SVitaly Wool 	platform_set_drvdata(pdev, data);
73711fdf62SVitaly Wool 
74bf95efd4SH Hartley Sweeten 	/* Handle any platform specific setup */
75bf95efd4SH Hartley Sweeten 	if (pdata->ctrl.probe) {
762d098a72SH Hartley Sweeten 		err = pdata->ctrl.probe(pdev);
772d098a72SH Hartley Sweeten 		if (err)
78bf95efd4SH Hartley Sweeten 			goto out;
79bf95efd4SH Hartley Sweeten 	}
80bf95efd4SH Hartley Sweeten 
8125985edcSLucas De Marchi 	/* Scan to find existence of the device */
8200ad378fSBoris Brezillon 	err = nand_scan(&data->chip, pdata->chip.nr_chips);
83ce2eaca7SMasahiro Yamada 	if (err)
84711fdf62SVitaly Wool 		goto out;
85711fdf62SVitaly Wool 
868bf57b0dSBrian Norris 	part_types = pdata->chip.part_probe_types;
87f2e5a244SH Hartley Sweeten 
88a0260d21SBoris BREZILLON 	err = mtd_device_parse_register(mtd, part_types, NULL,
8942d7fbe2SArtem Bityutskiy 					pdata->chip.partitions,
9042d7fbe2SArtem Bityutskiy 					pdata->chip.nr_partitions);
91711fdf62SVitaly Wool 
922d098a72SH Hartley Sweeten 	if (!err)
932d098a72SH Hartley Sweeten 		return err;
94711fdf62SVitaly Wool 
9559ac276fSBoris Brezillon 	nand_release(&data->chip);
96711fdf62SVitaly Wool out:
97bf95efd4SH Hartley Sweeten 	if (pdata->ctrl.remove)
98bf95efd4SH Hartley Sweeten 		pdata->ctrl.remove(pdev);
992d098a72SH Hartley Sweeten 	return err;
100711fdf62SVitaly Wool }
101711fdf62SVitaly Wool 
102711fdf62SVitaly Wool /*
103711fdf62SVitaly Wool  * Remove a NAND device.
104711fdf62SVitaly Wool  */
105810b7e06SBill Pemberton static int plat_nand_remove(struct platform_device *pdev)
106711fdf62SVitaly Wool {
107711fdf62SVitaly Wool 	struct plat_nand_data *data = platform_get_drvdata(pdev);
108453810b7SJingoo Han 	struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev);
109711fdf62SVitaly Wool 
11059ac276fSBoris Brezillon 	nand_release(&data->chip);
111bf95efd4SH Hartley Sweeten 	if (pdata->ctrl.remove)
112bf95efd4SH Hartley Sweeten 		pdata->ctrl.remove(pdev);
113711fdf62SVitaly Wool 
114711fdf62SVitaly Wool 	return 0;
115711fdf62SVitaly Wool }
116711fdf62SVitaly Wool 
117a4f20351SJohn Crispin static const struct of_device_id plat_nand_match[] = {
118a4f20351SJohn Crispin 	{ .compatible = "gen_nand" },
119a4f20351SJohn Crispin 	{},
120a4f20351SJohn Crispin };
121a4f20351SJohn Crispin MODULE_DEVICE_TABLE(of, plat_nand_match);
122a4f20351SJohn Crispin 
123711fdf62SVitaly Wool static struct platform_driver plat_nand_driver = {
124711fdf62SVitaly Wool 	.probe	= plat_nand_probe,
1255153b88cSBill Pemberton 	.remove	= plat_nand_remove,
126711fdf62SVitaly Wool 	.driver	= {
127711fdf62SVitaly Wool 		.name		= "gen_nand",
128a4f20351SJohn Crispin 		.of_match_table = plat_nand_match,
129711fdf62SVitaly Wool 	},
130711fdf62SVitaly Wool };
131711fdf62SVitaly Wool 
132f99640deSAxel Lin module_platform_driver(plat_nand_driver);
133711fdf62SVitaly Wool 
134711fdf62SVitaly Wool MODULE_LICENSE("GPL");
135711fdf62SVitaly Wool MODULE_AUTHOR("Vitaly Wool");
136711fdf62SVitaly Wool MODULE_DESCRIPTION("Simple generic NAND driver");
1371ff18422SKay Sievers MODULE_ALIAS("platform:gen_nand");
138