第15章 Linux I2C核心、总线与设备驱动之Linux I 2 C适配器驱动

15.3 Linux I 2 C适配器驱动

15.3.1 I 2 C适配器驱动的注册与注销

    由于I 2 C总线控制器通常在内存上,所以I 2 C总线控制器本身也连接在platform总线上,要通过platform_driver和platform_device的匹配来执行。因此尽管I 2 C适配器给别人提供了总线,I 2 C适配器自己也被认为是接在platform总线上的一个客户。Linux的总线、设备和驱动模型实际上是一个树形结构,每个节点虽然可能成为别人的总线控制器,但是自己也被认为是从上一级总线枚举出来的。

通常在与I 2 C适配器所对应的platform_driver的probe()函数中完成两个工作。

1、初始化I 2 C适配器所使用的硬件资源,如申请I/O地址、中断号、时钟等。

2、通过i2c_add_adapter()添加i2c_adapter的数据结构,这个i2c_adapter数据结构的成员已经被xxx适配器的相应函数指针所初始化。

通常在platform_driver的remove()函数中完成与加载函数相反的工作。

1、释放I 2 C适配器所使用的硬件资源,如释放I/O地址、中断号、时钟等。

2、通过i2c_del_adapter()删除i2c_adapter的数据结构。

代码清单15.9 I 2 C适配器驱动的注册和注销模板。

static int xxx_i2c_probe(struct platform_device *pdev)
{
         struct i2c_adapter *adap;

         ...
        xxx_adpater_hw_init();//与具体的CPU和I 2 C适配器硬件直接相关
        adap->dev.parent = &pdev->dev;
        adap->dev.of_node = pdev->dev.of_node;

         rc = i2c_add_adapter(adap);
        ...
}

static int xxx_i2c_remove(struct platform_device *pdev)
{
            ...
         xxx_adpater_hw_free();//与具体的CPU和I 2 C适配器硬件直接相关
         i2c_del_adapter(&dev->adapter);

         return 0;
}

static const struct of_device_id xxx_i2c_of_match[] = {
         {.compatible = "vendor,xxx-i2c",},
         {},
};
MODULE_DEVICE_TABLE(of, xxx_i2c_of_match);

static struct platform_driver xxx_i2c_driver = {
         .driver = {
                 .name = "xxx-i2c",
                 .owner = THIS_MODULE,
                 .of_match_table = xxx_i2c_of_match,
        },
         .probe = xxx_i2c_probe,
         .remove = xxx_i2c_remove,
};
module_platform_driver(xxx_i2c_driver);

备注:

上述代码中的xxx_adpater_hw_init()和xxx_adpater_hw_free()函数的实现都与具体的CPU和I 2 C适

配器硬件直接相关。

15.3.2 I 2 C总线的通信方法

需要为特定的I 2 C适配器实现通信方法,主要是实现i2c_algorithm的functionality()函数和master_xfer()函数

functionality()函数非常简单,用于返回algorithm所支持的通信协议,如I2C_FUNC_I2C、
I2C_FUNC_10BIT_ADDR、I2C_FUNC_SMBUS_READ_BYTE、I2C_FUNC_SMBUS_WRITE_BYTE等。

master_xfer()函数在I 2 C适配器上完成传递给该函数的i2c_msg数组中的每个I 2 C消息,代码清单15.10所

示为xxx设备的master_xfer()函数模板。

// Generic master transfer entrypoint.

static int i2c_adapter_xxx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,  int num)
{
         ...
         for (i = 0; i < num; i++) {
             i2c_adapter_xxx_start(); /* 产生开始位 */
             if (msgs[i]->flags &I2C_M_RD) {/* 读消息 */
                     i2c_adapter_xxx_setaddr((msg->addr << 1) | 1); /* 发送从设备读地址 */
                     i2c_adapter_xxx_wait_ack(); /* 获得从设备的 ack */
                     i2c_adapter_xxx_readbytes(msgs[i]->buf, msgs[i]->len); /* 从从设备读数据:读取 msgs[i] ->len长的数据到 msgs[i]->buf */
            } else { /* 写消息 */
                     i2c_adapter_xxx_setaddr(msg->addr << 1); /* 发送从设备写地址 */
                     i2c_adapter_xxx_wait_ack(); /* 获得从设备的 ack */
                     i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len); /*  向从设备写数据:写入msgs[i]->len长的数据到 msgs[i]->buf */ 
                     }
         }
         i2c_adapter_xxx_stop(); /* 产生停止位 */
}

分析:

上述代码给出一个master_xfer()函数处理I 2 C消息数组的流程,对于数组中的每个消息,先判断消息类型,若为读消息,则赋从设备地址为(msg->addr<<1)|1,否则为msg->addr<<1。对每个消息产生一个开始位,紧接着传送从设备地址,然后开始数据的发送或接收,且对最后的消息还需产生一个停止位

图15.3所示为整个master_xfer()完成的时序(时间顺序)。


图15.3 master_xfer()完成的时序

    master_xfer()函数模板中的i2c_adapter_xxx_start()、i2c_adapter_xxx_setaddr()、
i2c_adapter_xxx_wait_ack()、i2c_adapter_xxx_readbytes()、i2c_adapter_xxx_writebytes()和
i2c_adapter_xxx_stop()函数用于完成适配器的底层硬件操作,与I 2 C适配器和CPU的具体硬件直接相
关,
由工程师根据芯片的数据手册(data sheet)来实现。

i2c_adapter_xxx_readbytes()用于从从设备上接收一串数据,i2c_adapter_xxx_writebytes()用于向从设备写入一串数据,这两个函数的内部也会涉及I 2 C总线协议中的ACK应答。

master_xfer()函数的实现形式有多种,多数驱动以中断方式来完成这个流程,比如发起硬件操作请求后,将自己调度出去,因此中间会伴随着睡眠的动作。

多数I 2 C总线驱动会定义一个xxx_i2c结构体,作为i2c_adapter的algo_data(类似“私有数据”),其中绑定

I 2 C消息指针、包含I 2 C消息索引及I 2 C适配器Algorithm访问控制用的自旋锁、等待队列等,而
master_xfer()函数在完成i2c_msg数组中消息的处理时,也经常需要访问xxx_i2c结构体的成员以获取寄存器基地址、锁等信息。代码清单15.11所示为一个典型的xxx_i2c结构体的定义,与图15.2中的xxx_i2c是对应的,具体的实现因硬件而异。

代码清单15.11 xxx_i2c结构体模板
struct xxx_i2c {
         spinlock_t lock; //自旋锁
         wait_queue_head_t wait;//等待队列
         struct i2c_msg *msg; // i2c_msg数组
         unsigned int msg_num;// i2c_msg数组个数
         unsigned int msg_idx;//i2c_msg数组索引
         unsigned int msg_ptr;
         ...
         struct i2c_adapter adap;
};


猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80559951