b94163f7486baa1a4c0b01364f915fd6290c5283
[oweals/u-boot.git] / drivers / rtc / ds1302.c
1 /*
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
12 /* 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)
32 static 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 */
43 struct 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;
67         unsigned char day:3;            /* day of week */
68
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
76 static int ds1302_initted=0;
77
78 /* Pin control */
79 static inline void
80 rtc_go_high(unsigned int mask)
81 {
82         unsigned int f = GTREGREAD(GPP_VALUE) | mask;
83
84         GT_REG_WRITE(GPP_VALUE, f);
85 }
86
87 static inline void
88 rtc_go_low(unsigned int mask)
89 {
90         unsigned int f = GTREGREAD(GPP_VALUE) & ~mask;
91
92         GT_REG_WRITE(GPP_VALUE, f);
93 }
94
95 static inline void
96 rtc_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
103 static inline void
104 rtc_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
113 static void
114 write_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
129 static unsigned char
130 read_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
147 static void
148 read_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
171 static void
172 write_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
189 void
190 rtc_init(void)
191 {
192         struct ds1302_st bbclk;
193         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");
227                 bbclk.year10=100/10;    /* 2000 - why not? ;) */
228                 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
247 void
248 rtc_reset(void)
249 {
250         if(!ds1302_initted) rtc_init();
251         /* TODO */
252 }
253
254 int
255 rtc_get(struct rtc_time *tmp)
256 {
257         int rel = 0;
258         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");
267                 rel = -1;
268         }
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 );
284
285         return rel;
286 }
287
288 int rtc_set(struct rtc_time *tmp)
289 {
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 */
326
327         return 0;
328 }