13db86aabSstevel /*
23db86aabSstevel  * CDDL HEADER START
33db86aabSstevel  *
43db86aabSstevel  * The contents of this file are subject to the terms of the
53db86aabSstevel  * Common Development and Distribution License (the "License").
63db86aabSstevel  * You may not use this file except in compliance with the License.
73db86aabSstevel  *
83db86aabSstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93db86aabSstevel  * or http://www.opensolaris.org/os/licensing.
103db86aabSstevel  * See the License for the specific language governing permissions
113db86aabSstevel  * and limitations under the License.
123db86aabSstevel  *
133db86aabSstevel  * When distributing Covered Code, include this CDDL HEADER in each
143db86aabSstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153db86aabSstevel  * If applicable, add the following below this CDDL HEADER, with the
163db86aabSstevel  * fields enclosed by brackets "[]" replaced with your own identifying
173db86aabSstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
183db86aabSstevel  *
193db86aabSstevel  * CDDL HEADER END
203db86aabSstevel  */
213db86aabSstevel /*
225c066ec2SJerry Gilliam  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
233db86aabSstevel  * Use is subject to license terms.
243db86aabSstevel  */
253db86aabSstevel 
263db86aabSstevel /*
273db86aabSstevel  * Copyright (c)  * Copyright (c) 2001 Tadpole Technology plc
283db86aabSstevel  * All rights reserved.
293db86aabSstevel  * From "@(#)pcicfg.c   1.31    99/06/18 SMI"
303db86aabSstevel  */
313db86aabSstevel 
32*3fe80ca4SDan Cross /*
33*3fe80ca4SDan Cross  * Copyright 2023 Oxide Computer Company
34*3fe80ca4SDan Cross  */
35*3fe80ca4SDan Cross 
363db86aabSstevel /*
373db86aabSstevel  * Cardbus hotplug module
383db86aabSstevel  */
393db86aabSstevel 
403db86aabSstevel #include <sys/open.h>
413db86aabSstevel #include <sys/file.h>
423db86aabSstevel #include <sys/stat.h>
433db86aabSstevel #include <sys/ddi.h>
443db86aabSstevel #include <sys/sunndi.h>
453db86aabSstevel 
463db86aabSstevel #include <sys/note.h>
473db86aabSstevel 
483db86aabSstevel #include <sys/pci.h>
493db86aabSstevel 
503db86aabSstevel #include <sys/hotplug/hpcsvc.h>
513db86aabSstevel #include <sys/hotplug/pci/pcicfg.h>
523db86aabSstevel #include <sys/pcic_reg.h>
533db86aabSstevel 
543db86aabSstevel #include "cardbus.h"
553db86aabSstevel #include "cardbus_hp.h"
563db86aabSstevel #include "cardbus_cfg.h"
573db86aabSstevel 
583db86aabSstevel /*
593db86aabSstevel  * ************************************************************************
603db86aabSstevel  * *** Implementation specific data structures/definitions.             ***
613db86aabSstevel  * ************************************************************************
623db86aabSstevel  */
633db86aabSstevel 
643db86aabSstevel #ifndef HPC_MAX_OCCUPANTS
653db86aabSstevel #define	HPC_MAX_OCCUPANTS 8
663db86aabSstevel typedef struct hpc_occupant_info {
673db86aabSstevel 	int	i;
68647709cbSToomas Soome 	char	*id[HPC_MAX_OCCUPANTS];
693db86aabSstevel } hpc_occupant_info_t;
703db86aabSstevel #endif
713db86aabSstevel 
723db86aabSstevel #define	PCICFG_FLAGS_CONTINUE   0x1
733db86aabSstevel 
743db86aabSstevel #define	PCICFG_OP_ONLINE	0x1
753db86aabSstevel #define	PCICFG_OP_OFFLINE	0x0
763db86aabSstevel 
773db86aabSstevel #define	CBHP_DEVCTL_MINOR	255
783db86aabSstevel 
793db86aabSstevel #define	AP_MINOR_NUM_TO_CB_INSTANCE(x)	((x) & 0xFF)
803db86aabSstevel #define	AP_MINOR_NUM(x)		(((uint_t)(3) << 8) | ((x) & 0xFF))
813db86aabSstevel #define	AP_IS_CB_MINOR(x)	(((x)>>8) == (3))
823db86aabSstevel 
833db86aabSstevel extern int cardbus_debug;
840d282d13Srw extern int number_of_cardbus_cards;
853db86aabSstevel 
863db86aabSstevel static int cardbus_autocfg_enabled = 1;	/* auto config is enabled by default */
873db86aabSstevel 
883db86aabSstevel /* static functions */
893db86aabSstevel static int cardbus_event_handler(caddr_t slot_arg, uint_t event_mask);
903db86aabSstevel static int cardbus_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl,
913db86aabSstevel 				int request, caddr_t arg);
923db86aabSstevel static int cardbus_new_slot_state(dev_info_t *dip, hpc_slot_t hdl,
933db86aabSstevel 				hpc_slot_info_t *slot_info, int slot_state);
943db86aabSstevel static int cardbus_list_occupants(dev_info_t *dip, void *hdl);
953db86aabSstevel static void create_occupant_props(dev_info_t *self, dev_t dev);
963db86aabSstevel static void delete_occupant_props(dev_info_t *dip, dev_t dev);
973db86aabSstevel static int cardbus_configure_ap(cbus_t *cbp);
983db86aabSstevel static int cardbus_unconfigure_ap(cbus_t *cbp);
993db86aabSstevel static int cbus_unconfigure(dev_info_t *devi, int prim_bus);
1003db86aabSstevel void cardbus_dump_pci_config(dev_info_t *dip);
1013db86aabSstevel void cardbus_dump_pci_node(dev_info_t *dip);
1023db86aabSstevel 
1033db86aabSstevel int
cardbus_init_hotplug(cbus_t * cbp)1043db86aabSstevel cardbus_init_hotplug(cbus_t *cbp)
1053db86aabSstevel {
1063db86aabSstevel 	char tbuf[MAXNAMELEN];
1073db86aabSstevel 	hpc_slot_info_t	slot_info;
1083db86aabSstevel 	hpc_slot_ops_t	*slot_ops;
1093db86aabSstevel 	hpc_slot_t	slhandle;	/* HPS slot handle */
1103db86aabSstevel 
1113db86aabSstevel 	/*
1123db86aabSstevel 	 *  register the bus instance with the HPS framework.
1133db86aabSstevel 	 */
1143db86aabSstevel 	if (hpc_nexus_register_bus(cbp->cb_dip,
1153db86aabSstevel 	    cardbus_new_slot_state, 0) != 0) {
1163db86aabSstevel 		cmn_err(CE_WARN, "%s%d: failed to register the bus with HPS\n",
1173db86aabSstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance);
1183db86aabSstevel 		return (DDI_FAILURE);
1193db86aabSstevel 	}
1203db86aabSstevel 
1213db86aabSstevel 	(void) sprintf(cbp->ap_id, "slot%d", cbp->cb_instance);
1223db86aabSstevel 	(void) ddi_pathname(cbp->cb_dip, tbuf);
1233db86aabSstevel 	cbp->nexus_path = kmem_alloc(strlen(tbuf) + 1, KM_SLEEP);
1243db86aabSstevel 	(void) strcpy(cbp->nexus_path, tbuf);
1253db86aabSstevel 	cardbus_err(cbp->cb_dip, 8,
1263db86aabSstevel 	    "cardbus_init_hotplug: nexus_path set to %s", cbp->nexus_path);
1273db86aabSstevel 
1283db86aabSstevel 	slot_ops = hpc_alloc_slot_ops(KM_SLEEP);
1293db86aabSstevel 	cbp->slot_ops = slot_ops;
1303db86aabSstevel 
1313db86aabSstevel 	/*
1323db86aabSstevel 	 * Fill in the slot information structure that
1333db86aabSstevel 	 * describes the slot.
1343db86aabSstevel 	 */
1353db86aabSstevel 	slot_info.version = HPC_SLOT_INFO_VERSION;
1363db86aabSstevel 	slot_info.slot_type = HPC_SLOT_TYPE_PCI;
1373db86aabSstevel 	slot_info.slot.pci.device_number = 0;
1383db86aabSstevel 	slot_info.slot.pci.slot_capabilities = 0;
1393db86aabSstevel 
1403db86aabSstevel 	(void) strcpy(slot_info.slot.pci.slot_logical_name, cbp->ap_id);
1413db86aabSstevel 
1423db86aabSstevel 	slot_ops->hpc_version = HPC_SLOT_OPS_VERSION;
1433db86aabSstevel 	slot_ops->hpc_op_connect = NULL;
1443db86aabSstevel 	slot_ops->hpc_op_disconnect = NULL;
1453db86aabSstevel 	slot_ops->hpc_op_insert = NULL;
1463db86aabSstevel 	slot_ops->hpc_op_remove = NULL;
1473db86aabSstevel 	slot_ops->hpc_op_control = cardbus_pci_control;
1483db86aabSstevel 
1493db86aabSstevel 	if (hpc_slot_register(cbp->cb_dip, cbp->nexus_path, &slot_info,
1503db86aabSstevel 	    &slhandle, slot_ops, (caddr_t)cbp, 0) != 0) {
1513db86aabSstevel 		/*
1523db86aabSstevel 		 * If the slot can not be registered,
1533db86aabSstevel 		 * then the slot_ops need to be freed.
1543db86aabSstevel 		 */
1553db86aabSstevel 		cmn_err(CE_WARN,
1563db86aabSstevel 		    "cbp%d Unable to Register Slot %s", cbp->cb_instance,
1573db86aabSstevel 		    slot_info.slot.pci.slot_logical_name);
1583db86aabSstevel 
1593db86aabSstevel 		(void) hpc_nexus_unregister_bus(cbp->cb_dip);
1603db86aabSstevel 		hpc_free_slot_ops(slot_ops);
1613db86aabSstevel 		cbp->slot_ops = NULL;
1623db86aabSstevel 		return (DDI_FAILURE);
1633db86aabSstevel 	}
1643db86aabSstevel 
1653db86aabSstevel 	ASSERT(slhandle == cbp->slot_handle);
1663db86aabSstevel 
1673db86aabSstevel 	cardbus_err(cbp->cb_dip, 8,
1683db86aabSstevel 	    "cardbus_init_hotplug: slot_handle 0x%p", cbp->slot_handle);
1693db86aabSstevel 	return (DDI_SUCCESS);
1703db86aabSstevel }
1713db86aabSstevel 
1723db86aabSstevel static int
cardbus_event_handler(caddr_t slot_arg,uint_t event_mask)1733db86aabSstevel cardbus_event_handler(caddr_t slot_arg, uint_t event_mask)
1743db86aabSstevel {
1753db86aabSstevel 	int ap_minor = (int)((uintptr_t)slot_arg);
1763db86aabSstevel 	cbus_t *cbp;
1773db86aabSstevel 	int cb_instance;
1783db86aabSstevel 	int rv = HPC_EVENT_CLAIMED;
1793db86aabSstevel 
1803db86aabSstevel 	cb_instance = AP_MINOR_NUM_TO_CB_INSTANCE(ap_minor);
1813db86aabSstevel 
1823db86aabSstevel 	ASSERT(cb_instance >= 0);
1833db86aabSstevel 	cbp = (cbus_t *)ddi_get_soft_state(cardbus_state, cb_instance);
1843db86aabSstevel 	mutex_enter(&cbp->cb_mutex);
1853db86aabSstevel 
1863db86aabSstevel 	switch (event_mask) {
1873db86aabSstevel 
1883db86aabSstevel 	case HPC_EVENT_SLOT_INSERTION:
1893db86aabSstevel 		/*
1903db86aabSstevel 		 * A card is inserted in the slot. Just report this
1913db86aabSstevel 		 * event and return.
1923db86aabSstevel 		 */
1933db86aabSstevel 		cardbus_err(cbp->cb_dip, 7,
1943db86aabSstevel 		    "cardbus_event_handler(%s%d): card is inserted",
1953db86aabSstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance);
1963db86aabSstevel 
1973db86aabSstevel 		break;
1983db86aabSstevel 
1993db86aabSstevel 	case HPC_EVENT_SLOT_CONFIGURE:
2003db86aabSstevel 		/*
2013db86aabSstevel 		 * Configure the occupant that is just inserted in the slot.
2023db86aabSstevel 		 * The receptacle may or may not be in the connected state. If
2033db86aabSstevel 		 * the receptacle is not connected and the auto configuration
2043db86aabSstevel 		 * is enabled on this slot then connect the slot. If auto
2053db86aabSstevel 		 * configuration is enabled then configure the card.
2063db86aabSstevel 		 */
2073db86aabSstevel 		if (!(cbp->auto_config)) {
2083db86aabSstevel 			/*
2093db86aabSstevel 			 * auto configuration is disabled.
2103db86aabSstevel 			 */
2113db86aabSstevel 			cardbus_err(cbp->cb_dip, 7,
2123db86aabSstevel 			    "cardbus_event_handler(%s%d): "
2133db86aabSstevel 			    "SLOT_CONFIGURE event occured (slot %s)",
2143db86aabSstevel 			    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
2153db86aabSstevel 			    cbp->name);
2163db86aabSstevel 
2173db86aabSstevel 			break;
2183db86aabSstevel 		}
2193db86aabSstevel 
2203db86aabSstevel 		cardbus_err(cbp->cb_dip, 7,
2213db86aabSstevel 		    "cardbus_event_handler(%s%d): configure event",
2223db86aabSstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance);
2233db86aabSstevel 
2243db86aabSstevel 		if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
2253db86aabSstevel 			cmn_err(CE_WARN, "!slot%d already configured\n",
2265c066ec2SJerry Gilliam 			    cbp->cb_instance);
2273db86aabSstevel 			break;
2283db86aabSstevel 		}
2293db86aabSstevel 
2303db86aabSstevel 		/*
2313db86aabSstevel 		 * Auto configuration is enabled. First, make sure the
2323db86aabSstevel 		 * receptacle is in the CONNECTED state.
2333db86aabSstevel 		 */
2343db86aabSstevel 		if ((rv = hpc_nexus_connect(cbp->slot_handle,
2353db86aabSstevel 		    NULL, 0)) == HPC_SUCCESS) {
2363db86aabSstevel 			cbp->rstate = AP_RSTATE_CONNECTED; /* record rstate */
2373db86aabSstevel 		}
2383db86aabSstevel 
2393db86aabSstevel 		if (cardbus_configure_ap(cbp) == HPC_SUCCESS)
2405c066ec2SJerry Gilliam 			create_occupant_props(cbp->cb_dip, makedevice(
2415c066ec2SJerry Gilliam 			    ddi_driver_major((cbp->cb_dip)), ap_minor));
2423db86aabSstevel 		else
2433db86aabSstevel 			rv = HPC_ERR_FAILED;
2443db86aabSstevel 
2453db86aabSstevel 		break;
2463db86aabSstevel 
2473db86aabSstevel 	case HPC_EVENT_SLOT_UNCONFIGURE:
2483db86aabSstevel 		/*
2493db86aabSstevel 		 * Unconfigure the occupant in this slot.
2503db86aabSstevel 		 */
2513db86aabSstevel 		if (!(cbp->auto_config)) {
2523db86aabSstevel 			/*
2533db86aabSstevel 			 * auto configuration is disabled.
2543db86aabSstevel 			 */
2553db86aabSstevel 			cardbus_err(cbp->cb_dip, 7,
2563db86aabSstevel 			    "cardbus_event_handler(%s%d): "
2573db86aabSstevel 			    "SLOT_UNCONFIGURE event"
2583db86aabSstevel 			    " occured - auto-conf disabled (slot %s)",
2593db86aabSstevel 			    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
2603db86aabSstevel 			    cbp->name);
2613db86aabSstevel 
2623db86aabSstevel 			break;
2633db86aabSstevel 		}
2643db86aabSstevel 
2653db86aabSstevel 		cardbus_err(cbp->cb_dip, 7,
2663db86aabSstevel 		    "cardbus_event_handler(%s%d): SLOT_UNCONFIGURE event"
2673db86aabSstevel 		    " occured (slot %s)",
2683db86aabSstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
2693db86aabSstevel 		    cbp->name);
2703db86aabSstevel 
2713db86aabSstevel 		if (cardbus_unconfigure_ap(cbp) != HPC_SUCCESS)
2723db86aabSstevel 			rv = HPC_ERR_FAILED;
2733db86aabSstevel 
2740d282d13Srw 		DEVI(cbp->cb_dip)->devi_ops->devo_bus_ops = cbp->orig_bopsp;
2750d282d13Srw 		--number_of_cardbus_cards;
2763db86aabSstevel 		break;
2773db86aabSstevel 
2783db86aabSstevel 	case HPC_EVENT_SLOT_REMOVAL:
2793db86aabSstevel 		/*
2803db86aabSstevel 		 * Card is removed from the slot. The card must have been
2813db86aabSstevel 		 * unconfigured before this event.
2823db86aabSstevel 		 */
2833db86aabSstevel 		if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
2843db86aabSstevel 			cardbus_err(cbp->cb_dip, 1,
2853db86aabSstevel 			    "cardbus_event_handler(%s%d): "
2863db86aabSstevel 			    "card is removed from"
2873db86aabSstevel 			    " the slot %s before doing unconfigure!!",
2883db86aabSstevel 			    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
2893db86aabSstevel 			    cbp->name);
2903db86aabSstevel 
2913db86aabSstevel 			break;
2923db86aabSstevel 		}
2933db86aabSstevel 
2943db86aabSstevel 		cardbus_err(cbp->cb_dip, 7,
2953db86aabSstevel 		    "cardbus_event_handler(%s%d): "
2963db86aabSstevel 		    "card is removed from the slot %s",
2973db86aabSstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
2983db86aabSstevel 		    cbp->name);
2993db86aabSstevel 
3003db86aabSstevel 		break;
3013db86aabSstevel 
3023db86aabSstevel 	case HPC_EVENT_SLOT_POWER_ON:
3033db86aabSstevel 		/*
3043db86aabSstevel 		 * Slot is connected to the bus. i.e the card is powered
3053db86aabSstevel 		 * on.
3063db86aabSstevel 		 */
3073db86aabSstevel 		cardbus_err(cbp->cb_dip, 7,
3083db86aabSstevel 		    "cardbus_event_handler(%s%d): "
3093db86aabSstevel 		    "card is powered on in the slot %s",
3103db86aabSstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
3113db86aabSstevel 		    cbp->name);
3123db86aabSstevel 
3133db86aabSstevel 		cbp->rstate = AP_RSTATE_CONNECTED; /* record rstate */
3143db86aabSstevel 
3153db86aabSstevel 		break;
3163db86aabSstevel 
3173db86aabSstevel 	case HPC_EVENT_SLOT_POWER_OFF:
3183db86aabSstevel 		/*
3193db86aabSstevel 		 * Slot is disconnected from the bus. i.e the card is powered
3203db86aabSstevel 		 * off.
3213db86aabSstevel 		 */
3223db86aabSstevel 		cardbus_err(cbp->cb_dip, 7,
3233db86aabSstevel 		    "cardbus_event_handler(%s%d): "
3243db86aabSstevel 		    "card is powered off in the slot %s",
3253db86aabSstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
3263db86aabSstevel 		    cbp->name);
3273db86aabSstevel 
3283db86aabSstevel 		cbp->rstate = AP_RSTATE_DISCONNECTED; /* record rstate */
3293db86aabSstevel 
3303db86aabSstevel 		break;
3313db86aabSstevel 
3323db86aabSstevel 	default:
3333db86aabSstevel 		cardbus_err(cbp->cb_dip, 4,
3343db86aabSstevel 		    "cardbus_event_handler(%s%d): "
3353db86aabSstevel 		    "unknown event %x for this slot %s",
3363db86aabSstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
3373db86aabSstevel 		    event_mask, cbp->name);
3383db86aabSstevel 
3393db86aabSstevel 		break;
3403db86aabSstevel 	}
3413db86aabSstevel 
3423db86aabSstevel 	mutex_exit(&cbp->cb_mutex);
3433db86aabSstevel 
3443db86aabSstevel 	return (rv);
3453db86aabSstevel }
3463db86aabSstevel 
3473db86aabSstevel static int
cardbus_pci_control(caddr_t ops_arg,hpc_slot_t slot_hdl,int request,caddr_t arg)3483db86aabSstevel cardbus_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl, int request,
349647709cbSToomas Soome     caddr_t arg)
3503db86aabSstevel {
3513db86aabSstevel 	cbus_t *cbp;
3523db86aabSstevel 	int rval = HPC_SUCCESS;
3533db86aabSstevel 	hpc_led_info_t *hpc_led_info;
3543db86aabSstevel 
3553db86aabSstevel 	_NOTE(ARGUNUSED(slot_hdl))
3563db86aabSstevel 
3573db86aabSstevel 	cbp = (cbus_t *)ops_arg;
3583db86aabSstevel 	ASSERT(mutex_owned(&cbp->cb_mutex));
3593db86aabSstevel 
3603db86aabSstevel 	switch (request) {
3613db86aabSstevel 
3625c066ec2SJerry Gilliam 	case HPC_CTRL_GET_SLOT_STATE: {
3633db86aabSstevel 		hpc_slot_state_t	*hpc_slot_state;
3643db86aabSstevel 
3653db86aabSstevel 		hpc_slot_state = (hpc_slot_state_t *)arg;
3663db86aabSstevel 
3673db86aabSstevel 		cardbus_err(cbp->cb_dip, 7,
3683db86aabSstevel 		    "cardbus_pci_control() - "
3693db86aabSstevel 		    "HPC_CTRL_GET_SLOT_STATE hpc_slot_state=0x%p",
3703db86aabSstevel 		    (void *) hpc_slot_state);
3713db86aabSstevel 
3723db86aabSstevel 		if (cbp->card_present)
3733db86aabSstevel 			*hpc_slot_state = HPC_SLOT_CONNECTED;
3743db86aabSstevel 		else
3753db86aabSstevel 			*hpc_slot_state = HPC_SLOT_EMPTY;
3763db86aabSstevel 
3773db86aabSstevel 		break;
3785c066ec2SJerry Gilliam 	}
3793db86aabSstevel 
3805c066ec2SJerry Gilliam 	case HPC_CTRL_GET_BOARD_TYPE: {
3813db86aabSstevel 		hpc_board_type_t	*hpc_board_type;
3823db86aabSstevel 
3833db86aabSstevel 		hpc_board_type = (hpc_board_type_t *)arg;
3843db86aabSstevel 
3853db86aabSstevel 		cardbus_err(cbp->cb_dip, 7,
3863db86aabSstevel 		    "cardbus_pci_control() - HPC_CTRL_GET_BOARD_TYPE");
3873db86aabSstevel 
3883db86aabSstevel 		/*
3893db86aabSstevel 		 * The HPC driver does not know what board type
3903db86aabSstevel 		 * is plugged in.
3913db86aabSstevel 		 */
3923db86aabSstevel 		*hpc_board_type = HPC_BOARD_PCI_HOTPLUG;
3933db86aabSstevel 
3943db86aabSstevel 		break;
3955c066ec2SJerry Gilliam 	}
3963db86aabSstevel 
3973db86aabSstevel 	case HPC_CTRL_DEV_CONFIGURED:
3983db86aabSstevel 	case HPC_CTRL_DEV_UNCONFIGURED:
3993db86aabSstevel 		cardbus_err(cbp->cb_dip, 5,
4003db86aabSstevel 		    "cardbus_pci_control() - HPC_CTRL_DEV_%sCONFIGURED",
4013db86aabSstevel 		    request == HPC_CTRL_DEV_UNCONFIGURED ? "UN" : "");
4023db86aabSstevel 		break;
4033db86aabSstevel 
4043db86aabSstevel 	case HPC_CTRL_GET_LED_STATE:
4053db86aabSstevel 		hpc_led_info = (hpc_led_info_t *)arg;
4063db86aabSstevel 		cardbus_err(cbp->cb_dip, 5,
4073db86aabSstevel 		    "cardbus_pci_control() - HPC_CTRL_GET_LED_STATE "
4083db86aabSstevel 		    "led %d is %d",
4093db86aabSstevel 		    hpc_led_info->led, cbp->leds[hpc_led_info->led]);
4103db86aabSstevel 
4113db86aabSstevel 		hpc_led_info->state = cbp->leds[hpc_led_info->led];
4123db86aabSstevel 		break;
4133db86aabSstevel 
4143db86aabSstevel 	case HPC_CTRL_SET_LED_STATE:
4153db86aabSstevel 		hpc_led_info = (hpc_led_info_t *)arg;
4163db86aabSstevel 
4173db86aabSstevel 		cardbus_err(cbp->cb_dip, 4,
4183db86aabSstevel 		    "cardbus_pci_control() - HPC_CTRL_SET_LED_STATE "
4193db86aabSstevel 		    "led %d to %d",
4203db86aabSstevel 		    hpc_led_info->led, hpc_led_info->state);
4213db86aabSstevel 
4223db86aabSstevel 		cbp->leds[hpc_led_info->led] = hpc_led_info->state;
4233db86aabSstevel 		break;
4243db86aabSstevel 
4253db86aabSstevel 	case HPC_CTRL_ENABLE_AUTOCFG:
4263db86aabSstevel 		cardbus_err(cbp->cb_dip, 5,
4273db86aabSstevel 		    "cardbus_pci_control() - HPC_CTRL_ENABLE_AUTOCFG");
4283db86aabSstevel 
4293db86aabSstevel 		/*
4303db86aabSstevel 		 * Cardbus ALWAYS does auto config, from the slots point of
4313db86aabSstevel 		 * view this is turning on the card and making sure it's ok.
4323db86aabSstevel 		 * This is all done by the bridge driver before we see any
4333db86aabSstevel 		 * indication.
4343db86aabSstevel 		 */
4353db86aabSstevel 		break;
4363db86aabSstevel 
4373db86aabSstevel 	case HPC_CTRL_DISABLE_AUTOCFG:
4383db86aabSstevel 		cardbus_err(cbp->cb_dip, 5,
4393db86aabSstevel 		    "cardbus_pci_control() - HPC_CTRL_DISABLE_AUTOCFG");
4403db86aabSstevel 		break;
4413db86aabSstevel 
4423db86aabSstevel 	case HPC_CTRL_DISABLE_ENUM:
4433db86aabSstevel 	case HPC_CTRL_ENABLE_ENUM:
4443db86aabSstevel 	default:
4453db86aabSstevel 		rval = HPC_ERR_NOTSUPPORTED;
4463db86aabSstevel 		break;
4473db86aabSstevel 	}
4483db86aabSstevel 
4493db86aabSstevel 	return (rval);
4503db86aabSstevel }
4513db86aabSstevel 
4523db86aabSstevel /*
4533db86aabSstevel  * cardbus_new_slot_state()
4543db86aabSstevel  *
4553db86aabSstevel  * This function is called by the HPS when it finds a hot plug
4563db86aabSstevel  * slot is added or being removed from the hot plug framework.
4573db86aabSstevel  * It returns 0 for success and HPC_ERR_FAILED for errors.
4583db86aabSstevel  */
4593db86aabSstevel static int
cardbus_new_slot_state(dev_info_t * dip,hpc_slot_t hdl,hpc_slot_info_t * slot_info,int slot_state)4603db86aabSstevel cardbus_new_slot_state(dev_info_t *dip, hpc_slot_t hdl,
461647709cbSToomas Soome     hpc_slot_info_t *slot_info, int slot_state)
4623db86aabSstevel {
4633db86aabSstevel 	int cb_instance;
4643db86aabSstevel 	cbus_t *cbp;
4653db86aabSstevel 	int ap_minor;
4663db86aabSstevel 	int rv = 0;
4673db86aabSstevel 
4683db86aabSstevel 	cardbus_err(dip, 8,
4693db86aabSstevel 	    "cardbus_new_slot_state: slot_handle 0x%p", hdl);
4703db86aabSstevel 
4713db86aabSstevel 	/*
4723db86aabSstevel 	 * get the soft state structure for the bus instance.
4733db86aabSstevel 	 */
4743db86aabSstevel 	cb_instance = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
4753db86aabSstevel 	    DDI_PROP_DONTPASS, "cbus-instance", -1);
4765c066ec2SJerry Gilliam 	ASSERT(cb_instance >= 0);
4773db86aabSstevel 	cbp = (cbus_t *)ddi_get_soft_state(cardbus_state, cb_instance);
4783db86aabSstevel 
4793db86aabSstevel 	mutex_enter(&cbp->cb_mutex);
4803db86aabSstevel 
4813db86aabSstevel 	switch (slot_state) {
4823db86aabSstevel 
4833db86aabSstevel 	case HPC_SLOT_ONLINE:
4843db86aabSstevel 		/*
4853db86aabSstevel 		 * Make sure the slot is not already ONLINE
4863db86aabSstevel 		 */
4873db86aabSstevel 		if (cbp->slot_handle != NULL) {
4883db86aabSstevel 			cardbus_err(dip, 4,
4893db86aabSstevel 			    "cardbus_new_slot_state: "
4903db86aabSstevel 			    "cardbus already ONLINE!!");
4913db86aabSstevel 			rv = HPC_ERR_FAILED;
4923db86aabSstevel 			break;
4933db86aabSstevel 		}
4943db86aabSstevel 
4953db86aabSstevel 		/*
4963db86aabSstevel 		 * Add the hot plug slot to the bus.
4973db86aabSstevel 		 */
4983db86aabSstevel 
4993db86aabSstevel 		/* create the AP minor node */
5003db86aabSstevel 		ap_minor = AP_MINOR_NUM(cb_instance);
5013db86aabSstevel 		if (ddi_create_minor_node(dip, slot_info->pci_slot_name,
5023db86aabSstevel 		    S_IFCHR, ap_minor,
5033db86aabSstevel 		    DDI_NT_PCI_ATTACHMENT_POINT,
5043db86aabSstevel 		    0) == DDI_FAILURE) {
5053db86aabSstevel 			cardbus_err(dip, 4,
5063db86aabSstevel 			    "cardbus_new_slot_state: "
5073db86aabSstevel 			    "ddi_create_minor_node failed");
5083db86aabSstevel 			rv = HPC_ERR_FAILED;
5093db86aabSstevel 			break;
5103db86aabSstevel 		}
5113db86aabSstevel 
5123db86aabSstevel 		/* save the slot handle */
5133db86aabSstevel 		cbp->slot_handle = hdl;
5143db86aabSstevel 
5153db86aabSstevel 		/* setup event handler for all hardware events on the slot */
5163db86aabSstevel 		if (hpc_install_event_handler(hdl, -1, cardbus_event_handler,
5173db86aabSstevel 		    (caddr_t)((long)ap_minor)) != 0) {
5183db86aabSstevel 			cardbus_err(dip, 4,
5193db86aabSstevel 			    "cardbus_new_slot_state: "
5203db86aabSstevel 			    "install event handler failed");
5213db86aabSstevel 			rv = HPC_ERR_FAILED;
5223db86aabSstevel 			break;
5233db86aabSstevel 		}
5243db86aabSstevel 		cbp->event_mask = (uint32_t)0xFFFFFFFF;
5253db86aabSstevel 		create_occupant_props(dip,
5263db86aabSstevel 		    makedevice(ddi_name_to_major(ddi_get_name(dip)),
5273db86aabSstevel 		    ap_minor));
5283db86aabSstevel 
5293db86aabSstevel 		/* set default auto configuration enabled flag for this slot */
5303db86aabSstevel 		cbp->auto_config = cardbus_autocfg_enabled;
5313db86aabSstevel 
5323db86aabSstevel 		/* copy the slot information */
5333db86aabSstevel 		cbp->name = (char *)kmem_alloc(strlen(slot_info->pci_slot_name)
5343db86aabSstevel 		    + 1, KM_SLEEP);
5353db86aabSstevel 		(void) strcpy(cbp->name, slot_info->pci_slot_name);
5363db86aabSstevel 		cardbus_err(cbp->cb_dip, 10,
5373db86aabSstevel 		    "cardbus_new_slot_state: cbp->name set to %s", cbp->name);
5383db86aabSstevel 
5393db86aabSstevel 		cardbus_err(dip, 4,
5403db86aabSstevel 		    "Cardbus slot \"%s\" ONLINE\n", slot_info->pci_slot_name);
5413db86aabSstevel 
5423db86aabSstevel 		cbp->ostate = AP_OSTATE_UNCONFIGURED;
5433db86aabSstevel 		cbp->rstate = AP_RSTATE_EMPTY;
5443db86aabSstevel 
5453db86aabSstevel 		break;
5463db86aabSstevel 
5473db86aabSstevel 	case HPC_SLOT_OFFLINE:
5483db86aabSstevel 		/*
5493db86aabSstevel 		 * A hot plug slot is being removed from the bus.
5503db86aabSstevel 		 * Make sure there is no occupant configured on the
5513db86aabSstevel 		 * slot before removing the AP minor node.
5523db86aabSstevel 		 */
5533db86aabSstevel 		if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
5543db86aabSstevel 			cmn_err(CE_WARN,
5553db86aabSstevel 			    "cardbus: Card is still in configured state");
5563db86aabSstevel 			rv = HPC_ERR_FAILED;
5573db86aabSstevel 			break;
5583db86aabSstevel 		}
5593db86aabSstevel 
5603db86aabSstevel 		/*
5613db86aabSstevel 		 * If the AP device is in open state then return
5623db86aabSstevel 		 * error.
5633db86aabSstevel 		 */
5643db86aabSstevel 		if (cbp->soft_state != PCIHP_SOFT_STATE_CLOSED) {
5653db86aabSstevel 			rv = HPC_ERR_FAILED;
5663db86aabSstevel 			break;
5673db86aabSstevel 		}
5683db86aabSstevel 
5693db86aabSstevel 		/* remove the minor node */
5703db86aabSstevel 		ddi_remove_minor_node(dip, cbp->name);
5713db86aabSstevel 		/* free up the memory for the name string */
5723db86aabSstevel 		kmem_free(cbp->name, strlen(cbp->name) + 1);
5733db86aabSstevel 
5743db86aabSstevel 		/* update the slot info data */
5753db86aabSstevel 		cbp->name = NULL;
5763db86aabSstevel 		cbp->slot_handle = NULL;
5773db86aabSstevel 
5783db86aabSstevel 		cardbus_err(dip, 6,
5793db86aabSstevel 		    "cardbus_new_slot_state: Cardbus slot OFFLINE");
5803db86aabSstevel 		break;
5813db86aabSstevel 
5823db86aabSstevel 	default:
5833db86aabSstevel 		cmn_err(CE_WARN,
5843db86aabSstevel 		    "cardbus_new_slot_state: unknown slot_state %d\n",
5853db86aabSstevel 		    slot_state);
5863db86aabSstevel 		rv = HPC_ERR_FAILED;
5873db86aabSstevel 	}
5883db86aabSstevel 
5893db86aabSstevel 	mutex_exit(&cbp->cb_mutex);
5903db86aabSstevel 
5913db86aabSstevel 	return (rv);
5923db86aabSstevel }
5933db86aabSstevel 
5943db86aabSstevel static int
cardbus_list_occupants(dev_info_t * dip,void * hdl)5953db86aabSstevel cardbus_list_occupants(dev_info_t *dip, void *hdl)
5963db86aabSstevel {
5973db86aabSstevel 	hpc_occupant_info_t *occupant = (hpc_occupant_info_t *)hdl;
5983db86aabSstevel 	char pn[MAXPATHLEN];
5993db86aabSstevel 
6003db86aabSstevel 	/*
6013db86aabSstevel 	 * Ignore the attachment point and pcs.
6023db86aabSstevel 	 */
6033db86aabSstevel 	if (strcmp(ddi_binding_name(dip), "pcs") == 0) {
6043db86aabSstevel 		return (DDI_WALK_CONTINUE);
6053db86aabSstevel 	}
6063db86aabSstevel 
6073db86aabSstevel 	(void) ddi_pathname(dip, pn);
6083db86aabSstevel 
6093db86aabSstevel 	occupant->id[occupant->i] = kmem_alloc(strlen(pn) + 1, KM_SLEEP);
6103db86aabSstevel 	(void) strcpy(occupant->id[occupant->i], pn);
6113db86aabSstevel 
6123db86aabSstevel 	occupant->i++;
6133db86aabSstevel 
6143db86aabSstevel 	/*
6153db86aabSstevel 	 * continue the walk to the next sibling to look for a match
6163db86aabSstevel 	 * or to find other nodes if this card is a multi-function card.
6173db86aabSstevel 	 */
6183db86aabSstevel 	return (DDI_WALK_PRUNECHILD);
6193db86aabSstevel }
6203db86aabSstevel 
6213db86aabSstevel static void
create_occupant_props(dev_info_t * self,dev_t dev)6223db86aabSstevel create_occupant_props(dev_info_t *self, dev_t dev)
6233db86aabSstevel {
6243db86aabSstevel 	hpc_occupant_info_t occupant;
6253db86aabSstevel 	int i;
6263db86aabSstevel 
6273db86aabSstevel 	occupant.i = 0;
6283db86aabSstevel 
629*3fe80ca4SDan Cross 	ndi_devi_enter(self);
6303db86aabSstevel 	ddi_walk_devs(ddi_get_child(self), cardbus_list_occupants,
6313db86aabSstevel 	    (void *)&occupant);
632*3fe80ca4SDan Cross 	ndi_devi_exit(self);
6333db86aabSstevel 
6343db86aabSstevel 	if (occupant.i == 0) {
6353db86aabSstevel 		char *c[] = { "" };
6363db86aabSstevel 		cardbus_err(self, 1, "create_occupant_props: no occupant\n");
6373db86aabSstevel 		(void) ddi_prop_update_string_array(dev, self, "pci-occupant",
6383db86aabSstevel 		    c, 1);
6393db86aabSstevel 	} else {
6403db86aabSstevel 		cardbus_err(self, 1,
6413db86aabSstevel 		    "create_occupant_props: %d occupant\n", occupant.i);
6423db86aabSstevel 		(void) ddi_prop_update_string_array(dev, self, "pci-occupant",
6433db86aabSstevel 		    occupant.id, occupant.i);
6443db86aabSstevel 	}
6453db86aabSstevel 
6463db86aabSstevel 	for (i = 0; i < occupant.i; i++) {
6473db86aabSstevel 		kmem_free(occupant.id[i], strlen(occupant.id[i]) + 1);
6483db86aabSstevel 	}
6493db86aabSstevel }
6503db86aabSstevel 
6513db86aabSstevel static void
delete_occupant_props(dev_info_t * dip,dev_t dev)6523db86aabSstevel delete_occupant_props(dev_info_t *dip, dev_t dev)
6533db86aabSstevel {
6543db86aabSstevel 	if (ddi_prop_remove(dev, dip, "pci-occupant")
6553db86aabSstevel 	    != DDI_PROP_SUCCESS)
6563db86aabSstevel 		return; /* add error handling */
6573db86aabSstevel 
6583db86aabSstevel }
6593db86aabSstevel 
6603db86aabSstevel /*
6613db86aabSstevel  * **************************************
6623db86aabSstevel  * CONFIGURE the occupant in the slot.
6633db86aabSstevel  * **************************************
6643db86aabSstevel  */
6653db86aabSstevel static int
cardbus_configure_ap(cbus_t * cbp)6663db86aabSstevel cardbus_configure_ap(cbus_t *cbp)
6673db86aabSstevel {
6683db86aabSstevel 	dev_info_t *self = cbp->cb_dip;
6693db86aabSstevel 	int rv = HPC_SUCCESS;
6703db86aabSstevel 	hpc_slot_state_t rstate;
6713db86aabSstevel 	struct cardbus_config_ctrl ctrl;
6723db86aabSstevel 
6733db86aabSstevel 	/*
6743db86aabSstevel 	 * check for valid request:
6753db86aabSstevel 	 *  1. It is a hotplug slot.
6763db86aabSstevel 	 *  2. The receptacle is in the CONNECTED state.
6773db86aabSstevel 	 */
6783db86aabSstevel 	if (cbp->slot_handle == NULL || cbp->disabled) {
6793db86aabSstevel 		return (ENXIO);
6803db86aabSstevel 	}
6813db86aabSstevel 
6823db86aabSstevel 	/*
6833db86aabSstevel 	 * If the occupant is already in (partially) configured
6843db86aabSstevel 	 * state then call the ndi_devi_online() on the device
6853db86aabSstevel 	 * subtree(s) for this attachment point.
6863db86aabSstevel 	 */
6873db86aabSstevel 
6883db86aabSstevel 	if (cbp->ostate == AP_OSTATE_CONFIGURED) {
6893db86aabSstevel 		ctrl.flags = PCICFG_FLAGS_CONTINUE;
6903db86aabSstevel 		ctrl.busno = cardbus_primary_busno(self);
6913db86aabSstevel 		ctrl.rv = NDI_SUCCESS;
6923db86aabSstevel 		ctrl.dip = NULL;
6933db86aabSstevel 		ctrl.op = PCICFG_OP_ONLINE;
6943db86aabSstevel 
695*3fe80ca4SDan Cross 		ndi_devi_enter(self);
6963db86aabSstevel 		ddi_walk_devs(ddi_get_child(self),
6975c066ec2SJerry Gilliam 		    cbus_configure, (void *)&ctrl);
698*3fe80ca4SDan Cross 		ndi_devi_exit(self);
6993db86aabSstevel 
7003db86aabSstevel 		if (cardbus_debug) {
7013db86aabSstevel 			cardbus_dump_pci_config(self);
7023db86aabSstevel 			cardbus_dump_pci_node(self);
7033db86aabSstevel 		}
7043db86aabSstevel 
7053db86aabSstevel 		if (ctrl.rv != NDI_SUCCESS) {
7063db86aabSstevel 			/*
7073db86aabSstevel 			 * one or more of the devices are not
7083db86aabSstevel 			 * onlined.
7093db86aabSstevel 			 */
7103db86aabSstevel 			cmn_err(CE_WARN, "cardbus(%s%d): failed to attach "
7113db86aabSstevel 			    "one or more drivers for the card in the slot %s",
7123db86aabSstevel 			    ddi_driver_name(self), cbp->cb_instance,
7133db86aabSstevel 			    cbp->name);
7143db86aabSstevel 		}
7153db86aabSstevel 
7163db86aabSstevel 		/* tell HPC driver that the occupant is configured */
7173db86aabSstevel 		(void) hpc_nexus_control(cbp->slot_handle,
7183db86aabSstevel 		    HPC_CTRL_DEV_CONFIGURED, NULL);
7193db86aabSstevel 		return (rv);
7203db86aabSstevel 	}
7213db86aabSstevel 
7223db86aabSstevel 	/*
7233db86aabSstevel 	 * Occupant is in the UNCONFIGURED state.
7243db86aabSstevel 	 */
7253db86aabSstevel 
7263db86aabSstevel 	/* Check if the receptacle is in the CONNECTED state. */
7273db86aabSstevel 	if (hpc_nexus_control(cbp->slot_handle,
7283db86aabSstevel 	    HPC_CTRL_GET_SLOT_STATE, (caddr_t)&rstate) != 0) {
7293db86aabSstevel 		return (ENXIO);
7303db86aabSstevel 	}
7313db86aabSstevel 
7323db86aabSstevel 	if (rstate != HPC_SLOT_CONNECTED) {
7333db86aabSstevel 		/* error. either the slot is empty or connect failed */
7343db86aabSstevel 		return (ENXIO);
7353db86aabSstevel 	}
7363db86aabSstevel 
7373db86aabSstevel 	cbp->rstate = AP_RSTATE_CONNECTED; /* record rstate */
7383db86aabSstevel 
7393db86aabSstevel 	/*
7403db86aabSstevel 	 * Call the configurator to configure the card.
7413db86aabSstevel 	 */
7423db86aabSstevel 	if (cardbus_configure(cbp) != PCICFG_SUCCESS) {
7433db86aabSstevel 		return (EIO);
7443db86aabSstevel 	}
7453db86aabSstevel 
7463db86aabSstevel 	/* record the occupant state as CONFIGURED */
7473db86aabSstevel 	cbp->ostate = AP_OSTATE_CONFIGURED;
7483db86aabSstevel 	cbp->condition = AP_COND_OK;
7493db86aabSstevel 
7503db86aabSstevel 	/* now, online all the devices in the AP */
7513db86aabSstevel 	ctrl.flags = PCICFG_FLAGS_CONTINUE;
7523db86aabSstevel 	ctrl.busno = cardbus_primary_busno(self);
7533db86aabSstevel 	ctrl.rv = NDI_SUCCESS;
7543db86aabSstevel 	ctrl.dip = NULL;
7553db86aabSstevel 	ctrl.op = PCICFG_OP_ONLINE;
7563db86aabSstevel 
757*3fe80ca4SDan Cross 	ndi_devi_enter(self);
7583db86aabSstevel 	ddi_walk_devs(ddi_get_child(self), cbus_configure, (void *)&ctrl);
759*3fe80ca4SDan Cross 	ndi_devi_exit(self);
7603db86aabSstevel 
7613db86aabSstevel 	if (cardbus_debug) {
7623db86aabSstevel 		cardbus_dump_pci_config(self);
7633db86aabSstevel 		cardbus_dump_pci_node(self);
7643db86aabSstevel 	}
7653db86aabSstevel 	if (ctrl.rv != NDI_SUCCESS) {
7663db86aabSstevel 		/*
7673db86aabSstevel 		 * one or more of the devices are not
7683db86aabSstevel 		 * ONLINE'd.
7693db86aabSstevel 		 */
7703db86aabSstevel 		cmn_err(CE_WARN, "cbhp (%s%d): failed to attach one or"
7713db86aabSstevel 		    " more drivers for the card in the slot %s",
7723db86aabSstevel 		    ddi_driver_name(cbp->cb_dip),
7733db86aabSstevel 		    cbp->cb_instance, cbp->name);
7743db86aabSstevel 		/* rv = EFAULT; */
7753db86aabSstevel 	}
7763db86aabSstevel 
7773db86aabSstevel 	/* tell HPC driver that the occupant is configured */
7783db86aabSstevel 	(void) hpc_nexus_control(cbp->slot_handle,
7795c066ec2SJerry Gilliam 	    HPC_CTRL_DEV_CONFIGURED, NULL);
7803db86aabSstevel 
7813db86aabSstevel 	return (rv);
7823db86aabSstevel }
7833db86aabSstevel 
7843db86aabSstevel /*
7853db86aabSstevel  * **************************************
7863db86aabSstevel  * UNCONFIGURE the occupant in the slot.
7873db86aabSstevel  * **************************************
7883db86aabSstevel  */
7893db86aabSstevel static int
cardbus_unconfigure_ap(cbus_t * cbp)7903db86aabSstevel cardbus_unconfigure_ap(cbus_t *cbp)
7913db86aabSstevel {
7923db86aabSstevel 	dev_info_t *self = cbp->cb_dip;
7933db86aabSstevel 	int rv = HPC_SUCCESS, nrv;
7943db86aabSstevel 
7953db86aabSstevel 	/*
7963db86aabSstevel 	 * check for valid request:
7973db86aabSstevel 	 *  1. It is a hotplug slot.
7983db86aabSstevel 	 *  2. The occupant is in the CONFIGURED state.
7993db86aabSstevel 	 */
8003db86aabSstevel 
8013db86aabSstevel 	if (cbp->slot_handle == NULL || cbp->disabled) {
8023db86aabSstevel 		return (ENXIO);
8033db86aabSstevel 	}
8043db86aabSstevel 
8053db86aabSstevel 	/*
8063db86aabSstevel 	 * If the occupant is in the CONFIGURED state then
8073db86aabSstevel 	 * call the configurator to unconfigure the slot.
8083db86aabSstevel 	 */
8093db86aabSstevel 	if (cbp->ostate == AP_OSTATE_CONFIGURED) {
8103db86aabSstevel 		/*
8113db86aabSstevel 		 * Detach all the drivers for the devices in the
8123db86aabSstevel 		 * slot.
8133db86aabSstevel 		 */
8143db86aabSstevel 		nrv = cardbus_unconfigure_node(self,
8153db86aabSstevel 		    cardbus_primary_busno(self),
8163db86aabSstevel 		    B_TRUE);
8173db86aabSstevel 
8183db86aabSstevel 		if (nrv != NDI_SUCCESS) {
8193db86aabSstevel 			/*
8203db86aabSstevel 			 * Failed to detach one or more drivers.
8213db86aabSstevel 			 * Restore the status for the drivers
8223db86aabSstevel 			 * which are offlined during this step.
8233db86aabSstevel 			 */
8243db86aabSstevel 			cmn_err(CE_WARN,
8253db86aabSstevel 			    "cbhp (%s%d): Failed to offline all devices"
8263db86aabSstevel 			    " (slot %s)", ddi_driver_name(cbp->cb_dip),
8273db86aabSstevel 			    cbp->cb_instance, cbp->name);
8283db86aabSstevel 			rv = EBUSY;
8293db86aabSstevel 		} else {
8303db86aabSstevel 
8313db86aabSstevel 			if (cardbus_unconfigure(cbp) == PCICFG_SUCCESS) {
8323db86aabSstevel 				/*
8333db86aabSstevel 				 * Now that resources are freed,
8343db86aabSstevel 				 * clear EXT and Turn LED ON.
8353db86aabSstevel 				 */
8363db86aabSstevel 				cbp->ostate = AP_OSTATE_UNCONFIGURED;
8373db86aabSstevel 				cbp->condition = AP_COND_UNKNOWN;
8383db86aabSstevel 				/*
8393db86aabSstevel 				 * send the notification of state change
8403db86aabSstevel 				 * to the HPC driver.
8413db86aabSstevel 				 */
8423db86aabSstevel 				(void) hpc_nexus_control(cbp->slot_handle,
8433db86aabSstevel 				    HPC_CTRL_DEV_UNCONFIGURED, NULL);
8443db86aabSstevel 			} else {
8453db86aabSstevel 				rv = EIO;
8463db86aabSstevel 			}
8473db86aabSstevel 		}
8483db86aabSstevel 	}
8493db86aabSstevel 
8503db86aabSstevel 	return (rv);
8513db86aabSstevel }
8523db86aabSstevel 
8533db86aabSstevel int
cbus_configure(dev_info_t * dip,void * hdl)8543db86aabSstevel cbus_configure(dev_info_t *dip, void *hdl)
8553db86aabSstevel {
8563db86aabSstevel 	pci_regspec_t *pci_rp;
8573db86aabSstevel 	int length, rc;
8583db86aabSstevel 	struct cardbus_config_ctrl *ctrl = (struct cardbus_config_ctrl *)hdl;
8593db86aabSstevel 	uint8_t bus, device, function;
8603db86aabSstevel 
8613db86aabSstevel 	/*
8623db86aabSstevel 	 * Ignore the attachment point and pcs.
8633db86aabSstevel 	 */
8643db86aabSstevel 	if (strcmp(ddi_binding_name(dip), "hp_attachment") == 0 ||
8653db86aabSstevel 	    strcmp(ddi_binding_name(dip), "pcs") == 0) {
8663db86aabSstevel 		cardbus_err(dip, 8, "cbus_configure: Ignoring\n");
8673db86aabSstevel 		return (DDI_WALK_CONTINUE);
8683db86aabSstevel 	}
8693db86aabSstevel 
8703db86aabSstevel 	cardbus_err(dip, 6, "cbus_configure\n");
8713db86aabSstevel 
8723db86aabSstevel 	ASSERT(ctrl->op == PCICFG_OP_ONLINE);
8733db86aabSstevel 
8743db86aabSstevel 	/*
8753db86aabSstevel 	 * Get the PCI device number information from the devinfo
8763db86aabSstevel 	 * node. Since the node may not have the address field
8773db86aabSstevel 	 * setup (this is done in the DDI_INITCHILD of the parent)
8783db86aabSstevel 	 * we look up the 'reg' property to decode that information.
8793db86aabSstevel 	 */
8803db86aabSstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
8813db86aabSstevel 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
8823db86aabSstevel 	    (uint_t *)&length) != DDI_PROP_SUCCESS) {
8833db86aabSstevel 		/* Porbably not a real device, like PCS for example */
8843db86aabSstevel 		if (ddi_get_child(dip) == NULL)
8853db86aabSstevel 			return (DDI_WALK_PRUNECHILD);
8863db86aabSstevel 
8873db86aabSstevel 		cardbus_err(dip, 1, "cubs_configure: Don't configure device\n");
8883db86aabSstevel 		ctrl->rv = DDI_FAILURE;
8893db86aabSstevel 		ctrl->dip = dip;
8903db86aabSstevel 		return (DDI_WALK_TERMINATE);
8913db86aabSstevel 	}
8923db86aabSstevel 
8933db86aabSstevel 	if (pci_rp->pci_phys_hi == 0)
8943db86aabSstevel 		return (DDI_WALK_CONTINUE);
8953db86aabSstevel 
8963db86aabSstevel 	/* get the pci device id information */
8973db86aabSstevel 	bus = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
8983db86aabSstevel 	device = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
8993db86aabSstevel 	function = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
9003db86aabSstevel 
9013db86aabSstevel 	/*
9023db86aabSstevel 	 * free the memory allocated by ddi_prop_lookup_int_array
9033db86aabSstevel 	 */
9043db86aabSstevel 	ddi_prop_free(pci_rp);
9053db86aabSstevel 
9063db86aabSstevel 	if (bus <= ctrl->busno)
9073db86aabSstevel 		return (DDI_WALK_CONTINUE);
9083db86aabSstevel 
9093db86aabSstevel 	cardbus_err(dip, 8,
9103db86aabSstevel 	    "cbus_configure on-line device at: "
9113db86aabSstevel 	    "[0x%x][0x%x][0x%x]\n", bus, device, function);
9123db86aabSstevel 
9133db86aabSstevel 	rc = ndi_devi_online(dip, NDI_ONLINE_ATTACH|NDI_CONFIG);
9143db86aabSstevel 
9153db86aabSstevel 	cardbus_err(dip, 7,
9163db86aabSstevel 	    "cbus_configure %s\n",
9173db86aabSstevel 	    rc == NDI_SUCCESS ? "Success": "Failure");
9183db86aabSstevel 
9193db86aabSstevel 	if (rc != NDI_SUCCESS)
9203db86aabSstevel 		return (DDI_WALK_PRUNECHILD);
9213db86aabSstevel 
9223db86aabSstevel 	return (DDI_WALK_CONTINUE);
9233db86aabSstevel }
9243db86aabSstevel 
9253db86aabSstevel int
cardbus_unconfigure_node(dev_info_t * dip,int prim_bus,boolean_t top_bridge)9263db86aabSstevel cardbus_unconfigure_node(dev_info_t *dip, int prim_bus, boolean_t top_bridge)
9273db86aabSstevel {
9283db86aabSstevel 	dev_info_t *child, *next;
9293db86aabSstevel 
9303db86aabSstevel 	cardbus_err(dip, 6, "cardbus_unconfigure_node\n");
9313db86aabSstevel 
9323db86aabSstevel 	/*
9333db86aabSstevel 	 * Ignore pcs.
9343db86aabSstevel 	 */
9353db86aabSstevel 	if (strcmp(ddi_binding_name(dip), "pcs") == 0) {
9363db86aabSstevel 		cardbus_err(dip, 8, "cardbus_unconfigure_node: Ignoring\n");
9373db86aabSstevel 		return (NDI_SUCCESS);
9383db86aabSstevel 	}
9393db86aabSstevel 
9403db86aabSstevel 	/*
9413db86aabSstevel 	 * bottom up off-line
9423db86aabSstevel 	 */
9433db86aabSstevel 	for (child = ddi_get_child(dip); child; child = next) {
9443db86aabSstevel 		int rc;
9453db86aabSstevel 		next = ddi_get_next_sibling(child);
9463db86aabSstevel 		rc = cardbus_unconfigure_node(child, prim_bus, B_FALSE);
9473db86aabSstevel 		if (rc != NDI_SUCCESS)
9483db86aabSstevel 			return (rc);
9493db86aabSstevel 	}
9503db86aabSstevel 
9513db86aabSstevel 	/*
9523db86aabSstevel 	 * Don't unconfigure the bridge itself.
9533db86aabSstevel 	 */
9543db86aabSstevel 	if (top_bridge)
9553db86aabSstevel 		return (NDI_SUCCESS);
9563db86aabSstevel 
9573db86aabSstevel 	if (cbus_unconfigure(dip, prim_bus) != NDI_SUCCESS) {
9583db86aabSstevel 		cardbus_err(dip, 1,
9593db86aabSstevel 		    "cardbus_unconfigure_node: cardbus_unconfigure failed\n");
9603db86aabSstevel 		return (NDI_FAILURE);
9613db86aabSstevel 	}
9623db86aabSstevel 	return (NDI_SUCCESS);
9633db86aabSstevel }
9643db86aabSstevel 
9653db86aabSstevel /*
9663db86aabSstevel  * This will turn  resources allocated by cbus_configure()
9673db86aabSstevel  * and remove the device tree from the attachment point
9683db86aabSstevel  * and below.  The routine assumes the devices have their
9693db86aabSstevel  * drivers detached.
9703db86aabSstevel  */
9713db86aabSstevel static int
cbus_unconfigure(dev_info_t * devi,int prim_bus)9723db86aabSstevel cbus_unconfigure(dev_info_t *devi, int prim_bus)
9733db86aabSstevel {
9743db86aabSstevel 	pci_regspec_t *pci_rp;
9753db86aabSstevel 	uint_t bus, device, func, length;
9763db86aabSstevel 	int ndi_flags = NDI_UNCONFIG;
9773db86aabSstevel 
9783db86aabSstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, devi,
9793db86aabSstevel 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
9803db86aabSstevel 	    &length) != DDI_PROP_SUCCESS) {
9813db86aabSstevel 		/*
9823db86aabSstevel 		 * This cannot be one of our devices. If it's something like a
9833db86aabSstevel 		 * SCSI device then the attempt to offline the HBA
9843db86aabSstevel 		 * (which probably is one of our devices)
9853db86aabSstevel 		 * will also do bottom up offlining. That
9863db86aabSstevel 		 * will fail if this device is busy. So always
9873db86aabSstevel 		 * return success here
9883db86aabSstevel 		 * so that the walk will continue.
9893db86aabSstevel 		 */
9903db86aabSstevel 		return (NDI_SUCCESS);
9913db86aabSstevel 	}
9923db86aabSstevel 
9933db86aabSstevel 	if (pci_rp->pci_phys_hi == 0)
9943db86aabSstevel 		return (NDI_FAILURE);
9953db86aabSstevel 
9963db86aabSstevel 	bus = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
9973db86aabSstevel 
9983db86aabSstevel 	if (bus <= prim_bus)
9993db86aabSstevel 		return (NDI_SUCCESS);
10003db86aabSstevel 
10013db86aabSstevel 	device = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
10023db86aabSstevel 	func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
10033db86aabSstevel 	ddi_prop_free(pci_rp);
10043db86aabSstevel 
10053db86aabSstevel 	cardbus_err(devi, 8,
10063db86aabSstevel 	    "cbus_unconfigure: "
10073db86aabSstevel 	    "offline bus [0x%x] device [0x%x] function [%x]\n",
10083db86aabSstevel 	    bus, device, func);
10093db86aabSstevel 	if (ndi_devi_offline(devi, ndi_flags) != NDI_SUCCESS) {
10103db86aabSstevel 		cardbus_err(devi, 1,
10113db86aabSstevel 		    "Device [0x%x] function [%x] is busy\n", device, func);
10123db86aabSstevel 		return (NDI_FAILURE);
10133db86aabSstevel 	}
10143db86aabSstevel 
10153db86aabSstevel 	cardbus_err(devi, 9,
10163db86aabSstevel 	    "Tearing down device [0x%x] function [0x%x]\n", device, func);
10173db86aabSstevel 
10183db86aabSstevel 	if (cardbus_teardown_device(devi) != PCICFG_SUCCESS) {
10193db86aabSstevel 		cardbus_err(devi, 1,
10203db86aabSstevel 		    "Failed to tear down "
10213db86aabSstevel 		    "device [0x%x] function [0x%x]\n", device, func);
10223db86aabSstevel 		return (NDI_FAILURE);
10233db86aabSstevel 	}
10243db86aabSstevel 
10253db86aabSstevel 	return (NDI_SUCCESS);
10263db86aabSstevel }
10273db86aabSstevel 
10283db86aabSstevel boolean_t
cardbus_is_cb_minor(dev_t dev)10293db86aabSstevel cardbus_is_cb_minor(dev_t dev)
10303db86aabSstevel {
10313db86aabSstevel 	return (AP_IS_CB_MINOR(getminor(dev)) ? B_TRUE : B_FALSE);
10323db86aabSstevel }
10333db86aabSstevel 
10343db86aabSstevel int
cardbus_open(dev_t * devp,int flags,int otyp,cred_t * credp)10353db86aabSstevel cardbus_open(dev_t *devp, int flags, int otyp, cred_t *credp)
10363db86aabSstevel {
10373db86aabSstevel 	cbus_t *cbp;
10383db86aabSstevel 	int minor;
10393db86aabSstevel 
10403db86aabSstevel 	_NOTE(ARGUNUSED(credp))
10413db86aabSstevel 
10423db86aabSstevel 	minor = getminor(*devp);
10433db86aabSstevel 
10443db86aabSstevel 	/*
10453db86aabSstevel 	 * Make sure the open is for the right file type.
10463db86aabSstevel 	 */
10473db86aabSstevel 	if (otyp != OTYP_CHR)
10483db86aabSstevel 	return (EINVAL);
10493db86aabSstevel 
10503db86aabSstevel 	/*
10513db86aabSstevel 	 * Get the soft state structure for the 'devctl' device.
10523db86aabSstevel 	 */
10533db86aabSstevel 	cbp = (cbus_t *)ddi_get_soft_state(cardbus_state,
10545c066ec2SJerry Gilliam 	    AP_MINOR_NUM_TO_CB_INSTANCE(minor));
10553db86aabSstevel 	if (cbp == NULL)
10563db86aabSstevel 		return (ENXIO);
10573db86aabSstevel 
10583db86aabSstevel 	mutex_enter(&cbp->cb_mutex);
10593db86aabSstevel 
10603db86aabSstevel 	/*
10613db86aabSstevel 	 * Handle the open by tracking the device state.
10623db86aabSstevel 	 *
10633db86aabSstevel 	 * Note: Needs review w.r.t exclusive access to AP or the bus.
10643db86aabSstevel 	 * Currently in the pci plug-in we don't use EXCL open at all
10653db86aabSstevel 	 * so the code below implements EXCL access on the bus.
10663db86aabSstevel 	 */
10673db86aabSstevel 
10683db86aabSstevel 	/* enforce exclusive access to the bus */
10693db86aabSstevel 	if ((cbp->soft_state == PCIHP_SOFT_STATE_OPEN_EXCL) ||
10703db86aabSstevel 	    ((flags & FEXCL) &&
10713db86aabSstevel 	    (cbp->soft_state != PCIHP_SOFT_STATE_CLOSED))) {
10723db86aabSstevel 		mutex_exit(&cbp->cb_mutex);
10733db86aabSstevel 		return (EBUSY);
10743db86aabSstevel 	}
10753db86aabSstevel 
10763db86aabSstevel 	if (flags & FEXCL)
10773db86aabSstevel 		cbp->soft_state = PCIHP_SOFT_STATE_OPEN_EXCL;
10783db86aabSstevel 	else
10793db86aabSstevel 		cbp->soft_state = PCIHP_SOFT_STATE_OPEN;
10803db86aabSstevel 
10813db86aabSstevel 	mutex_exit(&cbp->cb_mutex);
10823db86aabSstevel 	return (0);
10833db86aabSstevel }
10843db86aabSstevel 
10853db86aabSstevel /*ARGSUSED*/
10863db86aabSstevel int
cardbus_close(dev_t dev,int flags,int otyp,cred_t * credp)10873db86aabSstevel cardbus_close(dev_t dev, int flags, int otyp, cred_t *credp)
10883db86aabSstevel {
10893db86aabSstevel 	cbus_t *cbp;
10903db86aabSstevel 	int minor;
10913db86aabSstevel 
10923db86aabSstevel 	_NOTE(ARGUNUSED(credp))
10933db86aabSstevel 
10943db86aabSstevel 	minor = getminor(dev);
10953db86aabSstevel 
10963db86aabSstevel 	if (otyp != OTYP_CHR)
10973db86aabSstevel 		return (EINVAL);
10983db86aabSstevel 
10993db86aabSstevel 	cbp = (cbus_t *)ddi_get_soft_state(cardbus_state,
11003db86aabSstevel 	    AP_MINOR_NUM_TO_CB_INSTANCE(minor));
11013db86aabSstevel 	if (cbp == NULL)
11023db86aabSstevel 		return (ENXIO);
11033db86aabSstevel 
11043db86aabSstevel 	mutex_enter(&cbp->cb_mutex);
11053db86aabSstevel 	cbp->soft_state = PCIHP_SOFT_STATE_CLOSED;
11063db86aabSstevel 	mutex_exit(&cbp->cb_mutex);
11073db86aabSstevel 	return (0);
11083db86aabSstevel }
11093db86aabSstevel 
11103db86aabSstevel /*
11113db86aabSstevel  * cardbus_ioctl: devctl hotplug controls
11123db86aabSstevel  */
11133db86aabSstevel /*ARGSUSED*/
11143db86aabSstevel int
cardbus_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)11153db86aabSstevel cardbus_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1116647709cbSToomas Soome     int *rvalp)
11173db86aabSstevel {
11183db86aabSstevel 	cbus_t *cbp;
11193db86aabSstevel 	dev_info_t *self;
11203db86aabSstevel 	dev_info_t *child_dip = NULL;
11213db86aabSstevel 	struct devctl_iocdata *dcp;
11223db86aabSstevel 	uint_t bus_state;
11233db86aabSstevel 	int rv = 0;
11243db86aabSstevel 	int nrv = 0;
11253db86aabSstevel 	int ap_minor;
11263db86aabSstevel 	hpc_slot_state_t rstate;
11273db86aabSstevel 	devctl_ap_state_t ap_state;
11283db86aabSstevel 	struct hpc_control_data hpc_ctrldata;
11293db86aabSstevel 	struct hpc_led_info led_info;
11303db86aabSstevel 
11313db86aabSstevel 	_NOTE(ARGUNUSED(credp))
11323db86aabSstevel 
11333db86aabSstevel 	ap_minor = getminor(dev);
11343db86aabSstevel 	cbp = (cbus_t *)ddi_get_soft_state(cardbus_state,
11353db86aabSstevel 	    AP_MINOR_NUM_TO_CB_INSTANCE(ap_minor));
11363db86aabSstevel 	if (cbp == NULL)
11373db86aabSstevel 		return (ENXIO);
11383db86aabSstevel 
11393db86aabSstevel 	self = cbp->cb_dip;
11403db86aabSstevel 	/*
11413db86aabSstevel 	 * read devctl ioctl data
11423db86aabSstevel 	 */
11433db86aabSstevel 	if ((cmd != DEVCTL_AP_CONTROL) && ndi_dc_allochdl((void *)arg,
11443db86aabSstevel 	    &dcp) != NDI_SUCCESS)
11453db86aabSstevel 		return (EFAULT);
11463db86aabSstevel 
11473db86aabSstevel #ifdef CARDBUS_DEBUG
11483db86aabSstevel {
11493db86aabSstevel 	char *cmd_name;
11503db86aabSstevel 
11513db86aabSstevel 	switch (cmd) {
11523db86aabSstevel 	case DEVCTL_DEVICE_GETSTATE: cmd_name = "DEVCTL_DEVICE_GETSTATE"; break;
11533db86aabSstevel 	case DEVCTL_DEVICE_ONLINE: cmd_name = "DEVCTL_DEVICE_ONLINE"; break;
11543db86aabSstevel 	case DEVCTL_DEVICE_OFFLINE: cmd_name = "DEVCTL_DEVICE_OFFLINE"; break;
11553db86aabSstevel 	case DEVCTL_DEVICE_RESET: cmd_name = "DEVCTL_DEVICE_RESET"; break;
11563db86aabSstevel 	case DEVCTL_BUS_QUIESCE: cmd_name = "DEVCTL_BUS_QUIESCE"; break;
11573db86aabSstevel 	case DEVCTL_BUS_UNQUIESCE: cmd_name = "DEVCTL_BUS_UNQUIESCE"; break;
11583db86aabSstevel 	case DEVCTL_BUS_RESET: cmd_name = "DEVCTL_BUS_RESET"; break;
11593db86aabSstevel 	case DEVCTL_BUS_RESETALL: cmd_name = "DEVCTL_BUS_RESETALL"; break;
11603db86aabSstevel 	case DEVCTL_BUS_GETSTATE: cmd_name = "DEVCTL_BUS_GETSTATE"; break;
11613db86aabSstevel 	case DEVCTL_AP_CONNECT: cmd_name = "DEVCTL_AP_CONNECT"; break;
11623db86aabSstevel 	case DEVCTL_AP_DISCONNECT: cmd_name = "DEVCTL_AP_DISCONNECT"; break;
11633db86aabSstevel 	case DEVCTL_AP_INSERT: cmd_name = "DEVCTL_AP_INSERT"; break;
11643db86aabSstevel 	case DEVCTL_AP_REMOVE: cmd_name = "DEVCTL_AP_REMOVE"; break;
11653db86aabSstevel 	case DEVCTL_AP_CONFIGURE: cmd_name = "DEVCTL_AP_CONFIGURE"; break;
11663db86aabSstevel 	case DEVCTL_AP_UNCONFIGURE: cmd_name = "DEVCTL_AP_UNCONFIGURE"; break;
11673db86aabSstevel 	case DEVCTL_AP_GETSTATE: cmd_name = "DEVCTL_AP_GETSTATE"; break;
11683db86aabSstevel 	case DEVCTL_AP_CONTROL: cmd_name = "DEVCTL_AP_CONTROL"; break;
11693db86aabSstevel 	default: cmd_name = "Unknown"; break;
11703db86aabSstevel 	}
11713db86aabSstevel 	cardbus_err(cbp->cb_dip, 7,
11723db86aabSstevel 	    "cardbus_ioctl: cmd = 0x%x, \"%s\"", cmd, cmd_name);
11733db86aabSstevel }
11743db86aabSstevel #endif
11753db86aabSstevel 
11763db86aabSstevel 	switch (cmd) {
11773db86aabSstevel 	case DEVCTL_DEVICE_GETSTATE:
11783db86aabSstevel 	case DEVCTL_DEVICE_ONLINE:
11793db86aabSstevel 	case DEVCTL_DEVICE_OFFLINE:
11803db86aabSstevel 	case DEVCTL_BUS_GETSTATE:
11813db86aabSstevel 		rv = ndi_devctl_ioctl(self, cmd, arg, mode, 0);
11823db86aabSstevel 		ndi_dc_freehdl(dcp);
11833db86aabSstevel 		return (rv);
11843db86aabSstevel 	default:
11853db86aabSstevel 		break;
11863db86aabSstevel 	}
11873db86aabSstevel 
11883db86aabSstevel 	switch (cmd) {
11893db86aabSstevel 	case DEVCTL_DEVICE_RESET:
11903db86aabSstevel 		rv = ENOTSUP;
11913db86aabSstevel 		break;
11923db86aabSstevel 
11933db86aabSstevel 	case DEVCTL_BUS_QUIESCE:
11943db86aabSstevel 		if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS)
11953db86aabSstevel 			if (bus_state == BUS_QUIESCED)
11963db86aabSstevel 				break;
11973db86aabSstevel 		(void) ndi_set_bus_state(self, BUS_QUIESCED);
11983db86aabSstevel 		break;
11993db86aabSstevel 
12003db86aabSstevel 	case DEVCTL_BUS_UNQUIESCE:
12013db86aabSstevel 		if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS)
12023db86aabSstevel 			if (bus_state == BUS_ACTIVE)
12033db86aabSstevel 				break;
12043db86aabSstevel 		(void) ndi_set_bus_state(self, BUS_ACTIVE);
12053db86aabSstevel 		break;
12063db86aabSstevel 
12073db86aabSstevel 	case DEVCTL_BUS_RESET:
12083db86aabSstevel 		rv = ENOTSUP;
12093db86aabSstevel 		break;
12103db86aabSstevel 
12113db86aabSstevel 	case DEVCTL_BUS_RESETALL:
12123db86aabSstevel 		rv = ENOTSUP;
12133db86aabSstevel 		break;
12143db86aabSstevel 
12153db86aabSstevel 	case DEVCTL_AP_CONNECT:
12163db86aabSstevel 	case DEVCTL_AP_DISCONNECT:
12173db86aabSstevel 		/*
12183db86aabSstevel 		 * CONNECT(DISCONNECT) the hot plug slot to(from) the bus.
12193db86aabSstevel 		 */
12203db86aabSstevel 	case DEVCTL_AP_INSERT:
12213db86aabSstevel 	case DEVCTL_AP_REMOVE:
12223db86aabSstevel 		/*
12233db86aabSstevel 		 * Prepare the slot for INSERT/REMOVE operation.
12243db86aabSstevel 		 */
12253db86aabSstevel 
12263db86aabSstevel 		/*
12273db86aabSstevel 		 * check for valid request:
1228647709cbSToomas Soome 		 *	1. It is a hotplug slot.
1229647709cbSToomas Soome 		 *	2. The slot has no occupant that is in
1230647709cbSToomas Soome 		 *	the 'configured' state.
12313db86aabSstevel 		 *
12323db86aabSstevel 		 * The lower 8 bits of the minor number is the PCI
12333db86aabSstevel 		 * device number for the slot.
12343db86aabSstevel 		 */
12353db86aabSstevel 		if ((cbp->slot_handle == NULL) || cbp->disabled) {
12363db86aabSstevel 			rv = ENXIO;
12373db86aabSstevel 			break;
12383db86aabSstevel 		}
12393db86aabSstevel 
12403db86aabSstevel 		/* the slot occupant must be in the UNCONFIGURED state */
12413db86aabSstevel 		if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
12423db86aabSstevel 			rv = EINVAL;
12433db86aabSstevel 			break;
12443db86aabSstevel 		}
12453db86aabSstevel 
12463db86aabSstevel 		/*
12473db86aabSstevel 		 * Call the HPC driver to perform the operation on the slot.
12483db86aabSstevel 		 */
12493db86aabSstevel 		mutex_enter(&cbp->cb_mutex);
12503db86aabSstevel 		switch (cmd) {
12513db86aabSstevel 		case DEVCTL_AP_INSERT:
12523db86aabSstevel 			rv = hpc_nexus_insert(cbp->slot_handle, NULL, 0);
12533db86aabSstevel 			break;
12543db86aabSstevel 		case DEVCTL_AP_REMOVE:
12553db86aabSstevel 			rv = hpc_nexus_remove(cbp->slot_handle, NULL, 0);
12563db86aabSstevel 			break;
12573db86aabSstevel 		case DEVCTL_AP_CONNECT:
12583db86aabSstevel 			if ((rv = hpc_nexus_connect(cbp->slot_handle,
12593db86aabSstevel 			    NULL, 0)) == 0)
12603db86aabSstevel 				cbp->rstate = AP_RSTATE_CONNECTED;
12613db86aabSstevel 			break;
12623db86aabSstevel 		case DEVCTL_AP_DISCONNECT:
12633db86aabSstevel 			if ((rv = hpc_nexus_disconnect(cbp->slot_handle,
12643db86aabSstevel 			    NULL, 0)) == 0)
12653db86aabSstevel 				cbp->rstate = AP_RSTATE_DISCONNECTED;
12663db86aabSstevel 			break;
12673db86aabSstevel 		}
12683db86aabSstevel 		mutex_exit(&cbp->cb_mutex);
12693db86aabSstevel 
12703db86aabSstevel 		switch (rv) {
12713db86aabSstevel 		case HPC_ERR_INVALID:
12723db86aabSstevel 			rv = ENXIO;
12733db86aabSstevel 			break;
12743db86aabSstevel 		case HPC_ERR_NOTSUPPORTED:
12753db86aabSstevel 			rv = ENOTSUP;
12763db86aabSstevel 			break;
12773db86aabSstevel 		case HPC_ERR_FAILED:
12783db86aabSstevel 			rv = EIO;
12793db86aabSstevel 			break;
12803db86aabSstevel 		}
12813db86aabSstevel 
12823db86aabSstevel 		break;
12833db86aabSstevel 
12843db86aabSstevel 	case DEVCTL_AP_CONFIGURE:
12853db86aabSstevel 		/*
12863db86aabSstevel 		 * **************************************
12873db86aabSstevel 		 * CONFIGURE the occupant in the slot.
12883db86aabSstevel 		 * **************************************
12893db86aabSstevel 		 */
12903db86aabSstevel 
12913db86aabSstevel 		mutex_enter(&cbp->cb_mutex);
12923db86aabSstevel 		if ((nrv = cardbus_configure_ap(cbp)) == HPC_SUCCESS) {
12933db86aabSstevel 			create_occupant_props(cbp->cb_dip, dev);
12943db86aabSstevel 		} else
12953db86aabSstevel 			rv = nrv;
12963db86aabSstevel 		mutex_exit(&cbp->cb_mutex);
12973db86aabSstevel 		break;
12983db86aabSstevel 
12993db86aabSstevel 	case DEVCTL_AP_UNCONFIGURE:
13003db86aabSstevel 		/*
13013db86aabSstevel 		 * **************************************
13023db86aabSstevel 		 * UNCONFIGURE the occupant in the slot.
13033db86aabSstevel 		 * **************************************
13043db86aabSstevel 		 */
13053db86aabSstevel 
13063db86aabSstevel 		mutex_enter(&cbp->cb_mutex);
13073db86aabSstevel 		if ((nrv = cardbus_unconfigure_ap(cbp)) == HPC_SUCCESS) {
13083db86aabSstevel 			delete_occupant_props(cbp->cb_dip, dev);
13093db86aabSstevel 		} else
13103db86aabSstevel 			rv = nrv;
13113db86aabSstevel 		mutex_exit(&cbp->cb_mutex);
13123db86aabSstevel 		break;
13133db86aabSstevel 
13143db86aabSstevel 	case DEVCTL_AP_GETSTATE:
13153db86aabSstevel 	    {
13163db86aabSstevel 		int mutex_held;
13173db86aabSstevel 
13183db86aabSstevel 		/*
13193db86aabSstevel 		 * return the state of Attachment Point.
13203db86aabSstevel 		 *
13213db86aabSstevel 		 * If the occupant is in UNCONFIGURED state then
13223db86aabSstevel 		 * we should get the receptacle state from the
13233db86aabSstevel 		 * HPC driver because the receptacle state
13243db86aabSstevel 		 * maintained in the nexus may not be accurate.
13253db86aabSstevel 		 */
13263db86aabSstevel 
13273db86aabSstevel 		/*
13283db86aabSstevel 		 * check for valid request:
1329647709cbSToomas Soome 		 *	1. It is a hotplug slot.
13303db86aabSstevel 		 */
13313db86aabSstevel 		if (cbp->slot_handle == NULL) {
13323db86aabSstevel 			rv = ENXIO;
13333db86aabSstevel 			break;
13343db86aabSstevel 		}
13353db86aabSstevel 
13363db86aabSstevel 		/* try to acquire the slot mutex */
13373db86aabSstevel 		mutex_held = mutex_tryenter(&cbp->cb_mutex);
13383db86aabSstevel 
13393db86aabSstevel 		if (cbp->ostate == AP_OSTATE_UNCONFIGURED) {
13403db86aabSstevel 			if (hpc_nexus_control(cbp->slot_handle,
13413db86aabSstevel 			    HPC_CTRL_GET_SLOT_STATE,
13423db86aabSstevel 			    (caddr_t)&rstate) != 0) {
13433db86aabSstevel 				rv = ENXIO;
13443db86aabSstevel 				if (mutex_held)
13453db86aabSstevel 					mutex_exit(&cbp->cb_mutex);
13463db86aabSstevel 				break;
13473db86aabSstevel 			}
13483db86aabSstevel 			cbp->rstate = (ap_rstate_t)rstate;
13493db86aabSstevel 		}
13503db86aabSstevel 
13513db86aabSstevel 		ap_state.ap_rstate = cbp->rstate;
13523db86aabSstevel 		ap_state.ap_ostate = cbp->ostate;
13533db86aabSstevel 		ap_state.ap_condition = cbp->condition;
13543db86aabSstevel 		ap_state.ap_last_change = 0;
13553db86aabSstevel 		ap_state.ap_error_code = 0;
13563db86aabSstevel 		if (mutex_held)
13573db86aabSstevel 			ap_state.ap_in_transition = 0; /* AP is not busy */
13583db86aabSstevel 		else
13593db86aabSstevel 			ap_state.ap_in_transition = 1; /* AP is busy */
13603db86aabSstevel 
13613db86aabSstevel 		if (mutex_held)
13623db86aabSstevel 			mutex_exit(&cbp->cb_mutex);
13633db86aabSstevel 
13643db86aabSstevel 		/* copy the return-AP-state information to the user space */
13653db86aabSstevel 		if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS)
13663db86aabSstevel 			rv = ENXIO;
13673db86aabSstevel 
13683db86aabSstevel 		break;
13693db86aabSstevel 
13703db86aabSstevel 	    }
13713db86aabSstevel 
13723db86aabSstevel 	case DEVCTL_AP_CONTROL:
13733db86aabSstevel 		/*
13743db86aabSstevel 		 * HPC control functions:
1375647709cbSToomas Soome 		 *	HPC_CTRL_ENABLE_SLOT/HPC_CTRL_DISABLE_SLOT
1376647709cbSToomas Soome 		 *		Changes the state of the slot and preserves
1377647709cbSToomas Soome 		 *		the state across the reboot.
1378647709cbSToomas Soome 		 *	HPC_CTRL_ENABLE_AUTOCFG/HPC_CTRL_DISABLE_AUTOCFG
1379647709cbSToomas Soome 		 *		Enables or disables the auto configuration
1380647709cbSToomas Soome 		 *		of hot plugged occupant if the hardware
1381647709cbSToomas Soome 		 *		supports notification of the hot plug
1382647709cbSToomas Soome 		 *		events.
1383647709cbSToomas Soome 		 *	HPC_CTRL_GET_LED_STATE/HPC_CTRL_SET_LED_STATE
1384647709cbSToomas Soome 		 *		Controls the state of an LED.
1385647709cbSToomas Soome 		 *	HPC_CTRL_GET_SLOT_INFO
1386647709cbSToomas Soome 		 *		Get slot information data structure
1387647709cbSToomas Soome 		 *		(hpc_slot_info_t).
1388647709cbSToomas Soome 		 *	HPC_CTRL_GET_BOARD_TYPE
1389647709cbSToomas Soome 		 *		Get board type information (hpc_board_type_t).
1390647709cbSToomas Soome 		 *	HPC_CTRL_GET_CARD_INFO
1391647709cbSToomas Soome 		 *		Get card information (hpc_card_info_t).
13923db86aabSstevel 		 *
13933db86aabSstevel 		 * These control functions are used by the cfgadm plug-in
13943db86aabSstevel 		 * to implement "-x" and "-v" options.
13953db86aabSstevel 		 */
13963db86aabSstevel 
13973db86aabSstevel 		/* copy user ioctl data first */
13983db86aabSstevel #ifdef _MULTI_DATAMODEL
13993db86aabSstevel 		if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
14003db86aabSstevel 			struct hpc_control32_data hpc_ctrldata32;
14013db86aabSstevel 
14023db86aabSstevel 			if (copyin((void *)arg, (void *)&hpc_ctrldata32,
14033db86aabSstevel 			    sizeof (struct hpc_control32_data)) != 0) {
14043db86aabSstevel 				rv = EFAULT;
14053db86aabSstevel 				break;
14063db86aabSstevel 			}
14073db86aabSstevel 			hpc_ctrldata.cmd = hpc_ctrldata32.cmd;
14083db86aabSstevel 			hpc_ctrldata.data =
14093db86aabSstevel 			    (void *)(intptr_t)hpc_ctrldata32.data;
14103db86aabSstevel 		}
14113db86aabSstevel #else
14123db86aabSstevel 		if (copyin((void *)arg, (void *)&hpc_ctrldata,
14133db86aabSstevel 		    sizeof (struct hpc_control_data)) != 0) {
14143db86aabSstevel 			rv = EFAULT;
14153db86aabSstevel 			break;
14163db86aabSstevel 		}
14173db86aabSstevel #endif
14183db86aabSstevel 
14193db86aabSstevel #ifdef CARDBUS_DEBUG
14203db86aabSstevel {
14213db86aabSstevel 		char *hpc_name;
14223db86aabSstevel 		switch (hpc_ctrldata.cmd) {
14233db86aabSstevel 		case HPC_CTRL_GET_LED_STATE:
14243db86aabSstevel 			hpc_name = "HPC_CTRL_GET_LED_STATE";
14253db86aabSstevel 			break;
14263db86aabSstevel 		case HPC_CTRL_SET_LED_STATE:
14273db86aabSstevel 			hpc_name = "HPC_CTRL_SET_LED_STATE";
14283db86aabSstevel 			break;
14293db86aabSstevel 		case HPC_CTRL_ENABLE_SLOT:
14303db86aabSstevel 			hpc_name = "HPC_CTRL_ENABLE_SLOT";
14313db86aabSstevel 			break;
14323db86aabSstevel 		case HPC_CTRL_DISABLE_SLOT:
14333db86aabSstevel 			hpc_name = "HPC_CTRL_DISABLE_SLOT";
14343db86aabSstevel 			break;
14353db86aabSstevel 		case HPC_CTRL_ENABLE_AUTOCFG:
14363db86aabSstevel 			hpc_name = "HPC_CTRL_ENABLE_AUTOCFG";
14373db86aabSstevel 			break;
14383db86aabSstevel 		case HPC_CTRL_DISABLE_AUTOCFG:
14393db86aabSstevel 			hpc_name = "HPC_CTRL_DISABLE_AUTOCFG";
14403db86aabSstevel 			break;
14413db86aabSstevel 		case HPC_CTRL_GET_BOARD_TYPE:
14423db86aabSstevel 			hpc_name = "HPC_CTRL_GET_BOARD_TYPE";
14433db86aabSstevel 			break;
14443db86aabSstevel 		case HPC_CTRL_GET_SLOT_INFO:
14453db86aabSstevel 			hpc_name = "HPC_CTRL_GET_SLOT_INFO";
14463db86aabSstevel 			break;
14473db86aabSstevel 		case HPC_CTRL_GET_CARD_INFO:
14483db86aabSstevel 			hpc_name = "HPC_CTRL_GET_CARD_INFO";
14493db86aabSstevel 			break;
14503db86aabSstevel 		default: hpc_name = "Unknown"; break;
14513db86aabSstevel 		}
14523db86aabSstevel 		cardbus_err(cbp->cb_dip, 7,
14533db86aabSstevel 		    "cardbus_ioctl: HP Control cmd 0x%x - \"%s\"",
14543db86aabSstevel 		    hpc_ctrldata.cmd, hpc_name);
14553db86aabSstevel }
14563db86aabSstevel #endif
14573db86aabSstevel 		/*
14583db86aabSstevel 		 * check for valid request:
1459647709cbSToomas Soome 		 *	1. It is a hotplug slot.
14603db86aabSstevel 		 */
14613db86aabSstevel 		if (cbp->slot_handle == NULL) {
14623db86aabSstevel 			rv = ENXIO;
14633db86aabSstevel 			break;
14643db86aabSstevel 		}
14653db86aabSstevel 
14663db86aabSstevel 		mutex_enter(&cbp->cb_mutex);
14673db86aabSstevel 		switch (hpc_ctrldata.cmd) {
14683db86aabSstevel 		case HPC_CTRL_GET_LED_STATE:
14693db86aabSstevel 			/* copy the led info from the user space */
14703db86aabSstevel 			if (copyin(hpc_ctrldata.data, (void *)&led_info,
14713db86aabSstevel 			    sizeof (hpc_led_info_t)) != 0) {
14723db86aabSstevel 				rv = ENXIO;
14733db86aabSstevel 				break;
14743db86aabSstevel 			}
14753db86aabSstevel 
14763db86aabSstevel 			/* get the state of LED information */
14773db86aabSstevel 			if (hpc_nexus_control(cbp->slot_handle,
14783db86aabSstevel 			    HPC_CTRL_GET_LED_STATE,
14793db86aabSstevel 			    (caddr_t)&led_info) != 0) {
14803db86aabSstevel 				rv = ENXIO;
14813db86aabSstevel 				break;
14823db86aabSstevel 			}
14833db86aabSstevel 
14843db86aabSstevel 			/* copy the led info to the user space */
14853db86aabSstevel 			if (copyout((void *)&led_info, hpc_ctrldata.data,
14863db86aabSstevel 			    sizeof (hpc_led_info_t)) != 0) {
14873db86aabSstevel 				rv = ENXIO;
14883db86aabSstevel 				break;
14893db86aabSstevel 			}
14903db86aabSstevel 			break;
14913db86aabSstevel 
14923db86aabSstevel 		case HPC_CTRL_SET_LED_STATE:
14933db86aabSstevel 			/* copy the led info from the user space */
14943db86aabSstevel 			if (copyin(hpc_ctrldata.data, (void *)&led_info,
14953db86aabSstevel 			    sizeof (hpc_led_info_t)) != 0) {
14963db86aabSstevel 				rv = ENXIO;
14973db86aabSstevel 				break;
14983db86aabSstevel 			}
14993db86aabSstevel 
15003db86aabSstevel 			/* set the state of an LED */
15013db86aabSstevel 			if (hpc_nexus_control(cbp->slot_handle,
15023db86aabSstevel 			    HPC_CTRL_SET_LED_STATE,
15033db86aabSstevel 			    (caddr_t)&led_info) != 0) {
15043db86aabSstevel 				rv = ENXIO;
15053db86aabSstevel 				break;
15063db86aabSstevel 			}
15073db86aabSstevel 
15083db86aabSstevel 			break;
15093db86aabSstevel 
15103db86aabSstevel 		case HPC_CTRL_ENABLE_SLOT:
15113db86aabSstevel 			/*
15123db86aabSstevel 			 * Enable the slot for hotplug operations.
15133db86aabSstevel 			 */
15143db86aabSstevel 			cbp->disabled = B_FALSE;
15153db86aabSstevel 
15163db86aabSstevel 			/* tell the HPC driver also */
15173db86aabSstevel 			(void) hpc_nexus_control(cbp->slot_handle,
15183db86aabSstevel 				HPC_CTRL_ENABLE_SLOT, NULL);
15193db86aabSstevel 
15203db86aabSstevel 			break;
15213db86aabSstevel 
15223db86aabSstevel 		case HPC_CTRL_DISABLE_SLOT:
15233db86aabSstevel 			/*
15243db86aabSstevel 			 * Disable the slot for hotplug operations.
15253db86aabSstevel 			 */
15263db86aabSstevel 			cbp->disabled = B_TRUE;
15273db86aabSstevel 
15283db86aabSstevel 			/* tell the HPC driver also */
15293db86aabSstevel 			(void) hpc_nexus_control(cbp->slot_handle,
15303db86aabSstevel 				HPC_CTRL_DISABLE_SLOT, NULL);
15313db86aabSstevel 
15323db86aabSstevel 			break;
15333db86aabSstevel 
15343db86aabSstevel 		case HPC_CTRL_ENABLE_AUTOCFG:
15353db86aabSstevel 			/*
15363db86aabSstevel 			 * Enable auto configuration on this slot.
15373db86aabSstevel 			 */
15383db86aabSstevel 			cbp->auto_config = B_TRUE;
15393db86aabSstevel 
15403db86aabSstevel 			/* tell the HPC driver also */
15413db86aabSstevel 			(void) hpc_nexus_control(cbp->slot_handle,
15423db86aabSstevel 				HPC_CTRL_ENABLE_AUTOCFG, NULL);
15433db86aabSstevel 			break;
15443db86aabSstevel 
15453db86aabSstevel 		case HPC_CTRL_DISABLE_AUTOCFG:
15463db86aabSstevel 			/*
15473db86aabSstevel 			 * Disable auto configuration on this slot.
15483db86aabSstevel 			 */
15493db86aabSstevel 			cbp->auto_config = B_FALSE;
15503db86aabSstevel 
15513db86aabSstevel 			/* tell the HPC driver also */
15523db86aabSstevel 			(void) hpc_nexus_control(cbp->slot_handle,
15533db86aabSstevel 				HPC_CTRL_DISABLE_AUTOCFG, NULL);
15543db86aabSstevel 
15553db86aabSstevel 			break;
15563db86aabSstevel 
15573db86aabSstevel 		case HPC_CTRL_GET_BOARD_TYPE:
15583db86aabSstevel 		    {
15593db86aabSstevel 			hpc_board_type_t board_type;
15603db86aabSstevel 
15613db86aabSstevel 			/*
15623db86aabSstevel 			 * Get board type data structure, hpc_board_type_t.
15633db86aabSstevel 			 */
15643db86aabSstevel 			if (hpc_nexus_control(cbp->slot_handle,
15653db86aabSstevel 			    HPC_CTRL_GET_BOARD_TYPE,
15663db86aabSstevel 			    (caddr_t)&board_type) != 0) {
15673db86aabSstevel 				rv = ENXIO;
15683db86aabSstevel 				break;
15693db86aabSstevel 			}
15703db86aabSstevel 
15713db86aabSstevel 			/* copy the board type info to the user space */
15723db86aabSstevel 			if (copyout((void *)&board_type, hpc_ctrldata.data,
15733db86aabSstevel 			    sizeof (hpc_board_type_t)) != 0) {
15743db86aabSstevel 				rv = ENXIO;
15753db86aabSstevel 				break;
15763db86aabSstevel 			}
15773db86aabSstevel 
15783db86aabSstevel 			break;
15793db86aabSstevel 		    }
15803db86aabSstevel 
15813db86aabSstevel 		case HPC_CTRL_GET_SLOT_INFO:
15823db86aabSstevel 		    {
15833db86aabSstevel 			hpc_slot_info_t slot_info;
15843db86aabSstevel 
15853db86aabSstevel 			/*
15863db86aabSstevel 			 * Get slot information structure, hpc_slot_info_t.
15873db86aabSstevel 			 */
15883db86aabSstevel 			slot_info.version = HPC_SLOT_INFO_VERSION;
15893db86aabSstevel 			slot_info.slot_type = 0;
15903db86aabSstevel 			slot_info.pci_slot_capabilities = 0;
15913db86aabSstevel 			slot_info.pci_dev_num =
15923db86aabSstevel 				(uint16_t)AP_MINOR_NUM_TO_CB_INSTANCE(ap_minor);
15933db86aabSstevel 			(void) strcpy(slot_info.pci_slot_name, cbp->name);
15943db86aabSstevel 
15953db86aabSstevel 			/* copy the slot info structure to the user space */
15963db86aabSstevel 			if (copyout((void *)&slot_info, hpc_ctrldata.data,
15973db86aabSstevel 			    sizeof (hpc_slot_info_t)) != 0) {
15983db86aabSstevel 				rv = ENXIO;
15993db86aabSstevel 				break;
16003db86aabSstevel 			}
16013db86aabSstevel 
16023db86aabSstevel 			break;
16033db86aabSstevel 		    }
16043db86aabSstevel 
16053db86aabSstevel 		case HPC_CTRL_GET_CARD_INFO:
16063db86aabSstevel 		    {
16073db86aabSstevel 			hpc_card_info_t card_info;
16083db86aabSstevel 			ddi_acc_handle_t handle;
16093db86aabSstevel 
16103db86aabSstevel 			/*
16113db86aabSstevel 			 * Get card information structure, hpc_card_info_t.
16123db86aabSstevel 			 */
16133db86aabSstevel 
16140d282d13Srw 			if (cbp->card_present == B_FALSE) {
16150d282d13Srw 				rv = ENXIO;
16160d282d13Srw 				break;
16170d282d13Srw 			}
16183db86aabSstevel 			/* verify that the card is configured */
16193db86aabSstevel 			if (cbp->ostate != AP_OSTATE_CONFIGURED) {
16203db86aabSstevel 				/* either the card is not present or */
16213db86aabSstevel 				/* it is not configured. */
16223db86aabSstevel 				rv = ENXIO;
16233db86aabSstevel 				break;
16243db86aabSstevel 			}
16253db86aabSstevel 
16263db86aabSstevel 			/* get the information from the PCI config header */
16273db86aabSstevel 			/* for the function 0. */
16283db86aabSstevel 			for (child_dip = ddi_get_child(cbp->cb_dip); child_dip;
16293db86aabSstevel 			    child_dip = ddi_get_next_sibling(child_dip))
16303db86aabSstevel 				if (strcmp("pcs", ddi_get_name(child_dip)))
16313db86aabSstevel 					break;
16323db86aabSstevel 
16333db86aabSstevel 			if (!child_dip) {
16343db86aabSstevel 				rv = ENXIO;
16353db86aabSstevel 				break;
16363db86aabSstevel 			}
16373db86aabSstevel 
16383db86aabSstevel 			if (pci_config_setup(child_dip, &handle)
16393db86aabSstevel 			    != DDI_SUCCESS) {
16403db86aabSstevel 				rv = EIO;
16413db86aabSstevel 				break;
16423db86aabSstevel 			}
16433db86aabSstevel 			card_info.prog_class = pci_config_get8(handle,
16443db86aabSstevel 							PCI_CONF_PROGCLASS);
16453db86aabSstevel 			card_info.base_class = pci_config_get8(handle,
16463db86aabSstevel 							PCI_CONF_BASCLASS);
16473db86aabSstevel 			card_info.sub_class = pci_config_get8(handle,
16483db86aabSstevel 							PCI_CONF_SUBCLASS);
16493db86aabSstevel 			card_info.header_type = pci_config_get8(handle,
16503db86aabSstevel 							PCI_CONF_HEADER);
16513db86aabSstevel 			pci_config_teardown(&handle);
16523db86aabSstevel 
16533db86aabSstevel 			/* copy the card info structure to the user space */
16543db86aabSstevel 			if (copyout((void *)&card_info, hpc_ctrldata.data,
16553db86aabSstevel 			    sizeof (hpc_card_info_t)) != 0) {
16563db86aabSstevel 				rv = ENXIO;
16573db86aabSstevel 				break;
16583db86aabSstevel 			}
16593db86aabSstevel 
16603db86aabSstevel 			break;
16613db86aabSstevel 		    }
16623db86aabSstevel 
16633db86aabSstevel 		default:
16643db86aabSstevel 			rv = EINVAL;
16653db86aabSstevel 			break;
16663db86aabSstevel 		}
16673db86aabSstevel 
16683db86aabSstevel 		mutex_exit(&cbp->cb_mutex);
16693db86aabSstevel 		break;
16703db86aabSstevel 
16713db86aabSstevel 	default:
16723db86aabSstevel 		rv = ENOTTY;
16733db86aabSstevel 	}
16743db86aabSstevel 
16753db86aabSstevel 	if (cmd != DEVCTL_AP_CONTROL)
16763db86aabSstevel 		ndi_dc_freehdl(dcp);
16773db86aabSstevel 
16783db86aabSstevel 	cardbus_err(cbp->cb_dip, 7,
16793db86aabSstevel 	    "cardbus_ioctl: rv = 0x%x", rv);
16803db86aabSstevel 
16813db86aabSstevel 	return (rv);
16823db86aabSstevel }
16833db86aabSstevel 
16843db86aabSstevel struct cardbus_pci_desc {
16853db86aabSstevel 	char	*name;
16863db86aabSstevel 	ushort_t	offset;
16873db86aabSstevel 	int	(*cfg_get_func)();
16883db86aabSstevel 	char	*fmt;
16893db86aabSstevel };
16903db86aabSstevel 
1691647709cbSToomas Soome #define	CFG_GET(f)	((int(*)())(uintptr_t)f)
1692647709cbSToomas Soome 
16933db86aabSstevel static struct cardbus_pci_desc generic_pci_cfg[] = {
1694647709cbSToomas Soome 	    { "VendorId    =", 0, CFG_GET(pci_config_get16), "%s 0x%04x" },
1695647709cbSToomas Soome 	    { "DeviceId    =", 2, CFG_GET(pci_config_get16), "%s 0x%04x" },
1696647709cbSToomas Soome 	    { "Command     =", 4, CFG_GET(pci_config_get16), "%s 0x%04x" },
1697647709cbSToomas Soome 	    { "Status      =", 6, CFG_GET(pci_config_get16), "%s 0x%04x" },
1698647709cbSToomas Soome 	    { "Latency     =", 0xd, CFG_GET(pci_config_get8), "%s 0x%02x" },
1699647709cbSToomas Soome 	    { "BASE0       =", 0x10, CFG_GET(pci_config_get32), "%s 0x%08x" },
1700647709cbSToomas Soome 	    { "BASE1       =", 0x14, CFG_GET(pci_config_get32), "%s 0x%08x" },
1701647709cbSToomas Soome 	    { "BASE2       =", 0x18, CFG_GET(pci_config_get32), "%s 0x%08x" },
1702647709cbSToomas Soome 	    { "BASE3       =", 0x1c, CFG_GET(pci_config_get32), "%s 0x%08x" },
1703647709cbSToomas Soome 	    { "BASE4       =", 0x20, CFG_GET(pci_config_get32), "%s 0x%08x" },
1704647709cbSToomas Soome 	    { "CIS Pointer =", 0x28, CFG_GET(pci_config_get32), "%s 0x%08x" },
1705647709cbSToomas Soome 	    { "ILINE       =", 0x3c, CFG_GET(pci_config_get8), "%s 0x%02x" },
1706647709cbSToomas Soome 	    { "IPIN        =", 0x3d, CFG_GET(pci_config_get8), "%s 0x%02x" },
17073db86aabSstevel 	    { NULL, 0, NULL, NULL }
17083db86aabSstevel };
17093db86aabSstevel 
17103db86aabSstevel static struct cardbus_pci_desc cardbus_pci_cfg[] = {
1711647709cbSToomas Soome 	    { "VendorId    =", 0, CFG_GET(pci_config_get16), "%s 0x%04x" },
1712647709cbSToomas Soome 	    { "DeviceId    =", 2, CFG_GET(pci_config_get16), "%s 0x%04x" },
1713647709cbSToomas Soome 	    { "Command     =", 4, CFG_GET(pci_config_get16), "%s 0x%04x" },
1714647709cbSToomas Soome 	    { "Status      =", 6, CFG_GET(pci_config_get16), "%s 0x%04x" },
1715647709cbSToomas Soome 	    { "CacheLineSz =", 0xc, CFG_GET(pci_config_get8), "%s 0x%02x" },
1716647709cbSToomas Soome 	    { "Latency     =", 0xd, CFG_GET(pci_config_get8), "%s 0x%02x" },
1717647709cbSToomas Soome 	    { "MemBase Addr=", 0x10, CFG_GET(pci_config_get32), "%s 0x%08x" },
1718647709cbSToomas Soome 	    { "Pri Bus     =", 0x18, CFG_GET(pci_config_get8), "%s 0x%02x" },
1719647709cbSToomas Soome 	    { "Sec Bus     =", 0x19, CFG_GET(pci_config_get8), "%s 0x%02x" },
1720647709cbSToomas Soome 	    { "Sub Bus     =", 0x1a, CFG_GET(pci_config_get8), "%s 0x%02x" },
1721647709cbSToomas Soome 	    { "CBus Latency=", 0x1b, CFG_GET(pci_config_get8), "%s 0x%02x" },
1722647709cbSToomas Soome 	    { "Mem0 Base   =", 0x1c, CFG_GET(pci_config_get32), "%s 0x%08x" },
1723647709cbSToomas Soome 	    { "Mem0 Limit  =", 0x20, CFG_GET(pci_config_get32), "%s 0x%08x" },
1724647709cbSToomas Soome 	    { "Mem1 Base   =", 0x24, CFG_GET(pci_config_get32), "%s 0x%08x" },
1725647709cbSToomas Soome 	    { "Mem1 Limit  =", 0x28, CFG_GET(pci_config_get32), "%s 0x%08x" },
1726647709cbSToomas Soome 	    { "I/O0 Base   =", 0x2c, CFG_GET(pci_config_get32), "%s 0x%08x" },
1727647709cbSToomas Soome 	    { "I/O0 Limit  =", 0x30, CFG_GET(pci_config_get32), "%s 0x%08x" },
1728647709cbSToomas Soome 	    { "I/O1 Base   =", 0x34, CFG_GET(pci_config_get32), "%s 0x%08x" },
1729647709cbSToomas Soome 	    { "I/O1 Limit  =", 0x38, CFG_GET(pci_config_get32), "%s 0x%08x" },
1730647709cbSToomas Soome 	    { "ILINE       =", 0x3c, CFG_GET(pci_config_get8), "%s 0x%02x" },
1731647709cbSToomas Soome 	    { "IPIN        =", 0x3d, CFG_GET(pci_config_get8), "%s 0x%02x" },
1732647709cbSToomas Soome 	    { "Bridge Ctrl =", 0x3e, CFG_GET(pci_config_get16), "%s 0x%04x" },
1733647709cbSToomas Soome 	    { "Legacy Addr =", 0x44, CFG_GET(pci_config_get32), "%s 0x%08x" },
17343db86aabSstevel 	    { NULL, 0, NULL, NULL }
17353db86aabSstevel };
17363db86aabSstevel 
17373db86aabSstevel static void
cardbus_dump(struct cardbus_pci_desc * spcfg,ddi_acc_handle_t handle)17383db86aabSstevel cardbus_dump(struct cardbus_pci_desc *spcfg, ddi_acc_handle_t handle)
17393db86aabSstevel {
17403db86aabSstevel 	int	i;
17413db86aabSstevel 	for (i = 0; spcfg[i].name; i++) {
17423db86aabSstevel 
17433db86aabSstevel 		cmn_err(CE_NOTE, spcfg[i].fmt, spcfg[i].name,
17445c066ec2SJerry Gilliam 		    spcfg[i].cfg_get_func(handle, spcfg[i].offset));
17453db86aabSstevel 	}
17463db86aabSstevel 
17473db86aabSstevel }
17483db86aabSstevel 
17493db86aabSstevel void
cardbus_dump_pci_node(dev_info_t * dip)17503db86aabSstevel cardbus_dump_pci_node(dev_info_t *dip)
17513db86aabSstevel {
17523db86aabSstevel 	dev_info_t *next;
17533db86aabSstevel 	struct cardbus_pci_desc *spcfg;
17543db86aabSstevel 	ddi_acc_handle_t config_handle;
17553db86aabSstevel 	uint32_t VendorId;
17563db86aabSstevel 
17573db86aabSstevel 	cmn_err(CE_NOTE, "\nPCI leaf node of dip 0x%p:\n", (void *)dip);
17583db86aabSstevel 	for (next = ddi_get_child(dip); next;
17595c066ec2SJerry Gilliam 	    next = ddi_get_next_sibling(next)) {
17603db86aabSstevel 
17613db86aabSstevel 		VendorId = ddi_getprop(DDI_DEV_T_ANY, next,
17625c066ec2SJerry Gilliam 		    DDI_PROP_CANSLEEP|DDI_PROP_DONTPASS,
17635c066ec2SJerry Gilliam 		    "vendor-id", -1);
17643db86aabSstevel 		if (VendorId == -1) {
17653db86aabSstevel 			/* not a pci device */
17663db86aabSstevel 			continue;
17673db86aabSstevel 		}
17683db86aabSstevel 
17693db86aabSstevel 		if (pci_config_setup(next, &config_handle) != DDI_SUCCESS) {
17703db86aabSstevel 			cmn_err(CE_WARN, "!pcic child: non pci device\n");
17713db86aabSstevel 			continue;
17723db86aabSstevel 		}
17733db86aabSstevel 
17743db86aabSstevel 		spcfg = generic_pci_cfg;
17753db86aabSstevel 		cardbus_dump(spcfg, config_handle);
17763db86aabSstevel 		pci_config_teardown(&config_handle);
17773db86aabSstevel 
17783db86aabSstevel 	}
17793db86aabSstevel 
17803db86aabSstevel }
17813db86aabSstevel 
17823db86aabSstevel void
cardbus_dump_pci_config(dev_info_t * dip)17833db86aabSstevel cardbus_dump_pci_config(dev_info_t *dip)
17843db86aabSstevel {
17853db86aabSstevel 	struct cardbus_pci_desc *spcfg;
17863db86aabSstevel 	ddi_acc_handle_t config_handle;
17873db86aabSstevel 
17883db86aabSstevel 	if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
17895c066ec2SJerry Gilliam 		cmn_err(CE_WARN,
17905c066ec2SJerry Gilliam 		    "!pci_config_setup() failed on 0x%p", (void *)dip);
17915c066ec2SJerry Gilliam 		return;
17923db86aabSstevel 	}
17933db86aabSstevel 
17943db86aabSstevel 	spcfg = cardbus_pci_cfg;
17953db86aabSstevel 	cardbus_dump(spcfg, config_handle);
17963db86aabSstevel 
17973db86aabSstevel 	pci_config_teardown(&config_handle);
17983db86aabSstevel }
17993db86aabSstevel 
18003db86aabSstevel void
cardbus_dump_socket(dev_info_t * dip)18013db86aabSstevel cardbus_dump_socket(dev_info_t *dip)
18023db86aabSstevel {
1803647709cbSToomas Soome 	ddi_acc_handle_t	iohandle;
18043db86aabSstevel 	caddr_t		ioaddr;
18053db86aabSstevel 	ddi_device_acc_attr_t attr;
18063db86aabSstevel 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
18073db86aabSstevel 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
18083db86aabSstevel 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
18093db86aabSstevel 	if (ddi_regs_map_setup(dip, 1,
18105c066ec2SJerry Gilliam 	    (caddr_t *)&ioaddr,
18115c066ec2SJerry Gilliam 	    0,
18125c066ec2SJerry Gilliam 	    4096,
18135c066ec2SJerry Gilliam 	    &attr, &iohandle) != DDI_SUCCESS) {
18145c066ec2SJerry Gilliam 		cmn_err(CE_WARN, "Failed to map address for 0x%p", (void *)dip);
18155c066ec2SJerry Gilliam 		return;
18163db86aabSstevel 	}
18173db86aabSstevel 
18183db86aabSstevel 	cmn_err(CE_NOTE, "////////////////////////////////////////");
18193db86aabSstevel 	cmn_err(CE_NOTE, "SOCKET_EVENT  = [0x%x]",
18205c066ec2SJerry Gilliam 	    ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_STATUS_EVENT)));
18213db86aabSstevel 	cmn_err(CE_NOTE, "SOCKET_MASK   = [0x%x]",
18225c066ec2SJerry Gilliam 	    ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_STATUS_MASK)));
18233db86aabSstevel 	cmn_err(CE_NOTE, "SOCKET_STATE  = [0x%x]",
18245c066ec2SJerry Gilliam 	    ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_PRESENT_STATE)));
18253db86aabSstevel 	cmn_err(CE_NOTE, "////////////////////////////////////////");
18263db86aabSstevel 
18273db86aabSstevel 	ddi_regs_map_free(&iohandle);
18283db86aabSstevel 
18293db86aabSstevel }
1830