diff options
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/Kconfig | 7 | ||||
-rw-r--r-- | arch/powerpc/include/asm/fsl_lbc.h | 33 | ||||
-rw-r--r-- | arch/powerpc/sysdev/fsl_lbc.c | 231 |
3 files changed, 228 insertions, 43 deletions
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 631e5a0fb6ab..44df1bac9701 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig | |||
@@ -687,9 +687,12 @@ config 4xx_SOC | |||
687 | bool | 687 | bool |
688 | 688 | ||
689 | config FSL_LBC | 689 | config FSL_LBC |
690 | bool | 690 | bool "Freescale Local Bus support" |
691 | depends on FSL_SOC | ||
691 | help | 692 | help |
692 | Freescale Localbus support | 693 | Enables reporting of errors from the Freescale local bus |
694 | controller. Also contains some common code used by | ||
695 | drivers for specific local bus peripherals. | ||
693 | 696 | ||
694 | config FSL_GTM | 697 | config FSL_GTM |
695 | bool | 698 | bool |
diff --git a/arch/powerpc/include/asm/fsl_lbc.h b/arch/powerpc/include/asm/fsl_lbc.h index 1b5a21041f9b..06a11124dde1 100644 --- a/arch/powerpc/include/asm/fsl_lbc.h +++ b/arch/powerpc/include/asm/fsl_lbc.h | |||
@@ -1,9 +1,10 @@ | |||
1 | /* Freescale Local Bus Controller | 1 | /* Freescale Local Bus Controller |
2 | * | 2 | * |
3 | * Copyright (c) 2006-2007 Freescale Semiconductor | 3 | * Copyright © 2006-2007, 2010 Freescale Semiconductor |
4 | * | 4 | * |
5 | * Authors: Nick Spence <nick.spence@freescale.com>, | 5 | * Authors: Nick Spence <nick.spence@freescale.com>, |
6 | * Scott Wood <scottwood@freescale.com> | 6 | * Scott Wood <scottwood@freescale.com> |
7 | * Jack Lan <jack.lan@freescale.com> | ||
7 | * | 8 | * |
8 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 10 | * it under the terms of the GNU General Public License as published by |
@@ -26,6 +27,8 @@ | |||
26 | #include <linux/compiler.h> | 27 | #include <linux/compiler.h> |
27 | #include <linux/types.h> | 28 | #include <linux/types.h> |
28 | #include <linux/io.h> | 29 | #include <linux/io.h> |
30 | #include <linux/device.h> | ||
31 | #include <linux/spinlock.h> | ||
29 | 32 | ||
30 | struct fsl_lbc_bank { | 33 | struct fsl_lbc_bank { |
31 | __be32 br; /**< Base Register */ | 34 | __be32 br; /**< Base Register */ |
@@ -125,13 +128,23 @@ struct fsl_lbc_regs { | |||
125 | #define LTESR_ATMW 0x00800000 | 128 | #define LTESR_ATMW 0x00800000 |
126 | #define LTESR_ATMR 0x00400000 | 129 | #define LTESR_ATMR 0x00400000 |
127 | #define LTESR_CS 0x00080000 | 130 | #define LTESR_CS 0x00080000 |
131 | #define LTESR_UPM 0x00000002 | ||
128 | #define LTESR_CC 0x00000001 | 132 | #define LTESR_CC 0x00000001 |
129 | #define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC) | 133 | #define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC) |
134 | #define LTESR_MASK (LTESR_BM | LTESR_FCT | LTESR_PAR | LTESR_WP \ | ||
135 | | LTESR_ATMW | LTESR_ATMR | LTESR_CS | LTESR_UPM \ | ||
136 | | LTESR_CC) | ||
137 | #define LTESR_CLEAR 0xFFFFFFFF | ||
138 | #define LTECCR_CLEAR 0xFFFFFFFF | ||
139 | #define LTESR_STATUS LTESR_MASK | ||
140 | #define LTEIR_ENABLE LTESR_MASK | ||
141 | #define LTEDR_ENABLE 0x00000000 | ||
130 | __be32 ltedr; /**< Transfer Error Disable Register */ | 142 | __be32 ltedr; /**< Transfer Error Disable Register */ |
131 | __be32 lteir; /**< Transfer Error Interrupt Register */ | 143 | __be32 lteir; /**< Transfer Error Interrupt Register */ |
132 | __be32 lteatr; /**< Transfer Error Attributes Register */ | 144 | __be32 lteatr; /**< Transfer Error Attributes Register */ |
133 | __be32 ltear; /**< Transfer Error Address Register */ | 145 | __be32 ltear; /**< Transfer Error Address Register */ |
134 | u8 res6[0xC]; | 146 | __be32 lteccr; /**< Transfer Error ECC Register */ |
147 | u8 res6[0x8]; | ||
135 | __be32 lbcr; /**< Configuration Register */ | 148 | __be32 lbcr; /**< Configuration Register */ |
136 | #define LBCR_LDIS 0x80000000 | 149 | #define LBCR_LDIS 0x80000000 |
137 | #define LBCR_LDIS_SHIFT 31 | 150 | #define LBCR_LDIS_SHIFT 31 |
@@ -265,7 +278,23 @@ static inline void fsl_upm_end_pattern(struct fsl_upm *upm) | |||
265 | cpu_relax(); | 278 | cpu_relax(); |
266 | } | 279 | } |
267 | 280 | ||
281 | /* overview of the fsl lbc controller */ | ||
282 | |||
283 | struct fsl_lbc_ctrl { | ||
284 | /* device info */ | ||
285 | struct device *dev; | ||
286 | struct fsl_lbc_regs __iomem *regs; | ||
287 | int irq; | ||
288 | wait_queue_head_t irq_wait; | ||
289 | spinlock_t lock; | ||
290 | void *nand; | ||
291 | |||
292 | /* status read from LTESR by irq handler */ | ||
293 | unsigned int irq_status; | ||
294 | }; | ||
295 | |||
268 | extern int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, | 296 | extern int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, |
269 | u32 mar); | 297 | u32 mar); |
298 | extern struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev; | ||
270 | 299 | ||
271 | #endif /* __ASM_FSL_LBC_H */ | 300 | #endif /* __ASM_FSL_LBC_H */ |
diff --git a/arch/powerpc/sysdev/fsl_lbc.c b/arch/powerpc/sysdev/fsl_lbc.c index dceb8d1a843d..91c9c53b1845 100644 --- a/arch/powerpc/sysdev/fsl_lbc.c +++ b/arch/powerpc/sysdev/fsl_lbc.c | |||
@@ -1,9 +1,12 @@ | |||
1 | /* | 1 | /* |
2 | * Freescale LBC and UPM routines. | 2 | * Freescale LBC and UPM routines. |
3 | * | 3 | * |
4 | * Copyright (c) 2007-2008 MontaVista Software, Inc. | 4 | * Copyright © 2007-2008 MontaVista Software, Inc. |
5 | * Copyright © 2010 Freescale Semiconductor | ||
5 | * | 6 | * |
6 | * Author: Anton Vorontsov <avorontsov@ru.mvista.com> | 7 | * Author: Anton Vorontsov <avorontsov@ru.mvista.com> |
8 | * Author: Jack Lan <Jack.Lan@freescale.com> | ||
9 | * Author: Roy Zang <tie-fei.zang@freescale.com> | ||
7 | * | 10 | * |
8 | * This program is free software; you can redistribute it and/or modify | 11 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 12 | * it under the terms of the GNU General Public License as published by |
@@ -19,39 +22,16 @@ | |||
19 | #include <linux/types.h> | 22 | #include <linux/types.h> |
20 | #include <linux/io.h> | 23 | #include <linux/io.h> |
21 | #include <linux/of.h> | 24 | #include <linux/of.h> |
25 | #include <linux/slab.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/interrupt.h> | ||
28 | #include <linux/mod_devicetable.h> | ||
22 | #include <asm/prom.h> | 29 | #include <asm/prom.h> |
23 | #include <asm/fsl_lbc.h> | 30 | #include <asm/fsl_lbc.h> |
24 | 31 | ||
25 | static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock); | 32 | static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock); |
26 | static struct fsl_lbc_regs __iomem *fsl_lbc_regs; | 33 | struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev; |
27 | 34 | EXPORT_SYMBOL(fsl_lbc_ctrl_dev); | |
28 | static char __initdata *compat_lbc[] = { | ||
29 | "fsl,pq2-localbus", | ||
30 | "fsl,pq2pro-localbus", | ||
31 | "fsl,pq3-localbus", | ||
32 | "fsl,elbc", | ||
33 | }; | ||
34 | |||
35 | static int __init fsl_lbc_init(void) | ||
36 | { | ||
37 | struct device_node *lbus; | ||
38 | int i; | ||
39 | |||
40 | for (i = 0; i < ARRAY_SIZE(compat_lbc); i++) { | ||
41 | lbus = of_find_compatible_node(NULL, NULL, compat_lbc[i]); | ||
42 | if (lbus) | ||
43 | goto found; | ||
44 | } | ||
45 | return -ENODEV; | ||
46 | |||
47 | found: | ||
48 | fsl_lbc_regs = of_iomap(lbus, 0); | ||
49 | of_node_put(lbus); | ||
50 | if (!fsl_lbc_regs) | ||
51 | return -ENOMEM; | ||
52 | return 0; | ||
53 | } | ||
54 | arch_initcall(fsl_lbc_init); | ||
55 | 35 | ||
56 | /** | 36 | /** |
57 | * fsl_lbc_find - find Localbus bank | 37 | * fsl_lbc_find - find Localbus bank |
@@ -65,13 +45,15 @@ arch_initcall(fsl_lbc_init); | |||
65 | int fsl_lbc_find(phys_addr_t addr_base) | 45 | int fsl_lbc_find(phys_addr_t addr_base) |
66 | { | 46 | { |
67 | int i; | 47 | int i; |
48 | struct fsl_lbc_regs __iomem *lbc; | ||
68 | 49 | ||
69 | if (!fsl_lbc_regs) | 50 | if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) |
70 | return -ENODEV; | 51 | return -ENODEV; |
71 | 52 | ||
72 | for (i = 0; i < ARRAY_SIZE(fsl_lbc_regs->bank); i++) { | 53 | lbc = fsl_lbc_ctrl_dev->regs; |
73 | __be32 br = in_be32(&fsl_lbc_regs->bank[i].br); | 54 | for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) { |
74 | __be32 or = in_be32(&fsl_lbc_regs->bank[i].or); | 55 | __be32 br = in_be32(&lbc->bank[i].br); |
56 | __be32 or = in_be32(&lbc->bank[i].or); | ||
75 | 57 | ||
76 | if (br & BR_V && (br & or & BR_BA) == addr_base) | 58 | if (br & BR_V && (br & or & BR_BA) == addr_base) |
77 | return i; | 59 | return i; |
@@ -94,22 +76,27 @@ int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm) | |||
94 | { | 76 | { |
95 | int bank; | 77 | int bank; |
96 | __be32 br; | 78 | __be32 br; |
79 | struct fsl_lbc_regs __iomem *lbc; | ||
97 | 80 | ||
98 | bank = fsl_lbc_find(addr_base); | 81 | bank = fsl_lbc_find(addr_base); |
99 | if (bank < 0) | 82 | if (bank < 0) |
100 | return bank; | 83 | return bank; |
101 | 84 | ||
102 | br = in_be32(&fsl_lbc_regs->bank[bank].br); | 85 | if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) |
86 | return -ENODEV; | ||
87 | |||
88 | lbc = fsl_lbc_ctrl_dev->regs; | ||
89 | br = in_be32(&lbc->bank[bank].br); | ||
103 | 90 | ||
104 | switch (br & BR_MSEL) { | 91 | switch (br & BR_MSEL) { |
105 | case BR_MS_UPMA: | 92 | case BR_MS_UPMA: |
106 | upm->mxmr = &fsl_lbc_regs->mamr; | 93 | upm->mxmr = &lbc->mamr; |
107 | break; | 94 | break; |
108 | case BR_MS_UPMB: | 95 | case BR_MS_UPMB: |
109 | upm->mxmr = &fsl_lbc_regs->mbmr; | 96 | upm->mxmr = &lbc->mbmr; |
110 | break; | 97 | break; |
111 | case BR_MS_UPMC: | 98 | case BR_MS_UPMC: |
112 | upm->mxmr = &fsl_lbc_regs->mcmr; | 99 | upm->mxmr = &lbc->mcmr; |
113 | break; | 100 | break; |
114 | default: | 101 | default: |
115 | return -EINVAL; | 102 | return -EINVAL; |
@@ -148,9 +135,12 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar) | |||
148 | int ret = 0; | 135 | int ret = 0; |
149 | unsigned long flags; | 136 | unsigned long flags; |
150 | 137 | ||
138 | if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) | ||
139 | return -ENODEV; | ||
140 | |||
151 | spin_lock_irqsave(&fsl_lbc_lock, flags); | 141 | spin_lock_irqsave(&fsl_lbc_lock, flags); |
152 | 142 | ||
153 | out_be32(&fsl_lbc_regs->mar, mar); | 143 | out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar); |
154 | 144 | ||
155 | switch (upm->width) { | 145 | switch (upm->width) { |
156 | case 8: | 146 | case 8: |
@@ -172,3 +162,166 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar) | |||
172 | return ret; | 162 | return ret; |
173 | } | 163 | } |
174 | EXPORT_SYMBOL(fsl_upm_run_pattern); | 164 | EXPORT_SYMBOL(fsl_upm_run_pattern); |
165 | |||
166 | static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl) | ||
167 | { | ||
168 | struct fsl_lbc_regs __iomem *lbc = ctrl->regs; | ||
169 | |||
170 | /* clear event registers */ | ||
171 | setbits32(&lbc->ltesr, LTESR_CLEAR); | ||
172 | out_be32(&lbc->lteatr, 0); | ||
173 | out_be32(&lbc->ltear, 0); | ||
174 | out_be32(&lbc->lteccr, LTECCR_CLEAR); | ||
175 | out_be32(&lbc->ltedr, LTEDR_ENABLE); | ||
176 | |||
177 | /* Enable interrupts for any detected events */ | ||
178 | out_be32(&lbc->lteir, LTEIR_ENABLE); | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * NOTE: This interrupt is used to report localbus events of various kinds, | ||
185 | * such as transaction errors on the chipselects. | ||
186 | */ | ||
187 | |||
188 | static irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data) | ||
189 | { | ||
190 | struct fsl_lbc_ctrl *ctrl = data; | ||
191 | struct fsl_lbc_regs __iomem *lbc = ctrl->regs; | ||
192 | u32 status; | ||
193 | |||
194 | status = in_be32(&lbc->ltesr); | ||
195 | if (!status) | ||
196 | return IRQ_NONE; | ||
197 | |||
198 | out_be32(&lbc->ltesr, LTESR_CLEAR); | ||
199 | out_be32(&lbc->lteatr, 0); | ||
200 | out_be32(&lbc->ltear, 0); | ||
201 | ctrl->irq_status = status; | ||
202 | |||
203 | if (status & LTESR_BM) | ||
204 | dev_err(ctrl->dev, "Local bus monitor time-out: " | ||
205 | "LTESR 0x%08X\n", status); | ||
206 | if (status & LTESR_WP) | ||
207 | dev_err(ctrl->dev, "Write protect error: " | ||
208 | "LTESR 0x%08X\n", status); | ||
209 | if (status & LTESR_ATMW) | ||
210 | dev_err(ctrl->dev, "Atomic write error: " | ||
211 | "LTESR 0x%08X\n", status); | ||
212 | if (status & LTESR_ATMR) | ||
213 | dev_err(ctrl->dev, "Atomic read error: " | ||
214 | "LTESR 0x%08X\n", status); | ||
215 | if (status & LTESR_CS) | ||
216 | dev_err(ctrl->dev, "Chip select error: " | ||
217 | "LTESR 0x%08X\n", status); | ||
218 | if (status & LTESR_UPM) | ||
219 | ; | ||
220 | if (status & LTESR_FCT) { | ||
221 | dev_err(ctrl->dev, "FCM command time-out: " | ||
222 | "LTESR 0x%08X\n", status); | ||
223 | smp_wmb(); | ||
224 | wake_up(&ctrl->irq_wait); | ||
225 | } | ||
226 | if (status & LTESR_PAR) { | ||
227 | dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: " | ||
228 | "LTESR 0x%08X\n", status); | ||
229 | smp_wmb(); | ||
230 | wake_up(&ctrl->irq_wait); | ||
231 | } | ||
232 | if (status & LTESR_CC) { | ||
233 | smp_wmb(); | ||
234 | wake_up(&ctrl->irq_wait); | ||
235 | } | ||
236 | if (status & ~LTESR_MASK) | ||
237 | dev_err(ctrl->dev, "Unknown error: " | ||
238 | "LTESR 0x%08X\n", status); | ||
239 | return IRQ_HANDLED; | ||
240 | } | ||
241 | |||
242 | /* | ||
243 | * fsl_lbc_ctrl_probe | ||
244 | * | ||
245 | * called by device layer when it finds a device matching | ||
246 | * one our driver can handled. This code allocates all of | ||
247 | * the resources needed for the controller only. The | ||
248 | * resources for the NAND banks themselves are allocated | ||
249 | * in the chip probe function. | ||
250 | */ | ||
251 | |||
252 | static int __devinit fsl_lbc_ctrl_probe(struct platform_device *dev) | ||
253 | { | ||
254 | int ret; | ||
255 | |||
256 | if (!dev->dev.of_node) { | ||
257 | dev_err(&dev->dev, "Device OF-Node is NULL"); | ||
258 | return -EFAULT; | ||
259 | } | ||
260 | |||
261 | fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL); | ||
262 | if (!fsl_lbc_ctrl_dev) | ||
263 | return -ENOMEM; | ||
264 | |||
265 | dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev); | ||
266 | |||
267 | spin_lock_init(&fsl_lbc_ctrl_dev->lock); | ||
268 | init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait); | ||
269 | |||
270 | fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0); | ||
271 | if (!fsl_lbc_ctrl_dev->regs) { | ||
272 | dev_err(&dev->dev, "failed to get memory region\n"); | ||
273 | ret = -ENODEV; | ||
274 | goto err; | ||
275 | } | ||
276 | |||
277 | fsl_lbc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0); | ||
278 | if (fsl_lbc_ctrl_dev->irq == NO_IRQ) { | ||
279 | dev_err(&dev->dev, "failed to get irq resource\n"); | ||
280 | ret = -ENODEV; | ||
281 | goto err; | ||
282 | } | ||
283 | |||
284 | fsl_lbc_ctrl_dev->dev = &dev->dev; | ||
285 | |||
286 | ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev); | ||
287 | if (ret < 0) | ||
288 | goto err; | ||
289 | |||
290 | ret = request_irq(fsl_lbc_ctrl_dev->irq, fsl_lbc_ctrl_irq, 0, | ||
291 | "fsl-lbc", fsl_lbc_ctrl_dev); | ||
292 | if (ret != 0) { | ||
293 | dev_err(&dev->dev, "failed to install irq (%d)\n", | ||
294 | fsl_lbc_ctrl_dev->irq); | ||
295 | ret = fsl_lbc_ctrl_dev->irq; | ||
296 | goto err; | ||
297 | } | ||
298 | |||
299 | return 0; | ||
300 | |||
301 | err: | ||
302 | iounmap(fsl_lbc_ctrl_dev->regs); | ||
303 | kfree(fsl_lbc_ctrl_dev); | ||
304 | return ret; | ||
305 | } | ||
306 | |||
307 | static const struct of_device_id fsl_lbc_match[] = { | ||
308 | { .compatible = "fsl,elbc", }, | ||
309 | { .compatible = "fsl,pq3-localbus", }, | ||
310 | { .compatible = "fsl,pq2-localbus", }, | ||
311 | { .compatible = "fsl,pq2pro-localbus", }, | ||
312 | {}, | ||
313 | }; | ||
314 | |||
315 | static struct platform_driver fsl_lbc_ctrl_driver = { | ||
316 | .driver = { | ||
317 | .name = "fsl-lbc", | ||
318 | .of_match_table = fsl_lbc_match, | ||
319 | }, | ||
320 | .probe = fsl_lbc_ctrl_probe, | ||
321 | }; | ||
322 | |||
323 | static int __init fsl_lbc_init(void) | ||
324 | { | ||
325 | return platform_driver_register(&fsl_lbc_ctrl_driver); | ||
326 | } | ||
327 | module_init(fsl_lbc_init); | ||