source: rtems/bsps/riscv/riscv/irq/irq.c @ c7b4eca7

Last change on this file since c7b4eca7 was c7b4eca7, checked in by Sebastian Huber <sebastian.huber@…>, on 07/27/21 at 07:58:43

bsps/irq: bsp_interrupt_facility_initialize()

Do not return a status code in bsp_interrupt_facility_initialize() since this
leads to unreachable code in bsp_interrupt_initialize(). Use RTEMS_DEBUG
assertions in bsp_interrupt_facility_initialize() if necessary.

  • Property mode set to 100644
File size: 12.2 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup riscv_interrupt
5 *
6 * @brief Interrupt support.
7 */
8
9/*
10 * Copyright (c) 2018 embedded brains GmbH
11 *
12 * Copyright (c) 2015 University of York.
13 * Hesham Almatary <hesham@alumni.york.ac.uk>
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 *    notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 *    notice, this list of conditions and the following disclaimer in the
22 *    documentation and/or other materials provided with the distribution.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#include <bsp/irq.h>
38#include <bsp/fatal.h>
39#include <bsp/fdt.h>
40#include <bsp/irq-generic.h>
41#include <bsp/riscv.h>
42
43#include <rtems/score/percpu.h>
44#include <rtems/score/riscv-utility.h>
45#include <rtems/score/smpimpl.h>
46
47#include <libfdt.h>
48
49volatile RISCV_CLINT_regs *riscv_clint;
50
51/*
52 * The lovely PLIC has an interrupt enable bit per hart for each interrupt
53 * source.  This makes the interrupt enable/disable a bit difficult.  We have
54 * to store the interrupt distribution in software.  To keep it simple, we
55 * support only a one-to-one and one-to-all interrupt to processor
56 * distribution.  For a one-to-one distribution, the array member must point to
57 * the enable register block of the corresponding.  For a one-to-all
58 * distribution, the array member must be NULL.  The array index is the
59 * external interrupt index minus one (external interrupt index zero is a
60 * special value, see PLIC documentation).
61 */
62static volatile uint32_t *
63riscv_plic_irq_to_cpu[RISCV_MAXIMUM_EXTERNAL_INTERRUPTS];
64
65RTEMS_INTERRUPT_LOCK_DEFINE(static, riscv_plic_lock, "PLIC")
66
67void _RISCV_Interrupt_dispatch(uintptr_t mcause, Per_CPU_Control *cpu_self)
68{
69  /*
70   * Get rid of the most significant bit which indicates if the exception was
71   * caused by an interrupt or not.
72   */
73  mcause <<= 1;
74
75  if (mcause == (RISCV_INTERRUPT_TIMER_MACHINE << 1)) {
76    bsp_interrupt_handler_dispatch(RISCV_INTERRUPT_VECTOR_TIMER);
77  } else if (mcause == (RISCV_INTERRUPT_EXTERNAL_MACHINE << 1)) {
78    volatile RISCV_PLIC_hart_regs *plic_hart_regs;
79    uint32_t interrupt_index;
80
81    plic_hart_regs = cpu_self->cpu_per_cpu.plic_hart_regs;
82
83    while ((interrupt_index = plic_hart_regs->claim_complete) != 0) {
84      bsp_interrupt_handler_dispatch(
85        RISCV_INTERRUPT_VECTOR_EXTERNAL(interrupt_index)
86      );
87
88      plic_hart_regs->claim_complete = interrupt_index;
89
90      /*
91       * FIXME: It is not clear which fence is necessary here or if a fence is
92       * necessary at all.  The goal is that the complete signal is somehow
93       * recognized by the PLIC before the next claim is issued.
94       */
95      __asm__ volatile ("fence o, i" : : : "memory");
96    }
97  } else if (mcause == (RISCV_INTERRUPT_SOFTWARE_MACHINE << 1)) {
98#ifdef RTEMS_SMP
99    /*
100     * Clear the software interrupt on this processor.  Synchronization of
101     * inter-processor interrupts is done via Per_CPU_Control::message in
102     * _SMP_Inter_processor_interrupt_handler().
103     */
104    *cpu_self->cpu_per_cpu.clint_msip = 0;
105
106    _SMP_Inter_processor_interrupt_handler(cpu_self);
107#else
108    bsp_interrupt_handler_dispatch(RISCV_INTERRUPT_VECTOR_SOFTWARE);
109#endif
110  } else {
111    bsp_fatal(RISCV_FATAL_UNEXPECTED_INTERRUPT_EXCEPTION);
112  }
113}
114
115static void riscv_clint_init(const void *fdt)
116{
117  volatile RISCV_CLINT_regs *clint;
118  int node;
119  const uint32_t *val;
120  int len;
121  int i;
122
123  node = fdt_node_offset_by_compatible(fdt, -1, "riscv,clint0");
124
125  clint = riscv_fdt_get_address(fdt, node);
126  if (clint == NULL) {
127    bsp_fatal(RISCV_FATAL_NO_CLINT_REG_IN_DEVICE_TREE);
128  }
129
130  riscv_clint = clint;
131
132  val = fdt_getprop(fdt, node, "interrupts-extended", &len);
133
134  for (i = 0; i < len; i += 16) {
135    uint32_t hart_index;
136    Per_CPU_Control *cpu;
137
138    hart_index = riscv_get_hart_index_by_phandle(fdt32_to_cpu(val[i / 4]));
139    if (hart_index >= rtems_configuration_get_maximum_processors()) {
140      continue;
141    }
142
143    cpu = _Per_CPU_Get_by_index(hart_index);
144    cpu->cpu_per_cpu.clint_msip = &clint->msip[i / 16];
145    cpu->cpu_per_cpu.clint_mtimecmp = &clint->mtimecmp[i / 16];
146  }
147}
148
149static void riscv_plic_init(const void *fdt)
150{
151  volatile RISCV_PLIC_regs *plic;
152  int node;
153  int i;
154  const uint32_t *val;
155  int len;
156  uint32_t interrupt_index;
157  uint32_t ndev;
158  Per_CPU_Control *cpu;
159
160  node = fdt_node_offset_by_compatible(fdt, -1, "riscv,plic0");
161
162  plic = riscv_fdt_get_address(fdt, node);
163  if (plic == NULL) {
164#if RISCV_ENABLE_HTIF_SUPPORT != 0
165    /* Spike platform has HTIF and does not have a PLIC */
166    return;
167#else
168    bsp_fatal(RISCV_FATAL_NO_PLIC_REG_IN_DEVICE_TREE);
169#endif
170  }
171
172  val = fdt_getprop(fdt, node, "riscv,ndev", &len);
173  if (val == NULL || len != 4) {
174    bsp_fatal(RISCV_FATAL_INVALID_PLIC_NDEV_IN_DEVICE_TREE);
175  }
176
177  ndev = fdt32_to_cpu(val[0]);
178  if (ndev > RISCV_MAXIMUM_EXTERNAL_INTERRUPTS) {
179    bsp_fatal(RISCV_FATAL_TOO_LARGE_PLIC_NDEV_IN_DEVICE_TREE);
180  }
181
182  val = fdt_getprop(fdt, node, "interrupts-extended", &len);
183
184  for (i = 0; i < len; i += 8) {
185    uint32_t hart_index;
186
187    hart_index = riscv_get_hart_index_by_phandle(fdt32_to_cpu(val[i / 4]));
188    if (hart_index >= rtems_configuration_get_maximum_processors()) {
189      continue;
190    }
191
192    interrupt_index = fdt32_to_cpu(val[i / 4 + 1]);
193    if (interrupt_index != RISCV_INTERRUPT_EXTERNAL_MACHINE) {
194      continue;
195    }
196
197    plic->harts[i / 8].priority_threshold = 0;
198
199    cpu = _Per_CPU_Get_by_index(hart_index);
200    cpu->cpu_per_cpu.plic_hart_regs = &plic->harts[i / 8];
201    cpu->cpu_per_cpu.plic_m_ie = &plic->enable[i / 8][0];
202  }
203
204  cpu = _Per_CPU_Get_by_index(0);
205
206  for (interrupt_index = 1; interrupt_index <= ndev; ++interrupt_index) {
207    plic->priority[interrupt_index] = 1;
208    riscv_plic_irq_to_cpu[interrupt_index - 1] = cpu->cpu_per_cpu.plic_m_ie;
209  }
210
211  /*
212   * External M-mode interrupts on secondary processors are enabled in
213   * bsp_start_on_secondary_processor().
214   */
215  set_csr(mie, MIP_MEIP);
216}
217
218void bsp_interrupt_facility_initialize(void)
219{
220  const void *fdt;
221
222  fdt = bsp_fdt_get();
223  riscv_clint_init(fdt);
224  riscv_plic_init(fdt);
225}
226
227rtems_status_code bsp_interrupt_get_attributes(
228  rtems_vector_number         vector,
229  rtems_interrupt_attributes *attributes
230)
231{
232  return RTEMS_SUCCESSFUL;
233}
234
235rtems_status_code bsp_interrupt_is_pending(
236  rtems_vector_number vector,
237  bool               *pending
238)
239{
240  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
241  bsp_interrupt_assert(pending != NULL);
242  *pending = false;
243  return RTEMS_UNSATISFIED;
244}
245
246rtems_status_code bsp_interrupt_raise(rtems_vector_number vector)
247{
248  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
249  return RTEMS_UNSATISFIED;
250}
251
252#if defined(RTEMS_SMP)
253rtems_status_code bsp_interrupt_raise_on(
254  rtems_vector_number vector,
255  uint32_t            cpu_index
256)
257{
258  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
259  return RTEMS_UNSATISFIED;
260}
261#endif
262
263rtems_status_code bsp_interrupt_clear(rtems_vector_number vector)
264{
265  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
266  return RTEMS_UNSATISFIED;
267}
268
269rtems_status_code bsp_interrupt_vector_is_enabled(
270  rtems_vector_number vector,
271  bool               *enabled
272)
273{
274  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
275  bsp_interrupt_assert(enabled != NULL);
276  *enabled = false;
277  return RTEMS_UNSATISFIED;
278}
279
280rtems_status_code bsp_interrupt_vector_enable(rtems_vector_number vector)
281{
282  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
283
284  if (RISCV_INTERRUPT_VECTOR_IS_EXTERNAL(vector)) {
285    uint32_t interrupt_index;
286    volatile uint32_t *enable;
287    uint32_t group;
288    uint32_t bit;
289    rtems_interrupt_lock_context lock_context;
290
291    interrupt_index = RISCV_INTERRUPT_VECTOR_EXTERNAL_TO_INDEX(vector);
292    enable = riscv_plic_irq_to_cpu[interrupt_index - 1];
293    group = interrupt_index / 32;
294    bit = UINT32_C(1) << (interrupt_index % 32);
295
296    rtems_interrupt_lock_acquire(&riscv_plic_lock, &lock_context);
297
298    if (enable != NULL) {
299      enable[group] |= bit;
300    } else {
301      uint32_t cpu_max;
302      uint32_t cpu_index;
303
304      cpu_max = _SMP_Get_processor_maximum();
305
306      for (cpu_index = 0; cpu_index < cpu_max; ++cpu_index) {
307        Per_CPU_Control *cpu;
308
309        cpu = _Per_CPU_Get_by_index(cpu_index);
310        enable = cpu->cpu_per_cpu.plic_m_ie;
311
312        if (enable != NULL) {
313          enable[group] |= bit;
314        }
315      }
316    }
317
318    rtems_interrupt_lock_release(&riscv_plic_lock, &lock_context);
319  }
320
321  return RTEMS_SUCCESSFUL;
322}
323
324rtems_status_code bsp_interrupt_vector_disable(rtems_vector_number vector)
325{
326  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
327
328  if (RISCV_INTERRUPT_VECTOR_IS_EXTERNAL(vector)) {
329    uint32_t interrupt_index;
330    volatile uint32_t *enable;
331    uint32_t group;
332    uint32_t bit;
333    rtems_interrupt_lock_context lock_context;
334
335    interrupt_index = RISCV_INTERRUPT_VECTOR_EXTERNAL_TO_INDEX(vector);
336    enable = riscv_plic_irq_to_cpu[interrupt_index - 1];
337    group = interrupt_index / 32;
338    bit = UINT32_C(1) << (interrupt_index % 32);
339
340    rtems_interrupt_lock_acquire(&riscv_plic_lock, &lock_context);
341
342    if (enable != NULL) {
343      enable[group] &= ~bit;
344    } else {
345      uint32_t cpu_max;
346      uint32_t cpu_index;
347
348      cpu_max = _SMP_Get_processor_maximum();
349
350      for (cpu_index = 0; cpu_index < cpu_max; ++cpu_index) {
351        Per_CPU_Control *cpu;
352
353        cpu = _Per_CPU_Get_by_index(cpu_index);
354        enable = cpu->cpu_per_cpu.plic_m_ie;
355
356        if (enable != NULL) {
357          enable[group] &= ~bit;
358        }
359      }
360    }
361
362    rtems_interrupt_lock_release(&riscv_plic_lock, &lock_context);
363  }
364
365  return RTEMS_SUCCESSFUL;
366}
367
368rtems_status_code bsp_interrupt_set_affinity(
369  rtems_vector_number vector,
370  const Processor_mask *affinity
371)
372{
373  if (RISCV_INTERRUPT_VECTOR_IS_EXTERNAL(vector)) {
374    uint32_t interrupt_index;
375    Processor_mask mask;
376
377    interrupt_index = RISCV_INTERRUPT_VECTOR_EXTERNAL_TO_INDEX(vector);
378
379    _Processor_mask_And(&mask, affinity, _SMP_Get_online_processors());
380
381    if (_Processor_mask_Is_equal(&mask, _SMP_Get_online_processors())) {
382      riscv_plic_irq_to_cpu[interrupt_index - 1] = NULL;
383      return RTEMS_SUCCESSFUL;
384    }
385
386    if (_Processor_mask_Count(&mask) == 1) {
387      uint32_t cpu_index;
388      Per_CPU_Control *cpu;
389
390      cpu_index = _Processor_mask_Find_last_set(&mask) - 1;
391      cpu = _Per_CPU_Get_by_index(cpu_index);
392      riscv_plic_irq_to_cpu[interrupt_index - 1] = cpu->cpu_per_cpu.plic_m_ie;
393      return RTEMS_SUCCESSFUL;
394    }
395
396    bsp_fatal(RISCV_FATAL_INVALID_INTERRUPT_AFFINITY);
397  }
398
399  return RTEMS_UNSATISFIED;
400}
401
402rtems_status_code bsp_interrupt_get_affinity(
403  rtems_vector_number vector,
404  Processor_mask *affinity
405)
406{
407  _Processor_mask_Zero(affinity);
408
409  if (RISCV_INTERRUPT_VECTOR_IS_EXTERNAL(vector)) {
410    uint32_t interrupt_index;
411    volatile uint32_t *enable;
412
413    interrupt_index = RISCV_INTERRUPT_VECTOR_EXTERNAL_TO_INDEX(vector);
414    enable = riscv_plic_irq_to_cpu[interrupt_index - 1];
415
416    if (enable != NULL) {
417      uint32_t cpu_max;
418      uint32_t cpu_index;
419
420      cpu_max = _SMP_Get_processor_maximum();
421
422      for (cpu_index = 0; cpu_index < cpu_max; ++cpu_index) {
423        Per_CPU_Control *cpu;
424
425        cpu = _Per_CPU_Get_by_index(cpu_index);
426
427        if (enable == cpu->cpu_per_cpu.plic_m_ie) {
428          _Processor_mask_Set(affinity, cpu_index);
429          break;
430        }
431      }
432    } else {
433      _Processor_mask_Assign(affinity, _SMP_Get_online_processors());
434    }
435  }
436
437  return RTEMS_SUCCESSFUL;
438}
Note: See TracBrowser for help on using the repository browser.