1 /* $NetBSD: msm6242b.c,v 1.7 2025/09/07 21:45:16 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2012 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Radoslaw Kujawa. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: msm6242b.c,v 1.7 2025/09/07 21:45:16 thorpej Exp $"); 34 35 /* 36 * Driver for OKI MSM6242B Real Time Clock. Somewhat based on an ancient, amiga 37 * specific a2kbbc driver (which was turned into frontend to this driver). 38 */ 39 40 #include <sys/param.h> 41 #include <sys/device.h> 42 #include <sys/bus.h> 43 44 #include <dev/clock_subr.h> 45 46 #include <dev/ic/msm6242bvar.h> 47 #include <dev/ic/msm6242breg.h> 48 49 /* #define MSM6242B_DEBUG 1 */ 50 51 static int msm6242b_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *); 52 int msm6242b_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *); 53 54 bool msm6242b_hold(struct msm6242b_softc *sc); 55 void msm6242b_free(struct msm6242b_softc *sc); 56 static uint8_t msm6242b_read(struct msm6242b_softc *, uint8_t); 57 static void msm6242b_write(struct msm6242b_softc *, uint8_t, uint8_t); 58 static void msm6242b_set(struct msm6242b_softc *, uint8_t, uint8_t); 59 static void msm6242b_unset(struct msm6242b_softc *, uint8_t, uint8_t); 60 61 void 62 msm6242b_attach(struct msm6242b_softc *sc) 63 { 64 struct clock_ymdhms dt; 65 66 todr_chip_handle_t handle; 67 aprint_normal(": OKI MSM6242B\n"); 68 69 handle = &sc->sc_handle; 70 handle->todr_dev = sc->sc_dev; 71 handle->todr_gettime_ymdhms = msm6242b_gettime_ymdhms; 72 handle->todr_settime_ymdhms = msm6242b_settime_ymdhms; 73 74 if (msm6242b_gettime_ymdhms(handle, &dt) != 0) { 75 aprint_error_dev(sc->sc_dev, "RTC does not work correctly\n"); 76 return; 77 } 78 79 #ifdef MSM6242B_DEBUG 80 aprint_normal_dev(sc->sc_dev, "the time is %d %d %d %d %d %d\n", 81 dt.dt_year, dt.dt_mon, dt.dt_day, dt.dt_hour, dt.dt_min, dt.dt_sec); 82 #endif /* MSM6242B_DEBUG */ 83 todr_attach(handle); 84 } 85 86 static int 87 msm6242b_gettime_ymdhms(todr_chip_handle_t handle, struct clock_ymdhms *dt) 88 { 89 struct msm6242b_softc *sc = device_private(handle->todr_dev); 90 91 /* XXX: splsched(); */ 92 93 if(!msm6242b_hold(sc)) 94 return (ENXIO); 95 96 dt->dt_sec = msm6242b_read(sc, MSM6242B_10SECOND) * 10 + 97 msm6242b_read(sc, MSM6242B_1SECOND); 98 dt->dt_min = msm6242b_read(sc, MSM6242B_10MINUTE) * 10 + 99 msm6242b_read(sc, MSM6242B_1MINUTE); 100 dt->dt_hour = (msm6242b_read(sc, MSM6242B_10HOUR_PMAM) & 101 MSM6242B_10HOUR_MASK) * 10 + msm6242b_read(sc, MSM6242B_1HOUR); 102 dt->dt_day = msm6242b_read(sc, MSM6242B_10DAY) * 10 + 103 msm6242b_read(sc, MSM6242B_1DAY); 104 dt->dt_mon = msm6242b_read(sc, MSM6242B_10MONTH) * 10 + 105 msm6242b_read(sc, MSM6242B_1MONTH); 106 dt->dt_year = msm6242b_read(sc, MSM6242B_10YEAR) * 10 + 107 msm6242b_read(sc, MSM6242B_1YEAR); 108 dt->dt_wday = msm6242b_read(sc, MSM6242B_WEEK); 109 110 #ifdef MSM6242B_DEBUG 111 aprint_normal_dev(sc->sc_dev, "the time is %d %d %d %d %d %d\n", 112 dt->dt_year, dt->dt_mon, dt->dt_day, dt->dt_hour, dt->dt_min, dt->dt_sec); 113 #endif /* MSM6242B_DEBUG */ 114 115 /* handle 12h mode */ 116 if ((msm6242b_read(sc, MSM6242B_CONTROL_F) & 117 MSM6242B_CONTROL_F_24H) == 0) { 118 if ((msm6242b_read(sc, MSM6242B_10HOUR_PMAM) & 119 MSM6242B_PMAM_BIT) == 0 && dt->dt_hour == 12) 120 dt->dt_hour = 0; 121 else if ((msm6242b_read(sc, MSM6242B_10HOUR_PMAM) & 122 MSM6242B_PMAM_BIT) && dt->dt_hour != 12) 123 dt->dt_hour += 12; 124 } 125 126 msm6242b_free(sc); 127 128 dt->dt_year += MSM6242B_BASE_YEAR; 129 if (dt->dt_year < POSIX_BASE_YEAR) 130 dt->dt_year += 100; 131 132 if ((dt->dt_hour > 23) || 133 (dt->dt_day > 31) || 134 (dt->dt_mon > 12) || 135 (dt->dt_year > 2036)) 136 return (EINVAL); 137 138 return 0; 139 } 140 141 bool 142 msm6242b_hold(struct msm6242b_softc *sc) 143 { 144 int try; 145 146 #define TRY_MAX 10 147 for (try = 0; try < TRY_MAX; try++) { 148 msm6242b_set(sc, MSM6242B_CONTROL_D, MSM6242B_CONTROL_D_HOLD); 149 if (msm6242b_read(sc, MSM6242B_CONTROL_D) 150 & MSM6242B_CONTROL_D_BUSY) { 151 #ifdef MSM6242B_DEBUG 152 aprint_normal_dev(sc->sc_dev, "gotta idle\n"); 153 #endif /* MSM6242B_DEBUG */ 154 msm6242b_unset(sc, MSM6242B_CONTROL_D, 155 MSM6242B_CONTROL_D_HOLD); 156 delay(70); 157 } else { 158 #ifdef MSM6242B_DEBUG 159 aprint_normal_dev(sc->sc_dev, "not busy\n"); 160 #endif /* MSM6242B_DEBUG */ 161 break; 162 } 163 } 164 165 if (try == TRY_MAX) { 166 aprint_error_dev(sc->sc_dev, "can't hold the chip\n"); 167 return false; 168 } 169 return true; 170 } 171 172 void 173 msm6242b_free(struct msm6242b_softc *sc) 174 { 175 msm6242b_unset(sc, MSM6242B_CONTROL_D, MSM6242B_CONTROL_D_HOLD); 176 } 177 178 static uint8_t 179 msm6242b_read(struct msm6242b_softc *sc, uint8_t reg) 180 { 181 uint8_t r; 182 r = bus_space_read_1(sc->sc_iot, sc->sc_ioh, reg) & MSM6242B_MASK; 183 return r; 184 } 185 186 static void 187 msm6242b_write(struct msm6242b_softc *sc, uint8_t reg, uint8_t val) 188 { 189 bus_space_write_1(sc->sc_iot, sc->sc_ioh, reg, val); 190 } 191 192 static void 193 msm6242b_set(struct msm6242b_softc *sc, uint8_t reg, uint8_t bits) 194 { 195 uint8_t v; 196 v = msm6242b_read(sc, reg) | bits; 197 msm6242b_write(sc, reg, v); 198 } 199 200 static void 201 msm6242b_unset(struct msm6242b_softc *sc, uint8_t reg, uint8_t bits) 202 { 203 uint8_t v; 204 v = msm6242b_read(sc, reg) & ~bits; 205 msm6242b_write(sc, reg, v); 206 } 207 208 int 209 msm6242b_settime_ymdhms(todr_chip_handle_t handle, struct clock_ymdhms *dt) 210 { 211 struct msm6242b_softc *sc = device_private(handle->todr_dev); 212 int ampm; 213 214 /* XXX: splsched(); */ 215 216 if(!msm6242b_hold(sc)) 217 return (ENXIO); 218 219 ampm = 0; 220 if ((msm6242b_read(sc, MSM6242B_CONTROL_F) & 221 MSM6242B_CONTROL_F_24H) == 0) { 222 if (dt->dt_hour >= 12) { 223 ampm = MSM6242B_CONTROL_F_24H; 224 if (dt->dt_hour != 12) 225 dt->dt_hour -= 12; 226 } else if (dt->dt_hour == 0) { 227 dt->dt_hour = 12; 228 } 229 } 230 231 msm6242b_write(sc, MSM6242B_10HOUR_PMAM, (dt->dt_hour / 10) | ampm); 232 msm6242b_write(sc, MSM6242B_1HOUR, dt->dt_hour % 10); 233 msm6242b_write(sc, MSM6242B_10SECOND, dt->dt_sec / 10); 234 msm6242b_write(sc, MSM6242B_1SECOND, dt->dt_sec % 10); 235 msm6242b_write(sc, MSM6242B_10MINUTE, dt->dt_min / 10); 236 msm6242b_write(sc, MSM6242B_1MINUTE, dt->dt_min % 10); 237 msm6242b_write(sc, MSM6242B_10DAY, dt->dt_day / 10); 238 msm6242b_write(sc, MSM6242B_1DAY, dt->dt_day % 10); 239 msm6242b_write(sc, MSM6242B_10MONTH, dt->dt_mon / 10); 240 msm6242b_write(sc, MSM6242B_1MONTH, dt->dt_mon % 10); 241 msm6242b_write(sc, MSM6242B_10YEAR, (dt->dt_year / 10) % 10); 242 msm6242b_write(sc, MSM6242B_1YEAR, dt->dt_year % 10); 243 msm6242b_write(sc, MSM6242B_WEEK, dt->dt_wday); 244 245 msm6242b_free(sc); 246 247 return 0; 248 } 249 250