1 /* $NetBSD: softintr.c,v 1.18 2020/11/21 21:27:09 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2001 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by IWAMOTO Toshihiro.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: softintr.c,v 1.18 2020/11/21 21:27:09 thorpej Exp $");
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/syslog.h>
38 #include <sys/kmem.h>
39
40 #include <machine/cpu.h>
41 #include <arm/cpufunc.h>
42 #include <machine/intr.h>
43
44 void softintr_free(void *);
45 void softintr_dispatch(int);
46
47 struct softintr_handler {
48 struct softintr_handler *sh_vlink; /* vertical link */
49 struct softintr_handler *sh_hlink; /* horizontal link */
50
51 void (*sh_fun)(void *);
52 void *sh_arg;
53 int sh_level;
54
55 int sh_pending;
56 };
57
58 struct softintr_handler *softintrs;
59 struct softintr_handler *softintr_pending;
60
61 void *
softintr_establish(int level,void (* fun)(void *),void * arg)62 softintr_establish(int level, void (*fun)(void *), void *arg)
63 {
64 struct softintr_handler *sh;
65
66 sh = kmem_alloc(sizeof(*sh), KM_SLEEP);
67 sh->sh_fun = fun;
68 sh->sh_level = ipl_to_spl(level);
69 sh->sh_arg = arg;
70 sh->sh_pending = 0;
71
72 return sh;
73 }
74
75 void
softintr_disestablish(void * cookie)76 softintr_disestablish(void *cookie)
77 {
78 struct softintr_handler *sh = cookie;
79 u_int saved_cpsr;
80
81 saved_cpsr = SetCPSR(I32_bit, I32_bit);
82 if (sh->sh_pending) {
83 sh->sh_fun = softintr_free;
84 sh->sh_arg = sh;
85 SetCPSR(I32_bit, I32_bit & saved_cpsr);
86 } else {
87 SetCPSR(I32_bit, I32_bit & saved_cpsr);
88 kmem_free(sh, sizeof(*sh));
89 }
90 }
91
92 void
softintr_free(void * arg)93 softintr_free(void *arg)
94 {
95 struct softintr_handler *sh = arg;
96
97 kmem_free(sh, sizeof(*sh));
98 }
99
100 void
softintr_schedule(void * cookie)101 softintr_schedule(void *cookie)
102 {
103 struct softintr_handler **p, *sh = cookie;
104 register int pending, saved_cpsr;
105
106 pending = 1;
107 __asm("swp %0, %0, [%1]" : "+r" (pending) : "r" (&sh->sh_pending));
108
109 if (pending)
110 return;
111
112 sh->sh_vlink = NULL;
113 sh->sh_hlink = NULL;
114
115 #ifdef __GNUC__
116 __asm volatile("mrs %0, cpsr\n orr r1, %0, %1\n msr cpsr_a;;, r1" :
117 "=r" (saved_cpsr) : "i" (I32_bit) : "r1");
118 #else
119 saved_cpsr = SetCPSR(I32_bit, I32_bit);
120 #endif
121 p = &softintr_pending;
122
123 for (;; p = &(*p)->sh_vlink) {
124 if (*p == NULL)
125 goto set_and_exit;
126
127 if ((*p)->sh_level <= sh->sh_level)
128 break;
129 }
130
131 if ((*p)->sh_level == sh->sh_level) {
132 sh->sh_hlink = *p;
133 sh->sh_vlink = (*p)->sh_vlink;
134 goto set_and_exit;
135 }
136
137 sh->sh_vlink = *p;
138 set_and_exit:
139 *p = sh;
140 #ifdef __GNUC__
141 __asm volatile("msr cpsr_c, %0" : : "r" (saved_cpsr));
142 #else
143 SetCPSR(I32_bit, I32_bit & saved_cpsr);
144 #endif
145 return;
146 }
147
148 void
softintr_dispatch(int s)149 softintr_dispatch(int s)
150 {
151 struct softintr_handler *sh, *sh1;
152 register int saved_cpsr;
153
154 while (1) {
155 /* Protect list operation from interrupts */
156 #ifdef __GNUC__
157 __asm volatile("mrs %0, cpsr\n orr r1, %0, %1\n"
158 " msr cpsr_all, r1" : "=r" (saved_cpsr) :
159 "i" (I32_bit) : "r1");
160 #else
161 saved_cpsr = SetCPSR(I32_bit, I32_bit);
162 #endif
163
164 if (softintr_pending == NULL ||
165 softintr_pending->sh_level <= s) {
166 #ifdef __GNUC__
167 __asm("msr cpsr_c, %0" : : "r" (saved_cpsr));
168 #else
169 SetCPSR(I32_bit, I32_bit & saved_cpsr);
170 #endif
171 splx(s);
172 return;
173 }
174 sh = softintr_pending;
175 softintr_pending = softintr_pending->sh_vlink;
176
177 if (sh->sh_level > current_spl_level)
178 raisespl(sh->sh_level);
179 #ifdef __GNUC__
180 __asm volatile("msr cpsr_c, %0" : : "r" (saved_cpsr));
181 #else
182 SetCPSR(I32_bit, I32_bit & saved_cpsr);
183 #endif
184 if (sh->sh_level < current_spl_level)
185 lowerspl(sh->sh_level);
186
187 while (1) {
188 /* The order is important */
189 sh1 = sh->sh_hlink;
190 sh->sh_pending = 0;
191
192 (sh->sh_fun)(sh->sh_arg);
193 if (sh1 == NULL)
194 break;
195 sh = sh1;
196 }
197 }
198 splx(s);
199 }
200