xref: /netbsd-src/sys/arch/hpcarm/hpcarm/softintr.c (revision ca53e06c953aeefc61765ee0aca815883ef3764d)
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