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