rs5c372.c 6.23 KB
Newer Older
wdenk's avatar
wdenk committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * rs5c372.c
 *
 * Device driver for Ricoh's Real Time Controller RS5C372A.
 *
 * Copyright (C) 2004 Gary Jennejohn garyj@denx.de
 *
 * Based in part in ds1307.c -
 * (C) Copyright 2001, 2002, 2003
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 * Keith Outwater, keith_outwater@mvis.com`
 * Steven Scholz, steven.scholz@imc-berlin.de
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
19
 * published by the Free Software Foundation.
wdenk's avatar
wdenk committed
20 21 22 23 24 25 26
 */

#include <common.h>
#include <command.h>
#include <rtc.h>
#include <i2c.h>

27
#if defined(CONFIG_CMD_DATE)
wdenk's avatar
wdenk committed
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
/*
 * Reads are always done starting with register 15, which requires some
 * jumping-through-hoops to access the data correctly.
 *
 * Writes are always done starting with register 0.
 */

#define DEBUG 0

#if DEBUG
static unsigned int rtc_debug = DEBUG;
#else
#define rtc_debug 0	/* gcc will remove all the debug code for us */
#endif

43 44
#ifndef CONFIG_SYS_I2C_RTC_ADDR
#define CONFIG_SYS_I2C_RTC_ADDR 0x32
wdenk's avatar
wdenk committed
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
#endif

#define RS5C372_RAM_SIZE 0x10
#define RATE_32000HZ	0x80	/* Rate Select 32.000KHz */
#define RATE_32768HZ	0x00	/* Rate Select 32.768KHz */

#define STATUS_XPT  0x10    /* data invalid because voltage was 0 */

#define USE_24HOUR_MODE 0x20
#define TWELVE_HOUR_MODE(n) ((((n) >> 5) & 1) == 0)
#define HOURS_AP(n)	(((n) >> 5) & 1)
#define HOURS_12(n)	bcd2bin((n) & 0x1F)
#define HOURS_24(n)	bcd2bin((n) & 0x3F)


static int setup_done = 0;

static int
63
rs5c372_readram(unsigned char *buf, int len)
wdenk's avatar
wdenk committed
64 65 66
{
	int ret;

67
	ret = i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 0, buf, len);
wdenk's avatar
wdenk committed
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
	if (ret != 0) {
		printf("%s: failed to read\n", __FUNCTION__);
		return ret;
	}

	if (buf[0] & STATUS_XPT)
		printf("### Warning: RTC lost power\n");

	return ret;
}

static void
rs5c372_enable(void)
{
	unsigned char buf[RS5C372_RAM_SIZE + 1];
	int ret;

	/* note that this returns reg. 15 in buf[1] */
	ret = rs5c372_readram(&buf[1], RS5C372_RAM_SIZE);
	if (ret != 0) {
		printf("%s: failed\n", __FUNCTION__);
		return;
	}

	buf[0] = 0;
	/* we want to start writing at register 0 so we have to copy the */
	/* register contents up one slot */
	for (ret = 2; ret < 9; ret++)
		buf[ret - 1] = buf[ret];
	/* registers 0 to 6 (time values) are not touched */
	buf[8] = RATE_32768HZ; /* reg. 7 */
	buf[9] = 0; /* reg. 8 */
	buf[10] = 0; /* reg. 9 */
	buf[11] = 0; /* reg. 10 */
	buf[12] = 0; /* reg. 11 */
	buf[13] = 0; /* reg. 12 */
	buf[14] = 0; /* reg. 13 */
	buf[15] = 0; /* reg. 14 */
	buf[16] = USE_24HOUR_MODE; /* reg. 15 */
107
	ret = i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 0, buf, RS5C372_RAM_SIZE+1);
wdenk's avatar
wdenk committed
108 109 110 111 112 113 114 115 116 117
	if (ret != 0) {
		printf("%s: failed\n", __FUNCTION__);
		return;
	}
	setup_done = 1;

	return;
}

static void
118
rs5c372_convert_to_time(struct rtc_time *dt, unsigned char *buf)
wdenk's avatar
wdenk committed
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
{
	/* buf[0] is register 15 */
	dt->tm_sec = bcd2bin(buf[1]);
	dt->tm_min = bcd2bin(buf[2]);

	if (TWELVE_HOUR_MODE(buf[0])) {
		dt->tm_hour = HOURS_12(buf[3]);
		if (HOURS_AP(buf[3])) /* PM */
			dt->tm_hour += 12;
	} else /* 24-hour-mode */
		dt->tm_hour = HOURS_24(buf[3]);

	dt->tm_mday = bcd2bin(buf[5]);
	dt->tm_mon = bcd2bin(buf[6]);
	dt->tm_year = bcd2bin(buf[7]);
	if (dt->tm_year >= 70)
		dt->tm_year += 1900;
	else
		dt->tm_year += 2000;
	/* 0 is Sunday */
	dt->tm_wday = bcd2bin(buf[4] & 0x07);
	dt->tm_yday = 0;
	dt->tm_isdst= 0;

	if(rtc_debug > 2) {
		printf("rs5c372_convert_to_time: year = %d\n", dt->tm_year);
		printf("rs5c372_convert_to_time: mon  = %d\n", dt->tm_mon);
		printf("rs5c372_convert_to_time: mday = %d\n", dt->tm_mday);
		printf("rs5c372_convert_to_time: hour = %d\n", dt->tm_hour);
		printf("rs5c372_convert_to_time: min  = %d\n", dt->tm_min);
		printf("rs5c372_convert_to_time: sec  = %d\n", dt->tm_sec);
	}
}

/*
 * Get the current time from the RTC
 */
Yuri Tikhonov's avatar
Yuri Tikhonov committed
156
int
wdenk's avatar
wdenk committed
157 158 159 160 161 162 163 164 165
rtc_get (struct rtc_time *tmp)
{
	unsigned char buf[RS5C372_RAM_SIZE];
	int ret;

	if (!setup_done)
		rs5c372_enable();

	if (!setup_done)
Yuri Tikhonov's avatar
Yuri Tikhonov committed
166
		return -1;
wdenk's avatar
wdenk committed
167 168 169 170 171 172 173

	memset(buf, 0, sizeof(buf));

	/* note that this returns reg. 15 in buf[0] */
	ret = rs5c372_readram(buf, RS5C372_RAM_SIZE);
	if (ret != 0) {
		printf("%s: failed\n", __FUNCTION__);
Yuri Tikhonov's avatar
Yuri Tikhonov committed
174
		return -1;
wdenk's avatar
wdenk committed
175 176 177 178
	}

	rs5c372_convert_to_time(tmp, buf);

Yuri Tikhonov's avatar
Yuri Tikhonov committed
179
	return 0;
wdenk's avatar
wdenk committed
180 181 182 183 184
}

/*
 * Set the RTC
 */
185
int rtc_set (struct rtc_time *tmp)
wdenk's avatar
wdenk committed
186 187 188 189 190 191 192 193
{
	unsigned char buf[8], reg15;
	int ret;

	if (!setup_done)
		rs5c372_enable();

	if (!setup_done)
194
		return -1;
wdenk's avatar
wdenk committed
195 196 197 198 199 200 201 202 203 204 205 206 207

	if(rtc_debug > 2) {
		printf("rtc_set: tm_year = %d\n", tmp->tm_year);
		printf("rtc_set: tm_mon	 = %d\n", tmp->tm_mon);
		printf("rtc_set: tm_mday = %d\n", tmp->tm_mday);
		printf("rtc_set: tm_hour = %d\n", tmp->tm_hour);
		printf("rtc_set: tm_min	 = %d\n", tmp->tm_min);
		printf("rtc_set: tm_sec	 = %d\n", tmp->tm_sec);
	}

	memset(buf, 0, sizeof(buf));

	/* only read register 15 */
208
	ret = i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 0, buf, 1);
wdenk's avatar
wdenk committed
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236

	if (ret == 0) {
		/* need to save register 15 */
		reg15 = buf[0];
		buf[0] = 0;	/* register address on RS5C372 */
		buf[1] = bin2bcd(tmp->tm_sec);
		buf[2] = bin2bcd(tmp->tm_min);
		/* need to handle 12 hour mode */
		if (TWELVE_HOUR_MODE(reg15)) {
			if (tmp->tm_hour >= 12) { /* PM */
				/* 12 PM is a special case */
				if (tmp->tm_hour == 12)
					buf[3] = bin2bcd(tmp->tm_hour);
				else
					buf[3] = bin2bcd(tmp->tm_hour - 12);
				buf[3] |= 0x20;
			}
		} else {
			buf[3] = bin2bcd(tmp->tm_hour);
		}

		buf[4] = bin2bcd(tmp->tm_wday);
		buf[5] = bin2bcd(tmp->tm_mday);
		buf[6] = bin2bcd(tmp->tm_mon);
		if (tmp->tm_year < 1970 || tmp->tm_year > 2069)
			printf("WARNING: year should be between 1970 and 2069!\n");
		buf[7] = bin2bcd(tmp->tm_year % 100);

237
		ret = i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 0, buf, 8);
238
		if (ret != 0) {
wdenk's avatar
wdenk committed
239
			printf("rs5c372_set_datetime(), i2c_master_send() returned %d\n",ret);
240 241 242 243
			return -1;
		}
	} else {
		return -1;
wdenk's avatar
wdenk committed
244 245
	}

246
	return 0;
wdenk's avatar
wdenk committed
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
}

/*
 * Reset the RTC. We set the date back to 1970-01-01.
 */
void
rtc_reset (void)
{
	struct rtc_time tmp;

	if (!setup_done)
		rs5c372_enable();

	if (!setup_done)
		return;

	tmp.tm_year = 1970;
	tmp.tm_mon = 1;
	/* Jan. 1, 1970 was a Thursday */
	tmp.tm_wday= 4;
	tmp.tm_mday= 1;
	tmp.tm_hour = 0;
	tmp.tm_min = 0;
	tmp.tm_sec = 0;

	rtc_set(&tmp);

	printf ("RTC:	%4d-%02d-%02d %2d:%02d:%02d UTC\n",
		tmp.tm_year, tmp.tm_mon, tmp.tm_mday,
		tmp.tm_hour, tmp.tm_min, tmp.tm_sec);

	return;
}

281
#endif