pinctrl(2)——驅動實現與設備樹配置


一、pinctrl子系統簡介

1. pin control subsystem驅動的硬件叫做pin controller,主要功能包括:
(1) pin multiplexing,pin引腳復用。
(2) pin configuration,這些配置參數包括 pull-up/down電阻的設定, tri-state設定,drive-strength的設定。

2. pin controller這個HW block需要是device tree中的一個節點。此外,device也需要在它自己的device tree node中描述pin control的相關內容。也就是說引腳配置在pin controller的設備樹節點中,設備的節點中通過其phandle去引用這些節點去使用。

3. 每個pin configuration都是pin controller的child node,描述了client device要使用到的一組pin的配置信息。具體如何定義pin configuration是和具體的pin controller相關的。在pin controller node中定義pin configuration其目的是為了讓client device引用。

4. pinctrl driver需要根據實際情況,將系統中所有的pin組織成一個struct pinctrl_pin_desc類型的數組。

5. Pin groups

在SoC系統中,有時需要將很多pin組合在一起,以實現特定的功能,例如SPI接口、I2C接口等。因此pin controller需要以group為單位,訪問、控制多個pin,這就是pin groups。相應地,pin controller subsystem需要提供一些機制,來獲取系統中到底有多少groups、每個groups包含哪些pins、等等。因此,pinctrl core在struct pinctrl_ops中抽象出三個回調函數,用來獲取pin groups相關信息。

struct pinctrl_ops {
    /*獲取系統中pin groups的個數,后續的操作,將以相應的索引為單位(類似數組的下標,個數為數組的大小)*/
    int (*get_groups_count) (struct pinctrl_dev *pctldev); 
    /*獲取指定group(由索引selector指定)的名稱。*/
    const char *(*get_group_name) (struct pinctrl_dev *pctldev, unsigned selector); 
    /*獲取指定group的所有pins(由索引selector指定),結果保存在pins(指針數組)和num_pins中。*/
    int (*get_group_pins) (struct pinctrl_dev *pctldev, unsigned selector, const unsigned **pins, unsigned *num_pins); 
    void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset); 
    /*將device tree中的pin state信息轉換為pin map*/
    int (*dt_node_to_map) (struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, unsigned *num_maps); 
    void (*dt_free_map) (struct pinctrl_dev *pctldev, struct pinctrl_map *map, unsigned num_maps); 
};

6. Pin configuration

Pin configuration 的對象是pin或者pin group,SoC中的管腳有些屬性可以配置,例如上拉、下拉、高阻、驅動能力等。pinctrl subsystem使用pin configuration來封裝這些功能,具體體現在struct pinconf_ops數據結構中,如下:

struct pinconf_ops { 
    #ifdef CONFIG_GENERIC_PINCONF 
    bool is_generic; 
    #endif
    /*獲取指定pin (管腳的編號,由pin的注冊信息獲得)當前配置,保存在config指針中(配置的具體含義,只有pinctrl driver自己知道)*/
    int (*pin_config_get) (struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config); 
    /*設置指定pin的配置(可以同時配置多個config,具體意義要由相應pinctrl driver)*/
    int (*pin_config_set) (struct pinctrl_dev *pctldev, unsigned pin, unsigned long *configs, unsigned num_configs); 
    /*獲取指定pin group的配置項*/
    int (*pin_config_group_get) (struct pinctrl_dev *pctldev, unsigned selector, unsigned long *config); 
    /*設置指定pin group的配置項*/
    int (*pin_config_group_set) (struct pinctrl_dev *pctldev, unsigned selector, unsigned long *configs, unsigned num_configs); 
    int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev, const char *arg, unsigned long *config); 
    void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset); 
    void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s, unsigned selector); 
    void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s, unsigned long config); 
}; 

kernel pinctrl subsystem 並不關心configuration的具體內容是什么,它只提供pin configuration get/set的通用機制,至於get到的東西,以及set的東西,到底是什么,是 pinctrl driver 自己的事情。后面結合pin map和pin state,就能更好地理解這種設計了。

7. Pin multiplexing(對象是pin或者pin group)

Pin multiplexing 的對象也是pin或者pin group,SoC中的很多管腳可以配置為不同的功能,這稱作管腳的復用(pin multiplexing,簡稱為pinmux)。kernel pinctrl subsystem使用struct pinmux_ops來抽象pinmux有關的操作,如下:

struct pinmux_ops {
    /*檢查某個pin是否已作它用,用於管腳復用時的互斥(避免多個功能同時使用某個pin而不知道,導致奇怪的錯誤)*/
    int (*request) (struct pinctrl_dev *pctldev, unsigned offset); 
    /*request的反操作。*/
    int (*free) (struct pinctrl_dev *pctldev, unsigned offset); 
    /*獲取系統中function的個數。*/
    int (*get_functions_count) (struct pinctrl_dev *pctldev); 
    /*獲取指定function的名稱。*/
    const char *(*get_function_name) (struct pinctrl_dev *pctldev, unsigned selector); 
    /*獲取指定function所占用的pin group(可以有多個)。*/
    int (*get_function_groups) (struct pinctrl_dev *pctldev, unsigned selector, const char * const **groups, unsigned *num_groups); 
    /*將指定的pin group(group_selector)設置為指定的function(func_selector)。*/
    int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector, unsigned group_selector); 
    /*下面是gpio有關的操作*/
    int (*gpio_request_enable) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset); 
    void (*gpio_disable_free) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset); 
    int (*gpio_set_direction) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset, bool input); 
    bool strict; 
};

注釋:

(1)為了管理SoC的管腳復用,pinctrl subsystem抽象出function的概念,用來表示I2C0、UART5等功能。pin(或者pin group)所對應的function一經確定,它們的管腳復用狀態也就確定了。

(2)和pin group類似,pinctrl core不關心function的具體形態,只要求pinctrl driver將SoC的所有可能的function枚舉出來(格式自行定義,不過需要有編號、名稱等內容),並注冊給pinctrl core。后續pinctrl core將會通過function的索引,訪問、控制相應的function。

(3)同一個function(如I2C0),可能可以map到不同的pin(或者pin group)上。

8. pinctrl subsystem的控制邏輯

8.1 pin state

pin(pin group)以及相應的function和configuration的組合,可以確定一個設備的一個“狀態”。這個狀態在pinctrl subsystem中就稱作pin state。

8.2 pin map

(1) 在pinctrl subsystem中,pin state有關的信息是通過pin map收集,相關的數據結構如下:

/* include/linux/pinctrl/machine.h */
struct pinctrl_map { 
    const char *dev_name; //device的名稱。
    const char *name; //pin state的名稱。
    enum pinctrl_map_type type;
    const char *ctrl_dev_name; //pin controller device的名字。
    /*
    data,該map需要用到的數據項, 如果map的類型是 PIN_MAP_TYPE_CONFIGS_GROUP,
    則為struct pinctrl_map_mux 類型的變量;
    如果map的類型是 PIN_MAP_TYPE_CONFIGS_PIN 或者 PIN_MAP_TYPE_CONFIGS_GROUP,
    則為struct pinctrl_map_configs 類型的變量。
    */
    union { 
        struct pinctrl_map_mux mux;
        struct pinctrl_map_configs configs;
    } data; 
};

/*
type取值包括:
PIN_MAP_TYPE_MUX_GROUP 配置管腳復用
PIN_MAP_TYPE_CONFIGS_PIN 配置pin
PIN_MAP_TYPE_CONFIGS_GROUP 配置pin group 
PIN_MAP_TYPE_DUMMY_STATE 不需要任何配置,僅僅為了表示state的存在。
*/

struct pinctrl_map_mux的定義如下:

struct pinctrl_map_mux { 
    const char *group; //group的名字,指明該map所涉及的pin group。
    const char *function; //function的名字,表示該map需要將group配置為哪種function。
};

struct pinctrl_map_configs的定義如下:

struct pinctrl_map_configs { 
    const char *group_or_pin; //pin或者pin group的名字。
    unsigned long *configs; //configuration數組,指明要將該group或pin配置成“什么樣子”。
    unsigned num_configs; //配置項的個數。
};  

注:講到這里,應該理解為什么struct pinconf_ops中的api,都不知道configuration到底是什么東西了吧,因為都是pinctrl driver自己安排好的,自產自銷,外人(pinctrl subsystem以及consumers)沒必要理解!

(2) 通過dts生成pin map

在舊時代,kernel的bsp工程師需要在machine有關的代碼中,靜態的定義pin map數組,這一個非常繁瑣且不易維護的過程。不過當kernel引入device tree之后,事情就簡單了很多:pinctrl driver確定了pin map各個字段的格式之后,就可以在dts文件中維護pin state以及相應的mapping table。pinctrl core在初始化的時候,會讀取並解析dts,並生成pin map。而各個consumer,可以在自己的dts node中,直接引用pinctrl driver定義的pin state,並在設備驅動的相應的位置,調用pinctrl subsystem提供的API,active或者deactive這些state。至於dts中pin map描述的格式是什么,則完全由pinctrl driver自己決定,因為,最終的解析工作(dts to map)也是它自己做的。

9.Consumer驅動對pinctrl子系統的使用

(1) 設備樹中配置好。

(2) 驅動中調用 pinctrl_get / devm_pinctrl_get 接口,獲得一個struct pinctrl類型的handle指針,此函數調用如下:

pinctrl_get 
    create_pinctrl //drivers/pinctrl/core.c
        pinctrl_dt_to_map //drivers/pinctrl/devicetree.c
            dt_to_map_one_config 
                pctlops->dt_node_to_map

(3) 調用 pinctrl_select_state()選擇一個state,使自己的某個pin state生效。

(4) 調用pinctrl subsystem提供的其它API,pinctrl subsystem進而調用pinctrl driver提供的各種回調函數,配置pin controller的硬件。

注:在設備和驅動在探測(probe)之前,就已經調用了pinctrl_bind_pins()來獲取4鍾state的引腳配置了,保存在struct device結構的struct dev_pin_info pins成員中,也可以從中直接獲取。

struct dev_pin_info {
    struct pinctrl *p;
    struct pinctrl_state *default_state;
    struct pinctrl_state *init_state;
#ifdef CONFIG_PM
    struct pinctrl_state *sleep_state;
    struct pinctrl_state *idle_state;
#endif
};

really_probe
    pinctrl_bind_pins
        dev->pins->default_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_DEFAULT);
        dev->pins->init_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_INIT); /*這里會根據init state在設備樹中是否配置來選擇是能"init tate"還是"default state"*/
        dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_SLEEP);
        dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_IDLE);

 

二、pinctrl設備樹配置

(1) client device的DTS

一個典型的device tree中的外設node定義如下:

device-node-name {  
    ......
    pinctrl-names = "sleep", "active";
    pinctrl-0 = <pin-config-0-a>; //pinctrl-x是一個句柄(phandle)列表,每個句柄指向一個pin configuration。
    pinctrl-1 = <pin-config-1-a>;
    ......
};

如果設備樹中沒有通過 pinctrl-names 字段指定state的話,由解析函數 pinctrl_dt_to_map() 可知,statename 降為pinctrl-X的X字符。

(2) pinctrl host的設備樹配置

pinctrl@f120000 {
    ......
    uart2_pins {
        uart2_active {
            phandle = <0x261>;
            mux {
                pins = "gpio117", "gpio118";
                function = "qup2";
            };
            config {
                pins = "gpio117", "gpio118";
                drive-strength = <0x2>;
                bias-disable;
            };
        };

        uart2_sleep {
            phandle = <0x262>;
            mux {
                pins = "gpio117", "gpio118";
                function = "gpio";
            };
            config {
                pins = "gpio117", "gpio118";
                drive-strength = <0x2>;
                bias-pull-down;
            };
        };
    };
};

舉例:

/* 1.1 設備端的設備樹配置 */
i2c@990000 {
    compatible = "qcom,i2c-geni";
    ......
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <0x27d>;   //default狀態對應下面的phandle = <0x27d>;
    pinctrl-1 = <0x27e>;   //sleep狀態對應下面的phandle = <0x27e>;
    ......
};

/* 1.2 pinctrl host端的設備樹配置 */    
qup7_i2c_pins {
    phandle = <0x480>;

    qup7_i2c_active {
        phandle = <0x27d>;
        mux {
            pins = "gpio20", "gpio21";
            function = "qup7";
        };
        config {
            pins = "gpio20", "gpio21";
            drive-strength = <0x2>;
            bias-disable;
        };
    };

    qup7_i2c_sleep {
        phandle = <0x27e>;
        mux {
            pins = "gpio20", "gpio21";
            function = "gpio";
        };
        config {
            pins = "gpio20", "gpio21";
            drive-strength = <0x2>;
            bias-no-pull;
        };
    };
};

注:phandle是dtb反編譯成dts文件后額外生成的,源設備樹文件中沒有這個域,對應關系如下:

1. 設備樹種的配置

tsif1_signals_active: tsif1_signals_active {
    tsif2_clk {
        pins = "gpio73";
        function = "tsif1_clk";
    };
};

tsif1_sync_active: tsif1_sync_active {
    tsif2_sync {
        pins = "gpio76";
        function = "tsif1_sync";
        drive_strength = <2>;    /* 2 mA */
        bias-pull-down;        /* pull down */
    };
};
        
tspp: msm_tspp@8880000 {
    compatible = "qcom,msm_tspp";
    ...
    pinctrl-4 = <&tsif1_signals_active &tsif1_sync_active>;        /* tsif1-mode2 */
    ...
}

2.dtb反解析后的配置:

tsif1_signals_active {
    phandle = <0x9f>; //反編譯后生成的,原始設備樹種沒有。

    tsif2_clk {
        pins = "gpio73";
        function = "tsif1_clk";
    };
};

tsif1_sync_active {
    phandle = <0xa0>; //dtb反編譯成dts才生成的,只是這個是增加的,其它的沒有變。

    tsif2_sync {
        pins = "gpio76";
        function = "tsif1_sync";
        drive_strength = <0x2>;
        bias-pull-down;
    };
};

msm_tspp@8880000 {
    compatible = "qcom,msm_tspp";
    ...
    pinctrl-4 = <0x9f 0xa0>; //反編譯后的使用的是phandle值來代替"&節點名稱"
    ...
}

 

 

三、gpio子系統與pinctrl子系統之間的耦合關系

1. gpio subsystem是pinctrl subsystem的client,基於pinctrl subsystem提供的功能,處理GPIO有關的邏輯。 

2. pinctrl中和gpio有關的后門

static inline int pinctrl_request_gpio(unsigned gpio) ; 
static inline void pinctrl_free_gpio(unsigned gpio) ; 
static inline int pinctrl_gpio_direction_input(unsigned gpio); 
static inline int pinctrl_gpio_direction_output(unsigned gpio);

當 gpio driver 需要使用某個管腳的時候,直接調用 pinctrl_request_gpio,向pinctrl subsystem申請。pinctrl subsystem會維護一個gpio number到pin number的map,將gpio subsystem傳來的gpio number轉換為pin number之后,調用 struct pinmux_ops 中有關的回調函數即可:

struct pinmux_ops { 
    ... 
    int (*gpio_request_enable) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset); 
    void (*gpio_disable_free) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset); 
    int (*gpio_set_direction) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset, bool input); 
};

對gpio driver來說,要做的事情就是提供gpio number到pin number的map。而pinctrl subsystem呢,做自己分內的事情即可:管理系統的pin資源,並根據gpio subsystem的請求,將某些pin設置為GPIO功能。   

4. gpio number通過struct pinctrl_gpio_range結構映射到pin number

(1) gpio number和pin number的map是通過一個名稱為gpio range的數據結構(pinctrl subsystem提供的),如下:

struct pinctrl_gpio_range { 
    struct list_head node; 
    const char *name; 
    unsigned int id; 
    unsigned int base; 
    unsigned int pin_base; 
    unsigned const *pins; 
    unsigned int npins; 
    struct gpio_chip *gc; 
};

就是:gpio controller(gc)中的gpio(base)到gpio(base + npins - 1) 和 pin controller中的 pin(pin_base) 到 pin(pin_base + npins - 1) 是一一對應的。

(2) gpio range dts node的解析過程大致如下

devm_gpiochip_add_data(drivers/gpio/gpiolib.c) 
    gpiochip_add_data 
        of_gpiochip_add(drivers/gpio/gpiolib-of.c) 
            of_gpiochip_add_pin_range 
                gpiochip_add_pin_range(drivers/gpio/gpiolib.c) 
                    pinctrl_find_and_add_gpio_range(drivers/pinctrl/core.c) 
                        pinctrl_add_gpio_range

以上過程的結果,就是在相應的pin controller device的數據結構中生成了一個gpio range的鏈表,如下:

struct pinctrl_dev { 
    struct list_head node; 
    struct pinctrl_desc *desc; 
    struct radix_tree_root pin_desc_tree; 
    struct list_head gpio_ranges; 
    ... 
};

(3) gpio range的使用

當gpio driver需要使用某一個gpio的時候,可以在gpiochip的request函數中,調用pinctrl core提供的pinctrl_request_gpio()接口(參數是gpio編號),然后pinctrl core會查尋gpio ranges鏈表,將gpio編號轉換成pin編號,然后調用pinctrl的相應接口(參數是pin編號),申請該pin的使用。這就是gpio subsystem和pinctrl subsystem的耦合。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM