1 1.49 thorpej /* $NetBSD: kern_todr.c,v 1.49 2025/09/08 00:12:21 thorpej Exp $ */ 2 1.44 thorpej 3 1.44 thorpej /*- 4 1.44 thorpej * Copyright (c) 2020 The NetBSD Foundation, Inc. 5 1.44 thorpej * All rights reserved. 6 1.44 thorpej * 7 1.44 thorpej * This code is derived from software contributed to The NetBSD Foundation 8 1.44 thorpej * by Jason R. Thorpe. 9 1.44 thorpej * 10 1.44 thorpej * Redistribution and use in source and binary forms, with or without 11 1.44 thorpej * modification, are permitted provided that the following conditions 12 1.44 thorpej * are met: 13 1.44 thorpej * 1. Redistributions of source code must retain the above copyright 14 1.44 thorpej * notice, this list of conditions and the following disclaimer. 15 1.44 thorpej * 2. Redistributions in binary form must reproduce the above copyright 16 1.44 thorpej * notice, this list of conditions and the following disclaimer in the 17 1.44 thorpej * documentation and/or other materials provided with the distribution. 18 1.44 thorpej * 19 1.44 thorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.44 thorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.44 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.44 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.44 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.44 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.44 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.44 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.44 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.44 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.44 thorpej * POSSIBILITY OF SUCH DAMAGE. 30 1.44 thorpej */ 31 1.1 gdamore 32 1.1 gdamore /* 33 1.34 rmind * Copyright (c) 1988 University of Utah. 34 1.1 gdamore * Copyright (c) 1992, 1993 35 1.1 gdamore * The Regents of the University of California. All rights reserved. 36 1.1 gdamore * 37 1.1 gdamore * This code is derived from software contributed to Berkeley by 38 1.1 gdamore * the Systems Programming Group of the University of Utah Computer 39 1.1 gdamore * Science Department and Ralph Campbell. 40 1.1 gdamore * 41 1.1 gdamore * Redistribution and use in source and binary forms, with or without 42 1.1 gdamore * modification, are permitted provided that the following conditions 43 1.1 gdamore * are met: 44 1.1 gdamore * 1. Redistributions of source code must retain the above copyright 45 1.1 gdamore * notice, this list of conditions and the following disclaimer. 46 1.1 gdamore * 2. Redistributions in binary form must reproduce the above copyright 47 1.1 gdamore * notice, this list of conditions and the following disclaimer in the 48 1.1 gdamore * documentation and/or other materials provided with the distribution. 49 1.1 gdamore * 3. Neither the name of the University nor the names of its contributors 50 1.1 gdamore * may be used to endorse or promote products derived from this software 51 1.1 gdamore * without specific prior written permission. 52 1.1 gdamore * 53 1.1 gdamore * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 54 1.1 gdamore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 55 1.1 gdamore * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 56 1.1 gdamore * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 57 1.1 gdamore * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 58 1.1 gdamore * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 59 1.1 gdamore * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 60 1.1 gdamore * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 61 1.1 gdamore * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 62 1.1 gdamore * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 63 1.1 gdamore * SUCH DAMAGE. 64 1.1 gdamore * 65 1.1 gdamore * from: Utah Hdr: clock.c 1.18 91/01/21 66 1.1 gdamore * 67 1.1 gdamore * @(#)clock.c 8.1 (Berkeley) 6/10/93 68 1.1 gdamore */ 69 1.34 rmind 70 1.38 jmcneill #include "opt_todr.h" 71 1.38 jmcneill 72 1.1 gdamore #include <sys/cdefs.h> 73 1.49 thorpej __KERNEL_RCSID(0, "$NetBSD: kern_todr.c,v 1.49 2025/09/08 00:12:21 thorpej Exp $"); 74 1.1 gdamore 75 1.1 gdamore #include <sys/param.h> 76 1.1 gdamore #include <sys/kernel.h> 77 1.1 gdamore #include <sys/systm.h> 78 1.1 gdamore #include <sys/device.h> 79 1.49 thorpej #include <sys/device_calls.h> 80 1.1 gdamore #include <sys/timetc.h> 81 1.25 ad #include <sys/intr.h> 82 1.39 riastrad #include <sys/rndsource.h> 83 1.44 thorpej #include <sys/mutex.h> 84 1.25 ad 85 1.1 gdamore #include <dev/clock_subr.h> /* hmm.. this should probably move to sys */ 86 1.1 gdamore 87 1.42 thorpej static int todr_gettime(todr_chip_handle_t, struct timeval *); 88 1.42 thorpej static int todr_settime(todr_chip_handle_t, struct timeval *); 89 1.42 thorpej 90 1.44 thorpej static kmutex_t todr_mutex; 91 1.44 thorpej static todr_chip_handle_t todr_handle; 92 1.44 thorpej static bool todr_initialized; 93 1.44 thorpej 94 1.47 simonb /* The minimum reasonable RTC date before preposterousness */ 95 1.47 simonb #define PREPOSTEROUS_YEARS (2021 - POSIX_BASE_YEAR) 96 1.47 simonb 97 1.44 thorpej /* 98 1.44 thorpej * todr_init: 99 1.44 thorpej * Initialize TOD clock data. 100 1.44 thorpej */ 101 1.44 thorpej void 102 1.44 thorpej todr_init(void) 103 1.44 thorpej { 104 1.44 thorpej 105 1.44 thorpej mutex_init(&todr_mutex, MUTEX_DEFAULT, IPL_NONE); 106 1.44 thorpej todr_initialized = true; 107 1.44 thorpej } 108 1.44 thorpej 109 1.44 thorpej /* 110 1.44 thorpej * todr_lock: 111 1.44 thorpej * Acquire the TODR lock. 112 1.44 thorpej */ 113 1.44 thorpej void 114 1.44 thorpej todr_lock(void) 115 1.44 thorpej { 116 1.44 thorpej 117 1.44 thorpej mutex_enter(&todr_mutex); 118 1.44 thorpej } 119 1.44 thorpej 120 1.44 thorpej /* 121 1.44 thorpej * todr_unlock: 122 1.44 thorpej * Release the TODR lock. 123 1.44 thorpej */ 124 1.44 thorpej void 125 1.44 thorpej todr_unlock(void) 126 1.44 thorpej { 127 1.44 thorpej 128 1.44 thorpej mutex_exit(&todr_mutex); 129 1.44 thorpej } 130 1.44 thorpej 131 1.44 thorpej /* 132 1.44 thorpej * todr_lock_owned: 133 1.44 thorpej * Return true if the current thread owns the TODR lock. 134 1.44 thorpej * This is to be used by diagnostic assertions only. 135 1.44 thorpej */ 136 1.44 thorpej bool 137 1.44 thorpej todr_lock_owned(void) 138 1.44 thorpej { 139 1.44 thorpej 140 1.44 thorpej return mutex_owned(&todr_mutex) ? true : false; 141 1.44 thorpej } 142 1.1 gdamore 143 1.1 gdamore /* 144 1.49 thorpej * todr_should_skip: 145 1.49 thorpej * Evaluate if we should skip attaching the clock device 146 1.49 thorpej * specifed by the todr handle. 147 1.49 thorpej */ 148 1.49 thorpej static bool 149 1.49 thorpej todr_should_skip(todr_chip_handle_t todr) 150 1.49 thorpej { 151 1.49 thorpej device_t dev = todr->todr_dev; 152 1.49 thorpej struct device_is_system_todr_args args = { }; 153 1.49 thorpej 154 1.49 thorpej /* No basis for evaluation if no device backs the TODR. */ 155 1.49 thorpej if (dev == NULL) { 156 1.49 thorpej return false; 157 1.49 thorpej } 158 1.49 thorpej 159 1.49 thorpej if (device_call(dev, DEVICE_IS_SYSTEM_TODR(&args)) != 0) { 160 1.49 thorpej /* Call is not supported; proceed as if we should attach. */ 161 1.49 thorpej return false; 162 1.49 thorpej } 163 1.49 thorpej 164 1.49 thorpej if (args.result) { 165 1.49 thorpej /* This is the system TODR; proceed. */ 166 1.49 thorpej return false; 167 1.49 thorpej } 168 1.49 thorpej 169 1.49 thorpej aprint_normal_dev(dev, "disabled (not system TODR)\n"); 170 1.49 thorpej return true; 171 1.49 thorpej } 172 1.49 thorpej 173 1.49 thorpej /* 174 1.44 thorpej * todr_attach: 175 1.44 thorpej * Attach the clock device to todr_handle. 176 1.1 gdamore */ 177 1.1 gdamore void 178 1.1 gdamore todr_attach(todr_chip_handle_t todr) 179 1.1 gdamore { 180 1.1 gdamore 181 1.44 thorpej /* 182 1.44 thorpej * todr_init() is called very early in main(), but this is 183 1.44 thorpej * here to catch a case where todr_attach() is called before 184 1.44 thorpej * main(). 185 1.44 thorpej */ 186 1.44 thorpej KASSERT(todr_initialized); 187 1.44 thorpej 188 1.49 thorpej if (todr_should_skip(todr)) { 189 1.49 thorpej return; 190 1.49 thorpej } 191 1.49 thorpej 192 1.44 thorpej todr_lock(); 193 1.24 peter if (todr_handle) { 194 1.44 thorpej todr_unlock(); 195 1.24 peter printf("todr_attach: TOD already configured\n"); 196 1.1 gdamore return; 197 1.1 gdamore } 198 1.24 peter todr_handle = todr; 199 1.44 thorpej todr_unlock(); 200 1.1 gdamore } 201 1.1 gdamore 202 1.31 tsutsui static bool timeset = false; 203 1.2 gdamore 204 1.1 gdamore /* 205 1.44 thorpej * todr_set_systime: 206 1.44 thorpej * Set up the system's time. The "base" argument is a best-guess 207 1.44 thorpej * close-enough value to use if the TOD clock is unavailable or 208 1.44 thorpej * contains garbage. Must be called with the TODR lock held. 209 1.1 gdamore */ 210 1.1 gdamore void 211 1.44 thorpej todr_set_systime(time_t base) 212 1.1 gdamore { 213 1.31 tsutsui bool badbase = false; 214 1.31 tsutsui bool waszero = (base == 0); 215 1.31 tsutsui bool goodtime = false; 216 1.31 tsutsui bool badrtc = false; 217 1.1 gdamore struct timespec ts; 218 1.2 gdamore struct timeval tv; 219 1.1 gdamore 220 1.44 thorpej KASSERT(todr_lock_owned()); 221 1.44 thorpej 222 1.35 tls rnd_add_data(NULL, &base, sizeof(base), 0); 223 1.35 tls 224 1.37 christos if (base < 5 * SECS_PER_COMMON_YEAR) { 225 1.4 gdamore struct clock_ymdhms basedate; 226 1.4 gdamore 227 1.1 gdamore /* 228 1.1 gdamore * If base is 0, assume filesystem time is just unknown 229 1.2 gdamore * instead of preposterous. Don't bark. 230 1.1 gdamore */ 231 1.1 gdamore if (base != 0) 232 1.1 gdamore printf("WARNING: preposterous time in file system\n"); 233 1.1 gdamore /* not going to use it anyway, if the chip is readable */ 234 1.33 tsutsui basedate.dt_year = 2010; 235 1.4 gdamore basedate.dt_mon = 1; 236 1.4 gdamore basedate.dt_day = 1; 237 1.4 gdamore basedate.dt_hour = 12; 238 1.4 gdamore basedate.dt_min = 0; 239 1.4 gdamore basedate.dt_sec = 0; 240 1.4 gdamore base = clock_ymdhms_to_secs(&basedate); 241 1.31 tsutsui badbase = true; 242 1.1 gdamore } 243 1.1 gdamore 244 1.5 matt /* 245 1.5 matt * Some ports need to be supplied base in order to fabricate a time_t. 246 1.5 matt */ 247 1.22 gdamore if (todr_handle) 248 1.48 thorpej todr_handle->todr_base_time = base; 249 1.5 matt 250 1.40 maxv memset(&tv, 0, sizeof(tv)); 251 1.40 maxv 252 1.1 gdamore if ((todr_handle == NULL) || 253 1.2 gdamore (todr_gettime(todr_handle, &tv) != 0) || 254 1.47 simonb (tv.tv_sec < (PREPOSTEROUS_YEARS * SECS_PER_COMMON_YEAR))) { 255 1.1 gdamore 256 1.24 peter if (todr_handle != NULL) 257 1.2 gdamore printf("WARNING: preposterous TOD clock time\n"); 258 1.1 gdamore else 259 1.2 gdamore printf("WARNING: no TOD clock present\n"); 260 1.31 tsutsui badrtc = true; 261 1.1 gdamore } else { 262 1.30 tsutsui time_t deltat = tv.tv_sec - base; 263 1.1 gdamore 264 1.1 gdamore if (deltat < 0) 265 1.1 gdamore deltat = -deltat; 266 1.2 gdamore 267 1.37 christos if (!badbase && deltat >= 2 * SECS_PER_DAY) { 268 1.2 gdamore 269 1.2 gdamore if (tv.tv_sec < base) { 270 1.2 gdamore /* 271 1.2 gdamore * The clock should never go backwards 272 1.2 gdamore * relative to filesystem time. If it 273 1.2 gdamore * does by more than the threshold, 274 1.2 gdamore * believe the filesystem. 275 1.2 gdamore */ 276 1.30 tsutsui printf("WARNING: clock lost %" PRId64 " days\n", 277 1.37 christos deltat / SECS_PER_DAY); 278 1.31 tsutsui badrtc = true; 279 1.24 peter } else { 280 1.30 tsutsui aprint_verbose("WARNING: clock gained %" PRId64 281 1.37 christos " days\n", deltat / SECS_PER_DAY); 282 1.31 tsutsui goodtime = true; 283 1.2 gdamore } 284 1.2 gdamore } else { 285 1.31 tsutsui goodtime = true; 286 1.2 gdamore } 287 1.35 tls 288 1.35 tls rnd_add_data(NULL, &tv, sizeof(tv), 0); 289 1.2 gdamore } 290 1.2 gdamore 291 1.2 gdamore /* if the rtc time is bad, use the filesystem time */ 292 1.2 gdamore if (badrtc) { 293 1.2 gdamore if (badbase) { 294 1.2 gdamore printf("WARNING: using default initial time\n"); 295 1.2 gdamore } else { 296 1.2 gdamore printf("WARNING: using filesystem time\n"); 297 1.2 gdamore } 298 1.2 gdamore tv.tv_sec = base; 299 1.2 gdamore tv.tv_usec = 0; 300 1.1 gdamore } 301 1.2 gdamore 302 1.31 tsutsui timeset = true; 303 1.2 gdamore 304 1.2 gdamore ts.tv_sec = tv.tv_sec; 305 1.2 gdamore ts.tv_nsec = tv.tv_usec * 1000; 306 1.2 gdamore tc_setclock(&ts); 307 1.2 gdamore 308 1.2 gdamore if (waszero || goodtime) 309 1.2 gdamore return; 310 1.2 gdamore 311 1.2 gdamore printf("WARNING: CHECK AND RESET THE DATE!\n"); 312 1.1 gdamore } 313 1.1 gdamore 314 1.1 gdamore /* 315 1.44 thorpej * todr_save_systime: 316 1.44 thorpej * Save the current system time back to the TOD clock. 317 1.44 thorpej * Must be called with the TODR lock held. 318 1.1 gdamore */ 319 1.1 gdamore void 320 1.44 thorpej todr_save_systime(void) 321 1.1 gdamore { 322 1.26 yamt struct timeval tv; 323 1.1 gdamore 324 1.44 thorpej KASSERT(todr_lock_owned()); 325 1.44 thorpej 326 1.2 gdamore /* 327 1.2 gdamore * We might have been called by boot() due to a crash early 328 1.2 gdamore * on. Don't reset the clock chip if we don't know what time 329 1.2 gdamore * it is. 330 1.2 gdamore */ 331 1.2 gdamore if (!timeset) 332 1.2 gdamore return; 333 1.2 gdamore 334 1.26 yamt getmicrotime(&tv); 335 1.1 gdamore 336 1.26 yamt if (tv.tv_sec == 0) 337 1.1 gdamore return; 338 1.1 gdamore 339 1.1 gdamore if (todr_handle) 340 1.26 yamt if (todr_settime(todr_handle, &tv) != 0) 341 1.1 gdamore printf("Cannot set TOD clock time\n"); 342 1.1 gdamore } 343 1.1 gdamore 344 1.44 thorpej /* 345 1.44 thorpej * inittodr: 346 1.44 thorpej * Legacy wrapper around todr_set_systime(). 347 1.44 thorpej */ 348 1.44 thorpej void 349 1.44 thorpej inittodr(time_t base) 350 1.44 thorpej { 351 1.44 thorpej 352 1.44 thorpej todr_lock(); 353 1.44 thorpej todr_set_systime(base); 354 1.44 thorpej todr_unlock(); 355 1.44 thorpej } 356 1.44 thorpej 357 1.44 thorpej /* 358 1.44 thorpej * resettodr: 359 1.44 thorpej * Legacy wrapper around todr_save_systime(). 360 1.44 thorpej */ 361 1.44 thorpej void 362 1.44 thorpej resettodr(void) 363 1.44 thorpej { 364 1.44 thorpej 365 1.44 thorpej /* 366 1.44 thorpej * If we're shutting down, we don't want to get stuck in case 367 1.44 thorpej * someone was already fiddling with the TOD clock. 368 1.44 thorpej */ 369 1.44 thorpej if (shutting_down) { 370 1.44 thorpej if (mutex_tryenter(&todr_mutex) == 0) { 371 1.44 thorpej printf("WARNING: Cannot set TOD clock time (busy)\n"); 372 1.44 thorpej return; 373 1.44 thorpej } 374 1.44 thorpej } else { 375 1.44 thorpej todr_lock(); 376 1.44 thorpej } 377 1.44 thorpej todr_save_systime(); 378 1.44 thorpej todr_unlock(); 379 1.44 thorpej } 380 1.44 thorpej 381 1.18 gdamore #ifdef TODR_DEBUG 382 1.18 gdamore static void 383 1.18 gdamore todr_debug(const char *prefix, int rv, struct clock_ymdhms *dt, 384 1.32 tsutsui struct timeval *tvp) 385 1.18 gdamore { 386 1.18 gdamore struct timeval tv_val; 387 1.18 gdamore struct clock_ymdhms dt_val; 388 1.18 gdamore 389 1.18 gdamore if (dt == NULL) { 390 1.18 gdamore clock_secs_to_ymdhms(tvp->tv_sec, &dt_val); 391 1.18 gdamore dt = &dt_val; 392 1.18 gdamore } 393 1.18 gdamore if (tvp == NULL) { 394 1.18 gdamore tvp = &tv_val; 395 1.18 gdamore tvp->tv_sec = clock_ymdhms_to_secs(dt); 396 1.18 gdamore tvp->tv_usec = 0; 397 1.18 gdamore } 398 1.18 gdamore printf("%s: rv = %d\n", prefix, rv); 399 1.18 gdamore printf("%s: rtc_offset = %d\n", prefix, rtc_offset); 400 1.23 tsutsui printf("%s: %4u/%02u/%02u %02u:%02u:%02u, (wday %d) (epoch %u.%06u)\n", 401 1.18 gdamore prefix, 402 1.36 jmcneill (unsigned)dt->dt_year, dt->dt_mon, dt->dt_day, 403 1.18 gdamore dt->dt_hour, dt->dt_min, dt->dt_sec, 404 1.18 gdamore dt->dt_wday, (unsigned)tvp->tv_sec, (unsigned)tvp->tv_usec); 405 1.18 gdamore } 406 1.18 gdamore #else /* !TODR_DEBUG */ 407 1.18 gdamore #define todr_debug(prefix, rv, dt, tvp) 408 1.18 gdamore #endif /* TODR_DEBUG */ 409 1.18 gdamore 410 1.42 thorpej static int 411 1.43 thorpej todr_wenable(todr_chip_handle_t todr, int onoff) 412 1.43 thorpej { 413 1.43 thorpej 414 1.43 thorpej if (todr->todr_setwen != NULL) 415 1.43 thorpej return todr->todr_setwen(todr, onoff); 416 1.43 thorpej 417 1.43 thorpej return 0; 418 1.43 thorpej } 419 1.43 thorpej 420 1.43 thorpej #define ENABLE_TODR_WRITES() \ 421 1.43 thorpej do { \ 422 1.43 thorpej if ((rv = todr_wenable(tch, 1)) != 0) { \ 423 1.43 thorpej printf("%s: cannot enable TODR writes\n", __func__); \ 424 1.43 thorpej return rv; \ 425 1.43 thorpej } \ 426 1.43 thorpej } while (/*CONSTCOND*/0) 427 1.43 thorpej 428 1.43 thorpej #define DISABLE_TODR_WRITES() \ 429 1.43 thorpej do { \ 430 1.43 thorpej if (todr_wenable(tch, 0) != 0) \ 431 1.43 thorpej printf("%s: WARNING: cannot disable TODR writes\n", \ 432 1.43 thorpej __func__); \ 433 1.43 thorpej } while (/*CONSTCOND*/0) 434 1.43 thorpej 435 1.43 thorpej static int 436 1.32 tsutsui todr_gettime(todr_chip_handle_t tch, struct timeval *tvp) 437 1.6 gdamore { 438 1.46 thorpej int rv; 439 1.6 gdamore 440 1.43 thorpej /* 441 1.43 thorpej * Write-enable is used even when reading the TODR because 442 1.43 thorpej * writing to registers may be required in order to do so. 443 1.43 thorpej */ 444 1.43 thorpej 445 1.17 gdamore if (tch->todr_gettime) { 446 1.43 thorpej ENABLE_TODR_WRITES(); 447 1.17 gdamore rv = tch->todr_gettime(tch, tvp); 448 1.43 thorpej DISABLE_TODR_WRITES(); 449 1.20 gdamore /* 450 1.20 gdamore * Some unconverted ports have their own references to 451 1.20 gdamore * rtc_offset. A converted port must not do that. 452 1.20 gdamore */ 453 1.20 gdamore if (rv == 0) 454 1.20 gdamore tvp->tv_sec += rtc_offset * 60; 455 1.23 tsutsui todr_debug("TODR-GET-SECS", rv, NULL, tvp); 456 1.17 gdamore return rv; 457 1.17 gdamore } else if (tch->todr_gettime_ymdhms) { 458 1.46 thorpej struct clock_ymdhms dt = { 0 }; 459 1.43 thorpej ENABLE_TODR_WRITES(); 460 1.17 gdamore rv = tch->todr_gettime_ymdhms(tch, &dt); 461 1.43 thorpej DISABLE_TODR_WRITES(); 462 1.23 tsutsui todr_debug("TODR-GET-YMDHMS", rv, &dt, NULL); 463 1.17 gdamore if (rv) 464 1.6 gdamore return rv; 465 1.10 gdamore 466 1.6 gdamore /* 467 1.19 gdamore * Simple sanity checks. Note that this includes a 468 1.19 gdamore * value for clocks that can return a leap second. 469 1.19 gdamore * Note that we don't support double leap seconds, 470 1.19 gdamore * since this was apparently an error/misunderstanding 471 1.19 gdamore * on the part of the ISO C committee, and can never 472 1.19 gdamore * actually occur. If your clock issues us a double 473 1.19 gdamore * leap second, it must be broken. Ultimately, you'd 474 1.19 gdamore * have to be trying to read time at precisely that 475 1.19 gdamore * instant to even notice, so even broken clocks will 476 1.19 gdamore * work the vast majority of the time. In such a case 477 1.21 gdamore * it is recommended correction be applied in the 478 1.21 gdamore * clock driver. 479 1.19 gdamore */ 480 1.13 tsutsui if (dt.dt_mon < 1 || dt.dt_mon > 12 || 481 1.13 tsutsui dt.dt_day < 1 || dt.dt_day > 31 || 482 1.19 gdamore dt.dt_hour > 23 || dt.dt_min > 59 || dt.dt_sec > 60) { 483 1.17 gdamore return EINVAL; 484 1.17 gdamore } 485 1.6 gdamore tvp->tv_sec = clock_ymdhms_to_secs(&dt) + rtc_offset * 60; 486 1.6 gdamore tvp->tv_usec = 0; 487 1.17 gdamore return tvp->tv_sec < 0 ? EINVAL : 0; 488 1.6 gdamore } 489 1.6 gdamore 490 1.17 gdamore return ENXIO; 491 1.6 gdamore } 492 1.6 gdamore 493 1.42 thorpej static int 494 1.32 tsutsui todr_settime(todr_chip_handle_t tch, struct timeval *tvp) 495 1.6 gdamore { 496 1.46 thorpej int rv; 497 1.6 gdamore 498 1.17 gdamore if (tch->todr_settime) { 499 1.46 thorpej struct timeval copy = *tvp; 500 1.20 gdamore copy.tv_sec -= rtc_offset * 60; 501 1.43 thorpej ENABLE_TODR_WRITES(); 502 1.20 gdamore rv = tch->todr_settime(tch, ©); 503 1.43 thorpej DISABLE_TODR_WRITES(); 504 1.17 gdamore todr_debug("TODR-SET-SECS", rv, NULL, tvp); 505 1.17 gdamore return rv; 506 1.17 gdamore } else if (tch->todr_settime_ymdhms) { 507 1.46 thorpej struct clock_ymdhms dt; 508 1.46 thorpej time_t sec = tvp->tv_sec - rtc_offset * 60; 509 1.10 gdamore if (tvp->tv_usec >= 500000) 510 1.10 gdamore sec++; 511 1.10 gdamore clock_secs_to_ymdhms(sec, &dt); 512 1.43 thorpej ENABLE_TODR_WRITES(); 513 1.17 gdamore rv = tch->todr_settime_ymdhms(tch, &dt); 514 1.43 thorpej DISABLE_TODR_WRITES(); 515 1.17 gdamore todr_debug("TODR-SET-YMDHMS", rv, &dt, NULL); 516 1.17 gdamore return rv; 517 1.6 gdamore } 518 1.46 thorpej 519 1.46 thorpej return ENXIO; 520 1.6 gdamore } 521