|  | /* Copyright (c) 2021 Intel Corporation | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include <zephyr/device.h> | 
|  | #include <xtensa/xtruntime.h> | 
|  | #include <zephyr/irq_nextlevel.h> | 
|  | #include <xtensa/hal.h> | 
|  | #include <zephyr/init.h> | 
|  | #include <zephyr/logging/log.h> | 
|  |  | 
|  | #include <adsp_shim.h> | 
|  | #include <cavs-idc.h> | 
|  | #include "soc.h" | 
|  |  | 
|  | #ifdef CONFIG_DYNAMIC_INTERRUPTS | 
|  | #include <zephyr/sw_isr_table.h> | 
|  | #endif | 
|  |  | 
|  | LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL); | 
|  |  | 
|  | #define CAVS_INTC_NODE(n) DT_INST(n, intel_cavs_intc) | 
|  |  | 
|  | void z_soc_irq_enable(uint32_t irq) | 
|  | { | 
|  | const struct device *dev_cavs; | 
|  |  | 
|  | switch (XTENSA_IRQ_NUMBER(irq)) { | 
|  | case DT_IRQN(CAVS_INTC_NODE(0)): | 
|  | dev_cavs = DEVICE_DT_GET(CAVS_INTC_NODE(0)); | 
|  | break; | 
|  | case DT_IRQN(CAVS_INTC_NODE(1)): | 
|  | dev_cavs = DEVICE_DT_GET(CAVS_INTC_NODE(1)); | 
|  | break; | 
|  | case DT_IRQN(CAVS_INTC_NODE(2)): | 
|  | dev_cavs = DEVICE_DT_GET(CAVS_INTC_NODE(2)); | 
|  | break; | 
|  | case DT_IRQN(CAVS_INTC_NODE(3)): | 
|  | dev_cavs = DEVICE_DT_GET(CAVS_INTC_NODE(3)); | 
|  | break; | 
|  | default: | 
|  | /* regular interrupt */ | 
|  | z_xtensa_irq_enable(XTENSA_IRQ_NUMBER(irq)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!device_is_ready(dev_cavs)) { | 
|  | LOG_DBG("board: CAVS device is not ready"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * The specified interrupt is in CAVS interrupt controller. | 
|  | * So enable core interrupt first. | 
|  | */ | 
|  | z_xtensa_irq_enable(XTENSA_IRQ_NUMBER(irq)); | 
|  |  | 
|  | /* Then enable the interrupt in CAVS interrupt controller */ | 
|  | irq_enable_next_level(dev_cavs, CAVS_IRQ_NUMBER(irq)); | 
|  | } | 
|  |  | 
|  | void z_soc_irq_disable(uint32_t irq) | 
|  | { | 
|  | const struct device *dev_cavs; | 
|  |  | 
|  | switch (XTENSA_IRQ_NUMBER(irq)) { | 
|  | case DT_IRQN(CAVS_INTC_NODE(0)): | 
|  | dev_cavs = DEVICE_DT_GET(CAVS_INTC_NODE(0)); | 
|  | break; | 
|  | case DT_IRQN(CAVS_INTC_NODE(1)): | 
|  | dev_cavs = DEVICE_DT_GET(CAVS_INTC_NODE(1)); | 
|  | break; | 
|  | case DT_IRQN(CAVS_INTC_NODE(2)): | 
|  | dev_cavs = DEVICE_DT_GET(CAVS_INTC_NODE(2)); | 
|  | break; | 
|  | case DT_IRQN(CAVS_INTC_NODE(3)): | 
|  | dev_cavs = DEVICE_DT_GET(CAVS_INTC_NODE(3)); | 
|  | break; | 
|  | default: | 
|  | /* regular interrupt */ | 
|  | z_xtensa_irq_disable(XTENSA_IRQ_NUMBER(irq)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!device_is_ready(dev_cavs)) { | 
|  | LOG_DBG("board: CAVS device is not ready"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * The specified interrupt is in CAVS interrupt controller. | 
|  | * So disable the interrupt in CAVS interrupt controller. | 
|  | */ | 
|  | irq_disable_next_level(dev_cavs, CAVS_IRQ_NUMBER(irq)); | 
|  |  | 
|  | /* Then disable the parent IRQ if all children are disabled */ | 
|  | if (!irq_is_enabled_next_level(dev_cavs)) { | 
|  | z_xtensa_irq_disable(XTENSA_IRQ_NUMBER(irq)); | 
|  | } | 
|  | } | 
|  |  | 
|  | int z_soc_irq_is_enabled(unsigned int irq) | 
|  | { | 
|  | const struct device *dev_cavs; | 
|  | int ret = 0; | 
|  |  | 
|  | switch (XTENSA_IRQ_NUMBER(irq)) { | 
|  | case DT_IRQN(CAVS_INTC_NODE(0)): | 
|  | dev_cavs = DEVICE_DT_GET(CAVS_INTC_NODE(0)); | 
|  | break; | 
|  | case DT_IRQN(CAVS_INTC_NODE(1)): | 
|  | dev_cavs = DEVICE_DT_GET(CAVS_INTC_NODE(1)); | 
|  | break; | 
|  | case DT_IRQN(CAVS_INTC_NODE(2)): | 
|  | dev_cavs = DEVICE_DT_GET(CAVS_INTC_NODE(2)); | 
|  | break; | 
|  | case DT_IRQN(CAVS_INTC_NODE(3)): | 
|  | dev_cavs = DEVICE_DT_GET(CAVS_INTC_NODE(3)); | 
|  | break; | 
|  | default: | 
|  | /* regular interrupt */ | 
|  | ret = z_xtensa_irq_is_enabled(XTENSA_IRQ_NUMBER(irq)); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (!device_is_ready(dev_cavs)) { | 
|  | LOG_DBG("board: CAVS device is not ready"); | 
|  | ret = -ENODEV; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | /* Then check the interrupt in CAVS interrupt controller */ | 
|  | ret = irq_line_is_enabled_next_level(dev_cavs, CAVS_IRQ_NUMBER(irq)); | 
|  |  | 
|  | out: | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_DYNAMIC_INTERRUPTS | 
|  | int z_soc_irq_connect_dynamic(unsigned int irq, unsigned int priority, | 
|  | void (*routine)(const void *parameter), | 
|  | const void *parameter, uint32_t flags) | 
|  | { | 
|  | uint32_t table_idx; | 
|  | uint32_t cavs_irq, cavs_idx; | 
|  | int ret; | 
|  |  | 
|  | ARG_UNUSED(flags); | 
|  | ARG_UNUSED(priority); | 
|  |  | 
|  | /* extract 2nd level interrupt number */ | 
|  | cavs_irq = CAVS_IRQ_NUMBER(irq); | 
|  | ret = irq; | 
|  |  | 
|  | if (cavs_irq == 0) { | 
|  | /* Not affecting 2nd level interrupts */ | 
|  | z_isr_install(irq, routine, parameter); | 
|  | goto irq_connect_out; | 
|  | } | 
|  |  | 
|  | /* Figure out the base index. */ | 
|  | switch (XTENSA_IRQ_NUMBER(irq)) { | 
|  | case DT_IRQN(CAVS_INTC_NODE(0)): | 
|  | cavs_idx = 0; | 
|  | break; | 
|  | case DT_IRQN(CAVS_INTC_NODE(1)): | 
|  | cavs_idx = 1; | 
|  | break; | 
|  | case DT_IRQN(CAVS_INTC_NODE(2)): | 
|  | cavs_idx = 2; | 
|  | break; | 
|  | case DT_IRQN(CAVS_INTC_NODE(3)): | 
|  | cavs_idx = 3; | 
|  | break; | 
|  | default: | 
|  | ret = -EINVAL; | 
|  | goto irq_connect_out; | 
|  | } | 
|  |  | 
|  | table_idx = CONFIG_CAVS_ISR_TBL_OFFSET + | 
|  | CONFIG_MAX_IRQ_PER_AGGREGATOR * cavs_idx; | 
|  | table_idx += cavs_irq; | 
|  |  | 
|  | _sw_isr_table[table_idx].arg = parameter; | 
|  | _sw_isr_table[table_idx].isr = routine; | 
|  |  | 
|  | irq_connect_out: | 
|  | return ret; | 
|  | } | 
|  | #endif |