Unifying External Interrupt System (#51)
[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, ptr_t data)
88 {
89     irq_t irq;
90
91     irq  = valloc(sizeof(*irq));
92     *irq = (struct irq_object) {
93         .type = type,
94         .serve = callback ?: __default_servant,
95         .vector = IRQ_VECTOR_UNSET
96     };
97
98     if (type == IRQ_LINE) {
99         __irq_create_line(irq, data);
100     }
101
102     else if (type == IRQ_MESSAGE) {
103         __irq_create_msi(irq, data);
104     }
105
106     return irq;
107 }
108
109 void
110 irq_revoke(irq_t irq)
111 {
112     if (--(irq->ref) > 0) {
113         return;
114     }
115     vfree(irq);
116 }
117
118 int
119 irq_assign(struct irq_domain* domain, irq_t irq, void* irq_spec)
120 {
121     int err = 0;    
122     if (domain->ops->map_irq) {
123         err = domain->ops->map_irq(domain, irq, irq_spec);
124         if (err) {
125             return err;
126         }
127     }
128
129     /*
130         A domain controller may choose to forward the interrupt
131         (i.e., irq became transparent and belongs to the higher domain)
132         We allow controller decide whether to record the irq under its wing
133     */
134     err = domain->ops->install_irq(domain, irq);
135     if (err) {
136         return err;
137     }
138
139     irq->domain = domain;
140     return 0;
141 }
142
143 irq_t
144 irq_find(struct irq_domain* domain, int local_irq)
145 {
146     irq_t irq = NULL;
147     struct irq_domain* current;
148
149     // Find recursively, in case of irq forwarding
150
151     current = domain ?: default_domain;
152     while (current && !irq) {
153         irq = (irq_t)btrie_get(&current->irq_map, local_irq);
154         current = irq_parent_domain(current);
155     }
156     
157     return irq;
158 }
159
160 void
161 irq_record(struct irq_domain* domain, irq_t irq)
162 {
163     irq->ref++;
164     btrie_set(&domain->irq_map, irq->vector, irq);
165 }
166
167 void
168 irq_set_default_domain(struct irq_domain* domain)
169 {
170     assert(!default_domain);
171     default_domain = domain;
172 }
173
174 int
175 irq_forward_install(struct irq_domain* current, irq_t irq)
176 {
177     struct irq_domain* parent;
178
179     parent = irq_parent_domain(current);
180
181     if (irq->type == IRQ_LINE) {
182         irq->line->domain_local = irq->line->domain_mapped;
183     }
184
185     irq->domain = parent;
186     return parent->ops->install_irq(parent, irq);
187 }
188
189 struct irq_domain*
190 irq_get_default_domain()
191 {
192     assert(default_domain);
193     return default_domain;
194 }