aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/sysdev/xics/ics-rtas.c
diff options
context:
space:
mode:
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