core: using native interrupt handlers
Adds a section in core architecture documentation on how to
implement and register interrupt controllers and handler functions.
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
Reviewed-by: Joakim Bech <joakim.bech@linaro.org>
Signed-off-by: Etienne Carriere <etienne.carriere@foss.st.com>
diff --git a/architecture/core.rst b/architecture/core.rst
index 94f353e..9ae8f49 100644
--- a/architecture/core.rst
+++ b/architecture/core.rst
@@ -390,6 +390,149 @@
several trusted threads in optee_os helps normal world scheduler to be
efficient.
+Core handlers for native interrupts
+===================================
+
+OP-TEE core provides methods for device drivers to setup and register
+handler functions for native interrupt controller drivers
+(see:ref:`native_foreign_irqs`).
+Interrupt handlers can be nested as when an interrupt controller
+exposes interrupts which signaling is multiplexed on an interrupt
+controlled by a parent interrupt controller.
+
+Interrupt controllers are represented by an instance of ``struct itr_chip``.
+An interrupt controller exposes a given number of interrupts, each identified
+by an index from 0 to N-1 where N is the total number of interrupts exposed
+by that controller. In the literature, an interrupt index identifier
+is called interrupt number.
+
+**Interrupt management API functions**
+
+Interrupt management resources are declared in header file interrupt.h_.
+Interrupt consumers main API functions are:
+
+ - ``interrupt_enable()`` and ``interrupt_disable()`` to respectively
+ enable or disable an interrupt.
+
+ - ``interrupt_mask()`` and ``interrupt_unmask()`` to respectively mask
+ or unmask an interrupt. Masking of an enabled interrupt temporarily
+ disables the interrupt while unmasking enables a previously masked
+ interrupt. ``interrupt_mask()`` and ``interrupt_unmask()`` are
+ allowed to be called from an interrupt context, but
+ ``interrupt_enable()`` and ``interrupt_disable()`` not so.
+
+ - ``interrupt_configure()`` to configure an interrupt detection mode
+ and priority.
+
+ - ``interrupt_add_handler()`` to configure an interrupt and register
+ an interrupt handler function, see below.
+
+ - ``interrupt_remove_handler()`` to unregister an interrupt handler
+ function from an interrupt.
+
+**Interrupt controller drivers**
+
+An interrupt controller instance, named chip (``struct itr_chip``) defines
+operation function handlers for management of the interrupt(s) it controls.
+An interrupt chip driver must provide operation handler functions ``.add``,
+``.mask``, ``.unmask``, ``.enable`` and ``.disable``. There are other
+operation handler functions that are optional, as for example ``.rasie_pi``,
+``.raise_sgi`` and ``.set_priority``.
+
+An interrupt chip driver registers the controller instance with API
+function ``itr_chip_init()``. The driver calls the registered interrupt
+consumer(s) handler(s) with API function ``interrupt_call_handlers()``.
+
+**CPU main interrupt controller driver**
+
+The CPU interrupt controller (e.g. a GIC instance on Arm architecture CPUs)
+is called the main interrupt controller. Its driver must register as main
+controller using API function ``interrupt_main_init()``. The function is
+in charge of calling ``itr_chip_init()`` for that chip instance.
+
+Interrupt consumer drivers can get a reference to the main interrupt
+controller with the API function ``interrupt_get_main_chip()``.
+
+**Interrupt handlers**
+
+Interrupt handler functions are callback functions registered by interrupt
+consumer drivers that core shall call when the related interrupt occurs.
+Structure ``struct itr_handler`` references a handler. It contains the
+handler function entry point, the interrupt number, the interrupt controller
+device and a few more parameters.
+
+An interrupt handler function return value is of type ``enum itr_return``.
+It shall return ``ITRR_HANDLED`` when the interrupt is served and
+``ITRR_NONE`` when the interrupt cannot be served.
+
+The interrupt handler runs in an interrupt context rather than a thread
+context. When this occurs, all other interrupts are masked, necessitating fast
+execution of the interrupt handler to avoid delaying or missing out on other
+interrupts. When an interrupt occurs that requires the completion of
+long-running operations, the interrupt handler should request the OP-TEE
+bottom half thread (see :ref:`notifications`) to execute those operations.
+
+API function ``interrupt_add_handler()``,
+``interrupt_add_handler_with_chip()`` and ``interrupt_alloc_add_handler()``
+configure and register a handler function to a given interrupt.
+
+API function ``interrupt_remove_handler()`` and
+``interrupt_remove_free_handler()`` unregister a registered handler.
+
+**Interrupt consumer driver**
+
+A typical implementation of a driver consuming an interrupt includes
+retrieving of the interrupt resource (interrupt controller and interrupt
+number in that controller), configuring the interrupt, registering a handler
+for the interrupt and enabling/disabling the interrupt.
+
+For example, the dummy driver below prints a debug trace when the related
+interrupt occurs:
+
+.. code-block:: c
+
+ static struct itr_handler *foo_int1_handler;
+
+ static struct foo_int1_data = {
+ /* field with some interrupt handler private data */
+ };
+
+ static enum itr_return foo_it_handler_fn(struct itr_handler *h)
+ {
+ foo_acknowledge_interrupt(h->it);.
+ DMSG("Interrupt FOO%u served", h->it);
+
+ return ITRR_HANDLED;
+ }
+
+ static TEE_Result foo_initialization(void)
+ {
+ TEE_Result res = TEE_ERROR_GENERIC;
+
+ res = interrupt_alloc_add_handler(itr_core_get(),
+ GIC_INT_FOO,
+ foo_it_handler_fn,
+ ITRF_TRIGGER_LEVEL,
+ &foo_int1_data,
+ &foo_int1_handler);
+ if (res)
+ return res;
+
+ interrupt_enable(itr_chip, it_num);
+
+ return TEE_SUCCESS;
+ }
+
+ static void foo_release(void)
+ {
+ if (foo_int1_handler) {
+ interrupt_disable(foo_int1_handler->chip,
+ foo_int1_handler->it);
+
+ interrupt_remove_free_handler(&foo_int1_handler);
+ }
+ }
+
----
.. _notifications:
@@ -1621,6 +1764,7 @@
.. _optee_smc.h: https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/include/sm/optee_smc.h
.. _thread.c: https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/kernel/thread.c
.. _thread.h: https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/include/kernel/thread.h
+.. _interrupt.h: https://github.com/OP-TEE/optee_os/blob/master/core/include/kernel/interrupt.h
.. _ARM_DEN0028A_SMC_Calling_Convention: http://infocenter.arm.com/help/topic/com.arm.doc.den0028b/ARM_DEN0028B_SMC_Calling_Convention.pdf
.. _Cortex-A53 TRM: http://infocenter.arm.com/help/topic/com.arm.doc.ddi0500j/DDI0500J_cortex_a53_trm.pdf