日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

Linux内核中的GPIO系统之(3):pin controller driver代码分析

發(fā)布時(shí)間:2025/4/16 67 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux内核中的GPIO系统之(3):pin controller driver代码分析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、前言

對(duì)于一個(gè)嵌入式軟件工程師,我們的軟件模塊經(jīng)常和硬件打交道,pin control subsystem也不例外,被它驅(qū)動(dòng)的硬件叫做pin controller(一般ARM soc的datasheet會(huì)把pin controller的內(nèi)容放入GPIO controller的章節(jié)中),主要功能包括:

(1)pin multiplexing。基于ARM core的嵌入式處理器一般會(huì)提供豐富的功能,例如camera interface、LCD interface、USB、I2C、SPI等等。雖然處理器有幾百個(gè)pin,但是這些pin還是不夠分配,因此有些pin需要復(fù)用。例如:127號(hào)GPIO可以做一個(gè)普通的GPIO控制LED,也可以配置成I2C的clock信號(hào),也可以配置成SPI的data out信號(hào)。當(dāng)然,這些功能不可能同時(shí)存在,因?yàn)橛布盘?hào)只有一個(gè)。

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

本文主要描述pin control subsystem中的low level driver,也就是驅(qū)動(dòng)pin controller的driver。具體的硬件選用的是S3C2416的硬件平臺(tái)。既然是代碼分析,本文不是非常多的描述框架性的內(nèi)容,關(guān)于整個(gè)pin control subsystem軟件結(jié)構(gòu)的描述請(qǐng)參考Linux內(nèi)核中的GPIO系統(tǒng)之(2)。

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

?

二、pin controller相關(guān)的DTS描述

類似其他的硬件,pin controller這個(gè)HW block需要是device tree中的一個(gè)節(jié)點(diǎn)。此外,各個(gè)其他的HW block在驅(qū)動(dòng)之前也需要先配置其引腳復(fù)用功能,因此,這些device(我們稱pin controller是host,那么這些使用pin controller進(jìn)行引腳配置的device叫做client device)也需要在它自己的device tree node中描述pin control的相關(guān)內(nèi)容

1、S3C2416 pin controller DTS結(jié)構(gòu)

下面的偽代碼描述了S3C2416 pin controller 的DTS結(jié)構(gòu):

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

??????? 定義屬于S3C2416 pin controller的pin configurations

}

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

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

2、pin configuration定義

我們舉兩個(gè)簡(jiǎn)單的例子(當(dāng)然一個(gè)是pin bank,另外一個(gè)是定義功能復(fù)用配置)來(lái)理解pin configuration第一個(gè)例子是描述pin bank:

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

……

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

……

}

其實(shí)S3C2416 pin controller定義了gpa到gpm共計(jì)11個(gè)sub node,每個(gè)sub node是描述S3C2416 GPIO controller的各個(gè)bank信息。S3C2416有138個(gè)I/O 端口(或者叫做pin、finger、pad)這些端口分成了11個(gè)bank(這里沒(méi)有用group這個(gè)術(shù)語(yǔ),為了和pin group這個(gè)概念區(qū)分開(kāi),pin group的概念下面會(huì)具體描述):

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進(jìn)行分組,方便控制。例如:這些bank中,只有GPF和GPG這兩個(gè)bank上的引腳有中斷功能,其他的都沒(méi)有。interrupt-controller這個(gè)屬性相信大家已經(jīng)熟悉,就是說(shuō)明該node是一個(gè)interrupt controller。gpio-controller類似,說(shuō)明該device node是一個(gè)GPIO controller。#gpio-cells屬性是一個(gè)GPIO controller的必須定義的屬性,它描述了需要多少個(gè)cell來(lái)具體描述一個(gè)GPIO(這是和具體的GPIO controller相關(guān)的)。#interrupt-cells的概念類似,不再贅述。phandle(linux,phandle這個(gè)屬性和phandle是一樣的,只不過(guò)linux,phandle是old-style,多定義一個(gè)屬性是為了兼容)定義了一個(gè)句柄,當(dāng)其他的device node想要引用這個(gè)node的時(shí)候就可以使用該句柄。具體的例子參考下節(jié)client device的DTS的描述。

另外一個(gè)例子是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這個(gè)屬性定義了一個(gè)pin configuration所涉及到的引腳定義。對(duì)于uart0-data這個(gè)node,該配置涉及了gph bank中的第一個(gè)和第二個(gè)GPIO pin。一旦選擇了一個(gè)功能,那么samsung,pins定義的所有的引腳都需要做相應(yīng)的功能設(shè)定,那么具體設(shè)定什么值呢?這就是samsung,pin-function定義的內(nèi)容了。而具體設(shè)定哪個(gè)值則需要去查閱datasheet了。對(duì)于uart0-data,向gph bank中的第一個(gè)和第二個(gè)GPIO pin對(duì)應(yīng)的配置寄存器中寫入2就可以把這兩個(gè)pin定義為uart功能。

3.client device的DTS

一個(gè)典型的device tree中的外設(shè)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定義了一個(gè)state列表。那么什么是state呢?具體說(shuō)應(yīng)該是pin state,對(duì)于一個(gè)client device,它使用了一組pin,這一組pin應(yīng)該同時(shí)處于某種狀態(tài),畢竟這些pin是屬于一個(gè)具體的設(shè)備功能。state的定義和電源管理關(guān)系比較緊密,例如當(dāng)設(shè)備active的時(shí)候,我們需要pin controller將相關(guān)的一組pin設(shè)定為具體的設(shè)備功能,而當(dāng)設(shè)備進(jìn)入sleep狀態(tài)的時(shí)候,需要pin controller將相關(guān)的一組pin設(shè)定為普通GPIO,并精確的控制GPIO狀態(tài)以便節(jié)省系統(tǒng)的功耗。state有兩種,標(biāo)識(shí),一種就是pinctrl-names定義的字符串列表,另外一種就是ID。ID從0開(kāi)始,依次加一。根據(jù)例子中的定義,state ID等于0(名字是active)的state對(duì)應(yīng)pinctrl-0屬性,state ID等于1(名字是idle)的state對(duì)應(yīng)pinctrl-1屬性。具體設(shè)備state的定義和各個(gè)設(shè)備相關(guān),具體參考在自己的device bind。

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

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

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

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

?

三、 pin controller driver初始化

1、注冊(cè)pin control device

舊的內(nèi)核一般是在machine相關(guān)的代碼中建立靜態(tài)的platform device的數(shù)據(jù)結(jié)構(gòu),然后在machine初始化的時(shí)候,將靜態(tài)定義的platform device注冊(cè)到系統(tǒng)。不過(guò)在引入device tree之后,事情發(fā)生了變化。

根據(jù)device tree代碼分析,我們知道,在系統(tǒng)初始化的時(shí)候,dts描述的device node會(huì)形成一個(gè)樹(shù)狀結(jié)構(gòu),在machine初始化的過(guò)程中,會(huì)scan device node的樹(shù)狀結(jié)構(gòu),將真正的硬件device node變成一個(gè)個(gè)的設(shè)備模型中的device結(jié)構(gòu)(比如struct platform_device)并加入到系統(tǒng)中。我們看看具體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的相關(guān)描述

}

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

2、注冊(cè)pin controller driver

當(dāng)然,pinctrl@56000000這個(gè)device node也會(huì)變成一個(gè)platform device加入系統(tǒng)。有了device,那么對(duì)應(yīng)的platform driver是如何注冊(cè)到系統(tǒng)中的呢?代碼如下:

static int __init samsung_pinctrl_drv_register(void)?
{?
?? ……

??? return platform_driver_register(&samsung_pinctrl_driver);?
}

系統(tǒng)初始化的時(shí)候,該函數(shù)會(huì)執(zhí)行,向系統(tǒng)注冊(cè)了samsung_pinctrl_driver:

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

?

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

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

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的匹配函數(shù)就是簡(jiǎn)單的比較device和driver的名字,多么簡(jiǎn)單,多么清晰,真是有點(diǎn)懷念過(guò)去單純而美好的生活。當(dāng)然,事情沒(méi)有那么糟糕,我們這里只要關(guān)注OF style的匹配過(guò)程即可,思路很簡(jiǎn)單,解鈴還需系鈴人,把匹配過(guò)程推給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成員)。對(duì)于我們這個(gè)場(chǎng)景,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的節(jié)點(diǎn)compatible屬性的定義,你會(huì)禁不住感慨:啊,終于遇到對(duì)的人。這里還要特別說(shuō)明的是data成員被設(shè)定為s3c2416_pin_ctrl ,它描述了2416的HW pin controller,我們稱之samsung pin controller的描述符,初始化的過(guò)程中需要這個(gè)數(shù)據(jù)結(jié)構(gòu),后面還會(huì)詳細(xì)介紹它。一旦pin controller這個(gè)device遇到了適當(dāng)?shù)膁river,就會(huì)調(diào)用probe函數(shù)進(jìn)行具體的driver初始化的動(dòng)作了,代碼如下:

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); -設(shè)定platform device的私有數(shù)據(jù)為samsung_pinctrl_drv_data

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

??? return 0;?
}

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

(2)分配了struct samsung_pinctrl_drv_data數(shù)據(jù)結(jié)構(gòu)的內(nèi)存,當(dāng)然下一步就是初始化這個(gè)數(shù)據(jù)結(jié)構(gòu)了。我們先看看2416的pin controller driver是如何定義該數(shù)據(jù)結(jié)構(gòu)的:

struct samsung_pinctrl_drv_data {?
??? struct list_head??????? node;---------多個(gè)pin controller的描述符可以形成鏈表?
??? void __iomem??????????? *virt_base;---------訪問(wèn)硬件寄存器的基地址?
??? struct device??????????? *dev;-----------和platform device建立聯(lián)系?
??? int??????????????? irq; --------irq number,對(duì)于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的數(shù)目?
??? const struct samsung_pmx_func??? *pmx_functions; --描述samsung pin controller中function信息
??? unsigned int??????????? nr_functions; --------描述samsung pin controller中function的數(shù)目?
};

struct pinctrl_desc和struct pinctrl_dev 都是pin control subsystem中core driver的概念。各個(gè)具體硬件的pin controller可能會(huì)各不相同,但是可以抽取其共同的部分來(lái)形成一個(gè)HW independent的數(shù)據(jù)結(jié)構(gòu),這個(gè)數(shù)據(jù)就是pin controller描述符,在core driver中用struct pinctrl_desc表示,具體該數(shù)據(jù)結(jié)構(gòu)的定義如下:

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

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

struct pinctrl_pin_desc {?
??? unsigned number;-------ID,在本pin controller中唯一標(biāo)識(shí)該引腳?
??? const char *name;-------user friedly name?
??? void *drv_data;?
};

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

struct pinctrl_dev在pin control subsystem的core driver中抽象一個(gè)pin control device。其實(shí)我們可以通過(guò)多個(gè)層面來(lái)定義一個(gè)device。在這個(gè)場(chǎng)景下,pin control subsystem的core driver關(guān)注的是一類pin controller的硬件設(shè)備,具體其底層是什么硬件連接方式,使用什么硬件協(xié)議它不關(guān)心,它關(guān)心的是pin controller這類設(shè)備所有的通用特性和功能。當(dāng)然2416的pin controller是通過(guò)platform bus連接的,因此,在low level的層面,需要一個(gè)platform device來(lái)標(biāo)識(shí)2416的pin controller(需要注意的是:pin controller class device和platform device都是基于一個(gè)驅(qū)動(dòng)模型中的device派生而來(lái)的,這里struct device是基類,struct pinctrl_dev和struct platform_device都是派生類,當(dāng)然c本身不支持class,但面向?qū)ο蟮母拍钍峭瑯拥?#xff09;。為了充分理解class這個(gè)概念,我們?cè)倥e一個(gè)例子。對(duì)于audio的硬件抽象層,它應(yīng)該管理所有的audio設(shè)備,因此這個(gè)軟件模塊應(yīng)該有一個(gè)audio class的鏈表,連接了所有的系統(tǒng)中的audio設(shè)備。但這些具體的audio設(shè)備可能是PCI接口的audio設(shè)備,也可能是usb接口的audio設(shè)備,從具體的總線層面來(lái)看,也會(huì)有PCI或者USB設(shè)備來(lái)抽象對(duì)應(yīng)的聲卡設(shè)備。

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

我們前面已經(jīng)說(shuō)過(guò)了,struct samsung_pinctrl_drv_data數(shù)據(jù)結(jié)構(gòu)就是2416的pin controller driver要驅(qū)動(dòng)2416的HW pin controller的私有數(shù)據(jù)結(jié)構(gòu)。這個(gè)數(shù)據(jù)結(jié)構(gòu)中最重要的就是samsung pin controller描述符了。關(guān)于pin controller有兩個(gè)描述符,一個(gè)是struct pinctrl_desc,是具體硬件無(wú)關(guān)的pin controller的描述符。struct samsung_pin_ctrl描述的具體samsung pin controller硬件相關(guān)的信息,比如說(shuō):pin bank的信息,不是所有的pin controller都是分bank的,因此pin bank的信息只能封裝在low level的samsung pin controller driver中。這個(gè)數(shù)據(jù)結(jié)構(gòu)定義如下:

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的數(shù)目

其他成員和本場(chǎng)景無(wú)關(guān),和GPIO type的中斷控制器驅(qū)動(dòng)代碼有關(guān)?
};

關(guān)于上面的base可以多說(shuō)兩句。實(shí)際上,系統(tǒng)支持多個(gè)pin controller設(shè)備,這時(shí)候,系統(tǒng)要管理多個(gè)pin controller控制下的多個(gè)pin。每個(gè)pin有自己的pin ID,是唯一的,假設(shè)系統(tǒng)中有兩個(gè)pin controller,一個(gè)是A,控制32個(gè),另外一個(gè)是B,控制64個(gè)pin,我們可以統(tǒng)一編號(hào),對(duì)A,pin ID從0~31,對(duì)于B,pin ID是從32~95。對(duì)于B,其pin base就是32。

samsung_pinctrl_probe->samsung_pinctrl_get_soc_data函數(shù)中會(huì)根據(jù)device tree的信息和靜態(tài)定義的table來(lái)初始化該描述符,具體的代碼如下:

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初始的時(shí)候等于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這個(gè)函數(shù)名字基本反應(yīng)了其功能,2416是samsung的一個(gè)具體的SOC型號(hào),調(diào)用該函數(shù)可以返回一個(gè)表示2416 SOC的samsung pin controller的描述符。

A:這段代碼主要是獲取具體的2416的HW pin controller的信息,該數(shù)據(jù)結(jié)構(gòu)在上文中出現(xiàn)過(guò)(具體參考pin controller的device tree match table:samsung_pinctrl_dt_match),就是s3c2416_pin_ctrl這個(gè)變量。這個(gè)變量定義了2416的pin controller的信息(S3C2416的pin controller的pin bank信息是定義在pin controller driver的靜態(tài)數(shù)據(jù),其實(shí)最好在dts中定義)如下:

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

這個(gè)變量中包含了2416的pin bank的信息,包括:有多少個(gè)pin bank,每個(gè)bank中有多少個(gè)pin,pin bank的名字是什么,寄存器的offset是多少。這些信息用來(lái)初始化pin controller描述符數(shù)據(jù)結(jié)構(gòu)。

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

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

D:系統(tǒng)中有可能有多個(gè)pin controller,多個(gè)pin controller上的pin ID 應(yīng)該是系統(tǒng)唯一的,ctrl->base表示本pin controller中的pin ID的起始值。

(3)本來(lái)pin control subsystem和GPIO subsystem應(yīng)該是無(wú)關(guān)的兩個(gè)子系統(tǒng),應(yīng)該各自進(jìn)行自己的初始化過(guò)程。但實(shí)際中,由于硬件的復(fù)雜性,這兩個(gè)子系統(tǒng)耦合性非常高。這里samsung_gpiolib_register函數(shù)就是把各個(gè)bank代表的gpio chip注冊(cè)到GPIO subsystem中。更具體的信息請(qǐng)參考GPIO subsystem軟件框架文檔。

(4)samsung_pinctrl_register函數(shù)的主要功能是將本pin controller注冊(cè)到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 函數(shù),具體參考第四章的內(nèi)容?
??? 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:初始化硬件無(wú)關(guān)的pin controller描述符(struct samsung_pinctrl_drv_data中的pctl成員)。該數(shù)據(jù)結(jié)構(gòu)中還包含了所有pin的描述符的信息,這些pin descriptor所需要的內(nèi)存在步驟B中分配

B:初始化過(guò)程中涉及不少內(nèi)存分配,這些內(nèi)存主要用于描述每一個(gè)pin(術(shù)語(yǔ)叫做pin descriptor)以及pin name。

C:初始化每一個(gè)pin 描述符的名字和ID。對(duì)于samsung的pin描述符,其名字使用pin-bank name + pin ID的形式。 ID的分配是從該pin controller的pin base開(kāi)始分配ID的,逐個(gè)加一。

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

E:調(diào)用pinctrl_register注冊(cè)到pin control subsystem 。這是pin control subsystem的核心函數(shù),可以參考GPIO系統(tǒng)之2的描述。

F:在這里又不得不進(jìn)行pin control subsystem和GPIO系統(tǒng)的耦合了。每個(gè)bank都是一個(gè)GPIO controller,但是pin bank使用的ID是pin control space中的ID,GPIO 子系統(tǒng)中使用的是GPIO space的ID,對(duì)于pin control subsystem而言,它需要建立這兩個(gè)ID的映射關(guān)系。pinctrl_add_gpio_range就是起這個(gè)作用的。更具體的內(nèi)容請(qǐng)參考pin control subsystem軟件結(jié)構(gòu)文檔。 需要注意的是直接在pin controller driver中調(diào)用pinctrl_add_gpio_range是不推薦的,建議使用dts的方式在GPIO controller設(shè)備節(jié)點(diǎn)中描述。

(5)這里的代碼是向kernel中的中斷子系統(tǒng)注冊(cè)interrupt controller。對(duì)于2416,有兩個(gè)bank有中斷功能,gpf和gpg,本質(zhì)上gpf和gpg就是兩個(gè)interrupt controller,掛接在2416真正的那個(gè)interrupt contrller之下,形成樹(shù)狀結(jié)構(gòu)。具體的代碼就不分析了,請(qǐng)參考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)) -忽略掉那些沒(méi)有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名字需要的內(nèi)存
??????????????????? 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; ----忽略掉那些沒(méi)有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的私有數(shù)據(jù)結(jié)構(gòu)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有若干個(gè)child node,每個(gè)child node都描述了一個(gè)pin configuration。of_get_child_count函數(shù)可以獲取pin configuration的數(shù)目。

(2)根據(jù)pin configuration的數(shù)目分配內(nèi)存。在這里共計(jì)分配了兩片內(nèi)存,一片保存了所有pin group的信息(struct samsung_pin_group ),一片保存了pin mux function的信息(struct samsung_pmx_func)。實(shí)際上,分配pin configuration的數(shù)目的內(nèi)存有些浪費(fèi),因?yàn)椴皇敲恳粋€(gè)pin control的child node都是和pin group相關(guān)(例如pin bank node就是和pin group無(wú)關(guān))。對(duì)于function,就更浪費(fèi)了,因?yàn)橛锌赡芏鄠€(gè)pin group對(duì)應(yīng)一個(gè)function。

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

(4)初始化samsung pin group的描述符。具體的數(shù)據(jù)結(jié)構(gòu)解釋如下:

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中的數(shù)目?
??? u8??????????? func;------------對(duì)應(yīng)samsung,pin-function屬性的值,用來(lái)配置pin list中各個(gè)pin的功能設(shè)定寄存器?
};

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

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

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

??? const char??????? **groups;-----指向pin groups的指針數(shù)組?
??? u8??????????? num_groups;------屬于該function的pin group的個(gè)數(shù)?
};

在具體的代碼實(shí)現(xiàn)中num_groups總是等于1。

?

四、S3C2416 pin controller driver的操作函數(shù)

1、操作函數(shù)概述

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

本章節(jié)主要描述這些call back函數(shù)的邏輯,這些callback的調(diào)用時(shí)機(jī)不會(huì)在這里描述,那些內(nèi)容請(qǐng)參考pin control subsystem的描述。

2、struct pinctrl_ops中各個(gè)callback函數(shù)的具體的解釋如下:

(1)samsung_get_group_count

該函數(shù)的代碼如下:

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;?
}

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

(2)samsung_get_group_name

該函數(shù)的代碼如下:

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;?
}

該函數(shù)主要用來(lái)獲取指定group selector的pin group信息。

(3)samsung_get_group_pins

該函數(shù)的代碼如下:

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;?
}

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

(4)samsung_dt_node_to_map

該函數(shù)的代碼如下:

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

該函數(shù)的代碼如下:

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、復(fù)用引腳相關(guān)的操作函數(shù)struct pinmux_ops的具體解釋如下:

(1)samsung_get_functions_count

該函數(shù)的代碼如下:

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;?
}

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


(2)samsung_pinmux_get_fname

該函數(shù)的代碼如下:

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;?
}

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

該函數(shù)的代碼如下:

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;?
}

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

這個(gè)兩個(gè)callback函數(shù)都是通過(guò)samsung_pinmux_setup實(shí)現(xiàn),該函數(shù)的代碼如下:

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);?
??? }?
}

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

該函數(shù)的代碼如下:

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;?
}

該函數(shù)用來(lái)設(shè)定GPIO的方向。?
?

4、配置引腳的特性的struct pinconf_ops數(shù)據(jù)結(jié)構(gòu)的各個(gè)成員定義如下:

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

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

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

?

原創(chuàng)文章,轉(zhuǎn)發(fā)請(qǐng)注明出處。蝸窩科技。http://www.wowotech.net/linux_kenrel/pin-controller-driver.html

總結(jié)

以上是生活随笔為你收集整理的Linux内核中的GPIO系统之(3):pin controller driver代码分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

亚洲一区二区三区精品在线观看 | 99久久婷婷国产 | 在线免费观看成人 | 麻豆94tv免费版| 去看片 | 天天操人人干 | 国产精品久久久久永久免费观看 | 免费在线观看91 | 中文字幕在线观看视频一区二区三区 | 亚洲免费在线观看视频 | 麻豆91在线 | 人人爽人人爽人人片 | 日本久久久久久久久久久 | 男女视频91 | 97碰碰精品嫩模在线播放 | 久久69精品 | 人人精久 | 91免费版成人 | 久久精品视频3 | 亚洲成人免费 | 久久亚洲综合国产精品99麻豆的功能介绍 | 中文字幕av在线免费 | 91精品国自产在线观看欧美 | 欧美性大战| 日日弄天天弄美女bbbb | 国产成人精品久久二区二区 | 91成人网在线观看 | 草在线视频| 国产一区二区在线免费观看 | 91精品国产欧美一区二区 | 色婷婷 亚洲 | 亚洲国产精品久久久久久 | 久久久久久国产精品 | 91av资源网| 97超碰总站 | 亚洲欧美视频一区二区三区 | 狠狠色噜噜狠狠狠合久 | 日本成址在线观看 | 西西444www| 久色网| 天天天天天天天操 | 天天伊人狠狠 | 亚洲春色成人 | 日韩高清无线码2023 | 草久在线视频 | 蜜臀久久99精品久久久无需会员 | 成人毛片在线视频 | 婷婷色在线观看 | 91福利影院在线观看 | 国产亚洲精品无 | 91色蜜桃 | 激情 婷婷 | 国产精品麻豆果冻传媒在线播放 | 999成人网 | 青春草视频 | 少妇性xxx| 日本精品久久久久久 | 精品欧美一区二区在线观看 | 玖玖爱免费视频 | 成人午夜电影在线观看 | 国产剧在线观看片 | 久久精品电影院 | 亚洲欧美日本国产 | 精品一区二区在线观看 | 中文乱码视频在线观看 | 亚洲国内精品视频 | 久久免费视频8 | 久久天天躁狠狠躁亚洲综合公司 | 中文字幕黄色网址 | 日日爽视频 | 日日干日日 | 亚洲精品视频在线免费播放 | av成人免费在线看 | 日韩欧美国产精品 | 国产九色在线播放九色 | 午夜精品一区二区三区在线播放 | 国产精品18久久久久久久久久久久 | 夜夜视频欧洲 | 欧美亚洲精品在线观看 | 天天色天天色天天色 | 一区二区视频在线观看免费 | 久久这里只有精品9 | 国产精品免费久久 | 久久久九色精品国产一区二区三区 | 久久精品视频中文字幕 | 国模视频一区二区三区 | 久久成人免费视频 | 欧美天天干 | 日本久久久精品视频 | 日韩精品免费一区二区在线观看 | av一区二区三区在线 | 亚洲精品456在线播放乱码 | 91精品国产自产91精品 | 久久国产精品二国产精品中国洋人 | 在线精品亚洲 | 超碰成人网 | 91在线播放国产 | 国产麻豆视频免费观看 | 香蕉视频在线播放 | 国产视频在线免费观看 | 91丝袜美腿 | 久久久久国产精品午夜一区 | 最新中文字幕 | 黄色www免费| 久久久久久蜜桃一区二区 | 在线观看片 | 国产精品久久久影视 | 欧美一级性 | 91日韩在线 | 欧美精品久久99 | 涩涩网站在线播放 | 99久久这里只有精品 | 久久综合久久综合九色 | 日韩免费在线网站 | 激情视频在线观看网址 | 欧亚日韩精品一区二区在线 | 97av.com| 精品日韩在线 | 麻豆免费视频观看 | 美女一级毛片视频 | 精品国产欧美一区二区三区不卡 | 日韩成人看片 | 99超碰在线播放 | 精品国产乱码久久久久久浪潮 | 成人av在线电影 | 97超碰在线播放 | 在线观看免费版高清版 | 欧美孕交vivoestv另类 | 国产精品人人做人人爽人人添 | 丝袜足交在线 | 2019av在线视频 | 成人在线观看你懂的 | 麻豆播放 | 欧美 日韩 国产 中文字幕 | 亚洲,播放 | 国产91aaa | 伊人久久国产精品 | 日韩在线视频免费看 | 国产在线观看xxx | 欧美91精品 | 91麻豆看国产在线紧急地址 | 国产精品初高中精品久久 | 成人黄色在线 | 99在线视频精品 | 91女神的呻吟细腰翘臀美女 | 99久久婷婷国产一区二区三区 | 国产手机在线精品 | 久久激情片 | 久草视频在线观 | a久久久久久 | 欧美一区二区三区在线播放 | 国产色视频网站2 | 少妇18xxxx性xxxx片 | 伊人色播 | 久久视频在线免费观看 | 免费看一级片 | 亚洲作爱视频 | 国产日韩欧美自拍 | 九九免费观看全部免费视频 | avove黑丝 | 色中射 | 免费黄色一区 | 欧美日韩1区2区 | 久久精品香蕉视频 | 99久久精品一区二区成人 | 热久久99这里有精品 | 国产精品视频免费看 | 日韩精选在线 | 亚洲手机天堂 | 夜夜狠狠 | 欧美淫aaa免费观看 日韩激情免费视频 | 国产精品自产拍在线观看网站 | 日韩一区精品 | 免费看黄色大全 | 午夜精品久久久 | 国产黄色片一级三级 | 国产成人1区 | 一区二区三区日韩视频在线观看 | 午夜av日韩 | 日韩欧美亚州 | 97视频在线 | 中文永久免费观看 | 久久久精品国产一区二区 | 五月天六月丁香 | 久色婷婷| 国产大片免费久久 | 九色精品免费永久在线 | www五月 | 日韩精品一区在线播放 | 久久久久久片 | 中文高清av | 久久影院精品 | 青青草国产精品视频 | 国产精品1区 | 成年人视频在线免费播放 | 久久五月激情 | 我爱av激情网 | 久久毛片网 | 天天操操操操操 | 久久兔费看a级 | 国产v亚洲v | 国产成人精品一区在线 | 精品在线视频观看 | 97超碰成人在线 | 亚洲涩综合 | 亚洲精品美女久久久 | 精品久久久久久久久久岛国gif | 国产淫片免费看 | 色婷婷免费视频 | 日日操操| 91女人18片女毛片60分钟 | 国产精品一区二区你懂的 | 国产精品免费不 | 天天躁日日躁狠狠躁av麻豆 | 日韩网站免费观看 | 欧美日韩国产在线观看 | 97理论片| 1区2区视频 | 成人av免费在线看 | 国产午夜精品av一区二区 | 蜜桃视频成人在线观看 | 日韩色区| 精品xxx| 五月视频 | www.黄色片网站 | 久热av| 免费观看的黄色片 | 亚洲九九影院 | 国产成人精品在线观看 | 手机av永久免费 | 亚洲国产精品久久久久 | 天天操天天爱天天爽 | 亚洲精品一区二区三区新线路 | 亚洲精品在线资源 | 久久,天天综合 | 91爱爱网址 | 亚洲精品在线资源 | 高清精品视频 | 最近高清中文字幕在线国语5 | 久久精品亚洲一区二区三区观看模式 | 欧美亚洲精品在线观看 | 久久久久免费精品 | 激情丁香在线 | 在线观看韩国av | 国产视频日韩视频欧美视频 | 99视频这里有精品 | 色综合天天色 | 婷婷综合电影 | 久草视频免费看 | 国产99在线免费 | 麻豆成人精品视频 | 久久综合九色综合久99 | 91久久国产综合精品女同国语 | 国产精品欧美久久久久三级 | 成年人app网址 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 久久av影视 | 99精品视频在线观看 | 国产精品s色 | 久草久热 | 99热最新地址 | 天天综合网久久综合网 | 久久久久在线 | 人人澡澡人人 | 精品国产一区二区三区久久久蜜月 | 欧美日韩久久不卡 | 色噜噜噜 | 欧美性色19p | 国产精品久久久久久久久免费 | 欧美人交a欧美精品 | av福利网址导航 | 青青河边草手机免费 | 久久久久久久久国产 | 国产91对白在线 | 亚洲午夜久久久久久久久久久 | 中文字幕在线观看视频一区二区三区 | 五月激情五月激情 | 亚洲精品ww| 超碰人人在 | 久久99这里只有精品 | 日韩欧美观看 | 草久久av | 九九热免费精品视频 | 456成人精品影院 | 日韩最新理论电影 | 欧美另类xxx| 黄色av网站在线观看免费 | 精品国产理论 | 国产一区二区三区视频在线 | 在线观看的av | 99久久精品国产网站 | 丁香网五月天 | 亚洲一区美女视频在线观看免费 | 91视频网址入口 | 中文字幕91在线 | 欧美日韩在线视频一区二区 | 国产二区免费视频 | 日本护士三级少妇三级999 | 激情片av | 伊人久久国产精品 | 国产护士hd高朝护士1 | 亚洲电影自拍 | 国产精品一区二区在线观看免费 | 国产精品99久久久久久久久久久久 | 天天操夜夜想 | 亚洲精品在线观看视频 | 中文字幕有码在线 | 在线观看免费高清视频大全追剧 | 免费国产黄线在线观看视频 | 亚洲精品在线观看中文字幕 | 粉嫩一二三区 | 午夜三级影院 | 天天爽天天做 | 国产伦理一区二区三区 | 91成人在线看 | 国产aa精品 | 中文av网| 欧美激情xxxx性bbbb | 丁香电影小说免费视频观看 | 欧美激情视频一区二区三区免费 | 国产精品videossex国产高清 | 免费看污片 | 18+视频网站链接 | 91黄视频在线 | 91精品视频免费看 | 91九色国产在线 | 中文字幕在线观看网 | 国产麻豆果冻传媒在线观看 | 日日干影院 | 成人午夜网址 | 精品国产免费观看 | 99热官网 | 麻豆91精品| 国产日产精品一区二区三区四区的观看方式 | 最近日本韩国中文字幕 | 久久精品综合网 | 色综合天天天天做夜夜夜夜做 | 精品国产_亚洲人成在线 | 欧美五月婷婷 | 国产精品嫩草69影院 | 美女视频黄在线观看 | 韩日精品在线 | 少妇视频一区 | 中文字幕av免费观看 | 色com网| 黄色毛片在线 | 三三级黄色片之日韩 | 国产成人精品av久久 | 欧美亚洲一区二区在线 | 国产在线观看中文字幕 | 国产人成在线观看 | 免费看片成年人 | 国内综合精品午夜久久资源 | 91精品国自产在线观看欧美 | 日韩在线精品 | 超碰在线官网 | 国产福利中文字幕 | 久草在线99 | 亚洲国产剧情 | 精品国产一区二区三区久久久蜜月 | 国产无遮挡又黄又爽在线观看 | 久久国产视屏 | 日韩在线观看精品 | 国产精品观看视频 | 91综合色 | 在线日韩精品视频 | 蜜臀av性久久久久蜜臀aⅴ四虎 | 亚洲九九九在线观看 | 天天天综合 | 黄色网址a | 天天天综合| 久久久99精品免费观看乱色 | 五月开心婷婷网 | 999久久久 | 99九九热只有国产精品 | 日韩欧美大片免费观看 | 色视频成人在线观看免 | 国产精品一区二区在线观看 | 伊色综合久久之综合久久 | 在线观看亚洲精品 | 成人国产精品久久久春色 | 免费在线观看av网址 | 在线电影 一区 | 在线 日韩 av | 国产精品色婷婷视频 | 91激情在线视频 | 在线视频中文字幕一区 | 色婷婷丁香 | 久久桃花网 | a级国产乱理伦片在线播放 久久久久国产精品一区 | 精品久久五月天 | 久久精品这里都是精品 | 99九九免费视频 | 久久蜜臀一区二区三区av | 99久久久久国产精品免费 | 国产精品美女久久久网av | 亚洲经典在线 | 五月婷丁香网 | 在线综合 亚洲 欧美在线视频 | 亚洲jizzjizz日本少妇 | 99久久精品免费看国产麻豆 | 中文字幕日韩国产 | 亚洲在线视频播放 | 麻豆视频国产精品 | 成人免费观看网址 | 午夜精品视频福利 | 日韩在线一二三区 | 久久激情视频 久久 | 日韩av二区| 黄色网址a | 欧美国产视频在线 | 欧美日韩国产mv | 久久久久免费看 | 911久久| 国产香蕉视频在线播放 | 99久久精品国产亚洲 | 在线成人免费电影 | 国产精品白浆视频 | 欧美日韩国产精品一区二区 | 日韩最新av | 免费成人黄色 | 天天草av | 深夜男人影院 | 九七视频在线观看 | 久久久久国产精品免费 | 欧洲精品码一区二区三区免费看 | 久久久久婷 | 国内久久久久久 | 中文字幕高清在线 | 国产视频精品久久 | 超碰av在线免费观看 | 国产精品国产三级国产不产一地 | 日韩免费一二三区 | 国产又粗又硬又长又爽的视频 | 99视频导航 | 日日操天天操夜夜操 | 成人动漫视频在线 | 精品在线亚洲视频 | 99爱国产精品 | 久久久久免费 | 黄色软件在线观看 | 天天操夜夜曰 | 超级碰99| 亚洲一区二区精品在线 | 欧美孕交vivoestv另类 | 色综合在| 欧洲一区精品 | www色综合| 永久精品视频 | 日日夜夜免费精品 | 视频在线99 | 国产精品国内免费一区二区三区 | 久久九九久久精品 | 国产中文在线播放 | 国产精品久久久久久一区二区三区 | 99久久日韩精品免费热麻豆美女 | 99在线免费观看 | 精品一二三区视频 | 九九视频在线播放 | 久久视屏网| 成人av资源网 | 日韩女同一区二区三区在线观看 | 欧美午夜性 | av电影亚洲 | 四虎在线视频免费观看 | 成人夜晚看av | 国产无套视频 | 国内精品久久久久久久影视麻豆 | 久久久999精品视频 国产美女免费观看 | 91亚洲夫妻| 精品一区三区 | 中文字幕高清视频 | 亚洲精品小区久久久久久 | 日韩欧美电影网 | 天天曰天天爽 | 国内精品久久久久久久97牛牛 | 成人国产精品一区 | www好男人 | 日韩高清av在线 | 天天干天天干天天 | 99久视频| 日本久久久久久久久久 | 国产精品theporn | www.色午夜,com| 免费观看午夜视频 | 久久久久电影网站 | 亚洲国产999 | 91福利国产在线观看 | 亚洲在线综合 | 日本久久精 | 狠狠狠狠狠狠 | 国产人成在线观看 | 国产精品字幕 | 最近在线中文字幕 | 中文字幕在线观看一区二区三区 | 欧美日韩国产在线 | 久久久久久久福利 | 亚洲黄色免费在线看 | 久久国产麻豆 | 国产99黄 | 亚洲精品成人 | 青青河边草免费观看完整版高清 | 91香蕉视频在线 | 九九九热精品免费视频观看网站 | 欧美日韩精品在线视频 | 色婷婷www| 久久久亚洲影院 | 91视频成人免费 | 99中文字幕在线观看 | 亚洲精品午夜aaa久久久 | 最近2019中文免费高清视频观看www99 | 国内精品亚洲 | 欧美老人xxxx18 | 69人人 | 国产午夜小视频 | 99视频在线精品国自产拍免费观看 | 日韩一区二区免费播放 | 久久国产视频网站 | 久爱精品在线 | 国产又黄又爽无遮挡 | 日韩a级免费视频 | 99成人精品 | 插插插色综合 | 麻豆va一区二区三区久久浪 | 久久久精品在线观看 | 男女免费av | 国内外成人在线视频 | 色综合久久精品 | 天天看天天干 | 中文字幕免费观看视频 | 免费观看www小视频的软件 | 天天射天天射 | 国产精品网红福利 | 中文字幕国语官网在线视频 | 九九视频精品免费 | 国产在线观看午夜 | 亚洲成aⅴ人在线观看 | 欧美一二三专区 | 成人黄色小说在线观看 | 国产精品日韩久久久久 | 久久经典国产视频 | 久久婷婷久久 | 午夜av免费在线观看 | 亚洲精品视频在线观看免费视频 | 欧美a√大片 | 日韩电影中文,亚洲精品乱码 | 精品国产一区二区三区四区在线观看 | 97精品视频在线播放 | 久久久久久高潮国产精品视 | 国产爽视频 | 中文字幕 国产专区 | 日本h视频在线观看 | 成人国产亚洲 | 中文字幕亚洲综合久久五月天色无吗'' | 久青草国产在线 | 在线观看免费中文字幕 | 日韩精品中文字幕在线播放 | 91最新网址 | 国产精品免费在线观看视频 | 久久免费的视频 | 亚洲精品91天天久久人人 | 操一草| 天天干一干 | 中文字幕亚洲在线观看 | 国产免费av一区二区三区 | 午夜视频免费在线观看 | 精品日本视频 | 成人毛片一区二区三区 | 日韩中文字幕第一页 | 丁香六月婷婷开心婷婷网 | 97人人爽| 九色自拍视频 | 在线免费黄色av | 亚洲精品综合在线 | 天天操天天操天天操天天操天天操 | 国产精品av久久久久久无 | ,久久福利影视 | 国产高清在线 | 国产手机精品视频 | 91在线中文| 999视频在线播放 | 久草国产在线观看 | 亚洲一区 影院 | 国产精品久久久999 国产91九色视频 | 久草在线精品观看 | 国产一区福利 | 在线免费观看视频一区 | 狠狠激情中文字幕 | 日韩精品视频在线观看网址 | 午夜精品在线看 | 国产日韩在线播放 | 免费看片日韩 | 婷婷激情五月 | 色综合久久88色综合天天人守婷 | 久久久久久99精品 | 国产精品美女免费视频 | 国产成人在线观看 | 亚洲精品午夜久久久久久久久久久 | 精品久久久久久国产 | 精品国产伦一区二区三区观看方式 | 日韩一二三区不卡 | 黄色网免费 | 免费69视频 | 欧美在线观看视频一区二区 | 久久久久9999亚洲精品 | 中文字幕在线观看免费高清电影 | 成人黄色中文字幕 | 国产人成免费视频 | 久久一区二区三区四区 | 国产视频每日更新 | 日韩成人高清在线 | 国产美女在线免费观看 | 久久黄色网址 | 人人爽人人片 | 在线观看亚洲免费视频 | 九九色网 | 亚洲欧美国产日韩在线观看 | 偷拍久久久 | 欧美小视频在线观看 | 日韩在线观看视频免费 | 91亚洲国产成人 | 深爱激情婷婷网 | 夜夜夜草| 日韩视频在线观看免费 | 久热色超碰 | 国产伦精品一区二区三区照片91 | 久久香蕉影视 | 在线免费视 | 九九九九热精品免费视频点播观看 | 国产成人在线观看免费 | 综合色综合 | 国产不卡精品 | 久久99亚洲精品 | 国产高清区| 精品久久久久久亚洲 | 亚洲伊人成综合网 | 成人综合日日夜夜 | 六月色婷婷 | 国产精品电影一区二区 | 九九久久婷婷 | 五月天久久综合网 | 国产午夜一区二区 | 91系列在线 | 人人爱夜夜操 | 91视频 - 114av | 久久久久久久久久久久久久av | 国产精品3 | 亚洲mv大片欧洲mv大片免费 | 日韩在线色视频 | 免费观看xxxx9999片 | 91av久久| 中文字幕 在线看 | 九九免费视频 | 国产精品精品久久久久久 | 国产黑丝袜在线 | 久久中文字幕在线视频 | 美女黄频网站 | 欧美日韩免费一区二区 | 一区二区三区在线不卡 | 中文在线字幕免费观看 | 手机在线看a | 久久在线视频精品 | 国产成人一区二区啪在线观看 | 97理论电影| 国产精品成人一区二区三区吃奶 | 天天综合网在线观看 | 久久久www成人免费精品 | 久久免费高清 | 免费男女羞羞的视频网站中文字幕 | 精品国产一区二区三区四区vr | 日韩欧美在线视频一区二区三区 | 亚洲精品成人 | 婷婷成人亚洲综合国产xv88 | 中文字幕亚洲精品日韩 | 欧美有色 | 五月天狠狠操 | 一区二区三区在线免费 | 亚洲国产成人av网 | 国产成人精品亚洲日本在线观看 | 91香蕉视频色版 | 欧美一级久久久久 | 精品久久久99 | 成人免费观看大片 | 国产精品永久久久久久久www | 在线观看视频中文字幕 | 一级黄色毛片 | 91传媒免费观看 | 欧美男女爱爱视频 | 日韩免费观看高清 | 欧美精品在线观看免费 | 久草a视频 | av中文天堂 | 久久综合加勒比 | 国产99久久99热这里精品5 | 粉嫩av一区二区三区四区 | 日日操操 | 国产精品久久久久久久久毛片 | 亚洲男男gaygayxxxgv| 免费成人结看片 | 久久九九九九 | 青春草免费在线视频 | 亚洲 欧美变态 另类 综合 | 亚洲日本va在线观看 | 国产黄影院色大全免费 | 久久九九精品 | 人人搞人人搞 | 日韩黄色在线观看 | 五月天色综合 | 91亚州| 亚洲理论在线观看 | 91香蕉视频好色先生 | 久久男女视频 | 亚洲激情电影在线 | 国产精品麻豆99久久久久久 | 日韩av一卡二卡三卡 | 98超碰人人 | 夜色资源站国产www在线视频 | 开心激情五月婷婷 | 成人性生交视频 | 久久香蕉电影网 | 激情黄色av | 国产精品美女毛片真酒店 | 97超碰资源站 | 成人av免费在线播放 | 亚洲国产中文字幕在线视频综合 | 婷婷色网站 | av直接看| av高清免费在线 | 国产成人福利在线观看 | 日韩免费中文 | 久久成人午夜 | 国产精品自产拍在线观看网站 | 视频一区在线免费观看 | 一本一本久久a久久精品牛牛影视 | 久久综合亚洲鲁鲁五月久久 | 午夜在线观看影院 | 久草在线资源观看 | 日日日视频 | 亚洲国产资源 | 97国产在线观看 | 免费在线观看污 | 成人av免费看 | 在线成人看片 | 久久99久久久久 | 日韩av视屏| 天堂视频一区 | 久久观看最新视频 | 超碰97国产精品人人cao | 视频在线观看亚洲 | 视频在线观看91 | 国产又粗又猛又色又黄视频 | 欧美一级在线观看视频 | 开心综合网 | 人人天天夜夜 | 国产高清在线看 | 在线精品在线 | 免费看片网址 | 成人在线视频免费看 | 五月婷婷丁香色 | 99超碰在线播放 | 久久国产欧美日韩精品 | 欧美成人精品欧美一级乱 | 国产精品区在线观看 | 最近中文国产在线视频 | 黄色激情网址 | 在线激情影院一区 | 日日干天天| av福利超碰网站 | 天天草综合网 | 久久草av| 97在线视频免费看 | 在线观看一级视频 | 在线免费看黄色 | 欧洲成人av | 国产精品福利午夜在线观看 | 亚洲高清视频在线 | 亚洲成人第一区 | 97超碰影视 | 天天爱天天操 | 丁香激情网 | 日韩在线三级 | 99国产视频| 欧美在线日韩在线 | av电影中文字幕 | 视频在线一区二区三区 | 国产亚洲高清视频 | 麻豆国产精品永久免费视频 | 黄色大全免费观看 | 国产精品国产三级国产aⅴ入口 | 天天综合狠狠精品 | 色av男人的天堂免费在线 | 国产看片网站 | 国产精品久久久久久影院 | 欧美色图东方 | 91人人插| 精品久久久久久久久久久久久 | 91看片淫黄大片一级在线观看 | 日韩动漫免费观看高清完整版在线观看 | a级国产乱理伦片在线观看 亚洲3级 | 99国产精品免费网站 | av国产网站 | 香蕉在线影院 | 午夜性福利 | 国产一区二区精品 | 成片人卡1卡2卡3手机免费看 | 69中文字幕| 毛片美女网站 | 国产精品一区二区久久精品爱微奶 | 一区二区三区福利 | 这里只有精彩视频 | 激情小说网站亚洲综合网 | 国产精品嫩草影院99网站 | 国产精品一区电影 | 一级黄色片毛片 | 激情在线网站 | 国产精品21区 | 一区二区中文字幕在线观看 | 日韩在线高清 | 国产综合片 | 在线观看国产日韩欧美 | 国产精品岛国久久久久久久久红粉 | 免费在线观看一级片 | 天天色成人网 | 亚洲精品黄色片 | 国产高清在线a视频大全 | 狠狠亚洲 | 日韩精品久久一区二区三区 | 99久久久久久国产精品 | 天天综合在线观看 | 米奇狠狠狠888 | 插插插色综合 | 中文字幕日韩免费视频 | 久久综合影院 | 黄色小说网站在线 | 天天操天天干天天摸 | 欧美激情在线看 | 韩国精品在线 | 日韩精品免费一区二区 | 18性欧美xxxⅹ性满足 | 最新不卡av | 中文字幕最新精品 | 99视频+国产日韩欧美 | 久久久国产电影 | a黄色影院 | 国产一区视频在线 | 国产精品综合久久 | 亚洲国产日韩一区 | 91三级视频| 国产精品 中文在线 | 一级片视频在线 | 欧美一区二区伦理片 | 国产精品网在线观看 | 久久毛片高清国产 | 久久在线精品 | 天天操天天干天天摸 | 久久五月激情 | 国产午夜精品福利视频 | 最新av网站在线观看 | 日日操网站| 在线观看理论 | 99精品国产aⅴ | 亚洲理论在线 | 蜜臀一区二区三区精品免费视频 | 在线黄色国产 | 日本高清中文字幕有码在线 | 91精彩视频 | 国产精品99免费看 | 中文字幕视频在线播放 | 亚洲春色奇米影视 | 97高清视频| 在线观看日本高清mv视频 | 免费观看一区二区三区视频 | 五月天色丁香 | 九九有精品 | 精品久久网 | 黄污视频网站 | 韩日色视频| 国产精品黑丝在线观看 | 国产一级免费在线观看 | 精品无人国产偷自产在线 | 美女免费视频黄 | 日韩av在线免费看 | 91在线资源 | 欧美在线视频日韩 | 午夜精品久久久久久久久久久久 | www.com操| 亚洲成人av一区 | 在线看片a| 超碰国产在线播放 | 午夜日b视频 | 亚洲一级片av | 国内精品久久久 | 一本一本久久a久久精品牛牛影视 | 国产免费xvideos视频入口 | 99成人精品 | 天天射天天拍 | 91精品国产欧美一区二区 | 亚洲欧美视频一区二区三区 | 日韩精品视频免费在线观看 | 日韩精品视频一二三 | 亚洲欧美综合精品久久成人 | 国产国产人免费人成免费视频 | 久久国内精品99久久6app | 久久免费视频这里只有精品 | 国产免费区 | 欧美最爽乱淫视频播放 | 在线观看中文字幕一区二区 | 日韩高清三区 | 毛片网站在线观看 | 黄色的视频 | 天天操天天射天天 | 午夜国产一区二区 | 在线观看911视频 | 日韩一区二区三区在线看 | 四虎影视欧美 | 天天五月天色 | 丁香综合激情 | 久久r精品 | 久久这里精品视频 | 深爱婷婷 | 99久视频 | 欧美亚洲免费在线一区 | 91视频免费视频 | 在线高清| 久久99精品热在线观看 | 天天操伊人 | 国产精品一区二区久久国产 | 黄在线免费看 | 人人揉人人揉人人揉人人揉97 | 中文字幕av全部资源www中文字幕在线观看 | 日韩电影在线一区二区 | 在线91播放 | 麻豆免费在线播放 | 国产成人三级一区二区在线观看一 | 日本韩国欧美在线观看 | 99视频精品免费视频 | 成人性生交大片免费看中文网站 | 色资源二区在线视频 | 亚洲精品欧美视频 | 中文字幕不卡在线88 | 日韩a级黄色片 | 国产免费不卡av | 亚洲国产精品一区二区尤物区 | 婷婷色综合网 | 久久久久免费电影 | 国产精品午夜免费福利视频 | 日韩在线观看视频网站 | 伊人狠狠| 久久国产高清视频 | 精品久久久国产 | 欧美黑人xxxx猛性大交 | 贫乳av女优大全 | 永久免费精品视频 | 亚洲一级黄色 | 婷婷午夜| 在线黄色国产 | 日韩激情久久 | 欧美性免费| 偷拍视频一区 | 国产精品18久久久久久久久久久久 | 在线高清一区 | 在线黄色免费 | 欧美激情视频一二三区 | 99国产视频 | 欧美视频二区 | 99精品热视频只有精品10 | 国产成人精品三级 | 99婷婷狠狠成为人免费视频 | 国产精品理论在线观看 | 色99在线 | 欧美va在线观看 | 国产精品久久久久久高潮 | 日韩激情在线 | 久久这里只有精品视频首页 | 999在线视频| 成人性生交大片免费看中文网站 | 91网站观看 | adn—256中文在线观看 | 国产一级片在线播放 | 久99久在线 | 国产亚洲精品成人 | 韩国在线一区 | 国产精品久久久久永久免费 | 999国内精品永久免费视频 | 91看片一区二区三区 | 国产免费亚洲 | 久久一区二区三区国产精品 | 91丨九色丨91啦蝌蚪老版 | 天天草夜夜| 久久免费黄色 | 国产精品福利小视频 | 日韩av中文字幕在线免费观看 | www.黄色片网站 | 波多野结衣日韩 | 最新中文字幕在线观看视频 | 久久久麻豆视频 | 婷婷久久综合网 | 午夜av网站 |