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 { 835ab788d7SArnd Bergmann mc146818_get_time(tm); 8485d0b3a5SRichard Henderson 8585d0b3a5SRichard Henderson /* Adjust for non-default epochs. It's easier to depend on the 8685d0b3a5SRichard Henderson generic __get_rtc_time and adjust the epoch here than create 8785d0b3a5SRichard Henderson a copy of __get_rtc_time with the edits we need. */ 8885d0b3a5SRichard Henderson if (rtc_epoch != 1900) { 8985d0b3a5SRichard Henderson int year = tm->tm_year; 9085d0b3a5SRichard Henderson /* Undo the century adjustment made in __get_rtc_time. */ 9185d0b3a5SRichard Henderson if (year >= 100) 9285d0b3a5SRichard Henderson year -= 100; 9385d0b3a5SRichard Henderson year += rtc_epoch - 1900; 9485d0b3a5SRichard Henderson /* Redo the century adjustment with the epoch in place. */ 9585d0b3a5SRichard Henderson if (year <= 69) 9685d0b3a5SRichard Henderson year += 100; 9785d0b3a5SRichard Henderson tm->tm_year = year; 9885d0b3a5SRichard Henderson } 9985d0b3a5SRichard Henderson 10054f16b19SAlexandre Belloni return 0; 10185d0b3a5SRichard Henderson } 10285d0b3a5SRichard Henderson 10385d0b3a5SRichard Henderson static int 10485d0b3a5SRichard Henderson alpha_rtc_set_time(struct device *dev, struct rtc_time *tm) 10585d0b3a5SRichard Henderson { 10685d0b3a5SRichard Henderson struct rtc_time xtm; 10785d0b3a5SRichard Henderson 10885d0b3a5SRichard Henderson if (rtc_epoch != 1900) { 10985d0b3a5SRichard Henderson xtm = *tm; 11085d0b3a5SRichard Henderson xtm.tm_year -= rtc_epoch - 1900; 11185d0b3a5SRichard Henderson tm = &xtm; 11285d0b3a5SRichard Henderson } 11385d0b3a5SRichard Henderson 1145ab788d7SArnd Bergmann return mc146818_set_time(tm); 11585d0b3a5SRichard Henderson } 11685d0b3a5SRichard Henderson 11785d0b3a5SRichard Henderson static int 11885d0b3a5SRichard Henderson alpha_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) 11985d0b3a5SRichard Henderson { 12085d0b3a5SRichard Henderson switch (cmd) { 12185d0b3a5SRichard Henderson case RTC_EPOCH_READ: 12285d0b3a5SRichard Henderson return put_user(rtc_epoch, (unsigned long __user *)arg); 12385d0b3a5SRichard Henderson case RTC_EPOCH_SET: 12485d0b3a5SRichard Henderson if (arg < 1900) 12585d0b3a5SRichard Henderson return -EINVAL; 12685d0b3a5SRichard Henderson rtc_epoch = arg; 12785d0b3a5SRichard Henderson return 0; 12885d0b3a5SRichard Henderson default: 12985d0b3a5SRichard Henderson return -ENOIOCTLCMD; 13085d0b3a5SRichard Henderson } 13185d0b3a5SRichard Henderson } 13285d0b3a5SRichard Henderson 13385d0b3a5SRichard Henderson static const struct rtc_class_ops alpha_rtc_ops = { 13485d0b3a5SRichard Henderson .read_time = alpha_rtc_read_time, 13585d0b3a5SRichard Henderson .set_time = alpha_rtc_set_time, 13685d0b3a5SRichard Henderson .ioctl = alpha_rtc_ioctl, 13785d0b3a5SRichard Henderson }; 13885d0b3a5SRichard Henderson 13985d0b3a5SRichard Henderson /* 14085d0b3a5SRichard Henderson * Similarly, except do the actual CMOS access on the boot cpu only. 14185d0b3a5SRichard Henderson * This requires marshalling the data across an interprocessor call. 14285d0b3a5SRichard Henderson */ 14385d0b3a5SRichard Henderson 14485d0b3a5SRichard Henderson #if defined(CONFIG_SMP) && \ 14585d0b3a5SRichard Henderson (defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_MARVEL)) 14685d0b3a5SRichard Henderson # define HAVE_REMOTE_RTC 1 14785d0b3a5SRichard Henderson 14885d0b3a5SRichard Henderson union remote_data { 14985d0b3a5SRichard Henderson struct rtc_time *tm; 15085d0b3a5SRichard Henderson long retval; 15185d0b3a5SRichard Henderson }; 15285d0b3a5SRichard Henderson 15385d0b3a5SRichard Henderson static void 15485d0b3a5SRichard Henderson do_remote_read(void *data) 15585d0b3a5SRichard Henderson { 15685d0b3a5SRichard Henderson union remote_data *x = data; 15785d0b3a5SRichard Henderson x->retval = alpha_rtc_read_time(NULL, x->tm); 15885d0b3a5SRichard Henderson } 15985d0b3a5SRichard Henderson 16085d0b3a5SRichard Henderson static int 16185d0b3a5SRichard Henderson remote_read_time(struct device *dev, struct rtc_time *tm) 16285d0b3a5SRichard Henderson { 16385d0b3a5SRichard Henderson union remote_data x; 16485d0b3a5SRichard Henderson if (smp_processor_id() != boot_cpuid) { 16585d0b3a5SRichard Henderson x.tm = tm; 16685d0b3a5SRichard Henderson smp_call_function_single(boot_cpuid, do_remote_read, &x, 1); 16785d0b3a5SRichard Henderson return x.retval; 16885d0b3a5SRichard Henderson } 16985d0b3a5SRichard Henderson return alpha_rtc_read_time(NULL, tm); 17085d0b3a5SRichard Henderson } 17185d0b3a5SRichard Henderson 17285d0b3a5SRichard Henderson static void 17385d0b3a5SRichard Henderson do_remote_set(void *data) 17485d0b3a5SRichard Henderson { 17585d0b3a5SRichard Henderson union remote_data *x = data; 17685d0b3a5SRichard Henderson x->retval = alpha_rtc_set_time(NULL, x->tm); 17785d0b3a5SRichard Henderson } 17885d0b3a5SRichard Henderson 17985d0b3a5SRichard Henderson static int 18085d0b3a5SRichard Henderson remote_set_time(struct device *dev, struct rtc_time *tm) 18185d0b3a5SRichard Henderson { 18285d0b3a5SRichard Henderson union remote_data x; 18385d0b3a5SRichard Henderson if (smp_processor_id() != boot_cpuid) { 18485d0b3a5SRichard Henderson x.tm = tm; 18585d0b3a5SRichard Henderson smp_call_function_single(boot_cpuid, do_remote_set, &x, 1); 18685d0b3a5SRichard Henderson return x.retval; 18785d0b3a5SRichard Henderson } 18885d0b3a5SRichard Henderson return alpha_rtc_set_time(NULL, tm); 18985d0b3a5SRichard Henderson } 19085d0b3a5SRichard Henderson 19185d0b3a5SRichard Henderson static const struct rtc_class_ops remote_rtc_ops = { 19285d0b3a5SRichard Henderson .read_time = remote_read_time, 19385d0b3a5SRichard Henderson .set_time = remote_set_time, 19485d0b3a5SRichard Henderson .ioctl = alpha_rtc_ioctl, 19585d0b3a5SRichard Henderson }; 19685d0b3a5SRichard Henderson #endif 19785d0b3a5SRichard Henderson 19885d0b3a5SRichard Henderson static int __init 19985d0b3a5SRichard Henderson alpha_rtc_init(void) 20085d0b3a5SRichard Henderson { 20185d0b3a5SRichard Henderson struct platform_device *pdev; 20285d0b3a5SRichard Henderson struct rtc_device *rtc; 20385d0b3a5SRichard Henderson 20485d0b3a5SRichard Henderson init_rtc_epoch(); 20585d0b3a5SRichard Henderson 206*3030cf95SAlexandre Belloni pdev = platform_device_register_simple("rtc-alpha", -1, NULL, 0); 207*3030cf95SAlexandre Belloni rtc = devm_rtc_allocate_device(&pdev->dev); 20885d0b3a5SRichard Henderson if (IS_ERR(rtc)) 20985d0b3a5SRichard Henderson return PTR_ERR(rtc); 21085d0b3a5SRichard Henderson 21185d0b3a5SRichard Henderson platform_set_drvdata(pdev, rtc); 212*3030cf95SAlexandre Belloni rtc->ops = &alpha_rtc_ops; 213*3030cf95SAlexandre Belloni 214*3030cf95SAlexandre Belloni #ifdef HAVE_REMOTE_RTC 215*3030cf95SAlexandre Belloni if (alpha_mv.rtc_boot_cpu_only) 216*3030cf95SAlexandre Belloni rtc->ops = &remote_rtc_ops; 217*3030cf95SAlexandre Belloni #endif 218*3030cf95SAlexandre Belloni 219*3030cf95SAlexandre Belloni return rtc_register_device(rtc); 22085d0b3a5SRichard Henderson } 22185d0b3a5SRichard Henderson device_initcall(alpha_rtc_init); 222