Working switch2led demonstration on tic55x
[firmerware] / src / driver / tic55x / timer.c
1 /* General timer management.
2  *
3  * The tic55x has multiple 64-bit timers, we'll use timer0 and timer1.
4  *
5  * Since a timer can either be set to fire or continue to count,
6  * but not both, we'll need two timers:
7  *
8  *  - timer0 counts until the next time-based interrupt
9  *  - timer1 counts the time since the system started
10  *
11  * The reason for assigning the timers like this?  timer0 has a higher
12  * interrupt level than, say, uart.  That helps to get more accuracy
13  * from a realtime system.
14  *
15  * The tic55x timers are 64-bit, and can be split in two halves.
16  *
17  * The first half (CNT4:CNT3 and PRD4:PRD3) is used as a prescale-counter,
18  * to reduce the crystal clock frequency to a 1 ms pace.  This is chained
19  * to the second half (CNT2:CNT1), which then forms the actual timer used
20  * by the 0cpm firmerware.
21  *
22  * The actual 2:1 timer1 is a 32-bit counter that counts round and round
23  * in circles, without ever causing an interrupt.  The actual 2:1 timer0
24  * is set to the next interrupt time, and causes an interrupt when it is
25  * reached.  This setting is based on the requested interrupt time and
26  * the value in timer1 at the time of the request.
27  *
28  * The top half takes care of setting up timer queues to implement a more
29  * general timing discipline, processing interrupts at such timeouts.
30  *
31  *
32  * From: Rick van Rein <rick@openfortress.nl>
33  */
34
35
36 #include <stdint.h>
37 #include <stdbool.h>
38
39 #define BOTTOM
40 #include <config.h>
41
42 #include <0cpm/cpu.h>
43 #include <0cpm/irq.h>
44 #include <0cpm/timer.h>
45
46
47 /* Global variables */
48
49 static timing_t current_timer = 0;
50
51
52 /* Read the current time from timer1's top part.  Be careful about
53  * timer increments between the two word reads.
54  *
55  * The tic55x copies timer registers to shadow registers to ensure
56  * that all values are in sync; the code need not verify that.
57  */
58 timing_t bottom_time (void) {
59         uint16_t cnt1 = GPTCNT1_1;
60         uint16_t cnt2 = GPTCNT2_1;
61         return (((timing_t) cnt2) << 16) | ((timing_t) cnt1);
62 }
63
64
65 /* Setup a new timing for the following timer interrupt.  If one is
66  * currently underway, disable it so it will not spark new interrupts
67  * after this function returns.
68  */
69 timing_t bottom_timer_set (timing_t tim) {
70         timing_t intval, previous;
71         /* First of all, reset timer0 so it can be configured */
72         GPTGCTL1_0 &= ~ ( REGVAL_GCTL_TIM12RS | REGVAL_GCTL_TIM34RS );
73         IFR0 = (1 << REGBIT_IER0_TINT0);
74         previous = current_timer;
75         current_timer = tim;
76         /* Poll the running timer1 */
77         // tim = bottom_time () + TIME_MSEC(1500);
78         intval = tim - bottom_time ();
79 // intval = intval & 0x0000ffff;
80         if ((intval == 0) || (intval >> 31)) {
81                 /* Cause interrupt right now, do not run the timer */
82                 top_timer_expiration (tim);
83                 current_timer = 0;
84 bottom_led_set (0, 1);
85 { uint32_t ctr = 6500000; while (ctr > 0) ctr--; }
86 bottom_led_set (0, 0);
87 { uint32_t ctr = 6500000; while (ctr > 0) ctr--; }
88                 return previous;
89         }
90         /* Setup counter value and period */
91         GPTCNT3_0 = GPTCNT3_1;  /* also copies GPTCNT4_1 to shadow register */
92         GPTCNT4_0 = GPTCNT4_1;  /* retrieve shadow register for GPTCNT4_1 */
93         GPTCNT2_0 =
94         GPTCNT1_0 = 0x0000;
95         GPTPRD2_0 = (uint16_t) (intval >> 16   );
96         GPTPRD1_0 = (uint16_t)  intval          ; //(intval & 0xffff);
97         /* Finally, enable timer1 so it can cause interrupts */
98         GPTGCTL1_0 |= REGVAL_GCTL_TIM12RS | REGVAL_GCTL_TIM34RS;
99 bottom_led_set (0, 1);
100 { uint32_t ctr = 650000; while (ctr > 0) ctr--; }
101 bottom_led_set (0, 0);
102 { uint32_t ctr = 650000; while (ctr > 0) ctr--; }
103         return previous;
104 }
105
106
107 /* Timer interrupt handler for timer0 expiration */
108 interrupt void tic55x_tint0_isr (void) {
109 #if 0
110         extern bool tic55x_top_has_been_interrupted;
111         timing_t now = bottom_time ();
112         current_timer = 0;
113         top_timer_expiration (now);
114         tic55x_top_has_been_interrupted = true;
115 #endif
116 }
117
118
119 /* Setup timers and the corresponding interrupts for timer0/timer1
120  */
121 void tic55x_setup_timers (void) {
122         /* Be sure that the timers are reset, so they can be configured */
123         GPTGCTL1_0 =
124         GPTGCTL1_1 = 0;
125         /* Let the lower half of timer0/timer1 count once per ms */
126         GPTPRD4_0 =
127         GPTPRD4_1 = (uint16_t) ((SYSCLK1_TO_MS_DIVIDER-1) >> 16);
128         GPTPRD3_0 =
129         GPTPRD3_1 = (uint16_t) ((SYSCLK1_TO_MS_DIVIDER-1) & 0xffff);
130         /* Let the upper half of timer1 count with a 2^32 period */
131         GPTPRD2_1 =
132         GPTPRD1_1 = 0xffff;
133         /* Let timer1 run entirely free; let timer0 lower half run free */
134         GPTCTL1_0 = REGVAL_CTL12_ENAMODE_ONETIME;
135         GPTCTL1_1 = REGVAL_CTL12_ENAMODE_CONTINUOUS;
136         GPTGCTL1_0 = REGVAL_GCTL_TIMMODE_DUAL32CHAINED;
137         GPTGCTL1_1 = REGVAL_GCTL_TIMMODE_DUAL32CHAINED | REGVAL_GCTL_TIM12RS | REGVAL_GCTL_TIM34RS;
138         /* Clear, then permit interrupts from timer0 */
139         IFR0  = (1 << REGBIT_IER0_TINT0);
140 }
141