Linux I2C(四) i2c device_driver注册与数据传输接口

一,I2C驱动注册

1,i2c设备驱动注册API - i2c_add_driver

i2c_add_driver代码流程:

i2c_add_driver(driver)
----i2c_register_driver(THIS_MODULE, driver)
--------driver_register(&driver->driver);


--------i2c_for_each_dev(driver, __process_new_driver);
------------__process_new_driver(struct device *dev, void *data)
----------------i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap)
--------------------i2c_detect(adap, driver); // 对应上一篇中的 3.3 Method 3: Probe an I2C bus for certain devices
------------------------i2c_detect_address(temp_client, driver);
----------------------------driver->detect(temp_client, &info); // Detect supported devices on that bus, and instantiate them
----------------------------i2c_new_client_device(adapter, &info);
--------------------------------client->addr = info->addr;
--------------------------------client->dev.parent = &client->adapter->dev;
--------------------------------client->dev.bus = &i2c_bus_type;
--------------------------------client->dev.type = &i2c_client_type;
--------------------------------status = device_register(&client->dev); //register i2c client
----------------------------list_add_tail(&client->detected, &driver->clients);

关键代码:

/*
* An i2c_driver is used with one or more i2c_client (device) nodes to access
* i2c slave chips, on a bus instance associated with some i2c_adapter.
*/
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
    int res;

    /* Can't register until after driver model init */
    if (WARN_ON(!is_registered))
        return -EAGAIN;

    /* add the driver to the list of i2c drivers in the driver core */
    driver->driver.owner = owner;
    driver->driver.bus = &i2c_bus_type;
    INIT_LIST_HEAD(&driver->clients);

    /* When registration returns, the driver core
     * will have called probe() for all matching-but-unbound devices.
     */
    res = driver_register(&driver->driver);
    if (res)
        return res;

    pr_debug("driver [%s] registered\n", driver->driver.name);

    /* Walk the adapters that are already present */
    i2c_for_each_dev(driver, __process_new_driver);

    return 0;
}
EXPORT_SYMBOL(i2c_register_driver);

static int __process_new_driver(struct device *dev, void *data)
{
    if (dev->type != &i2c_adapter_type) // bus->p->klist_devices 遍历i2c总线上的adapter设备
        return 0;
    return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}

2,I2C slave device的两种形态

2.1 形态1

CPU和设备之间的所有数据交互,都是通过I2C总线进行,没有其它方式,如PMIC、Audio codec等。 设备在设备模型中的位置,可以把它看作I2C bus上的一个设备。

I2C slave device在DTS中的描述方式,pmic的DTS node是i2c1的一个child node,I2C core负责该设备的创建和注册,以及和其driver的probe等操作:

dts : kernel/arch/arm/boot/dts/imx6dl-riotboard.dts

&i2c1 {
    clock-frequency = <100000>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_i2c1>;
    status = "okay";

    codec: sgtl5000@a {
        compatible = "fsl,sgtl5000";
        reg = <0x0a>;
        clocks = <&clks IMX6QDL_CLK_CKO>;
        VDDA-supply = <&reg_2p5v>;
        VDDIO-supply = <&reg_3p3v>;
    };

    pmic: pf0100@8 {
        compatible = "fsl,pfuze100";
        reg = <0x08>;
        interrupt-parent = <&gpio5>;
        interrupts = <16 8>;
        fsl,pmic-stby-poweroff;

        regulators {
            reg_vddcore: sw1ab {                /* VDDARM_IN */
                regulator-min-microvolt = <300000>;
                regulator-max-microvolt = <1875000>;
                regulator-always-on;
            };
        };
    };
};
2.2 形态2

I2C只是CPU和设备之间进行数据交互的一种,例如HDMI,图像以及音频数据通过TMDS接口传输,EDID等信息的交互通过I2C总线(在HDMI协议中称作DDC接口)。

设备在设备模型中的位置,以TV为例,它一部分功能可看作I2C bus上的一个设备,另一部分是却是platform bus(HDMI Controller)上的一个设备,它的设备驱动要怎么写?一般是以其主要功能为准,TV的主要功能明显是音视频传输,因此应该当做一个platform设备。

在设备模型中的位置不同,最终可以体现在I2C slave device在DTS中的描述方式的不同,hdmi的DTS node位于根目录,作为platform device存在,它的DDC功能所使用的i2c2,是以一个变量的形式引用的:

dts : kernel/arch/arm/boot/dts/imx6dl-riotboard.dts

&hdmi {
    ddc-i2c-bus = <&i2c2>;
    status = "okay";
};

这两种不同的DTS描述,决定了最终的I2C slave device driver有不同的编写方式,具体请参考后面章节的描述。

3,驱动编写步骤

3.1 形态1

1)根据硬件的连接方式,确定该设备所从属的I2C controller(在I2C framework中称作I2C adapter)。

2)在I2C adapter的DTS node中,添加该设备的DTS描述,其格式和正常的platform device一致。

3)DTS描述中的compatible关键字用于设备和驱动的probe,如“compatible = "fsl,pfuze100";”,其它字段根据实际情况自行添加。

4)编写该设备的驱动程序,完成如下内容(具体可参考drivers/regulator/pfuze100-regulator.c):

    a)定义一个struct i2c_driver类型的变量,并调用module_i2c_driver接口将其注册到I2C core中。

    b)该变量包含应包含一个DTS中的“compatible ”字段相同的of_match_table,以及一个probe接口。

5)I2C framework core会在每一个I2C adapter注册时,为它下面所有的slave device创建struct i2c_client结构,并匹配对应的struct i2c_driver变量,调用driver的probe接口。

6)i2c_driver的probe接口的输入参数是struct i2c_client类型的指针(代表I2C slave device),以struct i2c_client指针为参数,可以调用i2c_master_send/i2c_master_recv接口进行简单的I2C传输,同时,也可以通过该指针获得所属的I2C adapter指针,然后通过i2c_transfer接口,进行更为复杂的read、write操作(可参考“drivers/base/regmap/regmap-i2c.c”中的regmap_i2c_read接口)。

Example:

driver: kernel/drivers/regulator/pfuze100-regulator.c

static const struct of_device_id pfuze_dt_ids[] = {
    { .compatible = "fsl,pfuze100", .data = (void *)PFUZE100},
    { .compatible = "fsl,pfuze200", .data = (void *)PFUZE200},
    { .compatible = "fsl,pfuze3000", .data = (void *)PFUZE3000},
    { .compatible = "fsl,pfuze3001", .data = (void *)PFUZE3001},
    { }
};
MODULE_DEVICE_TABLE(of, pfuze_dt_ids);

static int pfuze100_regulator_probe(struct i2c_client *client,
                    const struct i2c_device_id *id)
{
    struct pfuze_chip *pfuze_chip;
    ... ...
}

static struct i2c_driver pfuze_driver = {
    .id_table = pfuze_device_id,
    .driver = {
        .name = "pfuze100-regulator",
        .of_match_table = pfuze_dt_ids,
    },
    .probe = pfuze100_regulator_probe,
    .remove = pfuze100_regulator_remove,
};
module_i2c_driver(pfuze_driver);
3.2 形态2

1)根据硬件的连接方式,确定该设备所从属的I2C controller(在I2C framework中称作I2C adapter),例如第2章例子中的i2c2。

2)将该设备(如HDMI)当做一个platform device,并按照platform device的通用方法,提供DTS描述、编写platform driver,可参考第2章中描述的hdmi的例子。

3)DTS描述中,使用一个变量,指向其I2C adapter的DTS节点,例如:“ddc-i2c-bus = <&i2c2>; ”。

4)在platform driver的probe函数中,以“ddc-i2c-bus ”参数,调用of_parse_phandle接口,获取I2C adapter的device node(即i2c的device node),然后调用of_find_i2c_adapter_by_node获取相应的I2C adapter指针,如下:

/* drivers/gpu/drm/panel/panel-simple.c */

ddc = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
if (ddc) {
        panel->ddc = of_find_i2c_adapter_by_node(ddc);
        of_node_put(ddc);

        if (!panel->ddc) {
                err = -EPROBE_DEFER;
                goto free_backlight;
        }
}

5)获得struct i2c_adapter指针后,即可通过i2c_transfer接口,即可进行read、write操作。

Example:

driver: kernel/drivers/gpu/drm/panel/panel-simple.c

struct panel_simple {
    struct drm_panel base;
    bool prepared;
    bool enabled;
    bool no_hpd;

    const struct panel_desc *desc;

    struct regulator *supply;
    struct i2c_adapter *ddc; //adapter的定义

    struct gpio_desc *enable_gpio;
    struct gpio_desc *hpd_gpio;

    struct drm_display_mode override_mode;

    enum drm_panel_orientation orientation;
};

static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
{
    struct panel_simple *panel;
    struct device_node *ddc;

    ddc = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
    if (ddc) {
        panel->ddc = of_find_i2c_adapter_by_node(ddc); //从device_node中解析出i2c adapter
        of_node_put(ddc);

        if (!panel->ddc)
            return -EPROBE_DEFER;
    }

... ...

free_ddc:
    if (panel->ddc)
        put_device(&panel->ddc->dev);
}

static int panel_simple_platform_probe(struct platform_device *pdev)
{
    const struct of_device_id *id;

    id = of_match_node(platform_of_match, pdev->dev.of_node);
    if (!id)
        return -ENODEV;

    return panel_simple_probe(&pdev->dev, id->data);                
}

static struct platform_driver panel_simple_platform_driver = {
    .driver = {
        .name = "panel-simple",
        .of_match_table = platform_of_match,
    },
    .probe = panel_simple_platform_probe,
    .remove = panel_simple_platform_remove,
    .shutdown = panel_simple_platform_shutdown,
};

static int __init panel_simple_init(void)
{
    int err;

    err = platform_driver_register(&panel_simple_platform_driver); //主驱动是platform架构的
    if (err < 0)
        return err;

    if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) {
        err = mipi_dsi_driver_register(&panel_simple_dsi_driver);
        if (err < 0)
            return err;
    }

    return 0;
}
module_init(panel_simple_init);

//使用获取到的adapter进行数据传输
/**
* drm_do_probe_ddc_edid() - get EDID information via I2C
* @data: I2C device adapter
* @buf: EDID data buffer to be filled
* @block: 128 byte EDID block to start fetching from
* @len: EDID data buffer length to fetch
*
* Try to fetch EDID information by calling I2C driver functions.
*
* Return: 0 on success or -1 on failure.
*/
static int
drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len)
{
    struct i2c_adapter *adapter = data;
    unsigned char start = block * EDID_LENGTH;
    unsigned char segment = block >> 1;
    unsigned char xfers = segment ? 3 : 2;
    int ret, retries = 5;

    /*
     * The core I2C driver will automatically retry the transfer if the
     * adapter reports EAGAIN. However, we find that bit-banging transfers
     * are susceptible to errors under a heavily loaded machine and
     * generate spurious NAKs and timeouts. Retrying the transfer
     * of the individual block a few times seems to overcome this.
     */
    do {
        struct i2c_msg msgs[] = {
            {
                .addr    = DDC_SEGMENT_ADDR,
                .flags    = 0,
                .len    = 1,
                .buf    = &segment,
            }, {
                .addr    = DDC_ADDR,
                .flags    = 0,
                .len    = 1,
                .buf    = &start,
            }, {
                .addr    = DDC_ADDR,
                .flags    = I2C_M_RD,
                .len    = len,
                .buf    = buf,
            }
        };

        /*
         * Avoid sending the segment addr to not upset non-compliant
         * DDC monitors.
         */
        ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers);

        if (ret == -ENXIO) {
            DRM_DEBUG_KMS("drm: skipping non-existent adapter %s\n",
                    adapter->name);
            break;
        }
    } while (ret != xfers && --retries);

    return ret == xfers ? 0 : -1;
}

二,I2C数据传输API

I2C数据传输(read or write),需要以struct i2c_adapter为操作对象。

1,通过DTS节点获取相应的client或者adapter指针

通过DTS节点获取相应的client或者adapter指针,3.2中的例子已经说明了of_find_i2c_adapter_by_node函数的使用场景。

  /* include/linux/i2c.h */

  /* must call put_device() when done with returned i2c_client device */
  extern struct i2c_client *of_find_i2c_device_by_node(struct device_node *node);

  /* must call put_device() when done with returned i2c_adapter device */
  extern struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node);
1.1 of_find_i2c_device_by_node 函数的实现
of_find_i2c_device_by_node(struct device_node *node)
----bus_find_device_by_of_node(&i2c_bus_type, node);
--------bus_find_device(bus, NULL, np, device_match_of_node);
------------while ((dev = next_device(&i)))
----------------device_match_of_node(struct device *dev, const void *np)
--------------------return dev->of_node == np;
----client = i2c_verify_client(dev);
--------dev->type == &i2c_client_type
------------to_i2c_client(dev)
1.2 of_find_i2c_adapter_by_node 函数的实现
of_find_i2c_adapter_by_node(struct device_node *node)
----bus_find_device(&i2c_bus_type, NULL, node, of_dev_or_parent_node_match);
--------while ((dev = next_device(&i)))
------------of_dev_or_parent_node_match(struct device *dev, const void *data)
----------------dev->of_node == data / dev->parent->of_node == data;
----adapter = i2c_verify_adapter(dev);
--------dev->type == &i2c_client_type
------------to_i2c_adapter(dev)

2,i2c数据传输有关的两类接口

一类是以i2c client为参数,进行简单的数据收发,包括i2c_master_send/i2c_master_recv。该方法只可以通过标准方式,发送或者接收一定数量的数据。

另一类是以i2c adapter和i2c msg为参数,可以更为灵活的read或者write数据,包括i2c_transfer。使用该方法可以以struct i2c_msg为参数,一次读取、或者写入、或者读取加写入,一定数量的数据。

2.1 i2c_master_send/i2c_master_recv
/* include/linux/i2c.h */

   /*
    * The master routines are the ones normally used to transmit data to devices
    * on a bus (or read from them). Apart from two basic transfer functions to
    * transmit one message at a time, a more complex version can be used to
    * transmit an arbitrary number of messages without interruption.
    * @count must be be less than 64k since msg.len is u16.
    */
  extern int i2c_master_send(const struct i2c_client *client, const char *buf,
                             int count);
  extern int i2c_master_recv(const struct i2c_client *client, char *buf,
                             int count);
2.1.1 i2c_master_send/i2c_master_recv for example
//kernel\msm_kernel\drivers\media\i2c\ov7251.c
write data:
static int ov7251_write_reg(struct ov7251 *ov7251, u16 reg, u8 val)
{
    u8 regbuf[3];
    int ret;

    regbuf[0] = reg >> 8;
    regbuf[1] = reg & 0xff;
    regbuf[2] = val;

    ret = i2c_master_send(ov7251->i2c_client, regbuf, 3);
    if (ret < 0) {
        dev_err(ov7251->dev, "%s: write reg error %d: reg=%x, val=%x\n",
            __func__, ret, reg, val);
        return ret;
    }

    return 0;
}

read data:
static int ov7251_read_reg(struct ov7251 *ov7251, u16 reg, u8 *val)
{
    u8 regbuf[2];
    int ret;

    regbuf[0] = reg >> 8;
    regbuf[1] = reg & 0xff;

    ret = i2c_master_send(ov7251->i2c_client, regbuf, 2);
    if (ret < 0) {
        dev_err(ov7251->dev, "%s: write reg error %d: reg=%x\n",
            __func__, ret, reg);
        return ret;
    }

    ret = i2c_master_recv(ov7251->i2c_client, val, 1);
    if (ret < 0) {
        dev_err(ov7251->dev, "%s: read reg error %d: reg=%x\n",
            __func__, ret, reg);
        return ret;
    }

    return 0;
}
2.1.2 i2c_master_send/i2c_master_recv 函数实现
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
----i2c_transfer_buffer_flags(client, (char *)buf, count, 0);
--------i2c_transfer(client->adapter, &msg, 1);
------------__i2c_transfer(adap, msgs, num);
----------------for (ret = 0, try = 0; try <= adap->retries; try++)
--------------------if (i2c_in_atomic_xfer_mode() && adap->algo->master_xfer_atomic)
------------------------ret = adap->algo->master_xfer_atomic(adap, msgs, num);
--------------------else
------------------------ret = adap->algo->master_xfer(adap, msgs, num);


int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
----i2c_transfer_buffer_flags(client, buf, count, I2C_M_RD);
--------i2c_transfer(client->adapter, &msg, 1);
2.2 i2c_transfer
  /* Transfer num messages.
   */
  extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
                          int num);
  /* Unlocked flavor */
  extern int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
2.2.1 i2c_transfer for example
//kernel\msm_kernel\drivers\media\i2c\ov5640.c
write data:
static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val)
{
    struct i2c_client *client = sensor->i2c_client;
    struct i2c_msg msg;
    u8 buf[3];
    int ret;

    buf[0] = reg >> 8;
    buf[1] = reg & 0xff;
    buf[2] = val;

    msg.addr = client->addr;
    msg.flags = client->flags;
    msg.buf = buf;
    msg.len = sizeof(buf);

    ret = i2c_transfer(client->adapter, &msg, 1);
    if (ret < 0) {
        dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
            __func__, reg, val);
        return ret;
    }

    return 0;
}

read data:
static int ov5640_read_reg(struct ov5640_dev *sensor, u16 reg, u8 *val)
{
    struct i2c_client *client = sensor->i2c_client;
    struct i2c_msg msg[2];
    u8 buf[2];
    int ret;

    buf[0] = reg >> 8;
    buf[1] = reg & 0xff;

    msg[0].addr = client->addr;
    msg[0].flags = client->flags;
    msg[0].buf = buf;
    msg[0].len = sizeof(buf);

    msg[1].addr = client->addr;
    msg[1].flags = client->flags | I2C_M_RD;
    msg[1].buf = buf;
    msg[1].len = 1;

    ret = i2c_transfer(client->adapter, msg, 2);
    if (ret < 0) {
        dev_err(&client->dev, "%s: error: reg=%x\n",
            __func__, reg);
        return ret;
    }

    *val = buf[0];
    return 0;
}
2.2.2 i2c_transfer的实现
i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
----__i2c_transfer(adap, msgs, num);
--------for (ret = 0, try = 0; try <= adap->retries; try++)
------------if (i2c_in_atomic_xfer_mode() && adap->algo->master_xfer_atomic)
----------------ret = adap->algo->master_xfer_atomic(adap, msgs, num);
------------else
----------------ret = adap->algo->master_xfer(adap, msgs, num);

参考链接:

Linux I2C framework(3)_I2C consumer

  • 12
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值