linuxdtb编译
Linux源码的arch/powerpc/boot/dts/目录下存放了很多dts文件,可以作为参考文件。另外dtc编译器在内核源码2.6.25版本之后已经被包含进去。在2.6.26版本之后,生成blob的简单规则已经加入makefile,如下命令:
$ make ARCH=powerpc canyonlands.dtb
也可以根据自己的硬件修改好dts文件后,用下面类似命令生成dtb文件。
$ dtc -f -I dts -O dtb -R 8 -S 0x3000 test.dts > mpc836x_mds.dtb
$ mkimage -A ppc -O Linux -T flat_dt -C none -a 0x300000 -e 0 -d mpc836x_mds.dtb mpc836x_mds.dtu
2. Linux触摸屏驱动中什么时候会调用suspend这个函数
android系统摁下电源键后会让系统进入休眠以达到节电的目的。内核驱动中和休眠相关的就是suspend和resume函数。
suspend函数用于休眠,resume函数用于唤醒。下面分析驱动中的这两个函数是如何被调用到的。
驱动部分:
首先需要分析驱动的注册过程,较新的内核都是采用DTS方式来取代在内核中直接定义platform_device数据结构的注册方式,本文是基于DTS机制的内核来分析。
proct对应的dts文件在编译时被编译为dtb文件,uboot在启动时候会将其地址传给内核,内核在启动过程中会去解析,具体解析是在start_kernel()->setup_arch() --> unflatten_device_tree()中具体分析可以参考网上,解析的最终结果会存放在allnodes地址处,这个allnodes随后在machine的init函数
中被使用,init函数中会根据allnodes中的节点数据组合成platform_device数据结构,然后将其注册到platform总线上,下面简要分析一下并重点关注这些初始化过程中和
pm相关的初始化。
我参与的项目中machine的init函数就是via_init_machine函数,在这个函数中就是调用了of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL)这个函数来解析allnodes的。of_platform_populate是系统提供的接口。下面分析这个接口的实现:
[html] view plain
int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent)
{
struct device_node *child;
int rc = 0;
root = root ? of_node_get(root) : of_find_node_by_path("/");
if (!root)
return -EINVAL;
for_each_child_of_node(root, child) {
rc = of_platform_bus_create(child, matches, lookup, parent, true);
if (rc)
break;
}
of_node_put(root);
return rc;
}
root最后就是取到的根节点,然后其作为参数传递给of_platform_bus_create,of_platform_device_create_pdata的实现如下:
[html] view plain
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent, bool strict)
{
const struct of_dev_auxdata *auxdata;
struct device_node *child;
struct platform_device *dev;
const char *bus_id = NULL;
void *platform_data = NULL;
int rc = 0;
/* Make sure it has a compatible property */
if (strict && (!of_get_property(bus, "compatible", NULL))) {
pr_debug("%s() - skipping %s, no compatible prop\n",
__func__, bus->full_name);
return 0;
}
auxdata = of_dev_lookup(lookup, bus);
if (auxdata) {
bus_id = auxdata->name;
platform_data = auxdata->platform_data;
}
if (of_device_is_compatible(bus, "arm,primecell")) {
of_amba_device_create(bus, bus_id, platform_data, parent);
return 0;
}
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
if (!dev || !of_match_node(matches, bus))
return 0;
for_each_child_of_node(bus, child) {
pr_debug(" create child: %s\n", child->full_name);
rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
if (rc) {
of_node_put(child);
break;
}
}
return rc;
}
根据传入参数,我们这里直接分析of_platform_device_create_padate函数,如下:
[html] view plain
struct platform_device *of_platform_device_create_pdata(
struct device_node *np,
const char *bus_id,
void *platform_data,
struct device *parent)
{
struct platform_device *dev;
if (!of_device_is_available(np))
return NULL;
dev = of_device_alloc(np, bus_id, parent);
if (!dev)
return NULL;
#if defined(CONFIG_MICROBLAZE)
dev->archdata.dma_mask = 0xffffffffUL;
#endif
dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
dev->dev.bus = &platform_bus_type;
dev->dev.platform_data = platform_data;
/* We do not fill the DMA ops for platform devices by default.
* This is currently the responsibility of the platform code
* to do such, possibly using a device notifier
*/
if (of_device_add(dev) != 0) {
platform_device_put(dev);
return NULL;
}
return dev;
}
of_platform_device_create_padate->of_device_alloc->platform_device_alloc
便在platform_device_alloc函数中进行进行alloc和初始化了,实现如下:
[html] view plain
struct platform_device *platform_device_alloc(const char *name, int id)
{
struct platform_object *pa;
pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);
if (pa) {
strcpy(pa->name, name);
pa->pdev.name = pa->name;
pa->pdev.id = id;
device_initialize(&pa->pdev.dev);
pa->pdev.dev.release = platform_device_release;
arch_setup_pdev_archdata(&pa->pdev);
}
return pa ? &pa->pdev : NULL;
}
可以看到有个device_initialize,这里面对pdev.dev做一些列的初始化,其中有一个函数就是device_pm_init,这个函数就是我们一直关心的device相关的pm函数,具体实现如下:
[html] view plain
void device_pm_init(struct device *dev)
{
dev->power.is_prepared = false;
dev->power.is_suspended = false;
init_completion(&dev->power.completion);
complete_all(&dev->power.completion);
dev->power.wakeup = NULL;
spin_lock_init(&dev->power.lock);
pm_runtime_init(dev);
INIT_LIST_HEAD(&dev->power.entry);
dev->power.power_state = PMSG_INVALID;
}
可以看见它对device和功耗相关的数据做了一些初始化,我们这里先重点关注下dev->power.entry,初始化一个链表头,所以他/它很有可能会在后面加到某个链表里面去,而那个链表应该是用来保存所有的device用的。系统中所有的platform_device都是通过这种方式注册到系统中的,那么应该所有的platform_device都会初始化一个dev->power.entry,如果到时候把所有的dev->power.entry都添加到某个链表上去,那么系统到时候查询的时候只要找到这个list head就可以找到所有的platform_device了。嗯,不过这是我们的猜测。我们接下去分析来验证下。
platform_device通过alloc之后已经初始化好了,那么接下去就可以添加到系统中了,所以我们再回头看of_platform_device_create_pdata的实现。
函数在of_device_alloc之后把dev->dev.bus赋值给了platform_bus_type,接着就调用了of_device_add函数,在of_device_add函数中最后通过device_add添加到了bus上,但是device_add中有个函数需要我们关系,就是device_pm_add(dev),实现如下:
[html] view plain
void device_pm_add(struct device *dev)
{
pr_debug("PM: Adding info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
mutex_lock(&dpm_list_mtx);
if (dev->parent && dev->parent->power.is_prepared)
dev_warn(dev, "parent %s should not be sleeping\n",
dev_name(dev->parent));
list_add_tail(&dev->power.entry, &dpm_list);
dev_pm_qos_constraints_init(dev);
mutex_unlock(&dpm_list_mtx);
}
可以看到这里list_add_tail(&dev->power.entry, &dpm_list);这就验证了我们之前的猜测。所有注册到系统中的设备,最终都是会添加到dpm_list这条链表上。
那么系统在休眠的时候是如何通过dmp_list这表链表来suspend设备的呢?接下去就是我们要分析的电源管理部分内容。
系统电源部分:
电源管理相关文件在kernel/power目录下,前面已经分析到。系统中注册的设备都是会添加到dmp_list这条链表上的。那么睡眠的时候系统应该是会查找dmp_list这条链表,
然后通过这条链表依次去查到对应的driver,然后调用driver中的suspend方法。下面我们来验证。
2.在suspend会轮询bus下的driver,然后一次调用到driver->pm->suspend方法,然后进入休眠。
3.state_store->pm_suspend->enter_state->suspend_devices_and_enter->dpm_suspend_start->dpm_suspend->device_suspend->__device_suspend->pm_op->(ops->suspend)
3. 如何linux dtb mp
dtb文件作用的描述是,使用dtb可以减少linux内核版本的数量。同一份linux 内核代码可以在多个板卡上运行,每个板卡可以使用自己的dtb文件。 1,在linux内核启动过程中会解析dtb文件,根据dtb文件中设备列表进行加注各个外设的驱动模块。
4. linux编译u-boot时显示[arch/arm/dts/zynq-zc702.dtb]错误
在编译uboot的时候,会出现出错,因此我们要首先做 make disclean. 将原来的一些中间文件清理干净。
因此在编译Uboot依次执行 1.make disclean
2.make smdk2440_config
3. make
就可以编译通过了
5. dtb是什么文件怎么打开
dtb是可以减少linux内核版本数量的文件,打开方式如下:
1、首先,网络搜索框中输入UltraEdit,然后单击网络一下,如下图所示,然后进入下一步。
6. 关于linux安装TBB的问题
编译
tar -xf tbb41_20120718oss_src.tgz
cd ./tbb41_20120718oss_src.tgz,
按照readme的提示gmake 编译完成之后,build路径下面,会出现:
drwxr-xr-x 2 root root 20480 01-24 12:42 linux_intel64_gcc_cc4.1.2_libc2.5_kernel2.6.18_debug
drwxr-xr-x 2 root root 20480 01-24 12:38 linux_intel64_gcc_cc4.1.2_libc2.5_kernel2.6.18_release
修改环境变量
shell下执行: # vim ~/.bashrc
在最后添加 source /root/Desktop/tbb40_233oss/build/linux_intel64_gcc_cc4.1.2_libc2.5_kernel2.6.18_debug/tbbvars.sh
那么使用连接库的时候要使用-ltbb_debug
或者 source /root/Desktop/tbb40_233oss/build/linux_intel64_gcc_cc4.1.2_libc2.5_kernel2.6.18_release/tbbvars.sh 对应的链接库就是-ltbb了
1、测试 ltbb
重新打开一个shell,进入/tbb/tbb41_20120718oss/examples/pipeline/square
输入make,出现下面信息,表示成功安装
g++ -O2 -DNDEBUG -o square square.cpp -ltbb -lrt
./square 0 input.txt output.txt
serial run time = 0.160147
parallel run time = 0.0810647
elapsed time : 0.359243 seconds
2、测试ltbb_debug,就source deubg文件里面的tbbvars.sh
输入make debug test,出现下面信息表示成功安装
g++ -O2 -o gen_input gen_input.cpp -lrt
./gen_input >input.txt
g++ -O0 -g -DTBB_USE_DEBUG -o square square.cpp -ltbb_debug -lrt
./square 0 input.txt output.txt
serial run time = 0.169299
parallel run time = 0.0883386
elapsed time : 0.26235 seconds
3、测试自己写的cpp文件
记住在编译的时候加上-ltbb或-ltbb_debug。错误一般都是找不到库或者头文件,可以上网看看怎么设置PATH,LIBRARY_PATH,LD_LIBRARY_PATH。把这些路径设到tbb编译好的路径里面就行了。
例如gcc test.cc -ltbb_debug
7. 如何在Zedboard上运行linux下的应用程序
参考文档:http://www.elecfans.com/pld/PLDkaifaban/306232.html
ZedBoard开发板上的Zynq是一个ARM PS(processing system, 双核A9 + 存储管理 + 外设)+ PL(programable Logic) 结构,如果不使用PL,zynq的开发和普通的ARM 开发一样。不同的是ARM PS是可配置,因而硬件信息是不固定的。这也是zynq灵活性的一个表现。电子发烧友网编辑现为读者整合《玩转赛灵思Zedboard开发板》系列文章, 其中包括在ZedBoard开发板上的一些应用实例。其内容包括:
玩转赛灵思Zedboard开发板(1):ZedBoard详解
玩转赛灵思Zedboard开发板(2):ZedBoard最简单的测试工程
玩转赛灵思Zedboard开发板(3):基于Zynq PL的流水灯
玩转赛灵思Zedboard开发板(4):如何使用自带外设IP让ARM PS访问FPGA?
玩转赛灵思Zedboard开发板(5):基于AXI Lite 总线的从设备IP设计
从本小节开始,讲着重介绍Zedboard上的嵌入式linux应用,包括使用SDK设计最简单的linux应用程序、linux交叉编译环境搭建、设备驱动编写等内容。本小节使用的linux系统是Digilent官方提供的OOB设计,它是Digilent官网给出的一个完整的、Zedboard可运行的linux 系统,包含了Zedboard上的几个重要的设备驱动如串口、USB、以太网、OLED、HDMI等。当linux在Zedboard上运行起来后,Zedboard就是一个小型的嵌入式系统,使用SDK及Xilinx ARM Linux工具链编译生成的可执行文件可以在这个系统执行。
硬件平台:Digilent ZedBoard;开发环境:Windows XP 32 bit;软件: SDK 14.2。
一、获取Zedboard可运行的linux
Digilent官网给出Zedboard的可运行linux设计ZedBoard_OOB_Design包,可从http://www.digilentinc.com/Data/Documents/Other/ZedBoard_OOB_Design.zip获取,下载后解压,可以看到包的结构和内容:
boot_image目录:
-- system.bit – 配置FPGA的bit文件
-- u-boot.elf – 引导linux需要的Second-Stage boot loader
-- zynq_fsbl.elf – 配置ARM PS系统的First-Stage boot loader(FSBL)
doc目录:
-- README.txt – 说明文件
hw目录:
-- EDK 14.1版本的XPS工程,用来生成硬件文件和bit文件
linux目录:
-- devicetree_ramdisk.dts – 设备树源代码
-- .config – Linux内核配置文件,用来生成zImage
sd_image目录:
-- BOOT.BIN – 使用boot_images中的三个文件生成的Zynq配置文件
-- devicetree_ramdisk.dtb – 编译后的设备树文件
-- ramdisk8M.image.gz – 编译后的文件系统
-- README – 介绍如何运行OOB设计的说明文档
-- zImage – 编译后的内核
sw目录:
--硬件配置
--FSBL源代码
其中sd_image目录中包含了ZedBoard上能够运行linux的所有文件。将SD卡格式化为Fat32,把sd_image目录文件拷贝到SD 卡根目录下;然后将JP7~JP11设置为SD卡启动模式,将JP2短路,JP3断开,这样可以下一步我们可以把U盘作为USB 从设备挂载到Zedboard上。
图 Zedboard的连接和短路块设置
上电后,等待ARM PS的配置、FPGA的配置(蓝色LED DONE 亮起),之后开始引导Linux系统了。可以从超级终端上看到一系列的引导信息:
1 U-Boot 2012.04.01-00297-gc319bf9-dirty (Sep 13 2012 - 09:30:49)
2
3 DRAM: 512 MiB
4 WARNING: Caches not enabled
5 MMC: SDHCI: 0
6 Using default environment
8. linux加载dts的时候会创建设备节点吗
From:http://m.blog.csdn.net/blog/liliyaya/9188193
1. 在\kernel\of\fdt.c 中有如下初始化函数 注释上:展开设备树,创建device_nodes到全局变量allnodes中
void __init unflatten_device_tree(void)
{
__unflatten_device_tree(initial_boot_params, &allnodes,
early_init_dt_alloc_memory_arch);
/* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */
of_alias_scan(early_init_dt_alloc_memory_arch);
}
unflatten_device_tree函数被setup_arch函数调用,
因为我们使用得是arm平台所以存在\kernel\arch\arm\kernel\setup.c中
void __init setup_arch(char **cmdline_p)
{
unflatten_device_tree()
}
setup_arch函数在kernel启动是被调用,如下启动kernel存在\kernel\init\main.c中
asmlinkage void __init start_kernel(void)
{
setup_arch(&command_line);
}
这些工作完成解析DTS文件。保存到全局链表allnodes中。
2、在makefile中有这段话来编译dts文件:
$(obj)/A20%.dtb: $(src)/dts/A20%.dts FORCE
$(call if_changed_dep,dtc)
$(obj)/A68M%.dtb: $(src)/dts/A68M%.dts FORCE
$(call if_changed_dep,dtc)
和.c文件生成.o文件一样 回生成.dtb文件。在
/home/liyang/workspace/SZ_JB-mr1-8628-bsp-1012/out/target/proct/msm8226/obj/KERNEL_OBJ/arch/arm/boot
目录下,与zimage一个目录。
3、
在 board-8226.c中有初始化函数-->启动自动掉用
void __init msm8226_init(void)
{
of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
}
of_platform_populate在kernel\driver\of\platform.c中定义,回查询
root = root ? of_node_get(root) : of_find_node_by_path("/");
for_each_child_of_node(root, child)
{
rc = of_platform_bus_create(child, matches, lookup, parent, true);
if (rc)
break;
}
of_node_put(root);
在这里用到得函数of_find_node_by_path会最终调用到kernel\driver\of\base.c中得函数
struct device_node *of_find_node_by_path(const char *path)
{
遍历第1步中得allnodes找到根节点
}
of_platform_bus_create()函数中创建得内容存在了 adata中。
以下内容为转载:
(2)使用DTS注册总线设备的过程
以高通8974平台为例,在注册i2c总线时,会调用到qup_i2c_probe()接口,该接口用于申请总线资源和添加i2c适配器。在成功添加i2c适配器后,会调用of_i2c_register_devices()接口。此接口会解析i2c总线节点的子节点(挂载在该总线上的i2c设备节点),获取i2c设备的地址、中断号等硬件信息。然后调用request_mole()加载设备对应的驱动文件,调用i2c_new_device(),生成i2c设备。此时设备和驱动都已加载,于是drvier里面的probe方法将被调用。后面流程就和之前一样了。
简而言之,Linux采用DTS描述设备硬件信息后,省去了大量板文件垃圾信息。Linux在开机启动阶段,会解析DTS文件,保存到全局链表allnodes中,在掉用.init_machine时,会跟据allnodes中的信息注册平台总线和设备。值得注意的是,加载流程并不是按找从树根到树叶的方式递归注册,而是只注册根节点下的第一级子节点,第二级及之后的子节点暂不注册。Linux系统下的设备大多都是挂载在平台总线下的,因此在平台总线被注册后,会根据allnodes节点的树结构,去寻找该总线的子节点,所有的子节点将被作为设备注册到该总线上。
9. 请教linux启动过程中dtb的作用
是如何使用dtb文件的?
我的理解是,在linux内核启动过程中会解析dtb文件,根据dtb文件中设备列表进行加注各个外设的驱动模块。不知这样理解是否正确?
答“正确,解析dtb并创建platform
device
过程请参见of_platform_populate和of_platform_bus_probe。
dtb文件中外设列表,和在linux内核中使能各个外设驱动模块(设置*或M)有什么关系?
是否可以这么理解,要想一份linux
kernel可以运行在多块板卡上,必须在linux
kernel配置中包括所有这些板卡的driver
模块,这样在linux启动过程中才能根据dtb
中的列表进行加载模块。
答:dtb里面最后被kernel解析,并创建为platform
device,
相应的
你也必须要有platform
driver
才行。
是否可以这么理解?PC机在启动时会自动扫描外设,而在嵌入式中,linux内核启动过程中只是解析dtb文件,从而加载对应的模块。
答:因为PC机基本就是暗指X86,这样X86架构上外设都是基于PCI或者PCIE总线,这类总线可以在启动的时候,动态扫描。
嵌入式里面,ARM和PowerPC,
SOC
主要的设备控制器都是基于soc总线,其不像pci/pcie总线可以动态的扫描。所以需要dtb。
是否可以这么理解?编译linux内核时必须选择某外设模块,并且dtb中包括该外设的信息。在linux内核启动过程中才能自动加载该模块。
答:对
10. dtb是什么文件怎么打开
dtb是可以减少linux内核版本的数量的文件。同一份linux 内核代码可以在多个板卡上运行,每个板卡可以使用自己的dtb文件。
1,在linux内核启动过程中会解析dtb文件,根据dtb文件中设备列表进行加注各个外设的驱动模块。
2,PC机在启动时会自动扫描外设,而在嵌入式中,linux内核启动过程中只是解析dtb文件,从而加载对应的模块。
3,编译linux内核时必须选择某外设模块,并且dtb中包括该外设的信息。在linux内核启动过程中才能自动加载该模块。