mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-22 13:37:10 +00:00
hv: add rtcdev
emulate vrtc
Current code would read physical RTC register and return it directly to guest. This patch would read a base physical RTC time and a base physical TSC time at initialize stage. Then when guest tries to read vRTC time, ACRN HV would read the real TSC time and use the TSC offset to calculate the real RTC time. This patch only support BIN data mode and 24 hour mode. BCD data mode and 12 hour mode will add in other patch. The accuracy of clock provided by this patch is limited by TSC, and will be improved in a following patch also. Tracked-On: #7440 Signed-off-by: Yuanyuan Zhao <yuanyuan.zhao@linux.intel.com> Reviewed-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
parent
f8de4178e4
commit
23177d0a1d
@ -1,18 +1,272 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation.
|
||||
* Copyright (c) 2014, Neel Natu (neel@freebsd.org)
|
||||
* Copyright (c) 2022 Intel Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <asm/guest/vm.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/tsc.h>
|
||||
#include <vrtc.h>
|
||||
#include <logmsg.h>
|
||||
|
||||
#include "mc146818rtc.h"
|
||||
|
||||
/* #define DEBUG_RTC */
|
||||
#ifdef DEBUG_RTC
|
||||
# define RTC_DEBUG pr_info
|
||||
#else
|
||||
# define RTC_DEBUG(format, ...) do { } while (false)
|
||||
#endif
|
||||
|
||||
struct clktime {
|
||||
uint32_t year; /* year (4 digit year) */
|
||||
uint32_t mon; /* month (1 - 12) */
|
||||
uint32_t day; /* day (1 - 31) */
|
||||
uint32_t hour; /* hour (0 - 23) */
|
||||
uint32_t min; /* minute (0 - 59) */
|
||||
uint32_t sec; /* second (0 - 59) */
|
||||
uint32_t dow; /* day of week (0 - 6; 0 = Sunday) */
|
||||
};
|
||||
|
||||
#define POSIX_BASE_YEAR 1970
|
||||
#define SECDAY (24 * 60 * 60)
|
||||
#define SECYR (SECDAY * 365)
|
||||
#define VRTC_BROKEN_TIME ((time_t)-1)
|
||||
|
||||
#define FEBRUARY 2U
|
||||
|
||||
static const uint32_t month_days[12] = {
|
||||
31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U
|
||||
};
|
||||
|
||||
/*
|
||||
* This inline avoids some unnecessary modulo operations
|
||||
* as compared with the usual macro:
|
||||
* ( ((year % 4) == 0 &&
|
||||
* (year % 100) != 0) ||
|
||||
* ((year % 400) == 0) )
|
||||
* It is otherwise equivalent.
|
||||
*/
|
||||
static inline uint32_t leapyear(uint32_t year)
|
||||
{
|
||||
uint32_t rv = 0U;
|
||||
|
||||
if ((year & 3U) == 0) {
|
||||
rv = 1U;
|
||||
if ((year % 100U) == 0) {
|
||||
rv = 0U;
|
||||
if ((year % 400U) == 0) {
|
||||
rv = 1U;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static inline uint32_t days_in_year(uint32_t year)
|
||||
{
|
||||
return leapyear(year) ? 366U : 365U;
|
||||
}
|
||||
|
||||
static inline uint32_t days_in_month(uint32_t year, uint32_t month)
|
||||
{
|
||||
return month_days[(month) - 1U] + ((month == FEBRUARY) ? leapyear(year) : 0U);
|
||||
}
|
||||
|
||||
/*
|
||||
* Day of week. Days are counted from 1/1/1970, which was a Thursday.
|
||||
*/
|
||||
static inline uint32_t day_of_week(uint32_t days)
|
||||
{
|
||||
return ((days) + 4U) % 7U;
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate clktime (such as year, month, day) to time_t.
|
||||
*/
|
||||
static int32_t clk_ct_to_ts(struct clktime *ct, time_t *sec)
|
||||
{
|
||||
uint32_t i, year, days;
|
||||
int32_t err = 0;
|
||||
|
||||
year = ct->year;
|
||||
|
||||
/* Sanity checks. */
|
||||
if ((ct->mon < 1U) || (ct->mon > 12U) || (ct->day < 1U) ||
|
||||
(ct->day > days_in_month(year, ct->mon)) ||
|
||||
(ct->hour > 23U) || (ct->min > 59U) || (ct->sec > 59U) ||
|
||||
(year < POSIX_BASE_YEAR) || (year > 2037U)) {
|
||||
/* time_t overflow */
|
||||
err = -EINVAL;
|
||||
} else {
|
||||
/*
|
||||
* Compute days since start of time
|
||||
* First from years, then from months.
|
||||
*/
|
||||
days = 0U;
|
||||
for (i = POSIX_BASE_YEAR; i < year; i++) {
|
||||
days += days_in_year(i);
|
||||
}
|
||||
|
||||
/* Months */
|
||||
for (i = 1; i < ct->mon; i++) {
|
||||
days += days_in_month(year, i);
|
||||
}
|
||||
days += (ct->day - 1);
|
||||
|
||||
*sec = (((time_t)days * 24 + ct->hour) * 60 + ct->min) * 60 + ct->sec;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate time_t to clktime (such as year, month, day)
|
||||
*/
|
||||
static int32_t clk_ts_to_ct(time_t secs, struct clktime *ct)
|
||||
{
|
||||
uint32_t i, year, days;
|
||||
time_t rsec; /* remainder seconds */
|
||||
int32_t err = 0;
|
||||
|
||||
days = secs / SECDAY;
|
||||
rsec = secs % SECDAY;
|
||||
|
||||
ct->dow = day_of_week(days);
|
||||
|
||||
/* Substract out whole years, counting them in i. */
|
||||
for (year = POSIX_BASE_YEAR; days >= days_in_year(year); year++) {
|
||||
days -= days_in_year(year);
|
||||
}
|
||||
ct->year = year;
|
||||
|
||||
/* Substract out whole months, counting them in i. */
|
||||
for (i = 1; days >= days_in_month(year, i); i++) {
|
||||
days -= days_in_month(year, i);
|
||||
}
|
||||
ct->mon = i;
|
||||
|
||||
/* Days are what is left over (+1) from all that. */
|
||||
ct->day = days + 1;
|
||||
|
||||
/* Hours, minutes, seconds are easy */
|
||||
ct->hour = rsec / 3600U;
|
||||
rsec = rsec % 3600U;
|
||||
ct->min = rsec / 60U;
|
||||
rsec = rsec % 60U;
|
||||
ct->sec = rsec;
|
||||
|
||||
/* time_t is defined as int32_t, so year should not be more than 2037. */
|
||||
if ((ct->mon > 12U) || (ct->year > 2037) || (ct->day > days_in_month(ct->year, ct->mon))) {
|
||||
pr_err("Invalid vRTC param mon %d, year %d, day %d\n", ct->mon, ct->year, ct->day);
|
||||
err = -EINVAL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate second value from rtcdev register info which save in vrtc.
|
||||
*/
|
||||
static time_t rtc_to_secs(const struct acrn_vrtc *vrtc)
|
||||
{
|
||||
struct clktime ct;
|
||||
time_t second = VRTC_BROKEN_TIME;
|
||||
const struct rtcdev *rtc= &vrtc->rtcdev;
|
||||
|
||||
do {
|
||||
ct.sec = rtc->sec;
|
||||
ct.min = rtc->min;
|
||||
ct.hour = rtc->hour;
|
||||
ct.day = rtc->day_of_month;
|
||||
ct.mon = rtc->month;
|
||||
|
||||
/*
|
||||
* Ignore 'rtc->dow' because some guests like Linux don't bother
|
||||
* setting it at all while others like OpenBSD/i386 set it incorrectly.
|
||||
*
|
||||
* clock_ct_to_ts() does not depend on 'ct.dow' anyways so ignore it.
|
||||
*/
|
||||
ct.dow = -1;
|
||||
|
||||
ct.year = rtc->century * 100 + rtc->year;
|
||||
if (ct.year < POSIX_BASE_YEAR) {
|
||||
pr_err("Invalid RTC century %x/%d\n", rtc->century,
|
||||
ct.year);
|
||||
break;
|
||||
}
|
||||
|
||||
if (clk_ct_to_ts(&ct, &second) != 0) {
|
||||
pr_err("Invalid RTC clocktime.date %04d-%02d-%02d\n",
|
||||
ct.year, ct.mon, ct.day);
|
||||
pr_err("Invalid RTC clocktime.time %02d:%02d:%02d\n",
|
||||
ct.hour, ct.min, ct.sec);
|
||||
break;
|
||||
}
|
||||
} while (false);
|
||||
|
||||
return second;
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate second value to rtcdev register info and save it in vrtc.
|
||||
*/
|
||||
static void secs_to_rtc(time_t rtctime, struct acrn_vrtc *vrtc)
|
||||
{
|
||||
struct clktime ct;
|
||||
struct rtcdev *rtc;
|
||||
|
||||
if ((rtctime > 0) && (clk_ts_to_ct(rtctime, &ct) == 0)) {
|
||||
rtc = &vrtc->rtcdev;
|
||||
rtc->sec = ct.sec;
|
||||
rtc->min = ct.min;
|
||||
rtc->hour = ct.hour;
|
||||
rtc->day_of_week = ct.dow + 1;
|
||||
rtc->day_of_month = ct.day;
|
||||
rtc->month = ct.mon;
|
||||
rtc->year = ct.year % 100;
|
||||
rtc->century = ct.year / 100;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the base_rtctime is valid, calculate current time by add tsc offset and offset_rtctime.
|
||||
*/
|
||||
static time_t vrtc_get_current_time(struct acrn_vrtc *vrtc)
|
||||
{
|
||||
uint64_t offset;
|
||||
time_t second = VRTC_BROKEN_TIME;
|
||||
|
||||
if (vrtc->base_rtctime > 0) {
|
||||
offset = (cpu_ticks() - vrtc->base_tsc) / (get_tsc_khz() * 1000U);
|
||||
second = vrtc->base_rtctime + (time_t)offset;
|
||||
}
|
||||
return second;
|
||||
}
|
||||
|
||||
#define CMOS_ADDR_PORT 0x70U
|
||||
#define CMOS_DATA_PORT 0x71U
|
||||
|
||||
#define RTC_STATUSA 0x0AU /* status register A */
|
||||
#define RTCSA_TUP 0x80U /* time update, don't look now */
|
||||
|
||||
static spinlock_t cmos_lock = { .head = 0U, .tail = 0U };
|
||||
|
||||
static uint8_t cmos_read(uint8_t addr)
|
||||
@ -23,7 +277,7 @@ static uint8_t cmos_read(uint8_t addr)
|
||||
|
||||
static bool cmos_update_in_progress(void)
|
||||
{
|
||||
return (cmos_read(RTC_STATUSA) & RTCSA_TUP)?1:0;
|
||||
return (cmos_read(RTC_STATUSA) & RTCSA_TUP) ? 1 : 0;
|
||||
}
|
||||
|
||||
static uint8_t cmos_get_reg_val(uint8_t addr)
|
||||
@ -51,18 +305,34 @@ static uint8_t cmos_get_reg_val(uint8_t addr)
|
||||
static bool vrtc_read(struct acrn_vcpu *vcpu, uint16_t addr, __unused size_t width)
|
||||
{
|
||||
uint8_t offset;
|
||||
time_t current;
|
||||
struct acrn_vrtc *vrtc = &vcpu->vm->vrtc;
|
||||
struct acrn_pio_request *pio_req = &vcpu->req.reqs.pio_request;
|
||||
struct acrn_vm *vm = vcpu->vm;
|
||||
bool ret = true;
|
||||
|
||||
offset = vm->vrtc_offset;
|
||||
offset = vm->vrtc.addr;
|
||||
|
||||
if (addr == CMOS_ADDR_PORT) {
|
||||
pio_req->value = vm->vrtc_offset;
|
||||
pio_req->value = offset;
|
||||
} else {
|
||||
pio_req->value = cmos_get_reg_val(offset);
|
||||
if (is_service_vm(vm)) {
|
||||
pio_req->value = cmos_get_reg_val(offset);
|
||||
} else {
|
||||
if (offset <= RTC_CENTURY) {
|
||||
current = vrtc_get_current_time(vrtc);
|
||||
secs_to_rtc(current, vrtc);
|
||||
|
||||
pio_req->value = *((uint8_t *)&vm->vrtc.rtcdev + offset);
|
||||
RTC_DEBUG("read 0x%x, 0x%x", offset, pio_req->value);
|
||||
} else {
|
||||
pr_err("vrtc read invalid addr 0x%x", offset);
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,19 +343,45 @@ static bool vrtc_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width,
|
||||
uint32_t value)
|
||||
{
|
||||
if ((width == 1U) && (addr == CMOS_ADDR_PORT)) {
|
||||
vcpu->vm->vrtc_offset = (uint8_t)value & 0x7FU;
|
||||
vcpu->vm->vrtc.addr = (uint8_t)value & 0x7FU;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void vrtc_set_basetime(struct acrn_vrtc *vrtc)
|
||||
{
|
||||
struct rtcdev *vrtcdev = &vrtc->rtcdev;
|
||||
|
||||
/*
|
||||
* Read base time from physical rtc.
|
||||
*/
|
||||
vrtcdev->sec = cmos_get_reg_val(RTC_SEC);
|
||||
vrtcdev->min = cmos_get_reg_val(RTC_MIN);
|
||||
vrtcdev->hour = cmos_get_reg_val(RTC_HRS);
|
||||
vrtcdev->day_of_month = cmos_get_reg_val(RTC_DAY);
|
||||
vrtcdev->month = cmos_get_reg_val(RTC_MONTH);
|
||||
vrtcdev->year = cmos_get_reg_val(RTC_YEAR);
|
||||
vrtcdev->century = cmos_get_reg_val(RTC_CENTURY);
|
||||
vrtcdev->reg_a = cmos_get_reg_val(RTC_STATUSA) & (~RTCSA_TUP);
|
||||
vrtcdev->reg_b = cmos_get_reg_val(RTC_STATUSB);
|
||||
vrtcdev->reg_c = cmos_get_reg_val(RTC_INTR);
|
||||
vrtcdev->reg_d = cmos_get_reg_val(RTC_STATUSD);
|
||||
|
||||
vrtc->base_rtctime = rtc_to_secs(vrtc);
|
||||
}
|
||||
|
||||
void vrtc_init(struct acrn_vm *vm)
|
||||
{
|
||||
struct vm_io_range range = {
|
||||
.base = CMOS_ADDR_PORT, .len = 2U};
|
||||
|
||||
/* Initializing the CMOS RAM offset to 0U */
|
||||
vm->vrtc_offset = 0U;
|
||||
vm->vrtc.addr = 0U;
|
||||
|
||||
vm->vrtc.vm = vm;
|
||||
register_pio_emulation_handler(vm, RTC_PIO_IDX, &range, vrtc_read, vrtc_write);
|
||||
|
||||
vrtc_set_basetime(&vm->vrtc);
|
||||
vm->vrtc.base_tsc = cpu_ticks();
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <vpic.h>
|
||||
#include <asm/guest/vmx_io.h>
|
||||
#include <vuart.h>
|
||||
#include <vrtc.h>
|
||||
#include <asm/guest/trusty.h>
|
||||
#include <asm/guest/vcpuid.h>
|
||||
#include <vpci.h>
|
||||
@ -169,7 +170,7 @@ struct acrn_vm {
|
||||
uint32_t vcpuid_entry_nr, vcpuid_level, vcpuid_xlevel;
|
||||
struct vcpuid_entry vcpuid_entries[MAX_VM_VCPUID_ENTRIES];
|
||||
struct acrn_vpci vpci;
|
||||
uint8_t vrtc_offset;
|
||||
struct acrn_vrtc vrtc;
|
||||
|
||||
uint64_t intr_inject_delay_delta; /* delay of intr injection */
|
||||
} __aligned(PAGE_SIZE);
|
||||
|
64
hypervisor/include/dm/mc146818rtc.h
Normal file
64
hypervisor/include/dm/mc146818rtc.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*-
|
||||
* Copyright (c) 1990 The Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2022 Intel Corporation.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* William Jolitz.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* from: @(#)rtc.h 7.1 (Berkeley) 5/12/91
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _MC146818_RTC_H_
|
||||
#define _MC146818_RTC_H_
|
||||
|
||||
/*
|
||||
* MC146818 RTC Register locations
|
||||
*/
|
||||
|
||||
#define RTC_SEC 0x00 /* seconds */
|
||||
#define RTC_SECALRM 0x01 /* seconds alarm */
|
||||
#define RTC_MIN 0x02 /* minutes */
|
||||
#define RTC_MINALRM 0x03 /* minutes alarm */
|
||||
#define RTC_HRS 0x04 /* hours */
|
||||
#define RTC_HRSALRM 0x05 /* hours alarm */
|
||||
#define RTC_WDAY 0x06 /* week day */
|
||||
#define RTC_DAY 0x07 /* day of month */
|
||||
#define RTC_MONTH 0x08 /* month of year */
|
||||
#define RTC_YEAR 0x09 /* year in century*/
|
||||
#define RTC_CENTURY 0x32 /* century */
|
||||
|
||||
#define RTC_STATUSA 0x0a /* status register A */
|
||||
#define RTCSA_TUP 0x80U /* time update, don't look now */
|
||||
|
||||
#define RTC_STATUSB 0x0b /* status register B */
|
||||
#define RTC_INTR 0x0c /* status register C (R) interrupt source */
|
||||
#define RTC_STATUSD 0x0d /* status register D (R) Lost Power */
|
||||
|
||||
#endif /* _MC146818_RTC_H_ */
|
42
hypervisor/include/dm/vrtc.h
Normal file
42
hypervisor/include/dm/vrtc.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef VRTC_H
|
||||
#define VRTC_H
|
||||
|
||||
typedef int32_t time_t;
|
||||
|
||||
/* Register layout of the RTC */
|
||||
struct rtcdev {
|
||||
uint8_t sec;
|
||||
uint8_t alarm_sec;
|
||||
uint8_t min;
|
||||
uint8_t alarm_min;
|
||||
uint8_t hour;
|
||||
uint8_t alarm_hour;
|
||||
uint8_t day_of_week;
|
||||
uint8_t day_of_month;
|
||||
uint8_t month;
|
||||
uint8_t year;
|
||||
uint8_t reg_a;
|
||||
uint8_t reg_b;
|
||||
uint8_t reg_c;
|
||||
uint8_t reg_d;
|
||||
uint8_t res[36];
|
||||
uint8_t century;
|
||||
};
|
||||
|
||||
struct acrn_vrtc {
|
||||
struct acrn_vm *vm;
|
||||
uint32_t addr; /* RTC register to read or write */
|
||||
|
||||
time_t base_rtctime; /* Base time calulated from physical rtc register. */
|
||||
uint64_t base_tsc; /* Base tsc value */
|
||||
|
||||
struct rtcdev rtcdev; /* RTC register */
|
||||
};
|
||||
|
||||
#endif /* VRTC_H */
|
Loading…
Reference in New Issue
Block a user