rtc_rx5c348.c 4.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * RTC routines for RICOH Rx5C348 SPI chip.
 * Copyright (C) 2000-2001 Toshiba Corporation
 *
 * 2003-2005 (c) MontaVista Software, Inc. This file is licensed under the
 * terms of the GNU General Public License version 2. This program is
 * licensed "as is" without any warranty of any kind, whether express
 * or implied.
 *
 * Support for TX4938 in 2.6 - Manish Lachwani (mlachwani@mvista.com)
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/rtc.h>
#include <linux/time.h>
17
#include <linux/bcd.h>
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 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
#include <asm/time.h>
#include <asm/tx4938/spi.h>

#define	EPOCH		2000

/* registers */
#define Rx5C348_REG_SECOND	0
#define Rx5C348_REG_MINUTE	1
#define Rx5C348_REG_HOUR	2
#define Rx5C348_REG_WEEK	3
#define Rx5C348_REG_DAY	4
#define Rx5C348_REG_MONTH	5
#define Rx5C348_REG_YEAR	6
#define Rx5C348_REG_ADJUST	7
#define Rx5C348_REG_ALARM_W_MIN	8
#define Rx5C348_REG_ALARM_W_HOUR	9
#define Rx5C348_REG_ALARM_W_WEEK	10
#define Rx5C348_REG_ALARM_D_MIN	11
#define Rx5C348_REG_ALARM_D_HOUR	12
#define Rx5C348_REG_CTL1	14
#define Rx5C348_REG_CTL2	15

/* register bits */
#define Rx5C348_BIT_PM	0x20	/* REG_HOUR */
#define Rx5C348_BIT_Y2K	0x80	/* REG_MONTH */
#define Rx5C348_BIT_24H	0x20	/* REG_CTL1 */
#define Rx5C348_BIT_XSTP	0x10	/* REG_CTL2 */

/* commands */
#define Rx5C348_CMD_W(addr)	(((addr) << 4) | 0x08)	/* single write */
#define Rx5C348_CMD_R(addr)	(((addr) << 4) | 0x0c)	/* single read */
#define Rx5C348_CMD_MW(addr)	(((addr) << 4) | 0x00)	/* burst write */
#define Rx5C348_CMD_MR(addr)	(((addr) << 4) | 0x04)	/* burst read */

static struct spi_dev_desc srtc_dev_desc = {
	.baud 		= 1000000,	/* 1.0Mbps @ Vdd 2.0V */
	.tcss		= 31,
	.tcsh		= 1,
	.tcsr		= 62,
	/* 31us for Tcss (62us for Tcsr) is required for carry operation) */
	.byteorder	= 1,		/* MSB-First */
	.polarity	= 0,		/* High-Active */
	.phase		= 1,		/* Shift-Then-Sample */

};
static int srtc_chipid;
static int srtc_24h;

static inline int
spi_rtc_io(unsigned char *inbuf, unsigned char *outbuf, unsigned int count)
{
	unsigned char *inbufs[1], *outbufs[1];
	unsigned int incounts[2], outcounts[2];
	inbufs[0] = inbuf;
	incounts[0] = count;
	incounts[1] = 0;
	outbufs[0] = outbuf;
	outcounts[0] = count;
	outcounts[1] = 0;
	return txx9_spi_io(srtc_chipid, &srtc_dev_desc,
			   inbufs, incounts, outbufs, outcounts, 0);
}

/* RTC-dependent code for time.c */

static int
rtc_rx5c348_set_time(unsigned long t)
{
	unsigned char inbuf[8];
	struct rtc_time tm;
	u8 year, month, day, hour, minute, second, century;

	/* convert */
	to_tm(t, &tm);

	year = tm.tm_year % 100;
	month = tm.tm_mon+1;	/* tm_mon starts from 0 to 11 */
	day = tm.tm_mday;
	hour = tm.tm_hour;
	minute = tm.tm_min;
	second = tm.tm_sec;
	century = tm.tm_year / 100;

	inbuf[0] = Rx5C348_CMD_MW(Rx5C348_REG_SECOND);
	BIN_TO_BCD(second);
	inbuf[1] = second;
	BIN_TO_BCD(minute);
	inbuf[2] = minute;

	if (srtc_24h) {
		BIN_TO_BCD(hour);
		inbuf[3] = hour;
	} else {
		/* hour 0 is AM12, noon is PM12 */
		inbuf[3] = 0;
		if (hour >= 12)
			inbuf[3] = Rx5C348_BIT_PM;
		hour = (hour + 11) % 12 + 1;
		BIN_TO_BCD(hour);
		inbuf[3] |= hour;
	}
	inbuf[4] = 0;	/* ignore week */
	BIN_TO_BCD(day);
	inbuf[5] = day;
	BIN_TO_BCD(month);
	inbuf[6] = month;
	if (century >= 20)
		inbuf[6] |= Rx5C348_BIT_Y2K;
	BIN_TO_BCD(year);
	inbuf[7] = year;
	/* write in one transfer to avoid data inconsistency */
	return spi_rtc_io(inbuf, NULL, 8);
}

static unsigned long
rtc_rx5c348_get_time(void)
{
	unsigned char inbuf[8], outbuf[8];
	unsigned int year, month, day, hour, minute, second;

	inbuf[0] = Rx5C348_CMD_MR(Rx5C348_REG_SECOND);
	memset(inbuf + 1, 0, 7);
	/* read in one transfer to avoid data inconsistency */
	if (spi_rtc_io(inbuf, outbuf, 8))
		return 0;
	second = outbuf[1];
	BCD_TO_BIN(second);
	minute = outbuf[2];
	BCD_TO_BIN(minute);
	if (srtc_24h) {
		hour = outbuf[3];
		BCD_TO_BIN(hour);
	} else {
		hour = outbuf[3] & ~Rx5C348_BIT_PM;
		BCD_TO_BIN(hour);
		hour %= 12;
		if (outbuf[3] & Rx5C348_BIT_PM)
			hour += 12;
	}
	day = outbuf[5];
	BCD_TO_BIN(day);
	month = outbuf[6] & ~Rx5C348_BIT_Y2K;
	BCD_TO_BIN(month);
	year = outbuf[7];
	BCD_TO_BIN(year);
	year += EPOCH;

	return mktime(year, month, day, hour, minute, second);
}

void __init
rtc_rx5c348_init(int chipid)
{
	unsigned char inbuf[2], outbuf[2];
	srtc_chipid = chipid;
	/* turn on RTC if it is not on */
	inbuf[0] = Rx5C348_CMD_R(Rx5C348_REG_CTL2);
	inbuf[1] = 0;
	spi_rtc_io(inbuf, outbuf, 2);
	if (outbuf[1] & Rx5C348_BIT_XSTP) {
		inbuf[0] = Rx5C348_CMD_W(Rx5C348_REG_CTL2);
		inbuf[1] = 0;
		spi_rtc_io(inbuf, NULL, 2);
	}

	inbuf[0] = Rx5C348_CMD_R(Rx5C348_REG_CTL1);
	inbuf[1] = 0;
	spi_rtc_io(inbuf, outbuf, 2);
	if (outbuf[1] & Rx5C348_BIT_24H)
		srtc_24h = 1;

	/* set the function pointers */
190 191
	rtc_mips_get_time = rtc_rx5c348_get_time;
	rtc_mips_set_time = rtc_rx5c348_set_time;
192
}