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