2 * linux/arch/sh/kernel/rtc.c -- SH3 / SH4 on-chip RTC support
4 * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
5 * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
8 #include <linux/init.h>
9 #include <linux/kernel.h>
10 #include <linux/sched.h>
11 #include <linux/time.h>
12 #include <linux/bcd.h>
16 void sh_rtc_gettimeofday(struct timespec
*ts
)
18 unsigned int sec128
, sec
, sec2
, min
, hr
, wk
, day
, mon
, yr
, yr100
, cf_bit
;
23 local_irq_save(flags
);
24 ctrl_outb(0, RCR1
); /* Clear CF-bit */
25 sec128
= ctrl_inb(R64CNT
);
26 sec
= ctrl_inb(RSECCNT
);
27 min
= ctrl_inb(RMINCNT
);
28 hr
= ctrl_inb(RHRCNT
);
29 wk
= ctrl_inb(RWKCNT
);
30 day
= ctrl_inb(RDAYCNT
);
31 mon
= ctrl_inb(RMONCNT
);
32 #if defined(CONFIG_CPU_SH4)
33 yr
= ctrl_inw(RYRCNT
);
37 yr
= ctrl_inb(RYRCNT
);
38 yr100
= (yr
== 0x99) ? 0x19 : 0x20;
40 sec2
= ctrl_inb(R64CNT
);
41 cf_bit
= ctrl_inb(RCR1
) & RCR1_CF
;
42 local_irq_restore(flags
);
43 } while (cf_bit
!= 0 || ((sec128
^ sec2
) & RTC_BIT_INVERTED
) != 0);
53 if (yr
> 99 || mon
< 1 || mon
> 12 || day
> 31 || day
< 1 ||
54 hr
> 23 || min
> 59 || sec
> 59) {
56 "SH RTC: invalid value, resetting to 1 Jan 2000\n");
57 local_irq_save(flags
);
58 ctrl_outb(RCR2_RESET
, RCR2
); /* Reset & Stop */
59 ctrl_outb(0, RSECCNT
);
60 ctrl_outb(0, RMINCNT
);
63 ctrl_outb(1, RDAYCNT
);
64 ctrl_outb(1, RMONCNT
);
65 #if defined(CONFIG_CPU_SH4)
66 ctrl_outw(0x2000, RYRCNT
);
70 ctrl_outb(RCR2_RTCEN
|RCR2_START
, RCR2
); /* Start */
74 #if RTC_BIT_INVERTED != 0
75 if ((sec128
& RTC_BIT_INVERTED
))
79 ts
->tv_sec
= mktime(yr100
* 100 + yr
, mon
, day
, hr
, min
, sec
);
80 ts
->tv_nsec
= ((sec128
* 1000000) / 128) * 1000;
84 * Changed to only care about tv_sec, and not the full timespec struct
85 * (i.e. tv_nsec). It can easily be switched to timespec for future cpus
86 * that support setting usec or nsec RTC values.
88 int sh_rtc_settimeofday(const time_t secs
)
91 int real_seconds
, real_minutes
, cmos_minutes
;
94 local_irq_save(flags
);
95 ctrl_outb(RCR2_RESET
, RCR2
); /* Reset pre-scaler & stop RTC */
97 cmos_minutes
= ctrl_inb(RMINCNT
);
98 BCD_TO_BIN(cmos_minutes
);
101 * since we're only adjusting minutes and seconds,
102 * don't interfere with hour overflow. This avoids
103 * messing with unknown time zones but requires your
104 * RTC not to be off by more than 15 minutes
106 real_seconds
= secs
% 60;
107 real_minutes
= secs
/ 60;
108 if (((abs(real_minutes
- cmos_minutes
) + 15)/30) & 1)
109 real_minutes
+= 30; /* correct for half hour time zone */
112 if (abs(real_minutes
- cmos_minutes
) < 30) {
113 BIN_TO_BCD(real_seconds
);
114 BIN_TO_BCD(real_minutes
);
115 ctrl_outb(real_seconds
, RSECCNT
);
116 ctrl_outb(real_minutes
, RMINCNT
);
119 "set_rtc_time: can't update from %d to %d\n",
120 cmos_minutes
, real_minutes
);
124 ctrl_outb(RCR2_RTCEN
|RCR2_START
, RCR2
); /* Start RTC */
125 local_irq_restore(flags
);