Re: Device tree representation of (hotplug) connectors: discussion at ELCE

Herve Codina
2025-09-09T09:41:51Z

Hi David,

On Tue, 9 Sep 2025 15:09:18 +1000
David Gibson <david@gibson.dropbear.id.au> wrote:

...

> > I think that a connector is something with a bunch of resources provided
> > by the board where the connector is soldered. Those resources are wired
> > to the connector and defined by the connector pinout.
> >
> > 3v3 ------- Pin 0
> > i2c_scl ------- Pin 1
> > i2c_sda ------- Pin 2
> > gpio A ------- Pin 3
> > gpio B ------- Pin 4
> > gnd ------- Pin 5
> >
> > IMHO, this need to be described and defined in the base board and an addon can
> > only reference resources wired and described by the connector node.
>
> Yes, that's exactly what I'm proposing too.
>
> > Now, questions are:
> > - 1) How to describe a connector?
> > - 2) How to reference resources provided at connector level from an add-on?
>
> Right.
>
> > Our current approach was:
> > ---- base board DT ----
> > connector0 {
> > gpio-map = <0 &gpio0 12>, /* gpio A wired to gpio 12 of gpio0 controller */
> > <1 &gpio2 10; /* gpio B wired to gpio 10 of gpio2 controller */
> > i2c-one {
> > compatible = "i2c-bus-extension";
> > i2c-parent = <i2c5>; /* i2c-one wired to i2c5 controller */
> > };
> >
> > i2c-two {
> > compatible = "i2c-bus-extension";
> > i2c-parent = <i2c6>; /* i2c-two wired to i2c6 controller */
> > };
> >
> > /*
> > * From the addon we need to reference:
> > * - The connector itself,
> > * - Maybe some pinctrl related to signals wired to the connector,
> > * - In some cases the i2c bus (HDMI, ddc-i2c-bus = <&i2c-two>;)
> > *
> > * This was solved introducing the controversial export-symbols node.
> > */
>
> I think the type of connector should also be named on both sides (with
> 'compatible' or something like it).

It makes sense.

>
> > };
> >
> > ---- addon board DT ----
> > {
> > some-node {
> > compatible = "foo,bar";
> > reset-gpios = <&connector 0>; /* gpio A used as a reset gpio */
> > ddc-i2c-bus = <&i2c-two>;
> > }
> >
> > i2c-one {
> > eeprom@10 {
> > compatible = "baz,eeprom"
> > reg = 10;
> > };
> > };
> > };
> >
> > The addon board DT can only be applied at a connector node.
>
> Right. This is not how overlays work now. By the nature of how
> they're built they apply global updates to the base tree. That means
> we need to spec a new way of describing addons that *is* restricted to
> a particular connector slot (or slots, as Geert points out). Since we
> have that opportunity, we should _not_ try to make it a minimal
> extension to existing overlay format, but define a new and better
> encoding, designed to meet the problems you're looking to address.

On the kernel side, overlays can be applied at a specific node.
The node is chosen when the overlay is apply.
https://elixir.bootlin.com/linux/v6.16/source/drivers/of/overlay.c#L970

This allows to apply an overlay to a specific node without any modification
of the overlay dtb (dtbo).

Ajush proposed an update to support this feature in fdtoverlay
https://lore.kernel.org/all/20250313-fdtoverlay-target-v1-0-dd5924e12bd3@beagleboard.org/

...
>
> > > > > 3) bus-reg / bus-ranges
> > > > >
> > > > > One thing that makes connector plugins a bit awkward is that they
> > > > > often need to add things to multiple buses on the host system (MMIO &
> > > > > i2c for a simple case). This means that once resolved the plugin
> > > > > isn't neatly a single subtree. That's one factor making removal
> >
> > It can be a single subtree if decoupling is present at connector node available
> > in the base device tree.
>
> Right - allowing that decoupling is exactly what I'm proposing bus-reg
> for. Note that the case of an addon that plugs into multiple
> connectors that Geert pointed out complicates this again.

Geert's use case needs to be clarified.

Suppose a base board with 2 connectors:
- connA
- connB

Case 1: Addons are independant
+--------+
connA <----> | AddonA |
+--------+
+--------+
connB <---------------->| AddonB |
+--------+

With addonA and B two addon board each connected at one connector without any
relationship between addon A and B

Case 2: Only one Addons using ressources from both connector

+------+
connA <-----> |Addon |
| |
connB <-----> | |
+------+

The addon is connected to both connector and uses ressources from connA and
connB in a dependent manner.

The Case 2 can be solved using a connector that described both connA and connB.
Having the split connA and connB is a mechanical point of view.

Also adding and Addon on only one part (connA for instance) should not be an issue
if the connector describe both parts.

but well, here again I can miss something.
Geert, can you provide details?

...

> > > >
> > > > There is an i2c-bus-extension [1] and spi-bus-extension proposal to do the
> > > > same. But, if we can figure out a common way for all buses, that would be
> > > > great.
> >
> > Exactly, this is the purpose of bus extensions.
>
> Right, but redefining it for each bus type seems silly.

Nexus node properties are re-defined for each kind of resources (interrupt,
gpio, pwm, ...).

Why not for bus extensions.

Also I am pretty sure that some bus extension will need to define some
properties specific to the bus related to the extension.

>
> > Also, I don't thing that the 'ranges' property should be used for that purpose.
> > The 'ranges' property is used to translate addresses from child addresses space
> > to parent addresses space.
>
> The rationale for bus-ranges is that the add-on board could re-expose
> one of the buses on the connector (say MMIO for simplicity) to several
> chips physically included on the addon board. We don't want those
> sub-chips to need different device nodes depending on whether they're
> on an addon board or wired directly onto a main board. bus-ranges
> would allow the root of the connector to create a subtree in which
> regular MMIO (or whatever) devices can be described, and then routed
> via the connector onto the bus on the main board.

bus extensions supports this feature without bus-regs nor bus-ranges.

A bus extension ended by a sub node in the connector node.
Applying the addon DT at the connector node allow the addon to had
subnode to the extension node.

>
> > For instance, in case of i2c, where is the address translation?
>
> I think i2c multiplexers can sometimes do that. But in any case,
> ranges can be used for 1:1 translation as well.

i2c multiplexers are physical devices performing some internal logic.
They are real bridges and route an upstream i2c bus to downstream
i2c busses.
They are handled by a specific driver.

>
> > The address of a child (device) is its I2C address. This address is
> > device in its datasheet. There is no reason to have this address depending
> > on the I2C bus this child is connected to.
>
> Maybe not for I2C, but other buses can certainly allow translation, so
> the flexibility is needed.
>
> > In your example, the bridge@XXXX is not related to any hardware components.
>
> I'm not sure what you mean by that.

We have just wires from a controller to a connector. No bridges
nor other devices between the controller and the connector.

The controller is described in the base device tree, the connector is
described in the base device tree and the physical links between the controller
and the connector are wires on PCB.

If a physical device is present between the controller and the connector
such as a i2c mux, this physical device is allready described in the base DT.

In that case, your "bridge" will be betweeh the i2c mux and the connector.

In the extension bus case, the extension bus is between the i2c mux and the
connector.

>
> > If is allows to physically perform I2C transaction from a MMIO device
> > connected to MMIO bus, this is a traduction I2C controller with a bunch of
> > registers to configure an perform I2C transactions.
>
> Yes, absolutely. The i2c in bus-reg there is not supposed to indicate
> that the bridge is used to access i2c devices. Rather it's saying
> that the bridge itself has configuration registers accessible via i2c.
>
> If a device does have its own i2c bus hanging off, then as you say
> there should not be a 'ranges' property. It would have its own i2c
> controller driver.
>
> bus-ranges could be used when a connector has pins on multiple MMIO
> buses, and an addon could have sub-devices on either one of them.
>
> Note that bus-ranges translated between i2c buses isn't theoretically
> impossible: that would represent a case with some sort of I2C "relay"
> device that forwards I2C commands from one side to the other altering
> the address on the way. I don't know if such devices exist in
> practice, but they're certainly theoretically possible (and could be
> used, e.g. for allowing 10-bit I2C devices to be accessed from an old
> 7-bit I2C bus).
>
> > In that case, this "bridge" looks like all I2C we already support in the
> > kernel.
> > Having the additional indirection with the "bridge" between the MMIO bus
> > and the i2c@... node seems not a hardware representation of the system.
> >
> > Did I miss something?
>
> I think so. That's representing the bridge itself being configured
> via I2C, not allowing I2C access to devices _on_ the bus.

If I understand correctly the addon board can configure the bridge available on
the base board.

How do you describe that when you have everything soldered on a base board
and described in a base DT?

I think that the description should be the same with when some devices
are soldered on an addon board connected to a connector.

The only difference should be the location of the nodes. Indeed, those
devices, on the addon board, behid the connector, should be described
as subnodes of the connector.

Extension bus is exactly for that purpose.

...

> > > > > 4) You don't necessarily need to build a "full" device tree
> > > > >
> > > > > Flattened device trees (as opposed to original IEEE1275 device trees)
> > > > > - by design - allow certain information to be omitted. The most
> > > > > common example is that for introspectable buses, like PCI, it's normal
> > > > > to have the DT only include a node for the host bridge, with devices
> > > > > under it being discovered by their own bus specific methods. That's
> > > > > discovery is handled by the bus/bridge driver.
> > > > >
> > > > > Connectors usually aren't introspectable, but it's still possible to
> > > > > use an approach like this where the connector driver's discovery
> > > > > method is "look at a different device tree". So, for example,
> > > > >
> > > > > Board device tree:
> > > > >
> > > > > / {
> > > > > compatible = "board-with-foo-connector";
> > > > > . . .
> > > > > mmio@... {
> > > > > foo-connector@... {
> > > > > compatible = "foo-connector";
> > > > > ranges = < ... >;
> > > > > }
> > > > > }
> > > > > }
> >
> > I would expect a description of resources wired to the connector
> > available at the foo-connector node.
>
> Possibly yes. Here I was assuming a case where the resources
> presented by a "foo-connector" are fixed and described in a
> foo-connector binding. But an approach which explicitly describes the
> exposed resources could certainly have advantages.
>
> > > > >
> > > > > Foo device tree:
> > > > >
> > > > > / {
> > > > > compatible = "foo-device";
> > > > > foo-port-id = < 0x1234 >;
> > > > > component@... {
> > > > > reg = < ... >;
> > > > > }
> > > > > }
> > > > >
> > > > > Obviously a "foo device tree" would have different conventions than a
> > > > > board device tree. It wouldn't have /cpus, /memory, /chosen - but it
> > > > > could have its own "magic" nodes that make sense for the properties of
> > > > > the specific connector type.
> >
> > I agree with the fact that /cpus, /memory, ... wouldn't be present at this
> > node.
> >
> > Can you provide an example of the "magic" node and what do you have in mind
> > to store this information in DTB?
>
> One obvious one is somewhere to store a mapping / renaming of
> resources for local use on the connector. Another might be for
> metadata, like an ID code, if this connector type supports it.
>
> > > > > Again, that would require work in the device core part of the OS. The
> > > > > bonus is that runtime addition and removal is now trivial. No hacking
> > > > > of the base device tree is needed, and so doesn't need to be reverted.
> > > > > The connector driver just adds/removes the reference to its own
> > > > > private tree.
> >
> > Here also, I don't see exactly what you have in mind. Can you provide some
> > details and example?
>
> What I'm suggesting is that the "main" DT managed by the kernel would
> never include devices from the addon board. Instead the connector
> driver would locally a store a different device tree representing just
> the things under the connector. It would present a bus to the kernel
> device core that would "discover" devices on the bus based on that
> local device tree.

We agree with that and this is what we have proposed with our connector
binding.
The main DT has to provide (describe) busses that can be used by the addon
(adding devices to those busses).

Our binding with bus extension fulfills this requirement.

>
> > > > > This would, of course, need some way to refer to board resources
> > > > > (interrupt controller, gpio controller) etc. I think that can be
> > > > > assembled using some of the previous ideas, though.
> > > >
> > > > I would need to wrap my head around this a bit, specially in context of
> > > > chaining connectors. It does seem like it will still require the points you
> > > > mentioned above to be present in one form or another, i.e. some way to
> > > > extend busses to different nodes/trees and connector (even a chained one)
> > > > local symbols/aliases.
> > >
> > > Yes, it would still require those mappings. I don't think chained
> > > connectors introduce a lot of extra complication. An intermediate
> > > connector would need to be able to "re-export" things it got from its
> > > parent connector to its child connector(s) - renaming them if
> > > necessary.
> > >
> > > I think those two elements would be enough to make something that's
> > > useful in at least a few cases. However, the pretty common case of a
> > > connector with pins from multiple different parent buses would need
> > > bus-reg or something similar. Or else the nasty multiplexed encoding
> > > described above.
> > >
> > > I say, "nasty" and in many cases it would be, but I think there may be
> > > some cases where that approach does make sense: e.g. a connector that
> > > has several logically separate address spaces but which always travel
> > > together and are typically handled by a common bridge device. PCI is
> > > a case of this, if you squint - a host bridge provides a config space
> > > bus, and an MMIO bus and a PIO bus. Also sometimes some sort of
> > > interrupt controller / interrupt routing, complicated by the fact that
> > > there are several different models for that across PCI and PCI-E.
> >
> > To move forward on the topic, some concrete example would help to
> > understand how to describe link, how the information is stored in DTB.
> >
> > This would help us in moving in the right direction.
> >
> >
> > If the issue with export-symbols is having it described as a DTS node, this
> > could be changed with a new DTS keyword.
> >
> > /export/ <symbol_name_to_resolve> <resolved_symbol_name>
> > - <symbol_name_tp_resolved>: Symbol name seen by a user
> > - <resolved_symbol_name>: Symbol used once resolved.
>
> I might have some quibbles with the details, but I'd be open to
> something like that.

Ok

>
> > <resolved_symbol_name> can be restricted to phandle values.
>
> Because we're operating at the dts level here, whether it's a phandle
> is not actually relevant. We can treat this as a dts label name (or
> optionally path) - how that's encoded in the dtb we get to choose. A
> phandle is the most obvious approach.
>
> > Mutiple /export/ can be available inside a node in order to give a list
> > of exported symbols.
>
> Sure.
>
> > For instance with the connector example I previously mentioned.
> > We can replace the export-symbols node by the following:
> >
> > ---- base board DT ----
> > conn0: connector0 {
> > gpio-map = <0 &gpio0 12>, /* gpio A wired to gpio 12 of gpio0 controller */
> > <1 &gpio2 10; /* gpio B wired to gpio 10 of gpio2 controller */
> >
> > i2c_one: i2c-one {
> > compatible = "i2c-bus-extension";
> > i2c-parent = <i2c5>; /* i2c wired to i2c5 controller */
>
> I think bus-reg or bus-ranges is a more natural way to do this than
> per-bus bindings, but that's not particularly relevant to the rest of
> the proposal.
>
> > };
> >
> > i2c_two: i2c-two {
> > compatible = "i2c-bus-extension";
> > i2c-parent = <i2c6>; /* i2c wired to i2c6 controller */
> > };
> >
> > /export/ connector &conn0
> > /export/ i2cA &i2c_one
> > /export/ i2cB &i2c_two
>
> Since dtc is explicitly aware of this, we could even allow relative
> paths here, say
> /export/ connector &.;

Indeed, good point.

>
>
> > };
> >
> > A reference to connector (&connector) from the addon will be resolve
> > to a reference to &conn0 (phandle of the connector0 node.
>
> To handle the addon with multiple connectors we might want an
> (optional) remapping / renaming on the addon side as well. Or maybe
> simpler, we could allow namespacing the references on the addon side.

I think you talk about the Geert use case.
Geert, an example should be welcome.

The plan was to apply the DT related to an addon at a connector node.
Maybe this will not fit well with Geert's use case but to know if it
fits or not and to find the best way to handle this use case, an
example is needed.

>
> >
> > ---- addon board DT, applied at connector0 node ----
> > {
> > some-node {
> > compatible = "foo,bar";
> > reset-gpios = <&connector 0>; /* gpioA used as a reset gpio */
> > ddc-i2c-bus = <&i2cB> /* Resolved thanks to /export/ */
> >
> > }
> >
> > i2c-one {
> > /*
> > * A device added on the i2c-one bus wire to the connector.
> > * /export/ not involved. i2c-one is a node (bus extension) available
> > * in the DT node where this addon board DT is applied.
> > *
> > */
> > eeprom@10 {
> > compatible = "baz,eeprom"
> > reg = 10;
> > };
> > };
> > };
> >
> >
> > Now, what is expected in term of DTB format?
> >
> > I think we need:
>
> > - Base DT: List of exported symbols per nodes (/export/)
>
> Yes. I'm not necessarily opposed to encoding this as a regular
> property (basically, 'exported-symbols'). It's more the fixup
> information that is horrible as regular properties. Connectors should
> also probably have a property marking them *as* a connector and giving
> the type.

I thought that exported-symbols (or export-symbols) node was a no go.

With our proposal, nothing change in current __symbols__, __fixup__ and
__local_fixup__ implementation.

We just add the export-symbols item in current resolution process.

Even if a new DTB format landed, we should support already existing format.

>
> If we didn't want a regular property we could encode this as an
> FDT_EXPORT tag. These would go after FDT_BEGIN_NODE, before FDT_PROP
> and each include a (local-name, phandle) tuple. local-name could be a
> string table reference.
>
> > - Addon DT: List of unresolved symbols and their location
>
> Not sure we need this as a singular list, see below.
>
> > - Addon DT: A way to avoid collision in phandle values
>
> I'd suggest maybe three new tags for this, these would appear
> immediately after the FDT_PROP tag to which they're relevant.
>
> FDT_REF_PHANDLE (offset, name)
>
> This would replace the 4 bytes at `offset` in the previous property
> with the phandle of the node referred to by `name` (which must match
> something in the connector's exported list).
>
> FDT_REF_PATH (offset, name)
>
> Similar but would insert the full path name of the referenced node in
> the property.
>
> FDT_REF_LOCAL (offset)
>
> This indicates that that offset in the property contains a phandle
> value pointing to something within the addon - when we adjust addon
> phandles to avoid collisions, it needs to be updated too.

Ok for FDT_REF_PHANDLE(), FDT_REF_PATH() and FDT_REF_LOCAL().

They replace __fixups__ and __local_fixups__ but we need also
something to replace __symbols__ and the "export-symbols"

Solution 1:

A new section is needed.
we have dt_struct and dt_strings. dt_symbols could be this new section.

In this section we have table(s) of exported symbols. In term of
information, I think we need:
- The symbol name
- The node exporting the symbol (path)
- The symbol source:
- node referenced by the symbol (path)
- phandle referenced by the symbol
- original name for symbol remapping

Solution 2:
At nodes exporting symbols we could had:
- FDT_EXPORT_PHANDLE(name, phandle)
with name, the exported symbol name and phandle, the phandle value.

- FDT_EXPORT_PATH(name, path)
with name, the exported symbol name and path, the path to exported
object

- FDT_EXPORT_REMAP(name, orig_name)
with name, the exported symbol name and orig_name, the original symbol
name remapped to the exported symbol name.

>
> The addon should also include information on the type of connector(s)
> it wants to plug into. That should be a stringlist, like
> 'compatible', to allow for backwards compatible future extensions.

This information (if needed) is used by the driver applying the connector.
I don't think if should be involved in the symbol resolution mechanism.

From the symbol resolution point of view, we have:
- A base DT,
- A specific node in the base DT where the addon should be added
- The addon DT.

With this 3 inputs, the symbol resolution should do its work.
It should not check any compatible strings values.

>
>
> Or... allowing for addons plugging into multiple connectors, possibly
> each of those `name`s needs to be extended to (connector, name).
> `connector` would be either a name or index saying which connector
> we're referring to (numbering/naming controlled by the addon) with
> name being the name of the symbol within that connector (naming
> controlled by the base board / connector type).
>

Best regards,
Hervé

--
Hervé Codina, Bootlin
Embedded Linux and Kernel engineering