xref: /linux/drivers/rtc/rtc-s5m.c (revision 756d5282bf04db868b1c0b93f41981dabe2af57f)
15bccae6eSSangbeom Kim /*
2f8b23bbdSKrzysztof Kozlowski  * Copyright (c) 2013-2014 Samsung Electronics Co., Ltd
35bccae6eSSangbeom Kim  *	http://www.samsung.com
45bccae6eSSangbeom Kim  *
55bccae6eSSangbeom Kim  *  Copyright (C) 2013 Google, Inc
65bccae6eSSangbeom Kim  *
75bccae6eSSangbeom Kim  *  This program is free software; you can redistribute it and/or modify
85bccae6eSSangbeom Kim  *  it under the terms of the GNU General Public License as published by
95bccae6eSSangbeom Kim  *  the Free Software Foundation; either version 2 of the License, or
105bccae6eSSangbeom Kim  *  (at your option) any later version.
115bccae6eSSangbeom Kim  *
125bccae6eSSangbeom Kim  *  This program is distributed in the hope that it will be useful,
135bccae6eSSangbeom Kim  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
145bccae6eSSangbeom Kim  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
155bccae6eSSangbeom Kim  *  GNU General Public License for more details.
165bccae6eSSangbeom Kim  */
175bccae6eSSangbeom Kim 
18a737e835SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19a737e835SJoe Perches 
205bccae6eSSangbeom Kim #include <linux/module.h>
215bccae6eSSangbeom Kim #include <linux/i2c.h>
225bccae6eSSangbeom Kim #include <linux/bcd.h>
235bccae6eSSangbeom Kim #include <linux/regmap.h>
245bccae6eSSangbeom Kim #include <linux/rtc.h>
255bccae6eSSangbeom Kim #include <linux/platform_device.h>
265bccae6eSSangbeom Kim #include <linux/mfd/samsung/core.h>
275bccae6eSSangbeom Kim #include <linux/mfd/samsung/irq.h>
285bccae6eSSangbeom Kim #include <linux/mfd/samsung/rtc.h>
290c5deb1eSKrzysztof Kozlowski #include <linux/mfd/samsung/s2mps14.h>
305bccae6eSSangbeom Kim 
31d73238d4SKrzysztof Kozlowski /*
32d73238d4SKrzysztof Kozlowski  * Maximum number of retries for checking changes in UDR field
33602cb5bbSKrzysztof Kozlowski  * of S5M_RTC_UDR_CON register (to limit possible endless loop).
34d73238d4SKrzysztof Kozlowski  *
35d73238d4SKrzysztof Kozlowski  * After writing to RTC registers (setting time or alarm) read the UDR field
36602cb5bbSKrzysztof Kozlowski  * in S5M_RTC_UDR_CON register. UDR is auto-cleared when data have
37d73238d4SKrzysztof Kozlowski  * been transferred.
38d73238d4SKrzysztof Kozlowski  */
39d73238d4SKrzysztof Kozlowski #define UDR_READ_RETRY_CNT	5
40d73238d4SKrzysztof Kozlowski 
414a681243SGustavo A. R. Silva enum {
424a681243SGustavo A. R. Silva 	RTC_SEC = 0,
434a681243SGustavo A. R. Silva 	RTC_MIN,
444a681243SGustavo A. R. Silva 	RTC_HOUR,
454a681243SGustavo A. R. Silva 	RTC_WEEKDAY,
464a681243SGustavo A. R. Silva 	RTC_DATE,
474a681243SGustavo A. R. Silva 	RTC_MONTH,
484a681243SGustavo A. R. Silva 	RTC_YEAR1,
494a681243SGustavo A. R. Silva 	RTC_YEAR2,
50*756d5282SGustavo A. R. Silva 	/* Make sure this is always the last enum name. */
51*756d5282SGustavo A. R. Silva 	RTC_MAX_NUM_TIME_REGS
524a681243SGustavo A. R. Silva };
534a681243SGustavo A. R. Silva 
548ae83b6fSKrzysztof Kozlowski /*
558ae83b6fSKrzysztof Kozlowski  * Registers used by the driver which are different between chipsets.
568ae83b6fSKrzysztof Kozlowski  *
578ae83b6fSKrzysztof Kozlowski  * Operations like read time and write alarm/time require updating
588ae83b6fSKrzysztof Kozlowski  * specific fields in UDR register. These fields usually are auto-cleared
598ae83b6fSKrzysztof Kozlowski  * (with some exceptions).
608ae83b6fSKrzysztof Kozlowski  *
618ae83b6fSKrzysztof Kozlowski  * Table of operations per device:
628ae83b6fSKrzysztof Kozlowski  *
638ae83b6fSKrzysztof Kozlowski  * Device     | Write time | Read time | Write alarm
648ae83b6fSKrzysztof Kozlowski  * =================================================
658ae83b6fSKrzysztof Kozlowski  * S5M8767    | UDR + TIME |           | UDR
668ae83b6fSKrzysztof Kozlowski  * S2MPS11/14 | WUDR       | RUDR      | WUDR + RUDR
678ae83b6fSKrzysztof Kozlowski  * S2MPS13    | WUDR       | RUDR      | WUDR + AUDR
688ae83b6fSKrzysztof Kozlowski  * S2MPS15    | WUDR       | RUDR      | AUDR
698ae83b6fSKrzysztof Kozlowski  */
70f8b23bbdSKrzysztof Kozlowski struct s5m_rtc_reg_config {
71f8b23bbdSKrzysztof Kozlowski 	/* Number of registers used for setting time/alarm0/alarm1 */
72f8b23bbdSKrzysztof Kozlowski 	unsigned int regs_count;
73f8b23bbdSKrzysztof Kozlowski 	/* First register for time, seconds */
74f8b23bbdSKrzysztof Kozlowski 	unsigned int time;
75f8b23bbdSKrzysztof Kozlowski 	/* RTC control register */
76f8b23bbdSKrzysztof Kozlowski 	unsigned int ctrl;
77f8b23bbdSKrzysztof Kozlowski 	/* First register for alarm 0, seconds */
78f8b23bbdSKrzysztof Kozlowski 	unsigned int alarm0;
79f8b23bbdSKrzysztof Kozlowski 	/* First register for alarm 1, seconds */
80f8b23bbdSKrzysztof Kozlowski 	unsigned int alarm1;
81f8b23bbdSKrzysztof Kozlowski 	/*
82f8b23bbdSKrzysztof Kozlowski 	 * Register for update flag (UDR). Typically setting UDR field to 1
83f8b23bbdSKrzysztof Kozlowski 	 * will enable update of time or alarm register. Then it will be
84f8b23bbdSKrzysztof Kozlowski 	 * auto-cleared after successful update.
85f8b23bbdSKrzysztof Kozlowski 	 */
86a83a793aSKrzysztof Kozlowski 	unsigned int udr_update;
8767a6025aSKrzysztof Kozlowski 	/* Auto-cleared mask in UDR field for writing time and alarm */
8867a6025aSKrzysztof Kozlowski 	unsigned int autoclear_udr_mask;
898ae83b6fSKrzysztof Kozlowski 	/*
908ae83b6fSKrzysztof Kozlowski 	 * Masks in UDR field for time and alarm operations.
918ae83b6fSKrzysztof Kozlowski 	 * The read time mask can be 0. Rest should not.
928ae83b6fSKrzysztof Kozlowski 	 */
938ae83b6fSKrzysztof Kozlowski 	unsigned int read_time_udr_mask;
948ae83b6fSKrzysztof Kozlowski 	unsigned int write_time_udr_mask;
958ae83b6fSKrzysztof Kozlowski 	unsigned int write_alarm_udr_mask;
96f8b23bbdSKrzysztof Kozlowski };
97f8b23bbdSKrzysztof Kozlowski 
98f8b23bbdSKrzysztof Kozlowski /* Register map for S5M8763 and S5M8767 */
99f8b23bbdSKrzysztof Kozlowski static const struct s5m_rtc_reg_config s5m_rtc_regs = {
100f8b23bbdSKrzysztof Kozlowski 	.regs_count		= 8,
101f8b23bbdSKrzysztof Kozlowski 	.time			= S5M_RTC_SEC,
102f8b23bbdSKrzysztof Kozlowski 	.ctrl			= S5M_ALARM1_CONF,
103f8b23bbdSKrzysztof Kozlowski 	.alarm0			= S5M_ALARM0_SEC,
104f8b23bbdSKrzysztof Kozlowski 	.alarm1			= S5M_ALARM1_SEC,
105a83a793aSKrzysztof Kozlowski 	.udr_update		= S5M_RTC_UDR_CON,
10667a6025aSKrzysztof Kozlowski 	.autoclear_udr_mask	= S5M_RTC_UDR_MASK,
1078ae83b6fSKrzysztof Kozlowski 	.read_time_udr_mask	= 0, /* Not needed */
1088ae83b6fSKrzysztof Kozlowski 	.write_time_udr_mask	= S5M_RTC_UDR_MASK | S5M_RTC_TIME_EN_MASK,
1098ae83b6fSKrzysztof Kozlowski 	.write_alarm_udr_mask	= S5M_RTC_UDR_MASK,
110f8b23bbdSKrzysztof Kozlowski };
111f8b23bbdSKrzysztof Kozlowski 
1128ae83b6fSKrzysztof Kozlowski /* Register map for S2MPS13 */
1138ae83b6fSKrzysztof Kozlowski static const struct s5m_rtc_reg_config s2mps13_rtc_regs = {
1140c5deb1eSKrzysztof Kozlowski 	.regs_count		= 7,
1150c5deb1eSKrzysztof Kozlowski 	.time			= S2MPS_RTC_SEC,
1160c5deb1eSKrzysztof Kozlowski 	.ctrl			= S2MPS_RTC_CTRL,
1170c5deb1eSKrzysztof Kozlowski 	.alarm0			= S2MPS_ALARM0_SEC,
1180c5deb1eSKrzysztof Kozlowski 	.alarm1			= S2MPS_ALARM1_SEC,
119a83a793aSKrzysztof Kozlowski 	.udr_update		= S2MPS_RTC_UDR_CON,
12067a6025aSKrzysztof Kozlowski 	.autoclear_udr_mask	= S2MPS_RTC_WUDR_MASK,
1218ae83b6fSKrzysztof Kozlowski 	.read_time_udr_mask	= S2MPS_RTC_RUDR_MASK,
1228ae83b6fSKrzysztof Kozlowski 	.write_time_udr_mask	= S2MPS_RTC_WUDR_MASK,
1238ae83b6fSKrzysztof Kozlowski 	.write_alarm_udr_mask	= S2MPS_RTC_WUDR_MASK | S2MPS13_RTC_AUDR_MASK,
1248ae83b6fSKrzysztof Kozlowski };
1258ae83b6fSKrzysztof Kozlowski 
1268ae83b6fSKrzysztof Kozlowski /* Register map for S2MPS11/14 */
1278ae83b6fSKrzysztof Kozlowski static const struct s5m_rtc_reg_config s2mps14_rtc_regs = {
1288ae83b6fSKrzysztof Kozlowski 	.regs_count		= 7,
1298ae83b6fSKrzysztof Kozlowski 	.time			= S2MPS_RTC_SEC,
1308ae83b6fSKrzysztof Kozlowski 	.ctrl			= S2MPS_RTC_CTRL,
1318ae83b6fSKrzysztof Kozlowski 	.alarm0			= S2MPS_ALARM0_SEC,
1328ae83b6fSKrzysztof Kozlowski 	.alarm1			= S2MPS_ALARM1_SEC,
1338ae83b6fSKrzysztof Kozlowski 	.udr_update		= S2MPS_RTC_UDR_CON,
1348ae83b6fSKrzysztof Kozlowski 	.autoclear_udr_mask	= S2MPS_RTC_WUDR_MASK,
1358ae83b6fSKrzysztof Kozlowski 	.read_time_udr_mask	= S2MPS_RTC_RUDR_MASK,
1368ae83b6fSKrzysztof Kozlowski 	.write_time_udr_mask	= S2MPS_RTC_WUDR_MASK,
1378ae83b6fSKrzysztof Kozlowski 	.write_alarm_udr_mask	= S2MPS_RTC_WUDR_MASK | S2MPS_RTC_RUDR_MASK,
1388ae83b6fSKrzysztof Kozlowski };
1398ae83b6fSKrzysztof Kozlowski 
1408ae83b6fSKrzysztof Kozlowski /*
1418ae83b6fSKrzysztof Kozlowski  * Register map for S2MPS15 - in comparison to S2MPS14 the WUDR and AUDR bits
1428ae83b6fSKrzysztof Kozlowski  * are swapped.
1438ae83b6fSKrzysztof Kozlowski  */
1448ae83b6fSKrzysztof Kozlowski static const struct s5m_rtc_reg_config s2mps15_rtc_regs = {
1458ae83b6fSKrzysztof Kozlowski 	.regs_count		= 7,
1468ae83b6fSKrzysztof Kozlowski 	.time			= S2MPS_RTC_SEC,
1478ae83b6fSKrzysztof Kozlowski 	.ctrl			= S2MPS_RTC_CTRL,
1488ae83b6fSKrzysztof Kozlowski 	.alarm0			= S2MPS_ALARM0_SEC,
1498ae83b6fSKrzysztof Kozlowski 	.alarm1			= S2MPS_ALARM1_SEC,
1508ae83b6fSKrzysztof Kozlowski 	.udr_update		= S2MPS_RTC_UDR_CON,
1518ae83b6fSKrzysztof Kozlowski 	.autoclear_udr_mask	= S2MPS_RTC_WUDR_MASK,
1528ae83b6fSKrzysztof Kozlowski 	.read_time_udr_mask	= S2MPS_RTC_RUDR_MASK,
1538ae83b6fSKrzysztof Kozlowski 	.write_time_udr_mask	= S2MPS15_RTC_WUDR_MASK,
1548ae83b6fSKrzysztof Kozlowski 	.write_alarm_udr_mask	= S2MPS15_RTC_AUDR_MASK,
1550c5deb1eSKrzysztof Kozlowski };
1560c5deb1eSKrzysztof Kozlowski 
1575bccae6eSSangbeom Kim struct s5m_rtc_info {
1585bccae6eSSangbeom Kim 	struct device *dev;
159e349c910SKrzysztof Kozlowski 	struct i2c_client *i2c;
1605bccae6eSSangbeom Kim 	struct sec_pmic_dev *s5m87xx;
1615ccb7d71SGeert Uytterhoeven 	struct regmap *regmap;
1625bccae6eSSangbeom Kim 	struct rtc_device *rtc_dev;
1635bccae6eSSangbeom Kim 	int irq;
16494f91922SKrzysztof Kozlowski 	enum sec_device_type device_type;
1655bccae6eSSangbeom Kim 	int rtc_24hr_mode;
166f8b23bbdSKrzysztof Kozlowski 	const struct s5m_rtc_reg_config	*regs;
1675bccae6eSSangbeom Kim };
1685bccae6eSSangbeom Kim 
169e349c910SKrzysztof Kozlowski static const struct regmap_config s5m_rtc_regmap_config = {
170e349c910SKrzysztof Kozlowski 	.reg_bits = 8,
171e349c910SKrzysztof Kozlowski 	.val_bits = 8,
172e349c910SKrzysztof Kozlowski 
173602cb5bbSKrzysztof Kozlowski 	.max_register = S5M_RTC_REG_MAX,
174e349c910SKrzysztof Kozlowski };
175e349c910SKrzysztof Kozlowski 
176e349c910SKrzysztof Kozlowski static const struct regmap_config s2mps14_rtc_regmap_config = {
177e349c910SKrzysztof Kozlowski 	.reg_bits = 8,
178e349c910SKrzysztof Kozlowski 	.val_bits = 8,
179e349c910SKrzysztof Kozlowski 
180e349c910SKrzysztof Kozlowski 	.max_register = S2MPS_RTC_REG_MAX,
181e349c910SKrzysztof Kozlowski };
182e349c910SKrzysztof Kozlowski 
1835bccae6eSSangbeom Kim static void s5m8767_data_to_tm(u8 *data, struct rtc_time *tm,
1845bccae6eSSangbeom Kim 			       int rtc_24hr_mode)
1855bccae6eSSangbeom Kim {
1865bccae6eSSangbeom Kim 	tm->tm_sec = data[RTC_SEC] & 0x7f;
1875bccae6eSSangbeom Kim 	tm->tm_min = data[RTC_MIN] & 0x7f;
1885bccae6eSSangbeom Kim 	if (rtc_24hr_mode) {
1895bccae6eSSangbeom Kim 		tm->tm_hour = data[RTC_HOUR] & 0x1f;
1905bccae6eSSangbeom Kim 	} else {
1915bccae6eSSangbeom Kim 		tm->tm_hour = data[RTC_HOUR] & 0x0f;
1925bccae6eSSangbeom Kim 		if (data[RTC_HOUR] & HOUR_PM_MASK)
1935bccae6eSSangbeom Kim 			tm->tm_hour += 12;
1945bccae6eSSangbeom Kim 	}
1955bccae6eSSangbeom Kim 
1965bccae6eSSangbeom Kim 	tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0x7f);
1975bccae6eSSangbeom Kim 	tm->tm_mday = data[RTC_DATE] & 0x1f;
1985bccae6eSSangbeom Kim 	tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
1995bccae6eSSangbeom Kim 	tm->tm_year = (data[RTC_YEAR1] & 0x7f) + 100;
2005bccae6eSSangbeom Kim 	tm->tm_yday = 0;
2015bccae6eSSangbeom Kim 	tm->tm_isdst = 0;
2025bccae6eSSangbeom Kim }
2035bccae6eSSangbeom Kim 
2045bccae6eSSangbeom Kim static int s5m8767_tm_to_data(struct rtc_time *tm, u8 *data)
2055bccae6eSSangbeom Kim {
2065bccae6eSSangbeom Kim 	data[RTC_SEC] = tm->tm_sec;
2075bccae6eSSangbeom Kim 	data[RTC_MIN] = tm->tm_min;
2085bccae6eSSangbeom Kim 
2095bccae6eSSangbeom Kim 	if (tm->tm_hour >= 12)
2105bccae6eSSangbeom Kim 		data[RTC_HOUR] = tm->tm_hour | HOUR_PM_MASK;
2115bccae6eSSangbeom Kim 	else
2125bccae6eSSangbeom Kim 		data[RTC_HOUR] = tm->tm_hour & ~HOUR_PM_MASK;
2135bccae6eSSangbeom Kim 
2145bccae6eSSangbeom Kim 	data[RTC_WEEKDAY] = 1 << tm->tm_wday;
2155bccae6eSSangbeom Kim 	data[RTC_DATE] = tm->tm_mday;
2165bccae6eSSangbeom Kim 	data[RTC_MONTH] = tm->tm_mon + 1;
2175bccae6eSSangbeom Kim 	data[RTC_YEAR1] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0;
2185bccae6eSSangbeom Kim 
2195bccae6eSSangbeom Kim 	if (tm->tm_year < 100) {
220a737e835SJoe Perches 		pr_err("RTC cannot handle the year %d\n",
2215bccae6eSSangbeom Kim 		       1900 + tm->tm_year);
2225bccae6eSSangbeom Kim 		return -EINVAL;
2235bccae6eSSangbeom Kim 	} else {
2245bccae6eSSangbeom Kim 		return 0;
2255bccae6eSSangbeom Kim 	}
2265bccae6eSSangbeom Kim }
2275bccae6eSSangbeom Kim 
228d73238d4SKrzysztof Kozlowski /*
229d73238d4SKrzysztof Kozlowski  * Read RTC_UDR_CON register and wait till UDR field is cleared.
230d73238d4SKrzysztof Kozlowski  * This indicates that time/alarm update ended.
231d73238d4SKrzysztof Kozlowski  */
2328124c711SKrzysztof Kozlowski static int s5m8767_wait_for_udr_update(struct s5m_rtc_info *info)
233d73238d4SKrzysztof Kozlowski {
234d73238d4SKrzysztof Kozlowski 	int ret, retry = UDR_READ_RETRY_CNT;
235d73238d4SKrzysztof Kozlowski 	unsigned int data;
236d73238d4SKrzysztof Kozlowski 
237d73238d4SKrzysztof Kozlowski 	do {
238a83a793aSKrzysztof Kozlowski 		ret = regmap_read(info->regmap, info->regs->udr_update, &data);
23967a6025aSKrzysztof Kozlowski 	} while (--retry && (data & info->regs->autoclear_udr_mask) && !ret);
240d73238d4SKrzysztof Kozlowski 
241d73238d4SKrzysztof Kozlowski 	if (!retry)
242d73238d4SKrzysztof Kozlowski 		dev_err(info->dev, "waiting for UDR update, reached max number of retries\n");
243d73238d4SKrzysztof Kozlowski 
244d73238d4SKrzysztof Kozlowski 	return ret;
245d73238d4SKrzysztof Kozlowski }
246d73238d4SKrzysztof Kozlowski 
2478124c711SKrzysztof Kozlowski static int s5m_check_peding_alarm_interrupt(struct s5m_rtc_info *info,
248f8b23bbdSKrzysztof Kozlowski 		struct rtc_wkalrm *alarm)
249f8b23bbdSKrzysztof Kozlowski {
250f8b23bbdSKrzysztof Kozlowski 	int ret;
251f8b23bbdSKrzysztof Kozlowski 	unsigned int val;
252f8b23bbdSKrzysztof Kozlowski 
253f8b23bbdSKrzysztof Kozlowski 	switch (info->device_type) {
254f8b23bbdSKrzysztof Kozlowski 	case S5M8767X:
255f8b23bbdSKrzysztof Kozlowski 	case S5M8763X:
256f8b23bbdSKrzysztof Kozlowski 		ret = regmap_read(info->regmap, S5M_RTC_STATUS, &val);
257f8b23bbdSKrzysztof Kozlowski 		val &= S5M_ALARM0_STATUS;
258f8b23bbdSKrzysztof Kozlowski 		break;
259a65e5efaSAlim Akhtar 	case S2MPS15X:
2600c5deb1eSKrzysztof Kozlowski 	case S2MPS14X:
2615281f94aSKrzysztof Kozlowski 	case S2MPS13X:
2620c5deb1eSKrzysztof Kozlowski 		ret = regmap_read(info->s5m87xx->regmap_pmic, S2MPS14_REG_ST2,
2630c5deb1eSKrzysztof Kozlowski 				&val);
2640c5deb1eSKrzysztof Kozlowski 		val &= S2MPS_ALARM0_STATUS;
2650c5deb1eSKrzysztof Kozlowski 		break;
266f8b23bbdSKrzysztof Kozlowski 	default:
267f8b23bbdSKrzysztof Kozlowski 		return -EINVAL;
268f8b23bbdSKrzysztof Kozlowski 	}
269f8b23bbdSKrzysztof Kozlowski 	if (ret < 0)
270f8b23bbdSKrzysztof Kozlowski 		return ret;
271f8b23bbdSKrzysztof Kozlowski 
272f8b23bbdSKrzysztof Kozlowski 	if (val)
273f8b23bbdSKrzysztof Kozlowski 		alarm->pending = 1;
274f8b23bbdSKrzysztof Kozlowski 	else
275f8b23bbdSKrzysztof Kozlowski 		alarm->pending = 0;
276f8b23bbdSKrzysztof Kozlowski 
277f8b23bbdSKrzysztof Kozlowski 	return 0;
278f8b23bbdSKrzysztof Kozlowski }
279f8b23bbdSKrzysztof Kozlowski 
2808124c711SKrzysztof Kozlowski static int s5m8767_rtc_set_time_reg(struct s5m_rtc_info *info)
2815bccae6eSSangbeom Kim {
2825bccae6eSSangbeom Kim 	int ret;
2835bccae6eSSangbeom Kim 	unsigned int data;
2845bccae6eSSangbeom Kim 
285a83a793aSKrzysztof Kozlowski 	ret = regmap_read(info->regmap, info->regs->udr_update, &data);
2865bccae6eSSangbeom Kim 	if (ret < 0) {
2875bccae6eSSangbeom Kim 		dev_err(info->dev, "failed to read update reg(%d)\n", ret);
2885bccae6eSSangbeom Kim 		return ret;
2895bccae6eSSangbeom Kim 	}
2905bccae6eSSangbeom Kim 
2918ae83b6fSKrzysztof Kozlowski 	data |= info->regs->write_time_udr_mask;
292a65e5efaSAlim Akhtar 
293a83a793aSKrzysztof Kozlowski 	ret = regmap_write(info->regmap, info->regs->udr_update, data);
2945bccae6eSSangbeom Kim 	if (ret < 0) {
2955bccae6eSSangbeom Kim 		dev_err(info->dev, "failed to write update reg(%d)\n", ret);
2965bccae6eSSangbeom Kim 		return ret;
2975bccae6eSSangbeom Kim 	}
2985bccae6eSSangbeom Kim 
299d73238d4SKrzysztof Kozlowski 	ret = s5m8767_wait_for_udr_update(info);
3005bccae6eSSangbeom Kim 
3015bccae6eSSangbeom Kim 	return ret;
3025bccae6eSSangbeom Kim }
3035bccae6eSSangbeom Kim 
3048124c711SKrzysztof Kozlowski static int s5m8767_rtc_set_alarm_reg(struct s5m_rtc_info *info)
3055bccae6eSSangbeom Kim {
3065bccae6eSSangbeom Kim 	int ret;
3075bccae6eSSangbeom Kim 	unsigned int data;
3085bccae6eSSangbeom Kim 
309a83a793aSKrzysztof Kozlowski 	ret = regmap_read(info->regmap, info->regs->udr_update, &data);
3105bccae6eSSangbeom Kim 	if (ret < 0) {
3115bccae6eSSangbeom Kim 		dev_err(info->dev, "%s: fail to read update reg(%d)\n",
3125bccae6eSSangbeom Kim 			__func__, ret);
3135bccae6eSSangbeom Kim 		return ret;
3145bccae6eSSangbeom Kim 	}
3155bccae6eSSangbeom Kim 
3168ae83b6fSKrzysztof Kozlowski 	data |= info->regs->write_alarm_udr_mask;
3170c5deb1eSKrzysztof Kozlowski 	switch (info->device_type) {
3180c5deb1eSKrzysztof Kozlowski 	case S5M8763X:
3190c5deb1eSKrzysztof Kozlowski 	case S5M8767X:
3200c5deb1eSKrzysztof Kozlowski 		data &= ~S5M_RTC_TIME_EN_MASK;
3210c5deb1eSKrzysztof Kozlowski 		break;
322a65e5efaSAlim Akhtar 	case S2MPS15X:
3230c5deb1eSKrzysztof Kozlowski 	case S2MPS14X:
3245281f94aSKrzysztof Kozlowski 	case S2MPS13X:
3258ae83b6fSKrzysztof Kozlowski 		/* No exceptions needed */
3265281f94aSKrzysztof Kozlowski 		break;
3270c5deb1eSKrzysztof Kozlowski 	default:
3280c5deb1eSKrzysztof Kozlowski 		return -EINVAL;
3290c5deb1eSKrzysztof Kozlowski 	}
3305bccae6eSSangbeom Kim 
331a83a793aSKrzysztof Kozlowski 	ret = regmap_write(info->regmap, info->regs->udr_update, data);
3325bccae6eSSangbeom Kim 	if (ret < 0) {
3335bccae6eSSangbeom Kim 		dev_err(info->dev, "%s: fail to write update reg(%d)\n",
3345bccae6eSSangbeom Kim 			__func__, ret);
3355bccae6eSSangbeom Kim 		return ret;
3365bccae6eSSangbeom Kim 	}
3375bccae6eSSangbeom Kim 
338d73238d4SKrzysztof Kozlowski 	ret = s5m8767_wait_for_udr_update(info);
3395bccae6eSSangbeom Kim 
3405281f94aSKrzysztof Kozlowski 	/* On S2MPS13 the AUDR is not auto-cleared */
3415281f94aSKrzysztof Kozlowski 	if (info->device_type == S2MPS13X)
342a83a793aSKrzysztof Kozlowski 		regmap_update_bits(info->regmap, info->regs->udr_update,
3435281f94aSKrzysztof Kozlowski 				   S2MPS13_RTC_AUDR_MASK, 0);
3445281f94aSKrzysztof Kozlowski 
3455bccae6eSSangbeom Kim 	return ret;
3465bccae6eSSangbeom Kim }
3475bccae6eSSangbeom Kim 
3485bccae6eSSangbeom Kim static void s5m8763_data_to_tm(u8 *data, struct rtc_time *tm)
3495bccae6eSSangbeom Kim {
3505bccae6eSSangbeom Kim 	tm->tm_sec = bcd2bin(data[RTC_SEC]);
3515bccae6eSSangbeom Kim 	tm->tm_min = bcd2bin(data[RTC_MIN]);
3525bccae6eSSangbeom Kim 
3535bccae6eSSangbeom Kim 	if (data[RTC_HOUR] & HOUR_12) {
3545bccae6eSSangbeom Kim 		tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x1f);
3555bccae6eSSangbeom Kim 		if (data[RTC_HOUR] & HOUR_PM)
3565bccae6eSSangbeom Kim 			tm->tm_hour += 12;
3575bccae6eSSangbeom Kim 	} else {
3585bccae6eSSangbeom Kim 		tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3f);
3595bccae6eSSangbeom Kim 	}
3605bccae6eSSangbeom Kim 
3615bccae6eSSangbeom Kim 	tm->tm_wday = data[RTC_WEEKDAY] & 0x07;
3625bccae6eSSangbeom Kim 	tm->tm_mday = bcd2bin(data[RTC_DATE]);
3635bccae6eSSangbeom Kim 	tm->tm_mon = bcd2bin(data[RTC_MONTH]);
3645bccae6eSSangbeom Kim 	tm->tm_year = bcd2bin(data[RTC_YEAR1]) + bcd2bin(data[RTC_YEAR2]) * 100;
3655bccae6eSSangbeom Kim 	tm->tm_year -= 1900;
3665bccae6eSSangbeom Kim }
3675bccae6eSSangbeom Kim 
3685bccae6eSSangbeom Kim static void s5m8763_tm_to_data(struct rtc_time *tm, u8 *data)
3695bccae6eSSangbeom Kim {
3705bccae6eSSangbeom Kim 	data[RTC_SEC] = bin2bcd(tm->tm_sec);
3715bccae6eSSangbeom Kim 	data[RTC_MIN] = bin2bcd(tm->tm_min);
3725bccae6eSSangbeom Kim 	data[RTC_HOUR] = bin2bcd(tm->tm_hour);
3735bccae6eSSangbeom Kim 	data[RTC_WEEKDAY] = tm->tm_wday;
3745bccae6eSSangbeom Kim 	data[RTC_DATE] = bin2bcd(tm->tm_mday);
3755bccae6eSSangbeom Kim 	data[RTC_MONTH] = bin2bcd(tm->tm_mon);
3765bccae6eSSangbeom Kim 	data[RTC_YEAR1] = bin2bcd(tm->tm_year % 100);
3775bccae6eSSangbeom Kim 	data[RTC_YEAR2] = bin2bcd((tm->tm_year + 1900) / 100);
3785bccae6eSSangbeom Kim }
3795bccae6eSSangbeom Kim 
3805bccae6eSSangbeom Kim static int s5m_rtc_read_time(struct device *dev, struct rtc_time *tm)
3815bccae6eSSangbeom Kim {
3825bccae6eSSangbeom Kim 	struct s5m_rtc_info *info = dev_get_drvdata(dev);
383*756d5282SGustavo A. R. Silva 	u8 data[RTC_MAX_NUM_TIME_REGS];
3845bccae6eSSangbeom Kim 	int ret;
3855bccae6eSSangbeom Kim 
3868ae83b6fSKrzysztof Kozlowski 	if (info->regs->read_time_udr_mask) {
3870c5deb1eSKrzysztof Kozlowski 		ret = regmap_update_bits(info->regmap,
388a83a793aSKrzysztof Kozlowski 				info->regs->udr_update,
3898ae83b6fSKrzysztof Kozlowski 				info->regs->read_time_udr_mask,
3908ae83b6fSKrzysztof Kozlowski 				info->regs->read_time_udr_mask);
3910c5deb1eSKrzysztof Kozlowski 		if (ret) {
3920c5deb1eSKrzysztof Kozlowski 			dev_err(dev,
3930c5deb1eSKrzysztof Kozlowski 				"Failed to prepare registers for time reading: %d\n",
3940c5deb1eSKrzysztof Kozlowski 				ret);
3950c5deb1eSKrzysztof Kozlowski 			return ret;
3960c5deb1eSKrzysztof Kozlowski 		}
3970c5deb1eSKrzysztof Kozlowski 	}
398f8b23bbdSKrzysztof Kozlowski 	ret = regmap_bulk_read(info->regmap, info->regs->time, data,
399f8b23bbdSKrzysztof Kozlowski 			info->regs->regs_count);
4005bccae6eSSangbeom Kim 	if (ret < 0)
4015bccae6eSSangbeom Kim 		return ret;
4025bccae6eSSangbeom Kim 
4035bccae6eSSangbeom Kim 	switch (info->device_type) {
4045bccae6eSSangbeom Kim 	case S5M8763X:
4055bccae6eSSangbeom Kim 		s5m8763_data_to_tm(data, tm);
4065bccae6eSSangbeom Kim 		break;
4075bccae6eSSangbeom Kim 
4085bccae6eSSangbeom Kim 	case S5M8767X:
409a65e5efaSAlim Akhtar 	case S2MPS15X:
4100c5deb1eSKrzysztof Kozlowski 	case S2MPS14X:
4115281f94aSKrzysztof Kozlowski 	case S2MPS13X:
4125bccae6eSSangbeom Kim 		s5m8767_data_to_tm(data, tm, info->rtc_24hr_mode);
4135bccae6eSSangbeom Kim 		break;
4145bccae6eSSangbeom Kim 
4155bccae6eSSangbeom Kim 	default:
4165bccae6eSSangbeom Kim 		return -EINVAL;
4175bccae6eSSangbeom Kim 	}
4185bccae6eSSangbeom Kim 
4195bccae6eSSangbeom Kim 	dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
4205bccae6eSSangbeom Kim 		1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday,
4215bccae6eSSangbeom Kim 		tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
4225bccae6eSSangbeom Kim 
42322652ba7SAlexandre Belloni 	return 0;
4245bccae6eSSangbeom Kim }
4255bccae6eSSangbeom Kim 
4265bccae6eSSangbeom Kim static int s5m_rtc_set_time(struct device *dev, struct rtc_time *tm)
4275bccae6eSSangbeom Kim {
4285bccae6eSSangbeom Kim 	struct s5m_rtc_info *info = dev_get_drvdata(dev);
429*756d5282SGustavo A. R. Silva 	u8 data[RTC_MAX_NUM_TIME_REGS];
4305bccae6eSSangbeom Kim 	int ret = 0;
4315bccae6eSSangbeom Kim 
4325bccae6eSSangbeom Kim 	switch (info->device_type) {
4335bccae6eSSangbeom Kim 	case S5M8763X:
4345bccae6eSSangbeom Kim 		s5m8763_tm_to_data(tm, data);
4355bccae6eSSangbeom Kim 		break;
4365bccae6eSSangbeom Kim 	case S5M8767X:
437a65e5efaSAlim Akhtar 	case S2MPS15X:
4380c5deb1eSKrzysztof Kozlowski 	case S2MPS14X:
4395281f94aSKrzysztof Kozlowski 	case S2MPS13X:
4405bccae6eSSangbeom Kim 		ret = s5m8767_tm_to_data(tm, data);
4415bccae6eSSangbeom Kim 		break;
4425bccae6eSSangbeom Kim 	default:
4435bccae6eSSangbeom Kim 		return -EINVAL;
4445bccae6eSSangbeom Kim 	}
4455bccae6eSSangbeom Kim 
4465bccae6eSSangbeom Kim 	if (ret < 0)
4475bccae6eSSangbeom Kim 		return ret;
4485bccae6eSSangbeom Kim 
4495bccae6eSSangbeom Kim 	dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
4505bccae6eSSangbeom Kim 		1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday,
4515bccae6eSSangbeom Kim 		tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
4525bccae6eSSangbeom Kim 
453f8b23bbdSKrzysztof Kozlowski 	ret = regmap_raw_write(info->regmap, info->regs->time, data,
454f8b23bbdSKrzysztof Kozlowski 			info->regs->regs_count);
4555bccae6eSSangbeom Kim 	if (ret < 0)
4565bccae6eSSangbeom Kim 		return ret;
4575bccae6eSSangbeom Kim 
4585bccae6eSSangbeom Kim 	ret = s5m8767_rtc_set_time_reg(info);
4595bccae6eSSangbeom Kim 
4605bccae6eSSangbeom Kim 	return ret;
4615bccae6eSSangbeom Kim }
4625bccae6eSSangbeom Kim 
4635bccae6eSSangbeom Kim static int s5m_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
4645bccae6eSSangbeom Kim {
4655bccae6eSSangbeom Kim 	struct s5m_rtc_info *info = dev_get_drvdata(dev);
466*756d5282SGustavo A. R. Silva 	u8 data[RTC_MAX_NUM_TIME_REGS];
4675bccae6eSSangbeom Kim 	unsigned int val;
4685bccae6eSSangbeom Kim 	int ret, i;
4695bccae6eSSangbeom Kim 
470f8b23bbdSKrzysztof Kozlowski 	ret = regmap_bulk_read(info->regmap, info->regs->alarm0, data,
471f8b23bbdSKrzysztof Kozlowski 			info->regs->regs_count);
4725bccae6eSSangbeom Kim 	if (ret < 0)
4735bccae6eSSangbeom Kim 		return ret;
4745bccae6eSSangbeom Kim 
4755bccae6eSSangbeom Kim 	switch (info->device_type) {
4765bccae6eSSangbeom Kim 	case S5M8763X:
4775bccae6eSSangbeom Kim 		s5m8763_data_to_tm(data, &alrm->time);
478602cb5bbSKrzysztof Kozlowski 		ret = regmap_read(info->regmap, S5M_ALARM0_CONF, &val);
4795bccae6eSSangbeom Kim 		if (ret < 0)
4805bccae6eSSangbeom Kim 			return ret;
4815bccae6eSSangbeom Kim 
4825bccae6eSSangbeom Kim 		alrm->enabled = !!val;
4835bccae6eSSangbeom Kim 		break;
4845bccae6eSSangbeom Kim 
4855bccae6eSSangbeom Kim 	case S5M8767X:
486a65e5efaSAlim Akhtar 	case S2MPS15X:
4870c5deb1eSKrzysztof Kozlowski 	case S2MPS14X:
4885281f94aSKrzysztof Kozlowski 	case S2MPS13X:
4895bccae6eSSangbeom Kim 		s5m8767_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
4905bccae6eSSangbeom Kim 		alrm->enabled = 0;
491f8b23bbdSKrzysztof Kozlowski 		for (i = 0; i < info->regs->regs_count; i++) {
4925bccae6eSSangbeom Kim 			if (data[i] & ALARM_ENABLE_MASK) {
4935bccae6eSSangbeom Kim 				alrm->enabled = 1;
4945bccae6eSSangbeom Kim 				break;
4955bccae6eSSangbeom Kim 			}
4965bccae6eSSangbeom Kim 		}
4975bccae6eSSangbeom Kim 		break;
4985bccae6eSSangbeom Kim 
4995bccae6eSSangbeom Kim 	default:
5005bccae6eSSangbeom Kim 		return -EINVAL;
5015bccae6eSSangbeom Kim 	}
5025bccae6eSSangbeom Kim 
503f8b23bbdSKrzysztof Kozlowski 	dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
504f8b23bbdSKrzysztof Kozlowski 		1900 + alrm->time.tm_year, 1 + alrm->time.tm_mon,
505f8b23bbdSKrzysztof Kozlowski 		alrm->time.tm_mday, alrm->time.tm_hour,
506f8b23bbdSKrzysztof Kozlowski 		alrm->time.tm_min, alrm->time.tm_sec,
507f8b23bbdSKrzysztof Kozlowski 		alrm->time.tm_wday);
508f8b23bbdSKrzysztof Kozlowski 
509f8b23bbdSKrzysztof Kozlowski 	ret = s5m_check_peding_alarm_interrupt(info, alrm);
5105bccae6eSSangbeom Kim 
5115bccae6eSSangbeom Kim 	return 0;
5125bccae6eSSangbeom Kim }
5135bccae6eSSangbeom Kim 
5145bccae6eSSangbeom Kim static int s5m_rtc_stop_alarm(struct s5m_rtc_info *info)
5155bccae6eSSangbeom Kim {
516*756d5282SGustavo A. R. Silva 	u8 data[RTC_MAX_NUM_TIME_REGS];
5175bccae6eSSangbeom Kim 	int ret, i;
5185bccae6eSSangbeom Kim 	struct rtc_time tm;
5195bccae6eSSangbeom Kim 
520f8b23bbdSKrzysztof Kozlowski 	ret = regmap_bulk_read(info->regmap, info->regs->alarm0, data,
521f8b23bbdSKrzysztof Kozlowski 			info->regs->regs_count);
5225bccae6eSSangbeom Kim 	if (ret < 0)
5235bccae6eSSangbeom Kim 		return ret;
5245bccae6eSSangbeom Kim 
5255bccae6eSSangbeom Kim 	s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode);
5265bccae6eSSangbeom Kim 	dev_dbg(info->dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
5275bccae6eSSangbeom Kim 		1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday,
5285bccae6eSSangbeom Kim 		tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday);
5295bccae6eSSangbeom Kim 
5305bccae6eSSangbeom Kim 	switch (info->device_type) {
5315bccae6eSSangbeom Kim 	case S5M8763X:
532602cb5bbSKrzysztof Kozlowski 		ret = regmap_write(info->regmap, S5M_ALARM0_CONF, 0);
5335bccae6eSSangbeom Kim 		break;
5345bccae6eSSangbeom Kim 
5355bccae6eSSangbeom Kim 	case S5M8767X:
536a65e5efaSAlim Akhtar 	case S2MPS15X:
5370c5deb1eSKrzysztof Kozlowski 	case S2MPS14X:
5385281f94aSKrzysztof Kozlowski 	case S2MPS13X:
539f8b23bbdSKrzysztof Kozlowski 		for (i = 0; i < info->regs->regs_count; i++)
5405bccae6eSSangbeom Kim 			data[i] &= ~ALARM_ENABLE_MASK;
5415bccae6eSSangbeom Kim 
542f8b23bbdSKrzysztof Kozlowski 		ret = regmap_raw_write(info->regmap, info->regs->alarm0, data,
543f8b23bbdSKrzysztof Kozlowski 				info->regs->regs_count);
5445bccae6eSSangbeom Kim 		if (ret < 0)
5455bccae6eSSangbeom Kim 			return ret;
5465bccae6eSSangbeom Kim 
5475bccae6eSSangbeom Kim 		ret = s5m8767_rtc_set_alarm_reg(info);
5485bccae6eSSangbeom Kim 
5495bccae6eSSangbeom Kim 		break;
5505bccae6eSSangbeom Kim 
5515bccae6eSSangbeom Kim 	default:
5525bccae6eSSangbeom Kim 		return -EINVAL;
5535bccae6eSSangbeom Kim 	}
5545bccae6eSSangbeom Kim 
5555bccae6eSSangbeom Kim 	return ret;
5565bccae6eSSangbeom Kim }
5575bccae6eSSangbeom Kim 
5585bccae6eSSangbeom Kim static int s5m_rtc_start_alarm(struct s5m_rtc_info *info)
5595bccae6eSSangbeom Kim {
5605bccae6eSSangbeom Kim 	int ret;
561*756d5282SGustavo A. R. Silva 	u8 data[RTC_MAX_NUM_TIME_REGS];
5625bccae6eSSangbeom Kim 	u8 alarm0_conf;
5635bccae6eSSangbeom Kim 	struct rtc_time tm;
5645bccae6eSSangbeom Kim 
565f8b23bbdSKrzysztof Kozlowski 	ret = regmap_bulk_read(info->regmap, info->regs->alarm0, data,
566f8b23bbdSKrzysztof Kozlowski 			info->regs->regs_count);
5675bccae6eSSangbeom Kim 	if (ret < 0)
5685bccae6eSSangbeom Kim 		return ret;
5695bccae6eSSangbeom Kim 
5705bccae6eSSangbeom Kim 	s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode);
5715bccae6eSSangbeom Kim 	dev_dbg(info->dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
5725bccae6eSSangbeom Kim 		1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday,
5735bccae6eSSangbeom Kim 		tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday);
5745bccae6eSSangbeom Kim 
5755bccae6eSSangbeom Kim 	switch (info->device_type) {
5765bccae6eSSangbeom Kim 	case S5M8763X:
5775bccae6eSSangbeom Kim 		alarm0_conf = 0x77;
578602cb5bbSKrzysztof Kozlowski 		ret = regmap_write(info->regmap, S5M_ALARM0_CONF, alarm0_conf);
5795bccae6eSSangbeom Kim 		break;
5805bccae6eSSangbeom Kim 
5815bccae6eSSangbeom Kim 	case S5M8767X:
582a65e5efaSAlim Akhtar 	case S2MPS15X:
5830c5deb1eSKrzysztof Kozlowski 	case S2MPS14X:
5845281f94aSKrzysztof Kozlowski 	case S2MPS13X:
5855bccae6eSSangbeom Kim 		data[RTC_SEC] |= ALARM_ENABLE_MASK;
5865bccae6eSSangbeom Kim 		data[RTC_MIN] |= ALARM_ENABLE_MASK;
5875bccae6eSSangbeom Kim 		data[RTC_HOUR] |= ALARM_ENABLE_MASK;
5885bccae6eSSangbeom Kim 		data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK;
5895bccae6eSSangbeom Kim 		if (data[RTC_DATE] & 0x1f)
5905bccae6eSSangbeom Kim 			data[RTC_DATE] |= ALARM_ENABLE_MASK;
5915bccae6eSSangbeom Kim 		if (data[RTC_MONTH] & 0xf)
5925bccae6eSSangbeom Kim 			data[RTC_MONTH] |= ALARM_ENABLE_MASK;
5935bccae6eSSangbeom Kim 		if (data[RTC_YEAR1] & 0x7f)
5945bccae6eSSangbeom Kim 			data[RTC_YEAR1] |= ALARM_ENABLE_MASK;
5955bccae6eSSangbeom Kim 
596f8b23bbdSKrzysztof Kozlowski 		ret = regmap_raw_write(info->regmap, info->regs->alarm0, data,
597f8b23bbdSKrzysztof Kozlowski 				info->regs->regs_count);
5985bccae6eSSangbeom Kim 		if (ret < 0)
5995bccae6eSSangbeom Kim 			return ret;
6005bccae6eSSangbeom Kim 		ret = s5m8767_rtc_set_alarm_reg(info);
6015bccae6eSSangbeom Kim 
6025bccae6eSSangbeom Kim 		break;
6035bccae6eSSangbeom Kim 
6045bccae6eSSangbeom Kim 	default:
6055bccae6eSSangbeom Kim 		return -EINVAL;
6065bccae6eSSangbeom Kim 	}
6075bccae6eSSangbeom Kim 
6085bccae6eSSangbeom Kim 	return ret;
6095bccae6eSSangbeom Kim }
6105bccae6eSSangbeom Kim 
6115bccae6eSSangbeom Kim static int s5m_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
6125bccae6eSSangbeom Kim {
6135bccae6eSSangbeom Kim 	struct s5m_rtc_info *info = dev_get_drvdata(dev);
614*756d5282SGustavo A. R. Silva 	u8 data[RTC_MAX_NUM_TIME_REGS];
6155bccae6eSSangbeom Kim 	int ret;
6165bccae6eSSangbeom Kim 
6175bccae6eSSangbeom Kim 	switch (info->device_type) {
6185bccae6eSSangbeom Kim 	case S5M8763X:
6195bccae6eSSangbeom Kim 		s5m8763_tm_to_data(&alrm->time, data);
6205bccae6eSSangbeom Kim 		break;
6215bccae6eSSangbeom Kim 
6225bccae6eSSangbeom Kim 	case S5M8767X:
623a65e5efaSAlim Akhtar 	case S2MPS15X:
6240c5deb1eSKrzysztof Kozlowski 	case S2MPS14X:
6255281f94aSKrzysztof Kozlowski 	case S2MPS13X:
6265bccae6eSSangbeom Kim 		s5m8767_tm_to_data(&alrm->time, data);
6275bccae6eSSangbeom Kim 		break;
6285bccae6eSSangbeom Kim 
6295bccae6eSSangbeom Kim 	default:
6305bccae6eSSangbeom Kim 		return -EINVAL;
6315bccae6eSSangbeom Kim 	}
6325bccae6eSSangbeom Kim 
6335bccae6eSSangbeom Kim 	dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
6345bccae6eSSangbeom Kim 		1900 + alrm->time.tm_year, 1 + alrm->time.tm_mon,
6355bccae6eSSangbeom Kim 		alrm->time.tm_mday, alrm->time.tm_hour, alrm->time.tm_min,
6365bccae6eSSangbeom Kim 		alrm->time.tm_sec, alrm->time.tm_wday);
6375bccae6eSSangbeom Kim 
6385bccae6eSSangbeom Kim 	ret = s5m_rtc_stop_alarm(info);
6395bccae6eSSangbeom Kim 	if (ret < 0)
6405bccae6eSSangbeom Kim 		return ret;
6415bccae6eSSangbeom Kim 
642f8b23bbdSKrzysztof Kozlowski 	ret = regmap_raw_write(info->regmap, info->regs->alarm0, data,
643f8b23bbdSKrzysztof Kozlowski 			info->regs->regs_count);
6445bccae6eSSangbeom Kim 	if (ret < 0)
6455bccae6eSSangbeom Kim 		return ret;
6465bccae6eSSangbeom Kim 
6475bccae6eSSangbeom Kim 	ret = s5m8767_rtc_set_alarm_reg(info);
6485bccae6eSSangbeom Kim 	if (ret < 0)
6495bccae6eSSangbeom Kim 		return ret;
6505bccae6eSSangbeom Kim 
6515bccae6eSSangbeom Kim 	if (alrm->enabled)
6525bccae6eSSangbeom Kim 		ret = s5m_rtc_start_alarm(info);
6535bccae6eSSangbeom Kim 
6545bccae6eSSangbeom Kim 	return ret;
6555bccae6eSSangbeom Kim }
6565bccae6eSSangbeom Kim 
6575bccae6eSSangbeom Kim static int s5m_rtc_alarm_irq_enable(struct device *dev,
6585bccae6eSSangbeom Kim 				    unsigned int enabled)
6595bccae6eSSangbeom Kim {
6605bccae6eSSangbeom Kim 	struct s5m_rtc_info *info = dev_get_drvdata(dev);
6615bccae6eSSangbeom Kim 
6625bccae6eSSangbeom Kim 	if (enabled)
6635bccae6eSSangbeom Kim 		return s5m_rtc_start_alarm(info);
6645bccae6eSSangbeom Kim 	else
6655bccae6eSSangbeom Kim 		return s5m_rtc_stop_alarm(info);
6665bccae6eSSangbeom Kim }
6675bccae6eSSangbeom Kim 
6685bccae6eSSangbeom Kim static irqreturn_t s5m_rtc_alarm_irq(int irq, void *data)
6695bccae6eSSangbeom Kim {
6705bccae6eSSangbeom Kim 	struct s5m_rtc_info *info = data;
6715bccae6eSSangbeom Kim 
6725bccae6eSSangbeom Kim 	rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
6735bccae6eSSangbeom Kim 
6745bccae6eSSangbeom Kim 	return IRQ_HANDLED;
6755bccae6eSSangbeom Kim }
6765bccae6eSSangbeom Kim 
6775bccae6eSSangbeom Kim static const struct rtc_class_ops s5m_rtc_ops = {
6785bccae6eSSangbeom Kim 	.read_time = s5m_rtc_read_time,
6795bccae6eSSangbeom Kim 	.set_time = s5m_rtc_set_time,
6805bccae6eSSangbeom Kim 	.read_alarm = s5m_rtc_read_alarm,
6815bccae6eSSangbeom Kim 	.set_alarm = s5m_rtc_set_alarm,
6825bccae6eSSangbeom Kim 	.alarm_irq_enable = s5m_rtc_alarm_irq_enable,
6835bccae6eSSangbeom Kim };
6845bccae6eSSangbeom Kim 
6855bccae6eSSangbeom Kim static int s5m8767_rtc_init_reg(struct s5m_rtc_info *info)
6865bccae6eSSangbeom Kim {
6875bccae6eSSangbeom Kim 	u8 data[2];
6885bccae6eSSangbeom Kim 	int ret;
6895bccae6eSSangbeom Kim 
6900c5deb1eSKrzysztof Kozlowski 	switch (info->device_type) {
6910c5deb1eSKrzysztof Kozlowski 	case S5M8763X:
6920c5deb1eSKrzysztof Kozlowski 	case S5M8767X:
6930c5f5d9aSKrzysztof Kozlowski 		/* UDR update time. Default of 7.32 ms is too long. */
6940c5f5d9aSKrzysztof Kozlowski 		ret = regmap_update_bits(info->regmap, S5M_RTC_UDR_CON,
6950c5f5d9aSKrzysztof Kozlowski 				S5M_RTC_UDR_T_MASK, S5M_RTC_UDR_T_450_US);
6960c5f5d9aSKrzysztof Kozlowski 		if (ret < 0)
6970c5f5d9aSKrzysztof Kozlowski 			dev_err(info->dev, "%s: fail to change UDR time: %d\n",
6980c5f5d9aSKrzysztof Kozlowski 					__func__, ret);
6990c5f5d9aSKrzysztof Kozlowski 
7005bccae6eSSangbeom Kim 		/* Set RTC control register : Binary mode, 24hour mode */
7015bccae6eSSangbeom Kim 		data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
7025bccae6eSSangbeom Kim 		data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
7035bccae6eSSangbeom Kim 
704602cb5bbSKrzysztof Kozlowski 		ret = regmap_raw_write(info->regmap, S5M_ALARM0_CONF, data, 2);
7050c5deb1eSKrzysztof Kozlowski 		break;
7060c5deb1eSKrzysztof Kozlowski 
707a65e5efaSAlim Akhtar 	case S2MPS15X:
7080c5deb1eSKrzysztof Kozlowski 	case S2MPS14X:
7095281f94aSKrzysztof Kozlowski 	case S2MPS13X:
7100c5deb1eSKrzysztof Kozlowski 		data[0] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
7110c5deb1eSKrzysztof Kozlowski 		ret = regmap_write(info->regmap, info->regs->ctrl, data[0]);
712ff02c044SJoonyoung Shim 		if (ret < 0)
713ff02c044SJoonyoung Shim 			break;
714ff02c044SJoonyoung Shim 
715ff02c044SJoonyoung Shim 		/*
716ff02c044SJoonyoung Shim 		 * Should set WUDR & (RUDR or AUDR) bits to high after writing
717ff02c044SJoonyoung Shim 		 * RTC_CTRL register like writing Alarm registers. We can't find
718ff02c044SJoonyoung Shim 		 * the description from datasheet but vendor code does that
719ff02c044SJoonyoung Shim 		 * really.
720ff02c044SJoonyoung Shim 		 */
721ff02c044SJoonyoung Shim 		ret = s5m8767_rtc_set_alarm_reg(info);
7220c5deb1eSKrzysztof Kozlowski 		break;
7230c5deb1eSKrzysztof Kozlowski 
7240c5deb1eSKrzysztof Kozlowski 	default:
7250c5deb1eSKrzysztof Kozlowski 		return -EINVAL;
7260c5deb1eSKrzysztof Kozlowski 	}
7270c5deb1eSKrzysztof Kozlowski 
7280c5deb1eSKrzysztof Kozlowski 	info->rtc_24hr_mode = 1;
7295bccae6eSSangbeom Kim 	if (ret < 0) {
7305bccae6eSSangbeom Kim 		dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
7315bccae6eSSangbeom Kim 			__func__, ret);
7325bccae6eSSangbeom Kim 		return ret;
7335bccae6eSSangbeom Kim 	}
7345bccae6eSSangbeom Kim 
7355bccae6eSSangbeom Kim 	return ret;
7365bccae6eSSangbeom Kim }
7375bccae6eSSangbeom Kim 
7385bccae6eSSangbeom Kim static int s5m_rtc_probe(struct platform_device *pdev)
7395bccae6eSSangbeom Kim {
7405bccae6eSSangbeom Kim 	struct sec_pmic_dev *s5m87xx = dev_get_drvdata(pdev->dev.parent);
7415bccae6eSSangbeom Kim 	struct sec_platform_data *pdata = s5m87xx->pdata;
7425bccae6eSSangbeom Kim 	struct s5m_rtc_info *info;
743e349c910SKrzysztof Kozlowski 	const struct regmap_config *regmap_cfg;
744a0347f20SKrzysztof Kozlowski 	int ret, alarm_irq;
7455bccae6eSSangbeom Kim 
7465bccae6eSSangbeom Kim 	if (!pdata) {
7475bccae6eSSangbeom Kim 		dev_err(pdev->dev.parent, "Platform data not supplied\n");
7485bccae6eSSangbeom Kim 		return -ENODEV;
7495bccae6eSSangbeom Kim 	}
7505bccae6eSSangbeom Kim 
7515bccae6eSSangbeom Kim 	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
7525bccae6eSSangbeom Kim 	if (!info)
7535bccae6eSSangbeom Kim 		return -ENOMEM;
7545bccae6eSSangbeom Kim 
75594f91922SKrzysztof Kozlowski 	switch (platform_get_device_id(pdev)->driver_data) {
756a65e5efaSAlim Akhtar 	case S2MPS15X:
7578ae83b6fSKrzysztof Kozlowski 		regmap_cfg = &s2mps14_rtc_regmap_config;
7588ae83b6fSKrzysztof Kozlowski 		info->regs = &s2mps15_rtc_regs;
7598ae83b6fSKrzysztof Kozlowski 		alarm_irq = S2MPS14_IRQ_RTCA0;
7608ae83b6fSKrzysztof Kozlowski 		break;
761e349c910SKrzysztof Kozlowski 	case S2MPS14X:
7628ae83b6fSKrzysztof Kozlowski 		regmap_cfg = &s2mps14_rtc_regmap_config;
7638ae83b6fSKrzysztof Kozlowski 		info->regs = &s2mps14_rtc_regs;
7648ae83b6fSKrzysztof Kozlowski 		alarm_irq = S2MPS14_IRQ_RTCA0;
7658ae83b6fSKrzysztof Kozlowski 		break;
7665281f94aSKrzysztof Kozlowski 	case S2MPS13X:
767e349c910SKrzysztof Kozlowski 		regmap_cfg = &s2mps14_rtc_regmap_config;
7688ae83b6fSKrzysztof Kozlowski 		info->regs = &s2mps13_rtc_regs;
769a0347f20SKrzysztof Kozlowski 		alarm_irq = S2MPS14_IRQ_RTCA0;
770e349c910SKrzysztof Kozlowski 		break;
771e349c910SKrzysztof Kozlowski 	case S5M8763X:
772e349c910SKrzysztof Kozlowski 		regmap_cfg = &s5m_rtc_regmap_config;
773f8b23bbdSKrzysztof Kozlowski 		info->regs = &s5m_rtc_regs;
774a0347f20SKrzysztof Kozlowski 		alarm_irq = S5M8763_IRQ_ALARM0;
775e349c910SKrzysztof Kozlowski 		break;
776e349c910SKrzysztof Kozlowski 	case S5M8767X:
777e349c910SKrzysztof Kozlowski 		regmap_cfg = &s5m_rtc_regmap_config;
778f8b23bbdSKrzysztof Kozlowski 		info->regs = &s5m_rtc_regs;
779a0347f20SKrzysztof Kozlowski 		alarm_irq = S5M8767_IRQ_RTCA1;
780e349c910SKrzysztof Kozlowski 		break;
781e349c910SKrzysztof Kozlowski 	default:
78294f91922SKrzysztof Kozlowski 		dev_err(&pdev->dev,
78394f91922SKrzysztof Kozlowski 				"Device type %lu is not supported by RTC driver\n",
78494f91922SKrzysztof Kozlowski 				platform_get_device_id(pdev)->driver_data);
785e349c910SKrzysztof Kozlowski 		return -ENODEV;
786e349c910SKrzysztof Kozlowski 	}
787e349c910SKrzysztof Kozlowski 
788e349c910SKrzysztof Kozlowski 	info->i2c = i2c_new_dummy(s5m87xx->i2c->adapter, RTC_I2C_ADDR);
789e349c910SKrzysztof Kozlowski 	if (!info->i2c) {
790e349c910SKrzysztof Kozlowski 		dev_err(&pdev->dev, "Failed to allocate I2C for RTC\n");
791e349c910SKrzysztof Kozlowski 		return -ENODEV;
792e349c910SKrzysztof Kozlowski 	}
793e349c910SKrzysztof Kozlowski 
794e349c910SKrzysztof Kozlowski 	info->regmap = devm_regmap_init_i2c(info->i2c, regmap_cfg);
795e349c910SKrzysztof Kozlowski 	if (IS_ERR(info->regmap)) {
796e349c910SKrzysztof Kozlowski 		ret = PTR_ERR(info->regmap);
797e349c910SKrzysztof Kozlowski 		dev_err(&pdev->dev, "Failed to allocate RTC register map: %d\n",
798e349c910SKrzysztof Kozlowski 				ret);
799e349c910SKrzysztof Kozlowski 		goto err;
800e349c910SKrzysztof Kozlowski 	}
801e349c910SKrzysztof Kozlowski 
8025bccae6eSSangbeom Kim 	info->dev = &pdev->dev;
8035bccae6eSSangbeom Kim 	info->s5m87xx = s5m87xx;
80494f91922SKrzysztof Kozlowski 	info->device_type = platform_get_device_id(pdev)->driver_data;
8055bccae6eSSangbeom Kim 
806b7d5b9a9SBartlomiej Zolnierkiewicz 	if (s5m87xx->irq_data) {
807a0347f20SKrzysztof Kozlowski 		info->irq = regmap_irq_get_virq(s5m87xx->irq_data, alarm_irq);
808a0347f20SKrzysztof Kozlowski 		if (info->irq <= 0) {
8095bccae6eSSangbeom Kim 			ret = -EINVAL;
810a0347f20SKrzysztof Kozlowski 			dev_err(&pdev->dev, "Failed to get virtual IRQ %d\n",
811a0347f20SKrzysztof Kozlowski 				alarm_irq);
812e349c910SKrzysztof Kozlowski 			goto err;
8135bccae6eSSangbeom Kim 		}
814b7d5b9a9SBartlomiej Zolnierkiewicz 	}
8155bccae6eSSangbeom Kim 
8165bccae6eSSangbeom Kim 	platform_set_drvdata(pdev, info);
8175bccae6eSSangbeom Kim 
8185bccae6eSSangbeom Kim 	ret = s5m8767_rtc_init_reg(info);
8195bccae6eSSangbeom Kim 
8205bccae6eSSangbeom Kim 	device_init_wakeup(&pdev->dev, 1);
8215bccae6eSSangbeom Kim 
8225bccae6eSSangbeom Kim 	info->rtc_dev = devm_rtc_device_register(&pdev->dev, "s5m-rtc",
8235bccae6eSSangbeom Kim 						 &s5m_rtc_ops, THIS_MODULE);
8245bccae6eSSangbeom Kim 
825e349c910SKrzysztof Kozlowski 	if (IS_ERR(info->rtc_dev)) {
826e349c910SKrzysztof Kozlowski 		ret = PTR_ERR(info->rtc_dev);
827e349c910SKrzysztof Kozlowski 		goto err;
828e349c910SKrzysztof Kozlowski 	}
8295bccae6eSSangbeom Kim 
830b7d5b9a9SBartlomiej Zolnierkiewicz 	if (!info->irq) {
831b7d5b9a9SBartlomiej Zolnierkiewicz 		dev_info(&pdev->dev, "Alarm IRQ not available\n");
832b7d5b9a9SBartlomiej Zolnierkiewicz 		return 0;
833b7d5b9a9SBartlomiej Zolnierkiewicz 	}
834b7d5b9a9SBartlomiej Zolnierkiewicz 
8355bccae6eSSangbeom Kim 	ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
8365bccae6eSSangbeom Kim 					s5m_rtc_alarm_irq, 0, "rtc-alarm0",
8375bccae6eSSangbeom Kim 					info);
838e349c910SKrzysztof Kozlowski 	if (ret < 0) {
8395bccae6eSSangbeom Kim 		dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
8405bccae6eSSangbeom Kim 			info->irq, ret);
841e349c910SKrzysztof Kozlowski 		goto err;
842e349c910SKrzysztof Kozlowski 	}
843e349c910SKrzysztof Kozlowski 
844e349c910SKrzysztof Kozlowski 	return 0;
845e349c910SKrzysztof Kozlowski 
846e349c910SKrzysztof Kozlowski err:
847e349c910SKrzysztof Kozlowski 	i2c_unregister_device(info->i2c);
8485bccae6eSSangbeom Kim 
8495bccae6eSSangbeom Kim 	return ret;
8505bccae6eSSangbeom Kim }
8515bccae6eSSangbeom Kim 
852e349c910SKrzysztof Kozlowski static int s5m_rtc_remove(struct platform_device *pdev)
853e349c910SKrzysztof Kozlowski {
854e349c910SKrzysztof Kozlowski 	struct s5m_rtc_info *info = platform_get_drvdata(pdev);
855e349c910SKrzysztof Kozlowski 
856e349c910SKrzysztof Kozlowski 	i2c_unregister_device(info->i2c);
857e349c910SKrzysztof Kozlowski 
858e349c910SKrzysztof Kozlowski 	return 0;
859e349c910SKrzysztof Kozlowski }
860e349c910SKrzysztof Kozlowski 
86111ba5a1eSGeert Uytterhoeven #ifdef CONFIG_PM_SLEEP
862222ead7fSKrzysztof Kozlowski static int s5m_rtc_resume(struct device *dev)
863222ead7fSKrzysztof Kozlowski {
864222ead7fSKrzysztof Kozlowski 	struct s5m_rtc_info *info = dev_get_drvdata(dev);
865222ead7fSKrzysztof Kozlowski 	int ret = 0;
866222ead7fSKrzysztof Kozlowski 
867b7d5b9a9SBartlomiej Zolnierkiewicz 	if (info->irq && device_may_wakeup(dev))
868222ead7fSKrzysztof Kozlowski 		ret = disable_irq_wake(info->irq);
869222ead7fSKrzysztof Kozlowski 
870222ead7fSKrzysztof Kozlowski 	return ret;
871222ead7fSKrzysztof Kozlowski }
872222ead7fSKrzysztof Kozlowski 
873222ead7fSKrzysztof Kozlowski static int s5m_rtc_suspend(struct device *dev)
874222ead7fSKrzysztof Kozlowski {
875222ead7fSKrzysztof Kozlowski 	struct s5m_rtc_info *info = dev_get_drvdata(dev);
876222ead7fSKrzysztof Kozlowski 	int ret = 0;
877222ead7fSKrzysztof Kozlowski 
878b7d5b9a9SBartlomiej Zolnierkiewicz 	if (info->irq && device_may_wakeup(dev))
879222ead7fSKrzysztof Kozlowski 		ret = enable_irq_wake(info->irq);
880222ead7fSKrzysztof Kozlowski 
881222ead7fSKrzysztof Kozlowski 	return ret;
882222ead7fSKrzysztof Kozlowski }
88311ba5a1eSGeert Uytterhoeven #endif /* CONFIG_PM_SLEEP */
884222ead7fSKrzysztof Kozlowski 
885222ead7fSKrzysztof Kozlowski static SIMPLE_DEV_PM_OPS(s5m_rtc_pm_ops, s5m_rtc_suspend, s5m_rtc_resume);
886222ead7fSKrzysztof Kozlowski 
8875bccae6eSSangbeom Kim static const struct platform_device_id s5m_rtc_id[] = {
8880c5deb1eSKrzysztof Kozlowski 	{ "s5m-rtc",		S5M8767X },
8895281f94aSKrzysztof Kozlowski 	{ "s2mps13-rtc",	S2MPS13X },
8900c5deb1eSKrzysztof Kozlowski 	{ "s2mps14-rtc",	S2MPS14X },
891a65e5efaSAlim Akhtar 	{ "s2mps15-rtc",	S2MPS15X },
89245cd15e6SAndrey Ryabinin 	{ },
8935bccae6eSSangbeom Kim };
89463074cc3SJavier Martinez Canillas MODULE_DEVICE_TABLE(platform, s5m_rtc_id);
8955bccae6eSSangbeom Kim 
8965bccae6eSSangbeom Kim static struct platform_driver s5m_rtc_driver = {
8975bccae6eSSangbeom Kim 	.driver		= {
8985bccae6eSSangbeom Kim 		.name	= "s5m-rtc",
899222ead7fSKrzysztof Kozlowski 		.pm	= &s5m_rtc_pm_ops,
9005bccae6eSSangbeom Kim 	},
9015bccae6eSSangbeom Kim 	.probe		= s5m_rtc_probe,
902e349c910SKrzysztof Kozlowski 	.remove		= s5m_rtc_remove,
9035bccae6eSSangbeom Kim 	.id_table	= s5m_rtc_id,
9045bccae6eSSangbeom Kim };
9055bccae6eSSangbeom Kim 
9065bccae6eSSangbeom Kim module_platform_driver(s5m_rtc_driver);
9075bccae6eSSangbeom Kim 
9085bccae6eSSangbeom Kim /* Module information */
9095bccae6eSSangbeom Kim MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
9100c5deb1eSKrzysztof Kozlowski MODULE_DESCRIPTION("Samsung S5M/S2MPS14 RTC driver");
9115bccae6eSSangbeom Kim MODULE_LICENSE("GPL");
9125bccae6eSSangbeom Kim MODULE_ALIAS("platform:s5m-rtc");
913