参考资料:
Linux4.x内核
在设备树中,使用pinctrl时格式如下:
设备节点要么被转换为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; //有一个描述pinctrl的结构体
#endif
#ifdef CONFIG_GENERIC_MSI_IRQ
struct list_head msi_list;
#endif
//....
};
struct pinctrl_state {
struct list_head node;
const char *name;
struct list_head settings; //状态下的一系列的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 {
unsigned group_or_pin; //引脚值或者组
unsigned long *configs; //配置值
unsigned num_configs; //多少个配置值
};
每个device结构体里都有一个dev_pin_info结构体,用来保存设备的pinctrl信息:
struct dev_pin_info {
struct pinctrl *p; //多个state链表
struct pinctrl_state *default_state;
struct pinctrl_state *init_state;
#ifdef CONFIG_PM
struct pinctrl_state *sleep_state;
struct pinctrl_state *idle_state;
#endif
};
假设芯片上有多个pin controller,那么这个设备使用哪个pin controller?
这需要通过设备树来确定:
设备引用pin controller中的某个节点时,这个节点会被转换为一系列的pinctrl_map:
pinctrl-0 = <&state_0_node_a>
really_probe >>
int pinctrl_bind_pins(struct device *dev)
{
int ret;
dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL); //创建一个dev_pin_info结构体
if (!dev->pins)
return -ENOMEM;
dev->pins->p = devm_pinctrl_get(dev); //获取pinctrl结构体
//...
dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_DEFAULT); //查找default状态
//....
dev->pins->init_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_INIT); //查找init状态
if (IS_ERR(dev->pins->init_state)) { //如果没有init状态
//...
ret = pinctrl_select_state(dev->pins->p,
dev->pins->default_state); //就设置为default状态
} else {
ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state); //有的话就是init状态
}
//....
#ifdef CONFIG_PM
//....
dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_SLEEP); //查找sleep状态
//...
dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_IDLE); //查找idle状态
//...
#endif
return 0;
//...
}
struct pinctrl *devm_pinctrl_get(struct device *dev)
{
struct pinctrl **ptr, *p;
//.....
p = pinctrl_get(dev); //从dev中获取pinctrl
//....
return p;
}
struct pinctrl *pinctrl_get(struct device *dev)
{
//.....
p = find_pinctrl(dev);
if (p != NULL) { //第一次调用时必为空,就会去调用create_pinctrl()
dev_dbg(dev, "obtain a copy of previously claimed pinctrl\n");
kref_get(&p->users);
return p;
}
return create_pinctrl(dev);
}
static struct pinctrl *create_pinctrl(struct device *dev)
{
p = kzalloc(sizeof(*p), GFP_KERNEL); //分配了一个pinctrl结构体
//....
ret = pinctrl_dt_to_map(p); //处理设备树信息,把信息转换成一系列的map
//....
devname = dev_name(dev);
//....
for_each_maps(maps_node, i, map) { //对于每一个map
/* Map must be for this device */
if (strcmp(map->dev_name, devname))
continue;
ret = add_setting(p, map); //把map转换成setting
//....
}
mutex_unlock(&pinctrl_maps_mutex);
//....
return p;
}
int pinctrl_dt_to_map(struct pinctrl *p)
{
//.....
/* We may store pointers to property names within the node */
of_node_get(np);
/* For each defined state ID */
for (state = 0; ; state++) { //为每一个state
/* Retrieve the pinctrl-* property */
propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state); //获得属性,比如设备树上是pinctrl-0 = <&node1, &node2>;
prop = of_find_property(np, propname, &size);
kfree(propname);
if (!prop) {
if (state == 0) {
of_node_put(np);
return -ENODEV;
}
break;
}
list = prop->value;
size /= sizeof(*list);
/* Determine whether pinctrl-names property names the state */
ret = of_property_read_string_index(np, "pinctrl-names",
state, &statename); //获得pinctrl的名字
//.....
/* For every referenced pin configuration node in it */
for (config = 0; config < size; config++) { //解析设备树,config0就是上面的node0,config1就是node1
phandle = be32_to_cpup(list++);
/* Look up the pin configuration node */
np_config = of_find_node_by_phandle(phandle); //使用phandle找到节点,np_config来自于phandle的节点
//....
/* Parse the node */
ret = dt_to_map_one_config(p, statename, np_config); //处理每一个节点(pintrol-0=<&nodexxxxxx>)
of_node_put(np_config);
if (ret < 0)
goto err;
}
/* No entries in DT? Generate a dummy state table entry */
if (!size) {
ret = dt_remember_dummy_state(p, statename);
if (ret < 0)
goto err;
}
}
return 0;
err:
pinctrl_dt_free_maps(p);
return ret;
}
int pinctrl_dt_to_map(struct pinctrl *p)
{
//......
/* For each defined state ID */
for (state = 0; ; state++) {
//......
/* Parse the node */
ret = dt_to_map_one_config(p, statename, np_config); //处理单个节点
of_node_put(np_config);
//......
}
//.....
}
static int dt_to_map_one_config(struct pinctrl *p, const char *statename,
struct device_node *np_config)
{
//.......
const struct pinctrl_ops *ops;
//...
ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps); //设备树节点转换成map
if (ret < 0)
return ret;
//........
}
在driver/pinctrl/freescale/pinctrl-imx.c
中有对应的ops->dt_node_to_map
函数:
static const struct pinctrl_ops imx_pctrl_ops = {
.get_groups_count = imx_get_groups_count,
.get_group_name = imx_get_group_name,
.get_group_pins = imx_get_group_pins,
.pin_dbg_show = imx_pin_dbg_show,
.dt_node_to_map = imx_dt_node_to_map, //对应的函数,把设备树节点转换成map
.dt_free_map = imx_dt_free_map,
};
static int imx_dt_node_to_map(struct pinctrl_dev *pctldev,
struct device_node *np,
struct pinctrl_map **map, unsigned *num_maps)
{
//.....
/*
* first find the group of this node and check if we need create
* config maps for pins
*/
grp = imx_pinctrl_find_group_by_name(info, np->name); //根据节点的名字找到imx_pin_group
if (!grp) {
dev_err(info->dev, "unable to find group for node %s\n",
np->name);
return -EINVAL;
}
if (info->flags & IMX8_USE_SCU) {
map_num += grp->npins;
} else {
for (i = 0; i < grp->npins; i++) {
if (!(grp->pins[i].pin_conf.pin_memmap.config &
IMX_NO_PAD_CTL))
map_num++; //统计map的数量
}
}
new_map = kmalloc(sizeof(struct pinctrl_map) * map_num, GFP_KERNEL); //分配同等数量的pinctrl_map
if (!new_map)
return -ENOMEM;
*map = new_map;
*num_maps = map_num;
/* create mux map */
parent = of_get_parent(np);
if (!parent) {
kfree(new_map);
return -EINVAL;
}
//对于第0个结构体,把group对应的节点名字,复用成function(imx6ul-evk)的功能
new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;
new_map[0].data.mux.function = parent->name;
new_map[0].data.mux.group = np->name;
of_node_put(parent);
/* create config map 创建config map */
new_map++;
for (i = j = 0; i < grp->npins; i++) {
if (info->flags & IMX8_USE_SCU) {
new_map[j].type = PIN_MAP_TYPE_CONFIGS_PIN;
new_map[j].data.configs.group_or_pin =
pin_get_name(pctldev, grp->pins[i].pin);
new_map[j].data.configs.configs =
(unsigned long *)&grp->pins[i].pin_conf.pin_scu.mux;
new_map[j].data.configs.num_configs = 2;
j++;
} else if (!(grp->pins[i].pin_conf.pin_memmap.config & IMX_NO_PAD_CTL)) {
//分别设置为设备树中对应的引脚
new_map[j].type = PIN_MAP_TYPE_CONFIGS_PIN; //type是配置值
new_map[j].data.configs.group_or_pin =
pin_get_name(pctldev, grp->pins[i].pin); //来自于imx_pin_memmap的config值
new_map[j].data.configs.configs =
&grp->pins[i].pin_conf.pin_memmap.config;
new_map[j].data.configs.num_configs = 1;
j++;
}
}
dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n",
(*map)->data.mux.function, (*map)->data.mux.group, map_num);
return 0;
}
static int add_setting(struct pinctrl *p, struct pinctrl_map const *map)
{
//....
switch (map->type) { //根据type来执行操作
case PIN_MAP_TYPE_MUX_GROUP: //如果是复用
ret = pinmux_map_to_setting(map, setting);
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP: //如果是配置
ret = pinconf_map_to_setting(map, setting);
break;
default:
ret = -EINVAL;
break;
}
//.....
return 0;
}
int pinmux_map_to_setting(struct pinctrl_map const *map,
struct pinctrl_setting *setting)
{
//....
ret = pinmux_func_name_to_selector(pctldev, map->data.mux.function); //需要把之前的名字转换成index
//....
setting->data.mux.func = ret; //设置func的值为成员的值,用来记录
ret = pmxops->get_function_groups(pctldev, setting->data.mux.func,
&groups, &num_groups); //这个功能下面有哪一些组的引脚,哪些group
//....
if (map->data.mux.group) {
group = map->data.mux.group;
ret = match_string(groups, num_groups, group); //判断data.mux.group能够复用为该function
if (ret < 0) {
dev_err(pctldev->dev,
"invalid group \"%s\" for function \"%s\"\n",
group, map->data.mux.function);
return ret;
}
} else {
group = groups[0];
}
ret = pinctrl_get_group_selector(pctldev, group); //如果可以的话,就设置
//...
setting->data.mux.group = ret; //设置group的值为成员的值,用来记录
return 0;
}
int pinconf_map_to_setting(struct pinctrl_map const *map,
struct pinctrl_setting *setting)
{
struct pinctrl_dev *pctldev = setting->pctldev;
int pin;
switch (setting->type) {
case PIN_MAP_TYPE_CONFIGS_PIN:
pin = pin_get_from_name(pctldev,
map->data.configs.group_or_pin); //从名字获得引脚的索引值(整数)
//....
setting->data.configs.group_or_pin = pin; //保存获得的信息
break;
case PIN_MAP_TYPE_CONFIGS_GROUP:
pin = pinctrl_get_group_selector(pctldev,
map->data.configs.group_or_pin); //从名字获得组的索引值(整数)
//....
setting->data.configs.group_or_pin = pin; //保存获得的信息
break;
default:
return -EINVAL;
}
//把map里的configs拷贝到setting里面来
setting->data.configs.num_configs = map->data.configs.num_configs;
setting->data.configs.configs = map->data.configs.configs;
return 0;
}
really_probe
ret = pinctrl_bind_pins(dev); //绑定引脚
dev->pins->p = devm_pinctrl_get(dev); //创建pinctrl结构体,得到一系列的setting
p = pinctrl_get(dev); //获取pinctrl结构体
return create_pinctrl(dev); //创建pinctrl结构体
dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_DEFAULT); //找到default状态
dev->pins->init_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_INIT); //找到init状态
if (IS_ERR(dev->pins->init_state)) { //如果没有init状态
ret = pinctrl_select_state(dev->pins->p,
dev->pins->default_state); //设置为default状态
ret = pinmux_enable_setting(setting); //设置复用状态
ret = ops->set_mux(pctldev, setting->data.mux.func,
setting->data.mux.group); //最终会设置复用状态
ret = pinconf_apply_setting(setting); //应用成某一种功能
ret = ops->pin_config_set(pctldev,
setting->data.configs.group_or_pin,
setting->data.configs.configs,
setting->data.configs.num_configs); //配置成某些功能
} else {
ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state); //有就设置init状态
}
dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_SLEEP); //找到sleep状态
dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_IDLE); //找到idle状态
从pinctrl的创建开始,读取设备树的信息,来构建map和setting的配置
当pinctrl构造好后,把所有的state状态一一获取,并默认配置成init状态,如果没有init状态就设置成default状态
所有的状态都是setting中的一些配置值。