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 21*97ed4838SPekka 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 282f062989SPekka Enberg struct rtc_device { 292f062989SPekka Enberg u8 cmos_idx; 30d951bae0SPekka Enberg u8 cmos_data[128]; 312f062989SPekka Enberg }; 322f062989SPekka Enberg 332f062989SPekka Enberg static struct rtc_device rtc; 342f062989SPekka Enberg 3564136c1cSPekka Enberg static inline unsigned char bin2bcd(unsigned val) 3664136c1cSPekka Enberg { 3764136c1cSPekka Enberg return ((val / 10) << 4) + val % 10; 3864136c1cSPekka Enberg } 3964136c1cSPekka Enberg 40c9f6a037SXiao Guangrong static bool cmos_ram_data_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size) 4164136c1cSPekka Enberg { 4264136c1cSPekka Enberg struct tm *tm; 4364136c1cSPekka Enberg time_t ti; 4464136c1cSPekka Enberg 4564136c1cSPekka Enberg time(&ti); 4664136c1cSPekka Enberg 4764136c1cSPekka Enberg tm = gmtime(&ti); 4864136c1cSPekka Enberg 492f062989SPekka Enberg switch (rtc.cmos_idx) { 50c8f6893bSPekka Enberg case RTC_SECONDS: 5164136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_sec)); 5264136c1cSPekka Enberg break; 53c8f6893bSPekka Enberg case RTC_MINUTES: 5464136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_min)); 5564136c1cSPekka Enberg break; 56c8f6893bSPekka Enberg case RTC_HOURS: 5764136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_hour)); 5864136c1cSPekka Enberg break; 59c717392fSPekka Enberg case RTC_DAY_OF_WEEK: 60c717392fSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_wday + 1)); 61c717392fSPekka Enberg break; 62c8f6893bSPekka Enberg case RTC_DAY_OF_MONTH: 6364136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_mday)); 6464136c1cSPekka Enberg break; 65c8f6893bSPekka Enberg case RTC_MONTH: 6664136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_mon + 1)); 6764136c1cSPekka Enberg break; 68*97ed4838SPekka Enberg case RTC_YEAR: { 69*97ed4838SPekka Enberg int year; 70*97ed4838SPekka Enberg 71*97ed4838SPekka Enberg year = tm->tm_year + 1900; 72*97ed4838SPekka Enberg 73*97ed4838SPekka Enberg ioport__write8(data, bin2bcd(year % 100)); 74*97ed4838SPekka Enberg 7564136c1cSPekka Enberg break; 76*97ed4838SPekka Enberg } 77*97ed4838SPekka Enberg case RTC_CENTURY: { 78*97ed4838SPekka Enberg int year; 79*97ed4838SPekka Enberg 80*97ed4838SPekka Enberg year = tm->tm_year + 1900; 81*97ed4838SPekka Enberg 82*97ed4838SPekka Enberg ioport__write8(data, bin2bcd(year / 100)); 83*97ed4838SPekka Enberg 84*97ed4838SPekka Enberg break; 85*97ed4838SPekka Enberg } 86d951bae0SPekka Enberg default: 87d951bae0SPekka Enberg ioport__write8(data, rtc.cmos_data[rtc.cmos_idx]); 88d951bae0SPekka Enberg break; 8964136c1cSPekka Enberg } 9064136c1cSPekka Enberg 9164136c1cSPekka Enberg return true; 9264136c1cSPekka Enberg } 9364136c1cSPekka Enberg 94c9f6a037SXiao Guangrong static bool cmos_ram_data_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size) 9564136c1cSPekka Enberg { 96d951bae0SPekka Enberg switch (rtc.cmos_idx) { 97d951bae0SPekka Enberg case RTC_REG_C: 98d951bae0SPekka Enberg case RTC_REG_D: 99d951bae0SPekka Enberg /* Read-only */ 100d951bae0SPekka Enberg break; 101d951bae0SPekka Enberg default: 102d951bae0SPekka Enberg rtc.cmos_data[rtc.cmos_idx] = ioport__read8(data); 103d951bae0SPekka Enberg break; 104d951bae0SPekka Enberg } 105d951bae0SPekka Enberg 10664136c1cSPekka Enberg return true; 10764136c1cSPekka Enberg } 10864136c1cSPekka Enberg 10964136c1cSPekka Enberg static struct ioport_operations cmos_ram_data_ioport_ops = { 11064136c1cSPekka Enberg .io_out = cmos_ram_data_out, 11164136c1cSPekka Enberg .io_in = cmos_ram_data_in, 11264136c1cSPekka Enberg }; 11364136c1cSPekka Enberg 114c9f6a037SXiao Guangrong static bool cmos_ram_index_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size) 11564136c1cSPekka Enberg { 1162f062989SPekka Enberg u8 value = ioport__read8(data); 11764136c1cSPekka Enberg 11843835ac9SSasha Levin kvm->nmi_disabled = value & (1UL << 7); 1192f062989SPekka Enberg rtc.cmos_idx = value & ~(1UL << 7); 12064136c1cSPekka Enberg 12164136c1cSPekka Enberg return true; 12264136c1cSPekka Enberg } 12364136c1cSPekka Enberg 12464136c1cSPekka Enberg static struct ioport_operations cmos_ram_index_ioport_ops = { 12564136c1cSPekka Enberg .io_out = cmos_ram_index_out, 12664136c1cSPekka Enberg }; 12764136c1cSPekka Enberg 12820c39545SSasha Levin int rtc__init(struct kvm *kvm) 12920c39545SSasha Levin { 13020c39545SSasha Levin int r = 0; 13120c39545SSasha Levin 13220c39545SSasha Levin /* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */ 1334346fd8fSSasha Levin r = ioport__register(kvm, 0x0070, &cmos_ram_index_ioport_ops, 1, NULL); 13420c39545SSasha Levin if (r < 0) 13520c39545SSasha Levin return r; 13620c39545SSasha Levin 1374346fd8fSSasha Levin r = ioport__register(kvm, 0x0071, &cmos_ram_data_ioport_ops, 1, NULL); 13820c39545SSasha Levin if (r < 0) { 1394346fd8fSSasha Levin ioport__unregister(kvm, 0x0071); 14020c39545SSasha Levin return r; 14120c39545SSasha Levin } 14220c39545SSasha Levin 14320c39545SSasha Levin return r; 14420c39545SSasha Levin } 14549a8afd1SSasha Levin dev_init(rtc__init); 14620c39545SSasha Levin 14720c39545SSasha Levin int rtc__exit(struct kvm *kvm) 14864136c1cSPekka Enberg { 14964136c1cSPekka Enberg /* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */ 1504346fd8fSSasha Levin ioport__unregister(kvm, 0x0070); 1514346fd8fSSasha Levin ioport__unregister(kvm, 0x0071); 15220c39545SSasha Levin 15320c39545SSasha Levin return 0; 15464136c1cSPekka Enberg } 15549a8afd1SSasha Levin dev_exit(rtc__exit); 156