1、相关的数据结构
1 struct s3c_gpio_chip { // 这个结构体是三星在移植gpiolib时封装的一个结构体 用来描述一组gpio端口信息 2 struct gpio_chip chip; 3 struct s3c_gpio_cfg *config; // 三星封装的用来配置一个gpio端口的结构体 主要是上下拉模式配置 4 struct s3c_gpio_pm *pm; // 这个是电源管理相关的数据结构 5 void __iomem *base; // gpio相关寄存器的虚拟基地址 6 int eint_offset; 7 spinlock_t lock; 8 #ifdef CONFIG_PM 9 u32 pm_save[7]; 10 #endif 11 };
1 struct gpio_chip { // 内核提供的用来描述一组gpio端口信息的结构体 2 const char *label; // 一个标号 也就是gpio端口的名字 3 struct device *dev; // device指针指向这个gpio端口设备 4 struct module *owner; 5 6 int (*request)(struct gpio_chip *chip, // 请求gpio 申请 7 unsigned offset); 8 void (*free)(struct gpio_chip *chip, // 释放 9 unsigned offset); 10 11 int (*direction_input)(struct gpio_chip *chip, // 用于将gpio配置为输入模式 12 unsigned offset); 13 int (*get)(struct gpio_chip *chip, // 获取gpio电平状态 14 unsigned offset); 15 int (*direction_output)(struct gpio_chip *chip, // 用于将gpio配置为输出模式 16 unsigned offset, int value); 17 int (*set_debounce)(struct gpio_chip *chip, // 用于消抖 18 unsigned offset, unsigned debounce); 19 20 void (*set)(struct gpio_chip *chip, // 设置gpio的电平 21 unsigned offset, int value); 22 23 int (*to_irq)(struct gpio_chip *chip, // 如果该gpio是一个外部中断源,则使用这个函数来获取对应的中断号 24 unsigned offset); 25 26 void (*dbg_show)(struct seq_file *s, 27 struct gpio_chip *chip); 28 int base; // 这组gpio端口的 基准编号 (这个编号是内核设计的) 29 u16 ngpio; // 这组端口的gpio数量 30 const char *const *names; 31 unsigned can_sleep:1; 32 unsigned exported:1; 33 };
1 struct s3c_gpio_cfg { 2 unsigned int cfg_eint; // 用于外部中断源时的一个配置值 3 4 s3c_gpio_pull_t (*get_pull)(struct s3c_gpio_chip *chip, unsigned offs); // 读取下拉状态时gpio的电流 5 int (*set_pull)(struct s3c_gpio_chip *chip, unsigned offs, // 设置下拉状态gpio电流 6 s3c_gpio_pull_t pull); 7 8 int (*set_pin)(struct s3c_gpio_chip *chip, unsigned offs, 9 s3c_gpio_pull_t level); 10 11 unsigned (*get_config)(struct s3c_gpio_chip *chip, unsigned offs); // 获取gpio的当前配置 12 int (*set_config)(struct s3c_gpio_chip *chip, unsigned offs, // 设置gpio的当前配置 13 unsigned config); 14 };
2、函数调用关系图
smdkc110_map_io
s5pv210_gpiolib_init
samsung_gpiolib_add_4bit_chips
samsung_gpiolib_add_4bit
s3c_gpiolib_add
gpiochip_add // 这个函数就是内核的gpiolib驱动框架提供的用来注册gpiolib的函数
3、函数详解
smdkc110_map_io:
1 static void __init smdkc110_map_io(void) 2 { 3 s5p_init_io(NULL, 0, S5P_VA_CHIPID); // 静态物理地址到虚拟地址的映射初始化 4 s3c24xx_init_clocks(24000000); // 系统时钟初始化 5 s5pv210_gpiolib_init(); // gpiolib管理器初始化 6 s3c24xx_init_uarts(smdkc110_uartcfgs, ARRAY_SIZE(smdkc110_uartcfgs)); 7 s5p_reserve_bootmem(smdkc110_media_devs, ARRAY_SIZE(smdkc110_media_devs)); 8 #ifdef CONFIG_MTD_ONENAND 9 s5pc110_device_onenand.name = "s5pc110-onenand"; 10 #endif 11 #ifdef CONFIG_MTD_NAND 12 s3c_device_nand.name = "s5pv210-nand"; 13 #endif 14 s5p_device_rtc.name = "smdkc110-rtc"; 15 }
s5pv210_gpiolib_init:
1 __init int s5pv210_gpiolib_init(void) 2 { 3 struct s3c_gpio_chip *chip = s5pv210_gpio_4bit; // 4bit表示的是一个gpio使用4位来配置描述 s5pv210_gpio_4bit是一个struct s3c_gpio_chip数组,是三星移植时写好的 4 int nr_chips = ARRAY_SIZE(s5pv210_gpio_4bit); // 获取端口数量(注意一组端口和一个具体的gpio) 5 int i = 0; 6 7 for (i = 0; i < nr_chips; i++, chip++) { 8 if (chip->config == NULL) // 如果我们的gpio端口没有 配置方法 则使用 gpio_cfg 进行默认配置 9 chip->config = &gpio_cfg; 10 if (chip->base == NULL) // 如果我们的gpio端口结构体中没有填充 基准编号 则使用下面进行填充 11 chip->base = S5PV210_BANK_BASE(i); 12 } 13 14 samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips); // 添加gpiolib 15 16 return 0; 17 }
samsung_gpiolib_add_4bit_chips:
1 void __init samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip, 2 int nr_chips) 3 { 4 for (; nr_chips > 0; nr_chips--, chip++) { // 对于每一个gpio端口组都调用下面的两个函数 5 samsung_gpiolib_add_4bit(chip); // 给gpio端口配置输入输出模式配置方法 6 s3c_gpiolib_add(chip); // 最终是通过这个函数去添加gpiolib 7 } 8 } 9 10 11 void __init samsung_gpiolib_add_4bit(struct s3c_gpio_chip *chip) 12 { 13 chip->chip.direction_input = samsung_gpiolib_4bit_input; // 给gpio端口加入配置gpio为输入模式的方法 14 chip->chip.direction_output = samsung_gpiolib_4bit_output; // 给gpio端口加入配置gpio为输出模式的方法 15 chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); // 电源管理相关 16 }
s3c_gpiolib_add:
1 __init void s3c_gpiolib_add(struct s3c_gpio_chip *chip) 2 { 3 struct gpio_chip *gc = &chip->chip; // 使用gc指针指向 chip->chip 4 int ret; 5 6 BUG_ON(!chip->base); 7 BUG_ON(!gc->label); 8 BUG_ON(!gc->ngpio); 9 10 spin_lock_init(&chip->lock); 11 12 // 如果我们的结构体中还没有加入相应的方法 则在下面加入方法 13 if (!gc->direction_input) 14 gc->direction_input = s3c_gpiolib_input; 15 if (!gc->direction_output) 16 gc->direction_output = s3c_gpiolib_output; 17 if (!gc->set) 18 gc->set = s3c_gpiolib_set; 19 if (!gc->get) 20 gc->get = s3c_gpiolib_get; 21 22 #ifdef CONFIG_PM // 电源管理相关 23 if (chip->pm != NULL) { 24 if (!chip->pm->save || !chip->pm->resume) 25 printk(KERN_ERR "gpio: %s has missing PM functions\n", 26 gc->label); 27 } else 28 printk(KERN_ERR "gpio: %s has no PM function\n", gc->label); 29 #endif 30 31 /* gpiochip_add() prints own failure message on error. */ 32 ret = gpiochip_add(gc); // 最终又是通过这个函数去注册我们的gpiolib管理器 这个函数就是内核gpiolib驱动框架提供的了 33 if (ret >= 0) 34 s3c_gpiolib_track(chip); 35 }
总结:
其实我们的gpiolib虽然说是一个gpio的管理者,同样他也是一个设备,在/sys目录下是能够去通过设备的属性方法对具体的gpio进行设置的,三星在移植gpiolib时是使用内核
提供的gpiolib驱动框架来实现的。
其实这个注册就是将我们的封装了一个GPIO端口的所有信息的chip结构体变量挂接到内核gpiolib模块定义的一个gpio_desc数组中的某一个格子中。