forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge tag 'for-linus-4.13-v2' of git://github.com/cminyard/linux-ipmi
Pull IPMI updates from Corey Minyard: "Some small fixes for IPMI, and one medium sized changed. The medium sized change is adding a platform device for IPMI entries in the DMI table. Otherwise there is no auto loading for IPMI devices if they are only in the DMI table" * tag 'for-linus-4.13-v2' of git://github.com/cminyard/linux-ipmi: ipmi:ssif: Add missing unlock in error branch char: ipmi: constify bmc_dev_attr_group and bmc_device_type ipmi:ssif: Check dev before setting drvdata ipmi: Convert DMI handling over to a platform device ipmi: Create a platform device for a DMI-specified IPMI interface ipmi: use rcu lock around call to intf->handlers->sender() ipmi:ssif: Use i2c_adapter_id instead of adapter->nr ipmi: Use the proper default value for register size in ACPI ipmi_ssif: remove redundant null check on array client->adapter->name ipmi/watchdog: fix watchdog timeout set on reboot ipmi_ssif: unlock on allocation failure
- Loading branch information
Showing
8 changed files
with
533 additions
and
214 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,273 @@ | ||
/* | ||
* A hack to create a platform device from a DMI entry. This will | ||
* allow autoloading of the IPMI drive based on SMBIOS entries. | ||
*/ | ||
|
||
#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" | ||
|
||
struct ipmi_dmi_info { | ||
int type; | ||
u32 flags; | ||
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, | ||
u32 flags, | ||
u8 slave_addr, | ||
int irq, | ||
int offset, | ||
int type) | ||
{ | ||
struct platform_device *pdev; | ||
struct resource r[4]; | ||
unsigned int num_r = 1, size; | ||
struct property_entry p[4] = { | ||
PROPERTY_ENTRY_U8("slave-addr", slave_addr), | ||
PROPERTY_ENTRY_U8("ipmi-type", type), | ||
PROPERTY_ENTRY_U16("i2c-addr", base_addr), | ||
{ } | ||
}; | ||
char *name, *override; | ||
int rv; | ||
struct ipmi_dmi_info *info; | ||
|
||
info = kmalloc(sizeof(*info), GFP_KERNEL); | ||
if (!info) { | ||
pr_warn("ipmi:dmi: Could not allocate dmi info\n"); | ||
} else { | ||
info->type = type; | ||
info->flags = flags; | ||
info->addr = base_addr; | ||
info->slave_addr = slave_addr; | ||
info->next = ipmi_dmi_infos; | ||
ipmi_dmi_infos = info; | ||
} | ||
|
||
name = "dmi-ipmi-si"; | ||
override = "ipmi_si"; | ||
switch (type) { | ||
case IPMI_DMI_TYPE_SSIF: | ||
name = "dmi-ipmi-ssif"; | ||
override = "ipmi_ssif"; | ||
offset = 1; | ||
size = 1; | ||
break; | ||
case IPMI_DMI_TYPE_BT: | ||
size = 3; | ||
break; | ||
case IPMI_DMI_TYPE_KCS: | ||
case IPMI_DMI_TYPE_SMIC: | ||
size = 2; | ||
break; | ||
default: | ||
pr_err("ipmi:dmi: Invalid IPMI type: %d", type); | ||
return; | ||
} | ||
|
||
pdev = platform_device_alloc(name, ipmi_dmi_nr); | ||
if (!pdev) { | ||
pr_err("ipmi:dmi: Error allocation IPMI platform device"); | ||
return; | ||
} | ||
pdev->driver_override = override; | ||
|
||
if (type == IPMI_DMI_TYPE_SSIF) | ||
goto add_properties; | ||
|
||
memset(r, 0, sizeof(r)); | ||
|
||
r[0].start = base_addr; | ||
r[0].end = r[0].start + offset - 1; | ||
r[0].name = "IPMI Address 1"; | ||
r[0].flags = flags; | ||
|
||
if (size > 1) { | ||
r[1].start = r[0].start + offset; | ||
r[1].end = r[1].start + offset - 1; | ||
r[1].name = "IPMI Address 2"; | ||
r[1].flags = flags; | ||
num_r++; | ||
} | ||
|
||
if (size > 2) { | ||
r[2].start = r[1].start + offset; | ||
r[2].end = r[2].start + offset - 1; | ||
r[2].name = "IPMI Address 3"; | ||
r[2].flags = flags; | ||
num_r++; | ||
} | ||
|
||
if (irq) { | ||
r[num_r].start = irq; | ||
r[num_r].end = irq; | ||
r[num_r].name = "IPMI IRQ"; | ||
r[num_r].flags = IORESOURCE_IRQ; | ||
num_r++; | ||
} | ||
|
||
rv = platform_device_add_resources(pdev, r, num_r); | ||
if (rv) { | ||
dev_err(&pdev->dev, | ||
"ipmi:dmi: Unable to add resources: %d\n", rv); | ||
goto err; | ||
} | ||
|
||
add_properties: | ||
rv = platform_device_add_properties(pdev, p); | ||
if (rv) { | ||
dev_err(&pdev->dev, | ||
"ipmi:dmi: Unable to add properties: %d\n", rv); | ||
goto err; | ||
} | ||
|
||
rv = platform_device_add(pdev); | ||
if (rv) { | ||
dev_err(&pdev->dev, "ipmi:dmi: Unable to add device: %d\n", rv); | ||
goto err; | ||
} | ||
|
||
ipmi_dmi_nr++; | ||
return; | ||
|
||
err: | ||
platform_device_put(pdev); | ||
} | ||
|
||
/* | ||
* 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. | ||
*/ | ||
int ipmi_dmi_get_slave_addr(int type, u32 flags, unsigned long base_addr) | ||
{ | ||
struct ipmi_dmi_info *info = ipmi_dmi_infos; | ||
|
||
while (info) { | ||
if (info->type == type && | ||
info->flags == flags && | ||
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) | ||
{ | ||
const u8 *data = (const u8 *) dm; | ||
u32 flags = IORESOURCE_IO; | ||
unsigned long base_addr; | ||
u8 len = dm->length; | ||
u8 slave_addr; | ||
int irq = 0, offset; | ||
int type; | ||
|
||
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)); | ||
if (len >= DMI_IPMI_VER2_LENGTH) { | ||
if (type == IPMI_DMI_TYPE_SSIF) { | ||
offset = 0; | ||
flags = 0; | ||
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 */ | ||
flags = IORESOURCE_MEM; | ||
} | ||
|
||
/* | ||
* 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: | ||
pr_err("ipmi:dmi: Invalid offset: 0"); | ||
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; | ||
} | ||
|
||
dmi_add_platform_ipmi(base_addr, flags, slave_addr, irq, | ||
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/* | ||
* DMI defines for use by IPMI | ||
*/ | ||
|
||
#define IPMI_DMI_TYPE_KCS 0x01 | ||
#define IPMI_DMI_TYPE_SMIC 0x02 | ||
#define IPMI_DMI_TYPE_BT 0x03 | ||
#define IPMI_DMI_TYPE_SSIF 0x04 | ||
|
||
#ifdef CONFIG_IPMI_DMI_DECODE | ||
int ipmi_dmi_get_slave_addr(int type, u32 flags, unsigned long base_addr); | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.