xref: /netbsd-src/sys/external/bsd/drm2/linux/linux_hrtimer.c (revision 147f7663cf2cbe2cc5e5fbd2b3d4b5956344b310)
1*147f7663Sriastradh /*	$NetBSD: linux_hrtimer.c,v 1.3 2021/12/19 11:55:47 riastradh Exp $	*/
26934fb94Sriastradh 
36934fb94Sriastradh /*-
46934fb94Sriastradh  * Copyright (c) 2021 The NetBSD Foundation, Inc.
56934fb94Sriastradh  * All rights reserved.
66934fb94Sriastradh  *
76934fb94Sriastradh  * Redistribution and use in source and binary forms, with or without
86934fb94Sriastradh  * modification, are permitted provided that the following conditions
96934fb94Sriastradh  * are met:
106934fb94Sriastradh  * 1. Redistributions of source code must retain the above copyright
116934fb94Sriastradh  *    notice, this list of conditions and the following disclaimer.
126934fb94Sriastradh  * 2. Redistributions in binary form must reproduce the above copyright
136934fb94Sriastradh  *    notice, this list of conditions and the following disclaimer in the
146934fb94Sriastradh  *    documentation and/or other materials provided with the distribution.
156934fb94Sriastradh  *
166934fb94Sriastradh  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
176934fb94Sriastradh  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
186934fb94Sriastradh  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
196934fb94Sriastradh  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
206934fb94Sriastradh  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
216934fb94Sriastradh  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
226934fb94Sriastradh  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
236934fb94Sriastradh  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
246934fb94Sriastradh  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
256934fb94Sriastradh  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
266934fb94Sriastradh  * POSSIBILITY OF SUCH DAMAGE.
276934fb94Sriastradh  */
286934fb94Sriastradh 
296934fb94Sriastradh #include <sys/cdefs.h>
30*147f7663Sriastradh __KERNEL_RCSID(0, "$NetBSD: linux_hrtimer.c,v 1.3 2021/12/19 11:55:47 riastradh Exp $");
316934fb94Sriastradh 
326934fb94Sriastradh #include <sys/types.h>
336934fb94Sriastradh #include <sys/callout.h>
346934fb94Sriastradh 
356934fb94Sriastradh #include <linux/hrtimer.h>
366934fb94Sriastradh #include <linux/ktime.h>
376934fb94Sriastradh 
386934fb94Sriastradh static void hrtimer_fire(void *);
396934fb94Sriastradh 
406934fb94Sriastradh void
hrtimer_init(struct hrtimer * hrt,clockid_t clkid,enum hrtimer_mode mode)416934fb94Sriastradh hrtimer_init(struct hrtimer *hrt, clockid_t clkid, enum hrtimer_mode mode)
426934fb94Sriastradh {
436934fb94Sriastradh 
446934fb94Sriastradh 	KASSERTMSG(clkid == CLOCK_MONOTONIC, "clkid %d", clkid);
456934fb94Sriastradh 
46*147f7663Sriastradh 	callout_init(&hrt->hrt_ch, CALLOUT_MPSAFE);
47*147f7663Sriastradh 	callout_setfunc(&hrt->hrt_ch, hrtimer_fire, hrt);
48*147f7663Sriastradh 	hrt->hrt_mode = mode;
496934fb94Sriastradh }
506934fb94Sriastradh 
516934fb94Sriastradh static void
_hrtimer_schedule(struct hrtimer * hrt)526934fb94Sriastradh _hrtimer_schedule(struct hrtimer *hrt)
536934fb94Sriastradh {
546934fb94Sriastradh 	int delta;
556934fb94Sriastradh 
56*147f7663Sriastradh 	switch (hrt->hrt_mode) {
576934fb94Sriastradh 	case HRTIMER_MODE_ABS:
586934fb94Sriastradh 		panic("absolute hrtimer NYI");
596934fb94Sriastradh 		break;
606934fb94Sriastradh 	case HRTIMER_MODE_REL:
61*147f7663Sriastradh 		delta = ktime_to_ms(hrt->hrt_expires);
626934fb94Sriastradh 		break;
636934fb94Sriastradh 	default:
64*147f7663Sriastradh 		panic("invalid hrtimer mode %d", hrt->hrt_mode);
656934fb94Sriastradh 	}
66*147f7663Sriastradh 	callout_schedule(&hrt->hrt_ch, delta);
676934fb94Sriastradh }
686934fb94Sriastradh 
696934fb94Sriastradh static void
hrtimer_fire(void * cookie)706934fb94Sriastradh hrtimer_fire(void *cookie)
716934fb94Sriastradh {
726934fb94Sriastradh 	struct hrtimer *hrt = cookie;
736934fb94Sriastradh 
746934fb94Sriastradh 	switch ((*hrt->function)(hrt)) {
756934fb94Sriastradh 	case HRTIMER_RESTART:
766934fb94Sriastradh 		_hrtimer_schedule(hrt);
776934fb94Sriastradh 		break;
786934fb94Sriastradh 	case HRTIMER_NORESTART:
796934fb94Sriastradh 		break;
806934fb94Sriastradh 	}
816934fb94Sriastradh 
82*147f7663Sriastradh 	callout_ack(&hrt->hrt_ch);
836934fb94Sriastradh }
846934fb94Sriastradh 
856934fb94Sriastradh void
hrtimer_set_expires(struct hrtimer * hrt,ktime_t expires)866934fb94Sriastradh hrtimer_set_expires(struct hrtimer *hrt, ktime_t expires)
876934fb94Sriastradh {
886934fb94Sriastradh 
89*147f7663Sriastradh 	hrt->hrt_expires = expires;
906934fb94Sriastradh }
916934fb94Sriastradh 
926934fb94Sriastradh void
hrtimer_add_expires_ns(struct hrtimer * hrt,uint64_t ns)936934fb94Sriastradh hrtimer_add_expires_ns(struct hrtimer *hrt, uint64_t ns)
946934fb94Sriastradh {
956934fb94Sriastradh 
96*147f7663Sriastradh 	hrt->hrt_expires = ktime_add_ns(hrt->hrt_expires, ns);
976934fb94Sriastradh }
986934fb94Sriastradh 
996934fb94Sriastradh void
hrtimer_start(struct hrtimer * hrt,ktime_t expires,enum hrtimer_mode mode)1006934fb94Sriastradh hrtimer_start(struct hrtimer *hrt, ktime_t expires, enum hrtimer_mode mode)
1016934fb94Sriastradh {
1026934fb94Sriastradh 
1036934fb94Sriastradh 	hrtimer_start_range_ns(hrt, expires, 0, mode);
1046934fb94Sriastradh }
1056934fb94Sriastradh 
1066934fb94Sriastradh void
hrtimer_start_range_ns(struct hrtimer * hrt,ktime_t expires,uint64_t range_ns,enum hrtimer_mode mode)1076934fb94Sriastradh hrtimer_start_range_ns(struct hrtimer *hrt, ktime_t expires, uint64_t range_ns,
1086934fb94Sriastradh     enum hrtimer_mode mode)
1096934fb94Sriastradh {
1106934fb94Sriastradh 
111*147f7663Sriastradh 	hrt->hrt_expires = expires;
1126934fb94Sriastradh 	(void)range_ns;
113*147f7663Sriastradh 	hrt->hrt_mode = mode;
1146934fb94Sriastradh 	_hrtimer_schedule(hrt);
1156934fb94Sriastradh }
1166934fb94Sriastradh 
1176934fb94Sriastradh int
hrtimer_cancel(struct hrtimer * hrt)1186934fb94Sriastradh hrtimer_cancel(struct hrtimer *hrt)
1196934fb94Sriastradh {
1206934fb94Sriastradh 	bool active;
1216934fb94Sriastradh 
1226934fb94Sriastradh 	/*
1236934fb94Sriastradh 	 * Halt the callout and ascertain whether the hrtimer was
1246934fb94Sriastradh 	 * active when we invoked hrtimer_cancel.
1256934fb94Sriastradh 	 */
126*147f7663Sriastradh 	if (callout_halt(&hrt->hrt_ch, NULL)) {
1276934fb94Sriastradh 		/* Callout expired, meaning it was active.  */
1286934fb94Sriastradh 		active = true;
1296934fb94Sriastradh 	} else {
1306934fb94Sriastradh 		/*
1316934fb94Sriastradh 		 * Callout had not yet expired.  It will not expire
1326934fb94Sriastradh 		 * now, so callout_pending is now stable and
1336934fb94Sriastradh 		 * corresponds with whether the hrtimer was active or
1346934fb94Sriastradh 		 * not.
1356934fb94Sriastradh 		 */
136*147f7663Sriastradh 		active = callout_pending(&hrt->hrt_ch);
1376934fb94Sriastradh 	}
138c7c0494bSriastradh 	return active;
139c7c0494bSriastradh }
140c7c0494bSriastradh 
1416934fb94Sriastradh bool
hrtimer_active(struct hrtimer * hrt)1426934fb94Sriastradh hrtimer_active(struct hrtimer *hrt)
1436934fb94Sriastradh {
1446934fb94Sriastradh 
1456934fb94Sriastradh 	/*
1466934fb94Sriastradh 	 * If the callout has been scheduled, but has not yet fired,
1476934fb94Sriastradh 	 * then it is pending.
1486934fb94Sriastradh 	 *
1496934fb94Sriastradh 	 * If the callout has fired, but has not yet reached
1506934fb94Sriastradh 	 * callout_ack, then it is invoking.
1516934fb94Sriastradh 	 */
152*147f7663Sriastradh 	return callout_pending(&hrt->hrt_ch) || callout_invoking(&hrt->hrt_ch);
1536934fb94Sriastradh }
1546934fb94Sriastradh 
1556934fb94Sriastradh uint64_t
hrtimer_forward(struct hrtimer * hrt,ktime_t now,ktime_t period)1566934fb94Sriastradh hrtimer_forward(struct hrtimer *hrt, ktime_t now, ktime_t period)
1576934fb94Sriastradh {
1586934fb94Sriastradh 	uint64_t now_ms, period_ms, expires_ms, nperiods;
1596934fb94Sriastradh 
160*147f7663Sriastradh 	KASSERT(!callout_pending(&hrt->hrt_ch));
1616934fb94Sriastradh 
1626934fb94Sriastradh 	/*
1636934fb94Sriastradh 	 * Can't get better than 10ms precision (or ~1ms if you set
1646934fb94Sriastradh 	 * HZ=1000) so not much point in doing this arithmetic at finer
1656934fb94Sriastradh 	 * resolution than ms.
1666934fb94Sriastradh 	 */
1676934fb94Sriastradh 	now_ms = ktime_to_ms(now);
1686934fb94Sriastradh 	period_ms = ktime_to_ms(period);
169*147f7663Sriastradh 	expires_ms = ktime_to_ms(hrt->hrt_expires);
1706934fb94Sriastradh 
1716934fb94Sriastradh 	/* If it hasn't yet expired, no overruns.  */
1726934fb94Sriastradh 	if (now_ms < expires_ms)
1736934fb94Sriastradh 		return 0;
1746934fb94Sriastradh 
1756934fb94Sriastradh 	/* Advance it by as many periods as it should have fired.  */
1766934fb94Sriastradh 	/* XXX fenceposts */
1776934fb94Sriastradh 	nperiods = howmany(now_ms - expires_ms, period_ms);
178*147f7663Sriastradh 	hrt->hrt_expires = ktime_add_ns(hrt->hrt_expires,
179*147f7663Sriastradh 	    1000000*nperiods*period_ms);
1806934fb94Sriastradh 
1816934fb94Sriastradh 	return nperiods;
1826934fb94Sriastradh }
1836934fb94Sriastradh 
1846934fb94Sriastradh uint64_t
hrtimer_forward_now(struct hrtimer * hrt,ktime_t period)1856934fb94Sriastradh hrtimer_forward_now(struct hrtimer *hrt, ktime_t period)
1866934fb94Sriastradh {
1876934fb94Sriastradh 
1886934fb94Sriastradh 	return hrtimer_forward(hrt, ktime_get(), period);
1896934fb94Sriastradh }
190