天天看點

基于字元裝置的IIC驅動源代碼

 http://blog.csdn.net/vincent_zou/article/details/4141875

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");