| /* |
| * 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> |
| #include <linux/bcd.h> |
| #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 */ |
| rtc_mips_get_time = rtc_rx5c348_get_time; |
| rtc_mips_set_time = rtc_rx5c348_set_time; |
| } |