xref: /openbmc/linux/arch/alpha/kernel/rtc.c (revision 7d7ae873b5e0f46d19e5dc818d1a7809e4b7cc81)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
285d0b3a5SRichard Henderson /*
385d0b3a5SRichard Henderson  *  linux/arch/alpha/kernel/rtc.c
485d0b3a5SRichard Henderson  *
585d0b3a5SRichard Henderson  *  Copyright (C) 1991, 1992, 1995, 1999, 2000  Linus Torvalds
685d0b3a5SRichard Henderson  *
785d0b3a5SRichard Henderson  * This file contains date handling.
885d0b3a5SRichard Henderson  */
985d0b3a5SRichard Henderson #include <linux/errno.h>
1085d0b3a5SRichard Henderson #include <linux/init.h>
1185d0b3a5SRichard Henderson #include <linux/kernel.h>
1285d0b3a5SRichard Henderson #include <linux/param.h>
1385d0b3a5SRichard Henderson #include <linux/string.h>
1485d0b3a5SRichard Henderson #include <linux/mc146818rtc.h>
1585d0b3a5SRichard Henderson #include <linux/bcd.h>
1685d0b3a5SRichard Henderson #include <linux/rtc.h>
1785d0b3a5SRichard Henderson #include <linux/platform_device.h>
1885d0b3a5SRichard Henderson 
1985d0b3a5SRichard Henderson #include "proto.h"
2085d0b3a5SRichard Henderson 
2185d0b3a5SRichard Henderson 
2285d0b3a5SRichard Henderson /*
2385d0b3a5SRichard Henderson  * Support for the RTC device.
2485d0b3a5SRichard Henderson  *
2585d0b3a5SRichard Henderson  * We don't want to use the rtc-cmos driver, because we don't want to support
2685d0b3a5SRichard Henderson  * alarms, as that would be indistinguishable from timer interrupts.
2785d0b3a5SRichard Henderson  *
2885d0b3a5SRichard Henderson  * Further, generic code is really, really tied to a 1900 epoch.  This is
2985d0b3a5SRichard Henderson  * true in __get_rtc_time as well as the users of struct rtc_time e.g.
3085d0b3a5SRichard Henderson  * rtc_tm_to_time.  Thankfully all of the other epochs in use are later
3185d0b3a5SRichard Henderson  * than 1900, and so it's easy to adjust.
3285d0b3a5SRichard Henderson  */
3385d0b3a5SRichard Henderson 
3485d0b3a5SRichard Henderson static unsigned long rtc_epoch;
3585d0b3a5SRichard Henderson 
3685d0b3a5SRichard Henderson static int __init
specifiy_epoch(char * str)3785d0b3a5SRichard Henderson specifiy_epoch(char *str)
3885d0b3a5SRichard Henderson {
3985d0b3a5SRichard Henderson 	unsigned long epoch = simple_strtoul(str, NULL, 0);
4085d0b3a5SRichard Henderson 	if (epoch < 1900)
4185d0b3a5SRichard Henderson 		printk("Ignoring invalid user specified epoch %lu\n", epoch);
4285d0b3a5SRichard Henderson 	else
4385d0b3a5SRichard Henderson 		rtc_epoch = epoch;
4485d0b3a5SRichard Henderson 	return 1;
4585d0b3a5SRichard Henderson }
4685d0b3a5SRichard Henderson __setup("epoch=", specifiy_epoch);
4785d0b3a5SRichard Henderson 
4885d0b3a5SRichard Henderson static void __init
init_rtc_epoch(void)4985d0b3a5SRichard Henderson init_rtc_epoch(void)
5085d0b3a5SRichard Henderson {
5185d0b3a5SRichard Henderson 	int epoch, year, ctrl;
5285d0b3a5SRichard Henderson 
5385d0b3a5SRichard Henderson 	if (rtc_epoch != 0) {
5485d0b3a5SRichard Henderson 		/* The epoch was specified on the command-line.  */
5585d0b3a5SRichard Henderson 		return;
5685d0b3a5SRichard Henderson 	}
5785d0b3a5SRichard Henderson 
5885d0b3a5SRichard Henderson 	/* Detect the epoch in use on this computer.  */
5985d0b3a5SRichard Henderson 	ctrl = CMOS_READ(RTC_CONTROL);
6085d0b3a5SRichard Henderson 	year = CMOS_READ(RTC_YEAR);
6185d0b3a5SRichard Henderson 	if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
6285d0b3a5SRichard Henderson 		year = bcd2bin(year);
6385d0b3a5SRichard Henderson 
6485d0b3a5SRichard Henderson 	/* PC-like is standard; used for year >= 70 */
6585d0b3a5SRichard Henderson 	epoch = 1900;
6685d0b3a5SRichard Henderson 	if (year < 20) {
6785d0b3a5SRichard Henderson 		epoch = 2000;
6885d0b3a5SRichard Henderson 	} else if (year >= 20 && year < 48) {
6985d0b3a5SRichard Henderson 		/* NT epoch */
7085d0b3a5SRichard Henderson 		epoch = 1980;
7185d0b3a5SRichard Henderson 	} else if (year >= 48 && year < 70) {
7285d0b3a5SRichard Henderson 		/* Digital UNIX epoch */
7385d0b3a5SRichard Henderson 		epoch = 1952;
7485d0b3a5SRichard Henderson 	}
7585d0b3a5SRichard Henderson 	rtc_epoch = epoch;
7685d0b3a5SRichard Henderson 
7785d0b3a5SRichard Henderson 	printk(KERN_INFO "Using epoch %d for rtc year %d\n", epoch, year);
7885d0b3a5SRichard Henderson }
7985d0b3a5SRichard Henderson 
8085d0b3a5SRichard Henderson static int
alpha_rtc_read_time(struct device * dev,struct rtc_time * tm)8185d0b3a5SRichard Henderson alpha_rtc_read_time(struct device *dev, struct rtc_time *tm)
8285d0b3a5SRichard Henderson {
83*9d201856SMario Limonciello 	int ret = mc146818_get_time(tm, 10);
840dd8d6cbSMateusz Jończyk 
850dd8d6cbSMateusz Jończyk 	if (ret < 0) {
860dd8d6cbSMateusz Jończyk 		dev_err_ratelimited(dev, "unable to read current time\n");
870dd8d6cbSMateusz Jończyk 		return ret;
880dd8d6cbSMateusz Jończyk 	}
8985d0b3a5SRichard Henderson 
9085d0b3a5SRichard Henderson 	/* Adjust for non-default epochs.  It's easier to depend on the
9185d0b3a5SRichard Henderson 	   generic __get_rtc_time and adjust the epoch here than create
9285d0b3a5SRichard Henderson 	   a copy of __get_rtc_time with the edits we need.  */
9385d0b3a5SRichard Henderson 	if (rtc_epoch != 1900) {
9485d0b3a5SRichard Henderson 		int year = tm->tm_year;
9585d0b3a5SRichard Henderson 		/* Undo the century adjustment made in __get_rtc_time.  */
9685d0b3a5SRichard Henderson 		if (year >= 100)
9785d0b3a5SRichard Henderson 			year -= 100;
9885d0b3a5SRichard Henderson 		year += rtc_epoch - 1900;
9985d0b3a5SRichard Henderson 		/* Redo the century adjustment with the epoch in place.  */
10085d0b3a5SRichard Henderson 		if (year <= 69)
10185d0b3a5SRichard Henderson 			year += 100;
10285d0b3a5SRichard Henderson 		tm->tm_year = year;
10385d0b3a5SRichard Henderson 	}
10485d0b3a5SRichard Henderson 
10554f16b19SAlexandre Belloni 	return 0;
10685d0b3a5SRichard Henderson }
10785d0b3a5SRichard Henderson 
10885d0b3a5SRichard Henderson static int
alpha_rtc_set_time(struct device * dev,struct rtc_time * tm)10985d0b3a5SRichard Henderson alpha_rtc_set_time(struct device *dev, struct rtc_time *tm)
11085d0b3a5SRichard Henderson {
11185d0b3a5SRichard Henderson 	struct rtc_time xtm;
11285d0b3a5SRichard Henderson 
11385d0b3a5SRichard Henderson 	if (rtc_epoch != 1900) {
11485d0b3a5SRichard Henderson 		xtm = *tm;
11585d0b3a5SRichard Henderson 		xtm.tm_year -= rtc_epoch - 1900;
11685d0b3a5SRichard Henderson 		tm = &xtm;
11785d0b3a5SRichard Henderson 	}
11885d0b3a5SRichard Henderson 
1195ab788d7SArnd Bergmann 	return mc146818_set_time(tm);
12085d0b3a5SRichard Henderson }
12185d0b3a5SRichard Henderson 
12285d0b3a5SRichard Henderson static int
alpha_rtc_ioctl(struct device * dev,unsigned int cmd,unsigned long arg)12385d0b3a5SRichard Henderson alpha_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
12485d0b3a5SRichard Henderson {
12585d0b3a5SRichard Henderson 	switch (cmd) {
12685d0b3a5SRichard Henderson 	case RTC_EPOCH_READ:
12785d0b3a5SRichard Henderson 		return put_user(rtc_epoch, (unsigned long __user *)arg);
12885d0b3a5SRichard Henderson 	case RTC_EPOCH_SET:
12985d0b3a5SRichard Henderson 		if (arg < 1900)
13085d0b3a5SRichard Henderson 			return -EINVAL;
13185d0b3a5SRichard Henderson 		rtc_epoch = arg;
13285d0b3a5SRichard Henderson 		return 0;
13385d0b3a5SRichard Henderson 	default:
13485d0b3a5SRichard Henderson 		return -ENOIOCTLCMD;
13585d0b3a5SRichard Henderson 	}
13685d0b3a5SRichard Henderson }
13785d0b3a5SRichard Henderson 
13885d0b3a5SRichard Henderson static const struct rtc_class_ops alpha_rtc_ops = {
13985d0b3a5SRichard Henderson 	.read_time = alpha_rtc_read_time,
14085d0b3a5SRichard Henderson 	.set_time = alpha_rtc_set_time,
14185d0b3a5SRichard Henderson 	.ioctl = alpha_rtc_ioctl,
14285d0b3a5SRichard Henderson };
14385d0b3a5SRichard Henderson 
14485d0b3a5SRichard Henderson /*
14585d0b3a5SRichard Henderson  * Similarly, except do the actual CMOS access on the boot cpu only.
14685d0b3a5SRichard Henderson  * This requires marshalling the data across an interprocessor call.
14785d0b3a5SRichard Henderson  */
14885d0b3a5SRichard Henderson 
14985d0b3a5SRichard Henderson #if defined(CONFIG_SMP) && \
15085d0b3a5SRichard Henderson     (defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_MARVEL))
15185d0b3a5SRichard Henderson # define HAVE_REMOTE_RTC 1
15285d0b3a5SRichard Henderson 
15385d0b3a5SRichard Henderson union remote_data {
15485d0b3a5SRichard Henderson 	struct rtc_time *tm;
15585d0b3a5SRichard Henderson 	long retval;
15685d0b3a5SRichard Henderson };
15785d0b3a5SRichard Henderson 
15885d0b3a5SRichard Henderson static void
do_remote_read(void * data)15985d0b3a5SRichard Henderson do_remote_read(void *data)
16085d0b3a5SRichard Henderson {
16185d0b3a5SRichard Henderson 	union remote_data *x = data;
16285d0b3a5SRichard Henderson 	x->retval = alpha_rtc_read_time(NULL, x->tm);
16385d0b3a5SRichard Henderson }
16485d0b3a5SRichard Henderson 
16585d0b3a5SRichard Henderson static int
remote_read_time(struct device * dev,struct rtc_time * tm)16685d0b3a5SRichard Henderson remote_read_time(struct device *dev, struct rtc_time *tm)
16785d0b3a5SRichard Henderson {
16885d0b3a5SRichard Henderson 	union remote_data x;
16985d0b3a5SRichard Henderson 	if (smp_processor_id() != boot_cpuid) {
17085d0b3a5SRichard Henderson 		x.tm = tm;
17185d0b3a5SRichard Henderson 		smp_call_function_single(boot_cpuid, do_remote_read, &x, 1);
17285d0b3a5SRichard Henderson 		return x.retval;
17385d0b3a5SRichard Henderson 	}
17485d0b3a5SRichard Henderson 	return alpha_rtc_read_time(NULL, tm);
17585d0b3a5SRichard Henderson }
17685d0b3a5SRichard Henderson 
17785d0b3a5SRichard Henderson static void
do_remote_set(void * data)17885d0b3a5SRichard Henderson do_remote_set(void *data)
17985d0b3a5SRichard Henderson {
18085d0b3a5SRichard Henderson 	union remote_data *x = data;
18185d0b3a5SRichard Henderson 	x->retval = alpha_rtc_set_time(NULL, x->tm);
18285d0b3a5SRichard Henderson }
18385d0b3a5SRichard Henderson 
18485d0b3a5SRichard Henderson static int
remote_set_time(struct device * dev,struct rtc_time * tm)18585d0b3a5SRichard Henderson remote_set_time(struct device *dev, struct rtc_time *tm)
18685d0b3a5SRichard Henderson {
18785d0b3a5SRichard Henderson 	union remote_data x;
18885d0b3a5SRichard Henderson 	if (smp_processor_id() != boot_cpuid) {
18985d0b3a5SRichard Henderson 		x.tm = tm;
19085d0b3a5SRichard Henderson 		smp_call_function_single(boot_cpuid, do_remote_set, &x, 1);
19185d0b3a5SRichard Henderson 		return x.retval;
19285d0b3a5SRichard Henderson 	}
19385d0b3a5SRichard Henderson 	return alpha_rtc_set_time(NULL, tm);
19485d0b3a5SRichard Henderson }
19585d0b3a5SRichard Henderson 
19685d0b3a5SRichard Henderson static const struct rtc_class_ops remote_rtc_ops = {
19785d0b3a5SRichard Henderson 	.read_time = remote_read_time,
19885d0b3a5SRichard Henderson 	.set_time = remote_set_time,
19985d0b3a5SRichard Henderson 	.ioctl = alpha_rtc_ioctl,
20085d0b3a5SRichard Henderson };
20185d0b3a5SRichard Henderson #endif
20285d0b3a5SRichard Henderson 
20385d0b3a5SRichard Henderson static int __init
alpha_rtc_init(void)20485d0b3a5SRichard Henderson alpha_rtc_init(void)
20585d0b3a5SRichard Henderson {
20685d0b3a5SRichard Henderson 	struct platform_device *pdev;
20785d0b3a5SRichard Henderson 	struct rtc_device *rtc;
20885d0b3a5SRichard Henderson 
20985d0b3a5SRichard Henderson 	init_rtc_epoch();
21085d0b3a5SRichard Henderson 
2113030cf95SAlexandre Belloni 	pdev = platform_device_register_simple("rtc-alpha", -1, NULL, 0);
2123030cf95SAlexandre Belloni 	rtc = devm_rtc_allocate_device(&pdev->dev);
21385d0b3a5SRichard Henderson 	if (IS_ERR(rtc))
21485d0b3a5SRichard Henderson 		return PTR_ERR(rtc);
21585d0b3a5SRichard Henderson 
21685d0b3a5SRichard Henderson 	platform_set_drvdata(pdev, rtc);
2173030cf95SAlexandre Belloni 	rtc->ops = &alpha_rtc_ops;
2183030cf95SAlexandre Belloni 
2193030cf95SAlexandre Belloni #ifdef HAVE_REMOTE_RTC
2203030cf95SAlexandre Belloni 	if (alpha_mv.rtc_boot_cpu_only)
2213030cf95SAlexandre Belloni 		rtc->ops = &remote_rtc_ops;
2223030cf95SAlexandre Belloni #endif
2233030cf95SAlexandre Belloni 
224fdcfd854SBartosz Golaszewski 	return devm_rtc_register_device(rtc);
22585d0b3a5SRichard Henderson }
22685d0b3a5SRichard Henderson device_initcall(alpha_rtc_init);
227