Linux之pinctrl子系统与gpio
文章目录
参考链接:
http://www.wowotech.net/sort/gpio_subsystem
pinctrl子系统文件列表
linux/drivers/pinctrl目录下的源文件列表
- 源文件列表
| 文件名 | 描述 |
|---|---|
| core.c core.h | pin control subsystem的core driver |
| pinctrl-utils.c pinctrl-utils.h | pin control subsystem的一些utility接口函数 |
| pinmux.c pinmux.h | pin control subsystem的core driver(pin muxing部分的代码,也称为pinmux driver) |
| pinconf.c pinconf.h | pin control subsystem的core driver(pin config部分的代码,也称为pin config driver) |
| devicetree.c devicetree.h | pin control subsystem的device tree代码 |
| pinctrl-xxxx.c | 各种pin controller的low level driver |
- 其他内核模块接口文件,很多内核的其他模块需要用到pin control subsystem的服务,这些头文件就定义了pin control subsystem的外部接口以及相关的数据结构
| 文件名 | 描述 |
|---|---|
| consumer.h | 其他的driver要使用pin control subsystem的接口:a、设置引脚复用功能 b、配置引脚的电气特性;需要include这个头文件 |
| devinfo.h | 这是linux内核的驱动模型模块使用的接口。struct device中包括struct dev_pin_info *pins的成员,描述了该设备的引脚的初始状态信息,在probe之前,driver model中的core driver在调用driver的probe函数之前会先设定pin state |
| machine.h | machine模块的接口 |
- Low level pin controller driver接口,提供给底层specific pin controller driver的头文件列表
| 文件名 | 描述 |
|---|---|
| pinconf-generic.h | 主要是提供给各种pin controller driver使用的,不是外部接口 |
| pinconf.h | pin configuration 接口 |
| pinctrl-state.h | pin control state状态定义 |
| pinmux.h | pin mux function接口 |
pinctrl主要数据结构
pinctrl_dev 是 pinctrl 子系统的根源结构体
struct pinctrl_dev {
struct list_head node;
struct pinctrl_desc *desc;
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_desc:这里包含了pinctrl 子系统三个最重要的结构体,有三个操作函数集,pinctrl_ops 包含了对 PIN 的操作函数集,pinmux_ops 包含了对 PIN 的复用函数集,pinconf_ops 包含了对 PIN 的配置函数
- pin controller描述符。每一个特定的pin controller都用一个struct pinctrl_desc来抽象
- pin controller描述符需要描述它可以控制多少个pin,每一个pin的信息。这两个成员就确定了一个pin controller所能控制的引脚的信息。
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/platform_device.h>
struct pinctrl_desc {
const char *name;
//指向npins个pin描述符,每个描述符描述一个pin
const struct pinctrl_pin_desc *pins;
//该pin controller中有多少个可控的pin
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
};
//对PIN的操作函数集
struct pinctrl_ops {
//该pin controller支持多少个pin group
//pin group的定义可以参考关于pin controller的功能规格中的描述。
//注意不要把pin group和IO port的硬件分组搞混了。例如:S3C2416有138个I/O 端口,分成11组,分别是gpa~gpl,这个组并不叫pin group,而是叫做pin bank。pin group是和特定功能(例如SPI、I2C)相关的一组pin。
int (*get_groups_count) (struct pinctrl_dev *pctldev);
//给定一个selector(index),获取指定pin group的name
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);
//分析一个pin configuration node并把分析的结果保存成mapping table entry,每一个entry表示一个setting(一个功能复用设定,或者电气特性设定)
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);
};
//对PIN的复用函数集
struct pinmux_ops {
//pin control core进行具体的复用设定之前需要调用该函数,主要是用来底层的driver判断某个引脚的复用设定是否是OK的。
int (*request) (struct pinctrl_dev *pctldev, unsigned offset);
//是request的逆函数。调用request函数请求占用了某些pin的资源,调用free可以释放这些资源
int (*free) (struct pinctrl_dev *pctldev, unsigned offset);
//返回pin controller支持的function的数目
int (*get_functions_count) (struct pinctrl_dev *pctldev);
//给定一个selector(index),获取指定function的name
const char *(*get_function_name) (struct pinctrl_dev *pctldev, unsigned selector);
//给定一个selector(index),获取指定function的pin groups信息
int (*get_function_groups) (struct pinctrl_dev *pctldev, unsigned selector, const char * const **groups, unsigned *num_groups);
//
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;
};
//对PIN的配置函数
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为单位,获取pin上的配置信息
int (*pin_config_group_get) (struct pinctrl_dev *pctldev, unsigned selector, unsigned long *config);
// 以pin group为单位,设定pin group的特性配置
int (*pin_config_group_set) (struct pinctrl_dev *pctldev, unsigned selector, unsigned long *configs, unsigned num_configs);
//debug接口
int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev, const char *arg, unsigned long *config);
//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 结构体:这里包含了 PIN 控制器所控制 PIN 的状态 state,state 里面包含了setting,这个 setting 就是在设备树中对PIN的设置,设计理念如下:
- 设置该设备的功能复用,一个是function,另外一个pin group。function是功能抽象,对应一个HW逻辑block,例如SPI0。虽然给定了具体的gunction name,并不能确定其使用的pins的情况。例如:芯片内部的SPI0的功能可能引出到pin group { A8, A7, A6, A5 },也可能引出到另外一个pin group{ G4, G3, G2, G1 },但是这两个pin group不能同时active, 因此,只有给出function selector(selector就是一个ID或者index)以及function的pin group selector才能进行function mux的设定。
- 设定该device对应的那些pin的电气特性。由于电源管理的要求,某个device可能处于某个电源管理状态,如idle或者sleep,这时候,属于该device的所有的pin就会需要处于另外的状态。综合上述的需求,定义了pin control state的概念,也就是设备可能处于非常多的状态中的一个,device driver可以切换设备处于的状态。为了方便管理pin control state,提出了一个pin control state holder的概念,用来管理一个设备的所有的pin control状态
struct pinctrl {
struct list_head node;//系统中的所有device的pin control state holder被挂入到了一个全局链表中
struct device *dev;//该pin control state holder对应的device
struct list_head states;//该设备的所有的状态被挂入到这个链表中
struct pinctrl_state *state;//当前的pin control state
struct list_head dt_maps;//mapping table
struct kref users;//引用计数
};
- 通过create_pinctrl()函数来创建一个pinctrl,通过pinctrl_dt_to_map()来解析
- 系统中的每一个需要和pin control subsystem进行交互的设备在进行设定之前都需要首先获取这个结构体指针。而属于该设备的所有的状态都是挂入到一个链表中,链表头就是pin control state holder的states成员,一个state的定义如下:
struct pinctrl_state {
struct list_head node;//挂入链表头的节点
const char *name;//该state的名字
struct list_head settings;//属于该状态的所有的settings
};
//一个pin state包含若干个setting,所有的settings被挂入一个链表中,链表头就是pin state中的settings成员,定义如下:
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;
struct pinctrl_setting_configs configs;
} data;
};
/*
当driver设定一个pin state的时候,pin control subsystem内部会遍历该state的settings链表,将一个一个的setting进行设定。这些settings有各种类型,定义如下:
*/
enum pinctrl_map_type {
PIN_MAP_TYPE_INVALID,
PIN_MAP_TYPE_DUMMY_STATE,
PIN_MAP_TYPE_MUX_GROUP,//功能复用的setting
PIN_MAP_TYPE_CONFIGS_PIN,//设定单一个pin的电气特性
PIN_MAP_TYPE_CONFIGS_GROUP,//设定单pin group的电气特性
};
//有pin mux相关的设定(PIN_MAP_TYPE_MUX_GROUP),定义如下:
struct pinctrl_setting_mux {
unsigned group;//该setting所对应的group selector
unsigned func;//该setting所对应的function selector
};
//有了function selector以及属于该functiong的roup selector就可以进行该device和pin mux相关的设定了。设定电气特性的settings定义如下:
struct pinctrl_map_configs {
const char *group_or_pin;//该pin或者pin group的名字
unsigned long *configs;//要设定的值的列表。这个值被用来写入HW
unsigned num_configs;//列表中值的个数
};
- gpio 相关的结构体gpio_chip,因 pinctrl 子系统和 gpio 子系统是耦合的
struct gpio_chip {
const char *label;
struct gpio_device *gpiodev;
struct device *parent;
struct module *owner;
int (*request)(struct gpio_chip *chip, unsigned offset);
void (*free)(struct gpio_chip *chip, unsigned offset);
int (*get_direction)(struct gpio_chip *chip, unsigned offset);
int (*direction_input)(struct gpio_chip *chip, unsigned offset);
int (*direction_output)(struct gpio_chip *chip, unsigned offset, int value);
int (*get)(struct gpio_chip *chip, unsigned offset);
void (*set)(struct gpio_chip *chip, unsigned offset, int value);
void (*set_multiple)(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits);
int (*set_config)(struct gpio_chip *chip, unsigned offset, unsigned long config);
int (*to_irq)(struct gpio_chip *chip, unsigned offset);
void (*dbg_show)(struct seq_file *s, struct gpio_chip *chip);
int base;
u16 ngpio;
const char *const *names;
bool can_sleep;
#if IS_ENABLED(CONFIG_GPIO_GENERIC)
unsigned long (*read_reg)(void __iomem *reg);
void (*write_reg)(void __iomem *reg, unsigned long data);
unsigned long (*pin2mask)(struct gpio_chip *gc, unsigned int pin);
void __iomem *reg_dat;
void __iomem *reg_set;
void __iomem *reg_clr;
void __iomem *reg_dir;
int bgpio_bits;
spinlock_t bgpio_lock;
unsigned long bgpio_data;
unsigned long bgpio_dir;
#endif
#ifdef CONFIG_GPIOLIB_IRQCHIP
/*
* With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib
* to handle IRQs for most practical cases.
*/
struct irq_chip *irqchip;
struct irq_domain *irqdomain;
unsigned int irq_base;
irq_flow_handler_t irq_handler;
unsigned int irq_default_type;
unsigned int irq_chained_parent;
bool irq_nested;
bool irq_need_valid_mask;
unsigned long *irq_valid_mask;
struct lock_class_key *lock_key;
#endif
#if defined(CONFIG_OF_GPIO)
/*
* If CONFIG_OF is enabled, then all GPIO controllers described in the
* device tree automatically may have an OF translation
*/
/*Pointer to a device tree node representing this GPIO controller.*/
struct device_node *of_node;
/* Number of cells used to form the GPIO specifier.*/
unsigned int of_gpio_n_cells;
/**
* Callback to translate a device tree GPIO specifier into a chip-
* relative GPIO number and flags.
*/
int (*of_xlate)(struct gpio_chip *gc, const struct of_phandle_args *gpiospec, u32 *flags);
#endif
};
- 根据GPIO ID找到该ID对应的pin control device(struct pinctrl_dev)和GPIO rang(pinctrl_gpio_range)。在core driver中,每个low level的pin controller device都被映射成一个struct pinctrl_dev,并形成链表,链表头就是pinctrldev_list。由于实际的硬件设计(例如GPIO block被分成若干个GPIO 的bank,每个bank就对应一个HW GPIO Controller Block),一个pin control device要管理的GPIO ID是分成区域的,每个区域用struct pinctrl_gpio_range来抽象,在low level 的pin controller初始化的时候(具体参考samsung_pinctrl_register的代码),会调用pinctrl_add_gpio_range将每个GPIO bank表示的gpio range挂入到pin control device的range list中(gpio_ranges成员)。pinctrl_gpio_range 的定义如下:
struct pinctrl_gpio_range {
struct list_head node;
const char *name;
unsigned int id;//GPIO chip ID
unsigned int base;//该range中的起始GPIO IDD
unsigned int pin_base;//在线性映射的情况下,这是起始的pin base
unsigned const *pins;//在非线性映射的时候,这是table是pin到GPIO的lookup table
unsigned int npins;//这个range有多少个GPIO引脚
struct gpio_chip *gc;-//每个GPIO bank都是一个gpio chip,对应一个GPIO range
};
- pin ID和GPIO ID有两种映射关系,一种是线性映射(这时候pin_base有效),对于这个GPIO range,GPIO base ID是a,pin ID base是b,那么a<—>b,a+1<—>b+1,a+2<—>b+2,以此类推。对于非线性映射(pin_base无效,pins是有效的),需要建立一个lookup table,以GPIO ID为索引,可以找到对于的pin ID。
函数调用逻辑
- 在文件 drivers/pinctrl/freescale/pinctrl-xxx.c中,驱动的入口是 arch_initcall 中声明的函数
- pinctrl 子系统的驱动也是一个标准的 platform 驱动框架,分为:驱动、设备、总线。platform 虚拟总线会按照 of_device_id 结构体中的 compatible 属性去匹配 pinctrl 驱动和设备
- of_device_id 定义的数组,最后一个是空元素,这是必须的,原因是 platform 本身的 match 函数中需要判断是否达到末尾,of_device_id 定义的 compatible 经常不止一个,系统需要知道是否匹配到最后一个元素
- probe 函数后面的调用中,调用 pinctrl_register 函数,向 Linux 内核注册一个 PIN 控制器
struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, struct device *dev, void *driver_data)
设备树实例
- pin controller 的DTS结构
- 每个pin configuration都是pin controller的child node,描述了client device要使用到的一组pin的配置信息
- 各个device可以通过自己节点的属性来指向pin controller的某个child node,也就是pin configuration
- DTS的PIN属性
- actions,groups的属性用于表示Mux Group的名字数组
- actions,pins的属性用于表示该组pin的名字数组
- actions,function的属性表示该组Mux Group使用功能的名字
- actions,paddrv的属性表示该组Dirve Group所代表的pin的驱动能力
- actions,pull的属性表述该组的pin需要配置的上下拉状态
pinctrl@56000000 {
//定义S3C2416 pin controller自己的属性
reg = <0x56000000 0x1000="">;
compatible = "samsung,s3c2416-pinctrl";
...
//定义pin bank
gpf {
//gpio-controller属性,说明该device node是一个GPIO controller
gpio-controller;
//#gpio-cells属性是一个GPIO controller的必须定义的属性,描述了需要多少个cell来具体描述一个GPIO(这是和具体的GPIO controller相关的)
#gpio-cells = <0x2>;
interrupt-controller;
#interrupt-cells = <0x2>;
//phandle(linux,phandle这个属性和phandle是一样的,只不过linux,phandle是old-style,多定义一个属性是为了兼容)定义了一个句柄,当其他的device node想要引用这个node的时候就可以使用该句柄
linux,phandle = <0xc>;
phandle = <0xc>;
};
...
//定义功能复用配置
uart0-data {
//samsung,pins这个属性定义了一个pin configuration所涉及到的引脚定义
//对于uart0-data这个node,该配置涉及了gph bank中的第一个和第二个GPIO pin
//一旦选择功能,amsung,pins定义的引脚需要做功能设定,这就是samsung,pin-function定义的内容。而具体设定需要去查阅datasheet
samsung,pins = "gph-0", "gph-1";
samsung,pin-function = <0x2>;
linux,phandle = <0x2>;
phandle = <0x2>;
};
uart0-fctl {
samsung,pins = "gph-8", "gph-9";
samsung,pin-function = <0x2>;
linux,phandle = <0x3>;
phandle = <0x3>;
};
...
}
//client device的DTS
device-node-name {
//定义该device自己的属性
...
//pinctrl-names定义了一个pin state列表
//state有两种,标识,一种就是pinctrl-names定义的字符串列表,另外一种就是ID,ID从0开始,依次加
pinctrl-names = "sleep", "active";
//pinctrl-x的定义。pinctrl-x是一个句柄(phandle)列表,每个句柄指向一个pin configuration。
//有时候,一个state对应多个pin configure。例如I2C功能有两种配置,一是pin ID{7,8}引出,另一是pin ID{69,103}引出
pinctrl-0 = <pin-config-0-a>;
pinctrl-1 = <pin-config-1-a pin-config-1-b>;
};
//例如
serial@50000000 {
……
pinctrl-names = "default";
pinctrl-0 = <0x2 0x3>;//或 pinctrl-0 = <&uart0-data &uart0-fctl>
};
驱动使用pinctrl
//设备驱动模块获取对应的pinctrl
struct pinctrl *devm_pinctrl_get(struct device *dev);
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name);
struct pinctrl *pinctrl_get_select_default(struct device *dev);
struct pinctrl *devm_pinctrl_get_select(struct device *dev, const char *name);
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state);
//pinctrl子系统申请GPIO
int pinctrl_request_gpio(unsigned gpio);
void pinctrl_free_gpio(unsigned gpio);
int pinctrl_gpio_direction_input(unsigned gpio);
int pinctrl_gpio_direction_output(unsigned gpio);
//GPIO子系统申请GPIO
#include <linux/gpio.h> //里面声明io口的操作函数
int gpio_request(unsigned gpio, const char *label);//每个io只能被请求一次,可防止多个驱动来控制同一个IO口
void gpio_free(unsigned gpio); //释放已请求的io口
int gpio_direction_input(unsigned gpio); //把指定的IO口作输入功能, gpio用于指定具体哪个io口
int gpio_direction_output(unsigned gpio, int value); //作输出功能,并根据value的值输出高低电平
int gpio_get_value(unsigned gpio); //获取指定IO口的电平
void gpio_set_value(unsigned gpio, int value); //设置IO口的电平为value(0/1)
int gpio_to_irq(unsigned gpio); //根据io口,获取到它对应的中断号(io口大都有外部中断功能)
与驱动模型的接口
- 与其写代码调用devm_pinctrl_get、pinctrl_lookup_state、pinctrl_select_state等pin control subsystem的接口函数,最好是让统一设备驱动模型(Driver model)来处理pin 的各种设定。
- linux kernel中的驱动模型提供了driver和device的绑定机制,一旦匹配会调用probe函数如下:
static int really_probe(struct device *dev, struct device_driver *drv)
{
……
ret = pinctrl_bind_pins(dev);//对该device涉及的pin进行pin control相关设定
……
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;
}
……
}
//pinctrl_bind_pins的代码如下:
int pinctrl_bind_pins(struct device *dev)
{
int ret;
dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
dev->pins->p = devm_pinctrl_get(dev);//调用devm_pinctrl_get获取该device对应的 pin control state holder句柄
//搜索default state,sleep state,idle state并记录在本device中
dev->pins->default_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_DEFAULT);
ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state);//将该设备设定为pin default state
dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_SLEEP);//(3)
dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_IDLE);//(3)
return 0;
}
//struct device数据结构有一个pins的成员,它描述了和该设备相关的pin control的信息,定义如下:
struct dev_pin_info {
struct pinctrl *p;//该device对应的pin control state holder
struct pinctrl_state *default_state;//缺省状态
struct pinctrl_state *sleep_state;//电源管理相关的状态
struct pinctrl_state *idle_state;//电源管理相关的状态
};
和device tree或者machine driver相关的接口
- device tree或者machine driver这两个模块主要是为 pin control subsystem提供pin mapping database的支持。这个database的每个entry用下面的数据结构表示:
struct pinctrl_map {
const char *dev_name;//使用这个mapping entry的设备名
const char *name;//该名字表示了该mapping entry
enum pinctrl_map_type type;//这个entry的mapping type
const char *ctrl_dev_name;//pin controller这个设备的名字
union {
struct pinctrl_map_mux mux;
struct pinctrl_map_configs configs;
} data;
};
- 通过machine driver静态定义的数据来建立pin mapping database,machine driver定义一个巨大的mapping table,描述,然后在machine初始化的时候,调用pinctrl_register_mappings将该table注册到pin control subsystem中。
- 通过device tree来建立pin mapping database,pin mapping信息定义在dts中,主要包括两个部分,一个是定义在各个具体的device node中,另外一处是定义在pin controller的device node中。
//一个典型的device tree中的外设node定义如下
device-node-name {
//定义该device自己的属性
pinctrl-names = "sleep", "default";
pinctrl-0 = ;
pinctrl-1 = ;
};
- 对普通device的dts分析在函数pinctrl_dt_to_map中,代码如下:
int pinctrl_dt_to_map(struct pinctrl *p)
{
of_node_get(np);
//pinctrl-0 pinctrl-1……表示了该设备的一个个的状态,这里定义了两个pinctrl-0和pinctrl-1分别对应sleep和default状态。这里每次循环分析一个pin state。
for (state = 0; ; state++) {
/* Retrieve the pinctrl-* property */
propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);
prop = of_find_property(np, propname, &size);
kfree(propname);
if (!prop)
break;
//size和list分别保存了该pin state中所涉及pin configuration phandle的数目以及phandle的列表
//list指向pinctrl-x,size等于pinctrl-x=<...>元素个数
list = prop->value;
size /= sizeof(*list);
//读取从pinctrl-names属性中获取对应的state name,如:sleep 或 default
ret = of_property_read_string_index(np, "pinctrl-names", state, &statename);
if (ret < 0) {
//如果没有定义pinctrl-names属性,那么我们将pinctrl-0 pinctrl-1……中的那个ID取出来作为state name
/* strlen("pinctrl-") == 8 */
statename = prop->name + 8;
}
//遍历一个pin state中的pin configuration list,即pinctrl-x=<...>对应的pin configuration,实际应该是pin controler device node中的sub node,用指针phandle标识
for (config = 0; config < size; config++) {
phandle = be32_to_cpup(list++);
//用phandle作为索引,在device tree中找他该phandle表示的那个pin configuration
np_config = of_find_node_by_phandle(phandle);
if (!np_config) {
dev_err(p->dev, "prop %s index %i invalid phandle\n", prop->name, config);
ret = -EINVAL;
goto err;
}
//分析一个pin configuration
ret = dt_to_map_one_config(p, statename, np_config);
of_node_put(np_config);
if (ret < 0)
goto err;
}
//如果该设备没有定义pin configuration,那么也要创建一个dummy的pin state
if (!size) {
ret = dt_remember_dummy_state(p, statename);
if (ret < 0)
goto err;
}
}
return 0;
err:
pinctrl_dt_free_maps(p);
return ret;
}
- 对普通device的dts分析在函数dt_to_map_one_config中,代码如下:
//statename,对应的pinctrl-x的pinctrl-names属性名字,或ID
//np_config,指向pinctrl-x=<...>其中的一个节点
static int dt_to_map_one_config(struct pinctrl *p, const char *statename, struct device_node *np_config)
{
struct device_node *np_pctldev;
struct pinctrl_dev *pctldev;
const struct pinctrl_ops *ops;
int ret;
struct pinctrl_map *map;
unsigned num_maps;
/* 增加节点np_config的引用 */
np_pctldev = of_node_get(np_config);
for (;;) {
//首先找到该pin configuration node对应的parent node(也就是pin controler对应的node,如device-node-name),如果找不到或者是root node,则进入出错处理。
np_pctldev = of_get_next_parent(np_pctldev);
if (!np_pctldev || of_node_is_root(np_pctldev)) {
of_node_put(np_pctldev);
return -EPROBE_DEFER;
}
//获取pin control class device,也就是底层注册的pinctrl_dev
pctldev = get_pinctrl_dev_from_of_node(np_pctldev);
if (pctldev)
//一旦找到pin control class device则跳出for循环
break;
/* Do not defer probing of hogs (circular loop) */
if (np_pctldev == p->dev->of_node) {
of_node_put(np_pctldev);
return -ENODEV;
}
}
of_node_put(np_pctldev);
/*
* Call pinctrl driver to parse device tree node, and
* generate mapping table entries
*/
ops = pctldev->desc->pctlops;
//调用底层的callback函数处理pin configuration node
ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);
if (ret < 0)
return ret;
/* Stash the mapping table chunk away for later use */
//将该pin configuration node的mapping entry信息注册到系统中
return dt_remember_or_free_map(p, statename, pctldev, map, num_maps);
}