rework external irq system, introduce hierarchical irq
[lunaix-os.git] / lunaix-os / hal / irq.c
1 #include <hal/irq.h>
2 #include <lunaix/mm/valloc.h>
3 #include <lunaix/owloysius.h>
4
5 static struct irq_domain* default_domain = NULL;
6 static DEFINE_LLIST(irq_domains);
7
8 static void
9 __default_servant(irq_t irq, const struct hart_state* state)
10 {
11     return;
12 }
13
14 struct irq_domain*
15 irq_create_domain(struct device* intc_dev, const struct irq_domain_ops* ops)
16 {
17     struct irq_domain* domain;
18
19     assert(ops->install_irq);
20
21     domain = new_potens(potens(INT_DOMAIN), struct irq_domain);
22     device_grant_potens(intc_dev, potens_meta(domain));
23
24     btrie_init(&domain->irq_map, ilog2(8));
25     llist_append(&irq_domains, &domain->list);
26     domain->ops = ops;
27     
28     return domain;
29 }
30
31 struct irq_domain*
32 irq_owning_domain(struct device* dev)
33 {
34 #ifdef CONFIG_USE_DEVICETREE
35     struct device* intc;
36     struct dtn* intr_parent;
37     struct potens_meta* domain_m;
38
39     intr_parent = dev->devtree_node->intr.parent;
40     intc = resolve_device_morph(dt_mobj(intr_parent));
41
42     if (!intc) {
43         return NULL;
44     }
45
46     domain_m = device_get_potens(intc, potens(INT_DOMAIN));
47     return !domain_m ?: get_potens(domain_m, struct irq_domain);
48 #else
49     return default_domain;
50 #endif
51 }
52
53 int
54 irq_attach_domain(struct irq_domain* parent, struct irq_domain* child)
55 {
56 #ifndef CONFIG_USE_DEVICETREE
57     parent = parent ?: default_domain;
58     child->parent = parent;
59 #endif
60     return 0;
61 }
62
63 static void
64 __irq_create_line(irq_t irq, ptr_t local_int)
65 {
66     struct irq_line_wire *line;
67
68     line = valloc(sizeof(*line));
69     line->domain_local = local_int;
70     line->domain_mapped = local_int;
71
72     irq->line = line;
73 }
74
75 static void
76 __irq_create_msi(irq_t irq, ptr_t message)
77 {
78     struct irq_msi_wire *msi;
79
80     msi = valloc(sizeof(*msi));
81     msi->message  = message;
82
83     irq->msi = msi;
84 }
85
86 irq_t
87 irq_declare(enum irq_type type, irq_servant callback, 
88             ptr_t data, void* irq_extra)
89 {
90     irq_t irq;
91
92     irq  = valloc(sizeof(*irq));
93     *irq = (struct irq_object) {
94         .type = type,
95         .serve = callback ?: __default_servant,
96         .irq_extra = irq_extra
97     };
98
99     if (type == IRQ_LINE) {
100         __irq_create_line(irq, data);
101     }
102
103     else if (type == IRQ_MESSAGE) {
104         __irq_create_msi(irq, data);
105     }
106
107     return irq;
108 }
109
110 void
111 irq_revoke(irq_t irq)
112 {
113     if (--(irq->ref) > 0) {
114         return;
115     }
116     vfree(irq);
117 }
118
119 int
120 irq_assign(struct irq_domain* domain, irq_t irq)
121 {
122     int err = 0;    
123     if (domain->ops->map_irq) {
124         err = domain->ops->map_irq(domain, irq, irq->irq_extra);
125         if (err) {
126             return err;
127         }
128     }
129
130     /*
131         A domain controller may choose to forward the interrupt
132         (i.e., irq became transparent and belongs to the higher domain)
133         We allow controller decide whether to record the irq under its wing
134     */
135     err = domain->ops->install_irq(domain, irq);
136     if (err) {
137         return err;
138     }
139
140     irq->domain = domain;
141     return 0;
142 }
143
144 irq_t
145 irq_find(struct irq_domain* domain, int local_irq)
146 {
147     irq_t irq = NULL;
148     struct irq_domain* current;
149
150     // Find recursively, in case of irq forwarding
151
152     current = domain ?: default_domain;
153     while (current && !irq) {
154         irq = (irq_t)btrie_get(&current->irq_map, local_irq);
155         current = irq_parent_domain(current);
156     }
157     
158     return irq;
159 }
160
161 void
162 irq_record(struct irq_domain* domain, irq_t irq)
163 {
164     irq->ref++;
165     btrie_set(&domain->irq_map, irq->vector, irq);
166 }
167
168 void
169 irq_set_default_domain(struct irq_domain* domain)
170 {
171     assert(!default_domain);
172     default_domain = domain;
173 }
174
175 int
176 irq_forward_install(struct irq_domain* current, irq_t irq)
177 {
178     struct irq_domain* parent;
179
180     parent = irq_parent_domain(current);
181
182     if (irq->type == IRQ_LINE) {
183         irq->line->domain_local = irq->line->domain_mapped;
184     }
185
186     irq->domain = parent;
187     return parent->ops->install_irq(parent, irq);
188 }
189
190 struct irq_domain*
191 irq_get_default_domain()
192 {
193     assert(default_domain);
194     return default_domain;
195 }