source: rtems/bsps/riscv/riscv/irq/irq.c @ 44c2d393

5
Last change on this file since 44c2d393 was 44c2d393, checked in by Sebastian Huber <sebastian.huber@…>, on 07/27/18 at 13:04:38

bsp/riscv: Fix inter-processor interrupts

The previous version worked only on a patched Qemu. Writes to mip are
illegal according to the The RISC-V Instruction Set Manual, Volume II:
Privileged Architecture, Privileged Architecture Version 1.10.

Update #3433.

  • Property mode set to 100644
File size: 10.5 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
51static volatile RISCV_PLIC_regs *riscv_plic;
52
53/*
54 * The lovely PLIC has an interrupt enable bit per hart for each interrupt
55 * source.  This makes the interrupt enable/disable a bit difficult.  We have
56 * to store the interrupt distribution in software.  To keep it simple, we
57 * support only a one-to-one and one-to-all interrupt to processor
58 * distribution.  For a one-to-one distribution, the array member must point to
59 * the enable register block of the corresponding.  For a one-to-all
60 * distribution, the array member must be NULL.  The array index is the
61 * external interrupt index minus one (external interrupt index zero is a
62 * special value, see PLIC documentation).
63 */
64static volatile uint32_t *
65riscv_plic_irq_to_cpu[RISCV_MAXIMUM_EXTERNAL_INTERRUPTS];
66
67RTEMS_INTERRUPT_LOCK_DEFINE(static, riscv_plic_lock, "PLIC")
68
69void _RISCV_Interrupt_dispatch(uintptr_t mcause, Per_CPU_Control *cpu_self)
70{
71  /*
72   * Get rid of the most significant bit which indicates if the exception was
73   * caused by an interrupt or not.
74   */
75  mcause <<= 1;
76
77  if (mcause == (RISCV_INTERRUPT_TIMER_MACHINE << 1)) {
78    bsp_interrupt_handler_dispatch(RISCV_INTERRUPT_VECTOR_TIMER);
79  } else if (mcause == (RISCV_INTERRUPT_EXTERNAL_MACHINE << 1)) {
80    volatile RISCV_PLIC_hart_regs *plic_hart_regs;
81    uint32_t interrupt_index;
82
83    plic_hart_regs = cpu_self->cpu_per_cpu.plic_hart_regs;
84
85    while ((interrupt_index = plic_hart_regs->claim_complete) != 0) {
86      bsp_interrupt_handler_dispatch(
87        RISCV_INTERRUPT_VECTOR_EXTERNAL(interrupt_index)
88      );
89      plic_hart_regs->claim_complete = interrupt_index;
90    }
91  } else if (mcause == (RISCV_INTERRUPT_SOFTWARE_MACHINE << 1)) {
92#ifdef RTEMS_SMP
93    /*
94     * Clear the software interrupt on this processor.  Synchronization of
95     * inter-processor interrupts is done via Per_CPU_Control::message in
96     * _SMP_Inter_processor_interrupt_handler().
97     */
98    *cpu_self->cpu_per_cpu.clint_msip = 0;
99
100    _SMP_Inter_processor_interrupt_handler(cpu_self);
101#else
102    bsp_interrupt_handler_dispatch(RISCV_INTERRUPT_VECTOR_SOFTWARE);
103#endif
104  } else {
105    bsp_fatal(RISCV_FATAL_UNEXPECTED_INTERRUPT_EXCEPTION);
106  }
107}
108
109static void riscv_clint_init(const void *fdt)
110{
111  volatile RISCV_CLINT_regs *clint;
112  int node;
113#ifdef RTEMS_SMP
114  const uint32_t *val;
115  int len;
116  int i;
117#endif
118
119  node = fdt_node_offset_by_compatible(fdt, -1, "riscv,clint0");
120
121  clint = riscv_fdt_get_address(fdt, node);
122  if (clint == NULL) {
123    bsp_fatal(RISCV_FATAL_NO_CLINT_REG_IN_DEVICE_TREE);
124  }
125
126  riscv_clint = clint;
127
128#ifdef RTEMS_SMP
129  val = fdt_getprop(fdt, node, "interrupts-extended", &len);
130
131  for (i = 0; i < len; i += 16) {
132    uint32_t hart_index;
133    Per_CPU_Control *cpu;
134
135    hart_index = riscv_get_hart_index_by_phandle(fdt32_to_cpu(val[i / 4]));
136    if (hart_index >= rtems_configuration_get_maximum_processors()) {
137      continue;
138    }
139
140    cpu = _Per_CPU_Get_by_index(hart_index);
141    cpu->cpu_per_cpu.clint_msip = &clint->msip[i / 16];
142    cpu->cpu_per_cpu.clint_mtimecmp = &clint->mtimecmp[i / 16];
143  }
144#endif
145}
146
147static void riscv_plic_init(const void *fdt)
148{
149  volatile RISCV_PLIC_regs *plic;
150  int node;
151  int i;
152  const uint32_t *val;
153  int len;
154  uint32_t interrupt_index;
155  uint32_t ndev;
156  Per_CPU_Control *cpu;
157
158  node = fdt_node_offset_by_compatible(fdt, -1, "riscv,plic0");
159
160  plic = riscv_fdt_get_address(fdt, node);
161  if (plic == NULL) {
162    bsp_fatal(RISCV_FATAL_NO_PLIC_REG_IN_DEVICE_TREE);
163  }
164
165  riscv_plic = plic;
166
167  val = fdt_getprop(fdt, node, "riscv,ndev", &len);
168  if (val == NULL || len != 4) {
169    bsp_fatal(RISCV_FATAL_INVALID_PLIC_NDEV_IN_DEVICE_TREE);
170  }
171
172  ndev = fdt32_to_cpu(val[0]);
173  if (ndev > RISCV_MAXIMUM_EXTERNAL_INTERRUPTS) {
174    bsp_fatal(RISCV_FATAL_TOO_LARGE_PLIC_NDEV_IN_DEVICE_TREE);
175  }
176
177  val = fdt_getprop(fdt, node, "interrupts-extended", &len);
178
179  for (i = 0; i < len; i += 8) {
180    uint32_t hart_index;
181
182    hart_index = riscv_get_hart_index_by_phandle(fdt32_to_cpu(val[i / 4]));
183    if (hart_index >= rtems_configuration_get_maximum_processors()) {
184      continue;
185    }
186
187    interrupt_index = fdt32_to_cpu(val[i / 4 + 1]);
188    if (interrupt_index != RISCV_INTERRUPT_EXTERNAL_MACHINE) {
189      continue;
190    }
191
192    plic->harts[i / 8].priority_threshold = 0;
193
194    cpu = _Per_CPU_Get_by_index(hart_index);
195    cpu->cpu_per_cpu.plic_hart_regs = &plic->harts[i / 8];
196    cpu->cpu_per_cpu.plic_m_ie = &plic->enable[i / 8][0];
197  }
198
199  cpu = _Per_CPU_Get_by_index(0);
200
201  for (interrupt_index = 1; interrupt_index <= ndev; ++interrupt_index) {
202    plic->priority[interrupt_index] = 1;
203    riscv_plic_irq_to_cpu[interrupt_index - 1] = cpu->cpu_per_cpu.plic_m_ie;
204  }
205
206  /*
207   * External M-mode interrupts on secondary processors are enabled in
208   * bsp_start_on_secondary_processor().
209   */
210  set_csr(mie, MIP_MEIP);
211}
212
213rtems_status_code bsp_interrupt_facility_initialize(void)
214{
215  const void *fdt;
216
217  fdt = bsp_fdt_get();
218  riscv_clint_init(fdt);
219  riscv_plic_init(fdt);
220
221  return RTEMS_SUCCESSFUL;
222}
223
224void bsp_interrupt_vector_enable(rtems_vector_number vector)
225{
226  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
227
228  if (RISCV_INTERRUPT_VECTOR_IS_EXTERNAL(vector)) {
229    uint32_t interrupt_index;
230    volatile uint32_t *enable;
231    uint32_t group;
232    uint32_t bit;
233    rtems_interrupt_lock_context lock_context;
234
235    interrupt_index = RISCV_INTERRUPT_VECTOR_EXTERNAL_TO_INDEX(vector);
236    enable = riscv_plic_irq_to_cpu[interrupt_index - 1];
237    group = interrupt_index / 32;
238    bit = UINT32_C(1) << (interrupt_index % 32);
239
240    rtems_interrupt_lock_acquire(&riscv_plic_lock, &lock_context);
241
242    if (enable != NULL) {
243      enable[group] |= bit;
244    } else {
245      uint32_t cpu_index;
246      uint32_t cpu_count;
247
248      cpu_count = _SMP_Get_processor_count();
249
250      for (cpu_index = 0; cpu_index < cpu_count; ++cpu_index) {
251        Per_CPU_Control *cpu;
252
253        cpu = _Per_CPU_Get_by_index(cpu_index);
254        enable = cpu->cpu_per_cpu.plic_m_ie;
255
256        if (enable != NULL) {
257          enable[group] |= bit;
258        }
259      }
260    }
261
262    rtems_interrupt_lock_release(&riscv_plic_lock, &lock_context);
263  }
264}
265
266void bsp_interrupt_vector_disable(rtems_vector_number vector)
267{
268  bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
269
270  if (RISCV_INTERRUPT_VECTOR_IS_EXTERNAL(vector)) {
271    uint32_t interrupt_index;
272    volatile uint32_t *enable;
273    uint32_t group;
274    uint32_t bit;
275    rtems_interrupt_lock_context lock_context;
276
277    interrupt_index = RISCV_INTERRUPT_VECTOR_EXTERNAL_TO_INDEX(vector);
278    enable = riscv_plic_irq_to_cpu[interrupt_index - 1];
279    group = interrupt_index / 32;
280    bit = UINT32_C(1) << (interrupt_index % 32);
281
282    rtems_interrupt_lock_acquire(&riscv_plic_lock, &lock_context);
283
284    if (enable != NULL) {
285      enable[group] &= ~bit;
286    } else {
287      uint32_t cpu_index;
288      uint32_t cpu_count;
289
290      cpu_count = _SMP_Get_processor_count();
291
292      for (cpu_index = 0; cpu_index < cpu_count; ++cpu_index) {
293        Per_CPU_Control *cpu;
294
295        cpu = _Per_CPU_Get_by_index(cpu_index);
296        enable = cpu->cpu_per_cpu.plic_m_ie;
297
298        if (enable != NULL) {
299          enable[group] &= ~bit;
300        }
301      }
302    }
303
304    rtems_interrupt_lock_release(&riscv_plic_lock, &lock_context);
305  }
306}
307
308void bsp_interrupt_set_affinity(
309  rtems_vector_number vector,
310  const Processor_mask *affinity
311)
312{
313  if (RISCV_INTERRUPT_VECTOR_IS_EXTERNAL(vector)) {
314    uint32_t interrupt_index;
315    Processor_mask mask;
316
317    interrupt_index = RISCV_INTERRUPT_VECTOR_EXTERNAL_TO_INDEX(vector);
318
319    _Processor_mask_And(&mask, affinity, _SMP_Get_online_processors());
320
321    if (_Processor_mask_Is_equal(&mask, _SMP_Get_online_processors())) {
322      riscv_plic_irq_to_cpu[interrupt_index - 1] = NULL;
323      return;
324    }
325
326    if (_Processor_mask_Count(&mask) == 1) {
327      uint32_t cpu_index;
328      Per_CPU_Control *cpu;
329
330      cpu_index = _Processor_mask_Find_last_set(&mask) - 1;
331      cpu = _Per_CPU_Get_by_index(cpu_index);
332      riscv_plic_irq_to_cpu[interrupt_index - 1] = cpu->cpu_per_cpu.plic_m_ie;
333      return;
334    }
335
336    bsp_fatal(RISCV_FATAL_INVALID_INTERRUPT_AFFINITY);
337  }
338}
339
340void bsp_interrupt_get_affinity(
341  rtems_vector_number vector,
342  Processor_mask *affinity
343)
344{
345  _Processor_mask_Zero(affinity);
346
347  if (RISCV_INTERRUPT_VECTOR_IS_EXTERNAL(vector)) {
348    uint32_t interrupt_index;
349    volatile uint32_t *enable;
350
351    interrupt_index = RISCV_INTERRUPT_VECTOR_EXTERNAL_TO_INDEX(vector);
352    enable = riscv_plic_irq_to_cpu[interrupt_index - 1];
353
354    if (enable != NULL) {
355      uint32_t cpu_index;
356      uint32_t cpu_count;
357
358      cpu_count = _SMP_Get_processor_count();
359
360      for (cpu_index = 0; cpu_index < cpu_count; ++cpu_index) {
361        Per_CPU_Control *cpu;
362
363        cpu = _Per_CPU_Get_by_index(cpu_index);
364
365        if (enable == cpu->cpu_per_cpu.plic_m_ie) {
366          _Processor_mask_Set(affinity, cpu_index);
367          break;
368        }
369      }
370    } else {
371      _Processor_mask_Assign(affinity, _SMP_Get_online_processors());
372    }
373  }
374}
Note: See TracBrowser for help on using the repository browser.