1 1.19 thorpej /* $NetBSD: mm58167.c,v 1.19 2025/09/07 21:45:16 thorpej Exp $ */ 2 1.1 fredette 3 1.1 fredette /* 4 1.1 fredette * Copyright (c) 2001 The NetBSD Foundation, Inc. 5 1.1 fredette * All rights reserved. 6 1.1 fredette * 7 1.1 fredette * This code is derived from software contributed to The NetBSD Foundation 8 1.1 fredette * by Matthew Fredette. 9 1.1 fredette * 10 1.1 fredette * Redistribution and use in source and binary forms, with or without 11 1.1 fredette * modification, are permitted provided that the following conditions 12 1.1 fredette * are met: 13 1.1 fredette * 1. Redistributions of source code must retain the above copyright 14 1.1 fredette * notice, this list of conditions and the following disclaimer. 15 1.1 fredette * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 fredette * notice, this list of conditions and the following disclaimer in the 17 1.1 fredette * documentation and/or other materials provided with the distribution. 18 1.1 fredette * 19 1.1 fredette * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 fredette * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 fredette * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 fredette * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 fredette * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 fredette * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 fredette * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 fredette * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 fredette * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 fredette * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 fredette * POSSIBILITY OF SUCH DAMAGE. 30 1.1 fredette */ 31 1.1 fredette 32 1.1 fredette /* 33 1.1 fredette * National Semiconductor MM58167 time-of-day chip subroutines. 34 1.1 fredette */ 35 1.3 lukem 36 1.3 lukem #include <sys/cdefs.h> 37 1.19 thorpej __KERNEL_RCSID(0, "$NetBSD: mm58167.c,v 1.19 2025/09/07 21:45:16 thorpej Exp $"); 38 1.1 fredette 39 1.1 fredette #include <sys/param.h> 40 1.1 fredette #include <sys/systm.h> 41 1.1 fredette #include <sys/errno.h> 42 1.1 fredette #include <sys/device.h> 43 1.1 fredette 44 1.9 ad #include <sys/bus.h> 45 1.1 fredette #include <dev/clock_subr.h> 46 1.1 fredette #include <dev/ic/mm58167var.h> 47 1.1 fredette 48 1.14 tsutsui static int mm58167_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *); 49 1.14 tsutsui static int mm58167_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *); 50 1.1 fredette 51 1.1 fredette /* 52 1.1 fredette * To quote SunOS's todreg.h: 53 1.1 fredette * "This brain damaged chip insists on keeping the time in 54 1.1 fredette * MM/DD HH:MM:SS format, even though it doesn't know about 55 1.1 fredette * leap years and Feb. 29, thus making it nearly worthless." 56 1.1 fredette */ 57 1.11 tsutsui #define mm58167_read(sc, r) \ 58 1.11 tsutsui bus_space_read_1(sc->mm58167_regt, sc->mm58167_regh, sc-> r) 59 1.11 tsutsui #define mm58167_write(sc, r, v) \ 60 1.11 tsutsui bus_space_write_1(sc->mm58167_regt, sc->mm58167_regh, sc-> r, v) 61 1.1 fredette 62 1.1 fredette todr_chip_handle_t 63 1.11 tsutsui mm58167_attach(struct mm58167_softc *sc) 64 1.1 fredette { 65 1.1 fredette struct todr_chip_handle *handle; 66 1.5 perry 67 1.11 tsutsui aprint_normal(": mm58167"); 68 1.1 fredette 69 1.1 fredette handle = &sc->_mm58167_todr_handle; 70 1.15 msaitoh memset(handle, 0, sizeof(*handle)); 71 1.19 thorpej handle->todr_dev = sc->mm58167_dev; 72 1.14 tsutsui handle->todr_gettime_ymdhms = mm58167_gettime_ymdhms; 73 1.14 tsutsui handle->todr_settime_ymdhms = mm58167_settime_ymdhms; 74 1.11 tsutsui return handle; 75 1.1 fredette } 76 1.1 fredette 77 1.1 fredette /* 78 1.1 fredette * Set up the system's time, given a `reasonable' time value. 79 1.1 fredette */ 80 1.1 fredette int 81 1.14 tsutsui mm58167_gettime_ymdhms(todr_chip_handle_t handle, struct clock_ymdhms *dt) 82 1.1 fredette { 83 1.19 thorpej struct mm58167_softc *sc = device_private(handle->todr_dev); 84 1.1 fredette struct clock_ymdhms dt_reasonable; 85 1.12 tsutsui struct timeval now; 86 1.1 fredette int s; 87 1.11 tsutsui uint8_t byte_value; 88 1.1 fredette int leap_year, had_leap_day; 89 1.1 fredette 90 1.1 fredette /* First, read the date out of the chip. */ 91 1.1 fredette 92 1.1 fredette /* No interrupts while we're in the chip. */ 93 1.1 fredette s = splhigh(); 94 1.1 fredette 95 1.1 fredette /* Reset the status bit: */ 96 1.1 fredette byte_value = mm58167_read(sc, mm58167_status); 97 1.1 fredette 98 1.1 fredette /* 99 1.1 fredette * Read the date values until we get a coherent read (one 100 1.1 fredette * where the status stays zero, indicating no increment was 101 1.1 fredette * rippling through while we were reading). 102 1.1 fredette */ 103 1.1 fredette do { 104 1.11 tsutsui #define _MM58167_GET(dt_f, mm_f) \ 105 1.11 tsutsui byte_value = mm58167_read(sc, mm_f); \ 106 1.16 christos dt->dt_f = bcdtobin(byte_value) 107 1.11 tsutsui 108 1.1 fredette _MM58167_GET(dt_mon, mm58167_mon); 109 1.1 fredette _MM58167_GET(dt_day, mm58167_day); 110 1.1 fredette _MM58167_GET(dt_hour, mm58167_hour); 111 1.1 fredette _MM58167_GET(dt_min, mm58167_min); 112 1.1 fredette _MM58167_GET(dt_sec, mm58167_sec); 113 1.1 fredette #undef _MM58167_GET 114 1.1 fredette } while ((mm58167_read(sc, mm58167_status) & 1) == 0); 115 1.1 fredette 116 1.1 fredette splx(s); 117 1.1 fredette 118 1.1 fredette /* Convert the reasonable time into a date: */ 119 1.12 tsutsui getmicrotime(&now); 120 1.12 tsutsui clock_secs_to_ymdhms(now.tv_sec, &dt_reasonable); 121 1.12 tsutsui if (dt_reasonable.dt_year == POSIX_BASE_YEAR) { 122 1.12 tsutsui /* 123 1.12 tsutsui * Not a reasonable year. 124 1.12 tsutsui * Assume called from inittodr(9) on boot and 125 1.12 tsutsui * use file system time set in inittodr(9). 126 1.12 tsutsui */ 127 1.18 thorpej clock_secs_to_ymdhms(handle->todr_base_time, &dt_reasonable); 128 1.12 tsutsui } 129 1.1 fredette 130 1.1 fredette /* 131 1.1 fredette * We need to fake a hardware year. if the hardware MM/DD 132 1.1 fredette * HH:MM:SS date is less than the reasonable MM/DD 133 1.1 fredette * HH:MM:SS, call it the reasonable year plus one, else call 134 1.1 fredette * it the reasonable year. 135 1.1 fredette */ 136 1.14 tsutsui if (dt->dt_mon < dt_reasonable.dt_mon || 137 1.14 tsutsui (dt->dt_mon == dt_reasonable.dt_mon && 138 1.14 tsutsui (dt->dt_day < dt_reasonable.dt_day || 139 1.14 tsutsui (dt->dt_day == dt_reasonable.dt_day && 140 1.14 tsutsui (dt->dt_hour < dt_reasonable.dt_hour || 141 1.14 tsutsui (dt->dt_hour == dt_reasonable.dt_hour && 142 1.14 tsutsui (dt->dt_min < dt_reasonable.dt_min || 143 1.14 tsutsui (dt->dt_min == dt_reasonable.dt_min && 144 1.14 tsutsui (dt->dt_sec < dt_reasonable.dt_sec))))))))) { 145 1.14 tsutsui dt->dt_year = dt_reasonable.dt_year + 1; 146 1.1 fredette } else { 147 1.14 tsutsui dt->dt_year = dt_reasonable.dt_year; 148 1.1 fredette } 149 1.1 fredette 150 1.1 fredette /* 151 1.1 fredette * Make a reasonable effort to see if a leap day has passed 152 1.1 fredette * that we need to account for. This does the right thing 153 1.1 fredette * only when the system was shut down before a leap day, and 154 1.1 fredette * it is now after that leap day. It doesn't do the right 155 1.1 fredette * thing when a leap day happened while the machine was last 156 1.1 fredette * up. When that happens, the hardware clock becomes 157 1.1 fredette * instantly wrong forever, until it gets fixed for some 158 1.1 fredette * reason. Use NTP to deal. 159 1.1 fredette */ 160 1.1 fredette 161 1.1 fredette /* 162 1.1 fredette * This may have happened if the hardware says we're into 163 1.1 fredette * March in the following year. Check that following year for 164 1.1 fredette * a leap day. 165 1.1 fredette */ 166 1.14 tsutsui if (dt->dt_year > dt_reasonable.dt_year && 167 1.14 tsutsui dt->dt_mon >= 3) { 168 1.14 tsutsui leap_year = dt->dt_year; 169 1.1 fredette } 170 1.1 fredette 171 1.1 fredette /* 172 1.1 fredette * This may have happened if the hardware says we're in the 173 1.1 fredette * following year, and the system was shut down before March 174 1.1 fredette * the previous year. check that previous year for a leap 175 1.1 fredette * day. 176 1.1 fredette */ 177 1.14 tsutsui else if (dt->dt_year > dt_reasonable.dt_year && 178 1.11 tsutsui dt_reasonable.dt_mon < 3) { 179 1.11 tsutsui leap_year = dt_reasonable.dt_year; 180 1.1 fredette } 181 1.1 fredette 182 1.1 fredette /* 183 1.1 fredette * This may have happened if the hardware says we're in the 184 1.1 fredette * same year, but we weren't to March before, and we're in or 185 1.1 fredette * past March now. Check this year for a leap day. 186 1.1 fredette */ 187 1.14 tsutsui else if (dt->dt_year == dt_reasonable.dt_year 188 1.11 tsutsui && dt_reasonable.dt_mon < 3 189 1.14 tsutsui && dt->dt_mon >= 3) { 190 1.11 tsutsui leap_year = dt_reasonable.dt_year; 191 1.1 fredette } 192 1.1 fredette 193 1.1 fredette /* 194 1.1 fredette * Otherwise, no leap year to check. 195 1.1 fredette */ 196 1.1 fredette else { 197 1.11 tsutsui leap_year = 0; 198 1.1 fredette } 199 1.1 fredette 200 1.1 fredette /* Do the real leap day check. */ 201 1.1 fredette had_leap_day = 0; 202 1.1 fredette if (leap_year > 0) { 203 1.1 fredette if ((leap_year & 3) == 0) { 204 1.1 fredette had_leap_day = 1; 205 1.1 fredette if ((leap_year % 100) == 0) { 206 1.1 fredette had_leap_day = 0; 207 1.1 fredette if ((leap_year % 400) == 0) 208 1.1 fredette had_leap_day = 1; 209 1.1 fredette } 210 1.1 fredette } 211 1.1 fredette } 212 1.1 fredette 213 1.1 fredette /* 214 1.1 fredette * If we had a leap day, adjust the value we will return, and 215 1.1 fredette * also update the hardware clock. 216 1.5 perry */ 217 1.5 perry /* 218 1.1 fredette * XXX - Since this update just writes back a corrected 219 1.5 perry * version of what we read out above, we lose whatever 220 1.1 fredette * amount of time the clock has advanced since that read. 221 1.1 fredette * Use NTP to deal. 222 1.1 fredette */ 223 1.1 fredette if (had_leap_day) { 224 1.14 tsutsui mm58167_settime_ymdhms(handle, dt); 225 1.1 fredette } 226 1.1 fredette 227 1.11 tsutsui return 0; 228 1.1 fredette } 229 1.1 fredette 230 1.1 fredette int 231 1.14 tsutsui mm58167_settime_ymdhms(todr_chip_handle_t handle, struct clock_ymdhms *dt) 232 1.1 fredette { 233 1.19 thorpej struct mm58167_softc *sc = device_private(handle->todr_dev); 234 1.1 fredette int s; 235 1.11 tsutsui uint8_t byte_value; 236 1.1 fredette 237 1.1 fredette /* No interrupts while we're in the chip. */ 238 1.1 fredette s = splhigh(); 239 1.1 fredette 240 1.5 perry /* 241 1.1 fredette * Issue a GO command to reset everything less significant 242 1.1 fredette * than the minutes to zero. 243 1.1 fredette */ 244 1.1 fredette mm58167_write(sc, mm58167_go, 0xFF); 245 1.5 perry 246 1.1 fredette /* Load everything. */ 247 1.11 tsutsui #define _MM58167_PUT(dt_f, mm_f) \ 248 1.16 christos byte_value = bintobcd(dt->dt_f); \ 249 1.11 tsutsui mm58167_write(sc, mm_f, byte_value) 250 1.11 tsutsui 251 1.1 fredette _MM58167_PUT(dt_mon, mm58167_mon); 252 1.1 fredette _MM58167_PUT(dt_day, mm58167_day); 253 1.1 fredette _MM58167_PUT(dt_hour, mm58167_hour); 254 1.1 fredette _MM58167_PUT(dt_min, mm58167_min); 255 1.1 fredette _MM58167_PUT(dt_sec, mm58167_sec); 256 1.1 fredette #undef _MM58167_PUT 257 1.1 fredette 258 1.1 fredette splx(s); 259 1.11 tsutsui return 0; 260 1.1 fredette } 261