273 lines
5.6 KiB
C
273 lines
5.6 KiB
C
/*
|
|
* Copyright (c) 2017 Oticon A/S
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* HW IRQ controller model
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include "hw_models_top.h"
|
|
#include "irq_ctrl.h"
|
|
#include "irq_handler.h"
|
|
#include "arch/posix/arch.h" /* for find_lsb_set() */
|
|
#include "board_soc.h"
|
|
#include "posix_soc.h"
|
|
#include "zephyr/types.h"
|
|
|
|
u64_t irq_ctrl_timer = NEVER;
|
|
|
|
|
|
static u64_t irq_status; /* pending interrupts */
|
|
static u64_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 u64_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 u8_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 = 0; /* Let's assume all interrupts are disable at boot */
|
|
irq_premask = 0;
|
|
irqs_locked = false;
|
|
lock_ignore = false;
|
|
|
|
for (int i = 0 ; i < N_IRQS; i++) {
|
|
irq_prio[i] = 255;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
u8_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;
|
|
}
|
|
|
|
u64_t irq_status = hw_irq_ctrl_get_irq_status();
|
|
int winner = -1;
|
|
int winner_prio = 256;
|
|
|
|
while (irq_status != 0) {
|
|
int irq_nbr = find_lsb_set(irq_status) - 1;
|
|
|
|
irq_status &= ~((u64_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;
|
|
}
|
|
|
|
|
|
u32_t hw_irq_ctrl_get_current_lock(void)
|
|
{
|
|
return irqs_locked;
|
|
}
|
|
|
|
u32_t hw_irq_ctrl_change_lock(u32_t new_lock)
|
|
{
|
|
u32_t previous_lock = irqs_locked;
|
|
|
|
irqs_locked = new_lock;
|
|
|
|
if ((previous_lock == true) && (new_lock == false)) {
|
|
if (irq_status != 0) {
|
|
posix_irq_handler_im_from_sw();
|
|
}
|
|
}
|
|
return previous_lock;
|
|
}
|
|
|
|
u64_t hw_irq_ctrl_get_irq_status(void)
|
|
{
|
|
return irq_status;
|
|
}
|
|
|
|
void hw_irq_ctrl_clear_all_enabled_irqs(void)
|
|
{
|
|
irq_status = 0;
|
|
irq_premask &= ~irq_mask;
|
|
}
|
|
|
|
void hw_irq_ctrl_clear_all_irqs(void)
|
|
{
|
|
irq_status = 0;
|
|
irq_premask = 0;
|
|
}
|
|
|
|
void hw_irq_ctrl_disable_irq(unsigned int irq)
|
|
{
|
|
irq_mask &= ~((u64_t)1<<irq);
|
|
}
|
|
|
|
int hw_irq_ctrl_is_irq_enabled(unsigned int irq)
|
|
{
|
|
return (irq_mask & ((u64_t)1 << irq))?1:0;
|
|
}
|
|
|
|
u64_t hw_irq_ctrl_get_irq_mask(void)
|
|
{
|
|
return irq_mask;
|
|
}
|
|
|
|
void hw_irq_ctrl_clear_irq(unsigned int irq)
|
|
{
|
|
irq_status &= ~((u64_t)1<<irq);
|
|
irq_premask &= ~((u64_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 |= ((u64_t)1<<irq);
|
|
if (irq_premask & ((u64_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 |= ((u64_t)1<<irq);
|
|
|
|
if (irq_mask & (1 << irq)) {
|
|
irq_status |= ((u64_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 inmediately.
|
|
* 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();
|
|
}
|
|
|
|
|