2416,LED驱动分析

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

管脚功能选择寄存器
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值