1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
/*
* linux/arch/sh/kernel/rtc.c -- SH3 / SH4 on-chip RTC support
*
* Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
* Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/time.h>
#include <asm/io.h>
#include <asm/rtc.h>
#ifndef BCD_TO_BIN
#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
#endif
#ifndef BIN_TO_BCD
#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10)
#endif
void sh_rtc_gettimeofday(struct timeval *tv)
{
unsigned int sec128, sec, min, hr, wk, day, mon, yr, yr100;
again:
do {
ctrl_outb(0, RCR1); /* Clear CF-bit */
sec128 = ctrl_inb(R64CNT);
sec = ctrl_inb(RSECCNT);
min = ctrl_inb(RMINCNT);
hr = ctrl_inb(RHRCNT);
wk = ctrl_inb(RWKCNT);
day = ctrl_inb(RDAYCNT);
mon = ctrl_inb(RMONCNT);
#if defined(__SH4__)
yr = ctrl_inw(RYRCNT);
yr100 = (yr >> 8);
yr &= 0xff;
#else
yr = ctrl_inb(RYRCNT);
yr100 = (yr == 0x99) ? 0x19 : 0x20;
#endif
} while ((ctrl_inb(RCR1) & RCR1_CF) != 0);
#if RTC_BIT_INVERTED != 0
/* Work around to avoid reading correct value. */
if (sec128 == RTC_BIT_INVERTED) {
schedule_timeout(1);
goto again;
}
#endif
BCD_TO_BIN(yr100);
BCD_TO_BIN(yr);
BCD_TO_BIN(mon);
BCD_TO_BIN(day);
BCD_TO_BIN(hr);
BCD_TO_BIN(min);
BCD_TO_BIN(sec);
if (yr > 99 || mon < 1 || mon > 12 || day > 31 || day < 1 ||
hr > 23 || min > 59 || sec > 59) {
printk(KERN_ERR
"SH RTC: invalid value, resetting to 1 Jan 2000\n");
ctrl_outb(RCR2_RESET, RCR2); /* Reset & Stop */
ctrl_outb(0, RSECCNT);
ctrl_outb(0, RMINCNT);
ctrl_outb(0, RHRCNT);
ctrl_outb(6, RWKCNT);
ctrl_outb(1, RDAYCNT);
ctrl_outb(1, RMONCNT);
#if defined(__SH4__)
ctrl_outw(0x2000, RYRCNT);
#else
ctrl_outb(0, RYRCNT);
#endif
ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2); /* Start */
goto again;
}
tv->tv_sec = mktime(yr100 * 100 + yr, mon, day, hr, min, sec);
tv->tv_usec = ((sec128 ^ RTC_BIT_INVERTED) * 1000000) / 128;
}
static int set_rtc_time(unsigned long nowtime)
{
int retval = 0;
int real_seconds, real_minutes, cmos_minutes;
ctrl_outb(RCR2_RESET, RCR2); /* Reset pre-scaler & stop RTC */
cmos_minutes = ctrl_inb(RMINCNT);
BCD_TO_BIN(cmos_minutes);
/*
* since we're only adjusting minutes and seconds,
* don't interfere with hour overflow. This avoids
* messing with unknown time zones but requires your
* RTC not to be off by more than 15 minutes
*/
real_seconds = nowtime % 60;
real_minutes = nowtime / 60;
if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
real_minutes += 30; /* correct for half hour time zone */
real_minutes %= 60;
if (abs(real_minutes - cmos_minutes) < 30) {
BIN_TO_BCD(real_seconds);
BIN_TO_BCD(real_minutes);
ctrl_outb(real_seconds, RSECCNT);
ctrl_outb(real_minutes, RMINCNT);
} else {
printk(KERN_WARNING
"set_rtc_time: can't update from %d to %d\n",
cmos_minutes, real_minutes);
retval = -1;
}
ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2); /* Start RTC */
return retval;
}
int sh_rtc_settimeofday(const struct timeval *tv)
{
#if RTC_BIT_INVERTED != 0
/* This is not accurate, but better than nothing. */
schedule_timeout(HZ/2);
#endif
return set_rtc_time(tv->tv_sec);
}
|