12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2077eaf5bSMark Brown /*
3077eaf5bSMark Brown * Real Time Clock driver for Wolfson Microelectronics WM8350
4077eaf5bSMark Brown *
5077eaf5bSMark Brown * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC.
6077eaf5bSMark Brown *
7077eaf5bSMark Brown * Author: Liam Girdwood
8077eaf5bSMark Brown * linux@wolfsonmicro.com
9077eaf5bSMark Brown */
10077eaf5bSMark Brown
11077eaf5bSMark Brown #include <linux/module.h>
12077eaf5bSMark Brown #include <linux/kernel.h>
13077eaf5bSMark Brown #include <linux/time.h>
14077eaf5bSMark Brown #include <linux/rtc.h>
15077eaf5bSMark Brown #include <linux/bcd.h>
16077eaf5bSMark Brown #include <linux/interrupt.h>
17077eaf5bSMark Brown #include <linux/ioctl.h>
18077eaf5bSMark Brown #include <linux/completion.h>
19077eaf5bSMark Brown #include <linux/mfd/wm8350/rtc.h>
20077eaf5bSMark Brown #include <linux/mfd/wm8350/core.h>
21077eaf5bSMark Brown #include <linux/delay.h>
22077eaf5bSMark Brown #include <linux/platform_device.h>
23077eaf5bSMark Brown
24077eaf5bSMark Brown #define WM8350_SET_ALM_RETRIES 5
25077eaf5bSMark Brown #define WM8350_SET_TIME_RETRIES 5
26077eaf5bSMark Brown #define WM8350_GET_TIME_RETRIES 5
27077eaf5bSMark Brown
28077eaf5bSMark Brown /*
29077eaf5bSMark Brown * Read current time and date in RTC
30077eaf5bSMark Brown */
wm8350_rtc_readtime(struct device * dev,struct rtc_time * tm)31077eaf5bSMark Brown static int wm8350_rtc_readtime(struct device *dev, struct rtc_time *tm)
32077eaf5bSMark Brown {
33077eaf5bSMark Brown struct wm8350 *wm8350 = dev_get_drvdata(dev);
34077eaf5bSMark Brown u16 time1[4], time2[4];
35077eaf5bSMark Brown int retries = WM8350_GET_TIME_RETRIES, ret;
36077eaf5bSMark Brown
37077eaf5bSMark Brown /*
38077eaf5bSMark Brown * Read the time twice and compare.
39077eaf5bSMark Brown * If time1 == time2, then time is valid else retry.
40077eaf5bSMark Brown */
41077eaf5bSMark Brown do {
42077eaf5bSMark Brown ret = wm8350_block_read(wm8350, WM8350_RTC_SECONDS_MINUTES,
43077eaf5bSMark Brown 4, time1);
44077eaf5bSMark Brown if (ret < 0)
45077eaf5bSMark Brown return ret;
46077eaf5bSMark Brown ret = wm8350_block_read(wm8350, WM8350_RTC_SECONDS_MINUTES,
47077eaf5bSMark Brown 4, time2);
48077eaf5bSMark Brown if (ret < 0)
49077eaf5bSMark Brown return ret;
50077eaf5bSMark Brown
51077eaf5bSMark Brown if (memcmp(time1, time2, sizeof(time1)) == 0) {
52077eaf5bSMark Brown tm->tm_sec = time1[0] & WM8350_RTC_SECS_MASK;
53077eaf5bSMark Brown
54077eaf5bSMark Brown tm->tm_min = (time1[0] & WM8350_RTC_MINS_MASK)
55077eaf5bSMark Brown >> WM8350_RTC_MINS_SHIFT;
56077eaf5bSMark Brown
57077eaf5bSMark Brown tm->tm_hour = time1[1] & WM8350_RTC_HRS_MASK;
58077eaf5bSMark Brown
59077eaf5bSMark Brown tm->tm_wday = ((time1[1] >> WM8350_RTC_DAY_SHIFT)
60077eaf5bSMark Brown & 0x7) - 1;
61077eaf5bSMark Brown
62077eaf5bSMark Brown tm->tm_mon = ((time1[2] & WM8350_RTC_MTH_MASK)
63077eaf5bSMark Brown >> WM8350_RTC_MTH_SHIFT) - 1;
64077eaf5bSMark Brown
65077eaf5bSMark Brown tm->tm_mday = (time1[2] & WM8350_RTC_DATE_MASK);
66077eaf5bSMark Brown
67077eaf5bSMark Brown tm->tm_year = ((time1[3] & WM8350_RTC_YHUNDREDS_MASK)
68077eaf5bSMark Brown >> WM8350_RTC_YHUNDREDS_SHIFT) * 100;
69077eaf5bSMark Brown tm->tm_year += time1[3] & WM8350_RTC_YUNITS_MASK;
70077eaf5bSMark Brown
71077eaf5bSMark Brown tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon,
72077eaf5bSMark Brown tm->tm_year);
73077eaf5bSMark Brown tm->tm_year -= 1900;
74077eaf5bSMark Brown
75077eaf5bSMark Brown dev_dbg(dev, "Read (%d left): %04x %04x %04x %04x\n",
76077eaf5bSMark Brown retries,
77077eaf5bSMark Brown time1[0], time1[1], time1[2], time1[3]);
78077eaf5bSMark Brown
79077eaf5bSMark Brown return 0;
80077eaf5bSMark Brown }
81077eaf5bSMark Brown } while (retries--);
82077eaf5bSMark Brown
83077eaf5bSMark Brown dev_err(dev, "timed out reading RTC time\n");
84077eaf5bSMark Brown return -EIO;
85077eaf5bSMark Brown }
86077eaf5bSMark Brown
87077eaf5bSMark Brown /*
88077eaf5bSMark Brown * Set current time and date in RTC
89077eaf5bSMark Brown */
wm8350_rtc_settime(struct device * dev,struct rtc_time * tm)90077eaf5bSMark Brown static int wm8350_rtc_settime(struct device *dev, struct rtc_time *tm)
91077eaf5bSMark Brown {
92077eaf5bSMark Brown struct wm8350 *wm8350 = dev_get_drvdata(dev);
93077eaf5bSMark Brown u16 time[4];
94077eaf5bSMark Brown u16 rtc_ctrl;
95077eaf5bSMark Brown int ret, retries = WM8350_SET_TIME_RETRIES;
96077eaf5bSMark Brown
97077eaf5bSMark Brown time[0] = tm->tm_sec;
98077eaf5bSMark Brown time[0] |= tm->tm_min << WM8350_RTC_MINS_SHIFT;
99077eaf5bSMark Brown time[1] = tm->tm_hour;
100077eaf5bSMark Brown time[1] |= (tm->tm_wday + 1) << WM8350_RTC_DAY_SHIFT;
101077eaf5bSMark Brown time[2] = tm->tm_mday;
102077eaf5bSMark Brown time[2] |= (tm->tm_mon + 1) << WM8350_RTC_MTH_SHIFT;
103077eaf5bSMark Brown time[3] = ((tm->tm_year + 1900) / 100) << WM8350_RTC_YHUNDREDS_SHIFT;
104077eaf5bSMark Brown time[3] |= (tm->tm_year + 1900) % 100;
105077eaf5bSMark Brown
106077eaf5bSMark Brown dev_dbg(dev, "Setting: %04x %04x %04x %04x\n",
107077eaf5bSMark Brown time[0], time[1], time[2], time[3]);
108077eaf5bSMark Brown
109077eaf5bSMark Brown /* Set RTC_SET to stop the clock */
110077eaf5bSMark Brown ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL, WM8350_RTC_SET);
111077eaf5bSMark Brown if (ret < 0)
112077eaf5bSMark Brown return ret;
113077eaf5bSMark Brown
114077eaf5bSMark Brown /* Wait until confirmation of stopping */
115077eaf5bSMark Brown do {
116077eaf5bSMark Brown rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL);
117077eaf5bSMark Brown schedule_timeout_uninterruptible(msecs_to_jiffies(1));
11862da659aSRoel Kluin } while (--retries && !(rtc_ctrl & WM8350_RTC_STS));
119077eaf5bSMark Brown
120077eaf5bSMark Brown if (!retries) {
121077eaf5bSMark Brown dev_err(dev, "timed out on set confirmation\n");
122077eaf5bSMark Brown return -EIO;
123077eaf5bSMark Brown }
124077eaf5bSMark Brown
125077eaf5bSMark Brown /* Write time to RTC */
126077eaf5bSMark Brown ret = wm8350_block_write(wm8350, WM8350_RTC_SECONDS_MINUTES, 4, time);
127077eaf5bSMark Brown if (ret < 0)
128077eaf5bSMark Brown return ret;
129077eaf5bSMark Brown
130077eaf5bSMark Brown /* Clear RTC_SET to start the clock */
131077eaf5bSMark Brown ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL,
132077eaf5bSMark Brown WM8350_RTC_SET);
133077eaf5bSMark Brown return ret;
134077eaf5bSMark Brown }
135077eaf5bSMark Brown
136077eaf5bSMark Brown /*
137077eaf5bSMark Brown * Read alarm time and date in RTC
138077eaf5bSMark Brown */
wm8350_rtc_readalarm(struct device * dev,struct rtc_wkalrm * alrm)139077eaf5bSMark Brown static int wm8350_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
140077eaf5bSMark Brown {
141077eaf5bSMark Brown struct wm8350 *wm8350 = dev_get_drvdata(dev);
142077eaf5bSMark Brown struct rtc_time *tm = &alrm->time;
143077eaf5bSMark Brown u16 time[4];
144077eaf5bSMark Brown int ret;
145077eaf5bSMark Brown
146077eaf5bSMark Brown ret = wm8350_block_read(wm8350, WM8350_ALARM_SECONDS_MINUTES, 4, time);
147077eaf5bSMark Brown if (ret < 0)
148077eaf5bSMark Brown return ret;
149077eaf5bSMark Brown
150077eaf5bSMark Brown tm->tm_sec = time[0] & WM8350_RTC_ALMSECS_MASK;
151077eaf5bSMark Brown if (tm->tm_sec == WM8350_RTC_ALMSECS_MASK)
152077eaf5bSMark Brown tm->tm_sec = -1;
153077eaf5bSMark Brown
154077eaf5bSMark Brown tm->tm_min = time[0] & WM8350_RTC_ALMMINS_MASK;
155077eaf5bSMark Brown if (tm->tm_min == WM8350_RTC_ALMMINS_MASK)
156077eaf5bSMark Brown tm->tm_min = -1;
157077eaf5bSMark Brown else
158077eaf5bSMark Brown tm->tm_min >>= WM8350_RTC_ALMMINS_SHIFT;
159077eaf5bSMark Brown
160077eaf5bSMark Brown tm->tm_hour = time[1] & WM8350_RTC_ALMHRS_MASK;
161077eaf5bSMark Brown if (tm->tm_hour == WM8350_RTC_ALMHRS_MASK)
162077eaf5bSMark Brown tm->tm_hour = -1;
163077eaf5bSMark Brown
164077eaf5bSMark Brown tm->tm_wday = ((time[1] >> WM8350_RTC_ALMDAY_SHIFT) & 0x7) - 1;
165077eaf5bSMark Brown if (tm->tm_wday > 7)
166077eaf5bSMark Brown tm->tm_wday = -1;
167077eaf5bSMark Brown
168077eaf5bSMark Brown tm->tm_mon = time[2] & WM8350_RTC_ALMMTH_MASK;
169077eaf5bSMark Brown if (tm->tm_mon == WM8350_RTC_ALMMTH_MASK)
170077eaf5bSMark Brown tm->tm_mon = -1;
171077eaf5bSMark Brown else
172077eaf5bSMark Brown tm->tm_mon = (tm->tm_mon >> WM8350_RTC_ALMMTH_SHIFT) - 1;
173077eaf5bSMark Brown
174077eaf5bSMark Brown tm->tm_mday = (time[2] & WM8350_RTC_ALMDATE_MASK);
175077eaf5bSMark Brown if (tm->tm_mday == WM8350_RTC_ALMDATE_MASK)
176077eaf5bSMark Brown tm->tm_mday = -1;
177077eaf5bSMark Brown
178077eaf5bSMark Brown tm->tm_year = -1;
179077eaf5bSMark Brown
180077eaf5bSMark Brown alrm->enabled = !(time[3] & WM8350_RTC_ALMSTS);
181077eaf5bSMark Brown
182077eaf5bSMark Brown return 0;
183077eaf5bSMark Brown }
184077eaf5bSMark Brown
wm8350_rtc_stop_alarm(struct wm8350 * wm8350)185077eaf5bSMark Brown static int wm8350_rtc_stop_alarm(struct wm8350 *wm8350)
186077eaf5bSMark Brown {
187077eaf5bSMark Brown int retries = WM8350_SET_ALM_RETRIES;
188077eaf5bSMark Brown u16 rtc_ctrl;
189077eaf5bSMark Brown int ret;
190077eaf5bSMark Brown
191077eaf5bSMark Brown /* Set RTC_SET to stop the clock */
192077eaf5bSMark Brown ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL,
193077eaf5bSMark Brown WM8350_RTC_ALMSET);
194077eaf5bSMark Brown if (ret < 0)
195077eaf5bSMark Brown return ret;
196077eaf5bSMark Brown
197077eaf5bSMark Brown /* Wait until confirmation of stopping */
198077eaf5bSMark Brown do {
199077eaf5bSMark Brown rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL);
200077eaf5bSMark Brown schedule_timeout_uninterruptible(msecs_to_jiffies(1));
201077eaf5bSMark Brown } while (retries-- && !(rtc_ctrl & WM8350_RTC_ALMSTS));
202077eaf5bSMark Brown
203077eaf5bSMark Brown if (!(rtc_ctrl & WM8350_RTC_ALMSTS))
204077eaf5bSMark Brown return -ETIMEDOUT;
205077eaf5bSMark Brown
206077eaf5bSMark Brown return 0;
207077eaf5bSMark Brown }
208077eaf5bSMark Brown
wm8350_rtc_start_alarm(struct wm8350 * wm8350)209077eaf5bSMark Brown static int wm8350_rtc_start_alarm(struct wm8350 *wm8350)
210077eaf5bSMark Brown {
211077eaf5bSMark Brown int ret;
212077eaf5bSMark Brown int retries = WM8350_SET_ALM_RETRIES;
213077eaf5bSMark Brown u16 rtc_ctrl;
214077eaf5bSMark Brown
215077eaf5bSMark Brown ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL,
216077eaf5bSMark Brown WM8350_RTC_ALMSET);
217077eaf5bSMark Brown if (ret < 0)
218077eaf5bSMark Brown return ret;
219077eaf5bSMark Brown
220077eaf5bSMark Brown /* Wait until confirmation */
221077eaf5bSMark Brown do {
222077eaf5bSMark Brown rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL);
223077eaf5bSMark Brown schedule_timeout_uninterruptible(msecs_to_jiffies(1));
224077eaf5bSMark Brown } while (retries-- && rtc_ctrl & WM8350_RTC_ALMSTS);
225077eaf5bSMark Brown
226077eaf5bSMark Brown if (rtc_ctrl & WM8350_RTC_ALMSTS)
227077eaf5bSMark Brown return -ETIMEDOUT;
228077eaf5bSMark Brown
229077eaf5bSMark Brown return 0;
230077eaf5bSMark Brown }
231077eaf5bSMark Brown
wm8350_rtc_alarm_irq_enable(struct device * dev,unsigned int enabled)23247367a3bSMark Brown static int wm8350_rtc_alarm_irq_enable(struct device *dev,
23347367a3bSMark Brown unsigned int enabled)
23447367a3bSMark Brown {
23547367a3bSMark Brown struct wm8350 *wm8350 = dev_get_drvdata(dev);
23647367a3bSMark Brown
23747367a3bSMark Brown if (enabled)
23847367a3bSMark Brown return wm8350_rtc_start_alarm(wm8350);
23947367a3bSMark Brown else
24047367a3bSMark Brown return wm8350_rtc_stop_alarm(wm8350);
24147367a3bSMark Brown }
24247367a3bSMark Brown
wm8350_rtc_setalarm(struct device * dev,struct rtc_wkalrm * alrm)243077eaf5bSMark Brown static int wm8350_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
244077eaf5bSMark Brown {
245077eaf5bSMark Brown struct wm8350 *wm8350 = dev_get_drvdata(dev);
246077eaf5bSMark Brown struct rtc_time *tm = &alrm->time;
247077eaf5bSMark Brown u16 time[3];
248077eaf5bSMark Brown int ret;
249077eaf5bSMark Brown
250077eaf5bSMark Brown memset(time, 0, sizeof(time));
251077eaf5bSMark Brown
252077eaf5bSMark Brown if (tm->tm_sec != -1)
253077eaf5bSMark Brown time[0] |= tm->tm_sec;
254077eaf5bSMark Brown else
255077eaf5bSMark Brown time[0] |= WM8350_RTC_ALMSECS_MASK;
256077eaf5bSMark Brown
257077eaf5bSMark Brown if (tm->tm_min != -1)
258077eaf5bSMark Brown time[0] |= tm->tm_min << WM8350_RTC_ALMMINS_SHIFT;
259077eaf5bSMark Brown else
260077eaf5bSMark Brown time[0] |= WM8350_RTC_ALMMINS_MASK;
261077eaf5bSMark Brown
262077eaf5bSMark Brown if (tm->tm_hour != -1)
263077eaf5bSMark Brown time[1] |= tm->tm_hour;
264077eaf5bSMark Brown else
265077eaf5bSMark Brown time[1] |= WM8350_RTC_ALMHRS_MASK;
266077eaf5bSMark Brown
267077eaf5bSMark Brown if (tm->tm_wday != -1)
268077eaf5bSMark Brown time[1] |= (tm->tm_wday + 1) << WM8350_RTC_ALMDAY_SHIFT;
269077eaf5bSMark Brown else
270077eaf5bSMark Brown time[1] |= WM8350_RTC_ALMDAY_MASK;
271077eaf5bSMark Brown
272077eaf5bSMark Brown if (tm->tm_mday != -1)
273077eaf5bSMark Brown time[2] |= tm->tm_mday;
274077eaf5bSMark Brown else
275077eaf5bSMark Brown time[2] |= WM8350_RTC_ALMDATE_MASK;
276077eaf5bSMark Brown
277077eaf5bSMark Brown if (tm->tm_mon != -1)
278077eaf5bSMark Brown time[2] |= (tm->tm_mon + 1) << WM8350_RTC_ALMMTH_SHIFT;
279077eaf5bSMark Brown else
280077eaf5bSMark Brown time[2] |= WM8350_RTC_ALMMTH_MASK;
281077eaf5bSMark Brown
282077eaf5bSMark Brown ret = wm8350_rtc_stop_alarm(wm8350);
283077eaf5bSMark Brown if (ret < 0)
284077eaf5bSMark Brown return ret;
285077eaf5bSMark Brown
286077eaf5bSMark Brown /* Write time to RTC */
287077eaf5bSMark Brown ret = wm8350_block_write(wm8350, WM8350_ALARM_SECONDS_MINUTES,
288077eaf5bSMark Brown 3, time);
289077eaf5bSMark Brown if (ret < 0)
290077eaf5bSMark Brown return ret;
291077eaf5bSMark Brown
292077eaf5bSMark Brown if (alrm->enabled)
293077eaf5bSMark Brown ret = wm8350_rtc_start_alarm(wm8350);
294077eaf5bSMark Brown
295077eaf5bSMark Brown return ret;
296077eaf5bSMark Brown }
297077eaf5bSMark Brown
wm8350_rtc_alarm_handler(int irq,void * data)2985a65edbcSMark Brown static irqreturn_t wm8350_rtc_alarm_handler(int irq, void *data)
299077eaf5bSMark Brown {
3005a65edbcSMark Brown struct wm8350 *wm8350 = data;
301077eaf5bSMark Brown struct rtc_device *rtc = wm8350->rtc.rtc;
302077eaf5bSMark Brown int ret;
303077eaf5bSMark Brown
304077eaf5bSMark Brown rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
305077eaf5bSMark Brown
306077eaf5bSMark Brown /* Make it one shot */
307077eaf5bSMark Brown ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL,
308077eaf5bSMark Brown WM8350_RTC_ALMSET);
309077eaf5bSMark Brown if (ret != 0) {
310077eaf5bSMark Brown dev_err(&(wm8350->rtc.pdev->dev),
311077eaf5bSMark Brown "Failed to disable alarm: %d\n", ret);
312077eaf5bSMark Brown }
3135a65edbcSMark Brown
3145a65edbcSMark Brown return IRQ_HANDLED;
315077eaf5bSMark Brown }
316077eaf5bSMark Brown
wm8350_rtc_update_handler(int irq,void * data)3175a65edbcSMark Brown static irqreturn_t wm8350_rtc_update_handler(int irq, void *data)
318077eaf5bSMark Brown {
3195a65edbcSMark Brown struct wm8350 *wm8350 = data;
320077eaf5bSMark Brown struct rtc_device *rtc = wm8350->rtc.rtc;
321077eaf5bSMark Brown
322077eaf5bSMark Brown rtc_update_irq(rtc, 1, RTC_IRQF | RTC_UF);
3235a65edbcSMark Brown
3245a65edbcSMark Brown return IRQ_HANDLED;
325077eaf5bSMark Brown }
326077eaf5bSMark Brown
327077eaf5bSMark Brown static const struct rtc_class_ops wm8350_rtc_ops = {
328077eaf5bSMark Brown .read_time = wm8350_rtc_readtime,
329077eaf5bSMark Brown .set_time = wm8350_rtc_settime,
330077eaf5bSMark Brown .read_alarm = wm8350_rtc_readalarm,
331077eaf5bSMark Brown .set_alarm = wm8350_rtc_setalarm,
33247367a3bSMark Brown .alarm_irq_enable = wm8350_rtc_alarm_irq_enable,
333077eaf5bSMark Brown };
334077eaf5bSMark Brown
3357d17158cSJingoo Han #ifdef CONFIG_PM_SLEEP
wm8350_rtc_suspend(struct device * dev)3366f38b043SMark Brown static int wm8350_rtc_suspend(struct device *dev)
337077eaf5bSMark Brown {
338527bd754SKefeng Wang struct wm8350 *wm8350 = dev_get_drvdata(dev);
339077eaf5bSMark Brown int ret = 0;
340077eaf5bSMark Brown u16 reg;
341077eaf5bSMark Brown
342077eaf5bSMark Brown reg = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL);
343077eaf5bSMark Brown
344077eaf5bSMark Brown if (device_may_wakeup(&wm8350->rtc.pdev->dev) &&
345077eaf5bSMark Brown reg & WM8350_RTC_ALMSTS) {
346077eaf5bSMark Brown ret = wm8350_rtc_stop_alarm(wm8350);
347077eaf5bSMark Brown if (ret != 0)
348527bd754SKefeng Wang dev_err(dev, "Failed to stop RTC alarm: %d\n", ret);
349077eaf5bSMark Brown }
350077eaf5bSMark Brown
351077eaf5bSMark Brown return ret;
352077eaf5bSMark Brown }
353077eaf5bSMark Brown
wm8350_rtc_resume(struct device * dev)3546f38b043SMark Brown static int wm8350_rtc_resume(struct device *dev)
355077eaf5bSMark Brown {
356527bd754SKefeng Wang struct wm8350 *wm8350 = dev_get_drvdata(dev);
357077eaf5bSMark Brown int ret;
358077eaf5bSMark Brown
359077eaf5bSMark Brown if (wm8350->rtc.alarm_enabled) {
360077eaf5bSMark Brown ret = wm8350_rtc_start_alarm(wm8350);
361077eaf5bSMark Brown if (ret != 0)
362527bd754SKefeng Wang dev_err(dev, "Failed to restart RTC alarm: %d\n", ret);
363077eaf5bSMark Brown }
364077eaf5bSMark Brown
365077eaf5bSMark Brown return 0;
366077eaf5bSMark Brown }
367077eaf5bSMark Brown #endif
368077eaf5bSMark Brown
wm8350_rtc_probe(struct platform_device * pdev)369077eaf5bSMark Brown static int wm8350_rtc_probe(struct platform_device *pdev)
370077eaf5bSMark Brown {
371077eaf5bSMark Brown struct wm8350 *wm8350 = platform_get_drvdata(pdev);
372077eaf5bSMark Brown struct wm8350_rtc *wm_rtc = &wm8350->rtc;
373077eaf5bSMark Brown int ret = 0;
374077eaf5bSMark Brown u16 timectl, power5;
375077eaf5bSMark Brown
376077eaf5bSMark Brown timectl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL);
377077eaf5bSMark Brown if (timectl & WM8350_RTC_BCD) {
378077eaf5bSMark Brown dev_err(&pdev->dev, "RTC BCD mode not supported\n");
379077eaf5bSMark Brown return -EINVAL;
380077eaf5bSMark Brown }
381077eaf5bSMark Brown if (timectl & WM8350_RTC_12HR) {
382077eaf5bSMark Brown dev_err(&pdev->dev, "RTC 12 hour mode not supported\n");
383077eaf5bSMark Brown return -EINVAL;
384077eaf5bSMark Brown }
385077eaf5bSMark Brown
386077eaf5bSMark Brown /* enable the RTC if it's not already enabled */
387077eaf5bSMark Brown power5 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5);
388077eaf5bSMark Brown if (!(power5 & WM8350_RTC_TICK_ENA)) {
389077eaf5bSMark Brown wm8350_reg_unlock(wm8350);
390077eaf5bSMark Brown
391077eaf5bSMark Brown ret = wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5,
392077eaf5bSMark Brown WM8350_RTC_TICK_ENA);
393077eaf5bSMark Brown if (ret < 0) {
394077eaf5bSMark Brown dev_err(&pdev->dev, "failed to enable RTC: %d\n", ret);
395077eaf5bSMark Brown return ret;
396077eaf5bSMark Brown }
397077eaf5bSMark Brown
398077eaf5bSMark Brown wm8350_reg_lock(wm8350);
399077eaf5bSMark Brown }
400077eaf5bSMark Brown
401077eaf5bSMark Brown if (timectl & WM8350_RTC_STS) {
402077eaf5bSMark Brown int retries;
403077eaf5bSMark Brown
404077eaf5bSMark Brown ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL,
405077eaf5bSMark Brown WM8350_RTC_SET);
406077eaf5bSMark Brown if (ret < 0) {
407077eaf5bSMark Brown dev_err(&pdev->dev, "failed to start: %d\n", ret);
408077eaf5bSMark Brown return ret;
409077eaf5bSMark Brown }
410077eaf5bSMark Brown
411077eaf5bSMark Brown retries = WM8350_SET_TIME_RETRIES;
412077eaf5bSMark Brown do {
413077eaf5bSMark Brown timectl = wm8350_reg_read(wm8350,
414077eaf5bSMark Brown WM8350_RTC_TIME_CONTROL);
41562da659aSRoel Kluin } while (timectl & WM8350_RTC_STS && --retries);
416077eaf5bSMark Brown
417077eaf5bSMark Brown if (retries == 0) {
418077eaf5bSMark Brown dev_err(&pdev->dev, "failed to start: timeout\n");
419077eaf5bSMark Brown return -ENODEV;
420077eaf5bSMark Brown }
421077eaf5bSMark Brown }
422077eaf5bSMark Brown
423077eaf5bSMark Brown device_init_wakeup(&pdev->dev, 1);
424077eaf5bSMark Brown
425a5aa7776SJingoo Han wm_rtc->rtc = devm_rtc_device_register(&pdev->dev, "wm8350",
426077eaf5bSMark Brown &wm8350_rtc_ops, THIS_MODULE);
427*d844c64bSAlexandre Belloni if (IS_ERR(wm_rtc->rtc))
428*d844c64bSAlexandre Belloni return PTR_ERR(wm_rtc->rtc);
429077eaf5bSMark Brown
43043f0269bSJiasheng Jiang ret = wm8350_register_irq(wm8350, WM8350_IRQ_RTC_SEC,
4315a65edbcSMark Brown wm8350_rtc_update_handler, 0,
4325a65edbcSMark Brown "RTC Seconds", wm8350);
43343f0269bSJiasheng Jiang if (ret)
43443f0269bSJiasheng Jiang return ret;
43543f0269bSJiasheng Jiang
4366a612746SMark Brown wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC);
437077eaf5bSMark Brown
43843f0269bSJiasheng Jiang ret = wm8350_register_irq(wm8350, WM8350_IRQ_RTC_ALM,
4395a65edbcSMark Brown wm8350_rtc_alarm_handler, 0,
4405a65edbcSMark Brown "RTC Alarm", wm8350);
44143f0269bSJiasheng Jiang if (ret) {
44243f0269bSJiasheng Jiang wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC, wm8350);
44343f0269bSJiasheng Jiang return ret;
44443f0269bSJiasheng Jiang }
445077eaf5bSMark Brown
446077eaf5bSMark Brown return 0;
447077eaf5bSMark Brown }
448077eaf5bSMark Brown
wm8350_rtc_remove(struct platform_device * pdev)449631aa2d9SUwe Kleine-König static void wm8350_rtc_remove(struct platform_device *pdev)
450077eaf5bSMark Brown {
451077eaf5bSMark Brown struct wm8350 *wm8350 = platform_get_drvdata(pdev);
452077eaf5bSMark Brown
453f99344fcSMark Brown wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC, wm8350);
454f99344fcSMark Brown wm8350_free_irq(wm8350, WM8350_IRQ_RTC_ALM, wm8350);
455077eaf5bSMark Brown }
456077eaf5bSMark Brown
4577d17158cSJingoo Han static SIMPLE_DEV_PM_OPS(wm8350_rtc_pm_ops, wm8350_rtc_suspend,
4587d17158cSJingoo Han wm8350_rtc_resume);
4596f38b043SMark Brown
460077eaf5bSMark Brown static struct platform_driver wm8350_rtc_driver = {
461077eaf5bSMark Brown .probe = wm8350_rtc_probe,
462631aa2d9SUwe Kleine-König .remove_new = wm8350_rtc_remove,
463077eaf5bSMark Brown .driver = {
464077eaf5bSMark Brown .name = "wm8350-rtc",
4656f38b043SMark Brown .pm = &wm8350_rtc_pm_ops,
466077eaf5bSMark Brown },
467077eaf5bSMark Brown };
468077eaf5bSMark Brown
4690c4eae66SAxel Lin module_platform_driver(wm8350_rtc_driver);
470077eaf5bSMark Brown
471077eaf5bSMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
472077eaf5bSMark Brown MODULE_DESCRIPTION("RTC driver for the WM8350");
473077eaf5bSMark Brown MODULE_LICENSE("GPL");
474077eaf5bSMark Brown MODULE_ALIAS("platform:wm8350-rtc");
475