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