Pinctrl子系统之一了解基础概念【转】
转自:https://blog.csdn.net/u012830148/article/details/80609337
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u012830148/article/details/80609337
1.Linux Pinctrl子系统简介
在许多soc内部都包含有pin控制器,通过pin控制器的寄存器,我们可以配置一个或者一组引脚的功能和特性。在软件方面,Linux内核提供了pinctrl子系统,目的是为了统一各soc厂商的pin脚管理。
2.Linux Pinctrl子系统提供的功能
(1)管理系统中所有的可以控制的pin,在系统初始化的时候,枚举所有可以控制的pin,并标识这些pin。
(2)管理这些pin的复用(Multiplexing)。对于SOC而言,其引脚除了配置成普通的GPIO之外,若干个引脚还可以组成一个pin group,行程特定的功能。pin control subsystem要管理所有的pin group。
(3)配置这些pin的特性。例如使能或关闭引脚上的pull-up、pull-down电阻,配置引脚的driver strength。
3.pinctrl相关概念
普通driver调用pin control subsystem的主要目标有两个:
(1)设定该设备的功能复用。
(2)设定该device对应的pin的电气特性。
设定设备的功能复用需要了解两个概念,一个是function,另外一个是pin group。function是功能抽象,对应一个HW逻辑block,例如SPI0.虽然给定了具体的function name,我们并不能确定其使用的pins的情况。例如为了设计灵活,芯片内部的SPI0的功能引出到pin group{C6,C7,C8,C9},也可能引出的另外一个pin group{C22.C23,C24,C25},但毫无疑问,这两个pin group不能同时active,毕竟芯片内部的SPI0的逻辑功能电路只有一个,因此只有给出function selector以及function的pin group selector才能进行function mux 的设定。
此外,由于电源管理的要求,某个device可能处于某个电源管理状态,例如idle或者sleep,这时候,属于device的所有pin就会需要处于另外的状态。
综合上述的需求,就定义了pin control state的概念,也就是说设备可能处于非常多的状态中的一个,device driver可以切换设备处于的状态。为了方便管理pin control state 就有了pin control state holder的概念,用来管理一个设备的所有的pin control状态。
综上所述,普通的driver调用pin control subsystem 的接口就是只有三个步骤:
(1)驱动加载或是运行时,获取pin control state holder的句柄
(2)设定pin control的状态
(3)驱动卸载或是退出时,释放pin control state holder的句柄
4.与GPIO子系统的关系
上图显示了gpio子系统和pinctrl子系统之间的关系,即pinctrl子系统实际上也把gpio一起管理起来,所有的gpio操作也需要透过pinctrl子系统来完成,这样,如果一个pin已经被申请为gpio,再通过pinctrl子系统申请为某个function时就会返回错误。
5.与统一设备驱动模型的关系
从"pinctrl子系统关系图"中得知,linux kernel中的统一设备驱动模型也调用了pinctrl子系统的功能。linux kernel中的统一设备驱动模型提供了driver 和device的绑定机制,一旦匹配就会调用driver 的probe函数。
而在调用票probe函数之前,驱动模型实际上已经申请过一次pin了,(前提是dts文件中该驱动节点中有定义名为“default”的pinctrl配置)。
驱动模型中调用driver的probe函数的地方:
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
......
/* If using pinctrl, bind pins now before probing */
ret = pinctrl_bind_pins(dev); //对该device涉及的pin进行pin control相关设定
if (ret)
goto probe_failed;
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
if (dev->bus->probe) { //下面是真正的probe过程。
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
.....
}
pinctrl_bind_pins函数定义:
#define PINCTRL_STATE_DEFAULT "default"
#define PINCTRL_STATE_IDLE "idle"
#define PINCTRL_STATE_SLEEP "sleep"
int pinctrl_bind_pins(struct device *dev)
{
int ret;
dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
if (!dev->pins)
return -ENOMEM;
dev->pins->p = devm_pinctrl_get(dev); //获取pinctrl
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, //查找这个pin的default状态
PINCTRL_STATE_DEFAULT);
if (IS_ERR(dev->pins->default_state)) {
dev_dbg(dev, "no default pinctrl state\n");
ret = 0;
goto cleanup_get;
}
ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state); //设置pin的default状态
if (ret) {
dev_dbg(dev, "failed to activate default pinctrl state\n");
goto cleanup_get;
}
return 0;
从驱动模型的实现中,不难看出在驱动probe前,就已经申请到default的pin配置了,当然pinctrl的计数已经+1了。
Driver的probe函数可以通过devm_pinctrl_get获得pinctrl的句柄,再自行调用pinctrl_select_state设置pin state。
6.与device tree的关系
在device tree source 文件中可以在驱动节点中定义该驱动需要用到的pin配置。
device-node-name{
//定义该device自己的属性
pinctrl-names= "sleep","default","idle";
pinctrl-0 = &xxx_State_sleep;
pinctrl-1 = &xxx_state_default;
pinctrl-2 = &xxx_state_idle;
}
pinctrl-0 pinctrl-1 pinctrl-2 .....表示了该设备的一个个状态,这里我们定义了三个pinctrl-0 pinctrl-1 pinctrl-2,数字0、1、2就是pinctrl-names中对应的字符串数组的index。其中pinctrl-0就是“sleep”状态,pinctrl-1就是“default”状态,pinctrl-2就是“idle”状态。而xxx_state_sleep,xxx_state_default,xxx_state_idel就是驱动具体的pin配置项了,需要在pinctrl设备节点处定义:
pinctrl@e01b0000{
pinctrl-names = "default";
pinctrl-0 = <&state_default>;
state_default:pinctrl_default{
};
xxx_state_sleep:xxx_sleep{
xxx_mfp{
actions,groups = "fmp1_3_1spi0_ss","mfp1_3_1_spi0_miso","mfp1_5_4";
actions,function = "spi0";
};
};
Pinctrl子系统在加载时,会调用pinctrl_dt_to_map函数将dts文件中有关pinctrl的配置项解析出来,并根据dts各驱动节点对pinctrl的引用关系,将phandle挂到各个驱动的device tree子节点,各个驱动就可以通过自己的dev句柄获得pinctrl的配置了。
7.与主控驱动的关系
在kernel的machine driver中,需要将主控的pinctrl相关硬件操作形象成一个符合linux pinctrl子系统规范的结构pinctrl_desc,并调用pinctrl_register注册到pinctrl子系统中,这样pinctrl子系统就可以将上层行为转换成具体的硬件操作了:
struct pinctrl_desc {
const char *name;
struct pinctrl_pin_desc const *pins; //描述每个pin的信息为何。
unsigned int npins; //表示可以控制多少个pin。pins和npins这两个成员就确定了一个pin controller所能控制的引脚信息。
const struct pinctrl_ops *pctlops;//全局的控制函数
const struct pinmux_ops *pmxops;//复用引脚的相关的操作函数
const struct pinconf_ops *confops; //用来配置引脚特性(如pull-up/pull-down)
struct module *owner;
};
pctlops成员callback函数说明:
struct pinctrl_ops {
int (*get_groups_count) (struct pinctrl_dev *pctldev); //该pin controller支持多少个pin group
const char *(*get_group_name) (struct pinctrl_dev *pctldev,//给定一个selector(index),获取指定的pin group的name
unsigned selector);
int (*get_group_pins) (struct pinctrl_dev *pctldev, //给定一个selector(index)获取指定的pin group中pin的信息(该pin group
unsigned selector,, //包括多少个pin,每个pin的ID是什么)
const unsigned **pins,
unsigned *num_pins);
void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,//debug fs的callback接口
unsigned offset);
int (*dt_node_to_map) (struct pinctrl_dev *pctldev, //分析一个pin configuration node并把分析结果保存成mapping table
struct device_node *np_config, //entry 每一个entry表示一个setting(一个功能复用设定,或者电气特性设定)
struct pinctrl_map **map, unsigned *num_maps);
void (*dt_free_map) (struct pinctrl_dev *pctldev, //dt_node_to_map的逆函数。
struct pinctrl_map *map, unsigned num_maps);
};
pmxops成员callback函数说明:
struct pinmux_ops {
int (*request) (struct pinctrl_dev *pctldev, unsigned offset);//pinctrl子系统进行具体的复用设定之前需要调用该函数,主要用来请底层的driver判断某个引脚的复用设定是否ok
int (*free) (struct pinctrl_dev *pctldev, unsigned offset);//request的逆函数,调用request函数请求占用了某些pin的资源,调用free可以释放这些资源。
int (*get_functions_count) (struct pinctrl_dev *pctldev);//返回pin controller支持的function的数目。
const char *(*get_function_name) (struct pinctrl_dev *pctldev,//给定一个selector(index)获取指定function的name。
unsigned selector);
int (*get_function_groups) (struct pinctrl_dev *pctldev,//给定一个selector(index)获取指定的function 的pin group信息。
unsigned selector,
const char * const **groups,
unsigned * const num_groups);
int (*enable) (struct pinctrl_dev *pctldev, unsigned func_selector, //enable一个function当然要给我出function selector和pin group的selector
unsigned group_selector);
void (*disable) (struct pinctrl_dev *pctldev, unsigned func_selector,//enable的逆函数
unsigned group_selector);
int (*gpio_request_enable) (struct pinctrl_dev *pctldev,//request并且enable一个单独的gpio pin
struct pinctrl_gpio_range *range,
unsigned offset);
void (*gpio_disable_free) (struct pinctrl_dev *pctldev,//gpio_request_free的逆函数
struct pinctrl_gpio_range *range,
unsigned offset);
int (*gpio_set_direction) (struct pinctrl_dev *pctldev,//设定gpio方向的回调函数
struct pinctrl_gpio_range *range,
unsigned offset,
bool input);
};
confops成员的callback函数说明:
struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
bool is_generic;
#endif
int (*pin_config_get) (struct pinctrl_dev *pctldev,//给定一个pin ID以及config type ID,获取该引脚上指定的type的配置。
unsigned pin,
unsigned long *config);
int (*pin_config_set) (struct pinctrl_dev *pctldev,//设定一个指定pin的配置
unsigned pin,
unsigned long config);
int (*pin_config_set_bulk) (struct pinctrl_dev *pctldev,
unsigned pin,
unsigned long *configs,
unsigned num_configs);
int (*pin_config_group_get) (struct pinctrl_dev *pctldev,//以pin group为单位,获取pin上的配置信息。
unsigned selector,
unsigned long *config);
int (*pin_config_group_set) (struct pinctrl_dev *pctldev,//以pin group为单位,设定pin group 的特性配置。
unsigned selector,
unsigned long config);
int (*pin_config_group_set_bulk) (struct pinctrl_dev *pctldev,
unsigned selector,
unsigned long *configs,
unsigned num_configs);
int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev, //debug接口
const char *arg,
unsigned long *config);
void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev, //debug接口
struct seq_file *s,
unsigned offset);
void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev, //debug接口
struct seq_file *s,
unsigned selector);
void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,//debug接口
struct seq_file *s,
unsigned long config);
};
7.dts文件中的pinctrl关键词表
7.1 pin命名表
pin的命名遵循IC spec上的命名,以下命名表以每个pin默认的功能命名,但实际使用中各个pin的功能会随着配置发生变化。
在dts中使用关键词“actions,pins”(不同厂商不同,这是炬芯的)后跟名字数组来定义需要使用哪些pin,如:
i2c0_over_uart0_pull_up{
actions,pins = “P_UART0_RX”,“P_UART0_TX”;
actions,pull = <2>;
}
7.2 MUX Group-Function表
7.2.1 Mux Group命名表
pin group按照寄存器的名字和bit来命名。比如:MFP_CTL1 bit22:21,定义了10个pin的mux:P_LVDS_OEP,P_LVDS_OEN,P_LVDS_ODP,P_LVDS_ODN,P_LVDS_OCP,P_LVDS_OCN,P_LVDS_OBP,P_LVDS_OBN,P_LVDS_OAP,P_LVDS_OAN。该pin group的名字为mfp1_22_21.
有些mfp寄存器的cell中,设置某一个值会将多个pin配置为不同的功能。那么这个cell中的pin就不能归为同一个pin group。需按情况拆解开,那么拆解开的pin group的名字还会加上一些后缀。
比如:根据IC SPEC,mfp1 bit[31:29]可控制3根pin:P_KS_IN0,P_KS_IN1,P_KS_IN2.该cell的0x11选项会将这2跟pin分别定义为pwm0,pwm1,pwm4.
那么就将其分拆为3个group:分别为“mfp1_31_29_ks_in2”、“mfp1_31_29_ks_in2”、“mfp1_31_29_ks_in0”。这几个mfp cell分割以后,他们之间仍然存在硬件上的互斥关系,比如将mfp1_31_29_ks_in2的function选为pwm0,mfp1_31_29_ks_in1的function选为pwm1,可以工作。但是将mfp1_31_29_ks_in2的function选为pwm0,mfp1_31_29_ks_in1的function选为jtag,就会返回错误。
在pin group的命名表中还会看到“xxx_dummy”的命名,这种pin group在pinmux设置中可能会用到。这些pin group只有一种mux功能,所以在mfp寄存器中不会表示,但是这些pin可能和gpio复用,申请这些pin有助于发现它和gpio存在的潜在复用错误。
在dts中使用关键词“actions,groups”后跟mux Group名字数组来定义需要使用哪些Mux group,如:
sd0_mfp_cmd_clk{
actions,groups = "mfp2_8_7","mfp2_6_5";
actions,function = "sd0";
};
7.2.2MUX Group-function
Mux Group所控制的pin,可以通过设置Mux Function的方式改变IC内部连通性,使得同一组pin用作不同的功能,例如:
sd0_mfp_cmd_clk{
actions,groups ="mfp2_8_7","mfp2_6_5";
actions,function = "jtag";
};
7.2.3 Drive Group
paddrv使用的配置值完全等同于IC spec中关于PAD_DRVx寄存器的定义。
对于某些pin可以设置pin的驱动能力(即供电能力),可以通过配置driver group的等级对pin的驱动能力进行配置。
在dts中使用关键词“actions,paddrv”后跟驱动能力等级来定义“actions,groups”指定drive group所代表的pin组使用哪种驱动能力,如:
sd0_d0_d3_cmd_clk_paddrv{
actions,groups="paddrv1_19_18","paddrv1_17_16";
actions,paddrv = "1" /*level 1, range :0 ~ 3*/
}
表示“paddrv1_19_18”所代表的P_SD0_CMD和paddrv1_17_16 所代表的P_SD0_CLK,使用驱动能力1来提升数据传输稳定性。
7.2.4 Pin Pull Up/Down
在dts中使用关键词“actions,pull”后跟上下拉数据定义“actions,pin”指定的pin组使用哪种上下拉,如:
sd0_pull_d0_d3_cmd{
actions,pins = "P_SD0_CMD","P_SD0_CLK";
actions,pull = <1>;
};
表示将P_SD0_CMD和P_SD0_CLK这两个pin下拉。
actions,pull = <0>,表示上下拉功能关闭
actions,pull = <1>,表示下拉
actions,pull = <2>,表示上拉
7.2.5 GPIO-PIN
pin除了可以复用作各种功能外,还可以配置成GPIO使用,pinctrl子系统将GPIO子系统也管理起来了,因此申请GPIO的时候会去检查该gpio所对应的pin是否已经被其他驱动申请作其他功能了。如果已经被申请则申请时会报错,反之亦然。
8 使用dts描述pinctrl配置
8.1dts中pinctrl配置方法
所有的pinctrl-state都定义在pin controller device节点中:
在kernel/arch/arm64/boot/dts/s700_pinctrl.dtsi 中
/ {
pinctrl@e01b0000 {
compatible = "actions,s700-pinctrl";
reg = <0 0xe01b0000 0 0x1000>;
pinctrl-names = "default";
pinctrl-0 = <&state_default>;
clocks = <&clock CLK_GPIO>;
clock-names = "mfp";
state_default: pinctrl_default {
};
而个驱动引用定义在pin controller device 节点中的子节点,即pinctrl-state节点。
各驱动使用如下方法引用pinctrl-state节点:
pinctrl-N:描述该设备需要使用的pin的一个状态(pin state),相当于上述的state。N的数值必须从0开始顺序递增。pinctrl-N属性的值为pin configuration nodes的phandle。pinctrl-N属性引用的pin configuration nodes必须是pin controller device node的直接子节点。
pinctrl-names:为每个pin state定义一个名字。每个名字顺序对应一个pin state。比如pinctrl-0的名字为“default”,pinctrl-1的名字对应“idle”。若不能指定pinctrl-names属性,这样的话,pin state的名字就是该属性的“N”字符。比如pinctrl-0的名字为字符“0”
mmc@e0330000{
pinctrl-names = "pinctrl_mmc0","share_uart2_5";
pinctrl-0 = <&mm0_pinctrl_state>;
pinctrl-1 = <&mmc_share_uart_state>;
}
上面的例子中,mmc驱动定义的pinctrl-state有两个,其中mmc0_state_default是定义pin controller device node下的直接子节点,表示sd0 的pin group 作为sd功能使用,而mmc_share_uart_state则表示作为serial功能使用,其中mmc_state_default对应的pinctrl-names 属性为default,而mmc_share_uart_state对应的pinctrl-names属性为share_uart2_5。
8.2 pin mapping database的建立
pin controller device节点配置好后,内核是如何获取到pin mapping database并使用的呢?
通过查看pinctrl子系统代码,了解到pin mapping database建立有两种情况:
一种情况是主控驱动调用pinctrl_register注册machine的struct pinctrl_desc结构到pinctrl子系统时,由pinctrl_register调用pinctrl_get创建。
另一种就是各驱动在加载时由统一设备驱动模型在driver和device绑定时调用devm_pinctrl_get转而调用pinctrl_get创建。
下一篇中通过代码来具体分析pinctrl_get。
————————————————
版权声明:本文为CSDN博主「白鲸入海」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012830148/article/details/80609337
Pinctrl子系统之一了解基础概念【转】的更多相关文章
- 快速入门系列--WCF--01基础概念
转眼微软的WCF已走过十个年头,它是微软通信框架的集大成者,将之前微软所有的通信框架进行了整合,提供了统一的应用方式.记得从自己最开始做MFC时,就使用过Named Pipe命名管道,之后做Winfo ...
- gpio子系统和pinctrl子系统(下)
情景分析 打算从两个角度来情景分析,先从bsp驱动工程师的角度,然后是驱动工程师的角度,下面以三星s3c6410 Pinctrl-samsung.c为例看看pinctrl输入参数的初始化过程(最开始的 ...
- gpio子系统和pinctrl子系统(中)
pinctrl子系统核心实现分析 pinctrl子系统的内容在drivers/pinctrl文件夹下,主要文件有(建议先看看pinctrl内核文档Documentation/pinctrl.txt): ...
- Linux驱动之GPIO子系统和pinctrl子系统
前期知识 1.如何编写一个简单的Linux驱动(一)--驱动的基本框架 2.如何编写一个简单的Linux驱动(二)--设备操作集file_operations 3.如何编写一个简单的Lin ...
- 【Machine Learning】机器学习及其基础概念简介
机器学习及其基础概念简介 作者:白宁超 2016年12月23日21:24:51 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本系列文章是作者结 ...
- TCP/IP基础概念及通信过程举例
TCP/IP基础概念及通信过程举例 出现 上个世纪60年代,由于中央集中式网络的容灾性较弱,以美国国防部为中心的一家组织研究出分组交换网络.后来为了验证分组交换技术的实用性,ARPANET出现了,并且 ...
- Jmeter基础之---jmeter基础概念
Jmeter基础之---jmeter基础概念 JMeter 介绍: 一个非常优秀的开源的性能测试工具. 优点:你用着用着就会发现它的重多优点,当然不足点也会呈现出来. JMeter 介绍: 一个非常优 ...
- 理解 angular2 基础概念和结构 ----angular2系列(二)
前言: angular2官方将框架按以下结构划分: Module Component Template Metadata Data Binding Directive Service Dependen ...
- JavaBean 基础概念、使用实例及代码分析
JavaBean 基础概念.使用实例及代码分析 JavaBean的概念 JavaBean是一种可重复使用的.且跨平台的软件组件. JavaBean可分为两种:一种是有用户界面的(有UI的):另一种是没 ...
随机推荐
- 5-6 可视化库Seaborn-Facetgrid使用和绘制多变量
基本工作流程是FacetGrid使用数据集和用于构造网格的变量初始化对象.然后,可以通过调用FacetGrid.map()或将一个或多个绘图函数应用于每个子集 FacetGrid.map_data ...
- Python的3种执行方式
1.Python源程序就是一个特殊格式的文本文件,可以使用任意文本编辑器软件做python的开发,python的文件扩展名为 .py 2.执行python程序的三种方式 解释器:用命令行输入:如输 ...
- 初学树型dp
树型DP DFS的回溯是树形DP的重点以及核心,当回溯结束后,root的子树已经被遍历完并处理完了.这便是树形DP的最重要的特点 自己认为应该注意的点 好多人都说在更新当前节点时,它的儿子结点都给更新 ...
- 压力测试中tps上不去的原因
PS (transaction per second)代表每秒执行的事务数量,可基于测试周期内完成的事务数量计算得出.例如,用户每分钟执行6个事务,TPS为6 / 60s = 0.10 TPS. 同时 ...
- C++ class内的==重载,判断相等,测试等于,重载示例。二元操作符
#include <iostream> // overloading "operator == " inside class // == 是二元操作符 //////// ...
- verilog 常见单元描述
半加器: //行为级建模 module half_adder2(a, b, sum, c_out); input a, b; output sum, c_out; assign {c_out, sum ...
- angular 运行报错
angular 运行时报错ERROR in node_modules/rxjs/internal/types.d.ts(81,44): error TS1005: ';' expected. node ...
- ionic4 ion-picker用法
ion-picker实际开发中肯定多处使用,所以封装成服务的形式调用 新建picker.service服务模块 ionic g service picker import { Injectable ...
- scrapyd--scrapydweb
scrapyd-实际的管理爬虫程序 scrapyd 是由scrapy 官方提供的爬虫管理工具,使用它我们可以非常方便地上传.控制爬虫并且查看运行日志. scrapyd是c/s架构 所有的爬虫调度工作全 ...
- Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2) E. Rock Is Push dp
E. Rock Is Push You are at the top left cell (1,1) of an n×m labyrinth. Your goal is to get to the b ...