xref: /onnv-gate/usr/src/uts/common/io/cardbus/cardbus_hp.c (revision 8459:5b8974f1a0ce)
12305Sstevel /*
22305Sstevel  * CDDL HEADER START
32305Sstevel  *
42305Sstevel  * The contents of this file are subject to the terms of the
52305Sstevel  * Common Development and Distribution License (the "License").
62305Sstevel  * You may not use this file except in compliance with the License.
72305Sstevel  *
82305Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92305Sstevel  * or http://www.opensolaris.org/os/licensing.
102305Sstevel  * See the License for the specific language governing permissions
112305Sstevel  * and limitations under the License.
122305Sstevel  *
132305Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
142305Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152305Sstevel  * If applicable, add the following below this CDDL HEADER, with the
162305Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
172305Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
182305Sstevel  *
192305Sstevel  * CDDL HEADER END
202305Sstevel  */
212305Sstevel /*
22*8459SJerry.Gilliam@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
232305Sstevel  * Use is subject to license terms.
242305Sstevel  */
252305Sstevel 
262305Sstevel /*
272305Sstevel  * Copyright (c)  * Copyright (c) 2001 Tadpole Technology plc
282305Sstevel  * All rights reserved.
292305Sstevel  * From "@(#)pcicfg.c   1.31    99/06/18 SMI"
302305Sstevel  */
312305Sstevel 
322305Sstevel /*
332305Sstevel  * Cardbus hotplug module
342305Sstevel  */
352305Sstevel 
362305Sstevel #include <sys/open.h>
372305Sstevel #include <sys/file.h>
382305Sstevel #include <sys/stat.h>
392305Sstevel #include <sys/ddi.h>
402305Sstevel #include <sys/sunndi.h>
412305Sstevel 
422305Sstevel #include <sys/note.h>
432305Sstevel 
442305Sstevel #include <sys/pci.h>
452305Sstevel 
462305Sstevel #include <sys/hotplug/hpcsvc.h>
472305Sstevel #include <sys/hotplug/pci/pcicfg.h>
482305Sstevel #include <sys/pcic_reg.h>
492305Sstevel 
502305Sstevel #include "cardbus.h"
512305Sstevel #include "cardbus_hp.h"
522305Sstevel #include "cardbus_cfg.h"
532305Sstevel 
542305Sstevel /*
552305Sstevel  * ************************************************************************
562305Sstevel  * *** Implementation specific data structures/definitions.             ***
572305Sstevel  * ************************************************************************
582305Sstevel  */
592305Sstevel 
602305Sstevel #ifndef HPC_MAX_OCCUPANTS
612305Sstevel #define	HPC_MAX_OCCUPANTS 8
622305Sstevel typedef struct hpc_occupant_info {
632305Sstevel 	int	i;
642305Sstevel 	char 	*id[HPC_MAX_OCCUPANTS];
652305Sstevel } hpc_occupant_info_t;
662305Sstevel #endif
672305Sstevel 
682305Sstevel #define	PCICFG_FLAGS_CONTINUE   0x1
692305Sstevel 
702305Sstevel #define	PCICFG_OP_ONLINE	0x1
712305Sstevel #define	PCICFG_OP_OFFLINE	0x0
722305Sstevel 
732305Sstevel #define	CBHP_DEVCTL_MINOR	255
742305Sstevel 
752305Sstevel #define	AP_MINOR_NUM_TO_CB_INSTANCE(x)	((x) & 0xFF)
762305Sstevel #define	AP_MINOR_NUM(x)		(((uint_t)(3) << 8) | ((x) & 0xFF))
772305Sstevel #define	AP_IS_CB_MINOR(x)	(((x)>>8) == (3))
782305Sstevel 
792305Sstevel extern int cardbus_debug;
802451Srw148561 extern int number_of_cardbus_cards;
812305Sstevel 
822305Sstevel static int cardbus_autocfg_enabled = 1;	/* auto config is enabled by default */
832305Sstevel 
842305Sstevel /* static functions */
852305Sstevel static int cardbus_event_handler(caddr_t slot_arg, uint_t event_mask);
862305Sstevel static int cardbus_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl,
872305Sstevel 				int request, caddr_t arg);
882305Sstevel static int cardbus_new_slot_state(dev_info_t *dip, hpc_slot_t hdl,
892305Sstevel 				hpc_slot_info_t *slot_info, int slot_state);
902305Sstevel static int cardbus_list_occupants(dev_info_t *dip, void *hdl);
912305Sstevel static void create_occupant_props(dev_info_t *self, dev_t dev);
922305Sstevel static void delete_occupant_props(dev_info_t *dip, dev_t dev);
932305Sstevel static int cardbus_configure_ap(cbus_t *cbp);
942305Sstevel static int cardbus_unconfigure_ap(cbus_t *cbp);
952305Sstevel static int cbus_unconfigure(dev_info_t *devi, int prim_bus);
962305Sstevel void cardbus_dump_pci_config(dev_info_t *dip);
972305Sstevel void cardbus_dump_pci_node(dev_info_t *dip);
982305Sstevel 
992305Sstevel int
cardbus_init_hotplug(cbus_t * cbp)1002305Sstevel cardbus_init_hotplug(cbus_t *cbp)
1012305Sstevel {
1022305Sstevel 	char tbuf[MAXNAMELEN];
1032305Sstevel 	hpc_slot_info_t	slot_info;
1042305Sstevel 	hpc_slot_ops_t	*slot_ops;
1052305Sstevel 	hpc_slot_t	slhandle;	/* HPS slot handle */
1062305Sstevel 
1072305Sstevel 	/*
1082305Sstevel 	 *  register the bus instance with the HPS framework.
1092305Sstevel 	 */
1102305Sstevel 	if (hpc_nexus_register_bus(cbp->cb_dip,
1112305Sstevel 	    cardbus_new_slot_state, 0) != 0) {
1122305Sstevel 		cmn_err(CE_WARN, "%s%d: failed to register the bus with HPS\n",
1132305Sstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance);
1142305Sstevel 		return (DDI_FAILURE);
1152305Sstevel 	}
1162305Sstevel 
1172305Sstevel 	(void) sprintf(cbp->ap_id, "slot%d", cbp->cb_instance);
1182305Sstevel 	(void) ddi_pathname(cbp->cb_dip, tbuf);
1192305Sstevel 	cbp->nexus_path = kmem_alloc(strlen(tbuf) + 1, KM_SLEEP);
1202305Sstevel 	(void) strcpy(cbp->nexus_path, tbuf);
1212305Sstevel 	cardbus_err(cbp->cb_dip, 8,
1222305Sstevel 	    "cardbus_init_hotplug: nexus_path set to %s", cbp->nexus_path);
1232305Sstevel 
1242305Sstevel 	slot_ops = hpc_alloc_slot_ops(KM_SLEEP);
1252305Sstevel 	cbp->slot_ops = slot_ops;
1262305Sstevel 
1272305Sstevel 	/*
1282305Sstevel 	 * Fill in the slot information structure that
1292305Sstevel 	 * describes the slot.
1302305Sstevel 	 */
1312305Sstevel 	slot_info.version = HPC_SLOT_INFO_VERSION;
1322305Sstevel 	slot_info.slot_type = HPC_SLOT_TYPE_PCI;
1332305Sstevel 	slot_info.slot.pci.device_number = 0;
1342305Sstevel 	slot_info.slot.pci.slot_capabilities = 0;
1352305Sstevel 
1362305Sstevel 	(void) strcpy(slot_info.slot.pci.slot_logical_name, cbp->ap_id);
1372305Sstevel 
1382305Sstevel 	slot_ops->hpc_version = HPC_SLOT_OPS_VERSION;
1392305Sstevel 	slot_ops->hpc_op_connect = NULL;
1402305Sstevel 	slot_ops->hpc_op_disconnect = NULL;
1412305Sstevel 	slot_ops->hpc_op_insert = NULL;
1422305Sstevel 	slot_ops->hpc_op_remove = NULL;
1432305Sstevel 	slot_ops->hpc_op_control = cardbus_pci_control;
1442305Sstevel 
1452305Sstevel 	if (hpc_slot_register(cbp->cb_dip, cbp->nexus_path, &slot_info,
1462305Sstevel 	    &slhandle, slot_ops, (caddr_t)cbp, 0) != 0) {
1472305Sstevel 		/*
1482305Sstevel 		 * If the slot can not be registered,
1492305Sstevel 		 * then the slot_ops need to be freed.
1502305Sstevel 		 */
1512305Sstevel 		cmn_err(CE_WARN,
1522305Sstevel 		    "cbp%d Unable to Register Slot %s", cbp->cb_instance,
1532305Sstevel 		    slot_info.slot.pci.slot_logical_name);
1542305Sstevel 
1552305Sstevel 		(void) hpc_nexus_unregister_bus(cbp->cb_dip);
1562305Sstevel 		hpc_free_slot_ops(slot_ops);
1572305Sstevel 		cbp->slot_ops = NULL;
1582305Sstevel 		return (DDI_FAILURE);
1592305Sstevel 	}
1602305Sstevel 
1612305Sstevel 	ASSERT(slhandle == cbp->slot_handle);
1622305Sstevel 
1632305Sstevel 	cardbus_err(cbp->cb_dip, 8,
1642305Sstevel 	    "cardbus_init_hotplug: slot_handle 0x%p", cbp->slot_handle);
1652305Sstevel 	return (DDI_SUCCESS);
1662305Sstevel }
1672305Sstevel 
1682305Sstevel static int
cardbus_event_handler(caddr_t slot_arg,uint_t event_mask)1692305Sstevel cardbus_event_handler(caddr_t slot_arg, uint_t event_mask)
1702305Sstevel {
1712305Sstevel 	int ap_minor = (int)((uintptr_t)slot_arg);
1722305Sstevel 	cbus_t *cbp;
1732305Sstevel 	int cb_instance;
1742305Sstevel 	int rv = HPC_EVENT_CLAIMED;
1752305Sstevel 
1762305Sstevel 	cb_instance = AP_MINOR_NUM_TO_CB_INSTANCE(ap_minor);
1772305Sstevel 
1782305Sstevel 	ASSERT(cb_instance >= 0);
1792305Sstevel 	cbp = (cbus_t *)ddi_get_soft_state(cardbus_state, cb_instance);
1802305Sstevel 	mutex_enter(&cbp->cb_mutex);
1812305Sstevel 
1822305Sstevel 	switch (event_mask) {
1832305Sstevel 
1842305Sstevel 	case HPC_EVENT_SLOT_INSERTION:
1852305Sstevel 		/*
1862305Sstevel 		 * A card is inserted in the slot. Just report this
1872305Sstevel 		 * event and return.
1882305Sstevel 		 */
1892305Sstevel 		cardbus_err(cbp->cb_dip, 7,
1902305Sstevel 		    "cardbus_event_handler(%s%d): card is inserted",
1912305Sstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance);
1922305Sstevel 
1932305Sstevel 		break;
1942305Sstevel 
1952305Sstevel 	case HPC_EVENT_SLOT_CONFIGURE:
1962305Sstevel 		/*
1972305Sstevel 		 * Configure the occupant that is just inserted in the slot.
1982305Sstevel 		 * The receptacle may or may not be in the connected state. If
1992305Sstevel 		 * the receptacle is not connected and the auto configuration
2002305Sstevel 		 * is enabled on this slot then connect the slot. If auto
2012305Sstevel 		 * configuration is enabled then configure the card.
2022305Sstevel 		 */
2032305Sstevel 		if (!(cbp->auto_config)) {
2042305Sstevel 			/*
2052305Sstevel 			 * auto configuration is disabled.
2062305Sstevel 			 */
2072305Sstevel 			cardbus_err(cbp->cb_dip, 7,
2082305Sstevel 			    "cardbus_event_handler(%s%d): "
2092305Sstevel 			    "SLOT_CONFIGURE event occured (slot %s)",
2102305Sstevel 			    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
2112305Sstevel 			    cbp->name);
2122305Sstevel 
2132305Sstevel 			break;
2142305Sstevel 		}
2152305Sstevel 
2162305Sstevel 		cardbus_err(cbp->cb_dip, 7,
2172305Sstevel 		    "cardbus_event_handler(%s%d): configure event",
2182305Sstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance);
2192305Sstevel 
2202305Sstevel 		if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
2212305Sstevel 			cmn_err(CE_WARN, "!slot%d already configured\n",
222*8459SJerry.Gilliam@Sun.COM 			    cbp->cb_instance);
2232305Sstevel 			break;
2242305Sstevel 		}
2252305Sstevel 
2262305Sstevel 		/*
2272305Sstevel 		 * Auto configuration is enabled. First, make sure the
2282305Sstevel 		 * receptacle is in the CONNECTED state.
2292305Sstevel 		 */
2302305Sstevel 		if ((rv = hpc_nexus_connect(cbp->slot_handle,
2312305Sstevel 		    NULL, 0)) == HPC_SUCCESS) {
2322305Sstevel 			cbp->rstate = AP_RSTATE_CONNECTED; /* record rstate */
2332305Sstevel 		}
2342305Sstevel 
2352305Sstevel 		if (cardbus_configure_ap(cbp) == HPC_SUCCESS)
236*8459SJerry.Gilliam@Sun.COM 			create_occupant_props(cbp->cb_dip, makedevice(
237*8459SJerry.Gilliam@Sun.COM 			    ddi_driver_major((cbp->cb_dip)), ap_minor));
2382305Sstevel 		else
2392305Sstevel 			rv = HPC_ERR_FAILED;
2402305Sstevel 
2412305Sstevel 		break;
2422305Sstevel 
2432305Sstevel 	case HPC_EVENT_SLOT_UNCONFIGURE:
2442305Sstevel 		/*
2452305Sstevel 		 * Unconfigure the occupant in this slot.
2462305Sstevel 		 */
2472305Sstevel 		if (!(cbp->auto_config)) {
2482305Sstevel 			/*
2492305Sstevel 			 * auto configuration is disabled.
2502305Sstevel 			 */
2512305Sstevel 			cardbus_err(cbp->cb_dip, 7,
2522305Sstevel 			    "cardbus_event_handler(%s%d): "
2532305Sstevel 			    "SLOT_UNCONFIGURE event"
2542305Sstevel 			    " occured - auto-conf disabled (slot %s)",
2552305Sstevel 			    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
2562305Sstevel 			    cbp->name);
2572305Sstevel 
2582305Sstevel 			break;
2592305Sstevel 		}
2602305Sstevel 
2612305Sstevel 		cardbus_err(cbp->cb_dip, 7,
2622305Sstevel 		    "cardbus_event_handler(%s%d): SLOT_UNCONFIGURE event"
2632305Sstevel 		    " occured (slot %s)",
2642305Sstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
2652305Sstevel 		    cbp->name);
2662305Sstevel 
2672305Sstevel 		if (cardbus_unconfigure_ap(cbp) != HPC_SUCCESS)
2682305Sstevel 			rv = HPC_ERR_FAILED;
2692305Sstevel 
2702451Srw148561 		DEVI(cbp->cb_dip)->devi_ops->devo_bus_ops = cbp->orig_bopsp;
2712451Srw148561 		--number_of_cardbus_cards;
2722305Sstevel 		break;
2732305Sstevel 
2742305Sstevel 	case HPC_EVENT_SLOT_REMOVAL:
2752305Sstevel 		/*
2762305Sstevel 		 * Card is removed from the slot. The card must have been
2772305Sstevel 		 * unconfigured before this event.
2782305Sstevel 		 */
2792305Sstevel 		if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
2802305Sstevel 			cardbus_err(cbp->cb_dip, 1,
2812305Sstevel 			    "cardbus_event_handler(%s%d): "
2822305Sstevel 			    "card is removed from"
2832305Sstevel 			    " the slot %s before doing unconfigure!!",
2842305Sstevel 			    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
2852305Sstevel 			    cbp->name);
2862305Sstevel 
2872305Sstevel 			break;
2882305Sstevel 		}
2892305Sstevel 
2902305Sstevel 		cardbus_err(cbp->cb_dip, 7,
2912305Sstevel 		    "cardbus_event_handler(%s%d): "
2922305Sstevel 		    "card is removed from the slot %s",
2932305Sstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
2942305Sstevel 		    cbp->name);
2952305Sstevel 
2962305Sstevel 		break;
2972305Sstevel 
2982305Sstevel 	case HPC_EVENT_SLOT_POWER_ON:
2992305Sstevel 		/*
3002305Sstevel 		 * Slot is connected to the bus. i.e the card is powered
3012305Sstevel 		 * on.
3022305Sstevel 		 */
3032305Sstevel 		cardbus_err(cbp->cb_dip, 7,
3042305Sstevel 		    "cardbus_event_handler(%s%d): "
3052305Sstevel 		    "card is powered on in the slot %s",
3062305Sstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
3072305Sstevel 		    cbp->name);
3082305Sstevel 
3092305Sstevel 		cbp->rstate = AP_RSTATE_CONNECTED; /* record rstate */
3102305Sstevel 
3112305Sstevel 		break;
3122305Sstevel 
3132305Sstevel 	case HPC_EVENT_SLOT_POWER_OFF:
3142305Sstevel 		/*
3152305Sstevel 		 * Slot is disconnected from the bus. i.e the card is powered
3162305Sstevel 		 * off.
3172305Sstevel 		 */
3182305Sstevel 		cardbus_err(cbp->cb_dip, 7,
3192305Sstevel 		    "cardbus_event_handler(%s%d): "
3202305Sstevel 		    "card is powered off in the slot %s",
3212305Sstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
3222305Sstevel 		    cbp->name);
3232305Sstevel 
3242305Sstevel 		cbp->rstate = AP_RSTATE_DISCONNECTED; /* record rstate */
3252305Sstevel 
3262305Sstevel 		break;
3272305Sstevel 
3282305Sstevel 	default:
3292305Sstevel 		cardbus_err(cbp->cb_dip, 4,
3302305Sstevel 		    "cardbus_event_handler(%s%d): "
3312305Sstevel 		    "unknown event %x for this slot %s",
3322305Sstevel 		    ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
3332305Sstevel 		    event_mask, cbp->name);
3342305Sstevel 
3352305Sstevel 		break;
3362305Sstevel 	}
3372305Sstevel 
3382305Sstevel 	mutex_exit(&cbp->cb_mutex);
3392305Sstevel 
3402305Sstevel 	return (rv);
3412305Sstevel }
3422305Sstevel 
3432305Sstevel static int
cardbus_pci_control(caddr_t ops_arg,hpc_slot_t slot_hdl,int request,caddr_t arg)3442305Sstevel cardbus_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl, int request,
3452305Sstevel 			caddr_t arg)
3462305Sstevel {
3472305Sstevel 	cbus_t *cbp;
3482305Sstevel 	int rval = HPC_SUCCESS;
3492305Sstevel 	hpc_led_info_t *hpc_led_info;
3502305Sstevel 
3512305Sstevel 	_NOTE(ARGUNUSED(slot_hdl))
3522305Sstevel 
3532305Sstevel 	cbp = (cbus_t *)ops_arg;
3542305Sstevel 	ASSERT(mutex_owned(&cbp->cb_mutex));
3552305Sstevel 
3562305Sstevel 	switch (request) {
3572305Sstevel 
358*8459SJerry.Gilliam@Sun.COM 	case HPC_CTRL_GET_SLOT_STATE: {
3592305Sstevel 		hpc_slot_state_t	*hpc_slot_state;
3602305Sstevel 
3612305Sstevel 		hpc_slot_state = (hpc_slot_state_t *)arg;
3622305Sstevel 
3632305Sstevel 		cardbus_err(cbp->cb_dip, 7,
3642305Sstevel 		    "cardbus_pci_control() - "
3652305Sstevel 		    "HPC_CTRL_GET_SLOT_STATE hpc_slot_state=0x%p",
3662305Sstevel 		    (void *) hpc_slot_state);
3672305Sstevel 
3682305Sstevel 		if (cbp->card_present)
3692305Sstevel 			*hpc_slot_state = HPC_SLOT_CONNECTED;
3702305Sstevel 		else
3712305Sstevel 			*hpc_slot_state = HPC_SLOT_EMPTY;
3722305Sstevel 
3732305Sstevel 		break;
374*8459SJerry.Gilliam@Sun.COM 	}
3752305Sstevel 
376*8459SJerry.Gilliam@Sun.COM 	case HPC_CTRL_GET_BOARD_TYPE: {
3772305Sstevel 		hpc_board_type_t	*hpc_board_type;
3782305Sstevel 
3792305Sstevel 		hpc_board_type = (hpc_board_type_t *)arg;
3802305Sstevel 
3812305Sstevel 		cardbus_err(cbp->cb_dip, 7,
3822305Sstevel 		    "cardbus_pci_control() - HPC_CTRL_GET_BOARD_TYPE");
3832305Sstevel 
3842305Sstevel 		/*
3852305Sstevel 		 * The HPC driver does not know what board type
3862305Sstevel 		 * is plugged in.
3872305Sstevel 		 */
3882305Sstevel 		*hpc_board_type = HPC_BOARD_PCI_HOTPLUG;
3892305Sstevel 
3902305Sstevel 		break;
391*8459SJerry.Gilliam@Sun.COM 	}
3922305Sstevel 
3932305Sstevel 	case HPC_CTRL_DEV_CONFIGURED:
3942305Sstevel 	case HPC_CTRL_DEV_UNCONFIGURED:
3952305Sstevel 		cardbus_err(cbp->cb_dip, 5,
3962305Sstevel 		    "cardbus_pci_control() - HPC_CTRL_DEV_%sCONFIGURED",
3972305Sstevel 		    request == HPC_CTRL_DEV_UNCONFIGURED ? "UN" : "");
3982305Sstevel 		break;
3992305Sstevel 
4002305Sstevel 	case HPC_CTRL_GET_LED_STATE:
4012305Sstevel 		hpc_led_info = (hpc_led_info_t *)arg;
4022305Sstevel 		cardbus_err(cbp->cb_dip, 5,
4032305Sstevel 		    "cardbus_pci_control() - HPC_CTRL_GET_LED_STATE "
4042305Sstevel 		    "led %d is %d",
4052305Sstevel 		    hpc_led_info->led, cbp->leds[hpc_led_info->led]);
4062305Sstevel 
4072305Sstevel 		hpc_led_info->state = cbp->leds[hpc_led_info->led];
4082305Sstevel 		break;
4092305Sstevel 
4102305Sstevel 	case HPC_CTRL_SET_LED_STATE:
4112305Sstevel 		hpc_led_info = (hpc_led_info_t *)arg;
4122305Sstevel 
4132305Sstevel 		cardbus_err(cbp->cb_dip, 4,
4142305Sstevel 		    "cardbus_pci_control() - HPC_CTRL_SET_LED_STATE "
4152305Sstevel 		    "led %d to %d",
4162305Sstevel 		    hpc_led_info->led, hpc_led_info->state);
4172305Sstevel 
4182305Sstevel 		cbp->leds[hpc_led_info->led] = hpc_led_info->state;
4192305Sstevel 		break;
4202305Sstevel 
4212305Sstevel 	case HPC_CTRL_ENABLE_AUTOCFG:
4222305Sstevel 		cardbus_err(cbp->cb_dip, 5,
4232305Sstevel 		    "cardbus_pci_control() - HPC_CTRL_ENABLE_AUTOCFG");
4242305Sstevel 
4252305Sstevel 		/*
4262305Sstevel 		 * Cardbus ALWAYS does auto config, from the slots point of
4272305Sstevel 		 * view this is turning on the card and making sure it's ok.
4282305Sstevel 		 * This is all done by the bridge driver before we see any
4292305Sstevel 		 * indication.
4302305Sstevel 		 */
4312305Sstevel 		break;
4322305Sstevel 
4332305Sstevel 	case HPC_CTRL_DISABLE_AUTOCFG:
4342305Sstevel 		cardbus_err(cbp->cb_dip, 5,
4352305Sstevel 		    "cardbus_pci_control() - HPC_CTRL_DISABLE_AUTOCFG");
4362305Sstevel 		break;
4372305Sstevel 
4382305Sstevel 	case HPC_CTRL_DISABLE_ENUM:
4392305Sstevel 	case HPC_CTRL_ENABLE_ENUM:
4402305Sstevel 	default:
4412305Sstevel 		rval = HPC_ERR_NOTSUPPORTED;
4422305Sstevel 		break;
4432305Sstevel 	}
4442305Sstevel 
4452305Sstevel 	return (rval);
4462305Sstevel }
4472305Sstevel 
4482305Sstevel /*
4492305Sstevel  * cardbus_new_slot_state()
4502305Sstevel  *
4512305Sstevel  * This function is called by the HPS when it finds a hot plug
4522305Sstevel  * slot is added or being removed from the hot plug framework.
4532305Sstevel  * It returns 0 for success and HPC_ERR_FAILED for errors.
4542305Sstevel  */
4552305Sstevel static int
cardbus_new_slot_state(dev_info_t * dip,hpc_slot_t hdl,hpc_slot_info_t * slot_info,int slot_state)4562305Sstevel cardbus_new_slot_state(dev_info_t *dip, hpc_slot_t hdl,
4572305Sstevel 			hpc_slot_info_t *slot_info, int slot_state)
4582305Sstevel {
4592305Sstevel 	int cb_instance;
4602305Sstevel 	cbus_t *cbp;
4612305Sstevel 	int ap_minor;
4622305Sstevel 	int rv = 0;
4632305Sstevel 
4642305Sstevel 	cardbus_err(dip, 8,
4652305Sstevel 	    "cardbus_new_slot_state: slot_handle 0x%p", hdl);
4662305Sstevel 
4672305Sstevel 	/*
4682305Sstevel 	 * get the soft state structure for the bus instance.
4692305Sstevel 	 */
4702305Sstevel 	cb_instance = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
4712305Sstevel 	    DDI_PROP_DONTPASS, "cbus-instance", -1);
472*8459SJerry.Gilliam@Sun.COM 	ASSERT(cb_instance >= 0);
4732305Sstevel 	cbp = (cbus_t *)ddi_get_soft_state(cardbus_state, cb_instance);
4742305Sstevel 
4752305Sstevel 	mutex_enter(&cbp->cb_mutex);
4762305Sstevel 
4772305Sstevel 	switch (slot_state) {
4782305Sstevel 
4792305Sstevel 	case HPC_SLOT_ONLINE:
4802305Sstevel 		/*
4812305Sstevel 		 * Make sure the slot is not already ONLINE
4822305Sstevel 		 */
4832305Sstevel 		if (cbp->slot_handle != NULL) {
4842305Sstevel 			cardbus_err(dip, 4,
4852305Sstevel 			    "cardbus_new_slot_state: "
4862305Sstevel 			    "cardbus already ONLINE!!");
4872305Sstevel 			rv = HPC_ERR_FAILED;
4882305Sstevel 			break;
4892305Sstevel 		}
4902305Sstevel 
4912305Sstevel 		/*
4922305Sstevel 		 * Add the hot plug slot to the bus.
4932305Sstevel 		 */
4942305Sstevel 
4952305Sstevel 		/* create the AP minor node */
4962305Sstevel 		ap_minor = AP_MINOR_NUM(cb_instance);
4972305Sstevel 		if (ddi_create_minor_node(dip, slot_info->pci_slot_name,
4982305Sstevel 		    S_IFCHR, ap_minor,
4992305Sstevel 		    DDI_NT_PCI_ATTACHMENT_POINT,
5002305Sstevel 		    0) == DDI_FAILURE) {
5012305Sstevel 			cardbus_err(dip, 4,
5022305Sstevel 			    "cardbus_new_slot_state: "
5032305Sstevel 			    "ddi_create_minor_node failed");
5042305Sstevel 			rv = HPC_ERR_FAILED;
5052305Sstevel 			break;
5062305Sstevel 		}
5072305Sstevel 
5082305Sstevel 		/* save the slot handle */
5092305Sstevel 		cbp->slot_handle = hdl;
5102305Sstevel 
5112305Sstevel 		/* setup event handler for all hardware events on the slot */
5122305Sstevel 		if (hpc_install_event_handler(hdl, -1, cardbus_event_handler,
5132305Sstevel 		    (caddr_t)((long)ap_minor)) != 0) {
5142305Sstevel 			cardbus_err(dip, 4,
5152305Sstevel 			    "cardbus_new_slot_state: "
5162305Sstevel 			    "install event handler failed");
5172305Sstevel 			rv = HPC_ERR_FAILED;
5182305Sstevel 			break;
5192305Sstevel 		}
5202305Sstevel 		cbp->event_mask = (uint32_t)0xFFFFFFFF;
5212305Sstevel 		create_occupant_props(dip,
5222305Sstevel 		    makedevice(ddi_name_to_major(ddi_get_name(dip)),
5232305Sstevel 		    ap_minor));
5242305Sstevel 
5252305Sstevel 		/* set default auto configuration enabled flag for this slot */
5262305Sstevel 		cbp->auto_config = cardbus_autocfg_enabled;
5272305Sstevel 
5282305Sstevel 		/* copy the slot information */
5292305Sstevel 		cbp->name = (char *)kmem_alloc(strlen(slot_info->pci_slot_name)
5302305Sstevel 		    + 1, KM_SLEEP);
5312305Sstevel 		(void) strcpy(cbp->name, slot_info->pci_slot_name);
5322305Sstevel 		cardbus_err(cbp->cb_dip, 10,
5332305Sstevel 		    "cardbus_new_slot_state: cbp->name set to %s", cbp->name);
5342305Sstevel 
5352305Sstevel 		cardbus_err(dip, 4,
5362305Sstevel 		    "Cardbus slot \"%s\" ONLINE\n", slot_info->pci_slot_name);
5372305Sstevel 
5382305Sstevel 		cbp->ostate = AP_OSTATE_UNCONFIGURED;
5392305Sstevel 		cbp->rstate = AP_RSTATE_EMPTY;
5402305Sstevel 
5412305Sstevel 		break;
5422305Sstevel 
5432305Sstevel 	case HPC_SLOT_OFFLINE:
5442305Sstevel 		/*
5452305Sstevel 		 * A hot plug slot is being removed from the bus.
5462305Sstevel 		 * Make sure there is no occupant configured on the
5472305Sstevel 		 * slot before removing the AP minor node.
5482305Sstevel 		 */
5492305Sstevel 		if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
5502305Sstevel 			cmn_err(CE_WARN,
5512305Sstevel 			    "cardbus: Card is still in configured state");
5522305Sstevel 			rv = HPC_ERR_FAILED;
5532305Sstevel 			break;
5542305Sstevel 		}
5552305Sstevel 
5562305Sstevel 		/*
5572305Sstevel 		 * If the AP device is in open state then return
5582305Sstevel 		 * error.
5592305Sstevel 		 */
5602305Sstevel 		if (cbp->soft_state != PCIHP_SOFT_STATE_CLOSED) {
5612305Sstevel 			rv = HPC_ERR_FAILED;
5622305Sstevel 			break;
5632305Sstevel 		}
5642305Sstevel 
5652305Sstevel 		/* remove the minor node */
5662305Sstevel 		ddi_remove_minor_node(dip, cbp->name);
5672305Sstevel 		/* free up the memory for the name string */
5682305Sstevel 		kmem_free(cbp->name, strlen(cbp->name) + 1);
5692305Sstevel 
5702305Sstevel 		/* update the slot info data */
5712305Sstevel 		cbp->name = NULL;
5722305Sstevel 		cbp->slot_handle = NULL;
5732305Sstevel 
5742305Sstevel 		cardbus_err(dip, 6,
5752305Sstevel 		    "cardbus_new_slot_state: Cardbus slot OFFLINE");
5762305Sstevel 		break;
5772305Sstevel 
5782305Sstevel 	default:
5792305Sstevel 		cmn_err(CE_WARN,
5802305Sstevel 		    "cardbus_new_slot_state: unknown slot_state %d\n",
5812305Sstevel 		    slot_state);
5822305Sstevel 		rv = HPC_ERR_FAILED;
5832305Sstevel 	}
5842305Sstevel 
5852305Sstevel 	mutex_exit(&cbp->cb_mutex);
5862305Sstevel 
5872305Sstevel 	return (rv);
5882305Sstevel }
5892305Sstevel 
5902305Sstevel static int
cardbus_list_occupants(dev_info_t * dip,void * hdl)5912305Sstevel cardbus_list_occupants(dev_info_t *dip, void *hdl)
5922305Sstevel {
5932305Sstevel 	hpc_occupant_info_t *occupant = (hpc_occupant_info_t *)hdl;
5942305Sstevel 	char pn[MAXPATHLEN];
5952305Sstevel 
5962305Sstevel 	/*
5972305Sstevel 	 * Ignore the attachment point and pcs.
5982305Sstevel 	 */
5992305Sstevel 	if (strcmp(ddi_binding_name(dip), "pcs") == 0) {
6002305Sstevel 		return (DDI_WALK_CONTINUE);
6012305Sstevel 	}
6022305Sstevel 
6032305Sstevel 	(void) ddi_pathname(dip, pn);
6042305Sstevel 
6052305Sstevel 	occupant->id[occupant->i] = kmem_alloc(strlen(pn) + 1, KM_SLEEP);
6062305Sstevel 	(void) strcpy(occupant->id[occupant->i], pn);
6072305Sstevel 
6082305Sstevel 	occupant->i++;
6092305Sstevel 
6102305Sstevel 	/*
6112305Sstevel 	 * continue the walk to the next sibling to look for a match
6122305Sstevel 	 * or to find other nodes if this card is a multi-function card.
6132305Sstevel 	 */
6142305Sstevel 	return (DDI_WALK_PRUNECHILD);
6152305Sstevel }
6162305Sstevel 
6172305Sstevel static void
create_occupant_props(dev_info_t * self,dev_t dev)6182305Sstevel create_occupant_props(dev_info_t *self, dev_t dev)
6192305Sstevel {
6202305Sstevel 	hpc_occupant_info_t occupant;
6212305Sstevel 	int i;
6222305Sstevel 	int circular;
6232305Sstevel 
6242305Sstevel 	occupant.i = 0;
6252305Sstevel 
6262305Sstevel 	ndi_devi_enter(self, &circular);
6272305Sstevel 	ddi_walk_devs(ddi_get_child(self), cardbus_list_occupants,
6282305Sstevel 	    (void *)&occupant);
6292305Sstevel 	ndi_devi_exit(self, circular);
6302305Sstevel 
6312305Sstevel 	if (occupant.i == 0) {
6322305Sstevel 		char *c[] = { "" };
6332305Sstevel 		cardbus_err(self, 1, "create_occupant_props: no occupant\n");
6342305Sstevel 		(void) ddi_prop_update_string_array(dev, self, "pci-occupant",
6352305Sstevel 		    c, 1);
6362305Sstevel 	} else {
6372305Sstevel 		cardbus_err(self, 1,
6382305Sstevel 		    "create_occupant_props: %d occupant\n", occupant.i);
6392305Sstevel 		(void) ddi_prop_update_string_array(dev, self, "pci-occupant",
6402305Sstevel 		    occupant.id, occupant.i);
6412305Sstevel 	}
6422305Sstevel 
6432305Sstevel 	for (i = 0; i < occupant.i; i++) {
6442305Sstevel 		kmem_free(occupant.id[i], strlen(occupant.id[i]) + 1);
6452305Sstevel 	}
6462305Sstevel }
6472305Sstevel 
6482305Sstevel static void
delete_occupant_props(dev_info_t * dip,dev_t dev)6492305Sstevel delete_occupant_props(dev_info_t *dip, dev_t dev)
6502305Sstevel {
6512305Sstevel 	if (ddi_prop_remove(dev, dip, "pci-occupant")
6522305Sstevel 	    != DDI_PROP_SUCCESS)
6532305Sstevel 		return; /* add error handling */
6542305Sstevel 
6552305Sstevel }
6562305Sstevel 
6572305Sstevel /*
6582305Sstevel  * **************************************
6592305Sstevel  * CONFIGURE the occupant in the slot.
6602305Sstevel  * **************************************
6612305Sstevel  */
6622305Sstevel static int
cardbus_configure_ap(cbus_t * cbp)6632305Sstevel cardbus_configure_ap(cbus_t *cbp)
6642305Sstevel {
6652305Sstevel 	dev_info_t *self = cbp->cb_dip;
6662305Sstevel 	int rv = HPC_SUCCESS;
6672305Sstevel 	hpc_slot_state_t rstate;
6682305Sstevel 	struct cardbus_config_ctrl ctrl;
6692305Sstevel 	int circular_count;
6702305Sstevel 
6712305Sstevel 	/*
6722305Sstevel 	 * check for valid request:
6732305Sstevel 	 *  1. It is a hotplug slot.
6742305Sstevel 	 *  2. The receptacle is in the CONNECTED state.
6752305Sstevel 	 */
6762305Sstevel 	if (cbp->slot_handle == NULL || cbp->disabled) {
6772305Sstevel 		return (ENXIO);
6782305Sstevel 	}
6792305Sstevel 
6802305Sstevel 	/*
6812305Sstevel 	 * If the occupant is already in (partially) configured
6822305Sstevel 	 * state then call the ndi_devi_online() on the device
6832305Sstevel 	 * subtree(s) for this attachment point.
6842305Sstevel 	 */
6852305Sstevel 
6862305Sstevel 	if (cbp->ostate == AP_OSTATE_CONFIGURED) {
6872305Sstevel 		ctrl.flags = PCICFG_FLAGS_CONTINUE;
6882305Sstevel 		ctrl.busno = cardbus_primary_busno(self);
6892305Sstevel 		ctrl.rv = NDI_SUCCESS;
6902305Sstevel 		ctrl.dip = NULL;
6912305Sstevel 		ctrl.op = PCICFG_OP_ONLINE;
6922305Sstevel 
6932305Sstevel 		ndi_devi_enter(self, &circular_count);
6942305Sstevel 		ddi_walk_devs(ddi_get_child(self),
695*8459SJerry.Gilliam@Sun.COM 		    cbus_configure, (void *)&ctrl);
6962305Sstevel 		ndi_devi_exit(self, circular_count);
6972305Sstevel 
6982305Sstevel 		if (cardbus_debug) {
6992305Sstevel 			cardbus_dump_pci_config(self);
7002305Sstevel 			cardbus_dump_pci_node(self);
7012305Sstevel 		}
7022305Sstevel 
7032305Sstevel 		if (ctrl.rv != NDI_SUCCESS) {
7042305Sstevel 			/*
7052305Sstevel 			 * one or more of the devices are not
7062305Sstevel 			 * onlined.
7072305Sstevel 			 */
7082305Sstevel 			cmn_err(CE_WARN, "cardbus(%s%d): failed to attach "
7092305Sstevel 			    "one or more drivers for the card in the slot %s",
7102305Sstevel 			    ddi_driver_name(self), cbp->cb_instance,
7112305Sstevel 			    cbp->name);
7122305Sstevel 		}
7132305Sstevel 
7142305Sstevel 		/* tell HPC driver that the occupant is configured */
7152305Sstevel 		(void) hpc_nexus_control(cbp->slot_handle,
7162305Sstevel 		    HPC_CTRL_DEV_CONFIGURED, NULL);
7172305Sstevel 		return (rv);
7182305Sstevel 	}
7192305Sstevel 
7202305Sstevel 	/*
7212305Sstevel 	 * Occupant is in the UNCONFIGURED state.
7222305Sstevel 	 */
7232305Sstevel 
7242305Sstevel 	/* Check if the receptacle is in the CONNECTED state. */
7252305Sstevel 	if (hpc_nexus_control(cbp->slot_handle,
7262305Sstevel 	    HPC_CTRL_GET_SLOT_STATE, (caddr_t)&rstate) != 0) {
7272305Sstevel 		return (ENXIO);
7282305Sstevel 	}
7292305Sstevel 
7302305Sstevel 	if (rstate != HPC_SLOT_CONNECTED) {
7312305Sstevel 		/* error. either the slot is empty or connect failed */
7322305Sstevel 		return (ENXIO);
7332305Sstevel 	}
7342305Sstevel 
7352305Sstevel 	cbp->rstate = AP_RSTATE_CONNECTED; /* record rstate */
7362305Sstevel 
7372305Sstevel 	/*
7382305Sstevel 	 * Call the configurator to configure the card.
7392305Sstevel 	 */
7402305Sstevel 	if (cardbus_configure(cbp) != PCICFG_SUCCESS) {
7412305Sstevel 		return (EIO);
7422305Sstevel 	}
7432305Sstevel 
7442305Sstevel 	/* record the occupant state as CONFIGURED */
7452305Sstevel 	cbp->ostate = AP_OSTATE_CONFIGURED;
7462305Sstevel 	cbp->condition = AP_COND_OK;
7472305Sstevel 
7482305Sstevel 	/* now, online all the devices in the AP */
7492305Sstevel 	ctrl.flags = PCICFG_FLAGS_CONTINUE;
7502305Sstevel 	ctrl.busno = cardbus_primary_busno(self);
7512305Sstevel 	ctrl.rv = NDI_SUCCESS;
7522305Sstevel 	ctrl.dip = NULL;
7532305Sstevel 	ctrl.op = PCICFG_OP_ONLINE;
7542305Sstevel 
7552305Sstevel 	ndi_devi_enter(self, &circular_count);
7562305Sstevel 	ddi_walk_devs(ddi_get_child(self), cbus_configure, (void *)&ctrl);
7572305Sstevel 	ndi_devi_exit(self, circular_count);
7582305Sstevel 
7592305Sstevel 	if (cardbus_debug) {
7602305Sstevel 		cardbus_dump_pci_config(self);
7612305Sstevel 		cardbus_dump_pci_node(self);
7622305Sstevel 	}
7632305Sstevel 	if (ctrl.rv != NDI_SUCCESS) {
7642305Sstevel 		/*
7652305Sstevel 		 * one or more of the devices are not
7662305Sstevel 		 * ONLINE'd.
7672305Sstevel 		 */
7682305Sstevel 		cmn_err(CE_WARN, "cbhp (%s%d): failed to attach one or"
7692305Sstevel 		    " more drivers for the card in the slot %s",
7702305Sstevel 		    ddi_driver_name(cbp->cb_dip),
7712305Sstevel 		    cbp->cb_instance, cbp->name);
7722305Sstevel 		/* rv = EFAULT; */
7732305Sstevel 	}
7742305Sstevel 
7752305Sstevel 	/* tell HPC driver that the occupant is configured */
7762305Sstevel 	(void) hpc_nexus_control(cbp->slot_handle,
777*8459SJerry.Gilliam@Sun.COM 	    HPC_CTRL_DEV_CONFIGURED, NULL);
7782305Sstevel 
7792305Sstevel 	return (rv);
7802305Sstevel }
7812305Sstevel 
7822305Sstevel /*
7832305Sstevel  * **************************************
7842305Sstevel  * UNCONFIGURE the occupant in the slot.
7852305Sstevel  * **************************************
7862305Sstevel  */
7872305Sstevel static int
cardbus_unconfigure_ap(cbus_t * cbp)7882305Sstevel cardbus_unconfigure_ap(cbus_t *cbp)
7892305Sstevel {
7902305Sstevel 	dev_info_t *self = cbp->cb_dip;
7912305Sstevel 	int rv = HPC_SUCCESS, nrv;
7922305Sstevel 
7932305Sstevel 	/*
7942305Sstevel 	 * check for valid request:
7952305Sstevel 	 *  1. It is a hotplug slot.
7962305Sstevel 	 *  2. The occupant is in the CONFIGURED state.
7972305Sstevel 	 */
7982305Sstevel 
7992305Sstevel 	if (cbp->slot_handle == NULL || cbp->disabled) {
8002305Sstevel 		return (ENXIO);
8012305Sstevel 	}
8022305Sstevel 
8032305Sstevel 	/*
8042305Sstevel 	 * If the occupant is in the CONFIGURED state then
8052305Sstevel 	 * call the configurator to unconfigure the slot.
8062305Sstevel 	 */
8072305Sstevel 	if (cbp->ostate == AP_OSTATE_CONFIGURED) {
8082305Sstevel 		/*
8092305Sstevel 		 * Detach all the drivers for the devices in the
8102305Sstevel 		 * slot.
8112305Sstevel 		 */
8122305Sstevel 		nrv = cardbus_unconfigure_node(self,
8132305Sstevel 		    cardbus_primary_busno(self),
8142305Sstevel 		    B_TRUE);
8152305Sstevel 
8162305Sstevel 		if (nrv != NDI_SUCCESS) {
8172305Sstevel 			/*
8182305Sstevel 			 * Failed to detach one or more drivers.
8192305Sstevel 			 * Restore the status for the drivers
8202305Sstevel 			 * which are offlined during this step.
8212305Sstevel 			 */
8222305Sstevel 			cmn_err(CE_WARN,
8232305Sstevel 			    "cbhp (%s%d): Failed to offline all devices"
8242305Sstevel 			    " (slot %s)", ddi_driver_name(cbp->cb_dip),
8252305Sstevel 			    cbp->cb_instance, cbp->name);
8262305Sstevel 			rv = EBUSY;
8272305Sstevel 		} else {
8282305Sstevel 
8292305Sstevel 			if (cardbus_unconfigure(cbp) == PCICFG_SUCCESS) {
8302305Sstevel 				/*
8312305Sstevel 				 * Now that resources are freed,
8322305Sstevel 				 * clear EXT and Turn LED ON.
8332305Sstevel 				 */
8342305Sstevel 				cbp->ostate = AP_OSTATE_UNCONFIGURED;
8352305Sstevel 				cbp->condition = AP_COND_UNKNOWN;
8362305Sstevel 				/*
8372305Sstevel 				 * send the notification of state change
8382305Sstevel 				 * to the HPC driver.
8392305Sstevel 				 */
8402305Sstevel 				(void) hpc_nexus_control(cbp->slot_handle,
8412305Sstevel 				    HPC_CTRL_DEV_UNCONFIGURED, NULL);
8422305Sstevel 			} else {
8432305Sstevel 				rv = EIO;
8442305Sstevel 			}
8452305Sstevel 		}
8462305Sstevel 	}
8472305Sstevel 
8482305Sstevel 	return (rv);
8492305Sstevel }
8502305Sstevel 
8512305Sstevel int
cbus_configure(dev_info_t * dip,void * hdl)8522305Sstevel cbus_configure(dev_info_t *dip, void *hdl)
8532305Sstevel {
8542305Sstevel 	pci_regspec_t *pci_rp;
8552305Sstevel 	int length, rc;
8562305Sstevel 	struct cardbus_config_ctrl *ctrl = (struct cardbus_config_ctrl *)hdl;
8572305Sstevel 	uint8_t bus, device, function;
8582305Sstevel 
8592305Sstevel 	/*
8602305Sstevel 	 * Ignore the attachment point and pcs.
8612305Sstevel 	 */
8622305Sstevel 	if (strcmp(ddi_binding_name(dip), "hp_attachment") == 0 ||
8632305Sstevel 	    strcmp(ddi_binding_name(dip), "pcs") == 0) {
8642305Sstevel 		cardbus_err(dip, 8, "cbus_configure: Ignoring\n");
8652305Sstevel 		return (DDI_WALK_CONTINUE);
8662305Sstevel 	}
8672305Sstevel 
8682305Sstevel 	cardbus_err(dip, 6, "cbus_configure\n");
8692305Sstevel 
8702305Sstevel 	ASSERT(ctrl->op == PCICFG_OP_ONLINE);
8712305Sstevel 
8722305Sstevel 	/*
8732305Sstevel 	 * Get the PCI device number information from the devinfo
8742305Sstevel 	 * node. Since the node may not have the address field
8752305Sstevel 	 * setup (this is done in the DDI_INITCHILD of the parent)
8762305Sstevel 	 * we look up the 'reg' property to decode that information.
8772305Sstevel 	 */
8782305Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
8792305Sstevel 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
8802305Sstevel 	    (uint_t *)&length) != DDI_PROP_SUCCESS) {
8812305Sstevel 		/* Porbably not a real device, like PCS for example */
8822305Sstevel 		if (ddi_get_child(dip) == NULL)
8832305Sstevel 			return (DDI_WALK_PRUNECHILD);
8842305Sstevel 
8852305Sstevel 		cardbus_err(dip, 1, "cubs_configure: Don't configure device\n");
8862305Sstevel 		ctrl->rv = DDI_FAILURE;
8872305Sstevel 		ctrl->dip = dip;
8882305Sstevel 		return (DDI_WALK_TERMINATE);
8892305Sstevel 	}
8902305Sstevel 
8912305Sstevel 	if (pci_rp->pci_phys_hi == 0)
8922305Sstevel 		return (DDI_WALK_CONTINUE);
8932305Sstevel 
8942305Sstevel 	/* get the pci device id information */
8952305Sstevel 	bus = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
8962305Sstevel 	device = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
8972305Sstevel 	function = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
8982305Sstevel 
8992305Sstevel 	/*
9002305Sstevel 	 * free the memory allocated by ddi_prop_lookup_int_array
9012305Sstevel 	 */
9022305Sstevel 	ddi_prop_free(pci_rp);
9032305Sstevel 
9042305Sstevel 	if (bus <= ctrl->busno)
9052305Sstevel 		return (DDI_WALK_CONTINUE);
9062305Sstevel 
9072305Sstevel 	cardbus_err(dip, 8,
9082305Sstevel 	    "cbus_configure on-line device at: "
9092305Sstevel 	    "[0x%x][0x%x][0x%x]\n", bus, device, function);
9102305Sstevel 
9112305Sstevel 	rc = ndi_devi_online(dip, NDI_ONLINE_ATTACH|NDI_CONFIG);
9122305Sstevel 
9132305Sstevel 	cardbus_err(dip, 7,
9142305Sstevel 	    "cbus_configure %s\n",
9152305Sstevel 	    rc == NDI_SUCCESS ? "Success": "Failure");
9162305Sstevel 
9172305Sstevel 	if (rc != NDI_SUCCESS)
9182305Sstevel 		return (DDI_WALK_PRUNECHILD);
9192305Sstevel 
9202305Sstevel 	return (DDI_WALK_CONTINUE);
9212305Sstevel }
9222305Sstevel 
9232305Sstevel int
cardbus_unconfigure_node(dev_info_t * dip,int prim_bus,boolean_t top_bridge)9242305Sstevel cardbus_unconfigure_node(dev_info_t *dip, int prim_bus, boolean_t top_bridge)
9252305Sstevel {
9262305Sstevel 	dev_info_t *child, *next;
9272305Sstevel 
9282305Sstevel 	cardbus_err(dip, 6, "cardbus_unconfigure_node\n");
9292305Sstevel 
9302305Sstevel 	/*
9312305Sstevel 	 * Ignore pcs.
9322305Sstevel 	 */
9332305Sstevel 	if (strcmp(ddi_binding_name(dip), "pcs") == 0) {
9342305Sstevel 		cardbus_err(dip, 8, "cardbus_unconfigure_node: Ignoring\n");
9352305Sstevel 		return (NDI_SUCCESS);
9362305Sstevel 	}
9372305Sstevel 
9382305Sstevel 	/*
9392305Sstevel 	 * bottom up off-line
9402305Sstevel 	 */
9412305Sstevel 	for (child = ddi_get_child(dip); child; child = next) {
9422305Sstevel 		int rc;
9432305Sstevel 		next = ddi_get_next_sibling(child);
9442305Sstevel 		rc = cardbus_unconfigure_node(child, prim_bus, B_FALSE);
9452305Sstevel 		if (rc != NDI_SUCCESS)
9462305Sstevel 			return (rc);
9472305Sstevel 	}
9482305Sstevel 
9492305Sstevel 	/*
9502305Sstevel 	 * Don't unconfigure the bridge itself.
9512305Sstevel 	 */
9522305Sstevel 	if (top_bridge)
9532305Sstevel 		return (NDI_SUCCESS);
9542305Sstevel 
9552305Sstevel 	if (cbus_unconfigure(dip, prim_bus) != NDI_SUCCESS) {
9562305Sstevel 		cardbus_err(dip, 1,
9572305Sstevel 		    "cardbus_unconfigure_node: cardbus_unconfigure failed\n");
9582305Sstevel 		return (NDI_FAILURE);
9592305Sstevel 	}
9602305Sstevel 	return (NDI_SUCCESS);
9612305Sstevel }
9622305Sstevel 
9632305Sstevel /*
9642305Sstevel  * This will turn  resources allocated by cbus_configure()
9652305Sstevel  * and remove the device tree from the attachment point
9662305Sstevel  * and below.  The routine assumes the devices have their
9672305Sstevel  * drivers detached.
9682305Sstevel  */
9692305Sstevel static int
cbus_unconfigure(dev_info_t * devi,int prim_bus)9702305Sstevel cbus_unconfigure(dev_info_t *devi, int prim_bus)
9712305Sstevel {
9722305Sstevel 	pci_regspec_t *pci_rp;
9732305Sstevel 	uint_t bus, device, func, length;
9742305Sstevel 	int ndi_flags = NDI_UNCONFIG;
9752305Sstevel 
9762305Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, devi,
9772305Sstevel 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
9782305Sstevel 	    &length) != DDI_PROP_SUCCESS) {
9792305Sstevel 		/*
9802305Sstevel 		 * This cannot be one of our devices. If it's something like a
9812305Sstevel 		 * SCSI device then the attempt to offline the HBA
9822305Sstevel 		 * (which probably is one of our devices)
9832305Sstevel 		 * will also do bottom up offlining. That
9842305Sstevel 		 * will fail if this device is busy. So always
9852305Sstevel 		 * return success here
9862305Sstevel 		 * so that the walk will continue.
9872305Sstevel 		 */
9882305Sstevel 		return (NDI_SUCCESS);
9892305Sstevel 	}
9902305Sstevel 
9912305Sstevel 	if (pci_rp->pci_phys_hi == 0)
9922305Sstevel 		return (NDI_FAILURE);
9932305Sstevel 
9942305Sstevel 	bus = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
9952305Sstevel 
9962305Sstevel 	if (bus <= prim_bus)
9972305Sstevel 		return (NDI_SUCCESS);
9982305Sstevel 
9992305Sstevel 	device = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
10002305Sstevel 	func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
10012305Sstevel 	ddi_prop_free(pci_rp);
10022305Sstevel 
10032305Sstevel 	cardbus_err(devi, 8,
10042305Sstevel 	    "cbus_unconfigure: "
10052305Sstevel 	    "offline bus [0x%x] device [0x%x] function [%x]\n",
10062305Sstevel 	    bus, device, func);
10072305Sstevel 	if (ndi_devi_offline(devi, ndi_flags) != NDI_SUCCESS) {
10082305Sstevel 		cardbus_err(devi, 1,
10092305Sstevel 		    "Device [0x%x] function [%x] is busy\n", device, func);
10102305Sstevel 		return (NDI_FAILURE);
10112305Sstevel 	}
10122305Sstevel 
10132305Sstevel 	cardbus_err(devi, 9,
10142305Sstevel 	    "Tearing down device [0x%x] function [0x%x]\n", device, func);
10152305Sstevel 
10162305Sstevel 	if (cardbus_teardown_device(devi) != PCICFG_SUCCESS) {
10172305Sstevel 		cardbus_err(devi, 1,
10182305Sstevel 		    "Failed to tear down "
10192305Sstevel 		    "device [0x%x] function [0x%x]\n", device, func);
10202305Sstevel 		return (NDI_FAILURE);
10212305Sstevel 	}
10222305Sstevel 
10232305Sstevel 	return (NDI_SUCCESS);
10242305Sstevel }
10252305Sstevel 
10262305Sstevel boolean_t
cardbus_is_cb_minor(dev_t dev)10272305Sstevel cardbus_is_cb_minor(dev_t dev)
10282305Sstevel {
10292305Sstevel 	return (AP_IS_CB_MINOR(getminor(dev)) ? B_TRUE : B_FALSE);
10302305Sstevel }
10312305Sstevel 
10322305Sstevel int
cardbus_open(dev_t * devp,int flags,int otyp,cred_t * credp)10332305Sstevel cardbus_open(dev_t *devp, int flags, int otyp, cred_t *credp)
10342305Sstevel {
10352305Sstevel 	cbus_t *cbp;
10362305Sstevel 	int minor;
10372305Sstevel 
10382305Sstevel 	_NOTE(ARGUNUSED(credp))
10392305Sstevel 
10402305Sstevel 	minor = getminor(*devp);
10412305Sstevel 
10422305Sstevel 	/*
10432305Sstevel 	 * Make sure the open is for the right file type.
10442305Sstevel 	 */
10452305Sstevel 	if (otyp != OTYP_CHR)
10462305Sstevel 	return (EINVAL);
10472305Sstevel 
10482305Sstevel 	/*
10492305Sstevel 	 * Get the soft state structure for the 'devctl' device.
10502305Sstevel 	 */
10512305Sstevel 	cbp = (cbus_t *)ddi_get_soft_state(cardbus_state,
1052*8459SJerry.Gilliam@Sun.COM 	    AP_MINOR_NUM_TO_CB_INSTANCE(minor));
10532305Sstevel 	if (cbp == NULL)
10542305Sstevel 		return (ENXIO);
10552305Sstevel 
10562305Sstevel 	mutex_enter(&cbp->cb_mutex);
10572305Sstevel 
10582305Sstevel 	/*
10592305Sstevel 	 * Handle the open by tracking the device state.
10602305Sstevel 	 *
10612305Sstevel 	 * Note: Needs review w.r.t exclusive access to AP or the bus.
10622305Sstevel 	 * Currently in the pci plug-in we don't use EXCL open at all
10632305Sstevel 	 * so the code below implements EXCL access on the bus.
10642305Sstevel 	 */
10652305Sstevel 
10662305Sstevel 	/* enforce exclusive access to the bus */
10672305Sstevel 	if ((cbp->soft_state == PCIHP_SOFT_STATE_OPEN_EXCL) ||
10682305Sstevel 	    ((flags & FEXCL) &&
10692305Sstevel 	    (cbp->soft_state != PCIHP_SOFT_STATE_CLOSED))) {
10702305Sstevel 		mutex_exit(&cbp->cb_mutex);
10712305Sstevel 		return (EBUSY);
10722305Sstevel 	}
10732305Sstevel 
10742305Sstevel 	if (flags & FEXCL)
10752305Sstevel 		cbp->soft_state = PCIHP_SOFT_STATE_OPEN_EXCL;
10762305Sstevel 	else
10772305Sstevel 		cbp->soft_state = PCIHP_SOFT_STATE_OPEN;
10782305Sstevel 
10792305Sstevel 	mutex_exit(&cbp->cb_mutex);
10802305Sstevel 	return (0);
10812305Sstevel }
10822305Sstevel 
10832305Sstevel /*ARGSUSED*/
10842305Sstevel int
cardbus_close(dev_t dev,int flags,int otyp,cred_t * credp)10852305Sstevel cardbus_close(dev_t dev, int flags, int otyp, cred_t *credp)
10862305Sstevel {
10872305Sstevel 	cbus_t *cbp;
10882305Sstevel 	int minor;
10892305Sstevel 
10902305Sstevel 	_NOTE(ARGUNUSED(credp))
10912305Sstevel 
10922305Sstevel 	minor = getminor(dev);
10932305Sstevel 
10942305Sstevel 	if (otyp != OTYP_CHR)
10952305Sstevel 		return (EINVAL);
10962305Sstevel 
10972305Sstevel 	cbp = (cbus_t *)ddi_get_soft_state(cardbus_state,
10982305Sstevel 	    AP_MINOR_NUM_TO_CB_INSTANCE(minor));
10992305Sstevel 	if (cbp == NULL)
11002305Sstevel 		return (ENXIO);
11012305Sstevel 
11022305Sstevel 	mutex_enter(&cbp->cb_mutex);
11032305Sstevel 	cbp->soft_state = PCIHP_SOFT_STATE_CLOSED;
11042305Sstevel 	mutex_exit(&cbp->cb_mutex);
11052305Sstevel 	return (0);
11062305Sstevel }
11072305Sstevel 
11082305Sstevel /*
11092305Sstevel  * cardbus_ioctl: devctl hotplug controls
11102305Sstevel  */
11112305Sstevel /*ARGSUSED*/
11122305Sstevel int
cardbus_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)11132305Sstevel cardbus_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
11142305Sstevel 		int *rvalp)
11152305Sstevel {
11162305Sstevel 	cbus_t *cbp;
11172305Sstevel 	dev_info_t *self;
11182305Sstevel 	dev_info_t *child_dip = NULL;
11192305Sstevel 	struct devctl_iocdata *dcp;
11202305Sstevel 	uint_t bus_state;
11212305Sstevel 	int rv = 0;
11222305Sstevel 	int nrv = 0;
11232305Sstevel 	int ap_minor;
11242305Sstevel 	hpc_slot_state_t rstate;
11252305Sstevel 	devctl_ap_state_t ap_state;
11262305Sstevel 	struct hpc_control_data hpc_ctrldata;
11272305Sstevel 	struct hpc_led_info led_info;
11282305Sstevel 
11292305Sstevel 	_NOTE(ARGUNUSED(credp))
11302305Sstevel 
11312305Sstevel 	ap_minor = getminor(dev);
11322305Sstevel 	cbp = (cbus_t *)ddi_get_soft_state(cardbus_state,
11332305Sstevel 	    AP_MINOR_NUM_TO_CB_INSTANCE(ap_minor));
11342305Sstevel 	if (cbp == NULL)
11352305Sstevel 		return (ENXIO);
11362305Sstevel 
11372305Sstevel 	self = cbp->cb_dip;
11382305Sstevel 	/*
11392305Sstevel 	 * read devctl ioctl data
11402305Sstevel 	 */
11412305Sstevel 	if ((cmd != DEVCTL_AP_CONTROL) && ndi_dc_allochdl((void *)arg,
11422305Sstevel 	    &dcp) != NDI_SUCCESS)
11432305Sstevel 		return (EFAULT);
11442305Sstevel 
11452305Sstevel #ifdef CARDBUS_DEBUG
11462305Sstevel {
11472305Sstevel 	char *cmd_name;
11482305Sstevel 
11492305Sstevel 	switch (cmd) {
11502305Sstevel 	case DEVCTL_DEVICE_GETSTATE: cmd_name = "DEVCTL_DEVICE_GETSTATE"; break;
11512305Sstevel 	case DEVCTL_DEVICE_ONLINE: cmd_name = "DEVCTL_DEVICE_ONLINE"; break;
11522305Sstevel 	case DEVCTL_DEVICE_OFFLINE: cmd_name = "DEVCTL_DEVICE_OFFLINE"; break;
11532305Sstevel 	case DEVCTL_DEVICE_RESET: cmd_name = "DEVCTL_DEVICE_RESET"; break;
11542305Sstevel 	case DEVCTL_BUS_QUIESCE: cmd_name = "DEVCTL_BUS_QUIESCE"; break;
11552305Sstevel 	case DEVCTL_BUS_UNQUIESCE: cmd_name = "DEVCTL_BUS_UNQUIESCE"; break;
11562305Sstevel 	case DEVCTL_BUS_RESET: cmd_name = "DEVCTL_BUS_RESET"; break;
11572305Sstevel 	case DEVCTL_BUS_RESETALL: cmd_name = "DEVCTL_BUS_RESETALL"; break;
11582305Sstevel 	case DEVCTL_BUS_GETSTATE: cmd_name = "DEVCTL_BUS_GETSTATE"; break;
11592305Sstevel 	case DEVCTL_AP_CONNECT: cmd_name = "DEVCTL_AP_CONNECT"; break;
11602305Sstevel 	case DEVCTL_AP_DISCONNECT: cmd_name = "DEVCTL_AP_DISCONNECT"; break;
11612305Sstevel 	case DEVCTL_AP_INSERT: cmd_name = "DEVCTL_AP_INSERT"; break;
11622305Sstevel 	case DEVCTL_AP_REMOVE: cmd_name = "DEVCTL_AP_REMOVE"; break;
11632305Sstevel 	case DEVCTL_AP_CONFIGURE: cmd_name = "DEVCTL_AP_CONFIGURE"; break;
11642305Sstevel 	case DEVCTL_AP_UNCONFIGURE: cmd_name = "DEVCTL_AP_UNCONFIGURE"; break;
11652305Sstevel 	case DEVCTL_AP_GETSTATE: cmd_name = "DEVCTL_AP_GETSTATE"; break;
11662305Sstevel 	case DEVCTL_AP_CONTROL: cmd_name = "DEVCTL_AP_CONTROL"; break;
11672305Sstevel 	default: cmd_name = "Unknown"; break;
11682305Sstevel 	}
11692305Sstevel 	cardbus_err(cbp->cb_dip, 7,
11702305Sstevel 	    "cardbus_ioctl: cmd = 0x%x, \"%s\"", cmd, cmd_name);
11712305Sstevel }
11722305Sstevel #endif
11732305Sstevel 
11742305Sstevel 	switch (cmd) {
11752305Sstevel 	case DEVCTL_DEVICE_GETSTATE:
11762305Sstevel 	case DEVCTL_DEVICE_ONLINE:
11772305Sstevel 	case DEVCTL_DEVICE_OFFLINE:
11782305Sstevel 	case DEVCTL_BUS_GETSTATE:
11792305Sstevel 		rv = ndi_devctl_ioctl(self, cmd, arg, mode, 0);
11802305Sstevel 		ndi_dc_freehdl(dcp);
11812305Sstevel 		return (rv);
11822305Sstevel 	default:
11832305Sstevel 		break;
11842305Sstevel 	}
11852305Sstevel 
11862305Sstevel 	switch (cmd) {
11872305Sstevel 	case DEVCTL_DEVICE_RESET:
11882305Sstevel 		rv = ENOTSUP;
11892305Sstevel 		break;
11902305Sstevel 
11912305Sstevel 	case DEVCTL_BUS_QUIESCE:
11922305Sstevel 		if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS)
11932305Sstevel 			if (bus_state == BUS_QUIESCED)
11942305Sstevel 				break;
11952305Sstevel 		(void) ndi_set_bus_state(self, BUS_QUIESCED);
11962305Sstevel 		break;
11972305Sstevel 
11982305Sstevel 	case DEVCTL_BUS_UNQUIESCE:
11992305Sstevel 		if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS)
12002305Sstevel 			if (bus_state == BUS_ACTIVE)
12012305Sstevel 				break;
12022305Sstevel 		(void) ndi_set_bus_state(self, BUS_ACTIVE);
12032305Sstevel 		break;
12042305Sstevel 
12052305Sstevel 	case DEVCTL_BUS_RESET:
12062305Sstevel 		rv = ENOTSUP;
12072305Sstevel 		break;
12082305Sstevel 
12092305Sstevel 	case DEVCTL_BUS_RESETALL:
12102305Sstevel 		rv = ENOTSUP;
12112305Sstevel 		break;
12122305Sstevel 
12132305Sstevel 	case DEVCTL_AP_CONNECT:
12142305Sstevel 	case DEVCTL_AP_DISCONNECT:
12152305Sstevel 		/*
12162305Sstevel 		 * CONNECT(DISCONNECT) the hot plug slot to(from) the bus.
12172305Sstevel 		 */
12182305Sstevel 	case DEVCTL_AP_INSERT:
12192305Sstevel 	case DEVCTL_AP_REMOVE:
12202305Sstevel 		/*
12212305Sstevel 		 * Prepare the slot for INSERT/REMOVE operation.
12222305Sstevel 		 */
12232305Sstevel 
12242305Sstevel 		/*
12252305Sstevel 		 * check for valid request:
12262305Sstevel 		 * 	1. It is a hotplug slot.
12272305Sstevel 		 * 	2. The slot has no occupant that is in
12282305Sstevel 		 * 	the 'configured' state.
12292305Sstevel 		 *
12302305Sstevel 		 * The lower 8 bits of the minor number is the PCI
12312305Sstevel 		 * device number for the slot.
12322305Sstevel 		 */
12332305Sstevel 		if ((cbp->slot_handle == NULL) || cbp->disabled) {
12342305Sstevel 			rv = ENXIO;
12352305Sstevel 			break;
12362305Sstevel 		}
12372305Sstevel 
12382305Sstevel 		/* the slot occupant must be in the UNCONFIGURED state */
12392305Sstevel 		if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
12402305Sstevel 			rv = EINVAL;
12412305Sstevel 			break;
12422305Sstevel 		}
12432305Sstevel 
12442305Sstevel 		/*
12452305Sstevel 		 * Call the HPC driver to perform the operation on the slot.
12462305Sstevel 		 */
12472305Sstevel 		mutex_enter(&cbp->cb_mutex);
12482305Sstevel 		switch (cmd) {
12492305Sstevel 		case DEVCTL_AP_INSERT:
12502305Sstevel 			rv = hpc_nexus_insert(cbp->slot_handle, NULL, 0);
12512305Sstevel 			break;
12522305Sstevel 		case DEVCTL_AP_REMOVE:
12532305Sstevel 			rv = hpc_nexus_remove(cbp->slot_handle, NULL, 0);
12542305Sstevel 			break;
12552305Sstevel 		case DEVCTL_AP_CONNECT:
12562305Sstevel 			if ((rv = hpc_nexus_connect(cbp->slot_handle,
12572305Sstevel 			    NULL, 0)) == 0)
12582305Sstevel 				cbp->rstate = AP_RSTATE_CONNECTED;
12592305Sstevel 			break;
12602305Sstevel 		case DEVCTL_AP_DISCONNECT:
12612305Sstevel 			if ((rv = hpc_nexus_disconnect(cbp->slot_handle,
12622305Sstevel 			    NULL, 0)) == 0)
12632305Sstevel 				cbp->rstate = AP_RSTATE_DISCONNECTED;
12642305Sstevel 			break;
12652305Sstevel 		}
12662305Sstevel 		mutex_exit(&cbp->cb_mutex);
12672305Sstevel 
12682305Sstevel 		switch (rv) {
12692305Sstevel 		case HPC_ERR_INVALID:
12702305Sstevel 			rv = ENXIO;
12712305Sstevel 			break;
12722305Sstevel 		case HPC_ERR_NOTSUPPORTED:
12732305Sstevel 			rv = ENOTSUP;
12742305Sstevel 			break;
12752305Sstevel 		case HPC_ERR_FAILED:
12762305Sstevel 			rv = EIO;
12772305Sstevel 			break;
12782305Sstevel 		}
12792305Sstevel 
12802305Sstevel 		break;
12812305Sstevel 
12822305Sstevel 	case DEVCTL_AP_CONFIGURE:
12832305Sstevel 		/*
12842305Sstevel 		 * **************************************
12852305Sstevel 		 * CONFIGURE the occupant in the slot.
12862305Sstevel 		 * **************************************
12872305Sstevel 		 */
12882305Sstevel 
12892305Sstevel 		mutex_enter(&cbp->cb_mutex);
12902305Sstevel 		if ((nrv = cardbus_configure_ap(cbp)) == HPC_SUCCESS) {
12912305Sstevel 			create_occupant_props(cbp->cb_dip, dev);
12922305Sstevel 		} else
12932305Sstevel 			rv = nrv;
12942305Sstevel 		mutex_exit(&cbp->cb_mutex);
12952305Sstevel 		break;
12962305Sstevel 
12972305Sstevel 	case DEVCTL_AP_UNCONFIGURE:
12982305Sstevel 		/*
12992305Sstevel 		 * **************************************
13002305Sstevel 		 * UNCONFIGURE the occupant in the slot.
13012305Sstevel 		 * **************************************
13022305Sstevel 		 */
13032305Sstevel 
13042305Sstevel 		mutex_enter(&cbp->cb_mutex);
13052305Sstevel 		if ((nrv = cardbus_unconfigure_ap(cbp)) == HPC_SUCCESS) {
13062305Sstevel 			delete_occupant_props(cbp->cb_dip, dev);
13072305Sstevel 		} else
13082305Sstevel 			rv = nrv;
13092305Sstevel 		mutex_exit(&cbp->cb_mutex);
13102305Sstevel 		break;
13112305Sstevel 
13122305Sstevel 	case DEVCTL_AP_GETSTATE:
13132305Sstevel 	    {
13142305Sstevel 		int mutex_held;
13152305Sstevel 
13162305Sstevel 		/*
13172305Sstevel 		 * return the state of Attachment Point.
13182305Sstevel 		 *
13192305Sstevel 		 * If the occupant is in UNCONFIGURED state then
13202305Sstevel 		 * we should get the receptacle state from the
13212305Sstevel 		 * HPC driver because the receptacle state
13222305Sstevel 		 * maintained in the nexus may not be accurate.
13232305Sstevel 		 */
13242305Sstevel 
13252305Sstevel 		/*
13262305Sstevel 		 * check for valid request:
13272305Sstevel 		 * 	1. It is a hotplug slot.
13282305Sstevel 		 */
13292305Sstevel 		if (cbp->slot_handle == NULL) {
13302305Sstevel 			rv = ENXIO;
13312305Sstevel 			break;
13322305Sstevel 		}
13332305Sstevel 
13342305Sstevel 		/* try to acquire the slot mutex */
13352305Sstevel 		mutex_held = mutex_tryenter(&cbp->cb_mutex);
13362305Sstevel 
13372305Sstevel 		if (cbp->ostate == AP_OSTATE_UNCONFIGURED) {
13382305Sstevel 			if (hpc_nexus_control(cbp->slot_handle,
13392305Sstevel 			    HPC_CTRL_GET_SLOT_STATE,
13402305Sstevel 			    (caddr_t)&rstate) != 0) {
13412305Sstevel 				rv = ENXIO;
13422305Sstevel 				if (mutex_held)
13432305Sstevel 					mutex_exit(&cbp->cb_mutex);
13442305Sstevel 				break;
13452305Sstevel 			}
13462305Sstevel 			cbp->rstate = (ap_rstate_t)rstate;
13472305Sstevel 		}
13482305Sstevel 
13492305Sstevel 		ap_state.ap_rstate = cbp->rstate;
13502305Sstevel 		ap_state.ap_ostate = cbp->ostate;
13512305Sstevel 		ap_state.ap_condition = cbp->condition;
13522305Sstevel 		ap_state.ap_last_change = 0;
13532305Sstevel 		ap_state.ap_error_code = 0;
13542305Sstevel 		if (mutex_held)
13552305Sstevel 			ap_state.ap_in_transition = 0; /* AP is not busy */
13562305Sstevel 		else
13572305Sstevel 			ap_state.ap_in_transition = 1; /* AP is busy */
13582305Sstevel 
13592305Sstevel 		if (mutex_held)
13602305Sstevel 			mutex_exit(&cbp->cb_mutex);
13612305Sstevel 
13622305Sstevel 		/* copy the return-AP-state information to the user space */
13632305Sstevel 		if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS)
13642305Sstevel 			rv = ENXIO;
13652305Sstevel 
13662305Sstevel 		break;
13672305Sstevel 
13682305Sstevel 	    }
13692305Sstevel 
13702305Sstevel 	case DEVCTL_AP_CONTROL:
13712305Sstevel 		/*
13722305Sstevel 		 * HPC control functions:
13732305Sstevel 		 * 	HPC_CTRL_ENABLE_SLOT/HPC_CTRL_DISABLE_SLOT
13742305Sstevel 		 * 		Changes the state of the slot and preserves
13752305Sstevel 		 * 		the state across the reboot.
13762305Sstevel 		 * 	HPC_CTRL_ENABLE_AUTOCFG/HPC_CTRL_DISABLE_AUTOCFG
13772305Sstevel 		 * 		Enables or disables the auto configuration
13782305Sstevel 		 * 		of hot plugged occupant if the hardware
13792305Sstevel 		 * 		supports notification of the hot plug
13802305Sstevel 		 * 		events.
13812305Sstevel 		 * 	HPC_CTRL_GET_LED_STATE/HPC_CTRL_SET_LED_STATE
13822305Sstevel 		 * 		Controls the state of an LED.
13832305Sstevel 		 * 	HPC_CTRL_GET_SLOT_INFO
13842305Sstevel 		 * 		Get slot information data structure
13852305Sstevel 		 * 		(hpc_slot_info_t).
13862305Sstevel 		 * 	HPC_CTRL_GET_BOARD_TYPE
13872305Sstevel 		 * 		Get board type information (hpc_board_type_t).
13882305Sstevel 		 * 	HPC_CTRL_GET_CARD_INFO
13892305Sstevel 		 * 		Get card information (hpc_card_info_t).
13902305Sstevel 		 *
13912305Sstevel 		 * These control functions are used by the cfgadm plug-in
13922305Sstevel 		 * to implement "-x" and "-v" options.
13932305Sstevel 		 */
13942305Sstevel 
13952305Sstevel 		/* copy user ioctl data first */
13962305Sstevel #ifdef _MULTI_DATAMODEL
13972305Sstevel 		if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
13982305Sstevel 			struct hpc_control32_data hpc_ctrldata32;
13992305Sstevel 
14002305Sstevel 			if (copyin((void *)arg, (void *)&hpc_ctrldata32,
14012305Sstevel 			    sizeof (struct hpc_control32_data)) != 0) {
14022305Sstevel 				rv = EFAULT;
14032305Sstevel 				break;
14042305Sstevel 			}
14052305Sstevel 			hpc_ctrldata.cmd = hpc_ctrldata32.cmd;
14062305Sstevel 			hpc_ctrldata.data =
14072305Sstevel 			    (void *)(intptr_t)hpc_ctrldata32.data;
14082305Sstevel 		}
14092305Sstevel #else
14102305Sstevel 		if (copyin((void *)arg, (void *)&hpc_ctrldata,
14112305Sstevel 		    sizeof (struct hpc_control_data)) != 0) {
14122305Sstevel 			rv = EFAULT;
14132305Sstevel 			break;
14142305Sstevel 		}
14152305Sstevel #endif
14162305Sstevel 
14172305Sstevel #ifdef CARDBUS_DEBUG
14182305Sstevel {
14192305Sstevel 		char *hpc_name;
14202305Sstevel 		switch (hpc_ctrldata.cmd) {
14212305Sstevel 		case HPC_CTRL_GET_LED_STATE:
14222305Sstevel 			hpc_name = "HPC_CTRL_GET_LED_STATE";
14232305Sstevel 			break;
14242305Sstevel 		case HPC_CTRL_SET_LED_STATE:
14252305Sstevel 			hpc_name = "HPC_CTRL_SET_LED_STATE";
14262305Sstevel 			break;
14272305Sstevel 		case HPC_CTRL_ENABLE_SLOT:
14282305Sstevel 			hpc_name = "HPC_CTRL_ENABLE_SLOT";
14292305Sstevel 			break;
14302305Sstevel 		case HPC_CTRL_DISABLE_SLOT:
14312305Sstevel 			hpc_name = "HPC_CTRL_DISABLE_SLOT";
14322305Sstevel 			break;
14332305Sstevel 		case HPC_CTRL_ENABLE_AUTOCFG:
14342305Sstevel 			hpc_name = "HPC_CTRL_ENABLE_AUTOCFG";
14352305Sstevel 			break;
14362305Sstevel 		case HPC_CTRL_DISABLE_AUTOCFG:
14372305Sstevel 			hpc_name = "HPC_CTRL_DISABLE_AUTOCFG";
14382305Sstevel 			break;
14392305Sstevel 		case HPC_CTRL_GET_BOARD_TYPE:
14402305Sstevel 			hpc_name = "HPC_CTRL_GET_BOARD_TYPE";
14412305Sstevel 			break;
14422305Sstevel 		case HPC_CTRL_GET_SLOT_INFO:
14432305Sstevel 			hpc_name = "HPC_CTRL_GET_SLOT_INFO";
14442305Sstevel 			break;
14452305Sstevel 		case HPC_CTRL_GET_CARD_INFO:
14462305Sstevel 			hpc_name = "HPC_CTRL_GET_CARD_INFO";
14472305Sstevel 			break;
14482305Sstevel 		default: hpc_name = "Unknown"; break;
14492305Sstevel 		}
14502305Sstevel 		cardbus_err(cbp->cb_dip, 7,
14512305Sstevel 		    "cardbus_ioctl: HP Control cmd 0x%x - \"%s\"",
14522305Sstevel 		    hpc_ctrldata.cmd, hpc_name);
14532305Sstevel }
14542305Sstevel #endif
14552305Sstevel 		/*
14562305Sstevel 		 * check for valid request:
14572305Sstevel 		 * 	1. It is a hotplug slot.
14582305Sstevel 		 */
14592305Sstevel 		if (cbp->slot_handle == NULL) {
14602305Sstevel 			rv = ENXIO;
14612305Sstevel 			break;
14622305Sstevel 		}
14632305Sstevel 
14642305Sstevel 		mutex_enter(&cbp->cb_mutex);
14652305Sstevel 		switch (hpc_ctrldata.cmd) {
14662305Sstevel 		case HPC_CTRL_GET_LED_STATE:
14672305Sstevel 			/* copy the led info from the user space */
14682305Sstevel 			if (copyin(hpc_ctrldata.data, (void *)&led_info,
14692305Sstevel 			    sizeof (hpc_led_info_t)) != 0) {
14702305Sstevel 				rv = ENXIO;
14712305Sstevel 				break;
14722305Sstevel 			}
14732305Sstevel 
14742305Sstevel 			/* get the state of LED information */
14752305Sstevel 			if (hpc_nexus_control(cbp->slot_handle,
14762305Sstevel 			    HPC_CTRL_GET_LED_STATE,
14772305Sstevel 			    (caddr_t)&led_info) != 0) {
14782305Sstevel 				rv = ENXIO;
14792305Sstevel 				break;
14802305Sstevel 			}
14812305Sstevel 
14822305Sstevel 			/* copy the led info to the user space */
14832305Sstevel 			if (copyout((void *)&led_info, hpc_ctrldata.data,
14842305Sstevel 			    sizeof (hpc_led_info_t)) != 0) {
14852305Sstevel 				rv = ENXIO;
14862305Sstevel 				break;
14872305Sstevel 			}
14882305Sstevel 			break;
14892305Sstevel 
14902305Sstevel 		case HPC_CTRL_SET_LED_STATE:
14912305Sstevel 			/* copy the led info from the user space */
14922305Sstevel 			if (copyin(hpc_ctrldata.data, (void *)&led_info,
14932305Sstevel 			    sizeof (hpc_led_info_t)) != 0) {
14942305Sstevel 				rv = ENXIO;
14952305Sstevel 				break;
14962305Sstevel 			}
14972305Sstevel 
14982305Sstevel 			/* set the state of an LED */
14992305Sstevel 			if (hpc_nexus_control(cbp->slot_handle,
15002305Sstevel 			    HPC_CTRL_SET_LED_STATE,
15012305Sstevel 			    (caddr_t)&led_info) != 0) {
15022305Sstevel 				rv = ENXIO;
15032305Sstevel 				break;
15042305Sstevel 			}
15052305Sstevel 
15062305Sstevel 			break;
15072305Sstevel 
15082305Sstevel 		case HPC_CTRL_ENABLE_SLOT:
15092305Sstevel 			/*
15102305Sstevel 			 * Enable the slot for hotplug operations.
15112305Sstevel 			 */
15122305Sstevel 			cbp->disabled = B_FALSE;
15132305Sstevel 
15142305Sstevel 			/* tell the HPC driver also */
15152305Sstevel 			(void) hpc_nexus_control(cbp->slot_handle,
15162305Sstevel 				HPC_CTRL_ENABLE_SLOT, NULL);
15172305Sstevel 
15182305Sstevel 			break;
15192305Sstevel 
15202305Sstevel 		case HPC_CTRL_DISABLE_SLOT:
15212305Sstevel 			/*
15222305Sstevel 			 * Disable the slot for hotplug operations.
15232305Sstevel 			 */
15242305Sstevel 			cbp->disabled = B_TRUE;
15252305Sstevel 
15262305Sstevel 			/* tell the HPC driver also */
15272305Sstevel 			(void) hpc_nexus_control(cbp->slot_handle,
15282305Sstevel 				HPC_CTRL_DISABLE_SLOT, NULL);
15292305Sstevel 
15302305Sstevel 			break;
15312305Sstevel 
15322305Sstevel 		case HPC_CTRL_ENABLE_AUTOCFG:
15332305Sstevel 			/*
15342305Sstevel 			 * Enable auto configuration on this slot.
15352305Sstevel 			 */
15362305Sstevel 			cbp->auto_config = B_TRUE;
15372305Sstevel 
15382305Sstevel 			/* tell the HPC driver also */
15392305Sstevel 			(void) hpc_nexus_control(cbp->slot_handle,
15402305Sstevel 				HPC_CTRL_ENABLE_AUTOCFG, NULL);
15412305Sstevel 			break;
15422305Sstevel 
15432305Sstevel 		case HPC_CTRL_DISABLE_AUTOCFG:
15442305Sstevel 			/*
15452305Sstevel 			 * Disable auto configuration on this slot.
15462305Sstevel 			 */
15472305Sstevel 			cbp->auto_config = B_FALSE;
15482305Sstevel 
15492305Sstevel 			/* tell the HPC driver also */
15502305Sstevel 			(void) hpc_nexus_control(cbp->slot_handle,
15512305Sstevel 				HPC_CTRL_DISABLE_AUTOCFG, NULL);
15522305Sstevel 
15532305Sstevel 			break;
15542305Sstevel 
15552305Sstevel 		case HPC_CTRL_GET_BOARD_TYPE:
15562305Sstevel 		    {
15572305Sstevel 			hpc_board_type_t board_type;
15582305Sstevel 
15592305Sstevel 			/*
15602305Sstevel 			 * Get board type data structure, hpc_board_type_t.
15612305Sstevel 			 */
15622305Sstevel 			if (hpc_nexus_control(cbp->slot_handle,
15632305Sstevel 			    HPC_CTRL_GET_BOARD_TYPE,
15642305Sstevel 			    (caddr_t)&board_type) != 0) {
15652305Sstevel 				rv = ENXIO;
15662305Sstevel 				break;
15672305Sstevel 			}
15682305Sstevel 
15692305Sstevel 			/* copy the board type info to the user space */
15702305Sstevel 			if (copyout((void *)&board_type, hpc_ctrldata.data,
15712305Sstevel 			    sizeof (hpc_board_type_t)) != 0) {
15722305Sstevel 				rv = ENXIO;
15732305Sstevel 				break;
15742305Sstevel 			}
15752305Sstevel 
15762305Sstevel 			break;
15772305Sstevel 		    }
15782305Sstevel 
15792305Sstevel 		case HPC_CTRL_GET_SLOT_INFO:
15802305Sstevel 		    {
15812305Sstevel 			hpc_slot_info_t slot_info;
15822305Sstevel 
15832305Sstevel 			/*
15842305Sstevel 			 * Get slot information structure, hpc_slot_info_t.
15852305Sstevel 			 */
15862305Sstevel 			slot_info.version = HPC_SLOT_INFO_VERSION;
15872305Sstevel 			slot_info.slot_type = 0;
15882305Sstevel 			slot_info.pci_slot_capabilities = 0;
15892305Sstevel 			slot_info.pci_dev_num =
15902305Sstevel 				(uint16_t)AP_MINOR_NUM_TO_CB_INSTANCE(ap_minor);
15912305Sstevel 			(void) strcpy(slot_info.pci_slot_name, cbp->name);
15922305Sstevel 
15932305Sstevel 			/* copy the slot info structure to the user space */
15942305Sstevel 			if (copyout((void *)&slot_info, hpc_ctrldata.data,
15952305Sstevel 			    sizeof (hpc_slot_info_t)) != 0) {
15962305Sstevel 				rv = ENXIO;
15972305Sstevel 				break;
15982305Sstevel 			}
15992305Sstevel 
16002305Sstevel 			break;
16012305Sstevel 		    }
16022305Sstevel 
16032305Sstevel 		case HPC_CTRL_GET_CARD_INFO:
16042305Sstevel 		    {
16052305Sstevel 			hpc_card_info_t card_info;
16062305Sstevel 			ddi_acc_handle_t handle;
16072305Sstevel 
16082305Sstevel 			/*
16092305Sstevel 			 * Get card information structure, hpc_card_info_t.
16102305Sstevel 			 */
16112305Sstevel 
16122451Srw148561 			if (cbp->card_present == B_FALSE) {
16132451Srw148561 				rv = ENXIO;
16142451Srw148561 				break;
16152451Srw148561 			}
16162305Sstevel 			/* verify that the card is configured */
16172305Sstevel 			if (cbp->ostate != AP_OSTATE_CONFIGURED) {
16182305Sstevel 				/* either the card is not present or */
16192305Sstevel 				/* it is not configured. */
16202305Sstevel 				rv = ENXIO;
16212305Sstevel 				break;
16222305Sstevel 			}
16232305Sstevel 
16242305Sstevel 			/* get the information from the PCI config header */
16252305Sstevel 			/* for the function 0. */
16262305Sstevel 			for (child_dip = ddi_get_child(cbp->cb_dip); child_dip;
16272305Sstevel 			    child_dip = ddi_get_next_sibling(child_dip))
16282305Sstevel 				if (strcmp("pcs", ddi_get_name(child_dip)))
16292305Sstevel 					break;
16302305Sstevel 
16312305Sstevel 			if (!child_dip) {
16322305Sstevel 				rv = ENXIO;
16332305Sstevel 				break;
16342305Sstevel 			}
16352305Sstevel 
16362305Sstevel 			if (pci_config_setup(child_dip, &handle)
16372305Sstevel 			    != DDI_SUCCESS) {
16382305Sstevel 				rv = EIO;
16392305Sstevel 				break;
16402305Sstevel 			}
16412305Sstevel 			card_info.prog_class = pci_config_get8(handle,
16422305Sstevel 							PCI_CONF_PROGCLASS);
16432305Sstevel 			card_info.base_class = pci_config_get8(handle,
16442305Sstevel 							PCI_CONF_BASCLASS);
16452305Sstevel 			card_info.sub_class = pci_config_get8(handle,
16462305Sstevel 							PCI_CONF_SUBCLASS);
16472305Sstevel 			card_info.header_type = pci_config_get8(handle,
16482305Sstevel 							PCI_CONF_HEADER);
16492305Sstevel 			pci_config_teardown(&handle);
16502305Sstevel 
16512305Sstevel 			/* copy the card info structure to the user space */
16522305Sstevel 			if (copyout((void *)&card_info, hpc_ctrldata.data,
16532305Sstevel 			    sizeof (hpc_card_info_t)) != 0) {
16542305Sstevel 				rv = ENXIO;
16552305Sstevel 				break;
16562305Sstevel 			}
16572305Sstevel 
16582305Sstevel 			break;
16592305Sstevel 		    }
16602305Sstevel 
16612305Sstevel 		default:
16622305Sstevel 			rv = EINVAL;
16632305Sstevel 			break;
16642305Sstevel 		}
16652305Sstevel 
16662305Sstevel 		mutex_exit(&cbp->cb_mutex);
16672305Sstevel 		break;
16682305Sstevel 
16692305Sstevel 	default:
16702305Sstevel 		rv = ENOTTY;
16712305Sstevel 	}
16722305Sstevel 
16732305Sstevel 	if (cmd != DEVCTL_AP_CONTROL)
16742305Sstevel 		ndi_dc_freehdl(dcp);
16752305Sstevel 
16762305Sstevel 	cardbus_err(cbp->cb_dip, 7,
16772305Sstevel 	    "cardbus_ioctl: rv = 0x%x", rv);
16782305Sstevel 
16792305Sstevel 	return (rv);
16802305Sstevel }
16812305Sstevel 
16822305Sstevel struct cardbus_pci_desc {
16832305Sstevel 	char	*name;
16842305Sstevel 	ushort_t	offset;
16852305Sstevel 	int	(*cfg_get_func)();
16862305Sstevel 	char	*fmt;
16872305Sstevel };
16882305Sstevel 
16892305Sstevel static struct cardbus_pci_desc generic_pci_cfg[] = {
16902305Sstevel 	    { "VendorId    =", 0, (int(*)())pci_config_get16, "%s 0x%04x" },
16912305Sstevel 	    { "DeviceId    =", 2, (int(*)())pci_config_get16, "%s 0x%04x" },
16922305Sstevel 	    { "Command     =", 4, (int(*)())pci_config_get16, "%s 0x%04x" },
16932305Sstevel 	    { "Status      =", 6, (int(*)())pci_config_get16, "%s 0x%04x" },
16942305Sstevel 	    { "Latency     =", 0xd, (int(*)())pci_config_get8, "%s 0x%02x" },
16952305Sstevel 	    { "BASE0       =", 0x10, (int(*)())pci_config_get32, "%s 0x%08x" },
16962305Sstevel 	    { "BASE1       =", 0x14, (int(*)())pci_config_get32, "%s 0x%08x" },
16972305Sstevel 	    { "BASE2       =", 0x18, (int(*)())pci_config_get32, "%s 0x%08x" },
16982305Sstevel 	    { "BASE3       =", 0x1c, (int(*)())pci_config_get32, "%s 0x%08x" },
16992305Sstevel 	    { "BASE4       =", 0x20, (int(*)())pci_config_get32, "%s 0x%08x" },
17002305Sstevel 	    { "CIS Pointer =", 0x28, (int(*)())pci_config_get32, "%s 0x%08x" },
17012305Sstevel 	    { "ILINE       =", 0x3c, (int(*)())pci_config_get8, "%s 0x%02x" },
17022305Sstevel 	    { "IPIN        =", 0x3d, (int(*)())pci_config_get8, "%s 0x%02x" },
17032305Sstevel 	    { NULL, 0, NULL, NULL }
17042305Sstevel };
17052305Sstevel 
17062305Sstevel static struct cardbus_pci_desc cardbus_pci_cfg[] = {
17072305Sstevel 	    { "VendorId    =", 0, (int(*)())pci_config_get16, "%s 0x%04x" },
17082305Sstevel 	    { "DeviceId    =", 2, (int(*)())pci_config_get16, "%s 0x%04x" },
17092305Sstevel 	    { "Command     =", 4, (int(*)())pci_config_get16, "%s 0x%04x" },
17102305Sstevel 	    { "Status      =", 6, (int(*)())pci_config_get16, "%s 0x%04x" },
17112305Sstevel 	    { "CacheLineSz =", 0xc, (int(*)())pci_config_get8, "%s 0x%02x" },
17122305Sstevel 	    { "Latency     =", 0xd, (int(*)())pci_config_get8, "%s 0x%02x" },
17132305Sstevel 	    { "MemBase Addr=", 0x10, (int(*)())pci_config_get32, "%s 0x%08x" },
17142305Sstevel 	    { "Pri Bus     =", 0x18, (int(*)())pci_config_get8, "%s 0x%02x" },
17152305Sstevel 	    { "Sec Bus     =", 0x19, (int(*)())pci_config_get8, "%s 0x%02x" },
17162305Sstevel 	    { "Sub Bus     =", 0x1a, (int(*)())pci_config_get8, "%s 0x%02x" },
17172305Sstevel 	    { "CBus Latency=", 0x1b, (int(*)())pci_config_get8, "%s 0x%02x" },
17182305Sstevel 	    { "Mem0 Base   =", 0x1c, (int(*)())pci_config_get32, "%s 0x%08x" },
17192305Sstevel 	    { "Mem0 Limit  =", 0x20, (int(*)())pci_config_get32, "%s 0x%08x" },
17202305Sstevel 	    { "Mem1 Base   =", 0x24, (int(*)())pci_config_get32, "%s 0x%08x" },
17212305Sstevel 	    { "Mem1 Limit  =", 0x28, (int(*)())pci_config_get32, "%s 0x%08x" },
17222305Sstevel 	    { "I/O0 Base   =", 0x2c, (int(*)())pci_config_get32, "%s 0x%08x" },
17232305Sstevel 	    { "I/O0 Limit  =", 0x30, (int(*)())pci_config_get32, "%s 0x%08x" },
17242305Sstevel 	    { "I/O1 Base   =", 0x34, (int(*)())pci_config_get32, "%s 0x%08x" },
17252305Sstevel 	    { "I/O1 Limit  =", 0x38, (int(*)())pci_config_get32, "%s 0x%08x" },
17262305Sstevel 	    { "ILINE       =", 0x3c, (int(*)())pci_config_get8, "%s 0x%02x" },
17272305Sstevel 	    { "IPIN        =", 0x3d, (int(*)())pci_config_get8, "%s 0x%02x" },
17282305Sstevel 	    { "Bridge Ctrl =", 0x3e, (int(*)())pci_config_get16, "%s 0x%04x" },
17292305Sstevel 	    { "Legacy Addr =", 0x44, (int(*)())pci_config_get32, "%s 0x%08x" },
17302305Sstevel 	    { NULL, 0, NULL, NULL }
17312305Sstevel };
17322305Sstevel 
17332305Sstevel static void
cardbus_dump(struct cardbus_pci_desc * spcfg,ddi_acc_handle_t handle)17342305Sstevel cardbus_dump(struct cardbus_pci_desc *spcfg, ddi_acc_handle_t handle)
17352305Sstevel {
17362305Sstevel 	int	i;
17372305Sstevel 	for (i = 0; spcfg[i].name; i++) {
17382305Sstevel 
17392305Sstevel 		cmn_err(CE_NOTE, spcfg[i].fmt, spcfg[i].name,
1740*8459SJerry.Gilliam@Sun.COM 		    spcfg[i].cfg_get_func(handle, spcfg[i].offset));
17412305Sstevel 	}
17422305Sstevel 
17432305Sstevel }
17442305Sstevel 
17452305Sstevel void
cardbus_dump_pci_node(dev_info_t * dip)17462305Sstevel cardbus_dump_pci_node(dev_info_t *dip)
17472305Sstevel {
17482305Sstevel 	dev_info_t *next;
17492305Sstevel 	struct cardbus_pci_desc *spcfg;
17502305Sstevel 	ddi_acc_handle_t config_handle;
17512305Sstevel 	uint32_t VendorId;
17522305Sstevel 
17532305Sstevel 	cmn_err(CE_NOTE, "\nPCI leaf node of dip 0x%p:\n", (void *)dip);
17542305Sstevel 	for (next = ddi_get_child(dip); next;
1755*8459SJerry.Gilliam@Sun.COM 	    next = ddi_get_next_sibling(next)) {
17562305Sstevel 
17572305Sstevel 		VendorId = ddi_getprop(DDI_DEV_T_ANY, next,
1758*8459SJerry.Gilliam@Sun.COM 		    DDI_PROP_CANSLEEP|DDI_PROP_DONTPASS,
1759*8459SJerry.Gilliam@Sun.COM 		    "vendor-id", -1);
17602305Sstevel 		if (VendorId == -1) {
17612305Sstevel 			/* not a pci device */
17622305Sstevel 			continue;
17632305Sstevel 		}
17642305Sstevel 
17652305Sstevel 		if (pci_config_setup(next, &config_handle) != DDI_SUCCESS) {
17662305Sstevel 			cmn_err(CE_WARN, "!pcic child: non pci device\n");
17672305Sstevel 			continue;
17682305Sstevel 		}
17692305Sstevel 
17702305Sstevel 		spcfg = generic_pci_cfg;
17712305Sstevel 		cardbus_dump(spcfg, config_handle);
17722305Sstevel 		pci_config_teardown(&config_handle);
17732305Sstevel 
17742305Sstevel 	}
17752305Sstevel 
17762305Sstevel }
17772305Sstevel 
17782305Sstevel void
cardbus_dump_pci_config(dev_info_t * dip)17792305Sstevel cardbus_dump_pci_config(dev_info_t *dip)
17802305Sstevel {
17812305Sstevel 	struct cardbus_pci_desc *spcfg;
17822305Sstevel 	ddi_acc_handle_t config_handle;
17832305Sstevel 
17842305Sstevel 	if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
1785*8459SJerry.Gilliam@Sun.COM 		cmn_err(CE_WARN,
1786*8459SJerry.Gilliam@Sun.COM 		    "!pci_config_setup() failed on 0x%p", (void *)dip);
1787*8459SJerry.Gilliam@Sun.COM 		return;
17882305Sstevel 	}
17892305Sstevel 
17902305Sstevel 	spcfg = cardbus_pci_cfg;
17912305Sstevel 	cardbus_dump(spcfg, config_handle);
17922305Sstevel 
17932305Sstevel 	pci_config_teardown(&config_handle);
17942305Sstevel }
17952305Sstevel 
17962305Sstevel void
cardbus_dump_socket(dev_info_t * dip)17972305Sstevel cardbus_dump_socket(dev_info_t *dip)
17982305Sstevel {
17992305Sstevel 	ddi_acc_handle_t 	iohandle;
18002305Sstevel 	caddr_t		ioaddr;
18012305Sstevel 	ddi_device_acc_attr_t attr;
18022305Sstevel 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
18032305Sstevel 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
18042305Sstevel 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
18052305Sstevel 	if (ddi_regs_map_setup(dip, 1,
1806*8459SJerry.Gilliam@Sun.COM 	    (caddr_t *)&ioaddr,
1807*8459SJerry.Gilliam@Sun.COM 	    0,
1808*8459SJerry.Gilliam@Sun.COM 	    4096,
1809*8459SJerry.Gilliam@Sun.COM 	    &attr, &iohandle) != DDI_SUCCESS) {
1810*8459SJerry.Gilliam@Sun.COM 		cmn_err(CE_WARN, "Failed to map address for 0x%p", (void *)dip);
1811*8459SJerry.Gilliam@Sun.COM 		return;
18122305Sstevel 	}
18132305Sstevel 
18142305Sstevel 	cmn_err(CE_NOTE, "////////////////////////////////////////");
18152305Sstevel 	cmn_err(CE_NOTE, "SOCKET_EVENT  = [0x%x]",
1816*8459SJerry.Gilliam@Sun.COM 	    ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_STATUS_EVENT)));
18172305Sstevel 	cmn_err(CE_NOTE, "SOCKET_MASK   = [0x%x]",
1818*8459SJerry.Gilliam@Sun.COM 	    ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_STATUS_MASK)));
18192305Sstevel 	cmn_err(CE_NOTE, "SOCKET_STATE  = [0x%x]",
1820*8459SJerry.Gilliam@Sun.COM 	    ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_PRESENT_STATE)));
18212305Sstevel 	cmn_err(CE_NOTE, "////////////////////////////////////////");
18222305Sstevel 
18232305Sstevel 	ddi_regs_map_free(&iohandle);
18242305Sstevel 
18252305Sstevel }
1826