((((pin) & ~31) >> 1)到底是神马意思?这个主要靠理解,刚才上面说了每组端口定义为32个,((pin) & ~31)相当于就是把低五位全部清零,而第五位所能代表的范围正好是32,有点以大小32进行对其的意思。如果将得到的数值右移5位的话,得到的数值(设为ppvalue)能正好代表是哪组I/O口。这里为什么右移1位呢,我们看下
几个GPXCON寄存器的物理地址。
GPACON 0X56000000
GPBCON 0X56000010
GPCCON 0X56000020
GPDCON 0X56000030
其他的以此类推,可以看出这个I/O口控制寄存器的规律,如果将ppvalue左移四位,加上GPIO虚拟基地址,就能得到GPXCON控制寄存器的虚拟地址了。顺便说下,这里的虚实地址的映射只是相差了一个偏移量。
分析:if (pin < S3C2410_GPIO_BANKB)
S3C2410_GPIO_BANKB的定义如下
#define S3C2410_GPIO_BANKA (32*0)
#define S3C2410_GPIO_BANKB (32*1)
#define S3C2410_GPIO_BANKC (32*2)
#define S3C2410_GPIO_BANKD (32*3)
#define S3C2410_GPIO_BANKE (32*4)
#define S3C2410_GPIO_BANKF (32*5)
#define S3C2410_GPIO_BANKG (32*6)
#define S3C2410_GPIO_BANKH (32*7)
用于判断此I/O口是否为GPA端口,这是为了区分开GPA与其他各组端口,因为GPA控制寄存器的操作和其他的有点区别,另外要注意,它是没有输入功能的。看datasheet能够更好的了解。
分析:S3C2410_GPIO_OFFSET(pin)
#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31) //用此宏能得出偏移量
if (pin < S3C2410_GPIO_BANKB) { //判断I/O口是不是属于GPA, mask = 1 << S3C2410_GPIO_OFFSET(pin);//设置屏蔽码
} else {
mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;//设置屏蔽码
}
分析:local_irq_save(flags);
这个与下面出现的local_irq_restore(flags);成对使用,用于关闭、打开中断,同时将中断的标志存储在flags中。
分析:__raw_readl(base + 0x00); __raw_writel(con, base + 0x00);
con = __raw_readl(base + 0x00); //读取控制寄存器数据
con &= ~mask; //屏蔽掉相应的位
con |= function; //设置要设置的位
__raw_writel(con, base + 0x00);//把改变后的数据写回控制寄存器
上面的是两个函数宏,定义如下
#define __raw_writeb(v,a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a) = (v))
#define __raw_writew(v,a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v))
#define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a) = (v))
PS(ZXX):先检查指针a是否合法,然后将数值v写入a所指向的空间。
三种类型分别对应char,short,int
#define __raw_readb(a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a))
#define __raw_readw(a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a))
#define __raw_readl(a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a))
PS(ZXX):先检查指针a是否合法,然后读取a所指向的空间的数值。
三种类型分别对应char,short,int
5.分析s3c2410_gpio_setpin(led_table[i], 0);
void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
{
void __iomem *base = S3C24XX_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);
}
有了上述的对s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i])的分析,上面的代码大同小异罢了,只是说一下__raw_readl(base + 0x04);这个,这是对数据寄存器进行操作,看datasheet就知道,每组的GPXDAT的地址值都比GPXCON的地址值大4。
总结:到此这个led驱动分析就结束了,程序虽小,里面涉及到的东西还是不少的,主要是刚入门者感觉很乱主要是对刚入门的同志有一定的帮助。关于这个驱动程序有几个版本,内核也在不断改变。我同时贴出另一个转载来的也是led驱动程序的文章,但里面涉及的结构数据比这个要复杂点,在我的博客里面可以找到,看懂的话还是能学到不少东西的。