rtc-s3c.c 20.6 KB
Newer Older
1
/* drivers/rtc/rtc-s3c.c
2
3
4
 *
 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
 *		http://www.samsung.com/
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 *
 * Copyright (c) 2004,2006 Simtec Electronics
 *	Ben Dooks, <ben@simtec.co.uk>
 *	http://armlinux.simtec.co.uk/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * S3C2410/S3C2440/S3C24XX Internal RTC Driver
*/

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/clk.h>
26
#include <linux/log2.h>
27
#include <linux/slab.h>
28
#include <linux/of.h>
29
30
#include <linux/uaccess.h>
#include <linux/io.h>
31
32

#include <asm/irq.h>
33
#include "rtc-s3c.h"
34

35
36
37
38
39
40
struct s3c_rtc {
	struct device *dev;
	struct rtc_device *rtc;

	void __iomem *base;
	struct clk *rtc_clk;
41
	struct clk *rtc_src_clk;
42
	bool clk_disabled;
43

44
	const struct s3c_rtc_data *data;
45

46
47
	int irq_alarm;
	int irq_tick;
48

49
50
	spinlock_t pie_lock;
	spinlock_t alarm_clk_lock;
51

52
53
	int ticnt_save;
	int ticnt_en_save;
54
55
56
	bool wake_en;
};

57
58
struct s3c_rtc_data {
	int max_user_freq;
59
	bool needs_src_clk;
60
61
62
63
64
65
66
67
68
69
70

	void (*irq_handler) (struct s3c_rtc *info, int mask);
	void (*set_freq) (struct s3c_rtc *info, int freq);
	void (*enable_tick) (struct s3c_rtc *info, struct seq_file *seq);
	void (*select_tick_clk) (struct s3c_rtc *info);
	void (*save_tick_cnt) (struct s3c_rtc *info);
	void (*restore_tick_cnt) (struct s3c_rtc *info);
	void (*enable) (struct s3c_rtc *info);
	void (*disable) (struct s3c_rtc *info);
};

71
static void s3c_rtc_enable_clk(struct s3c_rtc *info)
72
73
74
{
	unsigned long irq_flags;

75
	spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
76
77
78
79
80
81
	if (info->clk_disabled) {
		clk_enable(info->rtc_clk);
		if (info->data->needs_src_clk)
			clk_enable(info->rtc_src_clk);
		info->clk_disabled = false;
	}
82
83
84
85
86
87
88
89
	spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
}

static void s3c_rtc_disable_clk(struct s3c_rtc *info)
{
	unsigned long irq_flags;

	spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
90
91
92
93
94
95
	if (!info->clk_disabled) {
		if (info->data->needs_src_clk)
			clk_disable(info->rtc_src_clk);
		clk_disable(info->rtc_clk);
		info->clk_disabled = true;
	}
96
	spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
97
98
}

99
/* IRQ Handlers */
100
static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
101
{
102
	struct s3c_rtc *info = (struct s3c_rtc *)id;
103

104
105
	if (info->data->irq_handler)
		info->data->irq_handler(info, S3C2410_INTP_TIC);
106

107
108
109
	return IRQ_HANDLED;
}

110
static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
111
{
112
	struct s3c_rtc *info = (struct s3c_rtc *)id;
113

114
115
	if (info->data->irq_handler)
		info->data->irq_handler(info, S3C2410_INTP_ALM);
116

117
118
119
120
	return IRQ_HANDLED;
}

/* Update control registers */
121
static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
122
{
123
	struct s3c_rtc *info = dev_get_drvdata(dev);
124
125
	unsigned int tmp;

126
	dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled);
127

128
129
	s3c_rtc_enable_clk(info);

130
	tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
131

132
	if (enabled)
133
134
		tmp |= S3C2410_RTCALM_ALMEN;

135
	writeb(tmp, info->base + S3C2410_RTCALM);
136

137
	s3c_rtc_disable_clk(info);
138

139
140
141
142
143
	if (enabled)
		s3c_rtc_enable_clk(info);
	else
		s3c_rtc_disable_clk(info);

144
	return 0;
145
146
}

147
/* Set RTC frequency */
148
static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq)
149
{
150
151
152
	if (!is_power_of_2(freq))
		return -EINVAL;

153
	s3c_rtc_enable_clk(info);
154
	spin_lock_irq(&info->pie_lock);
155

156
157
	if (info->data->set_freq)
		info->data->set_freq(info, freq);
158

159
	spin_unlock_irq(&info->pie_lock);
160
	s3c_rtc_disable_clk(info);
Ben Dooks's avatar
Ben Dooks committed
161
162

	return 0;
163
164
165
166
167
}

/* Time read/write */
static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
{
168
	struct s3c_rtc *info = dev_get_drvdata(dev);
169
170
	unsigned int have_retried = 0;

171
	s3c_rtc_enable_clk(info);
172

173
retry_get_time:
174
175
176
177
178
179
	rtc_tm->tm_min  = readb(info->base + S3C2410_RTCMIN);
	rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR);
	rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE);
	rtc_tm->tm_mon  = readb(info->base + S3C2410_RTCMON);
	rtc_tm->tm_year = readb(info->base + S3C2410_RTCYEAR);
	rtc_tm->tm_sec  = readb(info->base + S3C2410_RTCSEC);
180

181
	/* the only way to work out whether the system was mid-update
182
183
184
185
186
187
188
189
190
	 * when we read it is to check the second counter, and if it
	 * is zero, then we re-try the entire read
	 */

	if (rtc_tm->tm_sec == 0 && !have_retried) {
		have_retried = 1;
		goto retry_get_time;
	}

191
192
193
194
195
196
	rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
	rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
	rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
	rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
	rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
	rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
197

198
199
	s3c_rtc_disable_clk(info);

200
	rtc_tm->tm_year += 100;
201

202
	dev_dbg(dev, "read time %04d.%02d.%02d %02d:%02d:%02d\n",
203
204
		1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
		rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
205

206
207
	rtc_tm->tm_mon -= 1;

208
	return rtc_valid_tm(rtc_tm);
209
210
211
212
}

static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
{
213
	struct s3c_rtc *info = dev_get_drvdata(dev);
214
	int year = tm->tm_year - 100;
Ben Dooks's avatar
Ben Dooks committed
215

216
	dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n",
217
218
		1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
		tm->tm_hour, tm->tm_min, tm->tm_sec);
219
220

	/* we get around y2k by simply not supporting it */
221

222
	if (year < 0 || year >= 100) {
Ben Dooks's avatar
Ben Dooks committed
223
		dev_err(dev, "rtc only supports 100 years\n");
224
		return -EINVAL;
Ben Dooks's avatar
Ben Dooks committed
225
226
	}

227
	s3c_rtc_enable_clk(info);
228
229
230
231
232
233
234
235

	writeb(bin2bcd(tm->tm_sec),  info->base + S3C2410_RTCSEC);
	writeb(bin2bcd(tm->tm_min),  info->base + S3C2410_RTCMIN);
	writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_RTCHOUR);
	writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_RTCDATE);
	writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_RTCMON);
	writeb(bin2bcd(year), info->base + S3C2410_RTCYEAR);

236
	s3c_rtc_disable_clk(info);
237
238
239
240
241
242

	return 0;
}

static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
243
	struct s3c_rtc *info = dev_get_drvdata(dev);
244
245
246
	struct rtc_time *alm_tm = &alrm->time;
	unsigned int alm_en;

247
	s3c_rtc_enable_clk(info);
248

249
250
251
252
253
254
	alm_tm->tm_sec  = readb(info->base + S3C2410_ALMSEC);
	alm_tm->tm_min  = readb(info->base + S3C2410_ALMMIN);
	alm_tm->tm_hour = readb(info->base + S3C2410_ALMHOUR);
	alm_tm->tm_mon  = readb(info->base + S3C2410_ALMMON);
	alm_tm->tm_mday = readb(info->base + S3C2410_ALMDATE);
	alm_tm->tm_year = readb(info->base + S3C2410_ALMYEAR);
255

256
	alm_en = readb(info->base + S3C2410_RTCALM);
257

258
259
	s3c_rtc_disable_clk(info);

260
261
	alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;

262
	dev_dbg(dev, "read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n",
263
264
265
		alm_en,
		1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
		alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
266
267
268

	/* decode the alarm enable field */
	if (alm_en & S3C2410_RTCALM_SECEN)
269
		alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);
270
271

	if (alm_en & S3C2410_RTCALM_MINEN)
272
		alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
273
274

	if (alm_en & S3C2410_RTCALM_HOUREN)
275
		alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);
276
277

	if (alm_en & S3C2410_RTCALM_DAYEN)
278
		alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);
279
280

	if (alm_en & S3C2410_RTCALM_MONEN) {
281
		alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon);
282
283
284
285
		alm_tm->tm_mon -= 1;
	}

	if (alm_en & S3C2410_RTCALM_YEAREN)
286
		alm_tm->tm_year = bcd2bin(alm_tm->tm_year);
287
288
289
290
291
292

	return 0;
}

static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
293
	struct s3c_rtc *info = dev_get_drvdata(dev);
294
295
	struct rtc_time *tm = &alrm->time;
	unsigned int alrm_en;
296
	int year = tm->tm_year - 100;
297

298
	dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
299
300
301
		alrm->enabled,
		1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
		tm->tm_hour, tm->tm_min, tm->tm_sec);
302

303
304
	s3c_rtc_enable_clk(info);

305
306
	alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
	writeb(0x00, info->base + S3C2410_RTCALM);
307
308
309

	if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
		alrm_en |= S3C2410_RTCALM_SECEN;
310
		writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_ALMSEC);
311
312
313
314
	}

	if (tm->tm_min < 60 && tm->tm_min >= 0) {
		alrm_en |= S3C2410_RTCALM_MINEN;
315
		writeb(bin2bcd(tm->tm_min), info->base + S3C2410_ALMMIN);
316
317
318
319
	}

	if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
		alrm_en |= S3C2410_RTCALM_HOUREN;
320
		writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_ALMHOUR);
321
322
	}

323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
	if (year < 100 && year >= 0) {
		alrm_en |= S3C2410_RTCALM_YEAREN;
		writeb(bin2bcd(year), info->base + S3C2410_ALMYEAR);
	}

	if (tm->tm_mon < 12 && tm->tm_mon >= 0) {
		alrm_en |= S3C2410_RTCALM_MONEN;
		writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_ALMMON);
	}

	if (tm->tm_mday <= 31 && tm->tm_mday >= 1) {
		alrm_en |= S3C2410_RTCALM_DAYEN;
		writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_ALMDATE);
	}

338
	dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en);
339

340
	writeb(alrm_en, info->base + S3C2410_RTCALM);
341

342
	s3c_rtc_disable_clk(info);
343

344
	s3c_rtc_setaie(dev, alrm->enabled);
345

346
347
348
349
350
	return 0;
}

static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
{
351
	struct s3c_rtc *info = dev_get_drvdata(dev);
352

353
	s3c_rtc_enable_clk(info);
354

355
356
357
	if (info->data->enable_tick)
		info->data->enable_tick(info, seq);

358
	s3c_rtc_disable_clk(info);
359

360
361
362
	return 0;
}

363
static const struct rtc_class_ops s3c_rtcops = {
364
365
366
367
	.read_time	= s3c_rtc_gettime,
	.set_time	= s3c_rtc_settime,
	.read_alarm	= s3c_rtc_getalarm,
	.set_alarm	= s3c_rtc_setalarm,
368
369
	.proc		= s3c_rtc_proc,
	.alarm_irq_enable = s3c_rtc_setaie,
370
371
};

372
static void s3c24xx_rtc_enable(struct s3c_rtc *info)
373
{
374
	unsigned int con, tmp;
375

376
	con = readw(info->base + S3C2410_RTCCON);
377
378
379
	/* re-enable the device, and check it is ok */
	if ((con & S3C2410_RTCCON_RTCEN) == 0) {
		dev_info(info->dev, "rtc disabled, re-enabling\n");
380

381
		tmp = readw(info->base + S3C2410_RTCCON);
382
		writew(tmp | S3C2410_RTCCON_RTCEN, info->base + S3C2410_RTCCON);
383
	}
384

385
386
	if (con & S3C2410_RTCCON_CNTSEL) {
		dev_info(info->dev, "removing RTCCON_CNTSEL\n");
387

388
389
		tmp = readw(info->base + S3C2410_RTCCON);
		writew(tmp & ~S3C2410_RTCCON_CNTSEL,
390
		       info->base + S3C2410_RTCCON);
391
	}
392

393
394
	if (con & S3C2410_RTCCON_CLKRST) {
		dev_info(info->dev, "removing RTCCON_CLKRST\n");
395

396
397
		tmp = readw(info->base + S3C2410_RTCCON);
		writew(tmp & ~S3C2410_RTCCON_CLKRST,
398
		       info->base + S3C2410_RTCCON);
399
	}
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
}

static void s3c24xx_rtc_disable(struct s3c_rtc *info)
{
	unsigned int con;

	con = readw(info->base + S3C2410_RTCCON);
	con &= ~S3C2410_RTCCON_RTCEN;
	writew(con, info->base + S3C2410_RTCCON);

	con = readb(info->base + S3C2410_TICNT);
	con &= ~S3C2410_TICNT_ENABLE;
	writeb(con, info->base + S3C2410_TICNT);
}

static void s3c6410_rtc_disable(struct s3c_rtc *info)
{
	unsigned int con;

	con = readw(info->base + S3C2410_RTCCON);
	con &= ~S3C64XX_RTCCON_TICEN;
	con &= ~S3C2410_RTCCON_RTCEN;
	writew(con, info->base + S3C2410_RTCCON);
423
424
}

425
static int s3c_rtc_remove(struct platform_device *pdev)
426
{
427
428
429
	struct s3c_rtc *info = platform_get_drvdata(pdev);

	s3c_rtc_setaie(info->dev, 0);
430

431
432
	if (info->data->needs_src_clk)
		clk_unprepare(info->rtc_src_clk);
433
	clk_unprepare(info->rtc_clk);
434

435
436
437
	return 0;
}

438
439
static const struct of_device_id s3c_rtc_dt_match[];

440
static const struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev)
441
{
442
	const struct of_device_id *match;
443

444
	match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node);
445
	return match->data;
446
447
}

448
static int s3c_rtc_probe(struct platform_device *pdev)
449
{
450
	struct s3c_rtc *info = NULL;
451
	struct rtc_time rtc_tm;
452
453
454
	struct resource *res;
	int ret;

455
456
457
	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
	if (!info)
		return -ENOMEM;
458
459

	/* find the IRQs */
460
461
	info->irq_tick = platform_get_irq(pdev, 1);
	if (info->irq_tick < 0) {
462
		dev_err(&pdev->dev, "no irq for rtc tick\n");
463
		return info->irq_tick;
464
465
	}

466
	info->dev = &pdev->dev;
467
468
469
470
471
	info->data = s3c_rtc_get_data(pdev);
	if (!info->data) {
		dev_err(&pdev->dev, "failed getting s3c_rtc_data\n");
		return -EINVAL;
	}
472
473
474
475
476
477
478
	spin_lock_init(&info->pie_lock);
	spin_lock_init(&info->alarm_clk_lock);

	platform_set_drvdata(pdev, info);

	info->irq_alarm = platform_get_irq(pdev, 0);
	if (info->irq_alarm < 0) {
479
		dev_err(&pdev->dev, "no irq for alarm\n");
480
		return info->irq_alarm;
481
482
	}

483
	dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n",
484
		info->irq_tick, info->irq_alarm);
485
486
487

	/* get the memory region */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
488
489
490
	info->base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(info->base))
		return PTR_ERR(info->base);
491

492
493
	info->rtc_clk = devm_clk_get(&pdev->dev, "rtc");
	if (IS_ERR(info->rtc_clk)) {
494
495
496
497
498
499
		ret = PTR_ERR(info->rtc_clk);
		if (ret != -EPROBE_DEFER)
			dev_err(&pdev->dev, "failed to find rtc clock\n");
		else
			dev_dbg(&pdev->dev, "probe deferred due to missing rtc clk\n");
		return ret;
500
	}
501
	clk_prepare_enable(info->rtc_clk);
502

503
504
505
	if (info->data->needs_src_clk) {
		info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src");
		if (IS_ERR(info->rtc_src_clk)) {
506
507
508
509
510
511
512
			ret = PTR_ERR(info->rtc_src_clk);
			if (ret != -EPROBE_DEFER)
				dev_err(&pdev->dev,
					"failed to find rtc source clock\n");
			else
				dev_dbg(&pdev->dev,
					"probe deferred due to missing rtc src clk\n");
513
			goto err_src_clk;
514
515
		}
		clk_prepare_enable(info->rtc_src_clk);
516
517
	}

518
	/* check to see if everything is setup correctly */
519
520
	if (info->data->enable)
		info->data->enable(info);
521

522
	dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n",
523
		readw(info->base + S3C2410_RTCCON));
524

525
526
	device_init_wakeup(&pdev->dev, 1);

527
	/* Check RTC Time */
528
	if (s3c_rtc_gettime(&pdev->dev, &rtc_tm)) {
529
530
531
532
533
534
535
536
537
538
539
540
		rtc_tm.tm_year	= 100;
		rtc_tm.tm_mon	= 0;
		rtc_tm.tm_mday	= 1;
		rtc_tm.tm_hour	= 0;
		rtc_tm.tm_min	= 0;
		rtc_tm.tm_sec	= 0;

		s3c_rtc_settime(&pdev->dev, &rtc_tm);

		dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n");
	}

541
	/* register RTC and exit */
542
	info->rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops,
543
					     THIS_MODULE);
544
	if (IS_ERR(info->rtc)) {
545
		dev_err(&pdev->dev, "cannot attach rtc\n");
546
		ret = PTR_ERR(info->rtc);
547
548
549
		goto err_nortc;
	}

550
	ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq,
551
			       0, "s3c2410-rtc alarm", info);
552
553
554
555
	if (ret) {
		dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret);
		goto err_nortc;
	}
556

557
	ret = devm_request_irq(&pdev->dev, info->irq_tick, s3c_rtc_tickirq,
558
			       0, "s3c2410-rtc tick", info);
559
560
561
562
	if (ret) {
		dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_tick, ret);
		goto err_nortc;
	}
563

564
565
	if (info->data->select_tick_clk)
		info->data->select_tick_clk(info);
566

567
	s3c_rtc_setfreq(info, 1);
568

569
570
	return 0;

571
err_nortc:
572
573
	if (info->data->disable)
		info->data->disable(info);
574
575
576

	if (info->data->needs_src_clk)
		clk_disable_unprepare(info->rtc_src_clk);
577
err_src_clk:
578
	clk_disable_unprepare(info->rtc_clk);
579
580
581
582

	return ret;
}

583
#ifdef CONFIG_PM_SLEEP
584

585
static int s3c_rtc_suspend(struct device *dev)
586
{
587
	struct s3c_rtc *info = dev_get_drvdata(dev);
588

589
	s3c_rtc_enable_clk(info);
590

591
	/* save TICNT for anyone using periodic interrupts */
592
593
594
595
596
	if (info->data->save_tick_cnt)
		info->data->save_tick_cnt(info);

	if (info->data->disable)
		info->data->disable(info);
597

598
599
600
	if (device_may_wakeup(dev) && !info->wake_en) {
		if (enable_irq_wake(info->irq_alarm) == 0)
			info->wake_en = true;
601
		else
602
			dev_err(dev, "enable_irq_wake failed\n");
603
	}
604

605
606
607
	return 0;
}

608
static int s3c_rtc_resume(struct device *dev)
609
{
610
	struct s3c_rtc *info = dev_get_drvdata(dev);
611

612
613
614
615
616
	if (info->data->enable)
		info->data->enable(info);

	if (info->data->restore_tick_cnt)
		info->data->restore_tick_cnt(info);
617

618
619
	s3c_rtc_disable_clk(info);

620
621
622
	if (device_may_wakeup(dev) && info->wake_en) {
		disable_irq_wake(info->irq_alarm);
		info->wake_en = false;
623
	}
624

625
626
627
	return 0;
}
#endif
628
629
static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume);

630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask)
{
	rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
}

static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask)
{
	rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
	writeb(mask, info->base + S3C2410_INTP);
}

static void s3c2410_rtc_setfreq(struct s3c_rtc *info, int freq)
{
	unsigned int tmp = 0;
	int val;

	tmp = readb(info->base + S3C2410_TICNT);
	tmp &= S3C2410_TICNT_ENABLE;

	val = (info->rtc->max_user_freq / freq) - 1;
	tmp |= val;

	writel(tmp, info->base + S3C2410_TICNT);
}

static void s3c2416_rtc_setfreq(struct s3c_rtc *info, int freq)
{
	unsigned int tmp = 0;
	int val;

	tmp = readb(info->base + S3C2410_TICNT);
	tmp &= S3C2410_TICNT_ENABLE;

	val = (info->rtc->max_user_freq / freq) - 1;

	tmp |= S3C2443_TICNT_PART(val);
	writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1);

	writel(S3C2416_TICNT2_PART(val), info->base + S3C2416_TICNT2);

	writel(tmp, info->base + S3C2410_TICNT);
}

static void s3c2443_rtc_setfreq(struct s3c_rtc *info, int freq)
{
	unsigned int tmp = 0;
	int val;

	tmp = readb(info->base + S3C2410_TICNT);
	tmp &= S3C2410_TICNT_ENABLE;

	val = (info->rtc->max_user_freq / freq) - 1;

	tmp |= S3C2443_TICNT_PART(val);
	writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1);

	writel(tmp, info->base + S3C2410_TICNT);
}

static void s3c6410_rtc_setfreq(struct s3c_rtc *info, int freq)
{
	int val;

	val = (info->rtc->max_user_freq / freq) - 1;
	writel(val, info->base + S3C2410_TICNT);
}

static void s3c24xx_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq)
{
	unsigned int ticnt;

	ticnt = readb(info->base + S3C2410_TICNT);
	ticnt &= S3C2410_TICNT_ENABLE;

	seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt  ? "yes" : "no");
}

static void s3c2416_rtc_select_tick_clk(struct s3c_rtc *info)
{
	unsigned int con;

	con = readw(info->base + S3C2410_RTCCON);
	con |= S3C2443_RTCCON_TICSEL;
	writew(con, info->base + S3C2410_RTCCON);
}

static void s3c6410_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq)
{
	unsigned int ticnt;

	ticnt = readw(info->base + S3C2410_RTCCON);
	ticnt &= S3C64XX_RTCCON_TICEN;

	seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt  ? "yes" : "no");
}

static void s3c24xx_rtc_save_tick_cnt(struct s3c_rtc *info)
{
	info->ticnt_save = readb(info->base + S3C2410_TICNT);
}

static void s3c24xx_rtc_restore_tick_cnt(struct s3c_rtc *info)
{
	writeb(info->ticnt_save, info->base + S3C2410_TICNT);
}

static void s3c6410_rtc_save_tick_cnt(struct s3c_rtc *info)
{
	info->ticnt_en_save = readw(info->base + S3C2410_RTCCON);
	info->ticnt_en_save &= S3C64XX_RTCCON_TICEN;
	info->ticnt_save = readl(info->base + S3C2410_TICNT);
}

static void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info)
{
	unsigned int con;

	writel(info->ticnt_save, info->base + S3C2410_TICNT);
	if (info->ticnt_en_save) {
		con = readw(info->base + S3C2410_RTCCON);
750
		writew(con | info->ticnt_en_save, info->base + S3C2410_RTCCON);
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
	}
}

static struct s3c_rtc_data const s3c2410_rtc_data = {
	.max_user_freq		= 128,
	.irq_handler		= s3c24xx_rtc_irq,
	.set_freq		= s3c2410_rtc_setfreq,
	.enable_tick		= s3c24xx_rtc_enable_tick,
	.save_tick_cnt		= s3c24xx_rtc_save_tick_cnt,
	.restore_tick_cnt	= s3c24xx_rtc_restore_tick_cnt,
	.enable			= s3c24xx_rtc_enable,
	.disable		= s3c24xx_rtc_disable,
};

static struct s3c_rtc_data const s3c2416_rtc_data = {
	.max_user_freq		= 32768,
	.irq_handler		= s3c24xx_rtc_irq,
	.set_freq		= s3c2416_rtc_setfreq,
	.enable_tick		= s3c24xx_rtc_enable_tick,
	.select_tick_clk	= s3c2416_rtc_select_tick_clk,
	.save_tick_cnt		= s3c24xx_rtc_save_tick_cnt,
	.restore_tick_cnt	= s3c24xx_rtc_restore_tick_cnt,
	.enable			= s3c24xx_rtc_enable,
	.disable		= s3c24xx_rtc_disable,
};

static struct s3c_rtc_data const s3c2443_rtc_data = {
	.max_user_freq		= 32768,
	.irq_handler		= s3c24xx_rtc_irq,
	.set_freq		= s3c2443_rtc_setfreq,
	.enable_tick		= s3c24xx_rtc_enable_tick,
	.select_tick_clk	= s3c2416_rtc_select_tick_clk,
	.save_tick_cnt		= s3c24xx_rtc_save_tick_cnt,
	.restore_tick_cnt	= s3c24xx_rtc_restore_tick_cnt,
	.enable			= s3c24xx_rtc_enable,
	.disable		= s3c24xx_rtc_disable,
};

static struct s3c_rtc_data const s3c6410_rtc_data = {
	.max_user_freq		= 32768,
791
	.needs_src_clk		= true,
792
793
794
795
796
797
798
	.irq_handler		= s3c6410_rtc_irq,
	.set_freq		= s3c6410_rtc_setfreq,
	.enable_tick		= s3c6410_rtc_enable_tick,
	.save_tick_cnt		= s3c6410_rtc_save_tick_cnt,
	.restore_tick_cnt	= s3c6410_rtc_restore_tick_cnt,
	.enable			= s3c24xx_rtc_enable,
	.disable		= s3c6410_rtc_disable,
799
800
};

801
static const struct of_device_id s3c_rtc_dt_match[] = {
802
	{
803
		.compatible = "samsung,s3c2410-rtc",
804
		.data = &s3c2410_rtc_data,
805
	}, {
806
		.compatible = "samsung,s3c2416-rtc",
807
		.data = &s3c2416_rtc_data,
808
	}, {
809
		.compatible = "samsung,s3c2443-rtc",
810
		.data = &s3c2443_rtc_data,
811
	}, {
812
		.compatible = "samsung,s3c6410-rtc",
813
		.data = &s3c6410_rtc_data,
814
815
	}, {
		.compatible = "samsung,exynos3250-rtc",
816
		.data = &s3c6410_rtc_data,
817
	},
818
	{ /* sentinel */ },
819
820
};
MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match);
821
822

static struct platform_driver s3c_rtc_driver = {
823
	.probe		= s3c_rtc_probe,
824
	.remove		= s3c_rtc_remove,
825
	.driver		= {
826
		.name	= "s3c-rtc",
827
		.pm	= &s3c_rtc_pm_ops,
828
		.of_match_table	= of_match_ptr(s3c_rtc_dt_match),
829
830
	},
};
831
module_platform_driver(s3c_rtc_driver);
832
833
834
835

MODULE_DESCRIPTION("Samsung S3C RTC Driver");
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
MODULE_LICENSE("GPL");
836
MODULE_ALIAS("platform:s3c2410-rtc");