Linux I2C总线控制器驱动(S3C2440)

发布时间: 2024-06-06
来源: 电子工程世界

s3c2440的i2c控制器驱动(精简DIY),直接上代码,注释很详细:

#include
#include

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include

#include
#include

//#define PRINTK printk
#define PRINTK(...)

enum s3c24xx_i2c_state {
    STATE_IDLE,
    STATE_START,
    STATE_READ,
    STATE_WRITE,
    STATE_STOP
};

//i2c控制器寄存器
struct s3c2440_i2c_regs {
    unsigned int iiccon;
    unsigned int iicstat;
    unsigned int iicadd;
    unsigned int iicds;
    unsigned int iiclc;
};

//i2c数据传输载体
struct s3c2440_i2c_xfer_data {
    struct i2c_msg *msgs;
    int msn_num;
    int cur_msg;
    int cur_ptr;
    int state;
    int err;
    wait_queue_head_t wait;
};

static struct s3c2440_i2c_xfer_data s3c2440_i2c_xfer_data;


static struct s3c2440_i2c_regs *s3c2440_i2c_regs;


static void s3c2440_i2c_start(void)
{
    s3c2440_i2c_xfer_data.state = STATE_START;
   
    if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) /* 读 */
    {
        s3c2440_i2c_regs->iicds        = s3c2440_i2c_xfer_data.msgs->addr << 1; 
        s3c2440_i2c_regs->iicstat      = 0xb0;    // 主机接收,启动
    }
    else /* 写 */
    {
        s3c2440_i2c_regs->iicds        = s3c2440_i2c_xfer_data.msgs->addr << 1;
        s3c2440_i2c_regs->iicstat    = 0xf0;        // 主机发送,启动
    }
}

static void s3c2440_i2c_stop(int err)
{
    s3c2440_i2c_xfer_data.state = STATE_STOP;
    s3c2440_i2c_xfer_data.err  = err;

    PRINTK("STATE_STOP, err = %dn", err);


    if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) /* 读 */
    {
        // 下面两行恢复I2C操作,发出P信号
        s3c2440_i2c_regs->iicstat = 0x90;
        s3c2440_i2c_regs->iiccon  = 0xaf;
        ndelay(50);  // 等待一段时间以便P信号已经发出
    }
    else /* 写 */
    {
        // 下面两行用来恢复I2C操作,发出P信号
        s3c2440_i2c_regs->iicstat = 0xd0;
        s3c2440_i2c_regs->iiccon  = 0xaf;
        ndelay(50);  // 等待一段时间以便P信号已经发出
    }

    /* 唤醒 */
    wake_up(&s3c2440_i2c_xfer_data.wait);
   
}

//i2c总线数据传输处理函数
static int s3c2440_i2c_xfer(struct i2c_adapter *adap,
            struct i2c_msg *msgs, int num)
{
    unsigned long timeout;
   
    /* 把num个msg的I2C数据发送出去/读进来 */
    s3c2440_i2c_xfer_data.msgs    = msgs;
    s3c2440_i2c_xfer_data.msn_num = num;
    s3c2440_i2c_xfer_data.cur_msg = 0;
    s3c2440_i2c_xfer_data.cur_ptr = 0;
    s3c2440_i2c_xfer_data.err    = -ENODEV; //确认是否有ack应答

    s3c2440_i2c_start(); //发出start信号,判断read or write

    /* 休眠-等待i2c读写状态改变 */
    timeout = wait_event_timeout(s3c2440_i2c_xfer_data.wait, (s3c2440_i2c_xfer_data.state == STATE_STOP), HZ * 5); //等待状态成立或5s
    if (0 == timeout)
    {
        printk("s3c2440_i2c_xfer time outn");
        return -ETIMEDOUT;
    }
    else
    {
        return s3c2440_i2c_xfer_data.err;
    }
}

static u32 s3c2440_i2c_func(struct i2c_adapter *adap)
{
    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}


static const struct i2c_algorithm s3c2440_i2c_algo = {
//    .smbus_xfer    = ,  //smbus是i2c传输的一个子集,支持的话可以在这里指定处理函数
    .master_xfer    = s3c2440_i2c_xfer, //传输函数
    .functionality    = s3c2440_i2c_func,
};

/* 1. 分配/设置i2c_adapter
 */
static struct i2c_adapter s3c2440_i2c_adapter = {
 .name            = "s3c2440_sheldon",
 .algo            = &s3c2440_i2c_algo, //算法函数
 .owner          = THIS_MODULE,
};

static int isLastMsg(void)
{
    return (s3c2440_i2c_xfer_data.cur_msg == s3c2440_i2c_xfer_data.msn_num - 1);
}

static int isEndData(void)
{
    return (s3c2440_i2c_xfer_data.cur_ptr >= s3c2440_i2c_xfer_data.msgs->len);
}

static int isLastData(void)
{
    return (s3c2440_i2c_xfer_data.cur_ptr == s3c2440_i2c_xfer_data.msgs->len - 1);
}

static irqreturn_t s3c2440_i2c_xfer_irq(int irq, void *dev_id)
{
    unsigned int iicSt;
       
    iicSt  = s3c2440_i2c_regs->iicstat;  //读取i2c控制器的状态寄存器,判断是否读写成功

    if(iicSt & 0x8){ printk("Bus arbitration failednr"); }

    switch (s3c2440_i2c_xfer_data.state)
    {
        case STATE_START : /* 发出S和设备地址后,产生中断 */
        {
            PRINTK("Startn");
            /* 如果没有ACK, 返回错误 */
            if (iicSt & S3C2410_IICSTAT_LASTBIT)
            {
                s3c2440_i2c_stop(-ENODEV);
                break;
            }

            if (isLastMsg() && isEndData())
            {
                s3c2440_i2c_stop(0);
                break;
            }

            /* 进入下一个状态 */
            if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) /* 读 */
            {
                s3c2440_i2c_xfer_data.state = STATE_READ;
                goto next_read;
            }
            else
            {
                s3c2440_i2c_xfer_data.state = STATE_WRITE;
            }   
        }

        case STATE_WRITE:
        {
            PRINTK("STATE_WRITEn");
            /* 如果没有ACK, 返回错误 */
            if (iicSt & S3C2410_IICSTAT_LASTBIT)
            {
                s3c2440_i2c_stop(-ENODEV);
                break;
            }

            if (!isEndData())  /* 如果当前msg还有数据要发送 */
            {
                s3c2440_i2c_regs->iicds = s3c2440_i2c_xfer_data.msgs->buf[s3c2440_i2c_xfer_data.cur_ptr];
                s3c2440_i2c_xfer_data.cur_ptr++;
               
                // 将数据写入IICDS后,需要一段时间才能出现在SDA线上
                ndelay(50);   
               
                s3c2440_i2c_regs->iiccon = 0xaf;        // 恢复I2C传输
                break;               
            }
            else if (!isLastMsg())
            {
                /* 开始处理下一个消息 */
                s3c2440_i2c_xfer_data.msgs++;
                s3c2440_i2c_xfer_data.cur_msg++;
                s3c2440_i2c_xfer_data.cur_ptr = 0;
                s3c2440_i2c_xfer_data.state = STATE_START;
                /* 发出START信号和发出设备地址 */
                s3c2440_i2c_start();
                break;
            }
            else
            {
                /* 是最后一个消息的最后一个数据 */
                s3c2440_i2c_stop(0);
                break;               
            }

            break;
        }

        case STATE_READ:
        {
            PRINTK("STATE_READn");
            /* 读出数据 */
            s3c2440_i2c_xfer_data.msgs->buf[s3c2440_i2c_xfer_data.cur_ptr] = s3c2440_i2c_regs->iicds;           
            s3c2440_i2c_xfer_data.cur_ptr++;
next_read:
            if (!isEndData()) /* 如果数据没读写, 继续发起读操作 */
            {
                if (isLastData())  /* 如果即将读的数据是最后一个, 不发ack */
                {
                    s3c2440_i2c_regs->iiccon = 0x2f;  // 恢复I2C传输,接收到下一数据时无ACK
                }
                else
                {
                    s3c2440_i2c_regs->iiccon = 0xaf;  // 恢复I2C传输,接收到下一数据时发出ACK
                }               
                break;
            }
            else if (!isLastMsg())
            {
                /* 开始处理下一个消息 */
                s3c2440_i2c_xfer_data.msgs++;
                s3c2440_i2c_xfer_data.cur_msg++;
                s3c2440_i2c_xfer_data.cur_ptr = 0;
                s3c2440_i2c_xfer_data.state = STATE_START;
                /* 发出START信号和发出设备地址 */
                s3c2440_i2c_start();
                break;
            }
            else
            {
                /* 是最后一个消息的最后一个数据 */
                s3c2440_i2c_stop(0);
                break;                               
            }
            break;
        }

文章来源于: 电子工程世界 原文链接

本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。