|
1 | 1 | /* |
2 | 2 | * Copyright (c) 2021 Tokita, Hiroshi <[email protected]> |
| 3 | + * Copyright (c) 2025 Andes Technology Corporation |
3 | 4 | * |
4 | 5 | * SPDX-License-Identifier: Apache-2.0 |
5 | 6 | */ |
6 | 7 |
|
7 | 8 | /** |
8 | | - * @brief Driver for Nuclie's Extended Core Interrupt Controller |
| 9 | + * @brief Driver for Core Local Interrupt Controller |
9 | 10 | */ |
10 | 11 |
|
11 | 12 | #include <zephyr/kernel.h> |
12 | | -#include <zephyr/arch/cpu.h> |
13 | | -#include <zephyr/sys/util.h> |
| 13 | +#include <zephyr/arch/riscv/csr.h> |
14 | 14 | #include <zephyr/device.h> |
15 | | - |
16 | | -#include <zephyr/sw_isr_table.h> |
17 | 15 | #include <zephyr/drivers/interrupt_controller/riscv_clic.h> |
18 | 16 | #include "intc_clic.h" |
19 | 17 |
|
| 18 | +#if DT_HAS_COMPAT_STATUS_OKAY(riscv_clic) |
| 19 | +#define DT_DRV_COMPAT riscv_clic |
| 20 | +#elif DT_HAS_COMPAT_STATUS_OKAY(nuclei_eclic) |
20 | 21 | #define DT_DRV_COMPAT nuclei_eclic |
| 22 | +#else |
| 23 | +#error "Unknown CLIC controller compatible for this configuration" |
| 24 | +#endif |
21 | 25 |
|
22 | | -/** CLIC INTATTR: TRIG Mask */ |
23 | | -#define CLIC_INTATTR_TRIG_Msk 0x3U |
24 | | - |
25 | | -#define ECLIC_CFG (*((volatile union CLICCFG *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 0)))) |
26 | | -#define ECLIC_INFO (*((volatile union CLICINFO *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 1)))) |
27 | | -#define ECLIC_MTH (*((volatile union CLICMTH *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 2)))) |
28 | | -#define ECLIC_CTRL ((volatile struct CLICCTRL *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 3))) |
29 | | -#define ECLIC_CTRL_SIZE (DT_REG_SIZE_BY_IDX(DT_NODELABEL(eclic), 3)) |
| 26 | +struct clic_data { |
| 27 | + uint8_t nlbits; |
| 28 | + uint8_t intctlbits; |
| 29 | +}; |
30 | 30 |
|
31 | | -static uint8_t nlbits; |
32 | | -static uint8_t intctlbits; |
33 | | -static uint8_t max_prio; |
34 | | -static uint8_t max_level; |
35 | | -static uint8_t intctrl_mask; |
36 | | - |
37 | | -static inline uint8_t leftalign8(uint8_t val, uint8_t shift) |
38 | | -{ |
39 | | - return (val << (8U - shift)); |
40 | | -} |
41 | | - |
42 | | -static inline uint8_t mask8(uint8_t len) |
43 | | -{ |
44 | | - return ((1 << len) - 1) & 0xFFFFU; |
45 | | -} |
| 31 | +struct clic_config { |
| 32 | + mem_addr_t base; |
| 33 | +}; |
46 | 34 |
|
47 | 35 | /** |
48 | 36 | * @brief Enable interrupt |
49 | 37 | */ |
50 | 38 | void riscv_clic_irq_enable(uint32_t irq) |
51 | 39 | { |
52 | | - ECLIC_CTRL[irq].INTIE.b.IE = 1; |
| 40 | + const struct device *dev = DEVICE_DT_INST_GET(0); |
| 41 | + const struct clic_config *config = dev->config; |
| 42 | + union CLICINTIE clicintie = {.b = {.IE = 0x1}}; |
| 43 | + |
| 44 | + sys_write8(clicintie.w, config->base + CLIC_INTIE(irq)); |
53 | 45 | } |
54 | 46 |
|
55 | 47 | /** |
56 | 48 | * @brief Disable interrupt |
57 | 49 | */ |
58 | 50 | void riscv_clic_irq_disable(uint32_t irq) |
59 | 51 | { |
60 | | - ECLIC_CTRL[irq].INTIE.b.IE = 0; |
| 52 | + const struct device *dev = DEVICE_DT_INST_GET(0); |
| 53 | + const struct clic_config *config = dev->config; |
| 54 | + union CLICINTIE clicintie = {.b = {.IE = 0x0}}; |
| 55 | + |
| 56 | + sys_write8(clicintie.w, config->base + CLIC_INTIE(irq)); |
61 | 57 | } |
62 | 58 |
|
63 | 59 | /** |
64 | 60 | * @brief Get enable status of interrupt |
65 | 61 | */ |
66 | 62 | int riscv_clic_irq_is_enabled(uint32_t irq) |
67 | 63 | { |
68 | | - return ECLIC_CTRL[irq].INTIE.b.IE; |
| 64 | + const struct device *dev = DEVICE_DT_INST_GET(0); |
| 65 | + const struct clic_config *config = dev->config; |
| 66 | + union CLICINTIE clicintie = {.w = sys_read8(config->base + CLIC_INTIE(irq))}; |
| 67 | + |
| 68 | + return clicintie.b.IE; |
69 | 69 | } |
70 | 70 |
|
71 | 71 | /** |
72 | 72 | * @brief Set priority and level of interrupt |
73 | 73 | */ |
74 | 74 | void riscv_clic_irq_priority_set(uint32_t irq, uint32_t pri, uint32_t flags) |
75 | 75 | { |
76 | | - const uint8_t prio = leftalign8(MIN(pri, max_prio), intctlbits); |
77 | | - const uint8_t level = leftalign8(max_level, nlbits); |
78 | | - const uint8_t intctrl = (prio | level) | (~intctrl_mask); |
79 | | - |
80 | | - ECLIC_CTRL[irq].INTCTRL = intctrl; |
81 | | - |
82 | | - union CLICINTATTR intattr = {.w = 0}; |
83 | | - |
84 | | - /* Set non-vectoring as default. */ |
85 | | - intattr.b.shv = 0; |
86 | | - intattr.b.trg = (uint8_t)(flags & CLIC_INTATTR_TRIG_Msk); |
87 | | - ECLIC_CTRL[irq].INTATTR = intattr; |
| 76 | + const struct device *dev = DEVICE_DT_INST_GET(0); |
| 77 | + const struct clic_config *config = dev->config; |
| 78 | + const struct clic_data *data = dev->data; |
| 79 | + |
| 80 | + /* |
| 81 | + * Set the interrupt level and the interrupt priority. |
| 82 | + * Examples of mcliccfg settings: |
| 83 | + * CLICINTCTLBITS mnlbits clicintctl[i] interrupt levels |
| 84 | + * 0 2 ........ 255 |
| 85 | + * 1 2 l....... 127,255 |
| 86 | + * 2 2 ll...... 63,127,191,255 |
| 87 | + * 3 3 lll..... 31,63,95,127,159,191,223,255 |
| 88 | + * 4 1 lppp.... 127,255 |
| 89 | + * "." bits are non-existent bits for level encoding, assumed to be 1 |
| 90 | + * "l" bits are available variable bits in level specification |
| 91 | + * "p" bits are available variable bits in priority specification |
| 92 | + */ |
| 93 | + const uint8_t max_level = BIT_MASK(data->nlbits); |
| 94 | + const uint8_t max_prio = BIT_MASK(data->intctlbits - data->nlbits); |
| 95 | + uint8_t intctrl = (MIN(pri, max_prio) << (8U - data->intctlbits)) | |
| 96 | + (MIN(pri, max_level) << (8U - data->nlbits)) | |
| 97 | + BIT_MASK(8U - data->intctlbits); |
| 98 | + |
| 99 | + sys_write8(intctrl, config->base + CLIC_INTCTRL(irq)); |
| 100 | + |
| 101 | + /* Set the IRQ operates in machine mode, non-vectoring and the trigger type. */ |
| 102 | + union CLICINTATTR clicattr = {.b = {.mode = 0x3, .shv = 0x0, .trg = flags & BIT_MASK(3)}}; |
| 103 | + |
| 104 | + sys_write8(clicattr.w, config->base + CLIC_INTATTR(irq)); |
88 | 105 | } |
89 | 106 |
|
90 | 107 | /** |
91 | 108 | * @brief Set vector mode of interrupt |
92 | 109 | */ |
93 | 110 | void riscv_clic_irq_vector_set(uint32_t irq) |
94 | 111 | { |
95 | | - /* Set Selective Hardware Vectoring. */ |
96 | | - union CLICINTATTR intattr = ECLIC_CTRL[irq].INTATTR; |
| 112 | + const struct device *dev = DEVICE_DT_INST_GET(0); |
| 113 | + const struct clic_config *config = dev->config; |
| 114 | + union CLICINTATTR clicattr = {.w = sys_read8(config->base + CLIC_INTATTR(irq))}; |
97 | 115 |
|
98 | | - intattr.b.shv = 1; |
99 | | - ECLIC_CTRL[irq].INTATTR = intattr; |
| 116 | + /* Set Selective Hardware Vectoring. */ |
| 117 | + clicattr.b.shv = 1; |
| 118 | + sys_write8(clicattr.w, config->base + CLIC_INTATTR(irq)); |
100 | 119 | } |
101 | 120 |
|
102 | 121 | /** |
103 | 122 | * @brief Set pending bit of an interrupt |
104 | 123 | */ |
105 | 124 | void riscv_clic_irq_set_pending(uint32_t irq) |
106 | 125 | { |
107 | | - ECLIC_CTRL[irq].INTIP.b.IP = 1; |
| 126 | + const struct device *dev = DEVICE_DT_INST_GET(0); |
| 127 | + const struct clic_config *config = dev->config; |
| 128 | + union CLICINTIP clicintip = {.b = {.IP = 0x1}}; |
| 129 | + |
| 130 | + sys_write8(clicintip.w, config->base + CLIC_INTIP(irq)); |
108 | 131 | } |
109 | 132 |
|
110 | | -static int nuclei_eclic_init(const struct device *dev) |
| 133 | +static int clic_init(const struct device *dev) |
111 | 134 | { |
112 | | - ECLIC_MTH.w = 0; |
113 | | - ECLIC_CFG.w = 0; |
114 | | - ECLIC_CFG.b.nlbits = 0; |
115 | | - for (int i = 0; i < ECLIC_CTRL_SIZE; i++) { |
116 | | - ECLIC_CTRL[i] = (struct CLICCTRL) { 0 }; |
| 135 | + const struct clic_config *config = dev->config; |
| 136 | + struct clic_data *data = dev->data; |
| 137 | + |
| 138 | + if (IS_ENABLED(CONFIG_NUCLEI_ECLIC)) { |
| 139 | + /* Configure the interrupt level threshold. */ |
| 140 | + union CLICMTH clicmth = {.b = {.mth = 0x0}}; |
| 141 | + |
| 142 | + sys_write32(clicmth.qw, config->base + CLIC_MTH); |
| 143 | + |
| 144 | + /* Detect the number of bits for the clicintctl register. */ |
| 145 | + union CLICINFO clicinfo = {.qw = sys_read32(config->base + CLIC_INFO)}; |
| 146 | + |
| 147 | + data->intctlbits = clicinfo.b.intctlbits; |
| 148 | + |
| 149 | + if (data->nlbits > data->intctlbits) { |
| 150 | + data->nlbits = data->intctlbits; |
| 151 | + } |
| 152 | + |
| 153 | + /* Configure the number of bits assigned to interrupt levels. */ |
| 154 | + union CLICCFG cliccfg = {.qw = sys_read32(config->base + CLIC_CFG)}; |
| 155 | + |
| 156 | + cliccfg.w.nlbits = data->nlbits; |
| 157 | + sys_write32(cliccfg.qw, config->base + CLIC_CFG); |
| 158 | + } else { |
| 159 | + /* Configure the interrupt level threshold by CSR mintthresh. */ |
| 160 | + csr_write(CSR_MINTTHRESH, 0x0); |
117 | 161 | } |
118 | 162 |
|
119 | | - nlbits = ECLIC_CFG.b.nlbits; |
120 | | - intctlbits = ECLIC_INFO.b.intctlbits; |
121 | | - max_prio = mask8(intctlbits - nlbits); |
122 | | - max_level = mask8(nlbits); |
123 | | - intctrl_mask = leftalign8(mask8(intctlbits), intctlbits); |
| 163 | + /* Reset all interrupt control register */ |
| 164 | + for (int i = 0; i < CONFIG_NUM_IRQS; i++) { |
| 165 | + sys_write32(0, config->base + CLIC_CTRL(i)); |
| 166 | + } |
124 | 167 |
|
125 | 168 | return 0; |
126 | 169 | } |
127 | 170 |
|
128 | | -DEVICE_DT_INST_DEFINE(0, nuclei_eclic_init, NULL, NULL, NULL, |
129 | | - PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, NULL); |
| 171 | +#define CLIC_INTC_DATA_INIT(n) \ |
| 172 | + static struct clic_data clic_data_##n = { \ |
| 173 | + .nlbits = 0, \ |
| 174 | + .intctlbits = 8, \ |
| 175 | + }; |
| 176 | +#define CLIC_INTC_CONFIG_INIT(n) \ |
| 177 | + const static struct clic_config clic_config_##n = { \ |
| 178 | + .base = DT_REG_ADDR(DT_DRV_INST(n)), \ |
| 179 | + }; |
| 180 | +#define CLIC_INTC_DEVICE_INIT(n) \ |
| 181 | + CLIC_INTC_DATA_INIT(n) \ |
| 182 | + CLIC_INTC_CONFIG_INIT(n) \ |
| 183 | + DEVICE_DT_INST_DEFINE(n, &clic_init, NULL, &clic_data_##n, &clic_config_##n, PRE_KERNEL_1, \ |
| 184 | + CONFIG_INTC_INIT_PRIORITY, NULL); |
| 185 | + |
| 186 | +DT_INST_FOREACH_STATUS_OKAY(CLIC_INTC_DEVICE_INIT) |
0 commit comments