731 lines
20 KiB
C
731 lines
20 KiB
C
/*
|
|
* sd_vic.c
|
|
*
|
|
* Copyright (c) 2020 Semidrive Semiconductor.
|
|
* All rights reserved.
|
|
*
|
|
* Description: SemiDrive VIC driver.
|
|
*
|
|
* Revision History:
|
|
* -----------------
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "compiler.h"
|
|
#include "param.h"
|
|
#include "reg.h"
|
|
#include "armv7-r/cache.h"
|
|
#include "armv7-r/irq.h"
|
|
#include "bits.h"
|
|
#include "core_id.h"
|
|
#include "common.h"
|
|
#include "part.h"
|
|
#include "sdrv_vic.h"
|
|
|
|
#define OFFSET32(v) (((v) >> 5) << 2)
|
|
#define SHIFT32(v) ((v) & 31U)
|
|
|
|
#define VICINTENABLE(v) (g_vic_data[core_id].base + 0x100U + OFFSET32(v))
|
|
#define VICINTENCLEAR(v) (g_vic_data[core_id].base + 0x1C0U + OFFSET32(v))
|
|
#define VICSWMASK(v) (g_vic_data[core_id].base + 0x180U + OFFSET32(v))
|
|
#define VICVECTPRIORITY(v) (g_vic_data[core_id].base + 0x400U + ((v) << 2))
|
|
#define VIC_WDT_EN (g_vic_data[core_id].base + 0x380U)
|
|
#define VIC_WDT_TH (g_vic_data[core_id].base + 0x3C0U)
|
|
#define VIC_ERRINT_CLR (g_vic_data[core_id].base + 0x3CCU)
|
|
#define VIC_SFINT_MASK (g_vic_data[core_id].base + 0x1F00U)
|
|
#define VICVECADDR(v) (g_vic_data[core_id].base + 0xF000U + ((v) << 2))
|
|
#define VICADDRESS (g_vic_data[core_id].base + 0xF00U)
|
|
#define VICINTSELECT(v) (g_vic_data[core_id].base + 0xC0U + OFFSET32(v))
|
|
#define VICSOFTINT(v) (g_vic_data[core_id].base + 0x140U + OFFSET32(v))
|
|
#define VICSOFTINTCLEAR(v) (g_vic_data[core_id].base + 0x200U + OFFSET32(v))
|
|
#define VICFIQSTATUS(v) (g_vic_data[core_id].base + 0x40U + OFFSET32(v))
|
|
#define VICSWPRIORITYMASK (g_vic_data[core_id].base + 0x3C8U)
|
|
#define VICSWMASK_REG(n) (g_vic_data[core_id].base + 0x180U + ((n) << 2))
|
|
|
|
#define MAX_PRI 15
|
|
|
|
#define SWMASK_REG_NUM 8
|
|
|
|
/* Timeout configuration */
|
|
#define WDT_EN 0U
|
|
#define WDT_DIV 1U
|
|
#define WDT_TH 0x80U
|
|
#define MAKE_WDT_TH(en, div, th) (((en) << 31) | (((div) & 0x3FFU) << 16) | \
|
|
((th) & 0xFFU))
|
|
|
|
struct vic_data {
|
|
uint32_t base;
|
|
uint32_t int_num;
|
|
uint32_t cur_rp;
|
|
uint32_t irq_nest_cnt;
|
|
/* Current masked priority set by user or interrrupt framework. */
|
|
uint32_t pri_masked;
|
|
|
|
#if !CONFIG_VIC_IRQ_INTERRUPT_MODE
|
|
#ifndef CONFIG_VIC_INT_NEST_MAX_CNT
|
|
#define CONFIG_VIC_INT_NEST_MAX_CNT 16
|
|
#endif
|
|
|
|
struct stack {
|
|
uint32_t buf[CONFIG_VIC_INT_NEST_MAX_CNT];
|
|
uint32_t head;
|
|
} old_pri;
|
|
#endif
|
|
|
|
/* The following members are only needed on E3 v1.0. */
|
|
struct sw_mask {
|
|
/* Bitmap that record registered interrupt of each priority
|
|
* and each sw mask register value.
|
|
*/
|
|
struct pri_mask {
|
|
/* Is each priority masked?
|
|
* Each priority is arranged linealy to get higher access performance.
|
|
*/
|
|
bool masked[MAX_PRI + 1] __CACHE_ALIGN;
|
|
/* Registered interrupt of each priority. */
|
|
unsigned long int_mask[MAX_PRI + 1][SWMASK_REG_NUM] __CACHE_ALIGN;
|
|
/* Registerd interrupt which has a equal or lower priority than
|
|
* each given priority specified by array index.
|
|
*/
|
|
unsigned long low_pri_mask[MAX_PRI + 1][SWMASK_REG_NUM] __CACHE_ALIGN;
|
|
} pmsk;
|
|
uint32_t cur_sw_mask[SWMASK_REG_NUM] __CACHE_ALIGN;
|
|
} swmsk;
|
|
|
|
/* Current masked priority set by user. */
|
|
uint32_t cur_swmask;
|
|
/* Previous masked priority by user. */
|
|
uint32_t previous_masked_pri;
|
|
} __CACHE_ALIGN;
|
|
|
|
static struct vic_data g_vic_data[CORE_NUM_SMP];
|
|
|
|
static uint8_t get_minor_chip_id(void)
|
|
{
|
|
#if CONFIG_E3L
|
|
return 1;
|
|
#else
|
|
static uint8_t minor_id = 0xFF;
|
|
|
|
if (minor_id == 0xFF)
|
|
minor_id = sdrv_fuse_get_minor_chipid();
|
|
return minor_id;
|
|
#endif
|
|
}
|
|
|
|
int int_nest_errata_enabled(void)
|
|
{
|
|
uint8_t minor_id = get_minor_chip_id();
|
|
|
|
/* Maybe other version number needed, if so add it here. */
|
|
if ((minor_id == 0) || (minor_id == 1)) {
|
|
#if CONFIG_VIC_INT_NEST_ERRATA
|
|
if (minor_id == 0)
|
|
return INT_NEST_ERRATA_TYPE_0;
|
|
else {
|
|
#if CONFIG_VIC_IRQ_INTERRUPT_MODE
|
|
return INT_NEST_ERRATA_TYPE_1;
|
|
#else
|
|
return INT_NEST_ERRATA_TYPE_2;
|
|
#endif
|
|
}
|
|
#else
|
|
return NO_INT_NEST_ERRATA;
|
|
#endif
|
|
}
|
|
else
|
|
return NO_INT_NEST_ERRATA;
|
|
}
|
|
|
|
static inline void sdrv_vic_lld_int_disable_all(void)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
uint32_t int_num = g_vic_data[core_id].int_num;
|
|
|
|
for (uint32_t i = 0U; i < int_num; i += 32U) {
|
|
writel(0xFFFFFFFFU, VICINTENCLEAR(i));
|
|
}
|
|
}
|
|
|
|
static void sdrv_vic_lld_func_safety_en(void)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
/* Enable timeout dection. */
|
|
writel(MAKE_WDT_TH(WDT_EN, WDT_DIV, WDT_TH), VIC_WDT_TH);
|
|
writel(WDT_EN, VIC_WDT_EN);
|
|
|
|
/* Unmask error interrupts. */
|
|
writel(0U, VIC_SFINT_MASK);
|
|
}
|
|
|
|
static void sdrv_vic_lld_all_ints_trigger_irq(void)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
uint32_t int_num = g_vic_data[core_id].int_num;
|
|
|
|
for (uint32_t i = 0U; i < int_num; i += 32U) {
|
|
writel(0U, VICINTSELECT(i));
|
|
}
|
|
}
|
|
|
|
static void sdrv_vic_lld_set_vector(void)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
uint32_t int_num = g_vic_data[core_id].int_num;
|
|
|
|
#if CONFIG_VIC_IRQ_INTERRUPT_MODE
|
|
extern uint8_t vectored_irq0_handler, vectored_irq1_handler;
|
|
|
|
uint32_t vector_start_addr = (uint32_t)&vectored_irq0_handler;
|
|
uint32_t each_vector_size = (uint32_t)(&vectored_irq1_handler -
|
|
&vectored_irq0_handler);
|
|
|
|
for (uint32_t i = 0U; i < int_num; i++) {
|
|
writel(vector_start_addr + i * each_vector_size,
|
|
VICVECADDR(i));
|
|
}
|
|
#else
|
|
for (uint32_t i = 0U; i < int_num; i++) {
|
|
writel(i, VICVECADDR(i));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if CONFIG_VIC_IRQ_INTERRUPT_MODE
|
|
static int sdrv_vic_dummy_isr(uint32_t vector)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/* IRQ handler may be allowed not to use per cpu variable?
|
|
* Because this variable value is the same on all cores.
|
|
*/
|
|
int (*g_vic_user_isr)(uint32_t vector) = sdrv_vic_dummy_isr;
|
|
|
|
static inline void sdrv_vic_lld_set_user_isr(int (*isr)(uint32_t))
|
|
{
|
|
if (isr) {
|
|
g_vic_user_isr = isr;
|
|
}
|
|
}
|
|
#else
|
|
static inline void sdrv_vic_inc_int_nest_cnt(void)
|
|
{
|
|
g_vic_data[get_core_id_smp()].irq_nest_cnt++;
|
|
}
|
|
|
|
static inline int sdrv_vic_dec_int_nest_cnt(void)
|
|
{
|
|
return (--g_vic_data[get_core_id_smp()].irq_nest_cnt);
|
|
}
|
|
#endif
|
|
|
|
static inline void sdrv_vic_lld_clr_int_nest_cnt(void)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
g_vic_data[core_id].irq_nest_cnt = 0U;
|
|
}
|
|
|
|
static void sdrv_vic_lld_unmask_interrupt(uint32_t vector)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
RMWREG32(VICSWMASK(vector), SHIFT32(vector), 1U, 0U);
|
|
}
|
|
|
|
/**
|
|
* @brief Mask specified interrupt
|
|
* @param vector priority to be masked
|
|
*/
|
|
static void sdrv_vic_lld_mask_interrupt(uint32_t vector)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
RMWREG32(VICSWMASK(vector), SHIFT32(vector), 1U, 1U);
|
|
}
|
|
|
|
/**
|
|
* @brief Mask all low priority level
|
|
* @param [in] pri_threshold interrupts with priority equals
|
|
* to or lower than pri_threshold are masked.
|
|
* @param [in] user if this api called by user or interrupt
|
|
* framework.
|
|
* @return Previous masked priority value
|
|
*/
|
|
uint32_t sdrv_vic_lld_mask_low_priority(uint32_t pri_threshold, bool user)
|
|
{
|
|
uint32_t ret;
|
|
int core_id = get_core_id_smp();
|
|
struct vic_data *vicdata = &g_vic_data[core_id];
|
|
|
|
irq_state_t state = arch_irq_save();
|
|
ret = vicdata->previous_masked_pri;
|
|
pri_threshold = MIN(pri_threshold, MAX_PRI + 1);
|
|
|
|
if (user)
|
|
vicdata->previous_masked_pri = pri_threshold;
|
|
|
|
if (NO_INT_NEST_ERRATA != int_nest_errata_enabled()) {
|
|
/* If called by user, update current masked priority. */
|
|
if (user)
|
|
vicdata->cur_swmask = pri_threshold;
|
|
|
|
/* When user sets priority mask, always no unmasking any
|
|
* priority masked by interrupt framework.
|
|
*/
|
|
if (user && (vicdata->cur_rp < pri_threshold))
|
|
pri_threshold = vicdata->cur_rp;
|
|
/* When interrupt framework sets priority mask, always no
|
|
* unmasking any priority masked by user.
|
|
*/
|
|
else if (!user && (vicdata->cur_swmask < pri_threshold))
|
|
pri_threshold = vicdata->cur_swmask;
|
|
}
|
|
|
|
if (0 == get_minor_chip_id()) {
|
|
uint32_t swmask[SWMASK_REG_NUM] = {0};
|
|
struct pri_mask *pmsk = &vicdata->swmsk.pmsk;
|
|
uint32_t *curmask = vicdata->swmsk.cur_sw_mask;
|
|
|
|
/* Larger priority value, lower priority. */
|
|
for (int i = 0; i <= MAX_PRI; i++) {
|
|
/* Update mask value of each priroity. */
|
|
if (i < pri_threshold)
|
|
pmsk->masked[i] = false;
|
|
else
|
|
pmsk->masked[i] = true;
|
|
}
|
|
if (pri_threshold <= MAX_PRI) {
|
|
/* Update swmask register value to low pri masked bitmap. */
|
|
memcpy(swmask, pmsk->low_pri_mask[pri_threshold], sizeof(swmask));
|
|
}
|
|
|
|
for (int i = 0; i < SWMASK_REG_NUM; i++) {
|
|
if (curmask[i] != swmask[i]) {
|
|
writel(swmask[i], VICSWMASK_REG(i));
|
|
curmask[i] = swmask[i];
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
uint16_t temp = 0xFFFF;
|
|
temp = temp >> (16 - pri_threshold);
|
|
writel(temp, VICSWPRIORITYMASK);
|
|
}
|
|
|
|
vicdata->pri_masked = pri_threshold;
|
|
arch_irq_restore(state);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Unmask all interrupt
|
|
*/
|
|
void sdrv_vic_lld_unmask_all_interrupt(void)
|
|
{
|
|
if (1 == get_minor_chip_id()) {
|
|
int core_id = get_core_id_smp();
|
|
irq_state_t state = arch_irq_save();
|
|
|
|
for (int i = 0; i < SWMASK_REG_NUM; i++)
|
|
writel(0, VICSWMASK_REG(i));
|
|
|
|
arch_irq_restore(state);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Unmask all priority
|
|
*/
|
|
void sdrv_vic_lld_unmask_all_priority(void)
|
|
{
|
|
sdrv_vic_lld_mask_low_priority(0xFF, true);
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize VIC
|
|
* @param isr user ISR
|
|
*/
|
|
void sdrv_vic_lld_init(uint32_t base, uint32_t intr_num, int (*isr)(uint32_t))
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
|
|
g_vic_data[core_id].base = base;
|
|
g_vic_data[core_id].int_num = intr_num;
|
|
g_vic_data[core_id].cur_rp = 0xFF;
|
|
g_vic_data[core_id].previous_masked_pri = ~0;
|
|
if (0 == get_minor_chip_id())
|
|
memset(g_vic_data[core_id].swmsk.cur_sw_mask, 0xFF,
|
|
sizeof(g_vic_data[core_id].swmsk.cur_sw_mask));
|
|
|
|
sdrv_vic_lld_int_disable_all();
|
|
sdrv_vic_lld_all_ints_trigger_irq();
|
|
sdrv_vic_lld_unmask_all_interrupt();
|
|
sdrv_vic_lld_unmask_all_priority();
|
|
|
|
sdrv_vic_lld_func_safety_en();
|
|
|
|
sdrv_vic_lld_set_vector();
|
|
|
|
#if CONFIG_VIC_IRQ_INTERRUPT_MODE
|
|
sdrv_vic_lld_set_user_isr(isr);
|
|
arch_vectored_irq_enable(1U);
|
|
#else
|
|
arch_vectored_irq_enable(0U);
|
|
#endif
|
|
|
|
sdrv_vic_lld_clr_int_nest_cnt();
|
|
}
|
|
|
|
static inline void
|
|
sdrv_vic_lld_clear_int_mask(uint32_t vector)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
struct sw_mask *swmsk = &g_vic_data[core_id].swmsk;
|
|
|
|
for (int i = 0; i <= MAX_PRI; i++) {
|
|
unsigned long *intmask = swmsk->pmsk.int_mask[i];
|
|
unsigned long *low_pir_mask = swmsk->pmsk.low_pri_mask[i];
|
|
if (bitmap_test(intmask, vector))
|
|
bitmap_clear(intmask, vector);
|
|
if (bitmap_test(low_pir_mask, vector))
|
|
bitmap_clear(low_pir_mask, vector);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Set priority
|
|
*/
|
|
void sdrv_vic_lld_set_priority(uint32_t vector,
|
|
uint32_t pri)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
irq_state_t state = arch_irq_save();
|
|
writel(pri & 15U, VICVECTPRIORITY(vector));
|
|
|
|
if (0 == get_minor_chip_id()) {
|
|
struct pri_mask *pmsk = &g_vic_data[core_id].swmsk.pmsk;
|
|
unsigned long *intmask = pmsk->int_mask[pri];
|
|
|
|
/* In case of modifying priority dynamically, clear
|
|
* previous int bitmap first.
|
|
*/
|
|
sdrv_vic_lld_clear_int_mask(vector);
|
|
bitmap_set(intmask, vector);
|
|
/* If masking a equal or higher priority, this
|
|
* vector should be masked. So, set this vector
|
|
* in each low pir mask bitmap with a equal or
|
|
* higher priority.
|
|
*/
|
|
for (int i = 0; i <= pri; i++) {
|
|
bitmap_set(pmsk->low_pri_mask[i], vector);
|
|
}
|
|
|
|
if (pmsk->masked[pri])
|
|
sdrv_vic_lld_mask_interrupt(vector);
|
|
else
|
|
sdrv_vic_lld_unmask_interrupt(vector);
|
|
}
|
|
arch_irq_restore(state);
|
|
}
|
|
|
|
/**
|
|
* @brief Enable interrupt
|
|
* @param [in] vector interrupt vector number
|
|
*/
|
|
void sdrv_vic_lld_int_enable(uint32_t vector)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
irq_state_t state = arch_irq_save();
|
|
RMWREG32(VICINTENABLE(vector), SHIFT32(vector), 1U, 1U);
|
|
arch_irq_restore(state);
|
|
}
|
|
|
|
/**
|
|
* @brief Disable interrupt
|
|
* @param [in] vector interrupt vector number
|
|
*/
|
|
void sdrv_vic_lld_int_disable(uint32_t vector)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
irq_state_t state = arch_irq_save();
|
|
RMWREG32(VICINTENCLEAR(vector), SHIFT32(vector), 1U, 1U);
|
|
arch_irq_restore(state);
|
|
}
|
|
|
|
/**
|
|
* @brief Get priority
|
|
* @param [in] vector interrupt vector number
|
|
* @return interrupt priority
|
|
*/
|
|
uint32_t sdrv_vic_lld_get_priority(uint32_t vector)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
return readl(VICVECTPRIORITY(vector)) & 15U;
|
|
}
|
|
|
|
/**
|
|
* @brief Get current running priority
|
|
* @return uint32_t current running priority
|
|
*/
|
|
uint32_t sdrv_vic_lld_current_running_priority(void)
|
|
{
|
|
return g_vic_data[get_core_id_smp()].cur_rp;
|
|
}
|
|
|
|
/**
|
|
* @brief Set one vector to trigger FIQ requeset
|
|
* @param [in] vector vector number
|
|
*/
|
|
void sdrv_vic_lld_set_fiq_req_src(uint32_t vector)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
uint32_t int_num = g_vic_data[core_id].int_num;
|
|
irq_state_t state = arch_irq_save();
|
|
|
|
/* The system should have at most one FIQ source. */
|
|
for (uint32_t i = 0U; i < int_num; i += 32U) {
|
|
if ((i >> 5) == (vector >> 5)) {
|
|
writel(1U << SHIFT32(vector), VICINTSELECT(vector));
|
|
}
|
|
else {
|
|
writel(0U, VICINTSELECT(i));
|
|
}
|
|
}
|
|
|
|
arch_irq_restore(state);
|
|
}
|
|
|
|
static inline int local_fls(int x)
|
|
{
|
|
int r = 32;
|
|
|
|
if (!x)
|
|
return 0;
|
|
if (!(x & 0xffff0000u)) {
|
|
x <<= 16;
|
|
r -= 16;
|
|
}
|
|
if (!(x & 0xff000000u)) {
|
|
x <<= 8;
|
|
r -= 8;
|
|
}
|
|
if (!(x & 0xf0000000u)) {
|
|
x <<= 4;
|
|
r -= 4;
|
|
}
|
|
if (!(x & 0xc0000000u)) {
|
|
x <<= 2;
|
|
r -= 2;
|
|
}
|
|
if (!(x & 0x80000000u)) {
|
|
x <<= 1;
|
|
r -= 1;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* @brief Get current serviced fiq vector number
|
|
* @return uint32_t vector number
|
|
*/
|
|
uint32_t sdrv_vic_lld_get_active_fiq_src(void)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
uint32_t int_num = g_vic_data[core_id].int_num;
|
|
uint32_t i = 0U;
|
|
uint32_t fiq_status = 0U;
|
|
uint32_t ret = ~0;
|
|
|
|
irq_state_t state = arch_irq_save();
|
|
|
|
for (i = 0U; i < int_num; i += 32U) {
|
|
fiq_status = readl(VICFIQSTATUS(i));
|
|
if (fiq_status) {
|
|
ret = i + local_fls(fiq_status) - 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
arch_irq_restore(state);
|
|
return ret;
|
|
}
|
|
|
|
#if !CONFIG_VIC_IRQ_INTERRUPT_MODE
|
|
/**
|
|
* No lock needed, because the folling function
|
|
* is always called in IRQ disabled interrupt context.
|
|
*/
|
|
static void sdrv_vic_push_stack(struct stack *s, uint32_t val)
|
|
{
|
|
uint32_t head = s->head;
|
|
|
|
s->buf[head] = val;
|
|
s->head = head + 1;
|
|
}
|
|
|
|
static uint32_t sdrv_vic_pop_stack(struct stack *s)
|
|
{
|
|
uint32_t top = s->head - 1;
|
|
uint32_t ret = s->buf[top];
|
|
s->head = top;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline void sdrv_vic_push_old_pri(void)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
struct stack *old_pri = &g_vic_data[core_id].old_pri;
|
|
|
|
sdrv_vic_push_stack(old_pri, g_vic_data[core_id].cur_rp);
|
|
}
|
|
|
|
static inline void sdrv_vic_pop_old_pri(void)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
struct stack *old_pri = &g_vic_data[core_id].old_pri;
|
|
g_vic_data[core_id].cur_rp = sdrv_vic_pop_stack(old_pri);
|
|
}
|
|
#endif
|
|
|
|
void sdrv_vic_lld_ack_slow_path(int errata, uint32_t running_pri)
|
|
{
|
|
/* Mask all interrupt with the same and lower priority
|
|
* to prevent:
|
|
* 1) same or lower priority interrupt preempting after
|
|
* writing VICADDRESS
|
|
* 2) same sw priority but higher hw priority interrupt
|
|
* preempting
|
|
*/
|
|
sdrv_vic_lld_mask_low_priority(running_pri, false);
|
|
|
|
/* Type0 (v1.0) & Type2 (v1.1 non-vectored irq) should wr isr to
|
|
* clear hwmask, in order to avoid mismatch between hwmask and
|
|
* vicaddress.
|
|
*/
|
|
if ((errata == INT_NEST_ERRATA_TYPE_0) ||
|
|
(errata == INT_NEST_ERRATA_TYPE_2)) {
|
|
int core_id = get_core_id_smp();
|
|
if (errata == INT_NEST_ERRATA_TYPE_0)
|
|
/* Because irq_b given by vic can only be cleared by CPU
|
|
* acknowledge to vic, so if vic outputs another valid
|
|
* irq_b signal triggered by interrupt with the same sw
|
|
* priority but lower interrupt number before masking it,
|
|
* CPU will take it after IRQ re-enabled.
|
|
* To avoid this, here we should read vicaddress register
|
|
* first to clear the additional irq_b signal, and then
|
|
* writing vicaddress register to clear hwmask to make all
|
|
* interrupts, including this additional interrupt, can be
|
|
* sent to CPU at the right time.
|
|
*/
|
|
(void)readl(VICADDRESS);
|
|
/* A dummy write to VICADDRESS
|
|
* finish the current interrupt.
|
|
*/
|
|
writel(0, VICADDRESS);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Interrupt acknowledge
|
|
* @return uint32_t interrupt number
|
|
*/
|
|
uint32_t sdrv_vic_lld_ack(void)
|
|
{
|
|
#if !CONFIG_VIC_IRQ_INTERRUPT_MODE
|
|
int core_id = get_core_id_smp();
|
|
if (!arch_in_fiq_mode()) {
|
|
sdrv_vic_inc_int_nest_cnt();
|
|
|
|
uint32_t vector_num = readl(VICADDRESS);
|
|
|
|
/* Save previous running priority. */
|
|
sdrv_vic_push_old_pri();
|
|
/* Update current running priority. */
|
|
uint32_t cur_rp = sdrv_vic_lld_get_priority(vector_num);
|
|
g_vic_data[core_id].cur_rp = cur_rp;
|
|
/* Save priority mask before this interrupt. */
|
|
uint32_t pri_mask = g_vic_data[core_id].pri_masked;
|
|
|
|
int errata = int_nest_errata_enabled();
|
|
if (errata != NO_INT_NEST_ERRATA)
|
|
sdrv_vic_lld_ack_slow_path(errata, cur_rp);
|
|
|
|
/* Make sure only unmasked interrupt can be taken. */
|
|
if (cur_rp >= pri_mask)
|
|
return g_vic_data[core_id].int_num;
|
|
|
|
#if CONFIG_VIC_INT_NEST_AUTO_ENABLE
|
|
if (g_vic_data[core_id].irq_nest_cnt < CONFIG_VIC_INT_NEST_MAX_CNT)
|
|
arch_irq_enable();
|
|
#endif
|
|
|
|
return vector_num;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
return sdrv_vic_lld_get_active_fiq_src();
|
|
}
|
|
}
|
|
|
|
#if !CONFIG_VIC_IRQ_INTERRUPT_MODE
|
|
/**
|
|
* @brief Interrupt completed
|
|
*/
|
|
void sdrv_vic_lld_eoi(uint32_t vector)
|
|
{
|
|
/* No need to write vicaddress register
|
|
* in FIQ service routine.
|
|
*/
|
|
if (!arch_in_fiq_mode()) {
|
|
int core_id = get_core_id_smp();
|
|
arch_irq_disable();
|
|
int errta = int_nest_errata_enabled();
|
|
if (errta == NO_INT_NEST_ERRATA)
|
|
/* A dummy write to VICADDRESS
|
|
* finish the current interrupt.
|
|
*/
|
|
writel(vector, VICADDRESS);
|
|
|
|
sdrv_vic_dec_int_nest_cnt();
|
|
/* Restore previous running priority. */
|
|
sdrv_vic_pop_old_pri();
|
|
|
|
if (errta != NO_INT_NEST_ERRATA)
|
|
/* Unmask priority to previous state. */
|
|
sdrv_vic_lld_mask_low_priority(g_vic_data[core_id].cur_rp,
|
|
false);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief Trigger software interrupt
|
|
* @param [in] vector interrupt number
|
|
*/
|
|
void sdrv_vic_lld_trigger_soft_int(uint32_t vector)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
irq_state_t state = arch_irq_save();
|
|
writel(1U << SHIFT32(vector), VICSOFTINT(vector));
|
|
arch_irq_restore(state);
|
|
}
|
|
|
|
/**
|
|
* @brief Clear software interrupt
|
|
* @param [in] vector interrupt number
|
|
*/
|
|
void sdrv_vic_lld_clear_soft_int(uint32_t vector)
|
|
{
|
|
int core_id = get_core_id_smp();
|
|
irq_state_t state = arch_irq_save();
|
|
writel(1U << SHIFT32(vector), VICSOFTINTCLEAR(vector));
|
|
arch_irq_restore(state);
|
|
}
|
|
|
|
void *get_vic_data_base(void)
|
|
{
|
|
return &g_vic_data[get_core_id_smp()];
|
|
}
|