1b72d5b75SMichael Corcoran /*
2b72d5b75SMichael Corcoran  * CDDL HEADER START
3b72d5b75SMichael Corcoran  *
4b72d5b75SMichael Corcoran  * The contents of this file are subject to the terms of the
5b72d5b75SMichael Corcoran  * Common Development and Distribution License (the "License").
6b72d5b75SMichael Corcoran  * You may not use this file except in compliance with the License.
7b72d5b75SMichael Corcoran  *
8b72d5b75SMichael Corcoran  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9b72d5b75SMichael Corcoran  * or http://www.opensolaris.org/os/licensing.
10b72d5b75SMichael Corcoran  * See the License for the specific language governing permissions
11b72d5b75SMichael Corcoran  * and limitations under the License.
12b72d5b75SMichael Corcoran  *
13b72d5b75SMichael Corcoran  * When distributing Covered Code, include this CDDL HEADER in each
14b72d5b75SMichael Corcoran  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15b72d5b75SMichael Corcoran  * If applicable, add the following below this CDDL HEADER, with the
16b72d5b75SMichael Corcoran  * fields enclosed by brackets "[]" replaced with your own identifying
17b72d5b75SMichael Corcoran  * information: Portions Copyright [yyyy] [name of copyright owner]
18b72d5b75SMichael Corcoran  *
19b72d5b75SMichael Corcoran  * CDDL HEADER END
20b72d5b75SMichael Corcoran  */
21b72d5b75SMichael Corcoran 
22b72d5b75SMichael Corcoran /*
23b72d5b75SMichael Corcoran  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24b72d5b75SMichael Corcoran  * Use is subject to license terms.
25b72d5b75SMichael Corcoran  */
26b72d5b75SMichael Corcoran /*
27*a3114836SGerry Liu  * Copyright (c) 2009-2010, Intel Corporation.
28b72d5b75SMichael Corcoran  * All rights reserved.
29b72d5b75SMichael Corcoran  */
30b72d5b75SMichael Corcoran /*
31b72d5b75SMichael Corcoran  * This module implements a nexus driver for the ACPI virtual bus.
32b72d5b75SMichael Corcoran  * It does not handle any of the DDI functions passed up to it by the child
33b72d5b75SMichael Corcoran  * drivers, but instead allows them to bubble up to the root node.
34b72d5b75SMichael Corcoran  */
35b72d5b75SMichael Corcoran 
36b72d5b75SMichael Corcoran #include <sys/types.h>
37b72d5b75SMichael Corcoran #include <sys/cmn_err.h>
38b72d5b75SMichael Corcoran #include <sys/conf.h>
39b72d5b75SMichael Corcoran #include <sys/modctl.h>
40b72d5b75SMichael Corcoran #include <sys/ddi.h>
41b72d5b75SMichael Corcoran #include <sys/ddi_impldefs.h>
42b72d5b75SMichael Corcoran #include <sys/ddifm.h>
43*a3114836SGerry Liu #include <sys/note.h>
44b72d5b75SMichael Corcoran #include <sys/ndifm.h>
45b72d5b75SMichael Corcoran #include <sys/sunddi.h>
46b72d5b75SMichael Corcoran #include <sys/sunndi.h>
47b72d5b75SMichael Corcoran #include <sys/acpidev.h>
48b72d5b75SMichael Corcoran #include <sys/acpinex.h>
49b72d5b75SMichael Corcoran 
50b72d5b75SMichael Corcoran /* Patchable through /etc/system. */
51b72d5b75SMichael Corcoran #ifdef	DEBUG
52b72d5b75SMichael Corcoran int acpinex_debug = 1;
53b72d5b75SMichael Corcoran #else
54b72d5b75SMichael Corcoran int acpinex_debug = 0;
55b72d5b75SMichael Corcoran #endif
56b72d5b75SMichael Corcoran 
57b72d5b75SMichael Corcoran /*
58b72d5b75SMichael Corcoran  * Driver globals
59b72d5b75SMichael Corcoran  */
60b72d5b75SMichael Corcoran static kmutex_t acpinex_lock;
61b72d5b75SMichael Corcoran static void *acpinex_softstates;
62b72d5b75SMichael Corcoran 
63b72d5b75SMichael Corcoran static int acpinex_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
64b72d5b75SMichael Corcoran static int acpinex_attach(dev_info_t *, ddi_attach_cmd_t);
65b72d5b75SMichael Corcoran static int acpinex_detach(dev_info_t *, ddi_detach_cmd_t);
66b72d5b75SMichael Corcoran static int acpinex_open(dev_t *, int, int, cred_t *);
67b72d5b75SMichael Corcoran static int acpinex_close(dev_t, int, int, cred_t *);
68b72d5b75SMichael Corcoran static int acpinex_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
69b72d5b75SMichael Corcoran static int acpinex_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
70b72d5b75SMichael Corcoran     off_t offset, off_t len, caddr_t *vaddrp);
71b72d5b75SMichael Corcoran static int acpinex_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
72b72d5b75SMichael Corcoran     void *);
73b72d5b75SMichael Corcoran static int acpinex_fm_init_child(dev_info_t *, dev_info_t *, int,
74b72d5b75SMichael Corcoran     ddi_iblock_cookie_t *);
75b72d5b75SMichael Corcoran static void acpinex_fm_init(acpinex_softstate_t *softsp);
76b72d5b75SMichael Corcoran static void acpinex_fm_fini(acpinex_softstate_t *softsp);
77b72d5b75SMichael Corcoran 
78b72d5b75SMichael Corcoran extern void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **);
79b72d5b75SMichael Corcoran 
80b72d5b75SMichael Corcoran /*
81b72d5b75SMichael Corcoran  * Configuration data structures
82b72d5b75SMichael Corcoran  */
83b72d5b75SMichael Corcoran static struct bus_ops acpinex_bus_ops = {
84b72d5b75SMichael Corcoran 	BUSO_REV,			/* busops_rev */
85b72d5b75SMichael Corcoran 	acpinex_bus_map,		/* bus_map */
86b72d5b75SMichael Corcoran 	NULL,				/* bus_get_intrspec */
87b72d5b75SMichael Corcoran 	NULL,				/* bus_add_intrspec */
88b72d5b75SMichael Corcoran 	NULL,				/* bus_remove_intrspec */
89b72d5b75SMichael Corcoran 	i_ddi_map_fault,		/* bus_map_fault */
90b72d5b75SMichael Corcoran 	ddi_dma_map,			/* bus_dma_map */
91b72d5b75SMichael Corcoran 	ddi_dma_allochdl,		/* bus_dma_allochdl */
92b72d5b75SMichael Corcoran 	ddi_dma_freehdl,		/* bus_dma_freehdl */
93b72d5b75SMichael Corcoran 	ddi_dma_bindhdl,		/* bus_dma_bindhdl */
94b72d5b75SMichael Corcoran 	ddi_dma_unbindhdl,		/* bus_dma_unbindhdl */
95b72d5b75SMichael Corcoran 	ddi_dma_flush,			/* bus_dma_flush */
96b72d5b75SMichael Corcoran 	ddi_dma_win,			/* bus_dma_win */
97b72d5b75SMichael Corcoran 	ddi_dma_mctl,			/* bus_dma_ctl */
98b72d5b75SMichael Corcoran 	acpinex_ctlops,			/* bus_ctl */
99b72d5b75SMichael Corcoran 	ddi_bus_prop_op,		/* bus_prop_op */
100b72d5b75SMichael Corcoran 	ndi_busop_get_eventcookie,	/* bus_get_eventcookie */
101b72d5b75SMichael Corcoran 	ndi_busop_add_eventcall,	/* bus_add_eventcall */
102b72d5b75SMichael Corcoran 	ndi_busop_remove_eventcall,	/* bus_remove_eventcall */
103b72d5b75SMichael Corcoran 	ndi_post_event,			/* bus_post_event */
104b72d5b75SMichael Corcoran 	NULL,				/* bus_intr_ctl */
105b72d5b75SMichael Corcoran 	NULL,				/* bus_config */
106b72d5b75SMichael Corcoran 	NULL,				/* bus_unconfig */
107b72d5b75SMichael Corcoran 	acpinex_fm_init_child,		/* bus_fm_init */
108b72d5b75SMichael Corcoran 	NULL,				/* bus_fm_fini */
109b72d5b75SMichael Corcoran 	NULL,				/* bus_fm_access_enter */
110b72d5b75SMichael Corcoran 	NULL,				/* bus_fm_access_exit */
111b72d5b75SMichael Corcoran 	NULL,				/* bus_power */
112b72d5b75SMichael Corcoran 	i_ddi_intr_ops			/* bus_intr_op */
113b72d5b75SMichael Corcoran };
114b72d5b75SMichael Corcoran 
115b72d5b75SMichael Corcoran static struct cb_ops acpinex_cb_ops = {
116b72d5b75SMichael Corcoran 	acpinex_open,			/* cb_open */
117b72d5b75SMichael Corcoran 	acpinex_close,			/* cb_close */
118b72d5b75SMichael Corcoran 	nodev,				/* cb_strategy */
119b72d5b75SMichael Corcoran 	nodev,				/* cb_print */
120b72d5b75SMichael Corcoran 	nodev,				/* cb_dump */
121b72d5b75SMichael Corcoran 	nodev,				/* cb_read */
122b72d5b75SMichael Corcoran 	nodev,				/* cb_write */
123b72d5b75SMichael Corcoran 	acpinex_ioctl,			/* cb_ioctl */
124b72d5b75SMichael Corcoran 	nodev,				/* cb_devmap */
125b72d5b75SMichael Corcoran 	nodev,				/* cb_mmap */
126b72d5b75SMichael Corcoran 	nodev,				/* cb_segmap */
127b72d5b75SMichael Corcoran 	nochpoll,			/* cb_poll */
128b72d5b75SMichael Corcoran 	ddi_prop_op,			/* cb_prop_op */
129b72d5b75SMichael Corcoran 	NULL,				/* cb_str */
130b72d5b75SMichael Corcoran 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
131b72d5b75SMichael Corcoran 	CB_REV,				/* rev */
132b72d5b75SMichael Corcoran 	nodev,				/* int (*cb_aread)() */
133b72d5b75SMichael Corcoran 	nodev				/* int (*cb_awrite)() */
134b72d5b75SMichael Corcoran };
135b72d5b75SMichael Corcoran 
136b72d5b75SMichael Corcoran static struct dev_ops acpinex_ops = {
137b72d5b75SMichael Corcoran 	DEVO_REV,			/* devo_rev, */
138b72d5b75SMichael Corcoran 	0,				/* devo_refcnt */
139b72d5b75SMichael Corcoran 	acpinex_info,			/* devo_getinfo */
140b72d5b75SMichael Corcoran 	nulldev,			/* devo_identify */
141b72d5b75SMichael Corcoran 	nulldev,			/* devo_probe */
142b72d5b75SMichael Corcoran 	acpinex_attach,			/* devo_attach */
143b72d5b75SMichael Corcoran 	acpinex_detach,			/* devo_detach */
144b72d5b75SMichael Corcoran 	nulldev,			/* devo_reset */
145b72d5b75SMichael Corcoran 	&acpinex_cb_ops,		/* devo_cb_ops */
146b72d5b75SMichael Corcoran 	&acpinex_bus_ops,		/* devo_bus_ops */
147b72d5b75SMichael Corcoran 	nulldev,			/* devo_power */
148b72d5b75SMichael Corcoran 	ddi_quiesce_not_needed		/* devo_quiesce */
149b72d5b75SMichael Corcoran };
150b72d5b75SMichael Corcoran 
151b72d5b75SMichael Corcoran static struct modldrv modldrv = {
152b72d5b75SMichael Corcoran 	&mod_driverops,			/* Type of module */
153b72d5b75SMichael Corcoran 	"ACPI virtual bus driver",	/* name of module */
154b72d5b75SMichael Corcoran 	&acpinex_ops,			/* driver ops */
155b72d5b75SMichael Corcoran };
156b72d5b75SMichael Corcoran 
157b72d5b75SMichael Corcoran static struct modlinkage modlinkage = {
158b72d5b75SMichael Corcoran 	MODREV_1,			/* rev */
159b72d5b75SMichael Corcoran 	(void *)&modldrv,
160b72d5b75SMichael Corcoran 	NULL
161b72d5b75SMichael Corcoran };
162b72d5b75SMichael Corcoran 
163b72d5b75SMichael Corcoran /*
164b72d5b75SMichael Corcoran  * Module initialization routines.
165b72d5b75SMichael Corcoran  */
166b72d5b75SMichael Corcoran int
167b72d5b75SMichael Corcoran _init(void)
168b72d5b75SMichael Corcoran {
169b72d5b75SMichael Corcoran 	int error;
170b72d5b75SMichael Corcoran 
171b72d5b75SMichael Corcoran 	/* Initialize soft state pointer. */
172b72d5b75SMichael Corcoran 	if ((error = ddi_soft_state_init(&acpinex_softstates,
173b72d5b75SMichael Corcoran 	    sizeof (acpinex_softstate_t), 8)) != 0) {
174b72d5b75SMichael Corcoran 		cmn_err(CE_WARN,
175b72d5b75SMichael Corcoran 		    "acpinex: failed to initialize soft state structure.");
176b72d5b75SMichael Corcoran 		return (error);
177b72d5b75SMichael Corcoran 	}
178b72d5b75SMichael Corcoran 
179*a3114836SGerry Liu 	/* Initialize event subsystem. */
180*a3114836SGerry Liu 	acpinex_event_init();
181*a3114836SGerry Liu 
182b72d5b75SMichael Corcoran 	/* Install the module. */
183b72d5b75SMichael Corcoran 	if ((error = mod_install(&modlinkage)) != 0) {
184b72d5b75SMichael Corcoran 		cmn_err(CE_WARN, "acpinex: failed to install module.");
185b72d5b75SMichael Corcoran 		ddi_soft_state_fini(&acpinex_softstates);
186b72d5b75SMichael Corcoran 		return (error);
187b72d5b75SMichael Corcoran 	}
188b72d5b75SMichael Corcoran 
189b72d5b75SMichael Corcoran 	mutex_init(&acpinex_lock, NULL, MUTEX_DRIVER, NULL);
190b72d5b75SMichael Corcoran 
191b72d5b75SMichael Corcoran 	return (0);
192b72d5b75SMichael Corcoran }
193b72d5b75SMichael Corcoran 
194b72d5b75SMichael Corcoran int
195b72d5b75SMichael Corcoran _fini(void)
196b72d5b75SMichael Corcoran {
197b72d5b75SMichael Corcoran 	int error;
198b72d5b75SMichael Corcoran 
199b72d5b75SMichael Corcoran 	/* Remove the module. */
200b72d5b75SMichael Corcoran 	if ((error = mod_remove(&modlinkage)) != 0) {
201b72d5b75SMichael Corcoran 		return (error);
202b72d5b75SMichael Corcoran 	}
203b72d5b75SMichael Corcoran 
204*a3114836SGerry Liu 	/* Shut down event subsystem. */
205*a3114836SGerry Liu 	acpinex_event_fini();
206*a3114836SGerry Liu 
207b72d5b75SMichael Corcoran 	/* Free the soft state info. */
208b72d5b75SMichael Corcoran 	ddi_soft_state_fini(&acpinex_softstates);
209b72d5b75SMichael Corcoran 
210b72d5b75SMichael Corcoran 	mutex_destroy(&acpinex_lock);
211b72d5b75SMichael Corcoran 
212b72d5b75SMichael Corcoran 	return (0);
213b72d5b75SMichael Corcoran }
214b72d5b75SMichael Corcoran 
215b72d5b75SMichael Corcoran int
216b72d5b75SMichael Corcoran _info(struct modinfo *modinfop)
217b72d5b75SMichael Corcoran {
218b72d5b75SMichael Corcoran 	return (mod_info(&modlinkage, modinfop));
219b72d5b75SMichael Corcoran }
220b72d5b75SMichael Corcoran 
221b72d5b75SMichael Corcoran static int
222b72d5b75SMichael Corcoran acpinex_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
223b72d5b75SMichael Corcoran {
224*a3114836SGerry Liu 	_NOTE(ARGUNUSED(dip));
225*a3114836SGerry Liu 
226b72d5b75SMichael Corcoran 	dev_t	dev;
227b72d5b75SMichael Corcoran 	int	instance;
228b72d5b75SMichael Corcoran 
229b72d5b75SMichael Corcoran 	if (infocmd == DDI_INFO_DEVT2INSTANCE) {
230b72d5b75SMichael Corcoran 		dev = (dev_t)arg;
231b72d5b75SMichael Corcoran 		instance = ACPINEX_GET_INSTANCE(getminor(dev));
232b72d5b75SMichael Corcoran 		*result = (void *)(uintptr_t)instance;
233b72d5b75SMichael Corcoran 		return (DDI_SUCCESS);
234b72d5b75SMichael Corcoran 	}
235b72d5b75SMichael Corcoran 
236b72d5b75SMichael Corcoran 	return (DDI_FAILURE);
237b72d5b75SMichael Corcoran }
238b72d5b75SMichael Corcoran 
239b72d5b75SMichael Corcoran static int
240b72d5b75SMichael Corcoran acpinex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
241b72d5b75SMichael Corcoran {
242b72d5b75SMichael Corcoran 	int instance;
243b72d5b75SMichael Corcoran 	acpinex_softstate_t *softsp;
244b72d5b75SMichael Corcoran 
245b72d5b75SMichael Corcoran 	switch (cmd) {
246b72d5b75SMichael Corcoran 	case DDI_ATTACH:
247b72d5b75SMichael Corcoran 		break;
248b72d5b75SMichael Corcoran 
249b72d5b75SMichael Corcoran 	case DDI_RESUME:
250b72d5b75SMichael Corcoran 		return (DDI_SUCCESS);
251b72d5b75SMichael Corcoran 
252b72d5b75SMichael Corcoran 	default:
253b72d5b75SMichael Corcoran 		return (DDI_FAILURE);
254b72d5b75SMichael Corcoran 	}
255b72d5b75SMichael Corcoran 
256b72d5b75SMichael Corcoran 	/* Get and check instance number. */
257b72d5b75SMichael Corcoran 	instance = ddi_get_instance(devi);
258b72d5b75SMichael Corcoran 	if (instance >= ACPINEX_INSTANCE_MAX) {
259b72d5b75SMichael Corcoran 		cmn_err(CE_WARN, "acpinex: instance number %d is out of range "
260b72d5b75SMichael Corcoran 		    "in acpinex_attach(), max %d.",
261b72d5b75SMichael Corcoran 		    instance, ACPINEX_INSTANCE_MAX - 1);
262b72d5b75SMichael Corcoran 		return (DDI_FAILURE);
263b72d5b75SMichael Corcoran 	}
264b72d5b75SMichael Corcoran 
265b72d5b75SMichael Corcoran 	/* Get soft state structure. */
266b72d5b75SMichael Corcoran 	if (ddi_soft_state_zalloc(acpinex_softstates, instance)
267b72d5b75SMichael Corcoran 	    != DDI_SUCCESS) {
268b72d5b75SMichael Corcoran 		cmn_err(CE_WARN, "!acpinex: failed to allocate soft state "
269b72d5b75SMichael Corcoran 		    "object in acpinex_attach().");
270b72d5b75SMichael Corcoran 		return (DDI_FAILURE);
271b72d5b75SMichael Corcoran 	}
272b72d5b75SMichael Corcoran 	softsp = ddi_get_soft_state(acpinex_softstates, instance);
273b72d5b75SMichael Corcoran 
274b72d5b75SMichael Corcoran 	/* Initialize soft state structure */
275b72d5b75SMichael Corcoran 	softsp->ans_dip = devi;
276b72d5b75SMichael Corcoran 	(void) ddi_pathname(devi, softsp->ans_path);
277b72d5b75SMichael Corcoran 	if (ACPI_FAILURE(acpica_get_handle(devi, &softsp->ans_hdl))) {
278b72d5b75SMichael Corcoran 		ACPINEX_DEBUG(CE_WARN,
279*a3114836SGerry Liu 		    "!acpinex: failed to get ACPI handle for %s.",
280b72d5b75SMichael Corcoran 		    softsp->ans_path);
281b72d5b75SMichael Corcoran 		ddi_soft_state_free(acpinex_softstates, instance);
282b72d5b75SMichael Corcoran 		return (DDI_FAILURE);
283b72d5b75SMichael Corcoran 	}
284b72d5b75SMichael Corcoran 	mutex_init(&softsp->ans_lock, NULL, MUTEX_DRIVER, NULL);
285b72d5b75SMichael Corcoran 
286*a3114836SGerry Liu 	/* Install event handler for child/descendant objects. */
287*a3114836SGerry Liu 	if (acpinex_event_scan(softsp, B_TRUE) != DDI_SUCCESS) {
288*a3114836SGerry Liu 		cmn_err(CE_WARN, "!acpinex: failed to install event handler "
289*a3114836SGerry Liu 		    "for children of %s.", softsp->ans_path);
290*a3114836SGerry Liu 	}
291*a3114836SGerry Liu 
292b72d5b75SMichael Corcoran 	/* nothing to suspend/resume here */
293b72d5b75SMichael Corcoran 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
294b72d5b75SMichael Corcoran 	    "pm-hardware-state", "no-suspend-resume");
295*a3114836SGerry Liu 	(void) ddi_prop_update_int(DDI_DEV_T_NONE, devi,
296*a3114836SGerry Liu 	    DDI_NO_AUTODETACH, 1);
297b72d5b75SMichael Corcoran 
298b72d5b75SMichael Corcoran 	acpinex_fm_init(softsp);
299b72d5b75SMichael Corcoran 	ddi_report_dev(devi);
300b72d5b75SMichael Corcoran 
301b72d5b75SMichael Corcoran 	return (DDI_SUCCESS);
302b72d5b75SMichael Corcoran }
303b72d5b75SMichael Corcoran 
304b72d5b75SMichael Corcoran static int
305b72d5b75SMichael Corcoran acpinex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
306b72d5b75SMichael Corcoran {
307b72d5b75SMichael Corcoran 	int instance;
308b72d5b75SMichael Corcoran 	acpinex_softstate_t *softsp;
309b72d5b75SMichael Corcoran 
310b72d5b75SMichael Corcoran 	instance = ddi_get_instance(devi);
311b72d5b75SMichael Corcoran 	if (instance >= ACPINEX_INSTANCE_MAX) {
312b72d5b75SMichael Corcoran 		cmn_err(CE_WARN, "acpinex: instance number %d is out of range "
313b72d5b75SMichael Corcoran 		    "in acpinex_detach(), max %d.",
314b72d5b75SMichael Corcoran 		    instance, ACPINEX_INSTANCE_MAX - 1);
315b72d5b75SMichael Corcoran 		return (DDI_FAILURE);
316b72d5b75SMichael Corcoran 	}
317b72d5b75SMichael Corcoran 
318b72d5b75SMichael Corcoran 	softsp = ddi_get_soft_state(acpinex_softstates, instance);
319b72d5b75SMichael Corcoran 	if (softsp == NULL) {
320*a3114836SGerry Liu 		ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to get soft state "
321b72d5b75SMichael Corcoran 		    "object for instance %d in acpinex_detach()", instance);
322b72d5b75SMichael Corcoran 		return (DDI_FAILURE);
323b72d5b75SMichael Corcoran 	}
324b72d5b75SMichael Corcoran 
325b72d5b75SMichael Corcoran 	switch (cmd) {
326b72d5b75SMichael Corcoran 	case DDI_DETACH:
327*a3114836SGerry Liu 		if (acpinex_event_scan(softsp, B_FALSE) != DDI_SUCCESS) {
328*a3114836SGerry Liu 			cmn_err(CE_WARN, "!acpinex: failed to uninstall event "
329*a3114836SGerry Liu 			    "handler for children of %s.", softsp->ans_path);
330*a3114836SGerry Liu 			return (DDI_FAILURE);
331*a3114836SGerry Liu 		}
332b72d5b75SMichael Corcoran 		ddi_remove_minor_node(devi, NULL);
333b72d5b75SMichael Corcoran 		acpinex_fm_fini(softsp);
334b72d5b75SMichael Corcoran 		mutex_destroy(&softsp->ans_lock);
335b72d5b75SMichael Corcoran 		ddi_soft_state_free(acpinex_softstates, instance);
336*a3114836SGerry Liu 		(void) ddi_prop_update_int(DDI_DEV_T_NONE, devi,
337*a3114836SGerry Liu 		    DDI_NO_AUTODETACH, 0);
338b72d5b75SMichael Corcoran 		return (DDI_SUCCESS);
339b72d5b75SMichael Corcoran 
340b72d5b75SMichael Corcoran 	case DDI_SUSPEND:
341b72d5b75SMichael Corcoran 		return (DDI_SUCCESS);
342b72d5b75SMichael Corcoran 
343b72d5b75SMichael Corcoran 	default:
344b72d5b75SMichael Corcoran 		return (DDI_FAILURE);
345b72d5b75SMichael Corcoran 	}
346b72d5b75SMichael Corcoran }
347b72d5b75SMichael Corcoran 
348b72d5b75SMichael Corcoran static int
349b72d5b75SMichael Corcoran name_child(dev_info_t *child, char *name, int namelen)
350b72d5b75SMichael Corcoran {
351b72d5b75SMichael Corcoran 	char *unitaddr;
352b72d5b75SMichael Corcoran 
353b72d5b75SMichael Corcoran 	ddi_set_parent_data(child, NULL);
354b72d5b75SMichael Corcoran 
355b72d5b75SMichael Corcoran 	name[0] = '\0';
356b72d5b75SMichael Corcoran 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
357b72d5b75SMichael Corcoran 	    ACPIDEV_PROP_NAME_UNIT_ADDR, &unitaddr) == DDI_SUCCESS) {
358*a3114836SGerry Liu 		(void) strlcpy(name, unitaddr, namelen);
359b72d5b75SMichael Corcoran 		ddi_prop_free(unitaddr);
360b72d5b75SMichael Corcoran 	} else {
361*a3114836SGerry Liu 		ACPINEX_DEBUG(CE_NOTE, "!acpinex: failed to lookup child "
362*a3114836SGerry Liu 		    "unit-address prop for %p.", (void *)child);
363b72d5b75SMichael Corcoran 	}
364b72d5b75SMichael Corcoran 
365b72d5b75SMichael Corcoran 	return (DDI_SUCCESS);
366b72d5b75SMichael Corcoran }
367b72d5b75SMichael Corcoran 
368b72d5b75SMichael Corcoran static int
369b72d5b75SMichael Corcoran init_child(dev_info_t *child)
370b72d5b75SMichael Corcoran {
371b72d5b75SMichael Corcoran 	char name[MAXNAMELEN];
372b72d5b75SMichael Corcoran 
373b72d5b75SMichael Corcoran 	(void) name_child(child, name, MAXNAMELEN);
374b72d5b75SMichael Corcoran 	ddi_set_name_addr(child, name);
375b72d5b75SMichael Corcoran 	if ((ndi_dev_is_persistent_node(child) == 0) &&
376b72d5b75SMichael Corcoran 	    (ndi_merge_node(child, name_child) == DDI_SUCCESS)) {
377b72d5b75SMichael Corcoran 		impl_ddi_sunbus_removechild(child);
378b72d5b75SMichael Corcoran 		return (DDI_FAILURE);
379b72d5b75SMichael Corcoran 	}
380b72d5b75SMichael Corcoran 
381b72d5b75SMichael Corcoran 	return (DDI_SUCCESS);
382b72d5b75SMichael Corcoran }
383b72d5b75SMichael Corcoran 
384b72d5b75SMichael Corcoran /*
385b72d5b75SMichael Corcoran  * Control ops entry point:
386b72d5b75SMichael Corcoran  *
387b72d5b75SMichael Corcoran  * Requests handled completely:
388b72d5b75SMichael Corcoran  *      DDI_CTLOPS_INITCHILD
389b72d5b75SMichael Corcoran  *      DDI_CTLOPS_UNINITCHILD
390b72d5b75SMichael Corcoran  * All others are passed to the parent.
391b72d5b75SMichael Corcoran  */
392b72d5b75SMichael Corcoran static int
393b72d5b75SMichael Corcoran acpinex_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, void *arg,
394b72d5b75SMichael Corcoran     void *result)
395b72d5b75SMichael Corcoran {
396b72d5b75SMichael Corcoran 	int rval = DDI_SUCCESS;
397b72d5b75SMichael Corcoran 
398b72d5b75SMichael Corcoran 	switch (op) {
399b72d5b75SMichael Corcoran 	case DDI_CTLOPS_INITCHILD:
400b72d5b75SMichael Corcoran 		rval = init_child((dev_info_t *)arg);
401b72d5b75SMichael Corcoran 		break;
402b72d5b75SMichael Corcoran 
403b72d5b75SMichael Corcoran 	case DDI_CTLOPS_UNINITCHILD:
404b72d5b75SMichael Corcoran 		impl_ddi_sunbus_removechild((dev_info_t *)arg);
405b72d5b75SMichael Corcoran 		break;
406b72d5b75SMichael Corcoran 
407b72d5b75SMichael Corcoran 	case DDI_CTLOPS_REPORTDEV: {
408b72d5b75SMichael Corcoran 		if (rdip == (dev_info_t *)0)
409b72d5b75SMichael Corcoran 			return (DDI_FAILURE);
410b72d5b75SMichael Corcoran 		cmn_err(CE_CONT, "?acpinex: %s@%s, %s%d\n",
411b72d5b75SMichael Corcoran 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
412b72d5b75SMichael Corcoran 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
413b72d5b75SMichael Corcoran 		break;
414b72d5b75SMichael Corcoran 	}
415b72d5b75SMichael Corcoran 
416b72d5b75SMichael Corcoran 	default:
417b72d5b75SMichael Corcoran 		rval = ddi_ctlops(dip, rdip, op, arg, result);
418b72d5b75SMichael Corcoran 		break;
419b72d5b75SMichael Corcoran 	}
420b72d5b75SMichael Corcoran 
421b72d5b75SMichael Corcoran 	return (rval);
422b72d5b75SMichael Corcoran }
423b72d5b75SMichael Corcoran 
424b72d5b75SMichael Corcoran /* ARGSUSED */
425b72d5b75SMichael Corcoran static int
426b72d5b75SMichael Corcoran acpinex_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
427b72d5b75SMichael Corcoran     off_t offset, off_t len, caddr_t *vaddrp)
428b72d5b75SMichael Corcoran {
429b72d5b75SMichael Corcoran 	ACPINEX_DEBUG(CE_WARN,
430*a3114836SGerry Liu 	    "!acpinex: acpinex_bus_map called and it's unimplemented.");
431b72d5b75SMichael Corcoran 	return (DDI_ME_UNIMPLEMENTED);
432b72d5b75SMichael Corcoran }
433b72d5b75SMichael Corcoran 
434b72d5b75SMichael Corcoran static int
435b72d5b75SMichael Corcoran acpinex_open(dev_t *devi, int flags, int otyp, cred_t *credp)
436b72d5b75SMichael Corcoran {
437*a3114836SGerry Liu 	_NOTE(ARGUNUSED(flags, otyp, credp));
438*a3114836SGerry Liu 
439b72d5b75SMichael Corcoran 	minor_t minor, instance;
440b72d5b75SMichael Corcoran 	acpinex_softstate_t *softsp;
441b72d5b75SMichael Corcoran 
442b72d5b75SMichael Corcoran 	minor = getminor(*devi);
443b72d5b75SMichael Corcoran 	instance = ACPINEX_GET_INSTANCE(minor);
444b72d5b75SMichael Corcoran 	if (instance >= ACPINEX_INSTANCE_MAX) {
445*a3114836SGerry Liu 		ACPINEX_DEBUG(CE_WARN, "!acpinex: instance number %d out of "
446b72d5b75SMichael Corcoran 		    "range in acpinex_open, max %d.",
447b72d5b75SMichael Corcoran 		    instance, ACPINEX_INSTANCE_MAX - 1);
448b72d5b75SMichael Corcoran 		return (EINVAL);
449b72d5b75SMichael Corcoran 	}
450b72d5b75SMichael Corcoran 
451b72d5b75SMichael Corcoran 	softsp = ddi_get_soft_state(acpinex_softstates, instance);
452b72d5b75SMichael Corcoran 	if (softsp == NULL) {
453*a3114836SGerry Liu 		ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to get soft state "
454b72d5b75SMichael Corcoran 		    "object for instance %d in acpinex_open().", instance);
455b72d5b75SMichael Corcoran 		return (EINVAL);
456b72d5b75SMichael Corcoran 	}
457b72d5b75SMichael Corcoran 
458b72d5b75SMichael Corcoran 	if (ACPINEX_IS_DEVCTL(minor)) {
459b72d5b75SMichael Corcoran 		return (0);
460b72d5b75SMichael Corcoran 	} else {
461b72d5b75SMichael Corcoran 		ACPINEX_DEBUG(CE_WARN,
462*a3114836SGerry Liu 		    "!acpinex: invalid minor number %d in acpinex_open().",
463b72d5b75SMichael Corcoran 		    minor);
464b72d5b75SMichael Corcoran 		return (EINVAL);
465b72d5b75SMichael Corcoran 	}
466b72d5b75SMichael Corcoran }
467b72d5b75SMichael Corcoran 
468b72d5b75SMichael Corcoran static int
469b72d5b75SMichael Corcoran acpinex_close(dev_t dev, int flags, int otyp, cred_t *credp)
470b72d5b75SMichael Corcoran {
471*a3114836SGerry Liu 	_NOTE(ARGUNUSED(flags, otyp, credp));
472*a3114836SGerry Liu 
473b72d5b75SMichael Corcoran 	minor_t minor, instance;
474b72d5b75SMichael Corcoran 	acpinex_softstate_t *softsp;
475b72d5b75SMichael Corcoran 
476b72d5b75SMichael Corcoran 	minor = getminor(dev);
477b72d5b75SMichael Corcoran 	instance = ACPINEX_GET_INSTANCE(minor);
478b72d5b75SMichael Corcoran 	if (instance >= ACPINEX_INSTANCE_MAX) {
479*a3114836SGerry Liu 		ACPINEX_DEBUG(CE_WARN, "!acpinex: instance number %d out of "
480b72d5b75SMichael Corcoran 		    "range in acpinex_close(), max %d.",
481b72d5b75SMichael Corcoran 		    instance, ACPINEX_INSTANCE_MAX - 1);
482b72d5b75SMichael Corcoran 		return (EINVAL);
483b72d5b75SMichael Corcoran 	}
484b72d5b75SMichael Corcoran 
485b72d5b75SMichael Corcoran 	softsp = ddi_get_soft_state(acpinex_softstates, instance);
486b72d5b75SMichael Corcoran 	if (softsp == NULL) {
487*a3114836SGerry Liu 		ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to get soft state "
488b72d5b75SMichael Corcoran 		    "object for instance %d in acpinex_close().", instance);
489b72d5b75SMichael Corcoran 		return (EINVAL);
490b72d5b75SMichael Corcoran 	}
491b72d5b75SMichael Corcoran 
492b72d5b75SMichael Corcoran 	if (ACPINEX_IS_DEVCTL(minor)) {
493b72d5b75SMichael Corcoran 		return (0);
494b72d5b75SMichael Corcoran 	} else {
495b72d5b75SMichael Corcoran 		ACPINEX_DEBUG(CE_WARN,
496*a3114836SGerry Liu 		    "!acpinex: invalid minor number %d in acpinex_close().",
497b72d5b75SMichael Corcoran 		    minor);
498b72d5b75SMichael Corcoran 		return (EINVAL);
499b72d5b75SMichael Corcoran 	}
500b72d5b75SMichael Corcoran }
501b72d5b75SMichael Corcoran 
502b72d5b75SMichael Corcoran static int
503b72d5b75SMichael Corcoran acpinex_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
504b72d5b75SMichael Corcoran     int *rvalp)
505b72d5b75SMichael Corcoran {
506*a3114836SGerry Liu 	_NOTE(ARGUNUSED(cmd, arg, mode, credp, rvalp));
507*a3114836SGerry Liu 
508b72d5b75SMichael Corcoran 	int rv = 0;
509b72d5b75SMichael Corcoran 	minor_t minor, instance;
510b72d5b75SMichael Corcoran 	acpinex_softstate_t *softsp;
511b72d5b75SMichael Corcoran 
512b72d5b75SMichael Corcoran 	minor = getminor(dev);
513b72d5b75SMichael Corcoran 	instance = ACPINEX_GET_INSTANCE(minor);
514b72d5b75SMichael Corcoran 	if (instance >= ACPINEX_INSTANCE_MAX) {
515*a3114836SGerry Liu 		ACPINEX_DEBUG(CE_NOTE, "!acpinex: instance number %d out of "
516b72d5b75SMichael Corcoran 		    "range in acpinex_ioctl(), max %d.",
517b72d5b75SMichael Corcoran 		    instance, ACPINEX_INSTANCE_MAX - 1);
518b72d5b75SMichael Corcoran 		return (EINVAL);
519b72d5b75SMichael Corcoran 	}
520b72d5b75SMichael Corcoran 	softsp = ddi_get_soft_state(acpinex_softstates, instance);
521b72d5b75SMichael Corcoran 	if (softsp == NULL) {
522*a3114836SGerry Liu 		ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to get soft state "
523b72d5b75SMichael Corcoran 		    "object for instance %d in acpinex_ioctl().", instance);
524b72d5b75SMichael Corcoran 		return (EINVAL);
525b72d5b75SMichael Corcoran 	}
526b72d5b75SMichael Corcoran 
527b72d5b75SMichael Corcoran 	rv = ENOTSUP;
528b72d5b75SMichael Corcoran 	ACPINEX_DEBUG(CE_WARN,
529*a3114836SGerry Liu 	    "!acpinex: invalid minor number %d in acpinex_ioctl().", minor);
530b72d5b75SMichael Corcoran 
531b72d5b75SMichael Corcoran 	return (rv);
532b72d5b75SMichael Corcoran }
533b72d5b75SMichael Corcoran 
534b72d5b75SMichael Corcoran /*
535b72d5b75SMichael Corcoran  * FMA error callback.
536b72d5b75SMichael Corcoran  * Register error handling callback with our parent. We will just call
537b72d5b75SMichael Corcoran  * our children's error callbacks and return their status.
538b72d5b75SMichael Corcoran  */
539b72d5b75SMichael Corcoran static int
540b72d5b75SMichael Corcoran acpinex_err_callback(dev_info_t *dip, ddi_fm_error_t *derr,
541b72d5b75SMichael Corcoran     const void *impl_data)
542b72d5b75SMichael Corcoran {
543*a3114836SGerry Liu 	_NOTE(ARGUNUSED(impl_data));
544*a3114836SGerry Liu 
545b72d5b75SMichael Corcoran 	/* Call our childrens error handlers */
546b72d5b75SMichael Corcoran 	return (ndi_fm_handler_dispatch(dip, NULL, derr));
547b72d5b75SMichael Corcoran }
548b72d5b75SMichael Corcoran 
549b72d5b75SMichael Corcoran /*
550b72d5b75SMichael Corcoran  * Initialize our FMA resources
551b72d5b75SMichael Corcoran  */
552b72d5b75SMichael Corcoran static void
553b72d5b75SMichael Corcoran acpinex_fm_init(acpinex_softstate_t *softsp)
554b72d5b75SMichael Corcoran {
555b72d5b75SMichael Corcoran 	softsp->ans_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE |
556b72d5b75SMichael Corcoran 	    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
557b72d5b75SMichael Corcoran 
558b72d5b75SMichael Corcoran 	/*
559b72d5b75SMichael Corcoran 	 * Request our capability level and get our parent's capability and ibc.
560b72d5b75SMichael Corcoran 	 */
561b72d5b75SMichael Corcoran 	ddi_fm_init(softsp->ans_dip, &softsp->ans_fm_cap, &softsp->ans_fm_ibc);
562b72d5b75SMichael Corcoran 	if (softsp->ans_fm_cap & DDI_FM_ERRCB_CAPABLE) {
563b72d5b75SMichael Corcoran 		/*
564b72d5b75SMichael Corcoran 		 * Register error callback with our parent if supported.
565b72d5b75SMichael Corcoran 		 */
566b72d5b75SMichael Corcoran 		ddi_fm_handler_register(softsp->ans_dip, acpinex_err_callback,
567b72d5b75SMichael Corcoran 		    softsp);
568b72d5b75SMichael Corcoran 	}
569b72d5b75SMichael Corcoran }
570b72d5b75SMichael Corcoran 
571b72d5b75SMichael Corcoran /*
572b72d5b75SMichael Corcoran  * Breakdown our FMA resources
573b72d5b75SMichael Corcoran  */
574b72d5b75SMichael Corcoran static void
575b72d5b75SMichael Corcoran acpinex_fm_fini(acpinex_softstate_t *softsp)
576b72d5b75SMichael Corcoran {
577b72d5b75SMichael Corcoran 	/* Clean up allocated fm structures */
578b72d5b75SMichael Corcoran 	if (softsp->ans_fm_cap & DDI_FM_ERRCB_CAPABLE) {
579b72d5b75SMichael Corcoran 		ddi_fm_handler_unregister(softsp->ans_dip);
580b72d5b75SMichael Corcoran 	}
581b72d5b75SMichael Corcoran 	ddi_fm_fini(softsp->ans_dip);
582b72d5b75SMichael Corcoran }
583b72d5b75SMichael Corcoran 
584b72d5b75SMichael Corcoran /*
585b72d5b75SMichael Corcoran  * Initialize FMA resources for child devices.
586b72d5b75SMichael Corcoran  * Called when child calls ddi_fm_init().
587b72d5b75SMichael Corcoran  */
588b72d5b75SMichael Corcoran static int
589b72d5b75SMichael Corcoran acpinex_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap,
590b72d5b75SMichael Corcoran     ddi_iblock_cookie_t *ibc)
591b72d5b75SMichael Corcoran {
592*a3114836SGerry Liu 	_NOTE(ARGUNUSED(tdip, cap));
593*a3114836SGerry Liu 
594b72d5b75SMichael Corcoran 	acpinex_softstate_t *softsp = ddi_get_soft_state(acpinex_softstates,
595b72d5b75SMichael Corcoran 	    ddi_get_instance(dip));
596b72d5b75SMichael Corcoran 
597b72d5b75SMichael Corcoran 	*ibc = softsp->ans_fm_ibc;
598b72d5b75SMichael Corcoran 
599b72d5b75SMichael Corcoran 	return (softsp->ans_fm_cap);
600b72d5b75SMichael Corcoran }
601