| /* Copyright (c) 2021 Intel Corporation |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <device.h> |
| #include <xtensa/xtruntime.h> |
| #include <irq_nextlevel.h> |
| #include <xtensa/hal.h> |
| #include <init.h> |
| #include <logging/log.h> |
| |
| #include <cavs-shim.h> |
| #include <cavs-idc.h> |
| #include "soc.h" |
| |
| #ifdef CONFIG_DYNAMIC_INTERRUPTS |
| #include <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_get_binding(DT_LABEL(CAVS_INTC_NODE(0))); |
| break; |
| case DT_IRQN(CAVS_INTC_NODE(1)): |
| dev_cavs = device_get_binding(DT_LABEL(CAVS_INTC_NODE(1))); |
| break; |
| case DT_IRQN(CAVS_INTC_NODE(2)): |
| dev_cavs = device_get_binding(DT_LABEL(CAVS_INTC_NODE(2))); |
| break; |
| case DT_IRQN(CAVS_INTC_NODE(3)): |
| dev_cavs = device_get_binding(DT_LABEL(CAVS_INTC_NODE(3))); |
| break; |
| default: |
| /* regular interrupt */ |
| z_xtensa_irq_enable(XTENSA_IRQ_NUMBER(irq)); |
| return; |
| } |
| |
| if (!dev_cavs) { |
| LOG_DBG("board: CAVS device binding failed"); |
| 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_get_binding(DT_LABEL(CAVS_INTC_NODE(0))); |
| break; |
| case DT_IRQN(CAVS_INTC_NODE(1)): |
| dev_cavs = device_get_binding(DT_LABEL(CAVS_INTC_NODE(1))); |
| break; |
| case DT_IRQN(CAVS_INTC_NODE(2)): |
| dev_cavs = device_get_binding(DT_LABEL(CAVS_INTC_NODE(2))); |
| break; |
| case DT_IRQN(CAVS_INTC_NODE(3)): |
| dev_cavs = device_get_binding(DT_LABEL(CAVS_INTC_NODE(3))); |
| break; |
| default: |
| /* regular interrupt */ |
| z_xtensa_irq_disable(XTENSA_IRQ_NUMBER(irq)); |
| return; |
| } |
| |
| if (!dev_cavs) { |
| LOG_DBG("board: CAVS device binding failed"); |
| 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_get_binding(DT_LABEL(CAVS_INTC_NODE(0))); |
| break; |
| case DT_IRQN(CAVS_INTC_NODE(1)): |
| dev_cavs = device_get_binding(DT_LABEL(CAVS_INTC_NODE(1))); |
| break; |
| case DT_IRQN(CAVS_INTC_NODE(2)): |
| dev_cavs = device_get_binding(DT_LABEL(CAVS_INTC_NODE(2))); |
| break; |
| case DT_IRQN(CAVS_INTC_NODE(3)): |
| dev_cavs = device_get_binding(DT_LABEL(CAVS_INTC_NODE(3))); |
| break; |
| default: |
| /* regular interrupt */ |
| ret = z_xtensa_irq_is_enabled(XTENSA_IRQ_NUMBER(irq)); |
| goto out; |
| } |
| |
| if (!dev_cavs) { |
| LOG_DBG("board: CAVS device binding failed"); |
| 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 |