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