嵌入式爱好者

linux gpio

2019-11-29 15:49| 发布者: yangy| 查看: 1368| 评论: 0

类目: I.MX6系列产品  >  Linux     文档编号: 330

在Linux中,会在gpiolib.c中定义一个 ARCH_NR_GPIOS 大小的数组static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];。每个元素对应于当前CPU上各个GPIO接口的信息,记录各个GPIO的描述符,即对应struct gpio_desc结构体。struct gpio_desc内的成员gpio_chip又指向了一系列关于GPIO的操作。具体的操作在 driver/gpio/gpio_**.c中实现。 gpio_desc结构体中,flags为一标志位,用来指示当前GPIO是否已经占用,当用gpio_request申请GPIO资源时,flags位就会置位,当调用gpio_free释放GPIO资源时,flags就会清零。 当我们调用gpio_request(),实际上会将参数gpio在数组中查找其对应的gpio_desc,在根据gpio_chip就可以找到底层关于request()的实现。 所有的GPIO控制器驱动应包括下面的头文件,包含 struct gpio_chip 的定义。

include <linux/gpio/driver.h>

struct gpio_chip:标记GPIO所属的控制器,里面包含诸多回调函数,用于控制GPIO的行为,各个板卡都有实现自己的gpio_chip控制模块; struct gpio_desc:用于标记一个GPIO; 在driver/gpio/gpio_**.c文件中会注册特定平台**的GPIO驱动

  • 定义了GPIO板级控制的内容,包括设置 set gpio,get gpio,dir_in,dir_out,gpio_to_irq等行为;

  • 获取GPIO板级的资源,包括 gpio_reg_base,并赋值给 struct gpio_chip;

  • 将struct gpio_chip添加到GPIOLIB中;

driver/gpio/gpio_**.c会定义平台驱动并初始化 板级文件或设备树会添加平台设备

当设备与驱动匹配就会执行**_gpio_probe()的内容。 probe() 中初始化struct gpio_chip结构体的成员。并调用 gpiochip_add()将gpio_chip添加到 GPIOLIB 中。 GPIOLIB 提供GPIO调用的接口函数。比如常见的 gpio_request()/gpio_free()/gpio_get_value()/gpio_is_valid() 等。

相关结构体

  1. gpio_chip代表一个芯片的一个gpio bank

struct gpio_chip {  //gpio bank
const char *label; //bank名
struct device *dev; //设备文件
struct module *owner; //模块所有者
int (*request)(struct gpio_chip *chip,unsigned offset);
void (*free)(struct gpio_chip *chip,unsigned offset);
int (*direction_input)(struct gpio_chip *chip,unsigned offset); //配置gpio为输入
int (*get)(struct gpio_chip *chip,unsigned offset); //获取值
int (*direction_output)(struct gpio_chip *chip,unsigned offset, int value); //配置gpio为输出
int (*direction_output_array)(struct gpio_chip*chip, unsigned value, unsigned mask);
int (*set_debounce)(struct gpio_chip *chip,unsigned offset, unsigned debounce); //去抖动时间
void (*set)(struct gpio_chip *chip,unsigned offset, int value); //设置gpio值
int (*to_irq)(struct gpio_chip *chip,unsigned offset); //中断
void (*dbg_show)(struct seq_file *s,struct gpio_chip *chip);
int base; //gpio基数
u16 ngpio; //gpio个数
const char *const *names;
unsigned can_sleep:1; //能否睡眠
unsigned exported:1;
#if defined(CONFIG_OF_GPIO)
struct device_node *of_node;
int of_gpio_n_cells;
int (*of_xlate)(struct gpio_chip *gc, struct device_node *np,const void *gpio_spec, u32 *flags);
#endif
};
  1. gpio_desc 代表一个gpio口

struct gpio_desc {  //GPIO描述符
struct gpio_chip *chip; //所属gpio_chip
unsigned long flags; //gpio标志
#ifdef CONFIG_DEBUG_FS
const char *label;
#endif
};

2.1 gpio_desc flags标志

#define FLAG_REQUESTED  0   //已请求资源
#define FLAG_IS_OUT 1 //输出io
#define FLAG_RESERVED 2 //保留
#define FLAG_EXPORT 3 /* protected by sysfs_lock */
#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */
#define FLAG_TRIG_FALL 5 /* trigger on falling edge */
#define FLAG_TRIG_RISE 6 /* trigger on rising edge */
#define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */

2.2 全局gpio_desc数组

static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
  1. gpio类

static struct class gpio_class = {
.name = "gpio",
.owner = THIS_MODULE,
.class_attrs = gpio_class_attrs,
};

api接口

int gpiochip_add(struct gpio_chip *chip)                //添加初始化gpio_chip管理的gpio_desc
int gpiochip_remove(struct gpio_chip *chip) //清空gpio_chip管理的全局gpio_desc数组项
struct gpio_chip *gpiochip_find(void *data,int (*match)(struct gpio_chip *chip, void *data)) //查找获取gpio_chip

int gpio_request(unsigned gpio, const char *label) //请求gpio资源
void gpio_free(unsigned gpio) //释放gpio资源

int gpio_set_debounce(unsigned gpio, unsigned debounce) //设置去抖动时间
int gpio_direction_input(unsigned gpio) //设置gpio为输入io
int gpio_direction_output(unsigned gpio, int value) //设置gpio为输出io

int gpio_cansleep(unsigned gpio) //判断gpio可否睡眠
int gpio_get_value_cansleep(unsigned gpio) //获取gpio值(gpio可睡眠)
void gpio_set_value_cansleep(unsigned gpio, int value) //设置gpio值(gpio可睡眠)
int gpio_get_value(unsigned gpio) //获取gpio值(gpio不可睡眠)

int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) //请求一个gpio资源
int gpio_request_array(struct gpio *array, size_t num) //请求一组gpio资源
void gpio_free_array(struct gpio *array, size_t num) //释放一组gpio资源

int gpio_to_irq(unsigned gpio) //根据gpio号获取对应的中断号

int gpio_export(unsigned gpio, bool direction_may_change) //gpio创建用户接口
void gpio_unexport(unsigned gpio) //gpio移除用户接口

api及调用到的相关函数解析

1. 设置gpio_chip管理的全局gpio_desc数组项

int gpiochip_add(struct gpio_chip *chip)
{
unsigned long flags;
int status = 0;
unsigned id;
int base = chip->base;    //获取gpio_chip基数
//验证gpio的基数,gpio的最后一个io的编号正确性
if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1))
&& base >= 0) {
status = -EINVAL;
goto fail;
}

spin_lock_irqsave(&gpio_lock, flags);  //上自旋锁

if (base < 0) {
base = gpiochip_find_base(chip->ngpio); //根据gpio个数分配新的基数
if (base < 0) {
status = base;
goto unlock;
}
chip->base = base;  //设置新的基数
}

status = gpiochip_add_to_list(chip);

if (status == 0) {
chip->desc = &gpio_desc[chip->base];

for (id = 0; id < chip->ngpio; id++) {  //填充gpio_desc数组项
struct gpio_desc *desc = &chip->desc[id];
desc->chip = chip;

/* REVISIT: most hardware initializes GPIOs as
* inputs (often with pullups enabled) so power
* usage is minimized. Linux code should set the
* gpio direction first thing; but until it does,
* and in case chip->get_direction is not set,
* we may expose the wrong direction in sysfs.
*/
desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0;
}
}

spin_unlock_irqrestore(&gpio_lock, flags);  //解自旋锁

if (status)
goto fail;

#ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&chip->pin_ranges);
#endif

of_gpiochip_add(chip);
acpi_gpiochip_add(chip);

status = gpiochip_export(chip);
if (status) {
acpi_gpiochip_remove(chip);
of_gpiochip_remove(chip);
goto fail;
}

pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__,
chip->base, chip->base + chip->ngpio - 1,
chip->label ? : "generic");

return 0;

unlock:
spin_unlock_irqrestore(&gpio_lock, flags);
fail:
/* failures here can mean systems won't boot... */
pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__,
chip->base, chip->base + chip->ngpio - 1,
chip->label ? : "generic");
return status;
}
EXPORT_SYMBOL_GPL(gpiochip_add);

2. 清空gpio_chip管理的全局gpio_desc数组项

/**
* gpiochip_remove() - unregister a gpio_chip
* @chip: the chip to unregister
*
* A gpio_chip with any GPIOs still requested may not be removed.
*/
int gpiochip_remove(struct gpio_chip *chip)
{
unsigned long flags;
int status = 0;
unsigned id;

spin_lock_irqsave(&gpio_lock, flags);  //上自旋锁

gpiochip_remove_pin_ranges(chip);
of_gpiochip_remove(chip);
acpi_gpiochip_remove(chip);

for (id = 0; id < chip->ngpio; id++) {
if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags)) { //判断是否存在标志含FLAG_REQUESTED的gpio口
status = -EBUSY;
break;
}
}
if (status == 0) {
for (id = 0; id < chip->ngpio; id++) //清空对应全局gpio_desc数组项
chip->desc[id].chip = NULL;

list_del(&chip->list);
}

spin_unlock_irqrestore(&gpio_lock, flags); //解自旋锁

if (status == 0)
gpiochip_unexport(chip);  //gpio_chip移除用户接口

return status;
}
EXPORT_SYMBOL_GPL(gpiochip_remove);

3. 查找获取gpio_chip

/**
* gpiochip_find() - iterator for locating a specific gpio_chip
* @data: data to pass to match function
* @callback: Callback function to check gpio_chip
*
* Similar to bus_find_device. It returns a reference to a gpio_chip as
* determined by a user supplied @match callback. The callback should return
* 0 if the device doesn't match and non-zero if it does. If the callback is
* non-zero, this function will return to the caller and not iterate over any
* more gpio_chips.
*/
struct gpio_chip *gpiochip_find(void *data,
int (*match)(struct gpio_chip *chip,
    void *data))
{
struct gpio_chip *chip;
unsigned long flags;

spin_lock_irqsave(&gpio_lock, flags);  //上自旋锁
list_for_each_entry(chip, &gpio_chips, list)  //遍历整个全局gpio_desc数组
if (match(chip, data))   //调用传递进来的匹配函数
break;

/* No match? */
if (&chip->list == &gpio_chips)
chip = NULL;
spin_unlock_irqrestore(&gpio_lock, flags);  //解自旋锁

return chip;    //返回查找到的gpio_chip指针
}
EXPORT_SYMBOL_GPL(gpiochip_find);

4. 请求gpio资源

/* These "optional" allocation calls help prevent drivers from stomping
* on each other, and help provide better diagnostics in debugfs.
* They're called even less than the "set direction" calls.
*/
static int gpiod_request(struct gpio_desc *desc, const char *label)
{
struct gpio_chip *chip;
int status = -EPROBE_DEFER;
unsigned long flags;

if (!desc) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}

spin_lock_irqsave(&gpio_lock, flags);  //上自旋锁

chip = desc->chip;
if (chip == NULL)  //获取gpio_chip失败
goto done;

if (!try_module_get(chip->owner))   //增加模块引用计数
goto done;

/* NOTE: gpio_request() can be called in early boot,
* before IRQs are enabled, for non-sleeping (SOC) GPIOs.
*/

if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {   //测试并设置gpio_desc的FLAG_REQUESTED标志
desc_set_label(desc, label ? : "?");  //设置gpio_desc的label
status = 0;
} else {
status = -EBUSY;
module_put(chip->owner);      //减少模块引用计数
goto done;
}

if (chip->request) {    //gpio_chip存在request方法
/* chip->request may sleep */
spin_unlock_irqrestore(&gpio_lock, flags);   //解自旋锁
status = chip->request(chip, gpio_chip_hwgpio(desc));  //调用gpio_chip的request方法
spin_lock_irqsave(&gpio_lock, flags);    //上自旋锁

if (status < 0) {
desc_set_label(desc, NULL);   //清除gpio_desc的label
module_put(chip->owner);       //减少模块引用计数
clear_bit(FLAG_REQUESTED, &desc->flags);  //清除gpio_chip的FLAG_REQUESTED标志
goto done;
}
}
if (chip->get_direction) {   //gpio_chip存在get_direction方法
/* chip->get_direction may sleep */
spin_unlock_irqrestore(&gpio_lock, flags);
gpiod_get_direction(desc);
spin_lock_irqsave(&gpio_lock, flags);
}
done:
if (status)
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
spin_unlock_irqrestore(&gpio_lock, flags);
return status;
}

int gpio_request(unsigned gpio, const char *label)
{
return gpiod_request(gpio_to_desc(gpio), label);
}
EXPORT_SYMBOL_GPL(gpio_request);

5. 释放gpio资源

static void gpiod_free(struct gpio_desc *desc)
{
unsigned long flags;
struct gpio_chip *chip;

might_sleep();

if (!desc) {  //验证gpio号
WARN_ON(extra_checks);
return;
}

gpiod_unexport(desc);    //gpio移除用户接口

spin_lock_irqsave(&gpio_lock, flags);

chip = desc->chip;
if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) { //测试gpio_desc的FLAG_REQUESTED标志
if (chip->free) {     //gpio_chip存在free方法
spin_unlock_irqrestore(&gpio_lock, flags);
might_sleep_if(chip->can_sleep);
chip->free(chip, gpio_chip_hwgpio(desc));  //调用gpio_chip的free方法
spin_lock_irqsave(&gpio_lock, flags);
}
desc_set_label(desc, NULL);   //清除gpio_desc的label信息
module_put(desc->chip->owner);  //减少模块引用计数
clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
clear_bit(FLAG_REQUESTED, &desc->flags);
clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
} else
WARN_ON(extra_checks);

spin_unlock_irqrestore(&gpio_lock, flags);
}

void gpio_free(unsigned gpio)
{
gpiod_free(gpio_to_desc(gpio));
}
EXPORT_SYMBOL_GPL(gpio_free);

6. 设置去抖动时间

int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
{
unsigned long flags;
struct gpio_chip *chip;
int status = -EINVAL;
int offset;

if (!desc || !desc->chip) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}

chip = desc->chip;  //获取gpio_chip
if (!chip->set || !chip->set_debounce) { //判断gpio_chip是否存在且是否存在set和set_debounce方法
gpiod_dbg(desc,
 "%s: missing set() or set_debounce() operations\n",
 __func__);
return -ENOTSUPP;
}

spin_lock_irqsave(&gpio_lock, flags);

status = gpio_ensure_requested(desc); //确保gpio是request请求过了的
if (status < 0)
goto fail;

/* now we know the gpio is valid and chip won't vanish */

spin_unlock_irqrestore(&gpio_lock, flags);

might_sleep_if(chip->can_sleep);

offset = gpio_chip_hwgpio(desc);  //计算gpio在该gpio_chip的偏移量
return chip->set_debounce(chip, offset, debounce);  //返回gpio_chip的set_debounce方法

fail:
spin_unlock_irqrestore(&gpio_lock, flags);
if (status)
gpiod_dbg(desc, "%s: status %d\n", __func__, status);

return status;
}
EXPORT_SYMBOL_GPL(gpiod_set_debounce);

7. 设置gpio为输入io

/**
* gpiod_direction_input - set the GPIO direction to input
* @desc: GPIO to set to input
*
* Set the direction of the passed GPIO to input, such as gpiod_get_value() can
* be called safely on it.
*
* Return 0 in case of success, else an error code.
*/
int gpiod_direction_input(struct gpio_desc *desc)
{
unsigned long flags;
struct gpio_chip *chip;
int status = -EINVAL;
int offset;

if (!desc || !desc->chip) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}

chip = desc->chip;
if (!chip->get || !chip->direction_input) { //判断gpio_chip是否存在且存在get和directon_input方法
gpiod_warn(desc,
"%s: missing get() or direction_input() operations\n",
__func__);
return -EIO;
}

spin_lock_irqsave(&gpio_lock, flags);

status = gpio_ensure_requested(desc);  //确保gpio是request请求过了的
if (status < 0)
goto fail;

/* now we know the gpio is valid and chip won't vanish */

spin_unlock_irqrestore(&gpio_lock, flags);

might_sleep_if(chip->can_sleep);

offset = gpio_chip_hwgpio(desc);  //计算gpio在该gpio_chip的偏移量
if (status) {
status = chip->request(chip, offset);  //调用chip_gpio的request方法
if (status < 0) {
gpiod_dbg(desc, "%s: chip request fail, %d\n",
__func__, status);
/* and it's not available to anyone else ...
* gpio_request() is the fully clean solution.
*/
goto lose;
}
}

status = chip->direction_input(chip, offset);  //调用chip_gpio的direction_input方法
if (status == 0)
clear_bit(FLAG_IS_OUT, &desc->flags);

trace_gpio_direction(desc_to_gpio(desc), 1, status);
lose:
return status;
fail:
spin_unlock_irqrestore(&gpio_lock, flags);
if (status)
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
return status;
}
EXPORT_SYMBOL_GPL(gpiod_direction_input);

8. 设备gpio为输出io

/**
* gpiod_direction_output - set the GPIO direction to input
* @desc: GPIO to set to output
* @value: initial output value of the GPIO
*
* Set the direction of the passed GPIO to output, such as gpiod_set_value() can
* be called safely on it. The initial value of the output must be specified.
*
* Return 0 in case of success, else an error code.
*/
int gpiod_direction_output(struct gpio_desc *desc, int value)
{
unsigned long flags;
struct gpio_chip *chip;
int status = -EINVAL;
int offset;

if (!desc || !desc->chip) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}

/* GPIOs used for IRQs shall not be set as output */
if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) {
gpiod_err(desc,
 "%s: tried to set a GPIO tied to an IRQ as output\n",
 __func__);
return -EIO;
}

/* Open drain pin should not be driven to 1 */
if (value && test_bit(FLAG_OPEN_DRAIN,  &desc->flags))
return gpiod_direction_input(desc);

/* Open source pin should not be driven to 0 */
if (!value && test_bit(FLAG_OPEN_SOURCE,  &desc->flags))
return gpiod_direction_input(desc);

chip = desc->chip;
if (!chip->set || !chip->direction_output) {
gpiod_warn(desc,
      "%s: missing set() or direction_output() operations\n",
      __func__);
return -EIO;
}

spin_lock_irqsave(&gpio_lock, flags);

status = gpio_ensure_requested(desc);  //确保gpio是request请求过了的
if (status < 0)
goto fail;

/* now we know the gpio is valid and chip won't vanish */

spin_unlock_irqrestore(&gpio_lock, flags);

might_sleep_if(chip->can_sleep);

offset = gpio_chip_hwgpio(desc);
if (status) {
status = chip->request(chip, offset);
if (status < 0) {
gpiod_dbg(desc, "%s: chip request fail, %d\n",
__func__, status);
/* and it's not available to anyone else ...
* gpio_request() is the fully clean solution.
*/
goto lose;
}
}

status = chip->direction_output(chip, offset, value);
if (status == 0)
set_bit(FLAG_IS_OUT, &desc->flags);
trace_gpio_value(desc_to_gpio(desc), 0, value);
trace_gpio_direction(desc_to_gpio(desc), 0, status);
lose:
return status;
fail:
spin_unlock_irqrestore(&gpio_lock, flags);
if (status)
gpiod_dbg(desc, "%s: gpio status %d\n", __func__, status);
return status;
}
EXPORT_SYMBOL_GPL(gpiod_direction_output);

用户态使用gpio监听中断

首先需要将该gpio配置为中断 echo "rising" > /sys/class/gpio/gpio12/edge

nt gpio_id;

struct pollfd fds[1];

gpio_fd = open("/sys/class/gpio/gpio12/value",O_RDONLY);

if( gpio_fd == -1 )

  err_print("gpio open");

fds[0].fd = gpio_fd;

fds[0].events  = POLLPRI;

ret = read(gpio_fd,buff,10);

if( ret == -1 )

   err_print("read");

while(1){

    ret = poll(fds,1,-1);

    if( ret == -1 )

        err_print("poll");

      if( fds[0].revents & POLLPRI){

          ret = lseek(gpio_fd,0,SEEK_SET);

          if( ret == -1 )

              err_print("lseek");

          ret = read(gpio_fd,buff,10);

          if( ret == -1 )

              err_print("read");

           /*此时表示已经监听到中断触发了,该干事了*/

          ...............

  }

}

记住使用poll()函数,设置事件监听类型为POLLPRI和POLLERR在poll()返回后,使用lseek()移动到文件开头读取新的值或者关闭它再重新打开读取新值。必须这样做否则poll函数会总是返回。


已解决

未解决

只是看看

最新评论

QQ|小黑屋| 飞凌嵌入式 ( 冀ICP备12004394号-1 )

GMT+8, 2024-11-23 08:42

Powered by Discuz! X3.4

© 2001-2013 Comsenz Inc.

返回顶部