1 1.31 thorpej /* $NetBSD: au_icu.c,v 1.31 2021/01/04 17:35:12 thorpej Exp $ */ 2 1.13 gdamore 3 1.13 gdamore /*- 4 1.13 gdamore * Copyright (c) 2006 Itronix Inc. 5 1.13 gdamore * All rights reserved. 6 1.13 gdamore * 7 1.13 gdamore * Written by Garrett D'Amore for Itronix Inc. 8 1.13 gdamore * 9 1.13 gdamore * Redistribution and use in source and binary forms, with or without 10 1.13 gdamore * modification, are permitted provided that the following conditions 11 1.13 gdamore * are met: 12 1.13 gdamore * 1. Redistributions of source code must retain the above copyright 13 1.13 gdamore * notice, this list of conditions and the following disclaimer. 14 1.13 gdamore * 2. Redistributions in binary form must reproduce the above copyright 15 1.13 gdamore * notice, this list of conditions and the following disclaimer in the 16 1.13 gdamore * documentation and/or other materials provided with the distribution. 17 1.13 gdamore * 3. The name of Itronix Inc. may not be used to endorse 18 1.13 gdamore * or promote products derived from this software without specific 19 1.13 gdamore * prior written permission. 20 1.13 gdamore * 21 1.13 gdamore * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND 22 1.13 gdamore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 1.13 gdamore * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 1.13 gdamore * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY 25 1.13 gdamore * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 1.13 gdamore * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 1.13 gdamore * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 1.13 gdamore * ON ANY THEORY OF LIABILITY, WHETHER IN 29 1.13 gdamore * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 1.13 gdamore * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 1.13 gdamore * POSSIBILITY OF SUCH DAMAGE. 32 1.13 gdamore */ 33 1.1 simonb 34 1.1 simonb /*- 35 1.1 simonb * Copyright (c) 2001 The NetBSD Foundation, Inc. 36 1.1 simonb * All rights reserved. 37 1.1 simonb * 38 1.1 simonb * This code is derived from software contributed to The NetBSD Foundation 39 1.1 simonb * by Jason R. Thorpe. 40 1.1 simonb * 41 1.1 simonb * Redistribution and use in source and binary forms, with or without 42 1.1 simonb * modification, are permitted provided that the following conditions 43 1.1 simonb * are met: 44 1.1 simonb * 1. Redistributions of source code must retain the above copyright 45 1.1 simonb * notice, this list of conditions and the following disclaimer. 46 1.1 simonb * 2. Redistributions in binary form must reproduce the above copyright 47 1.1 simonb * notice, this list of conditions and the following disclaimer in the 48 1.1 simonb * documentation and/or other materials provided with the distribution. 49 1.1 simonb * 50 1.1 simonb * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 51 1.1 simonb * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 52 1.1 simonb * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53 1.1 simonb * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 54 1.1 simonb * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 55 1.1 simonb * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 56 1.1 simonb * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 57 1.1 simonb * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 58 1.1 simonb * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 59 1.1 simonb * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 60 1.1 simonb * POSSIBILITY OF SUCH DAMAGE. 61 1.1 simonb */ 62 1.1 simonb 63 1.1 simonb /* 64 1.1 simonb * Interrupt support for the Alchemy Semiconductor Au1x00 CPUs. 65 1.1 simonb * 66 1.1 simonb * The Alchemy Semiconductor Au1x00's interrupts are wired to two internal 67 1.1 simonb * interrupt controllers. 68 1.1 simonb */ 69 1.7 lukem 70 1.7 lukem #include <sys/cdefs.h> 71 1.31 thorpej __KERNEL_RCSID(0, "$NetBSD: au_icu.c,v 1.31 2021/01/04 17:35:12 thorpej Exp $"); 72 1.1 simonb 73 1.1 simonb #include "opt_ddb.h" 74 1.26 matt #define __INTR_PRIVATE 75 1.1 simonb 76 1.1 simonb #include <sys/param.h> 77 1.28 matt #include <sys/bus.h> 78 1.28 matt #include <sys/device.h> 79 1.28 matt #include <sys/intr.h> 80 1.28 matt #include <sys/kernel.h> 81 1.31 thorpej #include <sys/kmem.h> 82 1.1 simonb #include <sys/systm.h> 83 1.1 simonb 84 1.1 simonb #include <mips/locore.h> 85 1.1 simonb #include <mips/alchemy/include/aureg.h> 86 1.1 simonb #include <mips/alchemy/include/auvar.h> 87 1.1 simonb 88 1.14 gdamore #define REGVAL(x) *((volatile uint32_t *)(MIPS_PHYS_TO_KSEG1((x)))) 89 1.1 simonb 90 1.1 simonb /* 91 1.1 simonb * This is a mask of bits to clear in the SR when we go to a 92 1.1 simonb * given hardware interrupt priority level. 93 1.1 simonb */ 94 1.1 simonb 95 1.26 matt static const struct ipl_sr_map alchemy_ipl_sr_map = { 96 1.26 matt .sr_bits = { 97 1.26 matt [IPL_NONE] = 0, 98 1.26 matt [IPL_SOFTCLOCK] = MIPS_SOFT_INT_MASK_0, 99 1.26 matt [IPL_SOFTBIO] = MIPS_SOFT_INT_MASK_0, 100 1.26 matt [IPL_SOFTNET] = MIPS_SOFT_INT_MASK, 101 1.26 matt [IPL_SOFTSERIAL] = MIPS_SOFT_INT_MASK, 102 1.29 kiyohara [IPL_VM] = MIPS_SOFT_INT_MASK | 103 1.29 kiyohara MIPS_INT_MASK_0 | 104 1.29 kiyohara MIPS_INT_MASK_1 | 105 1.29 kiyohara MIPS_INT_MASK_2 | 106 1.29 kiyohara MIPS_INT_MASK_3, 107 1.26 matt [IPL_SCHED] = MIPS_INT_MASK, 108 1.29 kiyohara [IPL_DDB] = MIPS_INT_MASK, 109 1.26 matt [IPL_HIGH] = MIPS_INT_MASK, 110 1.26 matt }, 111 1.1 simonb }; 112 1.1 simonb 113 1.1 simonb #define NIRQS 64 114 1.1 simonb 115 1.13 gdamore struct au_icu_intrhead { 116 1.1 simonb struct evcnt intr_count; 117 1.1 simonb int intr_refcnt; 118 1.1 simonb }; 119 1.13 gdamore struct au_icu_intrhead au_icu_intrtab[NIRQS]; 120 1.1 simonb 121 1.1 simonb #define NINTRS 4 /* MIPS INT0 - INT3 */ 122 1.1 simonb 123 1.14 gdamore struct au_intrhand { 124 1.14 gdamore LIST_ENTRY(au_intrhand) ih_q; 125 1.14 gdamore int (*ih_func)(void *); 126 1.14 gdamore void *ih_arg; 127 1.14 gdamore int ih_irq; 128 1.19 gdamore int ih_mask; 129 1.14 gdamore }; 130 1.14 gdamore 131 1.13 gdamore struct au_cpuintr { 132 1.14 gdamore LIST_HEAD(, au_intrhand) cintr_list; 133 1.1 simonb struct evcnt cintr_count; 134 1.1 simonb }; 135 1.1 simonb 136 1.13 gdamore struct au_cpuintr au_cpuintrs[NINTRS]; 137 1.26 matt const char * const au_cpuintrnames[NINTRS] = { 138 1.1 simonb "icu 0, req 0", 139 1.1 simonb "icu 0, req 1", 140 1.1 simonb "icu 1, req 0", 141 1.1 simonb "icu 1, req 1", 142 1.1 simonb }; 143 1.1 simonb 144 1.13 gdamore static bus_addr_t ic0_base, ic1_base; 145 1.13 gdamore 146 1.1 simonb void 147 1.1 simonb au_intr_init(void) 148 1.1 simonb { 149 1.26 matt ipl_sr_map = alchemy_ipl_sr_map; 150 1.1 simonb 151 1.26 matt for (size_t i = 0; i < NINTRS; i++) { 152 1.13 gdamore LIST_INIT(&au_cpuintrs[i].cintr_list); 153 1.13 gdamore evcnt_attach_dynamic(&au_cpuintrs[i].cintr_count, 154 1.13 gdamore EVCNT_TYPE_INTR, NULL, "mips", au_cpuintrnames[i]); 155 1.1 simonb } 156 1.1 simonb 157 1.26 matt struct au_chipdep * const chip = au_chipdep(); 158 1.13 gdamore KASSERT(chip != NULL); 159 1.13 gdamore 160 1.13 gdamore ic0_base = chip->icus[0]; 161 1.13 gdamore ic1_base = chip->icus[1]; 162 1.13 gdamore 163 1.26 matt for (size_t i = 0; i < NIRQS; i++) { 164 1.13 gdamore au_icu_intrtab[i].intr_refcnt = 0; 165 1.13 gdamore evcnt_attach_dynamic(&au_icu_intrtab[i].intr_count, 166 1.13 gdamore EVCNT_TYPE_INTR, NULL, chip->name, chip->irqnames[i]); 167 1.1 simonb } 168 1.19 gdamore 169 1.19 gdamore /* start with all interrupts masked */ 170 1.19 gdamore REGVAL(ic0_base + IC_MASK_CLEAR) = 0xffffffff; 171 1.19 gdamore REGVAL(ic0_base + IC_WAKEUP_CLEAR) = 0xffffffff; 172 1.19 gdamore REGVAL(ic0_base + IC_SOURCE_SET) = 0xffffffff; 173 1.19 gdamore REGVAL(ic0_base + IC_RISING_EDGE) = 0xffffffff; 174 1.19 gdamore REGVAL(ic0_base + IC_FALLING_EDGE) = 0xffffffff; 175 1.19 gdamore REGVAL(ic0_base + IC_TEST_BIT) = 0; 176 1.19 gdamore 177 1.19 gdamore REGVAL(ic1_base + IC_MASK_CLEAR) = 0xffffffff; 178 1.19 gdamore REGVAL(ic1_base + IC_WAKEUP_CLEAR) = 0xffffffff; 179 1.19 gdamore REGVAL(ic1_base + IC_SOURCE_SET) = 0xffffffff; 180 1.19 gdamore REGVAL(ic1_base + IC_RISING_EDGE) = 0xffffffff; 181 1.19 gdamore REGVAL(ic1_base + IC_FALLING_EDGE) = 0xffffffff; 182 1.19 gdamore REGVAL(ic1_base + IC_TEST_BIT) = 0; 183 1.1 simonb } 184 1.1 simonb 185 1.1 simonb void * 186 1.1 simonb au_intr_establish(int irq, int req, int level, int type, 187 1.1 simonb int (*func)(void *), void *arg) 188 1.1 simonb { 189 1.14 gdamore struct au_intrhand *ih; 190 1.13 gdamore uint32_t icu_base; 191 1.13 gdamore int cpu_int, s; 192 1.13 gdamore struct au_chipdep *chip; 193 1.13 gdamore 194 1.13 gdamore chip = au_chipdep(); 195 1.13 gdamore KASSERT(chip != NULL); 196 1.1 simonb 197 1.1 simonb if (irq >= NIRQS) 198 1.3 provos panic("au_intr_establish: bogus IRQ %d", irq); 199 1.1 simonb if (req > 1) 200 1.3 provos panic("au_intr_establish: bogus request %d", req); 201 1.1 simonb 202 1.31 thorpej ih = kmem_alloc(sizeof(*ih), KM_SLEEP); 203 1.1 simonb ih->ih_func = func; 204 1.1 simonb ih->ih_arg = arg; 205 1.1 simonb ih->ih_irq = irq; 206 1.19 gdamore ih->ih_mask = (1 << (irq & 31)); 207 1.1 simonb 208 1.1 simonb s = splhigh(); 209 1.1 simonb 210 1.1 simonb /* 211 1.1 simonb * First, link it into the tables. 212 1.1 simonb * XXX do we want a separate list (really, should only be one item, not 213 1.9 wiz * a list anyway) per irq, not per CPU interrupt? 214 1.1 simonb */ 215 1.20 gdamore cpu_int = (irq < 32 ? 0 : 2) + req; 216 1.13 gdamore LIST_INSERT_HEAD(&au_cpuintrs[cpu_int].cintr_list, ih, ih_q); 217 1.1 simonb 218 1.1 simonb /* 219 1.1 simonb * Now enable it. 220 1.1 simonb */ 221 1.13 gdamore if (au_icu_intrtab[irq].intr_refcnt++ == 0) { 222 1.13 gdamore icu_base = (irq < 32) ? ic0_base : ic1_base; 223 1.1 simonb 224 1.1 simonb irq &= 31; /* throw away high bit if set */ 225 1.1 simonb irq = 1 << irq; /* only used as a mask from here on */ 226 1.1 simonb 227 1.13 gdamore /* XXX Only level interrupts for now */ 228 1.1 simonb switch (type) { 229 1.1 simonb case IST_NONE: 230 1.1 simonb case IST_PULSE: 231 1.1 simonb case IST_EDGE: 232 1.1 simonb panic("unsupported irq type %d", type); 233 1.5 hpeyerl /* NOTREACHED */ 234 1.1 simonb case IST_LEVEL: 235 1.5 hpeyerl case IST_LEVEL_HIGH: 236 1.1 simonb REGVAL(icu_base + IC_CONFIG2_SET) = irq; 237 1.1 simonb REGVAL(icu_base + IC_CONFIG1_CLEAR) = irq; 238 1.1 simonb REGVAL(icu_base + IC_CONFIG0_SET) = irq; 239 1.5 hpeyerl break; 240 1.5 hpeyerl case IST_LEVEL_LOW: 241 1.5 hpeyerl REGVAL(icu_base + IC_CONFIG2_SET) = irq; 242 1.5 hpeyerl REGVAL(icu_base + IC_CONFIG1_SET) = irq; 243 1.5 hpeyerl REGVAL(icu_base + IC_CONFIG0_CLEAR) = irq; 244 1.5 hpeyerl break; 245 1.1 simonb } 246 1.18 gdamore wbflush(); 247 1.1 simonb 248 1.1 simonb /* XXX handle GPIO interrupts - not done at all yet */ 249 1.10 he if (cpu_int & 0x1) 250 1.1 simonb REGVAL(icu_base + IC_ASSIGN_REQUEST_CLEAR) = irq; 251 1.1 simonb else 252 1.1 simonb REGVAL(icu_base + IC_ASSIGN_REQUEST_SET) = irq; 253 1.1 simonb 254 1.1 simonb /* Associate interrupt with peripheral */ 255 1.1 simonb REGVAL(icu_base + IC_SOURCE_SET) = irq; 256 1.1 simonb 257 1.1 simonb /* Actually enable the interrupt */ 258 1.1 simonb REGVAL(icu_base + IC_MASK_SET) = irq; 259 1.1 simonb 260 1.1 simonb /* And allow the interrupt to interrupt idle */ 261 1.1 simonb REGVAL(icu_base + IC_WAKEUP_SET) = irq; 262 1.18 gdamore 263 1.18 gdamore wbflush(); 264 1.1 simonb } 265 1.1 simonb splx(s); 266 1.1 simonb 267 1.1 simonb return (ih); 268 1.1 simonb } 269 1.1 simonb 270 1.1 simonb void 271 1.1 simonb au_intr_disestablish(void *cookie) 272 1.1 simonb { 273 1.14 gdamore struct au_intrhand *ih = cookie; 274 1.1 simonb uint32_t icu_base; 275 1.1 simonb int irq, s; 276 1.1 simonb 277 1.1 simonb irq = ih->ih_irq; 278 1.1 simonb 279 1.1 simonb s = splhigh(); 280 1.1 simonb 281 1.1 simonb /* 282 1.1 simonb * First, remove it from the table. 283 1.1 simonb */ 284 1.1 simonb LIST_REMOVE(ih, ih_q); 285 1.1 simonb 286 1.1 simonb /* 287 1.1 simonb * Now, disable it, if there is nothing remaining on the 288 1.1 simonb * list. 289 1.1 simonb */ 290 1.13 gdamore if (au_icu_intrtab[irq].intr_refcnt-- == 1) { 291 1.13 gdamore icu_base = (irq < 32) ? ic0_base : ic1_base; 292 1.1 simonb 293 1.1 simonb irq &= 31; /* throw away high bit if set */ 294 1.1 simonb irq = 1 << irq; /* only used as a mask from here on */ 295 1.1 simonb 296 1.1 simonb REGVAL(icu_base + IC_CONFIG2_CLEAR) = irq; 297 1.1 simonb REGVAL(icu_base + IC_CONFIG1_CLEAR) = irq; 298 1.1 simonb REGVAL(icu_base + IC_CONFIG0_CLEAR) = irq; 299 1.1 simonb 300 1.15 gdamore /* disable with MASK_CLEAR and WAKEUP_CLEAR */ 301 1.15 gdamore REGVAL(icu_base + IC_MASK_CLEAR) = irq; 302 1.15 gdamore REGVAL(icu_base + IC_WAKEUP_CLEAR) = irq; 303 1.18 gdamore wbflush(); 304 1.1 simonb } 305 1.1 simonb 306 1.1 simonb splx(s); 307 1.1 simonb 308 1.31 thorpej kmem_free(ih, sizeof(*ih)); 309 1.1 simonb } 310 1.1 simonb 311 1.1 simonb void 312 1.26 matt au_iointr(int ipl, vaddr_t pc, uint32_t ipending) 313 1.1 simonb { 314 1.14 gdamore struct au_intrhand *ih; 315 1.1 simonb int level; 316 1.17 simonb uint32_t icu_base, irqstat, irqmask; 317 1.17 simonb 318 1.19 gdamore icu_base = irqstat = 0; 319 1.1 simonb 320 1.1 simonb for (level = 3; level >= 0; level--) { 321 1.1 simonb if ((ipending & (MIPS_INT_MASK_0 << level)) == 0) 322 1.1 simonb continue; 323 1.1 simonb 324 1.1 simonb /* 325 1.1 simonb * XXX the following may well be slow to execute. 326 1.1 simonb * investigate and possibly speed up. 327 1.1 simonb * 328 1.1 simonb * is something like: 329 1.1 simonb * 330 1.17 simonb * irqstat = REGVAL( 331 1.1 simonb * (level & 4 == 0) ? IC0_BASE ? IC1_BASE + 332 1.1 simonb * (level & 2 == 0) ? IC_REQUEST0_INT : IC_REQUEST1_INT); 333 1.1 simonb * 334 1.1 simonb * be any better? 335 1.1 simonb * 336 1.1 simonb */ 337 1.1 simonb switch (level) { 338 1.1 simonb case 0: 339 1.13 gdamore icu_base = ic0_base; 340 1.15 gdamore irqstat = REGVAL(icu_base + IC_REQUEST0_INT); 341 1.1 simonb break; 342 1.1 simonb case 1: 343 1.13 gdamore icu_base = ic0_base; 344 1.15 gdamore irqstat = REGVAL(icu_base + IC_REQUEST1_INT); 345 1.1 simonb break; 346 1.1 simonb case 2: 347 1.13 gdamore icu_base = ic1_base; 348 1.15 gdamore irqstat = REGVAL(icu_base + IC_REQUEST0_INT); 349 1.1 simonb break; 350 1.1 simonb case 3: 351 1.13 gdamore icu_base = ic1_base; 352 1.15 gdamore irqstat = REGVAL(icu_base + IC_REQUEST1_INT); 353 1.1 simonb break; 354 1.1 simonb } 355 1.15 gdamore irqmask = REGVAL(icu_base + IC_MASK_READ); 356 1.13 gdamore au_cpuintrs[level].cintr_count.ev_count++; 357 1.13 gdamore LIST_FOREACH(ih, &au_cpuintrs[level].cintr_list, ih_q) { 358 1.19 gdamore int mask = ih->ih_mask; 359 1.15 gdamore 360 1.19 gdamore if (mask & irqmask & irqstat) { 361 1.13 gdamore au_icu_intrtab[ih->ih_irq].intr_count.ev_count++; 362 1.1 simonb (*ih->ih_func)(ih->ih_arg); 363 1.1 simonb 364 1.19 gdamore if (REGVAL(icu_base + IC_MASK_READ) & mask) { 365 1.19 gdamore REGVAL(icu_base + IC_MASK_CLEAR) = mask; 366 1.19 gdamore REGVAL(icu_base + IC_MASK_SET) = mask; 367 1.18 gdamore wbflush(); 368 1.16 gdamore } 369 1.1 simonb } 370 1.1 simonb } 371 1.1 simonb } 372 1.1 simonb } 373 1.15 gdamore 374 1.15 gdamore /* 375 1.15 gdamore * Some devices (e.g. PCMCIA) want to be able to mask interrupts at 376 1.15 gdamore * the ICU, and leave them masked off until some later time 377 1.15 gdamore * (e.g. reenabled by a soft interrupt). 378 1.15 gdamore */ 379 1.15 gdamore 380 1.15 gdamore void 381 1.15 gdamore au_intr_enable(int irq) 382 1.15 gdamore { 383 1.15 gdamore int s; 384 1.15 gdamore uint32_t icu_base, mask; 385 1.15 gdamore 386 1.15 gdamore if (irq >= NIRQS) 387 1.15 gdamore panic("au_intr_enable: bogus IRQ %d", irq); 388 1.15 gdamore 389 1.15 gdamore icu_base = (irq < 32) ? ic0_base : ic1_base; 390 1.15 gdamore mask = irq & 31; 391 1.15 gdamore mask = 1 << mask; 392 1.15 gdamore 393 1.15 gdamore s = splhigh(); 394 1.15 gdamore /* only enable the interrupt if we have a handler */ 395 1.18 gdamore if (au_icu_intrtab[irq].intr_refcnt) { 396 1.15 gdamore REGVAL(icu_base + IC_MASK_SET) = mask; 397 1.18 gdamore REGVAL(icu_base + IC_WAKEUP_SET) = mask; 398 1.18 gdamore wbflush(); 399 1.18 gdamore } 400 1.15 gdamore splx(s); 401 1.15 gdamore } 402 1.15 gdamore 403 1.15 gdamore void 404 1.15 gdamore au_intr_disable(int irq) 405 1.15 gdamore { 406 1.15 gdamore int s; 407 1.15 gdamore uint32_t icu_base, mask; 408 1.15 gdamore 409 1.18 gdamore if (irq >= NIRQS) 410 1.18 gdamore panic("au_intr_disable: bogus IRQ %d", irq); 411 1.18 gdamore 412 1.15 gdamore icu_base = (irq < 32) ? ic0_base : ic1_base; 413 1.15 gdamore mask = irq & 31; 414 1.15 gdamore mask = 1 << mask; 415 1.15 gdamore 416 1.15 gdamore s = splhigh(); 417 1.15 gdamore REGVAL(icu_base + IC_MASK_CLEAR) = mask; 418 1.18 gdamore REGVAL(icu_base + IC_WAKEUP_CLEAR) = mask; 419 1.18 gdamore wbflush(); 420 1.15 gdamore splx(s); 421 1.15 gdamore } 422