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