Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core interrupt: use device tree data #6361

Merged
merged 6 commits into from
Nov 22, 2023
Merged
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 <mm/core_mmu.h>
Expand Down
67 changes: 65 additions & 2 deletions core/drivers/gic.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@

#include <arm.h>
#include <assert.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <config.h>
#include <compiler.h>
#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 @@ -207,10 +209,10 @@ static int gic_dt_get_irq(const uint32_t *properties, int count, uint32_t *type,
it_num = fdt32_to_cpu(properties[1]);

switch (fdt32_to_cpu(properties[0])) {
case 1:
case GIC_PPI:
it_num += 16;
break;
case 0:
case GIC_SPI:
it_num += 32;
break;
default:
Expand Down Expand Up @@ -656,3 +658,64 @@ 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 "interrupts" and "interrupts-extended" DT node properties */
static TEE_Result dt_get_gic_chip_cb(struct dt_pargs *arg, void *priv_data,
struct itr_desc *itr_desc)
{
int itr_num = DT_INFO_INVALID_INTERRUPT;
struct itr_chip *chip = priv_data;
uint32_t phandle_args[2] = { };
uint32_t type = 0;
uint32_t prio = 0;

assert(arg && itr_desc);

/*
* gic_dt_get_irq() expects phandle arguments passed are still in DT
* format (big-endian) whereas struct dt_pargs carries converted
* formats. Therefore swap again phandle arguments. gic_dt_get_irq()
* consumes only the 2 first arguments.
*/
if (arg->args_count < 2)
return TEE_ERROR_GENERIC;
phandle_args[0] = cpu_to_fdt32(arg->args[0]);
phandle_args[1] = cpu_to_fdt32(arg->args[1]);

itr_num = gic_dt_get_irq((const void *)phandle_args, 2, &type, &prio);
if (itr_num == DT_INFO_INVALID_INTERRUPT)
return TEE_ERROR_GENERIC;

gic_op_add(chip, itr_num, type, prio);

itr_desc->chip = chip;
itr_desc->itr_num = itr_num;

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
14 changes: 7 additions & 7 deletions core/include/kernel/dt_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ struct dt_pargs {
*
* @parg: phandle argument(s) referencing the device in the FDT.
* @data: driver private data registered in struct dt_driver.
* @out_device: output device reference upon success, e.g. a struct clk
* @device_ref: output device reference upon success, e.g. a struct clk
* pointer for a clock driver.
*
* Return code:
Expand All @@ -111,7 +111,7 @@ struct dt_pargs {
* Any TEE_Result compliant code in case of error.
*/
typedef TEE_Result (*get_of_device_func)(struct dt_pargs *parg, void *data,
void **out_device);
void *device_ref);

/**
* dt_driver_register_provider - Register a driver provider
Expand Down Expand Up @@ -139,7 +139,7 @@ TEE_Result dt_driver_register_provider(const void *fdt, int nodeoffset,
* @nodeoffset: node offset in the FDT
* @prop_idx: index of the phandle data in the property
* @type: Driver type
* @out_device: output device opaque reference upon support, for example
* @device_ref: output device opaque reference upon support, for example
* a struct clk pointer for a clock driver.

* Return code:
Expand All @@ -153,7 +153,7 @@ TEE_Result dt_driver_device_from_node_idx_prop(const char *prop_name,
const void *fdt, int nodeoffset,
unsigned int prop_idx,
enum dt_driver_type type,
void **out_device);
void *device_ref);

/*
* dt_driver_device_from_parent - Return a device instance based on the parent.
Expand All @@ -163,7 +163,7 @@ TEE_Result dt_driver_device_from_node_idx_prop(const char *prop_name,
* @fdt: FDT base address
* @nodeoffset: node offset in the FDT
* @type: Driver type
* @out_device: output device opaque reference upon success, for example
* @device_ref: output device opaque reference upon success, for example
* a struct i2c_dev pointer for a I2C bus driver
*
* Return code:
Expand All @@ -173,7 +173,7 @@ TEE_Result dt_driver_device_from_node_idx_prop(const char *prop_name,
*/
TEE_Result dt_driver_device_from_parent(const void *fdt, int nodeoffset,
enum dt_driver_type type,
void **out_device);
void *device_ref);

/*
* dt_driver_device_from_node_idx_prop_phandle() - Same as
Expand All @@ -190,7 +190,7 @@ TEE_Result dt_driver_device_from_node_idx_prop_phandle(const char *prop_name,
unsigned int prop_index,
enum dt_driver_type type,
uint32_t phandle,
void **out_device);
void *device_ref);

/*
* dt_driver_get_crypto() - Request crypto support for driver initialization
Expand Down
140 changes: 140 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 @@ -75,6 +76,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 @@ -330,6 +345,25 @@ static inline TEE_Result interrupt_add_handler(struct itr_handler *hdl)
return interrupt_add_configure_handler(hdl, IRQ_TYPE_NONE, 0);
}

/*
* interrupt_create_handler() - Allocate/register an interrupt callback handler
* @itr_chip Interrupt chip obtained from dt_get_interrupt_by_*()
* @itr_num Interrupt number obtained from dt_get_interrupt_by_*()
* @callback Callback handler function
* @priv Private dat to pssa to @callback
* @flags INTERRUPT_FLAGS_* or 0
* @out_hdl Output allocated and registered handler or NULL
*
* This function differs from interrupt_add_handler() in that the
* interrupt is not reconfigured. interrupt_create_handler() expects
* @itr_desc was obtained from a call to dt_get_interrupt_by_index()
* or dt_get_interrupt_by_name(). That call configured the interrupt.
*/
TEE_Result interrupt_create_handler(struct itr_chip *itr_chip, size_t itr_num,
itr_handler_t callback, void *priv,
uint32_t flags,
struct itr_handler **out_hdl);

/*
* interrupt_add_handler_with_chip() - Register an interrupt handler providing
* the interrupt chip reference in specific argument @chip.
Expand Down Expand Up @@ -399,4 +433,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*/
Loading