xref: /onnv-gate/usr/src/uts/common/io/ppm/ppm.c (revision 4667:2cb417b1d90c)
1*4667Smh27603 /*
2*4667Smh27603  * CDDL HEADER START
3*4667Smh27603  *
4*4667Smh27603  * The contents of this file are subject to the terms of the
5*4667Smh27603  * Common Development and Distribution License (the "License").
6*4667Smh27603  * You may not use this file except in compliance with the License.
7*4667Smh27603  *
8*4667Smh27603  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*4667Smh27603  * or http://www.opensolaris.org/os/licensing.
10*4667Smh27603  * See the License for the specific language governing permissions
11*4667Smh27603  * and limitations under the License.
12*4667Smh27603  *
13*4667Smh27603  * When distributing Covered Code, include this CDDL HEADER in each
14*4667Smh27603  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*4667Smh27603  * If applicable, add the following below this CDDL HEADER, with the
16*4667Smh27603  * fields enclosed by brackets "[]" replaced with your own identifying
17*4667Smh27603  * information: Portions Copyright [yyyy] [name of copyright owner]
18*4667Smh27603  *
19*4667Smh27603  * CDDL HEADER END
20*4667Smh27603  */
21*4667Smh27603 /*
22*4667Smh27603  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*4667Smh27603  * Use is subject to license terms.
24*4667Smh27603  */
25*4667Smh27603 
26*4667Smh27603 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*4667Smh27603 
28*4667Smh27603 /*
29*4667Smh27603  * Platform Power Management master pseudo driver -
30*4667Smh27603  *    - attaches only  when ppm.conf file is present, indicating a
31*4667Smh27603  *      workstation (since Excalibur era ) that is designed to
32*4667Smh27603  *      be MOU-3 EPA compliant and which uses platform-specific
33*4667Smh27603  *	hardware to do so;
34*4667Smh27603  *    - this pseudo driver uses a set of simple satellite
35*4667Smh27603  *      device drivers responsible for accessing platform
36*4667Smh27603  *      specific devices to modify the registers they own.
37*4667Smh27603  *	ppm drivers tells these	satellite drivers what to do
38*4667Smh27603  *	according to using command values taken from ppm.conf.
39*4667Smh27603  */
40*4667Smh27603 #include <sys/conf.h>
41*4667Smh27603 #include <sys/stat.h>
42*4667Smh27603 #include <sys/file.h>
43*4667Smh27603 #include <sys/types.h>
44*4667Smh27603 #include <sys/param.h>
45*4667Smh27603 #include <sys/open.h>
46*4667Smh27603 #include <sys/callb.h>
47*4667Smh27603 #include <sys/va_list.h>
48*4667Smh27603 #include <sys/errno.h>
49*4667Smh27603 #include <sys/modctl.h>
50*4667Smh27603 #include <sys/sysmacros.h>
51*4667Smh27603 #include <sys/ddi_impldefs.h>
52*4667Smh27603 #include <sys/promif.h>
53*4667Smh27603 #include <sys/epm.h>
54*4667Smh27603 #include <sys/sunpm.h>
55*4667Smh27603 #include <sys/ppmio.h>
56*4667Smh27603 #include <sys/sunldi.h>
57*4667Smh27603 #include <sys/ppmvar.h>
58*4667Smh27603 #include <sys/ddi.h>
59*4667Smh27603 #include <sys/sunddi.h>
60*4667Smh27603 #include <sys/ppm_plat.h>
61*4667Smh27603 
62*4667Smh27603 /*
63*4667Smh27603  * Note: When pm_power() is called (directly or indirectly) to change the
64*4667Smh27603  * power level of a device and the call returns failure, DO NOT assume the
65*4667Smh27603  * level is unchanged.  Doublecheck it against ppmd->level.
66*4667Smh27603  */
67*4667Smh27603 
68*4667Smh27603 /*
69*4667Smh27603  * cb_ops
70*4667Smh27603  */
71*4667Smh27603 static int	ppm_open(dev_t *, int, int, cred_t *);
72*4667Smh27603 static int	ppm_close(dev_t, int, int, cred_t *);
73*4667Smh27603 static int	ppm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
74*4667Smh27603 
75*4667Smh27603 static struct cb_ops ppm_cb_ops = {
76*4667Smh27603 	ppm_open,		/* open	*/
77*4667Smh27603 	ppm_close,		/* close */
78*4667Smh27603 	nodev,			/* strategy */
79*4667Smh27603 	nodev,			/* print */
80*4667Smh27603 	nodev,			/* dump */
81*4667Smh27603 	nodev,			/* read */
82*4667Smh27603 	nodev,			/* write */
83*4667Smh27603 	ppm_ioctl,		/* ioctl */
84*4667Smh27603 	nodev,			/* devmap */
85*4667Smh27603 	nodev,			/* mmap */
86*4667Smh27603 	nodev,			/* segmap */
87*4667Smh27603 	nochpoll,		/* poll */
88*4667Smh27603 	ddi_prop_op,		/* prop_op */
89*4667Smh27603 	NULL,			/* streamtab */
90*4667Smh27603 	D_MP | D_NEW,		/* driver compatibility flag */
91*4667Smh27603 	CB_REV,			/* cb_ops revision */
92*4667Smh27603 	nodev,			/* async read */
93*4667Smh27603 	nodev			/* async write */
94*4667Smh27603 };
95*4667Smh27603 
96*4667Smh27603 /*
97*4667Smh27603  * bus_ops
98*4667Smh27603  */
99*4667Smh27603 static int	ppm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
100*4667Smh27603     void *);
101*4667Smh27603 
102*4667Smh27603 static struct bus_ops ppm_bus_ops = {
103*4667Smh27603 	BUSO_REV,		/* busops_rev		*/
104*4667Smh27603 	0,			/* bus_map		*/
105*4667Smh27603 	0,			/* bus_get_intrspec	*/
106*4667Smh27603 	0,			/* bus_add_intrspec	*/
107*4667Smh27603 	0,			/* bus_remove_intrspec	*/
108*4667Smh27603 	0,			/* bus_map_fault	*/
109*4667Smh27603 	ddi_no_dma_map,		/* bus_dma_map		*/
110*4667Smh27603 	ddi_no_dma_allochdl,	/* bus_dma_allochdl	*/
111*4667Smh27603 	NULL,			/* bus_dma_freehdl	*/
112*4667Smh27603 	NULL,			/* bus_dma_bindhdl	*/
113*4667Smh27603 	NULL,			/* bus_dma_unbindhdl	*/
114*4667Smh27603 	NULL,			/* bus_dma_flush	*/
115*4667Smh27603 	NULL,			/* bus_dma_win		*/
116*4667Smh27603 	NULL,			/* bus_dma_ctl		*/
117*4667Smh27603 	ppm_ctlops,		/* bus_ctl		*/
118*4667Smh27603 	0,			/* bus_prop_op		*/
119*4667Smh27603 	0,			/* bus_get_eventcookie	*/
120*4667Smh27603 	0,			/* bus_add_eventcall	*/
121*4667Smh27603 	0,			/* bus_remove_eventcall	*/
122*4667Smh27603 	0,			/* bus_post_event	*/
123*4667Smh27603 	0			/* bus_intr_ctl		*/
124*4667Smh27603 };
125*4667Smh27603 
126*4667Smh27603 /*
127*4667Smh27603  * dev_ops
128*4667Smh27603  */
129*4667Smh27603 static int	ppm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
130*4667Smh27603 static int	ppm_attach(dev_info_t *, ddi_attach_cmd_t);
131*4667Smh27603 static int	ppm_detach(dev_info_t *, ddi_detach_cmd_t);
132*4667Smh27603 
133*4667Smh27603 static struct dev_ops ppm_ops = {
134*4667Smh27603 	DEVO_REV,		/* devo_rev */
135*4667Smh27603 	0,			/* refcnt */
136*4667Smh27603 	ppm_getinfo,		/* info */
137*4667Smh27603 	nulldev,		/* identify */
138*4667Smh27603 	nulldev,		/* probe */
139*4667Smh27603 	ppm_attach,		/* attach */
140*4667Smh27603 	ppm_detach,		/* detach */
141*4667Smh27603 	nodev,			/* reset */
142*4667Smh27603 	&ppm_cb_ops,		/* cb_ops */
143*4667Smh27603 	&ppm_bus_ops,		/* bus_ops */
144*4667Smh27603 	nulldev			/* power */
145*4667Smh27603 };
146*4667Smh27603 
147*4667Smh27603 extern struct mod_ops mod_driverops;
148*4667Smh27603 
149*4667Smh27603 static struct modldrv modldrv = {
150*4667Smh27603 	&mod_driverops,
151*4667Smh27603 	"platform pm driver v%I%",
152*4667Smh27603 	&ppm_ops
153*4667Smh27603 };
154*4667Smh27603 
155*4667Smh27603 static struct modlinkage modlinkage = {
156*4667Smh27603 	MODREV_1,
157*4667Smh27603 	&modldrv,
158*4667Smh27603 	NULL
159*4667Smh27603 };
160*4667Smh27603 
161*4667Smh27603 /*
162*4667Smh27603  * Global data structure and variables
163*4667Smh27603  */
164*4667Smh27603 int	ppm_inst = -1;
165*4667Smh27603 void	*ppm_statep;
166*4667Smh27603 ppm_domain_t *ppm_domain_p;
167*4667Smh27603 callb_id_t   *ppm_cprcb_id;
168*4667Smh27603 static kmutex_t ppm_cpr_window_lock;	/* guard ppm_cpr_window_flag */
169*4667Smh27603 static	boolean_t ppm_cpr_window_flag;	/* set indicating chpt-resume period */
170*4667Smh27603 
171*4667Smh27603 /* LED actions */
172*4667Smh27603 #define	PPM_LED_SOLIDON		0
173*4667Smh27603 #define	PPM_LED_BLINKING	1
174*4667Smh27603 
175*4667Smh27603 /*
176*4667Smh27603  * Debug
177*4667Smh27603  */
178*4667Smh27603 #ifdef	DEBUG
179*4667Smh27603 uint_t	ppm_debug = 0;
180*4667Smh27603 #endif
181*4667Smh27603 
182*4667Smh27603 /*
183*4667Smh27603  * Local function prototypes and data
184*4667Smh27603  */
185*4667Smh27603 static boolean_t	ppm_cpr_callb(void *, int);
186*4667Smh27603 static int		ppm_fetset(ppm_domain_t *, uint8_t);
187*4667Smh27603 static int		ppm_fetget(ppm_domain_t *, uint8_t *);
188*4667Smh27603 static int		ppm_gpioset(ppm_domain_t *, int);
189*4667Smh27603 static int		ppm_manage_cpus(dev_info_t *, power_req_t *, int *);
190*4667Smh27603 static int		ppm_manage_pci(dev_info_t *, power_req_t *, int *);
191*4667Smh27603 static int		ppm_manage_pcie(dev_info_t *, power_req_t *, int *);
192*4667Smh27603 static int		ppm_manage_fet(dev_info_t *, power_req_t *, int *);
193*4667Smh27603 static void		ppm_manage_led(int);
194*4667Smh27603 static void		ppm_set_led(ppm_domain_t *, int);
195*4667Smh27603 static void		ppm_blink_led(void *);
196*4667Smh27603 static void		ppm_svc_resume_ctlop(dev_info_t *, power_req_t *);
197*4667Smh27603 static int		ppm_set_level(ppm_dev_t *, int, int, boolean_t);
198*4667Smh27603 static int		ppm_change_power_level(ppm_dev_t *, int, int);
199*4667Smh27603 static int		ppm_record_level_change(ppm_dev_t *, int, int);
200*4667Smh27603 static int		ppm_switch_clock(ppm_domain_t *, int);
201*4667Smh27603 static int		ppm_pcie_pwr(ppm_domain_t *, int);
202*4667Smh27603 static int		ppm_power_up_domain(dev_info_t *dip);
203*4667Smh27603 static int		ppm_power_down_domain(dev_info_t *dip);
204*4667Smh27603 
205*4667Smh27603 int
206*4667Smh27603 _init(void)
207*4667Smh27603 {
208*4667Smh27603 	if (ddi_soft_state_init(
209*4667Smh27603 	    &ppm_statep, sizeof (ppm_unit_t), 1) != DDI_SUCCESS)
210*4667Smh27603 		return (DDI_FAILURE);
211*4667Smh27603 
212*4667Smh27603 	if (mod_install(&modlinkage) != DDI_SUCCESS) {
213*4667Smh27603 		ddi_soft_state_fini(&ppm_statep);
214*4667Smh27603 		return (DDI_FAILURE);
215*4667Smh27603 	}
216*4667Smh27603 	return (DDI_SUCCESS);
217*4667Smh27603 }
218*4667Smh27603 
219*4667Smh27603 
220*4667Smh27603 int
221*4667Smh27603 _fini(void)
222*4667Smh27603 {
223*4667Smh27603 	return (mod_remove(&modlinkage));
224*4667Smh27603 }
225*4667Smh27603 
226*4667Smh27603 
227*4667Smh27603 int
228*4667Smh27603 _info(struct modinfo *modinfop)
229*4667Smh27603 {
230*4667Smh27603 	return (mod_info(&modlinkage, modinfop));
231*4667Smh27603 }
232*4667Smh27603 
233*4667Smh27603 
234*4667Smh27603 /* ARGSUSED */
235*4667Smh27603 int
236*4667Smh27603 ppm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
237*4667Smh27603 {
238*4667Smh27603 	struct ppm_unit *unitp;
239*4667Smh27603 	dev_t	dev;
240*4667Smh27603 	int	instance;
241*4667Smh27603 	int	rval;
242*4667Smh27603 
243*4667Smh27603 	if (ppm_inst == -1)
244*4667Smh27603 		return (DDI_FAILURE);
245*4667Smh27603 
246*4667Smh27603 	switch (cmd) {
247*4667Smh27603 	case DDI_INFO_DEVT2DEVINFO:
248*4667Smh27603 		if (unitp = ddi_get_soft_state(ppm_statep, (dev_t)arg)) {
249*4667Smh27603 			*resultp = unitp->dip;
250*4667Smh27603 			rval = DDI_SUCCESS;
251*4667Smh27603 		} else
252*4667Smh27603 			rval = DDI_FAILURE;
253*4667Smh27603 
254*4667Smh27603 		return (rval);
255*4667Smh27603 
256*4667Smh27603 	case DDI_INFO_DEVT2INSTANCE:
257*4667Smh27603 		dev = (dev_t)arg;
258*4667Smh27603 		instance = getminor(dev);
259*4667Smh27603 		*resultp = (void *)(uintptr_t)instance;
260*4667Smh27603 		return (DDI_SUCCESS);
261*4667Smh27603 
262*4667Smh27603 	default:
263*4667Smh27603 		return (DDI_FAILURE);
264*4667Smh27603 	}
265*4667Smh27603 }
266*4667Smh27603 
267*4667Smh27603 
268*4667Smh27603 /*
269*4667Smh27603  * attach(9E)
270*4667Smh27603  */
271*4667Smh27603 static int
272*4667Smh27603 ppm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
273*4667Smh27603 {
274*4667Smh27603 	ppm_unit_t *unitp;
275*4667Smh27603 	int ret;
276*4667Smh27603 #ifdef	DEBUG
277*4667Smh27603 	char *str = "ppm_attach";
278*4667Smh27603 #endif
279*4667Smh27603 
280*4667Smh27603 
281*4667Smh27603 	switch (cmd) {
282*4667Smh27603 	case DDI_ATTACH:
283*4667Smh27603 		PPMD(D_ATTACH, ("%s: attaching ...\n", str))
284*4667Smh27603 		break;
285*4667Smh27603 
286*4667Smh27603 	case DDI_RESUME:
287*4667Smh27603 		PPMD(D_ATTACH, ("%s: Resuming ...\n", str))
288*4667Smh27603 		unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
289*4667Smh27603 		mutex_enter(&unitp->lock);
290*4667Smh27603 		unitp->states &= ~PPM_STATE_SUSPENDED;
291*4667Smh27603 		mutex_exit(&unitp->lock);
292*4667Smh27603 		return (DDI_SUCCESS);
293*4667Smh27603 
294*4667Smh27603 	default:
295*4667Smh27603 		cmn_err(CE_WARN, "ppm_attach: unknown command %d, dip(0x%p)",
296*4667Smh27603 		    cmd, (void *)dip);
297*4667Smh27603 		return (DDI_FAILURE);
298*4667Smh27603 	}
299*4667Smh27603 
300*4667Smh27603 	if (ppm_inst != -1) {
301*4667Smh27603 		PPMD(D_ATTACH, ("%s: Already attached !", str))
302*4667Smh27603 		return (DDI_FAILURE);
303*4667Smh27603 	}
304*4667Smh27603 
305*4667Smh27603 	ppm_inst = ddi_get_instance(dip);
306*4667Smh27603 	if (ddi_soft_state_zalloc(ppm_statep, ppm_inst) != DDI_SUCCESS) {
307*4667Smh27603 		PPMD(D_ATTACH, ("%s: soft states alloc error!\n", str))
308*4667Smh27603 		return (DDI_FAILURE);
309*4667Smh27603 	}
310*4667Smh27603 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
311*4667Smh27603 
312*4667Smh27603 	ret = ddi_create_minor_node(dip, "ppm", S_IFCHR, ppm_inst,
313*4667Smh27603 	    "ddi_ppm", 0);
314*4667Smh27603 	if (ret != DDI_SUCCESS) {
315*4667Smh27603 		PPMD(D_ATTACH, ("%s: can't create minor node!\n", str))
316*4667Smh27603 		goto fail1;
317*4667Smh27603 	}
318*4667Smh27603 
319*4667Smh27603 	unitp->dip = dip;
320*4667Smh27603 	mutex_init(&unitp->lock, NULL, MUTEX_DRIVER, NULL);
321*4667Smh27603 
322*4667Smh27603 	/*
323*4667Smh27603 	 * read ppm.conf, construct ppm_domain data structure and
324*4667Smh27603 	 * their sub data structure.
325*4667Smh27603 	 */
326*4667Smh27603 	if ((ret = ppm_create_db(dip)) != DDI_SUCCESS)
327*4667Smh27603 		goto fail2;
328*4667Smh27603 
329*4667Smh27603 	/*
330*4667Smh27603 	 * walk down ppm domain control from each domain, initialize
331*4667Smh27603 	 * domain control orthogonal function call handle
332*4667Smh27603 	 */
333*4667Smh27603 	ppm_init_cb(dip);
334*4667Smh27603 
335*4667Smh27603 	if ((ret = pm_register_ppm(ppm_claim_dev, dip)) != DDI_SUCCESS) {
336*4667Smh27603 		cmn_err(CE_WARN, "ppm_attach: can't register ppm handler!");
337*4667Smh27603 		goto fail2;
338*4667Smh27603 	}
339*4667Smh27603 
340*4667Smh27603 	mutex_init(&ppm_cpr_window_lock, NULL, MUTEX_DRIVER, NULL);
341*4667Smh27603 	ppm_cpr_window_flag = B_FALSE;
342*4667Smh27603 	ppm_cprcb_id = callb_add(ppm_cpr_callb, (void *)NULL,
343*4667Smh27603 	    CB_CL_CPR_PM, "ppm_cpr");
344*4667Smh27603 
345*4667Smh27603 #if defined(__x86)
346*4667Smh27603 	/*
347*4667Smh27603 	 * Register callback so that once CPUs have been added to
348*4667Smh27603 	 * the device tree, ppm can rebuild CPU domains using ACPI
349*4667Smh27603 	 * data.
350*4667Smh27603 	 */
351*4667Smh27603 	cpupm_rebuild_cpu_domains = ppm_rebuild_cpu_domains;
352*4667Smh27603 
353*4667Smh27603 	/*
354*4667Smh27603 	 * Register callback so that the ppm can initialize the
355*4667Smh27603 	 * topspeed for all CPUs in all domains.
356*4667Smh27603 	 */
357*4667Smh27603 	cpupm_init_topspeed = ppm_init_topspeed;
358*4667Smh27603 
359*4667Smh27603 	/*
360*4667Smh27603 	 * Register callback so that whenever max speed throttle requests
361*4667Smh27603 	 * are received, ppm can redefine the high power level for
362*4667Smh27603 	 * all CPUs in the domain.
363*4667Smh27603 	 */
364*4667Smh27603 	cpupm_redefine_topspeed = ppm_redefine_topspeed;
365*4667Smh27603 #endif
366*4667Smh27603 
367*4667Smh27603 	ddi_report_dev(dip);
368*4667Smh27603 	return (DDI_SUCCESS);
369*4667Smh27603 
370*4667Smh27603 fail2:
371*4667Smh27603 	ddi_remove_minor_node(dip, "ddi_ppm");
372*4667Smh27603 	mutex_destroy(&unitp->lock);
373*4667Smh27603 fail1:
374*4667Smh27603 	ddi_soft_state_free(ppm_statep, ppm_inst);
375*4667Smh27603 	ppm_inst = -1;
376*4667Smh27603 	return (DDI_FAILURE);
377*4667Smh27603 }
378*4667Smh27603 
379*4667Smh27603 
380*4667Smh27603 /* ARGSUSED */
381*4667Smh27603 static int
382*4667Smh27603 ppm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
383*4667Smh27603 {
384*4667Smh27603 	ppm_unit_t *unitp;
385*4667Smh27603 #ifdef	DEBUG
386*4667Smh27603 	char *str = "ppm_detach";
387*4667Smh27603 #endif
388*4667Smh27603 
389*4667Smh27603 	switch (cmd) {
390*4667Smh27603 	case DDI_DETACH:
391*4667Smh27603 		PPMD(D_DETACH, ("%s: detach not allowed.\n", str))
392*4667Smh27603 		return (DDI_FAILURE);
393*4667Smh27603 
394*4667Smh27603 	case DDI_SUSPEND:
395*4667Smh27603 		PPMD(D_DETACH, ("%s: suspending ...\n", str))
396*4667Smh27603 		unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
397*4667Smh27603 		mutex_enter(&unitp->lock);
398*4667Smh27603 		unitp->states |= PPM_STATE_SUSPENDED;
399*4667Smh27603 		mutex_exit(&unitp->lock);
400*4667Smh27603 
401*4667Smh27603 		/*
402*4667Smh27603 		 * Suspend requires that timeout callouts to be canceled.
403*4667Smh27603 		 * Turning off the LED blinking will cancel the timeout.
404*4667Smh27603 		 */
405*4667Smh27603 		ppm_manage_led(PPM_LED_SOLIDON);
406*4667Smh27603 		return (DDI_SUCCESS);
407*4667Smh27603 
408*4667Smh27603 	default:
409*4667Smh27603 		cmn_err(CE_WARN, "ppm_detach: unsupported command %d, dip(%p)",
410*4667Smh27603 		    cmd, (void *)dip);
411*4667Smh27603 		return (DDI_FAILURE);
412*4667Smh27603 	}
413*4667Smh27603 }
414*4667Smh27603 
415*4667Smh27603 
416*4667Smh27603 /* ARGSUSED */
417*4667Smh27603 int
418*4667Smh27603 ppm_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
419*4667Smh27603 {
420*4667Smh27603 	if (otyp != OTYP_CHR)
421*4667Smh27603 		return (EINVAL);
422*4667Smh27603 	PPMD(D_OPEN, ("ppm_open: devp 0x%p, flag 0x%x, otyp %d\n",
423*4667Smh27603 	    (void *)devp, flag, otyp))
424*4667Smh27603 	return (0);
425*4667Smh27603 }
426*4667Smh27603 
427*4667Smh27603 
428*4667Smh27603 /* ARGSUSED */
429*4667Smh27603 int
430*4667Smh27603 ppm_close(dev_t dev, int flag, int otyp, cred_t *credp)
431*4667Smh27603 {
432*4667Smh27603 	PPMD(D_CLOSE, ("ppm_close: dev 0x%lx, flag 0x%x, otyp %d\n",
433*4667Smh27603 	    dev, flag, otyp))
434*4667Smh27603 	return (0);
435*4667Smh27603 }
436*4667Smh27603 
437*4667Smh27603 
438*4667Smh27603 /* ARGSUSED */
439*4667Smh27603 int
440*4667Smh27603 ppm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
441*4667Smh27603     int *rval_p)
442*4667Smh27603 {
443*4667Smh27603 #ifdef DEBUG
444*4667Smh27603 	char *str = "ppm_ioctl";
445*4667Smh27603 #endif
446*4667Smh27603 	ppm_domain_t *domp = NULL;
447*4667Smh27603 	uint8_t level, lvl;
448*4667Smh27603 	int ret = 0;
449*4667Smh27603 
450*4667Smh27603 	PPMD(D_IOCTL, ("%s: dev 0x%lx, cmd 0x%x, mode 0x%x\n",
451*4667Smh27603 	    str, dev, cmd, mode))
452*4667Smh27603 
453*4667Smh27603 	switch (cmd) {
454*4667Smh27603 	case PPMGET_DPWR:
455*4667Smh27603 	{
456*4667Smh27603 		STRUCT_DECL(ppm_dpwr, dpwr);
457*4667Smh27603 		struct ppm_unit *unitp;
458*4667Smh27603 		char *domain;
459*4667Smh27603 
460*4667Smh27603 		STRUCT_INIT(dpwr, mode);
461*4667Smh27603 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(dpwr),
462*4667Smh27603 		    STRUCT_SIZE(dpwr), mode);
463*4667Smh27603 		if (ret != 0)
464*4667Smh27603 			return (EFAULT);
465*4667Smh27603 
466*4667Smh27603 		/* copyin domain name */
467*4667Smh27603 		domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
468*4667Smh27603 		ret = copyinstr(
469*4667Smh27603 		    STRUCT_FGETP(dpwr, domain), domain, MAXNAMELEN, NULL);
470*4667Smh27603 		if (ret != 0) {
471*4667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n",
472*4667Smh27603 			    str, __LINE__))
473*4667Smh27603 			ret = EFAULT;
474*4667Smh27603 			goto err_dpwr;
475*4667Smh27603 		}
476*4667Smh27603 
477*4667Smh27603 		/* locate domain */
478*4667Smh27603 		if ((domp = ppm_lookup_domain(domain)) == NULL) {
479*4667Smh27603 			PPMD(D_IOCTL, ("%s: no such domain %s\n", str, domain))
480*4667Smh27603 			ret = ENODEV;
481*4667Smh27603 			goto err_dpwr;
482*4667Smh27603 		}
483*4667Smh27603 
484*4667Smh27603 		switch (domp->model) {
485*4667Smh27603 		case PPMD_FET:	/* report power fet ON or OFF */
486*4667Smh27603 			if ((ret = ppm_fetget(domp, &lvl)) != 0) {
487*4667Smh27603 				ret = EIO;
488*4667Smh27603 				goto err_dpwr;
489*4667Smh27603 			}
490*4667Smh27603 			level = (lvl == PPMD_ON) ?
491*4667Smh27603 			    PPMIO_POWER_ON : PPMIO_POWER_OFF;
492*4667Smh27603 			break;
493*4667Smh27603 
494*4667Smh27603 		case PPMD_PCI:	/* report pci slot clock ON or OFF */
495*4667Smh27603 		case PPMD_PCI_PROP:
496*4667Smh27603 		case PPMD_PCIE:
497*4667Smh27603 			level = (domp->status == PPMD_ON) ?
498*4667Smh27603 			    PPMIO_POWER_ON : PPMIO_POWER_OFF;
499*4667Smh27603 			break;
500*4667Smh27603 
501*4667Smh27603 		case PPMD_LED:	/* report LED blinking or solid on */
502*4667Smh27603 
503*4667Smh27603 			unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
504*4667Smh27603 			if (unitp->led_tid == 0)
505*4667Smh27603 				level = PPMIO_LED_SOLIDON;
506*4667Smh27603 			else
507*4667Smh27603 				level = PPMIO_LED_BLINKING;
508*4667Smh27603 			break;
509*4667Smh27603 
510*4667Smh27603 		case PPMD_CPU:	/* report cpu speed divisor */
511*4667Smh27603 			level = domp->devlist->level;
512*4667Smh27603 			break;
513*4667Smh27603 
514*4667Smh27603 		default:
515*4667Smh27603 			ret = EINVAL;
516*4667Smh27603 			goto err_dpwr;
517*4667Smh27603 		}
518*4667Smh27603 
519*4667Smh27603 		STRUCT_FSET(dpwr, level, level);
520*4667Smh27603 		ret = ddi_copyout(STRUCT_BUF(dpwr), (caddr_t)arg,
521*4667Smh27603 		    STRUCT_SIZE(dpwr), mode);
522*4667Smh27603 		if (ret != 0) {
523*4667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n",
524*4667Smh27603 			    str, __LINE__))
525*4667Smh27603 			ret = EFAULT;
526*4667Smh27603 		}
527*4667Smh27603 err_dpwr:
528*4667Smh27603 		kmem_free(domain, MAXNAMELEN);
529*4667Smh27603 
530*4667Smh27603 		break;
531*4667Smh27603 	}
532*4667Smh27603 
533*4667Smh27603 	case PPMGET_DOMBYDEV:
534*4667Smh27603 	{
535*4667Smh27603 		STRUCT_DECL(ppm_bydev, bydev);
536*4667Smh27603 		char *path = NULL;
537*4667Smh27603 		size_t   size, l;
538*4667Smh27603 
539*4667Smh27603 		STRUCT_INIT(bydev, mode);
540*4667Smh27603 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydev),
541*4667Smh27603 		    STRUCT_SIZE(bydev), mode);
542*4667Smh27603 		if (ret != 0)
543*4667Smh27603 			return (EFAULT);
544*4667Smh27603 
545*4667Smh27603 		/* copyin .path */
546*4667Smh27603 		path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
547*4667Smh27603 		ret = copyinstr(
548*4667Smh27603 		    STRUCT_FGETP(bydev, path), path, MAXPATHLEN, NULL);
549*4667Smh27603 		if (ret != 0) {
550*4667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n",
551*4667Smh27603 			    str, __LINE__))
552*4667Smh27603 			kmem_free(path, MAXPATHLEN);
553*4667Smh27603 			return (EFAULT);
554*4667Smh27603 		}
555*4667Smh27603 
556*4667Smh27603 		/* so far we have up to one domain for a given device */
557*4667Smh27603 		size = STRUCT_FGET(bydev, size);
558*4667Smh27603 		domp = ppm_get_domain_by_dev(path);
559*4667Smh27603 		kmem_free(path, MAXPATHLEN);
560*4667Smh27603 		if (domp != NULL) {
561*4667Smh27603 			l = strlen(domp->name) + 1;
562*4667Smh27603 			if (l > size) {
563*4667Smh27603 				PPMD(D_IOCTL, ("%s: buffer too small\n", str))
564*4667Smh27603 				return ((size == 0) ? EINVAL : EFAULT);
565*4667Smh27603 			}
566*4667Smh27603 		} else	/* no domain found to be associated with given device */
567*4667Smh27603 			return (ENODEV);
568*4667Smh27603 
569*4667Smh27603 		ret = copyoutstr(
570*4667Smh27603 		    domp->name, STRUCT_FGETP(bydev, domlist), l, &l);
571*4667Smh27603 		if (ret != 0) {
572*4667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyout domlist, line(%d)"
573*4667Smh27603 			    " \n", str, __LINE__))
574*4667Smh27603 			return (EFAULT);
575*4667Smh27603 		}
576*4667Smh27603 
577*4667Smh27603 		break;
578*4667Smh27603 	}
579*4667Smh27603 
580*4667Smh27603 
581*4667Smh27603 	case PPMGET_DEVBYDOM:
582*4667Smh27603 	{
583*4667Smh27603 		STRUCT_DECL(ppm_bydom, bydom);
584*4667Smh27603 		char *domain = NULL;
585*4667Smh27603 		char *devlist = NULL;
586*4667Smh27603 		ppm_dev_t *ppmd;
587*4667Smh27603 		dev_info_t *odip = NULL;
588*4667Smh27603 		char *s, *d;
589*4667Smh27603 		size_t  size, l;
590*4667Smh27603 
591*4667Smh27603 		STRUCT_INIT(bydom, mode);
592*4667Smh27603 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydom),
593*4667Smh27603 		    STRUCT_SIZE(bydom), mode);
594*4667Smh27603 		if (ret != 0)
595*4667Smh27603 			return (EFAULT);
596*4667Smh27603 
597*4667Smh27603 		/* copyin .domain */
598*4667Smh27603 		domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
599*4667Smh27603 		ret = copyinstr(STRUCT_FGETP(bydom, domain), domain,
600*4667Smh27603 		    MAXNAMELEN, NULL);
601*4667Smh27603 		if (ret != 0) {
602*4667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n",
603*4667Smh27603 			    str, __LINE__))
604*4667Smh27603 			ret = EFAULT;
605*4667Smh27603 			goto err_bydom;
606*4667Smh27603 		}
607*4667Smh27603 
608*4667Smh27603 		/* locate domain */
609*4667Smh27603 		if ((domp = ppm_lookup_domain(domain)) == NULL) {
610*4667Smh27603 			ret = ENODEV;
611*4667Smh27603 			goto err_bydom;
612*4667Smh27603 		}
613*4667Smh27603 
614*4667Smh27603 		l = 0;
615*4667Smh27603 		if ((size = STRUCT_FGET(bydom, size)) == 0)
616*4667Smh27603 			ret = EINVAL;
617*4667Smh27603 		else
618*4667Smh27603 			if ((d = devlist = kmem_zalloc(size, KM_SLEEP)) == NULL)
619*4667Smh27603 				ret = EFAULT;
620*4667Smh27603 		if (ret != 0)
621*4667Smh27603 			goto err_bydom;
622*4667Smh27603 
623*4667Smh27603 		for (ppmd = domp->devlist; ppmd;
624*4667Smh27603 		    odip = ppmd->dip, ppmd = ppmd->next) {
625*4667Smh27603 
626*4667Smh27603 			if (ppmd->dip == odip)
627*4667Smh27603 				continue;
628*4667Smh27603 			if (ppmd != domp->devlist)
629*4667Smh27603 				*d++ = ' ';
630*4667Smh27603 
631*4667Smh27603 			l += strlen(ppmd->path) + 1;
632*4667Smh27603 			if (l > size) {
633*4667Smh27603 				PPMD(D_IOCTL, ("%s: buffer overflow\n", str))
634*4667Smh27603 				ret = EFAULT;
635*4667Smh27603 				goto err_bydom;
636*4667Smh27603 			}
637*4667Smh27603 
638*4667Smh27603 			for (s = ppmd->path; *s != 0; )
639*4667Smh27603 				*d++ = *s++;
640*4667Smh27603 		}
641*4667Smh27603 		*d = 0;
642*4667Smh27603 
643*4667Smh27603 		if (*devlist == 0)
644*4667Smh27603 			goto err_bydom;
645*4667Smh27603 
646*4667Smh27603 		ret = copyoutstr(
647*4667Smh27603 		    devlist, STRUCT_FGETP(bydom, devlist), l, &l);
648*4667Smh27603 		if (ret != 0) {
649*4667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyout devlist, line(%d)"
650*4667Smh27603 			    " \n", str, __LINE__))
651*4667Smh27603 			ret = EFAULT;
652*4667Smh27603 		}
653*4667Smh27603 
654*4667Smh27603 err_bydom:
655*4667Smh27603 		if (devlist)
656*4667Smh27603 			kmem_free(devlist, size);
657*4667Smh27603 		if (domain)
658*4667Smh27603 			kmem_free(domain, MAXNAMELEN);
659*4667Smh27603 
660*4667Smh27603 		break;
661*4667Smh27603 	}
662*4667Smh27603 
663*4667Smh27603 #if defined(__x86)
664*4667Smh27603 	/*
665*4667Smh27603 	 * Note that these two ioctls exist for test purposes only.
666*4667Smh27603 	 * Unfortunately, there really isn't any other good way of
667*4667Smh27603 	 * unit testing the dynamic redefinition of the top speed as it
668*4667Smh27603 	 * usually occurs due to environmental conditions.
669*4667Smh27603 	 */
670*4667Smh27603 	case PPMGET_NORMAL:
671*4667Smh27603 	case PPMSET_NORMAL:
672*4667Smh27603 	{
673*4667Smh27603 		STRUCT_DECL(ppm_norm, norm);
674*4667Smh27603 		char *path = NULL;
675*4667Smh27603 		struct pm_component *dcomps;
676*4667Smh27603 		struct pm_comp *pm_comp;
677*4667Smh27603 		ppm_dev_t *ppmd;
678*4667Smh27603 		int i;
679*4667Smh27603 
680*4667Smh27603 		STRUCT_INIT(norm, mode);
681*4667Smh27603 		ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(norm),
682*4667Smh27603 		STRUCT_SIZE(norm), mode);
683*4667Smh27603 		if (ret != 0)
684*4667Smh27603 			return (EFAULT);
685*4667Smh27603 
686*4667Smh27603 		/* copyin .path */
687*4667Smh27603 		path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
688*4667Smh27603 		ret = copyinstr(
689*4667Smh27603 		    STRUCT_FGETP(norm, path), path, MAXPATHLEN, NULL);
690*4667Smh27603 		if (ret != 0) {
691*4667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n",
692*4667Smh27603 			    str, __LINE__))
693*4667Smh27603 			kmem_free(path, MAXPATHLEN);
694*4667Smh27603 			return (EFAULT);
695*4667Smh27603 		}
696*4667Smh27603 
697*4667Smh27603 		domp = ppm_get_domain_by_dev(path);
698*4667Smh27603 		kmem_free(path, MAXPATHLEN);
699*4667Smh27603 
700*4667Smh27603 		if (domp == NULL)
701*4667Smh27603 			return (ENODEV);
702*4667Smh27603 
703*4667Smh27603 		ppmd = domp->devlist;
704*4667Smh27603 		if (cmd == PPMSET_NORMAL) {
705*4667Smh27603 			if (domp->model != PPMD_CPU)
706*4667Smh27603 				return (EINVAL);
707*4667Smh27603 			level = STRUCT_FGET(norm, norm);
708*4667Smh27603 			dcomps = DEVI(ppmd->dip)->devi_pm_components;
709*4667Smh27603 			pm_comp = &dcomps[ppmd->cmpt].pmc_comp;
710*4667Smh27603 			for (i = pm_comp->pmc_numlevels; i > 0; i--) {
711*4667Smh27603 				if (pm_comp->pmc_lvals[i-1] == level)
712*4667Smh27603 					break;
713*4667Smh27603 			}
714*4667Smh27603 			if (i == 0)
715*4667Smh27603 				return (EINVAL);
716*4667Smh27603 
717*4667Smh27603 			ppm_set_topspeed(ppmd, pm_comp->pmc_numlevels - i);
718*4667Smh27603 		}
719*4667Smh27603 
720*4667Smh27603 		level = pm_get_normal_power(ppmd->dip, 0);
721*4667Smh27603 
722*4667Smh27603 		STRUCT_FSET(norm, norm, level);
723*4667Smh27603 		ret = ddi_copyout(STRUCT_BUF(norm), (caddr_t)arg,
724*4667Smh27603 		    STRUCT_SIZE(norm), mode);
725*4667Smh27603 		if (ret != 0) {
726*4667Smh27603 			PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n",
727*4667Smh27603 			    str, __LINE__))
728*4667Smh27603 			ret = EFAULT;
729*4667Smh27603 		}
730*4667Smh27603 		break;
731*4667Smh27603 	}
732*4667Smh27603 #endif
733*4667Smh27603 	default:
734*4667Smh27603 		PPMD(D_IOCTL, ("%s: unsupported ioctl command(%d)\n", str, cmd))
735*4667Smh27603 		return (EINVAL);
736*4667Smh27603 	}
737*4667Smh27603 
738*4667Smh27603 	return (ret);
739*4667Smh27603 }
740*4667Smh27603 
741*4667Smh27603 
742*4667Smh27603 /*
743*4667Smh27603  * interface between pm framework and ppm driver
744*4667Smh27603  */
745*4667Smh27603 /* ARGSUSED */
746*4667Smh27603 static int
747*4667Smh27603 ppm_ctlops(dev_info_t *dip, dev_info_t *rdip,
748*4667Smh27603     ddi_ctl_enum_t ctlop, void *arg, void *result)
749*4667Smh27603 {
750*4667Smh27603 	power_req_t	*reqp = (power_req_t *)arg;
751*4667Smh27603 	ppm_unit_t	*unitp;
752*4667Smh27603 	ppm_domain_t	*domp;
753*4667Smh27603 	ppm_dev_t	*ppmd;
754*4667Smh27603 	char		path[MAXNAMELEN];
755*4667Smh27603 	ppm_owned_t	*owned;
756*4667Smh27603 	int		mode;
757*4667Smh27603 	int		ret = DDI_SUCCESS;
758*4667Smh27603 
759*4667Smh27603 #ifdef DEBUG
760*4667Smh27603 	char	*str = "ppm_ctlops";
761*4667Smh27603 	int	mask = ppm_debug & (D_CTLOPS1 | D_CTLOPS2);
762*4667Smh27603 	char *ctlstr = ppm_get_ctlstr(reqp->request_type, mask);
763*4667Smh27603 	if (mask && ctlstr)
764*4667Smh27603 		PPMD(mask, ("%s: %s, %s\n",
765*4667Smh27603 		    str, ddi_binding_name(rdip), ctlstr))
766*4667Smh27603 #endif
767*4667Smh27603 
768*4667Smh27603 	if (ctlop != DDI_CTLOPS_POWER)
769*4667Smh27603 		return (DDI_FAILURE);
770*4667Smh27603 
771*4667Smh27603 	unitp = (ppm_unit_t *)ddi_get_soft_state(ppm_statep, ppm_inst);
772*4667Smh27603 
773*4667Smh27603 	switch (reqp->request_type) {
774*4667Smh27603 
775*4667Smh27603 	/* attempt to blink led if indeed all at lowest */
776*4667Smh27603 	case PMR_PPM_ALL_LOWEST:
777*4667Smh27603 		mode = (reqp->req.ppm_all_lowest_req.mode == PM_ALL_LOWEST);
778*4667Smh27603 		if (!(unitp->states & PPM_STATE_SUSPENDED) && mode)
779*4667Smh27603 			ppm_manage_led(PPM_LED_BLINKING);
780*4667Smh27603 		else
781*4667Smh27603 			ppm_manage_led(PPM_LED_SOLIDON);
782*4667Smh27603 		PPMD(D_LOWEST, ("%s: %sall devices are at lowest power \n",
783*4667Smh27603 		    str, mode ? "" : "not "))
784*4667Smh27603 		return (DDI_SUCCESS);
785*4667Smh27603 
786*4667Smh27603 	/* undo the claiming of 'rdip' at attach time */
787*4667Smh27603 	case PMR_PPM_POST_DETACH:
788*4667Smh27603 		ASSERT(reqp->req.ppm_set_power_req.who == rdip);
789*4667Smh27603 		mutex_enter(&unitp->lock);
790*4667Smh27603 		if (reqp->req.ppm_config_req.result != DDI_SUCCESS ||
791*4667Smh27603 		    (PPM_GET_PRIVATE(rdip) == NULL)) {
792*4667Smh27603 			mutex_exit(&unitp->lock);
793*4667Smh27603 			return (DDI_FAILURE);
794*4667Smh27603 		}
795*4667Smh27603 		mutex_exit(&unitp->lock);
796*4667Smh27603 		ppm_rem_dev(rdip);
797*4667Smh27603 		return (DDI_SUCCESS);
798*4667Smh27603 
799*4667Smh27603 	/* chance to adjust pwr_cnt if resume is about to power up rdip */
800*4667Smh27603 	case PMR_PPM_PRE_RESUME:
801*4667Smh27603 		ppm_svc_resume_ctlop(rdip, reqp);
802*4667Smh27603 		return (DDI_SUCCESS);
803*4667Smh27603 
804*4667Smh27603 	/*
805*4667Smh27603 	 * synchronizing, so that only the owner of the power lock is
806*4667Smh27603 	 * permitted to change device and component's power level.
807*4667Smh27603 	 */
808*4667Smh27603 	case PMR_PPM_UNLOCK_POWER:
809*4667Smh27603 	case PMR_PPM_TRY_LOCK_POWER:
810*4667Smh27603 	case PMR_PPM_LOCK_POWER:
811*4667Smh27603 		ppmd = PPM_GET_PRIVATE(rdip);
812*4667Smh27603 		if (ppmd)
813*4667Smh27603 			domp = ppmd->domp;
814*4667Smh27603 		else if (reqp->request_type != PMR_PPM_UNLOCK_POWER) {
815*4667Smh27603 			domp = ppm_lookup_dev(rdip);
816*4667Smh27603 			ASSERT(domp);
817*4667Smh27603 			ppmd = ppm_get_dev(rdip, domp);
818*4667Smh27603 		}
819*4667Smh27603 
820*4667Smh27603 		PPMD(D_LOCKS, ("ppm_lock_%s: %s, %s\n",
821*4667Smh27603 		    (domp->dflags & PPMD_LOCK_ALL) ? "all" : "one",
822*4667Smh27603 		    ppmd->path, ppm_get_ctlstr(reqp->request_type, D_LOCKS)))
823*4667Smh27603 
824*4667Smh27603 		if (domp->dflags & PPMD_LOCK_ALL)
825*4667Smh27603 			ppm_lock_all(domp, reqp, result);
826*4667Smh27603 		else
827*4667Smh27603 			ppm_lock_one(ppmd, reqp, result);
828*4667Smh27603 		return (DDI_SUCCESS);
829*4667Smh27603 
830*4667Smh27603 	case PMR_PPM_POWER_LOCK_OWNER:
831*4667Smh27603 		ASSERT(reqp->req.ppm_power_lock_owner_req.who == rdip);
832*4667Smh27603 		ppmd = PPM_GET_PRIVATE(rdip);
833*4667Smh27603 		if (ppmd)
834*4667Smh27603 			domp = ppmd->domp;
835*4667Smh27603 		else {
836*4667Smh27603 			domp = ppm_lookup_dev(rdip);
837*4667Smh27603 			ASSERT(domp);
838*4667Smh27603 			ppmd = ppm_get_dev(rdip, domp);
839*4667Smh27603 		}
840*4667Smh27603 
841*4667Smh27603 		/*
842*4667Smh27603 		 * In case of LOCK_ALL, effective owner of the power lock
843*4667Smh27603 		 * is the owner of the domain lock. otherwise, it is the owner
844*4667Smh27603 		 * of the power lock.
845*4667Smh27603 		 */
846*4667Smh27603 		if (domp->dflags & PPMD_LOCK_ALL)
847*4667Smh27603 			reqp->req.ppm_power_lock_owner_req.owner =
848*4667Smh27603 			    mutex_owner(&domp->lock);
849*4667Smh27603 		else {
850*4667Smh27603 			reqp->req.ppm_power_lock_owner_req.owner =
851*4667Smh27603 			    DEVI(rdip)->devi_busy_thread;
852*4667Smh27603 		}
853*4667Smh27603 		return (DDI_SUCCESS);
854*4667Smh27603 
855*4667Smh27603 	case PMR_PPM_INIT_CHILD:
856*4667Smh27603 		ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
857*4667Smh27603 		if ((domp = ppm_lookup_dev(rdip)) == NULL)
858*4667Smh27603 			return (DDI_SUCCESS);
859*4667Smh27603 
860*4667Smh27603 		/*
861*4667Smh27603 		 * We keep track of power-manageable devices starting with
862*4667Smh27603 		 * initialization process.  The initializing flag remains
863*4667Smh27603 		 * set until it is cleared by ppm_add_dev().  Power management
864*4667Smh27603 		 * policy for some domains are affected even during device
865*4667Smh27603 		 * initialization.  For example, PCI domains should leave
866*4667Smh27603 		 * their clock running meanwhile a device in that domain
867*4667Smh27603 		 * is initializing.
868*4667Smh27603 		 */
869*4667Smh27603 		mutex_enter(&domp->lock);
870*4667Smh27603 		owned = ppm_add_owned(rdip, domp);
871*4667Smh27603 		ASSERT(owned->initializing == 0);
872*4667Smh27603 		owned->initializing = 1;
873*4667Smh27603 
874*4667Smh27603 		if (PPMD_IS_PCI(domp->model) && domp->status == PPMD_OFF) {
875*4667Smh27603 			ret = ppm_switch_clock(domp, PPMD_ON);
876*4667Smh27603 			if (ret == DDI_SUCCESS)
877*4667Smh27603 				domp->dflags |= PPMD_INITCHILD_CLKON;
878*4667Smh27603 		}
879*4667Smh27603 		mutex_exit(&domp->lock);
880*4667Smh27603 		return (ret);
881*4667Smh27603 
882*4667Smh27603 	case PMR_PPM_POST_ATTACH:
883*4667Smh27603 		ASSERT(reqp->req.ppm_config_req.who == rdip);
884*4667Smh27603 		domp = ppm_lookup_dev(rdip);
885*4667Smh27603 		ASSERT(domp);
886*4667Smh27603 		ASSERT(domp->status == PPMD_ON);
887*4667Smh27603 		if (reqp->req.ppm_config_req.result == DDI_SUCCESS) {
888*4667Smh27603 			/*
889*4667Smh27603 			 * call ppm_get_dev, which will increment the
890*4667Smh27603 			 * domain power count by the right number.
891*4667Smh27603 			 * Undo the power count increment, done in PRE_PROBE.
892*4667Smh27603 			 */
893*4667Smh27603 			if (PM_GET_PM_INFO(rdip))
894*4667Smh27603 				ppmd = ppm_get_dev(rdip, domp);
895*4667Smh27603 			mutex_enter(&domp->lock);
896*4667Smh27603 			ASSERT(domp->pwr_cnt > 0);
897*4667Smh27603 			domp->pwr_cnt--;
898*4667Smh27603 			mutex_exit(&domp->lock);
899*4667Smh27603 			return (DDI_SUCCESS);
900*4667Smh27603 		}
901*4667Smh27603 
902*4667Smh27603 		ret = ppm_power_down_domain(rdip);
903*4667Smh27603 		/* FALLTHROUGH */
904*4667Smh27603 	case PMR_PPM_UNINIT_CHILD:
905*4667Smh27603 		ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
906*4667Smh27603 		if ((domp = ppm_lookup_dev(rdip)) == NULL)
907*4667Smh27603 			return (DDI_SUCCESS);
908*4667Smh27603 
909*4667Smh27603 		(void) ddi_pathname(rdip, path);
910*4667Smh27603 		mutex_enter(&domp->lock);
911*4667Smh27603 		for (owned = domp->owned; owned; owned = owned->next)
912*4667Smh27603 			if (strcmp(owned->path, path) == 0)
913*4667Smh27603 				break;
914*4667Smh27603 
915*4667Smh27603 		/*
916*4667Smh27603 		 * In case we didn't go through a complete attach and detach,
917*4667Smh27603 		 * the initializing flag will still be set, so clear it.
918*4667Smh27603 		 */
919*4667Smh27603 		if ((owned != NULL) && (owned->initializing))
920*4667Smh27603 			owned->initializing = 0;
921*4667Smh27603 
922*4667Smh27603 		if (PPMD_IS_PCI(domp->model) &&
923*4667Smh27603 		    domp->status == PPMD_ON && domp->pwr_cnt == 0 &&
924*4667Smh27603 		    (domp->dflags & PPMD_INITCHILD_CLKON) &&
925*4667Smh27603 		    ppm_none_else_holds_power(domp)) {
926*4667Smh27603 			ret = ppm_switch_clock(domp, PPMD_OFF);
927*4667Smh27603 			if (ret == DDI_SUCCESS)
928*4667Smh27603 				domp->dflags &= ~PPMD_INITCHILD_CLKON;
929*4667Smh27603 		}
930*4667Smh27603 		mutex_exit(&domp->lock);
931*4667Smh27603 		return (ret);
932*4667Smh27603 
933*4667Smh27603 	/* place holders */
934*4667Smh27603 	case PMR_PPM_UNMANAGE:
935*4667Smh27603 	case PMR_PPM_PRE_DETACH:
936*4667Smh27603 		return (DDI_SUCCESS);
937*4667Smh27603 
938*4667Smh27603 	case PMR_PPM_PRE_PROBE:
939*4667Smh27603 		ASSERT(reqp->req.ppm_config_req.who == rdip);
940*4667Smh27603 		return (ppm_power_up_domain(rdip));
941*4667Smh27603 
942*4667Smh27603 	case PMR_PPM_POST_PROBE:
943*4667Smh27603 		ASSERT(reqp->req.ppm_config_req.who == rdip);
944*4667Smh27603 		if (reqp->req.ppm_config_req.result == DDI_PROBE_SUCCESS ||
945*4667Smh27603 		    reqp->req.ppm_config_req.result == DDI_PROBE_DONTCARE)
946*4667Smh27603 			return (DDI_SUCCESS);
947*4667Smh27603 
948*4667Smh27603 		/* Probe failed */
949*4667Smh27603 		PPMD(D_CTLOPS1 | D_CTLOPS2, ("%s: probe failed for %s@%s "
950*4667Smh27603 		    "rv %d\n", str, PM_NAME(rdip), PM_ADDR(rdip),
951*4667Smh27603 		    reqp->req.ppm_config_req.result))
952*4667Smh27603 		return (ppm_power_down_domain(rdip));
953*4667Smh27603 
954*4667Smh27603 	case PMR_PPM_PRE_ATTACH:
955*4667Smh27603 		ASSERT(reqp->req.ppm_config_req.who == rdip);
956*4667Smh27603 		/* Domain has already been powered up in PRE_PROBE */
957*4667Smh27603 		domp = ppm_lookup_dev(rdip);
958*4667Smh27603 		ASSERT(domp);
959*4667Smh27603 		ASSERT(domp->status == PPMD_ON);
960*4667Smh27603 		return (DDI_SUCCESS);
961*4667Smh27603 
962*4667Smh27603 	/* ppm intercepts power change process to the claimed devices */
963*4667Smh27603 	case PMR_PPM_SET_POWER:
964*4667Smh27603 	case PMR_PPM_POWER_CHANGE_NOTIFY:
965*4667Smh27603 		if ((ppmd = PPM_GET_PRIVATE(rdip)) == NULL) {
966*4667Smh27603 			domp = ppm_lookup_dev(rdip);
967*4667Smh27603 			ASSERT(domp);
968*4667Smh27603 			ppmd = ppm_get_dev(rdip, domp);
969*4667Smh27603 		}
970*4667Smh27603 		switch (ppmd->domp->model) {
971*4667Smh27603 		case PPMD_CPU:
972*4667Smh27603 			return (ppm_manage_cpus(rdip, reqp, result));
973*4667Smh27603 		case PPMD_FET:
974*4667Smh27603 			return (ppm_manage_fet(rdip, reqp, result));
975*4667Smh27603 		case PPMD_PCI:
976*4667Smh27603 		case PPMD_PCI_PROP:
977*4667Smh27603 			return (ppm_manage_pci(rdip, reqp, result));
978*4667Smh27603 		case PPMD_PCIE:
979*4667Smh27603 			return (ppm_manage_pcie(rdip, reqp, result));
980*4667Smh27603 		default:
981*4667Smh27603 			cmn_err(CE_WARN, "ppm_ctlops: domain model %d does"
982*4667Smh27603 			    " not support PMR_PPM_SET_POWER ctlop",
983*4667Smh27603 			    ppmd->domp->model);
984*4667Smh27603 			return (DDI_FAILURE);
985*4667Smh27603 		}
986*4667Smh27603 
987*4667Smh27603 	default:
988*4667Smh27603 		cmn_err(CE_WARN, "ppm_ctlops: unrecognized ctlops req(%d)",
989*4667Smh27603 			reqp->request_type);
990*4667Smh27603 		return (DDI_FAILURE);
991*4667Smh27603 	}
992*4667Smh27603 }
993*4667Smh27603 
994*4667Smh27603 
995*4667Smh27603 /*
996*4667Smh27603  * Raise the power level of a subrange of cpus.  Used when cpu driver
997*4667Smh27603  * failed an attempt to lower the power of a cpu (probably because
998*4667Smh27603  * it got busy).  Need to revert the ones we already changed.
999*4667Smh27603  *
1000*4667Smh27603  * ecpup = the ppm_dev_t for the cpu which failed to lower power
1001*4667Smh27603  * level = power level to reset prior cpus to
1002*4667Smh27603  */
1003*4667Smh27603 int
1004*4667Smh27603 ppm_revert_cpu_power(ppm_dev_t *ecpup, int level)
1005*4667Smh27603 {
1006*4667Smh27603 	ppm_dev_t *cpup;
1007*4667Smh27603 	int ret = DDI_SUCCESS;
1008*4667Smh27603 
1009*4667Smh27603 	for (cpup = ecpup->domp->devlist; cpup != ecpup; cpup = cpup->next) {
1010*4667Smh27603 		PPMD(D_CPU, ("ppm_revert_cpu_power: \"%s\", revert to "
1011*4667Smh27603 		    "level %d\n", cpup->path, level))
1012*4667Smh27603 
1013*4667Smh27603 		ret = pm_power(cpup->dip, 0, level);
1014*4667Smh27603 		if (ret == DDI_SUCCESS) {
1015*4667Smh27603 			cpup->level = level;
1016*4667Smh27603 			cpup->rplvl = PM_LEVEL_UNKNOWN;
1017*4667Smh27603 		}
1018*4667Smh27603 	}
1019*4667Smh27603 	return (ret);
1020*4667Smh27603 }
1021*4667Smh27603 
1022*4667Smh27603 
1023*4667Smh27603 /*
1024*4667Smh27603  * ppm_manage_cpus - Process a request to change the power level of a cpu.
1025*4667Smh27603  * If not all cpus want to be at the same level, OR if we are currently
1026*4667Smh27603  * refusing slowdown requests due to thermal stress, we cache the request.
1027*4667Smh27603  * Otherwise, set all cpus to the new power level.
1028*4667Smh27603  */
1029*4667Smh27603 /* ARGSUSED */
1030*4667Smh27603 static int
1031*4667Smh27603 ppm_manage_cpus(dev_info_t *dip, power_req_t *reqp, int *result)
1032*4667Smh27603 {
1033*4667Smh27603 #ifdef	DEBUG
1034*4667Smh27603 	char *str = "ppm_manage_cpus";
1035*4667Smh27603 #endif
1036*4667Smh27603 	int old, new, ret, kmflag;
1037*4667Smh27603 	ppm_dev_t *ppmd, *cpup;
1038*4667Smh27603 	int change_notify = 0;
1039*4667Smh27603 	pm_ppm_devlist_t *devlist = NULL, *p;
1040*4667Smh27603 	int		do_rescan = 0;
1041*4667Smh27603 
1042*4667Smh27603 	*result = DDI_SUCCESS;
1043*4667Smh27603 
1044*4667Smh27603 	switch (reqp->request_type) {
1045*4667Smh27603 	case PMR_PPM_SET_POWER:
1046*4667Smh27603 		break;
1047*4667Smh27603 
1048*4667Smh27603 	case PMR_PPM_POWER_CHANGE_NOTIFY:
1049*4667Smh27603 		change_notify = 1;
1050*4667Smh27603 		break;
1051*4667Smh27603 
1052*4667Smh27603 	default:
1053*4667Smh27603 		return (DDI_FAILURE);
1054*4667Smh27603 	}
1055*4667Smh27603 
1056*4667Smh27603 	ppmd = PPM_GET_PRIVATE(dip);
1057*4667Smh27603 	ASSERT(MUTEX_HELD(&ppmd->domp->lock));
1058*4667Smh27603 	old = reqp->req.ppm_set_power_req.old_level;
1059*4667Smh27603 	new = reqp->req.ppm_set_power_req.new_level;
1060*4667Smh27603 
1061*4667Smh27603 	if (change_notify) {
1062*4667Smh27603 		ppmd->level = new;
1063*4667Smh27603 		ppmd->rplvl = PM_LEVEL_UNKNOWN;
1064*4667Smh27603 
1065*4667Smh27603 		PPMD(D_CPU, ("%s: Notify cpu dip %p power level has changed "
1066*4667Smh27603 		    "from %d to %d", str, (void *)dip, old, new))
1067*4667Smh27603 		return (DDI_SUCCESS);
1068*4667Smh27603 	}
1069*4667Smh27603 
1070*4667Smh27603 	if (ppm_manage_early_cpus(dip, new, result))
1071*4667Smh27603 		return (*result);
1072*4667Smh27603 
1073*4667Smh27603 	if (new == ppmd->level) {
1074*4667Smh27603 		PPMD(D_CPU, ("%s: already at power level %d\n", str, new))
1075*4667Smh27603 		return (DDI_SUCCESS);
1076*4667Smh27603 	}
1077*4667Smh27603 
1078*4667Smh27603 	/*
1079*4667Smh27603 	 * A request from lower to higher level transition is granted and
1080*4667Smh27603 	 * made effective on all cpus. A request from higher to lower must
1081*4667Smh27603 	 * be agreed upon by all cpus.
1082*4667Smh27603 	 */
1083*4667Smh27603 	ppmd->rplvl = new;
1084*4667Smh27603 	for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
1085*4667Smh27603 		if (cpup->rplvl == new)
1086*4667Smh27603 			continue;
1087*4667Smh27603 
1088*4667Smh27603 		if (new < old) {
1089*4667Smh27603 			PPMD(D_SOME, ("%s: not all cpus wants to be at new "
1090*4667Smh27603 			    "level %d yet.\n", str, new))
1091*4667Smh27603 			return (DDI_SUCCESS);
1092*4667Smh27603 		}
1093*4667Smh27603 
1094*4667Smh27603 		/*
1095*4667Smh27603 		 * If a single cpu requests power up, honor the request
1096*4667Smh27603 		 * powering up all cpus.
1097*4667Smh27603 		 */
1098*4667Smh27603 		if (new > old) {
1099*4667Smh27603 			PPMD(D_SOME, ("%s: powering up device(%s@%s, %p) "
1100*4667Smh27603 			    "because of request from dip(%s@%s, %p), "
1101*4667Smh27603 			    "need pm_rescan\n", str, PM_NAME(cpup->dip),
1102*4667Smh27603 			    PM_ADDR(cpup->dip), (void *)cpup->dip,
1103*4667Smh27603 			    PM_NAME(dip), PM_ADDR(dip), (void *)dip))
1104*4667Smh27603 			do_rescan++;
1105*4667Smh27603 		}
1106*4667Smh27603 	}
1107*4667Smh27603 
1108*4667Smh27603 	PPMD(D_SETLVL, ("%s: \"%s\" set power level old %d, new %d \n",
1109*4667Smh27603 	    str, ppmd->path, ppmd->level, new))
1110*4667Smh27603 	ret = ppm_change_cpu_power(ppmd, new);
1111*4667Smh27603 	*result = ret;
1112*4667Smh27603 
1113*4667Smh27603 	if (ret == DDI_SUCCESS) {
1114*4667Smh27603 		if (reqp->req.ppm_set_power_req.canblock == PM_CANBLOCK_BLOCK)
1115*4667Smh27603 			kmflag = KM_SLEEP;
1116*4667Smh27603 		else
1117*4667Smh27603 			kmflag = KM_NOSLEEP;
1118*4667Smh27603 
1119*4667Smh27603 		for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
1120*4667Smh27603 			if (cpup->dip == dip)
1121*4667Smh27603 				continue;
1122*4667Smh27603 
1123*4667Smh27603 			if ((p = kmem_zalloc(sizeof (pm_ppm_devlist_t),
1124*4667Smh27603 			    kmflag)) == NULL) {
1125*4667Smh27603 				break;
1126*4667Smh27603 			}
1127*4667Smh27603 			p->ppd_who = cpup->dip;
1128*4667Smh27603 			p->ppd_cmpt = cpup->cmpt;
1129*4667Smh27603 			p->ppd_old_level = old;
1130*4667Smh27603 			p->ppd_new_level = new;
1131*4667Smh27603 			p->ppd_next = devlist;
1132*4667Smh27603 
1133*4667Smh27603 			PPMD(D_SETLVL, ("%s: devlist entry[\"%s\"] %d -> %d\n",
1134*4667Smh27603 			    str, cpup->path, old, new))
1135*4667Smh27603 
1136*4667Smh27603 			devlist = p;
1137*4667Smh27603 		}
1138*4667Smh27603 		reqp->req.ppm_set_power_req.cookie = (void *) devlist;
1139*4667Smh27603 
1140*4667Smh27603 		if (do_rescan > 0) {
1141*4667Smh27603 			for (cpup = ppmd->domp->devlist; cpup;
1142*4667Smh27603 			    cpup = cpup->next) {
1143*4667Smh27603 				if (cpup->dip == dip)
1144*4667Smh27603 					continue;
1145*4667Smh27603 				pm_rescan(cpup->dip);
1146*4667Smh27603 			}
1147*4667Smh27603 		}
1148*4667Smh27603 	}
1149*4667Smh27603 
1150*4667Smh27603 	return (ret);
1151*4667Smh27603 }
1152*4667Smh27603 
1153*4667Smh27603 
1154*4667Smh27603 /*
1155*4667Smh27603  * ppm_svc_resume_ctlop - this is a small bookkeeping ppm does -
1156*4667Smh27603  * increments its FET domain power count, in anticipation of that
1157*4667Smh27603  * the indicated device(dip) would be powered up by its driver as
1158*4667Smh27603  * a result of cpr resuming.
1159*4667Smh27603  */
1160*4667Smh27603 /* ARGSUSED */
1161*4667Smh27603 static void
1162*4667Smh27603 ppm_svc_resume_ctlop(dev_info_t *dip, power_req_t *reqp)
1163*4667Smh27603 {
1164*4667Smh27603 	ppm_domain_t *domp;
1165*4667Smh27603 	ppm_dev_t *ppmd;
1166*4667Smh27603 	int powered;	/* power up count per dip */
1167*4667Smh27603 
1168*4667Smh27603 	ppmd = PPM_GET_PRIVATE(dip);
1169*4667Smh27603 	if (ppmd == NULL)
1170*4667Smh27603 		return;
1171*4667Smh27603 
1172*4667Smh27603 	/*
1173*4667Smh27603 	 * Maintain correct powered count for domain which cares
1174*4667Smh27603 	 */
1175*4667Smh27603 	powered = 0;
1176*4667Smh27603 	domp = ppmd->domp;
1177*4667Smh27603 	mutex_enter(&domp->lock);
1178*4667Smh27603 	if ((domp->model == PPMD_FET) || PPMD_IS_PCI(domp->model) ||
1179*4667Smh27603 	    (domp->model == PPMD_PCIE)) {
1180*4667Smh27603 		for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
1181*4667Smh27603 			if (ppmd->dip == dip && ppmd->level)
1182*4667Smh27603 				powered++;
1183*4667Smh27603 		}
1184*4667Smh27603 
1185*4667Smh27603 		/*
1186*4667Smh27603 		 * All fets and clocks are held on during suspend -
1187*4667Smh27603 		 * resume window regardless their domain devices' power
1188*4667Smh27603 		 * level.
1189*4667Smh27603 		 */
1190*4667Smh27603 		ASSERT(domp->status == PPMD_ON);
1191*4667Smh27603 
1192*4667Smh27603 		/*
1193*4667Smh27603 		 * The difference indicates the number of components
1194*4667Smh27603 		 * being off prior to suspend operation, that is the
1195*4667Smh27603 		 * amount needs to be compensated in order to sync up
1196*4667Smh27603 		 * bookkeeping with reality, for PROM reset would have
1197*4667Smh27603 		 * brought up all devices.
1198*4667Smh27603 		 */
1199*4667Smh27603 		if (powered < PM_NUMCMPTS(dip))
1200*4667Smh27603 			domp->pwr_cnt += PM_NUMCMPTS(dip) - powered;
1201*4667Smh27603 	}
1202*4667Smh27603 	for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
1203*4667Smh27603 		if (ppmd->dip == dip)
1204*4667Smh27603 			ppmd->level = ppmd->rplvl = PM_LEVEL_UNKNOWN;
1205*4667Smh27603 	}
1206*4667Smh27603 	mutex_exit(&domp->lock);
1207*4667Smh27603 }
1208*4667Smh27603 
1209*4667Smh27603 #ifdef	DEBUG
1210*4667Smh27603 static int ppmbringup = 0;
1211*4667Smh27603 #endif
1212*4667Smh27603 
1213*4667Smh27603 int
1214*4667Smh27603 ppm_bringup_domains()
1215*4667Smh27603 {
1216*4667Smh27603 #ifdef DEBUG
1217*4667Smh27603 	char *str = "ppm_bringup_domains";
1218*4667Smh27603 #endif
1219*4667Smh27603 	ppm_domain_t	*domp;
1220*4667Smh27603 	int	ret = DDI_SUCCESS;
1221*4667Smh27603 
1222*4667Smh27603 	PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmbringup))
1223*4667Smh27603 	for (domp = ppm_domain_p; domp; domp = domp->next) {
1224*4667Smh27603 		if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
1225*4667Smh27603 		    (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
1226*4667Smh27603 			continue;
1227*4667Smh27603 
1228*4667Smh27603 		mutex_enter(&domp->lock);
1229*4667Smh27603 		if (domp->status == PPMD_ON) {
1230*4667Smh27603 			mutex_exit(&domp->lock);
1231*4667Smh27603 			continue;
1232*4667Smh27603 		}
1233*4667Smh27603 		switch (domp->model) {
1234*4667Smh27603 		case PPMD_FET:
1235*4667Smh27603 			ret = ppm_fetset(domp, PPMD_ON);
1236*4667Smh27603 			break;
1237*4667Smh27603 		case PPMD_PCI:
1238*4667Smh27603 		case PPMD_PCI_PROP:
1239*4667Smh27603 			ret = ppm_switch_clock(domp, PPMD_ON);
1240*4667Smh27603 			break;
1241*4667Smh27603 		case PPMD_PCIE:
1242*4667Smh27603 			ret = ppm_pcie_pwr(domp, PPMD_ON);
1243*4667Smh27603 			break;
1244*4667Smh27603 		default:
1245*4667Smh27603 			break;
1246*4667Smh27603 		}
1247*4667Smh27603 		mutex_exit(&domp->lock);
1248*4667Smh27603 	}
1249*4667Smh27603 	PPMD(D_CPR, ("%s[%d]: exit, ret=%d\n", str, ppmbringup, ret))
1250*4667Smh27603 
1251*4667Smh27603 	return (ret);
1252*4667Smh27603 }
1253*4667Smh27603 
1254*4667Smh27603 #ifdef	DEBUG
1255*4667Smh27603 static int ppmsyncbp = 0;
1256*4667Smh27603 #endif
1257*4667Smh27603 
1258*4667Smh27603 int
1259*4667Smh27603 ppm_sync_bookkeeping()
1260*4667Smh27603 {
1261*4667Smh27603 #ifdef DEBUG
1262*4667Smh27603 	char *str = "ppm_sync_bookkeeping";
1263*4667Smh27603 #endif
1264*4667Smh27603 	ppm_domain_t	*domp;
1265*4667Smh27603 	int	ret = DDI_SUCCESS;
1266*4667Smh27603 
1267*4667Smh27603 	PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmsyncbp))
1268*4667Smh27603 	for (domp = ppm_domain_p; domp; domp = domp->next) {
1269*4667Smh27603 		if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
1270*4667Smh27603 		    (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
1271*4667Smh27603 			continue;
1272*4667Smh27603 
1273*4667Smh27603 		mutex_enter(&domp->lock);
1274*4667Smh27603 		if ((domp->pwr_cnt != 0) || !ppm_none_else_holds_power(domp)) {
1275*4667Smh27603 			mutex_exit(&domp->lock);
1276*4667Smh27603 			continue;
1277*4667Smh27603 		}
1278*4667Smh27603 		switch (domp->model) {
1279*4667Smh27603 		case PPMD_FET:
1280*4667Smh27603 			ret = ppm_fetset(domp, PPMD_OFF);
1281*4667Smh27603 			break;
1282*4667Smh27603 		case PPMD_PCI:
1283*4667Smh27603 		case PPMD_PCI_PROP:
1284*4667Smh27603 			ret = ppm_switch_clock(domp, PPMD_OFF);
1285*4667Smh27603 			break;
1286*4667Smh27603 		case PPMD_PCIE:
1287*4667Smh27603 			ret = ppm_pcie_pwr(domp, PPMD_OFF);
1288*4667Smh27603 			break;
1289*4667Smh27603 		default:
1290*4667Smh27603 			break;
1291*4667Smh27603 		}
1292*4667Smh27603 		mutex_exit(&domp->lock);
1293*4667Smh27603 	}
1294*4667Smh27603 	PPMD(D_CPR, ("%s[%d]: exit, ret=%d\n", str, ppmsyncbp, ret))
1295*4667Smh27603 
1296*4667Smh27603 	return (ret);
1297*4667Smh27603 }
1298*4667Smh27603 
1299*4667Smh27603 
1300*4667Smh27603 
1301*4667Smh27603 /*
1302*4667Smh27603  * pre-suspend window;
1303*4667Smh27603  *
1304*4667Smh27603  * power up every FET and PCI clock that are off;
1305*4667Smh27603  *
1306*4667Smh27603  * set ppm_cpr_window global flag to indicate
1307*4667Smh27603  * that even though all pm_scan requested power transitions
1308*4667Smh27603  * will be honored as usual but that until we're out
1309*4667Smh27603  * of this window,  no FET or clock will be turned off
1310*4667Smh27603  * for domains with pwr_cnt decremented down to 0.
1311*4667Smh27603  * Such is to avoid accessing the orthogonal drivers that own
1312*4667Smh27603  * the FET and clock registers that may not be resumed yet.
1313*4667Smh27603  *
1314*4667Smh27603  * at post-resume window, walk through each FET and PCI domains,
1315*4667Smh27603  * bring pwr_cnt and domp->status to sense: if pwr-cnt == 0,
1316*4667Smh27603  * and noinvol check okays, power down the FET or PCI.  At last,
1317*4667Smh27603  * clear the global flag ppm_cpr_window.
1318*4667Smh27603  *
1319*4667Smh27603  * ASSERT case 1, during cpr window, checks pwr_cnt against power
1320*4667Smh27603  *	transitions;
1321*4667Smh27603  * ASSERT case 2, out of cpr window, checks four things:
1322*4667Smh27603  *	pwr_cnt <> power transition in/out of 0
1323*4667Smh27603  *	<> status <> record of noinvol device detached
1324*4667Smh27603  *
1325*4667Smh27603  */
1326*4667Smh27603 /* ARGSUSED */
1327*4667Smh27603 static boolean_t
1328*4667Smh27603 ppm_cpr_callb(void *arg, int code)
1329*4667Smh27603 {
1330*4667Smh27603 	int	ret;
1331*4667Smh27603 
1332*4667Smh27603 	switch (code) {
1333*4667Smh27603 	case CB_CODE_CPR_CHKPT:
1334*4667Smh27603 
1335*4667Smh27603 		/* pre-suspend: start of cpr window */
1336*4667Smh27603 		mutex_enter(&ppm_cpr_window_lock);
1337*4667Smh27603 		ASSERT(ppm_cpr_window_flag == B_FALSE);
1338*4667Smh27603 		ppm_cpr_window_flag = B_TRUE;
1339*4667Smh27603 		mutex_exit(&ppm_cpr_window_lock);
1340*4667Smh27603 
1341*4667Smh27603 		ret = ppm_bringup_domains();
1342*4667Smh27603 
1343*4667Smh27603 		break;
1344*4667Smh27603 
1345*4667Smh27603 	case CB_CODE_CPR_RESUME:
1346*4667Smh27603 
1347*4667Smh27603 		/* post-resume: end of cpr window */
1348*4667Smh27603 		ret = ppm_sync_bookkeeping();
1349*4667Smh27603 
1350*4667Smh27603 		mutex_enter(&ppm_cpr_window_lock);
1351*4667Smh27603 		ASSERT(ppm_cpr_window_flag == B_TRUE);
1352*4667Smh27603 		ppm_cpr_window_flag = B_FALSE;
1353*4667Smh27603 		mutex_exit(&ppm_cpr_window_lock);
1354*4667Smh27603 
1355*4667Smh27603 		break;
1356*4667Smh27603 	}
1357*4667Smh27603 
1358*4667Smh27603 	return (ret == DDI_SUCCESS);
1359*4667Smh27603 }
1360*4667Smh27603 
1361*4667Smh27603 
1362*4667Smh27603 /*
1363*4667Smh27603  * Initialize our private version of real power level
1364*4667Smh27603  * as well as lowest and highest levels the device supports;
1365*4667Smh27603  * relate to ppm_add_dev
1366*4667Smh27603  */
1367*4667Smh27603 void
1368*4667Smh27603 ppm_dev_init(ppm_dev_t *ppmd)
1369*4667Smh27603 {
1370*4667Smh27603 	struct pm_component *dcomps;
1371*4667Smh27603 	struct pm_comp *pm_comp;
1372*4667Smh27603 	dev_info_t *dip;
1373*4667Smh27603 	int maxi, i;
1374*4667Smh27603 
1375*4667Smh27603 	ASSERT(MUTEX_HELD(&ppmd->domp->lock));
1376*4667Smh27603 	ppmd->level = PM_LEVEL_UNKNOWN;
1377*4667Smh27603 	ppmd->rplvl = PM_LEVEL_UNKNOWN;
1378*4667Smh27603 
1379*4667Smh27603 	/* increment pwr_cnt per component */
1380*4667Smh27603 	if ((ppmd->domp->model == PPMD_FET) ||
1381*4667Smh27603 	    PPMD_IS_PCI(ppmd->domp->model) ||
1382*4667Smh27603 	    (ppmd->domp->model == PPMD_PCIE))
1383*4667Smh27603 		ppmd->domp->pwr_cnt++;
1384*4667Smh27603 
1385*4667Smh27603 	dip = ppmd->dip;
1386*4667Smh27603 
1387*4667Smh27603 	/*
1388*4667Smh27603 	 * ppm exists to handle power-manageable devices which require
1389*4667Smh27603 	 * special handling on the current platform.  However, a
1390*4667Smh27603 	 * driver for such a device may choose not to support power
1391*4667Smh27603 	 * management on a particular load/attach.  In this case we
1392*4667Smh27603 	 * we create a structure to represent a single-component device
1393*4667Smh27603 	 * for which "level" = PM_LEVEL_UNKNOWN and "lowest" = 0
1394*4667Smh27603 	 * are effectively constant.
1395*4667Smh27603 	 */
1396*4667Smh27603 	if (PM_GET_PM_INFO(dip)) {
1397*4667Smh27603 		dcomps = DEVI(dip)->devi_pm_components;
1398*4667Smh27603 		pm_comp = &dcomps[ppmd->cmpt].pmc_comp;
1399*4667Smh27603 
1400*4667Smh27603 		ppmd->lowest = pm_comp->pmc_lvals[0];
1401*4667Smh27603 		ASSERT(ppmd->lowest >= 0);
1402*4667Smh27603 		maxi = pm_comp->pmc_numlevels - 1;
1403*4667Smh27603 		ppmd->highest = pm_comp->pmc_lvals[maxi];
1404*4667Smh27603 
1405*4667Smh27603 		/*
1406*4667Smh27603 		 * If 66mhz PCI device on pci 66mhz bus supports D2 state
1407*4667Smh27603 		 * (config reg PMC bit 10 set), ppm could turn off its bus
1408*4667Smh27603 		 * clock once it is at D3hot.
1409*4667Smh27603 		 */
1410*4667Smh27603 		if (ppmd->domp->dflags & PPMD_PCI66MHZ) {
1411*4667Smh27603 			for (i = 0; i < maxi; i++)
1412*4667Smh27603 				if (pm_comp->pmc_lvals[i] == PM_LEVEL_D2) {
1413*4667Smh27603 					ppmd->flags |= PPMDEV_PCI66_D2;
1414*4667Smh27603 					break;
1415*4667Smh27603 				}
1416*4667Smh27603 		}
1417*4667Smh27603 	}
1418*4667Smh27603 
1419*4667Smh27603 	/*
1420*4667Smh27603 	 * If device is in PCI_PROP domain and has exported the
1421*4667Smh27603 	 * property listed in ppm.conf, its clock will be turned
1422*4667Smh27603 	 * off when all pm'able devices in that domain are at D3.
1423*4667Smh27603 	 */
1424*4667Smh27603 	if ((ppmd->domp->model == PPMD_PCI_PROP) &&
1425*4667Smh27603 	    (ppmd->domp->propname != NULL) &&
1426*4667Smh27603 	    ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1427*4667Smh27603 	    ppmd->domp->propname))
1428*4667Smh27603 		ppmd->flags |= PPMDEV_PCI_PROP_CLKPM;
1429*4667Smh27603 }
1430*4667Smh27603 
1431*4667Smh27603 
1432*4667Smh27603 /*
1433*4667Smh27603  * relate to ppm_rem_dev
1434*4667Smh27603  */
1435*4667Smh27603 void
1436*4667Smh27603 ppm_dev_fini(ppm_dev_t *ppmd)
1437*4667Smh27603 {
1438*4667Smh27603 	ASSERT(MUTEX_HELD(&ppmd->domp->lock));
1439*4667Smh27603 
1440*4667Smh27603 	/* decrement pwr_cnt per component */
1441*4667Smh27603 	if ((ppmd->domp->model == PPMD_FET) ||
1442*4667Smh27603 	    PPMD_IS_PCI(ppmd->domp->model) ||
1443*4667Smh27603 	    (ppmd->domp->model == PPMD_PCIE))
1444*4667Smh27603 		if (ppmd->level != ppmd->lowest)
1445*4667Smh27603 			ppmd->domp->pwr_cnt--;
1446*4667Smh27603 }
1447*4667Smh27603 
1448*4667Smh27603 /*
1449*4667Smh27603  * Each power fet controls the power of one or more platform
1450*4667Smh27603  * device(s) within their domain.  Hence domain devices' power
1451*4667Smh27603  * level change has been monitored, such that once all devices
1452*4667Smh27603  * are powered off, the fet is turned off to save more power.
1453*4667Smh27603  *
1454*4667Smh27603  * To power on any domain device, the domain power fet
1455*4667Smh27603  * needs to be turned on first. always one fet per domain.
1456*4667Smh27603  */
1457*4667Smh27603 static int
1458*4667Smh27603 ppm_manage_fet(dev_info_t *dip, power_req_t *reqp, int *result)
1459*4667Smh27603 {
1460*4667Smh27603 #ifdef DEBUG
1461*4667Smh27603 	char *str = "ppm_manage_fet";
1462*4667Smh27603 #endif
1463*4667Smh27603 	int (*pwr_func)(ppm_dev_t *, int, int);
1464*4667Smh27603 	int		new, old, cmpt;
1465*4667Smh27603 	ppm_dev_t	*ppmd;
1466*4667Smh27603 	ppm_domain_t	*domp;
1467*4667Smh27603 	int		incr = 0;
1468*4667Smh27603 	int		dummy_ret;
1469*4667Smh27603 
1470*4667Smh27603 
1471*4667Smh27603 	*result = DDI_SUCCESS;
1472*4667Smh27603 	switch (reqp->request_type) {
1473*4667Smh27603 	case PMR_PPM_SET_POWER:
1474*4667Smh27603 		pwr_func = ppm_change_power_level;
1475*4667Smh27603 		old = reqp->req.ppm_set_power_req.old_level;
1476*4667Smh27603 		new = reqp->req.ppm_set_power_req.new_level;
1477*4667Smh27603 		cmpt = reqp->req.ppm_set_power_req.cmpt;
1478*4667Smh27603 		break;
1479*4667Smh27603 	case PMR_PPM_POWER_CHANGE_NOTIFY:
1480*4667Smh27603 		pwr_func = ppm_record_level_change;
1481*4667Smh27603 		old = reqp->req.ppm_notify_level_req.old_level;
1482*4667Smh27603 		new = reqp->req.ppm_notify_level_req.new_level;
1483*4667Smh27603 		cmpt = reqp->req.ppm_notify_level_req.cmpt;
1484*4667Smh27603 		break;
1485*4667Smh27603 	default:
1486*4667Smh27603 		*result = DDI_FAILURE;
1487*4667Smh27603 		PPMD(D_FET, ("%s: unknown request type %d for %s@%s\n",
1488*4667Smh27603 		    str, reqp->request_type, PM_NAME(dip), PM_ADDR(dip)))
1489*4667Smh27603 		return (DDI_FAILURE);
1490*4667Smh27603 	}
1491*4667Smh27603 
1492*4667Smh27603 	for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
1493*4667Smh27603 		if (cmpt == ppmd->cmpt)
1494*4667Smh27603 			break;
1495*4667Smh27603 	if (!ppmd) {
1496*4667Smh27603 		PPMD(D_FET, ("%s: dip(%p): old(%d)->new(%d): no ppm_dev"
1497*4667Smh27603 		    " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
1498*4667Smh27603 		*result = DDI_FAILURE;
1499*4667Smh27603 		return (DDI_FAILURE);
1500*4667Smh27603 	}
1501*4667Smh27603 	domp = ppmd->domp;
1502*4667Smh27603 	PPMD(D_FET, ("%s: %s@%s %s old %d, new %d, c%d, level %d, "
1503*4667Smh27603 	    "status %s\n", str, PM_NAME(dip), PM_ADDR(dip),
1504*4667Smh27603 	    ppm_get_ctlstr(reqp->request_type, ~0), old, new, cmpt,
1505*4667Smh27603 	    ppmd->level, (domp->status == PPMD_OFF ? "off" : "on")))
1506*4667Smh27603 
1507*4667Smh27603 
1508*4667Smh27603 	ASSERT(old == ppmd->level);
1509*4667Smh27603 
1510*4667Smh27603 	if (new == ppmd->level) {
1511*4667Smh27603 		PPMD(D_FET, ("nop\n"))
1512*4667Smh27603 		return (DDI_SUCCESS);
1513*4667Smh27603 	}
1514*4667Smh27603 
1515*4667Smh27603 	PPM_LOCK_DOMAIN(domp);
1516*4667Smh27603 
1517*4667Smh27603 	/*
1518*4667Smh27603 	 * In general, a device's published lowest power level does not
1519*4667Smh27603 	 * have to be 0 if power-off is not tolerated. i.e. a device
1520*4667Smh27603 	 * instance may export its lowest level > 0.  It is reasonable to
1521*4667Smh27603 	 * assume that level 0 indicates off state, positive level values
1522*4667Smh27603 	 * indicate power states above off, include full power state.
1523*4667Smh27603 	 */
1524*4667Smh27603 	if (new > 0) { /* device powering up or to different positive level */
1525*4667Smh27603 		if (domp->status == PPMD_OFF) {
1526*4667Smh27603 
1527*4667Smh27603 			/* can not be in (chpt, resume) window */
1528*4667Smh27603 			ASSERT(ppm_cpr_window_flag == B_FALSE);
1529*4667Smh27603 
1530*4667Smh27603 			ASSERT(old == 0 && domp->pwr_cnt == 0);
1531*4667Smh27603 
1532*4667Smh27603 			PPMD(D_FET, ("About to turn fet on for %s@%s c%d\n",
1533*4667Smh27603 			    PM_NAME(dip), PM_ADDR(dip), cmpt))
1534*4667Smh27603 
1535*4667Smh27603 			*result = ppm_fetset(domp, PPMD_ON);
1536*4667Smh27603 			if (*result != DDI_SUCCESS) {
1537*4667Smh27603 				PPMD(D_FET, ("\tCan't turn on power FET: "
1538*4667Smh27603 				    "ret(%d)\n", *result))
1539*4667Smh27603 				PPM_UNLOCK_DOMAIN(domp);
1540*4667Smh27603 				return (DDI_FAILURE);
1541*4667Smh27603 			}
1542*4667Smh27603 		}
1543*4667Smh27603 
1544*4667Smh27603 		/*
1545*4667Smh27603 		 * If powering up, pre-increment the count before
1546*4667Smh27603 		 * calling pwr_func, because we are going to release
1547*4667Smh27603 		 * the domain lock and another thread might turn off
1548*4667Smh27603 		 * domain power otherwise.
1549*4667Smh27603 		 */
1550*4667Smh27603 		if (old == 0) {
1551*4667Smh27603 			domp->pwr_cnt++;
1552*4667Smh27603 			incr = 1;
1553*4667Smh27603 		}
1554*4667Smh27603 
1555*4667Smh27603 		PPMD(D_FET, ("\t%s domain power count: %d\n",
1556*4667Smh27603 		    domp->name, domp->pwr_cnt))
1557*4667Smh27603 	}
1558*4667Smh27603 
1559*4667Smh27603 
1560*4667Smh27603 	PPM_UNLOCK_DOMAIN(domp);
1561*4667Smh27603 
1562*4667Smh27603 	ASSERT(domp->pwr_cnt > 0);
1563*4667Smh27603 
1564*4667Smh27603 	if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
1565*4667Smh27603 		PPMD(D_FET, ("\t%s power change failed: ret(%d)\n",
1566*4667Smh27603 		    ppmd->path, *result))
1567*4667Smh27603 	}
1568*4667Smh27603 
1569*4667Smh27603 	PPM_LOCK_DOMAIN(domp);
1570*4667Smh27603 
1571*4667Smh27603 	/*
1572*4667Smh27603 	 * Decr the power count in two cases:
1573*4667Smh27603 	 *
1574*4667Smh27603 	 *   1) request was to power device down and was successful
1575*4667Smh27603 	 *   2) request was to power up (we pre-incremented count), but failed.
1576*4667Smh27603 	 */
1577*4667Smh27603 	if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
1578*4667Smh27603 	    (*result != DDI_SUCCESS && incr)) {
1579*4667Smh27603 		ASSERT(domp->pwr_cnt > 0);
1580*4667Smh27603 		domp->pwr_cnt--;
1581*4667Smh27603 	}
1582*4667Smh27603 
1583*4667Smh27603 	PPMD(D_FET, ("\t%s domain power count: %d\n",
1584*4667Smh27603 	    domp->name, domp->pwr_cnt))
1585*4667Smh27603 
1586*4667Smh27603 	/*
1587*4667Smh27603 	 * call to pwr_func will update ppm data structures, if it
1588*4667Smh27603 	 * succeeds. ppm should return whatever is the return value
1589*4667Smh27603 	 * from call to pwr_func. This way pm and ppm data structures
1590*4667Smh27603 	 * always in sync. Use dummy_ret from here for any further
1591*4667Smh27603 	 * return values.
1592*4667Smh27603 	 */
1593*4667Smh27603 	if ((domp->pwr_cnt == 0) &&
1594*4667Smh27603 	    (ppm_cpr_window_flag == B_FALSE) &&
1595*4667Smh27603 	    ppm_none_else_holds_power(domp)) {
1596*4667Smh27603 
1597*4667Smh27603 		PPMD(D_FET, ("About to turn FET off for %s@%s c%d\n",
1598*4667Smh27603 		    PM_NAME(dip), PM_ADDR(dip), cmpt))
1599*4667Smh27603 
1600*4667Smh27603 		dummy_ret = ppm_fetset(domp, PPMD_OFF);
1601*4667Smh27603 		if (dummy_ret != DDI_SUCCESS) {
1602*4667Smh27603 			PPMD(D_FET, ("\tCan't turn off FET: ret(%d)\n",
1603*4667Smh27603 			    dummy_ret))
1604*4667Smh27603 		}
1605*4667Smh27603 	}
1606*4667Smh27603 
1607*4667Smh27603 	PPM_UNLOCK_DOMAIN(domp);
1608*4667Smh27603 	ASSERT(domp->pwr_cnt >= 0);
1609*4667Smh27603 	return (*result);
1610*4667Smh27603 }
1611*4667Smh27603 
1612*4667Smh27603 
1613*4667Smh27603 /*
1614*4667Smh27603  * the actual code that turn on or off domain power fet and
1615*4667Smh27603  * update domain status
1616*4667Smh27603  */
1617*4667Smh27603 static int
1618*4667Smh27603 ppm_fetset(ppm_domain_t *domp, uint8_t value)
1619*4667Smh27603 {
1620*4667Smh27603 	char	*str = "ppm_fetset";
1621*4667Smh27603 	int	key;
1622*4667Smh27603 	ppm_dc_t *dc;
1623*4667Smh27603 	int	ret;
1624*4667Smh27603 	clock_t	temp;
1625*4667Smh27603 	clock_t delay = 0;
1626*4667Smh27603 
1627*4667Smh27603 	key = (value == PPMD_ON) ? PPMDC_FET_ON : PPMDC_FET_OFF;
1628*4667Smh27603 	for (dc = domp->dc; dc; dc = dc->next)
1629*4667Smh27603 		if (dc->cmd == key)
1630*4667Smh27603 			break;
1631*4667Smh27603 	if (!dc || !dc->lh) {
1632*4667Smh27603 		PPMD(D_FET, ("%s: %s domain: NULL ppm_dc handle\n",
1633*4667Smh27603 		    str, domp->name))
1634*4667Smh27603 		return (DDI_FAILURE);
1635*4667Smh27603 	}
1636*4667Smh27603 
1637*4667Smh27603 	if (key == PPMDC_FET_ON) {
1638*4667Smh27603 		PPM_GET_IO_DELAY(dc, delay);
1639*4667Smh27603 		if (delay > 0 && domp->last_off_time > 0) {
1640*4667Smh27603 			/*
1641*4667Smh27603 			 * provide any delay required before turning on.
1642*4667Smh27603 			 * some devices e.g. Samsung DVD require minimum
1643*4667Smh27603 			 * of 1 sec between OFF->ON. no delay is required
1644*4667Smh27603 			 * for the first time.
1645*4667Smh27603 			 */
1646*4667Smh27603 			temp = ddi_get_lbolt();
1647*4667Smh27603 			temp -= domp->last_off_time;
1648*4667Smh27603 			temp = drv_hztousec(temp);
1649*4667Smh27603 
1650*4667Smh27603 			if (temp < delay) {
1651*4667Smh27603 				/*
1652*4667Smh27603 				 * busy wait untill we meet the
1653*4667Smh27603 				 * required delay. Since we maintain
1654*4667Smh27603 				 * time stamps in terms of clock ticks
1655*4667Smh27603 				 * we might wait for longer than required
1656*4667Smh27603 				 */
1657*4667Smh27603 				PPMD(D_FET, ("%s : waiting %lu micro seconds "
1658*4667Smh27603 					"before on\n", domp->name,
1659*4667Smh27603 					delay - temp))
1660*4667Smh27603 				drv_usecwait(delay - temp);
1661*4667Smh27603 			}
1662*4667Smh27603 		}
1663*4667Smh27603 	}
1664*4667Smh27603 	switch (dc->method) {
1665*4667Smh27603 #if !defined(__x86)
1666*4667Smh27603 	case PPMDC_I2CKIO: {
1667*4667Smh27603 		i2c_gpio_t i2c_req;
1668*4667Smh27603 		i2c_req.reg_mask = dc->m_un.i2c.mask;
1669*4667Smh27603 		i2c_req.reg_val = dc->m_un.i2c.val;
1670*4667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
1671*4667Smh27603 		    (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
1672*4667Smh27603 		break;
1673*4667Smh27603 	}
1674*4667Smh27603 #endif
1675*4667Smh27603 
1676*4667Smh27603 	case PPMDC_KIO:
1677*4667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
1678*4667Smh27603 		    (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
1679*4667Smh27603 		    NULL);
1680*4667Smh27603 		break;
1681*4667Smh27603 
1682*4667Smh27603 	default:
1683*4667Smh27603 		PPMD(D_FET, ("\t%s: unsupported domain control method %d\n",
1684*4667Smh27603 		    str, domp->dc->method))
1685*4667Smh27603 		return (DDI_FAILURE);
1686*4667Smh27603 	}
1687*4667Smh27603 
1688*4667Smh27603 	PPMD(D_FET, ("%s: %s domain(%s) FET from %s to %s\n", str,
1689*4667Smh27603 	    (ret == 0) ? "turned" : "failed to turn",
1690*4667Smh27603 	    domp->name,
1691*4667Smh27603 	    (domp->status == PPMD_ON) ? "ON" : "OFF",
1692*4667Smh27603 	    (value == PPMD_ON) ? "ON" : "OFF"))
1693*4667Smh27603 
1694*4667Smh27603 	if (ret == DDI_SUCCESS) {
1695*4667Smh27603 		domp->status = value;
1696*4667Smh27603 
1697*4667Smh27603 		if (key == PPMDC_FET_OFF)
1698*4667Smh27603 			/*
1699*4667Smh27603 			 * record the time, when it is off. time is recorded
1700*4667Smh27603 			 * in clock ticks
1701*4667Smh27603 			 */
1702*4667Smh27603 			domp->last_off_time = ddi_get_lbolt();
1703*4667Smh27603 
1704*4667Smh27603 		/* implement any post op delay. */
1705*4667Smh27603 		if (key == PPMDC_FET_ON) {
1706*4667Smh27603 			PPM_GET_IO_DELAY(dc, delay);
1707*4667Smh27603 			PPMD(D_FET, ("%s : waiting %lu micro seconds "
1708*4667Smh27603 			    "after on\n", domp->name, delay))
1709*4667Smh27603 			if (delay > 0)
1710*4667Smh27603 				drv_usecwait(delay);
1711*4667Smh27603 		}
1712*4667Smh27603 	}
1713*4667Smh27603 
1714*4667Smh27603 	return (ret);
1715*4667Smh27603 }
1716*4667Smh27603 
1717*4667Smh27603 
1718*4667Smh27603 /*
1719*4667Smh27603  * read power fet status
1720*4667Smh27603  */
1721*4667Smh27603 static int
1722*4667Smh27603 ppm_fetget(ppm_domain_t *domp, uint8_t *lvl)
1723*4667Smh27603 {
1724*4667Smh27603 	char	*str = "ppm_fetget";
1725*4667Smh27603 	ppm_dc_t *dc = domp->dc;
1726*4667Smh27603 	uint_t	kio_val;
1727*4667Smh27603 	int	off_val;
1728*4667Smh27603 	int	ret;
1729*4667Smh27603 
1730*4667Smh27603 	if (!dc->lh) {
1731*4667Smh27603 		PPMD(D_FET, ("%s: %s domain NULL ppm_dc layered handle\n",
1732*4667Smh27603 		    str, domp->name))
1733*4667Smh27603 		return (DDI_FAILURE);
1734*4667Smh27603 	}
1735*4667Smh27603 	if (!dc->next) {
1736*4667Smh27603 		cmn_err(CE_WARN, "%s: expect both fet on and fet off ops "
1737*4667Smh27603 		    "defined, found only one in domain(%s)", str, domp->name);
1738*4667Smh27603 		return (DDI_FAILURE);
1739*4667Smh27603 	}
1740*4667Smh27603 
1741*4667Smh27603 	switch (dc->method) {
1742*4667Smh27603 #if !defined(__x86)
1743*4667Smh27603 	case PPMDC_I2CKIO: {
1744*4667Smh27603 		i2c_gpio_t i2c_req;
1745*4667Smh27603 		i2c_req.reg_mask = dc->m_un.i2c.mask;
1746*4667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iord,
1747*4667Smh27603 		    (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
1748*4667Smh27603 
1749*4667Smh27603 		if (ret) {
1750*4667Smh27603 			PPMD(D_FET, ("%s: PPMDC_I2CKIO failed: ret(%d)\n",
1751*4667Smh27603 			    str, ret))
1752*4667Smh27603 			return (ret);
1753*4667Smh27603 		}
1754*4667Smh27603 
1755*4667Smh27603 		off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.i2c.val :
1756*4667Smh27603 		    dc->next->m_un.i2c.val;
1757*4667Smh27603 		*lvl = (i2c_req.reg_val == off_val) ? PPMD_OFF : PPMD_ON;
1758*4667Smh27603 
1759*4667Smh27603 		PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
1760*4667Smh27603 		    (i2c_req.reg_val == off_val) ? "OFF" : "ON"))
1761*4667Smh27603 
1762*4667Smh27603 		break;
1763*4667Smh27603 	}
1764*4667Smh27603 #endif
1765*4667Smh27603 
1766*4667Smh27603 	case PPMDC_KIO:
1767*4667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iord,
1768*4667Smh27603 		    (intptr_t)&kio_val, FWRITE | FKIOCTL, kcred, NULL);
1769*4667Smh27603 		if (ret) {
1770*4667Smh27603 			PPMD(D_FET, ("%s: PPMDC_KIO failed: ret(%d)\n",
1771*4667Smh27603 			    str, ret))
1772*4667Smh27603 			return (ret);
1773*4667Smh27603 		}
1774*4667Smh27603 
1775*4667Smh27603 		off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.kio.val :
1776*4667Smh27603 			dc->next->m_un.kio.val;
1777*4667Smh27603 		*lvl = (kio_val == off_val) ? PPMD_OFF : PPMD_ON;
1778*4667Smh27603 
1779*4667Smh27603 		PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
1780*4667Smh27603 		    (kio_val == off_val) ? "OFF" : "ON"))
1781*4667Smh27603 
1782*4667Smh27603 		break;
1783*4667Smh27603 
1784*4667Smh27603 	default:
1785*4667Smh27603 		PPMD(D_FET, ("%s: unsupported domain control method %d\n",
1786*4667Smh27603 		    str, domp->dc->method))
1787*4667Smh27603 		return (DDI_FAILURE);
1788*4667Smh27603 	}
1789*4667Smh27603 
1790*4667Smh27603 	return (DDI_SUCCESS);
1791*4667Smh27603 }
1792*4667Smh27603 
1793*4667Smh27603 
1794*4667Smh27603 /*
1795*4667Smh27603  * the actual code that switches pci clock and update domain status
1796*4667Smh27603  */
1797*4667Smh27603 static int
1798*4667Smh27603 ppm_switch_clock(ppm_domain_t *domp, int onoff)
1799*4667Smh27603 {
1800*4667Smh27603 #ifdef DEBUG
1801*4667Smh27603 	char *str = "ppm_switch_clock";
1802*4667Smh27603 #endif
1803*4667Smh27603 	int	cmd, pio_save;
1804*4667Smh27603 	ppm_dc_t *dc;
1805*4667Smh27603 	int ret;
1806*4667Smh27603 	extern int do_polled_io;
1807*4667Smh27603 	extern uint_t cfb_inuse;
1808*4667Smh27603 	ppm_dev_t	*pdev;
1809*4667Smh27603 
1810*4667Smh27603 	cmd = (onoff == PPMD_ON) ? PPMDC_CLK_ON : PPMDC_CLK_OFF;
1811*4667Smh27603 	dc = ppm_lookup_dc(domp, cmd);
1812*4667Smh27603 	if (!dc) {
1813*4667Smh27603 		PPMD(D_PCI, ("%s: no ppm_dc found for domain (%s)\n",
1814*4667Smh27603 		    str, domp->name))
1815*4667Smh27603 		return (DDI_FAILURE);
1816*4667Smh27603 	}
1817*4667Smh27603 
1818*4667Smh27603 	switch (dc->method) {
1819*4667Smh27603 	case PPMDC_KIO:
1820*4667Smh27603 		/*
1821*4667Smh27603 		 * If we're powering up cfb on a Stop-A, we only
1822*4667Smh27603 		 * want to do polled i/o to turn ON the clock
1823*4667Smh27603 		 */
1824*4667Smh27603 		pio_save = do_polled_io;
1825*4667Smh27603 		if ((cfb_inuse) && (cmd == PPMDC_CLK_ON)) {
1826*4667Smh27603 			for (pdev = domp->devlist; pdev; pdev = pdev->next) {
1827*4667Smh27603 				if (pm_is_cfb(pdev->dip)) {
1828*4667Smh27603 					do_polled_io = 1;
1829*4667Smh27603 					break;
1830*4667Smh27603 				}
1831*4667Smh27603 			}
1832*4667Smh27603 		}
1833*4667Smh27603 
1834*4667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
1835*4667Smh27603 		    (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL,
1836*4667Smh27603 		    kcred, NULL);
1837*4667Smh27603 
1838*4667Smh27603 		do_polled_io = pio_save;
1839*4667Smh27603 
1840*4667Smh27603 		if (ret == 0) {
1841*4667Smh27603 			if (cmd == PPMDC_CLK_ON) {
1842*4667Smh27603 				domp->status = PPMD_ON;
1843*4667Smh27603 
1844*4667Smh27603 				/*
1845*4667Smh27603 				 * PCI PM spec requires 50ms delay
1846*4667Smh27603 				 */
1847*4667Smh27603 				drv_usecwait(50000);
1848*4667Smh27603 			} else
1849*4667Smh27603 				domp->status = PPMD_OFF;
1850*4667Smh27603 		}
1851*4667Smh27603 
1852*4667Smh27603 		PPMD(D_PCI, ("%s: %s pci clock %s for domain (%s)\n", str,
1853*4667Smh27603 		    (ret == 0) ? "turned" : "failed to turn",
1854*4667Smh27603 		    (cmd == PPMDC_CLK_OFF) ? "OFF" : "ON",
1855*4667Smh27603 		    domp->name))
1856*4667Smh27603 
1857*4667Smh27603 		break;
1858*4667Smh27603 
1859*4667Smh27603 	default:
1860*4667Smh27603 		PPMD(D_PCI, ("%s: unsupported domain control method %d\n",
1861*4667Smh27603 		    str, dc->method))
1862*4667Smh27603 		return (DDI_FAILURE);
1863*4667Smh27603 	}
1864*4667Smh27603 
1865*4667Smh27603 	return (DDI_SUCCESS);
1866*4667Smh27603 }
1867*4667Smh27603 
1868*4667Smh27603 
1869*4667Smh27603 /*
1870*4667Smh27603  * pci slot domain is formed of pci device(s) reside in a pci slot.
1871*4667Smh27603  * This function monitors domain device's power level change, such
1872*4667Smh27603  * that,
1873*4667Smh27603  *   when all domain power count has gone to 0, it attempts to turn off
1874*4667Smh27603  *        the pci slot's clock;
1875*4667Smh27603  *   if any domain device is powering up, it'll turn on the pci slot's
1876*4667Smh27603  *        clock as the first thing.
1877*4667Smh27603  */
1878*4667Smh27603 /* ARGUSED */
1879*4667Smh27603 static int
1880*4667Smh27603 ppm_manage_pci(dev_info_t *dip, power_req_t *reqp, int *result)
1881*4667Smh27603 {
1882*4667Smh27603 #ifdef DEBUG
1883*4667Smh27603 	char *str = "ppm_manage_pci";
1884*4667Smh27603 #endif
1885*4667Smh27603 	int (*pwr_func)(ppm_dev_t *, int, int);
1886*4667Smh27603 	int old, new, cmpt;
1887*4667Smh27603 	ppm_dev_t *ppmd;
1888*4667Smh27603 	ppm_domain_t *domp;
1889*4667Smh27603 	int incr = 0;
1890*4667Smh27603 	int dummy_ret;
1891*4667Smh27603 
1892*4667Smh27603 	*result = DDI_SUCCESS;
1893*4667Smh27603 	switch (reqp->request_type) {
1894*4667Smh27603 	case PMR_PPM_SET_POWER:
1895*4667Smh27603 		pwr_func = ppm_change_power_level;
1896*4667Smh27603 		old = reqp->req.ppm_set_power_req.old_level;
1897*4667Smh27603 		new = reqp->req.ppm_set_power_req.new_level;
1898*4667Smh27603 		cmpt = reqp->req.ppm_set_power_req.cmpt;
1899*4667Smh27603 		break;
1900*4667Smh27603 
1901*4667Smh27603 	case PMR_PPM_POWER_CHANGE_NOTIFY:
1902*4667Smh27603 		pwr_func = ppm_record_level_change;
1903*4667Smh27603 		old = reqp->req.ppm_notify_level_req.old_level;
1904*4667Smh27603 		new = reqp->req.ppm_notify_level_req.new_level;
1905*4667Smh27603 		cmpt = reqp->req.ppm_notify_level_req.cmpt;
1906*4667Smh27603 		break;
1907*4667Smh27603 
1908*4667Smh27603 	default:
1909*4667Smh27603 		*result = DDI_FAILURE;
1910*4667Smh27603 		return (DDI_FAILURE);
1911*4667Smh27603 	}
1912*4667Smh27603 
1913*4667Smh27603 	for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
1914*4667Smh27603 		if (cmpt == ppmd->cmpt)
1915*4667Smh27603 			break;
1916*4667Smh27603 	if (!ppmd) {
1917*4667Smh27603 		PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
1918*4667Smh27603 		    " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
1919*4667Smh27603 		*result = DDI_FAILURE;
1920*4667Smh27603 		return (DDI_FAILURE);
1921*4667Smh27603 	}
1922*4667Smh27603 	domp = ppmd->domp;
1923*4667Smh27603 	PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
1924*4667Smh27603 	    ppm_get_ctlstr(reqp->request_type, ~0),
1925*4667Smh27603 	    ppmd->path, cmpt, old, new))
1926*4667Smh27603 
1927*4667Smh27603 	ASSERT(old == ppmd->level);
1928*4667Smh27603 	if (new == ppmd->level)
1929*4667Smh27603 		return (DDI_SUCCESS);
1930*4667Smh27603 
1931*4667Smh27603 	PPM_LOCK_DOMAIN(domp);
1932*4667Smh27603 
1933*4667Smh27603 	if (new > 0) {		/* device powering up */
1934*4667Smh27603 		if (domp->status == PPMD_OFF) {
1935*4667Smh27603 
1936*4667Smh27603 			/* cannot be off during (chpt, resume) window */
1937*4667Smh27603 			ASSERT(ppm_cpr_window_flag == B_FALSE);
1938*4667Smh27603 
1939*4667Smh27603 			/* either both OFF or both ON */
1940*4667Smh27603 			ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
1941*4667Smh27603 
1942*4667Smh27603 			PPMD(D_PCI, ("About to turn clock on for %s@%s c%d\n",
1943*4667Smh27603 			    PM_NAME(dip), PM_ADDR(dip), cmpt))
1944*4667Smh27603 
1945*4667Smh27603 			*result = ppm_switch_clock(domp, PPMD_ON);
1946*4667Smh27603 			if (*result != DDI_SUCCESS) {
1947*4667Smh27603 				PPMD(D_PCI, ("\tcan't switch on pci clock: "
1948*4667Smh27603 				    "ret(%d)\n", *result))
1949*4667Smh27603 				PPM_UNLOCK_DOMAIN(domp);
1950*4667Smh27603 				return (DDI_FAILURE);
1951*4667Smh27603 			}
1952*4667Smh27603 		}
1953*4667Smh27603 
1954*4667Smh27603 		if (old == 0) {
1955*4667Smh27603 			domp->pwr_cnt++;
1956*4667Smh27603 			incr = 1;
1957*4667Smh27603 		}
1958*4667Smh27603 
1959*4667Smh27603 		PPMD(D_PCI, ("\t%s domain power count: %d\n",
1960*4667Smh27603 		    domp->name, domp->pwr_cnt))
1961*4667Smh27603 	}
1962*4667Smh27603 
1963*4667Smh27603 	PPM_UNLOCK_DOMAIN(domp);
1964*4667Smh27603 
1965*4667Smh27603 	ASSERT(domp->pwr_cnt > 0);
1966*4667Smh27603 
1967*4667Smh27603 	if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
1968*4667Smh27603 		PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
1969*4667Smh27603 		    ppmd->path, *result))
1970*4667Smh27603 	}
1971*4667Smh27603 
1972*4667Smh27603 	PPM_LOCK_DOMAIN(domp);
1973*4667Smh27603 
1974*4667Smh27603 	/*
1975*4667Smh27603 	 * Decr the power count in two cases:
1976*4667Smh27603 	 *
1977*4667Smh27603 	 *   1) request was to power device down and was successful
1978*4667Smh27603 	 *   2) request was to power up (we pre-incremented count), but failed.
1979*4667Smh27603 	 */
1980*4667Smh27603 	if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
1981*4667Smh27603 	    (*result != DDI_SUCCESS && incr)) {
1982*4667Smh27603 		ASSERT(domp->pwr_cnt > 0);
1983*4667Smh27603 		domp->pwr_cnt--;
1984*4667Smh27603 	}
1985*4667Smh27603 
1986*4667Smh27603 	PPMD(D_PCI, ("\t%s domain power count: %d\n",
1987*4667Smh27603 	    domp->name, domp->pwr_cnt))
1988*4667Smh27603 
1989*4667Smh27603 	/*
1990*4667Smh27603 	 * call to pwr_func will update ppm data structures, if it
1991*4667Smh27603 	 * succeeds. ppm should return whatever is the return value
1992*4667Smh27603 	 * from call to pwr_func. This way pm and ppm data structures
1993*4667Smh27603 	 * always in sync. Use dummy_ret from here for any further
1994*4667Smh27603 	 * return values.
1995*4667Smh27603 	 */
1996*4667Smh27603 	if ((domp->pwr_cnt == 0) &&
1997*4667Smh27603 	    (ppm_cpr_window_flag == B_FALSE) &&
1998*4667Smh27603 	    ppm_none_else_holds_power(domp)) {
1999*4667Smh27603 
2000*4667Smh27603 		PPMD(D_PCI, ("About to turn clock off for %s@%s c%d\n",
2001*4667Smh27603 		    PM_NAME(dip), PM_ADDR(dip), cmpt))
2002*4667Smh27603 
2003*4667Smh27603 		dummy_ret = ppm_switch_clock(domp, PPMD_OFF);
2004*4667Smh27603 		if (dummy_ret != DDI_SUCCESS) {
2005*4667Smh27603 			PPMD(D_PCI, ("\tCan't switch clock off: "
2006*4667Smh27603 			    "ret(%d)\n", dummy_ret))
2007*4667Smh27603 		}
2008*4667Smh27603 	}
2009*4667Smh27603 
2010*4667Smh27603 	PPM_UNLOCK_DOMAIN(domp);
2011*4667Smh27603 	ASSERT(domp->pwr_cnt >= 0);
2012*4667Smh27603 	return (*result);
2013*4667Smh27603 }
2014*4667Smh27603 
2015*4667Smh27603 /*
2016*4667Smh27603  * When the driver for the primary PCI-Express child has set the device to
2017*4667Smh27603  * lowest power (D3hot), we come here to save even more power by transitioning
2018*4667Smh27603  * the slot to D3cold.  Similarly, if the slot is in D3cold and we need to
2019*4667Smh27603  * power up the child, we come here first to power up the slot.
2020*4667Smh27603  */
2021*4667Smh27603 /* ARGUSED */
2022*4667Smh27603 static int
2023*4667Smh27603 ppm_manage_pcie(dev_info_t *dip, power_req_t *reqp, int *result)
2024*4667Smh27603 {
2025*4667Smh27603 #ifdef DEBUG
2026*4667Smh27603 	char *str = "ppm_manage_pcie";
2027*4667Smh27603 #endif
2028*4667Smh27603 	int (*pwr_func)(ppm_dev_t *, int, int);
2029*4667Smh27603 	int old, new, cmpt;
2030*4667Smh27603 	ppm_dev_t *ppmd;
2031*4667Smh27603 	ppm_domain_t *domp;
2032*4667Smh27603 	int incr = 0;
2033*4667Smh27603 	int dummy_ret;
2034*4667Smh27603 
2035*4667Smh27603 	*result = DDI_SUCCESS;
2036*4667Smh27603 	switch (reqp->request_type) {
2037*4667Smh27603 	case PMR_PPM_SET_POWER:
2038*4667Smh27603 		pwr_func = ppm_change_power_level;
2039*4667Smh27603 		old = reqp->req.ppm_set_power_req.old_level;
2040*4667Smh27603 		new = reqp->req.ppm_set_power_req.new_level;
2041*4667Smh27603 		cmpt = reqp->req.ppm_set_power_req.cmpt;
2042*4667Smh27603 		break;
2043*4667Smh27603 
2044*4667Smh27603 	case PMR_PPM_POWER_CHANGE_NOTIFY:
2045*4667Smh27603 		pwr_func = ppm_record_level_change;
2046*4667Smh27603 		old = reqp->req.ppm_notify_level_req.old_level;
2047*4667Smh27603 		new = reqp->req.ppm_notify_level_req.new_level;
2048*4667Smh27603 		cmpt = reqp->req.ppm_notify_level_req.cmpt;
2049*4667Smh27603 		break;
2050*4667Smh27603 
2051*4667Smh27603 	default:
2052*4667Smh27603 		*result = DDI_FAILURE;
2053*4667Smh27603 		return (DDI_FAILURE);
2054*4667Smh27603 	}
2055*4667Smh27603 
2056*4667Smh27603 	for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
2057*4667Smh27603 		if (cmpt == ppmd->cmpt)
2058*4667Smh27603 			break;
2059*4667Smh27603 	if (!ppmd) {
2060*4667Smh27603 		PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
2061*4667Smh27603 		    " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
2062*4667Smh27603 		*result = DDI_FAILURE;
2063*4667Smh27603 		return (DDI_FAILURE);
2064*4667Smh27603 	}
2065*4667Smh27603 	domp = ppmd->domp;
2066*4667Smh27603 	PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
2067*4667Smh27603 	    ppm_get_ctlstr(reqp->request_type, ~0),
2068*4667Smh27603 	    ppmd->path, cmpt, old, new))
2069*4667Smh27603 
2070*4667Smh27603 	ASSERT(old == ppmd->level);
2071*4667Smh27603 	if (new == ppmd->level)
2072*4667Smh27603 		return (DDI_SUCCESS);
2073*4667Smh27603 
2074*4667Smh27603 	PPM_LOCK_DOMAIN(domp);
2075*4667Smh27603 
2076*4667Smh27603 	if (new > 0) {		/* device powering up */
2077*4667Smh27603 		if (domp->status == PPMD_OFF) {
2078*4667Smh27603 
2079*4667Smh27603 			/* cannot be off during (chpt, resume) window */
2080*4667Smh27603 			ASSERT(ppm_cpr_window_flag == B_FALSE);
2081*4667Smh27603 
2082*4667Smh27603 			/* either both OFF or both ON */
2083*4667Smh27603 			ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
2084*4667Smh27603 
2085*4667Smh27603 			PPMD(D_PCI, ("About to turn on pcie slot for "
2086*4667Smh27603 			    "%s@%s c%d\n", PM_NAME(dip), PM_ADDR(dip), cmpt))
2087*4667Smh27603 
2088*4667Smh27603 			*result = ppm_pcie_pwr(domp, PPMD_ON);
2089*4667Smh27603 			if (*result != DDI_SUCCESS) {
2090*4667Smh27603 				PPMD(D_PCI, ("\tcan't switch on pcie slot: "
2091*4667Smh27603 				    "ret(%d)\n", *result))
2092*4667Smh27603 				PPM_UNLOCK_DOMAIN(domp);
2093*4667Smh27603 				return (DDI_FAILURE);
2094*4667Smh27603 			}
2095*4667Smh27603 		}
2096*4667Smh27603 
2097*4667Smh27603 		if (old == 0) {
2098*4667Smh27603 			domp->pwr_cnt++;
2099*4667Smh27603 			incr = 1;
2100*4667Smh27603 		}
2101*4667Smh27603 
2102*4667Smh27603 		PPMD(D_PCI, ("\t%s domain power count: %d\n",
2103*4667Smh27603 		    domp->name, domp->pwr_cnt))
2104*4667Smh27603 	}
2105*4667Smh27603 
2106*4667Smh27603 	PPM_UNLOCK_DOMAIN(domp);
2107*4667Smh27603 
2108*4667Smh27603 	ASSERT(domp->pwr_cnt > 0);
2109*4667Smh27603 
2110*4667Smh27603 	if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
2111*4667Smh27603 		PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
2112*4667Smh27603 		    ppmd->path, *result))
2113*4667Smh27603 	}
2114*4667Smh27603 
2115*4667Smh27603 	PPM_LOCK_DOMAIN(domp);
2116*4667Smh27603 
2117*4667Smh27603 	/*
2118*4667Smh27603 	 * Decr the power count in two cases:
2119*4667Smh27603 	 *
2120*4667Smh27603 	 *   1) request was to power device down and was successful
2121*4667Smh27603 	 *   2) request was to power up (we pre-incremented count), but failed.
2122*4667Smh27603 	 */
2123*4667Smh27603 	if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
2124*4667Smh27603 	    (*result != DDI_SUCCESS && incr)) {
2125*4667Smh27603 		ASSERT(domp->pwr_cnt > 0);
2126*4667Smh27603 		domp->pwr_cnt--;
2127*4667Smh27603 	}
2128*4667Smh27603 
2129*4667Smh27603 	PPMD(D_PCI, ("\t%s domain power count: %d\n",
2130*4667Smh27603 	    domp->name, domp->pwr_cnt))
2131*4667Smh27603 
2132*4667Smh27603 	/*
2133*4667Smh27603 	 * call to pwr_func will update ppm data structures, if it
2134*4667Smh27603 	 * succeeds. ppm should return whatever is the return value
2135*4667Smh27603 	 * from call to pwr_func. This way pm and ppm data structures
2136*4667Smh27603 	 * always in sync. Use dummy_ret from here for any further
2137*4667Smh27603 	 * return values.
2138*4667Smh27603 	 */
2139*4667Smh27603 	if ((domp->pwr_cnt == 0) &&
2140*4667Smh27603 	    (ppm_cpr_window_flag == B_FALSE) &&
2141*4667Smh27603 	    ppm_none_else_holds_power(domp)) {
2142*4667Smh27603 
2143*4667Smh27603 		PPMD(D_PCI, ("About to turn off pcie slot for %s@%s c%d\n",
2144*4667Smh27603 		    PM_NAME(dip), PM_ADDR(dip), cmpt))
2145*4667Smh27603 
2146*4667Smh27603 		dummy_ret = ppm_pcie_pwr(domp, PPMD_OFF);
2147*4667Smh27603 		if (dummy_ret != DDI_SUCCESS) {
2148*4667Smh27603 			PPMD(D_PCI, ("\tCan't switch pcie slot off: "
2149*4667Smh27603 			    "ret(%d)\n", dummy_ret))
2150*4667Smh27603 		}
2151*4667Smh27603 	}
2152*4667Smh27603 
2153*4667Smh27603 	PPM_UNLOCK_DOMAIN(domp);
2154*4667Smh27603 	ASSERT(domp->pwr_cnt >= 0);
2155*4667Smh27603 	return (*result);
2156*4667Smh27603 
2157*4667Smh27603 }
2158*4667Smh27603 
2159*4667Smh27603 /*
2160*4667Smh27603  * Set or clear a bit on a GPIO device.  These bits are used for various device-
2161*4667Smh27603  * specific purposes.
2162*4667Smh27603  */
2163*4667Smh27603 static int
2164*4667Smh27603 ppm_gpioset(ppm_domain_t *domp, int key)
2165*4667Smh27603 {
2166*4667Smh27603 #ifdef DEBUG
2167*4667Smh27603 	char	*str = "ppm_gpioset";
2168*4667Smh27603 #endif
2169*4667Smh27603 	ppm_dc_t *dc;
2170*4667Smh27603 	int	ret;
2171*4667Smh27603 	clock_t delay = 0;
2172*4667Smh27603 
2173*4667Smh27603 	for (dc = domp->dc; dc; dc = dc->next)
2174*4667Smh27603 		if (dc->cmd == key)
2175*4667Smh27603 			break;
2176*4667Smh27603 	if (!dc || !dc->lh) {
2177*4667Smh27603 		PPMD(D_GPIO, ("%s: %s domain: NULL ppm_dc handle\n",
2178*4667Smh27603 		    str, domp->name))
2179*4667Smh27603 		return (DDI_FAILURE);
2180*4667Smh27603 	}
2181*4667Smh27603 
2182*4667Smh27603 	PPM_GET_IO_DELAY(dc, delay);
2183*4667Smh27603 	if (delay > 0) {
2184*4667Smh27603 		PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2185*4667Smh27603 		    "before change\n", domp->name, delay))
2186*4667Smh27603 		drv_usecwait(delay);
2187*4667Smh27603 	}
2188*4667Smh27603 
2189*4667Smh27603 	switch (dc->method) {
2190*4667Smh27603 #if !defined(__x86)
2191*4667Smh27603 	case PPMDC_I2CKIO: {
2192*4667Smh27603 		i2c_gpio_t i2c_req;
2193*4667Smh27603 		ppm_dev_t *pdev;
2194*4667Smh27603 		int pio_save;
2195*4667Smh27603 		extern int do_polled_io;
2196*4667Smh27603 		extern uint_t cfb_inuse;
2197*4667Smh27603 		i2c_req.reg_mask = dc->m_un.i2c.mask;
2198*4667Smh27603 		i2c_req.reg_val = dc->m_un.i2c.val;
2199*4667Smh27603 
2200*4667Smh27603 		pio_save = do_polled_io;
2201*4667Smh27603 		if (cfb_inuse) {
2202*4667Smh27603 			for (pdev = domp->devlist; pdev; pdev = pdev->next) {
2203*4667Smh27603 				if (pm_is_cfb(pdev->dip)) {
2204*4667Smh27603 					do_polled_io = 1;
2205*4667Smh27603 					PPMD(D_GPIO, ("%s: cfb is in use, "
2206*4667Smh27603 					    "i2c transaction is done in "
2207*4667Smh27603 					    "poll-mode.\n", str))
2208*4667Smh27603 					break;
2209*4667Smh27603 				}
2210*4667Smh27603 			}
2211*4667Smh27603 		}
2212*4667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
2213*4667Smh27603 		    (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
2214*4667Smh27603 		do_polled_io = pio_save;
2215*4667Smh27603 
2216*4667Smh27603 		PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
2217*4667Smh27603 		    "to gpio\n",
2218*4667Smh27603 		    str, (ret == 0) ? "turned" : "FAILed to turn",
2219*4667Smh27603 		    domp->name,
2220*4667Smh27603 		    (domp->status == PPMD_ON) ? "ON" : "OFF",
2221*4667Smh27603 		    dc->m_un.i2c.val))
2222*4667Smh27603 
2223*4667Smh27603 		break;
2224*4667Smh27603 	}
2225*4667Smh27603 #endif
2226*4667Smh27603 	case PPMDC_KIO:
2227*4667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
2228*4667Smh27603 		    (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
2229*4667Smh27603 		    NULL);
2230*4667Smh27603 
2231*4667Smh27603 		PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
2232*4667Smh27603 		    "to gpio\n",
2233*4667Smh27603 		    str, (ret == 0) ? "turned" : "FAILed to turn",
2234*4667Smh27603 		    domp->name,
2235*4667Smh27603 		    (domp->status == PPMD_ON) ? "ON" : "OFF",
2236*4667Smh27603 		    dc->m_un.kio.val))
2237*4667Smh27603 
2238*4667Smh27603 		break;
2239*4667Smh27603 
2240*4667Smh27603 	default:
2241*4667Smh27603 		PPMD(D_GPIO, ("\t%s: unsupported domain control method %d\n",
2242*4667Smh27603 		    str, domp->dc->method))
2243*4667Smh27603 		return (DDI_FAILURE);
2244*4667Smh27603 	}
2245*4667Smh27603 
2246*4667Smh27603 	/* implement any post op delay. */
2247*4667Smh27603 	PPM_GET_IO_DELAY(dc, delay);
2248*4667Smh27603 	if (delay > 0) {
2249*4667Smh27603 		PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2250*4667Smh27603 		    "after change\n", domp->name, delay))
2251*4667Smh27603 		drv_usecwait(delay);
2252*4667Smh27603 	}
2253*4667Smh27603 
2254*4667Smh27603 	return (ret);
2255*4667Smh27603 }
2256*4667Smh27603 
2257*4667Smh27603 static int
2258*4667Smh27603 ppm_pcie_pwr(ppm_domain_t *domp, int onoff)
2259*4667Smh27603 {
2260*4667Smh27603 #ifdef DEBUG
2261*4667Smh27603 	char *str = "ppm_pcie_pwr";
2262*4667Smh27603 #endif
2263*4667Smh27603 	int ret = DDI_FAILURE;
2264*4667Smh27603 	ppm_dc_t *dc;
2265*4667Smh27603 	clock_t delay;
2266*4667Smh27603 
2267*4667Smh27603 	ASSERT(onoff == PPMD_OFF || onoff == PPMD_ON);
2268*4667Smh27603 
2269*4667Smh27603 	dc = ppm_lookup_dc(domp,
2270*4667Smh27603 	    onoff == PPMD_ON ? PPMDC_PRE_PWR_ON : PPMDC_PRE_PWR_OFF);
2271*4667Smh27603 	if (dc) {
2272*4667Smh27603 
2273*4667Smh27603 		/*
2274*4667Smh27603 		 * Invoke layered ioctl for pcie root complex nexus to
2275*4667Smh27603 		 * transition the link
2276*4667Smh27603 		 */
2277*4667Smh27603 		ASSERT(dc->method == PPMDC_KIO);
2278*4667Smh27603 		delay = dc->m_un.kio.delay;
2279*4667Smh27603 		if (delay > 0) {
2280*4667Smh27603 			PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2281*4667Smh27603 			    "before change\n", domp->name, delay))
2282*4667Smh27603 			drv_usecwait(delay);
2283*4667Smh27603 		}
2284*4667Smh27603 		ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
2285*4667Smh27603 		    (intptr_t)&(dc->m_un.kio.val),
2286*4667Smh27603 		    FWRITE | FKIOCTL, kcred, NULL);
2287*4667Smh27603 		if (ret == DDI_SUCCESS) {
2288*4667Smh27603 			delay = dc->m_un.kio.post_delay;
2289*4667Smh27603 			if (delay > 0) {
2290*4667Smh27603 				PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2291*4667Smh27603 				    "after change\n", domp->name, delay))
2292*4667Smh27603 				drv_usecwait(delay);
2293*4667Smh27603 			}
2294*4667Smh27603 		} else {
2295*4667Smh27603 			PPMD(D_PCI, ("%s: ldi_ioctl FAILED for domain(%s)\n",
2296*4667Smh27603 			    str, domp->name))
2297*4667Smh27603 			return (ret);
2298*4667Smh27603 		}
2299*4667Smh27603 	}
2300*4667Smh27603 
2301*4667Smh27603 	switch (onoff) {
2302*4667Smh27603 	case PPMD_OFF:
2303*4667Smh27603 		/* Turn off the clock for this slot. */
2304*4667Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_CLK_OFF)) != DDI_SUCCESS) {
2305*4667Smh27603 			PPMD(D_GPIO,
2306*4667Smh27603 			    ("%s: failed to turn off domain(%s) clock\n",
2307*4667Smh27603 			    str, domp->name))
2308*4667Smh27603 			return (ret);
2309*4667Smh27603 		}
2310*4667Smh27603 
2311*4667Smh27603 		/* Turn off the power to this slot */
2312*4667Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_PWR_OFF)) != DDI_SUCCESS) {
2313*4667Smh27603 			PPMD(D_GPIO,
2314*4667Smh27603 			    ("%s: failed to turn off domain(%s) power\n",
2315*4667Smh27603 			    str, domp->name))
2316*4667Smh27603 			return (ret);
2317*4667Smh27603 		}
2318*4667Smh27603 		break;
2319*4667Smh27603 	case PPMD_ON:
2320*4667Smh27603 		/* Assert RESET for this slot. */
2321*4667Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_RESET_ON)) != DDI_SUCCESS) {
2322*4667Smh27603 			PPMD(D_GPIO,
2323*4667Smh27603 			    ("%s: failed to assert reset for domain(%s)\n",
2324*4667Smh27603 			    str, domp->name))
2325*4667Smh27603 			return (ret);
2326*4667Smh27603 		}
2327*4667Smh27603 
2328*4667Smh27603 		/* Turn on the power to this slot */
2329*4667Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_PWR_ON)) != DDI_SUCCESS) {
2330*4667Smh27603 			PPMD(D_GPIO,
2331*4667Smh27603 			    ("%s: failed to turn on domain(%s) power\n",
2332*4667Smh27603 			    str, domp->name))
2333*4667Smh27603 			return (ret);
2334*4667Smh27603 		}
2335*4667Smh27603 
2336*4667Smh27603 		/* Turn on the clock for this slot */
2337*4667Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_CLK_ON)) != DDI_SUCCESS) {
2338*4667Smh27603 			PPMD(D_GPIO,
2339*4667Smh27603 			    ("%s: failed to turn on domain(%s) clock\n",
2340*4667Smh27603 			    str, domp->name))
2341*4667Smh27603 			return (ret);
2342*4667Smh27603 		}
2343*4667Smh27603 
2344*4667Smh27603 		/* De-assert RESET for this slot. */
2345*4667Smh27603 		if ((ret = ppm_gpioset(domp, PPMDC_RESET_OFF)) != DDI_SUCCESS) {
2346*4667Smh27603 			PPMD(D_GPIO,
2347*4667Smh27603 			    ("%s: failed to de-assert reset for domain(%s)\n",
2348*4667Smh27603 			    str, domp->name))
2349*4667Smh27603 			return (ret);
2350*4667Smh27603 		}
2351*4667Smh27603 
2352*4667Smh27603 		dc = ppm_lookup_dc(domp, PPMDC_POST_PWR_ON);
2353*4667Smh27603 		if (dc) {
2354*4667Smh27603 			/*
2355*4667Smh27603 			 * Invoke layered ioctl to PCIe root complex nexus
2356*4667Smh27603 			 * to transition the link.
2357*4667Smh27603 			 */
2358*4667Smh27603 			ASSERT(dc->method == PPMDC_KIO);
2359*4667Smh27603 			delay = dc->m_un.kio.delay;
2360*4667Smh27603 			if (delay > 0) {
2361*4667Smh27603 				PPMD(D_GPIO, ("%s: waiting %lu micro seconds "
2362*4667Smh27603 				    "before change\n", domp->name, delay))
2363*4667Smh27603 				drv_usecwait(delay);
2364*4667Smh27603 			}
2365*4667Smh27603 			ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
2366*4667Smh27603 			    (intptr_t)&(dc->m_un.kio.val),
2367*4667Smh27603 			    FWRITE | FKIOCTL, kcred, NULL);
2368*4667Smh27603 
2369*4667Smh27603 			if (ret != DDI_SUCCESS) {
2370*4667Smh27603 				PPMD(D_PCI, ("%s: layered ioctl to PCIe"
2371*4667Smh27603 				    "root complex nexus FAILed\n", str))
2372*4667Smh27603 				return (ret);
2373*4667Smh27603 			}
2374*4667Smh27603 
2375*4667Smh27603 			delay = dc->m_un.kio.post_delay;
2376*4667Smh27603 			if (delay > 0) {
2377*4667Smh27603 				PPMD(D_GPIO, ("%s: waiting %lu micro "
2378*4667Smh27603 				    "seconds after change\n",
2379*4667Smh27603 				    domp->name, delay))
2380*4667Smh27603 				drv_usecwait(delay);
2381*4667Smh27603 			}
2382*4667Smh27603 		}
2383*4667Smh27603 		break;
2384*4667Smh27603 	default:
2385*4667Smh27603 		ASSERT(0);
2386*4667Smh27603 	}
2387*4667Smh27603 
2388*4667Smh27603 	PPMD(D_PCI, ("%s: turned domain(%s) PCIe slot power from %s to %s\n",
2389*4667Smh27603 	    str, domp->name, (domp->status == PPMD_ON) ? "ON" : "OFF",
2390*4667Smh27603 	    onoff == PPMD_ON ? "ON" : "OFF"))
2391*4667Smh27603 
2392*4667Smh27603 	domp->status = onoff;
2393*4667Smh27603 	return (ret);
2394*4667Smh27603 }
2395*4667Smh27603 
2396*4667Smh27603 
2397*4667Smh27603 /*
2398*4667Smh27603  * Change the power level for a component of a device.  If the change
2399*4667Smh27603  * arg is true, we call the framework to actually change the device's
2400*4667Smh27603  * power; otherwise, we just update our own copy of the power level.
2401*4667Smh27603  */
2402*4667Smh27603 static int
2403*4667Smh27603 ppm_set_level(ppm_dev_t *ppmd, int cmpt, int level, boolean_t change)
2404*4667Smh27603 {
2405*4667Smh27603 #ifdef DEBUG
2406*4667Smh27603 	char *str = "ppm_set_level";
2407*4667Smh27603 #endif
2408*4667Smh27603 	int ret;
2409*4667Smh27603 
2410*4667Smh27603 	ret = DDI_SUCCESS;
2411*4667Smh27603 	if (change)
2412*4667Smh27603 		ret = pm_power(ppmd->dip, cmpt, level);
2413*4667Smh27603 
2414*4667Smh27603 	PPMD(D_SETLVL, ("%s: %s change=%d, old %d, new %d, ret %d\n",
2415*4667Smh27603 	    str, ppmd->path, change, ppmd->level, level, ret))
2416*4667Smh27603 
2417*4667Smh27603 	if (ret == DDI_SUCCESS) {
2418*4667Smh27603 		ppmd->level = level;
2419*4667Smh27603 		ppmd->rplvl = PM_LEVEL_UNKNOWN;
2420*4667Smh27603 	}
2421*4667Smh27603 
2422*4667Smh27603 	return (ret);
2423*4667Smh27603 }
2424*4667Smh27603 
2425*4667Smh27603 
2426*4667Smh27603 static int
2427*4667Smh27603 ppm_change_power_level(ppm_dev_t *ppmd, int cmpt, int level)
2428*4667Smh27603 {
2429*4667Smh27603 	return (ppm_set_level(ppmd, cmpt, level, B_TRUE));
2430*4667Smh27603 }
2431*4667Smh27603 
2432*4667Smh27603 
2433*4667Smh27603 static int
2434*4667Smh27603 ppm_record_level_change(ppm_dev_t *ppmd, int cmpt, int level)
2435*4667Smh27603 {
2436*4667Smh27603 	return (ppm_set_level(ppmd, cmpt, level, B_FALSE));
2437*4667Smh27603 }
2438*4667Smh27603 
2439*4667Smh27603 
2440*4667Smh27603 static void
2441*4667Smh27603 ppm_manage_led(int action)
2442*4667Smh27603 {
2443*4667Smh27603 	ppm_domain_t *domp;
2444*4667Smh27603 	ppm_unit_t *unitp;
2445*4667Smh27603 	timeout_id_t	tid;
2446*4667Smh27603 
2447*4667Smh27603 
2448*4667Smh27603 	PPMD(D_LED, ("ppm_manage_led: action: %s\n",
2449*4667Smh27603 	    (action == PPM_LED_BLINKING) ? "PPM_LED_BLINKING" :
2450*4667Smh27603 	    "PPM_LED_SOLIDON"))
2451*4667Smh27603 
2452*4667Smh27603 	/*
2453*4667Smh27603 	 * test whether led operation is practically supported,
2454*4667Smh27603 	 * if not, we waive without pressing for reasons
2455*4667Smh27603 	 */
2456*4667Smh27603 	if (!ppm_lookup_dc(NULL, PPMDC_LED_ON))
2457*4667Smh27603 		return;
2458*4667Smh27603 
2459*4667Smh27603 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
2460*4667Smh27603 	for (domp = ppm_domain_p; (domp && (domp->model != PPMD_LED)); )
2461*4667Smh27603 		domp = domp->next;
2462*4667Smh27603 
2463*4667Smh27603 	mutex_enter(&unitp->lock);
2464*4667Smh27603 	if (action == PPM_LED_BLINKING) {
2465*4667Smh27603 		ppm_set_led(domp, PPMD_OFF);
2466*4667Smh27603 		unitp->led_tid = timeout(
2467*4667Smh27603 		    ppm_blink_led, domp, PPM_LEDOFF_INTERVAL);
2468*4667Smh27603 
2469*4667Smh27603 	} else {	/* PPM_LED_SOLIDON */
2470*4667Smh27603 		ASSERT(action == PPM_LED_SOLIDON);
2471*4667Smh27603 		tid = unitp->led_tid;
2472*4667Smh27603 		unitp->led_tid = 0;
2473*4667Smh27603 
2474*4667Smh27603 		mutex_exit(&unitp->lock);
2475*4667Smh27603 		(void) untimeout(tid);
2476*4667Smh27603 
2477*4667Smh27603 		mutex_enter(&unitp->lock);
2478*4667Smh27603 		ppm_set_led(domp, PPMD_ON);
2479*4667Smh27603 	}
2480*4667Smh27603 	mutex_exit(&unitp->lock);
2481*4667Smh27603 }
2482*4667Smh27603 
2483*4667Smh27603 
2484*4667Smh27603 static void
2485*4667Smh27603 ppm_set_led(ppm_domain_t *domp, int val)
2486*4667Smh27603 {
2487*4667Smh27603 	int ret;
2488*4667Smh27603 
2489*4667Smh27603 	ret = ppm_gpioset(domp,
2490*4667Smh27603 	    (val == PPMD_ON) ? PPMDC_LED_ON : PPMDC_LED_OFF);
2491*4667Smh27603 
2492*4667Smh27603 	PPMD(D_LED, ("ppm_set_led:  %s LED from %s\n",
2493*4667Smh27603 	    (ret == 0) ? "turned" : "FAILed to turn",
2494*4667Smh27603 	    (domp->status == PPMD_ON) ? "ON to OFF" : "OFF to ON"))
2495*4667Smh27603 
2496*4667Smh27603 	if (ret == DDI_SUCCESS)
2497*4667Smh27603 		domp->status = val;
2498*4667Smh27603 }
2499*4667Smh27603 
2500*4667Smh27603 
2501*4667Smh27603 static void
2502*4667Smh27603 ppm_blink_led(void *arg)
2503*4667Smh27603 {
2504*4667Smh27603 	ppm_unit_t *unitp;
2505*4667Smh27603 	clock_t intvl;
2506*4667Smh27603 	ppm_domain_t *domp = arg;
2507*4667Smh27603 
2508*4667Smh27603 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
2509*4667Smh27603 
2510*4667Smh27603 	mutex_enter(&unitp->lock);
2511*4667Smh27603 	if (unitp->led_tid == 0) {
2512*4667Smh27603 		mutex_exit(&unitp->lock);
2513*4667Smh27603 		return;
2514*4667Smh27603 	}
2515*4667Smh27603 
2516*4667Smh27603 	if (domp->status == PPMD_ON) {
2517*4667Smh27603 		ppm_set_led(domp, PPMD_OFF);
2518*4667Smh27603 		intvl = PPM_LEDOFF_INTERVAL;
2519*4667Smh27603 	} else {
2520*4667Smh27603 		ppm_set_led(domp, PPMD_ON);
2521*4667Smh27603 		intvl = PPM_LEDON_INTERVAL;
2522*4667Smh27603 	}
2523*4667Smh27603 
2524*4667Smh27603 	unitp->led_tid = timeout(ppm_blink_led, domp, intvl);
2525*4667Smh27603 	mutex_exit(&unitp->lock);
2526*4667Smh27603 }
2527*4667Smh27603 
2528*4667Smh27603 /*
2529*4667Smh27603  * Function to power up a domain, if required. It also increments the
2530*4667Smh27603  * domain pwr_cnt to prevent it from going down.
2531*4667Smh27603  */
2532*4667Smh27603 static int
2533*4667Smh27603 ppm_power_up_domain(dev_info_t *dip)
2534*4667Smh27603 {
2535*4667Smh27603 	int		ret = DDI_SUCCESS;
2536*4667Smh27603 	ppm_domain_t	*domp;
2537*4667Smh27603 	char		*str = "ppm_power_up_domain";
2538*4667Smh27603 
2539*4667Smh27603 	domp = ppm_lookup_dev(dip);
2540*4667Smh27603 	ASSERT(domp);
2541*4667Smh27603 	mutex_enter(&domp->lock);
2542*4667Smh27603 	switch (domp->model) {
2543*4667Smh27603 	case PPMD_FET:
2544*4667Smh27603 		if (domp->status == PPMD_OFF) {
2545*4667Smh27603 			if ((ret = ppm_fetset(domp,  PPMD_ON)) ==
2546*4667Smh27603 			    DDI_SUCCESS) {
2547*4667Smh27603 				PPMD(D_FET, ("%s: turned on fet for %s@%s\n",
2548*4667Smh27603 				    str, PM_NAME(dip), PM_ADDR(dip)))
2549*4667Smh27603 			} else {
2550*4667Smh27603 				PPMD(D_FET, ("%s: couldn't turn on fet "
2551*4667Smh27603 				    "for %s@%s\n", str, PM_NAME(dip),
2552*4667Smh27603 				    PM_ADDR(dip)))
2553*4667Smh27603 			}
2554*4667Smh27603 		}
2555*4667Smh27603 		break;
2556*4667Smh27603 
2557*4667Smh27603 	case PPMD_PCI:
2558*4667Smh27603 	case PPMD_PCI_PROP:
2559*4667Smh27603 		if (domp->status == PPMD_OFF) {
2560*4667Smh27603 			if ((ret = ppm_switch_clock(domp, PPMD_ON)) ==
2561*4667Smh27603 			    DDI_SUCCESS) {
2562*4667Smh27603 				PPMD(D_PCI, ("%s: turned on clock for "
2563*4667Smh27603 				    "%s@%s\n", str, PM_NAME(dip),
2564*4667Smh27603 				    PM_ADDR(dip)))
2565*4667Smh27603 			} else {
2566*4667Smh27603 				PPMD(D_PCI, ("%s: couldn't turn on clock "
2567*4667Smh27603 				    "for %s@%s\n", str, PM_NAME(dip),
2568*4667Smh27603 				    PM_ADDR(dip)))
2569*4667Smh27603 			}
2570*4667Smh27603 		}
2571*4667Smh27603 		break;
2572*4667Smh27603 
2573*4667Smh27603 	case PPMD_PCIE:
2574*4667Smh27603 		if (domp->status == PPMD_OFF) {
2575*4667Smh27603 			if ((ret = ppm_pcie_pwr(domp, PPMD_ON)) ==
2576*4667Smh27603 			    DDI_SUCCESS) {
2577*4667Smh27603 				PPMD(D_PCI, ("%s: turned on link for "
2578*4667Smh27603 				    "%s@%s\n", str, PM_NAME(dip),
2579*4667Smh27603 				    PM_ADDR(dip)))
2580*4667Smh27603 			} else {
2581*4667Smh27603 				PPMD(D_PCI, ("%s: couldn't turn on link "
2582*4667Smh27603 				    "for %s@%s\n", str, PM_NAME(dip),
2583*4667Smh27603 				    PM_ADDR(dip)))
2584*4667Smh27603 			}
2585*4667Smh27603 		}
2586*4667Smh27603 		break;
2587*4667Smh27603 
2588*4667Smh27603 	default:
2589*4667Smh27603 		break;
2590*4667Smh27603 	}
2591*4667Smh27603 	if (ret == DDI_SUCCESS)
2592*4667Smh27603 		domp->pwr_cnt++;
2593*4667Smh27603 	mutex_exit(&domp->lock);
2594*4667Smh27603 	return (ret);
2595*4667Smh27603 }
2596*4667Smh27603 
2597*4667Smh27603 /*
2598*4667Smh27603  * Decrements the domain pwr_cnt. if conditions to power down the domain
2599*4667Smh27603  * are met, powers down the domain,.
2600*4667Smh27603  */
2601*4667Smh27603 static int
2602*4667Smh27603 ppm_power_down_domain(dev_info_t *dip)
2603*4667Smh27603 {
2604*4667Smh27603 	int		ret = DDI_SUCCESS;
2605*4667Smh27603 	char		*str = "ppm_power_down_domain";
2606*4667Smh27603 	ppm_domain_t	*domp;
2607*4667Smh27603 
2608*4667Smh27603 	domp = ppm_lookup_dev(dip);
2609*4667Smh27603 	ASSERT(domp);
2610*4667Smh27603 	mutex_enter(&domp->lock);
2611*4667Smh27603 	ASSERT(domp->pwr_cnt > 0);
2612*4667Smh27603 	domp->pwr_cnt--;
2613*4667Smh27603 	switch (domp->model) {
2614*4667Smh27603 	case PPMD_FET:
2615*4667Smh27603 		if ((domp->pwr_cnt == 0) &&
2616*4667Smh27603 		    (ppm_cpr_window_flag == B_FALSE) &&
2617*4667Smh27603 		    ppm_none_else_holds_power(domp)) {
2618*4667Smh27603 			if ((ret = ppm_fetset(domp, PPMD_OFF)) ==
2619*4667Smh27603 			    DDI_SUCCESS) {
2620*4667Smh27603 				PPMD(D_FET, ("%s: turned off FET for %s@%s \n",
2621*4667Smh27603 				    str, PM_NAME(dip), PM_ADDR(dip)))
2622*4667Smh27603 			} else {
2623*4667Smh27603 				PPMD(D_FET, ("%s: couldn't turn off FET for "
2624*4667Smh27603 				    " %s@%s\n", str, PM_NAME(dip),
2625*4667Smh27603 				    PM_ADDR(dip)))
2626*4667Smh27603 			}
2627*4667Smh27603 		}
2628*4667Smh27603 		break;
2629*4667Smh27603 
2630*4667Smh27603 	case PPMD_PCI:
2631*4667Smh27603 	case PPMD_PCI_PROP:
2632*4667Smh27603 		if ((domp->pwr_cnt == 0) &&
2633*4667Smh27603 		    (ppm_cpr_window_flag == B_FALSE) &&
2634*4667Smh27603 		    ppm_none_else_holds_power(domp)) {
2635*4667Smh27603 			if ((ret = ppm_switch_clock(domp, PPMD_OFF)) ==
2636*4667Smh27603 			    DDI_SUCCESS) {
2637*4667Smh27603 				PPMD(D_PCI, ("%s: turned off clock for %s@%s\n",
2638*4667Smh27603 				    str, PM_NAME(dip), PM_ADDR(dip)))
2639*4667Smh27603 			} else {
2640*4667Smh27603 				PPMD(D_PCI, ("%s: couldn't turn off clock "
2641*4667Smh27603 				    "for %s@%s\n", str, PM_NAME(dip),
2642*4667Smh27603 				    PM_ADDR(dip)))
2643*4667Smh27603 			}
2644*4667Smh27603 		}
2645*4667Smh27603 		break;
2646*4667Smh27603 
2647*4667Smh27603 	case PPMD_PCIE:
2648*4667Smh27603 		if ((domp->pwr_cnt == 0) &&
2649*4667Smh27603 		    (ppm_cpr_window_flag == B_FALSE) &&
2650*4667Smh27603 		    ppm_none_else_holds_power(domp)) {
2651*4667Smh27603 			if ((ret = ppm_pcie_pwr(domp, PPMD_OFF)) ==
2652*4667Smh27603 			    DDI_SUCCESS) {
2653*4667Smh27603 				PPMD(D_PCI, ("%s: turned off link for %s@%s\n",
2654*4667Smh27603 				    str, PM_NAME(dip), PM_ADDR(dip)))
2655*4667Smh27603 			} else {
2656*4667Smh27603 				PPMD(D_PCI, ("%s: couldn't turn off link "
2657*4667Smh27603 				    "for %s@%s\n", str, PM_NAME(dip),
2658*4667Smh27603 				    PM_ADDR(dip)))
2659*4667Smh27603 			}
2660*4667Smh27603 		}
2661*4667Smh27603 		break;
2662*4667Smh27603 
2663*4667Smh27603 	default:
2664*4667Smh27603 		break;
2665*4667Smh27603 	}
2666*4667Smh27603 	mutex_exit(&domp->lock);
2667*4667Smh27603 	return (ret);
2668*4667Smh27603 }
2669