1 1.21 thorpej /* $NetBSD: pcf8583.c,v 1.21 2025/09/07 21:45:15 thorpej Exp $ */ 2 1.1 thorpej 3 1.1 thorpej /* 4 1.1 thorpej * Copyright (c) 2003 Wasabi Systems, Inc. 5 1.1 thorpej * All rights reserved. 6 1.1 thorpej * 7 1.1 thorpej * Written by Steve C. Woodford and Jason R. Thorpe for Wasabi Systems, Inc. 8 1.1 thorpej * 9 1.1 thorpej * Redistribution and use in source and binary forms, with or without 10 1.1 thorpej * modification, are permitted provided that the following conditions 11 1.1 thorpej * are met: 12 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 13 1.1 thorpej * notice, this list of conditions and the following disclaimer. 14 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 thorpej * notice, this list of conditions and the following disclaimer in the 16 1.1 thorpej * documentation and/or other materials provided with the distribution. 17 1.1 thorpej * 3. All advertising materials mentioning features or use of this software 18 1.1 thorpej * must display the following acknowledgement: 19 1.1 thorpej * This product includes software developed for the NetBSD Project by 20 1.1 thorpej * Wasabi Systems, Inc. 21 1.1 thorpej * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 1.1 thorpej * or promote products derived from this software without specific prior 23 1.1 thorpej * written permission. 24 1.1 thorpej * 25 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 1.1 thorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 1.1 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 1.1 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 1.1 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 1.1 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 1.1 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 1.1 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 1.1 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 1.1 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 1.1 thorpej * POSSIBILITY OF SUCH DAMAGE. 36 1.1 thorpej */ 37 1.1 thorpej 38 1.1 thorpej /* 39 1.1 thorpej * Driver for the Philips PCF8583 Real Time Clock. 40 1.1 thorpej * 41 1.1 thorpej * This driver is partially derived from Ben Harris's PCF8583 driver 42 1.1 thorpej * for NetBSD/acorn26. 43 1.1 thorpej */ 44 1.1 thorpej 45 1.8 lukem #include <sys/cdefs.h> 46 1.21 thorpej __KERNEL_RCSID(0, "$NetBSD: pcf8583.c,v 1.21 2025/09/07 21:45:15 thorpej Exp $"); 47 1.8 lukem 48 1.1 thorpej #include <sys/param.h> 49 1.1 thorpej #include <sys/systm.h> 50 1.1 thorpej #include <sys/device.h> 51 1.1 thorpej #include <sys/kernel.h> 52 1.1 thorpej #include <sys/fcntl.h> 53 1.1 thorpej #include <sys/uio.h> 54 1.1 thorpej #include <sys/conf.h> 55 1.1 thorpej #include <sys/event.h> 56 1.1 thorpej 57 1.1 thorpej #include <dev/clock_subr.h> 58 1.1 thorpej 59 1.1 thorpej #include <dev/i2c/i2cvar.h> 60 1.1 thorpej #include <dev/i2c/pcf8583reg.h> 61 1.1 thorpej #include <dev/i2c/pcf8583var.h> 62 1.1 thorpej 63 1.17 riastrad #include "ioconf.h" 64 1.17 riastrad 65 1.1 thorpej struct pcfrtc_softc { 66 1.10 xtraeme device_t sc_dev; 67 1.1 thorpej i2c_tag_t sc_tag; 68 1.1 thorpej int sc_address; 69 1.1 thorpej int sc_open; 70 1.1 thorpej struct todr_chip_handle sc_todr; 71 1.1 thorpej }; 72 1.1 thorpej 73 1.10 xtraeme static int pcfrtc_match(device_t, cfdata_t, void *); 74 1.10 xtraeme static void pcfrtc_attach(device_t, device_t, void *); 75 1.1 thorpej 76 1.10 xtraeme CFATTACH_DECL_NEW(pcfrtc, sizeof(struct pcfrtc_softc), 77 1.1 thorpej pcfrtc_match, pcfrtc_attach, NULL, NULL); 78 1.1 thorpej 79 1.1 thorpej dev_type_open(pcfrtc_open); 80 1.1 thorpej dev_type_close(pcfrtc_close); 81 1.1 thorpej dev_type_read(pcfrtc_read); 82 1.1 thorpej dev_type_write(pcfrtc_write); 83 1.1 thorpej 84 1.1 thorpej const struct cdevsw pcfrtc_cdevsw = { 85 1.14 dholland .d_open = pcfrtc_open, 86 1.14 dholland .d_close = pcfrtc_close, 87 1.14 dholland .d_read = pcfrtc_read, 88 1.14 dholland .d_write = pcfrtc_write, 89 1.14 dholland .d_ioctl = noioctl, 90 1.14 dholland .d_stop = nostop, 91 1.14 dholland .d_tty = notty, 92 1.14 dholland .d_poll = nopoll, 93 1.14 dholland .d_mmap = nommap, 94 1.14 dholland .d_kqfilter = nokqfilter, 95 1.15 dholland .d_discard = nodiscard, 96 1.14 dholland .d_flag = D_OTHER 97 1.1 thorpej }; 98 1.1 thorpej 99 1.1 thorpej static int pcfrtc_clock_read(struct pcfrtc_softc *, struct clock_ymdhms *, 100 1.1 thorpej uint8_t *); 101 1.1 thorpej static int pcfrtc_clock_write(struct pcfrtc_softc *, struct clock_ymdhms *, 102 1.1 thorpej uint8_t); 103 1.12 tsutsui static int pcfrtc_gettime(struct todr_chip_handle *, struct timeval *); 104 1.12 tsutsui static int pcfrtc_settime(struct todr_chip_handle *, struct timeval *); 105 1.1 thorpej 106 1.1 thorpej int 107 1.10 xtraeme pcfrtc_match(device_t parent, cfdata_t cf, void *aux) 108 1.1 thorpej { 109 1.1 thorpej struct i2c_attach_args *ia = aux; 110 1.1 thorpej 111 1.1 thorpej if ((ia->ia_addr & PCF8583_ADDRMASK) == PCF8583_ADDR) 112 1.18 thorpej return (I2C_MATCH_ADDRESS_ONLY); 113 1.1 thorpej 114 1.1 thorpej return (0); 115 1.1 thorpej } 116 1.1 thorpej 117 1.1 thorpej void 118 1.10 xtraeme pcfrtc_attach(device_t parent, device_t self, void *aux) 119 1.1 thorpej { 120 1.4 thorpej struct pcfrtc_softc *sc = device_private(self); 121 1.1 thorpej struct i2c_attach_args *ia = aux; 122 1.1 thorpej uint8_t cmdbuf[1], csr; 123 1.1 thorpej 124 1.1 thorpej sc->sc_tag = ia->ia_tag; 125 1.1 thorpej sc->sc_address = ia->ia_addr; 126 1.10 xtraeme sc->sc_dev = self; 127 1.1 thorpej 128 1.1 thorpej aprint_naive(": Real-time Clock/NVRAM\n"); 129 1.1 thorpej aprint_normal(": PCF8583 Real-time Clock/NVRAM\n"); 130 1.1 thorpej 131 1.1 thorpej cmdbuf[0] = PCF8583_REG_CSR; 132 1.1 thorpej if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address, 133 1.1 thorpej cmdbuf, 1, &csr, 1, 0) != 0) { 134 1.10 xtraeme aprint_error_dev(self, "unable to read CSR\n"); 135 1.1 thorpej return; 136 1.1 thorpej } 137 1.10 xtraeme aprint_normal_dev(sc->sc_dev, ""); 138 1.1 thorpej switch (csr & PCF8583_CSR_FN_MASK) { 139 1.1 thorpej case PCF8583_CSR_FN_32768HZ: 140 1.1 thorpej aprint_normal(" 32.768 kHz clock"); 141 1.1 thorpej break; 142 1.1 thorpej 143 1.1 thorpej case PCF8583_CSR_FN_50HZ: 144 1.1 thorpej aprint_normal(" 50 Hz clock"); 145 1.1 thorpej break; 146 1.1 thorpej 147 1.1 thorpej case PCF8583_CSR_FN_EVENT: 148 1.1 thorpej aprint_normal(" event counter"); 149 1.1 thorpej break; 150 1.1 thorpej 151 1.1 thorpej case PCF8583_CSR_FN_TEST: 152 1.1 thorpej aprint_normal(" test mode"); 153 1.1 thorpej break; 154 1.1 thorpej } 155 1.1 thorpej if (csr & PCF8583_CSR_STOP) 156 1.1 thorpej aprint_normal(", stopped"); 157 1.1 thorpej if (csr & PCF8583_CSR_ALARMENABLE) 158 1.1 thorpej aprint_normal(", alarm enabled"); 159 1.1 thorpej aprint_normal("\n"); 160 1.1 thorpej 161 1.1 thorpej sc->sc_open = 0; 162 1.1 thorpej 163 1.21 thorpej sc->sc_todr.todr_dev = self; 164 1.1 thorpej sc->sc_todr.todr_gettime = pcfrtc_gettime; 165 1.1 thorpej sc->sc_todr.todr_settime = pcfrtc_settime; 166 1.1 thorpej 167 1.1 thorpej todr_attach(&sc->sc_todr); 168 1.1 thorpej } 169 1.1 thorpej 170 1.1 thorpej /*ARGSUSED*/ 171 1.1 thorpej int 172 1.3 christos pcfrtc_open(dev_t dev, int flag, int fmt, struct lwp *l) 173 1.1 thorpej { 174 1.1 thorpej struct pcfrtc_softc *sc; 175 1.1 thorpej 176 1.11 tsutsui if ((sc = device_lookup_private(&pcfrtc_cd, minor(dev))) == NULL) 177 1.1 thorpej return (ENXIO); 178 1.1 thorpej 179 1.1 thorpej /* XXX: Locking */ 180 1.1 thorpej 181 1.1 thorpej if (sc->sc_open) 182 1.1 thorpej return (EBUSY); 183 1.1 thorpej 184 1.1 thorpej sc->sc_open = 1; 185 1.1 thorpej return (0); 186 1.1 thorpej } 187 1.1 thorpej 188 1.1 thorpej /*ARGSUSED*/ 189 1.1 thorpej int 190 1.3 christos pcfrtc_close(dev_t dev, int flag, int fmt, struct lwp *l) 191 1.1 thorpej { 192 1.1 thorpej struct pcfrtc_softc *sc; 193 1.1 thorpej 194 1.11 tsutsui if ((sc = device_lookup_private(&pcfrtc_cd, minor(dev))) == NULL) 195 1.1 thorpej return (ENXIO); 196 1.1 thorpej 197 1.1 thorpej sc->sc_open = 0; 198 1.1 thorpej return (0); 199 1.1 thorpej } 200 1.1 thorpej 201 1.1 thorpej /*ARGSUSED*/ 202 1.1 thorpej int 203 1.1 thorpej pcfrtc_read(dev_t dev, struct uio *uio, int flags) 204 1.1 thorpej { 205 1.1 thorpej struct pcfrtc_softc *sc; 206 1.1 thorpej u_int8_t ch, cmdbuf[1]; 207 1.1 thorpej int a, error; 208 1.1 thorpej 209 1.11 tsutsui if ((sc = device_lookup_private(&pcfrtc_cd, minor(dev))) == NULL) 210 1.1 thorpej return (ENXIO); 211 1.1 thorpej 212 1.1 thorpej if (uio->uio_offset >= PCF8583_NVRAM_SIZE) 213 1.1 thorpej return (EINVAL); 214 1.1 thorpej 215 1.1 thorpej if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0) 216 1.1 thorpej return (error); 217 1.1 thorpej 218 1.1 thorpej while (uio->uio_resid && uio->uio_offset < PCF8583_NVRAM_SIZE) { 219 1.1 thorpej a = (int)uio->uio_offset; 220 1.1 thorpej cmdbuf[0] = a + PCF8583_NVRAM_START; 221 1.1 thorpej if ((error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 222 1.1 thorpej sc->sc_address, cmdbuf, 1, 223 1.1 thorpej &ch, 1, 0)) != 0) { 224 1.1 thorpej iic_release_bus(sc->sc_tag, 0); 225 1.10 xtraeme aprint_error_dev(sc->sc_dev, 226 1.10 xtraeme "pcfrtc_read: read failed at 0x%x\n", a); 227 1.1 thorpej return (error); 228 1.1 thorpej } 229 1.1 thorpej if ((error = uiomove(&ch, 1, uio)) != 0) { 230 1.1 thorpej iic_release_bus(sc->sc_tag, 0); 231 1.1 thorpej return (error); 232 1.1 thorpej } 233 1.1 thorpej } 234 1.1 thorpej 235 1.1 thorpej iic_release_bus(sc->sc_tag, 0); 236 1.1 thorpej 237 1.1 thorpej return (0); 238 1.1 thorpej } 239 1.1 thorpej 240 1.1 thorpej /*ARGSUSED*/ 241 1.1 thorpej int 242 1.1 thorpej pcfrtc_write(dev_t dev, struct uio *uio, int flags) 243 1.1 thorpej { 244 1.1 thorpej struct pcfrtc_softc *sc; 245 1.1 thorpej u_int8_t cmdbuf[2]; 246 1.1 thorpej int a, error; 247 1.1 thorpej 248 1.11 tsutsui if ((sc = device_lookup_private(&pcfrtc_cd, minor(dev))) == NULL) 249 1.1 thorpej return (ENXIO); 250 1.1 thorpej 251 1.1 thorpej if (uio->uio_offset >= PCF8583_NVRAM_SIZE) 252 1.1 thorpej return (EINVAL); 253 1.1 thorpej 254 1.1 thorpej if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0) 255 1.1 thorpej return (error); 256 1.1 thorpej 257 1.1 thorpej while (uio->uio_resid && uio->uio_offset < PCF8583_NVRAM_SIZE) { 258 1.1 thorpej a = (int)uio->uio_offset; 259 1.1 thorpej cmdbuf[0] = a + PCF8583_NVRAM_START; 260 1.1 thorpej if ((error = uiomove(&cmdbuf[1], 1, uio)) != 0) 261 1.1 thorpej break; 262 1.1 thorpej 263 1.1 thorpej if ((error = iic_exec(sc->sc_tag, 264 1.1 thorpej uio->uio_resid ? I2C_OP_WRITE : I2C_OP_WRITE_WITH_STOP, 265 1.1 thorpej sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, 0)) != 0) { 266 1.10 xtraeme aprint_error_dev(sc->sc_dev, 267 1.10 xtraeme "pcfrtc_write: write failed at 0x%x\n", a); 268 1.1 thorpej return (error); 269 1.1 thorpej } 270 1.1 thorpej } 271 1.1 thorpej 272 1.1 thorpej iic_release_bus(sc->sc_tag, 0); 273 1.1 thorpej 274 1.1 thorpej return (error); 275 1.1 thorpej } 276 1.1 thorpej 277 1.1 thorpej static int 278 1.12 tsutsui pcfrtc_gettime(struct todr_chip_handle *ch, struct timeval *tv) 279 1.1 thorpej { 280 1.21 thorpej struct pcfrtc_softc *sc = device_private(ch->todr_dev); 281 1.1 thorpej struct clock_ymdhms dt; 282 1.5 bjh21 int err; 283 1.1 thorpej uint8_t centi; 284 1.1 thorpej 285 1.5 bjh21 if ((err = pcfrtc_clock_read(sc, &dt, ¢i))) 286 1.5 bjh21 return err; 287 1.1 thorpej 288 1.1 thorpej tv->tv_sec = clock_ymdhms_to_secs(&dt); 289 1.1 thorpej tv->tv_usec = centi * 10000; 290 1.1 thorpej 291 1.1 thorpej return (0); 292 1.1 thorpej } 293 1.1 thorpej 294 1.1 thorpej static int 295 1.12 tsutsui pcfrtc_settime(struct todr_chip_handle *ch, struct timeval *tv) 296 1.1 thorpej { 297 1.21 thorpej struct pcfrtc_softc *sc = device_private(ch->todr_dev); 298 1.1 thorpej struct clock_ymdhms dt; 299 1.5 bjh21 int err; 300 1.1 thorpej 301 1.1 thorpej clock_secs_to_ymdhms(tv->tv_sec, &dt); 302 1.1 thorpej 303 1.13 mbalmer if ((err = pcfrtc_clock_write(sc, &dt, tv->tv_usec / 10000)) != 0) 304 1.5 bjh21 return err; 305 1.1 thorpej 306 1.1 thorpej return (0); 307 1.1 thorpej } 308 1.1 thorpej 309 1.1 thorpej static const int pcf8583_rtc_offset[] = { 310 1.1 thorpej PCF8583_REG_CSR, 311 1.1 thorpej PCF8583_REG_CENTI, 312 1.1 thorpej PCF8583_REG_SEC, 313 1.1 thorpej PCF8583_REG_MIN, 314 1.1 thorpej PCF8583_REG_HOUR, 315 1.1 thorpej PCF8583_REG_YEARDATE, 316 1.1 thorpej PCF8583_REG_WKDYMON, 317 1.1 thorpej PCF8583_REG_TIMER, 318 1.1 thorpej 0xc0, /* NVRAM -- year stored here */ 319 1.1 thorpej 0xc1, /* NVRAM -- century stored here */ 320 1.1 thorpej }; 321 1.1 thorpej 322 1.1 thorpej static int 323 1.1 thorpej pcfrtc_clock_read(struct pcfrtc_softc *sc, struct clock_ymdhms *dt, 324 1.1 thorpej uint8_t *centi) 325 1.1 thorpej { 326 1.1 thorpej u_int8_t bcd[10], cmdbuf[1]; 327 1.5 bjh21 int i, err; 328 1.1 thorpej 329 1.19 thorpej if ((err = iic_acquire_bus(sc->sc_tag, 0))) { 330 1.10 xtraeme aprint_error_dev(sc->sc_dev, 331 1.10 xtraeme "pcfrtc_clock_read: failed to acquire I2C bus\n"); 332 1.5 bjh21 return err; 333 1.1 thorpej } 334 1.1 thorpej 335 1.1 thorpej /* Read each timekeeping register in order. */ 336 1.1 thorpej for (i = 0; i < 10; i++) { 337 1.1 thorpej cmdbuf[0] = pcf8583_rtc_offset[i]; 338 1.1 thorpej 339 1.5 bjh21 if ((err = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 340 1.1 thorpej sc->sc_address, cmdbuf, 1, 341 1.19 thorpej &bcd[i], 1, 0))) { 342 1.19 thorpej iic_release_bus(sc->sc_tag, 0); 343 1.10 xtraeme aprint_error_dev(sc->sc_dev, 344 1.10 xtraeme "pcfrtc_clock_read: failed to read rtc " 345 1.9 cegger "at 0x%x\n", 346 1.1 thorpej pcf8583_rtc_offset[i]); 347 1.5 bjh21 return err; 348 1.1 thorpej } 349 1.1 thorpej } 350 1.1 thorpej 351 1.1 thorpej /* Done with I2C */ 352 1.19 thorpej iic_release_bus(sc->sc_tag, 0); 353 1.1 thorpej 354 1.1 thorpej /* 355 1.1 thorpej * Convert the PCF8583's register values into something useable 356 1.1 thorpej */ 357 1.16 christos *centi = bcdtobin(bcd[PCF8583_REG_CENTI]); 358 1.16 christos dt->dt_sec = bcdtobin(bcd[PCF8583_REG_SEC]); 359 1.16 christos dt->dt_min = bcdtobin(bcd[PCF8583_REG_MIN]); 360 1.16 christos dt->dt_hour = bcdtobin(bcd[PCF8583_REG_HOUR] & PCF8583_HOUR_MASK); 361 1.1 thorpej if (bcd[PCF8583_REG_HOUR] & PCF8583_HOUR_12H) { 362 1.1 thorpej dt->dt_hour %= 12; /* 12AM -> 0, 12PM -> 12 */ 363 1.1 thorpej if (bcd[PCF8583_REG_HOUR] & PCF8583_HOUR_PM) 364 1.1 thorpej dt->dt_hour += 12; 365 1.1 thorpej } 366 1.1 thorpej 367 1.16 christos dt->dt_day = bcdtobin(bcd[PCF8583_REG_YEARDATE] & PCF8583_DATE_MASK); 368 1.16 christos dt->dt_mon = bcdtobin(bcd[PCF8583_REG_WKDYMON] & PCF8583_MON_MASK); 369 1.1 thorpej 370 1.1 thorpej dt->dt_year = bcd[8] + (bcd[9] * 100); 371 1.1 thorpej /* Try to notice if the year's rolled over. */ 372 1.1 thorpej if (bcd[PCF8583_REG_CSR] & PCF8583_CSR_MASK) 373 1.10 xtraeme aprint_error_dev(sc->sc_dev, 374 1.10 xtraeme "cannot check year in mask mode\n"); 375 1.1 thorpej else { 376 1.1 thorpej while (dt->dt_year % 4 != 377 1.1 thorpej (bcd[PCF8583_REG_YEARDATE] & 378 1.1 thorpej PCF8583_YEAR_MASK) >> PCF8583_YEAR_SHIFT) 379 1.1 thorpej dt->dt_year++; 380 1.1 thorpej } 381 1.1 thorpej 382 1.5 bjh21 return 0; 383 1.1 thorpej } 384 1.1 thorpej 385 1.1 thorpej static int 386 1.1 thorpej pcfrtc_clock_write(struct pcfrtc_softc *sc, struct clock_ymdhms *dt, 387 1.1 thorpej uint8_t centi) 388 1.1 thorpej { 389 1.1 thorpej uint8_t bcd[10], cmdbuf[2]; 390 1.5 bjh21 int i, err; 391 1.1 thorpej 392 1.1 thorpej /* 393 1.1 thorpej * Convert our time representation into something the PCF8583 394 1.1 thorpej * can understand. 395 1.1 thorpej */ 396 1.1 thorpej bcd[PCF8583_REG_CENTI] = centi; 397 1.16 christos bcd[PCF8583_REG_SEC] = bintobcd(dt->dt_sec); 398 1.16 christos bcd[PCF8583_REG_MIN] = bintobcd(dt->dt_min); 399 1.16 christos bcd[PCF8583_REG_HOUR] = bintobcd(dt->dt_hour) & PCF8583_HOUR_MASK; 400 1.16 christos bcd[PCF8583_REG_YEARDATE] = bintobcd(dt->dt_day) | 401 1.1 thorpej ((dt->dt_year % 4) << PCF8583_YEAR_SHIFT); 402 1.16 christos bcd[PCF8583_REG_WKDYMON] = bintobcd(dt->dt_mon) | 403 1.1 thorpej ((dt->dt_wday % 4) << PCF8583_WKDY_SHIFT); 404 1.1 thorpej bcd[8] = dt->dt_year % 100; 405 1.1 thorpej bcd[9] = dt->dt_year / 100; 406 1.1 thorpej 407 1.19 thorpej if ((err = iic_acquire_bus(sc->sc_tag, 0))) { 408 1.10 xtraeme aprint_error_dev(sc->sc_dev, 409 1.10 xtraeme "pcfrtc_clock_write: failed to acquire I2C bus\n"); 410 1.5 bjh21 return err; 411 1.1 thorpej } 412 1.1 thorpej 413 1.1 thorpej for (i = 1; i < 10; i++) { 414 1.1 thorpej cmdbuf[0] = pcf8583_rtc_offset[i]; 415 1.5 bjh21 if ((err = iic_exec(sc->sc_tag, 416 1.1 thorpej i != 9 ? I2C_OP_WRITE : I2C_OP_WRITE_WITH_STOP, 417 1.1 thorpej sc->sc_address, cmdbuf, 1, 418 1.19 thorpej &bcd[i], 1, 0))) { 419 1.19 thorpej iic_release_bus(sc->sc_tag, 0); 420 1.10 xtraeme aprint_error_dev(sc->sc_dev, 421 1.10 xtraeme "pcfrtc_clock_write: failed to write rtc " 422 1.9 cegger " at 0x%x\n", 423 1.1 thorpej pcf8583_rtc_offset[i]); 424 1.5 bjh21 return err; 425 1.1 thorpej } 426 1.1 thorpej } 427 1.1 thorpej 428 1.19 thorpej iic_release_bus(sc->sc_tag, 0); 429 1.1 thorpej 430 1.5 bjh21 return 0; 431 1.1 thorpej } 432 1.1 thorpej 433 1.1 thorpej int 434 1.1 thorpej pcfrtc_bootstrap_read(i2c_tag_t tag, int i2caddr, int offset, 435 1.1 thorpej u_int8_t *rvp, size_t len) 436 1.1 thorpej { 437 1.1 thorpej u_int8_t cmdbuf[1]; 438 1.1 thorpej 439 1.1 thorpej /* 440 1.1 thorpej * NOTE: "offset" is an absolute offset into the PCF8583 441 1.1 thorpej * address space, not relative to the NVRAM. 442 1.1 thorpej */ 443 1.1 thorpej 444 1.1 thorpej if (len == 0) 445 1.1 thorpej return (0); 446 1.1 thorpej 447 1.19 thorpej if (iic_acquire_bus(tag, 0) != 0) 448 1.1 thorpej return (-1); 449 1.1 thorpej 450 1.1 thorpej while (len) { 451 1.1 thorpej /* Read a single byte. */ 452 1.1 thorpej cmdbuf[0] = offset; 453 1.1 thorpej if (iic_exec(tag, I2C_OP_READ_WITH_STOP, i2caddr, 454 1.19 thorpej cmdbuf, 1, rvp, 1, 0)) { 455 1.19 thorpej iic_release_bus(tag, 0); 456 1.1 thorpej return (-1); 457 1.1 thorpej } 458 1.1 thorpej 459 1.1 thorpej len--; 460 1.1 thorpej rvp++; 461 1.1 thorpej offset++; 462 1.1 thorpej } 463 1.1 thorpej 464 1.19 thorpej iic_release_bus(tag, 0); 465 1.1 thorpej return (0); 466 1.1 thorpej } 467 1.1 thorpej 468 1.1 thorpej int 469 1.1 thorpej pcfrtc_bootstrap_write(i2c_tag_t tag, int i2caddr, int offset, 470 1.1 thorpej u_int8_t *rvp, size_t len) 471 1.1 thorpej { 472 1.1 thorpej u_int8_t cmdbuf[1]; 473 1.1 thorpej 474 1.1 thorpej /* 475 1.1 thorpej * NOTE: "offset" is an absolute offset into the PCF8583 476 1.1 thorpej * address space, not relative to the NVRAM. 477 1.1 thorpej */ 478 1.1 thorpej 479 1.1 thorpej if (len == 0) 480 1.1 thorpej return (0); 481 1.1 thorpej 482 1.19 thorpej if (iic_acquire_bus(tag, 0) != 0) 483 1.1 thorpej return (-1); 484 1.1 thorpej 485 1.1 thorpej while (len) { 486 1.1 thorpej /* Write a single byte. */ 487 1.1 thorpej cmdbuf[0] = offset; 488 1.1 thorpej if (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, i2caddr, 489 1.19 thorpej cmdbuf, 1, rvp, 1, 0)) { 490 1.19 thorpej iic_release_bus(tag, 0); 491 1.1 thorpej return (-1); 492 1.1 thorpej } 493 1.1 thorpej 494 1.1 thorpej len--; 495 1.1 thorpej rvp++; 496 1.1 thorpej offset++; 497 1.1 thorpej } 498 1.1 thorpej 499 1.19 thorpej iic_release_bus(tag, 0); 500 1.1 thorpej return (0); 501 1.1 thorpej } 502