2015-02-05 13:44:49 +08:00
/*
* IOAPIC / IOxAPIC / IOSAPIC driver
*
* Copyright ( C ) 2009 Fujitsu Limited .
* ( c ) Copyright 2009 Hewlett - Packard Development Company , L . P .
*
* Copyright ( C ) 2014 Intel Corporation
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* Based on original drivers / pci / ioapic . c
* Yinghai Lu < yinghai @ kernel . org >
* Jiang Liu < jiang . liu @ intel . com >
*/
/*
* This driver manages I / O APICs added by hotplug after boot .
* We try to claim all I / O APIC devices , but those present at boot were
* registered when we parsed the ACPI MADT .
*/
2017-05-20 20:38:13 +02:00
# define pr_fmt(fmt) "ACPI: IOAPIC: " fmt
2015-02-05 13:44:49 +08:00
# include <linux/slab.h>
# include <linux/acpi.h>
# include <linux/pci.h>
# include <acpi/acpi.h>
struct acpi_pci_ioapic {
acpi_handle root_handle ;
acpi_handle handle ;
u32 gsi_base ;
struct resource res ;
struct pci_dev * pdev ;
struct list_head list ;
} ;
static LIST_HEAD ( ioapic_list ) ;
static DEFINE_MUTEX ( ioapic_list_lock ) ;
static acpi_status setup_res ( struct acpi_resource * acpi_res , void * data )
{
struct resource * res = data ;
struct resource_win win ;
2017-03-22 18:33:23 +01:00
/*
* We might assign this to ' res ' later , make sure all pointers are
* cleared before the resource is added to the global list
*/
memset ( & win , 0 , sizeof ( win ) ) ;
2015-02-05 13:44:49 +08:00
res - > flags = 0 ;
2016-08-17 16:00:35 +08:00
if ( acpi_dev_filter_resource_type ( acpi_res , IORESOURCE_MEM ) )
2015-02-05 13:44:49 +08:00
return AE_OK ;
if ( ! acpi_dev_resource_memory ( acpi_res , res ) ) {
if ( acpi_dev_resource_address_space ( acpi_res , & win ) | |
acpi_dev_resource_ext_address_space ( acpi_res , & win ) )
* res = win . res ;
}
if ( ( res - > flags & IORESOURCE_PREFETCH ) | |
( res - > flags & IORESOURCE_DISABLED ) )
res - > flags = 0 ;
return AE_CTRL_TERMINATE ;
}
static bool acpi_is_ioapic ( acpi_handle handle , char * * type )
{
acpi_status status ;
struct acpi_device_info * info ;
char * hid = NULL ;
bool match = false ;
if ( ! acpi_has_method ( handle , " _GSB " ) )
return false ;
status = acpi_get_object_info ( handle , & info ) ;
if ( ACPI_SUCCESS ( status ) ) {
if ( info - > valid & ACPI_VALID_HID )
hid = info - > hardware_id . string ;
if ( hid ) {
if ( strcmp ( hid , " ACPI0009 " ) = = 0 ) {
* type = " IOxAPIC " ;
match = true ;
} else if ( strcmp ( hid , " ACPI000A " ) = = 0 ) {
* type = " IOAPIC " ;
match = true ;
}
}
kfree ( info ) ;
}
return match ;
}
static acpi_status handle_ioapic_add ( acpi_handle handle , u32 lvl ,
void * context , void * * rv )
{
acpi_status status ;
unsigned long long gsi_base ;
struct acpi_pci_ioapic * ioapic ;
struct pci_dev * dev = NULL ;
2016-08-17 16:00:36 +08:00
struct resource * res = NULL , * pci_res = NULL , * crs_res ;
2015-02-05 13:44:49 +08:00
char * type = NULL ;
if ( ! acpi_is_ioapic ( handle , & type ) )
return AE_OK ;
mutex_lock ( & ioapic_list_lock ) ;
list_for_each_entry ( ioapic , & ioapic_list , list )
if ( ioapic - > handle = = handle ) {
mutex_unlock ( & ioapic_list_lock ) ;
return AE_OK ;
}
status = acpi_evaluate_integer ( handle , " _GSB " , NULL , & gsi_base ) ;
if ( ACPI_FAILURE ( status ) ) {
acpi_handle_warn ( handle , " failed to evaluate _GSB method \n " ) ;
goto exit ;
}
ioapic = kzalloc ( sizeof ( * ioapic ) , GFP_KERNEL ) ;
if ( ! ioapic ) {
pr_err ( " cannot allocate memory for new IOAPIC \n " ) ;
goto exit ;
} else {
ioapic - > root_handle = ( acpi_handle ) context ;
ioapic - > handle = handle ;
ioapic - > gsi_base = ( u32 ) gsi_base ;
INIT_LIST_HEAD ( & ioapic - > list ) ;
}
if ( acpi_ioapic_registered ( handle , ( u32 ) gsi_base ) )
goto done ;
dev = acpi_get_pci_dev ( handle ) ;
if ( dev & & pci_resource_len ( dev , 0 ) ) {
if ( pci_enable_device ( dev ) < 0 )
goto exit_put ;
pci_set_master ( dev ) ;
if ( pci_request_region ( dev , 0 , type ) )
goto exit_disable ;
2016-08-17 16:00:36 +08:00
pci_res = & dev - > resource [ 0 ] ;
2015-02-05 13:44:49 +08:00
ioapic - > pdev = dev ;
} else {
pci_dev_put ( dev ) ;
dev = NULL ;
2016-08-17 16:00:36 +08:00
}
2015-02-05 13:44:49 +08:00
2016-08-17 16:00:36 +08:00
crs_res = & ioapic - > res ;
acpi_walk_resources ( handle , METHOD_NAME__CRS , setup_res , crs_res ) ;
2016-08-17 16:00:37 +08:00
crs_res - > name = type ;
crs_res - > flags | = IORESOURCE_BUSY ;
2016-08-17 16:00:36 +08:00
if ( crs_res - > flags = = 0 ) {
acpi_handle_warn ( handle , " failed to get resource \n " ) ;
goto exit_release ;
2016-08-17 16:00:37 +08:00
} else if ( insert_resource ( & iomem_resource , crs_res ) ) {
2016-08-17 16:00:36 +08:00
acpi_handle_warn ( handle , " failed to insert resource \n " ) ;
goto exit_release ;
2015-02-05 13:44:49 +08:00
}
2016-08-17 16:00:36 +08:00
/* try pci resource first, then "_CRS" resource */
res = pci_res ;
if ( ! res | | ! res - > flags )
res = crs_res ;
2015-02-05 13:44:49 +08:00
if ( acpi_register_ioapic ( handle , res - > start , ( u32 ) gsi_base ) ) {
acpi_handle_warn ( handle , " failed to register IOAPIC \n " ) ;
goto exit_release ;
}
done :
list_add ( & ioapic - > list , & ioapic_list ) ;
mutex_unlock ( & ioapic_list_lock ) ;
if ( dev )
dev_info ( & dev - > dev , " %s at %pR, GSI %u \n " ,
type , res , ( u32 ) gsi_base ) ;
else
acpi_handle_info ( handle , " %s at %pR, GSI %u \n " ,
type , res , ( u32 ) gsi_base ) ;
return AE_OK ;
exit_release :
if ( dev )
pci_release_region ( dev , 0 ) ;
2016-08-17 16:00:36 +08:00
if ( ioapic - > res . flags & & ioapic - > res . parent )
release_resource ( & ioapic - > res ) ;
2015-02-05 13:44:49 +08:00
exit_disable :
if ( dev )
pci_disable_device ( dev ) ;
exit_put :
pci_dev_put ( dev ) ;
kfree ( ioapic ) ;
exit :
mutex_unlock ( & ioapic_list_lock ) ;
* ( acpi_status * ) rv = AE_ERROR ;
return AE_OK ;
}
2016-08-17 16:00:33 +08:00
int acpi_ioapic_add ( acpi_handle root_handle )
2015-02-05 13:44:49 +08:00
{
acpi_status status , retval = AE_OK ;
2016-08-17 16:00:33 +08:00
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE , root_handle ,
2015-02-05 13:44:49 +08:00
UINT_MAX , handle_ioapic_add , NULL ,
2016-08-17 16:00:33 +08:00
root_handle , ( void * * ) & retval ) ;
2015-02-05 13:44:49 +08:00
return ACPI_SUCCESS ( status ) & & ACPI_SUCCESS ( retval ) ? 0 : - ENODEV ;
}
2017-02-28 21:34:29 +08:00
void pci_ioapic_remove ( struct acpi_pci_root * root )
2015-02-05 13:44:49 +08:00
{
struct acpi_pci_ioapic * ioapic , * tmp ;
mutex_lock ( & ioapic_list_lock ) ;
list_for_each_entry_safe ( ioapic , tmp , & ioapic_list , list ) {
if ( root - > device - > handle ! = ioapic - > root_handle )
continue ;
if ( ioapic - > pdev ) {
pci_release_region ( ioapic - > pdev , 0 ) ;
pci_disable_device ( ioapic - > pdev ) ;
pci_dev_put ( ioapic - > pdev ) ;
}
2017-02-28 21:34:29 +08:00
}
mutex_unlock ( & ioapic_list_lock ) ;
}
int acpi_ioapic_remove ( struct acpi_pci_root * root )
{
int retval = 0 ;
struct acpi_pci_ioapic * ioapic , * tmp ;
mutex_lock ( & ioapic_list_lock ) ;
list_for_each_entry_safe ( ioapic , tmp , & ioapic_list , list ) {
if ( root - > device - > handle ! = ioapic - > root_handle )
continue ;
if ( acpi_unregister_ioapic ( ioapic - > handle , ioapic - > gsi_base ) )
retval = - EBUSY ;
2016-08-17 16:00:36 +08:00
if ( ioapic - > res . flags & & ioapic - > res . parent )
release_resource ( & ioapic - > res ) ;
2015-02-05 13:44:49 +08:00
list_del ( & ioapic - > list ) ;
kfree ( ioapic ) ;
}
mutex_unlock ( & ioapic_list_lock ) ;
return retval ;
}