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;
        }

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

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>