* [PATCH 00/11] Add NVIDIA LSTP USB adapter drivers
@ 2026-10-12 17:35 Adrian Ambrozewicz
2026-10-12 17:36 ` [PATCH 02/11] dt-bindings: i2c: Add NVIDIA LSTP I2C Adrian Ambrozewicz
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Adrian Ambrozewicz @ 2026-10-12 17:35 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Donald Shannon, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Linus Walleij, Bartosz Golaszewski, Lee Jones, Pavel Machek,
Andi Shyti, Mark Brown, Jiri Slaby, Corey Minyard,
Rafael J. Wysocki, Len Brown, Jonathan Corbet, Ilpo Järvinen,
Sakari Ailus, Hans de Goede, Lixu Zhang, Danilo Krummrich,
Johan Hovold, Oliver Neukum, Quan Nguyen, Andrew Jeffery,
linux-usb, devicetree, linux-gpio, linux-leds, linux-i2c,
linux-spi, linux-serial, openipmi-developer, linux-acpi,
linux-doc, driver-core, linux-kernel
This series adds drivers for the NVIDIA LSTP USB adapter: a USB core
driver and GPIO, LED, I2C, SPI, UART and BMC-side IPMI function drivers,
with their device tree bindings, an ACPI description document and the
sysfs ABI.
What LSTP is
------------
LSTP (Low-Speed Transport Protocol or Low-Speed Tunnelling Protocol) is
a vendor-specific USB interface (class 0xff, subclass 0x3f) that
multiplexes several low-speed "channels" over one pair of bulk
endpoints. It is implemented by NVIDIA's SMA, an MCU (0955:cf11), and
by other vendors' parts: two hub/bridge chips have it built in
(0424:4940, 174c:2214). Boards use it to
reach SPI flash, I2C sensors, GPIOs and LEDs, a UART and a host-to-BMC
IPMI path, on both BMCs and host systems.
The channel types are SPI host, I2C host, GPIO (with interrupts and LED
pins), UART, IPMI and MMIO. Channel 0 is the control channel: at probe
the driver reads the channel table from it, which gives each channel's
ID, type and configuration. The set of channels is decided by the
firmware, so the same VID:PID carries different channels on different
boards. No firmware description is needed for the channels to work.
Series layout
-------------
1 dt-bindings: gpio: Add NVIDIA LSTP GPIO
2 dt-bindings: i2c: Add NVIDIA LSTP I2C
3 dt-bindings: spi: Add NVIDIA LSTP SPI
4 dt-bindings: usb: Add NVIDIA LSTP USB adapter
5 usb: misc: Add NVIDIA LSTP USB adapter driver
6 gpio: Add NVIDIA LSTP GPIO driver
7 leds: Add NVIDIA LSTP LED driver
8 i2c: Add NVIDIA LSTP I2C driver
9 spi: Add NVIDIA LSTP SPI driver
10 tty: Add NVIDIA LSTP UART driver
11 ipmi: Add NVIDIA LSTP IPMI BMC driver
Patch 4 $refs the schemas from patches 1-3, so it comes last among the
bindings; it also defines the serial@N and ipmi@N channel nodes inline.
The MAINTAINERS entry starts in patch 1 and gains each later patch's
files. Patch 5 carries:
- the core in drivers/usb/misc/;
- include/linux/usb/lstp.h for the function drivers;
- the sysfs ABI file;
- Documentation/driver-api/usb/lstp.rst;
- Documentation/firmware-guide/acpi/lstp.rst.
Patches 6 and 8-11 each depend only on patch 5; patch 7 (LEDs) also
needs patch 6, whose GPIO driver hands the LED pins to it.
Why the auxiliary bus, and why not MFD
--------------------------------------
The core binds the USB interface and owns everything shared:
- URBs and packet framing;
- request/response matching and the IRQ-style event path;
- channel 0 discovery.
Each channel the device reports is then created as an auxiliary device,
lstp.<type>.<id>, and the function drivers in their subsystems bind
those. They reach the device only through functions the core exports in
the "LSTP" symbol namespace, such as lstp_request() and lstp_send().
The closest model in tree is LJCA (drivers/usb/misc/usb-ljca.c), and
the series follows it. LJCA is a USB adapter that muxes GPIO, I2C and
SPI over its bulk endpoints. At probe it asks the device which
functions it has, creates one auxiliary device per reported function,
and exports a transfer and an event interface to the function drivers.
LSTP does the same with more channel types, with channel IDs assigned
by the device firmware, and with device tree as well as ACPI.
A channel has no registers, no MMIO and no interrupt lines of its own;
it is a logical function of one protocol stream, and its driver needs
nothing but the core's API. That is the use the auxiliary bus
documentation describes (Documentation/driver-api/auxiliary_bus.rst):
no dependency on a physical bus, device, register accesses or regmap.
It names MFD as the wrong fit for that case, because MFD relies on the
function devices being physical devices: an MFD cell is a platform
device, and its resources are ranges of the parent's memory and
interrupts (struct mfd_cell). Firmware matching does not decide between
the two, since MFD cells can also match DT nodes and ACPI _ADR children.
The same documentation says auxiliary devices "are not physical devices
that are controlled by DT/ACPI". An LSTP channel is not enumerated from
firmware either: it exists because the device reported it. Firmware
only describes it and what is attached to it. Before
auxiliary_device_add(), the core resolves each channel's node and sets
it with device_set_node():
- device tree: a child of the interface node the USB core attached;
- ACPI: a Device() under the interface's Device(), which is a child of
the port companion (Documentation/firmware-guide/acpi/lstp.rst);
- neither: the channel has no firmware node and works as reported.
A channel matches on its address (reg, or _ADR on ACPI) plus
compatible. The I2C, SPI and GPIO cores then enumerate children and
resolve references as for any other controller.
The UART: a plain tty_driver (patch 10)
---------------------------------------
The firmware owns the UART line: baud rate, framing, parity and flow
control are provisioned on the device and only read at probe. The host
cannot read or set modem control lines; it can send a fixed-length
break, and a carrier loss reported by the device hangs the tty up unless
CLOCAL is set. Most of serial_core's uart_ops would be stubs, and
usb-serial does not fit because it binds the whole USB interface, where
the UART is one function among several. So patch 10 registers a plain
tty_driver (TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV) with one
tty_port per channel, the structure cdc-acm uses for a UART behind USB.
set_termios keeps the hardware fields at the firmware's values, so stty
reports the real line settings. The nodes are /dev/ttyLSTP<n>, parented
on the channel's auxiliary device. Jiri, Ilpo: if you would rather see
this on serial_core despite the empty uart_ops, please say so on
patch 10.
The IPMI channel: BMC side, with a message number (patch 11)
------------------------------------------------------------
On a BMC, the IPMI channel carries the host's IPMI requests to BMC
userspace and the responses back, like ssif_bmc does for SSIF; it is
not an SSIF transport and has no host-side (ipmi_si) part. Each channel
is a character device, /dev/ipmi-lstp<n>.
By default, each message on that node carries a one-byte message number
between the length and the payload, and a response must echo the number
of the request it answers. The number exists only on the node, not in
the LSTP protocol, and no other BMC-side IPMI driver has it. It names
the request a response is for: when the host has sent a newer request,
a late response to the older one is dropped, even if userspace has
already read the newer request. Without the number, a response answers
the request read last. With CONFIG_USB_LSTP_IPMI_MSG_NUM=n the node uses
ssif_bmc's struct ipmi_ssif_msg layout instead, so userspace written for
ssif_bmc works unchanged. Corey: is the message-number layout
acceptable as an LSTP-specific extension, or would you prefer it as an
option of the generic BMC message format (and ssif_bmc)?
Responses carry no tag
----------------------
The protocol has no request tag: the first response after a request is
its answer. Two places in the series depend on that:
- The core (patch 5) drops a response that arrives when no request is
waiting, or after the waiting request already has its answer, and
counts it in the channel's rx_late statistic. A surplus response (a
reply to a request that timed out, one left from before a rebind, or
a device answering twice) therefore cannot overwrite a response the
waiter is still reading.
- The GPIO driver (patch 6) reads every pin's interrupt configuration
at probe. A late reply can answer the next request and shift every
later pin's state by one, so a single read is not retried on its
own: after a timeout, all pins are read again, in alternating order,
until two consecutive passes without a timeout agree. That is at most
five passes, ending within 10 s of the first timeout, or the probe
fails as it would without the re-read. The bound is there because
the probe runs in the context that added the device, often the hub's
worker.
The DT nodes for IPMI and UART channels
---------------------------------------
The USB binding (patch 4) defines serial@N and ipmi@N channel nodes with
only compatible, reg and status. They exist for status = "disabled",
which stops a channel the firmware reports from being created on a
board where it must not be exposed. Without a node, a channel is still
created. The binding shape, including one vendor property
(nvidia,retries on the I2C channel), is discussed in a separate mail to
the DT maintainers sent with this series:
https://lore.kernel.org/r/20261012-lstp-dt-rfc-v1-aambrozewicz@nvidia.com
What is not in this series
--------------------------
- The MMIO channel stays out of tree: raw register access, with no
binding and no kernel consumer.
- No auto-created spidev. The out-of-tree driver has a module parameter
that creates a spidev on every chip select of an undescribed SPI
channel; it is not part of this series. With CONFIG_SPI_DYNAMIC,
userspace does that through the controller's new_device attribute:
echo "spidev <cs>" > /sys/class/spi_master/spi<B>/new_device
(Documentation/ABI/testing/sysfs-class-spi-master)
- No separate IPMI POST-code node and no debug fault injection; both
stay in the out-of-tree package.
Testing
-------
The drivers were tested on arm64 and x86_64 with four kinds of device:
- real endpoints, the SMA and other vendors' implementations, driven by
the hardware test suite of the out-of-tree package on Raspberry Pi
hosts;
- a real SMA passed through over USB to QEMU guests running
v7.3-rc5 with this series;
- an emulated endpoint, a raw-gadget program on dummy_hcd, playing
scripted protocol scenarios;
- the same emulator mutating its replies at random.
The test plan covers:
- every channel type end to end: SPI flash identification, write and
read-back verification and throughput through spidev; I2C transfers;
GPIO values, interrupts, line names and hogs; LED default states,
brightness and hardware patterns; UART and IPMI traffic; concurrent
traffic on all channels;
- firmware descriptions: ACPI SSDTs and device tree overlays in many
variants, including incomplete, mismatched and outdated ones;
- lifetime: channel driver, USB interface and module unbind, rebind and
removal, USB disconnect and reset, with nodes open and consumers
attached, under load, and with nodes opened as soon as they appear;
- protocol faults: late, duplicate, missing and malformed replies, and
a disconnect in the middle of a transfer;
- allocation failures injected across the interface bind and unbind,
the function driver probes, open, read and module load.
The kernels used KASAN with kmemleak, KCSAN and KMSAN; lockdep with
PROVE_RCU, DEBUG_OBJECTS, DEBUG_LIST, DEBUG_KOBJECT_RELEASE,
DEBUG_ATOMIC_SLEEP and UBSAN; and failslab, fail_page_alloc and
fail-nth for fault injection. None of them reported a problem in these
drivers. The only reports involving them came from gpiolib when a GPIO
chip is removed with lines still requested; the fixes are posted
separately:
https://lore.kernel.org/r/20261012-gpiolib-chip-removal-v1-0-aambrozewicz@nvidia.com
Per patch, on the posted base: each patch builds (allmodconfig, W=1,
LSTP objects) on x86_64 with gcc and clang and on arm64 and arm with
gcc; sparse; kernel-doc; checkpatch --strict (clean apart from two
MACRO_ARG_REUSE checks in patch 5); dt_binding_check on each binding;
make htmldocs adds no warnings.
The out-of-tree package these drivers come from is at
https://github.com/NVIDIA/lstp_module; the version these patches match
is the public-next-v1 tag:
https://github.com/NVIDIA/lstp_module/tree/public-next-v1
It stays as the backport for kernels without this series.
Base: usb-next at bbf6b99a57b2 ("usb: typec: ucsi_ccg: Add retry
mechanism in ccg_read").
Adrian Ambrozewicz (11):
dt-bindings: gpio: Add NVIDIA LSTP GPIO
dt-bindings: i2c: Add NVIDIA LSTP I2C
dt-bindings: spi: Add NVIDIA LSTP SPI
dt-bindings: usb: Add NVIDIA LSTP USB adapter
usb: misc: Add NVIDIA LSTP USB adapter driver
gpio: Add NVIDIA LSTP GPIO driver
leds: Add NVIDIA LSTP LED driver
i2c: Add NVIDIA LSTP I2C driver
spi: Add NVIDIA LSTP SPI driver
tty: Add NVIDIA LSTP UART driver
ipmi: Add NVIDIA LSTP IPMI BMC driver
.../ABI/testing/sysfs-bus-usb-devices-lstp | 331 ++
.../bindings/gpio/nvidia,lstp-gpio.yaml | 276 ++
.../bindings/i2c/nvidia,lstp-i2c.yaml | 71 +
.../bindings/spi/nvidia,lstp-spi.yaml | 68 +
.../devicetree/bindings/usb/nvidia,lstp.yaml | 280 ++
Documentation/driver-api/usb/index.rst | 1 +
Documentation/driver-api/usb/lstp.rst | 276 ++
Documentation/firmware-guide/acpi/index.rst | 1 +
Documentation/firmware-guide/acpi/lstp.rst | 419 +++
MAINTAINERS | 21 +
drivers/char/ipmi/Kconfig | 32 +
drivers/char/ipmi/Makefile | 1 +
drivers/char/ipmi/ipmi-lstp.c | 464 +++
drivers/gpio/Kconfig | 14 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-lstp.c | 1194 +++++++
drivers/i2c/busses/Kconfig | 13 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-lstp.c | 573 ++++
drivers/leds/Kconfig | 14 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-lstp.c | 404 +++
drivers/spi/Kconfig | 13 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-lstp.c | 316 ++
drivers/tty/Kconfig | 13 +
drivers/tty/Makefile | 1 +
drivers/tty/lstp_tty.c | 829 +++++
drivers/usb/misc/Kconfig | 15 +
drivers/usb/misc/Makefile | 1 +
drivers/usb/misc/lstp.c | 2983 +++++++++++++++++
include/linux/usb/lstp.h | 219 ++
32 files changed, 8847 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-usb-devices-lstp
create mode 100644 Documentation/devicetree/bindings/gpio/nvidia,lstp-gpio.yaml
create mode 100644 Documentation/devicetree/bindings/i2c/nvidia,lstp-i2c.yaml
create mode 100644 Documentation/devicetree/bindings/spi/nvidia,lstp-spi.yaml
create mode 100644 Documentation/devicetree/bindings/usb/nvidia,lstp.yaml
create mode 100644 Documentation/driver-api/usb/lstp.rst
create mode 100644 Documentation/firmware-guide/acpi/lstp.rst
create mode 100644 drivers/char/ipmi/ipmi-lstp.c
create mode 100644 drivers/gpio/gpio-lstp.c
create mode 100644 drivers/i2c/busses/i2c-lstp.c
create mode 100644 drivers/leds/leds-lstp.c
create mode 100644 drivers/spi/spi-lstp.c
create mode 100644 drivers/tty/lstp_tty.c
create mode 100644 drivers/usb/misc/lstp.c
create mode 100644 include/linux/usb/lstp.h
base-commit: bbf6b99a57b2479bf51c3c8ad0946f4b8c33ed69
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 02/11] dt-bindings: i2c: Add NVIDIA LSTP I2C
2026-10-12 17:35 [PATCH 00/11] Add NVIDIA LSTP USB adapter drivers Adrian Ambrozewicz
@ 2026-10-12 17:36 ` Adrian Ambrozewicz
2026-10-13 6:11 ` Krzysztof Kozlowski
2026-10-12 17:36 ` [PATCH 08/11] i2c: Add NVIDIA LSTP I2C driver Adrian Ambrozewicz
` (2 subsequent siblings)
3 siblings, 1 reply; 6+ messages in thread
From: Adrian Ambrozewicz @ 2026-10-12 17:36 UTC (permalink / raw)
To: Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Donald Shannon, linux-usb, linux-i2c, devicetree, linux-kernel,
Greg Kroah-Hartman
Add the binding for an I2C controller channel of an NVIDIA LSTP USB
adapter. Peripherals on the bus are child nodes, as for any I2C
controller. The device sets the bus speed and the protocol cannot
change it, so clock-frequency is not supported.
nvidia,retries sets how many times the host re-issues a transfer that
failed on arbitration loss or a busy bus (default 3). The generic I2C
controller schema has no property for this. A device that reports its
own arbitration retries handles both cases itself, and the property is
ignored for it.
Signed-off-by: Adrian Ambrozewicz <aambrozewicz@nvidia.com>
---
.../bindings/i2c/nvidia,lstp-i2c.yaml | 71 +++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 72 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/nvidia,lstp-i2c.yaml
diff --git a/Documentation/devicetree/bindings/i2c/nvidia,lstp-i2c.yaml b/Documentation/devicetree/bindings/i2c/nvidia,lstp-i2c.yaml
new file mode 100644
index 000000000000..eacabed52cd1
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/nvidia,lstp-i2c.yaml
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/i2c/nvidia,lstp-i2c.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NVIDIA LSTP I2C Channel
+
+maintainers:
+ - Adrian Ambrozewicz <aambrozewicz@nvidia.com>
+ - Donald Shannon <donalds@nvidia.com>
+
+description: |
+ I2C controller channel on an NVIDIA LSTP USB device.
+
+ Peripherals on the bus are described as child nodes. An i2c alias
+ gives the bus a fixed number.
+
+ The bus speed is set by the device. The LSTP protocol has no way to
+ change it, so clock-frequency is not supported and is ignored.
+
+allOf:
+ - $ref: /schemas/i2c/i2c-controller.yaml#
+
+properties:
+ compatible:
+ const: nvidia,lstp-i2c
+
+ reg:
+ description: Channel ID reported by the device firmware.
+ maxItems: 1
+
+ nvidia,retries:
+ description: |
+ Number of times the host re-issues a transfer that fails on
+ arbitration loss or a busy bus before giving up.
+
+ Ignored when the channel config reports non-zero bus arbitration
+ retries. Such a device is expected to retry both arbitration loss and
+ a busy bus itself before reporting failure, so the host adds no
+ attempts on top.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ maximum: 255
+ default: 3
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ interface {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ i2c@2 {
+ compatible = "nvidia,lstp-i2c";
+ reg = <2>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ nvidia,retries = <5>;
+
+ temperature-sensor@48 {
+ compatible = "ti,tmp102";
+ reg = <0x48>;
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 284000589694..d8092f6d91b2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19571,6 +19571,7 @@ M: Donald Shannon <donalds@nvidia.com>
L: linux-usb@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/gpio/nvidia,lstp-gpio.yaml
+F: Documentation/devicetree/bindings/i2c/nvidia,lstp-i2c.yaml
NVIDIA VRS RTC DRIVER
M: Shubhi Garg <shgarg@nvidia.com>
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 08/11] i2c: Add NVIDIA LSTP I2C driver
2026-10-12 17:35 [PATCH 00/11] Add NVIDIA LSTP USB adapter drivers Adrian Ambrozewicz
2026-10-12 17:36 ` [PATCH 02/11] dt-bindings: i2c: Add NVIDIA LSTP I2C Adrian Ambrozewicz
@ 2026-10-12 17:36 ` Adrian Ambrozewicz
2026-10-13 11:37 ` [PATCH 00/11] Add NVIDIA LSTP USB adapter drivers Greg Kroah-Hartman
2026-10-13 11:39 ` Greg Kroah-Hartman
3 siblings, 0 replies; 6+ messages in thread
From: Adrian Ambrozewicz @ 2026-10-12 17:36 UTC (permalink / raw)
To: Andi Shyti
Cc: Donald Shannon, linux-kernel, linux-i2c, linux-usb,
Greg Kroah-Hartman
Add a driver for the I2C controller channels of NVIDIA LSTP USB
adapters. Each channel is registered as an I2C adapter; devices behind
it can be described by device tree or ACPI, or instantiated from
userspace. The device sets the bus speed. A transfer that fails on
arbitration loss or a busy bus is re-issued up to nvidia,retries times
(default 3), unless the device reports that it retries itself.
Signed-off-by: Adrian Ambrozewicz <aambrozewicz@nvidia.com>
---
MAINTAINERS | 1 +
drivers/i2c/busses/Kconfig | 13 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-lstp.c | 573 ++++++++++++++++++++++++++++++++++
4 files changed, 588 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-lstp.c
diff --git a/MAINTAINERS b/MAINTAINERS
index c8ed59cd4b11..082c4b3a0eb6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19578,6 +19578,7 @@ F: Documentation/devicetree/bindings/usb/nvidia,lstp.yaml
F: Documentation/driver-api/usb/lstp.rst
F: Documentation/firmware-guide/acpi/lstp.rst
F: drivers/gpio/gpio-lstp.c
+F: drivers/i2c/busses/i2c-lstp.c
F: drivers/leds/leds-lstp.c
F: drivers/usb/misc/lstp.c
F: include/linux/usb/lstp.h
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index d7b89508311f..9e3b21f18159 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1387,6 +1387,19 @@ config I2C_USBIO
This driver can also be built as a module. If so, the module
will be called i2c_usbio.
+config I2C_LSTP
+ tristate "NVIDIA LSTP I2C support"
+ depends on USB_LSTP
+ default USB_LSTP
+ help
+ Say Y or M here to support the I2C channels of NVIDIA LSTP USB
+ adapters. Each channel is registered as an I2C adapter, and the
+ devices behind it can be described by device tree, ACPI or
+ instantiated from userspace.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-lstp.
+
config I2C_CP2615
tristate "Silicon Labs CP2615 USB sound card and I2C adapter"
depends on USB
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 3755c54b3d82..5970b0bb1926 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -138,6 +138,7 @@ obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o
obj-$(CONFIG_I2C_LJCA) += i2c-ljca.o
obj-$(CONFIG_I2C_NCT6694) += i2c-nct6694.o
obj-$(CONFIG_I2C_USBIO) += i2c-usbio.o
+obj-$(CONFIG_I2C_LSTP) += i2c-lstp.o
obj-$(CONFIG_I2C_CP2615) += i2c-cp2615.o
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
obj-$(CONFIG_I2C_PCI1XXXX) += i2c-mchp-pci1xxxx.o
diff --git a/drivers/i2c/busses/i2c-lstp.c b/drivers/i2c/busses/i2c-lstp.c
new file mode 100644
index 000000000000..f1af36a709dd
--- /dev/null
+++ b/drivers/i2c/busses/i2c-lstp.c
@@ -0,0 +1,573 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * I2C driver for LSTP USB interface.
+ *
+ * Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ */
+
+#include <linux/bits.h>
+#include <linux/build_bug.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/property.h>
+#include <linux/string.h>
+#include <linux/uio.h>
+
+#include <linux/usb/lstp.h>
+
+enum lstp_i2c_cmd {
+ LSTP_I2C_CMD_BUS_RECOVERY = 0x00,
+ LSTP_I2C_CMD_READ = 0x01,
+ LSTP_I2C_CMD_WRITE = 0x02,
+ LSTP_I2C_CMD_WRITE_READ = 0x04,
+ LSTP_I2C_CMD_SMBUS_BLOCK_READ = 0x05
+} __packed;
+
+enum lstp_i2c_cmd_flags { LSTP_I2C_CMD_FLAG_NO_STOP = 0x40 };
+
+enum lstp_i2c_smbus_block_read_flags {
+ LSTP_I2C_SMBUS_BLOCK_READ_FLAG_PEC = BIT(0),
+} __packed;
+
+struct lstp_i2c_read_req {
+ u8 addr;
+ __le16 rd_len;
+} __packed;
+
+struct lstp_i2c_write_req {
+ u8 addr;
+ u8 wr_data[];
+} __packed;
+
+struct lstp_i2c_wr_rd_req {
+ u8 addr;
+ __le16 rd_len;
+ u8 wr_data[]; /* Write length inferred from length in header */
+} __packed;
+
+struct lstp_i2c_smbus_block_read_req {
+ u8 addr;
+ __le16 flags; /* lstp_i2c_smbus_block_read_flags */
+ u8 wr_data[]; /* Write length inferred from length in header */
+} __packed;
+
+/* Largest block-read write phase: SMBus command byte, block count, maximum-size block. */
+#define LSTP_I2C_SMBUS_BLOCK_READ_WR_MAX (2 + I2C_SMBUS_BLOCK_MAX)
+
+enum lstp_i2c_speed {
+ LSTP_I2C_SPEED_100KHZ = 0x00,
+ LSTP_I2C_SPEED_400KHZ = 0x01,
+ LSTP_I2C_SPEED_1000KHZ = 0x02,
+ LSTP_I2C_SPEED_3400KHZ = 0x03,
+};
+
+struct lstp_i2c_config {
+ u8 speed;
+ __le16 output_drive_strength; /* in mA */
+ __le16 slew_rate; /* in 100 ps (0.1 ns) increments */
+ __le16 input_debounce_time; /* in us */
+ __le16 glitch_filter_width; /* in ns */
+ u8 bus_low_timeout; /* in ms */
+ u8 bus_arbitration_retries;
+} __packed;
+
+/* Wire offsets: inserting or resizing a field silently shifts every field after it. */
+static_assert(sizeof(struct lstp_i2c_config) == 11,
+ "I2C channel config must match the LSTP specification");
+
+/* Shortest blob the channel accepts; every field after speed is optional. */
+#define LSTP_I2C_CONFIG_MIN_LEN offsetofend(struct lstp_i2c_config, speed)
+
+static bool lstp_i2c_can_retry(int lstp_status)
+{
+ return lstp_status == LSTP_BUSY || lstp_status == LSTP_ARB_LOST;
+}
+
+static bool lstp_i2c_suppress_error_logs(int ret, int lstp_status)
+{
+ return ret == -ENODEV || lstp_status == LSTP_NACK || lstp_i2c_can_retry(lstp_status);
+}
+
+/* i2c_bus_recovery_info.recover_bus: ask the device to run its own bus recovery. */
+static int lstp_i2c_bus_recovery(struct i2c_adapter *adap)
+{
+ struct lstp_channel *ch = adap->algo_data;
+ int lstp_status = LSTP_NO_RESPONSE_STATUS;
+
+ int ret = lstp_request(ch, LSTP_I2C_CMD_BUS_RECOVERY, NULL, 0, NULL, 0, NULL, &lstp_status);
+
+ if (ret && !lstp_i2c_suppress_error_logs(ret, lstp_status))
+ dev_err_ratelimited(ch->dev, "%s: ch_%d: Bus recovery failed (%pe)\n", __func__,
+ ch->ch_id, ERR_PTR(ret));
+ if (ret && lstp_i2c_can_retry(lstp_status))
+ return -EAGAIN;
+ return ret;
+}
+
+static int lstp_i2c_validate_msg(struct i2c_msg *msg)
+{
+ if (!msg->buf && msg->len > 0)
+ return -EINVAL;
+
+ if (msg->flags & I2C_M_TEN)
+ return -EAFNOSUPPORT; /* 10-bit address not supported */
+
+ if (msg->addr > 0x7F)
+ return -EINVAL; /* Only 7-bit address supported */
+
+ if ((msg->flags & I2C_M_RECV_LEN) && (!(msg->flags & I2C_M_RD) || msg->len < 1))
+ return -EINVAL;
+
+ return 0;
+}
+
+/* One READ transaction; @no_stop omits STOP so a further message can follow. */
+static int lstp_i2c_read(struct i2c_adapter *adap, struct i2c_msg *msg, bool no_stop)
+{
+ u8 cmd = LSTP_I2C_CMD_READ;
+ struct lstp_channel *ch = adap->algo_data;
+ struct lstp_i2c_read_req req = {
+ .addr = msg->addr,
+ .rd_len = cpu_to_le16(msg->len),
+ };
+ int lstp_status = LSTP_NO_RESPONSE_STATUS;
+
+ int ret = lstp_i2c_validate_msg(msg);
+
+ if (ret) {
+ dev_err_ratelimited(ch->dev, "%s: ch_%d: Invalid message to addr=0x%02x (%pe)\n",
+ __func__, ch->ch_id, msg->addr, ERR_PTR(ret));
+ return ret;
+ }
+
+ if (msg->len > ch->max_rx_payload) {
+ dev_err_ratelimited(ch->dev,
+ "%s: ch_%d: Read request message to addr=0x%02x too long (%u bytes, max %zu)\n",
+ __func__, ch->ch_id, msg->addr, msg->len, ch->max_rx_payload);
+ return -EINVAL;
+ }
+
+ dev_dbg(ch->dev, "%s: ch_%d: addr=0x%02x, len=%d\n", __func__, ch->ch_id, msg->addr,
+ msg->len);
+
+ if (no_stop)
+ cmd |= LSTP_I2C_CMD_FLAG_NO_STOP;
+
+ ret = lstp_request(ch, cmd, &req, sizeof(req), msg->buf, msg->len, NULL, &lstp_status);
+ if (ret && !lstp_i2c_suppress_error_logs(ret, lstp_status))
+ dev_err_ratelimited(ch->dev, "%s: ch_%d: READ to addr=0x%02x failed (%pe)\n",
+ __func__, ch->ch_id, msg->addr, ERR_PTR(ret));
+ if (ret && lstp_i2c_can_retry(lstp_status))
+ return -EAGAIN;
+ return ret;
+}
+
+/* One WRITE transaction; @no_stop omits STOP so a further message can follow. */
+static int lstp_i2c_write(struct i2c_adapter *adap, struct i2c_msg *msg, bool no_stop)
+{
+ u8 cmd = LSTP_I2C_CMD_WRITE;
+ struct lstp_channel *ch = adap->algo_data;
+ struct lstp_i2c_write_req req_head = { .addr = msg->addr };
+ struct kvec vecs[2] = {
+ { .iov_base = &req_head, .iov_len = sizeof(req_head) },
+ { .iov_base = msg->buf, .iov_len = msg->len },
+ };
+ int lstp_status = LSTP_NO_RESPONSE_STATUS;
+
+ int ret = lstp_i2c_validate_msg(msg);
+
+ if (ret) {
+ dev_err_ratelimited(ch->dev, "%s: ch_%d: Invalid message to addr=0x%02x (%pe)\n",
+ __func__, ch->ch_id, msg->addr, ERR_PTR(ret));
+ return ret;
+ }
+
+ if (sizeof(req_head) + msg->len > ch->max_tx_payload) {
+ dev_err_ratelimited(ch->dev,
+ "%s: ch_%d: Write message to addr=0x%02x too long (%u bytes, max %zu)\n",
+ __func__, ch->ch_id, msg->addr, msg->len,
+ ch->max_tx_payload - sizeof(req_head));
+ return -EINVAL;
+ }
+
+ if (msg->len > 0)
+ dev_dbg(ch->dev, "%s: ch_%d: addr=0x%02x, len=%d, data=0x%*ph\n", __func__,
+ ch->ch_id, msg->addr, msg->len, msg->len, msg->buf);
+ else
+ dev_dbg(ch->dev, "%s: ch_%d: addr=0x%02x, len=%d\n", __func__, ch->ch_id, msg->addr,
+ msg->len);
+
+ if (no_stop)
+ cmd |= LSTP_I2C_CMD_FLAG_NO_STOP;
+
+ ret = lstp_requestv(ch, cmd, vecs, ARRAY_SIZE(vecs), NULL, 0, NULL, &lstp_status);
+ if (ret && !lstp_i2c_suppress_error_logs(ret, lstp_status))
+ dev_err_ratelimited(ch->dev, "%s: ch_%d: WRITE to addr=0x%02x failed (%pe)\n",
+ __func__, ch->ch_id, msg->addr, ERR_PTR(ret));
+ if (ret && lstp_i2c_can_retry(lstp_status))
+ return -EAGAIN;
+ return ret;
+}
+
+/* Write then read at one address, separated by a repeated START, as a single request. */
+static int lstp_i2c_write_read(struct i2c_adapter *adap, struct i2c_msg *wr_msg,
+ struct i2c_msg *rd_msg, bool no_stop)
+{
+ u8 cmd = LSTP_I2C_CMD_WRITE_READ;
+ struct lstp_channel *ch = adap->algo_data;
+ struct lstp_i2c_wr_rd_req req_head = {
+ .addr = wr_msg->addr,
+ .rd_len = cpu_to_le16(rd_msg->len),
+ };
+ struct kvec vecs[2] = {
+ { .iov_base = &req_head, .iov_len = sizeof(req_head) },
+ { .iov_base = wr_msg->buf, .iov_len = wr_msg->len },
+ };
+ int lstp_status = LSTP_NO_RESPONSE_STATUS;
+
+ int ret = lstp_i2c_validate_msg(wr_msg);
+
+ if (ret) {
+ dev_err_ratelimited(ch->dev,
+ "%s: ch_%d: Invalid write message to addr=0x%02x (%pe)\n",
+ __func__, ch->ch_id, wr_msg->addr, ERR_PTR(ret));
+ return ret;
+ }
+
+ if (sizeof(req_head) + wr_msg->len > ch->max_tx_payload) {
+ dev_err_ratelimited(ch->dev,
+ "%s: ch_%d: Write message to addr=0x%02x too long (%u bytes, max %zu)\n",
+ __func__, ch->ch_id, wr_msg->addr, wr_msg->len,
+ ch->max_tx_payload - sizeof(req_head));
+ return -EINVAL;
+ }
+
+ ret = lstp_i2c_validate_msg(rd_msg);
+ if (ret) {
+ dev_err_ratelimited(ch->dev,
+ "%s: ch_%d: Invalid read message to addr=0x%02x (%pe)\n",
+ __func__, ch->ch_id, rd_msg->addr, ERR_PTR(ret));
+ return ret;
+ }
+
+ if (rd_msg->len > ch->max_rx_payload) {
+ dev_err_ratelimited(ch->dev,
+ "%s: ch_%d: Read message to addr=0x%02x too long (%u bytes, max %zu)\n",
+ __func__, ch->ch_id, rd_msg->addr, rd_msg->len,
+ ch->max_rx_payload);
+ return -EINVAL;
+ }
+
+ if (wr_msg->addr != rd_msg->addr) {
+ dev_err_ratelimited(ch->dev,
+ "%s: ch_%d: Write/read addr mismatch (wr=0x%02x, rd=0x%02x)\n",
+ __func__, ch->ch_id, wr_msg->addr, rd_msg->addr);
+ return -EINVAL;
+ }
+
+ if (no_stop)
+ cmd |= LSTP_I2C_CMD_FLAG_NO_STOP;
+
+ ret = lstp_requestv(ch, cmd, vecs, ARRAY_SIZE(vecs), rd_msg->buf, rd_msg->len, NULL,
+ &lstp_status);
+ if (ret && !lstp_i2c_suppress_error_logs(ret, lstp_status))
+ dev_err_ratelimited(ch->dev, "%s: ch_%d: WRITE_READ to addr=0x%02x failed (%pe)\n",
+ __func__, ch->ch_id, wr_msg->addr, ERR_PTR(ret));
+ if (ret && lstp_i2c_can_retry(lstp_status))
+ return -EAGAIN;
+ return ret;
+}
+
+/*
+ * SMBus block read with an optional write phase, which must address the same target.
+ * @rd_msg->len is updated to the bytes received.
+ */
+static int lstp_i2c_smbus_block_read(struct i2c_adapter *adap, struct i2c_msg *wr_msg,
+ struct i2c_msg *rd_msg, bool no_stop)
+{
+ u8 cmd = LSTP_I2C_CMD_SMBUS_BLOCK_READ;
+ struct lstp_channel *ch = adap->algo_data;
+ DEFINE_RAW_FLEX(struct lstp_i2c_smbus_block_read_req, req, wr_data,
+ LSTP_I2C_SMBUS_BLOCK_READ_WR_MAX);
+ u16 wr_len = wr_msg ? wr_msg->len : 0;
+ u8 *wr_buf = wr_msg ? wr_msg->buf : NULL;
+ bool pec = rd_msg->flags & I2C_CLIENT_PEC;
+ /* The target frames the block with a count byte, plus a trailing PEC byte if asked for. */
+ u8 framing = pec ? 2 : 1;
+ u16 rx_buf_len = I2C_SMBUS_BLOCK_MAX + framing;
+ size_t pkt_len = 0;
+ int lstp_status = LSTP_NO_RESPONSE_STATUS;
+
+ /* Probe enforces bulk_tx_size >= LSTP_USB_EP_MIN_SIZE; anchor the largest write phase. */
+ BUILD_BUG_ON(sizeof(struct lstp_header) +
+ struct_size(req, wr_data, LSTP_I2C_SMBUS_BLOCK_READ_WR_MAX) >
+ LSTP_USB_EP_MIN_SIZE);
+
+ int ret = lstp_i2c_validate_msg(rd_msg);
+
+ if (ret) {
+ dev_err_ratelimited(ch->dev,
+ "%s: ch_%d: Invalid read message to addr=0x%02x (%pe)\n",
+ __func__, ch->ch_id, rd_msg->addr, ERR_PTR(ret));
+ return ret;
+ }
+
+ /*
+ * An I2C_M_RECV_LEN read reserves rd_msg->len bytes on top of I2C_SMBUS_BLOCK_MAX, so the
+ * framing has to fit in there for a maximum-size block to still land inside the buffer.
+ */
+ if (rd_msg->len < framing) {
+ dev_err_ratelimited(ch->dev,
+ "%s: ch_%d: Read from addr=0x%02x reserves %u bytes, needs %u for framing\n",
+ __func__, ch->ch_id, rd_msg->addr, rd_msg->len, framing);
+ return -EINVAL;
+ }
+
+ if (wr_msg) {
+ ret = lstp_i2c_validate_msg(wr_msg);
+ if (ret) {
+ dev_err_ratelimited(ch->dev,
+ "%s: ch_%d: Invalid write message to addr=0x%02x (%pe)\n",
+ __func__, ch->ch_id, wr_msg->addr, ERR_PTR(ret));
+ return ret;
+ }
+
+ if (wr_msg->addr != rd_msg->addr) {
+ dev_err_ratelimited(ch->dev,
+ "%s: ch_%d: Write/read addr mismatch (wr=0x%02x, rd=0x%02x)\n",
+ __func__, ch->ch_id, wr_msg->addr, rd_msg->addr);
+ return -EINVAL;
+ }
+ }
+
+ if (wr_len > LSTP_I2C_SMBUS_BLOCK_READ_WR_MAX) {
+ dev_err_ratelimited(ch->dev,
+ "%s: ch_%d: SMBus write phase to addr=0x%02x too long (%u bytes, max %u)\n",
+ __func__, ch->ch_id, rd_msg->addr, wr_len,
+ LSTP_I2C_SMBUS_BLOCK_READ_WR_MAX);
+ return -EINVAL;
+ }
+
+ if (rx_buf_len > ch->max_rx_payload) {
+ dev_err_ratelimited(ch->dev,
+ "%s: ch_%d: Read message to addr=0x%02x too long (%u bytes, max %zu)\n",
+ __func__, ch->ch_id, rd_msg->addr, rx_buf_len,
+ ch->max_rx_payload);
+ return -EINVAL;
+ }
+
+ *req = (struct lstp_i2c_smbus_block_read_req){
+ .addr = rd_msg->addr,
+ .flags = cpu_to_le16(pec ? LSTP_I2C_SMBUS_BLOCK_READ_FLAG_PEC : 0),
+ };
+ if (wr_len)
+ memcpy(req->wr_data, wr_buf, wr_len);
+
+ if (no_stop)
+ cmd |= LSTP_I2C_CMD_FLAG_NO_STOP;
+
+ ret = lstp_request(ch, cmd, req, struct_size(req, wr_data, wr_len), rd_msg->buf, rx_buf_len,
+ &pkt_len, &lstp_status);
+ if (ret) {
+ if (!lstp_i2c_suppress_error_logs(ret, lstp_status))
+ dev_err_ratelimited(ch->dev,
+ "%s: ch_%d: SMBUS_BLOCK_READ to addr=0x%02x failed (%pe)\n",
+ __func__, ch->ch_id, rd_msg->addr, ERR_PTR(ret));
+ if (lstp_i2c_can_retry(lstp_status))
+ return -EAGAIN;
+ return ret;
+ }
+
+ if (pkt_len == 0) {
+ dev_err_ratelimited(ch->dev, "%s: ch_%d: Empty response packet\n", __func__,
+ ch->ch_id);
+ return -EIO;
+ }
+
+ u8 block_len = rd_msg->buf[0];
+
+ if (block_len > I2C_SMBUS_BLOCK_MAX) {
+ dev_err_ratelimited(ch->dev, "%s: ch_%d: Invalid block length %u\n", __func__,
+ ch->ch_id, block_len);
+ return -EPROTO;
+ }
+
+ u16 rx_len = block_len + framing;
+
+ if (pkt_len < rx_len) {
+ dev_err_ratelimited(ch->dev, "%s: ch_%d: Invalid response length (%zu)\n", __func__,
+ ch->ch_id, pkt_len);
+ return -EIO;
+ }
+
+ rd_msg->len = rx_len;
+ return 0;
+}
+
+static bool lstp_i2c_is_combined_pair(const struct i2c_msg *wr_msg, const struct i2c_msg *rd_msg)
+{
+ return !(wr_msg->flags & I2C_M_RD) && (rd_msg->flags & I2C_M_RD) &&
+ wr_msg->addr == rd_msg->addr;
+}
+
+/*
+ * i2c_algorithm.xfer: one device request per message, except a write/read pair to the same
+ * target, which folds into one combined request. Every request but the last omits STOP, so
+ * the whole message array stays a single bus transaction.
+ */
+static int lstp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct lstp_channel *ch = adap->algo_data;
+
+ if (!msgs || num <= 0)
+ return -EINVAL;
+
+ dev_dbg(ch->dev, "%s: ch_%d: I2C transfer with %d message(s)\n", __func__, ch->ch_id, num);
+
+ for (int i = 0; i < num; i++) {
+ struct i2c_msg *wr_msg = NULL;
+ struct i2c_msg *msg = &msgs[i];
+
+ if (i + 1 < num && lstp_i2c_is_combined_pair(msg, &msgs[i + 1])) {
+ wr_msg = msg;
+ msg = &msgs[++i];
+ }
+
+ bool recv_len = msg->flags & I2C_M_RECV_LEN;
+ bool read = msg->flags & I2C_M_RD;
+ bool no_stop = i != num - 1;
+ int ret;
+
+ if (!read)
+ ret = lstp_i2c_write(adap, msg, no_stop);
+ else if (read && recv_len)
+ ret = lstp_i2c_smbus_block_read(adap, wr_msg, msg, no_stop);
+ else if (read && !recv_len && wr_msg)
+ ret = lstp_i2c_write_read(adap, wr_msg, msg, no_stop);
+ else
+ ret = lstp_i2c_read(adap, msg, no_stop);
+
+ if (ret)
+ return ret;
+ }
+
+ return num;
+}
+
+static u32 lstp_i2c_functionality(struct i2c_adapter *adap)
+{
+ /* TODO: This needs to be discoverable, also need separate commands for 10-bit address */
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL_ALL;
+}
+
+static struct i2c_bus_recovery_info lstp_i2c_recovery_info = {
+ .recover_bus = lstp_i2c_bus_recovery,
+};
+
+static const struct i2c_algorithm lstp_i2c_algorithm = {
+ .xfer = lstp_i2c_xfer,
+ .functionality = lstp_i2c_functionality,
+};
+
+#define LSTP_I2C_DEFAULT_RETRIES 3
+
+/*
+ * The I2C core re-issues only an -EAGAIN transfer and bounds the whole retry loop, not each
+ * attempt, by adap->timeout, so the timeout spans every attempt. A device reporting
+ * bus_arbitration_retries retries LSTP_BUSY and LSTP_ARB_LOST itself, so the host does not
+ * and ignores "nvidia,retries". @adap's firmware node must already be set.
+ */
+static void lstp_i2c_configure_retry_policy(struct lstp_channel *ch, struct i2c_adapter *adap,
+ const struct lstp_i2c_config *cfg)
+{
+ u32 retries = LSTP_I2C_DEFAULT_RETRIES;
+ u32 configured_retries;
+ bool have_configured;
+
+ have_configured =
+ !device_property_read_u32(&adap->dev, "nvidia,retries", &configured_retries);
+
+ if (cfg->bus_arbitration_retries) {
+ if (have_configured && configured_retries)
+ dev_warn(ch->dev,
+ "%s: ch_%d: Ignoring retries=%u; device retries the bus %u time(s) itself\n",
+ __func__, ch->ch_id, configured_retries,
+ cfg->bus_arbitration_retries);
+ retries = 0;
+ } else if (have_configured) {
+ if (configured_retries <= U8_MAX)
+ retries = configured_retries;
+ else
+ dev_warn(ch->dev, "%s: ch_%d: Ignoring retries value %u; maximum is %u\n",
+ __func__, ch->ch_id, configured_retries, U8_MAX);
+ }
+
+ adap->retries = retries;
+ adap->timeout = msecs_to_jiffies((retries + 1) * LSTP_USB_RESPONSE_TIMEOUT_MS);
+}
+
+/* The I2C core takes the bus number from an "i2c" alias on adap->dev's firmware node. */
+static int lstp_i2c_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
+{
+ struct lstp_channel *ch = lstp_channel_from_auxdev(auxdev);
+ const struct lstp_channel_init_info *info = dev_get_platdata(&auxdev->dev);
+
+ if (!info)
+ return -ENODEV;
+
+ if (info->config_len < LSTP_I2C_CONFIG_MIN_LEN) {
+ dev_err(ch->dev, "%s: ch_%d: I2C config too short: %zu < %zu\n", __func__,
+ ch->ch_id, info->config_len, LSTP_I2C_CONFIG_MIN_LEN);
+ return -EINVAL;
+ }
+
+ /* Full-size copy, so field reads need no length checks: unreported fields read as zero. */
+ struct lstp_i2c_config cfg;
+
+ memcpy_and_pad(&cfg, sizeof(cfg), info->config, info->config_len, 0);
+
+ struct i2c_adapter *adap = devm_kzalloc(ch->dev, sizeof(*adap), GFP_KERNEL);
+
+ if (!adap)
+ return -ENOMEM;
+
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_HWMON;
+ adap->algo = &lstp_i2c_algorithm;
+ adap->algo_data = ch;
+ adap->dev.parent = ch->dev;
+ adap->bus_recovery_info = &lstp_i2c_recovery_info;
+ device_set_node(&adap->dev, ch->fwnode);
+ lstp_i2c_configure_retry_policy(ch, adap, &cfg);
+ strscpy(adap->name, ch->display_name, sizeof(adap->name));
+ /* TODO: save i2c speed from config */
+
+ int ret = devm_i2c_add_adapter(ch->dev, adap);
+
+ if (ret) {
+ dev_err(ch->dev, "%s: ch_%d: Could not register I2C adapter (%pe)\n", __func__,
+ ch->ch_id, ERR_PTR(ret));
+ return ret;
+ }
+
+ dev_info(ch->dev, "%s: ch_%d: Started as %s\n", __func__, ch->ch_id, adap->name);
+ return 0;
+}
+
+static const struct auxiliary_device_id lstp_i2c_id_table[] = { { .name = "lstp.i2c" }, {} };
+MODULE_DEVICE_TABLE(auxiliary, lstp_i2c_id_table);
+
+static struct lstp_auxiliary_driver lstp_i2c_drv = {
+ .adrv = {
+ .name = "i2c",
+ .probe = lstp_i2c_probe,
+ .id_table = lstp_i2c_id_table,
+ },
+};
+module_lstp_auxiliary_driver(lstp_i2c_drv);
+
+MODULE_IMPORT_NS("LSTP");
+MODULE_DESCRIPTION("LSTP I2C driver");
+MODULE_LICENSE("GPL");
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 02/11] dt-bindings: i2c: Add NVIDIA LSTP I2C
2026-10-12 17:36 ` [PATCH 02/11] dt-bindings: i2c: Add NVIDIA LSTP I2C Adrian Ambrozewicz
@ 2026-10-13 6:11 ` Krzysztof Kozlowski
0 siblings, 0 replies; 6+ messages in thread
From: Krzysztof Kozlowski @ 2026-10-13 6:11 UTC (permalink / raw)
To: Adrian Ambrozewicz, Andi Shyti, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: Donald Shannon, linux-usb, linux-i2c, devicetree, linux-kernel,
Greg Kroah-Hartman
On 12/10/2026 19:36, Adrian Ambrozewicz wrote:
> Add the binding for an I2C controller channel of an NVIDIA LSTP USB
> adapter. Peripherals on the bus are child nodes, as for any I2C
> controller. The device sets the bus speed and the protocol cannot
> change it, so clock-frequency is not supported.
>
> nvidia,retries sets how many times the host re-issues a transfer that
> failed on arbitration loss or a busy bus (default 3). The generic I2C
> controller schema has no property for this. A device that reports its
> own arbitration retries handles both cases itself, and the property is
> ignored for it.
>
> Signed-off-by: Adrian Ambrozewicz <aambrozewicz@nvidia.com>
> ---
> .../bindings/i2c/nvidia,lstp-i2c.yaml | 71 +++++++++++++++++++
> MAINTAINERS | 1 +
> 2 files changed, 72 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/i2c/nvidia,lstp-i2c.yaml
>
> diff --git a/Documentation/devicetree/bindings/i2c/nvidia,lstp-i2c.yaml b/Documentation/devicetree/bindings/i2c/nvidia,lstp-i2c.yaml
> new file mode 100644
> index 000000000000..eacabed52cd1
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/i2c/nvidia,lstp-i2c.yaml
> @@ -0,0 +1,71 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
We do not use that tag. Drop it everywhere.
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/i2c/nvidia,lstp-i2c.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: NVIDIA LSTP I2C Channel
> +
> +maintainers:
> + - Adrian Ambrozewicz <aambrozewicz@nvidia.com>
> + - Donald Shannon <donalds@nvidia.com>
> +
> +description: |
> + I2C controller channel on an NVIDIA LSTP USB device.
Say something useful.
> +
> + Peripherals on the bus are described as child nodes. An i2c alias
> + gives the bus a fixed number.
What are you describing here?
> +
> + The bus speed is set by the device. The LSTP protocol has no way to
> + change it, so clock-frequency is not supported and is ignored.
> +
> +allOf:
> + - $ref: /schemas/i2c/i2c-controller.yaml#
> +
> +properties:
> + compatible:
> + const: nvidia,lstp-i2c
> +
> + reg:
> + description: Channel ID reported by the device firmware.
> + maxItems: 1
> +
> + nvidia,retries:
You described the desired Linux feature or behavior, not the actual
hardware. The bindings are about the latter, so instead you need to
rephrase the property and its description to match actual hardware
capabilities/features/configuration etc.
> + description: |
> + Number of times the host re-issues a transfer that fails on
> + arbitration loss or a busy bus before giving up.
> +
> + Ignored when the channel config reports non-zero bus arbitration
> + retries. Such a device is expected to retry both arbitration loss and
> + a busy bus itself before reporting failure, so the host adds no
> + attempts on top.
> + $ref: /schemas/types.yaml#/definitions/uint32
> + maximum: 255
> + default: 3
> +
> +required:
> + - compatible
> + - reg
Incomplete, where are any resources? If you do not have any, then this
should be folded into the parent node.
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + interface {
> + #address-cells = <1>;
> + #size-cells = <0>;
Why do you need that wrapping node and what is "interface"?
> +
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 00/11] Add NVIDIA LSTP USB adapter drivers
2026-10-12 17:35 [PATCH 00/11] Add NVIDIA LSTP USB adapter drivers Adrian Ambrozewicz
2026-10-12 17:36 ` [PATCH 02/11] dt-bindings: i2c: Add NVIDIA LSTP I2C Adrian Ambrozewicz
2026-10-12 17:36 ` [PATCH 08/11] i2c: Add NVIDIA LSTP I2C driver Adrian Ambrozewicz
@ 2026-10-13 11:37 ` Greg Kroah-Hartman
2026-10-13 11:39 ` Greg Kroah-Hartman
3 siblings, 0 replies; 6+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-13 11:37 UTC (permalink / raw)
To: Adrian Ambrozewicz
Cc: Donald Shannon, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Linus Walleij, Bartosz Golaszewski, Lee Jones, Pavel Machek,
Andi Shyti, Mark Brown, Jiri Slaby, Corey Minyard,
Rafael J. Wysocki, Len Brown, Jonathan Corbet, Ilpo Järvinen,
Sakari Ailus, Hans de Goede, Lixu Zhang, Danilo Krummrich,
Johan Hovold, Oliver Neukum, Quan Nguyen, Andrew Jeffery,
linux-usb, devicetree, linux-gpio, linux-leds, linux-i2c,
linux-spi, linux-serial, openipmi-developer, linux-acpi,
linux-doc, driver-core, linux-kernel
On Mon, Oct 12, 2026 at 08:35:58PM +0300, Adrian Ambrozewicz wrote:
> This series adds drivers for the NVIDIA LSTP USB adapter: a USB core
> driver and GPIO, LED, I2C, SPI, UART and BMC-side IPMI function drivers,
> with their device tree bindings, an ACPI description document and the
> sysfs ABI.
Why would a USB device have anything to do with device tree?
> What LSTP is
> ------------
>
> LSTP (Low-Speed Transport Protocol or Low-Speed Tunnelling Protocol) is
> a vendor-specific USB interface (class 0xff, subclass 0x3f) that
> multiplexes several low-speed "channels" over one pair of bulk
> endpoints. It is implemented by NVIDIA's SMA, an MCU (0955:cf11), and
> by other vendors' parts: two hub/bridge chips have it built in
> (0424:4940, 174c:2214). Boards use it to
> reach SPI flash, I2C sensors, GPIOs and LEDs, a UART and a host-to-BMC
> IPMI path, on both BMCs and host systems.
Cute, but that's a total abuse of the USB standard. Why wasn't this
implemented by the proper "each channel is a USB interface" model
instead? Have the firmware engineers there not read the USB spec?
> The channel types are SPI host, I2C host, GPIO (with interrupts and LED
> pins), UART, IPMI and MMIO. Channel 0 is the control channel: at probe
> the driver reads the channel table from it, which gives each channel's
> ID, type and configuration. The set of channels is decided by the
> firmware, so the same VID:PID carries different channels on different
> boards. No firmware description is needed for the channels to work.
Again cute, but that's not how USB devices are supposed to work, why
would we want to do this at all?
> Series layout
> -------------
>
> 1 dt-bindings: gpio: Add NVIDIA LSTP GPIO
> 2 dt-bindings: i2c: Add NVIDIA LSTP I2C
> 3 dt-bindings: spi: Add NVIDIA LSTP SPI
> 4 dt-bindings: usb: Add NVIDIA LSTP USB adapter
USB devices do not have DT bindings. It's a dynamic bus, with
self-describing devices, don't make the mistake of thinking this is
giong to work.
> 5 usb: misc: Add NVIDIA LSTP USB adapter driver
> 6 gpio: Add NVIDIA LSTP GPIO driver
> 7 leds: Add NVIDIA LSTP LED driver
> 8 i2c: Add NVIDIA LSTP I2C driver
> 9 spi: Add NVIDIA LSTP SPI driver
> 10 tty: Add NVIDIA LSTP UART driver
> 11 ipmi: Add NVIDIA LSTP IPMI BMC driver
So you have a custom "bus" for a single USB device/interface? Ick,
again, why? Please do this the "proper" way in your firmware and do
this one-device-per-usb-interface like the rest of the world does
please.
Doing it that way will not cost any more hardware pins at all, it's all
handled in firmware, and as you all have complete control over the
firmware, you can fix this up now to do things properly.
> Patch 4 $refs the schemas from patches 1-3, so it comes last among the
> bindings; it also defines the serial@N and ipmi@N channel nodes inline.
> The MAINTAINERS entry starts in patch 1 and gains each later patch's
> files. Patch 5 carries:
> - the core in drivers/usb/misc/;
> - include/linux/usb/lstp.h for the function drivers;
> - the sysfs ABI file;
> - Documentation/driver-api/usb/lstp.rst;
> - Documentation/firmware-guide/acpi/lstp.rst.
>
> Patches 6 and 8-11 each depend only on patch 5; patch 7 (LEDs) also
> needs patch 6, whose GPIO driver hands the LED pins to it.
>
> Why the auxiliary bus, and why not MFD
> --------------------------------------
Neither is ok for USB devices, that's not an argument either way.
Please go talk to some people with USB spec experience on how to make a
multi-function device correctly. This way sure does not look like it at
all.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 00/11] Add NVIDIA LSTP USB adapter drivers
2026-10-12 17:35 [PATCH 00/11] Add NVIDIA LSTP USB adapter drivers Adrian Ambrozewicz
` (2 preceding siblings ...)
2026-10-13 11:37 ` [PATCH 00/11] Add NVIDIA LSTP USB adapter drivers Greg Kroah-Hartman
@ 2026-10-13 11:39 ` Greg Kroah-Hartman
3 siblings, 0 replies; 6+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-13 11:39 UTC (permalink / raw)
To: Adrian Ambrozewicz
Cc: Donald Shannon, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Linus Walleij, Bartosz Golaszewski, Lee Jones, Pavel Machek,
Andi Shyti, Mark Brown, Jiri Slaby, Corey Minyard,
Rafael J. Wysocki, Len Brown, Jonathan Corbet, Ilpo Järvinen,
Sakari Ailus, Hans de Goede, Lixu Zhang, Danilo Krummrich,
Johan Hovold, Oliver Neukum, Quan Nguyen, Andrew Jeffery,
linux-usb, devicetree, linux-gpio, linux-leds, linux-i2c,
linux-spi, linux-serial, openipmi-developer, linux-acpi,
linux-doc, driver-core, linux-kernel
On Mon, Oct 12, 2026 at 08:35:58PM +0300, Adrian Ambrozewicz wrote:
> This series adds drivers for the NVIDIA LSTP USB adapter: a USB core
> driver and GPIO, LED, I2C, SPI, UART and BMC-side IPMI function drivers,
> with their device tree bindings, an ACPI description document and the
> sysfs ABI.
>
> What LSTP is
> ------------
>
> LSTP (Low-Speed Transport Protocol or Low-Speed Tunnelling Protocol) is
> a vendor-specific USB interface (class 0xff, subclass 0x3f) that
> multiplexes several low-speed "channels" over one pair of bulk
> endpoints. It is implemented by NVIDIA's SMA, an MCU (0955:cf11), and
> by other vendors' parts: two hub/bridge chips have it built in
> (0424:4940, 174c:2214). Boards use it to
> reach SPI flash, I2C sensors, GPIOs and LEDs, a UART and a host-to-BMC
> IPMI path, on both BMCs and host systems.
Also, if you really want to do this in a dynamic way, use greybus, as
that is EXPLICITLY what it was designed for, and is patent-free (i.e.
unencumbered, and we already support it in the kernel.) Please don't
reinvent the wheel/spec just because no one did any prior-art
research...
thanks,
greg k-h
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-13 11:38 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-12 17:35 [PATCH 00/11] Add NVIDIA LSTP USB adapter drivers Adrian Ambrozewicz
2026-10-12 17:36 ` [PATCH 02/11] dt-bindings: i2c: Add NVIDIA LSTP I2C Adrian Ambrozewicz
2026-10-13 6:11 ` Krzysztof Kozlowski
2026-10-12 17:36 ` [PATCH 08/11] i2c: Add NVIDIA LSTP I2C driver Adrian Ambrozewicz
2026-10-13 11:37 ` [PATCH 00/11] Add NVIDIA LSTP USB adapter drivers Greg Kroah-Hartman
2026-10-13 11:39 ` Greg Kroah-Hartman
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox