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
next prev parent reply other threads:[~2026-05-02 11:20 UTC|newest]
Thread overview: 20+ 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-03 14:20 ` Krzysztof Kozlowski
2026-05-03 22:52 ` Salih Erim
2026-05-02 11:19 ` [PATCH v2 2/5] iio: adc: add Versal SysMon driver Salih Erim
2026-05-04 10:18 ` Andy Shevchenko
2026-05-04 15:50 ` Salih Erim
2026-05-05 7:12 ` Andy Shevchenko
2026-05-04 17:32 ` Jonathan Cameron
2026-05-04 19:26 ` Guenter Roeck
2026-05-12 11:35 ` Salih Erim
2026-05-16 10:20 ` Jonathan Cameron
2026-05-16 15:04 ` Guenter Roeck
2026-05-02 11:19 ` Salih Erim [this message]
2026-05-04 10:25 ` [PATCH v2 3/5] iio: adc: versal-sysmon: add I2C driver Andy Shevchenko
2026-05-15 15:50 ` Erim, Salih
2026-05-02 11:19 ` [PATCH v2 4/5] iio: adc: versal-sysmon: add threshold event support Salih Erim
2026-05-04 10:52 ` Andy Shevchenko
2026-05-04 17:44 ` Jonathan Cameron
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 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.