1 /*
2 * (C) Copyright 2019 Rockchip Electronics Co., Ltd
3 *
4 * SPDX-License-Identifier: GPL-2.0+
5 */
6
7 #include <common.h>
8 #include <dm.h>
9 #include <fdtdec.h>
10 #include <malloc.h>
11 #include <asm/io.h>
12 #include <asm/u-boot-arm.h>
13 #include <irq-generic.h>
14 #include "irq-internal.h"
15
16 DECLARE_GLOBAL_DATA_PTR;
17
18 static LIST_HEAD(virq_desc_head);
19 static u32 virq_id = PLATFORM_MAX_IRQ;
20
virq_id_alloc(void)21 static u32 virq_id_alloc(void)
22 {
23 return ++virq_id;
24 }
25
26 struct virq_data {
27 int irq;
28 u32 flag;
29 u32 count;
30
31 void *data;
32 interrupt_handler_t *handle_irq;
33 };
34
35 /* The structure to maintail the irqchip and child virqs */
36 struct virq_desc {
37 struct virq_chip *chip; /* irq chip */
38 struct virq_data *virqs; /* child irq data list */
39 struct udevice *parent; /* parent device */
40 int pirq; /* parent irq */
41 int use_count; /* enable count */
42 int irq_base; /* child irq base */
43 int irq_end; /* child irq end */
44 uint reg_stride;
45 uint unalign_reg_idx;
46 uint unalign_reg_stride;
47 uint *status_buf;
48
49 struct list_head node;
50 };
51
find_virq_desc(int irq)52 static struct virq_desc *find_virq_desc(int irq)
53 {
54 struct virq_desc *desc;
55 struct list_head *node;
56
57 list_for_each(node, &virq_desc_head) {
58 desc = list_entry(node, struct virq_desc, node);
59 if (irq >= desc->irq_base && irq <= desc->irq_end)
60 return desc;
61 }
62
63 return NULL;
64 }
65
find_virq_desc_by_pirq(int parent_irq)66 static struct virq_desc *find_virq_desc_by_pirq(int parent_irq)
67 {
68 struct virq_desc *desc;
69 struct list_head *node;
70
71 list_for_each(node, &virq_desc_head) {
72 desc = list_entry(node, struct virq_desc, node);
73 if (parent_irq == desc->pirq)
74 return desc;
75 }
76
77 return NULL;
78 }
79
virq_to_irq(struct virq_chip * chip,int virq)80 int virq_to_irq(struct virq_chip *chip, int virq)
81 {
82 struct virq_desc *desc;
83 struct list_head *node;
84 int irq;
85
86 if (!chip)
87 return -EINVAL;
88
89 list_for_each(node, &virq_desc_head) {
90 desc = list_entry(node, struct virq_desc, node);
91 if (desc->chip == chip) {
92 irq = desc->irq_base + virq;
93 if (irq >= desc->irq_base && irq <= desc->irq_end)
94 return irq;
95 }
96 }
97
98 return -ENONET;
99 }
100
bad_virq(int irq)101 int bad_virq(int irq)
102 {
103 return !find_virq_desc(irq);
104 }
105
virqs_show(int pirq)106 void virqs_show(int pirq)
107 {
108 struct virq_data *vdata;
109 struct virq_desc *desc;
110 struct udevice *dev;
111 int num;
112 int i;
113
114 desc = find_virq_desc_by_pirq(pirq);
115 if (!desc)
116 return;
117
118 vdata = desc->virqs;
119 num = desc->irq_end - desc->irq_base;
120
121 for (i = 0; i < num; i++) {
122 if (!vdata[i].handle_irq)
123 continue;
124
125 dev = (struct udevice *)vdata[i].data;
126 printf(" %3d %d 0x%08lx %-12s |-- %-12s %d\n",
127 vdata[i].irq,
128 vdata[i].flag & IRQ_FLG_ENABLE ? 1 : 0,
129 (ulong)vdata[i].handle_irq, dev->driver->name, dev->name,
130 vdata[i].count);
131 }
132 }
133
virq_install_handler(int irq,interrupt_handler_t * handler,void * data)134 int virq_install_handler(int irq, interrupt_handler_t *handler, void *data)
135 {
136 struct virq_desc *desc;
137 int virq;
138
139 if (!handler)
140 return -EINVAL;
141
142 desc = find_virq_desc(irq);
143 if (!desc)
144 return -ENOENT;
145
146 virq = irq - desc->irq_base;
147 if (desc->virqs[virq].handle_irq)
148 return -EBUSY;
149
150 desc->virqs[virq].handle_irq = handler;
151 desc->virqs[virq].data = data;
152
153 return 0;
154 }
155
virq_free_handler(int irq)156 void virq_free_handler(int irq)
157 {
158 struct virq_desc *desc;
159 int virq;
160
161 desc = find_virq_desc(irq);
162 if (!desc)
163 return;
164
165 virq = irq - desc->irq_base;
166 desc->virqs[virq].handle_irq = NULL;
167 desc->virqs[virq].data = NULL;
168 }
169
reg_base_get(struct virq_desc * desc,uint reg_base,int idx)170 static uint reg_base_get(struct virq_desc *desc, uint reg_base, int idx)
171 {
172 int reg_addr;
173
174 if (idx <= desc->unalign_reg_idx) {
175 reg_addr = reg_base + (idx * desc->unalign_reg_stride);
176 } else {
177 reg_addr = reg_base +
178 (desc->unalign_reg_idx * desc->unalign_reg_stride);
179 reg_addr += (idx - desc->unalign_reg_idx) * desc->reg_stride;
180 }
181
182 return reg_addr;
183 }
184
virq_chip_generic_handler(int pirq,void * pdata)185 void virq_chip_generic_handler(int pirq, void *pdata)
186 {
187 struct virq_chip *chip;
188 struct virq_desc *desc;
189 struct virq_data *vdata;
190 struct udevice *parent;
191 uint status_reg;
192 void *data;
193 int irq;
194 int ret;
195 int i;
196
197 desc = find_virq_desc_by_pirq(pirq);
198 if (!desc)
199 return;
200
201 chip = desc->chip;
202 vdata = desc->virqs;
203 parent = (struct udevice *)pdata;
204
205 if (!chip || !vdata || !parent)
206 return;
207
208 /* Read all status register */
209 for (i = 0; i < chip->num_regs; i++) {
210 status_reg = reg_base_get(desc, chip->status_base, i);
211 desc->status_buf[i] = chip->read(parent, status_reg);
212 if (desc->status_buf[i] < 0) {
213 printf("%s: Read status register 0x%x failed, ret=%d\n",
214 __func__, status_reg, desc->status_buf[i]);
215 }
216 }
217
218 /* Handle all virq handler */
219 for (i = 0; i < chip->num_irqs; i++) {
220 if (desc->status_buf[chip->irqs[i].reg_offset] &
221 chip->irqs[i].mask) {
222 irq = vdata[i].irq;
223 data = vdata[i].data;
224
225 if (vdata[i].handle_irq) {
226 vdata[i].count++;
227 vdata[i].handle_irq(irq, data);
228 }
229 }
230 }
231
232 /* Clear all status register */
233 for (i = 0; i < chip->num_regs; i++) {
234 status_reg = reg_base_get(desc, chip->status_base, i);
235 ret = chip->write(parent, status_reg, ~0U);
236 if (ret)
237 printf("%s: Clear status register 0x%x failed, ret=%d\n",
238 __func__, status_reg, ret);
239 }
240 }
241
virq_add_chip(struct udevice * dev,struct virq_chip * chip,int irq)242 int virq_add_chip(struct udevice *dev, struct virq_chip *chip, int irq)
243 {
244 struct virq_data *vdata;
245 struct virq_desc *desc;
246 uint *status_buf;
247 uint status_reg;
248 uint mask_reg;
249 int ret;
250 int i;
251
252 if (irq < 0)
253 return -EINVAL;
254
255 desc = (struct virq_desc *)malloc(sizeof(*desc));
256 if (!desc)
257 return -ENOMEM;
258
259 vdata = (struct virq_data *)calloc(sizeof(*vdata), chip->num_irqs);
260 if (!vdata) {
261 ret = -ENOMEM;
262 goto free1;
263 }
264
265 status_buf = (uint *)calloc(sizeof(*status_buf), chip->num_irqs);
266 if (!status_buf) {
267 ret = -ENOMEM;
268 goto free2;
269 }
270
271 for (i = 0; i < chip->num_irqs; i++)
272 vdata[i].irq = virq_id_alloc();
273
274 desc->parent = dev;
275 desc->pirq = irq;
276 desc->chip = chip;
277 desc->virqs = vdata;
278 desc->use_count = 0;
279 desc->irq_base = vdata[0].irq;
280 desc->irq_end = vdata[chip->num_irqs - 1].irq;
281 desc->status_buf = status_buf;
282 desc->reg_stride = chip->irq_reg_stride ? : 1;
283 desc->unalign_reg_stride = chip->irq_unalign_reg_stride ? : 1;
284 desc->unalign_reg_idx = chip->irq_unalign_reg_stride ?
285 chip->irq_unalign_reg_idx : 0;
286 list_add_tail(&desc->node, &virq_desc_head);
287
288 /* Mask all register */
289 for (i = 0; i < chip->num_regs; i++) {
290 mask_reg = reg_base_get(desc, chip->mask_base, i);
291 ret = chip->write(dev, mask_reg, ~0U);
292 if (ret)
293 printf("%s: Set mask register 0x%x failed, ret=%d\n",
294 __func__, mask_reg, ret);
295 }
296
297 /* Clear all status */
298 for (i = 0; i < chip->num_regs; i++) {
299 status_reg = reg_base_get(desc, chip->status_base, i);
300 ret = chip->write(dev, status_reg, ~0U);
301 if (ret)
302 printf("%s: Clear status register 0x%x failed, ret=%d\n",
303 __func__, status_reg, ret);
304 }
305
306 /* Add parent irq into interrupt framework with generic virq handler */
307 irq_install_handler(irq, virq_chip_generic_handler, dev);
308
309 return irq_handler_disable(irq);
310
311 free1:
312 free(desc);
313 free2:
314 free(status_buf);
315
316 return ret;
317 }
318
virq_init(void)319 static int virq_init(void)
320 {
321 INIT_LIST_HEAD(&virq_desc_head);
322 return 0;
323 }
324
__virq_enable(int irq,int enable)325 static int __virq_enable(int irq, int enable)
326 {
327 struct virq_chip *chip;
328 struct virq_desc *desc;
329 uint mask_reg, mask_val;
330 uint reg_val;
331 int virq;
332 int ret;
333
334 desc = find_virq_desc(irq);
335 if (!desc) {
336 printf("%s: %s Invalid irq %d\n",
337 __func__, enable ? "Enable" : "Disable", irq);
338 return -ENOENT;
339 }
340
341 chip = desc->chip;
342 if (!chip)
343 return -ENOENT;
344
345 virq = irq - desc->irq_base;
346 mask_val = chip->irqs[virq].mask;
347 mask_reg = reg_base_get(desc, chip->mask_base,
348 chip->irqs[virq].reg_offset);
349 reg_val = chip->read(desc->parent, mask_reg);
350 if (enable)
351 reg_val &= ~mask_val;
352 else
353 reg_val |= mask_val;
354
355 ret = chip->write(desc->parent, mask_reg, reg_val);
356 if (ret) {
357 printf("%s: Clear status register 0x%x failed, ret=%d\n",
358 __func__, mask_reg, ret);
359 return ret;
360 }
361
362 if (enable)
363 desc->virqs[virq].flag |= IRQ_FLG_ENABLE;
364 else
365 desc->virqs[virq].flag &= ~IRQ_FLG_ENABLE;
366
367 return 0;
368 }
369
virq_enable(int irq)370 static int virq_enable(int irq)
371 {
372 struct virq_desc *desc = find_virq_desc(irq);
373 int ret;
374
375 if (bad_virq(irq))
376 return -EINVAL;
377
378 ret = __virq_enable(irq, 1);
379 if (!ret) {
380 if (desc->use_count == 0)
381 irq_handler_enable(desc->pirq);
382 desc->use_count++;
383 }
384
385 return ret;
386 }
387
virq_disable(int irq)388 static int virq_disable(int irq)
389 {
390 struct virq_desc *desc = find_virq_desc(irq);
391 int ret;
392
393 if (bad_virq(irq))
394 return -EINVAL;
395
396 ret = __virq_enable(irq, 0);
397 if (!ret) {
398 if (desc->use_count <= 0)
399 return ret;
400
401 if (desc->use_count == 1)
402 irq_handler_disable(desc->pirq);
403 desc->use_count--;
404 }
405
406 return ret;
407 }
408
409 struct irq_chip virq_generic_chip = {
410 .name = "virq-irq-chip",
411 .irq_init = virq_init,
412 .irq_enable = virq_enable,
413 .irq_disable = virq_disable,
414 };
415
arch_virq_get_irqchip(void)416 struct irq_chip *arch_virq_get_irqchip(void)
417 {
418 return &virq_generic_chip;
419 }
420