IIC控制器驱动流程

                                     IIC控制器驱动流程

IIC控制器也属于片上设备, 因此它的流程也如其他的片上设备类似。首先是静态的初始化好这个设备的相关信息, arch/arm/mach-s3c2410.c

/* I2C */

static struct resource s3c_i2c_resource[] = {

    [0] = {  /*寄存器地址*/

        .start = S3C24XX_PA_IIC,

        .end   = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,

        .flags = IORESOURCE_MEM,

    },

    [1] = {  /*中断号*/

        .start = IRQ_IIC,

        .end   = IRQ_IIC,

        .flags = IORESOURCE_IRQ,

    }

 

};

 

struct platform_device s3c_device_i2c = {   /*IIC设备*/

    .name         = "s3c2410-i2c",    /*名字和驱动里定义的名字要一样,用于匹配*/

    .id       = -1,

    .num_resources    = ARRAY_SIZE(s3c_i2c_resource),

    .resource     = s3c_i2c_resource,

};

 

EXPORT_SYMBOL(s3c_device_i2c);

在系统初始化的时候这个设备会被添加到系统中去。等IIC驱动注册到系统后会probe到这个设备,并初始化这里定义的资源信息。

接下来就是IIC控制器的驱动了   在如下目录: drivers/i2c/busses/i2c-s3c2410.c

模块编程都有一个initexit的函数,init在模块被插入系统时调用,exit在模块被卸载时调用。我们先来看这个驱动的init函数。

static int __init i2c_adap_s3c_init(void)

{

    int ret;

 

    ret = platform_driver_register(&s3c2410_i2c_driver);  /*主测IIC驱动*/

    if (ret == 0) {

        ret = platform_driver_register(&s3c2440_i2c_driver);

        if (ret)

            platform_driver_unregister(&s3c2410_i2c_driver);

    }

 

    return ret;

}

很简单就是注册一个代表IIC驱动的platform_driver对象。

在看exit函数

static void __exit i2c_adap_s3c_exit(void)

{

    platform_driver_unregister(&s3c2410_i2c_driver);  /*卸载IIC驱动*/

    platform_driver_unregister(&s3c2440_i2c_driver);

}

做和init函数相反的工作。

我们看到在init中还注册了s3c2440_i2c_driver对象, 由于它和定义的设备兑现不匹配所以这里就不列出来了。 我们看s3c2410_i2c_driver

static struct platform_driver s3c2410_i2c_driver = {

    .probe      = s3c24xx_i2c_probe,  /*探测函数*/

    .remove     = s3c24xx_i2c_remove,  /*卸载函数*/

    .resume     = s3c24xx_i2c_resume,

    .driver     = {

        .owner  = THIS_MODULE,

        .name   = "s3c2410-i2c",  /*这个要和设备定义的名字相同以能够探测到设备*/

    },

};

当驱动被注册进系统后, 系统会探测到上面提到的已被添加到系统的IIC控制器,所以就会调用probe函数

/* s3c24xx_i2c_probe

 *

 * called by the bus driver when a suitable device is found

*/

 

static int s3c24xx_i2c_probe(struct platform_device *pdev)

{

    struct s3c24xx_i2c *i2c = &s3c24xx_i2c;  /*自己定义的用于保存IIC设备信息的对象*/

    struct resource *res;

    int ret;

 

    /* find the clock and enable it */

 

    i2c->dev = &pdev->dev;

    i2c->clk = clk_get(&pdev->dev, "i2c");  /*获取IIC的时钟源*/

    if (IS_ERR(i2c->clk)) {

        dev_err(&pdev->dev, "cannot get clock/n");

        ret = -ENOENT;

        goto out;

    }

 

    dev_dbg(&pdev->dev, "clock source %p/n", i2c->clk);

 

    clk_enable(i2c->clk);  /*使能IIC时钟*/

 

    /* map the registers */

 

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  /*获取寄存器资源(就是地址)*/

    if (res == NULL) {

        dev_err(&pdev->dev, "cannot find IO resource/n");

        ret = -ENOENT;

        goto out;

    }

 

    i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,

                    pdev->name);  /*查看地址是否空闲*/

 

    if (i2c->ioarea == NULL) {

        dev_err(&pdev->dev, "cannot request IO/n");

        ret = -ENXIO;

        goto out;

    }

 

     /*

* 内存映射, 即把寄存器的物理地址映射为虚拟地址, 以便以后在代码中使用。

*/

    i2c->regs = ioremap(res->start, (res->end-res->start)+1);

 

    if (i2c->regs == NULL) {

        dev_err(&pdev->dev, "cannot map IO/n");

        ret = -ENXIO;

        goto out;

    }

 

    dev_dbg(&pdev->dev, "registers %p (%p, %p)/n", i2c->regs, i2c->ioarea, res);

 

    /* setup info block for the i2c core */

 

    i2c->adap.algo_data = i2c;  /*保存数据便于以后使用*/

    i2c->adap.dev.parent = &pdev->dev;

 

    /* initialise the i2c controller */

 

    ret = s3c24xx_i2c_init(i2c);  /*初始化IIC硬件控制器*/

    if (ret != 0)

        goto out;

 

    /* find the IRQ for this unit (note, this relies on the init call to

     * ensure no current IRQs pending

     */

 

    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  /*获取中断号*/

    if (res == NULL) {

        dev_err(&pdev->dev, "cannot find IRQ/n");

        ret = -ENOENT;

        goto out;

    }

 

    ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,

              pdev->name, i2c);  /*注册中断例程*/

 

    if (ret != 0) {

        dev_err(&pdev->dev, "cannot claim IRQ/n");

        goto out;

    }

 

    i2c->irq = res;

       

    dev_dbg(&pdev->dev, "irq resource %p (%ld)/n", res, res->start);

 

    ret = i2c_add_adapter(&i2c->adap);  /*把适配器添加到系统中去*/

    if (ret < 0) {

        dev_err(&pdev->dev, "failed to add bus to i2c core/n");

        goto out;

    }

 

    platform_set_drvdata(pdev, i2c);  /*保存信息,便于以后使用*/

 

    dev_info(&pdev->dev, "%s: S3C I2C adapter/n", i2c->adap.dev.bus_id);

 

 out:

    if (ret < 0)

        s3c24xx_i2c_free(i2c);

 

    return ret;

}

1       IIC有两个信号线:时钟线和数据线, 任何数据都要在每个时钟点上才能收发, 所以要有个时钟来驱动IIC

2       Probe函数中获取到的资源都是在上面我们讲过的根据硬件spec静态设置好的,我们这里就是把这些资源初始化好。

3       每个IIC控制器都由一个i2c_adapter(适配器)来表示,这里我们把它嵌在了我们自己的结构中s3c24xx_i2c,即代码中的i2c->adap 每个适配器都要有一套接受,发送的通信规则,这在s3c24xx_i2c中定义, 以后会讲到,

4       最后我们要把控制器添加到系统中去: i2c_add_adapter(&i2c->adap) 这样用于就能通过设备文件访问到这个设备。

 

s3c24xx_i2c_remove 主要做一些与probe相反的工作,这里就不多讲了。

接下来看一下s3c24xx_i2c

/* i2c bus registration info */

static struct i2c_algorithm s3c24xx_i2c_algorithm = {   /*iic通信函数*/

    .master_xfer       = s3c24xx_i2c_xfer,   /*收发*/

    .functionality     = s3c24xx_i2c_func,   /*返回IIC支持的功能*/

};

 

static struct s3c24xx_i2c s3c24xx_i2c = {

    .lock   = SPIN_LOCK_UNLOCKED,

    .wait   = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),

    .adap   = {

        .name           = "s3c2410-i2c",

        .owner          = THIS_MODULE,

        .algo           = &s3c24xx_i2c_algorithm,  /*通信算法*/

        .retries        = 2,

        .class          = I2C_CLASS_HWMON,

    },

};

实际上最主要的就是s3c24xx_i2c_xfer了, 它就是实际上用来实现IIC算法的函数。当用于要读写IIC控制器的设备文件时,系统最终会把执行流程走到这个函数。

 

/* 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)

{

/*我们probe中保存的信息*/

    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);  /*IIC通信*/

 

        if (ret != -EAGAIN)

            return ret;

 

        dev_dbg(i2c->dev, "Retrying transmission (%d)/n", retry);

 

        udelay(100);

    }

 

    return -EREMOTEIO;

}

可以看出它是调用s3c24xx_i2c_doxfer来实现IIC通信的

/* 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;

}

具体的算法及IIC通信规则就不多讲了, 可以参考代码和IIC的规范, 这里只是简要讲述了IIC适配器在linux下的驱动的编写流程。

 

你可能感兴趣的:(s3c2410架构描述)