1/*	$OpenBSD: rs5c313.c,v 1.4 2021/03/11 11:16:57 jsg 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
49struct rs5c313_softc {
50	struct device sc_dev;
51
52	int sc_valid;		/* oscillation halt sensing on init */
53};
54
55/* chip access methods */
56void rtc_begin(struct rs5c313_softc *);
57void rtc_ce(struct rs5c313_softc *, int);
58void rtc_dir(struct rs5c313_softc *, int);
59void rtc_clk(struct rs5c313_softc *, int);
60int  rtc_read(struct rs5c313_softc *);
61void rtc_write(struct rs5c313_softc *, int);
62
63int rs5c313_init(struct rs5c313_softc *);
64int rs5c313_read_reg(struct rs5c313_softc *, int);
65void rs5c313_write_reg(struct rs5c313_softc *, int, int);
66void rs5c313_gettime(void *, time_t, struct clock_ymdhms *);
67void rs5c313_settime(void *, struct clock_ymdhms *);
68
69int
70rs5c313_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
105done:
106	rtc_ce(sc, 0);
107	return status;
108}
109
110int
111rs5c313_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
151void
152rs5c313_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
182void
183rs5c313_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
240void
241rs5c313_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
295struct rtc_ops rs5c313_ops = {
296	NULL,
297	NULL,			/* not used */
298	rs5c313_gettime,
299	rs5c313_settime
300};
301
302void
303rtc_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 */
313void
314rtc_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 connected to SPB0DT.
325 * SPB0DT is always in output mode, we set SPB0IO in rtc_begin.
326 */
327void
328rtc_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 */
343void
344rtc_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 */
358int
359rtc_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 */
377void
378rtc_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 */
396int rs5c313_landisk_match(struct device *, void *, void *);
397void rs5c313_landisk_attach(struct device *, struct device *, void *);
398
399const struct cfattach rsclock_ca = {
400	sizeof (struct rs5c313_softc),
401	rs5c313_landisk_match, rs5c313_landisk_attach
402};
403
404struct cfdriver rsclock_cd = {
405	0, "rsclock", DV_DULL
406};
407
408int
409rs5c313_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
419void
420rs5c313_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