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