Skip to content

Ideas for the ongoing pinctrl discussion #35077

Unanswered
henrikbrixandersen asked this question in Ideas
Ideas for the ongoing pinctrl discussion #35077
May 10, 2021 · 7 answers · 13 replies

henrikbrixandersen
May 10, 2021
Collaborator

Here is my input to the ongoing discussions of how to structure the upcoming pinctrl API and devicetree nodes in Zephyr. These ideas build upon the many proposals and ideas by other members of the community. The following PRs have been used as inspiration:

The discussions have focused on two different areas of implementation; the pinctrl C/C++ API for configuring pin function and pin properties at runtime, and the devicetree representation of pre-defined pin functions and pin properties.

Devicetree representation

Multiple ideas for devicetree representation has surfaced during the ongoing API discussions. From what I have seen, the discussions have been centred around the following concepts:

  1. A (Linux-like) DTS representation, where a peripheral can have a number of pinctrl-n properties (states), each with a reference to a pinctrl group node (a child of the pinctrl devicetree node). This pinctrl group contains a number of child nodes, each representing a pin, its function, and its properties to be configured when the given pinctrl group is selected. A pinctrl-names property allow associating each pinctrl-n property with a name for easier reference.
  2. A DTS representation much like bullet 1, but without the "group" indirection. A peripheral can have a number of pinctrl-n properties, each with a list of references to pins, their functions, and their properties to be configured when the given pinctrl-n is selected.
  3. A DTS representation much like bullet 2, but with the addition of (optional) SoC specific DTS include files (.dtsi) with an auto-generated list of all possible pin functions and associated properties. This is what we have in Zephyr today for STM32 (where the DTS representation is used already) and NXP (where the DTS representation has been added to the device trees, but the drivers have yet to be converted to use it).

API

Given the differences between the pin controllers/pin multiplexers present in the various SoCs, I do not see any way (or even value) of a truly common API. One option that comes close is an API that allows passing in a SoC pinctrl/pinmux specific data structure (or array of said data structure along with a length) representing the specific information needed for that given SoC pinctrl/pinmux as a void pointer.

SoC pinctrl/pinmux DT_ macros can be added to extract the needed pinctrl-n devicetree node properties and encode them as the SoC pinctrl/pinmux specific data structure, effectively reducing these structures to opaque types for typical use-cases.

The data structures representing the pinctrl-n states internally supported by a given peripheral driver (e.g. normal state, power saving state, error recovery state, ...) can be stored within the struct config of the given driver. This has the benefit that disabling a driver (or an instance) will also prevent any of the unused pinctrl-0 data structures from taking up flash. Most drivers will likely only support one state (normal) or two states (normal and power save).

Should a user application have a need for pinctrl-n states not supported by the given peripheral driver (e.g. remapping of UART ports between a TTL UART pin header and a USB to UART IC), application specific pinctrl-n nodes can be added (e.g. in a devicetree overlay). The data structures representing the custom pinctrl-n states can be extracted from DTS using the same DT_ macros as used by the drivers - and the same pinctrl API can be used for selecting the custom states. Again, unused pinctrl-n properties (those, that are not referenced by code) will not take up any flash space.

For more advanced use-cases (e.g those outlined for CircuitPython in #11918), the above can also support run-time configuration of pinctrl/pinmux without any compile-time devicetree representation of the desired pinctrl-n state. For this use-case, the data structures cannot be considered opaque, but would rather be filled in at run-time by the application and passed to the same pinctrl API as used in the above examples.

Replies

7 suggested answers
·
13 replies

effectively reducing these structures to opaque types for typical use-cases.

This is basically what we have for the clock control API. I could see some value in this, since it would allow hardware-specific overlays to configure a subsystem or application for e.g. "on" / "powersave" without cluttering the source code with board ifdefs.

0 replies

For DTS, we have something like this today for NXP:

In board.dts:

&i2c1 {
        pinctrl-0 = <&I2C1_SCL_PTC2 &I2C1_SDA_PTC3>;
}

&I2C1_SCL_PTC2 {
        bias-pull-up;
};
&I2C1_SDA_PTC3 {
        bias-pull-up;
};

In SOC-pinctrl.dtsi (this data is generated from vendor XML files):

/*
 * Pin nodes are of the form:
 *
 *      <SIGNAL[0..n]>: <signal[0]> {
 *              nxp,kinetis-port-pins = < PIN PCR[MUX] >;
 *      };
 */

        I2C1_SCL_PTC2: i2c1_scl_ptc2 {
                nxp,kinetis-port-pins = < 2 3 >;
        };
        I2C1_SDA_PTC3: i2c1_sda_ptc3 {
                nxp,kinetis-port-pins = < 3 3 >;
        };

From #35039 for STM32:

&pinctrl {
	usart1_lp_tx_pa9: usart1_lp_tx_pa9 {
		pinmux = <STM32_PINMUX('A', 9, ANALOG)>;
	};
	usart1_lp_rx_pa10: usart1_lp_rx_pa10 {
		pinmux = <STM32_PINMUX('A', 10, ANALOG)>;
	};
};

&usart1 {
	pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>;
	pinctrl-1 = <&usart1_lp_tx_pa9 &usart1_lp_rx_pa10>;
};

From SoC dtsi we have:

                        usart1_tx_pa9: usart1_tx_pa9 {
                                pinmux = <STM32_PINMUX('A', 9, AF7)>;
                                bias-pull-up;
                        };
                        usart1_rx_pa10: usart1_rx_pa10 {
                                pinmux = <STM32_PINMUX('A', 10, AF7)>;
                        };
0 replies

henrikbrixandersen
May 25, 2021
Collaborator Author

  1. A (Linux-like) DTS representation, where a peripheral can have a number of pinctrl-n properties (states), each with a
    reference to a pinctrl group node (a child of the pinctrl devicetree node). This pinctrl group contains a number of child nodes,
    each representing a pin, its function, and its properties to be configured when the given pinctrl group is selected.
    A pinctrl-names property allow associating each pinctrl-n property with a name for easier reference.

I have been giving this some more thought. The pinctrl DTS grouping makes sense if the pinctrl API takes an index for which group to configure. If the API does not take an index (but instead takes a pointer to a semi-opaque structure as discussed above) I do not see any gain from adding the group indirection in DTS. I therefore think DTS representation option 3. (see above) makes most sense.

0 replies

Some notes/thoughts on the Devicetree representation used by Linux vs. Zephyr for the STM32 case:

On Linux we have:

  • boardx.dts
...
&usart2 {
	pinctrl-names = "default", "sleep", "idle";  /* state names */
	pinctrl-0 = <&usart2_pins_c>;                /* a phandle pointing to a node containing pins configuration for state 0 ("default") */
	pinctrl-1 = <&usart2_sleep_pins_c>;
	pinctrl-2 = <&usart2_idle_pins_c>;
	status = "disabled";
};
...
  • boardx-pinctrl.dtsi
...
	usart2_pins_c: usart2-2 {
                /* each "subgroup" defines a set of pin configurations sharing same properties (e.g. bias, drive, etc.) */ 
		pins1 {
			pinmux = <STM32_PINMUX('D', 5, AF7)>, /* USART2_TX */
				 <STM32_PINMUX('D', 4, AF7)>; /* USART2_RTS */
			bias-disable;
			drive-push-pull;
			slew-rate = <3>;
		};
		pins2 {
			pinmux = <STM32_PINMUX('D', 6, AF7)>, /* USART2_RX */
				 <STM32_PINMUX('D', 3, AF7)>; /* USART2_CTS_NSS */
			bias-disable;
		};
	};

...

The Linux approach could be improved if we had the following content auto-generated:

			pinmux = <STM32_PINMUX('D', 5, AF7)>, /* USART2_TX */
				 <STM32_PINMUX('D', 4, AF7)>; /* USART2_RTS */
...
			pinmux = <STM32_PINMUX('D', 6, AF7)>, /* USART2_RX */
				 <STM32_PINMUX('D', 3, AF7)>; /* USART2_CTS_NSS */

That is, if we had a stm32socmodel-pinctrl.h with:

...
#define USART2_TX_PD5        STM32_PINMUX('D', 5, AF7)
#define USART2_RTS_PD4       STM32_PINMUX('D', 4, AF7)
#define USART2_RX_PD6        STM32_PINMUX('D', 6, AF7)
#define USART2_CTS_NSS_PD3   STM32_PINMUX('D', 3, AF7)
...

NOTE: This can be easily done re-using parts from https://github.com/zephyrproject-rtos/hal_stm32/blob/main/scripts/genpinctrl/genpinctrl.py

This way, the boardx-pinctrl.dtsi file would become:

...
	usart2_pins_c: usart2-2 {
		pins1 {
			pinmux = <USART2_TX_PD5>, <USART2_RTS_PD4>;
			bias-disable;
			drive-push-pull;
			slew-rate = <3>;
		};
		pins2 {
			pinmux = <USART2_RX_PD6>, <USART2_CTS_NSS_PD3>;
			bias-disable;
		};
	};

...

I'm not sure if the "pins1" and "pins2" subgroups can be extracted properly with current DT tooling, @mbolivar-nordic ?

If we compare with the current approach in Zephyr:

  • boardx.dts:
&i2c1 {
	pinctrl-0 = <&i2c1_scl_pb8 &i2c1_sda_pb9>;
	status = "okay";
	clock-frequency = <I2C_BITRATE_FAST>;
};
  • stm32socmodel-pinctrl.dtsi (autogenerated file):
...
			i2c1_scl_pb8: i2c1_scl_pb8 {
				pinmux = <STM32_PINMUX('B', 8, AF4)>;
				bias-pull-up;
				drive-open-drain;
			};
...
			i2c1_sda_pb9: i2c1_sda_pb9 {
				pinmux = <STM32_PINMUX('B', 9, AF4)>;
				bias-pull-up;
				drive-open-drain;
			};

I think that the most important difference is that if we used the Linux approach the board takes the final decision on pin settings such as the pull-up/down, slew-rate, etc. This is something that I think makes sense, it's really the board/application that has the information to make this decision. As of today the STM32 auto-generator provides common defaults, but they are not necessarily the best ones. They can certainly be overridden, though.

A side note of a limitation I found when using current STM32 approach: #22748 (comment)

3 replies
@mbolivar-nordic

mbolivar-nordic Aug 10, 2021
Collaborator

I'm not sure if the "pins1" and "pins2" subgroups can be extracted properly with current DT tooling, @mbolivar-nordic ?

Based on the example you gave, it looks doable to me. No compatible is shown, so I'm guessing you would want to use a child binding of the pinctrl node. Something like

	pinctrl: ... {
		compatible = "st,socmodel-pinctrl";

		pins1 {
			pinmux = <USART2_TX_PD5>, <USART2_RTS_PD4>;
			bias-disable;
			drive-push-pull;
			slew-rate = <3>;
		};
		pins2 {
			pinmux = <USART2_RX_PD6>, <USART2_CTS_NSS_PD3>;
			bias-disable;
		};
	}

then the binding would be something like

compatible: st,socmodel-pinctrl
include: pincfg-node.yaml
child-binding:
    properties:
        pinmux:
            type: array
@gmarull

@mbolivar-nordic there's actually one more level:

	pinctrl: ... {
		compatible = "st,socmodel-pinctrl";
                usart2_pins_c: ... {
                    pins1 {
	                    pinmux = <USART2_TX_PD5>, <USART2_RTS_PD4>;
	                    bias-disable;
	                    drive-push-pull;
	                    slew-rate = <3>;
                    };
                    pins2 {
	                    pinmux = <USART2_RX_PD6>, <USART2_CTS_NSS_PD3>;
	                    bias-disable;
                    };
              };
	};

so it's like the child binding has groups inside.

@mbolivar-nordic

You could do this with the above binding if you give the usart2_pins_c node a binding with the above content. We might need some edtlib changes if you want to do it from the st,socmodel-pinctrl binding.

gmarull
Aug 18, 2021
Collaborator

Some more notes and thoughts on DT representation. Feel free to edit, comment, discuss pros/cons, etc.

1. DT node for each state, containing all pins (Linux approach, e.g. for ST)

board.dts: pinctrl-N properties reference a node that contains the whole configuration set for a given state. Nodes are defined in a board-pinctrl.dtsi file.

#include "board-pinctrl.dtsi"

...
&usart2 {
	pinctrl-names = "default", "sleep", "idle";
	pinctrl-0 = <&usart2_pins_c>;
	pinctrl-1 = <&usart2_sleep_pins_c>;
	pinctrl-2 = <&usart2_idle_pins_c>;
};
...

board-pinctrl.dtsi: contains all configurations for a given board.

pinctrl: ... {
	compatible = "st,socmodel-pinctrl";
        usart2_pins_c: ... {
            pins1 {
                    pinmux = <USART2_TX_PD5>, <USART2_RTS_PD4>;
                    bias-disable;
                    drive-push-pull;
                    slew-rate = <3>;
            };
            pins2 {
                    pinmux = <USART2_RX_PD6>, <USART2_CTS_NSS_PD3>;
                    bias-disable;
            };
      };
};

Pros:

  • Use a established convention that works on another OS
  • Pin configuration can still be auto-generated from a database (e.g. USART2_TX_PD5)
  • The board makes the final decision on pin configuration (pull-up, slew-rate...)
  • Uses standardized properties (bias-..., slew-rate...)
  • Each board has a -pinctrl.dtsi file, means board.dts is potentially more re-usable

Cons:

  • May require more macro logic to extract pin information (it's part of a binding grand-children)
  • Re-use of pinctrl-node.yaml binding requires DT tooling changes
  • There are no defaults for pin configuration, board needs to manually define all of them

Notes:

  • Content of pinmux property could be auto-generated in a header file (e.g. #define USART2_TX_PD5 STM32_PINMUX('A', 0, AF8)).

2. DT node for each pin (Zephyr approach, e.g. for ST)

board.dts: Includes a file containing nodes, each representing the configuration of a pin. This file can be auto-generated, as is today for STM32 (SoC+package specific, e.g. stm32h743zitx-pinctrl.dtsi). Each pinctrl-N property references a set of pin nodes.

#include <st/h7/stm32h743zitx-pinctrl.dtsi>

&i2c1 {
	pinctrl-0 = <&i2c1_scl_pb8 &i2c1_sda_pb9>;
	status = "okay";
	clock-frequency = <I2C_BITRATE_FAST>;
};

socpkg-pinctrl.dtsi: File containing all pin configurations. Such file can be auto-generated, in which case all possible combinations would be included. Defaults for pin configuration such as pull-up/down would also be there.

...
		i2c1_scl_pb8: i2c1_scl_pb8 {
			pinmux = <STM32_PINMUX('B', 8, AF4)>;
			bias-pull-up;
			drive-open-drain;
		};
...
		i2c1_sda_pb9: i2c1_sda_pb9 {
			pinmux = <STM32_PINMUX('B', 9, AF4)>;
			bias-pull-up;
			drive-open-drain;
		};

If the file is auto-generated and the board needs to change any of the default pin properties, it can do it like this:

&i2c1_scl_pb8 {
	/delete-property/ bias-pull-up;
	bias-pull-down;
};

There is a proof of concept that introduces low power/sleep states using this approach:
#35039

Pros:

  • Pin configuration can be auto-generated from a database (e.g. i2c1_sda_pb9 node), including "common" defaults
  • Uses standardized properties (bias-..., slew-rate...)
  • Proven to work, e.g. for STM32

Cons:

  • If not auto-generated, -pinctrl.dtsi file is more verbose compared to (1)
  • When auto-generated, node names do not give a hint on pin configurations (e.g. does i2c1_sda_pb9 set open-drain?)
  • When auto-generated, common defaults may not be appropriate for some boards. Override mechanism is not "ideal" (see example above)
  • When auto-generated, some edge cases are difficult to solve properly. See https://github.com/zephyrproject-rtos/hal_stm32/blob/main/scripts/genpinctrl/stm32-pinctrl-config.yaml#L194 (slew-rate is set to a maximum, even though only required for a single family).

3. Variant of (2) for auto-generated cases

If the auto-generated files do not contain pin configuration, i.e. only pin routing, we'd then have:

board.dts: Same as in (2), but including board-pintrl.dtsi:

#include "board-pinctrl.dtsi"

&i2c1 {
	pinctrl-0 = <&i2c1_scl_pb8 &i2c1_sda_pb9>;
	status = "okay";
	clock-frequency = <I2C_BITRATE_FAST>;
};

board-pinctrl.dtsi: A file that sets pin configurations.

#include <st/h7/stm32h743zitx-pinctrl.dtsi>

...
&i2c1_scl_pb8 {
	bias-pull-up;
	drive-open-drain;
};

&i2c1_sda_pb9 {
	bias-pull-up;
	drive-open-drain;
};
...

socpkg-pinctrl.dtsi: Auto-generated file with just pin routing:

...
			i2c1_scl_pb8: i2c1_scl_pb8 {
				pinmux = <STM32_PINMUX('B', 8, AF4)>;
			};
...
			i2c1_sda_pb9: i2c1_sda_pb9 {
				pinmux = <STM32_PINMUX('B', 9, AF4)>;
			};

Pros:

  • Pin configuration is auto-generated from a database (e.g. i2c1_sda_pb9 node)
  • Uses standardized properties (bias-..., slew-rate...)
  • The board makes the final decision on pin configuration (pull-up, slew-rate...)

Cons:

  • Less compact than approach (1), where pins with common configurations can be grouped

4. Vendor specific approach 1 (nRF)

The pinctrl API doesn't need to make any assumption on how pinctrl data is stored internally. This means that, in practice, vendors can really decide what to do in nodes referenced from pinctrl-N properties.

For example, for nRF we could have:

board.dts:

&uart0 {
	compatible = "nordic,nrf-uarte";
	status = "disabled";
	current-speed = <115200>;

	pinctrl-0 = <&uart0_default>;
	pinctrl-1 = <&uart0_sleep>;
	pinctrl-names = "default", "sleep";
};

board-pinctrl.dts:

&pinctrl {
	/* UART0 */
	uart0_default: uart0_default {
		pincfg = <PINCTRL_NRF(0, 6, OUT, IDISC, PN, UART_TX)
			  PINCTRL_NRF(0, 8, INP, ICONN, PU, UART_RX)
			  PINCTRL_NRF(0, 5, OUT, IDISC, PN, UART_RTS)
			  PINCTRL_NRF(0, 7, INP, ICONN, PU, UART_CTS)>;
	};

	uart0_sleep: uart0_sleep {
		pincfg = <PINCTRL_NRF(0, 6, INP, IDISC, PN, UART_TX)
			  PINCTRL_NRF(0, 8, INP, IDISC, PN, UART_RX)
			  PINCTRL_NRF(0, 5, INP, IDISC, PN, UART_RTS)
			  PINCTRL_NRF(0, 7, INP, IDISC, PN, UART_CTS)>;
	};
};

Pros:

  • Vendor specific needs can be always accommodated, nothing is enforced from the API side.
  • API usage doesn't change, same concepts are used
  • Representation is more compact

Cons:

  • Does not re-use pincfg-node.yaml properties (e.g. bias-..., slew-rate...)
  • In this case, there are no "common" defaults, users needs to explicitly set all pin configurations. Could be mitigated with more PINCTRL_NRF_XXX like macros, specific to e.g. UART.

5. Vendor specific approach 2 (nRF)

Following on vendor specific approach, we could have, for nRF:

board.dts:

&uart0 {
	compatible = "nordic,nrf-uarte";
	status = "disabled";
	current-speed = <115200>;

	pinctrl-0 = <&uart0_default>;
	pinctrl-1 = <&uart0_sleep>;
	pinctrl-names = "default", "sleep";
};

board-pinctrl.dts:

&pinctrl {
	/* UART0 */
	uart0_default: uart0_default {
	        compatible = "nordic,nrf-uarte-pinctrl";
	        tx-pin = <6>;
	        tx-config = <OUT IDISC PN>; /* could have this default, so made optional */
	        /* or: tx-pull-up; boolean like properties */
	        rx-pin = <8>;
	        ...
	};

	uart0_sleep: uart0_sleep {
	        compatible = "nordic,nrf-uarte-pinctrl";
	        tx-pin = <6>;
	        tx-config = <INP IDISC PN>;
	        rx-pin = <8>;
	        rx-config = <INP IDISC PN>;
	        ...
	};
};

Pros:

  • A similar approach compared to current solution, where the driver has x-pin style properties.
  • Allows to have "common" defaults

Cons:

  • Does not re-use pincfg-node.yaml properties (e.g. bias-..., slew-rate...)
  • Requires per-driver pinctrl compatibles (can make information extraction a bit more complicated)
9 replies
@erwango

The Linux approach can be simplified by autogenerating the possible combinations

Sure, auto-generation is required, but this doesn't answer any of these points (bloated + hard to find correct config)

I imagine Linux has taken this path since they end up running on a large number of boards where "common configs" don't always apply.

In the contrary, as I know from ST part at least, they chose this path because they only have to consider a quite limited number of boards, which makes it easier to define groups (and they only provide a limited subset of combinations).

@gmarull

The Linux approach can be simplified by autogenerating the possible combinations

Sure, auto-generation is required, but this doesn't answer any of these points (bloated + hard to find correct config)

Current auto-generated DTS contains common but not necessarily correct configuration when it comes to pin properties. The correct configuration depends, in many cases, on the board design or operating conditions (e.g. bus speed).

I imagine Linux has taken this path since they end up running on a large number of boards where "common configs" don't always apply.

In the contrary, as I know from ST part at least, they chose this path because they only have to consider a quite limited number of boards, which makes it easier to define groups (and they only provide a limited subset of combinations).

The number of in-tree boards in Linux is low, that's true. I assume they just provide the few combinations that apply to common dev kits. Custom boards needs to create their own board-pinctrl.dtsi files.

@gmarull

Examples for multiple of the approaches listed in this thread for Nordic:

Initial thoughts:

  • When crafting -pinctrl.dtsi files manually, Option 1 seems to be the one generating less verbose files
  • If the provided common defaults option is acceptable, Option 2 is the one that requires less user effort.
@trond-snekvik

@mbolivar-nordic asked me to weigh in on the proposals from an editor support point of view. All of these proposals are snippet-able, but there's definitely a difference in usability, IMO.

Generally, the less macro magic there is going on, the easier it is to provide editor support for these definitions. For instance, the VS Code DT extension already supports autocompleting full nodes by spawning all properties as a snippet. If you for example type bma280 in an i2c bus node and press tab, it'll expand to

bma280 {
    compatible = "bosch,bma280";
    reg = <addr size>;
    label = "BMA280";
};

and mark addr and size as snippet fields to be filled. This uses the required properties from the bindings file to populate the properties, and this would work right out of the box for me in option 5. Option 6 would also be a good fit for this type of snippet expansion, but the engine would need to support child nodes in the snippets too.

For the macro based ones, there's no way to provide completion options for the parameters without adding custom logic for each macro, not to mention things like guiconfig for DT. Unless some sort of schema for these macros are maintained in the main repo, they're a major pain to support in editor extensions, and I'd be very hesitant to add them in any out-of-tree support module. Additionally, they're completely unreadable in the compiled output, and cannot really be replaced by manual text. The only feasible way to let users know that they need to write certain properties as a macro is through examples and tribal knowledge, which increases the cost of entry for what should be one of the most basic things you'd want to do in your overlay file. It's a minor problem in things like -gpios definitions (e.g. GPIO_PULL_UP), but interacting with files like https://github.com/zephyrproject-rtos/zephyr/blob/main/dts/arm/atmel/same70-pinctrl.dtsi and https://github.com/zephyrproject-rtos/hal_stm32/blob/f8ff8d25aa0a9e65948040c7b47ec67f3fa300df/dts/st/f0/stm32f031k(4-6)ux-pinctrl.dtsi is tough, and there's virtually nothing an editor extension can do to improve it. Most of these things are read-only for end-user now, as they'll just reference them, but if they start leaking into overlay files, they will degrade the usability, IMO.

I'm not super familiar with pinctrl or its history in the Linux Kernel, but from a usability point of view, the closer we are to WYSIWYG, the better.

@trond-snekvik

FWIW, I think options 5 and 6 are the most sensible, with option 2 as a runner-up. I am a bit surprised that none of the options use the -gpios syntax, but I know this has been rejected in the past. I'm pretty sure I'm missing something, but the phandle syntax is by far the most readable and sensible pin configuration scheme we use in Zephyr, IMO. For instance, I find this to be a lot more readable (and writable!) than the suggestions with macros or splitting pins and configurations into multiple properties:

&pinctrl {
    uart0_sleep {
        compatible = "nordic,nrf-uarte-pinctrl";
        tx-gpio = <&gpio0 6 GPIO_ACTIVE_HIGH>;
        rx-gpio = <&gpio0 8 GPIO_PULL_DOWN>;
        rts-gpio = <&gpio0 5 0>;
        cts-gpio = <&gpio0 7 GPIO_PULL_DOWN>;
    };
};

I think proposal 6 by @mbolivar-nordic makes the most sense, as it doesn't mix config and pin properties in the same node scope. Option 5 kind of mixes domains by splitting pins into multiple properties without grouping them by pins in any way. Having tx-pin, rx-pin and tx-cfg all at the same place in the hierarchy when there are several ways to make a hierarchy in DTS, seems a bit odd.

if properties are stored on a pin basis, the final value could be a combination of group, pin_creation macros and individuals.

at (4)

Cons:

Does not re-use pincfg-node.yaml properties (e.g. bias-..., slew-rate...)
In this case, there are no "common" defaults, users needs to explicitly set all pin configurations. Could be mitigated with more PINCTRL_NRF_XXX like macros, specific to e.g. UART.

It is possible to use a variable length list at pin creation at any proposal. I mean, a pin can be defined and all properties can follow pincfg-node.yaml. This is already available and below can be an option too. It is possible allow pincfg-node.yaml on the group too.

&pinctrl {
	/* UART0 */
	uart0_default: uart0_default {
		pincfg = <PINCTRL_NRF(0, 6, OUT, IDISC, PN, UART_TX)
                         /* Set individual property */
			  PINCTRL_NRF(0, 8, INP, ICONN, PU, UART_RX [, bias-pull-up, ..] )
			  PINCTRL_NRF(0, 5, OUT, IDISC, PN, UART_RTS)
			  PINCTRL_NRF(0, 7, INP, ICONN, PU, UART_CTS [, bias-pull-up, ..] )>;

		/* Add a common property for all 4 pins */
		slew-rate = <3>;
	};
};

User still have the possibility to do below changes because properties are stored by pin individually.

&i2c1_scl_pb8 {
	/delete-property/ bias-pull-up;
	bias-pull-down;
};

Not sure if this kind of flexibility should be available.

1 reply
@gmarull

if properties are stored on a pin basis, the final value could be a combination of group, pin_creation macros and individuals.

at (4)

Cons:
Does not re-use pincfg-node.yaml properties (e.g. bias-..., slew-rate...)
In this case, there are no "common" defaults, users needs to explicitly set all pin configurations. Could be mitigated with more PINCTRL_NRF_XXX like macros, specific to e.g. UART.

It is possible to use a variable length list at pin creation at any proposal. I mean, a pin can be defined and all properties can follow pincfg-node.yaml. This is already available and below can be an option too. It is possible allow pincfg-node.yaml on the group too.

&pinctrl {
	/* UART0 */
	uart0_default: uart0_default {
		pincfg = <PINCTRL_NRF(0, 6, OUT, IDISC, PN, UART_TX)
                         /* Set individual property */
			  PINCTRL_NRF(0, 8, INP, ICONN, PU, UART_RX [, bias-pull-up, ..] )
			  PINCTRL_NRF(0, 5, OUT, IDISC, PN, UART_RTS)
			  PINCTRL_NRF(0, 7, INP, ICONN, PU, UART_CTS [, bias-pull-up, ..] )>;

This would re-use names in the macro, but they'd not be DT properties as defined in pincfg-node. Not sure how non-boolean properties would work in that case.

  /* Add a common property for all 4 pins */
  slew-rate = <3>;

If not common to all 4 pins then the "subgroups" need to be introduced somehow to group pins with common properties, which takes us to case 1 representation.

};
};


User still have the possibility to do below changes because properties are stored by pin individually.

&i2c1_scl_pb8 {
/delete-property/ bias-pull-up;
bias-pull-down;
};


Not sure if this kind of flexibility should be available.

This is how it's done today for the STM32 case, the flexibility is given by Devicetree itself.

I noted that loopback interfaces currently require pinctrl definitions to avoid build errors. I'm not sure if this is a valid use case.

0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment