162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/arch/alpha/kernel/rtc.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1991, 1992, 1995, 1999, 2000 Linus Torvalds 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This file contains date handling. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/param.h> 1362306a36Sopenharmony_ci#include <linux/string.h> 1462306a36Sopenharmony_ci#include <linux/mc146818rtc.h> 1562306a36Sopenharmony_ci#include <linux/bcd.h> 1662306a36Sopenharmony_ci#include <linux/rtc.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "proto.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * Support for the RTC device. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * We don't want to use the rtc-cmos driver, because we don't want to support 2662306a36Sopenharmony_ci * alarms, as that would be indistinguishable from timer interrupts. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * Further, generic code is really, really tied to a 1900 epoch. This is 2962306a36Sopenharmony_ci * true in __get_rtc_time as well as the users of struct rtc_time e.g. 3062306a36Sopenharmony_ci * rtc_tm_to_time. Thankfully all of the other epochs in use are later 3162306a36Sopenharmony_ci * than 1900, and so it's easy to adjust. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic unsigned long rtc_epoch; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int __init 3762306a36Sopenharmony_cispecifiy_epoch(char *str) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci unsigned long epoch = simple_strtoul(str, NULL, 0); 4062306a36Sopenharmony_ci if (epoch < 1900) 4162306a36Sopenharmony_ci printk("Ignoring invalid user specified epoch %lu\n", epoch); 4262306a36Sopenharmony_ci else 4362306a36Sopenharmony_ci rtc_epoch = epoch; 4462306a36Sopenharmony_ci return 1; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci__setup("epoch=", specifiy_epoch); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void __init 4962306a36Sopenharmony_ciinit_rtc_epoch(void) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci int epoch, year, ctrl; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (rtc_epoch != 0) { 5462306a36Sopenharmony_ci /* The epoch was specified on the command-line. */ 5562306a36Sopenharmony_ci return; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* Detect the epoch in use on this computer. */ 5962306a36Sopenharmony_ci ctrl = CMOS_READ(RTC_CONTROL); 6062306a36Sopenharmony_ci year = CMOS_READ(RTC_YEAR); 6162306a36Sopenharmony_ci if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) 6262306a36Sopenharmony_ci year = bcd2bin(year); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* PC-like is standard; used for year >= 70 */ 6562306a36Sopenharmony_ci epoch = 1900; 6662306a36Sopenharmony_ci if (year < 20) { 6762306a36Sopenharmony_ci epoch = 2000; 6862306a36Sopenharmony_ci } else if (year >= 20 && year < 48) { 6962306a36Sopenharmony_ci /* NT epoch */ 7062306a36Sopenharmony_ci epoch = 1980; 7162306a36Sopenharmony_ci } else if (year >= 48 && year < 70) { 7262306a36Sopenharmony_ci /* Digital UNIX epoch */ 7362306a36Sopenharmony_ci epoch = 1952; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci rtc_epoch = epoch; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci printk(KERN_INFO "Using epoch %d for rtc year %d\n", epoch, year); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int 8162306a36Sopenharmony_cialpha_rtc_read_time(struct device *dev, struct rtc_time *tm) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci int ret = mc146818_get_time(tm, 10); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (ret < 0) { 8662306a36Sopenharmony_ci dev_err_ratelimited(dev, "unable to read current time\n"); 8762306a36Sopenharmony_ci return ret; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* Adjust for non-default epochs. It's easier to depend on the 9162306a36Sopenharmony_ci generic __get_rtc_time and adjust the epoch here than create 9262306a36Sopenharmony_ci a copy of __get_rtc_time with the edits we need. */ 9362306a36Sopenharmony_ci if (rtc_epoch != 1900) { 9462306a36Sopenharmony_ci int year = tm->tm_year; 9562306a36Sopenharmony_ci /* Undo the century adjustment made in __get_rtc_time. */ 9662306a36Sopenharmony_ci if (year >= 100) 9762306a36Sopenharmony_ci year -= 100; 9862306a36Sopenharmony_ci year += rtc_epoch - 1900; 9962306a36Sopenharmony_ci /* Redo the century adjustment with the epoch in place. */ 10062306a36Sopenharmony_ci if (year <= 69) 10162306a36Sopenharmony_ci year += 100; 10262306a36Sopenharmony_ci tm->tm_year = year; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int 10962306a36Sopenharmony_cialpha_rtc_set_time(struct device *dev, struct rtc_time *tm) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct rtc_time xtm; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (rtc_epoch != 1900) { 11462306a36Sopenharmony_ci xtm = *tm; 11562306a36Sopenharmony_ci xtm.tm_year -= rtc_epoch - 1900; 11662306a36Sopenharmony_ci tm = &xtm; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return mc146818_set_time(tm); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int 12362306a36Sopenharmony_cialpha_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci switch (cmd) { 12662306a36Sopenharmony_ci case RTC_EPOCH_READ: 12762306a36Sopenharmony_ci return put_user(rtc_epoch, (unsigned long __user *)arg); 12862306a36Sopenharmony_ci case RTC_EPOCH_SET: 12962306a36Sopenharmony_ci if (arg < 1900) 13062306a36Sopenharmony_ci return -EINVAL; 13162306a36Sopenharmony_ci rtc_epoch = arg; 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci default: 13462306a36Sopenharmony_ci return -ENOIOCTLCMD; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic const struct rtc_class_ops alpha_rtc_ops = { 13962306a36Sopenharmony_ci .read_time = alpha_rtc_read_time, 14062306a36Sopenharmony_ci .set_time = alpha_rtc_set_time, 14162306a36Sopenharmony_ci .ioctl = alpha_rtc_ioctl, 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* 14562306a36Sopenharmony_ci * Similarly, except do the actual CMOS access on the boot cpu only. 14662306a36Sopenharmony_ci * This requires marshalling the data across an interprocessor call. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci#if defined(CONFIG_SMP) && \ 15062306a36Sopenharmony_ci (defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_MARVEL)) 15162306a36Sopenharmony_ci# define HAVE_REMOTE_RTC 1 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ciunion remote_data { 15462306a36Sopenharmony_ci struct rtc_time *tm; 15562306a36Sopenharmony_ci long retval; 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void 15962306a36Sopenharmony_cido_remote_read(void *data) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci union remote_data *x = data; 16262306a36Sopenharmony_ci x->retval = alpha_rtc_read_time(NULL, x->tm); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int 16662306a36Sopenharmony_ciremote_read_time(struct device *dev, struct rtc_time *tm) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci union remote_data x; 16962306a36Sopenharmony_ci if (smp_processor_id() != boot_cpuid) { 17062306a36Sopenharmony_ci x.tm = tm; 17162306a36Sopenharmony_ci smp_call_function_single(boot_cpuid, do_remote_read, &x, 1); 17262306a36Sopenharmony_ci return x.retval; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci return alpha_rtc_read_time(NULL, tm); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void 17862306a36Sopenharmony_cido_remote_set(void *data) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci union remote_data *x = data; 18162306a36Sopenharmony_ci x->retval = alpha_rtc_set_time(NULL, x->tm); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int 18562306a36Sopenharmony_ciremote_set_time(struct device *dev, struct rtc_time *tm) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci union remote_data x; 18862306a36Sopenharmony_ci if (smp_processor_id() != boot_cpuid) { 18962306a36Sopenharmony_ci x.tm = tm; 19062306a36Sopenharmony_ci smp_call_function_single(boot_cpuid, do_remote_set, &x, 1); 19162306a36Sopenharmony_ci return x.retval; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci return alpha_rtc_set_time(NULL, tm); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic const struct rtc_class_ops remote_rtc_ops = { 19762306a36Sopenharmony_ci .read_time = remote_read_time, 19862306a36Sopenharmony_ci .set_time = remote_set_time, 19962306a36Sopenharmony_ci .ioctl = alpha_rtc_ioctl, 20062306a36Sopenharmony_ci}; 20162306a36Sopenharmony_ci#endif 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int __init 20462306a36Sopenharmony_cialpha_rtc_init(void) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct platform_device *pdev; 20762306a36Sopenharmony_ci struct rtc_device *rtc; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci init_rtc_epoch(); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci pdev = platform_device_register_simple("rtc-alpha", -1, NULL, 0); 21262306a36Sopenharmony_ci rtc = devm_rtc_allocate_device(&pdev->dev); 21362306a36Sopenharmony_ci if (IS_ERR(rtc)) 21462306a36Sopenharmony_ci return PTR_ERR(rtc); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci platform_set_drvdata(pdev, rtc); 21762306a36Sopenharmony_ci rtc->ops = &alpha_rtc_ops; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci#ifdef HAVE_REMOTE_RTC 22062306a36Sopenharmony_ci if (alpha_mv.rtc_boot_cpu_only) 22162306a36Sopenharmony_ci rtc->ops = &remote_rtc_ops; 22262306a36Sopenharmony_ci#endif 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return devm_rtc_register_device(rtc); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_cidevice_initcall(alpha_rtc_init); 227