1 /* $NetBSD: pcf8583.c,v 1.12 2009/12/12 14:44:10 tsutsui 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.12 2009/12/12 14:44:10 tsutsui 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 device_t 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(device_t, cfdata_t, void *); 72 static void pcfrtc_attach(device_t, device_t, void *); 73 74 CFATTACH_DECL_NEW(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 *, struct timeval *); 93 static int pcfrtc_settime(struct todr_chip_handle *, struct timeval *); 94 95 int 96 pcfrtc_match(device_t parent, cfdata_t 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(device_t parent, device_t 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 sc->sc_dev = self; 116 117 aprint_naive(": Real-time Clock/NVRAM\n"); 118 aprint_normal(": PCF8583 Real-time Clock/NVRAM\n"); 119 120 cmdbuf[0] = PCF8583_REG_CSR; 121 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address, 122 cmdbuf, 1, &csr, 1, 0) != 0) { 123 aprint_error_dev(self, "unable to read CSR\n"); 124 return; 125 } 126 aprint_normal_dev(sc->sc_dev, ""); 127 switch (csr & PCF8583_CSR_FN_MASK) { 128 case PCF8583_CSR_FN_32768HZ: 129 aprint_normal(" 32.768 kHz clock"); 130 break; 131 132 case PCF8583_CSR_FN_50HZ: 133 aprint_normal(" 50 Hz clock"); 134 break; 135 136 case PCF8583_CSR_FN_EVENT: 137 aprint_normal(" event counter"); 138 break; 139 140 case PCF8583_CSR_FN_TEST: 141 aprint_normal(" test mode"); 142 break; 143 } 144 if (csr & PCF8583_CSR_STOP) 145 aprint_normal(", stopped"); 146 if (csr & PCF8583_CSR_ALARMENABLE) 147 aprint_normal(", alarm enabled"); 148 aprint_normal("\n"); 149 150 sc->sc_open = 0; 151 152 sc->sc_todr.cookie = sc; 153 sc->sc_todr.todr_gettime = pcfrtc_gettime; 154 sc->sc_todr.todr_settime = pcfrtc_settime; 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 lwp *l) 163 { 164 struct pcfrtc_softc *sc; 165 166 if ((sc = device_lookup_private(&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 lwp *l) 181 { 182 struct pcfrtc_softc *sc; 183 184 if ((sc = device_lookup_private(&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_private(&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 aprint_error_dev(sc->sc_dev, 216 "pcfrtc_read: read failed at 0x%x\n", 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_private(&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 aprint_error_dev(sc->sc_dev, 257 "pcfrtc_write: write failed at 0x%x\n", 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 int err; 273 uint8_t centi; 274 275 if ((err = pcfrtc_clock_read(sc, &dt, ¢i))) 276 return err; 277 278 tv->tv_sec = clock_ymdhms_to_secs(&dt); 279 tv->tv_usec = centi * 10000; 280 281 return (0); 282 } 283 284 static int 285 pcfrtc_settime(struct todr_chip_handle *ch, struct timeval *tv) 286 { 287 struct pcfrtc_softc *sc = ch->cookie; 288 struct clock_ymdhms dt; 289 int err; 290 291 clock_secs_to_ymdhms(tv->tv_sec, &dt); 292 293 if ((err = pcfrtc_clock_write(sc, &dt, tv->tv_usec / 10000) == 0)) 294 return err; 295 296 return (0); 297 } 298 299 static const int pcf8583_rtc_offset[] = { 300 PCF8583_REG_CSR, 301 PCF8583_REG_CENTI, 302 PCF8583_REG_SEC, 303 PCF8583_REG_MIN, 304 PCF8583_REG_HOUR, 305 PCF8583_REG_YEARDATE, 306 PCF8583_REG_WKDYMON, 307 PCF8583_REG_TIMER, 308 0xc0, /* NVRAM -- year stored here */ 309 0xc1, /* NVRAM -- century stored here */ 310 }; 311 312 static int 313 pcfrtc_clock_read(struct pcfrtc_softc *sc, struct clock_ymdhms *dt, 314 uint8_t *centi) 315 { 316 u_int8_t bcd[10], cmdbuf[1]; 317 int i, err; 318 319 if ((err = iic_acquire_bus(sc->sc_tag, I2C_F_POLL))) { 320 aprint_error_dev(sc->sc_dev, 321 "pcfrtc_clock_read: failed to acquire I2C bus\n"); 322 return err; 323 } 324 325 /* Read each timekeeping register in order. */ 326 for (i = 0; i < 10; i++) { 327 cmdbuf[0] = pcf8583_rtc_offset[i]; 328 329 if ((err = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 330 sc->sc_address, cmdbuf, 1, 331 &bcd[i], 1, I2C_F_POLL))) { 332 iic_release_bus(sc->sc_tag, I2C_F_POLL); 333 aprint_error_dev(sc->sc_dev, 334 "pcfrtc_clock_read: failed to read rtc " 335 "at 0x%x\n", 336 pcf8583_rtc_offset[i]); 337 return err; 338 } 339 } 340 341 /* Done with I2C */ 342 iic_release_bus(sc->sc_tag, I2C_F_POLL); 343 344 /* 345 * Convert the PCF8583's register values into something useable 346 */ 347 *centi = FROMBCD(bcd[PCF8583_REG_CENTI]); 348 dt->dt_sec = FROMBCD(bcd[PCF8583_REG_SEC]); 349 dt->dt_min = FROMBCD(bcd[PCF8583_REG_MIN]); 350 dt->dt_hour = FROMBCD(bcd[PCF8583_REG_HOUR] & PCF8583_HOUR_MASK); 351 if (bcd[PCF8583_REG_HOUR] & PCF8583_HOUR_12H) { 352 dt->dt_hour %= 12; /* 12AM -> 0, 12PM -> 12 */ 353 if (bcd[PCF8583_REG_HOUR] & PCF8583_HOUR_PM) 354 dt->dt_hour += 12; 355 } 356 357 dt->dt_day = FROMBCD(bcd[PCF8583_REG_YEARDATE] & PCF8583_DATE_MASK); 358 dt->dt_mon = FROMBCD(bcd[PCF8583_REG_WKDYMON] & PCF8583_MON_MASK); 359 360 dt->dt_year = bcd[8] + (bcd[9] * 100); 361 /* Try to notice if the year's rolled over. */ 362 if (bcd[PCF8583_REG_CSR] & PCF8583_CSR_MASK) 363 aprint_error_dev(sc->sc_dev, 364 "cannot check year in mask mode\n"); 365 else { 366 while (dt->dt_year % 4 != 367 (bcd[PCF8583_REG_YEARDATE] & 368 PCF8583_YEAR_MASK) >> PCF8583_YEAR_SHIFT) 369 dt->dt_year++; 370 } 371 372 return 0; 373 } 374 375 static int 376 pcfrtc_clock_write(struct pcfrtc_softc *sc, struct clock_ymdhms *dt, 377 uint8_t centi) 378 { 379 uint8_t bcd[10], cmdbuf[2]; 380 int i, err; 381 382 /* 383 * Convert our time representation into something the PCF8583 384 * can understand. 385 */ 386 bcd[PCF8583_REG_CENTI] = centi; 387 bcd[PCF8583_REG_SEC] = TOBCD(dt->dt_sec); 388 bcd[PCF8583_REG_MIN] = TOBCD(dt->dt_min); 389 bcd[PCF8583_REG_HOUR] = TOBCD(dt->dt_hour) & PCF8583_HOUR_MASK; 390 bcd[PCF8583_REG_YEARDATE] = TOBCD(dt->dt_day) | 391 ((dt->dt_year % 4) << PCF8583_YEAR_SHIFT); 392 bcd[PCF8583_REG_WKDYMON] = TOBCD(dt->dt_mon) | 393 ((dt->dt_wday % 4) << PCF8583_WKDY_SHIFT); 394 bcd[8] = dt->dt_year % 100; 395 bcd[9] = dt->dt_year / 100; 396 397 if ((err = iic_acquire_bus(sc->sc_tag, I2C_F_POLL))) { 398 aprint_error_dev(sc->sc_dev, 399 "pcfrtc_clock_write: failed to acquire I2C bus\n"); 400 return err; 401 } 402 403 for (i = 1; i < 10; i++) { 404 cmdbuf[0] = pcf8583_rtc_offset[i]; 405 if ((err = iic_exec(sc->sc_tag, 406 i != 9 ? I2C_OP_WRITE : I2C_OP_WRITE_WITH_STOP, 407 sc->sc_address, cmdbuf, 1, 408 &bcd[i], 1, I2C_F_POLL))) { 409 iic_release_bus(sc->sc_tag, I2C_F_POLL); 410 aprint_error_dev(sc->sc_dev, 411 "pcfrtc_clock_write: failed to write rtc " 412 " at 0x%x\n", 413 pcf8583_rtc_offset[i]); 414 return err; 415 } 416 } 417 418 iic_release_bus(sc->sc_tag, I2C_F_POLL); 419 420 return 0; 421 } 422 423 int 424 pcfrtc_bootstrap_read(i2c_tag_t tag, int i2caddr, int offset, 425 u_int8_t *rvp, size_t len) 426 { 427 u_int8_t cmdbuf[1]; 428 429 /* 430 * NOTE: "offset" is an absolute offset into the PCF8583 431 * address space, not relative to the NVRAM. 432 */ 433 434 if (len == 0) 435 return (0); 436 437 if (iic_acquire_bus(tag, I2C_F_POLL) != 0) 438 return (-1); 439 440 while (len) { 441 /* Read a single byte. */ 442 cmdbuf[0] = offset; 443 if (iic_exec(tag, I2C_OP_READ_WITH_STOP, i2caddr, 444 cmdbuf, 1, rvp, 1, I2C_F_POLL)) { 445 iic_release_bus(tag, I2C_F_POLL); 446 return (-1); 447 } 448 449 len--; 450 rvp++; 451 offset++; 452 } 453 454 iic_release_bus(tag, I2C_F_POLL); 455 return (0); 456 } 457 458 int 459 pcfrtc_bootstrap_write(i2c_tag_t tag, int i2caddr, int offset, 460 u_int8_t *rvp, size_t len) 461 { 462 u_int8_t cmdbuf[1]; 463 464 /* 465 * NOTE: "offset" is an absolute offset into the PCF8583 466 * address space, not relative to the NVRAM. 467 */ 468 469 if (len == 0) 470 return (0); 471 472 if (iic_acquire_bus(tag, I2C_F_POLL) != 0) 473 return (-1); 474 475 while (len) { 476 /* Write a single byte. */ 477 cmdbuf[0] = offset; 478 if (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, i2caddr, 479 cmdbuf, 1, rvp, 1, I2C_F_POLL)) { 480 iic_release_bus(tag, I2C_F_POLL); 481 return (-1); 482 } 483 484 len--; 485 rvp++; 486 offset++; 487 } 488 489 iic_release_bus(tag, I2C_F_POLL); 490 return (0); 491 } 492