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
37209371SmavSLIST_HEAD(et_eventtimers_list, eventtimer);
38209371Smavstatic struct et_eventtimers_list eventtimers = SLIST_HEAD_INITIALIZER(et_eventtimers);
39209371Smav
40209371Smavstruct mtx	et_eventtimers_mtx;
41212541SmavMTX_SYSINIT(et_eventtimers_init, &et_eventtimers_mtx, "et_mtx", MTX_DEF);
42209371Smav
43209371SmavSYSCTL_NODE(_kern, OID_AUTO, eventtimer, CTLFLAG_RW, 0, "Event timers");
44227309Sedstatic SYSCTL_NODE(_kern_eventtimer, OID_AUTO, et, CTLFLAG_RW, 0, "");
45209371Smav
46209371Smav/*
47209371Smav * Register a new event timer hardware.
48209371Smav */
49209371Smavint
50209371Smavet_register(struct eventtimer *et)
51209371Smav{
52209371Smav	struct eventtimer *tmp, *next;
53209371Smav
54209371Smav	if (et->et_quality >= 0 || bootverbose) {
55212479Smav		if (et->et_frequency == 0) {
56212479Smav			printf("Event timer \"%s\" quality %d\n",
57212479Smav			    et->et_name, et->et_quality);
58212479Smav		} else {
59212479Smav			printf("Event timer \"%s\" "
60212479Smav			    "frequency %ju Hz quality %d\n",
61212479Smav			    et->et_name, (uintmax_t)et->et_frequency,
62212479Smav			    et->et_quality);
63212479Smav		}
64209371Smav	}
65247463Smav	KASSERT(et->et_start, ("et_register: timer has no start function"));
66209371Smav	et->et_sysctl = SYSCTL_ADD_NODE(NULL,
67209371Smav	    SYSCTL_STATIC_CHILDREN(_kern_eventtimer_et), OID_AUTO, et->et_name,
68209371Smav	    CTLFLAG_RW, 0, "event timer description");
69217326Smdf	SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO,
70209371Smav	    "flags", CTLFLAG_RD, &(et->et_flags), 0,
71209371Smav	    "Event timer capabilities");
72217326Smdf	SYSCTL_ADD_UQUAD(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO,
73210346Smav	    "frequency", CTLFLAG_RD, &(et->et_frequency),
74209371Smav	    "Event timer base frequency");
75209371Smav	SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO,
76209371Smav	    "quality", CTLFLAG_RD, &(et->et_quality), 0,
77209371Smav	    "Goodness of event timer");
78209371Smav	ET_LOCK();
79209371Smav	if (SLIST_EMPTY(&eventtimers) ||
80209371Smav	    SLIST_FIRST(&eventtimers)->et_quality < et->et_quality) {
81209371Smav		SLIST_INSERT_HEAD(&eventtimers, et, et_all);
82209371Smav	} else {
83209371Smav		SLIST_FOREACH(tmp, &eventtimers, et_all) {
84209371Smav			next = SLIST_NEXT(tmp, et_all);
85209371Smav			if (next == NULL || next->et_quality < et->et_quality) {
86209371Smav				SLIST_INSERT_AFTER(tmp, et, et_all);
87209371Smav				break;
88209371Smav			}
89209371Smav		}
90209371Smav	}
91209371Smav	ET_UNLOCK();
92209371Smav	return (0);
93209371Smav}
94209371Smav
95209371Smav/*
96209371Smav * Deregister event timer hardware.
97209371Smav */
98209371Smavint
99209371Smavet_deregister(struct eventtimer *et)
100209371Smav{
101209371Smav	int err = 0;
102209371Smav
103209371Smav	if (et->et_deregister_cb != NULL) {
104209371Smav		if ((err = et->et_deregister_cb(et, et->et_arg)) != 0)
105209371Smav			return (err);
106209371Smav	}
107209371Smav
108209371Smav	ET_LOCK();
109209371Smav	SLIST_REMOVE(&eventtimers, et, eventtimer, et_all);
110209371Smav	ET_UNLOCK();
111209371Smav	sysctl_remove_oid(et->et_sysctl, 1, 1);
112209371Smav	return (0);
113209371Smav}
114209371Smav
115209371Smav/*
116209371Smav * Find free event timer hardware with specified parameters.
117209371Smav */
118209371Smavstruct eventtimer *
119209371Smavet_find(const char *name, int check, int want)
120209371Smav{
121209371Smav	struct eventtimer *et = NULL;
122209371Smav
123209371Smav	SLIST_FOREACH(et, &eventtimers, et_all) {
124209371Smav		if (et->et_active)
125209371Smav			continue;
126209371Smav		if (name != NULL && strcasecmp(et->et_name, name) != 0)
127209371Smav			continue;
128209371Smav		if (name == NULL && et->et_quality < 0)
129209371Smav			continue;
130209371Smav		if ((et->et_flags & check) != want)
131209371Smav			continue;
132209371Smav		break;
133209371Smav	}
134209371Smav	return (et);
135209371Smav}
136209371Smav
137209371Smav/*
138209371Smav * Initialize event timer hardware. Set callbacks.
139209371Smav */
140209371Smavint
141209371Smavet_init(struct eventtimer *et, et_event_cb_t *event,
142209371Smav    et_deregister_cb_t *deregister, void *arg)
143209371Smav{
144209371Smav
145209371Smav	if (event == NULL)
146209371Smav		return (EINVAL);
147209371Smav	if (et->et_active)
148209371Smav		return (EBUSY);
149209371Smav
150209371Smav	et->et_active = 1;
151209371Smav	et->et_event_cb = event;
152209371Smav	et->et_deregister_cb = deregister;
153209371Smav	et->et_arg = arg;
154209371Smav	return (0);
155209371Smav}
156209371Smav
157209371Smav/*
158209371Smav * Start event timer hardware.
159209371Smav * first - delay before first tick.
160209371Smav * period - period of subsequent periodic ticks.
161209371Smav */
162209371Smavint
163247463Smavet_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
164209371Smav{
165209371Smav
166209371Smav	if (!et->et_active)
167209371Smav		return (ENXIO);
168247463Smav	KASSERT(period >= 0, ("et_start: negative period"));
169247463Smav	KASSERT((et->et_flags & ET_FLAGS_PERIODIC) || period == 0,
170247463Smav		("et_start: period specified for oneshot-only timer"));
171248230Smav	KASSERT((et->et_flags & ET_FLAGS_ONESHOT) || period != 0,
172247463Smav		("et_start: period not specified for periodic-only timer"));
173247463Smav	if (period != 0) {
174247463Smav		if (period < et->et_min_period)
175247463Smav		        period = et->et_min_period;
176247463Smav		else if (period > et->et_max_period)
177247463Smav		        period = et->et_max_period;
178210290Smav	}
179247463Smav	if (period == 0 || first != 0) {
180247463Smav		if (first < et->et_min_period)
181247463Smav		        first = et->et_min_period;
182247463Smav		else if (first > et->et_max_period)
183247463Smav		        first = et->et_max_period;
184210290Smav	}
185247463Smav	return (et->et_start(et, first, period));
186209371Smav}
187209371Smav
188209371Smav/* Stop event timer hardware. */
189209371Smavint
190209371Smavet_stop(struct eventtimer *et)
191209371Smav{
192209371Smav
193209371Smav	if (!et->et_active)
194209371Smav		return (ENXIO);
195209371Smav	if (et->et_stop)
196209371Smav		return (et->et_stop(et));
197209371Smav	return (0);
198209371Smav}
199209371Smav
200209371Smav/* Mark event timer hardware as broken. */
201209371Smavint
202209371Smavet_ban(struct eventtimer *et)
203209371Smav{
204209371Smav
205209371Smav	et->et_flags &= ~(ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT);
206209371Smav	return (0);
207209371Smav}
208209371Smav
209209371Smav/* Free event timer hardware. */
210209371Smavint
211209371Smavet_free(struct eventtimer *et)
212209371Smav{
213209371Smav
214209371Smav	if (!et->et_active)
215209371Smav		return (ENXIO);
216209371Smav
217209371Smav	et->et_active = 0;
218209371Smav	return (0);
219209371Smav}
220209371Smav
221209371Smav/* Report list of supported event timers hardware via sysctl. */
222209371Smavstatic int
223209371Smavsysctl_kern_eventtimer_choice(SYSCTL_HANDLER_ARGS)
224209371Smav{
225209371Smav	char buf[512], *spc;
226209371Smav	struct eventtimer *et;
227209371Smav	int error, off;
228209371Smav
229209371Smav	spc = "";
230209371Smav	error = 0;
231212223Smav	buf[0] = 0;
232209371Smav	off = 0;
233209371Smav	ET_LOCK();
234209371Smav	SLIST_FOREACH(et, &eventtimers, et_all) {
235209371Smav		off += snprintf(buf + off, sizeof(buf) - off, "%s%s(%d)",
236209371Smav		    spc, et->et_name, et->et_quality);
237209371Smav		spc = " ";
238209371Smav	}
239209371Smav	ET_UNLOCK();
240209371Smav	error = SYSCTL_OUT(req, buf, strlen(buf));
241209371Smav	return (error);
242209371Smav}
243209371SmavSYSCTL_PROC(_kern_eventtimer, OID_AUTO, choice,
244209371Smav    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
245209371Smav    0, 0, sysctl_kern_eventtimer_choice, "A", "Present event timers");
246209371Smav
247