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