xref: /netbsd-src/sys/dev/ic/rs5c313.c (revision b59f66e17c500a0d8ac8904c4c2fe7f387334983)
1 /*	$NetBSD: rs5c313.c,v 1.10 2014/11/20 16:34:26 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 2006 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: rs5c313.c,v 1.10 2014/11/20 16:34:26 christos Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/device.h>
35 #include <sys/kernel.h>
36 
37 #include <dev/clock_subr.h>
38 
39 #include <dev/ic/rs5c313reg.h>
40 #include <dev/ic/rs5c313var.h>
41 
42 
43 /* todr(9) methods */
44 static int rs5c313_todr_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
45 static int rs5c313_todr_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
46 
47 /* sugar for chip access */
48 #define rtc_begin(sc)		((*sc->sc_ops->rs5c313_op_begin)(sc))
49 #define rtc_ce(sc, onoff)	((*sc->sc_ops->rs5c313_op_ce)(sc, onoff))
50 #define rtc_clk(sc, onoff)	((*sc->sc_ops->rs5c313_op_clk)(sc, onoff))
51 #define rtc_dir(sc, output)	((*sc->sc_ops->rs5c313_op_dir)(sc, output))
52 #define rtc_di(sc)		((*sc->sc_ops->rs5c313_op_read)(sc))
53 #define rtc_do(sc, bit)		((*sc->sc_ops->rs5c313_op_write)(sc, bit))
54 
55 static int rs5c313_init(struct rs5c313_softc *);
56 static int rs5c313_read_reg(struct rs5c313_softc *, int);
57 static void rs5c313_write_reg(struct rs5c313_softc *, int, int);
58 
59 
60 void
rs5c313_attach(struct rs5c313_softc * sc)61 rs5c313_attach(struct rs5c313_softc *sc)
62 {
63 	device_t self = sc->sc_dev;
64 	const char *model;
65 
66 	switch (sc->sc_model) {
67 	case MODEL_5C313:
68 		model = "5C313";
69 		sc->sc_ctrl[0] = CTRL_24H;
70 		sc->sc_ctrl[1] = CTRL2_NTEST;
71 		break;
72 
73 	case MODEL_5C316:
74 		model = "5C316";
75 		sc->sc_ctrl[0] = 0;
76 		sc->sc_ctrl[1] = CTRL2_24H|CTRL2_NTEST;
77 		break;
78 
79 	default:
80 		aprint_error("unknown model (%d)\n", sc->sc_model);
81 		return;
82 	}
83 
84 	aprint_naive("\n");
85 	aprint_normal(": RICOH %s real time clock\n", model);
86 
87 	sc->sc_todr.cookie = sc;
88 	sc->sc_todr.todr_gettime_ymdhms = rs5c313_todr_gettime_ymdhms;
89 	sc->sc_todr.todr_settime_ymdhms = rs5c313_todr_settime_ymdhms;
90 
91 	if (rs5c313_init(sc) != 0) {
92 		aprint_error_dev(self, "init failed\n");
93 		return;
94 	}
95 
96 	todr_attach(&sc->sc_todr);
97 }
98 
99 
100 static int
rs5c313_init(struct rs5c313_softc * sc)101 rs5c313_init(struct rs5c313_softc *sc)
102 {
103 	device_t self = sc->sc_dev;
104 	int status = 0;
105 	int retry;
106 
107 	rtc_ce(sc, 0);
108 
109 	rtc_begin(sc);
110 	rtc_ce(sc, 1);
111 
112 	if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_XSTP) == 0) {
113 		sc->sc_valid = 1;
114 		goto done;
115 	}
116 
117 	sc->sc_valid = 0;
118 	aprint_error_dev(self, "time not valid\n");
119 
120 	rs5c313_write_reg(sc, RS5C313_TINT, 0);
121 	rs5c313_write_reg(sc, RS5C313_CTRL, (sc->sc_ctrl[0] | CTRL_ADJ));
122 
123 	for (retry = 1000; retry > 0; --retry) {
124 		if (rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY)
125 			delay(1);
126 		else
127 			break;
128 	}
129 	if (retry == 0) {
130 		status = EIO;
131 		goto done;
132 	}
133 
134 	rs5c313_write_reg(sc, RS5C313_CTRL, sc->sc_ctrl[0]);
135 	rs5c313_write_reg(sc, RS5C313_CTRL2, sc->sc_ctrl[1]);
136 
137   done:
138 	rtc_ce(sc, 0);
139 	return status;
140 }
141 
142 
143 static int
rs5c313_todr_gettime_ymdhms(todr_chip_handle_t todr,struct clock_ymdhms * dt)144 rs5c313_todr_gettime_ymdhms(todr_chip_handle_t todr, struct clock_ymdhms *dt)
145 {
146 	struct rs5c313_softc *sc = todr->cookie;
147 	int retry;
148 	int s;
149 
150 	/*
151 	 * If chip had invalid data on init, don't bother reading
152 	 * bogus values, let todr(9) cope.
153 	 */
154 	if (sc->sc_valid == 0)
155 		return EIO;
156 
157 	s = splhigh();
158 
159 	rtc_begin(sc);
160 	for (retry = 10; retry > 0; --retry) {
161 		rtc_ce(sc, 1);
162 
163 		rs5c313_write_reg(sc, RS5C313_CTRL, sc->sc_ctrl[0]);
164 		if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY) == 0)
165 			break;
166 
167 		rtc_ce(sc, 0);
168 		delay(1);
169 	}
170 	if (retry == 0) {
171 		splx(s);
172 		return EIO;
173 	}
174 
175 #define RTCGET(x, y)							\
176 	do {								\
177 		int ones = rs5c313_read_reg(sc, RS5C313_ ## y ## 1);	\
178 		int tens = rs5c313_read_reg(sc, RS5C313_ ## y ## 10);	\
179 		dt->dt_ ## x = tens * 10 + ones;			\
180 	} while (/* CONSTCOND */0)
181 
182 	RTCGET(sec, SEC);
183 	RTCGET(min, MIN);
184 	RTCGET(hour, HOUR);
185 	RTCGET(day, DAY);
186 	RTCGET(mon, MON);
187 	RTCGET(year, YEAR);
188 #undef	RTCGET
189 	dt->dt_wday = rs5c313_read_reg(sc, RS5C313_WDAY);
190 
191 	rtc_ce(sc, 0);
192 	splx(s);
193 
194 	dt->dt_year = (dt->dt_year % 100) + 1900;
195 	if (dt->dt_year < POSIX_BASE_YEAR) {
196 		dt->dt_year += 100;
197 	}
198 
199 	return 0;
200 }
201 
202 
203 static int
rs5c313_todr_settime_ymdhms(todr_chip_handle_t todr,struct clock_ymdhms * dt)204 rs5c313_todr_settime_ymdhms(todr_chip_handle_t todr, struct clock_ymdhms *dt)
205 {
206 	struct rs5c313_softc *sc = todr->cookie;
207 	int retry;
208 	int t;
209 	int s;
210 
211 	s = splhigh();
212 
213 	rtc_begin(sc);
214 	for (retry = 10; retry > 0; --retry) {
215 		rtc_ce(sc, 1);
216 
217 		rs5c313_write_reg(sc, RS5C313_CTRL, sc->sc_ctrl[0]);
218 		if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY) == 0)
219 			break;
220 
221 		rtc_ce(sc, 0);
222 		delay(1);
223 	}
224 
225 	if (retry == 0) {
226 		splx(s);
227 		return EIO;
228 	}
229 
230 #define	RTCSET(x, y)							     \
231 	do {								     \
232 		t = bintobcd(dt->dt_ ## y) & 0xff;				     \
233 		rs5c313_write_reg(sc, RS5C313_ ## x ## 1, t & 0x0f);	     \
234 		rs5c313_write_reg(sc, RS5C313_ ## x ## 10, (t >> 4) & 0x0f); \
235 	} while (/* CONSTCOND */0)
236 
237 	RTCSET(SEC, sec);
238 	RTCSET(MIN, min);
239 	RTCSET(HOUR, hour);
240 	RTCSET(DAY, day);
241 	RTCSET(MON, mon);
242 
243 #undef	RTCSET
244 
245 	t = dt->dt_year % 100;
246 	t = bintobcd(t);
247 	rs5c313_write_reg(sc, RS5C313_YEAR1, t & 0x0f);
248 	rs5c313_write_reg(sc, RS5C313_YEAR10, (t >> 4) & 0x0f);
249 
250 	rs5c313_write_reg(sc, RS5C313_WDAY, dt->dt_wday);
251 
252 	rtc_ce(sc, 0);
253 	splx(s);
254 
255 	sc->sc_valid = 1;
256 	return 0;
257 }
258 
259 
260 static int
rs5c313_read_reg(struct rs5c313_softc * sc,int addr)261 rs5c313_read_reg(struct rs5c313_softc *sc, int addr)
262 {
263 	int data;
264 
265 	/* output */
266 	rtc_dir(sc, 1);
267 
268 	/* control */
269 	rtc_do(sc, 1);		/* ignored */
270 	rtc_do(sc, 1);		/* R/#W = 1(READ) */
271 	rtc_do(sc, 1);		/* AD = 1 */
272 	rtc_do(sc, 0);		/* DT = 0 */
273 
274 	/* address */
275 	rtc_do(sc, addr & 0x8);	/* A3 */
276 	rtc_do(sc, addr & 0x4);	/* A2 */
277 	rtc_do(sc, addr & 0x2);	/* A1 */
278 	rtc_do(sc, addr & 0x1);	/* A0 */
279 
280 	/* input */
281 	rtc_dir(sc, 0);
282 
283 	/* ignore */
284 	(void)rtc_di(sc);
285 	(void)rtc_di(sc);
286 	(void)rtc_di(sc);
287 	(void)rtc_di(sc);
288 
289 	/* data */
290 	data = rtc_di(sc);	/* D3 */
291 	data <<= 1;
292 	data |= rtc_di(sc);	/* D2 */
293 	data <<= 1;
294 	data |= rtc_di(sc);	/* D1 */
295 	data <<= 1;
296 	data |= rtc_di(sc);	/* D0 */
297 
298 	return data;
299 }
300 
301 
302 static void
rs5c313_write_reg(struct rs5c313_softc * sc,int addr,int data)303 rs5c313_write_reg(struct rs5c313_softc *sc, int addr, int data)
304 {
305 
306 	/* output */
307 	rtc_dir(sc, 1);
308 
309 	/* control */
310 	rtc_do(sc, 1);		/* ignored */
311 	rtc_do(sc, 0);		/* R/#W = 0 (WRITE) */
312 	rtc_do(sc, 1);		/* AD = 1 */
313 	rtc_do(sc, 0);		/* DT = 0 */
314 
315 	/* address */
316 	rtc_do(sc, addr & 0x8);	/* A3 */
317 	rtc_do(sc, addr & 0x4);	/* A2 */
318 	rtc_do(sc, addr & 0x2);	/* A1 */
319 	rtc_do(sc, addr & 0x1);	/* A0 */
320 
321 	/* control */
322 	rtc_do(sc, 1);		/* ignored */
323 	rtc_do(sc, 0);		/* R/#W = 0(WRITE) */
324 	rtc_do(sc, 0);		/* AD = 0 */
325 	rtc_do(sc, 1);		/* DT = 1 */
326 
327 	/* data */
328 	rtc_do(sc, data & 0x8);	/* D3 */
329 	rtc_do(sc, data & 0x4);	/* D2 */
330 	rtc_do(sc, data & 0x2);	/* D1 */
331 	rtc_do(sc, data & 0x1);	/* D0 */
332 }
333