1#include <acpi/hpet.h>
2#include <asm.h>
3#include <console/printf.h>
4#include <sch/sched.h>
5#include <stdbool.h>
6#include <stdint.h>
7#include <time/date_time.h>
8#include <time/time.h>
9#include <time/tsc.h>
10
11#include "internal.h"
12
13#define CMOS_ADDRESS 0x70
14#define CMOS_DATA 0x71
15
16void time_print_unix(time_s_t timestamp) {
17 uint32_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
18
19 uint32_t year = 1970;
20 while (timestamp >= 31536000) {
21 if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
22 if (timestamp >= 31622400) {
23 timestamp -= 31622400;
24 } else {
25 break;
26 }
27 } else {
28 timestamp -= 31536000;
29 }
30 year++;
31 }
32
33 uint32_t month = 0;
34 if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
35 days_in_month[1] = 29;
36 }
37
38 for (month = 0; month < 12; month++) {
39 if (timestamp < days_in_month[month] * 86400) {
40 break;
41 }
42 timestamp -= days_in_month[month] * 86400;
43 }
44
45 uint32_t day = timestamp / 86400 + 1;
46 timestamp %= 86400;
47
48 uint32_t hour = timestamp / 3600;
49 timestamp %= 3600;
50 uint32_t minute = timestamp / 60;
51 uint32_t second = timestamp % 60;
52
53 printf(format: "%04d-%02d-%02d %02d:%02d:%02d", year, month + 1, day, hour, minute,
54 second);
55}
56void time_print_current() {
57 time_print_unix(timestamp: time_get_unix());
58}
59
60static uint32_t datetime_to_unix(int year, int month, int day, int hour,
61 int minute, int second) {
62 unsigned int timestamp = 0;
63
64 for (int y = 1970; y < year; y++) {
65 timestamp += is_leap_year(year: y) ? 366 * 24 * 3600 : 365 * 24 * 3600;
66 }
67
68 for (int m = 1; m < month; m++) {
69 timestamp += days_in_month(year, month: m) * 24 * 3600;
70 }
71
72 timestamp += (day - 1) * 24 * 3600;
73
74 timestamp += hour * 3600;
75 timestamp += minute * 60;
76 timestamp += second;
77
78 return timestamp;
79}
80
81static inline uint8_t cmos_read(uint8_t reg) {
82 outb(CMOS_ADDRESS, value: reg);
83 return inb(CMOS_DATA);
84}
85
86static bool is_updating() {
87 outb(CMOS_ADDRESS, value: 0x0A);
88 return inb(CMOS_DATA) & 0x80;
89}
90
91static uint8_t bcd_to_bin(uint8_t bcd) {
92 return ((bcd / 16) * 10) + (bcd & 0x0F);
93}
94
95#define GET_TIME_UNIT(unit, cmos_addr) \
96 uint8_t time_get_##unit() { \
97 uint8_t unit = 0; \
98 while (is_updating()) \
99 ; \
100 unit = cmos_read(cmos_addr); \
101 uint8_t status_b = cmos_read(0x0B); \
102 bool bcd = !(status_b & 0x04); \
103 if (bcd) { \
104 unit = bcd_to_bin(unit); \
105 } \
106 return unit; \
107 }
108
109GET_TIME_UNIT(second, 0x00)
110GET_TIME_UNIT(minute, 0x02)
111GET_TIME_UNIT(hour, 0x04)
112GET_TIME_UNIT(day, 0x07)
113GET_TIME_UNIT(month, 0x08)
114GET_TIME_UNIT(year, 0x09)
115GET_TIME_UNIT(century, 0x32)
116
117uint32_t time_get_unix() {
118 return datetime_to_unix(year: time_get_century() * 100 + time_get_year(),
119 month: time_get_month(), day: time_get_day(), hour: time_get_hour(),
120 minute: time_get_minute(), second: time_get_second());
121}
122
123time_ns_t time_get_ns() {
124 if (global.current_bootstage < BOOTSTAGE_MID_MP)
125 return hpet_timestamp_ns();
126
127 return timekeeper_get_ns();
128}
129
130time_ms_t time_get_ms(void) {
131 return US_TO_MS(time_get_us());
132}
133
134bool time_try_get_ms(time_ms_t *out) {
135 if (global.current_bootstage < BOOTSTAGE_MID_MP) {
136 *out = NS_TO_MS(hpet_timestamp_ns());
137 return true;
138 }
139
140 time_ns_t ns;
141 if (!timekeeper_try_get_ns(out: &ns))
142 return false;
143
144 *out = NS_TO_MS(ns);
145 return true;
146}
147
148time_us_t time_get_us(void) {
149 if (global.current_bootstage < BOOTSTAGE_MID_MP)
150 return hpet_timestamp_us();
151
152 return timekeeper_get_us();
153}
154
155freq_hz_t tsc_calibrate(void) {
156 return tsc_calibrate_hpet();
157}
158