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