1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* rtc-sun4v.c: Hypervisor based RTC for SUN4V systems. |
3 | * |
4 | * Author: David S. Miller |
5 | * |
6 | * Copyright (C) 2008 David S. Miller <davem@davemloft.net> |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | |
11 | #include <linux/kernel.h> |
12 | #include <linux/delay.h> |
13 | #include <linux/init.h> |
14 | #include <linux/rtc.h> |
15 | #include <linux/platform_device.h> |
16 | |
17 | #include <asm/hypervisor.h> |
18 | |
19 | static unsigned long hypervisor_get_time(void) |
20 | { |
21 | unsigned long ret, time; |
22 | int retries = 10000; |
23 | |
24 | retry: |
25 | ret = sun4v_tod_get(&time); |
26 | if (ret == HV_EOK) |
27 | return time; |
28 | if (ret == HV_EWOULDBLOCK) { |
29 | if (--retries > 0) { |
30 | udelay(100); |
31 | goto retry; |
32 | } |
33 | pr_warn("tod_get() timed out.\n" ); |
34 | return 0; |
35 | } |
36 | pr_warn("tod_get() not supported.\n" ); |
37 | return 0; |
38 | } |
39 | |
40 | static int sun4v_read_time(struct device *dev, struct rtc_time *tm) |
41 | { |
42 | rtc_time64_to_tm(time: hypervisor_get_time(), tm); |
43 | return 0; |
44 | } |
45 | |
46 | static int hypervisor_set_time(unsigned long secs) |
47 | { |
48 | unsigned long ret; |
49 | int retries = 10000; |
50 | |
51 | retry: |
52 | ret = sun4v_tod_set(secs); |
53 | if (ret == HV_EOK) |
54 | return 0; |
55 | if (ret == HV_EWOULDBLOCK) { |
56 | if (--retries > 0) { |
57 | udelay(100); |
58 | goto retry; |
59 | } |
60 | pr_warn("tod_set() timed out.\n" ); |
61 | return -EAGAIN; |
62 | } |
63 | pr_warn("tod_set() not supported.\n" ); |
64 | return -EOPNOTSUPP; |
65 | } |
66 | |
67 | static int sun4v_set_time(struct device *dev, struct rtc_time *tm) |
68 | { |
69 | return hypervisor_set_time(secs: rtc_tm_to_time64(tm)); |
70 | } |
71 | |
72 | static const struct rtc_class_ops sun4v_rtc_ops = { |
73 | .read_time = sun4v_read_time, |
74 | .set_time = sun4v_set_time, |
75 | }; |
76 | |
77 | static int __init sun4v_rtc_probe(struct platform_device *pdev) |
78 | { |
79 | struct rtc_device *rtc; |
80 | |
81 | rtc = devm_rtc_allocate_device(dev: &pdev->dev); |
82 | if (IS_ERR(ptr: rtc)) |
83 | return PTR_ERR(ptr: rtc); |
84 | |
85 | rtc->ops = &sun4v_rtc_ops; |
86 | rtc->range_max = U64_MAX; |
87 | platform_set_drvdata(pdev, data: rtc); |
88 | |
89 | return devm_rtc_register_device(rtc); |
90 | } |
91 | |
92 | static struct platform_driver sun4v_rtc_driver = { |
93 | .driver = { |
94 | .name = "rtc-sun4v" , |
95 | }, |
96 | }; |
97 | |
98 | builtin_platform_driver_probe(sun4v_rtc_driver, sun4v_rtc_probe); |
99 | |