/* * Copyright (c) 2021 Nordic Semiconductor ASA * SPDX-License-Identifier: Apache-2.0 */ /** * @file * Public APIs for pin control drivers */ #ifndef ZEPHYR_INCLUDE_DRIVERS_PINCTRL_H_ #define ZEPHYR_INCLUDE_DRIVERS_PINCTRL_H_ /** * @brief Pin Controller Interface * @defgroup pinctrl_interface Pin Controller Interface * @since 3.0 * @version 0.1.0 * @ingroup io_interfaces * @{ */ #include <errno.h> #include <zephyr/device.h> #include <zephyr/devicetree.h> #include <zephyr/devicetree/pinctrl.h> #include <pinctrl_soc.h> #include <zephyr/sys/util.h> #ifdef __cplusplus extern "C" { #endif /** * @name Pin control states * @anchor PINCTRL_STATES * @{ */ /** Default state (state used when the device is in operational state). */ #define PINCTRL_STATE_DEFAULT 0U /** Sleep state (state used when the device is in low power mode). */ #define PINCTRL_STATE_SLEEP 1U /** This and higher values refer to custom private states. */ #define PINCTRL_STATE_PRIV_START 2U /** @} */ /** Pin control state configuration. */ struct pinctrl_state { /** Pin configurations. */ const pinctrl_soc_pin_t *pins; /** Number of pin configurations. */ uint8_t pin_cnt; /** State identifier (see @ref PINCTRL_STATES). */ uint8_t id; }; /** Pin controller configuration for a given device. */ struct pinctrl_dev_config { #if defined(CONFIG_PINCTRL_STORE_REG) || defined(__DOXYGEN__) /** * Device address (only available if @kconfig{CONFIG_PINCTRL_STORE_REG} * is enabled). */ uintptr_t reg; #endif /* defined(CONFIG_PINCTRL_STORE_REG) || defined(__DOXYGEN__) */ /** List of state configurations. */ const struct pinctrl_state *states; /** Number of state configurations. */ uint8_t state_cnt; }; /** Utility macro to indicate no register is used. */ #define PINCTRL_REG_NONE 0U /** @cond INTERNAL_HIDDEN */ #if !defined(CONFIG_PM) && !defined(CONFIG_PM_DEVICE) /** Out of power management configurations, ignore "sleep" state. */ #define PINCTRL_SKIP_SLEEP 1 #endif /** * @brief Obtain the state identifier for the given node and state index. * * @param state_idx State index. * @param node_id Node identifier. */ #define Z_PINCTRL_STATE_ID(state_idx, node_id) \ _CONCAT(PINCTRL_STATE_, \ DT_PINCTRL_IDX_TO_NAME_UPPER_TOKEN(node_id, state_idx)) /** * @brief Obtain the variable name storing pinctrl config for the given DT node * identifier. * * @param node_id Node identifier. */ #define Z_PINCTRL_DEV_CONFIG_NAME(node_id) \ _CONCAT(__pinctrl_dev_config, DEVICE_DT_NAME_GET(node_id)) /** * @brief Obtain the variable name storing pinctrl states for the given DT node * identifier. * * @param node_id Node identifier. */ #define Z_PINCTRL_STATES_NAME(node_id) \ _CONCAT(__pinctrl_states, DEVICE_DT_NAME_GET(node_id)) /** * @brief Obtain the variable name storing pinctrl pins for the given DT node * identifier and state index. * * @param state_idx State index. * @param node_id Node identifier. */ #define Z_PINCTRL_STATE_PINS_NAME(state_idx, node_id) \ _CONCAT(__pinctrl_state_pins_ ## state_idx, DEVICE_DT_NAME_GET(node_id)) /** * @brief Utility macro to check if given state has to be skipped. * * If a certain state has to be skipped, a macro named PINCTRL_SKIP_<STATE> * can be defined evaluating to 1. This can be useful, for example, to * automatically ignore the sleep state if no device power management is * enabled. * * @param state_idx State index. * @param node_id Node identifier. */ #define Z_PINCTRL_SKIP_STATE(state_idx, node_id) \ _CONCAT(PINCTRL_SKIP_, \ DT_PINCTRL_IDX_TO_NAME_UPPER_TOKEN(node_id, state_idx)) /** * @brief Helper macro to define pins for a given pin control state. * * @param state_idx State index. * @param node_id Node identifier. */ #define Z_PINCTRL_STATE_PINS_DEFINE(state_idx, node_id) \ COND_CODE_1(Z_PINCTRL_SKIP_STATE(state_idx, node_id), (), \ (static const pinctrl_soc_pin_t \ Z_PINCTRL_STATE_PINS_NAME(state_idx, node_id)[] = \ Z_PINCTRL_STATE_PINS_INIT(node_id, pinctrl_ ## state_idx))) /** * @brief Helper macro to initialize a pin control state. * * @param state_idx State index. * @param node_id Node identifier. */ #define Z_PINCTRL_STATE_INIT(state_idx, node_id) \ COND_CODE_1(Z_PINCTRL_SKIP_STATE(state_idx, node_id), (), \ ({ \ .pins = Z_PINCTRL_STATE_PINS_NAME(state_idx, node_id), \ .pin_cnt = ARRAY_SIZE(Z_PINCTRL_STATE_PINS_NAME(state_idx, \ node_id)), \ .id = Z_PINCTRL_STATE_ID(state_idx, node_id) \ })) /** * @brief Define all the states for the given node identifier. * * @param node_id Node identifier. */ #define Z_PINCTRL_STATES_DEFINE(node_id) \ static const struct pinctrl_state \ Z_PINCTRL_STATES_NAME(node_id)[] = { \ LISTIFY(DT_NUM_PINCTRL_STATES(node_id), \ Z_PINCTRL_STATE_INIT, (,), node_id) \ }; #ifdef CONFIG_PINCTRL_STORE_REG /** * @brief Helper macro to initialize pin control config. * * @param node_id Node identifier. */ #define Z_PINCTRL_DEV_CONFIG_INIT(node_id) \ { \ .reg = DT_REG_ADDR(node_id), \ .states = Z_PINCTRL_STATES_NAME(node_id), \ .state_cnt = ARRAY_SIZE(Z_PINCTRL_STATES_NAME(node_id)), \ } #else #define Z_PINCTRL_DEV_CONFIG_INIT(node_id) \ { \ .states = Z_PINCTRL_STATES_NAME(node_id), \ .state_cnt = ARRAY_SIZE(Z_PINCTRL_STATES_NAME(node_id)), \ } #endif #ifdef CONFIG_PINCTRL_NON_STATIC #define Z_PINCTRL_DEV_CONFIG_STATIC #else #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 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 Z_PINCTRL_DEV_CONFIG_CONST struct pinctrl_dev_config \ Z_PINCTRL_DEV_CONFIG_NAME(node_id) #endif /* defined(CONFIG_PINCTRL_NON_STATIC) || defined(__DOXYGEN__) */ /** * @brief Define all pin control information for the given node identifier. * * This helper macro should be called together with device definition. It * defines and initializes the pin control configuration for the device * represented by node_id. Each pin control state (pinctrl-0, ..., pinctrl-N) is * also defined and initialized. Note that states marked to be skipped will not * be defined (refer to Z_PINCTRL_SKIP_STATE for more details). * * @param node_id Node identifier. */ #define PINCTRL_DT_DEFINE(node_id) \ LISTIFY(DT_NUM_PINCTRL_STATES(node_id), \ Z_PINCTRL_STATE_PINS_DEFINE, (;), node_id); \ Z_PINCTRL_STATES_DEFINE(node_id) \ Z_PINCTRL_DEV_CONFIG_STATIC Z_PINCTRL_DEV_CONFIG_CONST \ 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. * * @param inst Instance number. * * @see #PINCTRL_DT_DEFINE */ #define PINCTRL_DT_INST_DEFINE(inst) PINCTRL_DT_DEFINE(DT_DRV_INST(inst)) /** * @brief Obtain a reference to the pin control configuration given a node * identifier. * * @param node_id Node identifier. */ #define PINCTRL_DT_DEV_CONFIG_GET(node_id) &Z_PINCTRL_DEV_CONFIG_NAME(node_id) /** * @brief Obtain a reference to the pin control configuration given current * compatible instance number. * * @param inst Instance number. * * @see #PINCTRL_DT_DEV_CONFIG_GET */ #define PINCTRL_DT_INST_DEV_CONFIG_GET(inst) \ PINCTRL_DT_DEV_CONFIG_GET(DT_DRV_INST(inst)) /** * @brief Find the state configuration for the given state id. * * @param config Pin controller configuration. * @param id Pin controller state id (see @ref PINCTRL_STATES). * @param state Found state. * * @retval 0 If state has been found. * @retval -ENOENT If the state has not been found. */ int pinctrl_lookup_state(const struct pinctrl_dev_config *config, uint8_t id, const struct pinctrl_state **state); /** * @brief Configure a set of pins. * * This function will configure the necessary hardware blocks to make the * configuration immediately effective. * * @warning This function must never be used to configure pins used by an * instantiated device driver. * * @param pins List of pins to be configured. * @param pin_cnt Number of pins. * @param reg Device register (optional, use #PINCTRL_REG_NONE if not used). * * @retval 0 If succeeded * @retval -errno Negative errno for other failures. */ int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, uintptr_t reg); /** * @brief Apply a state directly from the provided state configuration. * * @param config Pin control configuration. * @param state State. * * @retval 0 If succeeded * @retval -errno Negative errno for other failures. */ static inline int pinctrl_apply_state_direct( const struct pinctrl_dev_config *config, const struct pinctrl_state *state) { uintptr_t reg; #ifdef CONFIG_PINCTRL_STORE_REG reg = config->reg; #else ARG_UNUSED(config); reg = PINCTRL_REG_NONE; #endif return pinctrl_configure_pins(state->pins, state->pin_cnt, reg); } /** * @brief Apply a state from the given device configuration. * * @param config Pin control configuration. * @param id Id of the state to be applied (see @ref PINCTRL_STATES). * * @retval 0 If succeeded. * @retval -ENOENT If given state id does not exist. * @retval -errno Negative errno for other failures. */ static inline int pinctrl_apply_state(const struct pinctrl_dev_config *config, uint8_t id) { int ret; const struct pinctrl_state *state; ret = pinctrl_lookup_state(config, id, &state); if (ret < 0) { return ret; } 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) \ { \ .pins = prop ## _pins, \ .pin_cnt = ARRAY_SIZE(prop ## _pins), \ .id = state \ } /** * @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) { ARG_UNUSED(config); ARG_UNUSED(states); ARG_UNUSED(state_cnt); return -ENOSYS; } #endif /* defined(CONFIG_PINCTRL_DYNAMIC) || defined(__DOXYGEN__) */ #ifdef __cplusplus } #endif /** * @} */ #endif /* ZEPHYR_INCLUDE_DRIVERS_PINCTRL_H_ */