xref: /openbsd/sys/arch/landisk/dev/rs5c313.c (revision 09467b48)
1 /*	$OpenBSD: rs5c313.c,v 1.3 2008/06/27 06:03:08 ray Exp $	*/
2 /*	$NetBSD: rs5c313.c,v 1.1 2006/09/07 01:12:00 uwe Exp $	*/
3 /*	$NetBSD: rs5c313_landisk.c,v 1.1 2006/09/07 01:55:03 uwe Exp $	*/
4 
5 /*-
6  * Copyright (c) 2002 The NetBSD Foundation, Inc.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 /*
32  * RICOH RS5C313 Real Time Clock
33  */
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/device.h>
38 #include <sys/kernel.h>
39 
40 #include <dev/clock_subr.h>
41 #include <sh/clock.h>
42 
43 #include <sh/devreg.h>
44 #include <sh/dev/scireg.h>
45 
46 #include <landisk/dev/rs5c313reg.h>
47 #include <landisk/landisk/landiskreg.h>
48 
49 struct rs5c313_softc {
50 	struct device sc_dev;
51 
52 	int sc_valid;		/* oscillation halt sensing on init */
53 };
54 
55 /* chip access methods */
56 void rtc_begin(struct rs5c313_softc *);
57 void rtc_ce(struct rs5c313_softc *, int);
58 void rtc_dir(struct rs5c313_softc *, int);
59 void rtc_clk(struct rs5c313_softc *, int);
60 int  rtc_read(struct rs5c313_softc *);
61 void rtc_write(struct rs5c313_softc *, int);
62 
63 int rs5c313_init(struct rs5c313_softc *);
64 int rs5c313_read_reg(struct rs5c313_softc *, int);
65 void rs5c313_write_reg(struct rs5c313_softc *, int, int);
66 void rs5c313_gettime(void *, time_t, struct clock_ymdhms *);
67 void rs5c313_settime(void *, struct clock_ymdhms *);
68 
69 int
70 rs5c313_init(struct rs5c313_softc *sc)
71 {
72 	int status = 0;
73 	int retry;
74 
75 	rtc_ce(sc, 0);
76 
77 	rtc_begin(sc);
78 	rtc_ce(sc, 1);
79 
80 	if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_XSTP) == 0) {
81 		sc->sc_valid = 1;
82 		goto done;
83 	}
84 
85 	sc->sc_valid = 0;
86 	printf("%s: time not valid\n", sc->sc_dev.dv_xname);
87 
88 	rs5c313_write_reg(sc, RS5C313_TINT, 0);
89 	rs5c313_write_reg(sc, RS5C313_CTRL, (CTRL_BASE | CTRL_ADJ));
90 
91 	for (retry = 1000; retry > 0; --retry) {
92 		if (rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY)
93 			delay(1);
94 		else
95 			break;
96 	}
97 
98 	if (retry == 0) {
99 		status = EIO;
100 		goto done;
101 	}
102 
103 	rs5c313_write_reg(sc, RS5C313_CTRL, CTRL_BASE);
104 
105 done:
106 	rtc_ce(sc, 0);
107 	return status;
108 }
109 
110 int
111 rs5c313_read_reg(struct rs5c313_softc *sc, int addr)
112 {
113 	int data;
114 
115 	/* output */
116 	rtc_dir(sc, 1);
117 
118 	/* control */
119 	rtc_write(sc, 1);		/* ignored */
120 	rtc_write(sc, 1);		/* R/#W = 1(READ) */
121 	rtc_write(sc, 1);		/* AD = 1 */
122 	rtc_write(sc, 0);		/* DT = 0 */
123 
124 	/* address */
125 	rtc_write(sc, addr & 0x8);	/* A3 */
126 	rtc_write(sc, addr & 0x4);	/* A2 */
127 	rtc_write(sc, addr & 0x2);	/* A1 */
128 	rtc_write(sc, addr & 0x1);	/* A0 */
129 
130 	/* input */
131 	rtc_dir(sc, 0);
132 
133 	/* ignore */
134 	(void)rtc_read(sc);
135 	(void)rtc_read(sc);
136 	(void)rtc_read(sc);
137 	(void)rtc_read(sc);
138 
139 	/* data */
140 	data = rtc_read(sc);	/* D3 */
141 	data <<= 1;
142 	data |= rtc_read(sc);	/* D2 */
143 	data <<= 1;
144 	data |= rtc_read(sc);	/* D1 */
145 	data <<= 1;
146 	data |= rtc_read(sc);	/* D0 */
147 
148 	return data;
149 }
150 
151 void
152 rs5c313_write_reg(struct rs5c313_softc *sc, int addr, int data)
153 {
154 	/* output */
155 	rtc_dir(sc, 1);
156 
157 	/* control */
158 	rtc_write(sc, 1);		/* ignored */
159 	rtc_write(sc, 0);		/* R/#W = 0 (WRITE) */
160 	rtc_write(sc, 1);		/* AD = 1 */
161 	rtc_write(sc, 0);		/* DT = 0 */
162 
163 	/* address */
164 	rtc_write(sc, addr & 0x8);	/* A3 */
165 	rtc_write(sc, addr & 0x4);	/* A2 */
166 	rtc_write(sc, addr & 0x2);	/* A1 */
167 	rtc_write(sc, addr & 0x1);	/* A0 */
168 
169 	/* control */
170 	rtc_write(sc, 1);		/* ignored */
171 	rtc_write(sc, 0);		/* R/#W = 0(WRITE) */
172 	rtc_write(sc, 0);		/* AD = 0 */
173 	rtc_write(sc, 1);		/* DT = 1 */
174 
175 	/* data */
176 	rtc_write(sc, data & 0x8);	/* D3 */
177 	rtc_write(sc, data & 0x4);	/* D2 */
178 	rtc_write(sc, data & 0x2);	/* D1 */
179 	rtc_write(sc, data & 0x1);	/* D0 */
180 }
181 
182 void
183 rs5c313_gettime(void *cookie, time_t base, struct clock_ymdhms *dt)
184 {
185 	struct rs5c313_softc *sc = cookie;
186 	int retry;
187 	int s;
188 
189 	/*
190 	 * If chip had invalid data on init, don't bother reading
191 	 * bogus values.
192 	 */
193 	if (sc->sc_valid == 0)
194 		return;
195 
196 	s = splhigh();
197 
198 	rtc_begin(sc);
199 	for (retry = 10; retry > 0; --retry) {
200 		rtc_ce(sc, 1);
201 
202 		rs5c313_write_reg(sc, RS5C313_CTRL, CTRL_BASE);
203 		if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY) == 0)
204 			break;
205 
206 		rtc_ce(sc, 0);
207 		delay(1);
208 	}
209 
210 	if (retry == 0) {
211 		splx(s);
212 		return;
213 	}
214 
215 #define RTCGET(x, y)							\
216 	do {								\
217 		int ones = rs5c313_read_reg(sc, RS5C313_ ## y ## 1);	\
218 		int tens = rs5c313_read_reg(sc, RS5C313_ ## y ## 10);	\
219 		dt->dt_ ## x = tens * 10 + ones;			\
220 	} while (/* CONSTCOND */0)
221 
222 	RTCGET(sec, SEC);
223 	RTCGET(min, MIN);
224 	RTCGET(hour, HOUR);
225 	RTCGET(day, DAY);
226 	RTCGET(mon, MON);
227 	RTCGET(year, YEAR);
228 #undef	RTCGET
229 	dt->dt_wday = rs5c313_read_reg(sc, RS5C313_WDAY);
230 
231 	rtc_ce(sc, 0);
232 	splx(s);
233 
234 	dt->dt_year = (dt->dt_year % 100) + 1900;
235 	if (dt->dt_year < 1970) {
236 		dt->dt_year += 100;
237 	}
238 }
239 
240 void
241 rs5c313_settime(void *cookie, struct clock_ymdhms *dt)
242 {
243 	struct rs5c313_softc *sc = cookie;
244 	int retry;
245 	int t;
246 	int s;
247 
248 	s = splhigh();
249 
250 	rtc_begin(sc);
251 	for (retry = 10; retry > 0; --retry) {
252 		rtc_ce(sc, 1);
253 
254 		rs5c313_write_reg(sc, RS5C313_CTRL, CTRL_BASE);
255 		if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY) == 0)
256 			break;
257 
258 		rtc_ce(sc, 0);
259 		delay(1);
260 	}
261 
262 	if (retry == 0) {
263 		splx(s);
264 		return;
265 	}
266 
267 #define	RTCSET(x, y)							     \
268 	do {								     \
269 		t = TOBCD(dt->dt_ ## y) & 0xff;				     \
270 		rs5c313_write_reg(sc, RS5C313_ ## x ## 1, t & 0x0f);	     \
271 		rs5c313_write_reg(sc, RS5C313_ ## x ## 10, (t >> 4) & 0x0f); \
272 	} while (/* CONSTCOND */0)
273 
274 	RTCSET(SEC, sec);
275 	RTCSET(MIN, min);
276 	RTCSET(HOUR, hour);
277 	RTCSET(DAY, day);
278 	RTCSET(MON, mon);
279 
280 #undef	RTCSET
281 
282 	t = dt->dt_year % 100;
283 	t = TOBCD(t);
284 	rs5c313_write_reg(sc, RS5C313_YEAR1, t & 0x0f);
285 	rs5c313_write_reg(sc, RS5C313_YEAR10, (t >> 4) & 0x0f);
286 
287 	rs5c313_write_reg(sc, RS5C313_WDAY, dt->dt_wday);
288 
289 	rtc_ce(sc, 0);
290 	splx(s);
291 
292 	sc->sc_valid = 1;
293 }
294 
295 struct rtc_ops rs5c313_ops = {
296 	NULL,
297 	NULL,			/* not used */
298 	rs5c313_gettime,
299 	rs5c313_settime
300 };
301 
302 void
303 rtc_begin(struct rs5c313_softc *sc)
304 {
305 	SHREG_SCSPTR = SCSPTR_SPB1IO | SCSPTR_SPB1DT
306 		     | SCSPTR_SPB0IO | SCSPTR_SPB0DT;
307 	delay(100);
308 }
309 
310 /*
311  * CE pin
312  */
313 void
314 rtc_ce(struct rs5c313_softc *sc, int onoff)
315 {
316 	if (onoff)
317 		_reg_write_1(LANDISK_PWRMNG, PWRMNG_RTC_CE);
318 	else
319 		_reg_write_1(LANDISK_PWRMNG, 0);
320 	delay(600);
321 }
322 
323 /*
324  * SCLK pin is connnected to SPB0DT.
325  * SPB0DT is always in output mode, we set SPB0IO in rtc_begin.
326  */
327 void
328 rtc_clk(struct rs5c313_softc *sc, int onoff)
329 {
330 	uint8_t r = SHREG_SCSPTR;
331 
332 	if (onoff)
333 		r |= SCSPTR_SPB0DT;
334 	else
335 		r &= ~SCSPTR_SPB0DT;
336 	SHREG_SCSPTR = r;
337 }
338 
339 /*
340  * SIO pin is connected to SPB1DT.
341  * SPB1DT is output when SPB1IO is set.
342  */
343 void
344 rtc_dir(struct rs5c313_softc *sc, int output)
345 {
346 	uint8_t r = SHREG_SCSPTR;
347 
348 	if (output)
349 		r |= SCSPTR_SPB1IO;
350 	else
351 		r &= ~SCSPTR_SPB1IO;
352 	SHREG_SCSPTR = r;
353 }
354 
355 /*
356  * Read bit from SPB1DT pin.
357  */
358 int
359 rtc_read(struct rs5c313_softc *sc)
360 {
361 	int bit;
362 
363 	delay(300);
364 
365 	bit = (SHREG_SCSPTR & SCSPTR_SPB1DT) ? 1 : 0;
366 
367 	rtc_clk(sc, 0);
368 	delay(300);
369 	rtc_clk(sc, 1);
370 
371 	return bit;
372 }
373 
374 /*
375  * Write bit via SPB1DT pin.
376  */
377 void
378 rtc_write(struct rs5c313_softc *sc, int bit)
379 {
380 	uint8_t r = SHREG_SCSPTR;
381 
382 	if (bit)
383 		r |= SCSPTR_SPB1DT;
384 	else
385 		r &= ~SCSPTR_SPB1DT;
386 	SHREG_SCSPTR = r;
387 
388 	delay(300);
389 
390 	rtc_clk(sc, 0);
391 	delay(300);
392 	rtc_clk(sc, 1);
393 }
394 
395 /* autoconf glue */
396 int rs5c313_landisk_match(struct device *, void *, void *);
397 void rs5c313_landisk_attach(struct device *, struct device *, void *);
398 
399 const struct cfattach rsclock_ca = {
400 	sizeof (struct rs5c313_softc),
401 	rs5c313_landisk_match, rs5c313_landisk_attach
402 };
403 
404 struct cfdriver rsclock_cd = {
405 	0, "rsclock", DV_DULL
406 };
407 
408 int
409 rs5c313_landisk_match(struct device *parent, void *vcf, void *aux)
410 {
411 	static int matched = 0;
412 
413 	if (matched)
414 		return (0);
415 
416 	return (matched = 1);
417 }
418 
419 void
420 rs5c313_landisk_attach(struct device *parent, struct device *self, void *aux)
421 {
422 	struct rs5c313_softc *sc = (void *)self;
423 
424 	printf(": RS5C313 real time clock\n");
425 
426 	if (rs5c313_init(sc) != 0) {
427 		printf("%s: init failed\n", self->dv_xname);
428 		return;
429 	}
430 
431 	rs5c313_ops._cookie = sc;
432 	sh_clock_init(SH_CLOCK_NORTC, &rs5c313_ops);
433 }
434