1 /*
2 * Copyright (c) 2015 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include <arch/mips.h>
9 #include <lk/err.h>
10 #include <lk/trace.h>
11 #include <lk/debug.h>
12 #include <assert.h>
13 #include <stdint.h>
14 #include <lk/bits.h>
15 #include <arch/ops.h>
16 #include <platform.h>
17 #include <platform/timer.h>
18
19 #define LOCAL_TRACE 0
20
21 static volatile uint64_t ticks;
22 static volatile uint32_t last_compare_set;
23
24 static uint32_t tick_rate;
25 static uint32_t tick_rate_mhz;
26
27 static lk_time_t tick_interval_ms;
28 static lk_bigtime_t tick_interval_us;
29 static uint32_t tick_interval;
30
31 static platform_timer_callback cb;
32 static void *cb_args;
33
mips_timer_irq(void)34 enum handler_return mips_timer_irq(void) {
35 LTRACEF("count 0x%x\n", mips_read_c0_count());
36 LTRACEF("compare 0x%x\n", mips_read_c0_compare());
37
38 /* reset it for the next interval */
39 retry:
40 ticks++;
41 last_compare_set += tick_interval;
42 uint32_t count = mips_read_c0_count();
43 if (unlikely(TIME_GT(count, last_compare_set))) {
44 /* if it took us too long to get to this irq, make sure it fires immediately */
45 //printf("took too long to service timer irq! %u %u\n", count, last_compare_set);
46 goto retry;
47 //mips_write_c0_compare(mips_read_c0_count() + tick_rate_mhz);
48 } else {
49 mips_write_c0_compare(last_compare_set);
50 }
51
52 enum handler_return ret = INT_NO_RESCHEDULE;
53 if (cb) {
54 lk_time_t now = current_time();
55 ret = cb(cb_args, now);
56 }
57
58 return ret;
59 }
60
platform_set_periodic_timer(platform_timer_callback callback,void * arg,lk_time_t interval)61 status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, lk_time_t interval) {
62 TRACEF("callback %p, arg %p, interval %u\n", callback, arg, interval);
63
64 DEBUG_ASSERT(interval > 0);
65 DEBUG_ASSERT(tick_rate != 0 && tick_rate_mhz != 0);
66
67 cb = callback;
68 cb_args = arg;
69
70 tick_interval_ms = interval;
71 tick_interval_us = interval * 1000;
72 tick_interval = interval * (tick_rate / 1000);
73
74 uint32_t now = mips_read_c0_count();
75 last_compare_set = now + tick_interval;
76 mips_write_c0_compare(last_compare_set);
77
78 // enable the counter
79 mips_write_c0_cause(mips_read_c0_cause() & ~(1<<27));
80
81 return NO_ERROR;
82 }
83
current_time(void)84 lk_time_t current_time(void) {
85 uint64_t t;
86 uint32_t last_compare;
87 uint32_t delta;
88
89 /* sample the tick counter, the last compare register set, and the current count atomically */
90 do {
91 t = ticks;
92 last_compare = last_compare_set;
93 delta = mips_read_c0_count();
94 } while (ticks != t || last_compare_set != last_compare);
95
96 /* convert ticks to msec */
97 delta = (delta - last_compare - tick_interval) / (tick_rate_mhz * 1000);
98 lk_time_t res = (t * tick_interval_ms) + delta;
99
100 return res;
101 }
102
current_time_hires(void)103 lk_bigtime_t current_time_hires(void) {
104 uint64_t t;
105 uint32_t last_compare;
106 uint32_t delta;
107
108 /* sample the tick counter, the last compare register set, and the current count atomically */
109 do {
110 t = ticks;
111 last_compare = last_compare_set;
112 delta = mips_read_c0_count();
113 } while (ticks != t);
114
115 /* convert ticks to usec */
116 delta = (delta - last_compare - tick_interval) / tick_rate_mhz;
117 lk_bigtime_t res = (t * tick_interval_us) + delta;
118
119 return res;
120 }
121
mips_init_timer(uint32_t freq)122 void mips_init_timer(uint32_t freq) {
123 tick_rate = freq;
124 tick_rate_mhz = freq / 1000000;
125
126 // disable the counter
127 mips_write_c0_cause(mips_read_c0_cause() | (1<<27));
128
129 // figure out which interrupt the timer is set to
130 uint32_t ipti = BITS_SHIFT(mips_read_c0_intctl(), 31, 29);
131 if (ipti >= 2) {
132 mips_enable_irq(ipti);
133 }
134 }
135
136