18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/arch/sh/boards/sh03/rtc.c -- CTP/PCI-SH03 on-chip RTC support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2004  Saito.K & Jeanne(ksaito@interface.co.jp)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/sched.h>
128c2ecf20Sopenharmony_ci#include <linux/time.h>
138c2ecf20Sopenharmony_ci#include <linux/bcd.h>
148c2ecf20Sopenharmony_ci#include <linux/rtc.h>
158c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
168c2ecf20Sopenharmony_ci#include <linux/io.h>
178c2ecf20Sopenharmony_ci#include <linux/rtc.h>
188c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define RTC_BASE	0xb0000000
218c2ecf20Sopenharmony_ci#define RTC_SEC1	(RTC_BASE + 0)
228c2ecf20Sopenharmony_ci#define RTC_SEC10	(RTC_BASE + 1)
238c2ecf20Sopenharmony_ci#define RTC_MIN1	(RTC_BASE + 2)
248c2ecf20Sopenharmony_ci#define RTC_MIN10	(RTC_BASE + 3)
258c2ecf20Sopenharmony_ci#define RTC_HOU1	(RTC_BASE + 4)
268c2ecf20Sopenharmony_ci#define RTC_HOU10	(RTC_BASE + 5)
278c2ecf20Sopenharmony_ci#define RTC_WEE1	(RTC_BASE + 6)
288c2ecf20Sopenharmony_ci#define RTC_DAY1	(RTC_BASE + 7)
298c2ecf20Sopenharmony_ci#define RTC_DAY10	(RTC_BASE + 8)
308c2ecf20Sopenharmony_ci#define RTC_MON1	(RTC_BASE + 9)
318c2ecf20Sopenharmony_ci#define RTC_MON10	(RTC_BASE + 10)
328c2ecf20Sopenharmony_ci#define RTC_YEA1	(RTC_BASE + 11)
338c2ecf20Sopenharmony_ci#define RTC_YEA10	(RTC_BASE + 12)
348c2ecf20Sopenharmony_ci#define RTC_YEA100	(RTC_BASE + 13)
358c2ecf20Sopenharmony_ci#define RTC_YEA1000	(RTC_BASE + 14)
368c2ecf20Sopenharmony_ci#define RTC_CTL		(RTC_BASE + 15)
378c2ecf20Sopenharmony_ci#define RTC_BUSY	1
388c2ecf20Sopenharmony_ci#define RTC_STOP	2
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(sh03_rtc_lock);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic int sh03_rtc_gettimeofday(struct device *dev, struct rtc_time *tm)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	unsigned int year, mon, day, hour, min, sec;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	spin_lock(&sh03_rtc_lock);
478c2ecf20Sopenharmony_ci again:
488c2ecf20Sopenharmony_ci	do {
498c2ecf20Sopenharmony_ci		sec  = (__raw_readb(RTC_SEC1) & 0xf) + (__raw_readb(RTC_SEC10) & 0x7) * 10;
508c2ecf20Sopenharmony_ci		min  = (__raw_readb(RTC_MIN1) & 0xf) + (__raw_readb(RTC_MIN10) & 0xf) * 10;
518c2ecf20Sopenharmony_ci		hour = (__raw_readb(RTC_HOU1) & 0xf) + (__raw_readb(RTC_HOU10) & 0xf) * 10;
528c2ecf20Sopenharmony_ci		day  = (__raw_readb(RTC_DAY1) & 0xf) + (__raw_readb(RTC_DAY10) & 0xf) * 10;
538c2ecf20Sopenharmony_ci		mon  = (__raw_readb(RTC_MON1) & 0xf) + (__raw_readb(RTC_MON10) & 0xf) * 10;
548c2ecf20Sopenharmony_ci		year = (__raw_readb(RTC_YEA1) & 0xf) + (__raw_readb(RTC_YEA10) & 0xf) * 10
558c2ecf20Sopenharmony_ci		     + (__raw_readb(RTC_YEA100 ) & 0xf) * 100
568c2ecf20Sopenharmony_ci		     + (__raw_readb(RTC_YEA1000) & 0xf) * 1000;
578c2ecf20Sopenharmony_ci	} while (sec != (__raw_readb(RTC_SEC1) & 0xf) + (__raw_readb(RTC_SEC10) & 0x7) * 10);
588c2ecf20Sopenharmony_ci	if (year == 0 || mon < 1 || mon > 12 || day > 31 || day < 1 ||
598c2ecf20Sopenharmony_ci	    hour > 23 || min > 59 || sec > 59) {
608c2ecf20Sopenharmony_ci		printk(KERN_ERR
618c2ecf20Sopenharmony_ci		       "SH-03 RTC: invalid value, resetting to 1 Jan 2000\n");
628c2ecf20Sopenharmony_ci		printk("year=%d, mon=%d, day=%d, hour=%d, min=%d, sec=%d\n",
638c2ecf20Sopenharmony_ci		       year, mon, day, hour, min, sec);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci		__raw_writeb(0, RTC_SEC1); __raw_writeb(0, RTC_SEC10);
668c2ecf20Sopenharmony_ci		__raw_writeb(0, RTC_MIN1); __raw_writeb(0, RTC_MIN10);
678c2ecf20Sopenharmony_ci		__raw_writeb(0, RTC_HOU1); __raw_writeb(0, RTC_HOU10);
688c2ecf20Sopenharmony_ci		__raw_writeb(6, RTC_WEE1);
698c2ecf20Sopenharmony_ci		__raw_writeb(1, RTC_DAY1); __raw_writeb(0, RTC_DAY10);
708c2ecf20Sopenharmony_ci		__raw_writeb(1, RTC_MON1); __raw_writeb(0, RTC_MON10);
718c2ecf20Sopenharmony_ci		__raw_writeb(0, RTC_YEA1); __raw_writeb(0, RTC_YEA10);
728c2ecf20Sopenharmony_ci		__raw_writeb(0, RTC_YEA100);
738c2ecf20Sopenharmony_ci		__raw_writeb(2, RTC_YEA1000);
748c2ecf20Sopenharmony_ci		__raw_writeb(0, RTC_CTL);
758c2ecf20Sopenharmony_ci		goto again;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	spin_unlock(&sh03_rtc_lock);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	tm->tm_sec  = sec;
818c2ecf20Sopenharmony_ci	tm->tm_min  = min;
828c2ecf20Sopenharmony_ci	tm->tm_hour = hour;
838c2ecf20Sopenharmony_ci	tm->tm_mday = day;
848c2ecf20Sopenharmony_ci	tm->tm_mon  = mon;
858c2ecf20Sopenharmony_ci	tm->tm_year = year - 1900;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return 0;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int set_rtc_mmss(struct rtc_time *tm)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	int retval = 0;
938c2ecf20Sopenharmony_ci	int real_seconds, real_minutes, cmos_minutes;
948c2ecf20Sopenharmony_ci	int i;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	/* gets recalled with irq locally disabled */
978c2ecf20Sopenharmony_ci	spin_lock(&sh03_rtc_lock);
988c2ecf20Sopenharmony_ci	for (i = 0 ; i < 1000000 ; i++)	/* may take up to 1 second... */
998c2ecf20Sopenharmony_ci		if (!(__raw_readb(RTC_CTL) & RTC_BUSY))
1008c2ecf20Sopenharmony_ci			break;
1018c2ecf20Sopenharmony_ci	cmos_minutes = (__raw_readb(RTC_MIN1) & 0xf) + (__raw_readb(RTC_MIN10) & 0xf) * 10;
1028c2ecf20Sopenharmony_ci	real_seconds = tm->tm_sec;
1038c2ecf20Sopenharmony_ci	real_minutes = tm->tm_min;
1048c2ecf20Sopenharmony_ci	if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
1058c2ecf20Sopenharmony_ci		real_minutes += 30;		/* correct for half hour time zone */
1068c2ecf20Sopenharmony_ci	real_minutes %= 60;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (abs(real_minutes - cmos_minutes) < 30) {
1098c2ecf20Sopenharmony_ci		__raw_writeb(real_seconds % 10, RTC_SEC1);
1108c2ecf20Sopenharmony_ci		__raw_writeb(real_seconds / 10, RTC_SEC10);
1118c2ecf20Sopenharmony_ci		__raw_writeb(real_minutes % 10, RTC_MIN1);
1128c2ecf20Sopenharmony_ci		__raw_writeb(real_minutes / 10, RTC_MIN10);
1138c2ecf20Sopenharmony_ci	} else {
1148c2ecf20Sopenharmony_ci		printk_once(KERN_NOTICE
1158c2ecf20Sopenharmony_ci		       "set_rtc_mmss: can't update from %d to %d\n",
1168c2ecf20Sopenharmony_ci		       cmos_minutes, real_minutes);
1178c2ecf20Sopenharmony_ci		retval = -EINVAL;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci	spin_unlock(&sh03_rtc_lock);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return retval;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ciint sh03_rtc_settimeofday(struct device *dev, struct rtc_time *tm)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	return set_rtc_mmss(tm);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic const struct rtc_class_ops rtc_generic_ops = {
1308c2ecf20Sopenharmony_ci	.read_time = sh03_rtc_gettimeofday,
1318c2ecf20Sopenharmony_ci	.set_time = sh03_rtc_settimeofday,
1328c2ecf20Sopenharmony_ci};
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic int __init sh03_time_init(void)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct platform_device *pdev;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	pdev = platform_device_register_data(NULL, "rtc-generic", -1,
1398c2ecf20Sopenharmony_ci					     &rtc_generic_ops,
1408c2ecf20Sopenharmony_ci					     sizeof(rtc_generic_ops));
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(pdev);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ciarch_initcall(sh03_time_init);
145