1e07fa839SAlexandre Belloni // SPDX-License-Identifier: GPL-2.0+
235c86bf6SMark Brown /*
335c86bf6SMark Brown * Real Time Clock driver for Wolfson Microelectronics WM831x
435c86bf6SMark Brown *
535c86bf6SMark Brown * Copyright (C) 2009 Wolfson Microelectronics PLC.
635c86bf6SMark Brown *
735c86bf6SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
835c86bf6SMark Brown *
935c86bf6SMark Brown */
1035c86bf6SMark Brown
1135c86bf6SMark Brown #include <linux/module.h>
1235c86bf6SMark Brown #include <linux/kernel.h>
1335c86bf6SMark Brown #include <linux/time.h>
1435c86bf6SMark Brown #include <linux/rtc.h>
155a0e3ad6STejun Heo #include <linux/slab.h>
1635c86bf6SMark Brown #include <linux/bcd.h>
1735c86bf6SMark Brown #include <linux/interrupt.h>
1835c86bf6SMark Brown #include <linux/ioctl.h>
1935c86bf6SMark Brown #include <linux/completion.h>
2035c86bf6SMark Brown #include <linux/mfd/wm831x/core.h>
2135c86bf6SMark Brown #include <linux/delay.h>
2235c86bf6SMark Brown #include <linux/platform_device.h>
239dccf55fSMark Brown #include <linux/random.h>
2435c86bf6SMark Brown
2535c86bf6SMark Brown /*
2635c86bf6SMark Brown * R16416 (0x4020) - RTC Write Counter
2735c86bf6SMark Brown */
2835c86bf6SMark Brown #define WM831X_RTC_WR_CNT_MASK 0xFFFF /* RTC_WR_CNT - [15:0] */
2935c86bf6SMark Brown #define WM831X_RTC_WR_CNT_SHIFT 0 /* RTC_WR_CNT - [15:0] */
3035c86bf6SMark Brown #define WM831X_RTC_WR_CNT_WIDTH 16 /* RTC_WR_CNT - [15:0] */
3135c86bf6SMark Brown
3235c86bf6SMark Brown /*
3335c86bf6SMark Brown * R16417 (0x4021) - RTC Time 1
3435c86bf6SMark Brown */
3535c86bf6SMark Brown #define WM831X_RTC_TIME_MASK 0xFFFF /* RTC_TIME - [15:0] */
3635c86bf6SMark Brown #define WM831X_RTC_TIME_SHIFT 0 /* RTC_TIME - [15:0] */
3735c86bf6SMark Brown #define WM831X_RTC_TIME_WIDTH 16 /* RTC_TIME - [15:0] */
3835c86bf6SMark Brown
3935c86bf6SMark Brown /*
4035c86bf6SMark Brown * R16418 (0x4022) - RTC Time 2
4135c86bf6SMark Brown */
4235c86bf6SMark Brown #define WM831X_RTC_TIME_MASK 0xFFFF /* RTC_TIME - [15:0] */
4335c86bf6SMark Brown #define WM831X_RTC_TIME_SHIFT 0 /* RTC_TIME - [15:0] */
4435c86bf6SMark Brown #define WM831X_RTC_TIME_WIDTH 16 /* RTC_TIME - [15:0] */
4535c86bf6SMark Brown
4635c86bf6SMark Brown /*
4735c86bf6SMark Brown * R16419 (0x4023) - RTC Alarm 1
4835c86bf6SMark Brown */
4935c86bf6SMark Brown #define WM831X_RTC_ALM_MASK 0xFFFF /* RTC_ALM - [15:0] */
5035c86bf6SMark Brown #define WM831X_RTC_ALM_SHIFT 0 /* RTC_ALM - [15:0] */
5135c86bf6SMark Brown #define WM831X_RTC_ALM_WIDTH 16 /* RTC_ALM - [15:0] */
5235c86bf6SMark Brown
5335c86bf6SMark Brown /*
5435c86bf6SMark Brown * R16420 (0x4024) - RTC Alarm 2
5535c86bf6SMark Brown */
5635c86bf6SMark Brown #define WM831X_RTC_ALM_MASK 0xFFFF /* RTC_ALM - [15:0] */
5735c86bf6SMark Brown #define WM831X_RTC_ALM_SHIFT 0 /* RTC_ALM - [15:0] */
5835c86bf6SMark Brown #define WM831X_RTC_ALM_WIDTH 16 /* RTC_ALM - [15:0] */
5935c86bf6SMark Brown
6035c86bf6SMark Brown /*
6135c86bf6SMark Brown * R16421 (0x4025) - RTC Control
6235c86bf6SMark Brown */
6335c86bf6SMark Brown #define WM831X_RTC_VALID 0x8000 /* RTC_VALID */
6435c86bf6SMark Brown #define WM831X_RTC_VALID_MASK 0x8000 /* RTC_VALID */
6535c86bf6SMark Brown #define WM831X_RTC_VALID_SHIFT 15 /* RTC_VALID */
6635c86bf6SMark Brown #define WM831X_RTC_VALID_WIDTH 1 /* RTC_VALID */
6735c86bf6SMark Brown #define WM831X_RTC_SYNC_BUSY 0x4000 /* RTC_SYNC_BUSY */
6835c86bf6SMark Brown #define WM831X_RTC_SYNC_BUSY_MASK 0x4000 /* RTC_SYNC_BUSY */
6935c86bf6SMark Brown #define WM831X_RTC_SYNC_BUSY_SHIFT 14 /* RTC_SYNC_BUSY */
7035c86bf6SMark Brown #define WM831X_RTC_SYNC_BUSY_WIDTH 1 /* RTC_SYNC_BUSY */
7135c86bf6SMark Brown #define WM831X_RTC_ALM_ENA 0x0400 /* RTC_ALM_ENA */
7235c86bf6SMark Brown #define WM831X_RTC_ALM_ENA_MASK 0x0400 /* RTC_ALM_ENA */
7335c86bf6SMark Brown #define WM831X_RTC_ALM_ENA_SHIFT 10 /* RTC_ALM_ENA */
7435c86bf6SMark Brown #define WM831X_RTC_ALM_ENA_WIDTH 1 /* RTC_ALM_ENA */
7535c86bf6SMark Brown #define WM831X_RTC_PINT_FREQ_MASK 0x0070 /* RTC_PINT_FREQ - [6:4] */
7635c86bf6SMark Brown #define WM831X_RTC_PINT_FREQ_SHIFT 4 /* RTC_PINT_FREQ - [6:4] */
7735c86bf6SMark Brown #define WM831X_RTC_PINT_FREQ_WIDTH 3 /* RTC_PINT_FREQ - [6:4] */
7835c86bf6SMark Brown
7935c86bf6SMark Brown /*
8035c86bf6SMark Brown * R16422 (0x4026) - RTC Trim
8135c86bf6SMark Brown */
8235c86bf6SMark Brown #define WM831X_RTC_TRIM_MASK 0x03FF /* RTC_TRIM - [9:0] */
8335c86bf6SMark Brown #define WM831X_RTC_TRIM_SHIFT 0 /* RTC_TRIM - [9:0] */
8435c86bf6SMark Brown #define WM831X_RTC_TRIM_WIDTH 10 /* RTC_TRIM - [9:0] */
8535c86bf6SMark Brown
8635c86bf6SMark Brown #define WM831X_SET_TIME_RETRIES 5
8735c86bf6SMark Brown #define WM831X_GET_TIME_RETRIES 5
8835c86bf6SMark Brown
8935c86bf6SMark Brown struct wm831x_rtc {
9035c86bf6SMark Brown struct wm831x *wm831x;
9135c86bf6SMark Brown struct rtc_device *rtc;
9235c86bf6SMark Brown unsigned int alarm_enabled:1;
9335c86bf6SMark Brown };
9435c86bf6SMark Brown
wm831x_rtc_add_randomness(struct wm831x * wm831x)959dccf55fSMark Brown static void wm831x_rtc_add_randomness(struct wm831x *wm831x)
969dccf55fSMark Brown {
979dccf55fSMark Brown int ret;
989dccf55fSMark Brown u16 reg;
999dccf55fSMark Brown
1009dccf55fSMark Brown /*
1019dccf55fSMark Brown * The write counter contains a pseudo-random number which is
1029dccf55fSMark Brown * regenerated every time we set the RTC so it should be a
1039dccf55fSMark Brown * useful per-system source of entropy.
1049dccf55fSMark Brown */
1059dccf55fSMark Brown ret = wm831x_reg_read(wm831x, WM831X_RTC_WRITE_COUNTER);
1069dccf55fSMark Brown if (ret >= 0) {
1079dccf55fSMark Brown reg = ret;
1089dccf55fSMark Brown add_device_randomness(®, sizeof(reg));
1099dccf55fSMark Brown } else {
1109dccf55fSMark Brown dev_warn(wm831x->dev, "Failed to read RTC write counter: %d\n",
1119dccf55fSMark Brown ret);
1129dccf55fSMark Brown }
1139dccf55fSMark Brown }
1149dccf55fSMark Brown
11535c86bf6SMark Brown /*
11635c86bf6SMark Brown * Read current time and date in RTC
11735c86bf6SMark Brown */
wm831x_rtc_readtime(struct device * dev,struct rtc_time * tm)11835c86bf6SMark Brown static int wm831x_rtc_readtime(struct device *dev, struct rtc_time *tm)
11935c86bf6SMark Brown {
12035c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
12135c86bf6SMark Brown struct wm831x *wm831x = wm831x_rtc->wm831x;
12235c86bf6SMark Brown u16 time1[2], time2[2];
12335c86bf6SMark Brown int ret;
12435c86bf6SMark Brown int count = 0;
12535c86bf6SMark Brown
12635c86bf6SMark Brown /* Has the RTC been programmed? */
12735c86bf6SMark Brown ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
12835c86bf6SMark Brown if (ret < 0) {
12935c86bf6SMark Brown dev_err(dev, "Failed to read RTC control: %d\n", ret);
13035c86bf6SMark Brown return ret;
13135c86bf6SMark Brown }
13235c86bf6SMark Brown if (!(ret & WM831X_RTC_VALID)) {
13335c86bf6SMark Brown dev_dbg(dev, "RTC not yet configured\n");
13435c86bf6SMark Brown return -EINVAL;
13535c86bf6SMark Brown }
13635c86bf6SMark Brown
13735c86bf6SMark Brown /* Read twice to make sure we don't read a corrupt, partially
13835c86bf6SMark Brown * incremented, value.
13935c86bf6SMark Brown */
14035c86bf6SMark Brown do {
14135c86bf6SMark Brown ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1,
14235c86bf6SMark Brown 2, time1);
14335c86bf6SMark Brown if (ret != 0)
14435c86bf6SMark Brown continue;
14535c86bf6SMark Brown
14635c86bf6SMark Brown ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1,
14735c86bf6SMark Brown 2, time2);
14835c86bf6SMark Brown if (ret != 0)
14935c86bf6SMark Brown continue;
15035c86bf6SMark Brown
15135c86bf6SMark Brown if (memcmp(time1, time2, sizeof(time1)) == 0) {
15235c86bf6SMark Brown u32 time = (time1[0] << 16) | time1[1];
15335c86bf6SMark Brown
15423992449SAlexandre Belloni rtc_time64_to_tm(time, tm);
155ab62670eSAlexandre Belloni return 0;
15635c86bf6SMark Brown }
15735c86bf6SMark Brown
15835c86bf6SMark Brown } while (++count < WM831X_GET_TIME_RETRIES);
15935c86bf6SMark Brown
16035c86bf6SMark Brown dev_err(dev, "Timed out reading current time\n");
16135c86bf6SMark Brown
16235c86bf6SMark Brown return -EIO;
16335c86bf6SMark Brown }
16435c86bf6SMark Brown
16535c86bf6SMark Brown /*
16635c86bf6SMark Brown * Set current time and date in RTC
16735c86bf6SMark Brown */
wm831x_rtc_settime(struct device * dev,struct rtc_time * tm)168498ce4e7SAlexandre Belloni static int wm831x_rtc_settime(struct device *dev, struct rtc_time *tm)
16935c86bf6SMark Brown {
17035c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
17135c86bf6SMark Brown struct wm831x *wm831x = wm831x_rtc->wm831x;
17235c86bf6SMark Brown struct rtc_time new_tm;
173498ce4e7SAlexandre Belloni unsigned long time, new_time;
17435c86bf6SMark Brown int ret;
17535c86bf6SMark Brown int count = 0;
17635c86bf6SMark Brown
177498ce4e7SAlexandre Belloni time = rtc_tm_to_time64(tm);
178498ce4e7SAlexandre Belloni
17935c86bf6SMark Brown ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_1,
18035c86bf6SMark Brown (time >> 16) & 0xffff);
18135c86bf6SMark Brown if (ret < 0) {
18235c86bf6SMark Brown dev_err(dev, "Failed to write TIME_1: %d\n", ret);
18335c86bf6SMark Brown return ret;
18435c86bf6SMark Brown }
18535c86bf6SMark Brown
18635c86bf6SMark Brown ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_2, time & 0xffff);
18735c86bf6SMark Brown if (ret < 0) {
18835c86bf6SMark Brown dev_err(dev, "Failed to write TIME_2: %d\n", ret);
18935c86bf6SMark Brown return ret;
19035c86bf6SMark Brown }
19135c86bf6SMark Brown
19235c86bf6SMark Brown /* Wait for the update to complete - should happen first time
19335c86bf6SMark Brown * round but be conservative.
19435c86bf6SMark Brown */
19535c86bf6SMark Brown do {
19635c86bf6SMark Brown msleep(1);
19735c86bf6SMark Brown
19835c86bf6SMark Brown ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
19935c86bf6SMark Brown if (ret < 0)
20035c86bf6SMark Brown ret = WM831X_RTC_SYNC_BUSY;
20135c86bf6SMark Brown } while (!(ret & WM831X_RTC_SYNC_BUSY) &&
20235c86bf6SMark Brown ++count < WM831X_SET_TIME_RETRIES);
20335c86bf6SMark Brown
20435c86bf6SMark Brown if (ret & WM831X_RTC_SYNC_BUSY) {
20535c86bf6SMark Brown dev_err(dev, "Timed out writing RTC update\n");
20635c86bf6SMark Brown return -EIO;
20735c86bf6SMark Brown }
20835c86bf6SMark Brown
20935c86bf6SMark Brown /* Check that the update was accepted; security features may
21035c86bf6SMark Brown * have caused the update to be ignored.
21135c86bf6SMark Brown */
21235c86bf6SMark Brown ret = wm831x_rtc_readtime(dev, &new_tm);
21335c86bf6SMark Brown if (ret < 0)
21435c86bf6SMark Brown return ret;
21535c86bf6SMark Brown
21623992449SAlexandre Belloni new_time = rtc_tm_to_time64(&new_tm);
21735c86bf6SMark Brown
21835c86bf6SMark Brown /* Allow a second of change in case of tick */
21935c86bf6SMark Brown if (new_time - time > 1) {
22035c86bf6SMark Brown dev_err(dev, "RTC update not permitted by hardware\n");
22135c86bf6SMark Brown return -EPERM;
22235c86bf6SMark Brown }
22335c86bf6SMark Brown
22435c86bf6SMark Brown return 0;
22535c86bf6SMark Brown }
22635c86bf6SMark Brown
22735c86bf6SMark Brown /*
22835c86bf6SMark Brown * Read alarm time and date in RTC
22935c86bf6SMark Brown */
wm831x_rtc_readalarm(struct device * dev,struct rtc_wkalrm * alrm)23035c86bf6SMark Brown static int wm831x_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
23135c86bf6SMark Brown {
23235c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
23335c86bf6SMark Brown int ret;
23435c86bf6SMark Brown u16 data[2];
23535c86bf6SMark Brown u32 time;
23635c86bf6SMark Brown
23735c86bf6SMark Brown ret = wm831x_bulk_read(wm831x_rtc->wm831x, WM831X_RTC_ALARM_1,
23835c86bf6SMark Brown 2, data);
23935c86bf6SMark Brown if (ret != 0) {
24035c86bf6SMark Brown dev_err(dev, "Failed to read alarm time: %d\n", ret);
24135c86bf6SMark Brown return ret;
24235c86bf6SMark Brown }
24335c86bf6SMark Brown
24435c86bf6SMark Brown time = (data[0] << 16) | data[1];
24535c86bf6SMark Brown
24623992449SAlexandre Belloni rtc_time64_to_tm(time, &alrm->time);
24735c86bf6SMark Brown
24835c86bf6SMark Brown ret = wm831x_reg_read(wm831x_rtc->wm831x, WM831X_RTC_CONTROL);
24935c86bf6SMark Brown if (ret < 0) {
25035c86bf6SMark Brown dev_err(dev, "Failed to read RTC control: %d\n", ret);
25135c86bf6SMark Brown return ret;
25235c86bf6SMark Brown }
25335c86bf6SMark Brown
25435c86bf6SMark Brown if (ret & WM831X_RTC_ALM_ENA)
25535c86bf6SMark Brown alrm->enabled = 1;
25635c86bf6SMark Brown else
25735c86bf6SMark Brown alrm->enabled = 0;
25835c86bf6SMark Brown
25935c86bf6SMark Brown return 0;
26035c86bf6SMark Brown }
26135c86bf6SMark Brown
wm831x_rtc_stop_alarm(struct wm831x_rtc * wm831x_rtc)26235c86bf6SMark Brown static int wm831x_rtc_stop_alarm(struct wm831x_rtc *wm831x_rtc)
26335c86bf6SMark Brown {
26435c86bf6SMark Brown wm831x_rtc->alarm_enabled = 0;
26535c86bf6SMark Brown
26635c86bf6SMark Brown return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
26735c86bf6SMark Brown WM831X_RTC_ALM_ENA, 0);
26835c86bf6SMark Brown }
26935c86bf6SMark Brown
wm831x_rtc_start_alarm(struct wm831x_rtc * wm831x_rtc)27035c86bf6SMark Brown static int wm831x_rtc_start_alarm(struct wm831x_rtc *wm831x_rtc)
27135c86bf6SMark Brown {
27235c86bf6SMark Brown wm831x_rtc->alarm_enabled = 1;
27335c86bf6SMark Brown
27435c86bf6SMark Brown return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
27535c86bf6SMark Brown WM831X_RTC_ALM_ENA, WM831X_RTC_ALM_ENA);
27635c86bf6SMark Brown }
27735c86bf6SMark Brown
wm831x_rtc_setalarm(struct device * dev,struct rtc_wkalrm * alrm)27835c86bf6SMark Brown static int wm831x_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
27935c86bf6SMark Brown {
28035c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
28135c86bf6SMark Brown struct wm831x *wm831x = wm831x_rtc->wm831x;
28235c86bf6SMark Brown int ret;
28335c86bf6SMark Brown unsigned long time;
28435c86bf6SMark Brown
28523992449SAlexandre Belloni time = rtc_tm_to_time64(&alrm->time);
28635c86bf6SMark Brown
28735c86bf6SMark Brown ret = wm831x_rtc_stop_alarm(wm831x_rtc);
28835c86bf6SMark Brown if (ret < 0) {
28935c86bf6SMark Brown dev_err(dev, "Failed to stop alarm: %d\n", ret);
29035c86bf6SMark Brown return ret;
29135c86bf6SMark Brown }
29235c86bf6SMark Brown
29335c86bf6SMark Brown ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_1,
29435c86bf6SMark Brown (time >> 16) & 0xffff);
29535c86bf6SMark Brown if (ret < 0) {
29635c86bf6SMark Brown dev_err(dev, "Failed to write ALARM_1: %d\n", ret);
29735c86bf6SMark Brown return ret;
29835c86bf6SMark Brown }
29935c86bf6SMark Brown
30035c86bf6SMark Brown ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_2, time & 0xffff);
30135c86bf6SMark Brown if (ret < 0) {
30235c86bf6SMark Brown dev_err(dev, "Failed to write ALARM_2: %d\n", ret);
30335c86bf6SMark Brown return ret;
30435c86bf6SMark Brown }
30535c86bf6SMark Brown
30635c86bf6SMark Brown if (alrm->enabled) {
30735c86bf6SMark Brown ret = wm831x_rtc_start_alarm(wm831x_rtc);
30835c86bf6SMark Brown if (ret < 0) {
30935c86bf6SMark Brown dev_err(dev, "Failed to start alarm: %d\n", ret);
31035c86bf6SMark Brown return ret;
31135c86bf6SMark Brown }
31235c86bf6SMark Brown }
31335c86bf6SMark Brown
31435c86bf6SMark Brown return 0;
31535c86bf6SMark Brown }
31635c86bf6SMark Brown
wm831x_rtc_alarm_irq_enable(struct device * dev,unsigned int enabled)31735c86bf6SMark Brown static int wm831x_rtc_alarm_irq_enable(struct device *dev,
31835c86bf6SMark Brown unsigned int enabled)
31935c86bf6SMark Brown {
32035c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
32135c86bf6SMark Brown
32235c86bf6SMark Brown if (enabled)
32335c86bf6SMark Brown return wm831x_rtc_start_alarm(wm831x_rtc);
32435c86bf6SMark Brown else
32535c86bf6SMark Brown return wm831x_rtc_stop_alarm(wm831x_rtc);
32635c86bf6SMark Brown }
32735c86bf6SMark Brown
wm831x_alm_irq(int irq,void * data)32835c86bf6SMark Brown static irqreturn_t wm831x_alm_irq(int irq, void *data)
32935c86bf6SMark Brown {
33035c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc = data;
33135c86bf6SMark Brown
33235c86bf6SMark Brown rtc_update_irq(wm831x_rtc->rtc, 1, RTC_IRQF | RTC_AF);
33335c86bf6SMark Brown
33435c86bf6SMark Brown return IRQ_HANDLED;
33535c86bf6SMark Brown }
33635c86bf6SMark Brown
33735c86bf6SMark Brown static const struct rtc_class_ops wm831x_rtc_ops = {
33835c86bf6SMark Brown .read_time = wm831x_rtc_readtime,
339498ce4e7SAlexandre Belloni .set_time = wm831x_rtc_settime,
34035c86bf6SMark Brown .read_alarm = wm831x_rtc_readalarm,
34135c86bf6SMark Brown .set_alarm = wm831x_rtc_setalarm,
34235c86bf6SMark Brown .alarm_irq_enable = wm831x_rtc_alarm_irq_enable,
34335c86bf6SMark Brown };
34435c86bf6SMark Brown
34535c86bf6SMark Brown #ifdef CONFIG_PM
34635c86bf6SMark Brown /* Turn off the alarm if it should not be a wake source. */
wm831x_rtc_suspend(struct device * dev)34735c86bf6SMark Brown static int wm831x_rtc_suspend(struct device *dev)
34835c86bf6SMark Brown {
349527bd754SKefeng Wang struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
35035c86bf6SMark Brown int ret, enable;
35135c86bf6SMark Brown
352527bd754SKefeng Wang if (wm831x_rtc->alarm_enabled && device_may_wakeup(dev))
35335c86bf6SMark Brown enable = WM831X_RTC_ALM_ENA;
35435c86bf6SMark Brown else
35535c86bf6SMark Brown enable = 0;
35635c86bf6SMark Brown
35735c86bf6SMark Brown ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
35835c86bf6SMark Brown WM831X_RTC_ALM_ENA, enable);
35935c86bf6SMark Brown if (ret != 0)
360527bd754SKefeng Wang dev_err(dev, "Failed to update RTC alarm: %d\n", ret);
36135c86bf6SMark Brown
36235c86bf6SMark Brown return 0;
36335c86bf6SMark Brown }
36435c86bf6SMark Brown
36535c86bf6SMark Brown /* Enable the alarm if it should be enabled (in case it was disabled to
36635c86bf6SMark Brown * prevent use as a wake source).
36735c86bf6SMark Brown */
wm831x_rtc_resume(struct device * dev)36835c86bf6SMark Brown static int wm831x_rtc_resume(struct device *dev)
36935c86bf6SMark Brown {
370527bd754SKefeng Wang struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
37135c86bf6SMark Brown int ret;
37235c86bf6SMark Brown
37335c86bf6SMark Brown if (wm831x_rtc->alarm_enabled) {
37435c86bf6SMark Brown ret = wm831x_rtc_start_alarm(wm831x_rtc);
37535c86bf6SMark Brown if (ret != 0)
376527bd754SKefeng Wang dev_err(dev, "Failed to restart RTC alarm: %d\n", ret);
37735c86bf6SMark Brown }
37835c86bf6SMark Brown
37935c86bf6SMark Brown return 0;
38035c86bf6SMark Brown }
38135c86bf6SMark Brown
38235c86bf6SMark Brown /* Unconditionally disable the alarm */
wm831x_rtc_freeze(struct device * dev)38335c86bf6SMark Brown static int wm831x_rtc_freeze(struct device *dev)
38435c86bf6SMark Brown {
385527bd754SKefeng Wang struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
38635c86bf6SMark Brown int ret;
38735c86bf6SMark Brown
38835c86bf6SMark Brown ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
38935c86bf6SMark Brown WM831X_RTC_ALM_ENA, 0);
39035c86bf6SMark Brown if (ret != 0)
391527bd754SKefeng Wang dev_err(dev, "Failed to stop RTC alarm: %d\n", ret);
39235c86bf6SMark Brown
39335c86bf6SMark Brown return 0;
39435c86bf6SMark Brown }
39535c86bf6SMark Brown #else
39635c86bf6SMark Brown #define wm831x_rtc_suspend NULL
39735c86bf6SMark Brown #define wm831x_rtc_resume NULL
39835c86bf6SMark Brown #define wm831x_rtc_freeze NULL
39935c86bf6SMark Brown #endif
40035c86bf6SMark Brown
wm831x_rtc_probe(struct platform_device * pdev)40135c86bf6SMark Brown static int wm831x_rtc_probe(struct platform_device *pdev)
40235c86bf6SMark Brown {
40335c86bf6SMark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
40435c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc;
405cd99758bSMark Brown int alm_irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "ALM"));
40635c86bf6SMark Brown int ret = 0;
40735c86bf6SMark Brown
4085f85d20dSMark Brown wm831x_rtc = devm_kzalloc(&pdev->dev, sizeof(*wm831x_rtc), GFP_KERNEL);
40935c86bf6SMark Brown if (wm831x_rtc == NULL)
41035c86bf6SMark Brown return -ENOMEM;
41135c86bf6SMark Brown
41235c86bf6SMark Brown platform_set_drvdata(pdev, wm831x_rtc);
41335c86bf6SMark Brown wm831x_rtc->wm831x = wm831x;
41435c86bf6SMark Brown
41535c86bf6SMark Brown ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
41635c86bf6SMark Brown if (ret < 0) {
41735c86bf6SMark Brown dev_err(&pdev->dev, "Failed to read RTC control: %d\n", ret);
418d315bc1bSAlexandre Belloni return ret;
41935c86bf6SMark Brown }
42035c86bf6SMark Brown if (ret & WM831X_RTC_ALM_ENA)
42135c86bf6SMark Brown wm831x_rtc->alarm_enabled = 1;
42235c86bf6SMark Brown
42335c86bf6SMark Brown device_init_wakeup(&pdev->dev, 1);
42435c86bf6SMark Brown
425b9a1d801SAlexandre Belloni wm831x_rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
426b9a1d801SAlexandre Belloni if (IS_ERR(wm831x_rtc->rtc))
427b9a1d801SAlexandre Belloni return PTR_ERR(wm831x_rtc->rtc);
428b9a1d801SAlexandre Belloni
429b9a1d801SAlexandre Belloni wm831x_rtc->rtc->ops = &wm831x_rtc_ops;
430b9a1d801SAlexandre Belloni wm831x_rtc->rtc->range_max = U32_MAX;
431b9a1d801SAlexandre Belloni
432*fdcfd854SBartosz Golaszewski ret = devm_rtc_register_device(wm831x_rtc->rtc);
433b9a1d801SAlexandre Belloni if (ret)
434b9a1d801SAlexandre Belloni return ret;
43535c86bf6SMark Brown
436fd5231ceSJingoo Han ret = devm_request_threaded_irq(&pdev->dev, alm_irq, NULL,
437fd5231ceSJingoo Han wm831x_alm_irq,
438f0162d21SHariprasad Kelam IRQF_TRIGGER_RISING | IRQF_ONESHOT,
439f0162d21SHariprasad Kelam "RTC alarm",
44035c86bf6SMark Brown wm831x_rtc);
44135c86bf6SMark Brown if (ret != 0) {
44235c86bf6SMark Brown dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
44335c86bf6SMark Brown alm_irq, ret);
44435c86bf6SMark Brown }
44535c86bf6SMark Brown
4469dccf55fSMark Brown wm831x_rtc_add_randomness(wm831x);
4479dccf55fSMark Brown
44835c86bf6SMark Brown return 0;
44935c86bf6SMark Brown }
45035c86bf6SMark Brown
45147145210SAlexey Dobriyan static const struct dev_pm_ops wm831x_rtc_pm_ops = {
45235c86bf6SMark Brown .suspend = wm831x_rtc_suspend,
45335c86bf6SMark Brown .resume = wm831x_rtc_resume,
45435c86bf6SMark Brown
45535c86bf6SMark Brown .freeze = wm831x_rtc_freeze,
45635c86bf6SMark Brown .thaw = wm831x_rtc_resume,
45735c86bf6SMark Brown .restore = wm831x_rtc_resume,
45835c86bf6SMark Brown
45935c86bf6SMark Brown .poweroff = wm831x_rtc_suspend,
46035c86bf6SMark Brown };
46135c86bf6SMark Brown
46235c86bf6SMark Brown static struct platform_driver wm831x_rtc_driver = {
46335c86bf6SMark Brown .probe = wm831x_rtc_probe,
46435c86bf6SMark Brown .driver = {
46535c86bf6SMark Brown .name = "wm831x-rtc",
46635c86bf6SMark Brown .pm = &wm831x_rtc_pm_ops,
46735c86bf6SMark Brown },
46835c86bf6SMark Brown };
46935c86bf6SMark Brown
4700c4eae66SAxel Lin module_platform_driver(wm831x_rtc_driver);
47135c86bf6SMark Brown
47235c86bf6SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
47335c86bf6SMark Brown MODULE_DESCRIPTION("RTC driver for the WM831x series PMICs");
47435c86bf6SMark Brown MODULE_LICENSE("GPL");
47535c86bf6SMark Brown MODULE_ALIAS("platform:wm831x-rtc");
476