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