Linux GPIO简介

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 映射到 IRQ
      GPIO 编号是无符号整数;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 是沿触发时初始化或更新驱动状态。注意某些平台不支持反映射,所以你应该尽量避免使用它。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值