1 /* $NetBSD: intc.c,v 1.8 2014/03/31 11:25:49 martin 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 UCHIYAMA Yasushi. 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: intc.c,v 1.8 2014/03/31 11:25:49 martin Exp $"); 34 35 #include "debug_playstation2.h" 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 40 #include <playstation2/ee/eevar.h> 41 #include <playstation2/ee/intcvar.h> 42 #include <playstation2/ee/intcreg.h> 43 #include <playstation2/ee/gsvar.h> /* debug monitor */ 44 45 #include <playstation2/playstation2/interrupt.h> 46 47 #ifdef DEBUG 48 #define LEGAL_CHANNEL(x) ((x) >= 0 && (x) <= 15) 49 #define STATIC 50 #else 51 #define STATIC static 52 #endif 53 54 #define _INTC_NINTR 16 55 56 u_int32_t __intc_enabled_channel; 57 58 STATIC int __intc_initialized; 59 STATIC struct _ipl_dispatcher __intc_dispatcher[_INTC_NINTR]; 60 STATIC struct _ipl_holder __intc_ipl_holder[_IPL_N]; 61 62 STATIC SLIST_HEAD(, _ipl_dispatcher) __intc_dispatcher_head = 63 SLIST_HEAD_INITIALIZER(__intc_dispatcher_head); 64 65 void 66 intc_init(void) 67 { 68 int i; 69 70 if (__intc_initialized++) 71 return; 72 73 /* disable all channel */ 74 for (i = 0; i < _INTC_NINTR; i++) 75 intc_intr_disable(i); 76 77 /* clear interrupts */ 78 _reg_write_4(I_STAT_REG, _reg_read_4(I_STAT_REG)); 79 80 for (i = 0; i < _IPL_N; i++) 81 __intc_ipl_holder[i].mask = 0xffffffff; 82 } 83 84 int 85 intc_intr(u_int32_t mask) 86 { 87 struct _ipl_dispatcher *dispatcher; 88 u_int32_t r, dispatch, pending; 89 90 r = _reg_read_4(I_STAT_REG); 91 dispatch = r & ~mask & __intc_enabled_channel; 92 pending = r & mask & __intc_enabled_channel; 93 94 #if 0 95 __gsfb_print(1, 96 "INTC stat=%08x, mask=%08x, pend=%08x, disp=%08x enable=%08x\n", 97 r, mask, pending, dispatch, __intc_enabled_channel); 98 #endif 99 if (dispatch == 0) 100 return (pending == 0 ? 1 : 0); 101 102 /* clear interrupt */ 103 _reg_write_4(I_STAT_REG, dispatch); 104 105 /* dispatch interrupt handler */ 106 SLIST_FOREACH(dispatcher, &__intc_dispatcher_head, link) { 107 if (dispatcher->bit & dispatch) { 108 KDASSERT(dispatcher->func); 109 (*dispatcher->func)(dispatcher->arg); 110 dispatch &= ~dispatcher->bit; 111 } 112 } 113 114 /* disable spurious interrupt source */ 115 if (dispatch) { 116 int i, bit; 117 for (i = 0, bit = 1; i < _INTC_NINTR; i++, bit <<= 1) { 118 if (bit & dispatch) { 119 intc_intr_disable(i); 120 printf("%s: spurious interrupt %d disabled.\n", 121 __func__, i); 122 } 123 } 124 } 125 126 return (pending == 0 ? 1 : 0); 127 } 128 129 void 130 intc_intr_enable(enum intc_channel ch) 131 { 132 u_int32_t mask; 133 134 KDASSERT(LEGAL_CHANNEL(ch)); 135 mask = 1 << ch; 136 _reg_write_4(I_MASK_REG, (_reg_read_4(I_MASK_REG) & mask) ^ mask); 137 } 138 139 void 140 intc_intr_disable(enum intc_channel ch) 141 { 142 143 KDASSERT(LEGAL_CHANNEL(ch)); 144 _reg_write_4(I_MASK_REG, _reg_read_4(I_MASK_REG) & (1 << ch)); 145 } 146 147 void 148 intc_update_mask(u_int32_t mask) 149 { 150 u_int32_t cur_mask; 151 152 cur_mask = _reg_read_4(I_MASK_REG); 153 154 _reg_write_4(I_MASK_REG, ((cur_mask ^ ~mask) | (cur_mask & mask)) & 155 __intc_enabled_channel); 156 } 157 158 void * 159 intc_intr_establish(enum intc_channel ch, int ipl, int (*func)(void *), 160 void *arg) 161 { 162 struct _ipl_dispatcher *dispatcher = &__intc_dispatcher[ch]; 163 struct _ipl_dispatcher *d; 164 u_int32_t bit; 165 int i, s; 166 167 KDASSERT(dispatcher->func == NULL); 168 169 s = _intr_suspend(); 170 dispatcher->func = func; 171 dispatcher->arg = arg; 172 dispatcher->ipl = ipl; 173 dispatcher->channel = ch; 174 dispatcher->bit = bit = (1 << ch); 175 176 for (i = 0; i < _IPL_N; i++) 177 if (i < ipl) 178 __intc_ipl_holder[i].mask &= ~bit; 179 else 180 __intc_ipl_holder[i].mask |= bit; 181 182 /* insert queue IPL order */ 183 if (SLIST_EMPTY(&__intc_dispatcher_head)) { 184 SLIST_INSERT_HEAD(&__intc_dispatcher_head, dispatcher, link); 185 } else { 186 SLIST_FOREACH(d, &__intc_dispatcher_head, link) { 187 if (SLIST_NEXT(d, link) == 0 || 188 SLIST_NEXT(d, link)->ipl < ipl) { 189 SLIST_INSERT_AFTER(d, dispatcher, link); 190 break; 191 } 192 } 193 } 194 195 md_ipl_register(IPL_INTC, __intc_ipl_holder); 196 197 intc_intr_enable(ch); 198 __intc_enabled_channel |= bit; 199 200 _intr_resume(s); 201 202 return ((void *)ch); 203 } 204 205 void 206 intc_intr_disestablish(void *handle) 207 { 208 int ch = (int)(handle); 209 struct _ipl_dispatcher *dispatcher = &__intc_dispatcher[ch]; 210 u_int32_t bit; 211 int i, s; 212 213 s = _intr_suspend(); 214 215 intc_intr_disable(ch); 216 dispatcher->func = NULL; 217 218 SLIST_REMOVE(&__intc_dispatcher_head, dispatcher, 219 _ipl_dispatcher, link); 220 221 bit = dispatcher->bit; 222 for (i = 0; i < _IPL_N; i++) 223 __intc_ipl_holder[i].mask |= bit; 224 225 md_ipl_register(IPL_INTC, __intc_ipl_holder); 226 __intc_enabled_channel &= ~bit; 227 228 _intr_resume(s); 229 } 230 231