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