哈嘍,我是老吳,我來繼續(xù)分享我的學(xué)習(xí)心得啦。
gpio 和 pinctrl 子系統(tǒng)在內(nèi)核里的使用率非常高,和嵌入式產(chǎn)品的關(guān)聯(lián)非常大。從這兩個子系統(tǒng)開始學(xué)習(xí)驅(qū)動開發(fā)是個不錯的入門選擇。
本文目錄:
一、gpio與pinctrl
二、內(nèi)核里如何引用gpio
三、gpio子系統(tǒng)框架
四、應(yīng)用層如何訪問gpio
一、gpio 與 pinctrl
本文主要關(guān)注 gpio 子系統(tǒng),但是老吳認(rèn)為必要先說明一下 pinctrl 子系統(tǒng)和 gpio 子系統(tǒng)的之間關(guān)系。
pinctrl 的作用:
- 引腳復(fù)用,例如某個引腳即可用作為普通的gpio,也可以作為UART的TX;
- 引腳配置,一般包括上下拉、驅(qū)動能力等;
點擊查看大圖
gpio 的作用:
- 作為輸入功能時,支持讀引腳值;
- 作為輸出功能時,支持輸出高低電平;
- 部分 gpio 還負(fù)責(zé)接收中斷;
gpio 的使用依賴于 pinctrl:
點擊查看大圖
本文的關(guān)注點是 gpio driver --> gpio subsystem core -> gpio consumer 這一路徑,讀者如果想更深入地了解 pinctrl 子系統(tǒng),可以參考內(nèi)核文檔:Documentation/driver-api/pinctl.rst。
gpio 子系統(tǒng)內(nèi)核文檔:
Documentation/driver-api/gpio:
文檔 | 簡介 |
---|---|
index.rst | 文檔目錄和源碼清單 |
intro.rst | gpio 簡介 |
driver.rst | 描述如何編寫 gpio controller driver |
consumer.rst | 描述 gpio consumer 如何使用 gpio |
board.rst | 描述設(shè)備如何申請 gpio |
drivers-on-gpio.rst | 列舉一些使用了gpio子系統(tǒng)的常見驅(qū)動,例如 leds-gpio.c、gpio_keys.c 等 |
legacy.rst | 描述 legacy gpio 接口 |
注:本文基于 Linux-4.19。
二、內(nèi)核里如何引用 gpio
2 個步驟:
1) 設(shè)備樹里添加 gpio mappings
示例:
foo_device {
compatible = "packt,gpio-descriptor-sample";
led-gpios = <&gpio2 15 GPIO_ACTIVE_HIGH>, // red
<&gpio2 16 GPIO_ACTIVE_HIGH>, // green
btn1-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>;
btn2-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>;
};
要點:
-
屬性
-gpios
里的由使用者自行決定的, 例如上述例子中的
為 led,在 gpio consumer driver 里可以通過 "led" 這個字符串,配合偏移值來獲取這一組 gpio 里的任一 gpio。
-
至于如何標(biāo)志是硬件上的哪一個引腳,是由平臺相關(guān)的 gpio controller driver 的設(shè)備樹節(jié)點里的
#gpio-cells
的值來決定,上述例子中需要 2個參數(shù)才能確定硬件引腳,所以#gpio-cells = 2
。
2) 在 gpio consumer driver 中引用
目前 gpio subsystem 提供了 2 套接口:
-
legacy API:integer-based GPIO interface,形式為 gpio_xxx(),例如
void gpio_set_value(unsigned gpio, int value)
,不推薦使用該 API; -
推薦 API: descriptor-based GPIO interface,形式為 gpiod_xxx(),例如
void gpiod_set_value(struct gpio_desc *desc, int value)
,新添加的驅(qū)動代碼一律采用這套 API。
示例:
staticstructgpio_desc*red,*green,*btn1,*btn2;
staticintirq;
staticirqreturn_tbtn1_pushed_irq_handler(intirq,void*dev_id)
{
intstate;
/*readthebuttonvalueandchangetheledstate*/
state=gpiod_get_value(btn2);
gpiod_set_value(red,state);
gpiod_set_value(green,state);
pr_info("btn1interrupt:Interrupt!btn2stateis%d)
",state);
returnIRQ_HANDLED;
}
staticconststructof_device_idgpiod_dt_ids[]={
{.compatible="gpio-descriptor-sample",},
};
staticintmy_pdrv_probe(structplatform_device*pdev)
{
intretval;
structdevice*dev=&pdev->dev;
//獲得gpiodescriptor的同時也將其設(shè)置為output,并且輸出低電平
red=gpiod_get_index(dev,"led",0,GPIOD_OUT_LOW);
green=gpiod_get_index(dev,"led",1,GPIOD_OUT_LOW);
btn1=gpiod_get(dev,"btn1",GPIOD_IN);
btn2=gpiod_get(dev,"btn2",GPIOD_IN);
//獲得中斷號
irq=gpiod_to_irq(btn1);
//申請中斷
retval=request_threaded_irq(irq,NULL,
btn1_pushed_irq_handler,
IRQF_TRIGGER_LOW|IRQF_ONESHOT,
"gpio-descriptor-sample",NULL);
pr_info("Hello!deviceprobed!
");
return0;
}
staticintmy_pdrv_remove(structplatform_device*pdev)
{
free_irq(irq,NULL);
//釋放gpio
gpiod_put(red);
gpiod_put(green);
gpiod_put(btn1);
gpiod_put(btn2);
pr_info("goodbyereader!
");
return0;
}
staticstructplatform_drivermypdrv={
.probe=my_pdrv_probe,
.remove=my_pdrv_remove,
.driver={
.name="gpio_descriptor_sample",
.of_match_table=of_match_ptr(gpiod_dt_ids),
.owner=THIS_MODULE,
},
};
module_platform_driver(mypdrv);
gpiod_xxx() API
在 gpio 子系統(tǒng)中,用 struct gpio_desc 來描述一個 gpio 引腳,gpiod_xxx() 都是圍繞著 strcut gpio_desc 進行操作的。
完整的接口定義位于 linux/gpio/consumer.h,大約共有 70個 API。
常用 API:
-
獲得/釋放 一個或者一組 gpio:
- [devm]_gpiod_get*()
- [devm]_gpiod_put*()
-
設(shè)置/查詢 輸入或者輸出
- gpiod_direction_input()
- gpiod_direction_output()
- gpiod_get_direction()
-
讀寫一個 gpio
- gpiod_get_value()
- gpiod_set_value()
- gpiod_get_value_cansleep()
- gpiod_set_value_cansleep()
-
讀寫一組 gpio
- gpiod_get_array_value()
- gpiod_set_array_value()
-
獲得 gpio 對應(yīng)的中斷號
- gpiod_to_irq()
相關(guān)要點:
-
以 _cansleep 為后綴的函數(shù)是可能會睡眠的 API,不可以在 hard (non-threaded) IRQ handlers 中使用;
-
gpiod_get_value() 返回的是硬件上的電平值;
-
gpiod_set_value() 設(shè)置的值是邏輯值而非電平值,1 表示使能,0 表示不使能,由設(shè)備樹里的 gpio mappings 里的 GPIO_ACTIVE_XXX 來決定哪個電平值是有效的,總結(jié)如下:
Function | line property | physical line |
---|---|---|
gpiod_set_raw_value(desc, 0); | don't care | low |
gpiod_set_raw_value(desc, 1); | don't care | high |
gpiod_set_value(desc, 0); | default (active high) | low |
gpiod_set_value(desc, 1); | default (active high) | high |
gpiod_set_value(desc, 0); | active low | high |
gpiod_set_value(desc, 1); | active low | low |
gpiod_set_value(desc, 0); | default (active high) | low |
gpiod_set_value(desc, 1); | default (active high) | high |
gpiod_set_value(desc, 0); | open drain | low |
gpiod_set_value(desc, 1); | open drain | high impedance |
gpiod_set_value(desc, 0); | open source | high impedance |
gpiod_set_value(desc, 1); | open source | high |
三、gpio 子系統(tǒng)框架
1. 整體框架
點擊查看大圖
正常情況下,驅(qū)動工程師不需要了解 gpio chip driver 和 gpiolib:
-
驅(qū)動工程師負(fù)責(zé)編寫 gpio consumer drvier;
-
開源社區(qū)里的大牛負(fù)責(zé) gpiolib 的核心實現(xiàn);
但是當(dāng)功能和預(yù)期的不一樣時,為了調(diào)試定位出問題,這時就有必要弄清楚 gpio chip driver 和 gpiolib 的工作流程。
2. gpiolib
作用:
-
向下為 gpio chip driver 提供注冊 struct gpio_chip 的接口:gpiochip_xxx();
-
向上為 gpio consumer 提供引用 gpio 的接口:gpiod_xxx();
-
實現(xiàn)字符設(shè)備的功能;
-
注冊 sysfs;
源碼:
$cdlinux-4_19/drivers/gpio
$lsgpiolib*-1X
gpiolib-acpi.c//ACPIhelpersforGPIOAPI
gpiolib.c//GPIOsubsystemcore
gpiolib-devprop.c//DevicepropertyhelpersforGPIOchips.
gpiolib-legacy.c
gpiolib-of.c//OFhelpersfortheGPIOAPI
gpiolib-sysfs.c//sysfshelpers
gpiolib.h
int gpiochip_add(struct gpio_chip *chip)
這是 bsp 工程師比較關(guān)心的 api。
在 gpio 子系統(tǒng)中,SoC 上的每一個 gpio bank 都會被認(rèn)為是一個 gpio controller,每一個 gpio controller 都由一個 struct gpio_chip 來描述,bsp 工程師的核心工作就是填充該結(jié)構(gòu)體。
struct gpio_chip 比較龐大,但是我們只需要關(guān)注跟硬件聯(lián)系比較緊密的成員就好:
- .set(),輸出電平
- .get(),獲得電平
- .get_direction(),獲得方向
- .direction_input(),設(shè)置為輸入
- .direction_output(),設(shè)置為輸出
- .to_irq(),獲得中斷號
3. gpio chip driver
最簡單的 demo:
#defineGPIO_NUM16
staticstructgpio_chipchip;
staticintfake_get_value(structgpio_chip*gc,unsignedoffset)
{
return0;
}
staticvoidfake_set_value(structgpio_chip*gc,unsignedoffset,intval)
{
}
staticintfake_direction_output(structgpio_chip*gc,unsignedoffset,intval)
{
return0;
}
staticintfake_direction_input(structgpio_chip*gc,unsignedoffset)
{
return0;
}
staticconststructof_device_idfake_gpiochip_ids[]={
{.compatible="fake-gpio-chip",},
};
staticintmy_pdrv_probe(structplatform_device*pdev)
{
chip.label=pdev->name;
chip.base=-1;
chip.dev=&pdev->dev;
chip.owner=THIS_MODULE;
chip.ngpio=GPIO_NUM;
chip.can_sleep=1;
chip.get=fake_get_value;
chip.set=fake_set_value;
chip.direction_output=fake_direction_output;
chip.direction_input=fake_direction_input;
returngpiochip_add(&chip);
}
staticintmy_pdrv_remove(structplatform_device*pdev)
{
gpiochip_remove(&chip);
return0;
}
staticstructplatform_drivermypdrv={
.probe=my_pdrv_probe,
.remove=my_pdrv_remove,
.driver={
.name="fake-gpio-chip",
.of_match_table=of_match_ptr(fake_gpiochip_ids),
.owner=THIS_MODULE,
},
};
module_platform_driver(mypdrv);
RK3399 實例分析
1) 設(shè)備樹
gpio0: gpio0@ff720000 {
compatible = "rockchip,gpio-bank";
reg = <0x0 0xff720000 0x0 0x100>;
clocks = <&pmucru PCLK_GPIO0_PMU>;
interrupts = ;
gpio-controller;
#gpio-cells = <0x2>;
interrupt-controller;
#interrupt-cells = <0x2>;
};
...
gpio4: gpio4@ff790000 {
...
}
一共定義了 5 個 gpio-controller 節(jié)點,對應(yīng)芯片上的 5 個 gpio bank。
里面用于表明寄存器地址 和 clock 等屬性會在 gpio chip driver 中被使用。
2) chip driver
這里只關(guān)心程序主干,不關(guān)心芯片廠商為了適應(yīng)多款芯片而封裝的代碼。
gpio_chip 的注冊過程:
drivers/pinctrl/pinctrl-rockchip.c
rockchip_pinctrl_probe(){
//rockchip用于管理gpio/pinctrl大管家結(jié)構(gòu)體
structrockchip_pinctrl*info=devm_kzalloc()
//rk3399gpiobank相關(guān)的硬件描述信息
structrockchip_pin_ctrl*ctrl=&rk3399_pin_ctrl;
info->ctrl=ctrl;
rockchip_gpiolib_register(pdev,info);{
structgpio_chip*gc;
for(i=0;inr_banks;++i,++bank){
//初始化gpio_chip
gc=&rockchip_gpiolib_chip;
gc->base=bank->pin_base;
gc->ngpio=bank->nr_pins;
//注冊gpio_chip
gpiochip_add_data(gc,bank);
}
}
}
struct gpio_chip 的定義:
staticconststructgpio_chiprockchip_gpiolib_chip={
.request=gpiochip_generic_request,
.free=gpiochip_generic_free,
.set=rockchip_gpio_set,
.get=rockchip_gpio_get,
.get_direction=rockchip_gpio_get_direction,
.direction_input=rockchip_gpio_direction_input,
.direction_output=rockchip_gpio_direction_output,
.set_config=rockchip_gpio_set_config,
.to_irq=rockchip_gpio_to_irq,
.owner=THIS_MODULE,
};
這些函數(shù)都是在操作 rk3399 gpio 相關(guān)的寄存器,實現(xiàn)一個 gpio chip driver 本質(zhì)上就是實現(xiàn)上面一系列的硬件操作函數(shù)。
四、應(yīng)用層如何訪問 gpio
1. /dev/gpiochipX
直接操作字符設(shè)備是比較低效率的,內(nèi)核里提供了一些 demo:
$cdlinux-4_19/tools/gpio
$ls
Makefile
gpio-event-mon.c
gpio-hammer.c
gpio-utils.c
lsgpio.c
gpio-utils.h
$makeARCH=arm64CROSS_COMPILE=aarch64-linux-
具體的代碼請各位自行閱讀吧。
2. libgpiod
libgpiod 是一個用 C 語言編寫的用于訪問 gpio chardev 的庫,同時里面包含了一些訪問 gpio 的命令行工具,推薦優(yōu)先采用這個庫來訪問 gpio。
編譯:
$gitclonehttps://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git-bv1.6.x
$./autogen.sh--enable-tools=yes
$make&&makeinstall
$ldconfig
附帶的幾個命令行工具:
gpiodetect – list all gpiochips present on the system, their names, labels and number of GPIO lines
gpioinfo – list all lines of specified gpiochips, their names, consumers, direction, active state and additional flags
gpioget – read values of specified GPIO lines
gpioset – set values of specified GPIO lines, potentially keep the lines exported and wait until timeout, user input or signal
gpiofind – find the gpiochip name and line offset given the line name
gpiomon – wait for events on GPIO lines, specify which events to watch, how many events to process before exiting or if the events should be reported to the console
$gpiodetect
gpiochip0[gpio0](32lines)
gpiochip1[gpio1](32lines)
gpiochip2[gpio2](32lines)
gpiochip3[gpio3](32lines)
gpiochip4[gpio4](32lines)
$gpioinfogpio0
gpiochip0-32lines:
line0:unnamedunusedinputactive-high
line1:unnamed"vcc_sd"outputactive-high[used]
line2:unnamedunusedinputactive-high
line3:unnamedunusedinputactive-high
line4:unnamed"bt_default_wake_host"inputactive-high[used]
line5:unnamed"GPIOKeyPower"inputactive-low[used]
...
line13:unnamed"status_led"outputactive-high[used]
...
line30:unnamedunusedinputactive-high
line31:unnamedunusedinputactive-high
3. sysfs
過時的接口,不推薦使用,用法如下:
$echo25>/sys/class/gpio/export
$echoout>/sys/class/gpio/gpio25/direction
$echo1>/sys/class/gpio/gpio25/value
五、相關(guān)參考
-
Linux-4.19 Documentation
-
Linux Device Drivers Development / GPIO Controller Drivers
原文標(biāo)題:五、相關(guān)參考
文章出處:【微信公眾號:FPGA之家】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
-
嵌入式
+關(guān)注
關(guān)注
5082文章
19126瀏覽量
305242 -
內(nèi)核
+關(guān)注
關(guān)注
3文章
1372瀏覽量
40290 -
GPIO
+關(guān)注
關(guān)注
16文章
1204瀏覽量
52104
發(fā)布評論請先 登錄
相關(guān)推薦
評論