一、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分析问题。接下来我们按照入下图的逻辑一步一步深入分析。
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
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
通过下图还可知晓,上一端口组的计算结果是下一组的元素(顺着箭头一次类推):
我没在顺次计算几个:
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
再来一张猛图看个明白:(注意路径)
0、14、23、29、35 ...是不是对应上了呢。
细节一:细心的朋友会发现第一组和第二组相差8+1,二组三组相差4+1,三组四组相差8+1,四组五组相差5+1,五组六组相差5+1......
这些相差的数量是跟gpio管脚对应的,一组里有8个管脚,二组有4个......
细节二:还可以看出每组和每组的标号都不是连续的每组之间都多+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():