core: pinmux: improve dynamic pinmux configuration at runtime#415
core: pinmux: improve dynamic pinmux configuration at runtime#415dani-clo wants to merge 4 commits intoarduino:mainfrom
Conversation
pillo79
left a comment
There was a problem hiding this comment.
This will be a huge leap forward! 🚀 First review found a few scattered nits and improvements.
Overall, I think there are two big changes in this commit that are orthogonal:
- the new
PINCTRL_STATE_ARDUINO=> you callapply_dev_pinctrl()on any device, if it has that state, it resets the GPIOs. - devices are set to
deferred_init=> any device like that must be configured (if !ready(dev) { init(dev) }).
I think splitting these two topics in separate commits would make the implementation easier to get right.
| * | ||
| * Map deferred-init peripherals with their pinctrl configuration from devicetree. | ||
| */ | ||
| #define PINCTRL_NODE_DEFERRED(node_id) DT_PROP_OR(node_id, zephyr_deferred_init, 0) |
There was a problem hiding this comment.
This is unrelated to pin control. Also, IIRC there's no need for the "or", COND_CODE_1 should not error on empty.
| #define PINCTRL_NODE_DEFERRED(node_id) DT_PROP_OR(node_id, zephyr_deferred_init, 0) | |
| #define IS_DEFERRED(node_id) DT_PROP(node_id, zephyr_deferred_init) |
| * the peripheral's PSEL register must be written via the device base | ||
| * address stored in pcfg->reg. | ||
| * On platforms without CONFIG_PINCTRL_STORE_REG (e.g. STM32) the reg | ||
| * argument is ignored by their pinctrl driver, so passing PINCTRL_REG_NONE is safe. |
There was a problem hiding this comment.
the issue is the field is not present, otherwise you would not have an #ifdef...
| * Switches peripheral pins back to ARDUINO pinctrl state (alternate function), | ||
| * typically after a temporary transition to GPIO mode. | ||
| * | ||
| * @param idx Index of the pin within the device's ARDUINO pinctrl state |
| * @param dev Target peripheral device to acquire pin for | ||
| * @param target_state Desired pinctrl state (typically PINCTRL_STATE_DEFAULT or SLEEP) | ||
| */ | ||
| int apply_dev_pinctrl(const struct device *dev, uint8_t target_state) { |
There was a problem hiding this comment.
No need for target_state (the above function only works on PINCTRL_STATE_ARDUINO anyway)
| int apply_dev_pinctrl(const struct device *dev, uint8_t target_state) { | |
| int apply_dev_pinctrl(const struct device *dev) { |
| // TODO: add reverse map to find pin name from DAC* define | ||
| // In the meantime, consider A0 == DAC0 | ||
| _reinit_peripheral_if_needed((pin_size_t)(dacName + A0), dac_dev); | ||
| apply_dev_pinctrl(dac_dev, PINCTRL_STATE_DEFAULT); |
There was a problem hiding this comment.
just "apply", without specifying a state.
| void disableInterrupt(pin_size_t); | ||
| void _reinit_peripheral_if_needed(pin_size_t pin, const struct device *dev); | ||
|
|
||
| int apply_dev_pinctrl(const struct device *dev, uint8_t target_state); |
There was a problem hiding this comment.
missing forward declaration for the channel one?
| /* Re-apply DEFAULT pinctrl state so shared pins | ||
| * are remuxed back to Serial after temporary GPIO/PWM usage. | ||
| */ | ||
| (void)apply_dev_pinctrl(uart, PINCTRL_STATE_DEFAULT); |
There was a problem hiding this comment.
Doesn't this mean UARTs cannot be deferred?
I think a fix should be to have smarter stuff in the dev table and just call apply_dev_pinctrl 🙁
| template <typename DT_SPEC, size_t N> | ||
| static size_t state_pin_index_from_spec_index(const DT_SPEC (&specs)[N], size_t spec_idx) { | ||
| const struct device *dev = specs[spec_idx].dev; | ||
| size_t state_pin_idx = 0; | ||
|
|
||
| for (size_t i = 0; i < spec_idx; i++) { | ||
| if (specs[i].dev == dev) { | ||
| state_pin_idx++; | ||
| } | ||
| } | ||
|
|
||
| return state_pin_idx; | ||
| } |
There was a problem hiding this comment.
There was a problem hiding this comment.
No, the goal here is not to find a peripheral channel number; it is to find the correct pinctrl entry for the selected Arduino pin: for example on GIGA, A0 maps to adc1_inp4_pc4, and D3 maps to tim2_ch3_pa2.
There isn't a Zephyr generic API to map an arbitrary peripheral channel back to the specific pinctrl entry required by pinctrl_configure_pins(), so we derive the pinctrl entry from the board DT ordering.
This approach is peripheral and driver agnostic - works for all Arduino adc and pwm.
| #define PINCTRL_MAP_ENTRY_IF_PRESENT(node_id) \ | ||
| COND_CODE_1(PINCTRL_NODE_DEFERRED(node_id), (PINCTRL_MAP_ENTRY(node_id)), ()) |
There was a problem hiding this comment.
Possibly defining all entries here, or those that have a PINCTRL_STATE_ARDUINO, is better because it allows other devices (e.g serial, DAC) to be used in the same way.
Wondering, if the devices are marked deferred_init - if it would also make sense to maybe not allocate some of the Obviously that could be a follow on update instead as part of this one. But looks good |
3fd16e0 to
1aba51a
Compare
Built
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Artifact | Board | Core | Tests | RAM | Sketches | Warnings | Errors | |
|---|---|---|---|---|---|---|---|---|
✅ zephyr_contrib |
ek_ra8d1
| 📗 | ✅ |
11.9% |
2 | - | - | |
frdm_mcxn947
| 3 🏷️ | ✅ |
58.6% |
2 | - | - | ||
frdm_rw612
| 1 🏷️ | ✅ |
83.0% |
2 | - | - | ||
🔥 zephyr_main |
giga
| 🔥 | Core build failed! | |||||
nano33ble
| 🔥 | Core build failed! | ||||||
nano_matter
| 📗 | ✔️* |
|
20 | 8 | (2*) | ||
niclasense
| 2 🏷️ | ✅* |
|
20 | 8 | - | ||
opta
| 4 🏷️ | ✔️* |
46.7% |
54 | 8 | (2*) | ||
portentac33
| 🔥 | Core build failed! | ||||||
portentah7
| 🔥 | Core build failed! | ||||||
🔥 zephyr_unoq |
unoq
| 🔥 | Core build failed! | |||||
Legend
Board Test Status description 🔥 🔥 Test run failed to complete. ❌ 🔴 Test completed with unexpected errors. ✔️* 🚫 Test completed with errors, but all are known/expected. ✅* 🟡 Test completed with some warnings; no errors detected. ✅ 🟢 Test passed successfully, with no warnings or errors. 🌑 🌑 Test was skipped.
Caution
zephyr_main is blocked due to failures on giga, nano33ble, portentac33, portentah7!
Caution
zephyr_unoq is blocked due to failures on unoq!
DRAFT – work in progress: additional fixes may still come from https://github.com/maidnl/ArduinoCore-zephyr/tree/dynamic_pinctrl_fix
This PR improves the initial dynamic pinmux solution introduced in #269
What changes:
Marks all relevant nodes as zephyr,deferred-init.
Generates a flash-resident map from each node pinctrl devicetree configuration.
Configures peripheral pin routing at runtime using pinctrl_apply_state or pinctrl_configure_pins.
For channel-based peripherals (for example ADC and PWM), configures only the required channel pin instead of the full peripheral block.
Why:
Avoids early/static pin assignment for peripherals that are configured dynamically.
Reduces unnecessary pin configuration for multi-channel peripherals.
Documentation:
See dynamic_pinmux.md for design details.