sdk-core v2026.11.05
PS2 SDK - core tier (build-critical EE/IOP modules)
Loading...
Searching...
No Matches
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
10IRX_ID("Timer_Manager", 2, 2);
11extern 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
19enum {
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
53struct 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
73static 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
120static 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
131u32 timer0() { return _lh(TMR0_COUNT); }
132u32 timer1() { return _lh(TMR1_COUNT); }
133u32 timer2() { return _lh(TMR2_COUNT); }
134#if NUM_TIMERS > 3
135u32 timer3() { return _lw(TMR3_COUNT); }
136u32 timer4() { return _lw(TMR4_COUNT); }
137u32 timer5() { return _lw(TMR5_COUNT); }
138#endif
139
140static 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
151int _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
184void *GetTimersTable()
185{
186 return sTimerTable;
187}
188
189int 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
233int 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
274int 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
314void 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
325u32 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
335u32 GetTimerStatus(int timid)
336{
337 return _lh((timid << 4) + 4);
338}
339
340void 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
351u32 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
362void 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
373u32 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
384void 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
394u32 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
403u32 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
412int 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
423u32 (*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
434int 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
464int 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
493static 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
529int 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
620int 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
667int 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}
int CpuResumeIntr(int state)
Definition intrman.c:217
int ReleaseIntrHandler(int irq)
Definition intrman.c:157
int QueryIntrContext(void)
int DisableIntr(int irq, int *res)
Definition intrman.c:385
int CpuSuspendIntr(int *state)
Definition intrman.c:195
int RegisterIntrHandler(int irq, int mode, int(*handler)(void *arg), void *arg)
Definition intrman.c:115
int EnableIntr(int irq)
Definition intrman.c:336
#define COP0_REG_PRId
u32 count
start sector of fragmented bd/file