diff options
-rw-r--r-- | arch/powerpc/include/asm/mpic.h | 14 | ||||
-rw-r--r-- | arch/powerpc/sysdev/Makefile | 2 | ||||
-rw-r--r-- | arch/powerpc/sysdev/fsl_mpic_err.c | 149 | ||||
-rw-r--r-- | arch/powerpc/sysdev/mpic.c | 44 | ||||
-rw-r--r-- | arch/powerpc/sysdev/mpic.h | 22 |
5 files changed, 229 insertions, 2 deletions
diff --git a/arch/powerpc/include/asm/mpic.h b/arch/powerpc/include/asm/mpic.h index e14d35d572af..c0f9ef90f0b8 100644 --- a/arch/powerpc/include/asm/mpic.h +++ b/arch/powerpc/include/asm/mpic.h | |||
@@ -118,6 +118,9 @@ | |||
118 | #define MPIC_MAX_CPUS 32 | 118 | #define MPIC_MAX_CPUS 32 |
119 | #define MPIC_MAX_ISU 32 | 119 | #define MPIC_MAX_ISU 32 |
120 | 120 | ||
121 | #define MPIC_MAX_ERR 32 | ||
122 | #define MPIC_FSL_ERR_INT 16 | ||
123 | |||
121 | /* | 124 | /* |
122 | * Tsi108 implementation of MPIC has many differences from the original one | 125 | * Tsi108 implementation of MPIC has many differences from the original one |
123 | */ | 126 | */ |
@@ -270,6 +273,7 @@ struct mpic | |||
270 | struct irq_chip hc_ipi; | 273 | struct irq_chip hc_ipi; |
271 | #endif | 274 | #endif |
272 | struct irq_chip hc_tm; | 275 | struct irq_chip hc_tm; |
276 | struct irq_chip hc_err; | ||
273 | const char *name; | 277 | const char *name; |
274 | /* Flags */ | 278 | /* Flags */ |
275 | unsigned int flags; | 279 | unsigned int flags; |
@@ -283,6 +287,8 @@ struct mpic | |||
283 | /* vector numbers used for internal sources (ipi/timers) */ | 287 | /* vector numbers used for internal sources (ipi/timers) */ |
284 | unsigned int ipi_vecs[4]; | 288 | unsigned int ipi_vecs[4]; |
285 | unsigned int timer_vecs[8]; | 289 | unsigned int timer_vecs[8]; |
290 | /* vector numbers used for FSL MPIC error interrupts */ | ||
291 | unsigned int err_int_vecs[MPIC_MAX_ERR]; | ||
286 | 292 | ||
287 | /* Spurious vector to program into unused sources */ | 293 | /* Spurious vector to program into unused sources */ |
288 | unsigned int spurious_vec; | 294 | unsigned int spurious_vec; |
@@ -306,6 +312,9 @@ struct mpic | |||
306 | struct mpic_reg_bank cpuregs[MPIC_MAX_CPUS]; | 312 | struct mpic_reg_bank cpuregs[MPIC_MAX_CPUS]; |
307 | struct mpic_reg_bank isus[MPIC_MAX_ISU]; | 313 | struct mpic_reg_bank isus[MPIC_MAX_ISU]; |
308 | 314 | ||
315 | /* ioremap'ed base for error interrupt registers */ | ||
316 | u32 __iomem *err_regs; | ||
317 | |||
309 | /* Protected sources */ | 318 | /* Protected sources */ |
310 | unsigned long *protected; | 319 | unsigned long *protected; |
311 | 320 | ||
@@ -370,6 +379,11 @@ struct mpic | |||
370 | #define MPIC_NO_RESET 0x00004000 | 379 | #define MPIC_NO_RESET 0x00004000 |
371 | /* Freescale MPIC (compatible includes "fsl,mpic") */ | 380 | /* Freescale MPIC (compatible includes "fsl,mpic") */ |
372 | #define MPIC_FSL 0x00008000 | 381 | #define MPIC_FSL 0x00008000 |
382 | /* Freescale MPIC supports EIMR (error interrupt mask register). | ||
383 | * This flag is set for MPIC version >= 4.1 (version determined | ||
384 | * from the BRR1 register). | ||
385 | */ | ||
386 | #define MPIC_FSL_HAS_EIMR 0x00010000 | ||
373 | 387 | ||
374 | /* MPIC HW modification ID */ | 388 | /* MPIC HW modification ID */ |
375 | #define MPIC_REGSET_MASK 0xf0000000 | 389 | #define MPIC_REGSET_MASK 0xf0000000 |
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 1bd7ecb24620..a57600b3a4e3 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile | |||
@@ -15,7 +15,7 @@ obj-$(CONFIG_PPC_DCR_NATIVE) += dcr-low.o | |||
15 | obj-$(CONFIG_PPC_PMI) += pmi.o | 15 | obj-$(CONFIG_PPC_PMI) += pmi.o |
16 | obj-$(CONFIG_U3_DART) += dart_iommu.o | 16 | obj-$(CONFIG_U3_DART) += dart_iommu.o |
17 | obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o | 17 | obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o |
18 | obj-$(CONFIG_FSL_SOC) += fsl_soc.o | 18 | obj-$(CONFIG_FSL_SOC) += fsl_soc.o fsl_mpic_err.o |
19 | obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y) | 19 | obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y) |
20 | obj-$(CONFIG_FSL_PMC) += fsl_pmc.o | 20 | obj-$(CONFIG_FSL_PMC) += fsl_pmc.o |
21 | obj-$(CONFIG_FSL_LBC) += fsl_lbc.o | 21 | obj-$(CONFIG_FSL_LBC) += fsl_lbc.o |
diff --git a/arch/powerpc/sysdev/fsl_mpic_err.c b/arch/powerpc/sysdev/fsl_mpic_err.c new file mode 100644 index 000000000000..b83f32562a37 --- /dev/null +++ b/arch/powerpc/sysdev/fsl_mpic_err.c | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Freescale Semiconductor, Inc. | ||
3 | * | ||
4 | * Author: Varun Sethi <varun.sethi@freescale.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; version 2 of the | ||
9 | * License. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/irq.h> | ||
14 | #include <linux/smp.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | |||
17 | #include <asm/io.h> | ||
18 | #include <asm/irq.h> | ||
19 | #include <asm/mpic.h> | ||
20 | |||
21 | #include "mpic.h" | ||
22 | |||
23 | #define MPIC_ERR_INT_BASE 0x3900 | ||
24 | #define MPIC_ERR_INT_EISR 0x0000 | ||
25 | #define MPIC_ERR_INT_EIMR 0x0010 | ||
26 | |||
27 | static inline u32 mpic_fsl_err_read(u32 __iomem *base, unsigned int err_reg) | ||
28 | { | ||
29 | return in_be32(base + (err_reg >> 2)); | ||
30 | } | ||
31 | |||
32 | static inline void mpic_fsl_err_write(u32 __iomem *base, u32 value) | ||
33 | { | ||
34 | out_be32(base + (MPIC_ERR_INT_EIMR >> 2), value); | ||
35 | } | ||
36 | |||
37 | static void fsl_mpic_mask_err(struct irq_data *d) | ||
38 | { | ||
39 | u32 eimr; | ||
40 | struct mpic *mpic = irq_data_get_irq_chip_data(d); | ||
41 | unsigned int src = virq_to_hw(d->irq) - mpic->err_int_vecs[0]; | ||
42 | |||
43 | eimr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EIMR); | ||
44 | eimr |= (1 << (31 - src)); | ||
45 | mpic_fsl_err_write(mpic->err_regs, eimr); | ||
46 | } | ||
47 | |||
48 | static void fsl_mpic_unmask_err(struct irq_data *d) | ||
49 | { | ||
50 | u32 eimr; | ||
51 | struct mpic *mpic = irq_data_get_irq_chip_data(d); | ||
52 | unsigned int src = virq_to_hw(d->irq) - mpic->err_int_vecs[0]; | ||
53 | |||
54 | eimr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EIMR); | ||
55 | eimr &= ~(1 << (31 - src)); | ||
56 | mpic_fsl_err_write(mpic->err_regs, eimr); | ||
57 | } | ||
58 | |||
59 | static struct irq_chip fsl_mpic_err_chip = { | ||
60 | .irq_disable = fsl_mpic_mask_err, | ||
61 | .irq_mask = fsl_mpic_mask_err, | ||
62 | .irq_unmask = fsl_mpic_unmask_err, | ||
63 | }; | ||
64 | |||
65 | int mpic_setup_error_int(struct mpic *mpic, int intvec) | ||
66 | { | ||
67 | int i; | ||
68 | |||
69 | mpic->err_regs = ioremap(mpic->paddr + MPIC_ERR_INT_BASE, 0x1000); | ||
70 | if (!mpic->err_regs) { | ||
71 | pr_err("could not map mpic error registers\n"); | ||
72 | return -ENOMEM; | ||
73 | } | ||
74 | mpic->hc_err = fsl_mpic_err_chip; | ||
75 | mpic->hc_err.name = mpic->name; | ||
76 | mpic->flags |= MPIC_FSL_HAS_EIMR; | ||
77 | /* allocate interrupt vectors for error interrupts */ | ||
78 | for (i = MPIC_MAX_ERR - 1; i >= 0; i--) | ||
79 | mpic->err_int_vecs[i] = --intvec; | ||
80 | |||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | int mpic_map_error_int(struct mpic *mpic, unsigned int virq, irq_hw_number_t hw) | ||
85 | { | ||
86 | if ((mpic->flags & MPIC_FSL_HAS_EIMR) && | ||
87 | (hw >= mpic->err_int_vecs[0] && | ||
88 | hw <= mpic->err_int_vecs[MPIC_MAX_ERR - 1])) { | ||
89 | WARN_ON(mpic->flags & MPIC_SECONDARY); | ||
90 | |||
91 | pr_debug("mpic: mapping as Error Interrupt\n"); | ||
92 | irq_set_chip_data(virq, mpic); | ||
93 | irq_set_chip_and_handler(virq, &mpic->hc_err, | ||
94 | handle_level_irq); | ||
95 | return 1; | ||
96 | } | ||
97 | |||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | static irqreturn_t fsl_error_int_handler(int irq, void *data) | ||
102 | { | ||
103 | struct mpic *mpic = (struct mpic *) data; | ||
104 | u32 eisr, eimr; | ||
105 | int errint; | ||
106 | unsigned int cascade_irq; | ||
107 | |||
108 | eisr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EISR); | ||
109 | eimr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EIMR); | ||
110 | |||
111 | if (!(eisr & ~eimr)) | ||
112 | return IRQ_NONE; | ||
113 | |||
114 | while (eisr) { | ||
115 | errint = __builtin_clz(eisr); | ||
116 | cascade_irq = irq_linear_revmap(mpic->irqhost, | ||
117 | mpic->err_int_vecs[errint]); | ||
118 | WARN_ON(cascade_irq == NO_IRQ); | ||
119 | if (cascade_irq != NO_IRQ) { | ||
120 | generic_handle_irq(cascade_irq); | ||
121 | } else { | ||
122 | eimr |= 1 << (31 - errint); | ||
123 | mpic_fsl_err_write(mpic->err_regs, eimr); | ||
124 | } | ||
125 | eisr &= ~(1 << (31 - errint)); | ||
126 | } | ||
127 | |||
128 | return IRQ_HANDLED; | ||
129 | } | ||
130 | |||
131 | void mpic_err_int_init(struct mpic *mpic, irq_hw_number_t irqnum) | ||
132 | { | ||
133 | unsigned int virq; | ||
134 | int ret; | ||
135 | |||
136 | virq = irq_create_mapping(mpic->irqhost, irqnum); | ||
137 | if (virq == NO_IRQ) { | ||
138 | pr_err("Error interrupt setup failed\n"); | ||
139 | return; | ||
140 | } | ||
141 | |||
142 | /* Mask all error interrupts */ | ||
143 | mpic_fsl_err_write(mpic->err_regs, ~0); | ||
144 | |||
145 | ret = request_irq(virq, fsl_error_int_handler, IRQF_NO_THREAD, | ||
146 | "mpic-error-int", mpic); | ||
147 | if (ret) | ||
148 | pr_err("Failed to register error interrupt handler\n"); | ||
149 | } | ||
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 7e32db7e7b0d..9c6e535daad2 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c | |||
@@ -1026,6 +1026,9 @@ static int mpic_host_map(struct irq_domain *h, unsigned int virq, | |||
1026 | return 0; | 1026 | return 0; |
1027 | } | 1027 | } |
1028 | 1028 | ||
1029 | if (mpic_map_error_int(mpic, virq, hw)) | ||
1030 | return 0; | ||
1031 | |||
1029 | if (hw >= mpic->num_sources) | 1032 | if (hw >= mpic->num_sources) |
1030 | return -EINVAL; | 1033 | return -EINVAL; |
1031 | 1034 | ||
@@ -1085,7 +1088,16 @@ static int mpic_host_xlate(struct irq_domain *h, struct device_node *ct, | |||
1085 | */ | 1088 | */ |
1086 | switch (intspec[2]) { | 1089 | switch (intspec[2]) { |
1087 | case 0: | 1090 | case 0: |
1088 | case 1: /* no EISR/EIMR support for now, treat as shared IRQ */ | 1091 | break; |
1092 | case 1: | ||
1093 | if (!(mpic->flags & MPIC_FSL_HAS_EIMR)) | ||
1094 | break; | ||
1095 | |||
1096 | if (intspec[3] >= ARRAY_SIZE(mpic->err_int_vecs)) | ||
1097 | return -EINVAL; | ||
1098 | |||
1099 | *out_hwirq = mpic->err_int_vecs[intspec[3]]; | ||
1100 | |||
1089 | break; | 1101 | break; |
1090 | case 2: | 1102 | case 2: |
1091 | if (intspec[0] >= ARRAY_SIZE(mpic->ipi_vecs)) | 1103 | if (intspec[0] >= ARRAY_SIZE(mpic->ipi_vecs)) |
@@ -1302,6 +1314,9 @@ struct mpic * __init mpic_alloc(struct device_node *node, | |||
1302 | mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000); | 1314 | mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000); |
1303 | 1315 | ||
1304 | if (mpic->flags & MPIC_FSL) { | 1316 | if (mpic->flags & MPIC_FSL) { |
1317 | u32 brr1, version; | ||
1318 | int ret; | ||
1319 | |||
1305 | /* | 1320 | /* |
1306 | * Yes, Freescale really did put global registers in the | 1321 | * Yes, Freescale really did put global registers in the |
1307 | * magic per-cpu area -- and they don't even show up in the | 1322 | * magic per-cpu area -- and they don't even show up in the |
@@ -1309,6 +1324,29 @@ struct mpic * __init mpic_alloc(struct device_node *node, | |||
1309 | */ | 1324 | */ |
1310 | mpic_map(mpic, mpic->paddr, &mpic->thiscpuregs, | 1325 | mpic_map(mpic, mpic->paddr, &mpic->thiscpuregs, |
1311 | MPIC_CPU_THISBASE, 0x1000); | 1326 | MPIC_CPU_THISBASE, 0x1000); |
1327 | |||
1328 | brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs, | ||
1329 | MPIC_FSL_BRR1); | ||
1330 | version = brr1 & MPIC_FSL_BRR1_VER; | ||
1331 | |||
1332 | /* Error interrupt mask register (EIMR) is required for | ||
1333 | * handling individual device error interrupts. EIMR | ||
1334 | * was added in MPIC version 4.1. | ||
1335 | * | ||
1336 | * Over here we reserve vector number space for error | ||
1337 | * interrupt vectors. This space is stolen from the | ||
1338 | * global vector number space, as in case of ipis | ||
1339 | * and timer interrupts. | ||
1340 | * | ||
1341 | * Available vector space = intvec_top - 12, where 12 | ||
1342 | * is the number of vectors which have been consumed by | ||
1343 | * ipis and timer interrupts. | ||
1344 | */ | ||
1345 | if (version >= 0x401) { | ||
1346 | ret = mpic_setup_error_int(mpic, intvec_top - 12); | ||
1347 | if (ret) | ||
1348 | return NULL; | ||
1349 | } | ||
1312 | } | 1350 | } |
1313 | 1351 | ||
1314 | /* Reset */ | 1352 | /* Reset */ |
@@ -1474,6 +1512,10 @@ void __init mpic_init(struct mpic *mpic) | |||
1474 | num_timers = 8; | 1512 | num_timers = 8; |
1475 | } | 1513 | } |
1476 | 1514 | ||
1515 | /* FSL mpic error interrupt intialization */ | ||
1516 | if (mpic->flags & MPIC_FSL_HAS_EIMR) | ||
1517 | mpic_err_int_init(mpic, MPIC_FSL_ERR_INT); | ||
1518 | |||
1477 | /* Initialize timers to our reserved vectors and mask them for now */ | 1519 | /* Initialize timers to our reserved vectors and mask them for now */ |
1478 | for (i = 0; i < num_timers; i++) { | 1520 | for (i = 0; i < num_timers; i++) { |
1479 | unsigned int offset = mpic_tm_offset(mpic, i); | 1521 | unsigned int offset = mpic_tm_offset(mpic, i); |
diff --git a/arch/powerpc/sysdev/mpic.h b/arch/powerpc/sysdev/mpic.h index 13f3e8913a93..24bf07a63924 100644 --- a/arch/powerpc/sysdev/mpic.h +++ b/arch/powerpc/sysdev/mpic.h | |||
@@ -40,4 +40,26 @@ extern int mpic_set_affinity(struct irq_data *d, | |||
40 | const struct cpumask *cpumask, bool force); | 40 | const struct cpumask *cpumask, bool force); |
41 | extern void mpic_reset_core(int cpu); | 41 | extern void mpic_reset_core(int cpu); |
42 | 42 | ||
43 | #ifdef CONFIG_FSL_SOC | ||
44 | extern int mpic_map_error_int(struct mpic *mpic, unsigned int virq, irq_hw_number_t hw); | ||
45 | extern void mpic_err_int_init(struct mpic *mpic, irq_hw_number_t irqnum); | ||
46 | extern int mpic_setup_error_int(struct mpic *mpic, int intvec); | ||
47 | #else | ||
48 | static inline int mpic_map_error_int(struct mpic *mpic, unsigned int virq, irq_hw_number_t hw) | ||
49 | { | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | |||
54 | static inline void mpic_err_int_init(struct mpic *mpic, irq_hw_number_t irqnum) | ||
55 | { | ||
56 | return; | ||
57 | } | ||
58 | |||
59 | static inline int mpic_setup_error_int(struct mpic *mpic, int intvec) | ||
60 | { | ||
61 | return -1; | ||
62 | } | ||
63 | #endif | ||
64 | |||
43 | #endif /* _POWERPC_SYSDEV_MPIC_H */ | 65 | #endif /* _POWERPC_SYSDEV_MPIC_H */ |