xref: /onnv-gate/usr/src/uts/common/io/gen_drv.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate /*
31*0Sstevel@tonic-gate  * generic character driver
32*0Sstevel@tonic-gate  */
33*0Sstevel@tonic-gate #include <sys/types.h>
34*0Sstevel@tonic-gate #include <sys/param.h>
35*0Sstevel@tonic-gate #include <sys/errno.h>
36*0Sstevel@tonic-gate #include <sys/uio.h>
37*0Sstevel@tonic-gate #include <sys/buf.h>
38*0Sstevel@tonic-gate #include <sys/modctl.h>
39*0Sstevel@tonic-gate #include <sys/open.h>
40*0Sstevel@tonic-gate #include <sys/kmem.h>
41*0Sstevel@tonic-gate #include <sys/conf.h>
42*0Sstevel@tonic-gate #include <sys/cmn_err.h>
43*0Sstevel@tonic-gate #include <sys/stat.h>
44*0Sstevel@tonic-gate #include <sys/ddi.h>
45*0Sstevel@tonic-gate #include <sys/sunddi.h>
46*0Sstevel@tonic-gate #include <sys/sunndi.h>
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate 
49*0Sstevel@tonic-gate #define	NUMEVENTS 6
50*0Sstevel@tonic-gate #define	COMPONENTS 2
51*0Sstevel@tonic-gate #define	COMP_0_MAXPWR	3
52*0Sstevel@tonic-gate #define	COMP_1_MAXPWR	2
53*0Sstevel@tonic-gate #define	MINPWR		0
54*0Sstevel@tonic-gate static int maxpwr[] = { COMP_0_MAXPWR, COMP_1_MAXPWR };
55*0Sstevel@tonic-gate 
56*0Sstevel@tonic-gate /*
57*0Sstevel@tonic-gate  * The state for each generic device.
58*0Sstevel@tonic-gate  * NOTE: We save the node_type in the state structure. The node_type string
59*0Sstevel@tonic-gate  * (and not a copy) is stashed in a minor node by  ddi_create_minor_node(),
60*0Sstevel@tonic-gate  * so ddi_remove_minor_node() must occur prior to state free.
61*0Sstevel@tonic-gate  */
62*0Sstevel@tonic-gate typedef struct dstate {
63*0Sstevel@tonic-gate 	uint_t		flag;
64*0Sstevel@tonic-gate 	dev_info_t	*dip;			/* my devinfo handle */
65*0Sstevel@tonic-gate 	char		*node_type;	/* stable node_type copy */
66*0Sstevel@tonic-gate 	ddi_callback_id_t gen_cb_ids[NUMEVENTS];
67*0Sstevel@tonic-gate 	kmutex_t	lock;
68*0Sstevel@tonic-gate 	char		*nodename;
69*0Sstevel@tonic-gate 	int		level[COMPONENTS];	/* pm level */
70*0Sstevel@tonic-gate 	int		busy[COMPONENTS];	/* busy state */
71*0Sstevel@tonic-gate } dstate_t;
72*0Sstevel@tonic-gate 
73*0Sstevel@tonic-gate 
74*0Sstevel@tonic-gate static void *dstates;
75*0Sstevel@tonic-gate 
76*0Sstevel@tonic-gate static int gen_debug = 0;
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate #ifdef DEBUG
79*0Sstevel@tonic-gate #define	gen_debug gen_debug_on
80*0Sstevel@tonic-gate static int gen_debug_on = 0;
81*0Sstevel@tonic-gate #define	GEN_DEBUG(args) if (gen_debug) cmn_err args
82*0Sstevel@tonic-gate #else
83*0Sstevel@tonic-gate #define	GEN_DEBUG(args)
84*0Sstevel@tonic-gate #endif
85*0Sstevel@tonic-gate 
86*0Sstevel@tonic-gate extern void prom_printf(const char *fmt, ...);
87*0Sstevel@tonic-gate 
88*0Sstevel@tonic-gate static int gen_open(dev_t *devp, int flag, int otyp, cred_t *cred);
89*0Sstevel@tonic-gate static int gen_close(dev_t devp, int flag, int otyp, cred_t *cred);
90*0Sstevel@tonic-gate static int gen_read(dev_t dev, struct uio *uiop, cred_t *credp);
91*0Sstevel@tonic-gate static int gen_write(dev_t dev, struct uio *uiop, cred_t *credp);
92*0Sstevel@tonic-gate static int gen_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
93*0Sstevel@tonic-gate     cred_t *credp, int *rvalp);
94*0Sstevel@tonic-gate static int gen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
95*0Sstevel@tonic-gate static int gen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
96*0Sstevel@tonic-gate static void gen_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie,
97*0Sstevel@tonic-gate 	void *arg, void *impl_data);
98*0Sstevel@tonic-gate 
99*0Sstevel@tonic-gate static int gen_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
100*0Sstevel@tonic-gate     void **result);
101*0Sstevel@tonic-gate static int gen_create_minor_nodes(dev_info_t *, struct dstate *);
102*0Sstevel@tonic-gate static int gen_power(dev_info_t *, int, int);
103*0Sstevel@tonic-gate 
104*0Sstevel@tonic-gate static struct cb_ops gen_cb_ops = {
105*0Sstevel@tonic-gate 	gen_open,			/* open */
106*0Sstevel@tonic-gate 	gen_close,			/* close */
107*0Sstevel@tonic-gate 	nodev,				/* strategy */
108*0Sstevel@tonic-gate 	nodev,				/* print */
109*0Sstevel@tonic-gate 	nodev,				/* dump */
110*0Sstevel@tonic-gate 	gen_read,			/* read */
111*0Sstevel@tonic-gate 	gen_write,			/* write */
112*0Sstevel@tonic-gate 	gen_ioctl,			/* ioctl */
113*0Sstevel@tonic-gate 	nodev,				/* devmap */
114*0Sstevel@tonic-gate 	nodev,				/* mmap */
115*0Sstevel@tonic-gate 	nodev,				/* segmap */
116*0Sstevel@tonic-gate 	nochpoll,			/* poll */
117*0Sstevel@tonic-gate 	ddi_prop_op,			/* prop_op */
118*0Sstevel@tonic-gate 	NULL,				/* streamtab */
119*0Sstevel@tonic-gate 	D_NEW | D_MP | D_HOTPLUG,	/* flag */
120*0Sstevel@tonic-gate 	CB_REV,				/* cb_rev */
121*0Sstevel@tonic-gate 	nodev,				/* aread */
122*0Sstevel@tonic-gate 	nodev				/* awrite */
123*0Sstevel@tonic-gate };
124*0Sstevel@tonic-gate 
125*0Sstevel@tonic-gate 
126*0Sstevel@tonic-gate static struct dev_ops gen_ops = {
127*0Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev */
128*0Sstevel@tonic-gate 	0,			/* refcnt */
129*0Sstevel@tonic-gate 	gen_info,		/* getinfo */
130*0Sstevel@tonic-gate 	nulldev,		/* identify */
131*0Sstevel@tonic-gate 	nulldev,		/* probe */
132*0Sstevel@tonic-gate 	gen_attach,		/* attach */
133*0Sstevel@tonic-gate 	gen_detach,		/* detach */
134*0Sstevel@tonic-gate 	nodev,			/* reset */
135*0Sstevel@tonic-gate 	&gen_cb_ops,		/* driver ops */
136*0Sstevel@tonic-gate 	(struct bus_ops *)0,	/* bus ops */
137*0Sstevel@tonic-gate 	gen_power		/* power */
138*0Sstevel@tonic-gate };
139*0Sstevel@tonic-gate 
140*0Sstevel@tonic-gate /*
141*0Sstevel@tonic-gate  * INST_TO_MINOR() gives the starting minor number for a given gen_drv driver
142*0Sstevel@tonic-gate  * instance. A shift left by 6 bits allows for each instance to have upto
143*0Sstevel@tonic-gate  * 64 (2^6) minor numbers. The maximum minor number allowed by the system
144*0Sstevel@tonic-gate  * is L_MAXMIN32 (0x3ffff). This effectively limits the gen_drv instance
145*0Sstevel@tonic-gate  * numbers from 0 to 0xfff for a total of 4096 instances.
146*0Sstevel@tonic-gate  */
147*0Sstevel@tonic-gate #define	INST_TO_MINOR(i)	(i << 6)
148*0Sstevel@tonic-gate #define	MINOR_TO_INST(mn)	(mn >> 6)
149*0Sstevel@tonic-gate 
150*0Sstevel@tonic-gate static char *mnodetypes[] = {
151*0Sstevel@tonic-gate 	"ddi_nt",
152*0Sstevel@tonic-gate 	"ddi_nt:device_type",
153*0Sstevel@tonic-gate 	"ddi_nt:device_class:bus_class",
154*0Sstevel@tonic-gate 	"ddi_nt2",
155*0Sstevel@tonic-gate 	"ddi_nt2:device_type",
156*0Sstevel@tonic-gate 	"ddi_nt2:device_type:bus_class",
157*0Sstevel@tonic-gate };
158*0Sstevel@tonic-gate #define	N_NTYPES	(sizeof (mnodetypes) / sizeof (char *))
159*0Sstevel@tonic-gate 
160*0Sstevel@tonic-gate static struct modldrv modldrv = {
161*0Sstevel@tonic-gate 	&mod_driverops,
162*0Sstevel@tonic-gate 	"generic test driver %I%",
163*0Sstevel@tonic-gate 	&gen_ops
164*0Sstevel@tonic-gate };
165*0Sstevel@tonic-gate 
166*0Sstevel@tonic-gate static struct modlinkage modlinkage = {
167*0Sstevel@tonic-gate 	MODREV_1, &modldrv, NULL
168*0Sstevel@tonic-gate };
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate 
171*0Sstevel@tonic-gate /*
172*0Sstevel@tonic-gate  * flags
173*0Sstevel@tonic-gate  */
174*0Sstevel@tonic-gate #define	OPEN_FLAG			0x001
175*0Sstevel@tonic-gate #define	PWR_HAS_CHANGED_ON_RESUME_FLAG	0x002
176*0Sstevel@tonic-gate #define	FAIL_SUSPEND_FLAG		0x004
177*0Sstevel@tonic-gate #define	PUP_WITH_PWR_HAS_CHANGED_FLAG	0x008
178*0Sstevel@tonic-gate #define	POWER_FLAG			0x010
179*0Sstevel@tonic-gate #define	LOWER_POWER_FLAG		0x020
180*0Sstevel@tonic-gate #define	NO_INVOL_FLAG			0x040
181*0Sstevel@tonic-gate #define	PM_SUPPORTED_FLAG		0x080
182*0Sstevel@tonic-gate 
183*0Sstevel@tonic-gate /*
184*0Sstevel@tonic-gate  * ioctl commands (non-devctl ioctl commands)
185*0Sstevel@tonic-gate  */
186*0Sstevel@tonic-gate #define	GENDRV_IOCTL				('P' << 8)
187*0Sstevel@tonic-gate #define	GENDRV_IOFAULT_SIMULATE			(GENDRV_IOCTL | 0)
188*0Sstevel@tonic-gate #define	GENDRV_NDI_EVENT_TEST			(GENDRV_IOCTL | 1)
189*0Sstevel@tonic-gate 
190*0Sstevel@tonic-gate int
191*0Sstevel@tonic-gate _init(void)
192*0Sstevel@tonic-gate {
193*0Sstevel@tonic-gate 	int e;
194*0Sstevel@tonic-gate 
195*0Sstevel@tonic-gate 	if ((e = ddi_soft_state_init(&dstates,
196*0Sstevel@tonic-gate 	    sizeof (struct dstate), 0)) != 0) {
197*0Sstevel@tonic-gate 		return (e);
198*0Sstevel@tonic-gate 	}
199*0Sstevel@tonic-gate 
200*0Sstevel@tonic-gate 	if ((e = mod_install(&modlinkage)) != 0)  {
201*0Sstevel@tonic-gate 		ddi_soft_state_fini(&dstates);
202*0Sstevel@tonic-gate 	}
203*0Sstevel@tonic-gate 
204*0Sstevel@tonic-gate 	return (e);
205*0Sstevel@tonic-gate }
206*0Sstevel@tonic-gate 
207*0Sstevel@tonic-gate int
208*0Sstevel@tonic-gate _fini(void)
209*0Sstevel@tonic-gate {
210*0Sstevel@tonic-gate 	int e;
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate 	if ((e = mod_remove(&modlinkage)) != 0)  {
213*0Sstevel@tonic-gate 		return (e);
214*0Sstevel@tonic-gate 	}
215*0Sstevel@tonic-gate 	ddi_soft_state_fini(&dstates);
216*0Sstevel@tonic-gate 	return (e);
217*0Sstevel@tonic-gate }
218*0Sstevel@tonic-gate 
219*0Sstevel@tonic-gate int
220*0Sstevel@tonic-gate _info(struct modinfo *modinfop)
221*0Sstevel@tonic-gate {
222*0Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
223*0Sstevel@tonic-gate }
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate static int
226*0Sstevel@tonic-gate gen_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
227*0Sstevel@tonic-gate {
228*0Sstevel@tonic-gate 	int instance = ddi_get_instance(devi);
229*0Sstevel@tonic-gate 	struct dstate *dstatep;
230*0Sstevel@tonic-gate 	int rval;
231*0Sstevel@tonic-gate 	int n_devs;
232*0Sstevel@tonic-gate 	int n_minorcomps;
233*0Sstevel@tonic-gate 	int isclone;
234*0Sstevel@tonic-gate 	ddi_eventcookie_t dev_offline_cookie, dev_reset_cookie;
235*0Sstevel@tonic-gate 	ddi_eventcookie_t bus_reset_cookie, bus_quiesce_cookie;
236*0Sstevel@tonic-gate 	ddi_eventcookie_t bus_unquiesce_cookie, bus_test_post_cookie;
237*0Sstevel@tonic-gate 	int i_init = 0;
238*0Sstevel@tonic-gate 	int level_tmp;
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate 	int i;
241*0Sstevel@tonic-gate 	char *pm_comp[] = {
242*0Sstevel@tonic-gate 		"NAME=leaf0",
243*0Sstevel@tonic-gate 		"0=D0",
244*0Sstevel@tonic-gate 		"1=D1",
245*0Sstevel@tonic-gate 		"2=D2",
246*0Sstevel@tonic-gate 		"3=D3",
247*0Sstevel@tonic-gate 		"NAME=leaf1",
248*0Sstevel@tonic-gate 		"0=off",
249*0Sstevel@tonic-gate 		"1=blank",
250*0Sstevel@tonic-gate 		"2=on"};
251*0Sstevel@tonic-gate 	char *pm_hw_state = {"needs-suspend-resume"};
252*0Sstevel@tonic-gate 
253*0Sstevel@tonic-gate 
254*0Sstevel@tonic-gate 	switch (cmd) {
255*0Sstevel@tonic-gate 	case DDI_ATTACH:
256*0Sstevel@tonic-gate 
257*0Sstevel@tonic-gate 		if (ddi_soft_state_zalloc(dstates, instance) !=
258*0Sstevel@tonic-gate 		    DDI_SUCCESS) {
259*0Sstevel@tonic-gate 			cmn_err(CE_CONT, "%s%d: can't allocate state\n",
260*0Sstevel@tonic-gate 			    ddi_get_name(devi), instance);
261*0Sstevel@tonic-gate 
262*0Sstevel@tonic-gate 			return (DDI_FAILURE);
263*0Sstevel@tonic-gate 		}
264*0Sstevel@tonic-gate 
265*0Sstevel@tonic-gate 		dstatep = ddi_get_soft_state(dstates, instance);
266*0Sstevel@tonic-gate 		dstatep->dip = devi;
267*0Sstevel@tonic-gate 		mutex_init(&dstatep->lock, NULL, MUTEX_DRIVER, NULL);
268*0Sstevel@tonic-gate 
269*0Sstevel@tonic-gate 		n_devs = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
270*0Sstevel@tonic-gate 		    "ndevs", 1);
271*0Sstevel@tonic-gate 
272*0Sstevel@tonic-gate 		isclone = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
273*0Sstevel@tonic-gate 		    "isclone", 0);
274*0Sstevel@tonic-gate 
275*0Sstevel@tonic-gate 		n_minorcomps = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
276*0Sstevel@tonic-gate 		    "ncomps", 1);
277*0Sstevel@tonic-gate 
278*0Sstevel@tonic-gate 		GEN_DEBUG((CE_CONT,
279*0Sstevel@tonic-gate 		    "%s%d attaching: n_devs=%d n_minorcomps=%d isclone=%d",
280*0Sstevel@tonic-gate 		    ddi_get_name(devi), ddi_get_instance(devi),
281*0Sstevel@tonic-gate 		    n_devs, n_minorcomps, isclone));
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate 		if (isclone) {
284*0Sstevel@tonic-gate 			if (ddi_create_minor_node(devi, "gen", S_IFCHR,
285*0Sstevel@tonic-gate 			    INST_TO_MINOR(instance), mnodetypes[0],
286*0Sstevel@tonic-gate 			    isclone) != DDI_SUCCESS) {
287*0Sstevel@tonic-gate 				ddi_remove_minor_node(devi, NULL);
288*0Sstevel@tonic-gate 				ddi_soft_state_free(dstates, instance);
289*0Sstevel@tonic-gate 				cmn_err(CE_WARN, "%s%d: can't create minor "
290*0Sstevel@tonic-gate 				"node", ddi_get_name(devi), instance);
291*0Sstevel@tonic-gate 
292*0Sstevel@tonic-gate 				return (DDI_FAILURE);
293*0Sstevel@tonic-gate 			}
294*0Sstevel@tonic-gate 			rval = DDI_SUCCESS;
295*0Sstevel@tonic-gate 		} else {
296*0Sstevel@tonic-gate 			rval = gen_create_minor_nodes(devi, dstatep);
297*0Sstevel@tonic-gate 			if (rval != DDI_SUCCESS) {
298*0Sstevel@tonic-gate 				ddi_prop_remove_all(devi);
299*0Sstevel@tonic-gate 				ddi_remove_minor_node(devi, NULL);
300*0Sstevel@tonic-gate 				ddi_soft_state_free(dstates, instance);
301*0Sstevel@tonic-gate 				cmn_err(CE_WARN, "%s%d: can't create minor "
302*0Sstevel@tonic-gate 				"nodes", ddi_get_name(devi), instance);
303*0Sstevel@tonic-gate 
304*0Sstevel@tonic-gate 				return (DDI_FAILURE);
305*0Sstevel@tonic-gate 			}
306*0Sstevel@tonic-gate 		}
307*0Sstevel@tonic-gate 
308*0Sstevel@tonic-gate 		if (ddi_get_eventcookie(devi, "pshot_dev_offline",
309*0Sstevel@tonic-gate 		    &dev_offline_cookie) == DDI_SUCCESS) {
310*0Sstevel@tonic-gate 			(void) ddi_add_event_handler(devi, dev_offline_cookie,
311*0Sstevel@tonic-gate 			    gen_event_cb, NULL, &(dstatep->gen_cb_ids[0]));
312*0Sstevel@tonic-gate 		}
313*0Sstevel@tonic-gate 
314*0Sstevel@tonic-gate 		if (ddi_get_eventcookie(devi, "pshot_dev_reset",
315*0Sstevel@tonic-gate 		    &dev_reset_cookie) == DDI_SUCCESS) {
316*0Sstevel@tonic-gate 			(void) ddi_add_event_handler(devi, dev_reset_cookie,
317*0Sstevel@tonic-gate 			    gen_event_cb, NULL, &(dstatep->gen_cb_ids[1]));
318*0Sstevel@tonic-gate 		}
319*0Sstevel@tonic-gate 
320*0Sstevel@tonic-gate 		if (ddi_get_eventcookie(devi, "pshot_bus_reset",
321*0Sstevel@tonic-gate 		    &bus_reset_cookie) == DDI_SUCCESS) {
322*0Sstevel@tonic-gate 			(void) ddi_add_event_handler(devi, bus_reset_cookie,
323*0Sstevel@tonic-gate 			    gen_event_cb, NULL, &(dstatep->gen_cb_ids[2]));
324*0Sstevel@tonic-gate 		}
325*0Sstevel@tonic-gate 
326*0Sstevel@tonic-gate 		if (ddi_get_eventcookie(devi, "pshot_bus_quiesce",
327*0Sstevel@tonic-gate 		    &bus_quiesce_cookie) == DDI_SUCCESS) {
328*0Sstevel@tonic-gate 			(void) ddi_add_event_handler(devi, bus_quiesce_cookie,
329*0Sstevel@tonic-gate 			    gen_event_cb, NULL, &(dstatep->gen_cb_ids[3]));
330*0Sstevel@tonic-gate 		}
331*0Sstevel@tonic-gate 
332*0Sstevel@tonic-gate 		if (ddi_get_eventcookie(devi, "pshot_bus_unquiesce",
333*0Sstevel@tonic-gate 		    &bus_unquiesce_cookie) == DDI_SUCCESS) {
334*0Sstevel@tonic-gate 			(void) ddi_add_event_handler(devi,
335*0Sstevel@tonic-gate 			    bus_unquiesce_cookie, gen_event_cb,
336*0Sstevel@tonic-gate 			    NULL, &(dstatep->gen_cb_ids[4]));
337*0Sstevel@tonic-gate 		}
338*0Sstevel@tonic-gate 
339*0Sstevel@tonic-gate 		if (ddi_get_eventcookie(devi, "pshot_bus_test_post",
340*0Sstevel@tonic-gate 		    &bus_test_post_cookie) == DDI_SUCCESS) {
341*0Sstevel@tonic-gate 			(void) ddi_add_event_handler(devi,
342*0Sstevel@tonic-gate 			    bus_test_post_cookie, gen_event_cb,
343*0Sstevel@tonic-gate 			    NULL, &(dstatep->gen_cb_ids[5]));
344*0Sstevel@tonic-gate 		}
345*0Sstevel@tonic-gate 
346*0Sstevel@tonic-gate 		/*
347*0Sstevel@tonic-gate 		 * initialize the devices' pm state
348*0Sstevel@tonic-gate 		 */
349*0Sstevel@tonic-gate 		mutex_enter(&dstatep->lock);
350*0Sstevel@tonic-gate 		dstatep->flag &= ~OPEN_FLAG;
351*0Sstevel@tonic-gate 		dstatep->flag &= ~PWR_HAS_CHANGED_ON_RESUME_FLAG;
352*0Sstevel@tonic-gate 		dstatep->flag &= ~FAIL_SUSPEND_FLAG;
353*0Sstevel@tonic-gate 		dstatep->flag &= ~PUP_WITH_PWR_HAS_CHANGED_FLAG;
354*0Sstevel@tonic-gate 		dstatep->flag |= LOWER_POWER_FLAG;
355*0Sstevel@tonic-gate 		dstatep->flag &= ~NO_INVOL_FLAG;
356*0Sstevel@tonic-gate 		dstatep->flag |= PM_SUPPORTED_FLAG;
357*0Sstevel@tonic-gate 		dstatep->busy[0] = 0;
358*0Sstevel@tonic-gate 		dstatep->busy[1] = 0;
359*0Sstevel@tonic-gate 		dstatep->level[0] = -1;
360*0Sstevel@tonic-gate 		dstatep->level[1] = -1;
361*0Sstevel@tonic-gate 		mutex_exit(&dstatep->lock);
362*0Sstevel@tonic-gate 
363*0Sstevel@tonic-gate 		/*
364*0Sstevel@tonic-gate 		 * stash the nodename
365*0Sstevel@tonic-gate 		 */
366*0Sstevel@tonic-gate 		dstatep->nodename = ddi_node_name(devi);
367*0Sstevel@tonic-gate 
368*0Sstevel@tonic-gate 		/*
369*0Sstevel@tonic-gate 		 * Check if the no-involuntary-power-cycles property
370*0Sstevel@tonic-gate 		 * was created. Set NO_INVOL_FLAG if so.
371*0Sstevel@tonic-gate 		 */
372*0Sstevel@tonic-gate 		if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip,
373*0Sstevel@tonic-gate 		    (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
374*0Sstevel@tonic-gate 		    "no-involuntary-power-cycles") == 1) {
375*0Sstevel@tonic-gate 			GEN_DEBUG((CE_CONT,
376*0Sstevel@tonic-gate 			    "%s%d: DDI_ATTACH:\n\tno-involuntary-power-cycles"
377*0Sstevel@tonic-gate 			    " property was created",
378*0Sstevel@tonic-gate 			    ddi_node_name(devi), ddi_get_instance(devi)));
379*0Sstevel@tonic-gate 			mutex_enter(&dstatep->lock);
380*0Sstevel@tonic-gate 			dstatep->flag |= NO_INVOL_FLAG;
381*0Sstevel@tonic-gate 			mutex_exit(&dstatep->lock);
382*0Sstevel@tonic-gate 		}
383*0Sstevel@tonic-gate 
384*0Sstevel@tonic-gate 		/*
385*0Sstevel@tonic-gate 		 * Check if the dependency-property property
386*0Sstevel@tonic-gate 		 * was created.
387*0Sstevel@tonic-gate 		 */
388*0Sstevel@tonic-gate 		if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip,
389*0Sstevel@tonic-gate 		    (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
390*0Sstevel@tonic-gate 		    "dependency-property") == 1) {
391*0Sstevel@tonic-gate 			GEN_DEBUG((CE_CONT,
392*0Sstevel@tonic-gate 			    "%s%d: DDI_ATTACH:\n\tdependency-property"
393*0Sstevel@tonic-gate 			    " property was created",
394*0Sstevel@tonic-gate 			    ddi_node_name(devi), ddi_get_instance(devi)));
395*0Sstevel@tonic-gate 		}
396*0Sstevel@tonic-gate 
397*0Sstevel@tonic-gate 		/*
398*0Sstevel@tonic-gate 		 * create the pm-components property. two comps:
399*0Sstevel@tonic-gate 		 * 4 levels on comp0, 3 on comp 1.
400*0Sstevel@tonic-gate 		 * - skip for a "tape" device, clear PM_SUPPORTED_FLAG
401*0Sstevel@tonic-gate 		 */
402*0Sstevel@tonic-gate 		if (strcmp(ddi_node_name(devi), "tape") != 0) {
403*0Sstevel@tonic-gate 			if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi,
404*0Sstevel@tonic-gate 			    "pm-components", pm_comp, 9) != DDI_PROP_SUCCESS) {
405*0Sstevel@tonic-gate 				cmn_err(CE_WARN, "%s%d: %s\n",
406*0Sstevel@tonic-gate 				    ddi_node_name(devi),
407*0Sstevel@tonic-gate 				    ddi_get_instance(devi),
408*0Sstevel@tonic-gate 				    "unable to create \"pm-components\" "
409*0Sstevel@tonic-gate 				    " property.");
410*0Sstevel@tonic-gate 
411*0Sstevel@tonic-gate 				return (DDI_FAILURE);
412*0Sstevel@tonic-gate 			}
413*0Sstevel@tonic-gate 		} else {
414*0Sstevel@tonic-gate 			mutex_enter(&dstatep->lock);
415*0Sstevel@tonic-gate 			dstatep->flag &= ~PM_SUPPORTED_FLAG;
416*0Sstevel@tonic-gate 			mutex_exit(&dstatep->lock);
417*0Sstevel@tonic-gate 		}
418*0Sstevel@tonic-gate 
419*0Sstevel@tonic-gate 		/*
420*0Sstevel@tonic-gate 		 * Check if the pm-components property was created
421*0Sstevel@tonic-gate 		 */
422*0Sstevel@tonic-gate 		if (dstatep->flag & PM_SUPPORTED_FLAG) {
423*0Sstevel@tonic-gate 			if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip,
424*0Sstevel@tonic-gate 			    (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
425*0Sstevel@tonic-gate 			    "pm-components") != 1) {
426*0Sstevel@tonic-gate 				cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t%s",
427*0Sstevel@tonic-gate 				    ddi_node_name(devi),
428*0Sstevel@tonic-gate 				    ddi_get_instance(devi),
429*0Sstevel@tonic-gate 				    "\"pm-components\" property does"
430*0Sstevel@tonic-gate 				    " not exist");
431*0Sstevel@tonic-gate 
432*0Sstevel@tonic-gate 				return (DDI_FAILURE);
433*0Sstevel@tonic-gate 
434*0Sstevel@tonic-gate 			} else {
435*0Sstevel@tonic-gate 				GEN_DEBUG((CE_CONT, "%s%d: DDI_ATTACH:"
436*0Sstevel@tonic-gate 				    " created pm-components property",
437*0Sstevel@tonic-gate 				    ddi_node_name(devi),
438*0Sstevel@tonic-gate 				    ddi_get_instance(devi)));
439*0Sstevel@tonic-gate 			}
440*0Sstevel@tonic-gate 		}
441*0Sstevel@tonic-gate 
442*0Sstevel@tonic-gate 		/*
443*0Sstevel@tonic-gate 		 * create the pm-hardware-state property.
444*0Sstevel@tonic-gate 		 * needed to get DDI_SUSPEND and DDI_RESUME calls
445*0Sstevel@tonic-gate 		 */
446*0Sstevel@tonic-gate 		if (ddi_prop_update_string(DDI_DEV_T_NONE, devi,
447*0Sstevel@tonic-gate 		    "pm-hardware-state", pm_hw_state) != DDI_PROP_SUCCESS) {
448*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t%s\n",
449*0Sstevel@tonic-gate 			    ddi_node_name(devi), ddi_get_instance(devi),
450*0Sstevel@tonic-gate 			    "unable to create \"pm-hardware-state\" "
451*0Sstevel@tonic-gate 			    " property.");
452*0Sstevel@tonic-gate 
453*0Sstevel@tonic-gate 			return (DDI_FAILURE);
454*0Sstevel@tonic-gate 		}
455*0Sstevel@tonic-gate 
456*0Sstevel@tonic-gate 		/*
457*0Sstevel@tonic-gate 		 * set power levels to max via pm_raise_power(),
458*0Sstevel@tonic-gate 		 */
459*0Sstevel@tonic-gate 		mutex_enter(&dstatep->lock);
460*0Sstevel@tonic-gate 		i_init = (dstatep->flag & PM_SUPPORTED_FLAG) ? 0 : COMPONENTS;
461*0Sstevel@tonic-gate 		mutex_exit(&dstatep->lock);
462*0Sstevel@tonic-gate 		for (i = i_init; i < COMPONENTS; i++) {
463*0Sstevel@tonic-gate 			GEN_DEBUG((CE_CONT,
464*0Sstevel@tonic-gate 			    "%s%d: DDI_ATTACH: pm_raise_power comp %d "
465*0Sstevel@tonic-gate 			    "to level %d", ddi_node_name(devi),
466*0Sstevel@tonic-gate 			    ddi_get_instance(devi), i, maxpwr[i]));
467*0Sstevel@tonic-gate 			if (pm_raise_power(dstatep->dip, i, maxpwr[i]) !=
468*0Sstevel@tonic-gate 			    DDI_SUCCESS) {
469*0Sstevel@tonic-gate 				cmn_err(CE_WARN,
470*0Sstevel@tonic-gate 				    "%s%d: DDI_ATTACH: pm_raise_power failed\n",
471*0Sstevel@tonic-gate 				    ddi_node_name(devi),
472*0Sstevel@tonic-gate 				    ddi_get_instance(devi));
473*0Sstevel@tonic-gate 				dstatep->level[i] = -1;
474*0Sstevel@tonic-gate 
475*0Sstevel@tonic-gate 				return (DDI_FAILURE);
476*0Sstevel@tonic-gate 			}
477*0Sstevel@tonic-gate 		}
478*0Sstevel@tonic-gate 
479*0Sstevel@tonic-gate 		if (rval == DDI_SUCCESS) {
480*0Sstevel@tonic-gate 			ddi_report_dev(devi);
481*0Sstevel@tonic-gate 		}
482*0Sstevel@tonic-gate 		return (rval);
483*0Sstevel@tonic-gate 
484*0Sstevel@tonic-gate 
485*0Sstevel@tonic-gate 	case DDI_RESUME:
486*0Sstevel@tonic-gate 		GEN_DEBUG((CE_CONT, "%s%d: DDI_RESUME", ddi_node_name(devi),
487*0Sstevel@tonic-gate 		    ddi_get_instance(devi)));
488*0Sstevel@tonic-gate 
489*0Sstevel@tonic-gate 		dstatep = ddi_get_soft_state(dstates, ddi_get_instance(devi));
490*0Sstevel@tonic-gate 		if (dstatep == NULL) {
491*0Sstevel@tonic-gate 
492*0Sstevel@tonic-gate 			return (DDI_FAILURE);
493*0Sstevel@tonic-gate 		}
494*0Sstevel@tonic-gate 
495*0Sstevel@tonic-gate 		/*
496*0Sstevel@tonic-gate 		 * Call pm_power_has_changed() if flag
497*0Sstevel@tonic-gate 		 * PWR_HAS_CHANGED_ON_RESUME_FLAG is set,
498*0Sstevel@tonic-gate 		 * then clear the flag
499*0Sstevel@tonic-gate 		 */
500*0Sstevel@tonic-gate 		mutex_enter(&dstatep->lock);
501*0Sstevel@tonic-gate 		i_init = (dstatep->flag & PM_SUPPORTED_FLAG) ? 0 : COMPONENTS;
502*0Sstevel@tonic-gate 		mutex_exit(&dstatep->lock);
503*0Sstevel@tonic-gate 		if (dstatep->flag & PWR_HAS_CHANGED_ON_RESUME_FLAG) {
504*0Sstevel@tonic-gate 			for (i = i_init; i < COMPONENTS; i++) {
505*0Sstevel@tonic-gate 				GEN_DEBUG((CE_CONT,
506*0Sstevel@tonic-gate 				    "%s%d: DDI_RESUME: pm_power_has_changed "
507*0Sstevel@tonic-gate 				    "comp %d to level %d", ddi_node_name(devi),
508*0Sstevel@tonic-gate 				    ddi_get_instance(devi), i, maxpwr[i]));
509*0Sstevel@tonic-gate 				mutex_enter(&dstatep->lock);
510*0Sstevel@tonic-gate 				level_tmp = dstatep->level[i];
511*0Sstevel@tonic-gate 				dstatep->level[i] = maxpwr[i];
512*0Sstevel@tonic-gate 				if (pm_power_has_changed(dstatep->dip, i,
513*0Sstevel@tonic-gate 				    maxpwr[i]) != DDI_SUCCESS) {
514*0Sstevel@tonic-gate 					cmn_err(CE_WARN,
515*0Sstevel@tonic-gate 					    "%s%d: DDI_RESUME:\n\t"
516*0Sstevel@tonic-gate 					    " pm_power_has_changed"
517*0Sstevel@tonic-gate 					    " failed: comp %d to level %d\n",
518*0Sstevel@tonic-gate 					    ddi_node_name(devi),
519*0Sstevel@tonic-gate 					    ddi_get_instance(devi),
520*0Sstevel@tonic-gate 					    i, maxpwr[i]);
521*0Sstevel@tonic-gate 					dstatep->level[i] = level_tmp;
522*0Sstevel@tonic-gate 				}
523*0Sstevel@tonic-gate 				mutex_exit(&dstatep->lock);
524*0Sstevel@tonic-gate 			}
525*0Sstevel@tonic-gate 		} else {
526*0Sstevel@tonic-gate 			/*
527*0Sstevel@tonic-gate 			 * Call pm_raise_power() instead
528*0Sstevel@tonic-gate 			 */
529*0Sstevel@tonic-gate 			for (i = i_init; i < COMPONENTS; i++) {
530*0Sstevel@tonic-gate 				GEN_DEBUG((CE_CONT,
531*0Sstevel@tonic-gate 				    "%s%d: DDI_RESUME: pm_raise_power"
532*0Sstevel@tonic-gate 				    " comp %d to level %d",
533*0Sstevel@tonic-gate 				    ddi_node_name(devi), ddi_get_instance(devi),
534*0Sstevel@tonic-gate 				    i, maxpwr[i]));
535*0Sstevel@tonic-gate 				if (pm_raise_power(dstatep->dip, i, maxpwr[i])
536*0Sstevel@tonic-gate 				    != DDI_SUCCESS) {
537*0Sstevel@tonic-gate 					cmn_err(CE_WARN,
538*0Sstevel@tonic-gate 					    "%s%d: DDI_RESUME:"
539*0Sstevel@tonic-gate 					    "\n\tpm_raise_power"
540*0Sstevel@tonic-gate 					    "failed: comp %d to level %d\n",
541*0Sstevel@tonic-gate 					    ddi_node_name(devi),
542*0Sstevel@tonic-gate 					    ddi_get_instance(devi),
543*0Sstevel@tonic-gate 					    i, maxpwr[i]);
544*0Sstevel@tonic-gate 				}
545*0Sstevel@tonic-gate 			}
546*0Sstevel@tonic-gate 		}
547*0Sstevel@tonic-gate 
548*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
549*0Sstevel@tonic-gate 
550*0Sstevel@tonic-gate 	default:
551*0Sstevel@tonic-gate 		GEN_DEBUG((CE_WARN, "attach: default"));
552*0Sstevel@tonic-gate 		return (DDI_FAILURE);
553*0Sstevel@tonic-gate 	}
554*0Sstevel@tonic-gate }
555*0Sstevel@tonic-gate 
556*0Sstevel@tonic-gate static int
557*0Sstevel@tonic-gate gen_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
558*0Sstevel@tonic-gate {
559*0Sstevel@tonic-gate 	struct dstate *dstatep;
560*0Sstevel@tonic-gate 	int instance;
561*0Sstevel@tonic-gate 	int i;
562*0Sstevel@tonic-gate 	int rv;
563*0Sstevel@tonic-gate 	int rm_power;
564*0Sstevel@tonic-gate 	int level_tmp;
565*0Sstevel@tonic-gate 
566*0Sstevel@tonic-gate #ifdef DEBUG
567*0Sstevel@tonic-gate 	int n_devs;
568*0Sstevel@tonic-gate 	int n_minorcomps;
569*0Sstevel@tonic-gate 	int isclone;
570*0Sstevel@tonic-gate #endif
571*0Sstevel@tonic-gate 
572*0Sstevel@tonic-gate 	switch (cmd) {
573*0Sstevel@tonic-gate 	case DDI_DETACH:
574*0Sstevel@tonic-gate 		GEN_DEBUG((CE_CONT, "%s%d: DDI_DETACH", ddi_node_name(devi),
575*0Sstevel@tonic-gate 		    ddi_get_instance(devi)));
576*0Sstevel@tonic-gate 
577*0Sstevel@tonic-gate 		instance = ddi_get_instance(devi);
578*0Sstevel@tonic-gate 		dstatep = ddi_get_soft_state(dstates, instance);
579*0Sstevel@tonic-gate 		if (dstatep == NULL) {
580*0Sstevel@tonic-gate 
581*0Sstevel@tonic-gate 			return (DDI_FAILURE);
582*0Sstevel@tonic-gate }
583*0Sstevel@tonic-gate 
584*0Sstevel@tonic-gate #ifdef DEBUG
585*0Sstevel@tonic-gate 		n_devs = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
586*0Sstevel@tonic-gate 		    "ndevs", 1);
587*0Sstevel@tonic-gate 
588*0Sstevel@tonic-gate 		isclone = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
589*0Sstevel@tonic-gate 		    "isclone", 0);
590*0Sstevel@tonic-gate 
591*0Sstevel@tonic-gate 		n_minorcomps = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
592*0Sstevel@tonic-gate 		    "ncomps", 1);
593*0Sstevel@tonic-gate #endif /* DEBUG */
594*0Sstevel@tonic-gate 
595*0Sstevel@tonic-gate 		/*
596*0Sstevel@tonic-gate 		 * power off component 1.
597*0Sstevel@tonic-gate 		 */
598*0Sstevel@tonic-gate 		if (dstatep->flag & PM_SUPPORTED_FLAG) {
599*0Sstevel@tonic-gate 			GEN_DEBUG((CE_CONT,
600*0Sstevel@tonic-gate 			    "%s%d: DDI_DETACH: pm_lower_power comp 1 level %d",
601*0Sstevel@tonic-gate 			    ddi_node_name(devi), ddi_get_instance(devi),
602*0Sstevel@tonic-gate 			    MINPWR));
603*0Sstevel@tonic-gate 			if (pm_lower_power(dstatep->dip, 1, MINPWR)
604*0Sstevel@tonic-gate 			    != DDI_SUCCESS) {
605*0Sstevel@tonic-gate 				cmn_err(CE_WARN, "%s%d: DDI_DETACH:\n\t"
606*0Sstevel@tonic-gate 				    "pm_lower_power failed for comp 1 to"
607*0Sstevel@tonic-gate 				    " level %d\n", ddi_node_name(devi),
608*0Sstevel@tonic-gate 				    ddi_get_instance(devi), MINPWR);
609*0Sstevel@tonic-gate 
610*0Sstevel@tonic-gate 				return (DDI_FAILURE);
611*0Sstevel@tonic-gate 			}
612*0Sstevel@tonic-gate 
613*0Sstevel@tonic-gate 			/*
614*0Sstevel@tonic-gate 			 * check power level. Issue pm_power_has_changed
615*0Sstevel@tonic-gate 			 * if not at MINPWR.
616*0Sstevel@tonic-gate 			 */
617*0Sstevel@tonic-gate 			mutex_enter(&dstatep->lock);
618*0Sstevel@tonic-gate 			level_tmp = dstatep->level[1];
619*0Sstevel@tonic-gate 			dstatep->level[1] = MINPWR;
620*0Sstevel@tonic-gate 			if (dstatep->level[1] != MINPWR) {
621*0Sstevel@tonic-gate 				GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
622*0Sstevel@tonic-gate 				    " power off via pm_power_has_changed"
623*0Sstevel@tonic-gate 				    " instead", ddi_node_name(devi),
624*0Sstevel@tonic-gate 				    ddi_get_instance(devi)));
625*0Sstevel@tonic-gate 				if (pm_power_has_changed(dstatep->dip,
626*0Sstevel@tonic-gate 				    1, MINPWR) != DDI_SUCCESS) {
627*0Sstevel@tonic-gate 					GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
628*0Sstevel@tonic-gate 					    " pm_power_has_changed failed for"
629*0Sstevel@tonic-gate 					    " comp 1 to level %d",
630*0Sstevel@tonic-gate 					    ddi_node_name(devi),
631*0Sstevel@tonic-gate 					    ddi_get_instance(devi),
632*0Sstevel@tonic-gate 					    MINPWR));
633*0Sstevel@tonic-gate 					dstatep->level[1] = level_tmp;
634*0Sstevel@tonic-gate 					mutex_exit(&dstatep->lock);
635*0Sstevel@tonic-gate 
636*0Sstevel@tonic-gate 					return (DDI_FAILURE);
637*0Sstevel@tonic-gate 				}
638*0Sstevel@tonic-gate 			}
639*0Sstevel@tonic-gate 			mutex_exit(&dstatep->lock);
640*0Sstevel@tonic-gate 		}
641*0Sstevel@tonic-gate 
642*0Sstevel@tonic-gate 		/*
643*0Sstevel@tonic-gate 		 * If the LOWER_POWER_FLAG flag is not set,
644*0Sstevel@tonic-gate 		 * don't call pm_lowr_power() for comp 0.
645*0Sstevel@tonic-gate 		 * This should be used only for the XXXXX@XX,no_invol
646*0Sstevel@tonic-gate 		 * devices that export the
647*0Sstevel@tonic-gate 		 * no-involuntary-power-cycles property
648*0Sstevel@tonic-gate 		 */
649*0Sstevel@tonic-gate 		if (!(dstatep->flag & LOWER_POWER_FLAG) &&
650*0Sstevel@tonic-gate 		    dstatep->flag & PM_SUPPORTED_FLAG) {
651*0Sstevel@tonic-gate 			cmn_err(CE_NOTE, "%s%d: DDI_DETACH:\n\t"
652*0Sstevel@tonic-gate 			    " NOT CALLING PM_LOWER_POWER():"
653*0Sstevel@tonic-gate 			    " LOWER_POWER_FLAG NOT SET\n",
654*0Sstevel@tonic-gate 			    ddi_node_name(devi), ddi_get_instance(devi));
655*0Sstevel@tonic-gate 		} else if (dstatep->flag & PM_SUPPORTED_FLAG) {
656*0Sstevel@tonic-gate 			GEN_DEBUG((CE_CONT,
657*0Sstevel@tonic-gate 			    "%s%d: DDI_DETACH: pm_lower_power comp 0 level %d",
658*0Sstevel@tonic-gate 			    ddi_node_name(devi), ddi_get_instance(devi),
659*0Sstevel@tonic-gate 			    MINPWR));
660*0Sstevel@tonic-gate 			if (pm_lower_power(dstatep->dip, 0, MINPWR)
661*0Sstevel@tonic-gate 			    != DDI_SUCCESS) {
662*0Sstevel@tonic-gate 				cmn_err(CE_WARN, "%s%d: DDI_DETACH:\n\t"
663*0Sstevel@tonic-gate 				    "pm_lower_power failed for comp 0 to"
664*0Sstevel@tonic-gate 				    " level %d\n", ddi_node_name(devi),
665*0Sstevel@tonic-gate 				    ddi_get_instance(devi), MINPWR);
666*0Sstevel@tonic-gate 
667*0Sstevel@tonic-gate 				return (DDI_FAILURE);
668*0Sstevel@tonic-gate 			}
669*0Sstevel@tonic-gate 
670*0Sstevel@tonic-gate 			/*
671*0Sstevel@tonic-gate 			 * check power level. Issue pm_power_has_changed
672*0Sstevel@tonic-gate 			 * if not at MINPWR.
673*0Sstevel@tonic-gate 			 */
674*0Sstevel@tonic-gate 			mutex_enter(&dstatep->lock);
675*0Sstevel@tonic-gate 			level_tmp = dstatep->level[0];
676*0Sstevel@tonic-gate 			dstatep->level[0] = MINPWR;
677*0Sstevel@tonic-gate 			if (dstatep->level[0] != MINPWR) {
678*0Sstevel@tonic-gate 				GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
679*0Sstevel@tonic-gate 				    " power off via pm_power_has_changed"
680*0Sstevel@tonic-gate 				    " instead", ddi_node_name(devi),
681*0Sstevel@tonic-gate 				    ddi_get_instance(devi)));
682*0Sstevel@tonic-gate 				if (pm_power_has_changed(dstatep->dip,
683*0Sstevel@tonic-gate 				    0, MINPWR) != DDI_SUCCESS) {
684*0Sstevel@tonic-gate 					GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
685*0Sstevel@tonic-gate 					    " pm_power_has_changed failed for"
686*0Sstevel@tonic-gate 					    " comp 0 to level %d",
687*0Sstevel@tonic-gate 					    ddi_node_name(devi),
688*0Sstevel@tonic-gate 					    ddi_get_instance(devi),
689*0Sstevel@tonic-gate 					    MINPWR));
690*0Sstevel@tonic-gate 					dstatep->level[0] = level_tmp;
691*0Sstevel@tonic-gate 					mutex_exit(&dstatep->lock);
692*0Sstevel@tonic-gate 
693*0Sstevel@tonic-gate 					return (DDI_FAILURE);
694*0Sstevel@tonic-gate 				}
695*0Sstevel@tonic-gate 			}
696*0Sstevel@tonic-gate 			mutex_exit(&dstatep->lock);
697*0Sstevel@tonic-gate 		}
698*0Sstevel@tonic-gate 
699*0Sstevel@tonic-gate 		GEN_DEBUG((CE_CONT,
700*0Sstevel@tonic-gate 		    "%s%d detaching: n_devs=%d n_minorcomps=%d isclone=%d",
701*0Sstevel@tonic-gate 		    ddi_node_name(devi), ddi_get_instance(devi),
702*0Sstevel@tonic-gate 		    n_devs, n_minorcomps, isclone));
703*0Sstevel@tonic-gate 
704*0Sstevel@tonic-gate 		for (i = 0; i < NUMEVENTS; i++) {
705*0Sstevel@tonic-gate 			if (dstatep->gen_cb_ids[i]) {
706*0Sstevel@tonic-gate 		(void) ddi_remove_event_handler(dstatep->gen_cb_ids[i]);
707*0Sstevel@tonic-gate 				dstatep->gen_cb_ids[i] = NULL;
708*0Sstevel@tonic-gate 			}
709*0Sstevel@tonic-gate 		}
710*0Sstevel@tonic-gate 
711*0Sstevel@tonic-gate 		ddi_prop_remove_all(devi);
712*0Sstevel@tonic-gate 		ddi_remove_minor_node(devi, NULL);
713*0Sstevel@tonic-gate 		if (dstatep->node_type)
714*0Sstevel@tonic-gate 			kmem_free(dstatep->node_type,
715*0Sstevel@tonic-gate 			    strlen(dstatep->node_type) + 1);
716*0Sstevel@tonic-gate 		ddi_soft_state_free(dstates, instance);
717*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
718*0Sstevel@tonic-gate 
719*0Sstevel@tonic-gate 	case DDI_SUSPEND:
720*0Sstevel@tonic-gate 		GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND",
721*0Sstevel@tonic-gate 		    ddi_node_name(devi), ddi_get_instance(devi)));
722*0Sstevel@tonic-gate 
723*0Sstevel@tonic-gate 		instance = ddi_get_instance(devi);
724*0Sstevel@tonic-gate 		dstatep = ddi_get_soft_state(dstates, instance);
725*0Sstevel@tonic-gate 		if (dstatep == NULL) {
726*0Sstevel@tonic-gate 
727*0Sstevel@tonic-gate 			return (DDI_FAILURE);
728*0Sstevel@tonic-gate 		}
729*0Sstevel@tonic-gate 
730*0Sstevel@tonic-gate 		/*
731*0Sstevel@tonic-gate 		 * fail the suspend if FAIL_SUSPEND_FLAG is set.
732*0Sstevel@tonic-gate 		 * clear the FAIL_SUSPEND_FLAG flag
733*0Sstevel@tonic-gate 		 */
734*0Sstevel@tonic-gate 		mutex_enter(&dstatep->lock);
735*0Sstevel@tonic-gate 		if (dstatep->flag & FAIL_SUSPEND_FLAG) {
736*0Sstevel@tonic-gate 			GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:"
737*0Sstevel@tonic-gate 			    " FAIL_SUSPEND_FLAG is set,"
738*0Sstevel@tonic-gate 			    " fail suspend",
739*0Sstevel@tonic-gate 			    ddi_node_name(devi), ddi_get_instance(devi)));
740*0Sstevel@tonic-gate 			dstatep->flag &= ~FAIL_SUSPEND_FLAG;
741*0Sstevel@tonic-gate 			rv = DDI_FAILURE;
742*0Sstevel@tonic-gate 		} else {
743*0Sstevel@tonic-gate 			rv = DDI_SUCCESS;
744*0Sstevel@tonic-gate 		}
745*0Sstevel@tonic-gate 		mutex_exit(&dstatep->lock);
746*0Sstevel@tonic-gate 
747*0Sstevel@tonic-gate 		/*
748*0Sstevel@tonic-gate 		 * Issue ddi_removing_power() to determine if the suspend
749*0Sstevel@tonic-gate 		 * was initiated by either CPR or DR. If CPR, the system
750*0Sstevel@tonic-gate 		 * will be powered OFF; if this driver has set the
751*0Sstevel@tonic-gate 		 * NO_INVOL_FLAG, then refuse to suspend. If DR, power
752*0Sstevel@tonic-gate 		 * will not be removed, thus allow the suspend.
753*0Sstevel@tonic-gate 		 */
754*0Sstevel@tonic-gate 		if (dstatep->flag & NO_INVOL_FLAG &&
755*0Sstevel@tonic-gate 		    dstatep->flag & PM_SUPPORTED_FLAG) {
756*0Sstevel@tonic-gate 			GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:"
757*0Sstevel@tonic-gate 			    " check via ddi_removing_power()",
758*0Sstevel@tonic-gate 			    ddi_node_name(devi), ddi_get_instance(devi)));
759*0Sstevel@tonic-gate 
760*0Sstevel@tonic-gate 			rm_power = ddi_removing_power(dstatep->dip);
761*0Sstevel@tonic-gate 
762*0Sstevel@tonic-gate 			if (rm_power < 0) {
763*0Sstevel@tonic-gate 				cmn_err(CE_WARN, "%s%d: DDI_SUSPEND:"
764*0Sstevel@tonic-gate 				    " ddi_removing_power() failed\n",
765*0Sstevel@tonic-gate 				    ddi_node_name(devi),
766*0Sstevel@tonic-gate 				    ddi_get_instance(devi));
767*0Sstevel@tonic-gate 			} else if (rm_power == 1) {
768*0Sstevel@tonic-gate 				/*
769*0Sstevel@tonic-gate 				 * CPR: power will be removed
770*0Sstevel@tonic-gate 				 */
771*0Sstevel@tonic-gate 				GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:\n\t"
772*0Sstevel@tonic-gate 				    " CPR: POWER WILL BE REMOVED, THEREFORE"
773*0Sstevel@tonic-gate 				    " REFUSE TO SUSPEND", ddi_node_name(devi),
774*0Sstevel@tonic-gate 				    ddi_get_instance(devi)));
775*0Sstevel@tonic-gate 				rv = DDI_FAILURE;
776*0Sstevel@tonic-gate 			} else if (rm_power == 0) {
777*0Sstevel@tonic-gate 				/*
778*0Sstevel@tonic-gate 				 * DR: power will not be removed
779*0Sstevel@tonic-gate 				 */
780*0Sstevel@tonic-gate 				GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:\n\t"
781*0Sstevel@tonic-gate 				    " DR: POWER WILL NOT BE REMOVED, THEREFORE"
782*0Sstevel@tonic-gate 				    " ALLOW THE SUSPEND", ddi_node_name(devi),
783*0Sstevel@tonic-gate 				    ddi_get_instance(devi)));
784*0Sstevel@tonic-gate 				rv = DDI_SUCCESS;
785*0Sstevel@tonic-gate 			}
786*0Sstevel@tonic-gate 		}
787*0Sstevel@tonic-gate 
788*0Sstevel@tonic-gate 		/*
789*0Sstevel@tonic-gate 		 * power OFF via pm_power_has_changed()
790*0Sstevel@tonic-gate 		 */
791*0Sstevel@tonic-gate 		mutex_enter(&dstatep->lock);
792*0Sstevel@tonic-gate 		if (dstatep->flag & PM_SUPPORTED_FLAG &&
793*0Sstevel@tonic-gate 		    !(dstatep->flag & NO_INVOL_FLAG)) {
794*0Sstevel@tonic-gate 			level_tmp = dstatep->level[0];
795*0Sstevel@tonic-gate 			dstatep->level[0] = MINPWR;
796*0Sstevel@tonic-gate 			GEN_DEBUG((CE_CONT,
797*0Sstevel@tonic-gate 			    "%s%d: DDI_SUSPEND: pm_power_has_changed comp 0"
798*0Sstevel@tonic-gate 			    " level %d", ddi_node_name(devi),
799*0Sstevel@tonic-gate 			    ddi_get_instance(devi), MINPWR));
800*0Sstevel@tonic-gate 			if (pm_power_has_changed(dstatep->dip, 0, MINPWR)
801*0Sstevel@tonic-gate 			    != DDI_SUCCESS) {
802*0Sstevel@tonic-gate 				cmn_err(CE_WARN, "%s%d: DDI_SUSPEND:\n\t"
803*0Sstevel@tonic-gate 				    "pm_power_has_changed failed for comp 0 to"
804*0Sstevel@tonic-gate 				    " level %d\n", ddi_node_name(devi),
805*0Sstevel@tonic-gate 				    ddi_get_instance(devi), MINPWR);
806*0Sstevel@tonic-gate 				dstatep->level[0] = level_tmp;
807*0Sstevel@tonic-gate 				mutex_exit(&dstatep->lock);
808*0Sstevel@tonic-gate 
809*0Sstevel@tonic-gate 				return (DDI_FAILURE);
810*0Sstevel@tonic-gate 			}
811*0Sstevel@tonic-gate 		}
812*0Sstevel@tonic-gate 		mutex_exit(&dstatep->lock);
813*0Sstevel@tonic-gate 
814*0Sstevel@tonic-gate 		return (rv);
815*0Sstevel@tonic-gate 
816*0Sstevel@tonic-gate 	default:
817*0Sstevel@tonic-gate 
818*0Sstevel@tonic-gate 		return (DDI_FAILURE);
819*0Sstevel@tonic-gate 	}
820*0Sstevel@tonic-gate }
821*0Sstevel@tonic-gate 
822*0Sstevel@tonic-gate /* ARGSUSED */
823*0Sstevel@tonic-gate static int
824*0Sstevel@tonic-gate gen_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
825*0Sstevel@tonic-gate {
826*0Sstevel@tonic-gate 	dev_t	dev;
827*0Sstevel@tonic-gate 	int	instance;
828*0Sstevel@tonic-gate 
829*0Sstevel@tonic-gate 	if (infocmd != DDI_INFO_DEVT2INSTANCE)
830*0Sstevel@tonic-gate 		return (DDI_FAILURE);
831*0Sstevel@tonic-gate 
832*0Sstevel@tonic-gate 	dev = (dev_t)arg;
833*0Sstevel@tonic-gate 	instance = MINOR_TO_INST(getminor(dev));
834*0Sstevel@tonic-gate 	*result = (void *)(uintptr_t)instance;
835*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
836*0Sstevel@tonic-gate }
837*0Sstevel@tonic-gate 
838*0Sstevel@tonic-gate 
839*0Sstevel@tonic-gate /*ARGSUSED*/
840*0Sstevel@tonic-gate static int
841*0Sstevel@tonic-gate gen_open(dev_t *devp, int flag, int otyp, cred_t *cred)
842*0Sstevel@tonic-gate {
843*0Sstevel@tonic-gate 	minor_t minor;
844*0Sstevel@tonic-gate 	struct dstate *dstatep;
845*0Sstevel@tonic-gate 
846*0Sstevel@tonic-gate 	if (otyp != OTYP_BLK && otyp != OTYP_CHR)
847*0Sstevel@tonic-gate 		return (EINVAL);
848*0Sstevel@tonic-gate 
849*0Sstevel@tonic-gate 	minor = getminor(*devp);
850*0Sstevel@tonic-gate 	if ((dstatep = ddi_get_soft_state(dstates,
851*0Sstevel@tonic-gate 	    MINOR_TO_INST(minor))) == NULL)
852*0Sstevel@tonic-gate 		return (ENXIO);
853*0Sstevel@tonic-gate 
854*0Sstevel@tonic-gate 	mutex_enter(&dstatep->lock);
855*0Sstevel@tonic-gate 	dstatep->flag |= OPEN_FLAG;
856*0Sstevel@tonic-gate 	mutex_exit(&dstatep->lock);
857*0Sstevel@tonic-gate 
858*0Sstevel@tonic-gate 	GEN_DEBUG((CE_CONT,
859*0Sstevel@tonic-gate 	    "%s%d open",
860*0Sstevel@tonic-gate 	    dstatep->nodename, MINOR_TO_INST(minor)));
861*0Sstevel@tonic-gate 
862*0Sstevel@tonic-gate 	return (0);
863*0Sstevel@tonic-gate }
864*0Sstevel@tonic-gate 
865*0Sstevel@tonic-gate /*ARGSUSED*/
866*0Sstevel@tonic-gate static int
867*0Sstevel@tonic-gate gen_close(dev_t dev, int flag, int otyp, cred_t *cred)
868*0Sstevel@tonic-gate {
869*0Sstevel@tonic-gate 	struct dstate *dstatep;
870*0Sstevel@tonic-gate 	minor_t minor = getminor(dev);
871*0Sstevel@tonic-gate 
872*0Sstevel@tonic-gate 	if (otyp != OTYP_BLK && otyp != OTYP_CHR)
873*0Sstevel@tonic-gate 		return (EINVAL);
874*0Sstevel@tonic-gate 
875*0Sstevel@tonic-gate 	dstatep = ddi_get_soft_state(dstates, MINOR_TO_INST(minor));
876*0Sstevel@tonic-gate 
877*0Sstevel@tonic-gate 	if (dstatep == NULL)
878*0Sstevel@tonic-gate 		return (ENXIO);
879*0Sstevel@tonic-gate 
880*0Sstevel@tonic-gate 	mutex_enter(&dstatep->lock);
881*0Sstevel@tonic-gate 	dstatep->flag &= ~OPEN_FLAG;
882*0Sstevel@tonic-gate 	mutex_exit(&dstatep->lock);
883*0Sstevel@tonic-gate 
884*0Sstevel@tonic-gate 	GEN_DEBUG((CE_CONT,
885*0Sstevel@tonic-gate 	    "%s%d close",
886*0Sstevel@tonic-gate 	    dstatep->nodename, MINOR_TO_INST(minor)));
887*0Sstevel@tonic-gate 
888*0Sstevel@tonic-gate 	return (0);
889*0Sstevel@tonic-gate }
890*0Sstevel@tonic-gate 
891*0Sstevel@tonic-gate /*ARGSUSED*/
892*0Sstevel@tonic-gate static int
893*0Sstevel@tonic-gate gen_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
894*0Sstevel@tonic-gate {
895*0Sstevel@tonic-gate 	struct dstate *dstatep;
896*0Sstevel@tonic-gate 	ddi_eventcookie_t cookie;
897*0Sstevel@tonic-gate 	int instance;
898*0Sstevel@tonic-gate 	int rval = 0;
899*0Sstevel@tonic-gate 	char *nodename;
900*0Sstevel@tonic-gate 	int i;
901*0Sstevel@tonic-gate 	struct devctl_iocdata *dcp;
902*0Sstevel@tonic-gate 	uint_t state;
903*0Sstevel@tonic-gate 	int ret;
904*0Sstevel@tonic-gate 	int level_tmp;
905*0Sstevel@tonic-gate 
906*0Sstevel@tonic-gate 	instance = MINOR_TO_INST(getminor(dev));
907*0Sstevel@tonic-gate 	dstatep = ddi_get_soft_state(dstates, instance);
908*0Sstevel@tonic-gate 	nodename = dstatep->nodename;
909*0Sstevel@tonic-gate 
910*0Sstevel@tonic-gate 	if (dstatep == NULL)
911*0Sstevel@tonic-gate 		return (ENXIO);
912*0Sstevel@tonic-gate 
913*0Sstevel@tonic-gate 	/*
914*0Sstevel@tonic-gate 	 * read devctl ioctl data
915*0Sstevel@tonic-gate 	 */
916*0Sstevel@tonic-gate 	if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
917*0Sstevel@tonic-gate 		return (EFAULT);
918*0Sstevel@tonic-gate 
919*0Sstevel@tonic-gate 	switch (cmd) {
920*0Sstevel@tonic-gate 	case GENDRV_IOFAULT_SIMULATE:
921*0Sstevel@tonic-gate 		if (ddi_get_eventcookie(dstatep->dip, DDI_DEVI_FAULT_EVENT,
922*0Sstevel@tonic-gate 			    &(cookie)) != NDI_SUCCESS)
923*0Sstevel@tonic-gate 			return (DDI_FAILURE);
924*0Sstevel@tonic-gate 
925*0Sstevel@tonic-gate 		return (ndi_post_event(dstatep->dip, dstatep->dip, cookie,
926*0Sstevel@tonic-gate 			    NULL));
927*0Sstevel@tonic-gate 
928*0Sstevel@tonic-gate 	case GENDRV_NDI_EVENT_TEST:
929*0Sstevel@tonic-gate 		if (ddi_get_eventcookie(dstatep->dip, "pshot_dev_offline",
930*0Sstevel@tonic-gate 		    &cookie) == NDI_SUCCESS) {
931*0Sstevel@tonic-gate 			(void) ndi_post_event(dstatep->dip, dstatep->dip,
932*0Sstevel@tonic-gate 			    cookie, NULL);
933*0Sstevel@tonic-gate 		}
934*0Sstevel@tonic-gate 
935*0Sstevel@tonic-gate 		if (ddi_get_eventcookie(dstatep->dip, "pshot_dev_reset",
936*0Sstevel@tonic-gate 		    &cookie) == NDI_SUCCESS) {
937*0Sstevel@tonic-gate 			(void) ndi_post_event(dstatep->dip, dstatep->dip,
938*0Sstevel@tonic-gate 			    cookie, NULL);
939*0Sstevel@tonic-gate 		}
940*0Sstevel@tonic-gate 
941*0Sstevel@tonic-gate 		if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_reset",
942*0Sstevel@tonic-gate 		    &cookie) == NDI_SUCCESS) {
943*0Sstevel@tonic-gate 			(void) ndi_post_event(dstatep->dip, dstatep->dip,
944*0Sstevel@tonic-gate 			    cookie, NULL);
945*0Sstevel@tonic-gate 		}
946*0Sstevel@tonic-gate 
947*0Sstevel@tonic-gate 		if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_quiesce",
948*0Sstevel@tonic-gate 		    &cookie) == NDI_SUCCESS) {
949*0Sstevel@tonic-gate 			(void) ndi_post_event(dstatep->dip, dstatep->dip,
950*0Sstevel@tonic-gate 			    cookie, NULL);
951*0Sstevel@tonic-gate 		}
952*0Sstevel@tonic-gate 
953*0Sstevel@tonic-gate 		if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_unquiesce",
954*0Sstevel@tonic-gate 		    &cookie) == NDI_SUCCESS) {
955*0Sstevel@tonic-gate 			(void) ndi_post_event(dstatep->dip, dstatep->dip,
956*0Sstevel@tonic-gate 			    cookie, NULL);
957*0Sstevel@tonic-gate 		}
958*0Sstevel@tonic-gate 
959*0Sstevel@tonic-gate 		if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_test_post",
960*0Sstevel@tonic-gate 		    &cookie) == NDI_SUCCESS) {
961*0Sstevel@tonic-gate 			(void) ndi_post_event(dstatep->dip, dstatep->dip,
962*0Sstevel@tonic-gate 			    cookie, NULL);
963*0Sstevel@tonic-gate 		}
964*0Sstevel@tonic-gate 
965*0Sstevel@tonic-gate 		break;
966*0Sstevel@tonic-gate 
967*0Sstevel@tonic-gate 	case DEVCTL_PM_PWR_HAS_CHANGED_ON_RESUME:
968*0Sstevel@tonic-gate 		/*
969*0Sstevel@tonic-gate 		 * Issue pm_power_has_changed() call on DDI_RESUME
970*0Sstevel@tonic-gate 		 */
971*0Sstevel@tonic-gate 		mutex_enter(&dstatep->lock);
972*0Sstevel@tonic-gate 		dstatep->flag |= PWR_HAS_CHANGED_ON_RESUME_FLAG;
973*0Sstevel@tonic-gate 		mutex_exit(&dstatep->lock);
974*0Sstevel@tonic-gate 		GEN_DEBUG((CE_CONT, "%s%d:"
975*0Sstevel@tonic-gate 		    " DEVCTL_PM_PWR_HAS_CHANGED_ON_RESUME", nodename,
976*0Sstevel@tonic-gate 		    instance));
977*0Sstevel@tonic-gate 
978*0Sstevel@tonic-gate 		break;
979*0Sstevel@tonic-gate 
980*0Sstevel@tonic-gate 	case DEVCTL_PM_FAIL_SUSPEND:
981*0Sstevel@tonic-gate 		/*
982*0Sstevel@tonic-gate 		 * Fail the suspend attempt in DDI_SUSPEND
983*0Sstevel@tonic-gate 		 */
984*0Sstevel@tonic-gate 		mutex_enter(&dstatep->lock);
985*0Sstevel@tonic-gate 		dstatep->flag |= FAIL_SUSPEND_FLAG;
986*0Sstevel@tonic-gate 		mutex_exit(&dstatep->lock);
987*0Sstevel@tonic-gate 		GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_FAIL_SUSPEND",
988*0Sstevel@tonic-gate 		    nodename, instance));
989*0Sstevel@tonic-gate 
990*0Sstevel@tonic-gate 		break;
991*0Sstevel@tonic-gate 
992*0Sstevel@tonic-gate 	case DEVCTL_PM_PUP_WITH_PWR_HAS_CHANGED:
993*0Sstevel@tonic-gate 		/*
994*0Sstevel@tonic-gate 		 * Use pm_power_has_changed() to power up comp 0 when
995*0Sstevel@tonic-gate 		 * enforcing the comp 0 vs comp-not 0 dependency:
996*0Sstevel@tonic-gate 		 * Power up comp 0 first, if request for comp-not-0
997*0Sstevel@tonic-gate 		 * comes in.
998*0Sstevel@tonic-gate 		 * Else, default to pm_raise_power().
999*0Sstevel@tonic-gate 		 */
1000*0Sstevel@tonic-gate 		mutex_enter(&dstatep->lock);
1001*0Sstevel@tonic-gate 		dstatep->flag |= PUP_WITH_PWR_HAS_CHANGED_FLAG;
1002*0Sstevel@tonic-gate 		mutex_exit(&dstatep->lock);
1003*0Sstevel@tonic-gate 		GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_PUP_WITH_PWR_HAS_CHANGED",
1004*0Sstevel@tonic-gate 		    nodename, instance));
1005*0Sstevel@tonic-gate 
1006*0Sstevel@tonic-gate 		break;
1007*0Sstevel@tonic-gate 
1008*0Sstevel@tonic-gate 	case DEVCTL_PM_BUSY_COMP:
1009*0Sstevel@tonic-gate 		/*
1010*0Sstevel@tonic-gate 		 * mark component 0 busy via a pm_busy_component() call.
1011*0Sstevel@tonic-gate 		 * update the busy[] array.
1012*0Sstevel@tonic-gate 		 */
1013*0Sstevel@tonic-gate 		mutex_enter(&dstatep->lock);
1014*0Sstevel@tonic-gate 		++dstatep->busy[0];
1015*0Sstevel@tonic-gate 		GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_BUSY_COMP: comp 0:"
1016*0Sstevel@tonic-gate 		    " busy=%d", nodename, instance, dstatep->busy[0]));
1017*0Sstevel@tonic-gate 		mutex_exit(&dstatep->lock);
1018*0Sstevel@tonic-gate 		ret = pm_busy_component(dstatep->dip, 0);
1019*0Sstevel@tonic-gate 		ASSERT(ret == DDI_SUCCESS);
1020*0Sstevel@tonic-gate 
1021*0Sstevel@tonic-gate 		break;
1022*0Sstevel@tonic-gate 
1023*0Sstevel@tonic-gate 	case DEVCTL_PM_BUSY_COMP_TEST:
1024*0Sstevel@tonic-gate 		/*
1025*0Sstevel@tonic-gate 		 * test busy state on component 0
1026*0Sstevel@tonic-gate 		 */
1027*0Sstevel@tonic-gate 		mutex_enter(&dstatep->lock);
1028*0Sstevel@tonic-gate 		state = dstatep->busy[0];
1029*0Sstevel@tonic-gate 		if (copyout(&state, dcp->cpyout_buf,
1030*0Sstevel@tonic-gate 		    sizeof (uint_t)) != 0) {
1031*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s%d:"
1032*0Sstevel@tonic-gate 			    " DEVCTL_PM_BUSY_COMP_TEST: copyout failed\n",
1033*0Sstevel@tonic-gate 			    nodename, instance);
1034*0Sstevel@tonic-gate 			rval = EINVAL;
1035*0Sstevel@tonic-gate 		}
1036*0Sstevel@tonic-gate 		GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_BUSY_COMP_TEST:"
1037*0Sstevel@tonic-gate 		    " comp 0 busy %d",
1038*0Sstevel@tonic-gate 		    nodename, instance, state));
1039*0Sstevel@tonic-gate 		mutex_exit(&dstatep->lock);
1040*0Sstevel@tonic-gate 
1041*0Sstevel@tonic-gate 		break;
1042*0Sstevel@tonic-gate 
1043*0Sstevel@tonic-gate 	case DEVCTL_PM_IDLE_COMP:
1044*0Sstevel@tonic-gate 		/*
1045*0Sstevel@tonic-gate 		 * mark component 0 idle via a pm_idle_component() call.
1046*0Sstevel@tonic-gate 		 * NOP if dstatep->busy[0] == 0.
1047*0Sstevel@tonic-gate 		 */
1048*0Sstevel@tonic-gate 		mutex_enter(&dstatep->lock);
1049*0Sstevel@tonic-gate 		if (dstatep->busy[0] > 0) {
1050*0Sstevel@tonic-gate 			--dstatep->busy[0];
1051*0Sstevel@tonic-gate 			GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_IDLE_COMP:"
1052*0Sstevel@tonic-gate 			    " comp 0: busy=%d", nodename, instance,
1053*0Sstevel@tonic-gate 			    dstatep->busy[0]));
1054*0Sstevel@tonic-gate 			mutex_exit(&dstatep->lock);
1055*0Sstevel@tonic-gate 			ret = pm_idle_component(dstatep->dip, 0);
1056*0Sstevel@tonic-gate 			ASSERT(ret == DDI_SUCCESS);
1057*0Sstevel@tonic-gate 		} else {
1058*0Sstevel@tonic-gate 			mutex_exit(&dstatep->lock);
1059*0Sstevel@tonic-gate 		}
1060*0Sstevel@tonic-gate 
1061*0Sstevel@tonic-gate 		break;
1062*0Sstevel@tonic-gate 
1063*0Sstevel@tonic-gate 	case DEVCTL_PM_PROM_PRINTF:
1064*0Sstevel@tonic-gate 		(void) prom_printf("%s%d: PROM_PRINTF FROM GEN_DRV\n",
1065*0Sstevel@tonic-gate 		    nodename, instance);
1066*0Sstevel@tonic-gate 
1067*0Sstevel@tonic-gate 		break;
1068*0Sstevel@tonic-gate 
1069*0Sstevel@tonic-gate 	case DEVCTL_PM_RAISE_PWR:
1070*0Sstevel@tonic-gate 		/*
1071*0Sstevel@tonic-gate 		 * power up both components to MAXPWR via
1072*0Sstevel@tonic-gate 		 * pm_raise_power() calls. this ioctl() cmd
1073*0Sstevel@tonic-gate 		 * assumes that the current level is 0
1074*0Sstevel@tonic-gate 		 */
1075*0Sstevel@tonic-gate 		for (i = 0; i < COMPONENTS; i++) {
1076*0Sstevel@tonic-gate 			GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_RAISE_PWR:"
1077*0Sstevel@tonic-gate 			    " comp %d old 0 new %d",
1078*0Sstevel@tonic-gate 			    nodename, instance, i, maxpwr[i]));
1079*0Sstevel@tonic-gate 			if (pm_raise_power(dstatep->dip, 0, maxpwr[i])
1080*0Sstevel@tonic-gate 			    != DDI_SUCCESS) {
1081*0Sstevel@tonic-gate 				rval = EINVAL;
1082*0Sstevel@tonic-gate 			}
1083*0Sstevel@tonic-gate 		}
1084*0Sstevel@tonic-gate 
1085*0Sstevel@tonic-gate 		break;
1086*0Sstevel@tonic-gate 
1087*0Sstevel@tonic-gate 	case DEVCTL_PM_CHANGE_PWR_LOW:
1088*0Sstevel@tonic-gate 		/*
1089*0Sstevel@tonic-gate 		 * power off both components via pm_power_has_changed() calls
1090*0Sstevel@tonic-gate 		 */
1091*0Sstevel@tonic-gate 		for (i = (COMPONENTS - 1); i >= 0; --i) {
1092*0Sstevel@tonic-gate 			GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_CHANGE_PWR_LOW:"
1093*0Sstevel@tonic-gate 			    " comp %d new 0",
1094*0Sstevel@tonic-gate 			    nodename, instance, i));
1095*0Sstevel@tonic-gate 			mutex_enter(&dstatep->lock);
1096*0Sstevel@tonic-gate 			level_tmp = dstatep->level[i];
1097*0Sstevel@tonic-gate 			dstatep->level[i] = 0;
1098*0Sstevel@tonic-gate 			if (pm_power_has_changed(dstatep->dip, i, 0)
1099*0Sstevel@tonic-gate 			    != DDI_SUCCESS) {
1100*0Sstevel@tonic-gate 				dstatep->level[i] = level_tmp;
1101*0Sstevel@tonic-gate 				rval = EINVAL;
1102*0Sstevel@tonic-gate 			}
1103*0Sstevel@tonic-gate 			mutex_exit(&dstatep->lock);
1104*0Sstevel@tonic-gate 		}
1105*0Sstevel@tonic-gate 
1106*0Sstevel@tonic-gate 		break;
1107*0Sstevel@tonic-gate 
1108*0Sstevel@tonic-gate 	case DEVCTL_PM_CHANGE_PWR_HIGH:
1109*0Sstevel@tonic-gate 		/*
1110*0Sstevel@tonic-gate 		 * power up both components to MAXPWR via
1111*0Sstevel@tonic-gate 		 * pm_power_has_changed() calls
1112*0Sstevel@tonic-gate 		 */
1113*0Sstevel@tonic-gate 		for (i = 0; i < COMPONENTS; i++) {
1114*0Sstevel@tonic-gate 			GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_CHANGE_PWR_HIGH:"
1115*0Sstevel@tonic-gate 			    " comp %d new %d",
1116*0Sstevel@tonic-gate 			    nodename, instance, i, maxpwr[i]));
1117*0Sstevel@tonic-gate 			mutex_enter(&dstatep->lock);
1118*0Sstevel@tonic-gate 			level_tmp = dstatep->level[i];
1119*0Sstevel@tonic-gate 			dstatep->level[i] = maxpwr[i];
1120*0Sstevel@tonic-gate 			if (pm_power_has_changed(dstatep->dip, i, maxpwr[i])
1121*0Sstevel@tonic-gate 			    != DDI_SUCCESS) {
1122*0Sstevel@tonic-gate 				dstatep->level[i] = level_tmp;
1123*0Sstevel@tonic-gate 				rval = EINVAL;
1124*0Sstevel@tonic-gate 			}
1125*0Sstevel@tonic-gate 			mutex_exit(&dstatep->lock);
1126*0Sstevel@tonic-gate 		}
1127*0Sstevel@tonic-gate 
1128*0Sstevel@tonic-gate 		break;
1129*0Sstevel@tonic-gate 
1130*0Sstevel@tonic-gate 	case DEVCTL_PM_POWER:
1131*0Sstevel@tonic-gate 		/*
1132*0Sstevel@tonic-gate 		 * test if the gen_drv_power() routine has been called,
1133*0Sstevel@tonic-gate 		 * then clear
1134*0Sstevel@tonic-gate 		 */
1135*0Sstevel@tonic-gate 		mutex_enter(&dstatep->lock);
1136*0Sstevel@tonic-gate 		state = (dstatep->flag & POWER_FLAG) ? 1 : 0;
1137*0Sstevel@tonic-gate 		if (copyout(&state, dcp->cpyout_buf,
1138*0Sstevel@tonic-gate 		    sizeof (uint_t)) != 0) {
1139*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s%d: DEVCTL_PM_POWER:"
1140*0Sstevel@tonic-gate 			    " copyout failed\n", nodename, instance);
1141*0Sstevel@tonic-gate 			rval = EINVAL;
1142*0Sstevel@tonic-gate 		}
1143*0Sstevel@tonic-gate 		GEN_DEBUG((CE_CONT, "%s%d: %s POWER_FLAG: %d",
1144*0Sstevel@tonic-gate 		    nodename, instance, "DEVCTL_PM_POWER", state));
1145*0Sstevel@tonic-gate 		dstatep->flag &= ~POWER_FLAG;
1146*0Sstevel@tonic-gate 		mutex_exit(&dstatep->lock);
1147*0Sstevel@tonic-gate 		break;
1148*0Sstevel@tonic-gate 
1149*0Sstevel@tonic-gate 	case DEVCTL_PM_NO_LOWER_POWER:
1150*0Sstevel@tonic-gate 		/*
1151*0Sstevel@tonic-gate 		 * issue to not invoke pm_lower_power() on detach
1152*0Sstevel@tonic-gate 		 */
1153*0Sstevel@tonic-gate 		mutex_enter(&dstatep->lock);
1154*0Sstevel@tonic-gate 		dstatep->flag &= ~LOWER_POWER_FLAG;
1155*0Sstevel@tonic-gate 		mutex_exit(&dstatep->lock);
1156*0Sstevel@tonic-gate 		GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_NO_LOWER_POWER",
1157*0Sstevel@tonic-gate 			    nodename, instance));
1158*0Sstevel@tonic-gate 		break;
1159*0Sstevel@tonic-gate 
1160*0Sstevel@tonic-gate 	default:
1161*0Sstevel@tonic-gate 		return (ENOTTY);
1162*0Sstevel@tonic-gate 	}
1163*0Sstevel@tonic-gate 
1164*0Sstevel@tonic-gate 	return (rval);
1165*0Sstevel@tonic-gate }
1166*0Sstevel@tonic-gate 
1167*0Sstevel@tonic-gate /*ARGSUSED*/
1168*0Sstevel@tonic-gate static int
1169*0Sstevel@tonic-gate gen_read(dev_t dev, struct uio *uiop, cred_t *credp)
1170*0Sstevel@tonic-gate {
1171*0Sstevel@tonic-gate 	return (0);
1172*0Sstevel@tonic-gate }
1173*0Sstevel@tonic-gate 
1174*0Sstevel@tonic-gate /*ARGSUSED*/
1175*0Sstevel@tonic-gate static int
1176*0Sstevel@tonic-gate gen_write(dev_t dev, struct uio *uiop, cred_t *credp)
1177*0Sstevel@tonic-gate {
1178*0Sstevel@tonic-gate 	return (0);
1179*0Sstevel@tonic-gate }
1180*0Sstevel@tonic-gate 
1181*0Sstevel@tonic-gate /*ARGSUSED0*/
1182*0Sstevel@tonic-gate static int
1183*0Sstevel@tonic-gate gen_power(dev_info_t *dip, int cmpt, int level)
1184*0Sstevel@tonic-gate {
1185*0Sstevel@tonic-gate 	struct dstate *dstatep;
1186*0Sstevel@tonic-gate 	int instance = ddi_get_instance(dip);
1187*0Sstevel@tonic-gate 	char *nodename = ddi_node_name(dip);
1188*0Sstevel@tonic-gate 	int level_tmp;
1189*0Sstevel@tonic-gate 
1190*0Sstevel@tonic-gate 	GEN_DEBUG((CE_CONT, "%s%d: power: cmpt %d to level %d",
1191*0Sstevel@tonic-gate 	    nodename, instance, cmpt, level));
1192*0Sstevel@tonic-gate 
1193*0Sstevel@tonic-gate 	dstatep = ddi_get_soft_state(dstates, instance);
1194*0Sstevel@tonic-gate 	if (dstatep == NULL) {
1195*0Sstevel@tonic-gate 
1196*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1197*0Sstevel@tonic-gate 	}
1198*0Sstevel@tonic-gate 
1199*0Sstevel@tonic-gate 	/*
1200*0Sstevel@tonic-gate 	 * Keep track of the power levels for both components
1201*0Sstevel@tonic-gate 	 * in the dstatep->comp[] array.
1202*0Sstevel@tonic-gate 	 * Set comp 0 to full level if non-zero comps
1203*0Sstevel@tonic-gate 	 * are being set to a higher, non-zero level.
1204*0Sstevel@tonic-gate 	 */
1205*0Sstevel@tonic-gate 	if (cmpt == 0) {
1206*0Sstevel@tonic-gate 		mutex_enter(&dstatep->lock);
1207*0Sstevel@tonic-gate 		dstatep->level[cmpt] = level;
1208*0Sstevel@tonic-gate 		mutex_exit(&dstatep->lock);
1209*0Sstevel@tonic-gate 	} else if (level > dstatep->level[cmpt] && level != 0 &&
1210*0Sstevel@tonic-gate 	    dstatep->level[0] != COMP_0_MAXPWR) {
1211*0Sstevel@tonic-gate 		/*
1212*0Sstevel@tonic-gate 		 * If component 0 is not at COMP_0_MAXPWR, and component 1
1213*0Sstevel@tonic-gate 		 * is being powered ON, invoke pm_raise_power() or
1214*0Sstevel@tonic-gate 		 * pm_power_has_changed() based on the
1215*0Sstevel@tonic-gate 		 * PUP_WITH_PWR_HAS_CHANGED_FLAG flag.
1216*0Sstevel@tonic-gate 		 * PUP_WITH_PWR_HAS_CHANGED_FLAG = FALSE by default, invoking
1217*0Sstevel@tonic-gate 		 * pm_raise_power().
1218*0Sstevel@tonic-gate 		 */
1219*0Sstevel@tonic-gate 		if (!(dstatep->flag & PUP_WITH_PWR_HAS_CHANGED_FLAG)) {
1220*0Sstevel@tonic-gate 			/*
1221*0Sstevel@tonic-gate 			 * first set comp 0 to level COMP_0_MAXPWR
1222*0Sstevel@tonic-gate 			 */
1223*0Sstevel@tonic-gate 			GEN_DEBUG((CE_CONT, "%s%d: power:  "
1224*0Sstevel@tonic-gate 			    "pm_raise_power: comp 0 to level %d",
1225*0Sstevel@tonic-gate 			    nodename, instance, COMP_0_MAXPWR));
1226*0Sstevel@tonic-gate 			if (pm_raise_power(dip, 0, COMP_0_MAXPWR) !=
1227*0Sstevel@tonic-gate 			    DDI_SUCCESS) {
1228*0Sstevel@tonic-gate 				cmn_err(CE_WARN,
1229*0Sstevel@tonic-gate 				    "%s%d: power: pm_raise_power() "
1230*0Sstevel@tonic-gate 				    "failed: comp 0 to level %d\n",
1231*0Sstevel@tonic-gate 				    nodename, instance, COMP_0_MAXPWR);
1232*0Sstevel@tonic-gate 
1233*0Sstevel@tonic-gate 				return (DDI_FAILURE);
1234*0Sstevel@tonic-gate 
1235*0Sstevel@tonic-gate 			} else {
1236*0Sstevel@tonic-gate 				mutex_enter(&dstatep->lock);
1237*0Sstevel@tonic-gate 				dstatep->level[0] = COMP_0_MAXPWR;
1238*0Sstevel@tonic-gate 				/*
1239*0Sstevel@tonic-gate 				 * now set the level on the non-zero comp
1240*0Sstevel@tonic-gate 				 */
1241*0Sstevel@tonic-gate 				dstatep->level[cmpt] = level;
1242*0Sstevel@tonic-gate 				mutex_exit(&dstatep->lock);
1243*0Sstevel@tonic-gate 				GEN_DEBUG((CE_CONT, "%s%d: power: "
1244*0Sstevel@tonic-gate 				    "comp %d to level %d",
1245*0Sstevel@tonic-gate 				    nodename, instance, cmpt, level));
1246*0Sstevel@tonic-gate 			}
1247*0Sstevel@tonic-gate 		} else {
1248*0Sstevel@tonic-gate 			GEN_DEBUG((CE_CONT, "%s%d: power: "
1249*0Sstevel@tonic-gate 			    "pm_power_has_changed: comp 0 to level %d",
1250*0Sstevel@tonic-gate 			    nodename, instance, COMP_0_MAXPWR));
1251*0Sstevel@tonic-gate 			mutex_enter(&dstatep->lock);
1252*0Sstevel@tonic-gate 			level_tmp = dstatep->level[0];
1253*0Sstevel@tonic-gate 			dstatep->level[0] = COMP_0_MAXPWR;
1254*0Sstevel@tonic-gate 			if (pm_power_has_changed(dip, 0, COMP_0_MAXPWR) !=
1255*0Sstevel@tonic-gate 			    DDI_SUCCESS) {
1256*0Sstevel@tonic-gate 				cmn_err(CE_WARN,
1257*0Sstevel@tonic-gate 				    "%s%d: power: pm_power_has_changed() "
1258*0Sstevel@tonic-gate 				    "failed: comp 0 to level %d\n",
1259*0Sstevel@tonic-gate 				    nodename, instance, COMP_0_MAXPWR);
1260*0Sstevel@tonic-gate 				dstatep->level[0] = level_tmp;
1261*0Sstevel@tonic-gate 			} else {
1262*0Sstevel@tonic-gate 				/*
1263*0Sstevel@tonic-gate 				 * now set the level on the non-zero comp
1264*0Sstevel@tonic-gate 				 */
1265*0Sstevel@tonic-gate 				GEN_DEBUG((CE_CONT, "%s%d: power:"
1266*0Sstevel@tonic-gate 				    " pm_power_has_changed: comp %d"
1267*0Sstevel@tonic-gate 				    " to level %d", nodename, instance,
1268*0Sstevel@tonic-gate 				    cmpt, level));
1269*0Sstevel@tonic-gate 				dstatep->level[cmpt] = level;
1270*0Sstevel@tonic-gate 			}
1271*0Sstevel@tonic-gate 			mutex_exit(&dstatep->lock);
1272*0Sstevel@tonic-gate 		}
1273*0Sstevel@tonic-gate 	} else {
1274*0Sstevel@tonic-gate 		mutex_enter(&dstatep->lock);
1275*0Sstevel@tonic-gate 		dstatep->level[cmpt] = level;
1276*0Sstevel@tonic-gate 		mutex_exit(&dstatep->lock);
1277*0Sstevel@tonic-gate 	}
1278*0Sstevel@tonic-gate 
1279*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
1280*0Sstevel@tonic-gate }
1281*0Sstevel@tonic-gate 
1282*0Sstevel@tonic-gate 
1283*0Sstevel@tonic-gate /*
1284*0Sstevel@tonic-gate  * Create properties of various data types for testing devfs events.
1285*0Sstevel@tonic-gate  */
1286*0Sstevel@tonic-gate static int
1287*0Sstevel@tonic-gate gen_create_properties(dev_info_t *devi)
1288*0Sstevel@tonic-gate {
1289*0Sstevel@tonic-gate 	int int_val = 3023;
1290*0Sstevel@tonic-gate 	int int_array[] = { 3, 10, 304, 230, 4};
1291*0Sstevel@tonic-gate 	int64_t int64_val = 20;
1292*0Sstevel@tonic-gate 	int64_t int64_array[] = { 12, 24, 36, 48};
1293*0Sstevel@tonic-gate 	char *string_val = "Dev_node_prop";
1294*0Sstevel@tonic-gate 	char *string_array[] = {"Dev_node_prop:0",
1295*0Sstevel@tonic-gate 	    "Dev_node_prop:1", "Dev_node_prop:2", "Dev_node_prop:3"};
1296*0Sstevel@tonic-gate 	uchar_t byte_array[] = { (uchar_t)0xaa, (uchar_t)0x55,
1297*0Sstevel@tonic-gate 	    (uchar_t)0x12, (uchar_t)0xcd };
1298*0Sstevel@tonic-gate 	char bytes[] = { (char)0x00, (char)0xef, (char)0xff };
1299*0Sstevel@tonic-gate 
1300*0Sstevel@tonic-gate 	if (ddi_prop_update_int(DDI_DEV_T_NONE, devi, "int", int_val)
1301*0Sstevel@tonic-gate 	    != DDI_PROP_SUCCESS)
1302*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1303*0Sstevel@tonic-gate 
1304*0Sstevel@tonic-gate 	if (ddi_prop_update_int_array(DDI_DEV_T_NONE, devi, "int-array",
1305*0Sstevel@tonic-gate 	    int_array, sizeof (int_array) / sizeof (int)) != DDI_PROP_SUCCESS)
1306*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1307*0Sstevel@tonic-gate 
1308*0Sstevel@tonic-gate 	if (ddi_prop_update_int64(DDI_DEV_T_NONE, devi, "int64", int64_val)
1309*0Sstevel@tonic-gate 	    != DDI_PROP_SUCCESS)
1310*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1311*0Sstevel@tonic-gate 
1312*0Sstevel@tonic-gate 	if (ddi_prop_update_int64_array(DDI_DEV_T_NONE, devi, "int64-array",
1313*0Sstevel@tonic-gate 	    int64_array, sizeof (int64_array) / sizeof (int64_t))
1314*0Sstevel@tonic-gate 	    != DDI_PROP_SUCCESS)
1315*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1316*0Sstevel@tonic-gate 
1317*0Sstevel@tonic-gate 	if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "string", string_val)
1318*0Sstevel@tonic-gate 	    != DDI_PROP_SUCCESS)
1319*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1320*0Sstevel@tonic-gate 
1321*0Sstevel@tonic-gate 	if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi, "string-array",
1322*0Sstevel@tonic-gate 	    string_array, sizeof (string_array) / sizeof (char *))
1323*0Sstevel@tonic-gate 	    != DDI_PROP_SUCCESS)
1324*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1325*0Sstevel@tonic-gate 
1326*0Sstevel@tonic-gate 	if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
1327*0Sstevel@tonic-gate 	    "boolean", NULL, 0) != DDI_PROP_SUCCESS)
1328*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1329*0Sstevel@tonic-gate 
1330*0Sstevel@tonic-gate 	if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, devi, "byte-array",
1331*0Sstevel@tonic-gate 	    byte_array, sizeof (byte_array)) != DDI_PROP_SUCCESS)
1332*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1333*0Sstevel@tonic-gate 
1334*0Sstevel@tonic-gate 	/* untyped property */
1335*0Sstevel@tonic-gate 	if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP, "untyped",
1336*0Sstevel@tonic-gate 	    (caddr_t)bytes, sizeof (bytes)) != DDI_PROP_SUCCESS)
1337*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1338*0Sstevel@tonic-gate 
1339*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
1340*0Sstevel@tonic-gate }
1341*0Sstevel@tonic-gate 
1342*0Sstevel@tonic-gate static struct driver_minor_data {
1343*0Sstevel@tonic-gate 	char	*name;
1344*0Sstevel@tonic-gate 	minor_t	minor;
1345*0Sstevel@tonic-gate 	int	type;
1346*0Sstevel@tonic-gate } disk_minor_data[] = {
1347*0Sstevel@tonic-gate 	{"a", 0, S_IFBLK},
1348*0Sstevel@tonic-gate 	{"b", 1, S_IFBLK},
1349*0Sstevel@tonic-gate 	{"c", 2, S_IFBLK},
1350*0Sstevel@tonic-gate 	{"d", 3, S_IFBLK},
1351*0Sstevel@tonic-gate 	{"e", 4, S_IFBLK},
1352*0Sstevel@tonic-gate 	{"f", 5, S_IFBLK},
1353*0Sstevel@tonic-gate 	{"g", 6, S_IFBLK},
1354*0Sstevel@tonic-gate 	{"h", 7, S_IFBLK},
1355*0Sstevel@tonic-gate 	{"a,raw", 0, S_IFCHR},
1356*0Sstevel@tonic-gate 	{"b,raw", 1, S_IFCHR},
1357*0Sstevel@tonic-gate 	{"c,raw", 2, S_IFCHR},
1358*0Sstevel@tonic-gate 	{"d,raw", 3, S_IFCHR},
1359*0Sstevel@tonic-gate 	{"e,raw", 4, S_IFCHR},
1360*0Sstevel@tonic-gate 	{"f,raw", 5, S_IFCHR},
1361*0Sstevel@tonic-gate 	{"g,raw", 6, S_IFCHR},
1362*0Sstevel@tonic-gate 	{"h,raw", 7, S_IFCHR},
1363*0Sstevel@tonic-gate 	{0}
1364*0Sstevel@tonic-gate };
1365*0Sstevel@tonic-gate 
1366*0Sstevel@tonic-gate 
1367*0Sstevel@tonic-gate static struct driver_serial_minor_data {
1368*0Sstevel@tonic-gate 	char	*name;
1369*0Sstevel@tonic-gate 	minor_t minor;
1370*0Sstevel@tonic-gate 	int	type;
1371*0Sstevel@tonic-gate 	char	*node_type;
1372*0Sstevel@tonic-gate }  serial_minor_data[] = {
1373*0Sstevel@tonic-gate 	{"0", 0, S_IFCHR, "ddi_serial"},
1374*0Sstevel@tonic-gate 	{"1", 1, S_IFCHR, "ddi_serial"},
1375*0Sstevel@tonic-gate 	{"0,cu", 2, S_IFCHR, "ddi_serial:dialout"},
1376*0Sstevel@tonic-gate 	{"1,cu", 3, S_IFCHR, "ddi_serial:dialout"},
1377*0Sstevel@tonic-gate 	{0}
1378*0Sstevel@tonic-gate };
1379*0Sstevel@tonic-gate 
1380*0Sstevel@tonic-gate 
1381*0Sstevel@tonic-gate static int
1382*0Sstevel@tonic-gate gen_create_display(dev_info_t *devi)
1383*0Sstevel@tonic-gate {
1384*0Sstevel@tonic-gate 
1385*0Sstevel@tonic-gate 	int instance = ddi_get_instance(devi);
1386*0Sstevel@tonic-gate 	char minor_name[15];
1387*0Sstevel@tonic-gate 
1388*0Sstevel@tonic-gate 	(void) sprintf(minor_name, "cgtwenty%d", instance);
1389*0Sstevel@tonic-gate 
1390*0Sstevel@tonic-gate 	return (ddi_create_minor_node(devi, minor_name, S_IFCHR,
1391*0Sstevel@tonic-gate 	    INST_TO_MINOR(instance), DDI_NT_DISPLAY, NULL));
1392*0Sstevel@tonic-gate }
1393*0Sstevel@tonic-gate 
1394*0Sstevel@tonic-gate static int
1395*0Sstevel@tonic-gate gen_create_mn_disk_chan(dev_info_t *devi)
1396*0Sstevel@tonic-gate {
1397*0Sstevel@tonic-gate 	struct driver_minor_data *dmdp;
1398*0Sstevel@tonic-gate 	int instance = ddi_get_instance(devi);
1399*0Sstevel@tonic-gate 
1400*0Sstevel@tonic-gate 	if (gen_create_properties(devi) != DDI_SUCCESS)
1401*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1402*0Sstevel@tonic-gate 
1403*0Sstevel@tonic-gate 	for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
1404*0Sstevel@tonic-gate 		if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
1405*0Sstevel@tonic-gate 		    (INST_TO_MINOR(instance)) | dmdp->minor,
1406*0Sstevel@tonic-gate 		    DDI_NT_BLOCK_CHAN, NULL) != DDI_SUCCESS) {
1407*0Sstevel@tonic-gate 
1408*0Sstevel@tonic-gate 			return (DDI_FAILURE);
1409*0Sstevel@tonic-gate 		}
1410*0Sstevel@tonic-gate 	}
1411*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
1412*0Sstevel@tonic-gate }
1413*0Sstevel@tonic-gate 
1414*0Sstevel@tonic-gate static uint_t
1415*0Sstevel@tonic-gate atod(char *s)
1416*0Sstevel@tonic-gate {
1417*0Sstevel@tonic-gate 	uint_t val = 0;
1418*0Sstevel@tonic-gate 	uint_t digit;
1419*0Sstevel@tonic-gate 
1420*0Sstevel@tonic-gate 	while (*s) {
1421*0Sstevel@tonic-gate 		if (*s >= '0' && *s <= '9')
1422*0Sstevel@tonic-gate 			digit = *s++ - '0';
1423*0Sstevel@tonic-gate 		else
1424*0Sstevel@tonic-gate 			break;
1425*0Sstevel@tonic-gate 		val = (val * 10) + digit;
1426*0Sstevel@tonic-gate 	}
1427*0Sstevel@tonic-gate 	return (val);
1428*0Sstevel@tonic-gate }
1429*0Sstevel@tonic-gate 
1430*0Sstevel@tonic-gate 
1431*0Sstevel@tonic-gate static int
1432*0Sstevel@tonic-gate gen_create_mn_disk_wwn(dev_info_t *devi)
1433*0Sstevel@tonic-gate {
1434*0Sstevel@tonic-gate 	struct driver_minor_data *dmdp;
1435*0Sstevel@tonic-gate 	int instance = ddi_get_instance(devi);
1436*0Sstevel@tonic-gate 	char *address = ddi_get_name_addr(devi);
1437*0Sstevel@tonic-gate 	int target, lun;
1438*0Sstevel@tonic-gate 
1439*0Sstevel@tonic-gate 	if (address[0] >= '0' && address[0] <= '9' &&
1440*0Sstevel@tonic-gate 			strchr(address, ',')) {
1441*0Sstevel@tonic-gate 		target = atod(address);
1442*0Sstevel@tonic-gate 		address = strchr(address, ',');
1443*0Sstevel@tonic-gate 		lun = atod(++address);
1444*0Sstevel@tonic-gate 	} else { /* this hack is for rm_stale_link() testing */
1445*0Sstevel@tonic-gate 		target = 10;
1446*0Sstevel@tonic-gate 		lun = 5;
1447*0Sstevel@tonic-gate 	}
1448*0Sstevel@tonic-gate 
1449*0Sstevel@tonic-gate 	if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
1450*0Sstevel@tonic-gate 	    "target", (caddr_t)&target, sizeof (int))
1451*0Sstevel@tonic-gate 	    != DDI_PROP_SUCCESS) {
1452*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1453*0Sstevel@tonic-gate 	}
1454*0Sstevel@tonic-gate 	if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
1455*0Sstevel@tonic-gate 	    "lun", (caddr_t)&lun, sizeof (int))
1456*0Sstevel@tonic-gate 	    != DDI_PROP_SUCCESS) {
1457*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1458*0Sstevel@tonic-gate 	}
1459*0Sstevel@tonic-gate 
1460*0Sstevel@tonic-gate 	for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
1461*0Sstevel@tonic-gate 		if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
1462*0Sstevel@tonic-gate 		    (INST_TO_MINOR(instance)) | dmdp->minor,
1463*0Sstevel@tonic-gate 		    DDI_NT_BLOCK_WWN, NULL) != DDI_SUCCESS) {
1464*0Sstevel@tonic-gate 
1465*0Sstevel@tonic-gate 			return (DDI_FAILURE);
1466*0Sstevel@tonic-gate 		}
1467*0Sstevel@tonic-gate 	}
1468*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
1469*0Sstevel@tonic-gate }
1470*0Sstevel@tonic-gate 
1471*0Sstevel@tonic-gate static int
1472*0Sstevel@tonic-gate gen_create_mn_disk_cdrom(dev_info_t *devi)
1473*0Sstevel@tonic-gate {
1474*0Sstevel@tonic-gate 	struct driver_minor_data *dmdp;
1475*0Sstevel@tonic-gate 	int instance = ddi_get_instance(devi);
1476*0Sstevel@tonic-gate 
1477*0Sstevel@tonic-gate 	for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
1478*0Sstevel@tonic-gate 		if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
1479*0Sstevel@tonic-gate 		    (INST_TO_MINOR(instance)) | dmdp->minor,
1480*0Sstevel@tonic-gate 		    DDI_NT_CD_CHAN, NULL) != DDI_SUCCESS) {
1481*0Sstevel@tonic-gate 
1482*0Sstevel@tonic-gate 			return (DDI_FAILURE);
1483*0Sstevel@tonic-gate 		}
1484*0Sstevel@tonic-gate 	}
1485*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
1486*0Sstevel@tonic-gate }
1487*0Sstevel@tonic-gate 
1488*0Sstevel@tonic-gate static int
1489*0Sstevel@tonic-gate gen_create_mn_disk_fd(dev_info_t *devi)
1490*0Sstevel@tonic-gate {
1491*0Sstevel@tonic-gate 	struct driver_minor_data *dmdp;
1492*0Sstevel@tonic-gate 	int instance = ddi_get_instance(devi);
1493*0Sstevel@tonic-gate 
1494*0Sstevel@tonic-gate 	for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
1495*0Sstevel@tonic-gate 		if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
1496*0Sstevel@tonic-gate 		    (INST_TO_MINOR(instance)) | dmdp->minor,
1497*0Sstevel@tonic-gate 		    DDI_NT_BLOCK_CHAN, NULL) != DDI_SUCCESS) {
1498*0Sstevel@tonic-gate 
1499*0Sstevel@tonic-gate 			return (DDI_FAILURE);
1500*0Sstevel@tonic-gate 		}
1501*0Sstevel@tonic-gate 	}
1502*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
1503*0Sstevel@tonic-gate }
1504*0Sstevel@tonic-gate 
1505*0Sstevel@tonic-gate static int
1506*0Sstevel@tonic-gate gen_create_serial(dev_info_t *devi)
1507*0Sstevel@tonic-gate {
1508*0Sstevel@tonic-gate 	struct driver_serial_minor_data *dmdp;
1509*0Sstevel@tonic-gate 	int instance = ddi_get_instance(devi);
1510*0Sstevel@tonic-gate 
1511*0Sstevel@tonic-gate 	for (dmdp = serial_minor_data; dmdp->name != NULL; dmdp++) {
1512*0Sstevel@tonic-gate 		if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
1513*0Sstevel@tonic-gate 		    (INST_TO_MINOR(instance)) | dmdp->minor,
1514*0Sstevel@tonic-gate 		    dmdp->node_type, NULL) != DDI_SUCCESS) {
1515*0Sstevel@tonic-gate 
1516*0Sstevel@tonic-gate 			return (DDI_FAILURE);
1517*0Sstevel@tonic-gate 		}
1518*0Sstevel@tonic-gate 	}
1519*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
1520*0Sstevel@tonic-gate }
1521*0Sstevel@tonic-gate 
1522*0Sstevel@tonic-gate static int
1523*0Sstevel@tonic-gate gen_create_net(dev_info_t *devi)
1524*0Sstevel@tonic-gate {
1525*0Sstevel@tonic-gate 	int instance = ddi_get_instance(devi);
1526*0Sstevel@tonic-gate 	char minorname[32];
1527*0Sstevel@tonic-gate 
1528*0Sstevel@tonic-gate 	if (gen_create_properties(devi) != DDI_SUCCESS)
1529*0Sstevel@tonic-gate 		return (DDI_FAILURE);
1530*0Sstevel@tonic-gate 
1531*0Sstevel@tonic-gate 	(void) snprintf(minorname, sizeof (minorname), "gen_drv%d", instance);
1532*0Sstevel@tonic-gate 	return (ddi_create_minor_node(devi, minorname, S_IFCHR,
1533*0Sstevel@tonic-gate 	    INST_TO_MINOR(instance), DDI_NT_NET, 0));
1534*0Sstevel@tonic-gate }
1535*0Sstevel@tonic-gate 
1536*0Sstevel@tonic-gate static int
1537*0Sstevel@tonic-gate gen_create_minor_nodes(dev_info_t *devi, struct dstate *dstatep)
1538*0Sstevel@tonic-gate {
1539*0Sstevel@tonic-gate 	int rval = DDI_SUCCESS;
1540*0Sstevel@tonic-gate 	char *node_name;
1541*0Sstevel@tonic-gate 
1542*0Sstevel@tonic-gate 	node_name = ddi_node_name(devi);
1543*0Sstevel@tonic-gate 
1544*0Sstevel@tonic-gate 	if (strcmp(node_name, "disk_chan") == 0) {
1545*0Sstevel@tonic-gate 		rval = gen_create_mn_disk_chan(devi);
1546*0Sstevel@tonic-gate 	} else if (strcmp(node_name, "disk_wwn") == 0) {
1547*0Sstevel@tonic-gate 		rval = gen_create_mn_disk_wwn(devi);
1548*0Sstevel@tonic-gate 	} else if (strcmp(node_name, "disk_cdrom") == 0) {
1549*0Sstevel@tonic-gate 		rval = gen_create_mn_disk_cdrom(devi);
1550*0Sstevel@tonic-gate 	} else if (strcmp(node_name, "disk_fd") == 0) {
1551*0Sstevel@tonic-gate 		rval = gen_create_mn_disk_fd(devi);
1552*0Sstevel@tonic-gate 	} else if (strcmp(node_name, "cgtwenty") == 0) {
1553*0Sstevel@tonic-gate 		rval = gen_create_display(devi);
1554*0Sstevel@tonic-gate 	} else if (strcmp(node_name, "genzs") == 0) {
1555*0Sstevel@tonic-gate 		rval = gen_create_serial(devi);
1556*0Sstevel@tonic-gate 	} else if (strcmp(node_name, "net") == 0) {
1557*0Sstevel@tonic-gate 		rval = gen_create_net(devi);
1558*0Sstevel@tonic-gate 	} else {
1559*0Sstevel@tonic-gate 		int instance = ddi_get_instance(devi);
1560*0Sstevel@tonic-gate 		char *node_type;
1561*0Sstevel@tonic-gate 
1562*0Sstevel@tonic-gate 		/*
1563*0Sstevel@tonic-gate 		 * Solaris may directly hang the node_type off the minor node
1564*0Sstevel@tonic-gate 		 * (without making a copy).  Since we free the node_type
1565*0Sstevel@tonic-gate 		 * property below we need to make a private copy to pass
1566*0Sstevel@tonic-gate 		 * to ddi_create_minor_node to avoid devinfo snapshot panics.
1567*0Sstevel@tonic-gate 		 * We store a pointer to our copy in dstate and free it in
1568*0Sstevel@tonic-gate 		 * gen_detach after the minor nodes have been deleted by
1569*0Sstevel@tonic-gate 		 * ddi_remove_minor_node.
1570*0Sstevel@tonic-gate 		 */
1571*0Sstevel@tonic-gate 		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, devi,
1572*0Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "node-type", &node_type) != 0) {
1573*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "couldn't get node-type\n");
1574*0Sstevel@tonic-gate 			return (DDI_FAILURE);
1575*0Sstevel@tonic-gate 		}
1576*0Sstevel@tonic-gate 		if (node_type) {
1577*0Sstevel@tonic-gate 			dstatep->node_type = kmem_alloc(
1578*0Sstevel@tonic-gate 			    strlen(node_type) + 1, KM_SLEEP);
1579*0Sstevel@tonic-gate 			(void) strcpy(dstatep->node_type, node_type);
1580*0Sstevel@tonic-gate 		}
1581*0Sstevel@tonic-gate 		ddi_prop_free(node_type);
1582*0Sstevel@tonic-gate 
1583*0Sstevel@tonic-gate 		/* the minor name is the same as the node name */
1584*0Sstevel@tonic-gate 		if (ddi_create_minor_node(devi, node_name, S_IFCHR,
1585*0Sstevel@tonic-gate 		    (INST_TO_MINOR(instance)), dstatep->node_type, NULL) !=
1586*0Sstevel@tonic-gate 		    DDI_SUCCESS) {
1587*0Sstevel@tonic-gate 			if (dstatep->node_type) {
1588*0Sstevel@tonic-gate 				kmem_free(dstatep->node_type,
1589*0Sstevel@tonic-gate 				    strlen(dstatep->node_type) + 1);
1590*0Sstevel@tonic-gate 				dstatep->node_type = NULL;
1591*0Sstevel@tonic-gate 			}
1592*0Sstevel@tonic-gate 			return (DDI_FAILURE);
1593*0Sstevel@tonic-gate 		}
1594*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
1595*0Sstevel@tonic-gate 	}
1596*0Sstevel@tonic-gate 
1597*0Sstevel@tonic-gate 	if (rval != DDI_SUCCESS) {
1598*0Sstevel@tonic-gate 		ddi_prop_remove_all(devi);
1599*0Sstevel@tonic-gate 		ddi_remove_minor_node(devi, NULL);
1600*0Sstevel@tonic-gate 	}
1601*0Sstevel@tonic-gate 
1602*0Sstevel@tonic-gate 	return (rval);
1603*0Sstevel@tonic-gate }
1604*0Sstevel@tonic-gate 
1605*0Sstevel@tonic-gate /*ARGSUSED*/
1606*0Sstevel@tonic-gate static void
1607*0Sstevel@tonic-gate gen_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie, void *arg,
1608*0Sstevel@tonic-gate     void *impl_data)
1609*0Sstevel@tonic-gate {
1610*0Sstevel@tonic-gate 	if (gen_debug)
1611*0Sstevel@tonic-gate 		cmn_err(CE_NOTE, "gen_event_cb invoked");
1612*0Sstevel@tonic-gate 
1613*0Sstevel@tonic-gate }
1614