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.h>
17#include <linux/platform_device.h>
18#include <linux/io.h>
19#include <linux/slab.h>
20#include <asm/fsl_lbc.h>
21
22struct fsl_upm_nand {
23 struct nand_controller base;
24 struct device *dev;
25 struct nand_chip chip;
26 struct fsl_upm upm;
27 uint8_t upm_addr_offset;
28 uint8_t upm_cmd_offset;
29 void __iomem *io_base;
30 struct gpio_desc *rnb_gpio[NAND_MAX_CHIPS];
31 uint32_t mchip_offsets[NAND_MAX_CHIPS];
32 uint32_t mchip_count;
33 uint32_t mchip_number;
34};
35
36static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo)
37{
38 return container_of(mtd_to_nand(mtdinfo), struct fsl_upm_nand,
39 chip);
40}
41
42static int fun_chip_init(struct fsl_upm_nand *fun,
43 const struct device_node *upm_np,
44 const struct resource *io_res)
45{
46 struct mtd_info *mtd = nand_to_mtd(chip: &fun->chip);
47 int ret;
48 struct device_node *flash_np;
49
50 fun->chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
51 fun->chip.ecc.algo = NAND_ECC_ALGO_HAMMING;
52 fun->chip.controller = &fun->base;
53 mtd->dev.parent = fun->dev;
54
55 flash_np = of_get_next_child(node: upm_np, NULL);
56 if (!flash_np)
57 return -ENODEV;
58
59 nand_set_flash_node(chip: &fun->chip, np: flash_np);
60 mtd->name = devm_kasprintf(dev: fun->dev, GFP_KERNEL, fmt: "0x%llx.%pOFn",
61 (u64)io_res->start,
62 flash_np);
63 if (!mtd->name) {
64 ret = -ENOMEM;
65 goto err;
66 }
67
68 ret = nand_scan(chip: &fun->chip, max_chips: fun->mchip_count);
69 if (ret)
70 goto err;
71
72 ret = mtd_device_register(mtd, NULL, 0);
73err:
74 of_node_put(node: flash_np);
75 return ret;
76}
77
78static int func_exec_instr(struct nand_chip *chip,
79 const struct nand_op_instr *instr)
80{
81 struct fsl_upm_nand *fun = to_fsl_upm_nand(mtdinfo: nand_to_mtd(chip));
82 u32 mar, reg_offs = fun->mchip_offsets[fun->mchip_number];
83 unsigned int i;
84 const u8 *out;
85 u8 *in;
86
87 switch (instr->type) {
88 case NAND_OP_CMD_INSTR:
89 fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset);
90 mar = (instr->ctx.cmd.opcode << (32 - fun->upm.width)) |
91 reg_offs;
92 fsl_upm_run_pattern(&fun->upm, fun->io_base + reg_offs, mar);
93 fsl_upm_end_pattern(&fun->upm);
94 return 0;
95
96 case NAND_OP_ADDR_INSTR:
97 fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset);
98 for (i = 0; i < instr->ctx.addr.naddrs; i++) {
99 mar = (instr->ctx.addr.addrs[i] << (32 - fun->upm.width)) |
100 reg_offs;
101 fsl_upm_run_pattern(&fun->upm, fun->io_base + reg_offs, mar);
102 }
103 fsl_upm_end_pattern(&fun->upm);
104 return 0;
105
106 case NAND_OP_DATA_IN_INSTR:
107 in = instr->ctx.data.buf.in;
108 for (i = 0; i < instr->ctx.data.len; i++)
109 in[i] = in_8(fun->io_base + reg_offs);
110 return 0;
111
112 case NAND_OP_DATA_OUT_INSTR:
113 out = instr->ctx.data.buf.out;
114 for (i = 0; i < instr->ctx.data.len; i++)
115 out_8(fun->io_base + reg_offs, out[i]);
116 return 0;
117
118 case NAND_OP_WAITRDY_INSTR:
119 if (!fun->rnb_gpio[fun->mchip_number])
120 return nand_soft_waitrdy(chip, timeout_ms: instr->ctx.waitrdy.timeout_ms);
121
122 return nand_gpio_waitrdy(chip, gpiod: fun->rnb_gpio[fun->mchip_number],
123 timeout_ms: instr->ctx.waitrdy.timeout_ms);
124
125 default:
126 return -EINVAL;
127 }
128
129 return 0;
130}
131
132static int fun_exec_op(struct nand_chip *chip, const struct nand_operation *op,
133 bool check_only)
134{
135 struct fsl_upm_nand *fun = to_fsl_upm_nand(mtdinfo: nand_to_mtd(chip));
136 unsigned int i;
137 int ret;
138
139 if (op->cs >= NAND_MAX_CHIPS)
140 return -EINVAL;
141
142 if (check_only)
143 return 0;
144
145 fun->mchip_number = op->cs;
146
147 for (i = 0; i < op->ninstrs; i++) {
148 ret = func_exec_instr(chip, instr: &op->instrs[i]);
149 if (ret)
150 return ret;
151
152 if (op->instrs[i].delay_ns)
153 ndelay(op->instrs[i].delay_ns);
154 }
155
156 return 0;
157}
158
159static const struct nand_controller_ops fun_ops = {
160 .exec_op = fun_exec_op,
161};
162
163static int fun_probe(struct platform_device *ofdev)
164{
165 struct fsl_upm_nand *fun;
166 struct resource *io_res;
167 const __be32 *prop;
168 int ret;
169 int size;
170 int i;
171
172 fun = devm_kzalloc(dev: &ofdev->dev, size: sizeof(*fun), GFP_KERNEL);
173 if (!fun)
174 return -ENOMEM;
175
176 fun->io_base = devm_platform_get_and_ioremap_resource(pdev: ofdev, index: 0, res: &io_res);
177 if (IS_ERR(ptr: fun->io_base))
178 return PTR_ERR(ptr: 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(node: ofdev->dev.of_node, name: "fsl,upm-addr-offset",
187 lenp: &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(node: ofdev->dev.of_node, name: "fsl,upm-cmd-offset", lenp: &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(node: ofdev->dev.of_node,
202 name: "fsl,upm-addr-line-cs-offsets", lenp: &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(dev: &ofdev->dev,
217 NULL, index: i,
218 flags: GPIOD_IN);
219 if (IS_ERR(ptr: fun->rnb_gpio[i])) {
220 dev_err(&ofdev->dev, "RNB gpio #%d is invalid\n", i);
221 return PTR_ERR(ptr: fun->rnb_gpio[i]);
222 }
223 }
224
225 nand_controller_init(nfc: &fun->base);
226 fun->base.ops = &fun_ops;
227 fun->dev = &ofdev->dev;
228
229 ret = fun_chip_init(fun, upm_np: ofdev->dev.of_node, io_res);
230 if (ret)
231 return ret;
232
233 dev_set_drvdata(dev: &ofdev->dev, data: fun);
234
235 return 0;
236}
237
238static void fun_remove(struct platform_device *ofdev)
239{
240 struct fsl_upm_nand *fun = dev_get_drvdata(dev: &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(master: mtd);
246 WARN_ON(ret);
247 nand_cleanup(chip);
248}
249
250static const struct of_device_id of_fun_match[] = {
251 { .compatible = "fsl,upm-nand" },
252 {},
253};
254MODULE_DEVICE_TABLE(of, of_fun_match);
255
256static struct platform_driver of_fun_driver = {
257 .driver = {
258 .name = "fsl,upm-nand",
259 .of_match_table = of_fun_match,
260 },
261 .probe = fun_probe,
262 .remove_new = fun_remove,
263};
264
265module_platform_driver(of_fun_driver);
266
267MODULE_LICENSE("GPL");
268MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
269MODULE_DESCRIPTION("Driver for NAND chips working through Freescale "
270 "LocalBus User-Programmable Machine");
271

source code of linux/drivers/mtd/nand/raw/fsl_upm.c