PS2SDK
PS2 Homebrew Libraries
timrman.c
1 #include "timrman.h"
2 #include "kerr.h"
3 #include "xtimrman.h"
4 #include <mipscopaccess.h>
5 
6 #include "irx_imports.h"
7 
8 #include <tamtypes.h>
9 
10 IRX_ID("Timer_Manager", 2, 2);
11 extern struct irx_export_table _exp_timrman;
12 
13 #ifdef BUILDING_TIMRMANP
14 #define NUM_TIMERS 3
15 #else
16 #define NUM_TIMERS 6
17 #endif
18 
19 enum {
20  TMR0_COUNT = 0xBF801100,
21  TMR1_COUNT = 0xBF801110,
22  TMR2_COUNT = 0xBF801120,
23 #if NUM_TIMERS > 3
24  TMR3_COUNT = 0xBF801480,
25  TMR4_COUNT = 0xBF801490,
26  TMR5_COUNT = 0xBF8014A0,
27 #endif
28 
29  TMR_CTRL_OFFSET = 4,
30  TMR_CMP_OFFSET = 8,
31 
32  TMR_IN_PS1_MODE = 0xBF801450,
33 
34  TMR_HOLD_REG = 0xBF8014B0,
35  TMR_HOLD_MODE = 0xBF8014C0,
36 
37  // reśet counter on irq
38  TMR_CTRL_ZERO_RETURN = 1 << 3,
39 
40  // IRQ enable
41  TMR_CTRL_CMP_IRQ_ENABLE = 1 << 4,
42  TMR_CTRL_OVFL_IRQ_ENABLE = 1 << 5,
43  TMR_CTRL_IRQ_ENABLE = 1 << 11,
44  TMR_CTRL_IRQ_REPEAT = 1 << 6,
45 
46  TMR_CTRL_EXT_SIGNAL = 1 << 8,
47 
48  // IRQ status
49  TMR_CTRL_CMP_IRQ = 1 << 11,
50  TMR_CTRL_OVFL_IRQ = 1 << 12,
51 };
52 
53 struct Timer
54 {
55  u32 addr;
56  u8 sources;
57  u8 size;
58  u16 max_prescale;
59  u8 irq;
60  s8 users;
61  u8 has_irq_handler;
62  u32 mode;
63  u32 ctrl_config;
64  u16 timeup_flags;
65  u16 overflow_flags;
66  u32 compare_value;
67  unsigned int (*timeup_handler)(void *userdata);
68  void *timeup_common;
69  unsigned int (*overflow_handler)(void *userdata);
70  void *overflow_common;
71 };
72 
73 static struct Timer sTimerTable[NUM_TIMERS] = {
74  {
75  .addr = TMR0_COUNT,
76  .sources = 0xB,
77  .size = 16,
78  .max_prescale = 1,
79  .irq = 4,
80  },
81  {
82  .addr = TMR1_COUNT,
83  .sources = 0xD,
84  .size = 16,
85  .max_prescale = 1,
86  .irq = 5,
87  },
88  {
89  .addr = TMR2_COUNT,
90  .sources = 1,
91  .size = 16,
92  .max_prescale = 8,
93  .irq = 6,
94  },
95 #if NUM_TIMERS > 3
96  {
97  .addr = TMR3_COUNT,
98  .sources = 5,
99  .size = 32,
100  .max_prescale = 1,
101  .irq = 0xE,
102  },
103  {
104  .addr = TMR4_COUNT,
105  .sources = 1,
106  .size = 32,
107  .max_prescale = 256,
108  .irq = 0xF,
109  },
110  {
111  .addr = TMR5_COUNT,
112  .sources = 1,
113  .size = 32,
114  .max_prescale = 256,
115  .irq = 0x10,
116  },
117 #endif
118 };
119 
120 static int sIndexMap[NUM_TIMERS] = {
121  2,
122 #if NUM_TIMERS > 3
123  5,
124  4,
125  3,
126 #endif
127  0,
128  1,
129 };
130 
131 u32 timer0() { return _lh(TMR0_COUNT); }
132 u32 timer1() { return _lh(TMR1_COUNT); }
133 u32 timer2() { return _lh(TMR2_COUNT); }
134 #if NUM_TIMERS > 3
135 u32 timer3() { return _lw(TMR3_COUNT); }
136 u32 timer4() { return _lw(TMR4_COUNT); }
137 u32 timer5() { return _lw(TMR5_COUNT); }
138 #endif
139 
140 static u32 (*sTimerCountFun[NUM_TIMERS])() = {
141  timer0,
142  timer1,
143  timer2,
144 #if NUM_TIMERS > 3
145  timer3,
146  timer4,
147  timer5,
148 #endif
149 };
150 
151 int _start(int argc, char **argv)
152 {
153  int prid, ret;
154  prid = get_mips_cop_reg(0, COP0_REG_PRId);
155 
156 #ifdef BUILDING_TIMRMANP
157  if (prid >= 16) {
158  if ((_lw(TMR_IN_PS1_MODE) & 8) == 0) {
159  return MODULE_NO_RESIDENT_END;
160  }
161  }
162 #else
163  if (prid < 16) {
164  return MODULE_NO_RESIDENT_END;
165  }
166 
167  if ((_lw(TMR_IN_PS1_MODE) & 8) != 0) {
168  return MODULE_NO_RESIDENT_END;
169  }
170 #endif
171 
172  for (int i = 0; i < NUM_TIMERS; i++) {
173  sTimerTable[i].users = 0;
174  }
175 
176  ret = RegisterLibraryEntries(&_exp_timrman);
177  if (ret != 0) {
178  return MODULE_NO_RESIDENT_END;
179  }
180 
181  return MODULE_RESIDENT_END;
182 }
183 
184 void *GetTimersTable()
185 {
186  return sTimerTable;
187 }
188 
189 int AllocHardTimer(int source, int size, int prescale)
190 {
191  struct Timer *timer;
192  int oldstat, timid;
193 
194  if (QueryIntrContext()) {
195  return KE_ILLEGAL_CONTEXT;
196  }
197 
198  CpuSuspendIntr(&oldstat);
199 
200  for (int i = 0; i < NUM_TIMERS; i++) {
201  timer = &sTimerTable[sIndexMap[i]];
202  if (timer->users != 0) {
203  continue;
204  }
205 
206  if ((timer->sources & source) == 0) {
207  continue;
208  }
209 
210  if (timer->size != size || timer->max_prescale < prescale) {
211  continue;
212  }
213 
214  timid = ((sIndexMap[i] + 1) << 28) | (timer->addr >> 4);
215  SetTimerMode(timid, 0);
216 
217  DisableIntr(timer->irq, NULL);
218  timer->users++;
219  timer->ctrl_config = 0;
220  timer->timeup_flags = 0;
221  timer->overflow_flags = 0;
222  timer->timeup_handler = 0;
223  timer->overflow_handler = 0;
224  CpuResumeIntr(oldstat);
225 
226  return timid;
227  }
228 
229  CpuResumeIntr(oldstat);
230  return KE_NO_TIMER;
231 }
232 
233 int ReferHardTimer(int source, int size, int mode, int modemask)
234 {
235  struct Timer *timer;
236  int oldstat;
237 
238  if (QueryIntrContext()) {
239  return KE_ILLEGAL_CONTEXT;
240  }
241 
242  CpuSuspendIntr(&oldstat);
243 
244 
245  for (int i = 0; i < NUM_TIMERS; i++) {
246  timer = &sTimerTable[i];
247  if (timer->users == 0) {
248  continue;
249  }
250 
251  if (timer->mode == 0) {
252  continue;
253  }
254 
255  if ((timer->sources & source) == 0) {
256  continue;
257  }
258 
259  if (timer->size != size || (timer->mode & modemask) != mode) {
260  continue;
261  }
262 
263  timer->users++;
264  CpuResumeIntr(oldstat);
265 
266  // Should be correct, but reads very poorly in decompilers
267  return ((i << 28) + 1) | (timer->addr >> 4);
268  }
269 
270  CpuResumeIntr(oldstat);
271  return KE_NO_TIMER;
272 }
273 
274 int FreeHardTimer(int timid)
275 {
276  struct Timer *timer;
277  u32 timer_idx = (timid >> 28) - 1;
278  int oldstat;
279 
280  if (QueryIntrContext()) {
281  return KE_ILLEGAL_CONTEXT;
282  }
283 
284  timer = &sTimerTable[timer_idx];
285  CpuSuspendIntr(&oldstat);
286 
287  if (!timer->users) {
288  CpuResumeIntr(oldstat);
289  return KE_ILLEGAL_TIMERID;
290  }
291 
292  timer->users--;
293  // what is this shift for (it's a bnez, not sign compare)?
294  if ((timer->users << 24) != 0) {
295  SetTimerMode(timid, 0);
296  DisableIntr(timer->irq, NULL);
297  timer->ctrl_config = 0;
298  timer->timeup_flags = 0;
299  timer->overflow_flags = 0;
300  timer->timeup_handler = 0;
301  timer->overflow_handler = 0;
302 
303  if (timer->has_irq_handler) {
304  ReleaseIntrHandler(timer->irq);
305  }
306 
307  timer->has_irq_handler = 0;
308  }
309 
310  CpuResumeIntr(oldstat);
311  return 0;
312 }
313 
314 void SetTimerMode(int timid, int mode)
315 {
316  u32 timer = (timid >> 28) - 1;
317  if (timer >= NUM_TIMERS) {
318  return;
319  }
320 
321  sTimerTable[timer].mode = mode;
322  _sh(mode, (timid << 4) + 4);
323 }
324 
325 u32 GetTimerMode(int timid)
326 {
327  u32 timer = (timid >> 28) - 1;
328  if (timer >= NUM_TIMERS) {
329  return KE_ILLEGAL_TIMERID;
330  }
331 
332  return sTimerTable[timer].mode;
333 }
334 
335 u32 GetTimerStatus(int timid)
336 {
337  return _lh((timid << 4) + 4);
338 }
339 
340 void SetTimerCounter(int timid, u32 count)
341 {
342  u32 addr = timid << 4;
343 
344  if (addr & 0x400) {
345  _sw(count, addr);
346  } else {
347  _sh(count, addr);
348  }
349 }
350 
351 u32 GetTimerCounter(int timid)
352 {
353  u32 addr = timid << 4;
354 
355  if (addr & 0x400) {
356  return _lw(addr);
357  } else {
358  return _lh(addr);
359  }
360 }
361 
362 void SetTimerCompare(int timid, u32 compare)
363 {
364  u32 addr = timid << 4;
365 
366  if (addr & 0x400) {
367  _sw(compare, addr + 8);
368  } else {
369  _sh(compare, addr + 8);
370  }
371 }
372 
373 u32 GetTimerCompare(int timid)
374 {
375  u32 addr = timid << 4;
376 
377  if (addr & 0x400) {
378  return _lw(addr + 8);
379  } else {
380  return _lh(addr + 8);
381  }
382 }
383 
384 void SetHoldMode(int holdnum, int mode)
385 {
386 #ifndef BUILDING_TIMRMANP
387  u32 hold = _lw(TMR_HOLD_MODE);
388  hold &= ~(0xF << (4 * holdnum));
389  hold |= (mode & 0xF) << (4 * holdnum);
390  _sw(TMR_HOLD_MODE, hold);
391 #endif
392 }
393 
394 u32 GetHoldMode(int holdnum)
395 {
396 #ifdef BUILDING_TIMRMANP
397  return 0;
398 #else
399  return _lw(TMR_HOLD_MODE >> (holdnum * 4)) & 0xF;
400 #endif
401 }
402 
403 u32 GetHoldReg(int holdnum)
404 {
405 #ifdef BUILDING_TIMRMANP
406  return 0;
407 #else
408  return _lw(TMR_HOLD_REG + (holdnum * 4));
409 #endif
410 }
411 
412 int GetHardTimerIntrCode(int timid)
413 {
414  u32 timer = (timid >> 28) - 1;
415  if (timer >= NUM_TIMERS) {
416  return KE_ILLEGAL_TIMERID;
417  }
418 
419  return sTimerTable[timer].irq;
420 }
421 
422 // no clue on official name
423 u32 (*GetTimerReadFunc(int timid))()
424 {
425  u32 timer = (timid >> 28) - 1;
426  if (timer >= NUM_TIMERS) {
427  return (void *)KE_ILLEGAL_TIMERID;
428  }
429 
430  return sTimerCountFun[timer];
431 }
432 
433 
434 int SetTimerHandler(int timid, unsigned long comparevalue, unsigned int (*timeuphandler)(void *userdata), void *common)
435 {
436  struct Timer *timer;
437  int oldstat;
438  u32 timer_idx = (timid >> 28) - 1;
439  if (timer_idx >= NUM_TIMERS) {
440  return KE_ILLEGAL_TIMERID;
441  }
442 
443  CpuSuspendIntr(&oldstat);
444  timer = &sTimerTable[timer_idx];
445 
446  if (timer->mode != 0) {
447  CpuResumeIntr(oldstat);
448  return -156;
449  }
450 
451  timer->compare_value = comparevalue;
452  timer->timeup_handler = timeuphandler;
453  timer->timeup_common = common;
454  if (timeuphandler) {
455  timer->timeup_flags = TMR_CTRL_ZERO_RETURN | TMR_CTRL_CMP_IRQ_ENABLE | TMR_CTRL_IRQ_REPEAT;
456  } else {
457  timer->timeup_flags = 0;
458  }
459 
460  CpuResumeIntr(oldstat);
461  return 0;
462 }
463 
464 int SetOverflowHandler(int timid, unsigned int (*handler)(void *userdata), void *common)
465 {
466  struct Timer *timer;
467  int oldstat;
468 
469  u32 timer_idx = (timid >> 28) - 1;
470  if (timer_idx >= NUM_TIMERS) {
471  return KE_ILLEGAL_TIMERID;
472  }
473 
474  CpuSuspendIntr(&oldstat);
475  timer = &sTimerTable[timer_idx];
476 
477  if (timer->mode != 0) {
478  CpuResumeIntr(oldstat);
479  return -156;
480  }
481 
482  timer->overflow_handler = handler;
483  timer->overflow_common = common;
484  if (handler != NULL) {
485  timer->overflow_flags = TMR_CTRL_OVFL_IRQ_ENABLE | TMR_CTRL_IRQ_REPEAT;
486  } else {
487  timer->overflow_flags = 0;
488  }
489 
490  return 0;
491 }
492 
493 static int irqHandler(void *arg)
494 {
495  struct Timer *timer = arg;
496  u32 stop = 0;
497  u32 cb_ret;
498 
499  u32 ctrl = _lh(timer->addr + TMR_CTRL_OFFSET);
500  if (ctrl & TMR_CTRL_OVFL_IRQ) {
501  cb_ret = timer->overflow_handler(timer->overflow_common);
502  if (cb_ret == 0) {
503  stop = 1 << 5;
504  }
505  }
506 
507  if (ctrl & TMR_CTRL_CMP_IRQ) {
508  cb_ret = timer->timeup_handler(timer->timeup_common);
509  if (cb_ret == 0) {
510  stop |= 1 << 4;
511  } else {
512  if (timer->size == 16) {
513  _sh(cb_ret, timer->addr + TMR_CMP_OFFSET);
514  } else {
515  _sw(cb_ret, timer->addr + TMR_CMP_OFFSET);
516  }
517  }
518  }
519 
520  if (stop || timer->mode == 0) {
521  timer->mode = 0;
522  _sh(0, timer->addr + TMR_CTRL_OFFSET);
523  return 0;
524  }
525 
526  return 1;
527 }
528 
529 int SetupHardTimer(int timid, int source, int mode, int prescale)
530 {
531  u32 timer_idx = (timid >> 28) - 1;
532  struct Timer *timer;
533  int ret, oldstat;
534  u32 new_mode;
535 
536  if (QueryIntrContext()) {
537  return KE_ILLEGAL_CONTEXT;
538  }
539 
540  if (timer_idx >= NUM_TIMERS) {
541  return KE_ILLEGAL_TIMERID;
542  }
543 
544  CpuSuspendIntr(&oldstat);
545  timer = &sTimerTable[timer_idx];
546 
547  if (timer->mode != 0) {
548  CpuResumeIntr(oldstat);
549  return -154;
550  }
551 
552  timer->ctrl_config = 0;
553  if (!timer->has_irq_handler) {
554  DisableIntr(timer->irq, NULL);
555 
556  ret = RegisterIntrHandler(timer->irq, 1, irqHandler, timer);
557  if (ret < 0) {
558  CpuResumeIntr(oldstat);
559  return ret;
560  }
561 
562  timer->has_irq_handler = 1;
563  }
564 
565  switch (mode) {
566  case 0:
567  case 1:
568  case 3:
569  case 5:
570  case 7:
571  break;
572  default:
573  CpuResumeIntr(oldstat);
574  return KE_ILLEGAL_MODE;
575  }
576 
577  new_mode = mode | 0x80000000;
578 
579  switch (source) {
580  case 1:
581  break;
582  case 2:
583  case 4:
584  new_mode |= TMR_CTRL_EXT_SIGNAL;
585  break;
586  default:
587  CpuResumeIntr(oldstat);
588  return -152;
589  }
590 
591  switch (prescale) {
592  case 8:
593 #if NUM_TIMERS > 3
594  if (timer_idx >= 3)
595  {
596  new_mode |= 0x2000;
597  }
598  else
599 #endif
600  {
601  new_mode |= 0x200;
602  }
603  break;
604  case 16:
605  new_mode |= 0x4000;
606  break;
607  case 256:
608  new_mode |= 0x6000;
609  break;
610  default:
611  CpuResumeIntr(oldstat);
612  return -153;
613  }
614 
615  timer->ctrl_config = new_mode;
616  CpuResumeIntr(oldstat);
617  return 0;
618 }
619 
620 int StartHardTimer(int timid)
621 {
622  int oldstat;
623  struct Timer *timer;
624  u32 timer_idx = (timid >> 28) - 1;
625  if (timer_idx >= NUM_TIMERS) {
626  return KE_ILLEGAL_TIMERID;
627  }
628 
629  CpuSuspendIntr(&oldstat);
630  timer = &sTimerTable[timer_idx];
631  if (timer->mode != 0) {
632  CpuResumeIntr(oldstat);
633  return -154;
634  }
635 
636  if (timer->ctrl_config != 0) {
637  CpuResumeIntr(oldstat);
638  return -155;
639  }
640 
641  _sh(0, timer->addr + TMR_CTRL_OFFSET);
642 
643  if (timer->timeup_flags) {
644 #if NUM_TIMERS > 3
645  if (timer_idx > 3)
646  {
647  _sw(timer->compare_value, timer->addr + TMR_CMP_OFFSET);
648  }
649  else
650 #endif
651  {
652  _sh(timer->compare_value, timer->addr + TMR_CMP_OFFSET);
653  }
654  }
655 
656  timer->mode = timer->ctrl_config | timer->timeup_flags | timer->overflow_flags;
657  CpuResumeIntr(oldstat);
658  _sh(timer->mode, timer->addr + TMR_CTRL_OFFSET);
659 
660  if (timer->mode & 0x30) {
661  EnableIntr(timer->irq);
662  }
663 
664  return 0;
665 }
666 
667 int StopHardTimer(int timid)
668 {
669  int oldstat;
670  struct Timer *timer;
671  u32 timer_idx = (timid >> 28) - 1;
672  if (timer_idx >= NUM_TIMERS) {
673  return KE_ILLEGAL_TIMERID;
674  }
675 
676  CpuSuspendIntr(&oldstat);
677  timer = &sTimerTable[timer_idx];
678  if (timer->mode == 0) {
679  CpuResumeIntr(oldstat);
680  return -156;
681  }
682 
683  if (timer->mode & 0x30) {
684  DisableIntr(timer->irq, NULL);
685  }
686 
687  timer->mode = 0;
688  _sh(0, timer->addr + 4);
689 
690  return 0;
691 }
RegisterIntrHandler
int RegisterIntrHandler(int irq, int mode, int(*handler)(void *arg), void *arg)
Definition: intrman.c:115
ReleaseIntrHandler
int ReleaseIntrHandler(int irq)
Definition: intrman.c:157
xtimrman.h
DisableIntr
int DisableIntr(int irq, int *res)
Definition: intrman.c:385
QueryIntrContext
int QueryIntrContext(void)
CpuSuspendIntr
int CpuSuspendIntr(int *state)
Definition: intrman.c:195
mipscopaccess.h
timrman.h
count
u32 count
start sector of fragmented bd/file
Definition: usbhdfsd-common.h:3
EnableIntr
int EnableIntr(int irq)
Definition: intrman.c:336
irx_export_table
Definition: irx.h:90
tamtypes.h
CpuResumeIntr
int CpuResumeIntr(int state)
Definition: intrman.c:217
Timer
Definition: timrman.c:53
COP0_REG_PRId
@ COP0_REG_PRId
Definition: mipscopaccess.h:42
kerr.h