18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * RTC related functions
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
68c2ecf20Sopenharmony_ci#include <linux/mc146818rtc.h>
78c2ecf20Sopenharmony_ci#include <linux/acpi.h>
88c2ecf20Sopenharmony_ci#include <linux/bcd.h>
98c2ecf20Sopenharmony_ci#include <linux/export.h>
108c2ecf20Sopenharmony_ci#include <linux/pnp.h>
118c2ecf20Sopenharmony_ci#include <linux/of.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <asm/vsyscall.h>
148c2ecf20Sopenharmony_ci#include <asm/x86_init.h>
158c2ecf20Sopenharmony_ci#include <asm/time.h>
168c2ecf20Sopenharmony_ci#include <asm/intel-mid.h>
178c2ecf20Sopenharmony_ci#include <asm/setup.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_32
208c2ecf20Sopenharmony_ci/*
218c2ecf20Sopenharmony_ci * This is a special lock that is owned by the CPU and holds the index
228c2ecf20Sopenharmony_ci * register we are working with.  It is required for NMI access to the
238c2ecf20Sopenharmony_ci * CMOS/RTC registers.  See include/asm-i386/mc146818rtc.h for details.
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_civolatile unsigned long cmos_lock;
268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmos_lock);
278c2ecf20Sopenharmony_ci#endif /* CONFIG_X86_32 */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* For two digit years assume time is always after that */
308c2ecf20Sopenharmony_ci#define CMOS_YEARS_OFFS 2000
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ciDEFINE_SPINLOCK(rtc_lock);
338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rtc_lock);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * In order to set the CMOS clock precisely, set_rtc_mmss has to be
378c2ecf20Sopenharmony_ci * called 500 ms after the second nowtime has started, because when
388c2ecf20Sopenharmony_ci * nowtime is written into the registers of the CMOS clock, it will
398c2ecf20Sopenharmony_ci * jump to the next second precisely 500 ms later. Check the Motorola
408c2ecf20Sopenharmony_ci * MC146818A or Dallas DS12887 data sheet for details.
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_ciint mach_set_rtc_mmss(const struct timespec64 *now)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	unsigned long long nowtime = now->tv_sec;
458c2ecf20Sopenharmony_ci	struct rtc_time tm;
468c2ecf20Sopenharmony_ci	int retval = 0;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	rtc_time64_to_tm(nowtime, &tm);
498c2ecf20Sopenharmony_ci	if (!rtc_valid_tm(&tm)) {
508c2ecf20Sopenharmony_ci		retval = mc146818_set_time(&tm);
518c2ecf20Sopenharmony_ci		if (retval)
528c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: RTC write failed with error %d\n",
538c2ecf20Sopenharmony_ci			       __func__, retval);
548c2ecf20Sopenharmony_ci	} else {
558c2ecf20Sopenharmony_ci		printk(KERN_ERR
568c2ecf20Sopenharmony_ci		       "%s: Invalid RTC value: write of %llx to RTC failed\n",
578c2ecf20Sopenharmony_ci			__func__, nowtime);
588c2ecf20Sopenharmony_ci		retval = -EINVAL;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci	return retval;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_civoid mach_get_cmos_time(struct timespec64 *now)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	unsigned int status, year, mon, day, hour, min, sec, century = 0;
668c2ecf20Sopenharmony_ci	unsigned long flags;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/*
698c2ecf20Sopenharmony_ci	 * If pm_trace abused the RTC as storage, set the timespec to 0,
708c2ecf20Sopenharmony_ci	 * which tells the caller that this RTC value is unusable.
718c2ecf20Sopenharmony_ci	 */
728c2ecf20Sopenharmony_ci	if (!pm_trace_rtc_valid()) {
738c2ecf20Sopenharmony_ci		now->tv_sec = now->tv_nsec = 0;
748c2ecf20Sopenharmony_ci		return;
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	spin_lock_irqsave(&rtc_lock, flags);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/*
808c2ecf20Sopenharmony_ci	 * If UIP is clear, then we have >= 244 microseconds before
818c2ecf20Sopenharmony_ci	 * RTC registers will be updated.  Spec sheet says that this
828c2ecf20Sopenharmony_ci	 * is the reliable way to read RTC - registers. If UIP is set
838c2ecf20Sopenharmony_ci	 * then the register access might be invalid.
848c2ecf20Sopenharmony_ci	 */
858c2ecf20Sopenharmony_ci	while ((CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
868c2ecf20Sopenharmony_ci		cpu_relax();
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	sec = CMOS_READ(RTC_SECONDS);
898c2ecf20Sopenharmony_ci	min = CMOS_READ(RTC_MINUTES);
908c2ecf20Sopenharmony_ci	hour = CMOS_READ(RTC_HOURS);
918c2ecf20Sopenharmony_ci	day = CMOS_READ(RTC_DAY_OF_MONTH);
928c2ecf20Sopenharmony_ci	mon = CMOS_READ(RTC_MONTH);
938c2ecf20Sopenharmony_ci	year = CMOS_READ(RTC_YEAR);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
968c2ecf20Sopenharmony_ci	if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
978c2ecf20Sopenharmony_ci	    acpi_gbl_FADT.century)
988c2ecf20Sopenharmony_ci		century = CMOS_READ(acpi_gbl_FADT.century);
998c2ecf20Sopenharmony_ci#endif
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	status = CMOS_READ(RTC_CONTROL);
1028c2ecf20Sopenharmony_ci	WARN_ON_ONCE(RTC_ALWAYS_BCD && (status & RTC_DM_BINARY));
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&rtc_lock, flags);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (RTC_ALWAYS_BCD || !(status & RTC_DM_BINARY)) {
1078c2ecf20Sopenharmony_ci		sec = bcd2bin(sec);
1088c2ecf20Sopenharmony_ci		min = bcd2bin(min);
1098c2ecf20Sopenharmony_ci		hour = bcd2bin(hour);
1108c2ecf20Sopenharmony_ci		day = bcd2bin(day);
1118c2ecf20Sopenharmony_ci		mon = bcd2bin(mon);
1128c2ecf20Sopenharmony_ci		year = bcd2bin(year);
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (century) {
1168c2ecf20Sopenharmony_ci		century = bcd2bin(century);
1178c2ecf20Sopenharmony_ci		year += century * 100;
1188c2ecf20Sopenharmony_ci	} else
1198c2ecf20Sopenharmony_ci		year += CMOS_YEARS_OFFS;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	now->tv_sec = mktime64(year, mon, day, hour, min, sec);
1228c2ecf20Sopenharmony_ci	now->tv_nsec = 0;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/* Routines for accessing the CMOS RAM/RTC. */
1268c2ecf20Sopenharmony_ciunsigned char rtc_cmos_read(unsigned char addr)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	unsigned char val;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	lock_cmos_prefix(addr);
1318c2ecf20Sopenharmony_ci	outb(addr, RTC_PORT(0));
1328c2ecf20Sopenharmony_ci	val = inb(RTC_PORT(1));
1338c2ecf20Sopenharmony_ci	lock_cmos_suffix(addr);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return val;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rtc_cmos_read);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_civoid rtc_cmos_write(unsigned char val, unsigned char addr)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	lock_cmos_prefix(addr);
1428c2ecf20Sopenharmony_ci	outb(addr, RTC_PORT(0));
1438c2ecf20Sopenharmony_ci	outb(val, RTC_PORT(1));
1448c2ecf20Sopenharmony_ci	lock_cmos_suffix(addr);
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rtc_cmos_write);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ciint update_persistent_clock64(struct timespec64 now)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	return x86_platform.set_wallclock(&now);
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci/* not static: needed by APM */
1548c2ecf20Sopenharmony_civoid read_persistent_clock64(struct timespec64 *ts)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	x86_platform.get_wallclock(ts);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic struct resource rtc_resources[] = {
1618c2ecf20Sopenharmony_ci	[0] = {
1628c2ecf20Sopenharmony_ci		.start	= RTC_PORT(0),
1638c2ecf20Sopenharmony_ci		.end	= RTC_PORT(1),
1648c2ecf20Sopenharmony_ci		.flags	= IORESOURCE_IO,
1658c2ecf20Sopenharmony_ci	},
1668c2ecf20Sopenharmony_ci	[1] = {
1678c2ecf20Sopenharmony_ci		.start	= RTC_IRQ,
1688c2ecf20Sopenharmony_ci		.end	= RTC_IRQ,
1698c2ecf20Sopenharmony_ci		.flags	= IORESOURCE_IRQ,
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci};
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic struct platform_device rtc_device = {
1748c2ecf20Sopenharmony_ci	.name		= "rtc_cmos",
1758c2ecf20Sopenharmony_ci	.id		= -1,
1768c2ecf20Sopenharmony_ci	.resource	= rtc_resources,
1778c2ecf20Sopenharmony_ci	.num_resources	= ARRAY_SIZE(rtc_resources),
1788c2ecf20Sopenharmony_ci};
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic __init int add_rtc_cmos(void)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP
1838c2ecf20Sopenharmony_ci	static const char * const ids[] __initconst =
1848c2ecf20Sopenharmony_ci	    { "PNP0b00", "PNP0b01", "PNP0b02", };
1858c2ecf20Sopenharmony_ci	struct pnp_dev *dev;
1868c2ecf20Sopenharmony_ci	struct pnp_id *id;
1878c2ecf20Sopenharmony_ci	int i;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	pnp_for_each_dev(dev) {
1908c2ecf20Sopenharmony_ci		for (id = dev->id; id; id = id->next) {
1918c2ecf20Sopenharmony_ci			for (i = 0; i < ARRAY_SIZE(ids); i++) {
1928c2ecf20Sopenharmony_ci				if (compare_pnp_id(id, ids[i]) != 0)
1938c2ecf20Sopenharmony_ci					return 0;
1948c2ecf20Sopenharmony_ci			}
1958c2ecf20Sopenharmony_ci		}
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci#endif
1988c2ecf20Sopenharmony_ci	if (!x86_platform.legacy.rtc)
1998c2ecf20Sopenharmony_ci		return -ENODEV;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	platform_device_register(&rtc_device);
2028c2ecf20Sopenharmony_ci	dev_info(&rtc_device.dev,
2038c2ecf20Sopenharmony_ci		 "registered platform RTC device (no PNP device found)\n");
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return 0;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_cidevice_initcall(add_rtc_cmos);
208