内核初始化与gpio子系统

本文深入解析Linux内核中GPIO子系统的初始化过程,重点介绍了`s3c_gpio_cfgpin`函数及其相关结构体,如`s3c_gpio_chip`和`s3c_gpio_cfg`。内容涵盖GPIO结构体指针获取、GPIO配置、寄存器操作以及中断处理。通过代码分析,展示了GPIO的请求、方向设置、值的读写等操作,并探讨了gpiolib框架在系统中的作用和注册流程。
摘要由CSDN通过智能技术生成

定义在linux/arch/arm/plat-s3c/gpio-config.c

int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)

{

struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin);  //得到对应GPIO结构体首指针,里面包含了该GPIO的各种参数

unsigned long flags;

int offset;

int ret;

 offset = pin - chip->chip.base;    // 否则offset等于该GPIO引脚相对于GPX0)的偏移量,每个偏移1

ret = s3c_gpio_do_setcfg(chip, offset, config);   //设置该GPIO状态寄存器的数值为config

//s3c_gpio_do_setcfg操作

static inline int s3c_gpio_do_setcfg(struct s3c_gpio_chip *chip,unsigned int off, unsigned int config)

{

     return (chip->config->set_config)(chip, off, config);

}

//这里的set_config是一个函数指针,由后面的分析知道,如果针对GPA,该函数指针指向s3c_gpio_setcfg_s3c24xx_a ,如果针对GPX应该是指向s3c_gpio_setcfg_s3c24xx——但发现,如果是其他GPX,根本没有定义set_config!!!(这个问题已经解决,见后文s3c24xx_gpiolib_init函数,事实上,其余的config的确指向s3c_gpio_do_setcfg函数)

struct s3c_gpio_cfg s3c24xx_gpiocfg_default = {

           .set_config = s3c_gpio_setcfg_s3c24xx,

           .get_config = s3c_gpio_getcfg_s3c24xx,

};

 

int s3c_gpio_setcfg_s3c24xx_a(struct s3c_gpio_chip *chip, unsigned int off, unsigned int cfg)

{

void __iomem *reg = chip->base;   // GPXCON的物理基地址

unsigned int shift = off;    // 每个GPA对应一位

u32 con;

 if (s3c_gpio_is_cfg_special(cfg)) {     //OUTPUT状态是否为(0xfffffffX),是,返回1

         cfg &= 0xf;  // cfg = 0xX

  /* Map output to 0, and SFN2 to 1 */ 本实验不会运行到这

cfg -= 1;

if (cfg > 1)

        return -EINVAL;

cfg <<= shift;

}

con = __raw_readl(reg);     // 先读出该GPXCON的值,32

con &= ~(0x1 << shift);     // 

con |= cfg;                         //

 __raw_writel(con, reg);    // 将新值写入GPXCON

 

PS:   

#define __raw_writeb(v,a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a) = (v))

#define __raw_writew(v,a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v))

#define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a) = (v))

#define __raw_readb(a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a))

#define __raw_readw(a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a))

#define __raw_readl(a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a))

return 0;

}

如果针对GPX情况

int s3c_gpio_setcfg_s3c24xx(struct s3c_gpio_chip *chip,

unsigned int off, unsigned int cfg)

{

void __iomem *reg = chip->base;

 unsigned int shift = off * 2;    // 每个GPX对应2

u32 con;

if (s3c_gpio_is_cfg_special(cfg)) {

          cfg &= 0xf;

if (cfg > 3)

          return -EINVAL;

  cfg <<= shift;        // cfg0,1两位左移offset

}

 con = __raw_readl(reg);         // 读对应的GPXCON

 con &= ~(0x3 << shift);          // GPXCONpin)的两bits0

 con |= cfg;                               // 设置config

 __raw_writel(con, reg);           // 写入新的GPXCON

return 0;

}

 

return ret;

我们先来看s3c_gpiolib_getchip,它实现了返回对应pin值的GPIO结构体首指针的功能

#include<mach/gpio-core.h> 

 static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int pin)

{

struct s3c_gpio_chip *chip;

 if (pin > S3C_GPIO_END)    //如果超过GPJ(32)return NULL

     return NULL;

chip = &s3c24xx_gpios[pin/32];     //根据偏移,计算出对应pinGPIO结构体指针

     return ((pin - chip->chip.base) < chip->chip.ngpio) ? chip : NULL;

     //  这里验证,如果pin偏移超过了GPIO的个数,说明出错了,否则就返回该GPIO的结构体指针

}

回想以下之前s3c2410_gpio_cfgpin中,我们传入的参数是led_table[i]和 led_cfg_table[i]

struct s3c_gpio_chip s3c24xx_gpios[] = {

[0] = {

.base = S3C2410_GPACON,  // datasheet上地址为0x56000000

#define S3C2410_GPIOREG(x) ((x) + S3C24XX_VA_GPIO) 

#define S3C24XX_VA_GPIO     ((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)

S3C24XX_PA_GPIO相当于(0x15600000) 

S3C24XX_PA_UART相当于(0x15000000) 

#define S3C_VA_UART    S3C_ADDR(0x01000000)    /* UART */ 

#define S3C_ADDR_BASE 0xF6000000

#ifndef __ASSEMBLY__

#define S3C_ADDR(x) ((void __iomem __force *)S3C_ADDR_BASE + (x))

#else

#define S3C_ADDR(x) (S3C_ADDR_BASE + (x))

#endif

0x15600000-15000000+F7000000这里的S3C2410_GPACON应该怎么算?

 

.pm = __gpio_pm(&s3c_gpio_pm_1bit),

.config = &s3c24xx_gpiocfg_banka,   // 设置GPIO的函数指针

                     static struct s3c_gpio_cfg s3c24xx_gpiocfg_banka = {

                         .set_config = s3c_gpio_setcfg_s3c24xx_a,

                         .get_config = s3c_gpio_getcfg_s3c24xx_a,

                      };

.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),

.irq_base = IRQ_EINT8,

.chip = {

.base = S3C2410_GPG(0),

.owner = THIS_MODULE,

.label = "GPIOG",

.ngpio = 16,

.to_irq = samsung_gpiolib_to_irq,

},

}, {

.base = S3C2410_GPHCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPH(0),

.owner = THIS_MODULE,

.label = "GPIOH",

.ngpio = 11,

},

},

/* GPIOS for the S3C2443 and later devices. */2440用不到

{

.base = S3C2440_GPJCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPJ(0),

.owner = THIS_MODULE,

.label = "GPIOJ",

.ngpio = 16,

},

}, {

.base = S3C2443_GPKCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPK(0),

.owner = THIS_MODULE,

.label = "GPIOK",

.ngpio = 16,

},

}, {

.base = S3C2443_GPLCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPL(0),

.owner = THIS_MODULE,

.label = "GPIOL",

.ngpio = 15,

},

}, {

.base = S3C2443_GPMCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPM(0),

.owner = THIS_MODULE,

.label = "GPIOM",

.ngpio = 2,

},

},

};

*************************************************************************** 

下面分析第二个函数,先看一下相关结构体

void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)

{

/* do this via gpiolib until all users removed */

      gpio_request(pin, "temporary");

      gpio_set_value(pin, to);

      gpio_free(pin);

}

又出现了三个函数,我们一一说明:

1169/* These "optional" allocation calls help prevent drivers from stomping

1170 * on each other, and help provide better diagnostics in debugfs.

1171 * They're called even less than the "set direction" calls.

1172 */

PS:static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];

其中ARCH_NR_GPIOSarch/arm/mach-s3c2410/include/mach/gpio.h中定义

#define ARCH_NR_GPIOS (32 * 9 + CONFIG_S3C24XX_GPIO_EXTRA)

因此,每个引脚都分配了一个gpio_desc数据结构

1173int gpio_request(unsigned gpio, const char *label)        // 这个函数还不是很明白

1174{

1175 struct gpio_desc *desc;

1176 struct gpio_chip *chip;

1177 int status = -EINVAL;

1178 unsigned long flags;

1179

1180 spin_lock_irqsave(&gpio_lock, flags);    // gpio_lock是自旋锁,上锁,保存FLAGflags变量中

1181

1182 if (!gpio_is_valid(gpio))     // 不符合要求,跳转到done

1183     goto done;

1184 desc = &gpio_desc[gpio];    // desc = &gpio_desc[pin]

1185 chip = desc->chip;

1186 if (chip == NULL)              // gpio_desc.chip指向NULL,跳转到done

1187     goto done;

1188

1189 if (!try_module_get(chip->owner))   // 该函数用于增加模块使用计数;若返回为0,表示调用失败,希望使用的模块没有被加载或正在被卸载中

1190     goto done;

1191

1192 /* NOTE: gpio_request() can be called in early boot,

1193 * before IRQs are enabled, for non-sleeping (SOC) GPIOs.

1194 */

1195

1196 if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {    // 原子操作,将flags的第FLAG_REQUESTED位置1,并返回其原值

1197     desc_set_label(desc, label ? : "?");    // 如果原来的值是0, 执行desc_set_label, desc->chip.label赋值,如果label有定义,直接用定义,比如上面的“temporary”,否则用“?”

static inline void desc_set_label(struct gpio_desc *d, const char *label)

{

#ifdef CONFIG_DEBUG_FS

 d->label = label;               // 为什么不是d->chip.label = label; ?

#endif

}

1198 status = 0;

1199 } else {                      // 如果flags的第FLAG_REQUESTED位原来的值是1

1200 status = -EBUSY;

1201 module_put(chip->owner);    // 该函数用于减少模块使用计数

1202     goto done;

1203 }

1204

1205 if (chip->request) {    // chip->requestlinux初始化时是没有指向的,可以见后面s3c_gpiolib_add

1206 /* chip->request may sleep */

1207                    spin_unlock_irqrestore(&gpio_lock, flags);            // 如果chip->request不为0, 解锁,因为后面调用的chip->request有可能睡眠

12

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值