簡介GPIO, 全稱 General-Purpose Input/Output(通用輸入輸出),是一種軟件運行期間可以動態配置和控制的通用引腳。php RK3288 有 9 組 GPIO bank: GPIO0,GPIO1, ..., GPIO8。每組又以 A0~A7, B0~B7, C0~C7, D0~D7 做爲編號區分(不是全部 bank 都有所有編號,例如 GPIO5 就只有 B0~B7, C0~C3)。node 每一個 GPIO 口除了通用輸入輸出功能外,還可能有其它複用功能,例如 GPIO5_B4,能夠複用成如下功能之一:linux
每一個 GPIO 口的驅動電流、上下拉和重置後的初始狀態都不盡相同,詳細狀況請參考《RK3288 規格書》中的 "RK3288 function IO description" 一章。ios RK3288 的 GPIO 驅動是在如下 pinctrl 文件中實現的:es6 kernel/drivers/pinctrl/pinctrl-rockchip.c 其核心是填充 GPIO bank 的方法和參數,並調用 gpiochip_add 註冊到內核中。框架 使用開發板有兩個電源 LED 燈是 GPIO 口控制的,分別是:編碼 從電路圖上看,GPIO 口輸出低電平時燈亮,高電平時燈滅。spa 另外,擴展槽上引出了幾個空閒的 GPIO 口,分別是:orm 這幾個 GPIO 口能夠自定義做輸入、輸出使用。ip 輸入輸出下面以電源 LED 燈的驅動爲例,講述如何在內核編寫代碼控制 GPIO 口的輸出。 首先須要在 dts (Device Tree) 文件 firefly-rk3288.dts (0930版) 或 firefly-rk3288_beta.dts (0809版) 中增長驅動的資源描述:
這裏定義了兩顆 LED 燈的 GPIO 設置: led-work GPIO8_A2 GPIO_ACTIVE_LOW led-power GPIO8_A1 GPIO_ACTIVE_LOW GPIO_ACTIVE_LOW 表示低電平有效(燈亮),若是是高電平有效,須要替換爲 GPIO_ACTIVE_HIGH 。 以後在驅動程序中加入對 GPIO 口的申請和控制則可: #ifdef CONFIG_OF #include <linux/of.h> #include <linux/of_gpio.h> #endif static int firefly_led_probe(struct platform_device *pdev) { int ret = -1; int gpio, flag; struct device_node *led_node = pdev->dev.of_node; gpio = of_get_named_gpio_flags(led_node, "led-power", 0, &flag); if (!gpio_is_valid(gpio)){ printk("invalid led-power: %d\n",gpio); return -1; } if (gpio_request(gpio, "led_power")) { printk("gpio %d request failed!\n",gpio); return ret; } led_info.power_gpio = gpio; led_info.power_enable_value = (flag == OF_GPIO_ACTIVE_LOW) ? 0 : 1; gpio_direction_output(led_info.power_gpio, !(led_info.power_enable_value)); ... on_error: gpio_free(gpio); } of_get_named_gpio_flags 從設備樹中讀取 led-power 的 GPIO 配置編號和標誌,gpio_is_valid 判斷該 GPIO 編號是否有效,gpio_request 則申請佔用該 GPIO。若是初始化過程出錯,須要調用 gpio_free 來釋放以前申請過且成功的 GPIO 。 調用 gpio_direction_output 就能夠設置輸出高仍是低電平,由於是 GPIO_ACTIVE_LOW ,若是要燈亮,須要寫入 0 。 實際中若是要讀出 GPIO,須要先設置成輸入模式,而後再讀取值: int val; gpio_direction_input(your_gpio); val = gpio_get_value(your_gpio); 下面是經常使用的 GPIO API 定義: #include <linux/gpio.h> #include <linux/of_gpio.h> enum of_gpio_flags { OF_GPIO_ACTIVE_LOW = 0x1, }; int of_get_named_gpio_flags(struct device_node *np, const char *propname, int index, enum of_gpio_flags *flags); int gpio_is_valid(int gpio); int gpio_request(unsigned gpio, const char *label); void gpio_free(unsigned gpio); int gpio_direction_input(int gpio); int gpio_direction_output(int gpio, int v) 複用如何定義 GPIO 有哪些功能能夠複用,在運行時又如何切換功能呢?以 I2C4 爲例做簡單的介紹。 查規格表可知,I2C4_SDA 與 GPIO7C1 的功能定義以下:
在 /kernel/arch/arm/boot/dts/rk3288.dtsi 裏有: i2c4: i2c@ff160000 { compatible = "rockchip,rk30-i2c"; reg = <0xff160000 0x1000>; interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; pinctrl-names = "default", "gpio"; pinctrl-0 = <&i2c4_sda &i2c4_scl>; pinctrl-1 = <&i2c4_gpio>; gpios = <&gpio7 GPIO_C1 GPIO_ACTIVE_LOW>, <&gpio7 GPIO_C2 GPIO_ACTIVE_LOW>; clocks = <&clk_gates6 15>; rockchip,check-idle = <1>; status = "disabled"; }; 此處,跟複用控制相關的是 pinctrl- 開頭的屬性:
這些 pinctrl 在 /kernel/arch/arm/boot/dts/rk3288-pinctrl.dtsi 中定義: / { pinctrl: pinctrl@ff770000 { compatible = "rockchip,rk3288-pinctrl"; ... gpio7_i2c4 { i2c4_sda:i2c4-sda { rockchip,pins = <I2C4TP_SDA>; rockchip,pull = <VALUE_PULL_DISABLE>; rockchip,drive = <VALUE_DRV_DEFAULT>; //rockchip,tristate = <VALUE_TRI_DEFAULT>; }; i2c4_scl:i2c4-scl { rockchip,pins = <I2C4TP_SCL>; rockchip,pull = <VALUE_PULL_DISABLE>; rockchip,drive = <VALUE_DRV_DEFAULT>; //rockchip,tristate = <VALUE_TRI_DEFAULT>; }; i2c4_gpio: i2c4-gpio { rockchip,pins = <FUNC_TO_GPIO(I2C4TP_SDA)>, <FUNC_TO_GPIO(I2C4TP_SCL)>; rockchip,drive = <VALUE_DRV_DEFAULT>; }; }; ... } } I2C4TP_SDA, I2C4TP_SCL 的定義在 /kernel/arch/arm/boot/dts/include/dt-bindings/pinctrl/rockchip-rk3288.h 中: #define GPIO7_C1 0x7c10 #define I2C4TP_SDA 0x7c11 #define GPIO7_C2 0x7c20 #define I2C4TP_SCL 0x7c21 FUN_TO_GPIO 的定義在 /kernel/arch/arm/boot/dts/include/dt-bindings/pinctrl/rockchip.h 中: #define FUNC_TO_GPIO(m) ((m) & 0xfff0) 也就是說 FUNC_TO_GPIO(I2C4TP_SDA) == GPIO7_C1, FUNC_TO_GPIO(I2C4TP_SCL) == GPIO7_C2 。 像 0x7c11 這樣的值是有編碼規則的: 7 c1 1 | | `- func | `---- offset `------ bank 0x7c11 就表示 GPIO7_C1 func1, 即 i2c4tp_sda 。 在複用時,若是選擇了 "default" (即 i2c 功能),系統會應用 i2c4_sda 和 i2c4_scl 這兩個 pinctrl,最終得將 GPIO7_C1 和 GPIO7_C2 兩個針腳切換成對應的 i2c 功能;而若是選擇了 "gpio" ,系統會應用 i2c4_gpio 這個 pinctrl,將 GPIO7_C1 和 GPIO7_C2 兩個針腳還原爲 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 = <&gpio7 GPIO_C1 GPIO_ACTIVE_LOW>, <&gpio7 GPIO_C2 GPIO_ACTIVE_LOW>; 而後是調用 devm_gpio_request 來申請 gpio,接着是調用 pinctrl_lookup_state 來查找 「gpio」 狀態,而默認狀態 "default" 已經由框架保存到 i2c->dev-pins->default_state 中了。 最後調用 pinctrl_select_state 來選擇是 "default" 仍是 "gpio" 功能。 下面是經常使用的複用 API 定義: #include <linux/pinctrl/consumer.h> struct device { //... #ifdef CONFIG_PINCTRL struct dev_pin_info *pins; #endif //... }; struct dev_pin_info { struct pinctrl *p; struct pinctrl_state *default_state; #ifdef CONFIG_PM struct pinctrl_state *sleep_state; struct pinctrl_state *idle_state; #endif }; struct pinctrl_state * pinctrl_lookup_state(struct pinctrl *p, const char *name); int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s); |