git: 1f166509895d - main - ipmi: add Block Transfer interface support

From: Andrey V. Elsukov <ae_at_FreeBSD.org>
Date: Wed, 07 Jun 2023 09:33:16 UTC
The branch main has been updated by ae:

URL: https://cgit.FreeBSD.org/src/commit/?id=1f166509895dd90fcaaa08b557f9bc106e3f7e71

commit 1f166509895dd90fcaaa08b557f9bc106e3f7e71
Author:     Andrey V. Elsukov <ae@FreeBSD.org>
AuthorDate: 2023-06-07 09:30:47 +0000
Commit:     Andrey V. Elsukov <ae@FreeBSD.org>
CommitDate: 2023-06-07 09:30:47 +0000

    ipmi: add Block Transfer interface support
    
    Reviewed by:    ambrisko
    Obtained from:  Yandex LLC
    MFC after:      2 weeks
    Sponsored by:   Yandex LLC
    Differential Revision:  https://reviews.freebsd.org/D40421
---
 sys/conf/files.arm64       |   1 +
 sys/conf/files.x86         |   1 +
 sys/dev/ipmi/ipmi_acpi.c   |  19 +--
 sys/dev/ipmi/ipmi_bt.c     | 296 +++++++++++++++++++++++++++++++++++++++++++++
 sys/dev/ipmi/ipmi_isa.c    |  19 +--
 sys/dev/ipmi/ipmi_pci.c    |  28 +++--
 sys/dev/ipmi/ipmi_smbios.c |   1 +
 sys/dev/ipmi/ipmivars.h    |  19 ++-
 sys/modules/ipmi/Makefile  |   2 +-
 9 files changed, 353 insertions(+), 33 deletions(-)

diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64
index 9218336d154d..1fed31af0761 100644
--- a/sys/conf/files.arm64
+++ b/sys/conf/files.arm64
@@ -338,6 +338,7 @@ dev/iicbus/twsi/twsi.c				optional twsi fdt
 
 dev/ipmi/ipmi.c					optional ipmi
 dev/ipmi/ipmi_acpi.c				optional ipmi acpi
+dev/ipmi/ipmi_bt.c				optional ipmi
 dev/ipmi/ipmi_kcs.c				optional ipmi
 dev/ipmi/ipmi_smic.c				optional ipmi
 
diff --git a/sys/conf/files.x86 b/sys/conf/files.x86
index f80638f98f95..7f455b4c9bb3 100644
--- a/sys/conf/files.x86
+++ b/sys/conf/files.x86
@@ -153,6 +153,7 @@ dev/iommu/iommu_gas.c		optional	acpi iommu pci
 dev/ipmi/ipmi.c			optional	ipmi
 dev/ipmi/ipmi_acpi.c		optional	ipmi acpi
 dev/ipmi/ipmi_isa.c		optional	ipmi isa
+dev/ipmi/ipmi_bt.c		optional	ipmi
 dev/ipmi/ipmi_kcs.c		optional	ipmi
 dev/ipmi/ipmi_smic.c		optional	ipmi
 dev/ipmi/ipmi_smbus.c		optional	ipmi smbus
diff --git a/sys/dev/ipmi/ipmi_acpi.c b/sys/dev/ipmi/ipmi_acpi.c
index e06f929c80b9..bfebac6c92d6 100644
--- a/sys/dev/ipmi/ipmi_acpi.c
+++ b/sys/dev/ipmi/ipmi_acpi.c
@@ -94,16 +94,17 @@ ipmi_acpi_attach(device_t dev)
 
 	switch (interface_type) {
 	case KCS_MODE:
-		count = 2;
+		count = IPMI_IF_KCS_NRES;
 		mode = "KCS";
 		break;
 	case SMIC_MODE:
-		count = 3;
+		count = IPMI_IF_SMIC_NRES;
 		mode = "SMIC";
 		break;
 	case BT_MODE:
-		device_printf(dev, "BT interface not supported\n");
-		return (ENXIO);
+		count = IPMI_IF_BT_NRES;
+		mode = "BT";
+		break;
 	case SSIF_MODE:
 		device_printf(dev, "SSIF interface not supported on ACPI\n");
 		return (0);
@@ -170,18 +171,20 @@ ipmi_acpi_attach(device_t dev)
 	 * We assume an alignment of 1 byte as currently the IPMI spec
 	 * doesn't provide any way to determine the alignment via ACPI.
 	 */
+	error = ENXIO;
 	switch (interface_type) {
 	case KCS_MODE:
 		error = ipmi_kcs_attach(sc);
-		if (error)
-			goto bad;
 		break;
 	case SMIC_MODE:
 		error = ipmi_smic_attach(sc);
-		if (error)
-			goto bad;
+		break;
+	case BT_MODE:
+		error = ipmi_bt_attach(sc);
 		break;
 	}
+	if (error)
+		goto bad;
 	error = ipmi_attach(dev);
 	if (error)
 		goto bad;
diff --git a/sys/dev/ipmi/ipmi_bt.c b/sys/dev/ipmi/ipmi_bt.c
new file mode 100644
index 000000000000..0f970d966731
--- /dev/null
+++ b/sys/dev/ipmi/ipmi_bt.c
@@ -0,0 +1,296 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Yandex LLC
+ * Copyright (c) 2023 Andrey V. Elsukov <ae@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/selinfo.h>
+#include <machine/bus.h>
+
+#include <sys/ipmi.h>
+#include <dev/ipmi/ipmivars.h>
+
+/*
+ * BT interface
+ */
+
+#define	DMSG0(sc, fmt, ...)	do {				\
+	device_printf((sc)->ipmi_dev, "BT: %s: " fmt "\n",	\
+	    __func__, ## __VA_ARGS__);				\
+} while (0)
+
+#define	DMSGV(...)		if (bootverbose) {		\
+	DMSG0(__VA_ARGS__);					\
+}
+
+#ifdef IPMI_BT_DEBUG
+#define	DMSG(...)		DMSG0(__VA_ARGS__)
+#else
+#define	DMSG(...)
+#endif
+
+#define	BT_IO_BASE		0xe4
+
+#define	BT_CTRL_REG		0
+#define	  BT_C_CLR_WR_PTR	(1L << 0)
+#define	  BT_C_CLR_RD_PTR	(1L << 1)
+#define	  BT_C_H2B_ATN		(1L << 2)
+#define	  BT_C_B2H_ATN		(1L << 3)
+#define	  BT_C_SMS_ATN		(1L << 4)
+#define	  BT_C_OEM0		(1L << 5)
+#define	  BT_C_H_BUSY		(1L << 6)
+#define	  BT_C_B_BUSY		(1L << 7)
+
+#define	BT_CTRL_BITS		"\20\01CLR_WR_PTR\02CLR_RD_PTR\03H2B_ATN\04B2H_ATN"\
+				"\05SMS_ATN\06OEM0\07H_BUSY\010B_BUSY"
+
+#define	BT_DATA_REG		1
+#define	 BTMSG_REQLEN		3
+#define	 BTMSG_REPLEN		4
+
+#define	BT_INTMASK_REG		2
+#define	 BT_IM_B2H_IRQ_EN	(1L << 0)
+#define	 BT_IM_B2H_IRQ		(1L << 1)
+#define	 BT_IM_BMC_HWRST	(1L << 7)
+
+static int bt_polled_request(struct ipmi_softc *, struct ipmi_request *);
+static int bt_driver_request(struct ipmi_softc *, struct ipmi_request *, int);
+static int bt_wait(struct ipmi_softc *, uint8_t, uint8_t);
+static int bt_reset(struct ipmi_softc *);
+
+static void bt_loop(void *);
+static int bt_startup(struct ipmi_softc *);
+
+#define	BT_DELAY_MIN	1
+#define	BT_DELAY_MAX	256
+
+static int
+bt_wait(struct ipmi_softc *sc, uint8_t mask, uint8_t wanted)
+{
+	volatile uint8_t value;
+	int delay = BT_DELAY_MIN;
+	int count = 20000; /* about 5 seconds */
+
+	while (count--) {
+		value = INB(sc, BT_CTRL_REG);
+		if ((value & mask) == wanted)
+			return (value);
+		/*
+		 * The wait delay is increased exponentially to avoid putting
+		 * significant load on I/O bus.
+		 */
+		DELAY(delay);
+		if (delay < BT_DELAY_MAX)
+			delay <<= 1;
+	}
+	DMSGV(sc, "failed: m=%b w=%b v=0x%02x\n",
+	    mask, BT_CTRL_BITS, wanted, BT_CTRL_BITS, value);
+	return (-1);
+
+}
+
+static int
+bt_reset(struct ipmi_softc *sc)
+{
+	uint8_t v;
+
+	v = INB(sc, BT_CTRL_REG);
+	DMSG(sc, "ctrl: %b", v, BT_CTRL_BITS);
+	v &= BT_C_H_BUSY; /* clear H_BUSY iff it set */
+	v |= BT_C_CLR_WR_PTR | BT_C_CLR_RD_PTR | BT_C_B2H_ATN | BT_C_H2B_ATN;
+
+	bt_wait(sc, BT_C_B_BUSY, 0);
+	OUTB(sc, BT_CTRL_REG, v);
+
+	v = BT_IM_B2H_IRQ | BT_IM_BMC_HWRST;
+	OUTB(sc, BT_INTMASK_REG, v);
+
+	return (0);
+}
+
+/*
+ * Send a request message and collect the reply. Returns 1 if we
+ * succeed.
+ */
+static int
+bt_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
+{
+	uint8_t addr, cmd, seq, v;
+	int i;
+
+	IPMI_IO_LOCK(sc);
+
+	/*
+	 * Send the request:
+	 *
+	 * Byte 1 | Byte 2    | Byte 3 | Byte 4 | Byte 5:N
+	 * -------+-----------+--------+--------+---------
+	 * Length | NetFn/LUN | Seq    | Cmd    | Data
+	 */
+
+	if (bt_wait(sc, BT_C_B_BUSY | BT_C_H2B_ATN, 0) < 0) {
+		DMSG(sc, "failed to start write transfer");
+		goto fail;
+	}
+	DMSG(sc, "request: length=%d, addr=0x%02x, seq=%u, cmd=0x%02x",
+	    (int)req->ir_requestlen, req->ir_addr, sc->ipmi_bt_seq, req->ir_command);
+	OUTB(sc, BT_CTRL_REG, BT_C_CLR_WR_PTR);
+	OUTB(sc, BT_DATA_REG, req->ir_requestlen + BTMSG_REQLEN);
+	OUTB(sc, BT_DATA_REG, req->ir_addr);
+	OUTB(sc, BT_DATA_REG, sc->ipmi_bt_seq);
+	OUTB(sc, BT_DATA_REG, req->ir_command);
+	for (i = 0; i < req->ir_requestlen; i++)
+		OUTB(sc, BT_DATA_REG, req->ir_request[i]);
+	OUTB(sc, BT_CTRL_REG, BT_C_H2B_ATN);
+
+	if (bt_wait(sc, BT_C_B_BUSY | BT_C_H2B_ATN, 0) < 0) {
+		DMSG(sc, "failed to finish write transfer");
+		goto fail;
+	}
+
+	/*
+	 * Read the reply:
+	 *
+	 * Byte 1 | Byte 2    | Byte 3 | Byte 4 | Byte 5          | Byte 6:N
+	 * -------+-----------+--------+--------+-----------------+---------
+	 * Length | NetFn/LUN | Seq    | Cmd    | Completion code | Data
+	 */
+	if (bt_wait(sc, BT_C_B2H_ATN, BT_C_B2H_ATN) < 0) {
+		DMSG(sc, "got no reply from BMC");
+		goto fail;
+	}
+	OUTB(sc, BT_CTRL_REG, BT_C_H_BUSY);
+	OUTB(sc, BT_CTRL_REG, BT_C_B2H_ATN);
+	OUTB(sc, BT_CTRL_REG, BT_C_CLR_RD_PTR);
+
+	i = INB(sc, BT_DATA_REG);
+	if (i < BTMSG_REPLEN) {
+		DMSG(sc, "wrong data length: %d", i);
+		goto fail;
+	}
+	req->ir_replylen = i - BTMSG_REPLEN;
+	DMSG(sc, "data length: %d, frame length: %d", req->ir_replylen, i);
+
+	addr = INB(sc, BT_DATA_REG);
+	if (addr != IPMI_REPLY_ADDR(req->ir_addr)) {
+		DMSGV(sc, "address doesn't match: addr=0x%02x vs. 0x%02x",
+		    req->ir_addr, addr);
+	}
+
+	seq = INB(sc, BT_DATA_REG);
+	if (seq != sc->ipmi_bt_seq) {
+		DMSGV(sc, "seq number doesn't match: seq=0x%02x vs. 0x%02x",
+		    sc->ipmi_bt_seq, seq);
+	}
+
+	cmd = INB(sc, BT_DATA_REG);
+	if (cmd != req->ir_command) {
+		DMSGV(sc, "command doesn't match: cmd=0x%02x vs. 0x%02x",
+		    req->ir_command, cmd);
+	}
+
+	req->ir_compcode = INB(sc, BT_DATA_REG);
+	for (i = 0; i < req->ir_replylen; i++) {
+		v = INB(sc, BT_DATA_REG);
+		if (i < req->ir_replybuflen)
+			req->ir_reply[i] = v;
+	}
+
+	OUTB(sc, BT_CTRL_REG, BT_C_H_BUSY);
+	IPMI_IO_UNLOCK(sc);
+	DMSG(sc, "reply: length=%d, addr=0x%02x, seq=%u, cmd=0x%02x, code=0x%02x",
+	    (int)req->ir_replylen, addr, seq, req->ir_command, req->ir_compcode);
+	return (1);
+fail:
+	bt_reset(sc);
+	IPMI_IO_UNLOCK(sc);
+	return (0);
+}
+
+static void
+bt_loop(void *arg)
+{
+	struct ipmi_softc *sc = arg;
+	struct ipmi_request *req;
+
+	IPMI_LOCK(sc);
+	while ((req = ipmi_dequeue_request(sc)) != NULL) {
+		IPMI_UNLOCK(sc);
+		(void)bt_driver_request(sc, req, 0);
+		IPMI_LOCK(sc);
+		sc->ipmi_bt_seq++;
+		ipmi_complete_request(sc, req);
+	}
+	IPMI_UNLOCK(sc);
+	kproc_exit(0);
+}
+
+static int
+bt_startup(struct ipmi_softc *sc)
+{
+
+	return (kproc_create(bt_loop, sc, &sc->ipmi_kthread, 0, 0, "%s: bt",
+	    device_get_nameunit(sc->ipmi_dev)));
+}
+
+static int
+bt_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo __unused)
+{
+	int i, ok;
+
+	ok = 0;
+	for (i = 0; i < 3 && !ok; i++)
+		ok = bt_polled_request(sc, req);
+	if (ok)
+		req->ir_error = 0;
+	else
+		req->ir_error = EIO;
+	return (req->ir_error);
+}
+
+int
+ipmi_bt_attach(struct ipmi_softc *sc)
+{
+	/* Setup function pointers. */
+	sc->ipmi_startup = bt_startup;
+	sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
+	sc->ipmi_driver_request = bt_driver_request;
+	sc->ipmi_driver_requests_polled = 1;
+	sc->ipmi_bt_seq = 1;
+
+	return (bt_reset(sc));
+}
diff --git a/sys/dev/ipmi/ipmi_isa.c b/sys/dev/ipmi/ipmi_isa.c
index 275c2f5523f9..06fe10e87286 100644
--- a/sys/dev/ipmi/ipmi_isa.c
+++ b/sys/dev/ipmi/ipmi_isa.c
@@ -188,16 +188,17 @@ ipmi_isa_attach(device_t dev)
 
 	switch (info.iface_type) {
 	case KCS_MODE:
-		count = 2;
+		count = IPMI_IF_KCS_NRES;
 		mode = "KCS";
 		break;
 	case SMIC_MODE:
-		count = 3;
+		count = IPMI_IF_SMIC_NRES;
 		mode = "SMIC";
 		break;
 	case BT_MODE:
-		device_printf(dev, "BT mode is unsupported\n");
-		return (ENXIO);
+		count = IPMI_IF_BT_NRES;
+		mode = "BT";
+		break;
 	default:
 		return (ENXIO);
 	}
@@ -248,19 +249,21 @@ ipmi_isa_attach(device_t dev)
 		    RF_SHAREABLE | RF_ACTIVE);
 	}
 
+	error = ENXIO;
 	switch (info.iface_type) {
 	case KCS_MODE:
 		error = ipmi_kcs_attach(sc);
-		if (error)
-			goto bad;
 		break;
 	case SMIC_MODE:
 		error = ipmi_smic_attach(sc);
-		if (error)
-			goto bad;
+		break;
+	case BT_MODE:
+		error = ipmi_bt_attach(sc);
 		break;
 	}
 
+	if (error)
+		goto bad;
 	error = ipmi_attach(dev);
 	if (error)
 		goto bad;
diff --git a/sys/dev/ipmi/ipmi_pci.c b/sys/dev/ipmi/ipmi_pci.c
index 72a1c84f9b71..9417caa40c78 100644
--- a/sys/dev/ipmi/ipmi_pci.c
+++ b/sys/dev/ipmi/ipmi_pci.c
@@ -112,8 +112,8 @@ ipmi_pci_attach(device_t dev)
 		mode = "SMIC";
 		break;
 	case BT_MODE:
-		device_printf(dev, "BT mode is unsupported\n");
-		return (ENXIO);
+		mode = "BT";
+		break;
 	default:
 		device_printf(dev, "No IPMI interface found\n");
 		return (ENXIO);
@@ -143,18 +143,20 @@ ipmi_pci_attach(device_t dev)
 	sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
 	    &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
 
+	error = ENXIO;
 	switch (info.iface_type) {
 	case KCS_MODE:
 		error = ipmi_kcs_attach(sc);
-		if (error)
-			goto bad;
 		break;
 	case SMIC_MODE:
 		error = ipmi_smic_attach(sc);
-		if (error)
-			goto bad;
+		break;
+	case BT_MODE:
+		error = ipmi_bt_attach(sc);
 		break;
 	}
+	if (error)
+		goto bad;
 	error = ipmi_attach(dev);
 	if (error)
 		goto bad;
@@ -215,8 +217,7 @@ ipmi2_pci_attach(device_t dev)
 		break;
 	case PCIP_SERIALBUS_IPMI_BT:
 		iface = BT_MODE;
-		device_printf(dev, "BT interface unsupported\n");
-		return (ENXIO);
+		break;
 	default:
 		device_printf(dev, "Unsupported interface: %d\n",
 		    pci_get_progif(dev));
@@ -242,6 +243,7 @@ ipmi2_pci_attach(device_t dev)
 	sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
 	    &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
 
+	error = ENXIO;
 	switch (iface) {
 	case KCS_MODE:
 		device_printf(dev, "using KSC interface\n");
@@ -252,7 +254,6 @@ ipmi2_pci_attach(device_t dev)
 		 */
 		if (!ipmi_kcs_probe_align(sc)) {
 			device_printf(dev, "Unable to determine alignment\n");
-			error = ENXIO;
 			goto bad;
 		}
 
@@ -262,12 +263,15 @@ ipmi2_pci_attach(device_t dev)
 		break;
 	case SMIC_MODE:
 		device_printf(dev, "using SMIC interface\n");
-
 		error = ipmi_smic_attach(sc);
-		if (error)
-			goto bad;
+		break;
+	case BT_MODE:
+		device_printf(dev, "using BT interface\n");
+		error = ipmi_bt_attach(sc);
 		break;
 	}
+	if (error)
+		goto bad;
 	error = ipmi_attach(dev);
 	if (error)
 		goto bad;
diff --git a/sys/dev/ipmi/ipmi_smbios.c b/sys/dev/ipmi/ipmi_smbios.c
index db15a853e5ea..66b7b63087b0 100644
--- a/sys/dev/ipmi/ipmi_smbios.c
+++ b/sys/dev/ipmi/ipmi_smbios.c
@@ -104,6 +104,7 @@ smbios_ipmi_info(struct smbios_structure_header *h, void *arg)
 	switch (s->interface_type) {
 	case KCS_MODE:
 	case SMIC_MODE:
+	case BT_MODE:
 		info->address = IPMI_BAR_ADDR(s->base_address) |
 		    IPMI_BAM_ADDR_LSB(s->base_address_modifier);
 		info->io_mode = IPMI_BAR_MODE(s->base_address);
diff --git a/sys/dev/ipmi/ipmivars.h b/sys/dev/ipmi/ipmivars.h
index 3a20f5a9de1c..be684957122a 100644
--- a/sys/dev/ipmi/ipmivars.h
+++ b/sys/dev/ipmi/ipmivars.h
@@ -59,6 +59,10 @@ struct ipmi_request {
 	uint8_t		ir_ipmb_command;
 };
 
+#define	IPMI_IF_KCS_NRES		2
+#define	IPMI_IF_SMIC_NRES		3
+#define	IPMI_IF_BT_NRES			3
+
 #define	MAX_RES				3
 #define KCS_DATA			0
 #define KCS_CTL_STS			1
@@ -80,6 +84,10 @@ struct ipmi_device {
 	u_char			ipmi_lun;
 };
 
+struct ipmi_bt {
+	uint8_t	seq;
+};
+
 struct ipmi_kcs {
 };
 
@@ -94,6 +102,7 @@ struct ipmi_ssif {
 struct ipmi_softc {
 	device_t		ipmi_dev;
 	union {
+		struct ipmi_bt bt;
 		struct ipmi_kcs kcs;
 		struct ipmi_smic smic;
 		struct ipmi_ssif ssif;
@@ -131,11 +140,12 @@ struct ipmi_softc {
 
 #define	ipmi_ssif_smbus_address		_iface.ssif.smbus_address
 #define	ipmi_ssif_smbus			_iface.ssif.smbus
+#define	ipmi_bt_seq			_iface.bt.seq
 
-#define KCS_MODE		0x01
-#define SMIC_MODE		0x02
-#define	BT_MODE			0x03
-#define SSIF_MODE		0x04
+#define	KCS_MODE			0x01
+#define	SMIC_MODE			0x02
+#define	BT_MODE				0x03
+#define	SSIF_MODE			0x04
 
 /* KCS status flags */
 #define KCS_STATUS_OBF			0x01 /* Data Out ready from BMC */
@@ -252,6 +262,7 @@ const char *ipmi_pci_match(uint16_t, uint16_t);
 int	ipmi_kcs_attach(struct ipmi_softc *);
 int	ipmi_kcs_probe_align(struct ipmi_softc *);
 int	ipmi_smic_attach(struct ipmi_softc *);
+int	ipmi_bt_attach(struct ipmi_softc *);
 int	ipmi_ssif_attach(struct ipmi_softc *, device_t, int);
 
 extern int ipmi_attached;
diff --git a/sys/modules/ipmi/Makefile b/sys/modules/ipmi/Makefile
index 733ac6f30222..3d0414fc3807 100644
--- a/sys/modules/ipmi/Makefile
+++ b/sys/modules/ipmi/Makefile
@@ -10,7 +10,7 @@ SRCS+=	bus_if.h device_if.h
 
 .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" || \
     ${MACHINE_CPUARCH} == "aarch64"
-SRCS+=	ipmi_kcs.c ipmi_smic.c
+SRCS+=	ipmi_kcs.c ipmi_smic.c ipmi_bt.c
 SRCS+=	ipmi_acpi.c
 SRCS+=	opt_acpi.h
 SRCS+=	acpi_if.h