DM365的BSP主要包含mach-davinci和plat-davinci两个目录(及相关头文件),BSP复杂庞大又极其重要,它主要完成了板级的初始化,比如内存映射,时钟和电源初始化,中断和IO初始化,CPU及各模块的初始化,相关平台设备,总线设备的注册等等。下面就分板一下DM365的BSP部分,以程序流程为纵线,以各个文件为横线,进行分析。
内核在经过一系列的初始化后会进行板级的初始化,主要依靠一个MACHINE_START宏,DM365的板定义宏在board-dm365-evm.c文件中,这个宏有以下几个成员phys_io,io_pg_offst,boot_params,map_io,init_irq,timer,init_machine。这些成员变量或函数将会由内核在初始化时在适当的时机调用。DM365开发板对这几个成员都有针对其开发板的内容,phy_io设置为0x01c00000,这是DM365寄存器的起始地址。boot_params为0x80000100,它在DDR中,是用来存放UBOOT的传递参数的。map_io会调用davinci_map_io函数,完成开发板的静态地址映射,同时开发板中此函数还完成了MUX的功能初始化及各模块时钟的初始化。init_irq会调用davinci_dm365_evm_irq_init函数,对开发板的中断系统进行初始化。timer会调用davinci_timer相关的成员,它完成开发板的时间管理初始化。init_machine调用dm365_evm_init函数,完成板级设备(部分)的初始化。下面我们从davinci_map_io函数开始。
davinci_map_io函数主要由三个部分组成,第一是中断优先级的初始化,中断优先级的初始化就是将全局中断优先级结构指针davinci_irq_priorities指向一个默认的中断优先级数组dm365_default_priorities,这个第二是调用davinci_map_common_io函数完成静态地址映射,第三是调用davinci_init_common_hw函数完成MUX的功能初始化及各模块时钟的初始化。
davinci_map_common_io函数最主要就是完成了一段物理地址的静态虚拟地址映射,此后程序中操作这段物理地址的内容时不必再用ioremap动态得到一个虚拟地址了。至于如何做到映射的,不用去深究,只需要在map_desc结构定义的本平台使用的davinci_io_desc[]数组中中添加我们需要映射的地址段就可以了。开发板原程序中只映射了平台寄存器部分的地址空间,其物理起始地址,地址段大小,欲映射成的虚拟起始地址等值填入这个结构中就可以了。这些值定义中io.h文件中,其中物理起始地址IO_PHYS为0x01c00000,这正是寄存器物理地址段的起始地址,地址段大小IO_SIZE为0x00400000,这两个值由手册都可以知道。至于欲映射成的虚拟起始地址则由我们人为决定,可以选用IO_VIRT为0xfbc00000,这样物理地址和虚拟地址就有一个简单的偏移关系IO_OFFSET为0xfa000000。有了这个偏移关系,物理地址便和虚拟地址随时进行变换了,我们可以封装一个宏IO_ADDRESS用来做为物理地址到虚拟地址的转换。这里需要特别注意的,物理地址和虚拟地址的这层映射关系是内核来确定的,而两个地址的简单偏移关系是我们人为决定的,为了处理简单我们选了一个与物理地址有简单偏移关系的地址作为虚拟地址,并让内核去认同。静态映射一定要搞清楚这里面的东西,一开始很容易被迷惑的。davinci_map_common_io函数完成以后,接着是davinci_init_common_hw函数。
davinci_init_common_hw函数极其重要,它里面只有两个函数,davinci_mux_init函数完成模块引脚复用功能的初始化,davinci_clk_init函数完成模块时钟的初始化。
davinci_mux_init函数将各模块的复用进行初始化。它首先将几个重要的结构赋值,davinci_pins是二维指针,它被赋给一个和平台有关的默认的全局结构,比如dm365_pins,它是一个二维数组,分别定义了若干模块及每个模块需要用到的引脚,注意这个维数的索引值是对应模块在PSC管理中的索引值的。davinci_num_pins就是dm365_pins数组的维数。定义一个pin_config结构的指针table,并指向一个默认的全局结构数组davinci_dm365_pinmux,并得到这个数组的维数size。davinci_dm365_pinmux数组极其重要,它的每一维元素都是一个pin_config结构,它是用一个MUX_CFG宏来构造的,用于模块引脚复用功能的选择,实质是构造了欲对PINMUXx寄存器操作的位和欲写入的值。这个要好好理解,配合手册中PINMUXx寄存器理解。几个结构赋值以后就调用davinci_mux_register函数,这个函数将table赋给了全局结构指针pin_table,将size赋给全局变量pin_table_sz,将davinci_get_pins函数地址赋给函数指针get_pin_list,将一个标识用到哪一个pinmux寄存器的数组首址赋给全局指针pinmux_in_use。davinci_get_pins这个函数的作用是通过参数ctr和id来返回davinci_pins数组的某一维,它表示某一个模块用到的引脚。
davinci_clk_init函数完成模块时钟的初始化。它首先将PSC寄存器的地址赋给全局指针psc_bases,然后通过读取PLL相关的寄存器得到预分频,倍频,后分频,分频等值(注意这些设置都在UBL进行设置好了,这里仅仅是读值),这样就得到了各个模块的时钟值,把它们赋给若干全局变量,比如armrate,voicerate,commonrate,vpssrate,ddrrate等。然后定义一个clk结构的指针clk_list,并指向一个默认的全局结构数组davinci_dm365_clks,并得到这个数组的维数num_clks。davinci_dm365_clks数组的每一维元素都是一个clk结构,定义了平台各个模块的时钟管理参数,包括模块名,时钟值,本模块在电源域的索引序号等。以后对某一模块进行时钟电源的开关就是操纵这个结构。
然后调用davinci_enable_clks函数,这个函数实质是循环调用了clk_register函数和clk_enable函数,从而使能davinci_dm365_clks数组中定义的每一个模块的电源时钟。clk_register函数将当前模块放入全局的clocks链表中,而后根据usecount的判断值调用clk_enable函数,再调用__clk_enable函数,而其最终调用davinci_psc_config函数完成某一模块的时钟电源使能。需要特别注意的是此时几乎全部模块的usecount都不满足,不会调用clk_enable函数,而即使调用了clk_enable函数(AEMIF),也不会真正调用__clk_enable函数。也就是说这初始化部分并没有真正使能很多模块的时钟电源,只是按初始化的流程走了一个过程而已。在后面要提到的板级设备初始化部分会调用davinci_psc_config函数真正使能一些模块的时钟电源。另外其它模块的时钟电源则分布在各个模块的驱动程序中,当驱动加载的时候才会调用clk_enable函数打开这个模块的时钟电源。这部分挺迷惑人的,好好理解。
davinci_psc_config函数的前一半工作是操作PSC相关的寄存器来使能模块的时钟电源,这部分的操作详见数据手册,是按手册的步骤来实现的,另一半工作是调用davinci_pinmux_setup函数来将此模块相关的复用引脚功能确定,它首先调用get_pin_list函数,它是个函数指针,在先前已经被指向了davinci_get_pins函数,它通过ctlr和id两个参数从先前的dm365_pins数组中得到相应的某一维,这正是某一模块需要的那些引脚。然后再通过一个循环将这几个引脚调用davinci_cfg_reg函数实现其复用的本模块的功能。
davinci_cfg_reg函数,很重要也很复杂。它定义一个pin_config类型的指针cfg指向当前引脚在pin_table的位置,然后根据其所在的PINMUXx寄存器的位为操作。这里的实现过程操作比较复杂,涉及到屏蔽位,模式等,但总的来说就是模块需要这个引脚做此模块功能而不是其它功能,暂不深究。
到这里,davinci_cfg_reg函数结束了,davinci_pinmux_setup函数也就结束了,davinci_psc_config函数也就结束了,__clk_enable,clk_enable,davinci_enable_clks函数也就结束了,davinci_clk_init函数也就结束了,davinci_init_common_hw函数也就结束了,davinci_map_io函数也就结束了。DM365的BSP板定义宏中的map_io也就结束了。下面分析板定义宏中的init_irq,它指向davinci_dm365_evm_irq_init函数,下面开始分析davinci_dm365_evm_irq_init函数。
davinci_dm365_evm_irq_init函数,只调用一个davinci_irq_init函数。它首先获得全部64个中断的优先级数组,这个前面已经得到并放在全局数组davinci_irq_priorities中。而后操作INTC的寄存器,比如清除中断请求,禁止中断等,而后将设置好的优先级值写入优先级寄存器中。然后就将全部64个中断注册到内核中,主要是利用内核中断管理中的set_irq_chip函数,set_irq_flags函数,set_irq_handler函数,特别是set_irq_chip函数,它将每一个中断绑定到一个irq_chip结构类型的davinci_irq_chip_0,而它包括了三个对中断的处理的函数指针,比如使能unmask,禁止mask等,其实质还是操作了INTC的相关寄存器。这些函数就是在程序中,对某一中断进行使能,禁止的处理,比如调用enable_irq使能某一个中断时,其实最终就是调用unmask所指的函数。
至此,davinci_irq_init函数结束了,davinci_dm365_evm_irq_init函数也就结束了。DM365的BSP板定义宏中的init_irq也就结束了。板定义宏中的timer感觉很复杂,现在不是很理解,以后有机会再看。下面分析板定义宏中的init_machine,它指向dm365_evm_init函数,下面开始分析dm365_evm_init函数。
dm365_evm_init函数是BSP中最重要的了,它完成了部分设备的初始化及注册。
阅读(3502) | 评论(1) | 转发(0) |