diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index f1bfebb9855..7e1a67175b8 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -21,4 +21,12 @@ config PINCTRL_NON_STATIC a driver has to be accessed externally. This can happen, for example, when dynamic pin control is enabled or in testing environments. +config PINCTRL_DYNAMIC + bool "Enable dynamic configuration of pins" + select PINCTRL_NON_STATIC + help + When this option is enabled pin control configuration can be changed at + runtime. This can be useful, for example, to change the pins assigned to a + peripheral at early boot stages depending on a certain input. + endif # PINCTRL diff --git a/drivers/pinctrl/common.c b/drivers/pinctrl/common.c index a34eab38898..d2d26529102 100644 --- a/drivers/pinctrl/common.c +++ b/drivers/pinctrl/common.c @@ -20,3 +20,36 @@ int pinctrl_lookup_state(const struct pinctrl_dev_config *config, uint8_t id, return -ENOENT; } + +#ifdef CONFIG_PINCTRL_DYNAMIC +int pinctrl_update_states(struct pinctrl_dev_config *config, + const struct pinctrl_state *states, + uint8_t state_cnt) +{ + uint8_t equal = 0U; + + /* check we are inserting same number of states */ + if (config->state_cnt != state_cnt) { + return -EINVAL; + } + + /* check we have the same states */ + for (uint8_t i = 0U; i < state_cnt; i++) { + for (uint8_t j = 0U; j < config->state_cnt; j++) { + if (states[i].id == config->states[j].id) { + equal++; + break; + } + } + } + + if (equal != state_cnt) { + return -EINVAL; + } + + /* replace current states */ + config->states = states; + + return 0; +} +#endif /* CONFIG_PINCTRL_DYNAMIC */ diff --git a/include/drivers/pinctrl.h b/include/drivers/pinctrl.h index f8b6b3ff23c..b8a429c240a 100644 --- a/include/drivers/pinctrl.h +++ b/include/drivers/pinctrl.h @@ -197,22 +197,29 @@ struct pinctrl_dev_config { #define Z_PINCTRL_DEV_CONFIG_STATIC static #endif +#ifdef CONFIG_PINCTRL_DYNAMIC +#define Z_PINCTRL_DEV_CONFIG_CONST +#else +#define Z_PINCTRL_DEV_CONFIG_CONST const +#endif + /** @endcond */ #if defined(CONFIG_PINCTRL_NON_STATIC) || defined(__DOXYGEN__) /** * @brief Declare pin control configuration for a given node identifier. * - * This macro should be used by tests to declare the pin control configuration - * for a device. #PINCTRL_DT_DEV_CONFIG_GET can later be used to obtain a - * reference to such configuration. + * This macro should be used by tests or applications using runtime pin control + * to declare the pin control configuration for a device. + * #PINCTRL_DT_DEV_CONFIG_GET can later be used to obtain a reference to such + * configuration. * * Only available if @kconfig{CONFIG_PINCTRL_NON_STATIC} is selected. * * @param node_id Node identifier. */ #define PINCTRL_DT_DEV_CONFIG_DECLARE(node_id) \ - extern const struct pinctrl_dev_config \ + extern Z_PINCTRL_DEV_CONFIG_CONST struct pinctrl_dev_config \ Z_PINCTRL_DEV_CONFIG_NAME(node_id) #endif /* defined(CONFIG_PINCTRL_NON_STATIC) || defined(__DOXYGEN__) */ @@ -231,8 +238,9 @@ struct pinctrl_dev_config { UTIL_LISTIFY(DT_NUM_PINCTRL_STATES(node_id), \ Z_PINCTRL_STATE_PINS_DEFINE, node_id) \ Z_PINCTRL_STATES_DEFINE(node_id) \ - const Z_PINCTRL_DEV_CONFIG_STATIC struct pinctrl_dev_config \ - Z_PINCTRL_DEV_CONFIG_NAME(node_id) = Z_PINCTRL_DEV_CONFIG_INIT(node_id); + Z_PINCTRL_DEV_CONFIG_CONST Z_PINCTRL_DEV_CONFIG_STATIC \ + struct pinctrl_dev_config Z_PINCTRL_DEV_CONFIG_NAME(node_id) = \ + Z_PINCTRL_DEV_CONFIG_INIT(node_id); /** * @brief Define all pin control information for the given compatible index. @@ -342,6 +350,101 @@ static inline int pinctrl_apply_state(const struct pinctrl_dev_config *config, return pinctrl_apply_state_direct(config, state); } +#if defined(CONFIG_PINCTRL_DYNAMIC) || defined(__DOXYGEN__) +/** + * @defgroup pinctrl_interface_dynamic Dynamic Pin Control + * @{ + */ + +/** + * @brief Helper macro to define the pins of a pin control state from + * Devicetree. + * + * The name of the defined state pins variable is the same used by @p prop. This + * macro is expected to be used in conjunction with #PINCTRL_DT_STATE_INIT. + * + * @param node_id Node identifier containing @p prop. + * @param prop Property within @p node_id containing state configuration. + * + * @see #PINCTRL_DT_STATE_INIT + */ +#define PINCTRL_DT_STATE_PINS_DEFINE(node_id, prop) \ + static const pinctrl_soc_pin_t prop ## _pins[] = \ + Z_PINCTRL_STATE_PINS_INIT(node_id, prop); \ + +/** + * @brief Utility macro to initialize a pin control state. + * + * This macro should be used in conjunction with #PINCTRL_DT_STATE_PINS_DEFINE + * when using dynamic pin control to define an alternative state configuration + * stored in Devicetree. + * + * Example: + * + * @code{.devicetree} + * // board.dts + * + * /{ + * zephyr,user { + * // uart0_alt_default node contains alternative pin config + * uart0_alt_default = <&uart0_alt_default>; + * }; + * }; + * @endcode + * + * @code{.c} + * // application + * + * PINCTRL_DT_STATE_PINS_DEFINE(DT_PATH(zephyr_user), uart0_alt_default); + * + * static const struct pinctrl_state uart0_alt[] = { + * PINCTRL_DT_STATE_INIT(uart0_alt_default, PINCTRL_STATE_DEFAULT) + * }; + * @endcode + * + * @param prop Property name in Devicetree containing state configuration. + * @param state State represented by @p prop (see @ref PINCTRL_STATES). + * + * @see #PINCTRL_DT_STATE_PINS_DEFINE + */ +#define PINCTRL_DT_STATE_INIT(prop, state) \ + { \ + .id = state, \ + .pins = prop ## _pins, \ + .pin_cnt = ARRAY_SIZE(prop ## _pins) \ + } + +/** + * @brief Update states with a new set. + * + * @note In order to guarantee device drivers correct operation the same states + * have to be provided. For example, if @c default and @c sleep are in the + * current list of states, it is expected that the new array of states also + * contains both. + * + * @param config Pin control configuration. + * @param states New states to be set. + * @param state_cnt Number of new states to be set. + * + * @retval -EINVAL If the new configuration does not contain the same states as + * the current active configuration. + * @retval -ENOSYS If the functionality is not available. + * @retval 0 On success. + */ +int pinctrl_update_states(struct pinctrl_dev_config *config, + const struct pinctrl_state *states, + uint8_t state_cnt); + +/** @} */ +#else +static inline int pinctrl_update_states( + struct pinctrl_dev_config *config, + const struct pinctrl_state *states, uint8_t state_cnt) +{ + return -ENOSYS; +} +#endif /* defined(CONFIG_PINCTRL_DYNAMIC) || defined(__DOXYGEN__) */ + #ifdef __cplusplus } #endif