1 // SPDX-License-Identifier: GPL-2.0
3 * Driver for the Microchip LAN966x outbound interrupt controller
5 * Copyright (c) 2024 Technology Inc. and its subsidiaries.
8 * Horatiu Vultur <horatiu.vultur@microchip.com>
9 * Clément Léger <clement.leger@bootlin.com>
10 * Herve Codina <herve.codina@bootlin.com>
13 #include <linux/interrupt.h>
14 #include <linux/irqchip/chained_irq.h>
15 #include <linux/irqchip.h>
16 #include <linux/irq.h>
17 #include <linux/mod_devicetable.h>
18 #include <linux/module.h>
19 #include <linux/platform_device.h>
20 #include <linux/slab.h>
22 struct lan966x_oic_chip_regs
{
30 struct lan966x_oic_data
{
35 #define LAN966X_OIC_NR_IRQ 86
37 /* Interrupt sticky status */
38 #define LAN966X_OIC_INTR_STICKY 0x30
39 #define LAN966X_OIC_INTR_STICKY1 0x34
40 #define LAN966X_OIC_INTR_STICKY2 0x38
42 /* Interrupt enable */
43 #define LAN966X_OIC_INTR_ENA 0x48
44 #define LAN966X_OIC_INTR_ENA1 0x4c
45 #define LAN966X_OIC_INTR_ENA2 0x50
47 /* Atomic clear of interrupt enable */
48 #define LAN966X_OIC_INTR_ENA_CLR 0x54
49 #define LAN966X_OIC_INTR_ENA_CLR1 0x58
50 #define LAN966X_OIC_INTR_ENA_CLR2 0x5c
52 /* Atomic set of interrupt */
53 #define LAN966X_OIC_INTR_ENA_SET 0x60
54 #define LAN966X_OIC_INTR_ENA_SET1 0x64
55 #define LAN966X_OIC_INTR_ENA_SET2 0x68
57 /* Mapping of source to destination interrupts (_n = 0..8) */
58 #define LAN966X_OIC_DST_INTR_MAP(_n) (0x78 + (_n) * 4)
59 #define LAN966X_OIC_DST_INTR_MAP1(_n) (0x9c + (_n) * 4)
60 #define LAN966X_OIC_DST_INTR_MAP2(_n) (0xc0 + (_n) * 4)
62 /* Currently active interrupt sources per destination (_n = 0..8) */
63 #define LAN966X_OIC_DST_INTR_IDENT(_n) (0xe4 + (_n) * 4)
64 #define LAN966X_OIC_DST_INTR_IDENT1(_n) (0x108 + (_n) * 4)
65 #define LAN966X_OIC_DST_INTR_IDENT2(_n) (0x12c + (_n) * 4)
67 static unsigned int lan966x_oic_irq_startup(struct irq_data
*data
)
69 struct irq_chip_generic
*gc
= irq_data_get_irq_chip_data(data
);
70 struct irq_chip_type
*ct
= irq_data_get_chip_type(data
);
71 struct lan966x_oic_chip_regs
*chip_regs
= gc
->private;
76 /* Map the source interrupt to the destination */
77 map
= irq_reg_readl(gc
, chip_regs
->reg_off_map
);
79 irq_reg_writel(gc
, map
, chip_regs
->reg_off_map
);
83 ct
->chip
.irq_ack(data
);
84 ct
->chip
.irq_unmask(data
);
89 static void lan966x_oic_irq_shutdown(struct irq_data
*data
)
91 struct irq_chip_generic
*gc
= irq_data_get_irq_chip_data(data
);
92 struct irq_chip_type
*ct
= irq_data_get_chip_type(data
);
93 struct lan966x_oic_chip_regs
*chip_regs
= gc
->private;
96 ct
->chip
.irq_mask(data
);
100 /* Unmap the interrupt */
101 map
= irq_reg_readl(gc
, chip_regs
->reg_off_map
);
103 irq_reg_writel(gc
, map
, chip_regs
->reg_off_map
);
108 static int lan966x_oic_irq_set_type(struct irq_data
*data
,
109 unsigned int flow_type
)
111 if (flow_type
!= IRQ_TYPE_LEVEL_HIGH
) {
112 pr_err("lan966x oic doesn't support flow type %d\n", flow_type
);
119 static void lan966x_oic_irq_handler_domain(struct irq_domain
*d
, u32 first_irq
)
121 struct irq_chip_generic
*gc
= irq_get_domain_generic_chip(d
, first_irq
);
122 struct lan966x_oic_chip_regs
*chip_regs
= gc
->private;
126 ident
= irq_reg_readl(gc
, chip_regs
->reg_off_ident
);
130 for_each_set_bit(hwirq
, &ident
, 32)
131 generic_handle_domain_irq(d
, hwirq
+ first_irq
);
134 static void lan966x_oic_irq_handler(struct irq_desc
*desc
)
136 struct irq_domain
*d
= irq_desc_get_handler_data(desc
);
137 struct irq_chip
*chip
= irq_desc_get_chip(desc
);
139 chained_irq_enter(chip
, desc
);
140 lan966x_oic_irq_handler_domain(d
, 0);
141 lan966x_oic_irq_handler_domain(d
, 32);
142 lan966x_oic_irq_handler_domain(d
, 64);
143 chained_irq_exit(chip
, desc
);
146 static struct lan966x_oic_chip_regs lan966x_oic_chip_regs
[3] = {
148 .reg_off_ena_set
= LAN966X_OIC_INTR_ENA_SET
,
149 .reg_off_ena_clr
= LAN966X_OIC_INTR_ENA_CLR
,
150 .reg_off_sticky
= LAN966X_OIC_INTR_STICKY
,
151 .reg_off_ident
= LAN966X_OIC_DST_INTR_IDENT(0),
152 .reg_off_map
= LAN966X_OIC_DST_INTR_MAP(0),
154 .reg_off_ena_set
= LAN966X_OIC_INTR_ENA_SET1
,
155 .reg_off_ena_clr
= LAN966X_OIC_INTR_ENA_CLR1
,
156 .reg_off_sticky
= LAN966X_OIC_INTR_STICKY1
,
157 .reg_off_ident
= LAN966X_OIC_DST_INTR_IDENT1(0),
158 .reg_off_map
= LAN966X_OIC_DST_INTR_MAP1(0),
160 .reg_off_ena_set
= LAN966X_OIC_INTR_ENA_SET2
,
161 .reg_off_ena_clr
= LAN966X_OIC_INTR_ENA_CLR2
,
162 .reg_off_sticky
= LAN966X_OIC_INTR_STICKY2
,
163 .reg_off_ident
= LAN966X_OIC_DST_INTR_IDENT2(0),
164 .reg_off_map
= LAN966X_OIC_DST_INTR_MAP2(0),
168 static int lan966x_oic_chip_init(struct irq_chip_generic
*gc
)
170 struct lan966x_oic_data
*lan966x_oic
= gc
->domain
->host_data
;
171 struct lan966x_oic_chip_regs
*chip_regs
;
173 chip_regs
= &lan966x_oic_chip_regs
[gc
->irq_base
/ 32];
175 gc
->reg_base
= lan966x_oic
->regs
;
176 gc
->chip_types
[0].regs
.enable
= chip_regs
->reg_off_ena_set
;
177 gc
->chip_types
[0].regs
.disable
= chip_regs
->reg_off_ena_clr
;
178 gc
->chip_types
[0].regs
.ack
= chip_regs
->reg_off_sticky
;
179 gc
->chip_types
[0].chip
.irq_startup
= lan966x_oic_irq_startup
;
180 gc
->chip_types
[0].chip
.irq_shutdown
= lan966x_oic_irq_shutdown
;
181 gc
->chip_types
[0].chip
.irq_set_type
= lan966x_oic_irq_set_type
;
182 gc
->chip_types
[0].chip
.irq_mask
= irq_gc_mask_disable_reg
;
183 gc
->chip_types
[0].chip
.irq_unmask
= irq_gc_unmask_enable_reg
;
184 gc
->chip_types
[0].chip
.irq_ack
= irq_gc_ack_set_bit
;
185 gc
->private = chip_regs
;
187 /* Disable all interrupts handled by this chip */
188 irq_reg_writel(gc
, ~0U, chip_regs
->reg_off_ena_clr
);
193 static void lan966x_oic_chip_exit(struct irq_chip_generic
*gc
)
195 /* Disable and ack all interrupts handled by this chip */
196 irq_reg_writel(gc
, ~0U, gc
->chip_types
[0].regs
.disable
);
197 irq_reg_writel(gc
, ~0U, gc
->chip_types
[0].regs
.ack
);
200 static int lan966x_oic_domain_init(struct irq_domain
*d
)
202 struct lan966x_oic_data
*lan966x_oic
= d
->host_data
;
204 irq_set_chained_handler_and_data(lan966x_oic
->irq
, lan966x_oic_irq_handler
, d
);
209 static void lan966x_oic_domain_exit(struct irq_domain
*d
)
211 struct lan966x_oic_data
*lan966x_oic
= d
->host_data
;
213 irq_set_chained_handler_and_data(lan966x_oic
->irq
, NULL
, NULL
);
216 static int lan966x_oic_probe(struct platform_device
*pdev
)
218 struct irq_domain_chip_generic_info dgc_info
= {
219 .name
= "lan966x-oic",
220 .handler
= handle_level_irq
,
223 .init
= lan966x_oic_chip_init
,
224 .exit
= lan966x_oic_chip_exit
,
226 struct irq_domain_info d_info
= {
227 .fwnode
= of_node_to_fwnode(pdev
->dev
.of_node
),
228 .domain_flags
= IRQ_DOMAIN_FLAG_DESTROY_GC
,
229 .size
= LAN966X_OIC_NR_IRQ
,
230 .hwirq_max
= LAN966X_OIC_NR_IRQ
,
231 .ops
= &irq_generic_chip_ops
,
232 .dgc_info
= &dgc_info
,
233 .init
= lan966x_oic_domain_init
,
234 .exit
= lan966x_oic_domain_exit
,
236 struct lan966x_oic_data
*lan966x_oic
;
237 struct device
*dev
= &pdev
->dev
;
238 struct irq_domain
*domain
;
240 lan966x_oic
= devm_kmalloc(dev
, sizeof(*lan966x_oic
), GFP_KERNEL
);
244 lan966x_oic
->regs
= devm_platform_ioremap_resource(pdev
, 0);
245 if (IS_ERR(lan966x_oic
->regs
))
246 return dev_err_probe(dev
, PTR_ERR(lan966x_oic
->regs
),
247 "failed to map resource\n");
249 lan966x_oic
->irq
= platform_get_irq(pdev
, 0);
250 if (lan966x_oic
->irq
< 0)
251 return dev_err_probe(dev
, lan966x_oic
->irq
, "failed to get the IRQ\n");
253 d_info
.host_data
= lan966x_oic
;
254 domain
= devm_irq_domain_instantiate(dev
, &d_info
);
256 return dev_err_probe(dev
, PTR_ERR(domain
),
257 "failed to instantiate the IRQ domain\n");
261 static const struct of_device_id lan966x_oic_of_match
[] = {
262 { .compatible
= "microchip,lan966x-oic" },
265 MODULE_DEVICE_TABLE(of
, lan966x_oic_of_match
);
267 static struct platform_driver lan966x_oic_driver
= {
268 .probe
= lan966x_oic_probe
,
270 .name
= "lan966x-oic",
271 .of_match_table
= lan966x_oic_of_match
,
274 module_platform_driver(lan966x_oic_driver
);
276 MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
277 MODULE_DESCRIPTION("Microchip LAN966x OIC driver");
278 MODULE_LICENSE("GPL");