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