打印

IIC驱动问题(裸机转Linux的疑问)

[复制链接]
1817|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
zcc8607|  楼主 | 2011-8-6 14:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
最近在学2440的裸奔程序,有一个疑问:可不可以把裸奔程序直接套进Linux驱动框架里面而转成Linux驱动呢?
于是尝试了一下,遇到了一些困难,望大家能指导一下。

原始的裸奔程序,是mini2440出厂自带的测试程序,IIC的驱动,在iic.c文件中(中断模式):
#include <string.h>
#include "2440addr.h"
#include "2440lib.h"
#include "def.h"
#include "IIC.h"

static U8 _iicData[IICBUFSIZE];
static volatile int _iicDataCount;
static volatile int _iicStatus;
static volatile int _iicMode;
static int _iicPt;

//===================================================================
//       SMDK2440 IIC configuration
//  GPE15=IICSDA, GPE14=IICSCL
//  "Interrupt mode" for IIC block
//===================================================================  

//******************[ Test_Iic ]**************************************
void Test_Iic(void)
{
    unsigned int i,j,save_E,save_PE;
    static U8 data[256];

    Uart_Printf("\nIIC Test(Interrupt) using AT24C02\n");

    save_E   = rGPECON;
    save_PE  = rGPEUP;
    rGPEUP  |= 0xc000;                  //Pull-up disable
    rGPECON |= 0xa00000;                //GPE15:IICSDA , GPE14:IICSCL  

    pISR_IIC = (unsigned)IicInt;
    rINTMSK &= ~(BIT_IIC);

      //Enable ACK, Prescaler IICCLK=PCLK/16, Enable interrupt, Transmit clock value Tx clock=IICCLK/16
      // If PCLK 50.7MHz, IICCLK = 3.17MHz, Tx Clock = 0.198MHz
    rIICCON = (1<<7) | (0<<6) | (1<<5) | (0xf);

    rIICADD  = 0x10;                    //2440 slave address = [7:1]
    rIICSTAT = 0x10;                    //IIC bus data output enable(Rx/Tx)
        rIICLC = (1<<2)|(1);                                  // Filter enable, 15 clocks SDA output delay       added by junon
     
    Uart_Printf("Write test data into AT24C02\n");

    for(i=0;i<256;i++)
        Wr24C080(0xa0,(U8)i,i);
            
    for(i=0;i<256;i++)
        data[i] = 0;

    Uart_Printf("Read test data from AT24C02\n");
     
    for(i=0;i<256;i++)
        Rd24C080(0xa0,(U8)i,&(data[i]));  

        //Line changed 0 ~ f
    for(i=0;i<16;i++)
    {
        for(j=0;j<16;j++)
            Uart_Printf("%2x ",data[i*16+j]);
        Uart_Printf("\n");
    }
    rINTMSK |= BIT_IIC;     
    rGPEUP  = save_PE;
    rGPECON = save_E;
}


//*************************[ Wr24C080 ]****************************
void Wr24C080(U32 slvAddr,U32 addr,U8 data)
{
    _iicMode      = WRDATA;
    _iicPt        = 0;
    _iicData[0]   = (U8)addr;
    _iicData[1]   = data;
    _iicDataCount = 2;
     
    rIICDS   = slvAddr;                 //0xa0
    rIICSTAT = 0xf0;                    //MasTx,Start
      //Clearing the pending bit isn't needed because the pending bit has been cleared.
     
    while(_iicDataCount!=-1);

    _iicMode = POLLACK;

    while(1)
    {
        rIICDS     = slvAddr;
        _iicStatus = 0x100;
        rIICSTAT   = 0xf0;              //MasTx,Start
        rIICCON    = 0xaf;              //Resumes IIC operation.  
            
        while(_iicStatus==0x100);
            
        if(!(_iicStatus&0x1))
            break;                      //When ACK is received
    }
    rIICSTAT = 0xd0;                    //Stop MasTx condition  
    rIICCON  = 0xaf;                    //Resumes IIC operation.  
    Delay(1);                           //Wait until stop condtion is in effect.
       //Write is completed.
}
         
//**********************[ Rd24C080 ] ***********************************
void Rd24C080(U32 slvAddr,U32 addr,U8 *data)
{
    _iicMode      = SETRDADDR;
    _iicPt        = 0;
    _iicData[0]   = (U8)addr;
    _iicDataCount = 1;

    rIICDS   = slvAddr;
    rIICSTAT = 0xf0;                    //MasTx,Start   
      //Clearing the pending bit isn't needed because the pending bit has been cleared.
    while(_iicDataCount!=-1);

    _iicMode      = RDDATA;
    _iicPt        = 0;
    _iicDataCount = 1;
     
    rIICDS        = slvAddr;
    rIICSTAT      = 0xb0;               //MasRx,Start
    rIICCON       = 0xaf;               //Resumes IIC operation.   
    while(_iicDataCount!=-1);

    *data = _iicData[1];
}


//-------------------------------------------------------------------------
void __irq IicInt(void)
{
    U32 iicSt,i;
     
    rSRCPND = BIT_IIC;          //Clear pending bit
    rINTPND = BIT_IIC;
    iicSt   = rIICSTAT;  
     
    if(iicSt & 0x8){}           //When bus arbitration is failed.
    if(iicSt & 0x4){}           //When a slave address is matched with IICADD
    if(iicSt & 0x2){}           //When a slave address is 0000000b
    if(iicSt & 0x1){}           //When ACK isn't received

    switch(_iicMode)
    {
       case POLLACK:
           _iicStatus = iicSt;
           break;

       case RDDATA:
           if((_iicDataCount--)==0)
           {
               _iicData[_iicPt++] = rIICDS;
            
               rIICSTAT = 0x90;                 //Stop MasRx condition  
               rIICCON  = 0xaf;                 //Resumes IIC operation.
               Delay(1);                        //Wait until stop condtion is in effect.
                                                //Too long time...  
                                                //The pending bit will not be set after issuing stop condition.
               break;     
           }      
           _iicData[_iicPt++] = rIICDS;         //The last data has to be read with no ack.

           if((_iicDataCount)==0)
               rIICCON = 0x2f;                  //Resumes IIC operation with NOACK.   
           else  
               rIICCON = 0xaf;                  //Resumes IIC operation with ACK
               break;

        case WRDATA:
            if((_iicDataCount--)==0)
            {
                rIICSTAT = 0xd0;                //Stop MasTx condition  
                rIICCON  = 0xaf;                //Resumes IIC operation.
                Delay(1);                       //Wait until stop condtion is in effect.
                       //The pending bit will not be set after issuing stop condition.
                break;     
            }
            rIICDS = _iicData[_iicPt++];        //_iicData[0] has dummy.
            for(i=0;i<10;i++);                  //for setup time until rising edge of IICSCL
               
            rIICCON = 0xaf;                     //resumes IIC operation.
            break;

        case SETRDADDR:
//          Uart_Printf("[ S%d ]",_iicDataCount);
            if((_iicDataCount--)==0)
                break;                          //IIC operation is stopped because of IICCON[4]     
            rIICDS = _iicData[_iicPt++];
            for(i=0;i<10;i++);                  //For setup time until rising edge of IICSCL
            rIICCON = 0xaf;                     //Resumes IIC operation.
            break;

        default:
            break;      
    }
}

相应的,我直接把上面的程序套到Linux字符驱动的框架里面,除了进行地址映射和寄存器使用函数读取和赋值以外,没有大的改动:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <mach/regs-gpio.h>
#include <asm/mach/time.h>
#include <plat/regs-timer.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <mach/hardware.h>
#include <linux/irq.h>
#include <linux/kfifo.h>

#define U32 unsigned long

static void __iomem *gpio_base_addr;
static void __iomem *iic_base_addr;
static void __iomem *timer_base_addr;

#define TIMER_PA        0x51000000
#define rTCFG0  (*(volatile unsigned *)(0x00+timer_base_addr))        //Timer 0 configuration
#define rTCFG1  (*(volatile unsigned *)(0x04+timer_base_addr))        //Timer 1 configuration
#define rTCON   (*(volatile unsigned *)(0x08+timer_base_addr))        //Timer control
#define rTCNTB3 (*(volatile unsigned *)(0x30+timer_base_addr))        //Timer count buffer 3
#define rTCMPB3 (*(volatile unsigned *)(0x34+timer_base_addr))        //Timer compare buffer 3
#define rTCNTO3 (*(volatile unsigned *)(0x38+timer_base_addr))        //Timer count observation 3

#define GPIO_PA        0x56000040
#define rGPECON        (0x00+gpio_base_addr)
#define rGPEUP                (0x08+gpio_base_addr)

#define IIC_PA                0x54000000
#define rIICCON        0x00+iic_base_addr         
#define rIICSTAT        0x04+iic_base_addr
#define rIICADD        0x08+iic_base_addr
#define rIICDS                0x0c+iic_base_addr
#define rIICLC                0x10+iic_base_addr

#define WRDATA      (1)
#define POLLACK     (2)
#define RDDATA      (3)
#define SETRDADDR   (4)

#define IICBUFSIZE 0x20

#define DEVICE_NAME "iic"
MODULE_LICENSE("Dual BSD/GPL");

static int iic_major=254;
static unsigned long gpecon_save, gpeup_save;
module_param(iic_major, int, S_IRUGO);
struct cdev cdev;
static struct semaphore lock;
static unsigned long slvAddr = 0xa0;

static unsigned char _iicData[IICBUFSIZE];
static volatile int _iicDataCount;
static volatile int _iicStatus;
static volatile int _iicMode;
static int _iicPt;
static unsigned long rd_addr = 0;
static unsigned long wr_addr = 0;

void Delay(int time)
{
        unsigned long PCLK = 50000000;
        U32 val = (PCLK>>3)/1000-1;
         
        rTCFG0 &= ~(0xff<<8);
        rTCFG0 |= 3<<8;                        //prescaler = 3+1
        rTCFG1 &= ~(0xf<<12);
        rTCFG1 |= 0<<12;                //mux = 1/2

        rTCNTB3 = val;
        rTCMPB3 = val>>1;                // 50%
        rTCON &= ~(0xf<<16);
        rTCON |= 0xb<<16;                //interval, inv-off, update TCNTB3&TCMPB3, start timer 3
        rTCON &= ~(2<<16);                //clear manual update bit
        while(time--) {
                while(rTCNTO3>=val>>1);
                while(rTCNTO3<val>>1);
        };
}

static irqreturn_t iic_interrupt(int irq, void * dev_id)
{
            U32 iicSt,i;
     
//    rSRCPND = BIT_IIC;          //Clear pending bit
//    rINTPND = BIT_IIC;
    iicSt   = ioread32(rIICSTAT);  
     
    if(iicSt & 0x8){}           //When bus arbitration is failed.
    if(iicSt & 0x4){}           //When a slave address is matched with IICADD
    if(iicSt & 0x2){}           //When a slave address is 0000000b
    if(iicSt & 0x1){}           //When ACK isn't received

    switch(_iicMode)
    {
       case POLLACK:
           _iicStatus = iicSt;
           break;

       case RDDATA:
           if((_iicDataCount--)==0)
           {
               _iicData[_iicPt++] = ioread32(rIICDS);
            
               iowrite32(0x90, rIICSTAT);                 //Stop MasRx condition  
               iowrite32(0xaf, rIICCON);                 //Resumes IIC operation.
               Delay(1);                        //Wait until stop condtion is in effect.
                                                //Too long time...  
                                                //The pending bit will not be set after issuing stop condition.
               break;     
           }      
           _iicData[_iicPt++] = ioread32(rIICDS);         //The last data has to be read with no ack.

           if((_iicDataCount)==0)
               iowrite32(0x2f,rIICCON);                  //Resumes IIC operation with NOACK.   
           else  
               iowrite32(0xaf,rIICCON);                  //Resumes IIC operation with ACK
               break;

        case WRDATA:
        printk("write interrupt\n");
            if((_iicDataCount--)==0)
            {
                iowrite32(0xd0,rIICSTAT);                //Stop MasTx condition  
                iowrite32(0xaf,rIICCON);                //Resumes IIC operation.
                Delay(1);                       //Wait until stop condtion is in effect.
                       //The pending bit will not be set after issuing stop condition.
                break;     
            }
            iowrite32(_iicData[_iicPt++], rIICDS);        //_iicData[0] has dummy.
            for(i=0;i<10;i++);                  //for setup time until rising edge of IICSCL
               
            iowrite32(0xaf,rIICCON);                     //resumes IIC operation.
            break;

        case SETRDADDR:
//          Uart_Printf("[ S%d ]",_iicDataCount);
            if((_iicDataCount--)==0)
                break;                          //IIC operation is stopped because of IICCON[4]     
            iowrite32(_iicData[_iicPt++], rIICDS);
            for(i=0;i<10;i++);                  //For setup time until rising edge of IICSCL
            iowrite32(0xaf,rIICCON);                     //Resumes IIC operation.
            break;

        default:
            break;      
    }
    return IRQ_HANDLED;
}

static int iic_open(struct inode *inode, struct file *filp)
{
        int ret;
        unsigned long gpe;
//        if(!down_trylock(&lock))
        {
//                printk("open succeed.\n");
//                printk("%x\n",S3C2410_TCFG0);
//                unsigned long a=ioread32(S3C2410_TCFG0);
//                printk("%x\n",a);
                gpe = ioread32(rGPECON);
                gpecon_save = gpe;
                iowrite32(gpe|0xa00000, rGPECON);
                gpe = ioread32(rGPEUP);
                gpeup_save = gpe;
                iowrite32(gpe|0xc000, rGPEUP);
                 
                ret = request_irq(IRQ_IIC, iic_interrupt, IRQF_DISABLED, DEVICE_NAME,NULL);

                if (ret)
                 {

                        //DPRINTK("Register IRQ_TIMER0 failed!\n");
                        printk("Register IRQ_IIC failed!\n");
                        return ret;

                }
//                enable_irq(IRQ_IIC);
                 
                iowrite32(0xaf, rIICCON);
                iowrite32(0x10, rIICADD);
                iowrite32(0x10, rIICSTAT);
                iowrite32((1<<2)|(1<<0), rIICLC);
                 
                rd_addr=0;
                wr_addr=0;
                 
                printk("open succeed\n");
                return 0;
                                 
        }                 
//        else
//                return -EBUSY;
}

int iic_release(struct inode *inode, struct file *filp)
{
//        disable_irq(IRQ_IIC);
        free_irq(IRQ_IIC,NULL);
        iowrite32(gpecon_save,rGPECON);
        iowrite32(gpeup_save,rGPEUP);
//        up(&lock);
        return 0;
}

static ssize_t iic_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
        _iicMode = SETRDADDR;
        _iicPt = 0;
        _iicData[0] = (unsigned char)rd_addr;
        _iicDataCount = 1;
         
        iowrite32(slvAddr, rIICDS);
        iowrite32(0xf0, rIICSTAT);
         
        while(_iicDataCount != -1);
         
        _iicMode = RDDATA;
        _iicPt = 0;
        _iicDataCount =1;
         
        iowrite32(slvAddr, rIICDS);
        iowrite32(0xb0, rIICSTAT);
        iowrite32(0xaf, rIICCON);
         
        while(_iicDataCount != -1)
         
        *buf = _iicData[1];
        rd_addr++;
        return 1;
                 
}


static ssize_t iic_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
        _iicMode = WRDATA;
        _iicPt = 0;
        _iicData[0] = (unsigned char)wr_addr;
        _iicData[1] = *buf;
         
        iowrite32(0xaf, rIICCON);
        iowrite32(slvAddr, rIICDS);
        iowrite32(0xf0, rIICSTAT);
         
        while(_iicDataCount != -1);
         
        _iicMode = POLLACK;
         
        while(1)
        {
                iowrite32(slvAddr,rIICDS);
                _iicStatus = 0x100;
                iowrite32(0xf0, rIICSTAT);
                iowrite32(0xaf, rIICCON);
                 
                while(_iicStatus == 0x100);
                 
                if(!(_iicStatus&0x1))
                        break;
        }         
        iowrite32(0xd0,rIICSTAT);
        iowrite32(0xaf,rIICCON);
        Delay(1);
        wr_addr++;         
        return 1;
}

static int iic_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
        return 0;
}

static const struct file_operations iic_fops =
{
        .owner = THIS_MODULE,
        .open = iic_open,
        .read = iic_read,
        .write = iic_write,
        .ioctl = iic_ioctl,
        .release = iic_release,
};

static int iic_init(void)
{
        int result;
        dev_t devno = MKDEV(iic_major,0);

        if(iic_major)
        {
                result = register_chrdev_region(devno,1,"iic");
        }
          else  /* 动态分配设备号 */
  {
    result = alloc_chrdev_region(&devno, 0, 1, "iic");
    iic_major = MAJOR(devno);
  }   
   
  if (result < 0)
    return result;

  /*初始化cdev结构*/
  cdev_init(&cdev, &iic_fops);
  cdev.owner = THIS_MODULE;
  cdev.ops = &iic_fops;
   
  /* 注_册字符设备 */
  cdev_add(&cdev, MKDEV(iic_major, 0), 1);
   
          gpio_base_addr=ioremap(GPIO_PA,0x20);
        if (gpio_base_addr == NULL) {
                printk(KERN_ERR "Failed to remap register block\n");
                return -ENOMEM;
        }
        iic_base_addr=ioremap(IIC_PA,0x20);
        if (iic_base_addr == NULL) {
                printk(KERN_ERR "Failed to remap register block\n");
                iounmap(gpio_base_addr);
                return -ENOMEM;
        }
        timer_base_addr=ioremap(TIMER_PA,0x50);
        if (timer_base_addr == NULL) {
                printk(KERN_ERR "Failed to remap register block\n");
                iounmap(gpio_base_addr);
                iounmap(iic_base_addr);
                return -ENOMEM;
        }
         
          printk("iic_init ok!\n");
    return 0;
}

static void iic_exit(void)
{
        iounmap(gpio_base_addr);
        iounmap(iic_base_addr);
        iounmap(timer_base_addr);         
        cdev_del(&cdev);   /*注销设备*/
  unregister_chrdev_region(MKDEV(iic_major, 0), 1); /*释放设备号*/
          printk("iic_exit ok!\n");
}

module_init(iic_init);
module_exit(iic_exit);

上面的write函数每次往EEPROM中写一个字节的数据,而且每次写完wd_addr都会加1。

上面的驱动程序,写的时候,中断一直没有产生,不知道是什么原因。不知道是不是这种方法不可行。
希望大家不吝赐教,先谢过。

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

0

主题

1

帖子

1

粉丝