/* SPDX-License-Identifier: BSD-2-Clause */ /** * @file * * @ingroup RTEMSBSPsSPARCLEON3 * * @brief This source file contains the implementation of the interrupt * controller support. */ /* * Copyright (C) 2021 embedded brains GmbH & Co. KG * * COPYRIGHT (c) 2011 * Aeroflex Gaisler * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #if !defined(LEON3_IRQAMP_EXTENDED_INTERRUPT) /* GRLIB extended IRQ controller IRQ number */ uint32_t LEON3_IrqCtrl_EIrq; #endif rtems_interrupt_lock LEON3_IrqCtrl_Lock = RTEMS_INTERRUPT_LOCK_INITIALIZER("LEON3 IrqCtrl"); /* Initialize Extended Interrupt controller */ void leon3_ext_irq_init(irqamp *regs) { grlib_store_32(®s->pimask[LEON3_Cpu_Index], 0); grlib_store_32(®s->piforce[LEON3_Cpu_Index], 0); grlib_store_32(®s->iclear, 0xffffffff); #if !defined(LEON3_IRQAMP_EXTENDED_INTERRUPT) LEON3_IrqCtrl_EIrq = IRQAMP_MPSTAT_EIRQ_GET(grlib_load_32(®s->mpstat)); #endif } bool bsp_interrupt_is_valid_vector(rtems_vector_number vector) { if (vector == 0) { return false; } #if defined(LEON3_IRQAMP_EXTENDED_INTERRUPT) return vector <= BSP_INTERRUPT_VECTOR_MAX_EXT; #else if (LEON3_IrqCtrl_EIrq > 0) { return vector <= BSP_INTERRUPT_VECTOR_MAX_EXT; } return vector <= BSP_INTERRUPT_VECTOR_MAX_STD; #endif } #if defined(RTEMS_SMP) Processor_mask leon3_interrupt_affinities[BSP_INTERRUPT_VECTOR_MAX_STD + 1]; #endif void bsp_interrupt_facility_initialize(void) { #if defined(RTEMS_SMP) Processor_mask affinity; size_t i; _Processor_mask_From_index(&affinity, rtems_scheduler_get_processor()); for (i = 0; i < RTEMS_ARRAY_SIZE(leon3_interrupt_affinities); ++i) { leon3_interrupt_affinities[i] = affinity; } #endif leon3_ext_irq_init(LEON3_IrqCtrl_Regs); } rtems_status_code bsp_interrupt_get_attributes( rtems_vector_number vector, rtems_interrupt_attributes *attributes ) { bool is_standard_interrupt; is_standard_interrupt = (vector <= BSP_INTERRUPT_VECTOR_MAX_STD); attributes->is_maskable = (vector != 15); attributes->can_enable = true; attributes->maybe_enable = true; attributes->can_disable = true; attributes->maybe_disable = true; attributes->can_raise = true; attributes->can_raise_on = is_standard_interrupt; attributes->can_clear = true; attributes->cleared_by_acknowledge = true; attributes->can_get_affinity = is_standard_interrupt; attributes->can_set_affinity = is_standard_interrupt; return RTEMS_SUCCESSFUL; } rtems_status_code bsp_interrupt_is_pending( rtems_vector_number vector, bool *pending ) { rtems_interrupt_level level; uint32_t bit; irqamp *regs; bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector)); bsp_interrupt_assert(pending != NULL); bit = 1U << vector; regs = LEON3_IrqCtrl_Regs; rtems_interrupt_local_disable(level); *pending = (grlib_load_32(®s->ipend) & bit) != 0 || (grlib_load_32(®s->piforce[rtems_scheduler_get_processor()]) & bit) != 0; rtems_interrupt_local_enable(level); return RTEMS_SUCCESSFUL; } rtems_status_code bsp_interrupt_raise(rtems_vector_number vector) { uint32_t bit; irqamp *regs; bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector)); bit = 1U << vector; regs = LEON3_IrqCtrl_Regs; if ( vector <= BSP_INTERRUPT_VECTOR_MAX_STD ) { uint32_t cpu_count; uint32_t cpu_index; cpu_count = rtems_scheduler_get_processor_maximum(); for (cpu_index = 0; cpu_index < cpu_count; ++cpu_index) { grlib_store_32(®s->piforce[cpu_index], bit); } } else { rtems_interrupt_lock_context lock_context; uint32_t ipend; /* * This is a very dangerous operation and should only be used for test * software. We may accidentally clear the pending state set by * peripherals with this read-modify-write operation. */ LEON3_IRQCTRL_ACQUIRE(&lock_context); ipend = grlib_load_32(®s->ipend); ipend |= bit; grlib_store_32(®s->ipend, ipend); LEON3_IRQCTRL_RELEASE(&lock_context); } return RTEMS_SUCCESSFUL; } #if defined(RTEMS_SMP) rtems_status_code bsp_interrupt_raise_on( rtems_vector_number vector, uint32_t cpu_index ) { irqamp *regs; bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector)); bsp_interrupt_assert(cpu_index < rtems_scheduler_get_processor_maximum()); if ( vector > BSP_INTERRUPT_VECTOR_MAX_STD ) { return RTEMS_UNSATISFIED; } regs = LEON3_IrqCtrl_Regs; grlib_store_32(®s->piforce[cpu_index], 1U << vector); return RTEMS_SUCCESSFUL; } #endif rtems_status_code bsp_interrupt_clear(rtems_vector_number vector) { uint32_t bit; irqamp *regs; bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector)); bit = 1U << vector; regs = LEON3_IrqCtrl_Regs; grlib_store_32(®s->iclear, bit); if (vector <= BSP_INTERRUPT_VECTOR_MAX_STD) { grlib_store_32(®s->piforce[rtems_scheduler_get_processor()], bit << 16); } return RTEMS_SUCCESSFUL; } rtems_status_code bsp_interrupt_vector_is_enabled( rtems_vector_number vector, bool *enabled ) { uint32_t bit; irqamp *regs; uint32_t pimask; bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector)); bit = 1U << vector; regs = LEON3_IrqCtrl_Regs; pimask = grlib_load_32(®s->pimask[_LEON3_Get_current_processor()]); *enabled = (pimask & bit) != 0; return RTEMS_SUCCESSFUL; } #if defined(RTEMS_SMP) static void leon3_interrupt_vector_enable(rtems_vector_number vector) { uint32_t cpu_index; uint32_t cpu_count; Processor_mask affinity; uint32_t bit; uint32_t unmasked; uint32_t brdcst; irqamp *regs; if (vector <= BSP_INTERRUPT_VECTOR_MAX_STD) { affinity = leon3_interrupt_affinities[vector]; } else { affinity = leon3_interrupt_affinities[LEON3_IrqCtrl_EIrq]; } cpu_count = rtems_scheduler_get_processor_maximum(); bit = 1U << vector; regs = LEON3_IrqCtrl_Regs; unmasked = 0; for (cpu_index = 0; cpu_index < cpu_count; ++cpu_index) { uint32_t pimask; pimask = grlib_load_32(®s->pimask[cpu_index]); if (_Processor_mask_Is_set(&affinity, cpu_index)) { ++unmasked; pimask |= bit; } else { pimask &= ~bit; } grlib_store_32(®s->pimask[cpu_index], pimask); } brdcst = grlib_load_32(®s->brdcst); if (unmasked > 1) { brdcst |= bit; } else { brdcst &= ~bit; } grlib_store_32(®s->brdcst, brdcst); } #endif rtems_status_code bsp_interrupt_vector_enable(rtems_vector_number vector) { rtems_interrupt_lock_context lock_context; #if !defined(RTEMS_SMP) uint32_t bit; irqamp *regs; uint32_t pimask; uint32_t cpu_index; #endif bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector)); #if !defined(RTEMS_SMP) bit = 1U << vector; regs = LEON3_IrqCtrl_Regs; #endif LEON3_IRQCTRL_ACQUIRE(&lock_context); #if defined(RTEMS_SMP) leon3_interrupt_vector_enable(vector); #else cpu_index = _LEON3_Get_current_processor(); pimask = grlib_load_32(®s->pimask[cpu_index]); pimask |= bit; grlib_store_32(®s->pimask[cpu_index], pimask); #endif LEON3_IRQCTRL_RELEASE(&lock_context); return RTEMS_SUCCESSFUL; } rtems_status_code bsp_interrupt_vector_disable(rtems_vector_number vector) { rtems_interrupt_lock_context lock_context; uint32_t bit; irqamp *regs; uint32_t pimask; uint32_t cpu_index; #if defined(RTEMS_SMP) uint32_t cpu_count; uint32_t brdcst; #endif bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector)); bit = 1U << vector; regs = LEON3_IrqCtrl_Regs; LEON3_IRQCTRL_ACQUIRE(&lock_context); #if defined(RTEMS_SMP) cpu_count = rtems_scheduler_get_processor_maximum(); for (cpu_index = 0; cpu_index < cpu_count; ++cpu_index) { pimask = grlib_load_32(®s->pimask[cpu_index]); pimask &= ~bit; grlib_store_32(®s->pimask[cpu_index], pimask); } brdcst = grlib_load_32(®s->brdcst); brdcst &= ~bit; grlib_store_32(®s->brdcst, brdcst); #else cpu_index = _LEON3_Get_current_processor(); pimask = grlib_load_32(®s->pimask[cpu_index]); pimask &= ~bit; grlib_store_32(®s->pimask[cpu_index], pimask); #endif LEON3_IRQCTRL_RELEASE(&lock_context); return RTEMS_SUCCESSFUL; } #if defined(RTEMS_SMP) rtems_status_code bsp_interrupt_set_affinity( rtems_vector_number vector, const Processor_mask *affinity ) { rtems_interrupt_lock_context lock_context; uint32_t cpu_count; uint32_t cpu_index; uint32_t bit; irqamp *regs; if (vector >= RTEMS_ARRAY_SIZE(leon3_interrupt_affinities)) { return RTEMS_UNSATISFIED; } cpu_count = rtems_scheduler_get_processor_maximum(); bit = 1U << vector; regs = LEON3_IrqCtrl_Regs; LEON3_IRQCTRL_ACQUIRE(&lock_context); leon3_interrupt_affinities[vector] = *affinity; /* * If the interrupt is enabled on at least one processor, then re-enable it * using the new affinity. */ for (cpu_index = 0; cpu_index < cpu_count; ++cpu_index) { if ((grlib_load_32(®s->pimask[cpu_index]) & bit) != 0) { leon3_interrupt_vector_enable(vector); break; } } LEON3_IRQCTRL_RELEASE(&lock_context); return RTEMS_SUCCESSFUL; } rtems_status_code bsp_interrupt_get_affinity( rtems_vector_number vector, Processor_mask *affinity ) { if (vector >= RTEMS_ARRAY_SIZE(leon3_interrupt_affinities)) { return RTEMS_UNSATISFIED; } *affinity = leon3_interrupt_affinities[vector]; return RTEMS_SUCCESSFUL; } #endif