1 #include "mc146818a.hpp"
2 #include "mc146818a_def.hpp"
8 /* month is between 0 and 11. */
9 static int get_days_in_month(int month
, int year
)
11 static const int days_tab
[12] = {
12 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
15 if ((unsigned )month
>= 12)
19 if ((year
% 4) == 0 && ((year
% 100) != 0 || (year
% 400) == 0))
25 /* update 'tm' to the next second */
26 static void next_second(struct tm
*tm
)
31 if ((unsigned)tm
->tm_sec
>= 60) {
34 if ((unsigned)tm
->tm_min
>= 60) {
37 if ((unsigned)tm
->tm_hour
>= 24) {
41 if ((unsigned)tm
->tm_wday
>= 7)
43 days_in_month
= get_days_in_month(tm
->tm_mon
,
46 if (tm
->tm_mday
< 1) {
48 } else if (tm
->tm_mday
> days_in_month
) {
51 if (tm
->tm_mon
>= 12) {
61 RTC::RTC(int32_t base_year
, struct tm
&tm
) :
62 base_year(base_year
), current_tm(tm
)
66 memset(this->cmos_data
, 0, sizeof(this->cmos_data
));
68 this->periodic_timer
.set(this, &RTC::on_periodic
);
69 this->second_timer
.set(this, &RTC::on_second
);
70 this->second_timer2
.set(this, &RTC::on_second2
);
72 val
= this->to_bcd((tm
.tm_year
/ 100) + 19);
74 this->cmos_data
[RTC_REG_A
] = 0x26;
75 this->cmos_data
[RTC_REG_B
] = 0x02;
76 this->cmos_data
[RTC_REG_C
] = 0x00;
77 this->cmos_data
[RTC_REG_D
] = 0x80;
78 this->cmos_data
[REG_IBM_CENTURY_BYTE
] = val
;
79 this->cmos_data
[REG_IBM_PS2_CENTURY_BYTE
] = val
;
82 this->next_second_time
= now(SEC
);
83 next_second(&this->current_tm
);
84 this->second_timer2
.update(this->next_second_time
, SEC
);
93 this->cmos_data
[RTC_REG_B
] &= ~(REG_B_PIE
| REG_B_AIE
| REG_B_SQWE
);
94 this->cmos_data
[RTC_REG_C
] &= ~(REG_C_UF
| REG_C_IRQF
| REG_C_PF
| REG_C_AF
);
98 uint8_t RTC::cmos_read(uint8_t index
)
106 case RTC_DAY_OF_WEEK
:
107 case RTC_DAY_OF_MONTH
:
110 ret
= this->cmos_data
[index
];
113 ret
= this->cmos_data
[index
];
116 ret
= this->cmos_data
[index
];
118 this->cmos_data
[RTC_REG_C
] = 0x00;
121 ret
= this->cmos_data
[index
];
128 void RTC::cmos_write(uint8_t index
, uint8_t data
)
131 case RTC_SECONDS_ALARM
:
132 case RTC_MINUTES_ALARM
:
133 case RTC_HOURS_ALARM
:
134 this->cmos_data
[index
] = data
;
139 case RTC_DAY_OF_WEEK
:
140 case RTC_DAY_OF_MONTH
:
143 this->cmos_data
[index
] = data
;
144 /* if in set mode, do not update the time */
145 if (!(this->cmos_data
[RTC_REG_B
] & REG_B_SET
)) {
150 /* UIP bit is read only */
151 this->cmos_data
[RTC_REG_A
] &= ~REG_A_UIP
;
152 this->cmos_data
[RTC_REG_A
] |= (data
& ~REG_A_UIP
);
153 this->update_timer(now(USEC
));
156 if (data
& REG_B_SET
) {
157 /* set mode: reset UIP mode */
158 this->cmos_data
[RTC_REG_A
] &= ~REG_A_UIP
;
161 /* if disabling set mode, update the time */
162 if (this->cmos_data
[RTC_REG_B
] & REG_B_SET
) {
166 this->cmos_data
[RTC_REG_B
] = data
;
167 this->update_timer(now(USEC
));
171 /* cannot write to them */
174 this->cmos_data
[index
] = data
;
179 int RTC::from_bcd(int a
)
181 if (this->cmos_data
[RTC_REG_B
] & REG_B_DM
) {
184 return ((a
>> 4) * 10) + (a
& 0x0f);
188 int RTC::to_bcd(int a
)
190 if (this->cmos_data
[RTC_REG_B
] & REG_B_DM
) {
193 return ((a
/ 10) << 4) | (a
% 10);
197 void RTC::update_timer(int64_t current_time
)
199 int period_code
, period
;
200 int64_t cur_clock
, next_irq_clock
;
202 period_code
= this->cmos_data
[RTC_REG_A
] & 0x0f;
203 if ((period_code
== 0) ||
204 !(this->cmos_data
[RTC_REG_B
] & REG_B_PIE
)) {
205 this->periodic_timer
.cancel();
209 if (period_code
<= 2)
211 /* period in 32 Khz cycles */
212 period
= 1 << (period_code
- 1);
214 /* compute 32 khz clock */
215 cur_clock
= muldiv64(current_time
, 32768, USEC_PER_SEC
);
216 next_irq_clock
= (cur_clock
& ~(period
- 1)) + period
;
217 this->next_periodic_time
=
218 muldiv64(next_irq_clock
, USEC_PER_SEC
, 32768) + 1;
219 this->periodic_timer
.update(this->next_periodic_time
, USEC
);
222 void RTC::set_time(void)
224 struct tm
*tm
= &this->current_tm
;
226 tm
->tm_sec
= this->from_bcd(this->cmos_data
[RTC_SECONDS
]);
227 tm
->tm_min
= this->from_bcd(this->cmos_data
[RTC_MINUTES
]);
228 tm
->tm_hour
= this->from_bcd(this->cmos_data
[RTC_HOURS
] & 0x7f);
229 if (!(this->cmos_data
[RTC_REG_B
] & 0x02) &&
230 (this->cmos_data
[RTC_HOURS
] & 0x80)) {
233 tm
->tm_wday
= this->from_bcd(this->cmos_data
[RTC_DAY_OF_WEEK
]) - 1;
234 tm
->tm_mday
= this->from_bcd(this->cmos_data
[RTC_DAY_OF_MONTH
]);
235 tm
->tm_mon
= this->from_bcd(this->cmos_data
[RTC_MONTH
]) - 1;
236 tm
->tm_year
= this->from_bcd(this->cmos_data
[RTC_YEAR
]) +
237 this->base_year
- 1900;
240 void RTC::copy_date(void)
242 const struct tm
*tm
= &this->current_tm
;
245 this->cmos_data
[RTC_SECONDS
] = this->to_bcd(tm
->tm_sec
);
246 this->cmos_data
[RTC_MINUTES
] = this->to_bcd(tm
->tm_min
);
247 if (this->cmos_data
[RTC_REG_B
] & 0x02) {
249 this->cmos_data
[RTC_HOURS
] = this->to_bcd(tm
->tm_hour
);
252 this->cmos_data
[RTC_HOURS
] = this->to_bcd(tm
->tm_hour
% 12);
253 if (tm
->tm_hour
>= 12)
254 this->cmos_data
[RTC_HOURS
] |= 0x80;
256 this->cmos_data
[RTC_DAY_OF_WEEK
] = this->to_bcd(tm
->tm_wday
+ 1);
257 this->cmos_data
[RTC_DAY_OF_MONTH
] = this->to_bcd(tm
->tm_mday
);
258 this->cmos_data
[RTC_MONTH
] = this->to_bcd(tm
->tm_mon
+ 1);
259 year
= (tm
->tm_year
- this->base_year
) % 100;
262 this->cmos_data
[RTC_YEAR
] = this->to_bcd(year
);
265 void RTC::on_periodic(void)
267 this->update_timer(this->next_periodic_time
);
268 if (this->cmos_data
[RTC_REG_B
] & REG_B_PIE
) {
269 this->cmos_data
[RTC_REG_C
] |= 0xc0;
274 void RTC::on_second(void)
276 /* if the oscillator is not in normal operation, we do not update */
277 if ((this->cmos_data
[RTC_REG_A
] & 0x70) != 0x20) {
278 this->next_second_time
+= 1;
279 this->second_timer
.update(this->next_second_time
, SEC
);
281 uint64_t next_deadline
;
283 next_second(&this->current_tm
);
285 if (!(this->cmos_data
[RTC_REG_B
] & REG_B_SET
)) {
286 /* update in progress bit */
287 this->cmos_data
[RTC_REG_A
] |= REG_A_UIP
;
290 next_deadline
= time_to_ns(this->next_second_time
, SEC
);
291 next_deadline
+= time_to_ns(244, USEC
);
292 this->second_timer2
.update(next_deadline
, NSEC
);
296 void RTC::on_second2(void)
298 if (!(this->cmos_data
[RTC_REG_B
] & REG_B_SET
)) {
303 if (this->cmos_data
[RTC_REG_B
] & REG_B_AIE
) {
304 if (((this->cmos_data
[RTC_SECONDS_ALARM
] & 0xc0) == 0xc0 ||
305 this->from_bcd(this->cmos_data
[RTC_SECONDS_ALARM
]) == this->current_tm
.tm_sec
) &&
306 ((this->cmos_data
[RTC_MINUTES_ALARM
] & 0xc0) == 0xc0 ||
307 this->from_bcd(this->cmos_data
[RTC_MINUTES_ALARM
]) == this->current_tm
.tm_min
) &&
308 ((this->cmos_data
[RTC_HOURS_ALARM
] & 0xc0) == 0xc0 ||
309 this->from_bcd(this->cmos_data
[RTC_HOURS_ALARM
]) == this->current_tm
.tm_hour
)) {
311 this->cmos_data
[RTC_REG_C
] |= 0xa0;
316 /* update ended interrupt */
317 this->cmos_data
[RTC_REG_C
] |= REG_C_UF
;
318 if (this->cmos_data
[RTC_REG_B
] & REG_B_UIE
) {
319 this->cmos_data
[RTC_REG_C
] |= REG_C_IRQF
;
323 /* clear update in progress bit */
324 this->cmos_data
[RTC_REG_A
] &= ~REG_A_UIP
;
326 this->next_second_time
+= 1;
327 this->second_timer
.update(this->next_second_time
, SEC
);
330 void RTC::marshal(Marshaller
*m
, const char *name
)
332 m
->start_struct(name
, "RTC");
333 ::marshal(m
, "irq", &this->irq
);
334 ::marshal(m
, "base_year", &this->base_year
);
335 ::marshal_array(m
, "cmos_data", this->cmos_data
, 128);
336 ::marshal(m
, "current_tm", &this->current_tm
);
337 ::marshal(m
, "next_periodic_time", &this->next_periodic_time
);
338 ::marshal(m
, "next_second_time", &this->next_periodic_time
);
339 ::marshal(m
, "periodic_timer", &this->periodic_timer
);
340 ::marshal(m
, "second_timer", &this->second_timer
);
341 ::marshal(m
, "second_timer2", &this->second_timer2
);