185d0b3a5SRichard Henderson /* 285d0b3a5SRichard Henderson * linux/arch/alpha/kernel/rtc.c 385d0b3a5SRichard Henderson * 485d0b3a5SRichard Henderson * Copyright (C) 1991, 1992, 1995, 1999, 2000 Linus Torvalds 585d0b3a5SRichard Henderson * 685d0b3a5SRichard Henderson * This file contains date handling. 785d0b3a5SRichard Henderson */ 885d0b3a5SRichard Henderson #include <linux/errno.h> 985d0b3a5SRichard Henderson #include <linux/init.h> 1085d0b3a5SRichard Henderson #include <linux/kernel.h> 1185d0b3a5SRichard Henderson #include <linux/param.h> 1285d0b3a5SRichard Henderson #include <linux/string.h> 1385d0b3a5SRichard Henderson #include <linux/mc146818rtc.h> 1485d0b3a5SRichard Henderson #include <linux/bcd.h> 1585d0b3a5SRichard Henderson #include <linux/rtc.h> 1685d0b3a5SRichard Henderson #include <linux/platform_device.h> 1785d0b3a5SRichard Henderson 1885d0b3a5SRichard Henderson #include "proto.h" 1985d0b3a5SRichard Henderson 2085d0b3a5SRichard Henderson 2185d0b3a5SRichard Henderson /* 2285d0b3a5SRichard Henderson * Support for the RTC device. 2385d0b3a5SRichard Henderson * 2485d0b3a5SRichard Henderson * We don't want to use the rtc-cmos driver, because we don't want to support 2585d0b3a5SRichard Henderson * alarms, as that would be indistinguishable from timer interrupts. 2685d0b3a5SRichard Henderson * 2785d0b3a5SRichard Henderson * Further, generic code is really, really tied to a 1900 epoch. This is 2885d0b3a5SRichard Henderson * true in __get_rtc_time as well as the users of struct rtc_time e.g. 2985d0b3a5SRichard Henderson * rtc_tm_to_time. Thankfully all of the other epochs in use are later 3085d0b3a5SRichard Henderson * than 1900, and so it's easy to adjust. 3185d0b3a5SRichard Henderson */ 3285d0b3a5SRichard Henderson 3385d0b3a5SRichard Henderson static unsigned long rtc_epoch; 3485d0b3a5SRichard Henderson 3585d0b3a5SRichard Henderson static int __init 3685d0b3a5SRichard Henderson specifiy_epoch(char *str) 3785d0b3a5SRichard Henderson { 3885d0b3a5SRichard Henderson unsigned long epoch = simple_strtoul(str, NULL, 0); 3985d0b3a5SRichard Henderson if (epoch < 1900) 4085d0b3a5SRichard Henderson printk("Ignoring invalid user specified epoch %lu\n", epoch); 4185d0b3a5SRichard Henderson else 4285d0b3a5SRichard Henderson rtc_epoch = epoch; 4385d0b3a5SRichard Henderson return 1; 4485d0b3a5SRichard Henderson } 4585d0b3a5SRichard Henderson __setup("epoch=", specifiy_epoch); 4685d0b3a5SRichard Henderson 4785d0b3a5SRichard Henderson static void __init 4885d0b3a5SRichard Henderson init_rtc_epoch(void) 4985d0b3a5SRichard Henderson { 5085d0b3a5SRichard Henderson int epoch, year, ctrl; 5185d0b3a5SRichard Henderson 5285d0b3a5SRichard Henderson if (rtc_epoch != 0) { 5385d0b3a5SRichard Henderson /* The epoch was specified on the command-line. */ 5485d0b3a5SRichard Henderson return; 5585d0b3a5SRichard Henderson } 5685d0b3a5SRichard Henderson 5785d0b3a5SRichard Henderson /* Detect the epoch in use on this computer. */ 5885d0b3a5SRichard Henderson ctrl = CMOS_READ(RTC_CONTROL); 5985d0b3a5SRichard Henderson year = CMOS_READ(RTC_YEAR); 6085d0b3a5SRichard Henderson if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) 6185d0b3a5SRichard Henderson year = bcd2bin(year); 6285d0b3a5SRichard Henderson 6385d0b3a5SRichard Henderson /* PC-like is standard; used for year >= 70 */ 6485d0b3a5SRichard Henderson epoch = 1900; 6585d0b3a5SRichard Henderson if (year < 20) { 6685d0b3a5SRichard Henderson epoch = 2000; 6785d0b3a5SRichard Henderson } else if (year >= 20 && year < 48) { 6885d0b3a5SRichard Henderson /* NT epoch */ 6985d0b3a5SRichard Henderson epoch = 1980; 7085d0b3a5SRichard Henderson } else if (year >= 48 && year < 70) { 7185d0b3a5SRichard Henderson /* Digital UNIX epoch */ 7285d0b3a5SRichard Henderson epoch = 1952; 7385d0b3a5SRichard Henderson } 7485d0b3a5SRichard Henderson rtc_epoch = epoch; 7585d0b3a5SRichard Henderson 7685d0b3a5SRichard Henderson printk(KERN_INFO "Using epoch %d for rtc year %d\n", epoch, year); 7785d0b3a5SRichard Henderson } 7885d0b3a5SRichard Henderson 7985d0b3a5SRichard Henderson static int 8085d0b3a5SRichard Henderson alpha_rtc_read_time(struct device *dev, struct rtc_time *tm) 8185d0b3a5SRichard Henderson { 82*5ab788d7SArnd Bergmann mc146818_get_time(tm); 8385d0b3a5SRichard Henderson 8485d0b3a5SRichard Henderson /* Adjust for non-default epochs. It's easier to depend on the 8585d0b3a5SRichard Henderson generic __get_rtc_time and adjust the epoch here than create 8685d0b3a5SRichard Henderson a copy of __get_rtc_time with the edits we need. */ 8785d0b3a5SRichard Henderson if (rtc_epoch != 1900) { 8885d0b3a5SRichard Henderson int year = tm->tm_year; 8985d0b3a5SRichard Henderson /* Undo the century adjustment made in __get_rtc_time. */ 9085d0b3a5SRichard Henderson if (year >= 100) 9185d0b3a5SRichard Henderson year -= 100; 9285d0b3a5SRichard Henderson year += rtc_epoch - 1900; 9385d0b3a5SRichard Henderson /* Redo the century adjustment with the epoch in place. */ 9485d0b3a5SRichard Henderson if (year <= 69) 9585d0b3a5SRichard Henderson year += 100; 9685d0b3a5SRichard Henderson tm->tm_year = year; 9785d0b3a5SRichard Henderson } 9885d0b3a5SRichard Henderson 9985d0b3a5SRichard Henderson return rtc_valid_tm(tm); 10085d0b3a5SRichard Henderson } 10185d0b3a5SRichard Henderson 10285d0b3a5SRichard Henderson static int 10385d0b3a5SRichard Henderson alpha_rtc_set_time(struct device *dev, struct rtc_time *tm) 10485d0b3a5SRichard Henderson { 10585d0b3a5SRichard Henderson struct rtc_time xtm; 10685d0b3a5SRichard Henderson 10785d0b3a5SRichard Henderson if (rtc_epoch != 1900) { 10885d0b3a5SRichard Henderson xtm = *tm; 10985d0b3a5SRichard Henderson xtm.tm_year -= rtc_epoch - 1900; 11085d0b3a5SRichard Henderson tm = &xtm; 11185d0b3a5SRichard Henderson } 11285d0b3a5SRichard Henderson 113*5ab788d7SArnd Bergmann return mc146818_set_time(tm); 11485d0b3a5SRichard Henderson } 11585d0b3a5SRichard Henderson 11685d0b3a5SRichard Henderson static int 117a5312f56SXunlei Pang alpha_rtc_set_mmss(struct device *dev, time64_t nowtime) 11885d0b3a5SRichard Henderson { 11985d0b3a5SRichard Henderson int retval = 0; 12085d0b3a5SRichard Henderson int real_seconds, real_minutes, cmos_minutes; 12185d0b3a5SRichard Henderson unsigned char save_control, save_freq_select; 12285d0b3a5SRichard Henderson 12385d0b3a5SRichard Henderson /* Note: This code only updates minutes and seconds. Comments 12485d0b3a5SRichard Henderson indicate this was to avoid messing with unknown time zones, 12585d0b3a5SRichard Henderson and with the epoch nonsense described above. In order for 12685d0b3a5SRichard Henderson this to work, the existing clock cannot be off by more than 12785d0b3a5SRichard Henderson 15 minutes. 12885d0b3a5SRichard Henderson 12985d0b3a5SRichard Henderson ??? This choice is may be out of date. The x86 port does 13085d0b3a5SRichard Henderson not have problems with timezones, and the epoch processing has 13185d0b3a5SRichard Henderson now been fixed in alpha_set_rtc_time. 13285d0b3a5SRichard Henderson 13385d0b3a5SRichard Henderson In either case, one can always force a full rtc update with 13485d0b3a5SRichard Henderson the userland hwclock program, so surely 15 minute accuracy 13585d0b3a5SRichard Henderson is no real burden. */ 13685d0b3a5SRichard Henderson 13785d0b3a5SRichard Henderson /* In order to set the CMOS clock precisely, we have to be called 13885d0b3a5SRichard Henderson 500 ms after the second nowtime has started, because when 13985d0b3a5SRichard Henderson nowtime is written into the registers of the CMOS clock, it will 14085d0b3a5SRichard Henderson jump to the next second precisely 500 ms later. Check the Motorola 14185d0b3a5SRichard Henderson MC146818A or Dallas DS12887 data sheet for details. */ 14285d0b3a5SRichard Henderson 14385d0b3a5SRichard Henderson /* irq are locally disabled here */ 14485d0b3a5SRichard Henderson spin_lock(&rtc_lock); 14585d0b3a5SRichard Henderson /* Tell the clock it's being set */ 14685d0b3a5SRichard Henderson save_control = CMOS_READ(RTC_CONTROL); 14785d0b3a5SRichard Henderson CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); 14885d0b3a5SRichard Henderson 14985d0b3a5SRichard Henderson /* Stop and reset prescaler */ 15085d0b3a5SRichard Henderson save_freq_select = CMOS_READ(RTC_FREQ_SELECT); 15185d0b3a5SRichard Henderson CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); 15285d0b3a5SRichard Henderson 15385d0b3a5SRichard Henderson cmos_minutes = CMOS_READ(RTC_MINUTES); 15485d0b3a5SRichard Henderson if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) 15585d0b3a5SRichard Henderson cmos_minutes = bcd2bin(cmos_minutes); 15685d0b3a5SRichard Henderson 15785d0b3a5SRichard Henderson real_seconds = nowtime % 60; 15885d0b3a5SRichard Henderson real_minutes = nowtime / 60; 15985d0b3a5SRichard Henderson if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1) { 16085d0b3a5SRichard Henderson /* correct for half hour time zone */ 16185d0b3a5SRichard Henderson real_minutes += 30; 16285d0b3a5SRichard Henderson } 16385d0b3a5SRichard Henderson real_minutes %= 60; 16485d0b3a5SRichard Henderson 16585d0b3a5SRichard Henderson if (abs(real_minutes - cmos_minutes) < 30) { 16685d0b3a5SRichard Henderson if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 16785d0b3a5SRichard Henderson real_seconds = bin2bcd(real_seconds); 16885d0b3a5SRichard Henderson real_minutes = bin2bcd(real_minutes); 16985d0b3a5SRichard Henderson } 17085d0b3a5SRichard Henderson CMOS_WRITE(real_seconds,RTC_SECONDS); 17185d0b3a5SRichard Henderson CMOS_WRITE(real_minutes,RTC_MINUTES); 17285d0b3a5SRichard Henderson } else { 17385d0b3a5SRichard Henderson printk_once(KERN_NOTICE 17485d0b3a5SRichard Henderson "set_rtc_mmss: can't update from %d to %d\n", 17585d0b3a5SRichard Henderson cmos_minutes, real_minutes); 17685d0b3a5SRichard Henderson retval = -1; 17785d0b3a5SRichard Henderson } 17885d0b3a5SRichard Henderson 17985d0b3a5SRichard Henderson /* The following flags have to be released exactly in this order, 18085d0b3a5SRichard Henderson * otherwise the DS12887 (popular MC146818A clone with integrated 18185d0b3a5SRichard Henderson * battery and quartz) will not reset the oscillator and will not 18285d0b3a5SRichard Henderson * update precisely 500 ms later. You won't find this mentioned in 18385d0b3a5SRichard Henderson * the Dallas Semiconductor data sheets, but who believes data 18485d0b3a5SRichard Henderson * sheets anyway ... -- Markus Kuhn 18585d0b3a5SRichard Henderson */ 18685d0b3a5SRichard Henderson CMOS_WRITE(save_control, RTC_CONTROL); 18785d0b3a5SRichard Henderson CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); 18885d0b3a5SRichard Henderson spin_unlock(&rtc_lock); 18985d0b3a5SRichard Henderson 19085d0b3a5SRichard Henderson return retval; 19185d0b3a5SRichard Henderson } 19285d0b3a5SRichard Henderson 19385d0b3a5SRichard Henderson static int 19485d0b3a5SRichard Henderson alpha_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) 19585d0b3a5SRichard Henderson { 19685d0b3a5SRichard Henderson switch (cmd) { 19785d0b3a5SRichard Henderson case RTC_EPOCH_READ: 19885d0b3a5SRichard Henderson return put_user(rtc_epoch, (unsigned long __user *)arg); 19985d0b3a5SRichard Henderson case RTC_EPOCH_SET: 20085d0b3a5SRichard Henderson if (arg < 1900) 20185d0b3a5SRichard Henderson return -EINVAL; 20285d0b3a5SRichard Henderson rtc_epoch = arg; 20385d0b3a5SRichard Henderson return 0; 20485d0b3a5SRichard Henderson default: 20585d0b3a5SRichard Henderson return -ENOIOCTLCMD; 20685d0b3a5SRichard Henderson } 20785d0b3a5SRichard Henderson } 20885d0b3a5SRichard Henderson 20985d0b3a5SRichard Henderson static const struct rtc_class_ops alpha_rtc_ops = { 21085d0b3a5SRichard Henderson .read_time = alpha_rtc_read_time, 21185d0b3a5SRichard Henderson .set_time = alpha_rtc_set_time, 212a5312f56SXunlei Pang .set_mmss64 = alpha_rtc_set_mmss, 21385d0b3a5SRichard Henderson .ioctl = alpha_rtc_ioctl, 21485d0b3a5SRichard Henderson }; 21585d0b3a5SRichard Henderson 21685d0b3a5SRichard Henderson /* 21785d0b3a5SRichard Henderson * Similarly, except do the actual CMOS access on the boot cpu only. 21885d0b3a5SRichard Henderson * This requires marshalling the data across an interprocessor call. 21985d0b3a5SRichard Henderson */ 22085d0b3a5SRichard Henderson 22185d0b3a5SRichard Henderson #if defined(CONFIG_SMP) && \ 22285d0b3a5SRichard Henderson (defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_MARVEL)) 22385d0b3a5SRichard Henderson # define HAVE_REMOTE_RTC 1 22485d0b3a5SRichard Henderson 22585d0b3a5SRichard Henderson union remote_data { 22685d0b3a5SRichard Henderson struct rtc_time *tm; 22785d0b3a5SRichard Henderson unsigned long now; 22885d0b3a5SRichard Henderson long retval; 22985d0b3a5SRichard Henderson }; 23085d0b3a5SRichard Henderson 23185d0b3a5SRichard Henderson static void 23285d0b3a5SRichard Henderson do_remote_read(void *data) 23385d0b3a5SRichard Henderson { 23485d0b3a5SRichard Henderson union remote_data *x = data; 23585d0b3a5SRichard Henderson x->retval = alpha_rtc_read_time(NULL, x->tm); 23685d0b3a5SRichard Henderson } 23785d0b3a5SRichard Henderson 23885d0b3a5SRichard Henderson static int 23985d0b3a5SRichard Henderson remote_read_time(struct device *dev, struct rtc_time *tm) 24085d0b3a5SRichard Henderson { 24185d0b3a5SRichard Henderson union remote_data x; 24285d0b3a5SRichard Henderson if (smp_processor_id() != boot_cpuid) { 24385d0b3a5SRichard Henderson x.tm = tm; 24485d0b3a5SRichard Henderson smp_call_function_single(boot_cpuid, do_remote_read, &x, 1); 24585d0b3a5SRichard Henderson return x.retval; 24685d0b3a5SRichard Henderson } 24785d0b3a5SRichard Henderson return alpha_rtc_read_time(NULL, tm); 24885d0b3a5SRichard Henderson } 24985d0b3a5SRichard Henderson 25085d0b3a5SRichard Henderson static void 25185d0b3a5SRichard Henderson do_remote_set(void *data) 25285d0b3a5SRichard Henderson { 25385d0b3a5SRichard Henderson union remote_data *x = data; 25485d0b3a5SRichard Henderson x->retval = alpha_rtc_set_time(NULL, x->tm); 25585d0b3a5SRichard Henderson } 25685d0b3a5SRichard Henderson 25785d0b3a5SRichard Henderson static int 25885d0b3a5SRichard Henderson remote_set_time(struct device *dev, struct rtc_time *tm) 25985d0b3a5SRichard Henderson { 26085d0b3a5SRichard Henderson union remote_data x; 26185d0b3a5SRichard Henderson if (smp_processor_id() != boot_cpuid) { 26285d0b3a5SRichard Henderson x.tm = tm; 26385d0b3a5SRichard Henderson smp_call_function_single(boot_cpuid, do_remote_set, &x, 1); 26485d0b3a5SRichard Henderson return x.retval; 26585d0b3a5SRichard Henderson } 26685d0b3a5SRichard Henderson return alpha_rtc_set_time(NULL, tm); 26785d0b3a5SRichard Henderson } 26885d0b3a5SRichard Henderson 26985d0b3a5SRichard Henderson static void 27085d0b3a5SRichard Henderson do_remote_mmss(void *data) 27185d0b3a5SRichard Henderson { 27285d0b3a5SRichard Henderson union remote_data *x = data; 27385d0b3a5SRichard Henderson x->retval = alpha_rtc_set_mmss(NULL, x->now); 27485d0b3a5SRichard Henderson } 27585d0b3a5SRichard Henderson 27685d0b3a5SRichard Henderson static int 277a5312f56SXunlei Pang remote_set_mmss(struct device *dev, time64_t now) 27885d0b3a5SRichard Henderson { 27985d0b3a5SRichard Henderson union remote_data x; 28085d0b3a5SRichard Henderson if (smp_processor_id() != boot_cpuid) { 28185d0b3a5SRichard Henderson x.now = now; 28285d0b3a5SRichard Henderson smp_call_function_single(boot_cpuid, do_remote_mmss, &x, 1); 28385d0b3a5SRichard Henderson return x.retval; 28485d0b3a5SRichard Henderson } 28585d0b3a5SRichard Henderson return alpha_rtc_set_mmss(NULL, now); 28685d0b3a5SRichard Henderson } 28785d0b3a5SRichard Henderson 28885d0b3a5SRichard Henderson static const struct rtc_class_ops remote_rtc_ops = { 28985d0b3a5SRichard Henderson .read_time = remote_read_time, 29085d0b3a5SRichard Henderson .set_time = remote_set_time, 291a5312f56SXunlei Pang .set_mmss64 = remote_set_mmss, 29285d0b3a5SRichard Henderson .ioctl = alpha_rtc_ioctl, 29385d0b3a5SRichard Henderson }; 29485d0b3a5SRichard Henderson #endif 29585d0b3a5SRichard Henderson 29685d0b3a5SRichard Henderson static int __init 29785d0b3a5SRichard Henderson alpha_rtc_init(void) 29885d0b3a5SRichard Henderson { 29985d0b3a5SRichard Henderson const struct rtc_class_ops *ops; 30085d0b3a5SRichard Henderson struct platform_device *pdev; 30185d0b3a5SRichard Henderson struct rtc_device *rtc; 30285d0b3a5SRichard Henderson const char *name; 30385d0b3a5SRichard Henderson 30485d0b3a5SRichard Henderson init_rtc_epoch(); 30585d0b3a5SRichard Henderson name = "rtc-alpha"; 30685d0b3a5SRichard Henderson ops = &alpha_rtc_ops; 30785d0b3a5SRichard Henderson 30885d0b3a5SRichard Henderson #ifdef HAVE_REMOTE_RTC 30985d0b3a5SRichard Henderson if (alpha_mv.rtc_boot_cpu_only) 31085d0b3a5SRichard Henderson ops = &remote_rtc_ops; 31185d0b3a5SRichard Henderson #endif 31285d0b3a5SRichard Henderson 31385d0b3a5SRichard Henderson pdev = platform_device_register_simple(name, -1, NULL, 0); 31485d0b3a5SRichard Henderson rtc = devm_rtc_device_register(&pdev->dev, name, ops, THIS_MODULE); 31585d0b3a5SRichard Henderson if (IS_ERR(rtc)) 31685d0b3a5SRichard Henderson return PTR_ERR(rtc); 31785d0b3a5SRichard Henderson 31885d0b3a5SRichard Henderson platform_set_drvdata(pdev, rtc); 31985d0b3a5SRichard Henderson return 0; 32085d0b3a5SRichard Henderson } 32185d0b3a5SRichard Henderson device_initcall(alpha_rtc_init); 322