General Purpose Input Output (通用输入/输出)简称为GPIO,GPIO的驱动主要就是读取GPIO口的状态,设置GPIO口的状态,作为IRQ信号使用等。
linux中若要支持GPIO驱动,则需要定义如下宏:
CONFIG_GENERIC_GPIO=y
然而,为了让其它驱动可以方便的操作到GPIO,在linux里实现了对GPIO操作的统一接口,这个接口实则上就是GPIO驱动的框架,称为GPIO-LIB框架。若至此该框架则需要打开如下宏,表示将其编译进内内核里:
CONFIG_ARCH_REQUIRE_GPIOLIB=y
CONFIG_GPIOLIB=y
1. 在框架中每个 GPIO 控制器都包装为一个 "struct gpio_chip" ,它包含了该类型的每个控制器的常用信息,其中包含了操作GPIO的所有方法。
项目平台中的GPIO控制器分为如下四种:
/* EIC Enhanced Interrupt Controller增强型中断控制器
* GPIO&EIC bank and number summary:
*
* Bank From To NR Type bank name
* 1 0 ~ 15 16 EIC sprd-d-eic
* 2 16 ~ 271 256 GPIO sprd-d-gpio
* 3 272 ~ 287 16 ANA EIC sprd-a-eic
* 4 288 ~ 319 32 ANA GPIO sprd-a-gpio
*/
至于gpio_chip结构体如下所示:
//代表系统中一个GPIO控制器Bank
struct gpio_chip {
const char *label; // 对应为bank name
struct device *dev; // dev file
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);
int (*get)(struct gpio_chip *chip,
unsigned offset);
int (*direction_output)(struct gpio_chip *chip,
unsigned offset, int value);
void (*set)(struct gpio_chip *chip,
unsigned offset, int value);
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个数
char **names;
unsigned can_sleep:1;
unsigned exported:1;
};
在软件上,对应每一个控制器,都需要实现一个继承gpio_chip的结构体,例如:
static struct sci_gpio_chip d_sci_gpio = {
.chip.label = "sprd-d-gpio", // bank name
.chip.request = sci_gpio_request,
.chip.free = sci_gpio_free,
.chip.direction_input = sci_gpio_direction_input,
.chip.get = sci_gpio_get,
.chip.direction_output = sci_gpio_direction_output,
.chip.set = sci_gpio_set,
.chip.set_debounce = sci_gpio_set_debounce,
.chip.to_irq = sci_gpio_to_irq,
.group_offset = GPIO_GROUP_OFFSET,
.read_reg = d_read_reg,
.write_reg = d_write_reg,
.set_bits = d_set_bits,
.clr_bits = d_clr_bits,
};
最后,在GPIO驱动模块里的probe函数里,通过gpiochip_add(&d_sci_gpio.chip),将d_sci_gpio.chip添加到全局数组gpio_desc[]里。
2. 每一个GPIO口则用“struct gpio_desc”表示,存放在全局数组gpio_desc中,其中ARCH_NR_GPIOS则表示平台所支持的GPIO的数量;
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; //ARCH_NR_GPIOS=320
struct gpio_desc {
struct gpio_chip *chip;
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#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 */
#define FLAG_OPEN_DRAIN 8 /* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 9 /* Gpio is open source type */
#define ID_SHIFT 16 /* add new flags before this one */
#define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1)
#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
#ifdef CONFIG_DEBUG_FS
const char *label;
#endif
};
3. GPIO框架图如下:
图中,右上方部分为GPIO驱动对其它驱动提供的GPIO操作接口;
右下方部分为gpio_chip结构体中封装的GPIO硬件操作接口,也就是说对外提供的操作接口最终会调用到这里。
左半部分为全局数组gpio_desc[gpio_number],数组里存放着每个gpio控制器的gpio_chip;即其它驱动通过GPIO number和数组 gpio_desc定位到相应的gpio_chip的
4. Pinmap配置
在uboot启动过程中,会初始化配置每个PIN脚初始状态
int pin_init(void)
{
int i;
for (i = 0; i < sizeof(pinmap)/sizeof(pinmap[0]); i++){
__raw_writel(pinmap[i].val, CTL_PIN_BASE + pinmap[i].reg);
}
for (i = 0; i < sizeof(pinmap_ana)/sizeof(pinmap_ana[0]); i++){
ANA_REG_SET(ANA_PIN_BASE+pinmap_ana[i].reg,pinmap_ana[i].val);
}
return 0;
}
static pinmap_t pinmap[] ={
... ...
... ...
{REG_PIN_SCL1, BIT_PIN_SLP_AP|BIT_PIN_WPUS|BITS_PIN_DS(1)|BITS_PIN_AF(0)|BIT_PIN_WPU|BIT_PIN_SLP_WPU|BIT_PIN_SLP_Z},
{REG_PIN_SDA1, BIT_PIN_SLP_AP|BIT_PIN_WPUS|BITS_PIN_DS(1)|BITS_PIN_AF(0)|BIT_PIN_WPU|BIT_PIN_SLP_WPU|BIT_PIN_SLP_Z},
... ...
... ...
}
5. 常用GPIO操作接口
1). 标识 GPIO
GPIO 是通过无符号整型来标识的,范围是 0 到 MAX_INT。平台会定义这些整数的用法,且通常使用 #define 来定义 GPIO ,这样板级特定的启动代码可以直接关联相应的原理图。相对来说,驱动应该仅使用启动代码传递过来的 GPIO 编号,使用 platform_data 保存板级特定引脚配置数据(同时还有其他须要的板级特定数据),避免可能出现的问题。
测试一个结构体中的编号是否关联一个 GPIO,你可使用以下断言:
int gpio_is_valid(int number);
2). 使用GPIO
对于一个 GPIO,系统应该做的第一件事情就是通过 gpio_request() 函数分配它,见下文。而接下来要做的是标识它的方向,这通常是在板级启动代码中为 GPIO 设置一个 platform_device 时做的。
/* 设置为输入或输出, 返回 0 或负的错误代码 */
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
对于作为输出的 GPIO,为其提供初始输出值,对于避免在系统启动期间出现信号毛刺是很有帮助的。
3). 访问自旋锁安全的 GPIO
大多数 GPIO 控制器可以通过内存读/写指令来访问。这些指令不会休眠,可以安全地在硬(非线程)中断例程和类似的上下文中完成。对于那些用 gpio_cansleep()测试总是返回失败的 GPIO(见下文),使用以下的函数访问:
/* GPIO 输入:返回零或非零 */
int gpio_get_value(unsigned gpio);
/* GPIO 输入 */
void gpio_set_value(unsigned gpio, int value);
返回值是布尔值,零表示低电平,非零表示高电平。当读取一个输出引脚的值时,返回值应该是引脚上的值。这个值不总是和输出值相符,因为存在开漏输出信号和输出潜伏期的问题。
4). 访问可能休眠的 GPIO
某些 GPIO 控制器必须通过基于总线(如 I2C 或 SPI)的消息访问。读或写这些 GPIO 值的命令需要等待其信息排到队首才发送命令,再获得其反馈。期间需要休眠,这不能在 IRQ 例程(中断上下文)中执行。支持此类 GPIO 的平台通过以下函数返回非零值来区分出这种 GPIO。(此函数需要一个之前通过 gpio_request 分配到的有效的 GPIO 编号):
int gpio_cansleep(unsigned gpio);
为了访问这种 GPIO,内核定义了一套不同的函数:/* GPIO 输入:返回零或非零 ,可能会休眠 */
int gpio_get_value_cansleep(unsigned gpio);
/* GPIO 输入,可能会休眠 */
void gpio_set_value_cansleep(unsigned gpio, int value);
访问这样的 GPIO 需要一个允许休眠的上下文,例如线程 IRQ 处理例程,并用以上的访问函数替换那些没有 cansleep()后缀的自旋锁安全访问函数。除了这些访问函数可能休眠,且它们操作的 GPIO 不能在硬件 IRQ 处理例程中访问的事实,这些处理例程实际上和自旋锁安全的函数是一样的。
5). 声明和释放 GPIO
为了有助于捕获系统配置错误,定义了两个函数。
/* 申请 GPIO, 返回 0 或负的错误代码.
* 非空标签可能有助于诊断.
*/
int gpio_request(unsigned gpio, const char *label);
/* 释放之前声明的 GPIO */
void gpio_free(unsigned gpio);
这个函数完成两个基本的目标。一是标识那些实际上已作为 GPIO 使用的信号线,这样便于更好地诊断;系统可能需要服务几百个潜在的 GPIO,但是对于任何一个给定的电路板通常只有一些被使用。另一个目的是捕获冲突,查明错误:如两个或更多驱动错误地认为他们已经独占了某个信号线,或是错误地认为移除一个管理着某个已激活信号的驱动是安全的。也就是说,申请 GPIO 的作用类似一种锁机制。注意:申请一个 GPIO 并没有以任何方式配置它,只不过标识那个 GPIO 处于使用状态。必须有另外的代码来处理引脚配置(如控制 GPIO 使用的引脚、上拉/下拉)。
考虑到大多数情况下声明 GPIO 之后就会立即配置它们,所以定义了以下三个辅助函数:
/* 申请一个 GPIO 信号, 同时通过特定的'flags'初始化配置,
* 其他和 gpio_request()的参数和返回值相同
*
*/
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);
6). GPIO 映射到 IRQGPIO 编号是无符号整数;IRQ 编号也是。这些构成了两个逻辑上不同的命名空间(GPIO 0 不一定使用 IRQ 0)。你可以通过以下函数在它们之间实现映射:
/* 映射 GPIO 编号到 IRQ 编号 */
int gpio_to_irq(unsigned gpio);
/* 映射 IRQ 编号到 GPIO 编号 (尽量避免使用) */
int irq_to_gpio(unsigned irq);
它们的返回值为对应命名空间的相关编号,或是负的错误代码(如果无法映射)。(例如,某些 GPIO 无法做为 IRQ 使用。)以下的编号错误是未经检测的:使用一个未通过 gpio_direction_input()配置为输入的 GPIO 编号,或者使用一个并非来源于gpio_to_irq()的 IRQ 编号。
这两个映射函数可能会在信号编号的加减计算过程上花些时间。它们不可休眠。
gpio_to_irq()返回的非错误值可以传递给 request_irq()或者 free_irq()。它们通常通过板级特定的初始化代码存放到平台设备的 IRQ 资源中。注意:IRQ 触发选项是 IRQ 接口的一部分,比如 IRQF_TRIGGER_FALLING,系统唤醒能力也是如此。
irq_to_gpio()返回的非错误值大多数通常可以被 gpio_get_value()所使用,比如在 IRQ 是沿触发时初始化或更新驱动状态。注意某些平台不支持反映射,所以你应该尽量避免使用它。