1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // RTC driver for ChromeOS Embedded Controller. |
3 | // |
4 | // Copyright (C) 2017 Google, Inc. |
5 | // Author: Stephen Barber <smbarber@chromium.org> |
6 | |
7 | #include <linux/kernel.h> |
8 | #include <linux/module.h> |
9 | #include <linux/platform_data/cros_ec_commands.h> |
10 | #include <linux/platform_data/cros_ec_proto.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/rtc.h> |
13 | #include <linux/slab.h> |
14 | |
15 | #define DRV_NAME "cros-ec-rtc" |
16 | |
17 | #define SECS_PER_DAY (24 * 60 * 60) |
18 | |
19 | /** |
20 | * struct cros_ec_rtc - Driver data for EC RTC |
21 | * |
22 | * @cros_ec: Pointer to EC device |
23 | * @rtc: Pointer to RTC device |
24 | * @notifier: Notifier info for responding to EC events |
25 | * @saved_alarm: Alarm to restore when interrupts are reenabled |
26 | */ |
27 | struct cros_ec_rtc { |
28 | struct cros_ec_device *cros_ec; |
29 | struct rtc_device *rtc; |
30 | struct notifier_block notifier; |
31 | u32 saved_alarm; |
32 | }; |
33 | |
34 | static int cros_ec_rtc_get(struct cros_ec_device *cros_ec, u32 command, |
35 | u32 *response) |
36 | { |
37 | int ret; |
38 | struct { |
39 | struct cros_ec_command msg; |
40 | struct ec_response_rtc data; |
41 | } __packed msg; |
42 | |
43 | memset(&msg, 0, sizeof(msg)); |
44 | msg.msg.command = command; |
45 | msg.msg.insize = sizeof(msg.data); |
46 | |
47 | ret = cros_ec_cmd_xfer_status(ec_dev: cros_ec, msg: &msg.msg); |
48 | if (ret < 0) |
49 | return ret; |
50 | |
51 | *response = msg.data.time; |
52 | |
53 | return 0; |
54 | } |
55 | |
56 | static int cros_ec_rtc_set(struct cros_ec_device *cros_ec, u32 command, |
57 | u32 param) |
58 | { |
59 | int ret; |
60 | struct { |
61 | struct cros_ec_command msg; |
62 | struct ec_response_rtc data; |
63 | } __packed msg; |
64 | |
65 | memset(&msg, 0, sizeof(msg)); |
66 | msg.msg.command = command; |
67 | msg.msg.outsize = sizeof(msg.data); |
68 | msg.data.time = param; |
69 | |
70 | ret = cros_ec_cmd_xfer_status(ec_dev: cros_ec, msg: &msg.msg); |
71 | if (ret < 0) |
72 | return ret; |
73 | return 0; |
74 | } |
75 | |
76 | /* Read the current time from the EC. */ |
77 | static int cros_ec_rtc_read_time(struct device *dev, struct rtc_time *tm) |
78 | { |
79 | struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); |
80 | struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; |
81 | int ret; |
82 | u32 time; |
83 | |
84 | ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, response: &time); |
85 | if (ret) { |
86 | dev_err(dev, "error getting time: %d\n" , ret); |
87 | return ret; |
88 | } |
89 | |
90 | rtc_time64_to_tm(time, tm); |
91 | |
92 | return 0; |
93 | } |
94 | |
95 | /* Set the current EC time. */ |
96 | static int cros_ec_rtc_set_time(struct device *dev, struct rtc_time *tm) |
97 | { |
98 | struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); |
99 | struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; |
100 | int ret; |
101 | time64_t time = rtc_tm_to_time64(tm); |
102 | |
103 | ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_VALUE, param: (u32)time); |
104 | if (ret < 0) { |
105 | dev_err(dev, "error setting time: %d\n" , ret); |
106 | return ret; |
107 | } |
108 | |
109 | return 0; |
110 | } |
111 | |
112 | /* Read alarm time from RTC. */ |
113 | static int cros_ec_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
114 | { |
115 | struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); |
116 | struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; |
117 | int ret; |
118 | u32 current_time, alarm_offset; |
119 | |
120 | /* |
121 | * The EC host command for getting the alarm is relative (i.e. 5 |
122 | * seconds from now) whereas rtc_wkalrm is absolute. Get the current |
123 | * RTC time first so we can calculate the relative time. |
124 | */ |
125 | ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, response: ¤t_time); |
126 | if (ret < 0) { |
127 | dev_err(dev, "error getting time: %d\n" , ret); |
128 | return ret; |
129 | } |
130 | |
131 | ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM, response: &alarm_offset); |
132 | if (ret < 0) { |
133 | dev_err(dev, "error getting alarm: %d\n" , ret); |
134 | return ret; |
135 | } |
136 | |
137 | rtc_time64_to_tm(time: current_time + alarm_offset, tm: &alrm->time); |
138 | |
139 | return 0; |
140 | } |
141 | |
142 | /* Set the EC's RTC alarm. */ |
143 | static int cros_ec_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
144 | { |
145 | struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); |
146 | struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; |
147 | int ret; |
148 | time64_t alarm_time; |
149 | u32 current_time, alarm_offset; |
150 | |
151 | /* |
152 | * The EC host command for setting the alarm is relative |
153 | * (i.e. 5 seconds from now) whereas rtc_wkalrm is absolute. |
154 | * Get the current RTC time first so we can calculate the |
155 | * relative time. |
156 | */ |
157 | ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, response: ¤t_time); |
158 | if (ret < 0) { |
159 | dev_err(dev, "error getting time: %d\n" , ret); |
160 | return ret; |
161 | } |
162 | |
163 | alarm_time = rtc_tm_to_time64(tm: &alrm->time); |
164 | |
165 | if (alarm_time < 0 || alarm_time > U32_MAX) |
166 | return -EINVAL; |
167 | |
168 | if (!alrm->enabled) { |
169 | /* |
170 | * If the alarm is being disabled, send an alarm |
171 | * clear command. |
172 | */ |
173 | alarm_offset = EC_RTC_ALARM_CLEAR; |
174 | cros_ec_rtc->saved_alarm = (u32)alarm_time; |
175 | } else { |
176 | /* Don't set an alarm in the past. */ |
177 | if ((u32)alarm_time <= current_time) |
178 | return -ETIME; |
179 | |
180 | alarm_offset = (u32)alarm_time - current_time; |
181 | } |
182 | |
183 | ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, param: alarm_offset); |
184 | if (ret < 0) { |
185 | dev_err(dev, "error setting alarm in %u seconds: %d\n" , |
186 | alarm_offset, ret); |
187 | /* |
188 | * The EC code returns -EINVAL if the alarm time is too |
189 | * far in the future. Convert it to the expected error code. |
190 | */ |
191 | if (ret == -EINVAL) |
192 | ret = -ERANGE; |
193 | return ret; |
194 | } |
195 | |
196 | return 0; |
197 | } |
198 | |
199 | static int cros_ec_rtc_alarm_irq_enable(struct device *dev, |
200 | unsigned int enabled) |
201 | { |
202 | struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); |
203 | struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; |
204 | int ret; |
205 | u32 current_time, alarm_offset, alarm_value; |
206 | |
207 | ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, response: ¤t_time); |
208 | if (ret < 0) { |
209 | dev_err(dev, "error getting time: %d\n" , ret); |
210 | return ret; |
211 | } |
212 | |
213 | if (enabled) { |
214 | /* Restore saved alarm if it's still in the future. */ |
215 | if (cros_ec_rtc->saved_alarm < current_time) |
216 | alarm_offset = EC_RTC_ALARM_CLEAR; |
217 | else |
218 | alarm_offset = cros_ec_rtc->saved_alarm - current_time; |
219 | |
220 | ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, |
221 | param: alarm_offset); |
222 | if (ret < 0) { |
223 | dev_err(dev, "error restoring alarm: %d\n" , ret); |
224 | return ret; |
225 | } |
226 | } else { |
227 | /* Disable alarm, saving the old alarm value. */ |
228 | ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM, |
229 | response: &alarm_offset); |
230 | if (ret < 0) { |
231 | dev_err(dev, "error saving alarm: %d\n" , ret); |
232 | return ret; |
233 | } |
234 | |
235 | alarm_value = current_time + alarm_offset; |
236 | |
237 | /* |
238 | * If the current EC alarm is already past, we don't want |
239 | * to set an alarm when we go through the alarm irq enable |
240 | * path. |
241 | */ |
242 | if (alarm_value < current_time) |
243 | cros_ec_rtc->saved_alarm = EC_RTC_ALARM_CLEAR; |
244 | else |
245 | cros_ec_rtc->saved_alarm = alarm_value; |
246 | |
247 | alarm_offset = EC_RTC_ALARM_CLEAR; |
248 | ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, |
249 | param: alarm_offset); |
250 | if (ret < 0) { |
251 | dev_err(dev, "error disabling alarm: %d\n" , ret); |
252 | return ret; |
253 | } |
254 | } |
255 | |
256 | return 0; |
257 | } |
258 | |
259 | static int cros_ec_rtc_event(struct notifier_block *nb, |
260 | unsigned long queued_during_suspend, |
261 | void *_notify) |
262 | { |
263 | struct cros_ec_rtc *cros_ec_rtc; |
264 | struct rtc_device *rtc; |
265 | struct cros_ec_device *cros_ec; |
266 | u32 host_event; |
267 | |
268 | cros_ec_rtc = container_of(nb, struct cros_ec_rtc, notifier); |
269 | rtc = cros_ec_rtc->rtc; |
270 | cros_ec = cros_ec_rtc->cros_ec; |
271 | |
272 | host_event = cros_ec_get_host_event(ec_dev: cros_ec); |
273 | if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_RTC)) { |
274 | rtc_update_irq(rtc, num: 1, RTC_IRQF | RTC_AF); |
275 | return NOTIFY_OK; |
276 | } else { |
277 | return NOTIFY_DONE; |
278 | } |
279 | } |
280 | |
281 | static const struct rtc_class_ops cros_ec_rtc_ops = { |
282 | .read_time = cros_ec_rtc_read_time, |
283 | .set_time = cros_ec_rtc_set_time, |
284 | .read_alarm = cros_ec_rtc_read_alarm, |
285 | .set_alarm = cros_ec_rtc_set_alarm, |
286 | .alarm_irq_enable = cros_ec_rtc_alarm_irq_enable, |
287 | }; |
288 | |
289 | #ifdef CONFIG_PM_SLEEP |
290 | static int cros_ec_rtc_suspend(struct device *dev) |
291 | { |
292 | struct platform_device *pdev = to_platform_device(dev); |
293 | struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev: &pdev->dev); |
294 | |
295 | if (device_may_wakeup(dev)) |
296 | return enable_irq_wake(irq: cros_ec_rtc->cros_ec->irq); |
297 | |
298 | return 0; |
299 | } |
300 | |
301 | static int cros_ec_rtc_resume(struct device *dev) |
302 | { |
303 | struct platform_device *pdev = to_platform_device(dev); |
304 | struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev: &pdev->dev); |
305 | |
306 | if (device_may_wakeup(dev)) |
307 | return disable_irq_wake(irq: cros_ec_rtc->cros_ec->irq); |
308 | |
309 | return 0; |
310 | } |
311 | #endif |
312 | |
313 | static SIMPLE_DEV_PM_OPS(cros_ec_rtc_pm_ops, cros_ec_rtc_suspend, |
314 | cros_ec_rtc_resume); |
315 | |
316 | static int cros_ec_rtc_probe(struct platform_device *pdev) |
317 | { |
318 | struct cros_ec_dev *ec_dev = dev_get_drvdata(dev: pdev->dev.parent); |
319 | struct cros_ec_device *cros_ec = ec_dev->ec_dev; |
320 | struct cros_ec_rtc *cros_ec_rtc; |
321 | struct rtc_time tm; |
322 | int ret; |
323 | |
324 | cros_ec_rtc = devm_kzalloc(dev: &pdev->dev, size: sizeof(*cros_ec_rtc), |
325 | GFP_KERNEL); |
326 | if (!cros_ec_rtc) |
327 | return -ENOMEM; |
328 | |
329 | platform_set_drvdata(pdev, data: cros_ec_rtc); |
330 | cros_ec_rtc->cros_ec = cros_ec; |
331 | |
332 | /* Get initial time */ |
333 | ret = cros_ec_rtc_read_time(dev: &pdev->dev, tm: &tm); |
334 | if (ret) { |
335 | dev_err(&pdev->dev, "failed to read RTC time\n" ); |
336 | return ret; |
337 | } |
338 | |
339 | ret = device_init_wakeup(dev: &pdev->dev, enable: 1); |
340 | if (ret) { |
341 | dev_err(&pdev->dev, "failed to initialize wakeup\n" ); |
342 | return ret; |
343 | } |
344 | |
345 | cros_ec_rtc->rtc = devm_rtc_allocate_device(dev: &pdev->dev); |
346 | if (IS_ERR(ptr: cros_ec_rtc->rtc)) |
347 | return PTR_ERR(ptr: cros_ec_rtc->rtc); |
348 | |
349 | cros_ec_rtc->rtc->ops = &cros_ec_rtc_ops; |
350 | cros_ec_rtc->rtc->range_max = U32_MAX; |
351 | |
352 | /* |
353 | * The RTC on some older Chromebooks can only handle alarms less than |
354 | * 24 hours in the future. The only way to find out is to try to set an |
355 | * alarm further in the future. If that fails, assume that the RTC |
356 | * connected to the EC can only handle less than 24 hours of alarm |
357 | * window. |
358 | */ |
359 | ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, SECS_PER_DAY * 2); |
360 | if (ret == -EINVAL) |
361 | cros_ec_rtc->rtc->alarm_offset_max = SECS_PER_DAY - 1; |
362 | |
363 | (void)cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, |
364 | EC_RTC_ALARM_CLEAR); |
365 | |
366 | ret = devm_rtc_register_device(cros_ec_rtc->rtc); |
367 | if (ret) |
368 | return ret; |
369 | |
370 | /* Get RTC events from the EC. */ |
371 | cros_ec_rtc->notifier.notifier_call = cros_ec_rtc_event; |
372 | ret = blocking_notifier_chain_register(nh: &cros_ec->event_notifier, |
373 | nb: &cros_ec_rtc->notifier); |
374 | if (ret) { |
375 | dev_err(&pdev->dev, "failed to register notifier\n" ); |
376 | return ret; |
377 | } |
378 | |
379 | return 0; |
380 | } |
381 | |
382 | static void cros_ec_rtc_remove(struct platform_device *pdev) |
383 | { |
384 | struct cros_ec_rtc *cros_ec_rtc = platform_get_drvdata(pdev); |
385 | struct device *dev = &pdev->dev; |
386 | int ret; |
387 | |
388 | ret = blocking_notifier_chain_unregister( |
389 | nh: &cros_ec_rtc->cros_ec->event_notifier, |
390 | nb: &cros_ec_rtc->notifier); |
391 | if (ret) |
392 | dev_err(dev, "failed to unregister notifier\n" ); |
393 | } |
394 | |
395 | static struct platform_driver cros_ec_rtc_driver = { |
396 | .probe = cros_ec_rtc_probe, |
397 | .remove_new = cros_ec_rtc_remove, |
398 | .driver = { |
399 | .name = DRV_NAME, |
400 | .pm = &cros_ec_rtc_pm_ops, |
401 | }, |
402 | }; |
403 | |
404 | module_platform_driver(cros_ec_rtc_driver); |
405 | |
406 | MODULE_DESCRIPTION("RTC driver for Chrome OS ECs" ); |
407 | MODULE_AUTHOR("Stephen Barber <smbarber@chromium.org>" ); |
408 | MODULE_LICENSE("GPL v2" ); |
409 | MODULE_ALIAS("platform:" DRV_NAME); |
410 | |