linux设备驱动(GPIO子系统)

一、gpio子系统的内核源码主要函数

源代码:Gpiolib.c (arch\arm\mach-s5pv210)

 

static __init int s5pv210_gpiolib_init(void)
{
	struct s3c_gpio_chip *chip = s5pv210_gpio_4bit;
	int nr_chips = ARRAY_SIZE(s5pv210_gpio_4bit);
	int i = 0;

	for (i = 0; i < nr_chips; i++, chip++) {
		if (chip->config == NULL)
			chip->config = &gpio_cfg;
		if (chip->base == NULL)
			chip->base = S5PV210_BANK_BASE(i);
	}

	samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips);

	return 0;
}

解析:

 

这是三星自己意淫出来的一个配置填充结构,着实把大家玩了一把,其它厂家也是跟风,不管怎样为了工作和学习我们还是要尽量研究明白,这样有助于我们Debug分析问题。接下来我们按照入下图的逻辑一步一步深入分析。

linux设备驱动(GPIO子系统)_第1张图片

1、s3c_gpio_chip 路径Gpio-core.h (arch\arm\plat-samsung\include\plat)

首先我们在这里先要明确两个概念以免一会儿混淆:

端口组:cpu把gpio分成了好几个组,例如GPA、GPB...GPH;每一组中又又有很多io端口,例如GPA0、GPA1...GPA7等等。

gpio端口:专指一个io端口,在硬件上也指一根io管脚。我们用例如GPA0、GPA1等等表示。

 

struct s3c_gpio_chip {
	struct gpio_chip	chip;		//记录了gpio的属性信息,一些变量和操作函数的指针,里面的函数将来根据需求会有实体函数为期赋值。
	struct s3c_gpio_cfg	*config;	//实现一些配置功能。
	struct s3c_gpio_pm	*pm;		//这个带“pm”标记的结构在内核中常见,基本上都是跟电源管理相关,有关设备电源管理有机会我会另外写博文整理。
	void __iomem		*base;		//gpio相应的虚拟地址(这里注意这里表示的一个端口组的基地址)。
	spinlock_t		 lock;
#ifdef CONFIG_PM
	u32			pm_save[4];
#endif
};

这里我们最关系的结构还是struct gpio_chip: 路径为Gpio.h (include\asm-generic)

 

 

struct gpio_chip {
	const char		*label;		//这个变量值得关注 它其实对应的是一个“端口组”的名称,对应应用文件系统下的例:/sys/class/gpio/gpiochip0/label
	struct device		*dev;
	struct module		*owner;

	int			(*request)(struct gpio_chip *chip, 	//申请gpio资源
						unsigned offset);
	void			(*free)(struct gpio_chip *chip,		//释放gpio资源
						unsigned offset);

	int			(*direction_input)(struct gpio_chip *chip,	//将gpio设置成输入方向
						unsigned offset);
	int			(*get)(struct gpio_chip *chip,		//获取gpio的值(根据offset应该是对应gpio的值)
						unsigned offset);
	int			(*direction_output)(struct gpio_chip *chip,	//将gpio设置成输出方向
						unsigned offset, int value);
	int			(*set_debounce)(struct gpio_chip *chip,
						unsigned offset, unsigned debounce);

	void			(*set)(struct gpio_chip *chip,		//写入gpio的值
						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数量
	const char		*const *names;
	unsigned		can_sleep:1;
	unsigned		exported:1;
};	//将gpio设置成输出方向
						unsigned offset, int value);
	int			(*set_debounce)(struct gpio_chip *chip,
						unsigned offset, unsigned debounce);

	void			(*set)(struct gpio_chip *chip,		//写入gpio的值
						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数量
	const char		*const *names;
	unsigned		can_sleep:1;
	unsigned		exported:1;
};

下面我们来一张图更直观的显示,这回不要再说对应不上了:

[root@board gpiochip0]# pwd
/sys/class/gpio/gpiochip0

linux设备驱动(GPIO子系统)_第2张图片

 

 

 

2、s5pv210_gpio_4bit[]

 

 

 

 

static struct s3c_gpio_chip s5pv210_gpio_4bit[] = {
	{
		.chip	= {
			.base	= S5PV210_GPA0(0),
			.ngpio	= S5PV210_GPIO_A0_NR,
			.label	= "GPA0",
		},
		
		......
		
	}, {
		.base	= (S5P_VA_GPIO + 0xC60),
		.config	= &gpio_cfg_noint,
		.chip	= {
			.base	= S5PV210_GPH3(0),
			.ngpio	= S5PV210_GPIO_H3_NR,
			.label	= "GPH3",
		},
	},
};

 

首先这是一个结构数组,承担着为s3c_gpio_chip 填充信息的任务。

第一部分只填充了我们刚刚说过的base、nggpio、label;nggpio和label好理解自己跟代码一目了然。重点有个头疼的玩意:

 

.base	= S5PV210_GPA0(0),	= S5PV210_GPA0(0),

 

 

为了便于说明我们跟踪两个函数:
S5PV210_GPA0(0)
-#define S5PV210_GPA0(_nr)	(S5PV210_GPIO_A0_START + (_nr))
--S5PV210_GPIO_A0_START	= 0


S5PV210_GPA1(0)
-#define S5PV210_GPA1(_nr)	(S5PV210_GPIO_A1_START + (_nr))
--S5PV210_GPIO_A1_START	= S5PV210_GPIO_NEXT(S5PV210_GPIO_A0)
---#define S5PV210_GPIO_NEXT(__gpio) \
	((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1)
这一句替换完成为:
---#define S5PV210_GPIO_NEXT(__gpio) \
	((S5PV210_GPIO_A0_START) + (S5PV210_GPIO_A0_NR) + CONFIG_S3C_GPIO_SPACE + 1)	
通过检索:
1)S5PV210_GPIO_A0_START = 0		   路径:Gpio.h (arch\arm\mach-s5pv210\include\mach)
2)#define S5PV210_GPIO_A0_NR	(8)    路径:Gpio.h (arch\arm\mach-s5pv210\include\mach)
3)CONFIG_S3C_GPIO_SPACE = 0 注:看见“CONFIG_”没 咱家感觉这玩意不像是内核里的东西呢!没错他的定义在内核主目录的".config"文件中。

至此:S5PV210_GPA1 = 0 + 8 + 0 + 1 + 0 = 9

通过下图还可知晓,上一端口组的计算结果是下一组的元素(顺着箭头一次类推):

linux设备驱动(GPIO子系统)_第3张图片

我没在顺次计算几个:

 S5PV210_GPB(0) = 9 + 4 + 0 + 1 + 0 =14

 S5PV210_GPC0(0) = 14 + 8 + 0 + 1 + 0 = 23

S5PV210_GPC1(0) = 23 + 5 + 0 + 1 + 0 = 29 

S5PV210_GPD0(0) = 29 + 5 + 0 + 1 + 0 = 35

再来一张猛图看个明白:(注意路径)

linux设备驱动(GPIO子系统)_第4张图片

0、14、23、29、35 ...是不是对应上了呢。

细节一:细心的朋友会发现第一组和第二组相差8+1,二组三组相差4+1,三组四组相差8+1,四组五组相差5+1,五组六组相差5+1......

这些相差的数量是跟gpio管脚对应的,一组里有8个管脚,二组有4个......

linux设备驱动(GPIO子系统)_第5张图片
 

细节二:还可以看出每组和每组的标号都不是连续的每组之间都多+1,我目前不是很明白为啥,希望高人指点,人家三星就是这么搞得我们就这么看就行了。

 

*get_pull:获取gpio上拉下拉状态。 

set_pull:设置gpio上拉下拉状态。

 

 

 

void __iomem *base;操作:gpio相应的虚拟地址(这里注意这里表示的一个端口的基地址)。

 

 

s5pv210_gpio_4bit[]

结构体数组,每一个元素是gpio端口的抽象

端口:属性集合,比如GPA,GPB......每个端口有很多gpio口。

gpio口:单指硬io口

我们也可以理解为上下包含的关系如图:

3、填充虚拟地址:

1)首先先看循环部分:

 

int nr_chips = ARRAY_SIZE(s5pv210_gpio_4bit);
	int i = 0;

	for (i = 0; i < nr_chips; i++, chip++) {
		if (chip->config == NULL)
			chip->config = &gpio_cfg;
		if (chip->base == NULL)
			chip->base = S5PV210_BANK_BASE(i);
	}

-ARRAY_SIZE():返回数组成员的个数nr_chips。

 

-分析gpio_cfg:路径在Gpiolib.c (arch\arm\mach-s5pv210)

 

static struct s3c_gpio_cfg gpio_cfg = {
	.set_config	= s3c_gpio_setcfg_s3c64xx_4bit,
	.set_pull	= s3c_gpio_setpull_updown,
	.get_pull	= s3c_gpio_getpull_updown,
};

1))s3c_gpio_setcfg_s3c64xx_4bit():

 

 

 

 

 

 

你可能感兴趣的:(linux内核,linux驱动)