2008-12-17 06:07:38 +03:00
/*
* ACPI related functions for PCI Express Hot Plug driver .
*
* Copyright ( C ) 2008 Kenji Kaneshige
* Copyright ( C ) 2008 Fujitsu Limited .
*
* All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or ( at
* your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE , GOOD TITLE or
* NON INFRINGEMENT . See the GNU General Public License for more
* details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*/
# include <linux/acpi.h>
2008-12-17 06:09:12 +03:00
# include <linux/pci.h>
# include <linux/pci_hotplug.h>
2008-12-17 06:07:38 +03:00
# include "pciehp.h"
# define PCIEHP_DETECT_PCIE (0)
# define PCIEHP_DETECT_ACPI (1)
2008-12-17 06:08:15 +03:00
# define PCIEHP_DETECT_AUTO (2)
# define PCIEHP_DETECT_DEFAULT PCIEHP_DETECT_AUTO
2008-12-17 06:07:38 +03:00
2009-09-15 12:24:46 +04:00
struct dummy_slot {
u32 number ;
struct list_head list ;
} ;
2008-12-17 06:07:38 +03:00
static int slot_detection_mode ;
static char * pciehp_detect_mode ;
module_param ( pciehp_detect_mode , charp , 0444 ) ;
MODULE_PARM_DESC ( pciehp_detect_mode ,
2008-12-17 06:08:15 +03:00
" Slot detection mode: pcie, acpi, auto \n "
" pcie - Use PCIe based slot detection \n "
" acpi - Use ACPI for slot detection \n "
" auto(default) - Auto select mode. Use acpi option if duplicate \n "
" slot ids are found. Otherwise, use pcie option \n " ) ;
2008-12-17 06:07:38 +03:00
int pciehp_acpi_slot_detection_check ( struct pci_dev * dev )
{
if ( slot_detection_mode ! = PCIEHP_DETECT_ACPI )
return 0 ;
2009-09-10 22:34:09 +04:00
if ( acpi_pci_detect_ejectable ( DEVICE_ACPI_HANDLE ( & dev - > dev ) ) )
2008-12-17 06:07:38 +03:00
return 0 ;
return - ENODEV ;
}
static int __init parse_detect_mode ( void )
{
if ( ! pciehp_detect_mode )
return PCIEHP_DETECT_DEFAULT ;
if ( ! strcmp ( pciehp_detect_mode , " pcie " ) )
return PCIEHP_DETECT_PCIE ;
if ( ! strcmp ( pciehp_detect_mode , " acpi " ) )
return PCIEHP_DETECT_ACPI ;
2008-12-17 06:08:15 +03:00
if ( ! strcmp ( pciehp_detect_mode , " auto " ) )
return PCIEHP_DETECT_AUTO ;
2008-12-17 06:07:38 +03:00
warn ( " bad specifier '%s' for pciehp_detect_mode. Use default \n " ,
pciehp_detect_mode ) ;
return PCIEHP_DETECT_DEFAULT ;
}
2008-12-17 06:08:15 +03:00
static int __initdata dup_slot_id ;
static int __initdata acpi_slot_detected ;
static struct list_head __initdata dummy_slots = LIST_HEAD_INIT ( dummy_slots ) ;
/* Dummy driver for dumplicate name detection */
2009-01-13 16:44:19 +03:00
static int __init dummy_probe ( struct pcie_device * dev )
2008-12-17 06:08:15 +03:00
{
int pos ;
u32 slot_cap ;
2009-09-10 22:34:09 +04:00
acpi_handle handle ;
2009-09-15 12:24:46 +04:00
struct dummy_slot * slot , * tmp ;
2008-12-17 06:08:15 +03:00
struct pci_dev * pdev = dev - > port ;
/* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */
if ( pciehp_get_hp_hw_control_from_firmware ( pdev ) )
return - ENODEV ;
if ( ! ( pos = pci_find_capability ( pdev , PCI_CAP_ID_EXP ) ) )
return - ENODEV ;
pci_read_config_dword ( pdev , pos + PCI_EXP_SLTCAP , & slot_cap ) ;
2009-02-09 00:45:24 +03:00
slot = kzalloc ( sizeof ( * slot ) , GFP_KERNEL ) ;
if ( ! slot )
return - ENOMEM ;
2008-12-17 06:08:15 +03:00
slot - > number = slot_cap > > 19 ;
2009-09-15 12:24:46 +04:00
list_for_each_entry ( tmp , & dummy_slots , list ) {
2008-12-17 06:08:15 +03:00
if ( tmp - > number = = slot - > number )
dup_slot_id + + ;
}
2009-09-15 12:24:46 +04:00
list_add_tail ( & slot - > list , & dummy_slots ) ;
2009-09-10 22:34:09 +04:00
handle = DEVICE_ACPI_HANDLE ( & pdev - > dev ) ;
if ( ! acpi_slot_detected & & acpi_pci_detect_ejectable ( handle ) )
2008-12-17 06:08:15 +03:00
acpi_slot_detected = 1 ;
return - ENODEV ; /* dummy driver always returns error */
}
static struct pcie_port_service_driver __initdata dummy_driver = {
. name = " pciehp_dummy " ,
2009-01-13 16:46:46 +03:00
. port_type = PCIE_ANY_PORT ,
. service = PCIE_PORT_SERVICE_HP ,
2008-12-17 06:08:15 +03:00
. probe = dummy_probe ,
} ;
static int __init select_detection_mode ( void )
{
2009-09-15 12:24:46 +04:00
struct dummy_slot * slot , * tmp ;
2008-12-17 06:08:15 +03:00
pcie_port_service_register ( & dummy_driver ) ;
pcie_port_service_unregister ( & dummy_driver ) ;
2009-09-15 12:24:46 +04:00
list_for_each_entry_safe ( slot , tmp , & dummy_slots , list ) {
list_del ( & slot - > list ) ;
2008-12-17 06:08:15 +03:00
kfree ( slot ) ;
}
if ( acpi_slot_detected & & dup_slot_id )
return PCIEHP_DETECT_ACPI ;
return PCIEHP_DETECT_PCIE ;
}
2008-12-17 06:07:38 +03:00
void __init pciehp_acpi_slot_detection_init ( void )
{
slot_detection_mode = parse_detect_mode ( ) ;
2008-12-17 06:08:15 +03:00
if ( slot_detection_mode ! = PCIEHP_DETECT_AUTO )
goto out ;
slot_detection_mode = select_detection_mode ( ) ;
out :
if ( slot_detection_mode = = PCIEHP_DETECT_ACPI )
info ( " Using ACPI for slot detection. \n " ) ;
2008-12-17 06:07:38 +03:00
}