1209371Smav/*- 2247463Smav * Copyright (c) 2010-2013 Alexander Motin <mav@FreeBSD.org> 3209371Smav * All rights reserved. 4209371Smav * 5209371Smav * Redistribution and use in source and binary forms, with or without 6209371Smav * modification, are permitted provided that the following conditions 7209371Smav * are met: 8209371Smav * 1. Redistributions of source code must retain the above copyright 9209371Smav * notice, this list of conditions and the following disclaimer, 10209371Smav * without modification, immediately at the beginning of the file. 11209371Smav * 2. Redistributions in binary form must reproduce the above copyright 12209371Smav * notice, this list of conditions and the following disclaimer in the 13209371Smav * documentation and/or other materials provided with the distribution. 14209371Smav * 15209371Smav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16209371Smav * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17209371Smav * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18209371Smav * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19209371Smav * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20209371Smav * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21209371Smav * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22209371Smav * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23209371Smav * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24209371Smav * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25209371Smav */ 26209371Smav 27209371Smav#include <sys/cdefs.h> 28209371Smav__FBSDID("$FreeBSD$"); 29209371Smav 30209371Smav#include <sys/param.h> 31209371Smav#include <sys/kernel.h> 32209371Smav#include <sys/sysctl.h> 33209371Smav#include <sys/systm.h> 34209371Smav#include <sys/queue.h> 35209371Smav#include <sys/timeet.h> 36209371Smav 37266347Sian#include "opt_timer.h" 38266347Sian 39209371SmavSLIST_HEAD(et_eventtimers_list, eventtimer); 40209371Smavstatic struct et_eventtimers_list eventtimers = SLIST_HEAD_INITIALIZER(et_eventtimers); 41209371Smav 42209371Smavstruct mtx et_eventtimers_mtx; 43212541SmavMTX_SYSINIT(et_eventtimers_init, &et_eventtimers_mtx, "et_mtx", MTX_DEF); 44209371Smav 45209371SmavSYSCTL_NODE(_kern, OID_AUTO, eventtimer, CTLFLAG_RW, 0, "Event timers"); 46227309Sedstatic SYSCTL_NODE(_kern_eventtimer, OID_AUTO, et, CTLFLAG_RW, 0, ""); 47209371Smav 48209371Smav/* 49209371Smav * Register a new event timer hardware. 50209371Smav */ 51209371Smavint 52209371Smavet_register(struct eventtimer *et) 53209371Smav{ 54209371Smav struct eventtimer *tmp, *next; 55209371Smav 56209371Smav if (et->et_quality >= 0 || bootverbose) { 57212479Smav if (et->et_frequency == 0) { 58212479Smav printf("Event timer \"%s\" quality %d\n", 59212479Smav et->et_name, et->et_quality); 60212479Smav } else { 61212479Smav printf("Event timer \"%s\" " 62212479Smav "frequency %ju Hz quality %d\n", 63212479Smav et->et_name, (uintmax_t)et->et_frequency, 64212479Smav et->et_quality); 65212479Smav } 66209371Smav } 67247463Smav KASSERT(et->et_start, ("et_register: timer has no start function")); 68209371Smav et->et_sysctl = SYSCTL_ADD_NODE(NULL, 69209371Smav SYSCTL_STATIC_CHILDREN(_kern_eventtimer_et), OID_AUTO, et->et_name, 70209371Smav CTLFLAG_RW, 0, "event timer description"); 71217326Smdf SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 72209371Smav "flags", CTLFLAG_RD, &(et->et_flags), 0, 73209371Smav "Event timer capabilities"); 74217326Smdf SYSCTL_ADD_UQUAD(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 75210346Smav "frequency", CTLFLAG_RD, &(et->et_frequency), 76209371Smav "Event timer base frequency"); 77209371Smav SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 78209371Smav "quality", CTLFLAG_RD, &(et->et_quality), 0, 79209371Smav "Goodness of event timer"); 80209371Smav ET_LOCK(); 81209371Smav if (SLIST_EMPTY(&eventtimers) || 82209371Smav SLIST_FIRST(&eventtimers)->et_quality < et->et_quality) { 83209371Smav SLIST_INSERT_HEAD(&eventtimers, et, et_all); 84209371Smav } else { 85209371Smav SLIST_FOREACH(tmp, &eventtimers, et_all) { 86209371Smav next = SLIST_NEXT(tmp, et_all); 87209371Smav if (next == NULL || next->et_quality < et->et_quality) { 88209371Smav SLIST_INSERT_AFTER(tmp, et, et_all); 89209371Smav break; 90209371Smav } 91209371Smav } 92209371Smav } 93209371Smav ET_UNLOCK(); 94209371Smav return (0); 95209371Smav} 96209371Smav 97209371Smav/* 98209371Smav * Deregister event timer hardware. 99209371Smav */ 100209371Smavint 101209371Smavet_deregister(struct eventtimer *et) 102209371Smav{ 103209371Smav int err = 0; 104209371Smav 105209371Smav if (et->et_deregister_cb != NULL) { 106209371Smav if ((err = et->et_deregister_cb(et, et->et_arg)) != 0) 107209371Smav return (err); 108209371Smav } 109209371Smav 110209371Smav ET_LOCK(); 111209371Smav SLIST_REMOVE(&eventtimers, et, eventtimer, et_all); 112209371Smav ET_UNLOCK(); 113209371Smav sysctl_remove_oid(et->et_sysctl, 1, 1); 114209371Smav return (0); 115209371Smav} 116209371Smav 117209371Smav/* 118266347Sian * Change the frequency of the given timer. If it is the active timer, 119266347Sian * reconfigure it on all CPUs (reschedules all current events based on the new 120266347Sian * timer frequency). 121266347Sian */ 122266347Sianvoid 123266347Sianet_change_frequency(struct eventtimer *et, uint64_t newfreq) 124266347Sian{ 125266347Sian 126266347Sian#ifndef NO_EVENTTIMERS 127266347Sian cpu_et_frequency(et, newfreq); 128266347Sian#endif 129266347Sian} 130266347Sian 131266347Sian/* 132209371Smav * Find free event timer hardware with specified parameters. 133209371Smav */ 134209371Smavstruct eventtimer * 135209371Smavet_find(const char *name, int check, int want) 136209371Smav{ 137209371Smav struct eventtimer *et = NULL; 138209371Smav 139209371Smav SLIST_FOREACH(et, &eventtimers, et_all) { 140209371Smav if (et->et_active) 141209371Smav continue; 142209371Smav if (name != NULL && strcasecmp(et->et_name, name) != 0) 143209371Smav continue; 144209371Smav if (name == NULL && et->et_quality < 0) 145209371Smav continue; 146209371Smav if ((et->et_flags & check) != want) 147209371Smav continue; 148209371Smav break; 149209371Smav } 150209371Smav return (et); 151209371Smav} 152209371Smav 153209371Smav/* 154209371Smav * Initialize event timer hardware. Set callbacks. 155209371Smav */ 156209371Smavint 157209371Smavet_init(struct eventtimer *et, et_event_cb_t *event, 158209371Smav et_deregister_cb_t *deregister, void *arg) 159209371Smav{ 160209371Smav 161209371Smav if (event == NULL) 162209371Smav return (EINVAL); 163209371Smav if (et->et_active) 164209371Smav return (EBUSY); 165209371Smav 166209371Smav et->et_active = 1; 167209371Smav et->et_event_cb = event; 168209371Smav et->et_deregister_cb = deregister; 169209371Smav et->et_arg = arg; 170209371Smav return (0); 171209371Smav} 172209371Smav 173209371Smav/* 174209371Smav * Start event timer hardware. 175209371Smav * first - delay before first tick. 176209371Smav * period - period of subsequent periodic ticks. 177209371Smav */ 178209371Smavint 179247463Smavet_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 180209371Smav{ 181209371Smav 182209371Smav if (!et->et_active) 183209371Smav return (ENXIO); 184247463Smav KASSERT(period >= 0, ("et_start: negative period")); 185247463Smav KASSERT((et->et_flags & ET_FLAGS_PERIODIC) || period == 0, 186247463Smav ("et_start: period specified for oneshot-only timer")); 187248230Smav KASSERT((et->et_flags & ET_FLAGS_ONESHOT) || period != 0, 188247463Smav ("et_start: period not specified for periodic-only timer")); 189247463Smav if (period != 0) { 190247463Smav if (period < et->et_min_period) 191247463Smav period = et->et_min_period; 192247463Smav else if (period > et->et_max_period) 193247463Smav period = et->et_max_period; 194210290Smav } 195247463Smav if (period == 0 || first != 0) { 196247463Smav if (first < et->et_min_period) 197247463Smav first = et->et_min_period; 198247463Smav else if (first > et->et_max_period) 199247463Smav first = et->et_max_period; 200210290Smav } 201247463Smav return (et->et_start(et, first, period)); 202209371Smav} 203209371Smav 204209371Smav/* Stop event timer hardware. */ 205209371Smavint 206209371Smavet_stop(struct eventtimer *et) 207209371Smav{ 208209371Smav 209209371Smav if (!et->et_active) 210209371Smav return (ENXIO); 211209371Smav if (et->et_stop) 212209371Smav return (et->et_stop(et)); 213209371Smav return (0); 214209371Smav} 215209371Smav 216209371Smav/* Mark event timer hardware as broken. */ 217209371Smavint 218209371Smavet_ban(struct eventtimer *et) 219209371Smav{ 220209371Smav 221209371Smav et->et_flags &= ~(ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT); 222209371Smav return (0); 223209371Smav} 224209371Smav 225209371Smav/* Free event timer hardware. */ 226209371Smavint 227209371Smavet_free(struct eventtimer *et) 228209371Smav{ 229209371Smav 230209371Smav if (!et->et_active) 231209371Smav return (ENXIO); 232209371Smav 233209371Smav et->et_active = 0; 234209371Smav return (0); 235209371Smav} 236209371Smav 237209371Smav/* Report list of supported event timers hardware via sysctl. */ 238209371Smavstatic int 239209371Smavsysctl_kern_eventtimer_choice(SYSCTL_HANDLER_ARGS) 240209371Smav{ 241209371Smav char buf[512], *spc; 242209371Smav struct eventtimer *et; 243209371Smav int error, off; 244209371Smav 245209371Smav spc = ""; 246209371Smav error = 0; 247212223Smav buf[0] = 0; 248209371Smav off = 0; 249209371Smav ET_LOCK(); 250209371Smav SLIST_FOREACH(et, &eventtimers, et_all) { 251209371Smav off += snprintf(buf + off, sizeof(buf) - off, "%s%s(%d)", 252209371Smav spc, et->et_name, et->et_quality); 253209371Smav spc = " "; 254209371Smav } 255209371Smav ET_UNLOCK(); 256209371Smav error = SYSCTL_OUT(req, buf, strlen(buf)); 257209371Smav return (error); 258209371Smav} 259209371SmavSYSCTL_PROC(_kern_eventtimer, OID_AUTO, choice, 260209371Smav CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 261209371Smav 0, 0, sysctl_kern_eventtimer_choice, "A", "Present event timers"); 262209371Smav 263