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