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