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