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, &centi)))
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