drivers: pinctrl: add support for dynamic pin control

Add support for dynamic pin control, that is, allow to change device pin
configuration at runtime. Because no device de-initialization is
available yet, this API has limited usage options, e.g. modify pin
configuration at early boot stage (before device driver is initialized)

Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
This commit is contained in:
Gerard Marull-Paretas 2021-09-13 16:30:58 +02:00 committed by Kumar Gala
commit 329f2453c5
3 changed files with 150 additions and 6 deletions

View file

@ -21,4 +21,12 @@ config PINCTRL_NON_STATIC
a driver has to be accessed externally. This can happen, for example, when a driver has to be accessed externally. This can happen, for example, when
dynamic pin control is enabled or in testing environments. 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 endif # PINCTRL

View file

@ -20,3 +20,36 @@ int pinctrl_lookup_state(const struct pinctrl_dev_config *config, uint8_t id,
return -ENOENT; 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 */

View file

@ -197,22 +197,29 @@ struct pinctrl_dev_config {
#define Z_PINCTRL_DEV_CONFIG_STATIC static #define Z_PINCTRL_DEV_CONFIG_STATIC static
#endif #endif
#ifdef CONFIG_PINCTRL_DYNAMIC
#define Z_PINCTRL_DEV_CONFIG_CONST
#else
#define Z_PINCTRL_DEV_CONFIG_CONST const
#endif
/** @endcond */ /** @endcond */
#if defined(CONFIG_PINCTRL_NON_STATIC) || defined(__DOXYGEN__) #if defined(CONFIG_PINCTRL_NON_STATIC) || defined(__DOXYGEN__)
/** /**
* @brief Declare pin control configuration for a given node identifier. * @brief Declare pin control configuration for a given node identifier.
* *
* This macro should be used by tests to declare the pin control configuration * This macro should be used by tests or applications using runtime pin control
* for a device. #PINCTRL_DT_DEV_CONFIG_GET can later be used to obtain a * to declare the pin control configuration for a device.
* reference to such configuration. * #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. * Only available if @kconfig{CONFIG_PINCTRL_NON_STATIC} is selected.
* *
* @param node_id Node identifier. * @param node_id Node identifier.
*/ */
#define PINCTRL_DT_DEV_CONFIG_DECLARE(node_id) \ #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) Z_PINCTRL_DEV_CONFIG_NAME(node_id)
#endif /* defined(CONFIG_PINCTRL_NON_STATIC) || defined(__DOXYGEN__) */ #endif /* defined(CONFIG_PINCTRL_NON_STATIC) || defined(__DOXYGEN__) */
@ -231,8 +238,9 @@ struct pinctrl_dev_config {
UTIL_LISTIFY(DT_NUM_PINCTRL_STATES(node_id), \ UTIL_LISTIFY(DT_NUM_PINCTRL_STATES(node_id), \
Z_PINCTRL_STATE_PINS_DEFINE, node_id) \ Z_PINCTRL_STATE_PINS_DEFINE, node_id) \
Z_PINCTRL_STATES_DEFINE(node_id) \ Z_PINCTRL_STATES_DEFINE(node_id) \
const Z_PINCTRL_DEV_CONFIG_STATIC struct pinctrl_dev_config \ Z_PINCTRL_DEV_CONFIG_CONST Z_PINCTRL_DEV_CONFIG_STATIC \
Z_PINCTRL_DEV_CONFIG_NAME(node_id) = Z_PINCTRL_DEV_CONFIG_INIT(node_id); 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. * @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); 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 #ifdef __cplusplus
} }
#endif #endif