110394SMichael.Corcoran@Sun.COM /*
210394SMichael.Corcoran@Sun.COM * CDDL HEADER START
310394SMichael.Corcoran@Sun.COM *
410394SMichael.Corcoran@Sun.COM * The contents of this file are subject to the terms of the
510394SMichael.Corcoran@Sun.COM * Common Development and Distribution License (the "License").
610394SMichael.Corcoran@Sun.COM * You may not use this file except in compliance with the License.
710394SMichael.Corcoran@Sun.COM *
810394SMichael.Corcoran@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910394SMichael.Corcoran@Sun.COM * or http://www.opensolaris.org/os/licensing.
1010394SMichael.Corcoran@Sun.COM * See the License for the specific language governing permissions
1110394SMichael.Corcoran@Sun.COM * and limitations under the License.
1210394SMichael.Corcoran@Sun.COM *
1310394SMichael.Corcoran@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
1410394SMichael.Corcoran@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510394SMichael.Corcoran@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
1610394SMichael.Corcoran@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
1710394SMichael.Corcoran@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
1810394SMichael.Corcoran@Sun.COM *
1910394SMichael.Corcoran@Sun.COM * CDDL HEADER END
2010394SMichael.Corcoran@Sun.COM */
2110394SMichael.Corcoran@Sun.COM
2210394SMichael.Corcoran@Sun.COM /*
2310394SMichael.Corcoran@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2410394SMichael.Corcoran@Sun.COM * Use is subject to license terms.
2510394SMichael.Corcoran@Sun.COM */
2610394SMichael.Corcoran@Sun.COM /*
27*12004Sjiang.liu@intel.com * Copyright (c) 2009-2010, Intel Corporation.
2810394SMichael.Corcoran@Sun.COM * All rights reserved.
2910394SMichael.Corcoran@Sun.COM */
3010394SMichael.Corcoran@Sun.COM /*
3110394SMichael.Corcoran@Sun.COM * This module implements a nexus driver for the ACPI virtual bus.
3210394SMichael.Corcoran@Sun.COM * It does not handle any of the DDI functions passed up to it by the child
3310394SMichael.Corcoran@Sun.COM * drivers, but instead allows them to bubble up to the root node.
3410394SMichael.Corcoran@Sun.COM */
3510394SMichael.Corcoran@Sun.COM
3610394SMichael.Corcoran@Sun.COM #include <sys/types.h>
3710394SMichael.Corcoran@Sun.COM #include <sys/cmn_err.h>
3810394SMichael.Corcoran@Sun.COM #include <sys/conf.h>
3910394SMichael.Corcoran@Sun.COM #include <sys/modctl.h>
4010394SMichael.Corcoran@Sun.COM #include <sys/ddi.h>
4110394SMichael.Corcoran@Sun.COM #include <sys/ddi_impldefs.h>
4210394SMichael.Corcoran@Sun.COM #include <sys/ddifm.h>
43*12004Sjiang.liu@intel.com #include <sys/note.h>
4410394SMichael.Corcoran@Sun.COM #include <sys/ndifm.h>
4510394SMichael.Corcoran@Sun.COM #include <sys/sunddi.h>
4610394SMichael.Corcoran@Sun.COM #include <sys/sunndi.h>
4710394SMichael.Corcoran@Sun.COM #include <sys/acpidev.h>
4810394SMichael.Corcoran@Sun.COM #include <sys/acpinex.h>
4910394SMichael.Corcoran@Sun.COM
5010394SMichael.Corcoran@Sun.COM /* Patchable through /etc/system. */
5110394SMichael.Corcoran@Sun.COM #ifdef DEBUG
5210394SMichael.Corcoran@Sun.COM int acpinex_debug = 1;
5310394SMichael.Corcoran@Sun.COM #else
5410394SMichael.Corcoran@Sun.COM int acpinex_debug = 0;
5510394SMichael.Corcoran@Sun.COM #endif
5610394SMichael.Corcoran@Sun.COM
5710394SMichael.Corcoran@Sun.COM /*
5810394SMichael.Corcoran@Sun.COM * Driver globals
5910394SMichael.Corcoran@Sun.COM */
6010394SMichael.Corcoran@Sun.COM static kmutex_t acpinex_lock;
6110394SMichael.Corcoran@Sun.COM static void *acpinex_softstates;
6210394SMichael.Corcoran@Sun.COM
6310394SMichael.Corcoran@Sun.COM static int acpinex_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
6410394SMichael.Corcoran@Sun.COM static int acpinex_attach(dev_info_t *, ddi_attach_cmd_t);
6510394SMichael.Corcoran@Sun.COM static int acpinex_detach(dev_info_t *, ddi_detach_cmd_t);
6610394SMichael.Corcoran@Sun.COM static int acpinex_open(dev_t *, int, int, cred_t *);
6710394SMichael.Corcoran@Sun.COM static int acpinex_close(dev_t, int, int, cred_t *);
6810394SMichael.Corcoran@Sun.COM static int acpinex_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
6910394SMichael.Corcoran@Sun.COM static int acpinex_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
7010394SMichael.Corcoran@Sun.COM off_t offset, off_t len, caddr_t *vaddrp);
7110394SMichael.Corcoran@Sun.COM static int acpinex_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
7210394SMichael.Corcoran@Sun.COM void *);
7310394SMichael.Corcoran@Sun.COM static int acpinex_fm_init_child(dev_info_t *, dev_info_t *, int,
7410394SMichael.Corcoran@Sun.COM ddi_iblock_cookie_t *);
7510394SMichael.Corcoran@Sun.COM static void acpinex_fm_init(acpinex_softstate_t *softsp);
7610394SMichael.Corcoran@Sun.COM static void acpinex_fm_fini(acpinex_softstate_t *softsp);
7710394SMichael.Corcoran@Sun.COM
7810394SMichael.Corcoran@Sun.COM extern void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **);
7910394SMichael.Corcoran@Sun.COM
8010394SMichael.Corcoran@Sun.COM /*
8110394SMichael.Corcoran@Sun.COM * Configuration data structures
8210394SMichael.Corcoran@Sun.COM */
8310394SMichael.Corcoran@Sun.COM static struct bus_ops acpinex_bus_ops = {
8410394SMichael.Corcoran@Sun.COM BUSO_REV, /* busops_rev */
8510394SMichael.Corcoran@Sun.COM acpinex_bus_map, /* bus_map */
8610394SMichael.Corcoran@Sun.COM NULL, /* bus_get_intrspec */
8710394SMichael.Corcoran@Sun.COM NULL, /* bus_add_intrspec */
8810394SMichael.Corcoran@Sun.COM NULL, /* bus_remove_intrspec */
8910394SMichael.Corcoran@Sun.COM i_ddi_map_fault, /* bus_map_fault */
9010394SMichael.Corcoran@Sun.COM ddi_dma_map, /* bus_dma_map */
9110394SMichael.Corcoran@Sun.COM ddi_dma_allochdl, /* bus_dma_allochdl */
9210394SMichael.Corcoran@Sun.COM ddi_dma_freehdl, /* bus_dma_freehdl */
9310394SMichael.Corcoran@Sun.COM ddi_dma_bindhdl, /* bus_dma_bindhdl */
9410394SMichael.Corcoran@Sun.COM ddi_dma_unbindhdl, /* bus_dma_unbindhdl */
9510394SMichael.Corcoran@Sun.COM ddi_dma_flush, /* bus_dma_flush */
9610394SMichael.Corcoran@Sun.COM ddi_dma_win, /* bus_dma_win */
9710394SMichael.Corcoran@Sun.COM ddi_dma_mctl, /* bus_dma_ctl */
9810394SMichael.Corcoran@Sun.COM acpinex_ctlops, /* bus_ctl */
9910394SMichael.Corcoran@Sun.COM ddi_bus_prop_op, /* bus_prop_op */
10010394SMichael.Corcoran@Sun.COM ndi_busop_get_eventcookie, /* bus_get_eventcookie */
10110394SMichael.Corcoran@Sun.COM ndi_busop_add_eventcall, /* bus_add_eventcall */
10210394SMichael.Corcoran@Sun.COM ndi_busop_remove_eventcall, /* bus_remove_eventcall */
10310394SMichael.Corcoran@Sun.COM ndi_post_event, /* bus_post_event */
10410394SMichael.Corcoran@Sun.COM NULL, /* bus_intr_ctl */
10510394SMichael.Corcoran@Sun.COM NULL, /* bus_config */
10610394SMichael.Corcoran@Sun.COM NULL, /* bus_unconfig */
10710394SMichael.Corcoran@Sun.COM acpinex_fm_init_child, /* bus_fm_init */
10810394SMichael.Corcoran@Sun.COM NULL, /* bus_fm_fini */
10910394SMichael.Corcoran@Sun.COM NULL, /* bus_fm_access_enter */
11010394SMichael.Corcoran@Sun.COM NULL, /* bus_fm_access_exit */
11110394SMichael.Corcoran@Sun.COM NULL, /* bus_power */
11210394SMichael.Corcoran@Sun.COM i_ddi_intr_ops /* bus_intr_op */
11310394SMichael.Corcoran@Sun.COM };
11410394SMichael.Corcoran@Sun.COM
11510394SMichael.Corcoran@Sun.COM static struct cb_ops acpinex_cb_ops = {
11610394SMichael.Corcoran@Sun.COM acpinex_open, /* cb_open */
11710394SMichael.Corcoran@Sun.COM acpinex_close, /* cb_close */
11810394SMichael.Corcoran@Sun.COM nodev, /* cb_strategy */
11910394SMichael.Corcoran@Sun.COM nodev, /* cb_print */
12010394SMichael.Corcoran@Sun.COM nodev, /* cb_dump */
12110394SMichael.Corcoran@Sun.COM nodev, /* cb_read */
12210394SMichael.Corcoran@Sun.COM nodev, /* cb_write */
12310394SMichael.Corcoran@Sun.COM acpinex_ioctl, /* cb_ioctl */
12410394SMichael.Corcoran@Sun.COM nodev, /* cb_devmap */
12510394SMichael.Corcoran@Sun.COM nodev, /* cb_mmap */
12610394SMichael.Corcoran@Sun.COM nodev, /* cb_segmap */
12710394SMichael.Corcoran@Sun.COM nochpoll, /* cb_poll */
12810394SMichael.Corcoran@Sun.COM ddi_prop_op, /* cb_prop_op */
12910394SMichael.Corcoran@Sun.COM NULL, /* cb_str */
13010394SMichael.Corcoran@Sun.COM D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
13110394SMichael.Corcoran@Sun.COM CB_REV, /* rev */
13210394SMichael.Corcoran@Sun.COM nodev, /* int (*cb_aread)() */
13310394SMichael.Corcoran@Sun.COM nodev /* int (*cb_awrite)() */
13410394SMichael.Corcoran@Sun.COM };
13510394SMichael.Corcoran@Sun.COM
13610394SMichael.Corcoran@Sun.COM static struct dev_ops acpinex_ops = {
13710394SMichael.Corcoran@Sun.COM DEVO_REV, /* devo_rev, */
13810394SMichael.Corcoran@Sun.COM 0, /* devo_refcnt */
13910394SMichael.Corcoran@Sun.COM acpinex_info, /* devo_getinfo */
14010394SMichael.Corcoran@Sun.COM nulldev, /* devo_identify */
14110394SMichael.Corcoran@Sun.COM nulldev, /* devo_probe */
14210394SMichael.Corcoran@Sun.COM acpinex_attach, /* devo_attach */
14310394SMichael.Corcoran@Sun.COM acpinex_detach, /* devo_detach */
14410394SMichael.Corcoran@Sun.COM nulldev, /* devo_reset */
14510394SMichael.Corcoran@Sun.COM &acpinex_cb_ops, /* devo_cb_ops */
14610394SMichael.Corcoran@Sun.COM &acpinex_bus_ops, /* devo_bus_ops */
14710394SMichael.Corcoran@Sun.COM nulldev, /* devo_power */
14810394SMichael.Corcoran@Sun.COM ddi_quiesce_not_needed /* devo_quiesce */
14910394SMichael.Corcoran@Sun.COM };
15010394SMichael.Corcoran@Sun.COM
15110394SMichael.Corcoran@Sun.COM static struct modldrv modldrv = {
15210394SMichael.Corcoran@Sun.COM &mod_driverops, /* Type of module */
15310394SMichael.Corcoran@Sun.COM "ACPI virtual bus driver", /* name of module */
15410394SMichael.Corcoran@Sun.COM &acpinex_ops, /* driver ops */
15510394SMichael.Corcoran@Sun.COM };
15610394SMichael.Corcoran@Sun.COM
15710394SMichael.Corcoran@Sun.COM static struct modlinkage modlinkage = {
15810394SMichael.Corcoran@Sun.COM MODREV_1, /* rev */
15910394SMichael.Corcoran@Sun.COM (void *)&modldrv,
16010394SMichael.Corcoran@Sun.COM NULL
16110394SMichael.Corcoran@Sun.COM };
16210394SMichael.Corcoran@Sun.COM
16310394SMichael.Corcoran@Sun.COM /*
16410394SMichael.Corcoran@Sun.COM * Module initialization routines.
16510394SMichael.Corcoran@Sun.COM */
16610394SMichael.Corcoran@Sun.COM int
_init(void)16710394SMichael.Corcoran@Sun.COM _init(void)
16810394SMichael.Corcoran@Sun.COM {
16910394SMichael.Corcoran@Sun.COM int error;
17010394SMichael.Corcoran@Sun.COM
17110394SMichael.Corcoran@Sun.COM /* Initialize soft state pointer. */
17210394SMichael.Corcoran@Sun.COM if ((error = ddi_soft_state_init(&acpinex_softstates,
17310394SMichael.Corcoran@Sun.COM sizeof (acpinex_softstate_t), 8)) != 0) {
17410394SMichael.Corcoran@Sun.COM cmn_err(CE_WARN,
17510394SMichael.Corcoran@Sun.COM "acpinex: failed to initialize soft state structure.");
17610394SMichael.Corcoran@Sun.COM return (error);
17710394SMichael.Corcoran@Sun.COM }
17810394SMichael.Corcoran@Sun.COM
179*12004Sjiang.liu@intel.com /* Initialize event subsystem. */
180*12004Sjiang.liu@intel.com acpinex_event_init();
181*12004Sjiang.liu@intel.com
18210394SMichael.Corcoran@Sun.COM /* Install the module. */
18310394SMichael.Corcoran@Sun.COM if ((error = mod_install(&modlinkage)) != 0) {
18410394SMichael.Corcoran@Sun.COM cmn_err(CE_WARN, "acpinex: failed to install module.");
18510394SMichael.Corcoran@Sun.COM ddi_soft_state_fini(&acpinex_softstates);
18610394SMichael.Corcoran@Sun.COM return (error);
18710394SMichael.Corcoran@Sun.COM }
18810394SMichael.Corcoran@Sun.COM
18910394SMichael.Corcoran@Sun.COM mutex_init(&acpinex_lock, NULL, MUTEX_DRIVER, NULL);
19010394SMichael.Corcoran@Sun.COM
19110394SMichael.Corcoran@Sun.COM return (0);
19210394SMichael.Corcoran@Sun.COM }
19310394SMichael.Corcoran@Sun.COM
19410394SMichael.Corcoran@Sun.COM int
_fini(void)19510394SMichael.Corcoran@Sun.COM _fini(void)
19610394SMichael.Corcoran@Sun.COM {
19710394SMichael.Corcoran@Sun.COM int error;
19810394SMichael.Corcoran@Sun.COM
19910394SMichael.Corcoran@Sun.COM /* Remove the module. */
20010394SMichael.Corcoran@Sun.COM if ((error = mod_remove(&modlinkage)) != 0) {
20110394SMichael.Corcoran@Sun.COM return (error);
20210394SMichael.Corcoran@Sun.COM }
20310394SMichael.Corcoran@Sun.COM
204*12004Sjiang.liu@intel.com /* Shut down event subsystem. */
205*12004Sjiang.liu@intel.com acpinex_event_fini();
206*12004Sjiang.liu@intel.com
20710394SMichael.Corcoran@Sun.COM /* Free the soft state info. */
20810394SMichael.Corcoran@Sun.COM ddi_soft_state_fini(&acpinex_softstates);
20910394SMichael.Corcoran@Sun.COM
21010394SMichael.Corcoran@Sun.COM mutex_destroy(&acpinex_lock);
21110394SMichael.Corcoran@Sun.COM
21210394SMichael.Corcoran@Sun.COM return (0);
21310394SMichael.Corcoran@Sun.COM }
21410394SMichael.Corcoran@Sun.COM
21510394SMichael.Corcoran@Sun.COM int
_info(struct modinfo * modinfop)21610394SMichael.Corcoran@Sun.COM _info(struct modinfo *modinfop)
21710394SMichael.Corcoran@Sun.COM {
21810394SMichael.Corcoran@Sun.COM return (mod_info(&modlinkage, modinfop));
21910394SMichael.Corcoran@Sun.COM }
22010394SMichael.Corcoran@Sun.COM
22110394SMichael.Corcoran@Sun.COM static int
acpinex_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)22210394SMichael.Corcoran@Sun.COM acpinex_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
22310394SMichael.Corcoran@Sun.COM {
224*12004Sjiang.liu@intel.com _NOTE(ARGUNUSED(dip));
225*12004Sjiang.liu@intel.com
22610394SMichael.Corcoran@Sun.COM dev_t dev;
22710394SMichael.Corcoran@Sun.COM int instance;
22810394SMichael.Corcoran@Sun.COM
22910394SMichael.Corcoran@Sun.COM if (infocmd == DDI_INFO_DEVT2INSTANCE) {
23010394SMichael.Corcoran@Sun.COM dev = (dev_t)arg;
23110394SMichael.Corcoran@Sun.COM instance = ACPINEX_GET_INSTANCE(getminor(dev));
23210394SMichael.Corcoran@Sun.COM *result = (void *)(uintptr_t)instance;
23310394SMichael.Corcoran@Sun.COM return (DDI_SUCCESS);
23410394SMichael.Corcoran@Sun.COM }
23510394SMichael.Corcoran@Sun.COM
23610394SMichael.Corcoran@Sun.COM return (DDI_FAILURE);
23710394SMichael.Corcoran@Sun.COM }
23810394SMichael.Corcoran@Sun.COM
23910394SMichael.Corcoran@Sun.COM static int
acpinex_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)24010394SMichael.Corcoran@Sun.COM acpinex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
24110394SMichael.Corcoran@Sun.COM {
24210394SMichael.Corcoran@Sun.COM int instance;
24310394SMichael.Corcoran@Sun.COM acpinex_softstate_t *softsp;
24410394SMichael.Corcoran@Sun.COM
24510394SMichael.Corcoran@Sun.COM switch (cmd) {
24610394SMichael.Corcoran@Sun.COM case DDI_ATTACH:
24710394SMichael.Corcoran@Sun.COM break;
24810394SMichael.Corcoran@Sun.COM
24910394SMichael.Corcoran@Sun.COM case DDI_RESUME:
25010394SMichael.Corcoran@Sun.COM return (DDI_SUCCESS);
25110394SMichael.Corcoran@Sun.COM
25210394SMichael.Corcoran@Sun.COM default:
25310394SMichael.Corcoran@Sun.COM return (DDI_FAILURE);
25410394SMichael.Corcoran@Sun.COM }
25510394SMichael.Corcoran@Sun.COM
25610394SMichael.Corcoran@Sun.COM /* Get and check instance number. */
25710394SMichael.Corcoran@Sun.COM instance = ddi_get_instance(devi);
25810394SMichael.Corcoran@Sun.COM if (instance >= ACPINEX_INSTANCE_MAX) {
25910394SMichael.Corcoran@Sun.COM cmn_err(CE_WARN, "acpinex: instance number %d is out of range "
26010394SMichael.Corcoran@Sun.COM "in acpinex_attach(), max %d.",
26110394SMichael.Corcoran@Sun.COM instance, ACPINEX_INSTANCE_MAX - 1);
26210394SMichael.Corcoran@Sun.COM return (DDI_FAILURE);
26310394SMichael.Corcoran@Sun.COM }
26410394SMichael.Corcoran@Sun.COM
26510394SMichael.Corcoran@Sun.COM /* Get soft state structure. */
26610394SMichael.Corcoran@Sun.COM if (ddi_soft_state_zalloc(acpinex_softstates, instance)
26710394SMichael.Corcoran@Sun.COM != DDI_SUCCESS) {
26810394SMichael.Corcoran@Sun.COM cmn_err(CE_WARN, "!acpinex: failed to allocate soft state "
26910394SMichael.Corcoran@Sun.COM "object in acpinex_attach().");
27010394SMichael.Corcoran@Sun.COM return (DDI_FAILURE);
27110394SMichael.Corcoran@Sun.COM }
27210394SMichael.Corcoran@Sun.COM softsp = ddi_get_soft_state(acpinex_softstates, instance);
27310394SMichael.Corcoran@Sun.COM
27410394SMichael.Corcoran@Sun.COM /* Initialize soft state structure */
27510394SMichael.Corcoran@Sun.COM softsp->ans_dip = devi;
27610394SMichael.Corcoran@Sun.COM (void) ddi_pathname(devi, softsp->ans_path);
27710394SMichael.Corcoran@Sun.COM if (ACPI_FAILURE(acpica_get_handle(devi, &softsp->ans_hdl))) {
27810394SMichael.Corcoran@Sun.COM ACPINEX_DEBUG(CE_WARN,
279*12004Sjiang.liu@intel.com "!acpinex: failed to get ACPI handle for %s.",
28010394SMichael.Corcoran@Sun.COM softsp->ans_path);
28110394SMichael.Corcoran@Sun.COM ddi_soft_state_free(acpinex_softstates, instance);
28210394SMichael.Corcoran@Sun.COM return (DDI_FAILURE);
28310394SMichael.Corcoran@Sun.COM }
28410394SMichael.Corcoran@Sun.COM mutex_init(&softsp->ans_lock, NULL, MUTEX_DRIVER, NULL);
28510394SMichael.Corcoran@Sun.COM
286*12004Sjiang.liu@intel.com /* Install event handler for child/descendant objects. */
287*12004Sjiang.liu@intel.com if (acpinex_event_scan(softsp, B_TRUE) != DDI_SUCCESS) {
288*12004Sjiang.liu@intel.com cmn_err(CE_WARN, "!acpinex: failed to install event handler "
289*12004Sjiang.liu@intel.com "for children of %s.", softsp->ans_path);
290*12004Sjiang.liu@intel.com }
291*12004Sjiang.liu@intel.com
29210394SMichael.Corcoran@Sun.COM /* nothing to suspend/resume here */
29310394SMichael.Corcoran@Sun.COM (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
29410394SMichael.Corcoran@Sun.COM "pm-hardware-state", "no-suspend-resume");
295*12004Sjiang.liu@intel.com (void) ddi_prop_update_int(DDI_DEV_T_NONE, devi,
296*12004Sjiang.liu@intel.com DDI_NO_AUTODETACH, 1);
29710394SMichael.Corcoran@Sun.COM
29810394SMichael.Corcoran@Sun.COM acpinex_fm_init(softsp);
29910394SMichael.Corcoran@Sun.COM ddi_report_dev(devi);
30010394SMichael.Corcoran@Sun.COM
30110394SMichael.Corcoran@Sun.COM return (DDI_SUCCESS);
30210394SMichael.Corcoran@Sun.COM }
30310394SMichael.Corcoran@Sun.COM
30410394SMichael.Corcoran@Sun.COM static int
acpinex_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)30510394SMichael.Corcoran@Sun.COM acpinex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
30610394SMichael.Corcoran@Sun.COM {
30710394SMichael.Corcoran@Sun.COM int instance;
30810394SMichael.Corcoran@Sun.COM acpinex_softstate_t *softsp;
30910394SMichael.Corcoran@Sun.COM
31010394SMichael.Corcoran@Sun.COM instance = ddi_get_instance(devi);
31110394SMichael.Corcoran@Sun.COM if (instance >= ACPINEX_INSTANCE_MAX) {
31210394SMichael.Corcoran@Sun.COM cmn_err(CE_WARN, "acpinex: instance number %d is out of range "
31310394SMichael.Corcoran@Sun.COM "in acpinex_detach(), max %d.",
31410394SMichael.Corcoran@Sun.COM instance, ACPINEX_INSTANCE_MAX - 1);
31510394SMichael.Corcoran@Sun.COM return (DDI_FAILURE);
31610394SMichael.Corcoran@Sun.COM }
31710394SMichael.Corcoran@Sun.COM
31810394SMichael.Corcoran@Sun.COM softsp = ddi_get_soft_state(acpinex_softstates, instance);
31910394SMichael.Corcoran@Sun.COM if (softsp == NULL) {
320*12004Sjiang.liu@intel.com ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to get soft state "
32110394SMichael.Corcoran@Sun.COM "object for instance %d in acpinex_detach()", instance);
32210394SMichael.Corcoran@Sun.COM return (DDI_FAILURE);
32310394SMichael.Corcoran@Sun.COM }
32410394SMichael.Corcoran@Sun.COM
32510394SMichael.Corcoran@Sun.COM switch (cmd) {
32610394SMichael.Corcoran@Sun.COM case DDI_DETACH:
327*12004Sjiang.liu@intel.com if (acpinex_event_scan(softsp, B_FALSE) != DDI_SUCCESS) {
328*12004Sjiang.liu@intel.com cmn_err(CE_WARN, "!acpinex: failed to uninstall event "
329*12004Sjiang.liu@intel.com "handler for children of %s.", softsp->ans_path);
330*12004Sjiang.liu@intel.com return (DDI_FAILURE);
331*12004Sjiang.liu@intel.com }
33210394SMichael.Corcoran@Sun.COM ddi_remove_minor_node(devi, NULL);
33310394SMichael.Corcoran@Sun.COM acpinex_fm_fini(softsp);
33410394SMichael.Corcoran@Sun.COM mutex_destroy(&softsp->ans_lock);
33510394SMichael.Corcoran@Sun.COM ddi_soft_state_free(acpinex_softstates, instance);
336*12004Sjiang.liu@intel.com (void) ddi_prop_update_int(DDI_DEV_T_NONE, devi,
337*12004Sjiang.liu@intel.com DDI_NO_AUTODETACH, 0);
33810394SMichael.Corcoran@Sun.COM return (DDI_SUCCESS);
33910394SMichael.Corcoran@Sun.COM
34010394SMichael.Corcoran@Sun.COM case DDI_SUSPEND:
34110394SMichael.Corcoran@Sun.COM return (DDI_SUCCESS);
34210394SMichael.Corcoran@Sun.COM
34310394SMichael.Corcoran@Sun.COM default:
34410394SMichael.Corcoran@Sun.COM return (DDI_FAILURE);
34510394SMichael.Corcoran@Sun.COM }
34610394SMichael.Corcoran@Sun.COM }
34710394SMichael.Corcoran@Sun.COM
34810394SMichael.Corcoran@Sun.COM static int
name_child(dev_info_t * child,char * name,int namelen)34910394SMichael.Corcoran@Sun.COM name_child(dev_info_t *child, char *name, int namelen)
35010394SMichael.Corcoran@Sun.COM {
35110394SMichael.Corcoran@Sun.COM char *unitaddr;
35210394SMichael.Corcoran@Sun.COM
35310394SMichael.Corcoran@Sun.COM ddi_set_parent_data(child, NULL);
35410394SMichael.Corcoran@Sun.COM
35510394SMichael.Corcoran@Sun.COM name[0] = '\0';
35610394SMichael.Corcoran@Sun.COM if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
35710394SMichael.Corcoran@Sun.COM ACPIDEV_PROP_NAME_UNIT_ADDR, &unitaddr) == DDI_SUCCESS) {
358*12004Sjiang.liu@intel.com (void) strlcpy(name, unitaddr, namelen);
35910394SMichael.Corcoran@Sun.COM ddi_prop_free(unitaddr);
36010394SMichael.Corcoran@Sun.COM } else {
361*12004Sjiang.liu@intel.com ACPINEX_DEBUG(CE_NOTE, "!acpinex: failed to lookup child "
362*12004Sjiang.liu@intel.com "unit-address prop for %p.", (void *)child);
36310394SMichael.Corcoran@Sun.COM }
36410394SMichael.Corcoran@Sun.COM
36510394SMichael.Corcoran@Sun.COM return (DDI_SUCCESS);
36610394SMichael.Corcoran@Sun.COM }
36710394SMichael.Corcoran@Sun.COM
36810394SMichael.Corcoran@Sun.COM static int
init_child(dev_info_t * child)36910394SMichael.Corcoran@Sun.COM init_child(dev_info_t *child)
37010394SMichael.Corcoran@Sun.COM {
37110394SMichael.Corcoran@Sun.COM char name[MAXNAMELEN];
37210394SMichael.Corcoran@Sun.COM
37310394SMichael.Corcoran@Sun.COM (void) name_child(child, name, MAXNAMELEN);
37410394SMichael.Corcoran@Sun.COM ddi_set_name_addr(child, name);
37510394SMichael.Corcoran@Sun.COM if ((ndi_dev_is_persistent_node(child) == 0) &&
37610394SMichael.Corcoran@Sun.COM (ndi_merge_node(child, name_child) == DDI_SUCCESS)) {
37710394SMichael.Corcoran@Sun.COM impl_ddi_sunbus_removechild(child);
37810394SMichael.Corcoran@Sun.COM return (DDI_FAILURE);
37910394SMichael.Corcoran@Sun.COM }
38010394SMichael.Corcoran@Sun.COM
38110394SMichael.Corcoran@Sun.COM return (DDI_SUCCESS);
38210394SMichael.Corcoran@Sun.COM }
38310394SMichael.Corcoran@Sun.COM
38410394SMichael.Corcoran@Sun.COM /*
38510394SMichael.Corcoran@Sun.COM * Control ops entry point:
38610394SMichael.Corcoran@Sun.COM *
38710394SMichael.Corcoran@Sun.COM * Requests handled completely:
38810394SMichael.Corcoran@Sun.COM * DDI_CTLOPS_INITCHILD
38910394SMichael.Corcoran@Sun.COM * DDI_CTLOPS_UNINITCHILD
39010394SMichael.Corcoran@Sun.COM * All others are passed to the parent.
39110394SMichael.Corcoran@Sun.COM */
39210394SMichael.Corcoran@Sun.COM static int
acpinex_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)39310394SMichael.Corcoran@Sun.COM acpinex_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, void *arg,
39410394SMichael.Corcoran@Sun.COM void *result)
39510394SMichael.Corcoran@Sun.COM {
39610394SMichael.Corcoran@Sun.COM int rval = DDI_SUCCESS;
39710394SMichael.Corcoran@Sun.COM
39810394SMichael.Corcoran@Sun.COM switch (op) {
39910394SMichael.Corcoran@Sun.COM case DDI_CTLOPS_INITCHILD:
40010394SMichael.Corcoran@Sun.COM rval = init_child((dev_info_t *)arg);
40110394SMichael.Corcoran@Sun.COM break;
40210394SMichael.Corcoran@Sun.COM
40310394SMichael.Corcoran@Sun.COM case DDI_CTLOPS_UNINITCHILD:
40410394SMichael.Corcoran@Sun.COM impl_ddi_sunbus_removechild((dev_info_t *)arg);
40510394SMichael.Corcoran@Sun.COM break;
40610394SMichael.Corcoran@Sun.COM
40710394SMichael.Corcoran@Sun.COM case DDI_CTLOPS_REPORTDEV: {
40810394SMichael.Corcoran@Sun.COM if (rdip == (dev_info_t *)0)
40910394SMichael.Corcoran@Sun.COM return (DDI_FAILURE);
41010394SMichael.Corcoran@Sun.COM cmn_err(CE_CONT, "?acpinex: %s@%s, %s%d\n",
41110394SMichael.Corcoran@Sun.COM ddi_node_name(rdip), ddi_get_name_addr(rdip),
41210394SMichael.Corcoran@Sun.COM ddi_driver_name(rdip), ddi_get_instance(rdip));
41310394SMichael.Corcoran@Sun.COM break;
41410394SMichael.Corcoran@Sun.COM }
41510394SMichael.Corcoran@Sun.COM
41610394SMichael.Corcoran@Sun.COM default:
41710394SMichael.Corcoran@Sun.COM rval = ddi_ctlops(dip, rdip, op, arg, result);
41810394SMichael.Corcoran@Sun.COM break;
41910394SMichael.Corcoran@Sun.COM }
42010394SMichael.Corcoran@Sun.COM
42110394SMichael.Corcoran@Sun.COM return (rval);
42210394SMichael.Corcoran@Sun.COM }
42310394SMichael.Corcoran@Sun.COM
42410394SMichael.Corcoran@Sun.COM /* ARGSUSED */
42510394SMichael.Corcoran@Sun.COM 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)42610394SMichael.Corcoran@Sun.COM acpinex_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
42710394SMichael.Corcoran@Sun.COM off_t offset, off_t len, caddr_t *vaddrp)
42810394SMichael.Corcoran@Sun.COM {
42910394SMichael.Corcoran@Sun.COM ACPINEX_DEBUG(CE_WARN,
430*12004Sjiang.liu@intel.com "!acpinex: acpinex_bus_map called and it's unimplemented.");
43110394SMichael.Corcoran@Sun.COM return (DDI_ME_UNIMPLEMENTED);
43210394SMichael.Corcoran@Sun.COM }
43310394SMichael.Corcoran@Sun.COM
43410394SMichael.Corcoran@Sun.COM static int
acpinex_open(dev_t * devi,int flags,int otyp,cred_t * credp)43510394SMichael.Corcoran@Sun.COM acpinex_open(dev_t *devi, int flags, int otyp, cred_t *credp)
43610394SMichael.Corcoran@Sun.COM {
437*12004Sjiang.liu@intel.com _NOTE(ARGUNUSED(flags, otyp, credp));
438*12004Sjiang.liu@intel.com
43910394SMichael.Corcoran@Sun.COM minor_t minor, instance;
44010394SMichael.Corcoran@Sun.COM acpinex_softstate_t *softsp;
44110394SMichael.Corcoran@Sun.COM
44210394SMichael.Corcoran@Sun.COM minor = getminor(*devi);
44310394SMichael.Corcoran@Sun.COM instance = ACPINEX_GET_INSTANCE(minor);
44410394SMichael.Corcoran@Sun.COM if (instance >= ACPINEX_INSTANCE_MAX) {
445*12004Sjiang.liu@intel.com ACPINEX_DEBUG(CE_WARN, "!acpinex: instance number %d out of "
44610394SMichael.Corcoran@Sun.COM "range in acpinex_open, max %d.",
44710394SMichael.Corcoran@Sun.COM instance, ACPINEX_INSTANCE_MAX - 1);
44810394SMichael.Corcoran@Sun.COM return (EINVAL);
44910394SMichael.Corcoran@Sun.COM }
45010394SMichael.Corcoran@Sun.COM
45110394SMichael.Corcoran@Sun.COM softsp = ddi_get_soft_state(acpinex_softstates, instance);
45210394SMichael.Corcoran@Sun.COM if (softsp == NULL) {
453*12004Sjiang.liu@intel.com ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to get soft state "
45410394SMichael.Corcoran@Sun.COM "object for instance %d in acpinex_open().", instance);
45510394SMichael.Corcoran@Sun.COM return (EINVAL);
45610394SMichael.Corcoran@Sun.COM }
45710394SMichael.Corcoran@Sun.COM
45810394SMichael.Corcoran@Sun.COM if (ACPINEX_IS_DEVCTL(minor)) {
45910394SMichael.Corcoran@Sun.COM return (0);
46010394SMichael.Corcoran@Sun.COM } else {
46110394SMichael.Corcoran@Sun.COM ACPINEX_DEBUG(CE_WARN,
462*12004Sjiang.liu@intel.com "!acpinex: invalid minor number %d in acpinex_open().",
46310394SMichael.Corcoran@Sun.COM minor);
46410394SMichael.Corcoran@Sun.COM return (EINVAL);
46510394SMichael.Corcoran@Sun.COM }
46610394SMichael.Corcoran@Sun.COM }
46710394SMichael.Corcoran@Sun.COM
46810394SMichael.Corcoran@Sun.COM static int
acpinex_close(dev_t dev,int flags,int otyp,cred_t * credp)46910394SMichael.Corcoran@Sun.COM acpinex_close(dev_t dev, int flags, int otyp, cred_t *credp)
47010394SMichael.Corcoran@Sun.COM {
471*12004Sjiang.liu@intel.com _NOTE(ARGUNUSED(flags, otyp, credp));
472*12004Sjiang.liu@intel.com
47310394SMichael.Corcoran@Sun.COM minor_t minor, instance;
47410394SMichael.Corcoran@Sun.COM acpinex_softstate_t *softsp;
47510394SMichael.Corcoran@Sun.COM
47610394SMichael.Corcoran@Sun.COM minor = getminor(dev);
47710394SMichael.Corcoran@Sun.COM instance = ACPINEX_GET_INSTANCE(minor);
47810394SMichael.Corcoran@Sun.COM if (instance >= ACPINEX_INSTANCE_MAX) {
479*12004Sjiang.liu@intel.com ACPINEX_DEBUG(CE_WARN, "!acpinex: instance number %d out of "
48010394SMichael.Corcoran@Sun.COM "range in acpinex_close(), max %d.",
48110394SMichael.Corcoran@Sun.COM instance, ACPINEX_INSTANCE_MAX - 1);
48210394SMichael.Corcoran@Sun.COM return (EINVAL);
48310394SMichael.Corcoran@Sun.COM }
48410394SMichael.Corcoran@Sun.COM
48510394SMichael.Corcoran@Sun.COM softsp = ddi_get_soft_state(acpinex_softstates, instance);
48610394SMichael.Corcoran@Sun.COM if (softsp == NULL) {
487*12004Sjiang.liu@intel.com ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to get soft state "
48810394SMichael.Corcoran@Sun.COM "object for instance %d in acpinex_close().", instance);
48910394SMichael.Corcoran@Sun.COM return (EINVAL);
49010394SMichael.Corcoran@Sun.COM }
49110394SMichael.Corcoran@Sun.COM
49210394SMichael.Corcoran@Sun.COM if (ACPINEX_IS_DEVCTL(minor)) {
49310394SMichael.Corcoran@Sun.COM return (0);
49410394SMichael.Corcoran@Sun.COM } else {
49510394SMichael.Corcoran@Sun.COM ACPINEX_DEBUG(CE_WARN,
496*12004Sjiang.liu@intel.com "!acpinex: invalid minor number %d in acpinex_close().",
49710394SMichael.Corcoran@Sun.COM minor);
49810394SMichael.Corcoran@Sun.COM return (EINVAL);
49910394SMichael.Corcoran@Sun.COM }
50010394SMichael.Corcoran@Sun.COM }
50110394SMichael.Corcoran@Sun.COM
50210394SMichael.Corcoran@Sun.COM static int
acpinex_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)50310394SMichael.Corcoran@Sun.COM acpinex_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
50410394SMichael.Corcoran@Sun.COM int *rvalp)
50510394SMichael.Corcoran@Sun.COM {
506*12004Sjiang.liu@intel.com _NOTE(ARGUNUSED(cmd, arg, mode, credp, rvalp));
507*12004Sjiang.liu@intel.com
50810394SMichael.Corcoran@Sun.COM int rv = 0;
50910394SMichael.Corcoran@Sun.COM minor_t minor, instance;
51010394SMichael.Corcoran@Sun.COM acpinex_softstate_t *softsp;
51110394SMichael.Corcoran@Sun.COM
51210394SMichael.Corcoran@Sun.COM minor = getminor(dev);
51310394SMichael.Corcoran@Sun.COM instance = ACPINEX_GET_INSTANCE(minor);
51410394SMichael.Corcoran@Sun.COM if (instance >= ACPINEX_INSTANCE_MAX) {
515*12004Sjiang.liu@intel.com ACPINEX_DEBUG(CE_NOTE, "!acpinex: instance number %d out of "
51610394SMichael.Corcoran@Sun.COM "range in acpinex_ioctl(), max %d.",
51710394SMichael.Corcoran@Sun.COM instance, ACPINEX_INSTANCE_MAX - 1);
51810394SMichael.Corcoran@Sun.COM return (EINVAL);
51910394SMichael.Corcoran@Sun.COM }
52010394SMichael.Corcoran@Sun.COM softsp = ddi_get_soft_state(acpinex_softstates, instance);
52110394SMichael.Corcoran@Sun.COM if (softsp == NULL) {
522*12004Sjiang.liu@intel.com ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to get soft state "
52310394SMichael.Corcoran@Sun.COM "object for instance %d in acpinex_ioctl().", instance);
52410394SMichael.Corcoran@Sun.COM return (EINVAL);
52510394SMichael.Corcoran@Sun.COM }
52610394SMichael.Corcoran@Sun.COM
52710394SMichael.Corcoran@Sun.COM rv = ENOTSUP;
52810394SMichael.Corcoran@Sun.COM ACPINEX_DEBUG(CE_WARN,
529*12004Sjiang.liu@intel.com "!acpinex: invalid minor number %d in acpinex_ioctl().", minor);
53010394SMichael.Corcoran@Sun.COM
53110394SMichael.Corcoran@Sun.COM return (rv);
53210394SMichael.Corcoran@Sun.COM }
53310394SMichael.Corcoran@Sun.COM
53410394SMichael.Corcoran@Sun.COM /*
53510394SMichael.Corcoran@Sun.COM * FMA error callback.
53610394SMichael.Corcoran@Sun.COM * Register error handling callback with our parent. We will just call
53710394SMichael.Corcoran@Sun.COM * our children's error callbacks and return their status.
53810394SMichael.Corcoran@Sun.COM */
53910394SMichael.Corcoran@Sun.COM static int
acpinex_err_callback(dev_info_t * dip,ddi_fm_error_t * derr,const void * impl_data)54010394SMichael.Corcoran@Sun.COM acpinex_err_callback(dev_info_t *dip, ddi_fm_error_t *derr,
54110394SMichael.Corcoran@Sun.COM const void *impl_data)
54210394SMichael.Corcoran@Sun.COM {
543*12004Sjiang.liu@intel.com _NOTE(ARGUNUSED(impl_data));
544*12004Sjiang.liu@intel.com
54510394SMichael.Corcoran@Sun.COM /* Call our childrens error handlers */
54610394SMichael.Corcoran@Sun.COM return (ndi_fm_handler_dispatch(dip, NULL, derr));
54710394SMichael.Corcoran@Sun.COM }
54810394SMichael.Corcoran@Sun.COM
54910394SMichael.Corcoran@Sun.COM /*
55010394SMichael.Corcoran@Sun.COM * Initialize our FMA resources
55110394SMichael.Corcoran@Sun.COM */
55210394SMichael.Corcoran@Sun.COM static void
acpinex_fm_init(acpinex_softstate_t * softsp)55310394SMichael.Corcoran@Sun.COM acpinex_fm_init(acpinex_softstate_t *softsp)
55410394SMichael.Corcoran@Sun.COM {
55510394SMichael.Corcoran@Sun.COM softsp->ans_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE |
55610394SMichael.Corcoran@Sun.COM DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
55710394SMichael.Corcoran@Sun.COM
55810394SMichael.Corcoran@Sun.COM /*
55910394SMichael.Corcoran@Sun.COM * Request our capability level and get our parent's capability and ibc.
56010394SMichael.Corcoran@Sun.COM */
56110394SMichael.Corcoran@Sun.COM ddi_fm_init(softsp->ans_dip, &softsp->ans_fm_cap, &softsp->ans_fm_ibc);
56210394SMichael.Corcoran@Sun.COM if (softsp->ans_fm_cap & DDI_FM_ERRCB_CAPABLE) {
56310394SMichael.Corcoran@Sun.COM /*
56410394SMichael.Corcoran@Sun.COM * Register error callback with our parent if supported.
56510394SMichael.Corcoran@Sun.COM */
56610394SMichael.Corcoran@Sun.COM ddi_fm_handler_register(softsp->ans_dip, acpinex_err_callback,
56710394SMichael.Corcoran@Sun.COM softsp);
56810394SMichael.Corcoran@Sun.COM }
56910394SMichael.Corcoran@Sun.COM }
57010394SMichael.Corcoran@Sun.COM
57110394SMichael.Corcoran@Sun.COM /*
57210394SMichael.Corcoran@Sun.COM * Breakdown our FMA resources
57310394SMichael.Corcoran@Sun.COM */
57410394SMichael.Corcoran@Sun.COM static void
acpinex_fm_fini(acpinex_softstate_t * softsp)57510394SMichael.Corcoran@Sun.COM acpinex_fm_fini(acpinex_softstate_t *softsp)
57610394SMichael.Corcoran@Sun.COM {
57710394SMichael.Corcoran@Sun.COM /* Clean up allocated fm structures */
57810394SMichael.Corcoran@Sun.COM if (softsp->ans_fm_cap & DDI_FM_ERRCB_CAPABLE) {
57910394SMichael.Corcoran@Sun.COM ddi_fm_handler_unregister(softsp->ans_dip);
58010394SMichael.Corcoran@Sun.COM }
58110394SMichael.Corcoran@Sun.COM ddi_fm_fini(softsp->ans_dip);
58210394SMichael.Corcoran@Sun.COM }
58310394SMichael.Corcoran@Sun.COM
58410394SMichael.Corcoran@Sun.COM /*
58510394SMichael.Corcoran@Sun.COM * Initialize FMA resources for child devices.
58610394SMichael.Corcoran@Sun.COM * Called when child calls ddi_fm_init().
58710394SMichael.Corcoran@Sun.COM */
58810394SMichael.Corcoran@Sun.COM static int
acpinex_fm_init_child(dev_info_t * dip,dev_info_t * tdip,int cap,ddi_iblock_cookie_t * ibc)58910394SMichael.Corcoran@Sun.COM acpinex_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap,
59010394SMichael.Corcoran@Sun.COM ddi_iblock_cookie_t *ibc)
59110394SMichael.Corcoran@Sun.COM {
592*12004Sjiang.liu@intel.com _NOTE(ARGUNUSED(tdip, cap));
593*12004Sjiang.liu@intel.com
59410394SMichael.Corcoran@Sun.COM acpinex_softstate_t *softsp = ddi_get_soft_state(acpinex_softstates,
59510394SMichael.Corcoran@Sun.COM ddi_get_instance(dip));
59610394SMichael.Corcoran@Sun.COM
59710394SMichael.Corcoran@Sun.COM *ibc = softsp->ans_fm_ibc;
59810394SMichael.Corcoran@Sun.COM
59910394SMichael.Corcoran@Sun.COM return (softsp->ans_fm_cap);
60010394SMichael.Corcoran@Sun.COM }
601