IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Linux GPIO驱动相关笔记,分析的很好

    techbulo发表于 2015-11-28 12:55:10
    love 0

    本文转自大部分内容转自: http://blog.csdn.net/llxmedici/article/details/6282372

    在linux下编写GPIO驱动,控制相应的gpio管脚。一般有两种方式:

    1) 直接操作相应的寄存器

    2) 通过内核提供的gpio操作库函数

    第一种方式很简单,寄存器操作部分和写裸机代码差不多,只是需要把寄存器的物理地址映射到linuxxi的虚拟地址就可以,在此不在赘述,讲一下第二种方式的实现原理,大部分内容拷贝自上面的网址。

    首先看一下驱动的部分代码:

    
    /*
    * 设备初始化
    */
    static int __init dev_init(void)
    {
    int ret;
    int i;
    for (i = 0; i < 4; i++) {
    //设置 LED 对应的端口寄存器为输出(OUTPUT)
    if (s3c_gpio_cfgpin(led_table[i], led_cfg_table[i])<0)
    
    printk(KERN_INFO "config pin %d failed", i);
    printk(KERN_INFO "config pin %d failed", i);
    
    //设置 LED 对应的端口寄存器为低电平输出,在模块加载> 结束后,四个 LED 应该是全部都是发光
    状态
    s3c2410_gpio_setpin(led_table[i], 0);
    }
    ret = misc_register(&misc); //注册设备
    printk (DEVICE_NAME"/tinitialized/n"); //打印初始化信息
    return ret;
    }
    
    

    可以看到,这里涉及到两个函数,分别是s3c2410_gpio_cfgpin,s3c2410_gpio_setpin,这两个函数分别对四个LED进行配置,从函数名来看,cfgpin对引脚寄存器状态进行配置,而setpin应该是对寄存器数据值进行配置,我们在分析函数之前先弄清楚传入的参数到底是什么。

    
    led_table[i]
    //LED 对应的 GPIO 端口列表
    static unsigned long led_table [] = {
    S3C2410_GPB(5),
    S3C2410_GPB(6),
    S3C2410_GPB(7),
    S3C2410_GPB(8),
    };
    
    

    这里S3C2410_GPB宏定义在mach/gpio-nrs.h中

    
    /* GPIO bank sizes */
    #define S3C2410_GPIO_A_NR (32)
    #define S3C2410_GPIO_B_NR (32)
    #define S3C2410_GPIO_C_NR (32)
    #define S3C2410_GPIO_D_NR (32)
    #define S3C2410_GPIO_E_NR (32)
    #define S3C2410_GPIO_F_NR (32)
    #define S3C2410_GPIO_G_NR (32)
    #define S3C2410_GPIO_H_NR (32)
    #define S3C2410_GPIO_J_NR (32) /* technically 16. */
    #define S3C2410_GPIO_K_NR (32) /* technically 16. */
    #define S3C2410_GPIO_L_NR (32) /* technically 15. */
    #define S3C2410_GPIO_M_NR (32) /* technically 2. */
    
    #if CONFIG_S3C_GPIO_SPACE != 0
    #error CONFIG_S3C_GPIO_SPACE cannot be zero at the moment
    #endif
    
    #define S3C2410_GPIO_NEXT(__gpio) /
    ((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 0)
    
    

    //这里的CONFIG_S3C_GPIO_SPAC是内核配置选项,在.config中可以找到,我的配置为:

    CONFIG_S3C_GPIO_SPACE = 0

    
    enum s3c_gpio_number {
    S3C2410_GPIO_A_START = 0,
    S3C2410_GPIO_B_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_A),
    S3C2410_GPIO_C_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_B),
    S3C2410_GPIO_D_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_C),
    S3C2410_GPIO_E_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_D),
    S3C2410_GPIO_F_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_E),
    S3C2410_GPIO_G_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_F),
    S3C2410_GPIO_H_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_G),
    S3C2410_GPIO_J_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_H),
    S3C2410_GPIO_K_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_J),
    S3C2410_GPIO_L_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_K),
    S3C2410_GPIO_M_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_L),
    };
    
    #define S3C2410_GPB(_nr) (S3C2410_GPIO_B_START + (_nr))
    
    

    因此,以S3C2410_GPB(5)为例,其宏展开为:

    S3C2410_GPIO_NEXT(S3C2410_GPIO_A) +5 =>

    (S3C2410_GPIO_A_START + S3C2410_GPIO_A_NR +  CONFIG_S3C_GPIO_SPACE + 0) + 5 =>

    很显然, S3C2410_GPB(5)就是从GPA的首地址+GPA个数+GPB的offset就是当前GPB的IO偏移量,即

    0+32+5=37, 同理

    S3C2410_GPB(0) 相当于 32

    S3C2410_GPB(5) 相当于 37

    S3C2410_GPB(6) 相当于 38

    S3C2410_GPB(7) 相当于 39

    S3C2410_GPB(8) 相当于 40

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

    
    led_cfg_table[i]
    
    //LED 对应端口将要输出的状态列表
    static unsigned int led_cfg_table [] = {
    S3C2410_GPIO_OUTPUT,
    S3C2410_GPIO_OUTPUT,
    S3C2410_GPIO_OUTPUT,
    S3C2410_GPIO_OUTPUT,
    };
    
    

    S3C2410_GPIO_OUTPUT定义在mach/regs-gpio.h

    
    #define S3C2410_GPIO_LEAVE (0xFFFFFFFF) // 最后两位是设置,11表示RESERVE
    #define S3C2410_GPIO_INPUT (0xFFFFFFF0) /* not available on A */ // 最后两位是设置,00表示INPUT
    
    #define S3C2410_GPIO_OUTPUT (0xFFFFFFF1) // 最后两位是设置,01表示OUTPUT
    #define S3C2410_GPIO_IRQ (0xFFFFFFF2) /* not available for all */
    #define S3C2410_GPIO_SFN2 (0xFFFFFFF2) /* bank A => addr/cs/nand */
    #define S3C2410_GPIO_SFN3 (0xFFFFFFF3) /* not available on A */
    
    

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

    根据前面的分析,s3c2410传入了当前GPIO的偏移地址,以及OUTPUT状态

    现在我们深入前面的两个函数:

    定义在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;
    
    if (!chip)
    return -EINVAL; // 没找到的话,返回invalid
    
    offset = pin - chip->chip.base; // 否则offset等于该GPIO引脚相对于GPX(0)的偏移量,每个偏移1
    
    s3c_gpio_lock(chip, flags); // 自旋锁锁住该GPIO,通过chip指针指向lock,看下面的define和图
    ret = s3c_gpio_do_setcfg(chip, offset, config); //设置该GPIO状态寄存器的数值为config
    s3c_gpio_unlock(chip, flags); // 解锁
    
    // 自旋锁操作
    /* locking wrappers to deal with multiple access to the same gpio bank */
    //#define s3c_gpio_lock(_oc, _fl) spin_lock_irqsave(&(_oc)->lock, _fl)
    //#define s3c_gpio_unlock(_oc, _fl) spin_unlock_irqrestore(&(_oc)->lock, _fl)
    //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; // 将cfg的0,1两位左移offset
    }
    
    con = __raw_readl(reg); // 读对应的GPXCON值
    con &= ~(0x3 << shift); // 将GPXCON(pin)的两bits请0
    con |= cfg; // 设置config值
    __raw_writel(con, reg); // 写入新的GPXCON
    
    return 0;
    }
    return ret;
    } // end s3c_gpio_cfgpin
    
    

    这里涉及到了一个重要的数据结构,s3c_gpio_chip,此数据结构比较复杂,我贴出这个数据结构的结构图:

    s3c_gpio_chip

    s3c_gpio_chip

    这个重要的数据结构中可以记录每个GPIO所需要的所有数据,后面会遇到的s3c24xx_gpios[]结构体就是该结构体的集合,描述了芯片中所有的GPIO端口,之后我们需要时时回头看看这个结构。

    我们先来看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]; //根据偏移,计算出对应pin的GPIO结构体指针
    return ((pin - chip->chip.base) < chip->chip.ngpio) ? chip : NULL;
    
    // 这里验证,如果pin偏移超过了GPIO的个数,说明出错了,否则就返回该GPIO的结构体指针
    }
    
    

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

    
    /* GPIO sizes for various SoCs:
    *
    * 2442
    * 2410 2412 2440 2443 2416
    * ---- ---- ---- ---- ----
    * A 23 22 25 16 25
    * B 11 11 11 11 9
    * C 16 15 16 16 16
    * D 16 16 16 16 16
    * E 16 16 16 16 16
    * F 8 8 8 8 8
    * G16 16 16 16 8
    * H 11 11 9 15 15
    * J -- -- 13 16 --
    * K -- -- -- -- 16
    * L -- -- -- 15 7
    * M -- -- -- 2 2
    */
    
    struct s3c_gpio_chip s3c24xx_gpios[] = {
    [0] = {
    .base = S3C2410_GPACON, // datasheet上地址为0x56000000
    
    //#define S3C2410_GPACON S3C2410_GPIOREG(0x00)
    
    #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,
    },
    },
    };
    
    

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

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

    gpio_desc和gpio_chip结构图

    gpio_desc

    gpio_desc

    
    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_GPIOS在arch/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是自旋锁,上锁,保存FLAG在flags变量中
    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->request在linux初始化时是没有指向的,可以见后面s3c_gpiolib_add
    1206 /* chip->request may sleep */
    1207                    spin_unlock_irqrestore(&gpio_lock, flags);            // 如果chip->request不为0, 解锁,因为后面调用的chip->request有可能睡眠
    1208                    status = chip->request(chip, gpio - chip->base);
    1209                    spin_lock_irqsave(&gpio_lock, flags);                    // 执行完后,继续上锁
    1210
    1211                    if (status < 0) {        // status返回负数,说明出错
    1212                              desc_set_label(desc, NULL);
    1213                              module_put(chip->owner);
    1214                              clear_bit(FLAG_REQUESTED, &desc->flags);
    1215                     }
    1216 }
    1217
    1218done:
    1219 if (status)
    1220     pr_debug("gpio_request: gpio-%d (%s) status %d/n",  gpio, label ? : "?", status);

    1221    // 如果状态不为0, 打印gpio-pin"****"的状态
    1222 spin_unlock_irqrestore(&gpio_lock, flags);     // 解锁
    1223 return status;    // 返回状态
    1224}

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

    下面先分析gpio_free函数
    void gpio_free(unsigned gpio)            // 待分析
    {
    unsigned long flags;
    struct gpio_desc *desc;
    struct gpio_chip *chip;

    might_sleep();

    if (!gpio_is_valid(gpio)) {
    WARN_ON(extra_checks);
    return;
    }

    gpio_unexport(gpio);

    spin_lock_irqsave(&gpio_lock, flags);

    desc = &gpio_desc[gpio];
    chip = desc->chip;
    if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) {
    if (chip->free) {
    spin_unlock_irqrestore(&gpio_lock, flags);
    might_sleep_if(chip->can_sleep);
    chip->free(chip, gpio - chip->base);
    spin_lock_irqsave(&gpio_lock, flags);
    }
    desc_set_label(desc, NULL);
    module_put(desc->chip->owner);
    clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
    clear_bit(FLAG_REQUESTED, &desc->flags);
    } else
    WARN_ON(extra_checks);

    spin_unlock_irqrestore(&gpio_lock, flags);
    }
    EXPORT_SYMBOL_GPL(gpio_free);

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

    arch/arm/mach-s3c2410/include/mach/gpio.h

    #define gpio_set_value __gpio_set_value

    void __gpio_set_value(unsigned gpio, int value)
    {
    struct gpio_chip *chip;

    chip = gpio_to_chip(gpio);        // 返回对应于pin的gpio_desc[pin].chip指针
    WARN_ON(chip->can_sleep);
    chip->set(chip, gpio - chip->base, value);       // 这里调用的是s3c_gpiolib_set函数!!!
    }

    /* caller holds gpio_lock *OR* gpio is marked as requested */
    static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
    {
    return gpio_desc[gpio].chip;
    }

    看到这里,一直有个问题让我百思不得其解,这里的chip按理说应该是s3c_gpio_chip中的chip成员,但是之前都没有代码交代他们是如何联系到一起的,s3c_gpio_chip与gpio_desc结构体如何联系在一起,也没有函数交代,并且这里的chip->set函数指针也没有实现的代码,但是,经实验确认没有问题后,我开始查找plat-s3c24xx/gpiolib.c中的函数希望能有些线索,果然,找到了这么一个函数:

    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++) {
    if (!chip->config)
    chip->config = &s3c24xx_gpiocfg_default;          // 原来chip->config默认函数也是在这里!!!

    s3c_gpiolib_add(chip);           // 之前的疑惑都在这里实现!!!
    }

    return 0;
    }

    core_initcall(s3c24xx_gpiolib_init);

    但是,这个s3c24xx_gpiolib_init函数又是在什么时候执行的呢?可以看到,在该函数的下面,有一句:core_initcall(s3c24xx_gpiolib_init); 查阅相关资料发现, 在linux初始化的过程中,内核采用了一种initcall的机制,它利用gcc的扩展功能以及ld的连接控制脚本实现了在内核初始化的过程中通过简单的循环就实现了相关驱动的初始化

    也就是说,在linux初始化期间,就已经执行了s3c24xx_gpiolib_init,现在我们可以分析下s3c_gpiolib_add(chip);这个函数了,

    __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);

    spin_lock_init(&chip->lock);     // 初始化s3c_gpio_chip的自旋锁

    if (!gc->direction_input)
    gc->direction_input = s3c_gpiolib_input;         // direction_input 函数指针
    if (!gc->direction_output)
    gc->direction_output = s3c_gpiolib_output;     // direction_output 函数指针
    if (!gc->set)
    gc->set = s3c_gpiolib_set;        // set函数指针
    if (!gc->get)
    gc->get = s3c_gpiolib_get;        // 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

    /* gpiochip_add() prints own failure message on error. */
    ret = gpiochip_add(gc);
    if (ret >= 0)
    s3c_gpiolib_track(chip);
    }

     

    gpiochip_add函数分析:

    /**
    * gpiochip_add() - register a gpio_chip
    * @chip: the chip to register, with chip->base initialized
    * Context: potentially before irqs or kmalloc will work
    *
    * Returns a negative errno if the chip can't be registered, such as
    * because the chip->base is invalid or already associated with a
    * different chip. Otherwise it returns zero as a success code.
    *
    * When gpiochip_add() is called very early during boot, so that GPIOs
    * can be freely used, the chip->dev device must be registered before
    * the gpio framework's arch_initcall(). Otherwise sysfs initialization
    * for GPIOs will fail rudely.
    *
    * If chip->base is negative, this requests dynamic assignment of
    * a range of valid GPIOs.
    */
    int gpiochip_add(struct gpio_chip *chip)      // 在gpio_desc[]中分配空间,并链接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);   // 这个函数在gpiolib.c中,在gpio_desc[]中分配chip->ngpio个空间(从最后往前分配),返回第一个index
    if (base < 0) {          // 分配不到
    status = base;
    goto unlock;      // 解锁退出
    }
    chip->base = base;    // gpio_chip *chip->base = i (i是gpio_desc[i]中的index)

    }

    /* these GPIO numbers must not be managed by another gpio_chip */
    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与s3c_gpio_chip联系起来,他们的chip成员指向的是同一个数据结构

    /* REVISIT: most hardware initializes GPIOs as
    * inputs (often with pullups enabled) so power
    * usage is minimized. Linux code should set the
    * gpio direction first thing; but until it does,
    * we may expose the wrong direction in sysfs.
    */
    gpio_desc[id].flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0;   // 设置flags
    }
    } // end if

    of_gpiochip_add(chip);    // 没操作

    unlock:
    spin_unlock_irqrestore(&gpio_lock, flags);         // 解锁

    if (status)
    goto fail;

    status = gpiochip_export(chip);        //×××××××××××××××待分析
    if (status)
    goto fail;

    return 0;
    fail:
    /* failures here can mean systems won't boot... */
    pr_err("gpiochip_add: gpios %d..%d (%s) failed to register/n",
    chip->base, chip->base + chip->ngpio - 1,
    chip->label ? : "generic");
    return status;       // 返回状态
    }

    下面是s3c_gpiolib_track函数

    #ifdef CONFIG_S3C_GPIO_TRACK
    struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END];

    static __init void s3c_gpiolib_track(struct s3c_gpio_chip *chip)        // 没完全理解,待分析
    {
    unsigned int gpn;
    int i;

    gpn = chip->chip.base;
    for (i = 0; i < chip->chip.ngpio; i++, gpn++) {
    BUG_ON(gpn >= ARRAY_SIZE(s3c_gpios));
    s3c_gpios[gpn] = chip;
    }
    }
    #endif /* CONFIG_S3C_GPIO_TRACK */

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



沪ICP备19023445号-2号
友情链接