Commit | Line | Data |
---|---|---|
a95579cd AZ |
1 | /* |
2 | * An RTC test device/driver | |
3 | * Copyright (C) 2005 Tower Technologies | |
4 | * Author: Alessandro Zummo <a.zummo@towertech.it> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include <linux/module.h> | |
12 | #include <linux/err.h> | |
13 | #include <linux/rtc.h> | |
14 | #include <linux/platform_device.h> | |
15 | ||
5b257571 AB |
16 | #define MAX_RTC_TEST 3 |
17 | ||
4dc2403b AB |
18 | struct rtc_test_data { |
19 | struct rtc_device *rtc; | |
20 | time64_t offset; | |
8be09029 AB |
21 | struct timer_list alarm; |
22 | bool alarm_en; | |
4dc2403b AB |
23 | }; |
24 | ||
5b257571 | 25 | struct platform_device *pdev[MAX_RTC_TEST]; |
a95579cd | 26 | |
8be09029 | 27 | static int test_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
a95579cd | 28 | { |
8be09029 AB |
29 | struct rtc_test_data *rtd = dev_get_drvdata(dev); |
30 | time64_t alarm; | |
31 | ||
32 | alarm = (rtd->alarm.expires - jiffies) / HZ; | |
33 | alarm += ktime_get_real_seconds() + rtd->offset; | |
34 | ||
35 | rtc_time64_to_tm(alarm, &alrm->time); | |
36 | alrm->enabled = rtd->alarm_en; | |
37 | ||
a95579cd AZ |
38 | return 0; |
39 | } | |
40 | ||
8be09029 | 41 | static int test_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
a95579cd | 42 | { |
8be09029 AB |
43 | struct rtc_test_data *rtd = dev_get_drvdata(dev); |
44 | ktime_t timeout; | |
45 | u64 expires; | |
46 | ||
47 | timeout = rtc_tm_to_time64(&alrm->time) - ktime_get_real_seconds(); | |
48 | timeout -= rtd->offset; | |
49 | ||
50 | del_timer(&rtd->alarm); | |
51 | ||
52 | expires = jiffies + timeout * HZ; | |
53 | if (expires > U32_MAX) | |
54 | expires = U32_MAX; | |
55 | ||
56 | pr_err("ABE: %s +%d %s\n", __FILE__, __LINE__, __func__); | |
57 | rtd->alarm.expires = expires; | |
58 | ||
59 | if (alrm->enabled) | |
60 | add_timer(&rtd->alarm); | |
61 | ||
62 | rtd->alarm_en = alrm->enabled; | |
63 | ||
a95579cd AZ |
64 | return 0; |
65 | } | |
66 | ||
4dc2403b | 67 | static int test_rtc_read_time(struct device *dev, struct rtc_time *tm) |
a95579cd | 68 | { |
4dc2403b AB |
69 | struct rtc_test_data *rtd = dev_get_drvdata(dev); |
70 | ||
71 | rtc_time64_to_tm(ktime_get_real_seconds() + rtd->offset, tm); | |
72 | ||
4d644ab8 XP |
73 | return 0; |
74 | } | |
75 | ||
76 | static int test_rtc_set_mmss64(struct device *dev, time64_t secs) | |
77 | { | |
4dc2403b AB |
78 | struct rtc_test_data *rtd = dev_get_drvdata(dev); |
79 | ||
80 | rtd->offset = secs - ktime_get_real_seconds(); | |
81 | ||
a95579cd AZ |
82 | return 0; |
83 | } | |
84 | ||
16380c15 | 85 | static int test_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) |
a95579cd | 86 | { |
8be09029 AB |
87 | struct rtc_test_data *rtd = dev_get_drvdata(dev); |
88 | ||
89 | rtd->alarm_en = enable; | |
90 | if (enable) | |
91 | add_timer(&rtd->alarm); | |
92 | else | |
93 | del_timer(&rtd->alarm); | |
94 | ||
16380c15 | 95 | return 0; |
a95579cd AZ |
96 | } |
97 | ||
1928906d AB |
98 | static const struct rtc_class_ops test_rtc_ops_noalm = { |
99 | .read_time = test_rtc_read_time, | |
100 | .set_mmss64 = test_rtc_set_mmss64, | |
101 | .alarm_irq_enable = test_rtc_alarm_irq_enable, | |
102 | }; | |
103 | ||
78417682 | 104 | static const struct rtc_class_ops test_rtc_ops = { |
a95579cd | 105 | .read_time = test_rtc_read_time, |
a95579cd AZ |
106 | .read_alarm = test_rtc_read_alarm, |
107 | .set_alarm = test_rtc_set_alarm, | |
78417682 | 108 | .set_mmss64 = test_rtc_set_mmss64, |
16380c15 | 109 | .alarm_irq_enable = test_rtc_alarm_irq_enable, |
a95579cd AZ |
110 | }; |
111 | ||
8be09029 AB |
112 | static void test_rtc_alarm_handler(struct timer_list *t) |
113 | { | |
114 | struct rtc_test_data *rtd = from_timer(rtd, t, alarm); | |
115 | ||
116 | rtc_update_irq(rtd->rtc, 1, RTC_AF | RTC_IRQF); | |
117 | } | |
118 | ||
a95579cd AZ |
119 | static int test_probe(struct platform_device *plat_dev) |
120 | { | |
4dc2403b | 121 | struct rtc_test_data *rtd; |
dd8d8137 | 122 | |
4dc2403b AB |
123 | rtd = devm_kzalloc(&plat_dev->dev, sizeof(*rtd), GFP_KERNEL); |
124 | if (!rtd) | |
125 | return -ENOMEM; | |
91046a8a | 126 | |
4dc2403b | 127 | platform_set_drvdata(plat_dev, rtd); |
a95579cd | 128 | |
0b472ad2 | 129 | rtd->rtc = devm_rtc_allocate_device(&plat_dev->dev); |
4dc2403b AB |
130 | if (IS_ERR(rtd->rtc)) |
131 | return PTR_ERR(rtd->rtc); | |
a95579cd | 132 | |
1928906d AB |
133 | switch (plat_dev->id) { |
134 | case 0: | |
135 | rtd->rtc->ops = &test_rtc_ops_noalm; | |
136 | break; | |
137 | default: | |
138 | rtd->rtc->ops = &test_rtc_ops; | |
139 | } | |
0b472ad2 | 140 | |
8be09029 AB |
141 | timer_setup(&rtd->alarm, test_rtc_alarm_handler, 0); |
142 | rtd->alarm.expires = 0; | |
143 | ||
0b472ad2 | 144 | return rtc_register_device(rtd->rtc); |
a95579cd AZ |
145 | } |
146 | ||
c4646528 | 147 | static struct platform_driver test_driver = { |
a95579cd | 148 | .probe = test_probe, |
a95579cd AZ |
149 | .driver = { |
150 | .name = "rtc-test", | |
a95579cd AZ |
151 | }, |
152 | }; | |
153 | ||
154 | static int __init test_init(void) | |
155 | { | |
5b257571 | 156 | int i, err; |
a95579cd | 157 | |
c4646528 | 158 | if ((err = platform_driver_register(&test_driver))) |
a95579cd AZ |
159 | return err; |
160 | ||
5b257571 AB |
161 | err = -ENOMEM; |
162 | for (i = 0; i < MAX_RTC_TEST; i++) { | |
163 | pdev[i] = platform_device_alloc("rtc-test", i); | |
164 | if (!pdev[i]) | |
165 | goto exit_free_mem; | |
a95579cd AZ |
166 | } |
167 | ||
5b257571 AB |
168 | for (i = 0; i < MAX_RTC_TEST; i++) { |
169 | err = platform_device_add(pdev[i]); | |
170 | if (err) | |
171 | goto exit_device_del; | |
a95579cd AZ |
172 | } |
173 | ||
a95579cd AZ |
174 | return 0; |
175 | ||
5b257571 AB |
176 | exit_device_del: |
177 | for (; i > 0; i--) | |
178 | platform_device_del(pdev[i - 1]); | |
a95579cd | 179 | |
5b257571 AB |
180 | exit_free_mem: |
181 | for (i = 0; i < MAX_RTC_TEST; i++) | |
182 | platform_device_put(pdev[i]); | |
a95579cd | 183 | |
c4646528 | 184 | platform_driver_unregister(&test_driver); |
a95579cd AZ |
185 | return err; |
186 | } | |
187 | ||
188 | static void __exit test_exit(void) | |
189 | { | |
5b257571 AB |
190 | int i; |
191 | ||
192 | for (i = 0; i < MAX_RTC_TEST; i++) | |
193 | platform_device_unregister(pdev[i]); | |
194 | ||
c4646528 | 195 | platform_driver_unregister(&test_driver); |
a95579cd AZ |
196 | } |
197 | ||
198 | MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); | |
199 | MODULE_DESCRIPTION("RTC test driver/device"); | |
200 | MODULE_LICENSE("GPL"); | |
201 | ||
202 | module_init(test_init); | |
203 | module_exit(test_exit); |