1 /*
2  * Copyright (c) 2019, Xilinx Inc. and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <metal/errno.h>
8 #include <metal/irq.h>
9 #include <metal/irq_controller.h>
10 #include <metal/list.h>
11 #include <metal/utilities.h>
12 
13 /** List of registered IRQ controller */
14 static METAL_DECLARE_LIST(irq_cntrs);
15 
metal_irq_allocate(int irq_base,int irq_num)16 static int metal_irq_allocate(int irq_base, int irq_num)
17 {
18 	struct metal_list *node;
19 	struct metal_irq_controller *cntr;
20 	int irq_tocheck = irq_base, irq_end_tocheck;
21 
22 	if (irq_num == 0) {
23 		return METAL_IRQ_ANY;
24 	}
25 	if (irq_tocheck == METAL_IRQ_ANY) {
26 		irq_tocheck = 0;
27 	}
28 	irq_end_tocheck = irq_tocheck + irq_num;
29 
30 	metal_list_for_each(&irq_cntrs, node) {
31 		int cntr_irq_base, cntr_irq_end;
32 
33 		cntr = metal_container_of(node,
34 					  struct metal_irq_controller, node);
35 		cntr_irq_base = cntr->irq_base;
36 		cntr_irq_end = cntr_irq_base + cntr->irq_num;
37 		if (irq_tocheck < cntr_irq_end &&
38 		    irq_end_tocheck > cntr_irq_base) {
39 			if (irq_base != METAL_IRQ_ANY) {
40 				/* IRQ has been allocated */
41 				return METAL_IRQ_ANY;
42 			}
43 			irq_tocheck = cntr_irq_end;
44 			irq_end_tocheck = irq_tocheck + irq_num;
45 		}
46 	}
47 	return irq_tocheck;
48 }
49 
metal_irq_register_controller(struct metal_irq_controller * cntr)50 int metal_irq_register_controller(struct metal_irq_controller *cntr)
51 {
52 	int irq_base;
53 	struct metal_list *node;
54 
55 	if (cntr == NULL) {
56 		return -EINVAL;
57 	}
58 	metal_list_for_each(&irq_cntrs, node) {
59 		if (node == &cntr->node) {
60 			return 0;
61 		}
62 	}
63 
64 	/*
65 	 * Allocate IRQ numbers which are not yet used by any IRQ
66 	 * controllers.
67 	 */
68 	irq_base = metal_irq_allocate(cntr->irq_base, cntr->irq_num);
69 	if (irq_base == METAL_IRQ_ANY) {
70 		return -EINVAL;
71 	}
72 	cntr->irq_base = irq_base;
73 
74 	metal_list_add_tail(&irq_cntrs, &cntr->node);
75 	return 0;
76 }
77 
metal_irq_get_controller(int irq)78 static struct metal_irq_controller *metal_irq_get_controller(int irq)
79 {
80 	struct metal_list *node;
81 	struct metal_irq_controller *cntr;
82 
83 	metal_list_for_each(&irq_cntrs, node) {
84 		int irq_base, irq_end;
85 
86 		cntr = (struct metal_irq_controller *)
87 		       metal_container_of(node, struct metal_irq_controller,
88 					  node);
89 		irq_base = cntr->irq_base;
90 		irq_end = irq_base + cntr->irq_num;
91 		if (irq >= irq_base && irq < irq_end) {
92 			return cntr;
93 		}
94 	}
95 	return NULL;
96 }
97 
_metal_irq_set_enable(int irq,unsigned int state)98 static void _metal_irq_set_enable(int irq, unsigned int state)
99 {
100 	struct metal_irq_controller *cntr;
101 
102 	cntr = metal_irq_get_controller(irq);
103 	if (cntr == NULL) {
104 		return;
105 	}
106 	cntr->irq_set_enable(cntr, irq, state);
107 }
108 
metal_irq_register(int irq,metal_irq_handler irq_handler,void * arg)109 int metal_irq_register(int irq,
110 		       metal_irq_handler irq_handler,
111 		       void *arg)
112 {
113 	struct metal_irq_controller *cntr;
114 	struct metal_irq *irq_data;
115 
116 	cntr = metal_irq_get_controller(irq);
117 	if (cntr == NULL) {
118 		return -EINVAL;
119 	}
120 	if (cntr->irq_register != NULL) {
121 		return cntr->irq_register(cntr, irq, irq_handler, arg);
122 	}
123 	if (cntr->irqs == NULL) {
124 		return -EINVAL;
125 	}
126 	irq_data = &cntr->irqs[irq - cntr->irq_base];
127 	irq_data->hd = irq_handler;
128 	irq_data->arg = arg;
129 	return 0;
130 }
131 
metal_irq_enable(unsigned int vector)132 void metal_irq_enable(unsigned int vector)
133 {
134 	_metal_irq_set_enable((int)vector, METAL_IRQ_ENABLE);
135 }
136 
metal_irq_disable(unsigned int vector)137 void metal_irq_disable(unsigned int vector)
138 {
139 	_metal_irq_set_enable((int)vector, METAL_IRQ_DISABLE);
140 }
141