11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * linux/arch/sh/boards/sh03/rtc.c -- CTP/PCI-SH03 on-chip RTC support
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Copyright (C) 2004  Saito.K & Jeanne(ksaito@interface.co.jp)
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/sched.h>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/time.h>
124f3a36a7d0eb420471506fcd46ee46f4b5cd4ebcMatt Mackall#include <linux/bcd.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/rtc.h>
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/spinlock.h>
15af514ca7d27b31e3c278e1331f0ebdb3ad385a90Paul Mundt#include <asm/io.h>
16af514ca7d27b31e3c278e1331f0ebdb3ad385a90Paul Mundt#include <asm/rtc.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_BASE	0xb0000000
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_SEC1	(RTC_BASE + 0)
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_SEC10	(RTC_BASE + 1)
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_MIN1	(RTC_BASE + 2)
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_MIN10	(RTC_BASE + 3)
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_HOU1	(RTC_BASE + 4)
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_HOU10	(RTC_BASE + 5)
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_WEE1	(RTC_BASE + 6)
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_DAY1	(RTC_BASE + 7)
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_DAY10	(RTC_BASE + 8)
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_MON1	(RTC_BASE + 9)
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_MON10	(RTC_BASE + 10)
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_YEA1	(RTC_BASE + 11)
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_YEA10	(RTC_BASE + 12)
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_YEA100	(RTC_BASE + 13)
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_YEA1000	(RTC_BASE + 14)
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_CTL		(RTC_BASE + 15)
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_BUSY	1
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_STOP	2
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
38cd1408f22d2fd8f0d09c082b07cf0b9bcfb6eac9Paul Mundtstatic DEFINE_SPINLOCK(sh03_rtc_lock);
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsunsigned long get_cmos_time(void)
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int year, mon, day, hour, min, sec;
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
44cd1408f22d2fd8f0d09c082b07cf0b9bcfb6eac9Paul Mundt	spin_lock(&sh03_rtc_lock);
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds again:
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
479d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		sec  = (__raw_readb(RTC_SEC1) & 0xf) + (__raw_readb(RTC_SEC10) & 0x7) * 10;
489d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		min  = (__raw_readb(RTC_MIN1) & 0xf) + (__raw_readb(RTC_MIN10) & 0xf) * 10;
499d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		hour = (__raw_readb(RTC_HOU1) & 0xf) + (__raw_readb(RTC_HOU10) & 0xf) * 10;
509d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		day  = (__raw_readb(RTC_DAY1) & 0xf) + (__raw_readb(RTC_DAY10) & 0xf) * 10;
519d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		mon  = (__raw_readb(RTC_MON1) & 0xf) + (__raw_readb(RTC_MON10) & 0xf) * 10;
529d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		year = (__raw_readb(RTC_YEA1) & 0xf) + (__raw_readb(RTC_YEA10) & 0xf) * 10
539d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		     + (__raw_readb(RTC_YEA100 ) & 0xf) * 100
549d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		     + (__raw_readb(RTC_YEA1000) & 0xf) * 1000;
559d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt	} while (sec != (__raw_readb(RTC_SEC1) & 0xf) + (__raw_readb(RTC_SEC10) & 0x7) * 10);
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (year == 0 || mon < 1 || mon > 12 || day > 31 || day < 1 ||
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    hour > 23 || min > 59 || sec > 59) {
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       "SH-03 RTC: invalid value, resetting to 1 Jan 2000\n");
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("year=%d, mon=%d, day=%d, hour=%d, min=%d, sec=%d\n",
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       year, mon, day, hour, min, sec);
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
639d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		__raw_writeb(0, RTC_SEC1); __raw_writeb(0, RTC_SEC10);
649d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		__raw_writeb(0, RTC_MIN1); __raw_writeb(0, RTC_MIN10);
659d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		__raw_writeb(0, RTC_HOU1); __raw_writeb(0, RTC_HOU10);
669d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		__raw_writeb(6, RTC_WEE1);
679d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		__raw_writeb(1, RTC_DAY1); __raw_writeb(0, RTC_DAY10);
689d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		__raw_writeb(1, RTC_MON1); __raw_writeb(0, RTC_MON10);
699d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		__raw_writeb(0, RTC_YEA1); __raw_writeb(0, RTC_YEA10);
709d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		__raw_writeb(0, RTC_YEA100);
719d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		__raw_writeb(2, RTC_YEA1000);
729d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		__raw_writeb(0, RTC_CTL);
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto again;
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
76cd1408f22d2fd8f0d09c082b07cf0b9bcfb6eac9Paul Mundt	spin_unlock(&sh03_rtc_lock);
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return mktime(year, mon, day, hour, min, sec);
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid sh03_rtc_gettimeofday(struct timespec *tv)
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tv->tv_sec = get_cmos_time();
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tv->tv_nsec = 0;
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int set_rtc_mmss(unsigned long nowtime)
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval = 0;
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int real_seconds, real_minutes, cmos_minutes;
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* gets recalled with irq locally disabled */
94cd1408f22d2fd8f0d09c082b07cf0b9bcfb6eac9Paul Mundt	spin_lock(&sh03_rtc_lock);
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0 ; i < 1000000 ; i++)	/* may take up to 1 second... */
969d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		if (!(__raw_readb(RTC_CTL) & RTC_BUSY))
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
989d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt	cmos_minutes = (__raw_readb(RTC_MIN1) & 0xf) + (__raw_readb(RTC_MIN10) & 0xf) * 10;
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	real_seconds = nowtime % 60;
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	real_minutes = nowtime / 60;
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		real_minutes += 30;		/* correct for half hour time zone */
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	real_minutes %= 60;
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (abs(real_minutes - cmos_minutes) < 30) {
1069d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		__raw_writeb(real_seconds % 10, RTC_SEC1);
1079d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		__raw_writeb(real_seconds / 10, RTC_SEC10);
1089d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		__raw_writeb(real_minutes % 10, RTC_MIN1);
1099d56dd3b083a3bec56e9da35ce07baca81030b03Paul Mundt		__raw_writeb(real_minutes / 10, RTC_MIN10);
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
1113e5c12409c54c30f1d1b16bba5d4d24e35aa283cStephen Hemminger		printk_once(KERN_NOTICE
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       "set_rtc_mmss: can't update from %d to %d\n",
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       cmos_minutes, real_minutes);
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -1;
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
116cd1408f22d2fd8f0d09c082b07cf0b9bcfb6eac9Paul Mundt	spin_unlock(&sh03_rtc_lock);
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint sh03_rtc_settimeofday(const time_t secs)
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long nowtime = secs;
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return set_rtc_mmss(nowtime);
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid sh03_time_init(void)
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
130af514ca7d27b31e3c278e1331f0ebdb3ad385a90Paul Mundt	rtc_sh_get_time = sh03_rtc_gettimeofday;
131af514ca7d27b31e3c278e1331f0ebdb3ad385a90Paul Mundt	rtc_sh_set_time = sh03_rtc_settimeofday;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
133