您的位置:首页 > 其它

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分析!


管脚功能选择寄存器

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: