53c2df2f4e
Many RTC routines were not protected against each other, so there are potential races, for example, ntp-update against /dev/rtc. This patch fixes them using rtc_lock. Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
171 lines
3.6 KiB
C
171 lines
3.6 KiB
C
/*
|
|
* Copyright 2001 MontaVista Software Inc.
|
|
* Author: jsun@mvista.com or jsun@junsun.net
|
|
*
|
|
* arch/mips/ddb5xxx/common/rtc_ds1386.c
|
|
* low-level RTC hookups for s for Dallas 1396 chip.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*/
|
|
|
|
|
|
/*
|
|
* This file exports a function, rtc_ds1386_init(), which expects an
|
|
* uncached base address as the argument. It will set the two function
|
|
* pointers expected by the MIPS generic timer code.
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/time.h>
|
|
#include <linux/bcd.h>
|
|
|
|
#include <asm/time.h>
|
|
#include <asm/addrspace.h>
|
|
|
|
#include <asm/mc146818rtc.h>
|
|
#include <asm/debug.h>
|
|
|
|
#define EPOCH 2000
|
|
|
|
#define READ_RTC(x) *(volatile unsigned char*)(rtc_base+x)
|
|
#define WRITE_RTC(x, y) *(volatile unsigned char*)(rtc_base+x) = y
|
|
|
|
static unsigned long rtc_base;
|
|
|
|
static unsigned long
|
|
rtc_ds1386_get_time(void)
|
|
{
|
|
u8 byte;
|
|
u8 temp;
|
|
unsigned int year, month, day, hour, minute, second;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&rtc_lock, flags);
|
|
/* let us freeze external registers */
|
|
byte = READ_RTC(0xB);
|
|
byte &= 0x3f;
|
|
WRITE_RTC(0xB, byte);
|
|
|
|
/* read time data */
|
|
year = BCD2BIN(READ_RTC(0xA)) + EPOCH;
|
|
month = BCD2BIN(READ_RTC(0x9) & 0x1f);
|
|
day = BCD2BIN(READ_RTC(0x8));
|
|
minute = BCD2BIN(READ_RTC(0x2));
|
|
second = BCD2BIN(READ_RTC(0x1));
|
|
|
|
/* hour is special - deal with it later */
|
|
temp = READ_RTC(0x4);
|
|
|
|
/* enable time transfer */
|
|
byte |= 0x80;
|
|
WRITE_RTC(0xB, byte);
|
|
spin_unlock_irqrestore(&rtc_lock, flags);
|
|
|
|
/* calc hour */
|
|
if (temp & 0x40) {
|
|
/* 12 hour format */
|
|
hour = BCD2BIN(temp & 0x1f);
|
|
if (temp & 0x20) hour += 12; /* PM */
|
|
} else {
|
|
/* 24 hour format */
|
|
hour = BCD2BIN(temp & 0x3f);
|
|
}
|
|
|
|
return mktime(year, month, day, hour, minute, second);
|
|
}
|
|
|
|
static int
|
|
rtc_ds1386_set_time(unsigned long t)
|
|
{
|
|
struct rtc_time tm;
|
|
u8 byte;
|
|
u8 temp;
|
|
u8 year, month, day, hour, minute, second;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&rtc_lock, flags);
|
|
/* let us freeze external registers */
|
|
byte = READ_RTC(0xB);
|
|
byte &= 0x3f;
|
|
WRITE_RTC(0xB, byte);
|
|
|
|
/* convert */
|
|
to_tm(t, &tm);
|
|
|
|
|
|
/* check each field one by one */
|
|
year = BIN2BCD(tm.tm_year - EPOCH);
|
|
if (year != READ_RTC(0xA)) {
|
|
WRITE_RTC(0xA, year);
|
|
}
|
|
|
|
temp = READ_RTC(0x9);
|
|
month = BIN2BCD(tm.tm_mon+1); /* tm_mon starts from 0 to 11 */
|
|
if (month != (temp & 0x1f)) {
|
|
WRITE_RTC( 0x9,
|
|
(month & 0x1f) | (temp & ~0x1f) );
|
|
}
|
|
|
|
day = BIN2BCD(tm.tm_mday);
|
|
if (day != READ_RTC(0x8)) {
|
|
WRITE_RTC(0x8, day);
|
|
}
|
|
|
|
temp = READ_RTC(0x4);
|
|
if (temp & 0x40) {
|
|
/* 12 hour format */
|
|
hour = 0x40;
|
|
if (tm.tm_hour > 12) {
|
|
hour |= 0x20 | (BIN2BCD(hour-12) & 0x1f);
|
|
} else {
|
|
hour |= BIN2BCD(tm.tm_hour);
|
|
}
|
|
} else {
|
|
/* 24 hour format */
|
|
hour = BIN2BCD(tm.tm_hour) & 0x3f;
|
|
}
|
|
if (hour != temp) WRITE_RTC(0x4, hour);
|
|
|
|
minute = BIN2BCD(tm.tm_min);
|
|
if (minute != READ_RTC(0x2)) {
|
|
WRITE_RTC(0x2, minute);
|
|
}
|
|
|
|
second = BIN2BCD(tm.tm_sec);
|
|
if (second != READ_RTC(0x1)) {
|
|
WRITE_RTC(0x1, second);
|
|
}
|
|
spin_unlock_irqrestore(&rtc_lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
rtc_ds1386_init(unsigned long base)
|
|
{
|
|
unsigned char byte;
|
|
|
|
/* remember the base */
|
|
rtc_base = base;
|
|
db_assert((rtc_base & 0xe0000000) == KSEG1);
|
|
|
|
/* turn on RTC if it is not on */
|
|
byte = READ_RTC(0x9);
|
|
if (byte & 0x80) {
|
|
byte &= 0x7f;
|
|
WRITE_RTC(0x9, byte);
|
|
}
|
|
|
|
/* enable time transfer */
|
|
byte = READ_RTC(0xB);
|
|
byte |= 0x80;
|
|
WRITE_RTC(0xB, byte);
|
|
|
|
/* set the function pointers */
|
|
rtc_get_time = rtc_ds1386_get_time;
|
|
rtc_set_time = rtc_ds1386_set_time;
|
|
}
|