You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
271 lines
5.7 KiB
271 lines
5.7 KiB
/* |
|
* Copyright (c) 2017 Oticon A/S |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
* |
|
* HW IRQ controller model |
|
*/ |
|
|
|
#include <stdint.h> |
|
#include <stdbool.h> |
|
#include "hw_models_top.h" |
|
#include "irq_ctrl.h" |
|
#include "irq_handler.h" |
|
#include <zephyr/arch/posix/arch.h> /* for find_lsb_set() */ |
|
#include "board_soc.h" |
|
#include "posix_soc.h" |
|
#include <zephyr/types.h> |
|
|
|
uint64_t irq_ctrl_timer = NEVER; |
|
|
|
|
|
static uint64_t irq_status; /* pending interrupts */ |
|
static uint64_t irq_premask; /* interrupts before the mask */ |
|
|
|
/* |
|
* Mask of which interrupts will actually cause the cpu to vector into its |
|
* irq handler |
|
* If an interrupt is masked in this way, it will be pending in the premask in |
|
* case it is enabled later before clearing it. |
|
* If the irq_mask enables and interrupt pending in irq_premask, it will cause |
|
* the controller to raise the interrupt immediately |
|
*/ |
|
static uint64_t irq_mask; |
|
|
|
/* |
|
* Interrupts lock/disable. When set, interrupts are registered |
|
* (in the irq_status) but do not awake the cpu. if when unlocked, |
|
* irq_status != 0 an interrupt will be raised immediately |
|
*/ |
|
static bool irqs_locked; |
|
static bool lock_ignore; /* For the hard fake IRQ, temporarily ignore lock */ |
|
|
|
static uint8_t irq_prio[N_IRQS]; /* Priority of each interrupt */ |
|
/* note that prio = 0 == highest, prio=255 == lowest */ |
|
|
|
static int currently_running_prio = 256; /* 255 is the lowest prio interrupt */ |
|
|
|
void hw_irq_ctrl_init(void) |
|
{ |
|
irq_mask = 0U; /* Let's assume all interrupts are disable at boot */ |
|
irq_premask = 0U; |
|
irqs_locked = false; |
|
lock_ignore = false; |
|
|
|
for (int i = 0 ; i < N_IRQS; i++) { |
|
irq_prio[i] = 255U; |
|
} |
|
} |
|
|
|
void hw_irq_ctrl_cleanup(void) |
|
{ |
|
/* Nothing to be done */ |
|
} |
|
|
|
void hw_irq_ctrl_set_cur_prio(int new) |
|
{ |
|
currently_running_prio = new; |
|
} |
|
|
|
int hw_irq_ctrl_get_cur_prio(void) |
|
{ |
|
return currently_running_prio; |
|
} |
|
|
|
void hw_irq_ctrl_prio_set(unsigned int irq, unsigned int prio) |
|
{ |
|
irq_prio[irq] = prio; |
|
} |
|
|
|
uint8_t hw_irq_ctrl_get_prio(unsigned int irq) |
|
{ |
|
return irq_prio[irq]; |
|
} |
|
|
|
/** |
|
* Get the currently pending highest priority interrupt which has a priority |
|
* higher than a possibly currently running interrupt |
|
* |
|
* If none, return -1 |
|
*/ |
|
int hw_irq_ctrl_get_highest_prio_irq(void) |
|
{ |
|
if (irqs_locked) { |
|
return -1; |
|
} |
|
|
|
uint64_t hw_irq_status = hw_irq_ctrl_get_irq_status(); |
|
int winner = -1; |
|
int winner_prio = 256; |
|
|
|
while (hw_irq_status != 0U) { |
|
int irq_nbr = find_lsb_set(hw_irq_status) - 1; |
|
|
|
hw_irq_status &= ~((uint64_t) 1 << irq_nbr); |
|
if ((winner_prio > (int)irq_prio[irq_nbr]) |
|
&& (currently_running_prio > (int)irq_prio[irq_nbr])) { |
|
winner = irq_nbr; |
|
winner_prio = irq_prio[irq_nbr]; |
|
} |
|
} |
|
return winner; |
|
} |
|
|
|
|
|
uint32_t hw_irq_ctrl_get_current_lock(void) |
|
{ |
|
return irqs_locked; |
|
} |
|
|
|
uint32_t hw_irq_ctrl_change_lock(uint32_t new_lock) |
|
{ |
|
uint32_t previous_lock = irqs_locked; |
|
|
|
irqs_locked = new_lock; |
|
|
|
if ((previous_lock == true) && (new_lock == false)) { |
|
if (irq_status != 0U) { |
|
posix_irq_handler_im_from_sw(); |
|
} |
|
} |
|
return previous_lock; |
|
} |
|
|
|
uint64_t hw_irq_ctrl_get_irq_status(void) |
|
{ |
|
return irq_status; |
|
} |
|
|
|
void hw_irq_ctrl_clear_all_enabled_irqs(void) |
|
{ |
|
irq_status = 0U; |
|
irq_premask &= ~irq_mask; |
|
} |
|
|
|
void hw_irq_ctrl_clear_all_irqs(void) |
|
{ |
|
irq_status = 0U; |
|
irq_premask = 0U; |
|
} |
|
|
|
void hw_irq_ctrl_disable_irq(unsigned int irq) |
|
{ |
|
irq_mask &= ~((uint64_t)1<<irq); |
|
} |
|
|
|
int hw_irq_ctrl_is_irq_enabled(unsigned int irq) |
|
{ |
|
return (irq_mask & ((uint64_t)1 << irq))?1:0; |
|
} |
|
|
|
uint64_t hw_irq_ctrl_get_irq_mask(void) |
|
{ |
|
return irq_mask; |
|
} |
|
|
|
void hw_irq_ctrl_clear_irq(unsigned int irq) |
|
{ |
|
irq_status &= ~((uint64_t)1<<irq); |
|
irq_premask &= ~((uint64_t)1<<irq); |
|
} |
|
|
|
|
|
/** |
|
* Enable an interrupt |
|
* |
|
* This function may only be called from SW threads |
|
* |
|
* If the enabled interrupt is pending, it will immediately vector to its |
|
* interrupt handler and continue (maybe with some swap() before) |
|
*/ |
|
void hw_irq_ctrl_enable_irq(unsigned int irq) |
|
{ |
|
irq_mask |= ((uint64_t)1<<irq); |
|
if (irq_premask & ((uint64_t)1<<irq)) { /* if IRQ is pending */ |
|
hw_irq_ctrl_raise_im_from_sw(irq); |
|
} |
|
} |
|
|
|
|
|
static inline void hw_irq_ctrl_irq_raise_prefix(unsigned int irq) |
|
{ |
|
if (irq < N_IRQS) { |
|
irq_premask |= ((uint64_t)1<<irq); |
|
|
|
if (irq_mask & (1 << irq)) { |
|
irq_status |= ((uint64_t)1<<irq); |
|
} |
|
} else if (irq == PHONY_HARD_IRQ) { |
|
lock_ignore = true; |
|
} |
|
} |
|
|
|
/** |
|
* Set/Raise an interrupt |
|
* |
|
* This function is meant to be used by either the SW manual IRQ raising |
|
* or by HW which wants the IRQ to be raised in one delta cycle from now |
|
*/ |
|
void hw_irq_ctrl_set_irq(unsigned int irq) |
|
{ |
|
hw_irq_ctrl_irq_raise_prefix(irq); |
|
if ((irqs_locked == false) || (lock_ignore)) { |
|
/* |
|
* Awake CPU in 1 delta |
|
* Note that we awake the CPU even if the IRQ is disabled |
|
* => we assume the CPU is always idling in a WFE() like |
|
* instruction and the CPU is allowed to awake just with the irq |
|
* being marked as pending |
|
*/ |
|
irq_ctrl_timer = hwm_get_time(); |
|
hwm_find_next_timer(); |
|
} |
|
} |
|
|
|
|
|
|
|
static void irq_raising_from_hw_now(void) |
|
{ |
|
/* |
|
* We always awake the CPU even if the IRQ was masked, |
|
* but not if irqs are locked unless this is due to a |
|
* PHONY_HARD_IRQ |
|
*/ |
|
if ((irqs_locked == false) || (lock_ignore)) { |
|
lock_ignore = false; |
|
posix_interrupt_raised(); |
|
} |
|
} |
|
|
|
/** |
|
* Set/Raise an interrupt immediately. |
|
* Like hw_irq_ctrl_set_irq() but awake immediately the CPU instead of in |
|
* 1 delta cycle |
|
* |
|
* Call only from HW threads |
|
*/ |
|
void hw_irq_ctrl_raise_im(unsigned int irq) |
|
{ |
|
hw_irq_ctrl_irq_raise_prefix(irq); |
|
irq_raising_from_hw_now(); |
|
} |
|
|
|
/** |
|
* Like hw_irq_ctrl_raise_im() but for SW threads |
|
* |
|
* Call only from SW threads |
|
*/ |
|
void hw_irq_ctrl_raise_im_from_sw(unsigned int irq) |
|
{ |
|
hw_irq_ctrl_irq_raise_prefix(irq); |
|
|
|
if (irqs_locked == false) { |
|
posix_irq_handler_im_from_sw(); |
|
} |
|
} |
|
|
|
void hw_irq_ctrl_timer_triggered(void) |
|
{ |
|
irq_ctrl_timer = NEVER; |
|
irq_raising_from_hw_now(); |
|
}
|
|
|