Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Varun Sethi and committed by
Kumar Gala
0a408164 688ba1db

+229 -2
+14
arch/powerpc/include/asm/mpic.h
··· 118 118 #define MPIC_MAX_CPUS 32 119 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 125 * Tsi108 implementation of MPIC has many differences from the original one 123 126 */ ··· 273 270 struct irq_chip hc_ipi; 274 271 #endif 275 272 struct irq_chip hc_tm; 273 + struct irq_chip hc_err; 276 274 const char *name; 277 275 /* Flags */ 278 276 unsigned int flags; ··· 287 283 /* vector numbers used for internal sources (ipi/timers) */ 288 284 unsigned int ipi_vecs[4]; 289 285 unsigned int timer_vecs[8]; 286 + /* vector numbers used for FSL MPIC error interrupts */ 287 + unsigned int err_int_vecs[MPIC_MAX_ERR]; 290 288 291 289 /* Spurious vector to program into unused sources */ 292 290 unsigned int spurious_vec; ··· 311 305 struct mpic_reg_bank tmregs; 312 306 struct mpic_reg_bank cpuregs[MPIC_MAX_CPUS]; 313 307 struct mpic_reg_bank isus[MPIC_MAX_ISU]; 308 + 309 + /* ioremap'ed base for error interrupt registers */ 310 + u32 __iomem *err_regs; 314 311 315 312 /* Protected sources */ 316 313 unsigned long *protected; ··· 379 370 #define MPIC_NO_RESET 0x00004000 380 371 /* Freescale MPIC (compatible includes "fsl,mpic") */ 381 372 #define MPIC_FSL 0x00008000 373 + /* Freescale MPIC supports EIMR (error interrupt mask register). 374 + * This flag is set for MPIC version >= 4.1 (version determined 375 + * from the BRR1 register). 376 + */ 377 + #define MPIC_FSL_HAS_EIMR 0x00010000 382 378 383 379 /* MPIC HW modification ID */ 384 380 #define MPIC_REGSET_MASK 0xf0000000
+1 -1
arch/powerpc/sysdev/Makefile
··· 15 15 obj-$(CONFIG_PPC_PMI) += pmi.o 16 16 obj-$(CONFIG_U3_DART) += dart_iommu.o 17 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 19 obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y) 20 20 obj-$(CONFIG_FSL_PMC) += fsl_pmc.o 21 21 obj-$(CONFIG_FSL_LBC) += fsl_lbc.o
+149
arch/powerpc/sysdev/fsl_mpic_err.c
··· 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 + }
+43 -1
arch/powerpc/sysdev/mpic.c
··· 1026 1026 return 0; 1027 1027 } 1028 1028 1029 + if (mpic_map_error_int(mpic, virq, hw)) 1030 + return 0; 1031 + 1029 1032 if (hw >= mpic->num_sources) 1030 1033 return -EINVAL; 1031 1034 ··· 1088 1085 */ 1089 1086 switch (intspec[2]) { 1090 1087 case 0: 1091 - case 1: /* no EISR/EIMR support for now, treat as shared IRQ */ 1088 + break; 1089 + case 1: 1090 + if (!(mpic->flags & MPIC_FSL_HAS_EIMR)) 1091 + break; 1092 + 1093 + if (intspec[3] >= ARRAY_SIZE(mpic->err_int_vecs)) 1094 + return -EINVAL; 1095 + 1096 + *out_hwirq = mpic->err_int_vecs[intspec[3]]; 1097 + 1092 1098 break; 1093 1099 case 2: 1094 1100 if (intspec[0] >= ARRAY_SIZE(mpic->ipi_vecs)) ··· 1314 1302 mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000); 1315 1303 1316 1304 if (mpic->flags & MPIC_FSL) { 1305 + u32 brr1, version; 1306 + int ret; 1307 + 1317 1308 /* 1318 1309 * Yes, Freescale really did put global registers in the 1319 1310 * magic per-cpu area -- and they don't even show up in the ··· 1324 1309 */ 1325 1310 mpic_map(mpic, mpic->paddr, &mpic->thiscpuregs, 1326 1311 MPIC_CPU_THISBASE, 0x1000); 1312 + 1313 + brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs, 1314 + MPIC_FSL_BRR1); 1315 + version = brr1 & MPIC_FSL_BRR1_VER; 1316 + 1317 + /* Error interrupt mask register (EIMR) is required for 1318 + * handling individual device error interrupts. EIMR 1319 + * was added in MPIC version 4.1. 1320 + * 1321 + * Over here we reserve vector number space for error 1322 + * interrupt vectors. This space is stolen from the 1323 + * global vector number space, as in case of ipis 1324 + * and timer interrupts. 1325 + * 1326 + * Available vector space = intvec_top - 12, where 12 1327 + * is the number of vectors which have been consumed by 1328 + * ipis and timer interrupts. 1329 + */ 1330 + if (version >= 0x401) { 1331 + ret = mpic_setup_error_int(mpic, intvec_top - 12); 1332 + if (ret) 1333 + return NULL; 1334 + } 1327 1335 } 1328 1336 1329 1337 /* Reset */ ··· 1511 1473 if (version >= 0x0301) 1512 1474 num_timers = 8; 1513 1475 } 1476 + 1477 + /* FSL mpic error interrupt intialization */ 1478 + if (mpic->flags & MPIC_FSL_HAS_EIMR) 1479 + mpic_err_int_init(mpic, MPIC_FSL_ERR_INT); 1514 1480 1515 1481 /* Initialize timers to our reserved vectors and mask them for now */ 1516 1482 for (i = 0; i < num_timers; i++) {
+22
arch/powerpc/sysdev/mpic.h
··· 40 40 const struct cpumask *cpumask, bool force); 41 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 65 #endif /* _POWERPC_SYSDEV_MPIC_H */