Commit | Line | Data |
---|---|---|
23fbee9d RB |
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 */ | |
d23ee8fe YY |
200 | rtc_mips_get_time = rtc_rx5c348_get_time; |
201 | rtc_mips_set_time = rtc_rx5c348_set_time; | |
23fbee9d | 202 | } |