2017-09-13 06:55:57 +03:00
/*
* ipmi_si_platform . c
*
* Handling for platform devices in IPMI ( ACPI , OF , and things
* coming from the platform .
*/
# include <linux/types.h>
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/of_platform.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
# include <linux/acpi.h>
# include "ipmi_si.h"
2017-09-18 20:38:17 +03:00
# include "ipmi_dmi.h"
2017-09-13 06:55:57 +03:00
# define PFX "ipmi_platform: "
static bool si_tryplatform = true ;
# ifdef CONFIG_ACPI
static bool si_tryacpi = true ;
# endif
2017-09-18 20:38:17 +03:00
# ifdef CONFIG_OF
static bool si_tryopenfirmware = true ;
# endif
2017-09-13 06:55:57 +03:00
# ifdef CONFIG_DMI
static bool si_trydmi = true ;
2017-09-18 20:38:17 +03:00
# else
static bool si_trydmi = false ;
2017-09-13 06:55:57 +03:00
# endif
module_param_named ( tryplatform , si_tryplatform , bool , 0 ) ;
MODULE_PARM_DESC ( tryplatform , " Setting this to zero will disable the "
" default scan of the interfaces identified via platform "
2017-09-18 20:38:17 +03:00
" interfaces besides ACPI, OpenFirmware, and DMI " ) ;
2017-09-13 06:55:57 +03:00
# ifdef CONFIG_ACPI
module_param_named ( tryacpi , si_tryacpi , bool , 0 ) ;
MODULE_PARM_DESC ( tryacpi , " Setting this to zero will disable the "
" default scan of the interfaces identified via ACPI " ) ;
# endif
2017-09-18 20:38:17 +03:00
# ifdef CONFIG_OF
module_param_named ( tryopenfirmware , si_tryopenfirmware , bool , 0 ) ;
2017-11-03 12:52:45 +03:00
MODULE_PARM_DESC ( tryopenfirmware , " Setting this to zero will disable the "
2017-09-18 20:38:17 +03:00
" default scan of the interfaces identified via OpenFirmware " ) ;
# endif
2017-09-13 06:55:57 +03:00
# ifdef CONFIG_DMI
module_param_named ( trydmi , si_trydmi , bool , 0 ) ;
MODULE_PARM_DESC ( trydmi , " Setting this to zero will disable the "
" default scan of the interfaces identified via DMI " ) ;
# endif
# ifdef CONFIG_ACPI
/*
* Once we get an ACPI failure , we don ' t try any more , because we go
* through the tables sequentially . Once we don ' t find a table , there
* are no more .
*/
static int acpi_failure ;
/* For GPE-type interrupts. */
static u32 ipmi_acpi_gpe ( acpi_handle gpe_device ,
u32 gpe_number , void * context )
{
struct si_sm_io * io = context ;
ipmi_si_irq_handler ( io - > irq , io - > irq_handler_data ) ;
return ACPI_INTERRUPT_HANDLED ;
}
static void acpi_gpe_irq_cleanup ( struct si_sm_io * io )
{
if ( ! io - > irq )
return ;
ipmi_irq_start_cleanup ( io ) ;
acpi_remove_gpe_handler ( NULL , io - > irq , & ipmi_acpi_gpe ) ;
}
static int acpi_gpe_irq_setup ( struct si_sm_io * io )
{
acpi_status status ;
if ( ! io - > irq )
return 0 ;
status = acpi_install_gpe_handler ( NULL ,
io - > irq ,
ACPI_GPE_LEVEL_TRIGGERED ,
& ipmi_acpi_gpe ,
io ) ;
if ( status ! = AE_OK ) {
dev_warn ( io - > dev ,
" Unable to claim ACPI GPE %d, running polled \n " ,
io - > irq ) ;
io - > irq = 0 ;
return - EINVAL ;
} else {
io - > irq_cleanup = acpi_gpe_irq_cleanup ;
ipmi_irq_finish_setup ( io ) ;
dev_info ( io - > dev , " Using ACPI GPE %d \n " , io - > irq ) ;
return 0 ;
}
}
/*
* Defined at
* http : //h21007.www2.hp.com/portal/download/files/unprot/hpspmi.pdf
*/
struct SPMITable {
s8 Signature [ 4 ] ;
u32 Length ;
u8 Revision ;
u8 Checksum ;
s8 OEMID [ 6 ] ;
s8 OEMTableID [ 8 ] ;
s8 OEMRevision [ 4 ] ;
s8 CreatorID [ 4 ] ;
s8 CreatorRevision [ 4 ] ;
u8 InterfaceType ;
u8 IPMIlegacy ;
s16 SpecificationRevision ;
/*
* Bit 0 - SCI interrupt supported
* Bit 1 - I / O APIC / SAPIC
*/
u8 InterruptType ;
/*
* If bit 0 of InterruptType is set , then this is the SCI
* interrupt in the GPEx_STS register .
*/
u8 GPE ;
s16 Reserved ;
/*
* If bit 1 of InterruptType is set , then this is the I / O
* APIC / SAPIC interrupt .
*/
u32 GlobalSystemInterrupt ;
/* The actual register address. */
struct acpi_generic_address addr ;
u8 UID [ 4 ] ;
s8 spmi_id [ 1 ] ; /* A '\0' terminated array starts here. */
} ;
static int try_init_spmi ( struct SPMITable * spmi )
{
struct si_sm_io io ;
if ( spmi - > IPMIlegacy ! = 1 ) {
pr_info ( PFX " Bad SPMI legacy %d \n " , spmi - > IPMIlegacy ) ;
return - ENODEV ;
}
memset ( & io , 0 , sizeof ( io ) ) ;
io . addr_source = SI_SPMI ;
pr_info ( PFX " probing via SPMI \n " ) ;
/* Figure out the interface type. */
switch ( spmi - > InterfaceType ) {
case 1 : /* KCS */
io . si_type = SI_KCS ;
break ;
case 2 : /* SMIC */
io . si_type = SI_SMIC ;
break ;
case 3 : /* BT */
io . si_type = SI_BT ;
break ;
case 4 : /* SSIF, just ignore */
return - EIO ;
default :
pr_info ( PFX " Unknown ACPI/SPMI SI type %d \n " ,
spmi - > InterfaceType ) ;
return - EIO ;
}
if ( spmi - > InterruptType & 1 ) {
/* We've got a GPE interrupt. */
io . irq = spmi - > GPE ;
io . irq_setup = acpi_gpe_irq_setup ;
} else if ( spmi - > InterruptType & 2 ) {
/* We've got an APIC/SAPIC interrupt. */
io . irq = spmi - > GlobalSystemInterrupt ;
io . irq_setup = ipmi_std_irq_setup ;
} else {
/* Use the default interrupt setting. */
io . irq = 0 ;
io . irq_setup = NULL ;
}
if ( spmi - > addr . bit_width ) {
/* A (hopefully) properly formed register bit width. */
io . regspacing = spmi - > addr . bit_width / 8 ;
} else {
io . regspacing = DEFAULT_REGSPACING ;
}
io . regsize = io . regspacing ;
io . regshift = spmi - > addr . bit_offset ;
if ( spmi - > addr . space_id = = ACPI_ADR_SPACE_SYSTEM_MEMORY ) {
io . addr_type = IPMI_MEM_ADDR_SPACE ;
} else if ( spmi - > addr . space_id = = ACPI_ADR_SPACE_SYSTEM_IO ) {
io . addr_type = IPMI_IO_ADDR_SPACE ;
} else {
pr_warn ( PFX " Unknown ACPI I/O Address type \n " ) ;
return - EIO ;
}
io . addr_data = spmi - > addr . address ;
pr_info ( " ipmi_si: SPMI: %s %#lx regsize %d spacing %d irq %d \n " ,
( io . addr_type = = IPMI_IO_ADDR_SPACE ) ? " io " : " mem " ,
io . addr_data , io . regsize , io . regspacing , io . irq ) ;
return ipmi_si_add_smi ( & io ) ;
}
static void spmi_find_bmc ( void )
{
acpi_status status ;
struct SPMITable * spmi ;
int i ;
if ( acpi_disabled )
return ;
if ( acpi_failure )
return ;
for ( i = 0 ; ; i + + ) {
status = acpi_get_table ( ACPI_SIG_SPMI , i + 1 ,
( struct acpi_table_header * * ) & spmi ) ;
if ( status ! = AE_OK )
return ;
try_init_spmi ( spmi ) ;
}
}
# endif
static struct resource *
ipmi_get_info_from_resources ( struct platform_device * pdev ,
struct si_sm_io * io )
{
struct resource * res , * res_second ;
res = platform_get_resource ( pdev , IORESOURCE_IO , 0 ) ;
if ( res ) {
io - > addr_type = IPMI_IO_ADDR_SPACE ;
} else {
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( res )
io - > addr_type = IPMI_MEM_ADDR_SPACE ;
}
if ( ! res ) {
dev_err ( & pdev - > dev , " no I/O or memory address \n " ) ;
return NULL ;
}
io - > addr_data = res - > start ;
io - > regspacing = DEFAULT_REGSPACING ;
res_second = platform_get_resource ( pdev ,
( io - > addr_type = = IPMI_IO_ADDR_SPACE ) ?
IORESOURCE_IO : IORESOURCE_MEM ,
1 ) ;
if ( res_second ) {
if ( res_second - > start > io - > addr_data )
io - > regspacing = res_second - > start - io - > addr_data ;
}
io - > regsize = DEFAULT_REGSIZE ;
io - > regshift = 0 ;
return res ;
}
2017-09-18 20:38:17 +03:00
static int platform_ipmi_probe ( struct platform_device * pdev )
2017-09-13 06:55:57 +03:00
{
struct si_sm_io io ;
2017-09-18 20:38:17 +03:00
u8 type , slave_addr , addr_source ;
2017-09-13 06:55:57 +03:00
int rv ;
2017-09-18 20:38:17 +03:00
rv = device_property_read_u8 ( & pdev - > dev , " addr-source " , & addr_source ) ;
if ( rv )
addr_source = SI_PLATFORM ;
if ( addr_source > = SI_LAST )
return - EINVAL ;
if ( addr_source = = SI_SMBIOS ) {
if ( ! si_trydmi )
return - ENODEV ;
} else {
if ( ! si_tryplatform )
return - ENODEV ;
}
2017-09-13 06:55:57 +03:00
rv = device_property_read_u8 ( & pdev - > dev , " ipmi-type " , & type ) ;
if ( rv )
return - ENODEV ;
memset ( & io , 0 , sizeof ( io ) ) ;
2017-09-18 20:38:17 +03:00
io . addr_source = addr_source ;
dev_info ( & pdev - > dev , PFX " probing via %s \n " ,
ipmi_addr_src_to_str ( addr_source ) ) ;
2017-09-13 06:55:57 +03:00
switch ( type ) {
2017-09-18 20:38:17 +03:00
case SI_KCS :
case SI_SMIC :
case SI_BT :
io . si_type = type ;
2017-09-13 06:55:57 +03:00
break ;
default :
2017-09-18 20:38:17 +03:00
dev_err ( & pdev - > dev , " ipmi-type property is invalid \n " ) ;
2017-09-13 06:55:57 +03:00
return - EINVAL ;
}
2017-09-18 20:38:17 +03:00
if ( ! ipmi_get_info_from_resources ( pdev , & io ) )
return - EINVAL ;
2017-09-13 06:55:57 +03:00
rv = device_property_read_u8 ( & pdev - > dev , " slave-addr " , & slave_addr ) ;
if ( rv ) {
2017-09-18 20:38:17 +03:00
dev_warn ( & pdev - > dev , " device has no slave-addr property \n " ) ;
2017-09-13 06:55:57 +03:00
io . slave_addr = 0x20 ;
} else {
io . slave_addr = slave_addr ;
}
io . irq = platform_get_irq ( pdev , 0 ) ;
if ( io . irq > 0 )
io . irq_setup = ipmi_std_irq_setup ;
else
io . irq = 0 ;
io . dev = & pdev - > dev ;
pr_info ( " ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d \n " ,
( io . addr_type = = IPMI_IO_ADDR_SPACE ) ? " io " : " mem " ,
io . addr_data , io . regsize , io . regspacing , io . irq ) ;
ipmi_si_add_smi ( & io ) ;
return 0 ;
}
# ifdef CONFIG_OF
static const struct of_device_id of_ipmi_match [ ] = {
{ . type = " ipmi " , . compatible = " ipmi-kcs " ,
. data = ( void * ) ( unsigned long ) SI_KCS } ,
{ . type = " ipmi " , . compatible = " ipmi-smic " ,
. data = ( void * ) ( unsigned long ) SI_SMIC } ,
{ . type = " ipmi " , . compatible = " ipmi-bt " ,
. data = ( void * ) ( unsigned long ) SI_BT } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , of_ipmi_match ) ;
static int of_ipmi_probe ( struct platform_device * pdev )
{
const struct of_device_id * match ;
struct si_sm_io io ;
struct resource resource ;
const __be32 * regsize , * regspacing , * regshift ;
struct device_node * np = pdev - > dev . of_node ;
int ret ;
int proplen ;
2017-09-18 20:38:17 +03:00
if ( ! si_tryopenfirmware )
return - ENODEV ;
2017-09-13 06:55:57 +03:00
dev_info ( & pdev - > dev , " probing via device tree \n " ) ;
match = of_match_device ( of_ipmi_match , & pdev - > dev ) ;
if ( ! match )
return - ENODEV ;
if ( ! of_device_is_available ( np ) )
return - EINVAL ;
ret = of_address_to_resource ( np , 0 , & resource ) ;
if ( ret ) {
dev_warn ( & pdev - > dev , PFX " invalid address from OF \n " ) ;
return ret ;
}
regsize = of_get_property ( np , " reg-size " , & proplen ) ;
if ( regsize & & proplen ! = 4 ) {
dev_warn ( & pdev - > dev , PFX " invalid regsize from OF \n " ) ;
return - EINVAL ;
}
regspacing = of_get_property ( np , " reg-spacing " , & proplen ) ;
if ( regspacing & & proplen ! = 4 ) {
dev_warn ( & pdev - > dev , PFX " invalid regspacing from OF \n " ) ;
return - EINVAL ;
}
regshift = of_get_property ( np , " reg-shift " , & proplen ) ;
if ( regshift & & proplen ! = 4 ) {
dev_warn ( & pdev - > dev , PFX " invalid regshift from OF \n " ) ;
return - EINVAL ;
}
memset ( & io , 0 , sizeof ( io ) ) ;
io . si_type = ( enum si_type ) match - > data ;
io . addr_source = SI_DEVICETREE ;
io . irq_setup = ipmi_std_irq_setup ;
if ( resource . flags & IORESOURCE_IO )
io . addr_type = IPMI_IO_ADDR_SPACE ;
else
io . addr_type = IPMI_MEM_ADDR_SPACE ;
io . addr_data = resource . start ;
io . regsize = regsize ? be32_to_cpup ( regsize ) : DEFAULT_REGSIZE ;
io . regspacing = regspacing ? be32_to_cpup ( regspacing ) : DEFAULT_REGSPACING ;
io . regshift = regshift ? be32_to_cpup ( regshift ) : 0 ;
io . irq = irq_of_parse_and_map ( pdev - > dev . of_node , 0 ) ;
io . dev = & pdev - > dev ;
dev_dbg ( & pdev - > dev , " addr 0x%lx regsize %d spacing %d irq %d \n " ,
io . addr_data , io . regsize , io . regspacing , io . irq ) ;
return ipmi_si_add_smi ( & io ) ;
}
# else
# define of_ipmi_match NULL
static int of_ipmi_probe ( struct platform_device * dev )
{
return - ENODEV ;
}
# endif
# ifdef CONFIG_ACPI
static int find_slave_address ( struct si_sm_io * io , int slave_addr )
{
# ifdef CONFIG_IPMI_DMI_DECODE
if ( ! slave_addr ) {
u32 flags = IORESOURCE_IO ;
if ( io - > addr_type = = IPMI_MEM_ADDR_SPACE )
flags = IORESOURCE_MEM ;
2017-09-18 20:38:17 +03:00
slave_addr = ipmi_dmi_get_slave_addr ( io - > si_type , flags ,
2017-09-13 06:55:57 +03:00
io - > addr_data ) ;
}
# endif
return slave_addr ;
}
static int acpi_ipmi_probe ( struct platform_device * pdev )
{
struct si_sm_io io ;
acpi_handle handle ;
acpi_status status ;
unsigned long long tmp ;
struct resource * res ;
int rv = - EINVAL ;
if ( ! si_tryacpi )
return - ENODEV ;
handle = ACPI_HANDLE ( & pdev - > dev ) ;
if ( ! handle )
return - ENODEV ;
memset ( & io , 0 , sizeof ( io ) ) ;
io . addr_source = SI_ACPI ;
dev_info ( & pdev - > dev , PFX " probing via ACPI \n " ) ;
io . addr_info . acpi_info . acpi_handle = handle ;
/* _IFT tells us the interface type: KCS, BT, etc */
status = acpi_evaluate_integer ( handle , " _IFT " , NULL , & tmp ) ;
if ( ACPI_FAILURE ( status ) ) {
dev_err ( & pdev - > dev ,
" Could not find ACPI IPMI interface type \n " ) ;
goto err_free ;
}
switch ( tmp ) {
case 1 :
io . si_type = SI_KCS ;
break ;
case 2 :
io . si_type = SI_SMIC ;
break ;
case 3 :
io . si_type = SI_BT ;
break ;
case 4 : /* SSIF, just ignore */
rv = - ENODEV ;
goto err_free ;
default :
dev_info ( & pdev - > dev , " unknown IPMI type %lld \n " , tmp ) ;
goto err_free ;
}
res = ipmi_get_info_from_resources ( pdev , & io ) ;
if ( ! res ) {
rv = - EINVAL ;
goto err_free ;
}
/* If _GPE exists, use it; otherwise use standard interrupts */
status = acpi_evaluate_integer ( handle , " _GPE " , NULL , & tmp ) ;
if ( ACPI_SUCCESS ( status ) ) {
io . irq = tmp ;
io . irq_setup = acpi_gpe_irq_setup ;
} else {
int irq = platform_get_irq ( pdev , 0 ) ;
if ( irq > 0 ) {
io . irq = irq ;
io . irq_setup = ipmi_std_irq_setup ;
}
}
io . slave_addr = find_slave_address ( & io , io . slave_addr ) ;
io . dev = & pdev - > dev ;
dev_info ( io . dev , " %pR regsize %d spacing %d irq %d \n " ,
res , io . regsize , io . regspacing , io . irq ) ;
return ipmi_si_add_smi ( & io ) ;
err_free :
return rv ;
}
static const struct acpi_device_id acpi_ipmi_match [ ] = {
{ " IPI0001 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , acpi_ipmi_match ) ;
# else
static int acpi_ipmi_probe ( struct platform_device * dev )
{
return - ENODEV ;
}
# endif
static int ipmi_probe ( struct platform_device * pdev )
{
if ( pdev - > dev . of_node & & of_ipmi_probe ( pdev ) = = 0 )
return 0 ;
if ( acpi_ipmi_probe ( pdev ) = = 0 )
return 0 ;
2017-09-18 20:38:17 +03:00
return platform_ipmi_probe ( pdev ) ;
2017-09-13 06:55:57 +03:00
}
static int ipmi_remove ( struct platform_device * pdev )
{
return ipmi_si_remove_by_dev ( & pdev - > dev ) ;
}
struct platform_driver ipmi_platform_driver = {
. driver = {
. name = DEVICE_NAME ,
. of_match_table = of_ipmi_match ,
. acpi_match_table = ACPI_PTR ( acpi_ipmi_match ) ,
} ,
. probe = ipmi_probe ,
. remove = ipmi_remove ,
} ;
void ipmi_si_platform_init ( void )
{
2017-09-18 20:38:17 +03:00
int rv = platform_driver_register ( & ipmi_platform_driver ) ;
if ( rv )
pr_err ( PFX " Unable to register driver: %d \n " , rv ) ;
2017-09-13 06:55:57 +03:00
# ifdef CONFIG_ACPI
if ( si_tryacpi )
spmi_find_bmc ( ) ;
# endif
}
void ipmi_si_platform_shutdown ( void )
{
platform_driver_unregister ( & ipmi_platform_driver ) ;
}