1875b8844SAlexander Motin /*- 2*8a36da99SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*8a36da99SPedro F. Giffuni * 4fdc5dd2dSAlexander Motin * Copyright (c) 2010-2013 Alexander Motin <mav@FreeBSD.org> 5875b8844SAlexander Motin * All rights reserved. 6875b8844SAlexander Motin * 7875b8844SAlexander Motin * Redistribution and use in source and binary forms, with or without 8875b8844SAlexander Motin * modification, are permitted provided that the following conditions 9875b8844SAlexander Motin * are met: 10875b8844SAlexander Motin * 1. Redistributions of source code must retain the above copyright 11875b8844SAlexander Motin * notice, this list of conditions and the following disclaimer, 12875b8844SAlexander Motin * without modification, immediately at the beginning of the file. 13875b8844SAlexander Motin * 2. Redistributions in binary form must reproduce the above copyright 14875b8844SAlexander Motin * notice, this list of conditions and the following disclaimer in the 15875b8844SAlexander Motin * documentation and/or other materials provided with the distribution. 16875b8844SAlexander Motin * 17875b8844SAlexander Motin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18875b8844SAlexander Motin * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19875b8844SAlexander Motin * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20875b8844SAlexander Motin * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21875b8844SAlexander Motin * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22875b8844SAlexander Motin * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23875b8844SAlexander Motin * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24875b8844SAlexander Motin * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25875b8844SAlexander Motin * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26875b8844SAlexander Motin * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27875b8844SAlexander Motin */ 28875b8844SAlexander Motin 29875b8844SAlexander Motin #include <sys/cdefs.h> 30875b8844SAlexander Motin __FBSDID("$FreeBSD$"); 31875b8844SAlexander Motin 32875b8844SAlexander Motin #include <sys/param.h> 33875b8844SAlexander Motin #include <sys/kernel.h> 3491d9eda2SIan Lepore #include <sys/sbuf.h> 35875b8844SAlexander Motin #include <sys/sysctl.h> 36875b8844SAlexander Motin #include <sys/systm.h> 37875b8844SAlexander Motin #include <sys/queue.h> 38875b8844SAlexander Motin #include <sys/timeet.h> 39875b8844SAlexander Motin 409e24f238SIan Lepore #include "opt_timer.h" 419e24f238SIan Lepore 42875b8844SAlexander Motin SLIST_HEAD(et_eventtimers_list, eventtimer); 43875b8844SAlexander Motin static struct et_eventtimers_list eventtimers = SLIST_HEAD_INITIALIZER(et_eventtimers); 44875b8844SAlexander Motin 45875b8844SAlexander Motin struct mtx et_eventtimers_mtx; 46a157e425SAlexander Motin MTX_SYSINIT(et_eventtimers_init, &et_eventtimers_mtx, "et_mtx", MTX_DEF); 47875b8844SAlexander Motin 48875b8844SAlexander Motin SYSCTL_NODE(_kern, OID_AUTO, eventtimer, CTLFLAG_RW, 0, "Event timers"); 496472ac3dSEd Schouten static SYSCTL_NODE(_kern_eventtimer, OID_AUTO, et, CTLFLAG_RW, 0, ""); 50875b8844SAlexander Motin 51875b8844SAlexander Motin /* 52875b8844SAlexander Motin * Register a new event timer hardware. 53875b8844SAlexander Motin */ 54875b8844SAlexander Motin int 55875b8844SAlexander Motin et_register(struct eventtimer *et) 56875b8844SAlexander Motin { 57875b8844SAlexander Motin struct eventtimer *tmp, *next; 58875b8844SAlexander Motin 59875b8844SAlexander Motin if (et->et_quality >= 0 || bootverbose) { 6090baf564SAlexander Motin if (et->et_frequency == 0) { 6190baf564SAlexander Motin printf("Event timer \"%s\" quality %d\n", 6290baf564SAlexander Motin et->et_name, et->et_quality); 6390baf564SAlexander Motin } else { 6490baf564SAlexander Motin printf("Event timer \"%s\" " 6590baf564SAlexander Motin "frequency %ju Hz quality %d\n", 66875b8844SAlexander Motin et->et_name, (uintmax_t)et->et_frequency, 67875b8844SAlexander Motin et->et_quality); 68875b8844SAlexander Motin } 6990baf564SAlexander Motin } 70fdc5dd2dSAlexander Motin KASSERT(et->et_start, ("et_register: timer has no start function")); 71fd0f5970SEd Schouten et->et_sysctl = SYSCTL_ADD_NODE_WITH_LABEL(NULL, 72875b8844SAlexander Motin SYSCTL_STATIC_CHILDREN(_kern_eventtimer_et), OID_AUTO, et->et_name, 73fd0f5970SEd Schouten CTLFLAG_RW, 0, "event timer description", "eventtimer"); 74fbbb13f9SMatthew D Fleming SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 75875b8844SAlexander Motin "flags", CTLFLAG_RD, &(et->et_flags), 0, 76875b8844SAlexander Motin "Event timer capabilities"); 77fbbb13f9SMatthew D Fleming SYSCTL_ADD_UQUAD(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 78e88f9fb4SAlexander Motin "frequency", CTLFLAG_RD, &(et->et_frequency), 79875b8844SAlexander Motin "Event timer base frequency"); 80875b8844SAlexander Motin SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO, 81875b8844SAlexander Motin "quality", CTLFLAG_RD, &(et->et_quality), 0, 82875b8844SAlexander Motin "Goodness of event timer"); 83875b8844SAlexander Motin ET_LOCK(); 84875b8844SAlexander Motin if (SLIST_EMPTY(&eventtimers) || 85875b8844SAlexander Motin SLIST_FIRST(&eventtimers)->et_quality < et->et_quality) { 86875b8844SAlexander Motin SLIST_INSERT_HEAD(&eventtimers, et, et_all); 87875b8844SAlexander Motin } else { 88875b8844SAlexander Motin SLIST_FOREACH(tmp, &eventtimers, et_all) { 89875b8844SAlexander Motin next = SLIST_NEXT(tmp, et_all); 90875b8844SAlexander Motin if (next == NULL || next->et_quality < et->et_quality) { 91875b8844SAlexander Motin SLIST_INSERT_AFTER(tmp, et, et_all); 92875b8844SAlexander Motin break; 93875b8844SAlexander Motin } 94875b8844SAlexander Motin } 95875b8844SAlexander Motin } 96875b8844SAlexander Motin ET_UNLOCK(); 97875b8844SAlexander Motin return (0); 98875b8844SAlexander Motin } 99875b8844SAlexander Motin 100875b8844SAlexander Motin /* 101875b8844SAlexander Motin * Deregister event timer hardware. 102875b8844SAlexander Motin */ 103875b8844SAlexander Motin int 104875b8844SAlexander Motin et_deregister(struct eventtimer *et) 105875b8844SAlexander Motin { 106875b8844SAlexander Motin int err = 0; 107875b8844SAlexander Motin 108875b8844SAlexander Motin if (et->et_deregister_cb != NULL) { 109875b8844SAlexander Motin if ((err = et->et_deregister_cb(et, et->et_arg)) != 0) 110875b8844SAlexander Motin return (err); 111875b8844SAlexander Motin } 112875b8844SAlexander Motin 113875b8844SAlexander Motin ET_LOCK(); 114875b8844SAlexander Motin SLIST_REMOVE(&eventtimers, et, eventtimer, et_all); 115875b8844SAlexander Motin ET_UNLOCK(); 116875b8844SAlexander Motin sysctl_remove_oid(et->et_sysctl, 1, 1); 117875b8844SAlexander Motin return (0); 118875b8844SAlexander Motin } 119875b8844SAlexander Motin 120875b8844SAlexander Motin /* 121cfc4b56bSIan Lepore * Change the frequency of the given timer. If it is the active timer, 122cfc4b56bSIan Lepore * reconfigure it on all CPUs (reschedules all current events based on the new 123cfc4b56bSIan Lepore * timer frequency). 124cfc4b56bSIan Lepore */ 125cfc4b56bSIan Lepore void 126cfc4b56bSIan Lepore et_change_frequency(struct eventtimer *et, uint64_t newfreq) 127cfc4b56bSIan Lepore { 128cfc4b56bSIan Lepore 1299e24f238SIan Lepore #ifndef NO_EVENTTIMERS 130cfc4b56bSIan Lepore cpu_et_frequency(et, newfreq); 1319e24f238SIan Lepore #endif 132cfc4b56bSIan Lepore } 133cfc4b56bSIan Lepore 134cfc4b56bSIan Lepore /* 135875b8844SAlexander Motin * Find free event timer hardware with specified parameters. 136875b8844SAlexander Motin */ 137875b8844SAlexander Motin struct eventtimer * 138875b8844SAlexander Motin et_find(const char *name, int check, int want) 139875b8844SAlexander Motin { 140875b8844SAlexander Motin struct eventtimer *et = NULL; 141875b8844SAlexander Motin 142875b8844SAlexander Motin SLIST_FOREACH(et, &eventtimers, et_all) { 143875b8844SAlexander Motin if (et->et_active) 144875b8844SAlexander Motin continue; 145875b8844SAlexander Motin if (name != NULL && strcasecmp(et->et_name, name) != 0) 146875b8844SAlexander Motin continue; 147875b8844SAlexander Motin if (name == NULL && et->et_quality < 0) 148875b8844SAlexander Motin continue; 149875b8844SAlexander Motin if ((et->et_flags & check) != want) 150875b8844SAlexander Motin continue; 151875b8844SAlexander Motin break; 152875b8844SAlexander Motin } 153875b8844SAlexander Motin return (et); 154875b8844SAlexander Motin } 155875b8844SAlexander Motin 156875b8844SAlexander Motin /* 157875b8844SAlexander Motin * Initialize event timer hardware. Set callbacks. 158875b8844SAlexander Motin */ 159875b8844SAlexander Motin int 160875b8844SAlexander Motin et_init(struct eventtimer *et, et_event_cb_t *event, 161875b8844SAlexander Motin et_deregister_cb_t *deregister, void *arg) 162875b8844SAlexander Motin { 163875b8844SAlexander Motin 164875b8844SAlexander Motin if (event == NULL) 165875b8844SAlexander Motin return (EINVAL); 166875b8844SAlexander Motin if (et->et_active) 167875b8844SAlexander Motin return (EBUSY); 168875b8844SAlexander Motin 169875b8844SAlexander Motin et->et_active = 1; 170875b8844SAlexander Motin et->et_event_cb = event; 171875b8844SAlexander Motin et->et_deregister_cb = deregister; 172875b8844SAlexander Motin et->et_arg = arg; 173875b8844SAlexander Motin return (0); 174875b8844SAlexander Motin } 175875b8844SAlexander Motin 176875b8844SAlexander Motin /* 177875b8844SAlexander Motin * Start event timer hardware. 178875b8844SAlexander Motin * first - delay before first tick. 179875b8844SAlexander Motin * period - period of subsequent periodic ticks. 180875b8844SAlexander Motin */ 181875b8844SAlexander Motin int 182fdc5dd2dSAlexander Motin et_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 183875b8844SAlexander Motin { 184875b8844SAlexander Motin 185875b8844SAlexander Motin if (!et->et_active) 186875b8844SAlexander Motin return (ENXIO); 187fdc5dd2dSAlexander Motin KASSERT(period >= 0, ("et_start: negative period")); 188fdc5dd2dSAlexander Motin KASSERT((et->et_flags & ET_FLAGS_PERIODIC) || period == 0, 189fdc5dd2dSAlexander Motin ("et_start: period specified for oneshot-only timer")); 190ca9feb49SAlexander Motin KASSERT((et->et_flags & ET_FLAGS_ONESHOT) || period != 0, 191fdc5dd2dSAlexander Motin ("et_start: period not specified for periodic-only timer")); 192fdc5dd2dSAlexander Motin if (period != 0) { 193fdc5dd2dSAlexander Motin if (period < et->et_min_period) 194fdc5dd2dSAlexander Motin period = et->et_min_period; 195fdc5dd2dSAlexander Motin else if (period > et->et_max_period) 196fdc5dd2dSAlexander Motin period = et->et_max_period; 19751636352SAlexander Motin } 198fdc5dd2dSAlexander Motin if (period == 0 || first != 0) { 199fdc5dd2dSAlexander Motin if (first < et->et_min_period) 200fdc5dd2dSAlexander Motin first = et->et_min_period; 201fdc5dd2dSAlexander Motin else if (first > et->et_max_period) 202fdc5dd2dSAlexander Motin first = et->et_max_period; 20351636352SAlexander Motin } 204875b8844SAlexander Motin return (et->et_start(et, first, period)); 205875b8844SAlexander Motin } 206875b8844SAlexander Motin 207875b8844SAlexander Motin /* Stop event timer hardware. */ 208875b8844SAlexander Motin int 209875b8844SAlexander Motin et_stop(struct eventtimer *et) 210875b8844SAlexander Motin { 211875b8844SAlexander Motin 212875b8844SAlexander Motin if (!et->et_active) 213875b8844SAlexander Motin return (ENXIO); 214875b8844SAlexander Motin if (et->et_stop) 215875b8844SAlexander Motin return (et->et_stop(et)); 216875b8844SAlexander Motin return (0); 217875b8844SAlexander Motin } 218875b8844SAlexander Motin 219875b8844SAlexander Motin /* Mark event timer hardware as broken. */ 220875b8844SAlexander Motin int 221875b8844SAlexander Motin et_ban(struct eventtimer *et) 222875b8844SAlexander Motin { 223875b8844SAlexander Motin 224875b8844SAlexander Motin et->et_flags &= ~(ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT); 225875b8844SAlexander Motin return (0); 226875b8844SAlexander Motin } 227875b8844SAlexander Motin 228875b8844SAlexander Motin /* Free event timer hardware. */ 229875b8844SAlexander Motin int 230875b8844SAlexander Motin et_free(struct eventtimer *et) 231875b8844SAlexander Motin { 232875b8844SAlexander Motin 233875b8844SAlexander Motin if (!et->et_active) 234875b8844SAlexander Motin return (ENXIO); 235875b8844SAlexander Motin 236875b8844SAlexander Motin et->et_active = 0; 237875b8844SAlexander Motin return (0); 238875b8844SAlexander Motin } 239875b8844SAlexander Motin 240f62fbd30SIan Lepore /* Report list of supported event timer hardware via sysctl. */ 241875b8844SAlexander Motin static int 242875b8844SAlexander Motin sysctl_kern_eventtimer_choice(SYSCTL_HANDLER_ARGS) 243875b8844SAlexander Motin { 24491d9eda2SIan Lepore struct sbuf sb; 245875b8844SAlexander Motin struct eventtimer *et; 24691d9eda2SIan Lepore int error; 247875b8844SAlexander Motin 248ff352d89SIan Lepore sbuf_new(&sb, NULL, 256, SBUF_AUTOEXTEND | SBUF_INCLUDENUL); 24991d9eda2SIan Lepore 250875b8844SAlexander Motin ET_LOCK(); 251875b8844SAlexander Motin SLIST_FOREACH(et, &eventtimers, et_all) { 25291d9eda2SIan Lepore if (et != SLIST_FIRST(&eventtimers)) 25391d9eda2SIan Lepore sbuf_putc(&sb, ' '); 25491d9eda2SIan Lepore sbuf_printf(&sb, "%s(%d)", et->et_name, et->et_quality); 255875b8844SAlexander Motin } 256875b8844SAlexander Motin ET_UNLOCK(); 25791d9eda2SIan Lepore 25891d9eda2SIan Lepore error = sbuf_finish(&sb); 259ff352d89SIan Lepore if (error == 0) 260ff352d89SIan Lepore error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb)); 26191d9eda2SIan Lepore sbuf_delete(&sb); 262875b8844SAlexander Motin return (error); 263875b8844SAlexander Motin } 264875b8844SAlexander Motin SYSCTL_PROC(_kern_eventtimer, OID_AUTO, choice, 265875b8844SAlexander Motin CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 266875b8844SAlexander Motin 0, 0, sysctl_kern_eventtimer_choice, "A", "Present event timers"); 267875b8844SAlexander Motin 268