Linux提供了众多的驱动接口,但由于板卡各具特色,具体的操控函数还是需要板卡驱动实现。接下来就以s3c6410为例,看看SAMSUNG为该板卡做了哪些驱动实现:
/**
* struct samsung_gpio_chip - wrapper for specific implementation of gpio
* @chip: The chip structure to be exported via gpiolib.
* @base: The base pointer to the gpio configuration registers.
* @group: The group register number for gpio interrupt support.
* @irq_base: The base irq number.
* @config: special function and pull-resistor control information.
* @lock: Lock for exclusive access to this gpio bank.
* @pm_save: Save information for suspend/resume support.
*
* This wrapper provides the necessary information for the Samsung
* specific gpios being registered with gpiolib.
*
* The lock protects each gpio bank from multiple access of the shared
* configuration registers, or from reading of data whilst another thread
* is writing to the register set.
*
* Each chip has its own lock to avoid any contention between different
* CPU cores trying to get one lock for different GPIO banks, where each
* bank of GPIO has its own register space and configuration registers.
*/
struct samsung_gpio_chip {
struct gpio_chipchip;
struct samsung_gpio_cfg*config;
struct samsung_gpio_pm*pm;
void __iomem*base;
intirq_base;
intgroup;
spinlock_t lock;
#ifdef CONFIG_PM
u32pm_save[4];
#endif
};
arch/arm/plat-samsung/include/plat/gpio-core.h文件中,SAMSUNG封装了Linux提供的struct gpio_chip,并定义了自己的核心结构体structsamsung_gpio_chip。结构体中的base指针要与struct gpio_chip中的base区分(两个类型都不一样★_★),它存储的是该组GPIO第一个寄存器的地址(例如查看s3c6410数据手册,GPI的第一个寄存器为GPICON,它的地址为0x7F008100,base中存储的就是这个值)。
static struct samsung_gpio_chip s3c64xx_gpios_2bit[];
static struct samsung_gpio_chip s3c64xx_gpios_4bit[];
static struct samsung_gpio_chip s3c64xx_gpios_4bit2[];
s3c6410提供了GPA~GPQ共17个GPIO组,共计187个GPIO管脚。这些GPIO组按照GPxCON寄存器的数量以及配置方式,可以分为三大类。第一类,有1个GPxCON寄存器且每个GPIO管脚由其中的2bits控制,数组s3c64xx_gpios_2bit就包含这些GPIO组;第二类,有1个GPxCON寄存器且每个GPIO管脚由其中的4bits控制,数组s3c64xx_gpios_4bit就包含这些GPIO组;第三类,有2个GPxCON寄存器且每个GPIO管脚由其中的4bits控制,数组s3c64xx_gpios_4bit2就包含这些GPIO组。数组中对各GPIO组做了一些基本的初始化,详情请参见drivers/gpio/gpio-samsung.c。
/**
* struct samsung_gpio_cfg GPIO configuration
* @cfg_eint: Configuration setting when usedfor external interrupt source
* @get_pull: Read the current pullconfiguration for the GPIO
* @set_pull: Set the current pullconfiguraiton for the GPIO
* @set_config: Set the current configurationfor the GPIO
* @get_config: Read the current configurationfor the GPIO
*
* Each chip can have more than one type ofGPIO bank available and some
* have different capabilites even when theyhave the same control register
* layouts. Provide an point to vector controlroutine and provide any
* per-bank configuration information thatother systems such as the
* external interrupt code will need.
*
* @sa samsung_gpio_cfgpin
* @sa s3c_gpio_getcfg
* @sa s3c_gpio_setpull
* @sa s3c_gpio_getpull
*/
struct samsung_gpio_cfg{
unsigned int cfg_eint;
samsung_gpio_pull_t (*get_pull)(structsamsung_gpio_chip *chip, unsigned offs);
int (*set_pull)(structsamsung_gpio_chip *chip, unsigned offs,
samsung_gpio_pull_t pull);
unsigned (*get_config)(struct samsung_gpio_chip *chip,unsigned offs);
int (*set_config)(struct samsung_gpio_chip *chip, unsigned offs,
unsigned config);
};
struct samsung_gpio_chip中还有两个自定义的结构体,struct samsung_gpio_cfg封装了操控GPxCON(对应*_config函数指针)以及GPxPUD(对应*_pull函数指针)的函数指针。
/**
* struct samsung_gpio_pm - power management(suspend/resume) information
* @save: Routine to save the state of the GPIOblock
* @resume: Routine to resume the GPIO block.
*/
struct samsung_gpio_pm{
void (*save)(struct samsung_gpio_chip *chip);
void (*resume)(struct samsung_gpio_chip *chip);
};
struct samsung_gpio_pm封装了电源管理相关操作的函数指针。
SAMSUNG在drivers/gpio/gpio-samsung.c文件中还提供了旗下多种板卡的驱动函数,本文中我们只关注s3c6410的:
static __init intsamsung_gpiolib_init(void)
core_initcall(samsung_gpiolib_init);
samsung_gpiolib_init()根据不同的板卡,注册对应的struct samsung_gpio_chip结构体数组中的structgpio_chip。上文中看到,根据三大类GPIO定义了3个struct samsung_gpio_chip结构体数组。该函数将会调用samsung_gpiolib_add_2bit_chips()、samsung_gpiolib_add_4bit_chips()以及samsung_gpiolib_add_4bit2_chips()分别完成三类GPIO的注册。
core_initcall()将samsung_gpiolibg_init函数放到内核的初始化区段中,以供内核调用。详细信息请参考 http://gpg119.blog.163.com/blog/static/9153415320089824938513/。static void __init samsung_gpiolib_set_cfg(struct samsung_gpio_cfg *chipcfg, int nr_chips)
该函数用于检查samsung_gpio_cfgs结构体数组中每个数组元素的函数指针(set_config、get_config、set_pull、get_pull),如果有NULL,则设置为默认值。
static void __init samsung_gpiolib_add(struct samsung_gpio_chip *chip)
static void __init samsung_gpiolib_add_2bit_chips(struct samsung_gpio_chip *chip,
int nr_chips, void __iomem *base,
unsigned int offset)
static void __init samsung_gpiolib_add_4bit_chips(struct samsung_gpio_chip *chip,
int nr_chips, void __iomem *base)
static void __init samsung_gpiolib_add_4bit2_chips(struct samsung_gpio_chip *chip,
int nr_chips)
samsung_gpiolib_add()是公用的add函数,它查看一些函数指针是否已经指向实际的函数,如果没有则将其赋值为默认的函数,最后它调用Linux的GPIO驱动所提供的gpiochip_add()进行gpio的注册。其它三个函数主要的工作均是将自身每个struct gpio_chip的direction_input和direcetion_output函数指针赋值,最后均会调用samsung_gpiolib_add()。
int samsung_gpio_setpull_updown(structsamsung_gpio_chip *chip, unsigned int off, samsung_gpio_pull_t pull)
samsung_gpio_pull_t samsung_gpio_getpull_updown(struct samsung_gpio_chip *chip, unsigned int off)
这两个函数分别用于设置和获取GPxPUD寄存器的值。GPxPUD寄存器地址为每个GPIO组寄存器基地址+0x08,每一个GPIO管脚由2bits控制。其中samsung_gpio_setpull_updown函数内部执行标准的“读-改-写”操作。三大类struct samsung_gpio_chip结构体数组中每个数组元素的config->set_pull和config->get_pull函数指针均会指向这两个函数。
static int samsung_gpio_setcfg_2bit(struct samsung_gpio_chip *chip, unsigned int off,unsigned int cfg)
static unsigned int samsung_gpio_getcfg_2bit(struct samsung_gpio_chip *chip, unsigned int off)
static int samsung_gpio_setcfg_4bit(struct samsung_gpio_chip *chip, unsigned int off,unsigned int cfg)
static unsigned samsung_gpio_getcfg_4bit(structsamsung_gpio_chip *chip, unsigned int off)
static int samsung_gpiolib_2bit_input(struct gpio_chip *chip, unsigned offset)
static int samsung_gpiolib_2bit_output(struct gpio_chip *chip,unsigned offset, intvalue)
static int samsung_gpiolib_4bit_input(structgpio_chip *chip, unsigned int offset)
static int samsung_gpiolib_4bit_output(struct gpio_chip *chip, unsigned int offset, intvalue)
static int samsung_gpiolib_4bit2_input(struct gpio_chip *chip, unsigned int offset)
static int samsung_gpiolib_4bit2_output(struct gpio_chip *chip, unsigned int offset, intvalue)
前四个函数分别用于设置和获取GPxCON寄存器的值。GPxCON寄存器地址为每个GPIO组寄存器的基地址,s3c64xx_gpios_2bit结构体数组中的每个数组元素的config->set_config和config->get_config函数指针均会指向相应的2bit函数,而s3c64xx_gpios_4bit和s3c64xx_gpios_4bit2中的,均会指向相应的4bit函数。在相应的setcfg函数中,传入的cfg必须具有特殊的形式(cfg的低两bit表示需要设置的值,其它bit均为1)。在相应的getcfg函数中,返回的值也是相应的特殊形式。
后六个函数同样是设置和获取GPxCON寄存器的值,只不过分为了设置为特定的模式,并且当设置为输出模式时还会写GPxDAT寄存器,指定输出的值。s3c64xx_gpios_2bit结构体数组中的每个数组元素的chip.direction_input和chip.direction_output函数指针均会指向相应的2bit函数,s3c64xx_gpios_4bit中的均会指向相应的4bit函数,而s3c64xx_gpios_4bit2中的均会指向相应的4bit2函数。
这里有个小插曲,s3c64xx_gpios_4bit2结构体数组中每个数组元素的base成员保存的并非该GPIO组寄存器的基地址,而是基地址+0x04。这是为了无论对于哪种类型的GPIO组,GPxDAT寄存器的地址均是base+0x04。所以在这十个函数中部分4bit函数(包括4bit2函数)需要根据该组GPIO管脚的数量(ngpio)对base以及offset(当前GPIO管脚的序号相对于该组GPIO第一个管脚的序号的偏移,也就是代表它是该组第几个GPIO)进行调整。之所以说部分,是因为这里有个很有意思的现象。前四个函数中没有相应的4bit2函数,4bit函数能够处理有两个GPxCON寄存器的情况。可后六个函数中又分出了4bit2函数,为何不与后六个函数中的4bit函数合并在一起?猜想有可能是历史遗留问题,后六个函数早于前四个函数,为了兼容性,之后并没有再做修改。这里可能有些乱,举个简单的例子。GPH有GPHCON0和GPHCON1两个配置寄存器,现在base存储的是GPHCON1寄存器的地址。如果我想配置GPH(1),则base需要减4(指向GPHCON0),offset可以不变(offset为1);如果我要配置GPH(9),则base无需改变(GPH9的控制位在GPHCON1里),offset需要减8或者与7(因为GPIO组中最多有16个管脚,所以offset最大为15,所以这两种方法均是得到除8的余数,于是offset变为1)。
static void samsung_gpiolib_set(struct gpio_chip *chip, unsigned offset, int value)
static int samsung_gpiolib_get(struct gpio_chip *chip, unsigned offset)
这两个函数分别用于设置和获取GPxDAT寄存器的值。GPxDAT寄存器地址为每个GPIO组寄存器基地址+0x04,每一个GPIO管脚由1bit控制。三大类struct samsung_gpio_chip结构体数组中每个数组元素的chip.set和chip.get函数指针均会指向这两个函数。
int s3c_gpio_setpull(unsigned int pin, samsung_gpio_pull_t pull)
samsung_gpio_pull_t s3c_gpio_getpull(unsigned int pin)
这两个函数分别用于设置和获取GPxPUD寄存器的值。只不过函数内部会自动寻找该GPIO管脚所对应的struct samsung_gpio_chip,并分别调用其中的config->set_pull和config->get_pull函数指针所指向的函数。
int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)
unsigned s3c_gpio_getcfg(unsigned int pin)
这两个函数分别用于设置和获取GPxCON寄存器的值。只不过函数内部会自动寻找该GPIO管脚所对应的struct samsung_gpio_chip,并分别调用其中的config->set_config和config->get_config函数指针所指向的函数。
int s3c_gpio_cfgpin_range(unsigned int start, unsigned int nr, unsigned int cfg)
该函数对一段连续的GPIO调用s3c_gpio_cfgpin()设置他们的GPxCON寄存器。
int s3c_gpio_cfgall_range(unsigned int start, unsigned int nr, unsigned int cfg,samsung_gpio_pull_t pull)
该函数对一段连续的GPIO调用s3c_gpio_setpull()和s3c_gpio_cfgpin()设置他们的GPxPUD和GPxCON寄存器。