mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-19 16:57:47 +07:00
6363b3f3ac
This is signed by my new key (919BFF81), which is now signed by my old key. This is a fairly large rework of the IPMI code, along with a bunch of smaller fixes. The major changes have been in the next tree for a couple of months, so they should be good to do in. - Some users had IPMI systems where the GUID of the IPMI controller could change. So rescanning of the GUID was added. The naming of some sysfs things was dependent on the GUID, however, so this resulted in the sysfs interface code in IPMI changing to remove that dependency and name the IPMI BMCs like other sysfs devices. - The ipmi_si_intf.c code was fairly bloated with all the different discovery methods (PCI, ACPI, SMBIOS, OF, platform, module parameters, hot add). The structure of how the interfaces were added was redone to make them more modular, then the individual methods were pulled out into their own files. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJaDJ7qAAoJEGHzjJCRm/+BTt8P/j9pczwT/l6SZxtZpeLk1qwU DeDVXTg+xFgn3Pii+aZ3VTZFylOv5IVQguf0GNTT8l8HXUakOhUnrj7exeYm7k5G MCwi/gjegS5HBxttBCAxnFDg2Stns6nOcOoG+fUKek5vBjwZ6Su4khDZIS74kX91 9dVPDi+z7N2PLGb1URhbFDqyqBOw6g5LWny+ak+hgch8J9ct9ENlu+VWJdKoE2Ra tgAEZt+eFj0yiJg88gtuPbqBAbRtIBis/pO8ItzKhNQT3DEcs/lCkRkrr9NLZAdF vGxxIU628kGkhaohv2gbXdfSjoDQaX4FGU6sXIZle7O8VN3Wij/2xbWjwpjX9DQu U4aW2Ou13eknCPwCGmF1dtDbxea6sZLN0yw63/uyv2Jxp2pUpV8upOpD5jbMcbfs zn90pacztqsWN/f2BPqXarYhROKolQPjkEtMSxSiqX17Gp0zAzDwjBzATxtpJ9eo IR1ET4t5xecd0uCrfR4MYJi2wLCwJdysllUvbrRzNllAaepZiECv9hhis1SLZeUQ 8/pUg9SrPtIelAo8Hgus/kwa7TFI48S9OOm6umGwASGFyhx+lXeMYWKmoKHb5PQv 5KjOQ6HKCQjvNpuDyueR51sdK/oFUSSdQ12jk8vMJD0VCDXxa28oC+quknWisT6d vcNSdI6yIEn64SfWdGVO =8zFf -----END PGP SIGNATURE----- Merge tag 'ipmi-for-4.15' of git://github.com/cminyard/linux-ipmi Pull IPMI updates from Corey Minyard: "This is a fairly large rework of the IPMI code, along with a bunch of smaller fixes. The major changes have been in the next tree for a couple of months, so they should be good to do in. - Some users had IPMI systems where the GUID of the IPMI controller could change. So rescanning of the GUID was added. The naming of some sysfs things was dependent on the GUID, however, so this resulted in the sysfs interface code in IPMI changing to remove that dependency and name the IPMI BMCs like other sysfs devices. - The ipmi_si_intf.c code was fairly bloated with all the different discovery methods (PCI, ACPI, SMBIOS, OF, platform, module parameters, hot add). The structure of how the interfaces were added was redone to make them more modular, then the individual methods were pulled out into their own files" * tag 'ipmi-for-4.15' of git://github.com/cminyard/linux-ipmi: (48 commits) ipmi_si: Delete an error message for a failed memory allocation in try_smi_init() ipmi_si: fix memory leak on new_smi ipmi: remove redundant initialization of bmc ipmi: pr_err() strings should end with newlines ipmi: Clean up some print operations ipmi: Make the DMI probe into a generic platform probe ipmi: Make the IPMI proc interface configurable ipmi_ssif: Add device attrs for the things in proc ipmi_si: Add device attrs for the things in proc ipmi_si: remove ipmi_smi_alloc() function ipmi_si: Move port and mem I/O handling to their own files ipmi_si: Get rid of unused spacing and port fields ipmi_si: Move PARISC handling to another file ipmi_si: Move PCI setup to another file ipmi_si: Move platform device handling to another file ipmi_si: Move hardcode handling to a separate file. ipmi_si: Move the hotmod handling to another file. ipmi_si: Change ipmi_si_add_smi() to take just I/O info ipmi_si: Move io setup into io structure ipmi_si: Move irq setup handling into the io struct ...
303 lines
6.8 KiB
C
303 lines
6.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* 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_si_sm.h"
|
|
#include "ipmi_dmi.h"
|
|
|
|
#define IPMI_DMI_TYPE_KCS 0x01
|
|
#define IPMI_DMI_TYPE_SMIC 0x02
|
|
#define IPMI_DMI_TYPE_BT 0x03
|
|
#define IPMI_DMI_TYPE_SSIF 0x04
|
|
|
|
struct ipmi_dmi_info {
|
|
enum si_type si_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;
|
|
|
|
#define set_prop_entry(_p_, _name_, type, val) \
|
|
do { \
|
|
struct property_entry *_p = &_p_; \
|
|
_p->name = _name_; \
|
|
_p->length = sizeof(type); \
|
|
_p->is_string = false; \
|
|
_p->value.type##_data = val; \
|
|
} while(0)
|
|
|
|
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[5];
|
|
unsigned int pidx = 0;
|
|
char *name, *override;
|
|
int rv;
|
|
enum si_type si_type;
|
|
struct ipmi_dmi_info *info;
|
|
|
|
memset(p, 0, sizeof(p));
|
|
|
|
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;
|
|
si_type = SI_TYPE_INVALID;
|
|
break;
|
|
case IPMI_DMI_TYPE_BT:
|
|
size = 3;
|
|
si_type = SI_BT;
|
|
break;
|
|
case IPMI_DMI_TYPE_KCS:
|
|
size = 2;
|
|
si_type = SI_KCS;
|
|
break;
|
|
case IPMI_DMI_TYPE_SMIC:
|
|
size = 2;
|
|
si_type = SI_SMIC;
|
|
break;
|
|
default:
|
|
pr_err("ipmi:dmi: Invalid IPMI type: %d\n", type);
|
|
return;
|
|
}
|
|
|
|
if (si_type != SI_TYPE_INVALID)
|
|
set_prop_entry(p[pidx++], "ipmi-type", u8, si_type);
|
|
set_prop_entry(p[pidx++], "slave-addr", u8, slave_addr);
|
|
set_prop_entry(p[pidx++], "addr-source", u8, SI_SMBIOS);
|
|
|
|
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
|
if (!info) {
|
|
pr_warn("ipmi:dmi: Could not allocate dmi info\n");
|
|
} else {
|
|
info->si_type = si_type;
|
|
info->flags = flags;
|
|
info->addr = base_addr;
|
|
info->slave_addr = slave_addr;
|
|
info->next = ipmi_dmi_infos;
|
|
ipmi_dmi_infos = info;
|
|
}
|
|
|
|
pdev = platform_device_alloc(name, ipmi_dmi_nr);
|
|
if (!pdev) {
|
|
pr_err("ipmi:dmi: Error allocation IPMI platform device\n");
|
|
return;
|
|
}
|
|
pdev->driver_override = override;
|
|
|
|
if (type == IPMI_DMI_TYPE_SSIF) {
|
|
set_prop_entry(p[pidx++], "i2c-addr", u16, base_addr);
|
|
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(enum si_type si_type, u32 flags,
|
|
unsigned long base_addr)
|
|
{
|
|
struct ipmi_dmi_info *info = ipmi_dmi_infos;
|
|
|
|
while (info) {
|
|
if (info->si_type == si_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\n");
|
|
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);
|