简介
GPIO, 全称 General-Purpose Input/Output(通用输入输出),是一种软件运行期间能够动态配置和控制的通用引脚。
RK3288有9组 GPIO bank: GPIO0,GPIO1, ..., GPIO8。每组又以 A0~A7, B0~B7, C0~C7, D0~D7 作为编号区分(不是所有 bank 都有全部编号,例如 GPIO5 就只有 B0~B7, C0~C3)。
每个GPIO口除了通用输入输出功能外,还可能有其它复用功能,例如GPIO5_B4,可以复用成以下功能之一:
spi0_clk
ts0_data4
uart4exp_ctsn
每个 GPIO 口的驱动电流、上下拉和重置后的初始状态都不尽相同,详细情况请参考《RK3288 规格书》中的 "RK3288 function IO description" 一章。
RK3288 的 GPIO 驱动是在以下 pinctrl 文件中实现的: kernel/drivers/pinctrl/pinctrl-rockchip.c
其核心是填充 GPIO bank 的方法和参数,并调用 gpiochip_add 注册到内核中。

使用
开发板有两个电源 LED 灯是 GPIO 口控制的,分别是:

从电路图上看,GPIO 口输出低电平时灯亮,高电平时灯灭。
另外,扩展槽上引出了几个空闲的 GPIO 口,分别是:

这几个 GPIO 口可以自定义作输入、输出使用。

输入输出
下面以电源 LED 灯的驱动为例,讲述如何在内核编写代码控制 GPIO 口的输出。
首先需要在 dts (Device Tree) 文件 firefly-rk3288.dts (0930版) 或 firefly-rk3288_beta.dts (0809版) 中增加驱动的资源描述:

firefly-led{
compatible = "firefly,led";
led-work = <&gpio8 GPIO_A2 GPIO_ACTIVE_LOW>;
led-power = <&gpio8 GPIO_A1 GPIO_ACTIVE_LOW>;
status = "okay";
};

这里定义了两颗 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 = -;
int gpio, flag;
struct device_node *led_node = pdev->dev.of_node; gpio = of_get_named_gpio_flags(led_node, "led-power", , &flag);
if (!gpio_is_valid(gpio)){
printk("invalid led-power: %d\n",gpio);
return -;
}
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) ? : ;
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 的功能定义如下:
Pad# func0 func1
I2C4_SDA/GPIO7_C1  gpio7c1  i2c4tp_sda
I2C4_SCL/GPIO7_C2  gpio7c2  i2c4tp_scl
在 /kernel/arch/arm/boot/dts/rk3288.dtsi 里有:

i2c4: i2c@ff160000 {
compatible = "rockchip,rk30-i2c";
reg = <0xff160000 0x1000>;
interrupts = <GIC_SPI IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <>;
#size-cells = <>;
pinctrl-names = "default", "gpio";
pinctrl- = <&i2c4_sda &i2c4_scl>;
pinctrl- = <&i2c4_gpio>;
gpios = <&gpio7 GPIO_C1 GPIO_ACTIVE_LOW>,
<&gpio7 GPIO_C2 GPIO_ACTIVE_LOW>;
clocks = <&clk_gates6 >;
rockchip,check-idle = <>;
status = "disabled";
};

此处,跟复用控制相关的是 pinctrl- 开头的属性:
pinctrl-names 定义了状态名称列表: default (i2c 功能) 和 gpio 两种状态。
pinctrl-0 定义了状态 0 (即 default)时需要设置的 pinctrl: i2c4_sda 和 i2c4_scl
pinctrl-1 定义了状态 1 (即 gpio)时需要设置的 pinctrl: i2c4_gpio
这些 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, );
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, );
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);

RK3288 GPIO的更多相关文章

  1. RK3288 GPIO 输出问题

    cat /sys/kernel/debug/gpio cat /proc/bus/input/devices #define GPIO_BANK0              (0 << R ...

  2. RK3288 GPIO 输出问题【转】

    本文转载自:http://m.blog.csdn.net/jiangdou88/article/details/50158673 #define GPIO_BANK0              (0 ...

  3. RK3288 GPIO控制

    参考: https://blog.csdn.net/kris_fei/article/details/69553422

  4. rk3128 通过串口控制 GPIO

    2019-04-24 关键字: rk平台控制GPIO功能.rk串口控制引脚电平 本篇文章介绍了如何通过串口来控制 RK3128.RK3288 平台的 GPIO . 我们可以很便捷地通过串口命令来控制 ...

  5. RK3288的gpio设置

    http://www.360doc.com/content/14/1227/18/14351252_436191812.shtml 目录 [隐藏]  1 简介 2 使用 2.1 输入输出 2.2 复用 ...

  6. [RK3288][Android6.0] 调试笔记 --- 通用GPIO驱动控制LED【转】

    本文转载自:http://m.blog.csdn.net/kris_fei/article/details/69553422 Platform: ROCKCHIPOS: Android 6.0Kern ...

  7. RK3288的gpio设置【转】

    本文转载自:http://blog.csdn.net/keleming1/article/details/51034766 转http://www.360doc.com/content/14/1227 ...

  8. android jni控制gpio (rk3288)

    1.添加驱动程序 2.编写jni c程序编译为库给java调用 3.app调用jni静态链接库操作底层驱动 1.添加驱动程序 修改/work/rk3288/firefly-rk3288_android ...

  9. RK3288 控制usb摄像头补光GPIO

    正常可以通过添加dts配置,再从设备树中读取gpio号,在这里为了简单,直接使用GPIO号,先通过终端测试gpio, 系统自带GPIO控制驱动: 内核已经自带了通用GPIO驱动,可以直接在用户空间操作 ...

随机推荐

  1. vue.js把mounted里面的变量传到data里面

    data(){ return{ crux:[] //声明下变量 } }, mounted:function(){ var cruxs = localStorage.historyItems.split ...

  2. 细数Linux的文件权限

    普通权限 普通权限使用ls -l查看,最前面显示的即是,如: # ls -l .txt -rw-r--r-- 1 root root 8338 7月 19 20:27 1.txt 权限介绍: -/d/ ...

  3. Mac appStore下载软件报500错误的解决方法

    Step1.打开"启动台": Step2:打开"其他": Step3:打开"终端": Step4:复制如下代码,主要不要作任何更改,不要有多 ...

  4. 20171012--jq 遍历取值

    1.父节点:parent(); 2.子节点:children(); 3.兄弟节点:siblings(); 4.用find:$("div").find("span" ...

  5. [译]RabbitMQ教程C#版 - 远程过程调用(RPC)

    先决条件 本教程假定 RabbitMQ 已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需要调整连接设置. 从哪里获得帮助 如果您在阅读本教程时遇到困难, ...

  6. MySQL单表多次查询和多表联合查询,哪个效率高?

    很多高性能的应用都会对关联查询进行分解. 简单地,可以对每个表进行一次单表查询,然后将结果在应用程序中进行关联.例如,下面这个查询: select * from tag join tag_post o ...

  7. 用Python实现简单通讯录

    一个简单的通讯录例子 #!/usr/bin/python __author__ = 'fierce' #coding:utf-8 import os #引用os模块 import pickle #应用 ...

  8. _trigger

    -- 触发器设置-- 使用说明:-- 1.脚本名为TriggerScript,使用时添加该生物即可-- 2.可以控制玩家进入附近时是上马.下马.或者提示信息.

  9. HTTP响应 状态码描述

  10. C++学习建议

    C++学习建议 C++缺点之一,是相对许多语言复杂,而且难学难精.许多人说学习C语言只需一本K&R<C程序设计语言>即可,但C++书籍却是多不胜数.我是从C进入C++,皆是靠阅读自 ...