Linux驱动之GPIO初始化

开发平台iTOP4412,主机windows + 虚拟机Ubuntu,内核源码iTOP4412_Kernel_3.0.15,

Source Insight查看内核代码,EXYNOS4412的DATASHEET,可以直接下载(完整版)

首先在内核源码目录下使用命令 ls drivers/gpio/*.o 查看哪些.c文件被编译进内核

查看./drivers/gpio/gpio-exynos4.c ,跳转到文件的最后一行:

core_initcall(exynos4_gpiolib_init);

跳转到exynos4_gpiolib_init函数定义的地方,就在“core_initcall(exynos4_gpiolib_init);”的上方

static __init int exynos4_gpiolib_init(void)
{
	struct s3c_gpio_chip *chip;
	int i;
	int nr_chips;

	/* GPIO common part  */

	chip = exynos4_gpio_common_4bit;                              //注意这个结构体
	nr_chips = ARRAY_SIZE(exynos4_gpio_common_4bit);

	for (i = 0; i < nr_chips; i++, chip++) {
		if (chip->config == NULL)
			chip->config = &gpio_cfg;
		if (chip->base == NULL)
			pr_err("No allocation of base address for [common gpio]");
	}

	samsung_gpiolib_add_4bit_chips(exynos4_gpio_common_4bit, nr_chips);

	/* Only 4210 GPIO  part */
	if (soc_is_exynos4210()) {
		chip = exynos4210_gpio_4bit;
		nr_chips = ARRAY_SIZE(exynos4210_gpio_4bit);

		for (i = 0; i < nr_chips; i++, chip++) {
			if (chip->config == NULL)
				chip->config = &gpio_cfg;
			if (chip->base == NULL)
				pr_err("No allocation of base address [4210 gpio]");
		}

		samsung_gpiolib_add_4bit_chips(exynos4210_gpio_4bit, nr_chips);
	} else {
	/* Only 4212/4412 GPIO part */
		chip = exynos4212_gpio_4bit;
		nr_chips = ARRAY_SIZE(exynos4212_gpio_4bit);

		for (i = 0; i < nr_chips; i++, chip++) {
			if (chip->config == NULL)
				chip->config = &gpio_cfg;
			if (chip->base == NULL)
				pr_err("No allocation of base address [4212 gpio]");
		}

		samsung_gpiolib_add_4bit_chips(exynos4212_gpio_4bit, nr_chips);
	}

	s5p_register_gpioint_bank(IRQ_GPIO_XA, 0, IRQ_GPIO1_NR_GROUPS);
	s5p_register_gpioint_bank(IRQ_GPIO_XB, IRQ_GPIO1_NR_GROUPS, IRQ_GPIO2_NR_GROUPS);

	return 0;
}
这个函数定义了三星EXYNOS系列不同产品的GPIO,我的开发平台是EXYNOS4412,因此只看通用的这部分就行了,

注意chip = exynos4_gpio_common_4bit;这个结构体,跳转到exynos4_gpio_common_4bit定义的地方;

在内核源码目录下./drivers/gpio/gpio-exynos4.c中:

#if defined(CONFIG_MTK_COMBO_COMM) || defined(CONFIG_MTK_COMBO_COMM_MODULE)	//add by cym 20130301
struct s3c_gpio_chip exynos4_gpio_common_4bit[] = {
#else
static struct s3c_gpio_chip exynos4_gpio_common_4bit[] = {	//结构体数组,结构体类型为s3c_gpio_chip,等下再看这个结构体类型
#endif
	{	。
		。
		。
		。
		。
		。
		
		}, {						//我们取中间这个结构体,前后都是类似的结构体
		.base   = (S5P_VA_GPIO2 + 0x100),
		.eint_offset = 0x20,
		.group	= 22,
		.chip	= {
			.base	= EXYNOS4_GPL2(0),		
			.ngpio	= EXYNOS4_GPIO_L2_NR,
			.label	= "GPL2",
		},
	}, {
		。
		。
		。
		。
		。
		。
	}
接着我们来分析这个结构体数组中的元素,把它单独弄出来:我们先看一下成员chip,

跳转至s3c_gpio_chip这个结构体,定义在./arch/arm/plat-samsung/include/plat/gpio-core.h中

struct s3c_gpio_chip {
	struct gpio_chip	chip;
	struct s3c_gpio_cfg	*config;
	struct s3c_gpio_pm	*pm;
	void __iomem		*base;
	int			irq_base;
	int			group;
	unsigned int		eint_offset;
	spinlock_t		lock;
#ifdef CONFIG_PM
	u32			pm_save[4];
#endif
};

我们发现成员chip依然是一个类型为gpio_chip的结构体,结构体gpio_chip定义在./include/asm-generic/gpio.h中,

struct gpio_chip {
	const char		*label;										//成员1
	struct device		*dev;
	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);
	int			(*set_debounce)(struct gpio_chip *chip,
						unsigned offset, unsigned debounce);

	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;											//成员2
	u16			ngpio;											//成员3
	const char		*const *names;
	unsigned		can_sleep:1;
	unsigned		exported:1;

#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, struct device_node *np,
		        const void *gpio_spec, u32 *flags);
#endif
};

在这里我们再回到前面结构体数组中的元素chip中的成员

.chip	= {
			.base	= EXYNOS4_GPL2(0),			
			.ngpio	= EXYNOS4_GPIO_L2_NR,
			.label	= "GPL2",
		},

首先是.base = EXYNOS4_GPL2(0),顾名思义,表示GPL2(0)基地址的意思,但这个地址是多少呢?接着往下看

#define EXYNOS4_GPL2(_nr)	(EXYNOS4_GPIO_L2_START + (_nr)) 	//在内核源码目录下./arch/arm/mach-exynos/include/mach/gpio-exynos4.h中宏定义
同样的,查看到EXYNOS4_GPIO_L2_START是枚举类型exynos4_gpio_number的元素

enum exynos4_gpio_number {										//在内核源码目录下./arch/arm/mach-exynos/include/mach/gpio-exynos4.h中宏定义
	EXYNOS4_GPIO_A0_START		= 0,
	EXYNOS4_GPIO_A1_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_A0),
	EXYNOS4_GPIO_B_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_A1),
	EXYNOS4_GPIO_C0_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_B),
	EXYNOS4_GPIO_C1_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_C0),
	EXYNOS4_GPIO_D0_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_C1),
	EXYNOS4_GPIO_D1_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_D0),
	EXYNOS4_GPIO_F0_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_D1),
	EXYNOS4_GPIO_F1_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_F0),
	EXYNOS4_GPIO_F2_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_F1),
	EXYNOS4_GPIO_F3_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_F2),
	EXYNOS4_GPIO_K0_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_F3),
	EXYNOS4_GPIO_K1_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_K0),
	EXYNOS4_GPIO_K2_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_K1),
	EXYNOS4_GPIO_K3_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_K2),
	EXYNOS4_GPIO_L0_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_K3),
	EXYNOS4_GPIO_L1_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_L0),
	EXYNOS4_GPIO_L2_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_L1),				//☆☆☆☆☆☆☆☆☆☆☆☆☆
	EXYNOS4_GPIO_X0_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_L2),
	EXYNOS4_GPIO_X1_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_X0),
	EXYNOS4_GPIO_X2_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_X1),
	EXYNOS4_GPIO_X3_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_X2),
	EXYNOS4_GPIO_Y0_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_X3),
	EXYNOS4_GPIO_Y1_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_Y0),
	EXYNOS4_GPIO_Y2_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_Y1),
	EXYNOS4_GPIO_Y3_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_Y2),
	EXYNOS4_GPIO_Y4_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_Y3),
	EXYNOS4_GPIO_Y5_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_Y4),
	EXYNOS4_GPIO_Y6_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_Y5),
	EXYNOS4_GPIO_Z_START		= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_Y6),
};
再跳到EXYNOS4_GPIO_NEXT宏定义处:

#define EXYNOS4_GPIO_NEXT(__gpio) \										//在内核源码目录下./arch/arm/mach-exynos/include/mach/gpio-exynos4.h中宏定义
	((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1)
注意这个特殊的宏定义,##连接符号是把传递过来的参数当成字符串进行代替,例如这里的参数是EXYNOS4_GPIO_L1,其中CONFIG_S3C_GPIO_SPACE 也是一个宏定义,其值就是为0,这个宏定义表示的意思就是EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_L1) = EXYNOS4_GPIO_L1_START + EXYNOS4_GPIO_L1_NR + 1;

这个意思就很明显了,GPIO_L1的下一个GPIO的起始地址就是GPIO_L1的起始地址加上可以配置为GPIO的EXYNOS4_GPIO_L1_NR个数再加1(有点绕口,可能是我没表述清楚,继续往下看),比如说EXYNOS4_GPIO_L1_NR=3,那么GPIO_L1的下一个GPIO的起始地址 = EXYNOS4_GPIO_L1_START + 4,一层层的调用最终可以追溯到内存中表示GPIO的最小地址。

再看元素.ngpio = EXYNOS4_GPIO_L2_NR

宏定义#define EXYNOS4_GPIO_L2_NR	(8) 								//在内核源码目录下./arch/arm/mach-exynos/include/mach/gpio-exynos4.h中宏定义
刚好如前面有类似之处,为了验证我所说的,我们看看EXYNOS4412的DATASHEET

EXYNOS4_GPIO_L2_NR = 8 正好对应GPL2CON控制寄存器,其中可以用作为GPIO分别是KP_COL[7:0]共8个GPIO。(图片麻烦,可以下载链接的数据手册看看)

第三个元素.label = "GPL2",就一个名称,没什么好说的。

着重来看虚拟地址与物理地址的映射关系

.base   = (S5P_VA_GPIO2 + 0x100),			//基地址 = 虚拟地址 + 偏移地址
数据手册中关于GPL2CON控制寄存器是这样描述的:

Base Address: 0x1100_0000                                                        //基地址

Address = Base Address + 0x0100, Reset Value = 0x0000_0000      //物理地址 = 基地址 + 偏移地址, 复位值 = 0x0000_0000

当然datasheet里的地址都是实际的物理地址,基地址为0x1100_0000,

偏移地址为0x0100与.base   = (S5P_VA_GPIO2 + 0x100)中的0x100一致,这没什么好说的。

主要是这个S5P_VA_GPIO2,到底是如何映射的?

用Source Insight来查找有关的S5P_VA_GPIO2定义:

在内核源码目录下./arch/arm/mach-exynos/cpu-exynos4.c中有定义

static struct map_desc exynos4_iodesc[] __initdata = {
					。
					。
					。
					。
					。
					。
					
	}, {
		.virtual	= (unsigned long)S5P_VA_GPIO2,			//此处定义
		.pfn		= __phys_to_pfn(EXYNOS4_PA_GPIO2),
		.length		= SZ_4K,
		.type		= MT_DEVICE,
	}, {	
	
					。
					。
					。
					。
					。
					。
}
S5P_VA_GPIO2为结构体数组元素的一个成员赋的值:

类似前面的方法,先看看成员.virtual    = (unsigned long)S5P_VA_GPIO2,

#define S5P_VA_GPIO2		S3C_ADDR(0x02240000)		////在内核源码目录下./arch/arm/plat-s5p/include/plat/map-s5p.h中宏定义

跳转到S3C_ADDR定义的地方,在内核源码目录下./arch/arm/plat-samsung/include/plat/map-base.h中宏定义

#define S3C_ADDR_BASE	0xF6000000

#ifndef __ASSEMBLY__
#define S3C_ADDR(x)	((void __iomem __force *)S3C_ADDR_BASE + (x))
#else
#define S3C_ADDR(x)	(S3C_ADDR_BASE + (x))		
#endif

到这里我们可以看到这个虚拟地址的值是0xF6000000 + 0x02240000(同样是 基地址 + 偏移地址);显然这个地址值表示的是在3G~4G之间的范围,超过了内存1G的最大值,当然就是虚拟地址了。
再看成员.pfn = __phys_to_pfn(EXYNOS4_PA_GPIO2),

#define	__phys_to_pfn(paddr)	((paddr) >> PAGE_SHIFT)
这里就是表示物理地址怎么转换为虚拟地址,简单的说就是MMU一个换页的思想,当然其中还有有很多的道道,详细的可以参考一下其他资料。
#define EXYNOS4_PA_GPIO2        0x11000000        //在内核源码目录下./arch/arm/mach-exynos/include/mach/map-exynos4.h中宏定义
此刻,终于找到了实际的物理地址0x11000000这个地址值了,与前面datasheet中的物理地址也对应上了,皆大欢喜。

成员.length = SZ_4K 则表示映射块的大小,我们也来看一下它的值,顾名思义表示4K块大小

#define SZ_4K				0x00001000			//在内核源码目录下./include/asm-generic/sizes.h中宏定义
成员.type = MT_DEVICE,一般也没怎么管,其值就是为0, 没多大意义,也不是我们关注的点。

到此,我们理清了内核是如何实现GPIO物理地址到虚拟地址映射的,但是其中涉及到MMU的工作机制,还值得深入研究,再多捋一捋,无非就是查找调用的函数和一些宏定义,过程也不是很复杂,其实这也是平时阅读内核代码的一种常用手段。

接下来再看一下GPIO底层的调用过程

在内核源码目录下./arch/arm/plat-samsung/include/plat/gpio-cfg.h中定义了这几个函数

/*GPIO引脚功能配置*/
extern int s3c_gpio_cfgpin(unsigned int pin, unsigned int to);		//第一个参数表示GPIO的引脚号,例如EXYNOS4_GPL2(0),第二个参数选择以下三个宏定义中的一个

/* Defines for generic pin configurations */
#define S3C_GPIO_INPUT	(S3C_GPIO_SPECIAL(0))			//作为输入引脚使用
#define S3C_GPIO_OUTPUT	(S3C_GPIO_SPECIAL(1))			//作为输出引脚使用
#define S3C_GPIO_SFN(x)	(S3C_GPIO_SPECIAL(x))			//作为其它特殊功能使用 
我们看一下函数原型:

int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)		//在内核源码目录下./arch/arm/plat-samsung/gpio-config.c
{
	struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin);		//在前面初始化时讨论过这个结构体类型,与前面的一致
	unsigned long flags;
	int offset;
	int ret;

	if (!chip)
		return -EINVAL;

	offset = pin - chip->chip.base;

	s3c_gpio_lock(chip, flags);									//锁住GPIO,避免共享冲突
	ret = s3c_gpio_do_setcfg(chip, offset, config);				//配置GPIO	
	s3c_gpio_unlock(chip, flags);								//解锁

	return ret;
}
在内核源码目录下./include/linux/gpio.h中定义了这几个函数:

static inline int gpio_request(unsigned gpio, const char *label)			//GPIO申请函数
{
	return -ENOSYS;
}
static inline int gpio_request_array(const struct gpio *array, size_t num)		//多个GPIO以数组形式申请
{
	return -ENOSYS;
}

static inline void gpio_free(unsigned gpio)									//GPIO释放函数
{
	might_sleep();

	/* GPIO can never have been requested */
	WARN_ON(1);
}
static inline void gpio_free_array(const struct gpio *array, size_t num)
{
	might_sleep();

	/* GPIO can never have been requested */
	WARN_ON(1);
}
static inline int gpio_get_value(unsigned gpio)								//获得GPIO引脚的输入输出值
{
	/* GPIO can never have been requested or set as {in,out}put */
	WARN_ON(1);
	return 0;
}
static inline void gpio_set_value(unsigned gpio, int value)					//设置GPIO引脚的输入输出值
{
	/* GPIO can never have been requested or set as output */
	WARN_ON(1);
}

static inline int gpio_to_irq(unsigned gpio)								//GPIO作为中断源使用
{
	/* GPIO can never have been requested or set as input */
	WARN_ON(1);
	return -EINVAL;
}

static inline int irq_to_gpio(unsigned irq)
{
	/* irq can never have been returned from gpio_to_irq() */
	WARN_ON(1);
	return -EINVAL;
}
在驱动初始化时,直接调用这几个相关的函数就可以了,首先申请GPIO调用gpio_request函数,配置GPIO是输入输出还是作其它用,调用s3c_gpio_cfgpin函数,这个函数的第二参数选择S3C_GPIO_INPUT、S3C_GPIO_OUTPUT、S3C_GPIO_SFN(x)三者之一就可以了,卸载驱动时,记得释放申请的GPIO,有始有终,这时调用gpio_free函数,一个简单的IO控制就可以这样完成。












你可能感兴趣的:(ARM,+,Linux)