1/*
2 * Copyright 2007, François Revol, revol@free.fr.
3 * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>. All rights reserved.
4 * Copyright 2005-2007, Axel Dörfler, axeld@pinc-software.de
5 * Copyright 2003, Jeff Ward, jeff@r2d2.stcloudstate.edu. All rights reserved.
6 *
7 * Distributed under the terms of the MIT License.
8 */
9
10#include <arch/real_time_clock.h>
11
12#include <arch_platform.h>
13#include <boot/kernel_args.h>
14#include <real_time_clock.h>
15#include <real_time_data.h>
16#include <smp.h>
17
18typedef struct	{
19	uint8 second;
20	uint8 minute;
21	uint8 hour;
22	uint8 day;
23	uint8 month;
24	uint8 year;
25	uint8 century;
26} cmos_time;
27
28
29static uint32
30bcd_to_int(uint8 bcd)
31{
32	uint32 numl;
33	uint32 numh;
34
35	numl = bcd & 0x0f;
36	numh = (bcd & 0xf0) >> 4;
37
38	return numh * 10 + numl;
39}
40
41
42static uint8
43int_to_bcd(uint32 number)
44{
45	uint8 low;
46	uint8 high;
47
48	if (number > 99)
49		return 0;
50
51	high = number / 10;
52	low = number % 10;
53
54	return (high << 4) | low;
55}
56
57
58static int
59same_time(const cmos_time *time1, const cmos_time *time2)
60{
61	return time1->second == time2->second
62		&& time1->minute == time2->minute
63		&& time1->hour == time2->hour
64		&& time1->day == time2->day
65		&& time1->month == time2->month
66		&& time1->year == time2->year
67		&& time1->century == time2->century;
68}
69
70
71static uint8
72cmos_read(uint8 addr)
73{
74	return M68KPlatform::Default()->ReadRTCReg(addr);
75}
76
77
78static void
79cmos_write(uint8 addr, uint8 data)
80{
81	M68KPlatform::Default()->WriteRTCReg(addr, data);
82}
83
84
85static void
86set_24_hour_mode(void)
87{
88	uint8 status_b;
89
90	status_b = cmos_read(0x0b);
91	status_b |= 0x02;
92	cmos_write(0x0b, status_b);
93}
94
95
96static void
97read_cmos_clock(cmos_time *cmos)
98{
99	set_24_hour_mode();
100
101	cmos->century = cmos_read(0x32);
102	cmos->year = cmos_read(0x09);
103	cmos->month = cmos_read(0x08);
104	cmos->day = cmos_read(0x07);
105	cmos->hour = cmos_read(0x04);
106	cmos->minute = cmos_read(0x02);
107	cmos->second = cmos_read(0x00);
108}
109
110
111static void
112write_cmos_clock(cmos_time *cmos)
113{
114	set_24_hour_mode();
115
116	cmos_write(0x32, cmos->century);
117	cmos_write(0x09, cmos->year);
118	cmos_write(0x08, cmos->month);
119	cmos_write(0x07, cmos->day);
120	cmos_write(0x04, cmos->hour);
121	cmos_write(0x02, cmos->minute);
122	cmos_write(0x00, cmos->second);
123}
124
125
126static uint32
127cmos_to_secs(const cmos_time *cmos)
128{
129	struct tm t;
130	t.tm_year = bcd_to_int(cmos->century) * 100 + bcd_to_int(cmos->year)
131		- RTC_EPOCH_BASE_YEAR;
132	t.tm_mon = bcd_to_int(cmos->month) - 1;
133	t.tm_mday = bcd_to_int(cmos->day);
134	t.tm_hour = bcd_to_int(cmos->hour);
135	t.tm_min = bcd_to_int(cmos->minute);
136	t.tm_sec = bcd_to_int(cmos->second);
137
138	return rtc_tm_to_secs(&t);
139}
140
141
142static void
143secs_to_cmos(uint32 seconds, cmos_time *cmos)
144{
145	int wholeYear;
146
147	struct tm t;
148	rtc_secs_to_tm(seconds, &t);
149
150	wholeYear = t.tm_year + RTC_EPOCH_BASE_YEAR;
151
152	cmos->century = int_to_bcd(wholeYear / 100);
153	cmos->year = int_to_bcd(wholeYear % 100);
154	cmos->month = int_to_bcd(t.tm_mon + 1);
155	cmos->day = int_to_bcd(t.tm_mday);
156	cmos->hour = int_to_bcd(t.tm_hour);
157	cmos->minute = int_to_bcd(t.tm_min);
158	cmos->second = int_to_bcd(t.tm_sec);
159}
160
161
162//	#pragma mark -
163
164
165
166static spinlock sSetArchDataLock;
167
168status_t
169arch_rtc_init(kernel_args *args, struct real_time_data *data)
170{
171	// init the platform RTC service
172	status_t error = M68KPlatform::Default()->InitRTC(args, data);
173	if (error != B_OK)
174		return error;
175
176	// init the arch specific part of the real_time_data
177	data->arch_data.data[0].system_time_offset = 0;
178	// cvFactor = 2^32 * 1000000 / tbFreq
179	// => (tb * cvFactor) >> 32 = (tb * 2^32 * 1000000 / tbFreq) >> 32
180	//    = tb / tbFreq * 1000000 = time in us
181	data->arch_data.system_time_conversion_factor
182		= uint32((uint64(1) << 32) * 1000000
183			/ args->arch_args.time_base_frequency);
184	data->arch_data.version = 0;
185
186	// init spinlock
187	B_INITIALIZE_SPINLOCK(&sSetArchDataLock);
188
189	// init system_time() conversion factor
190	__m68k_setup_system_time(&data->arch_data.system_time_conversion_factor);
191
192	return B_OK;
193}
194
195
196uint32
197arch_rtc_get_hw_time(void)
198{
199	return M68KPlatform::Default()->GetHardwareRTC();
200}
201
202
203void
204arch_rtc_set_hw_time(uint32 seconds)
205{
206	M68KPlatform::Default()->SetHardwareRTC(seconds);
207}
208
209
210void
211arch_rtc_set_system_time_offset(struct real_time_data *data, bigtime_t offset)
212{
213	cpu_status state = disable_interrupts();
214	acquire_spinlock(&sSetArchDataLock);
215
216	int32 version = data->arch_data.version + 1;
217	data->arch_data.data[version % 2].system_time_offset = offset;
218	data->arch_data.version = version;
219
220	release_spinlock(&sSetArchDataLock);
221	restore_interrupts(state);
222}
223
224
225bigtime_t
226arch_rtc_get_system_time_offset(struct real_time_data *data)
227{
228	int32 version;
229	bigtime_t offset;
230	do {
231		version = data->arch_data.version;
232		offset = data->arch_data.data[version % 2].system_time_offset;
233	} while (version != data->arch_data.version);
234
235	return offset;
236}
237