* [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
* 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
* [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
* 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 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
* [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
* 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 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
* [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
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