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 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 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 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 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 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 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 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 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 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 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