source: rtems/bsps/riscv/riscv/irq/irq.c @ 4c740de

5
Last change on this file since 4c740de was 4c740de, checked in by Sebastian Huber <sebastian.huber@…>, on 08/02/18 at 12:13:25

bsp/riscv: Fix build with RTEMS_SMP undefined

Update #3433.

  • Property mode set to 100644
File size: 10.7 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    bsp_fatal(RISCV_FATAL_NO_PLIC_REG_IN_DEVICE_TREE);
165  }
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.