xref: /linux/drivers/rtc/rtc-wm831x.c (revision 498ce4e76b2d5b1dd56b478c967fcfef5b20c1b8)
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 
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(&reg, 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  */
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  */
168*498ce4e7SAlexandre 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;
173*498ce4e7SAlexandre Belloni 	unsigned long time, new_time;
17435c86bf6SMark Brown 	int ret;
17535c86bf6SMark Brown 	int count = 0;
17635c86bf6SMark Brown 
177*498ce4e7SAlexandre Belloni 	time = rtc_tm_to_time64(tm);
178*498ce4e7SAlexandre 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  */
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 
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 
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 
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 
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 
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,
339*498ce4e7SAlexandre 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. */
34735c86bf6SMark Brown static int wm831x_rtc_suspend(struct device *dev)
34835c86bf6SMark Brown {
34935c86bf6SMark Brown 	struct platform_device *pdev = to_platform_device(dev);
35035c86bf6SMark Brown 	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev);
35135c86bf6SMark Brown 	int ret, enable;
35235c86bf6SMark Brown 
35335c86bf6SMark Brown 	if (wm831x_rtc->alarm_enabled && device_may_wakeup(&pdev->dev))
35435c86bf6SMark Brown 		enable = WM831X_RTC_ALM_ENA;
35535c86bf6SMark Brown 	else
35635c86bf6SMark Brown 		enable = 0;
35735c86bf6SMark Brown 
35835c86bf6SMark Brown 	ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
35935c86bf6SMark Brown 			      WM831X_RTC_ALM_ENA, enable);
36035c86bf6SMark Brown 	if (ret != 0)
36135c86bf6SMark Brown 		dev_err(&pdev->dev, "Failed to update RTC alarm: %d\n", ret);
36235c86bf6SMark Brown 
36335c86bf6SMark Brown 	return 0;
36435c86bf6SMark Brown }
36535c86bf6SMark Brown 
36635c86bf6SMark Brown /* Enable the alarm if it should be enabled (in case it was disabled to
36735c86bf6SMark Brown  * prevent use as a wake source).
36835c86bf6SMark Brown  */
36935c86bf6SMark Brown static int wm831x_rtc_resume(struct device *dev)
37035c86bf6SMark Brown {
37135c86bf6SMark Brown 	struct platform_device *pdev = to_platform_device(dev);
37235c86bf6SMark Brown 	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev);
37335c86bf6SMark Brown 	int ret;
37435c86bf6SMark Brown 
37535c86bf6SMark Brown 	if (wm831x_rtc->alarm_enabled) {
37635c86bf6SMark Brown 		ret = wm831x_rtc_start_alarm(wm831x_rtc);
37735c86bf6SMark Brown 		if (ret != 0)
37835c86bf6SMark Brown 			dev_err(&pdev->dev,
37935c86bf6SMark Brown 				"Failed to restart RTC alarm: %d\n", ret);
38035c86bf6SMark Brown 	}
38135c86bf6SMark Brown 
38235c86bf6SMark Brown 	return 0;
38335c86bf6SMark Brown }
38435c86bf6SMark Brown 
38535c86bf6SMark Brown /* Unconditionally disable the alarm */
38635c86bf6SMark Brown static int wm831x_rtc_freeze(struct device *dev)
38735c86bf6SMark Brown {
38835c86bf6SMark Brown 	struct platform_device *pdev = to_platform_device(dev);
38935c86bf6SMark Brown 	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev);
39035c86bf6SMark Brown 	int ret;
39135c86bf6SMark Brown 
39235c86bf6SMark Brown 	ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
39335c86bf6SMark Brown 			      WM831X_RTC_ALM_ENA, 0);
39435c86bf6SMark Brown 	if (ret != 0)
39535c86bf6SMark Brown 		dev_err(&pdev->dev, "Failed to stop RTC alarm: %d\n", ret);
39635c86bf6SMark Brown 
39735c86bf6SMark Brown 	return 0;
39835c86bf6SMark Brown }
39935c86bf6SMark Brown #else
40035c86bf6SMark Brown #define wm831x_rtc_suspend NULL
40135c86bf6SMark Brown #define wm831x_rtc_resume NULL
40235c86bf6SMark Brown #define wm831x_rtc_freeze NULL
40335c86bf6SMark Brown #endif
40435c86bf6SMark Brown 
40535c86bf6SMark Brown static int wm831x_rtc_probe(struct platform_device *pdev)
40635c86bf6SMark Brown {
40735c86bf6SMark Brown 	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
40835c86bf6SMark Brown 	struct wm831x_rtc *wm831x_rtc;
409cd99758bSMark Brown 	int alm_irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "ALM"));
41035c86bf6SMark Brown 	int ret = 0;
41135c86bf6SMark Brown 
4125f85d20dSMark Brown 	wm831x_rtc = devm_kzalloc(&pdev->dev, sizeof(*wm831x_rtc), GFP_KERNEL);
41335c86bf6SMark Brown 	if (wm831x_rtc == NULL)
41435c86bf6SMark Brown 		return -ENOMEM;
41535c86bf6SMark Brown 
41635c86bf6SMark Brown 	platform_set_drvdata(pdev, wm831x_rtc);
41735c86bf6SMark Brown 	wm831x_rtc->wm831x = wm831x;
41835c86bf6SMark Brown 
41935c86bf6SMark Brown 	ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
42035c86bf6SMark Brown 	if (ret < 0) {
42135c86bf6SMark Brown 		dev_err(&pdev->dev, "Failed to read RTC control: %d\n", ret);
422d315bc1bSAlexandre Belloni 		return ret;
42335c86bf6SMark Brown 	}
42435c86bf6SMark Brown 	if (ret & WM831X_RTC_ALM_ENA)
42535c86bf6SMark Brown 		wm831x_rtc->alarm_enabled = 1;
42635c86bf6SMark Brown 
42735c86bf6SMark Brown 	device_init_wakeup(&pdev->dev, 1);
42835c86bf6SMark Brown 
429b9a1d801SAlexandre Belloni 	wm831x_rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
430b9a1d801SAlexandre Belloni 	if (IS_ERR(wm831x_rtc->rtc))
431b9a1d801SAlexandre Belloni 		return PTR_ERR(wm831x_rtc->rtc);
432b9a1d801SAlexandre Belloni 
433b9a1d801SAlexandre Belloni 	wm831x_rtc->rtc->ops = &wm831x_rtc_ops;
434b9a1d801SAlexandre Belloni 	wm831x_rtc->rtc->range_max = U32_MAX;
435b9a1d801SAlexandre Belloni 
436b9a1d801SAlexandre Belloni 	ret = rtc_register_device(wm831x_rtc->rtc);
437b9a1d801SAlexandre Belloni 	if (ret)
438b9a1d801SAlexandre Belloni 		return ret;
43935c86bf6SMark Brown 
440fd5231ceSJingoo Han 	ret = devm_request_threaded_irq(&pdev->dev, alm_irq, NULL,
441fd5231ceSJingoo Han 				wm831x_alm_irq,
4425815e5d3SMark Brown 				IRQF_TRIGGER_RISING, "RTC alarm",
44335c86bf6SMark Brown 				wm831x_rtc);
44435c86bf6SMark Brown 	if (ret != 0) {
44535c86bf6SMark Brown 		dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
44635c86bf6SMark Brown 			alm_irq, ret);
44735c86bf6SMark Brown 	}
44835c86bf6SMark Brown 
4499dccf55fSMark Brown 	wm831x_rtc_add_randomness(wm831x);
4509dccf55fSMark Brown 
45135c86bf6SMark Brown 	return 0;
45235c86bf6SMark Brown }
45335c86bf6SMark Brown 
45447145210SAlexey Dobriyan static const struct dev_pm_ops wm831x_rtc_pm_ops = {
45535c86bf6SMark Brown 	.suspend = wm831x_rtc_suspend,
45635c86bf6SMark Brown 	.resume = wm831x_rtc_resume,
45735c86bf6SMark Brown 
45835c86bf6SMark Brown 	.freeze = wm831x_rtc_freeze,
45935c86bf6SMark Brown 	.thaw = wm831x_rtc_resume,
46035c86bf6SMark Brown 	.restore = wm831x_rtc_resume,
46135c86bf6SMark Brown 
46235c86bf6SMark Brown 	.poweroff = wm831x_rtc_suspend,
46335c86bf6SMark Brown };
46435c86bf6SMark Brown 
46535c86bf6SMark Brown static struct platform_driver wm831x_rtc_driver = {
46635c86bf6SMark Brown 	.probe = wm831x_rtc_probe,
46735c86bf6SMark Brown 	.driver = {
46835c86bf6SMark Brown 		.name = "wm831x-rtc",
46935c86bf6SMark Brown 		.pm = &wm831x_rtc_pm_ops,
47035c86bf6SMark Brown 	},
47135c86bf6SMark Brown };
47235c86bf6SMark Brown 
4730c4eae66SAxel Lin module_platform_driver(wm831x_rtc_driver);
47435c86bf6SMark Brown 
47535c86bf6SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
47635c86bf6SMark Brown MODULE_DESCRIPTION("RTC driver for the WM831x series PMICs");
47735c86bf6SMark Brown MODULE_LICENSE("GPL");
47835c86bf6SMark Brown MODULE_ALIAS("platform:wm831x-rtc");
479