Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | /*
* Copyright (c) 2014 Wind River Systems, Inc.
* Copyright (c) 2020-2022 Synopsys.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief ARCv2 Interrupt Unit device driver
*
* The ARCv2 interrupt unit has 16 allocated exceptions associated with
* vectors 0 to 15 and 240 interrupts associated with vectors 16 to 255.
* The interrupt unit is optional in the ARCv2-based processors. When
* building a processor, you can configure the processor to include an
* interrupt unit. The ARCv2 interrupt unit is highly programmable.
*/
#include <zephyr/kernel.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/device.h>
#include <zephyr/device.h>
#define DT_DRV_COMPAT snps_arcv2_intc
#ifdef CONFIG_ARC_CONNECT
static void arc_shared_intc_init(void)
{
/*
* Initialize all IDU interrupts:
* - select round-robbin
* - disable all lines
*/
BUILD_ASSERT(CONFIG_NUM_IRQS > ARC_CONNECT_IDU_IRQ_START);
__ASSERT(z_arc_v2_core_id() == ARC_MP_PRIMARY_CPU_ID,
"idu interrupts must be inited from primary core");
z_arc_connect_idu_disable();
for (uint32_t i = 0; i < (CONFIG_NUM_IRQS - ARC_CONNECT_IDU_IRQ_START); i++) {
/*
* TODO: don't use z_arc_connect_idu* functions to avoid
* locking/unlocking every time.
*/
/* Disable (mask) line */
z_arc_connect_idu_set_mask(i, 0x1);
z_arc_connect_idu_set_mode(i, ARC_CONNECT_INTRPT_TRIGGER_LEVEL,
ARC_CONNECT_DISTRI_MODE_ROUND_ROBIN);
/*
* Fake round-robin: we allow to distribute interrupts only to primary core as
* secondary cores may be not initialized yet.
*/
z_arc_connect_idu_set_dest(i, BIT(ARC_MP_PRIMARY_CPU_ID));
}
z_arc_connect_idu_enable();
}
/* Allow to schedule IRQ to all cores after we bring up all secondary cores */
static int arc_shared_intc_update_post_smp(void)
{
__ASSERT(z_arc_v2_core_id() == ARC_MP_PRIMARY_CPU_ID,
"idu interrupts must be updated from primary core");
z_arc_connect_idu_disable();
for (uint32_t i = 0; i < (CONFIG_NUM_IRQS - ARC_CONNECT_IDU_IRQ_START); i++) {
/* TODO: take arc_connect_spinlock one time to avoid locking/unlocking every time */
z_arc_connect_idu_set_dest(i, BIT_MASK(arch_num_cpus()));
}
z_arc_connect_idu_enable();
return 0;
}
SYS_INIT(arc_shared_intc_update_post_smp, SMP, 0);
#endif /* CONFIG_ARC_CONNECT */
/* lowest IRQ priority */
#ifdef CONFIG_ARC_SECURE_FIRMWARE
#define ARC_IRQ_DEFAULT_PRIORITY ((CONFIG_NUM_IRQ_PRIO_LEVELS - 1) | _ARC_V2_IRQ_PRIORITY_SECURE)
#else
#define ARC_IRQ_DEFAULT_PRIORITY (CONFIG_NUM_IRQ_PRIO_LEVELS - 1)
#endif
static inline void arc_core_intc_init_nolock(uint32_t irq, uint32_t state)
{
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_SELECT, irq);
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_PRIORITY, ARC_IRQ_DEFAULT_PRIORITY);
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_TRIGGER, _ARC_V2_INT_LEVEL);
z_arc_v2_aux_reg_write(_ARC_V2_IRQ_ENABLE, state);
}
/*
* Initialize the core private interrupt controller.
*
* This function must be called on each CPU in case of SMP system.
*
* NOTE: core interrupts are still globally disabled at this point (STATUS32.IE = 0), so there is
* no need to protect the window between a write to IRQ_SELECT and subsequent writes to the
* selected IRQ's registers with locks.
*/
void arc_core_private_intc_init(void)
{
/*
* Interrupts from 0 to 15 are exceptions and they are ignored by IRQ auxiliary registers.
* We skip those interrupt lines while setting up core private interrupt controller.
*/
BUILD_ASSERT(CONFIG_GEN_IRQ_START_VECTOR == 16);
/*
* System with IDU case (most likely multi-core system):
* - disable private IRQs: they will be enabled with irq_enable before usage
* - enable shared (IDU) IRQs: their enabling / disabling is controlled via IDU, so we
* always pass them via core private interrupt controller.
* System without IDU case (single-core system):
* - disable all IRQs: they will be enabled with irq_enable before usage
*/
#ifdef CONFIG_ARC_CONNECT
for (uint32_t irq = CONFIG_GEN_IRQ_START_VECTOR; irq < ARC_CONNECT_IDU_IRQ_START; irq++) {
arc_core_intc_init_nolock(irq, _ARC_V2_INT_DISABLE);
}
for (uint32_t irq = ARC_CONNECT_IDU_IRQ_START; irq < CONFIG_NUM_IRQS; irq++) {
arc_core_intc_init_nolock(irq, _ARC_V2_INT_ENABLE);
}
#else
for (uint32_t irq = CONFIG_GEN_IRQ_START_VECTOR; irq < CONFIG_NUM_IRQS; irq++) {
arc_core_intc_init_nolock(irq, _ARC_V2_INT_DISABLE);
}
#endif /* CONFIG_ARC_CONNECT */
}
static int arc_irq_init(const struct device *dev)
{
#ifdef CONFIG_ARC_CONNECT
arc_shared_intc_init();
#endif /* CONFIG_ARC_CONNECT */
/*
* We initialize per-core part for core 0 here,
* for rest cores it will be initialized in slave_start.
*/
arc_core_private_intc_init();
return 0;
}
DEVICE_DT_INST_DEFINE(0, arc_irq_init, NULL, NULL, NULL,
PRE_KERNEL_1, 0, NULL);
|