164bef02cSAlexandre Belloni // SPDX-License-Identifier: GPL-2.0
2cecf61bdSAlessandro Zummo /* rtc-sun4v.c: Hypervisor based RTC for SUN4V systems.
37a138edeSDavid S. Miller *
48b610253SPaul Gortmaker * Author: David S. Miller
58b610253SPaul Gortmaker *
67a138edeSDavid S. Miller * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
77a138edeSDavid S. Miller */
87a138edeSDavid S. Miller
9d959f731SJingoo Han #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10d959f731SJingoo Han
117a138edeSDavid S. Miller #include <linux/kernel.h>
127a138edeSDavid S. Miller #include <linux/delay.h>
137a138edeSDavid S. Miller #include <linux/init.h>
147a138edeSDavid S. Miller #include <linux/rtc.h>
157a138edeSDavid S. Miller #include <linux/platform_device.h>
167a138edeSDavid S. Miller
177a138edeSDavid S. Miller #include <asm/hypervisor.h>
187a138edeSDavid S. Miller
hypervisor_get_time(void)197a138edeSDavid S. Miller static unsigned long hypervisor_get_time(void)
207a138edeSDavid S. Miller {
217a138edeSDavid S. Miller unsigned long ret, time;
227a138edeSDavid S. Miller int retries = 10000;
237a138edeSDavid S. Miller
247a138edeSDavid S. Miller retry:
257a138edeSDavid S. Miller ret = sun4v_tod_get(&time);
267a138edeSDavid S. Miller if (ret == HV_EOK)
277a138edeSDavid S. Miller return time;
287a138edeSDavid S. Miller if (ret == HV_EWOULDBLOCK) {
297a138edeSDavid S. Miller if (--retries > 0) {
307a138edeSDavid S. Miller udelay(100);
317a138edeSDavid S. Miller goto retry;
327a138edeSDavid S. Miller }
33d959f731SJingoo Han pr_warn("tod_get() timed out.\n");
347a138edeSDavid S. Miller return 0;
357a138edeSDavid S. Miller }
36d959f731SJingoo Han pr_warn("tod_get() not supported.\n");
377a138edeSDavid S. Miller return 0;
387a138edeSDavid S. Miller }
397a138edeSDavid S. Miller
sun4v_read_time(struct device * dev,struct rtc_time * tm)407a138edeSDavid S. Miller static int sun4v_read_time(struct device *dev, struct rtc_time *tm)
417a138edeSDavid S. Miller {
4265c6f638SAlexandre Belloni rtc_time64_to_tm(hypervisor_get_time(), tm);
437a138edeSDavid S. Miller return 0;
447a138edeSDavid S. Miller }
457a138edeSDavid S. Miller
hypervisor_set_time(unsigned long secs)467a138edeSDavid S. Miller static int hypervisor_set_time(unsigned long secs)
477a138edeSDavid S. Miller {
487a138edeSDavid S. Miller unsigned long ret;
497a138edeSDavid S. Miller int retries = 10000;
507a138edeSDavid S. Miller
517a138edeSDavid S. Miller retry:
527a138edeSDavid S. Miller ret = sun4v_tod_set(secs);
537a138edeSDavid S. Miller if (ret == HV_EOK)
547a138edeSDavid S. Miller return 0;
557a138edeSDavid S. Miller if (ret == HV_EWOULDBLOCK) {
567a138edeSDavid S. Miller if (--retries > 0) {
577a138edeSDavid S. Miller udelay(100);
587a138edeSDavid S. Miller goto retry;
597a138edeSDavid S. Miller }
60d959f731SJingoo Han pr_warn("tod_set() timed out.\n");
617a138edeSDavid S. Miller return -EAGAIN;
627a138edeSDavid S. Miller }
63d959f731SJingoo Han pr_warn("tod_set() not supported.\n");
647a138edeSDavid S. Miller return -EOPNOTSUPP;
657a138edeSDavid S. Miller }
667a138edeSDavid S. Miller
sun4v_set_time(struct device * dev,struct rtc_time * tm)677a138edeSDavid S. Miller static int sun4v_set_time(struct device *dev, struct rtc_time *tm)
687a138edeSDavid S. Miller {
6965c6f638SAlexandre Belloni return hypervisor_set_time(rtc_tm_to_time64(tm));
707a138edeSDavid S. Miller }
717a138edeSDavid S. Miller
727a138edeSDavid S. Miller static const struct rtc_class_ops sun4v_rtc_ops = {
737a138edeSDavid S. Miller .read_time = sun4v_read_time,
747a138edeSDavid S. Miller .set_time = sun4v_set_time,
757a138edeSDavid S. Miller };
767a138edeSDavid S. Miller
sun4v_rtc_probe(struct platform_device * pdev)77cecf61bdSAlessandro Zummo static int __init sun4v_rtc_probe(struct platform_device *pdev)
787a138edeSDavid S. Miller {
79cc40d642SJingoo Han struct rtc_device *rtc;
80cc40d642SJingoo Han
813ec99d61SAlexandre Belloni rtc = devm_rtc_allocate_device(&pdev->dev);
82cecf61bdSAlessandro Zummo if (IS_ERR(rtc))
83cecf61bdSAlessandro Zummo return PTR_ERR(rtc);
84cecf61bdSAlessandro Zummo
853ec99d61SAlexandre Belloni rtc->ops = &sun4v_rtc_ops;
863ec99d61SAlexandre Belloni rtc->range_max = U64_MAX;
87cecf61bdSAlessandro Zummo platform_set_drvdata(pdev, rtc);
883ec99d61SAlexandre Belloni
89*fdcfd854SBartosz Golaszewski return devm_rtc_register_device(rtc);
907a138edeSDavid S. Miller }
917a138edeSDavid S. Miller
927a138edeSDavid S. Miller static struct platform_driver sun4v_rtc_driver = {
937a138edeSDavid S. Miller .driver = {
947a138edeSDavid S. Miller .name = "rtc-sun4v",
957a138edeSDavid S. Miller },
967a138edeSDavid S. Miller };
977a138edeSDavid S. Miller
988b610253SPaul Gortmaker builtin_platform_driver_probe(sun4v_rtc_driver, sun4v_rtc_probe);
99