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