164136c1cSPekka Enberg #include "kvm/rtc.h" 264136c1cSPekka Enberg 364136c1cSPekka Enberg #include "kvm/ioport.h" 464136c1cSPekka Enberg #include "kvm/kvm.h" 564136c1cSPekka Enberg 664136c1cSPekka Enberg #include <time.h> 764136c1cSPekka Enberg 8*382eaad7SAndre Przywara #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) 9*382eaad7SAndre Przywara #define RTC_BUS_TYPE DEVICE_BUS_MMIO 10*382eaad7SAndre Przywara #define RTC_BASE_ADDRESS ARM_RTC_MMIO_BASE 11*382eaad7SAndre Przywara #else 12*382eaad7SAndre Przywara /* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */ 13*382eaad7SAndre Przywara #define RTC_BUS_TYPE DEVICE_BUS_IOPORT 14*382eaad7SAndre Przywara #define RTC_BASE_ADDRESS 0x70 15*382eaad7SAndre Przywara #endif 16*382eaad7SAndre Przywara 17c8f6893bSPekka Enberg /* 18c8f6893bSPekka Enberg * MC146818 RTC registers 19c8f6893bSPekka Enberg */ 20c8f6893bSPekka Enberg #define RTC_SECONDS 0x00 21c8f6893bSPekka Enberg #define RTC_SECONDS_ALARM 0x01 22c8f6893bSPekka Enberg #define RTC_MINUTES 0x02 23c8f6893bSPekka Enberg #define RTC_MINUTES_ALARM 0x03 24c8f6893bSPekka Enberg #define RTC_HOURS 0x04 25c8f6893bSPekka Enberg #define RTC_HOURS_ALARM 0x05 26c8f6893bSPekka Enberg #define RTC_DAY_OF_WEEK 0x06 27c8f6893bSPekka Enberg #define RTC_DAY_OF_MONTH 0x07 28c8f6893bSPekka Enberg #define RTC_MONTH 0x08 29c8f6893bSPekka Enberg #define RTC_YEAR 0x09 3097ed4838SPekka Enberg #define RTC_CENTURY 0x32 31c8f6893bSPekka Enberg 32c8f6893bSPekka Enberg #define RTC_REG_A 0x0A 33c8f6893bSPekka Enberg #define RTC_REG_B 0x0B 34c8f6893bSPekka Enberg #define RTC_REG_C 0x0C 35c8f6893bSPekka Enberg #define RTC_REG_D 0x0D 3664136c1cSPekka Enberg 37473c5b29SSami Mujawar /* 38473c5b29SSami Mujawar * Register D Bits 39473c5b29SSami Mujawar */ 40473c5b29SSami Mujawar #define RTC_REG_D_VRT (1 << 7) 41473c5b29SSami Mujawar 422f062989SPekka Enberg struct rtc_device { 432f062989SPekka Enberg u8 cmos_idx; 44d951bae0SPekka Enberg u8 cmos_data[128]; 452f062989SPekka Enberg }; 462f062989SPekka Enberg 472f062989SPekka Enberg static struct rtc_device rtc; 482f062989SPekka Enberg 4964136c1cSPekka Enberg static inline unsigned char bin2bcd(unsigned val) 5064136c1cSPekka Enberg { 5164136c1cSPekka Enberg return ((val / 10) << 4) + val % 10; 5264136c1cSPekka Enberg } 5364136c1cSPekka Enberg 548c45f364SAndre Przywara static void cmos_ram_io(struct kvm_cpu *vcpu, u64 addr, u8 *data, 558c45f364SAndre Przywara u32 len, u8 is_write, void *ptr) 5664136c1cSPekka Enberg { 5764136c1cSPekka Enberg struct tm *tm; 5864136c1cSPekka Enberg time_t ti; 5964136c1cSPekka Enberg 608c45f364SAndre Przywara if (is_write) { 61*382eaad7SAndre Przywara if (addr == RTC_BASE_ADDRESS) { /* index register */ 628c45f364SAndre Przywara u8 value = ioport__read8(data); 638c45f364SAndre Przywara 648c45f364SAndre Przywara vcpu->kvm->nmi_disabled = value & (1UL << 7); 658c45f364SAndre Przywara rtc.cmos_idx = value & ~(1UL << 7); 668c45f364SAndre Przywara 678c45f364SAndre Przywara return; 688c45f364SAndre Przywara } 698c45f364SAndre Przywara 708c45f364SAndre Przywara switch (rtc.cmos_idx) { 718c45f364SAndre Przywara case RTC_REG_C: 728c45f364SAndre Przywara case RTC_REG_D: 738c45f364SAndre Przywara /* Read-only */ 748c45f364SAndre Przywara break; 758c45f364SAndre Przywara default: 768c45f364SAndre Przywara rtc.cmos_data[rtc.cmos_idx] = ioport__read8(data); 778c45f364SAndre Przywara break; 788c45f364SAndre Przywara } 798c45f364SAndre Przywara return; 808c45f364SAndre Przywara } 818c45f364SAndre Przywara 82*382eaad7SAndre Przywara if (addr == RTC_BASE_ADDRESS) /* index register is write-only */ 838c45f364SAndre Przywara return; 848c45f364SAndre Przywara 8564136c1cSPekka Enberg time(&ti); 8664136c1cSPekka Enberg 8764136c1cSPekka Enberg tm = gmtime(&ti); 8864136c1cSPekka Enberg 892f062989SPekka Enberg switch (rtc.cmos_idx) { 90c8f6893bSPekka Enberg case RTC_SECONDS: 9164136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_sec)); 9264136c1cSPekka Enberg break; 93c8f6893bSPekka Enberg case RTC_MINUTES: 9464136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_min)); 9564136c1cSPekka Enberg break; 96c8f6893bSPekka Enberg case RTC_HOURS: 9764136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_hour)); 9864136c1cSPekka Enberg break; 99c717392fSPekka Enberg case RTC_DAY_OF_WEEK: 100c717392fSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_wday + 1)); 101c717392fSPekka Enberg break; 102c8f6893bSPekka Enberg case RTC_DAY_OF_MONTH: 10364136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_mday)); 10464136c1cSPekka Enberg break; 105c8f6893bSPekka Enberg case RTC_MONTH: 10664136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_mon + 1)); 10764136c1cSPekka Enberg break; 10897ed4838SPekka Enberg case RTC_YEAR: { 10997ed4838SPekka Enberg int year; 11097ed4838SPekka Enberg 11197ed4838SPekka Enberg year = tm->tm_year + 1900; 11297ed4838SPekka Enberg 11397ed4838SPekka Enberg ioport__write8(data, bin2bcd(year % 100)); 11497ed4838SPekka Enberg 11564136c1cSPekka Enberg break; 11697ed4838SPekka Enberg } 11797ed4838SPekka Enberg case RTC_CENTURY: { 11897ed4838SPekka Enberg int year; 11997ed4838SPekka Enberg 12097ed4838SPekka Enberg year = tm->tm_year + 1900; 12197ed4838SPekka Enberg 12297ed4838SPekka Enberg ioport__write8(data, bin2bcd(year / 100)); 12397ed4838SPekka Enberg 12497ed4838SPekka Enberg break; 12597ed4838SPekka Enberg } 126d951bae0SPekka Enberg default: 127d951bae0SPekka Enberg ioport__write8(data, rtc.cmos_data[rtc.cmos_idx]); 128d951bae0SPekka Enberg break; 12964136c1cSPekka Enberg } 13064136c1cSPekka Enberg } 13164136c1cSPekka Enberg 1325a3a5c07SAndre Przywara #ifdef CONFIG_HAS_LIBFDT 1335a3a5c07SAndre Przywara static void generate_rtc_fdt_node(void *fdt, 1345a3a5c07SAndre Przywara struct device_header *dev_hdr, 1355a3a5c07SAndre Przywara void (*generate_irq_prop)(void *fdt, 1365a3a5c07SAndre Przywara u8 irq, 1375a3a5c07SAndre Przywara enum irq_type)) 1385a3a5c07SAndre Przywara { 139*382eaad7SAndre Przywara u64 reg_prop[2] = { cpu_to_fdt64(RTC_BASE_ADDRESS), cpu_to_fdt64(2) }; 1405a3a5c07SAndre Przywara 1415a3a5c07SAndre Przywara _FDT(fdt_begin_node(fdt, "rtc")); 1425a3a5c07SAndre Przywara _FDT(fdt_property_string(fdt, "compatible", "motorola,mc146818")); 1435a3a5c07SAndre Przywara _FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop))); 1445a3a5c07SAndre Przywara _FDT(fdt_end_node(fdt)); 1455a3a5c07SAndre Przywara } 1465a3a5c07SAndre Przywara #else 1475a3a5c07SAndre Przywara #define generate_rtc_fdt_node NULL 1485a3a5c07SAndre Przywara #endif 1495a3a5c07SAndre Przywara 1505a3a5c07SAndre Przywara struct device_header rtc_dev_hdr = { 151*382eaad7SAndre Przywara .bus_type = RTC_BUS_TYPE, 1525a3a5c07SAndre Przywara .data = generate_rtc_fdt_node, 1535a3a5c07SAndre Przywara }; 1545a3a5c07SAndre Przywara 15520c39545SSasha Levin int rtc__init(struct kvm *kvm) 15620c39545SSasha Levin { 1575a3a5c07SAndre Przywara int r; 1585a3a5c07SAndre Przywara 1595a3a5c07SAndre Przywara r = device__register(&rtc_dev_hdr); 1605a3a5c07SAndre Przywara if (r < 0) 1615a3a5c07SAndre Przywara return r; 16220c39545SSasha Levin 163*382eaad7SAndre Przywara r = kvm__register_iotrap(kvm, RTC_BASE_ADDRESS, 2, cmos_ram_io, NULL, 164*382eaad7SAndre Przywara RTC_BUS_TYPE); 16520c39545SSasha Levin if (r < 0) 1665a3a5c07SAndre Przywara goto out_device; 16720c39545SSasha Levin 168473c5b29SSami Mujawar /* Set the VRT bit in Register D to indicate valid RAM and time */ 169473c5b29SSami Mujawar rtc.cmos_data[RTC_REG_D] = RTC_REG_D_VRT; 170473c5b29SSami Mujawar 17120c39545SSasha Levin return r; 1725a3a5c07SAndre Przywara 1735a3a5c07SAndre Przywara out_device: 1745a3a5c07SAndre Przywara device__unregister(&rtc_dev_hdr); 1755a3a5c07SAndre Przywara 1765a3a5c07SAndre Przywara return r; 17720c39545SSasha Levin } 17849a8afd1SSasha Levin dev_init(rtc__init); 17920c39545SSasha Levin 18020c39545SSasha Levin int rtc__exit(struct kvm *kvm) 18164136c1cSPekka Enberg { 182*382eaad7SAndre Przywara kvm__deregister_iotrap(kvm, RTC_BASE_ADDRESS, RTC_BUS_TYPE); 18320c39545SSasha Levin 18420c39545SSasha Levin return 0; 18564136c1cSPekka Enberg } 18649a8afd1SSasha Levin dev_exit(rtc__exit); 187