i2c驱动--驱动框架

来源:互联网 发布:宠宠熊淘宝店卖假货 编辑:程序博客网 时间:2024/04/29 23:50

I2C驱动框架

App :open 、read、 write

驱动:
drv_open 、drv_read 、drv_write :I2C设备驱动:包含数据含义
——————————–
I2C总线驱动程序:①识别 ② 提供读写函数:不知道数据含义

硬件: AT24C02 At24C08

I2c-s3c2410.c(linux-2.6.22.6\drivers\i2c\busses)

一、平台注册及平台设备的描述

平台注册:

static int __init i2c_adap_s3c_init(void){    int ret;    ret = platform_driver_register(&s3c2410_i2c_driver);//注册2410,返回ret    if (ret == 0) {        ret = platform_driver_register(&s3c2440_i2c_driver);//没有注册2410,注册2440,返回ret        if (ret)            platform_driver_unregister(&s3c2410_i2c_driver);//卸载2410    }    return ret;}

注意:
(1)这里只是调用了平台驱动注册函数注册了一个i2c的平台驱动s3c24xx_i2c_driver,这个驱动是一个platform_driver的结构体变量。不是i2c_driver结构体,因为i2c_driver是对设备的驱动,而这里对控制器的驱动要使用platform_driver。区分开总线驱动和设备驱动。

(2)这个驱动是基于platform总线的, 设备信息的部分在板级文件i2c-boardinfo.c中描述并作为platform_device随内核启动被注册, 所以控制器驱动在系统启动的时候就可以工作了。

描述:

/* device driver for platform bus bits */static struct platform_driver s3c2440_i2c_driver = {    .probe      = s3c24xx_i2c_probe,    .remove     = s3c24xx_i2c_remove,    .resume     = s3c24xx_i2c_resume,    .driver     = {        .owner  = THIS_MODULE,        .name   = "s3c2440-i2c",    },};

s3c2440_i2c_driver遵循platform编写,它的所有信息都要在一个platform_driver中描述, 分析也是围绕这个对象展开。

二、调用s3c24xx_i2c_probe

当内核中有同名的平台设备s3c2440-i2c(根据.id_table = s3c24xx_driver_ids来进行匹配),调用s3c24xx_i2c_probe。

http://blog.csdn.net/duzanuolu/article/details/63533696 参考这篇博客
一旦匹配上, 分析流程就会有点变化, 驱动开发都是基于面向对象的思想的, 内核虽然给我们封装了很多”类”, 但当我们开发一个具体的驱动的时候, 还是要对其进行”继承”, 进而创建针对具体设备的资源对象, 资源对象管理着驱动中诸多函数的共用资源, 是整个驱动运行过程中资源管理者与桥梁, 主要包括:内核类+资源(io, irq,时钟, 寄存器)+状态表示+其他,所以, 设计驱动的工作中很重要的一个工作就是”设计资源类”. 下面就是三星设计的类, 把次要的部分剔除了。

资源类

struct s3c24xx_i2c {    spinlock_t      lock;    wait_queue_head_t   wait;    struct i2c_msg      *msg;//收到的i2c-core.c发送过来的i2c_msg对象数组首地址    unsigned int        msg_num;//i2c_msg数组的元素个数    unsigned int        msg_idx;//i2c_msg数组元素的索引    unsigned int        msg_ptr;    unsigned int        tx_setup;    enum s3c24xx_i2c_state  state;//当前控制器的状态, 用枚举量表示STATE_IDLE, STATE_START,STATE_READ,STATE_WRITE,STATE_STOP    void __iomem        *regs;    struct clk      *clk;//时钟    struct device       *dev;//属于device, 按照device来管理    struct resource     *irq;//中断    struct resource     *ioarea;    struct i2c_adapter  adap;//构造并使用的i2c_adapter对象};

s3c24xx_i2c_probe的主要代码注释:

/* s3c24xx_i2c_probe * * called by the bus driver when a suitable device is found*/static int s3c24xx_i2c_probe(struct platform_device *pdev){     ...    /* find the clock and enable it */    i2c->dev = &pdev->dev;    i2c->clk = clk_get(&pdev->dev, "i2c");//使能i2c时钟    ...    ret = i2c_add_adapter(&i2c->adap);//将构造的adapter对象注册到内核    if (ret < 0) {        dev_err(&pdev->dev, "failed to add bus to i2c core\n");        goto err_irq;    }                      }

三、算法实现

注:需要详细注解以后!

static struct s3c24xx_i2c s3c24xx_i2c = {     ...     .algo          = &s3c24xx_i2c_algorithm,       ...        } 

i2c_algorithm

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {    .master_xfer        = s3c24xx_i2c_xfer,    .functionality      = s3c24xx_i2c_func,};

核心:
s3c24xx_i2c_xfer

/* s3c24xx_i2c_xfer * * first port of call from the i2c bus code when an message needs * transferring across the i2c bus.*/static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,            struct i2c_msg *msgs, int num){    struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;    int retry;    int ret;    for (retry = 0; retry < adap->retries; retry++) {        ret = s3c24xx_i2c_doxfer(i2c, msgs, num);        if (ret != -EAGAIN)            return ret;        dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);        udelay(100);    }    return -EREMOTEIO;}

s3c24xx_i2c_doxfer

/* s3c24xx_i2c_doxfer * * this starts an i2c transfer*/static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num){    unsigned long timeout;    int ret;    ret = s3c24xx_i2c_set_master(i2c);    if (ret != 0) {        dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);        ret = -EAGAIN;        goto out;    }    spin_lock_irq(&i2c->lock);    i2c->msg     = msgs;    i2c->msg_num = num;    i2c->msg_ptr = 0;    i2c->msg_idx = 0;    i2c->state   = STATE_START;    s3c24xx_i2c_enable_irq(i2c);    s3c24xx_i2c_message_start(i2c, msgs);    spin_unlock_irq(&i2c->lock);    timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);    ret = i2c->msg_idx;    /* having these next two as dev_err() makes life very      * noisy when doing an i2cdetect */    if (timeout == 0)        dev_dbg(i2c->dev, "timeout\n");    else if (ret != num)        dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);    /* ensure the stop has been through the bus */    msleep(1); out:    return ret;}

s3c24xx_i2c_message_start

/* s3c24xx_i2c_message_start * * put the start of a message onto the bus */static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,                       struct i2c_msg *msg){    unsigned int addr = (msg->addr & 0x7f) << 1;    unsigned long stat;    unsigned long iiccon;    stat = 0;    stat |=  S3C2410_IICSTAT_TXRXEN;    if (msg->flags & I2C_M_RD) {        stat |= S3C2410_IICSTAT_MASTER_RX;        addr |= 1;    } else        stat |= S3C2410_IICSTAT_MASTER_TX;    if (msg->flags & I2C_M_REV_DIR_ADDR)        addr ^= 1;    // todo - check for wether ack wanted or not    s3c24xx_i2c_enable_ack(i2c);    iiccon = readl(i2c->regs + S3C2410_IICCON);    writel(stat, i2c->regs + S3C2410_IICSTAT);    dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);    writeb(addr, i2c->regs + S3C2410_IICDS);    /* delay here to ensure the data byte has gotten onto the bus     * before the transaction is started */    ndelay(i2c->tx_setup);    dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);    writel(iiccon, i2c->regs + S3C2410_IICCON);    stat |=  S3C2410_IICSTAT_START;    writel(stat, i2c->regs + S3C2410_IICSTAT);}

总线驱动和设备驱动的总体框架:

这里写图片描述

i2c_add_driver

①将i2c_driver放入链表
② 从adapter 链表取出适配器,调用drv的 .attach_adapter
③ .attach_adapter调用i2c_probe (adapter,设备地址,function)
1.用adapter的.master_xfer发信号,确定有无该设备
2.若有则调用function

i2c_add_adapter
①放入链表
②调用drv的.attach_adapter
③ .attach_adapter调用i2c_probe (adapter,设备地址,function)
1.用adapter的.master_xfer发信号,确定有无该设备
2.若有则调用function

整个流程通过eeprom.c(drivers/i2c/chips)来分析:
这里写图片描述

原创粉丝点击