diff --git a/drivers/interrupt_controller/CMakeLists.txt b/drivers/interrupt_controller/CMakeLists.txt index 97b321b9731..3059a0e238d 100644 --- a/drivers/interrupt_controller/CMakeLists.txt +++ b/drivers/interrupt_controller/CMakeLists.txt @@ -7,3 +7,4 @@ zephyr_sources_ifdef(CONFIG_PIC_DISABLE i8259.c) zephyr_sources_ifdef(CONFIG_PLIC_FE310 plic_fe310.c) zephyr_sources_ifdef(CONFIG_SHARED_IRQ shared_irq.c) zephyr_sources_ifdef(CONFIG_SOC_FAMILY_STM32 exti_stm32.c) +zephyr_sources_ifdef(CONFIG_CAVS_ICTL cavs_ictl.c) diff --git a/drivers/interrupt_controller/Kconfig b/drivers/interrupt_controller/Kconfig index 2be10e0f7b0..c1cd73a63d8 100644 --- a/drivers/interrupt_controller/Kconfig +++ b/drivers/interrupt_controller/Kconfig @@ -130,4 +130,6 @@ source "drivers/interrupt_controller/Kconfig.stm32" source "drivers/interrupt_controller/Kconfig.multilevel" +source "drivers/interrupt_controller/Kconfig.s1000" + endmenu diff --git a/drivers/interrupt_controller/Kconfig.s1000 b/drivers/interrupt_controller/Kconfig.s1000 new file mode 100644 index 00000000000..c749c98db16 --- /dev/null +++ b/drivers/interrupt_controller/Kconfig.s1000 @@ -0,0 +1,68 @@ +# Kconfig - Intel_S1000 configuration +# +# Copyright (c) 2017 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +config CAVS_ICTL + bool "CAVS Interrupt Logic" + default n + depends on MULTI_LEVEL_INTERRUPTS + help + These are 4 in number supporting a max of 32 interrupts each. + +config CAVS_ISR_TBL_OFFSET + int "Offset in the SW ISR Table" + default 0 + depends on CAVS_ICTL + help + This indicates the offset in the SW_ISR_TABLE beginning from where + the ISRs for CAVS Interrupt Controller are assigned. + +config CAVS_ICTL_0_NAME + string "CAVS 0 Driver name" + depends on CAVS_ICTL + default "CAVS_0" + +config CAVS_ICTL_0_OFFSET + hex "Parent interrupt number to which CAVS_0 maps" + default 0x00 + depends on CAVS_ICTL + +config CAVS_ICTL_1_NAME + string "CAVS 1 Driver name" + depends on CAVS_ICTL + default "CAVS_1" + +config CAVS_ICTL_1_OFFSET + hex "Parent interrupt number to which CAVS_1 maps" + default 0x00 + depends on CAVS_ICTL + +config CAVS_ICTL_2_NAME + string "CAVS 2 Driver name" + depends on CAVS_ICTL + default "CAVS_2" + +config CAVS_ICTL_2_OFFSET + hex "Parent interrupt number to which CAVS_2 maps" + default 0x00 + depends on CAVS_ICTL + +config CAVS_ICTL_3_NAME + string "CAVS 3 Driver name" + depends on CAVS_ICTL + default "CAVS_3" + +config CAVS_ICTL_3_OFFSET + hex "Parent interrupt number to which CAVS_3 maps" + default 0x00 + depends on CAVS_ICTL + +config CAVS_ICTL_INIT_PRIORITY + int "CAVS ICTL Init priority" + default 60 + depends on CAVS_ICTL + help + Cavs Interrupt Logic initialization priority. diff --git a/drivers/interrupt_controller/cavs_ictl.c b/drivers/interrupt_controller/cavs_ictl.c new file mode 100644 index 00000000000..3fbefef51c2 --- /dev/null +++ b/drivers/interrupt_controller/cavs_ictl.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "cavs_ictl.h" + +static ALWAYS_INLINE void cavs_ictl_dispatch_child_isrs(u32_t intr_status, + u32_t isr_base_offset) +{ + u32_t intr_bitpos, intr_offset; + + /* Dispatch lower level ISRs depending upon the bit set */ + while (intr_status) { + intr_bitpos = find_lsb_set(intr_status) - 1; + intr_status &= ~(1 << intr_bitpos); + intr_offset = isr_base_offset + intr_bitpos; + _sw_isr_table[intr_offset].isr( + _sw_isr_table[intr_offset].arg); + } +} + +static void cavs_ictl_isr(void *arg) +{ + struct device *port = (struct device *)arg; + struct cavs_ictl_runtime *context = port->driver_data; + + const struct cavs_ictl_config *config = port->config->config_info; + + volatile struct cavs_registers * const regs = + (struct cavs_registers *)context->base_addr; + + cavs_ictl_dispatch_child_isrs(regs->status_il, + config->isr_table_offset); +} + +static inline void cavs_ictl_irq_enable(struct device *dev, unsigned int irq) +{ + struct cavs_ictl_runtime *context = dev->driver_data; + + volatile struct cavs_registers * const regs = + (struct cavs_registers *)context->base_addr; + + regs->enable_il = (1 << irq); +} + +static inline void cavs_ictl_irq_disable(struct device *dev, unsigned int irq) +{ + struct cavs_ictl_runtime *context = dev->driver_data; + + volatile struct cavs_registers * const regs = + (struct cavs_registers *)context->base_addr; + + regs->disable_il = (1 << irq); +} + +static inline unsigned int cavs_ictl_irq_get_state(struct device *dev) +{ + struct cavs_ictl_runtime *context = dev->driver_data; + + volatile struct cavs_registers * const regs = + (struct cavs_registers *)context->base_addr; + + /* When the bits of this register are set, it means the + * corresponding interrupts are disabled. This function + * returns 0 only if ALL the interrupts are disabled. + */ + if (regs->disable_state_il == 0xFFFFFFFF) { + return 0; + } + + return 1; +} + +static const struct irq_next_level_api cavs_apis = { + .intr_enable = cavs_ictl_irq_enable, + .intr_disable = cavs_ictl_irq_disable, + .intr_get_state = cavs_ictl_irq_get_state, +}; + +static int cavs_ictl_0_initialize(struct device *port) +{ + return 0; +} + +static void cavs_config_0_irq(struct device *port); + +static const struct cavs_ictl_config cavs_config_0 = { + .irq_num = CAVS_ICTL_0_IRQ, + .isr_table_offset = CONFIG_CAVS_ISR_TBL_OFFSET, + .config_func = cavs_config_0_irq, +}; + +static struct cavs_ictl_runtime cavs_0_runtime = { + .base_addr = CAVS_ICTL_BASE_ADDR, +}; + +DEVICE_AND_API_INIT(cavs_ictl_0, CONFIG_CAVS_ICTL_0_NAME, + cavs_ictl_0_initialize, &cavs_0_runtime, &cavs_config_0, + POST_KERNEL, CONFIG_CAVS_ICTL_INIT_PRIORITY, &cavs_apis); + +static void cavs_config_0_irq(struct device *port) +{ + IRQ_CONNECT(CAVS_ICTL_0_IRQ, CONFIG_CAVS_ICTL_0_IRQ_PRI, cavs_ictl_isr, + DEVICE_GET(cavs_ictl_0), CAVS_ICTL_0_IRQ_FLAGS); +} + +static int cavs_ictl_1_initialize(struct device *port) +{ + return 0; +} + +static void cavs_config_1_irq(struct device *port); + +static const struct cavs_ictl_config cavs_config_1 = { + .irq_num = CAVS_ICTL_1_IRQ, + .isr_table_offset = CONFIG_CAVS_ISR_TBL_OFFSET + + CONFIG_MAX_IRQ_PER_AGGREGATOR, + .config_func = cavs_config_1_irq, +}; + +static struct cavs_ictl_runtime cavs_1_runtime = { + .base_addr = CAVS_ICTL_BASE_ADDR + sizeof(struct cavs_registers), +}; + +DEVICE_AND_API_INIT(cavs_ictl_1, CONFIG_CAVS_ICTL_1_NAME, + cavs_ictl_1_initialize, &cavs_1_runtime, &cavs_config_1, + POST_KERNEL, CONFIG_CAVS_ICTL_INIT_PRIORITY, &cavs_apis); + +static void cavs_config_1_irq(struct device *port) +{ + IRQ_CONNECT(CAVS_ICTL_1_IRQ, CONFIG_CAVS_ICTL_1_IRQ_PRI, cavs_ictl_isr, + DEVICE_GET(cavs_ictl_1), CAVS_ICTL_1_IRQ_FLAGS); +} + +static int cavs_ictl_2_initialize(struct device *port) +{ + return 0; +} + +static void cavs_config_2_irq(struct device *port); + +static const struct cavs_ictl_config cavs_config_2 = { + .irq_num = CAVS_ICTL_2_IRQ, + .isr_table_offset = CONFIG_CAVS_ISR_TBL_OFFSET + + CONFIG_MAX_IRQ_PER_AGGREGATOR * 2, + .config_func = cavs_config_2_irq, +}; + +static struct cavs_ictl_runtime cavs_2_runtime = { + .base_addr = CAVS_ICTL_BASE_ADDR + sizeof(struct cavs_registers) * 2, +}; + +DEVICE_AND_API_INIT(cavs_ictl_2, CONFIG_CAVS_ICTL_2_NAME, + cavs_ictl_2_initialize, &cavs_2_runtime, &cavs_config_2, + POST_KERNEL, CONFIG_CAVS_ICTL_INIT_PRIORITY, &cavs_apis); + +static void cavs_config_2_irq(struct device *port) +{ + IRQ_CONNECT(CAVS_ICTL_2_IRQ, CONFIG_CAVS_ICTL_2_IRQ_PRI, cavs_ictl_isr, + DEVICE_GET(cavs_ictl_2), CAVS_ICTL_2_IRQ_FLAGS); +} + +static int cavs_ictl_3_initialize(struct device *port) +{ + return 0; +} + +static void cavs_config_3_irq(struct device *port); + +static const struct cavs_ictl_config cavs_config_3 = { + .irq_num = CAVS_ICTL_3_IRQ, + .isr_table_offset = CONFIG_CAVS_ISR_TBL_OFFSET + + CONFIG_MAX_IRQ_PER_AGGREGATOR*3, + .config_func = cavs_config_3_irq, +}; + +static struct cavs_ictl_runtime cavs_3_runtime = { + .base_addr = CAVS_ICTL_BASE_ADDR + sizeof(struct cavs_registers) * 3, +}; + +DEVICE_AND_API_INIT(cavs_ictl_3, CONFIG_CAVS_ICTL_3_NAME, + cavs_ictl_3_initialize, &cavs_3_runtime, &cavs_config_3, + POST_KERNEL, CONFIG_CAVS_ICTL_INIT_PRIORITY, &cavs_apis); + +static void cavs_config_3_irq(struct device *port) +{ + IRQ_CONNECT(CAVS_ICTL_3_IRQ, CONFIG_CAVS_ICTL_3_IRQ_PRI, cavs_ictl_isr, + DEVICE_GET(cavs_ictl_3), CAVS_ICTL_3_IRQ_FLAGS); +} diff --git a/drivers/interrupt_controller/cavs_ictl.h b/drivers/interrupt_controller/cavs_ictl.h new file mode 100644 index 00000000000..4913c612d98 --- /dev/null +++ b/drivers/interrupt_controller/cavs_ictl.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _CAVS_ICTL_H_ +#define _CAVS_ICTL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*cavs_ictl_config_irq_t)(struct device *port); + +struct cavs_ictl_config { + u32_t irq_num; + u32_t isr_table_offset; + cavs_ictl_config_irq_t config_func; +}; + +struct cavs_ictl_runtime { + u32_t base_addr; +}; + +struct cavs_registers { + u32_t disable_il; /* il_msd - offset 0x00 */ + u32_t enable_il; /* il_mcd - offset 0x04 */ + u32_t disable_state_il; /* il_md - offset 0x08 */ + u32_t status_il; /* il_sd - offset 0x0C */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _CAVS_ICTL_H_ */ diff --git a/include/irq_nextlevel.h b/include/irq_nextlevel.h new file mode 100644 index 00000000000..1495ec17498 --- /dev/null +++ b/include/irq_nextlevel.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2017 Intel corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Public interface for configuring interrupts + */ +#ifndef _IRQ_NEXTLEVEL_H_ +#define _IRQ_NEXTLEVEL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @cond INTERNAL_HIDDEN + * + * These are for internal use only, so skip these in + * public documentation. + */ +typedef void (*irq_next_level_func_t)(struct device *dev, unsigned int irq); +typedef unsigned int (*irq_next_level_get_state_t)(struct device *dev); + +struct irq_next_level_api { + irq_next_level_func_t intr_enable; + irq_next_level_func_t intr_disable; + irq_next_level_get_state_t intr_get_state; +}; +/** + * @endcond + */ + +/** + * @brief Enable an IRQ in the next level. + * + * This routine enables interrupts present in the interrupt controller. + * + * @param dev Pointer to the device structure for the driver instance. + * @param irq IRQ to be enabled. + * + * @return N/A + */ +static inline void irq_enable_next_level(struct device *dev, u32_t irq) +{ + const struct irq_next_level_api *api = dev->driver_api; + + api->intr_enable(dev, irq); +} + +/** + * @brief Disable an IRQ in the next level. + * + * This routine disables interrupts present in the interrupt controller. + * + * @param dev Pointer to the device structure for the driver instance. + * @param irq IRQ to be disabled. + * + * @return N/A + */ +static inline void irq_disable_next_level(struct device *dev, u32_t irq) +{ + const struct irq_next_level_api *api = dev->driver_api; + + api->intr_disable(dev, irq); +} + +/** + * @brief Get IRQ enable state. + * + * This routine indicates if any interrupts are enabled in the interrupt + * controller. + * + * @param dev Pointer to the device structure for the driver instance. + * + * @return interrupt enable state, true or false + */ +static inline unsigned int irq_is_enabled_next_level(struct device *dev) +{ + const struct irq_next_level_api *api = dev->driver_api; + + return api->intr_get_state(dev); +} + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* _IRQ_NEXTLEVEL_H_ */