diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-05-19 23:43:47 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-05-19 23:43:47 -0400 |
commit | 3d07f0e83d4323d2cd45cc583f7cf1957aca3cac (patch) | |
tree | 279203d24b3a366ed6da93a3f9664409eb1a8488 /arch/powerpc/sysdev/mpic.c | |
parent | 593adf317cf165f7c66facf2285db9d4befbd1c0 (diff) | |
parent | bbfff72ee3e76bd4712b87386af00bfe97114bc9 (diff) |
Merge remote branch 'kumar/next' into next
Diffstat (limited to 'arch/powerpc/sysdev/mpic.c')
-rw-r--r-- | arch/powerpc/sysdev/mpic.c | 129 |
1 files changed, 122 insertions, 7 deletions
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 53121f625068..57e954142c70 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c | |||
@@ -6,6 +6,7 @@ | |||
6 | * with various broken implementations of this HW. | 6 | * with various broken implementations of this HW. |
7 | * | 7 | * |
8 | * Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp. | 8 | * Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp. |
9 | * Copyright 2010-2011 Freescale Semiconductor, Inc. | ||
9 | * | 10 | * |
10 | * This file is subject to the terms and conditions of the GNU General Public | 11 | * This file is subject to the terms and conditions of the GNU General Public |
11 | * License. See the file COPYING in the main directory of this archive | 12 | * License. See the file COPYING in the main directory of this archive |
@@ -218,6 +219,28 @@ static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 valu | |||
218 | _mpic_write(mpic->reg_type, &mpic->gregs, offset, value); | 219 | _mpic_write(mpic->reg_type, &mpic->gregs, offset, value); |
219 | } | 220 | } |
220 | 221 | ||
222 | static inline u32 _mpic_tm_read(struct mpic *mpic, unsigned int tm) | ||
223 | { | ||
224 | unsigned int offset = MPIC_INFO(TIMER_VECTOR_PRI) + | ||
225 | ((tm & 3) * MPIC_INFO(TIMER_STRIDE)); | ||
226 | |||
227 | if (tm >= 4) | ||
228 | offset += 0x1000 / 4; | ||
229 | |||
230 | return _mpic_read(mpic->reg_type, &mpic->tmregs, offset); | ||
231 | } | ||
232 | |||
233 | static inline void _mpic_tm_write(struct mpic *mpic, unsigned int tm, u32 value) | ||
234 | { | ||
235 | unsigned int offset = MPIC_INFO(TIMER_VECTOR_PRI) + | ||
236 | ((tm & 3) * MPIC_INFO(TIMER_STRIDE)); | ||
237 | |||
238 | if (tm >= 4) | ||
239 | offset += 0x1000 / 4; | ||
240 | |||
241 | _mpic_write(mpic->reg_type, &mpic->tmregs, offset, value); | ||
242 | } | ||
243 | |||
221 | static inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg) | 244 | static inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg) |
222 | { | 245 | { |
223 | unsigned int cpu = mpic_processor_id(mpic); | 246 | unsigned int cpu = mpic_processor_id(mpic); |
@@ -268,6 +291,8 @@ static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no, | |||
268 | #define mpic_write(b,r,v) _mpic_write(mpic->reg_type,&(b),(r),(v)) | 291 | #define mpic_write(b,r,v) _mpic_write(mpic->reg_type,&(b),(r),(v)) |
269 | #define mpic_ipi_read(i) _mpic_ipi_read(mpic,(i)) | 292 | #define mpic_ipi_read(i) _mpic_ipi_read(mpic,(i)) |
270 | #define mpic_ipi_write(i,v) _mpic_ipi_write(mpic,(i),(v)) | 293 | #define mpic_ipi_write(i,v) _mpic_ipi_write(mpic,(i),(v)) |
294 | #define mpic_tm_read(i) _mpic_tm_read(mpic,(i)) | ||
295 | #define mpic_tm_write(i,v) _mpic_tm_write(mpic,(i),(v)) | ||
271 | #define mpic_cpu_read(i) _mpic_cpu_read(mpic,(i)) | 296 | #define mpic_cpu_read(i) _mpic_cpu_read(mpic,(i)) |
272 | #define mpic_cpu_write(i,v) _mpic_cpu_write(mpic,(i),(v)) | 297 | #define mpic_cpu_write(i,v) _mpic_cpu_write(mpic,(i),(v)) |
273 | #define mpic_irq_read(s,r) _mpic_irq_read(mpic,(s),(r)) | 298 | #define mpic_irq_read(s,r) _mpic_irq_read(mpic,(s),(r)) |
@@ -624,6 +649,13 @@ static unsigned int mpic_is_ipi(struct mpic *mpic, unsigned int irq) | |||
624 | return (src >= mpic->ipi_vecs[0] && src <= mpic->ipi_vecs[3]); | 649 | return (src >= mpic->ipi_vecs[0] && src <= mpic->ipi_vecs[3]); |
625 | } | 650 | } |
626 | 651 | ||
652 | /* Determine if the linux irq is a timer */ | ||
653 | static unsigned int mpic_is_tm(struct mpic *mpic, unsigned int irq) | ||
654 | { | ||
655 | unsigned int src = virq_to_hw(irq); | ||
656 | |||
657 | return (src >= mpic->timer_vecs[0] && src <= mpic->timer_vecs[7]); | ||
658 | } | ||
627 | 659 | ||
628 | /* Convert a cpu mask from logical to physical cpu numbers. */ | 660 | /* Convert a cpu mask from logical to physical cpu numbers. */ |
629 | static inline u32 mpic_physmask(u32 cpumask) | 661 | static inline u32 mpic_physmask(u32 cpumask) |
@@ -810,6 +842,25 @@ static void mpic_end_ipi(struct irq_data *d) | |||
810 | 842 | ||
811 | #endif /* CONFIG_SMP */ | 843 | #endif /* CONFIG_SMP */ |
812 | 844 | ||
845 | static void mpic_unmask_tm(struct irq_data *d) | ||
846 | { | ||
847 | struct mpic *mpic = mpic_from_irq_data(d); | ||
848 | unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0]; | ||
849 | |||
850 | DBG("%s: enable_tm: %d (tm %d)\n", mpic->name, irq, src); | ||
851 | mpic_tm_write(src, mpic_tm_read(src) & ~MPIC_VECPRI_MASK); | ||
852 | mpic_tm_read(src); | ||
853 | } | ||
854 | |||
855 | static void mpic_mask_tm(struct irq_data *d) | ||
856 | { | ||
857 | struct mpic *mpic = mpic_from_irq_data(d); | ||
858 | unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0]; | ||
859 | |||
860 | mpic_tm_write(src, mpic_tm_read(src) | MPIC_VECPRI_MASK); | ||
861 | mpic_tm_read(src); | ||
862 | } | ||
863 | |||
813 | int mpic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, | 864 | int mpic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, |
814 | bool force) | 865 | bool force) |
815 | { | 866 | { |
@@ -936,6 +987,12 @@ static struct irq_chip mpic_ipi_chip = { | |||
936 | }; | 987 | }; |
937 | #endif /* CONFIG_SMP */ | 988 | #endif /* CONFIG_SMP */ |
938 | 989 | ||
990 | static struct irq_chip mpic_tm_chip = { | ||
991 | .irq_mask = mpic_mask_tm, | ||
992 | .irq_unmask = mpic_unmask_tm, | ||
993 | .irq_eoi = mpic_end_irq, | ||
994 | }; | ||
995 | |||
939 | #ifdef CONFIG_MPIC_U3_HT_IRQS | 996 | #ifdef CONFIG_MPIC_U3_HT_IRQS |
940 | static struct irq_chip mpic_irq_ht_chip = { | 997 | static struct irq_chip mpic_irq_ht_chip = { |
941 | .irq_startup = mpic_startup_ht_irq, | 998 | .irq_startup = mpic_startup_ht_irq, |
@@ -979,6 +1036,16 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq, | |||
979 | } | 1036 | } |
980 | #endif /* CONFIG_SMP */ | 1037 | #endif /* CONFIG_SMP */ |
981 | 1038 | ||
1039 | if (hw >= mpic->timer_vecs[0] && hw <= mpic->timer_vecs[7]) { | ||
1040 | WARN_ON(!(mpic->flags & MPIC_PRIMARY)); | ||
1041 | |||
1042 | DBG("mpic: mapping as timer\n"); | ||
1043 | irq_set_chip_data(virq, mpic); | ||
1044 | irq_set_chip_and_handler(virq, &mpic->hc_tm, | ||
1045 | handle_fasteoi_irq); | ||
1046 | return 0; | ||
1047 | } | ||
1048 | |||
982 | if (hw >= mpic->irq_count) | 1049 | if (hw >= mpic->irq_count) |
983 | return -EINVAL; | 1050 | return -EINVAL; |
984 | 1051 | ||
@@ -1019,6 +1086,7 @@ static int mpic_host_xlate(struct irq_host *h, struct device_node *ct, | |||
1019 | irq_hw_number_t *out_hwirq, unsigned int *out_flags) | 1086 | irq_hw_number_t *out_hwirq, unsigned int *out_flags) |
1020 | 1087 | ||
1021 | { | 1088 | { |
1089 | struct mpic *mpic = h->host_data; | ||
1022 | static unsigned char map_mpic_senses[4] = { | 1090 | static unsigned char map_mpic_senses[4] = { |
1023 | IRQ_TYPE_EDGE_RISING, | 1091 | IRQ_TYPE_EDGE_RISING, |
1024 | IRQ_TYPE_LEVEL_LOW, | 1092 | IRQ_TYPE_LEVEL_LOW, |
@@ -1027,7 +1095,38 @@ static int mpic_host_xlate(struct irq_host *h, struct device_node *ct, | |||
1027 | }; | 1095 | }; |
1028 | 1096 | ||
1029 | *out_hwirq = intspec[0]; | 1097 | *out_hwirq = intspec[0]; |
1030 | if (intsize > 1) { | 1098 | if (intsize >= 4 && (mpic->flags & MPIC_FSL)) { |
1099 | /* | ||
1100 | * Freescale MPIC with extended intspec: | ||
1101 | * First two cells are as usual. Third specifies | ||
1102 | * an "interrupt type". Fourth is type-specific data. | ||
1103 | * | ||
1104 | * See Documentation/devicetree/bindings/powerpc/fsl/mpic.txt | ||
1105 | */ | ||
1106 | switch (intspec[2]) { | ||
1107 | case 0: | ||
1108 | case 1: /* no EISR/EIMR support for now, treat as shared IRQ */ | ||
1109 | break; | ||
1110 | case 2: | ||
1111 | if (intspec[0] >= ARRAY_SIZE(mpic->ipi_vecs)) | ||
1112 | return -EINVAL; | ||
1113 | |||
1114 | *out_hwirq = mpic->ipi_vecs[intspec[0]]; | ||
1115 | break; | ||
1116 | case 3: | ||
1117 | if (intspec[0] >= ARRAY_SIZE(mpic->timer_vecs)) | ||
1118 | return -EINVAL; | ||
1119 | |||
1120 | *out_hwirq = mpic->timer_vecs[intspec[0]]; | ||
1121 | break; | ||
1122 | default: | ||
1123 | pr_debug("%s: unknown irq type %u\n", | ||
1124 | __func__, intspec[2]); | ||
1125 | return -EINVAL; | ||
1126 | } | ||
1127 | |||
1128 | *out_flags = map_mpic_senses[intspec[1] & 3]; | ||
1129 | } else if (intsize > 1) { | ||
1031 | u32 mask = 0x3; | 1130 | u32 mask = 0x3; |
1032 | 1131 | ||
1033 | /* Apple invented a new race of encoding on machines with | 1132 | /* Apple invented a new race of encoding on machines with |
@@ -1103,6 +1202,9 @@ struct mpic * __init mpic_alloc(struct device_node *node, | |||
1103 | mpic->hc_ipi.name = name; | 1202 | mpic->hc_ipi.name = name; |
1104 | #endif /* CONFIG_SMP */ | 1203 | #endif /* CONFIG_SMP */ |
1105 | 1204 | ||
1205 | mpic->hc_tm = mpic_tm_chip; | ||
1206 | mpic->hc_tm.name = name; | ||
1207 | |||
1106 | mpic->flags = flags; | 1208 | mpic->flags = flags; |
1107 | mpic->isu_size = isu_size; | 1209 | mpic->isu_size = isu_size; |
1108 | mpic->irq_count = irq_count; | 1210 | mpic->irq_count = irq_count; |
@@ -1113,10 +1215,14 @@ struct mpic * __init mpic_alloc(struct device_node *node, | |||
1113 | else | 1215 | else |
1114 | intvec_top = 255; | 1216 | intvec_top = 255; |
1115 | 1217 | ||
1116 | mpic->timer_vecs[0] = intvec_top - 8; | 1218 | mpic->timer_vecs[0] = intvec_top - 12; |
1117 | mpic->timer_vecs[1] = intvec_top - 7; | 1219 | mpic->timer_vecs[1] = intvec_top - 11; |
1118 | mpic->timer_vecs[2] = intvec_top - 6; | 1220 | mpic->timer_vecs[2] = intvec_top - 10; |
1119 | mpic->timer_vecs[3] = intvec_top - 5; | 1221 | mpic->timer_vecs[3] = intvec_top - 9; |
1222 | mpic->timer_vecs[4] = intvec_top - 8; | ||
1223 | mpic->timer_vecs[5] = intvec_top - 7; | ||
1224 | mpic->timer_vecs[6] = intvec_top - 6; | ||
1225 | mpic->timer_vecs[7] = intvec_top - 5; | ||
1120 | mpic->ipi_vecs[0] = intvec_top - 4; | 1226 | mpic->ipi_vecs[0] = intvec_top - 4; |
1121 | mpic->ipi_vecs[1] = intvec_top - 3; | 1227 | mpic->ipi_vecs[1] = intvec_top - 3; |
1122 | mpic->ipi_vecs[2] = intvec_top - 2; | 1228 | mpic->ipi_vecs[2] = intvec_top - 2; |
@@ -1126,6 +1232,8 @@ struct mpic * __init mpic_alloc(struct device_node *node, | |||
1126 | /* Check for "big-endian" in device-tree */ | 1232 | /* Check for "big-endian" in device-tree */ |
1127 | if (node && of_get_property(node, "big-endian", NULL) != NULL) | 1233 | if (node && of_get_property(node, "big-endian", NULL) != NULL) |
1128 | mpic->flags |= MPIC_BIG_ENDIAN; | 1234 | mpic->flags |= MPIC_BIG_ENDIAN; |
1235 | if (node && of_device_is_compatible(node, "fsl,mpic")) | ||
1236 | mpic->flags |= MPIC_FSL; | ||
1129 | 1237 | ||
1130 | /* Look for protected sources */ | 1238 | /* Look for protected sources */ |
1131 | if (node) { | 1239 | if (node) { |
@@ -1317,15 +1425,17 @@ void __init mpic_init(struct mpic *mpic) | |||
1317 | /* Set current processor priority to max */ | 1425 | /* Set current processor priority to max */ |
1318 | mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf); | 1426 | mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf); |
1319 | 1427 | ||
1320 | /* Initialize timers: just disable them all */ | 1428 | /* Initialize timers to our reserved vectors and mask them for now */ |
1321 | for (i = 0; i < 4; i++) { | 1429 | for (i = 0; i < 4; i++) { |
1322 | mpic_write(mpic->tmregs, | 1430 | mpic_write(mpic->tmregs, |
1323 | i * MPIC_INFO(TIMER_STRIDE) + | 1431 | i * MPIC_INFO(TIMER_STRIDE) + |
1324 | MPIC_INFO(TIMER_DESTINATION), 0); | 1432 | MPIC_INFO(TIMER_DESTINATION), |
1433 | 1 << hard_smp_processor_id()); | ||
1325 | mpic_write(mpic->tmregs, | 1434 | mpic_write(mpic->tmregs, |
1326 | i * MPIC_INFO(TIMER_STRIDE) + | 1435 | i * MPIC_INFO(TIMER_STRIDE) + |
1327 | MPIC_INFO(TIMER_VECTOR_PRI), | 1436 | MPIC_INFO(TIMER_VECTOR_PRI), |
1328 | MPIC_VECPRI_MASK | | 1437 | MPIC_VECPRI_MASK | |
1438 | (9 << MPIC_VECPRI_PRIORITY_SHIFT) | | ||
1329 | (mpic->timer_vecs[0] + i)); | 1439 | (mpic->timer_vecs[0] + i)); |
1330 | } | 1440 | } |
1331 | 1441 | ||
@@ -1434,6 +1544,11 @@ void mpic_irq_set_priority(unsigned int irq, unsigned int pri) | |||
1434 | ~MPIC_VECPRI_PRIORITY_MASK; | 1544 | ~MPIC_VECPRI_PRIORITY_MASK; |
1435 | mpic_ipi_write(src - mpic->ipi_vecs[0], | 1545 | mpic_ipi_write(src - mpic->ipi_vecs[0], |
1436 | reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); | 1546 | reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); |
1547 | } else if (mpic_is_tm(mpic, irq)) { | ||
1548 | reg = mpic_tm_read(src - mpic->timer_vecs[0]) & | ||
1549 | ~MPIC_VECPRI_PRIORITY_MASK; | ||
1550 | mpic_tm_write(src - mpic->timer_vecs[0], | ||
1551 | reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); | ||
1437 | } else { | 1552 | } else { |
1438 | reg = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) | 1553 | reg = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) |
1439 | & ~MPIC_VECPRI_PRIORITY_MASK; | 1554 | & ~MPIC_VECPRI_PRIORITY_MASK; |