diff options
Diffstat (limited to 'drivers/char/ipmi/ipmi_ssif.c')
-rw-r--r-- | drivers/char/ipmi/ipmi_ssif.c | 162 |
1 files changed, 102 insertions, 60 deletions
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 05e18041c357..61434830e641 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; @@ -1171,6 +1174,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) @@ -1198,6 +1202,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. @@ -1406,27 +1417,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 && 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; } @@ -1448,7 +1445,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; @@ -1469,6 +1465,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; } } @@ -1707,8 +1704,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; @@ -1733,7 +1741,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; @@ -1766,6 +1775,9 @@ 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; + + dev_set_drvdata(dev, addr_info); list_add_tail(&addr_info->link, &ssif_infos); @@ -1904,7 +1916,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) @@ -1933,48 +1945,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[] = { @@ -1995,6 +1999,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; @@ -2009,7 +2043,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", @@ -2019,11 +2053,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); @@ -2043,10 +2082,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"); |