diff options
Diffstat (limited to 'arch/mips/pmc-sierra/msp71xx/msp_irq_per.c')
-rw-r--r-- | arch/mips/pmc-sierra/msp71xx/msp_irq_per.c | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/arch/mips/pmc-sierra/msp71xx/msp_irq_per.c b/arch/mips/pmc-sierra/msp71xx/msp_irq_per.c new file mode 100644 index 00000000000..f9b9dcdfa9d --- /dev/null +++ b/arch/mips/pmc-sierra/msp71xx/msp_irq_per.c | |||
@@ -0,0 +1,135 @@ | |||
1 | /* | ||
2 | * Copyright 2010 PMC-Sierra, Inc, derived from irq_cpu.c | ||
3 | * | ||
4 | * This file define the irq handler for MSP PER subsystem interrupts. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the | ||
8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
9 | * option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/init.h> | ||
13 | #include <linux/interrupt.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/spinlock.h> | ||
16 | #include <linux/bitops.h> | ||
17 | |||
18 | #include <asm/mipsregs.h> | ||
19 | #include <asm/system.h> | ||
20 | |||
21 | #include <msp_cic_int.h> | ||
22 | #include <msp_regs.h> | ||
23 | |||
24 | |||
25 | /* | ||
26 | * Convenience Macro. Should be somewhere generic. | ||
27 | */ | ||
28 | #define get_current_vpe() \ | ||
29 | ((read_c0_tcbind() >> TCBIND_CURVPE_SHIFT) & TCBIND_CURVPE) | ||
30 | |||
31 | #ifdef CONFIG_SMP | ||
32 | /* | ||
33 | * The PER registers must be protected from concurrent access. | ||
34 | */ | ||
35 | |||
36 | static DEFINE_SPINLOCK(per_lock); | ||
37 | #endif | ||
38 | |||
39 | /* ensure writes to per are completed */ | ||
40 | |||
41 | static inline void per_wmb(void) | ||
42 | { | ||
43 | const volatile void __iomem *per_mem = PER_INT_MSK_REG; | ||
44 | volatile u32 dummy_read; | ||
45 | |||
46 | wmb(); | ||
47 | dummy_read = __raw_readl(per_mem); | ||
48 | dummy_read++; | ||
49 | } | ||
50 | |||
51 | static inline void unmask_per_irq(struct irq_data *d) | ||
52 | { | ||
53 | #ifdef CONFIG_SMP | ||
54 | unsigned long flags; | ||
55 | spin_lock_irqsave(&per_lock, flags); | ||
56 | *PER_INT_MSK_REG |= (1 << (d->irq - MSP_PER_INTBASE)); | ||
57 | spin_unlock_irqrestore(&per_lock, flags); | ||
58 | #else | ||
59 | *PER_INT_MSK_REG |= (1 << (d->irq - MSP_PER_INTBASE)); | ||
60 | #endif | ||
61 | per_wmb(); | ||
62 | } | ||
63 | |||
64 | static inline void mask_per_irq(struct irq_data *d) | ||
65 | { | ||
66 | #ifdef CONFIG_SMP | ||
67 | unsigned long flags; | ||
68 | spin_lock_irqsave(&per_lock, flags); | ||
69 | *PER_INT_MSK_REG &= ~(1 << (d->irq - MSP_PER_INTBASE)); | ||
70 | spin_unlock_irqrestore(&per_lock, flags); | ||
71 | #else | ||
72 | *PER_INT_MSK_REG &= ~(1 << (d->irq - MSP_PER_INTBASE)); | ||
73 | #endif | ||
74 | per_wmb(); | ||
75 | } | ||
76 | |||
77 | static inline void msp_per_irq_ack(struct irq_data *d) | ||
78 | { | ||
79 | mask_per_irq(d); | ||
80 | /* | ||
81 | * In the PER interrupt controller, only bits 11 and 10 | ||
82 | * are write-to-clear, (SPI TX complete, SPI RX complete). | ||
83 | * It does nothing for any others. | ||
84 | */ | ||
85 | *PER_INT_STS_REG = (1 << (d->irq - MSP_PER_INTBASE)); | ||
86 | } | ||
87 | |||
88 | #ifdef CONFIG_SMP | ||
89 | static int msp_per_irq_set_affinity(struct irq_data *d, | ||
90 | const struct cpumask *affinity, bool force) | ||
91 | { | ||
92 | /* WTF is this doing ????? */ | ||
93 | unmask_per_irq(d); | ||
94 | return 0; | ||
95 | } | ||
96 | #endif | ||
97 | |||
98 | static struct irq_chip msp_per_irq_controller = { | ||
99 | .name = "MSP_PER", | ||
100 | .irq_enable = unmask_per_irq. | ||
101 | .irq_disable = mask_per_irq, | ||
102 | .irq_ack = msp_per_irq_ack, | ||
103 | #ifdef CONFIG_SMP | ||
104 | .irq_set_affinity = msp_per_irq_set_affinity, | ||
105 | #endif | ||
106 | }; | ||
107 | |||
108 | void __init msp_per_irq_init(void) | ||
109 | { | ||
110 | int i; | ||
111 | /* Mask/clear interrupts. */ | ||
112 | *PER_INT_MSK_REG = 0x00000000; | ||
113 | *PER_INT_STS_REG = 0xFFFFFFFF; | ||
114 | /* initialize all the IRQ descriptors */ | ||
115 | for (i = MSP_PER_INTBASE; i < MSP_PER_INTBASE + 32; i++) { | ||
116 | irq_set_chip(i, &msp_per_irq_controller); | ||
117 | #ifdef CONFIG_MIPS_MT_SMTC | ||
118 | irq_hwmask[i] = C_IRQ4; | ||
119 | #endif | ||
120 | } | ||
121 | } | ||
122 | |||
123 | void msp_per_irq_dispatch(void) | ||
124 | { | ||
125 | u32 per_mask = *PER_INT_MSK_REG; | ||
126 | u32 per_status = *PER_INT_STS_REG; | ||
127 | u32 pending; | ||
128 | |||
129 | pending = per_status & per_mask; | ||
130 | if (pending) { | ||
131 | do_IRQ(ffs(pending) + MSP_PER_INTBASE - 1); | ||
132 | } else { | ||
133 | spurious_interrupt(); | ||
134 | } | ||
135 | } | ||