* [PATCH v2 0/4] hwmon: Add Kandou KB9002 PCIe retimer driver
@ 2026-07-15 9:08 ` Andy Chung
0 siblings, 0 replies; 14+ messages in thread
From: Andy Chung via B4 Relay @ 2026-07-15 9:08 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 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 | 45 ++
.../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 | 479 +++++++++++++++++++++
8 files changed, 612 insertions(+)
---
base-commit: ca078d004cf58137bcf8cb24a8b271397431ba58
change-id: 20260713-kb9002-upstream-06c686e37833
Best regards,
--
Andy Chung <Andy.Chung@amd.com>
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v2 0/4] hwmon: Add Kandou KB9002 PCIe retimer driver @ 2026-07-15 9:08 ` Andy Chung 0 siblings, 0 replies; 14+ messages in thread From: Andy Chung @ 2026-07-15 9:08 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 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 | 45 ++ .../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 | 479 +++++++++++++++++++++ 8 files changed, 612 insertions(+) --- base-commit: ca078d004cf58137bcf8cb24a8b271397431ba58 change-id: 20260713-kb9002-upstream-06c686e37833 Best regards, -- Andy Chung <Andy.Chung@amd.com> ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 1/4] dt-bindings: Add vendor prefix for Kandou 2026-07-15 9:08 ` Andy Chung @ 2026-07-15 9:08 ` Andy Chung -1 siblings, 0 replies; 14+ messages in thread From: Andy Chung via B4 Relay @ 2026-07-15 9:08 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] 14+ messages in thread
* [PATCH v2 1/4] dt-bindings: Add vendor prefix for Kandou @ 2026-07-15 9:08 ` Andy Chung 0 siblings, 0 replies; 14+ messages in thread From: Andy Chung @ 2026-07-15 9:08 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 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] 14+ messages in thread
* Re: [PATCH v2 1/4] dt-bindings: Add vendor prefix for Kandou 2026-07-15 9:08 ` Andy Chung (?) @ 2026-07-15 9:10 ` sashiko-bot -1 siblings, 0 replies; 14+ messages in thread From: sashiko-bot @ 2026-07-15 9:10 UTC (permalink / raw) To: Andy Chung; +Cc: devicetree, linux-hwmon, conor+dt, robh > 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> Sashiko has reviewed this patch and found no issues. It looks great! -- Sashiko AI review · https://sashiko.dev/#/patchset/20260715-kb9002-upstream-v2-0-2fd390383da5@amd.com?part=1 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 2/4] dt-bindings: hwmon: Add Kandou KB9002 2026-07-15 9:08 ` Andy Chung @ 2026-07-15 9:08 ` Andy Chung -1 siblings, 0 replies; 14+ messages in thread From: Andy Chung via B4 Relay @ 2026-07-15 9:08 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 | 45 ++++++++++++++++++++++ 1 file changed, 45 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..67859e9d63c2 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/kandou,kb9002.yaml @@ -0,0 +1,45 @@ +# 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: + 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] 14+ messages in thread
* [PATCH v2 2/4] dt-bindings: hwmon: Add Kandou KB9002 @ 2026-07-15 9:08 ` Andy Chung 0 siblings, 0 replies; 14+ messages in thread From: Andy Chung @ 2026-07-15 9:08 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 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 | 45 ++++++++++++++++++++++ 1 file changed, 45 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..67859e9d63c2 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/kandou,kb9002.yaml @@ -0,0 +1,45 @@ +# 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: + 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] 14+ messages in thread
* Re: [PATCH v2 2/4] dt-bindings: hwmon: Add Kandou KB9002 2026-07-15 9:08 ` Andy Chung (?) @ 2026-07-15 9:16 ` sashiko-bot -1 siblings, 0 replies; 14+ messages in thread From: sashiko-bot @ 2026-07-15 9:16 UTC (permalink / raw) To: Andy Chung; +Cc: robh, conor+dt, devicetree, linux-hwmon Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] The `reg` property is missing the `maxItems: 1` constraint and incorrectly applies `minimum` and `maximum` constraints directly to the array instance. - [Low] The device tree binding for a PCIe retimer is incorrectly placed in the hwmon/ directory. -- --- Patch [2]: [PATCH v2 2/4] dt-bindings: hwmon: Add Kandou KB9002 --- commit e4c6f2bbc23572a071a648919c1c0c0e4520ad76 Author: Andy Chung <Andy.Chung@amd.com> dt-bindings: hwmon: Add Kandou KB9002 Add device tree bindings for the Kandou KB9002 PCIe 5.0 retimer, an SMBus target that exposes an aggregated die temperature. > diff --git a/Documentation/devicetree/bindings/hwmon/kandou,kb9002.yaml b/Documentation/devicetree/bindings/hwmon/kandou,kb9002.yaml > new file mode 100644 > index 0000000000000..67859e9d63c25 > --- /dev/null > +++ b/Documentation/devicetree/bindings/hwmon/kandou,kb9002.yaml > @@ -0,0 +1,45 @@ > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) > +%YAML 1.2 > +--- > +$id: http://devicetree.org/schemas/hwmon/kandou,kb9002.yaml# [Severity: Low] Does this binding belong in the hwmon directory? Since this device is primarily an 8-lane PCIe 5.0 retimer rather than a dedicated hardware monitor, it seems like the binding should be placed in a physical category directory like phy/ or pci/ instead. [ ... ] > +properties: > + compatible: > + const: kandou,kb9002 > + > + reg: > + minimum: 0x20 > + maximum: 0x27 [Severity: Medium] Can this correctly constrain the I2C address? The reg property evaluates as an array, so omitting maxItems: 1 allows multiple addresses to pass validation. Furthermore, applying numeric constraints directly to the array rather than its items causes them to be silently ignored by the validator, allowing out-of-range addresses to pass. > + > +required: > + - compatible > + - reg -- Sashiko AI review · https://sashiko.dev/#/patchset/20260715-kb9002-upstream-v2-0-2fd390383da5@amd.com?part=2 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 3/4] hwmon: (kb9002) Add driver for Kandou KB9002 retimer 2026-07-15 9:08 ` Andy Chung @ 2026-07-15 9:08 ` Andy Chung -1 siblings, 0 replies; 14+ messages in thread From: Andy Chung via B4 Relay @ 2026-07-15 9:08 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 | 479 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 499 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..94c7d95b3fff --- /dev/null +++ b/drivers/hwmon/kb9002.c @@ -0,0 +1,479 @@ +// 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/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/mutex.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 mutex lock; /* serialises register accesses */ +}; + +/* 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); + + mutex_lock(&data->lock); + + ret = i2c_smbus_write_block_data(client, KB9002_CC_FW_READ_PRIME, + sizeof(addr), addr); + if (ret < 0) + goto out; + + ret = i2c_smbus_read_block_data(client, KB9002_CC_FW_READ_DATA, rbuf); + if (ret < 0) + goto out; + if (ret < (int)(sizeof(addr) + sizeof(*val))) { + ret = -EIO; + goto out; + } + + *val = get_unaligned_le32(&rbuf[sizeof(addr)]); + ret = 0; +out: + mutex_unlock(&data->lock); + return ret; +} + +/* 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); + + mutex_lock(&data->lock); + + ret = i2c_smbus_write_block_data(client, KB9002_CC_HW_READ_PRIME, + sizeof(addr), addr); + if (ret < 0) + goto out; + + ret = i2c_smbus_read_block_data(client, KB9002_CC_HW_READ_DATA, rbuf); + if (ret < 0) + goto out; + if (ret < (int)(sizeof(addr) + sizeof(*val))) { + ret = -EIO; + goto out; + } + + *val = get_unaligned_le32(&rbuf[sizeof(addr)]); + ret = 0; +out: + mutex_unlock(&data->lock); + return ret; +} + +/* + * 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; + + 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; + + 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; + mutex_init(&data->lock); + + /* 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); + + kb9002_debugfs_init(data); + return 0; +} + +static const struct i2c_device_id kb9002_id[] = { + { 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] 14+ messages in thread
* [PATCH v2 3/4] hwmon: (kb9002) Add driver for Kandou KB9002 retimer @ 2026-07-15 9:08 ` Andy Chung 0 siblings, 0 replies; 14+ messages in thread From: Andy Chung @ 2026-07-15 9:08 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 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 | 479 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 499 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..94c7d95b3fff --- /dev/null +++ b/drivers/hwmon/kb9002.c @@ -0,0 +1,479 @@ +// 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/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/mutex.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 mutex lock; /* serialises register accesses */ +}; + +/* 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); + + mutex_lock(&data->lock); + + ret = i2c_smbus_write_block_data(client, KB9002_CC_FW_READ_PRIME, + sizeof(addr), addr); + if (ret < 0) + goto out; + + ret = i2c_smbus_read_block_data(client, KB9002_CC_FW_READ_DATA, rbuf); + if (ret < 0) + goto out; + if (ret < (int)(sizeof(addr) + sizeof(*val))) { + ret = -EIO; + goto out; + } + + *val = get_unaligned_le32(&rbuf[sizeof(addr)]); + ret = 0; +out: + mutex_unlock(&data->lock); + return ret; +} + +/* 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); + + mutex_lock(&data->lock); + + ret = i2c_smbus_write_block_data(client, KB9002_CC_HW_READ_PRIME, + sizeof(addr), addr); + if (ret < 0) + goto out; + + ret = i2c_smbus_read_block_data(client, KB9002_CC_HW_READ_DATA, rbuf); + if (ret < 0) + goto out; + if (ret < (int)(sizeof(addr) + sizeof(*val))) { + ret = -EIO; + goto out; + } + + *val = get_unaligned_le32(&rbuf[sizeof(addr)]); + ret = 0; +out: + mutex_unlock(&data->lock); + return ret; +} + +/* + * 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; + + 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; + + 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; + mutex_init(&data->lock); + + /* 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); + + kb9002_debugfs_init(data); + return 0; +} + +static const struct i2c_device_id kb9002_id[] = { + { 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] 14+ messages in thread
* Re: [PATCH v2 3/4] hwmon: (kb9002) Add driver for Kandou KB9002 retimer 2026-07-15 9:08 ` Andy Chung (?) @ 2026-07-15 9:17 ` sashiko-bot -1 siblings, 0 replies; 14+ messages in thread From: sashiko-bot @ 2026-07-15 9:17 UTC (permalink / raw) To: Andy Chung; +Cc: conor+dt, robh, linux-hwmon, devicetree Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] The driver implements its own internal mutex for locking hardware accesses instead of using the hwmon subsystem's `hwmon_lock()` API. - [Low] The `i2c_device_id` array is initialized using a positional instead of a named initializer. -- --- Patch [3]: [PATCH v2 3/4] hwmon: (kb9002) Add driver for Kandou KB9002 retimer --- commit 59c6448090144292eb629f4257436694b1eaaa0d Author: Andy Chung <Andy.Chung@amd.com> hwmon: (kb9002) Add driver for Kandou KB9002 retimer 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. > diff --git a/drivers/hwmon/kb9002.c b/drivers/hwmon/kb9002.c > new file mode 100644 > index 0000000000000..94c7d95b3fff5 > --- /dev/null > +++ b/drivers/hwmon/kb9002.c [ ... ] > +struct kb9002_data { > + struct i2c_client *client; > + struct mutex lock; /* serialises register accesses */ > +}; [ ... ] > +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); > + > + mutex_lock(&data->lock); [Severity: Medium] Is this custom mutex necessary? The hwmon subsystem guidelines state that drivers should use hwmon_lock() and hwmon_unlock() to serialize operations for attributes registered by other means. By using a custom internal mutex, does the driver bypass the standard subsystem synchronization architecture and introduce inefficient double-locking on sysfs reads, which are already protected by the hwmon core? [ ... ] > +static const struct i2c_device_id kb9002_id[] = { > + { KB9002_DEV_NAME }, > + { } > +}; [Severity: Low] This isn't a bug, but does this array initialization bypass the I2C subsystem guidelines? The I2C client initialization rules request that initialized arrays of type struct i2c_device_id use named initializers rather than positional initializers. -- Sashiko AI review · https://sashiko.dev/#/patchset/20260715-kb9002-upstream-v2-0-2fd390383da5@amd.com?part=3 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 4/4] hwmon: (kb9002) Add documentation 2026-07-15 9:08 ` Andy Chung @ 2026-07-15 9:08 ` Andy Chung -1 siblings, 0 replies; 14+ messages in thread From: Andy Chung via B4 Relay @ 2026-07-15 9:08 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] 14+ messages in thread
* [PATCH v2 4/4] hwmon: (kb9002) Add documentation @ 2026-07-15 9:08 ` Andy Chung 0 siblings, 0 replies; 14+ messages in thread From: Andy Chung @ 2026-07-15 9:08 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 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] 14+ messages in thread
* Re: [PATCH v2 4/4] hwmon: (kb9002) Add documentation 2026-07-15 9:08 ` Andy Chung (?) @ 2026-07-15 9:19 ` sashiko-bot -1 siblings, 0 replies; 14+ messages in thread From: sashiko-bot @ 2026-07-15 9:19 UTC (permalink / raw) To: Andy Chung; +Cc: robh, devicetree, linux-hwmon, conor+dt Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] The driver implements a custom private mutex to serialize access instead of using the mandated hwmon_lock() API. - [Low] The i2c_device_id array is initialized without named initializers, violating I2C subsystem guidelines. -- --- Patch [4]: [PATCH v2 4/4] hwmon: (kb9002) Add documentation --- commit 9ba497f3aeb63905e9272367f2a7abaf03805f1c Author: Andy Chung <Andy.Chung@amd.com> hwmon: (kb9002) Add documentation Document the sysfs and debugfs interfaces of the Kandou KB9002 hwmon driver. > diff --git a/drivers/hwmon/kb9002.c b/drivers/hwmon/kb9002.c > new file mode 100644 > index 0000000000000..94c7d95b3fff5 > --- /dev/null > +++ b/drivers/hwmon/kb9002.c [ ... ] > +struct kb9002_data { > + struct i2c_client *client; > + struct mutex lock; /* serialises register accesses */ > +}; [Severity: Medium] Does this custom mutex violate the hwmon locking guidelines? The hwmon subsystem core already serializes operations for registered attributes. For attributes registered by other means (such as the debugfs entries added by this driver), the hwmon guidelines state that drivers should use hwmon_lock() and hwmon_unlock() rather than implementing a private lock. Using a custom lock here results in unnecessary double-locking on the sysfs read paths. [ ... ] > +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); > + > + mutex_lock(&data->lock); [Severity: Medium] Can this lock be replaced with hwmon_lock()? The private lock is taken here during firmware reads instead of relying on hwmon core serialization or hwmon_lock(). [ ... ] > +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); > + > + mutex_lock(&data->lock); [Severity: Medium] Similarly, can this hardware read use hwmon_lock() instead of the custom lock? [ ... ] > +static const struct i2c_device_id kb9002_id[] = { > + { KB9002_DEV_NAME }, > + { } > +}; [Severity: Low] Does this array initialization violate I2C subsystem guidelines? The I2C subsystem explicitly mandates that initialized arrays of type struct i2c_device_id must use named initializers. Should this be updated to use .name = KB9002_DEV_NAME? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260715-kb9002-upstream-v2-0-2fd390383da5@amd.com?part=4 ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-07-15 9:19 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-15 9:08 [PATCH v2 0/4] hwmon: Add Kandou KB9002 PCIe retimer driver Andy Chung via B4 Relay 2026-07-15 9:08 ` Andy Chung 2026-07-15 9:08 ` [PATCH v2 1/4] dt-bindings: Add vendor prefix for Kandou Andy Chung via B4 Relay 2026-07-15 9:08 ` Andy Chung 2026-07-15 9:10 ` sashiko-bot 2026-07-15 9:08 ` [PATCH v2 2/4] dt-bindings: hwmon: Add Kandou KB9002 Andy Chung via B4 Relay 2026-07-15 9:08 ` Andy Chung 2026-07-15 9:16 ` sashiko-bot 2026-07-15 9:08 ` [PATCH v2 3/4] hwmon: (kb9002) Add driver for Kandou KB9002 retimer Andy Chung via B4 Relay 2026-07-15 9:08 ` Andy Chung 2026-07-15 9:17 ` sashiko-bot 2026-07-15 9:08 ` [PATCH v2 4/4] hwmon: (kb9002) Add documentation Andy Chung via B4 Relay 2026-07-15 9:08 ` Andy Chung 2026-07-15 9:19 ` sashiko-bot
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.