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, }, }, }; ![]() (编辑:云计算网_泰州站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |