1 /*
2 * Copyright (c) 2019, Xilinx Inc. and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <metal/atomic.h>
8 #include <metal/errno.h>
9 #include <metal/irq.h>
10 #include <metal/irq_controller.h>
11 #include <metal/log.h>
12 #include <metal/sys.h>
13 #include <metal/softirq.h>
14 #include <metal/utilities.h>
15 #include <string.h>
16
17 #define METAL_SOFTIRQ_NUM 64
18
19 #define METAL_SOFTIRQ_ARRAY_DECLARE(num) \
20 static const int metal_softirq_num = num; \
21 static struct metal_irq metal_softirqs[num]; \
22 static atomic_char metal_softirq_pending[num]; \
23 static atomic_char metal_softirq_enabled[num];
24
25 static int metal_softirq_avail;
METAL_SOFTIRQ_ARRAY_DECLARE(METAL_SOFTIRQ_NUM)26 METAL_SOFTIRQ_ARRAY_DECLARE(METAL_SOFTIRQ_NUM)
27
28 static void metal_softirq_set_enable(struct metal_irq_controller *cntr,
29 int irq, unsigned int enable)
30 {
31 if (irq < cntr->irq_base ||
32 irq >= (cntr->irq_base + cntr->irq_num)) {
33 return;
34 }
35
36 irq -= cntr->irq_base;
37 if (enable == METAL_IRQ_ENABLE) {
38 atomic_store(&metal_softirq_enabled[irq], 1);
39 } else {
40 atomic_store(&metal_softirq_enabled[irq], 0);
41 }
42 }
43
44 static METAL_IRQ_CONTROLLER_DECLARE(metal_softirq_cntr,
45 METAL_IRQ_ANY, METAL_SOFTIRQ_NUM,
46 NULL,
47 metal_softirq_set_enable, NULL,
48 metal_softirqs);
49
metal_softirq_set(int irq)50 void metal_softirq_set(int irq)
51 {
52 struct metal_irq_controller *cntr;
53
54 cntr = &metal_softirq_cntr;
55
56 if (irq < cntr->irq_base ||
57 irq >= (cntr->irq_base + cntr->irq_num)) {
58 return;
59 }
60
61 irq -= cntr->irq_base;
62 atomic_store(&metal_softirq_pending[irq], 1);
63 }
64
metal_softirq_init(void)65 int metal_softirq_init(void)
66 {
67 return metal_irq_register_controller(&metal_softirq_cntr);
68 }
69
metal_softirq_allocate(int num)70 int metal_softirq_allocate(int num)
71 {
72 int irq_base;
73
74 if ((metal_softirq_avail + num) >= metal_softirq_num) {
75 metal_log(METAL_LOG_ERROR, "No more available soft irqs\n");
76 return -EINVAL;
77 }
78 irq_base = metal_softirq_avail;
79 irq_base += metal_softirq_cntr.irq_base;
80 metal_softirq_avail += num;
81 return irq_base;
82 }
83
metal_softirq_dispatch(void)84 void metal_softirq_dispatch(void)
85 {
86 int i;
87
88 for (i = 0; i < metal_softirq_num; i++) {
89 struct metal_irq *irq;
90 char is_pending = 1;
91
92 if (atomic_load(&metal_softirq_enabled[i]) != 0 &&
93 atomic_compare_exchange_strong(&metal_softirq_pending[i],
94 &is_pending, 0)) {
95 irq = &metal_softirqs[i];
96 (void)metal_irq_handle(irq,
97 i + metal_softirq_cntr.irq_base);
98 }
99 }
100 }
101