【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 */ };二、在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,
};
#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>
三、中斷
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 //低電平觸發
調用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的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 指纹调试流程(高通、MT
- 下一篇: ADB安卓调试工具使用总结