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