您现在的位置是:首页 >学无止境 >gpio_device,gpio_chip 和 gpio_desc网站首页学无止境
gpio_device,gpio_chip 和 gpio_desc
简介gpio_device,gpio_chip 和 gpio_desc
Linux version: 4.14
Code link: Linux source code (v4.14) - Bootlin
1 gpio_device,gpio_chip 和 gpio_desc
(1)每个 GPIO 控制器 (Bank) 对应一个 gpio_device 结构。在这个结构中,包含了 gpio_chip 和 gpio_desc 。由于 gpio_device 代表一个 Bank ,一般的 GPIO 外设有多个 Bank ,所以在内核中用链表来组织 gpio_device。
struct gpio_device { /* 每个 GPIO 控制器对应一个 gpio_device 结构 */
int id; /* GPIO 控制器对应的 id 号 */
struct device dev; /* GPIO 设备结构 */
struct cdev chrdev; /* GPIO 设备的字符设备 */
struct device *mockdev; /* class设备被弃用的sysfs接口使用(可能是空)*/
struct module *owner; /* 有助于防止删除导出活动GPIO的模块 */
struct gpio_chip *chip; /* 指向相应gpiochip的指针,保存该设备的静态数据 */
struct gpio_desc *descs; /* ngpio描述符数组(gpio 引脚描述符数组) */
int base; /* DEPRECATED全局GPIO数字空间中的GPIO基数,在设备创建时分配 */
u16 ngpio; /* 该GPIO控制器上GPIO引脚的数量,等于descs数组的大小 */
char *label; /* GPIO设备的描述性名称,例如片上系统中IP组件的部件号或名称 */
void *data; /* 驱动程序分配的每个实例数据 */
struct list_head list; /* 用于将gpio_device链接在一起进行遍历 */
#ifdef CONFIG_PINCTRL
/*
* 如果CONFIG_PINCTRL被启用,
* 那么gpio控制器可以选择性地描述它们在SoC中服务的实际引脚范围。
* PINCTRL子系统将使用这些信息来配置gpio使用的相应引脚。
*/
struct list_head pin_ranges;
#endif
};
(2)gpio_chip 结构是对 gpio 引脚相关控制操作的抽象。在底层对接到 Gpiolib 的时候,就是对 gpio_chip 结构体相关的成员进行赋值,并最终调用 gpiochip_add_data 或者,将其注册到了内核的 Gpiolib 子系统。该结构主要包含:
①引脚相关的控制函数
②引脚相关的中断函数
③引脚相关信息
struct gpio_chip { // 抽象gpio控制器
// GPIO设备的功能名称,例如部件号或实现它的SoC IP块的名称。
const char *label;
// 内部状态持有者,不透明结构
struct gpio_device *gpiodev;
// 提供GPIO的可选父设备
struct device *parent;
// 有助于防止删除导出活动GPIO的模块
struct module *owner;
// 用于芯片特定激活的可选钩子函数,例如启用模块电源和时钟;可以休眠
int (*request)(struct gpio_chip *chip, unsigned offset);
// 用于芯片特定停用的钩子函数,例如禁用模块电源和时钟;可以休眠
void (*free)(struct gpio_chip *chip, unsigned offset);
// 返回信号“offset”的方向,0=out,1=in,(与GPIOF_DIR_XXX相同),或负错误
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);
// 返回信号“偏移”的值,0=低,1=高,或负错误
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);
// 用于各种设置的可选挂钩。使用与通用pinconf相同的压缩配置格式
int (*set_config)(struct gpio_chip *chip,
unsigned offset, unsigned long config);
// 支持非静态gpio_to_irq()映射的可选钩子;实现可能无法休眠
int (*to_irq)(struct gpio_chip *chip, unsigned offset);
// 在debugfs中显示内容的可选例程;省略时将使用默认代码,但自定义代码可以显示额外的状态
// 如上拉/下拉配置
void (*dbg_show)(struct seq_file *s, struct gpio_chip *chip);
int base; // 识别由该芯片处理的第一个GPIO号码;或者,
// 如果在注册过程中为负数,则请求动态ID分配。
// DEPRECATION:
// 不推荐提供任何非负数的内容并固定GPIO芯片的基本偏移量。
// 请将-1作为基数,让gpiolib在所有可能的情况下选择芯片基数。
// 从长远来看,我们希望摆脱静态GPIO数字空间。
u16 ngpio; // 该控制器处理的GPIO数量;最后处理的GPIO是(base+ngpio-1)
const char *const *names; // 如果设置,必须是一个字符串数组,用作该芯片中GPIO
// 的替代名称。如果GPIO没有别名,则数组中的任何条目
// 都可能为NULL,但数组的长度必须为@ngpio条目。
// 一个名称可以包含一个用于无符号int的printk格式说明符。
// 它被gpio的实际数字所取代。
bool can_sleep; // 必须在get()/set()方法休眠时设置flag,因为
// 它们在通过I2C或SPI访问GPIO扩展器芯片时必须这样做。
// 这意味着,如果芯片支持IRQ,则这些IRQ需要被线程化,
// 因为芯片访问可能在例如读取IRQ状态寄存器时休眠。
#if IS_ENABLED(CONFIG_GPIO_GENERIC)
// 通用GPIO的读函数
unsigned long (*read_reg)(void __iomem *reg);
// 通用GPIO的写函数
void (*write_reg)(void __iomem *reg, unsigned long data);
// 一些通用GPIO控制器使用big-endian位表示法,例如在8位寄存器中,GPIO7是最低有效位。
// 此回调分配正确的位掩码。
unsigned long (*pin2mask)(struct gpio_chip *gc, unsigned int pin);
void __iomem *reg_dat; // 通用GPIO的数据(in)寄存器
void __iomem *reg_set; // 通用GPIO的输出设置寄存器(out=高)
void __iomem *reg_clr; // 通用GPIO的输出清除寄存器(out=低)
void __iomem *reg_dir; // 通用GPIO的方向设置寄存器
int bgpio_bits; // 用于通用GPIO的寄存器位数,即<寄存器宽度>*8
// 用于锁定芯片->bgpio_data。此外,
// 这也是保持影子数据寄存器和真实数据寄存器一起写入所必需的
spinlock_t bgpio_lock;
// 通用GPIO的阴影数据寄存器,用于安全地清除/设置位。
unsigned long bgpio_data;
// 通用GPIO的阴影方向寄存器,用于安全地清除/设置方向。
unsigned long bgpio_dir;
#endif
#ifdef CONFIG_GPIOLIB_IRQCHIP
/*
* 通过CONFIG_GPIOLIB_IRQCHIP,我们在GPIOLIB中获得了一个irq芯片,
* 用于处理大多数实际情况下的IRQ。
*/
// GPIO IRQ芯片impl,由GPIO驱动程序提供
struct irq_chip *irqchip;
// 中断翻译域;负责GPIO hwirq号码和linux irq号码之间的映射
struct irq_domain *irqdomain;
// 分配给GPIO irq芯片的第一个linux irq编号(已弃用)
unsigned int irq_base;
// 用于GPIO irq的irq处理程序(通常是预定义的irq核心函数),由GPIO驱动程序提供
irq_flow_handler_t irq_handler;
// GPIO驱动程序初始化时应用的默认irq触发类型,由GPIO驱动提供
unsigned int irq_default_type;
// GPIO irq芯片父级/库linux irq号,由GPIO驱动程序提供,用于链式中断(不用于嵌套中断)
unsigned int irq_chained_parent;
// 如果设置中断处理是嵌套的,则为True。
bool irq_nested;
// 如果set core分配irq_valid_mask时所有位都设置为1
bool irq_need_valid_mask;
// 如果不是%NULL,则保留GPIO的位掩码,这些GPIO有效地包含在芯片的irq域中
unsigned long *irq_valid_mask;
// 每个GPIO IRQ芯片lockdep类
struct lock_class_key *lock_key;
#endif
#if defined(CONFIG_OF_GPIO)
/*
* 如果启用CONFIG_OF,则设备树中描述的所有GPIO控制器都可能自动进行OF转换
*
*/
/**
* @of_node:
*
* 指向表示此GPIO控制器的设备树节点的指针。
*/
struct device_node *of_node;
/**
* @of_gpio_n_cells:
*
* 用于形成GPIO说明符的单元格数。
*/
unsigned int of_gpio_n_cells;
/**
* @of_xlate:
*
* 回调,将 gpiospec 转换为 gpio 编号和标志
* 返回值就是 gpio 引脚编号,标志信息保存在 flags 中
*
*/
int (*of_xlate)(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec, u32 *flags);
#endif
};
// gpio_chip可以帮助平台抽象各种gpio源,以便通过公共编程接口访问它们。
// 示例源包括SOC控制器、FPGA、多功能芯片、专用gpio扩展器等。
// 每个芯片控制多个信号,这些信号在方法调用中标识通过在0..(@ngpio-1)范围内的“偏移”值。
// 当这些信号通过gpio_get_value(gpio)等调用引用时,
// 偏移量是通过从gpio数中减去@base来计算的。
(3)gpio_desc 结构可以认为是一个gpio引脚的描述符。这个结构比较简单,它包含了 gdev、flag、label 和 name 属性。其中 gdev 指针指向了 gpio_desc 所属的 gpio_device,而 flag 描述了当前这个gpio 引脚的属性状态。
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;
};
(4)编写 GPIO 驱动程序的大致思路
① gpiod_get获得一个gpiod指针
② 根据gpiod找到GPIO控制器,使用gpiod_set_value来设置输出的高低电平
2 参考:
GPIO子系统层次与数据结构_gpio_desc_习惯就好zz的博客-CSDN博客
Linux内核4.14版本——GPIO子系统(1)——gpiolib分析_linux gpio子系统_风雨兼程8023的博客-CSDN博客
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。