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