@@ -80,6 +80,14 @@ static void rtc_set_time(RTCState *s);
static void rtc_calibrate_time(RTCState *s);
static void rtc_set_cmos(RTCState *s);
+static int32_t divider_reset;
+
+static inline bool rtc_running(RTCState *s)
+{
+ return (!(s->cmos_data[RTC_REG_B] & REG_B_SET) &&
+ (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20);
+}
+
static uint64_t get_guest_rtc_us(RTCState *s)
{
uint64_t guest_rtc;
@@ -221,12 +229,30 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
case RTC_YEAR:
s->cmos_data[s->cmos_index] = data;
/* if in set mode, do not update the time */
- if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ if (rtc_running(s)) {
rtc_set_time(s);
rtc_set_offset(s, 1, 0);
}
break;
case RTC_REG_A:
+ if (((data & 0x60) == 0x60) &&
+ (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20) {
+ rtc_calibrate_time(s);
+ rtc_set_cmos(s);
+ s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+ s->old_guest_usec = get_guest_rtc_us(s);
+ } else if (((s->cmos_data[RTC_REG_A] & 0x60) == 0x60) &&
+ (data & 0x70) <= 0x20) {
+ /* when the divider reset is removed, the first update cycle
+ * begins one-half second later*/
+ divider_reset = 1;
+ if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ rtc_set_time(s);
+ rtc_set_offset(s, 0, 1);
+ s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+ divider_reset = 0;
+ }
+ }
/* UIP bit is read only */
s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
(s->cmos_data[RTC_REG_A] & REG_A_UIP);
@@ -235,7 +261,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
case RTC_REG_B:
if (data & REG_B_SET) {
/* update cmos to when the rtc was stopping */
- if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ if (rtc_running(s)) {
rtc_calibrate_time(s);
rtc_set_cmos(s);
s->old_guest_usec = get_guest_rtc_us(s);
@@ -245,9 +271,16 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
data &= ~REG_B_UIE;
} else {
/* if disabling set mode, update the time */
- if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
+ if ((s->cmos_data[RTC_REG_B] & REG_B_SET) &&
+ (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20) {
rtc_set_time(s);
- rtc_set_offset(s, 0, 0);
+ if (divider_reset == 1) {
+ rtc_set_offset(s, 0, 1);
+ s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+ divider_reset = 0;
+ } else {
+ rtc_set_offset(s, 0, 0);
+ }
}
}
s->cmos_data[RTC_REG_B] = data;
@@ -348,7 +381,8 @@ static int update_in_progress(RTCState *s)
{
int64_t guest_usec;
- if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
+ if ((s->cmos_data[RTC_REG_B] & REG_B_SET) ||
+ ((s->cmos_data[RTC_REG_A] & 0x60) == 0x60)) {
return 0;
}
guest_usec = get_guest_rtc_us(s);
@@ -376,7 +410,7 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
case RTC_YEAR:
/* if not in set mode, calibrate cmos before
* reading*/
- if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ if (rtc_running(s)) {
rtc_calibrate_time(s);
rtc_set_cmos(s);
}
The first update cycle begins one - half seconds later when divider reset is removing. Signed-off-by: Yang Zhang <yang.z.zhang@Intel.com> --- hw/mc146818rtc.c | 46 ++++++++++++++++++++++++++++++++++++++++------ 1 files changed, 40 insertions(+), 6 deletions(-) -- 1.7.1