1 1.11 thorpej /* $NetBSD: rs5c313.c,v 1.11 2025/09/07 21:45:16 thorpej Exp $ */ 2 1.1 uwe 3 1.1 uwe /*- 4 1.1 uwe * Copyright (c) 2006 The NetBSD Foundation, Inc. 5 1.1 uwe * All rights reserved. 6 1.1 uwe * 7 1.1 uwe * Redistribution and use in source and binary forms, with or without 8 1.1 uwe * modification, are permitted provided that the following conditions 9 1.1 uwe * are met: 10 1.1 uwe * 1. Redistributions of source code must retain the above copyright 11 1.1 uwe * notice, this list of conditions and the following disclaimer. 12 1.1 uwe * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 uwe * notice, this list of conditions and the following disclaimer in the 14 1.1 uwe * documentation and/or other materials provided with the distribution. 15 1.1 uwe * 16 1.1 uwe * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 1.1 uwe * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 uwe * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 uwe * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 1.1 uwe * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 uwe * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 uwe * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 uwe * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 uwe * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 uwe * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.1 uwe * POSSIBILITY OF SUCH DAMAGE. 27 1.1 uwe */ 28 1.1 uwe 29 1.1 uwe #include <sys/cdefs.h> 30 1.11 thorpej __KERNEL_RCSID(0, "$NetBSD: rs5c313.c,v 1.11 2025/09/07 21:45:16 thorpej Exp $"); 31 1.1 uwe 32 1.1 uwe #include <sys/param.h> 33 1.1 uwe #include <sys/systm.h> 34 1.1 uwe #include <sys/device.h> 35 1.1 uwe #include <sys/kernel.h> 36 1.1 uwe 37 1.1 uwe #include <dev/clock_subr.h> 38 1.1 uwe 39 1.1 uwe #include <dev/ic/rs5c313reg.h> 40 1.1 uwe #include <dev/ic/rs5c313var.h> 41 1.1 uwe 42 1.1 uwe 43 1.1 uwe /* todr(9) methods */ 44 1.4 uwe static int rs5c313_todr_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *); 45 1.4 uwe static int rs5c313_todr_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *); 46 1.1 uwe 47 1.1 uwe /* sugar for chip access */ 48 1.1 uwe #define rtc_begin(sc) ((*sc->sc_ops->rs5c313_op_begin)(sc)) 49 1.1 uwe #define rtc_ce(sc, onoff) ((*sc->sc_ops->rs5c313_op_ce)(sc, onoff)) 50 1.1 uwe #define rtc_clk(sc, onoff) ((*sc->sc_ops->rs5c313_op_clk)(sc, onoff)) 51 1.1 uwe #define rtc_dir(sc, output) ((*sc->sc_ops->rs5c313_op_dir)(sc, output)) 52 1.1 uwe #define rtc_di(sc) ((*sc->sc_ops->rs5c313_op_read)(sc)) 53 1.1 uwe #define rtc_do(sc, bit) ((*sc->sc_ops->rs5c313_op_write)(sc, bit)) 54 1.1 uwe 55 1.1 uwe static int rs5c313_init(struct rs5c313_softc *); 56 1.1 uwe static int rs5c313_read_reg(struct rs5c313_softc *, int); 57 1.1 uwe static void rs5c313_write_reg(struct rs5c313_softc *, int, int); 58 1.1 uwe 59 1.1 uwe 60 1.1 uwe void 61 1.1 uwe rs5c313_attach(struct rs5c313_softc *sc) 62 1.1 uwe { 63 1.7 uwe device_t self = sc->sc_dev; 64 1.9 nonaka const char *model; 65 1.9 nonaka 66 1.9 nonaka switch (sc->sc_model) { 67 1.9 nonaka case MODEL_5C313: 68 1.9 nonaka model = "5C313"; 69 1.9 nonaka sc->sc_ctrl[0] = CTRL_24H; 70 1.9 nonaka sc->sc_ctrl[1] = CTRL2_NTEST; 71 1.9 nonaka break; 72 1.9 nonaka 73 1.9 nonaka case MODEL_5C316: 74 1.9 nonaka model = "5C316"; 75 1.9 nonaka sc->sc_ctrl[0] = 0; 76 1.9 nonaka sc->sc_ctrl[1] = CTRL2_24H|CTRL2_NTEST; 77 1.9 nonaka break; 78 1.9 nonaka 79 1.9 nonaka default: 80 1.9 nonaka aprint_error("unknown model (%d)\n", sc->sc_model); 81 1.9 nonaka return; 82 1.9 nonaka } 83 1.1 uwe 84 1.2 uwe aprint_naive("\n"); 85 1.9 nonaka aprint_normal(": RICOH %s real time clock\n", model); 86 1.1 uwe 87 1.11 thorpej sc->sc_todr.todr_dev = self; 88 1.4 uwe sc->sc_todr.todr_gettime_ymdhms = rs5c313_todr_gettime_ymdhms; 89 1.4 uwe sc->sc_todr.todr_settime_ymdhms = rs5c313_todr_settime_ymdhms; 90 1.4 uwe 91 1.1 uwe if (rs5c313_init(sc) != 0) { 92 1.6 uwe aprint_error_dev(self, "init failed\n"); 93 1.1 uwe return; 94 1.1 uwe } 95 1.1 uwe 96 1.1 uwe todr_attach(&sc->sc_todr); 97 1.1 uwe } 98 1.1 uwe 99 1.1 uwe 100 1.1 uwe static int 101 1.1 uwe rs5c313_init(struct rs5c313_softc *sc) 102 1.1 uwe { 103 1.7 uwe device_t self = sc->sc_dev; 104 1.1 uwe int status = 0; 105 1.1 uwe int retry; 106 1.1 uwe 107 1.1 uwe rtc_ce(sc, 0); 108 1.1 uwe 109 1.1 uwe rtc_begin(sc); 110 1.1 uwe rtc_ce(sc, 1); 111 1.1 uwe 112 1.1 uwe if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_XSTP) == 0) { 113 1.1 uwe sc->sc_valid = 1; 114 1.1 uwe goto done; 115 1.1 uwe } 116 1.1 uwe 117 1.1 uwe sc->sc_valid = 0; 118 1.6 uwe aprint_error_dev(self, "time not valid\n"); 119 1.1 uwe 120 1.1 uwe rs5c313_write_reg(sc, RS5C313_TINT, 0); 121 1.9 nonaka rs5c313_write_reg(sc, RS5C313_CTRL, (sc->sc_ctrl[0] | CTRL_ADJ)); 122 1.1 uwe 123 1.1 uwe for (retry = 1000; retry > 0; --retry) { 124 1.1 uwe if (rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY) 125 1.1 uwe delay(1); 126 1.1 uwe else 127 1.1 uwe break; 128 1.1 uwe } 129 1.1 uwe if (retry == 0) { 130 1.1 uwe status = EIO; 131 1.1 uwe goto done; 132 1.1 uwe } 133 1.1 uwe 134 1.9 nonaka rs5c313_write_reg(sc, RS5C313_CTRL, sc->sc_ctrl[0]); 135 1.9 nonaka rs5c313_write_reg(sc, RS5C313_CTRL2, sc->sc_ctrl[1]); 136 1.1 uwe 137 1.1 uwe done: 138 1.1 uwe rtc_ce(sc, 0); 139 1.1 uwe return status; 140 1.1 uwe } 141 1.1 uwe 142 1.1 uwe 143 1.1 uwe static int 144 1.4 uwe rs5c313_todr_gettime_ymdhms(todr_chip_handle_t todr, struct clock_ymdhms *dt) 145 1.1 uwe { 146 1.11 thorpej struct rs5c313_softc *sc = device_private(todr->todr_dev); 147 1.1 uwe int retry; 148 1.1 uwe int s; 149 1.1 uwe 150 1.1 uwe /* 151 1.1 uwe * If chip had invalid data on init, don't bother reading 152 1.1 uwe * bogus values, let todr(9) cope. 153 1.1 uwe */ 154 1.1 uwe if (sc->sc_valid == 0) 155 1.1 uwe return EIO; 156 1.1 uwe 157 1.1 uwe s = splhigh(); 158 1.1 uwe 159 1.1 uwe rtc_begin(sc); 160 1.1 uwe for (retry = 10; retry > 0; --retry) { 161 1.1 uwe rtc_ce(sc, 1); 162 1.1 uwe 163 1.9 nonaka rs5c313_write_reg(sc, RS5C313_CTRL, sc->sc_ctrl[0]); 164 1.1 uwe if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY) == 0) 165 1.1 uwe break; 166 1.1 uwe 167 1.1 uwe rtc_ce(sc, 0); 168 1.1 uwe delay(1); 169 1.1 uwe } 170 1.1 uwe if (retry == 0) { 171 1.1 uwe splx(s); 172 1.1 uwe return EIO; 173 1.1 uwe } 174 1.1 uwe 175 1.1 uwe #define RTCGET(x, y) \ 176 1.1 uwe do { \ 177 1.1 uwe int ones = rs5c313_read_reg(sc, RS5C313_ ## y ## 1); \ 178 1.1 uwe int tens = rs5c313_read_reg(sc, RS5C313_ ## y ## 10); \ 179 1.4 uwe dt->dt_ ## x = tens * 10 + ones; \ 180 1.1 uwe } while (/* CONSTCOND */0) 181 1.1 uwe 182 1.1 uwe RTCGET(sec, SEC); 183 1.1 uwe RTCGET(min, MIN); 184 1.1 uwe RTCGET(hour, HOUR); 185 1.1 uwe RTCGET(day, DAY); 186 1.1 uwe RTCGET(mon, MON); 187 1.1 uwe RTCGET(year, YEAR); 188 1.1 uwe #undef RTCGET 189 1.4 uwe dt->dt_wday = rs5c313_read_reg(sc, RS5C313_WDAY); 190 1.1 uwe 191 1.1 uwe rtc_ce(sc, 0); 192 1.1 uwe splx(s); 193 1.1 uwe 194 1.4 uwe dt->dt_year = (dt->dt_year % 100) + 1900; 195 1.4 uwe if (dt->dt_year < POSIX_BASE_YEAR) { 196 1.4 uwe dt->dt_year += 100; 197 1.1 uwe } 198 1.1 uwe 199 1.1 uwe return 0; 200 1.1 uwe } 201 1.1 uwe 202 1.1 uwe 203 1.1 uwe static int 204 1.4 uwe rs5c313_todr_settime_ymdhms(todr_chip_handle_t todr, struct clock_ymdhms *dt) 205 1.1 uwe { 206 1.11 thorpej struct rs5c313_softc *sc = device_private(todr->todr_dev); 207 1.1 uwe int retry; 208 1.1 uwe int t; 209 1.1 uwe int s; 210 1.1 uwe 211 1.1 uwe s = splhigh(); 212 1.1 uwe 213 1.1 uwe rtc_begin(sc); 214 1.1 uwe for (retry = 10; retry > 0; --retry) { 215 1.1 uwe rtc_ce(sc, 1); 216 1.1 uwe 217 1.9 nonaka rs5c313_write_reg(sc, RS5C313_CTRL, sc->sc_ctrl[0]); 218 1.1 uwe if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY) == 0) 219 1.1 uwe break; 220 1.1 uwe 221 1.1 uwe rtc_ce(sc, 0); 222 1.1 uwe delay(1); 223 1.1 uwe } 224 1.1 uwe 225 1.1 uwe if (retry == 0) { 226 1.1 uwe splx(s); 227 1.1 uwe return EIO; 228 1.1 uwe } 229 1.1 uwe 230 1.1 uwe #define RTCSET(x, y) \ 231 1.1 uwe do { \ 232 1.10 christos t = bintobcd(dt->dt_ ## y) & 0xff; \ 233 1.1 uwe rs5c313_write_reg(sc, RS5C313_ ## x ## 1, t & 0x0f); \ 234 1.1 uwe rs5c313_write_reg(sc, RS5C313_ ## x ## 10, (t >> 4) & 0x0f); \ 235 1.1 uwe } while (/* CONSTCOND */0) 236 1.1 uwe 237 1.1 uwe RTCSET(SEC, sec); 238 1.1 uwe RTCSET(MIN, min); 239 1.1 uwe RTCSET(HOUR, hour); 240 1.1 uwe RTCSET(DAY, day); 241 1.1 uwe RTCSET(MON, mon); 242 1.1 uwe 243 1.1 uwe #undef RTCSET 244 1.1 uwe 245 1.4 uwe t = dt->dt_year % 100; 246 1.10 christos t = bintobcd(t); 247 1.1 uwe rs5c313_write_reg(sc, RS5C313_YEAR1, t & 0x0f); 248 1.1 uwe rs5c313_write_reg(sc, RS5C313_YEAR10, (t >> 4) & 0x0f); 249 1.1 uwe 250 1.4 uwe rs5c313_write_reg(sc, RS5C313_WDAY, dt->dt_wday); 251 1.1 uwe 252 1.1 uwe rtc_ce(sc, 0); 253 1.1 uwe splx(s); 254 1.1 uwe 255 1.1 uwe sc->sc_valid = 1; 256 1.1 uwe return 0; 257 1.1 uwe } 258 1.1 uwe 259 1.1 uwe 260 1.1 uwe static int 261 1.1 uwe rs5c313_read_reg(struct rs5c313_softc *sc, int addr) 262 1.1 uwe { 263 1.1 uwe int data; 264 1.1 uwe 265 1.1 uwe /* output */ 266 1.1 uwe rtc_dir(sc, 1); 267 1.1 uwe 268 1.1 uwe /* control */ 269 1.1 uwe rtc_do(sc, 1); /* ignored */ 270 1.1 uwe rtc_do(sc, 1); /* R/#W = 1(READ) */ 271 1.1 uwe rtc_do(sc, 1); /* AD = 1 */ 272 1.1 uwe rtc_do(sc, 0); /* DT = 0 */ 273 1.1 uwe 274 1.1 uwe /* address */ 275 1.1 uwe rtc_do(sc, addr & 0x8); /* A3 */ 276 1.1 uwe rtc_do(sc, addr & 0x4); /* A2 */ 277 1.1 uwe rtc_do(sc, addr & 0x2); /* A1 */ 278 1.1 uwe rtc_do(sc, addr & 0x1); /* A0 */ 279 1.1 uwe 280 1.1 uwe /* input */ 281 1.1 uwe rtc_dir(sc, 0); 282 1.1 uwe 283 1.1 uwe /* ignore */ 284 1.1 uwe (void)rtc_di(sc); 285 1.1 uwe (void)rtc_di(sc); 286 1.1 uwe (void)rtc_di(sc); 287 1.1 uwe (void)rtc_di(sc); 288 1.1 uwe 289 1.1 uwe /* data */ 290 1.1 uwe data = rtc_di(sc); /* D3 */ 291 1.1 uwe data <<= 1; 292 1.1 uwe data |= rtc_di(sc); /* D2 */ 293 1.1 uwe data <<= 1; 294 1.1 uwe data |= rtc_di(sc); /* D1 */ 295 1.1 uwe data <<= 1; 296 1.1 uwe data |= rtc_di(sc); /* D0 */ 297 1.1 uwe 298 1.1 uwe return data; 299 1.1 uwe } 300 1.1 uwe 301 1.1 uwe 302 1.1 uwe static void 303 1.1 uwe rs5c313_write_reg(struct rs5c313_softc *sc, int addr, int data) 304 1.1 uwe { 305 1.1 uwe 306 1.1 uwe /* output */ 307 1.1 uwe rtc_dir(sc, 1); 308 1.1 uwe 309 1.1 uwe /* control */ 310 1.1 uwe rtc_do(sc, 1); /* ignored */ 311 1.1 uwe rtc_do(sc, 0); /* R/#W = 0 (WRITE) */ 312 1.1 uwe rtc_do(sc, 1); /* AD = 1 */ 313 1.1 uwe rtc_do(sc, 0); /* DT = 0 */ 314 1.1 uwe 315 1.1 uwe /* address */ 316 1.1 uwe rtc_do(sc, addr & 0x8); /* A3 */ 317 1.1 uwe rtc_do(sc, addr & 0x4); /* A2 */ 318 1.1 uwe rtc_do(sc, addr & 0x2); /* A1 */ 319 1.1 uwe rtc_do(sc, addr & 0x1); /* A0 */ 320 1.1 uwe 321 1.1 uwe /* control */ 322 1.1 uwe rtc_do(sc, 1); /* ignored */ 323 1.1 uwe rtc_do(sc, 0); /* R/#W = 0(WRITE) */ 324 1.1 uwe rtc_do(sc, 0); /* AD = 0 */ 325 1.1 uwe rtc_do(sc, 1); /* DT = 1 */ 326 1.1 uwe 327 1.1 uwe /* data */ 328 1.1 uwe rtc_do(sc, data & 0x8); /* D3 */ 329 1.1 uwe rtc_do(sc, data & 0x4); /* D2 */ 330 1.1 uwe rtc_do(sc, data & 0x2); /* D1 */ 331 1.1 uwe rtc_do(sc, data & 0x1); /* D0 */ 332 1.1 uwe } 333