aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVarun Sethi <Varun.Sethi@freescale.com>2012-08-08 00:06:09 -0400
committerKumar Gala <galak@kernel.crashing.org>2012-09-12 15:57:10 -0400
commit0a4081641d722d3dee140505a86330ecf44db0fe (patch)
treef516459f8da722251d432c5bae2d544da2ab33e4
parent688ba1dbee8a49a2efe507cd9ae69634d92bb640 (diff)
powerpc/mpic: FSL MPIC error interrupt support.
All SOC device error interrupts are muxed and delivered to the core as a single MPIC error interrupt. Currently all the device drivers requiring access to device errors have to register for the MPIC error interrupt as a shared interrupt. With this patch we add interrupt demuxing capability in the mpic driver, allowing device drivers to register for their individual error interrupts. This is achieved by handling error interrupts in a cascaded fashion. MPIC error interrupt is handled by the "error_int_handler", which subsequently demuxes it using the EISR and delivers it to the respective drivers. The error interrupt capability is dependent on the MPIC EIMR register, which was introduced in FSL MPIC version 4.1 (P4080 rev2). So, error interrupt demuxing capability is dependent on the MPIC version and can be used for versions >= 4.1. Signed-off-by: Varun Sethi <Varun.Sethi@freescale.com> Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@freescale.com> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
-rw-r--r--arch/powerpc/include/asm/mpic.h14
-rw-r--r--arch/powerpc/sysdev/Makefile2
-rw-r--r--arch/powerpc/sysdev/fsl_mpic_err.c149
-rw-r--r--arch/powerpc/sysdev/mpic.c44
-rw-r--r--arch/powerpc/sysdev/mpic.h22
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
15obj-$(CONFIG_PPC_PMI) += pmi.o 15obj-$(CONFIG_PPC_PMI) += pmi.o
16obj-$(CONFIG_U3_DART) += dart_iommu.o 16obj-$(CONFIG_U3_DART) += dart_iommu.o
17obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o 17obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o
18obj-$(CONFIG_FSL_SOC) += fsl_soc.o 18obj-$(CONFIG_FSL_SOC) += fsl_soc.o fsl_mpic_err.o
19obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y) 19obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y)
20obj-$(CONFIG_FSL_PMC) += fsl_pmc.o 20obj-$(CONFIG_FSL_PMC) += fsl_pmc.o
21obj-$(CONFIG_FSL_LBC) += fsl_lbc.o 21obj-$(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
27static inline u32 mpic_fsl_err_read(u32 __iomem *base, unsigned int err_reg)
28{
29 return in_be32(base + (err_reg >> 2));
30}
31
32static inline void mpic_fsl_err_write(u32 __iomem *base, u32 value)
33{
34 out_be32(base + (MPIC_ERR_INT_EIMR >> 2), value);
35}
36
37static 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
48static 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
59static 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
65int 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
84int 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
101static 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
131void 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);
41extern void mpic_reset_core(int cpu); 41extern void mpic_reset_core(int cpu);
42 42
43#ifdef CONFIG_FSL_SOC
44extern int mpic_map_error_int(struct mpic *mpic, unsigned int virq, irq_hw_number_t hw);
45extern void mpic_err_int_init(struct mpic *mpic, irq_hw_number_t irqnum);
46extern int mpic_setup_error_int(struct mpic *mpic, int intvec);
47#else
48static inline int mpic_map_error_int(struct mpic *mpic, unsigned int virq, irq_hw_number_t hw)
49{
50 return 0;
51}
52
53
54static inline void mpic_err_int_init(struct mpic *mpic, irq_hw_number_t irqnum)
55{
56 return;
57}
58
59static 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 */