aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/sysdev/xics/icp-hv.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/icp-hv.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/icp-hv.c')
-rw-r--r--arch/powerpc/sysdev/xics/icp-hv.c184
1 files changed, 184 insertions, 0 deletions
diff --git a/arch/powerpc/sysdev/xics/icp-hv.c b/arch/powerpc/sysdev/xics/icp-hv.c
new file mode 100644
index 000000000000..b03d348b19a5
--- /dev/null
+++ b/arch/powerpc/sysdev/xics/icp-hv.c
@@ -0,0 +1,184 @@
1/*
2 * Copyright 2011 IBM Corporation.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 */
10#include <linux/types.h>
11#include <linux/kernel.h>
12#include <linux/irq.h>
13#include <linux/smp.h>
14#include <linux/interrupt.h>
15#include <linux/init.h>
16#include <linux/cpu.h>
17#include <linux/of.h>
18
19#include <asm/smp.h>
20#include <asm/irq.h>
21#include <asm/errno.h>
22#include <asm/xics.h>
23#include <asm/io.h>
24#include <asm/hvcall.h>
25
26static inline unsigned int icp_hv_get_xirr(unsigned char cppr)
27{
28 unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
29 long rc;
30
31 rc = plpar_hcall(H_XIRR, retbuf, cppr);
32 if (rc != H_SUCCESS)
33 panic(" bad return code xirr - rc = %lx\n", rc);
34 return (unsigned int)retbuf[0];
35}
36
37static inline void icp_hv_set_xirr(unsigned int value)
38{
39 long rc = plpar_hcall_norets(H_EOI, value);
40 if (rc != H_SUCCESS)
41 panic("bad return code EOI - rc = %ld, value=%x\n", rc, value);
42}
43
44static inline void icp_hv_set_cppr(u8 value)
45{
46 long rc = plpar_hcall_norets(H_CPPR, value);
47 if (rc != H_SUCCESS)
48 panic("bad return code cppr - rc = %lx\n", rc);
49}
50
51static inline void icp_hv_set_qirr(int n_cpu , u8 value)
52{
53 long rc = plpar_hcall_norets(H_IPI, get_hard_smp_processor_id(n_cpu),
54 value);
55 if (rc != H_SUCCESS)
56 panic("bad return code qirr - rc = %lx\n", rc);
57}
58
59static void icp_hv_eoi(struct irq_data *d)
60{
61 unsigned int hw_irq = (unsigned int)irq_data_to_hw(d);
62
63 iosync();
64 icp_hv_set_xirr((xics_pop_cppr() << 24) | hw_irq);
65}
66
67static void icp_hv_teardown_cpu(void)
68{
69 int cpu = smp_processor_id();
70
71 /* Clear any pending IPI */
72 icp_hv_set_qirr(cpu, 0xff);
73}
74
75static void icp_hv_flush_ipi(void)
76{
77 /* We take the ipi irq but and never return so we
78 * need to EOI the IPI, but want to leave our priority 0
79 *
80 * should we check all the other interrupts too?
81 * should we be flagging idle loop instead?
82 * or creating some task to be scheduled?
83 */
84
85 icp_hv_set_xirr((0x00 << 24) | XICS_IPI);
86}
87
88static unsigned int icp_hv_get_irq(void)
89{
90 unsigned int xirr = icp_hv_get_xirr(xics_cppr_top());
91 unsigned int vec = xirr & 0x00ffffff;
92 unsigned int irq;
93
94 if (vec == XICS_IRQ_SPURIOUS)
95 return NO_IRQ;
96
97 irq = irq_radix_revmap_lookup(xics_host, vec);
98 if (likely(irq != NO_IRQ)) {
99 xics_push_cppr(vec);
100 return irq;
101 }
102
103 /* We don't have a linux mapping, so have rtas mask it. */
104 xics_mask_unknown_vec(vec);
105
106 /* We might learn about it later, so EOI it */
107 icp_hv_set_xirr(xirr);
108
109 return NO_IRQ;
110}
111
112static void icp_hv_set_cpu_priority(unsigned char cppr)
113{
114 xics_set_base_cppr(cppr);
115 icp_hv_set_cppr(cppr);
116 iosync();
117}
118
119#ifdef CONFIG_SMP
120
121static inline void icp_hv_do_message(int cpu, int msg)
122{
123 unsigned long *tgt = &per_cpu(xics_ipi_message, cpu);
124
125 set_bit(msg, tgt);
126 mb();
127 icp_hv_set_qirr(cpu, IPI_PRIORITY);
128}
129
130static void icp_hv_message_pass(int target, int msg)
131{
132 unsigned int i;
133
134 if (target < NR_CPUS) {
135 icp_hv_do_message(target, msg);
136 } else {
137 for_each_online_cpu(i) {
138 if (target == MSG_ALL_BUT_SELF
139 && i == smp_processor_id())
140 continue;
141 icp_hv_do_message(i, msg);
142 }
143 }
144}
145
146static irqreturn_t icp_hv_ipi_action(int irq, void *dev_id)
147{
148 int cpu = smp_processor_id();
149
150 icp_hv_set_qirr(cpu, 0xff);
151
152 return xics_ipi_dispatch(cpu);
153}
154
155#endif /* CONFIG_SMP */
156
157static const struct icp_ops icp_hv_ops = {
158 .get_irq = icp_hv_get_irq,
159 .eoi = icp_hv_eoi,
160 .set_priority = icp_hv_set_cpu_priority,
161 .teardown_cpu = icp_hv_teardown_cpu,
162 .flush_ipi = icp_hv_flush_ipi,
163#ifdef CONFIG_SMP
164 .ipi_action = icp_hv_ipi_action,
165 .message_pass = icp_hv_message_pass,
166#endif
167};
168
169int icp_hv_init(void)
170{
171 struct device_node *np;
172
173 np = of_find_compatible_node(NULL, NULL, "ibm,ppc-xicp");
174 if (!np)
175 np = of_find_node_by_type(NULL,
176 "PowerPC-External-Interrupt-Presentation");
177 if (!np)
178 return -ENODEV;
179
180 icp_ops = &icp_hv_ops;
181
182 return 0;
183}
184