dev_ps2_stuff.cc Source File

Back to the index.

dev_ps2_stuff.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2003-2009 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  * COMMENT: PlayStation 2 misc stuff (timer, DMA, interrupts, ...)
29  *
30  * offset 0x0000 timer control
31  * offset 0x8000 DMA controller
32  * offset 0xf000 Interrupt register
33  *
34  * The 16 normal PS2 interrupts interrupt at MIPS interrupt 2.
35  * The 16 DMA interrupts are connected to MIPS interrupt 3.
36  *
37  * SBUS interrupts go via PS2 interrupt 1.
38  */
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 
44 #include "cpu.h"
45 #include "device.h"
46 #include "machine.h"
47 #include "memory.h"
48 #include "misc.h"
49 
50 #include "thirdparty/ee_timerreg.h"
51 #include "thirdparty/ps2_dmacreg.h"
52 
53 
54 #define TICK_STEPS_SHIFT 14
55 
56 /* NOTE/TODO: This should be the same as in ps2_gs: */
57 #define DEV_PS2_GIF_FAKE_BASE 0x50000000
58 
59 #define N_PS2_DMA_CHANNELS 10
60 #define N_PS2_TIMERS 4
61 
62 struct ps2_data {
67  /* NOTE: only 0 and 1 are valid */
69 
70  uint64_t dmac_reg[DMAC_REGSIZE / 0x10];
71  struct interrupt dmac_irq; /* MIPS irq 3 */
72  struct interrupt dma_channel2_irq; /* irq path of channel 2 */
73 
75 
76  uint32_t intr;
77  uint32_t imask;
78  uint32_t sbus_smflg;
79  struct interrupt intr_irq; /* MIPS irq 2 */
80  struct interrupt sbus_irq; /* PS2 irq 1 */
81 };
82 
83 #define DEV_PS2_LENGTH 0x10000
84 
85 
87 {
88  struct ps2_data *d = (struct ps2_data *) interrupt->extra;
89  d->intr |= (1 << interrupt->line);
90  if (d->intr & d->imask)
92 }
94 {
95  struct ps2_data *d = (struct ps2_data *) interrupt->extra;
96  d->intr &= ~(1 << interrupt->line);
97  if (!(d->intr & d->imask))
99 }
101 {
102  struct ps2_data *d = (struct ps2_data *) interrupt->extra;
103  d->dmac_reg[0x601] |= (1 << interrupt->line);
104  /* TODO: DMA interrupt mask? */
105  if (d->dmac_reg[0x601] & 0xffff)
107 }
109 {
110  struct ps2_data *d = (struct ps2_data *) interrupt->extra;
111  d->dmac_reg[0x601] &= ~(1 << interrupt->line);
112  /* TODO: DMA interrupt mask? */
113  if (!(d->dmac_reg[0x601] & 0xffff))
115 }
117 {
118  /* Note: sbus irq 0 = mask 0x100, sbus irq 1 = mask 0x400 */
119  struct ps2_data *d = (struct ps2_data *) interrupt->extra;
120  d->sbus_smflg |= (1 << (8 + interrupt->line * 2));
121  /* TODO: SBUS interrupt mask? */
122  if (d->sbus_smflg != 0)
124 }
126 {
127  /* Note: sbus irq 0 = mask 0x100, sbus irq 1 = mask 0x400 */
128  struct ps2_data *d = (struct ps2_data *) interrupt->extra;
129  d->sbus_smflg &= ~(1 << (8 + interrupt->line * 2));
130  /* TODO: SBUS interrupt mask? */
131  if (d->sbus_smflg == 0)
133 }
134 
135 
137 {
138  struct ps2_data *d = (struct ps2_data *) extra;
139  int i;
140 
141  /*
142  * Right now this interrupts every now and then.
143  * The main interrupt in NetBSD should be 100 Hz. TODO.
144  */
145  for (i=0; i<N_PS2_TIMERS; i++) {
146  /* Count-up Enable: TODO: by how much? */
147  if (d->timer_mode[i] & T_MODE_CUE)
148  d->timer_count[i] ++;
149 
150  if (d->timer_mode[i] & (T_MODE_CMPE | T_MODE_OVFE)) {
151  /* Zero return: */
152  if (d->timer_mode[i] & T_MODE_ZRET)
153  d->timer_count[i] = 0;
154 
156 
157  /* timer 1..3 are "single-shot"? TODO */
158  if (i > 0) {
159  d->timer_mode[i] &=
161  }
162  }
163  }
164 }
165 
166 
168 {
169  uint64_t idata = 0, odata = 0;
170  int regnr = 0;
171  struct ps2_data *d = (struct ps2_data *) extra;
172  int timer_nr = 0;
173 
174  if (writeflag == MEM_WRITE)
175  idata = memory_readmax64(cpu, data, len);
176 
177  if (relative_addr >= 0x8000 && relative_addr < 0x8000 + DMAC_REGSIZE) {
178  regnr = (relative_addr - 0x8000) / 16;
179  if (writeflag == MEM_READ)
180  odata = d->dmac_reg[regnr];
181  else
182  d->dmac_reg[regnr] = idata;
183  }
184 
185  /*
186  * Timer control:
187  * The four timers are at offsets 0, 0x800, 0x1000, and 0x1800.
188  */
189  if (relative_addr < TIMER_REGSIZE) {
190  /* 0, 1, 2, or 3 */
191  timer_nr = (relative_addr & 0x1800) >> 11;
192  relative_addr &= (TIMER_OFS-1);
193  }
194 
195  switch (relative_addr) {
196  case 0x0000: /* timer count */
197  if (writeflag == MEM_READ) {
198  odata = d->timer_count[timer_nr];
199  if (timer_nr == 0) {
200  /* :-) TODO: remove this? */
201  d->timer_count[timer_nr] ++;
202  }
203  debug("[ ps2: read timer %i count: 0x%llx ]\n",
204  timer_nr, (long long)odata);
205  } else {
206  d->timer_count[timer_nr] = idata;
207  debug("[ ps2: write timer %i count: 0x%llx ]\n",
208  timer_nr, (long long)idata);
209  }
210  break;
211  case 0x0010: /* timer mode */
212  if (writeflag == MEM_READ) {
213  odata = d->timer_mode[timer_nr];
214  debug("[ ps2: read timer %i mode: 0x%llx ]\n",
215  timer_nr, (long long)odata);
216  } else {
217  d->timer_mode[timer_nr] = idata;
218  debug("[ ps2: write timer %i mode: 0x%llx ]\n",
219  timer_nr, (long long)idata);
220  }
221  break;
222  case 0x0020: /* timer comp */
223  if (writeflag == MEM_READ) {
224  odata = d->timer_comp[timer_nr];
225  debug("[ ps2: read timer %i comp: 0x%llx ]\n",
226  timer_nr, (long long)odata);
227  } else {
228  d->timer_comp[timer_nr] = idata;
229  debug("[ ps2: write timer %i comp: 0x%llx ]\n",
230  timer_nr, (long long)idata);
231  }
232  break;
233  case 0x0030: /* timer hold */
234  if (writeflag == MEM_READ) {
235  odata = d->timer_hold[timer_nr];
236  debug("[ ps2: read timer %i hold: 0x%llx ]\n",
237  timer_nr, (long long)odata);
238  if (timer_nr >= 2)
239  fatal("[ WARNING: ps2: read from non-"
240  "existant timer %i hold register ]\n");
241  } else {
242  d->timer_hold[timer_nr] = idata;
243  debug("[ ps2: write timer %i hold: 0x%llx ]\n",
244  timer_nr, (long long)idata);
245  if (timer_nr >= 2)
246  fatal("[ WARNING: ps2: write to "
247  "non-existant timer %i hold register ]\n",
248  timer_nr);
249  }
250  break;
251 
252  case 0x8000 + D2_CHCR_REG:
253  if (writeflag==MEM_READ) {
254  odata = d->dmac_reg[regnr];
255  /* debug("[ ps2: dmac read from D2_CHCR "
256  "(0x%llx) ]\n", (long long)d->dmac_reg[regnr]); */
257  } else {
258  /* debug("[ ps2: dmac write to D2_CHCR, "
259  "data 0x%016llx ]\n", (long long) idata); */
260  if (idata & D_CHCR_STR) {
261  int length = d->dmac_reg[D2_QWC_REG/0x10] * 16;
262  uint64_t from_addr = d->dmac_reg[
263  D2_MADR_REG/0x10];
264  uint64_t to_addr = d->dmac_reg[
265  D2_TADR_REG/0x10];
266  unsigned char *copy_buf;
267 
268  debug("[ ps2: dmac [ch2] transfer addr="
269  "0x%016llx len=0x%lx ]\n", (long long)
270  d->dmac_reg[D2_MADR_REG/0x10],
271  (long)length);
272 
273  CHECK_ALLOCATION(copy_buf = (unsigned char *) malloc(length));
274 
275  cpu->memory_rw(cpu, cpu->mem, from_addr,
276  copy_buf, length, MEM_READ,
277  CACHE_NONE | PHYSICAL);
278  cpu->memory_rw(cpu, cpu->mem,
279  d->other_memory_base[DMA_CH_GIF] + to_addr,
280  copy_buf, length, MEM_WRITE,
281  CACHE_NONE | PHYSICAL);
282  free(copy_buf);
283 
284  /* Done with the transfer: */
285  d->dmac_reg[D2_QWC_REG/0x10] = 0;
286  idata &= ~D_CHCR_STR;
287 
288  /* interrupt DMA channel 2 */
290  } else
291  debug("[ ps2: dmac [ch2] stopping "
292  "transfer ]\n");
293  d->dmac_reg[regnr] = idata;
294  return 1;
295  }
296  break;
297 
298  case 0x8000 + D2_QWC_REG:
299  case 0x8000 + D2_MADR_REG:
300  case 0x8000 + D2_TADR_REG:
301  /* no debug output */
302  break;
303 
304  case 0xe010: /* dmac interrupt status (and mask, */
305  /* the upper 16 bits) */
306  if (writeflag == MEM_WRITE) {
307  uint32_t oldmask = d->dmac_reg[regnr] & 0xffff0000;
308  /* Clear out those bits that are set in idata: */
309  d->dmac_reg[regnr] &= ~idata;
310  d->dmac_reg[regnr] &= 0xffff;
311  d->dmac_reg[regnr] |= oldmask;
312  if (((d->dmac_reg[regnr] & 0xffff) &
313  ((d->dmac_reg[regnr]>>16) & 0xffff)) == 0) {
315  }
316  } else {
317  /* Hm... make it seem like the mask bits are (at
318  least as much as) the interrupt assertions: */
319  odata = d->dmac_reg[regnr];
320  odata |= (odata << 16);
321  }
322  break;
323 
324  case 0xf000: /* interrupt register */
325  if (writeflag == MEM_READ) {
326  odata = d->intr;
327  debug("[ ps2: read from Interrupt Register:"
328  " 0x%llx ]\n", (long long)odata);
329 
330  /* TODO: This is _NOT_ correct behavior: */
331 // d->intr = 0;
332 // INTERRUPT_DEASSERT(d->intr_irq);
333  } else {
334  debug("[ ps2: write to Interrupt Register: "
335  "0x%llx ]\n", (long long)idata);
336  /* Clear out bits that are set in idata: */
337  d->intr &= ~idata;
338 
339  if ((d->intr & d->imask) == 0)
341  }
342  break;
343 
344  case 0xf010: /* interrupt mask */
345  if (writeflag == MEM_READ) {
346  odata = d->imask;
347  /* debug("[ ps2: read from Interrupt Mask "
348  "Register: 0x%llx ]\n", (long long)odata); */
349  } else {
350  /* debug("[ ps2: write to Interrupt Mask "
351  "Register: 0x%llx ]\n", (long long)idata); */
352  /* Note: written value indicates which bits
353  to _toggle_, not which bits to set! */
354  d->imask ^= idata;
355  }
356  break;
357 
358  case 0xf230: /* sbus interrupt register? */
359  if (writeflag == MEM_READ) {
360  odata = d->sbus_smflg;
361  debug("[ ps2: read from SBUS SMFLG:"
362  " 0x%llx ]\n", (long long)odata);
363  } else {
364  /* Clear bits on write: */
365  debug("[ ps2: write to SBUS SMFLG:"
366  " 0x%llx ]\n", (long long)idata);
367  d->sbus_smflg &= ~idata;
368  /* irq 1 is SBUS */
369  if (d->sbus_smflg == 0)
371  }
372  break;
373  default:
374  if (writeflag==MEM_READ) {
375  debug("[ ps2: read from addr 0x%x: 0x%llx ]\n",
376  (int)relative_addr, (long long)odata);
377  } else {
378  debug("[ ps2: write to addr 0x%x: 0x%llx ]\n",
379  (int)relative_addr, (long long)idata);
380  }
381  }
382 
383  if (writeflag == MEM_READ)
384  memory_writemax64(cpu, data, len, odata);
385 
386  return 1;
387 }
388 
389 
391 {
392  struct ps2_data *d;
393  int i;
394  struct interrupt templ;
395  char n[300];
396 
397  CHECK_ALLOCATION(d = (struct ps2_data *) malloc(sizeof(struct ps2_data)));
398  memset(d, 0, sizeof(struct ps2_data));
399 
401 
402  /* Connect to MIPS irq 2 (interrupt controller) and 3 (dmac): */
403  snprintf(n, sizeof(n), "%s.2", devinit->interrupt_path);
405  snprintf(n, sizeof(n), "%s.3", devinit->interrupt_path);
407 
408  /*
409  * Register interrupts:
410  *
411  * 16 normal IRQs (machine[x].cpu[x].ps2_intr.%i)
412  * 16 DMA IRQs (machine[x].cpu[x].ps2_dmac.%i)
413  * 2 sbus IRQs (machine[x].cpu[x].ps2_sbus.%i)
414  */
415  for (i=0; i<16; i++) {
416  snprintf(n, sizeof(n), "%s.ps2_intr.%i",
417  devinit->interrupt_path, i);
418  memset(&templ, 0, sizeof(templ));
419  templ.line = i;
420  templ.name = n;
421  templ.extra = d;
425  }
426  for (i=0; i<16; i++) {
427  snprintf(n, sizeof(n), "%s.ps2_dmac.%i",
428  devinit->interrupt_path, i);
429  memset(&templ, 0, sizeof(templ));
430  templ.line = i;
431  templ.name = n;
432  templ.extra = d;
436  }
437  for (i=0; i<2; i++) {
438  snprintf(n, sizeof(n), "%s.ps2_sbus.%i",
439  devinit->interrupt_path, i);
440  memset(&templ, 0, sizeof(templ));
441  templ.line = i;
442  templ.name = n;
443  templ.extra = d;
447  }
448 
449  /* Connect to DMA channel 2 irq: */
450  snprintf(n, sizeof(n), "%s.ps2_dmac.2", devinit->interrupt_path);
452 
453  /* Connect to SBUS interrupt, at ps2 interrupt 1: */
454  snprintf(n, sizeof(n), "%s.ps2_intr.1", devinit->interrupt_path);
456 
457  /* Connect to the timers' interrupts: */
458  for (i=0; i<N_PS2_TIMERS; i++) {
459  /* PS2 irq 9 is timer0, etc. */
460  snprintf(n, sizeof(n), "%s.ps2_intr.%i",
461  devinit->interrupt_path, 9 + i);
462  INTERRUPT_CONNECT(n, d->timer_irq[i]);
463  }
464 
466  DEV_PS2_LENGTH, dev_ps2_access, d, DM_DEFAULT, NULL);
468  dev_ps2_tick, d, TICK_STEPS_SHIFT);
469 
470  return 1;
471 }
472 
uint64_t memory_readmax64(struct cpu *cpu, unsigned char *buf, int len)
Definition: memory.cc:55
void fatal(const char *fmt,...)
Definition: main.cc:152
uint64_t dmac_reg[DMAC_REGSIZE/0x10]
void(* interrupt_assert)(struct interrupt *)
Definition: interrupt.h:38
#define DM_DEFAULT
Definition: memory.h:130
void ps2_dmac_interrupt_deassert(struct interrupt *interrupt)
struct interrupt dma_channel2_irq
struct memory * mem
Definition: cpu.h:362
uint32_t timer_count[N_PS2_TIMERS]
#define DMAC_REGSIZE
Definition: ps2_dmacreg.h:53
uint32_t sbus_smflg
void interrupt_handler_register(struct interrupt *templ)
Definition: interrupt.cc:81
#define MEM_READ
Definition: memory.h:116
void(* interrupt_deassert)(struct interrupt *)
Definition: interrupt.h:39
#define N_PS2_TIMERS
struct memory * memory
Definition: machine.h:126
struct interrupt intr_irq
void ps2_dmac_interrupt_assert(struct interrupt *interrupt)
#define D2_MADR_REG
Definition: ps2_dmacreg.h:125
#define D2_QWC_REG
Definition: ps2_dmacreg.h:126
DEVICE_TICK(ps2)
#define T_MODE_CUE
Definition: ee_timerreg.h:114
#define T_MODE_CMPE
Definition: ee_timerreg.h:116
#define TIMER_REGSIZE
Definition: ee_timerreg.h:52
#define CHECK_ALLOCATION(ptr)
Definition: misc.h:239
#define PHYSICAL
Definition: memory.h:126
int(* memory_rw)(struct cpu *cpu, struct memory *mem, uint64_t vaddr, unsigned char *data, size_t len, int writeflag, int cache_flags)
Definition: cpu.h:365
u_short data
Definition: siireg.h:79
uint32_t intr
#define TIMER_OFS
Definition: ee_timerreg.h:53
uint64_t other_memory_base[N_PS2_DMA_CHANNELS]
#define INTERRUPT_ASSERT(istruct)
Definition: interrupt.h:74
#define MEM_WRITE
Definition: memory.h:117
#define D2_TADR_REG
Definition: ps2_dmacreg.h:127
struct interrupt dmac_irq
Definition: device.h:40
DEVICE_ACCESS(ps2)
void ps2_sbus_interrupt_deassert(struct interrupt *interrupt)
#define debug
Definition: dev_adb.cc:57
uint32_t line
Definition: interrupt.h:51
#define INTERRUPT_CONNECT(name, istruct)
Definition: interrupt.h:77
Definition: cpu.h:326
struct machine * machine
Definition: device.h:41
#define N_PS2_DMA_CHANNELS
char * name
Definition: interrupt.h:66
void ps2_intr_interrupt_deassert(struct interrupt *interrupt)
uint32_t imask
void memory_writemax64(struct cpu *cpu, unsigned char *buf, int len, uint64_t data)
Definition: memory.cc:89
uint32_t timer_hold[N_PS2_TIMERS]
void memory_device_register(struct memory *mem, const char *, uint64_t baseaddr, uint64_t len, int(*f)(struct cpu *, struct memory *, uint64_t, unsigned char *, size_t, int, void *), void *extra, int flags, unsigned char *dyntrans_data)
Definition: memory.cc:339
uint32_t timer_mode[N_PS2_TIMERS]
#define DEV_PS2_LENGTH
struct interrupt sbus_irq
#define T_MODE_OVFF
Definition: ee_timerreg.h:122
uint64_t addr
Definition: device.h:46
struct interrupt timer_irq[N_PS2_TIMERS]
#define D_CHCR_STR
Definition: ps2_dmacreg.h:376
void machine_add_tickfunction(struct machine *machine, void(*func)(struct cpu *, void *), void *extra, int clockshift)
Definition: machine.cc:280
#define T_MODE_OVFE
Definition: ee_timerreg.h:118
void * extra
Definition: interrupt.h:59
#define D2_CHCR_REG
Definition: ps2_dmacreg.h:124
void ps2_intr_interrupt_assert(struct interrupt *interrupt)
#define CACHE_NONE
Definition: memory.h:123
uint32_t timer_comp[N_PS2_TIMERS]
#define T_MODE_ZRET
Definition: ee_timerreg.h:112
#define DMA_CH_GIF
Definition: ps2_dmacreg.h:73
#define TICK_STEPS_SHIFT
void ps2_sbus_interrupt_assert(struct interrupt *interrupt)
#define DEV_PS2_GIF_FAKE_BASE
char * interrupt_path
Definition: device.h:50
#define INTERRUPT_DEASSERT(istruct)
Definition: interrupt.h:75
DEVINIT(ps2)

Generated on Sun Sep 30 2018 16:05:18 for GXemul by doxygen 1.8.13