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