1 /* $NetBSD: pcf8583.c,v 1.19 2020/01/02 16:53:05 thorpej 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.19 2020/01/02 16:53:05 thorpej 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 #include "ioconf.h"
64
65 struct pcfrtc_softc {
66 device_t sc_dev;
67 i2c_tag_t sc_tag;
68 int sc_address;
69 int sc_open;
70 struct todr_chip_handle sc_todr;
71 };
72
73 static int pcfrtc_match(device_t, cfdata_t, void *);
74 static void pcfrtc_attach(device_t, device_t, void *);
75
76 CFATTACH_DECL_NEW(pcfrtc, sizeof(struct pcfrtc_softc),
77 pcfrtc_match, pcfrtc_attach, NULL, NULL);
78
79 dev_type_open(pcfrtc_open);
80 dev_type_close(pcfrtc_close);
81 dev_type_read(pcfrtc_read);
82 dev_type_write(pcfrtc_write);
83
84 const struct cdevsw pcfrtc_cdevsw = {
85 .d_open = pcfrtc_open,
86 .d_close = pcfrtc_close,
87 .d_read = pcfrtc_read,
88 .d_write = pcfrtc_write,
89 .d_ioctl = noioctl,
90 .d_stop = nostop,
91 .d_tty = notty,
92 .d_poll = nopoll,
93 .d_mmap = nommap,
94 .d_kqfilter = nokqfilter,
95 .d_discard = nodiscard,
96 .d_flag = D_OTHER
97 };
98
99 static int pcfrtc_clock_read(struct pcfrtc_softc *, struct clock_ymdhms *,
100 uint8_t *);
101 static int pcfrtc_clock_write(struct pcfrtc_softc *, struct clock_ymdhms *,
102 uint8_t);
103 static int pcfrtc_gettime(struct todr_chip_handle *, struct timeval *);
104 static int pcfrtc_settime(struct todr_chip_handle *, struct timeval *);
105
106 int
pcfrtc_match(device_t parent,cfdata_t cf,void * aux)107 pcfrtc_match(device_t parent, cfdata_t cf, void *aux)
108 {
109 struct i2c_attach_args *ia = aux;
110
111 if ((ia->ia_addr & PCF8583_ADDRMASK) == PCF8583_ADDR)
112 return (I2C_MATCH_ADDRESS_ONLY);
113
114 return (0);
115 }
116
117 void
pcfrtc_attach(device_t parent,device_t self,void * aux)118 pcfrtc_attach(device_t parent, device_t self, void *aux)
119 {
120 struct pcfrtc_softc *sc = device_private(self);
121 struct i2c_attach_args *ia = aux;
122 uint8_t cmdbuf[1], csr;
123
124 sc->sc_tag = ia->ia_tag;
125 sc->sc_address = ia->ia_addr;
126 sc->sc_dev = self;
127
128 aprint_naive(": Real-time Clock/NVRAM\n");
129 aprint_normal(": PCF8583 Real-time Clock/NVRAM\n");
130
131 cmdbuf[0] = PCF8583_REG_CSR;
132 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address,
133 cmdbuf, 1, &csr, 1, 0) != 0) {
134 aprint_error_dev(self, "unable to read CSR\n");
135 return;
136 }
137 aprint_normal_dev(sc->sc_dev, "");
138 switch (csr & PCF8583_CSR_FN_MASK) {
139 case PCF8583_CSR_FN_32768HZ:
140 aprint_normal(" 32.768 kHz clock");
141 break;
142
143 case PCF8583_CSR_FN_50HZ:
144 aprint_normal(" 50 Hz clock");
145 break;
146
147 case PCF8583_CSR_FN_EVENT:
148 aprint_normal(" event counter");
149 break;
150
151 case PCF8583_CSR_FN_TEST:
152 aprint_normal(" test mode");
153 break;
154 }
155 if (csr & PCF8583_CSR_STOP)
156 aprint_normal(", stopped");
157 if (csr & PCF8583_CSR_ALARMENABLE)
158 aprint_normal(", alarm enabled");
159 aprint_normal("\n");
160
161 sc->sc_open = 0;
162
163 sc->sc_todr.cookie = sc;
164 sc->sc_todr.todr_gettime = pcfrtc_gettime;
165 sc->sc_todr.todr_settime = pcfrtc_settime;
166 sc->sc_todr.todr_setwen = NULL;
167
168 todr_attach(&sc->sc_todr);
169 }
170
171 /*ARGSUSED*/
172 int
pcfrtc_open(dev_t dev,int flag,int fmt,struct lwp * l)173 pcfrtc_open(dev_t dev, int flag, int fmt, struct lwp *l)
174 {
175 struct pcfrtc_softc *sc;
176
177 if ((sc = device_lookup_private(&pcfrtc_cd, minor(dev))) == NULL)
178 return (ENXIO);
179
180 /* XXX: Locking */
181
182 if (sc->sc_open)
183 return (EBUSY);
184
185 sc->sc_open = 1;
186 return (0);
187 }
188
189 /*ARGSUSED*/
190 int
pcfrtc_close(dev_t dev,int flag,int fmt,struct lwp * l)191 pcfrtc_close(dev_t dev, int flag, int fmt, struct lwp *l)
192 {
193 struct pcfrtc_softc *sc;
194
195 if ((sc = device_lookup_private(&pcfrtc_cd, minor(dev))) == NULL)
196 return (ENXIO);
197
198 sc->sc_open = 0;
199 return (0);
200 }
201
202 /*ARGSUSED*/
203 int
pcfrtc_read(dev_t dev,struct uio * uio,int flags)204 pcfrtc_read(dev_t dev, struct uio *uio, int flags)
205 {
206 struct pcfrtc_softc *sc;
207 u_int8_t ch, cmdbuf[1];
208 int a, error;
209
210 if ((sc = device_lookup_private(&pcfrtc_cd, minor(dev))) == NULL)
211 return (ENXIO);
212
213 if (uio->uio_offset >= PCF8583_NVRAM_SIZE)
214 return (EINVAL);
215
216 if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
217 return (error);
218
219 while (uio->uio_resid && uio->uio_offset < PCF8583_NVRAM_SIZE) {
220 a = (int)uio->uio_offset;
221 cmdbuf[0] = a + PCF8583_NVRAM_START;
222 if ((error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
223 sc->sc_address, cmdbuf, 1,
224 &ch, 1, 0)) != 0) {
225 iic_release_bus(sc->sc_tag, 0);
226 aprint_error_dev(sc->sc_dev,
227 "pcfrtc_read: read failed at 0x%x\n", a);
228 return (error);
229 }
230 if ((error = uiomove(&ch, 1, uio)) != 0) {
231 iic_release_bus(sc->sc_tag, 0);
232 return (error);
233 }
234 }
235
236 iic_release_bus(sc->sc_tag, 0);
237
238 return (0);
239 }
240
241 /*ARGSUSED*/
242 int
pcfrtc_write(dev_t dev,struct uio * uio,int flags)243 pcfrtc_write(dev_t dev, struct uio *uio, int flags)
244 {
245 struct pcfrtc_softc *sc;
246 u_int8_t cmdbuf[2];
247 int a, error;
248
249 if ((sc = device_lookup_private(&pcfrtc_cd, minor(dev))) == NULL)
250 return (ENXIO);
251
252 if (uio->uio_offset >= PCF8583_NVRAM_SIZE)
253 return (EINVAL);
254
255 if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
256 return (error);
257
258 while (uio->uio_resid && uio->uio_offset < PCF8583_NVRAM_SIZE) {
259 a = (int)uio->uio_offset;
260 cmdbuf[0] = a + PCF8583_NVRAM_START;
261 if ((error = uiomove(&cmdbuf[1], 1, uio)) != 0)
262 break;
263
264 if ((error = iic_exec(sc->sc_tag,
265 uio->uio_resid ? I2C_OP_WRITE : I2C_OP_WRITE_WITH_STOP,
266 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, 0)) != 0) {
267 aprint_error_dev(sc->sc_dev,
268 "pcfrtc_write: write failed at 0x%x\n", a);
269 return (error);
270 }
271 }
272
273 iic_release_bus(sc->sc_tag, 0);
274
275 return (error);
276 }
277
278 static int
pcfrtc_gettime(struct todr_chip_handle * ch,struct timeval * tv)279 pcfrtc_gettime(struct todr_chip_handle *ch, struct timeval *tv)
280 {
281 struct pcfrtc_softc *sc = ch->cookie;
282 struct clock_ymdhms dt;
283 int err;
284 uint8_t centi;
285
286 if ((err = pcfrtc_clock_read(sc, &dt, ¢i)))
287 return err;
288
289 tv->tv_sec = clock_ymdhms_to_secs(&dt);
290 tv->tv_usec = centi * 10000;
291
292 return (0);
293 }
294
295 static int
pcfrtc_settime(struct todr_chip_handle * ch,struct timeval * tv)296 pcfrtc_settime(struct todr_chip_handle *ch, struct timeval *tv)
297 {
298 struct pcfrtc_softc *sc = ch->cookie;
299 struct clock_ymdhms dt;
300 int err;
301
302 clock_secs_to_ymdhms(tv->tv_sec, &dt);
303
304 if ((err = pcfrtc_clock_write(sc, &dt, tv->tv_usec / 10000)) != 0)
305 return err;
306
307 return (0);
308 }
309
310 static const int pcf8583_rtc_offset[] = {
311 PCF8583_REG_CSR,
312 PCF8583_REG_CENTI,
313 PCF8583_REG_SEC,
314 PCF8583_REG_MIN,
315 PCF8583_REG_HOUR,
316 PCF8583_REG_YEARDATE,
317 PCF8583_REG_WKDYMON,
318 PCF8583_REG_TIMER,
319 0xc0, /* NVRAM -- year stored here */
320 0xc1, /* NVRAM -- century stored here */
321 };
322
323 static int
pcfrtc_clock_read(struct pcfrtc_softc * sc,struct clock_ymdhms * dt,uint8_t * centi)324 pcfrtc_clock_read(struct pcfrtc_softc *sc, struct clock_ymdhms *dt,
325 uint8_t *centi)
326 {
327 u_int8_t bcd[10], cmdbuf[1];
328 int i, err;
329
330 if ((err = iic_acquire_bus(sc->sc_tag, 0))) {
331 aprint_error_dev(sc->sc_dev,
332 "pcfrtc_clock_read: failed to acquire I2C bus\n");
333 return err;
334 }
335
336 /* Read each timekeeping register in order. */
337 for (i = 0; i < 10; i++) {
338 cmdbuf[0] = pcf8583_rtc_offset[i];
339
340 if ((err = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
341 sc->sc_address, cmdbuf, 1,
342 &bcd[i], 1, 0))) {
343 iic_release_bus(sc->sc_tag, 0);
344 aprint_error_dev(sc->sc_dev,
345 "pcfrtc_clock_read: failed to read rtc "
346 "at 0x%x\n",
347 pcf8583_rtc_offset[i]);
348 return err;
349 }
350 }
351
352 /* Done with I2C */
353 iic_release_bus(sc->sc_tag, 0);
354
355 /*
356 * Convert the PCF8583's register values into something useable
357 */
358 *centi = bcdtobin(bcd[PCF8583_REG_CENTI]);
359 dt->dt_sec = bcdtobin(bcd[PCF8583_REG_SEC]);
360 dt->dt_min = bcdtobin(bcd[PCF8583_REG_MIN]);
361 dt->dt_hour = bcdtobin(bcd[PCF8583_REG_HOUR] & PCF8583_HOUR_MASK);
362 if (bcd[PCF8583_REG_HOUR] & PCF8583_HOUR_12H) {
363 dt->dt_hour %= 12; /* 12AM -> 0, 12PM -> 12 */
364 if (bcd[PCF8583_REG_HOUR] & PCF8583_HOUR_PM)
365 dt->dt_hour += 12;
366 }
367
368 dt->dt_day = bcdtobin(bcd[PCF8583_REG_YEARDATE] & PCF8583_DATE_MASK);
369 dt->dt_mon = bcdtobin(bcd[PCF8583_REG_WKDYMON] & PCF8583_MON_MASK);
370
371 dt->dt_year = bcd[8] + (bcd[9] * 100);
372 /* Try to notice if the year's rolled over. */
373 if (bcd[PCF8583_REG_CSR] & PCF8583_CSR_MASK)
374 aprint_error_dev(sc->sc_dev,
375 "cannot check year in mask mode\n");
376 else {
377 while (dt->dt_year % 4 !=
378 (bcd[PCF8583_REG_YEARDATE] &
379 PCF8583_YEAR_MASK) >> PCF8583_YEAR_SHIFT)
380 dt->dt_year++;
381 }
382
383 return 0;
384 }
385
386 static int
pcfrtc_clock_write(struct pcfrtc_softc * sc,struct clock_ymdhms * dt,uint8_t centi)387 pcfrtc_clock_write(struct pcfrtc_softc *sc, struct clock_ymdhms *dt,
388 uint8_t centi)
389 {
390 uint8_t bcd[10], cmdbuf[2];
391 int i, err;
392
393 /*
394 * Convert our time representation into something the PCF8583
395 * can understand.
396 */
397 bcd[PCF8583_REG_CENTI] = centi;
398 bcd[PCF8583_REG_SEC] = bintobcd(dt->dt_sec);
399 bcd[PCF8583_REG_MIN] = bintobcd(dt->dt_min);
400 bcd[PCF8583_REG_HOUR] = bintobcd(dt->dt_hour) & PCF8583_HOUR_MASK;
401 bcd[PCF8583_REG_YEARDATE] = bintobcd(dt->dt_day) |
402 ((dt->dt_year % 4) << PCF8583_YEAR_SHIFT);
403 bcd[PCF8583_REG_WKDYMON] = bintobcd(dt->dt_mon) |
404 ((dt->dt_wday % 4) << PCF8583_WKDY_SHIFT);
405 bcd[8] = dt->dt_year % 100;
406 bcd[9] = dt->dt_year / 100;
407
408 if ((err = iic_acquire_bus(sc->sc_tag, 0))) {
409 aprint_error_dev(sc->sc_dev,
410 "pcfrtc_clock_write: failed to acquire I2C bus\n");
411 return err;
412 }
413
414 for (i = 1; i < 10; i++) {
415 cmdbuf[0] = pcf8583_rtc_offset[i];
416 if ((err = iic_exec(sc->sc_tag,
417 i != 9 ? I2C_OP_WRITE : I2C_OP_WRITE_WITH_STOP,
418 sc->sc_address, cmdbuf, 1,
419 &bcd[i], 1, 0))) {
420 iic_release_bus(sc->sc_tag, 0);
421 aprint_error_dev(sc->sc_dev,
422 "pcfrtc_clock_write: failed to write rtc "
423 " at 0x%x\n",
424 pcf8583_rtc_offset[i]);
425 return err;
426 }
427 }
428
429 iic_release_bus(sc->sc_tag, 0);
430
431 return 0;
432 }
433
434 int
pcfrtc_bootstrap_read(i2c_tag_t tag,int i2caddr,int offset,u_int8_t * rvp,size_t len)435 pcfrtc_bootstrap_read(i2c_tag_t tag, int i2caddr, int offset,
436 u_int8_t *rvp, size_t len)
437 {
438 u_int8_t cmdbuf[1];
439
440 /*
441 * NOTE: "offset" is an absolute offset into the PCF8583
442 * address space, not relative to the NVRAM.
443 */
444
445 if (len == 0)
446 return (0);
447
448 if (iic_acquire_bus(tag, 0) != 0)
449 return (-1);
450
451 while (len) {
452 /* Read a single byte. */
453 cmdbuf[0] = offset;
454 if (iic_exec(tag, I2C_OP_READ_WITH_STOP, i2caddr,
455 cmdbuf, 1, rvp, 1, 0)) {
456 iic_release_bus(tag, 0);
457 return (-1);
458 }
459
460 len--;
461 rvp++;
462 offset++;
463 }
464
465 iic_release_bus(tag, 0);
466 return (0);
467 }
468
469 int
pcfrtc_bootstrap_write(i2c_tag_t tag,int i2caddr,int offset,u_int8_t * rvp,size_t len)470 pcfrtc_bootstrap_write(i2c_tag_t tag, int i2caddr, int offset,
471 u_int8_t *rvp, size_t len)
472 {
473 u_int8_t cmdbuf[1];
474
475 /*
476 * NOTE: "offset" is an absolute offset into the PCF8583
477 * address space, not relative to the NVRAM.
478 */
479
480 if (len == 0)
481 return (0);
482
483 if (iic_acquire_bus(tag, 0) != 0)
484 return (-1);
485
486 while (len) {
487 /* Write a single byte. */
488 cmdbuf[0] = offset;
489 if (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, i2caddr,
490 cmdbuf, 1, rvp, 1, 0)) {
491 iic_release_bus(tag, 0);
492 return (-1);
493 }
494
495 len--;
496 rvp++;
497 offset++;
498 }
499
500 iic_release_bus(tag, 0);
501 return (0);
502 }
503