Linux驱动开发:gpio子系统

目录

1、GPIO配置流程

2、GPIO子系统API

2.1 of_find_node_by_path

2.2 of_get_named_gpio

2.3 gpio_request 与 gpiod_get 与 gpiod_get_index

2.4 gpio_direction_input 与 gpiod_direction_input

2.5 gpio_direction_output 与 gpiod_direction_output

2.6 gpio_get_value 与 gpiod_get_value

2.7 gpio_set_value 与 gpiod_set_value

2.8  gpiod_get_from_of_node

2.9 gpio_free 与 gpiod_put

3、GPIO子系统驱动程序

3.1 没有platform总线前怎么得到gpio节点

3.1.1 修改设备树

3.1.2 驱动程序:老版接口

3.1.3 驱动程序:新版接口

3.2 platform总线下得到

3.2.1 修改设备树:配置pinctrl

3.2.2 修改设备树:配置自己的节点

3.2.3 驱动程序


1、GPIO配置流程

        如果要操作GPIO引脚的话,需要先将所用引脚配置为GPIO功能,这需要通过Pinctrl子系统来实现。

        在Pinctrl子系统将引脚配置为GPIO功能后,就可以使用GPIO子系统来设置GPIO的方向等。

        在设备树中,“GPIO组”就是一个GPIO Controller,这通常都由芯片厂家设置好。我们要做的是找到它名字,比如“gpio1”,然后指定要用它里面的哪个引脚,比如<&gpio1 0>。

        我们需要关心里面的这2个属性:

#gpio-controller; 表示这个节点是一个GPIO Controller,它下面有很多引脚。

#gpio-cells = <2>; 表示这个控制器下每一个引脚要用2个32位的数(cell)来描述。

普遍的用法是,用第1个cell来表示哪一个引脚,用第2个cell来表示有效电平:

GPIO_ACTIVE_HIGH : 高电平有效

GPIO_ACTIVE_LOW : 低电平有效

可参考的帮助文档

Linux-4.9.88/Documentation/devicetree/bindings/gpio/gpio.txt 

Linux-4.9.88/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.txt

2、GPIO子系统API

2.1 of_find_node_by_path

struct device_node *of_find_node_by_path(const char *path)
/**/
功能:通过路径或者节点结构体
参数:
    @path:路径
返回值:成功返回节点的首地址,失败返回NULL

2.2 of_get_named_gpio

int of_get_named_gpio(struct device_node *np,const char *propname, int index)
/*
功能:根据节点结构体解析gpio编号
参数:
    @np:节点指针
	@propname:键
    @index:索引号
返回值:成功返回gpio的编号,失败返回错误码
*/

2.3 gpio_request 与 gpiod_get 与 gpiod_get_index

int gpio_request(unsigned gpio, const char *label)
/*
功能:申请要使用的gpio
参数:
    @gpio:gpio的编号
    @label:标签名
返回值:成功返回0,失败返回错误码
*/
//如果是一个GPIO就用gpiod_get,如果有多个就用gpiod_get_index
struct gpio_desc *__must_check gpiod_get(struct device *dev, 
    const char *con_id, enum gpiod_flags flags)

struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
					       const char *con_id,
					       unsigned int idx,
					       enum gpiod_flags flags)
/*
功能:申请要使用的gpio
参数:
    @dev: device结构体,如果有platform,则 &pdev->dev
    @con_id: 键
    @idx: 索引号
    @flags: 0
返回值:成功返回gpio_desc 结构体,失败返回错误码
*/

2.4 gpio_direction_input 与 gpiod_direction_input

int gpio_direction_input(unsigned gpio)
/*
功能:设置gpio的为输入
参数:
    @gpio:gpio的编号
返回值:成功返回0,失败返回错误码
*/
int gpiod_direction_input(struct gpio_desc *desc)
/*
功能:设置gpio为输入
参数:
    @desc:gpio_desc结构体
返回值:成功返回0,失败返回错误码
*/

2.5 gpio_direction_output 与 gpiod_direction_output

int gpio_direction_output(unsigned gpio, int value)
/*
功能:设置gpio的为输出
参数:
    @gpio:gpio的编号
    @value:默认电平的状态 1高电平  0低电平
返回值:成功返回0,失败返回错误码
*/
int gpiod_direction_output(struct gpio_desc *desc, int value)
/*
功能:设置gpio为输出
参数:
    @desc:gpio_desc结构体
返回值:成功返回0,失败返回错误码
*/

2.6 gpio_get_value 与 gpiod_get_value

int gpio_get_value(unsigned gpio)
/*
功能:读取管脚的值
参数:
    @gpio:gpio的编号
返回值:1高电平  0低电平
*/
int gpiod_get_value(const struct gpio_desc *desc)
/*
功能:读取管脚的值
参数:
    @desc: gpio_desc结构体
返回值:1高电平  0低电平
*/

2.7 gpio_set_value 与 gpiod_set_value

void gpio_set_value(unsigned gpio, int value)    
/*
功能:设置输出电平的值
参数:
     @gpio:gpio的编号
	 @value:1高电平  0低电平
返回值:无
*/
void gpiod_set_value(struct gpio_desc *desc, int value)
/*
功能:设置输出电平的值
参数:
    @desc: gpio_desc结构体
    @value: 1高电平  0低电平
返回值:无
*/

2.8  gpiod_get_from_of_node

struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, 
const char *propname, int index, enum gpiod_flags dflags, const char *label)
/*
功能:在设备树节点信息结构体中获取并申请要使用的gpio编号,然后输出
    此函数一个函数就完成了申请加上输出的功能
参数:
    @node:设备树节点信息结构体指针
    @propname:键名
    @index:索引
    @dflags:gpio状态值
        GPIOD_OUT_LOW
        GPIOD_OUT_HIGH
    @label:标签,填写NULL
返回值:成功返回gpio描述结构体指针,失败返回错误码指针
*/

2.9 gpio_free 与 gpiod_put

void gpio_free(unsigned gpio)
/*
功能:释放gpio
参数:
     @gpio:gpio的编号
返回值:无   
*/
void gpiod_put(struct gpio_desc *desc)
/*
功能:释放gpio
参数:
     @desc:gpio_desc结构体
返回值:无   
*/

3、GPIO子系统驱动程序

3.1 没有platform总线前怎么得到gpio节点

3.1.1 修改设备树

设备树文件在

arch/arm/boot/dts/100ask_imx6ull-14x14.dts

 在根节点下写自己的节点

myleds{ 
	led1=<&gpioe 10 0>; 
	led2=<&gpiof 10 0>; 
	led3=<&gpioe 8 0>;  
};

3.1.2 驱动程序:老版接口

struct device_node *node;
int gpiono;
static int __init mycdev_init(void)
{
	//1.获取节点
	node = of_find_node_by_path("/myleds");

	//2.解析得到gpio
	gpiono = of_get_named_gpio(node, "led1", 0);

	//3.申请使用的gpio
	gpio_request(gpio,NULL));

	//4.通过gpio申请设备
	gpio_direction_output(gpiono, 0);

	return 0;
}

static void __exit mycdev_exit(void) 
{
	gpio_set_value(gpiono,0);
	gpio_free(gpiono);
}

3.1.3 驱动程序:新版接口

static int __init mycdev_init(void)
{
	//1.获取节点
	node = of_find_node_by_path("/myleds");
	
    //2、申请GPIO
    //此函数一个函数就完成了申请加上输出的功能
	desc = gpiod_get_from_of_node(node, "led2", 0, GPIO_OUT_HIGH, 0);

	return 0;
}


static void __exit mycdev_exit(void) 
{
	gpiod_set_value(desc,0);
	gpiod_put(desc);
}

3.2 platform总线下得到

3.2.1 修改设备树:配置pinctrl

使用imx图形化界面来配置引脚

//在pinctrl下设置引脚
&iomuxc_snvs

myled_for_gpio_subsys: myled_for_gpio_subsys{       
    fsl,pins = <
        MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03        0x000110A0
    >;
};

3.2.2 修改设备树:配置自己的节点

在根节点下写自己的节点

myled {
    compatible = "100ask, leddrv" ;
    pinctrl-names = "default";
    pinctrl-0 = <&myLed_for_gpio_subsys>;
    led-gpios = <&gpio5 3 GPIO_ACTIVE_HIGH>;
};

3.2.3 驱动程序

static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	gpiod_set_value(led_gpio, status);

}

static struct file_operations led_drv = {
	.write   = led_drv_write,
};

static int chip_demo_gpio_probe(struct platform_device *pdev)
{	    
    led_gpio = gpiod_get(&pdev->dev, "led", 0);
    register_chrdev(0, "100ask_led", &led_drv);
    	
    return 0;
}

static int chip_demo_gpio_remove(struct platform_device *pdev)
{
    unregister_chrdev(major, "100ask_led");
	gpiod_put(led_gpio);
    
    return 0;
}


static const struct of_device_id ask100_leds[] = {
    { .compatible = "100ask,leddrv" },
    { },
};

/* 1. 定义platform_driver */
static struct platform_driver chip_demo_gpio_driver = {
    .probe      = chip_demo_gpio_probe,
    .remove     = chip_demo_gpio_remove,
    .driver     = {
        .name   = "100ask_led",
        .of_match_table = ask100_leds,
    },
};

static int __init led_init(void)
{    
    platform_driver_register(&chip_demo_gpio_driver); 
}

static void __exit led_exit(void)
{
    platform_driver_unregister(&chip_demo_gpio_driver);
}

你可能感兴趣的:(Linux驱动开发,驱动开发,linux,gpio子系统)