blob: 586f718b8e9511681fd843c4f9e9630249c88844 [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
Thomas Gleixnerfe599f92008-01-30 13:30:26 +01003 * RTC related functions
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 */
Jaswinder Singh Rajput8383d822009-03-21 16:56:37 +05305#include <linux/platform_device.h>
6#include <linux/mc146818rtc.h>
Thomas Gleixner1122b132008-01-30 13:30:27 +01007#include <linux/acpi.h>
Thomas Gleixnerfe599f92008-01-30 13:30:26 +01008#include <linux/bcd.h>
Paul Gortmaker69c60c82011-05-26 12:22:53 -04009#include <linux/export.h>
Stas Sergeev1da2e3d2008-06-12 15:21:54 -070010#include <linux/pnp.h>
Sebastian Andrzej Siewior3bcbaf62011-02-22 21:07:46 +010011#include <linux/of.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012
Ingo Molnarcdc79572008-01-30 13:32:39 +010013#include <asm/vsyscall.h>
Feng Tang7bd867d2009-09-10 10:48:56 +080014#include <asm/x86_init.h>
Jaswinder Singh Rajput8383d822009-03-21 16:56:37 +053015#include <asm/time.h>
Kuppuswamy Sathyanarayanan05454c22013-10-17 15:35:27 -070016#include <asm/intel-mid.h>
Luis R. Rodriguez8d152e72016-04-13 17:04:34 -070017#include <asm/setup.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018
Thomas Gleixner1122b132008-01-30 13:30:27 +010019#ifdef CONFIG_X86_32
Thomas Gleixner1122b132008-01-30 13:30:27 +010020/*
21 * This is a special lock that is owned by the CPU and holds the index
22 * register we are working with. It is required for NMI access to the
23 * CMOS/RTC registers. See include/asm-i386/mc146818rtc.h for details.
24 */
Jaswinder Singh Rajput8383d822009-03-21 16:56:37 +053025volatile unsigned long cmos_lock;
Thomas Gleixner1122b132008-01-30 13:30:27 +010026EXPORT_SYMBOL(cmos_lock);
Jaswinder Singh Rajput8383d822009-03-21 16:56:37 +053027#endif /* CONFIG_X86_32 */
Thomas Gleixner1122b132008-01-30 13:30:27 +010028
Andi Kleenb62576a2008-02-09 16:16:58 +010029/* For two digit years assume time is always after that */
30#define CMOS_YEARS_OFFS 2000
31
Thomas Gleixner1122b132008-01-30 13:30:27 +010032DEFINE_SPINLOCK(rtc_lock);
33EXPORT_SYMBOL(rtc_lock);
34
Linus Torvalds1da177e2005-04-16 15:20:36 -070035/*
36 * In order to set the CMOS clock precisely, set_rtc_mmss has to be
37 * called 500 ms after the second nowtime has started, because when
38 * nowtime is written into the registers of the CMOS clock, it will
39 * jump to the next second precisely 500 ms later. Check the Motorola
40 * MC146818A or Dallas DS12887 data sheet for details.
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 */
Arnd Bergmanne27c49292018-04-27 22:13:23 +020042int mach_set_rtc_mmss(const struct timespec64 *now)
Linus Torvalds1da177e2005-04-16 15:20:36 -070043{
Benjamin Gaignard13cc36d2018-03-09 10:42:50 -080044 unsigned long long nowtime = now->tv_sec;
Prarit Bhargava3195ef52013-02-14 12:02:54 -050045 struct rtc_time tm;
Jaswinder Singh Rajput8383d822009-03-21 16:56:37 +053046 int retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070047
Benjamin Gaignard13cc36d2018-03-09 10:42:50 -080048 rtc_time64_to_tm(nowtime, &tm);
Prarit Bhargava3195ef52013-02-14 12:02:54 -050049 if (!rtc_valid_tm(&tm)) {
Arnd Bergmann463a8632016-05-30 20:57:51 +020050 retval = mc146818_set_time(&tm);
Prarit Bhargava3195ef52013-02-14 12:02:54 -050051 if (retval)
52 printk(KERN_ERR "%s: RTC write failed with error %d\n",
Rasmus Villemoes02f1f212015-02-12 15:01:31 -080053 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 } else {
Prarit Bhargava3195ef52013-02-14 12:02:54 -050055 printk(KERN_ERR
Benjamin Gaignard13cc36d2018-03-09 10:42:50 -080056 "%s: Invalid RTC value: write of %llx to RTC failed\n",
Rasmus Villemoes02f1f212015-02-12 15:01:31 -080057 __func__, nowtime);
Prarit Bhargava3195ef52013-02-14 12:02:54 -050058 retval = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 return retval;
61}
62
Arnd Bergmanne27c49292018-04-27 22:13:23 +020063void mach_get_cmos_time(struct timespec64 *now)
Linus Torvalds1da177e2005-04-16 15:20:36 -070064{
Andi Kleen068c9222008-02-09 16:16:59 +010065 unsigned int status, year, mon, day, hour, min, sec, century = 0;
Matt Fleming47997d72011-09-21 16:08:03 +020066 unsigned long flags;
67
Chen Yuba58d102016-11-28 14:35:19 -080068 /*
69 * If pm_trace abused the RTC as storage, set the timespec to 0,
70 * which tells the caller that this RTC value is unusable.
71 */
72 if (!pm_trace_rtc_valid()) {
73 now->tv_sec = now->tv_nsec = 0;
74 return;
75 }
76
Matt Fleming47997d72011-09-21 16:08:03 +020077 spin_lock_irqsave(&rtc_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -070078
Thomas Gleixner1122b132008-01-30 13:30:27 +010079 /*
80 * If UIP is clear, then we have >= 244 microseconds before
81 * RTC registers will be updated. Spec sheet says that this
82 * is the reliable way to read RTC - registers. If UIP is set
83 * then the register access might be invalid.
84 */
85 while ((CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
86 cpu_relax();
Matt Mackall63732c22006-03-28 01:55:58 -080087
Thomas Gleixner1122b132008-01-30 13:30:27 +010088 sec = CMOS_READ(RTC_SECONDS);
89 min = CMOS_READ(RTC_MINUTES);
90 hour = CMOS_READ(RTC_HOURS);
91 day = CMOS_READ(RTC_DAY_OF_MONTH);
92 mon = CMOS_READ(RTC_MONTH);
93 year = CMOS_READ(RTC_YEAR);
94
Andi Kleen45de7072008-02-09 16:17:01 +010095#ifdef CONFIG_ACPI
Thomas Gleixner1122b132008-01-30 13:30:27 +010096 if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
97 acpi_gbl_FADT.century)
98 century = CMOS_READ(acpi_gbl_FADT.century);
99#endif
100
Andi Kleen068c9222008-02-09 16:16:59 +0100101 status = CMOS_READ(RTC_CONTROL);
Andi Kleen45de7072008-02-09 16:17:01 +0100102 WARN_ON_ONCE(RTC_ALWAYS_BCD && (status & RTC_DM_BINARY));
Andi Kleen068c9222008-02-09 16:16:59 +0100103
Matt Fleming47997d72011-09-21 16:08:03 +0200104 spin_unlock_irqrestore(&rtc_lock, flags);
105
Andi Kleen068c9222008-02-09 16:16:59 +0100106 if (RTC_ALWAYS_BCD || !(status & RTC_DM_BINARY)) {
Adrian Bunk357c6e62008-10-18 20:28:42 -0700107 sec = bcd2bin(sec);
108 min = bcd2bin(min);
109 hour = bcd2bin(hour);
110 day = bcd2bin(day);
111 mon = bcd2bin(mon);
112 year = bcd2bin(year);
Matt Mackall41623b02006-03-28 01:56:09 -0800113 }
114
Thomas Gleixner1122b132008-01-30 13:30:27 +0100115 if (century) {
Adrian Bunk357c6e62008-10-18 20:28:42 -0700116 century = bcd2bin(century);
Thomas Gleixner1122b132008-01-30 13:30:27 +0100117 year += century * 100;
Andi Kleenb62576a2008-02-09 16:16:58 +0100118 } else
Thomas Gleixner1122b132008-01-30 13:30:27 +0100119 year += CMOS_YEARS_OFFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
Arnd Bergmanne27c49292018-04-27 22:13:23 +0200121 now->tv_sec = mktime64(year, mon, day, hour, min, sec);
David Vrabel35651842013-05-13 18:56:06 +0100122 now->tv_nsec = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123}
124
Thomas Gleixnerfe599f92008-01-30 13:30:26 +0100125/* Routines for accessing the CMOS RAM/RTC. */
126unsigned char rtc_cmos_read(unsigned char addr)
127{
128 unsigned char val;
129
130 lock_cmos_prefix(addr);
David P. Reed04aaa7b2008-02-17 16:56:39 -0500131 outb(addr, RTC_PORT(0));
132 val = inb(RTC_PORT(1));
Thomas Gleixnerfe599f92008-01-30 13:30:26 +0100133 lock_cmos_suffix(addr);
Jaswinder Singh Rajput8383d822009-03-21 16:56:37 +0530134
Thomas Gleixnerfe599f92008-01-30 13:30:26 +0100135 return val;
136}
137EXPORT_SYMBOL(rtc_cmos_read);
138
139void rtc_cmos_write(unsigned char val, unsigned char addr)
140{
141 lock_cmos_prefix(addr);
David P. Reed04aaa7b2008-02-17 16:56:39 -0500142 outb(addr, RTC_PORT(0));
143 outb(val, RTC_PORT(1));
Thomas Gleixnerfe599f92008-01-30 13:30:26 +0100144 lock_cmos_suffix(addr);
145}
146EXPORT_SYMBOL(rtc_cmos_write);
147
Arnd Bergmanne27c49292018-04-27 22:13:23 +0200148int update_persistent_clock64(struct timespec64 now)
Thomas Gleixnerfe599f92008-01-30 13:30:26 +0100149{
David Vrabel35651842013-05-13 18:56:06 +0100150 return x86_platform.set_wallclock(&now);
Thomas Gleixnerfe599f92008-01-30 13:30:26 +0100151}
152
153/* not static: needed by APM */
Arnd Bergmanne27c49292018-04-27 22:13:23 +0200154void read_persistent_clock64(struct timespec64 *ts)
Thomas Gleixnerfe599f92008-01-30 13:30:26 +0100155{
David Vrabel35651842013-05-13 18:56:06 +0100156 x86_platform.get_wallclock(ts);
Thomas Gleixnerfe599f92008-01-30 13:30:26 +0100157}
158
Stas Sergeev1da2e3d2008-06-12 15:21:54 -0700159
160static struct resource rtc_resources[] = {
161 [0] = {
162 .start = RTC_PORT(0),
163 .end = RTC_PORT(1),
164 .flags = IORESOURCE_IO,
165 },
166 [1] = {
167 .start = RTC_IRQ,
168 .end = RTC_IRQ,
169 .flags = IORESOURCE_IRQ,
170 }
171};
172
173static struct platform_device rtc_device = {
174 .name = "rtc_cmos",
175 .id = -1,
176 .resource = rtc_resources,
177 .num_resources = ARRAY_SIZE(rtc_resources),
178};
179
180static __init int add_rtc_cmos(void)
181{
182#ifdef CONFIG_PNP
Colin Kingd505ad12015-01-14 14:07:55 +0000183 static const char * const ids[] __initconst =
Bjorn Helgaas758a7f72008-10-14 17:01:03 -0600184 { "PNP0b00", "PNP0b01", "PNP0b02", };
185 struct pnp_dev *dev;
186 struct pnp_id *id;
187 int i;
188
189 pnp_for_each_dev(dev) {
190 for (id = dev->id; id; id = id->next) {
191 for (i = 0; i < ARRAY_SIZE(ids); i++) {
192 if (compare_pnp_id(id, ids[i]) != 0)
193 return 0;
194 }
195 }
196 }
197#endif
Luis R. Rodriguez8d152e72016-04-13 17:04:34 -0700198 if (!x86_platform.legacy.rtc)
David Vrabeld8c98a12015-12-11 09:07:53 -0500199 return -ENODEV;
200
Stas Sergeev1da2e3d2008-06-12 15:21:54 -0700201 platform_device_register(&rtc_device);
Bjorn Helgaas758a7f72008-10-14 17:01:03 -0600202 dev_info(&rtc_device.dev,
203 "registered platform RTC device (no PNP device found)\n");
Jaswinder Singh Rajput8383d822009-03-21 16:56:37 +0530204
Stas Sergeev1da2e3d2008-06-12 15:21:54 -0700205 return 0;
206}
207device_initcall(add_rtc_cmos);