1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Abilis Systems interrupt controller driver |
4 | * |
5 | * Copyright (C) Abilis Systems 2012 |
6 | * |
7 | * Author: Christian Ruppert <christian.ruppert@abilis.com> |
8 | */ |
9 | |
10 | #include <linux/interrupt.h> |
11 | #include <linux/irqdomain.h> |
12 | #include <linux/irq.h> |
13 | #include <linux/irqchip.h> |
14 | #include <linux/of_irq.h> |
15 | #include <linux/of_address.h> |
16 | #include <linux/io.h> |
17 | #include <linux/slab.h> |
18 | #include <linux/bitops.h> |
19 | |
20 | #define AB_IRQCTL_INT_ENABLE 0x00 |
21 | #define AB_IRQCTL_INT_STATUS 0x04 |
22 | #define AB_IRQCTL_SRC_MODE 0x08 |
23 | #define AB_IRQCTL_SRC_POLARITY 0x0C |
24 | #define AB_IRQCTL_INT_MODE 0x10 |
25 | #define AB_IRQCTL_INT_POLARITY 0x14 |
26 | #define AB_IRQCTL_INT_FORCE 0x18 |
27 | |
28 | #define AB_IRQCTL_MAXIRQ 32 |
29 | |
30 | static inline void ab_irqctl_writereg(struct irq_chip_generic *gc, u32 reg, |
31 | u32 val) |
32 | { |
33 | irq_reg_writel(gc, val, reg_offset: reg); |
34 | } |
35 | |
36 | static inline u32 ab_irqctl_readreg(struct irq_chip_generic *gc, u32 reg) |
37 | { |
38 | return irq_reg_readl(gc, reg_offset: reg); |
39 | } |
40 | |
41 | static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type) |
42 | { |
43 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d: data); |
44 | uint32_t im, mod, pol; |
45 | |
46 | im = data->mask; |
47 | |
48 | irq_gc_lock(gc); |
49 | |
50 | mod = ab_irqctl_readreg(gc, AB_IRQCTL_SRC_MODE) | im; |
51 | pol = ab_irqctl_readreg(gc, AB_IRQCTL_SRC_POLARITY) | im; |
52 | |
53 | switch (flow_type & IRQF_TRIGGER_MASK) { |
54 | case IRQ_TYPE_EDGE_FALLING: |
55 | pol ^= im; |
56 | break; |
57 | case IRQ_TYPE_LEVEL_HIGH: |
58 | mod ^= im; |
59 | break; |
60 | case IRQ_TYPE_NONE: |
61 | flow_type = IRQ_TYPE_LEVEL_LOW; |
62 | fallthrough; |
63 | case IRQ_TYPE_LEVEL_LOW: |
64 | mod ^= im; |
65 | pol ^= im; |
66 | break; |
67 | case IRQ_TYPE_EDGE_RISING: |
68 | break; |
69 | default: |
70 | irq_gc_unlock(gc); |
71 | pr_err("%s: Cannot assign multiple trigger modes to IRQ %d.\n" , |
72 | __func__, data->irq); |
73 | return -EBADR; |
74 | } |
75 | |
76 | irqd_set_trigger_type(d: data, type: flow_type); |
77 | irq_setup_alt_chip(d: data, type: flow_type); |
78 | |
79 | ab_irqctl_writereg(gc, AB_IRQCTL_SRC_MODE, val: mod); |
80 | ab_irqctl_writereg(gc, AB_IRQCTL_SRC_POLARITY, val: pol); |
81 | ab_irqctl_writereg(gc, AB_IRQCTL_INT_STATUS, val: im); |
82 | |
83 | irq_gc_unlock(gc); |
84 | |
85 | return IRQ_SET_MASK_OK; |
86 | } |
87 | |
88 | static void tb10x_irq_cascade(struct irq_desc *desc) |
89 | { |
90 | struct irq_domain *domain = irq_desc_get_handler_data(desc); |
91 | unsigned int irq = irq_desc_get_irq(desc); |
92 | |
93 | generic_handle_domain_irq(domain, hwirq: irq); |
94 | } |
95 | |
96 | static int __init of_tb10x_init_irq(struct device_node *ictl, |
97 | struct device_node *parent) |
98 | { |
99 | int i, ret, nrirqs = of_irq_count(dev: ictl); |
100 | struct resource mem; |
101 | struct irq_chip_generic *gc; |
102 | struct irq_domain *domain; |
103 | void __iomem *reg_base; |
104 | |
105 | if (of_address_to_resource(dev: ictl, index: 0, r: &mem)) { |
106 | pr_err("%pOFn: No registers declared in DeviceTree.\n" , |
107 | ictl); |
108 | return -EINVAL; |
109 | } |
110 | |
111 | if (!request_mem_region(mem.start, resource_size(&mem), |
112 | ictl->full_name)) { |
113 | pr_err("%pOFn: Request mem region failed.\n" , ictl); |
114 | return -EBUSY; |
115 | } |
116 | |
117 | reg_base = ioremap(offset: mem.start, size: resource_size(res: &mem)); |
118 | if (!reg_base) { |
119 | ret = -EBUSY; |
120 | pr_err("%pOFn: ioremap failed.\n" , ictl); |
121 | goto ioremap_fail; |
122 | } |
123 | |
124 | domain = irq_domain_add_linear(of_node: ictl, AB_IRQCTL_MAXIRQ, |
125 | ops: &irq_generic_chip_ops, NULL); |
126 | if (!domain) { |
127 | ret = -ENOMEM; |
128 | pr_err("%pOFn: Could not register interrupt domain.\n" , |
129 | ictl); |
130 | goto irq_domain_add_fail; |
131 | } |
132 | |
133 | ret = irq_alloc_domain_generic_chips(domain, AB_IRQCTL_MAXIRQ, |
134 | 2, ictl->name, handle_level_irq, |
135 | IRQ_NOREQUEST, IRQ_NOPROBE, |
136 | IRQ_GC_INIT_MASK_CACHE); |
137 | if (ret) { |
138 | pr_err("%pOFn: Could not allocate generic interrupt chip.\n" , |
139 | ictl); |
140 | goto gc_alloc_fail; |
141 | } |
142 | |
143 | gc = domain->gc->gc[0]; |
144 | gc->reg_base = reg_base; |
145 | |
146 | gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK; |
147 | gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; |
148 | gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; |
149 | gc->chip_types[0].chip.irq_set_type = tb10x_irq_set_type; |
150 | gc->chip_types[0].regs.mask = AB_IRQCTL_INT_ENABLE; |
151 | |
152 | gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH; |
153 | gc->chip_types[1].chip.name = gc->chip_types[0].chip.name; |
154 | gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit; |
155 | gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit; |
156 | gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit; |
157 | gc->chip_types[1].chip.irq_set_type = tb10x_irq_set_type; |
158 | gc->chip_types[1].regs.ack = AB_IRQCTL_INT_STATUS; |
159 | gc->chip_types[1].regs.mask = AB_IRQCTL_INT_ENABLE; |
160 | gc->chip_types[1].handler = handle_edge_irq; |
161 | |
162 | for (i = 0; i < nrirqs; i++) { |
163 | unsigned int irq = irq_of_parse_and_map(node: ictl, index: i); |
164 | |
165 | irq_set_chained_handler_and_data(irq, handle: tb10x_irq_cascade, |
166 | data: domain); |
167 | } |
168 | |
169 | ab_irqctl_writereg(gc, AB_IRQCTL_INT_ENABLE, val: 0); |
170 | ab_irqctl_writereg(gc, AB_IRQCTL_INT_MODE, val: 0); |
171 | ab_irqctl_writereg(gc, AB_IRQCTL_INT_POLARITY, val: 0); |
172 | ab_irqctl_writereg(gc, AB_IRQCTL_INT_STATUS, val: ~0UL); |
173 | |
174 | return 0; |
175 | |
176 | gc_alloc_fail: |
177 | irq_domain_remove(host: domain); |
178 | irq_domain_add_fail: |
179 | iounmap(addr: reg_base); |
180 | ioremap_fail: |
181 | release_mem_region(mem.start, resource_size(&mem)); |
182 | return ret; |
183 | } |
184 | IRQCHIP_DECLARE(tb10x_intc, "abilis,tb10x-ictl" , of_tb10x_init_irq); |
185 | |