All of lore.kernel.org
 help / color / mirror / Atom feed
From: Akshay Gupta <Akshay.Gupta@amd.com>
To: <linux-doc@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	<linux-hwmon@vger.kernel.org>
Cc: <corbet@lwn.net>, <skhan@linuxfoundation.org>,
	<linux@roeck-us.net>, <arnd@arndb.de>,
	<gregkh@linuxfoundation.org>, <akshay.gupta@amd.com>,
	<naveenkrishna.chatradhi@amd.com>, <Prathima.Lk@amd.com>,
	<Anand.Umarji@amd.com>, <Kevin.Tung@quantatw.com>,
	Akshay Gupta <Akshay.Gupta@amd.com>
Subject: [PATCH v2 5/6] misc: amd-sbi: Add SBTSI ioctl register transfer interface
Date: Fri, 15 May 2026 19:15:05 +0530	[thread overview]
Message-ID: <20260515134506.397649-6-Akshay.Gupta@amd.com> (raw)
In-Reply-To: <20260515134506.397649-1-Akshay.Gupta@amd.com>

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

Implement IOCTL interface for SB-TSI driver to enable userspace access
to TSI register read/write operations through the AMD Advanced Platform
Management Link (APML) protocol.
Add an ioctl command (SBTSI_IOCTL_REG_XFER_CMD) that accepts a register
address, data byte, and direction flag. Serialize access with a mutex
shared between the hwmon and ioctl paths to prevent concurrent bus
transactions from corrupting register state.

Reviewed-by: Akshay Gupta <Akshay.Gupta@amd.com>
Signed-off-by: Prathima <Prathima.Lk@amd.com>
---
Changes since v1:
- Use of devm_mutex_init in place of mutex_init
- Use of guard_mutex in place of mutex_lock()/mutex_unlock()
- Use of devm_add_action_or_reset() for clean removal
 
 drivers/hwmon/sbtsi_temp.c      |  6 +++
 drivers/misc/amd-sbi/tsi-core.c | 84 ++++++++++++++++++++++++++++++++-
 drivers/misc/amd-sbi/tsi-core.h | 15 ++++++
 drivers/misc/amd-sbi/tsi.c      | 20 ++++++--
 include/linux/misc/tsi.h        |  8 ++++
 include/uapi/misc/amd-apml.h    | 23 +++++++++
 6 files changed, 151 insertions(+), 5 deletions(-)
 create mode 100644 drivers/misc/amd-sbi/tsi-core.h

diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c
index d7ae986d824c..00e982f4c716 100644
--- a/drivers/hwmon/sbtsi_temp.c
+++ b/drivers/hwmon/sbtsi_temp.c
@@ -64,12 +64,15 @@ static inline void sbtsi_mc_to_reg(s32 temp, u8 *integer, u8 *decimal)
 /*
  * Read integer and decimal parts of an SB-TSI temperature register pair
  * The read order is determined by the ReadOrder bit to ensure atomic latching.
+ * The mutex protects against concurrent access to the shared I2C/I3C bus by
+ * the hwmon sysfs and a userspace ioctl
  */
 static int sbtsi_temp_read(struct sbtsi_data *data, u8 reg1, u8 reg2,
 			   u8 *val1, u8 *val2)
 {
 	int ret;
 
+	guard(mutex)(&data->lock);
 	ret = sbtsi_xfer(data, reg1, val1, true);
 	if (!ret)
 		ret = sbtsi_xfer(data, reg2, val2, true);
@@ -78,12 +81,15 @@ static int sbtsi_temp_read(struct sbtsi_data *data, u8 reg1, u8 reg2,
 
 /*
  * Write integer and decimal parts of an SB-TSI temperature register pair.
+ * The mutex protects against concurrent access to the shared I2C/I3C bus by
+ * the hwmon sysfs and a userspace ioctl
  */
 static int sbtsi_temp_write(struct sbtsi_data *data, u8 reg_int, u8 reg_dec,
 			    u8 val_int, u8 val_dec)
 {
 	int ret;
 
+	guard(mutex)(&data->lock);
 	ret = sbtsi_xfer(data, reg_int, &val_int, false);
 	if (!ret)
 		ret = sbtsi_xfer(data, reg_dec, &val_dec, false);
diff --git a/drivers/misc/amd-sbi/tsi-core.c b/drivers/misc/amd-sbi/tsi-core.c
index 19388737b225..c5bd60409d5b 100644
--- a/drivers/misc/amd-sbi/tsi-core.c
+++ b/drivers/misc/amd-sbi/tsi-core.c
@@ -6,8 +6,12 @@
  * Copyright (C) 2026 Advanced Micro Devices, Inc.
  */
 
+#include <linux/fs.h>
+#include <linux/ioctl.h>
 #include <linux/module.h>
-#include <linux/misc/tsi.h>
+#include <linux/uaccess.h>
+#include <uapi/misc/amd-apml.h>
+#include "tsi-core.h"
 
 /* I2C transfer function */
 static int sbtsi_i2c_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read)
@@ -62,7 +66,83 @@ 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);
 }
 EXPORT_SYMBOL_GPL(sbtsi_xfer);
+
+/*
+ * The mutex protects against concurrent access to the shared I2C/I3C bus by
+ * the hwmon sysfs and a userspace ioctl.
+ */
+static int sbtsi_xfer_ioctl(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read)
+{
+	guard(mutex)(&data->lock);
+	return sbtsi_xfer(data, reg, val, is_read);
+}
+
+static int apml_tsi_reg_xfer(struct sbtsi_data *data,
+			     struct apml_tsi_xfer_msg __user *arg)
+{
+	struct apml_tsi_xfer_msg msg = { 0 };
+	int ret;
+
+	if (copy_from_user(&msg, arg, sizeof(struct apml_tsi_xfer_msg)))
+		return -EFAULT;
+
+	ret = sbtsi_xfer_ioctl(data, msg.reg_addr, &msg.data_in_out, msg.rflag);
+
+	if (msg.rflag && !ret) {
+		if (copy_to_user(arg, &msg, sizeof(struct apml_tsi_xfer_msg)))
+			return -EFAULT;
+	}
+	return ret;
+}
+
+static long sbtsi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+	void __user *argp = (void __user *)arg;
+	struct sbtsi_data *data;
+
+	data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev);
+	switch (cmd) {
+	case SBTSI_IOCTL_REG_XFER_CMD:
+		return apml_tsi_reg_xfer(data, argp);
+	default:
+		return -ENOTTY;
+	}
+}
+
+static const struct file_operations sbtsi_fops = {
+	.owner		= THIS_MODULE,
+	.unlocked_ioctl	= sbtsi_ioctl,
+	.compat_ioctl	= compat_ptr_ioctl,
+};
+
+static void sbtsi_misc_deregister(void *data)
+{
+	misc_deregister((struct miscdevice *)data);
+}
+
+int create_misc_tsi_device(struct sbtsi_data *data, struct device *dev)
+{
+	int ret;
+
+	data->sbtsi_misc_dev.name = devm_kasprintf(dev, GFP_KERNEL,
+						   "sbtsi-%x", data->dev_addr);
+	if (!data->sbtsi_misc_dev.name)
+		return -ENOMEM;
+	data->sbtsi_misc_dev.minor    = MISC_DYNAMIC_MINOR;
+	data->sbtsi_misc_dev.fops     = &sbtsi_fops;
+	data->sbtsi_misc_dev.parent   = dev;
+	data->sbtsi_misc_dev.nodename = devm_kasprintf(dev, GFP_KERNEL,
+						       "sbtsi-%x", data->dev_addr);
+	if (!data->sbtsi_misc_dev.nodename)
+		return -ENOMEM;
+	data->sbtsi_misc_dev.mode = 0600;
+
+	ret = misc_register(&data->sbtsi_misc_dev);
+	if (ret)
+		return ret;
+	return devm_add_action_or_reset(dev, sbtsi_misc_deregister,
+					&data->sbtsi_misc_dev);
+}
diff --git a/drivers/misc/amd-sbi/tsi-core.h b/drivers/misc/amd-sbi/tsi-core.h
new file mode 100644
index 000000000000..7bf967a09837
--- /dev/null
+++ b/drivers/misc/amd-sbi/tsi-core.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * AMD SBTSI misc tsi device .
+ *
+ * Copyright (C) 2026 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _LINUX_TSI_CORE_H_
+#define _LINUX_TSI_CORE_H_
+
+#include <linux/misc/tsi.h>
+
+int create_misc_tsi_device(struct sbtsi_data *data, struct device *dev);
+
+#endif /* _LINUX_TSI_CORE_H_ */
diff --git a/drivers/misc/amd-sbi/tsi.c b/drivers/misc/amd-sbi/tsi.c
index 43bbac7faf08..6a9356740f4e 100644
--- a/drivers/misc/amd-sbi/tsi.c
+++ b/drivers/misc/amd-sbi/tsi.c
@@ -10,8 +10,8 @@
 #include <linux/bitfield.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/misc/tsi.h>
 #include <linux/slab.h>
+#include "tsi-core.h"
 
 #define SBTSI_REG_CONFIG		0x03 /* RO */
 
@@ -89,6 +89,9 @@ static int sbtsi_i2c_probe(struct i2c_client *client)
 	if (!data)
 		return -ENOMEM;
 
+	err = devm_mutex_init(dev, &data->lock);
+	if (err)
+		return err;
 	data->is_i3c = false;
 	data->client = client;
 	err = i2c_smbus_read_byte_data(data->client, SBTSI_REG_CONFIG);
@@ -98,7 +101,11 @@ static int sbtsi_i2c_probe(struct i2c_client *client)
 	data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), err);
 
 	dev_set_drvdata(dev, data);
-	return sbtsi_create_hwmon_adev(dev, client->addr);
+	err = sbtsi_create_hwmon_adev(dev, client->addr);
+	if (err < 0)
+		return err;
+	data->dev_addr = client->addr;
+	return create_misc_tsi_device(data, dev);
 }
 
 static const struct i2c_device_id sbtsi_id[] = {
@@ -145,6 +152,9 @@ static int sbtsi_i3c_probe(struct i3c_device *i3cdev)
 	if (!data)
 		return -ENOMEM;
 
+	err = devm_mutex_init(dev, &data->lock);
+	if (err)
+		return err;
 	data->i3cdev = i3cdev;
 	data->is_i3c = true;
 
@@ -156,7 +166,11 @@ static int sbtsi_i3c_probe(struct i3c_device *i3cdev)
 	data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), val);
 
 	dev_set_drvdata(dev, data);
-	return sbtsi_create_hwmon_adev(dev, i3cdev->desc->info.dyn_addr);
+	err = sbtsi_create_hwmon_adev(dev, i3cdev->desc->info.dyn_addr);
+	if (err < 0)
+		return err;
+	data->dev_addr = i3cdev->desc->info.dyn_addr;
+	return create_misc_tsi_device(data, dev);
 }
 
 static const struct i3c_device_id sbtsi_i3c_id[] = {
diff --git a/include/linux/misc/tsi.h b/include/linux/misc/tsi.h
index 7ce689081427..184b1aa14f0a 100644
--- a/include/linux/misc/tsi.h
+++ b/include/linux/misc/tsi.h
@@ -11,12 +11,17 @@
 #include <linux/i2c.h>
 #include <linux/i3c/device.h>
 #include <linux/i3c/master.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
 #include <linux/types.h>
 
 /**
  * struct sbtsi_data - driver private data for an AMD SB-TSI device
  * @client:	underlying I2C client
  * @i3cdev:	underlying I3C device (when using I3C bus)
+ * @sbtsi_misc_dev: miscdevice exposing ioctl interface at /dev/sbtsi-<addr>
+ * @lock:           mutex protecting concurrent access to the device
+ * @dev_addr:       I2C/I3C device address, used to name the misc device node
  * @ext_range_mode:	sensor uses extended temperature range
  * @read_order:	if set, decimal part must be read before integer part
  * @is_i3c:	true when the device is accessed over I3C
@@ -26,6 +31,9 @@ struct sbtsi_data {
 		struct i2c_client *client;
 		struct i3c_device *i3cdev;
 	};
+	struct miscdevice sbtsi_misc_dev;
+	struct mutex lock;	/* protects concurrent access to the device */
+	u8 dev_addr;
 	bool ext_range_mode;
 	bool read_order;
 	bool is_i3c;
diff --git a/include/uapi/misc/amd-apml.h b/include/uapi/misc/amd-apml.h
index 745b3338fc06..8a85f79b0938 100644
--- a/include/uapi/misc/amd-apml.h
+++ b/include/uapi/misc/amd-apml.h
@@ -73,6 +73,13 @@ struct apml_reg_xfer_msg {
 	__u8 rflag;
 };
 
+struct apml_tsi_xfer_msg {
+	__u8 reg_addr;		/* TSI register address offset */
+	__u8 data_in_out;	/* Register data for read/write */
+	__u8 rflag;		/* Register read or write */
+	__u8 pad;		/* Explicit padding */
+};
+
 /*
  * AMD sideband interface base IOCTL
  */
@@ -149,4 +156,20 @@ struct apml_reg_xfer_msg {
  */
 #define SBRMI_IOCTL_REG_XFER_CMD	_IOWR(SB_BASE_IOCTL_NR, 3, struct apml_reg_xfer_msg)
 
+/**
+ * DOC: SBTSI_IOCTL_REG_XFER_CMD
+ *
+ * @Parameters
+ *
+ * @struct apml_tsi_xfer_msg
+ *	Pointer to the &struct apml_tsi_xfer_msg that will contain the protocol
+ *	information
+ *
+ * @Description
+ * IOCTL command for APML TSI messages using generic _IOWR
+ * The IOCTL provides userspace access to AMD sideband TSI register xfer protocol
+ * - TSI protocol to read/write temperature sensor registers
+ */
+#define SBTSI_IOCTL_REG_XFER_CMD	_IOWR(SB_BASE_IOCTL_NR, 4, struct apml_tsi_xfer_msg)
+
 #endif /*_AMD_APML_H_*/
-- 
2.34.1


  parent reply	other threads:[~2026-05-15 13:45 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-15 13:45 [PATCH v2 0/6] misc: amd-sbi: Refactor SBTSI driver with I3C support and ioctl interface Akshay Gupta
2026-05-15 13:45 ` [PATCH v2 1/6] hwmon/misc: amd-sbi: Move core sbtsi support from hwmon to misc Akshay Gupta
2026-05-15 14:22   ` sashiko-bot
2026-05-15 13:45 ` [PATCH v2 2/6] hwmon: sbtsi_temp: Refactor temperature register access into helpers Akshay Gupta
2026-05-15 13:45 ` [PATCH v2 3/6] hwmon/misc: amd-sbi: Move sbtsi register transfer to core abstraction Akshay Gupta
2026-05-15 13:45 ` [PATCH v2 4/6] misc: amd-sbi: Add support for SB-TSI over I3C Akshay Gupta
2026-05-15 15:33   ` sashiko-bot
2026-05-15 16:21     ` Guenter Roeck
2026-05-15 13:45 ` Akshay Gupta [this message]
2026-05-15 14:11   ` [PATCH v2 5/6] misc: amd-sbi: Add SBTSI ioctl register transfer interface Guenter Roeck
2026-05-15 15:58   ` sashiko-bot
2026-05-15 13:45 ` [PATCH v2 6/6] docs: misc: amd-sbi: Document SBTSI userspace interface 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=20260515134506.397649-6-Akshay.Gupta@amd.com \
    --to=akshay.gupta@amd.com \
    --cc=Anand.Umarji@amd.com \
    --cc=Kevin.Tung@quantatw.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-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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.