xref: /openbmc/linux/arch/powerpc/kernel/rtas-rtc.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
23136254cSBenjamin Herrenschmidt #include <linux/kernel.h>
33136254cSBenjamin Herrenschmidt #include <linux/time.h>
43136254cSBenjamin Herrenschmidt #include <linux/timer.h>
53136254cSBenjamin Herrenschmidt #include <linux/init.h>
63136254cSBenjamin Herrenschmidt #include <linux/rtc.h>
73136254cSBenjamin Herrenschmidt #include <linux/delay.h>
89a8f99faSChristian Dietrich #include <linux/ratelimit.h>
93136254cSBenjamin Herrenschmidt #include <asm/rtas.h>
103136254cSBenjamin Herrenschmidt #include <asm/time.h>
113136254cSBenjamin Herrenschmidt 
123136254cSBenjamin Herrenschmidt 
133136254cSBenjamin Herrenschmidt #define MAX_RTC_WAIT 5000	/* 5 sec */
144bfa5ddfSNathan Lynch 
rtas_get_boot_time(void)155bfd6435SArnd Bergmann time64_t __init rtas_get_boot_time(void)
163136254cSBenjamin Herrenschmidt {
173136254cSBenjamin Herrenschmidt 	int ret[8];
18507279dbSJohn Rose 	int error;
19507279dbSJohn Rose 	unsigned int wait_time;
2049e16b7bSPaul Mackerras 	u64 max_wait_tb;
213136254cSBenjamin Herrenschmidt 
223136254cSBenjamin Herrenschmidt 	max_wait_tb = get_tb() + tb_ticks_per_usec * 1000 * MAX_RTC_WAIT;
233136254cSBenjamin Herrenschmidt 	do {
24*08273c9fSNathan Lynch 		error = rtas_call(rtas_function_token(RTAS_FN_GET_TIME_OF_DAY), 0, 8, ret);
25507279dbSJohn Rose 
26507279dbSJohn Rose 		wait_time = rtas_busy_delay_time(error);
27507279dbSJohn Rose 		if (wait_time) {
283136254cSBenjamin Herrenschmidt 			/* This is boot time so we spin. */
293136254cSBenjamin Herrenschmidt 			udelay(wait_time*1000);
303136254cSBenjamin Herrenschmidt 		}
31507279dbSJohn Rose 	} while (wait_time && (get_tb() < max_wait_tb));
323136254cSBenjamin Herrenschmidt 
339a8f99faSChristian Dietrich 	if (error != 0) {
349a8f99faSChristian Dietrich 		printk_ratelimited(KERN_WARNING
359a8f99faSChristian Dietrich 				   "error: reading the clock failed (%d)\n",
363136254cSBenjamin Herrenschmidt 				   error);
373136254cSBenjamin Herrenschmidt 		return 0;
383136254cSBenjamin Herrenschmidt 	}
393136254cSBenjamin Herrenschmidt 
405bfd6435SArnd Bergmann 	return mktime64(ret[0], ret[1], ret[2], ret[3], ret[4], ret[5]);
413136254cSBenjamin Herrenschmidt }
423136254cSBenjamin Herrenschmidt 
433136254cSBenjamin Herrenschmidt /* NOTE: get_rtc_time will get an error if executed in interrupt context
443136254cSBenjamin Herrenschmidt  * and if a delay is needed to read the clock.  In this case we just
453136254cSBenjamin Herrenschmidt  * silently return without updating rtc_tm.
463136254cSBenjamin Herrenschmidt  */
rtas_get_rtc_time(struct rtc_time * rtc_tm)473136254cSBenjamin Herrenschmidt void rtas_get_rtc_time(struct rtc_time *rtc_tm)
483136254cSBenjamin Herrenschmidt {
493136254cSBenjamin Herrenschmidt         int ret[8];
50507279dbSJohn Rose 	int error;
51507279dbSJohn Rose 	unsigned int wait_time;
5249e16b7bSPaul Mackerras 	u64 max_wait_tb;
533136254cSBenjamin Herrenschmidt 
543136254cSBenjamin Herrenschmidt 	max_wait_tb = get_tb() + tb_ticks_per_usec * 1000 * MAX_RTC_WAIT;
553136254cSBenjamin Herrenschmidt 	do {
56*08273c9fSNathan Lynch 		error = rtas_call(rtas_function_token(RTAS_FN_GET_TIME_OF_DAY), 0, 8, ret);
57507279dbSJohn Rose 
58507279dbSJohn Rose 		wait_time = rtas_busy_delay_time(error);
59507279dbSJohn Rose 		if (wait_time) {
609a8f99faSChristian Dietrich 			if (in_interrupt()) {
610e8ed479SMichael Neuling 				memset(rtc_tm, 0, sizeof(struct rtc_time));
629a8f99faSChristian Dietrich 				printk_ratelimited(KERN_WARNING
639a8f99faSChristian Dietrich 						   "error: reading clock "
643136254cSBenjamin Herrenschmidt 						   "would delay interrupt\n");
653136254cSBenjamin Herrenschmidt 				return;	/* delay not allowed */
663136254cSBenjamin Herrenschmidt 			}
673136254cSBenjamin Herrenschmidt 			msleep(wait_time);
683136254cSBenjamin Herrenschmidt 		}
69507279dbSJohn Rose 	} while (wait_time && (get_tb() < max_wait_tb));
703136254cSBenjamin Herrenschmidt 
719a8f99faSChristian Dietrich 	if (error != 0) {
729a8f99faSChristian Dietrich 		printk_ratelimited(KERN_WARNING
739a8f99faSChristian Dietrich 				   "error: reading the clock failed (%d)\n",
743136254cSBenjamin Herrenschmidt 				   error);
753136254cSBenjamin Herrenschmidt 		return;
763136254cSBenjamin Herrenschmidt         }
773136254cSBenjamin Herrenschmidt 
783136254cSBenjamin Herrenschmidt 	rtc_tm->tm_sec = ret[5];
793136254cSBenjamin Herrenschmidt 	rtc_tm->tm_min = ret[4];
803136254cSBenjamin Herrenschmidt 	rtc_tm->tm_hour = ret[3];
813136254cSBenjamin Herrenschmidt 	rtc_tm->tm_mday = ret[2];
823136254cSBenjamin Herrenschmidt 	rtc_tm->tm_mon = ret[1] - 1;
833136254cSBenjamin Herrenschmidt 	rtc_tm->tm_year = ret[0] - 1900;
843136254cSBenjamin Herrenschmidt }
853136254cSBenjamin Herrenschmidt 
rtas_set_rtc_time(struct rtc_time * tm)863136254cSBenjamin Herrenschmidt int rtas_set_rtc_time(struct rtc_time *tm)
873136254cSBenjamin Herrenschmidt {
883136254cSBenjamin Herrenschmidt 	int error, wait_time;
8949e16b7bSPaul Mackerras 	u64 max_wait_tb;
903136254cSBenjamin Herrenschmidt 
913136254cSBenjamin Herrenschmidt 	max_wait_tb = get_tb() + tb_ticks_per_usec * 1000 * MAX_RTC_WAIT;
923136254cSBenjamin Herrenschmidt 	do {
93*08273c9fSNathan Lynch 		error = rtas_call(rtas_function_token(RTAS_FN_SET_TIME_OF_DAY), 7, 1, NULL,
943136254cSBenjamin Herrenschmidt 				  tm->tm_year + 1900, tm->tm_mon + 1,
953136254cSBenjamin Herrenschmidt 				  tm->tm_mday, tm->tm_hour, tm->tm_min,
963136254cSBenjamin Herrenschmidt 				  tm->tm_sec, 0);
97507279dbSJohn Rose 
98507279dbSJohn Rose 		wait_time = rtas_busy_delay_time(error);
99507279dbSJohn Rose 		if (wait_time) {
1003136254cSBenjamin Herrenschmidt 			if (in_interrupt())
1013136254cSBenjamin Herrenschmidt 				return 1;	/* probably decrementer */
1023136254cSBenjamin Herrenschmidt 			msleep(wait_time);
1033136254cSBenjamin Herrenschmidt 		}
104507279dbSJohn Rose 	} while (wait_time && (get_tb() < max_wait_tb));
1053136254cSBenjamin Herrenschmidt 
1069a8f99faSChristian Dietrich 	if (error != 0)
1079a8f99faSChristian Dietrich 		printk_ratelimited(KERN_WARNING
1089a8f99faSChristian Dietrich 				   "error: setting the clock failed (%d)\n",
1093136254cSBenjamin Herrenschmidt 				   error);
1103136254cSBenjamin Herrenschmidt 
1113136254cSBenjamin Herrenschmidt         return 0;
1123136254cSBenjamin Herrenschmidt }
113