From: Akshay Gupta <Akshay.Gupta@amd.com>
To: <linux-kernel@vger.kernel.org>, <linux-doc@vger.kernel.org>
Cc: <arnd@arndb.de>, <gregkh@linuxfoundation.org>, <corbet@lwn.net>,
<skhan@linuxfoundation.org>, <rdunlap@infradead.org>,
<linux@roeck-us.net>, <naveenkrishna.chatradhi@amd.com>,
<Anand.Umarji@amd.com>, Akshay Gupta <Akshay.Gupta@amd.com>,
Prathima Lk <Prathima.Lk@amd.com>
Subject: [PATCH v1 2/3] misc: amd-sbi: Add TBAI trace-buffer access over I3C
Date: Wed, 11 Nov 2026 11:10:33 +0530 [thread overview]
Message-ID: <20261111054034.2453874-3-Akshay.Gupta@amd.com> (raw)
In-Reply-To: <20261111054034.2453874-1-Akshay.Gupta@amd.com>
The Trace Buffer Acquire Interface (TBAI) in the Thermal Management
(THM) block allows the Board Management Controller (BMC) to access
MP trace data without managing the trace buffer's destination address
space.
Add TBAI support to the amd-sbi driver through a new SBTBAI_IOCTL_CMD
ioctl for acquire and flush operations. The ioctl is supported only
when the SB-TSI device is attached over I3C.
The acquire operation reads trace data in chunks of up to 32 dwords,
advances the entry offset between chunks, and reports trace-buffer
target errors returned in fw_ret_code. Add the corresponding UAPI
structures, command indices, and error codes, and enlarge the shared
I3C buffers to accommodate TBAI transfers.
Tested on I3C hardware with acquire, flush, short-read, and
trace-buffer target error cases. Confirmed that the I2C path returns
-EOPNOTSUPP.
Co-developed-by: Prathima Lk <Prathima.Lk@amd.com>
Signed-off-by: Prathima Lk <Prathima.Lk@amd.com>
Signed-off-by: Akshay Gupta <Akshay.Gupta@amd.com>
---
drivers/misc/amd-sbi/tsi-core.c | 194 ++++++++++++++++++++++++++++++++
drivers/misc/amd-sbi/tsi-core.h | 13 ++-
include/uapi/misc/amd-apml.h | 78 +++++++++++++
3 files changed, 280 insertions(+), 5 deletions(-)
diff --git a/drivers/misc/amd-sbi/tsi-core.c b/drivers/misc/amd-sbi/tsi-core.c
index 1920e4dc7b67..7e79c7eb3fde 100644
--- a/drivers/misc/amd-sbi/tsi-core.c
+++ b/drivers/misc/amd-sbi/tsi-core.c
@@ -8,11 +8,29 @@
#include <linux/fs.h>
#include <linux/ioctl.h>
+#include <linux/limits.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/uaccess.h>
+#include <linux/unaligned.h>
#include <uapi/misc/amd-apml.h>
#include "tsi-core.h"
+#define TBAI_WR_LEN 0x4 /* Input length (excludes command byte) */
+#define TBAI_REQ_LEN 0x5 /* Full request: command byte + TBAI_WR_LEN */
+#define TBAI_FLUSH_RD_LEN 0x4 /* flush buffer read length */
+#define MAX_PROTO_RD_SZ 32 /* Maximum bytes read in one transaction */
+#define BYTES_PER_DWORD 0x4 /* Number of bytes in dword */
+
+#define TBAI_IN_LUT_INDEX 0
+#define TBAI_IN_OFF_LO 1
+#define TBAI_IN_OFF_HI 2
+#define TBAI_IN_DWORD_COUNT 3
+
+/* Maximum dwords possible to read in one transaction */
+#define MAX_DWORDS_READ 0x8
+#define TBAI_OFFSET_HI_SHIFT 8 /* offset high byte bit position */
+
static inline struct sbtsi_i3c_priv *to_sbtsi_i3c_priv(struct sbtsi_data *data)
{
return container_of(data, struct sbtsi_i3c_priv, data);
@@ -136,6 +154,180 @@ static int apml_tsi_reg_xfer(struct sbtsi_data *data,
return ret;
}
+/* True if the single-dword reply is a trace buffer target (TBT) error status word */
+static bool tbai_is_tbt_err(u32 status)
+{
+ switch (status) {
+ case AMD_SBI_TBAI_TBT_ERR0:
+ case AMD_SBI_TBAI_TBT_ERR1:
+ case AMD_SBI_TBAI_TBT_ERR2:
+ case AMD_SBI_TBAI_TBT_ERR5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int tbai_protocol(struct sbtsi_data *data, u8 cmd, u8 *input, u8 count,
+ u8 *output, u16 *transferred_len)
+{
+ struct sbtsi_i3c_priv *priv = to_sbtsi_i3c_priv(data);
+ struct i3c_xfer xfers[2] = { };
+ int ret;
+
+ if (count > MAX_PROTO_RD_SZ)
+ return -EINVAL;
+
+ priv->tx[0] = cmd;
+ memcpy(&priv->tx[1], input, TBAI_WR_LEN);
+ xfers[0].len = TBAI_REQ_LEN;
+ xfers[0].data.out = priv->tx;
+ xfers[1].rnw = true;
+ xfers[1].len = count;
+ xfers[1].data.in = priv->rx;
+ memset(priv->rx, 0, count);
+
+ ret = i3c_device_do_xfers(data->i3cdev, xfers, ARRAY_SIZE(xfers), I3C_SDR);
+ if (ret)
+ return ret;
+ /* The I3C master returns the received length in xfers.len */
+ *transferred_len = xfers[1].len;
+ memcpy(output, priv->rx, count);
+ return 0;
+}
+
+static int flush_trace_buffer(struct sbtsi_data *data,
+ struct apml_tbai_msg *msg)
+{
+ /* Flush resets the whole TBAI logic; request bytes 2-5 are reserved */
+ u8 input[TBAI_WR_LEN] = { };
+ u16 transferred_len;
+ int ret;
+
+ msg->fw_ret_code = 0;
+
+ ret = tbai_protocol(data, msg->reg_in[AMD_SBI_TBAI_CMD_INDEX], input,
+ TBAI_FLUSH_RD_LEN, msg->data_out.bytes_out,
+ &transferred_len);
+ if (ret)
+ return ret;
+
+ if (transferred_len != TBAI_FLUSH_RD_LEN)
+ ret = -EMSGSIZE;
+
+ return ret;
+}
+
+static int acquire_trace_buffer(struct sbtsi_data *data,
+ struct apml_tbai_msg *msg)
+{
+ u32 total_dwords, expected_len, data_read = 0, status = 0;
+ u16 offset, offset_new, transferred_len;
+ u8 input[TBAI_WR_LEN] = { };
+ int dword_read, i, ret = 0, dword_remain;
+
+ msg->fw_ret_code = 0;
+ /* Dwords to read from user*/
+ total_dwords = msg->reg_in[AMD_SBI_TBAI_DWORD_RD_INDEX];
+ dword_remain = total_dwords;
+ /* Extract the offset to update, if more than 8 Dwords require to read */
+ offset = (u16)msg->reg_in[AMD_SBI_TBAI_OFFSET_LO] |
+ ((u16)msg->reg_in[AMD_SBI_TBAI_OFFSET_HI] << TBAI_OFFSET_HI_SHIFT);
+
+ /* If Dwords to read is 0 or more than 32, return */
+ if (!total_dwords || total_dwords > AMD_SBI_MAX_TBAI_DWORDS)
+ return -EINVAL;
+
+ /* Offset of the last chunk must fit in the 16-bit entry offset */
+ if ((u32)offset + (total_dwords - 1) / MAX_DWORDS_READ * MAX_PROTO_RD_SZ > U16_MAX)
+ return -EINVAL;
+
+ /*
+ * Set required variables to read dwords
+ * Maximum dwords supported from TBAI protocol is 8
+ */
+ for (i = 0; i <= total_dwords / MAX_DWORDS_READ && dword_remain > 0; i++) {
+ if (dword_remain > MAX_DWORDS_READ) {
+ dword_remain -= MAX_DWORDS_READ;
+ dword_read = MAX_DWORDS_READ;
+ } else {
+ dword_read = dword_remain;
+ dword_remain = 0;
+ }
+ /* update offset if more than 8 Dwords require to read */
+ offset_new = i * MAX_PROTO_RD_SZ + offset;
+ input[TBAI_IN_LUT_INDEX] = msg->reg_in[AMD_SBI_TBAI_LUT_INDEX];
+ input[TBAI_IN_OFF_LO] = offset_new & U8_MAX;
+ input[TBAI_IN_OFF_HI] = (offset_new >> TBAI_OFFSET_HI_SHIFT) & U8_MAX;
+ input[TBAI_IN_DWORD_COUNT] = dword_read - 1;
+
+ expected_len = dword_read * BYTES_PER_DWORD;
+ ret = tbai_protocol(data, msg->reg_in[AMD_SBI_TBAI_CMD_INDEX], input,
+ expected_len,
+ &msg->data_out.bytes_out[i * MAX_PROTO_RD_SZ],
+ &transferred_len);
+ if (ret)
+ return ret;
+
+ data_read += transferred_len;
+ /* A 1-dword reply matching a TBT error code is always taken as an error */
+ if (transferred_len == BYTES_PER_DWORD) {
+ status = get_unaligned_le32(&msg->data_out.bytes_out[i * MAX_PROTO_RD_SZ]);
+ if (tbai_is_tbt_err(status)) {
+ msg->fw_ret_code = status;
+ /* No trace data is returned on error, as with -EMSGSIZE */
+ memset(&msg->data_out, 0, sizeof(msg->data_out));
+ ret = -EPROTOTYPE;
+ return ret;
+ }
+ }
+
+ if (transferred_len < expected_len)
+ break;
+ }
+ if (data_read != (total_dwords * BYTES_PER_DWORD))
+ ret = -EMSGSIZE;
+
+ return ret;
+}
+
+static int apml_tbai_reg_xfer(struct sbtsi_data *data,
+ struct apml_tbai_msg __user *arg)
+{
+ struct apml_tbai_msg msg = { };
+ int ret;
+
+ /* TBAI is only available over I3C */
+ if (!data->is_i3c)
+ return -EOPNOTSUPP;
+
+ if (copy_from_user(&msg, arg, sizeof(struct apml_tbai_msg)))
+ return -EFAULT;
+ if (msg.logging_en || msg.pad[0] || msg.pad[1] || msg.pad[2])
+ return -EINVAL;
+
+ scoped_guard(sbtsi, data) {
+ if (data->detached)
+ return -ENODEV;
+
+ if (msg.reg_in[AMD_SBI_TBAI_CMD_INDEX] == AMD_SBI_TBAI_ACQUIRE)
+ ret = acquire_trace_buffer(data, &msg);
+ else if (msg.reg_in[AMD_SBI_TBAI_CMD_INDEX] == AMD_SBI_TBAI_FLUSH)
+ ret = flush_trace_buffer(data, &msg);
+ else
+ ret = -EINVAL;
+ }
+
+ /* Partial data is not returned on a short read */
+ if (ret && ret != -EPROTOTYPE)
+ return ret;
+
+ if (copy_to_user(arg, &msg, sizeof(struct apml_tbai_msg)))
+ return -EFAULT;
+
+ return ret;
+}
+
static int sbtsi_open(struct inode *inode, struct file *fp)
{
struct sbtsi_data *data;
@@ -169,6 +361,8 @@ static long sbtsi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
switch (cmd) {
case SBTSI_IOCTL_REG_XFER_CMD:
return apml_tsi_reg_xfer(data, argp);
+ case SBTBAI_IOCTL_CMD:
+ return apml_tbai_reg_xfer(data, argp);
default:
return -ENOTTY;
}
diff --git a/drivers/misc/amd-sbi/tsi-core.h b/drivers/misc/amd-sbi/tsi-core.h
index 8efe7e220f4a..0b38c0b60b8f 100644
--- a/drivers/misc/amd-sbi/tsi-core.h
+++ b/drivers/misc/amd-sbi/tsi-core.h
@@ -11,15 +11,18 @@
#include <linux/cache.h>
#include <linux/misc/tsi.h>
-/* SB-TSI register access transaction sizes in bytes. */
-#define SBTSI_TX_LEN 2 /* [0] register address, [1] value */
-#define SBTSI_RX_LEN 1 /* single data byte */
+/* Maximum SB-TSI I3C transaction sizes in bytes (register access and TBAI). */
+#define SBTSI_TX_LEN 5
+#define SBTSI_RX_LEN 32
/**
* struct sbtsi_i3c_priv - per-device state for I3C SBTSI (includes DMA-safe buffers)
* @data: public device state exposed via dev_set_drvdata()
- * @tx: outgoing I3C bytes (DMA_TO_DEVICE); [0] register address, [1] value
- * @rx: incoming I3C data byte (DMA_FROM_DEVICE)
+ * @tx: outgoing I3C bytes (DMA_TO_DEVICE); for TSI register access
+ * [0] register address, [1] value; for TBAI [0] command,
+ * [1..4] input
+ * @rx: incoming I3C data bytes (DMA_FROM_DEVICE); TSI register read
+ * returns one byte, TBAI returns up to SBTSI_RX_LEN bytes
*/
struct sbtsi_i3c_priv {
struct sbtsi_data data;
diff --git a/include/uapi/misc/amd-apml.h b/include/uapi/misc/amd-apml.h
index 8a85f79b0938..85741b8f4c21 100644
--- a/include/uapi/misc/amd-apml.h
+++ b/include/uapi/misc/amd-apml.h
@@ -10,6 +10,24 @@
/* Mailbox data size for data_in and data_out */
#define AMD_SBI_MB_DATA_SIZE 4
+/* TBAI trace-buffer access message. */
+#define AMD_SBI_MAX_TBAI_DWORDS 32
+#define AMD_SBI_MAX_TBAI_BYTES 128
+#define AMD_SBI_TBAI_INPUT_BYTES 5
+#define AMD_SBI_TBAI_CMD_INDEX 0
+#define AMD_SBI_TBAI_LUT_INDEX 1
+#define AMD_SBI_TBAI_OFFSET_LO 2
+#define AMD_SBI_TBAI_OFFSET_HI 3
+#define AMD_SBI_TBAI_DWORD_RD_INDEX 4
+#define AMD_SBI_TBAI_ACQUIRE 0x31
+#define AMD_SBI_TBAI_FLUSH 0x32
+
+/* Trace buffer target error status reported in fw_ret_code */
+#define AMD_SBI_TBAI_TBT_ERR0 0xDEADBEE0
+#define AMD_SBI_TBAI_TBT_ERR1 0xDEADBEE1
+#define AMD_SBI_TBAI_TBT_ERR2 0xDEADBEE2
+#define AMD_SBI_TBAI_TBT_ERR5 0xDEADBEE5
+
struct apml_mbox_msg {
/*
* Mailbox Message ID
@@ -80,6 +98,35 @@ struct apml_tsi_xfer_msg {
__u8 pad; /* Explicit padding */
};
+/* TBAI trace-buffer access message. */
+struct apml_tbai_msg {
+ /*
+ * Output trace-buffer data read back; accessed either as dwords_out
+ * (up to AMD_SBI_MAX_TBAI_DWORDS dwords) or bytes_out
+ * (up to AMD_SBI_MAX_TBAI_BYTES bytes)
+ */
+ union {
+ __u32 dwords_out[AMD_SBI_MAX_TBAI_DWORDS];
+ __u8 bytes_out[AMD_SBI_MAX_TBAI_BYTES];
+ } data_out;
+ __u32 logging_en; /* Reserved, must be zero */
+ __u32 fw_ret_code; /* TBT error status */
+ /*
+ * Input register bytes written to the device:
+ * [0] command ID (AMD_SBI_TBAI_ACQUIRE: acquire buffer,
+ * AMD_SBI_TBAI_FLUSH: flush/clear buffer)
+ * [1] LUT index; the LUT holds the programmed destination trace
+ * buffer target (TBT) address for each TraceBufferIndex
+ * [2] entry offset low byte
+ * [3] entry offset high byte
+ * [4] Dwords to read (1 to AMD_SBI_MAX_TBAI_DWORDS)
+ * [1]...[4] are used only by AMD_SBI_TBAI_ACQUIRE; flush ignores
+ * them as it resets the TBAI logic for all LUT indices.
+ */
+ __u8 reg_in[AMD_SBI_TBAI_INPUT_BYTES];
+ __u8 pad[3]; /* Explicit padding */
+};
+
/*
* AMD sideband interface base IOCTL
*/
@@ -172,4 +219,35 @@ struct apml_tsi_xfer_msg {
*/
#define SBTSI_IOCTL_REG_XFER_CMD _IOWR(SB_BASE_IOCTL_NR, 4, struct apml_tsi_xfer_msg)
+/**
+ * DOC: SBTBAI_IOCTL_CMD
+ *
+ * @Parameters
+ *
+ * @struct apml_tbai_msg
+ * Pointer to the &struct apml_tbai_msg that will contain the trace-buffer
+ * access information
+ *
+ * @Description
+ * IOCTL command for APML TBAI messages using generic _IOWR
+ * The IOCTL provides userspace APML TBAI trace-buffer access.
+ * TBAI is only available when the SB-TSI device is attached over I3C.
+ *
+ * @Return
+ * 0 on a full read/flush, otherwise:
+ *
+ * * -EOPNOTSUPP: the SB-TSI device is attached over I2C.
+ *
+ * The following are TBAI operation-status codes reported for the
+ * acquire/flush request itself; they are not generic ioctl(2) errors:
+ *
+ * * -EMSGSIZE: short read; less data returned than requested.
+ * data_out is not updated.
+ * * -EPROTOTYPE: trace buffer target (TBT) error; fw_ret_code holds the
+ * AMD_SBI_TBAI_TBT_ERR* code and data_out is zeroed. A 1-dword reply
+ * carrying one of these values cannot be told apart from trace data
+ * and is always reported as an error.
+ */
+#define SBTBAI_IOCTL_CMD _IOWR(SB_BASE_IOCTL_NR, 5, struct apml_tbai_msg)
+
#endif /*_AMD_APML_H_*/
--
2.34.1
next prev parent reply other threads:[~2026-11-11 5:41 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 5:40 [PATCH v1 0/3] " Akshay Gupta
2026-11-11 5:40 ` [PATCH v1 1/3] misc: amd-sbi: use sized DMA-safe I3C tx/rx buffers Akshay Gupta
2026-11-11 5:40 ` Akshay Gupta [this message]
2026-11-11 5:40 ` [PATCH v1 3/3] docs: amd-sbi: document TBAI trace-buffer ioctl Akshay Gupta
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261111054034.2453874-3-Akshay.Gupta@amd.com \
--to=akshay.gupta@amd.com \
--cc=Anand.Umarji@amd.com \
--cc=Prathima.Lk@amd.com \
--cc=arnd@arndb.de \
--cc=corbet@lwn.net \
--cc=gregkh@linuxfoundation.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=naveenkrishna.chatradhi@amd.com \
--cc=rdunlap@infradead.org \
--cc=skhan@linuxfoundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®