Merge branch 'perf-core-for-mingo' into perf/urgent
[firefly-linux-kernel-4.4.55.git] / drivers / i2c / i2c-core.c
index d74c0b34248ea6c38472cc401571d8f519844c4d..7c7f4b856badaea86063483e3b37c49485e411d3 100644 (file)
@@ -21,7 +21,7 @@
 /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>.
    All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl>
    SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and
-   Jean Delvare <khali@linux-fr.org>
+   Jean Delvare <jdelvare@suse.de>
    Mux support by Rodolfo Giometti <giometti@enneenne.com> and
    Michael Lawnick <michael.lawnick.ext@nsn.com>
    OF support is copyright (c) 2008 Jochen Friedrich <jochen@scram.de>
 #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,
@@ -104,6 +119,11 @@ static int i2c_device_match(struct device *dev, struct device_driver *drv)
 static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
        struct i2c_client       *client = to_i2c_client(dev);
+       int rc;
+
+       rc = acpi_device_uevent_modalias(dev, env);
+       if (rc != -ENODEV)
+               return rc;
 
        if (add_uevent_var(env, "MODALIAS=%s%s",
                           I2C_MODULE_PREFIX, client->name))
@@ -256,10 +276,9 @@ static int i2c_device_probe(struct device *dev)
 
        acpi_dev_pm_attach(&client->dev, true);
        status = driver->probe(client, i2c_match_id(driver->id_table, client));
-       if (status) {
-               i2c_set_clientdata(client, NULL);
+       if (status)
                acpi_dev_pm_detach(&client->dev, true);
-       }
+
        return status;
 }
 
@@ -267,7 +286,7 @@ static int i2c_device_remove(struct device *dev)
 {
        struct i2c_client       *client = i2c_verify_client(dev);
        struct i2c_driver       *driver;
-       int                     status;
+       int status = 0;
 
        if (!client || !dev->driver)
                return 0;
@@ -276,12 +295,8 @@ static int i2c_device_remove(struct device *dev)
        if (driver->remove) {
                dev_dbg(dev, "remove\n");
                status = driver->remove(client);
-       } else {
-               dev->driver = NULL;
-               status = 0;
        }
-       if (status == 0)
-               i2c_set_clientdata(client, NULL);
+
        acpi_dev_pm_detach(&client->dev, true);
        return status;
 }
@@ -409,6 +424,12 @@ static ssize_t
 show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct i2c_client *client = to_i2c_client(dev);
+       int len;
+
+       len = acpi_device_modalias(dev, buf, PAGE_SIZE -1);
+       if (len != -ENODEV)
+               return len;
+
        return sprintf(buf, "%s%s\n", I2C_MODULE_PREFIX, client->name);
 }
 
@@ -1680,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
@@ -1710,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++) {
@@ -1720,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);
@@ -1935,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);
@@ -2515,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) {
@@ -2535,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);