1 /* $NetBSD: m41t00.c,v 1.5 2005/12/11 12:21:22 christos 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 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/device.h> 41 #include <sys/kernel.h> 42 #include <sys/fcntl.h> 43 #include <sys/uio.h> 44 #include <sys/conf.h> 45 #include <sys/proc.h> 46 #include <sys/event.h> 47 48 #include <machine/bus.h> 49 50 #include <dev/clock_subr.h> 51 52 #include <dev/i2c/i2cvar.h> 53 #include <dev/i2c/m41t00reg.h> 54 55 struct m41t00_softc { 56 struct device sc_dev; 57 i2c_tag_t sc_tag; 58 int sc_address; 59 int sc_open; 60 struct todr_chip_handle sc_todr; 61 }; 62 63 static int m41t00_match(struct device *, struct cfdata *, void *); 64 static void m41t00_attach(struct device *, struct device *, void *); 65 66 CFATTACH_DECL(m41trtc, sizeof(struct m41t00_softc), 67 m41t00_match, m41t00_attach, NULL, NULL); 68 extern struct cfdriver m41trtc_cd; 69 70 dev_type_open(m41t00_open); 71 dev_type_close(m41t00_close); 72 dev_type_read(m41t00_read); 73 dev_type_write(m41t00_write); 74 75 const struct cdevsw m41t00_cdevsw = { 76 m41t00_open, m41t00_close, m41t00_read, m41t00_write, noioctl, 77 nostop, notty, nopoll, nommap, nokqfilter, 78 }; 79 80 static int m41t00_clock_read(struct m41t00_softc *, struct clock_ymdhms *); 81 static int m41t00_clock_write(struct m41t00_softc *, struct clock_ymdhms *); 82 static int m41t00_gettime(struct todr_chip_handle *, volatile struct timeval *); 83 static int m41t00_settime(struct todr_chip_handle *, volatile struct timeval *); 84 static int m41t00_getcal(struct todr_chip_handle *, int *); 85 static int m41t00_setcal(struct todr_chip_handle *, int); 86 87 int 88 m41t00_match(struct device *parent, struct cfdata *cf, void *aux) 89 { 90 struct i2c_attach_args *ia = aux; 91 92 if (ia->ia_addr == M41T00_ADDR) { 93 return 1; 94 } 95 96 return 0; 97 } 98 99 void 100 m41t00_attach(struct device *parent, struct device *self, void *aux) 101 { 102 struct m41t00_softc *sc = (struct m41t00_softc *)self; 103 struct i2c_attach_args *ia = aux; 104 105 sc->sc_tag = ia->ia_tag; 106 sc->sc_address = ia->ia_addr; 107 108 aprint_naive(": Real-time Clock\n"); 109 aprint_normal(": M41T00 Real-time Clock\n"); 110 111 sc->sc_open = 0; 112 sc->sc_todr.cookie = sc; 113 sc->sc_todr.todr_gettime = m41t00_gettime; 114 sc->sc_todr.todr_settime = m41t00_settime; 115 sc->sc_todr.todr_getcal = m41t00_getcal; 116 sc->sc_todr.todr_setcal = m41t00_setcal; 117 sc->sc_todr.todr_setwen = NULL; 118 119 todr_attach(&sc->sc_todr); 120 } 121 122 /*ARGSUSED*/ 123 int 124 m41t00_open(dev_t dev, int flag, int fmt, struct lwp *l) 125 { 126 struct m41t00_softc *sc; 127 128 if ((sc = device_lookup(&m41trtc_cd, minor(dev))) == NULL) 129 return ENXIO; 130 131 /* XXX: Locking */ 132 133 if (sc->sc_open) 134 return EBUSY; 135 136 sc->sc_open = 1; 137 return 0; 138 } 139 140 /*ARGSUSED*/ 141 int 142 m41t00_close(dev_t dev, int flag, int fmt, struct lwp *l) 143 { 144 struct m41t00_softc *sc; 145 146 if ((sc = device_lookup(&m41trtc_cd, minor(dev))) == NULL) 147 return ENXIO; 148 149 sc->sc_open = 0; 150 return 0; 151 } 152 153 /*ARGSUSED*/ 154 int 155 m41t00_read(dev_t dev, struct uio *uio, int flags) 156 { 157 struct m41t00_softc *sc; 158 u_int8_t ch, cmdbuf[1]; 159 int a, error; 160 161 if ((sc = device_lookup(&m41trtc_cd, minor(dev))) == NULL) 162 return (ENXIO); 163 164 if (uio->uio_offset >= M41T00_NBYTES) 165 return (EINVAL); 166 167 if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0) 168 return (error); 169 170 while (uio->uio_resid && uio->uio_offset < M41T00_NBYTES) { 171 a = (int)uio->uio_offset; 172 cmdbuf[0] = a; 173 if ((error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 174 sc->sc_address, cmdbuf, 1, 175 &ch, 1, 0)) != 0) { 176 iic_release_bus(sc->sc_tag, 0); 177 printf("%s: m41t00_read: read failed at 0x%x\n", 178 sc->sc_dev.dv_xname, a); 179 return (error); 180 } 181 if ((error = uiomove(&ch, 1, uio)) != 0) { 182 iic_release_bus(sc->sc_tag, 0); 183 return (error); 184 } 185 } 186 187 iic_release_bus(sc->sc_tag, 0); 188 189 return (0); 190 } 191 192 /*ARGSUSED*/ 193 int 194 m41t00_write(dev_t dev, struct uio *uio, int flags) 195 { 196 struct m41t00_softc *sc; 197 u_int8_t cmdbuf[2]; 198 int a, error; 199 200 if ((sc = device_lookup(&m41trtc_cd, minor(dev))) == NULL) 201 return (ENXIO); 202 203 if (uio->uio_offset >= M41T00_NBYTES) 204 return (EINVAL); 205 206 if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0) 207 return (error); 208 209 while (uio->uio_resid && uio->uio_offset < M41T00_NBYTES) { 210 a = (int)uio->uio_offset; 211 212 cmdbuf[0] = a; 213 if ((error = uiomove(&cmdbuf[1], 1, uio)) != 0) 214 break; 215 216 if ((error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 217 sc->sc_address, 218 cmdbuf, 1, &cmdbuf[1], 1, 0)) != 0) { 219 printf("%s: m41t00_write: write failed at 0x%x\n", 220 sc->sc_dev.dv_xname, a); 221 break; 222 } 223 } 224 225 iic_release_bus(sc->sc_tag, 0); 226 227 return (error); 228 } 229 230 static int 231 m41t00_gettime(struct todr_chip_handle *ch, volatile struct timeval *tv) 232 { 233 struct m41t00_softc *sc = ch->cookie; 234 struct clock_ymdhms dt; 235 236 if (m41t00_clock_read(sc, &dt) == 0) 237 return (-1); 238 239 tv->tv_sec = clock_ymdhms_to_secs(&dt); 240 tv->tv_usec = 0; 241 242 return (0); 243 } 244 245 static int 246 m41t00_settime(struct todr_chip_handle *ch, volatile struct timeval *tv) 247 { 248 struct m41t00_softc *sc = ch->cookie; 249 struct clock_ymdhms dt; 250 251 clock_secs_to_ymdhms(tv->tv_sec, &dt); 252 253 if (m41t00_clock_write(sc, &dt) == 0) 254 return (-1); 255 256 return (0); 257 } 258 259 static int 260 m41t00_setcal(struct todr_chip_handle *ch, int cal) 261 { 262 263 return (EOPNOTSUPP); 264 } 265 266 static int 267 m41t00_getcal(struct todr_chip_handle *ch, int *cal) 268 { 269 270 return (EOPNOTSUPP); 271 } 272 273 static int m41t00_rtc_offset[] = { 274 M41T00_SEC, 275 M41T00_MIN, 276 M41T00_CENHR, 277 M41T00_DAY, 278 M41T00_DATE, 279 M41T00_MONTH, 280 M41T00_YEAR, 281 }; 282 283 static int 284 m41t00_clock_read(struct m41t00_softc *sc, struct clock_ymdhms *dt) 285 { 286 u_int8_t bcd[M41T00_NBYTES], cmdbuf[1]; 287 int i, n; 288 289 if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) { 290 printf("%s: m41t00_clock_read: failed to acquire I2C bus\n", 291 sc->sc_dev.dv_xname); 292 return (0); 293 } 294 295 /* Read each timekeeping register in order. */ 296 n = sizeof(m41t00_rtc_offset) / sizeof(m41t00_rtc_offset[0]); 297 for (i = 0; i < n ; i++) { 298 cmdbuf[0] = m41t00_rtc_offset[i]; 299 300 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 301 sc->sc_address, cmdbuf, 1, 302 &bcd[i], 1, I2C_F_POLL)) { 303 iic_release_bus(sc->sc_tag, I2C_F_POLL); 304 printf("%s: m41t00_clock_read: failed to read rtc " 305 "at 0x%x\n", sc->sc_dev.dv_xname, 306 m41t00_rtc_offset[i]); 307 return (0); 308 } 309 } 310 311 /* Done with I2C */ 312 iic_release_bus(sc->sc_tag, I2C_F_POLL); 313 314 /* 315 * Convert the M41T00's register values into something useable 316 */ 317 dt->dt_sec = FROMBCD(bcd[M41T00_SEC] & M41T00_SEC_MASK); 318 dt->dt_min = FROMBCD(bcd[M41T00_MIN] & M41T00_MIN_MASK); 319 dt->dt_hour = FROMBCD(bcd[M41T00_CENHR] & M41T00_HOUR_MASK); 320 dt->dt_day = FROMBCD(bcd[M41T00_DATE] & M41T00_DATE_MASK); 321 dt->dt_wday = FROMBCD(bcd[M41T00_DAY] & M41T00_DAY_MASK); 322 dt->dt_mon = FROMBCD(bcd[M41T00_MONTH] & M41T00_MONTH_MASK); 323 dt->dt_year = FROMBCD(bcd[M41T00_YEAR] & M41T00_YEAR_MASK); 324 325 /* 326 * Since the m41t00 just stores 00-99, and this is 2003 as I write 327 * this comment, use 2000 as a base year 328 */ 329 dt->dt_year += 2000; 330 331 return (1); 332 } 333 334 static int 335 m41t00_clock_write(struct m41t00_softc *sc, struct clock_ymdhms *dt) 336 { 337 uint8_t bcd[M41T00_DATE_BYTES], cmdbuf[2]; 338 uint8_t init_seconds, final_seconds; 339 int i; 340 341 /* 342 * Convert our time representation into something the MAX6900 343 * can understand. 344 */ 345 bcd[M41T00_SEC] = TOBCD(dt->dt_sec); 346 bcd[M41T00_MIN] = TOBCD(dt->dt_min); 347 bcd[M41T00_CENHR] = TOBCD(dt->dt_hour); 348 bcd[M41T00_DATE] = TOBCD(dt->dt_day); 349 bcd[M41T00_DAY] = TOBCD(dt->dt_wday); 350 bcd[M41T00_MONTH] = TOBCD(dt->dt_mon); 351 bcd[M41T00_YEAR] = TOBCD(dt->dt_year % 100); 352 353 if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) { 354 printf("%s: m41t00_clock_write: failed to acquire I2C bus\n", 355 sc->sc_dev.dv_xname); 356 return (0); 357 } 358 359 /* 360 * The MAX6900 RTC manual recommends ensuring "atomicity" of 361 * a non-burst write by: 362 * 363 * - writing SECONDS 364 * - reading back SECONDS, remembering it as "initial seconds" 365 * - write the remaing RTC registers 366 * - read back SECONDS as "final seconds" 367 * - if "initial seconds" == 59, ensure "final seconds" == 59 368 * - else, ensure "final seconds" is no more than one second 369 * beyond "initial seconds". 370 * 371 * This sounds reasonable for the M41T00, too. 372 */ 373 again: 374 cmdbuf[0] = M41T00_SEC; 375 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address, 376 cmdbuf, 1, &bcd[M41T00_SEC], 1, I2C_F_POLL)) { 377 iic_release_bus(sc->sc_tag, I2C_F_POLL); 378 printf("%s: m41t00_clock_write: failed to write SECONDS\n", 379 sc->sc_dev.dv_xname); 380 return (0); 381 } 382 383 cmdbuf[0] = M41T00_SEC; 384 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address, 385 cmdbuf, 1, &init_seconds, 1, I2C_F_POLL)) { 386 iic_release_bus(sc->sc_tag, I2C_F_POLL); 387 printf("%s: m41t00_clock_write: failed to read " 388 "INITIAL SECONDS\n", sc->sc_dev.dv_xname); 389 return (0); 390 } 391 init_seconds = FROMBCD(init_seconds & M41T00_SEC_MASK); 392 393 for (i = 1; i < M41T00_DATE_BYTES; i++) { 394 cmdbuf[0] = m41t00_rtc_offset[i]; 395 if (iic_exec(sc->sc_tag, 396 I2C_OP_WRITE_WITH_STOP, sc->sc_address, 397 cmdbuf, 1, &bcd[i], 1, I2C_F_POLL)) { 398 iic_release_bus(sc->sc_tag, I2C_F_POLL); 399 printf("%s: m41t00_clock_write: failed to write rtc " 400 " at 0x%x\n", sc->sc_dev.dv_xname, 401 m41t00_rtc_offset[i]); 402 return (0); 403 } 404 } 405 406 cmdbuf[0] = M41T00_SEC; 407 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address, 408 cmdbuf, 1, &final_seconds, 1, I2C_F_POLL)) { 409 iic_release_bus(sc->sc_tag, I2C_F_POLL); 410 printf("%s: m41t00_clock_write: failed to read " 411 "FINAL SECONDS\n", sc->sc_dev.dv_xname); 412 return (0); 413 } 414 final_seconds = FROMBCD(final_seconds & M41T00_SEC_MASK); 415 416 if ((init_seconds != final_seconds) && 417 (((init_seconds + 1) % 60) != final_seconds)) { 418 #if 1 419 printf("%s: m41t00_clock_write: init %d, final %d, try again\n", 420 sc->sc_dev.dv_xname, init_seconds, final_seconds); 421 #endif 422 goto again; 423 } 424 425 iic_release_bus(sc->sc_tag, I2C_F_POLL); 426 427 return (1); 428 } 429