xref: /haiku/src/system/kernel/arch/x86/arch_real_time_clock.cpp (revision 5ef31662345df968ae978bf2a8ee198483b223cc)
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