18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Linux I2C core SMBus and SMBus emulation code
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This file contains the SMBus functions which are always included in the I2C
68c2ecf20Sopenharmony_ci * core because they can be emulated via I2C. SMBus specific extensions
78c2ecf20Sopenharmony_ci * (e.g. smbalert) are handled in a separate i2c-smbus module.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl>
108c2ecf20Sopenharmony_ci * SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and
118c2ecf20Sopenharmony_ci * Jean Delvare <jdelvare@suse.de>
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci#include <linux/device.h>
148c2ecf20Sopenharmony_ci#include <linux/err.h>
158c2ecf20Sopenharmony_ci#include <linux/i2c.h>
168c2ecf20Sopenharmony_ci#include <linux/i2c-smbus.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "i2c-core.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS
228c2ecf20Sopenharmony_ci#include <trace/events/smbus.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* The SMBus parts */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define POLY    (0x1070U << 3)
288c2ecf20Sopenharmony_cistatic u8 crc8(u16 data)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	int i;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++) {
338c2ecf20Sopenharmony_ci		if (data & 0x8000)
348c2ecf20Sopenharmony_ci			data = data ^ POLY;
358c2ecf20Sopenharmony_ci		data = data << 1;
368c2ecf20Sopenharmony_ci	}
378c2ecf20Sopenharmony_ci	return (u8)(data >> 8);
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/* Incremental CRC8 over count bytes in the array pointed to by p */
418c2ecf20Sopenharmony_cistatic u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	int i;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++)
468c2ecf20Sopenharmony_ci		crc = crc8((crc ^ p[i]) << 8);
478c2ecf20Sopenharmony_ci	return crc;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* Assume a 7-bit address, which is reasonable for SMBus */
518c2ecf20Sopenharmony_cistatic u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	/* The address will be sent first */
548c2ecf20Sopenharmony_ci	u8 addr = i2c_8bit_addr_from_msg(msg);
558c2ecf20Sopenharmony_ci	pec = i2c_smbus_pec(pec, &addr, 1);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* The data buffer follows */
588c2ecf20Sopenharmony_ci	return i2c_smbus_pec(pec, msg->buf, msg->len);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* Used for write only transactions */
628c2ecf20Sopenharmony_cistatic inline void i2c_smbus_add_pec(struct i2c_msg *msg)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	msg->buf[msg->len] = i2c_smbus_msg_pec(0, msg);
658c2ecf20Sopenharmony_ci	msg->len++;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* Return <0 on CRC error
698c2ecf20Sopenharmony_ci   If there was a write before this read (most cases) we need to take the
708c2ecf20Sopenharmony_ci   partial CRC from the write part into account.
718c2ecf20Sopenharmony_ci   Note that this function does modify the message (we need to decrease the
728c2ecf20Sopenharmony_ci   message length to hide the CRC byte from the caller). */
738c2ecf20Sopenharmony_cistatic int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	u8 rpec = msg->buf[--msg->len];
768c2ecf20Sopenharmony_ci	cpec = i2c_smbus_msg_pec(cpec, msg);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (rpec != cpec) {
798c2ecf20Sopenharmony_ci		pr_debug("Bad PEC 0x%02x vs. 0x%02x\n",
808c2ecf20Sopenharmony_ci			rpec, cpec);
818c2ecf20Sopenharmony_ci		return -EBADMSG;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci	return 0;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/**
878c2ecf20Sopenharmony_ci * i2c_smbus_read_byte - SMBus "receive byte" protocol
888c2ecf20Sopenharmony_ci * @client: Handle to slave device
898c2ecf20Sopenharmony_ci *
908c2ecf20Sopenharmony_ci * This executes the SMBus "receive byte" protocol, returning negative errno
918c2ecf20Sopenharmony_ci * else the byte received from the device.
928c2ecf20Sopenharmony_ci */
938c2ecf20Sopenharmony_cis32 i2c_smbus_read_byte(const struct i2c_client *client)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	union i2c_smbus_data data;
968c2ecf20Sopenharmony_ci	int status;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
998c2ecf20Sopenharmony_ci				I2C_SMBUS_READ, 0,
1008c2ecf20Sopenharmony_ci				I2C_SMBUS_BYTE, &data);
1018c2ecf20Sopenharmony_ci	return (status < 0) ? status : data.byte;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i2c_smbus_read_byte);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/**
1068c2ecf20Sopenharmony_ci * i2c_smbus_write_byte - SMBus "send byte" protocol
1078c2ecf20Sopenharmony_ci * @client: Handle to slave device
1088c2ecf20Sopenharmony_ci * @value: Byte to be sent
1098c2ecf20Sopenharmony_ci *
1108c2ecf20Sopenharmony_ci * This executes the SMBus "send byte" protocol, returning negative errno
1118c2ecf20Sopenharmony_ci * else zero on success.
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_cis32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
1168c2ecf20Sopenharmony_ci	                      I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i2c_smbus_write_byte);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/**
1218c2ecf20Sopenharmony_ci * i2c_smbus_read_byte_data - SMBus "read byte" protocol
1228c2ecf20Sopenharmony_ci * @client: Handle to slave device
1238c2ecf20Sopenharmony_ci * @command: Byte interpreted by slave
1248c2ecf20Sopenharmony_ci *
1258c2ecf20Sopenharmony_ci * This executes the SMBus "read byte" protocol, returning negative errno
1268c2ecf20Sopenharmony_ci * else a data byte received from the device.
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_cis32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	union i2c_smbus_data data;
1318c2ecf20Sopenharmony_ci	int status;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
1348c2ecf20Sopenharmony_ci				I2C_SMBUS_READ, command,
1358c2ecf20Sopenharmony_ci				I2C_SMBUS_BYTE_DATA, &data);
1368c2ecf20Sopenharmony_ci	return (status < 0) ? status : data.byte;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i2c_smbus_read_byte_data);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/**
1418c2ecf20Sopenharmony_ci * i2c_smbus_write_byte_data - SMBus "write byte" protocol
1428c2ecf20Sopenharmony_ci * @client: Handle to slave device
1438c2ecf20Sopenharmony_ci * @command: Byte interpreted by slave
1448c2ecf20Sopenharmony_ci * @value: Byte being written
1458c2ecf20Sopenharmony_ci *
1468c2ecf20Sopenharmony_ci * This executes the SMBus "write byte" protocol, returning negative errno
1478c2ecf20Sopenharmony_ci * else zero on success.
1488c2ecf20Sopenharmony_ci */
1498c2ecf20Sopenharmony_cis32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command,
1508c2ecf20Sopenharmony_ci			      u8 value)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	union i2c_smbus_data data;
1538c2ecf20Sopenharmony_ci	data.byte = value;
1548c2ecf20Sopenharmony_ci	return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
1558c2ecf20Sopenharmony_ci			      I2C_SMBUS_WRITE, command,
1568c2ecf20Sopenharmony_ci			      I2C_SMBUS_BYTE_DATA, &data);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i2c_smbus_write_byte_data);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/**
1618c2ecf20Sopenharmony_ci * i2c_smbus_read_word_data - SMBus "read word" protocol
1628c2ecf20Sopenharmony_ci * @client: Handle to slave device
1638c2ecf20Sopenharmony_ci * @command: Byte interpreted by slave
1648c2ecf20Sopenharmony_ci *
1658c2ecf20Sopenharmony_ci * This executes the SMBus "read word" protocol, returning negative errno
1668c2ecf20Sopenharmony_ci * else a 16-bit unsigned "word" received from the device.
1678c2ecf20Sopenharmony_ci */
1688c2ecf20Sopenharmony_cis32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	union i2c_smbus_data data;
1718c2ecf20Sopenharmony_ci	int status;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
1748c2ecf20Sopenharmony_ci				I2C_SMBUS_READ, command,
1758c2ecf20Sopenharmony_ci				I2C_SMBUS_WORD_DATA, &data);
1768c2ecf20Sopenharmony_ci	return (status < 0) ? status : data.word;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i2c_smbus_read_word_data);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci/**
1818c2ecf20Sopenharmony_ci * i2c_smbus_write_word_data - SMBus "write word" protocol
1828c2ecf20Sopenharmony_ci * @client: Handle to slave device
1838c2ecf20Sopenharmony_ci * @command: Byte interpreted by slave
1848c2ecf20Sopenharmony_ci * @value: 16-bit "word" being written
1858c2ecf20Sopenharmony_ci *
1868c2ecf20Sopenharmony_ci * This executes the SMBus "write word" protocol, returning negative errno
1878c2ecf20Sopenharmony_ci * else zero on success.
1888c2ecf20Sopenharmony_ci */
1898c2ecf20Sopenharmony_cis32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command,
1908c2ecf20Sopenharmony_ci			      u16 value)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	union i2c_smbus_data data;
1938c2ecf20Sopenharmony_ci	data.word = value;
1948c2ecf20Sopenharmony_ci	return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
1958c2ecf20Sopenharmony_ci			      I2C_SMBUS_WRITE, command,
1968c2ecf20Sopenharmony_ci			      I2C_SMBUS_WORD_DATA, &data);
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i2c_smbus_write_word_data);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci/**
2018c2ecf20Sopenharmony_ci * i2c_smbus_read_block_data - SMBus "block read" protocol
2028c2ecf20Sopenharmony_ci * @client: Handle to slave device
2038c2ecf20Sopenharmony_ci * @command: Byte interpreted by slave
2048c2ecf20Sopenharmony_ci * @values: Byte array into which data will be read; big enough to hold
2058c2ecf20Sopenharmony_ci *	the data returned by the slave.  SMBus allows at most 32 bytes.
2068c2ecf20Sopenharmony_ci *
2078c2ecf20Sopenharmony_ci * This executes the SMBus "block read" protocol, returning negative errno
2088c2ecf20Sopenharmony_ci * else the number of data bytes in the slave's response.
2098c2ecf20Sopenharmony_ci *
2108c2ecf20Sopenharmony_ci * Note that using this function requires that the client's adapter support
2118c2ecf20Sopenharmony_ci * the I2C_FUNC_SMBUS_READ_BLOCK_DATA functionality.  Not all adapter drivers
2128c2ecf20Sopenharmony_ci * support this; its emulation through I2C messaging relies on a specific
2138c2ecf20Sopenharmony_ci * mechanism (I2C_M_RECV_LEN) which may not be implemented.
2148c2ecf20Sopenharmony_ci */
2158c2ecf20Sopenharmony_cis32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command,
2168c2ecf20Sopenharmony_ci			      u8 *values)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	union i2c_smbus_data data;
2198c2ecf20Sopenharmony_ci	int status;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
2228c2ecf20Sopenharmony_ci				I2C_SMBUS_READ, command,
2238c2ecf20Sopenharmony_ci				I2C_SMBUS_BLOCK_DATA, &data);
2248c2ecf20Sopenharmony_ci	if (status)
2258c2ecf20Sopenharmony_ci		return status;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	memcpy(values, &data.block[1], data.block[0]);
2288c2ecf20Sopenharmony_ci	return data.block[0];
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i2c_smbus_read_block_data);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci/**
2338c2ecf20Sopenharmony_ci * i2c_smbus_write_block_data - SMBus "block write" protocol
2348c2ecf20Sopenharmony_ci * @client: Handle to slave device
2358c2ecf20Sopenharmony_ci * @command: Byte interpreted by slave
2368c2ecf20Sopenharmony_ci * @length: Size of data block; SMBus allows at most 32 bytes
2378c2ecf20Sopenharmony_ci * @values: Byte array which will be written.
2388c2ecf20Sopenharmony_ci *
2398c2ecf20Sopenharmony_ci * This executes the SMBus "block write" protocol, returning negative errno
2408c2ecf20Sopenharmony_ci * else zero on success.
2418c2ecf20Sopenharmony_ci */
2428c2ecf20Sopenharmony_cis32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command,
2438c2ecf20Sopenharmony_ci			       u8 length, const u8 *values)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	union i2c_smbus_data data;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (length > I2C_SMBUS_BLOCK_MAX)
2488c2ecf20Sopenharmony_ci		length = I2C_SMBUS_BLOCK_MAX;
2498c2ecf20Sopenharmony_ci	data.block[0] = length;
2508c2ecf20Sopenharmony_ci	memcpy(&data.block[1], values, length);
2518c2ecf20Sopenharmony_ci	return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
2528c2ecf20Sopenharmony_ci			      I2C_SMBUS_WRITE, command,
2538c2ecf20Sopenharmony_ci			      I2C_SMBUS_BLOCK_DATA, &data);
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i2c_smbus_write_block_data);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci/* Returns the number of read bytes */
2588c2ecf20Sopenharmony_cis32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command,
2598c2ecf20Sopenharmony_ci				  u8 length, u8 *values)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	union i2c_smbus_data data;
2628c2ecf20Sopenharmony_ci	int status;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (length > I2C_SMBUS_BLOCK_MAX)
2658c2ecf20Sopenharmony_ci		length = I2C_SMBUS_BLOCK_MAX;
2668c2ecf20Sopenharmony_ci	data.block[0] = length;
2678c2ecf20Sopenharmony_ci	status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
2688c2ecf20Sopenharmony_ci				I2C_SMBUS_READ, command,
2698c2ecf20Sopenharmony_ci				I2C_SMBUS_I2C_BLOCK_DATA, &data);
2708c2ecf20Sopenharmony_ci	if (status < 0)
2718c2ecf20Sopenharmony_ci		return status;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	memcpy(values, &data.block[1], data.block[0]);
2748c2ecf20Sopenharmony_ci	return data.block[0];
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i2c_smbus_read_i2c_block_data);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cis32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command,
2798c2ecf20Sopenharmony_ci				   u8 length, const u8 *values)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	union i2c_smbus_data data;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (length > I2C_SMBUS_BLOCK_MAX)
2848c2ecf20Sopenharmony_ci		length = I2C_SMBUS_BLOCK_MAX;
2858c2ecf20Sopenharmony_ci	data.block[0] = length;
2868c2ecf20Sopenharmony_ci	memcpy(data.block + 1, values, length);
2878c2ecf20Sopenharmony_ci	return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
2888c2ecf20Sopenharmony_ci			      I2C_SMBUS_WRITE, command,
2898c2ecf20Sopenharmony_ci			      I2C_SMBUS_I2C_BLOCK_DATA, &data);
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i2c_smbus_write_i2c_block_data);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic void i2c_smbus_try_get_dmabuf(struct i2c_msg *msg, u8 init_val)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	bool is_read = msg->flags & I2C_M_RD;
2968c2ecf20Sopenharmony_ci	unsigned char *dma_buf;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	dma_buf = kzalloc(I2C_SMBUS_BLOCK_MAX + (is_read ? 2 : 3), GFP_KERNEL);
2998c2ecf20Sopenharmony_ci	if (!dma_buf)
3008c2ecf20Sopenharmony_ci		return;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	msg->buf = dma_buf;
3038c2ecf20Sopenharmony_ci	msg->flags |= I2C_M_DMA_SAFE;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (init_val)
3068c2ecf20Sopenharmony_ci		msg->buf[0] = init_val;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci/*
3108c2ecf20Sopenharmony_ci * Simulate a SMBus command using the I2C protocol.
3118c2ecf20Sopenharmony_ci * No checking of parameters is done!
3128c2ecf20Sopenharmony_ci */
3138c2ecf20Sopenharmony_cistatic s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
3148c2ecf20Sopenharmony_ci				   unsigned short flags,
3158c2ecf20Sopenharmony_ci				   char read_write, u8 command, int size,
3168c2ecf20Sopenharmony_ci				   union i2c_smbus_data *data)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	/*
3198c2ecf20Sopenharmony_ci	 * So we need to generate a series of msgs. In the case of writing, we
3208c2ecf20Sopenharmony_ci	 * need to use only one message; when reading, we need two. We
3218c2ecf20Sopenharmony_ci	 * initialize most things with sane defaults, to keep the code below
3228c2ecf20Sopenharmony_ci	 * somewhat simpler.
3238c2ecf20Sopenharmony_ci	 */
3248c2ecf20Sopenharmony_ci	unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
3258c2ecf20Sopenharmony_ci	unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
3268c2ecf20Sopenharmony_ci	int num = read_write == I2C_SMBUS_READ ? 2 : 1;
3278c2ecf20Sopenharmony_ci	int i;
3288c2ecf20Sopenharmony_ci	u8 partial_pec = 0;
3298c2ecf20Sopenharmony_ci	int status;
3308c2ecf20Sopenharmony_ci	struct i2c_msg msg[2] = {
3318c2ecf20Sopenharmony_ci		{
3328c2ecf20Sopenharmony_ci			.addr = addr,
3338c2ecf20Sopenharmony_ci			.flags = flags,
3348c2ecf20Sopenharmony_ci			.len = 1,
3358c2ecf20Sopenharmony_ci			.buf = msgbuf0,
3368c2ecf20Sopenharmony_ci		}, {
3378c2ecf20Sopenharmony_ci			.addr = addr,
3388c2ecf20Sopenharmony_ci			.flags = flags | I2C_M_RD,
3398c2ecf20Sopenharmony_ci			.len = 0,
3408c2ecf20Sopenharmony_ci			.buf = msgbuf1,
3418c2ecf20Sopenharmony_ci		},
3428c2ecf20Sopenharmony_ci	};
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	msgbuf0[0] = command;
3458c2ecf20Sopenharmony_ci	switch (size) {
3468c2ecf20Sopenharmony_ci	case I2C_SMBUS_QUICK:
3478c2ecf20Sopenharmony_ci		msg[0].len = 0;
3488c2ecf20Sopenharmony_ci		/* Special case: The read/write field is used as data */
3498c2ecf20Sopenharmony_ci		msg[0].flags = flags | (read_write == I2C_SMBUS_READ ?
3508c2ecf20Sopenharmony_ci					I2C_M_RD : 0);
3518c2ecf20Sopenharmony_ci		num = 1;
3528c2ecf20Sopenharmony_ci		break;
3538c2ecf20Sopenharmony_ci	case I2C_SMBUS_BYTE:
3548c2ecf20Sopenharmony_ci		if (read_write == I2C_SMBUS_READ) {
3558c2ecf20Sopenharmony_ci			/* Special case: only a read! */
3568c2ecf20Sopenharmony_ci			msg[0].flags = I2C_M_RD | flags;
3578c2ecf20Sopenharmony_ci			num = 1;
3588c2ecf20Sopenharmony_ci		}
3598c2ecf20Sopenharmony_ci		break;
3608c2ecf20Sopenharmony_ci	case I2C_SMBUS_BYTE_DATA:
3618c2ecf20Sopenharmony_ci		if (read_write == I2C_SMBUS_READ)
3628c2ecf20Sopenharmony_ci			msg[1].len = 1;
3638c2ecf20Sopenharmony_ci		else {
3648c2ecf20Sopenharmony_ci			msg[0].len = 2;
3658c2ecf20Sopenharmony_ci			msgbuf0[1] = data->byte;
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci		break;
3688c2ecf20Sopenharmony_ci	case I2C_SMBUS_WORD_DATA:
3698c2ecf20Sopenharmony_ci		if (read_write == I2C_SMBUS_READ)
3708c2ecf20Sopenharmony_ci			msg[1].len = 2;
3718c2ecf20Sopenharmony_ci		else {
3728c2ecf20Sopenharmony_ci			msg[0].len = 3;
3738c2ecf20Sopenharmony_ci			msgbuf0[1] = data->word & 0xff;
3748c2ecf20Sopenharmony_ci			msgbuf0[2] = data->word >> 8;
3758c2ecf20Sopenharmony_ci		}
3768c2ecf20Sopenharmony_ci		break;
3778c2ecf20Sopenharmony_ci	case I2C_SMBUS_PROC_CALL:
3788c2ecf20Sopenharmony_ci		num = 2; /* Special case */
3798c2ecf20Sopenharmony_ci		read_write = I2C_SMBUS_READ;
3808c2ecf20Sopenharmony_ci		msg[0].len = 3;
3818c2ecf20Sopenharmony_ci		msg[1].len = 2;
3828c2ecf20Sopenharmony_ci		msgbuf0[1] = data->word & 0xff;
3838c2ecf20Sopenharmony_ci		msgbuf0[2] = data->word >> 8;
3848c2ecf20Sopenharmony_ci		break;
3858c2ecf20Sopenharmony_ci	case I2C_SMBUS_BLOCK_DATA:
3868c2ecf20Sopenharmony_ci		if (read_write == I2C_SMBUS_READ) {
3878c2ecf20Sopenharmony_ci			msg[1].flags |= I2C_M_RECV_LEN;
3888c2ecf20Sopenharmony_ci			msg[1].len = 1; /* block length will be added by
3898c2ecf20Sopenharmony_ci					   the underlying bus driver */
3908c2ecf20Sopenharmony_ci			i2c_smbus_try_get_dmabuf(&msg[1], 0);
3918c2ecf20Sopenharmony_ci		} else {
3928c2ecf20Sopenharmony_ci			msg[0].len = data->block[0] + 2;
3938c2ecf20Sopenharmony_ci			if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
3948c2ecf20Sopenharmony_ci				dev_err(&adapter->dev,
3958c2ecf20Sopenharmony_ci					"Invalid block write size %d\n",
3968c2ecf20Sopenharmony_ci					data->block[0]);
3978c2ecf20Sopenharmony_ci				return -EINVAL;
3988c2ecf20Sopenharmony_ci			}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci			i2c_smbus_try_get_dmabuf(&msg[0], command);
4018c2ecf20Sopenharmony_ci			for (i = 1; i < msg[0].len; i++)
4028c2ecf20Sopenharmony_ci				msg[0].buf[i] = data->block[i - 1];
4038c2ecf20Sopenharmony_ci		}
4048c2ecf20Sopenharmony_ci		break;
4058c2ecf20Sopenharmony_ci	case I2C_SMBUS_BLOCK_PROC_CALL:
4068c2ecf20Sopenharmony_ci		num = 2; /* Another special case */
4078c2ecf20Sopenharmony_ci		read_write = I2C_SMBUS_READ;
4088c2ecf20Sopenharmony_ci		if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
4098c2ecf20Sopenharmony_ci			dev_err(&adapter->dev,
4108c2ecf20Sopenharmony_ci				"Invalid block write size %d\n",
4118c2ecf20Sopenharmony_ci				data->block[0]);
4128c2ecf20Sopenharmony_ci			return -EINVAL;
4138c2ecf20Sopenharmony_ci		}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci		msg[0].len = data->block[0] + 2;
4168c2ecf20Sopenharmony_ci		i2c_smbus_try_get_dmabuf(&msg[0], command);
4178c2ecf20Sopenharmony_ci		for (i = 1; i < msg[0].len; i++)
4188c2ecf20Sopenharmony_ci			msg[0].buf[i] = data->block[i - 1];
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci		msg[1].flags |= I2C_M_RECV_LEN;
4218c2ecf20Sopenharmony_ci		msg[1].len = 1; /* block length will be added by
4228c2ecf20Sopenharmony_ci				   the underlying bus driver */
4238c2ecf20Sopenharmony_ci		i2c_smbus_try_get_dmabuf(&msg[1], 0);
4248c2ecf20Sopenharmony_ci		break;
4258c2ecf20Sopenharmony_ci	case I2C_SMBUS_I2C_BLOCK_DATA:
4268c2ecf20Sopenharmony_ci		if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
4278c2ecf20Sopenharmony_ci			dev_err(&adapter->dev, "Invalid block %s size %d\n",
4288c2ecf20Sopenharmony_ci				read_write == I2C_SMBUS_READ ? "read" : "write",
4298c2ecf20Sopenharmony_ci				data->block[0]);
4308c2ecf20Sopenharmony_ci			return -EINVAL;
4318c2ecf20Sopenharmony_ci		}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci		if (read_write == I2C_SMBUS_READ) {
4348c2ecf20Sopenharmony_ci			msg[1].len = data->block[0];
4358c2ecf20Sopenharmony_ci			i2c_smbus_try_get_dmabuf(&msg[1], 0);
4368c2ecf20Sopenharmony_ci		} else {
4378c2ecf20Sopenharmony_ci			msg[0].len = data->block[0] + 1;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci			i2c_smbus_try_get_dmabuf(&msg[0], command);
4408c2ecf20Sopenharmony_ci			for (i = 1; i <= data->block[0]; i++)
4418c2ecf20Sopenharmony_ci				msg[0].buf[i] = data->block[i];
4428c2ecf20Sopenharmony_ci		}
4438c2ecf20Sopenharmony_ci		break;
4448c2ecf20Sopenharmony_ci	default:
4458c2ecf20Sopenharmony_ci		dev_err(&adapter->dev, "Unsupported transaction %d\n", size);
4468c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK
4508c2ecf20Sopenharmony_ci				      && size != I2C_SMBUS_I2C_BLOCK_DATA);
4518c2ecf20Sopenharmony_ci	if (i) {
4528c2ecf20Sopenharmony_ci		/* Compute PEC if first message is a write */
4538c2ecf20Sopenharmony_ci		if (!(msg[0].flags & I2C_M_RD)) {
4548c2ecf20Sopenharmony_ci			if (num == 1) /* Write only */
4558c2ecf20Sopenharmony_ci				i2c_smbus_add_pec(&msg[0]);
4568c2ecf20Sopenharmony_ci			else /* Write followed by read */
4578c2ecf20Sopenharmony_ci				partial_pec = i2c_smbus_msg_pec(0, &msg[0]);
4588c2ecf20Sopenharmony_ci		}
4598c2ecf20Sopenharmony_ci		/* Ask for PEC if last message is a read */
4608c2ecf20Sopenharmony_ci		if (msg[num-1].flags & I2C_M_RD)
4618c2ecf20Sopenharmony_ci			msg[num-1].len++;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	status = __i2c_transfer(adapter, msg, num);
4658c2ecf20Sopenharmony_ci	if (status < 0)
4668c2ecf20Sopenharmony_ci		goto cleanup;
4678c2ecf20Sopenharmony_ci	if (status != num) {
4688c2ecf20Sopenharmony_ci		status = -EIO;
4698c2ecf20Sopenharmony_ci		goto cleanup;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci	status = 0;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	/* Check PEC if last message is a read */
4748c2ecf20Sopenharmony_ci	if (i && (msg[num-1].flags & I2C_M_RD)) {
4758c2ecf20Sopenharmony_ci		status = i2c_smbus_check_pec(partial_pec, &msg[num-1]);
4768c2ecf20Sopenharmony_ci		if (status < 0)
4778c2ecf20Sopenharmony_ci			goto cleanup;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (read_write == I2C_SMBUS_READ)
4818c2ecf20Sopenharmony_ci		switch (size) {
4828c2ecf20Sopenharmony_ci		case I2C_SMBUS_BYTE:
4838c2ecf20Sopenharmony_ci			data->byte = msgbuf0[0];
4848c2ecf20Sopenharmony_ci			break;
4858c2ecf20Sopenharmony_ci		case I2C_SMBUS_BYTE_DATA:
4868c2ecf20Sopenharmony_ci			data->byte = msgbuf1[0];
4878c2ecf20Sopenharmony_ci			break;
4888c2ecf20Sopenharmony_ci		case I2C_SMBUS_WORD_DATA:
4898c2ecf20Sopenharmony_ci		case I2C_SMBUS_PROC_CALL:
4908c2ecf20Sopenharmony_ci			data->word = msgbuf1[0] | (msgbuf1[1] << 8);
4918c2ecf20Sopenharmony_ci			break;
4928c2ecf20Sopenharmony_ci		case I2C_SMBUS_I2C_BLOCK_DATA:
4938c2ecf20Sopenharmony_ci			for (i = 0; i < data->block[0]; i++)
4948c2ecf20Sopenharmony_ci				data->block[i + 1] = msg[1].buf[i];
4958c2ecf20Sopenharmony_ci			break;
4968c2ecf20Sopenharmony_ci		case I2C_SMBUS_BLOCK_DATA:
4978c2ecf20Sopenharmony_ci		case I2C_SMBUS_BLOCK_PROC_CALL:
4988c2ecf20Sopenharmony_ci			if (msg[1].buf[0] > I2C_SMBUS_BLOCK_MAX) {
4998c2ecf20Sopenharmony_ci				dev_err(&adapter->dev,
5008c2ecf20Sopenharmony_ci					"Invalid block size returned: %d\n",
5018c2ecf20Sopenharmony_ci					msg[1].buf[0]);
5028c2ecf20Sopenharmony_ci				status = -EPROTO;
5038c2ecf20Sopenharmony_ci				goto cleanup;
5048c2ecf20Sopenharmony_ci			}
5058c2ecf20Sopenharmony_ci			for (i = 0; i < msg[1].buf[0] + 1; i++)
5068c2ecf20Sopenharmony_ci				data->block[i] = msg[1].buf[i];
5078c2ecf20Sopenharmony_ci			break;
5088c2ecf20Sopenharmony_ci		}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cicleanup:
5118c2ecf20Sopenharmony_ci	if (msg[0].flags & I2C_M_DMA_SAFE)
5128c2ecf20Sopenharmony_ci		kfree(msg[0].buf);
5138c2ecf20Sopenharmony_ci	if (msg[1].flags & I2C_M_DMA_SAFE)
5148c2ecf20Sopenharmony_ci		kfree(msg[1].buf);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	return status;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci/**
5208c2ecf20Sopenharmony_ci * i2c_smbus_xfer - execute SMBus protocol operations
5218c2ecf20Sopenharmony_ci * @adapter: Handle to I2C bus
5228c2ecf20Sopenharmony_ci * @addr: Address of SMBus slave on that bus
5238c2ecf20Sopenharmony_ci * @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC)
5248c2ecf20Sopenharmony_ci * @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE
5258c2ecf20Sopenharmony_ci * @command: Byte interpreted by slave, for protocols which use such bytes
5268c2ecf20Sopenharmony_ci * @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL
5278c2ecf20Sopenharmony_ci * @data: Data to be read or written
5288c2ecf20Sopenharmony_ci *
5298c2ecf20Sopenharmony_ci * This executes an SMBus protocol operation, and returns a negative
5308c2ecf20Sopenharmony_ci * errno code else zero on success.
5318c2ecf20Sopenharmony_ci */
5328c2ecf20Sopenharmony_cis32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
5338c2ecf20Sopenharmony_ci		   unsigned short flags, char read_write,
5348c2ecf20Sopenharmony_ci		   u8 command, int protocol, union i2c_smbus_data *data)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	s32 res;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	res = __i2c_lock_bus_helper(adapter);
5398c2ecf20Sopenharmony_ci	if (res)
5408c2ecf20Sopenharmony_ci		return res;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	res = __i2c_smbus_xfer(adapter, addr, flags, read_write,
5438c2ecf20Sopenharmony_ci			       command, protocol, data);
5448c2ecf20Sopenharmony_ci	i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	return res;
5478c2ecf20Sopenharmony_ci}
5488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i2c_smbus_xfer);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cis32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
5518c2ecf20Sopenharmony_ci		     unsigned short flags, char read_write,
5528c2ecf20Sopenharmony_ci		     u8 command, int protocol, union i2c_smbus_data *data)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	int (*xfer_func)(struct i2c_adapter *adap, u16 addr,
5558c2ecf20Sopenharmony_ci			 unsigned short flags, char read_write,
5568c2ecf20Sopenharmony_ci			 u8 command, int size, union i2c_smbus_data *data);
5578c2ecf20Sopenharmony_ci	unsigned long orig_jiffies;
5588c2ecf20Sopenharmony_ci	int try;
5598c2ecf20Sopenharmony_ci	s32 res;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	res = __i2c_check_suspended(adapter);
5628c2ecf20Sopenharmony_ci	if (res)
5638c2ecf20Sopenharmony_ci		return res;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	/* If enabled, the following two tracepoints are conditional on
5668c2ecf20Sopenharmony_ci	 * read_write and protocol.
5678c2ecf20Sopenharmony_ci	 */
5688c2ecf20Sopenharmony_ci	trace_smbus_write(adapter, addr, flags, read_write,
5698c2ecf20Sopenharmony_ci			  command, protocol, data);
5708c2ecf20Sopenharmony_ci	trace_smbus_read(adapter, addr, flags, read_write,
5718c2ecf20Sopenharmony_ci			 command, protocol);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	xfer_func = adapter->algo->smbus_xfer;
5768c2ecf20Sopenharmony_ci	if (i2c_in_atomic_xfer_mode()) {
5778c2ecf20Sopenharmony_ci		if (adapter->algo->smbus_xfer_atomic)
5788c2ecf20Sopenharmony_ci			xfer_func = adapter->algo->smbus_xfer_atomic;
5798c2ecf20Sopenharmony_ci		else if (adapter->algo->master_xfer_atomic)
5808c2ecf20Sopenharmony_ci			xfer_func = NULL; /* fallback to I2C emulation */
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	if (xfer_func) {
5848c2ecf20Sopenharmony_ci		/* Retry automatically on arbitration loss */
5858c2ecf20Sopenharmony_ci		orig_jiffies = jiffies;
5868c2ecf20Sopenharmony_ci		for (res = 0, try = 0; try <= adapter->retries; try++) {
5878c2ecf20Sopenharmony_ci			res = xfer_func(adapter, addr, flags, read_write,
5888c2ecf20Sopenharmony_ci					command, protocol, data);
5898c2ecf20Sopenharmony_ci			if (res != -EAGAIN)
5908c2ecf20Sopenharmony_ci				break;
5918c2ecf20Sopenharmony_ci			if (time_after(jiffies,
5928c2ecf20Sopenharmony_ci				       orig_jiffies + adapter->timeout))
5938c2ecf20Sopenharmony_ci				break;
5948c2ecf20Sopenharmony_ci		}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci		if (res != -EOPNOTSUPP || !adapter->algo->master_xfer)
5978c2ecf20Sopenharmony_ci			goto trace;
5988c2ecf20Sopenharmony_ci		/*
5998c2ecf20Sopenharmony_ci		 * Fall back to i2c_smbus_xfer_emulated if the adapter doesn't
6008c2ecf20Sopenharmony_ci		 * implement native support for the SMBus operation.
6018c2ecf20Sopenharmony_ci		 */
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
6058c2ecf20Sopenharmony_ci				      command, protocol, data);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_citrace:
6088c2ecf20Sopenharmony_ci	/* If enabled, the reply tracepoint is conditional on read_write. */
6098c2ecf20Sopenharmony_ci	trace_smbus_reply(adapter, addr, flags, read_write,
6108c2ecf20Sopenharmony_ci			  command, protocol, data, res);
6118c2ecf20Sopenharmony_ci	trace_smbus_result(adapter, addr, flags, read_write,
6128c2ecf20Sopenharmony_ci			   command, protocol, res);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	return res;
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__i2c_smbus_xfer);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci/**
6198c2ecf20Sopenharmony_ci * i2c_smbus_read_i2c_block_data_or_emulated - read block or emulate
6208c2ecf20Sopenharmony_ci * @client: Handle to slave device
6218c2ecf20Sopenharmony_ci * @command: Byte interpreted by slave
6228c2ecf20Sopenharmony_ci * @length: Size of data block; SMBus allows at most I2C_SMBUS_BLOCK_MAX bytes
6238c2ecf20Sopenharmony_ci * @values: Byte array into which data will be read; big enough to hold
6248c2ecf20Sopenharmony_ci *	the data returned by the slave.  SMBus allows at most
6258c2ecf20Sopenharmony_ci *	I2C_SMBUS_BLOCK_MAX bytes.
6268c2ecf20Sopenharmony_ci *
6278c2ecf20Sopenharmony_ci * This executes the SMBus "block read" protocol if supported by the adapter.
6288c2ecf20Sopenharmony_ci * If block read is not supported, it emulates it using either word or byte
6298c2ecf20Sopenharmony_ci * read protocols depending on availability.
6308c2ecf20Sopenharmony_ci *
6318c2ecf20Sopenharmony_ci * The addresses of the I2C slave device that are accessed with this function
6328c2ecf20Sopenharmony_ci * must be mapped to a linear region, so that a block read will have the same
6338c2ecf20Sopenharmony_ci * effect as a byte read. Before using this function you must double-check
6348c2ecf20Sopenharmony_ci * if the I2C slave does support exchanging a block transfer with a byte
6358c2ecf20Sopenharmony_ci * transfer.
6368c2ecf20Sopenharmony_ci */
6378c2ecf20Sopenharmony_cis32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client,
6388c2ecf20Sopenharmony_ci					      u8 command, u8 length, u8 *values)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	u8 i = 0;
6418c2ecf20Sopenharmony_ci	int status;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	if (length > I2C_SMBUS_BLOCK_MAX)
6448c2ecf20Sopenharmony_ci		length = I2C_SMBUS_BLOCK_MAX;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK))
6478c2ecf20Sopenharmony_ci		return i2c_smbus_read_i2c_block_data(client, command, length, values);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA))
6508c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) {
6538c2ecf20Sopenharmony_ci		while ((i + 2) <= length) {
6548c2ecf20Sopenharmony_ci			status = i2c_smbus_read_word_data(client, command + i);
6558c2ecf20Sopenharmony_ci			if (status < 0)
6568c2ecf20Sopenharmony_ci				return status;
6578c2ecf20Sopenharmony_ci			values[i] = status & 0xff;
6588c2ecf20Sopenharmony_ci			values[i + 1] = status >> 8;
6598c2ecf20Sopenharmony_ci			i += 2;
6608c2ecf20Sopenharmony_ci		}
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	while (i < length) {
6648c2ecf20Sopenharmony_ci		status = i2c_smbus_read_byte_data(client, command + i);
6658c2ecf20Sopenharmony_ci		if (status < 0)
6668c2ecf20Sopenharmony_ci			return status;
6678c2ecf20Sopenharmony_ci		values[i] = status;
6688c2ecf20Sopenharmony_ci		i++;
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	return i;
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i2c_smbus_read_i2c_block_data_or_emulated);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci/**
6768c2ecf20Sopenharmony_ci * i2c_new_smbus_alert_device - get ara client for SMBus alert support
6778c2ecf20Sopenharmony_ci * @adapter: the target adapter
6788c2ecf20Sopenharmony_ci * @setup: setup data for the SMBus alert handler
6798c2ecf20Sopenharmony_ci * Context: can sleep
6808c2ecf20Sopenharmony_ci *
6818c2ecf20Sopenharmony_ci * Setup handling of the SMBus alert protocol on a given I2C bus segment.
6828c2ecf20Sopenharmony_ci *
6838c2ecf20Sopenharmony_ci * Handling can be done either through our IRQ handler, or by the
6848c2ecf20Sopenharmony_ci * adapter (from its handler, periodic polling, or whatever).
6858c2ecf20Sopenharmony_ci *
6868c2ecf20Sopenharmony_ci * This returns the ara client, which should be saved for later use with
6878c2ecf20Sopenharmony_ci * i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or an
6888c2ecf20Sopenharmony_ci * ERRPTR to indicate an error.
6898c2ecf20Sopenharmony_ci */
6908c2ecf20Sopenharmony_cistruct i2c_client *i2c_new_smbus_alert_device(struct i2c_adapter *adapter,
6918c2ecf20Sopenharmony_ci					      struct i2c_smbus_alert_setup *setup)
6928c2ecf20Sopenharmony_ci{
6938c2ecf20Sopenharmony_ci	struct i2c_board_info ara_board_info = {
6948c2ecf20Sopenharmony_ci		I2C_BOARD_INFO("smbus_alert", 0x0c),
6958c2ecf20Sopenharmony_ci		.platform_data = setup,
6968c2ecf20Sopenharmony_ci	};
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	return i2c_new_client_device(adapter, &ara_board_info);
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_new_smbus_alert_device);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF)
7038c2ecf20Sopenharmony_ciint of_i2c_setup_smbus_alert(struct i2c_adapter *adapter)
7048c2ecf20Sopenharmony_ci{
7058c2ecf20Sopenharmony_ci	int irq;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	irq = of_property_match_string(adapter->dev.of_node, "interrupt-names",
7088c2ecf20Sopenharmony_ci				       "smbus_alert");
7098c2ecf20Sopenharmony_ci	if (irq == -EINVAL || irq == -ENODATA)
7108c2ecf20Sopenharmony_ci		return 0;
7118c2ecf20Sopenharmony_ci	else if (irq < 0)
7128c2ecf20Sopenharmony_ci		return irq;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(i2c_new_smbus_alert_device(adapter, NULL));
7158c2ecf20Sopenharmony_ci}
7168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_i2c_setup_smbus_alert);
7178c2ecf20Sopenharmony_ci#endif
718