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 45*8c45f364SAndre Przywara static void cmos_ram_io(struct kvm_cpu *vcpu, u64 addr, u8 *data, 46*8c45f364SAndre Przywara u32 len, u8 is_write, void *ptr) 4764136c1cSPekka Enberg { 4864136c1cSPekka Enberg struct tm *tm; 4964136c1cSPekka Enberg time_t ti; 5064136c1cSPekka Enberg 51*8c45f364SAndre Przywara if (is_write) { 52*8c45f364SAndre Przywara if (addr == 0x70) { /* index register */ 53*8c45f364SAndre Przywara u8 value = ioport__read8(data); 54*8c45f364SAndre Przywara 55*8c45f364SAndre Przywara vcpu->kvm->nmi_disabled = value & (1UL << 7); 56*8c45f364SAndre Przywara rtc.cmos_idx = value & ~(1UL << 7); 57*8c45f364SAndre Przywara 58*8c45f364SAndre Przywara return; 59*8c45f364SAndre Przywara } 60*8c45f364SAndre Przywara 61*8c45f364SAndre Przywara switch (rtc.cmos_idx) { 62*8c45f364SAndre Przywara case RTC_REG_C: 63*8c45f364SAndre Przywara case RTC_REG_D: 64*8c45f364SAndre Przywara /* Read-only */ 65*8c45f364SAndre Przywara break; 66*8c45f364SAndre Przywara default: 67*8c45f364SAndre Przywara rtc.cmos_data[rtc.cmos_idx] = ioport__read8(data); 68*8c45f364SAndre Przywara break; 69*8c45f364SAndre Przywara } 70*8c45f364SAndre Przywara return; 71*8c45f364SAndre Przywara } 72*8c45f364SAndre Przywara 73*8c45f364SAndre Przywara if (addr == 0x70) 74*8c45f364SAndre Przywara return; 75*8c45f364SAndre Przywara 7664136c1cSPekka Enberg time(&ti); 7764136c1cSPekka Enberg 7864136c1cSPekka Enberg tm = gmtime(&ti); 7964136c1cSPekka Enberg 802f062989SPekka Enberg switch (rtc.cmos_idx) { 81c8f6893bSPekka Enberg case RTC_SECONDS: 8264136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_sec)); 8364136c1cSPekka Enberg break; 84c8f6893bSPekka Enberg case RTC_MINUTES: 8564136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_min)); 8664136c1cSPekka Enberg break; 87c8f6893bSPekka Enberg case RTC_HOURS: 8864136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_hour)); 8964136c1cSPekka Enberg break; 90c717392fSPekka Enberg case RTC_DAY_OF_WEEK: 91c717392fSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_wday + 1)); 92c717392fSPekka Enberg break; 93c8f6893bSPekka Enberg case RTC_DAY_OF_MONTH: 9464136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_mday)); 9564136c1cSPekka Enberg break; 96c8f6893bSPekka Enberg case RTC_MONTH: 9764136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_mon + 1)); 9864136c1cSPekka Enberg break; 9997ed4838SPekka Enberg case RTC_YEAR: { 10097ed4838SPekka Enberg int year; 10197ed4838SPekka Enberg 10297ed4838SPekka Enberg year = tm->tm_year + 1900; 10397ed4838SPekka Enberg 10497ed4838SPekka Enberg ioport__write8(data, bin2bcd(year % 100)); 10597ed4838SPekka Enberg 10664136c1cSPekka Enberg break; 10797ed4838SPekka Enberg } 10897ed4838SPekka Enberg case RTC_CENTURY: { 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 11597ed4838SPekka Enberg break; 11697ed4838SPekka Enberg } 117d951bae0SPekka Enberg default: 118d951bae0SPekka Enberg ioport__write8(data, rtc.cmos_data[rtc.cmos_idx]); 119d951bae0SPekka Enberg break; 12064136c1cSPekka Enberg } 12164136c1cSPekka Enberg } 12264136c1cSPekka Enberg 123*8c45f364SAndre Przywara static bool cmos_ram_in(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size) 12464136c1cSPekka Enberg { 125*8c45f364SAndre Przywara cmos_ram_io(vcpu, port, data, size, false, NULL); 12664136c1cSPekka Enberg return true; 12764136c1cSPekka Enberg } 12864136c1cSPekka Enberg 129*8c45f364SAndre Przywara static bool cmos_ram_out(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size) 13064136c1cSPekka Enberg { 131*8c45f364SAndre Przywara cmos_ram_io(vcpu, port, data, size, true, NULL); 13264136c1cSPekka Enberg return true; 13364136c1cSPekka Enberg } 13464136c1cSPekka Enberg 135*8c45f364SAndre Przywara static struct ioport_operations cmos_ram_ioport_ops = { 136*8c45f364SAndre Przywara .io_out = cmos_ram_out, 137*8c45f364SAndre Przywara .io_in = cmos_ram_in, 13864136c1cSPekka Enberg }; 13964136c1cSPekka Enberg 1405a3a5c07SAndre Przywara #ifdef CONFIG_HAS_LIBFDT 1415a3a5c07SAndre Przywara static void generate_rtc_fdt_node(void *fdt, 1425a3a5c07SAndre Przywara struct device_header *dev_hdr, 1435a3a5c07SAndre Przywara void (*generate_irq_prop)(void *fdt, 1445a3a5c07SAndre Przywara u8 irq, 1455a3a5c07SAndre Przywara enum irq_type)) 1465a3a5c07SAndre Przywara { 1475a3a5c07SAndre Przywara u64 reg_prop[2] = { cpu_to_fdt64(0x70), cpu_to_fdt64(2) }; 1485a3a5c07SAndre Przywara 1495a3a5c07SAndre Przywara _FDT(fdt_begin_node(fdt, "rtc")); 1505a3a5c07SAndre Przywara _FDT(fdt_property_string(fdt, "compatible", "motorola,mc146818")); 1515a3a5c07SAndre Przywara _FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop))); 1525a3a5c07SAndre Przywara _FDT(fdt_end_node(fdt)); 1535a3a5c07SAndre Przywara } 1545a3a5c07SAndre Przywara #else 1555a3a5c07SAndre Przywara #define generate_rtc_fdt_node NULL 1565a3a5c07SAndre Przywara #endif 1575a3a5c07SAndre Przywara 1585a3a5c07SAndre Przywara struct device_header rtc_dev_hdr = { 1595a3a5c07SAndre Przywara .bus_type = DEVICE_BUS_IOPORT, 1605a3a5c07SAndre Przywara .data = generate_rtc_fdt_node, 1615a3a5c07SAndre Przywara }; 1625a3a5c07SAndre Przywara 16320c39545SSasha Levin int rtc__init(struct kvm *kvm) 16420c39545SSasha Levin { 1655a3a5c07SAndre Przywara int r; 1665a3a5c07SAndre Przywara 1675a3a5c07SAndre Przywara r = device__register(&rtc_dev_hdr); 1685a3a5c07SAndre Przywara if (r < 0) 1695a3a5c07SAndre Przywara return r; 17020c39545SSasha Levin 17120c39545SSasha Levin /* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */ 172*8c45f364SAndre Przywara r = ioport__register(kvm, 0x0070, &cmos_ram_ioport_ops, 2, NULL); 17320c39545SSasha Levin if (r < 0) 1745a3a5c07SAndre Przywara goto out_device; 17520c39545SSasha Levin 176473c5b29SSami Mujawar /* Set the VRT bit in Register D to indicate valid RAM and time */ 177473c5b29SSami Mujawar rtc.cmos_data[RTC_REG_D] = RTC_REG_D_VRT; 178473c5b29SSami Mujawar 17920c39545SSasha Levin return r; 1805a3a5c07SAndre Przywara 1815a3a5c07SAndre Przywara out_device: 1825a3a5c07SAndre Przywara device__unregister(&rtc_dev_hdr); 1835a3a5c07SAndre Przywara 1845a3a5c07SAndre Przywara return r; 18520c39545SSasha Levin } 18649a8afd1SSasha Levin dev_init(rtc__init); 18720c39545SSasha Levin 18820c39545SSasha Levin int rtc__exit(struct kvm *kvm) 18964136c1cSPekka Enberg { 19064136c1cSPekka Enberg /* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */ 1914346fd8fSSasha Levin ioport__unregister(kvm, 0x0070); 19220c39545SSasha Levin 19320c39545SSasha Levin return 0; 19464136c1cSPekka Enberg } 19549a8afd1SSasha Levin dev_exit(rtc__exit); 196