pcf8583.c revision 1.8 1 /* $NetBSD: pcf8583.c,v 1.8 2007/12/11 12:09:23 lukem Exp $ */
2
3 /*
4 * Copyright (c) 2003 Wasabi Systems, Inc.
5 * All rights reserved.
6 *
7 * Written by Steve C. Woodford and Jason R. Thorpe for Wasabi Systems, Inc.
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 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
23 * written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 /*
39 * Driver for the Philips PCF8583 Real Time Clock.
40 *
41 * This driver is partially derived from Ben Harris's PCF8583 driver
42 * for NetBSD/acorn26.
43 */
44
45 #include <sys/cdefs.h>
46 __KERNEL_RCSID(0, "$NetBSD: pcf8583.c,v 1.8 2007/12/11 12:09:23 lukem Exp $");
47
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/device.h>
51 #include <sys/kernel.h>
52 #include <sys/fcntl.h>
53 #include <sys/uio.h>
54 #include <sys/conf.h>
55 #include <sys/event.h>
56
57 #include <dev/clock_subr.h>
58
59 #include <dev/i2c/i2cvar.h>
60 #include <dev/i2c/pcf8583reg.h>
61 #include <dev/i2c/pcf8583var.h>
62
63 struct pcfrtc_softc {
64 struct device sc_dev;
65 i2c_tag_t sc_tag;
66 int sc_address;
67 int sc_open;
68 struct todr_chip_handle sc_todr;
69 };
70
71 static int pcfrtc_match(struct device *, struct cfdata *, void *);
72 static void pcfrtc_attach(struct device *, struct device *, void *);
73
74 CFATTACH_DECL(pcfrtc, sizeof(struct pcfrtc_softc),
75 pcfrtc_match, pcfrtc_attach, NULL, NULL);
76 extern struct cfdriver pcfrtc_cd;
77
78 dev_type_open(pcfrtc_open);
79 dev_type_close(pcfrtc_close);
80 dev_type_read(pcfrtc_read);
81 dev_type_write(pcfrtc_write);
82
83 const struct cdevsw pcfrtc_cdevsw = {
84 pcfrtc_open, pcfrtc_close, pcfrtc_read, pcfrtc_write, noioctl,
85 nostop, notty, nopoll, nommap, nokqfilter, D_OTHER
86 };
87
88 static int pcfrtc_clock_read(struct pcfrtc_softc *, struct clock_ymdhms *,
89 uint8_t *);
90 static int pcfrtc_clock_write(struct pcfrtc_softc *, struct clock_ymdhms *,
91 uint8_t);
92 static int pcfrtc_gettime(struct todr_chip_handle *, volatile struct timeval *);
93 static int pcfrtc_settime(struct todr_chip_handle *, volatile struct timeval *);
94
95 int
96 pcfrtc_match(struct device *parent, struct cfdata *cf, void *aux)
97 {
98 struct i2c_attach_args *ia = aux;
99
100 if ((ia->ia_addr & PCF8583_ADDRMASK) == PCF8583_ADDR)
101 return (1);
102
103 return (0);
104 }
105
106 void
107 pcfrtc_attach(struct device *parent, struct device *self, void *aux)
108 {
109 struct pcfrtc_softc *sc = device_private(self);
110 struct i2c_attach_args *ia = aux;
111 uint8_t cmdbuf[1], csr;
112
113 sc->sc_tag = ia->ia_tag;
114 sc->sc_address = ia->ia_addr;
115
116 aprint_naive(": Real-time Clock/NVRAM\n");
117 aprint_normal(": PCF8583 Real-time Clock/NVRAM\n");
118
119 cmdbuf[0] = PCF8583_REG_CSR;
120 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address,
121 cmdbuf, 1, &csr, 1, 0) != 0) {
122 aprint_error("%s: unable to read CSR\n", sc->sc_dev.dv_xname);
123 return;
124 }
125 aprint_normal("%s: ", sc->sc_dev.dv_xname);
126 switch (csr & PCF8583_CSR_FN_MASK) {
127 case PCF8583_CSR_FN_32768HZ:
128 aprint_normal(" 32.768 kHz clock");
129 break;
130
131 case PCF8583_CSR_FN_50HZ:
132 aprint_normal(" 50 Hz clock");
133 break;
134
135 case PCF8583_CSR_FN_EVENT:
136 aprint_normal(" event counter");
137 break;
138
139 case PCF8583_CSR_FN_TEST:
140 aprint_normal(" test mode");
141 break;
142 }
143 if (csr & PCF8583_CSR_STOP)
144 aprint_normal(", stopped");
145 if (csr & PCF8583_CSR_ALARMENABLE)
146 aprint_normal(", alarm enabled");
147 aprint_normal("\n");
148
149 sc->sc_open = 0;
150
151 sc->sc_todr.cookie = sc;
152 sc->sc_todr.todr_gettime = pcfrtc_gettime;
153 sc->sc_todr.todr_settime = pcfrtc_settime;
154 sc->sc_todr.todr_setwen = NULL;
155
156 todr_attach(&sc->sc_todr);
157 }
158
159 /*ARGSUSED*/
160 int
161 pcfrtc_open(dev_t dev, int flag, int fmt, struct lwp *l)
162 {
163 struct pcfrtc_softc *sc;
164
165 if ((sc = device_lookup(&pcfrtc_cd, minor(dev))) == NULL)
166 return (ENXIO);
167
168 /* XXX: Locking */
169
170 if (sc->sc_open)
171 return (EBUSY);
172
173 sc->sc_open = 1;
174 return (0);
175 }
176
177 /*ARGSUSED*/
178 int
179 pcfrtc_close(dev_t dev, int flag, int fmt, struct lwp *l)
180 {
181 struct pcfrtc_softc *sc;
182
183 if ((sc = device_lookup(&pcfrtc_cd, minor(dev))) == NULL)
184 return (ENXIO);
185
186 sc->sc_open = 0;
187 return (0);
188 }
189
190 /*ARGSUSED*/
191 int
192 pcfrtc_read(dev_t dev, struct uio *uio, int flags)
193 {
194 struct pcfrtc_softc *sc;
195 u_int8_t ch, cmdbuf[1];
196 int a, error;
197
198 if ((sc = device_lookup(&pcfrtc_cd, minor(dev))) == NULL)
199 return (ENXIO);
200
201 if (uio->uio_offset >= PCF8583_NVRAM_SIZE)
202 return (EINVAL);
203
204 if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
205 return (error);
206
207 while (uio->uio_resid && uio->uio_offset < PCF8583_NVRAM_SIZE) {
208 a = (int)uio->uio_offset;
209 cmdbuf[0] = a + PCF8583_NVRAM_START;
210 if ((error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
211 sc->sc_address, cmdbuf, 1,
212 &ch, 1, 0)) != 0) {
213 iic_release_bus(sc->sc_tag, 0);
214 printf("%s: pcfrtc_read: read failed at 0x%x\n",
215 sc->sc_dev.dv_xname, a);
216 return (error);
217 }
218 if ((error = uiomove(&ch, 1, uio)) != 0) {
219 iic_release_bus(sc->sc_tag, 0);
220 return (error);
221 }
222 }
223
224 iic_release_bus(sc->sc_tag, 0);
225
226 return (0);
227 }
228
229 /*ARGSUSED*/
230 int
231 pcfrtc_write(dev_t dev, struct uio *uio, int flags)
232 {
233 struct pcfrtc_softc *sc;
234 u_int8_t cmdbuf[2];
235 int a, error;
236
237 if ((sc = device_lookup(&pcfrtc_cd, minor(dev))) == NULL)
238 return (ENXIO);
239
240 if (uio->uio_offset >= PCF8583_NVRAM_SIZE)
241 return (EINVAL);
242
243 if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
244 return (error);
245
246 while (uio->uio_resid && uio->uio_offset < PCF8583_NVRAM_SIZE) {
247 a = (int)uio->uio_offset;
248 cmdbuf[0] = a + PCF8583_NVRAM_START;
249 if ((error = uiomove(&cmdbuf[1], 1, uio)) != 0)
250 break;
251
252 if ((error = iic_exec(sc->sc_tag,
253 uio->uio_resid ? I2C_OP_WRITE : I2C_OP_WRITE_WITH_STOP,
254 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, 0)) != 0) {
255 printf("%s: pcfrtc_write: write failed at 0x%x\n",
256 sc->sc_dev.dv_xname, a);
257 return (error);
258 }
259 }
260
261 iic_release_bus(sc->sc_tag, 0);
262
263 return (error);
264 }
265
266 static int
267 pcfrtc_gettime(struct todr_chip_handle *ch, volatile struct timeval *tv)
268 {
269 struct pcfrtc_softc *sc = ch->cookie;
270 struct clock_ymdhms dt;
271 int err;
272 uint8_t centi;
273
274 if ((err = pcfrtc_clock_read(sc, &dt, ¢i)))
275 return err;
276
277 tv->tv_sec = clock_ymdhms_to_secs(&dt);
278 tv->tv_usec = centi * 10000;
279
280 return (0);
281 }
282
283 static int
284 pcfrtc_settime(struct todr_chip_handle *ch, volatile struct timeval *tv)
285 {
286 struct pcfrtc_softc *sc = ch->cookie;
287 struct clock_ymdhms dt;
288 int err;
289
290 clock_secs_to_ymdhms(tv->tv_sec, &dt);
291
292 if ((err = pcfrtc_clock_write(sc, &dt, tv->tv_usec / 10000) == 0))
293 return err;
294
295 return (0);
296 }
297
298 static const int pcf8583_rtc_offset[] = {
299 PCF8583_REG_CSR,
300 PCF8583_REG_CENTI,
301 PCF8583_REG_SEC,
302 PCF8583_REG_MIN,
303 PCF8583_REG_HOUR,
304 PCF8583_REG_YEARDATE,
305 PCF8583_REG_WKDYMON,
306 PCF8583_REG_TIMER,
307 0xc0, /* NVRAM -- year stored here */
308 0xc1, /* NVRAM -- century stored here */
309 };
310
311 static int
312 pcfrtc_clock_read(struct pcfrtc_softc *sc, struct clock_ymdhms *dt,
313 uint8_t *centi)
314 {
315 u_int8_t bcd[10], cmdbuf[1];
316 int i, err;
317
318 if ((err = iic_acquire_bus(sc->sc_tag, I2C_F_POLL))) {
319 printf("%s: pcfrtc_clock_read: failed to acquire I2C bus\n",
320 sc->sc_dev.dv_xname);
321 return err;
322 }
323
324 /* Read each timekeeping register in order. */
325 for (i = 0; i < 10; i++) {
326 cmdbuf[0] = pcf8583_rtc_offset[i];
327
328 if ((err = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
329 sc->sc_address, cmdbuf, 1,
330 &bcd[i], 1, I2C_F_POLL))) {
331 iic_release_bus(sc->sc_tag, I2C_F_POLL);
332 printf("%s: pcfrtc_clock_read: failed to read rtc "
333 "at 0x%x\n", sc->sc_dev.dv_xname,
334 pcf8583_rtc_offset[i]);
335 return err;
336 }
337 }
338
339 /* Done with I2C */
340 iic_release_bus(sc->sc_tag, I2C_F_POLL);
341
342 /*
343 * Convert the PCF8583's register values into something useable
344 */
345 *centi = FROMBCD(bcd[PCF8583_REG_CENTI]);
346 dt->dt_sec = FROMBCD(bcd[PCF8583_REG_SEC]);
347 dt->dt_min = FROMBCD(bcd[PCF8583_REG_MIN]);
348 dt->dt_hour = FROMBCD(bcd[PCF8583_REG_HOUR] & PCF8583_HOUR_MASK);
349 if (bcd[PCF8583_REG_HOUR] & PCF8583_HOUR_12H) {
350 dt->dt_hour %= 12; /* 12AM -> 0, 12PM -> 12 */
351 if (bcd[PCF8583_REG_HOUR] & PCF8583_HOUR_PM)
352 dt->dt_hour += 12;
353 }
354
355 dt->dt_day = FROMBCD(bcd[PCF8583_REG_YEARDATE] & PCF8583_DATE_MASK);
356 dt->dt_mon = FROMBCD(bcd[PCF8583_REG_WKDYMON] & PCF8583_MON_MASK);
357
358 dt->dt_year = bcd[8] + (bcd[9] * 100);
359 /* Try to notice if the year's rolled over. */
360 if (bcd[PCF8583_REG_CSR] & PCF8583_CSR_MASK)
361 printf("%s: cannot check year in mask mode\n",
362 sc->sc_dev.dv_xname);
363 else {
364 while (dt->dt_year % 4 !=
365 (bcd[PCF8583_REG_YEARDATE] &
366 PCF8583_YEAR_MASK) >> PCF8583_YEAR_SHIFT)
367 dt->dt_year++;
368 }
369
370 return 0;
371 }
372
373 static int
374 pcfrtc_clock_write(struct pcfrtc_softc *sc, struct clock_ymdhms *dt,
375 uint8_t centi)
376 {
377 uint8_t bcd[10], cmdbuf[2];
378 int i, err;
379
380 /*
381 * Convert our time representation into something the PCF8583
382 * can understand.
383 */
384 bcd[PCF8583_REG_CENTI] = centi;
385 bcd[PCF8583_REG_SEC] = TOBCD(dt->dt_sec);
386 bcd[PCF8583_REG_MIN] = TOBCD(dt->dt_min);
387 bcd[PCF8583_REG_HOUR] = TOBCD(dt->dt_hour) & PCF8583_HOUR_MASK;
388 bcd[PCF8583_REG_YEARDATE] = TOBCD(dt->dt_day) |
389 ((dt->dt_year % 4) << PCF8583_YEAR_SHIFT);
390 bcd[PCF8583_REG_WKDYMON] = TOBCD(dt->dt_mon) |
391 ((dt->dt_wday % 4) << PCF8583_WKDY_SHIFT);
392 bcd[8] = dt->dt_year % 100;
393 bcd[9] = dt->dt_year / 100;
394
395 if ((err = iic_acquire_bus(sc->sc_tag, I2C_F_POLL))) {
396 printf("%s: pcfrtc_clock_write: failed to acquire I2C bus\n",
397 sc->sc_dev.dv_xname);
398 return err;
399 }
400
401 for (i = 1; i < 10; i++) {
402 cmdbuf[0] = pcf8583_rtc_offset[i];
403 if ((err = iic_exec(sc->sc_tag,
404 i != 9 ? I2C_OP_WRITE : I2C_OP_WRITE_WITH_STOP,
405 sc->sc_address, cmdbuf, 1,
406 &bcd[i], 1, I2C_F_POLL))) {
407 iic_release_bus(sc->sc_tag, I2C_F_POLL);
408 printf("%s: pcfrtc_clock_write: failed to write rtc "
409 " at 0x%x\n", sc->sc_dev.dv_xname,
410 pcf8583_rtc_offset[i]);
411 return err;
412 }
413 }
414
415 iic_release_bus(sc->sc_tag, I2C_F_POLL);
416
417 return 0;
418 }
419
420 int
421 pcfrtc_bootstrap_read(i2c_tag_t tag, int i2caddr, int offset,
422 u_int8_t *rvp, size_t len)
423 {
424 u_int8_t cmdbuf[1];
425
426 /*
427 * NOTE: "offset" is an absolute offset into the PCF8583
428 * address space, not relative to the NVRAM.
429 */
430
431 if (len == 0)
432 return (0);
433
434 if (iic_acquire_bus(tag, I2C_F_POLL) != 0)
435 return (-1);
436
437 while (len) {
438 /* Read a single byte. */
439 cmdbuf[0] = offset;
440 if (iic_exec(tag, I2C_OP_READ_WITH_STOP, i2caddr,
441 cmdbuf, 1, rvp, 1, I2C_F_POLL)) {
442 iic_release_bus(tag, I2C_F_POLL);
443 return (-1);
444 }
445
446 len--;
447 rvp++;
448 offset++;
449 }
450
451 iic_release_bus(tag, I2C_F_POLL);
452 return (0);
453 }
454
455 int
456 pcfrtc_bootstrap_write(i2c_tag_t tag, int i2caddr, int offset,
457 u_int8_t *rvp, size_t len)
458 {
459 u_int8_t cmdbuf[1];
460
461 /*
462 * NOTE: "offset" is an absolute offset into the PCF8583
463 * address space, not relative to the NVRAM.
464 */
465
466 if (len == 0)
467 return (0);
468
469 if (iic_acquire_bus(tag, I2C_F_POLL) != 0)
470 return (-1);
471
472 while (len) {
473 /* Write a single byte. */
474 cmdbuf[0] = offset;
475 if (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, i2caddr,
476 cmdbuf, 1, rvp, 1, I2C_F_POLL)) {
477 iic_release_bus(tag, I2C_F_POLL);
478 return (-1);
479 }
480
481 len--;
482 rvp++;
483 offset++;
484 }
485
486 iic_release_bus(tag, I2C_F_POLL);
487 return (0);
488 }
489