135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/*
235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown *	Real Time Clock driver for Wolfson Microelectronics WM831x
335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown *
435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown *	Copyright (C) 2009 Wolfson Microelectronics PLC.
535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown *
635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown *  Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown *
835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown *  This program is free software; you can redistribute  it and/or modify it
935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown *  under  the terms of  the GNU General  Public License as published by the
1035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown *  Free Software Foundation;  either version 2 of the  License, or (at your
1135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown *  option) any later version.
1235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown *
1335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */
1435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
1535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/module.h>
1635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/kernel.h>
1735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/time.h>
1835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/rtc.h>
195a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
2035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/bcd.h>
2135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/interrupt.h>
2235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/ioctl.h>
2335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/completion.h>
2435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/mfd/wm831x/core.h>
2535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/delay.h>
2635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/platform_device.h>
279dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown#include <linux/random.h>
2835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
2935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/*
3035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * R16416 (0x4020) - RTC Write Counter
3135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */
3235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_WR_CNT_MASK                  0xFFFF  /* RTC_WR_CNT - [15:0] */
3335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_WR_CNT_SHIFT                      0  /* RTC_WR_CNT - [15:0] */
3435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_WR_CNT_WIDTH                     16  /* RTC_WR_CNT - [15:0] */
3535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
3635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/*
3735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * R16417 (0x4021) - RTC Time 1
3835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */
3935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TIME_MASK                    0xFFFF  /* RTC_TIME - [15:0] */
4035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TIME_SHIFT                        0  /* RTC_TIME - [15:0] */
4135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TIME_WIDTH                       16  /* RTC_TIME - [15:0] */
4235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
4335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/*
4435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * R16418 (0x4022) - RTC Time 2
4535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */
4635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TIME_MASK                    0xFFFF  /* RTC_TIME - [15:0] */
4735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TIME_SHIFT                        0  /* RTC_TIME - [15:0] */
4835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TIME_WIDTH                       16  /* RTC_TIME - [15:0] */
4935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
5035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/*
5135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * R16419 (0x4023) - RTC Alarm 1
5235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */
5335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_MASK                     0xFFFF  /* RTC_ALM - [15:0] */
5435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_SHIFT                         0  /* RTC_ALM - [15:0] */
5535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_WIDTH                        16  /* RTC_ALM - [15:0] */
5635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
5735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/*
5835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * R16420 (0x4024) - RTC Alarm 2
5935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */
6035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_MASK                     0xFFFF  /* RTC_ALM - [15:0] */
6135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_SHIFT                         0  /* RTC_ALM - [15:0] */
6235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_WIDTH                        16  /* RTC_ALM - [15:0] */
6335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
6435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/*
6535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * R16421 (0x4025) - RTC Control
6635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */
6735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_VALID                        0x8000  /* RTC_VALID */
6835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_VALID_MASK                   0x8000  /* RTC_VALID */
6935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_VALID_SHIFT                      15  /* RTC_VALID */
7035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_VALID_WIDTH                       1  /* RTC_VALID */
7135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_SYNC_BUSY                    0x4000  /* RTC_SYNC_BUSY */
7235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_SYNC_BUSY_MASK               0x4000  /* RTC_SYNC_BUSY */
7335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_SYNC_BUSY_SHIFT                  14  /* RTC_SYNC_BUSY */
7435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_SYNC_BUSY_WIDTH                   1  /* RTC_SYNC_BUSY */
7535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_ENA                      0x0400  /* RTC_ALM_ENA */
7635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_ENA_MASK                 0x0400  /* RTC_ALM_ENA */
7735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_ENA_SHIFT                    10  /* RTC_ALM_ENA */
7835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_ENA_WIDTH                     1  /* RTC_ALM_ENA */
7935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_PINT_FREQ_MASK               0x0070  /* RTC_PINT_FREQ - [6:4] */
8035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_PINT_FREQ_SHIFT                   4  /* RTC_PINT_FREQ - [6:4] */
8135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_PINT_FREQ_WIDTH                   3  /* RTC_PINT_FREQ - [6:4] */
8235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
8335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/*
8435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * R16422 (0x4026) - RTC Trim
8535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */
8635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TRIM_MASK                    0x03FF  /* RTC_TRIM - [9:0] */
8735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TRIM_SHIFT                        0  /* RTC_TRIM - [9:0] */
8835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TRIM_WIDTH                       10  /* RTC_TRIM - [9:0] */
8935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
9035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_SET_TIME_RETRIES	5
9135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_GET_TIME_RETRIES	5
9235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
9335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstruct wm831x_rtc {
9435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	struct wm831x *wm831x;
9535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	struct rtc_device *rtc;
9635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	unsigned int alarm_enabled:1;
9735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown};
9835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
999dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brownstatic void wm831x_rtc_add_randomness(struct wm831x *wm831x)
1009dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown{
1019dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown	int ret;
1029dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown	u16 reg;
1039dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown
1049dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown	/*
1059dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown	 * The write counter contains a pseudo-random number which is
1069dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown	 * regenerated every time we set the RTC so it should be a
1079dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown	 * useful per-system source of entropy.
1089dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown	 */
1099dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown	ret = wm831x_reg_read(wm831x, WM831X_RTC_WRITE_COUNTER);
1109dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown	if (ret >= 0) {
1119dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown		reg = ret;
1129dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown		add_device_randomness(&reg, sizeof(reg));
1139dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown	} else {
1149dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown		dev_warn(wm831x->dev, "Failed to read RTC write counter: %d\n",
1159dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown			 ret);
1169dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown	}
1179dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown}
1189dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown
11935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/*
12035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * Read current time and date in RTC
12135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */
12235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_readtime(struct device *dev, struct rtc_time *tm)
12335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{
12435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
12535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	struct wm831x *wm831x = wm831x_rtc->wm831x;
12635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	u16 time1[2], time2[2];
12735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	int ret;
12835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	int count = 0;
12935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
13035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	/* Has the RTC been programmed? */
13135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
13235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (ret < 0) {
13335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		dev_err(dev, "Failed to read RTC control: %d\n", ret);
13435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return ret;
13535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	}
13635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (!(ret & WM831X_RTC_VALID)) {
13735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		dev_dbg(dev, "RTC not yet configured\n");
13835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return -EINVAL;
13935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	}
14035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
14135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	/* Read twice to make sure we don't read a corrupt, partially
14235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	 * incremented, value.
14335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	 */
14435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	do {
14535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1,
14635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown				       2, time1);
14735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		if (ret != 0)
14835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown			continue;
14935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
15035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1,
15135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown				       2, time2);
15235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		if (ret != 0)
15335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown			continue;
15435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
15535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		if (memcmp(time1, time2, sizeof(time1)) == 0) {
15635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown			u32 time = (time1[0] << 16) | time1[1];
15735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
15835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown			rtc_time_to_tm(time, tm);
15935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown			return rtc_valid_tm(tm);
16035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		}
16135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
16235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	} while (++count < WM831X_GET_TIME_RETRIES);
16335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
16435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	dev_err(dev, "Timed out reading current time\n");
16535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
16635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	return -EIO;
16735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown}
16835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
16935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/*
17035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * Set current time and date in RTC
17135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */
17235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_set_mmss(struct device *dev, unsigned long time)
17335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{
17435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
17535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	struct wm831x *wm831x = wm831x_rtc->wm831x;
17635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	struct rtc_time new_tm;
17735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	unsigned long new_time;
17835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	int ret;
17935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	int count = 0;
18035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
18135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_1,
18235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown			       (time >> 16) & 0xffff);
18335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (ret < 0) {
18435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		dev_err(dev, "Failed to write TIME_1: %d\n", ret);
18535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return ret;
18635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	}
18735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
18835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_2, time & 0xffff);
18935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (ret < 0) {
19035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		dev_err(dev, "Failed to write TIME_2: %d\n", ret);
19135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return ret;
19235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	}
19335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
19435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	/* Wait for the update to complete - should happen first time
19535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	 * round but be conservative.
19635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	 */
19735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	do {
19835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		msleep(1);
19935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
20035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
20135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		if (ret < 0)
20235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown			ret = WM831X_RTC_SYNC_BUSY;
20335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	} while (!(ret & WM831X_RTC_SYNC_BUSY) &&
20435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		 ++count < WM831X_SET_TIME_RETRIES);
20535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
20635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (ret & WM831X_RTC_SYNC_BUSY) {
20735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		dev_err(dev, "Timed out writing RTC update\n");
20835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return -EIO;
20935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	}
21035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
21135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	/* Check that the update was accepted; security features may
21235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	 * have caused the update to be ignored.
21335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	 */
21435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	ret = wm831x_rtc_readtime(dev, &new_tm);
21535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (ret < 0)
21635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return ret;
21735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
21835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	ret = rtc_tm_to_time(&new_tm, &new_time);
21935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (ret < 0) {
22035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		dev_err(dev, "Failed to convert time: %d\n", ret);
22135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return ret;
22235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	}
22335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
22435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	/* Allow a second of change in case of tick */
22535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (new_time - time > 1) {
22635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		dev_err(dev, "RTC update not permitted by hardware\n");
22735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return -EPERM;
22835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	}
22935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
23035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	return 0;
23135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown}
23235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
23335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/*
23435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * Read alarm time and date in RTC
23535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */
23635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
23735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{
23835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
23935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	int ret;
24035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	u16 data[2];
24135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	u32 time;
24235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
24335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	ret = wm831x_bulk_read(wm831x_rtc->wm831x, WM831X_RTC_ALARM_1,
24435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown			       2, data);
24535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (ret != 0) {
24635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		dev_err(dev, "Failed to read alarm time: %d\n", ret);
24735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return ret;
24835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	}
24935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
25035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	time = (data[0] << 16) | data[1];
25135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
25235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	rtc_time_to_tm(time, &alrm->time);
25335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
25435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	ret = wm831x_reg_read(wm831x_rtc->wm831x, WM831X_RTC_CONTROL);
25535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (ret < 0) {
25635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		dev_err(dev, "Failed to read RTC control: %d\n", ret);
25735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return ret;
25835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	}
25935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
26035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (ret & WM831X_RTC_ALM_ENA)
26135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		alrm->enabled = 1;
26235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	else
26335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		alrm->enabled = 0;
26435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
26535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	return 0;
26635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown}
26735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
26835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_stop_alarm(struct wm831x_rtc *wm831x_rtc)
26935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{
27035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	wm831x_rtc->alarm_enabled = 0;
27135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
27235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
27335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown			       WM831X_RTC_ALM_ENA, 0);
27435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown}
27535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
27635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_start_alarm(struct wm831x_rtc *wm831x_rtc)
27735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{
27835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	wm831x_rtc->alarm_enabled = 1;
27935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
28035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
28135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown			       WM831X_RTC_ALM_ENA, WM831X_RTC_ALM_ENA);
28235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown}
28335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
28435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
28535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{
28635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
28735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	struct wm831x *wm831x = wm831x_rtc->wm831x;
28835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	int ret;
28935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	unsigned long time;
29035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
29135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	ret = rtc_tm_to_time(&alrm->time, &time);
29235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (ret < 0) {
29335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		dev_err(dev, "Failed to convert time: %d\n", ret);
29435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return ret;
29535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	}
29635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
29735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	ret = wm831x_rtc_stop_alarm(wm831x_rtc);
29835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (ret < 0) {
29935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		dev_err(dev, "Failed to stop alarm: %d\n", ret);
30035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return ret;
30135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	}
30235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
30335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_1,
30435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown			       (time >> 16) & 0xffff);
30535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (ret < 0) {
30635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		dev_err(dev, "Failed to write ALARM_1: %d\n", ret);
30735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return ret;
30835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	}
30935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
31035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_2, time & 0xffff);
31135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (ret < 0) {
31235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		dev_err(dev, "Failed to write ALARM_2: %d\n", ret);
31335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return ret;
31435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	}
31535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
31635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (alrm->enabled) {
31735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		ret = wm831x_rtc_start_alarm(wm831x_rtc);
31835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		if (ret < 0) {
31935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown			dev_err(dev, "Failed to start alarm: %d\n", ret);
32035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown			return ret;
32135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		}
32235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	}
32335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
32435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	return 0;
32535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown}
32635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
32735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_alarm_irq_enable(struct device *dev,
32835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown				       unsigned int enabled)
32935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{
33035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
33135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
33235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	if (enabled)
33335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return wm831x_rtc_start_alarm(wm831x_rtc);
33435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	else
33535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown		return wm831x_rtc_stop_alarm(wm831x_rtc);
33635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown}
33735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
33835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic irqreturn_t wm831x_alm_irq(int irq, void *data)
33935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{
34035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown	struct wm831x_rtc *wm831x_rtc = data;
34135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown
342