xref: /openbmc/u-boot/drivers/rtc/ds1302.c (revision 09d84117)
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 }
329