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 void 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 | |
103 | /* |
104 | * NAND events are split between an operational interrupt which only |
105 | * receives OPC, and an error interrupt that receives everything else, |
106 | * including non-NAND errors. Whichever interrupt gets to it first |
107 | * records the status and wakes the wait queue. |
108 | */ |
109 | static DEFINE_SPINLOCK(nand_irq_lock); |
110 | |
111 | static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl) |
112 | { |
113 | struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; |
114 | unsigned long flags; |
115 | u32 stat; |
116 | |
117 | spin_lock_irqsave(&nand_irq_lock, flags); |
118 | |
119 | stat = ifc_in32(addr: &ifc->ifc_nand.nand_evter_stat); |
120 | if (stat) { |
121 | ifc_out32(val: stat, addr: &ifc->ifc_nand.nand_evter_stat); |
122 | ctrl->nand_stat = stat; |
123 | wake_up(&ctrl->nand_wait); |
124 | } |
125 | |
126 | spin_unlock_irqrestore(lock: &nand_irq_lock, flags); |
127 | |
128 | return stat; |
129 | } |
130 | |
131 | static irqreturn_t fsl_ifc_nand_irq(int irqno, void *data) |
132 | { |
133 | struct fsl_ifc_ctrl *ctrl = data; |
134 | |
135 | if (check_nand_stat(ctrl)) |
136 | return IRQ_HANDLED; |
137 | |
138 | return IRQ_NONE; |
139 | } |
140 | |
141 | /* |
142 | * NOTE: This interrupt is used to report ifc events of various kinds, |
143 | * such as transaction errors on the chipselects. |
144 | */ |
145 | static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data) |
146 | { |
147 | struct fsl_ifc_ctrl *ctrl = data; |
148 | struct fsl_ifc_global __iomem *ifc = ctrl->gregs; |
149 | u32 err_axiid, err_srcid, status, cs_err, err_addr; |
150 | irqreturn_t ret = IRQ_NONE; |
151 | |
152 | /* read for chip select error */ |
153 | cs_err = ifc_in32(addr: &ifc->cm_evter_stat); |
154 | if (cs_err) { |
155 | dev_err(ctrl->dev, "transaction sent to IFC is not mapped to any memory bank 0x%08X\n" , |
156 | cs_err); |
157 | /* clear the chip select error */ |
158 | ifc_out32(IFC_CM_EVTER_STAT_CSER, addr: &ifc->cm_evter_stat); |
159 | |
160 | /* read error attribute registers print the error information */ |
161 | status = ifc_in32(addr: &ifc->cm_erattr0); |
162 | err_addr = ifc_in32(addr: &ifc->cm_erattr1); |
163 | |
164 | if (status & IFC_CM_ERATTR0_ERTYP_READ) |
165 | dev_err(ctrl->dev, "Read transaction error CM_ERATTR0 0x%08X\n" , |
166 | status); |
167 | else |
168 | dev_err(ctrl->dev, "Write transaction error CM_ERATTR0 0x%08X\n" , |
169 | status); |
170 | |
171 | err_axiid = (status & IFC_CM_ERATTR0_ERAID) >> |
172 | IFC_CM_ERATTR0_ERAID_SHIFT; |
173 | dev_err(ctrl->dev, "AXI ID of the error transaction 0x%08X\n" , |
174 | err_axiid); |
175 | |
176 | err_srcid = (status & IFC_CM_ERATTR0_ESRCID) >> |
177 | IFC_CM_ERATTR0_ESRCID_SHIFT; |
178 | dev_err(ctrl->dev, "SRC ID of the error transaction 0x%08X\n" , |
179 | err_srcid); |
180 | |
181 | dev_err(ctrl->dev, "Transaction Address corresponding to error ERADDR 0x%08X\n" , |
182 | err_addr); |
183 | |
184 | ret = IRQ_HANDLED; |
185 | } |
186 | |
187 | if (check_nand_stat(ctrl)) |
188 | ret = IRQ_HANDLED; |
189 | |
190 | return ret; |
191 | } |
192 | |
193 | /* |
194 | * fsl_ifc_ctrl_probe |
195 | * |
196 | * called by device layer when it finds a device matching |
197 | * one our driver can handled. This code allocates all of |
198 | * the resources needed for the controller only. The |
199 | * resources for the NAND banks themselves are allocated |
200 | * in the chip probe function. |
201 | */ |
202 | static int fsl_ifc_ctrl_probe(struct platform_device *dev) |
203 | { |
204 | int ret = 0; |
205 | int version, banks; |
206 | void __iomem *addr; |
207 | |
208 | dev_info(&dev->dev, "Freescale Integrated Flash Controller\n" ); |
209 | |
210 | fsl_ifc_ctrl_dev = devm_kzalloc(dev: &dev->dev, size: sizeof(*fsl_ifc_ctrl_dev), |
211 | GFP_KERNEL); |
212 | if (!fsl_ifc_ctrl_dev) |
213 | return -ENOMEM; |
214 | |
215 | dev_set_drvdata(dev: &dev->dev, data: fsl_ifc_ctrl_dev); |
216 | |
217 | /* IOMAP the entire IFC region */ |
218 | fsl_ifc_ctrl_dev->gregs = of_iomap(node: dev->dev.of_node, index: 0); |
219 | if (!fsl_ifc_ctrl_dev->gregs) { |
220 | dev_err(&dev->dev, "failed to get memory region\n" ); |
221 | return -ENODEV; |
222 | } |
223 | |
224 | if (of_property_read_bool(np: dev->dev.of_node, propname: "little-endian" )) { |
225 | fsl_ifc_ctrl_dev->little_endian = true; |
226 | dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n" ); |
227 | } else { |
228 | fsl_ifc_ctrl_dev->little_endian = false; |
229 | dev_dbg(&dev->dev, "IFC REGISTERS are BIG endian\n" ); |
230 | } |
231 | |
232 | version = ifc_in32(addr: &fsl_ifc_ctrl_dev->gregs->ifc_rev) & |
233 | FSL_IFC_VERSION_MASK; |
234 | |
235 | banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8; |
236 | dev_info(&dev->dev, "IFC version %d.%d, %d banks\n" , |
237 | version >> 24, (version >> 16) & 0xf, banks); |
238 | |
239 | fsl_ifc_ctrl_dev->version = version; |
240 | fsl_ifc_ctrl_dev->banks = banks; |
241 | |
242 | addr = fsl_ifc_ctrl_dev->gregs; |
243 | if (version >= FSL_IFC_VERSION_2_0_0) |
244 | addr += PGOFFSET_64K; |
245 | else |
246 | addr += PGOFFSET_4K; |
247 | fsl_ifc_ctrl_dev->rregs = addr; |
248 | |
249 | /* get the Controller level irq */ |
250 | fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(node: dev->dev.of_node, index: 0); |
251 | if (fsl_ifc_ctrl_dev->irq == 0) { |
252 | dev_err(&dev->dev, "failed to get irq resource for IFC\n" ); |
253 | ret = -ENODEV; |
254 | goto err; |
255 | } |
256 | |
257 | /* get the nand machine irq */ |
258 | fsl_ifc_ctrl_dev->nand_irq = |
259 | irq_of_parse_and_map(node: dev->dev.of_node, index: 1); |
260 | |
261 | fsl_ifc_ctrl_dev->dev = &dev->dev; |
262 | |
263 | ret = fsl_ifc_ctrl_init(ctrl: fsl_ifc_ctrl_dev); |
264 | if (ret < 0) |
265 | goto err_unmap_nandirq; |
266 | |
267 | init_waitqueue_head(&fsl_ifc_ctrl_dev->nand_wait); |
268 | |
269 | ret = request_irq(irq: fsl_ifc_ctrl_dev->irq, handler: fsl_ifc_ctrl_irq, IRQF_SHARED, |
270 | name: "fsl-ifc" , dev: fsl_ifc_ctrl_dev); |
271 | if (ret != 0) { |
272 | dev_err(&dev->dev, "failed to install irq (%d)\n" , |
273 | fsl_ifc_ctrl_dev->irq); |
274 | goto err_unmap_nandirq; |
275 | } |
276 | |
277 | if (fsl_ifc_ctrl_dev->nand_irq) { |
278 | ret = request_irq(irq: fsl_ifc_ctrl_dev->nand_irq, handler: fsl_ifc_nand_irq, |
279 | flags: 0, name: "fsl-ifc-nand" , dev: fsl_ifc_ctrl_dev); |
280 | if (ret != 0) { |
281 | dev_err(&dev->dev, "failed to install irq (%d)\n" , |
282 | fsl_ifc_ctrl_dev->nand_irq); |
283 | goto err_free_irq; |
284 | } |
285 | } |
286 | |
287 | /* legacy dts may still use "simple-bus" compatible */ |
288 | ret = of_platform_default_populate(root: dev->dev.of_node, NULL, parent: &dev->dev); |
289 | if (ret) |
290 | goto err_free_nandirq; |
291 | |
292 | return 0; |
293 | |
294 | err_free_nandirq: |
295 | free_irq(fsl_ifc_ctrl_dev->nand_irq, fsl_ifc_ctrl_dev); |
296 | err_free_irq: |
297 | free_irq(fsl_ifc_ctrl_dev->irq, fsl_ifc_ctrl_dev); |
298 | err_unmap_nandirq: |
299 | irq_dispose_mapping(virq: fsl_ifc_ctrl_dev->nand_irq); |
300 | irq_dispose_mapping(virq: fsl_ifc_ctrl_dev->irq); |
301 | err: |
302 | iounmap(addr: fsl_ifc_ctrl_dev->gregs); |
303 | return ret; |
304 | } |
305 | |
306 | static const struct of_device_id fsl_ifc_match[] = { |
307 | { |
308 | .compatible = "fsl,ifc" , |
309 | }, |
310 | {}, |
311 | }; |
312 | |
313 | static struct platform_driver fsl_ifc_ctrl_driver = { |
314 | .driver = { |
315 | .name = "fsl-ifc" , |
316 | .of_match_table = fsl_ifc_match, |
317 | }, |
318 | .probe = fsl_ifc_ctrl_probe, |
319 | .remove_new = fsl_ifc_ctrl_remove, |
320 | }; |
321 | |
322 | static int __init fsl_ifc_init(void) |
323 | { |
324 | return platform_driver_register(&fsl_ifc_ctrl_driver); |
325 | } |
326 | subsys_initcall(fsl_ifc_init); |
327 | |
328 | MODULE_AUTHOR("Freescale Semiconductor" ); |
329 | MODULE_DESCRIPTION("Freescale Integrated Flash Controller driver" ); |
330 | |