pinctrl子系统分析(一)

pinctrl子系统分析(一)
pinctrl子系统分析(二)
pinctrl子系统分析(三)

pinctrl子系统的软件架构

许多SoC的内部都包含了pin控制器,通过pin控制器,我们可以匹配引脚的状态和功能特性。在了解pinctrl子系统之前,我们先来了解一些基本的概念。

SoC的很多引脚都可以配置成不同的功能,如A1和A2两个引脚,既可以当作普通的gpio使用,又可以配置为i2c的SCL和SDA,也可以配置为其他功能等等,这称作引脚的复用(简称pinmux)。

除此之外,还可以配置引脚的电气属性,如上拉/下拉、输入/输出等,这称作引脚的配置(简称pinconf)。

有时需要将多个引脚组合在一起,以实现特定的功能,如spi接口、i2c接口等,需要以group为单位,访问、控制多个pin,这就是pin groups。引脚复用的功能,称作pin function。

一些SoC为了设计灵活,同一个功能,可以引出多个pin groups,如在芯片内部的i2c0的功能可能引出到pin groups {pinA, pinB},也可能引出到另外一个pin groups {pinC, pinD},但毫无疑问,这两个pin group不能同时active,毕竟芯片内部的i2c0的硬件逻辑只有一个,也就是说一个pin function可以对应多个pin groups。
在这里插入图片描述
pinctrl子系统,正是通过pin groups和pin function这两个概念,来管理SoC的引脚复用,pin function及pin groups一经确定,引脚的复用功能也就确认了。

pinctrl子系统的软件架构,如下图。
在这里插入图片描述
最底层是pin控制器,pin控制器驱动提供了引脚复用、配置的功能。pin control核心是一个硬件无关模块,提供了pin控制器驱动的注册、注销方法,从用户角度给出了操作pin的接口,这样,各个driver不需要关注pin控制器的底层硬件相关的内容。

pinctrl相关概念

上层的driver调用pin control核心接口的主要目标有两个:

(1) 设置设备的引脚功能复用

(2) 设置设备的引脚电气特性

此外,由于电源管理的要求,某个device可能处于某个电源管理状态,例如idle或者sleep,这时候,属于device的所有pin就会需要处于另外的状态。

综合上述的需求,就定义了pin control state的概念,也就是说设备可能处于非常多的状态中的一个,设备驱动可以切换设备处于的状态。为了方便管理pin control state就有了pin control state holder的概念,用来管理一个设备的所有的pin control state。内核用struct pinctrl_state数据结构抽象pin control state。

设备的pin control state在设备节点里描述,如下图例子:
在这里插入图片描述
pinctrl-names里定义了2种状态:default、sleep。

第0种状态的引脚配置信息在pinctrl-0中定义,第1种状态的引脚配置信息在pinctrl-1中定义,以此类推。每种状态的引脚配置由一个或多个设备节点来描述,这些设备节点里面描述的配置信息没有统一标准,每家芯片都不太一样,但都要位于pin控制器的设备节点之下。

上层的driver可以调用以下函数来设置不同状态下的设备引脚配置:

//设置默认状态下的引脚配置
static inline struct pinctrl * __must_check pinctrl_get_select_default(struct device *dev);

//根据name选择某种状态的引脚配置
static inline struct pinctrl * __must_check pinctrl_get_select(
					struct device *dev, const char *name);

驱动程序可以不用调用pinctrl_get_select_default函数,因为在device与driver的match之后,调用driver的probe之前,会调用pinctrl_bind_pins函数设置默认状态下设备的引脚设置:

static int really_probe(struct device *dev, struct device_driver *drv)
{
	......

	/* 设置设备引脚状态 */
	ret = pinctrl_bind_pins(dev);
	
	......
	if (dev->bus->probe) {
		ret = dev->bus->probe(dev);
		......
	} else if (drv->probe) {
		ret = drv->probe(dev);
		......
	}
	......
}

主要的数据结构

pinctrl_dev结构体,描述一个pin控制器。一个pin控制器,并不都是可以配置芯片上所有的引脚,有些芯片含有多个pin控制器,那么系统上会注册多个pinctrl_dev,注册后会插入到全局链表pinctrldev_list里,便于查找。

pinctrl_dev结构体定义如下:

struct pinctrl_dev {

	//节点,用于链入pinctrldev_list
	struct list_head node;

	//用于描述pin控制器的相关信息
	struct pinctrl_desc *desc;

	//radix tree根节点,使用radix tree来保存注册进内核的pin信息
	struct radix_tree_root pin_desc_tree;

	......

	struct device *dev;

	struct module *owner;

	void *driver_data;

	//该设备的pin control state holder,用来管理该设备的pin control state
	struct pinctrl *p;

	//默认状态下的pin control state
	struct pinctrl_state *hog_default;

	//sleep状态下的pin control state
	struct pinctrl_state *hog_sleep;

	......

};


pinctrl_dev结构体有一成员desc,数据类型为struct pinctrl_desc,描述了该pin控制器名字、可配置引脚个数及引脚的pinctrl_pin_desc,还有引脚复用的设置方法、引脚配置的设置方法等信息,定义如下:

struct pinctrl_desc {
	//控制器的name
	const char *name;

	//pinctrl_pin_desc数组,对应每个引脚的pinctrl_pin_desc 
	const struct pinctrl_pin_desc *pins;

	//引脚数目
	unsigned int npins;

	//pin控制器的通用方法
	const struct pinctrl_ops *pctlops;

	//引脚复用的设置方法
	const struct pinmux_ops *pmxops;

	//引脚配置的设置方法
	const struct pinconf_ops *confops;

	......

};

注册pin控制器,需要提供一个pinctrl_desc结构体。

pinctrl_pin_desc结构体,描述由硬件平台提供的一个引脚信息,定义如下:

struct pinctrl_pin_desc {
	//pin号
	unsigned number;

	//引脚的名字
	const char *name;

	void *drv_data;
};

硬件平台提供的一个引脚信息会注册进内核,内核使用pin_desc数据结构描述一个引脚信息,定义如下:

struct pin_desc {

	//引脚所属的pin控制器
	struct pinctrl_dev *pctldev;

	//引脚name
	const char *name;

	bool dynamic_name;
	void *drv_data;

	......

};

pinctrl_ops结构体,描述pin控制器的通用方法,定义如下:

struct pinctrl_ops {
	
	//获取pin控制器包含的pin groups的个数
	int (*get_groups_count) (struct pinctrl_dev *pctldev);

	//获取指定pin groups的name,通过selector
	const char *(*get_group_name) (struct pinctrl_dev *pctldev,
				       unsigned selector);

	//获取指定pin groups的所有pin,通过selector
	int (*get_group_pins) (struct pinctrl_dev *pctldev,
			       unsigned selector,
			       const unsigned **pins,
			       unsigned *num_pins);
	......
	//用于解析配置节点,将解析得到的信息保存在pinctrl_map 
	int (*dt_node_to_map) (struct pinctrl_dev *pctldev,
			       struct device_node *np_config,
			       struct pinctrl_map **map, unsigned *num_maps);

	//用于释放pinctrl_map 
	void (*dt_free_map) (struct pinctrl_dev *pctldev,
			     struct pinctrl_map *map, unsigned num_maps);
};

pinmux_ops 结构体,描述引脚复用的方法,定义如下:

struct pinmux_ops {
	
	......
	int (*request) (struct pinctrl_dev *pctldev, unsigned offset);
	int (*free) (struct pinctrl_dev *pctldev, unsigned offset);
	
	//获取pin控制器中function的个数
	int (*get_functions_count) (struct pinctrl_dev *pctldev);
	
  	//获取指定function的名称
	const char *(*get_function_name) (struct pinctrl_dev *pctldev,
					  unsigned selector);

	//获取指定function所占用的pin group
	int (*get_function_groups) (struct pinctrl_dev *pctldev,
				  unsigned selector,
				  const char * const **groups,
				  unsigned *num_groups);

	//将指定的pin group(group_selector)设置为指定的function
	int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector,
			unsigned group_selector);

	......
};

pinconf_ops结构体,描述引脚配置的方法,定义如下:

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

	//设置指定pin group的配置
	int (*pin_config_group_set) (struct pinctrl_dev *pctldev,
				     unsigned selector,
				     unsigned long *configs,
				     unsigned num_configs);
	......
};

pinctrl结构体,pin control state holder,也可以称为管理者,用于管理一个设备不同状态下的引脚配置,每个设备有一个,系统中的pinctrl会插入到全局链表pinctrl_list里。

struct pinctrl {

	//节点,用于插入pinctrl_list链表
	struct list_head node;
	
	//指向所属设备
	struct device *dev;

	//链接设备所有的pin control state 
	struct list_head states;
	
	//指向设备当前pin control state
	struct pinctrl_state *state;

	//解析该设备的配置节点得到配置信息会以链表的形式,插入到这个节点里
	struct list_head dt_maps;

	......
};

pinctrl_state结构体,描述一个pin control state。

struct pinctrl_state {

	//节点,用于链入pinctrl的states链表
	struct list_head node;
	
	//状态的name
	const char *name;
	
	//链接属于该pin control state的所有的pinctrl_setting
	struct list_head settings;
};
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
设备树(device tree)机制是Linux内核从linux-3.x版本开始引进的一种机制,目的是解决内核源码的arch/arm目录下代码混乱的问题:随着ARM生态的快速发展,在内核源码的arch/arm目录下,存放着几十种arm芯片和几百个开发板相关的源文件,很多开发板和处理器的中断、寄存器等相关硬件资源都在这个目录下以.c或.h的文件格式定义。而对于内核来说,与这些硬件耦合,会导致内核代码混乱不堪,每个开发板上运行的内核镜像都必须单独编译配置,无法通用。什么时候Linux内核能像Windows镜像那样,无论你的电脑什么配置,一个Windows安装包,都可以直接下载安装运行呢?设备树机制,实现了Linux内核和硬件平台的解耦:每个硬件平台的硬件资源使用一个设备树文件(xxx.dts)来描述,而不是在arch/arm下以.c 或 .h 文件来定义。Linux内核是一个通用的内核,在启动过程中,在通过解析设备树中的硬件资源来初始化某个具体的平台。 引入设备树后,很多和内核驱动开发的工作也发生了变化:以往驱动工程师关注的头文件宏定义、寄存器定义,现在这些基本上不用关注,关注的重点则转向了如何根据硬件平台去配置和修改设备树文件。很多驱动的编程接口也发生了变化,开始慢慢使用device tree提供的编程接口去开发驱动。本期课程主要面向嵌入式开发人员,分享Linux下驱动开发所需要的设备树知识和必备技能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值