Chinaunix首页 | 论坛 | 博客
  • 博客访问: 63555
  • 博文数量: 17
  • 博客积分: 1430
  • 博客等级: 上尉
  • 技术积分: 140
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-13 12:56
文章分类
文章存档

2011年(1)

2008年(16)

我的朋友

分类: LINUX

2008-03-13 14:02:59

/*
*
*        2.6内核IIC驱动程序
*
*
*
*        1.四种模式的IIC驱动编写介绍
*        
*        2.一个完整的IIC驱动(从器件接收模式,并且是裸写驱动)
*        
*        
*        1.1 开启从器件接收模式的示例
*        
*        R_IICCON = 0xE2; // 使能ACK,使能中断
*        
*        R_IICADD = 0xAA; // 从器件地址
*        
*        R_IICSTAT = 0x10; // 设置从器件接收模式
*        
*        进入中断处理,读收数据:
*        
*        unit8_t ch = R_IICDS & 0xff; // 读取数据寄存器
*        
*        R_IICCON &= 0xEF; // 清除IICCON[4]恢复中断响应
*        
*        RET;
*        
*        
*        1.2 开启从器件发送模式的示例
*        
*        R_IICCON = 0xE2; // 使能ACK,使能中断
*        
*        R_IICADD = 0xAA; // 从器件地址
*        
*        R_IICSTAT = 0x50; // 设置从器件接收模式
*        
*        进入中断处理,发送数据:
*        
*        unit8_t ch = extern_buffer[i]; // 取得待发送到从器件的数据
*        
*        R_IICDS = ch; // 写入数据到IICDS
*        
*        R_IICCON &= 0xEF; // 清除IICCON[4]恢复中断响应
*        
*        RET;
*        
*        以切换模式的方式结束发送。
*        
*        
*        1.3 开启主器件发送模式的示例
*        
*        R_IICCON = 0xE2; // 使能ACK,使能中断
*        
*        R_IICSTAT = 0xD0; // 设置主器件发送模式
*        
*        R_IICDS = 0xD0; // 写入从器件地址到IICDS寄存器
*        
*        R_IICSTAT = 0xF0; // 送出IICDS中的数据
*        
*        ACK并进入中断处理:
*        
*        uint8_t ch = extern_buffer[i]; // 取得待发送到从器件的数据
*        
*        R_IICDS = ch; // 再次写入数据到IICDS
*        
*        R_IICCON &= 0xEF; // 清除IICCON[4]恢复中断响应
*        
*        结束发送:
*        
*        R_IICSTAT = 0xD0; // 置IICSTAT[5]为0,产生停止条件
*        
*        
*        1.4 开启主器件接收模式的示例
*        
*        R_IICCON = 0xE2; // 使能ACK,使能中断
*        
*        R_IICSTAT = 0x90; // 设置主器件接收模式
*        
*        R_IICDS = 0xD0; // 写入从器件地址到 IICDS
*        
*        R_IICSTAT = 0xB0; // 送出IICDS中的数据
*        
*        ACK并进入中断处理:
*        
*        uint8_t ch = R_IICDS & 0xFF; // 读取一个八位序列
*        
*        R_IICCON &= 0xEF; // 清除IICCON[4]恢复中断响应
*        
*        结束接收:
*        
*        R_IICSTAT = 0x90; // 置IICSTAT[5]为0,产生停止条件
*        
*        
*        2.1 驱动源代码
**/

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <asm/arch/irqs.h>

#define DEVICE "TEST_IIC"
#define DATA_LEN 6

int major = 233;
int minor = 0;
void *R_GPECON,*R_GPEUP,*R_IICCON,*R_IICSTAT,*R_IICADD,*R_IICDS;

int IIC_open(struct inode *, struct file *);
int IIC_release(struct inode *, struct file *);
ssize_t IIC_read(struct file *file, char* buf, size_t count, loff_t *f_pos);
unsigned int IIC_poll(struct file* file, poll_table* wait);
irqreturn_t interrupt_handle( int irq, void* dev_id, struct pt_regs* regs );

static void address_map(void)
{
#define IIC_BASE (0x54000000)
#define IIC_GPECON ( IIC_BASE + 0x40 )
#define IIC_GPEUP ( IIC_BASE + 0x48 )
#define IIC_CON ( IIC_BASE + 0x0 )
#define IIC_STAT ( IIC_BASE + 0x4 )
#define IIC_ADDR ( IIC_BASE + 0x8 )
#define IIC_DS ( IIC_BASE + 0xC )
        R_GPECON = ioremap(IIC_GPECON,4);
        R_GPEUP = ioremap(IIC_GPEUP ,4);
        R_IICCON = ioremap(IIC_CON ,4);
        R_IICSTAT = ioremap(IIC_STAT ,4);
        R_IICADD = ioremap(IIC_ADDR ,4);
        R_IICDS = ioremap(IIC_DS ,4);
}

static void address_unmap(void)
{
        iounmap( R_GPECON );
        iounmap( R_GPEUP );
        iounmap( R_IICCON );
        iounmap( R_IICSTAT );
        iounmap( R_IICADD );
        iounmap( R_IICDS );
}

static struct file_operations fops = {
        owner : THIS_MODULE,
        read: IIC_read,
        open: IIC_open,
        release: IIC_release,
        poll: IIC_poll,
};

struct IIC_dev{
        wait_queue_head_t rq; // 读取等待队列

        uint8_t *buffer;
        uint32_t size;
        uint32_t index;
        struct semaphore sem;
        struct cdev cdev;
};
struct IIC_dev *my_dev;

static int __init IIC_init(void)
{
        // 1. 分配主设备号

        dev_t devno = MKDEV( major, minor );
        int ret = register_chrdev_region( devno, 1, DEVICE );//(设备号,注册号,名称)

        if( ret < 0 )
        {
          printk(KERN_DEBUG "register major number failed with %d\n", ret);
          return ret;
        }
        printk(KERN_DEBUG "%s:register major number OK\n",DEVICE);

        // 2. 注册设备

        my_dev = kmalloc(sizeof(struct IIC_dev), GFP_KERNEL);
        memset( my_dev, 0, sizeof(struct IIC_dev) );
        cdev_init( &my_dev->cdev, &fops );
        my_dev->cdev.ops = &fops;
        my_dev->cdev.owner = THIS_MODULE;
        ret = cdev_add( &my_dev->cdev, devno, 1 );
        if( ret < 0 )
        {
          printk(KERN_DEBUG "register device failed with %d\n", ret);
          return ret;
        }
        printk(KERN_DEBUG "%s:register device OK\n",DEVICE);

        // 3. 分配本驱动要使用的内存

        my_dev->index = 0;
        my_dev->size = 128;
        my_dev->buffer = kmalloc( my_dev->size, GFP_KERNEL );
        if( NULL == my_dev->buffer )
        {
          printk(KERN_DEBUG "kmalloc failed\n");
          return -ENOMEM;
        }
        printk(KERN_DEBUG "%s:kmalloc buffer OK\n",DEVICE);

        // 4. 初始化信号量

        init_MUTEX( &(my_dev->sem) );
        printk(KERN_DEBUG "%s:init semaphore OK\n",DEVICE);

        // 5. 初始化等待队列头

        init_waitqueue_head(&my_dev->rq);

        // 6. Remap IIC 寄存器

        address_map();

        // 7. 设置 s3c2410 IIC

        unsigned int tmp = ioread32( R_GPEUP );
        tmp |= 0xc000; //Pull-up disable

        iowrite32( tmp, R_GPEUP );

        tmp = ioread32( R_GPECON );
        tmp |= 0xa0000000; //GPE15:IICSDA , GPE14:IICSCL

        iowrite32( tmp, R_GPECON );

        return 0;
}


static void __exit IIC_exit(void)
{
        dev_t devno = MKDEV( major, minor );
        // 以相反的顺序清除

        address_unmap();
        kfree( my_dev->buffer );
        cdev_del( &my_dev->cdev );
        kfree( my_dev );
        printk(KERN_DEBUG "%s:kfree OK\n",DEVICE);
        unregister_chrdev_region( devno, 1 );
        printk(KERN_DEBUG "%s:unregister device OK\n",DEVICE);
}

static void set_slave_recv_mode(void)
{
        iowrite8( 0xE2, R_IICCON ); // 使能ACK,使能中断

        iowrite8( 0xAA, R_IICADD ); // 从器件地址

        iowrite8( 0x10, R_IICSTAT); // 设置从器件接收模式

        barrier(); // 强制写入寄存器

}

int IIC_open(struct inode *inode, struct file *file)
{
        struct IIC_dev *dev = container_of(inode->i_cdev, struct IIC_dev, cdev);
        file->private_data = dev;

        if( down_interruptible(&dev->sem) )
                return -ERESTARTSYS;

        set_slave_recv_mode();

        int ret = request_irq( IRQ_IIC, interrupt_handle,
                                    SA_INTERRUPT, DEVICE, (void*)dev );
        if( ret )
        {
                printk( KERN_INFO "I2C: can't get assigned irq %d\n", IRQ_IIC );
        }
        return 0;
}

int IIC_read(struct file *file, char* buf, size_t count, loff_t *f_pos)
{
        struct IIC_dev *dev = file->private_data;
        size_t val = DATA_LEN;

        while( dev->index < val )
        {
                if( file->f_flags & O_NONBLOCK )
                        return -EAGAIN;
                // 在这里准备睡眠,等待条件为真

                if( wait_event_interruptible(dev->rq, (dev->index >= val)) )
                        return -ERESTARTSYS; // 返回非0表示被信号中断

        }
        if( copy_to_user(buf, dev->buffer, val) )
                return -EFAULT;
        memset( dev->buffer, 0, dev->size );
        dev->index = 0;

        set_slave_recv_mode();
        return val;
}

int IIC_release(struct inode *inode, struct file *file)
{
        struct IIC_dev *dev = file->private_data;

        iowrite8( 0x0, R_IICCON );
        iowrite8( 0x0, R_IICADD );
        iowrite8( 0x0, R_IICSTAT);
        barrier(); // 强制写入寄存器


        memset( dev->buffer, 0, dev->size );
        dev->index = 0;

        free_irq( IRQ_IIC, NULL );

        up(&dev->sem);
        return 0;
}

unsigned int IIC_poll(struct file* file, poll_table* wait)
{
        struct IIC_dev *dev = file->private_data;
        unsigned int mask = 0, val = DATA_LEN;

        poll_wait(file,&dev->rq,wait);
        if( dev->index >= val )
                mask |= POLLIN | POLLRDNORM;
        return mask;
}

irqreturn_t interrupt_handle( int irq, void* dev_id, struct pt_regs* regs )
{
        struct IIC_dev *dev = dev_id;
        int val = DATA_LEN;

        uint8_t ch = ioread8( R_IICDS );
        if( dev->index == 0 && ch == 0xAA )
                goto ret;
        dev->buffer[dev->index++] = ch;
        if( dev->index >= val )
        {
                wake_up_interruptible( &dev->rq );
                // 直接退出 Slave Receiver 模式

                return IRQ_HANDLED;
        }
ret:
        iowrite8( 0xEF, R_IICCON );
        return IRQ_HANDLED;
}

module_init(IIC_init);
module_exit(IIC_exit);

MODULE_AUTHOR("kf701.ye AT gmail.com");
MODULE_DESCRIPTION("Study");
MODULE_SUPPORTED_DEVICE(DEVICE);
MODULE_LICENSE("GPL");

阅读(1263) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~