xref: /OK3568_Linux_fs/u-boot/drivers/irq/virq.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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