2018-02-20 16:30:22 +03:00
// SPDX-License-Identifier: GPL-2.0+
2017-06-10 05:19:52 +03:00
/*
* A hack to create a platform device from a DMI entry . This will
* allow autoloading of the IPMI drive based on SMBIOS entries .
*/
2018-06-21 23:25:21 +03:00
# define pr_fmt(fmt) "%s" fmt, "ipmi:dmi: "
# define dev_fmt pr_fmt
2017-06-10 05:19:52 +03:00
# include <linux/ipmi.h>
# include <linux/init.h>
# include <linux/dmi.h>
# include <linux/platform_device.h>
# include <linux/property.h>
# include "ipmi_dmi.h"
2019-02-21 23:21:17 +03:00
# include "ipmi_plat_data.h"
2017-06-10 05:19:52 +03:00
2017-09-18 20:38:17 +03:00
# define IPMI_DMI_TYPE_KCS 0x01
# define IPMI_DMI_TYPE_SMIC 0x02
# define IPMI_DMI_TYPE_BT 0x03
# define IPMI_DMI_TYPE_SSIF 0x04
2017-06-10 05:19:52 +03:00
struct ipmi_dmi_info {
2017-09-18 20:38:17 +03:00
enum si_type si_type ;
2019-02-21 23:21:17 +03:00
unsigned int space ; /* addr space for si, intf# for ssif */
2017-06-10 05:19:52 +03:00
unsigned long addr ;
u8 slave_addr ;
struct ipmi_dmi_info * next ;
} ;
static struct ipmi_dmi_info * ipmi_dmi_infos ;
static int ipmi_dmi_nr __initdata ;
static void __init dmi_add_platform_ipmi ( unsigned long base_addr ,
2019-02-21 23:21:17 +03:00
unsigned int space ,
2017-06-10 05:19:52 +03:00
u8 slave_addr ,
int irq ,
int offset ,
int type )
{
2019-02-21 23:21:17 +03:00
const char * name ;
2017-06-10 05:19:52 +03:00
struct ipmi_dmi_info * info ;
2019-02-21 23:21:17 +03:00
struct ipmi_plat_data p ;
2017-06-10 05:19:52 +03:00
2019-02-21 23:21:17 +03:00
memset ( & p , 0 , sizeof ( p ) ) ;
2017-06-10 05:19:52 +03:00
name = " dmi-ipmi-si " ;
2019-04-24 16:32:02 +03:00
p . iftype = IPMI_PLAT_IF_SI ;
2017-06-10 05:19:52 +03:00
switch ( type ) {
case IPMI_DMI_TYPE_SSIF :
name = " dmi-ipmi-ssif " ;
2019-04-24 16:32:02 +03:00
p . iftype = IPMI_PLAT_IF_SSIF ;
2019-02-21 23:21:17 +03:00
p . type = SI_TYPE_INVALID ;
2017-06-10 05:19:52 +03:00
break ;
case IPMI_DMI_TYPE_BT :
2019-02-21 23:21:17 +03:00
p . type = SI_BT ;
2017-06-10 05:19:52 +03:00
break ;
case IPMI_DMI_TYPE_KCS :
2019-02-21 23:21:17 +03:00
p . type = SI_KCS ;
2017-09-18 20:38:17 +03:00
break ;
2017-06-10 05:19:52 +03:00
case IPMI_DMI_TYPE_SMIC :
2019-02-21 23:21:17 +03:00
p . type = SI_SMIC ;
2017-06-10 05:19:52 +03:00
break ;
default :
2018-06-21 23:25:21 +03:00
pr_err ( " Invalid IPMI type: %d \n " , type ) ;
2017-06-10 05:19:52 +03:00
return ;
}
2019-02-21 23:21:17 +03:00
p . addr = base_addr ;
p . space = space ;
p . regspacing = offset ;
p . irq = irq ;
p . slave_addr = slave_addr ;
p . addr_source = SI_SMBIOS ;
2017-09-18 20:38:17 +03:00
info = kmalloc ( sizeof ( * info ) , GFP_KERNEL ) ;
if ( ! info ) {
2018-06-21 23:25:21 +03:00
pr_warn ( " Could not allocate dmi info \n " ) ;
2017-09-18 20:38:17 +03:00
} else {
2019-02-21 23:21:17 +03:00
info - > si_type = p . type ;
info - > space = space ;
2017-09-18 20:38:17 +03:00
info - > addr = base_addr ;
info - > slave_addr = slave_addr ;
info - > next = ipmi_dmi_infos ;
ipmi_dmi_infos = info ;
}
2019-02-21 23:21:17 +03:00
if ( ipmi_platform_add ( name , ipmi_dmi_nr , & p ) )
ipmi_dmi_nr + + ;
2017-06-10 05:19:52 +03:00
}
/*
* Look up the slave address for a given interface . This is here
* because ACPI doesn ' t have a slave address while SMBIOS does , but we
* prefer using ACPI so the ACPI code can use the IPMI namespace .
* This function allows an ACPI - specified IPMI device to look up the
* slave address from the DMI table .
*/
2019-02-21 23:21:17 +03:00
int ipmi_dmi_get_slave_addr ( enum si_type si_type , unsigned int space ,
2017-09-18 20:38:17 +03:00
unsigned long base_addr )
2017-06-10 05:19:52 +03:00
{
struct ipmi_dmi_info * info = ipmi_dmi_infos ;
while ( info ) {
2017-09-18 20:38:17 +03:00
if ( info - > si_type = = si_type & &
2019-02-21 23:21:17 +03:00
info - > space = = space & &
2017-06-10 05:19:52 +03:00
info - > addr = = base_addr )
return info - > slave_addr ;
info = info - > next ;
}
return 0 ;
}
EXPORT_SYMBOL ( ipmi_dmi_get_slave_addr ) ;
# define DMI_IPMI_MIN_LENGTH 0x10
# define DMI_IPMI_VER2_LENGTH 0x12
# define DMI_IPMI_TYPE 4
# define DMI_IPMI_SLAVEADDR 6
# define DMI_IPMI_ADDR 8
# define DMI_IPMI_ACCESS 0x10
# define DMI_IPMI_IRQ 0x11
# define DMI_IPMI_IO_MASK 0xfffe
static void __init dmi_decode_ipmi ( const struct dmi_header * dm )
{
2019-02-21 23:21:17 +03:00
const u8 * data = ( const u8 * ) dm ;
int space = IPMI_IO_ADDR_SPACE ;
unsigned long base_addr ;
u8 len = dm - > length ;
u8 slave_addr ;
int irq = 0 , offset = 0 ;
int type ;
2017-06-10 05:19:52 +03:00
if ( len < DMI_IPMI_MIN_LENGTH )
return ;
type = data [ DMI_IPMI_TYPE ] ;
slave_addr = data [ DMI_IPMI_SLAVEADDR ] ;
memcpy ( & base_addr , data + DMI_IPMI_ADDR , sizeof ( unsigned long ) ) ;
2018-06-21 23:32:48 +03:00
if ( ! base_addr ) {
pr_err ( " Base address is zero, assuming no IPMI interface \n " ) ;
return ;
}
2017-06-10 05:19:52 +03:00
if ( len > = DMI_IPMI_VER2_LENGTH ) {
if ( type = = IPMI_DMI_TYPE_SSIF ) {
2019-02-21 23:21:17 +03:00
space = 0 ; /* Match I2C interface 0. */
2017-06-10 05:19:52 +03:00
base_addr = data [ DMI_IPMI_ADDR ] > > 1 ;
if ( base_addr = = 0 ) {
/*
* Some broken systems put the I2C address in
* the slave address field . We try to
* accommodate them here .
*/
base_addr = data [ DMI_IPMI_SLAVEADDR ] > > 1 ;
slave_addr = 0 ;
}
} else {
if ( base_addr & 1 ) {
/* I/O */
base_addr & = DMI_IPMI_IO_MASK ;
} else {
/* Memory */
2019-02-21 23:21:17 +03:00
space = IPMI_MEM_ADDR_SPACE ;
2017-06-10 05:19:52 +03:00
}
/*
* If bit 4 of byte 0x10 is set , then the lsb
* for the address is odd .
*/
base_addr | = ( data [ DMI_IPMI_ACCESS ] > > 4 ) & 1 ;
irq = data [ DMI_IPMI_IRQ ] ;
/*
* The top two bits of byte 0x10 hold the
* register spacing .
*/
switch ( ( data [ DMI_IPMI_ACCESS ] > > 6 ) & 3 ) {
case 0 : /* Byte boundaries */
offset = 1 ;
break ;
case 1 : /* 32-bit boundaries */
offset = 4 ;
break ;
case 2 : /* 16-byte boundaries */
offset = 16 ;
break ;
default :
2018-06-21 23:25:21 +03:00
pr_err ( " Invalid offset: 0 \n " ) ;
2017-06-10 05:19:52 +03:00
return ;
}
}
} else {
/* Old DMI spec. */
/*
* Note that technically , the lower bit of the base
* address should be 1 if the address is I / O and 0 if
* the address is in memory . So many systems get that
* wrong ( and all that I have seen are I / O ) so we just
* ignore that bit and assume I / O . Systems that use
* memory should use the newer spec , anyway .
*/
base_addr = base_addr & DMI_IPMI_IO_MASK ;
offset = 1 ;
}
2019-02-21 23:21:17 +03:00
dmi_add_platform_ipmi ( base_addr , space , slave_addr , irq ,
2017-06-10 05:19:52 +03:00
offset , type ) ;
}
static int __init scan_for_dmi_ipmi ( void )
{
const struct dmi_device * dev = NULL ;
while ( ( dev = dmi_find_device ( DMI_DEV_TYPE_IPMI , NULL , dev ) ) )
dmi_decode_ipmi ( ( const struct dmi_header * ) dev - > device_data ) ;
return 0 ;
}
subsys_initcall ( scan_for_dmi_ipmi ) ;