1 /*
2 * Copyright 2005-2007, Axel Dörfler, axeld@pinc-software.de
3 * Copyright 2003, Jeff Ward, jeff@r2d2.stcloudstate.edu. All rights reserved.
4 *
5 * Distributed under the terms of the MIT License.
6 */
7
8
9 #include <arch/real_time_clock.h>
10 #include <arch/cpu.h>
11 #include <boot/kernel_args.h>
12
13 #include <real_time_clock.h>
14 #include <real_time_data.h>
15
16
17 #define CMOS_ADDR_PORT 0x70
18 #define CMOS_DATA_PORT 0x71
19
20 typedef struct {
21 uint8 second;
22 uint8 minute;
23 uint8 hour;
24 uint8 day;
25 uint8 month;
26 uint8 year;
27 uint8 century;
28 } cmos_time;
29
30
31 static uint32
bcd_to_int(uint8 bcd)32 bcd_to_int(uint8 bcd)
33 {
34 uint32 numl;
35 uint32 numh;
36
37 numl = bcd & 0x0f;
38 numh = (bcd & 0xf0) >> 4;
39
40 return numh * 10 + numl;
41 }
42
43
44 static uint8
int_to_bcd(uint32 number)45 int_to_bcd(uint32 number)
46 {
47 uint8 low;
48 uint8 high;
49
50 if (number > 99)
51 return 0;
52
53 high = number / 10;
54 low = number % 10;
55
56 return (high << 4) | low;
57 }
58
59
60 static int
same_time(const cmos_time * time1,const cmos_time * time2)61 same_time(const cmos_time *time1, const cmos_time *time2)
62 {
63 return time1->second == time2->second
64 && time1->minute == time2->minute
65 && time1->hour == time2->hour
66 && time1->day == time2->day
67 && time1->month == time2->month
68 && time1->year == time2->year
69 && time1->century == time2->century;
70 }
71
72
73 static uint8
cmos_read(uint8 addr)74 cmos_read(uint8 addr)
75 {
76 int waitTime = 10000;
77
78 // Wait until bit 7 of Status Register A (indicating whether or not an update is in
79 // progress) is clear if we are reading one of the clock data registers...
80 if (addr < 0x0a) {
81 out8(0x0a, CMOS_ADDR_PORT);
82 while ((in8(CMOS_DATA_PORT) & 0x80) && --waitTime);
83 }
84
85 // then read the value.
86 out8(addr, CMOS_ADDR_PORT);
87 return in8(CMOS_DATA_PORT);
88 }
89
90
91 static void
cmos_write(uint8 addr,uint8 data)92 cmos_write(uint8 addr, uint8 data)
93 {
94 out8(addr, CMOS_ADDR_PORT);
95 out8(data, CMOS_DATA_PORT);
96 }
97
98
99 static void
set_24_hour_mode(void)100 set_24_hour_mode(void)
101 {
102 uint8 status_b;
103
104 status_b = cmos_read(0x0b);
105 status_b |= 0x02;
106 cmos_write(0x0b, status_b);
107 }
108
109
110 static void
read_cmos_clock(cmos_time * cmos)111 read_cmos_clock(cmos_time *cmos)
112 {
113 set_24_hour_mode();
114
115 cmos->century = cmos_read(0x32);
116 cmos->year = cmos_read(0x09);
117 cmos->month = cmos_read(0x08);
118 cmos->day = cmos_read(0x07);
119 cmos->hour = cmos_read(0x04);
120 cmos->minute = cmos_read(0x02);
121 cmos->second = cmos_read(0x00);
122 }
123
124
125 static void
write_cmos_clock(cmos_time * cmos)126 write_cmos_clock(cmos_time *cmos)
127 {
128 set_24_hour_mode();
129
130 cmos_write(0x32, cmos->century);
131 cmos_write(0x09, cmos->year);
132 cmos_write(0x08, cmos->month);
133 cmos_write(0x07, cmos->day);
134 cmos_write(0x04, cmos->hour);
135 cmos_write(0x02, cmos->minute);
136 cmos_write(0x00, cmos->second);
137 }
138
139
140 static uint32
cmos_to_secs(const cmos_time * cmos)141 cmos_to_secs(const cmos_time *cmos)
142 {
143 struct tm t;
144 t.tm_year = bcd_to_int(cmos->century) * 100 + bcd_to_int(cmos->year)
145 - RTC_EPOCH_BASE_YEAR;
146 t.tm_mon = bcd_to_int(cmos->month) - 1;
147 t.tm_mday = bcd_to_int(cmos->day);
148 t.tm_hour = bcd_to_int(cmos->hour);
149 t.tm_min = bcd_to_int(cmos->minute);
150 t.tm_sec = bcd_to_int(cmos->second);
151
152 return rtc_tm_to_secs(&t);
153 }
154
155
156 static void
secs_to_cmos(uint32 seconds,cmos_time * cmos)157 secs_to_cmos(uint32 seconds, cmos_time *cmos)
158 {
159 int wholeYear;
160
161 struct tm t;
162 rtc_secs_to_tm(seconds, &t);
163
164 wholeYear = t.tm_year + RTC_EPOCH_BASE_YEAR;
165
166 cmos->century = int_to_bcd(wholeYear / 100);
167 cmos->year = int_to_bcd(wholeYear % 100);
168 cmos->month = int_to_bcd(t.tm_mon + 1);
169 cmos->day = int_to_bcd(t.tm_mday);
170 cmos->hour = int_to_bcd(t.tm_hour);
171 cmos->minute = int_to_bcd(t.tm_min);
172 cmos->second = int_to_bcd(t.tm_sec);
173 }
174
175
176 // #pragma mark -
177
178
179 status_t
arch_rtc_init(struct kernel_args * args,struct real_time_data * data)180 arch_rtc_init(struct kernel_args *args, struct real_time_data *data)
181 {
182 data->arch_data.system_time_conversion_factor
183 = args->arch_args.system_time_cv_factor;
184 return B_OK;
185 }
186
187
188 uint32
arch_rtc_get_hw_time(void)189 arch_rtc_get_hw_time(void)
190 {
191 int waitTime;
192 cmos_time cmos1;
193 cmos_time cmos2;
194
195 waitTime = 1000;
196
197 // We will read the clock twice and make sure both reads are equal. This will prevent
198 // problems that would occur if the clock is read during an update (e.g. if we read the hour
199 // at 8:59:59, the clock gets changed, and then we read the minute and second, we would
200 // be off by a whole hour)
201 do {
202 read_cmos_clock(&cmos1);
203 read_cmos_clock(&cmos2);
204 } while (!same_time(&cmos1, &cmos2) && --waitTime);
205
206 // Convert the CMOS data to seconds since 1970.
207 return cmos_to_secs(&cmos1);
208 }
209
210
211 void
arch_rtc_set_hw_time(uint32 seconds)212 arch_rtc_set_hw_time(uint32 seconds)
213 {
214 cmos_time cmos;
215
216 secs_to_cmos(seconds, &cmos);
217 write_cmos_clock(&cmos);
218 }
219
220
221 void
arch_rtc_set_system_time_offset(struct real_time_data * data,bigtime_t offset)222 arch_rtc_set_system_time_offset(struct real_time_data *data, bigtime_t offset)
223 {
224 atomic_set64(&data->arch_data.system_time_offset, offset);
225 }
226
227
228 bigtime_t
arch_rtc_get_system_time_offset(struct real_time_data * data)229 arch_rtc_get_system_time_offset(struct real_time_data *data)
230 {
231 return atomic_get64(&data->arch_data.system_time_offset);
232 }
233