2018-02-20 07:30:22 -06:00
// SPDX-License-Identifier: GPL-2.0+
2017-09-12 23:04:35 -05:00
/*
* ipmi_si_pci . c
*
* Handling for IPMI devices on the PCI bus .
*/
2018-05-09 08:15:48 -07:00
# define pr_fmt(fmt) "ipmi_pci: " fmt
2017-09-12 23:04:35 -05:00
# include <linux/module.h>
# include <linux/pci.h>
# include "ipmi_si.h"
static bool pci_registered ;
static bool si_trypci = true ;
module_param_named ( trypci , si_trypci , bool , 0 ) ;
MODULE_PARM_DESC ( trypci , " Setting this to zero will disable the "
" default scan of the interfaces identified via pci " ) ;
2018-02-26 12:46:26 -06:00
# define PCI_DEVICE_ID_HP_MMC 0x121A
2017-09-12 23:04:35 -05:00
static int ipmi_pci_probe_regspacing ( struct si_sm_io * io )
{
if ( io - > si_type = = SI_KCS ) {
unsigned char status ;
int regspacing ;
io - > regsize = DEFAULT_REGSIZE ;
io - > regshift = 0 ;
/* detect 1, 4, 16byte spacing */
for ( regspacing = DEFAULT_REGSPACING ; regspacing < = 16 ; ) {
io - > regspacing = regspacing ;
if ( io - > io_setup ( io ) ) {
2018-05-09 08:15:48 -07:00
dev_err ( io - > dev , " Could not setup I/O space \n " ) ;
2017-09-12 23:04:35 -05:00
return DEFAULT_REGSPACING ;
}
/* write invalid cmd */
io - > outputb ( io , 1 , 0x10 ) ;
/* read status back */
status = io - > inputb ( io , 1 ) ;
io - > io_cleanup ( io ) ;
if ( status )
return regspacing ;
regspacing * = 4 ;
}
}
return DEFAULT_REGSPACING ;
}
2018-02-15 16:58:26 -06:00
static struct pci_device_id ipmi_pci_blacklist [ ] = {
/*
* This is a " Virtual IPMI device " , whatever that is . It appears
* as a KCS device by the class , but it is not one .
*/
{ PCI_VDEVICE ( REALTEK , 0x816c ) } ,
{ 0 , }
} ;
2017-09-12 23:04:35 -05:00
static int ipmi_pci_probe ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
{
int rv ;
struct si_sm_io io ;
2018-02-15 16:58:26 -06:00
if ( pci_match_id ( ipmi_pci_blacklist , pdev ) )
return - ENODEV ;
2017-09-12 23:04:35 -05:00
memset ( & io , 0 , sizeof ( io ) ) ;
io . addr_source = SI_PCI ;
dev_info ( & pdev - > dev , " probing via PCI " ) ;
2018-02-26 12:46:26 -06:00
switch ( pdev - > class ) {
case PCI_CLASS_SERIAL_IPMI_SMIC :
2017-09-12 23:04:35 -05:00
io . si_type = SI_SMIC ;
break ;
2018-02-26 12:46:26 -06:00
case PCI_CLASS_SERIAL_IPMI_KCS :
2017-09-12 23:04:35 -05:00
io . si_type = SI_KCS ;
break ;
2018-02-26 12:46:26 -06:00
case PCI_CLASS_SERIAL_IPMI_BT :
2017-09-12 23:04:35 -05:00
io . si_type = SI_BT ;
break ;
default :
2018-02-26 12:46:26 -06:00
dev_info ( & pdev - > dev , " Unknown IPMI class: %x \n " , pdev - > class ) ;
2017-09-12 23:04:35 -05:00
return - ENOMEM ;
}
2021-04-02 20:43:31 +03:00
rv = pcim_enable_device ( pdev ) ;
2017-09-12 23:04:35 -05:00
if ( rv ) {
dev_err ( & pdev - > dev , " couldn't enable PCI device \n " ) ;
return rv ;
}
2017-11-30 11:06:15 -06:00
if ( pci_resource_flags ( pdev , 0 ) & IORESOURCE_IO ) {
2019-02-21 12:53:00 -06:00
io . addr_space = IPMI_IO_ADDR_SPACE ;
2017-11-30 11:06:15 -06:00
io . io_setup = ipmi_si_port_setup ;
} else {
2019-02-21 12:53:00 -06:00
io . addr_space = IPMI_MEM_ADDR_SPACE ;
2017-11-30 11:06:15 -06:00
io . io_setup = ipmi_si_mem_setup ;
}
2017-09-12 23:04:35 -05:00
io . addr_data = pci_resource_start ( pdev , 0 ) ;
2018-06-06 16:11:26 +03:00
io . dev = & pdev - > dev ;
2017-09-12 23:04:35 -05:00
io . regspacing = ipmi_pci_probe_regspacing ( & io ) ;
io . regsize = DEFAULT_REGSIZE ;
io . regshift = 0 ;
io . irq = pdev - > irq ;
if ( io . irq )
io . irq_setup = ipmi_std_irq_setup ;
dev_info ( & pdev - > dev , " %pR regsize %d spacing %d irq %d \n " ,
2018-05-09 08:15:48 -07:00
& pdev - > resource [ 0 ] , io . regsize , io . regspacing , io . irq ) ;
2017-09-12 23:04:35 -05:00
2021-04-02 20:43:31 +03:00
return ipmi_si_add_smi ( & io ) ;
2017-09-12 23:04:35 -05:00
}
static void ipmi_pci_remove ( struct pci_dev * pdev )
{
ipmi_si_remove_by_dev ( & pdev - > dev ) ;
}
static const struct pci_device_id ipmi_pci_devices [ ] = {
2018-02-26 12:46:26 -06:00
{ PCI_VDEVICE ( HP , PCI_DEVICE_ID_HP_MMC ) } ,
{ PCI_DEVICE_CLASS ( PCI_CLASS_SERIAL_IPMI_SMIC , ~ 0 ) } ,
{ PCI_DEVICE_CLASS ( PCI_CLASS_SERIAL_IPMI_KCS , ~ 0 ) } ,
{ PCI_DEVICE_CLASS ( PCI_CLASS_SERIAL_IPMI_BT , ~ 0 ) } ,
2017-09-12 23:04:35 -05:00
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , ipmi_pci_devices ) ;
static struct pci_driver ipmi_pci_driver = {
2019-07-31 19:18:25 -05:00
. name = SI_DEVICE_NAME ,
2017-09-12 23:04:35 -05:00
. id_table = ipmi_pci_devices ,
. probe = ipmi_pci_probe ,
. remove = ipmi_pci_remove ,
} ;
void ipmi_si_pci_init ( void )
{
if ( si_trypci ) {
int rv = pci_register_driver ( & ipmi_pci_driver ) ;
if ( rv )
2018-05-09 08:15:48 -07:00
pr_err ( " Unable to register PCI driver: %d \n " , rv ) ;
2017-09-12 23:04:35 -05:00
else
pci_registered = true ;
}
}
void ipmi_si_pci_shutdown ( void )
{
if ( pci_registered )
pci_unregister_driver ( & ipmi_pci_driver ) ;
}