linuxGPIO 的模型实现主要一个gpio-chip:(抽象一个GPIO控制器)
struct gpio_chip {
const char *label;
struct device *dev;
struct module *owner;
int (*request)(struct gpio_chip *chip,
unsigned offset);
void (*free)(struct gpio_chip *chip,
unsigned offset);
int (*direction_input)(struct gpio_chip *chip,
unsigned offset);
int (*get)(struct gpio_chip *chip,
unsigned offset);
int (*direction_output)(struct gpio_chip *chip,
unsigned offset, int value);
void (*set)(struct gpio_chip *chip,
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;
u16 ngpio;
char **names;
unsigned can_sleep:1;
unsigned exported:1;
};
---------------------------------------------------------------------------------------
struct gpio_desc {
struct gpio_chip *chip;
unsigned long flags;
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_RESERVED 2
#define FLAG_EXPORT 3
#define FLAG_SYSFS 4
#define FLAG_TRIG_FALL 5
#define FLAG_TRIG_RISE 6
#define PDESC_ID_SHIFT 16
#define GPIO_FLAGS_MASK ((1 << PDESC_ID_SHIFT) - 1)
#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
#ifdef CONFIG_DEBUG_FS
const char *label;
#endif
};
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];定义了一个全局变量ARCH_NR_GPIOS是芯片GPIO口个数。
---------------------------------------------------------------------------------------
对于s3c2440来说,它有个GPIO-CORE.H,在这个文件夹中实现了对gpio-chip的封装和注册函数等。
---------------------------------------------------------------------------------
struct s3c_gpio_chip {
struct gpio_chip chip;
struct s3c_gpio_cfg *config;GPIO口设置结构体
struct s3c_gpio_pm *pm;
void __iomem *base;//控制这个gpio组的首址寄存器
#ifdef CONFIG_PM
u32 pm_save[4];存取寄存器控制器的值
#endif
};
-----------------------------------------------------------------------------------
下面先说看看这种模型是如何被加入系统中的,然后再看看在这个模型下,对外提供的API。
在plat-s3c24xx目录下的gpiolib.c中,我们看到了这么一个语句core_initcall(s3c24xx_gpiolib_init),
s3c24xx_gpiolib_init()函数就是在系统中注册GPIO的一个起始点。但是我们就会有疑问,这个函数是怎么被调用的,什么时候调用。这里就简单的说下,具体的以后分析。core_initcall().在linux\init.h中这么个定义
#define core_initcall(fn) __define_initcall("1",fn,1)。
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
把core_initcall(s3c24xx_gpiolib_init)展开可以得到
—define_initcall("1",s3c24xx_gpiolib_init,1)--->
static initcall_t __initcall_ s3c24xx_gpiolib_init1 __used __attribute__((__section__(".initcall" 1 ".init")))=s3c24xx_gpiolib_init
__initcall_##fn##id ,这个是指针变量,所以整个意思就是把指向函数s3c24xx_gpiolib_init()的指针放入到.initcall1.init的输入段.系统中还有很多的其他输入端,那么这些段在哪里被调用呢。
start_kernel()-->rest_init()-->kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND)-->
kernel_init()-->do_basic_setup()-->do_initcalls-->do_one_initcall-->ret.result = fn()
do_initcalls定义如下:
do_initcalls()
{
...
for (call = __early_initcall_end; call < __initcall_end; call++)
do_one_initcall(*call);
...
}
在__early_initcall_end和__initcall_end定义看arch/arm/kernel/vmlinux.lds
__initcall_start = .;
*(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)
__initcall_end = .;
所以属于.initcall1.init段的s3c24xx_gpiolib_init()函数会被调用。
关于core_initcall()就先到这。下面是接着叫GPIO
s3c24xx_gpiolib_init()定义如下:
----------------------------------------------------------------------------------
static __init int s3c24xx_gpiolib_init(void)
{
struct s3c_gpio_chip *chip = s3c24xx_gpios;
int gpn;
for (gpn = 0; gpn < ARRAY_SIZE(s3c24xx_gpios); gpn++, chip++)
s3c_gpiolib_add(chip);
return 0;
}
-----------------------------------------------------------------------------------
在里面有个s3c24xx_gpios中的没一个元素被s3c_gpiolib_add()调用一次。在本文件内我们还看到
-----------------------------------------------------------------------------------------
struct s3c_gpio_chip s3c24xx_gpios[] = {
[0] = {
.base = S3C2410_GPACON,
.pm = __gpio_pm(&s3c_gpio_pm_1bit),
.chip = {
.base = S3C2410_GPA(0),
.owner = THIS_MODULE,
.label = "GPIOA",
.ngpio = 24,
.direction_input = s3c24xx_gpiolib_banka_input,
.direction_output = s3c24xx_gpiolib_banka_output,
},
},
[1] = {
.base = S3C2410_GPBCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPB(0),
.owner = THIS_MODULE,
.label = "GPIOB",
.ngpio = 16,
},
},
[2] = {
.base = S3C2410_GPCCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPC(0),
.owner = THIS_MODULE,
.label = "GPIOC",
.ngpio = 16,
},
},
[3] = {
.base = S3C2410_GPDCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPD(0),
.owner = THIS_MODULE,
.label = "GPIOD",
.ngpio = 16,
},
},
[4] = {
.base = S3C2410_GPECON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPE(0),
.label = "GPIOE",
.owner = THIS_MODULE,
.ngpio = 16,
},
},
[5] = {
.base = S3C2410_GPFCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPF(0),
.owner = THIS_MODULE,
.label = "GPIOF",
.ngpio = 8,
.to_irq = s3c24xx_gpiolib_bankf_toirq,
},
},
[6] = {
.base = S3C2410_GPGCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPG(0),
.owner = THIS_MODULE,
.label = "GPIOG",
.ngpio = 16,
.to_irq = s3c24xx_gpiolib_bankg_toirq,
},
}, {
.base = S3C2410_GPHCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPH(0),
.owner = THIS_MODULE,
.label = "GPIOH",
.ngpio = 11,
},
},
};
---------------------------------------------------------------------------------------------
s3c24xx_gpios有七个元素。每一个元素与GPIO口的A,B,C,D,E,F,H组一一对应。拿第一个元素来说就对应A组GPIO。它描述了这组口的一些性质。外面.base是GPACON寄存器的首地址,里面.chip结构体的.base代表该组的第一个引脚。因为A口没有输入功能所以s3c24xx_gpiolib_banka_input为空。s3c24xx_gpiolib_banka_output()设置某个引脚输出的电平值。offset是第几位,value是电平值。第5,6组因为可以设置中断引脚所以.to_irq被赋值。
-------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------
static int s3c24xx_gpiolib_bankg_toirq(struct gpio_chip *chip, unsigned offset)
{
return IRQ_EINT8 + offset;
}
-------------------------------------------------------------------------------------------
为什么这样编写呢。这就关心到中断号了。有主中断和次中断。
#define IRQ_EINT0 S3C2410_IRQ(0)
#define IRQ_EINT1 S3C2410_IRQ(1)
#define IRQ_EINT2 S3C2410_IRQ(2)
#define IRQ_EINT3 S3C2410_IRQ(3)
#define IRQ_EINT4t7 S3C2410_IRQ(4)
#define IRQ_EINT8t23 S3C2410_IRQ(5)
#define IRQ_RESERVED6 S3C2410_IRQ(6)
#define IRQ_CAM S3C2410_IRQ(6)
#define IRQ_BATT_FLT S3C2410_IRQ(7)
#define IRQ_TICK S3C2410_IRQ(8)
#define IRQ_WDT S3C2410_IRQ(9)
#define IRQ_TIMER0 S3C2410_IRQ(10)
#define IRQ_TIMER1 S3C2410_IRQ(11)
#define IRQ_TIMER2 S3C2410_IRQ(12)
#define IRQ_TIMER3 S3C2410_IRQ(13)
#define IRQ_TIMER4 S3C2410_IRQ(14)
#define IRQ_UART2 S3C2410_IRQ(15)
#define IRQ_LCD S3C2410_IRQ(16)
#define IRQ_DMA0 S3C2410_IRQ(17)
#define IRQ_DMA1 S3C2410_IRQ(18)
#define IRQ_DMA2 S3C2410_IRQ(19)
#define IRQ_DMA3 S3C2410_IRQ(20)
#define IRQ_SDI S3C2410_IRQ(21)
#define IRQ_SPI0 S3C2410_IRQ(22)
#define IRQ_UART1 S3C2410_IRQ(23)
#define IRQ_RESERVED24 S3C2410_IRQ(24)
#define IRQ_NFCON S3C2410_IRQ(24)
#define IRQ_USBD S3C2410_IRQ(25)
#define IRQ_USBH S3C2410_IRQ(26)
#define IRQ_IIC S3C2410_IRQ(27)
#define IRQ_UART0 S3C2410_IRQ(28)
#define IRQ_SPI1 S3C2410_IRQ(29)
#define IRQ_RTC S3C2410_IRQ(30)
#define IRQ_ADCPARENT S3C2410_IRQ(31)
#define IRQ_EINT4 S3C2410_IRQ(32)
#define IRQ_EINT5 S3C2410_IRQ(33)
#define IRQ_EINT6 S3C2410_IRQ(34)
#define IRQ_EINT7 S3C2410_IRQ(35)
#define IRQ_EINT8 S3C2410_IRQ(36)
#define IRQ_EINT9 S3C2410_IRQ(37)
#define IRQ_EINT10 S3C2410_IRQ(38)
#define IRQ_EINT11 S3C2410_IRQ(39)
#define IRQ_EINT12 S3C2410_IRQ(40)
#define IRQ_EINT13 S3C2410_IRQ(41)
#define IRQ_EINT14 S3C2410_IRQ(42)
#define IRQ_EINT15 S3C2410_IRQ(43)
#define IRQ_EINT16 S3C2410_IRQ(44)
#define IRQ_EINT17 S3C2410_IRQ(45)
#define IRQ_EINT18 S3C2410_IRQ(46)
#define IRQ_EINT19 S3C2410_IRQ(47)
#define IRQ_EINT20 S3C2410_IRQ(48)
#define IRQ_EINT21 S3C2410_IRQ(49)
#define IRQ_EINT22 S3C2410_IRQ(50)
#define IRQ_EINT23 S3C2410_IRQ(51)
.ngpio的该组多少个引脚。.label只是对该组的一个标记符。其他的初始化在后面。.pm可以通过如下知道他的值。
如果定义了PM,那么__gpio_pm(x)就为x。
#ifdef CONFIG_PM
extern struct s3c_gpio_pm s3c_gpio_pm_1bit;
extern struct s3c_gpio_pm s3c_gpio_pm_2bit;
extern struct s3c_gpio_pm s3c_gpio_pm_4bit;
#define __gpio_pm(x) x
#else
#define s3c_gpio_pm_1bit NULL
#define s3c_gpio_pm_2bit NULL
#define s3c_gpio_pm_4bit NULL
#define __gpio_pm(x) NULL
#endif
那么这样的7个元素怎么加入的呢。看s3c_gpiolib_add()
-----------------------------------------------------------------------------------------------
__init void s3c_gpiolib_add(struct s3c_gpio_chip *chip)
{
struct gpio_chip *gc = &chip->chip;
int ret;
BUG_ON(!chip->base);
BUG_ON(!gc->label);
BUG_ON(!gc->ngpio);
if (!gc->direction_input)
gc->direction_input = s3c_gpiolib_input;
if (!gc->direction_output)
gc->direction_output = s3c_gpiolib_output;
if (!gc->set)
gc->set = s3c_gpiolib_set;
if (!gc->get)
gc->get = s3c_gpiolib_get;
#ifdef CONFIG_PM
if (chip->pm != NULL) {
if (!chip->pm->save || !chip->pm->resume)
printk(KERN_ERR "gpio: %s has missing PM functions\n",
gc->label);
} else
printk(KERN_ERR "gpio: %s has no PM function\n", gc->label);
#endif
ret = gpiochip_add(gc);
if (ret >= 0)
s3c_gpiolib_track(chip);
}
-------------------------------------------------------------------------------------------------
#define BUG_ON(a) if((a)) printf("bug report at line %d from %s\n",__LINE__,__FUNCTION__)
BUG_ON(!chip->base);
BUG_ON(!gc->label);
BUG_ON(!gc->ngpio);
所以,这里不难看懂了吧。然后进行相关chip的初始化。PM中save和resume分别是保存控制器的值和恢复控制器的值。它的值保存在pm_save[4]里。
gpiochip_add(gc)做什么,这里是真正的注册GPIO了。看源代码:
-----------------------------------------------------------------------------------------
int gpiochip_add(struct gpio_chip *chip)
{
unsigned long flags;
int status = 0;
unsigned id;
int base = chip->base;
if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1))
&& base >= 0) {
status = -EINVAL;
goto fail;
}
spin_lock_irqsave(&gpio_lock, flags);
if (base < 0) {
base = gpiochip_find_base(chip->ngpio);
if (base < 0) {
status = base;
goto unlock;
}
chip->base = base;
}
for (id = base; id < base + chip->ngpio; id++) {
if (gpio_desc[id].chip != NULL) {
status = -EBUSY;
break;
}
}
if (status == 0) {
for (id = base; id < base + chip->ngpio; id++) {
gpio_desc[id].chip = chip;
gpio_desc[id].flags = !chip->direction_input
? (1 << FLAG_IS_OUT)
: 0;
}
}
unlock:
spin_unlock_irqrestore(&gpio_lock, flags);
if (status == 0)
status = gpiochip_export(chip);
fail:
if (status)
pr_err("gpiochip_add: gpios %d..%d (%s) not registered\n",
chip->base, chip->base + chip->ngpio - 1,
chip->label ? : "generic");
return status;
}
-----------------------------------------------------------------------------------------
这里通过base值和.ngpio来确定gpio_desc[],为这个全局变量赋值。如果gpio_desc[]已经注册过了就会退出。
在这里我们就可以知道。gpio_desc[]记录了每一个gpio的属性。注意这里面的一个元素就是一个GPIO引脚。而这个元素是gpio_desc类型,它包含一个指向gpio_chip的结构体,这个结构体是这个引脚所属的GPIO组(A,或者B...)。
到这里,我其实有个疑问,在另外的一个mach-s3c2440中clock.c文件中有实现GPIO控制的API,这里又有,为什么弄这么多呢?