linux内核中2410_gpio_cfgpin函数分析

2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPIO_OUTPUT) 函数分析:作用设置相应GPIO口的工作模式,输入、输出、中断等。
一,首先宏展开:为方便描述,展开后用等号表示

#define S3C2410_GPIO_OUTPUT  (0xFFFFFFF1)          
#define S3C2410_GPB0         S3C2410_GPIONO(S3C2410_GPIO_BANKB, 0)    #define S3C2410_GPIO_BANKB   (32*1)
#define S3C2410_GPIONO(bank,offset) ((bank) + (offset))

所以S3C2410_GPB0 = S3C2410_GPIONO((32*1), 0)= (32*1);
S3C2410_GPIO_OUTPUT = (0xFFFFFFF1);
最终得到:
s3c2410_gpio_cfgpin(32, 0xFFFFFFF1)
二、接下来进入s3c2410_gpio_cfgpin函数分析:

void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)
{
    void __iomem *base = S3C24XX_GPIO_BASE(pin);
    unsigned long mask;
    unsigned long con;
    unsigned long flags;

    if (pin < S3C2410_GPIO_BANKB) {
        mask = 1 << S3C2410_GPIO_OFFSET(pin);
    } else {
        mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;
    }

    switch (function) {
    case S3C2410_GPIO_LEAVE:
        mask = 0;
        function = 0;
        break;

    case S3C2410_GPIO_INPUT:
    case S3C2410_GPIO_OUTPUT:
    case S3C2410_GPIO_SFN2:
    case S3C2410_GPIO_SFN3:
        if (pin < S3C2410_GPIO_BANKB) {
            function -= 1;
            function &= 1;
            function <<= S3C2410_GPIO_OFFSET(pin);
        } else {
            function &= 3;
            function <<= S3C2410_GPIO_OFFSET(pin)*2;
        }
    }

    /* modify the specified register wwith IRQs off */

    local_irq_save(flags);

    con  = __raw_readl(base + 0x00);
    con &= ~mask;
    con |= function;

    __raw_writel(con, base + 0x00);

    local_irq_restore(flags);
}

1、S3C24XX_GPIO_BASE(pin)宏展开:

#define S3C24XX_GPIO_BASE(x)  S3C2410_GPIO_BASE(x)
#define S3C2410_GPIO_BASE(pin)   ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
#define S3C24XX_VA_GPIO    ((S3C2410_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)
#define S3C2410_PA_GPIO    (0x56000000)
#define S3C24XX_PA_UART     S3C2410_PA_UART   ——>#define S3C2410_PA_UART       (0x50000000)   
#define S3C24XX_VA_UART    S3C2410_ADDR(0x00400000)   ——>#define S3C2410_ADDR(x)      (0xF0000000 + (x))

将宏全部替换:

S3C2410_PA_GPIO = 0x56000000  
S3C24XX_PA_UART = 0x50000000
S3C24XX_VA_UART = 0xF0400000//虚拟地址

S3C24XX_VA_GPIO = ((0x56000000 - 0x50000000) + 0xF0400000)  = 0xF6400000

S3C24XX_GPIO_BASE(32) = ((((32) & ~31) >> 1) + 0xF6400000)  = 0xF6400010

所以S3C24XX_GPIO_BASE(pin)宏展开后为:
S3C24XX_GPIO_BASE(32) = ((((32) & ~31) >> 1) + 0xF6400000) = 0xF6400010
作用:获得所要配置的控制寄存器的基地址,注意这是虚拟地址

2、S3C2410_GPIO_OFFSET宏展开:

#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31)

3、通过分析最终得到
pin = 32;
function= 0xFFFFFFF1 = S3C2410_GPIO_OUTPUT;
base = 0xF6400010;
mask = 3;
function &= 3; ——> function = 0x01;

__raw_readl,__raw_writel分析:
__raw_readl和__raw_writel
Linux对I/O的操作都定义在asm/io.h中,相应的在arm平台下,就在asm-arm/io.h中。

#define __raw_readl(a)   (__chk_io_ptr(a), *(volatile unsigned int __force   *)(a))
#define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force   *)(a) = (v))

在include\linux\compiler.h中:

#ifdef __CHECKER__
……
extern void __chk_io_ptr(void __iomem *);
#else
……
# define __chk_io_ptr(x) (void)0
……
#endif

raw_readl(a)展开是:((void)0, (volatile unsigned int _force )(a))。在定义了__CHECKER的时候先调用__chk_io_ptr检查该地址,否则__chk_io_ptr什么也不做,(volatile unsigned int _force )(a)就是返回地址为a处的值

所以以下代码进行寄存器的设置,使GPIO口的工作模式为,输入、输出、中断等:

    con  = __raw_readl(base + 0x00);//从基地址对应的寄存器中取出值
    con &= ~mask;//将我们要设置的相应位清零,其他位不变
    con |= function;//对们要设置的相应位进行设置
    __raw_writel(con, base + 0x00);//写回寄存器

mask = 3;
function = 0x01;
s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPIO_OUTPUT)通过这个函数名和参数起始我们也能看出他的作用:设置GPBCON寄存器,使得GPB0为OUTPUT,即输出。
GPB0对应GPBCON在0、1位,con &= ~mask即con &= ~3,j就是清除我们要设置的相应位,con |= function;对们要设置的相应位进行设置,con |= 0x01;就是把0、1位设置为01,即输出。所以与通过这个函数名和参数起始得出作用一致。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值