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 * 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 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 93 softintr_free(void *arg) 94 { 95 struct softintr_handler *sh = arg; 96 97 kmem_free(sh, sizeof(*sh)); 98 } 99 100 void 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 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