blob: b94163f7486baa1a4c0b01364f915fd6290c5283 [file] [log] [blame]
wdenkfe8c2802002-11-03 00:38:21 +00001/*
2 * ds1302.c - Support for the Dallas Semiconductor DS1302 Timekeeping Chip
3 *
4 * Rex G. Feany <rfeany@zumanetworks.com>
5 *
6 */
7
8#include <common.h>
9#include <command.h>
10#include <rtc.h>
11
wdenkfe8c2802002-11-03 00:38:21 +000012/* GPP Pins */
13#define DATA 0x200
14#define SCLK 0x400
15#define RST 0x800
16
17/* Happy Fun Defines(tm) */
18#define RESET rtc_go_low(RST), rtc_go_low(SCLK)
19#define N_RESET rtc_go_high(RST), rtc_go_low(SCLK)
20
21#define CLOCK_HIGH rtc_go_high(SCLK)
22#define CLOCK_LOW rtc_go_low(SCLK)
23
24#define DATA_HIGH rtc_go_high(DATA)
25#define DATA_LOW rtc_go_low(DATA)
26#define DATA_READ (GTREGREAD(GPP_VALUE) & DATA)
27
28#undef RTC_DEBUG
29
30#ifdef RTC_DEBUG
31# define DPRINTF(x,args...) printf("ds1302: " x , ##args)
32static inline void DUMP(const char *ptr, int num)
33{
34 while (num--) printf("%x ", *ptr++);
35 printf("]\n");
36}
37#else
38# define DPRINTF(x,args...)
39# define DUMP(ptr, num)
40#endif
41
42/* time data format for DS1302 */
43struct ds1302_st
44{
45 unsigned char CH:1; /* clock halt 1=stop 0=start */
46 unsigned char sec10:3;
47 unsigned char sec:4;
48
49 unsigned char zero0:1;
50 unsigned char min10:3;
51 unsigned char min:4;
52
53 unsigned char fmt:1; /* 1=12 hour 0=24 hour */
54 unsigned char zero1:1;
55 unsigned char hr10:2; /* 10 (0-2) or am/pm (am/pm, 0-1) */
56 unsigned char hr:4;
57
58 unsigned char zero2:2;
59 unsigned char date10:2;
60 unsigned char date:4;
61
62 unsigned char zero3:3;
63 unsigned char month10:1;
64 unsigned char month:4;
65
66 unsigned char zero4:5;
Wolfgang Denk53677ef2008-05-20 16:00:29 +020067 unsigned char day:3; /* day of week */
wdenkfe8c2802002-11-03 00:38:21 +000068
69 unsigned char year10:4;
70 unsigned char year:4;
71
72 unsigned char WP:1; /* write protect 1=protect 0=unprot */
73 unsigned char zero5:7;
74};
75
76static int ds1302_initted=0;
77
78/* Pin control */
79static inline void
80rtc_go_high(unsigned int mask)
81{
82 unsigned int f = GTREGREAD(GPP_VALUE) | mask;
83
84 GT_REG_WRITE(GPP_VALUE, f);
85}
86
87static inline void
88rtc_go_low(unsigned int mask)
89{
90 unsigned int f = GTREGREAD(GPP_VALUE) & ~mask;
91
92 GT_REG_WRITE(GPP_VALUE, f);
93}
94
95static inline void
96rtc_go_input(unsigned int mask)
97{
98 unsigned int f = GTREGREAD(GPP_IO_CONTROL) & ~mask;
99
100 GT_REG_WRITE(GPP_IO_CONTROL, f);
101}
102
103static inline void
104rtc_go_output(unsigned int mask)
105{
106 unsigned int f = GTREGREAD(GPP_IO_CONTROL) | mask;
107
108 GT_REG_WRITE(GPP_IO_CONTROL, f);
109}
110
111/* Access data in RTC */
112
113static void
114write_byte(unsigned char b)
115{
116 int i;
117 unsigned char mask=1;
118
119 for(i=0;i<8;i++) {
120 CLOCK_LOW; /* Lower clock */
121 (b&mask)?DATA_HIGH:DATA_LOW; /* set data */
122 udelay(1);
123 CLOCK_HIGH; /* latch data with rising clock */
124 udelay(1);
125 mask=mask<<1;
126 }
127}
128
129static unsigned char
130read_byte(void)
131{
132 int i;
133 unsigned char mask=1;
134 unsigned char b=0;
135
136 for(i=0;i<8;i++) {
137 CLOCK_LOW;
138 udelay(1);
139 if (DATA_READ) b|=mask; /* if this bit is high, set in b */
140 CLOCK_HIGH; /* clock out next bit */
141 udelay(1);
142 mask=mask<<1;
143 }
144 return b;
145}
146
147static void
148read_ser_drv(unsigned char addr, unsigned char *buf, int count)
149{
150 int i;
151#ifdef RTC_DEBUG
152 char *foo = buf;
153#endif
154
155 DPRINTF("READ 0x%x bytes @ 0x%x [ ", count, addr);
156
157 addr|=1; /* READ */
158 N_RESET;
159 udelay(4);
160 write_byte(addr);
161 rtc_go_input(DATA); /* Put gpp pin into input mode */
162 udelay(1);
163 for(i=0;i<count;i++) *(buf++)=read_byte();
164 RESET;
165 rtc_go_output(DATA);/* Reset gpp for output */
166 udelay(4);
167
168 DUMP(foo, count);
169}
170
171static void
172write_ser_drv(unsigned char addr, unsigned char *buf, int count)
173{
174 int i;
175
176 DPRINTF("WRITE 0x%x bytes @ 0x%x [ ", count, addr);
177 DUMP(buf, count);
178
179 addr&=~1; /* WRITE */
180 N_RESET;
181 udelay(4);
182 write_byte(addr);
183 for(i=0;i<count;i++) write_byte(*(buf++));
184 RESET;
185 udelay(4);
186
187}
188
189void
190rtc_init(void)
191{
Wolfgang Denk53677ef2008-05-20 16:00:29 +0200192 struct ds1302_st bbclk;
wdenkfe8c2802002-11-03 00:38:21 +0000193 unsigned char b;
194 int mod;
195
196 DPRINTF("init\n");
197
198 rtc_go_output(DATA|SCLK|RST);
199
200 /* disable write protect */
201 b = 0;
202 write_ser_drv(0x8e,&b,1);
203
204 /* enable trickle */
205 b = 0xa5; /* 1010.0101 */
206 write_ser_drv(0x90,&b,1);
207
208 /* read burst */
209 read_ser_drv(0xbe, (unsigned char *)&bbclk, 8);
210
211 /* Sanity checks */
212 mod = 0;
213 if (bbclk.CH) {
214 printf("ds1302: Clock was halted, starting clock\n");
215 bbclk.CH=0;
216 mod=1;
217 }
218
219 if (bbclk.fmt) {
220 printf("ds1302: Clock was in 12 hour mode, fixing\n");
221 bbclk.fmt=0;
222 mod=1;
223 }
224
225 if (bbclk.year>9) {
226 printf("ds1302: Year was corrupted, fixing\n");
Wolfgang Denk41253be2005-12-04 11:20:57 +0100227 bbclk.year10=100/10; /* 2000 - why not? ;) */
wdenkfe8c2802002-11-03 00:38:21 +0000228 bbclk.year=0;
229 mod=1;
230 }
231
232 /* Write out the changes if needed */
233 if (mod) {
234 /* enable write protect */
235 bbclk.WP = 1;
236 write_ser_drv(0xbe,(unsigned char *)&bbclk,8);
237 } else {
238 /* Else just turn write protect on */
239 b = 0x80;
240 write_ser_drv(0x8e,&b,1);
241 }
242 DPRINTF("init done\n");
243
244 ds1302_initted=1;
245}
246
247void
248rtc_reset(void)
249{
250 if(!ds1302_initted) rtc_init();
251 /* TODO */
252}
253
Yuri Tikhonovb73a19e2008-03-20 17:56:04 +0300254int
wdenkfe8c2802002-11-03 00:38:21 +0000255rtc_get(struct rtc_time *tmp)
256{
Yuri Tikhonovb73a19e2008-03-20 17:56:04 +0300257 int rel = 0;
wdenkfe8c2802002-11-03 00:38:21 +0000258 struct ds1302_st bbclk;
259
260 if(!ds1302_initted) rtc_init();
261
262 read_ser_drv(0xbe,(unsigned char *)&bbclk, 8); /* read burst */
263
264 if (bbclk.CH) {
265 printf("ds1302: rtc_get: Clock was halted, clock probably "
266 "corrupt\n");
Yuri Tikhonovb73a19e2008-03-20 17:56:04 +0300267 rel = -1;
wdenkfe8c2802002-11-03 00:38:21 +0000268 }
269
270 tmp->tm_sec=10*bbclk.sec10+bbclk.sec;
271 tmp->tm_min=10*bbclk.min10+bbclk.min;
272 tmp->tm_hour=10*bbclk.hr10+bbclk.hr;
273 tmp->tm_wday=bbclk.day;
274 tmp->tm_mday=10*bbclk.date10+bbclk.date;
275 tmp->tm_mon=10*bbclk.month10+bbclk.month;
276 tmp->tm_year=10*bbclk.year10+bbclk.year + 1900;
277
278 tmp->tm_yday = 0;
279 tmp->tm_isdst= 0;
280
281 DPRINTF("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
282 tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
283 tmp->tm_hour, tmp->tm_min, tmp->tm_sec );
Yuri Tikhonovb73a19e2008-03-20 17:56:04 +0300284
285 return rel;
wdenkfe8c2802002-11-03 00:38:21 +0000286}
287
Jean-Christophe PLAGNIOL-VILLARDd1e23192008-09-01 23:06:23 +0200288int rtc_set(struct rtc_time *tmp)
wdenkfe8c2802002-11-03 00:38:21 +0000289{
290 struct ds1302_st bbclk;
291 unsigned char b=0;
292
293 if(!ds1302_initted) rtc_init();
294
295 DPRINTF("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
296 tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
297 tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
298
299 memset(&bbclk,0,sizeof(bbclk));
300 bbclk.CH=0; /* dont halt */
301 bbclk.WP=1; /* write protect when we're done */
302
303 bbclk.sec10=tmp->tm_sec/10;
304 bbclk.sec=tmp->tm_sec%10;
305
306 bbclk.min10=tmp->tm_min/10;
307 bbclk.min=tmp->tm_min%10;
308
309 bbclk.hr10=tmp->tm_hour/10;
310 bbclk.hr=tmp->tm_hour%10;
311
312 bbclk.day=tmp->tm_wday;
313
314 bbclk.date10=tmp->tm_mday/10;
315 bbclk.date=tmp->tm_mday%10;
316
317 bbclk.month10=tmp->tm_mon/10;
318 bbclk.month=tmp->tm_mon%10;
319
320 tmp->tm_year -= 1900;
321 bbclk.year10=tmp->tm_year/10;
322 bbclk.year=tmp->tm_year%10;
323
324 write_ser_drv(0x8e,&b,1); /* disable write protect */
325 write_ser_drv(0xbe,(unsigned char *)&bbclk, 8); /* write burst */
Jean-Christophe PLAGNIOL-VILLARDd1e23192008-09-01 23:06:23 +0200326
327 return 0;
wdenkfe8c2802002-11-03 00:38:21 +0000328}