Device Tree

Pinctrl controller节点:

所有可选的支持属性可以查阅/dts/bindings/pinctrl/pincfg-node.yaml,支持配置上下拉,驱动能力等等。具体支持属性要参考soc的yaml文件/dts/bindings/pinctrl/xxx.yaml

当设备节点调用perip0_default的时候,group1~N都会被apply。

/* board-pinctrl.dtsi */
#include <vnd-soc-pkgxx.h>

&pinctrl {
    /* Node with pin configuration for default state */
    periph0_default: periph0_default {
        group1 {
            /* Mappings: PERIPH0_SIGA -> PX0, PERIPH0_SIGC -> PZ1 */
            pinmux = <PERIPH0_SIGA_PX0>, <PERIPH0_SIGC_PZ1>;
            /* Pins PX0 and PZ1 have pull-up enabled */
            bias-pull-up;
        };
        ...
        groupN {
            /* Mappings: PERIPH0_SIGB -> PY7 */
            pinmux = <PERIPH0_SIGB_PY7>;
        };
    };
};

使用pinctrl的设备节点:

&uart0 {
	pinctrl-0 = <&uart0_default>;
	pinctrl-1 = <&uart0_sleep>;
	pinctrl-names = "default", "sleep";
};

默认支持default, sleep两种属性,也可以自定义属性,比如slow, fast,这样需要在具体driver中自定义PINCTRL_STATE_XXX, 比如

// 自定义状态需要从PINCTRL_STATE_PRIV_START开始定义
#define PINCTRL_STATE_SLOW PINCTRL_STATE_PRIV_START
#define PINCTRL_STATE_MED (PINCTRL_STATE_PRIV_START + 1U)
#define PINCTRL_STATE_FAST (PINCTRL_STATE_PRIV_START + 2U)
#define PINCTRL_STATE_NOPULL (PINCTRL_STATE_PRIV_START + 3U)

// pinctrl apply的时候选择自定义属性
pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_NOPULL);

可以在pinctrl controller下面的pinctrl配置节点前加上/omit-if-no-ref/,表示这个节点没被引用的话会被丢弃,不会被解析到C头文件中。

&pinctrl {
    /omit-if-no-ref/ periph0_siga_px0_default: periph0_siga_px0_default {
        pinmux = <VNDSOC_PIN(X, 0, MUX0)>;
    };
};

Consumer

Device driver如何使用pinctrl配置引脚function:

i2c_dw.c为例,
通过PINCTRL_DT_INST_DEFINE(n), 创建并初始化好device对应的pinctrl_dev_config结构体。
可以通过PINCTRL_DT_INST_DEV_CONFIG_GET(n) 得到该pinctrl_dev_config结构体。

随后在init函数中调用pinctrl_apply_state(rom->pcfg, PINCTRL_STATE_DEFAULT);选择apply default的pinctrl配置。

如下:

#define DT_DRV_COMPAT mydev
...
#include <zephyr/drivers/pinctrl.h>
...
struct mydev_config {
    ...
    /* Reference to mydev pinctrl configuration */
    const struct pinctrl_dev_config *pcfg;
    ...
};
...
static int mydev_init(const struct device *dev)
{
    const struct mydev_config *config = dev->config;
    int ret;
    ...
    /* Select "default" state at initialization time */
    ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
    if (ret < 0) {
        return ret;
    }
    ...
}

#define MYDEV_DEFINE(i)                                                    \
    /* Define all pinctrl configuration for instance "i" */                \
    PINCTRL_DT_INST_DEFINE(i);                                             \
    ...                                                                    \
    static const struct mydev_config mydev_config_##i = {                  \
        ...                                                                \
        /* Keep a ref. to the pinctrl configuration for instance "i" */    \
        .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(i),                         \
        ...                                                                \
    };                                                                     \
    ...                                                                    \
                                                                           \
    DEVICE_DT_INST_DEFINE(i, mydev_init, NULL, &mydev_data##i,             \
                          &mydev_config##i, ...);

DT_INST_FOREACH_STATUS_OKAY(MYDEV_DEFINE)

分析下PINCTRL_DT_DEFINE这个宏,

#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)

2~3行针对dts某个device节点,有N个pinctrl-<N>就调用Z_PINCTRL_STATE_PINS_DEFINE函数,创建包含N个pinctrl_soc_pin_t结构体的数组, 每个结构体包含该pinctrl-<N>对应pinctrl controller节点所需要的pins。该结构体数组的具体创建过程由Z_PINCTRL_STATE_PINS_INIT决定,该宏需要不同厂商在pinctrl_soc.h中定义。

struct pinctrl_soc_pin_t
{
	// need to define in `pinctrl_soc.h`
}

第4行,根据N个pinctrl-<N>创建pinctrl_state结构体数组,从devicetree_generated.h中获取结构体信息。

每个pinctrl_state结构体:

struct pinctrl_state {
	const pinctrl_soc_pin_t *pins; // 对应上面2~3行创建的`pinctrl_soc_pin_t`结构体数组。
	uint8_t pin_cnt; // 该state包含多少个pin。
	uint8_t id = PINCTRL_STATE_XXX; // XXX可以是DEFAULT,SLEEP或自定义属性。
};

第5~7行,初始化一个pinctrl_dev_config结构体。

struct pinctrl_dev_config {
#if defined(CONFIG_PINCTRL_STORE_REG) || defined(__DOXYGEN__)
	uintptr_t reg; // 该device的reg地址
#endif
	const struct pinctrl_state *states; // 即上面的`pinctrl_state`结构体数组。
	uint8_t state_cnt; // 包含的state数量。
};

结构体关系如下, 其中
pinctrl_dev_config 是每个device拥有一个。
pinctrl_state 对应每个device的一个pinctrl state, 即dts中的pinctrl-<N>
pinctrl_soc_pin_t 对应一个pin,包含了pin number, config配置信息等。
Pinctrl 结构体

Provider

Pinctrl Driver实现:
主要需要实现回调函数pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, uintptr_t reg)
pinctrl_soc_pin_t *pins:某个pinctrl state包含的pins链表。
pin_cnt:该state包含的pins数量。
reg: pinctrl controller的地址。

添加pinctrl_soc.h, 一般路径为soc/<arch>/<vendor>/<board>/...
在其中定义pinctrl_soc_pin_t 结构体。Z_PINCTRL_STATE_PINS_INIT宏,该宏接收两个参数,设备树node identifier和property name(pinctrl-N),用来解析设备树属性。

参考ti-cc32xx pinctrl的实现,参考文件有:
dts/bindings/pinctrl/ti,cc32xx-pinctrl.yaml:描述设备树属性。
soc/arm/ti_simplelink/cc32xx/pinctrl_soc.h: 具体实现。
include/zephyr/dt-bindings/pinctrl/ti-cc32xx-pinctrl.h:头文件。
boards/arm/cc3220sf_launchxl/cc3220sf_launchxl-pinctrl.dtsi: pinctrl设备树。
drivers/pinctrl/pinctrl_ti_cc32xx.cpinctrl_nrf.c: pinctrl driver。


pinctrl.dtsi:

&pinctrl {
	uart0_default: uart0_default {
		group1 {
			pinmux = <UART0_TX_P55>, <UART0_RX_P57>;
		};
	};

	i2c0_default: i2c0_default {
		group1 {
			pinmux = <I2C_SCL_P1>, <I2C_SDA_P2>;
		};
	};
};

&uart0 {
	pinctrl-0 = <&uart0_default>;
	pinctrl-names = "default";
};

&i2c0 {
	pinctrl-0 = <&i2c0_default>;
	pinctrl-names = "default";
};

pinctrl_soc.h:

#define Z_PINCTRL_STATE_PINS_INIT(node_id, prop)                                                   \
	{                                                                                          \
		DT_FOREACH_CHILD_VARGS(DT_PHANDLE(node_id, prop), DT_FOREACH_PROP_ELEM, pinmux,    \
				       Z_PINCTRL_STATE_PIN_INIT)                                   \
	}

上层调用传入的node_id对应使用pinctrl的设备节点,prop对应pinctrl-0,1...

DT_FOREACH_CHILD_VARGS会遍历pinctrl-X引用的phandle下的group1~n,这里这个名称可以是任意值,因为会遍历所有子节点,对pinmux属性调用Z_PINCTRL_STATE_PIN_INIT函数,每个pin创建一个pinctrl_soc_pin_t结构体。


typedef uint32_t pinctrl_soc_pin_t;

#define Z_PINCTRL_STATE_PIN_INIT(node_id, prop, idx)                                               \
	(DT_PROP_BY_IDX(node_id, prop, idx) |                                                      \
	 (TI_CC32XX_OPEN_DRAIN * DT_PROP(node_id, drive_open_drain)) |                             \
	 (TI_CC32XX_PULL_UP * DT_PROP(node_id, bias_pull_down)) |                                  \
	 (TI_CC32XX_PULL_DOWN * DT_PROP(node_id, bias_pull_up)) |                                  \
	 ((DT_ENUM_IDX(node_id, drive_strength) & TI_CC32XX_DRIVE_STRENGTH_MSK)                    \
	  << TI_CC32XX_DRIVE_STRENGTH_POS) |                                                       \
	 TI_CC32XX_PAD_OUT_OVERRIDE | TI_CC32XX_PAD_OUT_BUF_OVERRIDE),

DT_PROP_BY_IDX(node_id, prop, idx): 从设备树pinmux prop中获取值,比如<UART0_TX_P55>, <UART0_RX_P57>,这些宏定义在ti-cc32xx-pinctrl.h中,前面UART0_TX表示mux function,后面P55表示第55个pin。mux function保存在bit0~3, pin offset保存在bit16~21。

(TI_CC32XX_OPEN_DRAIN * DT_PROP(node_id, drive_open_drain)) 判断设备中某个group中是否有属性drive_open_drain, 保存在TI_CC32XX_OPEN_DRAIN, bit4中。

其他几个属性同理,一样保存进pinctrl_soc_pin_t的bitmap中。

排列顺序如下图,这里的位置应该对应的ti pinctrol寄存器。


static int pinctrl_configure_pin(pinctrl_soc_pin_t pincfg)
{
	uint8_t pin;

	pin = (pincfg >> TI_CC32XX_PIN_POS) & TI_CC32XX_PIN_MSK;
	if ((pin >= ARRAY_SIZE(pin2pad)) || (pin2pad[pin] == 255U)) {
		return -EINVAL;
	}

	sys_write32(pincfg & MEM_GPIO_PAD_CONFIG_MSK, DT_INST_REG_ADDR(0) + (pin2pad[pin] << 2U));

	return 0;
}

接着在pinctrl_ti_cs32xx.c中,pinctrl_configure_pin函数,根据pin offset将其他配置写进对应的寄存器中。

对应的fingerprint项目的pad register,可以实现的pinctrl_soc_pin_t可以参考: