1 |
/* |
2 |
* Copyright (C) 2003-2007 Anders Gavare. All rights reserved. |
3 |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
6 |
* |
7 |
* 1. Redistributions of source code must retain the above copyright |
8 |
* notice, this list of conditions and the following disclaimer. |
9 |
* 2. Redistributions in binary form must reproduce the above copyright |
10 |
* notice, this list of conditions and the following disclaimer in the |
11 |
* documentation and/or other materials provided with the distribution. |
12 |
* 3. The name of the author may not be used to endorse or promote products |
13 |
* derived from this software without specific prior written permission. |
14 |
* |
15 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
* SUCH DAMAGE. |
26 |
* |
27 |
* |
28 |
* $Id: dev_ps2_stuff.c,v 1.34 2007/08/29 20:36:49 debug Exp $ |
29 |
* |
30 |
* COMMENT: PlayStation 2 misc stuff (timer, DMA, interrupts, ...) |
31 |
* |
32 |
* offset 0x0000 timer control |
33 |
* offset 0x8000 DMA controller |
34 |
* offset 0xf000 Interrupt register |
35 |
* |
36 |
* The 16 normal PS2 interrupts interrupt at MIPS interrupt 2. |
37 |
* The 16 DMA interrupts are connected to MIPS interrupt 3. |
38 |
* |
39 |
* SBUS interrupts go via PS2 interrupt 1. |
40 |
*/ |
41 |
|
42 |
#include <stdio.h> |
43 |
#include <stdlib.h> |
44 |
#include <string.h> |
45 |
|
46 |
#include "cpu.h" |
47 |
#include "device.h" |
48 |
#include "machine.h" |
49 |
#include "memory.h" |
50 |
#include "misc.h" |
51 |
|
52 |
#include "ee_timerreg.h" |
53 |
#include "ps2_dmacreg.h" |
54 |
|
55 |
#define TICK_STEPS_SHIFT 14 |
56 |
|
57 |
/* NOTE/TODO: This should be the same as in ps2_gs: */ |
58 |
#define DEV_PS2_GIF_FAKE_BASE 0x50000000 |
59 |
|
60 |
#define N_PS2_DMA_CHANNELS 10 |
61 |
#define N_PS2_TIMERS 4 |
62 |
|
63 |
struct ps2_data { |
64 |
uint32_t timer_count[N_PS2_TIMERS]; |
65 |
uint32_t timer_comp[N_PS2_TIMERS]; |
66 |
uint32_t timer_mode[N_PS2_TIMERS]; |
67 |
uint32_t timer_hold[N_PS2_TIMERS]; |
68 |
/* NOTE: only 0 and 1 are valid */ |
69 |
struct interrupt timer_irq[N_PS2_TIMERS]; |
70 |
|
71 |
uint64_t dmac_reg[DMAC_REGSIZE / 0x10]; |
72 |
struct interrupt dmac_irq; /* MIPS irq 3 */ |
73 |
struct interrupt dma_channel2_irq; /* irq path of channel 2 */ |
74 |
|
75 |
uint64_t other_memory_base[N_PS2_DMA_CHANNELS]; |
76 |
|
77 |
uint32_t intr; |
78 |
uint32_t imask; |
79 |
uint32_t sbus_smflg; |
80 |
struct interrupt intr_irq; /* MIPS irq 2 */ |
81 |
struct interrupt sbus_irq; /* PS2 irq 1 */ |
82 |
}; |
83 |
|
84 |
#define DEV_PS2_LENGTH 0x10000 |
85 |
|
86 |
|
87 |
void ps2_intr_interrupt_assert(struct interrupt *interrupt) |
88 |
{ |
89 |
struct ps2_data *d = interrupt->extra; |
90 |
d->intr |= (1 << interrupt->line); |
91 |
if (d->intr & d->imask) |
92 |
INTERRUPT_ASSERT(d->intr_irq); |
93 |
} |
94 |
void ps2_intr_interrupt_deassert(struct interrupt *interrupt) |
95 |
{ |
96 |
struct ps2_data *d = interrupt->extra; |
97 |
d->intr &= ~(1 << interrupt->line); |
98 |
if (!(d->intr & d->imask)) |
99 |
INTERRUPT_DEASSERT(d->intr_irq); |
100 |
} |
101 |
void ps2_dmac_interrupt_assert(struct interrupt *interrupt) |
102 |
{ |
103 |
struct ps2_data *d = interrupt->extra; |
104 |
d->dmac_reg[0x601] |= (1 << interrupt->line); |
105 |
/* TODO: DMA interrupt mask? */ |
106 |
if (d->dmac_reg[0x601] & 0xffff) |
107 |
INTERRUPT_ASSERT(d->dmac_irq); |
108 |
} |
109 |
void ps2_dmac_interrupt_deassert(struct interrupt *interrupt) |
110 |
{ |
111 |
struct ps2_data *d = interrupt->extra; |
112 |
d->dmac_reg[0x601] &= ~(1 << interrupt->line); |
113 |
/* TODO: DMA interrupt mask? */ |
114 |
if (!(d->dmac_reg[0x601] & 0xffff)) |
115 |
INTERRUPT_DEASSERT(d->dmac_irq); |
116 |
} |
117 |
void ps2_sbus_interrupt_assert(struct interrupt *interrupt) |
118 |
{ |
119 |
/* Note: sbus irq 0 = mask 0x100, sbus irq 1 = mask 0x400 */ |
120 |
struct ps2_data *d = interrupt->extra; |
121 |
d->sbus_smflg |= (1 << (8 + interrupt->line * 2)); |
122 |
/* TODO: SBUS interrupt mask? */ |
123 |
if (d->sbus_smflg != 0) |
124 |
INTERRUPT_ASSERT(d->sbus_irq); |
125 |
} |
126 |
void ps2_sbus_interrupt_deassert(struct interrupt *interrupt) |
127 |
{ |
128 |
/* Note: sbus irq 0 = mask 0x100, sbus irq 1 = mask 0x400 */ |
129 |
struct ps2_data *d = interrupt->extra; |
130 |
d->sbus_smflg &= ~(1 << (8 + interrupt->line * 2)); |
131 |
/* TODO: SBUS interrupt mask? */ |
132 |
if (d->sbus_smflg == 0) |
133 |
INTERRUPT_DEASSERT(d->sbus_irq); |
134 |
} |
135 |
|
136 |
|
137 |
DEVICE_TICK(ps2) |
138 |
{ |
139 |
struct ps2_data *d = extra; |
140 |
int i; |
141 |
|
142 |
/* |
143 |
* Right now this interrupts every now and then. |
144 |
* The main interrupt in NetBSD should be 100 Hz. TODO. |
145 |
*/ |
146 |
for (i=0; i<N_PS2_TIMERS; i++) { |
147 |
/* Count-up Enable: TODO: by how much? */ |
148 |
if (d->timer_mode[i] & T_MODE_CUE) |
149 |
d->timer_count[i] ++; |
150 |
|
151 |
if (d->timer_mode[i] & (T_MODE_CMPE | T_MODE_OVFE)) { |
152 |
/* Zero return: */ |
153 |
if (d->timer_mode[i] & T_MODE_ZRET) |
154 |
d->timer_count[i] = 0; |
155 |
|
156 |
INTERRUPT_ASSERT(d->timer_irq[i]); |
157 |
|
158 |
/* timer 1..3 are "single-shot"? TODO */ |
159 |
if (i > 0) { |
160 |
d->timer_mode[i] &= |
161 |
~(T_MODE_CMPE | T_MODE_OVFF); |
162 |
} |
163 |
} |
164 |
} |
165 |
} |
166 |
|
167 |
|
168 |
DEVICE_ACCESS(ps2) |
169 |
{ |
170 |
uint64_t idata = 0, odata = 0; |
171 |
int regnr = 0; |
172 |
struct ps2_data *d = extra; |
173 |
int timer_nr = 0; |
174 |
|
175 |
if (writeflag == MEM_WRITE) |
176 |
idata = memory_readmax64(cpu, data, len); |
177 |
|
178 |
if (relative_addr >= 0x8000 && relative_addr < 0x8000 + DMAC_REGSIZE) { |
179 |
regnr = (relative_addr - 0x8000) / 16; |
180 |
if (writeflag == MEM_READ) |
181 |
odata = d->dmac_reg[regnr]; |
182 |
else |
183 |
d->dmac_reg[regnr] = idata; |
184 |
} |
185 |
|
186 |
/* |
187 |
* Timer control: |
188 |
* The four timers are at offsets 0, 0x800, 0x1000, and 0x1800. |
189 |
*/ |
190 |
if (relative_addr < TIMER_REGSIZE) { |
191 |
/* 0, 1, 2, or 3 */ |
192 |
timer_nr = (relative_addr & 0x1800) >> 11; |
193 |
relative_addr &= (TIMER_OFS-1); |
194 |
} |
195 |
|
196 |
switch (relative_addr) { |
197 |
case 0x0000: /* timer count */ |
198 |
if (writeflag == MEM_READ) { |
199 |
odata = d->timer_count[timer_nr]; |
200 |
if (timer_nr == 0) { |
201 |
/* :-) TODO: remove this? */ |
202 |
d->timer_count[timer_nr] ++; |
203 |
} |
204 |
debug("[ ps2: read timer %i count: 0x%llx ]\n", |
205 |
timer_nr, (long long)odata); |
206 |
} else { |
207 |
d->timer_count[timer_nr] = idata; |
208 |
debug("[ ps2: write timer %i count: 0x%llx ]\n", |
209 |
timer_nr, (long long)idata); |
210 |
} |
211 |
break; |
212 |
case 0x0010: /* timer mode */ |
213 |
if (writeflag == MEM_READ) { |
214 |
odata = d->timer_mode[timer_nr]; |
215 |
debug("[ ps2: read timer %i mode: 0x%llx ]\n", |
216 |
timer_nr, (long long)odata); |
217 |
} else { |
218 |
d->timer_mode[timer_nr] = idata; |
219 |
debug("[ ps2: write timer %i mode: 0x%llx ]\n", |
220 |
timer_nr, (long long)idata); |
221 |
} |
222 |
break; |
223 |
case 0x0020: /* timer comp */ |
224 |
if (writeflag == MEM_READ) { |
225 |
odata = d->timer_comp[timer_nr]; |
226 |
debug("[ ps2: read timer %i comp: 0x%llx ]\n", |
227 |
timer_nr, (long long)odata); |
228 |
} else { |
229 |
d->timer_comp[timer_nr] = idata; |
230 |
debug("[ ps2: write timer %i comp: 0x%llx ]\n", |
231 |
timer_nr, (long long)idata); |
232 |
} |
233 |
break; |
234 |
case 0x0030: /* timer hold */ |
235 |
if (writeflag == MEM_READ) { |
236 |
odata = d->timer_hold[timer_nr]; |
237 |
debug("[ ps2: read timer %i hold: 0x%llx ]\n", |
238 |
timer_nr, (long long)odata); |
239 |
if (timer_nr >= 2) |
240 |
fatal("[ WARNING: ps2: read from non-" |
241 |
"existant timer %i hold register ]\n"); |
242 |
} else { |
243 |
d->timer_hold[timer_nr] = idata; |
244 |
debug("[ ps2: write timer %i hold: 0x%llx ]\n", |
245 |
timer_nr, (long long)idata); |
246 |
if (timer_nr >= 2) |
247 |
fatal("[ WARNING: ps2: write to " |
248 |
"non-existant timer %i hold register ]\n", |
249 |
timer_nr); |
250 |
} |
251 |
break; |
252 |
|
253 |
case 0x8000 + D2_CHCR_REG: |
254 |
if (writeflag==MEM_READ) { |
255 |
odata = d->dmac_reg[regnr]; |
256 |
/* debug("[ ps2: dmac read from D2_CHCR " |
257 |
"(0x%llx) ]\n", (long long)d->dmac_reg[regnr]); */ |
258 |
} else { |
259 |
/* debug("[ ps2: dmac write to D2_CHCR, " |
260 |
"data 0x%016llx ]\n", (long long) idata); */ |
261 |
if (idata & D_CHCR_STR) { |
262 |
int length = d->dmac_reg[D2_QWC_REG/0x10] * 16; |
263 |
uint64_t from_addr = d->dmac_reg[ |
264 |
D2_MADR_REG/0x10]; |
265 |
uint64_t to_addr = d->dmac_reg[ |
266 |
D2_TADR_REG/0x10]; |
267 |
unsigned char *copy_buf; |
268 |
|
269 |
debug("[ ps2: dmac [ch2] transfer addr=" |
270 |
"0x%016llx len=0x%lx ]\n", (long long) |
271 |
d->dmac_reg[D2_MADR_REG/0x10], |
272 |
(long)length); |
273 |
|
274 |
CHECK_ALLOCATION(copy_buf = malloc(length)); |
275 |
|
276 |
cpu->memory_rw(cpu, cpu->mem, from_addr, |
277 |
copy_buf, length, MEM_READ, |
278 |
CACHE_NONE | PHYSICAL); |
279 |
cpu->memory_rw(cpu, cpu->mem, |
280 |
d->other_memory_base[DMA_CH_GIF] + to_addr, |
281 |
copy_buf, length, MEM_WRITE, |
282 |
CACHE_NONE | PHYSICAL); |
283 |
free(copy_buf); |
284 |
|
285 |
/* Done with the transfer: */ |
286 |
d->dmac_reg[D2_QWC_REG/0x10] = 0; |
287 |
idata &= ~D_CHCR_STR; |
288 |
|
289 |
/* interrupt DMA channel 2 */ |
290 |
INTERRUPT_ASSERT(d->dma_channel2_irq); |
291 |
} else |
292 |
debug("[ ps2: dmac [ch2] stopping " |
293 |
"transfer ]\n"); |
294 |
d->dmac_reg[regnr] = idata; |
295 |
return 1; |
296 |
} |
297 |
break; |
298 |
|
299 |
case 0x8000 + D2_QWC_REG: |
300 |
case 0x8000 + D2_MADR_REG: |
301 |
case 0x8000 + D2_TADR_REG: |
302 |
/* no debug output */ |
303 |
break; |
304 |
|
305 |
case 0xe010: /* dmac interrupt status (and mask, */ |
306 |
/* the upper 16 bits) */ |
307 |
if (writeflag == MEM_WRITE) { |
308 |
uint32_t oldmask = d->dmac_reg[regnr] & 0xffff0000; |
309 |
/* Clear out those bits that are set in idata: */ |
310 |
d->dmac_reg[regnr] &= ~idata; |
311 |
d->dmac_reg[regnr] &= 0xffff; |
312 |
d->dmac_reg[regnr] |= oldmask; |
313 |
if (((d->dmac_reg[regnr] & 0xffff) & |
314 |
((d->dmac_reg[regnr]>>16) & 0xffff)) == 0) { |
315 |
INTERRUPT_DEASSERT(d->dmac_irq); |
316 |
} |
317 |
} else { |
318 |
/* Hm... make it seem like the mask bits are (at |
319 |
least as much as) the interrupt assertions: */ |
320 |
odata = d->dmac_reg[regnr]; |
321 |
odata |= (odata << 16); |
322 |
} |
323 |
break; |
324 |
|
325 |
case 0xf000: /* interrupt register */ |
326 |
if (writeflag == MEM_READ) { |
327 |
odata = d->intr; |
328 |
debug("[ ps2: read from Interrupt Register:" |
329 |
" 0x%llx ]\n", (long long)odata); |
330 |
|
331 |
/* TODO: This is _NOT_ correct behavior: */ |
332 |
// d->intr = 0; |
333 |
// INTERRUPT_DEASSERT(d->intr_irq); |
334 |
} else { |
335 |
debug("[ ps2: write to Interrupt Register: " |
336 |
"0x%llx ]\n", (long long)idata); |
337 |
/* Clear out bits that are set in idata: */ |
338 |
d->intr &= ~idata; |
339 |
|
340 |
if ((d->intr & d->imask) == 0) |
341 |
INTERRUPT_DEASSERT(d->intr_irq); |
342 |
} |
343 |
break; |
344 |
|
345 |
case 0xf010: /* interrupt mask */ |
346 |
if (writeflag == MEM_READ) { |
347 |
odata = d->imask; |
348 |
/* debug("[ ps2: read from Interrupt Mask " |
349 |
"Register: 0x%llx ]\n", (long long)odata); */ |
350 |
} else { |
351 |
/* debug("[ ps2: write to Interrupt Mask " |
352 |
"Register: 0x%llx ]\n", (long long)idata); */ |
353 |
/* Note: written value indicates which bits |
354 |
to _toggle_, not which bits to set! */ |
355 |
d->imask ^= idata; |
356 |
} |
357 |
break; |
358 |
|
359 |
case 0xf230: /* sbus interrupt register? */ |
360 |
if (writeflag == MEM_READ) { |
361 |
odata = d->sbus_smflg; |
362 |
debug("[ ps2: read from SBUS SMFLG:" |
363 |
" 0x%llx ]\n", (long long)odata); |
364 |
} else { |
365 |
/* Clear bits on write: */ |
366 |
debug("[ ps2: write to SBUS SMFLG:" |
367 |
" 0x%llx ]\n", (long long)idata); |
368 |
d->sbus_smflg &= ~idata; |
369 |
/* irq 1 is SBUS */ |
370 |
if (d->sbus_smflg == 0) |
371 |
INTERRUPT_DEASSERT(d->sbus_irq); |
372 |
} |
373 |
break; |
374 |
default: |
375 |
if (writeflag==MEM_READ) { |
376 |
debug("[ ps2: read from addr 0x%x: 0x%llx ]\n", |
377 |
(int)relative_addr, (long long)odata); |
378 |
} else { |
379 |
debug("[ ps2: write to addr 0x%x: 0x%llx ]\n", |
380 |
(int)relative_addr, (long long)idata); |
381 |
} |
382 |
} |
383 |
|
384 |
if (writeflag == MEM_READ) |
385 |
memory_writemax64(cpu, data, len, odata); |
386 |
|
387 |
return 1; |
388 |
} |
389 |
|
390 |
|
391 |
DEVINIT(ps2) |
392 |
{ |
393 |
struct ps2_data *d; |
394 |
int i; |
395 |
struct interrupt template; |
396 |
char n[300]; |
397 |
|
398 |
CHECK_ALLOCATION(d = malloc(sizeof(struct ps2_data))); |
399 |
memset(d, 0, sizeof(struct ps2_data)); |
400 |
|
401 |
d->other_memory_base[DMA_CH_GIF] = DEV_PS2_GIF_FAKE_BASE; |
402 |
|
403 |
/* Connect to MIPS irq 2 (interrupt controller) and 3 (dmac): */ |
404 |
snprintf(n, sizeof(n), "%s.2", devinit->interrupt_path); |
405 |
INTERRUPT_CONNECT(n, d->intr_irq); |
406 |
snprintf(n, sizeof(n), "%s.3", devinit->interrupt_path); |
407 |
INTERRUPT_CONNECT(n, d->dmac_irq); |
408 |
|
409 |
/* |
410 |
* Register interrupts: |
411 |
* |
412 |
* 16 normal IRQs (machine[x].cpu[x].ps2_intr.%i) |
413 |
* 16 DMA IRQs (machine[x].cpu[x].ps2_dmac.%i) |
414 |
* 2 sbus IRQs (machine[x].cpu[x].ps2_sbus.%i) |
415 |
*/ |
416 |
for (i=0; i<16; i++) { |
417 |
snprintf(n, sizeof(n), "%s.ps2_intr.%i", |
418 |
devinit->interrupt_path, i); |
419 |
memset(&template, 0, sizeof(template)); |
420 |
template.line = i; |
421 |
template.name = n; |
422 |
template.extra = d; |
423 |
template.interrupt_assert = ps2_intr_interrupt_assert; |
424 |
template.interrupt_deassert = ps2_intr_interrupt_deassert; |
425 |
interrupt_handler_register(&template); |
426 |
} |
427 |
for (i=0; i<16; i++) { |
428 |
snprintf(n, sizeof(n), "%s.ps2_dmac.%i", |
429 |
devinit->interrupt_path, i); |
430 |
memset(&template, 0, sizeof(template)); |
431 |
template.line = i; |
432 |
template.name = n; |
433 |
template.extra = d; |
434 |
template.interrupt_assert = ps2_dmac_interrupt_assert; |
435 |
template.interrupt_deassert = ps2_dmac_interrupt_deassert; |
436 |
interrupt_handler_register(&template); |
437 |
} |
438 |
for (i=0; i<2; i++) { |
439 |
snprintf(n, sizeof(n), "%s.ps2_sbus.%i", |
440 |
devinit->interrupt_path, i); |
441 |
memset(&template, 0, sizeof(template)); |
442 |
template.line = i; |
443 |
template.name = n; |
444 |
template.extra = d; |
445 |
template.interrupt_assert = ps2_sbus_interrupt_assert; |
446 |
template.interrupt_deassert = ps2_sbus_interrupt_deassert; |
447 |
interrupt_handler_register(&template); |
448 |
} |
449 |
|
450 |
/* Connect to DMA channel 2 irq: */ |
451 |
snprintf(n, sizeof(n), "%s.ps2_dmac.2", devinit->interrupt_path); |
452 |
INTERRUPT_CONNECT(n, d->dma_channel2_irq); |
453 |
|
454 |
/* Connect to SBUS interrupt, at ps2 interrupt 1: */ |
455 |
snprintf(n, sizeof(n), "%s.ps2_intr.1", devinit->interrupt_path); |
456 |
INTERRUPT_CONNECT(n, d->sbus_irq); |
457 |
|
458 |
/* Connect to the timers' interrupts: */ |
459 |
for (i=0; i<N_PS2_TIMERS; i++) { |
460 |
/* PS2 irq 9 is timer0, etc. */ |
461 |
snprintf(n, sizeof(n), "%s.ps2_intr.%i", |
462 |
devinit->interrupt_path, 9 + i); |
463 |
INTERRUPT_CONNECT(n, d->timer_irq[i]); |
464 |
} |
465 |
|
466 |
memory_device_register(devinit->machine->memory, "ps2", devinit->addr, |
467 |
DEV_PS2_LENGTH, dev_ps2_access, d, DM_DEFAULT, NULL); |
468 |
machine_add_tickfunction(devinit->machine, |
469 |
dev_ps2_tick, d, TICK_STEPS_SHIFT); |
470 |
|
471 |
return 1; |
472 |
} |
473 |
|