Release 4.14 arch/x86/kernel/rtc.c
// SPDX-License-Identifier: GPL-2.0
/*
* RTC related functions
*/
#include <linux/platform_device.h>
#include <linux/mc146818rtc.h>
#include <linux/acpi.h>
#include <linux/bcd.h>
#include <linux/export.h>
#include <linux/pnp.h>
#include <linux/of.h>
#include <asm/vsyscall.h>
#include <asm/x86_init.h>
#include <asm/time.h>
#include <asm/intel-mid.h>
#include <asm/setup.h>
#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
* CMOS/RTC registers. See include/asm-i386/mc146818rtc.h for details.
*/
volatile unsigned long cmos_lock;
EXPORT_SYMBOL(cmos_lock);
#endif /* CONFIG_X86_32 */
/* For two digit years assume time is always after that */
#define CMOS_YEARS_OFFS 2000
DEFINE_SPINLOCK(rtc_lock);
EXPORT_SYMBOL(rtc_lock);
/*
* In order to set the CMOS clock precisely, set_rtc_mmss has to be
* 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.
*/
int mach_set_rtc_mmss(const struct timespec *now)
{
unsigned long nowtime = now->tv_sec;
struct rtc_time tm;
int retval = 0;
rtc_time_to_tm(nowtime, &tm);
if (!rtc_valid_tm(&tm)) {
retval = mc146818_set_time(&tm);
if (retval)
printk(KERN_ERR "%s: RTC write failed with error %d\n",
__func__, retval);
} else {
printk(KERN_ERR
"%s: Invalid RTC value: write of %lx to RTC failed\n",
__func__, nowtime);
retval = -EINVAL;
}
return retval;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Alan Cox | 43 | 47.78% | 1 | 12.50% |
Prarit Bhargava | 25 | 27.78% | 1 | 12.50% |
David Vrabel | 12 | 13.33% | 1 | 12.50% |
Jaswinder Singh Rajput | 5 | 5.56% | 1 | 12.50% |
Rasmus Villemoes | 2 | 2.22% | 1 | 12.50% |
Arnd Bergmann | 1 | 1.11% | 1 | 12.50% |
Adrian Bunk | 1 | 1.11% | 1 | 12.50% |
Matt Fleming | 1 | 1.11% | 1 | 12.50% |
Total | 90 | 100.00% | 8 | 100.00% |
void mach_get_cmos_time(struct timespec *now)
{
unsigned int status, year, mon, day, hour, min, sec, century = 0;
unsigned long flags;
/*
* 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;
}
spin_lock_irqsave(&rtc_lock, flags);
/*
* If UIP is clear, then we have >= 244 microseconds before
* RTC registers will be updated. Spec sheet says that this
* is the reliable way to read RTC - registers. If UIP is set
* then the register access might be invalid.
*/
while ((CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
cpu_relax();
sec = CMOS_READ(RTC_SECONDS);
min = CMOS_READ(RTC_MINUTES);
hour = CMOS_READ(RTC_HOURS);
day = CMOS_READ(RTC_DAY_OF_MONTH);
mon = CMOS_READ(RTC_MONTH);
year = CMOS_READ(RTC_YEAR);
#ifdef CONFIG_ACPI
if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
acpi_gbl_FADT.century)
century = CMOS_READ(acpi_gbl_FADT.century);
#endif
status = CMOS_READ(RTC_CONTROL);
WARN_ON_ONCE(RTC_ALWAYS_BCD && (status & RTC_DM_BINARY));
spin_unlock_irqrestore(&rtc_lock, flags);
if (RTC_ALWAYS_BCD || !(status & RTC_DM_BINARY)) {
sec = bcd2bin(sec);
min = bcd2bin(min);
hour = bcd2bin(hour);
day = bcd2bin(day);
mon = bcd2bin(mon);
year = bcd2bin(year);
}
if (century) {
century = bcd2bin(century);
year += century * 100;
} else
year += CMOS_YEARS_OFFS;
now->tv_sec = mktime(year, mon, day, hour, min, sec);
now->tv_nsec = 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andi Kleen | 88 | 31.77% | 2 | 25.00% |
Alan Cox | 70 | 25.27% | 1 | 12.50% |
Thomas Gleixner | 42 | 15.16% | 1 | 12.50% |
Adrian Bunk | 21 | 7.58% | 1 | 12.50% |
Matt Fleming | 20 | 7.22% | 1 | 12.50% |
Chen Yu | 20 | 7.22% | 1 | 12.50% |
David Vrabel | 16 | 5.78% | 1 | 12.50% |
Total | 277 | 100.00% | 8 | 100.00% |
/* Routines for accessing the CMOS RAM/RTC. */
unsigned char rtc_cmos_read(unsigned char addr)
{
unsigned char val;
lock_cmos_prefix(addr);
outb(addr, RTC_PORT(0));
val = inb(RTC_PORT(1));
lock_cmos_suffix(addr);
return val;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andi Kleen | 43 | 91.49% | 1 | 33.33% |
Thomas Gleixner | 2 | 4.26% | 1 | 33.33% |
David P. Reed | 2 | 4.26% | 1 | 33.33% |
Total | 47 | 100.00% | 3 | 100.00% |
EXPORT_SYMBOL(rtc_cmos_read);
void rtc_cmos_write(unsigned char val, unsigned char addr)
{
lock_cmos_prefix(addr);
outb(addr, RTC_PORT(0));
outb(val, RTC_PORT(1));
lock_cmos_suffix(addr);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andi Kleen | 39 | 90.70% | 1 | 33.33% |
Thomas Gleixner | 2 | 4.65% | 1 | 33.33% |
David P. Reed | 2 | 4.65% | 1 | 33.33% |
Total | 43 | 100.00% | 3 | 100.00% |
EXPORT_SYMBOL(rtc_cmos_write);
int update_persistent_clock(struct timespec now)
{
return x86_platform.set_wallclock(&now);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andi Kleen | 9 | 50.00% | 1 | 25.00% |
Feng Tang | 7 | 38.89% | 1 | 25.00% |
David Vrabel | 1 | 5.56% | 1 | 25.00% |
Matt Fleming | 1 | 5.56% | 1 | 25.00% |
Total | 18 | 100.00% | 4 | 100.00% |
/* not static: needed by APM */
void read_persistent_clock(struct timespec *ts)
{
x86_platform.get_wallclock(ts);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Martin Schwidefsky | 7 | 41.18% | 1 | 25.00% |
Andi Kleen | 6 | 35.29% | 1 | 25.00% |
Feng Tang | 2 | 11.76% | 1 | 25.00% |
David Vrabel | 2 | 11.76% | 1 | 25.00% |
Total | 17 | 100.00% | 4 | 100.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)
{
#ifdef CONFIG_PNP
static const char * const ids[] __initconst =
{ "PNP0b00", "PNP0b01", "PNP0b02", };
struct pnp_dev *dev;
struct pnp_id *id;
int i;
pnp_for_each_dev(dev) {
for (id = dev->id; id; id = id->next) {
for (i = 0; i < ARRAY_SIZE(ids); i++) {
if (compare_pnp_id(id, ids[i]) != 0)
return 0;
}
}
}
#endif
if (!x86_platform.legacy.rtc)
return -ENODEV;
platform_device_register(&rtc_device);
dev_info(&rtc_device.dev,
"registered platform RTC device (no PNP device found)\n");
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Björn Helgaas | 92 | 68.15% | 1 | 16.67% |
Stas Sergeev | 29 | 21.48% | 1 | 16.67% |
David Vrabel | 6 | 4.44% | 1 | 16.67% |
Luis R. Rodriguez | 5 | 3.70% | 1 | 16.67% |
Sebastian Andrzej Siewior | 2 | 1.48% | 1 | 16.67% |
Andi Kleen | 1 | 0.74% | 1 | 16.67% |
Total | 135 | 100.00% | 6 | 100.00% |
device_initcall(add_rtc_cmos);
Overall Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andi Kleen | 199 | 24.81% | 4 | 14.29% |
Stas Sergeev | 126 | 15.71% | 1 | 3.57% |
Alan Cox | 115 | 14.34% | 1 | 3.57% |
Björn Helgaas | 92 | 11.47% | 1 | 3.57% |
Thomas Gleixner | 83 | 10.35% | 2 | 7.14% |
David Vrabel | 37 | 4.61% | 2 | 7.14% |
Prarit Bhargava | 28 | 3.49% | 1 | 3.57% |
Matt Fleming | 22 | 2.74% | 1 | 3.57% |
Adrian Bunk | 22 | 2.74% | 1 | 3.57% |
Chen Yu | 20 | 2.49% | 1 | 3.57% |
Feng Tang | 12 | 1.50% | 1 | 3.57% |
Jaswinder Singh Rajput | 12 | 1.50% | 1 | 3.57% |
Martin Schwidefsky | 7 | 0.87% | 1 | 3.57% |
Luis R. Rodriguez | 6 | 0.75% | 1 | 3.57% |
Sebastian Andrzej Siewior | 5 | 0.62% | 1 | 3.57% |
David P. Reed | 4 | 0.50% | 1 | 3.57% |
Paul Gortmaker | 3 | 0.37% | 1 | 3.57% |
Mathias Nyman | 2 | 0.25% | 1 | 3.57% |
Rasmus Villemoes | 2 | 0.25% | 1 | 3.57% |
Ingo Molnar | 2 | 0.25% | 1 | 3.57% |
Kuppuswamy Sathyanarayanan | 1 | 0.12% | 1 | 3.57% |
Greg Kroah-Hartman | 1 | 0.12% | 1 | 3.57% |
Arnd Bergmann | 1 | 0.12% | 1 | 3.57% |
Total | 802 | 100.00% | 28 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.