blob: 0cb09df7c667b15d4239a97401c16c811386d51c [file] [log] [blame]
/*
* 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
* @ingroup io_interfaces
* @{
*/
#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 */
#ifndef CONFIG_PM_DEVICE
/** If device power management is not enabled, "sleep" state will be ignored. */
#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), (), \
({ \
.id = Z_PINCTRL_STATE_ID(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)) \
}))
/**
* @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) \
{ \
.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)
{
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_ */