xref: /linux/drivers/rtc/rtc-s5m.c (revision 5bccae6ec4587044779f0b8e6fcb8f87db4181f0)
1*5bccae6eSSangbeom Kim /*
2*5bccae6eSSangbeom Kim  * Copyright (c) 2013 Samsung Electronics Co., Ltd
3*5bccae6eSSangbeom Kim  *	http://www.samsung.com
4*5bccae6eSSangbeom Kim  *
5*5bccae6eSSangbeom Kim  *  Copyright (C) 2013 Google, Inc
6*5bccae6eSSangbeom Kim  *
7*5bccae6eSSangbeom Kim  *  This program is free software; you can redistribute it and/or modify
8*5bccae6eSSangbeom Kim  *  it under the terms of the GNU General Public License as published by
9*5bccae6eSSangbeom Kim  *  the Free Software Foundation; either version 2 of the License, or
10*5bccae6eSSangbeom Kim  *  (at your option) any later version.
11*5bccae6eSSangbeom Kim  *
12*5bccae6eSSangbeom Kim  *  This program is distributed in the hope that it will be useful,
13*5bccae6eSSangbeom Kim  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14*5bccae6eSSangbeom Kim  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15*5bccae6eSSangbeom Kim  *  GNU General Public License for more details.
16*5bccae6eSSangbeom Kim  */
17*5bccae6eSSangbeom Kim 
18*5bccae6eSSangbeom Kim #include <linux/module.h>
19*5bccae6eSSangbeom Kim #include <linux/i2c.h>
20*5bccae6eSSangbeom Kim #include <linux/slab.h>
21*5bccae6eSSangbeom Kim #include <linux/bcd.h>
22*5bccae6eSSangbeom Kim #include <linux/bitops.h>
23*5bccae6eSSangbeom Kim #include <linux/regmap.h>
24*5bccae6eSSangbeom Kim #include <linux/rtc.h>
25*5bccae6eSSangbeom Kim #include <linux/delay.h>
26*5bccae6eSSangbeom Kim #include <linux/platform_device.h>
27*5bccae6eSSangbeom Kim #include <linux/mfd/samsung/core.h>
28*5bccae6eSSangbeom Kim #include <linux/mfd/samsung/irq.h>
29*5bccae6eSSangbeom Kim #include <linux/mfd/samsung/rtc.h>
30*5bccae6eSSangbeom Kim 
31*5bccae6eSSangbeom Kim struct s5m_rtc_info {
32*5bccae6eSSangbeom Kim 	struct device *dev;
33*5bccae6eSSangbeom Kim 	struct sec_pmic_dev *s5m87xx;
34*5bccae6eSSangbeom Kim 	struct regmap *rtc;
35*5bccae6eSSangbeom Kim 	struct rtc_device *rtc_dev;
36*5bccae6eSSangbeom Kim 	int irq;
37*5bccae6eSSangbeom Kim 	int device_type;
38*5bccae6eSSangbeom Kim 	int rtc_24hr_mode;
39*5bccae6eSSangbeom Kim 	bool wtsr_smpl;
40*5bccae6eSSangbeom Kim };
41*5bccae6eSSangbeom Kim 
42*5bccae6eSSangbeom Kim static void s5m8767_data_to_tm(u8 *data, struct rtc_time *tm,
43*5bccae6eSSangbeom Kim 			       int rtc_24hr_mode)
44*5bccae6eSSangbeom Kim {
45*5bccae6eSSangbeom Kim 	tm->tm_sec = data[RTC_SEC] & 0x7f;
46*5bccae6eSSangbeom Kim 	tm->tm_min = data[RTC_MIN] & 0x7f;
47*5bccae6eSSangbeom Kim 	if (rtc_24hr_mode) {
48*5bccae6eSSangbeom Kim 		tm->tm_hour = data[RTC_HOUR] & 0x1f;
49*5bccae6eSSangbeom Kim 	} else {
50*5bccae6eSSangbeom Kim 		tm->tm_hour = data[RTC_HOUR] & 0x0f;
51*5bccae6eSSangbeom Kim 		if (data[RTC_HOUR] & HOUR_PM_MASK)
52*5bccae6eSSangbeom Kim 			tm->tm_hour += 12;
53*5bccae6eSSangbeom Kim 	}
54*5bccae6eSSangbeom Kim 
55*5bccae6eSSangbeom Kim 	tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0x7f);
56*5bccae6eSSangbeom Kim 	tm->tm_mday = data[RTC_DATE] & 0x1f;
57*5bccae6eSSangbeom Kim 	tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
58*5bccae6eSSangbeom Kim 	tm->tm_year = (data[RTC_YEAR1] & 0x7f) + 100;
59*5bccae6eSSangbeom Kim 	tm->tm_yday = 0;
60*5bccae6eSSangbeom Kim 	tm->tm_isdst = 0;
61*5bccae6eSSangbeom Kim }
62*5bccae6eSSangbeom Kim 
63*5bccae6eSSangbeom Kim static int s5m8767_tm_to_data(struct rtc_time *tm, u8 *data)
64*5bccae6eSSangbeom Kim {
65*5bccae6eSSangbeom Kim 	data[RTC_SEC] = tm->tm_sec;
66*5bccae6eSSangbeom Kim 	data[RTC_MIN] = tm->tm_min;
67*5bccae6eSSangbeom Kim 
68*5bccae6eSSangbeom Kim 	if (tm->tm_hour >= 12)
69*5bccae6eSSangbeom Kim 		data[RTC_HOUR] = tm->tm_hour | HOUR_PM_MASK;
70*5bccae6eSSangbeom Kim 	else
71*5bccae6eSSangbeom Kim 		data[RTC_HOUR] = tm->tm_hour & ~HOUR_PM_MASK;
72*5bccae6eSSangbeom Kim 
73*5bccae6eSSangbeom Kim 	data[RTC_WEEKDAY] = 1 << tm->tm_wday;
74*5bccae6eSSangbeom Kim 	data[RTC_DATE] = tm->tm_mday;
75*5bccae6eSSangbeom Kim 	data[RTC_MONTH] = tm->tm_mon + 1;
76*5bccae6eSSangbeom Kim 	data[RTC_YEAR1] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0;
77*5bccae6eSSangbeom Kim 
78*5bccae6eSSangbeom Kim 	if (tm->tm_year < 100) {
79*5bccae6eSSangbeom Kim 		pr_err("s5m8767 RTC cannot handle the year %d.\n",
80*5bccae6eSSangbeom Kim 		       1900 + tm->tm_year);
81*5bccae6eSSangbeom Kim 		return -EINVAL;
82*5bccae6eSSangbeom Kim 	} else {
83*5bccae6eSSangbeom Kim 		return 0;
84*5bccae6eSSangbeom Kim 	}
85*5bccae6eSSangbeom Kim }
86*5bccae6eSSangbeom Kim 
87*5bccae6eSSangbeom Kim static inline int s5m8767_rtc_set_time_reg(struct s5m_rtc_info *info)
88*5bccae6eSSangbeom Kim {
89*5bccae6eSSangbeom Kim 	int ret;
90*5bccae6eSSangbeom Kim 	unsigned int data;
91*5bccae6eSSangbeom Kim 
92*5bccae6eSSangbeom Kim 	ret = regmap_read(info->rtc, SEC_RTC_UDR_CON, &data);
93*5bccae6eSSangbeom Kim 	if (ret < 0) {
94*5bccae6eSSangbeom Kim 		dev_err(info->dev, "failed to read update reg(%d)\n", ret);
95*5bccae6eSSangbeom Kim 		return ret;
96*5bccae6eSSangbeom Kim 	}
97*5bccae6eSSangbeom Kim 
98*5bccae6eSSangbeom Kim 	data |= RTC_TIME_EN_MASK;
99*5bccae6eSSangbeom Kim 	data |= RTC_UDR_MASK;
100*5bccae6eSSangbeom Kim 
101*5bccae6eSSangbeom Kim 	ret = regmap_write(info->rtc, SEC_RTC_UDR_CON, data);
102*5bccae6eSSangbeom Kim 	if (ret < 0) {
103*5bccae6eSSangbeom Kim 		dev_err(info->dev, "failed to write update reg(%d)\n", ret);
104*5bccae6eSSangbeom Kim 		return ret;
105*5bccae6eSSangbeom Kim 	}
106*5bccae6eSSangbeom Kim 
107*5bccae6eSSangbeom Kim 	do {
108*5bccae6eSSangbeom Kim 		ret = regmap_read(info->rtc, SEC_RTC_UDR_CON, &data);
109*5bccae6eSSangbeom Kim 	} while ((data & RTC_UDR_MASK) && !ret);
110*5bccae6eSSangbeom Kim 
111*5bccae6eSSangbeom Kim 	return ret;
112*5bccae6eSSangbeom Kim }
113*5bccae6eSSangbeom Kim 
114*5bccae6eSSangbeom Kim static inline int s5m8767_rtc_set_alarm_reg(struct s5m_rtc_info *info)
115*5bccae6eSSangbeom Kim {
116*5bccae6eSSangbeom Kim 	int ret;
117*5bccae6eSSangbeom Kim 	unsigned int data;
118*5bccae6eSSangbeom Kim 
119*5bccae6eSSangbeom Kim 	ret = regmap_read(info->rtc, SEC_RTC_UDR_CON, &data);
120*5bccae6eSSangbeom Kim 	if (ret < 0) {
121*5bccae6eSSangbeom Kim 		dev_err(info->dev, "%s: fail to read update reg(%d)\n",
122*5bccae6eSSangbeom Kim 			__func__, ret);
123*5bccae6eSSangbeom Kim 		return ret;
124*5bccae6eSSangbeom Kim 	}
125*5bccae6eSSangbeom Kim 
126*5bccae6eSSangbeom Kim 	data &= ~RTC_TIME_EN_MASK;
127*5bccae6eSSangbeom Kim 	data |= RTC_UDR_MASK;
128*5bccae6eSSangbeom Kim 
129*5bccae6eSSangbeom Kim 	ret = regmap_write(info->rtc, SEC_RTC_UDR_CON, data);
130*5bccae6eSSangbeom Kim 	if (ret < 0) {
131*5bccae6eSSangbeom Kim 		dev_err(info->dev, "%s: fail to write update reg(%d)\n",
132*5bccae6eSSangbeom Kim 			__func__, ret);
133*5bccae6eSSangbeom Kim 		return ret;
134*5bccae6eSSangbeom Kim 	}
135*5bccae6eSSangbeom Kim 
136*5bccae6eSSangbeom Kim 	do {
137*5bccae6eSSangbeom Kim 		ret = regmap_read(info->rtc, SEC_RTC_UDR_CON, &data);
138*5bccae6eSSangbeom Kim 	} while ((data & RTC_UDR_MASK) && !ret);
139*5bccae6eSSangbeom Kim 
140*5bccae6eSSangbeom Kim 	return ret;
141*5bccae6eSSangbeom Kim }
142*5bccae6eSSangbeom Kim 
143*5bccae6eSSangbeom Kim static void s5m8763_data_to_tm(u8 *data, struct rtc_time *tm)
144*5bccae6eSSangbeom Kim {
145*5bccae6eSSangbeom Kim 	tm->tm_sec = bcd2bin(data[RTC_SEC]);
146*5bccae6eSSangbeom Kim 	tm->tm_min = bcd2bin(data[RTC_MIN]);
147*5bccae6eSSangbeom Kim 
148*5bccae6eSSangbeom Kim 	if (data[RTC_HOUR] & HOUR_12) {
149*5bccae6eSSangbeom Kim 		tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x1f);
150*5bccae6eSSangbeom Kim 		if (data[RTC_HOUR] & HOUR_PM)
151*5bccae6eSSangbeom Kim 			tm->tm_hour += 12;
152*5bccae6eSSangbeom Kim 	} else {
153*5bccae6eSSangbeom Kim 		tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3f);
154*5bccae6eSSangbeom Kim 	}
155*5bccae6eSSangbeom Kim 
156*5bccae6eSSangbeom Kim 	tm->tm_wday = data[RTC_WEEKDAY] & 0x07;
157*5bccae6eSSangbeom Kim 	tm->tm_mday = bcd2bin(data[RTC_DATE]);
158*5bccae6eSSangbeom Kim 	tm->tm_mon = bcd2bin(data[RTC_MONTH]);
159*5bccae6eSSangbeom Kim 	tm->tm_year = bcd2bin(data[RTC_YEAR1]) + bcd2bin(data[RTC_YEAR2]) * 100;
160*5bccae6eSSangbeom Kim 	tm->tm_year -= 1900;
161*5bccae6eSSangbeom Kim }
162*5bccae6eSSangbeom Kim 
163*5bccae6eSSangbeom Kim static void s5m8763_tm_to_data(struct rtc_time *tm, u8 *data)
164*5bccae6eSSangbeom Kim {
165*5bccae6eSSangbeom Kim 	data[RTC_SEC] = bin2bcd(tm->tm_sec);
166*5bccae6eSSangbeom Kim 	data[RTC_MIN] = bin2bcd(tm->tm_min);
167*5bccae6eSSangbeom Kim 	data[RTC_HOUR] = bin2bcd(tm->tm_hour);
168*5bccae6eSSangbeom Kim 	data[RTC_WEEKDAY] = tm->tm_wday;
169*5bccae6eSSangbeom Kim 	data[RTC_DATE] = bin2bcd(tm->tm_mday);
170*5bccae6eSSangbeom Kim 	data[RTC_MONTH] = bin2bcd(tm->tm_mon);
171*5bccae6eSSangbeom Kim 	data[RTC_YEAR1] = bin2bcd(tm->tm_year % 100);
172*5bccae6eSSangbeom Kim 	data[RTC_YEAR2] = bin2bcd((tm->tm_year + 1900) / 100);
173*5bccae6eSSangbeom Kim }
174*5bccae6eSSangbeom Kim 
175*5bccae6eSSangbeom Kim static int s5m_rtc_read_time(struct device *dev, struct rtc_time *tm)
176*5bccae6eSSangbeom Kim {
177*5bccae6eSSangbeom Kim 	struct s5m_rtc_info *info = dev_get_drvdata(dev);
178*5bccae6eSSangbeom Kim 	u8 data[8];
179*5bccae6eSSangbeom Kim 	int ret;
180*5bccae6eSSangbeom Kim 
181*5bccae6eSSangbeom Kim 	ret = regmap_bulk_read(info->rtc, SEC_RTC_SEC, data, 8);
182*5bccae6eSSangbeom Kim 	if (ret < 0)
183*5bccae6eSSangbeom Kim 		return ret;
184*5bccae6eSSangbeom Kim 
185*5bccae6eSSangbeom Kim 	switch (info->device_type) {
186*5bccae6eSSangbeom Kim 	case S5M8763X:
187*5bccae6eSSangbeom Kim 		s5m8763_data_to_tm(data, tm);
188*5bccae6eSSangbeom Kim 		break;
189*5bccae6eSSangbeom Kim 
190*5bccae6eSSangbeom Kim 	case S5M8767X:
191*5bccae6eSSangbeom Kim 		s5m8767_data_to_tm(data, tm, info->rtc_24hr_mode);
192*5bccae6eSSangbeom Kim 		break;
193*5bccae6eSSangbeom Kim 
194*5bccae6eSSangbeom Kim 	default:
195*5bccae6eSSangbeom Kim 		return -EINVAL;
196*5bccae6eSSangbeom Kim 	}
197*5bccae6eSSangbeom Kim 
198*5bccae6eSSangbeom Kim 	dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
199*5bccae6eSSangbeom Kim 		1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday,
200*5bccae6eSSangbeom Kim 		tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
201*5bccae6eSSangbeom Kim 
202*5bccae6eSSangbeom Kim 	return rtc_valid_tm(tm);
203*5bccae6eSSangbeom Kim }
204*5bccae6eSSangbeom Kim 
205*5bccae6eSSangbeom Kim static int s5m_rtc_set_time(struct device *dev, struct rtc_time *tm)
206*5bccae6eSSangbeom Kim {
207*5bccae6eSSangbeom Kim 	struct s5m_rtc_info *info = dev_get_drvdata(dev);
208*5bccae6eSSangbeom Kim 	u8 data[8];
209*5bccae6eSSangbeom Kim 	int ret = 0;
210*5bccae6eSSangbeom Kim 
211*5bccae6eSSangbeom Kim 	switch (info->device_type) {
212*5bccae6eSSangbeom Kim 	case S5M8763X:
213*5bccae6eSSangbeom Kim 		s5m8763_tm_to_data(tm, data);
214*5bccae6eSSangbeom Kim 		break;
215*5bccae6eSSangbeom Kim 	case S5M8767X:
216*5bccae6eSSangbeom Kim 		ret = s5m8767_tm_to_data(tm, data);
217*5bccae6eSSangbeom Kim 		break;
218*5bccae6eSSangbeom Kim 	default:
219*5bccae6eSSangbeom Kim 		return -EINVAL;
220*5bccae6eSSangbeom Kim 	}
221*5bccae6eSSangbeom Kim 
222*5bccae6eSSangbeom Kim 	if (ret < 0)
223*5bccae6eSSangbeom Kim 		return ret;
224*5bccae6eSSangbeom Kim 
225*5bccae6eSSangbeom Kim 	dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
226*5bccae6eSSangbeom Kim 		1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday,
227*5bccae6eSSangbeom Kim 		tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
228*5bccae6eSSangbeom Kim 
229*5bccae6eSSangbeom Kim 	ret = regmap_raw_write(info->rtc, SEC_RTC_SEC, data, 8);
230*5bccae6eSSangbeom Kim 	if (ret < 0)
231*5bccae6eSSangbeom Kim 		return ret;
232*5bccae6eSSangbeom Kim 
233*5bccae6eSSangbeom Kim 	ret = s5m8767_rtc_set_time_reg(info);
234*5bccae6eSSangbeom Kim 
235*5bccae6eSSangbeom Kim 	return ret;
236*5bccae6eSSangbeom Kim }
237*5bccae6eSSangbeom Kim 
238*5bccae6eSSangbeom Kim static int s5m_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
239*5bccae6eSSangbeom Kim {
240*5bccae6eSSangbeom Kim 	struct s5m_rtc_info *info = dev_get_drvdata(dev);
241*5bccae6eSSangbeom Kim 	u8 data[8];
242*5bccae6eSSangbeom Kim 	unsigned int val;
243*5bccae6eSSangbeom Kim 	int ret, i;
244*5bccae6eSSangbeom Kim 
245*5bccae6eSSangbeom Kim 	ret = regmap_bulk_read(info->rtc, SEC_ALARM0_SEC, data, 8);
246*5bccae6eSSangbeom Kim 	if (ret < 0)
247*5bccae6eSSangbeom Kim 		return ret;
248*5bccae6eSSangbeom Kim 
249*5bccae6eSSangbeom Kim 	switch (info->device_type) {
250*5bccae6eSSangbeom Kim 	case S5M8763X:
251*5bccae6eSSangbeom Kim 		s5m8763_data_to_tm(data, &alrm->time);
252*5bccae6eSSangbeom Kim 		ret = regmap_read(info->rtc, SEC_ALARM0_CONF, &val);
253*5bccae6eSSangbeom Kim 		if (ret < 0)
254*5bccae6eSSangbeom Kim 			return ret;
255*5bccae6eSSangbeom Kim 
256*5bccae6eSSangbeom Kim 		alrm->enabled = !!val;
257*5bccae6eSSangbeom Kim 
258*5bccae6eSSangbeom Kim 		ret = regmap_read(info->rtc, SEC_RTC_STATUS, &val);
259*5bccae6eSSangbeom Kim 		if (ret < 0)
260*5bccae6eSSangbeom Kim 			return ret;
261*5bccae6eSSangbeom Kim 
262*5bccae6eSSangbeom Kim 		break;
263*5bccae6eSSangbeom Kim 
264*5bccae6eSSangbeom Kim 	case S5M8767X:
265*5bccae6eSSangbeom Kim 		s5m8767_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
266*5bccae6eSSangbeom Kim 		dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
267*5bccae6eSSangbeom Kim 			1900 + alrm->time.tm_year, 1 + alrm->time.tm_mon,
268*5bccae6eSSangbeom Kim 			alrm->time.tm_mday, alrm->time.tm_hour,
269*5bccae6eSSangbeom Kim 			alrm->time.tm_min, alrm->time.tm_sec,
270*5bccae6eSSangbeom Kim 			alrm->time.tm_wday);
271*5bccae6eSSangbeom Kim 
272*5bccae6eSSangbeom Kim 		alrm->enabled = 0;
273*5bccae6eSSangbeom Kim 		for (i = 0; i < 7; i++) {
274*5bccae6eSSangbeom Kim 			if (data[i] & ALARM_ENABLE_MASK) {
275*5bccae6eSSangbeom Kim 				alrm->enabled = 1;
276*5bccae6eSSangbeom Kim 				break;
277*5bccae6eSSangbeom Kim 			}
278*5bccae6eSSangbeom Kim 		}
279*5bccae6eSSangbeom Kim 
280*5bccae6eSSangbeom Kim 		alrm->pending = 0;
281*5bccae6eSSangbeom Kim 		ret = regmap_read(info->rtc, SEC_RTC_STATUS, &val);
282*5bccae6eSSangbeom Kim 		if (ret < 0)
283*5bccae6eSSangbeom Kim 			return ret;
284*5bccae6eSSangbeom Kim 		break;
285*5bccae6eSSangbeom Kim 
286*5bccae6eSSangbeom Kim 	default:
287*5bccae6eSSangbeom Kim 		return -EINVAL;
288*5bccae6eSSangbeom Kim 	}
289*5bccae6eSSangbeom Kim 
290*5bccae6eSSangbeom Kim 	if (val & ALARM0_STATUS)
291*5bccae6eSSangbeom Kim 		alrm->pending = 1;
292*5bccae6eSSangbeom Kim 	else
293*5bccae6eSSangbeom Kim 		alrm->pending = 0;
294*5bccae6eSSangbeom Kim 
295*5bccae6eSSangbeom Kim 	return 0;
296*5bccae6eSSangbeom Kim }
297*5bccae6eSSangbeom Kim 
298*5bccae6eSSangbeom Kim static int s5m_rtc_stop_alarm(struct s5m_rtc_info *info)
299*5bccae6eSSangbeom Kim {
300*5bccae6eSSangbeom Kim 	u8 data[8];
301*5bccae6eSSangbeom Kim 	int ret, i;
302*5bccae6eSSangbeom Kim 	struct rtc_time tm;
303*5bccae6eSSangbeom Kim 
304*5bccae6eSSangbeom Kim 	ret = regmap_bulk_read(info->rtc, SEC_ALARM0_SEC, data, 8);
305*5bccae6eSSangbeom Kim 	if (ret < 0)
306*5bccae6eSSangbeom Kim 		return ret;
307*5bccae6eSSangbeom Kim 
308*5bccae6eSSangbeom Kim 	s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode);
309*5bccae6eSSangbeom Kim 	dev_dbg(info->dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
310*5bccae6eSSangbeom Kim 		1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday,
311*5bccae6eSSangbeom Kim 		tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday);
312*5bccae6eSSangbeom Kim 
313*5bccae6eSSangbeom Kim 	switch (info->device_type) {
314*5bccae6eSSangbeom Kim 	case S5M8763X:
315*5bccae6eSSangbeom Kim 		ret = regmap_write(info->rtc, SEC_ALARM0_CONF, 0);
316*5bccae6eSSangbeom Kim 		break;
317*5bccae6eSSangbeom Kim 
318*5bccae6eSSangbeom Kim 	case S5M8767X:
319*5bccae6eSSangbeom Kim 		for (i = 0; i < 7; i++)
320*5bccae6eSSangbeom Kim 			data[i] &= ~ALARM_ENABLE_MASK;
321*5bccae6eSSangbeom Kim 
322*5bccae6eSSangbeom Kim 		ret = regmap_raw_write(info->rtc, SEC_ALARM0_SEC, data, 8);
323*5bccae6eSSangbeom Kim 		if (ret < 0)
324*5bccae6eSSangbeom Kim 			return ret;
325*5bccae6eSSangbeom Kim 
326*5bccae6eSSangbeom Kim 		ret = s5m8767_rtc_set_alarm_reg(info);
327*5bccae6eSSangbeom Kim 
328*5bccae6eSSangbeom Kim 		break;
329*5bccae6eSSangbeom Kim 
330*5bccae6eSSangbeom Kim 	default:
331*5bccae6eSSangbeom Kim 		return -EINVAL;
332*5bccae6eSSangbeom Kim 	}
333*5bccae6eSSangbeom Kim 
334*5bccae6eSSangbeom Kim 	return ret;
335*5bccae6eSSangbeom Kim }
336*5bccae6eSSangbeom Kim 
337*5bccae6eSSangbeom Kim static int s5m_rtc_start_alarm(struct s5m_rtc_info *info)
338*5bccae6eSSangbeom Kim {
339*5bccae6eSSangbeom Kim 	int ret;
340*5bccae6eSSangbeom Kim 	u8 data[8];
341*5bccae6eSSangbeom Kim 	u8 alarm0_conf;
342*5bccae6eSSangbeom Kim 	struct rtc_time tm;
343*5bccae6eSSangbeom Kim 
344*5bccae6eSSangbeom Kim 	ret = regmap_bulk_read(info->rtc, SEC_ALARM0_SEC, data, 8);
345*5bccae6eSSangbeom Kim 	if (ret < 0)
346*5bccae6eSSangbeom Kim 		return ret;
347*5bccae6eSSangbeom Kim 
348*5bccae6eSSangbeom Kim 	s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode);
349*5bccae6eSSangbeom Kim 	dev_dbg(info->dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
350*5bccae6eSSangbeom Kim 		1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday,
351*5bccae6eSSangbeom Kim 		tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday);
352*5bccae6eSSangbeom Kim 
353*5bccae6eSSangbeom Kim 	switch (info->device_type) {
354*5bccae6eSSangbeom Kim 	case S5M8763X:
355*5bccae6eSSangbeom Kim 		alarm0_conf = 0x77;
356*5bccae6eSSangbeom Kim 		ret = regmap_write(info->rtc, SEC_ALARM0_CONF, alarm0_conf);
357*5bccae6eSSangbeom Kim 		break;
358*5bccae6eSSangbeom Kim 
359*5bccae6eSSangbeom Kim 	case S5M8767X:
360*5bccae6eSSangbeom Kim 		data[RTC_SEC] |= ALARM_ENABLE_MASK;
361*5bccae6eSSangbeom Kim 		data[RTC_MIN] |= ALARM_ENABLE_MASK;
362*5bccae6eSSangbeom Kim 		data[RTC_HOUR] |= ALARM_ENABLE_MASK;
363*5bccae6eSSangbeom Kim 		data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK;
364*5bccae6eSSangbeom Kim 		if (data[RTC_DATE] & 0x1f)
365*5bccae6eSSangbeom Kim 			data[RTC_DATE] |= ALARM_ENABLE_MASK;
366*5bccae6eSSangbeom Kim 		if (data[RTC_MONTH] & 0xf)
367*5bccae6eSSangbeom Kim 			data[RTC_MONTH] |= ALARM_ENABLE_MASK;
368*5bccae6eSSangbeom Kim 		if (data[RTC_YEAR1] & 0x7f)
369*5bccae6eSSangbeom Kim 			data[RTC_YEAR1] |= ALARM_ENABLE_MASK;
370*5bccae6eSSangbeom Kim 
371*5bccae6eSSangbeom Kim 		ret = regmap_raw_write(info->rtc, SEC_ALARM0_SEC, data, 8);
372*5bccae6eSSangbeom Kim 		if (ret < 0)
373*5bccae6eSSangbeom Kim 			return ret;
374*5bccae6eSSangbeom Kim 		ret = s5m8767_rtc_set_alarm_reg(info);
375*5bccae6eSSangbeom Kim 
376*5bccae6eSSangbeom Kim 		break;
377*5bccae6eSSangbeom Kim 
378*5bccae6eSSangbeom Kim 	default:
379*5bccae6eSSangbeom Kim 		return -EINVAL;
380*5bccae6eSSangbeom Kim 	}
381*5bccae6eSSangbeom Kim 
382*5bccae6eSSangbeom Kim 	return ret;
383*5bccae6eSSangbeom Kim }
384*5bccae6eSSangbeom Kim 
385*5bccae6eSSangbeom Kim static int s5m_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
386*5bccae6eSSangbeom Kim {
387*5bccae6eSSangbeom Kim 	struct s5m_rtc_info *info = dev_get_drvdata(dev);
388*5bccae6eSSangbeom Kim 	u8 data[8];
389*5bccae6eSSangbeom Kim 	int ret;
390*5bccae6eSSangbeom Kim 
391*5bccae6eSSangbeom Kim 	switch (info->device_type) {
392*5bccae6eSSangbeom Kim 	case S5M8763X:
393*5bccae6eSSangbeom Kim 		s5m8763_tm_to_data(&alrm->time, data);
394*5bccae6eSSangbeom Kim 		break;
395*5bccae6eSSangbeom Kim 
396*5bccae6eSSangbeom Kim 	case S5M8767X:
397*5bccae6eSSangbeom Kim 		s5m8767_tm_to_data(&alrm->time, data);
398*5bccae6eSSangbeom Kim 		break;
399*5bccae6eSSangbeom Kim 
400*5bccae6eSSangbeom Kim 	default:
401*5bccae6eSSangbeom Kim 		return -EINVAL;
402*5bccae6eSSangbeom Kim 	}
403*5bccae6eSSangbeom Kim 
404*5bccae6eSSangbeom Kim 	dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
405*5bccae6eSSangbeom Kim 		1900 + alrm->time.tm_year, 1 + alrm->time.tm_mon,
406*5bccae6eSSangbeom Kim 		alrm->time.tm_mday, alrm->time.tm_hour, alrm->time.tm_min,
407*5bccae6eSSangbeom Kim 		alrm->time.tm_sec, alrm->time.tm_wday);
408*5bccae6eSSangbeom Kim 
409*5bccae6eSSangbeom Kim 	ret = s5m_rtc_stop_alarm(info);
410*5bccae6eSSangbeom Kim 	if (ret < 0)
411*5bccae6eSSangbeom Kim 		return ret;
412*5bccae6eSSangbeom Kim 
413*5bccae6eSSangbeom Kim 	ret = regmap_raw_write(info->rtc, SEC_ALARM0_SEC, data, 8);
414*5bccae6eSSangbeom Kim 	if (ret < 0)
415*5bccae6eSSangbeom Kim 		return ret;
416*5bccae6eSSangbeom Kim 
417*5bccae6eSSangbeom Kim 	ret = s5m8767_rtc_set_alarm_reg(info);
418*5bccae6eSSangbeom Kim 	if (ret < 0)
419*5bccae6eSSangbeom Kim 		return ret;
420*5bccae6eSSangbeom Kim 
421*5bccae6eSSangbeom Kim 	if (alrm->enabled)
422*5bccae6eSSangbeom Kim 		ret = s5m_rtc_start_alarm(info);
423*5bccae6eSSangbeom Kim 
424*5bccae6eSSangbeom Kim 	return ret;
425*5bccae6eSSangbeom Kim }
426*5bccae6eSSangbeom Kim 
427*5bccae6eSSangbeom Kim static int s5m_rtc_alarm_irq_enable(struct device *dev,
428*5bccae6eSSangbeom Kim 				    unsigned int enabled)
429*5bccae6eSSangbeom Kim {
430*5bccae6eSSangbeom Kim 	struct s5m_rtc_info *info = dev_get_drvdata(dev);
431*5bccae6eSSangbeom Kim 
432*5bccae6eSSangbeom Kim 	if (enabled)
433*5bccae6eSSangbeom Kim 		return s5m_rtc_start_alarm(info);
434*5bccae6eSSangbeom Kim 	else
435*5bccae6eSSangbeom Kim 		return s5m_rtc_stop_alarm(info);
436*5bccae6eSSangbeom Kim }
437*5bccae6eSSangbeom Kim 
438*5bccae6eSSangbeom Kim static irqreturn_t s5m_rtc_alarm_irq(int irq, void *data)
439*5bccae6eSSangbeom Kim {
440*5bccae6eSSangbeom Kim 	struct s5m_rtc_info *info = data;
441*5bccae6eSSangbeom Kim 
442*5bccae6eSSangbeom Kim 	rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
443*5bccae6eSSangbeom Kim 
444*5bccae6eSSangbeom Kim 	return IRQ_HANDLED;
445*5bccae6eSSangbeom Kim }
446*5bccae6eSSangbeom Kim 
447*5bccae6eSSangbeom Kim static const struct rtc_class_ops s5m_rtc_ops = {
448*5bccae6eSSangbeom Kim 	.read_time = s5m_rtc_read_time,
449*5bccae6eSSangbeom Kim 	.set_time = s5m_rtc_set_time,
450*5bccae6eSSangbeom Kim 	.read_alarm = s5m_rtc_read_alarm,
451*5bccae6eSSangbeom Kim 	.set_alarm = s5m_rtc_set_alarm,
452*5bccae6eSSangbeom Kim 	.alarm_irq_enable = s5m_rtc_alarm_irq_enable,
453*5bccae6eSSangbeom Kim };
454*5bccae6eSSangbeom Kim 
455*5bccae6eSSangbeom Kim static void s5m_rtc_enable_wtsr(struct s5m_rtc_info *info, bool enable)
456*5bccae6eSSangbeom Kim {
457*5bccae6eSSangbeom Kim 	int ret;
458*5bccae6eSSangbeom Kim 	ret = regmap_update_bits(info->rtc, SEC_WTSR_SMPL_CNTL,
459*5bccae6eSSangbeom Kim 				 WTSR_ENABLE_MASK,
460*5bccae6eSSangbeom Kim 				 enable ? WTSR_ENABLE_MASK : 0);
461*5bccae6eSSangbeom Kim 	if (ret < 0)
462*5bccae6eSSangbeom Kim 		dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n",
463*5bccae6eSSangbeom Kim 			__func__, ret);
464*5bccae6eSSangbeom Kim }
465*5bccae6eSSangbeom Kim 
466*5bccae6eSSangbeom Kim static void s5m_rtc_enable_smpl(struct s5m_rtc_info *info, bool enable)
467*5bccae6eSSangbeom Kim {
468*5bccae6eSSangbeom Kim 	int ret;
469*5bccae6eSSangbeom Kim 	ret = regmap_update_bits(info->rtc, SEC_WTSR_SMPL_CNTL,
470*5bccae6eSSangbeom Kim 				 SMPL_ENABLE_MASK,
471*5bccae6eSSangbeom Kim 				 enable ? SMPL_ENABLE_MASK : 0);
472*5bccae6eSSangbeom Kim 	if (ret < 0)
473*5bccae6eSSangbeom Kim 		dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n",
474*5bccae6eSSangbeom Kim 			__func__, ret);
475*5bccae6eSSangbeom Kim }
476*5bccae6eSSangbeom Kim 
477*5bccae6eSSangbeom Kim static int s5m8767_rtc_init_reg(struct s5m_rtc_info *info)
478*5bccae6eSSangbeom Kim {
479*5bccae6eSSangbeom Kim 	u8 data[2];
480*5bccae6eSSangbeom Kim 	unsigned int tp_read;
481*5bccae6eSSangbeom Kim 	int ret;
482*5bccae6eSSangbeom Kim 	struct rtc_time tm;
483*5bccae6eSSangbeom Kim 
484*5bccae6eSSangbeom Kim 	ret = regmap_read(info->rtc, SEC_RTC_UDR_CON, &tp_read);
485*5bccae6eSSangbeom Kim 	if (ret < 0) {
486*5bccae6eSSangbeom Kim 		dev_err(info->dev, "%s: fail to read control reg(%d)\n",
487*5bccae6eSSangbeom Kim 			__func__, ret);
488*5bccae6eSSangbeom Kim 		return ret;
489*5bccae6eSSangbeom Kim 	}
490*5bccae6eSSangbeom Kim 
491*5bccae6eSSangbeom Kim 	/* Set RTC control register : Binary mode, 24hour mode */
492*5bccae6eSSangbeom Kim 	data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
493*5bccae6eSSangbeom Kim 	data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
494*5bccae6eSSangbeom Kim 
495*5bccae6eSSangbeom Kim 	info->rtc_24hr_mode = 1;
496*5bccae6eSSangbeom Kim 	ret = regmap_raw_write(info->rtc, SEC_ALARM0_CONF, data, 2);
497*5bccae6eSSangbeom Kim 	if (ret < 0) {
498*5bccae6eSSangbeom Kim 		dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
499*5bccae6eSSangbeom Kim 			__func__, ret);
500*5bccae6eSSangbeom Kim 		return ret;
501*5bccae6eSSangbeom Kim 	}
502*5bccae6eSSangbeom Kim 
503*5bccae6eSSangbeom Kim 	/* In first boot time, Set rtc time to 1/1/2012 00:00:00(SUN) */
504*5bccae6eSSangbeom Kim 	if ((tp_read & RTC_TCON_MASK) == 0) {
505*5bccae6eSSangbeom Kim 		dev_dbg(info->dev, "rtc init\n");
506*5bccae6eSSangbeom Kim 		tm.tm_sec = 0;
507*5bccae6eSSangbeom Kim 		tm.tm_min = 0;
508*5bccae6eSSangbeom Kim 		tm.tm_hour = 0;
509*5bccae6eSSangbeom Kim 		tm.tm_wday = 0;
510*5bccae6eSSangbeom Kim 		tm.tm_mday = 1;
511*5bccae6eSSangbeom Kim 		tm.tm_mon = 0;
512*5bccae6eSSangbeom Kim 		tm.tm_year = 112;
513*5bccae6eSSangbeom Kim 		tm.tm_yday = 0;
514*5bccae6eSSangbeom Kim 		tm.tm_isdst = 0;
515*5bccae6eSSangbeom Kim 		ret = s5m_rtc_set_time(info->dev, &tm);
516*5bccae6eSSangbeom Kim 	}
517*5bccae6eSSangbeom Kim 
518*5bccae6eSSangbeom Kim 	ret = regmap_update_bits(info->rtc, SEC_RTC_UDR_CON,
519*5bccae6eSSangbeom Kim 				 RTC_TCON_MASK, tp_read | RTC_TCON_MASK);
520*5bccae6eSSangbeom Kim 	if (ret < 0)
521*5bccae6eSSangbeom Kim 		dev_err(info->dev, "%s: fail to update TCON reg(%d)\n",
522*5bccae6eSSangbeom Kim 			__func__, ret);
523*5bccae6eSSangbeom Kim 
524*5bccae6eSSangbeom Kim 	return ret;
525*5bccae6eSSangbeom Kim }
526*5bccae6eSSangbeom Kim 
527*5bccae6eSSangbeom Kim static int s5m_rtc_probe(struct platform_device *pdev)
528*5bccae6eSSangbeom Kim {
529*5bccae6eSSangbeom Kim 	struct sec_pmic_dev *s5m87xx = dev_get_drvdata(pdev->dev.parent);
530*5bccae6eSSangbeom Kim 	struct sec_platform_data *pdata = s5m87xx->pdata;
531*5bccae6eSSangbeom Kim 	struct s5m_rtc_info *info;
532*5bccae6eSSangbeom Kim 	int ret;
533*5bccae6eSSangbeom Kim 
534*5bccae6eSSangbeom Kim 	if (!pdata) {
535*5bccae6eSSangbeom Kim 		dev_err(pdev->dev.parent, "Platform data not supplied\n");
536*5bccae6eSSangbeom Kim 		return -ENODEV;
537*5bccae6eSSangbeom Kim 	}
538*5bccae6eSSangbeom Kim 
539*5bccae6eSSangbeom Kim 	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
540*5bccae6eSSangbeom Kim 	if (!info)
541*5bccae6eSSangbeom Kim 		return -ENOMEM;
542*5bccae6eSSangbeom Kim 
543*5bccae6eSSangbeom Kim 	info->dev = &pdev->dev;
544*5bccae6eSSangbeom Kim 	info->s5m87xx = s5m87xx;
545*5bccae6eSSangbeom Kim 	info->rtc = s5m87xx->rtc;
546*5bccae6eSSangbeom Kim 	info->device_type = s5m87xx->device_type;
547*5bccae6eSSangbeom Kim 	info->wtsr_smpl = s5m87xx->wtsr_smpl;
548*5bccae6eSSangbeom Kim 
549*5bccae6eSSangbeom Kim 	switch (pdata->device_type) {
550*5bccae6eSSangbeom Kim 	case S5M8763X:
551*5bccae6eSSangbeom Kim 		info->irq = s5m87xx->irq_base + S5M8763_IRQ_ALARM0;
552*5bccae6eSSangbeom Kim 		break;
553*5bccae6eSSangbeom Kim 
554*5bccae6eSSangbeom Kim 	case S5M8767X:
555*5bccae6eSSangbeom Kim 		info->irq = s5m87xx->irq_base + S5M8767_IRQ_RTCA1;
556*5bccae6eSSangbeom Kim 		break;
557*5bccae6eSSangbeom Kim 
558*5bccae6eSSangbeom Kim 	default:
559*5bccae6eSSangbeom Kim 		ret = -EINVAL;
560*5bccae6eSSangbeom Kim 		dev_err(&pdev->dev, "Unsupported device type: %d\n", ret);
561*5bccae6eSSangbeom Kim 		return ret;
562*5bccae6eSSangbeom Kim 	}
563*5bccae6eSSangbeom Kim 
564*5bccae6eSSangbeom Kim 	platform_set_drvdata(pdev, info);
565*5bccae6eSSangbeom Kim 
566*5bccae6eSSangbeom Kim 	ret = s5m8767_rtc_init_reg(info);
567*5bccae6eSSangbeom Kim 
568*5bccae6eSSangbeom Kim 	if (info->wtsr_smpl) {
569*5bccae6eSSangbeom Kim 		s5m_rtc_enable_wtsr(info, true);
570*5bccae6eSSangbeom Kim 		s5m_rtc_enable_smpl(info, true);
571*5bccae6eSSangbeom Kim 	}
572*5bccae6eSSangbeom Kim 
573*5bccae6eSSangbeom Kim 	device_init_wakeup(&pdev->dev, 1);
574*5bccae6eSSangbeom Kim 
575*5bccae6eSSangbeom Kim 	info->rtc_dev = devm_rtc_device_register(&pdev->dev, "s5m-rtc",
576*5bccae6eSSangbeom Kim 						 &s5m_rtc_ops, THIS_MODULE);
577*5bccae6eSSangbeom Kim 
578*5bccae6eSSangbeom Kim 	if (IS_ERR(info->rtc_dev))
579*5bccae6eSSangbeom Kim 		return PTR_ERR(info->rtc_dev);
580*5bccae6eSSangbeom Kim 
581*5bccae6eSSangbeom Kim 	ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
582*5bccae6eSSangbeom Kim 					s5m_rtc_alarm_irq, 0, "rtc-alarm0",
583*5bccae6eSSangbeom Kim 					info);
584*5bccae6eSSangbeom Kim 	if (ret < 0)
585*5bccae6eSSangbeom Kim 		dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
586*5bccae6eSSangbeom Kim 			info->irq, ret);
587*5bccae6eSSangbeom Kim 
588*5bccae6eSSangbeom Kim 	return ret;
589*5bccae6eSSangbeom Kim }
590*5bccae6eSSangbeom Kim 
591*5bccae6eSSangbeom Kim static void s5m_rtc_shutdown(struct platform_device *pdev)
592*5bccae6eSSangbeom Kim {
593*5bccae6eSSangbeom Kim 	struct s5m_rtc_info *info = platform_get_drvdata(pdev);
594*5bccae6eSSangbeom Kim 	int i;
595*5bccae6eSSangbeom Kim 	unsigned int val = 0;
596*5bccae6eSSangbeom Kim 	if (info->wtsr_smpl) {
597*5bccae6eSSangbeom Kim 		for (i = 0; i < 3; i++) {
598*5bccae6eSSangbeom Kim 			s5m_rtc_enable_wtsr(info, false);
599*5bccae6eSSangbeom Kim 			regmap_read(info->rtc, SEC_WTSR_SMPL_CNTL, &val);
600*5bccae6eSSangbeom Kim 			pr_debug("%s: WTSR_SMPL reg(0x%02x)\n", __func__, val);
601*5bccae6eSSangbeom Kim 			if (val & WTSR_ENABLE_MASK)
602*5bccae6eSSangbeom Kim 				pr_emerg("%s: fail to disable WTSR\n",
603*5bccae6eSSangbeom Kim 					 __func__);
604*5bccae6eSSangbeom Kim 			else {
605*5bccae6eSSangbeom Kim 				pr_info("%s: success to disable WTSR\n",
606*5bccae6eSSangbeom Kim 					__func__);
607*5bccae6eSSangbeom Kim 				break;
608*5bccae6eSSangbeom Kim 			}
609*5bccae6eSSangbeom Kim 		}
610*5bccae6eSSangbeom Kim 	}
611*5bccae6eSSangbeom Kim 	/* Disable SMPL when power off */
612*5bccae6eSSangbeom Kim 	s5m_rtc_enable_smpl(info, false);
613*5bccae6eSSangbeom Kim }
614*5bccae6eSSangbeom Kim 
615*5bccae6eSSangbeom Kim static const struct platform_device_id s5m_rtc_id[] = {
616*5bccae6eSSangbeom Kim 	{ "s5m-rtc", 0 },
617*5bccae6eSSangbeom Kim };
618*5bccae6eSSangbeom Kim 
619*5bccae6eSSangbeom Kim static struct platform_driver s5m_rtc_driver = {
620*5bccae6eSSangbeom Kim 	.driver		= {
621*5bccae6eSSangbeom Kim 		.name	= "s5m-rtc",
622*5bccae6eSSangbeom Kim 		.owner	= THIS_MODULE,
623*5bccae6eSSangbeom Kim 	},
624*5bccae6eSSangbeom Kim 	.probe		= s5m_rtc_probe,
625*5bccae6eSSangbeom Kim 	.shutdown	= s5m_rtc_shutdown,
626*5bccae6eSSangbeom Kim 	.id_table	= s5m_rtc_id,
627*5bccae6eSSangbeom Kim };
628*5bccae6eSSangbeom Kim 
629*5bccae6eSSangbeom Kim module_platform_driver(s5m_rtc_driver);
630*5bccae6eSSangbeom Kim 
631*5bccae6eSSangbeom Kim /* Module information */
632*5bccae6eSSangbeom Kim MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
633*5bccae6eSSangbeom Kim MODULE_DESCRIPTION("Samsung S5M RTC driver");
634*5bccae6eSSangbeom Kim MODULE_LICENSE("GPL");
635*5bccae6eSSangbeom Kim MODULE_ALIAS("platform:s5m-rtc");
636