Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <sama5d2.h>
Expand Down
56 changes: 56 additions & 0 deletions core/drivers/gic.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <drivers/gic.h>
#include <keep.h>
#include <kernel/dt.h>
#include <kernel/dt_driver.h>
#include <kernel/interrupt.h>
#include <kernel/panic.h>
#include <mm/core_memprot.h>
Expand Down Expand Up @@ -585,3 +586,58 @@ static void gic_op_set_affinity(struct itr_chip *chip, size_t it,

gic_it_set_cpu_mask(gd, it, cpu_mask);
}

#ifdef CFG_DT
/* Callback for "interrupt-extended" GIC interrupts in consumer DT nodes */
static TEE_Result dt_get_gic_chip_cb(struct dt_pargs *arg, void *priv_data,
struct itr_desc **out_itr_desc)
{
int itr_num = DT_INFO_INVALID_INTERRUPT;
struct itr_chip *chip = priv_data;
struct itr_desc *desc = NULL;
uint32_t type = 0;
uint32_t prio = 0;

itr_num = gic_dt_get_irq(arg->args, arg->args_count, &type, &prio);
if (itr_num == DT_INFO_INVALID_INTERRUPT)
return TEE_ERROR_GENERIC;

/* Allocate returned itr_desc as required by itr_dt_get_func type */
desc = calloc(1, sizeof(*desc));
if (!desc)
return TEE_ERROR_OUT_OF_MEMORY;

gic_op_add(chip, itr_num, type, prio);

desc->chip = chip;
desc->itr_num = itr_num;

*out_itr_desc = desc;

return TEE_SUCCESS;
}

static TEE_Result gic_probe(const void *fdt, int offs, const void *cd __unused)
{
if (interrupt_register_provider(fdt, offs, dt_get_gic_chip_cb,
&gic_data.chip))
panic();

return TEE_SUCCESS;
}

static const struct dt_device_match gic_match_table[] = {
{ .compatible = "arm,cortex-a15-gic" },
{ .compatible = "arm,cortex-a7-gic" },
{ .compatible = "arm,cortex-a5-gic" },
{ .compatible = "arm,cortex-a9-gic" },
{ .compatible = "arm,gic-400" },
{ }
};

DEFINE_DT_DRIVER(gic_dt_driver) = {
.name = "gic",
.match_table = gic_match_table,
.probe = gic_probe,
};
#endif /*CFG_DT*/
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
124 changes: 124 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 @@ -343,4 +358,113 @@ TEE_Result interrupt_alloc_add_handler(struct itr_chip *chip, size_t it_num,
* 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
* @out_itr_desc Output pointer to an allocated struct itr_desc upon success
* 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, struct itr_desc must point to memory allocated with malloc()
* or like as it is freed prior returning to caller function.
*
* 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 **out_itr_desc);

#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);

/**
* dt_get_interrupt_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 dt_get_interrupt_by_index(const void *fdt, int node,
unsigned int index, struct itr_chip **chip,
size_t *itr_num);

/**
* dt_get_interrupt_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 dt_get_interrupt_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 dt_get_interrupt_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 dt_get_interrupt_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 dt_get_interrupt(const void *fdt, int node,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be under CFG_DT switch

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed, it calls an empty inline function so compiler already knows it's also empty.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok but feels strange to have a dt_* function not under CFG_DT switch

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 definition instead of 2 under alternate #if/#else cases.

struct itr_chip **chip,
size_t *itr_num)
{
return dt_get_interrupt_by_index(fdt, node, 0, chip, itr_num);
}
#endif /*__KERNEL_INTERRUPT_H*/
126 changes: 126 additions & 0 deletions core/kernel/interrupt.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,129 @@ 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);
}

/*
* Provides 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 **out_desc)
{
TEE_Result res = TEE_ERROR_GENERIC;
const uint32_t *prop = NULL;
uint32_t phandle = 0;
void *desc = NULL;
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)
Comment thread
etienne-lms marked this conversation as resolved.
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 */
res = dt_driver_device_from_node_idx_prop_phandle("interrupts", fdt,
node, index,
DT_DRIVER_INTERRUPT,
phandle, desc);
if (!res)
*out_desc = desc;

return res;
}

/*
* Provides 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.
* With this function, provider is expected to have allocated itr_desc
* with malloc() or like.
*/
static TEE_Result get_extended_interrupt_by_index(const void *fdt, int node,
unsigned int index,
struct itr_desc **out_desc)
{
TEE_Result res = TEE_ERROR_GENERIC;
void *desc = NULL;

res = dt_driver_device_from_node_idx_prop("interrupts-extended",
fdt, node, index,
DT_DRIVER_INTERRUPT, desc);
if (!res)
*out_desc = desc;

return res;
}

TEE_Result dt_get_interrupt_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 = NULL;

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);

assert((!res && desc) || (res && !desc));

if (!res) {
*chip = desc->chip;
*itr_num = desc->itr_num;

/* Balance malloc() or like from itr_dt_get_func callback */
free(desc);
}

return res;
}

TEE_Result dt_get_interrupt_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 dt_get_interrupt_by_index(fdt, node, idx, chip, itr_num);
}
#endif /*CFG_DT*/