Commit | Line | Data |
---|---|---|
64bef02c | 1 | // SPDX-License-Identifier: GPL-2.0 |
cecf61bd | 2 | /* rtc-sun4v.c: Hypervisor based RTC for SUN4V systems. |
8b610253 PG |
3 | * |
4 | * Author: David S. Miller | |
7a138ede DM |
5 | * |
6 | * Copyright (C) 2008 David S. Miller <davem@davemloft.net> | |
7 | */ | |
8 | ||
d959f731 JH |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | ||
7a138ede | 11 | #include <linux/kernel.h> |
7a138ede DM |
12 | #include <linux/delay.h> |
13 | #include <linux/init.h> | |
7a138ede DM |
14 | #include <linux/rtc.h> |
15 | #include <linux/platform_device.h> | |
16 | ||
17 | #include <asm/hypervisor.h> | |
18 | ||
7a138ede DM |
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 | } | |
d959f731 | 33 | pr_warn("tod_get() timed out.\n"); |
7a138ede DM |
34 | return 0; |
35 | } | |
d959f731 | 36 | pr_warn("tod_get() not supported.\n"); |
7a138ede DM |
37 | return 0; |
38 | } | |
39 | ||
40 | static int sun4v_read_time(struct device *dev, struct rtc_time *tm) | |
41 | { | |
65c6f638 | 42 | rtc_time64_to_tm(hypervisor_get_time(), tm); |
7a138ede DM |
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 | } | |
d959f731 | 60 | pr_warn("tod_set() timed out.\n"); |
7a138ede DM |
61 | return -EAGAIN; |
62 | } | |
d959f731 | 63 | pr_warn("tod_set() not supported.\n"); |
7a138ede DM |
64 | return -EOPNOTSUPP; |
65 | } | |
66 | ||
67 | static int sun4v_set_time(struct device *dev, struct rtc_time *tm) | |
68 | { | |
65c6f638 | 69 | return hypervisor_set_time(rtc_tm_to_time64(tm)); |
7a138ede DM |
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 | ||
cecf61bd | 77 | static int __init sun4v_rtc_probe(struct platform_device *pdev) |
7a138ede | 78 | { |
cc40d642 JH |
79 | struct rtc_device *rtc; |
80 | ||
3ec99d61 | 81 | rtc = devm_rtc_allocate_device(&pdev->dev); |
cecf61bd AZ |
82 | if (IS_ERR(rtc)) |
83 | return PTR_ERR(rtc); | |
84 | ||
3ec99d61 AB |
85 | rtc->ops = &sun4v_rtc_ops; |
86 | rtc->range_max = U64_MAX; | |
cecf61bd | 87 | platform_set_drvdata(pdev, rtc); |
3ec99d61 AB |
88 | |
89 | return rtc_register_device(rtc); | |
7a138ede DM |
90 | } |
91 | ||
7a138ede DM |
92 | static struct platform_driver sun4v_rtc_driver = { |
93 | .driver = { | |
94 | .name = "rtc-sun4v", | |
7a138ede | 95 | }, |
7a138ede DM |
96 | }; |
97 | ||
8b610253 | 98 | builtin_platform_driver_probe(sun4v_rtc_driver, sun4v_rtc_probe); |