linux-3.6.8 s3c6410 GPIO 驱动 简要分析(续)

时间:2022-01-26 02:14:58

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寄存器。