上一章我们对CCF子系统进行了阐述,本章我们将介绍CCF子系统内部设计,说明CCF子系统内部是怎样实现的。我们主要从如下几点说明CCF子系统的内部设计流程:
一、CCF子系统内部数据结构的定义与关联
二、CCF子系统提供的插口
一、CCF子系统内部数据结构的定义与关联
在我们学习linux内核各子系统模块时,通过其内部数据结构的定义及关联,即可大致把握其实现过程及所须要提供的插口等等(本次剖析CCF子系统是基于linux4.4内核的)。
如下即为CCF子系统内部数据结构间的关联,可以分为两大部份:
在clksource注册时,提供clksource的初始配置信息(包含该clksource的操作插口、parentclk的个数及每一个parentclk的名称),即为图中绿色圈圈中的内容,涉及的数据结构主要为structclk_init_data、structclk_ops。Clk_soure注册完成后,clkprovider、clkconsumer之间的关联信息。
在右图的数据结构间的关联中,涉及了CCF中clkprovider、clkconsumer的主要具象内容,下边对这几个数据结构进行说明:
structclk_hw则为硬件时钟源的逻辑具象可以理解为clkprovider;structclk_core则可以理解为clkprovider的driver,包含了clkprovider的访问方式、时钟频度设定、支持的时钟频度、父时钟源的个数以及父时钟源的名称等内容;而structclk则可以理解clkconsumer,包含了指向clkprovider的表针、使用者的设备名称、使用者所定义的时钟别称(con_id)。structclk_ops则主要是clkprovider的操作插口,包含prepare与unprepare操作插口(这两个插口执行时容许sleep,主要是用的互斥锁)、enable与disable插口(这两个插口执行时不准许sleep,上面使用了载流子锁),也包含了速度设置插口、重新估算速度插口、parent设置与获取插口等等,通过这种插口可实现时钟频度的更改、父时钟源的选择、时钟的使能与否等功能。
从我们剖析的那么多的外设子系统而言,读者有没有发觉该CCF子系统的数据结构中没有使用设备驱动模型linux uart驱动分析,没包含structdevice类型的变量。这应当是clkprovider主要为系统提供时钟,而一些系统clk的初始化及使能会早于设备驱动模型的初始化,因而没有使用设备驱动模型,而且还是使用了引用计数功能的。
前面介绍的是一个clkprovider的数据结构关联,而多个clkprovier之间有哪些关联呢?
级联clkprovider的关联
由于clk之间是支持多级级联的,而一个clk既可以由多个可选的父时钟源、也可以向多个子时钟提供时钟源,因而多个clkprovider之间会产生树状的关联结构,如右图所示。
系统中所有注册的clkprovider的关联
里面主要说明了存在母女关系的clkprovider之间的关联,那系统上所有已注册的clkprovider之间有哪些关联呢?这主要有两种情况:
若一个clkprovider是一个rootclkprovider,即其不须要父节点提供时钟,则将其插入到数组clk_root_list;若一个clkprovider不是一个rootclkprovider,然而其父节点尚没有注册到系统中,则将其插入到数组clk_orphan_list中。
右图是以数组clk_root_list为例画出系统中已注册clkprovider之间的关联。即所有的rootclkprovider均在数组clk_root_list中;而所有的childclkprovider则主要在其parentclkprovider的child_node数组中。
当系统中注册一个clkprovider中,其即会在上述的clkprovider关联数组中linux uart驱动分析,在里面的关联图中,你们有没有发觉一个问题?里面的关联图中涉及多个数组,那假如搜索一个clkprovider岂不是很麻烦呢?这就牵涉到已注册clkprovider的搜索问题。
已注册clkprovider的搜索问题
当clkconsumer须要使用一个clkprovider,则涉及到clkprovider的搜索,在完成clkprovider的搜索后RAR FOR LINUX,方创建clkconsumer对象(structclk),并加入到clkprovider的clks数组中。并且若通过clk_root_list数组进行一级一级的搜索似乎时花费时间的,那怎么优化搜索时间呢?还记得我们之前在regulator子系统、pinctrl子系统的剖析时,均实现了对应的map关系,因而优化搜索时间,而在CCF子系统中,也提供了类似的map操作。
我们晓得LINUX内核的升级过程中降低了OF机制(即设备树),而LINUX内核呢也并没有强制要求所有的arch均支持OF,因而现今的内核既有支持OF机制,也存在不支持OF机制。因而在CCF子系统的map操作,也涉及到这两块的实现。
设备树机制下clk_core的map
若系统支持设备树时,则主要通过structdevice_node作为搜索关键字(一个structdevice_node对应设备树中的一个节点)。
在clkproviderdriver中,当clkprovider完成注册后,针对支持设备树机制的内核,则clkproviderdriver会将该clkprovider的map(即structof_clk_provider)信息,加入到数组of_clk_providers中;在clkconsumer中,则在该clkconsumer的设备数据节点中引用clkprovider对应的节点即可。
不支持设备树机制下clk_core的map
若系统不支持设备树时,则主要通过devicename、clkconfigname作为搜索关键字(若对于一个clkprovider,其名称为sys_clk4linux操作系统简介,其可为uart、mmc提供时钟(名称分别为uart_clk、mmc_clk),则可以通过名称“uart_clk/mmc_clk”作为搜索关键字)。
在clkproviderdriver中,当clkprovider完成注册后,则clkproviderdriver会将该clkprovider的map(即structclk_lookup类型的变量)信息,加入到数组clocks中(如针对里面的事例,则创建两个strctclk_lookup类型的变量(con_id分别为“uart_clk”、“mmc_clk”),并加入到clocks数组中),完成该clkproviderdrivermap的注册;在clkconsumer中,则在该clkconsumer的驱动中,在数组clocks中搜索即可找到对应的clkproviderdriver。
二、CCF子系统提供的插口
我们理解上述中学的内容后,基本上也可大致晓得CCF子系统须要提供的插口,大致可分为如下几类:
clkprovider的注册插口,该插口实现哪些功能呢,实现一个clkprovider的逻辑具象对象structclk_hw类型变量的创建、clkproviderdriver的创建(structclk_core)、clkprovider的操作插口、父子clkprovider的关联等等信息,主要完成上述图一、图二、图三的关联,主要是插口clk_register;提供clkprovider的map映射,便捷clkprovider的查找,主要完成上述图一中的关联,主要是插口of_clk_add_provider、clkdev_add;提供时钟使能、去使能、时钟频度设置及更新、parentclkprovider设置与查找等插口,这主要是对structclk_ops提供插口的封装,提供统一的对外插口,内部再依据具体的clkproviderdriver实现的clk_ops操作完成与具体的hwclk的通讯,主要为插口clk_enable、clk_disable、clk_prepare、clk_unprepare、clk_set_rate等;提供clkconsumer的创建及获取插口,主要是通过clkprovidermap数组中查找对应的clkprovider,并创建该clkprovider的使用者,并把使用者的信息放在该clkproviderdriver的数组中。主要为插口clk_get、clk_put、
基本上就是提供以上几种类型的插口。
以上即为本章的主要内容,本章主要介绍CCF子系统内部的实现机制,希望对你们有所帮助。