xref: /openbmc/linux/arch/alpha/kernel/rtc.c (revision 5ab788d7383289bfc141ab357767bc6c11bbf77f)
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