Linux驱动之 pinctrl和GPIO子系统
Posted bobuddy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux驱动之 pinctrl和GPIO子系统相关的知识,希望对你有一定的参考价值。
pinctrl 主要是用来进行pin脚的初始化
2、Linux pinctrl 子系统提供的功能是什么
(1) 管理系统中所有可以控制的pin, 在系统初始化的时候,枚举所有可以控制的pin,并标识这些pin枚举所有可用的pin 脚 ,于是每个引脚就有的唯一的 ID (num) ,这个ID 很关键,对于以后的操作。
(2) 管理这些pin的复用(Multiplexing)。 对于SOC来说,其引脚除了配置成普通的GPIO之外,若干个引脚还可组成一个pin group,形成特定的功能。
(3) 配置这些pin的特性。例如使能或关闭引脚上的pull-up、pull-down电阻,配置引脚的driver strength 主要的功能, 1、设置引脚功能的复用 2、 配置pin脚的状态。 也就是对引脚进行初始化
GPIO子系统结构
pinctrl 子系统结构
pinctrl 子系统结构 与 GPIO子系统的结构和功能类似, 但是内部结构有所差异
pinctrl 子系统与GPIO的关系
在引入设备树之后,GPIO子系统是通过pinctrl子系统来实现的,这一点要牢记。
pinctrl 内部结构图
pinctrl的内部主要分为两部分功能:pin config管脚配置和pin mux管脚复用
驱动调用pin脚(不仅指GPIO,例如uart也需要用到pin脚),那么需要用到两部分硬件的功能:
A: 设置引脚功能的复用;
B:配置pin脚的状态。
3、pinctl 中的几个概念
与pinctl相关的设备树文件为exynos4412-pinctrl.dtsi 以下的操作均在该文件中
3.1 pin bank
以引脚名为依据,这些引脚分为若干个组,每组称为一个bank
例如 GPA 是一个bank
每个bank中有若干个引脚例如 GPA0、GPA1 等等例如下面就被分为了11个bank
Port A(GPA) : 25-output port
Port B(GPB) : 9-input/output port
Port C(GPC) : 16-input/output port
Port D(GPD) : 16-input/output port
Port E(GPE) : 16-input/output port
Port F(GPF) : 8-input/output port
Port G(GPG) : 8-input/output port
Port H(GPH) : 15-input/output port
Port K(GPK) : 16-input/output port
Port L(GPL) : 7-input/output port
Port M(GPM) : 2-input port
之所以分成bank,主要是把特性相同的GPIO进行分组,方便控制。例如:这些bank中,只有GPF和GPG这两个bank上的引脚有中断功能,其他的都没有。
BANK属性 描述 实例
gpio-controller 说明该节点为 GPIO controller GPIO控制
interrupt-controller 说明该节点为 interrupt controller 中断控制
#gpio-cells 属性是一个GPIO controller的必须定义的属性,它描述了需要多少个cell来具体描述一个GPIO(这是和具体的GPIO controller相关的)。
#interrupt-cells 和gpio-cells 的概念类似
cell表示一个无符号的32位整数,xxxx-cells指定用多少个cell描述xxxx属性。
gpio-controller;
#gpio-cells = <2>;
“gpio-controller”表示这个节点是一个GPIO Controller,它下面有很多引脚。
“#gpio-cells = <2>”表示这个控制器下每一个引脚要用2个32位的数(cell)来描述。
为什么要用2个数?其实使用多个cell来描述一个引脚,这是GPIO Controller自己决定的。比如可以用其中一个cell来表示那是哪一个引脚,用另一个cell来表示它是高电平有效还是低电平有效,甚至还可以用更多的cell来示其他特性。
普遍的用法是,用第1个cell来表示哪一个引脚,用第2个cell来表示有效电平:
GPIO_ACTIVE_HIGH :高电平有效
GPIO_ACTIVE_LOW : 低电平有效
phandle(linux,phandle这个属性和phandle是一样的,只不过linux,phandle是old-style,多定义一个属性是为了兼容)定义了一个句柄,当其他的device node想要引用这个node的时候就可以使用该句柄。具体的例子参考下节client device的DTS的描述。
pin bank 的例子
gpa0: gpa0
gpio-controller; // 表明该节点为GPIO控制
#gpio-cells = <2>; // 槽为2 这里是根据文档获得的
//#interrupt-cells – 与#address-cells 和 #size-cells相似,它表明连接此中断控制器的设备的interrupts属性的cell大小。 #address-cells 表示子节点的reg address属性的大小 如果为1 那么代表只有一个address
interrupt-controller; // 中断控制
#interrupt-cells = <2>;
;
gpj0: gpj0 gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>; ;
3.2 pin group
以功能为依据,具有相同功能的引脚称为一个Group
比如4412的串口 TxD、RxD 引脚使用 GPA0_0, GPA0_1 那么这2个引脚可以列一组
属性名称 功能 实例
samsung,pins 用来引用管脚 [pin bank name]-[pin number within the bank]
samsung,pin-function 功能复用 设置功能 samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
samsung,pin-val 设置输出缓冲区的初始值 samsung,pin-val = <1> 例如使用LED时,输出1 高电平
samsung,pin-pud 上拉/下拉配置 samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
samsung,pin-drv 驱动器强度配置 samsung,pin-drv = <EXYNOS4_PIN_DRV_LV1>;
samsung,pin-pud-pdn 掉电模式下的上拉/下拉配置
samsung,pin-drv-pdn 电源关闭模式下的驱动器强度配置
/*pin group*/
uart0_data: uart0-data
samsung,pins = "gpa0-0", "gpa0-1"; //引用管脚 [pin bank name]-[pin number within the bank]
samsung,pin-function = <EXYNOS_PIN_FUNC_2>; // 设置功能复用模式
samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>; // pud 上拉下拉配置
samsung,pin-drv = <EXYNOS4_PIN_DRV_LV1>; // 驱动强度为LV1
;
这里的宏定义EXYNOS_PIN_FUNC_2 定义在了 include/dt-bindings/pinctrl/samsung.h中
/*
* Samsung's Exynos pinctrl bindings
*
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
* http://www.samsung.com
* Author: Krzysztof Kozlowski <krzk@kernel.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __DT_BINDINGS_PINCTRL_SAMSUNG_H__
#define __DT_BINDINGS_PINCTRL_SAMSUNG_H__
#define EXYNOS_PIN_PULL_NONE 0
#define EXYNOS_PIN_PULL_DOWN 1
#define EXYNOS_PIN_PULL_UP 3
#define S3C64XX_PIN_PULL_NONE 0
#define S3C64XX_PIN_PULL_DOWN 1
#define S3C64XX_PIN_PULL_UP 2
/* Pin function in power down mode */
#define EXYNOS_PIN_PDN_OUT0 0
#define EXYNOS_PIN_PDN_OUT1 1
#define EXYNOS_PIN_PDN_INPUT 2
#define EXYNOS_PIN_PDN_PREV 3
#define EXYNOS_PIN_FUNC_INPUT 0 //输入
#define EXYNOS_PIN_FUNC_OUTPUT 1 //输出
#define EXYNOS_PIN_FUNC_2 2
#define EXYNOS_PIN_FUNC_3 3
#define EXYNOS_PIN_FUNC_4 4
#define EXYNOS_PIN_FUNC_5 5
#define EXYNOS_PIN_FUNC_6 6
#define EXYNOS_PIN_FUNC_EINT 0xf
#define EXYNOS_PIN_FUNC_F EXYNOS_PIN_FUNC_EINT
#endif /* __DT_BINDINGS_PINCTRL_SAMSUNG_H__ */
上面的FUNC 就是用来设置GPx_CON寄存器的
例如上面的pin group 使用的GPA0_0 、 GPA0_1 这个是串口的引脚,所以使用 FUNC_2 0x2 代表串口的RxD、TxD
3.3 State:
设备的某种状态, 比如内核自己定义的"default",“init”,“idel”,"sleep"状态;
也可以是其他自己定义的状态, 比如串口的"flow_ctrl"状态(使用流量控制);
设备处于某种状态时, 它可以使用若干个Group引脚。
这个是在设备树节点下使用的 , 上面的两个都是定义在了pinctrl节点中
serial@50000000 undefined
…
pinctrl-names = “default”, “sleep”; /* 既是名字, 也称为state(状态) */
pinctrl-0 = <&uart0_data>;
pinctrl-1 = <&uart0_sleep>;
;
pinctrl-names中定义了2种state: default 和 sleep,
default 对应的引脚是: pinctrl-0, 它指定了使用哪些pin group: uart0_data
sleep 对应的引脚是: pinctrl-1, 它指定了使用哪些pin group: uart0_sleep
实例 添加LED pinctrl节点
因为使用LED需要用到GPL2 寄存器,而GPL2 BANK是定义已经定义好的,所以我们直接在pin group中使用即可
exynos4412-pinctrl-dtsi 文件
/*在pinctrl_1节点中*/
/* 因为gpl2定义在了pinctrl_1 节点里面*/
/*添加自定义的LED pinctrl节点*/
/*pin bank*/
pinctrl_1: pinctrl@11000000
gpl1: gpl1
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
;
/*pin group*/
my_led: my_led
samsung,pins = "gpl2-0"; //引用GPL2_0
samsung,pin-function = <EXYNOS_PIN_FUNC_OUTPUT>; // 设置为输出模式
samsung,pin-val = <0x1>; //初始值输出高电平
;
;
在设备树节点中,使用pin group 节点
/* 添加自定义节点*/
myled:myled
#address-cells = <1>;
#size-cells = <1>;
compatible = "myledxxx"; //描述
// reg = <0x11000100 0x4 0x11000104 0x1>; //GPL2CON GPL2DAT
status = "okay";
pinctrl-names = "default"; /* 即是名字也是 状态status*/
pinctrl-0 = <&my_led>; /* 当使用default状态时,就会使用 所引用节点的 配置*/
/*这里的配置主要是将gpl2_0 引脚设置为GPIO*/
/*管脚的描述信息 主要是*/
gpios = <&gpl2 0 GPIO_ACTIVE_HIGH>; //GPL2_0 GPIO_ACTIVE_HIGH 表示高电平有效
;
参数 功能
pinctrl-names 是names 配置的名字,也是状态, 在driver中使用该参数中的 名字 就可以引用相应的配置对管脚进行初始化
pinctrl-0 当pinctrl-names的参数只有一个的时候,例如pinctrl-names = “default”; 在driver中 使用"default就会使用这个配置",
pinctrl-1 当pinctrl-names 的参数有两个的时候,pinctrl-names = “default”, “led_on”; 在driver中 使用"led_on"就会使用这个配置
4、pinctrl 的API
struct pinctrl 结构体 每个设备引脚控制状态
#include <linux/pinctrl/consumer.h>
struct pinctrl
struct list_head node; //全局列表节点
struct device *dev; //使用这个pin控制的设备
struct list_head states;//这个设备的状态列表
struct pinctrl_state *state;//当前状态
struct list_head dt_maps; //:从设备树动态解析的映射表块
struct kref users; //引用计数
;
struct pinctrl_state 设备的pinctrl状态
struct pinctrl_state
struct list_head node; //states字段的列表节点
const char *name; //该状态的名称
struct list_head settings; //这个状态的设置列表
;
1、 获取pinctrl 句柄
#include <linux/pinctrl/consumer.h>
struct pinctrl *devm_pinctrl_get(struct device *dev) ;
//返回值 成功返回pinctrl句柄 失败返回NULL
参数是dev是包含这个pin的device结构体即xxx这个设备的device
devm机制
内核自动分配内存,回收内存
2、获取引脚状态
#include <linux/pinctrl/consumer.h>
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name) ;
// 返回值 成功返回pinctrl 状态 失败返回NULL
参数 功能
p pinctrl句柄
name 配置的名字,用于pinctrl检索
3、设置引脚状态
#include <linux/pinctrl/consumer.h>
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) ;
// 成功返回 0 失败返回错误码
参数 功能
p pinctrl句柄
state 要设置的引脚状态
4、回收pinctrl句柄资源
void devm_pinctrl_put(struct pinctrl *p);
5、实例
这里以LED为例
1、在pinctrl.dtsi 中添加pinctrl group节点
/*添加自定义的LED pinctrl节点*/
/* 在pinctrl_1 中 ,因为 gpl2 在pinctrl_1中*/
my_led_on: my_led-on
samsung,pins = "gpl2-0"; //引用GPL2_0
samsung,pin-function = <EXYNOS_PIN_FUNC_OUTPUT>; // 设置为输出模式
samsung,pin-val = <0x1>; //初始值输出高电平
samsung,pin-pud = <EXYNOS_PIN_PULL_UP>;
;
my_led_off: my_led-off
samsung,pins = "gpl2-0"; //引用GPL2_0
samsung,pin-function = <EXYNOS_PIN_FUNC_OUTPUT>; // 设置为输出模式
samsung,pin-val = <0x0>; //初始值输出低电平
samsung,pin-pud = <EXYNOS_PIN_PULL_UP>;
;
2、在 dts中添加节点
myled:myled
compatible = "myledxxx"; //描述
status = "okay";
pinctrl-names = "default", "my_led_off"; /* 即是名字也是 状态status*/
pinctrl-0 = <&my_led_on>; /* 当使用default状态时,就会使用所引用节点的配置*/
pinctrl-1 = <&my_led_off>;
;
3、driver的probe函数中使用pinctrl API 进行pin的初始化
int ledprobe(struct platform_device *pdev)
struct pinctrl * led_pinctrl;
struct pinctrl_state * led_pinctrl_state;
int ret;
/*1、获取pinctrl句柄*/
led_pinctrl = devm_pinctrl_get(&pdev->dev);
if (NULL == led_pinctrl)
printk("devm pinctrl get is error!\\n");
return -1;
/*2、获取 指定的name的 state*/
/* pinctrl-name = "default, led_off"; 即是名字也是 状态status*/
led_pinctrl_state = pinctrl_lookup_state(led_pinctrl, "default"); //获取pinctrl-0的配置状态
if (NULL == led_pinctrl_state)
printk("pinctrl lookup state is error!\\n");
return -1;
/*3、设置引脚状态*/
ret = pinctrl_select_state(led_pinctrl, led_pinctrl_state);
if (0 != ret)
printk("pinctrl select state is error!\\n");
return -1;
/*4、 句柄销毁*/
// devm_pinctrl_put(led_pinctrl); //
return 0;
划重点:
对于probe函数中设置pinctrl,如果说在设备树中 pinctrl-name = “default”,“init”,“idel”,“sleep” 那么我们在设备树中就不需要调用pinctrl的相关API,内核会自动帮我们设置
pinctrl子系统总结
pin bank 用来设置引脚的功能 例如 GPIO功能
pin group 主要是用来设置pin的复用
pinctrl 子系统主要是用来对管脚进行配置和复用初始化 设置引脚的复用关系和电气属性
真正操作GPIO 还需要使用GPIO子系统来完成,在引入了设备树的内核中,GPIO子系统是由pinctrl子系统来实现的
16、GPIO子系统
上一节我们学习了pinctrl子系统(主要是用来对管脚进行配置和复用初始化 复用关系和电气属性的初始化),linux内核提供了pinctrl子系统和gpio子系统用于GPIO驱动,
当我们在pin bank中使用了gpio-controller的时候,我们就可以使用GPIO子系统来操作管脚了,也就是说pinctrl子系统初始化的时候把引脚设置为gpio的时候,那么就可以使用GPIO子系统来操作管脚
当然,pinctrl子系统负责的不仅仅是GPIO子系统负责的就不仅仅是GPIO的驱动,而是所有pin脚的配置
pinctrl子系统是随着设备树的加入而加入的,依赖于设备树。GPIO子系统在之前的内核中也是存在的,但是pinctrl子系统的加入GPIO子系统也有很大的改变。
在以前的内核版本中,如果要配置GPIO的话,一般要使用SOC厂家实现的配置函数,例如三星的配置函数s3c_gpio_cfgpin等,这样带来的问题就是各家有各家的接口函数,与实现方法,但是内核的代码复用率低而且开发者很难记住这么多函数,如果使用多种平台的话背函数是很麻烦的,所以在引入了设备树后对GPIO子系统进行了很大的改造,使用设备树来实现,并提供统一的接口。通过GPIO子系统功能主要实现引脚功能的配置,如设置为GPIO特殊功能,GPIO的方向,设置为中断等等。
probe代码中如何引用pinctrl
这是透明的,我们的驱动基本不用管。当设备切换状态时,对应的pinctrl就会被调用。
比如在platform_device和platform_driver的枚举过程中,流程如下:
这个经过实验,发现即使init 和 default同时存在,还是会优先使用default状态的引脚
1、设备树使用pinctrl和GPIO子系统描述一个GPIO
pinctrl.dtsi
pinctrl_1: pinctrl@11000000
gpl2: gpl2
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
;
my_led_on: my_led-on
samsung,pins = "gpl2-0"; //引用GPL2_0
samsung,pin-function = <EXYNOS_PIN_FUNC_OUTPUT>; // 设置为输出模式
samsung,pin-val = <0x1>; //初始值输出高电平
samsung,pin-pud = <EXYNOS_PIN_PULL_UP>; //电气属性, 上拉/下拉
;
my_led_off: my_led-off
samsung,pins = "gpl2-0"; //引用GPL2_0
samsung,pin-function = <EXYNOS_PIN_FUNC_OUTPUT>; // 设置为输出模式
samsung,pin-val = <0x0>; //初始值输出低电平
samsung,pin-pud = <EXYNOS_PIN_PULL_UP>;
;
;
在 pin Bank中我们只关心两个属性
gpio-controller;
#gpio-cells = <2>;
“gpio-controller”表示这个节点是一个GPIO Controller,它下面有很多引脚。
“#gpio-cells = <2>”表示这个控制器下每一个引脚要用2个32位的数(cell)来描述。
为什么要用2个数?其实使用多个cell来描述一个引脚,这是GPIO Controller自己决定的。比如可以用其中一个cell来表示那是哪一个引脚,用另一个cell来表示它是高电平有效还是低电平有效,甚至还可以用更多的cell来示其他特性。
普遍的用法是,用第1个cell来表示哪一个引脚,用第2个cell来表示有效电平:
GPIO_ACTIVE_HIGH : 高电平有效
GPIO_ACTIVE_LOW : 低电平有效
pin group 中 设置复用关系和电气属性
my_led_on: my_led-on
samsung,pins = "gpl2-0"; //引用GPL2_0
samsung,pin-function = <EXYNOS_PIN_FUNC_OUTPUT>; // 设置为输出模式
samsung,pin-val = <0x1>; //初始值输出高电平
samsung,pin-pud = <EXYNOS_PIN_PULL_UP>; //电气属性, 上拉/下拉
;
定义GPIO Controller是芯片厂家的事,我们怎么引用某个引脚呢?在自己的设备节点中使用属性" [ name- ]gpios",示例如下:
exynos4412-itop-elit.dts
myled:myled
compatible = "myledxxx"; //描述
reg = <0x11000100 0x4 0x11000104 0x1>; //GPL2CON GPL2DAT
status = "okay";
/*pinctrl*/
pinctrl-names = "default", "my_led_off"; /* 即是名字也是 状态status*/
pinctrl-0 = <&my_led_on>; /* 当使用default状态时,就会使用所引用节点的配置*/
pinctrl-1 = <&my_led_off>;
/*GPIO*/
led-gpios = <&gpl2 0 GPIO_ACTIVE_HIGH>; //GPL2_0 GPIO_ACTIVE_HIGH 表示高电平有效
//gpios = <&gpl2 0 GPIO_ACTIVE_HIGH>;
;
上图中,可以使用gpios属性,也可以使用name-gpios属性。
2、GPIO子系统提供的API函数
1、of_get_named_gpio 函数 获取GPIO标号
作用:此函数获取 GPIO 编号,因为Linux内核中关于GPIO的API函数都要使用GPIO编号,此函数会将设备数中类似 <&gpl2 0 GPIO_ACTIVE_HIGH> 的属性信息转换为对应的GPIO编号
#include<linux/of_gpio.h>
int of_get_named_gpio(struct device_node *np,const char *propname, int index);
// 返回值 成功返回 GPIO编号, 失败返回 负数
参数 描述
np 设备节点
propname 包含要获取GPIO信息的属性名
index 因为一个属性里面可能包含多个GPIO,此参数指定要获取哪个GPIO的编号,如果只有一个GPIO信息的话此参数为0
2、gpio_request 函数
作用:用于申请一个GPIO管脚
#include<linux/gpio.h>
int gpio_request(unsigned gpio, const char *label);
// 返回值 成功 0, 失败返回 非0
参数 描述
gpio 要申请的gpio标号,使用of_get_named_gpio函数从设备树中获取指定GPIO属性信息,此函数会返回这个GPIO的标号
label 给这个gpio标号 起个名字 最好起与硬件相关的名字
3、gpio_free 函数
作用: 如果不使用某个GPIO了,那么就可以调用gpio_free函数进行释放
#include<linux/gpio.h>
void gpio_free(unsigned gpio);
参数 描述
gpio 要释放的gpio号
4、gpio_direction_input 函数
作用: 此函数用于设置某个GPIO为输入
#include <linux/gpio.h>
int gpio_direction_input(unsigned gpio);
//返回值 成功返回0 , 失败返回负数
参数 描述
gpio 要设置为输入的gpio的标号
5、gpio_direction_output 函数
作用:此函数用于设置某个GPIO为输出, 并且设置默认输出
#include <linux/gpio.h>
int gpio_direction_output(unsigned gpio, int value);
//返回值 成功返回0 ,失败返回 负数
参数 描述
gpio 要设置为输出的GPIO标号
value GPIO 默认输出值。 例如 1 默认输出高电平, 0 输出低电平
6、gpio_get_value 函数
作用: 此函数用于获取某个GPIO的值(0 或 1)
#include <linux/gpio.h>
int gpio_get_value(unsigned gpio);
//返回值, 成功返回GPIO的值, 失败返回 负数
参数 描述
gpio 要获取的GPIO标号
7、gpio_set_value 函数
#include<linux/gpio.h>
void gpio_set_value(unsigned gpio, int value);
参数 描述
gpio 要设置的GPIO标号
value 要设置的值
3、实例
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/pinctrl/consumer.h>
#include <dt-bindings/pinctrl/samsung.h>
#include <linux/of_gpio.h>
/* 字符设备框架
1、注册 platform driver
2、构建file_operations
3、获取硬件资源
4、注册字符设备驱动
4、生成字符设备节点
*/
struct device *device_cdev;
struct class *class_cdev;
int GPIO_ID;
ssize_t led_read (struct file *file, char __user * user, size_t size, loff_t * loff )
printk("read is success!\\n");
return 0;
ssize_t led_write (struct file *file, const char __user *user, size_t size, loff_t * loff)
char buf[128] = 0;
int ret ;
printk("write is success!\\n");
ret = copy_from_user(buf, user, size);
if (0 != ret)
printk("copy form user is error!\\n");
return ret;
if (buf[0] == 1 || buf[0] == 0)
gpio_set_value(GPIO_ID, buf[0]);
return 0;
int led_open(struct inode *inode, struct file *file)
printk("open is success!\\n");
return 0;
int led_release (struct inode *inode, struct file *file)
printk("release is success!\\n");
return 0;
/*2、获取硬件资源 */
int ledprobe(struct platform_device *pdev)
int ret;
/*1、获取GPIO号*/
GPIO_ID = of_get_named_gpio(pdev->dev.of_node, "led-gpios", 0);//index=0 ,因为在设备树中只引用了一个
if (GPIO_ID < 0)
printk("of get named gpio is error!\\n");
return -1;
/*2、申请一个GPIO管脚*/
ret = gpio_request(GPIO_ID, "led_gpio");
if (ret != 0)
printk("gpio request is error!\\n");
return -1;
/*3、 将管脚设置为输出*/
/* 这里先不设置,因为在pinctrl复用中已经将管脚设置为了OUTPUT*/
return 0;
int ledremove(struct platform_device *pdev)
return 0;
struct of_device_id of_match_table = // 与设备树节点进行匹配
.compatible = "myledxxx"
;
/*1、初始化platform driver*/
struct platform_driver pdev =
.probe = ledprobe, // 与 of_match_table 匹配成功后进入probe函数获取硬件资源
.remove = ledremove,
.driver =
.name = "myledxxx", //无设备树时 使用.name 与device进行匹配
.owner = THIS_MODULE,
.of_match_table = &of_match_table,
;
//3、注册字符设备驱动
/*3.1 分配设备号*/
dev_t dev_number;
/*3.2 定义cdev*/
struct cdev cdev_;
/*3.3 构建file_operation结构体*/
struct file_operations fop =
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.write = led_write,
.read = led_read,
;
static int char_driver_init(void)
/*1、注册platform driver*/
int ret = platform_driver_register(&pdev);
if (0 != ret)
printk("platform driver register is error!\\n");
return -1;
/*3.1 分配设备号(动态分配设备号)*/
ret = alloc_chrdev_region(&dev_number, 0, 1, "my_led");
if (0 != ret)
printk("alloc chrdev region is error!\\n");
return ret;
/*3.4 初始化cdev*/
cdev_.owner = THIS_MODULE;
cdev_init(&cdev_, &fop);
/*3.5 注册字符设备到内核*/
ret = cdev_add(&cdev_, dev_number, 1);
if (0 != ret)
printk("cdev add is error!\\n");
return -1;
/*4、生成设备节点*/
/*4.1 创建字符设备类*/
class_cdev = class_create(THIS_MODULE, "my_led");
if (NULL == class_cdev)
printk("class create is error!\\n");
return -1;
/*生成设备节点*/
device_cdev = device_create (class_cdev, NULL, dev_number, NULL, "my_led");
if (NULL == device_cdev)
printk("device create is error!\\n");
return 0;
;
static void char_driver_exit(void)
gpio_free(GPIO_ID); //释放GPIO
device_destroy(class_cdev, dev_number); // 卸载设备节点
class_destroy(class_cdev); //卸载设备类
cdev_del(&cdev_); //卸载cdev
unregister_chrdev_region(dev_number, 1);// 注销设备号
platform_driver_unregister(&pdev); // 注销platform driver
module_init(char_driver_init);
module_exit(char_driver_exit);
MODULE_LICENSE("GPL");
————————————————
版权声明:本文为CSDN博主「小袁OVO」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43937576/article/details/116131178
以上是关于Linux驱动之 pinctrl和GPIO子系统的主要内容,如果未能解决你的问题,请参考以下文章
Linux——Linux驱动之设备树中pinctrl和gpio子系统应用实践(如何使用其在设备树中配置GPIO,驱动中如何调用?)