Linux driver dts使用,例項驅動編寫
Device Tree後,許多硬體的細節可以直接透過它傳遞給Linux,而不再需要在kernel中進行大量的冗餘編碼。
Device Tree由一系列被命名的結點(node)和屬性(property)組成,而結點本身可包含子結點。所謂屬性,其實就是成對出現的name和value。在Device Tree中,可描述的資訊包括(原先這些資訊大多被hard code到kernel中):
CPU的數量和類別
記憶體基地址和大小
匯流排和橋
外設連線
中斷控制器和中斷使用情況
GPIO控制器和GPIO使用情況
Clock控制器和Clock使用情況
DTS (device tree source)
.dts檔案是一種ASCII 文字格式的Device Tree描述,易於閱讀。在ARM Linux系統一個.dts檔案對應一個ARM的machine,一般放置在核心的arch/arm/boot/dts/目錄。由於一個SoC可能對應多個machine(一個SoC可以對應多個產品和電路板),勢必這些.dts檔案需包含許多共同的部分,Linux核心為了簡化,把SoC公用的部分或者多個machine共同的部分一般提煉為.dtsi,類似於C語言的標頭檔案。其他的machine對應的.dts就include這個.dtsi。譬如,對於VEXPRESS而言,vexpress-v2m.dtsi就被vexpress-v2p-ca9.dts所引用, vexpress-v2p-ca9.dts有如下一行:
/include/ “vexpress-v2m.dtsi”,.dtsi也可以include其他的.dtsi,譬如幾乎所有的ARM SoC的.dtsi都引用了skeleton.dtsi。
.dts(或者其include的.dtsi)基本元素即為前文所述的結點和屬性:
本文以Amlogic S905D平臺
dts檔案引用的標頭檔案:
release_n_7.1_20170804\common\arch\arm\boot\dts\include\dt-bindings
|->clk
|->clock
|->dma
|->gpio
|->input
|->interrupt-controller
|->mfd
|->pinctrl
|->pwm
|->reset
|->sound
|->thermal
解析dts檔案的相關函式,定義在of.h檔案裡:
release_n_7.1_20170804\common\include\linux\of.h
常用函式原型:
static inline bool of_property_read_bool(const struct device_node *np,
const char *propname)
static inline int of_property_read_u8(const struct device_node *np,
const char *propname, u8 *out_value)
static inline int of_property_read_u16(const struct device_node *np,
const char *propname, u16 *out_value)
static inline int of_property_read_u32(const struct device_node *np,
const char *propname, u32 *out_value)
static inline int of_property_read_string_array(struct device_node *np,
const char *propname, const char **out_strs,
size_t sz)
static inline int of_property_count_strings(struct device_node *np,
const char *propname)
static inline int of_property_read_string_index(struct device_node *np,
const char *propname,
int index, const char **output)
dts檔案新增node:
//dts檔案以“/”為根目錄,以樹形展開,要注意思大括號的匹配。
/* add by song for dvb widgets */
dvb_widgets {
compatible = "amlogic, dvb_widgets"; //platform_driver 指定的匹配表。
status = "okay"; //裝置狀態
dw_name = "dvb-widgets"; //字串屬性
dw_num = <8>; //數值屬性
ant_power-gpio = <&gpio GPIODV_15 GPIO_ACTIVE_HIGH>; //gpio 描述
loops-gpio = <&gpio GPIODV_13 GPIO_ACTIVE_HIGH>; //gpio 描述
ch34-gpio = <&gpio GPIODV_12 GPIO_ACTIVE_HIGH>; //gpio 描述
};
驅動demo
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/errno.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/device.h>
#include <asm/irq.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <uapi/linux/input.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/amlogic/aml_gpio_consumer.h>
#include <linux/amlogic/gpio-amlogic.h>
#include <linux/amlogic/sd.h>
#include <linux/amlogic/iomap.h>
#include <dt-bindings/gpio/gxbb.h>
#include <linux/amlogic/cpu_version.h>
#include <linux/amlogic/pm.h>
#include <linux/of_address.h>
struct dw_dev {
unsigned int ant_power_pin;
unsigned int ant_overload_pin;
unsigned int loops_pin;
unsigned int ch3_4_pin;
};
struct dw_dev pdw_dev;
static int dvb_widgets_suspend(struct platform_device *pdev, pm_message_t pm_status)
{
pr_dbg("%s\n",__FUNCTION__);
return 0;
}
static int dvb_widgets_resume(struct platform_device *pdev)
{
pr_dbg("%s\n",__FUNCTION__);
return 0;
}
static void dvb_widgets_shutdown(struct platform_device *pdev)
{
pr_dbg("%s\n",__FUNCTION__);
}
static int dvb_widgets_remove(struct platform_device *pdev)
{
pr_dbg("%s\n",__FUNCTION__);
#ifdef D_SUPPORT_CLASS_INTERFACE
class_unregister(&dvb_widgets_class);
#endif
return 0;
}
static int dvb_widgets_probe(struct platform_device *pdev)
{
const char *str = NULL;
int dw_num = 0;
bool ant_power_overload_one_ping;
int error = -EINVAL;
pr_dbg("%s\n",__FUNCTION__);
//判斷節點是否存在
if (!pdev->dev.of_node) {
pr_dbg("dvb_widgets pdev->dev.of_node is NULL!\n");
error = -EINVAL;
goto get_node_fail;
}
// read string
error = of_property_read_string(pdev->dev.of_node, "dw_name", &str);
pr_dbg("dw_name:%s\n",str);
// read u32
error = of_property_read_u32(pdev->dev.of_node, "dw_num", &dw_num);
if (error) {
pr_err("Filed to get dw_num\n");
}else{
pr_dbg("dw_num:%d\n", dw_num);
}
// read bool 如果dts檔案定義的有ant_power_overload_one_ping此屬性:true
ant_power_overload_one_ping = of_property_read_bool(pdev->dev.of_node, "ant_power_overload_one_ping");
if (ant_power_overload_one_ping) {
pr_dbg("ant_power_overload_one_ping : true\n");
} else {
pr_dbg("ant_power_overload_one_ping : false\n");
}
#ifdef CONFIG_OF
//GPIO讀取
error = of_property_read_string(pdev->dev.of_node, "loops-gpio", &str);
if (!error) {
pdw_dev.loops_pin =
desc_to_gpio(of_get_named_gpiod_flags(pdev->dev.of_node,
"loops-gpio", 0, NULL));
pr_dbg("%s: %s\n", "loops-gpio", str);
} else {
pdw_dev.loops_pin = -1;
pr_dbg("cannot find loops-gpio \n");
}
error = of_property_read_string(pdev->dev.of_node, "ch34-gpio", &str);
if (!error) {
pdw_dev.ch3_4_pin =
desc_to_gpio(of_get_named_gpiod_flags(pdev->dev.of_node,
"ch34-gpio", 0, NULL));
pr_dbg("%s: %s\n", "ch34-gpio", str);
} else {
pdw_dev.ch3_4_pin = -1;
pr_dbg("cannot find ch3_4_gpio\n");
}
error = of_property_read_string(pdev->dev.of_node, "ant_power-gpio", &str);
if (!error) {
pdw_dev.ant_power_pin =
desc_to_gpio(of_get_named_gpiod_flags(pdev->dev.of_node,
"ant_power-gpio", 0, NULL));
pr_dbg("ant_power-gpio\n");
} else {
pdw_dev.ant_power_pin = -1;
pr_dbg("cannt find ant_power-gpio\n");
}
//申請GPIO
gpio_request(pdw_dev.ant_power_pin, MODULE_NAME);
gpio_request(pdw_dev.loops_pin, MODULE_NAME);
gpio_request(pdw_dev.ch3_4_pin, MODULE_NAME);
//設定GPIO 上拉狀態
gpio_set_pullup(pdw_dev.ant_power_pin, 1);
gpio_set_pullup(pdw_dev.loops_pin, 1);
gpio_set_pullup(pdw_dev.ch3_4_pin, 1);
//設定GPIO輸出電平
gpio_direction_output(pdw_dev.ant_power_pin, 1); //輸出高電平
gpio_direction_output(pdw_dev.loops_pin, 0); //輸出低電平
gpio_direction_output(pdw_dev.ch3_4_pin, 0); //輸出低電平
#else
#endif
return 0;
get_node_fail:
return error;
}
#ifdef CONFIG_OF
static const struct of_device_id dvb_widgets_match[] = {
{ .compatible = "amlogic, dvb_widgets"},
{},
};
#else
#define dvb_widgets_match NULL
#endif
static struct platform_driver dvb_widgets_driver = {
.probe = dvb_widgets_probe,
.remove = dvb_widgets_remove,
.shutdown = dvb_widgets_shutdown,
.suspend = dvb_widgets_suspend,
.resume = dvb_widgets_resume,
.driver = {
.name = MODULE_NAME, //裝置驅動名
.of_match_table = dvb_widgets_match,
},
};
static int __init dvb_widgets_init(void)
{
pr_dbg("%s\n",__FUNCTION__);
return platform_driver_register(&dvb_widgets_driver);
}
static void __exit dvb_widgets_exit(void)
{
pr_dbg("%s\n",__FUNCTION__);
platform_driver_unregister(&dvb_widgets_driver);
}
module_init(dvb_widgets_init);
module_exit(dvb_widgets_exit);
MODULE_AUTHOR("Song YuLong");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("AML DVB Widgets Driver.");
MODULE_AUTHOR("GOOD, Inc.");