IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32MP157 Pinctl子系统 -> 正文阅读

[嵌入式]STM32MP157 Pinctl子系统

Pinctrl子系统

1.什么是Pinctrl子系统

Pinctrl: Pin Controller,引脚控制器,用来控制引脚的多路复用和配置参数的硬件模块,对于复用来说,一个引脚可能有多个功能,如GPIO,I2C,SPI等等,我们要想使用哪个功能就得去配置,对于配置参数如上拉,下拉,开漏,推挽等等,这里说是一个硬件模块,但是大多数SOC并没有这个模块,所以linux内核就在软件层抽象出了一个pinctrl子系统用来实现这个功能。下面是一个结构简图:

所以一个pinctrl子系统应当由以下三部分:

  • 引脚枚举与命名(用来描述引脚,对所有引脚进行编号,命名,对引脚支持的功能进行描述)
  • 引脚复用,复用为GPIO,I2C等功能
  • 引脚配置,对引脚的的上拉、下拉、推挽、开漏、驱动能力进行配置

2.Pinctrl子系统的使用

Pinctrl子系统一般由芯片原厂的BSP工程时编写,所以我们主要是学会如何使用。

现在的驱动都是与设备树结合使用,所以一般我们使用Pinctrl子系统时主要体现在设备树中,在驱动程序中一般不用写Pinctrl相关的代码。下面采用一个案例来说明如何使用。

这是STM32MP157芯片串口3的设备节点

&usart3 { 
	pinctrl-names = "default", "sleep";
	pinctrl-0 = <&usart3_pins_c>;
	pinctrl-1 = <&usart3_sleep_pins_c>;
	status = "okay";
};
  • pinctrl-names:里边有两个字符串,这两个字符串是用来描述设备状态的,default:默认状态,sleep:休眠状态
  • pinctrl-0 :pinctrl-names所描述的第一个设备状态default所对应的引脚节点
  • pinctrl-1 :pinctrl-names所描述的第二个设备状态sleep所对应的引脚节点

如果有其他状态则继续添加,在pinctrl-names添加第三项,对应的引脚节点就是pinctrl-2。

上面所说的pinctrl-0和pinctrl-1的值 <&usart3_pins_c>、 <&usart3_sleep_pins_c>就是pincontroller子节点,最终pinctrl子系统会根据这个节点来配置引脚功能。这两个节点的定义如下图所示:

pin-controller{
        usart3_pins_c: uart3-0 {
                pins1 {
                    pinmux = <STM32_PINMUX('D', 8, AF7)>; /* UART5_TX */
                    bias-disable;
                    drive-push-pull;
                    slew-rate = <0>;
                };
                pins2 {
                    pinmux = <STM32_PINMUX('D', 9, AF7)>; /* UART5_RX */
                    bias-disable;
                };
            };

            usart3_sleep_pins_c: uart3-sleep-0 {
                pins {
                    pinmux = <STM32_PINMUX('D', 8, ANALOG)>, /* UART5_TX */
                                         <STM32_PINMUX('D', 9, ANALOG)>; /* UART5_RX */
                };
            };
    };	

进过查STM32MP157数据手册:

usart3_pins_c节点:

  • PD8引脚复用为AF7,复用功能为USART_TX功能,内部上下拉禁止,推挽输出,压摆率:低速

  • PD9引脚复用为AF7,复用功能为USART_RX功能,内部上下拉禁止。

usart3_sleep_pins_c节点:

  • PD8和PD9引脚复用为ANALOG,模拟量模式

通过上面代码及分析,我们可以将pinctrl子系统在设备树下的描述分为两部分:

  • 客户端设备:
    • pinctrl-names:描述设备状态
    • pinctrl-n: n从0开始,用来描述每个设备状态下所使用引脚,该引脚在pin controller中定义
  • pin-controller:
    • 客户端设备节点下所使用的引脚复用功能和配置信息的描述。

我们再看以下imx6ul的设备树下对于pinctrl的使用:

客户端设备:

&uart2 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_uart2>;
	uart-has-rtscts;
	status = "okay";
};

pin-controller端:

iomuxc{
        pinctrl_uart2: uart2grp {
                fsl,pins = <
                    MX6UL_PAD_UART2_TX_DATA__UART2_DCE_TX	0x1b0b1
                    MX6UL_PAD_UART2_RX_DATA__UART2_DCE_RX	0x1b0b1
                    MX6UL_PAD_UART3_RX_DATA__UART2_DCE_RTS	0x1b0b1
                    MX6UL_PAD_UART3_TX_DATA__UART2_DCE_CTS	0x1b0b1
                >;
            };
};

我们可以通过Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt文档得知:

  • MX6UL_PAD_UART2_TX_DATA__UART2_DCE_TX这种类型为一个宏定义,里边有5个值,用来配置引脚的复用功能,复用为串口2的TX功能。
  • 0x1b0b1:配置信息,配置上下拉、驱动能力等等

通过对比上面STM32MP157的设备树和imx6ul的设备树信息可知:

客户端设备:统一的格式,都是通过pinctrl-names描述设备状态,pinctrl-n用来指定对于客户端每个状态下对应的引脚。

pin-controller:没有统一的格式,对于不同的SOC厂家的格式不同,但是最后要实现的目的都是一致的,都是要设置引脚复用和引脚配置

3.Pinctrl子系统相关数据结构

以STM32MP157为例分析来分析Pinctrl子系统:位于drivers/pinctrl/stm32/pinctrl-stm32mp157.c文件

pinctrl: pin-controller@50002000 {
			#address-cells = <1>;
			#size-cells = <1>;
			compatible = "st,stm32mp157-pinctrl";
			ranges = <0 0x50002000 0xa400>;
			interrupt-parent = <&exti>;
			st,syscfg = <&exti 0x60 0xff>;
			hwlocks = <&hsem 0 1>;
			pins-are-numbered;

相应的可以找到它的驱动代码:位于drivers/pinctrl/stm32/pinctrl-stm32mp157.c

static const struct of_device_id stm32mp157_pctrl_match[] = {
	{
		.compatible = "st,stm32mp157-pinctrl",
		.data = &stm32mp157_match_data,
	},
	{
		.compatible = "st,stm32mp157-z-pinctrl",
		.data = &stm32mp157_z_match_data,
	},
	{ }
};
static struct platform_driver stm32mp157_pinctrl_driver = {
	.probe = stm32_pctl_probe,
	.driver = {
		.name = "stm32mp157-pinctrl",
		.of_match_table = stm32mp157_pctrl_match,
		.pm = &stm32_pinctrl_dev_pm_ops,
	},
};

在驱动和设备匹配成功后,调用stm32_pctl_probe函数:位于drivers/pinctrl/stm32/pinctrl-stm32.c文件

struct stm32_pinctrl {
        struct device *dev;
        struct pinctrl_dev *pctl_dev;
        struct pinctrl_desc pctl_desc;
        struct stm32_pinctrl_group *groups;
        unsigned ngroups;
        const char **grp_names;
        struct stm32_gpio_bank *banks;
        unsigned nbanks;
        const struct stm32_pinctrl_match_data *match_data;
        struct irq_domain	*domain;
        struct regmap		*regmap;
        struct regmap_field	*irqmux[STM32_GPIO_PINS_PER_BANK];
        struct hwspinlock *hwlock;
        struct stm32_desc_pin *pins;/* pin描述 */
        u32 npins;
        u32 pkg;
        u16 irqmux_map;
        spinlock_t irqmux_lock;
        u32 pin_base_shift;
};

int stm32_pctl_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;/* 对应的设备树节点 */
	struct device_node *child; /* 子节点 */
	const struct of_device_id *match; /* 设备驱动匹配项 */
	struct device *dev = &pdev->dev;/* 对应的设备 */
	struct stm32_pinctrl *pctl;     /* STM32 pinctrl */
	struct pinctrl_pin_desc *pins;  /* pin脚描述 */
	int i, ret, hwlock_id, banks = 0;

	if (!np)
		return -EINVAL;

	match = of_match_device(dev->driver->of_match_table, dev);/*获取设备对应的匹配项*/
	if (!match || !match->data)
		return -EINVAL;

	if (!of_find_property(np, "pins-are-numbered", NULL)) {
		dev_err(dev, "only support pins-are-numbered format\n");
		return -EINVAL;
	}

	pctl = devm_kzalloc(dev, sizeof(*pctl), GFP_KERNEL);
	if (!pctl)
		return -ENOMEM;

	/* pdev->dev->driver_data= pctl; */
	platform_set_drvdata(pdev, pctl);

	/* check for IRQ controller (may require deferred probe) */
	pctl->domain = stm32_pctrl_get_irq_domain(np);
	if (IS_ERR(pctl->domain))
		return PTR_ERR(pctl->domain);

	/* hwspinlock is optional */
	hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
	if (hwlock_id < 0) {
		if (hwlock_id == -EPROBE_DEFER)
			return hwlock_id;
	} else {
		pctl->hwlock = hwspin_lock_request_specific(hwlock_id);
	}

	spin_lock_init(&pctl->irqmux_lock);

	pctl->dev = dev;
	pctl->match_data = match->data;/* 获取匹配项中的数据 */

	/*  get package information */
	stm32_pctl_get_package(np, pctl);
	/* 
	 * 为pctl->pins分配内存,分配的大小为(pctl->match_data->npins)x(sizeof(*pctl->pins))
	 * 也就是(引脚个数*每个引脚描述所占用的内存大小)
	 */
	pctl->pins = devm_kcalloc(pctl->dev, pctl->match_data->npins,
				  sizeof(*pctl->pins), GFP_KERNEL);
	if (!pctl->pins)
		return -ENOMEM;
 
	/* 将匹配项match_data中的pins中的pin和functions拷贝到pctl->pins中, pctl->npins=拷贝的总的pins数量*/
	ret = stm32_pctrl_create_pins_tab(pctl, pctl->pins);
	if (ret)
		return ret;

	/*
	 * 将pctl中的group初始化
	 *  组名称配置为引脚名称 
		group->name = pin->pin.name;
		组的pin为引脚的pin number 
		group->pin = pin->pin.number;
		组名称保存到grp_names 
		pctl->grp_names[i] = pin->pin.name;
	 */
	ret = stm32_pctrl_build_state(pdev);
	if (ret) {
		dev_err(dev, "build state failed: %d\n", ret);
		return -EINVAL;
	}

	if (pctl->domain) {
		ret = stm32_pctrl_dt_setup_irq(pdev, pctl);
		if (ret)
			return ret;
	}

	pins = devm_kcalloc(&pdev->dev, pctl->npins, sizeof(*pins),
			    GFP_KERNEL);
	if (!pins)
		return -ENOMEM;
	/* 将pctl->pins拷贝到pins中 */
	for (i = 0; i < pctl->npins; i++)
		pins[i] = pctl->pins[i].pin;

	pctl->pctl_desc.name = dev_name(&pdev->dev);
	pctl->pctl_desc.owner = THIS_MODULE;
	pctl->pctl_desc.pins = pins;/* 将pins指针放入pctl->pctl_desc.pins */
	pctl->pctl_desc.npins = pctl->npins;
	pctl->pctl_desc.link_consumers = true;
	/* stm32_pconf_ops:用来配置引脚特性  如上拉或下拉   */
	pctl->pctl_desc.confops = &stm32_pconf_ops;
	/* stm32_pctrl_ops:全局的控制函数 */
	pctl->pctl_desc.pctlops = &stm32_pctrl_ops;
	/* stm32_pmx_ops:将引脚复用为其他功能 */
	pctl->pctl_desc.pmxops = &stm32_pmx_ops;
	pctl->dev = &pdev->dev;
	pctl->pin_base_shift = pctl->match_data->pin_base_shift;

	pctl->pctl_dev = devm_pinctrl_register(&pdev->dev, &pctl->pctl_desc,
					       pctl);

	if (IS_ERR(pctl->pctl_dev)) {
		dev_err(&pdev->dev, "Failed pinctrl registration\n");
		return PTR_ERR(pctl->pctl_dev);
	}

	for_each_available_child_of_node(np, child)
		if (of_property_read_bool(child, "gpio-controller"))
			banks++;

	if (!banks) {
		dev_err(dev, "at least one GPIO bank is required\n");
		return -EINVAL;
	}
	pctl->banks = devm_kcalloc(dev, banks, sizeof(*pctl->banks),
			GFP_KERNEL);
	if (!pctl->banks)
		return -ENOMEM;

	i = 0;
	for_each_available_child_of_node(np, child) {
		struct stm32_gpio_bank *bank = &pctl->banks[i];

		if (of_property_read_bool(child, "gpio-controller")) {
			bank->rstc = of_reset_control_get_exclusive(child,
								    NULL);
			if (PTR_ERR(bank->rstc) == -EPROBE_DEFER)
				return -EPROBE_DEFER;

			bank->clk = of_clk_get_by_name(child, NULL);
			if (IS_ERR(bank->clk)) {
				if (PTR_ERR(bank->clk) != -EPROBE_DEFER)
					dev_err(dev,
						"failed to get clk (%ld)\n",
						PTR_ERR(bank->clk));
				return PTR_ERR(bank->clk);
			}
			i++;
		}
	}

	for_each_available_child_of_node(np, child) {
		if (of_property_read_bool(child, "gpio-controller")) {
			ret = stm32_gpiolib_register_bank(pctl, child);
			if (ret) {
				of_node_put(child);
				return ret;
			}

			pctl->nbanks++;
		}
	}

	dev_info(dev, "Pinctrl STM32 initialized\n");

	return 0;
}

与Pinctrl相关的主要如下:

   pctl->pctl_desc.name = dev_name(&pdev->dev);
	pctl->pctl_desc.owner = THIS_MODULE;
	pctl->pctl_desc.pins = pins;/* 将pins指针放入pctl->pctl_desc.pins */
	pctl->pctl_desc.npins = pctl->npins;
	pctl->pctl_desc.link_consumers = true;
	/* stm32_pconf_ops:用来配置引脚特性  如上拉或下拉   */
	pctl->pctl_desc.confops = &stm32_pconf_ops;
	/* stm32_pctrl_ops:全局的控制函数 */
	pctl->pctl_desc.pctlops = &stm32_pctrl_ops;
	/* stm32_pmx_ops:将引脚复用为其他功能 */
	pctl->pctl_desc.pmxops = &stm32_pmx_ops;
	pctl->dev = &pdev->dev;
	pctl->pin_base_shift = pctl->match_data->pin_base_shift;

	pctl->pctl_dev = devm_pinctrl_register(&pdev->dev, &pctl->pctl_desc,
					       pctl);

pctl->pctl_desc是一个struct pinctrl_desc类型的结构体,从字面意思可看出就是pinctrl describe,用来描述pinctrl,最后通过struct pinctrl_dev *devm_pinctrl_register(struct device *dev, struct pinctrl_desc *pctldesc,void *driver_data)注册struct pinctrl_desc结构体,生成struct pinctrl_dev结构体,也就是pinctrl设备。pinctrl_dev结构体如下,可见每一个pinctrl设备中都有一个struct pinctrl_desc结构体

struct pinctrl_dev {
	struct list_head node;
	struct pinctrl_desc *desc;/* 用来描述pinctrl */
	struct radix_tree_root pin_desc_tree;
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
	struct radix_tree_root pin_group_tree;
	unsigned int num_groups;
#endif
#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS
	struct radix_tree_root pin_function_tree;
	unsigned int num_functions;
#endif
	struct list_head gpio_ranges;
	struct device *dev;
	struct module *owner;
	void *driver_data;
	struct pinctrl *p;
	struct pinctrl_state *hog_default;
	struct pinctrl_state *hog_sleep;
	struct mutex mutex;
#ifdef CONFIG_DEBUG_FS
	struct dentry *device_root;
#endif
};

从上面的代码可以看出,在写一个pinctrl驱动时我们要做的主要是:

  • 分配struct pinctrl_desc结构体
  • 设置struct pinctrl_desc结构体
  • 注册struct pinctrl_desc结构体

所以我们重点分析struct pinctrl_desc结构体:位于include/linux/pinctrl/pinctrl.h文件

struct pinctrl_desc {
   const char *name;/* pinctrl名称 */
   /*
       struct pinctrl_pin_desc {
       unsigned number; //引脚编号
       const char *name; //引脚名称 
       void *drv_data;
       };
   */
   const struct pinctrl_pin_desc *pins;/* 引脚的枚举(引脚名称,引脚编号) */
   unsigned int npins;/* 引脚的数量 */
   const struct pinctrl_ops *pctlops;/* 全局引脚控制操作 */
   const struct pinmux_ops *pmxops;/* 引脚配置操作 */
   const struct pinconf_ops *confops;/* 引脚复用操作 */
   struct module *owner;
#ifdef CONFIG_GENERIC_PINCONF
   unsigned int num_custom_params;
   const struct pinconf_generic_params *custom_params;
   const struct pin_config_item *custom_conf_items;
#endif
   bool link_consumers;
};

从上述结构体struct pinctrl_desc 结构体可以看出它包含了我们前面分析的三点:

  • 引脚枚举与命名:const struct pinctrl_pin_desc *pins;
  • 引脚复用:const struct pinmux_ops *pmxops;
  • 引脚配置:const struct pinconf_ops *confops;

全局引脚控制const struct pinctrl_ops结构体:位于include/linux/pinctrl/pinctrl.h文件

struct pinctrl_ops {
	/* 获取注册的group个数 */
	int (*get_groups_count) (struct pinctrl_dev *pctldev);
	/* 获取索引为selector的pin group的组名称 */
	const char *(*get_group_name) (struct pinctrl_dev *pctldev,
				       unsigned selector);
	/* 给定一个selector(或index),获取pin group中pin的信息(该pin group包含多少个pin,每个pin的id是什么) */
	int (*get_group_pins) (struct pinctrl_dev *pctldev,
			       unsigned selector,
			       const unsigned **pins,
			       unsigned *num_pins);
	/* debug fs的callback接口 */
	void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,
			  unsigned offset);
	/* 
	解析设备树中的"引脚配置节点"并把分析结果保存为mapping table 
	引脚配置节点如前面所列举的:
	STM32MP157:
	  usart3_pins_c: uart3-0 {
                pins1 {
                    pinmux = <STM32_PINMUX('D', 8, AF7)>; //UART5_TX 
                    bias-disable;
                    drive-push-pull;
                    slew-rate = <0>;
                };
                pins2 {
                    pinmux = <STM32_PINMUX('D', 9, AF7)>; // UART5_RX
                    bias-disable;
                };
            };
    IMX6UL:
    iomuxc{
        pinctrl_uart2: uart2grp {
                fsl,pins = <
                    MX6UL_PAD_UART2_TX_DATA__UART2_DCE_TX	0x1b0b1
                    MX6UL_PAD_UART2_RX_DATA__UART2_DCE_RX	0x1b0b1
                    MX6UL_PAD_UART3_RX_DATA__UART2_DCE_RTS	0x1b0b1
                    MX6UL_PAD_UART3_TX_DATA__UART2_DCE_CTS	0x1b0b1
                >;
            };
   };
	*/
	int (*dt_node_to_map) (struct pinctrl_dev *pctldev,
			       struct device_node *np_config,
			       struct pinctrl_map **map, unsigned *num_maps);
	/* 释放映射,与dt_node_to_map相对 */
	void (*dt_free_map) (struct pinctrl_dev *pctldev,
			     struct pinctrl_map *map, unsigned num_maps);
};

引脚复用:const struct pinmux_ops 位于include/linux/pinctrl/pinmux.h文件

struct pinmux_ops {
	/* pinctrl子系统进行具体的复用设定之前需要调用该函数,主要用来让底层的driver判断某个引脚的复用和设定是否ok */
	int (*request) (struct pinctrl_dev *pctldev, unsigned offset);
	/* request的逆函数,调用request函数请求占用了某些pin的资源,调用free可以释放这些资源 */
	int (*free) (struct pinctrl_dev *pctldev, unsigned offset);
	/* 返回pin control支持的functions的数目 */
	int (*get_functions_count) (struct pinctrl_dev *pctldev);
	/* 给定一个selector(或index),获取functions的name */
	const char *(*get_function_name) (struct pinctrl_dev *pctldev,
					  unsigned selector);
	/* 给定一个selector(或index),获取指定的functions的pin group信息 */
	int (*get_function_groups) (struct pinctrl_dev *pctldev,
				  unsigned selector,
				  const char * const **groups,
				  unsigned *num_groups);
	/* 给出function的selector(或index),和group的selector(或index),将group pin的functions配置为指定的functions */
	int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector,
			unsigned group_selector);
	/* request并且enable一个单独的gpio pin */
	int (*gpio_request_enable) (struct pinctrl_dev *pctldev,
				    struct pinctrl_gpio_range *range,
				    unsigned offset);
	/* gpio_request_enable的逆函数 */
	void (*gpio_disable_free) (struct pinctrl_dev *pctldev,
				   struct pinctrl_gpio_range *range,
				   unsigned offset);
	/* 设定GPIO 方向的回调函数 */
	int (*gpio_set_direction) (struct pinctrl_dev *pctldev,
				   struct pinctrl_gpio_range *range,
				   unsigned offset,
				   bool input);
	bool strict;
};

引脚配置:const struct pinconf_ops 位于include/linux/pinctrl/pinconf.h文件

struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
	bool is_generic;
#endif
	/* 给出一个pin ID以及config type ID,获取该引脚上指定的type的配置 */
	int (*pin_config_get) (struct pinctrl_dev *pctldev,
			       unsigned pin,
			       unsigned long *config);
	/* 设定一个pin的配置 */
	int (*pin_config_set) (struct pinctrl_dev *pctldev,
			       unsigned pin,
			       unsigned long *configs,
			       unsigned num_configs);
	/* 给定pin group的selector,获取pin group的配置*/
	int (*pin_config_group_get) (struct pinctrl_dev *pctldev,
				     unsigned selector,
				     unsigned long *config);
	/* 设定pin group的配置 */
	int (*pin_config_group_set) (struct pinctrl_dev *pctldev,
				     unsigned selector,
				     unsigned long *configs,
				     unsigned num_configs);
	/* debug接口 */
	void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev,
				     struct seq_file *s,
				     unsigned offset);
	/* debug接口 */
	void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev,
					   struct seq_file *s,
					   unsigned selector);
	/* debug接口 */
	void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,
					    struct seq_file *s,
					    unsigned long config);
};

从设备树中对节点进行设定后pinctrl子系统是如何一步一步将引脚的复用功能和配置信息提取,最后对硬件寄存器进行操作实现引脚的控制呢,主要过程如下:

首先当设备树节点与驱动匹配成功后首先会调用drivers/base/dd.c中的static int really_probe(struct device *dev, struct device_driver *drv)函数,在该函数中就实现了pinctrl子系统从设备树节点解析到最后对引脚进行复用功能设置,以及上下拉等配置。具体的代码量太大,我将实现过程列了出来,可以对比下面的函数调用过程去分析。

所以最终pinctrl子系统框架会调用在驱动中构建的struct pinctrl_desc结构体下的const struct pinctrl_ops、const struct pinmux_ops、const struct pinconf_ops中的操作函数对引脚进行配置。

我们可以看其中比较重要的几个结构体

struct device结构体下的struct dev_pin_info:

struct pinctrl {
	struct list_head node;
	struct device *dev;/* g该pinctrl所属的设备 */
	struct list_head states;
	struct pinctrl_state *state;/* 该pinctrl的状态描述 (包含状态名称和该状态所对应setting)*/
	struct list_head dt_maps;
	struct kref users;
};

struct pinctrl_state {
	struct list_head node;
	const char *name;  /* state名称:如default ,sleep,idle,init等 */
	struct list_head settings;/* 该状态下应的setting table的头结点 */
};

struct dev_pin_info {
	struct pinctrl *p;/*pinctrl链表*/
	struct pinctrl_state *default_state;/*默认状态*/
	struct pinctrl_state *init_state;/*初始化状态*/
#ifdef CONFIG_PM
	struct pinctrl_state *sleep_state;/*休眠状态*/
	struct pinctrl_state *idle_state;/* 空闲状态 */
#endif
};

pinctrl相关结构体

struct pinctrl_dev {
	struct list_head node;
	struct pinctrl_desc *desc;/*pin controller描述,就是驱动中申请、设置、注册*/
	struct radix_tree_root pin_desc_tree;
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
	struct radix_tree_root pin_group_tree;
	unsigned int num_groups;
#endif
#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS
	struct radix_tree_root pin_function_tree;
	unsigned int num_functions;
#endif
	struct list_head gpio_ranges;
	struct device *dev;
	struct module *owner;
	void *driver_data;
	struct pinctrl *p;
	struct pinctrl_state *hog_default;
	struct pinctrl_state *hog_sleep;
	struct mutex mutex;
#ifdef CONFIG_DEBUG_FS
	struct dentry *device_root;
#endif
};
&usart3 { 
	pinctrl-names = "default", "sleep";
	pinctrl-0 = <&usart3_pins_c>;
	pinctrl-1 = <&usart3_sleep_pins_c>;
	status = "okay";
};

每个设备驱动下都有struct device,如I2c驱动或platform驱动,在它构建的结构体中都有struct device成员,struct device下就有一个和pinctrl相关的成员struct dev_pin_info,也就是该设备下的引脚描述,这个结构体有五个元素:

  • struct pinctrl:如果设备节点pinctrl-names有描述状态,则最终会将每个状态解析为一个pinctrl,如上面的usart节点,在struct dev_pin_info下的struct pinctrl *p中就会解析出一个pinctrl table,里边有两个成员,第一个成员pinctrl的state就指向default状态struct pinctrl_state *default_state;第二个成员pinctrl的state就指向sleep状态struct pinctrl_state *sleep_state
  • struct pinctrl_state *default_state: default状态,例如usart3节点中的default状态对应的是pinctrl-0 = <&usart3_pins_c>;最后会将usart3_pins_c这个引脚配置节点解析为一个struct pinctrl_state,里边包含了状态名称和该状态对应的一系类setting,最后就是根据这一系类setting来对引脚进行配置。其他的struct pinctrl_state与它类似,就是将设备树中状态所对应的引脚配置节点解析为一个struct pinctrl_state,里边存储状态和该状态对应的setting,setting结构体后面会讲解,其实设备树引脚配置节点首先是被解析成map tabel,最后将map table转化为setting table。

map相关的结构体

struct pinctrl_maps {
	struct list_head node;
	const struct pinctrl_map *maps;
	unsigned num_maps;
};

enum pinctrl_map_type {
	PIN_MAP_TYPE_INVALID,
	PIN_MAP_TYPE_DUMMY_STATE,
	PIN_MAP_TYPE_MUX_GROUP,
	PIN_MAP_TYPE_CONFIGS_PIN,
	PIN_MAP_TYPE_CONFIGS_GROUP,
};

struct pinctrl_map_mux {
	const char *group;
	const char *function;
};

struct pinctrl_map_configs {
	const char *group_or_pin;
	unsigned long *configs;
	unsigned num_configs;
};

struct pinctrl_map {
	const char *dev_name;
	const char *name;
	enum pinctrl_map_type type;
	const char *ctrl_dev_name;
	union {
		struct pinctrl_map_mux mux;
		struct pinctrl_map_configs configs;
	} data;
};

map是从设备树解析而来,我们可以看出,每个map都有类型,一般常见的就是mux类型和configs类型。 struct pinctrl_map_mux mux和struct pinctrl_map_configs configs是一个联合体,当map类型为mux是就使用struct pinctrl_map_mux mux;当map类型为configs是就使用struct pinctrl_map_configs configs;

  • mux类型:
    • PIN_MAP_TYPE_MUX_GROUP:用来引脚复用功能的配置
      • 对于mux类型,使用struct pinctrl_map_mux mux来描述这个map功能,这个结构体有两项,第一项group用来描述组名称,第二个参数function用来描述组功能,这个参数不同的soc厂商对它的定义则不同,像STM32MP157就是用"gpio",“af0”,"af1"等来描述
  • configs类型:
    • PIN_MAP_TYPE_CONFIGS_PIN:对单个引脚进行上下拉、驱动能力等配置
      • 对于configs_pin类型,使用 pinctrl_map_configs configs来描述这个map功能,这个结构体有三项,第一项group_or_pin用来描述单个引脚名称,第二个参数*configs是配置的参数列表,第三个参数num_configs为配置参数的总数。配置参数对于不同的soc厂商对于configs的定义不同
    • PIN_MAP_TYPE_CONFIGS_GROUP:对一组引脚进行上下拉等配置
      • 对于configs_group类型,使用 pinctrl_map_configs configs来描述这个map功能,这个结构体有三项,第一项group_or_pin用来描述组名称,第二个参数*configs是配置的参数列表,第三个参数num_configs为配置参数的总数。配置参数对于不同的soc厂商对于configs的定义不同

struct pinctrl_maps 结构体就是将单个map构建成的链表,解析出来的map最后会通过struct pinctrl_maps链接成map table.

enum pinctrl_map_type {
	PIN_MAP_TYPE_INVALID,
	PIN_MAP_TYPE_DUMMY_STATE,
	PIN_MAP_TYPE_MUX_GROUP,
	PIN_MAP_TYPE_CONFIGS_PIN,
	PIN_MAP_TYPE_CONFIGS_GROUP,
};

struct pinctrl_setting_mux {
	unsigned group;
	unsigned func;
};

struct pinctrl_setting_configs {
	unsigned group_or_pin;
	unsigned long *configs;
	unsigned num_configs;
};

struct pinctrl_setting {
	struct list_head node;
	enum pinctrl_map_type type;
	struct pinctrl_dev *pctldev;
	const char *dev_name;
	union {
		struct pinctrl_setting_mux mux;/* setting类型为mux时使用 */
		struct pinctrl_setting_configs configs;/* setting类型为configs时使用 */
	} data;
};

从struct pinctrl_setting 和struct pinctrl_map相比较,可以看出它很相似,这也是通过前面所说的驱动文件中注册的的struct pinctrl_desc 下的const struct pinmux_ops *pmxops和const struct pinconf_ops *confops中所定义的相关操作获取map table各个map的参数值赋值给setting中相关的参数。最后调用这些操作函数中的相关设置函数使用setting对引脚进行复用功能和上拉,下拉,驱动能力等的配置。

4.STM32MP157 的Pincontroller实现过程

  • 设备树

    pinctrl: pin-controller@50002000 {
    			#address-cells = <1>;
    			#size-cells = <1>;
    			compatible = "st,stm32mp157-pinctrl";
    			ranges = <0 0x50002000 0xa400>;
    			interrupt-parent = <&exti>;
    			st,syscfg = <&exti 0x60 0xff>;
    			hwlocks = <&hsem 0 1>;
    			pins-are-numbered;
    }
    

    驱动文件:位于drivers/pinctrl/stm32/pinctrl-stm32mp157.c

    static const struct stm32_desc_pin stm32mp157_pins[] = {
    	STM32_PIN_PKG(
    		PINCTRL_PIN(0, "PA0"),
    		STM32MP_PKG_AA | STM32MP_PKG_AC | STM32MP_PKG_AB | STM32MP_PKG_AD,
    		STM32_FUNCTION(0, "GPIOA0"),
    		STM32_FUNCTION(2, "TIM2_CH1 TIM2_ETR"),
    		STM32_FUNCTION(3, "TIM5_CH1"),
    		STM32_FUNCTION(4, "TIM8_ETR"),
    		STM32_FUNCTION(5, "TIM15_BKIN"),
    		STM32_FUNCTION(8, "USART2_CTS USART2_NSS"),
    		STM32_FUNCTION(9, "UART4_TX"),
    		STM32_FUNCTION(10, "SDMMC2_CMD"),
    		STM32_FUNCTION(11, "SAI2_SD_B"),
    		STM32_FUNCTION(12, "ETH1_GMII_CRS ETH1_MII_CRS"),
    		STM32_FUNCTION(16, "EVENTOUT"),
    		STM32_FUNCTION(17, "ANALOG")
    	),
    	STM32_PIN_PKG(
    		PINCTRL_PIN(1, "PA1"),
    		STM32MP_PKG_AA | STM32MP_PKG_AC | STM32MP_PKG_AB | STM32MP_PKG_AD,
    		STM32_FUNCTION(0, "GPIOA1"),
    		STM32_FUNCTION(1, "ETH_CLK"),
    		STM32_FUNCTION(2, "TIM2_CH2"),
    		STM32_FUNCTION(3, "TIM5_CH2"),
    		STM32_FUNCTION(4, "LPTIM3_OUT"),
    		STM32_FUNCTION(5, "TIM15_CH1N"),
    		STM32_FUNCTION(8, "USART2_RTS USART2_DE"),
    		STM32_FUNCTION(9, "UART4_RX"),
    		STM32_FUNCTION(10, "QUADSPI_BK1_IO3"),
    		STM32_FUNCTION(11, "SAI2_MCLK_B"),
    		STM32_FUNCTION(12, "ETH1_GMII_RX_CLK ETH1_MII_RX_CLK ETH1_RGMII_RX_CLK ETH1_RMII_REF_CLK"),
    		STM32_FUNCTION(15, "LCD_R2"),
    		STM32_FUNCTION(16, "EVENTOUT"),
    		STM32_FUNCTION(17, "ANALOG")
    	),
        ..........
    };
    
    static const struct stm32_desc_pin stm32mp157_z_pins[] = {
    	STM32_PIN_PKG(
    		PINCTRL_PIN(400, "PZ0"),
    		STM32MP_PKG_AA | STM32MP_PKG_AC,
    		STM32_FUNCTION(0, "GPIOZ0"),
    		STM32_FUNCTION(3, "I2C6_SCL"),
    		STM32_FUNCTION(4, "I2C2_SCL"),
    		STM32_FUNCTION(6, "SPI1_SCK I2S1_CK"),
    		STM32_FUNCTION(8, "USART1_CK"),
    		STM32_FUNCTION(9, "SPI6_SCK"),
    		STM32_FUNCTION(16, "EVENTOUT"),
    		STM32_FUNCTION(17, "ANALOG")
    	),
    	STM32_PIN_PKG(
    		PINCTRL_PIN(401, "PZ1"),
    		STM32MP_PKG_AA | STM32MP_PKG_AC,
    		STM32_FUNCTION(0, "GPIOZ1"),
    		STM32_FUNCTION(3, "I2C6_SDA"),
    		STM32_FUNCTION(4, "I2C2_SDA"),
    		STM32_FUNCTION(5, "I2C5_SDA"),
    		STM32_FUNCTION(6, "SPI1_MISO I2S1_SDI"),
    		STM32_FUNCTION(7, "I2C4_SDA"),
    		STM32_FUNCTION(8, "USART1_RX"),
    		STM32_FUNCTION(9, "SPI6_MISO"),
    		STM32_FUNCTION(16, "EVENTOUT"),
    		STM32_FUNCTION(17, "ANALOG")
    	),
        .........
    };
    
    static struct stm32_pinctrl_match_data stm32mp157_match_data = {
    	.pins = stm32mp157_pins,/* pin描述 */
    	.npins = ARRAY_SIZE(stm32mp157_pins),/* pin的数量 */
    };
    
    static struct stm32_pinctrl_match_data stm32mp157_z_match_data = {
    	.pins = stm32mp157_z_pins,/* pin描述 */
    	.npins = ARRAY_SIZE(stm32mp157_z_pins),/* pin的数量 */
    	.pin_base_shift = STM32MP157_Z_BASE_SHIFT,/* pin number基数 */
    };
    
    static const struct of_device_id stm32mp157_pctrl_match[] = {
    	{
    		.compatible = "st,stm32mp157-pinctrl",
    		.data = &stm32mp157_match_data,
    	},
    	{
    		.compatible = "st,stm32mp157-z-pinctrl",
    		.data = &stm32mp157_z_match_data,
    	},
    	{ }
    };
    
    static const struct dev_pm_ops stm32_pinctrl_dev_pm_ops = {
    	 SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, stm32_pinctrl_resume)
    };
    
    static struct platform_driver stm32mp157_pinctrl_driver = {
    	.probe = stm32_pctl_probe,
    	.driver = {
    		.name = "stm32mp157-pinctrl",
    		.of_match_table = stm32mp157_pctrl_match,
    		.pm = &stm32_pinctrl_dev_pm_ops,
    	},
    };
    
    static int __init stm32mp157_pinctrl_init(void)
    {
    	return platform_driver_register(&stm32mp157_pinctrl_driver);
    }
    arch_initcall(stm32mp157_pinctrl_init);
    
    

    可以看出Pinctrl驱动为一个标准的platform驱动,驱动的probe函数在drivers/pinctrl/stm32/pinctrl-stm32.c文件中

    int stm32_pctl_probe(struct platform_device *pdev)
    {
    	struct device_node *np = pdev->dev.of_node;/* 对应的设备树节点 */
    	struct device_node *child; /* 子节点 */
    	const struct of_device_id *match; /* 设备驱动匹配项 */
    	struct device *dev = &pdev->dev;/* 对应的设备 */
    	struct stm32_pinctrl *pctl;     /* STM32 pinctrl */
    	struct pinctrl_pin_desc *pins;  /* pin脚描述 */
    	int i, ret, hwlock_id, banks = 0;
    
    	if (!np)
    		return -EINVAL;
    
    	match = of_match_device(dev->driver->of_match_table, dev);/*获取设备对应的匹配项*/
    	if (!match || !match->data)
    		return -EINVAL;
    
    	if (!of_find_property(np, "pins-are-numbered", NULL)) {
    		dev_err(dev, "only support pins-are-numbered format\n");
    		return -EINVAL;
    	}
    
    	pctl = devm_kzalloc(dev, sizeof(*pctl), GFP_KERNEL);
    	if (!pctl)
    		return -ENOMEM;
    
    	/* pdev->dev->driver_data= pctl; */
    	platform_set_drvdata(pdev, pctl);
    
    	/* check for IRQ controller (may require deferred probe) */
    	pctl->domain = stm32_pctrl_get_irq_domain(np);
    	if (IS_ERR(pctl->domain))
    		return PTR_ERR(pctl->domain);
    
    	/* hwspinlock is optional */
    	hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
    	if (hwlock_id < 0) {
    		if (hwlock_id == -EPROBE_DEFER)
    			return hwlock_id;
    	} else {
    		pctl->hwlock = hwspin_lock_request_specific(hwlock_id);
    	}
    
    	spin_lock_init(&pctl->irqmux_lock);
    
    	pctl->dev = dev;
    	pctl->match_data = match->data;/* 获取匹配项中的数据 */
    
    	/*  get package information */
    	stm32_pctl_get_package(np, pctl);
    	/* 
    	 * 为pctl->pins分配内存,分配的大小为(pctl->match_data->npins)x(sizeof(*pctl->pins))
    	 * 也就是(引脚个数*每个引脚描述所占用的内存大小)
    	 */
    	pctl->pins = devm_kcalloc(pctl->dev, pctl->match_data->npins,
    				  sizeof(*pctl->pins), GFP_KERNEL);
    	if (!pctl->pins)
    		return -ENOMEM;
     
    	/* 将匹配项match_data中的pins中的pin和functions拷贝到pctl->pins中, pctl->npins=拷贝的总的pins数量*/
    	ret = stm32_pctrl_create_pins_tab(pctl, pctl->pins);
    	if (ret)
    		return ret;
    
    	/*
    	 * 将pctl中的group初始化
    	 *  组名称配置为引脚名称 
    		group->name = pin->pin.name;
    		组的pin为引脚的pin number 
    		group->pin = pin->pin.number;
    		组名称保存到grp_names 
    		pctl->grp_names[i] = pin->pin.name;
    	 */
    	ret = stm32_pctrl_build_state(pdev);
    	if (ret) {
    		dev_err(dev, "build state failed: %d\n", ret);
    		return -EINVAL;
    	}
    
    	if (pctl->domain) {
    		ret = stm32_pctrl_dt_setup_irq(pdev, pctl);
    		if (ret)
    			return ret;
    	}
    
    	pins = devm_kcalloc(&pdev->dev, pctl->npins, sizeof(*pins),
    			    GFP_KERNEL);
    	if (!pins)
    		return -ENOMEM;
    	/* 将pctl->pins拷贝到pins中 */
    	for (i = 0; i < pctl->npins; i++)
    		pins[i] = pctl->pins[i].pin;
    
    	pctl->pctl_desc.name = dev_name(&pdev->dev);
    	pctl->pctl_desc.owner = THIS_MODULE;
        
        /* pins和npins两项就是引脚的枚举(pin 脚编号,命名,和pin脚的复用功能描述) */
    	pctl->pctl_desc.pins = pins;/* 将pins指针放入pctl->pctl_desc.pins */
    	pctl->pctl_desc.npins = pctl->npins;
        
    	pctl->pctl_desc.link_consumers = true;
    	/* stm32_pconf_ops:用来配置引脚特性  如上拉或下拉   */
    	pctl->pctl_desc.confops = &stm32_pconf_ops;
    	/* stm32_pctrl_ops:全局的控制函数 */
    	pctl->pctl_desc.pctlops = &stm32_pctrl_ops;
    	/* stm32_pmx_ops:将引脚复用为其他功能 */
    	pctl->pctl_desc.pmxops = &stm32_pmx_ops;
    	pctl->dev = &pdev->dev;
    	pctl->pin_base_shift = pctl->match_data->pin_base_shift;
    
        /* 注册sruct pinctrl_desc结构体*/
    	pctl->pctl_dev = devm_pinctrl_register(&pdev->dev, &pctl->pctl_desc,
    					       pctl);
    
    	if (IS_ERR(pctl->pctl_dev)) {
    		dev_err(&pdev->dev, "Failed pinctrl registration\n");
    		return PTR_ERR(pctl->pctl_dev);
    	}
    
    	for_each_available_child_of_node(np, child)
    		if (of_property_read_bool(child, "gpio-controller"))
    			banks++;
    
    	if (!banks) {
    		dev_err(dev, "at least one GPIO bank is required\n");
    		return -EINVAL;
    	}
    	pctl->banks = devm_kcalloc(dev, banks, sizeof(*pctl->banks),
    			GFP_KERNEL);
    	if (!pctl->banks)
    		return -ENOMEM;
    
    	i = 0;
    	for_each_available_child_of_node(np, child) {
    		struct stm32_gpio_bank *bank = &pctl->banks[i];
    
    		if (of_property_read_bool(child, "gpio-controller")) {
    			bank->rstc = of_reset_control_get_exclusive(child,
    								    NULL);
    			if (PTR_ERR(bank->rstc) == -EPROBE_DEFER)
    				return -EPROBE_DEFER;
    
    			bank->clk = of_clk_get_by_name(child, NULL);
    			if (IS_ERR(bank->clk)) {
    				if (PTR_ERR(bank->clk) != -EPROBE_DEFER)
    					dev_err(dev,
    						"failed to get clk (%ld)\n",
    						PTR_ERR(bank->clk));
    				return PTR_ERR(bank->clk);
    			}
    			i++;
    		}
    	}
    
    	for_each_available_child_of_node(np, child) {
    		if (of_property_read_bool(child, "gpio-controller")) {
    			ret = stm32_gpiolib_register_bank(pctl, child);
    			if (ret) {
    				of_node_put(child);
    				return ret;
    			}
    
    			pctl->nbanks++;
    		}
    	}
    
    	dev_info(dev, "Pinctrl STM32 initialized\n");
    
    	return 0;
    }
    

    里边所涉及的函数的功能注释了出来,其他的代码参考源码具体分析。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-04 11:22:45  更:2021-08-04 11:24:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 10:10:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计