Commit | Line | Data |
---|---|---|
7a138ede DM |
1 | /* rtc-sun4c.c: Hypervisor based RTC for SUN4V systems. |
2 | * | |
3 | * Copyright (C) 2008 David S. Miller <davem@davemloft.net> | |
4 | */ | |
5 | ||
6 | #include <linux/kernel.h> | |
7 | #include <linux/module.h> | |
8 | #include <linux/delay.h> | |
9 | #include <linux/init.h> | |
10 | #include <linux/time.h> | |
11 | #include <linux/rtc.h> | |
12 | #include <linux/platform_device.h> | |
13 | ||
14 | #include <asm/hypervisor.h> | |
15 | ||
16 | MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); | |
17 | MODULE_DESCRIPTION("SUN4V RTC driver"); | |
18 | MODULE_LICENSE("GPL"); | |
19 | ||
20 | struct sun4v_rtc { | |
21 | struct rtc_device *rtc; | |
22 | spinlock_t lock; | |
23 | }; | |
24 | ||
25 | static unsigned long hypervisor_get_time(void) | |
26 | { | |
27 | unsigned long ret, time; | |
28 | int retries = 10000; | |
29 | ||
30 | retry: | |
31 | ret = sun4v_tod_get(&time); | |
32 | if (ret == HV_EOK) | |
33 | return time; | |
34 | if (ret == HV_EWOULDBLOCK) { | |
35 | if (--retries > 0) { | |
36 | udelay(100); | |
37 | goto retry; | |
38 | } | |
39 | printk(KERN_WARNING "SUN4V: tod_get() timed out.\n"); | |
40 | return 0; | |
41 | } | |
42 | printk(KERN_WARNING "SUN4V: tod_get() not supported.\n"); | |
43 | return 0; | |
44 | } | |
45 | ||
46 | static int sun4v_read_time(struct device *dev, struct rtc_time *tm) | |
47 | { | |
48 | struct sun4v_rtc *p = dev_get_drvdata(dev); | |
49 | unsigned long flags, secs; | |
50 | ||
51 | spin_lock_irqsave(&p->lock, flags); | |
52 | secs = hypervisor_get_time(); | |
53 | spin_unlock_irqrestore(&p->lock, flags); | |
54 | ||
55 | rtc_time_to_tm(secs, tm); | |
56 | ||
57 | return 0; | |
58 | } | |
59 | ||
60 | static int hypervisor_set_time(unsigned long secs) | |
61 | { | |
62 | unsigned long ret; | |
63 | int retries = 10000; | |
64 | ||
65 | retry: | |
66 | ret = sun4v_tod_set(secs); | |
67 | if (ret == HV_EOK) | |
68 | return 0; | |
69 | if (ret == HV_EWOULDBLOCK) { | |
70 | if (--retries > 0) { | |
71 | udelay(100); | |
72 | goto retry; | |
73 | } | |
74 | printk(KERN_WARNING "SUN4V: tod_set() timed out.\n"); | |
75 | return -EAGAIN; | |
76 | } | |
77 | printk(KERN_WARNING "SUN4V: tod_set() not supported.\n"); | |
78 | return -EOPNOTSUPP; | |
79 | } | |
80 | ||
81 | static int sun4v_set_time(struct device *dev, struct rtc_time *tm) | |
82 | { | |
83 | struct sun4v_rtc *p = dev_get_drvdata(dev); | |
84 | unsigned long flags, secs; | |
85 | int err; | |
86 | ||
87 | err = rtc_tm_to_time(tm, &secs); | |
88 | if (err) | |
89 | return err; | |
90 | ||
91 | spin_lock_irqsave(&p->lock, flags); | |
92 | err = hypervisor_set_time(secs); | |
93 | spin_unlock_irqrestore(&p->lock, flags); | |
94 | ||
95 | return err; | |
96 | } | |
97 | ||
98 | static const struct rtc_class_ops sun4v_rtc_ops = { | |
99 | .read_time = sun4v_read_time, | |
100 | .set_time = sun4v_set_time, | |
101 | }; | |
102 | ||
103 | static int __devinit sun4v_rtc_probe(struct platform_device *pdev) | |
104 | { | |
105 | struct sun4v_rtc *p = kzalloc(sizeof(*p), GFP_KERNEL); | |
106 | ||
107 | if (!p) | |
108 | return -ENOMEM; | |
109 | ||
110 | spin_lock_init(&p->lock); | |
111 | ||
112 | p->rtc = rtc_device_register("sun4v", &pdev->dev, | |
113 | &sun4v_rtc_ops, THIS_MODULE); | |
114 | if (IS_ERR(p->rtc)) { | |
115 | int err = PTR_ERR(p->rtc); | |
116 | kfree(p); | |
117 | return err; | |
118 | } | |
119 | platform_set_drvdata(pdev, p); | |
120 | return 0; | |
121 | } | |
122 | ||
123 | static int __devexit sun4v_rtc_remove(struct platform_device *pdev) | |
124 | { | |
125 | struct sun4v_rtc *p = platform_get_drvdata(pdev); | |
126 | ||
127 | rtc_device_unregister(p->rtc); | |
128 | kfree(p); | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
133 | static struct platform_driver sun4v_rtc_driver = { | |
134 | .driver = { | |
135 | .name = "rtc-sun4v", | |
136 | .owner = THIS_MODULE, | |
137 | }, | |
138 | .probe = sun4v_rtc_probe, | |
139 | .remove = __devexit_p(sun4v_rtc_remove), | |
140 | }; | |
141 | ||
142 | static int __init sun4v_rtc_init(void) | |
143 | { | |
144 | return platform_driver_register(&sun4v_rtc_driver); | |
145 | } | |
146 | ||
147 | static void __exit sun4v_rtc_exit(void) | |
148 | { | |
149 | platform_driver_unregister(&sun4v_rtc_driver); | |
150 | } | |
151 | ||
152 | module_init(sun4v_rtc_init); | |
153 | module_exit(sun4v_rtc_exit); |