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\n"); 78 return (error); 79 } 80 device_set_desc(dev, "EFI Realtime Clock"); 81 return (BUS_PROBE_DEFAULT); 82 } 83 84 static int 85 efirtc_attach(device_t dev) 86 { 87 struct efi_tmcap tmcap; 88 long res; 89 int error; 90 91 bzero(&tmcap, sizeof(tmcap)); 92 if ((error = efi_get_time_capabilities(&tmcap)) != 0) { 93 device_printf(dev, "cannot get EFI time capabilities"); 94 return (error); 95 } 96 97 /* Translate resolution in Hz to tick length in usec. */ 98 if (tmcap.tc_res == 0) 99 res = us_per_s; /* 0 is insane, assume 1 Hz. */ 100 else if (tmcap.tc_res > us_per_s) 101 res = 1; /* 1us is the best we can represent */ 102 else 103 res = us_per_s / tmcap.tc_res; 104 105 /* Clock rounding adjustment is 1/2 of resolution, in nsec. */ 106 efirtc_resadj.tv_nsec = (res * ns_per_us) / 2; 107 108 /* Does the clock zero the subseconds when time is set? */ 109 efirtc_zeroes_subseconds = tmcap.tc_stz; 110 111 /* 112 * Register. If the clock zeroes out the subseconds when it's set, 113 * schedule the SetTime calls to happen just before top-of-second. 114 */ 115 clock_register_flags(dev, res, CLOCKF_SETTIME_NO_ADJ); 116 if (efirtc_zeroes_subseconds) 117 clock_schedule(dev, ns_per_s - ns_per_us); 118 119 return (0); 120 } 121 122 static int 123 efirtc_detach(device_t dev) 124 { 125 126 clock_unregister(dev); 127 return (0); 128 } 129 130 static int 131 efirtc_gettime(device_t dev, struct timespec *ts) 132 { 133 struct clocktime ct; 134 struct efi_tm tm; 135 int error; 136 137 error = efi_get_time(&tm); 138 if (error != 0) 139 return (error); 140 141 ct.sec = tm.tm_sec; 142 ct.min = tm.tm_min; 143 ct.hour = tm.tm_hour; 144 ct.day = tm.tm_mday; 145 ct.mon = tm.tm_mon; 146 ct.year = tm.tm_year; 147 ct.nsec = tm.tm_nsec; 148 149 clock_dbgprint_ct(dev, CLOCK_DBG_READ, &ct); 150 return (clock_ct_to_ts(&ct, ts)); 151 } 152 153 static int 154 efirtc_settime(device_t dev, struct timespec *ts) 155 { 156 struct clocktime ct; 157 struct efi_tm tm; 158 159 /* 160 * We request a timespec with no resolution-adjustment so that we can 161 * apply it ourselves based on whether or not the clock zeroes the 162 * sub-second part of the time when setting the time. 163 */ 164 ts->tv_sec -= utc_offset(); 165 if (!efirtc_zeroes_subseconds) 166 timespecadd(ts, &efirtc_resadj); 167 168 clock_ts_to_ct(ts, &ct); 169 clock_dbgprint_ct(dev, CLOCK_DBG_WRITE, &ct); 170 171 bzero(&tm, sizeof(tm)); 172 tm.tm_sec = ct.sec; 173 tm.tm_min = ct.min; 174 tm.tm_hour = ct.hour; 175 tm.tm_mday = ct.day; 176 tm.tm_mon = ct.mon; 177 tm.tm_year = ct.year; 178 tm.tm_nsec = ct.nsec; 179 180 return (efi_set_time(&tm)); 181 } 182 183 static device_method_t efirtc_methods[] = { 184 /* Device interface */ 185 DEVMETHOD(device_identify, efirtc_identify), 186 DEVMETHOD(device_probe, efirtc_probe), 187 DEVMETHOD(device_attach, efirtc_attach), 188 DEVMETHOD(device_detach, efirtc_detach), 189 190 /* Clock interface */ 191 DEVMETHOD(clock_gettime, efirtc_gettime), 192 DEVMETHOD(clock_settime, efirtc_settime), 193 194 DEVMETHOD_END 195 }; 196 197 static devclass_t efirtc_devclass; 198 static driver_t efirtc_driver = { 199 "efirtc", 200 efirtc_methods, 201 0 202 }; 203 204 DRIVER_MODULE(efirtc, nexus, efirtc_driver, efirtc_devclass, 0, 0); 205