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