Skip to content

Commit f0bcd78

Browse files
author
Christophe Leroy (CS GROUP)
committed
soc: fsl: qe: Add an interrupt controller for QUICC Engine Ports
The QUICC Engine provides interrupts for a few I/O ports. This is handled via a separate interrupt ID and managed via a triplet of dedicated registers hosted by the SoC. Implement an interrupt driver for it so that those IRQs can then be linked to the related GPIOs. Link: https://lore.kernel.org/r/63f19db21a91729d91b3df336a56a7eb4206e561.1767804922.git.chleroy@kernel.org Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
1 parent 66a4ff3 commit f0bcd78

2 files changed

Lines changed: 143 additions & 1 deletion

File tree

drivers/soc/fsl/qe/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ obj-$(CONFIG_UCC_SLOW) += ucc_slow.o
1111
obj-$(CONFIG_UCC_FAST) += ucc_fast.o
1212
obj-$(CONFIG_QE_TDM) += qe_tdm.o
1313
obj-$(CONFIG_QE_USB) += usb.o
14-
obj-$(CONFIG_QE_GPIO) += gpio.o
14+
obj-$(CONFIG_QE_GPIO) += gpio.o qe_ports_ic.o

drivers/soc/fsl/qe/qe_ports_ic.c

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* QUICC ENGINE I/O Ports Interrupt Controller
4+
*
5+
* Copyright (c) 2025 Christophe Leroy CS GROUP France (christophe.leroy@csgroup.eu)
6+
*/
7+
8+
#include <linux/irq.h>
9+
#include <linux/irqdomain.h>
10+
#include <linux/platform_device.h>
11+
12+
/* QE IC registers offset */
13+
#define CEPIER 0x0c
14+
#define CEPIMR 0x10
15+
#define CEPICR 0x14
16+
17+
struct qepic_data {
18+
void __iomem *reg;
19+
struct irq_domain *host;
20+
};
21+
22+
static void qepic_mask(struct irq_data *d)
23+
{
24+
struct qepic_data *data = irq_data_get_irq_chip_data(d);
25+
26+
clrbits32(data->reg + CEPIMR, 1 << (31 - irqd_to_hwirq(d)));
27+
}
28+
29+
static void qepic_unmask(struct irq_data *d)
30+
{
31+
struct qepic_data *data = irq_data_get_irq_chip_data(d);
32+
33+
setbits32(data->reg + CEPIMR, 1 << (31 - irqd_to_hwirq(d)));
34+
}
35+
36+
static void qepic_end(struct irq_data *d)
37+
{
38+
struct qepic_data *data = irq_data_get_irq_chip_data(d);
39+
40+
out_be32(data->reg + CEPIER, 1 << (31 - irqd_to_hwirq(d)));
41+
}
42+
43+
static int qepic_set_type(struct irq_data *d, unsigned int flow_type)
44+
{
45+
struct qepic_data *data = irq_data_get_irq_chip_data(d);
46+
unsigned int vec = (unsigned int)irqd_to_hwirq(d);
47+
48+
switch (flow_type & IRQ_TYPE_SENSE_MASK) {
49+
case IRQ_TYPE_EDGE_FALLING:
50+
setbits32(data->reg + CEPICR, 1 << (31 - vec));
51+
return 0;
52+
case IRQ_TYPE_EDGE_BOTH:
53+
case IRQ_TYPE_NONE:
54+
clrbits32(data->reg + CEPICR, 1 << (31 - vec));
55+
return 0;
56+
}
57+
return -EINVAL;
58+
}
59+
60+
static struct irq_chip qepic = {
61+
.name = "QEPIC",
62+
.irq_mask = qepic_mask,
63+
.irq_unmask = qepic_unmask,
64+
.irq_eoi = qepic_end,
65+
.irq_set_type = qepic_set_type,
66+
};
67+
68+
static int qepic_get_irq(struct irq_desc *desc)
69+
{
70+
struct qepic_data *data = irq_desc_get_handler_data(desc);
71+
u32 event = in_be32(data->reg + CEPIER);
72+
73+
if (!event)
74+
return -1;
75+
76+
return irq_find_mapping(data->host, 32 - ffs(event));
77+
}
78+
79+
static void qepic_cascade(struct irq_desc *desc)
80+
{
81+
generic_handle_irq(qepic_get_irq(desc));
82+
}
83+
84+
static int qepic_host_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw)
85+
{
86+
irq_set_chip_data(virq, h->host_data);
87+
irq_set_chip_and_handler(virq, &qepic, handle_fasteoi_irq);
88+
return 0;
89+
}
90+
91+
static const struct irq_domain_ops qepic_host_ops = {
92+
.map = qepic_host_map,
93+
};
94+
95+
static int qepic_probe(struct platform_device *pdev)
96+
{
97+
struct device *dev = &pdev->dev;
98+
struct qepic_data *data;
99+
int irq;
100+
101+
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
102+
if (!data)
103+
return -ENOMEM;
104+
105+
data->reg = devm_platform_ioremap_resource(pdev, 0);
106+
if (IS_ERR(data->reg))
107+
return PTR_ERR(data->reg);
108+
109+
irq = platform_get_irq(pdev, 0);
110+
if (irq < 0)
111+
return irq;
112+
113+
data->host = irq_domain_add_linear(dev->of_node, 32, &qepic_host_ops, data);
114+
if (!data->host)
115+
return -ENODEV;
116+
117+
irq_set_handler_data(irq, data);
118+
irq_set_chained_handler(irq, qepic_cascade);
119+
120+
return 0;
121+
}
122+
123+
static const struct of_device_id qepic_match[] = {
124+
{
125+
.compatible = "fsl,mpc8323-qe-ports-ic",
126+
},
127+
{},
128+
};
129+
130+
static struct platform_driver qepic_driver = {
131+
.driver = {
132+
.name = "qe_ports_ic",
133+
.of_match_table = qepic_match,
134+
},
135+
.probe = qepic_probe,
136+
};
137+
138+
static int __init qepic_init(void)
139+
{
140+
return platform_driver_register(&qepic_driver);
141+
}
142+
arch_initcall(qepic_init);

0 commit comments

Comments
 (0)