18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* rtc-sun4v.c: Hypervisor based RTC for SUN4V systems.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Author: David S. Miller
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/rtc.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <asm/hypervisor.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic unsigned long hypervisor_get_time(void)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	unsigned long ret, time;
228c2ecf20Sopenharmony_ci	int retries = 10000;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ciretry:
258c2ecf20Sopenharmony_ci	ret = sun4v_tod_get(&time);
268c2ecf20Sopenharmony_ci	if (ret == HV_EOK)
278c2ecf20Sopenharmony_ci		return time;
288c2ecf20Sopenharmony_ci	if (ret == HV_EWOULDBLOCK) {
298c2ecf20Sopenharmony_ci		if (--retries > 0) {
308c2ecf20Sopenharmony_ci			udelay(100);
318c2ecf20Sopenharmony_ci			goto retry;
328c2ecf20Sopenharmony_ci		}
338c2ecf20Sopenharmony_ci		pr_warn("tod_get() timed out.\n");
348c2ecf20Sopenharmony_ci		return 0;
358c2ecf20Sopenharmony_ci	}
368c2ecf20Sopenharmony_ci	pr_warn("tod_get() not supported.\n");
378c2ecf20Sopenharmony_ci	return 0;
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int sun4v_read_time(struct device *dev, struct rtc_time *tm)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	rtc_time64_to_tm(hypervisor_get_time(), tm);
438c2ecf20Sopenharmony_ci	return 0;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int hypervisor_set_time(unsigned long secs)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	unsigned long ret;
498c2ecf20Sopenharmony_ci	int retries = 10000;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ciretry:
528c2ecf20Sopenharmony_ci	ret = sun4v_tod_set(secs);
538c2ecf20Sopenharmony_ci	if (ret == HV_EOK)
548c2ecf20Sopenharmony_ci		return 0;
558c2ecf20Sopenharmony_ci	if (ret == HV_EWOULDBLOCK) {
568c2ecf20Sopenharmony_ci		if (--retries > 0) {
578c2ecf20Sopenharmony_ci			udelay(100);
588c2ecf20Sopenharmony_ci			goto retry;
598c2ecf20Sopenharmony_ci		}
608c2ecf20Sopenharmony_ci		pr_warn("tod_set() timed out.\n");
618c2ecf20Sopenharmony_ci		return -EAGAIN;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci	pr_warn("tod_set() not supported.\n");
648c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int sun4v_set_time(struct device *dev, struct rtc_time *tm)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	return hypervisor_set_time(rtc_tm_to_time64(tm));
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic const struct rtc_class_ops sun4v_rtc_ops = {
738c2ecf20Sopenharmony_ci	.read_time	= sun4v_read_time,
748c2ecf20Sopenharmony_ci	.set_time	= sun4v_set_time,
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int __init sun4v_rtc_probe(struct platform_device *pdev)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct rtc_device *rtc;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	rtc = devm_rtc_allocate_device(&pdev->dev);
828c2ecf20Sopenharmony_ci	if (IS_ERR(rtc))
838c2ecf20Sopenharmony_ci		return PTR_ERR(rtc);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	rtc->ops = &sun4v_rtc_ops;
868c2ecf20Sopenharmony_ci	rtc->range_max = U64_MAX;
878c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, rtc);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return rtc_register_device(rtc);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic struct platform_driver sun4v_rtc_driver = {
938c2ecf20Sopenharmony_ci	.driver		= {
948c2ecf20Sopenharmony_ci		.name	= "rtc-sun4v",
958c2ecf20Sopenharmony_ci	},
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cibuiltin_platform_driver_probe(sun4v_rtc_driver, sun4v_rtc_probe);
99