I2C设备驱动(三)--linux i2c驱动框架

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/flaoter/article/details/72991086

转载请标明出处floater的csdn blog,http://blog.csdn.net/flaoter

1 体系结构

linux的i2c体系由以下三部分组成:

1.1 i2c核心

由linux内核提供,定义基本数据结构,实现i2c总线,驱动和设备的注册、注销,通信方法等。与设备无关。

1.2 i2c控制器驱动

由i2c控制器厂商提供,目前i2c控制器大多集中在soc芯片中,所以大多由soc厂商提供。代表i2c master,主要实现控制器的设备注册和驱动注册。

1.3 i2c设备驱动

一般由外设厂商提供,代表i2c slave,主要实现从设备的注册和驱动注册。

2 数据结构

i2c_adapter对应一个适配器设备,用于描述i2c master控制器

struct i2c_adapter {
    struct module *owner;
    unsigned int class;       /* classes to allow probing for */
    const struct i2c_algorithm *algo; /* the algorithm to access the bus */
    void *algo_data;

    /* data fields that are valid for all devices   */
    struct rt_mutex bus_lock;

    int timeout;            /* in jiffies */
    int retries;
    struct device dev;      /* the adapter device */

    int nr;
    char name[48];
    struct completion dev_released;

    struct mutex userspace_clients_lock;
    struct list_head userspace_clients;

    struct i2c_bus_recovery_info *bus_recovery_info;
};

i2c总线传输方法,总线传输协议的就是在此结构体成员函数中实现的。

struct i2c_algorithm {
    /* If an adapter algorithm can't do I2C-level access, set master_xfer
       to NULL. If an adapter algorithm can do SMBus access, set
       smbus_xfer. If set to NULL, the SMBus protocol is simulated
       using common I2C messages */
    /* master_xfer should return the number of messages successfully
       processed, or a negative value on error */
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
               int num);
    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
               unsigned short flags, char read_write,
               u8 command, int size, union i2c_smbus_data *data);

    /* To determine what the adapter supports */
    u32 (*functionality) (struct i2c_adapter *);
};

上述两个数据结构i2c_adpter, i2c_algorithm都需要在控制器驱动中进行实现。

i2c_client是i2c slave设备的数据结构

struct i2c_client {
    unsigned short flags;       /* div., see below      */
    unsigned short addr;        /* chip address - NOTE: 7bit    */
                    /* addresses are stored in the  */
                    /* _LOWER_ 7 bits       */
    char name[I2C_NAME_SIZE];
    struct i2c_adapter *adapter;    /* the adapter we sit on    */
    struct device dev;      /* the device structure     */
    int irq;            /* irq issued by device     */
    struct list_head detected;
};

i2c设备驱动数据结构,是i2c_client设备的驱动。

struct i2c_driver {
    unsigned int class;

    /* Notifies the driver that a new bus has appeared. You should avoid
     * using this, it will be removed in a near future.
     */
    int (*attach_adapter)(struct i2c_adapter *) __deprecated;

    /* Standard driver model interfaces */
    int (*probe)(struct i2c_client *, const struct i2c_device_id *);
    int (*remove)(struct i2c_client *);

    /* driver model interfaces that don't relate to enumeration  */
    void (*shutdown)(struct i2c_client *);
    int (*suspend)(struct i2c_client *, pm_message_t mesg);
    int (*resume)(struct i2c_client *);

    /* Alert callback, for example for the SMBus alert protocol.
     * The format and meaning of the data value depends on the protocol.
     * For the SMBus alert protocol, there is a single bit of data passed
     * as the alert response's low bit ("event flag").
     */
    void (*alert)(struct i2c_client *, unsigned int data);

    /* a ioctl like command that can be used to perform specific functions
     * with the device.
     */
    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

    struct device_driver driver;
    const struct i2c_device_id *id_table;

    /* Device detection callback for automatic device creation */
    int (*detect)(struct i2c_client *, struct i2c_board_info *);
    const unsigned short *address_list;
    struct list_head clients;
};

i2c_driver需要在i2c设备驱动中进行实现。

3 控制器驱动

内核源码目录结构及config配置如下:
drivers/i2c/
├── algos
│ ├── i2c-algo-bit.c
│ ├── i2c-algo-pca.c
│ ├── i2c-algo-pcf.c
│ ├── i2c-algo-pcf.h
│ ├── Kconfig
│ └── Makefile
├── busses
│ ├── i2c-gpio.c
│ ├── i2c-vendor.c
│ ├── Kconfig
│ ├── Makefile
├── i2c-boardinfo.c
├── i2c-core.c
├── i2c-core.h
├── i2c-dev.c
├── i2c-mux.c
├── i2c-slave-eeprom.c
├── i2c-smbus.c
├── i2c-stub.c
├── Kconfig
├── Makefile
└── muxes
├── Kconfig
└── Makefile

obj-$(CONFIG_I2C) += i2c-core.o
obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
obj-y += algos/ busses/ muxes/
obj-$(CONFIG_I2C_VENDORNAME) += i2c-vendor.o //控制器驱动,隐藏了厂商名
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
//gpio模拟

控制器驱动部分包括控制器设备注册,控制器驱动注册,总线传输和用户层接口四个部分,下面分别对它们进行说明。

3.1 控制器设备注册

该部分代码在dt解析中实现,请参见kernel初始化时dt解析的代码。i2c controller是platform device。
dts中定义如下:

i2c2: i2c@50d00000 {
    compatible = "vendor-i2c";
    reg = <0 0x70d00000 0 0x1000>;
    interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
    clock-names = "i2c";
    clocks = <&clk_i2c0>;
    clock-frequency = <400000>;
};

3.2 控制器驱动注册

一般SOC厂商都会自己定义专有控制器结构体,不同厂商的数据结构成员不同,但肯定与自身控制器的使用密切相关的内容,比如内存基地址,clock,中断号等。linux驱动与设备是一对多的关系,在i2c_adapter设备注册时,控制器的结构体信息一般会提供给i2c_adapter作为私有数据。,本文中的结构体如下:

struct vendor_i2c {
    struct i2c_msg *msg;
    struct i2c_adapter adap;  
    void __iomem *membase;
    struct clk *clk;
    unsigned int src_clk;  
    int irq;
    struct vendor_platform_i2c *pdata;
};
static struct of_device_id vendor_i2c_of_match[] = {
    { .compatible = "vendor-i2c", },
};

static struct platform_driver vendor_i2c_driver = {
    .probe = vendor_i2c_probe,
    .remove = vendor_i2c_remove,
    .driver = {
           .owner = THIS_MODULE,
           .name = "vendor-i2c",
           .of_match_table = of_match_ptr(vendor_i2c_of_match),
           .pm = &vendor_i2c_pm_ops,
    },
};

static int __init vendor_i2c_init(void)
{
    return platform_driver_register(&vendor_i2c_driver);
}

arch_initcall_sync(vendor_i2c_init);

i2c controller是platform_device,因此驱动通过platform_driver_register进行注册。由于compatible属性为”vendor-i2c”的platform device已经在3.1中注册完成,vendor_i2c_probe会被加载执行。


static int vendor_i2c_probe(struct platform_device *pdev)
{
    struct vendor_i2c *pi2c;
    struct resource *res;
    struct device_node *np = pdev->dev.of_node;

    ...

    pi2c = devm_kzalloc(&pdev->dev, sizeof(struct vendor_i2c), GFP_KERNEL);

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //dt中reg资源

    //i2c_adapter数据结构的填充
    i2c_set_adapdata(&pi2c->adap, pi2c);  //adapter->dev->drvdata=pi2c
    snprintf(pi2c->adap.name, sizeof(pi2c->adap.name), "%s", "vendor-i2c");
    pi2c->adap.owner = THIS_MODULE;
    pi2c->adap.retries = 3;
    pi2c->adap.algo = &vendor_i2c_algo;
    pi2c->adap.algo_data = pi2c;
    pi2c->adap.dev.parent = &pdev->dev;
    pi2c->adap.nr = pdev->id;
    pi2c->adap.dev.of_node = pdev->dev.of_node;
    pi2c->membase = devm_ioremap_nocache(&pdev->dev, res->start,
        res->end - res->start);
    //clock相关
    pi2c->pdata = devm_kzalloc(&pdev->dev,
            sizeof(struct vendor_platform_i2c), GFP_KERNEL);
    memcpy(pi2c->pdata, &vendor_platform_i2c_default,
            sizeof(struct vendor_platform_i2c));
    if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency",
        &prop))
        pi2c->pdata->bus_freq = prop;

    ret = vendor_i2c_clk_init(pi2c);

    clk_prepare_enable(pi2c->clk);
    vendor_i2c_enable(pi2c);
    clk_disable(pi2c->clk);

    ret = i2c_add_numbered_adapter(&pi2c->adap); //adapter的注册过程在下面进行了展开
    ...
}

其中,最后的adapter注册如下代码,在i2c-core.c实现。

int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
    return __i2c_add_numbered_adapter(adap);
}
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
    return i2c_register_adapter(adap);
}
static int i2c_register_adapter(struct i2c_adapter *adap)
{
   ...
    dev_set_name(&adap->dev, "i2c-%d", adap->nr);  //adapter->dev的name是i2c-0,1,2等,设备注册后udev会在/dev下创建对应的设备名节点
    adap->dev.bus = &i2c_bus_type;       //i2c_bus_type对adpater作用??
    adap->dev.type = &i2c_adapter_type;
    res = device_register(&adap->dev);   //adapter设备注册

exit_recovery:
    /* create pre-declared device nodes */
    of_i2c_register_devices(adap);                 //(1)
    acpi_i2c_register_devices(adap);
    acpi_i2c_install_space_handler(adap);

    if (adap->nr < __i2c_first_dynamic_bus_num)
        i2c_scan_static_board_info(adap);   //(2)

}

(1)通过对dt进行解析,对挂在i2c controller下的i2c从设备进行注册。i2c从设备在linux中是通过i2c_client数据结构进行描述的,因此注册过程主要是对i2c_client数据结构的填充并将它注册到内核中。dt中的描述如下,本例中的i2c从设备是BOSCH的加速度传感器:

&i2c2 {
    status = "okay";
    clock-frequency = <400000>;
    bma2x2@19{
        compatible = "BOSCH,bma2x2";
        reg = <0x19>;
        gpios = <&ap_gpio 92 0>;
    };
}

继续i2c_client设备的注册,代码如下:

static void of_i2c_register_devices(struct i2c_adapter *adap)
{
    struct device_node *node;

    /* Only register child devices if the adapter has a node pointer set */
    if (!adap->dev.of_node)
        return;

    dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");

    for_each_available_child_of_node(adap->dev.of_node, node) {
        if (of_node_test_and_set_flag(node, OF_POPULATED))
            continue;
        of_i2c_register_device(adap, node);  //调用of_i2c_register_device进行设备注册,下面有展开
    }
}

static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
                         struct device_node *node)
{
    struct i2c_client *result;
    struct i2c_board_info info = {};
    struct dev_archdata dev_ad = {};
    const __be32 *addr_be;
    u32 addr;
    int len;

    dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);

    if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {   //解析dt中设备compiatble属性填充info.type
        dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
            node->full_name);
        return ERR_PTR(-EINVAL);
    }

    addr_be = of_get_property(node, "reg", &len);   //从设备地址
    if (!addr_be || (len < sizeof(*addr_be))) {
        dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
            node->full_name);
        return ERR_PTR(-EINVAL);
    }

    addr = be32_to_cpup(addr_be);
    if (addr & I2C_TEN_BIT_ADDRESS) {
        addr &= ~I2C_TEN_BIT_ADDRESS;
        info.flags |= I2C_CLIENT_TEN;
    }

    if (addr & I2C_OWN_SLAVE_ADDRESS) {
        addr &= ~I2C_OWN_SLAVE_ADDRESS;
        info.flags |= I2C_CLIENT_SLAVE;
    }

    if (i2c_check_addr_validity(addr, info.flags)) {
        dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
            info.addr, node->full_name);
        return ERR_PTR(-EINVAL);
    }

    info.addr = addr;
    info.of_node = of_node_get(node);
    info.archdata = &dev_ad;

    if (of_get_property(node, "wakeup-source", NULL))
        info.flags |= I2C_CLIENT_WAKE;

    result = i2c_new_device(adap, &info);       //注册i2c device,在下面会进行展开
    if (result == NULL) {
        dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
            node->full_name);
        of_node_put(node);
        return ERR_PTR(-EINVAL);
    }
    return result;
}

struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
    struct i2c_client   *client;
    int         status;

    client = kzalloc(sizeof *client, GFP_KERNEL);
    if (!client)
        return NULL;

    client->adapter = adap;   //client对应的adapter,建立client与adapter的对应关系

    client->dev.platform_data = info->platform_data;

    if (info->archdata)
        client->dev.archdata = *info->archdata;

    client->flags = info->flags;
    client->addr = info->addr;
    client->irq = info->irq;

    strlcpy(client->name, info->type, sizeof(client->name));  //client->name的赋值,它来源于dt中的slave设备的compatible属性,后面设备与驱动的probe主要是通过判断此设备成员和设备驱动的id_table成员是否一致

    status = i2c_check_addr_validity(client->addr, client->flags);
    if (status) {
        dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
            client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
        goto out_err_silent;
    }

    /* Check for address business */
    status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client));
    if (status)
        goto out_err;

    client->dev.parent = &client->adapter->dev;   //client设备与adapter设备的关系
    client->dev.bus = &i2c_bus_type;    //client设备的总线类型是i2c_bus_type,client与driver的probe就是通过i2c_bus_type的probe实现的
    client->dev.type = &i2c_client_type;
    client->dev.of_node = info->of_node;
    client->dev.fwnode = info->fwnode;

    i2c_dev_set_name(adap, client);
    status = device_register(&client->dev);  //设备注册
    if (status)
        goto out_err;

    dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
        client->name, dev_name(&client->dev));

    return client;

out_err:
    dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
        "(%d)\n", client->name, client->addr, status);
out_err_silent:
    kfree(client);
    return NULL;
}

(2) 扫描 __i2c_board_list,对已经添加到此list的i2c设备进行注册。在device tree应用之前,i2c从设备通过调用i2c_register_board_info将自己添加到 __i2c_board_list,目前此中方法已很少使用。

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
    struct i2c_devinfo  *devinfo;

    down_read(&__i2c_board_lock);
    list_for_each_entry(devinfo, &__i2c_board_list, list) {
        if (devinfo->busnum == adapter->nr
                && !i2c_new_device(adapter,
                        &devinfo->board_info))  //设备注册
            dev_err(&adapter->dev,
                "Can't create device at 0x%02x\n",
                devinfo->board_info.addr);
    }
    up_read(&__i2c_board_lock);
}

下面是在kernel的mach目录下找到的一个以此种方式添加到list中的例子。

static struct i2c_board_info i2c_devs[] __initdata = {
    { I2C_BOARD_INFO("wm8753", 0x1A), },
    { I2C_BOARD_INFO("24c08", 0x50), },
};

static void __init smdk6400_machine_init(void)
{
    i2c_register_board_info(0, i2c_devs, ARRAY_SIZE(i2c_devs));
}

至此,在控制器驱动中不仅对i2c_adapter设备进行了注册,还对i2c_client设备进行了注册,并建立了adapter和client之间的关系,而且它们都是i2c_bus_type总线类型的设备。

此处要强调一下, i2c控制器是platform device,它的驱动注册是通过platform_bus, platform_device和platform_driver之间的关系进行probe的,与i2c_bus_type不要混淆。

3.3 总线传输

在controller驱动中还对i2c另一重要结构体进行了赋值,它是发送和接收数据的实现函数。

pi2c->adap.algo = &vendor_i2c_algo;
static const struct i2c_algorithm vendor_i2c_algo = {
    .master_xfer = vendor_i2c_master_xfer,
    .functionality = vendor_i2c_func,
};

static int
vendor_i2c_master_xfer(struct i2c_adapter *i2c_adap,
                          struct i2c_msg *msgs, int num)
{
    int im = 0, ret = 0;
    struct vendor_i2c *pi2c = i2c_adap->algo_data;

    clk_enable(pi2c->clk);

    for (im = 0; ret >= 0 && im != num; im++) {
        ret = vendor_i2c_handle_msg(i2c_adap, &msgs[im], im == num - 1);
    }
    clk_disable(pi2c->clk);

    return (ret >= 0) ? im : -1;
}

static int
vendor_i2c_handle_msg(struct i2c_adapter *i2c_adap,
struct i2c_msg *pmsg, int is_last_msg)
{
    int rc;
    struct vendor_i2c *pi2c = i2c_adap->algo_data;

    rc = vendor_i2c_send_target_addr(pi2c, pmsg);

    if ((pmsg->flags & I2C_M_RD))
        return vendor_i2c_readbytes(pi2c, pmsg->buf, pmsg->len);
    else
        return vendor_i2c_writebytes(pi2c, pmsg->buf, pmsg->len,
                       is_last_msg);
}

vendor_i2c_send_target_addr, vendor_i2c_readbytes, vendor_i2c_writebytes等依赖于具体厂商的实现。

3.4 用户层接口

此外,i2c-dev.c中定义了与应用层交互接口的ops,用户可以直接打开/dev/i2c-0或其它设备节点,使用文件描述符进行读写等操作,不必关心底层实现。

static const struct file_operations i2cdev_fops = {
    .owner      = THIS_MODULE,
    .llseek     = no_llseek,
    .read       = i2cdev_read,
    .write      = i2cdev_write,
    .unlocked_ioctl = i2cdev_ioctl,
    .open       = i2cdev_open,
    .release    = i2cdev_release,
};

读写操作最终都是通过i2c-core提供的i2c_transfer函数实现的,i2c_transfer又是通过调用adap->algo->master_xfer实现的,此处不再进行展开。

4 设备驱动

i2c设备驱动,即i2c master对应的slave设备的驱动,在前面我们已经提到controller驱动中已经对slave设备i2c_client进行了注册,此处是对i2c_client设备对应的驱动i2c_driver进行注册。
此例中的i2c_driver使用传感器的驱动bma2x2_driver,注册代码如下。

#define SENSOR_NAME                 "bma2x2"
static const struct i2c_device_id bma2x2_id[] = {
    { SENSOR_NAME, 0 },
    { }
};

MODULE_DEVICE_TABLE(i2c, bma2x2_id);
static const struct of_device_id bma2x2_of_match[] = {
    { .compatible = "BOSCH,bma2x2", },
    { }
};
MODULE_DEVICE_TABLE(of, bma2x2_of_match);

static struct i2c_driver bma2x2_driver = {
    .driver = {
        .owner  = THIS_MODULE,
        .name   = SENSOR_NAME,
        .of_match_table = bma2x2_of_match,
        .pm = &bma2x2_pm_ops
    },
    .id_table   = bma2x2_id,
    .probe      = bma2x2_probe,
    .remove     = bma2x2_remove,
    .shutdown   = bma2x2_shutdown,
};

static int __init BMA2X2_init(void)
{
    return i2c_add_driver(&bma2x2_driver);
}

static void __exit BMA2X2_exit(void)
{
    i2c_del_driver(&bma2x2_driver);
}
module_init(BMA2X2_init);
module_exit(BMA2X2_exit);

驱动注册i2c_add_driver函数的调用过程中会调用dev->bus->probe,其中dev是通过遍历所有dev设备得到的。当i2c_client设备注册完成后,client->dev.bus = &i2c_bus_type,因此,此处调用的bus->probe就是i2c_bus_type.probe函数指针指向的函数。

i2c_add_driver
    i2c_register_driver(THIS_MODULE, driver)
        res = driver_register(&driver->driver);
            ret = bus_add_driver(drv);
                driver_attach(drv);
                    bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
                    __driver_attach
                        driver_probe_device(drv, dev);
                            ret = really_probe(dev, drv);
                                dev->bus->probe(dev);

i2c_bus_type在i2c-core.c中定义如下:

struct bus_type i2c_bus_type = {
    .name       = "i2c",
    .match      = i2c_device_match,
    .probe      = i2c_device_probe,
    .remove     = i2c_device_remove,
    .shutdown   = i2c_device_shutdown,
    .pm     = &i2c_device_pm_ops,
};

static int i2c_device_probe(struct device *dev)
{
    struct i2c_client   *client = i2c_verify_client(dev);
    struct i2c_driver   *driver;
    int status;

    if (!client)
        return 0;

    driver = to_i2c_driver(dev->driver);
    if (!driver->probe || !driver->id_table)
        return -ENODEV;

    if (!device_can_wakeup(&client->dev))
        device_init_wakeup(&client->dev,
                    client->flags & I2C_CLIENT_WAKE);
    dev_dbg(dev, "probe\n");

    status = of_clk_set_defaults(dev->of_node, false);
    if (status < 0)
        return status;

    status = dev_pm_domain_attach(&client->dev, true);
    if (status != -EPROBE_DEFER) {
        status = driver->probe(client, i2c_match_id(driver->id_table,
                    client));   //driver->probe的调用依赖于i2c_match_id的结果
        if (status)
            dev_pm_domain_detach(&client->dev, true);
    }

    return status;
}

static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
                        const struct i2c_client *client)
{
    while (id->name[0]) {
        if (strcmp(client->name, id->name) == 0)
            return id;
        id++;
    }
    return NULL;
}

i2c_match_id的实现就是在比对client->name和id->name。client->name在controller驱动中注册i2c_client设备的过程中有过赋值,它来自于dt中slave设备的compatible属性。id_name在设备驱动中定义,本例中就是宏定义SENSOR_NAME,即 “bma2x2”,与dt中slave中的compatible属性逗号后面的字符串一致。因此driver->probe即 bma2x2_probe会被调用。

5 总结

理解linux i2c框架就要理清如下关系:
1. bus, device, device_driver的驱动模型。
2. controller和slave设备的关系。
3. i2c_client与i2c_driver的关系。
4. core层的价值。

展开阅读全文

没有更多推荐了,返回首页