xref: /linux/drivers/rtc/rtc-wm831x.c (revision cdd38c5f1ce4398ec58fec95904b75824daab7b5)
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(&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  */
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