简介:
gpio源自GPIO控制器, 通常一个芯片会有多个GPIO控制器. GPIO驱动就是GPIO控制器驱动. 这部分驱动通常由
芯片原厂编写,不同芯片厂商的GPIO控制器差异很大, 但是他们基于linux内核会相同的架构编写,实现方式大同小异.
对于普通开发者来说,只需要知道怎么使用(申请,释放,设置输入输出...)就可以了,但是了解GPIO控制器驱动实现方式对深入理解
GPIO是有帮助的.
首先看下GPIO控制器驱动是如何编写的,然后介绍驱动中如何使用GPIO,最后介绍sysfs,proc,debugfs下的GPIO相关内容(如果有的话.)
1. gpio控制器驱动
struct gpio_chip 代表一个GPIO控制器(include/linux/gpio/driver.h), 定义如下:
1.1 gpio_chip 结构
/**
* struct gpio_chip 是GPIO控制器的抽象, 代表一个GPIO控制器.
*/
struct gpio_chip {
const char *label; //标签
struct gpio_device *gpiodev; //gpio_chip 关联的gpio_deivce, 一个gpiochip 就是一个gpio_device.
struct device *parent; // gpio_device的父设备, 通常是注册这个chip设备.
struct module *owner; // 所有者
/**
* GPIO控制器驱动要实现的GPIO操作函数. 当然不必实现全部的功能.
*/
int (*request)(struct gpio_chip *chip,unsigned offset);
void (*free)(struct gpio_chip *chip,unsigned offset);
int (*get_direction)(struct gpio_chip *chip,unsigned offset);
int (*direction_input)(struct gpio_chip *chip,unsigned offset);
int (*direction_output)(struct gpio_chip *chip,unsigned offset, int value);
int (*get)(struct gpio_chip *chip,unsigned offset);
void (*set)(struct gpio_chip *chip,unsigned offset, int value);
void (*set_multiple)(struct gpio_chip *chip,unsigned long *mask,unsigned long *bits);
int (*set_config)(struct gpio_chip *chip, unsigned offset,unsigned long config);
int (*to_irq)(struct gpio_chip *chip,unsigned offset);
void (*dbg_show)(struct seq_file *s,struct gpio_chip *chip);
/**
* 一个gpio_chip 管理一组GPIO, 系统中所有GPIO拥有唯一的编号.
* base:指定当前gpio_chip 管理的这组GPIO的初始编号,=-1 表示使用动态分配的初始编号
* ngpio: 这ge gpio_chip 管理的GPIO数量.
*/
int base;
u16 ngpio;
const char *const *names; //别名, 通常不设置.
bool can_sleep;
#if IS_ENABLED(CONFIG_GPIO_GENERIC)
unsigned long (*read_reg)(void __iomem *reg);
void (*write_reg)(void __iomem *reg, unsigned long data);
unsigned long (*pin2mask)(struct gpio_chip *gc, unsigned int pin);
void __iomem *reg_dat;
void __iomem *reg_set;
void __iomem *reg_clr;
void __iomem *reg_dir;
int bgpio_bits;
spinlock_t bgpio_lock;
unsigned long bgpio_data;
unsigned long bgpio_dir;
#endif
/*由GPIO管理中断,处理中断事件.*/
#ifdef CONFIG_GPIOLIB_IRQCHIP
/*
* With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib
* to handle IRQs for most practical cases.
*/
/**
* @irq:
*
* Integrates interrupt chip functionality with the GPIO chip. Can be
* used to handle IRQs for most practical cases.
*/
struct gpio_irq_chip irq;
#endif
/**
* 使用GPIO 设备树, 如果开启则可以使用
* of_get_name_gpio... 等等 of相关函数
*/
#if defined(CONFIG_OF_GPIO)
/*
* If CONFIG_OF is enabled, then all GPIO controllers described in the
* device tree automatically may have an OF translation
*/
/**
* @of_node:
* GPIO 控制器的设备树节点.
*/
struct device_node *of_node;
/**
* @of_gpio_n_cells:
* 描述一个GPIO(不是gpio_chip)用到的关键字数量,
* Number of cells used to form the GPIO specifier.
*/
unsigned int of_gpio_n_cells;
/**
* @of_xlate:
*
* Callback to translate a device tree GPIO specifier into a chip-
* relative GPIO number and flags.
*/
int (*of_xlate)(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec, u32 *flags);
#endif
};
1.2 注册一个gpio_chip
驱动中只需要填充一个 struct chio_chip结构体然后调用注册函数即可,重点是注册函数做了什么.
static int sprd_gpio_probe(struct platform_device *pdev)
{
struct gpio_irq_chip *irq;
struct sprd_gpio *sprd_gpio;
struct resource *res;
int ret;
sprd_gpio = devm_kzalloc(&pdev->dev, sizeof(*sprd_gpio), GFP_KERNEL);
if (!sprd_gpio)
return -ENOMEM;
sprd_gpio->irq = platform_get_irq(pdev, 0);
if (sprd_gpio->irq < 0) {
dev_err(&pdev->dev, "Failed to get GPIO interrupt.\n");
return sprd_gpio->irq;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sprd_gpio->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(sprd_gpio->base))
return PTR_ERR(sprd_gpio->base);
spin_lock_init(&sprd_gpio->lock);
sprd_gpio->chip.label = dev_name(&pdev->dev);
sprd_gpio->chip.ngpio = SPRD_GPIO_NR;
sprd_gpio->chip.base = -1;
sprd_gpio->chip.parent = &pdev->dev;
sprd_gpio->chip.of_node = pdev->dev.of_node;
sprd_gpio->chip.request = sprd_gpio_request;
sprd_gpio->chip.free = sprd_gpio_free;
sprd_gpio->chip.get = sprd_gpio_get;
sprd_gpio->chip.set = sprd_gpio_set;
sprd_gpio->chip.direction_input = sprd_gpio_direction_input;
sprd_gpio->chip.direction_output = sprd_gpio_direction_output;
irq = &sprd_gpio->chip.irq;
irq->chip = &sprd_gpio_irqchip;
irq->handler = handle_bad_irq;
irq->default_type = IRQ_TYPE_NONE;
irq->parent_handler = sprd_gpio_irq_handler;
irq->parent_handler_data = sprd_gpio;
irq->num_parents = 1;
irq->parents = &sprd_gpio->irq;
ret = devm_gpiochip_add_data(&pdev->dev, &sprd_gpio->chip, sprd_gpio);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip %d\n", ret);
return ret;
}
platform_set_drvdata(pdev, sprd_gpio);
return 0;
}
重点是注册函数,调用关系如下:evm_gpiochip_add_data() -> gpiochip_add_data(chip, data); -> gpiochip_add_data_with_key(chip, data, NULL)
函数如下:
int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
struct lock_class_key *key)
{
unsigned long flags;
int status = 0;
unsigned i;
int base = chip->base;
struct gpio_device *gdev;
/*
* First: allocate and populate the internal stat container, and
* set up the struct device.
*/
gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
if (!gdev)
return -ENOMEM;
gdev->dev.bus = &gpio_bus_type;
gdev->chip = chip;
chip->gpiodev = gdev;
if (chip->parent) {
gdev->dev.parent = chip->parent;
gdev->dev.of_node = chip->parent->of_node;
}
#ifdef CONFIG_OF_GPIO
/* If the gpiochip has an assigned OF node this takes precedence */
if (chip->of_node)
gdev->dev.of_node = chip->of_node;
#endif
/**
* 使用编号管理工具生成一个从0开始的编号. 例如 gpiochip0
*/
gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);
if (gdev->id < 0) {
status = gdev->id;
goto err_free_gdev;
}
dev_set_name(&gdev->dev, "gpiochip%d", gdev->id); //设置了 dev de kobject
device_initialize(&gdev->dev);
dev_set_drvdata(&gdev->dev, gdev);
if (chip->parent && chip->parent->driver)
gdev->owner = chip->parent->driver->owner;
else if (chip->owner)
/* TODO: remove chip->owner */
gdev->owner = chip->owner;
else
gdev->owner = THIS_MODULE;
/**
* 为每个引脚 struct gpio_desc *descs 申请空间
* kcalloc 申请的是连续的初始化为0的空间,这点很重要,后面会使用数组放温暖这些数据.
*/
gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
if (!gdev->descs) {
status = -ENOMEM;
goto err_free_ida;
}
if (chip->ngpio == 0) {
chip_err(chip, "tried to insert a GPIO chip with zero lines\n");
status = -EINVAL;
goto err_free_descs;
}
if (chip->label)
gdev->label = kstrdup(chip->label, GFP_KERNEL);
else
gdev->label = kstrdup("unknown", GFP_KERNEL);
if (!gdev->label) {
status = -ENOMEM;
goto err_free_descs;
}
/*设置gpio_device 支持多少个引脚*/
gdev->ngpio = chip->ngpio;
gdev->data = data;
spin_lock_irqsave(&gpio_lock, flags);
/*
* TODO: this allocates a Linux GPIO number base in the global
* GPIO numberspace for this chip. In the long run we want to
* get *rid* of this numberspace and use only descriptors, but
* it may be a pipe dream. It will not happen before we get rid
* of the sysfs interface anyways.
*/
if (base < 0) {
base = gpiochip_find_base(chip->ngpio);
if (base < 0) {
status = base;
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_label;
}
/*
* TODO: it should not be necessary to reflect the assigned
* base outside of the GPIO subsystem. Go over drivers and
* see if anyone makes use of this, else drop this and assign
* a poison instead.
*/
chip->base = base;
}
gdev->base = base;
/*所有的GPIO设备都在一个 gpio_devices 链表里*/
status = gpiodev_add_to_list(gdev);
if (status) {
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_label;
}
spin_unlock_irqrestore(&gpio_lock, flags);
for (i = 0; i < chip->ngpio; i++) {
struct gpio_desc *desc = &gdev->descs[i];
desc->gdev = gdev;
/*
* 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.
*/
if (chip->get_direction) {
/*
* If we have .get_direction, set up the initial
* direction flag from the hardware.
*/
int dir = chip->get_direction(chip, i);
if (!dir)
set_bit(FLAG_IS_OUT, &desc->flags);
} else if (!chip->direction_input) {
/*
* If the chip lacks the .direction_input callback
* we logically assume all lines are outputs.
*/
set_bit(FLAG_IS_OUT, &desc->flags);
}
}
#ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&gdev->pin_ranges);
#endif
status = gpiochip_set_desc_names(chip);
if (status)
goto err_remove_from_list;
status = gpiochip_irqchip_init_valid_mask(chip);
if (status)
goto err_remove_from_list;
status = gpiochip_add_irqchip(chip, key);
if (status)
goto err_remove_chip;
status = of_gpiochip_add(chip);
if (status)
goto err_remove_chip;
acpi_gpiochip_add(chip);
/*
* By first adding the chardev, and then adding the device,
* we get a device node entry in sysfs under
* /sys/bus/gpio/devices/gpiochipN/dev that can be used for
* coldplug of device nodes and other udev business.
* We can do this only if gpiolib has been initialized.
* Otherwise, defer until later.
*/
if (gpiolib_initialized) {
status = gpiochip_setup_dev(gdev);
if (status)
goto err_remove_chip;
}
return 0;
err_remove_chip:
acpi_gpiochip_remove(chip);
gpiochip_free_hogs(chip);
of_gpiochip_remove(chip);
gpiochip_irqchip_free_valid_mask(chip);
err_remove_from_list:
spin_lock_irqsave(&gpio_lock, flags);
list_del(&gdev->list);
spin_unlock_irqrestore(&gpio_lock, flags);
err_free_label:
kfree(gdev->label);
err_free_descs:
kfree(gdev->descs);
err_free_ida:
ida_simple_remove(&gpio_ida, gdev->id);
err_free_gdev:
/* failures here can mean systems won't boot... */
pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__,
gdev->base, gdev->base + gdev->ngpio - 1,
chip->label ? : "generic");
kfree(gdev);
return status;
}
函数gpiochip_add_data_with_key()用于注册一个gpio设备struct gpio_device *gdev 代表一个软件上的gpio_device, dev_set_name(&gdev->dev, "gpiochip%d", gdev->id); 设置了设备的名称, 系统启动后可以在 ls -l /dev 看到相应字符设备.
struct gpio_device 结构体如下:
struct gpio_device {
int id;
struct device dev;
struct cdev chrdev; //gpio_deivce的字符设备.
struct device *mockdev;
struct module *owner;
struct gpio_chip *chip; //gpio_device 管理的额gpio_chip描述结构体
struct gpio_desc *descs; //设备支持的所有的gpio, 一个 struct gpio_desc 代表一个gpio 实例
int base;
u16 ngpio; //总共有多少个GPIO
char *label;
void *data;
struct list_head list;
#ifdef CONFIG_PINCTRL
/*
* If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
* describe the actual pin range which they serve in an SoC. This
* information would be used by pinctrl subsystem to configure
* corresponding pins for gpio usage.
*/
struct list_head pin_ranges;
#endif
};
可以看到它包裹了一个字符设备, 这个字符设备将会注册到系统, 调用的是gpiochip_setup_dev(gdev);函数,
函数根据主设备号和gpio_device->id 生成一个设备号, (GPIO核心代码已经申请了gpio_devt 设备号, 子设备号
数量是255),调用cdev_device_add 函数注册进系统, 可以通过 ls -l /dev 看到注册的设备
最后调用gpiochip_sysfs_register() 添加sysfs接口, 可以通过 ls /sys/class/gpio/ 查看sysfs接口
代码实现如下:
static int gpiochip_setup_dev(struct gpio_device *gdev)
{
int status;
/*初始化字符设备*/
cdev_init(&gdev->chrdev, &gpio_fileops);
gdev->chrdev.owner = THIS_MODULE;
gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id);
/*把字符设备注册进系统*/
status = cdev_device_add(&gdev->chrdev, &gdev->dev);
if (status)
return status;
chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n",
MAJOR(gpio_devt), gdev->id);
/*创建 sysfs 文件接口*/
status = gpiochip_sysfs_register(gdev);
if (status)
goto err_remove_device;
/* From this point, the .release() function cleans up gpio_device */
gdev->dev.release = gpiodevice_release;
pr_debug("%s: registered GPIOs %d to %d on device: %s (%s)\n",
__func__, gdev->base, gdev->base + gdev->ngpio - 1,
dev_name(&gdev->dev), gdev->chip->label ? : "generic");
return 0;
err_remove_device:
cdev_device_del(&gdev->chrdev, &gdev->dev);
return status;
}
1.3 注册sysfs接口
前面看到调用gpiochip_sysfs_register(gdev) 函数注册了sysfs接口.主要是指定了sysfs接口的目录,以及几个基础属性,源码如下:
int gpiochip_sysfs_register(struct gpio_device *gdev)
{
struct device *dev;
struct device *parent;
struct gpio_chip *chip = gdev->chip;
/*
* Many systems add gpio chips for SOC support very early,
* before driver model support is available. In those cases we
* register later, in gpiolib_sysfs_init() ... here we just
* verify that _some_ field of gpio_class got initialized.
*/
if (!gpio_class.p)
return 0;
/*
* For sysfs backward compatibility we need to preserve this
* preferred parenting to the gpio_chip parent field, if set.
*/
if (chip->parent)
parent = chip->parent;
else
parent = &gdev->dev;
/* use chip->base for the ID; it's already known to be unique */
/**
* gpio_class 是提前初始化的class, name ="gpio", 所以 gpiochip的sys接口文件位于/sys/class/gpio目录下
* gpiochip_groups: gpiochipXXX 文件夹下支持的属性.
* static struct attribute *gpiochip_attrs[] = {
&dev_attr_base.attr,
&dev_attr_label.attr,
&dev_attr_ngpio.attr,
NULL,
};
ATTRIBUTE_GROUPS(gpiochip);
*/
dev = device_create_with_groups(&gpio_class, parent,
MKDEV(0, 0),
chip, gpiochip_groups,
"gpiochip%d", chip->base);
if (IS_ERR(dev))
return PTR_ERR(dev);
mutex_lock(&sysfs_lock);
gdev->mockdev = dev;
mutex_unlock(&sysfs_lock);
return 0;
}
1.4 sysfs的export接口.
1.3小节介绍到, device_create_with_group 函数创建了sysfs接口, 它所属的类是gpio_class gpio_class 相关代码位于drivers/gpio/gpiolib-sysfs.c 目录. 它有两个重要的属性, 如下:
static struct attribute *gpio_class_attrs[] = {
&class_attr_export.attr,
&class_attr_unexport.attr,
NULL,
};
ATTRIBUTE_GROUPS(gpio_class);
这两个属性就是 应用层导出引脚的底层实现,
2. 驱动程序中使用GPIO
在驱动中使用通常要借助设备树, 通常的定义如下:
/*1*/
gpio = <&gpio2 RK_PD7 GPIO_ACTIVE_HIGH>;
/*2*/
vbus-gpio = <&gpio3 26 GPIO_ACTIVE_HIGH>;
/*3*/
gpio = <&gpio0 RK_PB4 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&usbotg_pwren_h>;
如何在设备树中定义GPIO可以参考Documentation/devicetree/bindings/gpio.
2.1基于GPIO编号的API
每个GPIO被逻辑上分配了一个GPIO编号, 例如进入/sys/class/gpio/gpiochipxxx 目录cat base
cat ngpio 可以查看一个 gpio-chip 支持的起始GPIO编号和总的GPIO数量. 不同平台是不一样. 例如vbus-gpio = <&gpio3 26 GPIO_ACTIVE_HIGH>; 可能的编号可以是 (3 -1) * 32 + 26 其中的3是gpio_chip的编号.幸运的是通常不需要我么手动计算.of_get_named_gpio()函数可以根据名字找到GPIO并返回GPIO编号.
GPIO相关API如下:
//namexxx = <&gpio1 6 GPIO_ACTIVE_HIGH>
of_get_named_gpio(np, "namexxxxxxx", 0);// 根据设备树中定义的名字返回gpio编号.
gpio_request() //of_get_named_gpio() 只是找到GPIO编号, gpio_request获取了才能用,如果已经获取过,就会失败.
gpio_free();
gpio_is_valid();// 判断GPIO 是否可用.
gpiod_direction_output(); //设置方向
gpiod_direction_input();
gpio_set_value() //仅对输出的有效,设置高低. 设置的是active的值.
gpio_get_value() //获取GPIO的高低.
gpio_get_value_cansleep();
/*获取并使用一个GPIO的流程*/
GPIO_number = of_get_named_gpio(np, "name_xxx", 0);
if (gpio_is_valid(GPIO_number)) {
rc = gpio_request(dev_data->ear_dirl_gpio, "musb vbus detect");
if (rc)
{
}
gpio_direction_output(GPIO_number,0);
} else {
}
gpio_set_value();
2.2 基于struct gpio_desc 的API
struct gpio_desc 用于描述一个GPIO, GPIO控制器驱动中已经申请了gpio_device实例. 结构体示例如下:
struct gpio_desc {
struct gpio_device *gdev;
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_EXPORT 2 /* protected by sysfs_lock */
#define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */
#define FLAG_ACTIVE_LOW 6 /* value has active low */
#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
#define FLAG_IS_HOGGED 11 /* GPIO is hogged */
#define FLAG_SLEEP_MAY_LOOSE_VALUE 12 /* GPIO may loose value in sleep */
/* Connection label */
const char *label;
/* Name of the GPIO */
const char *name;
};
函数struct gpio_desc *__must_check devm_gpiod_get_optional(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
区别of_get_named_gpio(np, "namexxxxxxx", 0); 这里的 con_id 也是名字, 但是这里规定在设备树里定义的的名字必须以 -gpio 或 -gpios结尾. 这里的con_id 是出掉-gpio(s) 之后的名字.如下所示
powerdown-gpios = <&gpio7 12 GPIO_ACTIVE_HIGH>;
sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
GPIOD_OUT_HIGH);
其他相关API如下,
devm_gpiod_get_optional() //获取成功后GPIO就会被占用.
void gpiod_put(struct gpio_desc *desc)//释放
gpiod_direction_output(); //输出
gpiod_direction_output_raw();
gpiod_set_raw_value();//输出值
gpiod_set_raw_array_value();
gpiod_set_raw_value_cansleep();//可在休眠上下文
gpiod_set_raw_array_value_cansleep();
gpiod_direction_input(); //输入
gpiod_get_value();
gpiod_get_value_cansleep()
gpiod_get_raw_value();
gpiod_get_raw_value_cansleep()
2.3 GPIO编号和gpio_desc之间的转换
查看源码,可以看到GPIO编号和gpio_desc之间的转换
3. 应用层接口
3.1 sysfs接口
在1.3有介绍sysfs接口,这里简单说明:
echo 79> /sys/class/gpio/export //导出编号为79 的gpio 如果成功会再 gpio 目录下生成gpio79文件
进入 gpio79目录:
echo in/out/low/high > direction //设置引脚输入方向,out默认输出低电平,
cat value或echo 1/0 > value 获取或设置GPIO电平
使用如上命令设置GPIO.
3.2 debug 接口
cat /sys/kernel/debug/gpio 可以查看GPIO使用情况
cat /proc/interrupts //也可以查看中断相关的信息,