驱动相关的内核代码分析

arch\arm\include\asm\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))

注:(volatile unsigned int __force   *)指针强制转换为unsigned int型。

其中volatile关键字有以下用途:

(1)用来同步,因为同一个东西可能在不同的存储介质中有多个副本,有些情况下会使得这些副本中的值不同,这是不允许的,所以干脆用volatile,让它只有一个,没有其他的副本,这样就不会发生不同步的问题。

如下所示:

volatile的意思是告诉编译器,在编程源代码时,对这个变量不要使用优化。
在一般的程序设计中,如:
int *a; int b;
b = (*a) * (*a);这种情况。
通常编译器为了减少存储器的读写时间,会把代码优化为:
int *a; int b; int c;
c = *a;
b = c * c;
如果把int *a改为volatile int* a编译器就不会自动把它优化掉了。在整个运算过程中,对变量*a的值又读取了一次。防止因变量*a的值在这一期间发生了改变,而导致程序结果的错误。

(2)防止编译器优化去掉某些语句,像我在arm中见到个寄存器非常奇怪,当中断来的时候,相对应的位置1,而清0又不能向这位写0,向这位写1才是1才是清中断(清0),

// 假设0x560012300 为寄存器地址
#define INTPAND *(volatile unsigned int *)0x560012300

INTPAND = INTPAND; // 清中断

像编译器如果看到有INTPAND = INTPAND;这种看似无用的操作,如果没有volatile说明,编译器就很有可能会去掉INTPAND = INTPAND;实际上有用的东西,却被编译器当没用的东西优化掉了。

(3)当地址是io端口的时候,读写这个地址是不能对它进行缓存的,这是相对于某些嵌入式中有cache才有这个。比如写这个io端口的时候,如果没有这个volatile,很可能由于编译器的优化,会先把值先写到一个缓冲区,到一定时候再写到io端口,这样就不能使数据及时的写到io端口,有了volatile说明以后,就不会再经过cache,write buffer这种,而是直接写到io端口,从而避免了读写io端口的延时。

在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处的值。(void)xx的做法有时候是有用的,例如编译器打开了检查未使用的参数的时候需要将没有用到的参数这么弄一下才能 编译通过。

注:语句表达式 x= (y=(a+b), z=10); 的执行过程:顺序执行括号内的语句,注意各语句分隔使用的是逗号,把最后一个语句z的值赋给x。

CPU对I/O的物理地址的编程方式有两种:一种是I/O映射,一种是内存映射。 __raw_readl和__raw_writel等是原始的操作I/O的方法,由此派生出来的操作方法有:inb、outb、 _memcpy_fromio、readb、writeb、ioread8、iowrite8等。



S3C2410GPIO端口的宏定义 arch/arm/mach-s3c2410/include/mach/regs-gpio.h

1.S3C2410_GPB5是端口编号,定义在regs-gpio.h中,

#define S3C2410_GPIO_BANKB   (32*1)
#define S3C2410_GPIONO(bank,offset)   ((bank) + (offset))
#define S3C2410_GPB5         S3C2410_GPIONO(S3C2410_GPIO_BANKB, 5)

S3C2410共有130个GPIO,分为9组(GPA~GPJ),每组最多可以有 32个,每个GPIO有2~4个可选功能,每组的控制寄存器空间有4个,例如对于GPB,有GPBCON、GPBDAT、GPBUP和Reserved, 分别是功能配置、数据缓存、上拉使能和保留。

上面的S3C2410_GPB5就是GPIO的编号,也就是在号码空间(0~32*9-1)中的位置,bank是分组的基号码,offset是组内偏移量。(也就是说把所有的IO口从0开始进行统一的编号如:S3C2410_GPA0=0,S3C2410_GPA1=1,S3C2410_GPB0=32,S3C2410_GPC0=48等)

2.S3C2410_GPB5_OUTP是端口功能,定义在regs-gpio.h中,

#define S3C2410_GPB5_INP     (0x00 << 10)
#define S3C2410_GPB5_OUTP    (0x01 << 10)

GPBCON的第10、11两位用于配置GPB5的功能,00 = Input ,01 = Output

3.S3C2410 GPIO的操作函数

在hardware.h文件中有:

s3c2410_gpio_cfgpin     //配置端口的GPIO的功能
s3c2410_gpio_getcfg     //读取功能配置
s3c2410_gpio_pullup     //配置上拉电阻
s3c2410_modify_misccr //杂项配置

s3c2410_gpio_getirq      //给定端口,转换出IRQ号
s3c2410_gpio_irqfilter    //配置IRQ过滤使能与否

s3c2410_gpio_setpin     //写数据到端口
s3c2410_gpio_getpin     //从端口读数据

这些函数的实现在gpio.h中

void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
{
void __iomem *base = S3C2410_GPIO_BASE(pin);//算出端口所在组虚拟基址如://GPA=0xF0E00000   

                                                                                                                               //GPB=0XF0E00010

unsigned long offs = S3C2410_GPIO_OFFSET(pin);        //算出端口所在组的偏移量(0~31)
unsigned long flags;
unsigned long dat;

local_irq_save(flags);

dat = __raw_readl(base + 0x04);    //虚拟基址加0x04为 GP*DAT寄存器,加0x00为GP*ON等

//读出当前GP*DAT寄存器的值                
dat &= ~(1 << offs);     //根据offs偏移量对该寄存器中选中的 位 清零,其他位保持不变
dat |= to << offs;         //根据形参对要求的位进行位操作,来实现对具体某个IO口的配置
__raw_writel(dat, base + 0x04);     //将配置写入到寄存器(这里是虚拟地址)

local_irq_restore(flags);
}

4.S3C2410_GPIO_BASE和S3C2410_GPIO_OFFSET也是在regs-gpio.h文件中定义,

#define S3C2410_GPIO_BASE(pin)       ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
#define S3C2410_GPIO_OFFSET(pin)   ((pin) & 31)

而在map.h中有:

/* GPIO ports */
#define S3C24XX_VA_GPIO    S3C2410_ADDR(0x00E00000) //虚拟地址S3C24XX_VA_GPIO= 0xF0E00000
#define S3C2400_PA_GPIO    (0x15600000)
#define S3C2410_PA_GPIO    (0x56000000) //GPACON 物理地址
#define S3C24XX_SZ_GPIO    SZ_1M //0x100000 = 1024 *1024

S3C2410_GPIO_BASE作用是:根 据端口编号pin,算出端口所在组的虚拟基址。((pin) & ~31)是去掉pin当中小于等于31的零头(清0低5位),>>1的原因是每组GPIO中最多可以有32个端口,控制这些端口需要4个寄存 器空间,4个寄存器空间就需要4*4=16个字节进行编址,32/16=2,左移一位刚好满足。也就是说,上一组端口和下一组端口的编号相差32,而控制 寄存器的地址相差16。

S3C2410_GPIO_OFFSET作用是:根据端口编号pin,算出端口所在组的偏移量。((pin) & 31)即去掉比31大的数(清0第6位以上的位)。  

 



驱动相关的内核代码分析

时间: 2024-07-30 10:17:07

驱动相关的内核代码分析的相关文章

一个简单的时间片轮转多道程序内核代码分析 (学号后三位418)

一.基于mykernel的基本Linux内核分析 1.我们按照老师在github上给出的步骤在实验楼上启动最高小内核,可以看到如下现象 在窗口中我们可以看到一个内核以及运行起来了,比较简单的内核,只时不停的输出一些字符串,>>>>>>my_time_handler here<<<<<<<和my_start_kernel here和一些计数.这时因为我们并没有加入其他的代码,再次基础上我们可以加入我们主机要实现的功能. 在myin

基于mykernel的一个简单的时间片轮转多道程序内核代码分析

学号023作品 本实验资源来源: https://github.com/mengning/linuxkernel/ 一.观察简易操作系统 此处使用实验楼的虚拟机打开终端 cd LinuxKernel/linux-3.9.4 rm -rf mykernel patch -p1 < ../mykernel_for_linux3.9.4sc.patch make allnoconfig make #编译内核请耐心等待 qemu -kernel arch/x86/boot/bzImage 在QEMU窗口

字符设备驱动(1)代码分析---之gpio_get_value()

在中断处理函数中,调用gpio_get_value/gpio_set_value()函数来获取/设置gpio端口的值,在这里简单分析一下内核的实现流程. tmp = gpio_get_value(S5PV210_GPH2(0)); #define gpio_get_value __gpio_get_value int __gpio_get_value(unsigned gpio) { struct gpio_chip *chip; int value; chip = gpio_to_chip(g

Device Tree(三):代码分析【转】

转自:http://www.wowotech.net/linux_kenrel/dt-code-analysis.html Device Tree(三):代码分析 作者:linuxer 发布于:2014-6-6 16:03 分类:统一设备模型 一.前言 Device Tree总共有三篇,分别是: 1.为何要引入Device Tree,这个机制是用来解决什么问题的?(请参考引入Device Tree的原因) 2.Device Tree的基础概念(请参考DT基础概念) 3.ARM linux中和De

【转】Device Tree(三):代码分析

原文网址:http://www.wowotech.net/linux_kenrel/dt-code-analysis.html 一.前言 Device Tree总共有三篇,分别是: 1.为何要引入Device Tree,这个机制是用来解决什么问题的?(请参考引入Device Tree的原因) 2.Device Tree的基础概念(请参考DT基础概念) 3.ARM linux中和Device Tree相关的代码分析(这是本文的主题) 本文主要内容是:以Device Tree相关的数据流分析为索引,

(转):从内核代码聊聊pipe的实现

来源: http://luodw.cc/2016/07/09/pipeof/ 用linux也有两年多了,从命令,系统调用,到内核原理一路学过来,我发现我是深深喜欢上这个系统:使用起来就是一个字"爽":当初在看 linux内核原理时,对linux内核源码有种敬畏的心理,不敢涉入,主要是看不懂,直到最近实习的时候,在某次分享会上,某位老师分享了OOM机制, 我很感兴趣,就去看内核代码,发现,原来我能看懂了:所以想写篇博客,分享下从内核代码分析pipe的实现: 这部分内容说简单也很简单,说难

简单的时间片轮转多道程序内核的分析

学号后三位:256 原创作品,转载请注明出处.参考资料: https://github.com/mengning/linuxkernel/issues/2 1.mykernel部署 使用实验楼的虚拟机打开shell 依次输入如下指令: cd LinuxKernel/linux-3.9.4 rm -rf mykernel patch -p1 < ../mykernel_for_linux3.9.4sc.patch make allnoconfig make qemu -kernel arch/x8

platform总线驱动代码分析

/************************************************************************/ Linux内核版本:2.6.35.7 运行平台:三星s5pv210 /************************************************************************/ 1.本例中通过使用Linux驱动模型中的platform总线和led驱动框架编写出来的led驱动代码来分析platform总线的工作

Linux内核中的GPIO系统之(3):pin controller driver代码分析--devm_kzalloc使用【转】

转自:http://www.wowotech.net/linux_kenrel/pin-controller-driver.html 一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datasheet会把pin controller的内容放入GPIO controller的章节中),主要功能包括: (1)pin multiplexing.基于ARM core