]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
d7436188 BH |
2 | /* |
3 | * Copyright 2016 IBM Corporation. | |
d7436188 BH |
4 | */ |
5 | #include <linux/types.h> | |
6 | #include <linux/kernel.h> | |
7 | #include <linux/irq.h> | |
8 | #include <linux/smp.h> | |
9 | #include <linux/interrupt.h> | |
10 | #include <linux/cpu.h> | |
11 | #include <linux/of.h> | |
12 | ||
13 | #include <asm/smp.h> | |
14 | #include <asm/irq.h> | |
15 | #include <asm/errno.h> | |
16 | #include <asm/xics.h> | |
17 | #include <asm/io.h> | |
18 | #include <asm/opal.h> | |
9728a7c8 | 19 | #include <asm/kvm_ppc.h> |
d7436188 BH |
20 | |
21 | static void icp_opal_teardown_cpu(void) | |
22 | { | |
f8e33475 | 23 | int hw_cpu = hard_smp_processor_id(); |
d7436188 BH |
24 | |
25 | /* Clear any pending IPI */ | |
f8e33475 | 26 | opal_int_set_mfrr(hw_cpu, 0xff); |
d7436188 BH |
27 | } |
28 | ||
29 | static void icp_opal_flush_ipi(void) | |
30 | { | |
31 | /* | |
32 | * We take the ipi irq but and never return so we need to EOI the IPI, | |
33 | * but want to leave our priority 0. | |
34 | * | |
35 | * Should we check all the other interrupts too? | |
36 | * Should we be flagging idle loop instead? | |
37 | * Or creating some task to be scheduled? | |
38 | */ | |
9728a7c8 BH |
39 | if (opal_int_eoi((0x00 << 24) | XICS_IPI) > 0) |
40 | force_external_irq_replay(); | |
41 | } | |
42 | ||
43 | static unsigned int icp_opal_get_xirr(void) | |
44 | { | |
45 | unsigned int kvm_xirr; | |
46 | __be32 hw_xirr; | |
47 | int64_t rc; | |
48 | ||
49 | /* Handle an interrupt latched by KVM first */ | |
50 | kvm_xirr = kvmppc_get_xics_latch(); | |
51 | if (kvm_xirr) | |
52 | return kvm_xirr; | |
53 | ||
54 | /* Then ask OPAL */ | |
55 | rc = opal_int_get_xirr(&hw_xirr, false); | |
56 | if (rc < 0) | |
57 | return 0; | |
58 | return be32_to_cpu(hw_xirr); | |
d7436188 BH |
59 | } |
60 | ||
61 | static unsigned int icp_opal_get_irq(void) | |
62 | { | |
63 | unsigned int xirr; | |
64 | unsigned int vec; | |
65 | unsigned int irq; | |
d7436188 | 66 | |
9728a7c8 | 67 | xirr = icp_opal_get_xirr(); |
d7436188 BH |
68 | vec = xirr & 0x00ffffff; |
69 | if (vec == XICS_IRQ_SPURIOUS) | |
ef24ba70 | 70 | return 0; |
d7436188 BH |
71 | |
72 | irq = irq_find_mapping(xics_host, vec); | |
ef24ba70 | 73 | if (likely(irq)) { |
d7436188 BH |
74 | xics_push_cppr(vec); |
75 | return irq; | |
76 | } | |
77 | ||
78 | /* We don't have a linux mapping, so have rtas mask it. */ | |
79 | xics_mask_unknown_vec(vec); | |
80 | ||
81 | /* We might learn about it later, so EOI it */ | |
9728a7c8 BH |
82 | if (opal_int_eoi(xirr) > 0) |
83 | force_external_irq_replay(); | |
d7436188 | 84 | |
ef24ba70 | 85 | return 0; |
d7436188 BH |
86 | } |
87 | ||
88 | static void icp_opal_set_cpu_priority(unsigned char cppr) | |
89 | { | |
a69e2fb7 BS |
90 | /* |
91 | * Here be dragons. The caller has asked to allow only IPI's and not | |
92 | * external interrupts. But OPAL XIVE doesn't support that. So instead | |
93 | * of allowing no interrupts allow all. That's still not right, but | |
94 | * currently the only caller who does this is xics_migrate_irqs_away() | |
95 | * and it works in that case. | |
96 | */ | |
97 | if (cppr >= DEFAULT_PRIORITY) | |
98 | cppr = LOWEST_PRIORITY; | |
99 | ||
d7436188 BH |
100 | xics_set_base_cppr(cppr); |
101 | opal_int_set_cppr(cppr); | |
102 | iosync(); | |
103 | } | |
104 | ||
105 | static void icp_opal_eoi(struct irq_data *d) | |
106 | { | |
107 | unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); | |
108 | int64_t rc; | |
109 | ||
110 | iosync(); | |
111 | rc = opal_int_eoi((xics_pop_cppr() << 24) | hw_irq); | |
112 | ||
113 | /* | |
114 | * EOI tells us whether there are more interrupts to fetch. | |
115 | * | |
116 | * Some HW implementations might not be able to send us another | |
117 | * external interrupt in that case, so we force a replay. | |
118 | */ | |
119 | if (rc > 0) | |
120 | force_external_irq_replay(); | |
121 | } | |
122 | ||
123 | #ifdef CONFIG_SMP | |
124 | ||
b866cc21 | 125 | static void icp_opal_cause_ipi(int cpu) |
d7436188 | 126 | { |
f8e33475 BH |
127 | int hw_cpu = get_hard_smp_processor_id(cpu); |
128 | ||
f83e6862 | 129 | kvmppc_set_host_ipi(cpu, 1); |
f8e33475 | 130 | opal_int_set_mfrr(hw_cpu, IPI_PRIORITY); |
d7436188 BH |
131 | } |
132 | ||
133 | static irqreturn_t icp_opal_ipi_action(int irq, void *dev_id) | |
134 | { | |
f83e6862 | 135 | int cpu = smp_processor_id(); |
d7436188 | 136 | |
f83e6862 BH |
137 | kvmppc_set_host_ipi(cpu, 0); |
138 | opal_int_set_mfrr(get_hard_smp_processor_id(cpu), 0xff); | |
d7436188 BH |
139 | |
140 | return smp_ipi_demux(); | |
141 | } | |
142 | ||
9b256714 BH |
143 | /* |
144 | * Called when an interrupt is received on an off-line CPU to | |
145 | * clear the interrupt, so that the CPU can go back to nap mode. | |
146 | */ | |
147 | void icp_opal_flush_interrupt(void) | |
148 | { | |
149 | unsigned int xirr; | |
150 | unsigned int vec; | |
151 | ||
152 | do { | |
153 | xirr = icp_opal_get_xirr(); | |
154 | vec = xirr & 0x00ffffff; | |
155 | if (vec == XICS_IRQ_SPURIOUS) | |
156 | break; | |
157 | if (vec == XICS_IPI) { | |
158 | /* Clear pending IPI */ | |
159 | int cpu = smp_processor_id(); | |
160 | kvmppc_set_host_ipi(cpu, 0); | |
161 | opal_int_set_mfrr(get_hard_smp_processor_id(cpu), 0xff); | |
162 | } else { | |
163 | pr_err("XICS: hw interrupt 0x%x to offline cpu, " | |
164 | "disabling\n", vec); | |
165 | xics_mask_unknown_vec(vec); | |
166 | } | |
167 | ||
168 | /* EOI the interrupt */ | |
169 | } while (opal_int_eoi(xirr) > 0); | |
170 | } | |
171 | ||
d7436188 BH |
172 | #endif /* CONFIG_SMP */ |
173 | ||
174 | static const struct icp_ops icp_opal_ops = { | |
175 | .get_irq = icp_opal_get_irq, | |
176 | .eoi = icp_opal_eoi, | |
177 | .set_priority = icp_opal_set_cpu_priority, | |
178 | .teardown_cpu = icp_opal_teardown_cpu, | |
179 | .flush_ipi = icp_opal_flush_ipi, | |
180 | #ifdef CONFIG_SMP | |
181 | .ipi_action = icp_opal_ipi_action, | |
182 | .cause_ipi = icp_opal_cause_ipi, | |
183 | #endif | |
184 | }; | |
185 | ||
186 | int icp_opal_init(void) | |
187 | { | |
188 | struct device_node *np; | |
189 | ||
190 | np = of_find_compatible_node(NULL, NULL, "ibm,opal-intc"); | |
191 | if (!np) | |
192 | return -ENODEV; | |
193 | ||
194 | icp_ops = &icp_opal_ops; | |
195 | ||
196 | printk("XICS: Using OPAL ICP fallbacks\n"); | |
197 | ||
198 | return 0; | |
199 | } | |
200 |