linux内核驱动的gpio与pinctrl总结

前言

本文将介绍GPIO子系统与pinctrl子系统的使用方法,并给出相应的设备树配置与驱动框架。日后开发如果忘记了,可以常来看看。

GPIO

设备树

当你想使用gpio驱动外设时(如点灯),你需要熟悉gpio的节点以及如何使用gpio节点。不同的厂商gpio节点的描述方法可能不同,但大体上不会差太多,如下图为全志h3设备树中对gpio节点的描述:

pio: pinctrl@01c20800 {
                        /* compatible is in per SoC .dtsi file */
	reg = <0x01c20800 0x400>; /*起始地址与偏移量*/
	interrupts = 	<GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
					<GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&ccu CLK_BUS_PIO>, <&osc24M>, <&osc32k>;
	clock-names = "apb", "hosc", "losc";
	gpio-controller;/*只有一个节点含有gpio-controller时,才能代表它是一个gpio节点*/
	#gpio-cells = <3>;/*引用gpio节点时,需要调用的参数数量*/
	interrupt-controller;
	#interrupt-cells = <3>;
	......
};

在全志h3的设备树中,当引用gpio节点时,需要三个参数:主引脚号,次引脚号,以及引脚默认状态(高低电平),下面将介绍如何引用gpio引脚,将gpio引脚的熟悉添加到自己创建的节点中。
添加一个led的gpio节点:

my_led{
	compatible = "myled";
	pinctrl-names = "default";
	pinctrl-0 =<&led_pins>;
	led-gpios = <&pio 0 6 GPIO_ACTIVE_LOW>;
	status = "okay";
};

驱动

子系统的核心作用就是提供对上层统一的接口,使用gpio子系统控制外设,可以不关心底层的细节,把驱动和设备分开,当设备修改时,不需要再修改驱动,只需要修改设备树即可。
gpio子系统分为gpiolibgpio chip driver,分别由内核设计者与SOC厂商提供。

第一种方法
#include <linux/gpio/consumer.h>

struct gpio_desc * gpio_pointer;定义一个gpio设备指针

static inline struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id,enum gpiod_flags flags)
__must_check是一个标识符,告诉编译器必须检验返回值。
返回值为gpio子系统的结构体
参数分别是平台设备结构体指针,gpio的标识符,输出模式
枚举类型 gpiod_flags

void gpiod_set_value(struct gpio_desc *desc, int value)设置gpio的值
gpiod_direction_output()设置输出方式
int gpiod_get_value(const struct gpio_desc *desc)读gpio的值
(待补充)
第二种方法

//伪代码
#include <linux/gpio.h>
#include <linux/of_gpio.h>
struct my_gpio_dev{
	dev_t devid;				/* 设备号	*/
	struct cdev cdev;			/* cdev		*/
	struct class *class;		/* 类 		*/
	struct device *device;		/* 设备		*/
	int major;					/* 主设备号	*/	
	struct device_node *node;	/* LED设备节点 */
	int ledgpio;					/* LED灯GPIO标号 */
};
struct my_gpio_dev led = {
	
}
......
......

probe()
{
	...
	
	led_node = of_find_node_by_name(NULL,"my_led");//probe匹配后,获取设备节点
	if(led_node==NULL)
    {
        printk("find node faile!\n");
        return -1;
    }
	ledgpio = of_get_named_gpio(led_node, "led-gpios", 0);
    printk("gpioid=%d\n",ledgpio);
    gpio_request(ledgpio, "led0");/*申请资源*/
    gpio_direction_output(ledgpio,0);/*设置Gpio模式,初始电平值*/
    printk("led probe ok\n");
    
    ...
}
remove()
{
	gpio_set_value(ledgpio,0);
	gpio_free(ledgpio);
}
open/ioctrl/write/...()
{
	gpio_set_value(ledgpio,1);//亮灯
}

pinctrl

在上述例子中,我们已经看见了pinctrl的身影,那么pinctrl是什么呢?如果有学过单片机的朋友肯定清楚,如stm32中不同的引脚可以配置不同的功能来满足需要,设备树中的pinctrl也是用来定义引脚配置的。对于pinctrl的学习,一般分为客户端与服务端。

设备树

  • 客户端
    客户端主要是写在自己创建的节点中,用来引用服务端的内容,格式相对统一,如上述的my_led节点就是pinctrl的客户端。由通常由两个属性组成:pinctrl-namespinctrl-X
    pinctrl-names一般使用default默认值就行
    pinctrl-X中X表示引脚控制器子系统的编号,未级联的引脚控制器一般用0就行。
    引用的&led_pins为服务端内容,我们在下面会介绍。
  • 服务端
    服务端的pinctrl对于每个厂商都有不同的写法,我将以全志h3为例子写一个led_pins的服务端。
//写根节点外面,且要加标签,不加标签没法引用
&pio{
	led_pins:led_pins{
		pins = "PA6";
		function = "gpio_out"; 
	};	
};

不难体会服务端的主要属性就是引脚编号以及引脚模式,即使其他的厂商写法不一,但中心思想都是不会变的。

总结

内容还未完全写完,待修改
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值