aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig16
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-hid-sensor-time.c292
3 files changed, 309 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 5e44eaabf457..aa64d5d3fb20 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1183,4 +1183,20 @@ config RTC_DRV_SNVS
1183 This driver can also be built as a module, if so, the module 1183 This driver can also be built as a module, if so, the module
1184 will be called "rtc-snvs". 1184 will be called "rtc-snvs".
1185 1185
1186comment "HID Sensor RTC drivers"
1187
1188config RTC_DRV_HID_SENSOR_TIME
1189 tristate "HID Sensor Time"
1190 depends on USB_HID
1191 select IIO
1192 select HID_SENSOR_HUB
1193 select HID_SENSOR_IIO_COMMON
1194 help
1195 Say yes here to build support for the HID Sensors of type Time.
1196 This drivers makes such sensors available as RTCs.
1197
1198 If this driver is compiled as a module, it will be named
1199 rtc-hid-sensor-time.
1200
1201
1186endif # RTC_CLASS 1202endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index ec2988b00a44..60c0414d67fa 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o
53obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o 53obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
54obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o 54obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
55obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o 55obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
56obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
56obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o 57obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
57obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o 58obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
58obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o 59obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
diff --git a/drivers/rtc/rtc-hid-sensor-time.c b/drivers/rtc/rtc-hid-sensor-time.c
new file mode 100644
index 000000000000..31c5728ef629
--- /dev/null
+++ b/drivers/rtc/rtc-hid-sensor-time.c
@@ -0,0 +1,292 @@
1/*
2 * HID Sensor Time Driver
3 * Copyright (c) 2012, Alexander Holler.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 */
19#include <linux/device.h>
20#include <linux/platform_device.h>
21#include <linux/module.h>
22#include <linux/hid-sensor-hub.h>
23#include <linux/iio/iio.h>
24#include <linux/rtc.h>
25
26/* Format: HID-SENSOR-usage_id_in_hex */
27/* Usage ID from spec for Time: 0x2000A0 */
28#define DRIVER_NAME "HID-SENSOR-2000a0" /* must be lowercase */
29
30enum hid_time_channel {
31 CHANNEL_SCAN_INDEX_YEAR,
32 CHANNEL_SCAN_INDEX_MONTH,
33 CHANNEL_SCAN_INDEX_DAY,
34 CHANNEL_SCAN_INDEX_HOUR,
35 CHANNEL_SCAN_INDEX_MINUTE,
36 CHANNEL_SCAN_INDEX_SECOND,
37 TIME_RTC_CHANNEL_MAX,
38};
39
40struct hid_time_state {
41 struct hid_sensor_hub_callbacks callbacks;
42 struct hid_sensor_common common_attributes;
43 struct hid_sensor_hub_attribute_info info[TIME_RTC_CHANNEL_MAX];
44 struct rtc_time last_time;
45 spinlock_t lock_last_time;
46 struct completion comp_last_time;
47 struct rtc_time time_buf;
48 struct rtc_device *rtc;
49};
50
51static const u32 hid_time_addresses[TIME_RTC_CHANNEL_MAX] = {
52 HID_USAGE_SENSOR_TIME_YEAR,
53 HID_USAGE_SENSOR_TIME_MONTH,
54 HID_USAGE_SENSOR_TIME_DAY,
55 HID_USAGE_SENSOR_TIME_HOUR,
56 HID_USAGE_SENSOR_TIME_MINUTE,
57 HID_USAGE_SENSOR_TIME_SECOND,
58};
59
60/* Channel names for verbose error messages */
61static const char * const hid_time_channel_names[TIME_RTC_CHANNEL_MAX] = {
62 "year", "month", "day", "hour", "minute", "second",
63};
64
65/* Callback handler to send event after all samples are received and captured */
66static int hid_time_proc_event(struct hid_sensor_hub_device *hsdev,
67 unsigned usage_id, void *priv)
68{
69 unsigned long flags;
70 struct hid_time_state *time_state = platform_get_drvdata(priv);
71
72 spin_lock_irqsave(&time_state->lock_last_time, flags);
73 time_state->last_time = time_state->time_buf;
74 spin_unlock_irqrestore(&time_state->lock_last_time, flags);
75 complete(&time_state->comp_last_time);
76 return 0;
77}
78
79static int hid_time_capture_sample(struct hid_sensor_hub_device *hsdev,
80 unsigned usage_id, size_t raw_len,
81 char *raw_data, void *priv)
82{
83 struct hid_time_state *time_state = platform_get_drvdata(priv);
84 struct rtc_time *time_buf = &time_state->time_buf;
85
86 switch (usage_id) {
87 case HID_USAGE_SENSOR_TIME_YEAR:
88 time_buf->tm_year = *(u8 *)raw_data;
89 if (time_buf->tm_year < 70)
90 /* assume we are in 1970...2069 */
91 time_buf->tm_year += 100;
92 break;
93 case HID_USAGE_SENSOR_TIME_MONTH:
94 /* sensor sending the month as 1-12, we need 0-11 */
95 time_buf->tm_mon = *(u8 *)raw_data-1;
96 break;
97 case HID_USAGE_SENSOR_TIME_DAY:
98 time_buf->tm_mday = *(u8 *)raw_data;
99 break;
100 case HID_USAGE_SENSOR_TIME_HOUR:
101 time_buf->tm_hour = *(u8 *)raw_data;
102 break;
103 case HID_USAGE_SENSOR_TIME_MINUTE:
104 time_buf->tm_min = *(u8 *)raw_data;
105 break;
106 case HID_USAGE_SENSOR_TIME_SECOND:
107 time_buf->tm_sec = *(u8 *)raw_data;
108 break;
109 default:
110 return -EINVAL;
111 }
112 return 0;
113}
114
115/* small helper, haven't found any other way */
116static const char *hid_time_attrib_name(u32 attrib_id)
117{
118 static const char unknown[] = "unknown";
119 unsigned i;
120
121 for (i = 0; i < TIME_RTC_CHANNEL_MAX; ++i) {
122 if (hid_time_addresses[i] == attrib_id)
123 return hid_time_channel_names[i];
124 }
125 return unknown; /* should never happen */
126}
127
128static int hid_time_parse_report(struct platform_device *pdev,
129 struct hid_sensor_hub_device *hsdev,
130 unsigned usage_id,
131 struct hid_time_state *time_state)
132{
133 int report_id, i;
134
135 for (i = 0; i < TIME_RTC_CHANNEL_MAX; ++i)
136 if (sensor_hub_input_get_attribute_info(hsdev,
137 HID_INPUT_REPORT, usage_id,
138 hid_time_addresses[i],
139 &time_state->info[i]) < 0)
140 return -EINVAL;
141 /* Check the (needed) attributes for sanity */
142 report_id = time_state->info[0].report_id;
143 if (report_id < 0) {
144 dev_err(&pdev->dev, "bad report ID!\n");
145 return -EINVAL;
146 }
147 for (i = 0; i < TIME_RTC_CHANNEL_MAX; ++i) {
148 if (time_state->info[i].report_id != report_id) {
149 dev_err(&pdev->dev,
150 "not all needed attributes inside the same report!\n");
151 return -EINVAL;
152 }
153 if (time_state->info[i].size != 1) {
154 dev_err(&pdev->dev,
155 "attribute '%s' not 8 bits wide!\n",
156 hid_time_attrib_name(
157 time_state->info[i].attrib_id));
158 return -EINVAL;
159 }
160 if (time_state->info[i].units !=
161 HID_USAGE_SENSOR_UNITS_NOT_SPECIFIED &&
162 /* allow attribute seconds with unit seconds */
163 !(time_state->info[i].attrib_id ==
164 HID_USAGE_SENSOR_TIME_SECOND &&
165 time_state->info[i].units ==
166 HID_USAGE_SENSOR_UNITS_SECOND)) {
167 dev_err(&pdev->dev,
168 "attribute '%s' hasn't a unit of type 'none'!\n",
169 hid_time_attrib_name(
170 time_state->info[i].attrib_id));
171 return -EINVAL;
172 }
173 if (time_state->info[i].unit_expo) {
174 dev_err(&pdev->dev,
175 "attribute '%s' hasn't a unit exponent of 1!\n",
176 hid_time_attrib_name(
177 time_state->info[i].attrib_id));
178 return -EINVAL;
179 }
180 }
181
182 return 0;
183}
184
185static int hid_rtc_read_time(struct device *dev, struct rtc_time *tm)
186{
187 unsigned long flags;
188 struct hid_time_state *time_state =
189 platform_get_drvdata(to_platform_device(dev));
190 int ret;
191
192 INIT_COMPLETION(time_state->comp_last_time);
193 /* get a report with all values through requesting one value */
194 sensor_hub_input_attr_get_raw_value(time_state->common_attributes.hsdev,
195 HID_USAGE_SENSOR_TIME, hid_time_addresses[0],
196 time_state->info[0].report_id);
197 /* wait for all values (event) */
198 ret = wait_for_completion_killable_timeout(
199 &time_state->comp_last_time, HZ*6);
200 if (ret > 0) {
201 /* no error */
202 spin_lock_irqsave(&time_state->lock_last_time, flags);
203 *tm = time_state->last_time;
204 spin_unlock_irqrestore(&time_state->lock_last_time, flags);
205 return 0;
206 }
207 if (!ret)
208 return -EIO; /* timeouted */
209 return ret; /* killed (-ERESTARTSYS) */
210}
211
212static const struct rtc_class_ops hid_time_rtc_ops = {
213 .read_time = hid_rtc_read_time,
214};
215
216static int hid_time_probe(struct platform_device *pdev)
217{
218 int ret = 0;
219 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
220 struct hid_time_state *time_state = devm_kzalloc(&pdev->dev,
221 sizeof(struct hid_time_state), GFP_KERNEL);
222
223 if (time_state == NULL)
224 return -ENOMEM;
225
226 platform_set_drvdata(pdev, time_state);
227
228 spin_lock_init(&time_state->lock_last_time);
229 init_completion(&time_state->comp_last_time);
230 time_state->common_attributes.hsdev = hsdev;
231 time_state->common_attributes.pdev = pdev;
232
233 ret = hid_sensor_parse_common_attributes(hsdev,
234 HID_USAGE_SENSOR_TIME,
235 &time_state->common_attributes);
236 if (ret) {
237 dev_err(&pdev->dev, "failed to setup common attributes!\n");
238 return ret;
239 }
240
241 ret = hid_time_parse_report(pdev, hsdev, HID_USAGE_SENSOR_TIME,
242 time_state);
243 if (ret) {
244 dev_err(&pdev->dev, "failed to setup attributes!\n");
245 return ret;
246 }
247
248 time_state->callbacks.send_event = hid_time_proc_event;
249 time_state->callbacks.capture_sample = hid_time_capture_sample;
250 time_state->callbacks.pdev = pdev;
251 ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_TIME,
252 &time_state->callbacks);
253 if (ret < 0) {
254 dev_err(&pdev->dev, "register callback failed!\n");
255 return ret;
256 }
257
258 time_state->rtc = rtc_device_register("hid-sensor-time",
259 &pdev->dev, &hid_time_rtc_ops, THIS_MODULE);
260
261 if (IS_ERR(time_state->rtc)) {
262 dev_err(&pdev->dev, "rtc device register failed!\n");
263 return PTR_ERR(time_state->rtc);
264 }
265
266 return ret;
267}
268
269static int hid_time_remove(struct platform_device *pdev)
270{
271 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
272 struct hid_time_state *time_state = platform_get_drvdata(pdev);
273
274 rtc_device_unregister(time_state->rtc);
275 sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TIME);
276
277 return 0;
278}
279
280static struct platform_driver hid_time_platform_driver = {
281 .driver = {
282 .name = DRIVER_NAME,
283 .owner = THIS_MODULE,
284 },
285 .probe = hid_time_probe,
286 .remove = hid_time_remove,
287};
288module_platform_driver(hid_time_platform_driver);
289
290MODULE_DESCRIPTION("HID Sensor Time");
291MODULE_AUTHOR("Alexander Holler <holler@ahsoftware.de>");
292MODULE_LICENSE("GPL");