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 #include <linux/delay.h>
12 
13 /* 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)
DUMP(const char * ptr,int num)33 static 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 */
44 struct 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;
68 	unsigned char day:3;		/* day of week */
69 
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 
77 static int ds1302_initted=0;
78 
79 /* Pin control */
80 static inline void
rtc_go_high(unsigned int mask)81 rtc_go_high(unsigned int mask)
82 {
83 	unsigned int f = GTREGREAD(GPP_VALUE) | mask;
84 
85 	GT_REG_WRITE(GPP_VALUE, f);
86 }
87 
88 static inline void
rtc_go_low(unsigned int mask)89 rtc_go_low(unsigned int mask)
90 {
91 	unsigned int f = GTREGREAD(GPP_VALUE) & ~mask;
92 
93 	GT_REG_WRITE(GPP_VALUE, f);
94 }
95 
96 static inline void
rtc_go_input(unsigned int mask)97 rtc_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 
104 static inline void
rtc_go_output(unsigned int mask)105 rtc_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 
114 static void
write_byte(unsigned char b)115 write_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 
130 static unsigned char
read_byte(void)131 read_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 
148 static void
read_ser_drv(unsigned char addr,unsigned char * buf,int count)149 read_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 
172 static void
write_ser_drv(unsigned char addr,unsigned char * buf,int count)173 write_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 
190 void
rtc_init(void)191 rtc_init(void)
192 {
193 	struct ds1302_st bbclk;
194 	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");
228 		bbclk.year10=100/10;	/* 2000 - why not? ;) */
229 		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 
248 void
rtc_reset(void)249 rtc_reset(void)
250 {
251 	if(!ds1302_initted) rtc_init();
252 	/* TODO */
253 }
254 
255 int
rtc_get(struct rtc_time * tmp)256 rtc_get(struct rtc_time *tmp)
257 {
258 	int rel = 0;
259 	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");
268 		rel = -1;
269 	}
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 );
285 
286 	return rel;
287 }
288 
rtc_set(struct rtc_time * tmp)289 int rtc_set(struct rtc_time *tmp)
290 {
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 */
327 
328 	return 0;
329 }
330