164136c1cSPekka Enberg #include "kvm/rtc.h" 264136c1cSPekka Enberg 3*070fb918SAlexandru 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 12382eaad7SAndre Przywara #else 13382eaad7SAndre Przywara /* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */ 14382eaad7SAndre Przywara #define RTC_BUS_TYPE DEVICE_BUS_IOPORT 15382eaad7SAndre Przywara #define RTC_BASE_ADDRESS 0x70 16382eaad7SAndre Przywara #endif 17382eaad7SAndre Przywara 18c8f6893bSPekka Enberg /* 19c8f6893bSPekka Enberg * MC146818 RTC registers 20c8f6893bSPekka Enberg */ 21c8f6893bSPekka Enberg #define RTC_SECONDS 0x00 22c8f6893bSPekka Enberg #define RTC_SECONDS_ALARM 0x01 23c8f6893bSPekka Enberg #define RTC_MINUTES 0x02 24c8f6893bSPekka Enberg #define RTC_MINUTES_ALARM 0x03 25c8f6893bSPekka Enberg #define RTC_HOURS 0x04 26c8f6893bSPekka Enberg #define RTC_HOURS_ALARM 0x05 27c8f6893bSPekka Enberg #define RTC_DAY_OF_WEEK 0x06 28c8f6893bSPekka Enberg #define RTC_DAY_OF_MONTH 0x07 29c8f6893bSPekka Enberg #define RTC_MONTH 0x08 30c8f6893bSPekka Enberg #define RTC_YEAR 0x09 3197ed4838SPekka Enberg #define RTC_CENTURY 0x32 32c8f6893bSPekka Enberg 33c8f6893bSPekka Enberg #define RTC_REG_A 0x0A 34c8f6893bSPekka Enberg #define RTC_REG_B 0x0B 35c8f6893bSPekka Enberg #define RTC_REG_C 0x0C 36c8f6893bSPekka Enberg #define RTC_REG_D 0x0D 3764136c1cSPekka Enberg 38473c5b29SSami Mujawar /* 39473c5b29SSami Mujawar * Register D Bits 40473c5b29SSami Mujawar */ 41473c5b29SSami Mujawar #define RTC_REG_D_VRT (1 << 7) 42473c5b29SSami Mujawar 432f062989SPekka Enberg struct rtc_device { 442f062989SPekka Enberg u8 cmos_idx; 45d951bae0SPekka Enberg u8 cmos_data[128]; 462f062989SPekka Enberg }; 472f062989SPekka Enberg 482f062989SPekka Enberg static struct rtc_device rtc; 492f062989SPekka Enberg 5064136c1cSPekka Enberg static inline unsigned char bin2bcd(unsigned val) 5164136c1cSPekka Enberg { 5264136c1cSPekka Enberg return ((val / 10) << 4) + val % 10; 5364136c1cSPekka Enberg } 5464136c1cSPekka Enberg 558c45f364SAndre Przywara static void cmos_ram_io(struct kvm_cpu *vcpu, u64 addr, u8 *data, 568c45f364SAndre Przywara u32 len, u8 is_write, void *ptr) 5764136c1cSPekka Enberg { 5864136c1cSPekka Enberg struct tm *tm; 5964136c1cSPekka Enberg time_t ti; 6064136c1cSPekka Enberg 618c45f364SAndre Przywara if (is_write) { 62382eaad7SAndre Przywara if (addr == RTC_BASE_ADDRESS) { /* index register */ 638c45f364SAndre Przywara u8 value = ioport__read8(data); 648c45f364SAndre Przywara 658c45f364SAndre Przywara vcpu->kvm->nmi_disabled = value & (1UL << 7); 668c45f364SAndre Przywara rtc.cmos_idx = value & ~(1UL << 7); 678c45f364SAndre Przywara 688c45f364SAndre Przywara return; 698c45f364SAndre Przywara } 708c45f364SAndre Przywara 718c45f364SAndre Przywara switch (rtc.cmos_idx) { 728c45f364SAndre Przywara case RTC_REG_C: 738c45f364SAndre Przywara case RTC_REG_D: 748c45f364SAndre Przywara /* Read-only */ 758c45f364SAndre Przywara break; 768c45f364SAndre Przywara default: 778c45f364SAndre Przywara rtc.cmos_data[rtc.cmos_idx] = ioport__read8(data); 788c45f364SAndre Przywara break; 798c45f364SAndre Przywara } 808c45f364SAndre Przywara return; 818c45f364SAndre Przywara } 828c45f364SAndre Przywara 83382eaad7SAndre Przywara if (addr == RTC_BASE_ADDRESS) /* index register is write-only */ 848c45f364SAndre Przywara return; 858c45f364SAndre Przywara 8664136c1cSPekka Enberg time(&ti); 8764136c1cSPekka Enberg 8864136c1cSPekka Enberg tm = gmtime(&ti); 8964136c1cSPekka Enberg 902f062989SPekka Enberg switch (rtc.cmos_idx) { 91c8f6893bSPekka Enberg case RTC_SECONDS: 9264136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_sec)); 9364136c1cSPekka Enberg break; 94c8f6893bSPekka Enberg case RTC_MINUTES: 9564136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_min)); 9664136c1cSPekka Enberg break; 97c8f6893bSPekka Enberg case RTC_HOURS: 9864136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_hour)); 9964136c1cSPekka Enberg break; 100c717392fSPekka Enberg case RTC_DAY_OF_WEEK: 101c717392fSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_wday + 1)); 102c717392fSPekka Enberg break; 103c8f6893bSPekka Enberg case RTC_DAY_OF_MONTH: 10464136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_mday)); 10564136c1cSPekka Enberg break; 106c8f6893bSPekka Enberg case RTC_MONTH: 10764136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_mon + 1)); 10864136c1cSPekka Enberg break; 10997ed4838SPekka Enberg case RTC_YEAR: { 11097ed4838SPekka Enberg int year; 11197ed4838SPekka Enberg 11297ed4838SPekka Enberg year = tm->tm_year + 1900; 11397ed4838SPekka Enberg 11497ed4838SPekka Enberg ioport__write8(data, bin2bcd(year % 100)); 11597ed4838SPekka Enberg 11664136c1cSPekka Enberg break; 11797ed4838SPekka Enberg } 11897ed4838SPekka Enberg case RTC_CENTURY: { 11997ed4838SPekka Enberg int year; 12097ed4838SPekka Enberg 12197ed4838SPekka Enberg year = tm->tm_year + 1900; 12297ed4838SPekka Enberg 12397ed4838SPekka Enberg ioport__write8(data, bin2bcd(year / 100)); 12497ed4838SPekka Enberg 12597ed4838SPekka Enberg break; 12697ed4838SPekka Enberg } 127d951bae0SPekka Enberg default: 128d951bae0SPekka Enberg ioport__write8(data, rtc.cmos_data[rtc.cmos_idx]); 129d951bae0SPekka Enberg break; 13064136c1cSPekka Enberg } 13164136c1cSPekka Enberg } 13264136c1cSPekka Enberg 1335a3a5c07SAndre Przywara #ifdef CONFIG_HAS_LIBFDT 1345a3a5c07SAndre Przywara static void generate_rtc_fdt_node(void *fdt, 1355a3a5c07SAndre Przywara struct device_header *dev_hdr, 1365a3a5c07SAndre Przywara void (*generate_irq_prop)(void *fdt, 1375a3a5c07SAndre Przywara u8 irq, 1385a3a5c07SAndre Przywara enum irq_type)) 1395a3a5c07SAndre Przywara { 140382eaad7SAndre Przywara u64 reg_prop[2] = { cpu_to_fdt64(RTC_BASE_ADDRESS), cpu_to_fdt64(2) }; 1415a3a5c07SAndre Przywara 1425a3a5c07SAndre Przywara _FDT(fdt_begin_node(fdt, "rtc")); 1435a3a5c07SAndre Przywara _FDT(fdt_property_string(fdt, "compatible", "motorola,mc146818")); 1445a3a5c07SAndre Przywara _FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop))); 1455a3a5c07SAndre Przywara _FDT(fdt_end_node(fdt)); 1465a3a5c07SAndre Przywara } 1475a3a5c07SAndre Przywara #else 1485a3a5c07SAndre Przywara #define generate_rtc_fdt_node NULL 1495a3a5c07SAndre Przywara #endif 1505a3a5c07SAndre Przywara 1515a3a5c07SAndre Przywara struct device_header rtc_dev_hdr = { 152382eaad7SAndre Przywara .bus_type = RTC_BUS_TYPE, 1535a3a5c07SAndre Przywara .data = generate_rtc_fdt_node, 1545a3a5c07SAndre Przywara }; 1555a3a5c07SAndre Przywara 15620c39545SSasha Levin int rtc__init(struct kvm *kvm) 15720c39545SSasha Levin { 1585a3a5c07SAndre Przywara int r; 1595a3a5c07SAndre Przywara 1605a3a5c07SAndre Przywara r = device__register(&rtc_dev_hdr); 1615a3a5c07SAndre Przywara if (r < 0) 1625a3a5c07SAndre Przywara return r; 16320c39545SSasha Levin 164382eaad7SAndre Przywara r = kvm__register_iotrap(kvm, RTC_BASE_ADDRESS, 2, cmos_ram_io, NULL, 165382eaad7SAndre Przywara RTC_BUS_TYPE); 16620c39545SSasha Levin if (r < 0) 1675a3a5c07SAndre Przywara goto out_device; 16820c39545SSasha Levin 169473c5b29SSami Mujawar /* Set the VRT bit in Register D to indicate valid RAM and time */ 170473c5b29SSami Mujawar rtc.cmos_data[RTC_REG_D] = RTC_REG_D_VRT; 171473c5b29SSami Mujawar 17220c39545SSasha Levin return r; 1735a3a5c07SAndre Przywara 1745a3a5c07SAndre Przywara out_device: 1755a3a5c07SAndre Przywara device__unregister(&rtc_dev_hdr); 1765a3a5c07SAndre Przywara 1775a3a5c07SAndre Przywara return r; 17820c39545SSasha Levin } 17949a8afd1SSasha Levin dev_init(rtc__init); 18020c39545SSasha Levin 18120c39545SSasha Levin int rtc__exit(struct kvm *kvm) 18264136c1cSPekka Enberg { 183382eaad7SAndre Przywara kvm__deregister_iotrap(kvm, RTC_BASE_ADDRESS, RTC_BUS_TYPE); 18420c39545SSasha Levin 18520c39545SSasha Levin return 0; 18664136c1cSPekka Enberg } 18749a8afd1SSasha Levin dev_exit(rtc__exit); 188