1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Generic NAND driver |
4 | * |
5 | * Author: Vitaly Wool <vitalywool@gmail.com> |
6 | */ |
7 | |
8 | #include <linux/err.h> |
9 | #include <linux/io.h> |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/mtd/mtd.h> |
14 | #include <linux/mtd/platnand.h> |
15 | |
16 | struct plat_nand_data { |
17 | struct nand_controller controller; |
18 | struct nand_chip chip; |
19 | void __iomem *io_base; |
20 | }; |
21 | |
22 | static int plat_nand_attach_chip(struct nand_chip *chip) |
23 | { |
24 | if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT && |
25 | chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) |
26 | chip->ecc.algo = NAND_ECC_ALGO_HAMMING; |
27 | |
28 | return 0; |
29 | } |
30 | |
31 | static const struct nand_controller_ops plat_nand_ops = { |
32 | .attach_chip = plat_nand_attach_chip, |
33 | }; |
34 | |
35 | /* |
36 | * Probe for the NAND device. |
37 | */ |
38 | static int plat_nand_probe(struct platform_device *pdev) |
39 | { |
40 | struct platform_nand_data *pdata = dev_get_platdata(dev: &pdev->dev); |
41 | struct plat_nand_data *data; |
42 | struct mtd_info *mtd; |
43 | const char **part_types; |
44 | int err = 0; |
45 | |
46 | if (!pdata) { |
47 | dev_err(&pdev->dev, "platform_nand_data is missing\n" ); |
48 | return -EINVAL; |
49 | } |
50 | |
51 | if (pdata->chip.nr_chips < 1) { |
52 | dev_err(&pdev->dev, "invalid number of chips specified\n" ); |
53 | return -EINVAL; |
54 | } |
55 | |
56 | /* Allocate memory for the device structure (and zero it) */ |
57 | data = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct plat_nand_data), |
58 | GFP_KERNEL); |
59 | if (!data) |
60 | return -ENOMEM; |
61 | |
62 | data->controller.ops = &plat_nand_ops; |
63 | nand_controller_init(nfc: &data->controller); |
64 | data->chip.controller = &data->controller; |
65 | |
66 | data->io_base = devm_platform_ioremap_resource(pdev, index: 0); |
67 | if (IS_ERR(ptr: data->io_base)) |
68 | return PTR_ERR(ptr: data->io_base); |
69 | |
70 | nand_set_flash_node(chip: &data->chip, np: pdev->dev.of_node); |
71 | mtd = nand_to_mtd(chip: &data->chip); |
72 | mtd->dev.parent = &pdev->dev; |
73 | |
74 | data->chip.legacy.IO_ADDR_R = data->io_base; |
75 | data->chip.legacy.IO_ADDR_W = data->io_base; |
76 | data->chip.legacy.cmd_ctrl = pdata->ctrl.cmd_ctrl; |
77 | data->chip.legacy.dev_ready = pdata->ctrl.dev_ready; |
78 | data->chip.legacy.select_chip = pdata->ctrl.select_chip; |
79 | data->chip.legacy.write_buf = pdata->ctrl.write_buf; |
80 | data->chip.legacy.read_buf = pdata->ctrl.read_buf; |
81 | data->chip.legacy.chip_delay = pdata->chip.chip_delay; |
82 | data->chip.options |= pdata->chip.options; |
83 | data->chip.bbt_options |= pdata->chip.bbt_options; |
84 | |
85 | platform_set_drvdata(pdev, data); |
86 | |
87 | /* Handle any platform specific setup */ |
88 | if (pdata->ctrl.probe) { |
89 | err = pdata->ctrl.probe(pdev); |
90 | if (err) |
91 | goto out; |
92 | } |
93 | |
94 | /* |
95 | * This driver assumes that the default ECC engine should be TYPE_SOFT. |
96 | * Set ->engine_type before registering the NAND devices in order to |
97 | * provide a driver specific default value. |
98 | */ |
99 | data->chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; |
100 | |
101 | /* Scan to find existence of the device */ |
102 | err = nand_scan(chip: &data->chip, max_chips: pdata->chip.nr_chips); |
103 | if (err) |
104 | goto out; |
105 | |
106 | part_types = pdata->chip.part_probe_types; |
107 | |
108 | err = mtd_device_parse_register(mtd, part_probe_types: part_types, NULL, |
109 | defparts: pdata->chip.partitions, |
110 | defnr_parts: pdata->chip.nr_partitions); |
111 | |
112 | if (!err) |
113 | return err; |
114 | |
115 | nand_cleanup(chip: &data->chip); |
116 | out: |
117 | if (pdata->ctrl.remove) |
118 | pdata->ctrl.remove(pdev); |
119 | return err; |
120 | } |
121 | |
122 | /* |
123 | * Remove a NAND device. |
124 | */ |
125 | static void plat_nand_remove(struct platform_device *pdev) |
126 | { |
127 | struct plat_nand_data *data = platform_get_drvdata(pdev); |
128 | struct platform_nand_data *pdata = dev_get_platdata(dev: &pdev->dev); |
129 | struct nand_chip *chip = &data->chip; |
130 | int ret; |
131 | |
132 | ret = mtd_device_unregister(master: nand_to_mtd(chip)); |
133 | WARN_ON(ret); |
134 | nand_cleanup(chip); |
135 | if (pdata->ctrl.remove) |
136 | pdata->ctrl.remove(pdev); |
137 | } |
138 | |
139 | static const struct of_device_id plat_nand_match[] = { |
140 | { .compatible = "gen_nand" }, |
141 | {}, |
142 | }; |
143 | MODULE_DEVICE_TABLE(of, plat_nand_match); |
144 | |
145 | static struct platform_driver plat_nand_driver = { |
146 | .probe = plat_nand_probe, |
147 | .remove_new = plat_nand_remove, |
148 | .driver = { |
149 | .name = "gen_nand" , |
150 | .of_match_table = plat_nand_match, |
151 | }, |
152 | }; |
153 | |
154 | module_platform_driver(plat_nand_driver); |
155 | |
156 | MODULE_LICENSE("GPL" ); |
157 | MODULE_AUTHOR("Vitaly Wool" ); |
158 | MODULE_DESCRIPTION("Simple generic NAND driver" ); |
159 | MODULE_ALIAS("platform:gen_nand" ); |
160 | |