12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
27be2c7c9SDavid Brownell /*
37be2c7c9SDavid Brownell * RTC class driver for "CMOS RTC": PCs, ACPI, etc
47be2c7c9SDavid Brownell *
57be2c7c9SDavid Brownell * Copyright (C) 1996 Paul Gortmaker (drivers/char/rtc.c)
67be2c7c9SDavid Brownell * Copyright (C) 2006 David Brownell (convert to new framework)
77be2c7c9SDavid Brownell */
87be2c7c9SDavid Brownell
97be2c7c9SDavid Brownell /*
107be2c7c9SDavid Brownell * The original "cmos clock" chip was an MC146818 chip, now obsolete.
117be2c7c9SDavid Brownell * That defined the register interface now provided by all PCs, some
127be2c7c9SDavid Brownell * non-PC systems, and incorporated into ACPI. Modern PC chipsets
137be2c7c9SDavid Brownell * integrate an MC146818 clone in their southbridge, and boards use
147be2c7c9SDavid Brownell * that instead of discrete clones like the DS12887 or M48T86. There
157be2c7c9SDavid Brownell * are also clones that connect using the LPC bus.
167be2c7c9SDavid Brownell *
177be2c7c9SDavid Brownell * That register API is also used directly by various other drivers
187be2c7c9SDavid Brownell * (notably for integrated NVRAM), infrastructure (x86 has code to
197be2c7c9SDavid Brownell * bypass the RTC framework, directly reading the RTC during boot
207be2c7c9SDavid Brownell * and updating minutes/seconds for systems using NTP synch) and
217be2c7c9SDavid Brownell * utilities (like userspace 'hwclock', if no /dev node exists).
227be2c7c9SDavid Brownell *
237be2c7c9SDavid Brownell * So **ALL** calls to CMOS_READ and CMOS_WRITE must be done with
247be2c7c9SDavid Brownell * interrupts disabled, holding the global rtc_lock, to exclude those
257be2c7c9SDavid Brownell * other drivers and utilities on correctly configured systems.
267be2c7c9SDavid Brownell */
27a737e835SJoe Perches
28a737e835SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
29a737e835SJoe Perches
307be2c7c9SDavid Brownell #include <linux/kernel.h>
317be2c7c9SDavid Brownell #include <linux/module.h>
327be2c7c9SDavid Brownell #include <linux/init.h>
337be2c7c9SDavid Brownell #include <linux/interrupt.h>
347be2c7c9SDavid Brownell #include <linux/spinlock.h>
357be2c7c9SDavid Brownell #include <linux/platform_device.h>
365d2a5037SJonathan Cameron #include <linux/log2.h>
372fb08e6cSPaul Fox #include <linux/pm.h>
383bcbaf6eSSebastian Andrzej Siewior #include <linux/of.h>
393bcbaf6eSSebastian Andrzej Siewior #include <linux/of_platform.h>
40a1e23a42SHans de Goede #ifdef CONFIG_X86
41a1e23a42SHans de Goede #include <asm/i8259.h>
4236d91a4dSZhang Rui #include <asm/processor.h>
4336d91a4dSZhang Rui #include <linux/dmi.h>
44a1e23a42SHans de Goede #endif
457be2c7c9SDavid Brownell
467be2c7c9SDavid Brownell /* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
475ab788d7SArnd Bergmann #include <linux/mc146818rtc.h>
487be2c7c9SDavid Brownell
49bc51098cSMaciej W. Rozycki #ifdef CONFIG_ACPI
50311ee9c1SZhang Rui /*
51311ee9c1SZhang Rui * Use ACPI SCI to replace HPET interrupt for RTC Alarm event
52311ee9c1SZhang Rui *
53311ee9c1SZhang Rui * If cleared, ACPI SCI is only used to wake up the system from suspend
54311ee9c1SZhang Rui *
55311ee9c1SZhang Rui * If set, ACPI SCI is used to handle UIE/AIE and system wakeup
56311ee9c1SZhang Rui */
57311ee9c1SZhang Rui
58311ee9c1SZhang Rui static bool use_acpi_alarm;
59311ee9c1SZhang Rui module_param(use_acpi_alarm, bool, 0444);
60311ee9c1SZhang Rui
cmos_use_acpi_alarm(void)61bc51098cSMaciej W. Rozycki static inline int cmos_use_acpi_alarm(void)
62bc51098cSMaciej W. Rozycki {
63bc51098cSMaciej W. Rozycki return use_acpi_alarm;
64bc51098cSMaciej W. Rozycki }
65bc51098cSMaciej W. Rozycki #else /* !CONFIG_ACPI */
66bc51098cSMaciej W. Rozycki
cmos_use_acpi_alarm(void)67bc51098cSMaciej W. Rozycki static inline int cmos_use_acpi_alarm(void)
68bc51098cSMaciej W. Rozycki {
69bc51098cSMaciej W. Rozycki return 0;
70bc51098cSMaciej W. Rozycki }
71bc51098cSMaciej W. Rozycki #endif
72bc51098cSMaciej W. Rozycki
737be2c7c9SDavid Brownell struct cmos_rtc {
747be2c7c9SDavid Brownell struct rtc_device *rtc;
757be2c7c9SDavid Brownell struct device *dev;
767be2c7c9SDavid Brownell int irq;
777be2c7c9SDavid Brownell struct resource *iomem;
7888b8d33bSAdrian Huang time64_t alarm_expires;
797be2c7c9SDavid Brownell
8087ac84f4SDavid Brownell void (*wake_on)(struct device *);
8187ac84f4SDavid Brownell void (*wake_off)(struct device *);
8287ac84f4SDavid Brownell
8387ac84f4SDavid Brownell u8 enabled_wake;
847be2c7c9SDavid Brownell u8 suspend_ctrl;
857be2c7c9SDavid Brownell
867be2c7c9SDavid Brownell /* newer hardware extends the original register set */
877be2c7c9SDavid Brownell u8 day_alrm;
887be2c7c9SDavid Brownell u8 mon_alrm;
897be2c7c9SDavid Brownell u8 century;
9068669d55SGabriele Mazzotta
9168669d55SGabriele Mazzotta struct rtc_wkalrm saved_wkalrm;
927be2c7c9SDavid Brownell };
937be2c7c9SDavid Brownell
947be2c7c9SDavid Brownell /* both platform and pnp busses use negative numbers for invalid irqs */
952fac6674SAnton Vorontsov #define is_valid_irq(n) ((n) > 0)
967be2c7c9SDavid Brownell
977be2c7c9SDavid Brownell static const char driver_name[] = "rtc_cmos";
987be2c7c9SDavid Brownell
99bcd9b89cSDavid Brownell /* The RTC_INTR register may have e.g. RTC_PF set even if RTC_PIE is clear;
100bcd9b89cSDavid Brownell * always mask it against the irq enable bits in RTC_CONTROL. Bit values
101bcd9b89cSDavid Brownell * are the same: PF==PIE, AF=AIE, UF=UIE; so RTC_IRQMASK works with both.
102bcd9b89cSDavid Brownell */
103bcd9b89cSDavid Brownell #define RTC_IRQMASK (RTC_PF | RTC_AF | RTC_UF)
104bcd9b89cSDavid Brownell
is_intr(u8 rtc_intr)105bcd9b89cSDavid Brownell static inline int is_intr(u8 rtc_intr)
106bcd9b89cSDavid Brownell {
107bcd9b89cSDavid Brownell if (!(rtc_intr & RTC_IRQF))
108bcd9b89cSDavid Brownell return 0;
109bcd9b89cSDavid Brownell return rtc_intr & RTC_IRQMASK;
110bcd9b89cSDavid Brownell }
111bcd9b89cSDavid Brownell
1127be2c7c9SDavid Brownell /*----------------------------------------------------------------*/
1137be2c7c9SDavid Brownell
11435d3fdd5SDavid Brownell /* Much modern x86 hardware has HPETs (10+ MHz timers) which, because
11535d3fdd5SDavid Brownell * many BIOS programmers don't set up "sane mode" IRQ routing, are mostly
11635d3fdd5SDavid Brownell * used in a broken "legacy replacement" mode. The breakage includes
11735d3fdd5SDavid Brownell * HPET #1 hijacking the IRQ for this RTC, and being unavailable for
11835d3fdd5SDavid Brownell * other (better) use.
11935d3fdd5SDavid Brownell *
12035d3fdd5SDavid Brownell * When that broken mode is in use, platform glue provides a partial
12135d3fdd5SDavid Brownell * emulation of hardware RTC IRQ facilities using HPET #1. We don't
12235d3fdd5SDavid Brownell * want to use HPET for anything except those IRQs though...
12335d3fdd5SDavid Brownell */
12435d3fdd5SDavid Brownell #ifdef CONFIG_HPET_EMULATE_RTC
12535d3fdd5SDavid Brownell #include <asm/hpet.h>
12635d3fdd5SDavid Brownell #else
12735d3fdd5SDavid Brownell
is_hpet_enabled(void)12835d3fdd5SDavid Brownell static inline int is_hpet_enabled(void)
12935d3fdd5SDavid Brownell {
13035d3fdd5SDavid Brownell return 0;
13135d3fdd5SDavid Brownell }
13235d3fdd5SDavid Brownell
hpet_mask_rtc_irq_bit(unsigned long mask)13335d3fdd5SDavid Brownell static inline int hpet_mask_rtc_irq_bit(unsigned long mask)
13435d3fdd5SDavid Brownell {
13535d3fdd5SDavid Brownell return 0;
13635d3fdd5SDavid Brownell }
13735d3fdd5SDavid Brownell
hpet_set_rtc_irq_bit(unsigned long mask)13835d3fdd5SDavid Brownell static inline int hpet_set_rtc_irq_bit(unsigned long mask)
13935d3fdd5SDavid Brownell {
14035d3fdd5SDavid Brownell return 0;
14135d3fdd5SDavid Brownell }
14235d3fdd5SDavid Brownell
14335d3fdd5SDavid Brownell static inline int
hpet_set_alarm_time(unsigned char hrs,unsigned char min,unsigned char sec)14435d3fdd5SDavid Brownell hpet_set_alarm_time(unsigned char hrs, unsigned char min, unsigned char sec)
14535d3fdd5SDavid Brownell {
14635d3fdd5SDavid Brownell return 0;
14735d3fdd5SDavid Brownell }
14835d3fdd5SDavid Brownell
hpet_set_periodic_freq(unsigned long freq)14935d3fdd5SDavid Brownell static inline int hpet_set_periodic_freq(unsigned long freq)
15035d3fdd5SDavid Brownell {
15135d3fdd5SDavid Brownell return 0;
15235d3fdd5SDavid Brownell }
15335d3fdd5SDavid Brownell
hpet_rtc_dropped_irq(void)15435d3fdd5SDavid Brownell static inline int hpet_rtc_dropped_irq(void)
15535d3fdd5SDavid Brownell {
15635d3fdd5SDavid Brownell return 0;
15735d3fdd5SDavid Brownell }
15835d3fdd5SDavid Brownell
hpet_rtc_timer_init(void)15935d3fdd5SDavid Brownell static inline int hpet_rtc_timer_init(void)
16035d3fdd5SDavid Brownell {
16135d3fdd5SDavid Brownell return 0;
16235d3fdd5SDavid Brownell }
16335d3fdd5SDavid Brownell
16435d3fdd5SDavid Brownell extern irq_handler_t hpet_rtc_interrupt;
16535d3fdd5SDavid Brownell
hpet_register_irq_handler(irq_handler_t handler)16635d3fdd5SDavid Brownell static inline int hpet_register_irq_handler(irq_handler_t handler)
16735d3fdd5SDavid Brownell {
16835d3fdd5SDavid Brownell return 0;
16935d3fdd5SDavid Brownell }
17035d3fdd5SDavid Brownell
hpet_unregister_irq_handler(irq_handler_t handler)17135d3fdd5SDavid Brownell static inline int hpet_unregister_irq_handler(irq_handler_t handler)
17235d3fdd5SDavid Brownell {
17335d3fdd5SDavid Brownell return 0;
17435d3fdd5SDavid Brownell }
17535d3fdd5SDavid Brownell
17635d3fdd5SDavid Brownell #endif
17735d3fdd5SDavid Brownell
178311ee9c1SZhang Rui /* Don't use HPET for RTC Alarm event if ACPI Fixed event is used */
use_hpet_alarm(void)179d197a253SMaciej W. Rozycki static inline int use_hpet_alarm(void)
180311ee9c1SZhang Rui {
181bc51098cSMaciej W. Rozycki return is_hpet_enabled() && !cmos_use_acpi_alarm();
182311ee9c1SZhang Rui }
183311ee9c1SZhang Rui
18435d3fdd5SDavid Brownell /*----------------------------------------------------------------*/
18535d3fdd5SDavid Brownell
186c8fc40cdSDavid Brownell #ifdef RTC_PORT
187c8fc40cdSDavid Brownell
188c8fc40cdSDavid Brownell /* Most newer x86 systems have two register banks, the first used
189c8fc40cdSDavid Brownell * for RTC and NVRAM and the second only for NVRAM. Caller must
190c8fc40cdSDavid Brownell * own rtc_lock ... and we won't worry about access during NMI.
191c8fc40cdSDavid Brownell */
192c8fc40cdSDavid Brownell #define can_bank2 true
193c8fc40cdSDavid Brownell
cmos_read_bank2(unsigned char addr)194c8fc40cdSDavid Brownell static inline unsigned char cmos_read_bank2(unsigned char addr)
195c8fc40cdSDavid Brownell {
196c8fc40cdSDavid Brownell outb(addr, RTC_PORT(2));
197c8fc40cdSDavid Brownell return inb(RTC_PORT(3));
198c8fc40cdSDavid Brownell }
199c8fc40cdSDavid Brownell
cmos_write_bank2(unsigned char val,unsigned char addr)200c8fc40cdSDavid Brownell static inline void cmos_write_bank2(unsigned char val, unsigned char addr)
201c8fc40cdSDavid Brownell {
202c8fc40cdSDavid Brownell outb(addr, RTC_PORT(2));
203b43c1ea4SOndrej Zary outb(val, RTC_PORT(3));
204c8fc40cdSDavid Brownell }
205c8fc40cdSDavid Brownell
206c8fc40cdSDavid Brownell #else
207c8fc40cdSDavid Brownell
208c8fc40cdSDavid Brownell #define can_bank2 false
209c8fc40cdSDavid Brownell
cmos_read_bank2(unsigned char addr)210c8fc40cdSDavid Brownell static inline unsigned char cmos_read_bank2(unsigned char addr)
211c8fc40cdSDavid Brownell {
212c8fc40cdSDavid Brownell return 0;
213c8fc40cdSDavid Brownell }
214c8fc40cdSDavid Brownell
cmos_write_bank2(unsigned char val,unsigned char addr)215c8fc40cdSDavid Brownell static inline void cmos_write_bank2(unsigned char val, unsigned char addr)
216c8fc40cdSDavid Brownell {
217c8fc40cdSDavid Brownell }
218c8fc40cdSDavid Brownell
219c8fc40cdSDavid Brownell #endif
220c8fc40cdSDavid Brownell
221c8fc40cdSDavid Brownell /*----------------------------------------------------------------*/
222c8fc40cdSDavid Brownell
cmos_read_time(struct device * dev,struct rtc_time * t)2237be2c7c9SDavid Brownell static int cmos_read_time(struct device *dev, struct rtc_time *t)
2247be2c7c9SDavid Brownell {
2250dd8d6cbSMateusz Jończyk int ret;
2260dd8d6cbSMateusz Jończyk
227ba58d102SChen Yu /*
228ba58d102SChen Yu * If pm_trace abused the RTC for storage, set the timespec to 0,
229ba58d102SChen Yu * which tells the caller that this RTC value is unusable.
230ba58d102SChen Yu */
231ba58d102SChen Yu if (!pm_trace_rtc_valid())
232ba58d102SChen Yu return -EIO;
233ba58d102SChen Yu
23449a76c08SMario Limonciello ret = mc146818_get_time(t, 1000);
2350dd8d6cbSMateusz Jończyk if (ret < 0) {
2360dd8d6cbSMateusz Jończyk dev_err_ratelimited(dev, "unable to read current time\n");
2370dd8d6cbSMateusz Jończyk return ret;
2380dd8d6cbSMateusz Jończyk }
2390dd8d6cbSMateusz Jończyk
2407be2c7c9SDavid Brownell return 0;
2417be2c7c9SDavid Brownell }
2427be2c7c9SDavid Brownell
cmos_set_time(struct device * dev,struct rtc_time * t)2437be2c7c9SDavid Brownell static int cmos_set_time(struct device *dev, struct rtc_time *t)
2447be2c7c9SDavid Brownell {
245e1aba375SMateusz Jończyk /* NOTE: this ignores the issue whereby updating the seconds
2467be2c7c9SDavid Brownell * takes effect exactly 500ms after we write the register.
2477be2c7c9SDavid Brownell * (Also queueing and other delays before we get this far.)
2487be2c7c9SDavid Brownell */
2495ab788d7SArnd Bergmann return mc146818_set_time(t);
2507be2c7c9SDavid Brownell }
2517be2c7c9SDavid Brownell
252cdedc45cSMateusz Jończyk struct cmos_read_alarm_callback_param {
253cdedc45cSMateusz Jończyk struct cmos_rtc *cmos;
254cdedc45cSMateusz Jończyk struct rtc_time *time;
255cdedc45cSMateusz Jończyk unsigned char rtc_control;
256cdedc45cSMateusz Jończyk };
257cdedc45cSMateusz Jończyk
cmos_read_alarm_callback(unsigned char __always_unused seconds,void * param_in)258cdedc45cSMateusz Jończyk static void cmos_read_alarm_callback(unsigned char __always_unused seconds,
259cdedc45cSMateusz Jończyk void *param_in)
260cdedc45cSMateusz Jończyk {
261cdedc45cSMateusz Jończyk struct cmos_read_alarm_callback_param *p =
262cdedc45cSMateusz Jończyk (struct cmos_read_alarm_callback_param *)param_in;
263cdedc45cSMateusz Jończyk struct rtc_time *time = p->time;
264cdedc45cSMateusz Jończyk
265cdedc45cSMateusz Jończyk time->tm_sec = CMOS_READ(RTC_SECONDS_ALARM);
266cdedc45cSMateusz Jończyk time->tm_min = CMOS_READ(RTC_MINUTES_ALARM);
267cdedc45cSMateusz Jończyk time->tm_hour = CMOS_READ(RTC_HOURS_ALARM);
268cdedc45cSMateusz Jończyk
269cdedc45cSMateusz Jończyk if (p->cmos->day_alrm) {
270cdedc45cSMateusz Jończyk /* ignore upper bits on readback per ACPI spec */
271cdedc45cSMateusz Jończyk time->tm_mday = CMOS_READ(p->cmos->day_alrm) & 0x3f;
272cdedc45cSMateusz Jończyk if (!time->tm_mday)
273cdedc45cSMateusz Jończyk time->tm_mday = -1;
274cdedc45cSMateusz Jończyk
275cdedc45cSMateusz Jończyk if (p->cmos->mon_alrm) {
276cdedc45cSMateusz Jończyk time->tm_mon = CMOS_READ(p->cmos->mon_alrm);
277cdedc45cSMateusz Jończyk if (!time->tm_mon)
278cdedc45cSMateusz Jończyk time->tm_mon = -1;
279cdedc45cSMateusz Jończyk }
280cdedc45cSMateusz Jończyk }
281cdedc45cSMateusz Jończyk
282cdedc45cSMateusz Jończyk p->rtc_control = CMOS_READ(RTC_CONTROL);
283cdedc45cSMateusz Jończyk }
284cdedc45cSMateusz Jończyk
cmos_read_alarm(struct device * dev,struct rtc_wkalrm * t)2857be2c7c9SDavid Brownell static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
2867be2c7c9SDavid Brownell {
2877be2c7c9SDavid Brownell struct cmos_rtc *cmos = dev_get_drvdata(dev);
288cdedc45cSMateusz Jończyk struct cmos_read_alarm_callback_param p = {
289cdedc45cSMateusz Jończyk .cmos = cmos,
290cdedc45cSMateusz Jończyk .time = &t->time,
291cdedc45cSMateusz Jończyk };
2927be2c7c9SDavid Brownell
293fbb974baSHans de Goede /* This not only a rtc_op, but also called directly */
2947be2c7c9SDavid Brownell if (!is_valid_irq(cmos->irq))
295905d9e1cSMario Limonciello return -ETIMEDOUT;
2967be2c7c9SDavid Brownell
2977be2c7c9SDavid Brownell /* Basic alarms only support hour, minute, and seconds fields.
2987be2c7c9SDavid Brownell * Some also support day and month, for alarms up to a year in
2997be2c7c9SDavid Brownell * the future.
3007be2c7c9SDavid Brownell */
3017be2c7c9SDavid Brownell
302cdedc45cSMateusz Jończyk /* Some Intel chipsets disconnect the alarm registers when the clock
303cdedc45cSMateusz Jończyk * update is in progress - during this time reads return bogus values
304cdedc45cSMateusz Jończyk * and writes may fail silently. See for example "7th Generation Intel®
305cdedc45cSMateusz Jończyk * Processor Family I/O for U/Y Platforms [...] Datasheet", section
306cdedc45cSMateusz Jończyk * 27.7.1
307cdedc45cSMateusz Jończyk *
308cdedc45cSMateusz Jończyk * Use the mc146818_avoid_UIP() function to avoid this.
309cdedc45cSMateusz Jończyk */
3109d201856SMario Limonciello if (!mc146818_avoid_UIP(cmos_read_alarm_callback, 10, &p))
311cdedc45cSMateusz Jończyk return -EIO;
3127be2c7c9SDavid Brownell
313cdedc45cSMateusz Jończyk if (!(p.rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
3147be2c7c9SDavid Brownell if (((unsigned)t->time.tm_sec) < 0x60)
315fe20ba70SAdrian Bunk t->time.tm_sec = bcd2bin(t->time.tm_sec);
3167be2c7c9SDavid Brownell else
3177be2c7c9SDavid Brownell t->time.tm_sec = -1;
3187be2c7c9SDavid Brownell if (((unsigned)t->time.tm_min) < 0x60)
319fe20ba70SAdrian Bunk t->time.tm_min = bcd2bin(t->time.tm_min);
3207be2c7c9SDavid Brownell else
3217be2c7c9SDavid Brownell t->time.tm_min = -1;
3227be2c7c9SDavid Brownell if (((unsigned)t->time.tm_hour) < 0x24)
323fe20ba70SAdrian Bunk t->time.tm_hour = bcd2bin(t->time.tm_hour);
3247be2c7c9SDavid Brownell else
3257be2c7c9SDavid Brownell t->time.tm_hour = -1;
3267be2c7c9SDavid Brownell
3277be2c7c9SDavid Brownell if (cmos->day_alrm) {
3287be2c7c9SDavid Brownell if (((unsigned)t->time.tm_mday) <= 0x31)
329fe20ba70SAdrian Bunk t->time.tm_mday = bcd2bin(t->time.tm_mday);
3307be2c7c9SDavid Brownell else
3317be2c7c9SDavid Brownell t->time.tm_mday = -1;
3323804a89bSArnaud Patard
3337be2c7c9SDavid Brownell if (cmos->mon_alrm) {
3347be2c7c9SDavid Brownell if (((unsigned)t->time.tm_mon) <= 0x12)
335fe20ba70SAdrian Bunk t->time.tm_mon = bcd2bin(t->time.tm_mon)-1;
3367be2c7c9SDavid Brownell else
3377be2c7c9SDavid Brownell t->time.tm_mon = -1;
3387be2c7c9SDavid Brownell }
3397be2c7c9SDavid Brownell }
3403804a89bSArnaud Patard }
3417be2c7c9SDavid Brownell
342cdedc45cSMateusz Jończyk t->enabled = !!(p.rtc_control & RTC_AIE);
3437be2c7c9SDavid Brownell t->pending = 0;
3447be2c7c9SDavid Brownell
3457be2c7c9SDavid Brownell return 0;
3467be2c7c9SDavid Brownell }
3477be2c7c9SDavid Brownell
cmos_checkintr(struct cmos_rtc * cmos,unsigned char rtc_control)3487e2a31daSDavid Brownell static void cmos_checkintr(struct cmos_rtc *cmos, unsigned char rtc_control)
3497e2a31daSDavid Brownell {
3507e2a31daSDavid Brownell unsigned char rtc_intr;
3517e2a31daSDavid Brownell
3527e2a31daSDavid Brownell /* NOTE after changing RTC_xIE bits we always read INTR_FLAGS;
3537e2a31daSDavid Brownell * allegedly some older rtcs need that to handle irqs properly
3547e2a31daSDavid Brownell */
3557e2a31daSDavid Brownell rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
3567e2a31daSDavid Brownell
357311ee9c1SZhang Rui if (use_hpet_alarm())
3587e2a31daSDavid Brownell return;
3597e2a31daSDavid Brownell
3607e2a31daSDavid Brownell rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
3617e2a31daSDavid Brownell if (is_intr(rtc_intr))
3627e2a31daSDavid Brownell rtc_update_irq(cmos->rtc, 1, rtc_intr);
3637e2a31daSDavid Brownell }
3647e2a31daSDavid Brownell
cmos_irq_enable(struct cmos_rtc * cmos,unsigned char mask)3657e2a31daSDavid Brownell static void cmos_irq_enable(struct cmos_rtc *cmos, unsigned char mask)
3667e2a31daSDavid Brownell {
3677e2a31daSDavid Brownell unsigned char rtc_control;
3687e2a31daSDavid Brownell
3697e2a31daSDavid Brownell /* flush any pending IRQ status, notably for update irqs,
3707e2a31daSDavid Brownell * before we enable new IRQs
3717e2a31daSDavid Brownell */
3727e2a31daSDavid Brownell rtc_control = CMOS_READ(RTC_CONTROL);
3737e2a31daSDavid Brownell cmos_checkintr(cmos, rtc_control);
3747e2a31daSDavid Brownell
3757e2a31daSDavid Brownell rtc_control |= mask;
3767e2a31daSDavid Brownell CMOS_WRITE(rtc_control, RTC_CONTROL);
377311ee9c1SZhang Rui if (use_hpet_alarm())
3787e2a31daSDavid Brownell hpet_set_rtc_irq_bit(mask);
3797e2a31daSDavid Brownell
380bc51098cSMaciej W. Rozycki if ((mask & RTC_AIE) && cmos_use_acpi_alarm()) {
381311ee9c1SZhang Rui if (cmos->wake_on)
382311ee9c1SZhang Rui cmos->wake_on(cmos->dev);
383311ee9c1SZhang Rui }
384311ee9c1SZhang Rui
3857e2a31daSDavid Brownell cmos_checkintr(cmos, rtc_control);
3867e2a31daSDavid Brownell }
3877e2a31daSDavid Brownell
cmos_irq_disable(struct cmos_rtc * cmos,unsigned char mask)3887e2a31daSDavid Brownell static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask)
3897e2a31daSDavid Brownell {
3907e2a31daSDavid Brownell unsigned char rtc_control;
3917e2a31daSDavid Brownell
3927e2a31daSDavid Brownell rtc_control = CMOS_READ(RTC_CONTROL);
3937e2a31daSDavid Brownell rtc_control &= ~mask;
3947e2a31daSDavid Brownell CMOS_WRITE(rtc_control, RTC_CONTROL);
395311ee9c1SZhang Rui if (use_hpet_alarm())
3967e2a31daSDavid Brownell hpet_mask_rtc_irq_bit(mask);
3977e2a31daSDavid Brownell
398bc51098cSMaciej W. Rozycki if ((mask & RTC_AIE) && cmos_use_acpi_alarm()) {
399311ee9c1SZhang Rui if (cmos->wake_off)
400311ee9c1SZhang Rui cmos->wake_off(cmos->dev);
401311ee9c1SZhang Rui }
402311ee9c1SZhang Rui
4037e2a31daSDavid Brownell cmos_checkintr(cmos, rtc_control);
4047e2a31daSDavid Brownell }
4057e2a31daSDavid Brownell
cmos_validate_alarm(struct device * dev,struct rtc_wkalrm * t)4066a6af3d0SGabriele Mazzotta static int cmos_validate_alarm(struct device *dev, struct rtc_wkalrm *t)
4076a6af3d0SGabriele Mazzotta {
4086a6af3d0SGabriele Mazzotta struct cmos_rtc *cmos = dev_get_drvdata(dev);
4096a6af3d0SGabriele Mazzotta struct rtc_time now;
4106a6af3d0SGabriele Mazzotta
4116a6af3d0SGabriele Mazzotta cmos_read_time(dev, &now);
4126a6af3d0SGabriele Mazzotta
4136a6af3d0SGabriele Mazzotta if (!cmos->day_alrm) {
4146a6af3d0SGabriele Mazzotta time64_t t_max_date;
4156a6af3d0SGabriele Mazzotta time64_t t_alrm;
4166a6af3d0SGabriele Mazzotta
4176a6af3d0SGabriele Mazzotta t_max_date = rtc_tm_to_time64(&now);
4186a6af3d0SGabriele Mazzotta t_max_date += 24 * 60 * 60 - 1;
4196a6af3d0SGabriele Mazzotta t_alrm = rtc_tm_to_time64(&t->time);
4206a6af3d0SGabriele Mazzotta if (t_alrm > t_max_date) {
4216a6af3d0SGabriele Mazzotta dev_err(dev,
4226a6af3d0SGabriele Mazzotta "Alarms can be up to one day in the future\n");
4236a6af3d0SGabriele Mazzotta return -EINVAL;
4246a6af3d0SGabriele Mazzotta }
4256a6af3d0SGabriele Mazzotta } else if (!cmos->mon_alrm) {
4266a6af3d0SGabriele Mazzotta struct rtc_time max_date = now;
4276a6af3d0SGabriele Mazzotta time64_t t_max_date;
4286a6af3d0SGabriele Mazzotta time64_t t_alrm;
4296a6af3d0SGabriele Mazzotta int max_mday;
4306a6af3d0SGabriele Mazzotta
4316a6af3d0SGabriele Mazzotta if (max_date.tm_mon == 11) {
4326a6af3d0SGabriele Mazzotta max_date.tm_mon = 0;
4336a6af3d0SGabriele Mazzotta max_date.tm_year += 1;
4346a6af3d0SGabriele Mazzotta } else {
4356a6af3d0SGabriele Mazzotta max_date.tm_mon += 1;
4366a6af3d0SGabriele Mazzotta }
4376a6af3d0SGabriele Mazzotta max_mday = rtc_month_days(max_date.tm_mon, max_date.tm_year);
4386a6af3d0SGabriele Mazzotta if (max_date.tm_mday > max_mday)
4396a6af3d0SGabriele Mazzotta max_date.tm_mday = max_mday;
4406a6af3d0SGabriele Mazzotta
4416a6af3d0SGabriele Mazzotta t_max_date = rtc_tm_to_time64(&max_date);
4426a6af3d0SGabriele Mazzotta t_max_date -= 1;
4436a6af3d0SGabriele Mazzotta t_alrm = rtc_tm_to_time64(&t->time);
4446a6af3d0SGabriele Mazzotta if (t_alrm > t_max_date) {
4456a6af3d0SGabriele Mazzotta dev_err(dev,
4466a6af3d0SGabriele Mazzotta "Alarms can be up to one month in the future\n");
4476a6af3d0SGabriele Mazzotta return -EINVAL;
4486a6af3d0SGabriele Mazzotta }
4496a6af3d0SGabriele Mazzotta } else {
4506a6af3d0SGabriele Mazzotta struct rtc_time max_date = now;
4516a6af3d0SGabriele Mazzotta time64_t t_max_date;
4526a6af3d0SGabriele Mazzotta time64_t t_alrm;
4536a6af3d0SGabriele Mazzotta int max_mday;
4546a6af3d0SGabriele Mazzotta
4556a6af3d0SGabriele Mazzotta max_date.tm_year += 1;
4566a6af3d0SGabriele Mazzotta max_mday = rtc_month_days(max_date.tm_mon, max_date.tm_year);
4576a6af3d0SGabriele Mazzotta if (max_date.tm_mday > max_mday)
4586a6af3d0SGabriele Mazzotta max_date.tm_mday = max_mday;
4596a6af3d0SGabriele Mazzotta
4606a6af3d0SGabriele Mazzotta t_max_date = rtc_tm_to_time64(&max_date);
4616a6af3d0SGabriele Mazzotta t_max_date -= 1;
4626a6af3d0SGabriele Mazzotta t_alrm = rtc_tm_to_time64(&t->time);
4636a6af3d0SGabriele Mazzotta if (t_alrm > t_max_date) {
4646a6af3d0SGabriele Mazzotta dev_err(dev,
4656a6af3d0SGabriele Mazzotta "Alarms can be up to one year in the future\n");
4666a6af3d0SGabriele Mazzotta return -EINVAL;
4676a6af3d0SGabriele Mazzotta }
4686a6af3d0SGabriele Mazzotta }
4696a6af3d0SGabriele Mazzotta
4706a6af3d0SGabriele Mazzotta return 0;
4716a6af3d0SGabriele Mazzotta }
4726a6af3d0SGabriele Mazzotta
473cd17420eSMateusz Jończyk struct cmos_set_alarm_callback_param {
474cd17420eSMateusz Jończyk struct cmos_rtc *cmos;
475cd17420eSMateusz Jończyk unsigned char mon, mday, hrs, min, sec;
476cd17420eSMateusz Jończyk struct rtc_wkalrm *t;
477cd17420eSMateusz Jończyk };
478cd17420eSMateusz Jończyk
479cd17420eSMateusz Jończyk /* Note: this function may be executed by mc146818_avoid_UIP() more then
480cd17420eSMateusz Jończyk * once
481cd17420eSMateusz Jończyk */
cmos_set_alarm_callback(unsigned char __always_unused seconds,void * param_in)482cd17420eSMateusz Jończyk static void cmos_set_alarm_callback(unsigned char __always_unused seconds,
483cd17420eSMateusz Jończyk void *param_in)
484cd17420eSMateusz Jończyk {
485cd17420eSMateusz Jończyk struct cmos_set_alarm_callback_param *p =
486cd17420eSMateusz Jończyk (struct cmos_set_alarm_callback_param *)param_in;
487cd17420eSMateusz Jończyk
488cd17420eSMateusz Jończyk /* next rtc irq must not be from previous alarm setting */
489cd17420eSMateusz Jończyk cmos_irq_disable(p->cmos, RTC_AIE);
490cd17420eSMateusz Jończyk
491cd17420eSMateusz Jończyk /* update alarm */
492cd17420eSMateusz Jończyk CMOS_WRITE(p->hrs, RTC_HOURS_ALARM);
493cd17420eSMateusz Jończyk CMOS_WRITE(p->min, RTC_MINUTES_ALARM);
494cd17420eSMateusz Jończyk CMOS_WRITE(p->sec, RTC_SECONDS_ALARM);
495cd17420eSMateusz Jończyk
496cd17420eSMateusz Jończyk /* the system may support an "enhanced" alarm */
497cd17420eSMateusz Jończyk if (p->cmos->day_alrm) {
498cd17420eSMateusz Jończyk CMOS_WRITE(p->mday, p->cmos->day_alrm);
499cd17420eSMateusz Jończyk if (p->cmos->mon_alrm)
500cd17420eSMateusz Jończyk CMOS_WRITE(p->mon, p->cmos->mon_alrm);
501cd17420eSMateusz Jończyk }
502cd17420eSMateusz Jończyk
503cd17420eSMateusz Jończyk if (use_hpet_alarm()) {
504cd17420eSMateusz Jończyk /*
505cd17420eSMateusz Jończyk * FIXME the HPET alarm glue currently ignores day_alrm
506cd17420eSMateusz Jończyk * and mon_alrm ...
507cd17420eSMateusz Jończyk */
508cd17420eSMateusz Jończyk hpet_set_alarm_time(p->t->time.tm_hour, p->t->time.tm_min,
509cd17420eSMateusz Jończyk p->t->time.tm_sec);
510cd17420eSMateusz Jończyk }
511cd17420eSMateusz Jończyk
512cd17420eSMateusz Jończyk if (p->t->enabled)
513cd17420eSMateusz Jończyk cmos_irq_enable(p->cmos, RTC_AIE);
514cd17420eSMateusz Jończyk }
515cd17420eSMateusz Jończyk
cmos_set_alarm(struct device * dev,struct rtc_wkalrm * t)5167be2c7c9SDavid Brownell static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
5177be2c7c9SDavid Brownell {
5187be2c7c9SDavid Brownell struct cmos_rtc *cmos = dev_get_drvdata(dev);
519cd17420eSMateusz Jończyk struct cmos_set_alarm_callback_param p = {
520cd17420eSMateusz Jończyk .cmos = cmos,
521cd17420eSMateusz Jończyk .t = t
522cd17420eSMateusz Jończyk };
523cd17420eSMateusz Jończyk unsigned char rtc_control;
5246a6af3d0SGabriele Mazzotta int ret;
5257be2c7c9SDavid Brownell
526fbb974baSHans de Goede /* This not only a rtc_op, but also called directly */
5277be2c7c9SDavid Brownell if (!is_valid_irq(cmos->irq))
5287be2c7c9SDavid Brownell return -EIO;
5297be2c7c9SDavid Brownell
5306a6af3d0SGabriele Mazzotta ret = cmos_validate_alarm(dev, t);
5316a6af3d0SGabriele Mazzotta if (ret < 0)
5326a6af3d0SGabriele Mazzotta return ret;
5336a6af3d0SGabriele Mazzotta
534cd17420eSMateusz Jończyk p.mon = t->time.tm_mon + 1;
535cd17420eSMateusz Jończyk p.mday = t->time.tm_mday;
536cd17420eSMateusz Jończyk p.hrs = t->time.tm_hour;
537cd17420eSMateusz Jończyk p.min = t->time.tm_min;
538cd17420eSMateusz Jończyk p.sec = t->time.tm_sec;
5393804a89bSArnaud Patard
540454f47ffSMateusz Jończyk spin_lock_irq(&rtc_lock);
5413804a89bSArnaud Patard rtc_control = CMOS_READ(RTC_CONTROL);
542454f47ffSMateusz Jończyk spin_unlock_irq(&rtc_lock);
543454f47ffSMateusz Jończyk
5443804a89bSArnaud Patard if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
5453804a89bSArnaud Patard /* Writing 0xff means "don't care" or "match all". */
546cd17420eSMateusz Jończyk p.mon = (p.mon <= 12) ? bin2bcd(p.mon) : 0xff;
547cd17420eSMateusz Jończyk p.mday = (p.mday >= 1 && p.mday <= 31) ? bin2bcd(p.mday) : 0xff;
548cd17420eSMateusz Jończyk p.hrs = (p.hrs < 24) ? bin2bcd(p.hrs) : 0xff;
549cd17420eSMateusz Jończyk p.min = (p.min < 60) ? bin2bcd(p.min) : 0xff;
550cd17420eSMateusz Jończyk p.sec = (p.sec < 60) ? bin2bcd(p.sec) : 0xff;
5513804a89bSArnaud Patard }
5527be2c7c9SDavid Brownell
553311ee9c1SZhang Rui /*
554cd17420eSMateusz Jończyk * Some Intel chipsets disconnect the alarm registers when the clock
555cd17420eSMateusz Jończyk * update is in progress - during this time writes fail silently.
556cd17420eSMateusz Jończyk *
557cd17420eSMateusz Jończyk * Use mc146818_avoid_UIP() to avoid this.
55835d3fdd5SDavid Brownell */
5599d201856SMario Limonciello if (!mc146818_avoid_UIP(cmos_set_alarm_callback, 10, &p))
560905d9e1cSMario Limonciello return -ETIMEDOUT;
5617be2c7c9SDavid Brownell
56288b8d33bSAdrian Huang cmos->alarm_expires = rtc_tm_to_time64(&t->time);
56388b8d33bSAdrian Huang
5647be2c7c9SDavid Brownell return 0;
5657be2c7c9SDavid Brownell }
5667be2c7c9SDavid Brownell
cmos_alarm_irq_enable(struct device * dev,unsigned int enabled)567a8462ef6SHerton Ronaldo Krzesinski static int cmos_alarm_irq_enable(struct device *dev, unsigned int enabled)
5687be2c7c9SDavid Brownell {
5697be2c7c9SDavid Brownell struct cmos_rtc *cmos = dev_get_drvdata(dev);
5707be2c7c9SDavid Brownell unsigned long flags;
5717be2c7c9SDavid Brownell
5727be2c7c9SDavid Brownell spin_lock_irqsave(&rtc_lock, flags);
573a8462ef6SHerton Ronaldo Krzesinski
574a8462ef6SHerton Ronaldo Krzesinski if (enabled)
5757e2a31daSDavid Brownell cmos_irq_enable(cmos, RTC_AIE);
576a8462ef6SHerton Ronaldo Krzesinski else
577a8462ef6SHerton Ronaldo Krzesinski cmos_irq_disable(cmos, RTC_AIE);
578a8462ef6SHerton Ronaldo Krzesinski
5797be2c7c9SDavid Brownell spin_unlock_irqrestore(&rtc_lock, flags);
5807be2c7c9SDavid Brownell return 0;
5817be2c7c9SDavid Brownell }
5827be2c7c9SDavid Brownell
5836fca3fc5SJavier Martinez Canillas #if IS_ENABLED(CONFIG_RTC_INTF_PROC)
5847be2c7c9SDavid Brownell
cmos_procfs(struct device * dev,struct seq_file * seq)5857be2c7c9SDavid Brownell static int cmos_procfs(struct device *dev, struct seq_file *seq)
5867be2c7c9SDavid Brownell {
5877be2c7c9SDavid Brownell struct cmos_rtc *cmos = dev_get_drvdata(dev);
5887be2c7c9SDavid Brownell unsigned char rtc_control, valid;
5897be2c7c9SDavid Brownell
5907be2c7c9SDavid Brownell spin_lock_irq(&rtc_lock);
5917be2c7c9SDavid Brownell rtc_control = CMOS_READ(RTC_CONTROL);
5927be2c7c9SDavid Brownell valid = CMOS_READ(RTC_VALID);
5937be2c7c9SDavid Brownell spin_unlock_irq(&rtc_lock);
5947be2c7c9SDavid Brownell
5957be2c7c9SDavid Brownell /* NOTE: at least ICH6 reports battery status using a different
5967be2c7c9SDavid Brownell * (non-RTC) bit; and SQWE is ignored on many current systems.
5977be2c7c9SDavid Brownell */
5984395eb1fSJoe Perches seq_printf(seq,
5997be2c7c9SDavid Brownell "periodic_IRQ\t: %s\n"
6007be2c7c9SDavid Brownell "update_IRQ\t: %s\n"
601c8626a1dSDavid Brownell "HPET_emulated\t: %s\n"
6027be2c7c9SDavid Brownell // "square_wave\t: %s\n"
6033804a89bSArnaud Patard "BCD\t\t: %s\n"
6047be2c7c9SDavid Brownell "DST_enable\t: %s\n"
6057be2c7c9SDavid Brownell "periodic_freq\t: %d\n"
6067be2c7c9SDavid Brownell "batt_status\t: %s\n",
6077be2c7c9SDavid Brownell (rtc_control & RTC_PIE) ? "yes" : "no",
6087be2c7c9SDavid Brownell (rtc_control & RTC_UIE) ? "yes" : "no",
609311ee9c1SZhang Rui use_hpet_alarm() ? "yes" : "no",
6107be2c7c9SDavid Brownell // (rtc_control & RTC_SQWE) ? "yes" : "no",
6113804a89bSArnaud Patard (rtc_control & RTC_DM_BINARY) ? "no" : "yes",
6127be2c7c9SDavid Brownell (rtc_control & RTC_DST_EN) ? "yes" : "no",
6137be2c7c9SDavid Brownell cmos->rtc->irq_freq,
6147be2c7c9SDavid Brownell (valid & RTC_VRT) ? "okay" : "dead");
6154395eb1fSJoe Perches
6164395eb1fSJoe Perches return 0;
6177be2c7c9SDavid Brownell }
6187be2c7c9SDavid Brownell
6197be2c7c9SDavid Brownell #else
6207be2c7c9SDavid Brownell #define cmos_procfs NULL
6217be2c7c9SDavid Brownell #endif
6227be2c7c9SDavid Brownell
6237be2c7c9SDavid Brownell static const struct rtc_class_ops cmos_rtc_ops = {
6247be2c7c9SDavid Brownell .read_time = cmos_read_time,
6257be2c7c9SDavid Brownell .set_time = cmos_set_time,
6267be2c7c9SDavid Brownell .read_alarm = cmos_read_alarm,
6277be2c7c9SDavid Brownell .set_alarm = cmos_set_alarm,
6287be2c7c9SDavid Brownell .proc = cmos_procfs,
629a8462ef6SHerton Ronaldo Krzesinski .alarm_irq_enable = cmos_alarm_irq_enable,
6307be2c7c9SDavid Brownell };
6317be2c7c9SDavid Brownell
6327be2c7c9SDavid Brownell /*----------------------------------------------------------------*/
6337be2c7c9SDavid Brownell
634e07e232cSDavid Brownell /*
635e07e232cSDavid Brownell * All these chips have at least 64 bytes of address space, shared by
636e07e232cSDavid Brownell * RTC registers and NVRAM. Most of those bytes of NVRAM are used
637e07e232cSDavid Brownell * by boot firmware. Modern chips have 128 or 256 bytes.
638e07e232cSDavid Brownell */
639e07e232cSDavid Brownell
640e07e232cSDavid Brownell #define NVRAM_OFFSET (RTC_REG_D + 1)
641e07e232cSDavid Brownell
cmos_nvram_read(void * priv,unsigned int off,void * val,size_t count)6428b5b7958SAlexandre Belloni static int cmos_nvram_read(void *priv, unsigned int off, void *val,
6438b5b7958SAlexandre Belloni size_t count)
644e07e232cSDavid Brownell {
6458b5b7958SAlexandre Belloni unsigned char *buf = val;
646e07e232cSDavid Brownell
647c8fc40cdSDavid Brownell off += NVRAM_OFFSET;
648*79f1a5b1SDmitry Torokhov for (; count; count--, off++, buf++) {
649*79f1a5b1SDmitry Torokhov guard(spinlock_irq)(&rtc_lock);
650c8fc40cdSDavid Brownell if (off < 128)
651*79f1a5b1SDmitry Torokhov *buf = CMOS_READ(off);
652c8fc40cdSDavid Brownell else if (can_bank2)
653*79f1a5b1SDmitry Torokhov *buf = cmos_read_bank2(off);
654c8fc40cdSDavid Brownell else
655*79f1a5b1SDmitry Torokhov return -EIO;
656c8fc40cdSDavid Brownell }
657e07e232cSDavid Brownell
658*79f1a5b1SDmitry Torokhov return 0;
659e07e232cSDavid Brownell }
660e07e232cSDavid Brownell
cmos_nvram_write(void * priv,unsigned int off,void * val,size_t count)6618b5b7958SAlexandre Belloni static int cmos_nvram_write(void *priv, unsigned int off, void *val,
6628b5b7958SAlexandre Belloni size_t count)
663e07e232cSDavid Brownell {
6648b5b7958SAlexandre Belloni struct cmos_rtc *cmos = priv;
6658b5b7958SAlexandre Belloni unsigned char *buf = val;
666e07e232cSDavid Brownell
667e07e232cSDavid Brownell /* NOTE: on at least PCs and Ataris, the boot firmware uses a
668e07e232cSDavid Brownell * checksum on part of the NVRAM data. That's currently ignored
669e07e232cSDavid Brownell * here. If userspace is smart enough to know what fields of
670e07e232cSDavid Brownell * NVRAM to update, updating checksums is also part of its job.
671e07e232cSDavid Brownell */
672c8fc40cdSDavid Brownell off += NVRAM_OFFSET;
673*79f1a5b1SDmitry Torokhov for (; count; count--, off++, buf++) {
674e07e232cSDavid Brownell /* don't trash RTC registers */
675e07e232cSDavid Brownell if (off == cmos->day_alrm
676e07e232cSDavid Brownell || off == cmos->mon_alrm
677e07e232cSDavid Brownell || off == cmos->century)
678*79f1a5b1SDmitry Torokhov continue;
679e07e232cSDavid Brownell
680*79f1a5b1SDmitry Torokhov guard(spinlock_irq)(&rtc_lock);
681*79f1a5b1SDmitry Torokhov if (off < 128)
682*79f1a5b1SDmitry Torokhov CMOS_WRITE(*buf, off);
683*79f1a5b1SDmitry Torokhov else if (can_bank2)
684*79f1a5b1SDmitry Torokhov cmos_write_bank2(*buf, off);
685*79f1a5b1SDmitry Torokhov else
686*79f1a5b1SDmitry Torokhov return -EIO;
687*79f1a5b1SDmitry Torokhov }
688*79f1a5b1SDmitry Torokhov
689*79f1a5b1SDmitry Torokhov return 0;
690e07e232cSDavid Brownell }
691e07e232cSDavid Brownell
692e07e232cSDavid Brownell /*----------------------------------------------------------------*/
693e07e232cSDavid Brownell
6947be2c7c9SDavid Brownell static struct cmos_rtc cmos_rtc;
6957be2c7c9SDavid Brownell
cmos_interrupt(int irq,void * p)6967be2c7c9SDavid Brownell static irqreturn_t cmos_interrupt(int irq, void *p)
6977be2c7c9SDavid Brownell {
6987be2c7c9SDavid Brownell u8 irqstat;
6998a0bdfd7SDavid Brownell u8 rtc_control;
7007be2c7c9SDavid Brownell
7016950d046SXiaofei Tan spin_lock(&rtc_lock);
70235d3fdd5SDavid Brownell
70335d3fdd5SDavid Brownell /* When the HPET interrupt handler calls us, the interrupt
70435d3fdd5SDavid Brownell * status is passed as arg1 instead of the irq number. But
70535d3fdd5SDavid Brownell * always clear irq status, even when HPET is in the way.
70635d3fdd5SDavid Brownell *
70735d3fdd5SDavid Brownell * Note that HPET and RTC are almost certainly out of phase,
70835d3fdd5SDavid Brownell * giving different IRQ status ...
7099d8af78bSBernhard Walle */
7107be2c7c9SDavid Brownell irqstat = CMOS_READ(RTC_INTR_FLAGS);
7118a0bdfd7SDavid Brownell rtc_control = CMOS_READ(RTC_CONTROL);
712311ee9c1SZhang Rui if (use_hpet_alarm())
71335d3fdd5SDavid Brownell irqstat = (unsigned long)irq & 0xF0;
714998a0605SDerek Basehore
715998a0605SDerek Basehore /* If we were suspended, RTC_CONTROL may not be accurate since the
716998a0605SDerek Basehore * bios may have cleared it.
717998a0605SDerek Basehore */
718998a0605SDerek Basehore if (!cmos_rtc.suspend_ctrl)
7198a0bdfd7SDavid Brownell irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
720998a0605SDerek Basehore else
721998a0605SDerek Basehore irqstat &= (cmos_rtc.suspend_ctrl & RTC_IRQMASK) | RTC_IRQF;
7228a0bdfd7SDavid Brownell
7238a0bdfd7SDavid Brownell /* All Linux RTC alarms should be treated as if they were oneshot.
7248a0bdfd7SDavid Brownell * Similar code may be needed in system wakeup paths, in case the
7258a0bdfd7SDavid Brownell * alarm woke the system.
7268a0bdfd7SDavid Brownell */
7278a0bdfd7SDavid Brownell if (irqstat & RTC_AIE) {
728998a0605SDerek Basehore cmos_rtc.suspend_ctrl &= ~RTC_AIE;
7298a0bdfd7SDavid Brownell rtc_control &= ~RTC_AIE;
7308a0bdfd7SDavid Brownell CMOS_WRITE(rtc_control, RTC_CONTROL);
731311ee9c1SZhang Rui if (use_hpet_alarm())
73235d3fdd5SDavid Brownell hpet_mask_rtc_irq_bit(RTC_AIE);
7338a0bdfd7SDavid Brownell CMOS_READ(RTC_INTR_FLAGS);
7348a0bdfd7SDavid Brownell }
7356950d046SXiaofei Tan spin_unlock(&rtc_lock);
7367be2c7c9SDavid Brownell
737bcd9b89cSDavid Brownell if (is_intr(irqstat)) {
7387be2c7c9SDavid Brownell rtc_update_irq(p, 1, irqstat);
7397be2c7c9SDavid Brownell return IRQ_HANDLED;
7407be2c7c9SDavid Brownell } else
7417be2c7c9SDavid Brownell return IRQ_NONE;
7427be2c7c9SDavid Brownell }
7437be2c7c9SDavid Brownell
744dca4d3b7SRafael J. Wysocki #ifdef CONFIG_ACPI
745dca4d3b7SRafael J. Wysocki
746dca4d3b7SRafael J. Wysocki #include <linux/acpi.h>
747dca4d3b7SRafael J. Wysocki
rtc_handler(void * context)748dca4d3b7SRafael J. Wysocki static u32 rtc_handler(void *context)
749dca4d3b7SRafael J. Wysocki {
750dca4d3b7SRafael J. Wysocki struct device *dev = context;
751dca4d3b7SRafael J. Wysocki struct cmos_rtc *cmos = dev_get_drvdata(dev);
752dca4d3b7SRafael J. Wysocki unsigned char rtc_control = 0;
753dca4d3b7SRafael J. Wysocki unsigned char rtc_intr;
754dca4d3b7SRafael J. Wysocki unsigned long flags;
755dca4d3b7SRafael J. Wysocki
756dca4d3b7SRafael J. Wysocki
757dca4d3b7SRafael J. Wysocki /*
758dca4d3b7SRafael J. Wysocki * Always update rtc irq when ACPI is used as RTC Alarm.
759dca4d3b7SRafael J. Wysocki * Or else, ACPI SCI is enabled during suspend/resume only,
760dca4d3b7SRafael J. Wysocki * update rtc irq in that case.
761dca4d3b7SRafael J. Wysocki */
762dca4d3b7SRafael J. Wysocki if (cmos_use_acpi_alarm())
763dca4d3b7SRafael J. Wysocki cmos_interrupt(0, (void *)cmos->rtc);
764dca4d3b7SRafael J. Wysocki else {
765dca4d3b7SRafael J. Wysocki /* Fix me: can we use cmos_interrupt() here as well? */
766dca4d3b7SRafael J. Wysocki spin_lock_irqsave(&rtc_lock, flags);
767dca4d3b7SRafael J. Wysocki if (cmos_rtc.suspend_ctrl)
768dca4d3b7SRafael J. Wysocki rtc_control = CMOS_READ(RTC_CONTROL);
769dca4d3b7SRafael J. Wysocki if (rtc_control & RTC_AIE) {
770dca4d3b7SRafael J. Wysocki cmos_rtc.suspend_ctrl &= ~RTC_AIE;
771dca4d3b7SRafael J. Wysocki CMOS_WRITE(rtc_control, RTC_CONTROL);
772dca4d3b7SRafael J. Wysocki rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
773dca4d3b7SRafael J. Wysocki rtc_update_irq(cmos->rtc, 1, rtc_intr);
774dca4d3b7SRafael J. Wysocki }
775dca4d3b7SRafael J. Wysocki spin_unlock_irqrestore(&rtc_lock, flags);
776dca4d3b7SRafael J. Wysocki }
777dca4d3b7SRafael J. Wysocki
778dca4d3b7SRafael J. Wysocki pm_wakeup_hard_event(dev);
779dca4d3b7SRafael J. Wysocki acpi_clear_event(ACPI_EVENT_RTC);
780dca4d3b7SRafael J. Wysocki acpi_disable_event(ACPI_EVENT_RTC, 0);
781dca4d3b7SRafael J. Wysocki return ACPI_INTERRUPT_HANDLED;
782dca4d3b7SRafael J. Wysocki }
783dca4d3b7SRafael J. Wysocki
acpi_rtc_event_setup(struct device * dev)784d13e9ad9SRafael J. Wysocki static void acpi_rtc_event_setup(struct device *dev)
785dca4d3b7SRafael J. Wysocki {
786dca4d3b7SRafael J. Wysocki if (acpi_disabled)
787dca4d3b7SRafael J. Wysocki return;
788dca4d3b7SRafael J. Wysocki
789dca4d3b7SRafael J. Wysocki acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, dev);
790dca4d3b7SRafael J. Wysocki /*
791dca4d3b7SRafael J. Wysocki * After the RTC handler is installed, the Fixed_RTC event should
792dca4d3b7SRafael J. Wysocki * be disabled. Only when the RTC alarm is set will it be enabled.
793dca4d3b7SRafael J. Wysocki */
794dca4d3b7SRafael J. Wysocki acpi_clear_event(ACPI_EVENT_RTC);
795dca4d3b7SRafael J. Wysocki acpi_disable_event(ACPI_EVENT_RTC, 0);
796dca4d3b7SRafael J. Wysocki }
797dca4d3b7SRafael J. Wysocki
acpi_rtc_event_cleanup(void)79883ebb7b3SRafael J. Wysocki static void acpi_rtc_event_cleanup(void)
79983ebb7b3SRafael J. Wysocki {
80083ebb7b3SRafael J. Wysocki if (acpi_disabled)
80183ebb7b3SRafael J. Wysocki return;
80283ebb7b3SRafael J. Wysocki
80383ebb7b3SRafael J. Wysocki acpi_remove_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler);
80483ebb7b3SRafael J. Wysocki }
80583ebb7b3SRafael J. Wysocki
rtc_wake_on(struct device * dev)806dca4d3b7SRafael J. Wysocki static void rtc_wake_on(struct device *dev)
807dca4d3b7SRafael J. Wysocki {
808dca4d3b7SRafael J. Wysocki acpi_clear_event(ACPI_EVENT_RTC);
809dca4d3b7SRafael J. Wysocki acpi_enable_event(ACPI_EVENT_RTC, 0);
810dca4d3b7SRafael J. Wysocki }
811dca4d3b7SRafael J. Wysocki
rtc_wake_off(struct device * dev)812dca4d3b7SRafael J. Wysocki static void rtc_wake_off(struct device *dev)
813dca4d3b7SRafael J. Wysocki {
814dca4d3b7SRafael J. Wysocki acpi_disable_event(ACPI_EVENT_RTC, 0);
815dca4d3b7SRafael J. Wysocki }
816dca4d3b7SRafael J. Wysocki
817dca4d3b7SRafael J. Wysocki #ifdef CONFIG_X86
use_acpi_alarm_quirks(void)818dca4d3b7SRafael J. Wysocki static void use_acpi_alarm_quirks(void)
819dca4d3b7SRafael J. Wysocki {
82048451458SMario Limonciello switch (boot_cpu_data.x86_vendor) {
82148451458SMario Limonciello case X86_VENDOR_INTEL:
822dca4d3b7SRafael J. Wysocki if (dmi_get_bios_year() < 2015)
823dca4d3b7SRafael J. Wysocki return;
82448451458SMario Limonciello break;
82548451458SMario Limonciello case X86_VENDOR_AMD:
82648451458SMario Limonciello case X86_VENDOR_HYGON:
82748451458SMario Limonciello if (dmi_get_bios_year() < 2021)
82848451458SMario Limonciello return;
82948451458SMario Limonciello break;
83048451458SMario Limonciello default:
83148451458SMario Limonciello return;
83248451458SMario Limonciello }
83348451458SMario Limonciello if (!is_hpet_enabled())
83448451458SMario Limonciello return;
835dca4d3b7SRafael J. Wysocki
836dca4d3b7SRafael J. Wysocki use_acpi_alarm = true;
837dca4d3b7SRafael J. Wysocki }
838dca4d3b7SRafael J. Wysocki #else
use_acpi_alarm_quirks(void)839dca4d3b7SRafael J. Wysocki static inline void use_acpi_alarm_quirks(void) { }
840dca4d3b7SRafael J. Wysocki #endif
841dca4d3b7SRafael J. Wysocki
acpi_cmos_wake_setup(struct device * dev)842d13e9ad9SRafael J. Wysocki static void acpi_cmos_wake_setup(struct device *dev)
843dca4d3b7SRafael J. Wysocki {
844dca4d3b7SRafael J. Wysocki if (acpi_disabled)
845dca4d3b7SRafael J. Wysocki return;
846dca4d3b7SRafael J. Wysocki
847dca4d3b7SRafael J. Wysocki use_acpi_alarm_quirks();
848dca4d3b7SRafael J. Wysocki
849dca4d3b7SRafael J. Wysocki cmos_rtc.wake_on = rtc_wake_on;
850dca4d3b7SRafael J. Wysocki cmos_rtc.wake_off = rtc_wake_off;
851dca4d3b7SRafael J. Wysocki
852dca4d3b7SRafael J. Wysocki /* ACPI tables bug workaround. */
853dca4d3b7SRafael J. Wysocki if (acpi_gbl_FADT.month_alarm && !acpi_gbl_FADT.day_alarm) {
854dca4d3b7SRafael J. Wysocki dev_dbg(dev, "bogus FADT month_alarm (%d)\n",
855dca4d3b7SRafael J. Wysocki acpi_gbl_FADT.month_alarm);
856dca4d3b7SRafael J. Wysocki acpi_gbl_FADT.month_alarm = 0;
857dca4d3b7SRafael J. Wysocki }
858dca4d3b7SRafael J. Wysocki
859dca4d3b7SRafael J. Wysocki cmos_rtc.day_alrm = acpi_gbl_FADT.day_alarm;
860dca4d3b7SRafael J. Wysocki cmos_rtc.mon_alrm = acpi_gbl_FADT.month_alarm;
861dca4d3b7SRafael J. Wysocki cmos_rtc.century = acpi_gbl_FADT.century;
862dca4d3b7SRafael J. Wysocki
863dca4d3b7SRafael J. Wysocki if (acpi_gbl_FADT.flags & ACPI_FADT_S4_RTC_WAKE)
864dca4d3b7SRafael J. Wysocki dev_info(dev, "RTC can wake from S4\n");
865dca4d3b7SRafael J. Wysocki
866dca4d3b7SRafael J. Wysocki /* RTC always wakes from S1/S2/S3, and often S4/STD */
867dca4d3b7SRafael J. Wysocki device_init_wakeup(dev, 1);
868dca4d3b7SRafael J. Wysocki }
869dca4d3b7SRafael J. Wysocki
cmos_check_acpi_rtc_status(struct device * dev,unsigned char * rtc_control)870dca4d3b7SRafael J. Wysocki static void cmos_check_acpi_rtc_status(struct device *dev,
871dca4d3b7SRafael J. Wysocki unsigned char *rtc_control)
872dca4d3b7SRafael J. Wysocki {
873dca4d3b7SRafael J. Wysocki struct cmos_rtc *cmos = dev_get_drvdata(dev);
874dca4d3b7SRafael J. Wysocki acpi_event_status rtc_status;
875dca4d3b7SRafael J. Wysocki acpi_status status;
876dca4d3b7SRafael J. Wysocki
877dca4d3b7SRafael J. Wysocki if (acpi_gbl_FADT.flags & ACPI_FADT_FIXED_RTC)
878dca4d3b7SRafael J. Wysocki return;
879dca4d3b7SRafael J. Wysocki
880dca4d3b7SRafael J. Wysocki status = acpi_get_event_status(ACPI_EVENT_RTC, &rtc_status);
881dca4d3b7SRafael J. Wysocki if (ACPI_FAILURE(status)) {
882dca4d3b7SRafael J. Wysocki dev_err(dev, "Could not get RTC status\n");
883dca4d3b7SRafael J. Wysocki } else if (rtc_status & ACPI_EVENT_FLAG_SET) {
884dca4d3b7SRafael J. Wysocki unsigned char mask;
885dca4d3b7SRafael J. Wysocki *rtc_control &= ~RTC_AIE;
886dca4d3b7SRafael J. Wysocki CMOS_WRITE(*rtc_control, RTC_CONTROL);
887dca4d3b7SRafael J. Wysocki mask = CMOS_READ(RTC_INTR_FLAGS);
888dca4d3b7SRafael J. Wysocki rtc_update_irq(cmos->rtc, 1, mask);
889dca4d3b7SRafael J. Wysocki }
890dca4d3b7SRafael J. Wysocki }
891dca4d3b7SRafael J. Wysocki
892dca4d3b7SRafael J. Wysocki #else /* !CONFIG_ACPI */
893dca4d3b7SRafael J. Wysocki
acpi_rtc_event_setup(struct device * dev)894d13e9ad9SRafael J. Wysocki static inline void acpi_rtc_event_setup(struct device *dev)
895dca4d3b7SRafael J. Wysocki {
896dca4d3b7SRafael J. Wysocki }
897dca4d3b7SRafael J. Wysocki
acpi_rtc_event_cleanup(void)89883ebb7b3SRafael J. Wysocki static inline void acpi_rtc_event_cleanup(void)
89983ebb7b3SRafael J. Wysocki {
90083ebb7b3SRafael J. Wysocki }
90183ebb7b3SRafael J. Wysocki
acpi_cmos_wake_setup(struct device * dev)902d13e9ad9SRafael J. Wysocki static inline void acpi_cmos_wake_setup(struct device *dev)
903dca4d3b7SRafael J. Wysocki {
904dca4d3b7SRafael J. Wysocki }
905dca4d3b7SRafael J. Wysocki
cmos_check_acpi_rtc_status(struct device * dev,unsigned char * rtc_control)906dca4d3b7SRafael J. Wysocki static inline void cmos_check_acpi_rtc_status(struct device *dev,
907dca4d3b7SRafael J. Wysocki unsigned char *rtc_control)
908dca4d3b7SRafael J. Wysocki {
909dca4d3b7SRafael J. Wysocki }
910dca4d3b7SRafael J. Wysocki #endif /* CONFIG_ACPI */
911508ccdfbSRafael J. Wysocki
91241ac8df9SMarko Vrh #ifdef CONFIG_PNP
9137be2c7c9SDavid Brownell #define INITSECTION
9147be2c7c9SDavid Brownell
9157be2c7c9SDavid Brownell #else
9167be2c7c9SDavid Brownell #define INITSECTION __init
9177be2c7c9SDavid Brownell #endif
9187be2c7c9SDavid Brownell
9192546e708SGuenter Roeck #define SECS_PER_DAY (24 * 60 * 60)
9202546e708SGuenter Roeck #define SECS_PER_MONTH (28 * SECS_PER_DAY)
9212546e708SGuenter Roeck #define SECS_PER_YEAR (365 * SECS_PER_DAY)
9222546e708SGuenter Roeck
9237be2c7c9SDavid Brownell static int INITSECTION
cmos_do_probe(struct device * dev,struct resource * ports,int rtc_irq)9247be2c7c9SDavid Brownell cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
9257be2c7c9SDavid Brownell {
92697a92e77SJingoo Han struct cmos_rtc_board_info *info = dev_get_platdata(dev);
9277be2c7c9SDavid Brownell int retval = 0;
9287be2c7c9SDavid Brownell unsigned char rtc_control;
929e07e232cSDavid Brownell unsigned address_space;
93031632dbdSMaciej W. Rozycki u32 flags = 0;
9318b5b7958SAlexandre Belloni struct nvmem_config nvmem_cfg = {
9328b5b7958SAlexandre Belloni .name = "cmos_nvram",
9338b5b7958SAlexandre Belloni .word_size = 1,
9348b5b7958SAlexandre Belloni .stride = 1,
9358b5b7958SAlexandre Belloni .reg_read = cmos_nvram_read,
9368b5b7958SAlexandre Belloni .reg_write = cmos_nvram_write,
9378b5b7958SAlexandre Belloni .priv = &cmos_rtc,
9388b5b7958SAlexandre Belloni };
9397be2c7c9SDavid Brownell
9407be2c7c9SDavid Brownell /* there can be only one ... */
9417be2c7c9SDavid Brownell if (cmos_rtc.dev)
9427be2c7c9SDavid Brownell return -EBUSY;
9437be2c7c9SDavid Brownell
9447be2c7c9SDavid Brownell if (!ports)
9457be2c7c9SDavid Brownell return -ENODEV;
9467be2c7c9SDavid Brownell
94705440dfcSDavid Brownell /* Claim I/O ports ASAP, minimizing conflict with legacy driver.
94805440dfcSDavid Brownell *
94905440dfcSDavid Brownell * REVISIT non-x86 systems may instead use memory space resources
95005440dfcSDavid Brownell * (needing ioremap etc), not i/o space resources like this ...
95105440dfcSDavid Brownell */
95231632dbdSMaciej W. Rozycki if (RTC_IOMAPPED)
95331632dbdSMaciej W. Rozycki ports = request_region(ports->start, resource_size(ports),
95431632dbdSMaciej W. Rozycki driver_name);
95531632dbdSMaciej W. Rozycki else
95631632dbdSMaciej W. Rozycki ports = request_mem_region(ports->start, resource_size(ports),
95705440dfcSDavid Brownell driver_name);
95805440dfcSDavid Brownell if (!ports) {
95905440dfcSDavid Brownell dev_dbg(dev, "i/o registers already in use\n");
96005440dfcSDavid Brownell return -EBUSY;
96105440dfcSDavid Brownell }
96205440dfcSDavid Brownell
9637be2c7c9SDavid Brownell cmos_rtc.irq = rtc_irq;
9647be2c7c9SDavid Brownell cmos_rtc.iomem = ports;
9657be2c7c9SDavid Brownell
966e07e232cSDavid Brownell /* Heuristic to deduce NVRAM size ... do what the legacy NVRAM
967e07e232cSDavid Brownell * driver did, but don't reject unknown configs. Old hardware
968c8fc40cdSDavid Brownell * won't address 128 bytes. Newer chips have multiple banks,
969c8fc40cdSDavid Brownell * though they may not be listed in one I/O resource.
970e07e232cSDavid Brownell */
971e07e232cSDavid Brownell #if defined(CONFIG_ATARI)
972e07e232cSDavid Brownell address_space = 64;
97395abd0dfSWu Zhangjin #elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) \
9748cb7c71bSSrikanth Krishnakar || defined(__sparc__) || defined(__mips__) \
975739d875dSDavid Howells || defined(__powerpc__)
976e07e232cSDavid Brownell address_space = 128;
977e07e232cSDavid Brownell #else
978e07e232cSDavid Brownell #warning Assuming 128 bytes of RTC+NVRAM address space, not 64 bytes.
979e07e232cSDavid Brownell address_space = 128;
980e07e232cSDavid Brownell #endif
981c8fc40cdSDavid Brownell if (can_bank2 && ports->end > (ports->start + 1))
982c8fc40cdSDavid Brownell address_space = 256;
983e07e232cSDavid Brownell
98487ac84f4SDavid Brownell /* For ACPI systems extension info comes from the FADT. On others,
98587ac84f4SDavid Brownell * board specific setup provides it as appropriate. Systems where
98687ac84f4SDavid Brownell * the alarm IRQ isn't automatically a wakeup IRQ (like ACPI, and
98787ac84f4SDavid Brownell * some almost-clones) can provide hooks to make that behave.
988e07e232cSDavid Brownell *
989e07e232cSDavid Brownell * Note that ACPI doesn't preclude putting these registers into
990e07e232cSDavid Brownell * "extended" areas of the chip, including some that we won't yet
991e07e232cSDavid Brownell * expect CMOS_READ and friends to handle.
9927be2c7c9SDavid Brownell */
9937be2c7c9SDavid Brownell if (info) {
99431632dbdSMaciej W. Rozycki if (info->flags)
99531632dbdSMaciej W. Rozycki flags = info->flags;
99631632dbdSMaciej W. Rozycki if (info->address_space)
99731632dbdSMaciej W. Rozycki address_space = info->address_space;
99831632dbdSMaciej W. Rozycki
9997be2c7c9SDavid Brownell cmos_rtc.day_alrm = info->rtc_day_alarm;
10007be2c7c9SDavid Brownell cmos_rtc.mon_alrm = info->rtc_mon_alarm;
10017be2c7c9SDavid Brownell cmos_rtc.century = info->rtc_century;
100287ac84f4SDavid Brownell
100387ac84f4SDavid Brownell if (info->wake_on && info->wake_off) {
100487ac84f4SDavid Brownell cmos_rtc.wake_on = info->wake_on;
100587ac84f4SDavid Brownell cmos_rtc.wake_off = info->wake_off;
100687ac84f4SDavid Brownell }
1007508ccdfbSRafael J. Wysocki } else {
1008d13e9ad9SRafael J. Wysocki acpi_cmos_wake_setup(dev);
10097be2c7c9SDavid Brownell }
10107be2c7c9SDavid Brownell
1011508ccdfbSRafael J. Wysocki if (cmos_rtc.day_alrm >= 128)
1012508ccdfbSRafael J. Wysocki cmos_rtc.day_alrm = 0;
1013508ccdfbSRafael J. Wysocki
1014508ccdfbSRafael J. Wysocki if (cmos_rtc.mon_alrm >= 128)
1015508ccdfbSRafael J. Wysocki cmos_rtc.mon_alrm = 0;
1016508ccdfbSRafael J. Wysocki
1017508ccdfbSRafael J. Wysocki if (cmos_rtc.century >= 128)
1018508ccdfbSRafael J. Wysocki cmos_rtc.century = 0;
1019508ccdfbSRafael J. Wysocki
10206ba8bcd4SDan Carpenter cmos_rtc.dev = dev;
10216ba8bcd4SDan Carpenter dev_set_drvdata(dev, &cmos_rtc);
10226ba8bcd4SDan Carpenter
102353d29e0aSAlexandre Belloni cmos_rtc.rtc = devm_rtc_allocate_device(dev);
102405440dfcSDavid Brownell if (IS_ERR(cmos_rtc.rtc)) {
102505440dfcSDavid Brownell retval = PTR_ERR(cmos_rtc.rtc);
102605440dfcSDavid Brownell goto cleanup0;
102705440dfcSDavid Brownell }
10287be2c7c9SDavid Brownell
10292546e708SGuenter Roeck if (cmos_rtc.mon_alrm)
10302546e708SGuenter Roeck cmos_rtc.rtc->alarm_offset_max = SECS_PER_YEAR - 1;
10312546e708SGuenter Roeck else if (cmos_rtc.day_alrm)
10322546e708SGuenter Roeck cmos_rtc.rtc->alarm_offset_max = SECS_PER_MONTH - 1;
10332546e708SGuenter Roeck else
10342546e708SGuenter Roeck cmos_rtc.rtc->alarm_offset_max = SECS_PER_DAY - 1;
10352546e708SGuenter Roeck
1036d4afc76cSKay Sievers rename_region(ports, dev_name(&cmos_rtc.rtc->dev));
10377be2c7c9SDavid Brownell
1038ea6fa496SMateusz Jończyk if (!mc146818_does_rtc_work()) {
1039ea6fa496SMateusz Jończyk dev_warn(dev, "broken or not accessible\n");
1040211e5db1SThomas Gleixner retval = -ENXIO;
1041211e5db1SThomas Gleixner goto cleanup1;
1042211e5db1SThomas Gleixner }
1043211e5db1SThomas Gleixner
1044ea6fa496SMateusz Jończyk spin_lock_irq(&rtc_lock);
1045ea6fa496SMateusz Jończyk
104631632dbdSMaciej W. Rozycki if (!(flags & CMOS_RTC_FLAGS_NOFREQ)) {
10477be2c7c9SDavid Brownell /* force periodic irq to CMOS reset default of 1024Hz;
10487be2c7c9SDavid Brownell *
104931632dbdSMaciej W. Rozycki * REVISIT it's been reported that at least one x86_64 ALI
105031632dbdSMaciej W. Rozycki * mobo doesn't use 32KHz here ... for portability we might
105131632dbdSMaciej W. Rozycki * need to do something about other clock frequencies.
10527be2c7c9SDavid Brownell */
10537be2c7c9SDavid Brownell cmos_rtc.rtc->irq_freq = 1024;
1054311ee9c1SZhang Rui if (use_hpet_alarm())
105535d3fdd5SDavid Brownell hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq);
10569d8af78bSBernhard Walle CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT);
105731632dbdSMaciej W. Rozycki }
10587be2c7c9SDavid Brownell
10597e2a31daSDavid Brownell /* disable irqs */
106031632dbdSMaciej W. Rozycki if (is_valid_irq(rtc_irq))
10617e2a31daSDavid Brownell cmos_irq_disable(&cmos_rtc, RTC_PIE | RTC_AIE | RTC_UIE);
106235d3fdd5SDavid Brownell
10637e2a31daSDavid Brownell rtc_control = CMOS_READ(RTC_CONTROL);
10647be2c7c9SDavid Brownell
10657be2c7c9SDavid Brownell spin_unlock_irq(&rtc_lock);
10667be2c7c9SDavid Brownell
10673804a89bSArnaud Patard if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) {
10683804a89bSArnaud Patard dev_warn(dev, "only 24-hr supported\n");
10697be2c7c9SDavid Brownell retval = -ENXIO;
10707be2c7c9SDavid Brownell goto cleanup1;
10717be2c7c9SDavid Brownell }
10727be2c7c9SDavid Brownell
1073311ee9c1SZhang Rui if (use_hpet_alarm())
1074970fc7f4SPratyush Anand hpet_rtc_timer_init();
1075970fc7f4SPratyush Anand
10769d8af78bSBernhard Walle if (is_valid_irq(rtc_irq)) {
10779d8af78bSBernhard Walle irq_handler_t rtc_cmos_int_handler;
10789d8af78bSBernhard Walle
1079311ee9c1SZhang Rui if (use_hpet_alarm()) {
10809d8af78bSBernhard Walle rtc_cmos_int_handler = hpet_rtc_interrupt;
108124b34472SAndrew Morton retval = hpet_register_irq_handler(cmos_interrupt);
108224b34472SAndrew Morton if (retval) {
1083970fc7f4SPratyush Anand hpet_mask_rtc_irq_bit(RTC_IRQMASK);
1084ee443357SJingoo Han dev_warn(dev, "hpet_register_irq_handler "
10859d8af78bSBernhard Walle " failed in rtc_init().");
10869d8af78bSBernhard Walle goto cleanup1;
10879d8af78bSBernhard Walle }
10889d8af78bSBernhard Walle } else
10899d8af78bSBernhard Walle rtc_cmos_int_handler = cmos_interrupt;
10909d8af78bSBernhard Walle
10919d8af78bSBernhard Walle retval = request_irq(rtc_irq, rtc_cmos_int_handler,
1092b6da197aSAndy Shevchenko 0, dev_name(&cmos_rtc.rtc->dev),
1093ab6a2d70SDavid Brownell cmos_rtc.rtc);
10947be2c7c9SDavid Brownell if (retval < 0) {
10957be2c7c9SDavid Brownell dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq);
10967be2c7c9SDavid Brownell goto cleanup1;
10977be2c7c9SDavid Brownell }
109830f5bd53SAlexandre Belloni } else {
109930f5bd53SAlexandre Belloni clear_bit(RTC_FEATURE_ALARM, cmos_rtc.rtc->features);
110030f5bd53SAlexandre Belloni }
11017be2c7c9SDavid Brownell
110253d29e0aSAlexandre Belloni cmos_rtc.rtc->ops = &cmos_rtc_ops;
1103fbb974baSHans de Goede
1104fdcfd854SBartosz Golaszewski retval = devm_rtc_register_device(cmos_rtc.rtc);
110553d29e0aSAlexandre Belloni if (retval)
1106e07e232cSDavid Brownell goto cleanup2;
11077be2c7c9SDavid Brownell
1108b0ecd8e8SThomas Gleixner /* Set the sync offset for the periodic 11min update correct */
110969eca258SThomas Gleixner cmos_rtc.rtc->set_offset_nsec = NSEC_PER_SEC / 2;
1110b0ecd8e8SThomas Gleixner
11118b5b7958SAlexandre Belloni /* export at least the first block of NVRAM */
11128b5b7958SAlexandre Belloni nvmem_cfg.size = address_space - NVRAM_OFFSET;
11136746bc09SBartosz Golaszewski devm_rtc_nvmem_register(cmos_rtc.rtc, &nvmem_cfg);
11148b5b7958SAlexandre Belloni
1115375bbba0SRafael J. Wysocki /*
1116375bbba0SRafael J. Wysocki * Everything has gone well so far, so by default register a handler for
1117375bbba0SRafael J. Wysocki * the ACPI RTC fixed event.
1118375bbba0SRafael J. Wysocki */
1119375bbba0SRafael J. Wysocki if (!info)
1120d13e9ad9SRafael J. Wysocki acpi_rtc_event_setup(dev);
1121375bbba0SRafael J. Wysocki
11228b5b7958SAlexandre Belloni dev_info(dev, "%s%s, %d bytes nvram%s\n",
11236d029b64SKrzysztof Halasa !is_valid_irq(rtc_irq) ? "no alarms" :
11246d029b64SKrzysztof Halasa cmos_rtc.mon_alrm ? "alarms up to one year" :
11256d029b64SKrzysztof Halasa cmos_rtc.day_alrm ? "alarms up to one month" :
11266d029b64SKrzysztof Halasa "alarms up to one day",
112735d3fdd5SDavid Brownell cmos_rtc.century ? ", y3k" : "",
11288b5b7958SAlexandre Belloni nvmem_cfg.size,
1129311ee9c1SZhang Rui use_hpet_alarm() ? ", hpet irqs" : "");
11307be2c7c9SDavid Brownell
11317be2c7c9SDavid Brownell return 0;
11327be2c7c9SDavid Brownell
1133e07e232cSDavid Brownell cleanup2:
1134e07e232cSDavid Brownell if (is_valid_irq(rtc_irq))
1135e07e232cSDavid Brownell free_irq(rtc_irq, cmos_rtc.rtc);
11367be2c7c9SDavid Brownell cleanup1:
113705440dfcSDavid Brownell cmos_rtc.dev = NULL;
113805440dfcSDavid Brownell cleanup0:
113931632dbdSMaciej W. Rozycki if (RTC_IOMAPPED)
114028f65c11SJoe Perches release_region(ports->start, resource_size(ports));
114131632dbdSMaciej W. Rozycki else
114231632dbdSMaciej W. Rozycki release_mem_region(ports->start, resource_size(ports));
11437be2c7c9SDavid Brownell return retval;
11447be2c7c9SDavid Brownell }
11457be2c7c9SDavid Brownell
cmos_do_shutdown(int rtc_irq)114631632dbdSMaciej W. Rozycki static void cmos_do_shutdown(int rtc_irq)
11477be2c7c9SDavid Brownell {
11487be2c7c9SDavid Brownell spin_lock_irq(&rtc_lock);
114931632dbdSMaciej W. Rozycki if (is_valid_irq(rtc_irq))
11507e2a31daSDavid Brownell cmos_irq_disable(&cmos_rtc, RTC_IRQMASK);
11517be2c7c9SDavid Brownell spin_unlock_irq(&rtc_lock);
11527be2c7c9SDavid Brownell }
11537be2c7c9SDavid Brownell
cmos_do_remove(struct device * dev)1154a3a0673bSLABBE Corentin static void cmos_do_remove(struct device *dev)
11557be2c7c9SDavid Brownell {
11567be2c7c9SDavid Brownell struct cmos_rtc *cmos = dev_get_drvdata(dev);
115705440dfcSDavid Brownell struct resource *ports;
11587be2c7c9SDavid Brownell
115931632dbdSMaciej W. Rozycki cmos_do_shutdown(cmos->irq);
11607be2c7c9SDavid Brownell
11619d8af78bSBernhard Walle if (is_valid_irq(cmos->irq)) {
116205440dfcSDavid Brownell free_irq(cmos->irq, cmos->rtc);
1163311ee9c1SZhang Rui if (use_hpet_alarm())
11649d8af78bSBernhard Walle hpet_unregister_irq_handler(cmos_interrupt);
11659d8af78bSBernhard Walle }
11667be2c7c9SDavid Brownell
116783ebb7b3SRafael J. Wysocki if (!dev_get_platdata(dev))
116883ebb7b3SRafael J. Wysocki acpi_rtc_event_cleanup();
116983ebb7b3SRafael J. Wysocki
117005440dfcSDavid Brownell cmos->rtc = NULL;
11717be2c7c9SDavid Brownell
117205440dfcSDavid Brownell ports = cmos->iomem;
117331632dbdSMaciej W. Rozycki if (RTC_IOMAPPED)
117428f65c11SJoe Perches release_region(ports->start, resource_size(ports));
117531632dbdSMaciej W. Rozycki else
117631632dbdSMaciej W. Rozycki release_mem_region(ports->start, resource_size(ports));
117705440dfcSDavid Brownell cmos->iomem = NULL;
117805440dfcSDavid Brownell
117905440dfcSDavid Brownell cmos->dev = NULL;
11807be2c7c9SDavid Brownell }
11817be2c7c9SDavid Brownell
cmos_aie_poweroff(struct device * dev)118288b8d33bSAdrian Huang static int cmos_aie_poweroff(struct device *dev)
118388b8d33bSAdrian Huang {
118488b8d33bSAdrian Huang struct cmos_rtc *cmos = dev_get_drvdata(dev);
118588b8d33bSAdrian Huang struct rtc_time now;
118688b8d33bSAdrian Huang time64_t t_now;
118788b8d33bSAdrian Huang int retval = 0;
118888b8d33bSAdrian Huang unsigned char rtc_control;
118988b8d33bSAdrian Huang
119088b8d33bSAdrian Huang if (!cmos->alarm_expires)
119188b8d33bSAdrian Huang return -EINVAL;
119288b8d33bSAdrian Huang
119388b8d33bSAdrian Huang spin_lock_irq(&rtc_lock);
119488b8d33bSAdrian Huang rtc_control = CMOS_READ(RTC_CONTROL);
119588b8d33bSAdrian Huang spin_unlock_irq(&rtc_lock);
119688b8d33bSAdrian Huang
119788b8d33bSAdrian Huang /* We only care about the situation where AIE is disabled. */
119888b8d33bSAdrian Huang if (rtc_control & RTC_AIE)
119988b8d33bSAdrian Huang return -EBUSY;
120088b8d33bSAdrian Huang
120188b8d33bSAdrian Huang cmos_read_time(dev, &now);
120288b8d33bSAdrian Huang t_now = rtc_tm_to_time64(&now);
120388b8d33bSAdrian Huang
120488b8d33bSAdrian Huang /*
120588b8d33bSAdrian Huang * When enabling "RTC wake-up" in BIOS setup, the machine reboots
120688b8d33bSAdrian Huang * automatically right after shutdown on some buggy boxes.
120788b8d33bSAdrian Huang * This automatic rebooting issue won't happen when the alarm
120888b8d33bSAdrian Huang * time is larger than now+1 seconds.
120988b8d33bSAdrian Huang *
121088b8d33bSAdrian Huang * If the alarm time is equal to now+1 seconds, the issue can be
121188b8d33bSAdrian Huang * prevented by cancelling the alarm.
121288b8d33bSAdrian Huang */
121388b8d33bSAdrian Huang if (cmos->alarm_expires == t_now + 1) {
121488b8d33bSAdrian Huang struct rtc_wkalrm alarm;
121588b8d33bSAdrian Huang
121688b8d33bSAdrian Huang /* Cancel the AIE timer by configuring the past time. */
121788b8d33bSAdrian Huang rtc_time64_to_tm(t_now - 1, &alarm.time);
121888b8d33bSAdrian Huang alarm.enabled = 0;
121988b8d33bSAdrian Huang retval = cmos_set_alarm(dev, &alarm);
122088b8d33bSAdrian Huang } else if (cmos->alarm_expires > t_now + 1) {
122188b8d33bSAdrian Huang retval = -EBUSY;
122288b8d33bSAdrian Huang }
122388b8d33bSAdrian Huang
122488b8d33bSAdrian Huang return retval;
122588b8d33bSAdrian Huang }
122688b8d33bSAdrian Huang
cmos_suspend(struct device * dev)12272fb08e6cSPaul Fox static int cmos_suspend(struct device *dev)
12287be2c7c9SDavid Brownell {
12297be2c7c9SDavid Brownell struct cmos_rtc *cmos = dev_get_drvdata(dev);
1230bcd9b89cSDavid Brownell unsigned char tmp;
12317be2c7c9SDavid Brownell
12327be2c7c9SDavid Brownell /* only the alarm might be a wakeup event source */
12337be2c7c9SDavid Brownell spin_lock_irq(&rtc_lock);
12347be2c7c9SDavid Brownell cmos->suspend_ctrl = tmp = CMOS_READ(RTC_CONTROL);
12357be2c7c9SDavid Brownell if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) {
123635d3fdd5SDavid Brownell unsigned char mask;
1237bcd9b89cSDavid Brownell
123874c4633dSRafael J. Wysocki if (device_may_wakeup(dev))
123935d3fdd5SDavid Brownell mask = RTC_IRQMASK & ~RTC_AIE;
12407be2c7c9SDavid Brownell else
124135d3fdd5SDavid Brownell mask = RTC_IRQMASK;
124235d3fdd5SDavid Brownell tmp &= ~mask;
12437be2c7c9SDavid Brownell CMOS_WRITE(tmp, RTC_CONTROL);
1244311ee9c1SZhang Rui if (use_hpet_alarm())
1245e005715eSDerek Basehore hpet_mask_rtc_irq_bit(mask);
12467e2a31daSDavid Brownell cmos_checkintr(cmos, tmp);
1247bcd9b89cSDavid Brownell }
1248bcd9b89cSDavid Brownell spin_unlock_irq(&rtc_lock);
12497be2c7c9SDavid Brownell
1250bc51098cSMaciej W. Rozycki if ((tmp & RTC_AIE) && !cmos_use_acpi_alarm()) {
125187ac84f4SDavid Brownell cmos->enabled_wake = 1;
125287ac84f4SDavid Brownell if (cmos->wake_on)
125387ac84f4SDavid Brownell cmos->wake_on(dev);
125487ac84f4SDavid Brownell else
125587ac84f4SDavid Brownell enable_irq_wake(cmos->irq);
125687ac84f4SDavid Brownell }
12577be2c7c9SDavid Brownell
1258c254bcd7SVictor Ding memset(&cmos->saved_wkalrm, 0, sizeof(struct rtc_wkalrm));
125968669d55SGabriele Mazzotta cmos_read_alarm(dev, &cmos->saved_wkalrm);
126068669d55SGabriele Mazzotta
1261ee443357SJingoo Han dev_dbg(dev, "suspend%s, ctrl %02x\n",
12627be2c7c9SDavid Brownell (tmp & RTC_AIE) ? ", alarm may wake" : "",
12637be2c7c9SDavid Brownell tmp);
12647be2c7c9SDavid Brownell
12657be2c7c9SDavid Brownell return 0;
12667be2c7c9SDavid Brownell }
12677be2c7c9SDavid Brownell
126874c4633dSRafael J. Wysocki /* We want RTC alarms to wake us from e.g. ACPI G2/S5 "soft off", even
126974c4633dSRafael J. Wysocki * after a detour through G3 "mechanical off", although the ACPI spec
127074c4633dSRafael J. Wysocki * says wakeup should only work from G1/S4 "hibernate". To most users,
127174c4633dSRafael J. Wysocki * distinctions between S4 and S5 are pointless. So when the hardware
127274c4633dSRafael J. Wysocki * allows, don't draw that distinction.
127374c4633dSRafael J. Wysocki */
cmos_poweroff(struct device * dev)127474c4633dSRafael J. Wysocki static inline int cmos_poweroff(struct device *dev)
127574c4633dSRafael J. Wysocki {
127600f7f90cSArnd Bergmann if (!IS_ENABLED(CONFIG_PM))
127700f7f90cSArnd Bergmann return -ENOSYS;
127800f7f90cSArnd Bergmann
12792fb08e6cSPaul Fox return cmos_suspend(dev);
128074c4633dSRafael J. Wysocki }
128174c4633dSRafael J. Wysocki
cmos_check_wkalrm(struct device * dev)128268669d55SGabriele Mazzotta static void cmos_check_wkalrm(struct device *dev)
128368669d55SGabriele Mazzotta {
128468669d55SGabriele Mazzotta struct cmos_rtc *cmos = dev_get_drvdata(dev);
128568669d55SGabriele Mazzotta struct rtc_wkalrm current_alarm;
1286c6d3a278SZhang Rui time64_t t_now;
128768669d55SGabriele Mazzotta time64_t t_current_expires;
128868669d55SGabriele Mazzotta time64_t t_saved_expires;
1289c6d3a278SZhang Rui struct rtc_time now;
1290c6d3a278SZhang Rui
1291c6d3a278SZhang Rui /* Check if we have RTC Alarm armed */
1292c6d3a278SZhang Rui if (!(cmos->suspend_ctrl & RTC_AIE))
1293c6d3a278SZhang Rui return;
1294c6d3a278SZhang Rui
1295c6d3a278SZhang Rui cmos_read_time(dev, &now);
1296c6d3a278SZhang Rui t_now = rtc_tm_to_time64(&now);
1297c6d3a278SZhang Rui
1298c6d3a278SZhang Rui /*
1299c6d3a278SZhang Rui * ACPI RTC wake event is cleared after resume from STR,
1300c6d3a278SZhang Rui * ACK the rtc irq here
1301c6d3a278SZhang Rui */
1302bc51098cSMaciej W. Rozycki if (t_now >= cmos->alarm_expires && cmos_use_acpi_alarm()) {
130313be2efcSChris Wilson local_irq_disable();
1304c6d3a278SZhang Rui cmos_interrupt(0, (void *)cmos->rtc);
130513be2efcSChris Wilson local_irq_enable();
1306c6d3a278SZhang Rui return;
1307c6d3a278SZhang Rui }
130868669d55SGabriele Mazzotta
1309c254bcd7SVictor Ding memset(¤t_alarm, 0, sizeof(struct rtc_wkalrm));
131068669d55SGabriele Mazzotta cmos_read_alarm(dev, ¤t_alarm);
131168669d55SGabriele Mazzotta t_current_expires = rtc_tm_to_time64(¤t_alarm.time);
131268669d55SGabriele Mazzotta t_saved_expires = rtc_tm_to_time64(&cmos->saved_wkalrm.time);
131368669d55SGabriele Mazzotta if (t_current_expires != t_saved_expires ||
131468669d55SGabriele Mazzotta cmos->saved_wkalrm.enabled != current_alarm.enabled) {
131568669d55SGabriele Mazzotta cmos_set_alarm(dev, &cmos->saved_wkalrm);
131668669d55SGabriele Mazzotta }
131768669d55SGabriele Mazzotta }
131868669d55SGabriele Mazzotta
cmos_resume(struct device * dev)131900f7f90cSArnd Bergmann static int __maybe_unused cmos_resume(struct device *dev)
13207be2c7c9SDavid Brownell {
13217be2c7c9SDavid Brownell struct cmos_rtc *cmos = dev_get_drvdata(dev);
1322998a0605SDerek Basehore unsigned char tmp;
13237be2c7c9SDavid Brownell
1324bc51098cSMaciej W. Rozycki if (cmos->enabled_wake && !cmos_use_acpi_alarm()) {
132587ac84f4SDavid Brownell if (cmos->wake_off)
132687ac84f4SDavid Brownell cmos->wake_off(dev);
132787ac84f4SDavid Brownell else
132887ac84f4SDavid Brownell disable_irq_wake(cmos->irq);
132987ac84f4SDavid Brownell cmos->enabled_wake = 0;
133087ac84f4SDavid Brownell }
13317be2c7c9SDavid Brownell
133268669d55SGabriele Mazzotta /* The BIOS might have changed the alarm, restore it */
133368669d55SGabriele Mazzotta cmos_check_wkalrm(dev);
133468669d55SGabriele Mazzotta
13357be2c7c9SDavid Brownell spin_lock_irq(&rtc_lock);
1336998a0605SDerek Basehore tmp = cmos->suspend_ctrl;
1337998a0605SDerek Basehore cmos->suspend_ctrl = 0;
1338998a0605SDerek Basehore /* re-enable any irqs previously active */
1339998a0605SDerek Basehore if (tmp & RTC_IRQMASK) {
1340998a0605SDerek Basehore unsigned char mask;
1341998a0605SDerek Basehore
1342311ee9c1SZhang Rui if (device_may_wakeup(dev) && use_hpet_alarm())
1343ebf8d6c8SDerek Basehore hpet_rtc_timer_init();
1344ebf8d6c8SDerek Basehore
134535d3fdd5SDavid Brownell do {
13467be2c7c9SDavid Brownell CMOS_WRITE(tmp, RTC_CONTROL);
1347311ee9c1SZhang Rui if (use_hpet_alarm())
134835d3fdd5SDavid Brownell hpet_set_rtc_irq_bit(tmp & RTC_IRQMASK);
134935d3fdd5SDavid Brownell
135035d3fdd5SDavid Brownell mask = CMOS_READ(RTC_INTR_FLAGS);
135135d3fdd5SDavid Brownell mask &= (tmp & RTC_IRQMASK) | RTC_IRQF;
1352311ee9c1SZhang Rui if (!use_hpet_alarm() || !is_intr(mask))
135335d3fdd5SDavid Brownell break;
135435d3fdd5SDavid Brownell
135535d3fdd5SDavid Brownell /* force one-shot behavior if HPET blocked
135635d3fdd5SDavid Brownell * the wake alarm's irq
135735d3fdd5SDavid Brownell */
135835d3fdd5SDavid Brownell rtc_update_irq(cmos->rtc, 1, mask);
135935d3fdd5SDavid Brownell tmp &= ~RTC_AIE;
136035d3fdd5SDavid Brownell hpet_mask_rtc_irq_bit(RTC_AIE);
136135d3fdd5SDavid Brownell } while (mask & RTC_AIE);
1362983bf125SGabriele Mazzotta
1363983bf125SGabriele Mazzotta if (tmp & RTC_AIE)
1364983bf125SGabriele Mazzotta cmos_check_acpi_rtc_status(dev, &tmp);
13657be2c7c9SDavid Brownell }
1366998a0605SDerek Basehore spin_unlock_irq(&rtc_lock);
13677be2c7c9SDavid Brownell
1368ee443357SJingoo Han dev_dbg(dev, "resume, ctrl %02x\n", tmp);
13697be2c7c9SDavid Brownell
13707be2c7c9SDavid Brownell return 0;
13717be2c7c9SDavid Brownell }
13727be2c7c9SDavid Brownell
1373b5ada460SMika Westerberg static SIMPLE_DEV_PM_OPS(cmos_pm_ops, cmos_suspend, cmos_resume);
1374b5ada460SMika Westerberg
13757be2c7c9SDavid Brownell /*----------------------------------------------------------------*/
13767be2c7c9SDavid Brownell
1377e07e232cSDavid Brownell /* On non-x86 systems, a "CMOS" RTC lives most naturally on platform_bus.
1378e07e232cSDavid Brownell * ACPI systems always list these as PNPACPI devices, and pre-ACPI PCs
1379e07e232cSDavid Brownell * probably list them in similar PNPBIOS tables; so PNP is more common.
1380e07e232cSDavid Brownell *
1381e07e232cSDavid Brownell * We don't use legacy "poke at the hardware" probing. Ancient PCs that
1382e07e232cSDavid Brownell * predate even PNPBIOS should set up platform_bus devices.
13837be2c7c9SDavid Brownell */
13847be2c7c9SDavid Brownell
138541ac8df9SMarko Vrh #ifdef CONFIG_PNP
13867be2c7c9SDavid Brownell
13877be2c7c9SDavid Brownell #include <linux/pnp.h>
13887be2c7c9SDavid Brownell
cmos_pnp_probe(struct pnp_dev * pnp,const struct pnp_device_id * id)13895a167f45SGreg Kroah-Hartman static int cmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
13907be2c7c9SDavid Brownell {
1391375bbba0SRafael J. Wysocki int irq;
1392a474aaedSBjorn Helgaas
1393a1e23a42SHans de Goede if (pnp_port_start(pnp, 0) == 0x70 && !pnp_irq_valid(pnp, 0)) {
13944919d3ebSRafael J. Wysocki irq = 0;
1395a1e23a42SHans de Goede #ifdef CONFIG_X86
13966cd8fa87SMatthew Garrett /* Some machines contain a PNP entry for the RTC, but
13976cd8fa87SMatthew Garrett * don't define the IRQ. It should always be safe to
1398a1e23a42SHans de Goede * hardcode it on systems with a legacy PIC.
13996cd8fa87SMatthew Garrett */
1400a1e23a42SHans de Goede if (nr_legacy_irqs())
14015848ad2fSAndy Shevchenko irq = RTC_IRQ;
1402a1e23a42SHans de Goede #endif
1403a1e23a42SHans de Goede } else {
14044919d3ebSRafael J. Wysocki irq = pnp_irq(pnp, 0);
14057be2c7c9SDavid Brownell }
14064919d3ebSRafael J. Wysocki
1407375bbba0SRafael J. Wysocki return cmos_do_probe(&pnp->dev, pnp_get_resource(pnp, IORESOURCE_IO, 0), irq);
1408a1e23a42SHans de Goede }
14097be2c7c9SDavid Brownell
cmos_pnp_remove(struct pnp_dev * pnp)1410a3a0673bSLABBE Corentin static void cmos_pnp_remove(struct pnp_dev *pnp)
14117be2c7c9SDavid Brownell {
14127be2c7c9SDavid Brownell cmos_do_remove(&pnp->dev);
14137be2c7c9SDavid Brownell }
14147be2c7c9SDavid Brownell
cmos_pnp_shutdown(struct pnp_dev * pnp)1415004731b2SOGAWA Hirofumi static void cmos_pnp_shutdown(struct pnp_dev *pnp)
141674c4633dSRafael J. Wysocki {
141731632dbdSMaciej W. Rozycki struct device *dev = &pnp->dev;
141831632dbdSMaciej W. Rozycki struct cmos_rtc *cmos = dev_get_drvdata(dev);
141931632dbdSMaciej W. Rozycki
142088b8d33bSAdrian Huang if (system_state == SYSTEM_POWER_OFF) {
142188b8d33bSAdrian Huang int retval = cmos_poweroff(dev);
142288b8d33bSAdrian Huang
142388b8d33bSAdrian Huang if (cmos_aie_poweroff(dev) < 0 && !retval)
142474c4633dSRafael J. Wysocki return;
142588b8d33bSAdrian Huang }
142674c4633dSRafael J. Wysocki
142731632dbdSMaciej W. Rozycki cmos_do_shutdown(cmos->irq);
142874c4633dSRafael J. Wysocki }
14297be2c7c9SDavid Brownell
14307be2c7c9SDavid Brownell static const struct pnp_device_id rtc_ids[] = {
14317be2c7c9SDavid Brownell { .id = "PNP0b00", },
14327be2c7c9SDavid Brownell { .id = "PNP0b01", },
14337be2c7c9SDavid Brownell { .id = "PNP0b02", },
14347be2c7c9SDavid Brownell { },
14357be2c7c9SDavid Brownell };
14367be2c7c9SDavid Brownell MODULE_DEVICE_TABLE(pnp, rtc_ids);
14377be2c7c9SDavid Brownell
14387be2c7c9SDavid Brownell static struct pnp_driver cmos_pnp_driver = {
14398d4e59ecSCorentin Labbe .name = driver_name,
14407be2c7c9SDavid Brownell .id_table = rtc_ids,
14417be2c7c9SDavid Brownell .probe = cmos_pnp_probe,
1442a3a0673bSLABBE Corentin .remove = cmos_pnp_remove,
1443004731b2SOGAWA Hirofumi .shutdown = cmos_pnp_shutdown,
14447be2c7c9SDavid Brownell
14457be2c7c9SDavid Brownell /* flag ensures resume() gets called, and stops syslog spam */
14467be2c7c9SDavid Brownell .flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
1447a8a3808bSShuah Khan .driver = {
1448a8a3808bSShuah Khan .pm = &cmos_pm_ops,
1449a8a3808bSShuah Khan },
14507be2c7c9SDavid Brownell };
14517be2c7c9SDavid Brownell
14521da2e3d6SStas Sergeev #endif /* CONFIG_PNP */
14537be2c7c9SDavid Brownell
14543bcbaf6eSSebastian Andrzej Siewior #ifdef CONFIG_OF
14553bcbaf6eSSebastian Andrzej Siewior static const struct of_device_id of_cmos_match[] = {
14563bcbaf6eSSebastian Andrzej Siewior {
14573bcbaf6eSSebastian Andrzej Siewior .compatible = "motorola,mc146818",
14583bcbaf6eSSebastian Andrzej Siewior },
14593bcbaf6eSSebastian Andrzej Siewior { },
14603bcbaf6eSSebastian Andrzej Siewior };
14613bcbaf6eSSebastian Andrzej Siewior MODULE_DEVICE_TABLE(of, of_cmos_match);
14623bcbaf6eSSebastian Andrzej Siewior
cmos_of_init(struct platform_device * pdev)14633bcbaf6eSSebastian Andrzej Siewior static __init void cmos_of_init(struct platform_device *pdev)
14643bcbaf6eSSebastian Andrzej Siewior {
14653bcbaf6eSSebastian Andrzej Siewior struct device_node *node = pdev->dev.of_node;
14663bcbaf6eSSebastian Andrzej Siewior const __be32 *val;
14673bcbaf6eSSebastian Andrzej Siewior
14683bcbaf6eSSebastian Andrzej Siewior if (!node)
14693bcbaf6eSSebastian Andrzej Siewior return;
14703bcbaf6eSSebastian Andrzej Siewior
14713bcbaf6eSSebastian Andrzej Siewior val = of_get_property(node, "ctrl-reg", NULL);
14723bcbaf6eSSebastian Andrzej Siewior if (val)
14733bcbaf6eSSebastian Andrzej Siewior CMOS_WRITE(be32_to_cpup(val), RTC_CONTROL);
14743bcbaf6eSSebastian Andrzej Siewior
14753bcbaf6eSSebastian Andrzej Siewior val = of_get_property(node, "freq-reg", NULL);
14763bcbaf6eSSebastian Andrzej Siewior if (val)
14773bcbaf6eSSebastian Andrzej Siewior CMOS_WRITE(be32_to_cpup(val), RTC_FREQ_SELECT);
14783bcbaf6eSSebastian Andrzej Siewior }
14793bcbaf6eSSebastian Andrzej Siewior #else
cmos_of_init(struct platform_device * pdev)14803bcbaf6eSSebastian Andrzej Siewior static inline void cmos_of_init(struct platform_device *pdev) {}
14813bcbaf6eSSebastian Andrzej Siewior #endif
14827be2c7c9SDavid Brownell /*----------------------------------------------------------------*/
14837be2c7c9SDavid Brownell
148441ac8df9SMarko Vrh /* Platform setup should have set up an RTC device, when PNP is
1485bcd9b89cSDavid Brownell * unavailable ... this could happen even on (older) PCs.
14867be2c7c9SDavid Brownell */
14877be2c7c9SDavid Brownell
cmos_platform_probe(struct platform_device * pdev)14887be2c7c9SDavid Brownell static int __init cmos_platform_probe(struct platform_device *pdev)
14897be2c7c9SDavid Brownell {
149031632dbdSMaciej W. Rozycki struct resource *resource;
1491375bbba0SRafael J. Wysocki int irq;
149231632dbdSMaciej W. Rozycki
14933bcbaf6eSSebastian Andrzej Siewior cmos_of_init(pdev);
149431632dbdSMaciej W. Rozycki
149531632dbdSMaciej W. Rozycki if (RTC_IOMAPPED)
149631632dbdSMaciej W. Rozycki resource = platform_get_resource(pdev, IORESOURCE_IO, 0);
149731632dbdSMaciej W. Rozycki else
149831632dbdSMaciej W. Rozycki resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
149931632dbdSMaciej W. Rozycki irq = platform_get_irq(pdev, 0);
150031632dbdSMaciej W. Rozycki if (irq < 0)
150131632dbdSMaciej W. Rozycki irq = -1;
150231632dbdSMaciej W. Rozycki
1503375bbba0SRafael J. Wysocki return cmos_do_probe(&pdev->dev, resource, irq);
15047be2c7c9SDavid Brownell }
15057be2c7c9SDavid Brownell
cmos_platform_remove(struct platform_device * pdev)150663c18a07SUwe Kleine-König static void cmos_platform_remove(struct platform_device *pdev)
15077be2c7c9SDavid Brownell {
15087be2c7c9SDavid Brownell cmos_do_remove(&pdev->dev);
15097be2c7c9SDavid Brownell }
15107be2c7c9SDavid Brownell
cmos_platform_shutdown(struct platform_device * pdev)15117be2c7c9SDavid Brownell static void cmos_platform_shutdown(struct platform_device *pdev)
15127be2c7c9SDavid Brownell {
151331632dbdSMaciej W. Rozycki struct device *dev = &pdev->dev;
151431632dbdSMaciej W. Rozycki struct cmos_rtc *cmos = dev_get_drvdata(dev);
151531632dbdSMaciej W. Rozycki
151688b8d33bSAdrian Huang if (system_state == SYSTEM_POWER_OFF) {
151788b8d33bSAdrian Huang int retval = cmos_poweroff(dev);
151888b8d33bSAdrian Huang
151988b8d33bSAdrian Huang if (cmos_aie_poweroff(dev) < 0 && !retval)
152074c4633dSRafael J. Wysocki return;
152188b8d33bSAdrian Huang }
152274c4633dSRafael J. Wysocki
152331632dbdSMaciej W. Rozycki cmos_do_shutdown(cmos->irq);
15247be2c7c9SDavid Brownell }
15257be2c7c9SDavid Brownell
1526ad28a07bSKay Sievers /* work with hotplug and coldplug */
1527ad28a07bSKay Sievers MODULE_ALIAS("platform:rtc_cmos");
1528ad28a07bSKay Sievers
15297be2c7c9SDavid Brownell static struct platform_driver cmos_platform_driver = {
153063c18a07SUwe Kleine-König .remove_new = cmos_platform_remove,
15317be2c7c9SDavid Brownell .shutdown = cmos_platform_shutdown,
15327be2c7c9SDavid Brownell .driver = {
1533c823a202SGeert Uytterhoeven .name = driver_name,
15342fb08e6cSPaul Fox .pm = &cmos_pm_ops,
1535c8a6046eSSachin Kamat .of_match_table = of_match_ptr(of_cmos_match),
15367be2c7c9SDavid Brownell }
15377be2c7c9SDavid Brownell };
15387be2c7c9SDavid Brownell
153965909814SThadeu Lima de Souza Cascardo #ifdef CONFIG_PNP
154065909814SThadeu Lima de Souza Cascardo static bool pnp_driver_registered;
154165909814SThadeu Lima de Souza Cascardo #endif
154265909814SThadeu Lima de Souza Cascardo static bool platform_driver_registered;
154365909814SThadeu Lima de Souza Cascardo
cmos_init(void)15447be2c7c9SDavid Brownell static int __init cmos_init(void)
15457be2c7c9SDavid Brownell {
154672f22b1eSBjorn Helgaas int retval = 0;
154772f22b1eSBjorn Helgaas
15481da2e3d6SStas Sergeev #ifdef CONFIG_PNP
154965909814SThadeu Lima de Souza Cascardo retval = pnp_register_driver(&cmos_pnp_driver);
155065909814SThadeu Lima de Souza Cascardo if (retval == 0)
155165909814SThadeu Lima de Souza Cascardo pnp_driver_registered = true;
155272f22b1eSBjorn Helgaas #endif
155372f22b1eSBjorn Helgaas
155465909814SThadeu Lima de Souza Cascardo if (!cmos_rtc.dev) {
155572f22b1eSBjorn Helgaas retval = platform_driver_probe(&cmos_platform_driver,
15567be2c7c9SDavid Brownell cmos_platform_probe);
155765909814SThadeu Lima de Souza Cascardo if (retval == 0)
155865909814SThadeu Lima de Souza Cascardo platform_driver_registered = true;
155965909814SThadeu Lima de Souza Cascardo }
156072f22b1eSBjorn Helgaas
156172f22b1eSBjorn Helgaas if (retval == 0)
156272f22b1eSBjorn Helgaas return 0;
156372f22b1eSBjorn Helgaas
156472f22b1eSBjorn Helgaas #ifdef CONFIG_PNP
156565909814SThadeu Lima de Souza Cascardo if (pnp_driver_registered)
156672f22b1eSBjorn Helgaas pnp_unregister_driver(&cmos_pnp_driver);
156772f22b1eSBjorn Helgaas #endif
156872f22b1eSBjorn Helgaas return retval;
15697be2c7c9SDavid Brownell }
15707be2c7c9SDavid Brownell module_init(cmos_init);
15717be2c7c9SDavid Brownell
cmos_exit(void)15727be2c7c9SDavid Brownell static void __exit cmos_exit(void)
15737be2c7c9SDavid Brownell {
15741da2e3d6SStas Sergeev #ifdef CONFIG_PNP
157565909814SThadeu Lima de Souza Cascardo if (pnp_driver_registered)
15761da2e3d6SStas Sergeev pnp_unregister_driver(&cmos_pnp_driver);
157772f22b1eSBjorn Helgaas #endif
157865909814SThadeu Lima de Souza Cascardo if (platform_driver_registered)
15797be2c7c9SDavid Brownell platform_driver_unregister(&cmos_platform_driver);
15807be2c7c9SDavid Brownell }
15817be2c7c9SDavid Brownell module_exit(cmos_exit);
15827be2c7c9SDavid Brownell
15837be2c7c9SDavid Brownell
15847be2c7c9SDavid Brownell MODULE_AUTHOR("David Brownell");
15857be2c7c9SDavid Brownell MODULE_DESCRIPTION("Driver for PC-style 'CMOS' RTCs");
15867be2c7c9SDavid Brownell MODULE_LICENSE("GPL");
1587