xref: /kvmtool/hw/rtc.c (revision 8c45f36430bd039762628c4c146c80a47cee84da)
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