FreeBSD/Linux Kernel Cross Reference
sys/dev/ic/rs5c313.c
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
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
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
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
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
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
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 }
Cache object: c8a6cc783eea92b3ed7740acc286ea71
|