2017-11-01 15:07:57 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2005-04-16 15:20:36 -07:00
|
|
|
/*
|
2008-01-30 13:30:26 +01:00
|
|
|
* RTC related functions
|
2005-04-16 15:20:36 -07:00
|
|
|
*/
|
2026-02-23 16:29:37 +01:00
|
|
|
#include <linux/acpi.h>
|
2009-03-21 16:56:37 +05:30
|
|
|
#include <linux/platform_device.h>
|
|
|
|
|
#include <linux/mc146818rtc.h>
|
2011-05-26 12:22:53 -04:00
|
|
|
#include <linux/export.h>
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2008-01-30 13:32:39 +01:00
|
|
|
#include <asm/vsyscall.h>
|
2009-09-10 10:48:56 +08:00
|
|
|
#include <asm/x86_init.h>
|
2009-03-21 16:56:37 +05:30
|
|
|
#include <asm/time.h>
|
2016-04-13 17:04:34 -07:00
|
|
|
#include <asm/setup.h>
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2008-01-30 13:30:27 +01:00
|
|
|
#ifdef CONFIG_X86_32
|
|
|
|
|
/*
|
|
|
|
|
* This is a special lock that is owned by the CPU and holds the index
|
|
|
|
|
* register we are working with. It is required for NMI access to the
|
2022-08-13 15:10:33 +02:00
|
|
|
* CMOS/RTC registers. See arch/x86/include/asm/mc146818rtc.h for details.
|
2008-01-30 13:30:27 +01:00
|
|
|
*/
|
2009-03-21 16:56:37 +05:30
|
|
|
volatile unsigned long cmos_lock;
|
2008-01-30 13:30:27 +01:00
|
|
|
EXPORT_SYMBOL(cmos_lock);
|
2009-03-21 16:56:37 +05:30
|
|
|
#endif /* CONFIG_X86_32 */
|
2008-01-30 13:30:27 +01:00
|
|
|
|
|
|
|
|
DEFINE_SPINLOCK(rtc_lock);
|
|
|
|
|
EXPORT_SYMBOL(rtc_lock);
|
|
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
/*
|
2022-08-13 15:10:34 +02:00
|
|
|
* In order to set the CMOS clock precisely, mach_set_cmos_time has to be
|
2005-04-16 15:20:36 -07:00
|
|
|
* called 500 ms after the second nowtime has started, because when
|
|
|
|
|
* nowtime is written into the registers of the CMOS clock, it will
|
|
|
|
|
* jump to the next second precisely 500 ms later. Check the Motorola
|
|
|
|
|
* MC146818A or Dallas DS12887 data sheet for details.
|
|
|
|
|
*/
|
2022-08-13 15:10:34 +02:00
|
|
|
int mach_set_cmos_time(const struct timespec64 *now)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2018-03-09 10:42:50 -08:00
|
|
|
unsigned long long nowtime = now->tv_sec;
|
2013-02-14 12:02:54 -05:00
|
|
|
struct rtc_time tm;
|
2009-03-21 16:56:37 +05:30
|
|
|
int retval = 0;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2018-03-09 10:42:50 -08:00
|
|
|
rtc_time64_to_tm(nowtime, &tm);
|
2013-02-14 12:02:54 -05:00
|
|
|
if (!rtc_valid_tm(&tm)) {
|
2016-05-30 20:57:51 +02:00
|
|
|
retval = mc146818_set_time(&tm);
|
2013-02-14 12:02:54 -05:00
|
|
|
if (retval)
|
|
|
|
|
printk(KERN_ERR "%s: RTC write failed with error %d\n",
|
2015-02-12 15:01:31 -08:00
|
|
|
__func__, retval);
|
2005-04-16 15:20:36 -07:00
|
|
|
} else {
|
2013-02-14 12:02:54 -05:00
|
|
|
printk(KERN_ERR
|
2018-03-09 10:42:50 -08:00
|
|
|
"%s: Invalid RTC value: write of %llx to RTC failed\n",
|
2015-02-12 15:01:31 -08:00
|
|
|
__func__, nowtime);
|
2013-02-14 12:02:54 -05:00
|
|
|
retval = -EINVAL;
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-27 22:13:23 +02:00
|
|
|
void mach_get_cmos_time(struct timespec64 *now)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2022-08-13 15:10:33 +02:00
|
|
|
struct rtc_time tm;
|
2011-09-21 16:08:03 +02:00
|
|
|
|
2016-11-28 14:35:19 -08:00
|
|
|
/*
|
|
|
|
|
* If pm_trace abused the RTC as storage, set the timespec to 0,
|
|
|
|
|
* which tells the caller that this RTC value is unusable.
|
|
|
|
|
*/
|
|
|
|
|
if (!pm_trace_rtc_valid()) {
|
|
|
|
|
now->tv_sec = now->tv_nsec = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-27 23:36:53 -06:00
|
|
|
if (mc146818_get_time(&tm, 1000)) {
|
2022-08-13 15:10:33 +02:00
|
|
|
pr_err("Unable to read current time from RTC\n");
|
|
|
|
|
now->tv_sec = now->tv_nsec = 0;
|
|
|
|
|
return;
|
2006-03-28 01:56:09 -08:00
|
|
|
}
|
|
|
|
|
|
2022-08-13 15:10:33 +02:00
|
|
|
now->tv_sec = rtc_tm_to_time64(&tm);
|
2013-05-13 18:56:06 +01:00
|
|
|
now->tv_nsec = 0;
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
|
|
|
|
|
2008-01-30 13:30:26 +01:00
|
|
|
/* Routines for accessing the CMOS RAM/RTC. */
|
|
|
|
|
unsigned char rtc_cmos_read(unsigned char addr)
|
|
|
|
|
{
|
|
|
|
|
unsigned char val;
|
|
|
|
|
|
|
|
|
|
lock_cmos_prefix(addr);
|
2008-02-17 16:56:39 -05:00
|
|
|
outb(addr, RTC_PORT(0));
|
|
|
|
|
val = inb(RTC_PORT(1));
|
2008-01-30 13:30:26 +01:00
|
|
|
lock_cmos_suffix(addr);
|
2009-03-21 16:56:37 +05:30
|
|
|
|
2008-01-30 13:30:26 +01:00
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL(rtc_cmos_read);
|
|
|
|
|
|
|
|
|
|
void rtc_cmos_write(unsigned char val, unsigned char addr)
|
|
|
|
|
{
|
|
|
|
|
lock_cmos_prefix(addr);
|
2008-02-17 16:56:39 -05:00
|
|
|
outb(addr, RTC_PORT(0));
|
|
|
|
|
outb(val, RTC_PORT(1));
|
2008-01-30 13:30:26 +01:00
|
|
|
lock_cmos_suffix(addr);
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL(rtc_cmos_write);
|
|
|
|
|
|
2018-04-27 22:13:23 +02:00
|
|
|
int update_persistent_clock64(struct timespec64 now)
|
2008-01-30 13:30:26 +01:00
|
|
|
{
|
2013-05-13 18:56:06 +01:00
|
|
|
return x86_platform.set_wallclock(&now);
|
2008-01-30 13:30:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* not static: needed by APM */
|
2018-04-27 22:13:23 +02:00
|
|
|
void read_persistent_clock64(struct timespec64 *ts)
|
2008-01-30 13:30:26 +01:00
|
|
|
{
|
2013-05-13 18:56:06 +01:00
|
|
|
x86_platform.get_wallclock(ts);
|
2008-01-30 13:30:26 +01:00
|
|
|
}
|
|
|
|
|
|
2008-06-12 15:21:54 -07:00
|
|
|
|
|
|
|
|
static struct resource rtc_resources[] = {
|
|
|
|
|
[0] = {
|
|
|
|
|
.start = RTC_PORT(0),
|
|
|
|
|
.end = RTC_PORT(1),
|
|
|
|
|
.flags = IORESOURCE_IO,
|
|
|
|
|
},
|
|
|
|
|
[1] = {
|
|
|
|
|
.start = RTC_IRQ,
|
|
|
|
|
.end = RTC_IRQ,
|
|
|
|
|
.flags = IORESOURCE_IRQ,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct platform_device rtc_device = {
|
|
|
|
|
.name = "rtc_cmos",
|
|
|
|
|
.id = -1,
|
|
|
|
|
.resource = rtc_resources,
|
|
|
|
|
.num_resources = ARRAY_SIZE(rtc_resources),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static __init int add_rtc_cmos(void)
|
|
|
|
|
{
|
2026-02-23 16:29:37 +01:00
|
|
|
if (cmos_rtc_platform_device_present)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2016-04-13 17:04:34 -07:00
|
|
|
if (!x86_platform.legacy.rtc)
|
2015-12-11 09:07:53 -05:00
|
|
|
return -ENODEV;
|
|
|
|
|
|
2008-06-12 15:21:54 -07:00
|
|
|
platform_device_register(&rtc_device);
|
2026-02-23 16:31:49 +01:00
|
|
|
dev_info(&rtc_device.dev, "registered fallback platform RTC device\n");
|
2009-03-21 16:56:37 +05:30
|
|
|
|
2008-06-12 15:21:54 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
device_initcall(add_rtc_cmos);
|