Merge tag 'block-5.11-2021-01-10' of git://git.kernel.dk/linux-block
[linux/fpc-iii.git] / drivers / mtd / nand / raw / fsl_upm.c
blobb3cc427100a22904cab8b0569e1581fe8ab63489
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Freescale UPM NAND driver.
5 * Copyright © 2007-2008 MontaVista Software, Inc.
7 * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
8 */
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>
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;
35 static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo)
37 return container_of(mtd_to_nand(mtdinfo), struct fsl_upm_nand,
38 chip);
41 static int fun_chip_init(struct fsl_upm_nand *fun,
42 const struct device_node *upm_np,
43 const struct resource *io_res)
45 struct mtd_info *mtd = nand_to_mtd(&fun->chip);
46 int ret;
47 struct device_node *flash_np;
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;
54 flash_np = of_get_next_child(upm_np, NULL);
55 if (!flash_np)
56 return -ENODEV;
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;
67 ret = nand_scan(&fun->chip, fun->mchip_count);
68 if (ret)
69 goto err;
71 ret = mtd_device_register(mtd, NULL, 0);
72 err:
73 of_node_put(flash_np);
74 return ret;
77 static int func_exec_instr(struct nand_chip *chip,
78 const struct nand_op_instr *instr)
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;
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;
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);
102 fsl_upm_end_pattern(&fun->upm);
103 return 0;
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;
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;
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);
121 return nand_gpio_waitrdy(chip, fun->rnb_gpio[fun->mchip_number],
122 instr->ctx.waitrdy.timeout_ms);
124 default:
125 return -EINVAL;
128 return 0;
131 static int fun_exec_op(struct nand_chip *chip, const struct nand_operation *op,
132 bool check_only)
134 struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
135 unsigned int i;
136 int ret;
138 if (op->cs > NAND_MAX_CHIPS)
139 return -EINVAL;
141 if (check_only)
142 return 0;
144 fun->mchip_number = op->cs;
146 for (i = 0; i < op->ninstrs; i++) {
147 ret = func_exec_instr(chip, &op->instrs[i]);
148 if (ret)
149 return ret;
151 if (op->instrs[i].delay_ns)
152 ndelay(op->instrs[i].delay_ns);
155 return 0;
158 static const struct nand_controller_ops fun_ops = {
159 .exec_op = fun_exec_op,
162 static int fun_probe(struct platform_device *ofdev)
164 struct fsl_upm_nand *fun;
165 struct resource *io_res;
166 const __be32 *prop;
167 int ret;
168 int size;
169 int i;
171 fun = devm_kzalloc(&ofdev->dev, sizeof(*fun), GFP_KERNEL);
172 if (!fun)
173 return -ENOMEM;
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);
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;
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;
192 fun->upm_addr_offset = *prop;
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;
199 fun->upm_cmd_offset = *prop;
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;
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;
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]);
225 nand_controller_init(&fun->base);
226 fun->base.ops = &fun_ops;
227 fun->dev = &ofdev->dev;
229 ret = fun_chip_init(fun, ofdev->dev.of_node, io_res);
230 if (ret)
231 return ret;
233 dev_set_drvdata(&ofdev->dev, fun);
235 return 0;
238 static int fun_remove(struct platform_device *ofdev)
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;
245 ret = mtd_device_unregister(mtd);
246 WARN_ON(ret);
247 nand_cleanup(chip);
249 return 0;
252 static const struct of_device_id of_fun_match[] = {
253 { .compatible = "fsl,upm-nand" },
256 MODULE_DEVICE_TABLE(of, of_fun_match);
258 static struct platform_driver of_fun_driver = {
259 .driver = {
260 .name = "fsl,upm-nand",
261 .of_match_table = of_fun_match,
263 .probe = fun_probe,
264 .remove = fun_remove,
267 module_platform_driver(of_fun_driver);
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");