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