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 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 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 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 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 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 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 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 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 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 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 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 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