Linux pinctrl subsystem

最近学习linux中GPIO subsystem和pinctrl subsystem相关知识,查阅了一些资料,在这里与大家分享一下。
主要参考了:
[http://www.wowotech.net/linux_kenrel/pin-control-subsystem.html] 文档。


pin control subsystem的软件框架图

pin control subsystem的主要功能包括:

  • 管理系统中所有可以控制的pin。在系统初始化的时候,枚举所有可以控制的pin,并标识这些pin。
  • 管理这些pin的复用(Multiplexing)。对于SOC而言,其引脚除了配置成普通GPIO之外,若干个引脚还可以组成一个pin group,形成特定的功能。例如pin number是{ 0, 8, 16, 24 }这四个引脚组合形成一个pin group,提供SPI的功能。pin control subsystem要管理所有的pin group。
  • 配置这些pin的特性。例如配置该引脚上的pull-up/down电阻,配置drive strength等。

pin control subsystem在和其他linux内核模块的接口关系图


pin control subsystem内部block diagram

pin control subsystem向其他driver提供的接口

普通driver调用pin control subsystem的主要目标是:

  • 设定该设备的功能复用。设定设备的功能复用需要了解两个概念,一个是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,毕竟芯片内部的SPI0的逻辑功能电路只有一个。 因此,只有给出function selector(所谓selector就是一个ID或者index)以及function的pin group selector才能进行function mux的设定。
  • 设定该device对应的那些pin的电气特性。

同时为了表示device pin的不同状态(idle、sleep等),定义了pin control state的概念,device driver可以切换设备处于的状态。为了方便管理pin control state,使用一个pin control state holder的概念,用来管理一个设备的所有的pin control状态。因此普通driver调用pin control subsystem的接口从逻辑上将主要是:

  • 获取pin control state holder的句柄。
  • 设定pin control状态。
  • 释放pin control state holder的句柄。
    pin control state holder的定义如下:
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;  //reference count 
};

系统中的每一个需要和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;    //列表中值的个数 
};

pinctrl 接口

  • devm_pinctrl_get和pinctrl_get
    devm_pinctrl_get是Resource managed版本的pinctrl_get,系统会帮助我们管理并释放资源。这两个接口都是获取设备(设备模型中的struct device)的pin control state holder(struct pinctrl)。pin control state holder不是静态定义的,一般在第一次调用该函数的时候会动态创建。

  • devm_pinctrl_put和pinctrl_put
    devm_pinctrl_getpinctrl_get获取句柄的时候申请了很多资源,在devm_pinctrl_put和pinctrl_put可以释放。需要注意的是多次调用get函数不会重复分配资源,只会reference count加一,在put中referrenct count减一,当count==0的时候才释放该device的pin control state holder持有的所有资源。

  • pinctrl_lookup_state
    根据state name在pin control state holder找到对应的pin control state。具体的state是各个device自己定义的,不过pin control subsystem自己定义了一些标准的pin control state,定义在pinctrl-state.h文件中:

#define PINCTRL_STATE_DEFAULT "default" 
#define PINCTRL_STATE_IDLE "idle" 
#define PINCTRL_STATE_SLEEP "sleep"
  • pinctrl_select_state
    设定一个具体的pin control state接口。

pinctrl subsystem和GPIO subsystem交互

  • pinctrl_request_gpio。
    该接口主要用来申请GPIO。GPIO也是一种资源,使用前应该request,使用完毕后释放。具体的代码如下:
int pinctrl_request_gpio(unsigned gpio)   //这里传入的是GPIO 的ID 
{ 
    struct pinctrl_dev *pctldev; 
    struct pinctrl_gpio_range *range; 
    int ret; 
    int pin;
    ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);    //A 
    if (ret) { 
        if (pinctrl_ready_for_gpio_range(gpio)) 
            ret = 0; 
        return ret; 
    }
    mutex_lock(&pctldev->mutex);  
    pin = gpio_to_pin(range, gpio);   //将GPIO ID转换成pin ID
    ret = pinmux_request_gpio(pctldev, range, pin, gpio);     //B
    mutex_unlock(&pctldev->mutex);
    return ret; 
}

A:根据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。
B:这里主要是进行复用功能的设定,毕竟GPIO也是引脚的一个特定的功能。pinmux_request_gpio函数的作用主要有两个,一个是在core driver中标记该pin已经用作GPIO了,这样,如果有模块后续request该资源,那么core driver可以拒绝不合理的要求。第二步就是调用底层pin controller driver的callback函数,进行底层寄存器相关的操作。

  • pinctrl_free_gpio
    有申请就有释放,这是pinctrl_request_gpio的逆函数。
  • pinctrl_gpio_direction_input和pinctrl_gpio_direction_output
    为已经指定为GPIO功能的引脚设定方向,输入或者输出。

pinctrl subsystem 和驱动模型的接口

  • 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);  
    dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,  
                    PINCTRL_STATE_DEFAULT);
    ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state);  

    dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_SLEEP);  
    dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p,  PINCTRL_STATE_IDLE);  
    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;   //电源管理相关的状态 
};

该函数主要执行了以下步骤:

  • 调用devm_pinctrl_get获取该device对应的 pin control state holder句柄。
  • 搜索default state,sleep state,idle state并记录在本device中。
  • 将该设备设定为pin default state。

pinctrl subsystem 和device tree相关的接口

device tree模块主要是为 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; 
};
  • 通过device tree来建立pin mapping database。
    pin mapping信息定义在dts中,主要包括两个部分,一个是定义在各个具体的device node中,另外一处是定义在pin controller的device node中。主要包括两个部分,一个是定义在各个具体的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);  
    for (state = 0; ; state++) {    //(1)
        /* Retrieve the pinctrl-* property */ 
        propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state); 
        prop = of_find_property(np, propname, &size);  
        kfree(propname); 
        if (!prop) 
            break; 
        list = prop->value; 
        size /= sizeof(*list);    //(2)
        /* Determine whether pinctrl-names property names the state */ 
        ret = of_property_read_string_index(np, "pinctrl-names", state, &statename);  //(3)

        if (ret < 0) { 
            /* strlen("pinctrl-") == 8 */ 
            statename = prop->name + 8;  //(4)
        }
        /* For every referenced pin configuration node in it */ 
        for (config = 0; config < size; config++) { //(5)
            phandle = be32_to_cpup(list++);
            /* Look up the pin configuration node */ 
            np_config = of_find_node_by_phandle(phandle); //(6)
            /* Parse the node */ 
            ret = dt_to_map_one_config(p, statename, np_config);  //(7)
            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);    //(8)
            if (ret < 0) 
                goto err; 
        } 
    }
    return 0;
err: 
    pinctrl_dt_free_maps(p); 
    return ret; 
}

上述函数主要完成了以下步骤:

  • pinctrl-0 pinctrl-1 pinctrl-2……表示了该设备的一个个的状态,这里我们定义了两个pinctrl-0和pinctrl-1分别对应sleep和default状态。这里每次循环分析一个pin state。
  • 代码执行到这里,size和list分别保存了该pin state中所涉及pin configuration phandle的数目以及phandle的列表读取从pinctrl-names属性中获取state name
  • 如果没有定义pinctrl-names属性,那么我们将pinctrl-0 pinctrl-1 pinctrl-2……中的那个ID取出来作为state name
  • 遍历一个pin state中的pin configuration list,这里的pin configuration实际应该是pin controler device node中的sub node,用phandle标识。
  • 用phandle作为索引,在device tree中找他该phandle表示的那个pin configuration
  • 分析一个pin configuration,具体下面会仔细分析
  • 如果该设备没有定义pin configuration,那么也要创建一个dummy的pin state。
    这里我们已经进入对pin controller node下面的子节点的分析过程了。分析一个pin configuration的代码如下:
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;
    /* Find the pin controller containing np_config */ 
    np_pctldev = of_node_get(np_config); 
    for (;;) { 
        np_pctldev = of_get_next_parent(np_pctldev);//(1) 
        if (!np_pctldev || of_node_is_root(np_pctldev)) { 
            of_node_put(np_pctldev); 
            return -EPROBE_DEFER; 
        } 
        pctldev = get_pinctrl_dev_from_of_node(np_pctldev);  //(2) 
        if (pctldev) 
            break;  //(3) 
        /* 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; 
    ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);   //(4) 
    if (ret < 0) 
        return ret;
    /* Stash the mapping table chunk away for later use */ 
    return dt_remember_or_free_map(p, statename, pctldev, map, num_maps);  //(5) 
}
  1. 首先找到该pin configuration node对应的parent node(也就是pin controler对应的node),如果找不到或者是root node,则进入出错处理。
  2. 获取pin control class device 。
  3. 一旦找到pin control class device则跳出for循环。
  4. 调用底层的callback函数处理pin configuration node。这也是合理的,毕竟很多的pin controller bindings是需要自己解析的。
  5. 将该pin configuration node的mapping entry信息注册到系统中。

core driver和low level pin controller driver的接口规格

pin controller描述符。每一个特定的pin controller都用一个struct pinctrl_desc来抽象,具体如下:

struct pinctrl_desc { 
    const char *name; 
    struct pinctrl_pin_desc const *pins; 
    unsigned int npins; 
    const struct pinctrl_ops *pctlops; 
    const struct pinmux_ops *pmxops; 
    const struct pinconf_ops *confops; 
    struct module *owner; 
};

pin controller描述符需要描述它可以控制多少个pin(成员npins),每一个pin的信息为何?(成员pins)。这两个成员就确定了一个pin controller所能控制的引脚的信息。
pin controller描述符中包括了三类操作函数:pctlops是一些全局的控制函数,pmxops是复用引脚相关的操作函数,confops操作函数是用来配置引脚的特性(例如:pull-up/down)。struct pinctrl_ops中各个callback函数的具体的解释如下:

call back函数描述
requestpin control core进行具体的复用设定之前需要调用该函数,主要是用来请底层的driver判断某个引脚的复用设定是否是OK的。
free是request的逆函数。调用request函数请求占用了某些pin的资源,调用free可以释放这些资源
get_functions_count就是返回pin controller支持的function的数目
get_function_name给定一个selector(index),获取指定function的name
get_function_groups给定一个selector(index),获取指定function的pin groups信息
enableenable一个function。当然要给出function selector和pin group的selector
disableenable的逆函数
gpio_request_enablerequest并且enable一个单独的gpio pin
gpio_disable_freegpio_request_enable的逆函数
gpio_set_direction设定GPIO方向的回调函数

配置引脚的特性的struct pinconf_ops数据结构的各个成员定义如下:

call back函数描述
pin_config_get给定一个pin ID以及config type ID,获取该引脚上指定type的配置。
pin_config_set设定一个指定pin的配置
pin_config_group_get以pin group为单位,获取pin上的配置信息
pin_config_group_set以pin group为单位,设定pin group的特性配置
pin_config_dbg_parse_modifydebug接口
pin_config_dbg_showdebug接口
pin_config_group_dbg_showdebug接口
pin_config_config_dbg_showdebug接口

参考文档:
http://www.wowotech.net/linux_kenrel/pin-control-subsystem.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值