Skip to content

Commit cd844b0

Browse files
lokeshvutlaMarc Zyngier
authored andcommitted
irqchip/ti-sci-intr: Add support for Interrupt Router driver
Texas Instruments' K3 generation SoCs has an IP Interrupt Router that does allows for redirection of input interrupts to host interrupt controller. Interrupt Router inputs are either from a peripheral or from an Interrupt Aggregator which is another interrupt controller. Configuration of the interrupt router 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 Router driver over TISCI protocol. Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
1 parent 67d2075 commit cd844b0

File tree

4 files changed

+287
-0
lines changed

4 files changed

+287
-0
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15350,6 +15350,7 @@ F: Documentation/devicetree/bindings/clock/ti,sci-clk.txt
1535015350
F: drivers/clk/keystone/sci-clk.c
1535115351
F: drivers/reset/reset-ti-sci.c
1535215352
F: Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt
15353+
F: drivers/irqchip/irq-ti-sci-intr.c
1535315354

1535415355
Texas Instruments ASoC drivers
1535515356
M: Peter Ujfalusi <peter.ujfalusi@ti.com>

drivers/irqchip/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,16 @@ config LS1X_IRQ
419419
help
420420
Support for the Loongson-1 platform Interrupt Controller.
421421

422+
config TI_SCI_INTR_IRQCHIP
423+
bool
424+
depends on TI_SCI_PROTOCOL
425+
select IRQ_DOMAIN_HIERARCHY
426+
help
427+
This enables the irqchip driver support for K3 Interrupt router
428+
over TI System Control Interface available on some new TI's SoCs.
429+
If you wish to use interrupt router irq resources managed by the
430+
TI System Controller, say Y here. Otherwise, say N.
431+
422432
endmenu
423433

424434
config SIFIVE_PLIC

drivers/irqchip/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,4 @@ obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o
9797
obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o
9898
obj-$(CONFIG_MADERA_IRQ) += irq-madera.o
9999
obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
100+
obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o

drivers/irqchip/irq-ti-sci-intr.c

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Texas Instruments' K3 Interrupt Router irqchip driver
4+
*
5+
* Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/
6+
* Lokesh Vutla <lokeshvutla@ti.com>
7+
*/
8+
9+
#include <linux/err.h>
10+
#include <linux/module.h>
11+
#include <linux/moduleparam.h>
12+
#include <linux/io.h>
13+
#include <linux/irqchip.h>
14+
#include <linux/irqdomain.h>
15+
#include <linux/of_platform.h>
16+
#include <linux/of_address.h>
17+
#include <linux/of_irq.h>
18+
#include <linux/soc/ti/ti_sci_protocol.h>
19+
20+
#define TI_SCI_DEV_ID_MASK 0xffff
21+
#define TI_SCI_DEV_ID_SHIFT 16
22+
#define TI_SCI_IRQ_ID_MASK 0xffff
23+
#define TI_SCI_IRQ_ID_SHIFT 0
24+
#define HWIRQ_TO_DEVID(hwirq) (((hwirq) >> (TI_SCI_DEV_ID_SHIFT)) & \
25+
(TI_SCI_DEV_ID_MASK))
26+
#define HWIRQ_TO_IRQID(hwirq) ((hwirq) & (TI_SCI_IRQ_ID_MASK))
27+
#define TO_HWIRQ(dev, index) ((((dev) & TI_SCI_DEV_ID_MASK) << \
28+
TI_SCI_DEV_ID_SHIFT) | \
29+
((index) & TI_SCI_IRQ_ID_MASK))
30+
31+
/**
32+
* struct ti_sci_intr_irq_domain - Structure representing a TISCI based
33+
* Interrupt Router IRQ domain.
34+
* @sci: Pointer to TISCI handle
35+
* @dst_irq: TISCI resource pointer representing GIC irq controller.
36+
* @dst_id: TISCI device ID of the GIC irq controller.
37+
* @type: Specifies the trigger type supported by this Interrupt Router
38+
*/
39+
struct ti_sci_intr_irq_domain {
40+
const struct ti_sci_handle *sci;
41+
struct ti_sci_resource *dst_irq;
42+
u32 dst_id;
43+
u32 type;
44+
};
45+
46+
static struct irq_chip ti_sci_intr_irq_chip = {
47+
.name = "INTR",
48+
.irq_eoi = irq_chip_eoi_parent,
49+
.irq_mask = irq_chip_mask_parent,
50+
.irq_unmask = irq_chip_unmask_parent,
51+
.irq_set_type = irq_chip_set_type_parent,
52+
.irq_retrigger = irq_chip_retrigger_hierarchy,
53+
.irq_set_affinity = irq_chip_set_affinity_parent,
54+
};
55+
56+
/**
57+
* ti_sci_intr_irq_domain_translate() - Retrieve hwirq and type from
58+
* IRQ firmware specific handler.
59+
* @domain: Pointer to IRQ domain
60+
* @fwspec: Pointer to IRQ specific firmware structure
61+
* @hwirq: IRQ number identified by hardware
62+
* @type: IRQ type
63+
*
64+
* Return 0 if all went ok else appropriate error.
65+
*/
66+
static int ti_sci_intr_irq_domain_translate(struct irq_domain *domain,
67+
struct irq_fwspec *fwspec,
68+
unsigned long *hwirq,
69+
unsigned int *type)
70+
{
71+
struct ti_sci_intr_irq_domain *intr = domain->host_data;
72+
73+
if (fwspec->param_count != 2)
74+
return -EINVAL;
75+
76+
*hwirq = TO_HWIRQ(fwspec->param[0], fwspec->param[1]);
77+
*type = intr->type;
78+
79+
return 0;
80+
}
81+
82+
/**
83+
* ti_sci_intr_irq_domain_free() - Free the specified IRQs from the domain.
84+
* @domain: Domain to which the irqs belong
85+
* @virq: Linux virtual IRQ to be freed.
86+
* @nr_irqs: Number of continuous irqs to be freed
87+
*/
88+
static void ti_sci_intr_irq_domain_free(struct irq_domain *domain,
89+
unsigned int virq, unsigned int nr_irqs)
90+
{
91+
struct ti_sci_intr_irq_domain *intr = domain->host_data;
92+
struct irq_data *data, *parent_data;
93+
u16 dev_id, irq_index;
94+
95+
parent_data = irq_domain_get_irq_data(domain->parent, virq);
96+
data = irq_domain_get_irq_data(domain, virq);
97+
irq_index = HWIRQ_TO_IRQID(data->hwirq);
98+
dev_id = HWIRQ_TO_DEVID(data->hwirq);
99+
100+
intr->sci->ops.rm_irq_ops.free_irq(intr->sci, dev_id, irq_index,
101+
intr->dst_id, parent_data->hwirq);
102+
ti_sci_release_resource(intr->dst_irq, parent_data->hwirq);
103+
irq_domain_free_irqs_parent(domain, virq, 1);
104+
irq_domain_reset_irq_data(data);
105+
}
106+
107+
/**
108+
* ti_sci_intr_alloc_gic_irq() - Allocate GIC specific IRQ
109+
* @domain: Pointer to the interrupt router IRQ domain
110+
* @virq: Corresponding Linux virtual IRQ number
111+
* @hwirq: Corresponding hwirq for the IRQ within this IRQ domain
112+
*
113+
* Returns 0 if all went well else appropriate error pointer.
114+
*/
115+
static int ti_sci_intr_alloc_gic_irq(struct irq_domain *domain,
116+
unsigned int virq, u32 hwirq)
117+
{
118+
struct ti_sci_intr_irq_domain *intr = domain->host_data;
119+
struct irq_fwspec fwspec;
120+
u16 dev_id, irq_index;
121+
u16 dst_irq;
122+
int err;
123+
124+
dev_id = HWIRQ_TO_DEVID(hwirq);
125+
irq_index = HWIRQ_TO_IRQID(hwirq);
126+
127+
dst_irq = ti_sci_get_free_resource(intr->dst_irq);
128+
if (dst_irq == TI_SCI_RESOURCE_NULL)
129+
return -EINVAL;
130+
131+
fwspec.fwnode = domain->parent->fwnode;
132+
fwspec.param_count = 3;
133+
fwspec.param[0] = 0; /* SPI */
134+
fwspec.param[1] = dst_irq - 32; /* SPI offset */
135+
fwspec.param[2] = intr->type;
136+
137+
err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
138+
if (err)
139+
goto err_irqs;
140+
141+
err = intr->sci->ops.rm_irq_ops.set_irq(intr->sci, dev_id, irq_index,
142+
intr->dst_id, dst_irq);
143+
if (err)
144+
goto err_msg;
145+
146+
return 0;
147+
148+
err_msg:
149+
irq_domain_free_irqs_parent(domain, virq, 1);
150+
err_irqs:
151+
ti_sci_release_resource(intr->dst_irq, dst_irq);
152+
return err;
153+
}
154+
155+
/**
156+
* ti_sci_intr_irq_domain_alloc() - Allocate Interrupt router IRQs
157+
* @domain: Point to the interrupt router IRQ domain
158+
* @virq: Corresponding Linux virtual IRQ number
159+
* @nr_irqs: Continuous irqs to be allocated
160+
* @data: Pointer to firmware specifier
161+
*
162+
* Return 0 if all went well else appropriate error value.
163+
*/
164+
static int ti_sci_intr_irq_domain_alloc(struct irq_domain *domain,
165+
unsigned int virq, unsigned int nr_irqs,
166+
void *data)
167+
{
168+
struct irq_fwspec *fwspec = data;
169+
unsigned long hwirq;
170+
unsigned int flags;
171+
int err;
172+
173+
err = ti_sci_intr_irq_domain_translate(domain, fwspec, &hwirq, &flags);
174+
if (err)
175+
return err;
176+
177+
err = ti_sci_intr_alloc_gic_irq(domain, virq, hwirq);
178+
if (err)
179+
return err;
180+
181+
irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
182+
&ti_sci_intr_irq_chip, NULL);
183+
184+
return 0;
185+
}
186+
187+
static const struct irq_domain_ops ti_sci_intr_irq_domain_ops = {
188+
.free = ti_sci_intr_irq_domain_free,
189+
.alloc = ti_sci_intr_irq_domain_alloc,
190+
.translate = ti_sci_intr_irq_domain_translate,
191+
};
192+
193+
static int ti_sci_intr_irq_domain_probe(struct platform_device *pdev)
194+
{
195+
struct irq_domain *parent_domain, *domain;
196+
struct ti_sci_intr_irq_domain *intr;
197+
struct device_node *parent_node;
198+
struct device *dev = &pdev->dev;
199+
int ret;
200+
201+
parent_node = of_irq_find_parent(dev_of_node(dev));
202+
if (!parent_node) {
203+
dev_err(dev, "Failed to get IRQ parent node\n");
204+
return -ENODEV;
205+
}
206+
207+
parent_domain = irq_find_host(parent_node);
208+
if (!parent_domain) {
209+
dev_err(dev, "Failed to find IRQ parent domain\n");
210+
return -ENODEV;
211+
}
212+
213+
intr = devm_kzalloc(dev, sizeof(*intr), GFP_KERNEL);
214+
if (!intr)
215+
return -ENOMEM;
216+
217+
ret = of_property_read_u32(dev_of_node(dev), "ti,intr-trigger-type",
218+
&intr->type);
219+
if (ret) {
220+
dev_err(dev, "missing ti,intr-trigger-type property\n");
221+
return -EINVAL;
222+
}
223+
224+
intr->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci");
225+
if (IS_ERR(intr->sci)) {
226+
ret = PTR_ERR(intr->sci);
227+
if (ret != -EPROBE_DEFER)
228+
dev_err(dev, "ti,sci read fail %d\n", ret);
229+
intr->sci = NULL;
230+
return ret;
231+
}
232+
233+
ret = of_property_read_u32(dev_of_node(dev), "ti,sci-dst-id",
234+
&intr->dst_id);
235+
if (ret) {
236+
dev_err(dev, "missing 'ti,sci-dst-id' property\n");
237+
return -EINVAL;
238+
}
239+
240+
intr->dst_irq = devm_ti_sci_get_of_resource(intr->sci, dev,
241+
intr->dst_id,
242+
"ti,sci-rm-range-girq");
243+
if (IS_ERR(intr->dst_irq)) {
244+
dev_err(dev, "Destination irq resource allocation failed\n");
245+
return PTR_ERR(intr->dst_irq);
246+
}
247+
248+
domain = irq_domain_add_hierarchy(parent_domain, 0, 0, dev_of_node(dev),
249+
&ti_sci_intr_irq_domain_ops, intr);
250+
if (!domain) {
251+
dev_err(dev, "Failed to allocate IRQ domain\n");
252+
return -ENOMEM;
253+
}
254+
255+
return 0;
256+
}
257+
258+
static const struct of_device_id ti_sci_intr_irq_domain_of_match[] = {
259+
{ .compatible = "ti,sci-intr", },
260+
{ /* sentinel */ },
261+
};
262+
MODULE_DEVICE_TABLE(of, ti_sci_intr_irq_domain_of_match);
263+
264+
static struct platform_driver ti_sci_intr_irq_domain_driver = {
265+
.probe = ti_sci_intr_irq_domain_probe,
266+
.driver = {
267+
.name = "ti-sci-intr",
268+
.of_match_table = ti_sci_intr_irq_domain_of_match,
269+
},
270+
};
271+
module_platform_driver(ti_sci_intr_irq_domain_driver);
272+
273+
MODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ticom>");
274+
MODULE_DESCRIPTION("K3 Interrupt Router driver over TI SCI protocol");
275+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)