1 /*
2 * Copyright (c) 2021 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 "platform_p.h"
9
10 #include <assert.h>
11 #include <lk/err.h>
12 #include <lk/debug.h>
13 #include <lk/reg.h>
14 #include <lk/trace.h>
15 #include <kernel/debug.h>
16 #include <kernel/thread.h>
17 #include <platform/interrupts.h>
18 #include <platform/virt.h>
19 #include <platform/timer.h>
20 #include <platform.h>
21
22 #define LOCAL_TRACE 0
23
24 // implementation of RTC at
25 // https://github.com/qemu/qemu/blob/master/hw/rtc/goldfish_rtc.c
26 volatile unsigned int * const goldfish_rtc_base = (void *)VIRT_GF_RTC_MMIO_BASE;
27
28 // registers
29 enum {
30 RTC_TIME_LOW = 0x00,
31 RTC_TIME_HIGH = 0x04,
32 RTC_ALARM_LOW = 0x08,
33 RTC_ALARM_HIGH = 0x0c,
34 RTC_IRQ_ENABLED = 0x10,
35 RTC_CLEAR_ALARM = 0x14,
36 RTC_ALARM_STATUS = 0x18,
37 RTC_CLEAR_INTERRUPT = 0x1c,
38 };
39
40 static uint64_t system_boot_offset;
41
42 static platform_timer_callback t_callback;
43 static void *t_arg;
44
write_reg(int reg,uint32_t val)45 static void write_reg(int reg, uint32_t val) {
46 goldfish_rtc_base[reg / 4] = val;
47 }
48
read_reg(int reg)49 static uint32_t read_reg(int reg) {
50 return goldfish_rtc_base[reg / 4];
51 }
52
53 // raw time from the RTC is ns wall time
read_raw_time(void)54 static uint64_t read_raw_time(void) {
55 uint32_t low, high;
56
57 // read both registers and assemble a 64bit counter
58 // reading low first latches a shadow high register which will prevent wraparound
59 low = read_reg(RTC_TIME_LOW);
60 high = read_reg(RTC_TIME_HIGH);
61
62 return ((uint64_t)high << 32) | low;
63 }
64
rtc_irq(void * unused)65 enum handler_return rtc_irq(void *unused) {
66 enum handler_return ret = INT_NO_RESCHEDULE;
67
68 write_reg(RTC_CLEAR_ALARM, 1);
69 write_reg(RTC_CLEAR_INTERRUPT, 1);
70
71 if (t_callback) {
72 ret = t_callback(t_arg, current_time());
73 }
74
75 return ret;
76 }
77
goldfish_rtc_early_init(void)78 void goldfish_rtc_early_init(void) {
79 // sample the timer and use it as a offset for system start
80 system_boot_offset = read_raw_time();
81
82 // clear and stop any pending irqs on the timer
83 platform_stop_timer();
84
85 register_int_handler(GOLDFISH_RTC_IRQ, &rtc_irq, NULL);
86 unmask_interrupt(GOLDFISH_RTC_IRQ);
87
88 // its okay to enable the irq since we've cleared the alarm and any pending interrupts
89 write_reg(RTC_IRQ_ENABLED, 1);
90 }
91
goldfish_rtc_init(void)92 void goldfish_rtc_init(void) {
93 }
94
current_time_hires(void)95 lk_bigtime_t current_time_hires(void) {
96 uint64_t t = read_raw_time() - system_boot_offset;
97
98 return t / 1000ULL; // ns -> us
99 }
100
current_time(void)101 lk_time_t current_time(void) {
102 uint64_t t = read_raw_time() - system_boot_offset;
103
104 return (lk_time_t)(t / 1000000ULL); // ns -> ms
105 }
106
platform_set_oneshot_timer(platform_timer_callback callback,void * arg,lk_time_t interval)107 status_t platform_set_oneshot_timer (platform_timer_callback callback, void *arg, lk_time_t interval) {
108 LTRACEF("callback %p, arg %p, interval %u\n", callback, arg, interval);
109
110 t_callback = callback;
111 t_arg = arg;
112
113 uint64_t delta = read_raw_time();
114 delta += interval * 1000000ULL;
115
116 write_reg(RTC_ALARM_HIGH, delta >> 32);
117 write_reg(RTC_ALARM_LOW, delta & 0xffffffff);
118
119 return NO_ERROR;
120 }
121
platform_stop_timer(void)122 void platform_stop_timer(void) {
123 LTRACE;
124
125 write_reg(RTC_CLEAR_ALARM, 1);
126 write_reg(RTC_CLEAR_INTERRUPT, 1);
127 }
128
129
130