加入收藏 | 设为首页 | 会员中心 | 我要投稿 云计算网_泰州站长网 (http://www.0523zz.com/)- 视觉智能、AI应用、CDN、行业物联网、智能数字人!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

Linux下GPIO驱动介绍文章

发布时间:2021-12-22 12:54:18 所属栏目:PHP教程 来源:互联网
导读:注意:在/arch/arm/mach-s3c2410/include/mach/gpio-fns.h源代码中有如下说明: 16/* These functions are in the to-be-removed category and it is strongly 17 * encouraged not to use these in new code. They will be marked deprecated 18 * very soo

注意:在/arch/arm/mach-s3c2410/include/mach/gpio-fns.h源代码中有如下说明:
 
 16/* These functions are in the to-be-removed category and it is strongly
17 * encouraged not to use these in new code. They will be marked deprecated
18 * very soon.
19 *
20 * Most of the functionality can be either replaced by the gpiocfg calls
21 * for the s3c platform or by the generic GPIOlib API.
22 *
23 * As of 2.6.35-rc, these will be removed, with the few drivers using them
24 * either replaced or given a wrapper until the calls can be removed.
25*/
 
 
 
该头文件包括:
 
static inline void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int cfg)
 
该函数直接使用
 
linux/arch/arm/plat-s3c/gpio-config.c中的
 
int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)
 
即可
 
 
 
***************************************************************************
 
 
 
首先看一下设备初始化程序:
 
85 /*
86 * 设备初始化
87 */
88 static int __init dev_init(void)
89 {
90 int ret;
91 int i;
92 for (i = 0; i < 4; i++) {
93 //设置 LED 对应的端口寄存器为输出(OUTPUT)
94         if (s3c_gpio_cfgpin(led_table[i], led_cfg_table[i])<0)
 
                       printk(KERN_INFO "config pin %d failed", i);
 
 
95 printk(KERN_INFO "config pin %d failed", i);
 
95 //设置 LED 对应的端口寄存器为低电平输出,在模块加载> 结束后,四个 LED 应该是全部都是发光
96 状态
 97         s3c2410_gpio_setpin(led_table[i], 0);
98 }
 99  ret = misc_register(&misc); //注册设备
100 printk (DEVICE_NAME"/tinitialized/n"); //打印初始化信息
101 return ret;
102 }
 
 
 
可以看到,这里涉及到两个函数,分别是s3c2410_gpio_cfgpin,s3c2410_gpio_setpin,这两个函数分别对四个LED进行配置,从函数名来看,cfgpin对引脚寄存器状态进行配置,而setpin应该是对寄存器数据值进行配置,我们在分析函数之前先弄清楚传入的参数到底是什么。
 
led_table[i]
28 //LED 对应的 GPIO 端口列表
29 static unsigned long led_table [] = {
 30     S3C2410_GPB(5),
 31     S3C2410_GPB(6),
 32     S3C2410_GPB(7),
 33     S3C2410_GPB(8),
34 };
 
这里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
 
 30     S3C2410_GPB(5) 相当于 37
 31     S3C2410_GPB(6) 相当于 38
 32     S3C2410_GPB(7) 相当于 39
 33     S3C2410_GPB(8) 相当于 40
 
***************************************************************************
 
 led_cfg_table[i]
 
36 //LED 对应端口将要输出的状态列表
37 static unsigned int led_cfg_table [] = {
38 S3C2410_GPIO_OUTPUT,
39 S3C2410_GPIO_OUTPUT,
40 S3C2410_GPIO_OUTPUT,
41 S3C2410_GPIO_OUTPUT,
42 };
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,此数据结构比较复杂,我贴出这个数据结构的结构图:
 

 
这个重要的数据结构中可以记录每个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,
},
},
};

(编辑:云计算网_泰州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读