2005-04-17 02:20:36 +04:00
/*
* File : pci - acpi . c
2005-03-24 00:16:03 +03:00
* Purpose : Provide PCI support in ACPI
2005-04-17 02:20:36 +04:00
*
2005-03-19 02:53:36 +03:00
* Copyright ( C ) 2005 David Shaohua Li < shaohua . li @ intel . com >
* Copyright ( C ) 2004 Tom Long Nguyen < tom . l . nguyen @ intel . com >
* Copyright ( C ) 2004 Intel Corp .
2005-04-17 02:20:36 +04:00
*/
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/pci.h>
# include <linux/module.h>
2008-07-23 06:32:24 +04:00
# include <linux/pci-aspm.h>
2005-04-17 02:20:36 +04:00
# include <acpi/acpi.h>
# include <acpi/acpi_bus.h>
# include <linux/pci-acpi.h>
2005-03-19 08:15:48 +03:00
# include "pci.h"
2005-04-17 02:20:36 +04:00
2008-05-12 06:48:10 +04:00
struct acpi_osc_data {
acpi_handle handle ;
2008-05-15 10:20:11 +04:00
u32 support_set ;
u32 control_set ;
2008-11-20 09:22:39 +03:00
u32 control_query ;
2008-11-20 09:22:32 +03:00
int is_queried ;
2008-05-12 06:48:10 +04:00
struct list_head sibiling ;
} ;
static LIST_HEAD ( acpi_osc_data_list ) ;
2008-05-15 10:20:11 +04:00
struct acpi_osc_args {
u32 capbuf [ 3 ] ;
} ;
2008-10-17 08:48:36 +04:00
static DEFINE_MUTEX ( pci_acpi_lock ) ;
2008-05-12 06:48:10 +04:00
static struct acpi_osc_data * acpi_get_osc_data ( acpi_handle handle )
{
struct acpi_osc_data * data ;
list_for_each_entry ( data , & acpi_osc_data_list , sibiling ) {
if ( data - > handle = = handle )
return data ;
}
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return NULL ;
INIT_LIST_HEAD ( & data - > sibiling ) ;
data - > handle = handle ;
list_add_tail ( & data - > sibiling , & acpi_osc_data_list ) ;
return data ;
}
2008-05-15 10:23:13 +04:00
static u8 OSC_UUID [ 16 ] = { 0x5B , 0x4D , 0xDB , 0x33 , 0xF7 , 0x1F , 0x1C , 0x40 ,
0x96 , 0x57 , 0x74 , 0x41 , 0xC0 , 0x3D , 0xD7 , 0x66 } ;
2005-04-17 02:20:36 +04:00
2008-05-15 10:18:53 +04:00
static acpi_status acpi_run_osc ( acpi_handle handle ,
2008-11-20 09:22:39 +03:00
struct acpi_osc_args * osc_args , u32 * retval )
2005-04-17 02:20:36 +04:00
{
2008-05-15 10:18:53 +04:00
acpi_status status ;
struct acpi_object_list input ;
union acpi_object in_params [ 4 ] ;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * out_obj ;
2008-11-10 07:54:43 +03:00
u32 errors , flags = osc_args - > capbuf [ OSC_QUERY_TYPE ] ;
2005-04-17 02:20:36 +04:00
/* Setting up input parameters */
input . count = 4 ;
input . pointer = in_params ;
in_params [ 0 ] . type = ACPI_TYPE_BUFFER ;
in_params [ 0 ] . buffer . length = 16 ;
in_params [ 0 ] . buffer . pointer = OSC_UUID ;
in_params [ 1 ] . type = ACPI_TYPE_INTEGER ;
in_params [ 1 ] . integer . value = 1 ;
in_params [ 2 ] . type = ACPI_TYPE_INTEGER ;
in_params [ 2 ] . integer . value = 3 ;
in_params [ 3 ] . type = ACPI_TYPE_BUFFER ;
in_params [ 3 ] . buffer . length = 12 ;
2008-05-15 10:20:11 +04:00
in_params [ 3 ] . buffer . pointer = ( u8 * ) osc_args - > capbuf ;
2005-04-17 02:20:36 +04:00
status = acpi_evaluate_object ( handle , " _OSC " , & input , & output ) ;
2008-05-12 06:48:10 +04:00
if ( ACPI_FAILURE ( status ) )
2008-05-15 10:18:53 +04:00
return status ;
2006-05-21 02:00:08 +04:00
2008-10-24 23:50:31 +04:00
if ( ! output . length )
return AE_NULL_OBJECT ;
2008-05-15 10:18:53 +04:00
out_obj = output . pointer ;
2006-05-21 02:00:08 +04:00
if ( out_obj - > type ! = ACPI_TYPE_BUFFER ) {
2008-05-15 10:18:53 +04:00
printk ( KERN_DEBUG " Evaluate _OSC returns wrong type \n " ) ;
2006-05-21 02:00:08 +04:00
status = AE_TYPE ;
2008-05-15 10:18:53 +04:00
goto out_kfree ;
2005-04-17 02:20:36 +04:00
}
2008-11-10 07:54:43 +03:00
/* Need to ignore the bit0 in result code */
errors = * ( ( u32 * ) out_obj - > buffer . pointer ) & ~ ( 1 < < 0 ) ;
if ( errors ) {
if ( errors & OSC_REQUEST_ERROR )
2005-04-17 02:20:36 +04:00
printk ( KERN_DEBUG " _OSC request fails \n " ) ;
2008-11-10 07:54:43 +03:00
if ( errors & OSC_INVALID_UUID_ERROR )
2005-04-17 02:20:36 +04:00
printk ( KERN_DEBUG " _OSC invalid UUID \n " ) ;
2008-11-10 07:54:43 +03:00
if ( errors & OSC_INVALID_REVISION_ERROR )
2005-04-17 02:20:36 +04:00
printk ( KERN_DEBUG " _OSC invalid revision \n " ) ;
2008-11-10 07:54:43 +03:00
if ( errors & OSC_CAPABILITIES_MASK_ERROR ) {
2008-05-15 10:18:53 +04:00
if ( flags & OSC_QUERY_ENABLE )
goto out_success ;
printk ( KERN_DEBUG " _OSC FW not grant req. control \n " ) ;
status = AE_SUPPORT ;
goto out_kfree ;
2005-04-17 02:20:36 +04:00
}
2006-05-21 02:00:08 +04:00
status = AE_ERROR ;
2008-05-15 10:18:53 +04:00
goto out_kfree ;
}
out_success :
2008-11-20 09:22:39 +03:00
* retval = * ( ( u32 * ) ( out_obj - > buffer . pointer + 8 ) ) ;
2006-05-21 02:00:08 +04:00
status = AE_OK ;
2008-05-15 10:18:53 +04:00
out_kfree :
2006-05-21 02:00:08 +04:00
kfree ( output . pointer ) ;
return status ;
2005-04-17 02:20:36 +04:00
}
2008-11-20 09:22:32 +03:00
static acpi_status __acpi_query_osc ( u32 flags , struct acpi_osc_data * osc_data )
2008-10-17 08:49:46 +04:00
{
acpi_status status ;
2008-11-20 09:22:39 +03:00
u32 support_set , result ;
2008-10-17 08:49:46 +04:00
struct acpi_osc_args osc_args ;
/* do _OSC query for all possible controls */
support_set = osc_data - > support_set | ( flags & OSC_SUPPORT_MASKS ) ;
osc_args . capbuf [ OSC_QUERY_TYPE ] = OSC_QUERY_ENABLE ;
osc_args . capbuf [ OSC_SUPPORT_TYPE ] = support_set ;
osc_args . capbuf [ OSC_CONTROL_TYPE ] = OSC_CONTROL_MASKS ;
2008-11-20 09:22:39 +03:00
status = acpi_run_osc ( osc_data - > handle , & osc_args , & result ) ;
2008-10-17 08:49:46 +04:00
if ( ACPI_SUCCESS ( status ) ) {
osc_data - > support_set = support_set ;
2008-11-20 09:22:39 +03:00
osc_data - > control_query = result ;
2008-11-20 09:22:32 +03:00
osc_data - > is_queried = 1 ;
2008-10-17 08:49:46 +04:00
}
return status ;
}
2008-11-11 01:30:45 +03:00
/*
* pci_acpi_osc_support : Invoke _OSC indicating support for the given feature
* @ flags : Bitmask of flags to support
*
* See the ACPI spec for the definition of the flags
*/
int pci_acpi_osc_support ( acpi_handle handle , u32 flags )
2005-04-17 02:20:36 +04:00
{
2008-05-15 10:18:53 +04:00
acpi_status status ;
acpi_handle tmp ;
2008-11-11 01:30:45 +03:00
struct acpi_osc_data * osc_data ;
int rc = 0 ;
2005-04-17 02:20:36 +04:00
2008-05-15 10:18:53 +04:00
status = acpi_get_handle ( handle , " _OSC " , & tmp ) ;
if ( ACPI_FAILURE ( status ) )
2008-11-11 01:30:45 +03:00
return - ENOTTY ;
2007-06-06 07:44:16 +04:00
2008-10-17 08:48:36 +04:00
mutex_lock ( & pci_acpi_lock ) ;
2008-05-15 10:18:53 +04:00
osc_data = acpi_get_osc_data ( handle ) ;
if ( ! osc_data ) {
printk ( KERN_ERR " acpi osc data array is full \n " ) ;
2008-11-11 01:30:45 +03:00
rc = - ENOMEM ;
2008-10-17 08:48:36 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
}
2008-05-15 10:18:53 +04:00
2008-11-20 09:22:32 +03:00
__acpi_query_osc ( flags , osc_data ) ;
2008-10-17 08:48:36 +04:00
out :
mutex_unlock ( & pci_acpi_lock ) ;
2008-11-11 01:30:45 +03:00
return rc ;
2005-04-17 02:20:36 +04:00
}
/**
* pci_osc_control_set - commit requested control to Firmware
2005-11-24 02:45:04 +03:00
* @ handle : acpi_handle for the target ACPI object
2005-04-17 02:20:36 +04:00
* @ flags : driver ' s requested control bits
*
* Attempt to take control from Firmware on requested control bits .
* */
2005-11-01 03:20:11 +03:00
acpi_status pci_osc_control_set ( acpi_handle handle , u32 flags )
2005-04-17 02:20:36 +04:00
{
2008-05-15 10:20:11 +04:00
acpi_status status ;
2008-11-20 09:22:39 +03:00
u32 control_req , control_set , result ;
2008-05-12 17:55:45 +04:00
acpi_handle tmp ;
struct acpi_osc_data * osc_data ;
2008-05-15 10:20:11 +04:00
struct acpi_osc_args osc_args ;
2008-05-12 17:55:45 +04:00
status = acpi_get_handle ( handle , " _OSC " , & tmp ) ;
if ( ACPI_FAILURE ( status ) )
return status ;
2008-05-12 06:48:10 +04:00
2008-10-17 08:48:36 +04:00
mutex_lock ( & pci_acpi_lock ) ;
2008-05-12 17:55:45 +04:00
osc_data = acpi_get_osc_data ( handle ) ;
2008-05-12 06:48:10 +04:00
if ( ! osc_data ) {
printk ( KERN_ERR " acpi osc data array is full \n " ) ;
2008-10-17 08:48:36 +04:00
status = AE_ERROR ;
goto out ;
2008-05-12 06:48:10 +04:00
}
2005-04-17 02:20:36 +04:00
2008-11-20 09:22:39 +03:00
control_req = ( flags & OSC_CONTROL_MASKS ) ;
if ( ! control_req ) {
2008-10-17 08:48:36 +04:00
status = AE_TYPE ;
goto out ;
}
2008-05-15 10:20:11 +04:00
2008-11-20 09:22:37 +03:00
/* No need to evaluate _OSC if the control was already granted. */
2008-11-20 09:22:39 +03:00
if ( ( osc_data - > control_set & control_req ) = = control_req )
2008-10-17 08:51:00 +04:00
goto out ;
2008-10-17 08:49:46 +04:00
2008-11-20 09:22:32 +03:00
if ( ! osc_data - > is_queried ) {
status = __acpi_query_osc ( osc_data - > support_set , osc_data ) ;
if ( ACPI_FAILURE ( status ) )
goto out ;
}
2008-10-17 08:49:46 +04:00
2008-11-20 09:22:39 +03:00
if ( ( osc_data - > control_query & control_req ) ! = control_req ) {
2008-10-17 08:48:36 +04:00
status = AE_SUPPORT ;
goto out ;
}
2008-05-15 10:20:11 +04:00
2008-11-20 09:22:39 +03:00
control_set = osc_data - > control_set | control_req ;
2008-05-15 10:20:11 +04:00
osc_args . capbuf [ OSC_QUERY_TYPE ] = 0 ;
osc_args . capbuf [ OSC_SUPPORT_TYPE ] = osc_data - > support_set ;
osc_args . capbuf [ OSC_CONTROL_TYPE ] = control_set ;
2008-11-20 09:22:39 +03:00
status = acpi_run_osc ( handle , & osc_args , & result ) ;
2008-05-15 10:20:11 +04:00
if ( ACPI_SUCCESS ( status ) )
2008-11-20 09:22:39 +03:00
osc_data - > control_set = result ;
2008-10-17 08:48:36 +04:00
out :
mutex_unlock ( & pci_acpi_lock ) ;
2005-04-17 02:20:36 +04:00
return status ;
}
EXPORT_SYMBOL ( pci_osc_control_set ) ;
2005-03-19 02:53:36 +03:00
2005-03-19 08:15:48 +03:00
/*
* _SxD returns the D - state with the highest power
* ( lowest D - state number ) supported in the S - state " x " .
*
* If the devices does not have a _PRW
* ( Power Resources for Wake ) supporting system wakeup from " x "
* then the OS is free to choose a lower power ( higher number
* D - state ) than the return value from _SxD .
*
* But if _PRW is enabled at S - state " x " , the OS
* must not choose a power lower than _SxD - -
* unless the device has an _SxW method specifying
* the lowest power ( highest D - state number ) the device
* may enter while still able to wake the system .
*
* ie . depending on global OS policy :
*
* if ( _PRW at S - state x )
* choose from highest power _SxD to lowest power _SxW
* else // no _PRW at S-state x
* choose highest power _SxD or any lower power
*/
2008-06-05 03:16:37 +04:00
static pci_power_t acpi_pci_choose_state ( struct pci_dev * pdev )
2005-03-19 08:15:48 +03:00
{
2007-07-20 06:03:22 +04:00
int acpi_state ;
2005-03-19 08:15:48 +03:00
2008-06-05 03:15:40 +04:00
acpi_state = acpi_pm_device_sleep_state ( & pdev - > dev , NULL ) ;
2007-07-20 06:03:22 +04:00
if ( acpi_state < 0 )
return PCI_POWER_ERROR ;
switch ( acpi_state ) {
case ACPI_STATE_D0 :
return PCI_D0 ;
case ACPI_STATE_D1 :
return PCI_D1 ;
case ACPI_STATE_D2 :
return PCI_D2 ;
case ACPI_STATE_D3 :
return PCI_D3hot ;
}
return PCI_POWER_ERROR ;
2005-03-19 08:15:48 +03:00
}
2008-07-07 05:32:02 +04:00
static bool acpi_pci_power_manageable ( struct pci_dev * dev )
{
acpi_handle handle = DEVICE_ACPI_HANDLE ( & dev - > dev ) ;
return handle ? acpi_bus_power_manageable ( handle ) : false ;
}
2005-03-19 08:15:48 +03:00
2005-03-19 08:16:18 +03:00
static int acpi_pci_set_power_state ( struct pci_dev * dev , pci_power_t state )
{
acpi_handle handle = DEVICE_ACPI_HANDLE ( & dev - > dev ) ;
2007-07-20 06:03:25 +04:00
acpi_handle tmp ;
2008-02-23 08:41:51 +03:00
static const u8 state_conv [ ] = {
[ PCI_D0 ] = ACPI_STATE_D0 ,
[ PCI_D1 ] = ACPI_STATE_D1 ,
[ PCI_D2 ] = ACPI_STATE_D2 ,
[ PCI_D3hot ] = ACPI_STATE_D3 ,
[ PCI_D3cold ] = ACPI_STATE_D3
2005-03-19 08:16:18 +03:00
} ;
2008-07-07 05:32:52 +04:00
int error = - EINVAL ;
2005-03-19 08:16:18 +03:00
2007-07-20 06:03:25 +04:00
/* If the ACPI device has _EJ0, ignore the device */
2008-07-07 05:32:52 +04:00
if ( ! handle | | ACPI_SUCCESS ( acpi_get_handle ( handle , " _EJ0 " , & tmp ) ) )
return - ENODEV ;
2008-02-23 08:41:51 +03:00
switch ( state ) {
case PCI_D0 :
case PCI_D1 :
case PCI_D2 :
case PCI_D3hot :
case PCI_D3cold :
2008-07-07 05:32:52 +04:00
error = acpi_bus_set_power ( handle , state_conv [ state ] ) ;
2008-02-23 08:41:51 +03:00
}
2008-07-07 05:32:52 +04:00
if ( ! error )
dev_printk ( KERN_INFO , & dev - > dev ,
" power state changed by ACPI to D%d \n " , state ) ;
return error ;
2005-03-19 08:16:18 +03:00
}
2008-07-07 05:34:48 +04:00
static bool acpi_pci_can_wakeup ( struct pci_dev * dev )
{
acpi_handle handle = DEVICE_ACPI_HANDLE ( & dev - > dev ) ;
return handle ? acpi_bus_can_wakeup ( handle ) : false ;
}
static int acpi_pci_sleep_wake ( struct pci_dev * dev , bool enable )
{
int error = acpi_pm_device_sleep_wake ( & dev - > dev , enable ) ;
if ( ! error )
dev_printk ( KERN_INFO , & dev - > dev ,
" wake-up capability %s by ACPI \n " ,
enable ? " enabled " : " disabled " ) ;
return error ;
}
2008-07-07 05:32:02 +04:00
static struct pci_platform_pm_ops acpi_pci_platform_pm = {
. is_manageable = acpi_pci_power_manageable ,
. set_state = acpi_pci_set_power_state ,
. choose_state = acpi_pci_choose_state ,
2008-07-07 05:34:48 +04:00
. can_wakeup = acpi_pci_can_wakeup ,
. sleep_wake = acpi_pci_sleep_wake ,
2008-07-07 05:32:02 +04:00
} ;
2005-03-19 08:16:18 +03:00
2005-03-19 02:53:36 +03:00
/* ACPI bus type */
2006-04-28 11:42:21 +04:00
static int acpi_pci_find_device ( struct device * dev , acpi_handle * handle )
2005-03-19 02:53:36 +03:00
{
struct pci_dev * pci_dev ;
acpi_integer addr ;
pci_dev = to_pci_dev ( dev ) ;
/* Please ref to ACPI spec for the syntax of _ADR */
addr = ( PCI_SLOT ( pci_dev - > devfn ) < < 16 ) | PCI_FUNC ( pci_dev - > devfn ) ;
* handle = acpi_get_child ( DEVICE_ACPI_HANDLE ( dev - > parent ) , addr ) ;
if ( ! * handle )
return - ENODEV ;
return 0 ;
}
2006-04-28 11:42:21 +04:00
static int acpi_pci_find_root_bridge ( struct device * dev , acpi_handle * handle )
2005-03-19 02:53:36 +03:00
{
int num ;
unsigned int seg , bus ;
/*
* The string should be the same as root bridge ' s name
* Please look at ' pci_scan_bus_parented '
*/
2008-10-30 04:17:49 +03:00
num = sscanf ( dev_name ( dev ) , " pci%04x:%02x " , & seg , & bus ) ;
2005-03-19 02:53:36 +03:00
if ( num ! = 2 )
return - ENODEV ;
* handle = acpi_get_pci_rootbridge_handle ( seg , bus ) ;
if ( ! * handle )
return - ENODEV ;
return 0 ;
}
2006-04-28 11:42:21 +04:00
static struct acpi_bus_type acpi_pci_bus = {
2005-03-19 02:53:36 +03:00
. bus = & pci_bus_type ,
2006-04-28 11:42:21 +04:00
. find_device = acpi_pci_find_device ,
. find_bridge = acpi_pci_find_root_bridge ,
2005-03-19 02:53:36 +03:00
} ;
2006-04-28 11:42:21 +04:00
static int __init acpi_pci_init ( void )
2005-03-19 02:53:36 +03:00
{
int ret ;
2007-04-25 07:05:12 +04:00
if ( acpi_gbl_FADT . boot_flags & BAF_MSI_NOT_SUPPORTED ) {
printk ( KERN_INFO " ACPI FADT declares the system doesn't support MSI, so disable it \n " ) ;
pci_no_msi ( ) ;
}
2008-07-23 06:32:24 +04:00
if ( acpi_gbl_FADT . boot_flags & BAF_PCIE_ASPM_CONTROL ) {
printk ( KERN_INFO " ACPI FADT declares the system doesn't support PCIe ASPM, so disable it \n " ) ;
pcie_no_aspm ( ) ;
}
2006-04-28 11:42:21 +04:00
ret = register_acpi_bus_type ( & acpi_pci_bus ) ;
2005-03-19 02:53:36 +03:00
if ( ret )
return 0 ;
2008-07-07 05:32:02 +04:00
pci_set_platform_pm ( & acpi_pci_platform_pm ) ;
2005-03-19 02:53:36 +03:00
return 0 ;
}
2006-04-28 11:42:21 +04:00
arch_initcall ( acpi_pci_init ) ;