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;
}
上面的程序勉强能够运行出结果,但是总觉得有很多漏洞,希望朋友们能够多多提意见,以使我可以更加完善以上的代码……