diff --git a/core/arch/arm/kernel/thread.c b/core/arch/arm/kernel/thread.c index 66833b3a086..0dd9007ba63 100644 --- a/core/arch/arm/kernel/thread.c +++ b/core/arch/arm/kernel/thread.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/core/drivers/atmel_piobu.c b/core/drivers/atmel_piobu.c index e8bf9f2670f..df06620a616 100644 --- a/core/drivers/atmel_piobu.c +++ b/core/drivers/atmel_piobu.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/core/drivers/atmel_wdt.c b/core/drivers/atmel_wdt.c index 7d2a5c07e15..9f1aa304705 100644 --- a/core/drivers/atmel_wdt.c +++ b/core/drivers/atmel_wdt.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/core/drivers/gic.c b/core/drivers/gic.c index 48f4b6d824f..04b4fc466c9 100644 --- a/core/drivers/gic.c +++ b/core/drivers/gic.c @@ -6,11 +6,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -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: @@ -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*/ diff --git a/core/include/kernel/dt.h b/core/include/kernel/dt.h index e8bfac5d2ac..ad5cde23ab9 100644 --- a/core/include/kernel/dt.h +++ b/core/include/kernel/dt.h @@ -7,7 +7,6 @@ #define __KERNEL_DT_H #include -#include #include #include #include diff --git a/core/include/kernel/dt_driver.h b/core/include/kernel/dt_driver.h index d95f8b94c94..0eef55db004 100644 --- a/core/include/kernel/dt_driver.h +++ b/core/include/kernel/dt_driver.h @@ -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: @@ -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 @@ -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: @@ -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. @@ -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: @@ -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 @@ -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 diff --git a/core/include/kernel/interrupt.h b/core/include/kernel/interrupt.h index e445a1b4760..370f4330f66 100644 --- a/core/include/kernel/interrupt.h +++ b/core/include/kernel/interrupt.h @@ -6,6 +6,7 @@ #define __KERNEL_INTERRUPT_H #include +#include #include #include #include @@ -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, @@ -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. @@ -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*/ diff --git a/core/kernel/dt_driver.c b/core/kernel/dt_driver.c index 256aefa9058..4ffac37854c 100644 --- a/core/kernel/dt_driver.c +++ b/core/kernel/dt_driver.c @@ -249,7 +249,8 @@ dt_driver_get_provider_by_phandle(uint32_t phandle, enum dt_driver_type type) static TEE_Result device_from_provider_prop(struct dt_driver_provider *prv, const void *fdt, int phandle_node, - const uint32_t *prop, void **device) + const uint32_t *prop, + void *device_ref) { TEE_Result res = TEE_ERROR_GENERIC; struct dt_pargs *pargs = NULL; @@ -264,9 +265,9 @@ static TEE_Result device_from_provider_prop(struct dt_driver_provider *prv, pargs->phandle_node = phandle_node; pargs->args_count = prv->provider_cells; for (n = 0; n < prv->provider_cells; n++) - pargs->args[n] = fdt32_to_cpu(prop[n + 1]); + pargs->args[n] = fdt32_to_cpu(prop[n]); - res = prv->get_of_device(pargs, prv->priv_data, device); + res = prv->get_of_device(pargs, prv->priv_data, device_ref); free(pargs); @@ -274,7 +275,8 @@ static TEE_Result device_from_provider_prop(struct dt_driver_provider *prv, } TEE_Result dt_driver_device_from_parent(const void *fdt, int nodeoffset, - enum dt_driver_type type, void **device) + enum dt_driver_type type, + void *device_ref) { int parent = -1; struct dt_driver_provider *prv = NULL; @@ -291,7 +293,8 @@ TEE_Result dt_driver_device_from_parent(const void *fdt, int nodeoffset, return TEE_ERROR_DEFER_DRIVER_INIT; } - return device_from_provider_prop(prv, fdt, nodeoffset, NULL, device); + return device_from_provider_prop(prv, fdt, nodeoffset, NULL, + device_ref); } TEE_Result dt_driver_device_from_node_idx_prop_phandle(const char *prop_name, @@ -300,7 +303,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 **device) + void *device_ref) { int len = 0; const uint32_t *prop = NULL; @@ -328,14 +331,14 @@ TEE_Result dt_driver_device_from_node_idx_prop_phandle(const char *prop_name, return TEE_ERROR_ITEM_NOT_FOUND; return device_from_provider_prop(prv, fdt, phandle_node_unused, - prop + prop_index, device); + prop + prop_index, device_ref); } 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 **device) + void *device_ref) { int len = 0; int idx = 0; @@ -395,8 +398,11 @@ TEE_Result dt_driver_device_from_node_idx_prop(const char *prop_name, continue; } + /* Skip property cell with the phandle, already handled */ + idx32++; + return device_from_provider_prop(prv, fdt, phandle_node, - prop + idx32, device); + prop + idx32, device_ref); } return TEE_ERROR_ITEM_NOT_FOUND; diff --git a/core/kernel/interrupt.c b/core/kernel/interrupt.c index a49bbf2d106..f46a7700b75 100644 --- a/core/kernel/interrupt.c +++ b/core/kernel/interrupt.c @@ -104,8 +104,9 @@ TEE_Result interrupt_configure(struct itr_chip *chip, size_t itr_num, return TEE_SUCCESS; } -TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl, - uint32_t type, uint32_t prio) +static TEE_Result add_configure_handler(struct itr_handler *hdl, + uint32_t type, uint32_t prio, + bool configure) { struct itr_handler *h = NULL; @@ -122,13 +123,52 @@ TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl, } } - interrupt_configure(hdl->chip, hdl->it, type, prio); + if (configure) + interrupt_configure(hdl->chip, hdl->it, type, prio); SLIST_INSERT_HEAD(&hdl->chip->handlers, hdl, link); return TEE_SUCCESS; } +TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl, + uint32_t type, uint32_t prio) +{ + return add_configure_handler(hdl, type, prio, true /* configure */); +} + +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) +{ + TEE_Result res = TEE_ERROR_GENERIC; + struct itr_handler *itr_hdl = NULL; + + itr_hdl = calloc(1, sizeof(*itr_hdl)); + if (!itr_hdl) + return TEE_ERROR_OUT_OF_MEMORY; + + *itr_hdl = (struct itr_handler){ + .chip = itr_chip, + .it = itr_num, + .flags = flags, + .handler = callback, + .data = priv, + }; + + res = add_configure_handler(itr_hdl, 0, 0, false /* configure */); + if (res) { + free(itr_hdl); + return res; + } + + if (out_hdl) + *out_hdl = itr_hdl; + + return TEE_SUCCESS; +} + void interrupt_remove_handler(struct itr_handler *hdl) { struct itr_handler *h = NULL; @@ -196,3 +236,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*/