aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-07-13 18:07:02 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-07-13 18:07:02 -0400
commit92adeb616c9172dea9678f53ea6e5501fc4f4338 (patch)
tree7df0079de88ac11852908588b7c3551fd0c4f35f
parent43c95d3694cc448fdf50bd53b7ff3a5bb4655883 (diff)
parentac499fba98c3c65078fd84fa0a62cd6f6d5837ed (diff)
Merge tag 'for-linus-5.3' of git://github.com/cminyard/linux-ipmi
Pull IPMI updates from Corey Minyard: "Some small fixes for various things, nothing huge, mostly found by automated tools. Plus add a driver that allows Linux to act as an IPMB slave device, so it can be a satellite MC in an IPMI network" * tag 'for-linus-5.3' of git://github.com/cminyard/linux-ipmi: docs: ipmb: place it at driver-api and convert to ReST fix platform_no_drv_owner.cocci warnings ipmi: ipmb: don't allocate i2c_client on stack ipmi: ipmb: Fix build error while CONFIG_I2C is set to m Add support for IPMB driver drivers: ipmi: Drop device reference ipmi_ssif: fix unexpected driver unregister warning ipmi_si: use bool type for initialized variable ipmi_si: fix unexpected driver unregister warning
-rw-r--r--Documentation/driver-api/index.rst1
-rw-r--r--Documentation/driver-api/ipmb.rst105
-rw-r--r--drivers/char/ipmi/Kconfig9
-rw-r--r--drivers/char/ipmi/Makefile1
-rw-r--r--drivers/char/ipmi/ipmb_dev_int.c364
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c4
-rw-r--r--drivers/char/ipmi/ipmi_si_platform.c7
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c5
8 files changed, 492 insertions, 4 deletions
diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst
index 0dbaa987aa11..6cd750a03ea0 100644
--- a/Documentation/driver-api/index.rst
+++ b/Documentation/driver-api/index.rst
@@ -34,6 +34,7 @@ available subsections can be seen below.
34 pci/index 34 pci/index
35 spi 35 spi
36 i2c 36 i2c
37 ipmb
37 i3c/index 38 i3c/index
38 hsi 39 hsi
39 edac 40 edac
diff --git a/Documentation/driver-api/ipmb.rst b/Documentation/driver-api/ipmb.rst
new file mode 100644
index 000000000000..7e2265144157
--- /dev/null
+++ b/Documentation/driver-api/ipmb.rst
@@ -0,0 +1,105 @@
1==============================
2IPMB Driver for a Satellite MC
3==============================
4
5The Intelligent Platform Management Bus or IPMB, is an
6I2C bus that provides a standardized interconnection between
7different boards within a chassis. This interconnection is
8between the baseboard management (BMC) and chassis electronics.
9IPMB is also associated with the messaging protocol through the
10IPMB bus.
11
12The devices using the IPMB are usually management
13controllers that perform management functions such as servicing
14the front panel interface, monitoring the baseboard,
15hot-swapping disk drivers in the system chassis, etc...
16
17When an IPMB is implemented in the system, the BMC serves as
18a controller to give system software access to the IPMB. The BMC
19sends IPMI requests to a device (usually a Satellite Management
20Controller or Satellite MC) via IPMB and the device
21sends a response back to the BMC.
22
23For more information on IPMB and the format of an IPMB message,
24refer to the IPMB and IPMI specifications.
25
26IPMB driver for Satellite MC
27----------------------------
28
29ipmb-dev-int - This is the driver needed on a Satellite MC to
30receive IPMB messages from a BMC and send a response back.
31This driver works with the I2C driver and a userspace
32program such as OpenIPMI:
33
341) It is an I2C slave backend driver. So, it defines a callback
35 function to set the Satellite MC as an I2C slave.
36 This callback function handles the received IPMI requests.
37
382) It defines the read and write functions to enable a user
39 space program (such as OpenIPMI) to communicate with the kernel.
40
41
42Load the IPMB driver
43--------------------
44
45The driver needs to be loaded at boot time or manually first.
46First, make sure you have the following in your config file:
47CONFIG_IPMB_DEVICE_INTERFACE=y
48
491) If you want the driver to be loaded at boot time:
50
51a) Add this entry to your ACPI table, under the appropriate SMBus::
52
53 Device (SMB0) // Example SMBus host controller
54 {
55 Name (_HID, "<Vendor-Specific HID>") // Vendor-Specific HID
56 Name (_UID, 0) // Unique ID of particular host controller
57 :
58 :
59 Device (IPMB)
60 {
61 Name (_HID, "IPMB0001") // IPMB device interface
62 Name (_UID, 0) // Unique device identifier
63 }
64 }
65
66b) Example for device tree::
67
68 &i2c2 {
69 status = "okay";
70
71 ipmb@10 {
72 compatible = "ipmb-dev";
73 reg = <0x10>;
74 };
75 };
76
772) Manually from Linux::
78
79 modprobe ipmb-dev-int
80
81
82Instantiate the device
83----------------------
84
85After loading the driver, you can instantiate the device as
86described in 'Documentation/i2c/instantiating-devices'.
87If you have multiple BMCs, each connected to your Satellite MC via
88a different I2C bus, you can instantiate a device for each of
89those BMCs.
90
91The name of the instantiated device contains the I2C bus number
92associated with it as follows::
93
94 BMC1 ------ IPMB/I2C bus 1 ---------| /dev/ipmb-1
95 Satellite MC
96 BMC1 ------ IPMB/I2C bus 2 ---------| /dev/ipmb-2
97
98For instance, you can instantiate the ipmb-dev-int device from
99user space at the 7 bit address 0x10 on bus 2::
100
101 # echo ipmb-dev 0x1010 > /sys/bus/i2c/devices/i2c-2/new_device
102
103This will create the device file /dev/ipmb-2, which can be accessed
104by the user space program. The device needs to be instantiated
105before running the user space program.
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index caac5d24baa4..4bad0614109b 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -132,3 +132,12 @@ config ASPEED_BT_IPMI_BMC
132 Provides a driver for the BT (Block Transfer) IPMI interface 132 Provides a driver for the BT (Block Transfer) IPMI interface
133 found on Aspeed SOCs (AST2400 and AST2500). The driver 133 found on Aspeed SOCs (AST2400 and AST2500). The driver
134 implements the BMC side of the BT interface. 134 implements the BMC side of the BT interface.
135
136config IPMB_DEVICE_INTERFACE
137 tristate 'IPMB Interface handler'
138 depends on I2C
139 depends on I2C_SLAVE
140 help
141 Provides a driver for a device (Satellite MC) to
142 receive requests and send responses back to the BMC via
143 the IPMB interface. This module requires I2C support.
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index 3f06b2062475..0822adc2ec41 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
26obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o 26obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
27obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o 27obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
28obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o 28obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o
29obj-$(CONFIG_IPMB_DEVICE_INTERFACE) += ipmb_dev_int.o
diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c
new file mode 100644
index 000000000000..57204335c5f5
--- /dev/null
+++ b/drivers/char/ipmi/ipmb_dev_int.c
@@ -0,0 +1,364 @@
1// SPDX-License-Identifier: GPL-2.0
2
3/*
4 * IPMB driver to receive a request and send a response
5 *
6 * Copyright (C) 2019 Mellanox Techologies, Ltd.
7 *
8 * This was inspired by Brendan Higgins' ipmi-bmc-bt-i2c driver.
9 */
10
11#include <linux/acpi.h>
12#include <linux/errno.h>
13#include <linux/i2c.h>
14#include <linux/miscdevice.h>
15#include <linux/module.h>
16#include <linux/mutex.h>
17#include <linux/poll.h>
18#include <linux/slab.h>
19#include <linux/spinlock.h>
20#include <linux/wait.h>
21
22#define MAX_MSG_LEN 128
23#define IPMB_REQUEST_LEN_MIN 7
24#define NETFN_RSP_BIT_MASK 0x4
25#define REQUEST_QUEUE_MAX_LEN 256
26
27#define IPMB_MSG_LEN_IDX 0
28#define RQ_SA_8BIT_IDX 1
29#define NETFN_LUN_IDX 2
30
31#define GET_7BIT_ADDR(addr_8bit) (addr_8bit >> 1)
32#define GET_8BIT_ADDR(addr_7bit) ((addr_7bit << 1) & 0xff)
33
34#define IPMB_MSG_PAYLOAD_LEN_MAX (MAX_MSG_LEN - IPMB_REQUEST_LEN_MIN - 1)
35
36#define SMBUS_MSG_HEADER_LENGTH 2
37#define SMBUS_MSG_IDX_OFFSET (SMBUS_MSG_HEADER_LENGTH + 1)
38
39struct ipmb_msg {
40 u8 len;
41 u8 rs_sa;
42 u8 netfn_rs_lun;
43 u8 checksum1;
44 u8 rq_sa;
45 u8 rq_seq_rq_lun;
46 u8 cmd;
47 u8 payload[IPMB_MSG_PAYLOAD_LEN_MAX];
48 /* checksum2 is included in payload */
49} __packed;
50
51struct ipmb_request_elem {
52 struct list_head list;
53 struct ipmb_msg request;
54};
55
56struct ipmb_dev {
57 struct i2c_client *client;
58 struct miscdevice miscdev;
59 struct ipmb_msg request;
60 struct list_head request_queue;
61 atomic_t request_queue_len;
62 size_t msg_idx;
63 spinlock_t lock;
64 wait_queue_head_t wait_queue;
65 struct mutex file_mutex;
66};
67
68static inline struct ipmb_dev *to_ipmb_dev(struct file *file)
69{
70 return container_of(file->private_data, struct ipmb_dev, miscdev);
71}
72
73static ssize_t ipmb_read(struct file *file, char __user *buf, size_t count,
74 loff_t *ppos)
75{
76 struct ipmb_dev *ipmb_dev = to_ipmb_dev(file);
77 struct ipmb_request_elem *queue_elem;
78 struct ipmb_msg msg;
79 ssize_t ret;
80
81 memset(&msg, 0, sizeof(msg));
82
83 spin_lock_irq(&ipmb_dev->lock);
84
85 while (list_empty(&ipmb_dev->request_queue)) {
86 spin_unlock_irq(&ipmb_dev->lock);
87
88 if (file->f_flags & O_NONBLOCK)
89 return -EAGAIN;
90
91 ret = wait_event_interruptible(ipmb_dev->wait_queue,
92 !list_empty(&ipmb_dev->request_queue));
93 if (ret)
94 return ret;
95
96 spin_lock_irq(&ipmb_dev->lock);
97 }
98
99 queue_elem = list_first_entry(&ipmb_dev->request_queue,
100 struct ipmb_request_elem, list);
101 memcpy(&msg, &queue_elem->request, sizeof(msg));
102 list_del(&queue_elem->list);
103 kfree(queue_elem);
104 atomic_dec(&ipmb_dev->request_queue_len);
105
106 spin_unlock_irq(&ipmb_dev->lock);
107
108 count = min_t(size_t, count, msg.len + 1);
109 if (copy_to_user(buf, &msg, count))
110 ret = -EFAULT;
111
112 return ret < 0 ? ret : count;
113}
114
115static ssize_t ipmb_write(struct file *file, const char __user *buf,
116 size_t count, loff_t *ppos)
117{
118 struct ipmb_dev *ipmb_dev = to_ipmb_dev(file);
119 u8 rq_sa, netf_rq_lun, msg_len;
120 union i2c_smbus_data data;
121 u8 msg[MAX_MSG_LEN];
122 ssize_t ret;
123
124 if (count > sizeof(msg))
125 return -EINVAL;
126
127 if (copy_from_user(&msg, buf, count))
128 return -EFAULT;
129
130 if (count < msg[0])
131 return -EINVAL;
132
133 rq_sa = GET_7BIT_ADDR(msg[RQ_SA_8BIT_IDX]);
134 netf_rq_lun = msg[NETFN_LUN_IDX];
135
136 if (!(netf_rq_lun & NETFN_RSP_BIT_MASK))
137 return -EINVAL;
138
139 /*
140 * subtract rq_sa and netf_rq_lun from the length of the msg passed to
141 * i2c_smbus_xfer
142 */
143 msg_len = msg[IPMB_MSG_LEN_IDX] - SMBUS_MSG_HEADER_LENGTH;
144 if (msg_len > I2C_SMBUS_BLOCK_MAX)
145 msg_len = I2C_SMBUS_BLOCK_MAX;
146
147 data.block[0] = msg_len;
148 memcpy(&data.block[1], msg + SMBUS_MSG_IDX_OFFSET, msg_len);
149 ret = i2c_smbus_xfer(ipmb_dev->client->adapter, rq_sa,
150 ipmb_dev->client->flags,
151 I2C_SMBUS_WRITE, netf_rq_lun,
152 I2C_SMBUS_BLOCK_DATA, &data);
153
154 return ret ? : count;
155}
156
157static unsigned int ipmb_poll(struct file *file, poll_table *wait)
158{
159 struct ipmb_dev *ipmb_dev = to_ipmb_dev(file);
160 unsigned int mask = POLLOUT;
161
162 mutex_lock(&ipmb_dev->file_mutex);
163 poll_wait(file, &ipmb_dev->wait_queue, wait);
164
165 if (atomic_read(&ipmb_dev->request_queue_len))
166 mask |= POLLIN;
167 mutex_unlock(&ipmb_dev->file_mutex);
168
169 return mask;
170}
171
172static const struct file_operations ipmb_fops = {
173 .owner = THIS_MODULE,
174 .read = ipmb_read,
175 .write = ipmb_write,
176 .poll = ipmb_poll,
177};
178
179/* Called with ipmb_dev->lock held. */
180static void ipmb_handle_request(struct ipmb_dev *ipmb_dev)
181{
182 struct ipmb_request_elem *queue_elem;
183
184 if (atomic_read(&ipmb_dev->request_queue_len) >=
185 REQUEST_QUEUE_MAX_LEN)
186 return;
187
188 queue_elem = kmalloc(sizeof(*queue_elem), GFP_ATOMIC);
189 if (!queue_elem)
190 return;
191
192 memcpy(&queue_elem->request, &ipmb_dev->request,
193 sizeof(struct ipmb_msg));
194 list_add(&queue_elem->list, &ipmb_dev->request_queue);
195 atomic_inc(&ipmb_dev->request_queue_len);
196 wake_up_all(&ipmb_dev->wait_queue);
197}
198
199static u8 ipmb_verify_checksum1(struct ipmb_dev *ipmb_dev, u8 rs_sa)
200{
201 /* The 8 lsb of the sum is 0 when the checksum is valid */
202 return (rs_sa + ipmb_dev->request.netfn_rs_lun +
203 ipmb_dev->request.checksum1);
204}
205
206static bool is_ipmb_request(struct ipmb_dev *ipmb_dev, u8 rs_sa)
207{
208 if (ipmb_dev->msg_idx >= IPMB_REQUEST_LEN_MIN) {
209 if (ipmb_verify_checksum1(ipmb_dev, rs_sa))
210 return false;
211
212 /*
213 * Check whether this is an IPMB request or
214 * response.
215 * The 6 MSB of netfn_rs_lun are dedicated to the netfn
216 * while the remaining bits are dedicated to the lun.
217 * If the LSB of the netfn is cleared, it is associated
218 * with an IPMB request.
219 * If the LSB of the netfn is set, it is associated with
220 * an IPMB response.
221 */
222 if (!(ipmb_dev->request.netfn_rs_lun & NETFN_RSP_BIT_MASK))
223 return true;
224 }
225 return false;
226}
227
228/*
229 * The IPMB protocol only supports I2C Writes so there is no need
230 * to support I2C_SLAVE_READ* events.
231 * This i2c callback function only monitors IPMB request messages
232 * and adds them in a queue, so that they can be handled by
233 * receive_ipmb_request.
234 */
235static int ipmb_slave_cb(struct i2c_client *client,
236 enum i2c_slave_event event, u8 *val)
237{
238 struct ipmb_dev *ipmb_dev = i2c_get_clientdata(client);
239 u8 *buf = (u8 *)&ipmb_dev->request;
240 unsigned long flags;
241
242 spin_lock_irqsave(&ipmb_dev->lock, flags);
243 switch (event) {
244 case I2C_SLAVE_WRITE_REQUESTED:
245 memset(&ipmb_dev->request, 0, sizeof(ipmb_dev->request));
246 ipmb_dev->msg_idx = 0;
247
248 /*
249 * At index 0, ipmb_msg stores the length of msg,
250 * skip it for now.
251 * The len will be populated once the whole
252 * buf is populated.
253 *
254 * The I2C bus driver's responsibility is to pass the
255 * data bytes to the backend driver; it does not
256 * forward the i2c slave address.
257 * Since the first byte in the IPMB message is the
258 * address of the responder, it is the responsibility
259 * of the IPMB driver to format the message properly.
260 * So this driver prepends the address of the responder
261 * to the received i2c data before the request message
262 * is handled in userland.
263 */
264 buf[++ipmb_dev->msg_idx] = GET_8BIT_ADDR(client->addr);
265 break;
266
267 case I2C_SLAVE_WRITE_RECEIVED:
268 if (ipmb_dev->msg_idx >= sizeof(struct ipmb_msg))
269 break;
270
271 buf[++ipmb_dev->msg_idx] = *val;
272 break;
273
274 case I2C_SLAVE_STOP:
275 ipmb_dev->request.len = ipmb_dev->msg_idx;
276
277 if (is_ipmb_request(ipmb_dev, GET_8BIT_ADDR(client->addr)))
278 ipmb_handle_request(ipmb_dev);
279 break;
280
281 default:
282 break;
283 }
284 spin_unlock_irqrestore(&ipmb_dev->lock, flags);
285
286 return 0;
287}
288
289static int ipmb_probe(struct i2c_client *client,
290 const struct i2c_device_id *id)
291{
292 struct ipmb_dev *ipmb_dev;
293 int ret;
294
295 ipmb_dev = devm_kzalloc(&client->dev, sizeof(*ipmb_dev),
296 GFP_KERNEL);
297 if (!ipmb_dev)
298 return -ENOMEM;
299
300 spin_lock_init(&ipmb_dev->lock);
301 init_waitqueue_head(&ipmb_dev->wait_queue);
302 atomic_set(&ipmb_dev->request_queue_len, 0);
303 INIT_LIST_HEAD(&ipmb_dev->request_queue);
304
305 mutex_init(&ipmb_dev->file_mutex);
306
307 ipmb_dev->miscdev.minor = MISC_DYNAMIC_MINOR;
308
309 ipmb_dev->miscdev.name = devm_kasprintf(&client->dev, GFP_KERNEL,
310 "%s%d", "ipmb-",
311 client->adapter->nr);
312 ipmb_dev->miscdev.fops = &ipmb_fops;
313 ipmb_dev->miscdev.parent = &client->dev;
314 ret = misc_register(&ipmb_dev->miscdev);
315 if (ret)
316 return ret;
317
318 ipmb_dev->client = client;
319 i2c_set_clientdata(client, ipmb_dev);
320 ret = i2c_slave_register(client, ipmb_slave_cb);
321 if (ret) {
322 misc_deregister(&ipmb_dev->miscdev);
323 return ret;
324 }
325
326 return 0;
327}
328
329static int ipmb_remove(struct i2c_client *client)
330{
331 struct ipmb_dev *ipmb_dev = i2c_get_clientdata(client);
332
333 i2c_slave_unregister(client);
334 misc_deregister(&ipmb_dev->miscdev);
335
336 return 0;
337}
338
339static const struct i2c_device_id ipmb_id[] = {
340 { "ipmb-dev", 0 },
341 {},
342};
343MODULE_DEVICE_TABLE(i2c, ipmb_id);
344
345static const struct acpi_device_id acpi_ipmb_id[] = {
346 { "IPMB0001", 0 },
347 {},
348};
349MODULE_DEVICE_TABLE(acpi, acpi_ipmb_id);
350
351static struct i2c_driver ipmb_driver = {
352 .driver = {
353 .name = "ipmb-dev",
354 .acpi_match_table = ACPI_PTR(acpi_ipmb_id),
355 },
356 .probe = ipmb_probe,
357 .remove = ipmb_remove,
358 .id_table = ipmb_id,
359};
360module_i2c_driver(ipmb_driver);
361
362MODULE_AUTHOR("Mellanox Technologies");
363MODULE_DESCRIPTION("IPMB driver");
364MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index f124a2d2bb9f..da5b6723329a 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -71,7 +71,7 @@ enum si_intf_state {
71 71
72static const char * const si_to_str[] = { "invalid", "kcs", "smic", "bt" }; 72static const char * const si_to_str[] = { "invalid", "kcs", "smic", "bt" };
73 73
74static int initialized; 74static bool initialized;
75 75
76/* 76/*
77 * Indexes into stats[] in smi_info below. 77 * Indexes into stats[] in smi_info below.
@@ -2124,7 +2124,7 @@ static int __init init_ipmi_si(void)
2124 } 2124 }
2125 2125
2126skip_fallback_noirq: 2126skip_fallback_noirq:
2127 initialized = 1; 2127 initialized = true;
2128 mutex_unlock(&smi_infos_lock); 2128 mutex_unlock(&smi_infos_lock);
2129 2129
2130 if (type) 2130 if (type)
diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c
index fd94c4238449..22f6c9b20e9a 100644
--- a/drivers/char/ipmi/ipmi_si_platform.c
+++ b/drivers/char/ipmi/ipmi_si_platform.c
@@ -19,6 +19,7 @@
19#include "ipmi_si.h" 19#include "ipmi_si.h"
20#include "ipmi_dmi.h" 20#include "ipmi_dmi.h"
21 21
22static bool platform_registered;
22static bool si_tryplatform = true; 23static bool si_tryplatform = true;
23#ifdef CONFIG_ACPI 24#ifdef CONFIG_ACPI
24static bool si_tryacpi = true; 25static bool si_tryacpi = true;
@@ -443,6 +444,7 @@ void ipmi_remove_platform_device_by_name(char *name)
443 struct platform_device *pdev = to_platform_device(dev); 444 struct platform_device *pdev = to_platform_device(dev);
444 445
445 platform_device_unregister(pdev); 446 platform_device_unregister(pdev);
447 put_device(dev);
446 } 448 }
447} 449}
448 450
@@ -469,9 +471,12 @@ void ipmi_si_platform_init(void)
469 int rv = platform_driver_register(&ipmi_platform_driver); 471 int rv = platform_driver_register(&ipmi_platform_driver);
470 if (rv) 472 if (rv)
471 pr_err("Unable to register driver: %d\n", rv); 473 pr_err("Unable to register driver: %d\n", rv);
474 else
475 platform_registered = true;
472} 476}
473 477
474void ipmi_si_platform_shutdown(void) 478void ipmi_si_platform_shutdown(void)
475{ 479{
476 platform_driver_unregister(&ipmi_platform_driver); 480 if (platform_registered)
481 platform_driver_unregister(&ipmi_platform_driver);
477} 482}
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index cf8156d6bc07..305fa5054274 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -303,6 +303,7 @@ struct ssif_info {
303 ((unsigned int) atomic_read(&(ssif)->stats[SSIF_STAT_ ## stat])) 303 ((unsigned int) atomic_read(&(ssif)->stats[SSIF_STAT_ ## stat]))
304 304
305static bool initialized; 305static bool initialized;
306static bool platform_registered;
306 307
307static void return_hosed_msg(struct ssif_info *ssif_info, 308static void return_hosed_msg(struct ssif_info *ssif_info,
308 struct ipmi_smi_msg *msg); 309 struct ipmi_smi_msg *msg);
@@ -2088,6 +2089,8 @@ static int init_ipmi_ssif(void)
2088 rv = platform_driver_register(&ipmi_driver); 2089 rv = platform_driver_register(&ipmi_driver);
2089 if (rv) 2090 if (rv)
2090 pr_err("Unable to register driver: %d\n", rv); 2091 pr_err("Unable to register driver: %d\n", rv);
2092 else
2093 platform_registered = true;
2091 } 2094 }
2092 2095
2093 ssif_i2c_driver.address_list = ssif_address_list(); 2096 ssif_i2c_driver.address_list = ssif_address_list();
@@ -2111,7 +2114,7 @@ static void cleanup_ipmi_ssif(void)
2111 2114
2112 kfree(ssif_i2c_driver.address_list); 2115 kfree(ssif_i2c_driver.address_list);
2113 2116
2114 if (ssif_trydmi) 2117 if (ssif_trydmi && platform_registered)
2115 platform_driver_unregister(&ipmi_driver); 2118 platform_driver_unregister(&ipmi_driver);
2116 2119
2117 free_ssif_clients(); 2120 free_ssif_clients();