* [PATCH v3 0/4] hwmon: Add Kandou KB9002 PCIe retimer driver
@ 2026-07-21 6:31 Andy Chung via B4 Relay
2026-07-21 6:31 ` [PATCH v3 1/4] dt-bindings: Add vendor prefix for Kandou Andy Chung via B4 Relay
` (3 more replies)
0 siblings, 4 replies; 10+ messages in thread
From: Andy Chung via B4 Relay @ 2026-07-21 6:31 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andy Chung,
Guenter Roeck, Jonathan Corbet, Shuah Khan
Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, Andy Chung
The Kandou KB9002 is an 8-lane PCIe 5.0 retimer with an integrated
microcontroller that exposes an SMBus 3.0 target (with mandatory PEC)
on its sideband interface. Its firmware aggregates per-lane die
temperatures and publishes the maximum through a register window.
This series adds a hwmon driver for it. The driver reports the
aggregated maximum die temperature as temp1_input, and exposes the
running firmware version and boot status under debugfs.
The series is organised as:
1/4 add the "kandou" vendor prefix
2/4 device tree binding for the retimer
3/4 the driver, Kconfig/Makefile and MAINTAINERS entry
4/4 hwmon documentation
Signed-off-by: Andy Chung <Andy.Chung@amd.com>
---
Changes in v3:
- Drop the private register-access mutex. The temperature read is
already serialised by the hwmon core lock; the debugfs files
(fw_ver, fw_load_status) now take hwmon_lock() to serialise
against it, since they are read outside the core lock.
- Use a named initialiser for the i2c_device_id entry.
- dt-bindings: constrain the reg address range under 'items' rather
than applying minimum/maximum to the array.
- Link to v2: https://patch.msgid.link/20260715-kb9002-upstream-v2-0-2fd390383da5@amd.com
Changes in v2:
- Drop the unconditional I2C_FUNC_I2C requirement at probe; require raw
I2C only in the host-interface switch path, so the driver loads on
SMBus-only adapters when the chip is already strapped to SMBus.
- kb9002_read_revid(): return immediately on a hard I2C read error
instead of retrying the FIFO drain, avoiding needless bus traffic when
the device is absent.
- Link to v1: https://lore.kernel.org/all/20260714-kb9002-upstream-v1-0-8fd2f0b135d8@amd.com
---
Andy Chung (4):
dt-bindings: Add vendor prefix for Kandou
dt-bindings: hwmon: Add Kandou KB9002
hwmon: (kb9002) Add driver for Kandou KB9002 retimer
hwmon: (kb9002) Add documentation
.../devicetree/bindings/hwmon/kandou,kb9002.yaml | 46 ++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
Documentation/hwmon/index.rst | 1 +
Documentation/hwmon/kb9002.rst | 65 +++
MAINTAINERS | 8 +
drivers/hwmon/Kconfig | 11 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/kb9002.c | 469 +++++++++++++++++++++
8 files changed, 603 insertions(+)
---
base-commit: ca078d004cf58137bcf8cb24a8b271397431ba58
change-id: 20260713-kb9002-upstream-06c686e37833
Best regards,
--
Andy Chung <Andy.Chung@amd.com>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 1/4] dt-bindings: Add vendor prefix for Kandou
2026-07-21 6:31 [PATCH v3 0/4] hwmon: Add Kandou KB9002 PCIe retimer driver Andy Chung via B4 Relay
@ 2026-07-21 6:31 ` Andy Chung via B4 Relay
2026-07-21 8:27 ` Krzysztof Kozlowski
2026-07-21 6:31 ` [PATCH v3 2/4] dt-bindings: hwmon: Add Kandou KB9002 Andy Chung via B4 Relay
` (2 subsequent siblings)
3 siblings, 1 reply; 10+ messages in thread
From: Andy Chung via B4 Relay @ 2026-07-21 6:31 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andy Chung,
Guenter Roeck, Jonathan Corbet, Shuah Khan
Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, Andy Chung
From: Andy Chung <Andy.Chung@amd.com>
Kandou Bus, S.A. is the vendor of the KB9002 PCIe retimer.
Signed-off-by: Andy Chung <Andy.Chung@amd.com>
---
Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 396044f368e7..727871970d97 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -879,6 +879,8 @@ patternProperties:
description: JuTouch Technology Co., Ltd.
"^kam,.*":
description: Kamstrup A/S
+ "^kandou,.*":
+ description: Kandou Bus, S.A.
"^karo,.*":
description: Ka-Ro electronics GmbH
"^keithkoep,.*":
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 2/4] dt-bindings: hwmon: Add Kandou KB9002
2026-07-21 6:31 [PATCH v3 0/4] hwmon: Add Kandou KB9002 PCIe retimer driver Andy Chung via B4 Relay
2026-07-21 6:31 ` [PATCH v3 1/4] dt-bindings: Add vendor prefix for Kandou Andy Chung via B4 Relay
@ 2026-07-21 6:31 ` Andy Chung via B4 Relay
2026-07-22 7:41 ` Krzysztof Kozlowski
2026-07-21 6:31 ` [PATCH v3 3/4] hwmon: (kb9002) Add driver for Kandou KB9002 retimer Andy Chung via B4 Relay
2026-07-21 6:31 ` [PATCH v3 4/4] hwmon: (kb9002) Add documentation Andy Chung via B4 Relay
3 siblings, 1 reply; 10+ messages in thread
From: Andy Chung via B4 Relay @ 2026-07-21 6:31 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andy Chung,
Guenter Roeck, Jonathan Corbet, Shuah Khan
Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, Andy Chung
From: Andy Chung <Andy.Chung@amd.com>
Add device tree bindings for the Kandou KB9002 PCIe 5.0 retimer, an
SMBus target that exposes an aggregated die temperature.
Signed-off-by: Andy Chung <Andy.Chung@amd.com>
---
.../devicetree/bindings/hwmon/kandou,kb9002.yaml | 46 ++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/Documentation/devicetree/bindings/hwmon/kandou,kb9002.yaml b/Documentation/devicetree/bindings/hwmon/kandou,kb9002.yaml
new file mode 100644
index 000000000000..0d939c8aa734
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/kandou,kb9002.yaml
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/kandou,kb9002.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Kandou KB9002 PCIe 5.0 retimer
+
+maintainers:
+ - Andy Chung <andy.chung@amd.com>
+
+description: |
+ The Kandou KB9002 is an 8-lane PCIe 5.0 retimer that exposes a
+ firmware-controlled SMBus 3.0 target on its sideband interface.
+ The host can query the aggregated maximum die temperature and the
+ running firmware version through that target.
+
+ The 7-bit SMBus address is selected by three SMB_ADDR straps and
+ ranges from 0x20 to 0x27.
+
+properties:
+ compatible:
+ const: kandou,kb9002
+
+ reg:
+ items:
+ - minimum: 0x20
+ maximum: 0x27
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ retimer@20 {
+ compatible = "kandou,kb9002";
+ reg = <0x20>;
+ };
+ };
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 3/4] hwmon: (kb9002) Add driver for Kandou KB9002 retimer
2026-07-21 6:31 [PATCH v3 0/4] hwmon: Add Kandou KB9002 PCIe retimer driver Andy Chung via B4 Relay
2026-07-21 6:31 ` [PATCH v3 1/4] dt-bindings: Add vendor prefix for Kandou Andy Chung via B4 Relay
2026-07-21 6:31 ` [PATCH v3 2/4] dt-bindings: hwmon: Add Kandou KB9002 Andy Chung via B4 Relay
@ 2026-07-21 6:31 ` Andy Chung via B4 Relay
2026-07-21 14:43 ` Guenter Roeck
2026-07-21 6:31 ` [PATCH v3 4/4] hwmon: (kb9002) Add documentation Andy Chung via B4 Relay
3 siblings, 1 reply; 10+ messages in thread
From: Andy Chung via B4 Relay @ 2026-07-21 6:31 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andy Chung,
Guenter Roeck, Jonathan Corbet, Shuah Khan
Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, Andy Chung
From: Andy Chung <Andy.Chung@amd.com>
The Kandou KB9002 is an 8-lane PCIe 5.0 retimer that exposes an SMBus
target with mandatory PEC. Add a hwmon driver reporting the firmware
aggregated maximum die temperature as temp1_input, with the firmware
version and boot status under debugfs.
Signed-off-by: Andy Chung <Andy.Chung@amd.com>
---
MAINTAINERS | 8 +
drivers/hwmon/Kconfig | 11 ++
drivers/hwmon/Makefile | 1 +
drivers/hwmon/kb9002.c | 469 +++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 489 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 9b4b1575bdbd..1d80ac2660e2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13931,6 +13931,14 @@ S: Maintained
F: Documentation/hwmon/k8temp.rst
F: drivers/hwmon/k8temp.c
+KANDOU KB9002 PCIE RETIMER HWMON DRIVER
+M: Andy Chung <andy.chung@amd.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/hwmon/kandou,kb9002.yaml
+F: Documentation/hwmon/kb9002.rst
+F: drivers/hwmon/kb9002.c
+
KASAN
M: Andrey Ryabinin <ryabinin.a.a@gmail.com>
R: Alexander Potapenko <glider@google.com>
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 08c29685126a..300329773e87 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -335,6 +335,17 @@ config SENSORS_K10TEMP
This driver can also be built as a module. If so, the module
will be called k10temp.
+config SENSORS_KB9002
+ tristate "Kandou KB9002 PCIe retimer"
+ depends on I2C
+ help
+ If you say yes here you get support for the integrated
+ temperature sensor and firmware version readout on Kandou
+ KB9002 PCIe 5.0 retimers, accessed over SMBus.
+
+ This driver can also be built as a module. If so, the module
+ will be called kb9002.
+
config SENSORS_KBATT
tristate "KEBA battery controller support"
depends on KEBA_CP500
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 63809eeec2f4..0cad7e21634c 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -113,6 +113,7 @@ obj-$(CONFIG_SENSORS_IT87) += it87.o
obj-$(CONFIG_SENSORS_JC42) += jc42.o
obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o
+obj-$(CONFIG_SENSORS_KB9002) += kb9002.o
obj-$(CONFIG_SENSORS_KBATT) += kbatt.o
obj-$(CONFIG_SENSORS_KFAN) += kfan.o
obj-$(CONFIG_SENSORS_LAN966X) += lan966x-hwmon.o
diff --git a/drivers/hwmon/kb9002.c b/drivers/hwmon/kb9002.c
new file mode 100644
index 000000000000..61a6e522e770
--- /dev/null
+++ b/drivers/hwmon/kb9002.c
@@ -0,0 +1,469 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Kandou KB9002 PCIe 5.0 retimer hwmon driver.
+ *
+ * The retimer exposes a system management bus (SMBus 3.0 with PEC)
+ * target for firmware-managed status registers. This driver assumes
+ * the chip is strapped to SMBus mode and exports the aggregated
+ * maximum die temperature as hwmon temp1_input (millidegrees Celsius)
+ * plus the firmware version and boot status under debugfs.
+ *
+ * Datasheet: Kandou KB9002 PCIe retimer (KA-015171-PD).
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/seq_file.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#define KB9002_DEV_NAME "kb9002"
+
+/*
+ * SMBus read command codes. Each read is a two-phase PEC-protected
+ * transaction: prime writes the target address, data reads it back with
+ * the register contents. FW reads use a 16-bit address, HW reads 32-bit.
+ */
+#define KB9002_CC_FW_READ_PRIME 0x82
+#define KB9002_CC_FW_READ_DATA 0x81
+#define KB9002_CC_HW_READ_PRIME 0x8a
+#define KB9002_CC_HW_READ_DATA 0x89
+
+/* Firmware register offsets (16-bit). */
+#define KB9002_FW_REG_VID 0x0004
+#define KB9002_FW_REG_FW_VERSION 0x0500
+#define KB9002_FW_REG_TEMP_MAXIMUM 0x0550
+
+#define KB9002_VID_MASK GENMASK(31, 16)
+#define KB9002_VID_KANDOU 0x1e6f
+
+/* Firmware boot status: 0xe8 in the top byte means init completed OK. */
+#define KB9002_HW_REG_FW_BOOT_STATUS 0xe0090008
+#define KB9002_FW_BOOT_STATUS_OK_MSB 0xe8
+
+/*
+ * Hardware registers reached over raw I2C (32-bit addressing). The
+ * host-interface bit selects SMBus (set) vs raw-I2C target; parts
+ * strapped to raw I2C need it set before SMBus access works.
+ */
+#define KB9002_HW_REG_REVID 0x00480004
+#define KB9002_HW_REG_HOST_IF 0x00480008
+#define KB9002_HOST_IF_SMBUS BIT(1)
+
+#define KB9002_REVID_MASK GENMASK(7, 0)
+#define KB9002_REVID_B0 0x10
+#define KB9002_REVID_B1 0x11
+
+/* Retries to drain a stray leading 0xff from the raw-I2C FIFO. */
+#define KB9002_REVID_READ_RETRIES 16
+
+/* Temperature: 32-bit Q16.16 absolute Kelvin. */
+#define KB9002_TEMP_FRAC_BITS 16
+#define KB9002_ABS_ZERO_MILLI_C (-273150)
+
+/* Firmware takes up to ~2s to respond after a host-interface change. */
+#define KB9002_FW_READY_POLL_US (25 * USEC_PER_MSEC)
+#define KB9002_FW_READY_TIMEOUT_US (2 * USEC_PER_SEC)
+
+struct kb9002_data {
+ struct i2c_client *client;
+ struct device *hwmon_dev;
+};
+
+/* Raw-I2C read: write the 32-bit BE address, then read 4 BE data bytes. */
+static int kb9002_i2c_read(struct i2c_client *client, u32 reg, u32 *val)
+{
+ u8 addr[4];
+ u8 rbuf[4];
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = sizeof(addr),
+ .buf = addr,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(rbuf),
+ .buf = rbuf,
+ },
+ };
+ int ret;
+
+ put_unaligned_be32(reg, addr);
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *val = get_unaligned_be32(rbuf);
+ return 0;
+}
+
+/* Raw-I2C write: 4 BE address bytes followed by 4 BE data bytes. */
+static int kb9002_i2c_write(struct i2c_client *client, u32 reg, u32 val)
+{
+ u8 buf[8];
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ };
+ int ret;
+
+ put_unaligned_be32(reg, &buf[0]);
+ put_unaligned_be32(val, &buf[4]);
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ return ret;
+ if (ret != 1)
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * Read the silicon revision ID. A fresh FIFO may start with a stray
+ * 0xff that shifts the result, so drain one byte between retries until
+ * the top byte is no longer 0xff.
+ */
+static int kb9002_read_revid(struct i2c_client *client, u32 *revid)
+{
+ u8 dummy;
+ int ret;
+ int i;
+
+ for (i = 0; i < KB9002_REVID_READ_RETRIES; i++) {
+ ret = kb9002_i2c_read(client, KB9002_HW_REG_REVID, revid);
+ if (ret)
+ return ret;
+ if ((*revid >> 24) != 0xff)
+ return 0;
+ /* Drain one byte from the chip to re-align the I2C FIFO. */
+ i2c_master_recv(client, &dummy, 1);
+ }
+
+ return -EIO;
+}
+
+/*
+ * Read a 32-bit firmware register over SMBus: block-write the 16-bit LE
+ * address, then block-read the echoed address plus 4 LE data bytes.
+ */
+static int kb9002_fw_read(struct kb9002_data *data, u16 reg, u32 *val)
+{
+ struct i2c_client *client = data->client;
+ u8 addr[2];
+ u8 rbuf[I2C_SMBUS_BLOCK_MAX];
+ int ret;
+
+ put_unaligned_le16(reg, addr);
+
+ ret = i2c_smbus_write_block_data(client, KB9002_CC_FW_READ_PRIME,
+ sizeof(addr), addr);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_block_data(client, KB9002_CC_FW_READ_DATA, rbuf);
+ if (ret < 0)
+ return ret;
+ if (ret < (int)(sizeof(addr) + sizeof(*val)))
+ return -EIO;
+
+ *val = get_unaligned_le32(&rbuf[sizeof(addr)]);
+ return 0;
+}
+
+/* Like kb9002_fw_read but for a hardware register (32-bit LE address). */
+static int kb9002_smbus_hw_read(struct kb9002_data *data, u32 reg, u32 *val)
+{
+ struct i2c_client *client = data->client;
+ u8 addr[4];
+ u8 rbuf[I2C_SMBUS_BLOCK_MAX];
+ int ret;
+
+ put_unaligned_le32(reg, addr);
+
+ ret = i2c_smbus_write_block_data(client, KB9002_CC_HW_READ_PRIME,
+ sizeof(addr), addr);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_block_data(client, KB9002_CC_HW_READ_DATA, rbuf);
+ if (ret < 0)
+ return ret;
+ if (ret < (int)(sizeof(addr) + sizeof(*val)))
+ return -EIO;
+
+ *val = get_unaligned_le32(&rbuf[sizeof(addr)]);
+ return 0;
+}
+
+/*
+ * Switch the host interface from raw-I2C to SMBus and wait for firmware
+ * to come back up. Called only when SMBus access failed in probe, i.e.
+ * the chip is strapped to raw-I2C mode. Confirms the revision, sets the
+ * SMBus-mode bit, then polls until firmware responds again.
+ */
+static int kb9002_enable_smbus_target(struct kb9002_data *data)
+{
+ struct i2c_client *client = data->client;
+ u32 revid;
+ u32 val;
+ int op_ret;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return dev_err_probe(&client->dev, -ENODEV,
+ "raw I2C required to switch to SMBus mode\n");
+
+ ret = kb9002_read_revid(client, &revid);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "revision ID read failed\n");
+
+ switch (FIELD_GET(KB9002_REVID_MASK, revid)) {
+ case KB9002_REVID_B0:
+ case KB9002_REVID_B1:
+ break;
+ default:
+ return dev_err_probe(&client->dev, -ENODEV,
+ "unsupported revision ID 0x%08x\n", revid);
+ }
+
+ ret = kb9002_i2c_read(client, KB9002_HW_REG_HOST_IF, &val);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "host interface read failed\n");
+
+ val |= KB9002_HOST_IF_SMBUS;
+
+ ret = kb9002_i2c_write(client, KB9002_HW_REG_HOST_IF, val);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "host interface write failed\n");
+
+ /* Wait until firmware re-initialisation completes. */
+ ret = read_poll_timeout(kb9002_fw_read, op_ret, op_ret == 0,
+ KB9002_FW_READY_POLL_US,
+ KB9002_FW_READY_TIMEOUT_US, true,
+ data, KB9002_FW_REG_VID, &val);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "firmware not responding over SMBus\n");
+
+ return 0;
+}
+
+/* Convert Q16.16 absolute Kelvin to millidegrees Celsius. */
+static long kb9002_temp_to_milli_c(u32 raw)
+{
+ s64 milli_k = ((s64)raw * 1000) >> KB9002_TEMP_FRAC_BITS;
+
+ return (long)milli_k + KB9002_ABS_ZERO_MILLI_C;
+}
+
+static int kb9002_read_temp(struct kb9002_data *data, long *val)
+{
+ u32 raw;
+ int ret;
+
+ ret = kb9002_fw_read(data, KB9002_FW_REG_TEMP_MAXIMUM, &raw);
+ if (ret)
+ return ret;
+
+ *val = kb9002_temp_to_milli_c(raw);
+ return 0;
+}
+
+static umode_t kb9002_is_visible(const void *drvdata,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ if (type == hwmon_temp &&
+ (attr == hwmon_temp_input || attr == hwmon_temp_label))
+ return 0444;
+ return 0;
+}
+
+static int kb9002_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct kb9002_data *data = dev_get_drvdata(dev);
+
+ if (type == hwmon_temp && attr == hwmon_temp_input)
+ return kb9002_read_temp(data, val);
+
+ return -EOPNOTSUPP;
+}
+
+static int kb9002_read_string(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ if (type == hwmon_temp && attr == hwmon_temp_label) {
+ *str = KB9002_DEV_NAME;
+ return 0;
+ }
+ return -EOPNOTSUPP;
+}
+
+static const struct hwmon_ops kb9002_hwmon_ops = {
+ .is_visible = kb9002_is_visible,
+ .read = kb9002_read,
+ .read_string = kb9002_read_string,
+};
+
+static const struct hwmon_channel_info * const kb9002_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL),
+ NULL,
+};
+
+static const struct hwmon_chip_info kb9002_chip_info = {
+ .ops = &kb9002_hwmon_ops,
+ .info = kb9002_hwmon_info,
+};
+
+static int kb9002_fw_version_show(struct seq_file *s, void *unused)
+{
+ struct kb9002_data *data = s->private;
+ u32 ver;
+ int ret;
+
+ guard(hwmon_lock)(data->hwmon_dev);
+
+ ret = kb9002_fw_read(data, KB9002_FW_REG_FW_VERSION, &ver);
+ if (ret)
+ return ret;
+
+ seq_printf(s, "%u.%02u.%02u.%u\n",
+ (ver >> 24) & 0xff, (ver >> 16) & 0xff,
+ (ver >> 8) & 0xff, (ver >> 0) & 0xff);
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(kb9002_fw_version);
+
+static int kb9002_fw_load_status_show(struct seq_file *s, void *unused)
+{
+ struct kb9002_data *data = s->private;
+ u32 status;
+ int ret;
+
+ guard(hwmon_lock)(data->hwmon_dev);
+
+ ret = kb9002_smbus_hw_read(data, KB9002_HW_REG_FW_BOOT_STATUS, &status);
+ if (ret)
+ return ret;
+
+ seq_printf(s, "%s\n",
+ (status >> 24) == KB9002_FW_BOOT_STATUS_OK_MSB ?
+ "normal" : "abnormal");
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(kb9002_fw_load_status);
+
+static void kb9002_debugfs_init(struct kb9002_data *data)
+{
+ struct dentry *dir = data->client->debugfs;
+
+ debugfs_create_file("fw_ver", 0444, dir, data,
+ &kb9002_fw_version_fops);
+ debugfs_create_file("fw_load_status", 0444, dir, data,
+ &kb9002_fw_load_status_fops);
+}
+
+static int kb9002_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct kb9002_data *data;
+ struct device *hwmon_dev;
+ u32 vid;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BLOCK_DATA |
+ I2C_FUNC_SMBUS_PEC))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+
+ /* All firmware register accesses are PEC-protected. */
+ client->flags |= I2C_CLIENT_PEC;
+
+ i2c_set_clientdata(client, data);
+
+ /*
+ * Try SMBus first. If the chip is strapped to raw-I2C mode it
+ * will not respond to SMBus framing, so fall back to switching
+ * the host interface over raw I2C and retry.
+ */
+ ret = kb9002_fw_read(data, KB9002_FW_REG_VID, &vid);
+ if (ret) {
+ dev_dbg(dev, "SMBus probe failed (%d), trying raw-I2C host-interface switch\n",
+ ret);
+ ret = kb9002_enable_smbus_target(data);
+ if (ret)
+ return ret;
+ ret = kb9002_fw_read(data, KB9002_FW_REG_VID, &vid);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "VID read failed after host-interface switch\n");
+ }
+ if (FIELD_GET(KB9002_VID_MASK, vid) != KB9002_VID_KANDOU)
+ return dev_err_probe(dev, -ENODEV,
+ "unexpected VID 0x%08x\n", vid);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, KB9002_DEV_NAME,
+ data,
+ &kb9002_chip_info,
+ NULL);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ data->hwmon_dev = hwmon_dev;
+ kb9002_debugfs_init(data);
+ return 0;
+}
+
+static const struct i2c_device_id kb9002_id[] = {
+ { .name = KB9002_DEV_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, kb9002_id);
+
+static const struct of_device_id kb9002_of_match[] = {
+ { .compatible = "kandou,kb9002" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, kb9002_of_match);
+
+static struct i2c_driver kb9002_driver = {
+ .driver = {
+ .name = KB9002_DEV_NAME,
+ .of_match_table = kb9002_of_match,
+ },
+ .probe = kb9002_probe,
+ .id_table = kb9002_id,
+};
+module_i2c_driver(kb9002_driver);
+
+MODULE_AUTHOR("Andy Chung <andy.chung@amd.com>");
+MODULE_DESCRIPTION("Kandou KB9002 PCIe retimer hwmon driver");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 4/4] hwmon: (kb9002) Add documentation
2026-07-21 6:31 [PATCH v3 0/4] hwmon: Add Kandou KB9002 PCIe retimer driver Andy Chung via B4 Relay
` (2 preceding siblings ...)
2026-07-21 6:31 ` [PATCH v3 3/4] hwmon: (kb9002) Add driver for Kandou KB9002 retimer Andy Chung via B4 Relay
@ 2026-07-21 6:31 ` Andy Chung via B4 Relay
3 siblings, 0 replies; 10+ messages in thread
From: Andy Chung via B4 Relay @ 2026-07-21 6:31 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andy Chung,
Guenter Roeck, Jonathan Corbet, Shuah Khan
Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, Andy Chung
From: Andy Chung <Andy.Chung@amd.com>
Document the sysfs and debugfs interfaces of the Kandou KB9002 hwmon
driver.
Signed-off-by: Andy Chung <Andy.Chung@amd.com>
---
Documentation/hwmon/index.rst | 1 +
Documentation/hwmon/kb9002.rst | 65 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 66 insertions(+)
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 226789376217..9dc796d087dc 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -112,6 +112,7 @@ Hardware Monitoring Kernel Drivers
jc42
k10temp
k8temp
+ kb9002
kbatt
kfan
lan966x
diff --git a/Documentation/hwmon/kb9002.rst b/Documentation/hwmon/kb9002.rst
new file mode 100644
index 000000000000..e6d8d9c78923
--- /dev/null
+++ b/Documentation/hwmon/kb9002.rst
@@ -0,0 +1,65 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Kernel driver kb9002
+====================
+
+Supported chips:
+
+ * Kandou KB9002
+
+ Prefix: 'kb9002'
+
+ Addresses scanned: -
+
+ Datasheet: KA-015171-PD (available from Kandou under NDA)
+
+Author: Andy Chung <andy.chung@amd.com>
+
+Description
+-----------
+
+The Kandou KB9002 is an 8-lane PCIe 5.0 retimer with an integrated
+microcontroller. It exposes an SMBus 3.0 target (with mandatory PEC)
+on its sideband interface. The internal firmware aggregates per-lane
+die temperatures and publishes the maximum value through a 16-bit
+addressed register window.
+
+This driver reports that aggregated maximum as the only hwmon
+temperature channel. The running firmware version and the firmware
+boot status are exposed under debugfs.
+
+sysfs interface
+---------------
+
+================== ===============================================
+temp1_input Aggregated maximum die temperature across all
+ active lanes (millidegrees Celsius).
+temp1_label Always "kb9002".
+================== ===============================================
+
+debugfs interface
+-----------------
+
+Files live in the per-client debugfs directory created by the I2C
+core: ``/sys/kernel/debug/i2c/i2c-<bus>/<bus>-<addr>/``.
+
+================== ===============================================
+fw_ver Running firmware version in
+ "major.minor.patch.suffix" format. Read-only.
+fw_load_status Firmware boot status: "normal" once firmware has
+ finished initialising, "abnormal" otherwise.
+ Read-only.
+================== ===============================================
+
+Notes
+-----
+
+The driver requires ``I2C_FUNC_SMBUS_BLOCK_DATA``,
+``I2C_FUNC_SMBUS_PEC`` and ``I2C_FUNC_I2C`` from the host adapter. The
+last is needed only during probe for the host-interface mode switch;
+runtime accesses use SMBus block transactions exclusively.
+
+The retimer's SMBus address is configurable on three strap pins and
+ranges from 0x20 to 0x27. The address is selected through device tree
+(or i2c board info) the same way as any other I2C device; the driver
+does not auto-detect.
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v3 1/4] dt-bindings: Add vendor prefix for Kandou
2026-07-21 6:31 ` [PATCH v3 1/4] dt-bindings: Add vendor prefix for Kandou Andy Chung via B4 Relay
@ 2026-07-21 8:27 ` Krzysztof Kozlowski
0 siblings, 0 replies; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-21 8:27 UTC (permalink / raw)
To: Andy.Chung, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Guenter Roeck, Jonathan Corbet, Shuah Khan
Cc: devicetree, linux-kernel, linux-hwmon, linux-doc
On 21/07/2026 08:31, Andy Chung via B4 Relay wrote:
> From: Andy Chung <Andy.Chung@amd.com>
>
> Kandou Bus, S.A. is the vendor of the KB9002 PCIe retimer.
>
> Signed-off-by: Andy Chung <Andy.Chung@amd.com>
> ---
Bad timing, I replied under v2.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v3 3/4] hwmon: (kb9002) Add driver for Kandou KB9002 retimer
2026-07-21 6:31 ` [PATCH v3 3/4] hwmon: (kb9002) Add driver for Kandou KB9002 retimer Andy Chung via B4 Relay
@ 2026-07-21 14:43 ` Guenter Roeck
2026-07-24 5:25 ` Andy Chung
0 siblings, 1 reply; 10+ messages in thread
From: Guenter Roeck @ 2026-07-21 14:43 UTC (permalink / raw)
To: Andy.Chung, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan
Cc: devicetree, linux-kernel, linux-hwmon, linux-doc
On 7/20/26 23:31, Andy Chung via B4 Relay wrote:
> From: Andy Chung <Andy.Chung@amd.com>
>
> The Kandou KB9002 is an 8-lane PCIe 5.0 retimer that exposes an SMBus
> target with mandatory PEC. Add a hwmon driver reporting the firmware
> aggregated maximum die temperature as temp1_input, with the firmware
> version and boot status under debugfs.
>
> Signed-off-by: Andy Chung <Andy.Chung@amd.com>
> ---
> MAINTAINERS | 8 +
> drivers/hwmon/Kconfig | 11 ++
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/kb9002.c | 469 +++++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 489 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 9b4b1575bdbd..1d80ac2660e2 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -13931,6 +13931,14 @@ S: Maintained
> F: Documentation/hwmon/k8temp.rst
> F: drivers/hwmon/k8temp.c
>
> +KANDOU KB9002 PCIE RETIMER HWMON DRIVER
> +M: Andy Chung <andy.chung@amd.com>
> +L: linux-hwmon@vger.kernel.org
> +S: Maintained
> +F: Documentation/devicetree/bindings/hwmon/kandou,kb9002.yaml
> +F: Documentation/hwmon/kb9002.rst
> +F: drivers/hwmon/kb9002.c
> +
> KASAN
> M: Andrey Ryabinin <ryabinin.a.a@gmail.com>
> R: Alexander Potapenko <glider@google.com>
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 08c29685126a..300329773e87 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -335,6 +335,17 @@ config SENSORS_K10TEMP
> This driver can also be built as a module. If so, the module
> will be called k10temp.
>
> +config SENSORS_KB9002
> + tristate "Kandou KB9002 PCIe retimer"
> + depends on I2C
> + help
> + If you say yes here you get support for the integrated
> + temperature sensor and firmware version readout on Kandou
> + KB9002 PCIe 5.0 retimers, accessed over SMBus.
> +
> + This driver can also be built as a module. If so, the module
> + will be called kb9002.
> +
> config SENSORS_KBATT
> tristate "KEBA battery controller support"
> depends on KEBA_CP500
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 63809eeec2f4..0cad7e21634c 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -113,6 +113,7 @@ obj-$(CONFIG_SENSORS_IT87) += it87.o
> obj-$(CONFIG_SENSORS_JC42) += jc42.o
> obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
> obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o
> +obj-$(CONFIG_SENSORS_KB9002) += kb9002.o
> obj-$(CONFIG_SENSORS_KBATT) += kbatt.o
> obj-$(CONFIG_SENSORS_KFAN) += kfan.o
> obj-$(CONFIG_SENSORS_LAN966X) += lan966x-hwmon.o
> diff --git a/drivers/hwmon/kb9002.c b/drivers/hwmon/kb9002.c
> new file mode 100644
> index 000000000000..61a6e522e770
> --- /dev/null
> +++ b/drivers/hwmon/kb9002.c
> @@ -0,0 +1,469 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Kandou KB9002 PCIe 5.0 retimer hwmon driver.
> + *
> + * The retimer exposes a system management bus (SMBus 3.0 with PEC)
> + * target for firmware-managed status registers. This driver assumes
> + * the chip is strapped to SMBus mode and exports the aggregated
> + * maximum die temperature as hwmon temp1_input (millidegrees Celsius)
> + * plus the firmware version and boot status under debugfs.
> + *
> + * Datasheet: Kandou KB9002 PCIe retimer (KA-015171-PD).
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/bitops.h>
> +#include <linux/cleanup.h>
> +#include <linux/debugfs.h>
> +#include <linux/err.h>
> +#include <linux/hwmon.h>
> +#include <linux/i2c.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/seq_file.h>
> +#include <linux/types.h>
> +#include <linux/unaligned.h>
> +
> +#define KB9002_DEV_NAME "kb9002"
> +
> +/*
> + * SMBus read command codes. Each read is a two-phase PEC-protected
> + * transaction: prime writes the target address, data reads it back with
> + * the register contents. FW reads use a 16-bit address, HW reads 32-bit.
> + */
> +#define KB9002_CC_FW_READ_PRIME 0x82
> +#define KB9002_CC_FW_READ_DATA 0x81
> +#define KB9002_CC_HW_READ_PRIME 0x8a
> +#define KB9002_CC_HW_READ_DATA 0x89
> +
> +/* Firmware register offsets (16-bit). */
> +#define KB9002_FW_REG_VID 0x0004
> +#define KB9002_FW_REG_FW_VERSION 0x0500
> +#define KB9002_FW_REG_TEMP_MAXIMUM 0x0550
> +
> +#define KB9002_VID_MASK GENMASK(31, 16)
> +#define KB9002_VID_KANDOU 0x1e6f
> +
> +/* Firmware boot status: 0xe8 in the top byte means init completed OK. */
> +#define KB9002_HW_REG_FW_BOOT_STATUS 0xe0090008
> +#define KB9002_FW_BOOT_STATUS_OK_MSB 0xe8
> +
> +/*
> + * Hardware registers reached over raw I2C (32-bit addressing). The
> + * host-interface bit selects SMBus (set) vs raw-I2C target; parts
> + * strapped to raw I2C need it set before SMBus access works.
> + */
> +#define KB9002_HW_REG_REVID 0x00480004
> +#define KB9002_HW_REG_HOST_IF 0x00480008
> +#define KB9002_HOST_IF_SMBUS BIT(1)
> +
> +#define KB9002_REVID_MASK GENMASK(7, 0)
> +#define KB9002_REVID_B0 0x10
> +#define KB9002_REVID_B1 0x11
> +
> +/* Retries to drain a stray leading 0xff from the raw-I2C FIFO. */
> +#define KB9002_REVID_READ_RETRIES 16
> +
> +/* Temperature: 32-bit Q16.16 absolute Kelvin. */
> +#define KB9002_TEMP_FRAC_BITS 16
> +#define KB9002_ABS_ZERO_MILLI_C (-273150)
> +
> +/* Firmware takes up to ~2s to respond after a host-interface change. */
> +#define KB9002_FW_READY_POLL_US (25 * USEC_PER_MSEC)
> +#define KB9002_FW_READY_TIMEOUT_US (2 * USEC_PER_SEC)
> +
> +struct kb9002_data {
> + struct i2c_client *client;
> + struct device *hwmon_dev;
> +};
> +
> +/* Raw-I2C read: write the 32-bit BE address, then read 4 BE data bytes. */
> +static int kb9002_i2c_read(struct i2c_client *client, u32 reg, u32 *val)
> +{
> + u8 addr[4];
> + u8 rbuf[4];
> + struct i2c_msg msgs[2] = {
> + {
> + .addr = client->addr,
> + .flags = 0,
> + .len = sizeof(addr),
> + .buf = addr,
> + },
> + {
> + .addr = client->addr,
> + .flags = I2C_M_RD,
> + .len = sizeof(rbuf),
> + .buf = rbuf,
> + },
> + };
> + int ret;
> +
> + put_unaligned_be32(reg, addr);
> +
> + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
> + if (ret < 0)
> + return ret;
> + if (ret != ARRAY_SIZE(msgs))
> + return -EIO;
> +
> + *val = get_unaligned_be32(rbuf);
> + return 0;
> +}
> +
> +/* Raw-I2C write: 4 BE address bytes followed by 4 BE data bytes. */
> +static int kb9002_i2c_write(struct i2c_client *client, u32 reg, u32 val)
> +{
> + u8 buf[8];
> + struct i2c_msg msg = {
> + .addr = client->addr,
> + .flags = 0,
> + .len = sizeof(buf),
> + .buf = buf,
> + };
> + int ret;
> +
> + put_unaligned_be32(reg, &buf[0]);
> + put_unaligned_be32(val, &buf[4]);
> +
> + ret = i2c_transfer(client->adapter, &msg, 1);
> + if (ret < 0)
> + return ret;
> + if (ret != 1)
> + return -EIO;
> +
> + return 0;
> +}
> +
> +/*
> + * Read the silicon revision ID. A fresh FIFO may start with a stray
> + * 0xff that shifts the result, so drain one byte between retries until
> + * the top byte is no longer 0xff.
> + */
> +static int kb9002_read_revid(struct i2c_client *client, u32 *revid)
> +{
> + u8 dummy;
> + int ret;
> + int i;
> +
> + for (i = 0; i < KB9002_REVID_READ_RETRIES; i++) {
> + ret = kb9002_i2c_read(client, KB9002_HW_REG_REVID, revid);
> + if (ret)
> + return ret;
> + if ((*revid >> 24) != 0xff)
> + return 0;
> + /* Drain one byte from the chip to re-align the I2C FIFO. */
> + i2c_master_recv(client, &dummy, 1);
> + }
> +
> + return -EIO;
> +}
> +
> +/*
> + * Read a 32-bit firmware register over SMBus: block-write the 16-bit LE
> + * address, then block-read the echoed address plus 4 LE data bytes.
> + */
> +static int kb9002_fw_read(struct kb9002_data *data, u16 reg, u32 *val)
> +{
> + struct i2c_client *client = data->client;
> + u8 addr[2];
> + u8 rbuf[I2C_SMBUS_BLOCK_MAX];
> + int ret;
> +
> + put_unaligned_le16(reg, addr);
> +
> + ret = i2c_smbus_write_block_data(client, KB9002_CC_FW_READ_PRIME,
> + sizeof(addr), addr);
> + if (ret < 0)
> + return ret;
> +
> + ret = i2c_smbus_read_block_data(client, KB9002_CC_FW_READ_DATA, rbuf);
> + if (ret < 0)
> + return ret;
> + if (ret < (int)(sizeof(addr) + sizeof(*val)))
> + return -EIO;
> +
> + *val = get_unaligned_le32(&rbuf[sizeof(addr)]);
> + return 0;
> +}
> +
> +/* Like kb9002_fw_read but for a hardware register (32-bit LE address). */
> +static int kb9002_smbus_hw_read(struct kb9002_data *data, u32 reg, u32 *val)
> +{
> + struct i2c_client *client = data->client;
> + u8 addr[4];
> + u8 rbuf[I2C_SMBUS_BLOCK_MAX];
> + int ret;
> +
> + put_unaligned_le32(reg, addr);
> +
Just for verification: Some accesses are LE, others are BE. This is unusual.
To help understand what is LE and what is BE, please add a comment at the
top o the file describing which accesses are LE and which accesses are BE.
> + ret = i2c_smbus_write_block_data(client, KB9002_CC_HW_READ_PRIME,
> + sizeof(addr), addr);
> + if (ret < 0)
> + return ret;
> +
> + ret = i2c_smbus_read_block_data(client, KB9002_CC_HW_READ_DATA, rbuf);
> + if (ret < 0)
> + return ret;
> + if (ret < (int)(sizeof(addr) + sizeof(*val)))
> + return -EIO;
> +
> + *val = get_unaligned_le32(&rbuf[sizeof(addr)]);
> + return 0;
> +}
> +
> +/*
> + * Switch the host interface from raw-I2C to SMBus and wait for firmware
> + * to come back up. Called only when SMBus access failed in probe, i.e.
> + * the chip is strapped to raw-I2C mode. Confirms the revision, sets the
> + * SMBus-mode bit, then polls until firmware responds again.
> + */
> +static int kb9002_enable_smbus_target(struct kb9002_data *data)
> +{
> + struct i2c_client *client = data->client;
> + u32 revid;
> + u32 val;
> + int op_ret;
> + int ret;
> +
> + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
> + return dev_err_probe(&client->dev, -ENODEV,
> + "raw I2C required to switch to SMBus mode\n");
> +
> + ret = kb9002_read_revid(client, &revid);
> + if (ret)
> + return dev_err_probe(&client->dev, ret,
> + "revision ID read failed\n");
> +
> + switch (FIELD_GET(KB9002_REVID_MASK, revid)) {
> + case KB9002_REVID_B0:
> + case KB9002_REVID_B1:
> + break;
> + default:
> + return dev_err_probe(&client->dev, -ENODEV,
> + "unsupported revision ID 0x%08x\n", revid);
> + }
> +
> + ret = kb9002_i2c_read(client, KB9002_HW_REG_HOST_IF, &val);
> + if (ret)
> + return dev_err_probe(&client->dev, ret,
> + "host interface read failed\n");
> +
> + val |= KB9002_HOST_IF_SMBUS;
> +
> + ret = kb9002_i2c_write(client, KB9002_HW_REG_HOST_IF, val);
> + if (ret)
> + return dev_err_probe(&client->dev, ret,
> + "host interface write failed\n");
> +
> + /* Wait until firmware re-initialisation completes. */
> + ret = read_poll_timeout(kb9002_fw_read, op_ret, op_ret == 0,
> + KB9002_FW_READY_POLL_US,
> + KB9002_FW_READY_TIMEOUT_US, true,
> + data, KB9002_FW_REG_VID, &val);
> + if (ret)
> + return dev_err_probe(&client->dev, ret,
> + "firmware not responding over SMBus\n");
> +
> + return 0;
> +}
> +
> +/* Convert Q16.16 absolute Kelvin to millidegrees Celsius. */
> +static long kb9002_temp_to_milli_c(u32 raw)
> +{
> + s64 milli_k = ((s64)raw * 1000) >> KB9002_TEMP_FRAC_BITS;
> +
> + return (long)milli_k + KB9002_ABS_ZERO_MILLI_C;
> +}
> +
> +static int kb9002_read_temp(struct kb9002_data *data, long *val)
> +{
> + u32 raw;
> + int ret;
> +
> + ret = kb9002_fw_read(data, KB9002_FW_REG_TEMP_MAXIMUM, &raw);
> + if (ret)
> + return ret;
> +
> + *val = kb9002_temp_to_milli_c(raw);
> + return 0;
> +}
> +
> +static umode_t kb9002_is_visible(const void *drvdata,
> + enum hwmon_sensor_types type,
> + u32 attr, int channel)
> +{
> + if (type == hwmon_temp &&
> + (attr == hwmon_temp_input || attr == hwmon_temp_label))
> + return 0444;
> + return 0;
> +}
In this context, the function can just return 0444 without any checks.
> +
> +static int kb9002_read(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long *val)
> +{
> + struct kb9002_data *data = dev_get_drvdata(dev);
> +
> + if (type == hwmon_temp && attr == hwmon_temp_input)
> + return kb9002_read_temp(data, val);
> +
> + return -EOPNOTSUPP;
> +}
> +
> +static int kb9002_read_string(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, const char **str)
> +{
> + if (type == hwmon_temp && attr == hwmon_temp_label) {
> + *str = KB9002_DEV_NAME;
> + return 0;
> + }
> + return -EOPNOTSUPP;
> +}
This is a pointless attribute. The label attribute is supposed to return
the sensor location, not the chip name. The chip name is already reported
in the "name" attribute. Please drop.
> +
> +static const struct hwmon_ops kb9002_hwmon_ops = {
> + .is_visible = kb9002_is_visible,
> + .read = kb9002_read,
> + .read_string = kb9002_read_string,
> +};
> +
> +static const struct hwmon_channel_info * const kb9002_hwmon_info[] = {
> + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL),
> + NULL,
> +};
> +
> +static const struct hwmon_chip_info kb9002_chip_info = {
> + .ops = &kb9002_hwmon_ops,
> + .info = kb9002_hwmon_info,
> +};
> +
> +static int kb9002_fw_version_show(struct seq_file *s, void *unused)
> +{
> + struct kb9002_data *data = s->private;
> + u32 ver;
> + int ret;
> +
> + guard(hwmon_lock)(data->hwmon_dev);
> +
> + ret = kb9002_fw_read(data, KB9002_FW_REG_FW_VERSION, &ver);
> + if (ret)
> + return ret;
> +
> + seq_printf(s, "%u.%02u.%02u.%u\n",
> + (ver >> 24) & 0xff, (ver >> 16) & 0xff,
> + (ver >> 8) & 0xff, (ver >> 0) & 0xff);
> + return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(kb9002_fw_version);
> +
> +static int kb9002_fw_load_status_show(struct seq_file *s, void *unused)
> +{
> + struct kb9002_data *data = s->private;
> + u32 status;
> + int ret;
> +
> + guard(hwmon_lock)(data->hwmon_dev);
> +
> + ret = kb9002_smbus_hw_read(data, KB9002_HW_REG_FW_BOOT_STATUS, &status);
> + if (ret)
> + return ret;
> +
> + seq_printf(s, "%s\n",
> + (status >> 24) == KB9002_FW_BOOT_STATUS_OK_MSB ?
> + "normal" : "abnormal");
> + return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(kb9002_fw_load_status);
> +
> +static void kb9002_debugfs_init(struct kb9002_data *data)
> +{
> + struct dentry *dir = data->client->debugfs;
> +
> + debugfs_create_file("fw_ver", 0444, dir, data,
> + &kb9002_fw_version_fops);
> + debugfs_create_file("fw_load_status", 0444, dir, data,
> + &kb9002_fw_load_status_fops);
> +}
> +
> +static int kb9002_probe(struct i2c_client *client)
> +{
> + struct device *dev = &client->dev;
> + struct kb9002_data *data;
> + struct device *hwmon_dev;
> + u32 vid;
> + int ret;
> +
> + if (!i2c_check_functionality(client->adapter,
> + I2C_FUNC_SMBUS_BLOCK_DATA |
> + I2C_FUNC_SMBUS_PEC))
> + return -ENODEV;
> +
> + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + data->client = client;
> +
> + /* All firmware register accesses are PEC-protected. */
> + client->flags |= I2C_CLIENT_PEC;
> +
Doesn't this depend on controller PEC support (I2C_FUNC_SMBUS_PEC) ?
Also, the driver depends on I2C_FUNC_SMBUS_BLOCK_DATA support,
which is not always available. I would suggest to check for both.
Thanks,
Guenter
> + i2c_set_clientdata(client, data);
> +
> + /*
> + * Try SMBus first. If the chip is strapped to raw-I2C mode it
> + * will not respond to SMBus framing, so fall back to switching
> + * the host interface over raw I2C and retry.
> + */
> + ret = kb9002_fw_read(data, KB9002_FW_REG_VID, &vid);
> + if (ret) {
> + dev_dbg(dev, "SMBus probe failed (%d), trying raw-I2C host-interface switch\n",
> + ret);
> + ret = kb9002_enable_smbus_target(data);
> + if (ret)
> + return ret;
> + ret = kb9002_fw_read(data, KB9002_FW_REG_VID, &vid);
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "VID read failed after host-interface switch\n");
> + }
> + if (FIELD_GET(KB9002_VID_MASK, vid) != KB9002_VID_KANDOU)
> + return dev_err_probe(dev, -ENODEV,
> + "unexpected VID 0x%08x\n", vid);
> +
> + hwmon_dev = devm_hwmon_device_register_with_info(dev, KB9002_DEV_NAME,
> + data,
> + &kb9002_chip_info,
> + NULL);
> + if (IS_ERR(hwmon_dev))
> + return PTR_ERR(hwmon_dev);
> +
> + data->hwmon_dev = hwmon_dev;
> + kb9002_debugfs_init(data);
> + return 0;
> +}
> +
> +static const struct i2c_device_id kb9002_id[] = {
> + { .name = KB9002_DEV_NAME },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, kb9002_id);
> +
> +static const struct of_device_id kb9002_of_match[] = {
> + { .compatible = "kandou,kb9002" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, kb9002_of_match);
> +
> +static struct i2c_driver kb9002_driver = {
> + .driver = {
> + .name = KB9002_DEV_NAME,
> + .of_match_table = kb9002_of_match,
> + },
> + .probe = kb9002_probe,
> + .id_table = kb9002_id,
> +};
> +module_i2c_driver(kb9002_driver);
> +
> +MODULE_AUTHOR("Andy Chung <andy.chung@amd.com>");
> +MODULE_DESCRIPTION("Kandou KB9002 PCIe retimer hwmon driver");
> +MODULE_LICENSE("GPL");
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v3 2/4] dt-bindings: hwmon: Add Kandou KB9002
2026-07-21 6:31 ` [PATCH v3 2/4] dt-bindings: hwmon: Add Kandou KB9002 Andy Chung via B4 Relay
@ 2026-07-22 7:41 ` Krzysztof Kozlowski
2026-07-24 7:28 ` Andy Chung
0 siblings, 1 reply; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-22 7:41 UTC (permalink / raw)
To: Andy Chung
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Guenter Roeck,
Jonathan Corbet, Shuah Khan, devicetree, linux-kernel,
linux-hwmon, linux-doc
On Tue, Jul 21, 2026 at 02:31:33PM +0800, Andy Chung wrote:
> Add device tree bindings for the Kandou KB9002 PCIe 5.0 retimer, an
> SMBus target that exposes an aggregated die temperature.
>
> Signed-off-by: Andy Chung <Andy.Chung@amd.com>
> ---
> .../devicetree/bindings/hwmon/kandou,kb9002.yaml | 46 ++++++++++++++++++++++
> 1 file changed, 46 insertions(+)
>
So a trivial device and can go to trivial-devices? What is incomplete
here, what is missing?
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v3 3/4] hwmon: (kb9002) Add driver for Kandou KB9002 retimer
2026-07-21 14:43 ` Guenter Roeck
@ 2026-07-24 5:25 ` Andy Chung
0 siblings, 0 replies; 10+ messages in thread
From: Andy Chung @ 2026-07-24 5:25 UTC (permalink / raw)
To: Guenter Roeck
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
Shuah Khan, devicetree, linux-kernel, linux-hwmon, linux-doc,
Andy Chung
On Tue, 21 Jul 2026 07:43:11 -0700, Guenter Roeck wrote:
> Doesn't this depend on controller PEC support (I2C_FUNC_SMBUS_PEC) ?
> Also, the driver depends on I2C_FUNC_SMBUS_BLOCK_DATA support,
> which is not always available. I would suggest to check for both.
probe() already requires both before setting I2C_CLIENT_PEC:
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BLOCK_DATA |
I2C_FUNC_SMBUS_PEC))
return -ENODEV;
so the flag is only set on adapters that support PEC. The endianness,
is_visible and temp1_label comments are addressed in v5.
Thanks,
Andy
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v3 2/4] dt-bindings: hwmon: Add Kandou KB9002
2026-07-22 7:41 ` Krzysztof Kozlowski
@ 2026-07-24 7:28 ` Andy Chung
0 siblings, 0 replies; 10+ messages in thread
From: Andy Chung @ 2026-07-24 7:28 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Guenter Roeck,
Jonathan Corbet, Shuah Khan, devicetree, linux-kernel,
linux-hwmon, linux-doc, Andy Chung
On Wed, 22 Jul 2026 09:41:29 +0200, Krzysztof Kozlowski wrote:
> So a trivial device and can go to trivial-devices? What is incomplete
> here, what is missing?
Right, nothing beyond compatible and reg. v5 drops the dedicated binding
and adds kandou,kb9002 to trivial-devices.yaml. I also changed the
vendor-prefix description to "Kandou AI" per your other comment
("Kandou Bus SA" is the registered legal entity, "Kandou AI" the current
branding -- same company).
Thanks,
Andy
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-07-24 7:29 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-21 6:31 [PATCH v3 0/4] hwmon: Add Kandou KB9002 PCIe retimer driver Andy Chung via B4 Relay
2026-07-21 6:31 ` [PATCH v3 1/4] dt-bindings: Add vendor prefix for Kandou Andy Chung via B4 Relay
2026-07-21 8:27 ` Krzysztof Kozlowski
2026-07-21 6:31 ` [PATCH v3 2/4] dt-bindings: hwmon: Add Kandou KB9002 Andy Chung via B4 Relay
2026-07-22 7:41 ` Krzysztof Kozlowski
2026-07-24 7:28 ` Andy Chung
2026-07-21 6:31 ` [PATCH v3 3/4] hwmon: (kb9002) Add driver for Kandou KB9002 retimer Andy Chung via B4 Relay
2026-07-21 14:43 ` Guenter Roeck
2026-07-24 5:25 ` Andy Chung
2026-07-21 6:31 ` [PATCH v3 4/4] hwmon: (kb9002) Add documentation Andy Chung via B4 Relay
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox