public inbox for devicetree@vger.kernel.org
 help / color / mirror / Atom feed
From: Salih Erim <salih.erim@amd.com>
To: <jic23@kernel.org>, <robh@kernel.org>, <krzk+dt@kernel.org>,
	<conor+dt@kernel.org>, <git@amd.com>
Cc: <nuno.sa@analog.com>, <andy@kernel.org>, <dlechner@baylibre.com>,
	<michal.simek@amd.com>, <conall.ogriofa@amd.com>,
	<erimsalih@gmail.com>, <linux-iio@vger.kernel.org>,
	<devicetree@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	Salih Erim <salih.erim@amd.com>
Subject: [PATCH v2 3/5] iio: adc: versal-sysmon: add I2C driver
Date: Sat, 2 May 2026 12:19:49 +0100	[thread overview]
Message-ID: <20260502111951.538488-4-salih.erim@amd.com> (raw)
In-Reply-To: <20260502111951.538488-1-salih.erim@amd.com>

Add I2C bus driver for Versal SysMon to enable voltage and temperature
monitoring when the Versal chip has SysMon configured with an I2C
interface.

The I2C protocol uses a custom 8-byte write format:
  - Bytes 0-3: Data (little-endian, 32-bit)
  - Bytes 4-5: Register offset (split into low/high parts)
  - Byte 6: Instruction (read=0x4, write=0x8)
  - Byte 7: Reserved

For reads, the driver sends the 8-byte command then receives 4 bytes
of data. For writes, it sends the 8-byte command with embedded data.

The driver uses the regmap API with custom read/write callbacks to
share the bus-agnostic core driver (versal-sysmon-core).

Event support is not available on I2C since the SysMon interrupt
lines are not routed over the I2C bus.

Co-developed-by: Conall O'Griofa <conall.ogriofa@amd.com>
Signed-off-by: Conall O'Griofa <conall.ogriofa@amd.com>
Signed-off-by: Salih Erim <salih.erim@amd.com>
---
Changes in v2:
  - New patch (I2C was deferred to Series B in v1)
  - Uses regmap API with custom I2C read/write callbacks
  - Shares core module with MMIO driver via sysmon_core_probe()
  - No event support (I2C has no interrupt line)
  - Separate VERSAL_SYSMON_I2C Kconfig symbol
  - Reverse Christmas Tree variable ordering in read/write functions
 drivers/iio/adc/Kconfig             |  13 +++
 drivers/iio/adc/Makefile            |   1 +
 drivers/iio/adc/versal-sysmon-i2c.c | 166 ++++++++++++++++++++++++++++
 3 files changed, 180 insertions(+)
 create mode 100644 drivers/iio/adc/versal-sysmon-i2c.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index c7f19057484..8f9fc9de74a 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1963,6 +1963,19 @@ config VERSAL_SYSMON
 	  To compile this driver as a module, choose M here: the module
 	  will be called versal-sysmon.
 
+config VERSAL_SYSMON_I2C
+	tristate "AMD Versal SysMon I2C driver"
+	depends on I2C
+	select VERSAL_SYSMON_CORE
+	help
+	  Say yes here to have support for the AMD/Xilinx Versal System
+	  Monitor (SysMon) via I2C interface. This driver enables voltage
+	  and temperature monitoring when the Versal chip has SysMon
+	  configured with I2C access.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called versal-sysmon-i2c.
+
 config VF610_ADC
 	tristate "Freescale vf610 ADC driver"
 	depends on HAS_IOMEM
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index d7696b1b157..5abb611fe46 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -169,6 +169,7 @@ obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
 obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
 obj-$(CONFIG_VERSAL_SYSMON_CORE) += versal-sysmon-core.o
 obj-$(CONFIG_VERSAL_SYSMON) += versal-sysmon.o
+obj-$(CONFIG_VERSAL_SYSMON_I2C) += versal-sysmon-i2c.o
 obj-$(CONFIG_VF610_ADC) += vf610_adc.o
 obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
 obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o
diff --git a/drivers/iio/adc/versal-sysmon-i2c.c b/drivers/iio/adc/versal-sysmon-i2c.c
new file mode 100644
index 00000000000..0464b356bbf
--- /dev/null
+++ b/drivers/iio/adc/versal-sysmon-i2c.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Versal SysMon I2C driver
+ *
+ * Copyright (C) 2023 - 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "versal-sysmon.h"
+
+#define SYSMON_I2C_READ_SIZE	4
+#define SYSMON_I2C_WRITE_SIZE	8
+
+#define SYSMON_I2C_INSTR_READ	BIT(2)
+#define SYSMON_I2C_INSTR_WRITE	BIT(3)
+
+#define SYSMON_I2C_DATA0_MASK	GENMASK(7, 0)
+#define SYSMON_I2C_DATA1_MASK	GENMASK(15, 8)
+#define SYSMON_I2C_DATA2_MASK	GENMASK(23, 16)
+#define SYSMON_I2C_DATA3_MASK	GENMASK(31, 24)
+
+#define SYSMON_I2C_OFS_LOW_MASK		GENMASK(9, 2)
+#define SYSMON_I2C_OFS_HIGH_MASK	GENMASK(15, 10)
+
+enum sysmon_i2c_payload_idx {
+	SYSMON_I2C_DATA0_IDX = 0,
+	SYSMON_I2C_DATA1_IDX,
+	SYSMON_I2C_DATA2_IDX,
+	SYSMON_I2C_DATA3_IDX,
+	SYSMON_I2C_OFS_LOW_IDX,
+	SYSMON_I2C_OFS_HIGH_IDX,
+	SYSMON_I2C_INSTR_IDX,
+};
+
+struct sysmon_i2c {
+	struct i2c_client *client;
+};
+
+static int sysmon_i2c_reg_read(void *context, unsigned int reg,
+			       unsigned int *val)
+{
+	u8 write_buf[SYSMON_I2C_WRITE_SIZE] = { 0 };
+	u8 read_buf[SYSMON_I2C_READ_SIZE];
+	struct sysmon_i2c *priv = context;
+	int ret;
+
+	write_buf[SYSMON_I2C_OFS_LOW_IDX] =
+		FIELD_GET(SYSMON_I2C_OFS_LOW_MASK, reg);
+	write_buf[SYSMON_I2C_OFS_HIGH_IDX] =
+		FIELD_GET(SYSMON_I2C_OFS_HIGH_MASK, reg);
+	write_buf[SYSMON_I2C_INSTR_IDX] = SYSMON_I2C_INSTR_READ;
+
+	ret = i2c_master_send(priv->client, write_buf, SYSMON_I2C_WRITE_SIZE);
+	if (ret < 0)
+		return ret;
+	if (ret != SYSMON_I2C_WRITE_SIZE)
+		return -EIO;
+
+	ret = i2c_master_recv(priv->client, read_buf, SYSMON_I2C_READ_SIZE);
+	if (ret < 0)
+		return ret;
+	if (ret != SYSMON_I2C_READ_SIZE)
+		return -EIO;
+
+	*val = FIELD_PREP(SYSMON_I2C_DATA0_MASK,
+			  read_buf[SYSMON_I2C_DATA0_IDX]) |
+	       FIELD_PREP(SYSMON_I2C_DATA1_MASK,
+			  read_buf[SYSMON_I2C_DATA1_IDX]) |
+	       FIELD_PREP(SYSMON_I2C_DATA2_MASK,
+			  read_buf[SYSMON_I2C_DATA2_IDX]) |
+	       FIELD_PREP(SYSMON_I2C_DATA3_MASK,
+			  read_buf[SYSMON_I2C_DATA3_IDX]);
+
+	return 0;
+}
+
+static int sysmon_i2c_reg_write(void *context, unsigned int reg,
+				unsigned int val)
+{
+	u8 write_buf[SYSMON_I2C_WRITE_SIZE] = { 0 };
+	struct sysmon_i2c *priv = context;
+	int ret;
+
+	write_buf[SYSMON_I2C_DATA0_IDX] =
+		FIELD_GET(SYSMON_I2C_DATA0_MASK, val);
+	write_buf[SYSMON_I2C_DATA1_IDX] =
+		FIELD_GET(SYSMON_I2C_DATA1_MASK, val);
+	write_buf[SYSMON_I2C_DATA2_IDX] =
+		FIELD_GET(SYSMON_I2C_DATA2_MASK, val);
+	write_buf[SYSMON_I2C_DATA3_IDX] =
+		FIELD_GET(SYSMON_I2C_DATA3_MASK, val);
+	write_buf[SYSMON_I2C_OFS_LOW_IDX] =
+		FIELD_GET(SYSMON_I2C_OFS_LOW_MASK, reg);
+	write_buf[SYSMON_I2C_OFS_HIGH_IDX] =
+		FIELD_GET(SYSMON_I2C_OFS_HIGH_MASK, reg);
+	write_buf[SYSMON_I2C_INSTR_IDX] = SYSMON_I2C_INSTR_WRITE;
+
+	ret = i2c_master_send(priv->client, write_buf, SYSMON_I2C_WRITE_SIZE);
+	if (ret < 0)
+		return ret;
+	if (ret != SYSMON_I2C_WRITE_SIZE)
+		return -EIO;
+
+	return 0;
+}
+
+static const struct regmap_config sysmon_i2c_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = SYSMON_REG_STRIDE,
+	.max_register = SYSMON_MAX_REG,
+	.reg_read = sysmon_i2c_reg_read,
+	.reg_write = sysmon_i2c_reg_write,
+};
+
+static int sysmon_i2c_probe(struct i2c_client *client)
+{
+	struct sysmon_i2c *priv;
+	struct regmap *regmap;
+
+	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->client = client;
+
+	regmap = devm_regmap_init(&client->dev, NULL, priv,
+				  &sysmon_i2c_regmap_config);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	/* I2C has no IRQ connection; events are not supported */
+	return sysmon_core_probe(&client->dev, regmap, 0);
+}
+
+static const struct of_device_id sysmon_i2c_of_match_table[] = {
+	{ .compatible = "xlnx,versal-sysmon-i2c" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sysmon_i2c_of_match_table);
+
+static const struct i2c_device_id sysmon_i2c_id_table[] = {
+	{ "versal-sysmon" },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, sysmon_i2c_id_table);
+
+static struct i2c_driver sysmon_i2c_driver = {
+	.probe = sysmon_i2c_probe,
+	.driver = {
+		.name = "versal-sysmon-i2c",
+		.of_match_table = sysmon_i2c_of_match_table,
+	},
+	.id_table = sysmon_i2c_id_table,
+};
+module_i2c_driver(sysmon_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD Versal SysMon I2C Driver");
+MODULE_AUTHOR("Conall O'Griofa <conall.ogriofa@amd.com>");
+MODULE_AUTHOR("Salih Erim <salih.erim@amd.com>");
-- 
2.48.1


  parent reply	other threads:[~2026-05-02 11:20 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-02 11:19 [PATCH v2 0/5] iio: adc: add AMD/Xilinx Versal SysMon driver Salih Erim
2026-05-02 11:19 ` [PATCH v2 1/5] dt-bindings: iio: adc: add xlnx,versal-sysmon binding Salih Erim
2026-05-02 11:19 ` [PATCH v2 2/5] iio: adc: add Versal SysMon driver Salih Erim
2026-05-02 11:19 ` Salih Erim [this message]
2026-05-02 11:19 ` [PATCH v2 4/5] iio: adc: versal-sysmon: add threshold event support Salih Erim
2026-05-02 11:19 ` [PATCH v2 5/5] iio: adc: versal-sysmon: add oversampling support Salih Erim

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=20260502111951.538488-4-salih.erim@amd.com \
    --to=salih.erim@amd.com \
    --cc=andy@kernel.org \
    --cc=conall.ogriofa@amd.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dlechner@baylibre.com \
    --cc=erimsalih@gmail.com \
    --cc=git@amd.com \
    --cc=jic23@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=michal.simek@amd.com \
    --cc=nuno.sa@analog.com \
    --cc=robh@kernel.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