I2C設備驅動(三)--linux i2c驅動框架

轉載請標明出處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層的價值。

發佈了29 篇原創文章 · 獲贊 27 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章