2416,LED驱动分析
2017-01-11 13:55
357 查看
/*********************************************************************************** * drivers/char/my2416_leds.c * 功能简要: * 该驱动注册一个字符设备“/dev/my-leds”, 用于4个LED。 * 函数简介: * setGPMDAT_For_LED(int ON_OFF,int which_led), 用于点光亮LED,或者灭掉LED * 提供的外部接口: * ioctol(struct inode *inode,struct file *file,unsigned int brightness); * 用于LED的亮,灭。 * 调用实例: * 提供控制台,命令式的测试程序。 * 提供QT4界面化的测试程序 * 说明:使用的内核linux3.1.0 * *************************************************************************************/ #include <linux/miscdevice.h> //struct miscdevice #include <linux/delay.h> // mdelay() #include <mach/hardware.h> //该头文件含#include <asm/io.h>这个头文件声明__raw_writel,__raw_readl #include <linux/kernel.h> //printk() #include <linux/module.h>//module_init,module_exit,MODULE_LICENSE,MODULE_AUTHOR,MODULE_DESCRIPTION #include <linux/init.h>//__init ,__exit #include <linux/fs.h> //struct file_operations,struct inode #include <linux/ioctl.h> #include <asm/uaccess.h>//包含这个头文件<asm-generic/errno.h>,该头文件EINVAL #include <mach/gpio-fns.h> //s3c2410_gpio_cfgpin,s3c2410_gpio_setpin #include <mach/regs-gpio.h> //S3C2410_GPBX_OUTP,S3C2410_GPACON,S3C2410_GPADAT,这头文件包含了<gpio-nrs.h>,该头文件中定义了S3C2410_GPB #include <plat/gpio-cfg.h> //s3c_gpio_cfgpin,s3c_gpio_setpull #define DEBUG_ME_LEDS 0 #define DEVICE_NAME "led" /* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */ #define IOCTL_GPIO_ON 1 #define IOCTL_GPIO_OFF 0 /* 用来指定LED所用的GPIO引脚 */ static unsigned long gpio_table [] = { S3C2410_GPB(5), S3C2410_GPB(6), S3C2410_GPA(23), // S3C2410_GPA(24), }; /* 用来指定GPIO引脚的功能:输出 */ static unsigned int gpio_cfg_table [] = { S3C2410_GPB5_OUTP, S3C2410_GPB6_OUTP, S3C2410_GPA23_OUTP, // S3C2410_GPA24_OUTP, //对这些管脚可以单独控制,程序中GPA端口的24管脚是单独控制 }; #if DEBUG_ME_LEDS my2416_debug_leds(unsigned int cmd,unsigned long arg) { s3c2410_gpio_setpin(gpio_table[arg], cmd); } #endif static int my2416_gpio_open(struct inode *inode, struct file *file) { #if DEBUG_ME_LEDS printk("leds on\n"); my2416_debug_leds(1,0); mdelay(1000); printk("leds off\n"); my2416_debug_leds(0,0); mdelay(1000); printk("leds on\n"); my2416_debug_leds(1,0); mdelay(1000); printk("leds off\n"); my2416_debug_leds(0,0); mdelay(1000); printk("leds on\n"); my2416_debug_leds(1,0); mdelay(1000); printk("leds off\n"); my2416_debug_leds(0,0); #endif return 0; } static long my2416_gpio_ioctl(struct file *file,unsigned int cmd,unsigned long arg) { if (arg > sizeof(gpio_table)/sizeof(gpio_table[0])) { return -EINVAL; } switch(cmd) { case IOCTL_GPIO_ON: // 设置指定引脚的输出电平为1 if(arg==3) { __raw_writel(__raw_readl(S3C2410_GPADAT)|(1<<24), S3C2410_GPADAT); } else { s3c2410_gpio_cfgpin(gpio_table[arg], gpio_cfg_table[arg]); s3c2410_gpio_setpin(gpio_table[arg], 1); } return 0; case IOCTL_GPIO_OFF: // 设置指定引脚的输出电平为0 if(arg==3) { __raw_writel(__raw_readl(S3C2410_GPADAT)&(~(1<<24)), S3C2410_GPADAT); } else { s3c2410_gpio_setpin(gpio_table[arg], 0); } return 0; default: return -EINVAL; } } /*驱动接口设置*/ static struct file_operations dev_fops = { .owner = THIS_MODULE, .unlocked_ioctl = my2416_gpio_ioctl, .open = my2416_gpio_open, }; /*设备结构的设置*/ static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &dev_fops, }; /*初始化设备,配置对应的IO,以及注册设备*/ static int __init dev_init(void) { int ret; int i; __raw_writel(__raw_readl(S3C2416_GPBSEL)&(~(1<<0)), S3C2416_GPBSEL);//GPBSEL在S3C2440中没有这样的寄存器,从最后面的图“管脚功能选择寄存器”,几个管脚是啥功能受它控制,这里的GPB6受其控制。 for (i = 0; i < sizeof(gpio_table)/sizeof(gpio_table[0]); i++) { s3c_gpio_cfgpin(gpio_table[i], gpio_cfg_table[i]); s3c2410_gpio_setpin(gpio_table[i], 1); if(i<2) s3c_gpio_setpull(gpio_table[i], S3C_GPIO_PULL_NONE); } __raw_writel(__raw_readl(S3C2410_GPACON)&(~(1<<24)), S3C2410_GPACON);//这里将GPA端口的24管脚配置为输出。 __raw_writel(__raw_readl(S3C2410_GPADAT)|(1<<24), S3C2410_GPADAT);//这里将24管脚的设置为1。 ret = misc_register(&misc); if(ret) { printk (KERN_ERR "register miscdev \"%s\" failed!\n", DEVICE_NAME); return ret; } printk (DEVICE_NAME" initialized\n"); return ret; } /*注销设备*/ static void __exit dev_exit(void) { misc_deregister(&misc); } module_init(dev_init); module_exit(dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("www.maxgps.com"); MODULE_DESCRIPTION("LED driver for my2416 Board");
驱动程序配置寄存器和设置寄存器,是对虚拟地址进行操作,而不是物理地址,获取GPIO寄存器的虚拟地址有两种方法。第一种:静态映射法,使用系统已经配置好的虚拟地址,在系统启动时,已经生成了页表。第二种:动态映射法,使用ioremap函数映射获取虚拟地址。
先分析宏S3C2410_GPB(5),S3C2410_GPB5_OUTP,其他宏类似,在文件中 /arch/arm/mach-s3c2410/include/mach/regs-gpio.h/文件对这些宏进行了定义
#define S3C2410_GPB5_OUTP (0x01 << 10)
配置为输出,GPB寄存器每两位控制一根管脚
#define S3C2410_GPB(_nr) (S3C2410_GPIO_B_START + (_nr))
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_GPIO_NEXT(__gpio) \ ((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 0) //这里的##表示连着的意思。CONFIG_S3C_GPIO_SPACE是等于0在“.config“文件中定义。 #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. */ 由上述代码推出:S3C2410_GPB(5) = (S3C2410_GPIO_B_START + (5)) S3C2410_GPB(5) = S3C2410_GPIO_NEXT(S3C2410_GPIO_A)+ (5)) S3C2410_GPB(5) = ((S3C2410_GPIO_A_START) + (S3C2410_GPIO_A_NR) + CONFIG_S3C_GPIO_SPACE + 0)+(5)) S3C2410_GPB(5) = ((0) + (32) + 0 + 0)+(5)) = 37 在头文件中#include <mach/gpio-fns.h>定义了函数s3c2410_gpio_cfgpin,s3c2410_gpio_setpin static inline void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int cfg) { /* 1:1 mapping between cfgpin and setcfg calls at the moment */ s3c_gpio_cfgpin(pin, cfg); } int s3c_gpio_cfgpin(unsigned int pin, unsigned int config) { struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin);//获取GPIO一些信息,结构体s3c_gpio_chip中包含了寄存器基址,中断号,自旋锁等等,详细定义见下文。 unsigned long flags; int offset; int ret; if (!chip) //判断是否是有有效管脚 return -EINVAL; offset = pin - chip->chip.base; s3c_gpio_lock(chip, flags);//并发机制,加锁 ret = s3c_gpio_do_setcfg(chip, offset, config); s3c_gpio_unlock(chip, flags);//并发机制,解锁 return ret; } struct s3c_gpio_chip { struct gpio_chip chip; struct s3c_gpio_cfg *config;//特殊功能和上拉的一些控制信息 struct s3c_gpio_pm *pm; void __iomem *base;//配置寄存器的基址 int irq_base;//中断号 int group; spinlock_t lock;//自旋锁,这在linux2.6驱动中是没有这个。与linux2.6内核,s3c驱动发生较大的变化。 #ifdef CONFIG_PM u32 pm_save[4];//保存一些有关挂起与恢复一些信息 #endif }; static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int chip) { return (chip < S3C_GPIO_END) ? s3c_gpios[chip] : NULL; //判断芯片管教是否有效,静态数组s3c_gpios[chip] 获取芯片一些信息,如果配置了该款芯片,在内核启动的时候,将一些配置信息都存入了这个数组。在头文件中< linux/arch/arm/plat-s3c/gpio.c>定义了s3c_gpios[S3C_GPIO_END] } 通过定为查找发现在函数中对该数组赋值s3c_gpiolib_track 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; } } s3c_gpiolib_track这个函数在何处调用呢? __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); 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 /* gpiochip_add() prints own failure message on error. */ ret = gpiochip_add(gc); if (ret >= 0) s3c_gpiolib_track(chip); } 何处调用了s3c_gpiolib_add 定位发现在头文件/* linux/arch/arm/plat-s3c24xx/gpiolib.c*/ struct s3c_gpio_chip s3c24xx_gpios[] = { [0] = { //这个数组定义了GPIO的虚拟地址,使用静态映射,每个GPIO组包含的gpio数目,输入还是输出等等 .base = S3C2410_GPACON, .pm = __gpio_pm(&s3c_gpio_pm_1bit), .config = &s3c24xx_gpiocfg_banka, .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 = 15, }, }, /* GPIOS for the S3C2443 and later devices. */ { .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, }, }, }; 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; s3c_gpiolib_add(chip); } return 0; } 在内核初始化函数s3c24xx_gpiolib_init调用了s3c_gpiolib_add(chip)。 通过这些函数我们知道了s3c_gpios[S3C_GPIO_END]就是通过这样调用获取它们GPIO管脚的虚拟地址,我们在回到函数int s3c_gpio_cfgpin(unsigned int pin, unsigned int config),其中调用的函数ret = s3c_gpio_do_setcfg(chip, offset, config); 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); } struct s3c_gpio_cfg { unsigned int cfg_eint; s3c_gpio_pull_t (*get_pull)(struct s3c_gpio_chip *chip, unsigned offs); int (*set_pull)(struct s3c_gpio_chip *chip, unsigned offs, s3c_gpio_pull_t pull); unsigned (*get_config)(struct s3c_gpio_chip *chip, unsigned offs); int (*set_config)(struct s3c_gpio_chip *chip, unsigned offs, unsigned config); }; 这个结构体中 int (*set_config)(struct s3c_gpio_chip *chip, unsigned offs, unsigned config);//函数指针,这里指向了某个函数,我们定位到这个函数 struct s3c_gpio_cfg s3c24xx_gpiocfg_default = { .set_config = s3c_gpio_setcfg_s3c24xx,//指向了这个默认的函数 .get_config = s3c_gpio_getcfg_s3c24xx, }; 默认的函数定义如下: ifdef CONFIG_S3C_GPIO_CFG_S3C24XX int s3c_gpio_setcfg_s3c24xx_a(struct s3c_gpio_chip *chip, unsigned int off, unsigned int cfg) { void __iomem *reg = chip->base; //这个函数是不是与linux2.6中驱动有点类似 unsigned int shift = off; u32 con; if (s3c_gpio_is_cfg_special(cfg)) { cfg &= 0xf; /* Map output to 0, and SFN2 to 1 */ cfg -= 1; if (cfg > 1) return -EINVAL; cfg <<= shift; } con = __raw_readl(reg); con &= ~(0x1 << shift); con |= cfg; __raw_writel(con, reg); return 0; } 到这里我们完成了对函数s3c2410_gpio_cfgpin分析!
管脚功能选择寄存器
相关文章推荐
- 由简单的LED驱动分析内核源码包中的s3c2410寄存器宏定
- LED驱动分析s3c2410_gpio_setpin()
- mini2440 led驱动程序经典分析
- Tiny6410 led 驱动实现分析
- mini2440led驱动分析
- Analysis of LED Driver of Marvell (Marvell PXA310 LED驱动代码分析)
- Android GPIO LED 驱动与HAL分析
- linux驱动分析之LED
- 2416的LED驱动 for wince6
- Tiny6410 led 驱动实现分析
- 由简单的LED驱动分析内核源码包中的s3c2410寄存器宏定
- LED驱动GPIO相关头文件简要分析
- mini2440驱动分析之LED
- LED驱动分析
- Android GPIO LED 驱动与HAL分析
- LED驱动分析s3c2410_gpio_setpin()
- 2440驱动分析——LED驱动
- micro2440驱动分析1——LED驱动
- Tiny6410 led 驱动实现分析
- LED设备驱动二之重要代码和调试问题分析