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