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
parent aecb0c74b5
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
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

View File

@ -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 */

View File

@ -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