pciehpc_acpi.c revision 5af4ae460e4b20d1119c788db199a9821b2d19b2
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* ACPI interface related functions used in PCIEHPC driver module.
*/
#include <sys/ddi_impldefs.h>
#include "pciehpc_acpi.h"
/* local static functions */
void *context);
#ifdef DEBUG
#endif
{0x5b, 0x4d, 0xdb, 0x33, 0xf7, 0x1f, 0x1c, 0x40,
0x96, 0x57, 0x74, 0x41, 0xc0, 0x3d, 0xd7, 0x66};
/*
* Function to check if ACPI hot plug is enabled on this bridge device.
* Since there is no absolute way to figure out if ACPI BIOS has hot plug
* control, the following approach is necessary:
*
* 1) If _OSC method is present then it is straight forward.
* 2) Otherwise, we need platform specific way to determine
* whether ACPI BIOS has hot plug control or not.
* (e.g: using .conf file property)
*
* NOTE: As per the PCI FW 3.0 spec, in the absence of _OSC method OS
* should not try to do native hot plug control.
*
* Returns 0 if the current mode is not ACPI hot plug.
*/
int
{
int *hotplug_mode;
int use_native_hotplug = 0;
/*
* (1) Find the ACPI device node for this bus node.
*/
/*
* No ACPI device for this bus node. Assume there is
* no ACPI hot plug support on this bus.
*/
(void *)dip));
return (0);
}
/*
* NOTE: Without _OSC method there is no reliable way to know if the
* platform implements ACPI hot plug or native hot plug. But, if it is
* present then it may be possible to use either mode (i.e ACPI or
* native-hotplug) if the platform supports both.
*/
/*
* (2) Check if _OSC method is present.
*/
/* no _OSC method present; we need to guess here! */
/*
* If "use-native-hotplug-mode" property set then it means
* the platform supports only native hot-plug mode.
*/
DDI_PROP_DONTPASS, "use-native-hotplug-mode",
if (hotplug_mode[0] == 1)
use_native_hotplug = 1;
}
if (use_native_hotplug) {
"Assuming native Hot-Plug mode\n"));
return (0); /* assume native hot plug mode */
} else {
"Assuming legacy ACPI Hot-Plug mode\n"));
return (1); /* assume ACPI hot plug mode */
}
}
/*
* (3) _OSC method exists; evaluate _OSC to use native hot-plug mode.
*
* Note: The default policy is to use native hot-plug mode if the
* platform supports it.
*/
/* failed to evaluate _OSC method; assume ACPI hot plug */
"Assuming legacy ACPI Hot-Plug mode\n"));
return (1);
}
#ifdef DEBUG
if (pciehpc_debug > 1)
#endif
if (hp_mode == NATIVE_HP_MODE) {
return (0); /* it is in native hp mode! */
}
return (1); /* use ACPI mode */
}
void
{
}
/*
* Intialize hot plug control for ACPI mode.
*/
static int
{
uint16_t bus_methods = 0;
uint16_t slot_methods = 0;
/* get the ACPI object for the bus node */
return (DDI_FAILURE);
/* get the ACPI object handle for the child node */
NULL, &slot_dev_obj);
return (DDI_FAILURE);
/*
* gather the info about the ACPI methods present on the bus node
* and the child nodes.
*/
/* save ACPI object handles, etc. */
/* initialize the slot mutex */
(void *)PCIEHPC_INTR_PRI);
return (DDI_SUCCESS);
}
/*
* Uninitialize HPC.
*/
static int
{
/* free up buffer used for misc_data */
}
/* destroy the mutex */
return (DDI_SUCCESS);
}
/*
* Enable interrupts. For ACPI hot plug this is a NOP.
* Just return DDI_SUCCESS.
*/
/*ARGSUSED*/
static int
{
return (DDI_SUCCESS);
}
/*
* Disable interrupts. For ACPI hot plug this is a NOP.
* Just return DDI_SUCCESS.
*/
/*ARGSUSED*/
static int
{
return (DDI_SUCCESS);
}
/*
* This function is similar to pciehpc_slotinfo_init() with some
* changes:
* - no need for kernel thread to handle ATTN button events
* - function ops for connect/disconnect are different
*
* ASSUMPTION: No conflict in doing reads to HP registers directly.
* Otherwise, there are no ACPI interfaces to do LED control or to get
* the hot plug capabilities (ATTN button, MRL, etc.).
*/
static int
{
/*
* setup HPS framework slot ops structure
*/
/*
* setup HPS framework slot information structure
*/
p->slot_info.slot_flags =
/* the device number is fixed as 0 as per the spec */
p->slot_info.pci_dev_num = 0;
/* read Slot Capabilities Register */
/* check if Attn Button present */
/* check if Manual Retention Latch sensor present */
/*
* PCI-E (draft) version 1.1 defines EMI Lock Present bit
* in Slot Capabilities register. Check for it.
*/
/* initialize synchronization conditional variable */
/* get current slot state from the hw */
/* setup Notify() handler for hot plug events from ACPI BIOS */
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* This function is similar to pciehcp_slotinfo_uninit() but has ACPI
* specific cleanup.
*/
static int
{
/* uninstall Notify() event handler */
return (DDI_SUCCESS);
}
/*
* This function is same as pciehpc_slot_connect() except that it
* uses ACPI method PS0 to enable power to the slot. If no PS0 method
* is present then it returns HPC_ERR_FAILED.
*/
/*ARGSUSED*/
static int
{
/* get the current state of the slot */
/* check if the slot is already in the 'connected' state */
/* slot is already in the 'connected' state */
return (HPC_SUCCESS);
}
/* read the Slot Status Register */
/* make sure the MRL switch is closed if present */
/* MRL switch is open */
goto cleanup;
}
/* make sure the slot has a device present */
if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
/* slot is empty */
goto cleanup;
}
/* get the current state of Slot Control Register */
/* check if the slot's power state is ON */
if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
/* slot is already powered up */
return (HPC_SUCCESS);
}
/* turn on power to the slot using ACPI method (PS0) */
goto cleanup;
return (HPC_SUCCESS);
return (HPC_ERR_FAILED);
}
/*
* This function is same as pciehpc_slot_disconnect() except that it
* uses ACPI method EJ0 to disable power to the slot. If no EJ0 method
* is present then it returns HPC_ERR_FAILED.
*/
/*ARGSUSED*/
static int
{
/* get the current state of the slot */
/* check if the slot is already in the 'disconnected' state */
/* slot is in the 'disconnected' state */
return (HPC_SUCCESS);
}
/* read the Slot Status Register */
/* make sure the slot has a device present */
if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
/* slot is empty */
goto cleanup;
}
/* turn off power to the slot using ACPI method (EJ0) */
goto cleanup;
return (HPC_SUCCESS);
return (HPC_ERR_FAILED);
}
/*
* Install event handler for the hot plug events on the bus node as well
* as device function (dev=0,func=0).
*/
static ACPI_STATUS
{
return (AE_NOT_FOUND);
/*
* Install event hanlder for events on the bus object.
* (Note: Insert event (hot-insert) is delivered on this object)
*/
pciehpc_acpi_notify_handler, (void *)ctrl_p);
goto cleanup;
/*
* Install event hanlder for events on the device function object.
* (Note: Eject device event (hot-remove) is delivered on this object)
*
* NOTE: Here the assumption is that Notify events are delivered
* on all of the 8 possible device functions so, subscribing to
* one of them is sufficient.
*/
pciehpc_acpi_notify_handler, (void *)ctrl_p);
return (status);
return (status);
}
/*ARGSUSED*/
static void
{
int dev_state = 0;
/*
* get the state of the device (from _STA method)
*/
}
switch (val) {
case 0: /* (re)enumerate the device */
case 3: /* Request Eject */
/* unexpected slot state; surprise removal? */
}
/* send the ATTN button event to HPS framework */
break;
default:
break;
}
}
static void
{
}
/*
* Run _PS0 method to turn on power to the slot.
*/
static ACPI_STATUS
{
int dev_state = 0;
/* get the state of the device (from _STA method) */
}
"(dev_state 0x%x, ACPI_STATUS 0x%x)",
return (AE_ERROR);
}
return (status);
}
/*
* Run _EJ0 method to turn off power to the slot.
*/
static ACPI_STATUS
{
int dev_state = 0;
/* get the state of the device (from _STA method) */
}
"(dev_state 0x%x, ACPI_STATUS 0x%x)",
return (AE_ERROR);
}
return (status);
}
/*
* Check if the child device node of the bus object has _EJ0 method
* present.
*/
static ACPI_STATUS
{
/* child device node(s) are present; check for _EJ0 method */
}
return (status);
}
/*
* Get the status info (as returned by _STA method) for the device.
*/
static ACPI_STATUS
{
/*
* Get device info object
*/
return (ret);
} else {
/*
* no _STA present; assume the device status is normal
* (i.e present, enabled, shown in UI and functioning).
* See section 6.3.7 of ACPI 3.0 spec.
*/
*statusp = STATUS_NORMAL;
}
return (ret);
}
/*
* Evaluate _OSC method to use native hot-plug mode if platform supports it.
* If platform doesn't support native hot-plug then hp_mode will be set
* to ACPI_HP_MODE.
*/
{
/* construct argument list */
/* arg0 - UUID */
/* arg1 - Revision ID */
/* arg2 - Count */
/* arg3 - Capabilities Buffer */
/* Initialize Capabilities Buffer */
/* DWORD1: no query flag set */
caps_buffer[0] = 0;
/* DWORD2: Support Field */
/* DWORD3: Control Field */
"Failed to execute _OSC method (status %d)\n", status));
return (status);
}
/* check the STATUS word in the capability buffer */
if (rbuf[0] & OSC_STATUS_ERRORS) {
rbuf[0]));
AcpiOsFree(rv);
return (AE_ERROR);
}
"STATUS 0x%x SUPPORT 0x%x CONTROL 0x%x\n",
/* check if the Native Hot-Plug Control is granted */
else
*hp_mode = ACPI_HP_MODE;
AcpiOsFree(rv);
return (AE_OK);
}
#ifdef DEBUG
static void
{
int status;
if (pcibus_obj == NULL)
return;
/* print the full path name */
return;
/* dump all the methods for this bus node */
/* dump all the child devices */
}
/*ARGSUSED*/
static ACPI_STATUS
void **ret)
{
int status;
char buf[32];
/* print the full path name */
return (status);
buf[0] = 0;
while (nl--)
/* dump all the methods for this device */
return (status);
}
/*ARGSUSED*/
static ACPI_STATUS
{
int status;
char name[16];
return (AE_OK);
}
static void
{
int status;
}
}
#endif
static ACPI_STATUS
{
/*
* Walk up the ACPI device tree looking for _OSC method.
*/
do {
break;
return (status);
}