aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/sysdev/xics/ics-rtas.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-04-03 23:46:58 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-04-19 21:02:35 -0400
commit0b05ac6e24807f0c26f763b3a546c0bcbf84125f (patch)
tree9c1a113a050583e564dcd78a7aa80fde6320d8e1 /arch/powerpc/sysdev/xics/ics-rtas.c
parentf0e615c3cb72b42191b558c130409335812621d8 (diff)
powerpc/xics: Rewrite XICS driver
This is a significant rework of the XICS driver, too significant to conveniently break it up into a series of smaller patches to be honest. The driver is moved to a more generic location to allow new platforms to use it, and is broken up into separate ICP and ICS "backends". For now we have the native and "hypervisor" ICP backends and one common RTAS ICS backend. The driver supports one ICP backend instanciation, and many ICS ones, in order to accomodate future platforms with multiple possibly different interrupt "sources" mechanisms. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/sysdev/xics/ics-rtas.c')
-rw-r--r--arch/powerpc/sysdev/xics/ics-rtas.c229
1 files changed, 229 insertions, 0 deletions
diff --git a/arch/powerpc/sysdev/xics/ics-rtas.c b/arch/powerpc/sysdev/xics/ics-rtas.c
new file mode 100644
index 000000000000..5b3ee387e89d
--- /dev/null
+++ b/arch/powerpc/sysdev/xics/ics-rtas.c
@@ -0,0 +1,229 @@
1#include <linux/types.h>
2#include <linux/kernel.h>
3#include <linux/irq.h>
4#include <linux/smp.h>
5#include <linux/interrupt.h>
6#include <linux/init.h>
7#include <linux/cpu.h>
8#include <linux/of.h>
9#include <linux/spinlock.h>
10#include <linux/msi.h>
11
12#include <asm/prom.h>
13#include <asm/smp.h>
14#include <asm/machdep.h>
15#include <asm/irq.h>
16#include <asm/errno.h>
17#include <asm/xics.h>
18#include <asm/rtas.h>
19
20/* RTAS service tokens */
21static int ibm_get_xive;
22static int ibm_set_xive;
23static int ibm_int_on;
24static int ibm_int_off;
25
26static int ics_rtas_map(struct ics *ics, unsigned int virq);
27static void ics_rtas_mask_unknown(struct ics *ics, unsigned long vec);
28static long ics_rtas_get_server(struct ics *ics, unsigned long vec);
29
30/* Only one global & state struct ics */
31static struct ics ics_rtas = {
32 .map = ics_rtas_map,
33 .mask_unknown = ics_rtas_mask_unknown,
34 .get_server = ics_rtas_get_server,
35};
36
37static void ics_rtas_unmask_irq(struct irq_data *d)
38{
39 unsigned int hw_irq = (unsigned int)irq_data_to_hw(d);
40 int call_status;
41 int server;
42
43 pr_devel("xics: unmask virq %d [hw 0x%x]\n", d->irq, hw_irq);
44
45 if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
46 return;
47
48 server = xics_get_irq_server(d->irq, d->affinity, 0);
49
50 call_status = rtas_call(ibm_set_xive, 3, 1, NULL, hw_irq, server,
51 DEFAULT_PRIORITY);
52 if (call_status != 0) {
53 printk(KERN_ERR
54 "%s: ibm_set_xive irq %u server %x returned %d\n",
55 __func__, hw_irq, server, call_status);
56 return;
57 }
58
59 /* Now unmask the interrupt (often a no-op) */
60 call_status = rtas_call(ibm_int_on, 1, 1, NULL, hw_irq);
61 if (call_status != 0) {
62 printk(KERN_ERR "%s: ibm_int_on irq=%u returned %d\n",
63 __func__, hw_irq, call_status);
64 return;
65 }
66}
67
68static unsigned int ics_rtas_startup(struct irq_data *d)
69{
70#ifdef CONFIG_PCI_MSI
71 /*
72 * The generic MSI code returns with the interrupt disabled on the
73 * card, using the MSI mask bits. Firmware doesn't appear to unmask
74 * at that level, so we do it here by hand.
75 */
76 if (d->msi_desc)
77 unmask_msi_irq(d);
78#endif
79 /* unmask it */
80 ics_rtas_unmask_irq(d);
81 return 0;
82}
83
84static void ics_rtas_mask_real_irq(unsigned int hw_irq)
85{
86 int call_status;
87
88 if (hw_irq == XICS_IPI)
89 return;
90
91 call_status = rtas_call(ibm_int_off, 1, 1, NULL, hw_irq);
92 if (call_status != 0) {
93 printk(KERN_ERR "%s: ibm_int_off irq=%u returned %d\n",
94 __func__, hw_irq, call_status);
95 return;
96 }
97
98 /* Have to set XIVE to 0xff to be able to remove a slot */
99 call_status = rtas_call(ibm_set_xive, 3, 1, NULL, hw_irq,
100 xics_default_server, 0xff);
101 if (call_status != 0) {
102 printk(KERN_ERR "%s: ibm_set_xive(0xff) irq=%u returned %d\n",
103 __func__, hw_irq, call_status);
104 return;
105 }
106}
107
108static void ics_rtas_mask_irq(struct irq_data *d)
109{
110 unsigned int hw_irq = (unsigned int)irq_data_to_hw(d);
111
112 pr_devel("xics: mask virq %d [hw 0x%x]\n", d->irq, hw_irq);
113
114 if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
115 return;
116 ics_rtas_mask_real_irq(hw_irq);
117}
118
119static int ics_rtas_set_affinity(struct irq_data *d,
120 const struct cpumask *cpumask,
121 bool force)
122{
123 unsigned int hw_irq = (unsigned int)irq_data_to_hw(d);
124 int status;
125 int xics_status[2];
126 int irq_server;
127
128 if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
129 return -1;
130
131 status = rtas_call(ibm_get_xive, 1, 3, xics_status, hw_irq);
132
133 if (status) {
134 printk(KERN_ERR "%s: ibm,get-xive irq=%u returns %d\n",
135 __func__, hw_irq, status);
136 return -1;
137 }
138
139 irq_server = xics_get_irq_server(d->irq, cpumask, 1);
140 if (irq_server == -1) {
141 char cpulist[128];
142 cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask);
143 printk(KERN_WARNING
144 "%s: No online cpus in the mask %s for irq %d\n",
145 __func__, cpulist, d->irq);
146 return -1;
147 }
148
149 status = rtas_call(ibm_set_xive, 3, 1, NULL,
150 hw_irq, irq_server, xics_status[1]);
151
152 if (status) {
153 printk(KERN_ERR "%s: ibm,set-xive irq=%u returns %d\n",
154 __func__, hw_irq, status);
155 return -1;
156 }
157
158 return IRQ_SET_MASK_OK;
159}
160
161static struct irq_chip ics_rtas_irq_chip = {
162 .name = "XICS",
163 .irq_startup = ics_rtas_startup,
164 .irq_mask = ics_rtas_mask_irq,
165 .irq_unmask = ics_rtas_unmask_irq,
166 .irq_eoi = NULL, /* Patched at init time */
167 .irq_set_affinity = ics_rtas_set_affinity
168};
169
170static int ics_rtas_map(struct ics *ics, unsigned int virq)
171{
172 unsigned int hw_irq = (unsigned int)irq_map[virq].hwirq;
173 int status[2];
174 int rc;
175
176 if (WARN_ON(hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS))
177 return -EINVAL;
178
179 /* Check if RTAS knows about this interrupt */
180 rc = rtas_call(ibm_get_xive, 1, 3, status, hw_irq);
181 if (rc)
182 return -ENXIO;
183
184 irq_set_chip_and_handler(virq, &ics_rtas_irq_chip, handle_fasteoi_irq);
185 irq_set_chip_data(virq, &ics_rtas);
186
187 return 0;
188}
189
190static void ics_rtas_mask_unknown(struct ics *ics, unsigned long vec)
191{
192 ics_rtas_mask_real_irq(vec);
193}
194
195static long ics_rtas_get_server(struct ics *ics, unsigned long vec)
196{
197 int rc, status[2];
198
199 rc = rtas_call(ibm_get_xive, 1, 3, status, vec);
200 if (rc)
201 return -1;
202 return status[0];
203}
204
205int ics_rtas_init(void)
206{
207 ibm_get_xive = rtas_token("ibm,get-xive");
208 ibm_set_xive = rtas_token("ibm,set-xive");
209 ibm_int_on = rtas_token("ibm,int-on");
210 ibm_int_off = rtas_token("ibm,int-off");
211
212 /* We enable the RTAS "ICS" if RTAS is present with the
213 * appropriate tokens
214 */
215 if (ibm_get_xive == RTAS_UNKNOWN_SERVICE ||
216 ibm_set_xive == RTAS_UNKNOWN_SERVICE)
217 return -ENODEV;
218
219 /* We need to patch our irq chip's EOI to point to the
220 * right ICP
221 */
222 ics_rtas_irq_chip.irq_eoi = icp_ops->eoi;
223
224 /* Register ourselves */
225 xics_register_ics(&ics_rtas);
226
227 return 0;
228}
229