xref: /kvmtool/hw/rtc.c (revision 070fb918a563f5b25902419231e595f3c98b30ae)
164136c1cSPekka Enberg #include "kvm/rtc.h"
264136c1cSPekka Enberg 
3*070fb918SAlexandru Elisei #include "kvm/fdt.h"
464136c1cSPekka Enberg #include "kvm/ioport.h"
564136c1cSPekka Enberg #include "kvm/kvm.h"
664136c1cSPekka Enberg 
764136c1cSPekka Enberg #include <time.h>
864136c1cSPekka Enberg 
9382eaad7SAndre Przywara #if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
10382eaad7SAndre Przywara #define RTC_BUS_TYPE		DEVICE_BUS_MMIO
11382eaad7SAndre Przywara #define RTC_BASE_ADDRESS	ARM_RTC_MMIO_BASE
12382eaad7SAndre Przywara #else
13382eaad7SAndre Przywara /* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */
14382eaad7SAndre Przywara #define RTC_BUS_TYPE		DEVICE_BUS_IOPORT
15382eaad7SAndre Przywara #define RTC_BASE_ADDRESS	0x70
16382eaad7SAndre Przywara #endif
17382eaad7SAndre Przywara 
18c8f6893bSPekka Enberg /*
19c8f6893bSPekka Enberg  * MC146818 RTC registers
20c8f6893bSPekka Enberg  */
21c8f6893bSPekka Enberg #define RTC_SECONDS			0x00
22c8f6893bSPekka Enberg #define RTC_SECONDS_ALARM		0x01
23c8f6893bSPekka Enberg #define RTC_MINUTES			0x02
24c8f6893bSPekka Enberg #define RTC_MINUTES_ALARM		0x03
25c8f6893bSPekka Enberg #define RTC_HOURS			0x04
26c8f6893bSPekka Enberg #define RTC_HOURS_ALARM			0x05
27c8f6893bSPekka Enberg #define RTC_DAY_OF_WEEK			0x06
28c8f6893bSPekka Enberg #define RTC_DAY_OF_MONTH		0x07
29c8f6893bSPekka Enberg #define RTC_MONTH			0x08
30c8f6893bSPekka Enberg #define RTC_YEAR			0x09
3197ed4838SPekka Enberg #define RTC_CENTURY			0x32
32c8f6893bSPekka Enberg 
33c8f6893bSPekka Enberg #define RTC_REG_A			0x0A
34c8f6893bSPekka Enberg #define RTC_REG_B			0x0B
35c8f6893bSPekka Enberg #define RTC_REG_C			0x0C
36c8f6893bSPekka Enberg #define RTC_REG_D			0x0D
3764136c1cSPekka Enberg 
38473c5b29SSami Mujawar /*
39473c5b29SSami Mujawar  * Register D Bits
40473c5b29SSami Mujawar  */
41473c5b29SSami Mujawar #define RTC_REG_D_VRT			(1 << 7)
42473c5b29SSami Mujawar 
432f062989SPekka Enberg struct rtc_device {
442f062989SPekka Enberg 	u8			cmos_idx;
45d951bae0SPekka Enberg 	u8			cmos_data[128];
462f062989SPekka Enberg };
472f062989SPekka Enberg 
482f062989SPekka Enberg static struct rtc_device	rtc;
492f062989SPekka Enberg 
5064136c1cSPekka Enberg static inline unsigned char bin2bcd(unsigned val)
5164136c1cSPekka Enberg {
5264136c1cSPekka Enberg 	return ((val / 10) << 4) + val % 10;
5364136c1cSPekka Enberg }
5464136c1cSPekka Enberg 
558c45f364SAndre Przywara static void cmos_ram_io(struct kvm_cpu *vcpu, u64 addr, u8 *data,
568c45f364SAndre Przywara 			u32 len, u8 is_write, void *ptr)
5764136c1cSPekka Enberg {
5864136c1cSPekka Enberg 	struct tm *tm;
5964136c1cSPekka Enberg 	time_t ti;
6064136c1cSPekka Enberg 
618c45f364SAndre Przywara 	if (is_write) {
62382eaad7SAndre Przywara 		if (addr == RTC_BASE_ADDRESS) {	/* index register */
638c45f364SAndre Przywara 			u8 value = ioport__read8(data);
648c45f364SAndre Przywara 
658c45f364SAndre Przywara 			vcpu->kvm->nmi_disabled	= value & (1UL << 7);
668c45f364SAndre Przywara 			rtc.cmos_idx		= value & ~(1UL << 7);
678c45f364SAndre Przywara 
688c45f364SAndre Przywara 			return;
698c45f364SAndre Przywara 		}
708c45f364SAndre Przywara 
718c45f364SAndre Przywara 		switch (rtc.cmos_idx) {
728c45f364SAndre Przywara 		case RTC_REG_C:
738c45f364SAndre Przywara 		case RTC_REG_D:
748c45f364SAndre Przywara 			/* Read-only */
758c45f364SAndre Przywara 			break;
768c45f364SAndre Przywara 		default:
778c45f364SAndre Przywara 			rtc.cmos_data[rtc.cmos_idx] = ioport__read8(data);
788c45f364SAndre Przywara 			break;
798c45f364SAndre Przywara 		}
808c45f364SAndre Przywara 		return;
818c45f364SAndre Przywara 	}
828c45f364SAndre Przywara 
83382eaad7SAndre Przywara 	if (addr == RTC_BASE_ADDRESS)	/* index register is write-only */
848c45f364SAndre Przywara 		return;
858c45f364SAndre Przywara 
8664136c1cSPekka Enberg 	time(&ti);
8764136c1cSPekka Enberg 
8864136c1cSPekka Enberg 	tm = gmtime(&ti);
8964136c1cSPekka Enberg 
902f062989SPekka Enberg 	switch (rtc.cmos_idx) {
91c8f6893bSPekka Enberg 	case RTC_SECONDS:
9264136c1cSPekka Enberg 		ioport__write8(data, bin2bcd(tm->tm_sec));
9364136c1cSPekka Enberg 		break;
94c8f6893bSPekka Enberg 	case RTC_MINUTES:
9564136c1cSPekka Enberg 		ioport__write8(data, bin2bcd(tm->tm_min));
9664136c1cSPekka Enberg 		break;
97c8f6893bSPekka Enberg 	case RTC_HOURS:
9864136c1cSPekka Enberg 		ioport__write8(data, bin2bcd(tm->tm_hour));
9964136c1cSPekka Enberg 		break;
100c717392fSPekka Enberg 	case RTC_DAY_OF_WEEK:
101c717392fSPekka Enberg 		ioport__write8(data, bin2bcd(tm->tm_wday + 1));
102c717392fSPekka Enberg 		break;
103c8f6893bSPekka Enberg 	case RTC_DAY_OF_MONTH:
10464136c1cSPekka Enberg 		ioport__write8(data, bin2bcd(tm->tm_mday));
10564136c1cSPekka Enberg 		break;
106c8f6893bSPekka Enberg 	case RTC_MONTH:
10764136c1cSPekka Enberg 		ioport__write8(data, bin2bcd(tm->tm_mon + 1));
10864136c1cSPekka Enberg 		break;
10997ed4838SPekka Enberg 	case RTC_YEAR: {
11097ed4838SPekka Enberg 		int year;
11197ed4838SPekka Enberg 
11297ed4838SPekka Enberg 		year = tm->tm_year + 1900;
11397ed4838SPekka Enberg 
11497ed4838SPekka Enberg 		ioport__write8(data, bin2bcd(year % 100));
11597ed4838SPekka Enberg 
11664136c1cSPekka Enberg 		break;
11797ed4838SPekka Enberg 	}
11897ed4838SPekka Enberg 	case RTC_CENTURY: {
11997ed4838SPekka Enberg 		int year;
12097ed4838SPekka Enberg 
12197ed4838SPekka Enberg 		year = tm->tm_year + 1900;
12297ed4838SPekka Enberg 
12397ed4838SPekka Enberg 		ioport__write8(data, bin2bcd(year / 100));
12497ed4838SPekka Enberg 
12597ed4838SPekka Enberg 		break;
12697ed4838SPekka Enberg 	}
127d951bae0SPekka Enberg 	default:
128d951bae0SPekka Enberg 		ioport__write8(data, rtc.cmos_data[rtc.cmos_idx]);
129d951bae0SPekka Enberg 		break;
13064136c1cSPekka Enberg 	}
13164136c1cSPekka Enberg }
13264136c1cSPekka Enberg 
1335a3a5c07SAndre Przywara #ifdef CONFIG_HAS_LIBFDT
1345a3a5c07SAndre Przywara static void generate_rtc_fdt_node(void *fdt,
1355a3a5c07SAndre Przywara 				  struct device_header *dev_hdr,
1365a3a5c07SAndre Przywara 				  void (*generate_irq_prop)(void *fdt,
1375a3a5c07SAndre Przywara 							    u8 irq,
1385a3a5c07SAndre Przywara 							    enum irq_type))
1395a3a5c07SAndre Przywara {
140382eaad7SAndre Przywara 	u64 reg_prop[2] = { cpu_to_fdt64(RTC_BASE_ADDRESS), cpu_to_fdt64(2) };
1415a3a5c07SAndre Przywara 
1425a3a5c07SAndre Przywara 	_FDT(fdt_begin_node(fdt, "rtc"));
1435a3a5c07SAndre Przywara 	_FDT(fdt_property_string(fdt, "compatible", "motorola,mc146818"));
1445a3a5c07SAndre Przywara 	_FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop)));
1455a3a5c07SAndre Przywara 	_FDT(fdt_end_node(fdt));
1465a3a5c07SAndre Przywara }
1475a3a5c07SAndre Przywara #else
1485a3a5c07SAndre Przywara #define generate_rtc_fdt_node NULL
1495a3a5c07SAndre Przywara #endif
1505a3a5c07SAndre Przywara 
1515a3a5c07SAndre Przywara struct device_header rtc_dev_hdr = {
152382eaad7SAndre Przywara 	.bus_type = RTC_BUS_TYPE,
1535a3a5c07SAndre Przywara 	.data = generate_rtc_fdt_node,
1545a3a5c07SAndre Przywara };
1555a3a5c07SAndre Przywara 
15620c39545SSasha Levin int rtc__init(struct kvm *kvm)
15720c39545SSasha Levin {
1585a3a5c07SAndre Przywara 	int r;
1595a3a5c07SAndre Przywara 
1605a3a5c07SAndre Przywara 	r = device__register(&rtc_dev_hdr);
1615a3a5c07SAndre Przywara 	if (r < 0)
1625a3a5c07SAndre Przywara 		return r;
16320c39545SSasha Levin 
164382eaad7SAndre Przywara 	r = kvm__register_iotrap(kvm, RTC_BASE_ADDRESS, 2, cmos_ram_io, NULL,
165382eaad7SAndre Przywara 				 RTC_BUS_TYPE);
16620c39545SSasha Levin 	if (r < 0)
1675a3a5c07SAndre Przywara 		goto out_device;
16820c39545SSasha Levin 
169473c5b29SSami Mujawar 	/* Set the VRT bit in Register D to indicate valid RAM and time */
170473c5b29SSami Mujawar 	rtc.cmos_data[RTC_REG_D] = RTC_REG_D_VRT;
171473c5b29SSami Mujawar 
17220c39545SSasha Levin 	return r;
1735a3a5c07SAndre Przywara 
1745a3a5c07SAndre Przywara out_device:
1755a3a5c07SAndre Przywara 	device__unregister(&rtc_dev_hdr);
1765a3a5c07SAndre Przywara 
1775a3a5c07SAndre Przywara 	return r;
17820c39545SSasha Levin }
17949a8afd1SSasha Levin dev_init(rtc__init);
18020c39545SSasha Levin 
18120c39545SSasha Levin int rtc__exit(struct kvm *kvm)
18264136c1cSPekka Enberg {
183382eaad7SAndre Przywara 	kvm__deregister_iotrap(kvm, RTC_BASE_ADDRESS, RTC_BUS_TYPE);
18420c39545SSasha Levin 
18520c39545SSasha Levin 	return 0;
18664136c1cSPekka Enberg }
18749a8afd1SSasha Levin dev_exit(rtc__exit);
188