MPU平台:OAMP3352
内核版本:3.2.0
声明:我讲解的范畴是从内核解压以后经过汇编代码执行最后跳到第一个C代码这个点开始讲解,一直讲到文件系统被正确的挂载起来,用户可以正常登入!至于之前的解压缩内核、汇编启动代码我会以后另开文章讲解。
目标:本文想阐述清楚OAMP335X这个平台的BSP部分的hwmod。
start_kernel -->rest_init() -->kernel_init() --> do_basic_setup() -->do_initcalls()
TI的芯片很多,面对的应用领域也很广,而且更新换代的速度也相对要快点。这样的话就带来一个问题,芯片之间的代码可重用性就显得很重要,若果你一直有开发三星MPU 的话,你可能就深有感触(S3C6410的代码里面的一些数据结构竟然会在S3C2410的头文件里面,这样的情形在TI里面也是屡见不鲜)。所以今天说的这个hwmod的这个东西就是基于这种环境下的一个产物。
TI在omap_hwmod.c文件里面有对这个hwmod的介绍,我翻译了一下如下所示:
快速的来查看OMAP SoC的一个全局概貌的方法是,可以把SOC看成是通过互相连接在一起的一些IP模块的大集合。而这个IP模块包括如ARM处理器的设备、音频串行接口、UART等等。这些设备中的一些,比如像DSP,是由TI自己创建,其他的一些设备是由其他大厂家的制造商来提供的(如SGX)。在TI的架构中,TI把芯片上的片内设备称为“OMAP 模块”。这些IP模块是跨越几个相同OMAP版本,无需频繁修订。
这些OMAP模块是由不同的互连器连接在一起。大多数模块之间的地址和数据流是通过基于OCP(一种片内模块资源通信协议)的互连(如L3和L4总线),
OMAP hwmod提供一种统一的方式来描述芯片硬件模块并将其融入到芯片的其余部分。此描述可以自动从TI生成硬件数据库(mach-omap2/omap_hwmod_33xx_data.c这个文件有详细的解释)。 OMAP hwmod提供了一个标准,一致的API如复位、使能、睡眠以及禁止这些硬件模块功能。Hwmod也提供了一种方法用于其他核心代码,如Linux设备代码或OMAP电源管理或地址空间映射代码或查询硬件数据库。
说说怎么使用这个 hwmod:
驱动不应该直接调用硬件模块功能函数(我也这么认为的,但是太多的封装就受不了,你看android做了TMD多少封装啊,难怪用过IOS以后用google的就 你懂的),这个应该由omap_device的代码来干这个事情,或是在一些极其特殊的情况下由板级代码来执行。omap_device的代码功能会使用omap_hwmod来建立一个platform_device结构体,而这种调用方式特体现了hwmode 数据和linux设备驱动模型之间的通信方式。很多的驱动只可以调用pm_runtime*()来调用omap_hwmod功能函数。
下面看看OMAP hwmod 的代码在整个内核分层结构中所处的一个位置是是怎么样的:
* +-------------------------------+
* | Device driver code |
* | (e.g., drivers/) |
* +-------------------------------+
* | Linux driver model |
* | (platform_device / |
* | platform_driver data/code) |
* +-------------------------------+
* | OMAP core-driver integration |
* |(arch/arm/mach-omap2/devices.c)|
* +-------------------------------+
* | omap_device code |
* | (../plat-omap/omap_device.c) |
* +-------------------------------+
* ----> | omap_hwmod code/data | <-----
* | (../mach-omap2/omap_hwmod*) |
* +-------------------------------+
* | OMAP clock/PRCM/register fns |
* | (__raw_{read,write}l, clk*) |
* +-------------------------------+
*
这个层次我认为应该是从下往上的一个逻辑顺序!应该是这样的 ........
设备的驱动不应该直接包含OMAP特殊平台的的代码或数据,而只应该包含去如何操作这个IP模块的代码,这也是设备驱动的职责所在。这样的话IP模块也可以在其他平台上使用(比如达芬奇),做到一个平台无关性。
OMAP的hwmod代码在设备启动过程中也可以进行复位或睡眠这个设备,这样让设备可以处于一个正确的运行状态。
OMAP硬件模块的状态:
刚一开始时候硬件模块是未知的状态,一旦被注册的话,就处于被注册状态,若果该模块的时钟被得到分配,就进入时钟已初始化状态,最后模块设置完成了就出一个已经初始化状态。
Hwmod代码也包括:处理I/O映射代码功能
总线的吞吐率和模块延时的测试代码
上面零零碎碎的啰嗦了一下这个hwmod的是个什么东西,当时 “最好的文档还是代码本身” 这是我一直奉行的观念!
第一步:L3和L4
AM3352把片内的资源之间的相互连接用一种2层分级的方式来互联,这两个分级成为L3和L4;
L3:是一个基于Network-On-Chip(NoC)协议的高性能的互联器,Network-On-Chip(NoC)使用一种内部基于包一样的协议方式来发送和接受数据命令在每个模块之间;当时呢这个基于NOC的互联器的外部接口(发送或接受)都是遵循OCPIP2.2协议的,至于这个OCPIP2.2什么个什么协议的话,请君自己去查查吧。下面是一个L3的拓补图结构(根据时钟快慢分成L3F和L3S两个区域)
其实我们可以看出时钟快的是对应处理数据量大的部分。注意有些模块既可以是主动发送者,也可以是接受者。每一个模块的发生器和接收器都唯一的ID号;至于更详细的的L3互联器介绍去看芯片文档。
L4:是一种非阻塞的外设互联器,该互联器用于访问一些低带宽和一些分散的物理模块,其总线的架构和内存映射外设如下图所示:
仔细看会发现这个L4是嵌入到L3S里面的。其具体实现可以参考芯片手册。
好了我们说了一下这个L3和L4的互联器,但是这个和我文本的目标hwmod有什么关系呢?我也觉得没什么关系,非要说有点的话我认为是搞出这个hwmod的理由之一吧。也就是说hwmod为了符合这个邮件模块而搞出来的吧!
第二步:struct omap_hwmod
这个结构体TI的解释是:
integration data for OMAP hardware "modules" (IP blocks)
struct omap_hwmod {
const char *name; //硬件模块的名称
struct omap_hwmod_class *class; //硬件模块属于哪个类(硬件都有类了)
struct omap_device *od; //硬件模块对应的具体设备
struct omap_hwmod_mux_info *mux; //硬件模块的管教复用信息
struct omap_hwmod_irq_info *mpu_irqs; //硬件模块的中断信息
struct omap_hwmod_dma_info *sdma_reqs;//硬件模块的DMA信息
struct omap_hwmod_rst_info *rst_lines; //硬件模块的设置信息
union {
struct omap_hwmod_omap2_prcm omap2;//硬件模块特殊的PRCM数据
struct omap_hwmod_omap4_prcm omap4;//硬件模块特殊的PRCM数据
} prcm;
const char *main_clk; //OMAP时钟的名字
struct clk *_clk; //主时钟
struct omap_hwmod_opt_clk *opt_clks; //其他设备的时钟
char *clkdm_name; //
struct clockdomain *clkdm;
char *vdd_name;//电压的名称
struct omap_hwmod_ocp_if **masters; /* connect to *_IA */ //IA是指发生器的代理(就是说模块和互联器之前不是直接相连的而是通过这个代理器来收发数据和命令,嗨TI搞的好复杂可见BSP和硬件的耦合性是多么的紧密!)
struct omap_hwmod_ocp_if **slaves; /* connect to *_TA *///TA是目标的代理
void *dev_attr;
u32 _sysc_cache;
void __iomem *_mpu_rt_va; //模块cache寄存器的起始地址
spinlock_t _lock;
struct list_head node;
u16 flags;
u8 _mpu_port_index;
u8 response_lat;
u8 rst_lines_cnt;
u8 opt_clks_cnt;
u8 masters_cnt;
u8 slaves_cnt;
u8 hwmods_cnt;
u8 _int_flags;
u8 _state; //模块的状态(未知、已注册、已初始化)
u8 _postsetup_state;
}
第三步: omap_hwmod_33xx_data.c
该文件里面定义了所有的片内资源(以hwmod形式);
int __init am33xx_hwmod_init(void)
{
return omap_hwmod_register(am33xx_hwmods); //注册了所有的片内模块资源并以链表形式链接在一起!omap_hwmod_list
}
第四步:omap_hwmod.c
这个文件使用于 OMAP2/3/4 平台。
static int __init omap_hwmod_setup_all(void)
{
int r;
if (!mpu_oh) {
pr_err("omap_hwmod: %s: MPU initiator hwmod %s not yet registered\n",
__func__, MPU_INITIATOR_NAME);
return -EINVAL;
}
//为每一个模块找到自己的寄存器虚拟首地址
r = omap_hwmod_for_each(_populate_mpu_rt_base, NULL);
//为每一个模块初始化时钟
r = omap_hwmod_for_each(_init_clocks, NULL);
WARN(IS_ERR_VALUE(r),
"omap_hwmod: %s: _init_clocks failed\n", __func__);
//为每一个模块进行配置操作
omap_hwmod_for_each(_setup, NULL);
return 0;
}
core_initcall(omap_hwmod_setup_all);
同时这个文件还提供了:
/*对一个模块进行分配相应的资源(中断、寄存器地址、内存空间、DMA资源)*/
int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res)
/*得到一个目标模块寄存器的虚拟首地址*/
void __iomem *omap_hwmod_get_mpu_rt_va(struct omap_hwmod *oh)
/*读或写一个值到某个模块的一个寄存器里面*/
u32 omap_hwmod_read(struct omap_hwmod *oh, u16 reg_offs)
void omap_hwmod_write(u32 v, struct omap_hwmod *oh, u16 reg_offs)
/*通过名字找到一个对应的模块*/
struct omap_hwmod *omap_hwmod_lookup(const char *name)
/*对一个模块进行复位*/
/*对一个模块进行使能*/
/*对一个模块进行睡眠*/
/*对一个模块进行停止*/
/*对一个模块进行使能和禁止时钟*/
/*对一个模块:应许这个模块唤醒张整个系统*/
还有很多的其他函数用于操作这个硬件模块,君可以自己去查看;
总结:前前后后的讲了这么多的hwmod;
至少明确了:为什么TI要高一套hwmod的软件机制(硬件使用了这套L3和L4的硬件模块互联机制);
至少明确了:hwmod在BSP启动中充当一个什么的角色(一个hwmod封装了所有的当前属于这个硬件模块的所有资源,以及对硬件模块的操作功能函数,注意这个是和平台有关性的,所以omap_device.c(基于linux驱动模型的方式)对这些功能函数又进行一次平台无关性的封装,这个下一节讲。)
明确了:真正的BSP代码是和硬件的耦合性很大的,这一部分的代码才是水平的体现,你不仅要对整个硬件体系架构很清楚而且还要对每个硬件资源的性能有使用经验,而且还要把这些东西抽象出一套符合平台型的一个软件架构。最后这个架构还要很好的整合到linux那套底层BSP架构中。而且还要考虑到内核的一系列问题(性能、资源利用、调度、通信协议等等)。这些基本上都是芯片厂商的事情,等我们拿到一块TI的评估板 这一切的一切 都已经做的很好了,连顶层的应用程序都在一个SDK的包里面提供给用户,所以我们工作中很多的是软件方面事情(至少中国国内的整个大环境是这样的)。
原文链接:https://blog.csdn.net/zhou13454069844/article/details/16963853
原文链接:https://blog.csdn.net/zhou13454069844/article/details/16963731