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/param.h> 32 #include <sys/systm.h> 33 #include <sys/bus.h> 34 #include <sys/clock.h> 35 #include <sys/efi.h> 36 #include <sys/kernel.h> 37 #include <sys/module.h> 38 39 #include "clock_if.h" 40 41 static bool efirtc_zeroes_subseconds; 42 static struct timespec efirtc_resadj; 43 44 static const u_int us_per_s = 1000000; 45 static const u_int ns_per_s = 1000000000; 46 static const u_int ns_per_us = 1000; 47 48 static void 49 efirtc_identify(driver_t *driver, device_t parent) 50 { 51 52 /* Don't add the driver unless we have working runtime services. */ 53 if (efi_rt_ok() != 0) 54 return; 55 if (device_find_child(parent, "efirtc", -1) != NULL) 56 return; 57 if (BUS_ADD_CHILD(parent, 0, "efirtc", -1) == NULL) 58 device_printf(parent, "add child failed\n"); 59 } 60 61 static int 62 efirtc_probe(device_t dev) 63 { 64 struct efi_tm tm; 65 int error; 66 67 /* 68 * Check whether we can read the time. This will stop us from attaching 69 * when there is EFI Runtime support but the gettime function is 70 * unimplemented, e.g. on some builds of U-Boot. 71 */ 72 if ((error = efi_get_time(&tm)) != 0) { 73 if (bootverbose) 74 device_printf(dev, "cannot read EFI realtime clock, " 75 "error %d\n", error); 76 return (error); 77 } 78 device_set_desc(dev, "EFI Realtime Clock"); 79 return (BUS_PROBE_DEFAULT); 80 } 81 82 static int 83 efirtc_attach(device_t dev) 84 { 85 struct efi_tmcap tmcap; 86 long res; 87 int error; 88 89 bzero(&tmcap, sizeof(tmcap)); 90 if ((error = efi_get_time_capabilities(&tmcap)) != 0) { 91 device_printf(dev, "cannot get EFI time capabilities"); 92 return (error); 93 } 94 95 /* Translate resolution in Hz to tick length in usec. */ 96 if (tmcap.tc_res == 0) 97 res = us_per_s; /* 0 is insane, assume 1 Hz. */ 98 else if (tmcap.tc_res > us_per_s) 99 res = 1; /* 1us is the best we can represent */ 100 else 101 res = us_per_s / tmcap.tc_res; 102 103 /* Clock rounding adjustment is 1/2 of resolution, in nsec. */ 104 efirtc_resadj.tv_nsec = (res * ns_per_us) / 2; 105 106 /* Does the clock zero the subseconds when time is set? */ 107 efirtc_zeroes_subseconds = tmcap.tc_stz; 108 109 /* 110 * Register. If the clock zeroes out the subseconds when it's set, 111 * schedule the SetTime calls to happen just before top-of-second. 112 */ 113 clock_register_flags(dev, res, CLOCKF_SETTIME_NO_ADJ); 114 if (efirtc_zeroes_subseconds) 115 clock_schedule(dev, ns_per_s - ns_per_us); 116 117 return (0); 118 } 119 120 static int 121 efirtc_detach(device_t dev) 122 { 123 124 clock_unregister(dev); 125 return (0); 126 } 127 128 static int 129 efirtc_gettime(device_t dev, struct timespec *ts) 130 { 131 struct clocktime ct; 132 struct efi_tm tm; 133 int error; 134 135 error = efi_get_time(&tm); 136 if (error != 0) 137 return (error); 138 139 ct.sec = tm.tm_sec; 140 ct.min = tm.tm_min; 141 ct.hour = tm.tm_hour; 142 ct.day = tm.tm_mday; 143 ct.mon = tm.tm_mon; 144 ct.year = tm.tm_year; 145 ct.nsec = tm.tm_nsec; 146 147 clock_dbgprint_ct(dev, CLOCK_DBG_READ, &ct); 148 return (clock_ct_to_ts(&ct, ts)); 149 } 150 151 static int 152 efirtc_settime(device_t dev, struct timespec *ts) 153 { 154 struct clocktime ct; 155 struct efi_tm tm; 156 157 /* 158 * We request a timespec with no resolution-adjustment so that we can 159 * apply it ourselves based on whether or not the clock zeroes the 160 * sub-second part of the time when setting the time. 161 */ 162 ts->tv_sec -= utc_offset(); 163 if (!efirtc_zeroes_subseconds) 164 timespecadd(ts, &efirtc_resadj, ts); 165 166 clock_ts_to_ct(ts, &ct); 167 clock_dbgprint_ct(dev, CLOCK_DBG_WRITE, &ct); 168 169 bzero(&tm, sizeof(tm)); 170 tm.tm_sec = ct.sec; 171 tm.tm_min = ct.min; 172 tm.tm_hour = ct.hour; 173 tm.tm_mday = ct.day; 174 tm.tm_mon = ct.mon; 175 tm.tm_year = ct.year; 176 tm.tm_nsec = ct.nsec; 177 178 return (efi_set_time(&tm)); 179 } 180 181 static device_method_t efirtc_methods[] = { 182 /* Device interface */ 183 DEVMETHOD(device_identify, efirtc_identify), 184 DEVMETHOD(device_probe, efirtc_probe), 185 DEVMETHOD(device_attach, efirtc_attach), 186 DEVMETHOD(device_detach, efirtc_detach), 187 188 /* Clock interface */ 189 DEVMETHOD(clock_gettime, efirtc_gettime), 190 DEVMETHOD(clock_settime, efirtc_settime), 191 192 DEVMETHOD_END 193 }; 194 195 static driver_t efirtc_driver = { 196 "efirtc", 197 efirtc_methods, 198 0 199 }; 200 201 DRIVER_MODULE(efirtc, nexus, efirtc_driver, 0, 0); 202 MODULE_VERSION(efirtc, 1); 203 MODULE_DEPEND(efirtc, efirt, 1, 1, 1); 204