-
Notifications
You must be signed in to change notification settings - Fork 6.1k
/
intc_nuclei_eclic.c
205 lines (174 loc) · 4.6 KB
/
intc_nuclei_eclic.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/*
* 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/device.h>
#include <zephyr/irq_multilevel.h>
#include <zephyr/sw_isr_table.h>
#include <zephyr/drivers/interrupt_controller/riscv_clic.h>
#define DT_DRV_COMPAT nuclei_eclic
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 Mask */
#define CLIC_INTATTR_TRIG_Msk 0x3U
#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;
union CLICINTATTR intattr = {.w = 0};
#if defined(CONFIG_RISCV_VECTORED_MODE) && !defined(CONFIG_LEGACY_CLIC)
/*
* Set Selective Hardware Vectoring.
* Legacy SiFive does not implement smclicshv extension and vectoring is
* enabled in the mode bits of mtvec.
*/
intattr.b.shv = 1;
#else
intattr.b.shv = 0;
#endif
intattr.b.trg = (uint8_t)(flags & CLIC_INTATTR_TRIG_Msk);
ECLIC_CTRL[irq].INTATTR = intattr;
}
/**
* @brief Set pending bit of an interrupt
*/
void riscv_clic_irq_set_pending(uint32_t irq)
{
ECLIC_CTRL[irq].INTIP.b.IP = 1;
}
static int nuclei_eclic_init(const struct device *dev)
{
/* 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;
}
DEVICE_DT_INST_DEFINE(0, nuclei_eclic_init, NULL, NULL, NULL,
PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, NULL);