1 /* $NetBSD: rs5c372.c,v 1.10 2009/12/12 14:44:10 tsutsui Exp $ */ 2 3 /* 4 * Copyright (c) 2005 Kimihiro Nonaka 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: rs5c372.c,v 1.10 2009/12/12 14:44:10 tsutsui Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/device.h> 35 #include <sys/kernel.h> 36 #include <sys/fcntl.h> 37 #include <sys/uio.h> 38 #include <sys/conf.h> 39 #include <sys/event.h> 40 41 #include <dev/clock_subr.h> 42 43 #include <dev/i2c/i2cvar.h> 44 #include <dev/i2c/rs5c372reg.h> 45 46 struct rs5c372rtc_softc { 47 device_t sc_dev; 48 i2c_tag_t sc_tag; 49 int sc_address; 50 struct todr_chip_handle sc_todr; 51 }; 52 53 static int rs5c372rtc_match(device_t, cfdata_t, void *); 54 static void rs5c372rtc_attach(device_t, device_t, void *); 55 56 CFATTACH_DECL_NEW(rs5c372rtc, sizeof(struct rs5c372rtc_softc), 57 rs5c372rtc_match, rs5c372rtc_attach, NULL, NULL); 58 59 static void rs5c372rtc_reg_write(struct rs5c372rtc_softc *, int, uint8_t); 60 static int rs5c372rtc_clock_read(struct rs5c372rtc_softc *, struct clock_ymdhms *); 61 static int rs5c372rtc_clock_write(struct rs5c372rtc_softc *, struct clock_ymdhms *); 62 static int rs5c372rtc_gettime(struct todr_chip_handle *, struct timeval *); 63 static int rs5c372rtc_settime(struct todr_chip_handle *, struct timeval *); 64 65 static int 66 rs5c372rtc_match(device_t parent, cfdata_t cf, void *arg) 67 { 68 struct i2c_attach_args *ia = arg; 69 70 if (ia->ia_addr == RS5C372_ADDR) 71 return (1); 72 return (0); 73 } 74 75 static void 76 rs5c372rtc_attach(device_t parent, device_t self, void *arg) 77 { 78 struct rs5c372rtc_softc *sc = device_private(self); 79 struct i2c_attach_args *ia = arg; 80 81 aprint_naive(": Real-time Clock\n"); 82 aprint_normal(": RICOH RS5C372[AB] Real-time Clock\n"); 83 84 sc->sc_tag = ia->ia_tag; 85 sc->sc_address = ia->ia_addr; 86 sc->sc_dev = self; 87 sc->sc_todr.cookie = sc; 88 sc->sc_todr.todr_gettime = rs5c372rtc_gettime; 89 sc->sc_todr.todr_settime = rs5c372rtc_settime; 90 sc->sc_todr.todr_setwen = NULL; 91 92 todr_attach(&sc->sc_todr); 93 94 /* Initialize RTC */ 95 rs5c372rtc_reg_write(sc, RS5C372_CONTROL2, RS5C372_CONTROL2_24HRS); 96 rs5c372rtc_reg_write(sc, RS5C372_CONTROL1, 0); 97 } 98 99 static int 100 rs5c372rtc_gettime(struct todr_chip_handle *ch, struct timeval *tv) 101 { 102 struct rs5c372rtc_softc *sc = ch->cookie; 103 struct clock_ymdhms dt; 104 105 memset(&dt, 0, sizeof(dt)); 106 107 if (rs5c372rtc_clock_read(sc, &dt) == 0) 108 return (-1); 109 110 tv->tv_sec = clock_ymdhms_to_secs(&dt); 111 tv->tv_usec = 0; 112 113 return (0); 114 } 115 116 static int 117 rs5c372rtc_settime(struct todr_chip_handle *ch, struct timeval *tv) 118 { 119 struct rs5c372rtc_softc *sc = ch->cookie; 120 struct clock_ymdhms dt; 121 122 clock_secs_to_ymdhms(tv->tv_sec, &dt); 123 124 if (rs5c372rtc_clock_write(sc, &dt) == 0) 125 return (-1); 126 127 return (0); 128 } 129 130 static void 131 rs5c372rtc_reg_write(struct rs5c372rtc_softc *sc, int reg, uint8_t val) 132 { 133 uint8_t cmdbuf[2]; 134 135 if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) { 136 aprint_error_dev(sc->sc_dev, 137 "rs5c372rtc_reg_write: failed to acquire I2C bus\n"); 138 return; 139 } 140 141 reg &= 0xf; 142 cmdbuf[0] = (reg << 4); 143 cmdbuf[1] = val; 144 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address, 145 cmdbuf, 1, &cmdbuf[1], 1, I2C_F_POLL)) { 146 iic_release_bus(sc->sc_tag, I2C_F_POLL); 147 aprint_error_dev(sc->sc_dev, 148 "rs5c372rtc_reg_write: failed to write reg%d\n", reg); 149 return; 150 } 151 152 iic_release_bus(sc->sc_tag, I2C_F_POLL); 153 } 154 155 static int 156 rs5c372rtc_clock_read(struct rs5c372rtc_softc *sc, struct clock_ymdhms *dt) 157 { 158 uint8_t bcd[RS5C372_NRTC_REGS]; 159 uint8_t cmdbuf[1]; 160 161 if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) { 162 aprint_error_dev(sc->sc_dev, 163 "rs5c372rtc_clock_read: failed to acquire I2C bus\n"); 164 return (0); 165 } 166 167 cmdbuf[0] = (RS5C372_SECONDS << 4); 168 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address, 169 cmdbuf, 1, bcd, RS5C372_NRTC_REGS, I2C_F_POLL)) { 170 iic_release_bus(sc->sc_tag, I2C_F_POLL); 171 aprint_error_dev(sc->sc_dev, 172 "rs5c372rtc_clock_read: failed to read rtc\n"); 173 return (0); 174 } 175 176 iic_release_bus(sc->sc_tag, I2C_F_POLL); 177 178 /* 179 * Convert the RS5C372's register values into something useable 180 */ 181 dt->dt_sec = FROMBCD(bcd[RS5C372_SECONDS] & RS5C372_SECONDS_MASK); 182 dt->dt_min = FROMBCD(bcd[RS5C372_MINUTES] & RS5C372_MINUTES_MASK); 183 dt->dt_hour = FROMBCD(bcd[RS5C372_HOURS] & RS5C372_HOURS_24MASK); 184 dt->dt_day = FROMBCD(bcd[RS5C372_DATE] & RS5C372_DATE_MASK); 185 dt->dt_mon = FROMBCD(bcd[RS5C372_MONTH] & RS5C372_MONTH_MASK); 186 dt->dt_year = FROMBCD(bcd[RS5C372_YEAR]) + 2000; 187 188 return (1); 189 } 190 191 static int 192 rs5c372rtc_clock_write(struct rs5c372rtc_softc *sc, struct clock_ymdhms *dt) 193 { 194 uint8_t bcd[RS5C372_NRTC_REGS]; 195 uint8_t cmdbuf[1]; 196 197 /* 198 * Convert our time representation into something the RS5C372 199 * can understand. 200 */ 201 bcd[RS5C372_SECONDS] = TOBCD(dt->dt_sec); 202 bcd[RS5C372_MINUTES] = TOBCD(dt->dt_min); 203 bcd[RS5C372_HOURS] = TOBCD(dt->dt_hour); 204 bcd[RS5C372_DATE] = TOBCD(dt->dt_day); 205 bcd[RS5C372_DAY] = TOBCD(dt->dt_wday); 206 bcd[RS5C372_MONTH] = TOBCD(dt->dt_mon); 207 bcd[RS5C372_YEAR] = TOBCD(dt->dt_year % 100); 208 209 if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) { 210 aprint_error_dev(sc->sc_dev, "rs5c372rtc_clock_write: failed to " 211 "acquire I2C bus\n"); 212 return (0); 213 } 214 215 cmdbuf[0] = (RS5C372_SECONDS << 4); 216 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address, 217 cmdbuf, 1, bcd, RS5C372_NRTC_REGS, I2C_F_POLL)) { 218 iic_release_bus(sc->sc_tag, I2C_F_POLL); 219 aprint_error_dev(sc->sc_dev, 220 "rs5c372rtc_clock_write: failed to write rtc\n"); 221 return (0); 222 } 223 224 iic_release_bus(sc->sc_tag, I2C_F_POLL); 225 226 return (1); 227 } 228