164136c1cSPekka Enberg #include "kvm/rtc.h"
264136c1cSPekka Enberg
3070fb918SAlexandru Elisei #include "kvm/fdt.h"
464136c1cSPekka Enberg #include "kvm/ioport.h"
564136c1cSPekka Enberg #include "kvm/kvm.h"
664136c1cSPekka Enberg
764136c1cSPekka Enberg #include <time.h>
864136c1cSPekka Enberg
9382eaad7SAndre Przywara #if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
10382eaad7SAndre Przywara #define RTC_BUS_TYPE DEVICE_BUS_MMIO
11382eaad7SAndre Przywara #define RTC_BASE_ADDRESS ARM_RTC_MMIO_BASE
12*2f030d28SRajnesh Kanwal #elif defined(CONFIG_RISCV)
13*2f030d28SRajnesh Kanwal #define RTC_BUS_TYPE DEVICE_BUS_MMIO
14*2f030d28SRajnesh Kanwal #define RTC_BASE_ADDRESS RISCV_RTC_MMIO_BASE
15382eaad7SAndre Przywara #else
16382eaad7SAndre Przywara /* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */
17382eaad7SAndre Przywara #define RTC_BUS_TYPE DEVICE_BUS_IOPORT
18382eaad7SAndre Przywara #define RTC_BASE_ADDRESS 0x70
19382eaad7SAndre Przywara #endif
20382eaad7SAndre Przywara
21c8f6893bSPekka Enberg /*
22c8f6893bSPekka Enberg * MC146818 RTC registers
23c8f6893bSPekka Enberg */
24c8f6893bSPekka Enberg #define RTC_SECONDS 0x00
25c8f6893bSPekka Enberg #define RTC_SECONDS_ALARM 0x01
26c8f6893bSPekka Enberg #define RTC_MINUTES 0x02
27c8f6893bSPekka Enberg #define RTC_MINUTES_ALARM 0x03
28c8f6893bSPekka Enberg #define RTC_HOURS 0x04
29c8f6893bSPekka Enberg #define RTC_HOURS_ALARM 0x05
30c8f6893bSPekka Enberg #define RTC_DAY_OF_WEEK 0x06
31c8f6893bSPekka Enberg #define RTC_DAY_OF_MONTH 0x07
32c8f6893bSPekka Enberg #define RTC_MONTH 0x08
33c8f6893bSPekka Enberg #define RTC_YEAR 0x09
3497ed4838SPekka Enberg #define RTC_CENTURY 0x32
35c8f6893bSPekka Enberg
36c8f6893bSPekka Enberg #define RTC_REG_A 0x0A
37c8f6893bSPekka Enberg #define RTC_REG_B 0x0B
38c8f6893bSPekka Enberg #define RTC_REG_C 0x0C
39c8f6893bSPekka Enberg #define RTC_REG_D 0x0D
4064136c1cSPekka Enberg
41473c5b29SSami Mujawar /*
42473c5b29SSami Mujawar * Register D Bits
43473c5b29SSami Mujawar */
44473c5b29SSami Mujawar #define RTC_REG_D_VRT (1 << 7)
45473c5b29SSami Mujawar
462f062989SPekka Enberg struct rtc_device {
472f062989SPekka Enberg u8 cmos_idx;
48d951bae0SPekka Enberg u8 cmos_data[128];
492f062989SPekka Enberg };
502f062989SPekka Enberg
512f062989SPekka Enberg static struct rtc_device rtc;
522f062989SPekka Enberg
bin2bcd(unsigned val)5364136c1cSPekka Enberg static inline unsigned char bin2bcd(unsigned val)
5464136c1cSPekka Enberg {
5564136c1cSPekka Enberg return ((val / 10) << 4) + val % 10;
5664136c1cSPekka Enberg }
5764136c1cSPekka Enberg
cmos_ram_io(struct kvm_cpu * vcpu,u64 addr,u8 * data,u32 len,u8 is_write,void * ptr)588c45f364SAndre Przywara static void cmos_ram_io(struct kvm_cpu *vcpu, u64 addr, u8 *data,
598c45f364SAndre Przywara u32 len, u8 is_write, void *ptr)
6064136c1cSPekka Enberg {
6164136c1cSPekka Enberg struct tm *tm;
6264136c1cSPekka Enberg time_t ti;
6364136c1cSPekka Enberg
648c45f364SAndre Przywara if (is_write) {
65382eaad7SAndre Przywara if (addr == RTC_BASE_ADDRESS) { /* index register */
668c45f364SAndre Przywara u8 value = ioport__read8(data);
678c45f364SAndre Przywara
688c45f364SAndre Przywara vcpu->kvm->nmi_disabled = value & (1UL << 7);
698c45f364SAndre Przywara rtc.cmos_idx = value & ~(1UL << 7);
708c45f364SAndre Przywara
718c45f364SAndre Przywara return;
728c45f364SAndre Przywara }
738c45f364SAndre Przywara
748c45f364SAndre Przywara switch (rtc.cmos_idx) {
758c45f364SAndre Przywara case RTC_REG_C:
768c45f364SAndre Przywara case RTC_REG_D:
778c45f364SAndre Przywara /* Read-only */
788c45f364SAndre Przywara break;
798c45f364SAndre Przywara default:
808c45f364SAndre Przywara rtc.cmos_data[rtc.cmos_idx] = ioport__read8(data);
818c45f364SAndre Przywara break;
828c45f364SAndre Przywara }
838c45f364SAndre Przywara return;
848c45f364SAndre Przywara }
858c45f364SAndre Przywara
86382eaad7SAndre Przywara if (addr == RTC_BASE_ADDRESS) /* index register is write-only */
878c45f364SAndre Przywara return;
888c45f364SAndre Przywara
8964136c1cSPekka Enberg time(&ti);
9064136c1cSPekka Enberg
9164136c1cSPekka Enberg tm = gmtime(&ti);
9264136c1cSPekka Enberg
932f062989SPekka Enberg switch (rtc.cmos_idx) {
94c8f6893bSPekka Enberg case RTC_SECONDS:
9564136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_sec));
9664136c1cSPekka Enberg break;
97c8f6893bSPekka Enberg case RTC_MINUTES:
9864136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_min));
9964136c1cSPekka Enberg break;
100c8f6893bSPekka Enberg case RTC_HOURS:
10164136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_hour));
10264136c1cSPekka Enberg break;
103c717392fSPekka Enberg case RTC_DAY_OF_WEEK:
104c717392fSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_wday + 1));
105c717392fSPekka Enberg break;
106c8f6893bSPekka Enberg case RTC_DAY_OF_MONTH:
10764136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_mday));
10864136c1cSPekka Enberg break;
109c8f6893bSPekka Enberg case RTC_MONTH:
11064136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_mon + 1));
11164136c1cSPekka Enberg break;
11297ed4838SPekka Enberg case RTC_YEAR: {
11397ed4838SPekka Enberg int year;
11497ed4838SPekka Enberg
11597ed4838SPekka Enberg year = tm->tm_year + 1900;
11697ed4838SPekka Enberg
11797ed4838SPekka Enberg ioport__write8(data, bin2bcd(year % 100));
11897ed4838SPekka Enberg
11964136c1cSPekka Enberg break;
12097ed4838SPekka Enberg }
12197ed4838SPekka Enberg case RTC_CENTURY: {
12297ed4838SPekka Enberg int year;
12397ed4838SPekka Enberg
12497ed4838SPekka Enberg year = tm->tm_year + 1900;
12597ed4838SPekka Enberg
12697ed4838SPekka Enberg ioport__write8(data, bin2bcd(year / 100));
12797ed4838SPekka Enberg
12897ed4838SPekka Enberg break;
12997ed4838SPekka Enberg }
130d951bae0SPekka Enberg default:
131d951bae0SPekka Enberg ioport__write8(data, rtc.cmos_data[rtc.cmos_idx]);
132d951bae0SPekka Enberg break;
13364136c1cSPekka Enberg }
13464136c1cSPekka Enberg }
13564136c1cSPekka Enberg
1365a3a5c07SAndre Przywara #ifdef CONFIG_HAS_LIBFDT
generate_rtc_fdt_node(void * fdt,struct device_header * dev_hdr,void (* generate_irq_prop)(void * fdt,u8 irq,enum irq_type))1375a3a5c07SAndre Przywara static void generate_rtc_fdt_node(void *fdt,
1385a3a5c07SAndre Przywara struct device_header *dev_hdr,
1395a3a5c07SAndre Przywara void (*generate_irq_prop)(void *fdt,
1405a3a5c07SAndre Przywara u8 irq,
1415a3a5c07SAndre Przywara enum irq_type))
1425a3a5c07SAndre Przywara {
143382eaad7SAndre Przywara u64 reg_prop[2] = { cpu_to_fdt64(RTC_BASE_ADDRESS), cpu_to_fdt64(2) };
1445a3a5c07SAndre Przywara
1455a3a5c07SAndre Przywara _FDT(fdt_begin_node(fdt, "rtc"));
1465a3a5c07SAndre Przywara _FDT(fdt_property_string(fdt, "compatible", "motorola,mc146818"));
1475a3a5c07SAndre Przywara _FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop)));
1485a3a5c07SAndre Przywara _FDT(fdt_end_node(fdt));
1495a3a5c07SAndre Przywara }
1505a3a5c07SAndre Przywara #else
1515a3a5c07SAndre Przywara #define generate_rtc_fdt_node NULL
1525a3a5c07SAndre Przywara #endif
1535a3a5c07SAndre Przywara
1545a3a5c07SAndre Przywara struct device_header rtc_dev_hdr = {
155382eaad7SAndre Przywara .bus_type = RTC_BUS_TYPE,
1565a3a5c07SAndre Przywara .data = generate_rtc_fdt_node,
1575a3a5c07SAndre Przywara };
1585a3a5c07SAndre Przywara
rtc__init(struct kvm * kvm)15920c39545SSasha Levin int rtc__init(struct kvm *kvm)
16020c39545SSasha Levin {
1615a3a5c07SAndre Przywara int r;
1625a3a5c07SAndre Przywara
1635a3a5c07SAndre Przywara r = device__register(&rtc_dev_hdr);
1645a3a5c07SAndre Przywara if (r < 0)
1655a3a5c07SAndre Przywara return r;
16620c39545SSasha Levin
167382eaad7SAndre Przywara r = kvm__register_iotrap(kvm, RTC_BASE_ADDRESS, 2, cmos_ram_io, NULL,
168382eaad7SAndre Przywara RTC_BUS_TYPE);
16920c39545SSasha Levin if (r < 0)
1705a3a5c07SAndre Przywara goto out_device;
17120c39545SSasha Levin
172473c5b29SSami Mujawar /* Set the VRT bit in Register D to indicate valid RAM and time */
173473c5b29SSami Mujawar rtc.cmos_data[RTC_REG_D] = RTC_REG_D_VRT;
174473c5b29SSami Mujawar
17520c39545SSasha Levin return r;
1765a3a5c07SAndre Przywara
1775a3a5c07SAndre Przywara out_device:
1785a3a5c07SAndre Przywara device__unregister(&rtc_dev_hdr);
1795a3a5c07SAndre Przywara
1805a3a5c07SAndre Przywara return r;
18120c39545SSasha Levin }
18249a8afd1SSasha Levin dev_init(rtc__init);
18320c39545SSasha Levin
rtc__exit(struct kvm * kvm)18420c39545SSasha Levin int rtc__exit(struct kvm *kvm)
18564136c1cSPekka Enberg {
186382eaad7SAndre Przywara kvm__deregister_iotrap(kvm, RTC_BASE_ADDRESS, RTC_BUS_TYPE);
18720c39545SSasha Levin
18820c39545SSasha Levin return 0;
18964136c1cSPekka Enberg }
19049a8afd1SSasha Levin dev_exit(rtc__exit);
191