1 /* $NetBSD: pcf8583.c,v 1.1 2003/09/30 00:35:31 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/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 *, struct timeval *); 90 static int pcfrtc_settime(struct todr_chip_handle *, struct timeval *); 91 static int pcfrtc_getcal(struct todr_chip_handle *, int *); 92 static int pcfrtc_setcal(struct todr_chip_handle *, int); 93 94 int 95 pcfrtc_match(struct device *parent, struct cfdata *cf, void *aux) 96 { 97 struct i2c_attach_args *ia = aux; 98 99 if ((ia->ia_addr & PCF8583_ADDRMASK) == PCF8583_ADDR) 100 return (1); 101 102 return (0); 103 } 104 105 void 106 pcfrtc_attach(struct device *parent, struct device *self, void *aux) 107 { 108 struct pcfrtc_softc *sc = (struct pcfrtc_softc *)self; 109 struct i2c_attach_args *ia = aux; 110 uint8_t cmdbuf[1], csr; 111 112 sc->sc_tag = ia->ia_tag; 113 sc->sc_address = ia->ia_addr; 114 115 aprint_naive(": Real-time Clock/NVRAM\n"); 116 aprint_normal(": PCF8583 Real-time Clock/NVRAM\n"); 117 118 cmdbuf[0] = PCF8583_REG_CSR; 119 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address, 120 cmdbuf, 1, &csr, 1, 0) != 0) { 121 aprint_error("%s: unable to read CSR\n", sc->sc_dev.dv_xname); 122 return; 123 } 124 aprint_normal("%s: ", sc->sc_dev.dv_xname); 125 switch (csr & PCF8583_CSR_FN_MASK) { 126 case PCF8583_CSR_FN_32768HZ: 127 aprint_normal(" 32.768 kHz clock"); 128 break; 129 130 case PCF8583_CSR_FN_50HZ: 131 aprint_normal(" 50 Hz clock"); 132 break; 133 134 case PCF8583_CSR_FN_EVENT: 135 aprint_normal(" event counter"); 136 break; 137 138 case PCF8583_CSR_FN_TEST: 139 aprint_normal(" test mode"); 140 break; 141 } 142 if (csr & PCF8583_CSR_STOP) 143 aprint_normal(", stopped"); 144 if (csr & PCF8583_CSR_ALARMENABLE) 145 aprint_normal(", alarm enabled"); 146 aprint_normal("\n"); 147 148 sc->sc_open = 0; 149 150 sc->sc_todr.cookie = sc; 151 sc->sc_todr.todr_gettime = pcfrtc_gettime; 152 sc->sc_todr.todr_settime = pcfrtc_settime; 153 sc->sc_todr.todr_getcal = pcfrtc_getcal; 154 sc->sc_todr.todr_setcal = pcfrtc_setcal; 155 sc->sc_todr.todr_setwen = NULL; 156 157 todr_attach(&sc->sc_todr); 158 } 159 160 /*ARGSUSED*/ 161 int 162 pcfrtc_open(dev_t dev, int flag, int fmt, struct proc *p) 163 { 164 struct pcfrtc_softc *sc; 165 166 if ((sc = device_lookup(&pcfrtc_cd, minor(dev))) == NULL) 167 return (ENXIO); 168 169 /* XXX: Locking */ 170 171 if (sc->sc_open) 172 return (EBUSY); 173 174 sc->sc_open = 1; 175 return (0); 176 } 177 178 /*ARGSUSED*/ 179 int 180 pcfrtc_close(dev_t dev, int flag, int fmt, struct proc *p) 181 { 182 struct pcfrtc_softc *sc; 183 184 if ((sc = device_lookup(&pcfrtc_cd, minor(dev))) == NULL) 185 return (ENXIO); 186 187 sc->sc_open = 0; 188 return (0); 189 } 190 191 /*ARGSUSED*/ 192 int 193 pcfrtc_read(dev_t dev, struct uio *uio, int flags) 194 { 195 struct pcfrtc_softc *sc; 196 u_int8_t ch, cmdbuf[1]; 197 int a, error; 198 199 if ((sc = device_lookup(&pcfrtc_cd, minor(dev))) == NULL) 200 return (ENXIO); 201 202 if (uio->uio_offset >= PCF8583_NVRAM_SIZE) 203 return (EINVAL); 204 205 if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0) 206 return (error); 207 208 while (uio->uio_resid && uio->uio_offset < PCF8583_NVRAM_SIZE) { 209 a = (int)uio->uio_offset; 210 cmdbuf[0] = a + PCF8583_NVRAM_START; 211 if ((error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 212 sc->sc_address, cmdbuf, 1, 213 &ch, 1, 0)) != 0) { 214 iic_release_bus(sc->sc_tag, 0); 215 printf("%s: pcfrtc_read: read failed at 0x%x\n", 216 sc->sc_dev.dv_xname, a); 217 return (error); 218 } 219 if ((error = uiomove(&ch, 1, uio)) != 0) { 220 iic_release_bus(sc->sc_tag, 0); 221 return (error); 222 } 223 } 224 225 iic_release_bus(sc->sc_tag, 0); 226 227 return (0); 228 } 229 230 /*ARGSUSED*/ 231 int 232 pcfrtc_write(dev_t dev, struct uio *uio, int flags) 233 { 234 struct pcfrtc_softc *sc; 235 u_int8_t cmdbuf[2]; 236 int a, error; 237 238 if ((sc = device_lookup(&pcfrtc_cd, minor(dev))) == NULL) 239 return (ENXIO); 240 241 if (uio->uio_offset >= PCF8583_NVRAM_SIZE) 242 return (EINVAL); 243 244 if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0) 245 return (error); 246 247 while (uio->uio_resid && uio->uio_offset < PCF8583_NVRAM_SIZE) { 248 a = (int)uio->uio_offset; 249 cmdbuf[0] = a + PCF8583_NVRAM_START; 250 if ((error = uiomove(&cmdbuf[1], 1, uio)) != 0) 251 break; 252 253 if ((error = iic_exec(sc->sc_tag, 254 uio->uio_resid ? I2C_OP_WRITE : I2C_OP_WRITE_WITH_STOP, 255 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, 0)) != 0) { 256 printf("%s: pcfrtc_write: write failed at 0x%x\n", 257 sc->sc_dev.dv_xname, a); 258 return (error); 259 } 260 } 261 262 iic_release_bus(sc->sc_tag, 0); 263 264 return (error); 265 } 266 267 static int 268 pcfrtc_gettime(struct todr_chip_handle *ch, struct timeval *tv) 269 { 270 struct pcfrtc_softc *sc = ch->cookie; 271 struct clock_ymdhms dt; 272 uint8_t centi; 273 274 if (pcfrtc_clock_read(sc, &dt, ¢i) == 0) 275 return (-1); 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, struct timeval *tv) 285 { 286 struct pcfrtc_softc *sc = ch->cookie; 287 struct clock_ymdhms dt; 288 289 clock_secs_to_ymdhms(tv->tv_sec, &dt); 290 291 if (pcfrtc_clock_write(sc, &dt, tv->tv_usec / 10000) == 0) 292 return (-1); 293 294 return (0); 295 } 296 297 static int 298 pcfrtc_setcal(struct todr_chip_handle *ch, int cal) 299 { 300 301 return (EOPNOTSUPP); 302 } 303 304 static int 305 pcfrtc_getcal(struct todr_chip_handle *ch, int *cal) 306 { 307 308 return (EOPNOTSUPP); 309 } 310 311 static const int pcf8583_rtc_offset[] = { 312 PCF8583_REG_CSR, 313 PCF8583_REG_CENTI, 314 PCF8583_REG_SEC, 315 PCF8583_REG_MIN, 316 PCF8583_REG_HOUR, 317 PCF8583_REG_YEARDATE, 318 PCF8583_REG_WKDYMON, 319 PCF8583_REG_TIMER, 320 0xc0, /* NVRAM -- year stored here */ 321 0xc1, /* NVRAM -- century stored here */ 322 }; 323 324 static int 325 pcfrtc_clock_read(struct pcfrtc_softc *sc, struct clock_ymdhms *dt, 326 uint8_t *centi) 327 { 328 u_int8_t bcd[10], cmdbuf[1]; 329 int i; 330 331 if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) { 332 printf("%s: pcfrtc_clock_read: failed to acquire I2C bus\n", 333 sc->sc_dev.dv_xname); 334 return (0); 335 } 336 337 /* Read each timekeeping register in order. */ 338 for (i = 0; i < 10; i++) { 339 cmdbuf[0] = pcf8583_rtc_offset[i]; 340 341 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 342 sc->sc_address, cmdbuf, 1, 343 &bcd[i], 1, I2C_F_POLL)) { 344 iic_release_bus(sc->sc_tag, I2C_F_POLL); 345 printf("%s: pcfrtc_clock_read: failed to read rtc " 346 "at 0x%x\n", sc->sc_dev.dv_xname, 347 pcf8583_rtc_offset[i]); 348 return (0); 349 } 350 } 351 352 /* Done with I2C */ 353 iic_release_bus(sc->sc_tag, I2C_F_POLL); 354 355 /* 356 * Convert the PCF8583's register values into something useable 357 */ 358 *centi = FROMBCD(bcd[PCF8583_REG_CENTI]); 359 dt->dt_sec = FROMBCD(bcd[PCF8583_REG_SEC]); 360 dt->dt_min = FROMBCD(bcd[PCF8583_REG_MIN]); 361 dt->dt_hour = FROMBCD(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 = FROMBCD(bcd[PCF8583_REG_YEARDATE] & PCF8583_DATE_MASK); 369 dt->dt_mon = FROMBCD(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 printf("%s: cannot check year in mask mode\n", 375 sc->sc_dev.dv_xname); 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 (1); 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; 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] = TOBCD(dt->dt_sec); 399 bcd[PCF8583_REG_MIN] = TOBCD(dt->dt_min); 400 bcd[PCF8583_REG_HOUR] = TOBCD(dt->dt_hour) & PCF8583_HOUR_MASK; 401 bcd[PCF8583_REG_YEARDATE] = TOBCD(dt->dt_day) | 402 ((dt->dt_year % 4) << PCF8583_YEAR_SHIFT); 403 bcd[PCF8583_REG_WKDYMON] = TOBCD(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 (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) { 409 printf("%s: pcfrtc_clock_write: failed to acquire I2C bus\n", 410 sc->sc_dev.dv_xname); 411 return (0); 412 } 413 414 for (i = 1; i < 10; i++) { 415 cmdbuf[0] = pcf8583_rtc_offset[i]; 416 if (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, I2C_F_POLL)) { 420 iic_release_bus(sc->sc_tag, I2C_F_POLL); 421 printf("%s: pcfrtc_clock_write: failed to write rtc " 422 " at 0x%x\n", sc->sc_dev.dv_xname, 423 pcf8583_rtc_offset[i]); 424 return (0); 425 } 426 } 427 428 iic_release_bus(sc->sc_tag, I2C_F_POLL); 429 430 return (1); 431 } 432 433 int 434 pcfrtc_bootstrap_read(i2c_tag_t tag, int i2caddr, int offset, 435 u_int8_t *rvp, size_t len) 436 { 437 u_int8_t cmdbuf[1]; 438 439 /* 440 * NOTE: "offset" is an absolute offset into the PCF8583 441 * address space, not relative to the NVRAM. 442 */ 443 444 if (len == 0) 445 return (0); 446 447 if (iic_acquire_bus(tag, I2C_F_POLL) != 0) 448 return (-1); 449 450 while (len) { 451 /* Read a single byte. */ 452 cmdbuf[0] = offset; 453 if (iic_exec(tag, I2C_OP_READ_WITH_STOP, i2caddr, 454 cmdbuf, 1, rvp, 1, I2C_F_POLL)) { 455 iic_release_bus(tag, I2C_F_POLL); 456 return (-1); 457 } 458 459 len--; 460 rvp++; 461 offset++; 462 } 463 464 iic_release_bus(tag, I2C_F_POLL); 465 return (0); 466 } 467 468 int 469 pcfrtc_bootstrap_write(i2c_tag_t tag, int i2caddr, int offset, 470 u_int8_t *rvp, size_t len) 471 { 472 u_int8_t cmdbuf[1]; 473 474 /* 475 * NOTE: "offset" is an absolute offset into the PCF8583 476 * address space, not relative to the NVRAM. 477 */ 478 479 if (len == 0) 480 return (0); 481 482 if (iic_acquire_bus(tag, I2C_F_POLL) != 0) 483 return (-1); 484 485 while (len) { 486 /* Write a single byte. */ 487 cmdbuf[0] = offset; 488 if (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, i2caddr, 489 cmdbuf, 1, rvp, 1, I2C_F_POLL)) { 490 iic_release_bus(tag, I2C_F_POLL); 491 return (-1); 492 } 493 494 len--; 495 rvp++; 496 offset++; 497 } 498 499 iic_release_bus(tag, I2C_F_POLL); 500 return (0); 501 } 502