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 21c8f6893bSPekka Enberg 22c8f6893bSPekka Enberg #define RTC_REG_A 0x0A 23c8f6893bSPekka Enberg #define RTC_REG_B 0x0B 24c8f6893bSPekka Enberg #define RTC_REG_C 0x0C 25c8f6893bSPekka Enberg #define RTC_REG_D 0x0D 2664136c1cSPekka Enberg 272f062989SPekka Enberg struct rtc_device { 282f062989SPekka Enberg u8 cmos_idx; 29*d951bae0SPekka Enberg u8 cmos_data[128]; 302f062989SPekka Enberg }; 312f062989SPekka Enberg 322f062989SPekka Enberg static struct rtc_device rtc; 332f062989SPekka Enberg 3464136c1cSPekka Enberg static inline unsigned char bin2bcd(unsigned val) 3564136c1cSPekka Enberg { 3664136c1cSPekka Enberg return ((val / 10) << 4) + val % 10; 3764136c1cSPekka Enberg } 3864136c1cSPekka Enberg 39c9f6a037SXiao Guangrong static bool cmos_ram_data_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size) 4064136c1cSPekka Enberg { 4164136c1cSPekka Enberg struct tm *tm; 4264136c1cSPekka Enberg time_t ti; 4364136c1cSPekka Enberg 4464136c1cSPekka Enberg time(&ti); 4564136c1cSPekka Enberg 4664136c1cSPekka Enberg tm = gmtime(&ti); 4764136c1cSPekka Enberg 482f062989SPekka Enberg switch (rtc.cmos_idx) { 49c8f6893bSPekka Enberg case RTC_SECONDS: 5064136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_sec)); 5164136c1cSPekka Enberg break; 52c8f6893bSPekka Enberg case RTC_MINUTES: 5364136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_min)); 5464136c1cSPekka Enberg break; 55c8f6893bSPekka Enberg case RTC_HOURS: 5664136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_hour)); 5764136c1cSPekka Enberg break; 58c717392fSPekka Enberg case RTC_DAY_OF_WEEK: 59c717392fSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_wday + 1)); 60c717392fSPekka Enberg break; 61c8f6893bSPekka Enberg case RTC_DAY_OF_MONTH: 6264136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_mday)); 6364136c1cSPekka Enberg break; 64c8f6893bSPekka Enberg case RTC_MONTH: 6564136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_mon + 1)); 6664136c1cSPekka Enberg break; 67c8f6893bSPekka Enberg case RTC_YEAR: 6864136c1cSPekka Enberg ioport__write8(data, bin2bcd(tm->tm_year)); 6964136c1cSPekka Enberg break; 70*d951bae0SPekka Enberg default: 71*d951bae0SPekka Enberg ioport__write8(data, rtc.cmos_data[rtc.cmos_idx]); 72*d951bae0SPekka Enberg break; 7364136c1cSPekka Enberg } 7464136c1cSPekka Enberg 7564136c1cSPekka Enberg return true; 7664136c1cSPekka Enberg } 7764136c1cSPekka Enberg 78c9f6a037SXiao Guangrong static bool cmos_ram_data_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size) 7964136c1cSPekka Enberg { 80*d951bae0SPekka Enberg switch (rtc.cmos_idx) { 81*d951bae0SPekka Enberg case RTC_REG_C: 82*d951bae0SPekka Enberg case RTC_REG_D: 83*d951bae0SPekka Enberg /* Read-only */ 84*d951bae0SPekka Enberg break; 85*d951bae0SPekka Enberg default: 86*d951bae0SPekka Enberg rtc.cmos_data[rtc.cmos_idx] = ioport__read8(data); 87*d951bae0SPekka Enberg break; 88*d951bae0SPekka Enberg } 89*d951bae0SPekka Enberg 9064136c1cSPekka Enberg return true; 9164136c1cSPekka Enberg } 9264136c1cSPekka Enberg 9364136c1cSPekka Enberg static struct ioport_operations cmos_ram_data_ioport_ops = { 9464136c1cSPekka Enberg .io_out = cmos_ram_data_out, 9564136c1cSPekka Enberg .io_in = cmos_ram_data_in, 9664136c1cSPekka Enberg }; 9764136c1cSPekka Enberg 98c9f6a037SXiao Guangrong static bool cmos_ram_index_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size) 9964136c1cSPekka Enberg { 1002f062989SPekka Enberg u8 value = ioport__read8(data); 10164136c1cSPekka Enberg 10243835ac9SSasha Levin kvm->nmi_disabled = value & (1UL << 7); 10364136c1cSPekka Enberg 1042f062989SPekka Enberg rtc.cmos_idx = value & ~(1UL << 7); 10564136c1cSPekka Enberg 10664136c1cSPekka Enberg return true; 10764136c1cSPekka Enberg } 10864136c1cSPekka Enberg 10964136c1cSPekka Enberg static struct ioport_operations cmos_ram_index_ioport_ops = { 11064136c1cSPekka Enberg .io_out = cmos_ram_index_out, 11164136c1cSPekka Enberg }; 11264136c1cSPekka Enberg 11364136c1cSPekka Enberg void rtc__init(void) 11464136c1cSPekka Enberg { 11564136c1cSPekka Enberg /* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */ 1163d62dea6SSasha Levin ioport__register(0x0070, &cmos_ram_index_ioport_ops, 1, NULL); 1173d62dea6SSasha Levin ioport__register(0x0071, &cmos_ram_data_ioport_ops, 1, NULL); 11864136c1cSPekka Enberg } 119