diff options
Diffstat (limited to 'arch/powerpc/sysdev')
| -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 |
4 files changed, 215 insertions, 2 deletions
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 */ |
