Linux內核中的GPIO系統之(3):pin controller driver代碼分析--devm_kzalloc使用【轉】


轉自:http://www.wowotech.net/linux_kenrel/pin-controller-driver.html

一、前言

對於一個嵌入式軟件工程師,我們的軟件模塊經常和硬件打交道,pin control subsystem也不例外,被它驅動的硬件叫做pin controller(一般ARM soc的datasheet會把pin controller的內容放入GPIO controller的章節中),主要功能包括:

(1)pin multiplexing。基於ARM core的嵌入式處理器一般會提供豐富的功能,例如camera interface、LCD interface、USB、I2C、SPI等等。雖然處理器有幾百個pin,但是這些pin還是不夠分配,因此有些pin需要復用。例如:127號GPIO可以做一個普通的GPIO控制LED,也可以配置成I2C的clock信號,也可以配置成SPI的data out信號。當然,這些功能不可能同時存在,因為硬件信號只有一個。

(2)pin configuration。這些配置參數包括:pull-up/down電阻的設定, tri-state設定,drive-strength的設定。

本文主要描述pin control subsystem中的low level driver,也就是驅動pin controller的driver。具體的硬件選用的是S3C2416的硬件平台。既然是代碼分析,本文不是非常多的描述框架性的內容,關於整個pin control subsystem軟件結構的描述請參考Linux內核中的GPIO系統之(2)。

閱讀本文需要device tree的知識,建議首先閱讀device tree代碼分析

 

二、pin controller相關的DTS描述

類似其他的硬件,pin controller這個HW block需要是device tree中的一個節點。此外,各個其他的HW block在驅動之前也需要先配置其引腳復用功能,因此,這些device(我們稱pin controller是host,那么這些使用pin controller進行引腳配置的device叫做client device)也需要在它自己的device tree node中描述pin control的相關內容

1、S3C2416 pin controller DTS結構

下面的偽代碼描述了S3C2416 pin controller 的DTS結構:

pinctrl@56000000 {  
        定義S3C2416 pin controller自己的屬性

        定義屬於S3C2416 pin controller的pin configurations

}

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

在pin controller node中定義pin configuration其目的是為了讓client device引用。所謂client device其實就是使用pin control subsystem提供服務的那些設備,例如串口設備。在使用之前,我們一般會在初始化代碼中配置相關的引腳功能是串口功能。有了device tree,我們可以通過device tree來傳遞這樣的信息。也就是說,各個device可以通過自己節點的屬性來指向pin controller的某個child node,也就是pin configuration了。samsung 24xx系列SOC的pin controller的pin configurations包括兩類,一類是定義pin bank,另外一類是定義功能復用配置。

2、pin configuration定義

我們舉兩個簡單的例子(當然一個是pin bank,另外一個是定義功能復用配置)來理解pin configuration第一個例子是描述pin bank:

pinctrl@56000000 {  
        定義S3C2416 pin controller自己的屬性

……

        gpf { 
            gpio-controller; 
            #gpio-cells = <0x2>; 
            interrupt-controller; 
            #interrupt-cells = <0x2>; 
            linux,phandle = <0xc>; 
            phandle = <0xc>; 
        };

……

}

其實S3C2416 pin controller定義了gpa到gpm共計11個sub node,每個sub node是描述S3C2416 GPIO controller的各個bank信息。S3C2416有138個I/O 端口(或者叫做pin、finger、pad)這些端口分成了11個bank(這里沒有用group這個術語,為了和pin group這個概念區分開,pin group的概念下面會具體描述):

Port A(GPA) : 25-output port 
Port B(GPB) : 9-input/output port 
Port C(GPC) : 16-input/output port 
Port D(GPD) : 16-input/output port 
Port E(GPE) : 16-input/output port 
Port F(GPF) : 8-input/output port 
Port G(GPG) : 8-input/output port 
Port H(GPH) : 15-input/output port 
Port K(GPK) : 16-input/output port 
Port L(GPL) : 7-input/output port 
Port M(GPM) : 2-input port

之所以分成bank,主要是把特性相同的GPIO進行分組,方便控制。例如:這些bank中,只有GPF和GPG這兩個bank上的引腳有中斷功能,其他的都沒有。interrupt-controller這個屬性相信大家已經熟悉,就是說明該node是一個interrupt controller。gpio-controller類似,說明該device node是一個GPIO controller。#gpio-cells屬性是一個GPIO controller的必須定義的屬性,它描述了需要多少個cell來具體描述一個GPIO(這是和具體的GPIO controller相關的)。#interrupt-cells的概念類似,不再贅述。phandle(linux,phandle這個屬性和phandle是一樣的,只不過linux,phandle是old-style,多定義一個屬性是為了兼容)定義了一個句柄,當其他的device node想要引用這個node的時候就可以使用該句柄。具體的例子參考下節client device的DTS的描述。

另外一個例子是uart的pin configuration,代碼如下:

pinctrl@56000000 {  
        定義S3C2416 pin controller自己的屬性

……

uart0-data { 
    samsung,pins = "gph-0", "gph-1"; 
    samsung,pin-function = <0x2>; 
    linux,phandle = <0x2>; 
    phandle = <0x2>; 
};

uart0-fctl { 
    samsung,pins = "gph-8", "gph-9"; 
    samsung,pin-function = <0x2>; 
    linux,phandle = <0x3>; 
    phandle = <0x3>; 
};

……

}

samsung,pins這個屬性定義了一個pin configuration所涉及到的引腳定義。對於uart0-data這個node,該配置涉及了gph bank中的第一個和第二個GPIO pin。一旦選擇了一個功能,那么samsung,pins定義的所有的引腳都需要做相應的功能設定,那么具體設定什么值呢?這就是samsung,pin-function定義的內容了。而具體設定哪個值則需要去查閱datasheet了。對於uart0-data,向gph bank中的第一個和第二個GPIO pin對應的配置寄存器中寫入2就可以把這兩個pin定義為uart功能。

3.client device的DTS

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

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

        pinctrl-names = "sleep", "active";------(1) 
        pinctrl-0 = <pin-config-0-a>;--------------(2) 
        pinctrl-1 = <pin-config-1-a pin-config-1-b>;         
    };

(1)pinctrl-names定義了一個state列表。那么什么是state呢?具體說應該是pin state,對於一個client device,它使用了一組pin,這一組pin應該同時處於某種狀態,畢竟這些pin是屬於一個具體的設備功能。state的定義和電源管理關系比較緊密,例如當設備active的時候,我們需要pin controller將相關的一組pin設定為具體的設備功能,而當設備進入sleep狀態的時候,需要pin controller將相關的一組pin設定為普通GPIO,並精確的控制GPIO狀態以便節省系統的功耗。state有兩種,標識,一種就是pinctrl-names定義的字符串列表,另外一種就是ID。ID從0開始,依次加一。根據例子中的定義,state ID等於0(名字是active)的state對應pinctrl-0屬性,state ID等於1(名字是idle)的state對應pinctrl-1屬性。具體設備state的定義和各個設備相關,具體參考在自己的device bind。

(2)pinctrl-x的定義。pinctrl-x是一個句柄(phandle)列表,每個句柄指向一個pin configuration。有時候,一個state對應多個pin configure。例如在active的時候,I2C功能有兩種配置,一種是從pin ID{7,8}引出,另外一個是從pin ID{69,103}引出。

我們選取samsung串口的dts定義如下:

serial@50000000 {  
        …… 
        pinctrl-names = "default"; 
        pinctrl-0 = <0x2 0x3>; 
    };

該serial device只定義了一個state就是default,對應pinctrl-0屬性定義。pinctrl-0是一個句柄(phandle)列表,每個句柄指向一個pin configuration。0x2對應上節中的uart0-data節點,0x03對應uart0-fctl 節點,也就是說,這個串口有兩種配置,一種是從gph bank中的第一個和第二個GPIO pin引出,另外一個是從gph bank中的第8個和第9個GPIO pin引出。

 

三、 pin controller driver初始化

1、注冊pin control device

舊的內核一般是在machine相關的代碼中建立靜態的platform device的數據結構,然后在machine初始化的時候,將靜態定義的platform device注冊到系統。不過在引入device tree之后,事情發生了變化。

根據device tree代碼分析,我們知道,在系統初始化的時候,dts描述的device node會形成一個樹狀結構,在machine初始化的過程中,會scan device node的樹狀結構,將真正的硬件device node變成一個個的設備模型中的device結構(比如struct platform_device)並加入到系統中。我們看看具體2416描述pin controller的dts code,如下:

pinctrl@56000000 { 
        reg = <0x56000000 0x1000="">; 
        compatible = "samsung,s3c2416-pinctrl";

……省略wakeup的pin configuration

……省略gpb~gpm這些pink bank的pin configuration

……省略Pin groups的相關描述

}

reg屬性描述pin controller硬件的地址信息,開始地址是0x56000000 ,地址長度是0x1000。compatible屬性用來描述pin controller的programming model。該屬性的值是string list,定義了一系列的modle(每個string是一個model)。這些字符串列表被操作系統用來選擇用哪一個pin controller driver來驅動該設備,后面的代碼會更詳細的描述。 pin control subsystem要想進行控制,必須首先了解自己控制的對象,也就是說軟件需要提供一個方法將各種硬件信息(total有多少可控的pin,有多少bank,pin的復用情況以及pin的配置情況)注冊到pin control subsystem中,這也是pin controller driver的初始化的主要內容。這些信息當然可以通過定義靜態的表格(參考linux/drivers/pinctrl目錄下的pinctrl-u300.c文件,該文件定義了一個大數組u300_pads來描述每一個pin),也可以通過dts加上靜態表格的方式(2416采用的方式)。

2、注冊pin controller driver

當然,pinctrl@56000000這個device node也會變成一個platform device加入系統。有了device,那么對應的platform driver是如何注冊到系統中的呢?代碼如下:

static int __init samsung_pinctrl_drv_register(void) 

   ……

    return platform_driver_register(&samsung_pinctrl_driver); 
}

系統初始化的時候,該函數會執行,向系統注冊了samsung_pinctrl_driver:

static struct platform_driver samsung_pinctrl_driver = { 
    .probe        = samsung_pinctrl_probe, ----該driver的初始化函數 
    .driver = { 
        .name    = "samsung-pinctrl", 
        .owner    = THIS_MODULE, 
        .of_match_table = samsung_pinctrl_dt_match, ----匹配列表 
    }, 
};

 

3、probe過程(driver初始化過程)

在linux kernel引入統一設備模型之后,bus、driver和device形成了設備模型中的鐵三角。對於platform這種類型的bus,其鐵三角數據是platform_bus_type(表示platform這種類型的bus)、struct platform_device(platform bus上的device)、struct platform_driver(platform bus上的driver)。統一設備模型大大降低了驅動工程師的工作量,驅動工程師只要將driver注冊到系統即可,剩余的事情交給統一設備模型來完成。每次系統增加一個platform_driver,platform_bus_type都會啟動scan過程,讓新加入的driver掃描整個platform bus上的device的鏈表,看看是否有device讓該driver驅動。同樣的,每次系統增加一個platform_device,platform_bus_type也會啟動scan過程,遍歷整個platform bus上的driver的鏈表,看看是否有適合驅動該device的driver。具體匹配的代碼是platform bus上的match函數,如下:

static int platform_match(struct device *dev, struct device_driver *drv) 

    struct platform_device *pdev = to_platform_device(dev); 
    struct platform_driver *pdrv = to_platform_driver(drv);

    /* Attempt an OF style match first */ 
    if (of_driver_match_device(dev, drv)) 
        return 1;

    /* Then try ACPI style match */ 
    if (acpi_driver_match_device(dev, drv)) 
        return 1;

    /* Then try to match against the id table */ 
    if (pdrv->id_table) 
        return platform_match_id(pdrv->id_table, pdev) != NULL;

    /* fall-back to driver name match */ 
    return (strcmp(pdev->name, drv->name) == 0); 
}

舊的的platform的匹配函數就是簡單的比較device和driver的名字,多么簡單,多么清晰,真是有點懷念過去單純而美好的生活。當然,事情沒有那么糟糕,我們這里只要關注OF style的匹配過程即可,思路很簡單,解鈴還需系鈴人,把匹配過程推給device tree模塊,代碼如下:

const struct of_device_id *of_match_device(const struct of_device_id *matches, 
                       const struct device *dev) 

    if ((!matches) || (!dev->of_node)) 
        return NULL; 
    return of_match_node(matches, dev->of_node); 
}

platform driver提供了match table(struct device_driver 中的of_match_table的成員)。platform device提供了device tree node(struct device中的of_node成員)。對於我們這個場景,match table是samsung_pinctrl_dt_match,代碼如下:

static const struct of_device_id samsung_pinctrl_dt_match[] = { 
…… 
    { .compatible = "samsung,s3c2416-pinctrl", 
        .data = s3c2416_pin_ctrl }, 
…… 
    {}, 
};

再去看看dts中pin controller的節點compatible屬性的定義,你會禁不住感慨:啊,終於遇到對的人。這里還要特別說明的是data成員被設定為s3c2416_pin_ctrl ,它描述了2416的HW pin controller,我們稱之samsung pin controller的描述符,初始化的過程中需要這個數據結構,后面還會詳細介紹它。一旦pin controller這個device遇到了適當的driver,就會調用probe函數進行具體的driver初始化的動作了,代碼如下:

static int samsung_pinctrl_probe(struct platform_device *pdev) 

    struct samsung_pinctrl_drv_data *drvdata; 
    struct device *dev = &pdev->dev; 
    struct samsung_pin_ctrl *ctrl; 
    struct resource *res; 
    int ret;

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

    ctrl = samsung_pinctrl_get_soc_data(drvdata, pdev); ----------(2) 
    drvdata->ctrl = ctrl; 
    drvdata->dev = dev;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -----分配memory資源 
    drvdata->virt_base = devm_ioremap_resource(&pdev->dev, res); 
    if (IS_ERR(drvdata->virt_base)) 
        return PTR_ERR(drvdata->virt_base);

    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ------分配IRQ資源 
    if (res) 
        drvdata->irq = res->start;

    ret = samsung_gpiolib_register(pdev, drvdata); -------------(3)

    ret = samsung_pinctrl_register(pdev, drvdata); -------------(4)

    if (ctrl->eint_gpio_init) ------------------(5) 
        ctrl->eint_gpio_init(drvdata); 
    if (ctrl->eint_wkup_init) 
        ctrl->eint_wkup_init(drvdata);

    platform_set_drvdata(pdev, drvdata); -設定platform device的私有數據為samsung_pinctrl_drv_data

    /* Add to the global list */ 
    list_add_tail(&drvdata->node, &drvdata_list); --掛入全局鏈表

    return 0; 
}

(1)devm_kzalloc函數是為struct samsung_pinctrl_drv_data數據結構分配內存。每當driver probe一個具體的device實例的時候,都需要建立一些私有的數據結構來保存該device的一些具體的硬件信息(本場景中,這個數據結構就是struct samsung_pinctrl_drv_data)。在過去,驅動工程師多半使用kmalloc或者kzalloc來分配內存,但這會帶來一些潛在的問題。例如:在初始化過程中,有各種各樣可能的失敗情況,這時候就依靠driver工程師小心的撰寫代碼,釋放之前分配的內存。當然,初始化過程中,除了memory,driver會為probe的device分配各種資源,例如IRQ 號,io memory map、DMA等等。當初始化需要管理這么多的資源分配和釋放的時候,很多驅動程序都出現了資源管理的issue。而且,由於這些issue是異常路徑上的issue,不是那么容易測試出來,更加重了解決這個issue的必要性。內核解決這個問題的模式(所謂解決一類問題的設計方法就叫做設計模式)是Devres,即device resource management軟件模塊。更細節的內容就不介紹了,其核心思想就是資源是設備的資源,那么資源的管理歸於device,也就是說不需要driver過多的參與。當device和driver detach的時候,device會自動的釋放其所有的資源。

(2)分配了struct samsung_pinctrl_drv_data數據結構的內存,當然下一步就是初始化這個數據結構了。我們先看看2416的pin controller driver是如何定義該數據結構的:

struct samsung_pinctrl_drv_data { 
    struct list_head        node;---------多個pin controller的描述符可以形成鏈表 
    void __iomem            *virt_base;---------訪問硬件寄存器的基地址 
    struct device            *dev;-----------和platform device建立聯系 
    int                irq; --------irq number,對於2416 pin control硬件而言,不需要irq資源

    struct samsung_pin_ctrl        *ctrl;----samsung pin controller描述符 
    struct pinctrl_desc        pctl; ------指向pin control subsystem中core driver中抽象的

                                                              pin controller描述符。 
    struct pinctrl_dev        *pctl_dev; ------指向core driver的pin controller class device

    const struct samsung_pin_group    *pin_groups; -描述samsung pin controller中pin groups的信息 
    unsigned int            nr_groups; --------描述samsung pin controller中pin groups的數目 
    const struct samsung_pmx_func    *pmx_functions; --描述samsung pin controller中function信息
    unsigned int            nr_functions; --------描述samsung pin controller中function的數目 
};

struct pinctrl_desc和struct pinctrl_dev 都是pin control subsystem中core driver的概念。各個具體硬件的pin controller可能會各不相同,但是可以抽取其共同的部分來形成一個HW independent的數據結構,這個數據就是pin controller描述符,在core driver中用struct pinctrl_desc表示,具體該數據結構的定義如下:

struct pinctrl_desc { 
    const char *name; 
    struct pinctrl_pin_desc const *pins;---指向npins個pin描述符,每個描述符描述一個pin 
    unsigned int npins;------------該pin controller中有多少個可控的pin 
    const struct pinctrl_ops *pctlops;------callback函數,是core driver和底層driver的接口 
    const struct pinmux_ops *pmxops;-----callback函數,是core driver和底層driver的接口 
    const struct pinconf_ops *confops;-----callback函數,是core driver和底層driver的接口 
    struct module *owner; 
};

其實整個初始化過程的核心思想就是low level的driver定義一個pinctrl_desc ,設定pin相關的定義和callback函數,注冊到pin control subsystem中。我們用引腳描述符(pin descriptor)來描述一個pin。在pin control subsystem中,struct pinctrl_pin_desc用來描述一個可以控制的引腳,我們稱之引腳描述符,代碼如下:

struct pinctrl_pin_desc { 
    unsigned number;-------ID,在本pin controller中唯一標識該引腳 
    const char *name;-------user friedly name 
    void *drv_data; 
};

冰冷的pin ID是給機器用的,而name是給用戶使用的,是ascii字符。

struct pinctrl_dev在pin control subsystem的core driver中抽象一個pin control device。其實我們可以通過多個層面來定義一個device。在這個場景下,pin control subsystem的core driver關注的是一類pin controller的硬件設備,具體其底層是什么硬件連接方式,使用什么硬件協議它不關心,它關心的是pin controller這類設備所有的通用特性和功能。當然2416的pin controller是通過platform bus連接的,因此,在low level的層面,需要一個platform device來標識2416的pin controller(需要注意的是:pin controller class device和platform device都是基於一個驅動模型中的device派生而來的,這里struct device是基類,struct pinctrl_dev和struct platform_device都是派生類,當然c本身不支持class,但面向對象的概念是同樣的)。為了充分理解class這個概念,我們再舉一個例子。對於audio的硬件抽象層,它應該管理所有的audio設備,因此這個軟件模塊應該有一個audio class的鏈表,連接了所有的系統中的audio設備。但這些具體的audio設備可能是PCI接口的audio設備,也可能是usb接口的audio設備,從具體的總線層面來看,也會有PCI或者USB設備來抽象對應的聲卡設備。

OK,我們再看看samsung_pinctrl_drv_data底部四個成員,要理解該數據結構底部的四個成員,還要理解什么是pin mux function,什么是pin group。對於SOC而言,其引腳除了配置成普通GPIO之外,若干個引腳還可以組成一個pin group,形成特定的功能。例如pin number是{ 0, 8, 16, 24 }這四個引腳組合形成一個pin group,提供SPI的功能。既然有了pin group的概念,為何又有function這個概念呢?什么是function呢?SPI是function,I2C也是一個function,當然GPIO也是一個function。一個function有可能對應一組或者多組pin。例如:為了設計靈活,芯片內部的SPI0的功能可能引出到pin group { A8, A7, A6, A5 },也可能引出到另外一個pin group{ G4, G3, G2, G1 },但毫無疑問,這兩個pin group不能同時active,畢竟芯片內部的SPI0的邏輯功能電路只有一個。 從這個角度看,pin control subsystem要進行功能設定的時候必須要給出function以及function的pin group才能確定所有的物理pin的位置。

我們前面已經說過了,struct samsung_pinctrl_drv_data數據結構就是2416的pin controller driver要驅動2416的HW pin controller的私有數據結構。這個數據結構中最重要的就是samsung pin controller描述符了。關於pin controller有兩個描述符,一個是struct pinctrl_desc,是具體硬件無關的pin controller的描述符。struct samsung_pin_ctrl描述的具體samsung pin controller硬件相關的信息,比如說:pin bank的信息,不是所有的pin controller都是分bank的,因此pin bank的信息只能封裝在low level的samsung pin controller driver中。這個數據結構定義如下:

struct samsung_pin_ctrl { 
    struct samsung_pin_bank    *pin_banks;----定義具體的pin bank信息 
    u32        nr_banks; ---------number of pin bank

    u32        base;----該pin controller的pin ID base。 
    u32        nr_pins; -----總的可以控制的pin的數目

其他成員和本場景無關,和GPIO type的中斷控制器驅動代碼有關 
};

關於上面的base可以多說兩句。實際上,系統支持多個pin controller設備,這時候,系統要管理多個pin controller控制下的多個pin。每個pin有自己的pin ID,是唯一的,假設系統中有兩個pin controller,一個是A,控制32個,另外一個是B,控制64個pin,我們可以統一編號,對A,pin ID從0~31,對於B,pin ID是從32~95。對於B,其pin base就是32。

samsung_pinctrl_probe->samsung_pinctrl_get_soc_data函數中會根據device tree的信息和靜態定義的table來初始化該描述符,具體的代碼如下:

static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data( 
                struct samsung_pinctrl_drv_data *d, 
                struct platform_device *pdev) 

    int id; 
    const struct of_device_id *match; 
    struct device_node *node = pdev->dev.of_node; ---獲取device tree中的device node指針 
    struct device_node *np; 
    struct samsung_pin_ctrl *ctrl; 
    struct samsung_pin_bank *bank; 
    int i;

    id = of_alias_get_id(node, "pinctrl"); 
    match = of_match_node(samsung_pinctrl_dt_match, node); 
    ctrl = (struct samsung_pin_ctrl *)match->data + id; --------A

    bank = ctrl->pin_banks; 
    for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {------------B  
        spin_lock_init(&bank->slock); 
        bank->drvdata = d; 
        bank->pin_base = ctrl->nr_pins; ---ctrl->nr_pins初始的時候等於0,最后完成bank初始化后,

                                                                該值等於total的pin number。 
        ctrl->nr_pins += bank->nr_pins; 
    }

for_each_child_of_node(node, np) {  ----------------C 
        if (!of_find_property(np, "gpio-controller", NULL)) 
            continue; 
        bank = ctrl->pin_banks; 
        for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { 
            if (!strcmp(bank->name, np->name)) { 
                bank->of_node = np; 
                break; 
            } 
        } 
    } 

ctrl->base = pin_base; ----------------------D 
    pin_base += ctrl->nr_pins;

    return ctrl; 
}

samsung_pinctrl_get_soc_data這個函數名字基本反應了其功能,2416是samsung的一個具體的SOC型號,調用該函數可以返回一個表示2416 SOC的samsung pin controller的描述符。

A:這段代碼主要是獲取具體的2416的HW pin controller的信息,該數據結構在上文中出現過(具體參考pin controller的device tree match table:samsung_pinctrl_dt_match),就是s3c2416_pin_ctrl這個變量。這個變量定義了2416的pin controller的信息(S3C2416的pin controller的pin bank信息是定義在pin controller driver的靜態數據,其實最好在dts中定義)如下:

struct samsung_pin_ctrl s3c2416_pin_ctrl[] = { 
    { 
        .pin_banks    = s3c2416_pin_banks,------靜態定義的2416的pin bank的信息 
        .nr_banks    = ARRAY_SIZE(s3c2416_pin_banks), 
        .eint_wkup_init = s3c24xx_eint_init, 
        .label        = "S3C2416-GPIO", 
    }, 
};

這個變量中包含了2416的pin bank的信息,包括:有多少個pin bank,每個bank中有多少個pin,pin bank的名字是什么,寄存器的offset是多少。這些信息用來初始化pin controller描述符數據結構。

B:初始化2416 samsung pin controller中各個bank的描述符。

C:device tree中表示pin controller的device node有若干的child node,分別表示gpa~gpl這11個bank,每個bank都是一個gpio controller。下面的代碼遍歷各個child node,並初始化各個bank描述符中的device tree node成員。 這里需要注意的是靜態定義的pin bank的名字要和dts文件中定義的pin bank node的名字一樣。

D:系統中有可能有多個pin controller,多個pin controller上的pin ID 應該是系統唯一的,ctrl->base表示本pin controller中的pin ID的起始值。

(3)本來pin control subsystem和GPIO subsystem應該是無關的兩個子系統,應該各自進行自己的初始化過程。但實際中,由於硬件的復雜性,這兩個子系統耦合性非常高。這里samsung_gpiolib_register函數就是把各個bank代表的gpio chip注冊到GPIO subsystem中。更具體的信息請參考GPIO subsystem軟件框架文檔。

(4)samsung_pinctrl_register函數的主要功能是將本pin controller注冊到pin control subsystem。代碼如下:

static int samsung_pinctrl_register(struct platform_device *pdev, 
                    struct samsung_pinctrl_drv_data *drvdata) 

    struct pinctrl_desc *ctrldesc = &drvdata->pctl; 
    struct pinctrl_pin_desc *pindesc, *pdesc; 
    struct samsung_pin_bank *pin_bank; 
    char *pin_names; 
    int pin, bank, ret;

    ctrldesc->name = "samsung-pinctrl";--------A 
    ctrldesc->owner = THIS_MODULE; 
    ctrldesc->pctlops = &samsung_pctrl_ops; ---call 函數,具體參考第四章的內容 
    ctrldesc->pmxops = &samsung_pinmux_ops; 
    ctrldesc->confops = &samsung_pinconf_ops;

    pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) *-------B 
            drvdata->ctrl->nr_pins, GFP_KERNEL); 
    ctrldesc->pins = pindesc; 
    ctrldesc->npins = drvdata->ctrl->nr_pins;  
    for (pin = 0, pdesc = pindesc; pin < ctrldesc->npins; pin++, pdesc++)---C 
        pdesc->number = pin + drvdata->ctrl->base;


    pin_names = devm_kzalloc(&pdev->dev, sizeof(char) * PIN_NAME_LENGTH *---B 
                    drvdata->ctrl->nr_pins, GFP_KERNEL);


    for (bank = 0; bank < drvdata->ctrl->nr_banks; bank++) { ---------C 
        pin_bank = &drvdata->ctrl->pin_banks[bank]; 
        for (pin = 0; pin < pin_bank->nr_pins; pin++) { 
            sprintf(pin_names, "%s-%d", pin_bank->name, pin); 
            pdesc = pindesc + pin_bank->pin_base + pin; 
            pdesc->name = pin_names; 
            pin_names += PIN_NAME_LENGTH; 
        } 
    }

    ret = samsung_pinctrl_parse_dt(pdev, drvdata);------D

    drvdata->pctl_dev = pinctrl_register(ctrldesc, &pdev->dev, drvdata);---E

    for (bank = 0; bank < drvdata->ctrl->nr_banks; ++bank) {-----F 
        pin_bank = &drvdata->ctrl->pin_banks[bank]; 
        pin_bank->grange.name = pin_bank->name; 
        pin_bank->grange.id = bank; 
        pin_bank->grange.pin_base = pin_bank->pin_base; 
        pin_bank->grange.base = pin_bank->gpio_chip.base; 
        pin_bank->grange.npins = pin_bank->gpio_chip.ngpio; 
        pin_bank->grange.gc = &pin_bank->gpio_chip; 
        pinctrl_add_gpio_range(drvdata->pctl_dev, &pin_bank->grange); 
    }

    return 0; 
}

A:初始化硬件無關的pin controller描述符(struct samsung_pinctrl_drv_data中的pctl成員)。該數據結構中還包含了所有pin的描述符的信息,這些pin descriptor所需要的內存在步驟B中分配

B:初始化過程中涉及不少內存分配,這些內存主要用於描述每一個pin(術語叫做pin descriptor)以及pin name。

C:初始化每一個pin 描述符的名字和ID。對於samsung的pin描述符,其名字使用pin-bank name + pin ID的形式。 ID的分配是從該pin controller的pin base開始分配ID的,逐個加一。

D:初始化pin group和function(具體內容在下節描述)

E:調用pinctrl_register注冊到pin control subsystem 。這是pin control subsystem的核心函數,可以參考GPIO系統之2的描述。

F:在這里又不得不進行pin control subsystem和GPIO系統的耦合了。每個bank都是一個GPIO controller,但是pin bank使用的ID是pin control space中的ID,GPIO 子系統中使用的是GPIO space的ID,對於pin control subsystem而言,它需要建立這兩個ID的映射關系。pinctrl_add_gpio_range就是起這個作用的。更具體的內容請參考pin control subsystem軟件結構文檔。 需要注意的是直接在pin controller driver中調用pinctrl_add_gpio_range是不推薦的,建議使用dts的方式在GPIO controller設備節點中描述。

(5)這里的代碼是向kernel中的中斷子系統注冊interrupt controller。對於2416,有兩個bank有中斷功能,gpf和gpg,本質上gpf和gpg就是兩個interrupt controller,掛接在2416真正的那個interrupt contrller之下,形成樹狀結構。具體的代碼就不分析了,請參考GPIO類型的中斷控制器代碼分析。

 

4、pin control subsystem如何獲取pin group的信息

具體的代碼如下:

static int samsung_pinctrl_parse_dt(struct platform_device *pdev, 
                    struct samsung_pinctrl_drv_data *drvdata) 

    struct device *dev = &pdev->dev; 
    struct device_node *dev_np = dev->of_node; 
    struct device_node *cfg_np; 
    struct samsung_pin_group *groups, *grp; 
    struct samsung_pmx_func *functions, *func; 
    unsigned *pin_list; 
    unsigned int npins, grp_cnt, func_idx = 0; 
    char *gname, *fname; 
    int ret;

    grp_cnt = of_get_child_count(dev_np); ------(1)

    groups = devm_kzalloc(dev, grp_cnt * sizeof(*groups), GFP_KERNEL); ----(2) 
    grp = groups;

    functions = devm_kzalloc(dev, grp_cnt * sizeof(*functions), GFP_KERNEL); ---(2) 
    func = functions;


    for_each_child_of_node(dev_np, cfg_np) { ----遍歷pin control的所有的child node 
        u32 function;  
      if (!of_find_property(cfg_np, "samsung,pins", NULL)) -忽略掉那些沒有samsung,pins屬性的node 
            continue;

        ret = samsung_pinctrl_parse_dt_pins(pdev, cfg_np, --------(3) 
                    &drvdata->pctl,    &pin_list, &npins); 
        if (ret) 
            return ret;

        /* derive pin group name from the node name */  
  gname = devm_kzalloc(dev, strlen(cfg_np->name) + GSUFFIX_LEN, -分配pin group名字需要的內存
                    GFP_KERNEL);

    sprintf(gname, "%s%s", cfg_np->name, GROUP_SUFFIX);--添加“-grp”的后綴

        grp->name = gname; ----------------(4) 
        grp->pins = pin_list; 
        grp->num_pins = npins; 
        of_property_read_u32(cfg_np, "samsung,pin-function", &function); 
        grp->func = function; 
        grp++;

        if (!of_find_property(cfg_np, "samsung,pin-function", NULL))  
            continue; ----忽略掉那些沒有samsung,pin-function屬性的node

        /* derive function name from the node name */ 
        fname = devm_kzalloc(dev, strlen(cfg_np->name) + FSUFFIX_LEN, 
                    GFP_KERNEL);  
        sprintf(fname, "%s%s", cfg_np->name, FUNCTION_SUFFIX); -----(5)

        func->name = fname; 
        func->groups = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL); ----(6) 
        if (!func->groups) { 
            dev_err(dev, "failed to alloc memory for group list " 
                    "in pin function"); 
            return -ENOMEM; 
        } 
        func->groups[0] = gname; 
        func->num_groups = 1; 
        func++; 
        func_idx++; 
    }

    drvdata->pin_groups = groups; ----最終,pin group和function的信息被copy到pin controller

                                                            driver的私有數據結構struct samsung_pinctrl_drv_data 中 
    drvdata->nr_groups = grp_cnt; 
    drvdata->pmx_functions = functions; 
    drvdata->nr_functions = func_idx;

    return 0; 
}

(1)pin controller的device node有若干個child node,每個child node都描述了一個pin configuration。of_get_child_count函數可以獲取pin configuration的數目。

(2)根據pin configuration的數目分配內存。在這里共計分配了兩片內存,一片保存了所有pin group的信息(struct samsung_pin_group ),一片保存了pin mux function的信息(struct samsung_pmx_func)。實際上,分配pin configuration的數目的內存有些浪費,因為不是每一個pin control的child node都是和pin group相關(例如pin bank node就是和pin group無關)。對於function,就更浪費了,因為有可能多個pin group對應一個function。

(3)samsung_pinctrl_parse_dt_pins函數主要分析samsung,pins這個屬性,並根據屬性值返回一個pin list,該list中每個entry是一個pin ID。

(4)初始化samsung pin group的描述符。具體的數據結構解釋如下:

struct samsung_pin_group { 
    const char        *name;---------pin group的名字,名字是device tree node name+-grp 
    const unsigned int    *pins;-------pin list的信息 
    u8            num_pins;----------pin list中的數目 
    u8            func;------------對應samsung,pin-function屬性的值,用來配置pin list中各個pin的功能設定寄存器 
};

(5)一個pin configuration的device tree node被解析成兩個描述符,一個是samsung pin group的描述符,另外一個是samsung pin mux function描述符。這兩個描述符的名字都是根據dts file中的pin configuration的device node name生成,只不過pin group的名字附加-grp的后綴,而function描述符的名字后面附加-mux的后綴。

(6)對於samsung pin mux function描述符解釋如下:

struct samsung_pmx_func { 
    const char        *name;------pin function的名字,名字是device tree node name+-mux

    const char        **groups;-----指向pin groups的指針數組 
    u8            num_groups;------屬於該function的pin group的個數 
};

在具體的代碼實現中num_groups總是等於1。

 

四、S3C2416 pin controller driver的操作函數

1、操作函數概述

pin controller描述符中包括了三類操作函數:pctlops是一些全局的控制函數,pmxops是復用引腳相關的操作函數,confops操作函數是用來配置引腳的特性(例如:pull-up/down)。這些callback函數都是和具體的底層pin controller的操作相關。

本章節主要描述這些call back函數的邏輯,這些callback的調用時機不會在這里描述,那些內容請參考pin control subsystem的描述。

2、struct pinctrl_ops中各個callback函數的具體的解釋如下:

(1)samsung_get_group_count

該函數的代碼如下:

static int samsung_get_group_count(struct pinctrl_dev *pctldev) 

    struct samsung_pinctrl_drv_data *drvdata;

    drvdata = pinctrl_dev_get_drvdata(pctldev); 
    return drvdata->nr_groups; 
}

該函數主要是用來獲取指定pin control device的pin group的數目。邏輯很簡單,通過pin control的class device的driver_data成員可以獲得samsung pin control driver的私有數據(struct samsung_pinctrl_drv_data),可以nr_groups成員返回group的數目。

(2)samsung_get_group_name

該函數的代碼如下:

static const char *samsung_get_group_name(struct pinctrl_dev *pctldev, 
                        unsigned selector) 

    struct samsung_pinctrl_drv_data *drvdata;

    drvdata = pinctrl_dev_get_drvdata(pctldev); 
    return drvdata->pin_groups[selector].name; 
}

該函數主要用來獲取指定group selector的pin group信息。

(3)samsung_get_group_pins

該函數的代碼如下:

static int samsung_get_group_pins(struct pinctrl_dev *pctldev, 
        unsigned selector, const unsigned **pins, unsigned *num_pins) 

    struct samsung_pinctrl_drv_data *drvdata;

    drvdata = pinctrl_dev_get_drvdata(pctldev); 
    *pins = drvdata->pin_groups[selector].pins; 
    *num_pins = drvdata->pin_groups[selector].num_pins; 
    return 0; 
}

該函數的主要功能是給定一個group selector(index),獲取該pin group中pin的信息(該pin group包括多少個pin,每個pin的ID是什么) 。

(4)samsung_dt_node_to_map

該函數的代碼如下:

static int samsung_dt_node_to_map(struct pinctrl_dev *pctldev, 
            struct device_node *np, struct pinctrl_map **maps, 
            unsigned *nmaps) 

    struct device *dev = pctldev->dev; 
    struct pinctrl_map *map; 
    unsigned long *cfg = NULL; 
    char *gname, *fname; 
    int cfg_cnt = 0, map_cnt = 0, idx = 0;

    /* count the number of config options specfied in the node */ 
    for (idx = 0; idx < ARRAY_SIZE(pcfgs); idx++) { 
        if (of_find_property(np, pcfgs[idx].prop_cfg, NULL)) 
            cfg_cnt++; 
    }

    /* 
     * Find out the number of map entries to create. All the config options 
     * can be accomadated into a single config map entry. 
     */ 
    if (cfg_cnt) 
        map_cnt = 1; 
    if (of_find_property(np, "samsung,pin-function", NULL)) 
        map_cnt++; 
    if (!map_cnt) { 
        dev_err(dev, "node %s does not have either config or function " 
                "configurations\n", np->name); 
        return -EINVAL; 
    }

    /* Allocate memory for pin-map entries */ 
    map = kzalloc(sizeof(*map) * map_cnt, GFP_KERNEL); 
    if (!map) { 
        dev_err(dev, "could not alloc memory for pin-maps\n"); 
        return -ENOMEM; 
    } 
    *nmaps = 0;

    /* 
     * Allocate memory for pin group name. The pin group name is derived 
     * from the node name from which these map entries are be created. 
     */ 
    gname = kzalloc(strlen(np->name) + GSUFFIX_LEN, GFP_KERNEL); 
    if (!gname) { 
        dev_err(dev, "failed to alloc memory for group name\n"); 
        goto free_map; 
    } 
    sprintf(gname, "%s%s", np->name, GROUP_SUFFIX);

    /* 
     * don't have config options? then skip over to creating function 
     * map entries. 
     */ 
    if (!cfg_cnt) 
        goto skip_cfgs;

    /* Allocate memory for config entries */ 
    cfg = kzalloc(sizeof(*cfg) * cfg_cnt, GFP_KERNEL); 
    if (!cfg) { 
        dev_err(dev, "failed to alloc memory for configs\n"); 
        goto free_gname; 
    }

    /* Prepare a list of config settings */ 
    for (idx = 0, cfg_cnt = 0; idx < ARRAY_SIZE(pcfgs); idx++) { 
        u32 value; 
        if (!of_property_read_u32(np, pcfgs[idx].prop_cfg, &value)) 
            cfg[cfg_cnt++] = 
                PINCFG_PACK(pcfgs[idx].cfg_type, value); 
    }

    /* create the config map entry */ 
    map[*nmaps].data.configs.group_or_pin = gname; 
    map[*nmaps].data.configs.configs = cfg; 
    map[*nmaps].data.configs.num_configs = cfg_cnt; 
    map[*nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP; 
    *nmaps += 1;

skip_cfgs: 
    /* create the function map entry */ 
    if (of_find_property(np, "samsung,pin-function", NULL)) { 
        fname = kzalloc(strlen(np->name) + FSUFFIX_LEN,    GFP_KERNEL); 
        if (!fname) { 
            dev_err(dev, "failed to alloc memory for func name\n"); 
            goto free_cfg; 
        } 
        sprintf(fname, "%s%s", np->name, FUNCTION_SUFFIX);

        map[*nmaps].data.mux.group = gname; 
        map[*nmaps].data.mux.function = fname; 
        map[*nmaps].type = PIN_MAP_TYPE_MUX_GROUP; 
        *nmaps += 1; 
    }

    *maps = map; 
    return 0;

free_cfg: 
    kfree(cfg); 
free_gname: 
    kfree(gname); 
free_map: 
    kfree(map); 
    return -ENOMEM; 
}

具體分析TODO

(5)samsung_dt_free_map

該函數的代碼如下:

static void samsung_dt_free_map(struct pinctrl_dev *pctldev, 
                 struct pinctrl_map *map, unsigned num_maps) 

    int idx;

    for (idx = 0; idx < num_maps; idx++) { 
        if (map[idx].type == PIN_MAP_TYPE_MUX_GROUP) { 
            kfree(map[idx].data.mux.function); 
            if (!idx) 
                kfree(map[idx].data.mux.group); 
        } else if (map->type == PIN_MAP_TYPE_CONFIGS_GROUP) { 
            kfree(map[idx].data.configs.configs); 
            if (!idx) 
                kfree(map[idx].data.configs.group_or_pin); 
        } 
    };

    kfree(map); 
}

具體分析TODO

3、復用引腳相關的操作函數struct pinmux_ops的具體解釋如下:

(1)samsung_get_functions_count

該函數的代碼如下:

static int samsung_get_functions_count(struct pinctrl_dev *pctldev) 

    struct samsung_pinctrl_drv_data *drvdata;

    drvdata = pinctrl_dev_get_drvdata(pctldev); 
    return drvdata->nr_functions; 
}

該函數的主要功能是就是返回pin controller device支持的function的數目


(2)samsung_pinmux_get_fname

該函數的代碼如下:

static const char *samsung_pinmux_get_fname(struct pinctrl_dev *pctldev, 
                        unsigned selector) 

    struct samsung_pinctrl_drv_data *drvdata;

    drvdata = pinctrl_dev_get_drvdata(pctldev); 
    return drvdata->pmx_functions[selector].name; 
}

該函數的主要功能是就是:給定一個function selector(index),獲取指定function的name  
  
(3)samsung_pinmux_get_groups

該函數的代碼如下:

static int samsung_pinmux_get_groups(struct pinctrl_dev *pctldev, 
        unsigned selector, const char * const **groups, 
        unsigned * const num_groups) 

    struct samsung_pinctrl_drv_data *drvdata;

    drvdata = pinctrl_dev_get_drvdata(pctldev); 
    *groups = drvdata->pmx_functions[selector].groups; 
    *num_groups = drvdata->pmx_functions[selector].num_groups; 
    return 0; 
}

該函數的主要功能是就是:給定一個function selector(index),獲取指定function的pin groups信息 
  
(4)samsung_pinmux_enable和samsung_pinmux_disable

這個兩個callback函數都是通過samsung_pinmux_setup實現,該函數的代碼如下:

static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector, 
                    unsigned group, bool enable) 

    struct samsung_pinctrl_drv_data *drvdata; 
    const unsigned int *pins; 
    struct samsung_pin_bank *bank; 
    void __iomem *reg; 
    u32 mask, shift, data, pin_offset, cnt; 
    unsigned long flags;

    drvdata = pinctrl_dev_get_drvdata(pctldev); 
    pins = drvdata->pin_groups[group].pins;

    /* 
     * for each pin in the pin group selected, program the correspoding pin 
     * pin function number in the config register. 
     */ 
    for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++) { 
        struct samsung_pin_bank_type *type;

        pin_to_reg_bank(drvdata, pins[cnt] - drvdata->ctrl->base, 
                ®, &pin_offset, &bank); 
        type = bank->type; 
        mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1; 
        shift = pin_offset * type->fld_width[PINCFG_TYPE_FUNC]; 
        if (shift >= 32) { 
            /* Some banks have two config registers */ 
            shift -= 32; 
            reg += 4; 
        }

        spin_lock_irqsave(&bank->slock, flags);

        data = readl(reg + type->reg_offset[PINCFG_TYPE_FUNC]); 
        data &= ~(mask << shift); 
        if (enable) 
            data |= drvdata->pin_groups[group].func << shift; 
        writel(data, reg + type->reg_offset[PINCFG_TYPE_FUNC]);

        spin_unlock_irqrestore(&bank->slock, flags); 
    } 
}

該函數主要用來enable一個指定function。具體指定function的時候要給出function selector和pin group的selector 。具體的操作涉及很多底層的寄存器操作(TODO)。 
  
  
(5)samsung_pinmux_gpio_set_direction

該函數的代碼如下:

static int samsung_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev, 
        struct pinctrl_gpio_range *range, unsigned offset, bool input) 

    struct samsung_pin_bank_type *type; 
    struct samsung_pin_bank *bank; 
    struct samsung_pinctrl_drv_data *drvdata; 
    void __iomem *reg; 
    u32 data, pin_offset, mask, shift; 
    unsigned long flags;

    bank = gc_to_pin_bank(range->gc); 
    type = bank->type; 
    drvdata = pinctrl_dev_get_drvdata(pctldev);

    pin_offset = offset - bank->pin_base; 
    reg = drvdata->virt_base + bank->pctl_offset + 
                    type->reg_offset[PINCFG_TYPE_FUNC];

    mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1; 
    shift = pin_offset * type->fld_width[PINCFG_TYPE_FUNC]; 
    if (shift >= 32) { 
        /* Some banks have two config registers */ 
        shift -= 32; 
        reg += 4; 
    }

    spin_lock_irqsave(&bank->slock, flags);

    data = readl(reg); 
    data &= ~(mask << shift); 
    if (!input) 
        data |= FUNC_OUTPUT << shift; 
    writel(data, reg);

    spin_unlock_irqrestore(&bank->slock, flags);

    return 0; 
}

該函數用來設定GPIO的方向。 
 

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

(1)samsung_pinconf_get 
(2)samsung_pinconf_set 
(3)samsung_pinconf_group_get 
(4)samsung_pinconf_group_set

(1)和(2)是對單個pin的配置進行讀取或者設定,(3)和(4)是對pin group中的所有pin進行配置進行讀取或者設定。這些函數的底層都是samsung_pinconf_rw,該函數代碼如下:

static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin, 
                unsigned long *config, bool set) 

    struct samsung_pinctrl_drv_data *drvdata; 
    struct samsung_pin_bank_type *type; 
    struct samsung_pin_bank *bank; 
    void __iomem *reg_base; 
    enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE(*config); 
    u32 data, width, pin_offset, mask, shift; 
    u32 cfg_value, cfg_reg; 
    unsigned long flags;

    drvdata = pinctrl_dev_get_drvdata(pctldev); 
    pin_to_reg_bank(drvdata, pin - drvdata->ctrl->base, ®_base, 
                    &pin_offset, &bank); 
    type = bank->type;

    if (cfg_type >= PINCFG_TYPE_NUM || !type->fld_width[cfg_type]) 
        return -EINVAL;

    width = type->fld_width[cfg_type]; 
    cfg_reg = type->reg_offset[cfg_type];

    spin_lock_irqsave(&bank->slock, flags);

    mask = (1 << width) - 1; 
    shift = pin_offset * width; 
    data = readl(reg_base + cfg_reg);

    if (set) { 
        cfg_value = PINCFG_UNPACK_VALUE(*config); 
        data &= ~(mask << shift); 
        data |= (cfg_value << shift); 
        writel(data, reg_base + cfg_reg); 
    } else { 
        data >>= shift; 
        data &= mask; 
        *config = PINCFG_PACK(cfg_type, data); 
    }

    spin_unlock_irqrestore(&bank->slock, flags);

    return 0; 
}

具體分析TODO

 

原創文章,轉發請注明出處。蝸窩科技http://www.wowotech.net/linux_kenrel/pin-controller-driver.html

標簽: driver pin controller


免責聲明!

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



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