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
20typedef 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
31static uint32
32bcd_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
44static uint8
45int_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
60static int
61same_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
73static uint8
74cmos_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
91static void
92cmos_write(uint8 addr, uint8 data)
93{
94	out8(addr, CMOS_ADDR_PORT);
95	out8(data, CMOS_DATA_PORT);
96}
97
98
99static void
100set_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
110static void
111read_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
125static void
126write_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
140static uint32
141cmos_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
156static void
157secs_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
179status_t
180arch_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
188uint32
189arch_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
211void
212arch_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
221void
222arch_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
228bigtime_t
229arch_rtc_get_system_time_offset(struct real_time_data *data)
230{
231	return atomic_get64(&data->arch_data.system_time_offset);
232}
233