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