Linux BSP 开发的基础就是和GPIO打交道, 下面总结下这几天对某家开发板的GPIO控制的知识。
公司的开发板用的是 DTB 模式 ,首先,进入 dts,dtsi文件查看关于GPIO 的模块。
soc { . . . gpio0: [email protected]****addr { compatible = "**********"; reg = <0 0x****addr 0 0x50>; interrupts = <SPI 4 IRQ_TYPE_LEVEL_HIGH>; #gpio-cells = <2>; gpio-controller; gpio-ranges = <&pfc 0 0 16>; #interrupt-cells = <2>; interrupt-controller; clocks = <&cpg CPG_MOD 912>; power-domains = <*****>; }; . . . };
可以看到 GPIO 节点 挂在 SOC node 下 ,手上这块开发板 把GPIO分成了8个 bank : gpio0 - gpio7
reg =<0 地址 0 长度>
#gpio-cells =<2> 表示 要用2个cell描述一个 GPIO引脚
如 I2C中定义 : pwd-gpios = <&gpio6 7 GPIO_ACTIVE_HIGH>;
表示 bank 6 的gpio 用 2个cell 描述 :7,GPIO_ACTIVE_HIGH (7表示bank 6 下的第七个引脚一般是 GP 6_07表示 ;GPIO_ACTIVE_HIGH则为高电平有效)
gpio-controller; interrupt-controller; 表示 bank 0 下的引脚 既可以作为中断引脚 ,也可以作为 通用的GPIO引脚
gpio-ranges = <&pfc 0 0 16>; 表示 bank 0下有16个 GPIO引脚
GPIO 使用 pinctrl 方式来驱动 ,pin control subsystem 会 :
1. 枚举所有可用的pin 脚 ,于是每个引脚就有的唯一的 ID (num) ,这个ID 很关键,对于以后的操作。
enum { PINMUX_RESERVED = 0, PINMUX_DATA_BEGIN, GP_ALL(DATA), /* add GP_0_1_DATA ,GP_0_1_DATA..... */ PINMUX_DATA_END, #define F_(x, y) ..... }
2.管理 这些Pin脚的, 由于pin 可以复用 比如 SPI 和GPIO 复用一个pin脚 如: GP2_08 / MISO,于是引申出 pin group 和 pin functon 两个概念:
i2c2_pins: i2c2 { groups = "i2c2_a"; function = "i2c2"; };
在dts 中如上所示 ,其中 i2c2_a 在 pinctl 源码中 如下所示,i2c的两根引脚使用 GP5_0 ,GP5_4.
static const unsigned int i2c2_a_pins[] = { /* SDA, SCL */ GP_PIN(5, 0),GP_PIN(5, 4), };
function 如下 :
struct sh_pfc_function { const char *name; const char * const *groups; unsigned int nr_groups; }; #define SH_PFC_FUNCTION(n = i2c2) \ { .name = #n, .groups = n##_groups, .nr_groups = ARRAY_SIZE(n##_groups), }
好了 ,基本概念就先写这么多 。
内核层配置 GPIO
在 写内核驱动的时候 如果希望配置某个GPIO引脚 , 可以在dts中 添加: pwd-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
在 driver 代码中 , 需要包含 #include <linux/gpio.h>使用 :
gpio_id = of_get_named_gpio(your_driver->dev->of_node,"pwd-gpios", 0);
获取 GP0_07的 ID号 ,然后申请 一个GPIO 操作对象。
if (gpio_is_valid(gpio_id)) //判断一个IO是否合法
devm_gpio_request_one(&platform_device->dev, gpio_id,GPIOF_OUT_INIT_LOW, name);
设置GPIO的方向,如果是输出同时设置电平:
/* set as input or output, returning 0 or negative errno */
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
获取输入引脚的电平:
/* GPIO INPUT: return zero or nonzero */
int gpio_get_value(unsigned gpio);
设置输出:
void gpio_set_value(unsigned gpio, int value);
释放申请的GPIO对象
void gpio_free(unsigned gpio);
将GPIO映射为IRQ中断:
/* map GPIO numbers to IRQ numbers */
int gpio_to_irq(unsigned gpio);
/* map IRQ numbers to GPIO numbers (avoid using this) */
int irq_to_gpio(unsigned irq);
设置GPIO IRQ中断类型:
set_irq_type(gpio_to_irq( gpio), IRQ_TYPE_EDGE_FALLING);
用户层配置 GPIO
需要确认 内核 menuconfig 中 Device Drivers -》 GPIO Support 设 y .
在 /sys/class/gpio 目录下如下所示 :
[email protected]:/sys/class/gpio# ls export gpiochip361 gpiochip419 gpiochip453 gpiochip496 gpiochip357 gpiochip393 gpiochip437 gpiochip468 unexport
gpiochip 后面的数字 是每个bank 的基地址 也就是 GP0 对应 gpiochip468
那么 GP0_07 的 ID 就是 468 + 7 ;(可能不同的内核版本,或者 开发板不一样 )通过 :
[email protected]:/sys/class/gpio# echo 475 > export
会对应生成 gpio475 目录
[email protected]:/sys/class/gpio/gpio475# ls active_low device direction edge power subsystem uevent value
通过
[email protected]:/sys/class/gpio/gpio475# echo 1 >value [email protected]-x:/sys/class/gpio/gpio475# echo 0 >value [email protected]-x:/sys/class/gpio/gpio475# echo "in" > direction [email protected]-x:/sys/class/gpio/gpio475# echo "out" > direction
来控制输出输入 ,电平高低 。
最后,关于 Pinctrl 的内容还有太多要写,就先写到这把。。。。。
来不及写 参考文档了,具体是 结合 wowotech 和 公司的设备,要下班了。。。