xref: /netbsd-src/sys/kern/kern_pmf.c (revision 89f86b67162492beae87b9d4a96dcfa3769473a1)
1*89f86b67Sriastradh /* $NetBSD: kern_pmf.c,v 1.51 2022/08/24 11:41:39 riastradh Exp $ */
24c1d81b2Sjmcneill 
34c1d81b2Sjmcneill /*-
44c1d81b2Sjmcneill  * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
54c1d81b2Sjmcneill  * All rights reserved.
64c1d81b2Sjmcneill  *
74c1d81b2Sjmcneill  * Redistribution and use in source and binary forms, with or without
84c1d81b2Sjmcneill  * modification, are permitted provided that the following conditions
94c1d81b2Sjmcneill  * are met:
104c1d81b2Sjmcneill  * 1. Redistributions of source code must retain the above copyright
114c1d81b2Sjmcneill  *    notice, this list of conditions and the following disclaimer.
124c1d81b2Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
134c1d81b2Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
144c1d81b2Sjmcneill  *    documentation and/or other materials provided with the distribution.
154c1d81b2Sjmcneill  *
164c1d81b2Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
174c1d81b2Sjmcneill  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
184c1d81b2Sjmcneill  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
194c1d81b2Sjmcneill  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
204c1d81b2Sjmcneill  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
214c1d81b2Sjmcneill  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
224c1d81b2Sjmcneill  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
234c1d81b2Sjmcneill  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
244c1d81b2Sjmcneill  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
254c1d81b2Sjmcneill  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
264c1d81b2Sjmcneill  * POSSIBILITY OF SUCH DAMAGE.
274c1d81b2Sjmcneill  */
284c1d81b2Sjmcneill 
294c1d81b2Sjmcneill #include <sys/cdefs.h>
30*89f86b67Sriastradh __KERNEL_RCSID(0, "$NetBSD: kern_pmf.c,v 1.51 2022/08/24 11:41:39 riastradh Exp $");
314c1d81b2Sjmcneill 
324c1d81b2Sjmcneill #include <sys/types.h>
334c1d81b2Sjmcneill #include <sys/param.h>
349d9978e5Sdyoung #include <sys/kmem.h>
354c1d81b2Sjmcneill #include <sys/buf.h>
364c1d81b2Sjmcneill #include <sys/callout.h>
374c1d81b2Sjmcneill #include <sys/kernel.h>
384c1d81b2Sjmcneill #include <sys/device.h>
39b6c8c37fSriastradh #include <sys/device_impl.h>
404c1d81b2Sjmcneill #include <sys/pmf.h>
414c1d81b2Sjmcneill #include <sys/queue.h>
42d7d2a525Stsutsui #include <sys/sched.h>
434c1d81b2Sjmcneill #include <sys/workqueue.h>
444c1d81b2Sjmcneill #include <prop/proplib.h>
4549025715Sdyoung #include <sys/condvar.h>
4649025715Sdyoung #include <sys/mutex.h>
4749025715Sdyoung #include <sys/proc.h>
4849025715Sdyoung #include <sys/reboot.h>	/* for RB_NOSYNC */
4949025715Sdyoung #include <sys/sched.h>
503211dce1Smlelstv #include <sys/sysctl.h>
51ce817826Sdsl #include <sys/vfs_syscalls.h>
524c1d81b2Sjmcneill 
536bc03e0cSdrochner /* XXX ugly special case, but for now the only client */
546bc03e0cSdrochner #include "wsdisplay.h"
556bc03e0cSdrochner #if NWSDISPLAY > 0
566bc03e0cSdrochner #include <dev/wscons/wsdisplayvar.h>
576bc03e0cSdrochner #endif
586bc03e0cSdrochner 
5936fffd8dSdyoung #define PMF_DEBUG
6036fffd8dSdyoung 
614c1d81b2Sjmcneill #ifdef PMF_DEBUG
624c1d81b2Sjmcneill int  pmf_debug_event;
6336fffd8dSdyoung int  pmf_debug_suspend;
6436fffd8dSdyoung int  pmf_debug_suspensor;
654c1d81b2Sjmcneill int  pmf_debug_idle;
664c1d81b2Sjmcneill int  pmf_debug_transition;
674c1d81b2Sjmcneill 
6836fffd8dSdyoung #define	PMF_SUSPENSOR_PRINTF(x)		if (pmf_debug_suspensor) printf x
6936fffd8dSdyoung #define	PMF_SUSPEND_PRINTF(x)		if (pmf_debug_suspend) printf x
704c1d81b2Sjmcneill #define	PMF_EVENT_PRINTF(x)		if (pmf_debug_event) printf x
714c1d81b2Sjmcneill #define	PMF_IDLE_PRINTF(x)		if (pmf_debug_idle) printf x
724c1d81b2Sjmcneill #define	PMF_TRANSITION_PRINTF(x)	if (pmf_debug_transition) printf x
734c1d81b2Sjmcneill #define	PMF_TRANSITION_PRINTF2(y,x)	if (pmf_debug_transition>y) printf x
744c1d81b2Sjmcneill #else
7536fffd8dSdyoung #define	PMF_SUSPENSOR_PRINTF(x)		do { } while (0)
7636fffd8dSdyoung #define	PMF_SUSPEND_PRINTF(x)		do { } while (0)
774c1d81b2Sjmcneill #define	PMF_EVENT_PRINTF(x)		do { } while (0)
784c1d81b2Sjmcneill #define	PMF_IDLE_PRINTF(x)		do { } while (0)
794c1d81b2Sjmcneill #define	PMF_TRANSITION_PRINTF(x)	do { } while (0)
804c1d81b2Sjmcneill #define	PMF_TRANSITION_PRINTF2(y,x)	do { } while (0)
814c1d81b2Sjmcneill #endif
824c1d81b2Sjmcneill 
834c1d81b2Sjmcneill static prop_dictionary_t pmf_platform = NULL;
844c1d81b2Sjmcneill static struct workqueue *pmf_event_workqueue;
8536fffd8dSdyoung static struct workqueue *pmf_suspend_workqueue;
864c1d81b2Sjmcneill 
874c1d81b2Sjmcneill typedef struct pmf_event_handler {
884c1d81b2Sjmcneill 	TAILQ_ENTRY(pmf_event_handler) pmf_link;
894c1d81b2Sjmcneill 	pmf_generic_event_t pmf_event;
904c1d81b2Sjmcneill 	void (*pmf_handler)(device_t);
914c1d81b2Sjmcneill 	device_t pmf_device;
924c1d81b2Sjmcneill 	bool pmf_global;
934c1d81b2Sjmcneill } pmf_event_handler_t;
944c1d81b2Sjmcneill 
954c1d81b2Sjmcneill static TAILQ_HEAD(, pmf_event_handler) pmf_all_events =
964c1d81b2Sjmcneill     TAILQ_HEAD_INITIALIZER(pmf_all_events);
974c1d81b2Sjmcneill 
984c1d81b2Sjmcneill typedef struct pmf_event_workitem {
994c1d81b2Sjmcneill 	struct work				pew_work;
1004c1d81b2Sjmcneill 	pmf_generic_event_t			pew_event;
1014c1d81b2Sjmcneill 	device_t				pew_device;
1024c1d81b2Sjmcneill } pmf_event_workitem_t;
1034c1d81b2Sjmcneill 
10436fffd8dSdyoung typedef struct pmf_suspend_workitem {
10536fffd8dSdyoung 	struct work	psw_work;
10636fffd8dSdyoung 	device_t	psw_dev;
107c1b390d4Sdyoung 	pmf_qual_t	psw_qual;
10836fffd8dSdyoung } pmf_suspend_workitem_t;
10936fffd8dSdyoung 
1100ca6708cSrmind static struct pool pew_pl;
111bfd7452aSdyoung 
112bfd7452aSdyoung static pmf_event_workitem_t *pmf_event_workitem_get(void);
113bfd7452aSdyoung static void pmf_event_workitem_put(pmf_event_workitem_t *);
114bfd7452aSdyoung 
115*89f86b67Sriastradh static bool pmf_device_resume_locked(device_t, const pmf_qual_t *);
116*89f86b67Sriastradh static bool pmf_device_suspend_locked(device_t, const pmf_qual_t *);
11736fffd8dSdyoung static bool device_pmf_any_suspensor(device_t, devact_level_t);
11849025715Sdyoung 
11936fffd8dSdyoung static bool
complete_suspension(device_t dev,const device_suspensor_t ** susp,const pmf_qual_t * pqp)120c1b390d4Sdyoung complete_suspension(device_t dev, const device_suspensor_t **susp,
121c1b390d4Sdyoung     const pmf_qual_t *pqp)
12236fffd8dSdyoung {
12336fffd8dSdyoung 	int i;
124c1b390d4Sdyoung 	pmf_qual_t pq;
125c1b390d4Sdyoung 	const device_suspensor_t *ds;
12649025715Sdyoung 
12736fffd8dSdyoung 	ds = pmf_qual_suspension(pqp);
12836fffd8dSdyoung 	KASSERT(ds->ds_delegator != NULL);
12936fffd8dSdyoung 
13036fffd8dSdyoung 	pq = *pqp;
13136fffd8dSdyoung 	pq.pq_suspensor = ds->ds_delegator;
13236fffd8dSdyoung 
13336fffd8dSdyoung 	for (i = 0; i < DEVICE_SUSPENSORS_MAX; i++) {
13436fffd8dSdyoung 		if (susp[i] != ds)
13536fffd8dSdyoung 			continue;
13636fffd8dSdyoung 		if (!pmf_device_suspend(dev, &pq))
13736fffd8dSdyoung 			return false;
13836fffd8dSdyoung 	}
13936fffd8dSdyoung 	return true;
14036fffd8dSdyoung }
14136fffd8dSdyoung 
14236fffd8dSdyoung static void
pmf_suspend_worker(struct work * wk,void * dummy)14336fffd8dSdyoung pmf_suspend_worker(struct work *wk, void *dummy)
14436fffd8dSdyoung {
14536fffd8dSdyoung 	pmf_suspend_workitem_t *psw;
14636fffd8dSdyoung 	deviter_t di;
14736fffd8dSdyoung 	device_t dev;
14836fffd8dSdyoung 
14936fffd8dSdyoung 	psw = (void *)wk;
15036fffd8dSdyoung 	KASSERT(wk == &psw->psw_work);
15136fffd8dSdyoung 	KASSERT(psw != NULL);
15236fffd8dSdyoung 
15336fffd8dSdyoung 	for (dev = deviter_first(&di, 0); dev != NULL;
15436fffd8dSdyoung 	     dev = deviter_next(&di)) {
15536fffd8dSdyoung 		if (dev == psw->psw_dev && device_pmf_lock(dev))
15636fffd8dSdyoung 			break;
15736fffd8dSdyoung 	}
15836fffd8dSdyoung 	deviter_release(&di);
15936fffd8dSdyoung 
16036fffd8dSdyoung 	if (dev == NULL)
16136fffd8dSdyoung 		return;
16236fffd8dSdyoung 
16336fffd8dSdyoung 	switch (pmf_qual_depth(&psw->psw_qual)) {
16436fffd8dSdyoung 	case DEVACT_LEVEL_FULL:
16536fffd8dSdyoung 		if (!complete_suspension(dev, dev->dv_class_suspensors,
16636fffd8dSdyoung 			&psw->psw_qual))
16736fffd8dSdyoung 			break;
16836fffd8dSdyoung 		/*FALLTHROUGH*/
16936fffd8dSdyoung 	case DEVACT_LEVEL_DRIVER:
17036fffd8dSdyoung 		if (!complete_suspension(dev, dev->dv_driver_suspensors,
17136fffd8dSdyoung 			&psw->psw_qual))
17236fffd8dSdyoung 			break;
17336fffd8dSdyoung 		/*FALLTHROUGH*/
17436fffd8dSdyoung 	case DEVACT_LEVEL_BUS:
17536fffd8dSdyoung 		if (!complete_suspension(dev, dev->dv_bus_suspensors,
17636fffd8dSdyoung 			&psw->psw_qual))
17736fffd8dSdyoung 			break;
17836fffd8dSdyoung 	}
17936fffd8dSdyoung 	device_pmf_unlock(dev);
18036fffd8dSdyoung 	kmem_free(psw, sizeof(*psw));
18136fffd8dSdyoung }
1828705e09aSdyoung 
1834c1d81b2Sjmcneill static void
pmf_event_worker(struct work * wk,void * dummy)1844c1d81b2Sjmcneill pmf_event_worker(struct work *wk, void *dummy)
1854c1d81b2Sjmcneill {
1864c1d81b2Sjmcneill 	pmf_event_workitem_t *pew;
1874c1d81b2Sjmcneill 	pmf_event_handler_t *event;
1884c1d81b2Sjmcneill 
1894c1d81b2Sjmcneill 	pew = (void *)wk;
1904c1d81b2Sjmcneill 	KASSERT(wk == &pew->pew_work);
1914c1d81b2Sjmcneill 	KASSERT(pew != NULL);
1924c1d81b2Sjmcneill 
1934c1d81b2Sjmcneill 	TAILQ_FOREACH(event, &pmf_all_events, pmf_link) {
1944c1d81b2Sjmcneill 		if (event->pmf_event != pew->pew_event)
1954c1d81b2Sjmcneill 			continue;
196a992c95cSjmcneill 		if (event->pmf_device == pew->pew_device || event->pmf_global)
1974c1d81b2Sjmcneill 			(*event->pmf_handler)(event->pmf_device);
1984c1d81b2Sjmcneill 	}
1994c1d81b2Sjmcneill 
200bfd7452aSdyoung 	pmf_event_workitem_put(pew);
2014c1d81b2Sjmcneill }
2024c1d81b2Sjmcneill 
2034c1d81b2Sjmcneill static bool
pmf_check_system_drivers(void)2044c1d81b2Sjmcneill pmf_check_system_drivers(void)
2054c1d81b2Sjmcneill {
2064c1d81b2Sjmcneill 	device_t curdev;
2074c1d81b2Sjmcneill 	bool unsupported_devs;
20849025715Sdyoung 	deviter_t di;
2094c1d81b2Sjmcneill 
2104c1d81b2Sjmcneill 	unsupported_devs = false;
21149025715Sdyoung 	for (curdev = deviter_first(&di, 0); curdev != NULL;
21249025715Sdyoung 	     curdev = deviter_next(&di)) {
2134c1d81b2Sjmcneill 		if (device_pmf_is_registered(curdev))
2144c1d81b2Sjmcneill 			continue;
2154c1d81b2Sjmcneill 		if (!unsupported_devs)
2164c1d81b2Sjmcneill 			printf("Devices without power management support:");
2174c1d81b2Sjmcneill 		printf(" %s", device_xname(curdev));
2184c1d81b2Sjmcneill 		unsupported_devs = true;
2194c1d81b2Sjmcneill 	}
22049025715Sdyoung 	deviter_release(&di);
2214c1d81b2Sjmcneill 	if (unsupported_devs) {
2224c1d81b2Sjmcneill 		printf("\n");
2234c1d81b2Sjmcneill 		return false;
2244c1d81b2Sjmcneill 	}
2254c1d81b2Sjmcneill 	return true;
2264c1d81b2Sjmcneill }
2274c1d81b2Sjmcneill 
2284c1d81b2Sjmcneill bool
pmf_system_bus_resume(const pmf_qual_t * qual)229c1b390d4Sdyoung pmf_system_bus_resume(const pmf_qual_t *qual)
230e692a6c7Sjmcneill {
231e692a6c7Sjmcneill 	bool rv;
232e692a6c7Sjmcneill 	device_t curdev;
23349025715Sdyoung 	deviter_t di;
234e692a6c7Sjmcneill 
235e692a6c7Sjmcneill 	aprint_debug("Powering devices:");
236e692a6c7Sjmcneill 	/* D0 handlers are run in order */
237e692a6c7Sjmcneill 	rv = true;
23849025715Sdyoung 	for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); curdev != NULL;
23949025715Sdyoung 	     curdev = deviter_next(&di)) {
240e692a6c7Sjmcneill 		if (!device_pmf_is_registered(curdev))
241e692a6c7Sjmcneill 			continue;
242e692a6c7Sjmcneill 		if (device_is_active(curdev) ||
243e692a6c7Sjmcneill 		    !device_is_enabled(curdev))
244e692a6c7Sjmcneill 			continue;
245e692a6c7Sjmcneill 
246e692a6c7Sjmcneill 		aprint_debug(" %s", device_xname(curdev));
247e692a6c7Sjmcneill 
248cd6e1fbfSdyoung 		if (!device_pmf_bus_resume(curdev, qual)) {
24949025715Sdyoung 			rv = false;
250e692a6c7Sjmcneill 			aprint_debug("(failed)");
251e692a6c7Sjmcneill 		}
252e692a6c7Sjmcneill 	}
25349025715Sdyoung 	deviter_release(&di);
254e692a6c7Sjmcneill 	aprint_debug("\n");
255e692a6c7Sjmcneill 
256e692a6c7Sjmcneill 	return rv;
257e692a6c7Sjmcneill }
258e692a6c7Sjmcneill 
259e692a6c7Sjmcneill bool
pmf_system_resume(const pmf_qual_t * qual)260c1b390d4Sdyoung pmf_system_resume(const pmf_qual_t *qual)
2614c1d81b2Sjmcneill {
2624c1d81b2Sjmcneill 	bool rv;
2634c1d81b2Sjmcneill 	device_t curdev, parent;
26449025715Sdyoung 	deviter_t di;
2654c1d81b2Sjmcneill 
2664c1d81b2Sjmcneill 	if (!pmf_check_system_drivers())
2674c1d81b2Sjmcneill 		return false;
2684c1d81b2Sjmcneill 
2694c1d81b2Sjmcneill 	aprint_debug("Resuming devices:");
2704c1d81b2Sjmcneill 	/* D0 handlers are run in order */
2714c1d81b2Sjmcneill 	rv = true;
27249025715Sdyoung 	for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); curdev != NULL;
27349025715Sdyoung 	     curdev = deviter_next(&di)) {
2744c1d81b2Sjmcneill 		if (device_is_active(curdev) ||
2754c1d81b2Sjmcneill 		    !device_is_enabled(curdev))
2764c1d81b2Sjmcneill 			continue;
2774c1d81b2Sjmcneill 		parent = device_parent(curdev);
2784c1d81b2Sjmcneill 		if (parent != NULL &&
2794c1d81b2Sjmcneill 		    !device_is_active(parent))
2804c1d81b2Sjmcneill 			continue;
2814c1d81b2Sjmcneill 
2824c1d81b2Sjmcneill 		aprint_debug(" %s", device_xname(curdev));
2834c1d81b2Sjmcneill 
284cd6e1fbfSdyoung 		if (!pmf_device_resume(curdev, qual)) {
2854c1d81b2Sjmcneill 			rv = false;
2864c1d81b2Sjmcneill 			aprint_debug("(failed)");
2874c1d81b2Sjmcneill 		}
2884c1d81b2Sjmcneill 	}
28949025715Sdyoung 	deviter_release(&di);
2904c1d81b2Sjmcneill 	aprint_debug(".\n");
2914c1d81b2Sjmcneill 
2926bc03e0cSdrochner 	KERNEL_UNLOCK_ONE(0);
2936bc03e0cSdrochner #if NWSDISPLAY > 0
2946bc03e0cSdrochner 	if (rv)
2956bc03e0cSdrochner 		wsdisplay_handlex(1);
2966bc03e0cSdrochner #endif
2974c1d81b2Sjmcneill 	return rv;
2984c1d81b2Sjmcneill }
2994c1d81b2Sjmcneill 
3004c1d81b2Sjmcneill bool
pmf_system_suspend(const pmf_qual_t * qual)301c1b390d4Sdyoung pmf_system_suspend(const pmf_qual_t *qual)
3024c1d81b2Sjmcneill {
3034c1d81b2Sjmcneill 	device_t curdev;
30449025715Sdyoung 	deviter_t di;
3054c1d81b2Sjmcneill 
3064c1d81b2Sjmcneill 	if (!pmf_check_system_drivers())
3074c1d81b2Sjmcneill 		return false;
3086bc03e0cSdrochner #if NWSDISPLAY > 0
3096bc03e0cSdrochner 	if (wsdisplay_handlex(0))
3106bc03e0cSdrochner 		return false;
3116bc03e0cSdrochner #endif
312cc651889Sskrll 	KERNEL_LOCK(1, NULL);
3134c1d81b2Sjmcneill 
3144c1d81b2Sjmcneill 	/*
3154c1d81b2Sjmcneill 	 * Flush buffers only if the shutdown didn't do so
3164c1d81b2Sjmcneill 	 * already and if there was no panic.
3174c1d81b2Sjmcneill 	 */
3184c1d81b2Sjmcneill 	if (doing_shutdown == 0 && panicstr == NULL) {
3194c1d81b2Sjmcneill 		printf("Flushing disk caches: ");
320ce817826Sdsl 		do_sys_sync(&lwp0);
3214e73754fSad 		if (vfs_syncwait() != 0)
3224c1d81b2Sjmcneill 			printf("giving up\n");
3234c1d81b2Sjmcneill 		else
3244c1d81b2Sjmcneill 			printf("done\n");
3254c1d81b2Sjmcneill 	}
3264c1d81b2Sjmcneill 
3274c1d81b2Sjmcneill 	aprint_debug("Suspending devices:");
3284c1d81b2Sjmcneill 
32949025715Sdyoung 	for (curdev = deviter_first(&di, DEVITER_F_LEAVES_FIRST);
33049025715Sdyoung 	     curdev != NULL;
33149025715Sdyoung 	     curdev = deviter_next(&di)) {
3324c1d81b2Sjmcneill 		if (!device_is_active(curdev))
3334c1d81b2Sjmcneill 			continue;
3344c1d81b2Sjmcneill 
3354c1d81b2Sjmcneill 		aprint_debug(" %s", device_xname(curdev));
3364c1d81b2Sjmcneill 
3374c1d81b2Sjmcneill 		/* XXX joerg check return value and abort suspend */
338cd6e1fbfSdyoung 		if (!pmf_device_suspend(curdev, qual))
3394c1d81b2Sjmcneill 			aprint_debug("(failed)");
3404c1d81b2Sjmcneill 	}
34149025715Sdyoung 	deviter_release(&di);
3424c1d81b2Sjmcneill 
3434c1d81b2Sjmcneill 	aprint_debug(".\n");
3444c1d81b2Sjmcneill 
3454c1d81b2Sjmcneill 	return true;
3464c1d81b2Sjmcneill }
3474c1d81b2Sjmcneill 
348d2a57992Sdyoung static bool
shutdown_all(int how)349d2a57992Sdyoung shutdown_all(int how)
350d2a57992Sdyoung {
351d2a57992Sdyoung 	static struct shutdown_state s;
352d2a57992Sdyoung 	device_t curdev;
353d2a57992Sdyoung 	bool progress = false;
354d2a57992Sdyoung 
3555d7e3f87Sad 	KERNEL_LOCK(1, NULL);
356d2a57992Sdyoung 	for (curdev = shutdown_first(&s); curdev != NULL;
357d2a57992Sdyoung 	     curdev = shutdown_next(&s)) {
358d2a57992Sdyoung 		aprint_debug(" shutting down %s, ", device_xname(curdev));
359d2a57992Sdyoung 		if (!device_pmf_is_registered(curdev))
360d2a57992Sdyoung 			aprint_debug("skipped.");
361d2a57992Sdyoung #if 0 /* needed? */
362d2a57992Sdyoung 		else if (!device_pmf_class_shutdown(curdev, how))
363d2a57992Sdyoung 			aprint_debug("failed.");
364d2a57992Sdyoung #endif
365d2a57992Sdyoung 		else if (!device_pmf_driver_shutdown(curdev, how))
366d2a57992Sdyoung 			aprint_debug("failed.");
367d2a57992Sdyoung 		else if (!device_pmf_bus_shutdown(curdev, how))
368d2a57992Sdyoung 			aprint_debug("failed.");
369d2a57992Sdyoung 		else {
370d2a57992Sdyoung 			progress = true;
371d2a57992Sdyoung 			aprint_debug("success.");
372d2a57992Sdyoung 		}
373d2a57992Sdyoung 	}
3745d7e3f87Sad 	KERNEL_UNLOCK_ONE(NULL);
375d2a57992Sdyoung 	return progress;
376d2a57992Sdyoung }
377d2a57992Sdyoung 
3784c1d81b2Sjmcneill void
pmf_system_shutdown(int how)3790e748e63Sdrochner pmf_system_shutdown(int how)
3804c1d81b2Sjmcneill {
381b8894994Schs 
382b8894994Schs 	if (panicstr != NULL)
383b8894994Schs 		return;
384b8894994Schs 
3854c1d81b2Sjmcneill 	aprint_debug("Shutting down devices:");
386d2a57992Sdyoung 	shutdown_all(how);
3874c1d81b2Sjmcneill }
3884c1d81b2Sjmcneill 
3894c1d81b2Sjmcneill bool
pmf_set_platform(const char * key,const char * value)3904c1d81b2Sjmcneill pmf_set_platform(const char *key, const char *value)
3914c1d81b2Sjmcneill {
3924c1d81b2Sjmcneill 	if (pmf_platform == NULL)
3934c1d81b2Sjmcneill 		pmf_platform = prop_dictionary_create();
3944c1d81b2Sjmcneill 	if (pmf_platform == NULL)
3954c1d81b2Sjmcneill 		return false;
3964c1d81b2Sjmcneill 
397de43e596Sthorpej 	return prop_dictionary_set_string(pmf_platform, key, value);
3984c1d81b2Sjmcneill }
3994c1d81b2Sjmcneill 
4004c1d81b2Sjmcneill const char *
pmf_get_platform(const char * key)4014c1d81b2Sjmcneill pmf_get_platform(const char *key)
4024c1d81b2Sjmcneill {
4034c1d81b2Sjmcneill 	const char *value;
4044c1d81b2Sjmcneill 
4054c1d81b2Sjmcneill 	if (pmf_platform == NULL)
4064c1d81b2Sjmcneill 		return NULL;
4074c1d81b2Sjmcneill 
408e4c60075Sthorpej 	if (!prop_dictionary_get_string(pmf_platform, key, &value))
4094c1d81b2Sjmcneill 		return NULL;
4104c1d81b2Sjmcneill 
4114c1d81b2Sjmcneill 	return value;
4124c1d81b2Sjmcneill }
4134c1d81b2Sjmcneill 
4144c1d81b2Sjmcneill bool
pmf_device_register1(device_t dev,bool (* suspend)(device_t,const pmf_qual_t *),bool (* resume)(device_t,const pmf_qual_t *),bool (* shutdown)(device_t,int))4150e748e63Sdrochner pmf_device_register1(device_t dev,
416c1b390d4Sdyoung     bool (*suspend)(device_t, const pmf_qual_t *),
417c1b390d4Sdyoung     bool (*resume)(device_t, const pmf_qual_t *),
4180e748e63Sdrochner     bool (*shutdown)(device_t, int))
4194c1d81b2Sjmcneill {
4204c1d81b2Sjmcneill 
42100893cdfSriastradh 	device_pmf_driver_register(dev, suspend, resume, shutdown);
42231786995Sriastradh 	device_pmf_driver_child_register(dev);
4234c1d81b2Sjmcneill 
4244c1d81b2Sjmcneill 	return true;
4254c1d81b2Sjmcneill }
4264c1d81b2Sjmcneill 
4274c1d81b2Sjmcneill void
pmf_device_deregister(device_t dev)4284c1d81b2Sjmcneill pmf_device_deregister(device_t dev)
4294c1d81b2Sjmcneill {
430*89f86b67Sriastradh 
4314c1d81b2Sjmcneill 	device_pmf_class_deregister(dev);
4324c1d81b2Sjmcneill 	device_pmf_bus_deregister(dev);
4334c1d81b2Sjmcneill 	device_pmf_driver_deregister(dev);
4344c1d81b2Sjmcneill }
4354c1d81b2Sjmcneill 
436c1b390d4Sdyoung static const device_suspensor_t _device_suspensor_drvctl = {
437*89f86b67Sriastradh 	.ds_delegator = NULL,
438*89f86b67Sriastradh 	.ds_name = "drvctl",
43936fffd8dSdyoung };
44036fffd8dSdyoung 
441c1b390d4Sdyoung static const device_suspensor_t _device_suspensor_self = {
442*89f86b67Sriastradh 	.ds_delegator = NULL,
443*89f86b67Sriastradh 	.ds_name = "self",
44436fffd8dSdyoung };
44536fffd8dSdyoung 
44636fffd8dSdyoung #if 0
447c1b390d4Sdyoung static const device_suspensor_t _device_suspensor_self_delegate = {
448*89f86b67Sriastradh 	.ds_delegator = &_device_suspensor_self,
449*89f86b67Sriastradh 	.ds_name = "self delegate",
45036fffd8dSdyoung };
45136fffd8dSdyoung #endif
45236fffd8dSdyoung 
453c1b390d4Sdyoung static const device_suspensor_t _device_suspensor_system = {
454*89f86b67Sriastradh 	.ds_delegator = NULL,
455*89f86b67Sriastradh 	.ds_name = "system",
45636fffd8dSdyoung };
45736fffd8dSdyoung 
458c1b390d4Sdyoung const device_suspensor_t
45936fffd8dSdyoung     * const device_suspensor_self = &_device_suspensor_self,
46036fffd8dSdyoung #if 0
46136fffd8dSdyoung     * const device_suspensor_self_delegate = &_device_suspensor_self_delegate,
46236fffd8dSdyoung #endif
46336fffd8dSdyoung     * const device_suspensor_system = &_device_suspensor_system,
46436fffd8dSdyoung     * const device_suspensor_drvctl = &_device_suspensor_drvctl;
46536fffd8dSdyoung 
466c1b390d4Sdyoung static const pmf_qual_t _pmf_qual_system = {
467*89f86b67Sriastradh 	.pq_actlvl = DEVACT_LEVEL_FULL,
468*89f86b67Sriastradh 	.pq_suspensor = &_device_suspensor_system,
46936fffd8dSdyoung };
47036fffd8dSdyoung 
471c1b390d4Sdyoung static const pmf_qual_t _pmf_qual_drvctl = {
472*89f86b67Sriastradh 	.pq_actlvl = DEVACT_LEVEL_FULL,
473*89f86b67Sriastradh 	.pq_suspensor = &_device_suspensor_drvctl,
47436fffd8dSdyoung };
47536fffd8dSdyoung 
476c1b390d4Sdyoung static const pmf_qual_t _pmf_qual_self = {
477*89f86b67Sriastradh 	.pq_actlvl = DEVACT_LEVEL_DRIVER,
478*89f86b67Sriastradh 	.pq_suspensor = &_device_suspensor_self,
47936fffd8dSdyoung };
48036fffd8dSdyoung 
481c1b390d4Sdyoung const pmf_qual_t
48236fffd8dSdyoung     * const PMF_Q_DRVCTL = &_pmf_qual_drvctl,
48336fffd8dSdyoung     * const PMF_Q_NONE = &_pmf_qual_system,
48436fffd8dSdyoung     * const PMF_Q_SELF = &_pmf_qual_self;
48536fffd8dSdyoung 
48636fffd8dSdyoung static bool
device_suspensor_delegates_to(const device_suspensor_t * ds,const device_suspensor_t * delegate)487c1b390d4Sdyoung device_suspensor_delegates_to(const device_suspensor_t *ds,
488c1b390d4Sdyoung     const device_suspensor_t *delegate)
4893df2b2feSdyoung {
490c1b390d4Sdyoung 	const device_suspensor_t *iter;
49136fffd8dSdyoung 
49236fffd8dSdyoung 	for (iter = delegate->ds_delegator; iter != NULL;
49336fffd8dSdyoung 	     iter = iter->ds_delegator) {
49436fffd8dSdyoung 		if (ds == iter)
49536fffd8dSdyoung 			return true;
49636fffd8dSdyoung 	}
49736fffd8dSdyoung 	return false;
49836fffd8dSdyoung }
49936fffd8dSdyoung 
50036fffd8dSdyoung static bool
add_suspensor(device_t dev,const char * kind,const device_suspensor_t ** susp,const device_suspensor_t * ds)501c1b390d4Sdyoung add_suspensor(device_t dev, const char *kind, const device_suspensor_t **susp,
502c1b390d4Sdyoung     const device_suspensor_t *ds)
50336fffd8dSdyoung {
50436fffd8dSdyoung 	int i;
50536fffd8dSdyoung 
50636fffd8dSdyoung 	for (i = 0; i < DEVICE_SUSPENSORS_MAX; i++) {
50736fffd8dSdyoung 		if (susp[i] == NULL)
50836fffd8dSdyoung 			continue;
50936fffd8dSdyoung 		if (ds == susp[i]) {
51036fffd8dSdyoung 			PMF_SUSPENSOR_PRINTF((
51136fffd8dSdyoung 			    "%s: %s-suspended by %s (delegator %s) already\n",
51236fffd8dSdyoung 			    device_xname(dev), kind,
51336fffd8dSdyoung 			    susp[i]->ds_name,
51436fffd8dSdyoung 			    (susp[i]->ds_delegator != NULL) ?
51536fffd8dSdyoung 			    susp[i]->ds_delegator->ds_name : "<none>"));
51636fffd8dSdyoung 			return true;
51736fffd8dSdyoung 		}
51836fffd8dSdyoung 		if (device_suspensor_delegates_to(ds, susp[i])) {
51936fffd8dSdyoung 			PMF_SUSPENSOR_PRINTF((
52036fffd8dSdyoung 			    "%s: %s assumes %s-suspension by %s "
52136fffd8dSdyoung 			    "(delegator %s)\n",
52236fffd8dSdyoung 			    device_xname(dev), ds->ds_name, kind,
52336fffd8dSdyoung 			    susp[i]->ds_name,
52436fffd8dSdyoung 			    (susp[i]->ds_delegator != NULL) ?
52536fffd8dSdyoung 			    susp[i]->ds_delegator->ds_name : "<none>"));
52636fffd8dSdyoung 			susp[i] = ds;
52736fffd8dSdyoung 			return true;
52836fffd8dSdyoung 		}
52936fffd8dSdyoung 	}
53036fffd8dSdyoung 	for (i = 0; i < DEVICE_SUSPENSORS_MAX; i++) {
53136fffd8dSdyoung 		if (susp[i] == NULL) {
53236fffd8dSdyoung 			susp[i] = ds;
53336fffd8dSdyoung 			PMF_SUSPENSOR_PRINTF((
53436fffd8dSdyoung 			    "%s: newly %s-suspended by %s (delegator %s)\n",
53536fffd8dSdyoung 			    device_xname(dev), kind,
53636fffd8dSdyoung 			    susp[i]->ds_name,
53736fffd8dSdyoung 			    (susp[i]->ds_delegator != NULL) ?
53836fffd8dSdyoung 			    susp[i]->ds_delegator->ds_name : "<none>"));
53936fffd8dSdyoung 			return true;
54036fffd8dSdyoung 		}
54136fffd8dSdyoung 	}
54236fffd8dSdyoung 	return false;
54336fffd8dSdyoung }
54436fffd8dSdyoung 
54536fffd8dSdyoung static bool
device_pmf_add_suspensor(device_t dev,const pmf_qual_t * pq)546c1b390d4Sdyoung device_pmf_add_suspensor(device_t dev, const pmf_qual_t *pq)
54736fffd8dSdyoung {
548c1b390d4Sdyoung 	const device_suspensor_t *ds;
54936fffd8dSdyoung 
55036fffd8dSdyoung 	KASSERT(pq != NULL);
55136fffd8dSdyoung 
55236fffd8dSdyoung 	ds = pmf_qual_suspension(pq);
55336fffd8dSdyoung 
55436fffd8dSdyoung 	KASSERT(ds != NULL);
55536fffd8dSdyoung 
55636fffd8dSdyoung 	if (!add_suspensor(dev, "class", dev->dv_class_suspensors, ds))
55736fffd8dSdyoung 		return false;
55836fffd8dSdyoung 	if (!add_suspensor(dev, "driver", dev->dv_driver_suspensors, ds))
55936fffd8dSdyoung 		return false;
56036fffd8dSdyoung 	if (!add_suspensor(dev, "bus", dev->dv_bus_suspensors, ds))
56136fffd8dSdyoung 		return false;
56236fffd8dSdyoung 	return true;
56336fffd8dSdyoung }
56436fffd8dSdyoung 
56536fffd8dSdyoung #if 0
56636fffd8dSdyoung static bool
567c1b390d4Sdyoung device_pmf_has_suspension(device_t dev, const device_suspensor_t *ds)
56836fffd8dSdyoung {
56936fffd8dSdyoung 	int i;
57036fffd8dSdyoung 
57136fffd8dSdyoung 	for (i = 0; i < DEVICE_SUSPENSORS_MAX; i++) {
57236fffd8dSdyoung 		if (dev->dv_suspensions[i] == ds)
57336fffd8dSdyoung 			return true;
57436fffd8dSdyoung 		if (device_suspensor_delegates_to(dev->dv_suspensions[i], ds))
57536fffd8dSdyoung 			return true;
57636fffd8dSdyoung 	}
57736fffd8dSdyoung 	return false;
57836fffd8dSdyoung }
57936fffd8dSdyoung #endif
58036fffd8dSdyoung 
58136fffd8dSdyoung static bool
any_suspensor(device_t dev,const char * kind,const device_suspensor_t ** susp)582c1b390d4Sdyoung any_suspensor(device_t dev, const char *kind, const device_suspensor_t **susp)
58336fffd8dSdyoung {
58436fffd8dSdyoung 	int i;
58536fffd8dSdyoung 	bool suspended = false;
58636fffd8dSdyoung 
58736fffd8dSdyoung 	for (i = 0; i < DEVICE_SUSPENSORS_MAX; i++) {
58836fffd8dSdyoung 		if (susp[i] != NULL) {
58936fffd8dSdyoung 			PMF_SUSPENSOR_PRINTF(("%s: %s is suspended by %s "
59036fffd8dSdyoung 			    "(delegator %s)\n",
59136fffd8dSdyoung 			    device_xname(dev), kind,
59236fffd8dSdyoung 			    susp[i]->ds_name,
59336fffd8dSdyoung 			    (susp[i]->ds_delegator != NULL) ?
59436fffd8dSdyoung 			    susp[i]->ds_delegator->ds_name : "<none>"));
59536fffd8dSdyoung 			suspended = true;
59636fffd8dSdyoung 		}
59736fffd8dSdyoung 	}
59836fffd8dSdyoung 	return suspended;
59936fffd8dSdyoung }
60036fffd8dSdyoung 
60136fffd8dSdyoung static bool
device_pmf_any_suspensor(device_t dev,devact_level_t depth)60236fffd8dSdyoung device_pmf_any_suspensor(device_t dev, devact_level_t depth)
60336fffd8dSdyoung {
60436fffd8dSdyoung 	switch (depth) {
60536fffd8dSdyoung 	case DEVACT_LEVEL_FULL:
60636fffd8dSdyoung 		if (any_suspensor(dev, "class", dev->dv_class_suspensors))
60736fffd8dSdyoung 			return true;
60836fffd8dSdyoung 		/*FALLTHROUGH*/
60936fffd8dSdyoung 	case DEVACT_LEVEL_DRIVER:
61036fffd8dSdyoung 		if (any_suspensor(dev, "driver", dev->dv_driver_suspensors))
61136fffd8dSdyoung 			return true;
61236fffd8dSdyoung 		/*FALLTHROUGH*/
61336fffd8dSdyoung 	case DEVACT_LEVEL_BUS:
61436fffd8dSdyoung 		if (any_suspensor(dev, "bus", dev->dv_bus_suspensors))
61536fffd8dSdyoung 			return true;
61636fffd8dSdyoung 	}
61736fffd8dSdyoung 	return false;
61836fffd8dSdyoung }
61936fffd8dSdyoung 
62036fffd8dSdyoung static bool
remove_suspensor(device_t dev,const char * kind,const device_suspensor_t ** susp,const device_suspensor_t * ds)621c1b390d4Sdyoung remove_suspensor(device_t dev, const char *kind,
622c1b390d4Sdyoung     const device_suspensor_t **susp, const device_suspensor_t *ds)
62336fffd8dSdyoung {
62436fffd8dSdyoung 	int i;
62536fffd8dSdyoung 
62636fffd8dSdyoung 	for (i = 0; i < DEVICE_SUSPENSORS_MAX; i++) {
62736fffd8dSdyoung 		if (susp[i] == NULL)
62836fffd8dSdyoung 			continue;
62936fffd8dSdyoung 		if (ds == susp[i] ||
63036fffd8dSdyoung 		    device_suspensor_delegates_to(ds, susp[i])) {
63136fffd8dSdyoung 			PMF_SUSPENSOR_PRINTF(("%s: %s suspension %s "
63236fffd8dSdyoung 			    "(delegator %s) removed by %s\n",
63336fffd8dSdyoung 			    device_xname(dev), kind,
63436fffd8dSdyoung 			    susp[i]->ds_name,
63536fffd8dSdyoung 			    (susp[i]->ds_delegator != NULL)
63636fffd8dSdyoung 			        ?  susp[i]->ds_delegator->ds_name
63736fffd8dSdyoung 			        : "<none>",
63836fffd8dSdyoung 			    ds->ds_name));
63936fffd8dSdyoung 			susp[i] = NULL;
64036fffd8dSdyoung 			return true;
64136fffd8dSdyoung 		}
64236fffd8dSdyoung 	}
64336fffd8dSdyoung 	return false;
64436fffd8dSdyoung }
64536fffd8dSdyoung 
64636fffd8dSdyoung static bool
device_pmf_remove_suspensor(device_t dev,const pmf_qual_t * pq)647c1b390d4Sdyoung device_pmf_remove_suspensor(device_t dev, const pmf_qual_t *pq)
64836fffd8dSdyoung {
649c1b390d4Sdyoung 	const device_suspensor_t *ds;
65036fffd8dSdyoung 
65136fffd8dSdyoung 	KASSERT(pq != NULL);
65236fffd8dSdyoung 
65336fffd8dSdyoung 	ds = pmf_qual_suspension(pq);
65436fffd8dSdyoung 
65536fffd8dSdyoung 	KASSERT(ds != NULL);
65636fffd8dSdyoung 
65736fffd8dSdyoung 	if (!remove_suspensor(dev, "class", dev->dv_class_suspensors, ds))
65836fffd8dSdyoung 		return false;
65936fffd8dSdyoung 	if (!remove_suspensor(dev, "driver", dev->dv_driver_suspensors, ds))
66036fffd8dSdyoung 		return false;
66136fffd8dSdyoung 	if (!remove_suspensor(dev, "bus", dev->dv_bus_suspensors, ds))
66236fffd8dSdyoung 		return false;
66336fffd8dSdyoung 
66436fffd8dSdyoung 	return true;
66536fffd8dSdyoung }
66636fffd8dSdyoung 
66736fffd8dSdyoung void
pmf_self_suspensor_init(device_t dev,device_suspensor_t * ds,pmf_qual_t * pq)668c1b390d4Sdyoung pmf_self_suspensor_init(device_t dev, device_suspensor_t *ds,
669c1b390d4Sdyoung     pmf_qual_t *pq)
67036fffd8dSdyoung {
671*89f86b67Sriastradh 
67236fffd8dSdyoung 	ds->ds_delegator = device_suspensor_self;
67336fffd8dSdyoung 	snprintf(ds->ds_name, sizeof(ds->ds_name), "%s-self",
67436fffd8dSdyoung 	    device_xname(dev));
67536fffd8dSdyoung 	pq->pq_actlvl = DEVACT_LEVEL_DRIVER;
67636fffd8dSdyoung 	pq->pq_suspensor = ds;
6773df2b2feSdyoung }
6783df2b2feSdyoung 
6793df2b2feSdyoung bool
pmf_device_suspend(device_t dev,const pmf_qual_t * qual)680c1b390d4Sdyoung pmf_device_suspend(device_t dev, const pmf_qual_t *qual)
6814c1d81b2Sjmcneill {
6828705e09aSdyoung 	bool rc;
6838705e09aSdyoung 
6844c1d81b2Sjmcneill 	PMF_TRANSITION_PRINTF(("%s: suspend enter\n", device_xname(dev)));
6854c1d81b2Sjmcneill 	if (!device_pmf_is_registered(dev))
6864c1d81b2Sjmcneill 		return false;
6878705e09aSdyoung 
68836fffd8dSdyoung 	if (!device_pmf_lock(dev))
6898705e09aSdyoung 		return false;
6908705e09aSdyoung 
691cd6e1fbfSdyoung 	rc = pmf_device_suspend_locked(dev, qual);
6928705e09aSdyoung 
69336fffd8dSdyoung 	device_pmf_unlock(dev);
6948705e09aSdyoung 
6958705e09aSdyoung 	PMF_TRANSITION_PRINTF(("%s: suspend exit\n", device_xname(dev)));
6968705e09aSdyoung 	return rc;
6978705e09aSdyoung }
6988705e09aSdyoung 
69936fffd8dSdyoung bool
pmf_device_suspend_locked(device_t dev,const pmf_qual_t * qual)700c1b390d4Sdyoung pmf_device_suspend_locked(device_t dev, const pmf_qual_t *qual)
7018705e09aSdyoung {
702*89f86b67Sriastradh 
703cd6e1fbfSdyoung 	if (!device_pmf_add_suspensor(dev, qual))
70436fffd8dSdyoung 		return false;
70536fffd8dSdyoung 
7064c1d81b2Sjmcneill 	PMF_TRANSITION_PRINTF2(1, ("%s: class suspend\n", device_xname(dev)));
707cd6e1fbfSdyoung 	if (!device_pmf_class_suspend(dev, qual))
7084c1d81b2Sjmcneill 		return false;
70936fffd8dSdyoung 
7104c1d81b2Sjmcneill 	PMF_TRANSITION_PRINTF2(1, ("%s: driver suspend\n", device_xname(dev)));
711cd6e1fbfSdyoung 	if (!device_pmf_driver_suspend(dev, qual))
7124c1d81b2Sjmcneill 		return false;
71336fffd8dSdyoung 
7144c1d81b2Sjmcneill 	PMF_TRANSITION_PRINTF2(1, ("%s: bus suspend\n", device_xname(dev)));
715cd6e1fbfSdyoung 	if (!device_pmf_bus_suspend(dev, qual))
7164c1d81b2Sjmcneill 		return false;
7178705e09aSdyoung 
7184c1d81b2Sjmcneill 	return true;
7194c1d81b2Sjmcneill }
7204c1d81b2Sjmcneill 
7214c1d81b2Sjmcneill bool
pmf_device_resume(device_t dev,const pmf_qual_t * qual)722c1b390d4Sdyoung pmf_device_resume(device_t dev, const pmf_qual_t *qual)
7234c1d81b2Sjmcneill {
7248705e09aSdyoung 	bool rc;
7258705e09aSdyoung 
7264c1d81b2Sjmcneill 	PMF_TRANSITION_PRINTF(("%s: resume enter\n", device_xname(dev)));
7274c1d81b2Sjmcneill 	if (!device_pmf_is_registered(dev))
7284c1d81b2Sjmcneill 		return false;
7298705e09aSdyoung 
73036fffd8dSdyoung 	if (!device_pmf_lock(dev))
7318705e09aSdyoung 		return false;
7328705e09aSdyoung 
733cd6e1fbfSdyoung 	rc = pmf_device_resume_locked(dev, qual);
7348705e09aSdyoung 
73536fffd8dSdyoung 	device_pmf_unlock(dev);
7368705e09aSdyoung 
7378705e09aSdyoung 	PMF_TRANSITION_PRINTF(("%s: resume exit\n", device_xname(dev)));
7388705e09aSdyoung 	return rc;
7398705e09aSdyoung }
7408705e09aSdyoung 
74136fffd8dSdyoung bool
pmf_device_resume_locked(device_t dev,const pmf_qual_t * qual)742c1b390d4Sdyoung pmf_device_resume_locked(device_t dev, const pmf_qual_t *qual)
7438705e09aSdyoung {
744*89f86b67Sriastradh 
745cd6e1fbfSdyoung 	device_pmf_remove_suspensor(dev, qual);
74636fffd8dSdyoung 
74736fffd8dSdyoung 	if (device_pmf_any_suspensor(dev, DEVACT_LEVEL_FULL))
74836fffd8dSdyoung 		return true;
74936fffd8dSdyoung 
7504c1d81b2Sjmcneill 	PMF_TRANSITION_PRINTF2(1, ("%s: bus resume\n", device_xname(dev)));
751cd6e1fbfSdyoung 	if (!device_pmf_bus_resume(dev, qual))
7524c1d81b2Sjmcneill 		return false;
75336fffd8dSdyoung 
7544c1d81b2Sjmcneill 	PMF_TRANSITION_PRINTF2(1, ("%s: driver resume\n", device_xname(dev)));
755cd6e1fbfSdyoung 	if (!device_pmf_driver_resume(dev, qual))
7564c1d81b2Sjmcneill 		return false;
75736fffd8dSdyoung 
7584c1d81b2Sjmcneill 	PMF_TRANSITION_PRINTF2(1, ("%s: class resume\n", device_xname(dev)));
759cd6e1fbfSdyoung 	if (!device_pmf_class_resume(dev, qual))
7604c1d81b2Sjmcneill 		return false;
7613df2b2feSdyoung 
7624c1d81b2Sjmcneill 	return true;
7634c1d81b2Sjmcneill }
7644c1d81b2Sjmcneill 
7654c1d81b2Sjmcneill bool
pmf_device_recursive_suspend(device_t dv,const pmf_qual_t * qual)766c1b390d4Sdyoung pmf_device_recursive_suspend(device_t dv, const pmf_qual_t *qual)
7674c1d81b2Sjmcneill {
76849025715Sdyoung 	bool rv = true;
7694c1d81b2Sjmcneill 	device_t curdev;
77049025715Sdyoung 	deviter_t di;
771c1b390d4Sdyoung 	pmf_qual_t pq;
7724c1d81b2Sjmcneill 
77336fffd8dSdyoung 	pmf_qual_recursive_copy(&pq, qual);
7744c1d81b2Sjmcneill 
77549025715Sdyoung 	for (curdev = deviter_first(&di, 0); curdev != NULL;
77649025715Sdyoung 	     curdev = deviter_next(&di)) {
7774c1d81b2Sjmcneill 		if (device_parent(curdev) != dv)
7784c1d81b2Sjmcneill 			continue;
77936fffd8dSdyoung 		if (!pmf_device_recursive_suspend(curdev, &pq)) {
78049025715Sdyoung 			rv = false;
78149025715Sdyoung 			break;
7824c1d81b2Sjmcneill 		}
78349025715Sdyoung 	}
78449025715Sdyoung 	deviter_release(&di);
7854c1d81b2Sjmcneill 
786cd6e1fbfSdyoung 	return rv && pmf_device_suspend(dv, qual);
7874c1d81b2Sjmcneill }
7884c1d81b2Sjmcneill 
78936fffd8dSdyoung void
pmf_qual_recursive_copy(pmf_qual_t * dst,const pmf_qual_t * src)790c1b390d4Sdyoung pmf_qual_recursive_copy(pmf_qual_t *dst, const pmf_qual_t *src)
79136fffd8dSdyoung {
792*89f86b67Sriastradh 
79336fffd8dSdyoung 	*dst = *src;
79436fffd8dSdyoung 	dst->pq_actlvl = DEVACT_LEVEL_FULL;
79536fffd8dSdyoung }
79636fffd8dSdyoung 
7974c1d81b2Sjmcneill bool
pmf_device_recursive_resume(device_t dv,const pmf_qual_t * qual)798c1b390d4Sdyoung pmf_device_recursive_resume(device_t dv, const pmf_qual_t *qual)
7994c1d81b2Sjmcneill {
8004c1d81b2Sjmcneill 	device_t parent;
801c1b390d4Sdyoung 	pmf_qual_t pq;
8024c1d81b2Sjmcneill 
8034c1d81b2Sjmcneill 	if (device_is_active(dv))
8044c1d81b2Sjmcneill 		return true;
8054c1d81b2Sjmcneill 
80636fffd8dSdyoung 	pmf_qual_recursive_copy(&pq, qual);
80736fffd8dSdyoung 
8084c1d81b2Sjmcneill 	parent = device_parent(dv);
8094c1d81b2Sjmcneill 	if (parent != NULL) {
81036fffd8dSdyoung 		if (!pmf_device_recursive_resume(parent, &pq))
8114c1d81b2Sjmcneill 			return false;
8124c1d81b2Sjmcneill 	}
8134c1d81b2Sjmcneill 
814cd6e1fbfSdyoung 	return pmf_device_resume(dv, qual);
8154c1d81b2Sjmcneill }
8164c1d81b2Sjmcneill 
8174c1d81b2Sjmcneill bool
pmf_device_descendants_release(device_t dv,const pmf_qual_t * qual)818c1b390d4Sdyoung pmf_device_descendants_release(device_t dv, const pmf_qual_t *qual)
8194c1d81b2Sjmcneill {
82049025715Sdyoung 	bool rv = true;
8214c1d81b2Sjmcneill 	device_t curdev;
82249025715Sdyoung 	deviter_t di;
8234c1d81b2Sjmcneill 
82449025715Sdyoung 	for (curdev = deviter_first(&di, 0); curdev != NULL;
82549025715Sdyoung 	     curdev = deviter_next(&di)) {
8264c1d81b2Sjmcneill 		if (device_parent(curdev) != dv)
8274c1d81b2Sjmcneill 			continue;
828cd6e1fbfSdyoung 		device_pmf_remove_suspensor(curdev, qual);
829cd6e1fbfSdyoung 		if (!pmf_device_descendants_release(curdev, qual)) {
83049025715Sdyoung 			rv = false;
83149025715Sdyoung 			break;
8324c1d81b2Sjmcneill 		}
83349025715Sdyoung 	}
83449025715Sdyoung 	deviter_release(&di);
83549025715Sdyoung 	return rv;
8364c1d81b2Sjmcneill }
8374c1d81b2Sjmcneill 
838b4f3367aSdyoung bool
pmf_device_descendants_resume(device_t dv,const pmf_qual_t * qual)839c1b390d4Sdyoung pmf_device_descendants_resume(device_t dv, const pmf_qual_t *qual)
840b4f3367aSdyoung {
84136fffd8dSdyoung 	bool rv = true;
84236fffd8dSdyoung 	device_t curdev;
84336fffd8dSdyoung 	deviter_t di;
84436fffd8dSdyoung 
845cd6e1fbfSdyoung 	KASSERT(pmf_qual_descend_ok(qual));
84636fffd8dSdyoung 
84736fffd8dSdyoung 	for (curdev = deviter_first(&di, 0); curdev != NULL;
84836fffd8dSdyoung 	     curdev = deviter_next(&di)) {
84936fffd8dSdyoung 		if (device_parent(curdev) != dv)
85036fffd8dSdyoung 			continue;
851cd6e1fbfSdyoung 		if (!pmf_device_resume(curdev, qual) ||
852cd6e1fbfSdyoung 		    !pmf_device_descendants_resume(curdev, qual)) {
85336fffd8dSdyoung 			rv = false;
85436fffd8dSdyoung 			break;
85536fffd8dSdyoung 		}
85636fffd8dSdyoung 	}
85736fffd8dSdyoung 	deviter_release(&di);
85836fffd8dSdyoung 	return rv;
85936fffd8dSdyoung }
86036fffd8dSdyoung 
86136fffd8dSdyoung bool
pmf_device_subtree_release(device_t dv,const pmf_qual_t * qual)862c1b390d4Sdyoung pmf_device_subtree_release(device_t dv, const pmf_qual_t *qual)
86336fffd8dSdyoung {
864c1b390d4Sdyoung 	pmf_qual_t pq;
86536fffd8dSdyoung 
866cd6e1fbfSdyoung 	device_pmf_remove_suspensor(dv, qual);
86736fffd8dSdyoung 
868c26d0a3aSdyoung 	pmf_qual_recursive_copy(&pq, qual);
869c26d0a3aSdyoung 
87036fffd8dSdyoung 	return pmf_device_descendants_release(dv, &pq);
87136fffd8dSdyoung }
87236fffd8dSdyoung 
87336fffd8dSdyoung bool
pmf_device_subtree_resume(device_t dv,const pmf_qual_t * qual)874c1b390d4Sdyoung pmf_device_subtree_resume(device_t dv, const pmf_qual_t *qual)
87536fffd8dSdyoung {
876c1b390d4Sdyoung 	pmf_qual_t pq;
87736fffd8dSdyoung 
878cd6e1fbfSdyoung 	if (!pmf_device_subtree_release(dv, qual))
87936fffd8dSdyoung 		return false;
88036fffd8dSdyoung 
881cd6e1fbfSdyoung 	if (!pmf_device_recursive_resume(dv, qual))
882b4f3367aSdyoung 		return false;
883b4f3367aSdyoung 
88436fffd8dSdyoung 	pmf_qual_recursive_copy(&pq, qual);
88536fffd8dSdyoung 
88636fffd8dSdyoung 	return pmf_device_descendants_resume(dv, &pq);
887b4f3367aSdyoung }
888b4f3367aSdyoung 
8894c1d81b2Sjmcneill #include <net/if.h>
8904c1d81b2Sjmcneill 
8914c1d81b2Sjmcneill static bool
pmf_class_network_suspend(device_t dev,const pmf_qual_t * qual)892c1b390d4Sdyoung pmf_class_network_suspend(device_t dev, const pmf_qual_t *qual)
8934c1d81b2Sjmcneill {
8944c1d81b2Sjmcneill 	struct ifnet *ifp = device_pmf_class_private(dev);
8954c1d81b2Sjmcneill 	int s;
8964c1d81b2Sjmcneill 
8974c1d81b2Sjmcneill 	s = splnet();
898f906864eSjdolecek 	IFNET_LOCK(ifp);
8993df2b2feSdyoung 	(*ifp->if_stop)(ifp, 0);
900f906864eSjdolecek 	IFNET_UNLOCK(ifp);
9014c1d81b2Sjmcneill 	splx(s);
9024c1d81b2Sjmcneill 
9034c1d81b2Sjmcneill 	return true;
9044c1d81b2Sjmcneill }
9054c1d81b2Sjmcneill 
9064c1d81b2Sjmcneill static bool
pmf_class_network_resume(device_t dev,const pmf_qual_t * qual)907c1b390d4Sdyoung pmf_class_network_resume(device_t dev, const pmf_qual_t *qual)
9084c1d81b2Sjmcneill {
9094c1d81b2Sjmcneill 	struct ifnet *ifp = device_pmf_class_private(dev);
9104c1d81b2Sjmcneill 	int s;
911f906864eSjdolecek 	bool restart = false;
9124c1d81b2Sjmcneill 
9134c1d81b2Sjmcneill 	s = splnet();
914f906864eSjdolecek 	IFNET_LOCK(ifp);
9154c1d81b2Sjmcneill 	if (ifp->if_flags & IFF_UP) {
9164c1d81b2Sjmcneill 		ifp->if_flags &= ~IFF_RUNNING;
917729313d5Sjmcneill 		if ((*ifp->if_init)(ifp) != 0)
918729313d5Sjmcneill 			aprint_normal_ifnet(ifp, "resume failed\n");
919f906864eSjdolecek 		restart = true;
9204c1d81b2Sjmcneill 	}
921f906864eSjdolecek 	IFNET_UNLOCK(ifp);
922f906864eSjdolecek 
923f906864eSjdolecek 	if (restart)
924f906864eSjdolecek 		if_start_lock(ifp);
925f906864eSjdolecek 
9264c1d81b2Sjmcneill 	splx(s);
9274c1d81b2Sjmcneill 
9284c1d81b2Sjmcneill 	return true;
9294c1d81b2Sjmcneill }
9304c1d81b2Sjmcneill 
9314c1d81b2Sjmcneill void
pmf_class_network_register(device_t dev,struct ifnet * ifp)9324c1d81b2Sjmcneill pmf_class_network_register(device_t dev, struct ifnet *ifp)
9334c1d81b2Sjmcneill {
934*89f86b67Sriastradh 
9354c1d81b2Sjmcneill 	device_pmf_class_register(dev, ifp, pmf_class_network_suspend,
9364c1d81b2Sjmcneill 	    pmf_class_network_resume, NULL);
9374c1d81b2Sjmcneill }
9384c1d81b2Sjmcneill 
9394c1d81b2Sjmcneill bool
pmf_event_inject(device_t dv,pmf_generic_event_t ev)9404c1d81b2Sjmcneill pmf_event_inject(device_t dv, pmf_generic_event_t ev)
9414c1d81b2Sjmcneill {
9424c1d81b2Sjmcneill 	pmf_event_workitem_t *pew;
9434c1d81b2Sjmcneill 
944bfd7452aSdyoung 	pew = pmf_event_workitem_get();
9454c1d81b2Sjmcneill 	if (pew == NULL) {
9464c1d81b2Sjmcneill 		PMF_EVENT_PRINTF(("%s: PMF event %d dropped (no memory)\n",
9474c1d81b2Sjmcneill 		    dv ? device_xname(dv) : "<anonymous>", ev));
9484c1d81b2Sjmcneill 		return false;
9494c1d81b2Sjmcneill 	}
9504c1d81b2Sjmcneill 
9514c1d81b2Sjmcneill 	pew->pew_event = ev;
9524c1d81b2Sjmcneill 	pew->pew_device = dv;
9534c1d81b2Sjmcneill 
9549d9978e5Sdyoung 	workqueue_enqueue(pmf_event_workqueue, &pew->pew_work, NULL);
9554c1d81b2Sjmcneill 	PMF_EVENT_PRINTF(("%s: PMF event %d injected\n",
9564c1d81b2Sjmcneill 	    dv ? device_xname(dv) : "<anonymous>", ev));
9574c1d81b2Sjmcneill 
9584c1d81b2Sjmcneill 	return true;
9594c1d81b2Sjmcneill }
9604c1d81b2Sjmcneill 
9614c1d81b2Sjmcneill bool
pmf_event_register(device_t dv,pmf_generic_event_t ev,void (* handler)(device_t),bool global)9624c1d81b2Sjmcneill pmf_event_register(device_t dv, pmf_generic_event_t ev,
9634c1d81b2Sjmcneill     void (*handler)(device_t), bool global)
9644c1d81b2Sjmcneill {
9654c1d81b2Sjmcneill 	pmf_event_handler_t *event;
9664c1d81b2Sjmcneill 
9679d9978e5Sdyoung 	event = kmem_alloc(sizeof(*event), KM_SLEEP);
9684c1d81b2Sjmcneill 	event->pmf_event = ev;
9694c1d81b2Sjmcneill 	event->pmf_handler = handler;
9704c1d81b2Sjmcneill 	event->pmf_device = dv;
9714c1d81b2Sjmcneill 	event->pmf_global = global;
9724c1d81b2Sjmcneill 	TAILQ_INSERT_TAIL(&pmf_all_events, event, pmf_link);
9734c1d81b2Sjmcneill 
9744c1d81b2Sjmcneill 	return true;
9754c1d81b2Sjmcneill }
9764c1d81b2Sjmcneill 
9774c1d81b2Sjmcneill void
pmf_event_deregister(device_t dv,pmf_generic_event_t ev,void (* handler)(device_t),bool global)9784c1d81b2Sjmcneill pmf_event_deregister(device_t dv, pmf_generic_event_t ev,
9794c1d81b2Sjmcneill     void (*handler)(device_t), bool global)
9804c1d81b2Sjmcneill {
9814c1d81b2Sjmcneill 	pmf_event_handler_t *event;
9824c1d81b2Sjmcneill 
983862a6b5fSrmind 	TAILQ_FOREACH(event, &pmf_all_events, pmf_link) {
9844c1d81b2Sjmcneill 		if (event->pmf_event != ev)
9854c1d81b2Sjmcneill 			continue;
9864c1d81b2Sjmcneill 		if (event->pmf_device != dv)
9874c1d81b2Sjmcneill 			continue;
9884c1d81b2Sjmcneill 		if (event->pmf_global != global)
9894c1d81b2Sjmcneill 			continue;
9904c1d81b2Sjmcneill 		if (event->pmf_handler != handler)
9914c1d81b2Sjmcneill 			continue;
9924c1d81b2Sjmcneill 		TAILQ_REMOVE(&pmf_all_events, event, pmf_link);
9939d9978e5Sdyoung 		kmem_free(event, sizeof(*event));
994862a6b5fSrmind 		return;
9954c1d81b2Sjmcneill 	}
9964c1d81b2Sjmcneill }
9974c1d81b2Sjmcneill 
9984c1d81b2Sjmcneill struct display_class_softc {
9994c1d81b2Sjmcneill 	TAILQ_ENTRY(display_class_softc) dc_link;
10004c1d81b2Sjmcneill 	device_t dc_dev;
10014c1d81b2Sjmcneill };
10024c1d81b2Sjmcneill 
10034c1d81b2Sjmcneill static TAILQ_HEAD(, display_class_softc) all_displays;
10044c1d81b2Sjmcneill static callout_t global_idle_counter;
10054c1d81b2Sjmcneill static int idle_timeout = 30;
10064c1d81b2Sjmcneill 
10074c1d81b2Sjmcneill static void
input_idle(void * dummy)10084c1d81b2Sjmcneill input_idle(void *dummy)
10094c1d81b2Sjmcneill {
1010*89f86b67Sriastradh 
10114c1d81b2Sjmcneill 	PMF_IDLE_PRINTF(("Input idle handler called\n"));
10124c1d81b2Sjmcneill 	pmf_event_inject(NULL, PMFE_DISPLAY_OFF);
10134c1d81b2Sjmcneill }
10144c1d81b2Sjmcneill 
10154c1d81b2Sjmcneill static void
input_activity_handler(device_t dv,devactive_t type)10164c1d81b2Sjmcneill input_activity_handler(device_t dv, devactive_t type)
10174c1d81b2Sjmcneill {
1018*89f86b67Sriastradh 
10194c1d81b2Sjmcneill 	if (!TAILQ_EMPTY(&all_displays))
10204c1d81b2Sjmcneill 		callout_schedule(&global_idle_counter, idle_timeout * hz);
10214c1d81b2Sjmcneill }
10224c1d81b2Sjmcneill 
10234c1d81b2Sjmcneill static void
pmf_class_input_deregister(device_t dv)10244c1d81b2Sjmcneill pmf_class_input_deregister(device_t dv)
10254c1d81b2Sjmcneill {
1026*89f86b67Sriastradh 
10274c1d81b2Sjmcneill 	device_active_deregister(dv, input_activity_handler);
10284c1d81b2Sjmcneill }
10294c1d81b2Sjmcneill 
10304c1d81b2Sjmcneill bool
pmf_class_input_register(device_t dv)10314c1d81b2Sjmcneill pmf_class_input_register(device_t dv)
10324c1d81b2Sjmcneill {
1033*89f86b67Sriastradh 
10344c1d81b2Sjmcneill 	if (!device_active_register(dv, input_activity_handler))
10354c1d81b2Sjmcneill 		return false;
10364c1d81b2Sjmcneill 
10374c1d81b2Sjmcneill 	device_pmf_class_register(dv, NULL, NULL, NULL,
10384c1d81b2Sjmcneill 	    pmf_class_input_deregister);
10394c1d81b2Sjmcneill 
10404c1d81b2Sjmcneill 	return true;
10414c1d81b2Sjmcneill }
10424c1d81b2Sjmcneill 
10434c1d81b2Sjmcneill static void
pmf_class_display_deregister(device_t dv)10444c1d81b2Sjmcneill pmf_class_display_deregister(device_t dv)
10454c1d81b2Sjmcneill {
10464c1d81b2Sjmcneill 	struct display_class_softc *sc = device_pmf_class_private(dv);
10474c1d81b2Sjmcneill 	int s;
10484c1d81b2Sjmcneill 
10494c1d81b2Sjmcneill 	s = splsoftclock();
10504c1d81b2Sjmcneill 	TAILQ_REMOVE(&all_displays, sc, dc_link);
10514c1d81b2Sjmcneill 	if (TAILQ_EMPTY(&all_displays))
10524c1d81b2Sjmcneill 		callout_stop(&global_idle_counter);
10534c1d81b2Sjmcneill 	splx(s);
10544c1d81b2Sjmcneill 
10559d9978e5Sdyoung 	kmem_free(sc, sizeof(*sc));
10564c1d81b2Sjmcneill }
10574c1d81b2Sjmcneill 
10584c1d81b2Sjmcneill bool
pmf_class_display_register(device_t dv)10594c1d81b2Sjmcneill pmf_class_display_register(device_t dv)
10604c1d81b2Sjmcneill {
10614c1d81b2Sjmcneill 	struct display_class_softc *sc;
10624c1d81b2Sjmcneill 	int s;
10634c1d81b2Sjmcneill 
10649d9978e5Sdyoung 	sc = kmem_alloc(sizeof(*sc), KM_SLEEP);
10654c1d81b2Sjmcneill 
10664c1d81b2Sjmcneill 	s = splsoftclock();
10674c1d81b2Sjmcneill 	if (TAILQ_EMPTY(&all_displays))
10684c1d81b2Sjmcneill 		callout_schedule(&global_idle_counter, idle_timeout * hz);
10694c1d81b2Sjmcneill 
10704c1d81b2Sjmcneill 	TAILQ_INSERT_HEAD(&all_displays, sc, dc_link);
10714c1d81b2Sjmcneill 	splx(s);
10724c1d81b2Sjmcneill 
10734c1d81b2Sjmcneill 	device_pmf_class_register(dv, sc, NULL, NULL,
10744c1d81b2Sjmcneill 	    pmf_class_display_deregister);
10754c1d81b2Sjmcneill 
10764c1d81b2Sjmcneill 	return true;
10774c1d81b2Sjmcneill }
10784c1d81b2Sjmcneill 
1079bfd7452aSdyoung static void
pmf_event_workitem_put(pmf_event_workitem_t * pew)1080bfd7452aSdyoung pmf_event_workitem_put(pmf_event_workitem_t *pew)
1081bfd7452aSdyoung {
10820ca6708cSrmind 
1083bfd7452aSdyoung 	KASSERT(pew != NULL);
10840ca6708cSrmind 	pool_put(&pew_pl, pew);
1085bfd7452aSdyoung }
1086bfd7452aSdyoung 
1087bfd7452aSdyoung static pmf_event_workitem_t *
pmf_event_workitem_get(void)1088bfd7452aSdyoung pmf_event_workitem_get(void)
1089bfd7452aSdyoung {
1090bfd7452aSdyoung 
10910ca6708cSrmind 	return pool_get(&pew_pl, PR_NOWAIT);
1092bfd7452aSdyoung }
1093bfd7452aSdyoung 
10943211dce1Smlelstv SYSCTL_SETUP(sysctl_pmf_setup, "PMF subtree setup")
10953211dce1Smlelstv {
10963211dce1Smlelstv 	const struct sysctlnode *node = NULL;
10973211dce1Smlelstv 
10983211dce1Smlelstv 	sysctl_createv(clog, 0, NULL, &node,
10993211dce1Smlelstv 	    CTLFLAG_PERMANENT,
11003211dce1Smlelstv 	    CTLTYPE_NODE, "pmf",
11013211dce1Smlelstv 	    SYSCTL_DESCR("pmf controls"),
11023211dce1Smlelstv 	    NULL, 0, NULL, 0,
11033211dce1Smlelstv 	    CTL_KERN, CTL_CREATE, CTL_EOL);
11043211dce1Smlelstv 
11053211dce1Smlelstv #ifdef PMF_DEBUG
11063211dce1Smlelstv 	sysctl_createv(clog, 0, &node, &node,
11073211dce1Smlelstv 	    CTLFLAG_PERMANENT,
11083211dce1Smlelstv 	    CTLTYPE_NODE, "debug",
11093211dce1Smlelstv 	    SYSCTL_DESCR("debug levels"),
11103211dce1Smlelstv 	    NULL, 0, NULL, 0,
11113211dce1Smlelstv 	    CTL_CREATE, CTL_EOL);
11123211dce1Smlelstv 
11133211dce1Smlelstv 	sysctl_createv(clog, 0, &node, NULL,
11143211dce1Smlelstv 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
11153211dce1Smlelstv 	    CTLTYPE_INT, "event",
11163211dce1Smlelstv 	    SYSCTL_DESCR("event"),
11173211dce1Smlelstv 	    NULL, 0,  &pmf_debug_event, sizeof(pmf_debug_event),
11183211dce1Smlelstv 	    CTL_CREATE, CTL_EOL);
11193211dce1Smlelstv 	sysctl_createv(clog, 0, &node, NULL,
11203211dce1Smlelstv 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
11213211dce1Smlelstv 	    CTLTYPE_INT, "suspend",
11223211dce1Smlelstv 	    SYSCTL_DESCR("suspend"),
11233211dce1Smlelstv 	    NULL, 0,  &pmf_debug_suspend, sizeof(pmf_debug_suspend),
11243211dce1Smlelstv 	    CTL_CREATE, CTL_EOL);
11253211dce1Smlelstv 	sysctl_createv(clog, 0, &node, NULL,
11263211dce1Smlelstv 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
11273211dce1Smlelstv 	    CTLTYPE_INT, "suspensor",
11283211dce1Smlelstv 	    SYSCTL_DESCR("suspensor"),
11293211dce1Smlelstv 	    NULL, 0,  &pmf_debug_suspensor, sizeof(pmf_debug_suspensor),
11303211dce1Smlelstv 	    CTL_CREATE, CTL_EOL);
11313211dce1Smlelstv 	sysctl_createv(clog, 0, &node, NULL,
11323211dce1Smlelstv 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
11333211dce1Smlelstv 	    CTLTYPE_INT, "idle",
11343211dce1Smlelstv 	    SYSCTL_DESCR("idle"),
11353211dce1Smlelstv 	    NULL, 0,  &pmf_debug_idle, sizeof(pmf_debug_idle),
11363211dce1Smlelstv 	    CTL_CREATE, CTL_EOL);
11373211dce1Smlelstv 	sysctl_createv(clog, 0, &node, NULL,
11383211dce1Smlelstv 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
11393211dce1Smlelstv 	    CTLTYPE_INT, "transition",
11403211dce1Smlelstv 	    SYSCTL_DESCR("event"),
11413211dce1Smlelstv 	    NULL, 0,  &pmf_debug_transition, sizeof(pmf_debug_transition),
11423211dce1Smlelstv 	    CTL_CREATE, CTL_EOL);
11433211dce1Smlelstv #endif
11443211dce1Smlelstv }
11453211dce1Smlelstv 
11464c1d81b2Sjmcneill void
pmf_init(void)11474c1d81b2Sjmcneill pmf_init(void)
11484c1d81b2Sjmcneill {
11494c1d81b2Sjmcneill 	int err;
11504c1d81b2Sjmcneill 
11510ca6708cSrmind 	pool_init(&pew_pl, sizeof(pmf_event_workitem_t), 0, 0, 0,
11520ca6708cSrmind 	    "pewpl", NULL, IPL_HIGH);
11530ca6708cSrmind 	pool_setlowat(&pew_pl, 1);
11540ca6708cSrmind 	pool_sethiwat(&pew_pl, 8);
1155bfd7452aSdyoung 
11564c1d81b2Sjmcneill 	KASSERT(pmf_event_workqueue == NULL);
11574c1d81b2Sjmcneill 	err = workqueue_create(&pmf_event_workqueue, "pmfevent",
11584d6cb5afSjmcneill 	    pmf_event_worker, NULL, PRI_NONE, IPL_VM, 0);
11594c1d81b2Sjmcneill 	if (err)
11604c1d81b2Sjmcneill 		panic("couldn't create pmfevent workqueue");
11614c1d81b2Sjmcneill 
116236fffd8dSdyoung 	KASSERT(pmf_suspend_workqueue == NULL);
116336fffd8dSdyoung 	err = workqueue_create(&pmf_suspend_workqueue, "pmfsuspend",
116436fffd8dSdyoung 	    pmf_suspend_worker, NULL, PRI_NONE, IPL_VM, 0);
116536fffd8dSdyoung 	if (err)
116636fffd8dSdyoung 		panic("couldn't create pmfsuspend workqueue");
116736fffd8dSdyoung 
11684c1d81b2Sjmcneill 	callout_init(&global_idle_counter, 0);
11694c1d81b2Sjmcneill 	callout_setfunc(&global_idle_counter, input_idle, NULL);
11704c1d81b2Sjmcneill }
1171