cardbus_hp.c revision 5c066ec28ea93f3a7c93082611a61747f255290a
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) * Copyright (c) 2001 Tadpole Technology plc
* All rights reserved.
* From "@(#)pcicfg.c 1.31 99/06/18 SMI"
*/
/*
* Cardbus hotplug module
*/
#include <sys/open.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunndi.h>
#include <sys/note.h>
#include <sys/pci.h>
#include <sys/hotplug/hpcsvc.h>
#include <sys/hotplug/pci/pcicfg.h>
#include <sys/pcic_reg.h>
#include "cardbus.h"
#include "cardbus_hp.h"
#include "cardbus_cfg.h"
/*
* ************************************************************************
* *** Implementation specific data structures/definitions. ***
* ************************************************************************
*/
#ifndef HPC_MAX_OCCUPANTS
#define HPC_MAX_OCCUPANTS 8
typedef struct hpc_occupant_info {
int i;
char *id[HPC_MAX_OCCUPANTS];
} hpc_occupant_info_t;
#endif
#define PCICFG_FLAGS_CONTINUE 0x1
#define PCICFG_OP_ONLINE 0x1
#define PCICFG_OP_OFFLINE 0x0
#define CBHP_DEVCTL_MINOR 255
#define AP_MINOR_NUM_TO_CB_INSTANCE(x) ((x) & 0xFF)
#define AP_MINOR_NUM(x) (((uint_t)(3) << 8) | ((x) & 0xFF))
#define AP_IS_CB_MINOR(x) (((x)>>8) == (3))
extern int cardbus_debug;
extern int number_of_cardbus_cards;
static int cardbus_autocfg_enabled = 1; /* auto config is enabled by default */
/* static functions */
static int cardbus_event_handler(caddr_t slot_arg, uint_t event_mask);
static int cardbus_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl,
int request, caddr_t arg);
static int cardbus_new_slot_state(dev_info_t *dip, hpc_slot_t hdl,
hpc_slot_info_t *slot_info, int slot_state);
static int cardbus_list_occupants(dev_info_t *dip, void *hdl);
static void create_occupant_props(dev_info_t *self, dev_t dev);
static void delete_occupant_props(dev_info_t *dip, dev_t dev);
static int cardbus_configure_ap(cbus_t *cbp);
static int cardbus_unconfigure_ap(cbus_t *cbp);
static int cbus_unconfigure(dev_info_t *devi, int prim_bus);
void cardbus_dump_pci_config(dev_info_t *dip);
void cardbus_dump_pci_node(dev_info_t *dip);
int
cardbus_init_hotplug(cbus_t *cbp)
{
char tbuf[MAXNAMELEN];
hpc_slot_info_t slot_info;
hpc_slot_ops_t *slot_ops;
hpc_slot_t slhandle; /* HPS slot handle */
/*
* register the bus instance with the HPS framework.
*/
if (hpc_nexus_register_bus(cbp->cb_dip,
cardbus_new_slot_state, 0) != 0) {
cmn_err(CE_WARN, "%s%d: failed to register the bus with HPS\n",
ddi_driver_name(cbp->cb_dip), cbp->cb_instance);
return (DDI_FAILURE);
}
(void) sprintf(cbp->ap_id, "slot%d", cbp->cb_instance);
(void) ddi_pathname(cbp->cb_dip, tbuf);
cbp->nexus_path = kmem_alloc(strlen(tbuf) + 1, KM_SLEEP);
(void) strcpy(cbp->nexus_path, tbuf);
cardbus_err(cbp->cb_dip, 8,
"cardbus_init_hotplug: nexus_path set to %s", cbp->nexus_path);
slot_ops = hpc_alloc_slot_ops(KM_SLEEP);
cbp->slot_ops = slot_ops;
/*
* Fill in the slot information structure that
* describes the slot.
*/
slot_info.version = HPC_SLOT_INFO_VERSION;
slot_info.slot_type = HPC_SLOT_TYPE_PCI;
slot_info.slot.pci.device_number = 0;
slot_info.slot.pci.slot_capabilities = 0;
(void) strcpy(slot_info.slot.pci.slot_logical_name, cbp->ap_id);
slot_ops->hpc_version = HPC_SLOT_OPS_VERSION;
slot_ops->hpc_op_connect = NULL;
slot_ops->hpc_op_disconnect = NULL;
slot_ops->hpc_op_insert = NULL;
slot_ops->hpc_op_remove = NULL;
slot_ops->hpc_op_control = cardbus_pci_control;
if (hpc_slot_register(cbp->cb_dip, cbp->nexus_path, &slot_info,
&slhandle, slot_ops, (caddr_t)cbp, 0) != 0) {
/*
* If the slot can not be registered,
* then the slot_ops need to be freed.
*/
cmn_err(CE_WARN,
"cbp%d Unable to Register Slot %s", cbp->cb_instance,
slot_info.slot.pci.slot_logical_name);
(void) hpc_nexus_unregister_bus(cbp->cb_dip);
hpc_free_slot_ops(slot_ops);
cbp->slot_ops = NULL;
return (DDI_FAILURE);
}
ASSERT(slhandle == cbp->slot_handle);
cardbus_err(cbp->cb_dip, 8,
"cardbus_init_hotplug: slot_handle 0x%p", cbp->slot_handle);
return (DDI_SUCCESS);
}
static int
cardbus_event_handler(caddr_t slot_arg, uint_t event_mask)
{
int ap_minor = (int)((uintptr_t)slot_arg);
cbus_t *cbp;
int cb_instance;
int rv = HPC_EVENT_CLAIMED;
cb_instance = AP_MINOR_NUM_TO_CB_INSTANCE(ap_minor);
ASSERT(cb_instance >= 0);
cbp = (cbus_t *)ddi_get_soft_state(cardbus_state, cb_instance);
mutex_enter(&cbp->cb_mutex);
switch (event_mask) {
case HPC_EVENT_SLOT_INSERTION:
/*
* A card is inserted in the slot. Just report this
* event and return.
*/
cardbus_err(cbp->cb_dip, 7,
"cardbus_event_handler(%s%d): card is inserted",
ddi_driver_name(cbp->cb_dip), cbp->cb_instance);
break;
case HPC_EVENT_SLOT_CONFIGURE:
/*
* Configure the occupant that is just inserted in the slot.
* The receptacle may or may not be in the connected state. If
* the receptacle is not connected and the auto configuration
* is enabled on this slot then connect the slot. If auto
* configuration is enabled then configure the card.
*/
if (!(cbp->auto_config)) {
/*
* auto configuration is disabled.
*/
cardbus_err(cbp->cb_dip, 7,
"cardbus_event_handler(%s%d): "
"SLOT_CONFIGURE event occured (slot %s)",
ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
cbp->name);
break;
}
cardbus_err(cbp->cb_dip, 7,
"cardbus_event_handler(%s%d): configure event",
ddi_driver_name(cbp->cb_dip), cbp->cb_instance);
if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
cmn_err(CE_WARN, "!slot%d already configured\n",
cbp->cb_instance);
break;
}
/*
* Auto configuration is enabled. First, make sure the
* receptacle is in the CONNECTED state.
*/
if ((rv = hpc_nexus_connect(cbp->slot_handle,
NULL, 0)) == HPC_SUCCESS) {
cbp->rstate = AP_RSTATE_CONNECTED; /* record rstate */
}
if (cardbus_configure_ap(cbp) == HPC_SUCCESS)
create_occupant_props(cbp->cb_dip, makedevice(
ddi_driver_major((cbp->cb_dip)), ap_minor));
else
rv = HPC_ERR_FAILED;
break;
case HPC_EVENT_SLOT_UNCONFIGURE:
/*
* Unconfigure the occupant in this slot.
*/
if (!(cbp->auto_config)) {
/*
* auto configuration is disabled.
*/
cardbus_err(cbp->cb_dip, 7,
"cardbus_event_handler(%s%d): "
"SLOT_UNCONFIGURE event"
" occured - auto-conf disabled (slot %s)",
ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
cbp->name);
break;
}
cardbus_err(cbp->cb_dip, 7,
"cardbus_event_handler(%s%d): SLOT_UNCONFIGURE event"
" occured (slot %s)",
ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
cbp->name);
if (cardbus_unconfigure_ap(cbp) != HPC_SUCCESS)
rv = HPC_ERR_FAILED;
DEVI(cbp->cb_dip)->devi_ops->devo_bus_ops = cbp->orig_bopsp;
--number_of_cardbus_cards;
break;
case HPC_EVENT_SLOT_REMOVAL:
/*
* Card is removed from the slot. The card must have been
* unconfigured before this event.
*/
if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
cardbus_err(cbp->cb_dip, 1,
"cardbus_event_handler(%s%d): "
"card is removed from"
" the slot %s before doing unconfigure!!",
ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
cbp->name);
break;
}
cardbus_err(cbp->cb_dip, 7,
"cardbus_event_handler(%s%d): "
"card is removed from the slot %s",
ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
cbp->name);
break;
case HPC_EVENT_SLOT_POWER_ON:
/*
* Slot is connected to the bus. i.e the card is powered
* on.
*/
cardbus_err(cbp->cb_dip, 7,
"cardbus_event_handler(%s%d): "
"card is powered on in the slot %s",
ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
cbp->name);
cbp->rstate = AP_RSTATE_CONNECTED; /* record rstate */
break;
case HPC_EVENT_SLOT_POWER_OFF:
/*
* Slot is disconnected from the bus. i.e the card is powered
* off.
*/
cardbus_err(cbp->cb_dip, 7,
"cardbus_event_handler(%s%d): "
"card is powered off in the slot %s",
ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
cbp->name);
cbp->rstate = AP_RSTATE_DISCONNECTED; /* record rstate */
break;
default:
cardbus_err(cbp->cb_dip, 4,
"cardbus_event_handler(%s%d): "
"unknown event %x for this slot %s",
ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
event_mask, cbp->name);
break;
}
mutex_exit(&cbp->cb_mutex);
return (rv);
}
static int
cardbus_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl, int request,
caddr_t arg)
{
cbus_t *cbp;
int rval = HPC_SUCCESS;
hpc_led_info_t *hpc_led_info;
_NOTE(ARGUNUSED(slot_hdl))
cbp = (cbus_t *)ops_arg;
ASSERT(mutex_owned(&cbp->cb_mutex));
switch (request) {
case HPC_CTRL_GET_SLOT_STATE: {
hpc_slot_state_t *hpc_slot_state;
hpc_slot_state = (hpc_slot_state_t *)arg;
cardbus_err(cbp->cb_dip, 7,
"cardbus_pci_control() - "
"HPC_CTRL_GET_SLOT_STATE hpc_slot_state=0x%p",
(void *) hpc_slot_state);
if (cbp->card_present)
*hpc_slot_state = HPC_SLOT_CONNECTED;
else
*hpc_slot_state = HPC_SLOT_EMPTY;
break;
}
case HPC_CTRL_GET_BOARD_TYPE: {
hpc_board_type_t *hpc_board_type;
hpc_board_type = (hpc_board_type_t *)arg;
cardbus_err(cbp->cb_dip, 7,
"cardbus_pci_control() - HPC_CTRL_GET_BOARD_TYPE");
/*
* The HPC driver does not know what board type
* is plugged in.
*/
*hpc_board_type = HPC_BOARD_PCI_HOTPLUG;
break;
}
case HPC_CTRL_DEV_CONFIGURED:
case HPC_CTRL_DEV_UNCONFIGURED:
cardbus_err(cbp->cb_dip, 5,
"cardbus_pci_control() - HPC_CTRL_DEV_%sCONFIGURED",
request == HPC_CTRL_DEV_UNCONFIGURED ? "UN" : "");
break;
case HPC_CTRL_GET_LED_STATE:
hpc_led_info = (hpc_led_info_t *)arg;
cardbus_err(cbp->cb_dip, 5,
"cardbus_pci_control() - HPC_CTRL_GET_LED_STATE "
"led %d is %d",
hpc_led_info->led, cbp->leds[hpc_led_info->led]);
hpc_led_info->state = cbp->leds[hpc_led_info->led];
break;
case HPC_CTRL_SET_LED_STATE:
hpc_led_info = (hpc_led_info_t *)arg;
cardbus_err(cbp->cb_dip, 4,
"cardbus_pci_control() - HPC_CTRL_SET_LED_STATE "
"led %d to %d",
hpc_led_info->led, hpc_led_info->state);
cbp->leds[hpc_led_info->led] = hpc_led_info->state;
break;
case HPC_CTRL_ENABLE_AUTOCFG:
cardbus_err(cbp->cb_dip, 5,
"cardbus_pci_control() - HPC_CTRL_ENABLE_AUTOCFG");
/*
* Cardbus ALWAYS does auto config, from the slots point of
* view this is turning on the card and making sure it's ok.
* This is all done by the bridge driver before we see any
* indication.
*/
break;
case HPC_CTRL_DISABLE_AUTOCFG:
cardbus_err(cbp->cb_dip, 5,
"cardbus_pci_control() - HPC_CTRL_DISABLE_AUTOCFG");
break;
case HPC_CTRL_DISABLE_ENUM:
case HPC_CTRL_ENABLE_ENUM:
default:
rval = HPC_ERR_NOTSUPPORTED;
break;
}
return (rval);
}
/*
* cardbus_new_slot_state()
*
* This function is called by the HPS when it finds a hot plug
* slot is added or being removed from the hot plug framework.
* It returns 0 for success and HPC_ERR_FAILED for errors.
*/
static int
cardbus_new_slot_state(dev_info_t *dip, hpc_slot_t hdl,
hpc_slot_info_t *slot_info, int slot_state)
{
int cb_instance;
cbus_t *cbp;
int ap_minor;
int rv = 0;
cardbus_err(dip, 8,
"cardbus_new_slot_state: slot_handle 0x%p", hdl);
/*
* get the soft state structure for the bus instance.
*/
cb_instance = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "cbus-instance", -1);
ASSERT(cb_instance >= 0);
cbp = (cbus_t *)ddi_get_soft_state(cardbus_state, cb_instance);
mutex_enter(&cbp->cb_mutex);
switch (slot_state) {
case HPC_SLOT_ONLINE:
/*
* Make sure the slot is not already ONLINE
*/
if (cbp->slot_handle != NULL) {
cardbus_err(dip, 4,
"cardbus_new_slot_state: "
"cardbus already ONLINE!!");
rv = HPC_ERR_FAILED;
break;
}
/*
* Add the hot plug slot to the bus.
*/
/* create the AP minor node */
ap_minor = AP_MINOR_NUM(cb_instance);
if (ddi_create_minor_node(dip, slot_info->pci_slot_name,
S_IFCHR, ap_minor,
DDI_NT_PCI_ATTACHMENT_POINT,
0) == DDI_FAILURE) {
cardbus_err(dip, 4,
"cardbus_new_slot_state: "
"ddi_create_minor_node failed");
rv = HPC_ERR_FAILED;
break;
}
/* save the slot handle */
cbp->slot_handle = hdl;
/* setup event handler for all hardware events on the slot */
if (hpc_install_event_handler(hdl, -1, cardbus_event_handler,
(caddr_t)((long)ap_minor)) != 0) {
cardbus_err(dip, 4,
"cardbus_new_slot_state: "
"install event handler failed");
rv = HPC_ERR_FAILED;
break;
}
cbp->event_mask = (uint32_t)0xFFFFFFFF;
create_occupant_props(dip,
makedevice(ddi_name_to_major(ddi_get_name(dip)),
ap_minor));
/* set default auto configuration enabled flag for this slot */
cbp->auto_config = cardbus_autocfg_enabled;
/* copy the slot information */
cbp->name = (char *)kmem_alloc(strlen(slot_info->pci_slot_name)
+ 1, KM_SLEEP);
(void) strcpy(cbp->name, slot_info->pci_slot_name);
cardbus_err(cbp->cb_dip, 10,
"cardbus_new_slot_state: cbp->name set to %s", cbp->name);
cardbus_err(dip, 4,
"Cardbus slot \"%s\" ONLINE\n", slot_info->pci_slot_name);
cbp->ostate = AP_OSTATE_UNCONFIGURED;
cbp->rstate = AP_RSTATE_EMPTY;
break;
case HPC_SLOT_OFFLINE:
/*
* A hot plug slot is being removed from the bus.
* Make sure there is no occupant configured on the
* slot before removing the AP minor node.
*/
if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
cmn_err(CE_WARN,
"cardbus: Card is still in configured state");
rv = HPC_ERR_FAILED;
break;
}
/*
* If the AP device is in open state then return
* error.
*/
if (cbp->soft_state != PCIHP_SOFT_STATE_CLOSED) {
rv = HPC_ERR_FAILED;
break;
}
/* remove the minor node */
ddi_remove_minor_node(dip, cbp->name);
/* free up the memory for the name string */
kmem_free(cbp->name, strlen(cbp->name) + 1);
/* update the slot info data */
cbp->name = NULL;
cbp->slot_handle = NULL;
cardbus_err(dip, 6,
"cardbus_new_slot_state: Cardbus slot OFFLINE");
break;
default:
cmn_err(CE_WARN,
"cardbus_new_slot_state: unknown slot_state %d\n",
slot_state);
rv = HPC_ERR_FAILED;
}
mutex_exit(&cbp->cb_mutex);
return (rv);
}
static int
cardbus_list_occupants(dev_info_t *dip, void *hdl)
{
hpc_occupant_info_t *occupant = (hpc_occupant_info_t *)hdl;
char pn[MAXPATHLEN];
/*
* Ignore the attachment point and pcs.
*/
if (strcmp(ddi_binding_name(dip), "pcs") == 0) {
return (DDI_WALK_CONTINUE);
}
(void) ddi_pathname(dip, pn);
occupant->id[occupant->i] = kmem_alloc(strlen(pn) + 1, KM_SLEEP);
(void) strcpy(occupant->id[occupant->i], pn);
occupant->i++;
/*
* continue the walk to the next sibling to look for a match
* or to find other nodes if this card is a multi-function card.
*/
return (DDI_WALK_PRUNECHILD);
}
static void
create_occupant_props(dev_info_t *self, dev_t dev)
{
hpc_occupant_info_t occupant;
int i;
int circular;
occupant.i = 0;
ndi_devi_enter(self, &circular);
ddi_walk_devs(ddi_get_child(self), cardbus_list_occupants,
(void *)&occupant);
ndi_devi_exit(self, circular);
if (occupant.i == 0) {
char *c[] = { "" };
cardbus_err(self, 1, "create_occupant_props: no occupant\n");
(void) ddi_prop_update_string_array(dev, self, "pci-occupant",
c, 1);
} else {
cardbus_err(self, 1,
"create_occupant_props: %d occupant\n", occupant.i);
(void) ddi_prop_update_string_array(dev, self, "pci-occupant",
occupant.id, occupant.i);
}
for (i = 0; i < occupant.i; i++) {
kmem_free(occupant.id[i], strlen(occupant.id[i]) + 1);
}
}
static void
delete_occupant_props(dev_info_t *dip, dev_t dev)
{
if (ddi_prop_remove(dev, dip, "pci-occupant")
!= DDI_PROP_SUCCESS)
return; /* add error handling */
}
/*
* **************************************
* CONFIGURE the occupant in the slot.
* **************************************
*/
static int
cardbus_configure_ap(cbus_t *cbp)
{
dev_info_t *self = cbp->cb_dip;
int rv = HPC_SUCCESS;
hpc_slot_state_t rstate;
struct cardbus_config_ctrl ctrl;
int circular_count;
/*
* check for valid request:
* 1. It is a hotplug slot.
* 2. The receptacle is in the CONNECTED state.
*/
if (cbp->slot_handle == NULL || cbp->disabled) {
return (ENXIO);
}
/*
* If the occupant is already in (partially) configured
* state then call the ndi_devi_online() on the device
* subtree(s) for this attachment point.
*/
if (cbp->ostate == AP_OSTATE_CONFIGURED) {
ctrl.flags = PCICFG_FLAGS_CONTINUE;
ctrl.busno = cardbus_primary_busno(self);
ctrl.rv = NDI_SUCCESS;
ctrl.dip = NULL;
ctrl.op = PCICFG_OP_ONLINE;
ndi_devi_enter(self, &circular_count);
ddi_walk_devs(ddi_get_child(self),
cbus_configure, (void *)&ctrl);
ndi_devi_exit(self, circular_count);
if (cardbus_debug) {
cardbus_dump_pci_config(self);
cardbus_dump_pci_node(self);
}
if (ctrl.rv != NDI_SUCCESS) {
/*
* one or more of the devices are not
* onlined.
*/
cmn_err(CE_WARN, "cardbus(%s%d): failed to attach "
"one or more drivers for the card in the slot %s",
ddi_driver_name(self), cbp->cb_instance,
cbp->name);
}
/* tell HPC driver that the occupant is configured */
(void) hpc_nexus_control(cbp->slot_handle,
HPC_CTRL_DEV_CONFIGURED, NULL);
return (rv);
}
/*
* Occupant is in the UNCONFIGURED state.
*/
/* Check if the receptacle is in the CONNECTED state. */
if (hpc_nexus_control(cbp->slot_handle,
HPC_CTRL_GET_SLOT_STATE, (caddr_t)&rstate) != 0) {
return (ENXIO);
}
if (rstate != HPC_SLOT_CONNECTED) {
/* error. either the slot is empty or connect failed */
return (ENXIO);
}
cbp->rstate = AP_RSTATE_CONNECTED; /* record rstate */
/*
* Call the configurator to configure the card.
*/
if (cardbus_configure(cbp) != PCICFG_SUCCESS) {
return (EIO);
}
/* record the occupant state as CONFIGURED */
cbp->ostate = AP_OSTATE_CONFIGURED;
cbp->condition = AP_COND_OK;
/* now, online all the devices in the AP */
ctrl.flags = PCICFG_FLAGS_CONTINUE;
ctrl.busno = cardbus_primary_busno(self);
ctrl.rv = NDI_SUCCESS;
ctrl.dip = NULL;
ctrl.op = PCICFG_OP_ONLINE;
ndi_devi_enter(self, &circular_count);
ddi_walk_devs(ddi_get_child(self), cbus_configure, (void *)&ctrl);
ndi_devi_exit(self, circular_count);
if (cardbus_debug) {
cardbus_dump_pci_config(self);
cardbus_dump_pci_node(self);
}
if (ctrl.rv != NDI_SUCCESS) {
/*
* one or more of the devices are not
* ONLINE'd.
*/
cmn_err(CE_WARN, "cbhp (%s%d): failed to attach one or"
" more drivers for the card in the slot %s",
ddi_driver_name(cbp->cb_dip),
cbp->cb_instance, cbp->name);
/* rv = EFAULT; */
}
/* tell HPC driver that the occupant is configured */
(void) hpc_nexus_control(cbp->slot_handle,
HPC_CTRL_DEV_CONFIGURED, NULL);
return (rv);
}
/*
* **************************************
* UNCONFIGURE the occupant in the slot.
* **************************************
*/
static int
cardbus_unconfigure_ap(cbus_t *cbp)
{
dev_info_t *self = cbp->cb_dip;
int rv = HPC_SUCCESS, nrv;
/*
* check for valid request:
* 1. It is a hotplug slot.
* 2. The occupant is in the CONFIGURED state.
*/
if (cbp->slot_handle == NULL || cbp->disabled) {
return (ENXIO);
}
/*
* If the occupant is in the CONFIGURED state then
* call the configurator to unconfigure the slot.
*/
if (cbp->ostate == AP_OSTATE_CONFIGURED) {
/*
* Detach all the drivers for the devices in the
* slot.
*/
nrv = cardbus_unconfigure_node(self,
cardbus_primary_busno(self),
B_TRUE);
if (nrv != NDI_SUCCESS) {
/*
* Failed to detach one or more drivers.
* Restore the status for the drivers
* which are offlined during this step.
*/
cmn_err(CE_WARN,
"cbhp (%s%d): Failed to offline all devices"
" (slot %s)", ddi_driver_name(cbp->cb_dip),
cbp->cb_instance, cbp->name);
rv = EBUSY;
} else {
if (cardbus_unconfigure(cbp) == PCICFG_SUCCESS) {
/*
* Now that resources are freed,
* clear EXT and Turn LED ON.
*/
cbp->ostate = AP_OSTATE_UNCONFIGURED;
cbp->condition = AP_COND_UNKNOWN;
/*
* send the notification of state change
* to the HPC driver.
*/
(void) hpc_nexus_control(cbp->slot_handle,
HPC_CTRL_DEV_UNCONFIGURED, NULL);
} else {
rv = EIO;
}
}
}
return (rv);
}
int
cbus_configure(dev_info_t *dip, void *hdl)
{
pci_regspec_t *pci_rp;
int length, rc;
struct cardbus_config_ctrl *ctrl = (struct cardbus_config_ctrl *)hdl;
uint8_t bus, device, function;
/*
* Ignore the attachment point and pcs.
*/
if (strcmp(ddi_binding_name(dip), "hp_attachment") == 0 ||
strcmp(ddi_binding_name(dip), "pcs") == 0) {
cardbus_err(dip, 8, "cbus_configure: Ignoring\n");
return (DDI_WALK_CONTINUE);
}
cardbus_err(dip, 6, "cbus_configure\n");
ASSERT(ctrl->op == PCICFG_OP_ONLINE);
/*
* Get the PCI device number information from the devinfo
* node. Since the node may not have the address field
* setup (this is done in the DDI_INITCHILD of the parent)
* we look up the 'reg' property to decode that information.
*/
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
(uint_t *)&length) != DDI_PROP_SUCCESS) {
/* Porbably not a real device, like PCS for example */
if (ddi_get_child(dip) == NULL)
return (DDI_WALK_PRUNECHILD);
cardbus_err(dip, 1, "cubs_configure: Don't configure device\n");
ctrl->rv = DDI_FAILURE;
ctrl->dip = dip;
return (DDI_WALK_TERMINATE);
}
if (pci_rp->pci_phys_hi == 0)
return (DDI_WALK_CONTINUE);
/* get the pci device id information */
bus = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
device = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
function = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
/*
* free the memory allocated by ddi_prop_lookup_int_array
*/
ddi_prop_free(pci_rp);
if (bus <= ctrl->busno)
return (DDI_WALK_CONTINUE);
cardbus_err(dip, 8,
"cbus_configure on-line device at: "
"[0x%x][0x%x][0x%x]\n", bus, device, function);
rc = ndi_devi_online(dip, NDI_ONLINE_ATTACH|NDI_CONFIG);
cardbus_err(dip, 7,
"cbus_configure %s\n",
rc == NDI_SUCCESS ? "Success": "Failure");
if (rc != NDI_SUCCESS)
return (DDI_WALK_PRUNECHILD);
return (DDI_WALK_CONTINUE);
}
int
cardbus_unconfigure_node(dev_info_t *dip, int prim_bus, boolean_t top_bridge)
{
dev_info_t *child, *next;
cardbus_err(dip, 6, "cardbus_unconfigure_node\n");
/*
* Ignore pcs.
*/
if (strcmp(ddi_binding_name(dip), "pcs") == 0) {
cardbus_err(dip, 8, "cardbus_unconfigure_node: Ignoring\n");
return (NDI_SUCCESS);
}
/*
* bottom up off-line
*/
for (child = ddi_get_child(dip); child; child = next) {
int rc;
next = ddi_get_next_sibling(child);
rc = cardbus_unconfigure_node(child, prim_bus, B_FALSE);
if (rc != NDI_SUCCESS)
return (rc);
}
/*
* Don't unconfigure the bridge itself.
*/
if (top_bridge)
return (NDI_SUCCESS);
if (cbus_unconfigure(dip, prim_bus) != NDI_SUCCESS) {
cardbus_err(dip, 1,
"cardbus_unconfigure_node: cardbus_unconfigure failed\n");
return (NDI_FAILURE);
}
return (NDI_SUCCESS);
}
/*
* This will turn resources allocated by cbus_configure()
* and remove the device tree from the attachment point
* and below. The routine assumes the devices have their
* drivers detached.
*/
static int
cbus_unconfigure(dev_info_t *devi, int prim_bus)
{
pci_regspec_t *pci_rp;
uint_t bus, device, func, length;
int ndi_flags = NDI_UNCONFIG;
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, devi,
DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
&length) != DDI_PROP_SUCCESS) {
/*
* This cannot be one of our devices. If it's something like a
* SCSI device then the attempt to offline the HBA
* (which probably is one of our devices)
* will also do bottom up offlining. That
* will fail if this device is busy. So always
* return success here
* so that the walk will continue.
*/
return (NDI_SUCCESS);
}
if (pci_rp->pci_phys_hi == 0)
return (NDI_FAILURE);
bus = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
if (bus <= prim_bus)
return (NDI_SUCCESS);
device = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
ddi_prop_free(pci_rp);
cardbus_err(devi, 8,
"cbus_unconfigure: "
"offline bus [0x%x] device [0x%x] function [%x]\n",
bus, device, func);
if (ndi_devi_offline(devi, ndi_flags) != NDI_SUCCESS) {
cardbus_err(devi, 1,
"Device [0x%x] function [%x] is busy\n", device, func);
return (NDI_FAILURE);
}
cardbus_err(devi, 9,
"Tearing down device [0x%x] function [0x%x]\n", device, func);
if (cardbus_teardown_device(devi) != PCICFG_SUCCESS) {
cardbus_err(devi, 1,
"Failed to tear down "
"device [0x%x] function [0x%x]\n", device, func);
return (NDI_FAILURE);
}
return (NDI_SUCCESS);
}
boolean_t
cardbus_is_cb_minor(dev_t dev)
{
return (AP_IS_CB_MINOR(getminor(dev)) ? B_TRUE : B_FALSE);
}
int
cardbus_open(dev_t *devp, int flags, int otyp, cred_t *credp)
{
cbus_t *cbp;
int minor;
_NOTE(ARGUNUSED(credp))
minor = getminor(*devp);
/*
* Make sure the open is for the right file type.
*/
if (otyp != OTYP_CHR)
return (EINVAL);
/*
* Get the soft state structure for the 'devctl' device.
*/
cbp = (cbus_t *)ddi_get_soft_state(cardbus_state,
AP_MINOR_NUM_TO_CB_INSTANCE(minor));
if (cbp == NULL)
return (ENXIO);
mutex_enter(&cbp->cb_mutex);
/*
* Handle the open by tracking the device state.
*
* Note: Needs review w.r.t exclusive access to AP or the bus.
* Currently in the pci plug-in we don't use EXCL open at all
* so the code below implements EXCL access on the bus.
*/
/* enforce exclusive access to the bus */
if ((cbp->soft_state == PCIHP_SOFT_STATE_OPEN_EXCL) ||
((flags & FEXCL) &&
(cbp->soft_state != PCIHP_SOFT_STATE_CLOSED))) {
mutex_exit(&cbp->cb_mutex);
return (EBUSY);
}
if (flags & FEXCL)
cbp->soft_state = PCIHP_SOFT_STATE_OPEN_EXCL;
else
cbp->soft_state = PCIHP_SOFT_STATE_OPEN;
mutex_exit(&cbp->cb_mutex);
return (0);
}
/*ARGSUSED*/
int
cardbus_close(dev_t dev, int flags, int otyp, cred_t *credp)
{
cbus_t *cbp;
int minor;
_NOTE(ARGUNUSED(credp))
minor = getminor(dev);
if (otyp != OTYP_CHR)
return (EINVAL);
cbp = (cbus_t *)ddi_get_soft_state(cardbus_state,
AP_MINOR_NUM_TO_CB_INSTANCE(minor));
if (cbp == NULL)
return (ENXIO);
mutex_enter(&cbp->cb_mutex);
cbp->soft_state = PCIHP_SOFT_STATE_CLOSED;
mutex_exit(&cbp->cb_mutex);
return (0);
}
/*
* cardbus_ioctl: devctl hotplug controls
*/
/*ARGSUSED*/
int
cardbus_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
int *rvalp)
{
cbus_t *cbp;
dev_info_t *self;
dev_info_t *child_dip = NULL;
struct devctl_iocdata *dcp;
uint_t bus_state;
int rv = 0;
int nrv = 0;
int ap_minor;
hpc_slot_state_t rstate;
devctl_ap_state_t ap_state;
struct hpc_control_data hpc_ctrldata;
struct hpc_led_info led_info;
_NOTE(ARGUNUSED(credp))
ap_minor = getminor(dev);
cbp = (cbus_t *)ddi_get_soft_state(cardbus_state,
AP_MINOR_NUM_TO_CB_INSTANCE(ap_minor));
if (cbp == NULL)
return (ENXIO);
self = cbp->cb_dip;
/*
* read devctl ioctl data
*/
if ((cmd != DEVCTL_AP_CONTROL) && ndi_dc_allochdl((void *)arg,
&dcp) != NDI_SUCCESS)
return (EFAULT);
#ifdef CARDBUS_DEBUG
{
char *cmd_name;
switch (cmd) {
case DEVCTL_DEVICE_GETSTATE: cmd_name = "DEVCTL_DEVICE_GETSTATE"; break;
case DEVCTL_DEVICE_ONLINE: cmd_name = "DEVCTL_DEVICE_ONLINE"; break;
case DEVCTL_DEVICE_OFFLINE: cmd_name = "DEVCTL_DEVICE_OFFLINE"; break;
case DEVCTL_DEVICE_RESET: cmd_name = "DEVCTL_DEVICE_RESET"; break;
case DEVCTL_BUS_QUIESCE: cmd_name = "DEVCTL_BUS_QUIESCE"; break;
case DEVCTL_BUS_UNQUIESCE: cmd_name = "DEVCTL_BUS_UNQUIESCE"; break;
case DEVCTL_BUS_RESET: cmd_name = "DEVCTL_BUS_RESET"; break;
case DEVCTL_BUS_RESETALL: cmd_name = "DEVCTL_BUS_RESETALL"; break;
case DEVCTL_BUS_GETSTATE: cmd_name = "DEVCTL_BUS_GETSTATE"; break;
case DEVCTL_AP_CONNECT: cmd_name = "DEVCTL_AP_CONNECT"; break;
case DEVCTL_AP_DISCONNECT: cmd_name = "DEVCTL_AP_DISCONNECT"; break;
case DEVCTL_AP_INSERT: cmd_name = "DEVCTL_AP_INSERT"; break;
case DEVCTL_AP_REMOVE: cmd_name = "DEVCTL_AP_REMOVE"; break;
case DEVCTL_AP_CONFIGURE: cmd_name = "DEVCTL_AP_CONFIGURE"; break;
case DEVCTL_AP_UNCONFIGURE: cmd_name = "DEVCTL_AP_UNCONFIGURE"; break;
case DEVCTL_AP_GETSTATE: cmd_name = "DEVCTL_AP_GETSTATE"; break;
case DEVCTL_AP_CONTROL: cmd_name = "DEVCTL_AP_CONTROL"; break;
default: cmd_name = "Unknown"; break;
}
cardbus_err(cbp->cb_dip, 7,
"cardbus_ioctl: cmd = 0x%x, \"%s\"", cmd, cmd_name);
}
#endif
switch (cmd) {
case DEVCTL_DEVICE_GETSTATE:
case DEVCTL_DEVICE_ONLINE:
case DEVCTL_DEVICE_OFFLINE:
case DEVCTL_BUS_GETSTATE:
rv = ndi_devctl_ioctl(self, cmd, arg, mode, 0);
ndi_dc_freehdl(dcp);
return (rv);
default:
break;
}
switch (cmd) {
case DEVCTL_DEVICE_RESET:
rv = ENOTSUP;
break;
case DEVCTL_BUS_QUIESCE:
if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS)
if (bus_state == BUS_QUIESCED)
break;
(void) ndi_set_bus_state(self, BUS_QUIESCED);
break;
case DEVCTL_BUS_UNQUIESCE:
if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS)
if (bus_state == BUS_ACTIVE)
break;
(void) ndi_set_bus_state(self, BUS_ACTIVE);
break;
case DEVCTL_BUS_RESET:
rv = ENOTSUP;
break;
case DEVCTL_BUS_RESETALL:
rv = ENOTSUP;
break;
case DEVCTL_AP_CONNECT:
case DEVCTL_AP_DISCONNECT:
/*
* CONNECT(DISCONNECT) the hot plug slot to(from) the bus.
*/
case DEVCTL_AP_INSERT:
case DEVCTL_AP_REMOVE:
/*
* Prepare the slot for INSERT/REMOVE operation.
*/
/*
* check for valid request:
* 1. It is a hotplug slot.
* 2. The slot has no occupant that is in
* the 'configured' state.
*
* The lower 8 bits of the minor number is the PCI
* device number for the slot.
*/
if ((cbp->slot_handle == NULL) || cbp->disabled) {
rv = ENXIO;
break;
}
/* the slot occupant must be in the UNCONFIGURED state */
if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
rv = EINVAL;
break;
}
/*
* Call the HPC driver to perform the operation on the slot.
*/
mutex_enter(&cbp->cb_mutex);
switch (cmd) {
case DEVCTL_AP_INSERT:
rv = hpc_nexus_insert(cbp->slot_handle, NULL, 0);
break;
case DEVCTL_AP_REMOVE:
rv = hpc_nexus_remove(cbp->slot_handle, NULL, 0);
break;
case DEVCTL_AP_CONNECT:
if ((rv = hpc_nexus_connect(cbp->slot_handle,
NULL, 0)) == 0)
cbp->rstate = AP_RSTATE_CONNECTED;
break;
case DEVCTL_AP_DISCONNECT:
if ((rv = hpc_nexus_disconnect(cbp->slot_handle,
NULL, 0)) == 0)
cbp->rstate = AP_RSTATE_DISCONNECTED;
break;
}
mutex_exit(&cbp->cb_mutex);
switch (rv) {
case HPC_ERR_INVALID:
rv = ENXIO;
break;
case HPC_ERR_NOTSUPPORTED:
rv = ENOTSUP;
break;
case HPC_ERR_FAILED:
rv = EIO;
break;
}
break;
case DEVCTL_AP_CONFIGURE:
/*
* **************************************
* CONFIGURE the occupant in the slot.
* **************************************
*/
mutex_enter(&cbp->cb_mutex);
if ((nrv = cardbus_configure_ap(cbp)) == HPC_SUCCESS) {
create_occupant_props(cbp->cb_dip, dev);
} else
rv = nrv;
mutex_exit(&cbp->cb_mutex);
break;
case DEVCTL_AP_UNCONFIGURE:
/*
* **************************************
* UNCONFIGURE the occupant in the slot.
* **************************************
*/
mutex_enter(&cbp->cb_mutex);
if ((nrv = cardbus_unconfigure_ap(cbp)) == HPC_SUCCESS) {
delete_occupant_props(cbp->cb_dip, dev);
} else
rv = nrv;
mutex_exit(&cbp->cb_mutex);
break;
case DEVCTL_AP_GETSTATE:
{
int mutex_held;
/*
* return the state of Attachment Point.
*
* If the occupant is in UNCONFIGURED state then
* we should get the receptacle state from the
* HPC driver because the receptacle state
* maintained in the nexus may not be accurate.
*/
/*
* check for valid request:
* 1. It is a hotplug slot.
*/
if (cbp->slot_handle == NULL) {
rv = ENXIO;
break;
}
/* try to acquire the slot mutex */
mutex_held = mutex_tryenter(&cbp->cb_mutex);
if (cbp->ostate == AP_OSTATE_UNCONFIGURED) {
if (hpc_nexus_control(cbp->slot_handle,
HPC_CTRL_GET_SLOT_STATE,
(caddr_t)&rstate) != 0) {
rv = ENXIO;
if (mutex_held)
mutex_exit(&cbp->cb_mutex);
break;
}
cbp->rstate = (ap_rstate_t)rstate;
}
ap_state.ap_rstate = cbp->rstate;
ap_state.ap_ostate = cbp->ostate;
ap_state.ap_condition = cbp->condition;
ap_state.ap_last_change = 0;
ap_state.ap_error_code = 0;
if (mutex_held)
ap_state.ap_in_transition = 0; /* AP is not busy */
else
ap_state.ap_in_transition = 1; /* AP is busy */
if (mutex_held)
mutex_exit(&cbp->cb_mutex);
/* copy the return-AP-state information to the user space */
if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS)
rv = ENXIO;
break;
}
case DEVCTL_AP_CONTROL:
/*
* HPC control functions:
* HPC_CTRL_ENABLE_SLOT/HPC_CTRL_DISABLE_SLOT
* Changes the state of the slot and preserves
* the state across the reboot.
* HPC_CTRL_ENABLE_AUTOCFG/HPC_CTRL_DISABLE_AUTOCFG
* Enables or disables the auto configuration
* of hot plugged occupant if the hardware
* supports notification of the hot plug
* events.
* HPC_CTRL_GET_LED_STATE/HPC_CTRL_SET_LED_STATE
* Controls the state of an LED.
* HPC_CTRL_GET_SLOT_INFO
* Get slot information data structure
* (hpc_slot_info_t).
* HPC_CTRL_GET_BOARD_TYPE
* Get board type information (hpc_board_type_t).
* HPC_CTRL_GET_CARD_INFO
* Get card information (hpc_card_info_t).
*
* These control functions are used by the cfgadm plug-in
* to implement "-x" and "-v" options.
*/
/* copy user ioctl data first */
#ifdef _MULTI_DATAMODEL
if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
struct hpc_control32_data hpc_ctrldata32;
if (copyin((void *)arg, (void *)&hpc_ctrldata32,
sizeof (struct hpc_control32_data)) != 0) {
rv = EFAULT;
break;
}
hpc_ctrldata.cmd = hpc_ctrldata32.cmd;
hpc_ctrldata.data =
(void *)(intptr_t)hpc_ctrldata32.data;
}
#else
if (copyin((void *)arg, (void *)&hpc_ctrldata,
sizeof (struct hpc_control_data)) != 0) {
rv = EFAULT;
break;
}
#endif
#ifdef CARDBUS_DEBUG
{
char *hpc_name;
switch (hpc_ctrldata.cmd) {
case HPC_CTRL_GET_LED_STATE:
hpc_name = "HPC_CTRL_GET_LED_STATE";
break;
case HPC_CTRL_SET_LED_STATE:
hpc_name = "HPC_CTRL_SET_LED_STATE";
break;
case HPC_CTRL_ENABLE_SLOT:
hpc_name = "HPC_CTRL_ENABLE_SLOT";
break;
case HPC_CTRL_DISABLE_SLOT:
hpc_name = "HPC_CTRL_DISABLE_SLOT";
break;
case HPC_CTRL_ENABLE_AUTOCFG:
hpc_name = "HPC_CTRL_ENABLE_AUTOCFG";
break;
case HPC_CTRL_DISABLE_AUTOCFG:
hpc_name = "HPC_CTRL_DISABLE_AUTOCFG";
break;
case HPC_CTRL_GET_BOARD_TYPE:
hpc_name = "HPC_CTRL_GET_BOARD_TYPE";
break;
case HPC_CTRL_GET_SLOT_INFO:
hpc_name = "HPC_CTRL_GET_SLOT_INFO";
break;
case HPC_CTRL_GET_CARD_INFO:
hpc_name = "HPC_CTRL_GET_CARD_INFO";
break;
default: hpc_name = "Unknown"; break;
}
cardbus_err(cbp->cb_dip, 7,
"cardbus_ioctl: HP Control cmd 0x%x - \"%s\"",
hpc_ctrldata.cmd, hpc_name);
}
#endif
/*
* check for valid request:
* 1. It is a hotplug slot.
*/
if (cbp->slot_handle == NULL) {
rv = ENXIO;
break;
}
mutex_enter(&cbp->cb_mutex);
switch (hpc_ctrldata.cmd) {
case HPC_CTRL_GET_LED_STATE:
/* copy the led info from the user space */
if (copyin(hpc_ctrldata.data, (void *)&led_info,
sizeof (hpc_led_info_t)) != 0) {
rv = ENXIO;
break;
}
/* get the state of LED information */
if (hpc_nexus_control(cbp->slot_handle,
HPC_CTRL_GET_LED_STATE,
(caddr_t)&led_info) != 0) {
rv = ENXIO;
break;
}
/* copy the led info to the user space */
if (copyout((void *)&led_info, hpc_ctrldata.data,
sizeof (hpc_led_info_t)) != 0) {
rv = ENXIO;
break;
}
break;
case HPC_CTRL_SET_LED_STATE:
/* copy the led info from the user space */
if (copyin(hpc_ctrldata.data, (void *)&led_info,
sizeof (hpc_led_info_t)) != 0) {
rv = ENXIO;
break;
}
/* set the state of an LED */
if (hpc_nexus_control(cbp->slot_handle,
HPC_CTRL_SET_LED_STATE,
(caddr_t)&led_info) != 0) {
rv = ENXIO;
break;
}
break;
case HPC_CTRL_ENABLE_SLOT:
/*
* Enable the slot for hotplug operations.
*/
cbp->disabled = B_FALSE;
/* tell the HPC driver also */
(void) hpc_nexus_control(cbp->slot_handle,
HPC_CTRL_ENABLE_SLOT, NULL);
break;
case HPC_CTRL_DISABLE_SLOT:
/*
* Disable the slot for hotplug operations.
*/
cbp->disabled = B_TRUE;
/* tell the HPC driver also */
(void) hpc_nexus_control(cbp->slot_handle,
HPC_CTRL_DISABLE_SLOT, NULL);
break;
case HPC_CTRL_ENABLE_AUTOCFG:
/*
* Enable auto configuration on this slot.
*/
cbp->auto_config = B_TRUE;
/* tell the HPC driver also */
(void) hpc_nexus_control(cbp->slot_handle,
HPC_CTRL_ENABLE_AUTOCFG, NULL);
break;
case HPC_CTRL_DISABLE_AUTOCFG:
/*
* Disable auto configuration on this slot.
*/
cbp->auto_config = B_FALSE;
/* tell the HPC driver also */
(void) hpc_nexus_control(cbp->slot_handle,
HPC_CTRL_DISABLE_AUTOCFG, NULL);
break;
case HPC_CTRL_GET_BOARD_TYPE:
{
hpc_board_type_t board_type;
/*
* Get board type data structure, hpc_board_type_t.
*/
if (hpc_nexus_control(cbp->slot_handle,
HPC_CTRL_GET_BOARD_TYPE,
(caddr_t)&board_type) != 0) {
rv = ENXIO;
break;
}
/* copy the board type info to the user space */
if (copyout((void *)&board_type, hpc_ctrldata.data,
sizeof (hpc_board_type_t)) != 0) {
rv = ENXIO;
break;
}
break;
}
case HPC_CTRL_GET_SLOT_INFO:
{
hpc_slot_info_t slot_info;
/*
* Get slot information structure, hpc_slot_info_t.
*/
slot_info.version = HPC_SLOT_INFO_VERSION;
slot_info.slot_type = 0;
slot_info.pci_slot_capabilities = 0;
slot_info.pci_dev_num =
(uint16_t)AP_MINOR_NUM_TO_CB_INSTANCE(ap_minor);
(void) strcpy(slot_info.pci_slot_name, cbp->name);
/* copy the slot info structure to the user space */
if (copyout((void *)&slot_info, hpc_ctrldata.data,
sizeof (hpc_slot_info_t)) != 0) {
rv = ENXIO;
break;
}
break;
}
case HPC_CTRL_GET_CARD_INFO:
{
hpc_card_info_t card_info;
ddi_acc_handle_t handle;
/*
* Get card information structure, hpc_card_info_t.
*/
if (cbp->card_present == B_FALSE) {
rv = ENXIO;
break;
}
/* verify that the card is configured */
if (cbp->ostate != AP_OSTATE_CONFIGURED) {
/* either the card is not present or */
/* it is not configured. */
rv = ENXIO;
break;
}
/* get the information from the PCI config header */
/* for the function 0. */
for (child_dip = ddi_get_child(cbp->cb_dip); child_dip;
child_dip = ddi_get_next_sibling(child_dip))
if (strcmp("pcs", ddi_get_name(child_dip)))
break;
if (!child_dip) {
rv = ENXIO;
break;
}
if (pci_config_setup(child_dip, &handle)
!= DDI_SUCCESS) {
rv = EIO;
break;
}
card_info.prog_class = pci_config_get8(handle,
PCI_CONF_PROGCLASS);
card_info.base_class = pci_config_get8(handle,
PCI_CONF_BASCLASS);
card_info.sub_class = pci_config_get8(handle,
PCI_CONF_SUBCLASS);
card_info.header_type = pci_config_get8(handle,
PCI_CONF_HEADER);
pci_config_teardown(&handle);
/* copy the card info structure to the user space */
if (copyout((void *)&card_info, hpc_ctrldata.data,
sizeof (hpc_card_info_t)) != 0) {
rv = ENXIO;
break;
}
break;
}
default:
rv = EINVAL;
break;
}
mutex_exit(&cbp->cb_mutex);
break;
default:
rv = ENOTTY;
}
if (cmd != DEVCTL_AP_CONTROL)
ndi_dc_freehdl(dcp);
cardbus_err(cbp->cb_dip, 7,
"cardbus_ioctl: rv = 0x%x", rv);
return (rv);
}
struct cardbus_pci_desc {
char *name;
ushort_t offset;
int (*cfg_get_func)();
char *fmt;
};
static struct cardbus_pci_desc generic_pci_cfg[] = {
{ "VendorId =", 0, (int(*)())pci_config_get16, "%s 0x%04x" },
{ "DeviceId =", 2, (int(*)())pci_config_get16, "%s 0x%04x" },
{ "Command =", 4, (int(*)())pci_config_get16, "%s 0x%04x" },
{ "Status =", 6, (int(*)())pci_config_get16, "%s 0x%04x" },
{ "Latency =", 0xd, (int(*)())pci_config_get8, "%s 0x%02x" },
{ "BASE0 =", 0x10, (int(*)())pci_config_get32, "%s 0x%08x" },
{ "BASE1 =", 0x14, (int(*)())pci_config_get32, "%s 0x%08x" },
{ "BASE2 =", 0x18, (int(*)())pci_config_get32, "%s 0x%08x" },
{ "BASE3 =", 0x1c, (int(*)())pci_config_get32, "%s 0x%08x" },
{ "BASE4 =", 0x20, (int(*)())pci_config_get32, "%s 0x%08x" },
{ "CIS Pointer =", 0x28, (int(*)())pci_config_get32, "%s 0x%08x" },
{ "ILINE =", 0x3c, (int(*)())pci_config_get8, "%s 0x%02x" },
{ "IPIN =", 0x3d, (int(*)())pci_config_get8, "%s 0x%02x" },
{ NULL, 0, NULL, NULL }
};
static struct cardbus_pci_desc cardbus_pci_cfg[] = {
{ "VendorId =", 0, (int(*)())pci_config_get16, "%s 0x%04x" },
{ "DeviceId =", 2, (int(*)())pci_config_get16, "%s 0x%04x" },
{ "Command =", 4, (int(*)())pci_config_get16, "%s 0x%04x" },
{ "Status =", 6, (int(*)())pci_config_get16, "%s 0x%04x" },
{ "CacheLineSz =", 0xc, (int(*)())pci_config_get8, "%s 0x%02x" },
{ "Latency =", 0xd, (int(*)())pci_config_get8, "%s 0x%02x" },
{ "MemBase Addr=", 0x10, (int(*)())pci_config_get32, "%s 0x%08x" },
{ "Pri Bus =", 0x18, (int(*)())pci_config_get8, "%s 0x%02x" },
{ "Sec Bus =", 0x19, (int(*)())pci_config_get8, "%s 0x%02x" },
{ "Sub Bus =", 0x1a, (int(*)())pci_config_get8, "%s 0x%02x" },
{ "CBus Latency=", 0x1b, (int(*)())pci_config_get8, "%s 0x%02x" },
{ "Mem0 Base =", 0x1c, (int(*)())pci_config_get32, "%s 0x%08x" },
{ "Mem0 Limit =", 0x20, (int(*)())pci_config_get32, "%s 0x%08x" },
{ "Mem1 Base =", 0x24, (int(*)())pci_config_get32, "%s 0x%08x" },
{ "Mem1 Limit =", 0x28, (int(*)())pci_config_get32, "%s 0x%08x" },
{ "I/O0 Base =", 0x2c, (int(*)())pci_config_get32, "%s 0x%08x" },
{ "I/O0 Limit =", 0x30, (int(*)())pci_config_get32, "%s 0x%08x" },
{ "I/O1 Base =", 0x34, (int(*)())pci_config_get32, "%s 0x%08x" },
{ "I/O1 Limit =", 0x38, (int(*)())pci_config_get32, "%s 0x%08x" },
{ "ILINE =", 0x3c, (int(*)())pci_config_get8, "%s 0x%02x" },
{ "IPIN =", 0x3d, (int(*)())pci_config_get8, "%s 0x%02x" },
{ "Bridge Ctrl =", 0x3e, (int(*)())pci_config_get16, "%s 0x%04x" },
{ "Legacy Addr =", 0x44, (int(*)())pci_config_get32, "%s 0x%08x" },
{ NULL, 0, NULL, NULL }
};
static void
cardbus_dump(struct cardbus_pci_desc *spcfg, ddi_acc_handle_t handle)
{
int i;
for (i = 0; spcfg[i].name; i++) {
cmn_err(CE_NOTE, spcfg[i].fmt, spcfg[i].name,
spcfg[i].cfg_get_func(handle, spcfg[i].offset));
}
}
void
cardbus_dump_pci_node(dev_info_t *dip)
{
dev_info_t *next;
struct cardbus_pci_desc *spcfg;
ddi_acc_handle_t config_handle;
uint32_t VendorId;
cmn_err(CE_NOTE, "\nPCI leaf node of dip 0x%p:\n", (void *)dip);
for (next = ddi_get_child(dip); next;
next = ddi_get_next_sibling(next)) {
VendorId = ddi_getprop(DDI_DEV_T_ANY, next,
DDI_PROP_CANSLEEP|DDI_PROP_DONTPASS,
"vendor-id", -1);
if (VendorId == -1) {
/* not a pci device */
continue;
}
if (pci_config_setup(next, &config_handle) != DDI_SUCCESS) {
cmn_err(CE_WARN, "!pcic child: non pci device\n");
continue;
}
spcfg = generic_pci_cfg;
cardbus_dump(spcfg, config_handle);
pci_config_teardown(&config_handle);
}
}
void
cardbus_dump_pci_config(dev_info_t *dip)
{
struct cardbus_pci_desc *spcfg;
ddi_acc_handle_t config_handle;
if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
cmn_err(CE_WARN,
"!pci_config_setup() failed on 0x%p", (void *)dip);
return;
}
spcfg = cardbus_pci_cfg;
cardbus_dump(spcfg, config_handle);
pci_config_teardown(&config_handle);
}
void
cardbus_dump_socket(dev_info_t *dip)
{
ddi_acc_handle_t iohandle;
caddr_t ioaddr;
ddi_device_acc_attr_t attr;
attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
if (ddi_regs_map_setup(dip, 1,
(caddr_t *)&ioaddr,
0,
4096,
&attr, &iohandle) != DDI_SUCCESS) {
cmn_err(CE_WARN, "Failed to map address for 0x%p", (void *)dip);
return;
}
cmn_err(CE_NOTE, "////////////////////////////////////////");
cmn_err(CE_NOTE, "SOCKET_EVENT = [0x%x]",
ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_STATUS_EVENT)));
cmn_err(CE_NOTE, "SOCKET_MASK = [0x%x]",
ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_STATUS_MASK)));
cmn_err(CE_NOTE, "SOCKET_STATE = [0x%x]",
ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_PRESENT_STATE)));
cmn_err(CE_NOTE, "////////////////////////////////////////");
ddi_regs_map_free(&iohandle);
}