xref: /netbsd-src/sys/dev/ic/rs5c313.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /*	$NetBSD: rs5c313.c,v 1.8 2008/05/04 19:43:06 martin 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.8 2008/05/04 19:43:06 martin 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
61 rs5c313_attach(struct rs5c313_softc *sc)
62 {
63 	device_t self = sc->sc_dev;
64 
65 	aprint_naive("\n");
66 	aprint_normal(": real time clock\n");
67 
68 	sc->sc_todr.cookie = sc;
69 	sc->sc_todr.todr_gettime_ymdhms = rs5c313_todr_gettime_ymdhms;
70 	sc->sc_todr.todr_settime_ymdhms = rs5c313_todr_settime_ymdhms;
71 
72 	if (rs5c313_init(sc) != 0) {
73 		aprint_error_dev(self, "init failed\n");
74 		return;
75 	}
76 
77 	todr_attach(&sc->sc_todr);
78 }
79 
80 
81 static int
82 rs5c313_init(struct rs5c313_softc *sc)
83 {
84 	device_t self = sc->sc_dev;
85 	int status = 0;
86 	int retry;
87 
88 	rtc_ce(sc, 0);
89 
90 	rtc_begin(sc);
91 	rtc_ce(sc, 1);
92 
93 	if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_XSTP) == 0) {
94 		sc->sc_valid = 1;
95 		goto done;
96 	}
97 
98 	sc->sc_valid = 0;
99 	aprint_error_dev(self, "time not valid\n");
100 
101 	rs5c313_write_reg(sc, RS5C313_TINT, 0);
102 	rs5c313_write_reg(sc, RS5C313_CTRL, (CTRL_BASE | CTRL_ADJ));
103 
104 	for (retry = 1000; retry > 0; --retry) {
105 		if (rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY)
106 			delay(1);
107 		else
108 			break;
109 	}
110 
111 	if (retry == 0) {
112 		status = EIO;
113 		goto done;
114 	}
115 
116 	rs5c313_write_reg(sc, RS5C313_CTRL, CTRL_BASE);
117 
118   done:
119 	rtc_ce(sc, 0);
120 	return status;
121 }
122 
123 
124 static int
125 rs5c313_todr_gettime_ymdhms(todr_chip_handle_t todr, struct clock_ymdhms *dt)
126 {
127 	struct rs5c313_softc *sc = todr->cookie;
128 	int retry;
129 	int s;
130 
131 	/*
132 	 * If chip had invalid data on init, don't bother reading
133 	 * bogus values, let todr(9) cope.
134 	 */
135 	if (sc->sc_valid == 0)
136 		return EIO;
137 
138 	s = splhigh();
139 
140 	rtc_begin(sc);
141 	for (retry = 10; retry > 0; --retry) {
142 		rtc_ce(sc, 1);
143 
144 		rs5c313_write_reg(sc, RS5C313_CTRL, CTRL_BASE);
145 		if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY) == 0)
146 			break;
147 
148 		rtc_ce(sc, 0);
149 		delay(1);
150 	}
151 
152 	if (retry == 0) {
153 		splx(s);
154 		return EIO;
155 	}
156 
157 #define RTCGET(x, y)							\
158 	do {								\
159 		int ones = rs5c313_read_reg(sc, RS5C313_ ## y ## 1);	\
160 		int tens = rs5c313_read_reg(sc, RS5C313_ ## y ## 10);	\
161 		dt->dt_ ## x = tens * 10 + ones;			\
162 	} while (/* CONSTCOND */0)
163 
164 	RTCGET(sec, SEC);
165 	RTCGET(min, MIN);
166 	RTCGET(hour, HOUR);
167 	RTCGET(day, DAY);
168 	RTCGET(mon, MON);
169 	RTCGET(year, YEAR);
170 #undef	RTCGET
171 	dt->dt_wday = rs5c313_read_reg(sc, RS5C313_WDAY);
172 
173 	rtc_ce(sc, 0);
174 	splx(s);
175 
176 
177 	dt->dt_year = (dt->dt_year % 100) + 1900;
178 	if (dt->dt_year < POSIX_BASE_YEAR) {
179 		dt->dt_year += 100;
180 	}
181 
182 	return 0;
183 }
184 
185 
186 static int
187 rs5c313_todr_settime_ymdhms(todr_chip_handle_t todr, struct clock_ymdhms *dt)
188 {
189 	struct rs5c313_softc *sc = todr->cookie;
190 	int retry;
191 	int t;
192 	int s;
193 
194 	s = splhigh();
195 
196 	rtc_begin(sc);
197 	for (retry = 10; retry > 0; --retry) {
198 		rtc_ce(sc, 1);
199 
200 		rs5c313_write_reg(sc, RS5C313_CTRL, CTRL_BASE);
201 		if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY) == 0)
202 			break;
203 
204 		rtc_ce(sc, 0);
205 		delay(1);
206 	}
207 
208 	if (retry == 0) {
209 		splx(s);
210 		return EIO;
211 	}
212 
213 #define	RTCSET(x, y)							     \
214 	do {								     \
215 		t = TOBCD(dt->dt_ ## y) & 0xff;				     \
216 		rs5c313_write_reg(sc, RS5C313_ ## x ## 1, t & 0x0f);	     \
217 		rs5c313_write_reg(sc, RS5C313_ ## x ## 10, (t >> 4) & 0x0f); \
218 	} while (/* CONSTCOND */0)
219 
220 	RTCSET(SEC, sec);
221 	RTCSET(MIN, min);
222 	RTCSET(HOUR, hour);
223 	RTCSET(DAY, day);
224 	RTCSET(MON, mon);
225 
226 #undef	RTCSET
227 
228 	t = dt->dt_year % 100;
229 	t = TOBCD(t);
230 	rs5c313_write_reg(sc, RS5C313_YEAR1, t & 0x0f);
231 	rs5c313_write_reg(sc, RS5C313_YEAR10, (t >> 4) & 0x0f);
232 
233 	rs5c313_write_reg(sc, RS5C313_WDAY, dt->dt_wday);
234 
235 	rtc_ce(sc, 0);
236 	splx(s);
237 
238 	sc->sc_valid = 1;
239 	return 0;
240 }
241 
242 
243 static int
244 rs5c313_read_reg(struct rs5c313_softc *sc, int addr)
245 {
246 	int data;
247 
248 	/* output */
249 	rtc_dir(sc, 1);
250 
251 	/* control */
252 	rtc_do(sc, 1);		/* ignored */
253 	rtc_do(sc, 1);		/* R/#W = 1(READ) */
254 	rtc_do(sc, 1);		/* AD = 1 */
255 	rtc_do(sc, 0);		/* DT = 0 */
256 
257 	/* address */
258 	rtc_do(sc, addr & 0x8);	/* A3 */
259 	rtc_do(sc, addr & 0x4);	/* A2 */
260 	rtc_do(sc, addr & 0x2);	/* A1 */
261 	rtc_do(sc, addr & 0x1);	/* A0 */
262 
263 	/* input */
264 	rtc_dir(sc, 0);
265 
266 	/* ignore */
267 	(void)rtc_di(sc);
268 	(void)rtc_di(sc);
269 	(void)rtc_di(sc);
270 	(void)rtc_di(sc);
271 
272 	/* data */
273 	data = rtc_di(sc);	/* D3 */
274 	data <<= 1;
275 	data |= rtc_di(sc);	/* D2 */
276 	data <<= 1;
277 	data |= rtc_di(sc);	/* D1 */
278 	data <<= 1;
279 	data |= rtc_di(sc);	/* D0 */
280 
281 	return data;
282 }
283 
284 
285 static void
286 rs5c313_write_reg(struct rs5c313_softc *sc, int addr, int data)
287 {
288 
289 	/* output */
290 	rtc_dir(sc, 1);
291 
292 	/* control */
293 	rtc_do(sc, 1);		/* ignored */
294 	rtc_do(sc, 0);		/* R/#W = 0 (WRITE) */
295 	rtc_do(sc, 1);		/* AD = 1 */
296 	rtc_do(sc, 0);		/* DT = 0 */
297 
298 	/* address */
299 	rtc_do(sc, addr & 0x8);	/* A3 */
300 	rtc_do(sc, addr & 0x4);	/* A2 */
301 	rtc_do(sc, addr & 0x2);	/* A1 */
302 	rtc_do(sc, addr & 0x1);	/* A0 */
303 
304 	/* control */
305 	rtc_do(sc, 1);		/* ignored */
306 	rtc_do(sc, 0);		/* R/#W = 0(WRITE) */
307 	rtc_do(sc, 0);		/* AD = 0 */
308 	rtc_do(sc, 1);		/* DT = 1 */
309 
310 	/* data */
311 	rtc_do(sc, data & 0x8);	/* D3 */
312 	rtc_do(sc, data & 0x4);	/* D2 */
313 	rtc_do(sc, data & 0x2);	/* D1 */
314 	rtc_do(sc, data & 0x1);	/* D0 */
315 }
316