pinctrl子系统学习笔记

news2024/12/18 18:38:48

一、背景

cpu的gpio引脚可以复用成多个功能,如可以配置成I2C或者普通GPIO模式。配置方式一般是通过写引脚复用的配置寄存器,但是不同芯片厂商配置寄存器格式内容各不相同,设置引脚复用无法做到通用且自由的配置,只能在启动初始化时候在soc驱动初始化时对每个引脚配置好。 linux 3.0之后内核中抽象出了pinctrl子系统,每个soc注册设置引脚复用的方法以及将soc的每个引脚可选的复用功能。

二、pinctrl实现

2.1 API

# 用于soc向pinctrl系统中注册设置引脚复用方法以及设置引脚复用状态
# driver_data用于soc驱动私有数据,一般用来保存pin 引脚的可选复用功能列表。
struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,struct device *dev, void *driver_data)

# 设置引脚复用状态,用来设置引脚复用状态
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)

# 用于具体设备驱动(i2c等)关联pinctrl节点,以及设置默认复用模式,设备树解析时调用
int pinctrl_bind_pins(struct device *dev)

2.2 数据结构

数据结构关系图:

在这里插入图片描述

这里主要有几个对象:

  1. pin_group 和function : 引脚复用一般是多个引脚组成一个group,一起设置复用状态的。多个引脚复用为某个功能,成为function。
  1. pin_conf :设置引脚的上下拉电阻等电气参数
  2. pin_mux:设置引脚复用

pinctrl_desc

核心数据结构是pinctrl_desc,pinctrl的全局描述。

struct pinctrl_desc {
	const char *name;
	/*系统种有多少个pinctrl*/
	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;
};

pinctrl_pin_desc

描述系统中的所有引脚,drv_data保存驱动私有数据。

struct pinctrl_pin_desc {
	unsigned number;
	const char *name;
	void *drv_data;
};

pinctrl_ops

回调函数,获取pin group的操作,以及解析dts。

struct pinctrl_ops {
	/*获取系统中有多少个pin groups*/
	int (*get_groups_count) (struct pinctrl_dev *pctldev);
	/*获取指定group(由索引selector指定)的名称,由select确认*/
	const char *(*get_group_name) (struct pinctrl_dev *pctldev,
				       unsigned selector);
	/*获取指定group的所用pin信息*/
	int (*get_group_pins) (struct pinctrl_dev *pctldev,
			       unsigned selector,
			       const unsigned **pins,
			       unsigned *num_pins);
	void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,
			  unsigned offset);
    /*将对应驱动转换成pin map*/
	int (*dt_node_to_map) (struct pinctrl_dev *pctldev,
			       struct device_node *np_config,
			       struct pinctrl_map **map, unsigned *num_maps);
	void (*dt_free_map) (struct pinctrl_dev *pctldev,
			     struct pinctrl_map *map, unsigned num_maps);
};

pinconf_ops

配置管脚状态:pinctrl也提供管脚状态如(上下拉、开漏等)的接口。

struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
	bool is_generic;
#endif
	/*获取pin脚的当前状态*/
	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的配置项*/
	int (*pin_config_group_get) (struct pinctrl_dev *pctldev,
				     unsigned selector,
				     unsigned long *config);
	int (*pin_config_group_set) (struct pinctrl_dev *pctldev,
				     unsigned selector,
				     unsigned long *configs,
				     unsigned num_configs);
	int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev,
					   const char *arg,
					   unsigned long *config);
	void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev,
				     struct seq_file *s,
				     unsigned offset);
	void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev,
					   struct seq_file *s,
					   unsigned selector);
	void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,
					    struct seq_file *s,
					    unsigned long config);
};

pinmux_ops

设置或获取引脚复用情况,对应硬件是iomux。设置某个pin为某个function,通过set_mux设置function selecter。 一个group设置复用状态。

struct pinmux_ops {
	int (*request) (struct pinctrl_dev *pctldev, unsigned offset);
	int (*free) (struct pinctrl_dev *pctldev, unsigned offset);
	int (*get_functions_count) (struct pinctrl_dev *pctldev);
	const char *(*get_function_name) (struct pinctrl_dev *pctldev,
					  unsigned selector);
	int (*get_function_groups) (struct pinctrl_dev *pctldev,
				  unsigned selector,
				  const char * const **groups,
				  unsigned *num_groups);
	/*将指定的pin group(group_selector)设置为指定的function(func_selector)*/
	int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector,
			unsigned group_selector);
	int (*gpio_request_enable) (struct pinctrl_dev *pctldev,
				    struct pinctrl_gpio_range *range,
				    unsigned offset);
	void (*gpio_disable_free) (struct pinctrl_dev *pctldev,
				   struct pinctrl_gpio_range *range,
				   unsigned offset);
	int (*gpio_set_direction) (struct pinctrl_dev *pctldev,
				   struct pinctrl_gpio_range *range,
				   unsigned offset,
				   bool input);
	bool strict;
};

pinctrl_map

pinctrl_map 某个gpio具体的引脚配置和复用状态,由dts中定义,dt_node_to_map函数解析,和具体设备驱动关联。

struct pinctrl_map {
  //device的名称
 const char *dev_name;
  //pin state的名称
 const char *name;
  //该map的类型
 enum pinctrl_map_type type;
  //pin controller device的名字
 const char *ctrl_dev_name;
 union {
  struct pinctrl_map_mux mux;
  struct pinctrl_map_configs configs;
 } data;
};

enum pinctrl_map_type {
 PIN_MAP_TYPE_INVALID,
 //不需要任何配置,仅仅为了表示state的存在
 PIN_MAP_TYPE_DUMMY_STATE,
 //配置管脚复用
 PIN_MAP_TYPE_MUX_GROUP,
 //配置pin
 PIN_MAP_TYPE_CONFIGS_PIN,
 //配置pin group
 PIN_MAP_TYPE_CONFIGS_GROUP,
};

struct pinctrl_map_mux {
 //group的名字
 const char *group;
 //function的名字
 const char *function;
};

struct pinctrl_map_configs {
 //该pin或者pin group的名字
 const char *group_or_pin;
 //configuration数组
 unsigned long *configs;
 //配置项的个数
 unsigned num_configs;
};

三、实现流程

linux6.1.11为例,pinctrl系统融入内核设备树解析以及内核设备驱动模型中支持。以zynq为例从dts配置解析入手,了解pinctrl的工作原理。dts中主要关注:

1)pinctrl 子系统:描述系统多少引脚,每个引脚有多少复用情况。

2)I2C、GPIO、SPI等driver默认配置的pinctrl复用设置

《zynq-7000.dtsi》中,如列出i2C和uart的复用配置,其中只是选择了i2c0_10_grp这个pin_group,并且配置为i2c0的复用功能,具体引脚的引脚号等信息是在代码中定义的。

mux 和 conf的作用?

pinctrl0: pinctrl@700 {
                                compatible = "xlnx,pinctrl-zynq";
                                reg = <0x700 0x200>;
                                syscon = <&slcr>;
	/*设置引脚的复用状态*/
    pinctrl_i2c0_default: i2c0-default {
                    mux {
                    		/*pin_group*/
                            groups = "i2c0_10_grp";
                            /*复用功能为i2c0*/
                            function = "i2c0";
                    };

                    conf {
                            groups = "i2c0_10_grp";
                            bias-pull-up; #上拉电阻
                            slew-rate = <0>; #引脚转换速率
                            io-standard = <1>;
                    };
            };
     pinctrl_uart1_default: uart1-default {
                mux {
                        groups = "uart1_10_grp";
                        function = "uart1";
                };

                conf {
                        groups = "uart1_10_grp";
                        slew-rate = <0>;
                        io-standard = <1>;
                };

                conf-rx {
                        pins = "MIO49";
                        bias-high-impedance;
                };

                conf-tx {
                        pins = "MIO48";
                        bias-disable;
                };
        };

}
  1. 其他驱动如何关联pinctrl以及配置默认引脚复用?

对应驱动中的dts新增如下两个属性,在设备驱动device关联driver时,会解析如下字段,关联pinctrl驱动,并设置引脚复用状态,这里是选择pinctrl_i2c0_default。

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

3.1 驱动注册

《linux-6.1.11\drivers\pinctrl\pinctrl-zynq.c》

构造一个pinctrl_desc结构,然后注册到pinctrl系统中。

static struct pinctrl_desc zynq_desc = {
	.name = "zynq_pinctrl",
	.pins = zynq_pins,
	.npins = ARRAY_SIZE(zynq_pins),
	.pctlops = &zynq_pctrl_ops,
	.pmxops = &zynq_pinmux_ops,
	.confops = &zynq_pinconf_ops,
	.num_custom_params = ARRAY_SIZE(zynq_dt_params),
	.custom_params = zynq_dt_params,
#ifdef CONFIG_DEBUG_FS
	.custom_conf_items = zynq_conf_items,
#endif
	.owner = THIS_MODULE,
};

static int zynq_pinctrl_probe(struct platform_device *pdev)

{
	pctrl->pctrl = devm_pinctrl_register(&pdev->dev, &zynq_desc, pctrl);
}

pin引脚定义:

/*定义系统中引脚*/
static const struct pinctrl_pin_desc zynq_pins[] = {
	PINCTRL_PIN(0,  "MIO0"),
	PINCTRL_PIN(1,  "MIO1"),
	...
}

/*引脚可选复用功能*/
enum zynq_pinmux_functions {
	ZYNQ_PMUX_can0,
	ZYNQ_PMUX_can1,
	ZYNQ_PMUX_ethernet0,
	ZYNQ_PMUX_ethernet1,
}

/* pin groups 组,每个引脚功能对应的pin脚定义*/
static const unsigned int ethernet0_0_pins[] = {16, 17, 18, 19, 20, 21, 22, 23,
						24, 25, 26, 27};
static const unsigned int ethernet1_0_pins[] = {28, 29, 30, 31, 32, 33, 34, 35,
						36, 37, 38, 39};
static const unsigned int mdio0_0_pins[] = {52, 53};
static const unsigned int mdio1_0_pins[] = {52, 53};

/*zynq维护的私有数据结构pin_group组信息,关联上述pin脚*/
static const struct zynq_pctrl_group zynq_pctrl_groups[] = {
	DEFINE_ZYNQ_PINCTRL_GRP(ethernet0_0),
	DEFINE_ZYNQ_PINCTRL_GRP(ethernet1_0),
	DEFINE_ZYNQ_PINCTRL_GRP(mdio0_0),
	DEFINE_ZYNQ_PINCTRL_GRP(mdio1_0),
	DEFINE_ZYNQ_PINCTRL_GRP(qspi0_0),
	DEFINE_ZYNQ_PINCTRL_GRP(qspi1_0),
	DEFINE_ZYNQ_PINCTRL_GRP(qspi_fbclk),
	DEFINE_ZYNQ_PINCTRL_GRP(qspi_cs1),
	DEFINE_ZYNQ_PINCTRL_GRP(spi0_0),
}

对应的ops实现这里不展开,具体写寄存器信息。

3.2 具体驱动关联pinctrl

具体i2c、spi等驱动初始化时如何设置引脚复用情况? dts中定义i2c选择pinctrl_i2c0_default的引脚复用。 pinctrl-names用于表示pinctrl_state,有init、default,init是在驱动初始化的状态,default是默认状态。 pinctrl-0 选择复用情况,这两个属性是pinctrl的标准属性,设备树解析框架中实现,在设备驱动匹配时关联。

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

时机:设备驱动匹配时关联,以及设置默认复用状态

关键函数:pinctrl_bind_pins

《linux-6.1.11\drivers\base\dd.c》

__driver_probe_device -> really_probe -> pinctrl_bind_pins
  • 解析dts,关联pinctrl_map

调用pinconf_generic_dt_node_to_map_all解析dts。关联pinctrl_i2c0_default的pinctrl_map。

pinctrl_bind_pins -> create_pinctrl -> pinctrl_dt_to_map -> pinconf_generic_dt_node_to_map_all 
  • 设置引脚复用
pinctrl_bind_pins -> pinctrl_select_state (选择default state) -> pinctrl_commit_state
  -> pinmux_disable_setting  (禁用旧的复用)
  -> pinmux_enable_setting  (设置新复用)
  -> pinconf_apply_setting  (设置引脚状态)
/**
 * pinctrl_bind_pins() - called by the device core before probe
 * @dev: the device that is just about to probe
 */
int pinctrl_bind_pins(struct device *dev)
{
	int ret;

	if (dev->of_node_reused)
		return 0;

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

	dev->pins->p = devm_pinctrl_get(dev);
	if (IS_ERR(dev->pins->p)) {
		dev_dbg(dev, "no pinctrl handle\n");
		ret = PTR_ERR(dev->pins->p);
		goto cleanup_alloc;
	}

	dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_DEFAULT);
	if (IS_ERR(dev->pins->default_state)) {
		dev_dbg(dev, "no default pinctrl state\n");
		ret = 0;
		goto cleanup_get;
	}

	dev->pins->init_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_INIT);
	if (IS_ERR(dev->pins->init_state)) {
		/* Not supplying this state is perfectly legal */
		dev_dbg(dev, "no init pinctrl state\n");

		ret = pinctrl_select_state(dev->pins->p,
					   dev->pins->default_state);
	} else {
		ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state);
	}

	if (ret) {
		dev_dbg(dev, "failed to activate initial pinctrl state\n");
		goto cleanup_get;
	}

#ifdef CONFIG_PM
	/*
	 * If power management is enabled, we also look for the optional
	 * sleep and idle pin states, with semantics as defined in
	 * <linux/pinctrl/pinctrl-state.h>
	 */
	dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_SLEEP);
	if (IS_ERR(dev->pins->sleep_state))
		/* Not supplying this state is perfectly legal */
		dev_dbg(dev, "no sleep pinctrl state\n");

	dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_IDLE);
	if (IS_ERR(dev->pins->idle_state))
		/* Not supplying this state is perfectly legal */
		dev_dbg(dev, "no idle pinctrl state\n");
#endif

	return 0;

	/*
	 * If no pinctrl handle or default state was found for this device,
	 * let's explicitly free the pin container in the device, there is
	 * no point in keeping it around.
	 */
cleanup_get:
	devm_pinctrl_put(dev->pins->p);
cleanup_alloc:
	devm_kfree(dev, dev->pins);
	dev->pins = NULL;

	/* Return deferrals */
	if (ret == -EPROBE_DEFER)
		return ret;
	/* Return serious errors */
	if (ret == -EINVAL)
		return ret;
	/* We ignore errors like -ENOENT meaning no pinctrl state */

	return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2261734.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

免费开源了一个图床工具 github-spring-boot-starter

文章目录 第一步&#xff0c;新建一个SpringBoot项目第二步&#xff0c;在pom文件里面引入jar包第三步&#xff0c;配置你的github信息github.authorization1、进入github官网&#xff0c;登录账号&#xff0c;点击头像&#xff0c;选择setting2、选择[Developer Settings](htt…

JVM系列之内存区域

每日禅语 有一位年轻和尚&#xff0c;一心求道&#xff0c;多年苦修参禅&#xff0c;但一直没有开悟。有一天&#xff0c;他打听到深山中有一古寺&#xff0c;住持和尚修炼圆通&#xff0c;是得道高僧。于是&#xff0c;年轻和尚打点行装&#xff0c;跋山涉水&#xff0c;千辛万…

自动驾驶AVM环视算法--python版本的俯视碗型投影模式

c语言版本和算法原理的可以查看本人的其他文档。《自动驾驶AVM环视算法--3D碗型投影模式的exe测试工具》本文档进用于展示部分代码的视线&#xff0c;获取方式网盘自行获取&#xff08;非免费介意勿下载&#xff09;&#xff1a;链接: https://pan.baidu.com/s/1STjUd87_5wDk_C…

【并发容器】源码级ConcurrentHashMap详解(java78)

1. ConcurrentHashMap 为什么要使用ConcurrentHashmap 在多线程的情况下&#xff0c;使用HashMap是线程不安全的。另外可以使用Hashtable&#xff0c;其是线程安全的&#xff0c;但是Hashtable的运行效率很低&#xff0c;之所以效率低下主要是因为其实现使用了synchronized关…

程序设计考题汇总(四:SQL练习)

文章目录 查询结果限制返回行数 查询结果限制返回行数 select device_id from user_profile LIMIT 2;

Alan Chhabra:MongoDB AI应用程序计划(MAAP) 为客户提供价值

MongoDB全球合作伙伴执行副总裁 Alan Chhabra 每当有人向我问询MongoDB&#xff0c;我都会说他们很可能在不觉之间已经与MongoDB有过交集。事实上&#xff0c;包括70%财富百强在内的许多世界领先企业公司都在使用MongoDB。我们在MongoDB所做的一切都是为了服务客户&#xff0c…

centos使用mkisofs构建无人值守镜像(附官方学习文档)

安装mkisofs yum install -y mkisofs 挂载镜像并确认 并拷贝文件(/mnt 为我们的工作目录) 1.3 准备自动应答文件(保存为 ins.ks) 修改系统引导 实际上就是添加inst.ks 这个引导参数 传递应答文件 传统模式引导

jenkins pipeline打包流程

Jenkins Pipeline 是 Jenkins 提供的一种用于持续集成和持续交付&#xff08;CI/CD&#xff09;的脚本化流程工具。它允许你通过编写一个 Jenkinsfile 文件来定义整个构建、测试和部署的流程。本文介绍打包springcloud项目&#xff0c;react项目为docker镜像 文章目录 1.项目结…

【容器】k8s学习笔记原理详解(十万字超详细)

Pod详解 Pod介绍 Pod结构 每个Pod中都可以包含一个或者多个容器&#xff0c;这些容器可以分为两类&#xff1a; 用户程序所在的容器&#xff0c;数量可多可少Pause容器&#xff0c;这是每个Pod都会有的一个根容器&#xff0c;它的作用有两个&#xff1a; 可以以它为依据&am…

用.Net Core框架创建一个Web API接口服务器

我们选择一个Web Api类型的项目创建一个解决方案为解决方案取一个名称我们这里选择的是。Net 8.0框架 注意&#xff0c;需要勾选的项。 我们找到appsetting.json配置文件 appsettings.json配置文件内容如下 {"Logging": {"LogLevel": {"Default&quo…

多音轨视频使用FFmpeg删除不要音轨方法

近期给孩子找宫崎骏动画&#xff0c;但是有很多是多音轨视频但是默认的都是日语&#xff0c;电视上看没办法所以只能下载后删除音轨文件只保留中文。 方法分两步&#xff0c;先安装FFmpeg在转文件即可。 第一步FFmpeg安装 FFmpeg是一个开源项目&#xff0c;包含了处理视频的…

Ubuntu22.04切换gcc版本教程

在编译安装程序的时候,由于gcc版本过高,导致编译无法通过,需要降低gcc版本。 一、安装gcc版本 根据自己的需求安装gcc版本。 sudo apt update sudo apt install gcc-10 g++-10二、切换gcc版本 sudo update-alternatives --install /usr/bin/gcc gcc

[SZ901] JTAG合并功能(类似FPGA菊花链)

SZ901 JTAG支持将JTAG端口组合&#xff0c;最多将四个JTAG变成一个 设置如下 Vivado 识别结果如下 两块板子&#xff0c;变成一组&#xff0c;&#xff0c;可以同时抓取信号&#xff0c;调试&#xff01; SZ901 已上架淘宝&#xff0c;搜素“SZ901”哦

【收藏】Cesium 限制相机倾斜角(pitch)滑动范围

1.效果 2.思路 在项目开发的时候&#xff0c;有一个需求是限制相机倾斜角&#xff0c;也就是鼠标中键调整视图俯角时&#xff0c;不能过大&#xff0c;一般 pitch 角度范围在 0 至 -90之间&#xff0c;-90刚好为正俯视。 在网上查阅了很多资料&#xff0c;发现并没有一个合适的…

如何解决samba服务器共享文件夹不能粘贴文件

sudo vim /etc/samba/smb.conf在samba的配置文件中增加一个选项 writable yes重启Samba服务以使更改生效&#xff1a; sudo service smbd restart

数据结构简介:结构创造效率

一、数据结构的本质 数据结构是组织大量数据的方法&#xff08;data structures&#xff1a;methods of organizing large amounts of data &#xff09;。 根据这个定义&#xff0c;一条数据&#xff0c;就不能构成数据结构。 因为结构是数据与数据之间的关联&#xff0c;…

【经验分享】容器云运维的知识点

最近忙于备考没关注&#xff0c;有次点进某小黄鱼发现首页出现了我的笔记还被人收费了 虽然我也卖了一些资源&#xff0c;但我以交流、交换为主&#xff0c;笔记都是免费给别人看的 由于当时刚刚接触写的并不成熟&#xff0c;为了避免更多人花没必要的钱&#xff0c;所以决定公…

Linux系列之如何更换Centos yum源?

环境 Centos7Xshell7 问题描述 最近安装了一个虚拟机&#xff0c;准备用来学习&#xff0c;不过使用yum命令安装一些软件&#xff0c;不过使用这个命令时候&#xff0c;提示 Cannot find a valid baseurl for repo: base/7/x86_64&#xff0c;Could not retrieve mirrorlis…

RabbitMQ如何构建集群?

大家好&#xff0c;我是锋哥。今天分享关于【RabbitMQ如何构建集群&#xff1f;】面试题。希望对大家有帮助&#xff1b; RabbitMQ如何构建集群&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在RabbitMQ中&#xff0c;集群&#xff08;Cluster&#x…

负载均衡oj项目:介绍

目录 项目介绍 项目演示 项目介绍 负载均衡oj是一个基于bs模式的项目。 用户使用浏览器向oj模块提交代码&#xff0c;oj模块会在所有在线的后端主机中选择一个负载情况最低的主机&#xff0c;将用户的代码提交给该主机&#xff0c;该主机进行编译运行&#xff0c;将结果返回…