aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c/i2c-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/i2c-core.c')
-rw-r--r--drivers/i2c/i2c-core.c67
1 files changed, 64 insertions, 3 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 5fb80b8962a2..7c7f4b856bad 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -48,10 +48,13 @@
#include <linux/rwsem.h>
#include <linux/pm_runtime.h>
#include <linux/acpi.h>
+#include <linux/jump_label.h>
#include <asm/uaccess.h>
#include "i2c-core.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/i2c.h>
/* core_lock protects i2c_adapter_idr, and guarantees
that device detection, deletion of detected devices, and attach_adapter
@@ -62,6 +65,18 @@ static DEFINE_IDR(i2c_adapter_idr);
static struct device_type i2c_client_type;
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
+static struct static_key i2c_trace_msg = STATIC_KEY_INIT_FALSE;
+
+void i2c_transfer_trace_reg(void)
+{
+ static_key_slow_inc(&i2c_trace_msg);
+}
+
+void i2c_transfer_trace_unreg(void)
+{
+ static_key_slow_dec(&i2c_trace_msg);
+}
+
/* ------------------------------------------------------------------------- */
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
@@ -1686,6 +1701,7 @@ static void __exit i2c_exit(void)
class_compat_unregister(i2c_adapter_compat_class);
#endif
bus_unregister(&i2c_bus_type);
+ tracepoint_synchronize_unregister();
}
/* We must initialize early, because some subsystems register i2c drivers
@@ -1716,6 +1732,19 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
unsigned long orig_jiffies;
int ret, try;
+ /* i2c_trace_msg gets enabled when tracepoint i2c_transfer gets
+ * enabled. This is an efficient way of keeping the for-loop from
+ * being executed when not needed.
+ */
+ if (static_key_false(&i2c_trace_msg)) {
+ int i;
+ for (i = 0; i < num; i++)
+ if (msgs[i].flags & I2C_M_RD)
+ trace_i2c_read(adap, &msgs[i], i);
+ else
+ trace_i2c_write(adap, &msgs[i], i);
+ }
+
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
@@ -1726,6 +1755,14 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
break;
}
+ if (static_key_false(&i2c_trace_msg)) {
+ int i;
+ for (i = 0; i < ret; i++)
+ if (msgs[i].flags & I2C_M_RD)
+ trace_i2c_reply(adap, &msgs[i], i);
+ trace_i2c_result(adap, i, ret);
+ }
+
return ret;
}
EXPORT_SYMBOL(__i2c_transfer);
@@ -1941,6 +1978,13 @@ static int i2c_detect_address(struct i2c_client *temp_client,
struct i2c_client *client;
/* Detection succeeded, instantiate the device */
+ if (adapter->class & I2C_CLASS_DEPRECATED)
+ dev_warn(&adapter->dev,
+ "This adapter will soon drop class based instantiation of devices. "
+ "Please make sure client 0x%02x gets instantiated by other means. "
+ "Check 'Documentation/i2c/instantiating-devices' for details.\n",
+ info.addr);
+
dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n",
info.type, info.addr);
client = i2c_new_device(adapter, &info);
@@ -2521,6 +2565,14 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
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) {
@@ -2541,15 +2593,24 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
i2c_unlock_adapter(adapter);
if (res != -EOPNOTSUPP || !adapter->algo->master_xfer)
- return res;
+ goto trace;
/*
* Fall back to i2c_smbus_xfer_emulated if the adapter doesn't
* implement native support for the SMBus operation.
*/
}
- return i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
- command, protocol, data);
+ 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);
+ trace_smbus_result(adapter, addr, flags, read_write,
+ command, protocol, res);
+
+ return res;
}
EXPORT_SYMBOL(i2c_smbus_xfer);