diff options
author | Linus Torvalds | 2017-07-10 10:59:29 -0700 |
---|---|---|
committer | Linus Torvalds | 2017-07-10 10:59:29 -0700 |
commit | 9eb788800510ae1a6bc419636a66071ee4deafd5 (patch) | |
tree | 3ee54d847ea09b994bd9f9189cf3116be4e12daa /drivers/char | |
parent | 642338ba33c5331f2b94ca3944845741fbbf8b89 (diff) | |
parent | 4495ec6d770e1bca7a04e93ac453ab6720c56c5d (diff) |
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
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/ipmi/Kconfig | 4 | ||||
-rw-r--r-- | drivers/char/ipmi/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_dmi.c | 273 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_dmi.h | 12 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 9 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 265 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_ssif.c | 176 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_watchdog.c | 7 |
8 files changed, 533 insertions, 214 deletions
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 90f3edffb067..f6fa056a52fc 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -5,6 +5,7 @@ menuconfig IPMI_HANDLER tristate 'IPMI top-level message handler' depends on HAS_IOMEM + select IPMI_DMI_DECODE if DMI help This enables the central IPMI message handler, required for IPMI to work. @@ -16,6 +17,9 @@ menuconfig IPMI_HANDLER If unsure, say N. +config IPMI_DMI_DECODE + bool + if IPMI_HANDLER config IPMI_PANIC_EVENT diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index 0d98cd91def1..eefb0b301e83 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -7,6 +7,7 @@ ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o obj-$(CONFIG_IPMI_SI) += ipmi_si.o +obj-$(CONFIG_IPMI_DMI_DECODE) += ipmi_dmi.o obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c new file mode 100644 index 000000000000..2a84401dea05 --- /dev/null +++ b/drivers/char/ipmi/ipmi_dmi.c @@ -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); diff --git a/drivers/char/ipmi/ipmi_dmi.h b/drivers/char/ipmi/ipmi_dmi.h new file mode 100644 index 000000000000..0a1afe5ceb1e --- /dev/null +++ b/drivers/char/ipmi/ipmi_dmi.h @@ -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 diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 9f699951b75a..810b138f5897 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -2397,7 +2397,7 @@ static umode_t bmc_dev_attr_is_visible(struct kobject *kobj, return mode; } -static struct attribute_group bmc_dev_attr_group = { +static const struct attribute_group bmc_dev_attr_group = { .attrs = bmc_dev_attrs, .is_visible = bmc_dev_attr_is_visible, }; @@ -2407,7 +2407,7 @@ static const struct attribute_group *bmc_dev_attr_groups[] = { NULL }; -static struct device_type bmc_device_type = { +static const struct device_type bmc_device_type = { .groups = bmc_dev_attr_groups, }; @@ -3878,6 +3878,9 @@ static void smi_recv_tasklet(unsigned long val) * because the lower layer is allowed to hold locks while calling * message delivery. */ + + rcu_read_lock(); + if (!run_to_completion) spin_lock_irqsave(&intf->xmit_msgs_lock, flags); if (intf->curr_msg == NULL && !intf->in_shutdown) { @@ -3900,6 +3903,8 @@ static void smi_recv_tasklet(unsigned long val) if (newmsg) intf->handlers->sender(intf->send_info, newmsg); + rcu_read_unlock(); + handle_new_recv_msgs(intf); } diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 59ee93ea84eb..985973855005 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -61,6 +61,7 @@ #include <linux/ipmi_smi.h> #include <asm/io.h> #include "ipmi_si_sm.h" +#include "ipmi_dmi.h" #include <linux/dmi.h> #include <linux/string.h> #include <linux/ctype.h> @@ -1942,7 +1943,7 @@ static int hotmod_handler(const char *val, struct kernel_param *kp) info->io.regspacing = DEFAULT_REGSPACING; info->io.regsize = regsize; if (!info->io.regsize) - info->io.regsize = DEFAULT_REGSPACING; + info->io.regsize = DEFAULT_REGSIZE; info->io.regshift = regshift; info->irq = irq; if (info->irq) @@ -2036,7 +2037,7 @@ static int hardcode_find_bmc(void) info->io.regspacing = DEFAULT_REGSPACING; info->io.regsize = regsizes[i]; if (!info->io.regsize) - info->io.regsize = DEFAULT_REGSPACING; + info->io.regsize = DEFAULT_REGSIZE; info->io.regshift = regshifts[i]; info->irq = irqs[i]; if (info->irq) @@ -2273,136 +2274,105 @@ static void spmi_find_bmc(void) } #endif -#ifdef CONFIG_DMI -struct dmi_ipmi_data { - u8 type; - u8 addr_space; - unsigned long base_addr; - u8 irq; - u8 offset; - u8 slave_addr; -}; - -static int decode_dmi(const struct dmi_header *dm, - struct dmi_ipmi_data *dmi) +#if defined(CONFIG_DMI) || defined(CONFIG_ACPI) +struct resource *ipmi_get_info_from_resources(struct platform_device *pdev, + struct smi_info *info) { - const u8 *data = (const u8 *)dm; - unsigned long base_addr; - u8 reg_spacing; - u8 len = dm->length; - - dmi->type = data[4]; + struct resource *res, *res_second; - memcpy(&base_addr, data+8, sizeof(unsigned long)); - if (len >= 0x11) { - if (base_addr & 1) { - /* I/O */ - base_addr &= 0xFFFE; - dmi->addr_space = IPMI_IO_ADDR_SPACE; - } else - /* Memory */ - dmi->addr_space = IPMI_MEM_ADDR_SPACE; - - /* If bit 4 of byte 0x10 is set, then the lsb for the address - is odd. */ - dmi->base_addr = base_addr | ((data[0x10] & 0x10) >> 4); - - dmi->irq = data[0x11]; - - /* The top two bits of byte 0x10 hold the register spacing. */ - reg_spacing = (data[0x10] & 0xC0) >> 6; - switch (reg_spacing) { - case 0x00: /* Byte boundaries */ - dmi->offset = 1; - break; - case 0x01: /* 32-bit boundaries */ - dmi->offset = 4; - break; - case 0x02: /* 16-byte boundaries */ - dmi->offset = 16; - break; - default: - /* Some other interface, just ignore it. */ - return -EIO; - } + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (res) { + info->io_setup = port_setup; + info->io.addr_type = IPMI_IO_ADDR_SPACE; } 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. - */ - dmi->base_addr = base_addr & 0xfffe; - dmi->addr_space = IPMI_IO_ADDR_SPACE; - dmi->offset = 1; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) { + info->io_setup = mem_setup; + info->io.addr_type = IPMI_MEM_ADDR_SPACE; + } } + if (!res) { + dev_err(&pdev->dev, "no I/O or memory address\n"); + return NULL; + } + info->io.addr_data = res->start; - dmi->slave_addr = data[6]; + info->io.regspacing = DEFAULT_REGSPACING; + res_second = platform_get_resource(pdev, + (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? + IORESOURCE_IO : IORESOURCE_MEM, + 1); + if (res_second) { + if (res_second->start > info->io.addr_data) + info->io.regspacing = + res_second->start - info->io.addr_data; + } + info->io.regsize = DEFAULT_REGSIZE; + info->io.regshift = 0; - return 0; + return res; } -static void try_init_dmi(struct dmi_ipmi_data *ipmi_data) +#endif + +#ifdef CONFIG_DMI +static int dmi_ipmi_probe(struct platform_device *pdev) { struct smi_info *info; + u8 type, slave_addr; + int rv; + + if (!si_trydmi) + return -ENODEV; + + rv = device_property_read_u8(&pdev->dev, "ipmi-type", &type); + if (rv) + return -ENODEV; info = smi_info_alloc(); if (!info) { pr_err(PFX "Could not allocate SI data\n"); - return; + return -ENOMEM; } info->addr_source = SI_SMBIOS; pr_info(PFX "probing via SMBIOS\n"); - switch (ipmi_data->type) { - case 0x01: /* KCS */ + switch (type) { + case IPMI_DMI_TYPE_KCS: info->si_type = SI_KCS; break; - case 0x02: /* SMIC */ + case IPMI_DMI_TYPE_SMIC: info->si_type = SI_SMIC; break; - case 0x03: /* BT */ + case IPMI_DMI_TYPE_BT: info->si_type = SI_BT; break; default: kfree(info); - return; + return -EINVAL; } - switch (ipmi_data->addr_space) { - case IPMI_MEM_ADDR_SPACE: - info->io_setup = mem_setup; - info->io.addr_type = IPMI_MEM_ADDR_SPACE; - break; - - case IPMI_IO_ADDR_SPACE: - info->io_setup = port_setup; - info->io.addr_type = IPMI_IO_ADDR_SPACE; - break; - - default: - kfree(info); - pr_warn(PFX "Unknown SMBIOS I/O Address type: %d\n", - ipmi_data->addr_space); - return; + if (!ipmi_get_info_from_resources(pdev, info)) { + rv = -EINVAL; + goto err_free; } - info->io.addr_data = ipmi_data->base_addr; - info->io.regspacing = ipmi_data->offset; - if (!info->io.regspacing) - info->io.regspacing = DEFAULT_REGSPACING; - info->io.regsize = DEFAULT_REGSPACING; - info->io.regshift = 0; - - info->slave_addr = ipmi_data->slave_addr; + rv = device_property_read_u8(&pdev->dev, "slave-addr", &slave_addr); + if (rv) { + dev_warn(&pdev->dev, "device has no slave-addr property"); + info->slave_addr = 0x20; + } else { + info->slave_addr = slave_addr; + } - info->irq = ipmi_data->irq; - if (info->irq) + info->irq = platform_get_irq(pdev, 0); + if (info->irq > 0) info->irq_setup = std_irq_setup; + else + info->irq = 0; + + info->dev = &pdev->dev; pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n", (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem", @@ -2411,21 +2381,17 @@ static void try_init_dmi(struct dmi_ipmi_data *ipmi_data) if (add_smi(info)) kfree(info); -} -static void dmi_find_bmc(void) -{ - const struct dmi_device *dev = NULL; - struct dmi_ipmi_data data; - int rv; + return 0; - while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) { - memset(&data, 0, sizeof(data)); - rv = decode_dmi((const struct dmi_header *) dev->device_data, - &data); - if (!rv) - try_init_dmi(&data); - } +err_free: + kfree(info); + return rv; +} +#else +static int dmi_ipmi_probe(struct platform_device *pdev) +{ + return -ENODEV; } #endif /* CONFIG_DMI */ @@ -2684,17 +2650,47 @@ static int of_ipmi_probe(struct platform_device *dev) #endif #ifdef CONFIG_ACPI +static int find_slave_address(struct smi_info *info, int slave_addr) +{ +#ifdef CONFIG_IPMI_DMI_DECODE + if (!slave_addr) { + int type = -1; + u32 flags = IORESOURCE_IO; + + switch (info->si_type) { + case SI_KCS: + type = IPMI_DMI_TYPE_KCS; + break; + case SI_BT: + type = IPMI_DMI_TYPE_BT; + break; + case SI_SMIC: + type = IPMI_DMI_TYPE_SMIC; + break; + } + + if (info->io.addr_type == IPMI_MEM_ADDR_SPACE) + flags = IORESOURCE_MEM; + + slave_addr = ipmi_dmi_get_slave_addr(type, flags, + info->io.addr_data); + } +#endif + + return slave_addr; +} + static int acpi_ipmi_probe(struct platform_device *dev) { struct smi_info *info; - struct resource *res, *res_second; acpi_handle handle; acpi_status status; unsigned long long tmp; + struct resource *res; int rv = -EINVAL; if (!si_tryacpi) - return 0; + return -ENODEV; handle = ACPI_HANDLE(&dev->dev); if (!handle) @@ -2734,35 +2730,11 @@ static int acpi_ipmi_probe(struct platform_device *dev) goto err_free; } - res = platform_get_resource(dev, IORESOURCE_IO, 0); - if (res) { - info->io_setup = port_setup; - info->io.addr_type = IPMI_IO_ADDR_SPACE; - } else { - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (res) { - info->io_setup = mem_setup; - info->io.addr_type = IPMI_MEM_ADDR_SPACE; - } - } + res = ipmi_get_info_from_resources(dev, info); if (!res) { - dev_err(&dev->dev, "no I/O or memory address\n"); + rv = -EINVAL; goto err_free; } - info->io.addr_data = res->start; - - info->io.regspacing = DEFAULT_REGSPACING; - res_second = platform_get_resource(dev, - (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? - IORESOURCE_IO : IORESOURCE_MEM, - 1); - if (res_second) { - if (res_second->start > info->io.addr_data) - info->io.regspacing = - res_second->start - info->io.addr_data; - } - info->io.regsize = DEFAULT_REGSPACING; - info->io.regshift = 0; /* If _GPE exists, use it; otherwise use standard interrupts */ status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp); @@ -2778,6 +2750,8 @@ static int acpi_ipmi_probe(struct platform_device *dev) } } + info->slave_addr = find_slave_address(info, info->slave_addr); + info->dev = &dev->dev; platform_set_drvdata(dev, info); @@ -2813,7 +2787,10 @@ static int ipmi_probe(struct platform_device *dev) if (of_ipmi_probe(dev) == 0) return 0; - return acpi_ipmi_probe(dev); + if (acpi_ipmi_probe(dev) == 0) + return 0; + + return dmi_ipmi_probe(dev); } static int ipmi_remove(struct platform_device *dev) @@ -3786,11 +3763,6 @@ static int init_ipmi_si(void) } #endif -#ifdef CONFIG_DMI - if (si_trydmi) - dmi_find_bmc(); -#endif - #ifdef CONFIG_ACPI if (si_tryacpi) spmi_find_bmc(); @@ -3938,6 +3910,7 @@ static void cleanup_ipmi_si(void) } module_exit(cleanup_ipmi_si); +MODULE_ALIAS("platform:dmi-ipmi-si"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Corey Minyard <minyard@mvista.com>"); MODULE_DESCRIPTION("Interface to the IPMI driver for the KCS, SMIC, and BT" diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 0b22a9be5029..0aea3bcb6158 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -53,6 +53,7 @@ #include <linux/acpi.h> #include <linux/ctype.h> #include <linux/time64.h> +#include "ipmi_dmi.h" #define PFX "ipmi_ssif: " #define DEVICE_NAME "ipmi_ssif" @@ -180,6 +181,8 @@ struct ssif_addr_info { int slave_addr; enum ipmi_addr_src addr_src; union ipmi_smi_info_union addr_info; + struct device *dev; + struct i2c_client *client; struct mutex clients_mutex; struct list_head clients; @@ -408,6 +411,7 @@ static void start_event_fetch(struct ssif_info *ssif_info, unsigned long *flags) msg = ipmi_alloc_smi_msg(); if (!msg) { ssif_info->ssif_state = SSIF_NORMAL; + ipmi_ssif_unlock_cond(ssif_info, flags); return; } @@ -430,6 +434,7 @@ static void start_recv_msg_fetch(struct ssif_info *ssif_info, msg = ipmi_alloc_smi_msg(); if (!msg) { ssif_info->ssif_state = SSIF_NORMAL; + ipmi_ssif_unlock_cond(ssif_info, flags); return; } @@ -761,6 +766,11 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, result, len, data[2]); } else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || data[1] != IPMI_GET_MSG_FLAGS_CMD) { + /* + * Don't abort here, maybe it was a queued + * response to a previous command. + */ + ipmi_ssif_unlock_cond(ssif_info, flags); pr_warn(PFX "Invalid response getting flags: %x %x\n", data[0], data[1]); } else { @@ -1094,7 +1104,7 @@ static int inc_usecount(void *send_info) { struct ssif_info *ssif_info = send_info; - if (!i2c_get_adapter(ssif_info->client->adapter->nr)) + if (!i2c_get_adapter(i2c_adapter_id(ssif_info->client->adapter))) return -ENODEV; i2c_use_client(ssif_info->client); @@ -1169,6 +1179,7 @@ static LIST_HEAD(ssif_infos); static int ssif_remove(struct i2c_client *client) { struct ssif_info *ssif_info = i2c_get_clientdata(client); + struct ssif_addr_info *addr_info; int rv; if (!ssif_info) @@ -1196,6 +1207,13 @@ static int ssif_remove(struct i2c_client *client) kthread_stop(ssif_info->thread); } + list_for_each_entry(addr_info, &ssif_infos, link) { + if (addr_info->client == client) { + addr_info->client = NULL; + break; + } + } + /* * No message can be outstanding now, we have removed the * upper layer and it permitted us to do so. @@ -1404,28 +1422,13 @@ static bool check_acpi(struct ssif_info *ssif_info, struct device *dev) static int find_slave_address(struct i2c_client *client, int slave_addr) { - struct ssif_addr_info *info; - - if (slave_addr) - return slave_addr; - - /* - * Came in without a slave address, search around to see if - * the other sources have a slave address. This lets us pick - * up an SMBIOS slave address when using ACPI. - */ - list_for_each_entry(info, &ssif_infos, link) { - if (info->binfo.addr != client->addr) - continue; - if (info->adapter_name && client->adapter->name && - strcmp_nospace(info->adapter_name, - client->adapter->name)) - continue; - if (info->slave_addr) { - slave_addr = info->slave_addr; - break; - } - } +#ifdef CONFIG_IPMI_DMI_DECODE + if (!slave_addr) + slave_addr = ipmi_dmi_get_slave_addr( + IPMI_DMI_TYPE_SSIF, + i2c_adapter_id(client->adapter), + client->addr); +#endif return slave_addr; } @@ -1447,7 +1450,6 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) u8 slave_addr = 0; struct ssif_addr_info *addr_info = NULL; - resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); if (!resp) return -ENOMEM; @@ -1468,6 +1470,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ssif_info->addr_source = addr_info->addr_src; ssif_info->ssif_debug = addr_info->debug; ssif_info->addr_info = addr_info->addr_info; + addr_info->client = client; slave_addr = addr_info->slave_addr; } } @@ -1664,7 +1667,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) { unsigned int thread_num; - thread_num = ((ssif_info->client->adapter->nr << 8) | + thread_num = ((i2c_adapter_id(ssif_info->client->adapter) + << 8) | ssif_info->client->addr); init_completion(&ssif_info->wake_thread); ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info, @@ -1705,8 +1709,19 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) } out: - if (rv) + if (rv) { + /* + * Note that if addr_info->client is assigned, we + * leave it. The i2c client hangs around even if we + * return a failure here, and the failure here is not + * propagated back to the i2c code. This seems to be + * design intent, strange as it may be. But if we + * don't leave it, ssif_platform_remove will not remove + * the client like it should. + */ + dev_err(&client->dev, "Unable to start IPMI SSIF: %d\n", rv); kfree(ssif_info); + } kfree(resp); return rv; @@ -1731,7 +1746,8 @@ static int ssif_adapter_handler(struct device *adev, void *opaque) static int new_ssif_client(int addr, char *adapter_name, int debug, int slave_addr, - enum ipmi_addr_src addr_src) + enum ipmi_addr_src addr_src, + struct device *dev) { struct ssif_addr_info *addr_info; int rv = 0; @@ -1764,6 +1780,10 @@ static int new_ssif_client(int addr, char *adapter_name, addr_info->debug = debug; addr_info->slave_addr = slave_addr; addr_info->addr_src = addr_src; + addr_info->dev = dev; + + if (dev) + dev_set_drvdata(dev, addr_info); list_add_tail(&addr_info->link, &ssif_infos); @@ -1902,7 +1922,7 @@ static int try_init_spmi(struct SPMITable *spmi) myaddr = spmi->addr.address & 0x7f; - return new_ssif_client(myaddr, NULL, 0, 0, SI_SPMI); + return new_ssif_client(myaddr, NULL, 0, 0, SI_SPMI, NULL); } static void spmi_find_bmc(void) @@ -1931,48 +1951,40 @@ static void spmi_find_bmc(void) { } #endif #ifdef CONFIG_DMI -static int decode_dmi(const struct dmi_device *dmi_dev) +static int dmi_ipmi_probe(struct platform_device *pdev) { - struct dmi_header *dm = dmi_dev->device_data; - u8 *data = (u8 *) dm; - u8 len = dm->length; - unsigned short myaddr; - int slave_addr; + u8 type, slave_addr = 0; + u16 i2c_addr; + int rv; - if (num_addrs >= MAX_SSIF_BMCS) - return -1; + if (!ssif_trydmi) + return -ENODEV; - if (len < 9) - return -1; + rv = device_property_read_u8(&pdev->dev, "ipmi-type", &type); + if (rv) + return -ENODEV; - if (data[0x04] != 4) /* Not SSIF */ - return -1; + if (type != IPMI_DMI_TYPE_SSIF) + return -ENODEV; - if ((data[8] >> 1) == 0) { - /* - * Some broken systems put the I2C address in - * the slave address field. We try to - * accommodate them here. - */ - myaddr = data[6] >> 1; - slave_addr = 0; - } else { - myaddr = data[8] >> 1; - slave_addr = data[6]; + rv = device_property_read_u16(&pdev->dev, "i2c-addr", &i2c_addr); + if (rv) { + dev_warn(&pdev->dev, PFX "No i2c-addr property\n"); + return -ENODEV; } - return new_ssif_client(myaddr, NULL, 0, slave_addr, SI_SMBIOS); -} - -static void dmi_iterator(void) -{ - const struct dmi_device *dev = NULL; + rv = device_property_read_u8(&pdev->dev, "slave-addr", &slave_addr); + if (rv) + dev_warn(&pdev->dev, "device has no slave-addr property"); - while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) - decode_dmi(dev); + return new_ssif_client(i2c_addr, NULL, 0, + slave_addr, SI_SMBIOS, &pdev->dev); } #else -static void dmi_iterator(void) { } +static int dmi_ipmi_probe(struct platform_device *pdev) +{ + return -ENODEV; +} #endif static const struct i2c_device_id ssif_id[] = { @@ -1993,6 +2005,36 @@ static struct i2c_driver ssif_i2c_driver = { .detect = ssif_detect }; +static int ssif_platform_probe(struct platform_device *dev) +{ + return dmi_ipmi_probe(dev); +} + +static int ssif_platform_remove(struct platform_device *dev) +{ + struct ssif_addr_info *addr_info = dev_get_drvdata(&dev->dev); + + if (!addr_info) + return 0; + + mutex_lock(&ssif_infos_mutex); + if (addr_info->client) + i2c_unregister_device(addr_info->client); + + list_del(&addr_info->link); + kfree(addr_info); + mutex_unlock(&ssif_infos_mutex); + return 0; +} + +static struct platform_driver ipmi_driver = { + .driver = { + .name = DEVICE_NAME, + }, + .probe = ssif_platform_probe, + .remove = ssif_platform_remove, +}; + static int init_ipmi_ssif(void) { int i; @@ -2007,7 +2049,7 @@ static int init_ipmi_ssif(void) for (i = 0; i < num_addrs; i++) { rv = new_ssif_client(addr[i], adapter_name[i], dbg[i], slave_addrs[i], - SI_HARDCODED); + SI_HARDCODED, NULL); if (rv) pr_err(PFX "Couldn't add hardcoded device at addr 0x%x\n", @@ -2017,11 +2059,16 @@ static int init_ipmi_ssif(void) if (ssif_tryacpi) ssif_i2c_driver.driver.acpi_match_table = ACPI_PTR(ssif_acpi_match); - if (ssif_trydmi) - dmi_iterator(); + if (ssif_tryacpi) spmi_find_bmc(); + if (ssif_trydmi) { + rv = platform_driver_register(&ipmi_driver); + if (rv) + pr_err(PFX "Unable to register driver: %d\n", rv); + } + ssif_i2c_driver.address_list = ssif_address_list(); rv = i2c_add_driver(&ssif_i2c_driver); @@ -2041,10 +2088,13 @@ static void cleanup_ipmi_ssif(void) i2c_del_driver(&ssif_i2c_driver); + platform_driver_unregister(&ipmi_driver); + free_ssif_clients(); } module_exit(cleanup_ipmi_ssif); +MODULE_ALIAS("platform:dmi-ipmi-ssif"); MODULE_AUTHOR("Todd C Davis <todd.c.davis@intel.com>, Corey Minyard <minyard@acm.org>"); MODULE_DESCRIPTION("IPMI driver for management controllers on a SMBus"); MODULE_LICENSE("GPL"); diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index a5c6cfe71a8e..3d832d0362a4 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -1163,10 +1163,11 @@ static int wdog_reboot_handler(struct notifier_block *this, ipmi_watchdog_state = WDOG_TIMEOUT_NONE; ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); } else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) { - /* Set a long timer to let the reboot happens, but - reboot if it hangs, but only if the watchdog + /* Set a long timer to let the reboot happen or + reset if it hangs, but only if the watchdog timer was already running. */ - timeout = 120; + if (timeout < 120) + timeout = 120; pretimeout = 0; ipmi_watchdog_state = WDOG_TIMEOUT_RESET; ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); |