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
* 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/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];
#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)
extern int cardbus_debug;
extern int number_of_cardbus_cards;
/* static functions */
int
{
char tbuf[MAXNAMELEN];
/*
* register the bus instance with the HPS framework.
*/
cardbus_new_slot_state, 0) != 0) {
return (DDI_FAILURE);
}
/*
* Fill in the slot information structure that
* describes the slot.
*/
/*
* If the slot can not be registered,
* then the slot_ops need to be freed.
*/
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
{
int cb_instance;
int rv = HPC_EVENT_CLAIMED;
ASSERT(cb_instance >= 0);
switch (event_mask) {
case HPC_EVENT_SLOT_INSERTION:
/*
* A card is inserted in the slot. Just report this
* event and return.
*/
"cardbus_event_handler(%s%d): card is inserted",
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_event_handler(%s%d): "
"SLOT_CONFIGURE event occured (slot %s)",
break;
}
"cardbus_event_handler(%s%d): configure event",
cbp->cb_instance);
break;
}
/*
* Auto configuration is enabled. First, make sure the
* receptacle is in the CONNECTED state.
*/
NULL, 0)) == HPC_SUCCESS) {
}
else
rv = HPC_ERR_FAILED;
break;
/*
* Unconfigure the occupant in this slot.
*/
if (!(cbp->auto_config)) {
/*
* auto configuration is disabled.
*/
"cardbus_event_handler(%s%d): "
"SLOT_UNCONFIGURE event"
" occured - auto-conf disabled (slot %s)",
break;
}
"cardbus_event_handler(%s%d): SLOT_UNCONFIGURE event"
" occured (slot %s)",
rv = HPC_ERR_FAILED;
break;
case HPC_EVENT_SLOT_REMOVAL:
/*
* Card is removed from the slot. The card must have been
* unconfigured before this event.
*/
"cardbus_event_handler(%s%d): "
"card is removed from"
" the slot %s before doing unconfigure!!",
break;
}
"cardbus_event_handler(%s%d): "
"card is removed from the slot %s",
break;
case HPC_EVENT_SLOT_POWER_ON:
/*
* Slot is connected to the bus. i.e the card is powered
* on.
*/
"cardbus_event_handler(%s%d): "
"card is powered on in the slot %s",
break;
case HPC_EVENT_SLOT_POWER_OFF:
/*
* Slot is disconnected from the bus. i.e the card is powered
* off.
*/
"cardbus_event_handler(%s%d): "
"card is powered off in the slot %s",
break;
default:
"cardbus_event_handler(%s%d): "
"unknown event %x for this slot %s",
break;
}
return (rv);
}
static int
{
int rval = HPC_SUCCESS;
switch (request) {
case HPC_CTRL_GET_SLOT_STATE: {
"cardbus_pci_control() - "
"HPC_CTRL_GET_SLOT_STATE hpc_slot_state=0x%p",
(void *) hpc_slot_state);
if (cbp->card_present)
else
break;
}
case HPC_CTRL_GET_BOARD_TYPE: {
"cardbus_pci_control() - HPC_CTRL_GET_BOARD_TYPE");
/*
* The HPC driver does not know what board type
* is plugged in.
*/
break;
}
case HPC_CTRL_DEV_CONFIGURED:
"cardbus_pci_control() - HPC_CTRL_DEV_%sCONFIGURED",
break;
case HPC_CTRL_GET_LED_STATE:
"cardbus_pci_control() - HPC_CTRL_GET_LED_STATE "
"led %d is %d",
break;
case HPC_CTRL_SET_LED_STATE:
"cardbus_pci_control() - HPC_CTRL_SET_LED_STATE "
"led %d to %d",
break;
case HPC_CTRL_ENABLE_AUTOCFG:
"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_pci_control() - HPC_CTRL_DISABLE_AUTOCFG");
break;
case HPC_CTRL_DISABLE_ENUM:
case HPC_CTRL_ENABLE_ENUM:
default:
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
{
int cb_instance;
int ap_minor;
int rv = 0;
"cardbus_new_slot_state: slot_handle 0x%p", hdl);
/*
* get the soft state structure for the bus instance.
*/
ASSERT(cb_instance >= 0);
switch (slot_state) {
case HPC_SLOT_ONLINE:
/*
* Make sure the slot is not already ONLINE
*/
"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 */
0) == DDI_FAILURE) {
"cardbus_new_slot_state: "
"ddi_create_minor_node failed");
rv = HPC_ERR_FAILED;
break;
}
/* save the slot handle */
/* setup event handler for all hardware events on the slot */
"cardbus_new_slot_state: "
"install event handler failed");
rv = HPC_ERR_FAILED;
break;
}
ap_minor));
/* set default auto configuration enabled flag for this slot */
/* copy the slot information */
+ 1, KM_SLEEP);
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.
*/
"cardbus: Card is still in configured state");
rv = HPC_ERR_FAILED;
break;
}
/*
* If the AP device is in open state then return
* error.
*/
rv = HPC_ERR_FAILED;
break;
}
/* remove the minor node */
/* free up the memory for the name string */
/* update the slot info data */
"cardbus_new_slot_state: Cardbus slot OFFLINE");
break;
default:
"cardbus_new_slot_state: unknown slot_state %d\n",
rv = HPC_ERR_FAILED;
}
return (rv);
}
static int
{
char pn[MAXPATHLEN];
/*
* Ignore the attachment point and pcs.
*/
return (DDI_WALK_CONTINUE);
}
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
{
int i;
int circular;
occupant.i = 0;
(void *)&occupant);
if (occupant.i == 0) {
char *c[] = { "" };
c, 1);
} else {
"create_occupant_props: %d occupant\n", occupant.i);
}
for (i = 0; i < occupant.i; i++) {
}
}
static void
{
!= DDI_PROP_SUCCESS)
return; /* add error handling */
}
/*
* **************************************
* CONFIGURE the occupant in the slot.
* **************************************
*/
static int
{
int rv = HPC_SUCCESS;
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.
*/
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.
*/
cbus_configure, (void *)&ctrl);
if (cardbus_debug) {
}
/*
* one or more of the devices are not
* onlined.
*/
"one or more drivers for the card in the slot %s",
}
/* tell HPC driver that the occupant is configured */
return (rv);
}
/*
* Occupant is in the UNCONFIGURED state.
*/
/* Check if the receptacle is in the CONNECTED state. */
return (ENXIO);
}
if (rstate != HPC_SLOT_CONNECTED) {
/* error. either the slot is empty or connect failed */
return (ENXIO);
}
/*
* Call the configurator to configure the card.
*/
return (EIO);
}
/* record the occupant state as CONFIGURED */
/* now, online all the devices in the AP */
if (cardbus_debug) {
}
/*
* one or more of the devices are not
* ONLINE'd.
*/
" more drivers for the card in the slot %s",
/* rv = EFAULT; */
}
/* tell HPC driver that the occupant is configured */
return (rv);
}
/*
* **************************************
* UNCONFIGURE the occupant in the slot.
* **************************************
*/
static int
{
/*
* check for valid request:
* 1. It is a hotplug slot.
* 2. The occupant is in the CONFIGURED state.
*/
return (ENXIO);
}
/*
* If the occupant is in the CONFIGURED state then
* call the configurator to unconfigure the slot.
*/
/*
* Detach all the drivers for the devices in the
* slot.
*/
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.
*/
"cbhp (%s%d): Failed to offline all devices"
} else {
/*
* Now that resources are freed,
* clear EXT and Turn LED ON.
*/
/*
* send the notification of state change
* to the HPC driver.
*/
} else {
}
}
}
return (rv);
}
int
{
/*
* Ignore the attachment point and pcs.
*/
return (DDI_WALK_CONTINUE);
}
/*
* 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.
*/
/* Porbably not a real device, like PCS for example */
return (DDI_WALK_PRUNECHILD);
return (DDI_WALK_TERMINATE);
}
if (pci_rp->pci_phys_hi == 0)
return (DDI_WALK_CONTINUE);
/* get the pci device id information */
/*
* free the memory allocated by ddi_prop_lookup_int_array
*/
return (DDI_WALK_CONTINUE);
"cbus_configure on-line device at: "
"cbus_configure %s\n",
if (rc != NDI_SUCCESS)
return (DDI_WALK_PRUNECHILD);
return (DDI_WALK_CONTINUE);
}
int
{
/*
* Ignore pcs.
*/
return (NDI_SUCCESS);
}
/*
* bottom up off-line
*/
int rc;
if (rc != NDI_SUCCESS)
return (rc);
}
/*
* Don't unconfigure the bridge itself.
*/
if (top_bridge)
return (NDI_SUCCESS);
"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
{
int ndi_flags = NDI_UNCONFIG;
&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);
return (NDI_SUCCESS);
"cbus_unconfigure: "
"offline bus [0x%x] device [0x%x] function [%x]\n",
return (NDI_FAILURE);
}
"Failed to tear down "
return (NDI_FAILURE);
}
return (NDI_SUCCESS);
}
{
}
int
{
int minor;
/*
* Make sure the open is for the right file type.
*/
return (EINVAL);
/*
* Get the soft state structure for the 'devctl' device.
*/
return (ENXIO);
/*
* 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 */
return (EBUSY);
}
else
return (0);
}
/*ARGSUSED*/
int
{
int minor;
return (EINVAL);
return (ENXIO);
return (0);
}
/*
* cardbus_ioctl: devctl hotplug controls
*/
/*ARGSUSED*/
int
int *rvalp)
{
struct devctl_iocdata *dcp;
int rv = 0;
int nrv = 0;
int ap_minor;
struct hpc_control_data hpc_ctrldata;
struct hpc_led_info led_info;
return (ENXIO);
/*
* read devctl ioctl data
*/
&dcp) != NDI_SUCCESS)
return (EFAULT);
#ifdef CARDBUS_DEBUG
{
char *cmd_name;
switch (cmd) {
default: cmd_name = "Unknown"; break;
}
}
#endif
switch (cmd) {
case DEVCTL_DEVICE_GETSTATE:
case DEVCTL_DEVICE_ONLINE:
case DEVCTL_DEVICE_OFFLINE:
case DEVCTL_BUS_GETSTATE:
return (rv);
default:
break;
}
switch (cmd) {
case DEVCTL_DEVICE_RESET:
break;
case DEVCTL_BUS_QUIESCE:
if (bus_state == BUS_QUIESCED)
break;
break;
case DEVCTL_BUS_UNQUIESCE:
if (bus_state == BUS_ACTIVE)
break;
break;
case DEVCTL_BUS_RESET:
break;
case DEVCTL_BUS_RESETALL:
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:
/*
*/
/*
* 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.
*/
break;
}
/* the slot occupant must be in the UNCONFIGURED state */
break;
}
/*
* Call the HPC driver to perform the operation on the slot.
*/
switch (cmd) {
case DEVCTL_AP_INSERT:
break;
case DEVCTL_AP_REMOVE:
break;
case DEVCTL_AP_CONNECT:
NULL, 0)) == 0)
break;
case DEVCTL_AP_DISCONNECT:
NULL, 0)) == 0)
break;
}
switch (rv) {
case HPC_ERR_INVALID:
break;
case HPC_ERR_NOTSUPPORTED:
break;
case HPC_ERR_FAILED:
break;
}
break;
case DEVCTL_AP_CONFIGURE:
/*
* **************************************
* CONFIGURE the occupant in the slot.
* **************************************
*/
} else
break;
case DEVCTL_AP_UNCONFIGURE:
/*
* **************************************
* UNCONFIGURE the occupant in the slot.
* **************************************
*/
} else
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.
*/
break;
}
/* try to acquire the slot mutex */
if (mutex_held)
break;
}
}
ap_state.ap_last_change = 0;
ap_state.ap_error_code = 0;
if (mutex_held)
else
if (mutex_held)
/* copy the return-AP-state information to the user space */
break;
}
case DEVCTL_AP_CONTROL:
/*
* HPC control functions:
* Changes the state of the slot and preserves
* the state across the reboot.
* Enables or disables the auto configuration
* of hot plugged occupant if the hardware
* supports notification of the hot plug
* events.
* 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
struct hpc_control32_data hpc_ctrldata32;
sizeof (struct hpc_control32_data)) != 0) {
break;
}
}
#else
sizeof (struct hpc_control_data)) != 0) {
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_ioctl: HP Control cmd 0x%x - \"%s\"",
}
#endif
/*
* check for valid request:
* 1. It is a hotplug slot.
*/
break;
}
switch (hpc_ctrldata.cmd) {
case HPC_CTRL_GET_LED_STATE:
/* copy the led info from the user space */
sizeof (hpc_led_info_t)) != 0) {
break;
}
/* get the state of LED information */
break;
}
/* copy the led info to the user space */
sizeof (hpc_led_info_t)) != 0) {
break;
}
break;
case HPC_CTRL_SET_LED_STATE:
/* copy the led info from the user space */
sizeof (hpc_led_info_t)) != 0) {
break;
}
/* set the state of an LED */
break;
}
break;
case HPC_CTRL_ENABLE_SLOT:
/*
* Enable the slot for hotplug operations.
*/
/* tell the HPC driver also */
break;
case HPC_CTRL_DISABLE_SLOT:
/*
* Disable the slot for hotplug operations.
*/
/* tell the HPC driver also */
break;
case HPC_CTRL_ENABLE_AUTOCFG:
/*
* Enable auto configuration on this slot.
*/
/* tell the HPC driver also */
break;
case HPC_CTRL_DISABLE_AUTOCFG:
/*
* Disable auto configuration on this slot.
*/
/* tell the HPC driver also */
break;
case HPC_CTRL_GET_BOARD_TYPE:
{
/*
* Get board type data structure, hpc_board_type_t.
*/
(caddr_t)&board_type) != 0) {
break;
}
/* copy the board type info to the user space */
sizeof (hpc_board_type_t)) != 0) {
break;
}
break;
}
case HPC_CTRL_GET_SLOT_INFO:
{
/*
* Get slot information structure, hpc_slot_info_t.
*/
/* copy the slot info structure to the user space */
sizeof (hpc_slot_info_t)) != 0) {
break;
}
break;
}
case HPC_CTRL_GET_CARD_INFO:
{
/*
* Get card information structure, hpc_card_info_t.
*/
break;
}
/* verify that the card is configured */
/* either the card is not present or */
/* it is not configured. */
break;
}
/* get the information from the PCI config header */
/* for the function 0. */
break;
if (!child_dip) {
break;
}
!= DDI_SUCCESS) {
break;
}
/* copy the card info structure to the user space */
sizeof (hpc_card_info_t)) != 0) {
break;
}
break;
}
default:
break;
}
break;
default:
}
if (cmd != DEVCTL_AP_CONTROL)
"cardbus_ioctl: rv = 0x%x", rv);
return (rv);
}
struct cardbus_pci_desc {
char *name;
int (*cfg_get_func)();
char *fmt;
};
static struct cardbus_pci_desc generic_pci_cfg[] = {
};
static struct cardbus_pci_desc cardbus_pci_cfg[] = {
};
static void
{
int i;
}
}
void
{
struct cardbus_pci_desc *spcfg;
"vendor-id", -1);
if (VendorId == -1) {
/* not a pci device */
continue;
}
continue;
}
}
}
void
{
struct cardbus_pci_desc *spcfg;
"!pci_config_setup() failed on 0x%p", (void *)dip);
return;
}
}
void
{
0,
4096,
return;
}
}