系统管理总线(SMBus)介绍

系统管理总线协议概要

文档参考linux内核文件Documentation\i2c\smbus-protocol
下面是对SMBus协议的总结。它适用于协议的所有版本(1.0、1.1和2.0)。不支持的某些协议特性
在本文档的最后简要说明。
SMBus协议是I2C协议的一个子集,一些适配器只能适配SMBus协议,但是幸运的是,很多设备拥有相同的协议子集——SMBus协议,这使得可以将它们放在SMBus上。

如果您为某些I2C设备编写驱动程序,请尽可能使用SMBus命令(如果设备只使用I2C协议的子集)。这使得在SMBus适配器和I2C适配器上使用设备驱动程序成为可能(在I2C适配器上SMBus命令集会自动转换为I2C,但是在大多数纯SMBus适配器上根本不能处理普通的I2C命令)。

下面是SMBus协议操作的列表,以及执行这些操作的函数。注意,SMBus协议规范中使用的名称通常与这些函数名称不匹配。对于传递单个数据字节的一些操作,使用SMBus协议操作名的函数执行完全不同的协议操作。

每个操作类型对应一个功能标志。在调用事务函数之前,设备驱动程序应该总是检查(只检查一次)相应的功能标志,以确保底层I2C适配器支持相关的事务。(详情参见如下)

<file:Documentation/i2c/functionality> 
INTRODUCTION(介绍)
------------
Because not every I2C or SMBus adapter implements everything in the 
I2C specifications, a client can not trust that everything it needs
is implemented when it is given the option to attach to an adapter:
the client needs some way to check whether an adapter has the needed
functionality. 
(因为并不是每一个I2C或SMBus适配器实现I2C的一切规范,客户端可以不相信一切需要实现时选择连接到适配器:客户端需要一些方法来检查适配器是否所需的功能。)

FUNCTIONALITY CONSTANTS(功能常量)
-----------------------
For the most up-to-date list of functionality constants, please check
(最新的功能常量定义如下,请查收。定义在linux-5.18\include\uapi\linux\i2c.h)
<linux/i2c.h>!
  I2C_FUNC_I2C                    Plain i2c-level commands (Pure SMBus
                                  adapters typically can not do these)
                                  /普通的iic级别命令(纯SMBus适配器通常不能执行这些操作)
  I2C_FUNC_10BIT_ADDR             Handles the 10-bit address extensions /处理10位地址扩展
  I2C_FUNC_PROTOCOL_MANGLING      Knows about the I2C_M_IGNORE_NAK,
                                  I2C_M_REV_DIR_ADDR and I2C_M_NO_RD_ACK
                                  flags (which modify the I2C protocol!)
  I2C_FUNC_NOSTART                Can skip repeated start sequence /可以跳过重复的开始序列
  I2C_FUNC_SMBUS_QUICK            Handles the SMBus write_quick command
  I2C_FUNC_SMBUS_READ_BYTE        Handles the SMBus read_byte command
  I2C_FUNC_SMBUS_WRITE_BYTE       Handles the SMBus write_byte command
  I2C_FUNC_SMBUS_READ_BYTE_DATA   Handles the SMBus read_byte_data command
  I2C_FUNC_SMBUS_WRITE_BYTE_DATA  Handles the SMBus write_byte_data command
  I2C_FUNC_SMBUS_READ_WORD_DATA   Handles the SMBus read_word_data command
  I2C_FUNC_SMBUS_WRITE_WORD_DATA  Handles the SMBus write_byte_data command
  I2C_FUNC_SMBUS_PROC_CALL        Handles the SMBus process_call command
  I2C_FUNC_SMBUS_READ_BLOCK_DATA  Handles the SMBus read_block_data command
  I2C_FUNC_SMBUS_WRITE_BLOCK_DATA Handles the SMBus write_block_data command
  I2C_FUNC_SMBUS_READ_I2C_BLOCK   Handles the SMBus read_i2c_block_data command
  I2C_FUNC_SMBUS_WRITE_I2C_BLOCK  Handles the SMBus write_i2c_block_data command

A few combinations of the above flags are also defined for your convenience:
	为了方便起见,还定义了上述标志的一些组合
  I2C_FUNC_SMBUS_BYTE             Handles the SMBus read_byte
                                  and write_byte commands
  I2C_FUNC_SMBUS_BYTE_DATA        Handles the SMBus read_byte_data
                                  and write_byte_data commands
  I2C_FUNC_SMBUS_WORD_DATA        Handles the SMBus read_word_data
                                  and write_word_data commands
  I2C_FUNC_SMBUS_BLOCK_DATA       Handles the SMBus read_block_data
                                  and write_block_data commands
  I2C_FUNC_SMBUS_I2C_BLOCK        Handles the SMBus read_i2c_block_data
                                  and write_i2c_block_data commands
  I2C_FUNC_SMBUS_EMUL             Handles all SMBus commands that can be
                                  emulated by a real I2C adapter (using
                                  the transparent emulation layer)

In kernel versions prior to 3.5 I2C_FUNC_NOSTART was implemented as
part of I2C_FUNC_PROTOCOL_MANGLING.
在3.5之前的内核版本中,I2C_FUNC_NOSTART是作为I2C_FUNC_PROTOCOL_MANGLING的一部分实现的。

ADAPTER IMPLEMENTATION /适配器实现
----------------------
When you write a new adapter driver, you will have to implement a
function callback `functionality'. Typical implementations are given
below. /当你写一个新的适配器驱动,你将不得不实现一个函数回调“功能”。下面给出了典型的实现。

A typical SMBus-only adapter would list all the SMBus transactions it
supports. This example comes from the i2c-piix4 driver:

  static u32 piix4_func(struct i2c_adapter *adapter)
  {
	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
	       I2C_FUNC_SMBUS_BLOCK_DATA;
  }

A typical full-I2C adapter would use the following (from the i2c-pxa
driver):

  static u32 i2c_pxa_functionality(struct i2c_adapter *adap)
  {
	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
  }

I2C_FUNC_SMBUS_EMUL includes all the SMBus transactions (with the
addition of I2C block transactions) which i2c-core can emulate using
I2C_FUNC_I2C without any help from the adapter driver. The idea is
to let the client drivers check for the support of SMBus functions
without having to care whether the said functions are implemented in
hardware by the adapter, or emulated in software by i2c-core on top
of an I2C adapter.


CLIENT CHECKING
---------------

Before a client tries to attach to an adapter, or even do tests to check
whether one of the devices it supports is present on an adapter, it should
check whether the needed functionality is present. The typical way to do
this is (from the lm75 driver):

  static int lm75_detect(...)
  {
	(...)
	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
				     I2C_FUNC_SMBUS_WORD_DATA))
		goto exit;
	(...)
  }

Here, the lm75 driver checks if the adapter can do both SMBus byte data
and SMBus word data transactions. If not, then the driver won't work on
this adapter and there's no point in going on. If the check above is
successful, then the driver knows that it can call the following
functions: i2c_smbus_read_byte_data(), i2c_smbus_write_byte_data(),
i2c_smbus_read_word_data() and i2c_smbus_write_word_data(). As a rule of
thumb, the functionality constants you test for with
i2c_check_functionality() should match exactly the i2c_smbus_* functions
which you driver is calling.

Note that the check above doesn't tell whether the functionalities are
implemented in hardware by the underlying adapter or emulated in
software by i2c-core. Client drivers don't have to care about this, as
i2c-core will transparently implement SMBus transactions on top of I2C
adapters.


CHECKING THROUGH /DEV
---------------------

If you try to access an adapter from a userspace program, you will have
to use the /dev interface. You will still have to check whether the
functionality you need is supported, of course. This is done using
the I2C_FUNCS ioctl. An example, adapted from the i2cdetect program, is
below:

  int file;
  if (file = open("/dev/i2c-0", O_RDWR) < 0) {
	/* Some kind of error handling */
	exit(1);
  }
  if (ioctl(file, I2C_FUNCS, &funcs) < 0) {
	/* Some kind of error handling */
	exit(1);
  }
  if (!(funcs & I2C_FUNC_SMBUS_QUICK)) {
	/* Oops, the needed functionality (SMBus write_quick function) is
           not available! */
	exit(1);
  }
  /* Now it is safe to use the SMBus write_quick command */

系统管理总线 关键标记表

符号大小含义
S1 bit开始位(开始信号)
P1 bit停止位(停止信号)
Rd/Wr1 bit读/写 位,高电平(1)是读位,低电平(0)是写位
A, NA1 bitAccept and reverse accept bit. 按照I2C协议,为应答位,低电平有效
Addr7 bitI2C 7位地址。注意,可以像往常一样扩展它以获得一个10位的I2C地址。
Comm8 bit通常用于选择设备上的寄存器的数据字节,实际就是芯片片内寄存器地址
Data8 bit普通的数据字节
Count8 bit当为块操作时,为发送数据的字节数
由I2C设备发送的数据,与主机适配器发送的数据相反。

SMBus接口函数

1. SMBus Quick Command

命令功能:发送一个读写位给设备
操作时序:A Addr Rd/Wr [A] P
功能标记宏:I2C_FUNC_SMBUS_QUICK

2. SMBus接收字节函数:i2c_smbus_read_byte()

函数定义实现在linux-5.1.18\drivers\i2c\i2c-core-smbus.c

/**
 * i2c_smbus_read_byte - SMBus "receive byte" protocol
 * @client: Handle to slave device
 * This executes the SMBus "receive byte" protocol, returning negative errno
 * else the byte received from the device.
 */
s32 i2c_smbus_read_byte(const struct i2c_client *client)
{
	union i2c_smbus_data data;
	int status;
	status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
				I2C_SMBUS_READ, 0,
				I2C_SMBUS_BYTE, &data);
	return (status < 0) ? status : data.byte;
}
EXPORT_SYMBOL(i2c_smbus_read_byte);

函数功能:不指定设备寄存器从设备读取单个字节。该函数有如下两种应用场景:

  • 1.有些设备比较简单,寄存器资源很少,这个接口就足够了。
  • 2.对于其他相对复杂的设备,如果想读跟先前的SMBus操作中一样的寄存器,可以使用该接口。
    操作时序:S Addr Rd [A] [Data] NA P
    功能标记宏:I2C_FUNC_SMBUS_READ_BYTE

3. SMBus发送字节函数:i2c_smbus_write_byte()

函数定义实现在linux-5.1.18\drivers\i2c\i2c-core-smbus.c

/**
 * i2c_smbus_write_byte - SMBus "send byte" protocol
 * @client: Handle to slave device
 * @value: Byte to be sent
 * This executes the SMBus "send byte" protocol, returning negative errno
 * else zero on success.
 */
s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value)
{
	return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
	                      I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL);
}
EXPORT_SYMBOL(i2c_smbus_write_byte);

函数功能:发送单个字节给设备,跟i2c_smbus_read_byte()相反的功能
操作时序:S Addr Wr [A] Data [A] P
功能标记宏:I2C_FUNC_SMBUS_QUICK

4. SMBus读取字节函数:i2c_smbus_read_byte_data()

函数定义实现在linux-5.1.18\drivers\i2c\i2c-core-smbus.c

/**
 * i2c_smbus_read_byte_data - SMBus "read byte" protocol
 * @client: Handle to slave device
 * @command: Byte interpreted by slave
 * This executes the SMBus "read byte" protocol, returning negative errno
 * else a data byte received from the device.
 */
s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command)
{
	union i2c_smbus_data data;
	int status;

	status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
				I2C_SMBUS_READ, command,
				I2C_SMBUS_BYTE_DATA, &data);
	return (status < 0) ? status : data.byte;
}
EXPORT_SYMBOL(i2c_smbus_read_byte_data);

函数功能:从一个指定的寄存器中读取一个字节,该寄存器通过comm byte(芯片片内寄存器地址)指定
操作时序:S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] NA P
功能标记宏:I2C_FUNC_SMBUS_READ_BYTE_DATA

5. SMBus读取字(两字节)函数:i2c_smbus_read_word_data()

函数定义实现在linux-5.1.18\drivers\i2c\i2c-core-smbus.c

/**
 * i2c_smbus_read_word_data - SMBus "read word" protocol
 * @client: Handle to slave device
 * @command: Byte interpreted by slave
 *
 * This executes the SMBus "read word" protocol, returning negative errno
 * else a 16-bit unsigned "word" received from the device.
 */
s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command)
{
	union i2c_smbus_data data;
	int status;

	status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
				I2C_SMBUS_READ, command,
				I2C_SMBUS_WORD_DATA, &data);
	return (status < 0) ? status : data.word;
}
EXPORT_SYMBOL(i2c_smbus_read_word_data);

函数功能:跟i2c_smbus_read_byte_data()的功能很相似:从一个指定的寄存器中读取数据,该寄存器通过comm byte(芯片片内寄存器地址)指定,不同的是,读取的数据为一个字(两字节)。
操作时序:S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
功能标记宏:I2C_FUNC_SMBUS_READ_WORD_DATA

注意,有一个方便的函数i2c_smbus_read_word_swap可用于读取两个数据字节相反的位置(不符合SMBus,但非常流行)。
即操作时序为:S Addr Wr [A] Comm [A] S Addr Rd [A] [DataHigh] A [DataLow] NA P
以LM77温度传感器的一个例子佐证,见如下
在这里插入图片描述

6. SMBus写字节函数:i2c_smbus_write_byte_data()

函数定义实现在linux-5.1.18\drivers\i2c\i2c-core-smbus.c

/**
 * i2c_smbus_write_byte_data - SMBus "write byte" protocol
 * @client: Handle to slave device
 * @command: Byte interpreted by slave
 * @value: Byte being written
 * This executes the SMBus "write byte" protocol, returning negative errno
 * else zero on success.
 */
s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value)
{
	union i2c_smbus_data data;
	data.byte = value;
	return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
			      I2C_SMBUS_WRITE, command,
			      I2C_SMBUS_BYTE_DATA, &data);
}
EXPORT_SYMBOL(i2c_smbus_write_byte_data);

函数功能:与i2c_smbus_read_byte_data()函数的功能恰好相反:给一个设备写一个字节到指定的寄存器中,该寄存器通过comm byte(芯片片内寄存器地址)指定。
操作时序:S Addr Wr [A] Comm [A] Data [A] P
功能标记宏:I2C_FUNC_SMBUS_WRITE_BYTE_DATA
以向mma8653三轴加速度传感器写一个字节数据为例,如下图所示
在这里插入图片描述

7. SMBus写字(两字节)函数:i2c_smbus_write_word_data()

函数定义实现在linux-5.1.18\drivers\i2c\i2c-core-smbus.c

/**
 * i2c_smbus_write_word_data - SMBus "write word" protocol
 * @client: Handle to slave device
 * @command: Byte interpreted by slave
 * @value: 16-bit "word" being written
 * This executes the SMBus "write word" protocol, returning negative errno
 * else zero on success.
 */
s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command,
			      u16 value)
{
	union i2c_smbus_data data;
	data.word = value;
	return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
			      I2C_SMBUS_WRITE, command,
			      I2C_SMBUS_WORD_DATA, &data);
}
EXPORT_SYMBOL(i2c_smbus_write_word_data);

函数功能:该函数功能恰好与i2c_smbus_read_word_data()函数功能相反:向某个设备的特定寄存器写入2字节(16bit)的数据,该特定寄存器通过设备的片内寄存器地址来指定。
操作时序:S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] P
功能标记宏:I2C_FUNC_SMBUS_WRITE_WORD_DATA
同样,注意,有一个方便的函数i2c_smbus_write_word_swap可用于以相反的方向写两个字节数据(不符合SMBus,但非常流行)。
即操作时序为:S Addr Wr [A] Comm [A] DataHigh [A] DataLow [A] P

8. 字的写读过程调用:SMBus Process Call

过程调用第一个要求:先写后读,也就是说主设备向从设备写一个字,然后,从设备根据这个来自于主设备的字,在相同的偏移地址处作出相应的反应,发出一个字给主设备,让主设备读取。
过程调用第二个要求:“读写同址” 主设备发起repeat start来启动读的指令时,只需要对该从设备进行重复的寻址,而不需要再发起一个comm code(片内寄存器地址),因为这个片内寄存器地址在前面写的过程已经完成。所以主设备就不需要再告诉从设备读取数据的寄存器地址。所以称读写同址。
操作时序:S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A]
S Addr Rd [A] [DataLow] A [DataHigh] NA P
功能标记宏: I2C_FUNC_SMBUS_PROC_CALL
时序见下示意图
在这里插入图片描述

9. SMBus(Block Read)读块数据:i2c_smbus_read_block_data()

函数定义实现在linux-5.1.18\drivers\i2c\i2c-core-smbus.c

/**
 * i2c_smbus_read_block_data - SMBus "block read" protocol
 * @client: Handle to slave device
 * @command: Byte interpreted by slave
 * @values: Byte array into which data will be read; big enough to hold
 *	the data returned by the slave.  SMBus allows at most 32 bytes.
 * This executes the SMBus "block read" protocol, returning negative errno
 * else the number of data bytes in the slave's response.
 *
 * Note that using this function requires that the client's adapter support
 * the I2C_FUNC_SMBUS_READ_BLOCK_DATA functionality.  Not all adapter drivers
 * support this; its emulation through I2C messaging relies on a specific
 * mechanism (I2C_M_RECV_LEN) which may not be implemented.
 */
s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command,
			      u8 *values)
{
	union i2c_smbus_data data;
	int status;

	status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
				I2C_SMBUS_READ, command,
				I2C_SMBUS_BLOCK_DATA, &data);
	if (status)
		return status;

	memcpy(values, &data.block[1], data.block[0]);
	return data.block[0];
}
EXPORT_SYMBOL(i2c_smbus_read_block_data);

函数功能:从一个设备的指定偏移地址读取不超过32字节的数据,且读取数据的数量由count指定。
操作时序:S Addr Wr [A] Comm [A]
S Addr Rd [A] [Count] A [Data] A [Data] A … A [Data] NA P
功能标记宏: I2C_FUNC_SMBUS_READ_BLOCK_DATA

10. SMBus(Block Write)写块数据: i2c_smbus_write_block_data()

函数定义实现在linux-5.1.18\drivers\i2c\i2c-core-smbus.c

/**
 * i2c_smbus_write_block_data - SMBus "block write" protocol
 * @client: Handle to slave device
 * @command: Byte interpreted by slave
 * @length: Size of data block; SMBus allows at most 32 bytes
 * @values: Byte array which will be written.
 *
 * This executes the SMBus "block write" protocol, returning negative errno
 * else zero on success.
 */
s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command,
			       u8 length, const u8 *values)
{
	union i2c_smbus_data data;

	if (length > I2C_SMBUS_BLOCK_MAX)
		length = I2C_SMBUS_BLOCK_MAX;
	data.block[0] = length;
	memcpy(&data.block[1], values, length);
	return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
			      I2C_SMBUS_WRITE, command,
			      I2C_SMBUS_BLOCK_DATA, &data);
}
EXPORT_SYMBOL(i2c_smbus_write_block_data);

函数功能:与i2c_smbus_read_block_data() 函数功能恰恰相反,向某个设备从指定的偏移地址开始写最多32个字节的数据,数据的具体数量由Count指定。
操作时序:S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] … [A] Data [A] P
功能标记宏:I2C_FUNC_SMBUS_WRITE_BLOCK_DATA

11. 块数据的读写过程调用:SMBus Block Write - Block Read Process Call

同 8. 字的写读过程调用:块数据的写读过程调用也有“先写后读”和“读写同址”的要求
操作时序:S Addr Wr [A] Comm [A] Count [A] Data [A] …
S Addr Rd [A] [Count] A [Data] … A P
功能标记宏:I2C_FUNC_SMBUS_BLOCK_PROC_CALL

I2C块数据传输(I2C Block Transactions)

I2C块数据传输是被SMBus layer支持的,但是不属于SMBus协议的范畴。
I2C块数据传输没有数据大小的限制,但是SMBus块数据的传输有32字节大小的限制。

I2C读块数据(I2C Block Read):i2c_smbus_read_i2c_block_data()

函数定义实现在linux-5.1.18\drivers\i2c\i2c-core-smbus.c

/* Returns the number of read bytes */
s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command,
				  u8 length, u8 *values)
{
	union i2c_smbus_data data;
	int status;

	if (length > I2C_SMBUS_BLOCK_MAX)
		length = I2C_SMBUS_BLOCK_MAX;
	data.block[0] = length;
	status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
				I2C_SMBUS_READ, command,
				I2C_SMBUS_I2C_BLOCK_DATA, &data);
	if (status < 0)
		return status;

	memcpy(values, &data.block[1], data.block[0]);
	return data.block[0];
}
EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data);

函数功能:从某个设备指定的片内寄存器地址开始读块数据
函数参数:client:操作的从设备;command:片内寄存器偏移地址;length:要读块数据的长度;values:存放读取的块数据的内存。
操作时序:S Addr Wr [A] Comm [A]
S Addr Rd [A] [Data] A [Data] A … A [Data] NA P
功能标记宏:I2C_FUNC_SMBUS_READ_I2C_BLOCK

I2C写块数据(I2C Block Write)

s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command,
				   u8 length, const u8 *values)
{
	union i2c_smbus_data data;

	if (length > I2C_SMBUS_BLOCK_MAX)
		length = I2C_SMBUS_BLOCK_MAX;
	data.block[0] = length;
	memcpy(data.block + 1, values, length);
	return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
			      I2C_SMBUS_WRITE, command,
			      I2C_SMBUS_I2C_BLOCK_DATA, &data);
}
EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data);

函数功能:从某个设备指定的片内寄存器地址开始写块数据
函数参数:client:操作的从设备;command:片内寄存器偏移地址;length:要读块数据的长度;values:存放读取的块数据的内存。

i2c_smbus_xfer()

上面的一系列函数最终都是调用的i2c_smbus_xfer()函数,i2c_smbus_xfer()函数代码如下:

/**
 * i2c_smbus_xfer - execute SMBus protocol operations
 * @adapter: Handle to I2C bus
 * @addr: Address of SMBus slave on that bus
 * @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC)
 * @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE
 * @command: Byte interpreted by slave, for protocols which use such bytes
 * @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL
 * @data: Data to be read or written
 * This executes an SMBus protocol operation, and returns a negative
 * errno code else zero on success.
 */
s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
		   unsigned short flags, char read_write,
		   u8 command, int protocol, union i2c_smbus_data *data)
{
	s32 res;

	i2c_lock_bus(adapter, I2C_LOCK_SEGMENT);
	res = __i2c_smbus_xfer(adapter, addr, flags, read_write,
			       command, protocol, data);
	i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);

	return res;
}

s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
		     unsigned short flags, char read_write,
		     u8 command, int protocol, union i2c_smbus_data *data)
{
	unsigned long orig_jiffies;
	int try;
	s32 res;

	/* If enabled, the following two tracepoints are conditional on
	 * read_write and protocol.
	 */
	trace_smbus_write(adapter, addr, flags, read_write,
			  command, protocol, data);
	trace_smbus_read(adapter, addr, flags, read_write,
			 command, protocol);

	flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;

	if (adapter->algo->smbus_xfer) {
		/* Retry automatically on arbitration loss */
		orig_jiffies = jiffies;
		for (res = 0, try = 0; try <= adapter->retries; try++) {
			res = adapter->algo->smbus_xfer(adapter, addr, flags,
							read_write, command,
							protocol, data);
			if (res != -EAGAIN)
				break;
			if (time_after(jiffies,
				       orig_jiffies + adapter->timeout))
				break;
		}

		if (res != -EOPNOTSUPP || !adapter->algo->master_xfer)
			goto trace;
		/*
		 * Fall back to i2c_smbus_xfer_emulated if the adapter doesn't
		 * implement native support for the SMBus operation.
		 */
	}

	res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
				      command, protocol, data);

trace:
	/* If enabled, the reply tracepoint is conditional on read_write. */
	trace_smbus_reply(adapter, addr, flags, read_write,
			  command, protocol, data, res);
	trace_smbus_result(adapter, addr, flags, read_write,
			   command, protocol, res);

	return res;
}

  • 10
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值