#includ
#includ
#includ
#includ
#includ
s3c2410_gpio_cfgpin等 #includ
#includ
#includ
#definDEVICE_NA ME"leds"
#definLED_MA JOR231
#definIOCTL_LED_ON 1#definIOCTL_LED_OFF 0
staticunsignlongled_tabl[]={S3C2410_GPB5. S3C2410_GPB7, S3C2410_GPB6. S3C2410_GPB8,}
staticunsignintled_cfg_tabl[]={S3C2410_GPB5_OUTP,led_table数组相当于对应了GPB四个IO口的索引。 上面代码中。S3C2410_GPB6_OUTP,S3C2410_GPB7_OUTP,
S3C2410_GPB8_OUTP,};
structleds_type
{
structcdevcdev;
};
structleds_typ*my_leds_dev;
staticintEmbedSky_leds_openstructinod*inode,structfile*file
{
inti;
fori=0;i<4;i++
{ //设置GPIO引脚的功能:本驱动中LED所涉及的GPIO引脚设为输出功能 s3c2410_gpio_cfgpinled_table[i],led_cfg_table[i];
}
return0;}
staticintEmbedSky_leds_ioctlstructinod*inode,structfile*file,unsignintcmd,unsignlongarg
{
ifarg>4
{
return-EINVA L;
}
switchcmd
{
caseIOCTL_LED_ON://设置指定引脚的输出电平为0 s3c2410_gpio_setpinled_table[arg],0;
return0;
caseIOCTL_LED_OFF://设置指定引脚的输出电平为1 s3c2410_gpio_setpinled_table[arg],1; return0;
default:return-EINVA L;
}
}
staticstructfile_operEmbedSky_leds_fops={
.owner= THIS_MODULE,
.open = EmbedSky_leds_open,
.ioctl = EmbedSky_leds_ioctl,};
staticchar__initdatabanner[]="TQ2440/SKY2440LEDS,c2008,2009www.embedsky.net\n";statstructclass*led_class;
staticint__initEmbedSky_leds_initvoid{
intret;dev_tdevno=MKDELED_MA JOR,0;
printk"initled\n";
printkbanner;
if!my_leds_dev
{
ret=-ENOMEM;
gotofail_malloc;
}
memsetmy_leds_dev,0,sizeofstructleds_typ;
cdev_init&my_leds_dev->cdev,&EmbedSky_leds_fop;
ret=cdev_add&my_leds_dev->cdev,devno,1;
ifretprintkKERN_NOTICE"ERROR%d",ret;
//注册一个类,使mdev可以在"/dev/"目录下面建立设备节点
led_class=class_creatTHIS_MODULE,DEVICE_NA ME;
ifIS_ERRled_class
{
printk"Err:failinEmbedSky-lclass.\n"; return-1;
}//创建一个设备节点,节点名为DEVICE_NA ME
device_crled_class,NULL,MKDEVLED_MA JOR,0,NULL,DEVICE_NA ME;
printkDEVICE_NA ME"initialized\n";
return0;
fail_malloc:unregister_chrdev_regiondevno,1;
returnret;}
staticvoid__exitEmbedSky_leds_exitvoid{ unregister_chrdevLED_MA JOR,DEVICE_NA ME;
device_destroiled_class,MKDEVLED_MA JOR,0;//删掉设备节点 class_destroiled_class; //注销类}
module_initEmbedSky_leds_init;module_exitEmbedSky_leds_exit;
MODULE_A UTHOR"http://www.embedsky.net"; //驱动顺序的作者
MODULE_DESCRIPTION"TQ2440/SKY2440LEDDriver"; //一些描述信息
MODULE_LICENSE"GPL"; //遵循的协议
通过这四个值,对这四个IO口进行相关操作。例如:
S3C2410_GPB5= S3C2410_GPIONOS3C2410_GPIO_BA NKB,5
=S3C2410_GPIO_BA NKB+5
=32*1+5
s3c2410_gpio_setpinS3C2410_GPB50中,该函数首先通过S3C2410_GPB5获得GPB虚拟地址和偏移地址再对GPB5GPBDA T寄存器进行操作,具体
voids3c2410_gpio_setpinunsignintpin,unsignintto{
void__iomem*base=S3C2410_GPIO_BA SEpin;
unsignlongoff=S3C2410_GPIO_OFFSETpin;
unsignlongflags;unsignlongdat;local_irq_savflag;
dat=__raw_readlbase+0x04;//读取GPIODA T数据到dat
dat&=~1<<off;//先将要设置的IO口拉低
dat|=to<<offs; //再将形参的to值赋给dat
__raw_writeldat,base+0x04;//最后将DA T值写进GPIODA T
local_irq_restorflag;}
上面的函数调用了两个子函数,具体定义如下
#defineS3C2410_GPIO_BA SEpin pin&~31>>1+S3C24XX_VA _GPIO
#defineS3C2410_GPIO_OFFSETpinpin&31
其中S3C24XX_VA _GPIO定义如下:
#defineS3C24XX_VA _GPIO S3C2410_A DDR0x00E00000
#defineS3C2410_A DDRx 0xF0000000+x
这里S3C2410_A DDR基地址为0xF0000000??也即2440所有寄存器的虚拟地址的基地址。0x00E00000表示2440GPIO偏移地址,也就是说其GPIO虚拟地址首地址为0xF0E00000
再看看S3C2410_GPIO_BA SEpin定义。可以得到S3C2410_GPB5&~31=32其目的就是去掉GPB偏移值,不仿把S3C2410_GPB5值放进去计算。然后再右移一位,和GPIO虚拟地址首地址相加。因此,S3C2410_GPIO_BA SEpin只代表了对应GPIO组的虚拟地址,如GPB虚拟地址为10000B+0xF0E00000=0xF0E00010依此类推,可以得到所有GPIO偏移地址,具体如下表:
S3C2410_GPIO_OFFSET用于获得具体GPIO偏移地址。如GPB5则S3C2410_GPIO_OFFSETpin=pin&31=32*1+5&31=5有了*base和off就可以操作具体的寄存器了
函数s3c2410_gpio_cfgpin用于配置GPCON寄存器。具体代码
unsignintfunction{ voids3c2410_gpio_cfgpinunsignintpin.>
void__iomem*base=S3C2410_GPIO_BA SEpin;
unsignlongmask;
unsignlongcon;
unsignlongflags;
ifpin<S3C2410_GPIO_BA NKB
{
mask=1<<S3C2410_GPIO_OFFSETpin;//GPA 寄存器只占一位 }
else
{ mask=3<<S3C2410_GPIO_OFFSETpin*2;//非GPA 寄存器占两位 } local_irq_savflag;
con =__raw_readlbase+0x00;//先保留GPCON值
con&=~mask; //再将要设置的管脚的CON值清零
con|=function; //然后将形参传进来的配置赋给CON
base+0x00;//最后将CON值写进GPCON寄存器 __raw_writelcon.
local_irq_restorflag;}
上面的LED驱动顺序中。调用上面的函数后即设置为Output led_cfg_tabl数组给出了GPB相应管脚的属性设置。
此为止。应该就一目了然了整个S3C2440IO口操作。
#defin__raw_writelv.*volatilunsignint__forc*a=v a__chk_io_ptra.
#defin__chk_io_ptrxvoid0
__chk_io_ptr编译器为了更细致地检查参数的属性。正常编译时没有作用。用于调试。
volatil为了防止Compil优化。
如果要对0x56000010物理地址进行访问(一般是外设寄存器),不能直接访问0x56000010物理地址。内核中,对所有的地址都是通过虚拟地址进行访问的.因此。那么需要把0x56000010物理地址映射为虚拟地址,然后对该虚拟地址进行访问就是对实际的物理地址进行访问了
需要注意的:进行映射时.否则就是加了volatil也是没有用的该虚拟地址需要disablcach和WriteBuffer.
这个IOREMA P实现过程中
offset=phys_addr&~PA GE_MA SK;
phys_addr&=PA GE_MA SK;
size=PA GE_A LIGNlast_addr-phys_addrs;
下面是通过vmlist中查找以size大小的空闲块.已经做过了页的对齐,所以从这里可以看出.只以映射的大小
杂项设备(miscdevic
杂项设备也是嵌入式系统中用得比较多的一种设备驱动。Linux内核的include\linux目录下有Miscdevice.h文件。所有这些设备采用主编号10一起归于miscdevic其实misc_regist就是用主标号10调用register_chrdev 要把自己定义的miscdevic从设备定义在这里。其实是因为这些字符设备不符合预先确定的字符设备范畴。
misc设备其实也就是特殊的字符设备。也就是说。
字符设备(chardevic
LED_MA JOR不能相同,否则几个设备都无法注册(已验证)如果模块使用该方式注册并且 LED_MA JOR为0自动分配主设备号 使用insmod命令加载模块时会在终端显示分配的主设备号和次设备号,/dev目录下建立该节点,比如设备led如果加载该模块时分配的主设备号和次设备号为253和0则建立节点:mknodledc2530使用register_chrdevLED_MA JOR,如果有多个设备使用该函数注册驱动顺序。使用register_chrdevLED_MA JOR,DEVICE_NA ME,&dev_fop注册字符设备驱动顺序时。DEVICE_NA ME,&dev_fop注册字符设备驱动顺序时都要手动建立节点,否则在应用顺序无法打开该设备。
__raw_readl和__raw_writel
Linux对I/O操作都定义在asm/io.h中。就在asm-arm/io.h中。相应的arm平台下。
#defin__raw_writebv.*volatilunsignchar__force *a=v a __chk_io_ptra.
#defin__raw_writewv.*volatilunsignshort__forc*a=v a __chk_io_ptra.
#defin__raw_writelv.*volatilunsignint__force *a=vinclude\linux\compiler.h中: a __chk_io_ptra.
#ifdef__CHECKER__
externvoid__chk_io_ptrvoid__iomem*;
#els
#defin__chk_io_ptrxvoid0
#endif
*volatilunsignint_forc*a就是返回地址为a处的值。voidxx做法有时候是有用的例如编译器打开了检查未使用的参数的时候需要将没有用到参数这么弄一下才干编译通过。 __raw_readla展开是void0,否则__chk_io_ptr什么也不做。*volatilunsignint_forc*a定义了__CHECKER__时候先调用__chk_io_ptr检查该地址。
CPU对I/O物理地址的编程方式有两种:一种是I/O映射。由此派生进去的操作方法有:inboutb_memcpy_fromioreadbwritebioread8iowrite8等。一种是内存映射。__raw_readl和__raw_writel等是原始的操作I/O方法。
发表新评论
您还未登录!登录后可以发表回复
文章评论 0人参与