18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * RTC class driver for "CMOS RTC":  PCs, ACPI, etc
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 1996 Paul Gortmaker (drivers/char/rtc.c)
68c2ecf20Sopenharmony_ci * Copyright (C) 2006 David Brownell (convert to new framework)
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci * The original "cmos clock" chip was an MC146818 chip, now obsolete.
118c2ecf20Sopenharmony_ci * That defined the register interface now provided by all PCs, some
128c2ecf20Sopenharmony_ci * non-PC systems, and incorporated into ACPI.  Modern PC chipsets
138c2ecf20Sopenharmony_ci * integrate an MC146818 clone in their southbridge, and boards use
148c2ecf20Sopenharmony_ci * that instead of discrete clones like the DS12887 or M48T86.  There
158c2ecf20Sopenharmony_ci * are also clones that connect using the LPC bus.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * That register API is also used directly by various other drivers
188c2ecf20Sopenharmony_ci * (notably for integrated NVRAM), infrastructure (x86 has code to
198c2ecf20Sopenharmony_ci * bypass the RTC framework, directly reading the RTC during boot
208c2ecf20Sopenharmony_ci * and updating minutes/seconds for systems using NTP synch) and
218c2ecf20Sopenharmony_ci * utilities (like userspace 'hwclock', if no /dev node exists).
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * So **ALL** calls to CMOS_READ and CMOS_WRITE must be done with
248c2ecf20Sopenharmony_ci * interrupts disabled, holding the global rtc_lock, to exclude those
258c2ecf20Sopenharmony_ci * other drivers and utilities on correctly configured systems.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <linux/kernel.h>
318c2ecf20Sopenharmony_ci#include <linux/module.h>
328c2ecf20Sopenharmony_ci#include <linux/init.h>
338c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
348c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
358c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
368c2ecf20Sopenharmony_ci#include <linux/log2.h>
378c2ecf20Sopenharmony_ci#include <linux/pm.h>
388c2ecf20Sopenharmony_ci#include <linux/of.h>
398c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
408c2ecf20Sopenharmony_ci#ifdef CONFIG_X86
418c2ecf20Sopenharmony_ci#include <asm/i8259.h>
428c2ecf20Sopenharmony_ci#include <asm/processor.h>
438c2ecf20Sopenharmony_ci#include <linux/dmi.h>
448c2ecf20Sopenharmony_ci#endif
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
478c2ecf20Sopenharmony_ci#include <linux/mc146818rtc.h>
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
508c2ecf20Sopenharmony_ci/*
518c2ecf20Sopenharmony_ci * Use ACPI SCI to replace HPET interrupt for RTC Alarm event
528c2ecf20Sopenharmony_ci *
538c2ecf20Sopenharmony_ci * If cleared, ACPI SCI is only used to wake up the system from suspend
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci * If set, ACPI SCI is used to handle UIE/AIE and system wakeup
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic bool use_acpi_alarm;
598c2ecf20Sopenharmony_cimodule_param(use_acpi_alarm, bool, 0444);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic inline int cmos_use_acpi_alarm(void)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	return use_acpi_alarm;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci#else /* !CONFIG_ACPI */
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic inline int cmos_use_acpi_alarm(void)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	return 0;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci#endif
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistruct cmos_rtc {
748c2ecf20Sopenharmony_ci	struct rtc_device	*rtc;
758c2ecf20Sopenharmony_ci	struct device		*dev;
768c2ecf20Sopenharmony_ci	int			irq;
778c2ecf20Sopenharmony_ci	struct resource		*iomem;
788c2ecf20Sopenharmony_ci	time64_t		alarm_expires;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	void			(*wake_on)(struct device *);
818c2ecf20Sopenharmony_ci	void			(*wake_off)(struct device *);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	u8			enabled_wake;
848c2ecf20Sopenharmony_ci	u8			suspend_ctrl;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	/* newer hardware extends the original register set */
878c2ecf20Sopenharmony_ci	u8			day_alrm;
888c2ecf20Sopenharmony_ci	u8			mon_alrm;
898c2ecf20Sopenharmony_ci	u8			century;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	struct rtc_wkalrm	saved_wkalrm;
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/* both platform and pnp busses use negative numbers for invalid irqs */
958c2ecf20Sopenharmony_ci#define is_valid_irq(n)		((n) > 0)
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic const char driver_name[] = "rtc_cmos";
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/* The RTC_INTR register may have e.g. RTC_PF set even if RTC_PIE is clear;
1008c2ecf20Sopenharmony_ci * always mask it against the irq enable bits in RTC_CONTROL.  Bit values
1018c2ecf20Sopenharmony_ci * are the same: PF==PIE, AF=AIE, UF=UIE; so RTC_IRQMASK works with both.
1028c2ecf20Sopenharmony_ci */
1038c2ecf20Sopenharmony_ci#define	RTC_IRQMASK	(RTC_PF | RTC_AF | RTC_UF)
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic inline int is_intr(u8 rtc_intr)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	if (!(rtc_intr & RTC_IRQF))
1088c2ecf20Sopenharmony_ci		return 0;
1098c2ecf20Sopenharmony_ci	return rtc_intr & RTC_IRQMASK;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/* Much modern x86 hardware has HPETs (10+ MHz timers) which, because
1158c2ecf20Sopenharmony_ci * many BIOS programmers don't set up "sane mode" IRQ routing, are mostly
1168c2ecf20Sopenharmony_ci * used in a broken "legacy replacement" mode.  The breakage includes
1178c2ecf20Sopenharmony_ci * HPET #1 hijacking the IRQ for this RTC, and being unavailable for
1188c2ecf20Sopenharmony_ci * other (better) use.
1198c2ecf20Sopenharmony_ci *
1208c2ecf20Sopenharmony_ci * When that broken mode is in use, platform glue provides a partial
1218c2ecf20Sopenharmony_ci * emulation of hardware RTC IRQ facilities using HPET #1.  We don't
1228c2ecf20Sopenharmony_ci * want to use HPET for anything except those IRQs though...
1238c2ecf20Sopenharmony_ci */
1248c2ecf20Sopenharmony_ci#ifdef CONFIG_HPET_EMULATE_RTC
1258c2ecf20Sopenharmony_ci#include <asm/hpet.h>
1268c2ecf20Sopenharmony_ci#else
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic inline int is_hpet_enabled(void)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	return 0;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic inline int hpet_mask_rtc_irq_bit(unsigned long mask)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	return 0;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic inline int hpet_set_rtc_irq_bit(unsigned long mask)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	return 0;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic inline int
1448c2ecf20Sopenharmony_cihpet_set_alarm_time(unsigned char hrs, unsigned char min, unsigned char sec)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	return 0;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic inline int hpet_set_periodic_freq(unsigned long freq)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	return 0;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic inline int hpet_rtc_dropped_irq(void)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	return 0;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic inline int hpet_rtc_timer_init(void)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	return 0;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ciextern irq_handler_t hpet_rtc_interrupt;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic inline int hpet_register_irq_handler(irq_handler_t handler)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic inline int hpet_unregister_irq_handler(irq_handler_t handler)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	return 0;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci#endif
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/* Don't use HPET for RTC Alarm event if ACPI Fixed event is used */
1798c2ecf20Sopenharmony_cistatic inline int use_hpet_alarm(void)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	return is_hpet_enabled() && !cmos_use_acpi_alarm();
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci#ifdef RTC_PORT
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/* Most newer x86 systems have two register banks, the first used
1898c2ecf20Sopenharmony_ci * for RTC and NVRAM and the second only for NVRAM.  Caller must
1908c2ecf20Sopenharmony_ci * own rtc_lock ... and we won't worry about access during NMI.
1918c2ecf20Sopenharmony_ci */
1928c2ecf20Sopenharmony_ci#define can_bank2	true
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic inline unsigned char cmos_read_bank2(unsigned char addr)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	outb(addr, RTC_PORT(2));
1978c2ecf20Sopenharmony_ci	return inb(RTC_PORT(3));
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic inline void cmos_write_bank2(unsigned char val, unsigned char addr)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	outb(addr, RTC_PORT(2));
2038c2ecf20Sopenharmony_ci	outb(val, RTC_PORT(3));
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci#else
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci#define can_bank2	false
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic inline unsigned char cmos_read_bank2(unsigned char addr)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	return 0;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic inline void cmos_write_bank2(unsigned char val, unsigned char addr)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci#endif
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic int cmos_read_time(struct device *dev, struct rtc_time *t)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	int ret;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/*
2288c2ecf20Sopenharmony_ci	 * If pm_trace abused the RTC for storage, set the timespec to 0,
2298c2ecf20Sopenharmony_ci	 * which tells the caller that this RTC value is unusable.
2308c2ecf20Sopenharmony_ci	 */
2318c2ecf20Sopenharmony_ci	if (!pm_trace_rtc_valid())
2328c2ecf20Sopenharmony_ci		return -EIO;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	ret = mc146818_get_time(t);
2358c2ecf20Sopenharmony_ci	if (ret < 0) {
2368c2ecf20Sopenharmony_ci		dev_err_ratelimited(dev, "unable to read current time\n");
2378c2ecf20Sopenharmony_ci		return ret;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return 0;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int cmos_set_time(struct device *dev, struct rtc_time *t)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	/* NOTE: this ignores the issue whereby updating the seconds
2468c2ecf20Sopenharmony_ci	 * takes effect exactly 500ms after we write the register.
2478c2ecf20Sopenharmony_ci	 * (Also queueing and other delays before we get this far.)
2488c2ecf20Sopenharmony_ci	 */
2498c2ecf20Sopenharmony_ci	return mc146818_set_time(t);
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistruct cmos_read_alarm_callback_param {
2538c2ecf20Sopenharmony_ci	struct cmos_rtc *cmos;
2548c2ecf20Sopenharmony_ci	struct rtc_time *time;
2558c2ecf20Sopenharmony_ci	unsigned char	rtc_control;
2568c2ecf20Sopenharmony_ci};
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic void cmos_read_alarm_callback(unsigned char __always_unused seconds,
2598c2ecf20Sopenharmony_ci				     void *param_in)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct cmos_read_alarm_callback_param *p =
2628c2ecf20Sopenharmony_ci		(struct cmos_read_alarm_callback_param *)param_in;
2638c2ecf20Sopenharmony_ci	struct rtc_time *time = p->time;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	time->tm_sec = CMOS_READ(RTC_SECONDS_ALARM);
2668c2ecf20Sopenharmony_ci	time->tm_min = CMOS_READ(RTC_MINUTES_ALARM);
2678c2ecf20Sopenharmony_ci	time->tm_hour = CMOS_READ(RTC_HOURS_ALARM);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (p->cmos->day_alrm) {
2708c2ecf20Sopenharmony_ci		/* ignore upper bits on readback per ACPI spec */
2718c2ecf20Sopenharmony_ci		time->tm_mday = CMOS_READ(p->cmos->day_alrm) & 0x3f;
2728c2ecf20Sopenharmony_ci		if (!time->tm_mday)
2738c2ecf20Sopenharmony_ci			time->tm_mday = -1;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		if (p->cmos->mon_alrm) {
2768c2ecf20Sopenharmony_ci			time->tm_mon = CMOS_READ(p->cmos->mon_alrm);
2778c2ecf20Sopenharmony_ci			if (!time->tm_mon)
2788c2ecf20Sopenharmony_ci				time->tm_mon = -1;
2798c2ecf20Sopenharmony_ci		}
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	p->rtc_control = CMOS_READ(RTC_CONTROL);
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	struct cmos_rtc	*cmos = dev_get_drvdata(dev);
2888c2ecf20Sopenharmony_ci	struct cmos_read_alarm_callback_param p = {
2898c2ecf20Sopenharmony_ci		.cmos = cmos,
2908c2ecf20Sopenharmony_ci		.time = &t->time,
2918c2ecf20Sopenharmony_ci	};
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/* This not only a rtc_op, but also called directly */
2948c2ecf20Sopenharmony_ci	if (!is_valid_irq(cmos->irq))
2958c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	/* Basic alarms only support hour, minute, and seconds fields.
2988c2ecf20Sopenharmony_ci	 * Some also support day and month, for alarms up to a year in
2998c2ecf20Sopenharmony_ci	 * the future.
3008c2ecf20Sopenharmony_ci	 */
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* Some Intel chipsets disconnect the alarm registers when the clock
3038c2ecf20Sopenharmony_ci	 * update is in progress - during this time reads return bogus values
3048c2ecf20Sopenharmony_ci	 * and writes may fail silently. See for example "7th Generation Intel®
3058c2ecf20Sopenharmony_ci	 * Processor Family I/O for U/Y Platforms [...] Datasheet", section
3068c2ecf20Sopenharmony_ci	 * 27.7.1
3078c2ecf20Sopenharmony_ci	 *
3088c2ecf20Sopenharmony_ci	 * Use the mc146818_avoid_UIP() function to avoid this.
3098c2ecf20Sopenharmony_ci	 */
3108c2ecf20Sopenharmony_ci	if (!mc146818_avoid_UIP(cmos_read_alarm_callback, &p))
3118c2ecf20Sopenharmony_ci		return -EIO;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (!(p.rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
3148c2ecf20Sopenharmony_ci		if (((unsigned)t->time.tm_sec) < 0x60)
3158c2ecf20Sopenharmony_ci			t->time.tm_sec = bcd2bin(t->time.tm_sec);
3168c2ecf20Sopenharmony_ci		else
3178c2ecf20Sopenharmony_ci			t->time.tm_sec = -1;
3188c2ecf20Sopenharmony_ci		if (((unsigned)t->time.tm_min) < 0x60)
3198c2ecf20Sopenharmony_ci			t->time.tm_min = bcd2bin(t->time.tm_min);
3208c2ecf20Sopenharmony_ci		else
3218c2ecf20Sopenharmony_ci			t->time.tm_min = -1;
3228c2ecf20Sopenharmony_ci		if (((unsigned)t->time.tm_hour) < 0x24)
3238c2ecf20Sopenharmony_ci			t->time.tm_hour = bcd2bin(t->time.tm_hour);
3248c2ecf20Sopenharmony_ci		else
3258c2ecf20Sopenharmony_ci			t->time.tm_hour = -1;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		if (cmos->day_alrm) {
3288c2ecf20Sopenharmony_ci			if (((unsigned)t->time.tm_mday) <= 0x31)
3298c2ecf20Sopenharmony_ci				t->time.tm_mday = bcd2bin(t->time.tm_mday);
3308c2ecf20Sopenharmony_ci			else
3318c2ecf20Sopenharmony_ci				t->time.tm_mday = -1;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci			if (cmos->mon_alrm) {
3348c2ecf20Sopenharmony_ci				if (((unsigned)t->time.tm_mon) <= 0x12)
3358c2ecf20Sopenharmony_ci					t->time.tm_mon = bcd2bin(t->time.tm_mon)-1;
3368c2ecf20Sopenharmony_ci				else
3378c2ecf20Sopenharmony_ci					t->time.tm_mon = -1;
3388c2ecf20Sopenharmony_ci			}
3398c2ecf20Sopenharmony_ci		}
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	t->enabled = !!(p.rtc_control & RTC_AIE);
3438c2ecf20Sopenharmony_ci	t->pending = 0;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	return 0;
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic void cmos_checkintr(struct cmos_rtc *cmos, unsigned char rtc_control)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	unsigned char	rtc_intr;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/* NOTE after changing RTC_xIE bits we always read INTR_FLAGS;
3538c2ecf20Sopenharmony_ci	 * allegedly some older rtcs need that to handle irqs properly
3548c2ecf20Sopenharmony_ci	 */
3558c2ecf20Sopenharmony_ci	rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (use_hpet_alarm())
3588c2ecf20Sopenharmony_ci		return;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
3618c2ecf20Sopenharmony_ci	if (is_intr(rtc_intr))
3628c2ecf20Sopenharmony_ci		rtc_update_irq(cmos->rtc, 1, rtc_intr);
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic void cmos_irq_enable(struct cmos_rtc *cmos, unsigned char mask)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	unsigned char	rtc_control;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/* flush any pending IRQ status, notably for update irqs,
3708c2ecf20Sopenharmony_ci	 * before we enable new IRQs
3718c2ecf20Sopenharmony_ci	 */
3728c2ecf20Sopenharmony_ci	rtc_control = CMOS_READ(RTC_CONTROL);
3738c2ecf20Sopenharmony_ci	cmos_checkintr(cmos, rtc_control);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	rtc_control |= mask;
3768c2ecf20Sopenharmony_ci	CMOS_WRITE(rtc_control, RTC_CONTROL);
3778c2ecf20Sopenharmony_ci	if (use_hpet_alarm())
3788c2ecf20Sopenharmony_ci		hpet_set_rtc_irq_bit(mask);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if ((mask & RTC_AIE) && cmos_use_acpi_alarm()) {
3818c2ecf20Sopenharmony_ci		if (cmos->wake_on)
3828c2ecf20Sopenharmony_ci			cmos->wake_on(cmos->dev);
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	cmos_checkintr(cmos, rtc_control);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	unsigned char	rtc_control;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	rtc_control = CMOS_READ(RTC_CONTROL);
3938c2ecf20Sopenharmony_ci	rtc_control &= ~mask;
3948c2ecf20Sopenharmony_ci	CMOS_WRITE(rtc_control, RTC_CONTROL);
3958c2ecf20Sopenharmony_ci	if (use_hpet_alarm())
3968c2ecf20Sopenharmony_ci		hpet_mask_rtc_irq_bit(mask);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if ((mask & RTC_AIE) && cmos_use_acpi_alarm()) {
3998c2ecf20Sopenharmony_ci		if (cmos->wake_off)
4008c2ecf20Sopenharmony_ci			cmos->wake_off(cmos->dev);
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	cmos_checkintr(cmos, rtc_control);
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic int cmos_validate_alarm(struct device *dev, struct rtc_wkalrm *t)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	struct cmos_rtc *cmos = dev_get_drvdata(dev);
4098c2ecf20Sopenharmony_ci	struct rtc_time now;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	cmos_read_time(dev, &now);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (!cmos->day_alrm) {
4148c2ecf20Sopenharmony_ci		time64_t t_max_date;
4158c2ecf20Sopenharmony_ci		time64_t t_alrm;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci		t_max_date = rtc_tm_to_time64(&now);
4188c2ecf20Sopenharmony_ci		t_max_date += 24 * 60 * 60 - 1;
4198c2ecf20Sopenharmony_ci		t_alrm = rtc_tm_to_time64(&t->time);
4208c2ecf20Sopenharmony_ci		if (t_alrm > t_max_date) {
4218c2ecf20Sopenharmony_ci			dev_err(dev,
4228c2ecf20Sopenharmony_ci				"Alarms can be up to one day in the future\n");
4238c2ecf20Sopenharmony_ci			return -EINVAL;
4248c2ecf20Sopenharmony_ci		}
4258c2ecf20Sopenharmony_ci	} else if (!cmos->mon_alrm) {
4268c2ecf20Sopenharmony_ci		struct rtc_time max_date = now;
4278c2ecf20Sopenharmony_ci		time64_t t_max_date;
4288c2ecf20Sopenharmony_ci		time64_t t_alrm;
4298c2ecf20Sopenharmony_ci		int max_mday;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		if (max_date.tm_mon == 11) {
4328c2ecf20Sopenharmony_ci			max_date.tm_mon = 0;
4338c2ecf20Sopenharmony_ci			max_date.tm_year += 1;
4348c2ecf20Sopenharmony_ci		} else {
4358c2ecf20Sopenharmony_ci			max_date.tm_mon += 1;
4368c2ecf20Sopenharmony_ci		}
4378c2ecf20Sopenharmony_ci		max_mday = rtc_month_days(max_date.tm_mon, max_date.tm_year);
4388c2ecf20Sopenharmony_ci		if (max_date.tm_mday > max_mday)
4398c2ecf20Sopenharmony_ci			max_date.tm_mday = max_mday;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci		t_max_date = rtc_tm_to_time64(&max_date);
4428c2ecf20Sopenharmony_ci		t_max_date -= 1;
4438c2ecf20Sopenharmony_ci		t_alrm = rtc_tm_to_time64(&t->time);
4448c2ecf20Sopenharmony_ci		if (t_alrm > t_max_date) {
4458c2ecf20Sopenharmony_ci			dev_err(dev,
4468c2ecf20Sopenharmony_ci				"Alarms can be up to one month in the future\n");
4478c2ecf20Sopenharmony_ci			return -EINVAL;
4488c2ecf20Sopenharmony_ci		}
4498c2ecf20Sopenharmony_ci	} else {
4508c2ecf20Sopenharmony_ci		struct rtc_time max_date = now;
4518c2ecf20Sopenharmony_ci		time64_t t_max_date;
4528c2ecf20Sopenharmony_ci		time64_t t_alrm;
4538c2ecf20Sopenharmony_ci		int max_mday;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci		max_date.tm_year += 1;
4568c2ecf20Sopenharmony_ci		max_mday = rtc_month_days(max_date.tm_mon, max_date.tm_year);
4578c2ecf20Sopenharmony_ci		if (max_date.tm_mday > max_mday)
4588c2ecf20Sopenharmony_ci			max_date.tm_mday = max_mday;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		t_max_date = rtc_tm_to_time64(&max_date);
4618c2ecf20Sopenharmony_ci		t_max_date -= 1;
4628c2ecf20Sopenharmony_ci		t_alrm = rtc_tm_to_time64(&t->time);
4638c2ecf20Sopenharmony_ci		if (t_alrm > t_max_date) {
4648c2ecf20Sopenharmony_ci			dev_err(dev,
4658c2ecf20Sopenharmony_ci				"Alarms can be up to one year in the future\n");
4668c2ecf20Sopenharmony_ci			return -EINVAL;
4678c2ecf20Sopenharmony_ci		}
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	return 0;
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistruct cmos_set_alarm_callback_param {
4748c2ecf20Sopenharmony_ci	struct cmos_rtc *cmos;
4758c2ecf20Sopenharmony_ci	unsigned char mon, mday, hrs, min, sec;
4768c2ecf20Sopenharmony_ci	struct rtc_wkalrm *t;
4778c2ecf20Sopenharmony_ci};
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci/* Note: this function may be executed by mc146818_avoid_UIP() more then
4808c2ecf20Sopenharmony_ci *	 once
4818c2ecf20Sopenharmony_ci */
4828c2ecf20Sopenharmony_cistatic void cmos_set_alarm_callback(unsigned char __always_unused seconds,
4838c2ecf20Sopenharmony_ci				    void *param_in)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	struct cmos_set_alarm_callback_param *p =
4868c2ecf20Sopenharmony_ci		(struct cmos_set_alarm_callback_param *)param_in;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	/* next rtc irq must not be from previous alarm setting */
4898c2ecf20Sopenharmony_ci	cmos_irq_disable(p->cmos, RTC_AIE);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* update alarm */
4928c2ecf20Sopenharmony_ci	CMOS_WRITE(p->hrs, RTC_HOURS_ALARM);
4938c2ecf20Sopenharmony_ci	CMOS_WRITE(p->min, RTC_MINUTES_ALARM);
4948c2ecf20Sopenharmony_ci	CMOS_WRITE(p->sec, RTC_SECONDS_ALARM);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	/* the system may support an "enhanced" alarm */
4978c2ecf20Sopenharmony_ci	if (p->cmos->day_alrm) {
4988c2ecf20Sopenharmony_ci		CMOS_WRITE(p->mday, p->cmos->day_alrm);
4998c2ecf20Sopenharmony_ci		if (p->cmos->mon_alrm)
5008c2ecf20Sopenharmony_ci			CMOS_WRITE(p->mon, p->cmos->mon_alrm);
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	if (use_hpet_alarm()) {
5048c2ecf20Sopenharmony_ci		/*
5058c2ecf20Sopenharmony_ci		 * FIXME the HPET alarm glue currently ignores day_alrm
5068c2ecf20Sopenharmony_ci		 * and mon_alrm ...
5078c2ecf20Sopenharmony_ci		 */
5088c2ecf20Sopenharmony_ci		hpet_set_alarm_time(p->t->time.tm_hour, p->t->time.tm_min,
5098c2ecf20Sopenharmony_ci				    p->t->time.tm_sec);
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	if (p->t->enabled)
5138c2ecf20Sopenharmony_ci		cmos_irq_enable(p->cmos, RTC_AIE);
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	struct cmos_rtc	*cmos = dev_get_drvdata(dev);
5198c2ecf20Sopenharmony_ci	struct cmos_set_alarm_callback_param p = {
5208c2ecf20Sopenharmony_ci		.cmos = cmos,
5218c2ecf20Sopenharmony_ci		.t = t
5228c2ecf20Sopenharmony_ci	};
5238c2ecf20Sopenharmony_ci	unsigned char rtc_control;
5248c2ecf20Sopenharmony_ci	int ret;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* This not only a rtc_op, but also called directly */
5278c2ecf20Sopenharmony_ci	if (!is_valid_irq(cmos->irq))
5288c2ecf20Sopenharmony_ci		return -EIO;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	ret = cmos_validate_alarm(dev, t);
5318c2ecf20Sopenharmony_ci	if (ret < 0)
5328c2ecf20Sopenharmony_ci		return ret;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	p.mon = t->time.tm_mon + 1;
5358c2ecf20Sopenharmony_ci	p.mday = t->time.tm_mday;
5368c2ecf20Sopenharmony_ci	p.hrs = t->time.tm_hour;
5378c2ecf20Sopenharmony_ci	p.min = t->time.tm_min;
5388c2ecf20Sopenharmony_ci	p.sec = t->time.tm_sec;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	spin_lock_irq(&rtc_lock);
5418c2ecf20Sopenharmony_ci	rtc_control = CMOS_READ(RTC_CONTROL);
5428c2ecf20Sopenharmony_ci	spin_unlock_irq(&rtc_lock);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
5458c2ecf20Sopenharmony_ci		/* Writing 0xff means "don't care" or "match all".  */
5468c2ecf20Sopenharmony_ci		p.mon = (p.mon <= 12) ? bin2bcd(p.mon) : 0xff;
5478c2ecf20Sopenharmony_ci		p.mday = (p.mday >= 1 && p.mday <= 31) ? bin2bcd(p.mday) : 0xff;
5488c2ecf20Sopenharmony_ci		p.hrs = (p.hrs < 24) ? bin2bcd(p.hrs) : 0xff;
5498c2ecf20Sopenharmony_ci		p.min = (p.min < 60) ? bin2bcd(p.min) : 0xff;
5508c2ecf20Sopenharmony_ci		p.sec = (p.sec < 60) ? bin2bcd(p.sec) : 0xff;
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	/*
5548c2ecf20Sopenharmony_ci	 * Some Intel chipsets disconnect the alarm registers when the clock
5558c2ecf20Sopenharmony_ci	 * update is in progress - during this time writes fail silently.
5568c2ecf20Sopenharmony_ci	 *
5578c2ecf20Sopenharmony_ci	 * Use mc146818_avoid_UIP() to avoid this.
5588c2ecf20Sopenharmony_ci	 */
5598c2ecf20Sopenharmony_ci	if (!mc146818_avoid_UIP(cmos_set_alarm_callback, &p))
5608c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	cmos->alarm_expires = rtc_tm_to_time64(&t->time);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	return 0;
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_cistatic int cmos_alarm_irq_enable(struct device *dev, unsigned int enabled)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	struct cmos_rtc	*cmos = dev_get_drvdata(dev);
5708c2ecf20Sopenharmony_ci	unsigned long	flags;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	spin_lock_irqsave(&rtc_lock, flags);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	if (enabled)
5758c2ecf20Sopenharmony_ci		cmos_irq_enable(cmos, RTC_AIE);
5768c2ecf20Sopenharmony_ci	else
5778c2ecf20Sopenharmony_ci		cmos_irq_disable(cmos, RTC_AIE);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&rtc_lock, flags);
5808c2ecf20Sopenharmony_ci	return 0;
5818c2ecf20Sopenharmony_ci}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_RTC_INTF_PROC)
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_cistatic int cmos_procfs(struct device *dev, struct seq_file *seq)
5868c2ecf20Sopenharmony_ci{
5878c2ecf20Sopenharmony_ci	struct cmos_rtc	*cmos = dev_get_drvdata(dev);
5888c2ecf20Sopenharmony_ci	unsigned char	rtc_control, valid;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	spin_lock_irq(&rtc_lock);
5918c2ecf20Sopenharmony_ci	rtc_control = CMOS_READ(RTC_CONTROL);
5928c2ecf20Sopenharmony_ci	valid = CMOS_READ(RTC_VALID);
5938c2ecf20Sopenharmony_ci	spin_unlock_irq(&rtc_lock);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	/* NOTE:  at least ICH6 reports battery status using a different
5968c2ecf20Sopenharmony_ci	 * (non-RTC) bit; and SQWE is ignored on many current systems.
5978c2ecf20Sopenharmony_ci	 */
5988c2ecf20Sopenharmony_ci	seq_printf(seq,
5998c2ecf20Sopenharmony_ci		   "periodic_IRQ\t: %s\n"
6008c2ecf20Sopenharmony_ci		   "update_IRQ\t: %s\n"
6018c2ecf20Sopenharmony_ci		   "HPET_emulated\t: %s\n"
6028c2ecf20Sopenharmony_ci		   // "square_wave\t: %s\n"
6038c2ecf20Sopenharmony_ci		   "BCD\t\t: %s\n"
6048c2ecf20Sopenharmony_ci		   "DST_enable\t: %s\n"
6058c2ecf20Sopenharmony_ci		   "periodic_freq\t: %d\n"
6068c2ecf20Sopenharmony_ci		   "batt_status\t: %s\n",
6078c2ecf20Sopenharmony_ci		   (rtc_control & RTC_PIE) ? "yes" : "no",
6088c2ecf20Sopenharmony_ci		   (rtc_control & RTC_UIE) ? "yes" : "no",
6098c2ecf20Sopenharmony_ci		   use_hpet_alarm() ? "yes" : "no",
6108c2ecf20Sopenharmony_ci		   // (rtc_control & RTC_SQWE) ? "yes" : "no",
6118c2ecf20Sopenharmony_ci		   (rtc_control & RTC_DM_BINARY) ? "no" : "yes",
6128c2ecf20Sopenharmony_ci		   (rtc_control & RTC_DST_EN) ? "yes" : "no",
6138c2ecf20Sopenharmony_ci		   cmos->rtc->irq_freq,
6148c2ecf20Sopenharmony_ci		   (valid & RTC_VRT) ? "okay" : "dead");
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	return 0;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci#else
6208c2ecf20Sopenharmony_ci#define	cmos_procfs	NULL
6218c2ecf20Sopenharmony_ci#endif
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic const struct rtc_class_ops cmos_rtc_ops = {
6248c2ecf20Sopenharmony_ci	.read_time		= cmos_read_time,
6258c2ecf20Sopenharmony_ci	.set_time		= cmos_set_time,
6268c2ecf20Sopenharmony_ci	.read_alarm		= cmos_read_alarm,
6278c2ecf20Sopenharmony_ci	.set_alarm		= cmos_set_alarm,
6288c2ecf20Sopenharmony_ci	.proc			= cmos_procfs,
6298c2ecf20Sopenharmony_ci	.alarm_irq_enable	= cmos_alarm_irq_enable,
6308c2ecf20Sopenharmony_ci};
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_cistatic const struct rtc_class_ops cmos_rtc_ops_no_alarm = {
6338c2ecf20Sopenharmony_ci	.read_time		= cmos_read_time,
6348c2ecf20Sopenharmony_ci	.set_time		= cmos_set_time,
6358c2ecf20Sopenharmony_ci	.proc			= cmos_procfs,
6368c2ecf20Sopenharmony_ci};
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci/*
6418c2ecf20Sopenharmony_ci * All these chips have at least 64 bytes of address space, shared by
6428c2ecf20Sopenharmony_ci * RTC registers and NVRAM.  Most of those bytes of NVRAM are used
6438c2ecf20Sopenharmony_ci * by boot firmware.  Modern chips have 128 or 256 bytes.
6448c2ecf20Sopenharmony_ci */
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci#define NVRAM_OFFSET	(RTC_REG_D + 1)
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_cistatic int cmos_nvram_read(void *priv, unsigned int off, void *val,
6498c2ecf20Sopenharmony_ci			   size_t count)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	unsigned char *buf = val;
6528c2ecf20Sopenharmony_ci	int	retval;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	off += NVRAM_OFFSET;
6558c2ecf20Sopenharmony_ci	spin_lock_irq(&rtc_lock);
6568c2ecf20Sopenharmony_ci	for (retval = 0; count; count--, off++, retval++) {
6578c2ecf20Sopenharmony_ci		if (off < 128)
6588c2ecf20Sopenharmony_ci			*buf++ = CMOS_READ(off);
6598c2ecf20Sopenharmony_ci		else if (can_bank2)
6608c2ecf20Sopenharmony_ci			*buf++ = cmos_read_bank2(off);
6618c2ecf20Sopenharmony_ci		else
6628c2ecf20Sopenharmony_ci			break;
6638c2ecf20Sopenharmony_ci	}
6648c2ecf20Sopenharmony_ci	spin_unlock_irq(&rtc_lock);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	return retval;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_cistatic int cmos_nvram_write(void *priv, unsigned int off, void *val,
6708c2ecf20Sopenharmony_ci			    size_t count)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	struct cmos_rtc	*cmos = priv;
6738c2ecf20Sopenharmony_ci	unsigned char	*buf = val;
6748c2ecf20Sopenharmony_ci	int		retval;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	/* NOTE:  on at least PCs and Ataris, the boot firmware uses a
6778c2ecf20Sopenharmony_ci	 * checksum on part of the NVRAM data.  That's currently ignored
6788c2ecf20Sopenharmony_ci	 * here.  If userspace is smart enough to know what fields of
6798c2ecf20Sopenharmony_ci	 * NVRAM to update, updating checksums is also part of its job.
6808c2ecf20Sopenharmony_ci	 */
6818c2ecf20Sopenharmony_ci	off += NVRAM_OFFSET;
6828c2ecf20Sopenharmony_ci	spin_lock_irq(&rtc_lock);
6838c2ecf20Sopenharmony_ci	for (retval = 0; count; count--, off++, retval++) {
6848c2ecf20Sopenharmony_ci		/* don't trash RTC registers */
6858c2ecf20Sopenharmony_ci		if (off == cmos->day_alrm
6868c2ecf20Sopenharmony_ci				|| off == cmos->mon_alrm
6878c2ecf20Sopenharmony_ci				|| off == cmos->century)
6888c2ecf20Sopenharmony_ci			buf++;
6898c2ecf20Sopenharmony_ci		else if (off < 128)
6908c2ecf20Sopenharmony_ci			CMOS_WRITE(*buf++, off);
6918c2ecf20Sopenharmony_ci		else if (can_bank2)
6928c2ecf20Sopenharmony_ci			cmos_write_bank2(*buf++, off);
6938c2ecf20Sopenharmony_ci		else
6948c2ecf20Sopenharmony_ci			break;
6958c2ecf20Sopenharmony_ci	}
6968c2ecf20Sopenharmony_ci	spin_unlock_irq(&rtc_lock);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	return retval;
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_cistatic struct cmos_rtc	cmos_rtc;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_cistatic irqreturn_t cmos_interrupt(int irq, void *p)
7068c2ecf20Sopenharmony_ci{
7078c2ecf20Sopenharmony_ci	u8		irqstat;
7088c2ecf20Sopenharmony_ci	u8		rtc_control;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	spin_lock(&rtc_lock);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	/* When the HPET interrupt handler calls us, the interrupt
7138c2ecf20Sopenharmony_ci	 * status is passed as arg1 instead of the irq number.  But
7148c2ecf20Sopenharmony_ci	 * always clear irq status, even when HPET is in the way.
7158c2ecf20Sopenharmony_ci	 *
7168c2ecf20Sopenharmony_ci	 * Note that HPET and RTC are almost certainly out of phase,
7178c2ecf20Sopenharmony_ci	 * giving different IRQ status ...
7188c2ecf20Sopenharmony_ci	 */
7198c2ecf20Sopenharmony_ci	irqstat = CMOS_READ(RTC_INTR_FLAGS);
7208c2ecf20Sopenharmony_ci	rtc_control = CMOS_READ(RTC_CONTROL);
7218c2ecf20Sopenharmony_ci	if (use_hpet_alarm())
7228c2ecf20Sopenharmony_ci		irqstat = (unsigned long)irq & 0xF0;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	/* If we were suspended, RTC_CONTROL may not be accurate since the
7258c2ecf20Sopenharmony_ci	 * bios may have cleared it.
7268c2ecf20Sopenharmony_ci	 */
7278c2ecf20Sopenharmony_ci	if (!cmos_rtc.suspend_ctrl)
7288c2ecf20Sopenharmony_ci		irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
7298c2ecf20Sopenharmony_ci	else
7308c2ecf20Sopenharmony_ci		irqstat &= (cmos_rtc.suspend_ctrl & RTC_IRQMASK) | RTC_IRQF;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	/* All Linux RTC alarms should be treated as if they were oneshot.
7338c2ecf20Sopenharmony_ci	 * Similar code may be needed in system wakeup paths, in case the
7348c2ecf20Sopenharmony_ci	 * alarm woke the system.
7358c2ecf20Sopenharmony_ci	 */
7368c2ecf20Sopenharmony_ci	if (irqstat & RTC_AIE) {
7378c2ecf20Sopenharmony_ci		cmos_rtc.suspend_ctrl &= ~RTC_AIE;
7388c2ecf20Sopenharmony_ci		rtc_control &= ~RTC_AIE;
7398c2ecf20Sopenharmony_ci		CMOS_WRITE(rtc_control, RTC_CONTROL);
7408c2ecf20Sopenharmony_ci		if (use_hpet_alarm())
7418c2ecf20Sopenharmony_ci			hpet_mask_rtc_irq_bit(RTC_AIE);
7428c2ecf20Sopenharmony_ci		CMOS_READ(RTC_INTR_FLAGS);
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci	spin_unlock(&rtc_lock);
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	if (is_intr(irqstat)) {
7478c2ecf20Sopenharmony_ci		rtc_update_irq(p, 1, irqstat);
7488c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
7498c2ecf20Sopenharmony_ci	} else
7508c2ecf20Sopenharmony_ci		return IRQ_NONE;
7518c2ecf20Sopenharmony_ci}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci#ifdef	CONFIG_ACPI
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci#include <linux/acpi.h>
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cistatic u32 rtc_handler(void *context)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	struct device *dev = context;
7608c2ecf20Sopenharmony_ci	struct cmos_rtc *cmos = dev_get_drvdata(dev);
7618c2ecf20Sopenharmony_ci	unsigned char rtc_control = 0;
7628c2ecf20Sopenharmony_ci	unsigned char rtc_intr;
7638c2ecf20Sopenharmony_ci	unsigned long flags;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	/*
7678c2ecf20Sopenharmony_ci	 * Always update rtc irq when ACPI is used as RTC Alarm.
7688c2ecf20Sopenharmony_ci	 * Or else, ACPI SCI is enabled during suspend/resume only,
7698c2ecf20Sopenharmony_ci	 * update rtc irq in that case.
7708c2ecf20Sopenharmony_ci	 */
7718c2ecf20Sopenharmony_ci	if (cmos_use_acpi_alarm())
7728c2ecf20Sopenharmony_ci		cmos_interrupt(0, (void *)cmos->rtc);
7738c2ecf20Sopenharmony_ci	else {
7748c2ecf20Sopenharmony_ci		/* Fix me: can we use cmos_interrupt() here as well? */
7758c2ecf20Sopenharmony_ci		spin_lock_irqsave(&rtc_lock, flags);
7768c2ecf20Sopenharmony_ci		if (cmos_rtc.suspend_ctrl)
7778c2ecf20Sopenharmony_ci			rtc_control = CMOS_READ(RTC_CONTROL);
7788c2ecf20Sopenharmony_ci		if (rtc_control & RTC_AIE) {
7798c2ecf20Sopenharmony_ci			cmos_rtc.suspend_ctrl &= ~RTC_AIE;
7808c2ecf20Sopenharmony_ci			CMOS_WRITE(rtc_control, RTC_CONTROL);
7818c2ecf20Sopenharmony_ci			rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
7828c2ecf20Sopenharmony_ci			rtc_update_irq(cmos->rtc, 1, rtc_intr);
7838c2ecf20Sopenharmony_ci		}
7848c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&rtc_lock, flags);
7858c2ecf20Sopenharmony_ci	}
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	pm_wakeup_hard_event(dev);
7888c2ecf20Sopenharmony_ci	acpi_clear_event(ACPI_EVENT_RTC);
7898c2ecf20Sopenharmony_ci	acpi_disable_event(ACPI_EVENT_RTC, 0);
7908c2ecf20Sopenharmony_ci	return ACPI_INTERRUPT_HANDLED;
7918c2ecf20Sopenharmony_ci}
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_cistatic void acpi_rtc_event_setup(struct device *dev)
7948c2ecf20Sopenharmony_ci{
7958c2ecf20Sopenharmony_ci	if (acpi_disabled)
7968c2ecf20Sopenharmony_ci		return;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, dev);
7998c2ecf20Sopenharmony_ci	/*
8008c2ecf20Sopenharmony_ci	 * After the RTC handler is installed, the Fixed_RTC event should
8018c2ecf20Sopenharmony_ci	 * be disabled. Only when the RTC alarm is set will it be enabled.
8028c2ecf20Sopenharmony_ci	 */
8038c2ecf20Sopenharmony_ci	acpi_clear_event(ACPI_EVENT_RTC);
8048c2ecf20Sopenharmony_ci	acpi_disable_event(ACPI_EVENT_RTC, 0);
8058c2ecf20Sopenharmony_ci}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_cistatic void acpi_rtc_event_cleanup(void)
8088c2ecf20Sopenharmony_ci{
8098c2ecf20Sopenharmony_ci	if (acpi_disabled)
8108c2ecf20Sopenharmony_ci		return;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	acpi_remove_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler);
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_cistatic void rtc_wake_on(struct device *dev)
8168c2ecf20Sopenharmony_ci{
8178c2ecf20Sopenharmony_ci	acpi_clear_event(ACPI_EVENT_RTC);
8188c2ecf20Sopenharmony_ci	acpi_enable_event(ACPI_EVENT_RTC, 0);
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_cistatic void rtc_wake_off(struct device *dev)
8228c2ecf20Sopenharmony_ci{
8238c2ecf20Sopenharmony_ci	acpi_disable_event(ACPI_EVENT_RTC, 0);
8248c2ecf20Sopenharmony_ci}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci#ifdef CONFIG_X86
8278c2ecf20Sopenharmony_ci/* Enable use_acpi_alarm mode for Intel platforms no earlier than 2015 */
8288c2ecf20Sopenharmony_cistatic void use_acpi_alarm_quirks(void)
8298c2ecf20Sopenharmony_ci{
8308c2ecf20Sopenharmony_ci	if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
8318c2ecf20Sopenharmony_ci		return;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	if (!is_hpet_enabled())
8348c2ecf20Sopenharmony_ci		return;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	if (dmi_get_bios_year() < 2015)
8378c2ecf20Sopenharmony_ci		return;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	use_acpi_alarm = true;
8408c2ecf20Sopenharmony_ci}
8418c2ecf20Sopenharmony_ci#else
8428c2ecf20Sopenharmony_cistatic inline void use_acpi_alarm_quirks(void) { }
8438c2ecf20Sopenharmony_ci#endif
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_cistatic void acpi_cmos_wake_setup(struct device *dev)
8468c2ecf20Sopenharmony_ci{
8478c2ecf20Sopenharmony_ci	if (acpi_disabled)
8488c2ecf20Sopenharmony_ci		return;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	use_acpi_alarm_quirks();
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	cmos_rtc.wake_on = rtc_wake_on;
8538c2ecf20Sopenharmony_ci	cmos_rtc.wake_off = rtc_wake_off;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	/* ACPI tables bug workaround. */
8568c2ecf20Sopenharmony_ci	if (acpi_gbl_FADT.month_alarm && !acpi_gbl_FADT.day_alarm) {
8578c2ecf20Sopenharmony_ci		dev_dbg(dev, "bogus FADT month_alarm (%d)\n",
8588c2ecf20Sopenharmony_ci			acpi_gbl_FADT.month_alarm);
8598c2ecf20Sopenharmony_ci		acpi_gbl_FADT.month_alarm = 0;
8608c2ecf20Sopenharmony_ci	}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	cmos_rtc.day_alrm = acpi_gbl_FADT.day_alarm;
8638c2ecf20Sopenharmony_ci	cmos_rtc.mon_alrm = acpi_gbl_FADT.month_alarm;
8648c2ecf20Sopenharmony_ci	cmos_rtc.century = acpi_gbl_FADT.century;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	if (acpi_gbl_FADT.flags & ACPI_FADT_S4_RTC_WAKE)
8678c2ecf20Sopenharmony_ci		dev_info(dev, "RTC can wake from S4\n");
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	/* RTC always wakes from S1/S2/S3, and often S4/STD */
8708c2ecf20Sopenharmony_ci	device_init_wakeup(dev, 1);
8718c2ecf20Sopenharmony_ci}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_cistatic void cmos_check_acpi_rtc_status(struct device *dev,
8748c2ecf20Sopenharmony_ci					      unsigned char *rtc_control)
8758c2ecf20Sopenharmony_ci{
8768c2ecf20Sopenharmony_ci	struct cmos_rtc *cmos = dev_get_drvdata(dev);
8778c2ecf20Sopenharmony_ci	acpi_event_status rtc_status;
8788c2ecf20Sopenharmony_ci	acpi_status status;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	if (acpi_gbl_FADT.flags & ACPI_FADT_FIXED_RTC)
8818c2ecf20Sopenharmony_ci		return;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	status = acpi_get_event_status(ACPI_EVENT_RTC, &rtc_status);
8848c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
8858c2ecf20Sopenharmony_ci		dev_err(dev, "Could not get RTC status\n");
8868c2ecf20Sopenharmony_ci	} else if (rtc_status & ACPI_EVENT_FLAG_SET) {
8878c2ecf20Sopenharmony_ci		unsigned char mask;
8888c2ecf20Sopenharmony_ci		*rtc_control &= ~RTC_AIE;
8898c2ecf20Sopenharmony_ci		CMOS_WRITE(*rtc_control, RTC_CONTROL);
8908c2ecf20Sopenharmony_ci		mask = CMOS_READ(RTC_INTR_FLAGS);
8918c2ecf20Sopenharmony_ci		rtc_update_irq(cmos->rtc, 1, mask);
8928c2ecf20Sopenharmony_ci	}
8938c2ecf20Sopenharmony_ci}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci#else /* !CONFIG_ACPI */
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_cistatic inline void acpi_rtc_event_setup(struct device *dev)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_cistatic inline void acpi_rtc_event_cleanup(void)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_cistatic inline void acpi_cmos_wake_setup(struct device *dev)
9068c2ecf20Sopenharmony_ci{
9078c2ecf20Sopenharmony_ci}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_cistatic inline void cmos_check_acpi_rtc_status(struct device *dev,
9108c2ecf20Sopenharmony_ci					      unsigned char *rtc_control)
9118c2ecf20Sopenharmony_ci{
9128c2ecf20Sopenharmony_ci}
9138c2ecf20Sopenharmony_ci#endif /* CONFIG_ACPI */
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci#ifdef	CONFIG_PNP
9168c2ecf20Sopenharmony_ci#define	INITSECTION
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci#else
9198c2ecf20Sopenharmony_ci#define	INITSECTION	__init
9208c2ecf20Sopenharmony_ci#endif
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_cistatic int INITSECTION
9238c2ecf20Sopenharmony_cicmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
9248c2ecf20Sopenharmony_ci{
9258c2ecf20Sopenharmony_ci	struct cmos_rtc_board_info	*info = dev_get_platdata(dev);
9268c2ecf20Sopenharmony_ci	int				retval = 0;
9278c2ecf20Sopenharmony_ci	unsigned char			rtc_control;
9288c2ecf20Sopenharmony_ci	unsigned			address_space;
9298c2ecf20Sopenharmony_ci	u32				flags = 0;
9308c2ecf20Sopenharmony_ci	struct nvmem_config nvmem_cfg = {
9318c2ecf20Sopenharmony_ci		.name = "cmos_nvram",
9328c2ecf20Sopenharmony_ci		.word_size = 1,
9338c2ecf20Sopenharmony_ci		.stride = 1,
9348c2ecf20Sopenharmony_ci		.reg_read = cmos_nvram_read,
9358c2ecf20Sopenharmony_ci		.reg_write = cmos_nvram_write,
9368c2ecf20Sopenharmony_ci		.priv = &cmos_rtc,
9378c2ecf20Sopenharmony_ci	};
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	/* there can be only one ... */
9408c2ecf20Sopenharmony_ci	if (cmos_rtc.dev)
9418c2ecf20Sopenharmony_ci		return -EBUSY;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	if (!ports)
9448c2ecf20Sopenharmony_ci		return -ENODEV;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	/* Claim I/O ports ASAP, minimizing conflict with legacy driver.
9478c2ecf20Sopenharmony_ci	 *
9488c2ecf20Sopenharmony_ci	 * REVISIT non-x86 systems may instead use memory space resources
9498c2ecf20Sopenharmony_ci	 * (needing ioremap etc), not i/o space resources like this ...
9508c2ecf20Sopenharmony_ci	 */
9518c2ecf20Sopenharmony_ci	if (RTC_IOMAPPED)
9528c2ecf20Sopenharmony_ci		ports = request_region(ports->start, resource_size(ports),
9538c2ecf20Sopenharmony_ci				       driver_name);
9548c2ecf20Sopenharmony_ci	else
9558c2ecf20Sopenharmony_ci		ports = request_mem_region(ports->start, resource_size(ports),
9568c2ecf20Sopenharmony_ci					   driver_name);
9578c2ecf20Sopenharmony_ci	if (!ports) {
9588c2ecf20Sopenharmony_ci		dev_dbg(dev, "i/o registers already in use\n");
9598c2ecf20Sopenharmony_ci		return -EBUSY;
9608c2ecf20Sopenharmony_ci	}
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	cmos_rtc.irq = rtc_irq;
9638c2ecf20Sopenharmony_ci	cmos_rtc.iomem = ports;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	/* Heuristic to deduce NVRAM size ... do what the legacy NVRAM
9668c2ecf20Sopenharmony_ci	 * driver did, but don't reject unknown configs.   Old hardware
9678c2ecf20Sopenharmony_ci	 * won't address 128 bytes.  Newer chips have multiple banks,
9688c2ecf20Sopenharmony_ci	 * though they may not be listed in one I/O resource.
9698c2ecf20Sopenharmony_ci	 */
9708c2ecf20Sopenharmony_ci#if	defined(CONFIG_ATARI)
9718c2ecf20Sopenharmony_ci	address_space = 64;
9728c2ecf20Sopenharmony_ci#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) \
9738c2ecf20Sopenharmony_ci			|| defined(__sparc__) || defined(__mips__) \
9748c2ecf20Sopenharmony_ci			|| defined(__powerpc__)
9758c2ecf20Sopenharmony_ci	address_space = 128;
9768c2ecf20Sopenharmony_ci#else
9778c2ecf20Sopenharmony_ci#warning Assuming 128 bytes of RTC+NVRAM address space, not 64 bytes.
9788c2ecf20Sopenharmony_ci	address_space = 128;
9798c2ecf20Sopenharmony_ci#endif
9808c2ecf20Sopenharmony_ci	if (can_bank2 && ports->end > (ports->start + 1))
9818c2ecf20Sopenharmony_ci		address_space = 256;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	/* For ACPI systems extension info comes from the FADT.  On others,
9848c2ecf20Sopenharmony_ci	 * board specific setup provides it as appropriate.  Systems where
9858c2ecf20Sopenharmony_ci	 * the alarm IRQ isn't automatically a wakeup IRQ (like ACPI, and
9868c2ecf20Sopenharmony_ci	 * some almost-clones) can provide hooks to make that behave.
9878c2ecf20Sopenharmony_ci	 *
9888c2ecf20Sopenharmony_ci	 * Note that ACPI doesn't preclude putting these registers into
9898c2ecf20Sopenharmony_ci	 * "extended" areas of the chip, including some that we won't yet
9908c2ecf20Sopenharmony_ci	 * expect CMOS_READ and friends to handle.
9918c2ecf20Sopenharmony_ci	 */
9928c2ecf20Sopenharmony_ci	if (info) {
9938c2ecf20Sopenharmony_ci		if (info->flags)
9948c2ecf20Sopenharmony_ci			flags = info->flags;
9958c2ecf20Sopenharmony_ci		if (info->address_space)
9968c2ecf20Sopenharmony_ci			address_space = info->address_space;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci		cmos_rtc.day_alrm = info->rtc_day_alarm;
9998c2ecf20Sopenharmony_ci		cmos_rtc.mon_alrm = info->rtc_mon_alarm;
10008c2ecf20Sopenharmony_ci		cmos_rtc.century = info->rtc_century;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci		if (info->wake_on && info->wake_off) {
10038c2ecf20Sopenharmony_ci			cmos_rtc.wake_on = info->wake_on;
10048c2ecf20Sopenharmony_ci			cmos_rtc.wake_off = info->wake_off;
10058c2ecf20Sopenharmony_ci		}
10068c2ecf20Sopenharmony_ci	} else {
10078c2ecf20Sopenharmony_ci		acpi_cmos_wake_setup(dev);
10088c2ecf20Sopenharmony_ci	}
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	if (cmos_rtc.day_alrm >= 128)
10118c2ecf20Sopenharmony_ci		cmos_rtc.day_alrm = 0;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	if (cmos_rtc.mon_alrm >= 128)
10148c2ecf20Sopenharmony_ci		cmos_rtc.mon_alrm = 0;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	if (cmos_rtc.century >= 128)
10178c2ecf20Sopenharmony_ci		cmos_rtc.century = 0;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	cmos_rtc.dev = dev;
10208c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, &cmos_rtc);
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	cmos_rtc.rtc = devm_rtc_allocate_device(dev);
10238c2ecf20Sopenharmony_ci	if (IS_ERR(cmos_rtc.rtc)) {
10248c2ecf20Sopenharmony_ci		retval = PTR_ERR(cmos_rtc.rtc);
10258c2ecf20Sopenharmony_ci		goto cleanup0;
10268c2ecf20Sopenharmony_ci	}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	rename_region(ports, dev_name(&cmos_rtc.rtc->dev));
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	if (!mc146818_does_rtc_work()) {
10318c2ecf20Sopenharmony_ci		dev_warn(dev, "broken or not accessible\n");
10328c2ecf20Sopenharmony_ci		retval = -ENXIO;
10338c2ecf20Sopenharmony_ci		goto cleanup1;
10348c2ecf20Sopenharmony_ci	}
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	spin_lock_irq(&rtc_lock);
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	if (!(flags & CMOS_RTC_FLAGS_NOFREQ)) {
10398c2ecf20Sopenharmony_ci		/* force periodic irq to CMOS reset default of 1024Hz;
10408c2ecf20Sopenharmony_ci		 *
10418c2ecf20Sopenharmony_ci		 * REVISIT it's been reported that at least one x86_64 ALI
10428c2ecf20Sopenharmony_ci		 * mobo doesn't use 32KHz here ... for portability we might
10438c2ecf20Sopenharmony_ci		 * need to do something about other clock frequencies.
10448c2ecf20Sopenharmony_ci		 */
10458c2ecf20Sopenharmony_ci		cmos_rtc.rtc->irq_freq = 1024;
10468c2ecf20Sopenharmony_ci		if (use_hpet_alarm())
10478c2ecf20Sopenharmony_ci			hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq);
10488c2ecf20Sopenharmony_ci		CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT);
10498c2ecf20Sopenharmony_ci	}
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	/* disable irqs */
10528c2ecf20Sopenharmony_ci	if (is_valid_irq(rtc_irq))
10538c2ecf20Sopenharmony_ci		cmos_irq_disable(&cmos_rtc, RTC_PIE | RTC_AIE | RTC_UIE);
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	rtc_control = CMOS_READ(RTC_CONTROL);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	spin_unlock_irq(&rtc_lock);
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) {
10608c2ecf20Sopenharmony_ci		dev_warn(dev, "only 24-hr supported\n");
10618c2ecf20Sopenharmony_ci		retval = -ENXIO;
10628c2ecf20Sopenharmony_ci		goto cleanup1;
10638c2ecf20Sopenharmony_ci	}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	if (use_hpet_alarm())
10668c2ecf20Sopenharmony_ci		hpet_rtc_timer_init();
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	if (is_valid_irq(rtc_irq)) {
10698c2ecf20Sopenharmony_ci		irq_handler_t rtc_cmos_int_handler;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci		if (use_hpet_alarm()) {
10728c2ecf20Sopenharmony_ci			rtc_cmos_int_handler = hpet_rtc_interrupt;
10738c2ecf20Sopenharmony_ci			retval = hpet_register_irq_handler(cmos_interrupt);
10748c2ecf20Sopenharmony_ci			if (retval) {
10758c2ecf20Sopenharmony_ci				hpet_mask_rtc_irq_bit(RTC_IRQMASK);
10768c2ecf20Sopenharmony_ci				dev_warn(dev, "hpet_register_irq_handler "
10778c2ecf20Sopenharmony_ci						" failed in rtc_init().");
10788c2ecf20Sopenharmony_ci				goto cleanup1;
10798c2ecf20Sopenharmony_ci			}
10808c2ecf20Sopenharmony_ci		} else
10818c2ecf20Sopenharmony_ci			rtc_cmos_int_handler = cmos_interrupt;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci		retval = request_irq(rtc_irq, rtc_cmos_int_handler,
10848c2ecf20Sopenharmony_ci				0, dev_name(&cmos_rtc.rtc->dev),
10858c2ecf20Sopenharmony_ci				cmos_rtc.rtc);
10868c2ecf20Sopenharmony_ci		if (retval < 0) {
10878c2ecf20Sopenharmony_ci			dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq);
10888c2ecf20Sopenharmony_ci			goto cleanup1;
10898c2ecf20Sopenharmony_ci		}
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci		cmos_rtc.rtc->ops = &cmos_rtc_ops;
10928c2ecf20Sopenharmony_ci	} else {
10938c2ecf20Sopenharmony_ci		cmos_rtc.rtc->ops = &cmos_rtc_ops_no_alarm;
10948c2ecf20Sopenharmony_ci	}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	cmos_rtc.rtc->nvram_old_abi = true;
10978c2ecf20Sopenharmony_ci	retval = rtc_register_device(cmos_rtc.rtc);
10988c2ecf20Sopenharmony_ci	if (retval)
10998c2ecf20Sopenharmony_ci		goto cleanup2;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	/* export at least the first block of NVRAM */
11028c2ecf20Sopenharmony_ci	nvmem_cfg.size = address_space - NVRAM_OFFSET;
11038c2ecf20Sopenharmony_ci	if (rtc_nvmem_register(cmos_rtc.rtc, &nvmem_cfg))
11048c2ecf20Sopenharmony_ci		dev_err(dev, "nvmem registration failed\n");
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	/*
11078c2ecf20Sopenharmony_ci	 * Everything has gone well so far, so by default register a handler for
11088c2ecf20Sopenharmony_ci	 * the ACPI RTC fixed event.
11098c2ecf20Sopenharmony_ci	 */
11108c2ecf20Sopenharmony_ci	if (!info)
11118c2ecf20Sopenharmony_ci		acpi_rtc_event_setup(dev);
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	dev_info(dev, "%s%s, %d bytes nvram%s\n",
11148c2ecf20Sopenharmony_ci		 !is_valid_irq(rtc_irq) ? "no alarms" :
11158c2ecf20Sopenharmony_ci		 cmos_rtc.mon_alrm ? "alarms up to one year" :
11168c2ecf20Sopenharmony_ci		 cmos_rtc.day_alrm ? "alarms up to one month" :
11178c2ecf20Sopenharmony_ci		 "alarms up to one day",
11188c2ecf20Sopenharmony_ci		 cmos_rtc.century ? ", y3k" : "",
11198c2ecf20Sopenharmony_ci		 nvmem_cfg.size,
11208c2ecf20Sopenharmony_ci		 use_hpet_alarm() ? ", hpet irqs" : "");
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	return 0;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_cicleanup2:
11258c2ecf20Sopenharmony_ci	if (is_valid_irq(rtc_irq))
11268c2ecf20Sopenharmony_ci		free_irq(rtc_irq, cmos_rtc.rtc);
11278c2ecf20Sopenharmony_cicleanup1:
11288c2ecf20Sopenharmony_ci	cmos_rtc.dev = NULL;
11298c2ecf20Sopenharmony_cicleanup0:
11308c2ecf20Sopenharmony_ci	if (RTC_IOMAPPED)
11318c2ecf20Sopenharmony_ci		release_region(ports->start, resource_size(ports));
11328c2ecf20Sopenharmony_ci	else
11338c2ecf20Sopenharmony_ci		release_mem_region(ports->start, resource_size(ports));
11348c2ecf20Sopenharmony_ci	return retval;
11358c2ecf20Sopenharmony_ci}
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_cistatic void cmos_do_shutdown(int rtc_irq)
11388c2ecf20Sopenharmony_ci{
11398c2ecf20Sopenharmony_ci	spin_lock_irq(&rtc_lock);
11408c2ecf20Sopenharmony_ci	if (is_valid_irq(rtc_irq))
11418c2ecf20Sopenharmony_ci		cmos_irq_disable(&cmos_rtc, RTC_IRQMASK);
11428c2ecf20Sopenharmony_ci	spin_unlock_irq(&rtc_lock);
11438c2ecf20Sopenharmony_ci}
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_cistatic void cmos_do_remove(struct device *dev)
11468c2ecf20Sopenharmony_ci{
11478c2ecf20Sopenharmony_ci	struct cmos_rtc	*cmos = dev_get_drvdata(dev);
11488c2ecf20Sopenharmony_ci	struct resource *ports;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	cmos_do_shutdown(cmos->irq);
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	if (is_valid_irq(cmos->irq)) {
11538c2ecf20Sopenharmony_ci		free_irq(cmos->irq, cmos->rtc);
11548c2ecf20Sopenharmony_ci		if (use_hpet_alarm())
11558c2ecf20Sopenharmony_ci			hpet_unregister_irq_handler(cmos_interrupt);
11568c2ecf20Sopenharmony_ci	}
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	if (!dev_get_platdata(dev))
11598c2ecf20Sopenharmony_ci		acpi_rtc_event_cleanup();
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	cmos->rtc = NULL;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	ports = cmos->iomem;
11648c2ecf20Sopenharmony_ci	if (RTC_IOMAPPED)
11658c2ecf20Sopenharmony_ci		release_region(ports->start, resource_size(ports));
11668c2ecf20Sopenharmony_ci	else
11678c2ecf20Sopenharmony_ci		release_mem_region(ports->start, resource_size(ports));
11688c2ecf20Sopenharmony_ci	cmos->iomem = NULL;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	cmos->dev = NULL;
11718c2ecf20Sopenharmony_ci}
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_cistatic int cmos_aie_poweroff(struct device *dev)
11748c2ecf20Sopenharmony_ci{
11758c2ecf20Sopenharmony_ci	struct cmos_rtc	*cmos = dev_get_drvdata(dev);
11768c2ecf20Sopenharmony_ci	struct rtc_time now;
11778c2ecf20Sopenharmony_ci	time64_t t_now;
11788c2ecf20Sopenharmony_ci	int retval = 0;
11798c2ecf20Sopenharmony_ci	unsigned char rtc_control;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	if (!cmos->alarm_expires)
11828c2ecf20Sopenharmony_ci		return -EINVAL;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	spin_lock_irq(&rtc_lock);
11858c2ecf20Sopenharmony_ci	rtc_control = CMOS_READ(RTC_CONTROL);
11868c2ecf20Sopenharmony_ci	spin_unlock_irq(&rtc_lock);
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	/* We only care about the situation where AIE is disabled. */
11898c2ecf20Sopenharmony_ci	if (rtc_control & RTC_AIE)
11908c2ecf20Sopenharmony_ci		return -EBUSY;
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	cmos_read_time(dev, &now);
11938c2ecf20Sopenharmony_ci	t_now = rtc_tm_to_time64(&now);
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	/*
11968c2ecf20Sopenharmony_ci	 * When enabling "RTC wake-up" in BIOS setup, the machine reboots
11978c2ecf20Sopenharmony_ci	 * automatically right after shutdown on some buggy boxes.
11988c2ecf20Sopenharmony_ci	 * This automatic rebooting issue won't happen when the alarm
11998c2ecf20Sopenharmony_ci	 * time is larger than now+1 seconds.
12008c2ecf20Sopenharmony_ci	 *
12018c2ecf20Sopenharmony_ci	 * If the alarm time is equal to now+1 seconds, the issue can be
12028c2ecf20Sopenharmony_ci	 * prevented by cancelling the alarm.
12038c2ecf20Sopenharmony_ci	 */
12048c2ecf20Sopenharmony_ci	if (cmos->alarm_expires == t_now + 1) {
12058c2ecf20Sopenharmony_ci		struct rtc_wkalrm alarm;
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci		/* Cancel the AIE timer by configuring the past time. */
12088c2ecf20Sopenharmony_ci		rtc_time64_to_tm(t_now - 1, &alarm.time);
12098c2ecf20Sopenharmony_ci		alarm.enabled = 0;
12108c2ecf20Sopenharmony_ci		retval = cmos_set_alarm(dev, &alarm);
12118c2ecf20Sopenharmony_ci	} else if (cmos->alarm_expires > t_now + 1) {
12128c2ecf20Sopenharmony_ci		retval = -EBUSY;
12138c2ecf20Sopenharmony_ci	}
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	return retval;
12168c2ecf20Sopenharmony_ci}
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_cistatic int cmos_suspend(struct device *dev)
12198c2ecf20Sopenharmony_ci{
12208c2ecf20Sopenharmony_ci	struct cmos_rtc	*cmos = dev_get_drvdata(dev);
12218c2ecf20Sopenharmony_ci	unsigned char	tmp;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	/* only the alarm might be a wakeup event source */
12248c2ecf20Sopenharmony_ci	spin_lock_irq(&rtc_lock);
12258c2ecf20Sopenharmony_ci	cmos->suspend_ctrl = tmp = CMOS_READ(RTC_CONTROL);
12268c2ecf20Sopenharmony_ci	if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) {
12278c2ecf20Sopenharmony_ci		unsigned char	mask;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci		if (device_may_wakeup(dev))
12308c2ecf20Sopenharmony_ci			mask = RTC_IRQMASK & ~RTC_AIE;
12318c2ecf20Sopenharmony_ci		else
12328c2ecf20Sopenharmony_ci			mask = RTC_IRQMASK;
12338c2ecf20Sopenharmony_ci		tmp &= ~mask;
12348c2ecf20Sopenharmony_ci		CMOS_WRITE(tmp, RTC_CONTROL);
12358c2ecf20Sopenharmony_ci		if (use_hpet_alarm())
12368c2ecf20Sopenharmony_ci			hpet_mask_rtc_irq_bit(mask);
12378c2ecf20Sopenharmony_ci		cmos_checkintr(cmos, tmp);
12388c2ecf20Sopenharmony_ci	}
12398c2ecf20Sopenharmony_ci	spin_unlock_irq(&rtc_lock);
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	if ((tmp & RTC_AIE) && !cmos_use_acpi_alarm()) {
12428c2ecf20Sopenharmony_ci		cmos->enabled_wake = 1;
12438c2ecf20Sopenharmony_ci		if (cmos->wake_on)
12448c2ecf20Sopenharmony_ci			cmos->wake_on(dev);
12458c2ecf20Sopenharmony_ci		else
12468c2ecf20Sopenharmony_ci			enable_irq_wake(cmos->irq);
12478c2ecf20Sopenharmony_ci	}
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	memset(&cmos->saved_wkalrm, 0, sizeof(struct rtc_wkalrm));
12508c2ecf20Sopenharmony_ci	cmos_read_alarm(dev, &cmos->saved_wkalrm);
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	dev_dbg(dev, "suspend%s, ctrl %02x\n",
12538c2ecf20Sopenharmony_ci			(tmp & RTC_AIE) ? ", alarm may wake" : "",
12548c2ecf20Sopenharmony_ci			tmp);
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	return 0;
12578c2ecf20Sopenharmony_ci}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci/* We want RTC alarms to wake us from e.g. ACPI G2/S5 "soft off", even
12608c2ecf20Sopenharmony_ci * after a detour through G3 "mechanical off", although the ACPI spec
12618c2ecf20Sopenharmony_ci * says wakeup should only work from G1/S4 "hibernate".  To most users,
12628c2ecf20Sopenharmony_ci * distinctions between S4 and S5 are pointless.  So when the hardware
12638c2ecf20Sopenharmony_ci * allows, don't draw that distinction.
12648c2ecf20Sopenharmony_ci */
12658c2ecf20Sopenharmony_cistatic inline int cmos_poweroff(struct device *dev)
12668c2ecf20Sopenharmony_ci{
12678c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_PM))
12688c2ecf20Sopenharmony_ci		return -ENOSYS;
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	return cmos_suspend(dev);
12718c2ecf20Sopenharmony_ci}
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_cistatic void cmos_check_wkalrm(struct device *dev)
12748c2ecf20Sopenharmony_ci{
12758c2ecf20Sopenharmony_ci	struct cmos_rtc *cmos = dev_get_drvdata(dev);
12768c2ecf20Sopenharmony_ci	struct rtc_wkalrm current_alarm;
12778c2ecf20Sopenharmony_ci	time64_t t_now;
12788c2ecf20Sopenharmony_ci	time64_t t_current_expires;
12798c2ecf20Sopenharmony_ci	time64_t t_saved_expires;
12808c2ecf20Sopenharmony_ci	struct rtc_time now;
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	/* Check if we have RTC Alarm armed */
12838c2ecf20Sopenharmony_ci	if (!(cmos->suspend_ctrl & RTC_AIE))
12848c2ecf20Sopenharmony_ci		return;
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	cmos_read_time(dev, &now);
12878c2ecf20Sopenharmony_ci	t_now = rtc_tm_to_time64(&now);
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	/*
12908c2ecf20Sopenharmony_ci	 * ACPI RTC wake event is cleared after resume from STR,
12918c2ecf20Sopenharmony_ci	 * ACK the rtc irq here
12928c2ecf20Sopenharmony_ci	 */
12938c2ecf20Sopenharmony_ci	if (t_now >= cmos->alarm_expires && cmos_use_acpi_alarm()) {
12948c2ecf20Sopenharmony_ci		local_irq_disable();
12958c2ecf20Sopenharmony_ci		cmos_interrupt(0, (void *)cmos->rtc);
12968c2ecf20Sopenharmony_ci		local_irq_enable();
12978c2ecf20Sopenharmony_ci		return;
12988c2ecf20Sopenharmony_ci	}
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	memset(&current_alarm, 0, sizeof(struct rtc_wkalrm));
13018c2ecf20Sopenharmony_ci	cmos_read_alarm(dev, &current_alarm);
13028c2ecf20Sopenharmony_ci	t_current_expires = rtc_tm_to_time64(&current_alarm.time);
13038c2ecf20Sopenharmony_ci	t_saved_expires = rtc_tm_to_time64(&cmos->saved_wkalrm.time);
13048c2ecf20Sopenharmony_ci	if (t_current_expires != t_saved_expires ||
13058c2ecf20Sopenharmony_ci	    cmos->saved_wkalrm.enabled != current_alarm.enabled) {
13068c2ecf20Sopenharmony_ci		cmos_set_alarm(dev, &cmos->saved_wkalrm);
13078c2ecf20Sopenharmony_ci	}
13088c2ecf20Sopenharmony_ci}
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_cistatic int __maybe_unused cmos_resume(struct device *dev)
13118c2ecf20Sopenharmony_ci{
13128c2ecf20Sopenharmony_ci	struct cmos_rtc	*cmos = dev_get_drvdata(dev);
13138c2ecf20Sopenharmony_ci	unsigned char tmp;
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	if (cmos->enabled_wake && !cmos_use_acpi_alarm()) {
13168c2ecf20Sopenharmony_ci		if (cmos->wake_off)
13178c2ecf20Sopenharmony_ci			cmos->wake_off(dev);
13188c2ecf20Sopenharmony_ci		else
13198c2ecf20Sopenharmony_ci			disable_irq_wake(cmos->irq);
13208c2ecf20Sopenharmony_ci		cmos->enabled_wake = 0;
13218c2ecf20Sopenharmony_ci	}
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	/* The BIOS might have changed the alarm, restore it */
13248c2ecf20Sopenharmony_ci	cmos_check_wkalrm(dev);
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	spin_lock_irq(&rtc_lock);
13278c2ecf20Sopenharmony_ci	tmp = cmos->suspend_ctrl;
13288c2ecf20Sopenharmony_ci	cmos->suspend_ctrl = 0;
13298c2ecf20Sopenharmony_ci	/* re-enable any irqs previously active */
13308c2ecf20Sopenharmony_ci	if (tmp & RTC_IRQMASK) {
13318c2ecf20Sopenharmony_ci		unsigned char	mask;
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci		if (device_may_wakeup(dev) && use_hpet_alarm())
13348c2ecf20Sopenharmony_ci			hpet_rtc_timer_init();
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci		do {
13378c2ecf20Sopenharmony_ci			CMOS_WRITE(tmp, RTC_CONTROL);
13388c2ecf20Sopenharmony_ci			if (use_hpet_alarm())
13398c2ecf20Sopenharmony_ci				hpet_set_rtc_irq_bit(tmp & RTC_IRQMASK);
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci			mask = CMOS_READ(RTC_INTR_FLAGS);
13428c2ecf20Sopenharmony_ci			mask &= (tmp & RTC_IRQMASK) | RTC_IRQF;
13438c2ecf20Sopenharmony_ci			if (!use_hpet_alarm() || !is_intr(mask))
13448c2ecf20Sopenharmony_ci				break;
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci			/* force one-shot behavior if HPET blocked
13478c2ecf20Sopenharmony_ci			 * the wake alarm's irq
13488c2ecf20Sopenharmony_ci			 */
13498c2ecf20Sopenharmony_ci			rtc_update_irq(cmos->rtc, 1, mask);
13508c2ecf20Sopenharmony_ci			tmp &= ~RTC_AIE;
13518c2ecf20Sopenharmony_ci			hpet_mask_rtc_irq_bit(RTC_AIE);
13528c2ecf20Sopenharmony_ci		} while (mask & RTC_AIE);
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci		if (tmp & RTC_AIE)
13558c2ecf20Sopenharmony_ci			cmos_check_acpi_rtc_status(dev, &tmp);
13568c2ecf20Sopenharmony_ci	}
13578c2ecf20Sopenharmony_ci	spin_unlock_irq(&rtc_lock);
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	dev_dbg(dev, "resume, ctrl %02x\n", tmp);
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	return 0;
13628c2ecf20Sopenharmony_ci}
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(cmos_pm_ops, cmos_suspend, cmos_resume);
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci/* On non-x86 systems, a "CMOS" RTC lives most naturally on platform_bus.
13698c2ecf20Sopenharmony_ci * ACPI systems always list these as PNPACPI devices, and pre-ACPI PCs
13708c2ecf20Sopenharmony_ci * probably list them in similar PNPBIOS tables; so PNP is more common.
13718c2ecf20Sopenharmony_ci *
13728c2ecf20Sopenharmony_ci * We don't use legacy "poke at the hardware" probing.  Ancient PCs that
13738c2ecf20Sopenharmony_ci * predate even PNPBIOS should set up platform_bus devices.
13748c2ecf20Sopenharmony_ci */
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci#ifdef	CONFIG_PNP
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci#include <linux/pnp.h>
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_cistatic int cmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
13818c2ecf20Sopenharmony_ci{
13828c2ecf20Sopenharmony_ci	int irq;
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	if (pnp_port_start(pnp, 0) == 0x70 && !pnp_irq_valid(pnp, 0)) {
13858c2ecf20Sopenharmony_ci		irq = 0;
13868c2ecf20Sopenharmony_ci#ifdef CONFIG_X86
13878c2ecf20Sopenharmony_ci		/* Some machines contain a PNP entry for the RTC, but
13888c2ecf20Sopenharmony_ci		 * don't define the IRQ. It should always be safe to
13898c2ecf20Sopenharmony_ci		 * hardcode it on systems with a legacy PIC.
13908c2ecf20Sopenharmony_ci		 */
13918c2ecf20Sopenharmony_ci		if (nr_legacy_irqs())
13928c2ecf20Sopenharmony_ci			irq = RTC_IRQ;
13938c2ecf20Sopenharmony_ci#endif
13948c2ecf20Sopenharmony_ci	} else {
13958c2ecf20Sopenharmony_ci		irq = pnp_irq(pnp, 0);
13968c2ecf20Sopenharmony_ci	}
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	return cmos_do_probe(&pnp->dev, pnp_get_resource(pnp, IORESOURCE_IO, 0), irq);
13998c2ecf20Sopenharmony_ci}
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_cistatic void cmos_pnp_remove(struct pnp_dev *pnp)
14028c2ecf20Sopenharmony_ci{
14038c2ecf20Sopenharmony_ci	cmos_do_remove(&pnp->dev);
14048c2ecf20Sopenharmony_ci}
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_cistatic void cmos_pnp_shutdown(struct pnp_dev *pnp)
14078c2ecf20Sopenharmony_ci{
14088c2ecf20Sopenharmony_ci	struct device *dev = &pnp->dev;
14098c2ecf20Sopenharmony_ci	struct cmos_rtc	*cmos = dev_get_drvdata(dev);
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	if (system_state == SYSTEM_POWER_OFF) {
14128c2ecf20Sopenharmony_ci		int retval = cmos_poweroff(dev);
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci		if (cmos_aie_poweroff(dev) < 0 && !retval)
14158c2ecf20Sopenharmony_ci			return;
14168c2ecf20Sopenharmony_ci	}
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	cmos_do_shutdown(cmos->irq);
14198c2ecf20Sopenharmony_ci}
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_cistatic const struct pnp_device_id rtc_ids[] = {
14228c2ecf20Sopenharmony_ci	{ .id = "PNP0b00", },
14238c2ecf20Sopenharmony_ci	{ .id = "PNP0b01", },
14248c2ecf20Sopenharmony_ci	{ .id = "PNP0b02", },
14258c2ecf20Sopenharmony_ci	{ },
14268c2ecf20Sopenharmony_ci};
14278c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pnp, rtc_ids);
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_cistatic struct pnp_driver cmos_pnp_driver = {
14308c2ecf20Sopenharmony_ci	.name		= driver_name,
14318c2ecf20Sopenharmony_ci	.id_table	= rtc_ids,
14328c2ecf20Sopenharmony_ci	.probe		= cmos_pnp_probe,
14338c2ecf20Sopenharmony_ci	.remove		= cmos_pnp_remove,
14348c2ecf20Sopenharmony_ci	.shutdown	= cmos_pnp_shutdown,
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	/* flag ensures resume() gets called, and stops syslog spam */
14378c2ecf20Sopenharmony_ci	.flags		= PNP_DRIVER_RES_DO_NOT_CHANGE,
14388c2ecf20Sopenharmony_ci	.driver		= {
14398c2ecf20Sopenharmony_ci			.pm = &cmos_pm_ops,
14408c2ecf20Sopenharmony_ci	},
14418c2ecf20Sopenharmony_ci};
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci#endif	/* CONFIG_PNP */
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
14468c2ecf20Sopenharmony_cistatic const struct of_device_id of_cmos_match[] = {
14478c2ecf20Sopenharmony_ci	{
14488c2ecf20Sopenharmony_ci		.compatible = "motorola,mc146818",
14498c2ecf20Sopenharmony_ci	},
14508c2ecf20Sopenharmony_ci	{ },
14518c2ecf20Sopenharmony_ci};
14528c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_cmos_match);
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_cistatic __init void cmos_of_init(struct platform_device *pdev)
14558c2ecf20Sopenharmony_ci{
14568c2ecf20Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
14578c2ecf20Sopenharmony_ci	const __be32 *val;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	if (!node)
14608c2ecf20Sopenharmony_ci		return;
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	val = of_get_property(node, "ctrl-reg", NULL);
14638c2ecf20Sopenharmony_ci	if (val)
14648c2ecf20Sopenharmony_ci		CMOS_WRITE(be32_to_cpup(val), RTC_CONTROL);
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	val = of_get_property(node, "freq-reg", NULL);
14678c2ecf20Sopenharmony_ci	if (val)
14688c2ecf20Sopenharmony_ci		CMOS_WRITE(be32_to_cpup(val), RTC_FREQ_SELECT);
14698c2ecf20Sopenharmony_ci}
14708c2ecf20Sopenharmony_ci#else
14718c2ecf20Sopenharmony_cistatic inline void cmos_of_init(struct platform_device *pdev) {}
14728c2ecf20Sopenharmony_ci#endif
14738c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci/* Platform setup should have set up an RTC device, when PNP is
14768c2ecf20Sopenharmony_ci * unavailable ... this could happen even on (older) PCs.
14778c2ecf20Sopenharmony_ci */
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_cistatic int __init cmos_platform_probe(struct platform_device *pdev)
14808c2ecf20Sopenharmony_ci{
14818c2ecf20Sopenharmony_ci	struct resource *resource;
14828c2ecf20Sopenharmony_ci	int irq;
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	cmos_of_init(pdev);
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci	if (RTC_IOMAPPED)
14878c2ecf20Sopenharmony_ci		resource = platform_get_resource(pdev, IORESOURCE_IO, 0);
14888c2ecf20Sopenharmony_ci	else
14898c2ecf20Sopenharmony_ci		resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
14908c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
14918c2ecf20Sopenharmony_ci	if (irq < 0)
14928c2ecf20Sopenharmony_ci		irq = -1;
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci	return cmos_do_probe(&pdev->dev, resource, irq);
14958c2ecf20Sopenharmony_ci}
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_cistatic int cmos_platform_remove(struct platform_device *pdev)
14988c2ecf20Sopenharmony_ci{
14998c2ecf20Sopenharmony_ci	cmos_do_remove(&pdev->dev);
15008c2ecf20Sopenharmony_ci	return 0;
15018c2ecf20Sopenharmony_ci}
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_cistatic void cmos_platform_shutdown(struct platform_device *pdev)
15048c2ecf20Sopenharmony_ci{
15058c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
15068c2ecf20Sopenharmony_ci	struct cmos_rtc	*cmos = dev_get_drvdata(dev);
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	if (system_state == SYSTEM_POWER_OFF) {
15098c2ecf20Sopenharmony_ci		int retval = cmos_poweroff(dev);
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci		if (cmos_aie_poweroff(dev) < 0 && !retval)
15128c2ecf20Sopenharmony_ci			return;
15138c2ecf20Sopenharmony_ci	}
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	cmos_do_shutdown(cmos->irq);
15168c2ecf20Sopenharmony_ci}
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci/* work with hotplug and coldplug */
15198c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:rtc_cmos");
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_cistatic struct platform_driver cmos_platform_driver = {
15228c2ecf20Sopenharmony_ci	.remove		= cmos_platform_remove,
15238c2ecf20Sopenharmony_ci	.shutdown	= cmos_platform_shutdown,
15248c2ecf20Sopenharmony_ci	.driver = {
15258c2ecf20Sopenharmony_ci		.name		= driver_name,
15268c2ecf20Sopenharmony_ci		.pm		= &cmos_pm_ops,
15278c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(of_cmos_match),
15288c2ecf20Sopenharmony_ci	}
15298c2ecf20Sopenharmony_ci};
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP
15328c2ecf20Sopenharmony_cistatic bool pnp_driver_registered;
15338c2ecf20Sopenharmony_ci#endif
15348c2ecf20Sopenharmony_cistatic bool platform_driver_registered;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_cistatic int __init cmos_init(void)
15378c2ecf20Sopenharmony_ci{
15388c2ecf20Sopenharmony_ci	int retval = 0;
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci#ifdef	CONFIG_PNP
15418c2ecf20Sopenharmony_ci	retval = pnp_register_driver(&cmos_pnp_driver);
15428c2ecf20Sopenharmony_ci	if (retval == 0)
15438c2ecf20Sopenharmony_ci		pnp_driver_registered = true;
15448c2ecf20Sopenharmony_ci#endif
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	if (!cmos_rtc.dev) {
15478c2ecf20Sopenharmony_ci		retval = platform_driver_probe(&cmos_platform_driver,
15488c2ecf20Sopenharmony_ci					       cmos_platform_probe);
15498c2ecf20Sopenharmony_ci		if (retval == 0)
15508c2ecf20Sopenharmony_ci			platform_driver_registered = true;
15518c2ecf20Sopenharmony_ci	}
15528c2ecf20Sopenharmony_ci
15538c2ecf20Sopenharmony_ci	if (retval == 0)
15548c2ecf20Sopenharmony_ci		return 0;
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci#ifdef	CONFIG_PNP
15578c2ecf20Sopenharmony_ci	if (pnp_driver_registered)
15588c2ecf20Sopenharmony_ci		pnp_unregister_driver(&cmos_pnp_driver);
15598c2ecf20Sopenharmony_ci#endif
15608c2ecf20Sopenharmony_ci	return retval;
15618c2ecf20Sopenharmony_ci}
15628c2ecf20Sopenharmony_cimodule_init(cmos_init);
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_cistatic void __exit cmos_exit(void)
15658c2ecf20Sopenharmony_ci{
15668c2ecf20Sopenharmony_ci#ifdef	CONFIG_PNP
15678c2ecf20Sopenharmony_ci	if (pnp_driver_registered)
15688c2ecf20Sopenharmony_ci		pnp_unregister_driver(&cmos_pnp_driver);
15698c2ecf20Sopenharmony_ci#endif
15708c2ecf20Sopenharmony_ci	if (platform_driver_registered)
15718c2ecf20Sopenharmony_ci		platform_driver_unregister(&cmos_platform_driver);
15728c2ecf20Sopenharmony_ci}
15738c2ecf20Sopenharmony_cimodule_exit(cmos_exit);
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Brownell");
15778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for PC-style 'CMOS' RTCs");
15788c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1579