1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Freescale UPM NAND driver.
4 *
5 * Copyright © 2007-2008 MontaVista Software, Inc.
6 *
7 * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
8 */
9
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/delay.h>
13 #include <linux/mtd/rawnand.h>
14 #include <linux/mtd/partitions.h>
15 #include <linux/mtd/mtd.h>
16 #include <linux/of_platform.h>
17 #include <linux/io.h>
18 #include <linux/slab.h>
19 #include <asm/fsl_lbc.h>
20
21 struct fsl_upm_nand {
22 struct nand_controller base;
23 struct device *dev;
24 struct nand_chip chip;
25 struct fsl_upm upm;
26 uint8_t upm_addr_offset;
27 uint8_t upm_cmd_offset;
28 void __iomem *io_base;
29 struct gpio_desc *rnb_gpio[NAND_MAX_CHIPS];
30 uint32_t mchip_offsets[NAND_MAX_CHIPS];
31 uint32_t mchip_count;
32 uint32_t mchip_number;
33 };
34
to_fsl_upm_nand(struct mtd_info * mtdinfo)35 static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo)
36 {
37 return container_of(mtd_to_nand(mtdinfo), struct fsl_upm_nand,
38 chip);
39 }
40
fun_chip_init(struct fsl_upm_nand * fun,const struct device_node * upm_np,const struct resource * io_res)41 static int fun_chip_init(struct fsl_upm_nand *fun,
42 const struct device_node *upm_np,
43 const struct resource *io_res)
44 {
45 struct mtd_info *mtd = nand_to_mtd(&fun->chip);
46 int ret;
47 struct device_node *flash_np;
48
49 fun->chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
50 fun->chip.ecc.algo = NAND_ECC_ALGO_HAMMING;
51 fun->chip.controller = &fun->base;
52 mtd->dev.parent = fun->dev;
53
54 flash_np = of_get_next_child(upm_np, NULL);
55 if (!flash_np)
56 return -ENODEV;
57
58 nand_set_flash_node(&fun->chip, flash_np);
59 mtd->name = devm_kasprintf(fun->dev, GFP_KERNEL, "0x%llx.%pOFn",
60 (u64)io_res->start,
61 flash_np);
62 if (!mtd->name) {
63 ret = -ENOMEM;
64 goto err;
65 }
66
67 ret = nand_scan(&fun->chip, fun->mchip_count);
68 if (ret)
69 goto err;
70
71 ret = mtd_device_register(mtd, NULL, 0);
72 err:
73 of_node_put(flash_np);
74 return ret;
75 }
76
func_exec_instr(struct nand_chip * chip,const struct nand_op_instr * instr)77 static int func_exec_instr(struct nand_chip *chip,
78 const struct nand_op_instr *instr)
79 {
80 struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
81 u32 mar, reg_offs = fun->mchip_offsets[fun->mchip_number];
82 unsigned int i;
83 const u8 *out;
84 u8 *in;
85
86 switch (instr->type) {
87 case NAND_OP_CMD_INSTR:
88 fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset);
89 mar = (instr->ctx.cmd.opcode << (32 - fun->upm.width)) |
90 reg_offs;
91 fsl_upm_run_pattern(&fun->upm, fun->io_base + reg_offs, mar);
92 fsl_upm_end_pattern(&fun->upm);
93 return 0;
94
95 case NAND_OP_ADDR_INSTR:
96 fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset);
97 for (i = 0; i < instr->ctx.addr.naddrs; i++) {
98 mar = (instr->ctx.addr.addrs[i] << (32 - fun->upm.width)) |
99 reg_offs;
100 fsl_upm_run_pattern(&fun->upm, fun->io_base + reg_offs, mar);
101 }
102 fsl_upm_end_pattern(&fun->upm);
103 return 0;
104
105 case NAND_OP_DATA_IN_INSTR:
106 in = instr->ctx.data.buf.in;
107 for (i = 0; i < instr->ctx.data.len; i++)
108 in[i] = in_8(fun->io_base + reg_offs);
109 return 0;
110
111 case NAND_OP_DATA_OUT_INSTR:
112 out = instr->ctx.data.buf.out;
113 for (i = 0; i < instr->ctx.data.len; i++)
114 out_8(fun->io_base + reg_offs, out[i]);
115 return 0;
116
117 case NAND_OP_WAITRDY_INSTR:
118 if (!fun->rnb_gpio[fun->mchip_number])
119 return nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms);
120
121 return nand_gpio_waitrdy(chip, fun->rnb_gpio[fun->mchip_number],
122 instr->ctx.waitrdy.timeout_ms);
123
124 default:
125 return -EINVAL;
126 }
127
128 return 0;
129 }
130
fun_exec_op(struct nand_chip * chip,const struct nand_operation * op,bool check_only)131 static int fun_exec_op(struct nand_chip *chip, const struct nand_operation *op,
132 bool check_only)
133 {
134 struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
135 unsigned int i;
136 int ret;
137
138 if (op->cs >= NAND_MAX_CHIPS)
139 return -EINVAL;
140
141 if (check_only)
142 return 0;
143
144 fun->mchip_number = op->cs;
145
146 for (i = 0; i < op->ninstrs; i++) {
147 ret = func_exec_instr(chip, &op->instrs[i]);
148 if (ret)
149 return ret;
150
151 if (op->instrs[i].delay_ns)
152 ndelay(op->instrs[i].delay_ns);
153 }
154
155 return 0;
156 }
157
158 static const struct nand_controller_ops fun_ops = {
159 .exec_op = fun_exec_op,
160 };
161
fun_probe(struct platform_device * ofdev)162 static int fun_probe(struct platform_device *ofdev)
163 {
164 struct fsl_upm_nand *fun;
165 struct resource *io_res;
166 const __be32 *prop;
167 int ret;
168 int size;
169 int i;
170
171 fun = devm_kzalloc(&ofdev->dev, sizeof(*fun), GFP_KERNEL);
172 if (!fun)
173 return -ENOMEM;
174
175 io_res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
176 fun->io_base = devm_ioremap_resource(&ofdev->dev, io_res);
177 if (IS_ERR(fun->io_base))
178 return PTR_ERR(fun->io_base);
179
180 ret = fsl_upm_find(io_res->start, &fun->upm);
181 if (ret) {
182 dev_err(&ofdev->dev, "can't find UPM\n");
183 return ret;
184 }
185
186 prop = of_get_property(ofdev->dev.of_node, "fsl,upm-addr-offset",
187 &size);
188 if (!prop || size != sizeof(uint32_t)) {
189 dev_err(&ofdev->dev, "can't get UPM address offset\n");
190 return -EINVAL;
191 }
192 fun->upm_addr_offset = *prop;
193
194 prop = of_get_property(ofdev->dev.of_node, "fsl,upm-cmd-offset", &size);
195 if (!prop || size != sizeof(uint32_t)) {
196 dev_err(&ofdev->dev, "can't get UPM command offset\n");
197 return -EINVAL;
198 }
199 fun->upm_cmd_offset = *prop;
200
201 prop = of_get_property(ofdev->dev.of_node,
202 "fsl,upm-addr-line-cs-offsets", &size);
203 if (prop && (size / sizeof(uint32_t)) > 0) {
204 fun->mchip_count = size / sizeof(uint32_t);
205 if (fun->mchip_count >= NAND_MAX_CHIPS) {
206 dev_err(&ofdev->dev, "too much multiple chips\n");
207 return -EINVAL;
208 }
209 for (i = 0; i < fun->mchip_count; i++)
210 fun->mchip_offsets[i] = be32_to_cpu(prop[i]);
211 } else {
212 fun->mchip_count = 1;
213 }
214
215 for (i = 0; i < fun->mchip_count; i++) {
216 fun->rnb_gpio[i] = devm_gpiod_get_index_optional(&ofdev->dev,
217 NULL, i,
218 GPIOD_IN);
219 if (IS_ERR(fun->rnb_gpio[i])) {
220 dev_err(&ofdev->dev, "RNB gpio #%d is invalid\n", i);
221 return PTR_ERR(fun->rnb_gpio[i]);
222 }
223 }
224
225 nand_controller_init(&fun->base);
226 fun->base.ops = &fun_ops;
227 fun->dev = &ofdev->dev;
228
229 ret = fun_chip_init(fun, ofdev->dev.of_node, io_res);
230 if (ret)
231 return ret;
232
233 dev_set_drvdata(&ofdev->dev, fun);
234
235 return 0;
236 }
237
fun_remove(struct platform_device * ofdev)238 static int fun_remove(struct platform_device *ofdev)
239 {
240 struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev);
241 struct nand_chip *chip = &fun->chip;
242 struct mtd_info *mtd = nand_to_mtd(chip);
243 int ret;
244
245 ret = mtd_device_unregister(mtd);
246 WARN_ON(ret);
247 nand_cleanup(chip);
248
249 return 0;
250 }
251
252 static const struct of_device_id of_fun_match[] = {
253 { .compatible = "fsl,upm-nand" },
254 {},
255 };
256 MODULE_DEVICE_TABLE(of, of_fun_match);
257
258 static struct platform_driver of_fun_driver = {
259 .driver = {
260 .name = "fsl,upm-nand",
261 .of_match_table = of_fun_match,
262 },
263 .probe = fun_probe,
264 .remove = fun_remove,
265 };
266
267 module_platform_driver(of_fun_driver);
268
269 MODULE_LICENSE("GPL");
270 MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
271 MODULE_DESCRIPTION("Driver for NAND chips working through Freescale "
272 "LocalBus User-Programmable Machine");
273