/* 分析GPIO SUB子系统源码 Linux内核版本4.9.37 */
/****************************************** GPIO对应的数据结构*************************************/
/* 每个引脚都对应于一个引脚描述符 */
struct gpio_desc {
struct gpio_device *gdev; //GPIO设备
unsigned long flags; //引脚对应的标志
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0 // 引脚被已被请求
#define FLAG_IS_OUT 1 // 引脚是输出状态
#define FLAG_EXPORT 2 /* 被 sysfs_lock保护 */
#define FLAG_SYSFS 3 /* 导入到 /sys/class/gpio/control */
#define FLAG_ACTIVE_LOW 6 /* 输出低电平 */
#define FLAG_OPEN_DRAIN 7 /* 引脚是开漏类型 */
#define FLAG_OPEN_SOURCE 8 /* 引脚是开源类型 */
#define FLAG_USED_AS_IRQ 9 /* 引脚连接到中断 */
#define FLAG_IS_HOGGED 11 /* 引脚被占用 */
const char *label; //链接标志
const char *name;
};
struct gpio_device {
int id; //GPIO芯片的数字ID号
struct device dev; //对应的设备结构体
struct cdev chrdev; //对应的字符设备
struct device *mockdev;
struct module *owner;
struct gpio_chip *chip; //gpio芯片
struct gpio_desc *descs; //引脚描述符指针
int base; //gpio的起始号码
u16 ngpio; //管理的GPIO数量
char *label; //标签数据
void *data; //驱动程序分配的数据
struct list_head list;
#ifdef CONFIG_PINCTRL
struct list_head pin_ranges; //定义一个引脚范围的链表
#endif
};
struct gpio_chip {
const char *label;
struct gpio_device *gpiodev; //指向GPIO设备
struct device *parent; //GPIO的父设备
struct module *owner;
/*对应的回调函数 */
/* 向内核申请对应的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_debounce)(struct gpio_chip *chip,
unsigned offset,
unsigned debounce);
int (*set_single_ended)(struct gpio_chip *chip,
unsigned offset,
enum single_ended_mode mode);
int (*to_irq)(struct gpio_chip *chip,
unsigned offset);
void (*dbg_show)(struct seq_file *s,
struct gpio_chip *chip);
int base; //引脚的起始编号
u16 ngpio; //芯片支持的GPIO数量
const char *const *names;//对应的名字
bool can_sleep; //是否睡眠
bool irq_not_threaded;
#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); //该回调返回正确的GPIO对应掩码
void __iomem *reg_dat; //数据寄存器
void __iomem *reg_set; //置位寄存器
void __iomem *reg_clr; //置零寄存器
void __iomem *reg_dir; //方向寄存器
int bgpio_bits; //对应的bit
spinlock_t bgpio_lock;
unsigned long bgpio_data;
unsigned long bgpio_dir;
#endif
#ifdef CONFIG_GPIOLIB_IRQCHIP //支持中断
struct irq_chip *irqchip; //中断芯片
struct irq_domain *irqdomain; //中断映射的区域
unsigned int irq_base; //中断的起始号
irq_flow_handler_t irq_handler;//中断处理函数
unsigned int irq_default_type; //中断默认的类型
int irq_parent;
bool irq_need_valid_mask; //中断是否需要中断有效掩码
unsigned long *irq_valid_mask;
struct lock_class_key *lock_key;
#endif
#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
*/
struct device_node *of_node; //设备树的结点
int of_gpio_n_cells;
int (*of_xlate)(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec, u32 *flags);
#endif
};
/*使用此函数向内核注册一个GPIO芯片 并调用相应的API即可*/
static inline int gpiochip_add(struct gpio_chip *chip)
{
return gpiochip_add_data(chip, NULL);
}
int gpiochip_add_data(struct gpio_chip *chip, void *data)
{
unsigned long flags;
int status = 0;
unsigned i;
int base = chip->base; //芯片对应的起始芯片标号
struct gpio_device *gdev;
/* 向内核空间申请一个gpio_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
/* 申请对应的ID号 */
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); //设置对应的名字
device_initialize(&gdev->dev);
dev_set_drvdata(&gdev->dev, gdev);
/* 设置OWENER成员 */
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;
/*向内核申请对应的引脚描述符空间 */
gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
if (!gdev->descs) {
status = -ENOMEM;
goto err_free_gdev;
}
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;
}
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;
/* struct list_head gpio_devices;
* 将gdev添加到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 = 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_gdev:
ida_simple_remove(&gpio_ida, gdev->id);
/* 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;
}
/*******************************inclue/linux/gpio.h**************************************/
#define GPIOF_DIR_OUT (0 << 0)
#define GPIOF_DIR_IN (1 << 0)
#define GPIOF_INIT_LOW (0 << 1)
#define GPIOF_INIT_HIGH (1 << 1)
#define GPIOF_IN (GPIOF_DIR_IN)
#define GPIOF_OUT_INIT_LOW (GPIOF_DIR_OUT | GPIOF_INIT_LOW)
#define GPIOF_OUT_INIT_HIGH (GPIOF_DIR_OUT | GPIOF_INIT_HIGH)
/* Gpio pin is active-low */
#define GPIOF_ACTIVE_LOW (1 << 2)
/* Gpio pin is open drain */
#define GPIOF_OPEN_DRAIN (1 << 3)
/* Gpio pin is open source */
#define GPIOF_OPEN_SOURCE (1 << 4)
#define GPIOF_EXPORT (1 << 5)
#define GPIOF_EXPORT_CHANGEABLE (1 << 6)
#define GPIOF_EXPORT_DIR_FIXED (GPIOF_EXPORT)
#define GPIOF_EXPORT_DIR_CHANGEABLE (GPIOF_EXPORT | GPIOF_EXPORT_CHANGEABLE)
/*用来描述GPIO配置的一个结构体 */
struct gpio {
unsigned gpio; //GPIO引脚号
unsigned long flags; //使用以上GPIOF_*的宏来标记引脚的属性
const char *label; //用来描述引脚的字符串
};
/* 在Kconfig中配置了CONFIG_GPIOLIB选项提供以下接口来操作GPIO口 通过以下的接口会调用gpio_chip上对应的回调函数*/
/********************************************************************************/
static inline int gpio_get_value(unsigned int gpio)
{
return __gpio_get_value(gpio);
}
static inline int __gpio_get_value(unsigned gpio)
{
return gpiod_get_raw_value(gpio_to_desc(gpio));
}
nt gpiod_get_raw_value(const struct gpio_desc *desc)
{
VALIDATE_DESC(desc);
/* Should be using gpio_get_value_cansleep() */
WARN_ON(desc->gdev->chip->can_sleep);
return _gpiod_get_raw_value(desc);
}
static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc)
{
return desc - &desc->gdev->descs[0];
}
static int _gpiod_get_raw_value(const struct gpio_desc *desc)
{
struct gpio_chip *chip;
int offset;
int value;
chip = desc->gdev->chip; //从描述符找到GPIO芯片
offset = gpio_chip_hwgpio(desc); //对应引脚在该芯片上的引脚号偏移
value = chip->get ? chip->get(chip, offset) : -EIO;//调用回调函数获取GPIO的引脚值
value = value < 0 ? value : !!value;
trace_gpio_value(desc_to_gpio(desc), 1, value);
return value;
}
/********************************************************************************/
static inline void gpio_set_value(unsigned int gpio, int value)
{
__gpio_set_value(gpio, value);
}
void gpiod_set_raw_value(struct gpio_desc *desc, int value)
{
VALIDATE_DESC_VOID(desc);
/* Should be using gpiod_set_value_cansleep() */
WARN_ON(desc->gdev->chip->can_sleep);
_gpiod_set_raw_value(desc, value);
}
static void _gpiod_set_raw_value(struct gpio_desc *desc, bool value)
{
struct gpio_chip *chip;
chip = desc->gdev->chip;
trace_gpio_value(desc_to_gpio(desc), 0, value);
if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
_gpio_set_open_drain_value(desc, value);
else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
_gpio_set_open_source_value(desc, value);
else
chip->set(chip, gpio_chip_hwgpio(desc), value); //设置对应的输出值
}
/********************************************************************************/
static inline int gpio_cansleep(unsigned int gpio)
{
return __gpio_cansleep(gpio);
}
static inline int gpio_to_irq(unsigned int gpio)
{
return __gpio_to_irq(gpio);
}
static inline int irq_to_gpio(unsigned int irq)
{
return -EINVAL;
}
#endif /* ! CONFIG_ARCH_HAVE_CUSTOM_GPIO_H */
/* CONFIG_GPIOLIB: bindings for managed devices that want to request gpios */
struct device;
int devm_gpio_request(struct device *dev, unsigned gpio, const char *label);
int devm_gpio_request_one(struct device *dev, unsigned gpio,
unsigned long flags, const char *label);
void devm_gpio_free(struct device *dev, unsigned int gpio);
/*******************************************************include/asm-generic**********************************/
/* Platforms may implement their GPIO interface with library code,
* at a small performance cost for non-inlined operations and some
* extra memory (for code and for per-GPIO table entries).
*
* While the GPIO programming interface defines valid GPIO numbers
* to be in the range 0..MAX_INT, this library restricts them to the
* smaller range 0..ARCH_NR_GPIOS-1.
*
* ARCH_NR_GPIOS is somewhat arbitrary; it usually reflects the sum of
* builtin/SoC GPIOs plus a number of GPIOs on expanders; the latter is
* actually an estimate of a board-specific value.
*/
#ifndef ARCH_NR_GPIOS
#if defined(CONFIG_ARCH_NR_GPIO) && CONFIG_ARCH_NR_GPIO > 0
#define ARCH_NR_GPIOS CONFIG_ARCH_NR_GPIO
#else
#define ARCH_NR_GPIOS 512
#endif
#endif
/*
* "valid" GPIO numbers are nonnegative and may be passed to
* setup routines like gpio_request(). only some valid numbers
* can successfully be requested and used.
*
* Invalid GPIO numbers are useful for indicating no-such-GPIO in
* platform data and other tables.
*/
static inline bool gpio_is_valid(int number)
{
return number >= 0 && number < ARCH_NR_GPIOS;
}
struct device;
struct gpio;
struct seq_file;
struct module;
struct device_node;
struct gpio_desc;
/* caller holds gpio_lock *OR* gpio is marked as requested */
static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
{
return gpiod_to_chip(gpio_to_desc(gpio));
}
/* Always use the library code for GPIO management calls,
* or when sleeping may be involved.
*/
extern int gpio_request(unsigned gpio, const char *label);
extern void gpio_free(unsigned gpio);
static inline int gpio_direction_input(unsigned gpio)
{
return gpiod_direction_input(gpio_to_desc(gpio));
}
static inline int gpio_direction_output(unsigned gpio, int value)
{
return gpiod_direction_output_raw(gpio_to_desc(gpio), value);
}
static inline int gpio_set_debounce(unsigned gpio, unsigned debounce)
{
return gpiod_set_debounce(gpio_to_desc(gpio), debounce);
}
static inline int gpio_get_value_cansleep(unsigned gpio)
{
return gpiod_get_raw_value_cansleep(gpio_to_desc(gpio));
}
static inline void gpio_set_value_cansleep(unsigned gpio, int value)
{
return gpiod_set_raw_value_cansleep(gpio_to_desc(gpio), value);
}
/* A platform's <asm/gpio.h> code may want to inline the I/O calls when
* the GPIO is constant and refers to some always-present controller,
* giving direct access to chip registers and tight bitbanging loops.
*/
static inline int __gpio_get_value(unsigned gpio)
{
return gpiod_get_raw_value(gpio_to_desc(gpio));
}
static inline void __gpio_set_value(unsigned gpio, int value)
{
return gpiod_set_raw_value(gpio_to_desc(gpio), value);
}
static inline int __gpio_cansleep(unsigned gpio)
{
return gpiod_cansleep(gpio_to_desc(gpio));
}
static inline int __gpio_to_irq(unsigned gpio)
{
return gpiod_to_irq(gpio_to_desc(gpio));
}
extern int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
extern int gpio_request_array(const struct gpio *array, size_t num);
extern void gpio_free_array(const struct gpio *array, size_t num);
/*将对应的引脚导入的文件系统中 */
static inline int gpio_export(unsigned gpio, bool direction_may_change)
{
return gpiod_export(gpio_to_desc(gpio), direction_may_change);
}
int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
{
struct gpio_chip *chip;
struct gpio_device *gdev;
struct gpiod_data *data;
unsigned long flags;
int status;
const char *ioname = NULL;
struct device *dev;
int offset;
/* can't export until sysfs is available ... */
if (!gpio_class.p) {
pr_debug("%s: called too early!\n", __func__);
return -ENOENT;
}
if (!desc) {
pr_debug("%s: invalid gpio descriptor\n", __func__);
return -EINVAL;
}
gdev = desc->gdev;
chip = gdev->chip;
mutex_lock(&sysfs_lock);
/* check if chip is being removed */
if (!chip || !gdev->mockdev) {
status = -ENODEV;
goto err_unlock;
}
spin_lock_irqsave(&gpio_lock, flags);
if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
test_bit(FLAG_EXPORT, &desc->flags)) {
spin_unlock_irqrestore(&gpio_lock, flags);
gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d)\n",
__func__,
test_bit(FLAG_REQUESTED, &desc->flags),
test_bit(FLAG_EXPORT, &desc->flags));
status = -EPERM;
goto err_unlock;
}
spin_unlock_irqrestore(&gpio_lock, flags);
/*gpiod_data 分配空间*/
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
status = -ENOMEM;
goto err_unlock;
}
data->desc = desc;
mutex_init(&data->mutex);
if (chip->direction_input && chip->direction_output)
data->direction_can_change = direction_may_change;
else
data->direction_can_change = false;
offset = gpio_chip_hwgpio(desc);
if (chip->names && chip->names[offset])
ioname = chip->names[offset];
/* 创建一个device 并且在文件系统中注册 */
dev = device_create_with_groups(&gpio_class, &gdev->dev,
MKDEV(0, 0), data, gpio_groups,
ioname ? ioname : "gpio%u",
desc_to_gpio(desc));
if (IS_ERR(dev)) {
status = PTR_ERR(dev);
goto err_free_data;
}
/* 设置导入的标志 */
set_bit(FLAG_EXPORT, &desc->flags);
mutex_unlock(&sysfs_lock);
return 0;
err_free_data:
kfree(data);
err_unlock:
mutex_unlock(&sysfs_lock);
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
return status;
}
static inline int gpio_export_link(struct device *dev, const char *name,
unsigned gpio)
{
return gpiod_export_link(dev, name, gpio_to_desc(gpio));
}
static inline void gpio_unexport(unsigned gpio)
{
gpiod_unexport(gpio_to_desc(gpio));
}