Skip to content

Commit

Permalink
core: interrupt: registering interrupt providers
Browse files Browse the repository at this point in the history
Adds interrupt chip framework API functions for an interrupt controller
to register as an interrupt provider in the driver probing sequence
based on device tree. This allows interrupt consumer to be deferred
when a dependent interrupt controller is not yet initialized.

Interrupt controllers register a driver in DT_DRIVER providers list
with: interrupt_register_provider().

Interrupt consumer can get their interrupt through DT data with
interrupt_dt_get(), interrupt_dt_get_by_index() or
interrupt_dt_get_by_name().

This change removes inclusion of interrupt.h from kernel/dt.h as it is
not needed and conflicts with inclusion of kernel/dt.h from
kernel/interrupt.h.

Acked-by: Jens Wiklander <[email protected]>
Acked-by: Jerome Forissier <[email protected]>
Signed-off-by: Etienne Carriere <[email protected]>
  • Loading branch information
etienne-lms committed Nov 8, 2023
1 parent 89854a9 commit 8155c2b
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 1 deletion.
1 change: 1 addition & 0 deletions core/arch/arm/kernel/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <keep.h>
#include <kernel/asan.h>
#include <kernel/boot.h>
#include <kernel/interrupt.h>
#include <kernel/linker.h>
#include <kernel/lockdep.h>
#include <kernel/misc.h>
Expand Down
1 change: 1 addition & 0 deletions core/drivers/atmel_piobu.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <kernel/boot.h>
#include <kernel/dt.h>
#include <kernel/dt_driver.h>
#include <kernel/interrupt.h>
#include <kernel/pm.h>
#include <libfdt.h>
#include <mm/core_memprot.h>
Expand Down
1 change: 1 addition & 0 deletions core/drivers/atmel_wdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <kernel/delay.h>
#include <kernel/dt.h>
#include <kernel/dt_driver.h>
#include <kernel/interrupt.h>
#include <kernel/pm.h>
#include <matrix.h>
#include <mm/core_mmu.h>
Expand Down
1 change: 0 additions & 1 deletion core/include/kernel/dt.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#define __KERNEL_DT_H

#include <compiler.h>
#include <kernel/interrupt.h>
#include <kernel/panic.h>
#include <scattered_array.h>
#include <stdint.h>
Expand Down
121 changes: 121 additions & 0 deletions core/include/kernel/interrupt.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define __KERNEL_INTERRUPT_H

#include <dt-bindings/interrupt-controller/irq.h>
#include <kernel/dt_driver.h>
#include <mm/core_memprot.h>
#include <sys/queue.h>
#include <tee_api_types.h>
Expand Down Expand Up @@ -70,6 +71,20 @@ struct itr_ops {
uint8_t cpu_mask);
};

/*
* struct itr_desc - Interrupt description
* @chip Interrupt controller reference
* @itr_num Interrupt number
*
* This struct is used for binding interrupt device data between
* drivers when using DT_DRIVERS means. See itr_dt_get_func type
* definition.
*/
struct itr_desc {
struct itr_chip *chip;
size_t itr_num;
};

/* Interrupt handler return value */
enum itr_return {
ITRR_NONE,
Expand Down Expand Up @@ -392,4 +407,110 @@ static inline TEE_Result interrupt_alloc_add_handler(struct itr_chip *chip,
* This function may panic on non-NULL invalid @hdl reference.
*/
void interrupt_remove_free_handler(struct itr_handler *hdl);

/*
* itr_dt_get_func - Typedef of function to get an interrupt in DT node
*
* @args Reference to phandle arguments
* @data Pointer to data given at interrupt_register_provider() call
* @itr_desc_p Pointer to the struct itr_desc to fill
* Return TEE_SUCCESS in case of success.
* Return TEE_ERROR_DEFER_DRIVER_INIT if controller is not initialized.
* Return another TEE_Result code otherwise.
*
* Upon success, the interrupt is configured and consumer can add a handler
* function to the interrupt. Yet, the interrupt is not enabled until consumer
* calls interrupt_enable().
*/
typedef TEE_Result (*itr_dt_get_func)(struct dt_pargs *args, void *data,
struct itr_desc *itr_desc_p);

#ifdef CFG_DT
/**
* interrupt_register_provider() - Register an interrupt provider
*
* @fdt Device tree to work on
* @node Node offset of the interrupt controller in the DT
* @dt_get_itr Callback to match the devicetree interrupt reference with
* @data Data which will be passed to the get_dt_its callback
*/
TEE_Result interrupt_register_provider(const void *fdt, int node,
itr_dt_get_func dt_get_itr, void *data);

/**
* interrupt_dt_get_by_index() - Get an interrupt from DT by interrupt index
*
* Interrupt index (@index) refers to the index of the target interrupt to be
* retrieved as DT binding property "interrupts" may define several
* interrupts.
*
* @fdt Device tree to work on
* @node Node offset of the subnode containing interrupt(s) references
* @index Index in "interrupts" or "extended-interrupts" property list
* @chip Output interrupt controller reference upon success
* @itr_num Output interrupt number upon success
*
* Return TEE_SUCCESS in case of success
* Return TEE_ERROR_DEFER_DRIVER_INIT if interrupt driver is not yet initialized
* Return TEE_ERROR_ITEM_NOT_FOUND if the DT does not reference target interrupt
* Return any other TEE_Result compliant code in case of error
*/
TEE_Result interrupt_dt_get_by_index(const void *fdt, int node,
unsigned int index, struct itr_chip **chip,
size_t *itr_num);

/**
* interrupt_dt_get_by_name() - Get an interrupt from DT by interrupt name
*
* @fdt Device tree to work on
* @node Node offset of the subnode containing interrupt(s) references
* @name Name identifier used in "interrupt-names" property
* @chip Output interrupt controller reference upon success
* @itr_num Output interrupt number upon success
*
* Return TEE_SUCCESS in case of success
* Return TEE_ERROR_DEFER_DRIVER_INIT if interrupt driver is not yet initialized
* Return TEE_ERROR_ITEM_NOT_FOUND if the DT does not reference target interrupt
* Return any other TEE_Result compliant code in case of error
*/
TEE_Result interrupt_dt_get_by_name(const void *fdt, int node, const char *name,
struct itr_chip **chip, size_t *itr_num);
#else
static inline TEE_Result interrupt_register_provider(const void *dt __unused,
int node __unused,
itr_dt_get_func f __unused,
void *data __unused)
{
return TEE_ERROR_NOT_IMPLEMENTED;
}

static inline TEE_Result interrupt_dt_get_by_index(const void *fdt __unused,
int node __unused,
unsigned int index __unused,
struct itr_chip **c __unused,
size_t *itr_num __unused)
{
return TEE_ERROR_NOT_IMPLEMENTED;
}

static inline TEE_Result interrupt_dt_get_by_name(const void *fdt __unused,
int node __unused,
const char *name __unused,
struct itr_chip **ch __unused,
size_t *itr_num __unused)
{
return TEE_ERROR_NOT_IMPLEMENTED;
}
#endif /*CFG_DT*/

/*
* Helper function for when caller retrieves the first interrupt defined
* in "interrupts" or "extended-interrupts" DT binding property list.
*/
static inline TEE_Result interrupt_dt_get(const void *fdt, int node,
struct itr_chip **chip,
size_t *itr_num)
{
return interrupt_dt_get_by_index(fdt, node, 0, chip, itr_num);
}
#endif /*__KERNEL_INTERRUPT_H*/
109 changes: 109 additions & 0 deletions core/kernel/interrupt.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,112 @@ void interrupt_remove_free_handler(struct itr_handler *hdl)
free(hdl);
}
}

#ifdef CFG_DT
TEE_Result interrupt_register_provider(const void *fdt, int node,
itr_dt_get_func dt_get_itr, void *data)
{
return dt_driver_register_provider(fdt, node,
(get_of_device_func)dt_get_itr,
data, DT_DRIVER_INTERRUPT);
}

/*
* Fills an itr_desc reference based on "interrupts" property bindings.
* May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found but
* not yet initialized.
*/
static TEE_Result get_legacy_interrupt_by_index(const void *fdt, int node,
unsigned int index,
struct itr_desc *itr_desc)
{
const uint32_t *prop = NULL;
uint32_t phandle = 0;
int pnode = 0;
int len = 0;

prop = fdt_getprop(fdt, node, "interrupts", &len);
if (!prop)
return TEE_ERROR_ITEM_NOT_FOUND;

/* Find "interrupt-parent" in node or its parents */
pnode = node;
prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len);

while (!prop) {
pnode = fdt_parent_offset(fdt, pnode);
if (pnode < 0)
break;

prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len);
if (!prop && len != -FDT_ERR_NOTFOUND)
break;
}
if (!prop) {
DMSG("No interrupt parent for node %s",
fdt_get_name(fdt, node, NULL));
return TEE_ERROR_GENERIC;
}

/* "interrupt-parent" provides interrupt controller phandle */
phandle = fdt32_to_cpu(prop[0]);

/* Get interrupt chip/number from phandle and "interrupts" property */
return dt_driver_device_from_node_idx_prop_phandle("interrupts", fdt,
node, index,
DT_DRIVER_INTERRUPT,
phandle,
itr_desc);
}

/*
* Fills an itr_desc based on "interrupts-extended" property bindings.
* May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found
* but not yet initialized.
*/
static TEE_Result get_extended_interrupt_by_index(const void *fdt, int node,
unsigned int index,
struct itr_desc *itr_desc)
{
return dt_driver_device_from_node_idx_prop("interrupts-extended",
fdt, node, index,
DT_DRIVER_INTERRUPT,
itr_desc);
}

TEE_Result interrupt_dt_get_by_index(const void *fdt, int node,
unsigned int index, struct itr_chip **chip,
size_t *itr_num)
{
TEE_Result res = TEE_ERROR_GENERIC;
struct itr_desc desc = { };

assert(chip && itr_num);

/* "interrupts-extended" takes precedence over "interrupts" */
if (fdt_getprop(fdt, node, "interrupts-extended", NULL))
res = get_extended_interrupt_by_index(fdt, node, index, &desc);
else
res = get_legacy_interrupt_by_index(fdt, node, index, &desc);

if (!res) {
assert(desc.chip);
*chip = desc.chip;
*itr_num = desc.itr_num;
}

return res;
}

TEE_Result interrupt_dt_get_by_name(const void *fdt, int node, const char *name,
struct itr_chip **chip, size_t *itr_num)
{
int idx = 0;

idx = fdt_stringlist_search(fdt, node, "interrupt-names", name);
if (idx < 0)
return TEE_ERROR_GENERIC;

return interrupt_dt_get_by_index(fdt, node, idx, chip, itr_num);
}
#endif /*CFG_DT*/

0 comments on commit 8155c2b

Please sign in to comment.