linux內核中的GPIO系統之(2):pin control subsystem

 

一、前言

在linux2.6內核上工作的嵌入式軟件工程師在pin control上都會遇到這樣的狀況:

(1)啓動一個新的項目後,需要根據硬件平臺的設定進行pin control相關的編碼。例如:在bootloader中建立一個大的table,描述各個引腳的配置和缺省狀態。此外,由於SOC的引腳是可以複用的,因此在各個具體的driver中,也可能會對引腳進行的配置。這些工作都是比較繁瑣的工作,需要極大的耐心和細緻度。

(2)發現某個driver不能正常工作,辛辛苦苦debug後發現僅僅是因爲其他的driver在初始化的過程中修改了引腳的配置,導致自己的driver無法正常工作

(3)即便是主CPU是一樣的項目,但是由於外設的不同,我們也不能使用一個kernel image,而是必須要修改代碼(這些代碼主要是board-specific startup code)

(4)代碼不是非常的整潔,cut-and-pasted代碼滿天飛,linux中的冗餘代碼太多

作爲一個嵌入式軟件工程師,項目做多了,接觸的CPU就多了,摔的跤就多了,之後自然會去思考,我們是否可以解決上面的問題呢?此外,對於基於ARM core那些SOC,雖然表面上看起來各個SOC各不相同,但是在pin control上還有很多相同的內容的,是否可以把它抽取出來,進行進一步的抽象呢?新版本中的內核(本文以3.14版本內核爲例)提出了pin control subsystem來解決這些問題。

二、pin control subsystem的文件列表

1、源文件列表

我們整理linux/drivers/pinctrl目錄下pin control subsystem的源文件列表如下:

 

文件名 描述
core.c core.h pin control subsystem的core driver
pinctrl-utils.c pinctrl-utils.h pin control subsystem的一些utility接口函數
pinmux.c pinmux.h pin control subsystem的core driver(pin muxing部分的代碼,也稱爲pinmux driver)
pinconf.c pinconf.h pin control subsystem的core driver(pin config部分的代碼,也稱爲pin config driver)
devicetree.c devicetree.h pin control subsystem的device tree代碼
pinctrl-xxxx.c 各種pin controller的low level driver。

pin controller driver文檔中 ,我們以2416的pin controller爲例,描述了一個具體的low level的driver,這個driver涉及的文件包括pinctrl-samsung.c,pinctrl-samsung.h和pinctrl-s3c24xx.c。

2、和其他內核模塊接口頭文件

很多內核的其他模塊需要用到pin control subsystem的服務,這些頭文件就定義了pin control subsystem的外部接口以及相關的數據結構。我們整理linux/include/linux/pinctrl目錄下pin control subsystem的外部接口頭文件列表如下:

 

文件名 描述
consumer.h 其他的driver要使用pin control subsystem的下列接口:
a、設置引腳複用功能
b、配置引腳的電氣特性
這時候需要include這個頭文件
devinfo.h 這是for linux內核的驅動模型模塊(driver model)使用的接口。struct device中包括了一個struct dev_pin_info    *pins的成員,這個成員描述了該設備的引腳的初始狀態信息,在probe之前,driver model中的core driver在調用driver的probe函數之前會先設定pin state
machine.h 和machine模塊的接口。

3、Low level pin controller driver接口

我們整理linux/include/linux/pinctrl目錄下pin control subsystem提供給底層specific pin controller driver的頭文件列表如下:

 

文件名 描述
pinconf-generic.h 這個接口主要是提供給各種pin controller driver使用的,不是外部接口。
pinconf.h pin configuration 接口
pinctrl-state.h pin control state狀態定義
pinmux.h pin mux function接口

三、pin control subsystem的軟件框架圖

1、功能和接口概述

一般而言,學習複雜的軟件組件或者軟件模塊是一個痛苦的過程。我們可以把我們要學習的那個軟件block看成一個黑盒子,不論裏面有多麼複雜,第一步總是先了解其功能和外部接口特性。如果你願意,你可以不去看其內部實現,先自己思考其內部邏輯,並形成若干問題,然後帶着這些問題去看代碼,往往事半功倍。

(1)、功能規格。pin control subsystem的主要功能包括:

(A)管理系統中所有可以控制的pin。在系統初始化的時候,枚舉所有可以控制的pin,並標識這些pin。

(B)管理這些pin的複用(Multiplexing)。對於SOC而言,其引腳除了配置成普通GPIO之外,若干個引腳還可以組成一個pin group,形成特定的功能。例如pin number是{ 0, 8, 16, 24 }這四個引腳組合形成一個pin group,提供SPI的功能。pin control subsystem要管理所有的pin group。

(C)配置這些pin的特性。例如配置該引腳上的pull-up/down電阻,配置drive strength等

(2)接口規格。linux內核的某個軟件組件必須放回到linux系統中才容易探討它的接口以及在系統中的位置,因此,在本章的第二節會基於系統block上描述各個pin control subsystem和其他內核模塊的接口。

(3)內部邏輯。要研究一個subsystem的內部邏輯,首先要打開黑盒子,細分模塊,然後針對每一個模塊進行功能分析、外部接口分析、內部邏輯分析。如果模塊還是比較大,難於掌握,那麼就繼續細分,拆成子模塊,重複上面的分析過程。在本章的第三節中,我們打開pin control subsystem的黑盒子進行進一步的分析。

2、pin control subsystem在和其他linux內核模塊的接口關係圖如下圖所示:

pcb

pin control subsystem會向系統中的其他driver提供接口以便進行該driver的pin config和pin mux的設定,這部分的接口在第四章描述。理想的狀態是GPIO controll driver也只是象UART,SPI這樣driver一樣和pin control subsystem進行交互,但是,實際上由於各種源由(後文詳述),pin control subsystem和GPIO subsystem必須有交互,這部分的接口在第五章描述。第六章描述了Driver model和pin control subsystem的接口,第七章描述了爲Pin control subsystem提供database支持的Device Tree和Machine driver的接口。

3、pin control subsystem內部block diagram

pccore

起始理解了接口部分內容,閱讀和解析pin control subsystem的內部邏輯已經很簡單,本文就不再分析了。

四、pin control subsystem向其他driver提供的接口

當你準備撰寫一個普通的linux driver(例如串口驅動)的時候,你期望pin control subsystem提供的接口是什麼樣子的?簡單,當然最好是簡單的,最最好是沒有接口,當然這是可能的,具體請參考第六章的接口。

1、概述

普通driver調用pin control subsystem的主要目標是:

(1)設定該設備的功能複用。設定設備的功能複用需要了解兩個概念,一個是function,另外一個pin group。function是功能抽象,對應一個HW邏輯block,例如SPI0。雖然給定了具體的gunction name,我們並不能確定其使用的pins的情況。例如:爲了設計靈活,芯片內部的SPI0的功能可能引出到pin group { A8, A7, A6, A5 },也可能引出到另外一個pin group{ G4, G3, G2, G1 },但毫無疑問,這兩個pin group不能同時active,畢竟芯片內部的SPI0的邏輯功能電路只有一個。 因此,只有給出function selector(所謂selector就是一個ID或者index)以及function的pin group selector才能進行function mux的設定。

(2)設定該device對應的那些pin的電氣特性。

此外,由於電源管理的要求,某個device可能處於某個電源管理狀態,例如idle或者sleep,這時候,屬於該device的所有的pin就會需要處於另外的狀態。綜合上述的需求,我們把定義了pin control state的概念,也就是說設備可能處於非常多的狀態中的一個,device driver可以切換設備處於的狀態。爲了方便管理pin control state,我們又提出了一個pin control state holder的概念,用來管理一個設備的所有的pin control狀態。因此普通driver調用pin control subsystem的接口從邏輯上將主要是:

(1)獲取pin control state holder的句柄

(2)設定pin control狀態

(3)釋放pin control state holder的句柄

pin control state holder的定義如下:

struct pinctrl {
    struct list_head node;--系統中的所有device的pin control state holder被掛入到了一個全局鏈表中
    struct device *dev;---該pin control state holder對應的device
    struct list_head states;----該設備的所有的狀態被掛入到這個鏈表中
    struct pinctrl_state *state;---當前的pin control state
    struct list_head dt_maps;----mapping table
    struct kref users;------reference count
};

系統中的每一個需要和pin control subsystem進行交互的設備在進行設定之前都需要首先獲取這個句柄。而屬於該設備的所有的狀態都是掛入到一個鏈表中,鏈表頭就是pin control state holder的states成員,一個state的定義如下:

struct pinctrl_state {
    struct list_head node;---掛入鏈表頭的節點
    const char *name;-----該state的名字
    struct list_head settings;---屬於該狀態的所有的settings
};

一個pin state包含若干個setting,所有的settings被掛入一個鏈表中,鏈表頭就是pin state中的settings成員,定義如下:

struct pinctrl_setting {
    struct list_head node;
    enum pinctrl_map_type type;
    struct pinctrl_dev *pctldev;
    const char *dev_name;
    union {
        struct pinctrl_setting_mux mux;
        struct pinctrl_setting_configs configs;
    } data;
};

當driver設定一個pin state的時候,pin control subsystem內部會遍歷該state的settings鏈表,將一個一個的setting進行設定。這些settings有各種類型,定義如下:

enum pinctrl_map_type {
    PIN_MAP_TYPE_INVALID,
    PIN_MAP_TYPE_DUMMY_STATE,
    PIN_MAP_TYPE_MUX_GROUP,---功能複用的setting
    PIN_MAP_TYPE_CONFIGS_PIN,----設定單一一個pin的電氣特性
    PIN_MAP_TYPE_CONFIGS_GROUP,----設定單pin group的電氣特性
};

有pin mux相關的設定(PIN_MAP_TYPE_MUX_GROUP),定義如下:

struct pinctrl_setting_mux {
    unsigned group;--------該setting所對應的group selector
    unsigned func;---------該setting所對應的function selector
};

有了function selector以及屬於該functiong的roup selector就可以進行該device和pin mux相關的設定了。設定電氣特性的settings定義如下:

struct pinctrl_map_configs {
    const char *group_or_pin;----該pin或者pin group的名字
    unsigned long *configs;----要設定的值的列表。這個值被用來寫入HW
    unsigned num_configs;----列表中值的個數
};

2、具體的接口

(1)devm_pinctrl_get和pinctrl_get。devm_pinctrl_get是Resource managed版本的pinctrl_get,核心還是pinctrl_get函數。這兩個接口都是獲取設備(設備模型中的struct device)的pin control state holder(struct pinctrl)。pin control state holder不是靜態定義的,一般在第一次調用該函數的時候會動態創建。創建一個pin control state holder是一個大工程,我們分析一下這段代碼:

static struct pinctrl *create_pinctrl(struct device *dev)
{

   分配pin control state holder佔用的內存並初始化
    p = kzalloc(sizeof(*p), GFP_KERNEL);
    p->dev = dev;
    INIT_LIST_HEAD(&p->states);
    INIT_LIST_HEAD(&p->dt_maps);

mapping table這個database的建立也是動態的,當第一次調用pin control state holder的get函數的時候,就會通過調用pinctrl_dt_to_map來建立該device需要的mapping entry。具體請參考第七章。

    ret = pinctrl_dt_to_map(p);

    devname = dev_name(dev);

    mutex_lock(&pinctrl_maps_mutex);
    for_each_maps(maps_node, i, map) {
        /* Map must be for this device */
        if (strcmp(map->dev_name, devname))
            continue;

        ret = add_setting(p, map);----分析一個mapping entry,把這個setting的代碼加入到holder中

    }
    mutex_unlock(&pinctrl_maps_mutex);

    kref_init(&p->users);

    /* 把這個新增加的pin control state holder加入到全局鏈表中 */
    mutex_lock(&pinctrl_list_mutex);
    list_add_tail(&p->node, &pinctrl_list);
    mutex_unlock(&pinctrl_list_mutex);

    return p;
}

(2)devm_pinctrl_put和pinctrl_put。是(1)接口中的逆函數。devm_pinctrl_get和pinctrl_get獲取句柄的時候申請了很多資源,在devm_pinctrl_put和pinctrl_put可以釋放。需要注意的是多次調用get函數不會重複分配資源,只會reference count加一,在put中referrenct count減一,當count==0的時候才釋放該device的pin control state holder持有的所有資源。

(3)pinctrl_lookup_state。根據state name在pin control state holder找到對應的pin control state。具體的state是各個device自己定義的,不過pin control subsystem自己定義了一些標準的pin control state,定義在pinctrl-state.h文件中:

#define PINCTRL_STATE_DEFAULT "default"
#define PINCTRL_STATE_IDLE "idle"
#define PINCTRL_STATE_SLEEP "sleep"

(4)pinctrl_select_state。設定一個具體的pin control state接口。

五、和GPIO subsystem交互

1、爲何pin control subsystem要和GPIO subsystem交互?

作爲軟件工程師,我們期望的硬件設計應該如下圖所示:pin hw

GPIO的HW block應該和其他功能複用的block是對等關係的,它們共同輸入到一個複用器block,這個block的寄存器控制哪一個功能電路目前是active的。pin configuration是全局的,不論哪種功能是active的,都可以針對pin進行電氣特性的設定。這樣的架構下,上圖中紅色邊框的三個block是完全獨立的HW block,其控制寄存器在SOC datasheet中應該是分成三個章節描述,同時,這些block的寄存器應該分別處於不同的地址區間。

對於軟件工程師,我們可以讓pin control subsystem和GPIO subsystem完全獨立,各自進行初始化,各自映射自己的寄存器地址空間,對於pin control subsystem而言,GPIO和其他的HW block沒有什麼不同,都是使用自己提供服務的一個軟件模塊而已。然而實際上SOC的設計並非總是向軟件工程師期望的那樣,有的SOC的設計框架圖如下:

pin hw2

這時候,GPIO block是alway active的,而紅色邊框的三個block是緊密的捆綁在一起,它們的寄存器佔據了一個memory range(datasheet中用一個章節描述這三個block)。這時候,對於軟件工程師來說就有些糾結了,本來不屬於我的GPIO控制也被迫要參與進來。這時候,硬件寄存器的控制都是pin controller來處理,GPIO相關的操作都要經過pin controller driver,這時候,pin controller driver要作爲GPIO driver的back-end出現。

2、具體的接口形態

(1)pinctrl_request_gpio。該接口主要用來申請GPIO。GPIO也是一種資源,使用前應該request,使用完畢後釋放。具體的代碼如下:

int pinctrl_request_gpio(unsigned gpio)----這裏傳入的是GPIO 的ID
{
    struct pinctrl_dev *pctldev;
    struct pinctrl_gpio_range *range;
    int ret;
    int pin;

    ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);---A
    if (ret) {
        if (pinctrl_ready_for_gpio_range(gpio))
            ret = 0;
        return ret;
    }

    mutex_lock(&pctldev->mutex); 
    pin = gpio_to_pin(range, gpio); ---將GPIO ID轉換成pin ID

    ret = pinmux_request_gpio(pctldev, range, pin, gpio); ------B

    mutex_unlock(&pctldev->mutex);

    return ret;
}

毫無疑問,申請GPIO資源本應該是GPIO subsystem的責任,但是由於上一節描述的源由,pin control subsystem提供了這樣一個接口函數供GPIO driver使用(其他的內核driver不應該調用,它們應該使用GPIO subsystem提供的接口)。多麼醜陋的代碼,作爲pin control subsystem,除了維護pin space中的ID,還要維護GPIO 的ID以及pin ID和GPIO ID的關係。

A:根據GPIO ID找到該ID對應的pin control device(struct pinctrl_dev)和GPIO rang(pinctrl_gpio_range)。在core driver中,每個low level的pin controller device都被映射成一個struct pinctrl_dev,並形成鏈表,鏈表頭就是pinctrldev_list。由於實際的硬件設計(例如GPIO block被分成若干個GPIO 的bank,每個bank就對應一個HW GPIO Controller Block),一個pin control device要管理的GPIO ID是分成區域的,每個區域用struct pinctrl_gpio_range來抽象,在low level 的pin controller初始化的時候(具體參考samsung_pinctrl_register的代碼),會調用pinctrl_add_gpio_range將每個GPIO bank表示的gpio range掛入到pin control device的range list中(gpio_ranges成員)。pinctrl_gpio_range 的定義如下:

struct pinctrl_gpio_range {
    struct list_head node;
    const char *name;
    unsigned int id;-----------GPIO chip ID
    unsigned int base;------該range中的起始GPIO IDD
    unsigned int pin_base;---在線性映射的情況下,這是起始的pin base
    unsigned const *pins;---在非線性映射的時候,這是table是pin到GPIO的lookup table
    unsigned int npins;----這個range有多少個GPIO引腳
    struct gpio_chip *gc;------每個GPIO bank都是一個gpio chip,對應一個GPIO range
};

pin ID和GPIO ID有兩種映射關係,一種是線性映射(這時候pin_base有效),也就是說,對於這個GPIO range,GPIO base ID是a,pin ID base是b,那麼a<--->b,a+1<--->b+1,a+2<--->b+2,以此類推。對於非線性映射(pin_base無效,pins是有效的),我們需要建立一個lookup table,以GPIO ID爲索引,可以找到對於的pin ID。

B:這裏主要是進行復用功能的設定,畢竟GPIO也是引腳的一個特定的功能。pinmux_request_gpio函數的作用主要有兩個,一個是在core driver中標記該pin已經用作GPIO了,這樣,如果有模塊後續request該資源,那麼core driver可以拒絕不合理的要求。第二步就是調用底層pin controller driver的callback函數,進行底層寄存器相關的操作。

(2)pinctrl_free_gpio。有申請就有釋放,這是pinctrl_request_gpio的逆函數

(3)pinctrl_gpio_direction_input和pinctrl_gpio_direction_output。爲已經指定爲GPIO功能的引腳設定方向,輸入或者輸出。代碼很簡單,不再贅述。

六、和驅動模型的接口

前文已經表述過,最好是讓統一設備驅動模型(Driver model)來處理pin 的各種設定。與其自己寫代碼調用devm_pinctrl_get、pinctrl_lookup_state、pinctrl_select_state等pin control subsystem的接口函數,爲了不讓linux內核自己的框架處理呢。本章將分析具體的代碼,這些代碼實例對自己driver調用pin control subsystem的接口函數來設定本device的pin control的相關設定也是有指導意義的。 linux kernel中的驅動模型提供了driver和device的綁定機制,一旦匹配會調用probe函數如下:

static int really_probe(struct device *dev, struct device_driver *drv)
{
    ……
    ret = pinctrl_bind_pins(dev); ---對該device涉及的pin進行pin control相關設定
    ……

    if (dev->bus->probe) {------下面是真正的probe過程
        ret = dev->bus->probe(dev);
        if (ret)
            goto probe_failed;
    } else if (drv->probe) {
        ret = drv->probe(dev);
        if (ret)
            goto probe_failed;
    }

……

}

pinctrl_bind_pins的代碼如下:

int pinctrl_bind_pins(struct device *dev)
{
    int ret;

    dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);---(1)

    dev->pins->p = devm_pinctrl_get(dev);-----------------(2)

    dev->pins->default_state = pinctrl_lookup_state(dev->pins->p, -------(3)
                    PINCTRL_STATE_DEFAULT);

    ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state); -----(4)


    dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p, ------(3)
                    PINCTRL_STATE_SLEEP);

    dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p, -------(3)
                    PINCTRL_STATE_IDLE);

    return 0;
}

(1)struct device數據結構有一個pins的成員,它描述了和該設備相關的pin control的信息,定義如下:

struct dev_pin_info {
    struct pinctrl *p;------------該device對應的pin control state holder
    struct pinctrl_state *default_state;----缺省狀態
    struct pinctrl_state *sleep_state;-----電源管理相關的狀態
    struct pinctrl_state *idle_state;-----電源管理相關的狀態
};

(2)調用devm_pinctrl_get獲取該device對應的 pin control state holder句柄。

(3)搜索default state,sleep state,idle state並記錄在本device中

(3)將該設備設定爲pin default state

七、和device tree或者machine driver相關的接口

1、概述

device tree或者machine driver這兩個模塊主要是爲 pin control subsystem提供pin mapping database的支持。這個database的每個entry用下面的數據結構表示:

struct pinctrl_map {
    const char *dev_name;---使用這個mapping entry的設備名
    const char *name;------該名字表示了該mapping entry
    enum pinctrl_map_type type;---這個entry的mapping type
    const char *ctrl_dev_name; -----pin controller這個設備的名字
    union {
        struct pinctrl_map_mux mux;
        struct pinctrl_map_configs configs;
    } data;
};

2、通過machine driver靜態定義的數據來建立pin mapping database

machine driver定義一個巨大的mapping table,描述,然後在machine初始化的時候,調用pinctrl_register_mappings將該table註冊到pin control subsystem中。

3、通過device tree來建立pin mapping database

pin mapping信息定義在dts中,主要包括兩個部分,一個是定義在各個具體的device node中,另外一處是定義在pin controller的device node中。

一個典型的device tree中的外設node定義如下(建議先看看pin controller driver的第二章關於dts的描述):

device-node-name { 
        定義該device自己的屬性 

        pinctrl-names = "sleep", "default";
        pinctrl-0 = ;
        pinctrl-1 = ;        
    };

對普通device的dts分析在函數pinctrl_dt_to_map中,代碼如下:

int pinctrl_dt_to_map(struct pinctrl *p)
{  
    of_node_get(np); 
    for (state = 0; ; state++) {-------------------(1)
        /* Retrieve the pinctrl-* property */
        propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);
        prop = of_find_property(np, propname, &size); 
        kfree(propname);
        if (!prop)
            break;
        list = prop->value;
        size /= sizeof(*list); --------------(2)

        /* Determine whether pinctrl-names property names the state */
        ret = of_property_read_string_index(np, "pinctrl-names", ------(3)
                            state, &statename); 

        if (ret < 0) {
            /* strlen("pinctrl-") == 8 */
            statename = prop->name + 8; -------------(4)
        }

        /* For every referenced pin configuration node in it */
        for (config = 0; config < size; config++) { -----------(5)
            phandle = be32_to_cpup(list++);

            /* Look up the pin configuration node */
            np_config = of_find_node_by_phandle(phandle); ------(6)

            /* Parse the node */
            ret = dt_to_map_one_config(p, statename, np_config); ----(7)
            of_node_put(np_config);
            if (ret < 0)
                goto err;
        }

        /* No entries in DT? Generate a dummy state table entry */
        if (!size) {
            ret = dt_remember_dummy_state(p, statename); -------(8)
            if (ret < 0)
                goto err;
        }
    }

    return 0;

err:
    pinctrl_dt_free_maps(p);
    return ret;
}

(1)pinctrl-0 pinctrl-1 pinctrl-2……表示了該設備的一個個的狀態,這裏我們定義了兩個pinctrl-0和pinctrl-1分別對應sleep和default狀態。這裏每次循環分析一個pin state。

(2)代碼執行到這裏,size和list分別保存了該pin state中所涉及pin configuration phandle的數目以及phandle的列表

(3)讀取從pinctrl-names屬性中獲取state name

(4)如果沒有定義pinctrl-names屬性,那麼我們將pinctrl-0 pinctrl-1 pinctrl-2……中的那個ID取出來作爲state name

(5)遍歷一個pin state中的pin configuration list,這裏的pin configuration實際應該是pin controler device node中的sub node,用phandle標識。

(6)用phandle作爲索引,在device tree中找他該phandle表示的那個pin configuration

(7)分析一個pin configuration,具體下面會仔細分析

(8)如果該設備沒有定義pin configuration,那麼也要創建一個dummy的pin state。

這裏我們已經進入對pin controller node下面的子節點的分析過程了。分析一個pin configuration的代碼如下:

static int dt_to_map_one_config(struct pinctrl *p, const char *statename,
                struct device_node *np_config)
{
    struct device_node *np_pctldev;
    struct pinctrl_dev *pctldev;
    const struct pinctrl_ops *ops;
    int ret;
    struct pinctrl_map *map;
    unsigned num_maps;

    /* Find the pin controller containing np_config */
    np_pctldev = of_node_get(np_config);
    for (;;) {
        np_pctldev = of_get_next_parent(np_pctldev);-------(1)
        if (!np_pctldev || of_node_is_root(np_pctldev)) {
            of_node_put(np_pctldev);
            return -EPROBE_DEFER;
        }
        pctldev = get_pinctrl_dev_from_of_node(np_pctldev);-----(2)
        if (pctldev)
            break;------------------------(3)
        /* Do not defer probing of hogs (circular loop) */
        if (np_pctldev == p->dev->of_node) {
            of_node_put(np_pctldev);
            return -ENODEV;
        }
    }
    of_node_put(np_pctldev);

    /*
     * Call pinctrl driver to parse device tree node, and
     * generate mapping table entries
     */
    ops = pctldev->desc->pctlops;
    ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);----(4)
    if (ret < 0)
        return ret;

    /* Stash the mapping table chunk away for later use */
    return dt_remember_or_free_map(p, statename, pctldev, map, num_maps);----(5)
}

(1)首先找到該pin configuration node對應的parent node(也就是pin controler對應的node),如果找不到或者是root node,則進入出錯處理。

(2)獲取pin control class device

(3)一旦找到pin control class device則跳出for循環

(4)調用底層的callback函數處理pin configuration node。這也是合理的,畢竟很多的pin controller bindings是需要自己解析的。

(5)將該pin configuration node的mapping entry信息註冊到系統中

八、core driver和low level pin controller driver的接口規格

pin controller描述符。每一個特定的pin controller都用一個struct pinctrl_desc來抽象,具體如下:

struct pinctrl_desc {
    const char *name;
    struct pinctrl_pin_desc const *pins;
    unsigned int npins;
    const struct pinctrl_ops *pctlops;
    const struct pinmux_ops *pmxops;
    const struct pinconf_ops *confops;
    struct module *owner;
};

pin controller描述符需要描述它可以控制多少個pin(成員npins),每一個pin的信息爲何?(成員pins)。這兩個成員就確定了一個pin controller所能控制的引腳的信息。

pin controller描述符中包括了三類操作函數:pctlops是一些全局的控制函數,pmxops是複用引腳相關的操作函數,confops操作函數是用來配置引腳的特性(例如:pull-up/down)。struct pinctrl_ops中各個callback函數的具體的解釋如下:

 

callback函數 描述
get_groups_count 該pin controller支持多少個pin group。pin group的定義可以參考本文關於pin controller的功能規格中的描述。注意不要把pin group和IO port的硬件分組搞混了。例如:S3C2416有138個I/O 端口,分成11組,分別是gpa~gpl,這個組並不叫pin group,而是叫做pin bank。pin group是和特定功能(例如SPI、I2C)相關的一組pin。
get_group_name 給定一個selector(index),獲取指定pin group的name
get_group_pins 給定一個selector(index),獲取該pin group中pin的信息(該pin group包括多少個pin,每個pin的ID是什麼)
pin_dbg_show debug fs的callback接口
dt_node_to_map 分析一個pin configuration node並把分析的結果保存成mapping table entry,每一個entry表示一個setting(一個功能複用設定,或者電氣特性設定)
dt_free_map 上面函數的逆函數

複用引腳相關的操作函數的具體解釋如下:

 

call back函數 描述
request pin control core進行具體的複用設定之前需要調用該函數,主要是用來請底層的driver判斷某個引腳的複用設定是否是OK的。
free 是request的逆函數。調用request函數請求佔用了某些pin的資源,調用free可以釋放這些資源
get_functions_count 就是返回pin controller支持的function的數目
get_function_name 給定一個selector(index),獲取指定function的name
get_function_groups 給定一個selector(index),獲取指定function的pin groups信息
enable enable一個function。當然要給出function selector和pin group的selector
disable enable的逆函數
gpio_request_enable request並且enable一個單獨的gpio pin
gpio_disable_free gpio_request_enable的逆函數
gpio_set_direction 設定GPIO方向的回調函數

配置引腳的特性的struct pinconf_ops數據結構的各個成員定義如下:

 

call back函數 描述
pin_config_get 給定一個pin ID以及config type ID,獲取該引腳上指定type的配置。
pin_config_set 設定一個指定pin的配置
pin_config_group_get 以pin group爲單位,獲取pin上的配置信息
pin_config_group_set 以pin group爲單位,設定pin group的特性配置
pin_config_dbg_parse_modify debug接口
pin_config_dbg_show debug接口
pin_config_group_dbg_show debug接口
pin_config_config_dbg_show debug接口

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章