Linux内核4.14版本——I2C子系统(3)_I2C device (consumer)

1. 前言

2. 两种设备形态

2.1 形态1

2.2 形态2

3. 驱动编写步骤

3.1 形态1

3.2 形态2

4. 关键数据结构和API介绍

4.1 i2c client

4.2 I2C adapter

4.3 i2c msg

4.4 编写I2C slave driver所需使用的API


1. 前言

      本文从I2C consumer的角度,介绍怎么在linux中,利用I2C framework提供的接口,编写I2C slave device的驱动程序。

2. 两种设备形态

嵌入式系统中,I2C总线上连接的slave device,有两种形态,如下:

图片1 I2C slave device的两种形态

       形态1,CPU和设备之间的所有数据交互,都是通过I2C总线进行,没有其它方式,如PMIC、Audio codec等。
      形态2,I2C只是CPU和设备之间进行数据交互的一种,例如HDMI,图像以及音频数据通过TDMS接口传输,EDID等信息的交互通过I2C总线(在HDMI协议中称作DDC接口)。

      这两种设备形态决定了设备在设备模型中的位置:

      形态1比较简单,以PMIC为例,可以把它看作I2C bus上的一个设备;

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

2.1 形态1

      形态1,pmic的DTS node是i2c1的一个child node,I2C core负责该设备的创建和注册,以及和其driver的probe等操作:

/* arch/arm/boot/dts/imx6dl-riotboard.dts */
&i2c1 {
        clock-frequency = <100000>;
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_i2c1>;
        status = "okay";
        …
        pmic: pf0100@08 {
                compatible = "fsl,pfuze100";
                …
        };
        ...
};

2.2 形态2

      形态2,hdmi的DTS node位于根目录,作为platform device存在,它的DDC功能所使用的i2c2,是以一个变量的形式引用的:

/* arch/arm/boot/dts/imx6dl-riotboard.dts */
&hdmi {
       ddc-i2c-bus = <&i2c2>;
        status = "okay";
};

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

3. 驱动编写步骤

     针对第2节所描述的两种不同的设备形态,有两种驱动编写方法。

3.1 形态1

      1)根据硬件的连接方式,确定该设备所从属的I2C controller(在I2C framework中称作I2C adapter),例如第2节例子中的i2c1。
      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 provider”的描述可知,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接口)。

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操作。

4. 关键数据结构和API介绍

4.1 i2c client

      由前文可知,I2C framework使用struct i2c_client抽象I2C slave device(对应设备模型中的struct device),具体如下:

/**
 * struct i2c_client - represent an I2C slave device
 * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
 *	I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
 * @addr: Address used on the I2C bus connected to the parent adapter.
 * @name: Indicates the type of the device, usually a chip name that's
 *	generic enough to hide second-sourcing and compatible revisions.
 * @adapter: manages the bus segment hosting this I2C device
 * @dev: Driver model device node for the slave.
 * @irq: indicates the IRQ generated by this device (if any)
 * @detected: member of an i2c_driver.clients list or i2c-core's
 *	userspace_devices list
 * @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter
 *	calls it to pass on slave events to the slave driver.
 *
 * An i2c_client identifies a single device (i.e. chip) connected to an
 * i2c bus. The behaviour exposed to Linux is defined by the driver
 * managing the device.
 */
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;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
	i2c_slave_cb_t slave_cb;	/* callback for slave mode	*/
#endif
};

1)flags,指示该I2C slave device一些特性,包括:

        I2C_CLIENT_PEC,indicates it uses SMBus Packet Error Checking;
        I2C_CLIENT_TEN,indicates the device uses a ten bit chip address;
        I2C_CLIENT_WAKE,该设备具备wakeup的能力。
2)addr,该设备的7-bit的slave地址。
3)adapter,该设备所在的I2C controller。
4)irq,irq number(如果有的话)。

      通常情况下,struct i2c_client变量是由I2C core在register adapter的时候,解析adapter的child node自行创建的(具体可参考“文章 I2C provider”),该数据结构中的有些信息,可通过DTS配置,包括:

xxx:xxx@08 {
        reg = <0x08>;          /* 对应struct i2c_client中的‘addr’*/
        interrupts = <16 8>;   /* 对应struct i2c_client中的‘irq’*/
        wakeup-source;         /* 对应flags中的I2C_CLIENT_WAKE */
};

4.2 I2C adapter

      I2C数据传输(read or write),需要以struct i2c_adapter为操作对象,具体可参考“文章 I2C provider”。

4.3 i2c msg

      I2C数据传输的单位,具体可参考“文章I2C provider”。

4.4 编写I2C slave driver所需使用的API

/* 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);
通过DTS节点获取相应的client或者adapter指针,3.2中的例子已经说明了of_find_i2c_adapter_by_node函数的使用场景。
/* 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);

/* 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);

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

      一类是以i2c client为参数,进行简单的数据收发,包括i2c_master_send/i2c_master_recv。该方法只可以通过标准方式,发送或者接收一定数量的数据。
      另一类是以i2c adapter和i2c msg为参数,可以更为灵活的read或者write数据,包括i2c_transfer。使用该方法可以以struct i2c_msg为参数,一次读取、或者写入、或者读取加写入,一定数量的数据。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值