1 /*- 2 * Copyright (c) 2017 Andrew Turner 3 * All rights reserved. 4 * 5 * This software was developed by SRI International and the University of 6 * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 7 * ("CTSRD"), as part of the DARPA CRASH research programme. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/bus.h> 35 #include <sys/clock.h> 36 #include <sys/efi.h> 37 #include <sys/kernel.h> 38 #include <sys/module.h> 39 40 #include "clock_if.h" 41 42 static bool efirtc_zeroes_subseconds; 43 static struct timespec efirtc_resadj; 44 45 static const u_int us_per_s = 1000000; 46 static const u_int ns_per_s = 1000000000; 47 static const u_int ns_per_us = 1000; 48 49 static void 50 efirtc_identify(driver_t *driver, device_t parent) 51 { 52 53 /* Don't add the driver unless we have working runtime services. */ 54 if (efi_rt_ok() != 0) 55 return; 56 if (device_find_child(parent, "efirtc", -1) != NULL) 57 return; 58 if (BUS_ADD_CHILD(parent, 0, "efirtc", -1) == NULL) 59 device_printf(parent, "add child failed\n"); 60 } 61 62 static int 63 efirtc_probe(device_t dev) 64 { 65 struct efi_tm tm; 66 int error; 67 68 /* 69 * Check whether we can read the time. This will stop us from attaching 70 * when there is EFI Runtime support but the gettime function is 71 * unimplemented, e.g. on some builds of U-Boot. 72 */ 73 if ((error = efi_get_time(&tm)) != 0) { 74 if (bootverbose) 75 device_printf(dev, "cannot read EFI realtime clock, " 76 "error %d\n", error); 77 return (error); 78 } 79 device_set_desc(dev, "EFI Realtime Clock"); 80 return (BUS_PROBE_DEFAULT); 81 } 82 83 static int 84 efirtc_attach(device_t dev) 85 { 86 struct efi_tmcap tmcap; 87 long res; 88 int error; 89 90 bzero(&tmcap, sizeof(tmcap)); 91 if ((error = efi_get_time_capabilities(&tmcap)) != 0) { 92 device_printf(dev, "cannot get EFI time capabilities"); 93 return (error); 94 } 95 96 /* Translate resolution in Hz to tick length in usec. */ 97 if (tmcap.tc_res == 0) 98 res = us_per_s; /* 0 is insane, assume 1 Hz. */ 99 else if (tmcap.tc_res > us_per_s) 100 res = 1; /* 1us is the best we can represent */ 101 else 102 res = us_per_s / tmcap.tc_res; 103 104 /* Clock rounding adjustment is 1/2 of resolution, in nsec. */ 105 efirtc_resadj.tv_nsec = (res * ns_per_us) / 2; 106 107 /* Does the clock zero the subseconds when time is set? */ 108 efirtc_zeroes_subseconds = tmcap.tc_stz; 109 110 /* 111 * Register. If the clock zeroes out the subseconds when it's set, 112 * schedule the SetTime calls to happen just before top-of-second. 113 */ 114 clock_register_flags(dev, res, CLOCKF_SETTIME_NO_ADJ); 115 if (efirtc_zeroes_subseconds) 116 clock_schedule(dev, ns_per_s - ns_per_us); 117 118 return (0); 119 } 120 121 static int 122 efirtc_detach(device_t dev) 123 { 124 125 clock_unregister(dev); 126 return (0); 127 } 128 129 static int 130 efirtc_gettime(device_t dev, struct timespec *ts) 131 { 132 struct clocktime ct; 133 struct efi_tm tm; 134 int error; 135 136 error = efi_get_time(&tm); 137 if (error != 0) 138 return (error); 139 140 ct.sec = tm.tm_sec; 141 ct.min = tm.tm_min; 142 ct.hour = tm.tm_hour; 143 ct.day = tm.tm_mday; 144 ct.mon = tm.tm_mon; 145 ct.year = tm.tm_year; 146 ct.nsec = tm.tm_nsec; 147 148 clock_dbgprint_ct(dev, CLOCK_DBG_READ, &ct); 149 return (clock_ct_to_ts(&ct, ts)); 150 } 151 152 static int 153 efirtc_settime(device_t dev, struct timespec *ts) 154 { 155 struct clocktime ct; 156 struct efi_tm tm; 157 158 /* 159 * We request a timespec with no resolution-adjustment so that we can 160 * apply it ourselves based on whether or not the clock zeroes the 161 * sub-second part of the time when setting the time. 162 */ 163 ts->tv_sec -= utc_offset(); 164 if (!efirtc_zeroes_subseconds) 165 timespecadd(ts, &efirtc_resadj, ts); 166 167 clock_ts_to_ct(ts, &ct); 168 clock_dbgprint_ct(dev, CLOCK_DBG_WRITE, &ct); 169 170 bzero(&tm, sizeof(tm)); 171 tm.tm_sec = ct.sec; 172 tm.tm_min = ct.min; 173 tm.tm_hour = ct.hour; 174 tm.tm_mday = ct.day; 175 tm.tm_mon = ct.mon; 176 tm.tm_year = ct.year; 177 tm.tm_nsec = ct.nsec; 178 179 return (efi_set_time(&tm)); 180 } 181 182 static device_method_t efirtc_methods[] = { 183 /* Device interface */ 184 DEVMETHOD(device_identify, efirtc_identify), 185 DEVMETHOD(device_probe, efirtc_probe), 186 DEVMETHOD(device_attach, efirtc_attach), 187 DEVMETHOD(device_detach, efirtc_detach), 188 189 /* Clock interface */ 190 DEVMETHOD(clock_gettime, efirtc_gettime), 191 DEVMETHOD(clock_settime, efirtc_settime), 192 193 DEVMETHOD_END 194 }; 195 196 static driver_t efirtc_driver = { 197 "efirtc", 198 efirtc_methods, 199 0 200 }; 201 202 DRIVER_MODULE(efirtc, nexus, efirtc_driver, 0, 0); 203 MODULE_VERSION(efirtc, 1); 204 MODULE_DEPEND(efirtc, efirt, 1, 1, 1); 205