Software APIs
dif_rv_plic.c
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
6
7#include <stdbool.h>
8#include <stddef.h>
9#include <stdint.h>
10
13
14#include "rv_plic_regs.h" // Generated.
15
16const uint32_t kDifRvPlicMinPriority = 0;
17const uint32_t kDifRvPlicMaxPriority = RV_PLIC_PRIO_0_PRIO_0_MASK;
18
19/**
20 * PLIC register info.
21 *
22 * This data type is used to store IRQ source bit offset within a register,
23 * and the offset of this register inside the PLIC.
24 */
25typedef struct plic_reg_info {
26 ptrdiff_t offset;
27 bitfield_bit32_index_t bit_index;
28} plic_reg_info_t;
29
30/**
31 * Get an IE, IP or LE register offset (IE0_0, IE01, ...) from an IRQ source ID.
32 *
33 * With more than 32 IRQ sources, there is a multiple of these registers to
34 * accommodate all the bits (1 bit per IRQ source). This function calculates
35 * the offset for a specific IRQ source ID (ID 32 would be IE01, ...).
36 */
37static ptrdiff_t plic_offset_from_reg0(dif_rv_plic_irq_id_t irq) {
38 uint8_t register_index = (uint8_t)(irq / RV_PLIC_PARAM_REG_WIDTH);
39 return register_index * sizeof(uint32_t);
40}
41
42/**
43 * Get an IE, IP, LE register bit index from an IRQ source ID.
44 *
45 * With more than 32 IRQ sources, there is a multiple of these registers to
46 * accommodate all the bits (1 bit per IRQ source). This function calculates
47 * the bit position within a register for a specific IRQ source ID (ID 32 would
48 * be bit 0).
49 */
50static uint8_t plic_irq_bit_index(dif_rv_plic_irq_id_t irq) {
51 return irq % RV_PLIC_PARAM_REG_WIDTH;
52}
53
54/**
55 * Get the address of the first target N interrupt enable register (IEN0).
56 */
57static ptrdiff_t plic_irq_enable_base_for_target(dif_rv_plic_target_t target) {
58 ptrdiff_t range = RV_PLIC_IE0_MULTIREG_COUNT * sizeof(uint32_t);
59 return RV_PLIC_IE0_0_REG_OFFSET + (range * (ptrdiff_t)target);
60}
61
62/**
63 * Get the address of the first target N software interrupt register (MSIPN).
64 */
65static ptrdiff_t plic_software_irq_base_for_target(
66 dif_rv_plic_target_t target) {
67 return RV_PLIC_MSIP0_REG_OFFSET + (ptrdiff_t)(target * sizeof(uint32_t));
68}
69
70/**
71 * Get the address of the first target N threshold register (THRESHOLDN).
72 */
73static ptrdiff_t plic_threshold_base_for_target(dif_rv_plic_target_t target) {
74 return RV_PLIC_THRESHOLD0_REG_OFFSET + (ptrdiff_t)(target * sizeof(uint32_t));
75}
76
77/**
78 * Get the address of the first target N claim complete register (CCN).
79 */
80static ptrdiff_t plic_claim_complete_base_for_target(
81 dif_rv_plic_target_t target) {
82 return RV_PLIC_CC0_REG_OFFSET + (ptrdiff_t)(target * sizeof(uint32_t));
83}
84
85/**
86 * Get a target and an IRQ source specific Interrupt Enable register info.
87 */
88static plic_reg_info_t plic_irq_enable_reg_info(dif_rv_plic_irq_id_t irq,
89 dif_rv_plic_target_t target) {
90 ptrdiff_t offset = plic_offset_from_reg0(irq);
91 return (plic_reg_info_t){
92 .offset = plic_irq_enable_base_for_target(target) + offset,
93 .bit_index = plic_irq_bit_index(irq),
94 };
95}
96
97/**
98 * Get an IRQ source specific Interrupt Pending register info.
99 */
100static plic_reg_info_t plic_irq_pending_reg_info(dif_rv_plic_irq_id_t irq) {
101 ptrdiff_t offset = plic_offset_from_reg0(irq);
102 return (plic_reg_info_t){
103 .offset = RV_PLIC_IP_0_REG_OFFSET + offset,
104 .bit_index = plic_irq_bit_index(irq),
105 };
106}
107
108/**
109 * Get a PRIO register offset (PRIO_0, PRIO_1, ...) from an IRQ source ID.
110 *
111 * There is one PRIO register per IRQ source, this function calculates the IRQ
112 * source specific PRIO register offset.
113 */
114static ptrdiff_t plic_priority_reg_offset(dif_rv_plic_irq_id_t irq) {
115 ptrdiff_t offset = (ptrdiff_t)(irq * sizeof(uint32_t));
116 return RV_PLIC_PRIO_0_REG_OFFSET + offset;
117}
118
119dif_result_t dif_rv_plic_reset(const dif_rv_plic_t *plic) {
120 if (plic == NULL) {
121 return kDifBadArg;
122 }
123
124 // Clear all of the priority registers.
125 for (uint32_t i = 0; i < RV_PLIC_PARAM_NUM_SRC; ++i) {
126 ptrdiff_t offset = plic_priority_reg_offset(i);
127 mmio_region_write32(plic->base_addr, offset, 0);
128 }
129
130 // Clear all of the target related registers.
131 for (dif_rv_plic_target_t target = 0; target < RV_PLIC_PARAM_NUM_TARGET;
132 ++target) {
133 // Clear interrupt enable registers.
134 ptrdiff_t offset = plic_irq_enable_base_for_target(target);
135 for (size_t i = 0; i < RV_PLIC_IE0_MULTIREG_COUNT; ++i) {
136 ptrdiff_t multireg_offset = offset + (ptrdiff_t)(i * sizeof(uint32_t));
137 mmio_region_write32(plic->base_addr, multireg_offset, 0);
138 }
139
140 // Clear threshold registers.
141 offset = plic_threshold_base_for_target(target);
142 mmio_region_write32(plic->base_addr, offset, 0);
143
144 // Clear software interrupt registers.
145 offset = plic_software_irq_base_for_target(target);
146 mmio_region_write32(plic->base_addr, offset, 0);
147 }
148
149 return kDifOk;
150}
151
152dif_result_t dif_rv_plic_irq_get_enabled(const dif_rv_plic_t *plic,
155 dif_toggle_t *state) {
156 if (plic == NULL || irq >= RV_PLIC_PARAM_NUM_SRC ||
157 target >= RV_PLIC_PARAM_NUM_TARGET) {
158 return kDifBadArg;
159 }
160
161 plic_reg_info_t reg_info = plic_irq_enable_reg_info(irq, target);
162
163 uint32_t reg = mmio_region_read32(plic->base_addr, reg_info.offset);
164 bool is_enabled = bitfield_bit32_read(reg, reg_info.bit_index);
165 *state = is_enabled ? kDifToggleEnabled : kDifToggleDisabled;
166
167 return kDifOk;
168}
169
170dif_result_t dif_rv_plic_irq_set_enabled(const dif_rv_plic_t *plic,
173 dif_toggle_t state) {
174 if (plic == NULL || irq >= RV_PLIC_PARAM_NUM_SRC ||
175 target >= RV_PLIC_PARAM_NUM_TARGET) {
176 return kDifBadArg;
177 }
178
179 bool flag;
180 switch (state) {
182 flag = true;
183 break;
185 flag = false;
186 break;
187 default:
188 return kDifBadArg;
189 }
190
191 plic_reg_info_t reg_info = plic_irq_enable_reg_info(irq, target);
192
193 uint32_t reg = mmio_region_read32(plic->base_addr, reg_info.offset);
194 reg = bitfield_bit32_write(reg, reg_info.bit_index, flag);
195 mmio_region_write32(plic->base_addr, reg_info.offset, reg);
196
197 return kDifOk;
198}
199
200dif_result_t dif_rv_plic_irq_set_priority(const dif_rv_plic_t *plic,
202 uint32_t priority) {
203 if (plic == NULL || irq >= RV_PLIC_PARAM_NUM_SRC ||
204 priority > kDifRvPlicMaxPriority) {
205 return kDifBadArg;
206 }
207
208 ptrdiff_t offset = plic_priority_reg_offset(irq);
209 mmio_region_write32(plic->base_addr, offset, priority);
210
211 return kDifOk;
212}
213
214dif_result_t dif_rv_plic_target_set_threshold(const dif_rv_plic_t *plic,
216 uint32_t threshold) {
217 if (plic == NULL || target >= RV_PLIC_PARAM_NUM_TARGET ||
218 threshold > kDifRvPlicMaxPriority) {
219 return kDifBadArg;
220 }
221
222 ptrdiff_t threshold_offset = plic_threshold_base_for_target(target);
223 mmio_region_write32(plic->base_addr, threshold_offset, threshold);
224
225 return kDifOk;
226}
227
228dif_result_t dif_rv_plic_irq_is_pending(const dif_rv_plic_t *plic,
230 bool *is_pending) {
231 if (plic == NULL || irq >= RV_PLIC_PARAM_NUM_SRC || is_pending == NULL) {
232 return kDifBadArg;
233 }
234
235 plic_reg_info_t reg_info = plic_irq_pending_reg_info(irq);
236 uint32_t reg = mmio_region_read32(plic->base_addr, reg_info.offset);
237 *is_pending = bitfield_bit32_read(reg, reg_info.bit_index);
238
239 return kDifOk;
240}
241
242dif_result_t dif_rv_plic_irq_claim(const dif_rv_plic_t *plic,
244 dif_rv_plic_irq_id_t *claim_data) {
245 if (plic == NULL || target >= RV_PLIC_PARAM_NUM_TARGET ||
246 claim_data == NULL) {
247 return kDifBadArg;
248 }
249
250 ptrdiff_t claim_complete_reg = plic_claim_complete_base_for_target(target);
251 *claim_data = mmio_region_read32(plic->base_addr, claim_complete_reg);
252
253 return kDifOk;
254}
255
256dif_result_t dif_rv_plic_irq_complete(const dif_rv_plic_t *plic,
258 dif_rv_plic_irq_id_t complete_data) {
259 if (plic == NULL || target >= RV_PLIC_PARAM_NUM_TARGET) {
260 return kDifBadArg;
261 }
262
263 // Write back the claimed IRQ ID to the target specific CC register,
264 // to notify the PLIC of the IRQ completion.
265 ptrdiff_t claim_complete_reg = plic_claim_complete_base_for_target(target);
266 mmio_region_write32(plic->base_addr, claim_complete_reg, complete_data);
267
268 return kDifOk;
269}
270
271dif_result_t dif_rv_plic_software_irq_force(const dif_rv_plic_t *plic,
272 dif_rv_plic_target_t target) {
273 if (plic == NULL || target >= RV_PLIC_PARAM_NUM_TARGET) {
274 return kDifBadArg;
275 }
276
277 ptrdiff_t msip_offset = plic_software_irq_base_for_target(target);
278 mmio_region_write32(plic->base_addr, msip_offset, 1);
279
280 return kDifOk;
281}
282
283dif_result_t dif_rv_plic_software_irq_acknowledge(const dif_rv_plic_t *plic,
284 dif_rv_plic_target_t target) {
285 if (plic == NULL || target >= RV_PLIC_PARAM_NUM_TARGET) {
286 return kDifBadArg;
287 }
288
289 ptrdiff_t msip_offset = plic_software_irq_base_for_target(target);
290 mmio_region_write32(plic->base_addr, msip_offset, 0);
291
292 return kDifOk;
293}
294
295dif_result_t dif_rv_plic_software_irq_is_pending(const dif_rv_plic_t *plic,
297 bool *is_pending) {
298 if (plic == NULL || target >= RV_PLIC_PARAM_NUM_TARGET ||
299 is_pending == NULL) {
300 return kDifBadArg;
301 }
302
303 ptrdiff_t msip_offset = plic_software_irq_base_for_target(target);
304 uint32_t register_value = mmio_region_read32(plic->base_addr, msip_offset);
305
306 *is_pending = (register_value == 1) ? true : false;
307
308 return kDifOk;
309}