The init infrastructure, found in `init.h`, is currently used by:
- `SYS_INIT`: to call functions before `main`
- `DEVICE_*`: to initialize devices
They are all sorted according to an initialization level + a priority.
`SYS_INIT` calls are really orthogonal to devices, however, the required
function signature requires a `const struct device *dev` as a first
argument. The only reason for that is because the same init machinery is
used by devices, so we have something like:
```c
struct init_entry {
int (*init)(const struct device *dev);
/* only set by DEVICE_*, otherwise NULL */
const struct device *dev;
}
```
As a result, we end up with such weird/ugly pattern:
```c
static int my_init(const struct device *dev)
{
/* always NULL! add ARG_UNUSED to avoid compiler warning */
ARG_UNUSED(dev);
...
}
```
This is really a result of poor internals isolation. This patch proposes
a to make init entries more flexible so that they can accept sytem
initialization calls like this:
```c
static int my_init(void)
{
...
}
```
This is achieved using a union:
```c
union init_function {
/* for SYS_INIT, used when init_entry.dev == NULL */
int (*sys)(void);
/* for DEVICE*, used when init_entry.dev != NULL */
int (*dev)(const struct device *dev);
};
struct init_entry {
/* stores init function (either for SYS_INIT or DEVICE*)
union init_function init_fn;
/* stores device pointer for DEVICE*, NULL for SYS_INIT. Allows
* to know which union entry to call.
*/
const struct device *dev;
}
```
This solution **does not increase ROM usage**, and allows to offer clean
public APIs for both SYS_INIT and DEVICE*. Note that however, init
machinery keeps a coupling with devices.
**NOTE**: This is a breaking change! All `SYS_INIT` functions will need
to be converted to the new signature. See the script offered in the
following commit.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
init: convert SYS_INIT functions to the new signature
Conversion scripted using scripts/utils/migrate_sys_init.py.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
manifest: update projects for SYS_INIT changes
Update modules with updated SYS_INIT calls:
- hal_ti
- lvgl
- sof
- TraceRecorderSource
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
tests: devicetree: devices: adjust test
Adjust test according to the recently introduced SYS_INIT
infrastructure.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
tests: kernel: threads: adjust SYS_INIT call
Adjust to the new signature: int (*init_fn)(void);
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
186 lines
4.1 KiB
C
186 lines
4.1 KiB
C
/*
|
|
* Copyright (c) 2021 Tokita, Hiroshi <tokita.hiroshi@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @brief Driver for Nuclie's Extended Core Interrupt Controller
|
|
*/
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/arch/cpu.h>
|
|
#include <zephyr/sys/util.h>
|
|
#include <zephyr/init.h>
|
|
#include <soc.h>
|
|
|
|
#include <zephyr/sw_isr_table.h>
|
|
#include <zephyr/drivers/interrupt_controller/riscv_clic.h>
|
|
|
|
union CLICCFG {
|
|
struct {
|
|
uint8_t _reserved0 : 1;
|
|
/** number of interrupt level bits */
|
|
uint8_t nlbits : 4;
|
|
uint8_t _reserved1 : 2;
|
|
uint8_t _reserved2 : 1;
|
|
} b;
|
|
uint8_t w;
|
|
};
|
|
|
|
union CLICINFO {
|
|
struct {
|
|
/** number of max supported interrupts */
|
|
uint32_t numint : 13;
|
|
/** architecture version */
|
|
uint32_t version : 8;
|
|
/** supported bits in the clicintctl */
|
|
uint32_t intctlbits : 4;
|
|
uint32_t _reserved0 : 7;
|
|
} b;
|
|
uint32_t qw;
|
|
};
|
|
|
|
union CLICMTH {
|
|
uint8_t w;
|
|
};
|
|
|
|
union CLICINTIP {
|
|
struct {
|
|
/** Interrupt Pending */
|
|
uint8_t IP : 1;
|
|
uint8_t reserved0 : 7;
|
|
} b;
|
|
uint8_t w;
|
|
};
|
|
|
|
union CLICINTIE {
|
|
struct {
|
|
/** Interrupt Enabled */
|
|
uint8_t IE : 1;
|
|
uint8_t reserved0 : 7;
|
|
} b;
|
|
uint8_t w;
|
|
};
|
|
|
|
union CLICINTATTR {
|
|
struct {
|
|
/** 0: non-vectored 1:vectored */
|
|
uint8_t shv : 1;
|
|
/** 0: level 1: rising edge 2: falling edge */
|
|
uint8_t trg : 2;
|
|
uint8_t reserved0 : 3;
|
|
uint8_t reserved1 : 2;
|
|
} b;
|
|
uint8_t w;
|
|
};
|
|
|
|
struct CLICCTRL {
|
|
volatile union CLICINTIP INTIP;
|
|
volatile union CLICINTIE INTIE;
|
|
volatile union CLICINTATTR INTATTR;
|
|
volatile uint8_t INTCTRL;
|
|
};
|
|
|
|
/** ECLIC Mode mask for MTVT CSR Register */
|
|
#define ECLIC_MODE_MTVEC_Msk 3U
|
|
|
|
/** CLIC INTATTR: TRIG Position */
|
|
#define CLIC_INTATTR_TRIG_Pos 1U
|
|
/** CLIC INTATTR: TRIG Mask */
|
|
#define CLIC_INTATTR_TRIG_Msk (0x3UL << CLIC_INTATTR_TRIG_Pos)
|
|
|
|
#define ECLIC_CFG (*((volatile union CLICCFG *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 0))))
|
|
#define ECLIC_INFO (*((volatile union CLICINFO *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 1))))
|
|
#define ECLIC_MTH (*((volatile union CLICMTH *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 2))))
|
|
#define ECLIC_CTRL ((volatile struct CLICCTRL *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 3)))
|
|
#define ECLIC_CTRL_SIZE (DT_REG_SIZE_BY_IDX(DT_NODELABEL(eclic), 3))
|
|
|
|
#if CONFIG_3RD_LEVEL_INTERRUPTS
|
|
#define INTERRUPT_LEVEL 2
|
|
#elif CONFIG_2ND_LEVEL_INTERRUPTS
|
|
#define INTERRUPT_LEVEL 1
|
|
#else
|
|
#define INTERRUPT_LEVEL 0
|
|
#endif
|
|
|
|
static uint8_t nlbits;
|
|
static uint8_t intctlbits;
|
|
static uint8_t max_prio;
|
|
static uint8_t max_level;
|
|
static uint8_t intctrl_mask;
|
|
|
|
static inline uint8_t leftalign8(uint8_t val, uint8_t shift)
|
|
{
|
|
return (val << (8U - shift));
|
|
}
|
|
|
|
static inline uint8_t mask8(uint8_t len)
|
|
{
|
|
return ((1 << len) - 1) & 0xFFFFU;
|
|
}
|
|
|
|
/**
|
|
* @brief Enable interrupt
|
|
*/
|
|
void riscv_clic_irq_enable(uint32_t irq)
|
|
{
|
|
ECLIC_CTRL[irq].INTIE.b.IE = 1;
|
|
}
|
|
|
|
/**
|
|
* @brief Disable interrupt
|
|
*/
|
|
void riscv_clic_irq_disable(uint32_t irq)
|
|
{
|
|
ECLIC_CTRL[irq].INTIE.b.IE = 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Get enable status of interrupt
|
|
*/
|
|
int riscv_clic_irq_is_enabled(uint32_t irq)
|
|
{
|
|
return ECLIC_CTRL[irq].INTIE.b.IE;
|
|
}
|
|
|
|
/**
|
|
* @brief Set priority and level of interrupt
|
|
*/
|
|
void riscv_clic_irq_priority_set(uint32_t irq, uint32_t pri, uint32_t flags)
|
|
{
|
|
const uint8_t prio = leftalign8(MIN(pri, max_prio), intctlbits);
|
|
const uint8_t level = leftalign8(MIN((irq_get_level(irq) - 1), max_level), nlbits);
|
|
const uint8_t intctrl = (prio | level) | (~intctrl_mask);
|
|
|
|
ECLIC_CTRL[irq].INTCTRL = intctrl;
|
|
|
|
ECLIC_CTRL[irq].INTATTR.b.shv = 0;
|
|
ECLIC_CTRL[irq].INTATTR.b.trg = (uint8_t)(flags & CLIC_INTATTR_TRIG_Msk);
|
|
}
|
|
|
|
static int nuclei_eclic_init(void)
|
|
{
|
|
/* check hardware support required interrupt levels */
|
|
__ASSERT_NO_MSG(ECLIC_INFO.b.intctlbits >= INTERRUPT_LEVEL);
|
|
|
|
ECLIC_MTH.w = 0;
|
|
ECLIC_CFG.w = 0;
|
|
ECLIC_CFG.b.nlbits = INTERRUPT_LEVEL;
|
|
for (int i = 0; i < ECLIC_CTRL_SIZE; i++) {
|
|
ECLIC_CTRL[i] = (struct CLICCTRL) { 0 };
|
|
}
|
|
|
|
csr_write(mtvec, ((csr_read(mtvec) & 0xFFFFFFC0) | ECLIC_MODE_MTVEC_Msk));
|
|
|
|
nlbits = ECLIC_CFG.b.nlbits;
|
|
intctlbits = ECLIC_INFO.b.intctlbits;
|
|
max_prio = mask8(intctlbits - nlbits);
|
|
max_level = mask8(nlbits);
|
|
intctrl_mask = leftalign8(mask8(intctlbits), intctlbits);
|
|
|
|
return 0;
|
|
}
|
|
|
|
SYS_INIT(nuclei_eclic_init, PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY);
|