1/*
2 * RTC routines for RICOH Rx5C348 SPI chip.
3 * Copyright (C) 2000-2001 Toshiba Corporation
4 *
5 * 2003-2005 (c) MontaVista Software, Inc. This file is licensed under the
6 * terms of the GNU General Public License version 2. This program is
7 * licensed "as is" without any warranty of any kind, whether express
8 * or implied.
9 *
10 * Support for TX4938 in 2.6 - Manish Lachwani (mlachwani@mvista.com)
11 */
12#include <linux/init.h>
13#include <linux/kernel.h>
14#include <linux/string.h>
15#include <linux/rtc.h>
16#include <linux/time.h>
17#include <linux/bcd.h>
18#include <asm/time.h>
19#include <asm/tx4938/spi.h>
20
21#define	EPOCH		2000
22
23/* registers */
24#define Rx5C348_REG_SECOND	0
25#define Rx5C348_REG_MINUTE	1
26#define Rx5C348_REG_HOUR	2
27#define Rx5C348_REG_WEEK	3
28#define Rx5C348_REG_DAY	4
29#define Rx5C348_REG_MONTH	5
30#define Rx5C348_REG_YEAR	6
31#define Rx5C348_REG_ADJUST	7
32#define Rx5C348_REG_ALARM_W_MIN	8
33#define Rx5C348_REG_ALARM_W_HOUR	9
34#define Rx5C348_REG_ALARM_W_WEEK	10
35#define Rx5C348_REG_ALARM_D_MIN	11
36#define Rx5C348_REG_ALARM_D_HOUR	12
37#define Rx5C348_REG_CTL1	14
38#define Rx5C348_REG_CTL2	15
39
40/* register bits */
41#define Rx5C348_BIT_PM	0x20	/* REG_HOUR */
42#define Rx5C348_BIT_Y2K	0x80	/* REG_MONTH */
43#define Rx5C348_BIT_24H	0x20	/* REG_CTL1 */
44#define Rx5C348_BIT_XSTP	0x10	/* REG_CTL2 */
45
46/* commands */
47#define Rx5C348_CMD_W(addr)	(((addr) << 4) | 0x08)	/* single write */
48#define Rx5C348_CMD_R(addr)	(((addr) << 4) | 0x0c)	/* single read */
49#define Rx5C348_CMD_MW(addr)	(((addr) << 4) | 0x00)	/* burst write */
50#define Rx5C348_CMD_MR(addr)	(((addr) << 4) | 0x04)	/* burst read */
51
52static struct spi_dev_desc srtc_dev_desc = {
53	.baud 		= 1000000,	/* 1.0Mbps @ Vdd 2.0V */
54	.tcss		= 31,
55	.tcsh		= 1,
56	.tcsr		= 62,
57	/* 31us for Tcss (62us for Tcsr) is required for carry operation) */
58	.byteorder	= 1,		/* MSB-First */
59	.polarity	= 0,		/* High-Active */
60	.phase		= 1,		/* Shift-Then-Sample */
61
62};
63static int srtc_chipid;
64static int srtc_24h;
65
66static inline int
67spi_rtc_io(unsigned char *inbuf, unsigned char *outbuf, unsigned int count)
68{
69	unsigned char *inbufs[1], *outbufs[1];
70	unsigned int incounts[2], outcounts[2];
71	inbufs[0] = inbuf;
72	incounts[0] = count;
73	incounts[1] = 0;
74	outbufs[0] = outbuf;
75	outcounts[0] = count;
76	outcounts[1] = 0;
77	return txx9_spi_io(srtc_chipid, &srtc_dev_desc,
78			   inbufs, incounts, outbufs, outcounts, 0);
79}
80
81/* RTC-dependent code for time.c */
82
83static int
84rtc_rx5c348_set_time(unsigned long t)
85{
86	unsigned char inbuf[8];
87	struct rtc_time tm;
88	u8 year, month, day, hour, minute, second, century;
89
90	/* convert */
91	to_tm(t, &tm);
92
93	year = tm.tm_year % 100;
94	month = tm.tm_mon+1;	/* tm_mon starts from 0 to 11 */
95	day = tm.tm_mday;
96	hour = tm.tm_hour;
97	minute = tm.tm_min;
98	second = tm.tm_sec;
99	century = tm.tm_year / 100;
100
101	inbuf[0] = Rx5C348_CMD_MW(Rx5C348_REG_SECOND);
102	BIN_TO_BCD(second);
103	inbuf[1] = second;
104	BIN_TO_BCD(minute);
105	inbuf[2] = minute;
106
107	if (srtc_24h) {
108		BIN_TO_BCD(hour);
109		inbuf[3] = hour;
110	} else {
111		/* hour 0 is AM12, noon is PM12 */
112		inbuf[3] = 0;
113		if (hour >= 12)
114			inbuf[3] = Rx5C348_BIT_PM;
115		hour = (hour + 11) % 12 + 1;
116		BIN_TO_BCD(hour);
117		inbuf[3] |= hour;
118	}
119	inbuf[4] = 0;	/* ignore week */
120	BIN_TO_BCD(day);
121	inbuf[5] = day;
122	BIN_TO_BCD(month);
123	inbuf[6] = month;
124	if (century >= 20)
125		inbuf[6] |= Rx5C348_BIT_Y2K;
126	BIN_TO_BCD(year);
127	inbuf[7] = year;
128	/* write in one transfer to avoid data inconsistency */
129	return spi_rtc_io(inbuf, NULL, 8);
130}
131
132static unsigned long
133rtc_rx5c348_get_time(void)
134{
135	unsigned char inbuf[8], outbuf[8];
136	unsigned int year, month, day, hour, minute, second;
137
138	inbuf[0] = Rx5C348_CMD_MR(Rx5C348_REG_SECOND);
139	memset(inbuf + 1, 0, 7);
140	/* read in one transfer to avoid data inconsistency */
141	if (spi_rtc_io(inbuf, outbuf, 8))
142		return 0;
143	second = outbuf[1];
144	BCD_TO_BIN(second);
145	minute = outbuf[2];
146	BCD_TO_BIN(minute);
147	if (srtc_24h) {
148		hour = outbuf[3];
149		BCD_TO_BIN(hour);
150	} else {
151		hour = outbuf[3] & ~Rx5C348_BIT_PM;
152		BCD_TO_BIN(hour);
153		hour %= 12;
154		if (outbuf[3] & Rx5C348_BIT_PM)
155			hour += 12;
156	}
157	day = outbuf[5];
158	BCD_TO_BIN(day);
159	month = outbuf[6] & ~Rx5C348_BIT_Y2K;
160	BCD_TO_BIN(month);
161	year = outbuf[7];
162	BCD_TO_BIN(year);
163	year += EPOCH;
164
165	return mktime(year, month, day, hour, minute, second);
166}
167
168void __init
169rtc_rx5c348_init(int chipid)
170{
171	unsigned char inbuf[2], outbuf[2];
172	srtc_chipid = chipid;
173	/* turn on RTC if it is not on */
174	inbuf[0] = Rx5C348_CMD_R(Rx5C348_REG_CTL2);
175	inbuf[1] = 0;
176	spi_rtc_io(inbuf, outbuf, 2);
177	if (outbuf[1] & Rx5C348_BIT_XSTP) {
178		inbuf[0] = Rx5C348_CMD_W(Rx5C348_REG_CTL2);
179		inbuf[1] = 0;
180		spi_rtc_io(inbuf, NULL, 2);
181	}
182
183	inbuf[0] = Rx5C348_CMD_R(Rx5C348_REG_CTL1);
184	inbuf[1] = 0;
185	spi_rtc_io(inbuf, outbuf, 2);
186	if (outbuf[1] & Rx5C348_BIT_24H)
187		srtc_24h = 1;
188
189	/* set the function pointers */
190	rtc_mips_get_time = rtc_rx5c348_get_time;
191	rtc_mips_set_time = rtc_rx5c348_set_time;
192}
193