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