1 |
/* |
/* |
2 |
* Copyright (C) 2003-2005 Anders Gavare. All rights reserved. |
* Copyright (C) 2003-2007 Anders Gavare. All rights reserved. |
3 |
* |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
* modification, are permitted provided that the following conditions are met: |
25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: dev_ps2_stuff.c,v 1.24 2005/10/26 14:37:04 debug Exp $ |
* $Id: dev_ps2_stuff.c,v 1.33 2007/06/15 19:57:33 debug Exp $ |
29 |
* |
* |
30 |
* Playstation 2 misc. stuff: |
* COMMENT: PlayStation 2 misc stuff (timer, DMA, interrupts, ...) |
31 |
* |
* |
32 |
* offset 0x0000 timer control |
* offset 0x0000 timer control |
33 |
* offset 0x8000 DMA controller |
* offset 0x8000 DMA controller |
34 |
* offset 0xf000 Interrupt register |
* 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> |
#include <stdio.h> |
44 |
#include <string.h> |
#include <string.h> |
45 |
|
|
46 |
#include "cpu.h" |
#include "cpu.h" |
47 |
#include "devices.h" |
#include "device.h" |
48 |
#include "machine.h" |
#include "machine.h" |
49 |
#include "memory.h" |
#include "memory.h" |
50 |
#include "misc.h" |
#include "misc.h" |
57 |
/* NOTE/TODO: This should be the same as in ps2_gs: */ |
/* NOTE/TODO: This should be the same as in ps2_gs: */ |
58 |
#define DEV_PS2_GIF_FAKE_BASE 0x50000000 |
#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 |
* dev_ps2_stuff_tick(): |
uint32_t timer_count[N_PS2_TIMERS]; |
65 |
*/ |
uint32_t timer_comp[N_PS2_TIMERS]; |
66 |
void dev_ps2_stuff_tick(struct cpu *cpu, void *extra) |
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; |
struct ps2_data *d = extra; |
140 |
int i; |
int i; |
153 |
if (d->timer_mode[i] & T_MODE_ZRET) |
if (d->timer_mode[i] & T_MODE_ZRET) |
154 |
d->timer_count[i] = 0; |
d->timer_count[i] = 0; |
155 |
|
|
156 |
/* irq 9 is timer0, etc. */ |
INTERRUPT_ASSERT(d->timer_irq[i]); |
|
cpu_interrupt(cpu, 8 + 9 + i); |
|
157 |
|
|
158 |
/* timer 1..3 are "single-shot"? TODO */ |
/* timer 1..3 are "single-shot"? TODO */ |
159 |
if (i > 0) |
if (i > 0) { |
160 |
d->timer_mode[i] &= ~(T_MODE_CMPE | T_MODE_OVFF); |
d->timer_mode[i] &= |
161 |
|
~(T_MODE_CMPE | T_MODE_OVFF); |
162 |
|
} |
163 |
} |
} |
164 |
} |
} |
165 |
} |
} |
166 |
|
|
167 |
|
|
168 |
/* |
DEVICE_ACCESS(ps2) |
|
* dev_ps2_stuff_access(): |
|
|
*/ |
|
|
int dev_ps2_stuff_access(struct cpu *cpu, struct memory *mem, |
|
|
uint64_t relative_addr, unsigned char *data, size_t len, |
|
|
int writeflag, void *extra) |
|
169 |
{ |
{ |
170 |
uint64_t idata = 0, odata = 0; |
uint64_t idata = 0, odata = 0; |
171 |
int regnr = 0; |
int regnr = 0; |
201 |
/* :-) TODO: remove this? */ |
/* :-) TODO: remove this? */ |
202 |
d->timer_count[timer_nr] ++; |
d->timer_count[timer_nr] ++; |
203 |
} |
} |
204 |
debug("[ ps2_stuff: read timer %i count: 0x%llx ]\n", |
debug("[ ps2: read timer %i count: 0x%llx ]\n", |
205 |
timer_nr, (long long)odata); |
timer_nr, (long long)odata); |
206 |
} else { |
} else { |
207 |
d->timer_count[timer_nr] = idata; |
d->timer_count[timer_nr] = idata; |
208 |
debug("[ ps2_stuff: write timer %i count: 0x%llx ]\n", |
debug("[ ps2: write timer %i count: 0x%llx ]\n", |
209 |
timer_nr, (long long)idata); |
timer_nr, (long long)idata); |
210 |
} |
} |
211 |
break; |
break; |
212 |
case 0x0010: /* timer mode */ |
case 0x0010: /* timer mode */ |
213 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
214 |
odata = d->timer_mode[timer_nr]; |
odata = d->timer_mode[timer_nr]; |
215 |
debug("[ ps2_stuff: read timer %i mode: 0x%llx ]\n", |
debug("[ ps2: read timer %i mode: 0x%llx ]\n", |
216 |
timer_nr, (long long)odata); |
timer_nr, (long long)odata); |
217 |
} else { |
} else { |
218 |
d->timer_mode[timer_nr] = idata; |
d->timer_mode[timer_nr] = idata; |
219 |
debug("[ ps2_stuff: write timer %i mode: 0x%llx ]\n", |
debug("[ ps2: write timer %i mode: 0x%llx ]\n", |
220 |
timer_nr, (long long)idata); |
timer_nr, (long long)idata); |
221 |
} |
} |
222 |
break; |
break; |
223 |
case 0x0020: /* timer comp */ |
case 0x0020: /* timer comp */ |
224 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
225 |
odata = d->timer_comp[timer_nr]; |
odata = d->timer_comp[timer_nr]; |
226 |
debug("[ ps2_stuff: read timer %i comp: 0x%llx ]\n", |
debug("[ ps2: read timer %i comp: 0x%llx ]\n", |
227 |
timer_nr, (long long)odata); |
timer_nr, (long long)odata); |
228 |
} else { |
} else { |
229 |
d->timer_comp[timer_nr] = idata; |
d->timer_comp[timer_nr] = idata; |
230 |
debug("[ ps2_stuff: write timer %i comp: 0x%llx ]\n", |
debug("[ ps2: write timer %i comp: 0x%llx ]\n", |
231 |
timer_nr, (long long)idata); |
timer_nr, (long long)idata); |
232 |
} |
} |
233 |
break; |
break; |
234 |
case 0x0030: /* timer hold */ |
case 0x0030: /* timer hold */ |
235 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
236 |
odata = d->timer_hold[timer_nr]; |
odata = d->timer_hold[timer_nr]; |
237 |
debug("[ ps2_stuff: read timer %i hold: 0x%llx ]\n", |
debug("[ ps2: read timer %i hold: 0x%llx ]\n", |
238 |
timer_nr, (long long)odata); |
timer_nr, (long long)odata); |
239 |
if (timer_nr >= 2) |
if (timer_nr >= 2) |
240 |
fatal("[ WARNING: ps2_stuff: read from non-" |
fatal("[ WARNING: ps2: read from non-" |
241 |
"existant timer %i hold register ]\n"); |
"existant timer %i hold register ]\n"); |
242 |
} else { |
} else { |
243 |
d->timer_hold[timer_nr] = idata; |
d->timer_hold[timer_nr] = idata; |
244 |
debug("[ ps2_stuff: write timer %i hold: 0x%llx ]\n", |
debug("[ ps2: write timer %i hold: 0x%llx ]\n", |
245 |
timer_nr, (long long)idata); |
timer_nr, (long long)idata); |
246 |
if (timer_nr >= 2) |
if (timer_nr >= 2) |
247 |
fatal("[ WARNING: ps2_stuff: write to " |
fatal("[ WARNING: ps2: write to " |
248 |
"non-existant timer %i hold register ]\n", |
"non-existant timer %i hold register ]\n", |
249 |
timer_nr); |
timer_nr); |
250 |
} |
} |
253 |
case 0x8000 + D2_CHCR_REG: |
case 0x8000 + D2_CHCR_REG: |
254 |
if (writeflag==MEM_READ) { |
if (writeflag==MEM_READ) { |
255 |
odata = d->dmac_reg[regnr]; |
odata = d->dmac_reg[regnr]; |
256 |
/* debug("[ ps2_stuff: dmac read from D2_CHCR " |
/* debug("[ ps2: dmac read from D2_CHCR " |
257 |
"(0x%llx) ]\n", (long long)d->dmac_reg[regnr]); */ |
"(0x%llx) ]\n", (long long)d->dmac_reg[regnr]); */ |
258 |
} else { |
} else { |
259 |
/* debug("[ ps2_stuff: dmac write to D2_CHCR, " |
/* debug("[ ps2: dmac write to D2_CHCR, " |
260 |
"data 0x%016llx ]\n", (long long) idata); */ |
"data 0x%016llx ]\n", (long long) idata); */ |
261 |
if (idata & D_CHCR_STR) { |
if (idata & D_CHCR_STR) { |
262 |
int length = d->dmac_reg[D2_QWC_REG/0x10] * 16; |
int length = d->dmac_reg[D2_QWC_REG/0x10] * 16; |
266 |
D2_TADR_REG/0x10]; |
D2_TADR_REG/0x10]; |
267 |
unsigned char *copy_buf; |
unsigned char *copy_buf; |
268 |
|
|
269 |
debug("[ ps2_stuff: dmac [ch2] transfer addr=" |
debug("[ ps2: dmac [ch2] transfer addr=" |
270 |
"0x%016llx len=0x%lx ]\n", (long long) |
"0x%016llx len=0x%lx ]\n", (long long) |
271 |
d->dmac_reg[D2_MADR_REG/0x10], |
d->dmac_reg[D2_MADR_REG/0x10], |
272 |
(long)length); |
(long)length); |
273 |
|
|
274 |
copy_buf = malloc(length); |
CHECK_ALLOCATION(copy_buf = malloc(length)); |
275 |
if (copy_buf == NULL) { |
|
|
fprintf(stderr, "out of memory in " |
|
|
"dev_ps2_stuff_access()\n"); |
|
|
exit(1); |
|
|
} |
|
276 |
cpu->memory_rw(cpu, cpu->mem, from_addr, |
cpu->memory_rw(cpu, cpu->mem, from_addr, |
277 |
copy_buf, length, MEM_READ, |
copy_buf, length, MEM_READ, |
278 |
CACHE_NONE | PHYSICAL); |
CACHE_NONE | PHYSICAL); |
287 |
idata &= ~D_CHCR_STR; |
idata &= ~D_CHCR_STR; |
288 |
|
|
289 |
/* interrupt DMA channel 2 */ |
/* interrupt DMA channel 2 */ |
290 |
cpu_interrupt(cpu, 8 + 16 + 2); |
INTERRUPT_ASSERT(d->dma_channel2_irq); |
291 |
} else |
} else |
292 |
debug("[ ps2_stuff: dmac [ch2] stopping " |
debug("[ ps2: dmac [ch2] stopping " |
293 |
"transfer ]\n"); |
"transfer ]\n"); |
294 |
d->dmac_reg[regnr] = idata; |
d->dmac_reg[regnr] = idata; |
295 |
return 1; |
return 1; |
312 |
d->dmac_reg[regnr] |= oldmask; |
d->dmac_reg[regnr] |= oldmask; |
313 |
if (((d->dmac_reg[regnr] & 0xffff) & |
if (((d->dmac_reg[regnr] & 0xffff) & |
314 |
((d->dmac_reg[regnr]>>16) & 0xffff)) == 0) { |
((d->dmac_reg[regnr]>>16) & 0xffff)) == 0) { |
315 |
/* irq 3 is the DMAC */ |
INTERRUPT_DEASSERT(d->dmac_irq); |
|
cpu_interrupt_ack(cpu, 3); |
|
316 |
} |
} |
317 |
} else { |
} else { |
318 |
/* Hm... make it seem like the mask bits are (at |
/* Hm... make it seem like the mask bits are (at |
325 |
case 0xf000: /* interrupt register */ |
case 0xf000: /* interrupt register */ |
326 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
327 |
odata = d->intr; |
odata = d->intr; |
328 |
debug("[ ps2_stuff: read from Interrupt Register:" |
debug("[ ps2: read from Interrupt Register:" |
329 |
" 0x%llx ]\n", (long long)odata); |
" 0x%llx ]\n", (long long)odata); |
330 |
|
|
331 |
/* TODO: This is _NOT_ correct behavior: */ |
/* TODO: This is _NOT_ correct behavior: */ |
332 |
d->intr = 0; |
// d->intr = 0; |
333 |
cpu_interrupt_ack(cpu, 2); |
// INTERRUPT_DEASSERT(d->intr_irq); |
334 |
} else { |
} else { |
335 |
debug("[ ps2_stuff: write to Interrupt Register: " |
debug("[ ps2: write to Interrupt Register: " |
336 |
"0x%llx ]\n", (long long)idata); |
"0x%llx ]\n", (long long)idata); |
337 |
/* Clear out bits that are set in idata: */ |
/* Clear out bits that are set in idata: */ |
338 |
d->intr &= ~idata; |
d->intr &= ~idata; |
339 |
|
|
340 |
if ((d->intr & d->imask) == 0) |
if ((d->intr & d->imask) == 0) |
341 |
cpu_interrupt_ack(cpu, 2); |
INTERRUPT_DEASSERT(d->intr_irq); |
342 |
} |
} |
343 |
break; |
break; |
344 |
|
|
345 |
case 0xf010: /* interrupt mask */ |
case 0xf010: /* interrupt mask */ |
346 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
347 |
odata = d->imask; |
odata = d->imask; |
348 |
/* debug("[ ps2_stuff: read from Interrupt Mask " |
/* debug("[ ps2: read from Interrupt Mask " |
349 |
"Register: 0x%llx ]\n", (long long)odata); */ |
"Register: 0x%llx ]\n", (long long)odata); */ |
350 |
} else { |
} else { |
351 |
/* debug("[ ps2_stuff: write to Interrupt Mask " |
/* debug("[ ps2: write to Interrupt Mask " |
352 |
"Register: 0x%llx ]\n", (long long)idata); */ |
"Register: 0x%llx ]\n", (long long)idata); */ |
353 |
d->imask = idata; |
/* Note: written value indicates which bits |
354 |
|
to _toggle_, not which bits to set! */ |
355 |
|
d->imask ^= idata; |
356 |
} |
} |
357 |
break; |
break; |
358 |
|
|
359 |
case 0xf230: /* sbus interrupt register? */ |
case 0xf230: /* sbus interrupt register? */ |
360 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
361 |
odata = d->sbus_smflg; |
odata = d->sbus_smflg; |
362 |
debug("[ ps2_stuff: read from SBUS SMFLG:" |
debug("[ ps2: read from SBUS SMFLG:" |
363 |
" 0x%llx ]\n", (long long)odata); |
" 0x%llx ]\n", (long long)odata); |
364 |
} else { |
} else { |
365 |
/* Clear bits on write: */ |
/* Clear bits on write: */ |
366 |
debug("[ ps2_stuff: write to SBUS SMFLG:" |
debug("[ ps2: write to SBUS SMFLG:" |
367 |
" 0x%llx ]\n", (long long)idata); |
" 0x%llx ]\n", (long long)idata); |
368 |
d->sbus_smflg &= ~idata; |
d->sbus_smflg &= ~idata; |
369 |
/* irq 1 is SBUS */ |
/* irq 1 is SBUS */ |
370 |
if (d->sbus_smflg == 0) |
if (d->sbus_smflg == 0) |
371 |
cpu_interrupt_ack(cpu, 8 + 1); |
INTERRUPT_DEASSERT(d->sbus_irq); |
372 |
} |
} |
373 |
break; |
break; |
374 |
default: |
default: |
375 |
if (writeflag==MEM_READ) { |
if (writeflag==MEM_READ) { |
376 |
debug("[ ps2_stuff: read from addr 0x%x: 0x%llx ]\n", |
debug("[ ps2: read from addr 0x%x: 0x%llx ]\n", |
377 |
(int)relative_addr, (long long)odata); |
(int)relative_addr, (long long)odata); |
378 |
} else { |
} else { |
379 |
debug("[ ps2_stuff: write to addr 0x%x: 0x%llx ]\n", |
debug("[ ps2: write to addr 0x%x: 0x%llx ]\n", |
380 |
(int)relative_addr, (long long)idata); |
(int)relative_addr, (long long)idata); |
381 |
} |
} |
382 |
} |
} |
388 |
} |
} |
389 |
|
|
390 |
|
|
391 |
/* |
DEVINIT(ps2) |
|
* dev_ps2_stuff_init(): |
|
|
*/ |
|
|
struct ps2_data *dev_ps2_stuff_init(struct machine *machine, |
|
|
struct memory *mem, uint64_t baseaddr) |
|
392 |
{ |
{ |
393 |
struct ps2_data *d; |
struct ps2_data *d; |
394 |
|
int i; |
395 |
|
struct interrupt template; |
396 |
|
char n[300]; |
397 |
|
|
398 |
d = malloc(sizeof(struct ps2_data)); |
CHECK_ALLOCATION(d = malloc(sizeof(struct ps2_data))); |
|
if (d == NULL) { |
|
|
fprintf(stderr, "out of memory\n"); |
|
|
exit(1); |
|
|
} |
|
399 |
memset(d, 0, sizeof(struct ps2_data)); |
memset(d, 0, sizeof(struct ps2_data)); |
400 |
|
|
401 |
d->other_memory_base[DMA_CH_GIF] = DEV_PS2_GIF_FAKE_BASE; |
d->other_memory_base[DMA_CH_GIF] = DEV_PS2_GIF_FAKE_BASE; |
402 |
|
|
403 |
memory_device_register(mem, "ps2_stuff", baseaddr, |
/* Connect to MIPS irq 2 (interrupt controller) and 3 (dmac): */ |
404 |
DEV_PS2_STUFF_LENGTH, dev_ps2_stuff_access, d, MEM_DEFAULT, NULL); |
snprintf(n, sizeof(n), "%s.2", devinit->interrupt_path); |
405 |
machine_add_tickfunction(machine, |
INTERRUPT_CONNECT(n, d->intr_irq); |
406 |
dev_ps2_stuff_tick, d, TICK_STEPS_SHIFT); |
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 (emul[x].machine[x].cpu[x].ps2_intr.%i) |
413 |
|
* 16 DMA IRQs (emul[x].machine[x].cpu[x].ps2_dmac.%i) |
414 |
|
* 2 sbus IRQs (emul[x].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 |
return d; |
/* 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 |
|
|