1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2011 Freescale Semiconductor, Inc |
4 | * |
5 | * Freescale Integrated Flash Controller |
6 | * |
7 | * Author: Dipen Dudhat <Dipen.Dudhat@freescale.com> |
8 | */ |
9 | #include <linux/module.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/compiler.h> |
12 | #include <linux/sched.h> |
13 | #include <linux/spinlock.h> |
14 | #include <linux/types.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/io.h> |
17 | #include <linux/of.h> |
18 | #include <linux/of_platform.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/fsl_ifc.h> |
21 | #include <linux/irqdomain.h> |
22 | #include <linux/of_address.h> |
23 | #include <linux/of_irq.h> |
24 | |
25 | struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev; |
26 | EXPORT_SYMBOL(fsl_ifc_ctrl_dev); |
27 | |
28 | /* |
29 | * convert_ifc_address - convert the base address |
30 | * @addr_base: base address of the memory bank |
31 | */ |
32 | unsigned int convert_ifc_address(phys_addr_t addr_base) |
33 | { |
34 | return addr_base & CSPR_BA; |
35 | } |
36 | EXPORT_SYMBOL(convert_ifc_address); |
37 | |
38 | /* |
39 | * fsl_ifc_find - find IFC bank |
40 | * @addr_base: base address of the memory bank |
41 | * |
42 | * This function walks IFC banks comparing "Base address" field of the CSPR |
43 | * registers with the supplied addr_base argument. When bases match this |
44 | * function returns bank number (starting with 0), otherwise it returns |
45 | * appropriate errno value. |
46 | */ |
47 | int fsl_ifc_find(phys_addr_t addr_base) |
48 | { |
49 | int i = 0; |
50 | |
51 | if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->gregs) |
52 | return -ENODEV; |
53 | |
54 | for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) { |
55 | u32 cspr = ifc_in32(addr: &fsl_ifc_ctrl_dev->gregs->cspr_cs[i].cspr); |
56 | |
57 | if (cspr & CSPR_V && (cspr & CSPR_BA) == |
58 | convert_ifc_address(addr_base)) |
59 | return i; |
60 | } |
61 | |
62 | return -ENOENT; |
63 | } |
64 | EXPORT_SYMBOL(fsl_ifc_find); |
65 | |
66 | static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl) |
67 | { |
68 | struct fsl_ifc_global __iomem *ifc = ctrl->gregs; |
69 | |
70 | /* |
71 | * Clear all the common status and event registers |
72 | */ |
73 | if (ifc_in32(addr: &ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER) |
74 | ifc_out32(IFC_CM_EVTER_STAT_CSER, addr: &ifc->cm_evter_stat); |
75 | |
76 | /* enable all error and events */ |
77 | ifc_out32(IFC_CM_EVTER_EN_CSEREN, addr: &ifc->cm_evter_en); |
78 | |
79 | /* enable all error and event interrupts */ |
80 | ifc_out32(IFC_CM_EVTER_INTR_EN_CSERIREN, addr: &ifc->cm_evter_intr_en); |
81 | ifc_out32(val: 0x0, addr: &ifc->cm_erattr0); |
82 | ifc_out32(val: 0x0, addr: &ifc->cm_erattr1); |
83 | |
84 | return 0; |
85 | } |
86 | |
87 | static int fsl_ifc_ctrl_remove(struct platform_device *dev) |
88 | { |
89 | struct fsl_ifc_ctrl *ctrl = dev_get_drvdata(dev: &dev->dev); |
90 | |
91 | of_platform_depopulate(parent: &dev->dev); |
92 | free_irq(ctrl->nand_irq, ctrl); |
93 | free_irq(ctrl->irq, ctrl); |
94 | |
95 | irq_dispose_mapping(virq: ctrl->nand_irq); |
96 | irq_dispose_mapping(virq: ctrl->irq); |
97 | |
98 | iounmap(addr: ctrl->gregs); |
99 | |
100 | dev_set_drvdata(dev: &dev->dev, NULL); |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | /* |
106 | * NAND events are split between an operational interrupt which only |
107 | * receives OPC, and an error interrupt that receives everything else, |
108 | * including non-NAND errors. Whichever interrupt gets to it first |
109 | * records the status and wakes the wait queue. |
110 | */ |
111 | static DEFINE_SPINLOCK(nand_irq_lock); |
112 | |
113 | static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl) |
114 | { |
115 | struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; |
116 | unsigned long flags; |
117 | u32 stat; |
118 | |
119 | spin_lock_irqsave(&nand_irq_lock, flags); |
120 | |
121 | stat = ifc_in32(addr: &ifc->ifc_nand.nand_evter_stat); |
122 | if (stat) { |
123 | ifc_out32(val: stat, addr: &ifc->ifc_nand.nand_evter_stat); |
124 | ctrl->nand_stat = stat; |
125 | wake_up(&ctrl->nand_wait); |
126 | } |
127 | |
128 | spin_unlock_irqrestore(lock: &nand_irq_lock, flags); |
129 | |
130 | return stat; |
131 | } |
132 | |
133 | static irqreturn_t fsl_ifc_nand_irq(int irqno, void *data) |
134 | { |
135 | struct fsl_ifc_ctrl *ctrl = data; |
136 | |
137 | if (check_nand_stat(ctrl)) |
138 | return IRQ_HANDLED; |
139 | |
140 | return IRQ_NONE; |
141 | } |
142 | |
143 | /* |
144 | * NOTE: This interrupt is used to report ifc events of various kinds, |
145 | * such as transaction errors on the chipselects. |
146 | */ |
147 | static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data) |
148 | { |
149 | struct fsl_ifc_ctrl *ctrl = data; |
150 | struct fsl_ifc_global __iomem *ifc = ctrl->gregs; |
151 | u32 err_axiid, err_srcid, status, cs_err, err_addr; |
152 | irqreturn_t ret = IRQ_NONE; |
153 | |
154 | /* read for chip select error */ |
155 | cs_err = ifc_in32(addr: &ifc->cm_evter_stat); |
156 | if (cs_err) { |
157 | dev_err(ctrl->dev, "transaction sent to IFC is not mapped to any memory bank 0x%08X\n" , |
158 | cs_err); |
159 | /* clear the chip select error */ |
160 | ifc_out32(IFC_CM_EVTER_STAT_CSER, addr: &ifc->cm_evter_stat); |
161 | |
162 | /* read error attribute registers print the error information */ |
163 | status = ifc_in32(addr: &ifc->cm_erattr0); |
164 | err_addr = ifc_in32(addr: &ifc->cm_erattr1); |
165 | |
166 | if (status & IFC_CM_ERATTR0_ERTYP_READ) |
167 | dev_err(ctrl->dev, "Read transaction error CM_ERATTR0 0x%08X\n" , |
168 | status); |
169 | else |
170 | dev_err(ctrl->dev, "Write transaction error CM_ERATTR0 0x%08X\n" , |
171 | status); |
172 | |
173 | err_axiid = (status & IFC_CM_ERATTR0_ERAID) >> |
174 | IFC_CM_ERATTR0_ERAID_SHIFT; |
175 | dev_err(ctrl->dev, "AXI ID of the error transaction 0x%08X\n" , |
176 | err_axiid); |
177 | |
178 | err_srcid = (status & IFC_CM_ERATTR0_ESRCID) >> |
179 | IFC_CM_ERATTR0_ESRCID_SHIFT; |
180 | dev_err(ctrl->dev, "SRC ID of the error transaction 0x%08X\n" , |
181 | err_srcid); |
182 | |
183 | dev_err(ctrl->dev, "Transaction Address corresponding to error ERADDR 0x%08X\n" , |
184 | err_addr); |
185 | |
186 | ret = IRQ_HANDLED; |
187 | } |
188 | |
189 | if (check_nand_stat(ctrl)) |
190 | ret = IRQ_HANDLED; |
191 | |
192 | return ret; |
193 | } |
194 | |
195 | /* |
196 | * fsl_ifc_ctrl_probe |
197 | * |
198 | * called by device layer when it finds a device matching |
199 | * one our driver can handled. This code allocates all of |
200 | * the resources needed for the controller only. The |
201 | * resources for the NAND banks themselves are allocated |
202 | * in the chip probe function. |
203 | */ |
204 | static int fsl_ifc_ctrl_probe(struct platform_device *dev) |
205 | { |
206 | int ret = 0; |
207 | int version, banks; |
208 | void __iomem *addr; |
209 | |
210 | dev_info(&dev->dev, "Freescale Integrated Flash Controller\n" ); |
211 | |
212 | fsl_ifc_ctrl_dev = devm_kzalloc(dev: &dev->dev, size: sizeof(*fsl_ifc_ctrl_dev), |
213 | GFP_KERNEL); |
214 | if (!fsl_ifc_ctrl_dev) |
215 | return -ENOMEM; |
216 | |
217 | dev_set_drvdata(dev: &dev->dev, data: fsl_ifc_ctrl_dev); |
218 | |
219 | /* IOMAP the entire IFC region */ |
220 | fsl_ifc_ctrl_dev->gregs = of_iomap(node: dev->dev.of_node, index: 0); |
221 | if (!fsl_ifc_ctrl_dev->gregs) { |
222 | dev_err(&dev->dev, "failed to get memory region\n" ); |
223 | return -ENODEV; |
224 | } |
225 | |
226 | if (of_property_read_bool(np: dev->dev.of_node, propname: "little-endian" )) { |
227 | fsl_ifc_ctrl_dev->little_endian = true; |
228 | dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n" ); |
229 | } else { |
230 | fsl_ifc_ctrl_dev->little_endian = false; |
231 | dev_dbg(&dev->dev, "IFC REGISTERS are BIG endian\n" ); |
232 | } |
233 | |
234 | version = ifc_in32(addr: &fsl_ifc_ctrl_dev->gregs->ifc_rev) & |
235 | FSL_IFC_VERSION_MASK; |
236 | |
237 | banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8; |
238 | dev_info(&dev->dev, "IFC version %d.%d, %d banks\n" , |
239 | version >> 24, (version >> 16) & 0xf, banks); |
240 | |
241 | fsl_ifc_ctrl_dev->version = version; |
242 | fsl_ifc_ctrl_dev->banks = banks; |
243 | |
244 | addr = fsl_ifc_ctrl_dev->gregs; |
245 | if (version >= FSL_IFC_VERSION_2_0_0) |
246 | addr += PGOFFSET_64K; |
247 | else |
248 | addr += PGOFFSET_4K; |
249 | fsl_ifc_ctrl_dev->rregs = addr; |
250 | |
251 | /* get the Controller level irq */ |
252 | fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(node: dev->dev.of_node, index: 0); |
253 | if (fsl_ifc_ctrl_dev->irq == 0) { |
254 | dev_err(&dev->dev, "failed to get irq resource for IFC\n" ); |
255 | ret = -ENODEV; |
256 | goto err; |
257 | } |
258 | |
259 | /* get the nand machine irq */ |
260 | fsl_ifc_ctrl_dev->nand_irq = |
261 | irq_of_parse_and_map(node: dev->dev.of_node, index: 1); |
262 | |
263 | fsl_ifc_ctrl_dev->dev = &dev->dev; |
264 | |
265 | ret = fsl_ifc_ctrl_init(ctrl: fsl_ifc_ctrl_dev); |
266 | if (ret < 0) |
267 | goto err_unmap_nandirq; |
268 | |
269 | init_waitqueue_head(&fsl_ifc_ctrl_dev->nand_wait); |
270 | |
271 | ret = request_irq(irq: fsl_ifc_ctrl_dev->irq, handler: fsl_ifc_ctrl_irq, IRQF_SHARED, |
272 | name: "fsl-ifc" , dev: fsl_ifc_ctrl_dev); |
273 | if (ret != 0) { |
274 | dev_err(&dev->dev, "failed to install irq (%d)\n" , |
275 | fsl_ifc_ctrl_dev->irq); |
276 | goto err_unmap_nandirq; |
277 | } |
278 | |
279 | if (fsl_ifc_ctrl_dev->nand_irq) { |
280 | ret = request_irq(irq: fsl_ifc_ctrl_dev->nand_irq, handler: fsl_ifc_nand_irq, |
281 | flags: 0, name: "fsl-ifc-nand" , dev: fsl_ifc_ctrl_dev); |
282 | if (ret != 0) { |
283 | dev_err(&dev->dev, "failed to install irq (%d)\n" , |
284 | fsl_ifc_ctrl_dev->nand_irq); |
285 | goto err_free_irq; |
286 | } |
287 | } |
288 | |
289 | /* legacy dts may still use "simple-bus" compatible */ |
290 | ret = of_platform_default_populate(root: dev->dev.of_node, NULL, parent: &dev->dev); |
291 | if (ret) |
292 | goto err_free_nandirq; |
293 | |
294 | return 0; |
295 | |
296 | err_free_nandirq: |
297 | free_irq(fsl_ifc_ctrl_dev->nand_irq, fsl_ifc_ctrl_dev); |
298 | err_free_irq: |
299 | free_irq(fsl_ifc_ctrl_dev->irq, fsl_ifc_ctrl_dev); |
300 | err_unmap_nandirq: |
301 | irq_dispose_mapping(virq: fsl_ifc_ctrl_dev->nand_irq); |
302 | irq_dispose_mapping(virq: fsl_ifc_ctrl_dev->irq); |
303 | err: |
304 | iounmap(addr: fsl_ifc_ctrl_dev->gregs); |
305 | return ret; |
306 | } |
307 | |
308 | static const struct of_device_id fsl_ifc_match[] = { |
309 | { |
310 | .compatible = "fsl,ifc" , |
311 | }, |
312 | {}, |
313 | }; |
314 | |
315 | static struct platform_driver fsl_ifc_ctrl_driver = { |
316 | .driver = { |
317 | .name = "fsl-ifc" , |
318 | .of_match_table = fsl_ifc_match, |
319 | }, |
320 | .probe = fsl_ifc_ctrl_probe, |
321 | .remove = fsl_ifc_ctrl_remove, |
322 | }; |
323 | |
324 | static int __init fsl_ifc_init(void) |
325 | { |
326 | return platform_driver_register(&fsl_ifc_ctrl_driver); |
327 | } |
328 | subsys_initcall(fsl_ifc_init); |
329 | |
330 | MODULE_AUTHOR("Freescale Semiconductor" ); |
331 | MODULE_DESCRIPTION("Freescale Integrated Flash Controller driver" ); |
332 | |