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 
16 const uint32_t kDifRvPlicMinPriority = 0;
17 const uint32_t kDifRvPlicMaxPriority = RV_PLIC_PRIO0_PRIO0_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  */
25 typedef struct plic_reg_info {
26  ptrdiff_t offset;
27  bitfield_bit32_index_t bit_index;
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  */
37 static 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  */
50 static 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  */
57 static 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  */
65 static 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  */
73 static 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  */
80 static 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  */
88 static 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  */
100 static 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 (PRIO0, PRIO1, ...) 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  */
114 static 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_PRIO0_REG_OFFSET + offset;
117 }
118 
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 
154  dif_rv_plic_target_t target,
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 
172  dif_rv_plic_target_t target,
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) {
181  case kDifToggleEnabled:
182  flag = true;
183  break;
184  case kDifToggleDisabled:
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 
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 
215  dif_rv_plic_target_t target,
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 
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 
243  dif_rv_plic_target_t target,
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 
257  dif_rv_plic_target_t target,
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 
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 
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 
296  dif_rv_plic_target_t target,
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 }