Chinaunix首页 | 论坛 | 博客
  • 博客访问: 312452
  • 博文数量: 101
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 774
  • 用 户 组: 普通用户
  • 注册时间: 2018-10-15 14:13
个人简介

搭建一个和linux开发者知识共享和学习的平台

文章分类

全部博文(101)

文章存档

2024年(15)

2023年(24)

2022年(27)

2019年(8)

2018年(27)

分类: 嵌入式

2024-10-31 14:55:07

1. 概述

设备驱动层:定义了与硬件无关的GPIO API,包括GPIO的注册、卸载和控制等功能,而实现了某个模块的具体实现,比如led灯、按键等等。

gpiolib抽象层:GPIO框架中的核心抽象层,它的作用是为设备驱动层和控制器层提供一致的接口,该层提供了包括上层设备驱动和下层控制器驱动的API接口。

控制器层:GPIO控制器的实现和管理,在该层中实现特定GPIO控制器的底层硬件操作和功能实现包括GPIO控制器的初始化、操作和管理等。负责GPIO寄存器的读写操作和GPIO中断的处理等。

其中gpiolib抽象层是GPIO框架中的核心层,也是linux内核自己实现的,一般情况下没有人会修改这部分的代码,控制器层一般是芯片厂家BSP工程师实现的,设备驱动层是驱动工程师根据开发版的实际情况实现的。优秀的BSP工程师和驱动工程师可以把驱动写得与硬件解耦,把硬件的信息填充到设备树中,驱动读取设备树的信息进行各种操作。

2. 控制器

2.1 设备树

gpio0: gpio@28034000 {
compatible = "phytium,gpio";
reg = <0x0 0x28034000="" 0x0="" 0x1000="">;
gpio-controller;
#gpio-cells = <2>;
#address-cells = <1>;
#size-cells = <0>;

porta {
compatible = "phytium,gpio-port";
reg = <0>;
ngpios = <16>;
};
};

gpio3: gpio@28037000 {
compatible = "phytium,gpio";
reg = <0x0 0x28037000="" 0x0="" 0x1000="">;
interrupts = ;
gpio-controller;
#gpio-cells = <2>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";

porta {
compatible = "phytium,gpio-port";
reg = <0>;
ngpios = <16>;
};
};

在设备树中设置GPIO管脚输出值需要以下步骤:

  1. 在设备树中找到相应的GPIO节点,通常在arch/arm/boot/dts目录下的设备树文件中定义。GPIO节点的名称通常以"gpio"开头,后跟相应的管脚编号。
  2. 在GPIO节点的属性中设置GPIO的输出模式和初始值。常见的属性包括"gpio-controller"、"gpio-ranges"、"gpio-hog"和"gpio-line-names"等。
    • "gpio-controller"属性用于标识GPIO控制器的类型,例如"gpio-controller = <&gpio0>"表示使用gpio0作为控制器。
    • "gpio-ranges"属性用于设置GPIO管脚的编号范围。
    • "gpio-hog"属性用于将GPIO管脚设置为默认状态,常用于设备启动时的默认设置。
    • "gpio-line-names"属性用于设置GPIO管脚的名称。
  • 在GPIO节点的子节点中定义具体的管脚配置,包括管脚编号、方向和初始值等。
    • 管脚编号可以通过查阅设备手册或GPIO控制器的文档进行确认。
    • 方向可以设置为"input"或"output",表示管脚的输入或输出模式。
    • 初始值可以设置为"active-high"或"active-low",表示高电平或低电平为有效状态。
  • 保存设备树文件,并编译生成设备树二进制文件(.dtb)。
  • 将设备树二进制文件加载到系统中,可以通过引导加载程序(bootloader)或者运行时加载器进行加载。
    • 在启动时加载设备树,需要在引导加载程序中配置相应的参数。
    • 在运行时加载设备树,可以使用设备树管理工具(devicetree)进行加载。

2.1 驱动的数据结构

几乎所有的gpio控制器驱动做的事情不外乎以下几种:

分配私有数据结构体,这个结构体会包含struct gpio_chip结构体
读取设备树信息,填充gpio_chip结构体
把gpio_chip结构体绑定到平台设备中
实际上gpio的驱动跟中断和pinctrl放在一起的,初始化的时候会把irq_chip和 struct pinctrl_desc 和pinctrl_dev 一起填充的,这里主要讲解gpio,就不多扩展了。我们先看struct gpio_chip结构体:

struct gpio_chip {
const char *label; //gpio控制器的名字
struct gpio_device *gpiodev; //gpio设备描述符
struct device *parent; //gpio的父设备
struct module *owner;

//下面是一系列的操作函数
int (*get_direction)(struct gpio_chip *gc,unsigned int offset);
int (*direction_input)(struct gpio_chip *gc,unsigned int offset);
int  (*direction_output)(struct gpio_chip *gc,unsigned int offset, int value);
int (*get)(struct gpio_chip *gc,unsigned int offset);
void (*set)(struct gpio_chip *gc,unsigned int offset, int value);

int base; //gpio引脚基值
u16 ngpio; //gpio引脚个数
const char *const *names; //每一个引脚的名字
bool can_sleep; //控制器是否能睡眠

void __iomem *reg_dat; //gpio数据寄存器基地址
void __iomem *reg_set; //gpio设置寄存器基地址
void __iomem *reg_clr; //gpio控制寄存器基地址
void __iomem *reg_dir_out; //gpio输出寄存器基地址
void __iomem *reg_dir_in; //gpio输入寄存器基地址
};
gpio_chip结构体中包含了对应GPIO端口的硬件基地址、引脚数、GPIO组数、GPIO编号和IRQ号
等重要信息。同时,它还包括了访问GPIO寄存器的函数指针,例如读取和写入寄存器等函数,
以及描述GPIO的信息和配置的特定标志。其中{BANNED}{BANNED}最佳佳重要的是struct gpio_device *gpiodev; 他表示
gpio设备描述符。这个结构体存放着gpio控制器的设备信息和每个引脚的描述符结构体,我们来
看看struct gpio_device结构体:

struct gpio_device {
int id; //表示这是系统中第几个GPIO控制器
struct device dev;
struct cdev chrdev;
struct device *mockdev;
struct module *owner;
struct gpio_chip *chip; //gpio管理硬件的结构体,记录寄存器信息及其操作函数
struct gpio_desc *descs; //引脚的描述符指针,每一个引脚对应一个gpio_desc结构体
int base; //gpio号码基值
u16 ngpio; //gpio个数
const char *label; //gpio控制器的名字
void *data;
struct list_head        list;
struct blocking_notifier_head notifier;
};

gpio_device结构体中包含了与GPIO设备(GPIO控制器)相关的重要信息,例如GPIO控制器ID、
GPIO号、GPIO个数、GPIO管理硬件的结构体和gpio引脚描述符结构体。{BANNED}{BANNED}最佳佳重要的是GPIO管理
硬件的结构体gpio_chip 就是刚刚{BANNED}中国{BANNED}中国第一个介绍的数据结构,gpio设备是通过gpio_chip 找到对用
的硬件信息和操作方法的;其次是gpio_desc结构体,每一个引脚对应一个gpio_desc结构体,
他们通过数组的形式排列,我们看看gpio_desc结构体:

struct gpio_desc {
struct gpio_device *gdev; //属于哪个GPIO控制器
unsigned long flags; //gpio引脚属性,比如是否被使用、是否开漏等等

/* Connection label */
const char *label;
/* Name of the GPIO */
const char *name; //引脚名字
#ifdef CONFIG_OF_DYNAMIC
struct device_node *hog;
#endif
#ifdef CONFIG_GPIO_CDEV
/* debounce period in microseconds */
unsigned int debounce_period_us;
#endif
};

以上这3个结构体就包含了gpio控制器的硬件信息、状态信息和操作方法集合,这就我们的设备
驱动提供了底层的基础,我们的驱动就是通过gpiolib抽象层提供的API调用到这个操作方法的。

3. 设备驱动

在控制器驱动准备好的情况下,我们使用gpio是一件很简单的事情,举个例子,先看设备树:
mio14: i2c@28030000 {
...
codec0:es8336@10 {
det-gpios = <&gpio2 5 0>;
sel-gpios = <&gpio2 6 0>;
...
   };
};

其中gpio的设备树就两行,其他不重要的就忽略了。

es8336->spk_ctl_gpio = devm_gpiod_get_index_optional(&i2c->dev, "sel", 0,GPIOD_OUT_HIGH);
ret = of_property_read_u8(i2c->dev.of_node, "mic-src", &es8336->mic_src);
if (ret != 0) {
    dev_dbg(&i2c->dev, "mic1-src return %d", ret);
    es8336->mic_src = 0x20;
}
dev_dbg(&i2c->dev, "mic1-src %x", es8336->mic_src);

if (!es8336->spk_ctl_gpio)
    dev_info(&i2c->dev, "Can not get spk_ctl_gpio\n");
else
    es8336_enable_spk(es8336, false);

es8336->hp_det_gpio = devm_gpiod_get_index_optional(&i2c->dev, "det", 0,GPIOD_IN);

if (!es8336->hp_det_gpio) {
    dev_info(&i2c->dev, "Can not get hp_det_gpio\n");
} else {
INIT_DELAYED_WORK(&es8336->work, hp_work);
hp_irq = gpiod_to_irq(es8336->hp_det_gpio);
ret = devm_request_threaded_irq(&i2c->dev, hp_irq, NULL,
es8336_irq_handler,
IRQF_TRIGGER_FALLING |
IRQF_TRIGGER_RISING |
IRQF_ONESHOT,
"es8336_interrupt", es8336);
if (ret < 0) {
    dev_err(&i2c->dev, "request_irq failed: %d\n", ret);
    return ret;
}
}

其实设备驱动都是使用gpiolib提供的API:

gpiod_get_indexed:通过索引号获取GPIO设备引脚。
gpiod_get_optional:尝试获取GPIO设备引脚,如果失败返回NULL,不会导致注册失败。
gpiod_get_optional_indexed:与gpiod_get_optional类似,但通过索引号获取GPIO设备引脚。
gpiod_get_raw:通过GPIO编号获取GPIO设备引脚,不进行方向和值的配置。
devm_gpiod_get:这个函数自动为一个特定的设备申请所需的GPIO,不需要手动释放,适合临时使用的GPIO资源。
devm_gpiod_get_index:这个函数允许为使用多个GPIO的设备申请多个GPIO引脚,返回一个struct gpiod_hanlde数组。
devm_gpiod_get_optional:如果存在,允许驱动程序获取所需的GPIO,而不会阻止设备与其余的GPIO资源一起初始化。
devm_gpiod_get_optional_index:类似于devm_gpiod_get_optional,支持索引GPIO。
gpio_request:向内核申请一个GPIO引脚。
gpio_free:释放一个已经使用的GPIO引脚。
gpio_direction_input:配置GPIO引脚为输入模式。
gpio_direction_output:配置GPIO引脚为输出模式。
gpio_get_value:读取GPIO引脚状态。如果 GPIO 引脚已配置为输出模式,则返回当前输出值。如果 GPIO 引脚未配置或配置为输入模式,则返回实际引脚上的输入值。
gpio_set_value:设置GPIO引脚状态为高或低电平。如果 GPIO 引脚已配置为输出模式,则设置GPIO引脚状态为用户指定电平;如果 GPIO 引脚是输入模式,此函数没有作用。
gpio_to_irq:将 GPIO 引脚转换为专用中断号。
gpio_request_one:请求单个GPIO。
gpio_free_array:释放一组由gpio_request_array()调用请求的GPIO。
gpio_direction_input_array:将一组GPIO方向设置为输入模式。
gpio_direction_output_array:将一组GPIO方向设置为输出模式。
gpio_get_array:将一组GPIO值读入缓冲区中。
gpio_set_array:将一组GPIO指定的值写入用户指定的缓冲区中。
gpio_get_value_cansleep:读取GPIO引脚状态,如果引脚已配置为输出模式,则将与其关联的电平值复制到调用函数的参数变量中,如果引脚已配置为输入模式,则等待GPIO中断或超时发生后将其值复制到调用函数的参数变量中。
gpio_set_value_cansleep:设置GPIO端口电平,如果引脚仍被配置为输入模式,则什么也不做。

4. gpio复用


使用 struct pinctrl_desc 抽象一个 pin controller,该结构的定义如下:
struct pinctrl_desc {
 const char *name;
 const struct pinctrl_pin_desc *pins;
 unsigned int npins;
 const struct pinctrl_ops *pctlops;
 const struct pinmux_ops *pmxops;
 const struct pinconf_ops *confops;
 struct module *owner;
#ifdef CONFIG_GENERIC_PINCONF
 unsigned int num_custom_params;
 const struct pinconf_generic_params *custom_params;
 const struct pin_config_item *custom_conf_items;
#endif
};

  • pins

变量 pins 和 npins 把系统中所有的 pin 描述出来,并建立索引。驱动为了和具体的 pin 对应上,再将这些描述的这些 pin 组织成一个 struct pinctrl_pin_desc 类型的数组,该类型的定义为:

struct pinctrl_pin_desc {
 unsigned number;
 const char *name;
 void *drv_data;
};

  • pin groups

SoC中,有时需要将很多 pin 组合在一起,以实现特定的功能,例如 uart 接口、i2c 接口等。因此 pin controller 需要以 group 为单位,访问、控制多个 pin,这就是 pin groups。

struct group_desc {
 const char *name;
 int *pins;
 int num_pins;
 void *data;
};

pinctrl core在struct pinctrl_ops中抽象出三个回调函数,用来获取pin groups相关信息,如下:

struct pinctrl_ops {
  //获取系统中pin groups的个数,后续的操作,将以相应的索引为单位(类似数组的下标,个数为数组的大小)
 int (*get_groups_count) (struct pinctrl_dev *pctldev);
  //获取指定group(由索引selector指定)的名称
 const char *(*get_group_name) (struct pinctrl_dev *pctldev, unsigned selector);
  //获取指定group的所有pins(由索引selector指定),结果保存在pins(指针数组)和num_pins(指针)中
 int (*get_group_pins) (struct pinctrl_dev *pctldev, unsigned selector, const unsigned **pins, unsigned *num_pins);
 void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset);
  //用于将device tree中的pin state信息转换为pin map
 int (*dt_node_to_map) (struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, unsigned *num_maps);
 void (*dt_free_map) (struct pinctrl_dev *pctldev, struct pinctrl_map *map, unsigned num_maps);
};

group 的组织方式是由驱动决定的。

  • pin configuration

除了上面的 pin 和 pin group,有些管脚可以配置,比如上拉,下拉,高阻等。pin configuration 来封装这些功能,具体体现在 struct pinconf_ops数据结构中,如下:

struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
 bool is_generic;
#endif
  //获取指定 pin 的当前配置,保存在 config 指针中
 int (*pin_config_get) (struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config);
  //设置指定pin的配置
 int (*pin_config_set) (struct pinctrl_dev *pctldev, unsigned pin, unsigned long *configs, unsigned num_configs);
  //获取指定pin group的配置项
 int (*pin_config_group_get) (struct pinctrl_dev *pctldev, unsigned selector, unsigned long *config);
  //设置指定pin group的配置项
 int (*pin_config_group_set) (struct pinctrl_dev *pctldev, unsigned selector, unsigned long *configs, unsigned num_configs);
  ......

  • pin mux

为了兼容不同的应用场景,有很多管脚可以配置为不同的功能,例如A和B两个管脚,既可以当作普通的GPIO使用,又可以配置为I2C的的SCL和SDA,也可以配置为UART的TX和RX,这称作管脚的复用(简称 pin mux)。使用 struct pinmux_ops 来抽象 pin mux 有关的操作,如下:

struct pinmux_ops {
  //检查某个pin是否已作它用,用于管脚复用时的互斥
 int (*request) (struct pinctrl_dev *pctldev, unsigned offset);
  //request的反操作
 int (*free) (struct pinctrl_dev *pctldev, unsigned offset);
  //获取系统中function的个数
 int (*get_functions_count) (struct pinctrl_dev *pctldev);
  //获取指定function的名称
 const char *(*get_function_name) (struct pinctrl_dev *pctldev, unsigned selector);
  //获取指定function所占用的pin group
 int (*get_function_groups) (struct pinctrl_dev *pctldev, unsigned selector, const char * const **groups, unsigned *num_groups);
  //将指定的pin group(group_selector)设置为指定的function(func_selector)
 int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector, unsigned group_selector);
  //以下是gpio相关的操作
 int (*gpio_request_enable) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset);
 void (*gpio_disable_free) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset);
 int (*gpio_set_direction) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset, bool input);
  //为true时,说明该pin controller不允许某个pin作为gpio和其它功能同时使用
 bool strict;
};

  • pin state

根据前面的描述,pinctrl driver 抽象出来了一些离散的对象:pin(pin group)、function、configuration,并实现了这些对象的控制和配置方式。然后我们回到某一个具体的 device 上(如 lpuart,usdhc)。一个设备在某一状态下(如工作状态、休眠状态、等等),所使用的pin(pin group)、pin(pin group)的 function 和 configuration,是唯一确定的。所以固定的组合可以确定固定的状态,在设备树里用 pinctrl-names 指明状态名字,pinctrl-x 指明状态引脚。

  • pin map

pin state 有关的信息是通过 pin map 收集,相关的数据结构如下:

struct pinctrl_map {
  //device的名称
 const char *dev_name;
  //pin state的名称
 const char *name;
  //该map的类型
 enum pinctrl_map_type type;
  //pin controller device的名字
 const char *ctrl_dev_name;
 union {
  struct pinctrl_map_mux mux;
  struct pinctrl_map_configs configs;
 } data;
};

enum pinctrl_map_type {
 PIN_MAP_TYPE_INVALID,
 //不需要任何配置,仅仅为了表示state的存在
 PIN_MAP_TYPE_DUMMY_STATE,
 //配置管脚复用
 PIN_MAP_TYPE_MUX_GROUP,
 //配置pin
 PIN_MAP_TYPE_CONFIGS_PIN,
 //配置pin group
 PIN_MAP_TYPE_CONFIGS_GROUP,
};

struct pinctrl_map_mux {
 //group的名字
 const char *group;
 //function的名字
 const char *function;
};

struct pinctrl_map_configs {
 //该pin或者pin group的名字
 const char *group_or_pin;
 //configuration数组
 unsigned long *configs;
 //配置项的个数
 unsigned num_configs;
};

pinctrl driver 确定了 pin map 各个字段的格式之后,就可以在 dts 文件中维护 pin state 以及相应的 mapping table。pinctrl core 在初始化的时候,会读取并解析 dts,并生成 pin map。

而各个 consumer,可以在自己的 dts node 中,直接引用 pinctrl driver 定义的 pin state,并在设备驱动的相应的位置,调用 pinctrl subsystem 提供的 API(pinctrl_lookup_state,pinctrl_select_state),active 或者 deactive 这些 state。


pin 控制器描述符中包括了三类操作函数:pctlops 是一些全局的控制函数;pmxops 是复用引脚相关的操作函数;confops操作函数是用来配置引脚的特性。pin 控制器驱动的初始化主要是注册这三类函数的回调。

pinctrl subsystem 的整体流程

  1. pinctrl driver 根据 pin controller 的实际情况,定义 struct pinctrl_desc(包括pin/pin group 的抽象,function 的抽象,pinconf、pinmux 的 operation API 实现,dt_node_to_map 的实现,等等),并注册到 kernel 中。
  2. pinctrl driver 在 pin controller的 dts node 中,根据自己定义的格式,描述每个 device 的所有 pin state。如下所示:
&iomuxc1 {
 ......
    pinctrl_lpuart5: lpuart5grp {
            fsl,pins = <
                    MX8ULP_PAD_PTF14__LPUART5_TX    0x3
                    MX8ULP_PAD_PTF15__LPUART5_RX    0x3
            >;
    };
 ......
 pinctrl_usdhc0: usdhc0grp {
            fsl,pins = <
                    MX8ULP_PAD_PTD1__SDHC0_CMD      0x3
                    MX8ULP_PAD_PTD2__SDHC0_CLK      0x10002
                    MX8ULP_PAD_PTD10__SDHC0_D0      0x3
                    MX8ULP_PAD_PTD9__SDHC0_D1       0x3
                    MX8ULP_PAD_PTD8__SDHC0_D2       0x3
                    MX8ULP_PAD_PTD7__SDHC0_D3       0x3
                    MX8ULP_PAD_PTD6__SDHC0_D4       0x3
                    MX8ULP_PAD_PTD5__SDHC0_D5       0x3
                    MX8ULP_PAD_PTD4__SDHC0_D6       0x3
                    MX8ULP_PAD_PTD3__SDHC0_D7       0x3
                    MX8ULP_PAD_PTD11__SDHC0_DQS     0x10002
            >;
    };
 ......
}

  1. 相应的 consumer driver 可以在自己的 dts node 中,引用 pinctrl driver 所定义的 pin state,如下所示:
&lpuart5 {
        /* console */
        pinctrl-names = "default", "sleep";
        pinctrl-0 = <&pinctrl_lpuart5>;
        pinctrl-1 = <&pinctrl_lpuart5>;
        status = "okay";
};

&usdhc0 {
        pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep";
        pinctrl-0 = <&pinctrl_usdhc0>;
        pinctrl-1 = <&pinctrl_usdhc0>;
        pinctrl-2 = <&pinctrl_usdhc0>;
        pinctrl-3 = <&pinctrl_usdhc0>;
        non-removable;
        bus-width = <8>;
        status = "okay";
};
  1. consumer driver 在需要的时候,可以调用 pinctrl_get/devm_pinctrl_get 接口,获得一个 pinctrl handle(struct pinctrl类型的指针)。在 pinctrl get 的过程中,解析 consumer device 的 dts node,找到相应的 pin state,进行调用 pinctrl driver 提供的 dt_node_to_map 接口,解析 pin state 并转换为 pin map。
  • pin state

对于一个“client device”来说,比如对于一个 UART 设备,它有多个“状态”:default、sleep 等,那对应的引脚也有这些状态。比如当这个设备处于 default 状态时,pinctrl 子系统会自动根据上述信息把所用引脚复用为 uart0 功能。当这这个设备处于 sleep 状态时,pinctrl 子系统会自动根据上述信息把所用引脚配置为高电平。

  • groups 和 function

一个设备会用到一个或多个引脚,这些引脚就可以归为一组 group。这些引脚可以复用为某个功能 function。当然,一个设备可以用到多组多功能引脚,比如A1、A2两组引脚,A1组复用为F1功能,A2组复用为F2功能。

sysfs 访问方法

先确定某个GPIO Controller的基准引脚号(base number),再计算出某个引脚的号码。

然后进入某个gpiochip目录,查看文件label的内容,根据 label 的内容对比设备树,就可以知道这对应哪一个 GPIO Controller。比如用上面的例子,通过对比设备树可知 gpiochip448 对应 gpio1。

因为 pin number = base + offset,所以 GPIO1_27 的号码是 448 + 27 = 475,那么通过 sys 可以做如下操作。
“echo 475 > /sys/class/gpio/export
echo in > /sys/class/gpio/gpio475/direction
cat /sys/class/gpio/gpio475/value
echo 475 > /sys/class/gpio/unexport



pinctrl subsystem 管理系统的所有管脚,GPIO 是这些管脚的用途之一,因此 gpio subsystem 应该是 pinctrl subsystem 的 backend。在使用 GPIO 的时候,都需要向系统的 pinctrl subsystem 申请管脚,并将管脚配置为 GPIO 功能。

内核也提供了通过 pinctrl 控制 gpio 的接口:

“static inline int pinctrl_request_gpio(unsigned gpio);
static inline void pinctrl_free_gpio(unsigned gpio);
static inline int pinctrl_gpio_direction_input(unsigned gpio);
static inline int pinctrl_gpio_direction_output(unsigned gpio);


pinctrl subsystem会维护一个gpio number到pin number的map(gpio range),将gpio subsystem传来的gpio number转换为pin number之后,调用struct pinmux_ops中有关的回调函数即可:
struct pinmux_ops {
        ...
        int (*gpio_request_enable) (struct pinctrl_dev *pctldev,
                                     struct pinctrl_gpio_range *range,
                                     unsigned offset);
        void (*gpio_disable_free) (struct pinctrl_dev *pctldev,
                                    struct pinctrl_gpio_range *range,
                                    unsigned offset);
        int (*gpio_set_direction) (struct pinctrl_dev *pctldev,
                                    struct pinctrl_gpio_range *range,
                                    unsigned offset,
                                    bool input);
};

当 gpio driver 需要使用某一个 gpio 的时候,可以在 struct gpio_chip 的 request 函数中,调用 pinctrl core 提供的 pinctrl_request_gpio 接口(参数是gpio编号),然后 pinctrl core 会查寻 gpio ranges 链表,将 gpio 编号转换成 pin 编号,然后调用 pinctrl 的相应接口(参数是pin编号),申请该 pin 的使用。
阅读(42) | 评论(0) | 转发(0) |
0

上一篇:Linux显示系统框架

下一篇:没有了

给主人留下些什么吧!~~