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