blob: d769ba81963f09c26fe2d2aafb4a43abbfc2d98e [file] [log] [blame]
/*
* Copyright (c) 2019 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <device.h>
#include <arch/xtensa/xtensa_api.h>
#include <xtensa/xtruntime.h>
#include <irq_nextlevel.h>
#include <xtensa/hal.h>
#include <init.h>
#include "soc.h"
#ifdef CONFIG_DYNAMIC_INTERRUPTS
#include <sw_isr_table.h>
#endif
#define LOG_LEVEL CONFIG_SOC_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(soc);
#define CAVS_INTC_NODE(n) DT_INST(n, intel_cavs_intc)
void z_soc_irq_enable(u32_t irq)
{
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(u32_t irq)
{
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)
{
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 enable 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)(void *parameter),
void *parameter, u32_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
static inline void soc_set_power_and_clock(void)
{
volatile struct soc_dsp_shim_regs *dsp_shim_regs =
(volatile struct soc_dsp_shim_regs *)SOC_DSP_SHIM_REG_BASE;
/*
* DSP Core 0 PLL Clock Select divide by 1
* DSP Core 1 PLL Clock Select divide by 1
* Low Power Domain Clock Select depends on LMPCS bit
* High Power Domain Clock Select depands on HMPCS bit
* Low Power Domain PLL Clock Select device by 4
* High Power Domain PLL Clock Select device by 2
* Tensilica Core Prevent Audio PLL Shutdown (TCPAPLLS)
* Tensilica Core Prevent Local Clock Gating (Core 0)
* Tensilica Core Prevent Local Clock Gating (Core 1)
*/
dsp_shim_regs->clkctl =
SOC_CLKCTL_DPCS_DIV1(0) |
SOC_CLKCTL_DPCS_DIV1(1) |
SOC_CLKCTL_LDCS_LMPCS |
SOC_CLKCTL_HDCS_HMPCS |
SOC_CLKCTL_LPMEM_PLL_CLK_SEL_DIV4 |
SOC_CLKCTL_HPMEM_PLL_CLK_SEL_DIV2 |
SOC_CLKCTL_TCPAPLLS |
SOC_CLKCTL_TCPLCG_DIS(0) |
SOC_CLKCTL_TCPLCG_DIS(1);
/* Disable power gating for both cores */
dsp_shim_regs->pwrctl |= SOC_PWRCTL_DISABLE_PWR_GATING_DSP1 |
SOC_PWRCTL_DISABLE_PWR_GATING_DSP0;
/* Rewrite the low power sequencing control bits */
dsp_shim_regs->lpsctl = dsp_shim_regs->lpsctl;
}
static int soc_init(struct device *dev)
{
soc_set_power_and_clock();
return 0;
}
SYS_INIT(soc_init, PRE_KERNEL_1, 99);