xref: /openbmc/linux/drivers/rtc/rtc-s3c.c (revision 0bf49ffb)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* drivers/rtc/rtc-s3c.c
3  *
4  * Copyright (c) 2010 Samsung Electronics Co., Ltd.
5  *		http://www.samsung.com/
6  *
7  * Copyright (c) 2004,2006 Simtec Electronics
8  *	Ben Dooks, <ben@simtec.co.uk>
9  *	http://armlinux.simtec.co.uk/
10  *
11  * S3C2410/S3C2440/S3C24XX Internal RTC Driver
12 */
13 
14 #include <linux/module.h>
15 #include <linux/fs.h>
16 #include <linux/string.h>
17 #include <linux/init.h>
18 #include <linux/platform_device.h>
19 #include <linux/interrupt.h>
20 #include <linux/rtc.h>
21 #include <linux/bcd.h>
22 #include <linux/clk.h>
23 #include <linux/log2.h>
24 #include <linux/slab.h>
25 #include <linux/of.h>
26 #include <linux/of_device.h>
27 #include <linux/uaccess.h>
28 #include <linux/io.h>
29 
30 #include <asm/irq.h>
31 #include "rtc-s3c.h"
32 
33 struct s3c_rtc {
34 	struct device *dev;
35 	struct rtc_device *rtc;
36 
37 	void __iomem *base;
38 	struct clk *rtc_clk;
39 	struct clk *rtc_src_clk;
40 	bool alarm_enabled;
41 
42 	const struct s3c_rtc_data *data;
43 
44 	int irq_alarm;
45 	spinlock_t alarm_lock;
46 
47 	bool wake_en;
48 };
49 
50 struct s3c_rtc_data {
51 	bool needs_src_clk;
52 
53 	void (*irq_handler) (struct s3c_rtc *info, int mask);
54 	void (*enable) (struct s3c_rtc *info);
55 	void (*disable) (struct s3c_rtc *info);
56 };
57 
58 static int s3c_rtc_enable_clk(struct s3c_rtc *info)
59 {
60 	int ret;
61 
62 	ret = clk_enable(info->rtc_clk);
63 	if (ret)
64 		return ret;
65 
66 	if (info->data->needs_src_clk) {
67 		ret = clk_enable(info->rtc_src_clk);
68 		if (ret) {
69 			clk_disable(info->rtc_clk);
70 			return ret;
71 		}
72 	}
73 	return 0;
74 }
75 
76 static void s3c_rtc_disable_clk(struct s3c_rtc *info)
77 {
78 	if (info->data->needs_src_clk)
79 		clk_disable(info->rtc_src_clk);
80 	clk_disable(info->rtc_clk);
81 }
82 
83 /* IRQ Handler */
84 static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
85 {
86 	struct s3c_rtc *info = (struct s3c_rtc *)id;
87 
88 	if (info->data->irq_handler)
89 		info->data->irq_handler(info, S3C2410_INTP_ALM);
90 
91 	return IRQ_HANDLED;
92 }
93 
94 /* Update control registers */
95 static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
96 {
97 	struct s3c_rtc *info = dev_get_drvdata(dev);
98 	unsigned long flags;
99 	unsigned int tmp;
100 	int ret;
101 
102 	dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled);
103 
104 	ret = s3c_rtc_enable_clk(info);
105 	if (ret)
106 		return ret;
107 
108 	tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
109 
110 	if (enabled)
111 		tmp |= S3C2410_RTCALM_ALMEN;
112 
113 	writeb(tmp, info->base + S3C2410_RTCALM);
114 
115 	spin_lock_irqsave(&info->alarm_lock, flags);
116 
117 	if (info->alarm_enabled && !enabled)
118 		s3c_rtc_disable_clk(info);
119 	else if (!info->alarm_enabled && enabled)
120 		ret = s3c_rtc_enable_clk(info);
121 
122 	info->alarm_enabled = enabled;
123 	spin_unlock_irqrestore(&info->alarm_lock, flags);
124 
125 	s3c_rtc_disable_clk(info);
126 
127 	return ret;
128 }
129 
130 /* Time read/write */
131 static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
132 {
133 	struct s3c_rtc *info = dev_get_drvdata(dev);
134 	unsigned int have_retried = 0;
135 	int ret;
136 
137 	ret = s3c_rtc_enable_clk(info);
138 	if (ret)
139 		return ret;
140 
141 retry_get_time:
142 	rtc_tm->tm_min  = readb(info->base + S3C2410_RTCMIN);
143 	rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR);
144 	rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE);
145 	rtc_tm->tm_mon  = readb(info->base + S3C2410_RTCMON);
146 	rtc_tm->tm_year = readb(info->base + S3C2410_RTCYEAR);
147 	rtc_tm->tm_sec  = readb(info->base + S3C2410_RTCSEC);
148 
149 	/* the only way to work out whether the system was mid-update
150 	 * when we read it is to check the second counter, and if it
151 	 * is zero, then we re-try the entire read
152 	 */
153 
154 	if (rtc_tm->tm_sec == 0 && !have_retried) {
155 		have_retried = 1;
156 		goto retry_get_time;
157 	}
158 
159 	rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
160 	rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
161 	rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
162 	rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
163 	rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
164 	rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
165 
166 	s3c_rtc_disable_clk(info);
167 
168 	rtc_tm->tm_year += 100;
169 	rtc_tm->tm_mon -= 1;
170 
171 	dev_dbg(dev, "read time %ptR\n", rtc_tm);
172 	return 0;
173 }
174 
175 static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
176 {
177 	struct s3c_rtc *info = dev_get_drvdata(dev);
178 	int year = tm->tm_year - 100;
179 	int ret;
180 
181 	dev_dbg(dev, "set time %ptR\n", tm);
182 
183 	/* we get around y2k by simply not supporting it */
184 
185 	if (year < 0 || year >= 100) {
186 		dev_err(dev, "rtc only supports 100 years\n");
187 		return -EINVAL;
188 	}
189 
190 	ret = s3c_rtc_enable_clk(info);
191 	if (ret)
192 		return ret;
193 
194 	writeb(bin2bcd(tm->tm_sec),  info->base + S3C2410_RTCSEC);
195 	writeb(bin2bcd(tm->tm_min),  info->base + S3C2410_RTCMIN);
196 	writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_RTCHOUR);
197 	writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_RTCDATE);
198 	writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_RTCMON);
199 	writeb(bin2bcd(year), info->base + S3C2410_RTCYEAR);
200 
201 	s3c_rtc_disable_clk(info);
202 
203 	return 0;
204 }
205 
206 static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
207 {
208 	struct s3c_rtc *info = dev_get_drvdata(dev);
209 	struct rtc_time *alm_tm = &alrm->time;
210 	unsigned int alm_en;
211 	int ret;
212 
213 	ret = s3c_rtc_enable_clk(info);
214 	if (ret)
215 		return ret;
216 
217 	alm_tm->tm_sec  = readb(info->base + S3C2410_ALMSEC);
218 	alm_tm->tm_min  = readb(info->base + S3C2410_ALMMIN);
219 	alm_tm->tm_hour = readb(info->base + S3C2410_ALMHOUR);
220 	alm_tm->tm_mon  = readb(info->base + S3C2410_ALMMON);
221 	alm_tm->tm_mday = readb(info->base + S3C2410_ALMDATE);
222 	alm_tm->tm_year = readb(info->base + S3C2410_ALMYEAR);
223 
224 	alm_en = readb(info->base + S3C2410_RTCALM);
225 
226 	s3c_rtc_disable_clk(info);
227 
228 	alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
229 
230 	dev_dbg(dev, "read alarm %d, %ptR\n", alm_en, alm_tm);
231 
232 	/* decode the alarm enable field */
233 	if (alm_en & S3C2410_RTCALM_SECEN)
234 		alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);
235 
236 	if (alm_en & S3C2410_RTCALM_MINEN)
237 		alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
238 
239 	if (alm_en & S3C2410_RTCALM_HOUREN)
240 		alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);
241 
242 	if (alm_en & S3C2410_RTCALM_DAYEN)
243 		alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);
244 
245 	if (alm_en & S3C2410_RTCALM_MONEN) {
246 		alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon);
247 		alm_tm->tm_mon -= 1;
248 	}
249 
250 	if (alm_en & S3C2410_RTCALM_YEAREN)
251 		alm_tm->tm_year = bcd2bin(alm_tm->tm_year);
252 
253 	return 0;
254 }
255 
256 static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
257 {
258 	struct s3c_rtc *info = dev_get_drvdata(dev);
259 	struct rtc_time *tm = &alrm->time;
260 	unsigned int alrm_en;
261 	int ret;
262 
263 	dev_dbg(dev, "s3c_rtc_setalarm: %d, %ptR\n", alrm->enabled, tm);
264 
265 	ret = s3c_rtc_enable_clk(info);
266 	if (ret)
267 		return ret;
268 
269 	alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
270 	writeb(0x00, info->base + S3C2410_RTCALM);
271 
272 	if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
273 		alrm_en |= S3C2410_RTCALM_SECEN;
274 		writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_ALMSEC);
275 	}
276 
277 	if (tm->tm_min < 60 && tm->tm_min >= 0) {
278 		alrm_en |= S3C2410_RTCALM_MINEN;
279 		writeb(bin2bcd(tm->tm_min), info->base + S3C2410_ALMMIN);
280 	}
281 
282 	if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
283 		alrm_en |= S3C2410_RTCALM_HOUREN;
284 		writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_ALMHOUR);
285 	}
286 
287 	if (tm->tm_mon < 12 && tm->tm_mon >= 0) {
288 		alrm_en |= S3C2410_RTCALM_MONEN;
289 		writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_ALMMON);
290 	}
291 
292 	if (tm->tm_mday <= 31 && tm->tm_mday >= 1) {
293 		alrm_en |= S3C2410_RTCALM_DAYEN;
294 		writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_ALMDATE);
295 	}
296 
297 	dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en);
298 
299 	writeb(alrm_en, info->base + S3C2410_RTCALM);
300 
301 	s3c_rtc_setaie(dev, alrm->enabled);
302 
303 	s3c_rtc_disable_clk(info);
304 
305 	return 0;
306 }
307 
308 static const struct rtc_class_ops s3c_rtcops = {
309 	.read_time	= s3c_rtc_gettime,
310 	.set_time	= s3c_rtc_settime,
311 	.read_alarm	= s3c_rtc_getalarm,
312 	.set_alarm	= s3c_rtc_setalarm,
313 	.alarm_irq_enable = s3c_rtc_setaie,
314 };
315 
316 static void s3c24xx_rtc_enable(struct s3c_rtc *info)
317 {
318 	unsigned int con, tmp;
319 
320 	con = readw(info->base + S3C2410_RTCCON);
321 	/* re-enable the device, and check it is ok */
322 	if ((con & S3C2410_RTCCON_RTCEN) == 0) {
323 		dev_info(info->dev, "rtc disabled, re-enabling\n");
324 
325 		tmp = readw(info->base + S3C2410_RTCCON);
326 		writew(tmp | S3C2410_RTCCON_RTCEN, info->base + S3C2410_RTCCON);
327 	}
328 
329 	if (con & S3C2410_RTCCON_CNTSEL) {
330 		dev_info(info->dev, "removing RTCCON_CNTSEL\n");
331 
332 		tmp = readw(info->base + S3C2410_RTCCON);
333 		writew(tmp & ~S3C2410_RTCCON_CNTSEL,
334 		       info->base + S3C2410_RTCCON);
335 	}
336 
337 	if (con & S3C2410_RTCCON_CLKRST) {
338 		dev_info(info->dev, "removing RTCCON_CLKRST\n");
339 
340 		tmp = readw(info->base + S3C2410_RTCCON);
341 		writew(tmp & ~S3C2410_RTCCON_CLKRST,
342 		       info->base + S3C2410_RTCCON);
343 	}
344 }
345 
346 static void s3c24xx_rtc_disable(struct s3c_rtc *info)
347 {
348 	unsigned int con;
349 
350 	con = readw(info->base + S3C2410_RTCCON);
351 	con &= ~S3C2410_RTCCON_RTCEN;
352 	writew(con, info->base + S3C2410_RTCCON);
353 
354 	con = readb(info->base + S3C2410_TICNT);
355 	con &= ~S3C2410_TICNT_ENABLE;
356 	writeb(con, info->base + S3C2410_TICNT);
357 }
358 
359 static void s3c6410_rtc_disable(struct s3c_rtc *info)
360 {
361 	unsigned int con;
362 
363 	con = readw(info->base + S3C2410_RTCCON);
364 	con &= ~S3C64XX_RTCCON_TICEN;
365 	con &= ~S3C2410_RTCCON_RTCEN;
366 	writew(con, info->base + S3C2410_RTCCON);
367 }
368 
369 static int s3c_rtc_remove(struct platform_device *pdev)
370 {
371 	struct s3c_rtc *info = platform_get_drvdata(pdev);
372 
373 	s3c_rtc_setaie(info->dev, 0);
374 
375 	if (info->data->needs_src_clk)
376 		clk_unprepare(info->rtc_src_clk);
377 	clk_unprepare(info->rtc_clk);
378 
379 	return 0;
380 }
381 
382 static int s3c_rtc_probe(struct platform_device *pdev)
383 {
384 	struct s3c_rtc *info = NULL;
385 	int ret;
386 
387 	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
388 	if (!info)
389 		return -ENOMEM;
390 
391 	info->dev = &pdev->dev;
392 	info->data = of_device_get_match_data(&pdev->dev);
393 	if (!info->data) {
394 		dev_err(&pdev->dev, "failed getting s3c_rtc_data\n");
395 		return -EINVAL;
396 	}
397 	spin_lock_init(&info->alarm_lock);
398 
399 	platform_set_drvdata(pdev, info);
400 
401 	info->irq_alarm = platform_get_irq(pdev, 0);
402 	if (info->irq_alarm < 0)
403 		return info->irq_alarm;
404 
405 	dev_dbg(&pdev->dev, "s3c2410_rtc: alarm irq %d\n", info->irq_alarm);
406 
407 	/* get the memory region */
408 	info->base = devm_platform_ioremap_resource(pdev, 0);
409 	if (IS_ERR(info->base))
410 		return PTR_ERR(info->base);
411 
412 	info->rtc_clk = devm_clk_get(&pdev->dev, "rtc");
413 	if (IS_ERR(info->rtc_clk)) {
414 		ret = PTR_ERR(info->rtc_clk);
415 		if (ret != -EPROBE_DEFER)
416 			dev_err(&pdev->dev, "failed to find rtc clock\n");
417 		else
418 			dev_dbg(&pdev->dev, "probe deferred due to missing rtc clk\n");
419 		return ret;
420 	}
421 	ret = clk_prepare_enable(info->rtc_clk);
422 	if (ret)
423 		return ret;
424 
425 	if (info->data->needs_src_clk) {
426 		info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src");
427 		if (IS_ERR(info->rtc_src_clk)) {
428 			ret = dev_err_probe(&pdev->dev, PTR_ERR(info->rtc_src_clk),
429 					    "failed to find rtc source clock\n");
430 			goto err_src_clk;
431 		}
432 		ret = clk_prepare_enable(info->rtc_src_clk);
433 		if (ret)
434 			goto err_src_clk;
435 	}
436 
437 	/* disable RTC enable bits potentially set by the bootloader */
438 	if (info->data->disable)
439 		info->data->disable(info);
440 
441 	/* check to see if everything is setup correctly */
442 	if (info->data->enable)
443 		info->data->enable(info);
444 
445 	dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n",
446 		readw(info->base + S3C2410_RTCCON));
447 
448 	device_init_wakeup(&pdev->dev, 1);
449 
450 	/* register RTC and exit */
451 	info->rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops,
452 					     THIS_MODULE);
453 	if (IS_ERR(info->rtc)) {
454 		dev_err(&pdev->dev, "cannot attach rtc\n");
455 		ret = PTR_ERR(info->rtc);
456 		goto err_nortc;
457 	}
458 
459 	ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq,
460 			       0, "s3c2410-rtc alarm", info);
461 	if (ret) {
462 		dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret);
463 		goto err_nortc;
464 	}
465 
466 	s3c_rtc_disable_clk(info);
467 
468 	return 0;
469 
470 err_nortc:
471 	if (info->data->disable)
472 		info->data->disable(info);
473 
474 	if (info->data->needs_src_clk)
475 		clk_disable_unprepare(info->rtc_src_clk);
476 err_src_clk:
477 	clk_disable_unprepare(info->rtc_clk);
478 
479 	return ret;
480 }
481 
482 #ifdef CONFIG_PM_SLEEP
483 
484 static int s3c_rtc_suspend(struct device *dev)
485 {
486 	struct s3c_rtc *info = dev_get_drvdata(dev);
487 	int ret;
488 
489 	ret = s3c_rtc_enable_clk(info);
490 	if (ret)
491 		return ret;
492 
493 	if (info->data->disable)
494 		info->data->disable(info);
495 
496 	if (device_may_wakeup(dev) && !info->wake_en) {
497 		if (enable_irq_wake(info->irq_alarm) == 0)
498 			info->wake_en = true;
499 		else
500 			dev_err(dev, "enable_irq_wake failed\n");
501 	}
502 
503 	return 0;
504 }
505 
506 static int s3c_rtc_resume(struct device *dev)
507 {
508 	struct s3c_rtc *info = dev_get_drvdata(dev);
509 
510 	if (info->data->enable)
511 		info->data->enable(info);
512 
513 	s3c_rtc_disable_clk(info);
514 
515 	if (device_may_wakeup(dev) && info->wake_en) {
516 		disable_irq_wake(info->irq_alarm);
517 		info->wake_en = false;
518 	}
519 
520 	return 0;
521 }
522 #endif
523 static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume);
524 
525 static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask)
526 {
527 	rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
528 }
529 
530 static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask)
531 {
532 	rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
533 	writeb(mask, info->base + S3C2410_INTP);
534 }
535 
536 static struct s3c_rtc_data const s3c2410_rtc_data = {
537 	.irq_handler		= s3c24xx_rtc_irq,
538 	.enable			= s3c24xx_rtc_enable,
539 	.disable		= s3c24xx_rtc_disable,
540 };
541 
542 static struct s3c_rtc_data const s3c2416_rtc_data = {
543 	.irq_handler		= s3c24xx_rtc_irq,
544 	.enable			= s3c24xx_rtc_enable,
545 	.disable		= s3c24xx_rtc_disable,
546 };
547 
548 static struct s3c_rtc_data const s3c2443_rtc_data = {
549 	.irq_handler		= s3c24xx_rtc_irq,
550 	.enable			= s3c24xx_rtc_enable,
551 	.disable		= s3c24xx_rtc_disable,
552 };
553 
554 static struct s3c_rtc_data const s3c6410_rtc_data = {
555 	.needs_src_clk		= true,
556 	.irq_handler		= s3c6410_rtc_irq,
557 	.enable			= s3c24xx_rtc_enable,
558 	.disable		= s3c6410_rtc_disable,
559 };
560 
561 static const __maybe_unused struct of_device_id s3c_rtc_dt_match[] = {
562 	{
563 		.compatible = "samsung,s3c2410-rtc",
564 		.data = &s3c2410_rtc_data,
565 	}, {
566 		.compatible = "samsung,s3c2416-rtc",
567 		.data = &s3c2416_rtc_data,
568 	}, {
569 		.compatible = "samsung,s3c2443-rtc",
570 		.data = &s3c2443_rtc_data,
571 	}, {
572 		.compatible = "samsung,s3c6410-rtc",
573 		.data = &s3c6410_rtc_data,
574 	}, {
575 		.compatible = "samsung,exynos3250-rtc",
576 		.data = &s3c6410_rtc_data,
577 	},
578 	{ /* sentinel */ },
579 };
580 MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match);
581 
582 static struct platform_driver s3c_rtc_driver = {
583 	.probe		= s3c_rtc_probe,
584 	.remove		= s3c_rtc_remove,
585 	.driver		= {
586 		.name	= "s3c-rtc",
587 		.pm	= &s3c_rtc_pm_ops,
588 		.of_match_table	= of_match_ptr(s3c_rtc_dt_match),
589 	},
590 };
591 module_platform_driver(s3c_rtc_driver);
592 
593 MODULE_DESCRIPTION("Samsung S3C RTC Driver");
594 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
595 MODULE_LICENSE("GPL");
596 MODULE_ALIAS("platform:s3c2410-rtc");
597