日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【Device Tree】设备树(一)——GPIO

發布時間:2023/12/15 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Device Tree】设备树(一)——GPIO 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

以RK3328為例,介紹設備樹在GPIO方面的應用。
引腳圖如下

一、首先在DTS文件中增加GPIO資源描述:

gpio_demo: gpio_demo {status = "okay";compatible = "rk3328,gpio_demo";firefly-gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>; /* GPIO0_B4 */firefly-irq-gpio = <&gpio2 29 IRQ_TYPE_EDGE_RISING>; /* GPIO2_D5 */ };
  • GPIO_ACTIVE_HIGH:引腳狀態為常高
  • IRQ_TYPE_EDGE_RISING:上升沿觸發
  • RK系列GPIO分為GPIO0,GPIO1,GPIO2…等組,每組GPIO下分為A,B,C,D等小組,而每個A下有0-7個引腳,B,C,D同A。于是A=0,B與A相差8個引腳,C與B相差8個引腳,以此類推。。。GPIO0_B4?屬于B組,基數為8,加4即為引腳序列號12。
  • 二、在probe函數里添加解析

    1.區別于無Device Tree驅動程序而言,首先要有一個device_id結構體用于匹配我們在設備樹資源里添加的屬性:

    static const struct of_device_id gpio_demo_dt_ids[] = {{ .compatible = "rk3328,gpio_demo", },{}, };

    2.在平臺設備驅動結構體里加上.of_match_table?屬性,of_match_ptr(gpio_demo_dt_ids)是匹配設備樹的函數

    static struct platform_driver int_demo_driver = { .driver = { .name = "gpio_demo", .of_match_table = of_match_ptr(gpio_demo_dt_ids), }, .probe = int_demo_probe, .remove = int_demo_remove,};

    num of_gpio_flags {
    OF_GPIO_ACTIVE_LOW = 0x1,
    };

    static int firefly_gpio_probe(struct platform_device *pdev) {int ret; int gpio; enum of_gpio_flags flag; struct firefly_gpio_info *gpio_info; struct device_node *firefly_gpio_node = pdev->dev.of_node; //用于接收傳入的設備樹結點printk("Firefly GPIO Test Program Probe\n"); //1、申請空間gpio_info = devm_kzalloc(&pdev->dev,sizeof(struct firefly_gpio_info *), GFP_KERNEL); if (!gpio_info) { return -ENOMEM;}//2、獲取名為 "firefly-gpio"的gpio信息gpio = of_get_named_gpio_flags(firefly_gpio_node, "firefly-gpio", 0, &flag); if (!gpio_is_valid(gpio)) {printk("firefly-gpio: %d is invalid\n", gpio); return -ENODEV;} //請求控制獲取的gpioif (gpio_request(gpio, "firefly-gpio")) { printk("gpio %d request failed!\n", gpio); gpio_free(gpio); return -ENODEV;} //設置操作的gpiogpio_info->firefly_gpio = gpio;//根據flag判斷使能值gpio_info->gpio_enable_value = (flag == OF_GPIO_ACTIVE_LOW) ? 0:1;//設置為輸出,首參為gpio的名字,設置gpio的電平值。gpio_direction_output(gpio_info->firefly_gpio, gpio_info->gpio_enable_value); printk("Firefly gpio putout\n"); }

    #include <linux/gpio.h> //里面聲明io口的操作函數

    1. int gpio_request(unsigned gpio, const char *label);//每個io只能被請求一次,可防止多個驅動來控制同一個IO口 2. void gpio_free(unsigned gpio); //釋放已請求的io口 3. int gpio_direction_input(unsigned gpio); //把指定的IO口作輸入功能, gpio用于指定具體哪個io口 4. int gpio_direction_output(unsigned gpio, int value); //作輸出功能,并根據value的值輸出高低電平 5. int gpio_get_value(unsigned gpio); //獲取指定IO口的電平 6. void gpio_set_value(unsigned gpio, int value); //設置IO口的電平為value(0/1)7. int gpio_to_irq(unsigned gpio); //根據io口,獲取到它對應的中斷號(io口大都有外部中斷功能)

    獲取設備樹里設備節點的gpio口信息:
    #include <linux/of_gpio.h>

    //只需一個函數即可 int of_get_named_gpio_flags(struct device_node *np, const char *propname,int index, enum of_gpio_flags *flags);//返回值為int類型的gpio口. //np為設備或設備子節點對象, propname為指定的屬性名字, index表示獲取屬性里的第幾個值 // 其中flags一定得注意,按文檔里的說明應就是一個int類型的值,但根本就不能為int參數(會導致kernel panic), // 通過閱讀內核里的代碼得出, flags的參數應為struct gpio_config類型. 定義在下面文件: "include/linux/sys_config.h" struct gpio_config {u32 gpio; /* gpio global index, must be unique */u32 mul_sel; /* multi sel val: 0 - input, 1 - output... */u32 pull; /* pull val: 0 - pull up/down disable, 1 - pull up... */u32 drv_level; /* driver level val: 0 - level 0, 1 - level 1... */u32 data; /* data val: 0 - low, 1 - high, only vaild when mul_sel is input/output */};

    三、中斷

    firefly-irq-gpio = <&gpio2 29 IRQ_TYPE_EDGE_RISING>; /* GPIO2_D5 */

    中斷的類型

    IRQ_TYPE_NONE //默認值,無定義中斷觸發類型
    IRQ_TYPE_EDGE_RISING //上升沿觸發
    IRQ_TYPE_EDGE_FALLING //下降沿觸發
    IRQ_TYPE_EDGE_BOTH //上升沿和下降沿都觸發
    IRQ_TYPE_LEVEL_HIGH //高電平觸發
    IRQ_TYPE_LEVEL_LOW //低電平觸發

    static int firefly_gpio_probe(struct platform_device *pdev) { int ret; int gpio; enum of_gpio_flags flag; struct firefly_gpio_info *gpio_info; struct device_node *firefly_gpio_node = pdev->dev.of_node; ...... //1、獲取名為 "firefly-irq-gpio"的gpio信息gpio = of_get_named_gpio_flags(firefly_gpio_node, "firefly-irq-gpio", 0, &flag); gpio_info->firefly_irq_gpio = gpio; gpio_info->firefly_irq_mode = flag; //把GPIO的PIN值轉換為相應的IRQ值gpio_info->firefly_irq = gpio_to_irq(gpio_info->firefly_irq_gpio); if (gpio_info->firefly_irq) { if (gpio_request(gpio, "firefly-irq-gpio")) { printk("gpio %d request failed!\n", gpio); gpio_free(gpio); return IRQ_NONE; } //申請中斷ret = request_irq(gpio_info->firefly_irq, firefly_gpio_irq, flag, "firefly-irq-gpio", gpio_info); if (ret != 0) free_irq(gpio_info->firefly_irq, gpio_info); dev_err(&pdev->dev, "Failed to request IRQ: %d\n", ret); } return 0; } static irqreturn_t firefly_gpio_irq(int irq, void *dev_id) //中斷函數 { printk("Enter firefly gpio irq test program!\n");return IRQ_HANDLED; }

    調用gpio_to_irq把GPIO的PIN值轉換為相應的IRQ值,調用gpio_request申請占用該IO口,調用request_irq申請中斷,如果失敗要調用free_irq釋放,該函數中gpio_info->firefly_irq是要申請的硬件中斷號,firefly_gpio_irq是中斷函數,gpio_info->firefly_irq_mode是中斷處理的屬性,”firefly-gpio”是設備驅動程序名稱,gpio_info是該設備的device結構,在注冊共享中斷時會用到。

    四、復用

    如何定義 GPIO 有哪些功能可以復用,在運行時又如何切換功能呢?以 I2C4 為例作簡單的介紹。

    查規格表可知,I2C4_SDA 與 I2C4_SCL 的功能定義如下:

    Pad# func0 func1 I2C4_SDA/GPIO1_B3
    gpio1b3 i2c4_sda I2C4_SCL/GPIO1_B4 gpio1b4 i2c4_scl

    在 kernel/arch/arm64/boot/dts/rockchip/rk3328xx.dtsi 里有:

    i2c4: i2c@ff3d0000{compatible = "rockchip,rk3399-i2c"; reg = <0x0 0xff3d0000 0x0 0x1000>; clocks = <&pmucru SCLK_I2C4_PMU>, <&pmucru PCLK_I2C4_PMU>; clock-names = "i2c", "pclk"; interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH 0>; pinctrl-names = "default", "gpio"; pinctrl-0 = <&i2c4_xfer>; pinctrl-1 = <&i2c4_gpio>; //此處源碼未添加 #address-cells = <1>; #size-cells = <0>; status = "disabled"; };

    此處,跟復用控制相關的是 pinctrl- 開頭的屬性:

    pinctrl-names 定義了狀態名稱列表: default (i2c 功能) 和 gpio 兩種狀態。 pinctrl-0 定義了狀態 0 (即 default)時需要設置的 pinctrl: &i2c4_xfer pinctrl-1 定義了狀態 1 (即 gpio)時需要設置的 pinctrl: &i2c4_gpio

    這些 pinctrl 在kernel/arch/arm64/boot/dts/rockchip/rk3399.dtsi中這樣定義:

    pinctrl: pinctrl { compatible = "rockchip,rk3399-pinctrl"; rockchip,grf = <&grf>; rockchip,pmu = <&pmugrf>; #address-cells = <0x2>; #size-cells = <0x2>; ranges; i2c4{i2c4_xfer: i2c4-xfer{rockchip,pins = <1 12 RK_FUNC_1 &pcfg_pull_none>, <1 11 RK_FUNC_1 &pcfg_pull_none>;}; i2c4_gpio: i2c4-gpio { rockchip,pins = <1 12 RK_FUNC_GPIO &pcfg_pull_none>, <1 11 RK_FUNC_GPIO &pcfg_pull_none>; }; };

    RK_FUNC_1,RK_FUNC_GPIO 的定義在 kernel/include/dt-bindings/pinctrl/rk.h 中:

    #define RK_FUNC_GPIO 0#define RK_FUNC_1 1#define RK_FUNC_2 2#define RK_FUNC_3 3#define RK_FUNC_4 4#define RK_FUNC_5 5 #define RK_FUNC_6 6#define RK_FUNC_7 7

    另外,像”1 11”,”1 12”這樣的值是有編碼規則的,編碼方式與上一小節”輸入輸出”描述的一樣,”1 11”代表GPIO1_B3,”1 12”代表GPIO1_B4。

    在復用時,如果選擇了 “default” (即 i2c 功能),系統會應用 i2c4_xfer 這個 pinctrl,最終將 GPIO1_B3 和 GPIO1_B4 兩個針腳切換成對應的 i2c 功能;而如果選擇了 “gpio” ,系統會應用 i2c4_gpio 這個 pinctrl,將 GPIO1_B3 和 GPIO1_B4 兩個針腳還原為 GPIO 功能。

    我們看看 i2c 的驅動程序 kernel/drivers/i2c/busses/i2c-rockchip.c 是如何切換復用功能的:

    static int rockchip_i2c_probe(struct platform_device *pdev) {struct rockchip_i2c *i2c = NULL; struct resource *res; struct device_node *np = pdev->dev.of_node; int ret;// ...i2c->sda_gpio = of_get_gpio(np, 0);if (!gpio_is_valid(i2c->sda_gpio)) {dev_err(&pdev->dev, "sda gpio is invalid\n");return -EINVAL;}ret = devm_gpio_request(&pdev->dev, i2c->sda_gpio, dev_name(&i2c->adap.dev));if (ret) {dev_err(&pdev->dev, "failed to request sda gpio\n");return ret;}i2c->scl_gpio = of_get_gpio(np, 1);if (!gpio_is_valid(i2c->scl_gpio)) {dev_err(&pdev->dev, "scl gpio is invalid\n");return -EINVAL;}ret = devm_gpio_request(&pdev->dev, i2c->scl_gpio, dev_name(&i2c->adap.dev));if (ret) {dev_err(&pdev->dev, "failed to request scl gpio\n");return ret;}i2c->gpio_state = pinctrl_lookup_state(i2c->dev->pins->p, "gpio");if (IS_ERR(i2c->gpio_state)) {dev_err(&pdev->dev, "no gpio pinctrl state\n");return PTR_ERR(i2c->gpio_state);}pinctrl_select_state(i2c->dev->pins->p, i2c->gpio_state);gpio_direction_input(i2c->sda_gpio);gpio_direction_input(i2c->scl_gpio);pinctrl_select_state(i2c->dev->pins->p, i2c->dev->pins->default_state);// ...}

    首先是調用 of_get_gpio 取出設備樹中 i2c4 結點的 gpios 屬于所定義的兩個 gpio:

    gpios = <&gpio1 GPIO_B3 GPIO_ACTIVE_LOW>, <&gpio1 GPIO_B4 GPIO_ACTIVE_LOW>;

    然后是調用 devm_gpio_request 來申請 gpio,接著是調用 pinctrl_lookup_state 來查找 “gpio” 狀態,而默認狀態 “default” 已經由框架保存到 i2c->dev-pins->default_state 中了。

    最后調用 pinctrl_select_state 來選擇是 “default” 還是 “gpio” 功能。

    總結

    以上是生活随笔為你收集整理的【Device Tree】设备树(一)——GPIO的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。