aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLokesh Vutla2018-11-23 03:38:02 -0600
committerTero Kristo2018-11-27 01:11:47 -0600
commitb9c186aed4d5ff6eec4c3015cd07a0e040ccb0ba (patch)
tree7fa4f82fa938e8438db47ba7e126bb52a4c9f234
parent74d046b4db3cf5585f00f50989a8b114142d7746 (diff)
downloadhwspinlock-b9c186aed4d5ff6eec4c3015cd07a0e040ccb0ba.tar.gz
hwspinlock-b9c186aed4d5ff6eec4c3015cd07a0e040ccb0ba.tar.xz
hwspinlock-b9c186aed4d5ff6eec4c3015cd07a0e040ccb0ba.zip
irqchip: ti-sci-inta: Add support for Interrupt Aggregator driver
Texas Instruments' K3 generation SoCs has an IP Interrupt Aggregator which is an interrupt controller that does the following: - Converts events to interrupts that can be understood by an interrupt router. - Allows for multiplexing of events to interrupts. - Allows for grouping of multiple events to a single interrupt. Configuration of the interrupt aggregator registers can only be done by a system co-processor and the driver needs to send a message to this co processor over TISCI protocol. Add support for Interrupt Aggregator driver over TISCI protocol. Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/irqchip/Kconfig11
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-ti-sci-inta.c613
-rw-r--r--include/linux/irqchip/irq-ti-sci-inta.h35
5 files changed, 661 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 1889074c1bb7..e7932b6b414f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14436,6 +14436,7 @@ F: drivers/reset/reset-ti-sci.c
14436F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt 14436F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt
14437F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-inta.txt 14437F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-inta.txt
14438F: drivers/irqchip/irq-ti-sci-intr.c 14438F: drivers/irqchip/irq-ti-sci-intr.c
14439F: drivers/irqchip/irq-ti-sci-inta.c
14439 14440
14440THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER 14441THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
14441M: Hans Verkuil <hverkuil@xs4all.nl> 14442M: Hans Verkuil <hverkuil@xs4all.nl>
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index de102e6848a7..b8961c5ddfe0 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -382,6 +382,17 @@ config TI_SCI_INTR_IRQCHIP
382 If you wish to use interrupt router irq resources managed by the 382 If you wish to use interrupt router irq resources managed by the
383 TI System Controller, say Y here. Otherwise, say N. 383 TI System Controller, say Y here. Otherwise, say N.
384 384
385config TI_SCI_INTA_IRQCHIP
386 bool
387 depends on TI_SCI_PROTOCOL && ARCH_K3
388 select IRQ_DOMAIN
389 select IRQ_DOMAIN_HIERARCHY
390 help
391 This enables the irqchip driver support for K3 Interrupt aggregator
392 over TI System Control Interface available on some new TI's SoCs.
393 If you wish to use interrupt aggregator irq resources managed by the
394 TI System Controller, say Y here. Otherwise, say N.
395
385endmenu 396endmenu
386 397
387config SIFIVE_PLIC 398config SIFIVE_PLIC
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 1523a556bd5a..11418eb9ac11 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -89,3 +89,4 @@ obj-$(CONFIG_NDS32) += irq-ativic32.o
89obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o 89obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o
90obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o 90obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o
91obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o 91obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
92obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
diff --git a/drivers/irqchip/irq-ti-sci-inta.c b/drivers/irqchip/irq-ti-sci-inta.c
new file mode 100644
index 000000000000..64c08412d6fc
--- /dev/null
+++ b/drivers/irqchip/irq-ti-sci-inta.c
@@ -0,0 +1,613 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Texas Instruments' K3 Interrupt Aggregator irqchip driver
4 *
5 * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
6 * Lokesh Vutla <lokeshvutla@ti.com>
7 */
8
9#include <linux/err.h>
10#include <linux/io.h>
11#include <linux/irqchip.h>
12#include <linux/of_platform.h>
13#include <linux/of_address.h>
14#include <linux/of_irq.h>
15#include <linux/module.h>
16#include <linux/moduleparam.h>
17#include <linux/irqdomain.h>
18#include <linux/soc/ti/ti_sci_protocol.h>
19
20#define MAX_EVENTS_PER_VINT 64
21#define TI_SCI_EVENT_IRQ BIT(31)
22
23#define VINT_ENABLE_CLR_OFFSET 0x18
24
25/**
26 * struct ti_sci_inta_irq_domain - Structure representing a TISCI based
27 * Interrupt Aggregator IRQ domain.
28 * @sci: Pointer to TISCI handle
29 * @vint: TISCI resource pointer representing IA inerrupts.
30 * @global_event:TISCI resource pointer representing global events.
31 * @base: Base address of the memory mapped IO registers
32 * @ia_id: TISCI device ID of this Interrupt Aggregator.
33 * @dst_id: TISCI device ID of the destination irq controller.
34 */
35struct ti_sci_inta_irq_domain {
36 const struct ti_sci_handle *sci;
37 struct ti_sci_resource *vint;
38 struct ti_sci_resource *global_event;
39 void __iomem *base;
40 u16 ia_id;
41 u16 dst_id;
42};
43
44/**
45 * struct ti_sci_inta_event_desc - Description of an event coming to
46 * Interrupt Aggregator.
47 * @global_event: Global event number corresponding to this event
48 * @src_id: TISCI device ID of the event source
49 * @src_index: Event source index within the device.
50 */
51struct ti_sci_inta_event_desc {
52 u16 global_event;
53 u16 src_id;
54 u16 src_index;
55};
56
57/**
58 * struct ti_sci_inta_vint_desc - Description of a virtual interrupt coming out
59 * of Interrupt Aggregator.
60 * @lock: lock to guard the event map
61 * @event_map: Bitmap to manage the allocation of events to vint.
62 * @events: Array of event descriptors assigned to this vint.
63 * @ack_needed: Event needs to be acked via INTA. This is used when
64 * HW generating events cannot clear the events by itself.
65 * Assuming that only events from the same hw block are
66 * grouped. So all the events attached to vint needs
67 * an ack or none needs an ack.
68 */
69struct ti_sci_inta_vint_desc {
70 raw_spinlock_t lock;
71 unsigned long *event_map;
72 struct ti_sci_inta_event_desc events[MAX_EVENTS_PER_VINT];
73 bool ack_needed;
74};
75
76static void ti_sci_inta_irq_eoi(struct irq_data *data)
77{
78 struct ti_sci_inta_irq_domain *inta = data->domain->host_data;
79 struct ti_sci_inta_vint_desc *vint_desc;
80 u64 val;
81 int bit;
82
83 vint_desc = irq_data_get_irq_chip_data(data);
84 if (!vint_desc->ack_needed)
85 goto out;
86
87 for_each_set_bit(bit, vint_desc->event_map, MAX_EVENTS_PER_VINT) {
88 val = 1 << bit;
89 __raw_writeq(val, inta->base + data->hwirq * 0x1000 +
90 VINT_ENABLE_CLR_OFFSET);
91 }
92
93out:
94 irq_chip_eoi_parent(data);
95}
96
97static struct irq_chip ti_sci_inta_irq_chip = {
98 .name = "INTA",
99 .irq_eoi = ti_sci_inta_irq_eoi,
100 .irq_mask = irq_chip_mask_parent,
101 .irq_unmask = irq_chip_unmask_parent,
102 .irq_retrigger = irq_chip_retrigger_hierarchy,
103 .irq_set_type = irq_chip_set_type_parent,
104 .irq_set_affinity = irq_chip_set_affinity_parent,
105};
106
107/**
108 * ti_sci_inta_irq_domain_translate() - Retrieve hwirq and type from
109 * IRQ firmware specific handler.
110 * @domain: Pointer to IRQ domain
111 * @fwspec: Pointer to IRQ specific firmware structure
112 * @hwirq: IRQ number identified by hardware
113 * @type: IRQ type
114 *
115 * Return 0 if all went ok else appropriate error.
116 */
117static int ti_sci_inta_irq_domain_translate(struct irq_domain *domain,
118 struct irq_fwspec *fwspec,
119 unsigned long *hwirq,
120 unsigned int *type)
121{
122 if (is_of_node(fwspec->fwnode)) {
123 if (fwspec->param_count != 4)
124 return -EINVAL;
125
126 *hwirq = fwspec->param[2];
127 *type = fwspec->param[3] & IRQ_TYPE_SENSE_MASK;
128
129 return 0;
130 }
131
132 return -EINVAL;
133}
134
135/**
136 * ti_sci_free_event_irq() - Free an event from vint
137 * @inta: Pointer to Interrupt Aggregator IRQ domain
138 * @vint_desc: Virtual interrupt descriptor containing the event.
139 * @event_index:Event Index within the vint.
140 * @dst_irq: Destination host irq
141 * @vint: Interrupt number within interrupt aggregator.
142 */
143static void ti_sci_free_event_irq(struct ti_sci_inta_irq_domain *inta,
144 struct ti_sci_inta_vint_desc *vint_desc,
145 u32 event_index, u16 dst_irq, u16 vint)
146{
147 struct ti_sci_inta_event_desc *event;
148 unsigned long flags;
149
150 if (event_index >= MAX_EVENTS_PER_VINT)
151 return;
152
153 event = &vint_desc->events[event_index];
154 inta->sci->ops.rm_irq_ops.free_event_irq(inta->sci,
155 event->src_id,
156 event->src_index,
157 inta->dst_id,
158 dst_irq,
159 inta->ia_id, vint,
160 event->global_event,
161 event_index);
162
163 raw_spin_lock_irqsave(&vint_desc->lock, flags);
164 clear_bit(event_index, vint_desc->event_map);
165 raw_spin_unlock_irqrestore(&vint_desc->lock, flags);
166
167 ti_sci_release_resource(inta->global_event, event->global_event);
168}
169
170/**
171 * ti_sci_inta_irq_domain_free() - Free an IRQ from the IRQ domain
172 * @domain: Domain to which the irqs belong
173 * @virq: base linux virtual IRQ to be freed.
174 * @nr_irqs: Number of continuous irqs to be freed
175 */
176static void ti_sci_inta_irq_domain_free(struct irq_domain *domain,
177 unsigned int virq, unsigned int nr_irqs)
178{
179 struct ti_sci_inta_irq_domain *inta = domain->host_data;
180 struct ti_sci_inta_vint_desc *vint_desc;
181 struct irq_data *data, *gic_data;
182 int event_index;
183
184 data = irq_domain_get_irq_data(domain, virq);
185 gic_data = irq_domain_get_irq_data(domain->parent->parent, virq);
186
187 vint_desc = irq_data_get_irq_chip_data(data);
188
189 /* This is the last event in the vint */
190 event_index = find_first_bit(vint_desc->event_map, MAX_EVENTS_PER_VINT);
191 ti_sci_free_event_irq(inta, vint_desc, event_index,
192 gic_data->hwirq, data->hwirq);
193 irq_domain_free_irqs_parent(domain, virq, 1);
194 irq_domain_reset_irq_data(data);
195 ti_sci_release_resource(inta->vint, data->hwirq);
196 kfree(vint_desc->event_map);
197 kfree(vint_desc);
198}
199
200/**
201 * ti_sci_allocate_event_irq() - Allocate an event to a IA vint.
202 * @inta: Pointer to Interrupt Aggregator IRQ domain
203 * @vint_desc: Virtual interrupt descriptor to which the event gets attached.
204 * @src_id: TISCI device id of the event source
205 * @src_index: Event index with in the device.
206 * @dst_irq: Destination host irq
207 * @vint: Interrupt number within interrupt aggregator.
208 *
209 * Return 0 if all went ok else appropriate error value.
210 */
211static int ti_sci_allocate_event_irq(struct ti_sci_inta_irq_domain *inta,
212 struct ti_sci_inta_vint_desc *vint_desc,
213 u16 src_id, u16 src_index, u16 dst_irq,
214 u16 vint)
215{
216 struct ti_sci_inta_event_desc *event;
217 unsigned long flags;
218 u32 free_bit;
219 int err;
220
221 raw_spin_lock_irqsave(&vint_desc->lock, flags);
222 free_bit = find_first_zero_bit(vint_desc->event_map,
223 MAX_EVENTS_PER_VINT);
224 if (free_bit != MAX_EVENTS_PER_VINT)
225 set_bit(free_bit, vint_desc->event_map);
226 raw_spin_unlock_irqrestore(&vint_desc->lock, flags);
227
228 if (free_bit >= MAX_EVENTS_PER_VINT)
229 return -ENODEV;
230
231 event = &vint_desc->events[free_bit];
232
233 event->src_id = src_id;
234 event->src_index = src_index;
235 event->global_event = ti_sci_get_free_resource(inta->global_event);
236
237 err = inta->sci->ops.rm_irq_ops.set_event_irq(inta->sci,
238 src_id, src_index,
239 inta->dst_id,
240 dst_irq,
241 inta->ia_id,
242 vint,
243 event->global_event,
244 free_bit);
245 if (err) {
246 pr_err("%s: Event allocation failed from src = %d, index = %d, to dst = %d,irq = %d,via ia_id = %d, vint = %d,global event = %d, status_bit = %d\n",
247 __func__, src_id, src_index, inta->dst_id, dst_irq,
248 inta->ia_id, vint, event->global_event, free_bit);
249 return err;
250 }
251
252 return 0;
253}
254
255/**
256 * alloc_parent_irq() - Allocate parent irq to Interrupt aggregator
257 * @domain: IRQ domain corresponding to Interrupt Aggregator
258 * @virq: Linux virtual IRQ number
259 * @src_id: TISCI device id of the event source
260 * @src_index: Event index with in the device.
261 * @vint: Virtual interrupt number within IA
262 * @flags: Corresponding IRQ flags
263 *
264 * Return pointer to vint descriptor if all went well else corresponding
265 * error pointer.
266 */
267static struct ti_sci_inta_vint_desc *alloc_parent_irq(struct irq_domain *domain,
268 unsigned int virq,
269 u32 src_id, u32 src_index,
270 u32 vint, u32 flags)
271{
272 struct ti_sci_inta_irq_domain *inta = domain->host_data;
273 struct ti_sci_inta_vint_desc *vint_desc;
274 struct irq_data *gic_data;
275 struct irq_fwspec fwspec;
276 int err;
277
278 if (!irq_domain_get_of_node(domain->parent))
279 return ERR_PTR(-EINVAL);
280
281 vint_desc = kzalloc(sizeof(*vint_desc), GFP_KERNEL);
282 if (!vint_desc)
283 return ERR_PTR(-ENOMEM);
284
285 vint_desc->event_map = kcalloc(BITS_TO_LONGS(MAX_EVENTS_PER_VINT),
286 sizeof(*vint_desc->event_map),
287 GFP_KERNEL);
288 if (!vint_desc->event_map) {
289 kfree(vint_desc);
290 return ERR_PTR(-ENOMEM);
291 }
292
293 fwspec.fwnode = domain->parent->fwnode;
294 fwspec.param_count = 3;
295 /* Interrupt parent is Interrupt Router */
296 fwspec.param[0] = inta->ia_id;
297 fwspec.param[1] = vint;
298 fwspec.param[2] = flags | TI_SCI_EVENT_IRQ;
299
300 err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
301 if (err)
302 goto err_irqs;
303
304 gic_data = irq_domain_get_irq_data(domain->parent->parent, virq);
305
306 raw_spin_lock_init(&vint_desc->lock);
307
308 err = ti_sci_allocate_event_irq(inta, vint_desc, src_id, src_index,
309 gic_data->hwirq, vint);
310 if (err)
311 goto err_events;
312
313 return vint_desc;
314
315err_events:
316 irq_domain_free_irqs_parent(domain, virq, 1);
317err_irqs:
318 ti_sci_release_resource(inta->vint, vint);
319 kfree(vint_desc);
320 return ERR_PTR(err);
321}
322
323/**
324 * ti_sci_inta_irq_domain_alloc() - Allocate Interrupt aggregator IRQs
325 * @domain: Point to the interrupt aggregator IRQ domain
326 * @virq: Corresponding Linux virtual IRQ number
327 * @nr_irqs: Continuous irqs to be allocated
328 * @data: Pointer to firmware specifier
329 *
330 * Return 0 if all went well else appropriate error value.
331 */
332static int ti_sci_inta_irq_domain_alloc(struct irq_domain *domain,
333 unsigned int virq, unsigned int nr_irqs,
334 void *data)
335{
336 struct ti_sci_inta_vint_desc *vint_desc;
337 struct irq_fwspec *fwspec = data;
338 int err;
339
340 vint_desc = alloc_parent_irq(domain, virq, fwspec->param[0],
341 fwspec->param[1], fwspec->param[2],
342 fwspec->param[3]);
343 if (IS_ERR(vint_desc))
344 return PTR_ERR(vint_desc);
345
346 err = irq_domain_set_hwirq_and_chip(domain, virq, fwspec->param[2],
347 &ti_sci_inta_irq_chip, vint_desc);
348
349 return err;
350}
351
352static const struct irq_domain_ops ti_sci_inta_irq_domain_ops = {
353 .alloc = ti_sci_inta_irq_domain_alloc,
354 .free = ti_sci_inta_irq_domain_free,
355 .translate = ti_sci_inta_irq_domain_translate,
356};
357
358static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev)
359{
360 struct irq_domain *parent_domain, *domain;
361 struct ti_sci_inta_irq_domain *inta;
362 struct device_node *parent_node;
363 struct device *dev = &pdev->dev;
364 struct resource *res;
365 int ret;
366
367 parent_node = of_irq_find_parent(dev_of_node(dev));
368 if (!parent_node) {
369 dev_err(dev, "Failed to get IRQ parent node\n");
370 return -ENODEV;
371 }
372
373 parent_domain = irq_find_host(parent_node);
374 if (!parent_domain)
375 return -EPROBE_DEFER;
376
377 inta = devm_kzalloc(dev, sizeof(*inta), GFP_KERNEL);
378 if (!inta)
379 return -ENOMEM;
380
381 inta->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci");
382 if (IS_ERR(inta->sci)) {
383 ret = PTR_ERR(inta->sci);
384 if (ret != -EPROBE_DEFER)
385 dev_err(dev, "ti,sci read fail %d\n", ret);
386 inta->sci = NULL;
387 return ret;
388 }
389
390 ret = of_property_read_u32(dev->of_node, "ti,sci-dev-id",
391 (u32 *)&inta->ia_id);
392 if (ret) {
393 dev_err(dev, "missing 'ti,sci-dev-id' property\n");
394 return -EINVAL;
395 }
396
397 inta->vint = devm_ti_sci_get_of_resource(inta->sci, dev,
398 inta->ia_id,
399 "ti,sci-rm-range-vint");
400 if (IS_ERR(inta->vint)) {
401 dev_err(dev, "VINT resource allocation failed\n");
402 return PTR_ERR(inta->vint);
403 }
404
405 inta->global_event =
406 devm_ti_sci_get_of_resource(inta->sci, dev,
407 inta->ia_id,
408 "ti,sci-rm-range-global-event");
409 if (IS_ERR(inta->global_event)) {
410 dev_err(dev, "Global event resource allocation failed\n");
411 return PTR_ERR(inta->global_event);
412 }
413
414 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
415 inta->base = devm_ioremap_resource(dev, res);
416 if (IS_ERR(inta->base))
417 return -ENODEV;
418
419 ret = of_property_read_u32(parent_node, "ti,sci-dst-id",
420 (u32 *)&inta->dst_id);
421
422 domain = irq_domain_add_hierarchy(parent_domain, 0, 0, dev_of_node(dev),
423 &ti_sci_inta_irq_domain_ops, inta);
424 if (!domain) {
425 dev_err(dev, "Failed to allocate IRQ domain\n");
426 return -ENOMEM;
427 }
428
429 return 0;
430}
431
432/**
433 * ti_sci_inta_register_event() - Register a event to an interrupt aggregator
434 * @dev: Device pointer to source generating the event
435 * @src_id: TISCI device ID of the event source
436 * @src_index: Event source index within the device.
437 * @virq: Linux Virtual IRQ number
438 * @ack_needed: If explicit clearing of event is required.
439 * @flags: Corresponding IRQ flags
440 *
441 * Creates a new irq and attaches to IA domain if virq is not specified
442 * else attaches the event to vint corresponding to virq.
443 * When using TISCI within the client drivers, source indexes are always
444 * generated dynamically and cannot be represented in DT. So client
445 * drivers should call this API instead of platform_get_irq().
446 *
447 * Return virq if all went well else appropriate error value.
448 */
449int ti_sci_inta_register_event(struct device *dev, u16 src_id, u16 src_index,
450 unsigned int virq, bool ack_needed, u32 flags)
451{
452 struct ti_sci_inta_vint_desc *vint_desc;
453 struct ti_sci_inta_irq_domain *inta;
454 struct irq_data *data, *gic_data;
455 struct device_node *parent_node;
456 struct irq_domain *domain;
457 struct irq_fwspec fwspec;
458 int err;
459
460 parent_node = of_irq_find_parent(dev->of_node);
461 if (!parent_node)
462 return -ENODEV;
463
464 domain = irq_find_host(parent_node);
465 if (!domain)
466 return -EPROBE_DEFER;
467
468 inta = domain->host_data;
469
470 if (virq > 0) {
471 /* If Group already available */
472 data = irq_domain_get_irq_data(domain, virq);
473 gic_data = irq_domain_get_irq_data(domain->parent->parent,
474 virq);
475
476 vint_desc = irq_data_get_irq_chip_data(data);
477
478 err = ti_sci_allocate_event_irq(inta, vint_desc, src_id,
479 src_index, gic_data->hwirq,
480 data->hwirq);
481 if (err)
482 return err;
483 } else {
484 fwspec.param_count = 4;
485 fwspec.fwnode = domain->fwnode;
486 fwspec.param[0] = src_id;
487 fwspec.param[1] = src_index;
488 fwspec.param[2] = ti_sci_get_free_resource(inta->vint);
489 fwspec.param[3] = flags;
490
491 virq = irq_create_fwspec_mapping(&fwspec);
492 if (virq <= 0)
493 ti_sci_release_resource(inta->vint, fwspec.param[2]);
494
495 data = irq_domain_get_irq_data(domain, virq);
496 vint_desc = irq_data_get_irq_chip_data(data);
497 vint_desc->ack_needed = ack_needed;
498 }
499
500 return virq;
501}
502EXPORT_SYMBOL_GPL(ti_sci_inta_register_event);
503
504/**
505 * get_event_index() - Helper api to get the event index with the vint.
506 * @vint_desc: Pointer to the IA irq descriptor
507 * @src_id: TISCI device ID of the event source
508 * @src_index: Event source index within the device.
509 *
510 * Return the index if all went well else MAX_EVENTS_PER_VINT.
511 */
512static u8 get_event_index(struct ti_sci_inta_vint_desc *vint_desc, u16 src_id,
513 u16 src_index)
514{
515 int i;
516
517 for (i = 0; i < MAX_EVENTS_PER_VINT; i++) {
518 if (vint_desc->events[i].src_id == src_id &&
519 vint_desc->events[i].src_index == src_index)
520 return i;
521 }
522
523 return MAX_EVENTS_PER_VINT;
524}
525
526/**
527 * is_event_last() - Helper api to determine if the event is the last
528 * event within the vint
529 * @vint_desc: Pointer to the IA irq descriptor
530 * @event_index: Event index within the vint of IA.
531 *
532 * Return true if specified index is the last event else false.
533 */
534static bool is_event_last(struct ti_sci_inta_vint_desc *vint_desc,
535 u8 event_index)
536{
537 unsigned long flags;
538 int next_event;
539 bool status;
540
541 raw_spin_lock_irqsave(&vint_desc->lock, flags);
542 next_event = find_next_bit(vint_desc->event_map, MAX_EVENTS_PER_VINT,
543 event_index + 1);
544 status = (next_event != MAX_EVENTS_PER_VINT) ? false : true;
545 if (event_index !=
546 find_first_bit(vint_desc->event_map, MAX_EVENTS_PER_VINT))
547 status = false;
548 raw_spin_unlock_irqrestore(&vint_desc->lock, flags);
549
550 return status;
551}
552
553/**
554 * ti_sci_inta_unregister_event() - Unregister a event from an IA.
555 * @dev: Device pointer to source generating the event
556 * @src_id: TISCI device ID of the event source
557 * @src_index: Event source index within the device.
558 * @virq: Linux Virtual IRQ number
559 *
560 * Deletes the event from IA interrupt descriptor. If this event is the
561 * last event then delete the vint as well.
562 */
563void ti_sci_inta_unregister_event(struct device *dev, u16 src_id,
564 u16 src_index, unsigned int virq)
565{
566 struct ti_sci_inta_vint_desc *vint_desc;
567 struct irq_data *data, *gic_data;
568 struct device_node *parent_node;
569 struct irq_domain *domain;
570 u8 event_index;
571
572 parent_node = of_irq_find_parent(dev->of_node);
573 if (!parent_node)
574 return;
575
576 domain = irq_find_host(parent_node);
577
578 data = irq_domain_get_irq_data(domain, virq);
579 gic_data = irq_domain_get_irq_data(domain->parent->parent, virq);
580
581 vint_desc = irq_data_get_irq_chip_data(data);
582
583 event_index = get_event_index(vint_desc, src_id, src_index);
584 if (event_index == MAX_EVENTS_PER_VINT)
585 return;
586
587 if (is_event_last(vint_desc, event_index))
588 irq_dispose_mapping(virq);
589 else
590 ti_sci_free_event_irq(domain->host_data, vint_desc,
591 event_index, gic_data->hwirq,
592 data->hwirq);
593}
594EXPORT_SYMBOL_GPL(ti_sci_inta_unregister_event);
595
596static const struct of_device_id ti_sci_inta_irq_domain_of_match[] = {
597 { .compatible = "ti,sci-inta", },
598 { /* sentinel */ },
599};
600MODULE_DEVICE_TABLE(of, ti_sci_inta_irq_domain_of_match);
601
602static struct platform_driver ti_sci_inta_irq_domain_driver = {
603 .probe = ti_sci_inta_irq_domain_probe,
604 .driver = {
605 .name = "ti-sci-inta",
606 .of_match_table = ti_sci_inta_irq_domain_of_match,
607 },
608};
609module_platform_driver(ti_sci_inta_irq_domain_driver);
610
611MODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ticom>");
612MODULE_DESCRIPTION("K3 Interrupt Aggregator driver over TI SCI protocol");
613MODULE_LICENSE("GPL v2");
diff --git a/include/linux/irqchip/irq-ti-sci-inta.h b/include/linux/irqchip/irq-ti-sci-inta.h
new file mode 100644
index 000000000000..fbc3172dd776
--- /dev/null
+++ b/include/linux/irqchip/irq-ti-sci-inta.h
@@ -0,0 +1,35 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Texas Instruments' System Control Interface (TI-SCI) irqchip
4 *
5 * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
6 * Lokesh Vutla <lokeshvutla@ti.com>
7 */
8
9#ifndef __INCLUDE_LINUX_IRQCHIP_TI_SCI_INTA_H
10#define __INCLUDE_LINUX_IRQCHIP_TI_SCI_INTA_H
11
12#if IS_ENABLED(CONFIG_TI_SCI_INTA_IRQCHIP)
13int ti_sci_inta_register_event(struct device *dev, u16 src_id, u16 src_index,
14 unsigned int virq, bool ack_needed, u32 flags);
15int ti_sci_inta_unregister_event(struct device *dev, u16 src_id, u16 src_index,
16 unsigned int virq);
17
18#else /* CONFIG_TI_SCI_INTA_IRQCHIP */
19
20static inline int ti_sci_inta_register_event(struct device *dev, u16 src_id,
21 u16 src_index, unsigned int virq,
22 bool ack_needed, u32 flags)
23{
24 return -EINVAL;
25}
26
27static inline int ti_sci_inta_unregister_event(struct device *dev, u16 src_id,
28 u16 src_index, unsigned int virq)
29{
30 return -EINVAL;
31}
32
33#endif /* CONFIG_TI_SCI_INTA_IRQCHIP */
34
35#endif /* __INCLUDE_LINUX_IRQCHIP_TI_SCI_INTA_H */