TQ2440IIC驱动移植(非内核自带代码)
先前已经做了一个TQ2440IIC驱动的实验(是内核自带的代码),具体看我的上一篇文章,那么今天做这个实验是自己写的代码的移植(应该说是网上的模板,自己稍加修改)。说真的,自己做这个移植的时候还是比较困难的(相对于自己是一个初学者来说,虽然说它的原理来说,不是很难,但是实际的移植就会遇到比较多的细节问题),为了给自己的学习做一下总结,故把自己的移植过程简单地说一下(后面给出驱动代码和测试代码),同时希望高手们能够给我们这些初学者多多提些建议……
一、 include的路径问题:
头文件的路径不单单是顶层include下的文件,还有一些子目录下的include,当在顶层include下找不到一些文件时,我们就要在子目录下查找了。
二、 获取总线时钟问题:
我先前没有移植成功(我先前做过一次移植,是内核自带的iic移植,具体看我的上一篇博客文章),很大程度上是这个问题,我先前是直接写CLKCON这个寄存器,在调试的时候,好像是东西是写不到这个CLKCON里面去的,后来就改成用下面的方法来获取IIC总线时钟:
iic_clock
= clk_get(NULL, "i2c");
if
(!iic_clock)
{
printk(KERN_ERR
"failed to get adc clock source\n");
return
-ENOENT;
}
clk_enable(iic_clock);
改了之后就真的有进展了,呵呵……
三、 等待队列问题:
如果没有等待队列,那么在运行时CPU的使用率就会是100%,所以一般有中断的地方都会用到等待队列:
wait_event_interruptible(iic_waitq,
ack);
wake_up_interruptible(&iic_waitq);
这两个都是配套使用的。
四、 barrier函数的问题:
barrier()
barrier函数貌似是强制顺序写入,我在调试过程中先前是没有用到barrier函数的,在运行的时候有时能够正常,有时不行,用了barrier之后呢程序就运行得稳定点,但有时还是运行不下去(进入不了中断),总而言之,这个barrier函数还是有点用吧,呵呵……
五、 关于信号量的问题:
信号量是我后面才加上去的,就当是对阻塞型IO的驱动程序的完善吧……
下面贴出代码,程序比较乱,主要是调试时弄的,凑合着看吧……
实验环境:
硬件:TQ2440
软件:linux2.6.25.8
驱动代码:
///////////////////////////////////////////////////////////////////////////
#include 3c/iic.h>
#include
#include
#include
#include
//////////////////////////////////////////////////////////////////////////
#include
//#include
#include
#include
#include
//#include
#include
#include
#include
#include 3c2410/regs-gpio.h>
#include
#include 3c2410/irqs.h>
#include
#include
#include
/////////////////////////////////////////////////////////////////
#define S3C2410_IICCON_ACKEN
(1<<7)
#define S3C2410_IICCON_TXDIV_16
(0<<6)
#define S3C2410_IICCON_IRQEN
(1<<5)
#define S3C2410_IICLC_FILTER_ON
(1<<2)
#define S3C2410_IICLC_SDA_DELAY5 (0x01)
/////////////////////////////////////////////////////////////////
#define DEV_ID 110
#define DEV_NAME "2402_RW"
#define IICCON 0x54000000
#define IICSTAT
0x54000004
#define IICDS 0x5400000c
#define IICADD 0x54000008
#define IICLC 0x54000010
//#define CLKCON 0x4c00000c
//////////////////////////////////////////////////////////
static
DECLARE_WAIT_QUEUE_HEAD(iic_waitq);
static
struct
clk *iic_clock;
static
void *S3C2402_IICSTAT;
static
void *S3C2402_IICCON;
static
void *S3C2402_IICDS;
static
void *S3C2402_IICADD;
static
void *S3C2402_IICLC;
//static void *S3C2440_CLKCON;
static
int wr_data[5];
static
int ra_data[5];
static
int ack=0;
struct
semaphore sem;
//wait_queue_head_t iic_waitq;
void
delay(int t)
{
int
i;
for(;t>0;t--)
{
for(i=0;i<20000;i++);
}
}
static
void ioremap_2402( void )
{
S3C2402_IICCON=ioremap(IICCON,0x00000004); S3C2402_IICSTAT=ioremap(IICSTAT,0x00000004);
S3C2402_IICDS=ioremap(IICDS,0x00000004);
S3C2402_IICADD=ioremap(IICADD,0x00000004);
S3C2402_IICLC=ioremap(IICLC,0X00000004);
}
static
void iounremap_2402( void )
{
iounmap(
S3C2402_IICCON );
iounmap(
S3C2402_IICSTAT );
iounmap(
S3C2402_IICDS );
iounmap(
S3C2402_IICADD );
iounmap(
S3C2402_IICLC );
}
static
int open_2402(struct inode * inode, struct file * file)
{
int
cmmd;
if(
down_interruptible(&sem) )
return
-ERESTARTSYS;
writel(0xc0000,S3C2410_GPEUP);
// barrier();
s3c2410_gpio_cfgpin(S3C2410_GPE14,S3C2410_GPE14_IICSCL);
s3c2410_gpio_cfgpin(S3C2410_GPE15,S3C2410_GPE15_IICSDA); iic_clock
= clk_get(NULL, "i2c");
if
(!iic_clock)
{
printk(KERN_ERR
"failed to get adc clock source\n");
return
-ENOENT;
}
clk_enable(iic_clock);
cmmd=((S3C2410_IICCON_ACKEN)
| (S3C2410_IICCON_TXDIV_16) | (S3C2410_IICCON_IRQEN)
| (0x0f));
writel(cmmd,S3C2402_IICCON);
writel(0x10,S3C2402_IICADD);
writel(0x10,S3C2402_IICSTAT);
writel((S3C2410_IICLC_FILTER_ON
| S3C2410_IICLC_SDA_DELAY5) , S3C2402_IICLC);
barrier();
printk("open
successed!!!!!!!!!!!!\n");
return
0;
}
static
int write_2402(struct
file * file, const char __user * userbuf,size_t count, loff_t *
off)
{
int
i;
ack=0;
if(copy_from_user(wr_data,userbuf,count)
!= 0
)
{
printk("write
is error!!\n");
return EFAULT;
}
printk("write
is go on!!\n");
writel((
3<<6 ) | (1
<<4 ),S3C2402_IICSTAT);
writel(0xaf,S3C2402_IICCON);
writel(0xa0,S3C2402_IICDS);
writel(0xf0,S3C2402_IICSTAT);
barrier();
if
(!ack)
{
printk("ready
wait!!\n");
if
(file->f_flags &
O_NONBLOCK)
return
-EAGAIN;
else
wait_event_interruptible(iic_waitq,
ack);
}
ack
= 0;
writel(0x00,S3C2402_IICDS);
writel(0xaf,S3C2402_IICCON);
//
barrier();
udelay(100);
//while(ack==0);
//ack=0 ;
for(i=0;i<5;i++)
{
writel(wr_data[i],S3C2402_IICDS);
printk("wr_data=%x\n",wr_data[i]);
writel((readl(S3C2410_GPFDAT)|wr_data[i]),S3C2410_GPFDAT);
writel(0xaf,S3C2402_IICCON);
barrier();
udelay(100);
// while(ack==0);
if
(!ack)
{
if
(file->f_flags &
O_NONBLOCK)
return
-EAGAIN;
else
wait_event_interruptible(iic_waitq,
ack);
}
ack
= 0;
printk("ack=%x\n",ack);
// ack=0;
}
writel(0xd0,S3C2402_IICSTAT);
writel(0xaf,S3C2402_IICCON);
barrier();
udelay(100);
return
count;
}
static
unsigned int poll_2402( struct file *file, struct poll_table_struct
*wait)
{
unsigned
int mask = 0;
poll_wait(file,
&iic_waitq, wait);
if
(ack)
mask
|= POLLIN | POLLRDNORM;
return
mask;
}
static
int read_2402
(struct file * file, char __user * userbuf, size_t count, loff_t *
off)
{
int
i,a;
ack=0;
writel(0xa0,S3C2402_IICDS);
writel(0xf0,S3C2402_IICSTAT);
barrier();
udelay(100);
////
while(ack==0);
//
ack=0;
if
(!ack)
{
if
(file->f_flags &
O_NONBLOCK)
return
-EAGAIN;
else
wait_event_interruptible(iic_waitq,
ack);
}
ack
= 0;
writel(0x00,S3C2402_IICDS);
writel(0xaf,S3C2402_IICCON);
barrier();
udelay(100);
//
while(ack==0);
//
ack=0;
if
(!ack)
{
if
(file->f_flags &
O_NONBLOCK)
return
-EAGAIN;
else
wait_event_interruptible(iic_waitq,
ack);
}
ack
= 0;
writel(0xa1,S3C2402_IICDS);
writel(0xb0,S3C2402_IICSTAT);
writel(0xaf,S3C2402_IICCON);
barrier();
udelay(100);
a=readl(S3C2402_IICDS);
printk("a=%x\n",a);
for
(i=0;i<5;i++)
{
//while(ack==0);
//
printk("ack=%x\n",ack);
// ack=0;
if
(!ack)
{
if
(file->f_flags &
O_NONBLOCK)
return
-EAGAIN;
else
wait_event_interruptible(iic_waitq,
ack);
}
ack
= 0;
writel(0xaf,S3C2402_IICCON);
barrier();
udelay(100);
ra_data[i]=readl(S3C2402_IICDS);
writel((readl(S3C2410_GPFDAT)|ra_data[i]),S3C2410_GPFDAT);
barrier();
printk("ra_data=%x\n",ra_data[i]);
}
writel(0x90,S3C2402_IICSTAT);
writel(0xaf,S3C2402_IICCON);
barrier();
udelay(100);
if(copy_to_user(userbuf,ra_data,count)!=0)
{
printk("read
is error!!\n");
return
EFAULT;
}
return
count;
}
static
int release_2402(struct inode *inode, struct file *file)
{
up(&sem);
return
0;
}
static
irqreturn_t iic_interrupt(int irq,void *dev_id,struct pt_regs
*regs)
{
ack=1;
wake_up_interruptible(&iic_waitq);
return
IRQ_HANDLED;
}
static
struct file_operations test_2402 =
{
.owner = THIS_MODULE,
.open = open_2402,
.write = write_2402,
.read = read_2402,
.poll = poll_2402,
.release = release_2402,
};
static
struct class *i2c_class;
static
int __init init_2402(void)
{
int
result,ret;
init_MUTEX(&sem);
ioremap_2402();
result=register_chrdev(DEV_ID,DEV_NAME,&test_2402);
if
(result<0)
{
printk
("the derive register is fail!!!\n");
return
ENODEV;
}
else
{
printk
("the derive register is succss!!!\n");
ret=request_irq(IRQ_IIC,&iic_interrupt,SA_INTERRUPT,DEV_NAME,&iic_interrupt);
if(ret<0)
{
printk("the
iic_interrupt is requestted fail\n");
return
ENODEV;
}
else
{
printk("the
iic_interrupt is requestted sccuss!!\n");
i2c_class
= class_create(THIS_MODULE, DEV_NAME);
if(IS_ERR(i2c_class))
{
printk("Err:
failed in EmbedSky-leds class. \n");
return
-1;
}
class_device_create(i2c_class,
NULL, MKDEV(DEV_ID, 0), NULL, DEV_NAME);
printk(DEV_NAME
" initialized\n");
return
0;
return
ret;
}
return
result;
}
}
static
void __exit exit_2402(void)
{
unregister_chrdev(DEV_ID,DEV_NAME);
disable_irq(IRQ_IIC);
free_irq(IRQ_IIC,&iic_interrupt);
// devfs_remove(DEV_NAME);
class_device_destroy(i2c_class,
MKDEV(DEV_ID,
0));
class_destroy(i2c_class);
if
(iic_clock)
{
clk_disable(iic_clock);
clk_put(iic_clock);
iic_clock
= NULL;
}
iounremap_2402();
printk
("the derive is unregister\n");
}
module_init(init_2402);
module_exit(exit_2402);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("WENJAK");
MODULE_DESCRIPTION("2402_READ_WRITE");
测试代码:
#include
#include
#include
#include
#include
#include
#include
#define
PATH "/dev/adc/0" //device
file
static
struct ADC_DEV
{
int
channel;
int
prescale;
}adc_infor;
int
main(void)
{
int
fd;
int
result, data;
unsigned
char val;
int
times = 0;
fd
= open(PATH, O_RDWR);
if
(fd < 0) {
printf("Failed
to open ad_driver\n");
exit(1);
}
printf("Please
select which chanel to read....\n");
printf("0
: chanel--0\n");
printf("1
: chanel--1\n");
scanf("%d",
&val);
if((val
!=0) && (val !=1))
val
=
0; //by
default the chanel is 0
adc_infor.channel
=
val; //chanel
0 or 1
adc_infor.prescale
=
40;
do
{
result
= write(fd, (void *)&adc_infor, sizeof(adc_infor))
== sizeof(struct
ADC_DEV);
if
(!result) {
printf("wrong
when writing!\n");
goto
failed;
}
result
= read(fd, &data, sizeof(data)) ==
sizeof(data);
if
(!result) {
printf("Wrong
size!\n");
goto
failed;
}
printf("chanel
%d --ad result=%d\n", val, data);
sleep(1);
}while((times++)
< 10);
failed:
close(fd);
return
0;
}
上面的程序勉强能够运行出结果,但是总觉得有很多漏洞,希望朋友们能够多多提意见,以使我可以更加完善以上的代码……