aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlistair Popple <alistair@popple.id.au>2016-09-20 03:01:38 -0400
committerCorey Minyard <cminyard@mvista.com>2016-09-29 20:05:06 -0400
commit54f9c4d0778b3f9ab791b1b7eb1a5d2809f02f50 (patch)
treec92f2ba33d96d6cf86e0e866205f62b73e039e41
parentc6169de7308b397824bd418c5b871b5a42de83d2 (diff)
ipmi: add an Aspeed BT IPMI BMC driver
This patch adds a simple device driver to expose the iBT interface on Aspeed SOCs (AST2400 and AST2500) as a character device. Such SOCs are commonly used as BMCs (BaseBoard Management Controllers) and this driver implements the BMC side of the BT interface. The BT (Block Transfer) interface is used to perform in-band IPMI communication between a host and its BMC. Entire messages are buffered before sending a notification to the other end, host or BMC, that there is data to be read. Usually, the host emits requests and the BMC responses but the specification provides a mean for the BMC to send SMS Attention (BMC-to-Host attention or System Management Software attention) messages. For this purpose, the driver introduces a specific ioctl on the device: 'BT_BMC_IOCTL_SMS_ATN' that can be used by the system running on the BMC to signal the host of such an event. The device name defaults to '/dev/ipmi-bt-host' Signed-off-by: Alistair Popple <alistair@popple.id.au> Signed-off-by: Jeremy Kerr <jk@ozlabs.org> Signed-off-by: Joel Stanley <joel@jms.id.au> [clg: - checkpatch fixes - added a devicetree binding documentation - replace 'bt_host' by 'bt_bmc' to reflect that the driver is the BMC side of the IPMI BT interface - renamed the device to 'ipmi-bt-host' - introduced a temporary buffer to copy_{to,from}_user - used platform_get_irq() - moved the driver under drivers/char/ipmi/ but kept it as a misc device - changed the compatible cell to "aspeed,ast2400-bt-bmc" ] Signed-off-by: Cédric Le Goater <clg@kaod.org> Acked-by: Arnd Bergmann <arnd@arndb.de> [clg: - checkpatch --strict fixes - removed the use of devm_iounmap, devm_kfree in cleanup paths - introduced an atomic-t to limit opens to 1 - introduced a mutex to protect write/read operations] Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Corey Minyard <cminyard@mvista.com>
-rw-r--r--Documentation/devicetree/bindings/ipmi/aspeed,ast2400-bt-bmc.txt23
-rw-r--r--Documentation/devicetree/bindings/ipmi/ipmi-smic.txt (renamed from Documentation/devicetree/bindings/ipmi.txt)0
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/char/ipmi/Kconfig7
-rw-r--r--drivers/char/ipmi/Makefile1
-rw-r--r--drivers/char/ipmi/bt-bmc.c510
-rw-r--r--include/uapi/linux/Kbuild1
-rw-r--r--include/uapi/linux/bt-bmc.h18
8 files changed, 561 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-bt-bmc.txt b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-bt-bmc.txt
new file mode 100644
index 000000000000..fbbacd958240
--- /dev/null
+++ b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-bt-bmc.txt
@@ -0,0 +1,23 @@
1* Aspeed BT (Block Transfer) IPMI interface
2
3The Aspeed SOCs (AST2400 and AST2500) are commonly used as BMCs
4(BaseBoard Management Controllers) and the BT interface can be used to
5perform in-band IPMI communication with their host.
6
7Required properties:
8
9- compatible : should be "aspeed,ast2400-bt-bmc"
10- reg: physical address and size of the registers
11
12Optional properties:
13
14- interrupts: interrupt generated by the BT interface. without an
15 interrupt, the driver will operate in poll mode.
16
17Example:
18
19 ibt@1e789140 {
20 compatible = "aspeed,ast2400-bt-bmc";
21 reg = <0x1e789140 0x18>;
22 interrupts = <8>;
23 };
diff --git a/Documentation/devicetree/bindings/ipmi.txt b/Documentation/devicetree/bindings/ipmi/ipmi-smic.txt
index d5f1a877ed3e..d5f1a877ed3e 100644
--- a/Documentation/devicetree/bindings/ipmi.txt
+++ b/Documentation/devicetree/bindings/ipmi/ipmi-smic.txt
diff --git a/drivers/Makefile b/drivers/Makefile
index 53abb4a5f736..5a9e7b6b7928 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -21,7 +21,7 @@ obj-y += video/
21obj-y += idle/ 21obj-y += idle/
22 22
23# IPMI must come before ACPI in order to provide IPMI opregion support 23# IPMI must come before ACPI in order to provide IPMI opregion support
24obj-$(CONFIG_IPMI_HANDLER) += char/ipmi/ 24obj-y += char/ipmi/
25 25
26obj-$(CONFIG_ACPI) += acpi/ 26obj-$(CONFIG_ACPI) += acpi/
27obj-$(CONFIG_SFI) += sfi/ 27obj-$(CONFIG_SFI) += sfi/
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index 5a9350b1069a..2c234e3e7513 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -76,3 +76,10 @@ config IPMI_POWEROFF
76 the IPMI management controller is capable of this. 76 the IPMI management controller is capable of this.
77 77
78endif # IPMI_HANDLER 78endif # IPMI_HANDLER
79
80config ASPEED_BT_IPMI_BMC
81 tristate "BT IPMI bmc driver"
82 help
83 Provides a driver for the BT (Block Transfer) IPMI interface
84 found on Aspeed SOCs (AST2400 and AST2500). The driver
85 implements the BMC side of the BT interface.
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index f3ffde1f5f1f..0d98cd91def1 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
11obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o 11obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
12obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o 12obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
13obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o 13obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
14obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c
new file mode 100644
index 000000000000..2e880bf0be26
--- /dev/null
+++ b/drivers/char/ipmi/bt-bmc.c
@@ -0,0 +1,510 @@
1/*
2 * Copyright (c) 2015-2016, IBM Corporation.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9
10#include <linux/atomic.h>
11#include <linux/bt-bmc.h>
12#include <linux/errno.h>
13#include <linux/interrupt.h>
14#include <linux/io.h>
15#include <linux/miscdevice.h>
16#include <linux/module.h>
17#include <linux/platform_device.h>
18#include <linux/poll.h>
19#include <linux/sched.h>
20#include <linux/timer.h>
21
22/*
23 * This is a BMC device used to communicate to the host
24 */
25#define DEVICE_NAME "ipmi-bt-host"
26
27#define BT_IO_BASE 0xe4
28#define BT_IRQ 10
29
30#define BT_CR0 0x0
31#define BT_CR0_IO_BASE 16
32#define BT_CR0_IRQ 12
33#define BT_CR0_EN_CLR_SLV_RDP 0x8
34#define BT_CR0_EN_CLR_SLV_WRP 0x4
35#define BT_CR0_ENABLE_IBT 0x1
36#define BT_CR1 0x4
37#define BT_CR1_IRQ_H2B 0x01
38#define BT_CR1_IRQ_HBUSY 0x40
39#define BT_CR2 0x8
40#define BT_CR2_IRQ_H2B 0x01
41#define BT_CR2_IRQ_HBUSY 0x40
42#define BT_CR3 0xc
43#define BT_CTRL 0x10
44#define BT_CTRL_B_BUSY 0x80
45#define BT_CTRL_H_BUSY 0x40
46#define BT_CTRL_OEM0 0x20
47#define BT_CTRL_SMS_ATN 0x10
48#define BT_CTRL_B2H_ATN 0x08
49#define BT_CTRL_H2B_ATN 0x04
50#define BT_CTRL_CLR_RD_PTR 0x02
51#define BT_CTRL_CLR_WR_PTR 0x01
52#define BT_BMC2HOST 0x14
53#define BT_INTMASK 0x18
54#define BT_INTMASK_B2H_IRQEN 0x01
55#define BT_INTMASK_B2H_IRQ 0x02
56#define BT_INTMASK_BMC_HWRST 0x80
57
58#define BT_BMC_BUFFER_SIZE 256
59
60struct bt_bmc {
61 struct device dev;
62 struct miscdevice miscdev;
63 void __iomem *base;
64 int irq;
65 wait_queue_head_t queue;
66 struct timer_list poll_timer;
67 struct mutex mutex;
68};
69
70static atomic_t open_count = ATOMIC_INIT(0);
71
72static u8 bt_inb(struct bt_bmc *bt_bmc, int reg)
73{
74 return ioread8(bt_bmc->base + reg);
75}
76
77static void bt_outb(struct bt_bmc *bt_bmc, u8 data, int reg)
78{
79 iowrite8(data, bt_bmc->base + reg);
80}
81
82static void clr_rd_ptr(struct bt_bmc *bt_bmc)
83{
84 bt_outb(bt_bmc, BT_CTRL_CLR_RD_PTR, BT_CTRL);
85}
86
87static void clr_wr_ptr(struct bt_bmc *bt_bmc)
88{
89 bt_outb(bt_bmc, BT_CTRL_CLR_WR_PTR, BT_CTRL);
90}
91
92static void clr_h2b_atn(struct bt_bmc *bt_bmc)
93{
94 bt_outb(bt_bmc, BT_CTRL_H2B_ATN, BT_CTRL);
95}
96
97static void set_b_busy(struct bt_bmc *bt_bmc)
98{
99 if (!(bt_inb(bt_bmc, BT_CTRL) & BT_CTRL_B_BUSY))
100 bt_outb(bt_bmc, BT_CTRL_B_BUSY, BT_CTRL);
101}
102
103static void clr_b_busy(struct bt_bmc *bt_bmc)
104{
105 if (bt_inb(bt_bmc, BT_CTRL) & BT_CTRL_B_BUSY)
106 bt_outb(bt_bmc, BT_CTRL_B_BUSY, BT_CTRL);
107}
108
109static void set_b2h_atn(struct bt_bmc *bt_bmc)
110{
111 bt_outb(bt_bmc, BT_CTRL_B2H_ATN, BT_CTRL);
112}
113
114static u8 bt_read(struct bt_bmc *bt_bmc)
115{
116 return bt_inb(bt_bmc, BT_BMC2HOST);
117}
118
119static ssize_t bt_readn(struct bt_bmc *bt_bmc, u8 *buf, size_t n)
120{
121 int i;
122
123 for (i = 0; i < n; i++)
124 buf[i] = bt_read(bt_bmc);
125 return n;
126}
127
128static void bt_write(struct bt_bmc *bt_bmc, u8 c)
129{
130 bt_outb(bt_bmc, c, BT_BMC2HOST);
131}
132
133static ssize_t bt_writen(struct bt_bmc *bt_bmc, u8 *buf, size_t n)
134{
135 int i;
136
137 for (i = 0; i < n; i++)
138 bt_write(bt_bmc, buf[i]);
139 return n;
140}
141
142static void set_sms_atn(struct bt_bmc *bt_bmc)
143{
144 bt_outb(bt_bmc, BT_CTRL_SMS_ATN, BT_CTRL);
145}
146
147static struct bt_bmc *file_bt_bmc(struct file *file)
148{
149 return container_of(file->private_data, struct bt_bmc, miscdev);
150}
151
152static int bt_bmc_open(struct inode *inode, struct file *file)
153{
154 struct bt_bmc *bt_bmc = file_bt_bmc(file);
155
156 if (atomic_inc_return(&open_count) == 1) {
157 clr_b_busy(bt_bmc);
158 return 0;
159 }
160
161 atomic_dec(&open_count);
162 return -EBUSY;
163}
164
165/*
166 * The BT (Block Transfer) interface means that entire messages are
167 * buffered by the host before a notification is sent to the BMC that
168 * there is data to be read. The first byte is the length and the
169 * message data follows. The read operation just tries to capture the
170 * whole before returning it to userspace.
171 *
172 * BT Message format :
173 *
174 * Byte 1 Byte 2 Byte 3 Byte 4 Byte 5:N
175 * Length NetFn/LUN Seq Cmd Data
176 *
177 */
178static ssize_t bt_bmc_read(struct file *file, char __user *buf,
179 size_t count, loff_t *ppos)
180{
181 struct bt_bmc *bt_bmc = file_bt_bmc(file);
182 u8 len;
183 int len_byte = 1;
184 u8 kbuffer[BT_BMC_BUFFER_SIZE];
185 ssize_t ret = 0;
186 ssize_t nread;
187
188 if (!access_ok(VERIFY_WRITE, buf, count))
189 return -EFAULT;
190
191 WARN_ON(*ppos);
192
193 if (wait_event_interruptible(bt_bmc->queue,
194 bt_inb(bt_bmc, BT_CTRL) & BT_CTRL_H2B_ATN))
195 return -ERESTARTSYS;
196
197 mutex_lock(&bt_bmc->mutex);
198
199 if (unlikely(!(bt_inb(bt_bmc, BT_CTRL) & BT_CTRL_H2B_ATN))) {
200 ret = -EIO;
201 goto out_unlock;
202 }
203
204 set_b_busy(bt_bmc);
205 clr_h2b_atn(bt_bmc);
206 clr_rd_ptr(bt_bmc);
207
208 /*
209 * The BT frames start with the message length, which does not
210 * include the length byte.
211 */
212 kbuffer[0] = bt_read(bt_bmc);
213 len = kbuffer[0];
214
215 /* We pass the length back to userspace as well */
216 if (len + 1 > count)
217 len = count - 1;
218
219 while (len) {
220 nread = min_t(ssize_t, len, sizeof(kbuffer) - len_byte);
221
222 bt_readn(bt_bmc, kbuffer + len_byte, nread);
223
224 if (copy_to_user(buf, kbuffer, nread + len_byte)) {
225 ret = -EFAULT;
226 break;
227 }
228 len -= nread;
229 buf += nread + len_byte;
230 ret += nread + len_byte;
231 len_byte = 0;
232 }
233
234 clr_b_busy(bt_bmc);
235
236out_unlock:
237 mutex_unlock(&bt_bmc->mutex);
238 return ret;
239}
240
241/*
242 * BT Message response format :
243 *
244 * Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6:N
245 * Length NetFn/LUN Seq Cmd Code Data
246 */
247static ssize_t bt_bmc_write(struct file *file, const char __user *buf,
248 size_t count, loff_t *ppos)
249{
250 struct bt_bmc *bt_bmc = file_bt_bmc(file);
251 u8 kbuffer[BT_BMC_BUFFER_SIZE];
252 ssize_t ret = 0;
253 ssize_t nwritten;
254
255 /*
256 * send a minimum response size
257 */
258 if (count < 5)
259 return -EINVAL;
260
261 if (!access_ok(VERIFY_READ, buf, count))
262 return -EFAULT;
263
264 WARN_ON(*ppos);
265
266 /*
267 * There's no interrupt for clearing bmc busy so we have to
268 * poll
269 */
270 if (wait_event_interruptible(bt_bmc->queue,
271 !(bt_inb(bt_bmc, BT_CTRL) &
272 (BT_CTRL_H_BUSY | BT_CTRL_B2H_ATN))))
273 return -ERESTARTSYS;
274
275 mutex_lock(&bt_bmc->mutex);
276
277 if (unlikely(bt_inb(bt_bmc, BT_CTRL) &
278 (BT_CTRL_H_BUSY | BT_CTRL_B2H_ATN))) {
279 ret = -EIO;
280 goto out_unlock;
281 }
282
283 clr_wr_ptr(bt_bmc);
284
285 while (count) {
286 nwritten = min_t(ssize_t, count, sizeof(kbuffer));
287 if (copy_from_user(&kbuffer, buf, nwritten)) {
288 ret = -EFAULT;
289 break;
290 }
291
292 bt_writen(bt_bmc, kbuffer, nwritten);
293
294 count -= nwritten;
295 buf += nwritten;
296 ret += nwritten;
297 }
298
299 set_b2h_atn(bt_bmc);
300
301out_unlock:
302 mutex_unlock(&bt_bmc->mutex);
303 return ret;
304}
305
306static long bt_bmc_ioctl(struct file *file, unsigned int cmd,
307 unsigned long param)
308{
309 struct bt_bmc *bt_bmc = file_bt_bmc(file);
310
311 switch (cmd) {
312 case BT_BMC_IOCTL_SMS_ATN:
313 set_sms_atn(bt_bmc);
314 return 0;
315 }
316 return -EINVAL;
317}
318
319static int bt_bmc_release(struct inode *inode, struct file *file)
320{
321 struct bt_bmc *bt_bmc = file_bt_bmc(file);
322
323 atomic_dec(&open_count);
324 set_b_busy(bt_bmc);
325 return 0;
326}
327
328static unsigned int bt_bmc_poll(struct file *file, poll_table *wait)
329{
330 struct bt_bmc *bt_bmc = file_bt_bmc(file);
331 unsigned int mask = 0;
332 u8 ctrl;
333
334 poll_wait(file, &bt_bmc->queue, wait);
335
336 ctrl = bt_inb(bt_bmc, BT_CTRL);
337
338 if (ctrl & BT_CTRL_H2B_ATN)
339 mask |= POLLIN;
340
341 if (!(ctrl & (BT_CTRL_H_BUSY | BT_CTRL_B2H_ATN)))
342 mask |= POLLOUT;
343
344 return mask;
345}
346
347static const struct file_operations bt_bmc_fops = {
348 .owner = THIS_MODULE,
349 .open = bt_bmc_open,
350 .read = bt_bmc_read,
351 .write = bt_bmc_write,
352 .release = bt_bmc_release,
353 .poll = bt_bmc_poll,
354 .unlocked_ioctl = bt_bmc_ioctl,
355};
356
357static void poll_timer(unsigned long data)
358{
359 struct bt_bmc *bt_bmc = (void *)data;
360
361 bt_bmc->poll_timer.expires += msecs_to_jiffies(500);
362 wake_up(&bt_bmc->queue);
363 add_timer(&bt_bmc->poll_timer);
364}
365
366static irqreturn_t bt_bmc_irq(int irq, void *arg)
367{
368 struct bt_bmc *bt_bmc = arg;
369 u32 reg;
370
371 reg = ioread32(bt_bmc->base + BT_CR2);
372 reg &= BT_CR2_IRQ_H2B | BT_CR2_IRQ_HBUSY;
373 if (!reg)
374 return IRQ_NONE;
375
376 /* ack pending IRQs */
377 iowrite32(reg, bt_bmc->base + BT_CR2);
378
379 wake_up(&bt_bmc->queue);
380 return IRQ_HANDLED;
381}
382
383static int bt_bmc_config_irq(struct bt_bmc *bt_bmc,
384 struct platform_device *pdev)
385{
386 struct device *dev = &pdev->dev;
387 u32 reg;
388 int rc;
389
390 bt_bmc->irq = platform_get_irq(pdev, 0);
391 if (!bt_bmc->irq)
392 return -ENODEV;
393
394 rc = devm_request_irq(dev, bt_bmc->irq, bt_bmc_irq, IRQF_SHARED,
395 DEVICE_NAME, bt_bmc);
396 if (rc < 0) {
397 dev_warn(dev, "Unable to request IRQ %d\n", bt_bmc->irq);
398 bt_bmc->irq = 0;
399 return rc;
400 }
401
402 /*
403 * Configure IRQs on the bmc clearing the H2B and HBUSY bits;
404 * H2B will be asserted when the bmc has data for us; HBUSY
405 * will be cleared (along with B2H) when we can write the next
406 * message to the BT buffer
407 */
408 reg = ioread32(bt_bmc->base + BT_CR1);
409 reg |= BT_CR1_IRQ_H2B | BT_CR1_IRQ_HBUSY;
410 iowrite32(reg, bt_bmc->base + BT_CR1);
411
412 return 0;
413}
414
415static int bt_bmc_probe(struct platform_device *pdev)
416{
417 struct bt_bmc *bt_bmc;
418 struct device *dev;
419 struct resource *res;
420 int rc;
421
422 if (!pdev || !pdev->dev.of_node)
423 return -ENODEV;
424
425 dev = &pdev->dev;
426 dev_info(dev, "Found bt bmc device\n");
427
428 bt_bmc = devm_kzalloc(dev, sizeof(*bt_bmc), GFP_KERNEL);
429 if (!bt_bmc)
430 return -ENOMEM;
431
432 dev_set_drvdata(&pdev->dev, bt_bmc);
433
434 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
435 if (!res) {
436 dev_err(dev, "Unable to find resources\n");
437 return -ENXIO;
438 }
439
440 bt_bmc->base = devm_ioremap_resource(&pdev->dev, res);
441 if (!bt_bmc->base)
442 return -ENOMEM;
443
444 mutex_init(&bt_bmc->mutex);
445 init_waitqueue_head(&bt_bmc->queue);
446
447 bt_bmc->miscdev.minor = MISC_DYNAMIC_MINOR,
448 bt_bmc->miscdev.name = DEVICE_NAME,
449 bt_bmc->miscdev.fops = &bt_bmc_fops,
450 bt_bmc->miscdev.parent = dev;
451 rc = misc_register(&bt_bmc->miscdev);
452 if (rc) {
453 dev_err(dev, "Unable to register misc device\n");
454 return rc;
455 }
456
457 bt_bmc_config_irq(bt_bmc, pdev);
458
459 if (bt_bmc->irq) {
460 dev_info(dev, "Using IRQ %d\n", bt_bmc->irq);
461 } else {
462 dev_info(dev, "No IRQ; using timer\n");
463 setup_timer(&bt_bmc->poll_timer, poll_timer,
464 (unsigned long)bt_bmc);
465 bt_bmc->poll_timer.expires = jiffies + msecs_to_jiffies(10);
466 add_timer(&bt_bmc->poll_timer);
467 }
468
469 iowrite32((BT_IO_BASE << BT_CR0_IO_BASE) |
470 (BT_IRQ << BT_CR0_IRQ) |
471 BT_CR0_EN_CLR_SLV_RDP |
472 BT_CR0_EN_CLR_SLV_WRP |
473 BT_CR0_ENABLE_IBT,
474 bt_bmc->base + BT_CR0);
475
476 clr_b_busy(bt_bmc);
477
478 return 0;
479}
480
481static int bt_bmc_remove(struct platform_device *pdev)
482{
483 struct bt_bmc *bt_bmc = dev_get_drvdata(&pdev->dev);
484
485 misc_deregister(&bt_bmc->miscdev);
486 if (!bt_bmc->irq)
487 del_timer_sync(&bt_bmc->poll_timer);
488 return 0;
489}
490
491static const struct of_device_id bt_bmc_match[] = {
492 { .compatible = "aspeed,ast2400-bt-bmc" },
493 { },
494};
495
496static struct platform_driver bt_bmc_driver = {
497 .driver = {
498 .name = DEVICE_NAME,
499 .of_match_table = bt_bmc_match,
500 },
501 .probe = bt_bmc_probe,
502 .remove = bt_bmc_remove,
503};
504
505module_platform_driver(bt_bmc_driver);
506
507MODULE_DEVICE_TABLE(of, bt_bmc_match);
508MODULE_LICENSE("GPL");
509MODULE_AUTHOR("Alistair Popple <alistair@popple.id.au>");
510MODULE_DESCRIPTION("Linux device interface to the BT interface");
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 185f8ea2702f..17b12942c67d 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -74,6 +74,7 @@ header-y += bpf_common.h
74header-y += bpf.h 74header-y += bpf.h
75header-y += bpqether.h 75header-y += bpqether.h
76header-y += bsg.h 76header-y += bsg.h
77header-y += bt-bmc.h
77header-y += btrfs.h 78header-y += btrfs.h
78header-y += can.h 79header-y += can.h
79header-y += capability.h 80header-y += capability.h
diff --git a/include/uapi/linux/bt-bmc.h b/include/uapi/linux/bt-bmc.h
new file mode 100644
index 000000000000..d9ec766a63d0
--- /dev/null
+++ b/include/uapi/linux/bt-bmc.h
@@ -0,0 +1,18 @@
1/*
2 * Copyright (c) 2015-2016, IBM Corporation.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9
10#ifndef _UAPI_LINUX_BT_BMC_H
11#define _UAPI_LINUX_BT_BMC_H
12
13#include <linux/ioctl.h>
14
15#define __BT_BMC_IOCTL_MAGIC 0xb1
16#define BT_BMC_IOCTL_SMS_ATN _IO(__BT_BMC_IOCTL_MAGIC, 0x00)
17
18#endif /* _UAPI_LINUX_BT_BMC_H */