Linux设备驱动之3.4.2内核下的I2C驱动

本文详细介绍了Linux 3.4.2内核中的I2C驱动,包括硬件协议、驱动框架和设备创建方法。讲解了如何使用i2c_board_info和i2c_register_board_info注册设备,以及i2c_new_device和i2c_new_probed_device的使用区别。还探讨了如何从用户空间动态创建和删除设备,以及detect函数在设备识别中的作用。最后,简述了驱动编写和总线操作的过程。
摘要由CSDN通过智能技术生成
  1. 框架
    1.1 硬件协议简介
    1.2 驱动框架
    1.3 bus-drv-dev模型及写程序
    a. 设备的4种构建方法

详情参照:linux-3.4.2\Documentation\i2c:instantiating-devices:
以下摘取部分

Method 1: Declare the I2C devices by bus number

Method 2: Instantiate the devices explicitly
static struct i2c_board_info sfe4001_hwmon_info = {
    I2C_BOARD_INFO("max6647", 0x4e),
};

int sfe4001_init(struct efx_nic *efx)
{
    (...)
    efx->board_info.hwmon_client =
        i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);

    (...)
}


static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };

static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev)
{
    (...)
    struct i2c_adapter *i2c_adap;
    struct i2c_board_info i2c_info;

    (...)
    i2c_adap = i2c_get_adapter(2);
    memset(&i2c_info, 0, sizeof(struct i2c_board_info));
    strlcpy(i2c_info.type, "isp1301_nxp", I2C_NAME_SIZE);
    isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,
                           normal_i2c, NULL);
    i2c_put_adapter(i2c_adap);
    (...)
}

Method 3: Probe an I2C bus for certain devices

Method 4: Instantiate from user-space

Example:
# echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device

对上面该Document文档进行解析:

a.1 定义一个i2c_board_info, 里面有:名字, 设备地址
然后i2c_register_board_info(busnum, …) (把它们放入__i2c_board_list链表)
list_add_tail(&devinfo->list, &__i2c_board_list);

链表何时使用:
i2c_register_adapter > i2c_scan_static_board_info > i2c_new_device

使用限制:必须在 i2c_register_adapter 之前 i2c_register_board_info
所以:不适合我们动态加载insmod

a.2 直接i2c_new_device, i2c_new_probed_device : probed:创建已经被枚举被识别的设备!
a.2.1 i2c_new_device : 强制认为设备肯定存在
a.2.2
i**2c_new_probed_device :对于”已经识别出来的设备”(probed_device),才会创建(“new”)**
i2c_new_probed_device :对addr_list里面的每一项调用probe函数,如果存在的话,才进行new_device
probe(adap, addr_list[i]) /* 确定设备是否真实存在 */
info->addr = addr_list[i];/遍历链表/
i2c_new_device(adap, info);

a.4 从用户空间创建设备
创建设备
echo at24c08 0x50 > /sys/class/i2c-adapter/i2c-0/new_device
导致i2c_new_device被调用

删除设备
echo 0x50 > /sys/class/i2c-adapter/i2c-0/delete_device
导致i2c_unregister_device

a.3
前面的3种方法都要事先确定适配器(I2C总线,I2C控制器)
问1:如果我事先并不知道这个I2C设备在哪个适配器上,怎么办?
答1:去class表示的所有的适配器上查找
问2:有上一些I2C设备的地址是一样,怎么继续分配它是哪一款?
答2:用detect函数

static struct i2c_driver at24cxx_driver = {
    .class  = I2C_CLASS_HWMON,      /* 表示去哪些适配器上找设备 */
    .driver    = {
        .name    = "100ask",
        .owner    = THIS_MODULE,
    },
    .probe        = at24cxx_probe,
    .remove        = __devexit_p(at24cxx_remove),
    .id_table    = at24cxx_id_table,
    .detect     = at24cxx_detect,  /* 用这个函数来检测设备确实存在 */
    .address_list    = addr_list,   /* 这些设备的地址 */
};

例如at24xx的addr_list
static const unsigned short addr_list[] = { 0x60, 0x50, I2C_CLIENT_END };

解析:
去”class表示的这一类”I2C适配器,用”detect函数”来确定能否找到”address_list里的设备”,
如果能找到就调用i2c_new_device来注册i2c_client, 这会和i2c_driver的id_table比较,
如果匹配,调用probe

分析函数流程!
i2c_add_driver
i2c_register_driver
a. 将at24cxx_driver放入i2c_bus_type的drv链表
并且从dev链表里取出能匹配的i2c_client并调用probe
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
*/
driver_register

b. 对于每一个适配器,调用__process_new_driver
对于每一个适配器,调用它的函数确定address_list里的设备是否存在
如果存在,再调用detect进一步确定、设置,然后i2c_new_device

      /* Walk the adapters that are already present */
        i2c_for_each_dev(driver, __process_new_driver);
            __process_new_driver
                i2c_do_add_adapter
                    /* Detect supported devices on that bus, and instantiate them */
                    i2c_detect(adap, driver);
                        for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
                            err = i2c_detect_address(temp_client, driver);
                                         /* Make sure there is something at this address */
                                        /* 判断这个设备是否存在:简单的发出S信号确定有ACK */
                                        if (!i2c_default_probe(adapter, addr))
                                            return 0;

                                        memset(&info, 0, sizeof(struct i2c_board_info));
                                        info.addr = addr;   

                                        // 设置info.type
                                        err = driver->detect(temp_client, &info);

                                        i2c_new_device

附上方法三(即是a3中对应的)代码:

at24cxx_drv.c:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>


static int __devinit at24cxx_probe(struct i2c_client *client,
                  const struct i2c_device_id *id)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}

static int __devexit at24cxx_remove(struct i2c_client *client)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}

static const struct i2c_device_id at24cxx_id_table[] = {
    { "at24c08", 0 },
    {}
};

static int at24cxx_detect(struct i2c_client *client,
               struct i2c_board_info *info)
{
    /* 能运行到这里, 表示该addr的设备是存在的
     * 但是有些设备单凭地址无法分辨(A芯片的地址是0x50, B芯片的地址也是0x50)
     * 还需要进一步读写I2C设备来分辨是哪款芯片
     * detect就是用来进一步分辨这个芯片是哪一款,并且设置info->type
     */

    printk("at24cxx_detect : addr = 0x%x\n", client->addr);

    /* 进一步判断是哪一款 */

    strlcpy(info->type, "at24c08", I2C_NAME_SIZE);
    return 0;
}

static const unsigned short addr_list[] = { 0x60, 0x50, I2C_CLIENT_END };

/* 1. 分配/设置i2c_driver */
static struct i2c_driver at24cxx_driver = {
    .class  = I2C_CLASS_HWMON, /* 表示去哪些适配器上找设备 */
    .driver    = {
        .name    = "100ask",
        .owner    = THIS_MODULE,
    },
    .probe        = at24cxx_probe,
    .remove        = __devexit_p(at24cxx_remove),
    .id_table    = at24cxx_id_table,
    .detect     = at24cxx_detect,  /* 用这个函数来检测设备确实存在 */
    .address_list    = addr_list,   /* 这些设备的地址 */
};

static int at24cxx_drv_init(void)
{
    /* 2. 注册i2c_driver */
    i2c_add_driver(&at24cxx_driver);

    return 0;
}

static void at24cxx_drv_exit(void)
{
    i2c_del_driver(&at24cxx_driver);
}


module_init(at24cxx_drv_init);
module_exit(at24cxx_drv_exit);
MODULE_LICENSE("GPL");


总线:
1、SMBUS:system manager bus
2、 i2c_trasfer

 系统推荐我们用SMBUS,看下官方提供的Doucument

SMBus Protocol Summary
======================

The following is a summary of the SMBus protocol. It applies to
all revisions of the protocol (1.0, 1.1, and 2.0).
Certain protocol features which are not supported by
this package are briefly described at the end of this document.

Some adapters understand only the SMBus (System Management Bus) protocol,
which is a subset from the I2C protocol. Fortunately, many devices use
only the same subset, which makes it possible to put them on an SMBus.

If you write a driver for some I2C device, please try to use the SMBus
commands if at all possible (if the device uses only that subset of the
I2C protocol). This makes it possible to use the device driver on both
SMBus adapters and I2C adapters (the SMBus command set is automatically
translated to I2C on I2C adapters, but plain I2C commands can not be
handled at all on most pure SMBus adapters).

Below is a list of SMBus protocol operations, and the functions executing
them.  Note that the names used in the SMBus protocol specifications usually
don't match these function names.  For some of the operations which pass a
single data byte, the functions using SMBus protocol operation names execute
a different protocol operation entirely.


Key to symbols
==============

S     (1 bit) : Start bit
P     (1 bit) : Stop bit
Rd/Wr (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0.
A, NA (1 bit) : Accept and reverse accept bit. 
Addr  (7 bits): I2C 7 bit address. Note that this can be expanded as usual to 
                get a 10 bit I2C address.
Comm  (8 bits): Command byte, a data byte which often selects a register on
                the device.
Data  (8 bits): A plain data byte. Sometimes, I write DataLow, DataHigh
                for 16 bit data.
Count (8 bits): A data byte containing the length of a block operation.

[..]: Data sent by I2C device, as opposed to data sent by the host adapter.


SMBus Quick Command
===================

This sends a single bit to the device, at the place of the Rd/Wr bit.

A Addr Rd/Wr [A] P


SMBus Receive Byte:  i2c_smbus_read_byte()
==========================================

This reads a single byte from a device, without specifying a device
register. Some devices are so simple that this interface is enough; for
others, it
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值