1 /**
2 * \file
3 * Interrupt controller.
4 * \ingroup l4_api
5 */
6 /*
7 * (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
8 * Alexander Warg <warg@os.inf.tu-dresden.de>,
9 * Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
10 * economic rights: Technische Universität Dresden (Germany)
11 *
12 * This file is part of TUD:OS and distributed under the terms of the
13 * GNU General Public License 2.
14 * Please see the COPYING-GPL-2 file for details.
15 *
16 * As a special exception, you may use this file as part of a free software
17 * library without restriction. Specifically, if other files instantiate
18 * templates or use macros or inline functions from this file, or you compile
19 * this file and link it with other files to produce an executable, this
20 * file does not by itself cause the resulting executable to be covered by
21 * the GNU General Public License. This exception does not however
22 * invalidate any other reasons why the executable file might be covered by
23 * the GNU General Public License.
24 */
25 #pragma once
26
27 #include <l4/sys/kernel_object.h>
28 #include <l4/sys/ipc.h>
29
30 /**
31 * \defgroup l4_icu_api Interrupt controller
32 * \ingroup l4_kernel_object_api
33 *
34 * The C Icu interface.
35 *
36 * To setup an IRQ line the following steps are required:
37 * 1. #l4_icu_set_mode() (optional if IRQ has a default mode)
38 * 2. #l4_rcv_ep_bind_thread() to bind the IRQ capability to a thread
39 * 3. #l4_icu_bind()
40 * 4. #l4_icu_unmask() to receive the first IRQ
41 *
42 * \includefile{l4/sys/icu.h}
43 */
44
45
46 /**
47 * Flags for IRQ numbers used for the ICU.
48 * \ingroup l4_icu_api
49 */
50 enum L4_icu_flags
51 {
52 /**
53 * Flag to denote that the IRQ is actually an MSI.
54 * \hideinitializer
55 *
56 * This flag may be used for l4_icu_bind() and l4_icu_unbind() functions to
57 * denote that the IRQ number is meant to be an MSI.
58 */
59 L4_ICU_FLAG_MSI = 0x80000000,
60 };
61
62
63 /**
64 * Interrupt attributes.
65 * \ingroup l4_irq_api
66 */
67 enum L4_irq_mode
68 {
69 /** Flow types */
70 L4_IRQ_F_NONE = 0, /**< None */
71 L4_IRQ_F_LEVEL = 0x2, /**< Level triggered */
72 L4_IRQ_F_EDGE = 0x0, /**< Edge triggered */
73 L4_IRQ_F_POS = 0x0, /**< Positive trigger */
74 L4_IRQ_F_NEG = 0x4, /**< Negative trigger */
75 L4_IRQ_F_BOTH = 0x8, /**< Both edges trigger */
76 L4_IRQ_F_LEVEL_HIGH = 0x3, /**< Level high trigger */
77 L4_IRQ_F_LEVEL_LOW = 0x7, /**< Level low trigger */
78 L4_IRQ_F_POS_EDGE = 0x1, /**< Positive edge trigger */
79 L4_IRQ_F_NEG_EDGE = 0x5, /**< Negative edge trigger */
80 L4_IRQ_F_BOTH_EDGE = 0x9, /**< Both edges trigger */
81 L4_IRQ_F_MASK = 0xf, /**< Mask */
82
83 /** Wakeup source? */
84 L4_IRQ_F_SET_WAKEUP = 0x10, /**< Use irq as wakeup source */
85 L4_IRQ_F_CLEAR_WAKEUP = 0x20, /**< Do not use irq as wakeup source */
86 };
87
88
89 /**
90 * Opcodes to the ICU interface.
91 * \ingroup l4_protocol_ops
92 */
93 enum L4_icu_opcode
94 {
95 /**
96 * Bind opcode.
97 * \hideinitializer
98 * \see l4_icu_bind()
99 */
100 L4_ICU_OP_BIND = 0,
101
102 /**
103 * Unbind opcode.
104 * \hideinitializer
105 * \see l4_icu_unbind()
106 */
107 L4_ICU_OP_UNBIND = 1,
108
109 /**
110 * Info opcode.
111 * \hideinitializer
112 * \see l4_icu_info()
113 */
114 L4_ICU_OP_INFO = 2,
115
116 /**
117 * Msi-info opcode.
118 * \hideinitializer
119 * \see l4_icu_msi_info()
120 */
121 L4_ICU_OP_MSI_INFO = 3,
122
123 /**
124 * Unmask opcode.
125 * \hideinitializer
126 * \see l4_icu_unmask()
127 */
128 L4_ICU_OP_UNMASK = 4,
129
130 /**
131 * Mask opcode.
132 * \hideinitializer
133 * \see l4_icu_mask()
134 */
135 L4_ICU_OP_MASK = 5,
136
137 /**
138 * Set-mode opcode.
139 * \hideinitializer
140 * \see l4_icu_set_mode()
141 */
142 L4_ICU_OP_SET_MODE = 6,
143 };
144
145 enum L4_icu_ctl_op
146 {
147 L4_ICU_CTL_UNMASK = 0,
148 L4_ICU_CTL_MASK = 1
149 };
150
151
152 /**
153 * Info structure for an ICU.
154 * \ingroup l4_icu_api
155 *
156 * This structure contains information about the features of an ICU.
157 * \see l4_icu_info().
158 */
159 typedef struct l4_icu_info_t
160 {
161 /**
162 * Feature flags.
163 *
164 * If #L4_ICU_FLAG_MSI is set the ICU supports MSIs.
165 */
166 unsigned features;
167
168 /**
169 * The number of IRQ lines supported by the ICU,
170 */
171 unsigned nr_irqs;
172
173 /**
174 * The number of MSI vectors supported by the ICU,
175 */
176 unsigned nr_msis;
177 } l4_icu_info_t;
178
179 /** Info to use for a specific MSI */
180 typedef struct l4_icu_msi_info_t
181 {
182 /** Value to use as address when sending this MSI */
183 l4_uint64_t msi_addr;
184 /** Value to use as data written to msi_addr, when sending this MSI. */
185 l4_uint32_t msi_data;
186 } l4_icu_msi_info_t;
187
188 /**
189 * Bind an interrupt line of an interrupt controller to an interrupt object.
190 * \ingroup l4_icu_api
191 *
192 * \param icu ICU object to bind `irq` to.
193 * \param irqnum IRQ line at the ICU.
194 * \param irq IRQ object to bind to this ICU.
195 *
196 * \return Syscall return tag. The caller should check the return value using
197 * l4_error() to check for errors and to identify the correct method
198 * for unmasking the interrupt.
199 * Return values `< 0` indicate an error. A return value of `0` means a
200 * direct unmask via the IRQ object using l4_irq_unmask(). A return
201 * value of `1` means that the interrupt has to be unmasked via the ICU
202 * using l4_icu_unmask().
203 */
204 L4_INLINE l4_msgtag_t
205 l4_icu_bind(l4_cap_idx_t icu, unsigned irqnum, l4_cap_idx_t irq) L4_NOTHROW;
206
207 /**
208 * \ingroup l4_icu_api
209 * \copybrief L4::Icu::bind
210 * \param icu The ICU object to bind `irq` to.
211 * \copydetails L4::Icu::bind
212 */
213 L4_INLINE l4_msgtag_t
214 l4_icu_bind_u(l4_cap_idx_t icu, unsigned irqnum, l4_cap_idx_t irq,
215 l4_utcb_t *utcb) L4_NOTHROW;
216
217 /**
218 * Remove binding of an interrupt line from the interrupt controller object.
219 * \ingroup l4_icu_api
220 *
221 * \param icu The ICU object from where the binding shall be removed.
222 * \param irqnum IRQ line at the ICU.
223 * \param irq IRQ object to remove from the ICU.
224 *
225 * \return Syscall return tag
226 */
227 L4_INLINE l4_msgtag_t
228 l4_icu_unbind(l4_cap_idx_t icu, unsigned irqnum, l4_cap_idx_t irq) L4_NOTHROW;
229
230 /**
231 * \ingroup l4_icu_api
232 * \copybrief L4::Icu::unbind
233 * \param icu The ICU object from where the binding shall be removed.
234 * \copydetails L4::Icu::unbind
235 */
236 L4_INLINE l4_msgtag_t
237 l4_icu_unbind_u(l4_cap_idx_t icu, unsigned irqnum, l4_cap_idx_t irq,
238 l4_utcb_t *utcb) L4_NOTHROW;
239
240 /**
241 * Set interrupt mode.
242 * \ingroup l4_icu_api
243 *
244 * \param icu The ICU object.
245 * \param irqnum IRQ line at the ICU.
246 * \param mode Mode, see #L4_irq_mode.
247 *
248 * \return Syscall return tag
249 */
250 L4_INLINE l4_msgtag_t
251 l4_icu_set_mode(l4_cap_idx_t icu, unsigned irqnum, l4_umword_t mode) L4_NOTHROW;
252
253 /**
254 * \ingroup l4_icu_api
255 * \copybrief L4::Icu::set_mode
256 * \param icu The ICU object.
257 * \copydetails L4::Icu::set_mode
258 */
259 L4_INLINE l4_msgtag_t
260 l4_icu_set_mode_u(l4_cap_idx_t icu, unsigned irqnum, l4_umword_t mode,
261 l4_utcb_t *utcb) L4_NOTHROW;
262
263 /**
264 * \ingroup l4_icu_api
265 * \copybrief L4::Icu::info
266 * \param icu The ICU object from which information shall be retrieved.
267 * \param[out] info Info structure to be filled with information.
268 *
269 * \return Syscall return tag
270 */
271 L4_INLINE l4_msgtag_t
272 l4_icu_info(l4_cap_idx_t icu, l4_icu_info_t *info) L4_NOTHROW;
273
274 /**
275 * \ingroup l4_icu_api
276 * \copybrief L4::Icu::info
277 * \param icu The ICU object from which MSI information shall be retrieved.
278 * \copydetails L4::Icu::info
279 */
280 L4_INLINE l4_msgtag_t
281 l4_icu_info_u(l4_cap_idx_t icu, l4_icu_info_t *info,
282 l4_utcb_t *utcb) L4_NOTHROW;
283
284 /**
285 * \ingroup l4_icu_api
286 * \copybrief L4::Icu::msi_info
287 * \param icu The ICU object from which MSI information shall be retrieved.
288 * \copydetails L4::Icu::msi_info
289 */
290 L4_INLINE l4_msgtag_t
291 l4_icu_msi_info(l4_cap_idx_t icu, unsigned irqnum, l4_uint64_t source,
292 l4_icu_msi_info_t *msi_info) L4_NOTHROW;
293
294 /**
295 * \ingroup l4_icu_api
296 * \copybrief l4_icu_msi_info()
297 * \utcb{utcb}
298 * \copydetails l4_icu_msi_info()
299 */
300 L4_INLINE l4_msgtag_t
301 l4_icu_msi_info_u(l4_cap_idx_t icu, unsigned irqnum, l4_uint64_t source,
302 l4_icu_msi_info_t *msi_info, l4_utcb_t *utcb) L4_NOTHROW;
303
304
305 /**
306 * Unmask an IRQ line.
307 * \ingroup l4_icu_api
308 *
309 * \param icu The ICU object where the IRQ line shall be unmasked.
310 * \param irqnum IRQ line at the ICU.
311 * \param label If non-NULL the function also waits for the next message.
312 * \param to Timeout for message to ICU, if unsure use L4_IPC_NEVER.
313 *
314 * \return Syscall return tag, the error values therein are undefined because
315 * l4_icu_unmask() is a sender-only IPC.
316 */
317 L4_INLINE l4_msgtag_t
318 l4_icu_unmask(l4_cap_idx_t icu, unsigned irqnum, l4_umword_t *label,
319 l4_timeout_t to) L4_NOTHROW;
320
321 /**
322 * \ingroup l4_icu_api
323 * \copybrief L4::Icu::unmask
324 * \param icu The ICU object where the IRQ line shall be unmasked.
325 * \copydetails L4::Icu::unmask
326 */
327 L4_INLINE l4_msgtag_t
328 l4_icu_unmask_u(l4_cap_idx_t icu, unsigned irqnum, l4_umword_t *label,
329 l4_timeout_t to, l4_utcb_t *utcb) L4_NOTHROW;
330
331 /**
332 * Mask an IRQ line.
333 * \ingroup l4_icu_api
334 *
335 * \param icu The ICU object where the IRQ line 'irqnum' shall be masked.
336 * \param irqnum IRQ line at the ICU.
337 * \param label If non-NULL the function also waits for the next message.
338 * \param to Timeout for message to ICU, if unsure use L4_IPC_NEVER.
339 *
340 * \return Syscall return tag
341 */
342 L4_INLINE l4_msgtag_t
343 l4_icu_mask(l4_cap_idx_t icu, unsigned irqnum, l4_umword_t *label,
344 l4_timeout_t to) L4_NOTHROW;
345
346 /**
347 * \ingroup l4_icu_api
348 * \copybrief L4::Icu::mask
349 * \param icu The ICU object where the IRQ line 'irqnum' shall be masked.
350 * \copydetails L4::Icu::mask
351 */
352 L4_INLINE l4_msgtag_t
353 l4_icu_mask_u(l4_cap_idx_t icu, unsigned irqnum, l4_umword_t *label,
354 l4_timeout_t to, l4_utcb_t *utcb) L4_NOTHROW;
355
356 /**
357 * \internal
358 */
359 L4_INLINE l4_msgtag_t
360 l4_icu_control_u(l4_cap_idx_t icu, unsigned irqnum, unsigned op,
361 l4_umword_t *label,l4_timeout_t to,
362 l4_utcb_t *utcb) L4_NOTHROW;
363
364
365 /**************************************************************************
366 * Implementations
367 */
368
369 L4_INLINE l4_msgtag_t
l4_icu_bind_u(l4_cap_idx_t icu,unsigned irqnum,l4_cap_idx_t irq,l4_utcb_t * utcb)370 l4_icu_bind_u(l4_cap_idx_t icu, unsigned irqnum, l4_cap_idx_t irq,
371 l4_utcb_t *utcb) L4_NOTHROW
372 {
373 l4_msg_regs_t *m = l4_utcb_mr_u(utcb);
374 m->mr[0] = L4_ICU_OP_BIND;
375 m->mr[1] = irqnum;
376 m->mr[2] = l4_map_obj_control(0, 0);
377 m->mr[3] = l4_obj_fpage(irq, 0, L4_CAP_FPAGE_RWS).raw;
378 return l4_ipc_call(icu, utcb, l4_msgtag(L4_PROTO_IRQ, 2, 1, 0), L4_IPC_NEVER);
379 }
380
381 L4_INLINE l4_msgtag_t
l4_icu_unbind_u(l4_cap_idx_t icu,unsigned irqnum,l4_cap_idx_t irq,l4_utcb_t * utcb)382 l4_icu_unbind_u(l4_cap_idx_t icu, unsigned irqnum, l4_cap_idx_t irq,
383 l4_utcb_t *utcb) L4_NOTHROW
384 {
385 l4_msg_regs_t *m = l4_utcb_mr_u(utcb);
386 m->mr[0] = L4_ICU_OP_UNBIND;
387 m->mr[1] = irqnum;
388 m->mr[2] = l4_map_obj_control(0, 0);
389 m->mr[3] = l4_obj_fpage(irq, 0, L4_CAP_FPAGE_RWS).raw;
390 return l4_ipc_call(icu, utcb, l4_msgtag(L4_PROTO_IRQ, 2, 1, 0), L4_IPC_NEVER);
391 }
392
393 L4_INLINE l4_msgtag_t
l4_icu_info_u(l4_cap_idx_t icu,l4_icu_info_t * info,l4_utcb_t * utcb)394 l4_icu_info_u(l4_cap_idx_t icu, l4_icu_info_t *info,
395 l4_utcb_t *utcb) L4_NOTHROW
396 {
397 l4_msgtag_t res;
398 l4_msg_regs_t *m = l4_utcb_mr_u(utcb);
399 m->mr[0] = L4_ICU_OP_INFO;
400 res = l4_ipc_call(icu, utcb, l4_msgtag(L4_PROTO_IRQ, 1, 0, 0), L4_IPC_NEVER);
401 info->features = m->mr[0];
402 info->nr_irqs = m->mr[1];
403 info->nr_msis = m->mr[2];
404 return res;
405 }
406
407 L4_INLINE l4_msgtag_t
l4_icu_msi_info_u(l4_cap_idx_t icu,unsigned irqnum,l4_uint64_t source,l4_icu_msi_info_t * msi_info,l4_utcb_t * utcb)408 l4_icu_msi_info_u(l4_cap_idx_t icu, unsigned irqnum, l4_uint64_t source,
409 l4_icu_msi_info_t *msi_info, l4_utcb_t *utcb) L4_NOTHROW
410 {
411 l4_msgtag_t res;
412 l4_msg_regs_t *m = l4_utcb_mr_u(utcb);
413 m->mr[0] = L4_ICU_OP_MSI_INFO;
414 m->mr[1] = irqnum;
415 m->mr64[l4_utcb_mr64_idx(2)] = source;
416 res = l4_ipc_call(icu, utcb, l4_msgtag(L4_PROTO_IRQ,
417 2 + 1 * sizeof(l4_uint64_t)
418 / sizeof(l4_umword_t),
419 0, 0), L4_IPC_NEVER);
420 if (L4_UNLIKELY(l4_msgtag_has_error(res)))
421 return res;
422
423 if (L4_UNLIKELY(l4_msgtag_words(res) * sizeof(l4_umword_t) < sizeof(*msi_info)))
424 return res;
425
426 __builtin_memcpy(msi_info, &m->mr[0], sizeof(*msi_info));
427 return res;
428 }
429
430 L4_INLINE l4_msgtag_t
l4_icu_set_mode_u(l4_cap_idx_t icu,unsigned irqnum,l4_umword_t mode,l4_utcb_t * utcb)431 l4_icu_set_mode_u(l4_cap_idx_t icu, unsigned irqnum, l4_umword_t mode,
432 l4_utcb_t *utcb) L4_NOTHROW
433 {
434 l4_msg_regs_t *mr = l4_utcb_mr_u(utcb);
435 mr->mr[0] = L4_ICU_OP_SET_MODE;
436 mr->mr[1] = irqnum;
437 mr->mr[2] = mode;
438 return l4_ipc_call(icu, utcb, l4_msgtag(L4_PROTO_IRQ, 3, 0, 0), L4_IPC_NEVER);
439 }
440
441 L4_INLINE l4_msgtag_t
l4_icu_control_u(l4_cap_idx_t icu,unsigned irqnum,unsigned op,l4_umword_t * label,l4_timeout_t to,l4_utcb_t * utcb)442 l4_icu_control_u(l4_cap_idx_t icu, unsigned irqnum, unsigned op,
443 l4_umword_t *label, l4_timeout_t to,
444 l4_utcb_t *utcb) L4_NOTHROW
445 {
446 l4_msg_regs_t *m = l4_utcb_mr_u(utcb);
447 m->mr[0] = L4_ICU_OP_UNMASK + op;
448 m->mr[1] = irqnum;
449 if (label)
450 return l4_ipc_send_and_wait(icu, utcb, l4_msgtag(L4_PROTO_IRQ, 2, 0, 0),
451 label, to);
452 else
453 return l4_ipc_send(icu, utcb, l4_msgtag(L4_PROTO_IRQ, 2, 0, 0), to);
454 }
455
456 L4_INLINE l4_msgtag_t
l4_icu_mask_u(l4_cap_idx_t icu,unsigned irqnum,l4_umword_t * label,l4_timeout_t to,l4_utcb_t * utcb)457 l4_icu_mask_u(l4_cap_idx_t icu, unsigned irqnum, l4_umword_t *label,
458 l4_timeout_t to, l4_utcb_t *utcb) L4_NOTHROW
459 { return l4_icu_control_u(icu, irqnum, L4_ICU_CTL_MASK, label, to, utcb); }
460
461 L4_INLINE l4_msgtag_t
l4_icu_unmask_u(l4_cap_idx_t icu,unsigned irqnum,l4_umword_t * label,l4_timeout_t to,l4_utcb_t * utcb)462 l4_icu_unmask_u(l4_cap_idx_t icu, unsigned irqnum, l4_umword_t *label,
463 l4_timeout_t to, l4_utcb_t *utcb) L4_NOTHROW
464 { return l4_icu_control_u(icu, irqnum, L4_ICU_CTL_UNMASK, label, to, utcb); }
465
466
467
468
469 L4_INLINE l4_msgtag_t
l4_icu_bind(l4_cap_idx_t icu,unsigned irqnum,l4_cap_idx_t irq)470 l4_icu_bind(l4_cap_idx_t icu, unsigned irqnum, l4_cap_idx_t irq) L4_NOTHROW
471 { return l4_icu_bind_u(icu, irqnum, irq, l4_utcb()); }
472
473 L4_INLINE l4_msgtag_t
l4_icu_unbind(l4_cap_idx_t icu,unsigned irqnum,l4_cap_idx_t irq)474 l4_icu_unbind(l4_cap_idx_t icu, unsigned irqnum, l4_cap_idx_t irq) L4_NOTHROW
475 { return l4_icu_unbind_u(icu, irqnum, irq, l4_utcb()); }
476
477 L4_INLINE l4_msgtag_t
l4_icu_info(l4_cap_idx_t icu,l4_icu_info_t * info)478 l4_icu_info(l4_cap_idx_t icu, l4_icu_info_t *info) L4_NOTHROW
479 { return l4_icu_info_u(icu, info, l4_utcb()); }
480
481 L4_INLINE l4_msgtag_t
l4_icu_msi_info(l4_cap_idx_t icu,unsigned irqnum,l4_uint64_t source,l4_icu_msi_info_t * msi_info)482 l4_icu_msi_info(l4_cap_idx_t icu, unsigned irqnum, l4_uint64_t source,
483 l4_icu_msi_info_t *msi_info) L4_NOTHROW
484 { return l4_icu_msi_info_u(icu, irqnum, source, msi_info, l4_utcb()); }
485
486 L4_INLINE l4_msgtag_t
l4_icu_unmask(l4_cap_idx_t icu,unsigned irqnum,l4_umword_t * label,l4_timeout_t to)487 l4_icu_unmask(l4_cap_idx_t icu, unsigned irqnum, l4_umword_t *label,
488 l4_timeout_t to) L4_NOTHROW
489 { return l4_icu_control_u(icu, irqnum, L4_ICU_CTL_UNMASK, label, to, l4_utcb()); }
490
491 L4_INLINE l4_msgtag_t
l4_icu_mask(l4_cap_idx_t icu,unsigned irqnum,l4_umword_t * label,l4_timeout_t to)492 l4_icu_mask(l4_cap_idx_t icu, unsigned irqnum, l4_umword_t *label,
493 l4_timeout_t to) L4_NOTHROW
494 { return l4_icu_control_u(icu, irqnum, L4_ICU_CTL_MASK, label, to, l4_utcb()); }
495
496 L4_INLINE l4_msgtag_t
l4_icu_set_mode(l4_cap_idx_t icu,unsigned irqnum,l4_umword_t mode)497 l4_icu_set_mode(l4_cap_idx_t icu, unsigned irqnum, l4_umword_t mode) L4_NOTHROW
498 {
499 return l4_icu_set_mode_u(icu, irqnum, mode, l4_utcb());
500 }
501