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