Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/platforms/powernv/opal-irqchip.c
26481 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* This file implements an irqchip for OPAL events. Whenever there is
4
* an interrupt that is handled by OPAL we get passed a list of events
5
* that Linux needs to do something about. These basically look like
6
* interrupts to Linux so we implement an irqchip to handle them.
7
*
8
* Copyright Alistair Popple, IBM Corporation 2014.
9
*/
10
#include <linux/bitops.h>
11
#include <linux/irq.h>
12
#include <linux/irqchip.h>
13
#include <linux/irqdomain.h>
14
#include <linux/interrupt.h>
15
#include <linux/module.h>
16
#include <linux/of.h>
17
#include <linux/platform_device.h>
18
#include <linux/kthread.h>
19
#include <linux/delay.h>
20
#include <linux/slab.h>
21
#include <linux/of_irq.h>
22
23
#include <asm/machdep.h>
24
#include <asm/opal.h>
25
26
#include "powernv.h"
27
28
/* Maximum number of events supported by OPAL firmware */
29
#define MAX_NUM_EVENTS 64
30
31
struct opal_event_irqchip {
32
struct irq_chip irqchip;
33
struct irq_domain *domain;
34
unsigned long mask;
35
};
36
static struct opal_event_irqchip opal_event_irqchip;
37
static u64 last_outstanding_events;
38
static int opal_irq_count;
39
static struct resource *opal_irqs;
40
41
void opal_handle_events(void)
42
{
43
__be64 events = 0;
44
u64 e;
45
46
e = READ_ONCE(last_outstanding_events) & opal_event_irqchip.mask;
47
again:
48
while (e) {
49
int hwirq;
50
51
hwirq = fls64(e) - 1;
52
e &= ~BIT_ULL(hwirq);
53
54
local_irq_disable();
55
irq_enter();
56
generic_handle_domain_irq(opal_event_irqchip.domain, hwirq);
57
irq_exit();
58
local_irq_enable();
59
60
cond_resched();
61
}
62
WRITE_ONCE(last_outstanding_events, 0);
63
if (opal_poll_events(&events) != OPAL_SUCCESS)
64
return;
65
e = be64_to_cpu(events) & opal_event_irqchip.mask;
66
if (e)
67
goto again;
68
}
69
70
bool opal_have_pending_events(void)
71
{
72
if (READ_ONCE(last_outstanding_events) & opal_event_irqchip.mask)
73
return true;
74
return false;
75
}
76
77
static void opal_event_mask(struct irq_data *d)
78
{
79
clear_bit(d->hwirq, &opal_event_irqchip.mask);
80
}
81
82
static void opal_event_unmask(struct irq_data *d)
83
{
84
set_bit(d->hwirq, &opal_event_irqchip.mask);
85
if (opal_have_pending_events())
86
opal_wake_poller();
87
}
88
89
static int opal_event_set_type(struct irq_data *d, unsigned int flow_type)
90
{
91
/*
92
* For now we only support level triggered events. The irq
93
* handler will be called continuously until the event has
94
* been cleared in OPAL.
95
*/
96
if (flow_type != IRQ_TYPE_LEVEL_HIGH)
97
return -EINVAL;
98
99
return 0;
100
}
101
102
static struct opal_event_irqchip opal_event_irqchip = {
103
.irqchip = {
104
.name = "OPAL EVT",
105
.irq_mask = opal_event_mask,
106
.irq_unmask = opal_event_unmask,
107
.irq_set_type = opal_event_set_type,
108
},
109
.mask = 0,
110
};
111
112
static int opal_event_map(struct irq_domain *d, unsigned int irq,
113
irq_hw_number_t hwirq)
114
{
115
irq_set_chip_data(irq, &opal_event_irqchip);
116
irq_set_chip_and_handler(irq, &opal_event_irqchip.irqchip,
117
handle_level_irq);
118
119
return 0;
120
}
121
122
static irqreturn_t opal_interrupt(int irq, void *data)
123
{
124
__be64 events;
125
126
opal_handle_interrupt(virq_to_hw(irq), &events);
127
WRITE_ONCE(last_outstanding_events, be64_to_cpu(events));
128
if (opal_have_pending_events())
129
opal_wake_poller();
130
131
return IRQ_HANDLED;
132
}
133
134
static int opal_event_match(struct irq_domain *h, struct device_node *node,
135
enum irq_domain_bus_token bus_token)
136
{
137
return irq_domain_get_of_node(h) == node;
138
}
139
140
static int opal_event_xlate(struct irq_domain *h, struct device_node *np,
141
const u32 *intspec, unsigned int intsize,
142
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
143
{
144
*out_hwirq = intspec[0];
145
*out_flags = IRQ_TYPE_LEVEL_HIGH;
146
147
return 0;
148
}
149
150
static const struct irq_domain_ops opal_event_domain_ops = {
151
.match = opal_event_match,
152
.map = opal_event_map,
153
.xlate = opal_event_xlate,
154
};
155
156
void opal_event_shutdown(void)
157
{
158
unsigned int i;
159
160
/* First free interrupts, which will also mask them */
161
for (i = 0; i < opal_irq_count; i++) {
162
if (!opal_irqs || !opal_irqs[i].start)
163
continue;
164
165
if (in_interrupt() || irqs_disabled())
166
disable_irq_nosync(opal_irqs[i].start);
167
else
168
free_irq(opal_irqs[i].start, NULL);
169
170
opal_irqs[i].start = 0;
171
}
172
}
173
174
int __init opal_event_init(void)
175
{
176
struct device_node *dn, *opal_node;
177
bool old_style = false;
178
int i, rc = 0;
179
180
opal_node = of_find_node_by_path("/ibm,opal");
181
if (!opal_node) {
182
pr_warn("opal: Node not found\n");
183
return -ENODEV;
184
}
185
186
/* If dn is NULL it means the domain won't be linked to a DT
187
* node so therefore irq_of_parse_and_map(...) wont work. But
188
* that shouldn't be problem because if we're running a
189
* version of skiboot that doesn't have the dn then the
190
* devices won't have the correct properties and will have to
191
* fall back to the legacy method (opal_event_request(...))
192
* anyway. */
193
dn = of_find_compatible_node(NULL, NULL, "ibm,opal-event");
194
opal_event_irqchip.domain = irq_domain_create_linear(of_fwnode_handle(dn),
195
MAX_NUM_EVENTS,
196
&opal_event_domain_ops, &opal_event_irqchip);
197
of_node_put(dn);
198
if (!opal_event_irqchip.domain) {
199
pr_warn("opal: Unable to create irq domain\n");
200
rc = -ENOMEM;
201
goto out;
202
}
203
204
/* Look for new-style (standard) "interrupts" property */
205
opal_irq_count = of_irq_count(opal_node);
206
207
/* Absent ? Look for the old one */
208
if (opal_irq_count < 1) {
209
/* Get opal-interrupts property and names if present */
210
rc = of_property_count_u32_elems(opal_node, "opal-interrupts");
211
if (rc > 0)
212
opal_irq_count = rc;
213
old_style = true;
214
}
215
216
/* No interrupts ? Bail out */
217
if (!opal_irq_count)
218
goto out;
219
220
pr_debug("OPAL: Found %d interrupts reserved for OPAL using %s scheme\n",
221
opal_irq_count, old_style ? "old" : "new");
222
223
/* Allocate an IRQ resources array */
224
opal_irqs = kcalloc(opal_irq_count, sizeof(struct resource), GFP_KERNEL);
225
if (WARN_ON(!opal_irqs)) {
226
rc = -ENOMEM;
227
goto out;
228
}
229
230
/* Build the resources array */
231
if (old_style) {
232
/* Old style "opal-interrupts" property */
233
for (i = 0; i < opal_irq_count; i++) {
234
struct resource *r = &opal_irqs[i];
235
const char *name = NULL;
236
u32 hw_irq;
237
int virq;
238
239
rc = of_property_read_u32_index(opal_node, "opal-interrupts",
240
i, &hw_irq);
241
if (WARN_ON(rc < 0)) {
242
opal_irq_count = i;
243
break;
244
}
245
of_property_read_string_index(opal_node, "opal-interrupts-names",
246
i, &name);
247
virq = irq_create_mapping(NULL, hw_irq);
248
if (!virq) {
249
pr_warn("Failed to map OPAL irq 0x%x\n", hw_irq);
250
continue;
251
}
252
r->start = r->end = virq;
253
r->flags = IORESOURCE_IRQ | IRQ_TYPE_LEVEL_LOW;
254
r->name = name;
255
}
256
} else {
257
/* new style standard "interrupts" property */
258
rc = of_irq_to_resource_table(opal_node, opal_irqs, opal_irq_count);
259
if (WARN_ON(rc < 0)) {
260
opal_irq_count = 0;
261
kfree(opal_irqs);
262
goto out;
263
}
264
if (WARN_ON(rc < opal_irq_count))
265
opal_irq_count = rc;
266
}
267
268
/* Install interrupt handlers */
269
for (i = 0; i < opal_irq_count; i++) {
270
struct resource *r = &opal_irqs[i];
271
const char *name;
272
273
/* Prefix name */
274
if (r->name && strlen(r->name))
275
name = kasprintf(GFP_KERNEL, "opal-%s", r->name);
276
else
277
name = kasprintf(GFP_KERNEL, "opal");
278
279
if (!name)
280
continue;
281
/* Install interrupt handler */
282
rc = request_irq(r->start, opal_interrupt, r->flags & IRQD_TRIGGER_MASK,
283
name, NULL);
284
if (rc) {
285
pr_warn("Error %d requesting OPAL irq %d\n", rc, (int)r->start);
286
kfree(name);
287
continue;
288
}
289
}
290
rc = 0;
291
out:
292
of_node_put(opal_node);
293
return rc;
294
}
295
machine_arch_initcall(powernv, opal_event_init);
296
297
/**
298
* opal_event_request(unsigned int opal_event_nr) - Request an event
299
* @opal_event_nr: the opal event number to request
300
*
301
* This routine can be used to find the linux virq number which can
302
* then be passed to request_irq to assign a handler for a particular
303
* opal event. This should only be used by legacy devices which don't
304
* have proper device tree bindings. Most devices should use
305
* irq_of_parse_and_map() instead.
306
*/
307
int opal_event_request(unsigned int opal_event_nr)
308
{
309
if (WARN_ON_ONCE(!opal_event_irqchip.domain))
310
return 0;
311
312
return irq_create_mapping(opal_event_irqchip.domain, opal_event_nr);
313
}
314
EXPORT_SYMBOL(opal_event_request);
315
316