参考资料:
Documentation\pinctrl.txt
Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt
arch/arm/boot/dts/imx6ull-14x14-evk.dts
arch/arm/boot/dts/100ask_imx6ull-14x14.dts
drivers\pinctrl\freescale\pinctrl-imx6ul.c
drivers\pinctrl\freescale\pinctrl-imx.c
一、设备树
1.1 理想模型
左边的state_0_node_a、state_1_node_a、state_2_node_a
分别对应,右边的device的引用节点。
右边device里的pinctrl-0对应于"default"状态,pinctrl-1对应于"sleep"状态。
右边device里不同的状态,分别对应不同的pin controller的
1.2 实际的例子
这个图是imx6ull的设备树配置,右边是i2c1的配置,左边是pinctroller配置
右边部分:i2c1使用了pinctrl_i2c1的引脚配置,这个引脚的格式在左边,对应于i2c1grp。结构体内包含了I2C的SCL、SDA引脚的配置。
左边部分:设备驱动匹配到fsl,imx6ul-iomuxc
后,会根据设备树分配、设置、注册引脚。
二、pincontroller数据结构
pinctroller的三大作用,有助于理解相关的数据结构:
- 引脚命名和引脚的枚举(Enumerating and naming)
- 引脚复用(Multiplexing):比如用作GPIO、I2C或其他功能
- 引脚配置(Configuration):比如上拉、下拉、open drain、驱动强度等
2.1 pinctrl_desc和pinctrl_dev
这两个结构体就是代码中用来对应,左边的pin controller设备树的信息,包括注册、描述、获得、设置、复用、配置。
2.1.1 结构体说明
pincontroller虽然是一个软件概念,但是可以用一个结构体来表示:pinctrl_dev。
需要给创建的pinctrl_dev,提供一个pinctrl_desc,然后用pinctrl_register来注册它就可以了,函数原型:
struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
struct device *dev, void *driver_data);
struct pinctrl_dev {
struct list_head node;
struct pinctrl_desc *desc; //需要提供一个pinctrl_desc
struct radix_tree_root pin_desc_tree;
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
};
struct pinctrl_desc {
const char *name;
const struct pinctrl_pin_desc *pins; //结构体数组,描述的是单个引脚,支持哪些功能
unsigned int npins; //支持多少个引脚
const struct pinctrl_ops *pctlops; //下面三个是操作函数,pctops用来描述一组引脚
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
};
在imx6ull中的pinctrl_desc示例:
imx_pinctrl_desc->name = dev_name(&pdev->dev);
imx_pinctrl_desc->pins = info->pins;
imx_pinctrl_desc->npins = info->npins;
imx_pinctrl_desc->pctlops = &imx_pctrl_ops;
imx_pinctrl_desc->pmxops = &imx_pmx_ops;
imx_pinctrl_desc->confops = &imx_pinconf_ops;
imx_pinctrl_desc->owner = THIS_MODULE;
2.1.2 作用1:描述、获得引脚
使用结构体pinctrl_pin_desc
来描述一个引脚,一个pin controller
有多个引脚。
/**
* struct pinctrl_pin_desc - boards/machines provide information on their
* pins, pads or other muxable units in this struct
* @number: unique pin number from the global pin number space
* @name: a name for this pin
* @drv_data: driver-defined per-pin data. pinctrl core does not touch this
*/
struct pinctrl_pin_desc {
unsigned number; //第几个引脚
const char *name; //引脚的名字
void *drv_data; //驱动相关的数据
};
使用pinctrl_ops来操作引脚,主要功能有二:
- 来取出某组的引脚:
get_groups_cout
、get_group_pins
- 处理设备中pin_constroller中的某个节点:
dt_node_to_map
,把device_node转换为一系列的pinctrl_map
struct pinctrl_ops {
int (*get_groups_count) (struct pinctrl_dev *pctldev); //用来获得有多少个组
const char *(*get_group_name) (struct pinctrl_dev *pctldev, //获得组中引脚的名字
unsigned selector);
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);
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);
};
2.1.3 作用2:引脚复用
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);
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;
};
2.1.4 引脚配置
struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
bool is_generic;
#endif
int (*pin_config_get) (struct pinctrl_dev *pctldev, //获取配置
unsigned pin,
unsigned long *config);
int (*pin_config_set) (struct pinctrl_dev *pctldev, //设置配置
unsigned pin,
unsigned long *configs,
unsigned num_configs);
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);
};
2.1.5 使用pinctrl_desc注册得到pinctrl_dev
调用devm_pinctrl_registerhuopinctrl_register,就可以根据pinctrl_desc构造出pinctrl_dev,并且把pinctrl_dev放入链表:
struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
struct device *dev, void *driver_data);
{
//...
struct pinctrl_dev *pctldev;
pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);
pctldev->owner = pctldesc->owner;
pctldev->desc = pctldesc;
pctldev->driver_data = driver_data;
ret = pinctrl_check_ops(pctldev);
ret = pinmux_check_ops(pctldev);
ret = pinconf_check_ops(pctldev);
ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);
list_add_tail(&pctldev->node, &pinctrldev_list); //根据构造出的pinctrl_dev,放入链表
//....
}
三、client的数据结构
在设备树中,使用pinctrl时格式如下:
/* For a client device requiring named states */
device {
pinctrl-name = "active", "idle";
pinctrl-0 = <&state_0_node_a>;
pinctrl-1 = <&state_1_node_a &state_1_node_b>;
};
设备节点要么被转换为platform_device,或者其他结构体(比如i2c_client),但是里面都会有一个device结构体,比如:
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev; //这里包含一个device结构体
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name; /* initial name of the device */
const struct device_type *type;
struct mutex mutex; /* mutex to synchronize calls to
* its driver.
*/
struct bus_type *bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
void *platform_data; /* Platform specific data, device
core doesn't touch it */
void *driver_data; /* Driver data, set and get with
dev_set/get_drvdata */
struct dev_links_info links;
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
struct irq_domain *msi_domain;
#endif
#ifdef CONFIG_PINCTRL
struct dev_pin_info *pins; //这里就包含了一个pincontroller的信息
#endif
#ifdef CONFIG_GENERIC_MSI_IRQ
struct list_head msi_list;
#endif
//....
};
3.1 dev_pin_info
每个device结构体里都有一个dev_pin_info
结构体,用来保存设备的pinctrl信息:
//内核根据设备树中设置的pinctrl-name="default","sleep"等内容,构造出相应的pinctrl_state状态
struct dev_pin_info {
struct pinctrl *p; //包含了pinctrl的链表
struct pinctrl_state *default_state; //如果有的话就是default状态
struct pinctrl_state *init_state; //初始化状态
#ifdef CONFIG_PM
struct pinctrl_state *sleep_state; //休眠状态
struct pinctrl_state *idle_state; //空闲状态
#endif
};
//用来描述自己定义的状态
struct pinctrl {
struct list_head node; //链表
struct device *dev; //指向device设备
struct list_head states; //pinctrl状态链表
struct pinctrl_state *state; //pinctrl_state链表
struct list_head dt_maps;
struct kref users;
};
//设备树中设置了不同的状态,在代码中对应于不同的pinctrl_state
struct pinctrl_state {
struct list_head node;
const char *name; //状态名字
struct list_head settings; //settings链表,状态对应的配置
};
//这里通过pinctrl_ops->dt_node_to_map来把设备节点转换成一系列的pinctrl_map
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;
};
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;
};
//pinctrl_map会转换成pinctrl_setting
//这个pinctrl_setting会被存放在pinctrl_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;
};
struct pinctrl_setting_mux {
unsigned group; //把当前组引脚,用整数表示
unsigned func; //配置成功能
};
struct pinctrl_setting_configs { //与pinctrl_map_configs类似
unsigned group_or_pin;
unsigned long *configs;
unsigned num_configs;
};
3.2 pinctrl
假设芯片上有多个pin controller,那么这个设备使用哪个pin controller?
这需要通过设备树来确定:
- 分析设备树,找到pin controller
- 对于每个状态,比如defualt、init,去分析pin controller中的设备树节点
- 使用pin controller的pinctrl_ops.dt_node_to_map来处理设备树的pinctrl节点信息,得到一些列的pinctrl_map
- 这些pinctrl_map放在pinctrl.dt_maps链表中
- 每个pinctrl_map都被转换为pinctrl_setting,放在对应的pinctrl_state.settings链表中
3.3 pinctrl_map和pinctrl_setting
设备引用pin controller中的某个节点时,这个节点会被转换为一些列的pinctrl_map:
- 转换为多少个pinctrl_map,完全由具体的驱动决定
- 每个pinctrl_map,又被转换为一个pinctrl_setting
- 举例,设备节点里有:
pinctrl-0 = <&state_0_node_a>
- pinctrl-0对应一个状态,会得到一个pinctrl_state
- state_0_node_a节点被解析为一系列的pinctrl_map
- 这一些列的pinctrl_map被转换为一些列的pinctrl_setting
- 这些pinctrl_setting被放入pinctrl_state的settings链表
四、使用pinctrl_setting
pinctrl_setting如何其最终起到效果呢?
调用过程:
really_probe
pinctrl_bind_pins
pinctrl_select_state
/* Apply all the settings for the new state */
list_for_each_entry(setting, &state->settings, node) { //遍历所有的settings,根据不同的标志位进行设置
switch (setting->type) {
case PIN_MAP_TYPE_MUX_GROUP:
ret = pinmux_enable_setting(setting);
ret = ops->set_mux(...);
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
ret = pinconf_apply_setting(setting);
ret = ops->pin_config_group_set(...);
break;
default:
ret = -EINVAL;
break;
}
自我总结:
左边的设备树对应着pinctrl的各种引脚配置,这些配置分成了不同的组,不同的组里用到了不同的引脚,每一个引脚有各自的功能。
右边的设备里表明了一种设备,这个设备肯定使用到了芯片的某些功能,可以直接引用左边设备树的节点。
设备树在转换成驱动代码时,通过compatible匹配,左边的设备树主要的数据就是pinctrl_dev 下的pinctrl_desc结构体,它包含的信息不仅仅只有设备树里的各个引脚的功能,还包括了一些操作函数。这些函数用来配置引脚为不同的功能,不同的配置。
右边的设备树主要的数据结构体是dev_pin_info,它包含了设备的一些默认状态下的配置,这些配置操作就是pinctrl_desc中带的。