Blob Blame History Raw
From bb398a4cb09a0ed96cf0fc2e90012cf6bf13a824 Mon Sep 17 00:00:00 2001
From: Corey Minyard <cminyard@mvista.com>
Date: Tue, 12 Sep 2017 21:37:02 -0500
Subject: [PATCH] ipmi_si: Change ipmi_si_add_smi() to take just I/O info
Git-commit: bb398a4cb09a0ed96cf0fc2e90012cf6bf13a824
Patch-mainline: v4.15-rc1
References: FATE#326156

Instead of allocating the smi_info structure, filling in the I/O
info, and passing it to ipmi_si_add_smi(), just pass the I/O
info in the io structure and let ipmi_si_add_smi() allocate
the smi_info structure.

This required redoing the way the remove functions for some
device interfaces worked, a new function named
ipmi_si_remove_by_dev() allows the device to be passed in and
detected instead of using driver data, which couldn't be
filled out easily othersize.

After this the platform handling should be decoupled from the
smi_info structure and that handling can be pulled out to its
own files.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
Acked-by: Takashi Iwai <tiwai@suse.de>

---
 drivers/char/ipmi/ipmi_si.h      |   5 +-
 drivers/char/ipmi/ipmi_si_intf.c | 532 +++++++++++++------------------
 drivers/char/ipmi/ipmi_si_sm.h   |   1 +
 3 files changed, 229 insertions(+), 309 deletions(-)

diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h
index e84651acd772..9573b35d73af 100644
--- a/drivers/char/ipmi/ipmi_si.h
+++ b/drivers/char/ipmi/ipmi_si.h
@@ -14,10 +14,9 @@
 #define DEFAULT_REGSPACING	1
 #define DEFAULT_REGSIZE		1
 
-struct smi_info;
-
-int ipmi_si_add_smi(struct smi_info *info);
+int ipmi_si_add_smi(struct si_sm_io *io);
 irqreturn_t ipmi_si_irq_handler(int irq, void *data);
 void ipmi_irq_start_cleanup(struct si_sm_io *io);
 int ipmi_std_irq_setup(struct si_sm_io *io);
 void ipmi_irq_finish_setup(struct si_sm_io *io);
+int ipmi_si_remove_by_dev(struct device *dev);
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index d0a0a5d9e5ff..6c2e14af8321 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -110,6 +110,8 @@ static const char * const si_to_str[] = { "kcs", "smic", "bt" };
 
 static struct platform_driver ipmi_driver;
 
+static int initialized;
+
 /*
  * Indexes into stats[] in smi_info below.
  */
@@ -282,7 +284,6 @@ struct smi_info {
 	struct task_struct *thread;
 
 	struct list_head link;
-	union ipmi_smi_info_union addr_info;
 };
 
 #define smi_inc_stat(smi, stat) \
@@ -1126,8 +1127,6 @@ static void set_need_watch(void *send_info, bool enable)
 	spin_unlock_irqrestore(&smi_info->si_lock, flags);
 }
 
-static int initialized;
-
 static void smi_timeout(unsigned long data)
 {
 	struct smi_info   *smi_info = (struct smi_info *) data;
@@ -1245,7 +1244,7 @@ static int get_smi_info(void *send_info, struct ipmi_smi_info *data)
 
 	data->addr_src = smi->io.addr_source;
 	data->dev = smi->io.dev;
-	data->addr_info = smi->addr_info;
+	data->addr_info = smi->io.addr_info;
 	get_device(smi->io.dev);
 
 	return 0;
@@ -1795,7 +1794,6 @@ static int hotmod_handler(const char *val, struct kernel_param *kp)
 	int ipmb;
 	int ival;
 	int len;
-	struct smi_info *info;
 
 	if (!str)
 		return -ENOMEM;
@@ -1890,42 +1888,30 @@ static int hotmod_handler(const char *val, struct kernel_param *kp)
 		}
 
 		if (op == HM_ADD) {
-			info = smi_info_alloc();
-			if (!info) {
-				rv = -ENOMEM;
-				goto out;
-			}
-
-			info->io.addr_source = SI_HOTMOD;
-			info->io.si_type = si_type;
-			info->io.addr_data = addr;
-			info->io.addr_type = addr_space;
-
-			info->io.addr = NULL;
-			info->io.regspacing = regspacing;
-			if (!info->io.regspacing)
-				info->io.regspacing = DEFAULT_REGSPACING;
-			info->io.regsize = regsize;
-			if (!info->io.regsize)
-				info->io.regsize = DEFAULT_REGSIZE;
-			info->io.regshift = regshift;
-			info->io.irq = irq;
-			if (info->io.irq)
-				info->io.irq_setup = ipmi_std_irq_setup;
-			info->io.slave_addr = ipmb;
-
-			rv = ipmi_si_add_smi(info);
-			if (rv) {
-				kfree(info);
+			struct si_sm_io io;
+
+			memset(&io, 0, sizeof(io));
+			io.addr_source = SI_HOTMOD;
+			io.si_type = si_type;
+			io.addr_data = addr;
+			io.addr_type = addr_space;
+
+			io.addr = NULL;
+			io.regspacing = regspacing;
+			if (!io.regspacing)
+				io.regspacing = DEFAULT_REGSPACING;
+			io.regsize = regsize;
+			if (!io.regsize)
+				io.regsize = DEFAULT_REGSIZE;
+			io.regshift = regshift;
+			io.irq = irq;
+			if (io.irq)
+				io.irq_setup = ipmi_std_irq_setup;
+			io.slave_addr = ipmb;
+
+			rv = ipmi_si_add_smi(&io);
+			if (rv)
 				goto out;
-			}
-			mutex_lock(&smi_infos_lock);
-			rv = try_smi_init(info);
-			mutex_unlock(&smi_infos_lock);
-			if (rv) {
-				cleanup_one_si(info);
-				goto out;
-			}
 		} else {
 			/* remove */
 			struct smi_info *e, *tmp_e;
@@ -1952,69 +1938,56 @@ static int hardcode_find_bmc(void)
 {
 	int ret = -ENODEV;
 	int             i;
-	struct smi_info *info;
+	struct si_sm_io io;
 
+	memset(&io, 0, sizeof(io));
 	for (i = 0; i < SI_MAX_PARMS; i++) {
 		if (!ports[i] && !addrs[i])
 			continue;
 
-		info = smi_info_alloc();
-		if (!info)
-			return -ENOMEM;
-
-		info->io.addr_source = SI_HARDCODED;
+		io.addr_source = SI_HARDCODED;
 		pr_info(PFX "probing via hardcoded address\n");
 
 		if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) {
-			info->io.si_type = SI_KCS;
+			io.si_type = SI_KCS;
 		} else if (strcmp(si_type[i], "smic") == 0) {
-			info->io.si_type = SI_SMIC;
+			io.si_type = SI_SMIC;
 		} else if (strcmp(si_type[i], "bt") == 0) {
-			info->io.si_type = SI_BT;
+			io.si_type = SI_BT;
 		} else {
 			pr_warn(PFX "Interface type specified for interface %d, was invalid: %s\n",
 				i, si_type[i]);
-			kfree(info);
 			continue;
 		}
 
 		if (ports[i]) {
 			/* An I/O port */
-			info->io.addr_data = ports[i];
-			info->io.addr_type = IPMI_IO_ADDR_SPACE;
+			io.addr_data = ports[i];
+			io.addr_type = IPMI_IO_ADDR_SPACE;
 		} else if (addrs[i]) {
 			/* A memory port */
-			info->io.addr_data = addrs[i];
-			info->io.addr_type = IPMI_MEM_ADDR_SPACE;
+			io.addr_data = addrs[i];
+			io.addr_type = IPMI_MEM_ADDR_SPACE;
 		} else {
 			pr_warn(PFX "Interface type specified for interface %d, but port and address were not set or set to zero.\n",
 				i);
-			kfree(info);
 			continue;
 		}
 
-		info->io.addr = NULL;
-		info->io.regspacing = regspacings[i];
-		if (!info->io.regspacing)
-			info->io.regspacing = DEFAULT_REGSPACING;
-		info->io.regsize = regsizes[i];
-		if (!info->io.regsize)
-			info->io.regsize = DEFAULT_REGSIZE;
-		info->io.regshift = regshifts[i];
-		info->io.irq = irqs[i];
-		if (info->io.irq)
-			info->io.irq_setup = ipmi_std_irq_setup;
-		info->io.slave_addr = slave_addrs[i];
-
-		if (!ipmi_si_add_smi(info)) {
-			mutex_lock(&smi_infos_lock);
-			if (try_smi_init(info))
-				cleanup_one_si(info);
-			mutex_unlock(&smi_infos_lock);
-			ret = 0;
-		} else {
-			kfree(info);
-		}
+		io.addr = NULL;
+		io.regspacing = regspacings[i];
+		if (!io.regspacing)
+			io.regspacing = DEFAULT_REGSPACING;
+		io.regsize = regsizes[i];
+		if (!io.regsize)
+			io.regsize = DEFAULT_REGSIZE;
+		io.regshift = regshifts[i];
+		io.irq = irqs[i];
+		if (io.irq)
+			io.irq_setup = ipmi_std_irq_setup;
+		io.slave_addr = slave_addrs[i];
+
+		ret = ipmi_si_add_smi(&io);
 	}
 	return ret;
 }
@@ -2121,88 +2094,74 @@ struct SPMITable {
 
 static int try_init_spmi(struct SPMITable *spmi)
 {
-	struct smi_info  *info;
-	int rv;
+	struct si_sm_io io;
 
 	if (spmi->IPMIlegacy != 1) {
 		pr_info(PFX "Bad SPMI legacy %d\n", spmi->IPMIlegacy);
 		return -ENODEV;
 	}
 
-	info = smi_info_alloc();
-	if (!info) {
-		pr_err(PFX "Could not allocate SI data (3)\n");
-		return -ENOMEM;
-	}
-
-	info->io.addr_source = SI_SPMI;
+	memset(&io, 0, sizeof(io));
+	io.addr_source = SI_SPMI;
 	pr_info(PFX "probing via SPMI\n");
 
 	/* Figure out the interface type. */
 	switch (spmi->InterfaceType) {
 	case 1:	/* KCS */
-		info->io.si_type = SI_KCS;
+		io.si_type = SI_KCS;
 		break;
 	case 2:	/* SMIC */
-		info->io.si_type = SI_SMIC;
+		io.si_type = SI_SMIC;
 		break;
 	case 3:	/* BT */
-		info->io.si_type = SI_BT;
+		io.si_type = SI_BT;
 		break;
 	case 4: /* SSIF, just ignore */
-		kfree(info);
 		return -EIO;
 	default:
 		pr_info(PFX "Unknown ACPI/SPMI SI type %d\n",
 			spmi->InterfaceType);
-		kfree(info);
 		return -EIO;
 	}
 
 	if (spmi->InterruptType & 1) {
 		/* We've got a GPE interrupt. */
-		info->io.irq = spmi->GPE;
-		info->io.irq_setup = acpi_gpe_irq_setup;
+		io.irq = spmi->GPE;
+		io.irq_setup = acpi_gpe_irq_setup;
 	} else if (spmi->InterruptType & 2) {
 		/* We've got an APIC/SAPIC interrupt. */
-		info->io.irq = spmi->GlobalSystemInterrupt;
-		info->io.irq_setup = ipmi_std_irq_setup;
+		io.irq = spmi->GlobalSystemInterrupt;
+		io.irq_setup = ipmi_std_irq_setup;
 	} else {
 		/* Use the default interrupt setting. */
-		info->io.irq = 0;
-		info->io.irq_setup = NULL;
+		io.irq = 0;
+		io.irq_setup = NULL;
 	}
 
 	if (spmi->addr.bit_width) {
 		/* A (hopefully) properly formed register bit width. */
-		info->io.regspacing = spmi->addr.bit_width / 8;
+		io.regspacing = spmi->addr.bit_width / 8;
 	} else {
-		info->io.regspacing = DEFAULT_REGSPACING;
+		io.regspacing = DEFAULT_REGSPACING;
 	}
-	info->io.regsize = info->io.regspacing;
-	info->io.regshift = spmi->addr.bit_offset;
+	io.regsize = io.regspacing;
+	io.regshift = spmi->addr.bit_offset;
 
 	if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
-		info->io.addr_type = IPMI_MEM_ADDR_SPACE;
+		io.addr_type = IPMI_MEM_ADDR_SPACE;
 	} else if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
-		info->io.addr_type = IPMI_IO_ADDR_SPACE;
+		io.addr_type = IPMI_IO_ADDR_SPACE;
 	} else {
-		kfree(info);
 		pr_warn(PFX "Unknown ACPI I/O Address type\n");
 		return -EIO;
 	}
-	info->io.addr_data = spmi->addr.address;
+	io.addr_data = spmi->addr.address;
 
 	pr_info("ipmi_si: SPMI: %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,
-		info->io.irq);
-
-	rv = ipmi_si_add_smi(info);
-	if (rv)
-		kfree(info);
+		(io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
+		io.addr_data, io.regsize, io.regspacing, io.irq);
 
-	return rv;
+	return ipmi_si_add_smi(&io);
 }
 
 static void spmi_find_bmc(void)
@@ -2231,36 +2190,35 @@ static void spmi_find_bmc(void)
 #if defined(CONFIG_DMI) || defined(CONFIG_ACPI)
 static struct resource *
 ipmi_get_info_from_resources(struct platform_device *pdev,
-			     struct smi_info *info)
+			     struct si_sm_io *io)
 {
 	struct resource *res, *res_second;
 
 	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
 	if (res) {
-		info->io.addr_type = IPMI_IO_ADDR_SPACE;
+		io->addr_type = IPMI_IO_ADDR_SPACE;
 	} else {
 		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 		if (res)
-			info->io.addr_type = IPMI_MEM_ADDR_SPACE;
+			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;
+	io->addr_data = res->start;
 
-	info->io.regspacing = DEFAULT_REGSPACING;
+	io->regspacing = DEFAULT_REGSPACING;
 	res_second = platform_get_resource(pdev,
-			       (info->io.addr_type == IPMI_IO_ADDR_SPACE) ?
+			       (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;
+		if (res_second->start > io->addr_data)
+			io->regspacing = res_second->start - io->addr_data;
 	}
-	info->io.regsize = DEFAULT_REGSIZE;
-	info->io.regshift = 0;
+	io->regsize = DEFAULT_REGSIZE;
+	io->regshift = 0;
 
 	return res;
 }
@@ -2270,7 +2228,7 @@ ipmi_get_info_from_resources(struct platform_device *pdev,
 #ifdef CONFIG_DMI
 static int dmi_ipmi_probe(struct platform_device *pdev)
 {
-	struct smi_info *info;
+	struct si_sm_io io;
 	u8 type, slave_addr;
 	int rv;
 
@@ -2281,31 +2239,25 @@ static int dmi_ipmi_probe(struct platform_device *pdev)
 	if (rv)
 		return -ENODEV;
 
-	info = smi_info_alloc();
-	if (!info) {
-		pr_err(PFX "Could not allocate SI data\n");
-		return -ENOMEM;
-	}
-
-	info->io.addr_source = SI_SMBIOS;
+	memset(&io, 0, sizeof(io));
+	io.addr_source = SI_SMBIOS;
 	pr_info(PFX "probing via SMBIOS\n");
 
 	switch (type) {
 	case IPMI_DMI_TYPE_KCS:
-		info->io.si_type = SI_KCS;
+		io.si_type = SI_KCS;
 		break;
 	case IPMI_DMI_TYPE_SMIC:
-		info->io.si_type = SI_SMIC;
+		io.si_type = SI_SMIC;
 		break;
 	case IPMI_DMI_TYPE_BT:
-		info->io.si_type = SI_BT;
+		io.si_type = SI_BT;
 		break;
 	default:
-		kfree(info);
 		return -EINVAL;
 	}
 
-	if (!ipmi_get_info_from_resources(pdev, info)) {
+	if (!ipmi_get_info_from_resources(pdev, &io)) {
 		rv = -EINVAL;
 		goto err_free;
 	}
@@ -2313,31 +2265,28 @@ static int dmi_ipmi_probe(struct platform_device *pdev)
 	rv = device_property_read_u8(&pdev->dev, "slave-addr", &slave_addr);
 	if (rv) {
 		dev_warn(&pdev->dev, "device has no slave-addr property");
-		info->io.slave_addr = 0x20;
+		io.slave_addr = 0x20;
 	} else {
-		info->io.slave_addr = slave_addr;
+		io.slave_addr = slave_addr;
 	}
 
-	info->io.irq = platform_get_irq(pdev, 0);
-	if (info->io.irq > 0)
-		info->io.irq_setup = ipmi_std_irq_setup;
+	io.irq = platform_get_irq(pdev, 0);
+	if (io.irq > 0)
+		io.irq_setup = ipmi_std_irq_setup;
 	else
-		info->io.irq = 0;
+		io.irq = 0;
 
-	info->io.dev = &pdev->dev;
+	io.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,
-		info->io.irq);
+		(io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
+		io.addr_data, io.regsize, io.regspacing, io.irq);
 
-	if (ipmi_si_add_smi(info))
-		kfree(info);
+	ipmi_si_add_smi(&io);
 
 	return 0;
 
 err_free:
-	kfree(info);
 	return rv;
 }
 #else
@@ -2367,30 +2316,28 @@ static void ipmi_pci_cleanup(struct si_sm_io *io)
 	pci_disable_device(pdev);
 }
 
-static int ipmi_pci_probe_regspacing(struct smi_info *info)
+static int ipmi_pci_probe_regspacing(struct si_sm_io *io)
 {
-	if (info->io.si_type == SI_KCS) {
+	if (io->si_type == SI_KCS) {
 		unsigned char	status;
 		int		regspacing;
 
-		info->io.regsize = DEFAULT_REGSIZE;
-		info->io.regshift = 0;
-		info->io.io_size = 2;
-		info->handlers = &kcs_smi_handlers;
+		io->regsize = DEFAULT_REGSIZE;
+		io->regshift = 0;
 
 		/* detect 1, 4, 16byte spacing */
 		for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) {
-			info->io.regspacing = regspacing;
-			if (info->io.io_setup(&info->io)) {
-				dev_err(info->io.dev,
+			io->regspacing = regspacing;
+			if (io->io_setup(io)) {
+				dev_err(io->dev,
 					"Could not setup I/O space\n");
 				return DEFAULT_REGSPACING;
 			}
 			/* write invalid cmd */
-			info->io.outputb(&info->io, 1, 0x10);
+			io->outputb(io, 1, 0x10);
 			/* read status back */
-			status = info->io.inputb(&info->io, 1);
-			info->io.io_cleanup(&info->io);
+			status = io->inputb(io, 1);
+			io->io_cleanup(io);
 			if (status)
 				return regspacing;
 			regspacing *= 4;
@@ -2404,30 +2351,26 @@ static int ipmi_pci_probe(struct pci_dev *pdev,
 {
 	int rv;
 	int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK;
-	struct smi_info *info;
-
-	info = smi_info_alloc();
-	if (!info)
-		return -ENOMEM;
+	struct si_sm_io io;
 
-	info->io.addr_source = SI_PCI;
+	memset(&io, 0, sizeof(io));
+	io.addr_source = SI_PCI;
 	dev_info(&pdev->dev, "probing via PCI");
 
 	switch (class_type) {
 	case PCI_ERMC_CLASSCODE_TYPE_SMIC:
-		info->io.si_type = SI_SMIC;
+		io.si_type = SI_SMIC;
 		break;
 
 	case PCI_ERMC_CLASSCODE_TYPE_KCS:
-		info->io.si_type = SI_KCS;
+		io.si_type = SI_KCS;
 		break;
 
 	case PCI_ERMC_CLASSCODE_TYPE_BT:
-		info->io.si_type = SI_BT;
+		io.si_type = SI_BT;
 		break;
 
 	default:
-		kfree(info);
 		dev_info(&pdev->dev, "Unknown IPMI type: %d\n", class_type);
 		return -ENOMEM;
 	}
@@ -2435,47 +2378,41 @@ static int ipmi_pci_probe(struct pci_dev *pdev,
 	rv = pci_enable_device(pdev);
 	if (rv) {
 		dev_err(&pdev->dev, "couldn't enable PCI device\n");
-		kfree(info);
 		return rv;
 	}
 
-	info->io.addr_source_cleanup = ipmi_pci_cleanup;
-	info->io.addr_source_data = pdev;
+	io.addr_source_cleanup = ipmi_pci_cleanup;
+	io.addr_source_data = pdev;
 
 	if (pci_resource_flags(pdev, 0) & IORESOURCE_IO)
-		info->io.addr_type = IPMI_IO_ADDR_SPACE;
+		io.addr_type = IPMI_IO_ADDR_SPACE;
 	else
-		info->io.addr_type = IPMI_MEM_ADDR_SPACE;
-	info->io.addr_data = pci_resource_start(pdev, 0);
+		io.addr_type = IPMI_MEM_ADDR_SPACE;
+	io.addr_data = pci_resource_start(pdev, 0);
 
-	info->io.regspacing = ipmi_pci_probe_regspacing(info);
-	info->io.regsize = DEFAULT_REGSIZE;
-	info->io.regshift = 0;
+	io.regspacing = ipmi_pci_probe_regspacing(&io);
+	io.regsize = DEFAULT_REGSIZE;
+	io.regshift = 0;
 
-	info->io.irq = pdev->irq;
-	if (info->io.irq)
-		info->io.irq_setup = ipmi_std_irq_setup;
+	io.irq = pdev->irq;
+	if (io.irq)
+		io.irq_setup = ipmi_std_irq_setup;
 
-	info->io.dev = &pdev->dev;
-	pci_set_drvdata(pdev, info);
+	io.dev = &pdev->dev;
 
 	dev_info(&pdev->dev, "%pR regsize %d spacing %d irq %d\n",
-		&pdev->resource[0], info->io.regsize, info->io.regspacing,
-		info->io.irq);
+		&pdev->resource[0], io.regsize, io.regspacing, io.irq);
 
-	rv = ipmi_si_add_smi(info);
-	if (rv) {
-		kfree(info);
+	rv = ipmi_si_add_smi(&io);
+	if (rv)
 		pci_disable_device(pdev);
-	}
 
 	return rv;
 }
 
 static void ipmi_pci_remove(struct pci_dev *pdev)
 {
-	struct smi_info *info = pci_get_drvdata(pdev);
-	cleanup_one_si(info);
+	ipmi_si_remove_by_dev(&pdev->dev);
 }
 
 static const struct pci_device_id ipmi_pci_devices[] = {
@@ -2508,7 +2445,7 @@ MODULE_DEVICE_TABLE(of, of_ipmi_match);
 static int of_ipmi_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *match;
-	struct smi_info *info;
+	struct si_sm_io io;
 	struct resource resource;
 	const __be32 *regsize, *regspacing, *regshift;
 	struct device_node *np = pdev->dev.of_node;
@@ -2548,44 +2485,29 @@ static int of_ipmi_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 
-	info = smi_info_alloc();
-
-	if (!info) {
-		dev_err(&pdev->dev,
-			"could not allocate memory for OF probe\n");
-		return -ENOMEM;
-	}
-
-	info->io.si_type	= (enum si_type) match->data;
-	info->io.addr_source	= SI_DEVICETREE;
-	info->io.irq_setup	= ipmi_std_irq_setup;
+	memset(&io, 0, sizeof(io));
+	io.si_type	= (enum si_type) match->data;
+	io.addr_source	= SI_DEVICETREE;
+	io.irq_setup	= ipmi_std_irq_setup;
 
 	if (resource.flags & IORESOURCE_IO)
-		info->io.addr_type = IPMI_IO_ADDR_SPACE;
+		io.addr_type = IPMI_IO_ADDR_SPACE;
 	else
-		info->io.addr_type = IPMI_MEM_ADDR_SPACE;
+		io.addr_type = IPMI_MEM_ADDR_SPACE;
 
-	info->io.addr_data	= resource.start;
+	io.addr_data	= resource.start;
 
-	info->io.regsize	= regsize ? be32_to_cpup(regsize) : DEFAULT_REGSIZE;
-	info->io.regspacing	= regspacing ? be32_to_cpup(regspacing) : DEFAULT_REGSPACING;
-	info->io.regshift	= regshift ? be32_to_cpup(regshift) : 0;
+	io.regsize	= regsize ? be32_to_cpup(regsize) : DEFAULT_REGSIZE;
+	io.regspacing	= regspacing ? be32_to_cpup(regspacing) : DEFAULT_REGSPACING;
+	io.regshift	= regshift ? be32_to_cpup(regshift) : 0;
 
-	info->io.irq		= irq_of_parse_and_map(pdev->dev.of_node, 0);
-	info->io.dev		= &pdev->dev;
+	io.irq		= irq_of_parse_and_map(pdev->dev.of_node, 0);
+	io.dev		= &pdev->dev;
 
 	dev_dbg(&pdev->dev, "addr 0x%lx regsize %d spacing %d irq %d\n",
-		info->io.addr_data, info->io.regsize, info->io.regspacing,
-		info->io.irq);
-
-	dev_set_drvdata(&pdev->dev, info);
+		io.addr_data, io.regsize, io.regspacing, io.irq);
 
-	ret = ipmi_si_add_smi(info);
-	if (ret) {
-		kfree(info);
-		return ret;
-	}
-	return 0;
+	return ipmi_si_add_smi(&io);
 }
 #else
 #define of_ipmi_match NULL
@@ -2596,14 +2518,14 @@ 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)
+static int find_slave_address(struct si_sm_io *io, int slave_addr)
 {
 #ifdef CONFIG_IPMI_DMI_DECODE
 	if (!slave_addr) {
 		int type = -1;
 		u32 flags = IORESOURCE_IO;
 
-		switch (info->io.si_type) {
+		switch (io->si_type) {
 		case SI_KCS:
 			type = IPMI_DMI_TYPE_KCS;
 			break;
@@ -2615,11 +2537,11 @@ static int find_slave_address(struct smi_info *info, int slave_addr)
 			break;
 		}
 
-		if (info->io.addr_type == IPMI_MEM_ADDR_SPACE)
+		if (io->addr_type == IPMI_MEM_ADDR_SPACE)
 			flags = IORESOURCE_MEM;
 
 		slave_addr = ipmi_dmi_get_slave_addr(type, flags,
-						     info->io.addr_data);
+						     io->addr_data);
 	}
 #endif
 
@@ -2628,7 +2550,7 @@ static int find_slave_address(struct smi_info *info, int slave_addr)
 
 static int acpi_ipmi_probe(struct platform_device *pdev)
 {
-	struct smi_info *info;
+	struct si_sm_io io;
 	acpi_handle handle;
 	acpi_status status;
 	unsigned long long tmp;
@@ -2642,14 +2564,11 @@ static int acpi_ipmi_probe(struct platform_device *pdev)
 	if (!handle)
 		return -ENODEV;
 
-	info = smi_info_alloc();
-	if (!info)
-		return -ENOMEM;
-
-	info->io.addr_source = SI_ACPI;
+	memset(&io, 0, sizeof(io));
+	io.addr_source = SI_ACPI;
 	dev_info(&pdev->dev, PFX "probing via ACPI\n");
 
-	info->addr_info.acpi_info.acpi_handle = handle;
+	io.addr_info.acpi_info.acpi_handle = handle;
 
 	/* _IFT tells us the interface type: KCS, BT, etc */
 	status = acpi_evaluate_integer(handle, "_IFT", NULL, &tmp);
@@ -2661,13 +2580,13 @@ static int acpi_ipmi_probe(struct platform_device *pdev)
 
 	switch (tmp) {
 	case 1:
-		info->io.si_type = SI_KCS;
+		io.si_type = SI_KCS;
 		break;
 	case 2:
-		info->io.si_type = SI_SMIC;
+		io.si_type = SI_SMIC;
 		break;
 	case 3:
-		info->io.si_type = SI_BT;
+		io.si_type = SI_BT;
 		break;
 	case 4: /* SSIF, just ignore */
 		rv = -ENODEV;
@@ -2677,7 +2596,7 @@ static int acpi_ipmi_probe(struct platform_device *pdev)
 		goto err_free;
 	}
 
-	res = ipmi_get_info_from_resources(pdev, info);
+	res = ipmi_get_info_from_resources(pdev, &io);
 	if (!res) {
 		rv = -EINVAL;
 		goto err_free;
@@ -2686,34 +2605,27 @@ static int acpi_ipmi_probe(struct platform_device *pdev)
 	/* If _GPE exists, use it; otherwise use standard interrupts */
 	status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
 	if (ACPI_SUCCESS(status)) {
-		info->io.irq = tmp;
-		info->io.irq_setup = acpi_gpe_irq_setup;
+		io.irq = tmp;
+		io.irq_setup = acpi_gpe_irq_setup;
 	} else {
 		int irq = platform_get_irq(pdev, 0);
 
 		if (irq > 0) {
-			info->io.irq = irq;
-			info->io.irq_setup = ipmi_std_irq_setup;
+			io.irq = irq;
+			io.irq_setup = ipmi_std_irq_setup;
 		}
 	}
 
-	info->io.slave_addr = find_slave_address(info, info->io.slave_addr);
-
-	info->io.dev = &pdev->dev;
-	platform_set_drvdata(pdev, info);
+	io.slave_addr = find_slave_address(&io, io.slave_addr);
 
-	dev_info(info->io.dev, "%pR regsize %d spacing %d irq %d\n",
-		 res, info->io.regsize, info->io.regspacing,
-		 info->io.irq);
+	io.dev = &pdev->dev;
 
-	rv = ipmi_si_add_smi(info);
-	if (rv)
-		kfree(info);
+	dev_info(io.dev, "%pR regsize %d spacing %d irq %d\n",
+		 res, io.regsize, io.regspacing, io.irq);
 
-	return rv;
+	return ipmi_si_add_smi(&io);
 
 err_free:
-	kfree(info);
 	return rv;
 }
 
@@ -2742,10 +2654,7 @@ static int ipmi_probe(struct platform_device *pdev)
 
 static int ipmi_remove(struct platform_device *pdev)
 {
-	struct smi_info *info = dev_get_drvdata(&pdev->dev);
-
-	cleanup_one_si(info);
-	return 0;
+	return ipmi_si_remove_by_dev(&pdev->dev);
 }
 
 static struct platform_driver ipmi_driver = {
@@ -2761,45 +2670,27 @@ static struct platform_driver ipmi_driver = {
 #ifdef CONFIG_PARISC
 static int __init ipmi_parisc_probe(struct parisc_device *dev)
 {
-	struct smi_info *info;
-	int rv;
-
-	info = smi_info_alloc();
-
-	if (!info) {
-		dev_err(&dev->dev,
-			"could not allocate memory for PARISC probe\n");
-		return -ENOMEM;
-	}
-
-	info->io.si_type	= SI_KCS;
-	info->io.addr_source	= SI_DEVICETREE;
-	info->io.addr_type	= IPMI_MEM_ADDR_SPACE;
-	info->io.addr_data	= dev->hpa.start;
-	info->io.regsize	= 1;
-	info->io.regspacing	= 1;
-	info->io.regshift	= 0;
-	info->io.irq		= 0; /* no interrupt */
-	info->io.irq_setup	= NULL;
-	info->io.dev		= &dev->dev;
-
-	dev_dbg(&dev->dev, "addr 0x%lx\n", info->io.addr_data);
+	struct si_sm_io io;
 
-	dev_set_drvdata(&dev->dev, info);
+	io.si_type	= SI_KCS;
+	io.addr_source	= SI_DEVICETREE;
+	io.addr_type	= IPMI_MEM_ADDR_SPACE;
+	io.addr_data	= dev->hpa.start;
+	io.regsize	= 1;
+	io.regspacing	= 1;
+	io.regshift	= 0;
+	io.irq		= 0; /* no interrupt */
+	io.irq_setup	= NULL;
+	io.dev		= &dev->dev;
 
-	rv = ipmi_si_add_smi(info);
-	if (rv) {
-		kfree(info);
-		return rv;
-	}
+	dev_dbg(&dev->dev, "addr 0x%lx\n", io.addr_data);
 
-	return 0;
+	return ipmi_si_add_smi(&io);
 }
 
 static int __exit ipmi_parisc_remove(struct parisc_device *dev)
 {
-	cleanup_one_si(dev_get_drvdata(&dev->dev));
-	return 0;
+	return ipmi_si_remove_by_dev(&pdev->dev);
 }
 
 static const struct parisc_device_id ipmi_parisc_tbl[] __initconst = {
@@ -3393,21 +3284,27 @@ static struct smi_info *find_dup_si(struct smi_info *info)
 	return NULL;
 }
 
-int ipmi_si_add_smi(struct smi_info *new_smi)
+int ipmi_si_add_smi(struct si_sm_io *io)
 {
 	int rv = 0;
-	struct smi_info *dup;
+	struct smi_info *new_smi, *dup;
 
-	if (!new_smi->io.io_setup) {
-		if (new_smi->io.addr_type == IPMI_IO_ADDR_SPACE) {
-			new_smi->io.io_setup = port_setup;
-		} else if (new_smi->io.addr_type == IPMI_MEM_ADDR_SPACE) {
-			new_smi->io.io_setup = mem_setup;
+	if (!io->io_setup) {
+		if (io->addr_type == IPMI_IO_ADDR_SPACE) {
+			io->io_setup = port_setup;
+		} else if (io->addr_type == IPMI_MEM_ADDR_SPACE) {
+			io->io_setup = mem_setup;
 		} else {
 			return -EINVAL;
 		}
 	}
 
+	new_smi = smi_info_alloc();
+	if (!new_smi)
+		return -ENOMEM;
+
+	new_smi->io = *io;
+
 	mutex_lock(&smi_infos_lock);
 	dup = find_dup_si(new_smi);
 	if (dup) {
@@ -3439,6 +3336,14 @@ int ipmi_si_add_smi(struct smi_info *new_smi)
 
 	list_add_tail(&new_smi->link, &smi_infos);
 
+	if (initialized) {
+		rv = try_smi_init(new_smi);
+		if (rv) {
+			mutex_unlock(&smi_infos_lock);
+			cleanup_one_si(new_smi);
+			return rv;
+		}
+	}
 out_err:
 	mutex_unlock(&smi_infos_lock);
 	return rv;
@@ -3695,7 +3600,6 @@ static int init_ipmi_si(void)
 
 	if (initialized)
 		return 0;
-	initialized = 1;
 
 	if (si_tryplatform) {
 		rv = platform_driver_register(&ipmi_driver);
@@ -3764,10 +3668,8 @@ static int init_ipmi_si(void)
 	}
 
 	/* type will only have been set if we successfully registered an si */
-	if (type) {
-		mutex_unlock(&smi_infos_lock);
-		return 0;
-	}
+	if (type)
+		goto skip_fallback_noirq;
 
 	/* Fall back to the preferred device */
 
@@ -3778,6 +3680,9 @@ static int init_ipmi_si(void)
 			}
 		}
 	}
+
+skip_fallback_noirq:
+	initialized = 1;
 	mutex_unlock(&smi_infos_lock);
 
 	if (type)
@@ -3814,9 +3719,6 @@ static void cleanup_one_si(struct smi_info *to_clean)
 		}
 	}
 
-	if (to_clean->io.dev)
-		dev_set_drvdata(to_clean->io.dev, NULL);
-
 	list_del(&to_clean->link);
 
 	/*
@@ -3859,6 +3761,24 @@ static void cleanup_one_si(struct smi_info *to_clean)
 	kfree(to_clean);
 }
 
+int ipmi_si_remove_by_dev(struct device *dev)
+{
+	struct smi_info *e;
+	int rv = -ENOENT;
+
+	mutex_lock(&smi_infos_lock);
+	list_for_each_entry(e, &smi_infos, link) {
+		if (e->io.dev == dev) {
+			cleanup_one_si(e);
+			rv = 0;
+			break;
+		}
+	}
+	mutex_unlock(&smi_infos_lock);
+
+	return rv;
+}
+
 static void cleanup_ipmi_si(void)
 {
 	struct smi_info *e, *tmp_e;
diff --git a/drivers/char/ipmi/ipmi_si_sm.h b/drivers/char/ipmi/ipmi_si_sm.h
index 9df77c664908..fbf5bfccde2e 100644
--- a/drivers/char/ipmi/ipmi_si_sm.h
+++ b/drivers/char/ipmi/ipmi_si_sm.h
@@ -70,6 +70,7 @@ struct si_sm_io {
 	enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */
 	void (*addr_source_cleanup)(struct si_sm_io *io);
 	void *addr_source_data;
+	union ipmi_smi_info_union addr_info;
 
 	int (*io_setup)(struct si_sm_io *info);
 	void (*io_cleanup)(struct si_sm_io *info);
-- 
2.19.2