xref: /haiku/src/system/kernel/arch/m68k/arch_real_time_clock.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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 
18 typedef 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 
29 static uint32
30 bcd_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 
42 static uint8
43 int_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 
58 static int
59 same_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 
71 static uint8
72 cmos_read(uint8 addr)
73 {
74 	return M68KPlatform::Default()->ReadRTCReg(addr);
75 }
76 
77 
78 static void
79 cmos_write(uint8 addr, uint8 data)
80 {
81 	M68KPlatform::Default()->WriteRTCReg(addr, data);
82 }
83 
84 
85 static void
86 set_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 
96 static void
97 read_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 
111 static void
112 write_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 
126 static uint32
127 cmos_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 
142 static void
143 secs_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 
166 static spinlock sSetArchDataLock;
167 
168 status_t
169 arch_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 
196 uint32
197 arch_rtc_get_hw_time(void)
198 {
199 	return M68KPlatform::Default()->GetHardwareRTC();
200 }
201 
202 
203 void
204 arch_rtc_set_hw_time(uint32 seconds)
205 {
206 	M68KPlatform::Default()->SetHardwareRTC(seconds);
207 }
208 
209 
210 void
211 arch_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 
225 bigtime_t
226 arch_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