【linux】驅動-11-gpio子系統



前言

參考文檔:

建議:復制以下鏈接,到原文觀看,原文排版清晰,便於學習。

11. gpio子系統

引腳配置為 GPIO 模式后,便可使用 GPIO子系統 來控制引腳。
可以通過 pinctrl子系統 配置,也可以自己編程配置。

參考文檔:Documentation/devicetree/bindings/gpio/ 下對應芯片廠商的文件。

11.1 操作步驟

  1. 在設備樹對應節點中指定引腳。(哪一組、組里哪一個引腳)
  2. 在驅動程序中通過 GPIO 子系統提供的 API 控制引腳。

11.1.1 新版 API 操作流程

個人喜歡用代碼格式表述:

/** @file driver.c
 * @brief 驅動教程文件
 * @details 
 * @author lzm
 * @date 2021-04-09 20:22:22
 * @version v1.0
 * @copyright Copyright By lizhuming, All Rights Reserved
 * @cnblogs   https://www.cnblogs.com/lizhuming/
 **********************************************************
 * @LOG 修改日志:
 **********************************************************
 */

/* gpio 子系統開發步驟 */

/* [gpio][1] 請求引腳 */
/* 使用 gpiod_get() 或 devm_gpiod_get() 或 gpiod_get_index() 或 devm_gpiod_get_index() 等等函數 */

/* [gpio][2] 設置方向 */
/* 使用 gpiod_direction_input() 或 gpiod_direction_output() 等等函數 */

/* [gpio][3] 導出到應用層 */
/* 使用 gpiod_export() 函數 */

/* [gpio][4] 設置/獲取 值 */
/* 使用 gpiod_set_value() 或 gpiod_get_value() 等等函數 */

/* [gpio][5] 轉中斷,注冊中斷 */
/* 使用 gpiod_set_value() 和 request_irq() 函數 */

/* [gpio][6] 釋放引腳 */
/* 使用 gpiod_put() 或 gpiod_put() 函數 */

11.1.2 舊版 API 操作流程

流程和新版 API 操作流程差不多。找到對應的函數即可。

11.2 設備樹中使用gpio子系統

在設備樹中,GPIO組 就是一個 GPIO Controller

GPIO組 的節點內容是由芯片廠商設置好的,一般在芯片廠商提供的設備樹頭文件 xxx.dtsi 中。如 IMX6UL 的就在 imx6ull.dtsi 文件中定義。
用戶只需要做的是根據芯片廠商文檔格式要求,在相應設備樹節點中填寫引腳信息。
如 IMX6ULL:fsl-imx-gpio.txt

一般,我們參考 GPIO組 節點里面的兩個屬性即可:

  • gpio-controller
    • 如果 GPIO組 節點內含有該屬性,則表示該節點為 GPIO 控制器節點。
  • gpio-cells
    • 表示這個控制器下的每一個引腳需要用多少個 32 位數來描述。

如 IMX6ULL:

  • gpio-controller: Marks the device node as a gpio controller.
  • #gpio-cells: Should be two.
    • The first cell is the pin number.
    • The second cell is used to specify the gpio polarity:
      • 0 = active high.
      • 1 = active low.

例子:

/*添加rgb_led節點*/
rgb_led{
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "fire,rgb-led"; // 節點兼容性
    /* pinctrl 子系統 */
    pinctrl-names = "default"; // 引腳狀態名稱表
    pinctrl-0 = <&pinctrl_rgb_led>; // 第 0 個狀態使用的引腳
    /* GPIO子系統 */
    rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>; // 使用的引腳 。舊版
    rgb_led_green-gpios = <&gpio4 20 GPIO_ACTIVE_LOW>; // 新版
    rgb_led_blue-gpios = <&gpio4 19 GPIO_ACTIVE_LOW>; // 新版
    status = "okay";
};

分析 rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;

  • rgb_led_red:自定義的引腳名字。(舊版
    • 新版必須使用 gpios 或 后綴為 -gpios 的屬性名。這樣才能使用新的 GPIO 子系統 API。
    • 如:rgb_led_red 改為 rgb_led_red-gpios
  • &gpio1:GPIO組1。
  • 4:第 4 號引腳。
  • GPIO_ACTIVE_LOW:低電平有效。

11.3 GPIO 子系統 API 說明

GPIO 子系統有兩套API

  • 基於描述符(descriptor-based)
    • 前綴為 gpiod_
    • 使用 gpio_desc 幾個題來表示一個引腳。
    • 參考文檔:Documentation/gpio/consumer.txt
  • 老接口(legacy)
    • 前綴為 gpio_
    • 使用一個整數來表示一個引腳。
    • 參考文檔:Documentation/gpio/gpio-legacy.txt

11.3.1 驅動操作一個引腳的步驟

  1. get 引腳。
  2. 設置引腳方向。
  3. 讀、寫引腳。

11.3.2 API 所需頭文件

#include <linux/gpio/consumer.h>   // 基於描述符

#include <linux/gpio.h>            // 老接口 

11.3.3 主要結構體

gpio_desc:

struct gpio_desc 
{
    struct gpio_chip  *chip;  /* 這個 gpio pin 所在的 chip */
    unsigned long  flags;    /*  設置 is_out flag */
    const char  *label;        /* label 就是名字 */
};
  • 源碼路徑:內核源碼\drivers\gpio\gpiolib.h
struct gpio_descs 
{
    unsigned int ndescs; // gpio_desc 個數
    struct gpio_desc *desc[]; // gpio_desc
}
  • 源碼路徑:內核源碼\drivers\gpio\gpiolib.h

11.4 新舊版互相兼容轉換 API

舊版API是使用整數標記引腳的;
新版API是使用字符標記引腳的。
但是引腳都是唯一的,所以兩者可以相互轉化。

轉化函數:

  • desc_to_gpio()
  • gpio_to_desc()

desc_to_gpio

  • 函數原型:int desc_to_gpio(const struct gpio_desc *desc)
    • 源碼路徑:drivers\gpio\gpiolib.c
    • 通過引腳 gpio_desc 結構體指針 獲取引腳 GPIO 號。
    • gpio:GPIO number。

gpio_to_desc

  • 函數原型:struct gpio_desc *gpio_to_desc(unsigned gpio)
    • 源碼路徑:drivers\gpio\gpiolib.c
    • 通過引腳 GPIO 號獲取引腳 gpio_desc 結構體指針。
    • gpio:GPIO number。

11.5 descriptor-based 版常用 API

11.5.1 獲取 GPIO

gpiod_get

  • 函數原型:struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,enum gpiod_flags flags)
    • 源碼路徑:drivers\gpio\gpiolib.c
    • 獲取 dev 設備,con_id 的第 0 個引腳信息,並做 flags 初始化。
    • dev:設備指針。從該設備獲取引腳信息。
    • con_id:引腳組名稱(不包含前綴)。
      • 如引腳組名為 rgb_led_green-gpios。則 con_id = "rgb_led_green"
    • flags:初始化標志。
      • GPIOD_ASIS or 0 to not initialize the GPIO at all. The direction must be set later with one of the dedicated functions.
      • GPIOD_IN to initialize the GPIO as input.
      • GPIOD_OUT_LOW to initialize the GPIO as output with a value of 0.
      • GPIOD_OUT_HIGH to initialize the GPIO as output with a value of 1.
      • GPIOD_OUT_LOW_OPEN_DRAIN same as GPIOD_OUT_LOW but also enforce the line to be electrically used with open drain.
      • GPIOD_OUT_HIGH_OPEN_DRAIN same as GPIOD_OUT_HIGH but also enforce the line to be electrically used with open drain.
    • 返回:
      • 成功:gpio_desc 結構體指針。
      • 失敗:-ENOENT。具體可通過 IS_ERR() 獲取返回碼。

gpiod_get_index

  • 函數原型:struct gpio_desc *gpiod_get_index(struct device *dev, const char *con_id, unsigned int idx, enum gpiod_flags flags)
    • 源碼路徑:drivers\gpio\gpiolib.c
    • 獲取 dev 設備,con_id 的第 idx 個引腳信息,並做 flags 初始化。

gpiod_get_array

  • 函數原型:struct gpio_descs *__must_check gpiod_get_array(struct device *dev, const char *con_id, enum gpiod_flags flags)
    • 源碼路徑:drivers\gpio\gpiolib.c
    • 獲取 dev 設備 con_id 的所有引腳信息,並做 flags 初始化。

其它獲取GPIO的函數

  • gpiod_get_optional:和 gpiod_get 差不多。不同的是該函數返回 gpio_desc 結構體指針NULL
  • gpiod_get_index_optional
  • gpiod_get_index_optional
  • devm_xxx:以上函數均可添加 devm_ 前綴。比以上函數多了綁定設備,設備被刪除時,自動釋放引腳。

11.5.2 釋放 GPIO

gpiod_put

  • 函數原型:void gpiod_put(struct gpio_desc *desc)
    • 源碼路徑:drivers\gpio\gpiolib.c
    • 釋放 desc 引腳。
    • desc:gpio_desc 結構體指針。

其它釋放GPIO的函數

  • gpiod_put_array
  • devm_gpiod_put
  • devm_gpiod_put_array

11.5.3 設置/獲取 GPIO 方向

gpiod_direction_input

  • 函數原型:int gpiod_direction_input(struct gpio_desc *desc)
    • 源碼路徑:drivers\gpio\gpiolib.c
    • 設置該引腳為輸入方向。

gpiod_direction_output

  • 函數原型:int gpiod_direction_output(struct gpio_desc *desc, int value)
    • 源碼路徑:drivers\gpio\gpiolib.c
    • 設置該引腳為輸入方向。
    • value:設置方向后的初始值。

gpiod_get_direction

  • 函數原型:int gpiod_get_direction(struct gpio_desc *desc)
    • 源碼路徑:drivers\gpio\gpiolib.c
    • 獲取引腳方向。
    • 返回:
      • 0:輸出。
      • 1:輸入。

11.5.4 導出 GPIO 到 sysfs

gpio 通過 sys 文件系統導出,應用層可以通過文件操作gpio。如查看狀態、設置狀態等等。

主要用於調試

導出后訪問路徑:/sys/class/gpio 下。

gpiod_export

  • 函數原型:int gpiod_direction_input(struct gpio_desc *desc)
    • 源碼路徑:drivers\gpio\gpiolib-sysfs.c
    • 把該 gpio 導出到 sys。

gpiod_unexport

  • 函數原型:void gpiod_unexport(struct gpio_desc *desc)
    • 源碼路徑:drivers\gpio\gpiolib-sysfs.c
    • 取消導出。

11.5.5 設置/獲取 GPIO 值

有兩種訪問方式:

  1. 原子方式。
  2. 隊列方式。

gpiod_get_value

  • 函數原型:int gpiod_get_value(const struct gpio_desc *desc)
    • 源碼路徑:drivers\gpio\gpiolib.c
    • 獲取引腳值。
    • 返回:
      • 成功:非負數:zero for low, nonzero for high。
      • 失敗:負數。

gpiod_set_value

  • 函數原型:int gpiod_set_value(struct gpio_desc *desc, int value)
    • 源碼路徑:drivers\gpio\gpiolib.c

以上兩種函數均屬原子操作,能作用於中斷程序
以下兩種函數,在隊列中等待訪問引腳,可能會進入睡眠,不能作用於中斷

訪問必須通過消息總線比如I2C或者SPI,這些需要在隊列中訪問。

gpiod_get_value_cansleep

  • 函數原型:int gpiod_get_value_cansleep(const struct gpio_desc *desc)

gpiod_set_value_cansleep

  • 函數原型:void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)

可以使用 gpiod_cansleep() 函數分辨該引腳是否需要通過消息總線訪問

gpiod_cansleep

  • 函數原型:int gpiod_cansleep(const struct gpio_desc *desc)

11.5.6 GPIO IRQ

gpiod_to_irq

  • 函數原型:int gpiod_to_irq(const struct gpio_desc *desc)
    • 源碼路徑:drivers\gpio\gpiolib.c
    • 獲取該引腳對應的 IRQ number
    • 返回:
      • 成功:中斷號。
      • 失敗:負數。

request_irq

  • 函數原型:static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
    • 源碼路徑:drivers\gpio\gpiolib.c
    • 請求中斷。
    • irq:中斷號。
    • handler:中斷回調函數。
    • flags:中斷類型。
    • name:請求中斷的設備名稱。
    • dev:可取任意值。
      • 但必須唯一能夠代表發出中斷請求的設備。
      • 通常取描述該設備的結構體,或NULL。
      • 用於共享中斷時。(若中斷被共享,則不能為 NULL

11.5.7 GPIO 邏輯電平與物理電平

當設備采用低電平有效時,即是低電平為邏輯 1,高電平為邏輯 0

raw-value:忽略 DTS 中的 ACTIVE。即是實際的物理電平。
有以下函數:

int gpiod_get_raw_value(const struct gpio_desc *desc);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
int gpiod_direction_output_raw(struct gpio_desc *desc, int value);

邏輯電平相關函數 與 物理電平相關函數對比

Function (example) 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

11.6 legacy 版常用 API

盡管建議升級使用新版API,但是現在很多子系統依然使用舊版API。
所以,本筆記也記錄舊版API。

11.6.1 判斷 GPIO number

gpio_is_valid

  • 函數原型:static inline bool gpio_is_valid(int number)
    • 源碼路徑:include\asm-generic\gpio.h
    • 判斷 GPIO number 是否有效。
    • 返回:
      • 有效:1。
      • 無效:0。

11.6.2 申請 GPIO

gpio_request

  • 函數原型:int gpio_request(unsigned gpio, const char *label)

    • 源碼路徑:drivers\gpio\gpiolib-legacy.c
    • 申請 GPIO。
    • gpio:需要申請的 GPIO 編號。
    • label:引腳名字,相當於為申請到的引腳取個別名。
    • 返回:
      • 成功:0。
      • 失敗:負數。

    其它請求函數

    • gpio_request_one()
    • gpio_request_array()

11.6.3 釋放 GPIO

gpio_free

  • 函數原型:static inline void gpio_free(unsigned gpio);

    • 源碼路徑:drivers\gpio\gpiolib-legacy.c
    • 申請 GPIO。
    • gpio:需要釋放的 GPIO 編號。

    其它釋放函數

    • gpio_free_array()

11.6.3 設置 GPIO 方向

gpio_direction_input

  • 函數原型:static inline int gpio_direction_input(unsigned gpio)
    • 源碼路徑:include\linux\gpio.h
    • 把 gpio 引腳設置為為輸入方向。
    • gpio:GPIO 編號。
    • 返回:
      • 成功:0。
      • 失敗:負數。

gpio_direction_output

  • 函數原型:static inline int gpio_direction_output(unsigned gpio, int value)
    • 源碼路徑:include\linux\gpio.h
    • 把 gpio 引腳設置為為輸出方向。
    • gpio:GPIO 編號。
    • value:初始值。
    • 返回:
      • 成功:0。
      • 失敗:負數。

11.6.4 導出 GPIO 到 sysfs

gpio 通過 sys 文件系統導出,應用層可以通過文件操作gpio。如查看狀態、設置狀態等等。
主要用於調試

導出后訪問路徑:/sys/class/gpio 下。

gpio_export

  • 函數原型:static inline int gpio_export(unsigned gpio, bool direction_may_change)
    • 源碼路徑:include\linux\gpio.h
    • 把該 gpio 導出到 sys。
    • gpio:GPIO number。
    • direction_may_change:表示用戶是否可以改變方向。

gpio_unexport

  • 函數原型:static inline void gpio_unexport(unsigned gpio)
    • 源碼路徑:include\linux\gpio.h
    • 取消導出。
    • gpio:GPIO number。

11.6.5 設置/獲取 GPIO 值

有兩種訪問方式:

  1. 原子方式。
  2. 隊列方式。

gpio_get_value

  • 函數原型:static inline int gpio_get_value(unsigned gpio)
    • 源碼路徑:include\asm-generic\gpio.h
    • 獲取引腳值。
    • 返回:
      • 成功:非負數:zero for low, nonzero for high。
      • 失敗:負數。

gpio_set_value

  • 函數原型:static inline void gpio_set_value(unsigned int gpio, int value)

    • 源碼路徑:include\asm-generic\gpio.h

    其它設置函數

    • gpio_set_debounce():支持消抖。

以上兩種函數均屬原子操作,能作用於中斷程序
以下兩種函數,在隊列中等待訪問引腳,可能會進入睡眠,不能作用於中斷

訪問必須通過消息總線比如I2C或者SPI,這些需要在隊列中訪問。

gpio_get_value_cansleep

  • 函數原型:int gpio_get_value_cansleep(unsigned gpio)

gpio_set_value_cansleep

  • 函數原型:void gpio_set_value_cansleep(unsigned gpio, int value)

可以使用 gpiod_cansleep() 函數分辨該引腳是否需要通過消息總線訪問

gpio_cansleep

  • 函數原型:int gpiod_cansleep(unsigned gpio)

11.6.6 GPIO IRQ

gpio_to_irq

  • 函數原型:int gpio_to_irq(unsigned gpio)
    • 源碼路徑:drivers\gpio\gpiolib.c
    • 獲取該引腳對應的 IRQ number
    • 返回:
      • 成功:中斷號。
      • 失敗:負數。

irq_to_gpio:(盡量避免使用

  • 函數原型:int irq_to_gpio(unsigned irq)
    • 源碼路徑:drivers\gpio\gpiolib.c
    • 獲取該引腳對應的 GPIO number
    • 返回:
      • 成功:gpio號。
      • 失敗:負數。

request_irq

  • 函數原型:static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
    • 源碼路徑:drivers\gpio\gpiolib.c
    • 請求中斷。
    • irq:中斷號。
    • handler:中斷回調函數。
    • flags:中斷類型。
    • name:請求中斷的設備名稱。
    • dev:可取任意值。
      • 但必須唯一能夠代表發出中斷請求的設備。
      • 通常取描述該設備的結構體,或NULL。
      • 用於共享中斷時。(若中斷被共享,則不能為 NULL


免責聲明!

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



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