匿名
未登录
登录
百问网嵌入式Linux wiki
搜索
查看“第六课:在LCD驱动中使用设备树”的源代码
来自百问网嵌入式Linux wiki
名字空间
页面
讨论
更多
更多
页面选项
Read
查看源代码
历史
←
第六课:在LCD驱动中使用设备树
因为以下原因,您没有权限编辑本页:
您所请求的操作仅限于该用户组的用户使用:
用户
您可以查看与复制此页面的源代码。
按照计划,本课会讲解修改uboot和内核让JZ2440支持设备树。 但前面修改uboot已经讲解完了,修改内核也没必要单独讲,可以直接看内核补丁,修改的方法也并不复杂。 内核补丁路径: <syntaxhighlight lang="c" > doc_and_sources_for_device_tree/source_and_images/第5,6课的源码及映像文件(使用了完全版的设备树)/第5课第4节_内核补丁及设备树/linux-4.19-rc3_device_tree_for_irq_jz2440.patch </syntaxhighlight> 对内核的修改并不多,里面大部分是移植yaffs,yaffs是一个文件系统,他适合在nand flash上使用,要是对移植yaffs感兴趣的话,可以看看毕业班的视频。 实际上涉及设备树的修改并不多,那我怎么知道修改那些呢? 我使用最笨的方法——添加打印。在发现内核启动卡住后,就沿着内核启动流程调用的函数添加打印,比如在`init.c`函数添加了一系列打印,看它卡在哪个函数,再进入该函数添加打印。 这里打印使用的是<code>early_print()</code>,因为<code>printk()</code>很可能还不能使用,<code>early_print()</code>直接把数据写到串口里面,和硬件驱动没有什么关系。 = 第01节_使用设备树给DM9000网卡_触摸屏指定中断= 在上一课我们把中断体系讲得很清楚了,我们先看一下内核里的网卡驱动程序,所在路径为: <syntaxhighlight lang="c" > drivers/net/ethernet/davicom/dm9dev9000.c </syntaxhighlight> 在这里做了一件非常取巧的事情,以前中断号和硬件绑定时,它的中断号是<code>IRQ_EINT7</code>,现在我直接偷懒将其赋值为7,实际上这种方法是非常不保险的。 从原理上我们可以知道网卡使用的是EINT7,对于EINT7它的hwirq是7,它就会从bit7开始查找,bit7如果没有被占用,那么它的虚拟中断号就等于7。万一有其它中断程序使用了上一级的第7号中断,后面EINT7的虚拟中断号就不会等于7,所以我们在驱动程序里指定中断号存在风险,因此我们需要改正这种做法。 == 网卡设备树节点 == 我们可以先在设备树里声明使用哪一个中断,在网卡中指定中断: <syntaxhighlight lang="c" > srom-cs4@20000000 { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; reg = <0x20000000 0x8000000>; ranges; ethernet@20000000 { compatible = "davicom,dm9000"; reg = <0x20000000 0x2 0x20000004 0x2>; interrupt-parent = <&gpf>; interrupts = <7 IRQ_TYPE_EDGE_RISING>; local-mac-address = [00 00 de ad be ef]; davicom,no-eeprom; }; }; </syntaxhighlight> 节点<code>srom-cs</code>位于根目录下面,它的<code>compatible</code>是<code>simple-bus</code>,对于<code>simple-bus</code>下面的子节点它也会创建为一个平台设备,它的<code>compatible</code>是<code>davicom,dm9000</code>,我们以后将根据这个值找到对应的驱动程序,在这个节点里面它指定了中断的信息,我们需要修改驱动程序为这个设备节点添加一个<code>platform_driver</code>,在<code>platform_driver</code>的<code>probe()</code>函数里面,把这个中断号确定下来。 修改代码过程参考视频。 == 触摸屏设备树节点 == 触摸屏的设备树节点如下: <syntaxhighlight lang="c" > jz2440ts@5800000 { compatible = "jz2440,ts"; reg = <0x58000000 0x100>; reg-names = "adc_ts_physical"; interrupts = <1 31 9 3>, <1 31 10 3>; interrupt-names = "int_ts", "int_adc_s"; clocks = <&clocks PCLK_ADC>; clock-names = "adc"; }; </syntaxhighlight> 该节点没有指定<code>interrupt-parent</code>,中断将发给它的父节点(也就是根节点),在根节点有<code>interrupt-parent = <0x1>;</code>,根据<code>0x01</code>找到<code>phandle</code>。 触摸屏使用了两个中断,一个是按下/松开时产生的中断,另外一个是ADC的中断。一但触摸屏产生信号,就传给子中断控制器(sub interrupt),再由子中断控制器发给顶级的中断控制器(interrupt controller)。 <code>interrupts</code>后面的4个32位数字含义如下: 第一个表示是发给主控制器还是子控制器,为1表示发给子控制器; 第二个表示子中断控制器发给主控制器的哪一个; 第三个表示是这个中断控制器里的哪一个中断; 第四个表示中断的触发方式; 通过第三个数字可以知道该节点的第0个中断资源是TC,第1个中断是ADC。 == 测试 == 两个驱动程序修改完后,分别上传到内核如下目录: <syntaxhighlight lang="c" > drivers/net/ethernet/davicom drivers/input/touchscreen </syntaxhighlight> 测试步骤如下: a. 编译内核 b. 使用新的uImage启动 c. 测试网卡: <syntaxhighlight lang="c" > ifconfig eth0 192.168.1.101 ping 192.168.1.1 </syntaxhighlight> d. 测试触摸屏: <syntaxhighlight lang="c" > hexdump /dev/evetn0 // 然后点击触摸屏 </syntaxhighlight> =第02节_在设备树中时钟的简单使用= 在本课里,本来只打算讲解两节,分别是网卡、触摸屏指定中断和在设备树里为LCD指定参数,后来发现LCD节点涉及clock和pinctrl,因此又扩充了两节。 == 时钟框图 == 先来看看S3C2440时钟的硬件框图: [[File:ldd_devicetree_chapter6_2_001.png|700px]] 将该图简化如下: [[File:ldd_devicetree_chapter6_2_002.jpg|700px]] 我们只想作为消费者怎么去使用这些时钟,并不关心“提供者”内部的层级结构,只要知道“直接提供者”,也不关系“直接提供者”的实现,我们只需要发出请求就可以了。 == 晶振设备树描述 == 我们看看在2440的设备树里怎么描述这提供者和消费者。先来看看晶振: <syntaxhighlight lang="c" > xti: xti_clock { compatible = "fixed-clock"; clock-frequency = <12000000>; clock-output-names = "xti"; #clock-cells = <0>; }; </syntaxhighlight> 根据<code>compatible</code>可以找到对应的驱动,驱动程序将晶振的频率记录下来,以后作为计算的基准。 然后再是PLL的设备节点: <syntaxhighlight lang="c" > clocks: clock-controller@4c000000 { compatible = "samsung,s3c2440-clock"; reg = <0x4c000000 0x20>; #clock-cells = <1>; }; </syntaxhighlight> 设备节点本身非常简单,复杂的是它对应的驱动程序。在驱动程序里面,肯定会根据<code>reg</code>获得寄存器的地址,然后设置各种内容。 大部分的芯片为了省电,它的外部模块时钟平时都是关闭的,只有在使用某个模块时,才设置相应的寄存器开启对应的时钟。 这些使用者各有不同,要怎么描述这些使用者呢? 我们可以为它们配上一个ID。在设备树中的<code>#clock-cells = <1>;</code>表示 用多少个u32位来描述消费者。在本例中使用一个u32来描述。 这些ID值由谁提供的? 是由驱动程序提供的,该节点会对应一个驱动程序,驱动程序给硬件(消费者)都分配了一个ID,所以说复杂的操作都留给驱动程序来做。 == LCD时钟设备树描述 == 消费者想使用时钟时,首先要找到时钟的直接提供者,向它发出申请。以LCD为例: <syntaxhighlight lang="c" > fb0: fb@4d000000{ compatible = "jz2440,lcd"; reg = <0x4D000000 0x60>; interrupts = <0 0 16 3>; clocks = <&clocks HCLK_LCD>; clock-names = "lcd"; …… } </syntaxhighlight> 在<code>clock</code>属性里,首先要确定向谁发出时钟申请,这里是向<code>clocks</code>发出申请,然后确定想要时钟提供者提供哪一路时钟,这里是<code>HCLK_LCD</code>,在驱动程序里定义了该宏,每种宏对应了一个时钟ID。 定义如下: <syntaxhighlight lang="c" > …… /* hclk-gates */ #define HCLK_LCD 32 #define HCLK_USBH 33 #define HCLK_USBD 34 #define HCLK_NAND 35 #define HCLK_CAM 36 …… </syntaxhighlight> 因此,我们只需要在设备节点定义<code>clocks</code>这个属性,这个属性确定时钟提供者,然后确定时钟ID,也就是向时钟提供者申请哪一路时钟。 对应的内核文档可以参考这两个文件: <syntaxhighlight lang="c" > Documentation/devicetree/bindings/clock/clock-bindings.txt Documentation/devicetree/bindings/clock/samsung,s3c2410-clock.txt </syntaxhighlight> 那么我这个设备驱动程序,怎么去使用这些时钟呢? 以前的驱动程序:<code>clk_get(NULL, "name");</code> <code>clk_prepare_enable(clk);</code> 现在的驱动程序:<code>of_clk_get(node, 0); </code> <code>clk_prepare_enable(clk);</code> == 总结 == a. 设备树中定义了各种时钟, 在文档中称之为"Clock providers", 比如: <syntaxhighlight lang="c" > clocks: clock-controller@4c000000 { compatible = "samsung,s3c2440-clock"; reg = <0x4c000000 0x20>; #clock-cells = <1>; // 想使用这个clocks时要提供1个u32来指定它, 比如选择这个clocks中发出的LCD时钟、PWM时钟 }; </syntaxhighlight> b. 设备需要时钟时, 它是"Clock consumers", 它描述了使用哪一个"Clock providers"中的哪一个时钟(id), 比如: <syntaxhighlight lang="c" > fb0: fb@4d000000{ compatible = "jz2440,lcd"; reg = <0x4D000000 0x60>; interrupts = <0 0 16 3>; clocks = <&clocks HCLK_LCD>; // 使用clocks即clock-controller@4c000000中的HCLK_LCD }; </syntaxhighlight> c. 驱动中获得/使能时钟: <syntaxhighlight lang="c" > // 确定时钟个数 int nr_pclks = of_count_phandle_with_args(dev->of_node, "clocks", "#clock-cells"); // 获得时钟 for (i = 0; i < nr_pclks; i++) { struct clk *clk = of_clk_get(dev->of_node, i); } // 使能时钟 clk_prepare_enable(clk); // 禁止时钟 clk_disable_unprepare(clk); </syntaxhighlight> =第03节_在设备树中pinctrl的简单使用=
返回至
第六课:在LCD驱动中使用设备树
。
导航
导航
WIKI首页
官方店铺
资料下载
交流社区
所有页面
所有产品
MPU-Linux开发板
MCU-单片机开发板
Linux开发系列视频
单片机开发系列视频
所有模块配件
Wiki工具
Wiki工具
特殊页面
页面工具
页面工具
用户页面工具
更多
链入页面
相关更改
页面信息
页面日志