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 8c8f6893bSPekka Enberg /* 9c8f6893bSPekka Enberg * MC146818 RTC registers 10c8f6893bSPekka Enberg */ 11c8f6893bSPekka Enberg #define RTC_SECONDS 0x00 12c8f6893bSPekka Enberg #define RTC_SECONDS_ALARM 0x01 13c8f6893bSPekka Enberg #define RTC_MINUTES 0x02 14c8f6893bSPekka Enberg #define RTC_MINUTES_ALARM 0x03 15c8f6893bSPekka Enberg #define RTC_HOURS 0x04 16c8f6893bSPekka Enberg #define RTC_HOURS_ALARM 0x05 17c8f6893bSPekka Enberg #define RTC_DAY_OF_WEEK 0x06 18c8f6893bSPekka Enberg #define RTC_DAY_OF_MONTH 0x07 19c8f6893bSPekka Enberg #define RTC_MONTH 0x08 20c8f6893bSPekka Enberg #define RTC_YEAR 0x09 2197ed4838SPekka Enberg #define RTC_CENTURY 0x32 22c8f6893bSPekka Enberg 23c8f6893bSPekka Enberg #define RTC_REG_A 0x0A 24c8f6893bSPekka Enberg #define RTC_REG_B 0x0B 25c8f6893bSPekka Enberg #define RTC_REG_C 0x0C 26c8f6893bSPekka Enberg #define RTC_REG_D 0x0D 2764136c1cSPekka Enberg 28473c5b29SSami Mujawar /* 29473c5b29SSami Mujawar * Register D Bits 30473c5b29SSami Mujawar */ 31473c5b29SSami Mujawar #define RTC_REG_D_VRT (1 << 7) 32473c5b29SSami Mujawar 332f062989SPekka Enberg struct rtc_device { 342f062989SPekka Enberg u8 cmos_idx; 35d951bae0SPekka Enberg u8 cmos_data[128]; 362f062989SPekka Enberg }; 372f062989SPekka Enberg 382f062989SPekka Enberg static struct rtc_device rtc; 392f062989SPekka Enberg 4064136c1cSPekka Enberg static inline unsigned char bin2bcd(unsigned val) 4164136c1cSPekka Enberg { 4264136c1cSPekka Enberg return ((val / 10) << 4) + val % 10; 4364136c1cSPekka Enberg } 4464136c1cSPekka Enberg 454123ca55SMarc Zyngier static bool cmos_ram_data_in(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size) 4664136c1cSPekka Enberg { 4764136c1cSPekka Enberg struct tm *tm; 4864136c1cSPekka Enberg time_t ti; 4964136c1cSPekka Enberg 5064136c1cSPekka Enberg time(&ti); 5164136c1cSPekka Enberg 5264136c1cSPekka Enberg tm = gmtime(&ti); 5364136c1cSPekka Enberg 542f062989SPekka Enberg switch (rtc.cmos_idx) { 55c8f6893bSPekka Enberg case RTC_SECONDS: 5664136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_sec)); 5764136c1cSPekka Enberg break; 58c8f6893bSPekka Enberg case RTC_MINUTES: 5964136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_min)); 6064136c1cSPekka Enberg break; 61c8f6893bSPekka Enberg case RTC_HOURS: 6264136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_hour)); 6364136c1cSPekka Enberg break; 64c717392fSPekka Enberg case RTC_DAY_OF_WEEK: 65c717392fSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_wday + 1)); 66c717392fSPekka Enberg break; 67c8f6893bSPekka Enberg case RTC_DAY_OF_MONTH: 6864136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_mday)); 6964136c1cSPekka Enberg break; 70c8f6893bSPekka Enberg case RTC_MONTH: 7164136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_mon + 1)); 7264136c1cSPekka Enberg break; 7397ed4838SPekka Enberg case RTC_YEAR: { 7497ed4838SPekka Enberg int year; 7597ed4838SPekka Enberg 7697ed4838SPekka Enberg year = tm->tm_year + 1900; 7797ed4838SPekka Enberg 7897ed4838SPekka Enberg ioport__write8(data, bin2bcd(year % 100)); 7997ed4838SPekka Enberg 8064136c1cSPekka Enberg break; 8197ed4838SPekka Enberg } 8297ed4838SPekka Enberg case RTC_CENTURY: { 8397ed4838SPekka Enberg int year; 8497ed4838SPekka Enberg 8597ed4838SPekka Enberg year = tm->tm_year + 1900; 8697ed4838SPekka Enberg 8797ed4838SPekka Enberg ioport__write8(data, bin2bcd(year / 100)); 8897ed4838SPekka Enberg 8997ed4838SPekka Enberg break; 9097ed4838SPekka Enberg } 91d951bae0SPekka Enberg default: 92d951bae0SPekka Enberg ioport__write8(data, rtc.cmos_data[rtc.cmos_idx]); 93d951bae0SPekka Enberg break; 9464136c1cSPekka Enberg } 9564136c1cSPekka Enberg 9664136c1cSPekka Enberg return true; 9764136c1cSPekka Enberg } 9864136c1cSPekka Enberg 994123ca55SMarc Zyngier static bool cmos_ram_data_out(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size) 10064136c1cSPekka Enberg { 101d951bae0SPekka Enberg switch (rtc.cmos_idx) { 102d951bae0SPekka Enberg case RTC_REG_C: 103d951bae0SPekka Enberg case RTC_REG_D: 104d951bae0SPekka Enberg /* Read-only */ 105d951bae0SPekka Enberg break; 106d951bae0SPekka Enberg default: 107d951bae0SPekka Enberg rtc.cmos_data[rtc.cmos_idx] = ioport__read8(data); 108d951bae0SPekka Enberg break; 109d951bae0SPekka Enberg } 110d951bae0SPekka Enberg 11164136c1cSPekka Enberg return true; 11264136c1cSPekka Enberg } 11364136c1cSPekka Enberg 11464136c1cSPekka Enberg static struct ioport_operations cmos_ram_data_ioport_ops = { 11564136c1cSPekka Enberg .io_out = cmos_ram_data_out, 11664136c1cSPekka Enberg .io_in = cmos_ram_data_in, 11764136c1cSPekka Enberg }; 11864136c1cSPekka Enberg 1194123ca55SMarc Zyngier static bool cmos_ram_index_out(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size) 12064136c1cSPekka Enberg { 1212f062989SPekka Enberg u8 value = ioport__read8(data); 12264136c1cSPekka Enberg 1234123ca55SMarc Zyngier vcpu->kvm->nmi_disabled = value & (1UL << 7); 1242f062989SPekka Enberg rtc.cmos_idx = value & ~(1UL << 7); 12564136c1cSPekka Enberg 12664136c1cSPekka Enberg return true; 12764136c1cSPekka Enberg } 12864136c1cSPekka Enberg 12964136c1cSPekka Enberg static struct ioport_operations cmos_ram_index_ioport_ops = { 13064136c1cSPekka Enberg .io_out = cmos_ram_index_out, 13164136c1cSPekka Enberg }; 13264136c1cSPekka Enberg 133*5a3a5c07SAndre Przywara #ifdef CONFIG_HAS_LIBFDT 134*5a3a5c07SAndre Przywara static void generate_rtc_fdt_node(void *fdt, 135*5a3a5c07SAndre Przywara struct device_header *dev_hdr, 136*5a3a5c07SAndre Przywara void (*generate_irq_prop)(void *fdt, 137*5a3a5c07SAndre Przywara u8 irq, 138*5a3a5c07SAndre Przywara enum irq_type)) 139*5a3a5c07SAndre Przywara { 140*5a3a5c07SAndre Przywara u64 reg_prop[2] = { cpu_to_fdt64(0x70), cpu_to_fdt64(2) }; 141*5a3a5c07SAndre Przywara 142*5a3a5c07SAndre Przywara _FDT(fdt_begin_node(fdt, "rtc")); 143*5a3a5c07SAndre Przywara _FDT(fdt_property_string(fdt, "compatible", "motorola,mc146818")); 144*5a3a5c07SAndre Przywara _FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop))); 145*5a3a5c07SAndre Przywara _FDT(fdt_end_node(fdt)); 146*5a3a5c07SAndre Przywara } 147*5a3a5c07SAndre Przywara #else 148*5a3a5c07SAndre Przywara #define generate_rtc_fdt_node NULL 149*5a3a5c07SAndre Przywara #endif 150*5a3a5c07SAndre Przywara 151*5a3a5c07SAndre Przywara struct device_header rtc_dev_hdr = { 152*5a3a5c07SAndre Przywara .bus_type = DEVICE_BUS_IOPORT, 153*5a3a5c07SAndre Przywara .data = generate_rtc_fdt_node, 154*5a3a5c07SAndre Przywara }; 155*5a3a5c07SAndre Przywara 15620c39545SSasha Levin int rtc__init(struct kvm *kvm) 15720c39545SSasha Levin { 158*5a3a5c07SAndre Przywara int r; 159*5a3a5c07SAndre Przywara 160*5a3a5c07SAndre Przywara r = device__register(&rtc_dev_hdr); 161*5a3a5c07SAndre Przywara if (r < 0) 162*5a3a5c07SAndre Przywara return r; 16320c39545SSasha Levin 16420c39545SSasha Levin /* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */ 1654346fd8fSSasha Levin r = ioport__register(kvm, 0x0070, &cmos_ram_index_ioport_ops, 1, NULL); 16620c39545SSasha Levin if (r < 0) 167*5a3a5c07SAndre Przywara goto out_device; 16820c39545SSasha Levin 1694346fd8fSSasha Levin r = ioport__register(kvm, 0x0071, &cmos_ram_data_ioport_ops, 1, NULL); 170*5a3a5c07SAndre Przywara if (r < 0) 171*5a3a5c07SAndre Przywara goto out_ioport; 17220c39545SSasha Levin 173473c5b29SSami Mujawar /* Set the VRT bit in Register D to indicate valid RAM and time */ 174473c5b29SSami Mujawar rtc.cmos_data[RTC_REG_D] = RTC_REG_D_VRT; 175473c5b29SSami Mujawar 17620c39545SSasha Levin return r; 177*5a3a5c07SAndre Przywara 178*5a3a5c07SAndre Przywara out_ioport: 179*5a3a5c07SAndre Przywara ioport__unregister(kvm, 0x0070); 180*5a3a5c07SAndre Przywara out_device: 181*5a3a5c07SAndre Przywara device__unregister(&rtc_dev_hdr); 182*5a3a5c07SAndre Przywara 183*5a3a5c07SAndre Przywara return r; 18420c39545SSasha Levin } 18549a8afd1SSasha Levin dev_init(rtc__init); 18620c39545SSasha Levin 18720c39545SSasha Levin int rtc__exit(struct kvm *kvm) 18864136c1cSPekka Enberg { 18964136c1cSPekka Enberg /* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */ 1904346fd8fSSasha Levin ioport__unregister(kvm, 0x0070); 1914346fd8fSSasha Levin ioport__unregister(kvm, 0x0071); 19220c39545SSasha Levin 19320c39545SSasha Levin return 0; 19464136c1cSPekka Enberg } 19549a8afd1SSasha Levin dev_exit(rtc__exit); 196