[PATCH v5 4/8] hv: add vRTC reg_b and reg_c support


Zhao, Yuanyuan
 

During setting RTC time, driver will halt RTC update. So support
bit 7 of reg_b. When it set to 0, time will be updated. And
when it's set to 1, rtc will keep the second it was set.

In the process of getting RTC time, driver sets alarm interrupt,
waits 1s, and get alarm interrupt flag. So support alarm interrupt
flag update. If alarm interrupt is enabled (bit 5, reg_b set to 1),
interrupt flag register will be set (bit 7 & bit 5 of reg_c) at
appropriate time.

Signed-off-by: Yuanyuan Zhao <yuanyuan.zhao@...>
---
hypervisor/dm/vrtc.c | 70 +++++++++++++++++++++++++++--
hypervisor/include/dm/mc146818rtc.h | 5 +++
2 files changed, 72 insertions(+), 3 deletions(-)

diff --git a/hypervisor/dm/vrtc.c b/hypervisor/dm/vrtc.c
index 79ee2439d..1d2416df2 100644
--- a/hypervisor/dm/vrtc.c
+++ b/hypervisor/dm/vrtc.c
@@ -422,6 +422,41 @@ static uint8_t cmos_get_reg_val(uint8_t addr)
return reg;
}

+#define TRIGGER_ALARM (RTCIR_ALARM | RTCIR_INT)
+#define RTC_DELTA 1 /* For RTC and system time may out of sync for no more than 1s */
+static inline bool rtc_halted(struct acrn_vrtc *rtc)
+{
+ return ((rtc->rtcdev.reg_b & RTCSB_HALT) != 0U);
+}
+
+static uint8_t vrtc_get_reg_c(struct acrn_vrtc *vrtc)
+{
+ uint8_t ret = vrtc->rtcdev.reg_c;
+ struct rtcdev *rtc = &vrtc->rtcdev;
+ time_t current, alarm;
+
+ if ((rtc->reg_b & RTCSB_AINTR) != 0U) {
+ current = rtc->hour * 3600 + rtc->min * 60 + rtc->sec;
+ alarm = rtc->alarm_hour * 3600 + rtc->alarm_min * 60 + rtc->alarm_sec;
+
+ if (current >= alarm - RTC_DELTA && current <= alarm + RTC_DELTA) {
+ /*
+ * If alarm interrut is enabled and rtc time is in alarm time scale,
+ * set the interrupt flag as alarm triggered.
+ */
+ ret |= TRIGGER_ALARM;
+ }
+ }
+
+ vrtc->rtcdev.reg_c = 0;
+ return ret;
+}
+
+static void vrtc_set_reg_b(struct acrn_vrtc *vrtc, uint8_t newval)
+{
+ vrtc->rtcdev.reg_b = newval;
+}
+
/**
* @pre vcpu != NULL
* @pre vcpu->vm != NULL
@@ -435,7 +470,7 @@ static bool vrtc_read(struct acrn_vcpu *vcpu, uint16_t addr, __unused size_t wid
struct acrn_vm *vm = vcpu->vm;
bool ret = true;

- offset = vm->vrtc.addr;
+ offset = vrtc->addr;

if (addr == CMOS_ADDR_PORT) {
pio_req->value = offset;
@@ -447,7 +482,11 @@ static bool vrtc_read(struct acrn_vcpu *vcpu, uint16_t addr, __unused size_t wid
current = vrtc_get_current_time(vrtc);
secs_to_rtc(current, vrtc);

- pio_req->value = *((uint8_t *)&vm->vrtc.rtcdev + offset);
+ if(offset == 0xCU) {
+ pio_req->value = vrtc_get_reg_c(vrtc);
+ } else {
+ pio_req->value = *((uint8_t *)&vrtc->rtcdev + offset);
+ }
RTC_DEBUG("read 0x%x, 0x%x", offset, pio_req->value);
} else {
RTC_ERR("vrtc read invalid addr 0x%x", offset);
@@ -466,8 +505,33 @@ static bool vrtc_read(struct acrn_vcpu *vcpu, uint16_t addr, __unused size_t wid
static bool vrtc_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width,
uint32_t value)
{
+ struct acrn_vrtc *vrtc = &vcpu->vm->vrtc;
+
if ((width == 1U) && (addr == CMOS_ADDR_PORT)) {
- vcpu->vm->vrtc.addr = (uint8_t)value & 0x7FU;
+ vrtc->addr = (uint8_t)(value & 0x7FU);
+ } else {
+ if (!is_service_vm(vcpu->vm)) {
+ switch (vrtc->addr) {
+ case RTC_STATUSA:
+ case RTC_INTR:
+ case RTC_STATUSD:
+ RTC_DEBUG("RTC reg_%x set to %#x (ignored)\n", vrtc->addr, value);
+ break;
+ case RTC_STATUSB:
+ vrtc_set_reg_b(vrtc, value);
+ RTC_DEBUG("RTC reg_b set to %#x\n", value);
+ break;
+ case RTC_SECALRM:
+ case RTC_MINALRM:
+ /* FALLTHRU */
+ case RTC_HRSALRM:
+ *((uint8_t *)&vrtc->rtcdev + vrtc->addr) = (uint8_t)(value & 0x7FU);
+ RTC_DEBUG("RTC alarm reg(%d) set to %#x (ignored)\n", vrtc->addr, value);
+ break;
+ default:
+ break;
+ }
+ }
}

return true;
diff --git a/hypervisor/include/dm/mc146818rtc.h b/hypervisor/include/dm/mc146818rtc.h
index b23f90d34..1681cc960 100644
--- a/hypervisor/include/dm/mc146818rtc.h
+++ b/hypervisor/include/dm/mc146818rtc.h
@@ -60,8 +60,13 @@
#define RTC_STATUSB 0x0b /* status register B */
#define RTCSB_24HR 0x02 /* 0 = 12 hours, 1 = 24 hours */
#define RTCSB_BCD 0x04 /* 0 = BCD, 1 = Binary coded time */
+#define RTCSB_AINTR 0x20 /* 1 = enable alarm interrupt */
+#define RTCSB_HALT 0x80 /* stop clock updates */

#define RTC_INTR 0x0c /* status register C (R) interrupt source */
+#define RTCIR_ALARM 0x20 /* alarm intr */
+#define RTCIR_INT 0x80 /* interrupt output signal */
+
#define RTC_STATUSD 0x0d /* status register D (R) Lost Power */

#endif /* _MC146818_RTC_H_ */
--
2.25.1


Junjie Mao
 

Yuanyuan Zhao <yuanyuan.zhao@...> writes:

During setting RTC time, driver will halt RTC update. So support
bit 7 of reg_b. When it set to 0, time will be updated. And
when it's set to 1, rtc will keep the second it was set.

In the process of getting RTC time, driver sets alarm interrupt,
waits 1s, and get alarm interrupt flag. So support alarm interrupt
flag update. If alarm interrupt is enabled (bit 5, reg_b set to 1),
interrupt flag register will be set (bit 7 & bit 5 of reg_c) at
appropriate time.

Signed-off-by: Yuanyuan Zhao <yuanyuan.zhao@...>
---
hypervisor/dm/vrtc.c | 70 +++++++++++++++++++++++++++--
hypervisor/include/dm/mc146818rtc.h | 5 +++
2 files changed, 72 insertions(+), 3 deletions(-)

diff --git a/hypervisor/dm/vrtc.c b/hypervisor/dm/vrtc.c
index 79ee2439d..1d2416df2 100644
--- a/hypervisor/dm/vrtc.c
+++ b/hypervisor/dm/vrtc.c
@@ -422,6 +422,41 @@ static uint8_t cmos_get_reg_val(uint8_t addr)
return reg;
}

+#define TRIGGER_ALARM (RTCIR_ALARM | RTCIR_INT)
+#define RTC_DELTA 1 /* For RTC and system time may out of sync for no more than 1s */
+static inline bool rtc_halted(struct acrn_vrtc *rtc)
+{
+ return ((rtc->rtcdev.reg_b & RTCSB_HALT) != 0U);
+}
+
+static uint8_t vrtc_get_reg_c(struct acrn_vrtc *vrtc)
+{
+ uint8_t ret = vrtc->rtcdev.reg_c;
+ struct rtcdev *rtc = &vrtc->rtcdev;
+ time_t current, alarm;
+
+ if ((rtc->reg_b & RTCSB_AINTR) != 0U) {
+ current = rtc->hour * 3600 + rtc->min * 60 + rtc->sec;
+ alarm = rtc->alarm_hour * 3600 + rtc->alarm_min * 60 + rtc->alarm_sec;
+
+ if (current >= alarm - RTC_DELTA && current <= alarm + RTC_DELTA) {
+ /*
+ * If alarm interrut is enabled and rtc time is in alarm time scale,
+ * set the interrupt flag as alarm triggered.
I do not see any change to the comment here. At least highlight that
alarm interrupts are not implemented, but only the alarm flag, and why
that is sufficient for now.

--
Best Regards
Junjie Mao

+ */
+ ret |= TRIGGER_ALARM;
+ }
+ }
+
+ vrtc->rtcdev.reg_c = 0;
+ return ret;
+}
+
+static void vrtc_set_reg_b(struct acrn_vrtc *vrtc, uint8_t newval)
+{
+ vrtc->rtcdev.reg_b = newval;
+}
+
/**
* @pre vcpu != NULL
* @pre vcpu->vm != NULL
@@ -435,7 +470,7 @@ static bool vrtc_read(struct acrn_vcpu *vcpu, uint16_t addr, __unused size_t wid
struct acrn_vm *vm = vcpu->vm;
bool ret = true;

- offset = vm->vrtc.addr;
+ offset = vrtc->addr;

if (addr == CMOS_ADDR_PORT) {
pio_req->value = offset;
@@ -447,7 +482,11 @@ static bool vrtc_read(struct acrn_vcpu *vcpu, uint16_t addr, __unused size_t wid
current = vrtc_get_current_time(vrtc);
secs_to_rtc(current, vrtc);

- pio_req->value = *((uint8_t *)&vm->vrtc.rtcdev + offset);
+ if(offset == 0xCU) {
+ pio_req->value = vrtc_get_reg_c(vrtc);
+ } else {
+ pio_req->value = *((uint8_t *)&vrtc->rtcdev + offset);
+ }
RTC_DEBUG("read 0x%x, 0x%x", offset, pio_req->value);
} else {
RTC_ERR("vrtc read invalid addr 0x%x", offset);
@@ -466,8 +505,33 @@ static bool vrtc_read(struct acrn_vcpu *vcpu, uint16_t addr, __unused size_t wid
static bool vrtc_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width,
uint32_t value)
{
+ struct acrn_vrtc *vrtc = &vcpu->vm->vrtc;
+
if ((width == 1U) && (addr == CMOS_ADDR_PORT)) {
- vcpu->vm->vrtc.addr = (uint8_t)value & 0x7FU;
+ vrtc->addr = (uint8_t)(value & 0x7FU);
+ } else {
+ if (!is_service_vm(vcpu->vm)) {
+ switch (vrtc->addr) {
+ case RTC_STATUSA:
+ case RTC_INTR:
+ case RTC_STATUSD:
+ RTC_DEBUG("RTC reg_%x set to %#x (ignored)\n", vrtc->addr, value);
+ break;
+ case RTC_STATUSB:
+ vrtc_set_reg_b(vrtc, value);
+ RTC_DEBUG("RTC reg_b set to %#x\n", value);
+ break;
+ case RTC_SECALRM:
+ case RTC_MINALRM:
+ /* FALLTHRU */
+ case RTC_HRSALRM:
+ *((uint8_t *)&vrtc->rtcdev + vrtc->addr) = (uint8_t)(value & 0x7FU);
+ RTC_DEBUG("RTC alarm reg(%d) set to %#x (ignored)\n", vrtc->addr, value);
+ break;
+ default:
+ break;
+ }
+ }
}

return true;
diff --git a/hypervisor/include/dm/mc146818rtc.h b/hypervisor/include/dm/mc146818rtc.h
index b23f90d34..1681cc960 100644
--- a/hypervisor/include/dm/mc146818rtc.h
+++ b/hypervisor/include/dm/mc146818rtc.h
@@ -60,8 +60,13 @@
#define RTC_STATUSB 0x0b /* status register B */
#define RTCSB_24HR 0x02 /* 0 = 12 hours, 1 = 24 hours */
#define RTCSB_BCD 0x04 /* 0 = BCD, 1 = Binary coded time */
+#define RTCSB_AINTR 0x20 /* 1 = enable alarm interrupt */
+#define RTCSB_HALT 0x80 /* stop clock updates */

#define RTC_INTR 0x0c /* status register C (R) interrupt source */
+#define RTCIR_ALARM 0x20 /* alarm intr */
+#define RTCIR_INT 0x80 /* interrupt output signal */
+
#define RTC_STATUSD 0x0d /* status register D (R) Lost Power */

#endif /* _MC146818_RTC_H_ */


Zhao, Yuanyuan
 

On 5/7/2022 2:00 PM, Junjie Mao wrote:
Yuanyuan Zhao <yuanyuan.zhao@...> writes:

During setting RTC time, driver will halt RTC update. So support
bit 7 of reg_b. When it set to 0, time will be updated. And
when it's set to 1, rtc will keep the second it was set.

In the process of getting RTC time, driver sets alarm interrupt,
waits 1s, and get alarm interrupt flag. So support alarm interrupt
flag update. If alarm interrupt is enabled (bit 5, reg_b set to 1),
interrupt flag register will be set (bit 7 & bit 5 of reg_c) at
appropriate time.

Signed-off-by: Yuanyuan Zhao <yuanyuan.zhao@...>
---
hypervisor/dm/vrtc.c | 70 +++++++++++++++++++++++++++--
hypervisor/include/dm/mc146818rtc.h | 5 +++
2 files changed, 72 insertions(+), 3 deletions(-)

diff --git a/hypervisor/dm/vrtc.c b/hypervisor/dm/vrtc.c
index 79ee2439d..1d2416df2 100644
--- a/hypervisor/dm/vrtc.c
+++ b/hypervisor/dm/vrtc.c
@@ -422,6 +422,41 @@ static uint8_t cmos_get_reg_val(uint8_t addr)
return reg;
}
+#define TRIGGER_ALARM (RTCIR_ALARM | RTCIR_INT)
+#define RTC_DELTA 1 /* For RTC and system time may out of sync for no more than 1s */
+static inline bool rtc_halted(struct acrn_vrtc *rtc)
+{
+ return ((rtc->rtcdev.reg_b & RTCSB_HALT) != 0U);
+}
+
+static uint8_t vrtc_get_reg_c(struct acrn_vrtc *vrtc)
+{
+ uint8_t ret = vrtc->rtcdev.reg_c;
+ struct rtcdev *rtc = &vrtc->rtcdev;
+ time_t current, alarm;
+
+ if ((rtc->reg_b & RTCSB_AINTR) != 0U) {
+ current = rtc->hour * 3600 + rtc->min * 60 + rtc->sec;
+ alarm = rtc->alarm_hour * 3600 + rtc->alarm_min * 60 + rtc->alarm_sec;
+
+ if (current >= alarm - RTC_DELTA && current <= alarm + RTC_DELTA) {
+ /*
+ * If alarm interrut is enabled and rtc time is in alarm time scale,
+ * set the interrupt flag as alarm triggered.
I do not see any change to the comment here. At least highlight that
alarm interrupts are not implemented, but only the alarm flag, and why
that is sufficient for now.
If alarm interrut is enabled and rtc time is in alarm time scale,
set the interrupt flag. The interrupt is not acturally triggered for driver will read the register.