From: Corey Minyard <[email protected]> Now that the DMI code creates a platform device for IPMI devices in the firmware, use that instead of handling all the DMI work in the IPMI driver itself.
Signed-off-by: Corey Minyard <[email protected]> Tested-by: Andy Lutomirski <[email protected]> --- drivers/char/ipmi/ipmi_si_intf.c | 143 ++++++++------------------------------- drivers/char/ipmi/ipmi_ssif.c | 119 +++++++++++++++++++++----------- 2 files changed, 109 insertions(+), 153 deletions(-) diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 1e25b52..866539f 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2271,121 +2271,45 @@ 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) -{ - const u8 *data = (const u8 *)dm; - unsigned long base_addr; - u8 reg_spacing; - u8 len = dm->length; - - dmi->type = data[4]; - - 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; - } - } 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; - } - - dmi->slave_addr = data[6]; - - return 0; -} - -static void try_init_dmi(struct dmi_ipmi_data *ipmi_data) +static int dmi_ipmi_probe(struct platform_device *pdev) { + struct dmi_device *dmi_dev = to_dmi_device(pdev->dev.fwnode); + struct dmi_dev_ipmi *ipmi_data = to_dmi_dev_ipmi(dmi_dev); struct smi_info *info; + if (!si_trydmi || !ipmi_data) + return -ENODEV; + info = smi_info_alloc(); if (!info) { - printk(KERN_ERR PFX "Could not allocate SI data\n"); - return; + pr_err(PFX "Could not allocate SI data\n"); + return -ENOMEM; } info->addr_source = SI_SMBIOS; - printk(KERN_INFO PFX "probing via SMBIOS\n"); + pr_info(PFX "probing via SMBIOS\n"); switch (ipmi_data->type) { - case 0x01: /* KCS */ + 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: + if (ipmi_data->is_io_space) { info->io_setup = port_setup; info->io.addr_type = IPMI_IO_ADDR_SPACE; - break; - - default: - kfree(info); - printk(KERN_WARNING PFX "Unknown SMBIOS I/O Address type: %d\n", - ipmi_data->addr_space); - return; + } else { + info->io_setup = mem_setup; + info->io.addr_type = IPMI_MEM_ADDR_SPACE; } info->io.addr_data = ipmi_data->base_addr; @@ -2401,6 +2325,8 @@ static void try_init_dmi(struct dmi_ipmi_data *ipmi_data) if (info->irq) info->irq_setup = std_irq_setup; + 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", info->io.addr_data, info->io.regsize, info->io.regspacing, @@ -2408,21 +2334,13 @@ static void try_init_dmi(struct dmi_ipmi_data *ipmi_data) if (add_smi(info)) kfree(info); -} -static void dmi_find_bmc(void) + return 0; +} +#else +static int dmi_ipmi_probe(struct platform_device *pdev) { - const struct dmi_device *dev = NULL; - struct dmi_ipmi_data data; - int rv; - - 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); - } + return -ENODEV; } #endif /* CONFIG_DMI */ @@ -2810,7 +2728,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) @@ -3527,7 +3448,7 @@ static int add_smi(struct smi_info *new_smi) si_to_str[new_smi->si_type]); mutex_lock(&smi_infos_lock); if (!is_new_interface(new_smi)) { - printk(KERN_CONT " duplicate interface\n"); + printk(KERN_CONT ": duplicate interface\n"); rv = -EBUSY; goto out_err; } @@ -3818,11 +3739,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(); @@ -3983,6 +3899,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 <[email protected]>"); 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 8b3be8b..a8448705 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -181,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; @@ -1439,6 +1441,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; } } @@ -1675,8 +1678,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; @@ -1701,7 +1715,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; @@ -1731,9 +1746,14 @@ static int new_ssif_client(int addr, char *adapter_name, sizeof(addr_info->binfo.type)); addr_info->binfo.addr = addr; addr_info->binfo.platform_data = addr_info; + if (dev) + addr_info->binfo.fwnode = dev->fwnode; addr_info->debug = debug; addr_info->slave_addr = slave_addr; addr_info->addr_src = addr_src; + addr_info->dev = dev; + + dev_set_drvdata(dev, addr_info); list_add_tail(&addr_info->link, &ssif_infos); @@ -1872,7 +1892,7 @@ static int try_init_spmi(struct SPMITable *spmi) myaddr = spmi->addr.address >> 1; - 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) @@ -1901,48 +1921,30 @@ 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; - - if (num_addrs >= MAX_SSIF_BMCS) - return -1; + struct dmi_device *dmi_dev = to_dmi_device(pdev->dev.fwnode); + struct dmi_dev_ipmi *ipmi_data = to_dmi_dev_ipmi(dmi_dev); - if (len < 9) - return -1; - - if (data[0x04] != 4) /* Not SSIF */ - return -1; + if (!ssif_trydmi || !ipmi_data) + 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]; + if (!ipmi_data->good_data) { + pr_err(PFX "DMI data for this device was invalid.\n"); + return -EINVAL; } - return new_ssif_client(myaddr, NULL, 0, 0, SI_SMBIOS); -} - -static void dmi_iterator(void) -{ - const struct dmi_device *dev = NULL; + if (ipmi_data->type != IPMI_DMI_TYPE_SSIF) + return -ENODEV; - while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) - decode_dmi(dev); + return new_ssif_client(ipmi_data->base_addr, NULL, 0, + ipmi_data->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[] = { @@ -1963,6 +1965,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; @@ -1977,7 +2009,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", @@ -1987,8 +2019,7 @@ 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(); @@ -1998,6 +2029,11 @@ static int init_ipmi_ssif(void) if (!rv) initialized = true; + /* Wait until here so ACPI devices will start first. */ + rv = platform_driver_register(&ipmi_driver); + if (rv) + pr_err(PFX "Unable to register driver: %d\n", rv); + return rv; } module_init(init_ipmi_ssif); @@ -2009,12 +2045,15 @@ static void cleanup_ipmi_ssif(void) initialized = false; + platform_driver_unregister(&ipmi_driver); + i2c_del_driver(&ssif_i2c_driver); free_ssif_clients(); } module_exit(cleanup_ipmi_ssif); +MODULE_ALIAS("platform:dmi-ipmi-ssif"); MODULE_AUTHOR("Todd C Davis <[email protected]>, Corey Minyard <[email protected]>"); MODULE_DESCRIPTION("IPMI driver for management controllers on a SMBus"); MODULE_LICENSE("GPL"); -- 2.7.4 ------------------------------------------------------------------------------ Find and fix application performance issues faster with Applications Manager Applications Manager provides deep performance insights into multiple tiers of your business applications. It resolves application problems quickly and reduces your MTTR. Get your free trial! https://ad.doubleclick.net/ddm/clk/302982198;130105516;z _______________________________________________ Openipmi-developer mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openipmi-developer
