public inbox for linux-doc@vger.kernel.org
 help / color / mirror / Atom feed
From: Akshay Gupta <Akshay.Gupta@amd.com>
To: <linux-kernel@vger.kernel.org>
Cc: <corbet@lwn.net>, <skhan@linuxfoundation.org>,
	<linux@roeck-us.net>, <arnd@arndb.de>,
	<gregkh@linuxfoundation.org>, <akshay.gupta@amd.com>,
	<Prathima.Lk@amd.com>, <naveenkrishna.chatradhi@amd.com>,
	<Anand.Umarji@amd.com>, <linux-doc@vger.kernel.org>,
	<linux-hwmon@vger.kernel.org>, <kunyi@google.com>,
	Akshay Gupta <Akshay.Gupta@amd.com>
Subject: [PATCH v1 4/6] misc: amd-sbi: Add support for SB-TSI over I3C
Date: Mon, 23 Mar 2026 16:38:09 +0530	[thread overview]
Message-ID: <20260323110811.2898997-5-Akshay.Gupta@amd.com> (raw)
In-Reply-To: <20260323110811.2898997-1-Akshay.Gupta@amd.com>

From: Prathima <Prathima.Lk@amd.com>

- Extend the SB-TSI driver to support both I2C and I3C bus interfaces.
- The driver maintains backward compatibility with existing I2C
deployments while enabling support for systems using the I3C bus.

Reviewed-by: Akshay Gupta <Akshay.Gupta@amd.com>
Signed-off-by: Prathima <Prathima.Lk@amd.com>
---
 drivers/misc/amd-sbi/Kconfig     |  5 ++-
 drivers/misc/amd-sbi/Makefile    |  2 +-
 drivers/misc/amd-sbi/tsi-core.c  | 64 ++++++++++++++++++++++++++++++++
 drivers/misc/amd-sbi/tsi-core.h  | 11 +++++-
 drivers/misc/amd-sbi/tsi-hwmon.c | 50 ++++++++++++++++++-------
 drivers/misc/amd-sbi/tsi.c       | 62 +++++++++++++++++++++++++++++--
 6 files changed, 173 insertions(+), 21 deletions(-)
 create mode 100644 drivers/misc/amd-sbi/tsi-core.c

diff --git a/drivers/misc/amd-sbi/Kconfig b/drivers/misc/amd-sbi/Kconfig
index 2710860a86c7..d18652537788 100644
--- a/drivers/misc/amd-sbi/Kconfig
+++ b/drivers/misc/amd-sbi/Kconfig
@@ -23,11 +23,12 @@ config AMD_SBRMI_HWMON
 
 config SENSORS_SBTSI
 	tristate "Emulated SB-TSI temperature sensor"
-	depends on I2C
+	depends on I3C_OR_I2C
 	depends on ARM || ARM64 || COMPILE_TEST
 	help
 	  If you say yes here you get support for emulated temperature
-	  sensors on AMD SoCs with SB-TSI interface connected to a BMC device.
+	  sensors on AMD SoCs with SB-TSI interface connected to a BMC device
+	  over I2C and I3C.
 	  This driver is intended to run on the BMC, not the managed node.
 
 	  This driver can also be built as a module. If so, the module will
diff --git a/drivers/misc/amd-sbi/Makefile b/drivers/misc/amd-sbi/Makefile
index a874136e438f..e87c8e618dcc 100644
--- a/drivers/misc/amd-sbi/Makefile
+++ b/drivers/misc/amd-sbi/Makefile
@@ -3,6 +3,6 @@ sbrmi-i2c-objs  		+= rmi-i2c.o rmi-core.o
 sbrmi-i2c-$(CONFIG_AMD_SBRMI_HWMON)	+= rmi-hwmon.o
 obj-$(CONFIG_AMD_SBRMI_I2C)	+= sbrmi-i2c.o
 # SBTSI Configuration
-sbtsi-objs	+= tsi.o
+sbtsi-objs	+= tsi.o tsi-core.o
 sbtsi-$(CONFIG_AMD_SBTSI_HWMON)	+= tsi-hwmon.o
 obj-$(CONFIG_SENSORS_SBTSI)	+= sbtsi.o
diff --git a/drivers/misc/amd-sbi/tsi-core.c b/drivers/misc/amd-sbi/tsi-core.c
new file mode 100644
index 000000000000..fcb7fcf87a55
--- /dev/null
+++ b/drivers/misc/amd-sbi/tsi-core.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * tsi-core.c - file defining SB-TSI protocols compliant
+ *              AMD SoC device.
+ * Copyright (c) 2020, Google Inc.
+ * Copyright (c) 2020, Kun Yi <kunyi@google.com>
+ */
+
+#include "tsi-core.h"
+
+/* I3C read transfer function */
+static int sbtsi_i3c_read(struct sbtsi_data *data, u8 reg, u8 *val)
+{
+	struct i3c_xfer xfers[2];
+
+	/* Send register address */
+	xfers[0].rnw = false;
+	xfers[0].len = 1;
+	xfers[0].data.out = &reg;
+
+	/* Read data */
+	xfers[1].rnw = true;
+	xfers[1].len = 1;
+	xfers[1].data.in = val;
+
+	return i3c_device_do_xfers(data->i3cdev, xfers, 2, I3C_SDR);
+}
+
+/* I3C write transfer function */
+static int sbtsi_i3c_write(struct sbtsi_data *data, u8 reg, u8 *val)
+{
+	u8 buf[2] = { reg, *val };
+	struct i3c_xfer xfers = {
+		.rnw = false,
+		.len = 2,
+		.data.out = buf,
+	};
+
+	return i3c_device_do_xfers(data->i3cdev, &xfers, 1, I3C_SDR);
+}
+
+/* I2C transfer function */
+static int sbtsi_i2c_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read)
+{
+	if (is_read) {
+		int ret = i2c_smbus_read_byte_data(data->client, reg);
+
+		if (ret < 0)
+			return ret;
+		*val = ret;
+		return 0;
+	}
+	return i2c_smbus_write_byte_data(data->client, reg, *val);
+}
+
+/* Unified transfer function for I2C and I3C access */
+int sbtsi_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read)
+{
+	if (data->is_i3c)
+		return is_read ? sbtsi_i3c_read(data, reg, val)
+			: sbtsi_i3c_write(data, reg, val);
+
+	return sbtsi_i2c_xfer(data, reg, val, is_read);
+}
diff --git a/drivers/misc/amd-sbi/tsi-core.h b/drivers/misc/amd-sbi/tsi-core.h
index e60cf25fda7a..a4ded17942c9 100644
--- a/drivers/misc/amd-sbi/tsi-core.h
+++ b/drivers/misc/amd-sbi/tsi-core.h
@@ -11,14 +11,23 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/types.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/master.h>
 
 /* Each client has this additional data */
 struct sbtsi_data {
-	struct i2c_client *client;
+	union {
+		struct i2c_client *client;
+		struct i3c_device *i3cdev;
+	};
 	bool ext_range_mode;
 	bool read_order;
+	bool is_i3c;
 };
 
+int sbtsi_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read);
+
 #ifdef CONFIG_AMD_SBTSI_HWMON
 int create_sbtsi_hwmon_sensor_device(struct device *dev, struct sbtsi_data *data);
 #else
diff --git a/drivers/misc/amd-sbi/tsi-hwmon.c b/drivers/misc/amd-sbi/tsi-hwmon.c
index efd450a85d34..9b447042d4aa 100644
--- a/drivers/misc/amd-sbi/tsi-hwmon.c
+++ b/drivers/misc/amd-sbi/tsi-hwmon.c
@@ -27,8 +27,7 @@
 #define SBTSI_TEMP_MIN 0
 #define SBTSI_TEMP_MAX 255875
 
-/*
- * From SB-TSI spec: CPU temperature readings and limit registers encode the
+/* From SB-TSI spec: CPU temperature readings and limit registers encode the
  * temperature in increments of 0.125 from 0 to 255.875. The "high byte"
  * register encodes the base-2 of the integer portion, and the upper 3 bits of
  * the "low byte" encode in base-2 the decimal portion.
@@ -61,24 +60,50 @@ static int sbtsi_read(struct device *dev, enum hwmon_sensor_types type,
 {
 	struct sbtsi_data *data = dev_get_drvdata(dev);
 	s32 temp_int, temp_dec;
+	u8 reg_val;
+	int err;
 
 	switch (attr) {
 	case hwmon_temp_input:
 		if (data->read_order) {
-			temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC);
-			temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT);
+			err = sbtsi_xfer(data, SBTSI_REG_TEMP_DEC, &reg_val, true);
+			if (err < 0)
+				return err;
+			temp_dec = reg_val;
+			err = sbtsi_xfer(data, SBTSI_REG_TEMP_INT, &reg_val, true);
+			if (err < 0)
+				return err;
+			temp_int = reg_val;
 		} else {
-			temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT);
-			temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC);
+			err = sbtsi_xfer(data, SBTSI_REG_TEMP_INT, &reg_val, true);
+			if (err < 0)
+				return err;
+			temp_int = reg_val;
+			err = sbtsi_xfer(data, SBTSI_REG_TEMP_DEC, &reg_val, true);
+			if (err < 0)
+				return err;
+			temp_dec = reg_val;
 		}
 		break;
 	case hwmon_temp_max:
-		temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_INT);
-		temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_DEC);
+		err = sbtsi_xfer(data, SBTSI_REG_TEMP_HIGH_INT, &reg_val, true);
+		if (err < 0)
+			return err;
+		temp_int = reg_val;
+		err = sbtsi_xfer(data, SBTSI_REG_TEMP_HIGH_DEC, &reg_val, true);
+		if (err < 0)
+			return err;
+		temp_dec = reg_val;
 		break;
 	case hwmon_temp_min:
-		temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_INT);
-		temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_DEC);
+		err = sbtsi_xfer(data, SBTSI_REG_TEMP_LOW_INT, &reg_val, true);
+		if (err < 0)
+			return err;
+		temp_int = reg_val;
+		err = sbtsi_xfer(data, SBTSI_REG_TEMP_LOW_DEC, &reg_val, true);
+		if (err < 0)
+			return err;
+		temp_dec = reg_val;
 		break;
 	default:
 		return -EINVAL;
@@ -115,12 +140,11 @@ static int sbtsi_write(struct device *dev, enum hwmon_sensor_types type,
 		val += SBTSI_TEMP_EXT_RANGE_ADJ;
 	val = clamp_val(val, SBTSI_TEMP_MIN, SBTSI_TEMP_MAX);
 	sbtsi_mc_to_reg(val, &temp_int, &temp_dec);
-
-	err = i2c_smbus_write_byte_data(data->client, reg_int, temp_int);
+	err = sbtsi_xfer(data, reg_int, &temp_int, false);
 	if (err)
 		return err;
 
-	err = i2c_smbus_write_byte_data(data->client, reg_dec, temp_dec);
+	err = sbtsi_xfer(data, reg_dec, &temp_dec, false);
 	if (err)
 		return err;
 	return 0;
diff --git a/drivers/misc/amd-sbi/tsi.c b/drivers/misc/amd-sbi/tsi.c
index df754e60d84b..d7a8237fc4fd 100644
--- a/drivers/misc/amd-sbi/tsi.c
+++ b/drivers/misc/amd-sbi/tsi.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 /*
- * tsi.c - Side band TSI over I2C support for AMD out of band management.
+ * tsi.c - Side band TSI over I2C/I3C support for AMD out of band management.
  *
  * Copyright (c) 2020-2026, Google Inc.
  * Copyright (c) 2020-2026, Kun Yi <kunyi@google.com>
@@ -26,7 +26,7 @@
 
 #define SBTSI_CONFIG_READ_ORDER_SHIFT  5
 
-static int sbtsi_probe(struct i2c_client *client)
+static int sbtsi_i2c_probe(struct i2c_client *client)
 {
 	struct device *dev = &client->dev;
 	struct sbtsi_data *data;
@@ -36,6 +36,7 @@ static int sbtsi_probe(struct i2c_client *client)
 	if (!data)
 		return -ENOMEM;
 
+	data->is_i3c = false;
 	data->client = client;
 	err = i2c_smbus_read_byte_data(data->client, SBTSI_REG_CONFIG);
 	if (err < 0)
@@ -66,11 +67,64 @@ static struct i2c_driver sbtsi_driver = {
 		.name = "sbtsi",
 		.of_match_table = of_match_ptr(sbtsi_of_match),
 	},
-	.probe = sbtsi_probe,
+	.probe = sbtsi_i2c_probe,
 	.id_table = sbtsi_id,
 };
 
-module_i2c_driver(sbtsi_driver);
+static int sbtsi_i3c_probe(struct i3c_device *i3cdev)
+{
+	struct device *dev = i3cdev_to_dev(i3cdev);
+	struct sbtsi_data *data;
+	int err;
+	u8 val;
+	/*
+	 * AMD OOB devices differ on basis of Instance ID,
+	 * for SBTSI, instance ID is 0.
+	 * As the device Id match is not on basis of Instance ID,
+	 * add the below check to probe the SBTSI device only and
+	 * not other OOB devices.
+	 */
+	if (I3C_PID_INSTANCE_ID(i3cdev->desc->info.pid) != 0)
+		return -ENXIO;
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->i3cdev = i3cdev;
+	data->is_i3c = true;
+
+	err = sbtsi_xfer(data, SBTSI_REG_CONFIG, &val, true);
+	if (err)
+		return err;
+
+	data->ext_range_mode = FIELD_GET(BIT(SBTSI_CONFIG_EXT_RANGE_SHIFT), val);
+	data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), val);
+
+	dev_set_drvdata(dev, data);
+	return create_sbtsi_hwmon_sensor_device(dev, data);
+}
+
+static const struct i3c_device_id sbtsi_i3c_id[] = {
+	/* PID for AMD SBTSI device */
+	I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x1, NULL),
+	I3C_DEVICE_EXTRA_INFO(0x0, 0x0, 0x118, NULL), /* Socket:0, Venice A0 */
+	I3C_DEVICE_EXTRA_INFO(0x0, 0x100, 0x118, NULL), /* Socket:1, Venice A0 */
+	I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x119, NULL), /* Socket:0, Venice B0 */
+	I3C_DEVICE_EXTRA_INFO(0x112, 0x100, 0x119, NULL), /* Socket:1, Venice B0 */
+	{}
+};
+MODULE_DEVICE_TABLE(i3c, sbtsi_i3c_id);
+
+static struct i3c_driver sbtsi_i3c_driver = {
+	.driver = {
+		.name = "sbtsi-i3c",
+	},
+	.probe = sbtsi_i3c_probe,
+	.id_table = sbtsi_i3c_id,
+};
+
+module_i3c_i2c_driver(sbtsi_i3c_driver, &sbtsi_driver);
 
 MODULE_AUTHOR("Kun Yi <kunyi@google.com>");
 MODULE_DESCRIPTION("Hwmon driver for AMD SB-TSI emulated sensor");
-- 
2.34.1


  parent reply	other threads:[~2026-03-23 11:09 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-23 11:08 [PATCH v1 0/6] misc: amd-sbi: Refactor SBTSI driver with I3C support and ioctl interface Akshay Gupta
2026-03-23 11:08 ` [PATCH v1 1/6] hwmon/misc: amd-sbi: Move core SBTSI support from hwmon to misc Akshay Gupta
2026-03-23 14:15   ` Guenter Roeck
2026-03-24 10:36     ` Gupta, Akshay
2026-03-24 11:33       ` Guenter Roeck
2026-03-27  5:07         ` Gupta, Akshay
2026-03-27  5:52           ` Guenter Roeck
2026-03-27  7:23             ` gregkh
2026-03-23 11:08 ` [PATCH v1 2/6] misc: amd-sbi: Update SBTSI Kconfig to clarify this is BMC driver Akshay Gupta
2026-03-23 11:08 ` [PATCH v1 3/6] misc: amd-sbi: Split SBTSI hwmon sensor handling into a separate entity Akshay Gupta
2026-03-23 11:08 ` Akshay Gupta [this message]
2026-03-23 11:08 ` [PATCH v1 5/6] misc: amd-sbi: Add SBTSI ioctl register transfer interface Akshay Gupta
2026-03-27  7:24   ` Greg KH
2026-03-23 11:08 ` [PATCH v1 6/6] docs: misc: amd-sbi: Document SBTSI userspace interface Akshay Gupta
2026-03-27  7:25   ` Greg KH

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=20260323110811.2898997-5-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=kunyi@google.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-hwmon@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@roeck-us.net \
    --cc=naveenkrishna.chatradhi@amd.com \
    --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