Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1248598
  • 博文数量: 160
  • 博客积分: 4132
  • 博客等级: 中校
  • 技术积分: 2086
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-06 21:56
文章分类

全部博文(160)

文章存档

2012年(25)

2011年(120)

2010年(15)

分类: 嵌入式

2010-11-11 10:55:57

摘自:
 
mini2440 led驱动相关内核GPIO的操作函数解析
参考:
http://hi.baidu.com/zhxust/blog/item/c8 ... d9257.html
http://blog.sina.com.cn/s/blog_4da4ea3c0100gc93.html

一, 内核版本:Linux 2.6.13
驱动函数:mini2440 手册中的led驱动。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "leds"
#define LED_MAJOR 231

static unsigned long led_table [] = {
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB7,
S3C2410_GPB8,
};

static unsigned int led_cfg_table [] = {
S3C2410_GPB5_OUTP,
S3C2410_GPB6_OUTP,
S3C2410_GPB7_OUTP,
S3C2410_GPB8_OUTP,
};

static int qq2440_leds_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
switch(cmd) {
case 0:
case 1:
if (arg > 4) {
return -EINVAL;
}
s3c2410_gpio_setpin(led_table[arg], !cmd);
return 0;
default:
return -EINVAL;
}
}

static struct file_operations qq2440_leds_fops = {
.owner = THIS_MODULE,
.ioctl = qq2440_leds_ioctl,
};

static int __init qq2440_leds_init(void)
{
int ret;
int i;
ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &qq2440_leds_fops);
if (ret < 0) {
printk(DEVICE_NAME " can't register major number\n");
return ret;
}
devfs_mk_cdev(MKDEV(LED_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME);
for (i = 0; i < 4; i++) {
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
s3c2410_gpio_setpin(led_table[i], 1);
}
printk(DEVICE_NAME " initialized\n");
return 0;
}

static void __exit qq2440_leds_exit(void)
{
devfs_remove(DEVICE_NAME);
unregister_chrdev(LED_MAJOR, DEVICE_NAME);
}

module_init(qq2440_leds_init);
module_exit(qq2440_leds_exit);


二, 相关内核头文件,宏定义及函数。

1, 在kernel2.6.13/include/asm-arm/mach-s3c2410/map.h及regs-gpio.h
下定义了GPIO寄存器的物理地址及虚拟地址。摘取如下:

在map.h中:

/* GPIO ports */
#ifndef __ASSEMBLY__
#define S3C2410_ADDR(x) ((void __iomem *)0xF0000000 + (x))
#else
#define S3C2410_ADDR(x) (0xF0000000 + (x))
#endif
………….
#define S3C2400_ADDR(x) S3C2410_ADDR(x)
#define S3C24XX_VA_GPIO S3C2410_ADDR(0x00E00000) //GPIO的虚拟地址
#define S3C2400_PA_GPIO (0x15600000)
#define S3C2410_PA_GPIO (0x56000000)
#define S3C24XX_SZ_GPIO SZ_1M

在regs-gpio.h 中:

#define S3C2410_GPIONO(bank,offset) ((bank) + (offset))
#define S3C2410_GPIOREG(x) ((x) + S3C24XX_VA_GPIO)
#define S3C2410_GPIO_BANKA (32*0)
#define S3C2410_GPIO_BANKB (32*1)
#define S3C2410_GPIO_BANKC (32*2)
………….
#define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO) //GPIO寄存器的基地址
#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31) // GPIO寄存器偏移地址
…………
#define S3C2410_GPBCON S3C2410_GPIOREG(0x10)
#define S3C2410_GPBDAT S3C2410_GPIOREG(0x14)
#define S3C2410_GPBUP S3C2410_GPIOREG(0x18)

/* no i/o pin in port b can have value 3! */

#define S3C2410_GPB0 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 0)
#define S3C2410_GPB0_INP (0x00 << 0)
#define S3C2410_GPB0_OUTP (0x01 << 0)
#define S3C2410_GPB0_TOUT0 (0x02 << 0)

#define S3C2410_GPB1 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 1)
#define S3C2410_GPB1_INP (0x00 << 2)
#define S3C2410_GPB1_OUTP (0x01 << 2)
#define S3C2410_GPB1_TOUT1 (0x02 << 2)
……………

#define S3C2410_GPB5 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 5)
#define S3C2410_GPB5_INP (0x00 << 10)
#define S3C2410_GPB5_OUTP (0x01 << 10)
#define S3C2410_GPB5_nXBACK (0x02 << 10)
…………..
在/kernel-2.6.13/arch/arm/mach-s3c2410/下有使用以上宏定义的gpio.c
其中的函数:

这里设pin为S3C2410_GPB5
void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)
{
/*基地址的获取*/
/* ~11111=1110 0000 ,(pin) & ~31 即是屏蔽掉末5位,保留其余位*/
/*然后右移1位*/
/*为什么要右移一位呢?*/
/*因为偏移值只需要占用4位就可以了,能表示16以内的就行,可以一次改变2位寄存器的值*/
/*最终就是找到寄存器的基地址*/
void __iomem *base = S3C2410_GPIO_BASE(pin);
/*S3C2410_GPB5 = 32*1+5 = 10 0101*/
/*S3C2410_GPB5 & ~31 = 10 0000 */
/*(S3C2410_GPB5 & ~31) >> 1 =0x10*/
/*((S3C2410_GPB5 & ~31) >> 1) + S3C2410_VA_GPIO = 0xF0E0 0000 + 0x10 = 0xF0E0 0010 */
/*也就是寄存器GPB的地址为0xF0E0 0010*/
/*而其偏移值5,表示要对[11:10]两位赋值*/

/*掩码*/
unsigned long mask;

/*控制字*/
unsigned long con;

/*标志*/
unsigned long flags;

/*掩码的设置*/
/*bankA是一位一位的改变寄存器的值*/
/*而之后的bank则是两位两位地改变寄存器的值*/
/*这些都是通过设置掩码来实现的*/
/*如bankA的掩码设置可以在[31:0]的任一位设置为1*/
/*而其余bank则只能在偶数位开始的两位设置为1*/

if (pin < S3C2410_GPIO_BANKB) /*如果pin < 32,即为bankA*/
{
mask = 1 << S3C2410_GPIO_OFFSET(pin); /*则只保留末5位*/
/*32,很值得推敲的数字,即为寄存器的32位,而S3C2410_GPIO_OFFSET(pin)表示寄存器的哪一位*/
/*如果是0 0101,就表示是[5]位*/
}
else /*pin >= 32,bankB=32*1*/
{
mask = 3 << S3C2410_GPIO_OFFSET(pin)*2; /*掩码,一次处理两位*/
/*S3C2410_GPIO_OFFSET(pin) = 5 */
/* 5*2 = 10 */
/* 3 << 10 即 [11:10] = 01 */
/*就是处理两位*/
}


/*中断保留*/
local_irq_save(flags);
/*读取寄存器的值*/

/*如读取寄存器GPB的值*/
con = __raw_readl(base + 0x00);

/*掩码处理*/
con &= ~mask; /*把[11:10]位置0,其余位保留*/
/*就是空出[11:10]位,好用来添加功能值*/

/*添加功能*/
con |= function; /*把[11:10]位添加为10*/
/*可见,bankB以后的寄存器,都是一次处理两位*/

/*写入寄存器*/
__raw_writel(con, base + 0x00); /*往bankH寄存器里写值*/


/*中断恢复*/
local_irq_restore(flags);
}

这样s3c2410_gpio_setpin()就不难理解了:
void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
{
void __iomem *base = S3C2410_GPIO_BASE(pin);
unsigned long offs = S3C2410_GPIO_OFFSET(pin);
unsigned long flags;
unsigned long dat;
local_irq_save(flags);
dat = __raw_readl(base + 0x04);
dat &= ~(1 << offs);
dat |= to << offs;
__raw_writel(dat, base + 0x04);
local_irq_restore(flags);
}


以上是本人参考网友资料和自己做的修改,如有不对的地方,请大家指正!

阅读(1926) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~