xref: /linux/drivers/rtc/rtc-sun4v.c (revision cdd38c5f1ce4398ec58fec95904b75824daab7b5)
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