summaryrefslogtreecommitdiff
path: root/arch/mips/ddb5xxx/common/rtc_ds1386.c
blob: 995896ac0e39012f9b707860a429c79244908fce (plain)
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/*
 * 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;
}