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