1 /* $OpenBSD: rs5c313.c,v 1.5 2024/11/05 18:58:59 miod 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
rs5c313_init(struct rs5c313_softc * sc)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
rs5c313_read_reg(struct rs5c313_softc * sc,int addr)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
rs5c313_write_reg(struct rs5c313_softc * sc,int addr,int data)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
rs5c313_gettime(void * cookie,time_t base,struct clock_ymdhms * dt)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
rs5c313_settime(void * cookie,struct clock_ymdhms * dt)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
rtc_begin(struct rs5c313_softc * sc)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
rtc_ce(struct rs5c313_softc * sc,int onoff)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 connected to SPB0DT.
325 * SPB0DT is always in output mode, we set SPB0IO in rtc_begin.
326 */
327 void
rtc_clk(struct rs5c313_softc * sc,int onoff)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
rtc_dir(struct rs5c313_softc * sc,int output)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
rtc_read(struct rs5c313_softc * sc)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
rtc_write(struct rs5c313_softc * sc,int bit)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 NULL, "rsclock", DV_DULL
406 };
407
408 int
rs5c313_landisk_match(struct device * parent,void * vcf,void * aux)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
rs5c313_landisk_attach(struct device * parent,struct device * self,void * aux)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