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
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 ;
2009-02-03 10:14:33 +03:00
if ( acpi_gbl_FADT . boot_flags & ACPI_FADT_NO_MSI ) {
2007-04-25 07:05:12 +04:00
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
2009-02-03 10:14:33 +03:00
if ( acpi_gbl_FADT . boot_flags & ACPI_FADT_NO_ASPM ) {
2008-07-23 06:32:24 +04:00
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 ) ;