1 /* $NetBSD: dmac.c,v 1.2 2001/11/14 18:15:31 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 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include "debug_playstation2.h" 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 44 #include <mips/cache.h> 45 46 #include <playstation2/ee/eevar.h> 47 #include <playstation2/ee/dmacvar.h> 48 #include <playstation2/ee/dmacreg.h> 49 #include <playstation2/ee/gsvar.h> /* debug monitor */ 50 51 #include <playstation2/playstation2/interrupt.h> 52 53 #ifdef DEBUG 54 #define LEGAL_CHANNEL(x) ((x) >= 0 && (x) <= 15) 55 #define STATIC 56 #else 57 #define STATIC static 58 #endif 59 60 #define _DMAC_NINTR 10 61 62 STATIC vaddr_t __dmac_channel_base[_DMAC_NINTR] = { 63 D0_REGBASE, 64 D1_REGBASE, 65 D2_REGBASE, 66 D3_REGBASE, 67 D4_REGBASE, 68 D5_REGBASE, 69 D6_REGBASE, 70 D7_REGBASE, 71 D8_REGBASE, 72 D9_REGBASE 73 }; 74 75 u_int32_t __dmac_enabled_channel; 76 77 STATIC int __dmac_intialized; 78 STATIC struct _ipl_dispatcher __dmac_dispatcher[_DMAC_NINTR]; 79 STATIC struct _ipl_holder __dmac_ipl_holder[_IPL_N]; 80 STATIC SLIST_HEAD(, _ipl_dispatcher) __dmac_dispatcher_head = 81 SLIST_HEAD_INITIALIZER(__dmac_dispatcher_head); 82 83 void 84 dmac_init() 85 { 86 int i; 87 88 if (__dmac_intialized++) 89 return; 90 91 /* disable DMAC */ 92 _reg_write_4(D_ENABLEW_REG, D_ENABLE_SUSPEND); 93 94 /* disable all interrupt */ 95 for (i = 0; i < _DMAC_NINTR; i++) 96 dmac_intr_disable(i); 97 98 for (i = 0; i < _IPL_N; i++) 99 __dmac_ipl_holder[i].mask = 0xffffffff; 100 101 if (_reg_read_4(D_STAT_REG) & D_STAT_SIM) 102 _reg_write_4(D_STAT_REG, D_STAT_SIM); 103 if (_reg_read_4(D_STAT_REG) & D_STAT_MEIM) 104 _reg_write_4(D_STAT_REG, D_STAT_MEIM); 105 106 /* clear all status */ 107 _reg_write_4(D_STAT_REG, _reg_read_4(D_STAT_REG) & D_STAT_CIS_MASK); 108 109 /* enable DMAC */ 110 _reg_write_4(D_ENABLEW_REG, 0); 111 _reg_write_4(D_CTRL_REG, D_CTRL_DMAE); 112 } 113 114 /* 115 * Interrupt 116 */ 117 int 118 dmac_intr(u_int32_t mask) 119 { 120 struct _ipl_dispatcher *dispatcher; 121 u_int32_t r, dispatch, pending; 122 123 r = _reg_read_4(D_STAT_REG); 124 mask = D_STAT_CIM(mask); 125 dispatch = r & ~mask & __dmac_enabled_channel; 126 pending = r & mask & __dmac_enabled_channel; 127 #if 0 128 __gsfb_print(2, 129 "DMAC stat=%08x, mask=%08x, pend=%08x, disp=%08x enable=%08x\n", 130 r, mask, pending, dispatch, __dmac_enabled_channel); 131 #endif 132 if (dispatch == 0) 133 return (pending == 0 ? 1 : 0); 134 135 /* clear interrupt */ 136 _reg_write_4(D_STAT_REG, dispatch); 137 138 /* dispatch interrupt handler */ 139 SLIST_FOREACH(dispatcher, &__dmac_dispatcher_head, link) { 140 if (dispatcher->bit & dispatch) { 141 KDASSERT(dispatcher->func); 142 (*dispatcher->func)(dispatcher->arg); 143 dispatch &= ~dispatcher->bit; 144 } 145 } 146 147 /* disable spurious interrupt source */ 148 if (dispatch) { 149 int i, bit; 150 for (i = 0, bit = 1; i < _DMAC_NINTR; i++, bit <<= 1) { 151 if (bit & dispatch) { 152 dmac_intr_disable(i); 153 printf("%s: spurious interrupt %d disabled.\n", 154 __FUNCTION__, i); 155 } 156 } 157 } 158 159 160 return (pending == 0 ? 1 : 0); 161 } 162 163 void 164 dmac_intr_enable(enum dmac_channel ch) 165 { 166 u_int32_t mask; 167 168 KDASSERT(LEGAL_CHANNEL(ch)); 169 170 mask = D_STAT_CIM_BIT(ch); 171 _reg_write_4(D_STAT_REG, (_reg_read_4(D_STAT_REG) & mask) ^ mask); 172 } 173 174 void 175 dmac_intr_disable(enum dmac_channel ch) 176 { 177 KDASSERT(LEGAL_CHANNEL(ch)); 178 179 _reg_write_4(D_STAT_REG, _reg_read_4(D_STAT_REG) & D_STAT_CIM_BIT(ch)); 180 } 181 182 void 183 dmac_update_mask(u_int32_t mask) 184 { 185 u_int32_t cur_mask; 186 187 mask = D_STAT_CIM(mask); 188 cur_mask = _reg_read_4(D_STAT_REG); 189 190 _reg_write_4(D_STAT_REG, ((cur_mask ^ ~mask) | (cur_mask & mask)) & 191 D_STAT_CIM(__dmac_enabled_channel)); 192 } 193 194 void * 195 dmac_intr_establish(enum dmac_channel ch, int ipl, int (*func)(void *), 196 void *arg) 197 { 198 struct _ipl_dispatcher *dispatcher = &__dmac_dispatcher[ch]; 199 struct _ipl_dispatcher *d; 200 int i, s; 201 202 KDASSERT(dispatcher->func == NULL); 203 204 s = _intr_suspend(); 205 dispatcher->func = func; 206 dispatcher->arg = arg; 207 dispatcher->ipl = ipl; 208 dispatcher->channel = ch; 209 dispatcher->bit = D_STAT_CIS_BIT(ch); 210 211 for (i = 0; i < _IPL_N; i++) { 212 if (i < ipl) 213 __dmac_ipl_holder[i].mask &= ~D_STAT_CIM_BIT(ch); 214 else 215 __dmac_ipl_holder[i].mask |= D_STAT_CIM_BIT(ch); 216 } 217 218 /* insert queue IPL order */ 219 if (SLIST_EMPTY(&__dmac_dispatcher_head)) { 220 SLIST_INSERT_HEAD(&__dmac_dispatcher_head, dispatcher, link); 221 } else { 222 SLIST_FOREACH(d, &__dmac_dispatcher_head, link) { 223 if (SLIST_NEXT(d, link) == 0 || 224 SLIST_NEXT(d, link)->ipl < ipl) { 225 SLIST_INSERT_AFTER(d, dispatcher, link); 226 break; 227 } 228 } 229 } 230 231 md_ipl_register(IPL_DMAC, __dmac_ipl_holder); 232 233 dmac_intr_enable(ch); 234 __dmac_enabled_channel |= D_STAT_CIS_BIT(ch); 235 236 _intr_resume(s); 237 238 return ((void *)ch); 239 } 240 241 void 242 dmac_intr_disestablish(void *handle) 243 { 244 int ch = (int)(handle); 245 struct _ipl_dispatcher *dispatcher = &__dmac_dispatcher[ch]; 246 int i, s; 247 248 s = _intr_suspend(); 249 250 dmac_intr_disable(ch); 251 dispatcher->func = NULL; 252 253 SLIST_REMOVE(&__dmac_dispatcher_head, dispatcher, 254 _ipl_dispatcher, link); 255 256 for (i = 0; i < _IPL_N; i++) 257 __dmac_ipl_holder[i].mask |= D_STAT_CIM_BIT(ch); 258 259 md_ipl_register(IPL_DMAC, __dmac_ipl_holder); 260 __dmac_enabled_channel &= ~D_STAT_CIS_BIT(ch); 261 262 _intr_resume(s); 263 } 264 265 /* 266 * Start/Stop 267 */ 268 void 269 dmac_start_channel(enum dmac_channel ch) 270 { 271 bus_addr_t chcr = D_CHCR_REG(__dmac_channel_base[ch]); 272 u_int32_t r; 273 int s; 274 275 /* suspend all channels */ 276 s = _intr_suspend(); 277 r = _reg_read_4(D_ENABLER_REG); 278 _reg_write_4(D_ENABLEW_REG, r | D_ENABLE_SUSPEND); 279 280 /* access CHCR */ 281 _reg_write_4(chcr, (_reg_read_4(chcr) | D_CHCR_STR)); 282 283 /* start all channels */ 284 _reg_write_4(D_ENABLEW_REG, r & ~D_ENABLE_SUSPEND); 285 _intr_resume(s); 286 } 287 288 void 289 dmac_stop_channel(enum dmac_channel ch) 290 { 291 bus_addr_t chcr = D_CHCR_REG(__dmac_channel_base[ch]); 292 u_int32_t r; 293 int s; 294 295 /* suspend all channels */ 296 s = _intr_suspend(); 297 r = _reg_read_4(D_ENABLER_REG); 298 _reg_write_4(D_ENABLEW_REG, r | D_ENABLE_SUSPEND); 299 300 /* access CHCR */ 301 _reg_write_4(chcr, (_reg_read_4(chcr) & ~D_CHCR_STR)); 302 303 /* resume all chanells */ 304 _reg_write_4(D_ENABLEW_REG, r); 305 _intr_resume(s); 306 } 307 308 void 309 dmac_sync_buffer() 310 { 311 312 mips_dcache_wbinv_all(); 313 __asm__ __volatile("sync.l"); 314 } 315 316 /* 317 * Polling 318 * DMAC status connected to CPCOND[0]. 319 */ 320 void 321 dmac_cpc_set(enum dmac_channel ch) 322 { 323 u_int32_t r; 324 325 r = _reg_read_4(D_PCR_REG); 326 KDASSERT((D_PCR_CPC(r) & ~D_PCR_CPC_BIT(ch)) == 0); 327 328 /* clear interrupt status */ 329 _reg_write_4(D_STAT_REG, D_STAT_CIS_BIT(ch)); 330 331 _reg_write_4(D_PCR_REG, r | D_PCR_CPC_BIT(ch)); 332 } 333 334 void 335 dmac_cpc_clear(enum dmac_channel ch) 336 { 337 338 _reg_write_4(D_PCR_REG, _reg_read_4(D_PCR_REG) & ~D_PCR_CPC_BIT(ch)) 339 } 340 341 void 342 dmac_cpc_poll() 343 { 344 __asm__ __volatile__( 345 ".set noreorder;" 346 "1: nop;" 347 "nop;" 348 "nop;" 349 "nop;" 350 "nop;" 351 "bc0f 1b;" 352 " nop;" 353 ".set reorder"); 354 } 355 356 /* not recommended. use dmac_cpc_poll as possible */ 357 void 358 dmac_bus_poll(enum dmac_channel ch) 359 { 360 bus_addr_t chcr = D_CHCR_REG(__dmac_channel_base[ch]); 361 362 while (_reg_read_4(chcr) & D_CHCR_STR) 363 ; 364 } 365 366 /* 367 * Misc 368 */ 369 void 370 dmac_chcr_write(enum dmac_channel ch, u_int32_t v) 371 { 372 u_int32_t r; 373 int s; 374 375 /* suspend all channels */ 376 s = _intr_suspend(); 377 r = _reg_read_4(D_ENABLER_REG); 378 _reg_write_4(D_ENABLEW_REG, r | D_ENABLE_SUSPEND); 379 380 /* write CHCR reg */ 381 _reg_write_4(D_CHCR_REG(__dmac_channel_base[ch]), v); 382 383 /* resume all chanells */ 384 _reg_write_4(D_ENABLEW_REG, r); 385 _intr_resume(s); 386 } 387 388