xref: /OK3568_Linux_fs/kernel/drivers/rtc/rtc-s3c.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /* drivers/rtc/rtc-s3c.c
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Copyright (c) 2010 Samsung Electronics Co., Ltd.
5*4882a593Smuzhiyun  *		http://www.samsung.com/
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Copyright (c) 2004,2006 Simtec Electronics
8*4882a593Smuzhiyun  *	Ben Dooks, <ben@simtec.co.uk>
9*4882a593Smuzhiyun  *	http://armlinux.simtec.co.uk/
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * S3C2410/S3C2440/S3C24XX Internal RTC Driver
12*4882a593Smuzhiyun */
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/fs.h>
16*4882a593Smuzhiyun #include <linux/string.h>
17*4882a593Smuzhiyun #include <linux/init.h>
18*4882a593Smuzhiyun #include <linux/platform_device.h>
19*4882a593Smuzhiyun #include <linux/interrupt.h>
20*4882a593Smuzhiyun #include <linux/rtc.h>
21*4882a593Smuzhiyun #include <linux/bcd.h>
22*4882a593Smuzhiyun #include <linux/clk.h>
23*4882a593Smuzhiyun #include <linux/log2.h>
24*4882a593Smuzhiyun #include <linux/slab.h>
25*4882a593Smuzhiyun #include <linux/of.h>
26*4882a593Smuzhiyun #include <linux/of_device.h>
27*4882a593Smuzhiyun #include <linux/uaccess.h>
28*4882a593Smuzhiyun #include <linux/io.h>
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #include <asm/irq.h>
31*4882a593Smuzhiyun #include "rtc-s3c.h"
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun struct s3c_rtc {
34*4882a593Smuzhiyun 	struct device *dev;
35*4882a593Smuzhiyun 	struct rtc_device *rtc;
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun 	void __iomem *base;
38*4882a593Smuzhiyun 	struct clk *rtc_clk;
39*4882a593Smuzhiyun 	struct clk *rtc_src_clk;
40*4882a593Smuzhiyun 	bool alarm_enabled;
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun 	const struct s3c_rtc_data *data;
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	int irq_alarm;
45*4882a593Smuzhiyun 	int irq_tick;
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 	spinlock_t pie_lock;
48*4882a593Smuzhiyun 	spinlock_t alarm_lock;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	int ticnt_save;
51*4882a593Smuzhiyun 	int ticnt_en_save;
52*4882a593Smuzhiyun 	bool wake_en;
53*4882a593Smuzhiyun };
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun struct s3c_rtc_data {
56*4882a593Smuzhiyun 	int max_user_freq;
57*4882a593Smuzhiyun 	bool needs_src_clk;
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	void (*irq_handler) (struct s3c_rtc *info, int mask);
60*4882a593Smuzhiyun 	void (*set_freq) (struct s3c_rtc *info, int freq);
61*4882a593Smuzhiyun 	void (*enable_tick) (struct s3c_rtc *info, struct seq_file *seq);
62*4882a593Smuzhiyun 	void (*select_tick_clk) (struct s3c_rtc *info);
63*4882a593Smuzhiyun 	void (*save_tick_cnt) (struct s3c_rtc *info);
64*4882a593Smuzhiyun 	void (*restore_tick_cnt) (struct s3c_rtc *info);
65*4882a593Smuzhiyun 	void (*enable) (struct s3c_rtc *info);
66*4882a593Smuzhiyun 	void (*disable) (struct s3c_rtc *info);
67*4882a593Smuzhiyun };
68*4882a593Smuzhiyun 
s3c_rtc_enable_clk(struct s3c_rtc * info)69*4882a593Smuzhiyun static int s3c_rtc_enable_clk(struct s3c_rtc *info)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun 	int ret;
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	ret = clk_enable(info->rtc_clk);
74*4882a593Smuzhiyun 	if (ret)
75*4882a593Smuzhiyun 		return ret;
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 	if (info->data->needs_src_clk) {
78*4882a593Smuzhiyun 		ret = clk_enable(info->rtc_src_clk);
79*4882a593Smuzhiyun 		if (ret) {
80*4882a593Smuzhiyun 			clk_disable(info->rtc_clk);
81*4882a593Smuzhiyun 			return ret;
82*4882a593Smuzhiyun 		}
83*4882a593Smuzhiyun 	}
84*4882a593Smuzhiyun 	return 0;
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun 
s3c_rtc_disable_clk(struct s3c_rtc * info)87*4882a593Smuzhiyun static void s3c_rtc_disable_clk(struct s3c_rtc *info)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun 	if (info->data->needs_src_clk)
90*4882a593Smuzhiyun 		clk_disable(info->rtc_src_clk);
91*4882a593Smuzhiyun 	clk_disable(info->rtc_clk);
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun /* IRQ Handlers */
s3c_rtc_tickirq(int irq,void * id)95*4882a593Smuzhiyun static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun 	struct s3c_rtc *info = (struct s3c_rtc *)id;
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	if (info->data->irq_handler)
100*4882a593Smuzhiyun 		info->data->irq_handler(info, S3C2410_INTP_TIC);
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	return IRQ_HANDLED;
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun 
s3c_rtc_alarmirq(int irq,void * id)105*4882a593Smuzhiyun static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun 	struct s3c_rtc *info = (struct s3c_rtc *)id;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	if (info->data->irq_handler)
110*4882a593Smuzhiyun 		info->data->irq_handler(info, S3C2410_INTP_ALM);
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	return IRQ_HANDLED;
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun /* Update control registers */
s3c_rtc_setaie(struct device * dev,unsigned int enabled)116*4882a593Smuzhiyun static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun 	struct s3c_rtc *info = dev_get_drvdata(dev);
119*4882a593Smuzhiyun 	unsigned long flags;
120*4882a593Smuzhiyun 	unsigned int tmp;
121*4882a593Smuzhiyun 	int ret;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled);
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	ret = s3c_rtc_enable_clk(info);
126*4882a593Smuzhiyun 	if (ret)
127*4882a593Smuzhiyun 		return ret;
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	if (enabled)
132*4882a593Smuzhiyun 		tmp |= S3C2410_RTCALM_ALMEN;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	writeb(tmp, info->base + S3C2410_RTCALM);
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 	spin_lock_irqsave(&info->alarm_lock, flags);
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	if (info->alarm_enabled && !enabled)
139*4882a593Smuzhiyun 		s3c_rtc_disable_clk(info);
140*4882a593Smuzhiyun 	else if (!info->alarm_enabled && enabled)
141*4882a593Smuzhiyun 		ret = s3c_rtc_enable_clk(info);
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	info->alarm_enabled = enabled;
144*4882a593Smuzhiyun 	spin_unlock_irqrestore(&info->alarm_lock, flags);
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	s3c_rtc_disable_clk(info);
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	return ret;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun /* Set RTC frequency */
s3c_rtc_setfreq(struct s3c_rtc * info,int freq)152*4882a593Smuzhiyun static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun 	int ret;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	if (!is_power_of_2(freq))
157*4882a593Smuzhiyun 		return -EINVAL;
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	ret = s3c_rtc_enable_clk(info);
160*4882a593Smuzhiyun 	if (ret)
161*4882a593Smuzhiyun 		return ret;
162*4882a593Smuzhiyun 	spin_lock_irq(&info->pie_lock);
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	if (info->data->set_freq)
165*4882a593Smuzhiyun 		info->data->set_freq(info, freq);
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	spin_unlock_irq(&info->pie_lock);
168*4882a593Smuzhiyun 	s3c_rtc_disable_clk(info);
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	return 0;
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun /* Time read/write */
s3c_rtc_gettime(struct device * dev,struct rtc_time * rtc_tm)174*4882a593Smuzhiyun static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
175*4882a593Smuzhiyun {
176*4882a593Smuzhiyun 	struct s3c_rtc *info = dev_get_drvdata(dev);
177*4882a593Smuzhiyun 	unsigned int have_retried = 0;
178*4882a593Smuzhiyun 	int ret;
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	ret = s3c_rtc_enable_clk(info);
181*4882a593Smuzhiyun 	if (ret)
182*4882a593Smuzhiyun 		return ret;
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun retry_get_time:
185*4882a593Smuzhiyun 	rtc_tm->tm_min  = readb(info->base + S3C2410_RTCMIN);
186*4882a593Smuzhiyun 	rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR);
187*4882a593Smuzhiyun 	rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE);
188*4882a593Smuzhiyun 	rtc_tm->tm_mon  = readb(info->base + S3C2410_RTCMON);
189*4882a593Smuzhiyun 	rtc_tm->tm_year = readb(info->base + S3C2410_RTCYEAR);
190*4882a593Smuzhiyun 	rtc_tm->tm_sec  = readb(info->base + S3C2410_RTCSEC);
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	/* the only way to work out whether the system was mid-update
193*4882a593Smuzhiyun 	 * when we read it is to check the second counter, and if it
194*4882a593Smuzhiyun 	 * is zero, then we re-try the entire read
195*4882a593Smuzhiyun 	 */
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	if (rtc_tm->tm_sec == 0 && !have_retried) {
198*4882a593Smuzhiyun 		have_retried = 1;
199*4882a593Smuzhiyun 		goto retry_get_time;
200*4882a593Smuzhiyun 	}
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
203*4882a593Smuzhiyun 	rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
204*4882a593Smuzhiyun 	rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
205*4882a593Smuzhiyun 	rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
206*4882a593Smuzhiyun 	rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
207*4882a593Smuzhiyun 	rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	s3c_rtc_disable_clk(info);
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	rtc_tm->tm_year += 100;
212*4882a593Smuzhiyun 	rtc_tm->tm_mon -= 1;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	dev_dbg(dev, "read time %ptR\n", rtc_tm);
215*4882a593Smuzhiyun 	return 0;
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun 
s3c_rtc_settime(struct device * dev,struct rtc_time * tm)218*4882a593Smuzhiyun static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun 	struct s3c_rtc *info = dev_get_drvdata(dev);
221*4882a593Smuzhiyun 	int year = tm->tm_year - 100;
222*4882a593Smuzhiyun 	int ret;
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	dev_dbg(dev, "set time %ptR\n", tm);
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	/* we get around y2k by simply not supporting it */
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	if (year < 0 || year >= 100) {
229*4882a593Smuzhiyun 		dev_err(dev, "rtc only supports 100 years\n");
230*4882a593Smuzhiyun 		return -EINVAL;
231*4882a593Smuzhiyun 	}
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	ret = s3c_rtc_enable_clk(info);
234*4882a593Smuzhiyun 	if (ret)
235*4882a593Smuzhiyun 		return ret;
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	writeb(bin2bcd(tm->tm_sec),  info->base + S3C2410_RTCSEC);
238*4882a593Smuzhiyun 	writeb(bin2bcd(tm->tm_min),  info->base + S3C2410_RTCMIN);
239*4882a593Smuzhiyun 	writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_RTCHOUR);
240*4882a593Smuzhiyun 	writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_RTCDATE);
241*4882a593Smuzhiyun 	writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_RTCMON);
242*4882a593Smuzhiyun 	writeb(bin2bcd(year), info->base + S3C2410_RTCYEAR);
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	s3c_rtc_disable_clk(info);
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	return 0;
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun 
s3c_rtc_getalarm(struct device * dev,struct rtc_wkalrm * alrm)249*4882a593Smuzhiyun static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
250*4882a593Smuzhiyun {
251*4882a593Smuzhiyun 	struct s3c_rtc *info = dev_get_drvdata(dev);
252*4882a593Smuzhiyun 	struct rtc_time *alm_tm = &alrm->time;
253*4882a593Smuzhiyun 	unsigned int alm_en;
254*4882a593Smuzhiyun 	int ret;
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	ret = s3c_rtc_enable_clk(info);
257*4882a593Smuzhiyun 	if (ret)
258*4882a593Smuzhiyun 		return ret;
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	alm_tm->tm_sec  = readb(info->base + S3C2410_ALMSEC);
261*4882a593Smuzhiyun 	alm_tm->tm_min  = readb(info->base + S3C2410_ALMMIN);
262*4882a593Smuzhiyun 	alm_tm->tm_hour = readb(info->base + S3C2410_ALMHOUR);
263*4882a593Smuzhiyun 	alm_tm->tm_mon  = readb(info->base + S3C2410_ALMMON);
264*4882a593Smuzhiyun 	alm_tm->tm_mday = readb(info->base + S3C2410_ALMDATE);
265*4882a593Smuzhiyun 	alm_tm->tm_year = readb(info->base + S3C2410_ALMYEAR);
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun 	alm_en = readb(info->base + S3C2410_RTCALM);
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	s3c_rtc_disable_clk(info);
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	dev_dbg(dev, "read alarm %d, %ptR\n", alm_en, alm_tm);
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	/* decode the alarm enable field */
276*4882a593Smuzhiyun 	if (alm_en & S3C2410_RTCALM_SECEN)
277*4882a593Smuzhiyun 		alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	if (alm_en & S3C2410_RTCALM_MINEN)
280*4882a593Smuzhiyun 		alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	if (alm_en & S3C2410_RTCALM_HOUREN)
283*4882a593Smuzhiyun 		alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	if (alm_en & S3C2410_RTCALM_DAYEN)
286*4882a593Smuzhiyun 		alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	if (alm_en & S3C2410_RTCALM_MONEN) {
289*4882a593Smuzhiyun 		alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon);
290*4882a593Smuzhiyun 		alm_tm->tm_mon -= 1;
291*4882a593Smuzhiyun 	}
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	if (alm_en & S3C2410_RTCALM_YEAREN)
294*4882a593Smuzhiyun 		alm_tm->tm_year = bcd2bin(alm_tm->tm_year);
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	return 0;
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun 
s3c_rtc_setalarm(struct device * dev,struct rtc_wkalrm * alrm)299*4882a593Smuzhiyun static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
300*4882a593Smuzhiyun {
301*4882a593Smuzhiyun 	struct s3c_rtc *info = dev_get_drvdata(dev);
302*4882a593Smuzhiyun 	struct rtc_time *tm = &alrm->time;
303*4882a593Smuzhiyun 	unsigned int alrm_en;
304*4882a593Smuzhiyun 	int ret;
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	dev_dbg(dev, "s3c_rtc_setalarm: %d, %ptR\n", alrm->enabled, tm);
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	ret = s3c_rtc_enable_clk(info);
309*4882a593Smuzhiyun 	if (ret)
310*4882a593Smuzhiyun 		return ret;
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
313*4882a593Smuzhiyun 	writeb(0x00, info->base + S3C2410_RTCALM);
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 	if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
316*4882a593Smuzhiyun 		alrm_en |= S3C2410_RTCALM_SECEN;
317*4882a593Smuzhiyun 		writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_ALMSEC);
318*4882a593Smuzhiyun 	}
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	if (tm->tm_min < 60 && tm->tm_min >= 0) {
321*4882a593Smuzhiyun 		alrm_en |= S3C2410_RTCALM_MINEN;
322*4882a593Smuzhiyun 		writeb(bin2bcd(tm->tm_min), info->base + S3C2410_ALMMIN);
323*4882a593Smuzhiyun 	}
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun 	if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
326*4882a593Smuzhiyun 		alrm_en |= S3C2410_RTCALM_HOUREN;
327*4882a593Smuzhiyun 		writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_ALMHOUR);
328*4882a593Smuzhiyun 	}
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	if (tm->tm_mon < 12 && tm->tm_mon >= 0) {
331*4882a593Smuzhiyun 		alrm_en |= S3C2410_RTCALM_MONEN;
332*4882a593Smuzhiyun 		writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_ALMMON);
333*4882a593Smuzhiyun 	}
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	if (tm->tm_mday <= 31 && tm->tm_mday >= 1) {
336*4882a593Smuzhiyun 		alrm_en |= S3C2410_RTCALM_DAYEN;
337*4882a593Smuzhiyun 		writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_ALMDATE);
338*4882a593Smuzhiyun 	}
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en);
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	writeb(alrm_en, info->base + S3C2410_RTCALM);
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	s3c_rtc_setaie(dev, alrm->enabled);
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	s3c_rtc_disable_clk(info);
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	return 0;
349*4882a593Smuzhiyun }
350*4882a593Smuzhiyun 
s3c_rtc_proc(struct device * dev,struct seq_file * seq)351*4882a593Smuzhiyun static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
352*4882a593Smuzhiyun {
353*4882a593Smuzhiyun 	struct s3c_rtc *info = dev_get_drvdata(dev);
354*4882a593Smuzhiyun 	int ret;
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	ret = s3c_rtc_enable_clk(info);
357*4882a593Smuzhiyun 	if (ret)
358*4882a593Smuzhiyun 		return ret;
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	if (info->data->enable_tick)
361*4882a593Smuzhiyun 		info->data->enable_tick(info, seq);
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 	s3c_rtc_disable_clk(info);
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 	return 0;
366*4882a593Smuzhiyun }
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun static const struct rtc_class_ops s3c_rtcops = {
369*4882a593Smuzhiyun 	.read_time	= s3c_rtc_gettime,
370*4882a593Smuzhiyun 	.set_time	= s3c_rtc_settime,
371*4882a593Smuzhiyun 	.read_alarm	= s3c_rtc_getalarm,
372*4882a593Smuzhiyun 	.set_alarm	= s3c_rtc_setalarm,
373*4882a593Smuzhiyun 	.proc		= s3c_rtc_proc,
374*4882a593Smuzhiyun 	.alarm_irq_enable = s3c_rtc_setaie,
375*4882a593Smuzhiyun };
376*4882a593Smuzhiyun 
s3c24xx_rtc_enable(struct s3c_rtc * info)377*4882a593Smuzhiyun static void s3c24xx_rtc_enable(struct s3c_rtc *info)
378*4882a593Smuzhiyun {
379*4882a593Smuzhiyun 	unsigned int con, tmp;
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 	con = readw(info->base + S3C2410_RTCCON);
382*4882a593Smuzhiyun 	/* re-enable the device, and check it is ok */
383*4882a593Smuzhiyun 	if ((con & S3C2410_RTCCON_RTCEN) == 0) {
384*4882a593Smuzhiyun 		dev_info(info->dev, "rtc disabled, re-enabling\n");
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 		tmp = readw(info->base + S3C2410_RTCCON);
387*4882a593Smuzhiyun 		writew(tmp | S3C2410_RTCCON_RTCEN, info->base + S3C2410_RTCCON);
388*4882a593Smuzhiyun 	}
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	if (con & S3C2410_RTCCON_CNTSEL) {
391*4882a593Smuzhiyun 		dev_info(info->dev, "removing RTCCON_CNTSEL\n");
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 		tmp = readw(info->base + S3C2410_RTCCON);
394*4882a593Smuzhiyun 		writew(tmp & ~S3C2410_RTCCON_CNTSEL,
395*4882a593Smuzhiyun 		       info->base + S3C2410_RTCCON);
396*4882a593Smuzhiyun 	}
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	if (con & S3C2410_RTCCON_CLKRST) {
399*4882a593Smuzhiyun 		dev_info(info->dev, "removing RTCCON_CLKRST\n");
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 		tmp = readw(info->base + S3C2410_RTCCON);
402*4882a593Smuzhiyun 		writew(tmp & ~S3C2410_RTCCON_CLKRST,
403*4882a593Smuzhiyun 		       info->base + S3C2410_RTCCON);
404*4882a593Smuzhiyun 	}
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun 
s3c24xx_rtc_disable(struct s3c_rtc * info)407*4882a593Smuzhiyun static void s3c24xx_rtc_disable(struct s3c_rtc *info)
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun 	unsigned int con;
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	con = readw(info->base + S3C2410_RTCCON);
412*4882a593Smuzhiyun 	con &= ~S3C2410_RTCCON_RTCEN;
413*4882a593Smuzhiyun 	writew(con, info->base + S3C2410_RTCCON);
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	con = readb(info->base + S3C2410_TICNT);
416*4882a593Smuzhiyun 	con &= ~S3C2410_TICNT_ENABLE;
417*4882a593Smuzhiyun 	writeb(con, info->base + S3C2410_TICNT);
418*4882a593Smuzhiyun }
419*4882a593Smuzhiyun 
s3c6410_rtc_disable(struct s3c_rtc * info)420*4882a593Smuzhiyun static void s3c6410_rtc_disable(struct s3c_rtc *info)
421*4882a593Smuzhiyun {
422*4882a593Smuzhiyun 	unsigned int con;
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	con = readw(info->base + S3C2410_RTCCON);
425*4882a593Smuzhiyun 	con &= ~S3C64XX_RTCCON_TICEN;
426*4882a593Smuzhiyun 	con &= ~S3C2410_RTCCON_RTCEN;
427*4882a593Smuzhiyun 	writew(con, info->base + S3C2410_RTCCON);
428*4882a593Smuzhiyun }
429*4882a593Smuzhiyun 
s3c_rtc_remove(struct platform_device * pdev)430*4882a593Smuzhiyun static int s3c_rtc_remove(struct platform_device *pdev)
431*4882a593Smuzhiyun {
432*4882a593Smuzhiyun 	struct s3c_rtc *info = platform_get_drvdata(pdev);
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun 	s3c_rtc_setaie(info->dev, 0);
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun 	if (info->data->needs_src_clk)
437*4882a593Smuzhiyun 		clk_unprepare(info->rtc_src_clk);
438*4882a593Smuzhiyun 	clk_unprepare(info->rtc_clk);
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	return 0;
441*4882a593Smuzhiyun }
442*4882a593Smuzhiyun 
s3c_rtc_probe(struct platform_device * pdev)443*4882a593Smuzhiyun static int s3c_rtc_probe(struct platform_device *pdev)
444*4882a593Smuzhiyun {
445*4882a593Smuzhiyun 	struct s3c_rtc *info = NULL;
446*4882a593Smuzhiyun 	struct rtc_time rtc_tm;
447*4882a593Smuzhiyun 	int ret;
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun 	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
450*4882a593Smuzhiyun 	if (!info)
451*4882a593Smuzhiyun 		return -ENOMEM;
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 	/* find the IRQs */
454*4882a593Smuzhiyun 	info->irq_tick = platform_get_irq(pdev, 1);
455*4882a593Smuzhiyun 	if (info->irq_tick < 0)
456*4882a593Smuzhiyun 		return info->irq_tick;
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun 	info->dev = &pdev->dev;
459*4882a593Smuzhiyun 	info->data = of_device_get_match_data(&pdev->dev);
460*4882a593Smuzhiyun 	if (!info->data) {
461*4882a593Smuzhiyun 		dev_err(&pdev->dev, "failed getting s3c_rtc_data\n");
462*4882a593Smuzhiyun 		return -EINVAL;
463*4882a593Smuzhiyun 	}
464*4882a593Smuzhiyun 	spin_lock_init(&info->pie_lock);
465*4882a593Smuzhiyun 	spin_lock_init(&info->alarm_lock);
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun 	platform_set_drvdata(pdev, info);
468*4882a593Smuzhiyun 
469*4882a593Smuzhiyun 	info->irq_alarm = platform_get_irq(pdev, 0);
470*4882a593Smuzhiyun 	if (info->irq_alarm < 0)
471*4882a593Smuzhiyun 		return info->irq_alarm;
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 	dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n",
474*4882a593Smuzhiyun 		info->irq_tick, info->irq_alarm);
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 	/* get the memory region */
477*4882a593Smuzhiyun 	info->base = devm_platform_ioremap_resource(pdev, 0);
478*4882a593Smuzhiyun 	if (IS_ERR(info->base))
479*4882a593Smuzhiyun 		return PTR_ERR(info->base);
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 	info->rtc_clk = devm_clk_get(&pdev->dev, "rtc");
482*4882a593Smuzhiyun 	if (IS_ERR(info->rtc_clk)) {
483*4882a593Smuzhiyun 		ret = PTR_ERR(info->rtc_clk);
484*4882a593Smuzhiyun 		if (ret != -EPROBE_DEFER)
485*4882a593Smuzhiyun 			dev_err(&pdev->dev, "failed to find rtc clock\n");
486*4882a593Smuzhiyun 		else
487*4882a593Smuzhiyun 			dev_dbg(&pdev->dev, "probe deferred due to missing rtc clk\n");
488*4882a593Smuzhiyun 		return ret;
489*4882a593Smuzhiyun 	}
490*4882a593Smuzhiyun 	ret = clk_prepare_enable(info->rtc_clk);
491*4882a593Smuzhiyun 	if (ret)
492*4882a593Smuzhiyun 		return ret;
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	if (info->data->needs_src_clk) {
495*4882a593Smuzhiyun 		info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src");
496*4882a593Smuzhiyun 		if (IS_ERR(info->rtc_src_clk)) {
497*4882a593Smuzhiyun 			ret = dev_err_probe(&pdev->dev, PTR_ERR(info->rtc_src_clk),
498*4882a593Smuzhiyun 					    "failed to find rtc source clock\n");
499*4882a593Smuzhiyun 			goto err_src_clk;
500*4882a593Smuzhiyun 		}
501*4882a593Smuzhiyun 		ret = clk_prepare_enable(info->rtc_src_clk);
502*4882a593Smuzhiyun 		if (ret)
503*4882a593Smuzhiyun 			goto err_src_clk;
504*4882a593Smuzhiyun 	}
505*4882a593Smuzhiyun 
506*4882a593Smuzhiyun 	/* check to see if everything is setup correctly */
507*4882a593Smuzhiyun 	if (info->data->enable)
508*4882a593Smuzhiyun 		info->data->enable(info);
509*4882a593Smuzhiyun 
510*4882a593Smuzhiyun 	dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n",
511*4882a593Smuzhiyun 		readw(info->base + S3C2410_RTCCON));
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun 	device_init_wakeup(&pdev->dev, 1);
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 	/* Check RTC Time */
516*4882a593Smuzhiyun 	if (s3c_rtc_gettime(&pdev->dev, &rtc_tm)) {
517*4882a593Smuzhiyun 		rtc_tm.tm_year	= 100;
518*4882a593Smuzhiyun 		rtc_tm.tm_mon	= 0;
519*4882a593Smuzhiyun 		rtc_tm.tm_mday	= 1;
520*4882a593Smuzhiyun 		rtc_tm.tm_hour	= 0;
521*4882a593Smuzhiyun 		rtc_tm.tm_min	= 0;
522*4882a593Smuzhiyun 		rtc_tm.tm_sec	= 0;
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 		s3c_rtc_settime(&pdev->dev, &rtc_tm);
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 		dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n");
527*4882a593Smuzhiyun 	}
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	/* register RTC and exit */
530*4882a593Smuzhiyun 	info->rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops,
531*4882a593Smuzhiyun 					     THIS_MODULE);
532*4882a593Smuzhiyun 	if (IS_ERR(info->rtc)) {
533*4882a593Smuzhiyun 		dev_err(&pdev->dev, "cannot attach rtc\n");
534*4882a593Smuzhiyun 		ret = PTR_ERR(info->rtc);
535*4882a593Smuzhiyun 		goto err_nortc;
536*4882a593Smuzhiyun 	}
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 	ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq,
539*4882a593Smuzhiyun 			       0, "s3c2410-rtc alarm", info);
540*4882a593Smuzhiyun 	if (ret) {
541*4882a593Smuzhiyun 		dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret);
542*4882a593Smuzhiyun 		goto err_nortc;
543*4882a593Smuzhiyun 	}
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 	ret = devm_request_irq(&pdev->dev, info->irq_tick, s3c_rtc_tickirq,
546*4882a593Smuzhiyun 			       0, "s3c2410-rtc tick", info);
547*4882a593Smuzhiyun 	if (ret) {
548*4882a593Smuzhiyun 		dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_tick, ret);
549*4882a593Smuzhiyun 		goto err_nortc;
550*4882a593Smuzhiyun 	}
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun 	if (info->data->select_tick_clk)
553*4882a593Smuzhiyun 		info->data->select_tick_clk(info);
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	s3c_rtc_setfreq(info, 1);
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 	s3c_rtc_disable_clk(info);
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 	return 0;
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun err_nortc:
562*4882a593Smuzhiyun 	if (info->data->disable)
563*4882a593Smuzhiyun 		info->data->disable(info);
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun 	if (info->data->needs_src_clk)
566*4882a593Smuzhiyun 		clk_disable_unprepare(info->rtc_src_clk);
567*4882a593Smuzhiyun err_src_clk:
568*4882a593Smuzhiyun 	clk_disable_unprepare(info->rtc_clk);
569*4882a593Smuzhiyun 
570*4882a593Smuzhiyun 	return ret;
571*4882a593Smuzhiyun }
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
574*4882a593Smuzhiyun 
s3c_rtc_suspend(struct device * dev)575*4882a593Smuzhiyun static int s3c_rtc_suspend(struct device *dev)
576*4882a593Smuzhiyun {
577*4882a593Smuzhiyun 	struct s3c_rtc *info = dev_get_drvdata(dev);
578*4882a593Smuzhiyun 	int ret;
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	ret = s3c_rtc_enable_clk(info);
581*4882a593Smuzhiyun 	if (ret)
582*4882a593Smuzhiyun 		return ret;
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun 	/* save TICNT for anyone using periodic interrupts */
585*4882a593Smuzhiyun 	if (info->data->save_tick_cnt)
586*4882a593Smuzhiyun 		info->data->save_tick_cnt(info);
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun 	if (info->data->disable)
589*4882a593Smuzhiyun 		info->data->disable(info);
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun 	if (device_may_wakeup(dev) && !info->wake_en) {
592*4882a593Smuzhiyun 		if (enable_irq_wake(info->irq_alarm) == 0)
593*4882a593Smuzhiyun 			info->wake_en = true;
594*4882a593Smuzhiyun 		else
595*4882a593Smuzhiyun 			dev_err(dev, "enable_irq_wake failed\n");
596*4882a593Smuzhiyun 	}
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun 	return 0;
599*4882a593Smuzhiyun }
600*4882a593Smuzhiyun 
s3c_rtc_resume(struct device * dev)601*4882a593Smuzhiyun static int s3c_rtc_resume(struct device *dev)
602*4882a593Smuzhiyun {
603*4882a593Smuzhiyun 	struct s3c_rtc *info = dev_get_drvdata(dev);
604*4882a593Smuzhiyun 
605*4882a593Smuzhiyun 	if (info->data->enable)
606*4882a593Smuzhiyun 		info->data->enable(info);
607*4882a593Smuzhiyun 
608*4882a593Smuzhiyun 	if (info->data->restore_tick_cnt)
609*4882a593Smuzhiyun 		info->data->restore_tick_cnt(info);
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun 	s3c_rtc_disable_clk(info);
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun 	if (device_may_wakeup(dev) && info->wake_en) {
614*4882a593Smuzhiyun 		disable_irq_wake(info->irq_alarm);
615*4882a593Smuzhiyun 		info->wake_en = false;
616*4882a593Smuzhiyun 	}
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun 	return 0;
619*4882a593Smuzhiyun }
620*4882a593Smuzhiyun #endif
621*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume);
622*4882a593Smuzhiyun 
s3c24xx_rtc_irq(struct s3c_rtc * info,int mask)623*4882a593Smuzhiyun static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask)
624*4882a593Smuzhiyun {
625*4882a593Smuzhiyun 	rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
626*4882a593Smuzhiyun }
627*4882a593Smuzhiyun 
s3c6410_rtc_irq(struct s3c_rtc * info,int mask)628*4882a593Smuzhiyun static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask)
629*4882a593Smuzhiyun {
630*4882a593Smuzhiyun 	rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
631*4882a593Smuzhiyun 	writeb(mask, info->base + S3C2410_INTP);
632*4882a593Smuzhiyun }
633*4882a593Smuzhiyun 
s3c2410_rtc_setfreq(struct s3c_rtc * info,int freq)634*4882a593Smuzhiyun static void s3c2410_rtc_setfreq(struct s3c_rtc *info, int freq)
635*4882a593Smuzhiyun {
636*4882a593Smuzhiyun 	unsigned int tmp = 0;
637*4882a593Smuzhiyun 	int val;
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 	tmp = readb(info->base + S3C2410_TICNT);
640*4882a593Smuzhiyun 	tmp &= S3C2410_TICNT_ENABLE;
641*4882a593Smuzhiyun 
642*4882a593Smuzhiyun 	val = (info->rtc->max_user_freq / freq) - 1;
643*4882a593Smuzhiyun 	tmp |= val;
644*4882a593Smuzhiyun 
645*4882a593Smuzhiyun 	writel(tmp, info->base + S3C2410_TICNT);
646*4882a593Smuzhiyun }
647*4882a593Smuzhiyun 
s3c2416_rtc_setfreq(struct s3c_rtc * info,int freq)648*4882a593Smuzhiyun static void s3c2416_rtc_setfreq(struct s3c_rtc *info, int freq)
649*4882a593Smuzhiyun {
650*4882a593Smuzhiyun 	unsigned int tmp = 0;
651*4882a593Smuzhiyun 	int val;
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	tmp = readb(info->base + S3C2410_TICNT);
654*4882a593Smuzhiyun 	tmp &= S3C2410_TICNT_ENABLE;
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun 	val = (info->rtc->max_user_freq / freq) - 1;
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun 	tmp |= S3C2443_TICNT_PART(val);
659*4882a593Smuzhiyun 	writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1);
660*4882a593Smuzhiyun 
661*4882a593Smuzhiyun 	writel(S3C2416_TICNT2_PART(val), info->base + S3C2416_TICNT2);
662*4882a593Smuzhiyun 
663*4882a593Smuzhiyun 	writel(tmp, info->base + S3C2410_TICNT);
664*4882a593Smuzhiyun }
665*4882a593Smuzhiyun 
s3c2443_rtc_setfreq(struct s3c_rtc * info,int freq)666*4882a593Smuzhiyun static void s3c2443_rtc_setfreq(struct s3c_rtc *info, int freq)
667*4882a593Smuzhiyun {
668*4882a593Smuzhiyun 	unsigned int tmp = 0;
669*4882a593Smuzhiyun 	int val;
670*4882a593Smuzhiyun 
671*4882a593Smuzhiyun 	tmp = readb(info->base + S3C2410_TICNT);
672*4882a593Smuzhiyun 	tmp &= S3C2410_TICNT_ENABLE;
673*4882a593Smuzhiyun 
674*4882a593Smuzhiyun 	val = (info->rtc->max_user_freq / freq) - 1;
675*4882a593Smuzhiyun 
676*4882a593Smuzhiyun 	tmp |= S3C2443_TICNT_PART(val);
677*4882a593Smuzhiyun 	writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1);
678*4882a593Smuzhiyun 
679*4882a593Smuzhiyun 	writel(tmp, info->base + S3C2410_TICNT);
680*4882a593Smuzhiyun }
681*4882a593Smuzhiyun 
s3c6410_rtc_setfreq(struct s3c_rtc * info,int freq)682*4882a593Smuzhiyun static void s3c6410_rtc_setfreq(struct s3c_rtc *info, int freq)
683*4882a593Smuzhiyun {
684*4882a593Smuzhiyun 	int val;
685*4882a593Smuzhiyun 
686*4882a593Smuzhiyun 	val = (info->rtc->max_user_freq / freq) - 1;
687*4882a593Smuzhiyun 	writel(val, info->base + S3C2410_TICNT);
688*4882a593Smuzhiyun }
689*4882a593Smuzhiyun 
s3c24xx_rtc_enable_tick(struct s3c_rtc * info,struct seq_file * seq)690*4882a593Smuzhiyun static void s3c24xx_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq)
691*4882a593Smuzhiyun {
692*4882a593Smuzhiyun 	unsigned int ticnt;
693*4882a593Smuzhiyun 
694*4882a593Smuzhiyun 	ticnt = readb(info->base + S3C2410_TICNT);
695*4882a593Smuzhiyun 	ticnt &= S3C2410_TICNT_ENABLE;
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 	seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt  ? "yes" : "no");
698*4882a593Smuzhiyun }
699*4882a593Smuzhiyun 
s3c2416_rtc_select_tick_clk(struct s3c_rtc * info)700*4882a593Smuzhiyun static void s3c2416_rtc_select_tick_clk(struct s3c_rtc *info)
701*4882a593Smuzhiyun {
702*4882a593Smuzhiyun 	unsigned int con;
703*4882a593Smuzhiyun 
704*4882a593Smuzhiyun 	con = readw(info->base + S3C2410_RTCCON);
705*4882a593Smuzhiyun 	con |= S3C2443_RTCCON_TICSEL;
706*4882a593Smuzhiyun 	writew(con, info->base + S3C2410_RTCCON);
707*4882a593Smuzhiyun }
708*4882a593Smuzhiyun 
s3c6410_rtc_enable_tick(struct s3c_rtc * info,struct seq_file * seq)709*4882a593Smuzhiyun static void s3c6410_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq)
710*4882a593Smuzhiyun {
711*4882a593Smuzhiyun 	unsigned int ticnt;
712*4882a593Smuzhiyun 
713*4882a593Smuzhiyun 	ticnt = readw(info->base + S3C2410_RTCCON);
714*4882a593Smuzhiyun 	ticnt &= S3C64XX_RTCCON_TICEN;
715*4882a593Smuzhiyun 
716*4882a593Smuzhiyun 	seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt  ? "yes" : "no");
717*4882a593Smuzhiyun }
718*4882a593Smuzhiyun 
s3c24xx_rtc_save_tick_cnt(struct s3c_rtc * info)719*4882a593Smuzhiyun static void s3c24xx_rtc_save_tick_cnt(struct s3c_rtc *info)
720*4882a593Smuzhiyun {
721*4882a593Smuzhiyun 	info->ticnt_save = readb(info->base + S3C2410_TICNT);
722*4882a593Smuzhiyun }
723*4882a593Smuzhiyun 
s3c24xx_rtc_restore_tick_cnt(struct s3c_rtc * info)724*4882a593Smuzhiyun static void s3c24xx_rtc_restore_tick_cnt(struct s3c_rtc *info)
725*4882a593Smuzhiyun {
726*4882a593Smuzhiyun 	writeb(info->ticnt_save, info->base + S3C2410_TICNT);
727*4882a593Smuzhiyun }
728*4882a593Smuzhiyun 
s3c6410_rtc_save_tick_cnt(struct s3c_rtc * info)729*4882a593Smuzhiyun static void s3c6410_rtc_save_tick_cnt(struct s3c_rtc *info)
730*4882a593Smuzhiyun {
731*4882a593Smuzhiyun 	info->ticnt_en_save = readw(info->base + S3C2410_RTCCON);
732*4882a593Smuzhiyun 	info->ticnt_en_save &= S3C64XX_RTCCON_TICEN;
733*4882a593Smuzhiyun 	info->ticnt_save = readl(info->base + S3C2410_TICNT);
734*4882a593Smuzhiyun }
735*4882a593Smuzhiyun 
s3c6410_rtc_restore_tick_cnt(struct s3c_rtc * info)736*4882a593Smuzhiyun static void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info)
737*4882a593Smuzhiyun {
738*4882a593Smuzhiyun 	unsigned int con;
739*4882a593Smuzhiyun 
740*4882a593Smuzhiyun 	writel(info->ticnt_save, info->base + S3C2410_TICNT);
741*4882a593Smuzhiyun 	if (info->ticnt_en_save) {
742*4882a593Smuzhiyun 		con = readw(info->base + S3C2410_RTCCON);
743*4882a593Smuzhiyun 		writew(con | info->ticnt_en_save, info->base + S3C2410_RTCCON);
744*4882a593Smuzhiyun 	}
745*4882a593Smuzhiyun }
746*4882a593Smuzhiyun 
747*4882a593Smuzhiyun static struct s3c_rtc_data const s3c2410_rtc_data = {
748*4882a593Smuzhiyun 	.max_user_freq		= 128,
749*4882a593Smuzhiyun 	.irq_handler		= s3c24xx_rtc_irq,
750*4882a593Smuzhiyun 	.set_freq		= s3c2410_rtc_setfreq,
751*4882a593Smuzhiyun 	.enable_tick		= s3c24xx_rtc_enable_tick,
752*4882a593Smuzhiyun 	.save_tick_cnt		= s3c24xx_rtc_save_tick_cnt,
753*4882a593Smuzhiyun 	.restore_tick_cnt	= s3c24xx_rtc_restore_tick_cnt,
754*4882a593Smuzhiyun 	.enable			= s3c24xx_rtc_enable,
755*4882a593Smuzhiyun 	.disable		= s3c24xx_rtc_disable,
756*4882a593Smuzhiyun };
757*4882a593Smuzhiyun 
758*4882a593Smuzhiyun static struct s3c_rtc_data const s3c2416_rtc_data = {
759*4882a593Smuzhiyun 	.max_user_freq		= 32768,
760*4882a593Smuzhiyun 	.irq_handler		= s3c24xx_rtc_irq,
761*4882a593Smuzhiyun 	.set_freq		= s3c2416_rtc_setfreq,
762*4882a593Smuzhiyun 	.enable_tick		= s3c24xx_rtc_enable_tick,
763*4882a593Smuzhiyun 	.select_tick_clk	= s3c2416_rtc_select_tick_clk,
764*4882a593Smuzhiyun 	.save_tick_cnt		= s3c24xx_rtc_save_tick_cnt,
765*4882a593Smuzhiyun 	.restore_tick_cnt	= s3c24xx_rtc_restore_tick_cnt,
766*4882a593Smuzhiyun 	.enable			= s3c24xx_rtc_enable,
767*4882a593Smuzhiyun 	.disable		= s3c24xx_rtc_disable,
768*4882a593Smuzhiyun };
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun static struct s3c_rtc_data const s3c2443_rtc_data = {
771*4882a593Smuzhiyun 	.max_user_freq		= 32768,
772*4882a593Smuzhiyun 	.irq_handler		= s3c24xx_rtc_irq,
773*4882a593Smuzhiyun 	.set_freq		= s3c2443_rtc_setfreq,
774*4882a593Smuzhiyun 	.enable_tick		= s3c24xx_rtc_enable_tick,
775*4882a593Smuzhiyun 	.select_tick_clk	= s3c2416_rtc_select_tick_clk,
776*4882a593Smuzhiyun 	.save_tick_cnt		= s3c24xx_rtc_save_tick_cnt,
777*4882a593Smuzhiyun 	.restore_tick_cnt	= s3c24xx_rtc_restore_tick_cnt,
778*4882a593Smuzhiyun 	.enable			= s3c24xx_rtc_enable,
779*4882a593Smuzhiyun 	.disable		= s3c24xx_rtc_disable,
780*4882a593Smuzhiyun };
781*4882a593Smuzhiyun 
782*4882a593Smuzhiyun static struct s3c_rtc_data const s3c6410_rtc_data = {
783*4882a593Smuzhiyun 	.max_user_freq		= 32768,
784*4882a593Smuzhiyun 	.needs_src_clk		= true,
785*4882a593Smuzhiyun 	.irq_handler		= s3c6410_rtc_irq,
786*4882a593Smuzhiyun 	.set_freq		= s3c6410_rtc_setfreq,
787*4882a593Smuzhiyun 	.enable_tick		= s3c6410_rtc_enable_tick,
788*4882a593Smuzhiyun 	.save_tick_cnt		= s3c6410_rtc_save_tick_cnt,
789*4882a593Smuzhiyun 	.restore_tick_cnt	= s3c6410_rtc_restore_tick_cnt,
790*4882a593Smuzhiyun 	.enable			= s3c24xx_rtc_enable,
791*4882a593Smuzhiyun 	.disable		= s3c6410_rtc_disable,
792*4882a593Smuzhiyun };
793*4882a593Smuzhiyun 
794*4882a593Smuzhiyun static const struct of_device_id s3c_rtc_dt_match[] = {
795*4882a593Smuzhiyun 	{
796*4882a593Smuzhiyun 		.compatible = "samsung,s3c2410-rtc",
797*4882a593Smuzhiyun 		.data = &s3c2410_rtc_data,
798*4882a593Smuzhiyun 	}, {
799*4882a593Smuzhiyun 		.compatible = "samsung,s3c2416-rtc",
800*4882a593Smuzhiyun 		.data = &s3c2416_rtc_data,
801*4882a593Smuzhiyun 	}, {
802*4882a593Smuzhiyun 		.compatible = "samsung,s3c2443-rtc",
803*4882a593Smuzhiyun 		.data = &s3c2443_rtc_data,
804*4882a593Smuzhiyun 	}, {
805*4882a593Smuzhiyun 		.compatible = "samsung,s3c6410-rtc",
806*4882a593Smuzhiyun 		.data = &s3c6410_rtc_data,
807*4882a593Smuzhiyun 	}, {
808*4882a593Smuzhiyun 		.compatible = "samsung,exynos3250-rtc",
809*4882a593Smuzhiyun 		.data = &s3c6410_rtc_data,
810*4882a593Smuzhiyun 	},
811*4882a593Smuzhiyun 	{ /* sentinel */ },
812*4882a593Smuzhiyun };
813*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match);
814*4882a593Smuzhiyun 
815*4882a593Smuzhiyun static struct platform_driver s3c_rtc_driver = {
816*4882a593Smuzhiyun 	.probe		= s3c_rtc_probe,
817*4882a593Smuzhiyun 	.remove		= s3c_rtc_remove,
818*4882a593Smuzhiyun 	.driver		= {
819*4882a593Smuzhiyun 		.name	= "s3c-rtc",
820*4882a593Smuzhiyun 		.pm	= &s3c_rtc_pm_ops,
821*4882a593Smuzhiyun 		.of_match_table	= of_match_ptr(s3c_rtc_dt_match),
822*4882a593Smuzhiyun 	},
823*4882a593Smuzhiyun };
824*4882a593Smuzhiyun module_platform_driver(s3c_rtc_driver);
825*4882a593Smuzhiyun 
826*4882a593Smuzhiyun MODULE_DESCRIPTION("Samsung S3C RTC Driver");
827*4882a593Smuzhiyun MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
828*4882a593Smuzhiyun MODULE_LICENSE("GPL");
829*4882a593Smuzhiyun MODULE_ALIAS("platform:s3c2410-rtc");
830