1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Aspeed AST24XX, AST25XX, and AST26XX SCU Interrupt Controller |
4 | * Copyright 2019 IBM Corporation |
5 | * |
6 | * Eddie James <eajames@linux.ibm.com> |
7 | */ |
8 | |
9 | #include <linux/bitops.h> |
10 | #include <linux/irq.h> |
11 | #include <linux/irqchip.h> |
12 | #include <linux/irqchip/chained_irq.h> |
13 | #include <linux/irqdomain.h> |
14 | #include <linux/mfd/syscon.h> |
15 | #include <linux/of_irq.h> |
16 | #include <linux/regmap.h> |
17 | |
18 | #define ASPEED_SCU_IC_REG 0x018 |
19 | #define ASPEED_SCU_IC_SHIFT 0 |
20 | #define ASPEED_SCU_IC_ENABLE GENMASK(15, ASPEED_SCU_IC_SHIFT) |
21 | #define ASPEED_SCU_IC_NUM_IRQS 7 |
22 | #define ASPEED_SCU_IC_STATUS GENMASK(28, 16) |
23 | #define ASPEED_SCU_IC_STATUS_SHIFT 16 |
24 | |
25 | #define ASPEED_AST2600_SCU_IC0_REG 0x560 |
26 | #define ASPEED_AST2600_SCU_IC0_SHIFT 0 |
27 | #define ASPEED_AST2600_SCU_IC0_ENABLE \ |
28 | GENMASK(5, ASPEED_AST2600_SCU_IC0_SHIFT) |
29 | #define ASPEED_AST2600_SCU_IC0_NUM_IRQS 6 |
30 | |
31 | #define ASPEED_AST2600_SCU_IC1_REG 0x570 |
32 | #define ASPEED_AST2600_SCU_IC1_SHIFT 4 |
33 | #define ASPEED_AST2600_SCU_IC1_ENABLE \ |
34 | GENMASK(5, ASPEED_AST2600_SCU_IC1_SHIFT) |
35 | #define ASPEED_AST2600_SCU_IC1_NUM_IRQS 2 |
36 | |
37 | struct aspeed_scu_ic { |
38 | unsigned long irq_enable; |
39 | unsigned long irq_shift; |
40 | unsigned int num_irqs; |
41 | unsigned int reg; |
42 | struct regmap *scu; |
43 | struct irq_domain *irq_domain; |
44 | }; |
45 | |
46 | static void aspeed_scu_ic_irq_handler(struct irq_desc *desc) |
47 | { |
48 | unsigned int sts; |
49 | unsigned long bit; |
50 | unsigned long enabled; |
51 | unsigned long max; |
52 | unsigned long status; |
53 | struct aspeed_scu_ic *scu_ic = irq_desc_get_handler_data(desc); |
54 | struct irq_chip *chip = irq_desc_get_chip(desc); |
55 | unsigned int mask = scu_ic->irq_enable << ASPEED_SCU_IC_STATUS_SHIFT; |
56 | |
57 | chained_irq_enter(chip, desc); |
58 | |
59 | /* |
60 | * The SCU IC has just one register to control its operation and read |
61 | * status. The interrupt enable bits occupy the lower 16 bits of the |
62 | * register, while the interrupt status bits occupy the upper 16 bits. |
63 | * The status bit for a given interrupt is always 16 bits shifted from |
64 | * the enable bit for the same interrupt. |
65 | * Therefore, perform the IRQ operations in the enable bit space by |
66 | * shifting the status down to get the mapping and then back up to |
67 | * clear the bit. |
68 | */ |
69 | regmap_read(map: scu_ic->scu, reg: scu_ic->reg, val: &sts); |
70 | enabled = sts & scu_ic->irq_enable; |
71 | status = (sts >> ASPEED_SCU_IC_STATUS_SHIFT) & enabled; |
72 | |
73 | bit = scu_ic->irq_shift; |
74 | max = scu_ic->num_irqs + bit; |
75 | |
76 | for_each_set_bit_from(bit, &status, max) { |
77 | generic_handle_domain_irq(domain: scu_ic->irq_domain, |
78 | hwirq: bit - scu_ic->irq_shift); |
79 | |
80 | regmap_write_bits(map: scu_ic->scu, reg: scu_ic->reg, mask, |
81 | BIT(bit + ASPEED_SCU_IC_STATUS_SHIFT)); |
82 | } |
83 | |
84 | chained_irq_exit(chip, desc); |
85 | } |
86 | |
87 | static void aspeed_scu_ic_irq_mask(struct irq_data *data) |
88 | { |
89 | struct aspeed_scu_ic *scu_ic = irq_data_get_irq_chip_data(d: data); |
90 | unsigned int mask = BIT(data->hwirq + scu_ic->irq_shift) | |
91 | (scu_ic->irq_enable << ASPEED_SCU_IC_STATUS_SHIFT); |
92 | |
93 | /* |
94 | * Status bits are cleared by writing 1. In order to prevent the mask |
95 | * operation from clearing the status bits, they should be under the |
96 | * mask and written with 0. |
97 | */ |
98 | regmap_update_bits(map: scu_ic->scu, reg: scu_ic->reg, mask, val: 0); |
99 | } |
100 | |
101 | static void aspeed_scu_ic_irq_unmask(struct irq_data *data) |
102 | { |
103 | struct aspeed_scu_ic *scu_ic = irq_data_get_irq_chip_data(d: data); |
104 | unsigned int bit = BIT(data->hwirq + scu_ic->irq_shift); |
105 | unsigned int mask = bit | |
106 | (scu_ic->irq_enable << ASPEED_SCU_IC_STATUS_SHIFT); |
107 | |
108 | /* |
109 | * Status bits are cleared by writing 1. In order to prevent the unmask |
110 | * operation from clearing the status bits, they should be under the |
111 | * mask and written with 0. |
112 | */ |
113 | regmap_update_bits(map: scu_ic->scu, reg: scu_ic->reg, mask, val: bit); |
114 | } |
115 | |
116 | static int aspeed_scu_ic_irq_set_affinity(struct irq_data *data, |
117 | const struct cpumask *dest, |
118 | bool force) |
119 | { |
120 | return -EINVAL; |
121 | } |
122 | |
123 | static struct irq_chip aspeed_scu_ic_chip = { |
124 | .name = "aspeed-scu-ic" , |
125 | .irq_mask = aspeed_scu_ic_irq_mask, |
126 | .irq_unmask = aspeed_scu_ic_irq_unmask, |
127 | .irq_set_affinity = aspeed_scu_ic_irq_set_affinity, |
128 | }; |
129 | |
130 | static int aspeed_scu_ic_map(struct irq_domain *domain, unsigned int irq, |
131 | irq_hw_number_t hwirq) |
132 | { |
133 | irq_set_chip_and_handler(irq, chip: &aspeed_scu_ic_chip, handle: handle_level_irq); |
134 | irq_set_chip_data(irq, data: domain->host_data); |
135 | |
136 | return 0; |
137 | } |
138 | |
139 | static const struct irq_domain_ops aspeed_scu_ic_domain_ops = { |
140 | .map = aspeed_scu_ic_map, |
141 | }; |
142 | |
143 | static int aspeed_scu_ic_of_init_common(struct aspeed_scu_ic *scu_ic, |
144 | struct device_node *node) |
145 | { |
146 | int irq; |
147 | int rc = 0; |
148 | |
149 | if (!node->parent) { |
150 | rc = -ENODEV; |
151 | goto err; |
152 | } |
153 | |
154 | scu_ic->scu = syscon_node_to_regmap(np: node->parent); |
155 | if (IS_ERR(ptr: scu_ic->scu)) { |
156 | rc = PTR_ERR(ptr: scu_ic->scu); |
157 | goto err; |
158 | } |
159 | regmap_write_bits(map: scu_ic->scu, reg: scu_ic->reg, ASPEED_SCU_IC_STATUS, ASPEED_SCU_IC_STATUS); |
160 | regmap_write_bits(map: scu_ic->scu, reg: scu_ic->reg, ASPEED_SCU_IC_ENABLE, val: 0); |
161 | |
162 | irq = irq_of_parse_and_map(node, index: 0); |
163 | if (!irq) { |
164 | rc = -EINVAL; |
165 | goto err; |
166 | } |
167 | |
168 | scu_ic->irq_domain = irq_domain_add_linear(of_node: node, size: scu_ic->num_irqs, |
169 | ops: &aspeed_scu_ic_domain_ops, |
170 | host_data: scu_ic); |
171 | if (!scu_ic->irq_domain) { |
172 | rc = -ENOMEM; |
173 | goto err; |
174 | } |
175 | |
176 | irq_set_chained_handler_and_data(irq, handle: aspeed_scu_ic_irq_handler, |
177 | data: scu_ic); |
178 | |
179 | return 0; |
180 | |
181 | err: |
182 | kfree(objp: scu_ic); |
183 | |
184 | return rc; |
185 | } |
186 | |
187 | static int __init aspeed_scu_ic_of_init(struct device_node *node, |
188 | struct device_node *parent) |
189 | { |
190 | struct aspeed_scu_ic *scu_ic = kzalloc(size: sizeof(*scu_ic), GFP_KERNEL); |
191 | |
192 | if (!scu_ic) |
193 | return -ENOMEM; |
194 | |
195 | scu_ic->irq_enable = ASPEED_SCU_IC_ENABLE; |
196 | scu_ic->irq_shift = ASPEED_SCU_IC_SHIFT; |
197 | scu_ic->num_irqs = ASPEED_SCU_IC_NUM_IRQS; |
198 | scu_ic->reg = ASPEED_SCU_IC_REG; |
199 | |
200 | return aspeed_scu_ic_of_init_common(scu_ic, node); |
201 | } |
202 | |
203 | static int __init aspeed_ast2600_scu_ic0_of_init(struct device_node *node, |
204 | struct device_node *parent) |
205 | { |
206 | struct aspeed_scu_ic *scu_ic = kzalloc(size: sizeof(*scu_ic), GFP_KERNEL); |
207 | |
208 | if (!scu_ic) |
209 | return -ENOMEM; |
210 | |
211 | scu_ic->irq_enable = ASPEED_AST2600_SCU_IC0_ENABLE; |
212 | scu_ic->irq_shift = ASPEED_AST2600_SCU_IC0_SHIFT; |
213 | scu_ic->num_irqs = ASPEED_AST2600_SCU_IC0_NUM_IRQS; |
214 | scu_ic->reg = ASPEED_AST2600_SCU_IC0_REG; |
215 | |
216 | return aspeed_scu_ic_of_init_common(scu_ic, node); |
217 | } |
218 | |
219 | static int __init aspeed_ast2600_scu_ic1_of_init(struct device_node *node, |
220 | struct device_node *parent) |
221 | { |
222 | struct aspeed_scu_ic *scu_ic = kzalloc(size: sizeof(*scu_ic), GFP_KERNEL); |
223 | |
224 | if (!scu_ic) |
225 | return -ENOMEM; |
226 | |
227 | scu_ic->irq_enable = ASPEED_AST2600_SCU_IC1_ENABLE; |
228 | scu_ic->irq_shift = ASPEED_AST2600_SCU_IC1_SHIFT; |
229 | scu_ic->num_irqs = ASPEED_AST2600_SCU_IC1_NUM_IRQS; |
230 | scu_ic->reg = ASPEED_AST2600_SCU_IC1_REG; |
231 | |
232 | return aspeed_scu_ic_of_init_common(scu_ic, node); |
233 | } |
234 | |
235 | IRQCHIP_DECLARE(ast2400_scu_ic, "aspeed,ast2400-scu-ic" , aspeed_scu_ic_of_init); |
236 | IRQCHIP_DECLARE(ast2500_scu_ic, "aspeed,ast2500-scu-ic" , aspeed_scu_ic_of_init); |
237 | IRQCHIP_DECLARE(ast2600_scu_ic0, "aspeed,ast2600-scu-ic0" , |
238 | aspeed_ast2600_scu_ic0_of_init); |
239 | IRQCHIP_DECLARE(ast2600_scu_ic1, "aspeed,ast2600-scu-ic1" , |
240 | aspeed_ast2600_scu_ic1_of_init); |
241 | |