PINCTRL子系統(tǒng)用于處理:
- 枚舉和命名控制引腳
- 復(fù)用管腳,pad,金手指。
- 配置管腳、pad、金手指,如軟件控制的偏置和驅(qū)動(dòng)模式指定的管腳,如上拉/下拉,開漏極、負(fù)載電流等
頂層接口
===================
管腳控制器定義
-管腳控制器用于控制芯片管腳。它通??梢砸砸唤M寄存器的形式存在,用于使能獨(dú)立或成組管腳的復(fù)用、偏置、設(shè)置負(fù)載電流、設(shè)置驅(qū)動(dòng)能力等
管腳定義
-管腳(也代指pad、金手指、ball,依據(jù)其封裝不同)輸入/輸出線使用無(wú)符號(hào)整型數(shù)表示,范圍為0到maxpin。這個(gè)數(shù)字空間是每個(gè)管腳控制器獨(dú)有的,這樣,一個(gè)系統(tǒng)中可能有幾個(gè)此類的數(shù)字空間。管腳空間可以是稀疏的,空間中可能存在一些并沒有管腳存在間隙,。
實(shí)例化一個(gè)管腳控制器,會(huì)注冊(cè)一個(gè)描述符到管腳控制架構(gòu),這個(gè)描述符包含一組為它控制管腳的管腳描述符。
下面是一個(gè)PGA封裝芯片的底視圖:
A B C D E F G H
8 o o o o o o o o
7 o o o o o o o o
6 o o o o o o o o
5 o o o o o o o o
4 o o o o o o o o
3 o o o o o o o o
2 o o o o o o o o
1 o o o o o o o o
為了注冊(cè)一個(gè)管腳控制器且命名此封裝上的管腳,我們可以在驅(qū)動(dòng)中使用如下做法:
#include <linux/pinctrl/pinctrl.h>
const struct pinctrl_pin_desc foo_pins[] = {
PINCTRL_PIN(0, "A8"),
PINCTRL_PIN(1, "B8"),
PINCTRL_PIN(2, "C8"),
...
PINCTRL_PIN(61, "F1"),
PINCTRL_PIN(62, "G1"),
PINCTRL_PIN(63, "H1"),
};
static struct pinctrl_desc foo_desc = {
.name = "foo",
.pins = foo_pins,
.npins = ARRAY_SIZE(foo_pins),
//.maxpin = 63,(注:linux3.4.4已刪除)
.owner = THIS_MODULE,
};
int __init foo_probe(void)
{
struct pinctrl_dev *pctl;
pctl = pinctrl_register(&foo_desc, <PARENT>, NULL);
if (IS_ERR(pctl))
pr_err("could not register foo pin driver\n");
}
為了使能pinctrl子系統(tǒng)的PINCTRL及其下屬的PINMUX和PINCONF子組,且使能驅(qū)動(dòng),你需要在相關(guān)的Kconfig條目中選擇它們??梢圆榭碼rch/driver/Kconfig。
管腳通常有比我們所用的更有意義的名字。你可以在芯片的datasheet中找到它們。注意,核心pinctrl.h文件提供了一個(gè)名為PINCTRL_PIN()的宏來(lái)創(chuàng)建這個(gè)結(jié)構(gòu)條目。就像你看到的,代碼枚舉了從0到63的管腳。這個(gè)枚舉是必須的,實(shí)踐中,你需要徹底考慮清楚你的編號(hào)系統(tǒng),以便它能夠與寄存器布局和驅(qū)動(dòng)相匹配,否則的話,代碼會(huì)變得復(fù)雜且難于理解。你還要考慮與GPIO范圍偏移的匹配,這也可能被管腳控制器處理。
對(duì)于一個(gè)467pad的pad環(huán)(這與實(shí)際芯片的管腳相反),我使用一個(gè)如下的枚舉,繞在芯片邊沿的周圍,看起來(lái)也像一個(gè)工業(yè)標(biāo)準(zhǔn)(所有的這些pad也有名字)。
0 ..... 104
466 105
. .
. .
358 224
357 .... 225
管腳組
==========
許多控制器需要處理管腳組,因此管腳控制器子系統(tǒng)需要一個(gè)機(jī)制用來(lái)枚舉管腳組且檢索一個(gè)特定組中實(shí)際枚舉的管腳。
例如,假設(shè)我們有一組用于SPI接口的管腳{0,8,16,24},還有一組用于I2C接口的管腳{24,25}。
這兩組提供給管腳控制子系統(tǒng),以實(shí)現(xiàn)這些類的pinctrl_ops:
#include <linux/pinctrl/pinctrl.h>
struct foo_group {
const char *name;
const unsigned int *pins;
const unsigned num_pins;
};
static const unsigned int spi0_pins[] = { 0, 8, 16, 24 };
static const unsigned int i2c0_pins[] = { 24, 25 };
static const struct foo_group foo_groups[] = {
{
.name = "spi0_grp",
.pins = spi0_pins,
.num_pins = ARRAY_SIZE(spi0_pins),
},
{
.name = "i2c0_grp",
.pins = i2c0_pins,
.num_pins = ARRAY_SIZE(i2c0_pins),
},
};
static int foo_list_groups(struct pinctrl_dev *pctldev, unsigned selector)
{
if (selector >= ARRAY_SIZE(foo_groups))
return -EINVAL;
return 0;
}
static const char *foo_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
return foo_groups[selector].name;
}
static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
unsigned ** const pins,
unsigned * const num_pins)
{
*pins = (unsigned *) foo_groups[selector].pins;
*num_pins = foo_groups[selector].num_pins;
return 0;
}
static struct pinctrl_ops foo_pctrl_ops = {
.list_groups = foo_list_groups,
.get_group_name = foo_get_group_name,
.get_group_pins = foo_get_group_pins,
};
static struct pinctrl_desc foo_desc = {
...
.pctlops = &foo_pctrl_ops,
};
管腳控制子系統(tǒng)將從selector=0開始重復(fù)調(diào)用.list_groups()直到函數(shù)返回非零,以決定合法的選擇,接下來(lái)它將調(diào)用其余的函數(shù)來(lái)接收這些名字和管腳組。驅(qū)動(dòng)維護(hù)管腳組的數(shù)據(jù),這只是一個(gè)簡(jiǎn)單的例子 -- 實(shí)際中,你可能需要更多的條目在你的組結(jié)構(gòu)中,例如,指定與每個(gè)組相關(guān)的寄存器范圍等。
管腳配置
=================
管腳有時(shí)可以被軟件配置成多種方式,多數(shù)與它們作為輸入/輸出時(shí)的電氣特性相關(guān)。例如,可以使一個(gè)輸出管腳處于高阻狀態(tài),或是“三態(tài)”(意味著它被有效地?cái)嚅_連接)。你可以通過(guò)設(shè)置一個(gè)特定寄存器值將一個(gè)輸入管腳與VDD或GND相連---上拉/下拉---以便在沒有信號(hào)驅(qū)動(dòng)管腳或是未連接時(shí)管腳上可以有個(gè)確定的值。
管腳配置可以使用程序控制,一是顯式使用下面將要描述的API,或是在映射表中增加配置條目。查看下面的“單板配置”。
例如,一個(gè)平臺(tái)可以如下做以上拉一個(gè)管腳到VDD:
#include <linux/pinctrl/consumer.h>
ret = pin_config_set("foo-dev", "FOO_GPIO_PIN", PLATFORM_X_PULL_UP);
配置參數(shù)PLATFORM_X_PULL_UP的格式和含義完全由管腳控制器驅(qū)動(dòng)定義。
管腳配置驅(qū)動(dòng)在操作中實(shí)現(xiàn)了一個(gè)改變管腳配置的回調(diào),如下
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf.h>
#include "platform_x_pindefs.h"
static int foo_pin_config_get(struct pinctrl_dev *pctldev,
unsigned offset,
unsigned long *config)
{
struct my_conftype conf;
... Find setting for pin @ offset ...
*config = (unsigned long) conf;
}
static int foo_pin_config_set(struct pinctrl_dev *pctldev,
unsigned offset,
unsigned long config)
{
struct my_conftype *conf = (struct my_conftype *) config;
switch (conf) {
case PLATFORM_X_PULL_UP:
...
}
}
}
static int foo_pin_config_group_get (struct pinctrl_dev *pctldev,
unsigned selector,
unsigned long *config)
{
...
}
static int foo_pin_config_group_set (struct pinctrl_dev *pctldev,
unsigned selector,
unsigned long config)
{
...
}
static struct pinconf_ops foo_pconf_ops = {
.pin_config_get = foo_pin_config_get,
.pin_config_set = foo_pin_config_set,
.pin_config_group_get = foo_pin_config_group_get,
.pin_config_group_set = foo_pin_config_group_set,
};
/* Pin config operations are handled by some pin controller */
static struct pinctrl_desc foo_desc = {
...
.confops = &foo_pconf_ops,
};
由于一些控制器具有用于處理管腳全部組的特殊邏輯,它們可以開發(fā)整組管腳控制器函數(shù)(功能)。pin_config_group_set()函數(shù)允許返回錯(cuò)誤碼 -EAGAIN,表示它不愿意或無(wú)法處理對(duì)應(yīng)組,或是只能(只愿)做一些組級(jí)別的處理且接下來(lái)會(huì)掉落到遍歷所有的管腳操作(此種情況下,每個(gè)獨(dú)立的管腳會(huì)單獨(dú)被pin_config_set()調(diào)用處理)。
與GPIO子系統(tǒng)之間的交互
===================================
GPIO驅(qū)動(dòng)可能需要在一個(gè)已經(jīng)注冊(cè)到管腳控制器的物理管腳上執(zhí)行不同形式的操作。
由于管腳控制器有它自己的管腳空間,我們需要一個(gè)映射以便pinctrl子系統(tǒng)可以指出由哪個(gè)管腳控制器處理一個(gè)指定GPIO管腳的控制。由于一個(gè)單獨(dú)的管腳控制器可能被幾個(gè)GPIO范圍復(fù)用(典型的,SOC系統(tǒng)有一個(gè)管腳集合,但內(nèi)部幾個(gè)GPIO模塊,每個(gè)作為一個(gè)gpio_chip模塊化),GPIO范圍的任意號(hào)碼可以增加到一個(gè)管腳控制器,如下:
struct gpio_chip chip_a;
struct gpio_chip chip_b;
static struct pinctrl_gpio_range gpio_range_a = {
.name = "chip a",
.id = 0,
.base = 32,
.pin_base = 32,
.npins = 16,
.gc = &chip_a;
};
static struct pinctrl_gpio_range gpio_range_b = {
.name = "chip b",
.id = 0,
.base = 48,
.pin_base = 64,
.npins = 8,
.gc = &chip_b;
};
{
struct pinctrl_dev *pctl;
...
pinctrl_add_gpio_range(pctl, &gpio_range_a);
pinctrl_add_gpio_range(pctl, &gpio_range_b);
}
這樣,這個(gè)復(fù)雜系統(tǒng)由一個(gè)管腳控制器處理2個(gè)不同的GPIO chip?!癱hip a”有16個(gè)管腳,“chip b”有8個(gè)管腳。“chip a”和“chip b”具有不同的.pin_base,作為GPIO范圍的一個(gè)起始管腳號(hào)碼。
“chip a”的GPIO范圍始于32,實(shí)際管腳范圍也始于32。雖然如此“chip b”的GPIO起始偏移與管腳范圍的起始不同。“chip b”的GPIO范圍始于48,管腳起始于64。
我們可以使用這個(gè)pin_base將GPIO好轉(zhuǎn)換到實(shí)際的管腳號(hào)。它們映射到全局GPIO空間:
chip a:
- GPIO range : [32 .. 47]
- pin range : [32 .. 47]
chip b:
- GPIO range : [48 .. 55]
- pin range : [64 .. 71]
當(dāng)管腳控制子系統(tǒng)中GPIO特定函數(shù)被調(diào)用后,這些范圍將用于查找合適的管腳控制器,通過(guò)檢查且匹配管腳到所有控制器的管腳范圍。當(dāng)匹配到一個(gè)管腳控制器的處理范圍時(shí),GPIO-specific函數(shù)在此管腳控制器上被調(diào)用。
對(duì)于所有處理管腳偏置、管腳復(fù)用的函數(shù),管腳控制子系統(tǒng)將從傳遞給它的gpio號(hào)碼減去range的.base偏移,然后加上.pin_base偏移以得到一個(gè)管腳號(hào)碼。之后,子系統(tǒng)將它傳遞到管腳控制驅(qū)動(dòng),這樣驅(qū)動(dòng)可以將一個(gè)管腳號(hào)碼變到它可處理的號(hào)碼范圍。它也傳遞這個(gè)范圍ID值,以便管腳控制器知道它應(yīng)該處理的范圍。
PINMUX接口
=================
這些調(diào)用使用pinmux_*命名前綴,沒有別的調(diào)用會(huì)使用這個(gè)前綴。
什么是管腳多路復(fù)用?
==================
PINMUX,也稱作padmux,ballmux,它是由芯片廠商依據(jù)應(yīng)用,使用一個(gè)特定的物理管腳(ball/pad/finger/等等)進(jìn)行多種擴(kuò)展復(fù)用,以支持不同功能的電氣封裝的習(xí)慣。
下面是一個(gè)PGA封裝的底視圖:
A B C D E F G H
+---+
8 | o | o o o o o o o
| |
7 | o | o o o o o o o
| |
6 | o | o o o o o o o
+---+---+
5 | o | o | o o o o o o
+---+---+ +---+
4 o o o o o o | o | o
| |
3 o o o o o o | o | o
| |
2 o o o o o o | o | o
+-------+-------+-------+---+---+
1 | o o | o o | o o | o | o |
+-------+-------+-------+---+---+
這可不是俄羅斯方塊,我們可以聯(lián)想到國(guó)際象棋。并不是所有的PGA/BGA封裝看起來(lái)都像國(guó)際象棋棋盤,大型芯片的布置基于不同的設(shè)計(jì)模式。我們使用這作為一個(gè)簡(jiǎn)單的例子。你可以看到有一些管腳用于VCC和GND,給芯片供電,相當(dāng)多的一部分作為大型端口如一個(gè)外部存儲(chǔ)器接口,剩余的管腳通常用于管腳復(fù)用。
上述的8X8BGA封裝的例子將0~63號(hào)分配給物理管腳。它使用pinctrl_register_pins()和一個(gè)已經(jīng)聲明的合適的數(shù)據(jù)集合來(lái)命名這些管腳{ A1, A2, A3 ... H6, H7, H8 }
在這個(gè)8X8BGA封裝中,管腳{ A8, A7, A6, A5 } 可以被用作一個(gè)SPI端口(具有四個(gè)管腳:CLK、RXD、TXD、FRM)。這個(gè)情況下,B5管腳可以被用作GPIO管腳。同時(shí),管腳{A5,B5}也可以可以被用于I2C端口(僅有兩個(gè)管腳:SCL、SDA)。可以看出,我們無(wú)法同時(shí)使用SPI端口和I2C端口。然而內(nèi)部邏輯也可以將SPI邏輯路由到管腳{ G4, G3, G2, G1 }。
在最低行{ A1, B1, C1, D1, E1, F1, G1, H1 },它可以作為一個(gè)外部MMC總線,可以是2、4、8位寬,相應(yīng)的要使用2、4、8個(gè)管腳,{A1,B1}、{ A1, B1, C1, D1 }或是整行。同樣如果我們使用8位,我們就不能在管腳{ G4, G3, G2, G1 }上使用SPI端口。
芯片使用這個(gè)方法將不同的功能多路復(fù)用到不同管腳的范圍。現(xiàn)在的SOC系統(tǒng)會(huì)包含幾個(gè)I2C、SPI、SDIO/MMC等功能塊,它們可以通過(guò)管腳多路復(fù)用設(shè)置被路由到不同的管腳。
因?yàn)镚PIO常常不足,通常會(huì)將所有當(dāng)前未被使用的管腳用作GPIO。
pinmux復(fù)用約定
==================
ponctrl子系統(tǒng)中的pinmux功能為你的機(jī)器配置中選擇的設(shè)備抽象并提供pinmux設(shè)置。設(shè)備將請(qǐng)求它們的多路復(fù)用設(shè)置,但是申請(qǐng)一個(gè)單獨(dú)的管腳也是可能的,如GPIO。
定義:
- 功能塊可以通過(guò)pinctrl子系統(tǒng)目錄drivers/pinctrl/* 中的驅(qū)動(dòng)連同或斷開。管腳控制驅(qū)動(dòng)知道可能的功能。在上面的例子中,你可以標(biāo)識(shí)3個(gè)pinmux函數(shù),一個(gè)SPI,一個(gè)I2C和一個(gè)MMC。
-功能塊從0開始枚舉,并被分配到一個(gè)無(wú)限數(shù)組中。上述例子中,數(shù)組可以是:{spi0,i2c0,mmc0}3個(gè)有效的功能。
- 功能塊具有在在通用類層級(jí)(generic level)定義的管腳組--這樣驅(qū)動(dòng)的功能總是與確定管腳組集合(可以僅僅是一個(gè),也可以是多個(gè))相聯(lián)系。上面例子中,I2C與管腳{ A5, B5 }聯(lián)系,作為{ 24, 25 }在控制器管腳空間枚舉。
spi功能與管腳組{ A8, A7, A6, A5 }和{ G4, G3, G2, G1 }聯(lián)系,對(duì)應(yīng)被枚舉為{ 0, 8, 16, 24 }和{ 38, 46, 54, 62 }。
每個(gè)管腳控制器的組名必須是獨(dú)一無(wú)二的,同一個(gè)管腳控制器的的兩個(gè)組不能具有相同的名字。
- 功能和管腳組的組合決定了一個(gè)特定功能和一個(gè)特定管腳集合。功能和管腳組的知識(shí)信息及它們的machine-specific細(xì)節(jié)保持在pinmux驅(qū)動(dòng)中,外界只知道枚舉成員且驅(qū)動(dòng)核心可以:
- 使用一個(gè)確定selector(>= 0)申請(qǐng)一個(gè)功能的名字。
- 得到與一個(gè)確定功能相聯(lián)系的一個(gè)組列表
- 為一個(gè)確定的功能在列表中激活一個(gè)確定的管腳組
如上述,管腳組輪流自舉,這樣核心將從驅(qū)動(dòng)中取回一個(gè)確定組的實(shí)際的管腳范圍。
- 一個(gè)確定管腳控制器上的FUNCTIONS和GROUPS通過(guò)一個(gè)板級(jí)文件、設(shè)備樹或是類似的機(jī)器setup配置機(jī)制 被映射到到一個(gè)特定的設(shè)備,和一個(gè)regulator如何被連接到一個(gè)設(shè)備相似,通常是通過(guò)名字。定義一個(gè)管腳控制器,功能和組從而獨(dú)一無(wú)二的標(biāo)識(shí)被特定設(shè)備使用的管腳集。(如果只有一個(gè)可能的管腳組對(duì)于此功能有效,不需要提供組名--核心代碼將選擇第一個(gè)且只有一個(gè)有效的組)
在例子中,我們可以定義這個(gè)特定的機(jī)器應(yīng)該使用設(shè)備spi0(pinmux功能fspi0組gspi0)和i2c0(fi2c0和gi2c0),在主要的管腳控制器上,我們有下面的映射:
{
{"map-spi0", spi0, pinctrl0, fspi0, gspi0},
{"map-i2c0", i2c0, pinctrl0, fi2c0, gi2c0}
}
每個(gè)映射必須被分一個(gè)正式名字、管腳控制器、設(shè)備和功能。組成員不是必須的,如果它被省略,驅(qū)動(dòng)會(huì)提供選擇功能的第一個(gè)組給應(yīng)用,它對(duì)一些簡(jiǎn)單的情況是有用的。
映射幾個(gè)組到同一個(gè)設(shè)備、管腳控制器和功能的組合是可能的。這是為了那些在一個(gè)特定管腳控制器上的一個(gè)特定功能可能在不同配置中使用不同的管腳集合。
- 對(duì)于一個(gè)特定的功能使用的管腳,一個(gè)特定管腳控制器上的一個(gè)特定的管腳組依據(jù)先來(lái)先得原理提供,這樣加入一些別的設(shè)備復(fù)用設(shè)置或是GPIO管腳申請(qǐng)已經(jīng)獲取你的物理管腳,你將拒絕后續(xù)的使用申請(qǐng)。要得到一個(gè)新的設(shè)置,舊的要先被釋放。
有時(shí),文檔和硬件寄存器是面向pad或是finger而不是管腳---這些是封裝內(nèi)部硅芯片的焊接面,且可能或不能匹配外殼下面的pin/ball的實(shí)際號(hào)碼。選擇一些對(duì)你有意義的枚舉方式。僅對(duì)你可以控制的管腳在它起作用時(shí)定義枚舉成員。
假定:
我們假定可能映射到管腳組的功能數(shù)目被硬件限制。例如,我們假設(shè)沒有系統(tǒng)可以像電話交換一樣將任意功能映射到任意管腳。這樣,對(duì)于一個(gè)特定功能有效的管腳組將被限制到一些比較少選擇(例如達(dá)到8個(gè)這樣),而不是幾百個(gè)或是任何大額的選擇。這是我們?cè)谟行inmux硬件上檢查得到的特色,且一個(gè)必須的假設(shè)源于我們期望pinmux驅(qū)動(dòng)為映射到子系統(tǒng)的管腳組提供所有可能的功能。
Pinmux驅(qū)動(dòng)
==============
pinmux核心注意管腳上的沖突并且調(diào)用管腳控制器驅(qū)動(dòng)以執(zhí)行不同的設(shè)置。
pinmux驅(qū)動(dòng)有對(duì)管腳申請(qǐng)的功能是否可以被實(shí)際允許且以防執(zhí)行申請(qǐng)的多路復(fù)用設(shè)置擊穿硬件。
pinmux驅(qū)動(dòng)被要求支持一些回調(diào)函數(shù),一些是可選的。通常enable()和disable()函數(shù)被實(shí)現(xiàn),通常是寫值到一個(gè)一些特定的寄存器以激活一個(gè)特定管腳的特定多路復(fù)用設(shè)置。
上面例子的一個(gè)簡(jiǎn)單的驅(qū)動(dòng)將通過(guò)設(shè)置一些名為MUX寄存器中位0, 1, 2, 3 or 4來(lái)選擇一個(gè)特定的功能和一個(gè)特定的管腳組將如下工作:
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
struct foo_group {
const char *name;
const unsigned int *pins;
const unsigned num_pins;
};
static const unsigned spi0_0_pins[] = { 0, 8, 16, 24 };
static const unsigned spi0_1_pins[] = { 38, 46, 54, 62 };
static const unsigned i2c0_pins[] = { 24, 25 };
static const unsigned mmc0_1_pins[] = { 56, 57 };
static const unsigned mmc0_2_pins[] = { 58, 59 };
static const unsigned mmc0_3_pins[] = { 60, 61, 62, 63 };
static const struct foo_group foo_groups[] = {
{
.name = "spi0_0_grp",
.pins = spi0_0_pins,
.num_pins = ARRAY_SIZE(spi0_0_pins),
},
{
.name = "spi0_1_grp",
.pins = spi0_1_pins,
.num_pins = ARRAY_SIZE(spi0_1_pins),
},
{
.name = "i2c0_grp",
.pins = i2c0_pins,
.num_pins = ARRAY_SIZE(i2c0_pins),
},
{
.name = "mmc0_1_grp",
.pins = mmc0_1_pins,
.num_pins = ARRAY_SIZE(mmc0_1_pins),
},
{
.name = "mmc0_2_grp",
.pins = mmc0_2_pins,
.num_pins = ARRAY_SIZE(mmc0_2_pins),
},
{
.name = "mmc0_3_grp",
.pins = mmc0_3_pins,
.num_pins = ARRAY_SIZE(mmc0_3_pins),
},
};
static int foo_list_groups(struct pinctrl_dev *pctldev, unsigned selector)
{
if (selector >= ARRAY_SIZE(foo_groups))
return -EINVAL;
return 0;
}
static const char *foo_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
return foo_groups[selector].name;
}
static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
unsigned ** const pins,
unsigned * const num_pins)
{
*pins = (unsigned *) foo_groups[selector].pins;
*num_pins = foo_groups[selector].num_pins;
return 0;
}
static struct pinctrl_ops foo_pctrl_ops = {
.list_groups = foo_list_groups,
.get_group_name = foo_get_group_name,
.get_group_pins = foo_get_group_pins,
};
struct foo_pmx_func {
const char *name;
const char * const *groups;
const unsigned num_groups;
};
static const char * const spi0_groups[] = { "spi0_1_grp" };
static const char * const i2c0_groups[] = { "i2c0_grp" };
static const char * const mmc0_groups[] = { "mmc0_1_grp", "mmc0_2_grp",
"mmc0_3_grp" };
static const struct foo_pmx_func foo_functions[] = {
{
.name = "spi0",
.groups = spi0_groups,
.num_groups = ARRAY_SIZE(spi0_groups),
},
{
.name = "i2c0",
.groups = i2c0_groups,
.num_groups = ARRAY_SIZE(i2c0_groups),
},
{
.name = "mmc0",
.groups = mmc0_groups,
.num_groups = ARRAY_SIZE(mmc0_groups),
},
};
int foo_list_funcs(struct pinctrl_dev *pctldev, unsigned selector)
{
if (selector >= ARRAY_SIZE(foo_functions))
return -EINVAL;
return 0;
}
const char *foo_get_fname(struct pinctrl_dev *pctldev, unsigned selector)
{
return foo_functions[selector].name;
}
static int foo_get_groups(struct pinctrl_dev *pctldev, unsigned selector,
const char * const **groups,
unsigned * const num_groups)
{
*groups = foo_functions[selector].groups;
*num_groups = foo_functions[selector].num_groups;
return 0;
}
int foo_enable(struct pinctrl_dev *pctldev, unsigned selector,
unsigned group)
{
u8 regbit = (1 << selector + group);
writeb((readb(MUX)|regbit), MUX)
return 0;
}
void foo_disable(struct pinctrl_dev *pctldev, unsigned selector,
unsigned group)
{
u8 regbit = (1 << selector + group);
writeb((readb(MUX) & ~(regbit)), MUX)
return 0;
}
struct pinmux_ops foo_pmxops = {
.list_functions = foo_list_funcs,
.get_function_name = foo_get_fname,
.get_function_groups = foo_get_groups,
.enable = foo_enable,
.disable = foo_disable,
};
/* Pinmux operations are handled by some pin controller */
static struct pinctrl_desc foo_desc = {
...
.pctlops = &foo_pctrl_ops,
.pmxops = &foo_pmxops,
};
例子中,同時(shí)激活多路復(fù)用0和1設(shè)置位0和1,使用一個(gè)共同的管腳將導(dǎo)致沖突。
pinmux子系統(tǒng)保持了所有管腳的軌跡和誰(shuí)正在使用它們,它也將拒絕一個(gè)不能實(shí)現(xiàn)的申請(qǐng),這樣驅(qū)動(dòng)就不用擔(dān)心管腳沖突。因此位0和1將永遠(yuǎn)不會(huì)同時(shí)設(shè)置。
所有上述的函數(shù)強(qiáng)制由pinmux驅(qū)動(dòng)實(shí)現(xiàn)
管腳控制接口與GPIO子系統(tǒng)交互
===============================================
公開的pinmuxAPI包含兩個(gè)函數(shù):pinctrl_request_gpio()和pinctrl_free_gpio()。這兩個(gè)函數(shù)僅可以從基于gpiolib的驅(qū)動(dòng)中作為它們的gpio_request()和gpio_free()函數(shù)語(yǔ)義的一部分調(diào)用。同樣pinctrl_gpio_direction_[input|output]也只能在對(duì)應(yīng)的gpiolib中的gpio_direction_[input|output]函數(shù)調(diào)用。
注意,平臺(tái)和單獨(dú)的驅(qū)動(dòng)不能申請(qǐng)控制一個(gè)GPIO管腳。而要實(shí)現(xiàn)一個(gè)適當(dāng)?shù)膅piolib驅(qū)動(dòng)且使得驅(qū)動(dòng)為它的管腳申請(qǐng)適當(dāng)?shù)亩嗦窂?fù)用及別的控制。
功能列表可能變的很長(zhǎng),特別是如果你能轉(zhuǎn)換每個(gè)獨(dú)立的管腳到一個(gè)獨(dú)立于任意別的管腳的GPIO管腳,且接下來(lái)嘗試定義每個(gè)管腳作為一個(gè)功能的情況下。
在這種情況下,對(duì)于每個(gè)GPIO設(shè)置和設(shè)備功能(函數(shù))功能陣列會(huì)變?yōu)?4條。
基于此,管腳控制驅(qū)動(dòng)可以實(shí)現(xiàn)兩個(gè)函數(shù)在一個(gè)獨(dú)立管腳上僅使能GPIO:.gpio_request_enable()和.gpio_disable_free()。
這個(gè)函數(shù)將進(jìn)入影響的GPIO范圍(被管腳控制器核心標(biāo)識(shí)),這樣你就知道哪個(gè)GPIO管腳被申請(qǐng)函數(shù)影響。
如果你的驅(qū)動(dòng)需要架構(gòu)關(guān)于GPIO管腳應(yīng)該用作輸入或輸出的指示,你可以實(shí)現(xiàn).gpio_set_direction()函數(shù)。這個(gè)函數(shù)從gpiolib驅(qū)動(dòng)調(diào)用。
交替地使用這些特殊的函數(shù),它完全允許為每個(gè)GPIO管腳使用命名函數(shù),如果沒有注冊(cè)特殊的GPIO處理函數(shù),pinctrl_request_gpio()將嘗試獲取功能(函數(shù))"gpioN"。
單板/機(jī)器 配置
==================================
單板和機(jī)器定義一個(gè)運(yùn)行的系統(tǒng)如何組合在一起,包括GPIO和設(shè)備如何多路復(fù)用,regulator如何限制和時(shí)鐘樹如何看。當(dāng)然pinmux設(shè)置也是這個(gè)的一部分。
為一個(gè)機(jī)器配置的一個(gè)管腳控制器看起來(lái)相當(dāng)像一個(gè)簡(jiǎn)單regulator配置,對(duì)于上面的例子來(lái)說(shuō)如果我們想要在第二個(gè)功能映射上使能i2c和spi:
#include <linux/pinctrl/machine.h>
static const struct pinctrl_map __initdata mapping[] = {
{
.dev_name = "foo-spi.0",
.name = PINCTRL_STATE_DEFAULT,
.type = PIN_MAP_TYPE_MUX_GROUP,
.ctrl_dev_name = "pinctrl-foo",
.data.mux.function = "spi0",
},
{
.dev_name = "foo-i2c.0",
.name = PINCTRL_STATE_DEFAULT,
.type = PIN_MAP_TYPE_MUX_GROUP,
.ctrl_dev_name = "pinctrl-foo",
.data.mux.function = "i2c0",
},
{
.dev_name = "foo-mmc.0",
.name = PINCTRL_STATE_DEFAULT,
.type = PIN_MAP_TYPE_MUX_GROUP,
.ctrl_dev_name = "pinctrl-foo",
.data.mux.function = "mmc0",
},
};
這里的dev_name與獨(dú)一的設(shè)備名字匹配,可以被用于查找相關(guān)的device結(jié)構(gòu)體(就像clockdev或是regulator)。函數(shù)名必須匹配pinmux驅(qū)動(dòng)提供的函數(shù)處理這個(gè)管腳范圍。
就像你看到的,我們可能在系統(tǒng)上具有幾個(gè)管腳控制器,因此我們需要指出它們中的哪個(gè)包含我們希望映射的功能。
你可以簡(jiǎn)單地注冊(cè)這個(gè)pinmux映射到pinmux子系統(tǒng):
ret = pinctrl_register_mappings(mapping, ARRAY_SIZE(mapping));
由于上面的限制是相當(dāng)常用的,有一個(gè)宏可以幫助使它更加緊湊,例如,假設(shè)你想要使用pinctrl-foo和0位置映射:
static struct pinctrl_map __initdata mapping[] = {
PIN_MAP_MUX_GROUP("foo-i2c.o", PINCTRL_STATE_DEFAULT, "pinctrl-foo", NULL, "i2c0"),
};
映射表也可能包含管腳配置條目。每個(gè)管腳/組具有一些配置條目來(lái)影響它是常見的,所以這個(gè)關(guān)于配置的表?xiàng)l目引用一個(gè)配置參數(shù)和值的數(shù)組。使用這個(gè)宏的一個(gè)例子如下:
static unsigned long i2c_grp_configs[] = {
FOO_PIN_DRIVEN,
FOO_PIN_PULLUP,
};
static unsigned long i2c_pin_configs[] = {
FOO_OPEN_COLLECTOR,
FOO_SLEW_RATE_SLOW,
};
static struct pinctrl_map __initdata mapping[] = {
PIN_MAP_MUX_GROUP("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0", "i2c0"),
PIN_MAP_MUX_CONFIGS_GROUP("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0", i2c_grp_configs),
PIN_MAP_MUX_CONFIGS_PIN("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0scl", i2c_pin_configs),
PIN_MAP_MUX_CONFIGS_PIN("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0sda", i2c_pin_configs),
};
最后,一些設(shè)備期望映射表包含某個(gè)特定命名狀態(tài)。當(dāng)在一個(gè)不需要任何管腳控制器配置的硬件上運(yùn)行時(shí),映射表仍必須包含那些命名狀態(tài),為了顯式指出這些狀態(tài)被提供且將要為空。表?xiàng)l目宏P(guān)IN_MAP_DUMMY_STATE為這個(gè)目的服務(wù),定義一個(gè)命名狀態(tài),而不引發(fā)任何管腳控制器被編程。
static struct pinctrl_map __initdata mapping[] = {
PIN_MAP_DUMMY_STATE("foo-i2c.0", PINCTRL_STATE_DEFAULT),
};
復(fù)雜映射
================
由于映射一個(gè)函數(shù)到不同的組是可能的,一個(gè)可選的.group可以如下指定:
...
{
.dev_name = "foo-spi.0",
.name = "spi0-pos-A",
.type = PIN_MAP_TYPE_MUX_GROUP,
.ctrl_dev_name = "pinctrl-foo",
.function = "spi0",
.group = "spi0_0_grp",
},
{
.dev_name = "foo-spi.0",
.name = "spi0-pos-B",
.type = PIN_MAP_TYPE_MUX_GROUP,
.ctrl_dev_name = "pinctrl-foo",
.function = "spi0",
.group = "spi0_1_grp",
},
...
這個(gè)例子映射用于運(yùn)行時(shí)spi0在兩個(gè)位置轉(zhuǎn)換,如后面“運(yùn)行時(shí)管腳復(fù)用”所述。
更進(jìn)一步,一個(gè)命名state影響幾組管腳的多路復(fù)用是可能的,像上面的mmc0例子所說(shuō),你可以輪流擴(kuò)展mmc0總線為2、4、8位。如果我們想要使用所有的3組共2+2+4 = 8 管腳(對(duì)于一個(gè)8位MMC總線情況,我們定義一個(gè)映射如下):
...
{
.dev_name = "foo-mmc.0",
.name = "2bit"
.type = PIN_MAP_TYPE_MUX_GROUP,
.ctrl_dev_name = "pinctrl-foo",
.function = "mmc0",
.group = "mmc0_1_grp",
},
{
.dev_name = "foo-mmc.0",
.name = "4bit"
.type = PIN_MAP_TYPE_MUX_GROUP,
.ctrl_dev_name = "pinctrl-foo",
.function = "mmc0",
.group = "mmc0_1_grp",
},
{
.dev_name = "foo-mmc.0",
.name = "4bit"
.type = PIN_MAP_TYPE_MUX_GROUP,
.ctrl_dev_name = "pinctrl-foo",
.function = "mmc0",
.group = "mmc0_2_grp",
},
{
.dev_name = "foo-mmc.0",
.name = "8bit"
.type = PIN_MAP_TYPE_MUX_GROUP,
.ctrl_dev_name = "pinctrl-foo",
.function = "mmc0",
.group = "mmc0_1_grp",
},
{
.dev_name = "foo-mmc.0",
.name = "8bit"
.type = PIN_MAP_TYPE_MUX_GROUP,
.ctrl_dev_name = "pinctrl-foo",
.function = "mmc0",
.group = "mmc0_2_grp",
},
{
.dev_name = "foo-mmc.0",
.name = "8bit"
.type = PIN_MAP_TYPE_MUX_GROUP,
.ctrl_dev_name = "pinctrl-foo",
.function = "mmc0",
.group = "mmc0_3_grp",
},
...
從設(shè)備抓取映射的結(jié)果支持下面:
p = pinctrl_get(dev);
s = pinctrl_lookup_state(p, "8bit");
ret = pinctrl_select_state(p, s);
或是更簡(jiǎn)單的:
p = pinctrl_get_select(dev, "8bit");
將是你一次在映射中激活所有的3個(gè)底層記錄。由于它們分享相同的名字且我們?cè)试S多個(gè)組匹配一個(gè)單獨(dú)的設(shè)備,管腳控制器設(shè)備,函數(shù),和設(shè)備,它們都被選擇,且它們都同時(shí)的被pinmux核使能或禁止。
來(lái)自驅(qū)動(dòng)pinmux請(qǐng)求
============================
通常不鼓勵(lì)讓一個(gè)獨(dú)立的驅(qū)動(dòng)獲取和使能管腳控制。所以,如果可能的話,在平臺(tái)代碼中或別的你有權(quán)訪問(wèn)所有受到影響的結(jié)構(gòu)device *指針的地方處理管腳控制。在一些情況下,一個(gè)驅(qū)動(dòng)需要運(yùn)行時(shí)在不同的多路復(fù)用映射間轉(zhuǎn)換,上述方法就不能支持了。
一個(gè)驅(qū)動(dòng)可能請(qǐng)求激活一個(gè)確定的控制狀態(tài),常常僅是默認(rèn)的狀態(tài),如下:
#include <linux/pinctrl/consumer.h>
struct foo_state {
struct pinctrl *p;
struct pinctrl_state *s;
...
};
foo_probe()
{
/* Allocate a state holder named "foo" etc */
struct foo_state *foo = ...;
foo->p = pinctrl_get(&device);
if (IS_ERR(foo->p)) {
/* FIXME: clean up "foo" here */
return PTR_ERR(foo->p);
}
foo->s = pinctrl_lookup_state(foo->p, PINCTRL_STATE_DEFAULT);
if (IS_ERR(foo->s)) {
pinctrl_put(foo->p);
/* FIXME: clean up "foo" here */
return PTR_ERR(s);
}
ret = pinctrl_select_state(foo->s);
if (ret < 0) {
pinctrl_put(foo->p);
/* FIXME: clean up "foo" here */
return ret;
}
}
foo_remove()
{
pinctrl_put(state->p);
}
這個(gè)get/lookup/select/put序列正好可以被總線驅(qū)動(dòng)完好處理,如果你不想要每個(gè)驅(qū)動(dòng)都處理它且你知道關(guān)于你的總線的布置的話。
pinctrl API的語(yǔ)義如下
-pinctrl_get()在進(jìn)程上下文中調(diào)用,用于為一個(gè)給定客戶設(shè)備獲取到所有pinctrl信息的句柄。它將從內(nèi)核存儲(chǔ)器分配一個(gè)結(jié)構(gòu)體來(lái)保持pinmux狀態(tài)。所有的映射表解析或類似的慢速操作在這個(gè)API中完成。
- pinctrl_lookup_state()
在進(jìn)程上下文中調(diào)用以便為客戶設(shè)備獲取一個(gè)到給定state的句柄。這個(gè)操作也可能比較慢。
-pinctrl_select_state()依據(jù)映射表給出的state的定義來(lái)編程管腳控制器硬件。理論上,這是一個(gè)快速通道操作,因?yàn)樗鼉H引入一些硬件中的寄存器設(shè)置。雖然如此,注意一些管腳控制器可能具有它們自己的寄存器在一個(gè)慢速/基于中斷的總線上,這樣,客戶設(shè)備不應(yīng)假設(shè)它們可以從一個(gè)非阻塞上下文中調(diào)用此函數(shù)
- pinctrl_put() 釋放所有與此句柄相聯(lián)系的信息。
通常,管腳控制核心處理get/put對(duì),且向外調(diào)用設(shè)備驅(qū)動(dòng)bookkeeping操作,如檢查有效的函數(shù)和相關(guān)的管腳,反之,enable/disable傳遞給管腳控制器驅(qū)動(dòng)(通過(guò)快速設(shè)置一些寄存器管理激活和/或禁止多路復(fù)用)。
管腳為你的設(shè)備分配,當(dāng)你執(zhí)行pinctrl_get()調(diào)用時(shí),此后,你應(yīng)該可以在debugfs中看到所有的管腳列表。
系統(tǒng)管腳控制軸(hogging)
==========================
管腳控制映射條目在管腳控制器注冊(cè)時(shí)可以被核心使用。這意味著core將在管腳控制設(shè)備注冊(cè)之后立即嘗試在其上調(diào)用pinctrl_get(),lookup_state()和select_state()。
這發(fā)生在映射表?xiàng)l目客戶設(shè)備名與管腳控制設(shè)備名相同,且state名是PINCTRL_STATE_DEFAULT時(shí)。
{
.dev_name = "pinctrl-foo",
.name = PINCTRL_STATE_DEFAULT,
.type = PIN_MAP_TYPE_MUX_GROUP,
.ctrl_dev_name = "pinctrl-foo",
.function = "power_func",
},
因?yàn)樯暾?qǐng)核心在首要管腳控制器上使用一些總是可以應(yīng)用的多路復(fù)用設(shè)置是常見的,有一個(gè)便利的宏用于此:
PIN_MAP_MUX_GROUP_HOG_DEFAULT("pinctrl-foo", NULL /* group */, "power_func")
這個(gè)給出的結(jié)果與上面的解釋完全一致。
實(shí)時(shí)管腳復(fù)用
=================
在運(yùn)行時(shí)設(shè)置多路復(fù)用一個(gè)特定功能in/out是可能的,例如移動(dòng)一個(gè)spi端口從一個(gè)管腳集另一個(gè)管腳集。上面的spi0就是一個(gè)例子,我們?yōu)橥瑯拥墓δ鼙┞读藘山M不同的管腳,但是在映射中使用不同的名字。這樣對(duì)于一個(gè)SPI設(shè)備,我們具有兩個(gè)狀態(tài)名為“pos-A”和“pos-B”。
下面這段代碼首先多路復(fù)用此功能到組A定義的管腳,使能、禁止、然后釋放,接下來(lái)多路復(fù)用它到B組定義的管腳:
#include <linux/pinctrl/consumer.h>
foo_switch()
{
struct pinctrl *p;
struct pinctrl_state *s1, *s2;
/* Setup */
p = pinctrl_get(&device);
if (IS_ERR(p))
...
s1 = pinctrl_lookup_state(foo->p, "pos-A");
if (IS_ERR(s1))
...
s2 = pinctrl_lookup_state(foo->p, "pos-B");
if (IS_ERR(s2))
...
/* Enable on position A */
ret = pinctrl_select_state(s1);
if (ret < 0)
...
...
/* Enable on position B */
ret = pinctrl_select_state(s2);
if (ret < 0)
...
...
pinctrl_put(p);
}
上述代碼已經(jīng)在進(jìn)程上下文中完成。