summary refs log tree commit diff
path: root/drivers/i2c/i2c-core-smbus.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-02-04 10:57:43 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2018-02-04 10:57:43 -0800
commit4141cf676b9e345d3ddeb1710dd3156a09c50244 (patch)
tree4b2b6249e99efdd47eafa5780f106d593f64708b /drivers/i2c/i2c-core-smbus.c
parent3462ac57033e79a87dbae2497773f22b9c536fbc (diff)
parente38c85644e11c6dc5a39305c96b617f63403423d (diff)
downloadlinux-4141cf676b9e345d3ddeb1710dd3156a09c50244.tar.gz
Merge branch 'i2c/for-4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang:
 "I2C has the following changes for you:

   - new flag to mark DMA safe buffers in i2c_msg. Also, some
     infrastructure around it. And docs.

   - huge refactoring of the at24 driver led by the new maintainer
     Bartosz

   - update I2C bus recovery to send STOP after recovery

   - conversion from gpio to gpiod for I2C bus recovery

   - adding a fault-injector to the i2c-gpio driver

   - lots of small driver improvements, and bigger ones to
     i2c-sh_mobile"

* 'i2c/for-4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (99 commits)
  i2c: mv64xxx: Add myself as maintainer for this driver
  i2c: mv64xxx: Fix clock resource by adding an optional bus clock
  i2c: mv64xxx: Remove useless test before clk_disable_unprepare
  i2c: mxs: use true and false for boolean values
  i2c: meson: update doc description to fix build warnings
  i2c: meson: add configurable divider factors
  dt-bindings: i2c: update documentation for the Meson-AXG
  i2c: imx-lpi2c: add runtime pm support
  i2c: rcar: fix some trivial typos in comments
  i2c: davinci: fix the cpufreq transition
  i2c: rk3x: add proper kerneldoc header
  i2c: rk3x: account for const type of of_device_id.data
  i2c: acorn: remove outdated path from file header
  i2c: acorn: add MODULE_LICENSE tag
  i2c: rcar: implement bus recovery
  i2c: send STOP after successful bus recovery
  i2c: ensure SDA is released in recovery if SDA is controllable
  i2c: add 'set_sda' to bus_recovery_info
  i2c: add identifier in declarations for i2c_bus_recovery
  i2c: make kerneldoc about bus recovery more precise
  ...
Diffstat (limited to 'drivers/i2c/i2c-core-smbus.c')
-rw-r--r--drivers/i2c/i2c-core-smbus.c45
1 files changed, 39 insertions, 6 deletions
diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c
index a1082c04ac5c..59d5cf376f6a 100644
--- a/drivers/i2c/i2c-core-smbus.c
+++ b/drivers/i2c/i2c-core-smbus.c
@@ -18,6 +18,7 @@
 #include <linux/err.h>
 #include <linux/i2c.h>
 #include <linux/i2c-smbus.h>
+#include <linux/slab.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/smbus.h>
@@ -291,6 +292,22 @@ s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command,
 }
 EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data);
 
+static void i2c_smbus_try_get_dmabuf(struct i2c_msg *msg, u8 init_val)
+{
+	bool is_read = msg->flags & I2C_M_RD;
+	unsigned char *dma_buf;
+
+	dma_buf = kzalloc(I2C_SMBUS_BLOCK_MAX + (is_read ? 2 : 3), GFP_KERNEL);
+	if (!dma_buf)
+		return;
+
+	msg->buf = dma_buf;
+	msg->flags |= I2C_M_DMA_SAFE;
+
+	if (init_val)
+		msg->buf[0] = init_val;
+}
+
 /* Simulate a SMBus command using the i2c protocol
    No checking of parameters is done!  */
 static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
@@ -368,6 +385,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
 			msg[1].flags |= I2C_M_RECV_LEN;
 			msg[1].len = 1; /* block length will be added by
 					   the underlying bus driver */
+			i2c_smbus_try_get_dmabuf(&msg[1], 0);
 		} else {
 			msg[0].len = data->block[0] + 2;
 			if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
@@ -376,8 +394,10 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
 					data->block[0]);
 				return -EINVAL;
 			}
+
+			i2c_smbus_try_get_dmabuf(&msg[0], command);
 			for (i = 1; i < msg[0].len; i++)
-				msgbuf0[i] = data->block[i-1];
+				msg[0].buf[i] = data->block[i - 1];
 		}
 		break;
 	case I2C_SMBUS_BLOCK_PROC_CALL:
@@ -389,12 +409,16 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
 				data->block[0]);
 			return -EINVAL;
 		}
+
 		msg[0].len = data->block[0] + 2;
+		i2c_smbus_try_get_dmabuf(&msg[0], command);
 		for (i = 1; i < msg[0].len; i++)
-			msgbuf0[i] = data->block[i-1];
+			msg[0].buf[i] = data->block[i - 1];
+
 		msg[1].flags |= I2C_M_RECV_LEN;
 		msg[1].len = 1; /* block length will be added by
 				   the underlying bus driver */
+		i2c_smbus_try_get_dmabuf(&msg[1], 0);
 		break;
 	case I2C_SMBUS_I2C_BLOCK_DATA:
 		if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
@@ -406,10 +430,13 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
 
 		if (read_write == I2C_SMBUS_READ) {
 			msg[1].len = data->block[0];
+			i2c_smbus_try_get_dmabuf(&msg[1], 0);
 		} else {
 			msg[0].len = data->block[0] + 1;
+
+			i2c_smbus_try_get_dmabuf(&msg[0], command);
 			for (i = 1; i <= data->block[0]; i++)
-				msgbuf0[i] = data->block[i];
+				msg[0].buf[i] = data->block[i];
 		}
 		break;
 	default:
@@ -457,14 +484,20 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
 			break;
 		case I2C_SMBUS_I2C_BLOCK_DATA:
 			for (i = 0; i < data->block[0]; i++)
-				data->block[i+1] = msgbuf1[i];
+				data->block[i + 1] = msg[1].buf[i];
 			break;
 		case I2C_SMBUS_BLOCK_DATA:
 		case I2C_SMBUS_BLOCK_PROC_CALL:
-			for (i = 0; i < msgbuf1[0] + 1; i++)
-				data->block[i] = msgbuf1[i];
+			for (i = 0; i < msg[1].buf[0] + 1; i++)
+				data->block[i] = msg[1].buf[i];
 			break;
 		}
+
+	if (msg[0].flags & I2C_M_DMA_SAFE)
+		kfree(msg[0].buf);
+	if (msg[1].flags & I2C_M_DMA_SAFE)
+		kfree(msg[1].buf);
+
 	return 0;
 }