[RFC] dt-bindings: NVIDIA LSTP USB adapter: shape of the channel nodes
From: Adrian Ambrozewicz
Date: Mon Oct 12 2026 - 13:47:09 EST
Hi Rob, Krzysztof, Conor,
Together with this mail I am posting the driver series for the NVIDIA
LSTP USB adapter ("[PATCH 00/11] Add NVIDIA LSTP USB adapter drivers",
https://lore.kernel.org/r/20261012-lstp-usb-adapter-v1-0-aambrozewicz@xxxxxxxxxx);
patches 1-4 are its bindings. Here I would like your view on the binding
shape, in particular on the child nodes, and on one vendor property of
the I2C channel, without making you dig through the series for the
reasons. The schema excerpts and an example are below; the full bindings
are in patches 1-4 and in the out-of-tree repository, under
devicetree/bindings/ at
https://github.com/NVIDIA/lstp_module/tree/public-next-v1
What the device is
------------------
LSTP is a vendor-specific USB interface that carries several "channels"
over one pair of bulk endpoints. The channel types are SPI host, I2C
host, GPIO (with interrupts and LED pins), UART and an IPMI message
channel. 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).
The device reports its channels itself: their number, each one's ID
(1-255) and its type. Firmware decides the set, so one VID:PID carries
different channel sets on different boards, and one interface can carry
several channels of the same type. Boards in production have up to eight
I2C channels and two IPMI channels on one interface.
The DT is therefore optional. It is only for an adapter hard-wired on a
board, and it adds only what the device cannot report: peripherals on
the SPI and I2C buses, GPIO line names, hogs, consumers by phandle, and
LEDs.
Proposed shape
--------------
The device and interface nodes follow usb-device.yaml:
- a device node with compatible "usbVID,PID";
- under it, an interface node with a two-cell reg = <interface config>;
- for a class 0 device with one configuration and one interface, a
combined node instead.
Each channel is a child of the interface (or combined) node:
- named after its type: spi@N, i2c@N, gpio@N, serial@N, ipmi@N;
- with reg = <channel ID>;
- with a compatible naming the channel type.
A channel matches its node on reg plus compatible; node names are not
used. A channel without a node is still usable. A channel whose node has
status = "disabled" is not used at all.
The spi, i2c and gpio channel schemas are separate files in their
subsystem directories, each $ref'ing the generic controller schema. The
serial and ipmi channel schemas are defined inline in the USB binding,
which $refs the other three.
Example (from the binding, with nvidia,retries added to the I2C channel
and status added to the UART channel):
usb {
#address-cells = <1>;
#size-cells = <0>;
hub@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
/* Three interfaces; the LSTP channels are on interface 2. */
device@3 {
compatible = "usb955,cf11";
reg = <3>;
#address-cells = <2>;
#size-cells = <0>;
interface@2 {
compatible = "usbif955,cf11.config1.2";
reg = <2 1>;
#address-cells = <1>;
#size-cells = <0>;
spi@1 {
compatible = "nvidia,lstp-spi";
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <18750000>;
};
};
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>;
};
};
gpio@3 {
compatible = "nvidia,lstp-gpio";
reg = <3>;
gpio-controller;
#gpio-cells = <2>;
gpio-line-names = "LED", "INT";
};
ipmi@4 {
compatible = "nvidia,lstp-ipmi";
reg = <4>;
};
serial@5 {
compatible = "nvidia,lstp-uart";
reg = <5>;
status = "disabled";
};
};
};
};
};
Schema excerpt (usb/nvidia,lstp.yaml):
allOf:
- $ref: /schemas/usb/usb-device.yaml#
- $ref: "#/$defs/channels"
- if:
properties:
"#address-cells":
const: 1
then:
patternProperties:
"^interface@": false
else:
patternProperties:
"^(spi|i2c|gpio|serial|ipmi)@": false
properties:
compatible:
enum:
- usb955,cf11
- usb424,4940
- usb174c,2214
patternProperties:
"^interface@[0-9a-f]{1,2}(,[0-9a-f]{1,2})?$":
type: object
$ref: "#/$defs/channels"
properties:
compatible:
pattern: "^usbif[0-9a-f]{1,4},[0-9a-f]{1,4}\\.config[0-9a-f]{1,2}\\.[0-9a-f]{1,2}$"
reg:
maxItems: 1
...
$defs:
channels:
patternProperties:
"^spi@[0-9a-f]+$":
$ref: /schemas/spi/nvidia,lstp-spi.yaml#
"^i2c@[0-9a-f]+$":
$ref: /schemas/i2c/nvidia,lstp-i2c.yaml#
"^gpio@[0-9a-f]+$":
$ref: /schemas/gpio/nvidia,lstp-gpio.yaml#
"^serial@[0-9a-f]+$":
type: object
properties:
compatible:
const: nvidia,lstp-uart
reg:
maxItems: 1
required: [compatible, reg]
additionalProperties: false
"^ipmi@[0-9a-f]+$":
type: object
properties:
compatible:
const: nvidia,lstp-ipmi
reg:
maxItems: 1
required: [compatible, reg]
additionalProperties: false
The gpio schema (gpio/nvidia,lstp-gpio.yaml) defines:
- gpio-controller, #gpio-cells = <2> and gpio-line-names (up to 255);
- interrupt-controller with #interrupt-cells = <2>;
- hog children;
- a "leds" child holding led@N nodes on leds/common.yaml, for the pins
the device reports as LEDs.
The i2c schema (i2c/nvidia,lstp-i2c.yaml) is the generic controller plus
one property:
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
The spi schema (spi/nvidia,lstp-spi.yaml) is the generic controller with
compatible and reg only.
Questions
---------
1. Per-channel child nodes with their own compatible.
The channel type is discoverable, so the compatible does not choose a
driver for an unknown device. Instead it checks that the node written
for channel N describes the kind of channel the firmware put at N. The
alternative is to imply the type from the node name, which
writing-bindings.rst says not to treat as ABI. Is a compatible per
channel type acceptable here, or would you rather have a different
property, or no type at all?
2. gpio@N in particular.
I know the CP2112 review moved its GPIO controller off a subnode and
onto the USB device node (silabs,cp2112.yaml). That works because
CP2112 has exactly one GPIO block. Here a GPIO channel is one of
several channels behind one interface, at an ID the firmware assigns,
and the protocol does not limit an interface to one GPIO channel.
Putting gpio-controller on the interface node would need:
- a three-cell specifier (channel, line, flags);
- gpio-line-names, hogs, interrupts and the leds child keyed per
channel.
We think gpio@N, like i2c@N and spi@N, is the cleaner way to describe
that. Do you agree, or do you prefer the interface node as the
provider?
3. ipmi@N and serial@N have no resources of their own.
They allow only compatible, reg and status. writing-bindings.rst says
not to create nodes just to instantiate drivers, and these do not:
each channel is used whether or not it has a node. Their purpose is
status = "disabled", which keeps the channel from being used at all.
A board uses it when a channel the firmware reports must not be
exposed to the host OS on that board. Is that a sufficient reason for
the nodes?
If not, would you prefer a property on the interface node listing the
channel IDs not to use, or dropping the two node types (and
disabling) altogether?
4. Device and interface nodes.
The device node lists the three VID:PIDs in one enum, since the
channel layer is the same on all three. The interface node allows the
optional "usbifVID,PID.configC.I" compatible from usb-device.yaml.
- Is one binding for three USB devices from three vendors acceptable,
given that the LSTP interface is what is described?
- usb-device.yaml's interface pattern
"^interface@[0-9a-f]{1,2}(,[0-9a-f]{1,2})$" makes the ",C" suffix
mandatory. Its description, and ours, say the suffix is omitted for
configuration 1, so our pattern has "(...)?". Which is intended?
5. File layout.
The series has gpio/, i2c/ and spi/ schemas, each with its own patch,
ahead of usb/nvidia,lstp.yaml, which $refs them and defines serial@N
and ipmi@N inline. Would you rather have everything in usb/?
6. nvidia,retries on the I2C channel.
The channel config tells the host whether the device itself retries
a transfer that loses arbitration or finds the bus busy. When it
does, the host adds no attempts. When it does not, the host re-issues
such a transfer, three times by default. nvidia,retries sets that
count per board, for example for a bus shared with another master.
It is a property rather than something the OS sets later because the
peripherals described under the channel are probed as soon as the bus
is registered, before anything in userspace could change the count.
The nearest precedents I found are device properties, not controller
properties: sbs,i2c-retry-count (power/supply/sbs,sbs-battery.yaml)
and adi,overcurrent-retries (hwmon/adi,ltc4283.yaml).
Existing boards use the property without the vendor prefix, as
"retries"; our out-of-tree driver keeps reading that name, and the
upstream binding and driver would know only nvidia,retries. Is a
vendor property for this acceptable, or would you rather keep it out
of the binding and leave the count to the host default?
Thanks,
Adrian