Hi3519-pinctrl驱动开发③consumer driver与pinctrl子系统交互

前一篇文章介绍了如果创建consumer driver到pinctrl driver的mapping table,这个mapping table就是系统的配置表,在系统启动之初注册到pinctrl sub system中去,貌似是在pinctrl driver注册之前就注册了mapping table了。既然已经有了mapping table,那我们假设pinctrl driver也已经“编写完成”(假设可以用了),这时候编写一个设备驱动的时候需要使用pinctrl sub system提供的服务,这时候设备驱动如何和pinctrl sub system交互呢?
有一个概念需要理解,就是“state”,也就是“状态”,每个设备对应第一个引脚组,继续以spi0设备为例,假设spi0设备对应的引脚组有两个,分别是PA0、1、2、3和PB5、6、7、8。state表示的就是设备使用的引脚组中引脚的状态,往常单片机开发中没有这个概念,但是在linux中为了提供足够的便利性和功能支持使用了state这个概念,具体来说当设备spi0正常运行的时候引脚状态处于正常状态,一般叫做default状态,当spi0设备处于关闭或者说低功耗模式下的时候引脚状态处于低功耗状态,一般叫做idle状态或者sleep状态(引脚的低功耗状态可能就是高阻态)。这样看来一个设备的pins可以处于不同的状态,pinctrl sub system提供了一种机制:pinctrl state holder,这个holder是一个容器,每一个设备都对应一个pinctrl state holder,用来存放设备拥有的所有states,pinctrl 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 
};

pinctrl state holder结构体的名称为“pinctrl”,而pinctrl驱动的描述结构体的名称为“pinctrl_desc”,真的很郁闷为什么这么命名,我觉得可以是“pinctrl_state_holder”和“pinctrl”,这样对于理解会方便很多。
pinctrl state holder结构体中的states元素存放的就是device的state链表,state元素存放的是当前device处于的状态。说到这里state显得很神秘,state是由结构体pinctrl_state 描述的:

struct pinctrl_state {
        struct list_head node;---链表元素
        const char *name;---state的名称
        struct list_head settings;---设置
};

pinctrl_state 前两个元素很好理解,最后一个settings元素表示的是要设置成这个state的话要进行的配置,settings也是一个链表,也就是有多个setting。setting是结构体struct pinctrl_setting:

struct pinctrl_setting {
        struct list_head node;---链表元素
        enum pinctrl_map_type type;---map type
        struct pinctrl_dev *pctldev;---pinctrl驱动
        const char *dev_name;---使用这个state的设备名
        union {
                struct pinctrl_setting_mux mux;
                struct pinctrl_setting_configs configs;
        } data;
};

这个结构体和struct pinctrl_map结构体很像,type为PIN_MAP_TYPE_MUX_GROUP时data为mux,struct pinctrl_setting_mux的定义如下:

struct pinctrl_setting_mux {
        unsigned group;---the group selector to program
        unsigned func;---the function selector to program
};

其中group就是引脚组的索引,func就是功能的索引。state的组织原理图如下图:
这里写图片描述
state概念介绍完了,那么说每个device对应的pinctrl state holder中的states中的settings是怎么来的,难道说是device自己定义的?显然不能,这样的话驱动会有太多没用的程序,而且可移植性很差。具体是怎么创建device的states和state中的settings呢?我也没研究过,我猜测是这样的:mapping table在系统启动之初就建立好了,注册到pinctrl sub system中,在建立pinctrl state holder的时候pinctrl子系统会根据对应的mapping table中的mapping entry创建states和settings,先假设是这么回事。现在已经“知道了”states和settings是在建立pinctrl state holder的时候创建的,那么pinctrl state holder是如何创建的呢?
pinctrl state holder是动态定义的,且并不是每个device都对应一个pinctrl state holder,因为有的device是不用引脚的。需要引脚的device需要获取属于自己的pinctrl state holder,使用的pinctrl sub system接口函数是devm_pinctrl_get和pinctrl_get,前者是后者的resource managed版本。单看后者pinctrl_get函数:

868 struct pinctrl *pinctrl_get(struct device *dev)
869 {
870         struct pinctrl *p;
871 
872         if (WARN_ON(!dev))
873                 return ERR_PTR(-EINVAL);
874 
875         /*
876          * See if somebody else (such as the device core) has already
877          * obtained a handle to the pinctrl for this device. In that case,
878          * return another pointer to it.
879          */
880         p = find_pinctrl(dev);
881         if (p != NULL) {
882                 dev_dbg(dev, "obtain a copy of previously claimed pinctrl\n");
883                 kref_get(&p->users);
884                 return p;
885         }
886 
887         return create_pinctrl(dev);
888 }

第880行中的find_pinctrl函数用来查找当前系统是否已经定义了该device的pinctrl state holder,如果没有定义的话就调用887行中的 create_pinctrl函数创建该device的pinctrl state holder。create_pinctrl如下:

 788 static struct pinctrl *create_pinctrl(struct device *dev)
 789 {
 790         struct pinctrl *p;
 791         const char *devname;
 792         struct pinctrl_maps *maps_node;
 793         int i;
 794         struct pinctrl_map const *map;
 795         int ret;
 796 
 797         /*
 798          * create the state cookie holder struct pinctrl for each
 799          * mapping, this is what consumers will get when requesting
 800          * a pin control handle with pinctrl_get()
 801          */
 802         p = kzalloc(sizeof(*p), GFP_KERNEL);
 803         if (p == NULL) {
 804                 dev_err(dev, "failed to alloc struct pinctrl\n");
 805                 return ERR_PTR(-ENOMEM);
 806         }
 807         p->dev = dev;
 808         INIT_LIST_HEAD(&p->states);
 809         INIT_LIST_HEAD(&p->dt_maps);
 810 
 811         ret = pinctrl_dt_to_map(p);
 812         if (ret < 0) {
 813                 kfree(p);
 814                 return ERR_PTR(ret);
 815         }
 816 
 817         devname = dev_name(dev);
 818 
 819         mutex_lock(&pinctrl_maps_mutex);
 820         /* Iterate over the pin control maps to locate the right ones */
 821         for_each_maps(maps_node, i, map) {
 822                 /* Map must be for this device */
 823                 if (strcmp(map->dev_name, devname))
 824                         continue;
 825 
 826                 ret = add_setting(p, map);
 827                 /*
 828                  * At this point the adding of a setting may:
 829                  *
 830                  * - Defer, if the pinctrl device is not yet available
 831                  * - Fail, if the pinctrl device is not yet available,
 832                  *   AND the setting is a hog. We cannot defer that, since
 833                  *   the hog will kick in immediately after the device
 834                  *   is registered.
 835                  *
 836                  * If the error returned was not -EPROBE_DEFER then we
 837                  * accumulate the errors to see if we end up with
 838                  * an -EPROBE_DEFER later, as that is the worst case.
 839                  */
 840                 if (ret == -EPROBE_DEFER) {
 841                         pinctrl_free(p, false);
 842                         mutex_unlock(&pinctrl_maps_mutex);
 843                         return ERR_PTR(ret);
 844                 }
 845         }
 846         mutex_unlock(&pinctrl_maps_mutex);
 847 
 848         if (ret < 0) {
 849                 /* If some other error than deferral occured, return here */
 850                 pinctrl_free(p, false);
 851                 return ERR_PTR(ret);
 852         }
 853 
 854         kref_init(&p->users);
 855 
 856         /* Add the pinctrl handle to the global list */
 857         mutex_lock(&pinctrl_list_mutex);
 858         list_add_tail(&p->node, &pinctrl_list);
 859         mutex_unlock(&pinctrl_list_mutex);
 860 
 861         return p;
 862 }

简单看一下程序,802行分配pinctrl state holder内存空间,811行使用函数pinctrl_dt_to_map创建mapping table,这是使用DTS的方式创建的,我们使用的是Machine Driver的方式创建的,结果都是一样的,都是创建一个mapping table,只是DTS方式更加灵活方便,不需要修改内核代码就能系统支持不同的Board。这里有个疑问,没有使用DTS方式的话pinctrl_dt_to_map函数是不是会返回错误呢?这样就会在814行返回了,返回的话后面的初始化就没有得到执行,我看了一下pinctrl_dt_to_map函数的部分源码:

175 int pinctrl_dt_to_map(struct pinctrl *p)
176 {
177         struct device_node *np = p->dev->of_node;
178         int state, ret;
179         char *propname;
180         struct property *prop;
181         const char *statename;
182         const __be32 *list;
183         int size, config;
184         phandle phandle;
185         struct device_node *np_config;
186 
187         /* CONFIG_OF enabled, p->dev not instantiated from DT */
188         if (!np) {
189                 if (of_have_populated_dt())
190                         dev_dbg(p->dev,
191                                 "no of_node; not parsing pinctrl DT\n");
192                 return 0;
193         }
194 
195         /* We may store pointers to property names within the node */
196         of_node_get(np);
......

117行中获取p->dev->of_node,188行判断np是否为null,如果为null表示没有使用Device Tree机制,同时返回0表示没有错误。回到create_pinctrl函数,这样一来pinctrl_dt_to_map并没有完成解析DT来创建mapping table的任务,因为根本没有DT。接下里821行的“for_each_maps(maps_node, i, map)”其实就是遍历了系统全局变量pinctrl_maps链表中的每个pinctrl_maps,宏展开后是这样的:

list_for_each_entry(maps_node, &pinctrl_maps, node)
        for (i=0,map=&maps_node->maps[i];i<maps_node->num_maps;i++,map=&maps_node>maps[i])

list_for_each_entry的含义参见文章:http://blog.csdn.net/tq384998430/article/details/73188601,简单来说就是list_for_each_entry宏中maps_node是一个迭代变量,&pinctrl_maps是链表头指针,node是结构体中的元素名称。整体意思就是从链表pinctrl_maps中挨个获取pinctrl_maps元素赋值给maps_node,然后循环将maps_node中的元素赋值给map进入循环中执行。注意这里两个pinctrl_maps的区别,一个pinctrl_maps是core.c文件中定义的链表全局变量:

59:LIST_HEAD(pinctrl_maps);

另一个是结构体struct pinctrl_maps。前者pinctrl_maps链表中的元素都是struct pinctrl_maps类型。
下面再看create_pinctrl函数的826行,add_setting,很显然这是通过map元素来创建pinctrl state holder中的state和settings,add_setting函数源码就不看了,意图很明显。经过list_for_each_entry循环和第二层的for循环后将所有与创建pinctrl state holder的device相关的maps都生成了相应的states和settings,这样consumer driver就能使用这些个states了。下面来说具体怎么使用定义好了的state holder中的states。
前面使用pinctrl_get已经创建了device的pinctrl state holder,并且根据mapping table初始化了pinctrl state holder中的states和settings(对应的devm_pinctrl_put和pinctrl_put两个函数用于释放pinctrl state holder资源)。现在假设一个场景是spi0设备驱动从pinctrl sub system中获取到了自己的pinctrl state holder,然后它要设置自己需要的引脚的状态到default状态,那么它可以使用函数pinctrl_lookup_state,函数原型为:

struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name);

这个函数根据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_lookup_state示例:

dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,PINCTRL_STATE_DEFAULT);

获取到了default_state之后可以调用pinctrl_select_state函数:

int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state);

这个函数用来选中一个特定的state,意思就是设置引脚成该state所描述的状态,这个设置过程就是由pinctrl驱动完成的了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值