Linux 內核學習(6)---- Linux 設備樹相關API

platform_device 的生成

在dts/dtsi 文件中添加相應的 node 之后,linux 設備樹框架會將其解析為 platform_device 結構卢厂,編寫設備驅動程序時美莫,也會向platform_bus 注冊 platform_driver ;根據 node 中 compatible 屬性,匹配相應的驅動,最終調用到驅動的 probe 函數
dts/dtsi 文件中添加相應的 node酱床,對應于內核中的 struct device_node 結構體
/linux-5.4.6/arch/arm64/boot/dts/arm juno-motherboard.dtsi

apbregs@10000 {
    compatible = "syscon", "simple-mfd";
    reg = <0x010000 0x1000>;

    led0 {
        compatible = "register-bit-led";
        offset = <0x08>;
        mask = <0x01>;
        label = "vexpress:0";
        linux,default-trigger = "heartbeat";
        default-state = "on";
    };
    led1 {
        compatible = "register-bit-led";
        offset = <0x08>;
        mask = <0x02>;
        label = "vexpress:1";
        linux,default-trigger = "mmc0";
        default-state = "off";
    };
    led2 {
        compatible = "register-bit-led";
        offset = <0x08>;
        mask = <0x04>;
        label = "vexpress:2";
        linux,default-trigger = "cpu0";
        default-state = "off";
    };
    .......
    };
};

每一個大括號里面的內容被抽象為一個節(jié)點,在內核中對應的數據結構如下:
include/linux/of.h

struct device_node {
    const char *name; //節(jié)點名稱
    phandle phandle;
    const char *full_name; //帶路徑的節(jié)點全名
    struct fwnode_handle fwnode;

    struct  property *properties;
    struct  property *deadprops;    /* removed properties */
    struct  device_node *parent; //父節(jié)點名稱
    struct  device_node *child; //子節(jié)點
    struct  device_node *sibling; //兄弟節(jié)點
#if defined(CONFIG_OF_KOBJ)
    struct  kobject kobj;
#endif
    unsigned long _flags;
    void    *data;
#if defined(CONFIG_SPARC)
    unsigned int unique_id;
    struct of_irq_controller *irq_trans;
#endif
};

獲取節(jié)點常用函數

of_find 相關API

功能:通過節(jié)點的compatible屬性和type類型獲取設備節(jié)點

struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compat);

from:指定從哪里開始尋找設備節(jié)點趟佃,為NULL時從根節(jié)點開始尋找
type:要尋找的設備節(jié)點類型扇谣,為NULL表示忽略type
compatible:要尋找的設備節(jié)點compatible屬性字符串
結果:成功返回設備節(jié)點結構體,失敗返回NULL

功能:根據設備節(jié)點的名字和設備類型獲取設備節(jié)點

struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
struct device_node *of_find_node_by_type(struct device_node *from, const char *type);

功能:根據設備節(jié)點的名字和設備類型獲取設備節(jié)點
from:指定從哪里開始尋找設備節(jié)點闲昭,為NULL時從根節(jié)點開始尋找
name:要尋找的設備節(jié)點名字
type:要尋找的設備節(jié)點類型
成功返回設備節(jié)點結構體罐寨,失敗返回NULL
功能:通過路徑全名獲取設備節(jié)點

struct device_node *of_find_node_by_path(const char *path)
獲取子節(jié)點和父節(jié)點API
struct device_node *of_get_parent(const struct device_node *node);
struct device_node *of_get_next_parent(struct device_node *node);

功能:獲取當前節(jié)點的父節(jié)點
參數:需要查找的父節(jié)點的節(jié)點
返回值:成功返回該節(jié)點的父節(jié)點,失敗返回NULL

struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev);
struct device_node *of_get_next_available_child(const struct device_node *node, struct device_node *prev);

功能:獲取當前節(jié)點的父節(jié)點 of_get_next_child函數可以循環(huán)查找子節(jié)點
參數:node:表示當前的節(jié)點 prev:前一個子節(jié)點序矩,也就是從哪一個子節(jié)點開始尋找鸯绿,為NULL則表示從第一個開始尋找
返回值為找到的下一個子節(jié)點
返回值:成功返回該節(jié)點的父節(jié)點,失敗返回NULL

提取設備數屬性API

設備數的每個node 可以包含多個屬性簸淀,獲取的屬性用下面的結構體表示
主要包含:名稱瓶蝴,屬性的長度,屬性的值

struct property {
    char    *name; //名稱
    int length; //長度
    void    *value; //屬性值
    struct property *next; //下一個屬性
#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)
    unsigned long _flags;
#endif
#if defined(CONFIG_OF_PROMTREE)
    unsigned int unique_id;
#endif
#if defined(CONFIG_OF_KOBJ)
    struct bin_attribute attr;
#endif
};
of find property
struct property *of_find_property(const struct device_node *np,const char *name,int *lenp);

of_find_property 可以查找指定的屬性 np:設備節(jié)點 name:屬性 lenp:屬性長度
比如使用下面代碼:
of_find_property(client->dev.of_node, "linux,gpio-keymap", &proplen)
通過of_find_property函數獲取設備中"linux,gpio-keymap"這個屬性的值

of_property_read by index
int of_property_read_u32_index(const struct device_node *np, const char *propname,u32 index, u32 *out_value);
int of_property_read_u64_index(const struct device_node *np,const char *propname,u32 index, u64 *out_value);

功能:讀取設備樹中屬性為32/64位無符號整形的值租幕,可以指定標號讀取哪幾個
參數:np:設備節(jié)點 propname:屬性名字 index:標號舷手,表示讀第幾個 out_value:讀出來的值
返回值:0 讀取成功, -EINVAL 表示屬性不存在劲绪,-ENODATA 表示沒有要讀取的數據男窟, -EOVERFLOW 表示屬性值列表太小

of_property_read array
static inline int of_property_read_u8_array(const struct device_node *np,const char *propname, u8 *out_values, size_t sz)
static inline int of_property_read_u16_array(const struct device_node *np,const char *propname, u16 *out_values, size_t sz)
static inline int of_property_read_u32_array(const struct device_node *np,const char *propname,u32 *out_values, size_t sz)
static inline int of_property_read_u64_array(const struct device_node *np,const char *propname, u64 *out_values, size_t sz)

功能:可以一次性讀出多個無符號數據,比如地址信息
np:設備節(jié)點 propname:屬性名字 out_values:讀出的值 sz:要讀多少個數據
下面的接口用于讀取單個的值

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_s32(const struct device_node *np,const char *propname, s32 *out_value)

讀取string 值的接口

int of_property_read_string(const struct device_node *np, const char *propname,const char **out_string);

component 框架下的 DTS 寫法

component 框架下為了啟動順序的需要贾富,區(qū)分了master 和 component 設備歉眷,對應的parent 和 child 設備也有固定寫法:


dts_component.jpg

linux-5.4.6/Documentation/devicetree/bindings/display

/ {

    dp0: display@c00000 {
        #address-cells = <1>;
        #size-cells = <0>;
        compatible = "arm,mali-d71";
        reg = <0xc00000 0x20000>;
        interrupts = <0 168 4>;
        clocks = <&dpu_aclk>;
        clock-names = "aclk";
        iommus = <&smmu 0>, <&smmu 1>, <&smmu 2>, <&smmu 3>,
            <&smmu 4>, <&smmu 5>, <&smmu 6>, <&smmu 7>,
            <&smmu 8>, <&smmu 9>;

        dp0_pipe0: pipeline@0 {
            clocks = <&fpgaosc2>;
            clock-names = "pxclk";
            reg = <0>;

            port@0 {
                reg = <0>;
                dp0_pipe0_out: endpoint@0 {
                    reg = <0>;
                    remote-endpoint = <&db_dvi0_in>;
                };
                
                dp0_pipe0_out_ext: endpoint@1 {
                    reg = <1>;
                    remote-endpoint = <&db_dvi1_in>;
                };      
                
            };
            
            port@1 {
                reg = <1>;
            }
        };

        dp0_pipe1: pipeline@1 {
            clocks = <&fpgaosc2>;
            clock-names = "pxclk";
            reg = <1>;

            port {
                dp0_pipe1_out: endpoint {
                    remote-endpoint = <&db_dvi1_in>;
                };
            };
        };
    };
    ...
};

以 arm 的 komeda dpu 為例,一個display 設備區(qū)分多個 pipeline颤枪,一個pipeline 下可能有兩個port姥芥,可能其中一個是用于輸出數據的流向 比如輸出到MIPI_DSI,另一個是用于添加一級后處理器汇鞭,用于畫質的處理

單個port 也可能有多個輸出終端凉唐,比如同時輸出到 HDMI 端口和 MIPI_DSI 端口,用 endpoint 區(qū)分霍骄,其中的 remote-endpoint 字段特別用于指向 endpoint 的 device tree node

remote-endpoint 字段引用了其他的 device node台囱,對應了 componet 設備
display 表示顯示控制器,對應了DRM 中的 master 設備

設備樹中使用的 API

include/linux/of_graph.h

struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id);
struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
                    struct device_node *previous);
struct device_node *of_graph_get_endpoint_by_regs(
        const struct device_node *parent, int port_reg, int reg);
struct device_node *of_graph_get_remote_endpoint(
                    const struct device_node *node);
struct device_node *of_graph_get_port_parent(struct device_node *node);
struct device_node *of_graph_get_remote_port_parent(
                    const struct device_node *node);
struct device_node *of_graph_get_remote_port(const struct device_node *node);
struct device_node *of_graph_get_remote_node(const struct device_node *node,
                         u32 port, u32 endpoint);

of_graph_get_remote_node 指定 port 和 endpoint 可以返回 remote-endpoint 設備的 device tree node

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末读整,一起剝皮案震驚了整個濱河市簿训,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖强品,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膘侮,死亡現(xiàn)場離奇詭異,居然都是意外死亡的榛,警方通過查閱死者的電腦和手機琼了,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來夫晌,“玉大人雕薪,你說我怎么就攤上這事∠恚” “怎么了所袁?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長凶掰。 經常有香客問我燥爷,道長,這世上最難降的妖魔是什么懦窘? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任局劲,我火速辦了婚禮,結果婚禮上奶赠,老公的妹妹穿的比我還像新娘鱼填。我一直安慰自己,他們只是感情好毅戈,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布苹丸。 她就那樣靜靜地躺著,像睡著了一般苇经。 火紅的嫁衣襯著肌膚如雪赘理。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天扇单,我揣著相機與錄音商模,去河邊找鬼。 笑死蜘澜,一個胖子當著我的面吹牛施流,可吹牛的內容都是我干的。 我是一名探鬼主播鄙信,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼瞪醋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了装诡?” 一聲冷哼從身側響起银受,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤践盼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后宾巍,有當地人在樹林里發(fā)現(xiàn)了一具尸體咕幻,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年顶霞,在試婚紗的時候發(fā)現(xiàn)自己被綠了肄程。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡确丢,死狀恐怖绷耍,靈堂內的尸體忽然破棺而出吐限,到底是詐尸還是另有隱情鲜侥,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布诸典,位于F島的核電站描函,受9級特大地震影響,放射性物質發(fā)生泄漏狐粱。R本人自食惡果不足惜舀寓,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肌蜻。 院中可真熱鬧互墓,春花似錦、人聲如沸蒋搜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽豆挽。三九已至育谬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間帮哈,已是汗流浹背膛檀。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留娘侍,地道東北人咖刃。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像憾筏,于是被迫代替她去往敵國和親僵缺。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容