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