diff options
Diffstat (limited to 'drivers/i2c/i2c-core.c')
| -rw-r--r-- | drivers/i2c/i2c-core.c | 140 | 
1 files changed, 115 insertions, 25 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 29d3f045a2b..7c7f4b856ba 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -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> @@ -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, @@ -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)) @@ -248,17 +268,17 @@ static int i2c_device_probe(struct device *dev)  	driver = to_i2c_driver(dev->driver);  	if (!driver->probe || !driver->id_table)  		return -ENODEV; -	client->driver = driver; +  	if (!device_can_wakeup(&client->dev))  		device_init_wakeup(&client->dev,  					client->flags & I2C_CLIENT_WAKE);  	dev_dbg(dev, "probe\n"); +	acpi_dev_pm_attach(&client->dev, true);  	status = driver->probe(client, i2c_match_id(driver->id_table, client)); -	if (status) { -		client->driver = NULL; -		i2c_set_clientdata(client, NULL); -	} +	if (status) +		acpi_dev_pm_detach(&client->dev, true); +  	return status;  } @@ -266,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; @@ -275,14 +295,9 @@ 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) { -		client->driver = NULL; -		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);  } @@ -615,6 +636,22 @@ void i2c_unlock_adapter(struct i2c_adapter *adapter)  }  EXPORT_SYMBOL_GPL(i2c_unlock_adapter); +static void i2c_dev_set_name(struct i2c_adapter *adap, +			     struct i2c_client *client) +{ +	struct acpi_device *adev = ACPI_COMPANION(&client->dev); + +	if (adev) { +		dev_set_name(&client->dev, "i2c-%s", acpi_dev_name(adev)); +		return; +	} + +	/* For 10-bit clients, add an arbitrary offset to avoid collisions */ +	dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), +		     client->addr | ((client->flags & I2C_CLIENT_TEN) +				     ? 0xa000 : 0)); +} +  /**   * i2c_new_device - instantiate an i2c device   * @adap: the adapter managing the device @@ -671,12 +708,9 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)  	client->dev.bus = &i2c_bus_type;  	client->dev.type = &i2c_client_type;  	client->dev.of_node = info->of_node; -	ACPI_HANDLE_SET(&client->dev, info->acpi_node.handle); +	ACPI_COMPANION_SET(&client->dev, info->acpi_node.companion); -	/* For 10-bit clients, add an arbitrary offset to avoid collisions */ -	dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), -		     client->addr | ((client->flags & I2C_CLIENT_TEN) -				     ? 0xa000 : 0)); +	i2c_dev_set_name(adap, client);  	status = device_register(&client->dev);  	if (status)  		goto out_err; @@ -1100,7 +1134,7 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,  		return AE_OK;  	memset(&info, 0, sizeof(info)); -	info.acpi_node.handle = handle; +	info.acpi_node.companion = adev;  	info.irq = -1;  	INIT_LIST_HEAD(&resource_list); @@ -1111,8 +1145,10 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,  	if (ret < 0 || !info.addr)  		return AE_OK; +	adev->power.flags.ignore_parent = true;  	strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type));  	if (!i2c_new_device(adapter, &info)) { +		adev->power.flags.ignore_parent = false;  		dev_err(&adapter->dev,  			"failed to add I2C device %s from ACPI\n",  			dev_name(&adev->dev)); @@ -1134,6 +1170,9 @@ static void acpi_i2c_register_devices(struct i2c_adapter *adap)  	acpi_handle handle;  	acpi_status status; +	if (!adap->dev.parent) +		return; +  	handle = ACPI_HANDLE(adap->dev.parent);  	if (!handle)  		return; @@ -1606,9 +1645,14 @@ static int i2c_cmd(struct device *dev, void *_arg)  {  	struct i2c_client	*client = i2c_verify_client(dev);  	struct i2c_cmd_arg	*arg = _arg; +	struct i2c_driver	*driver; + +	if (!client || !client->dev.driver) +		return 0; -	if (client && client->driver && client->driver->command) -		client->driver->command(client, arg->cmd, arg->arg); +	driver = to_i2c_driver(client->dev.driver); +	if (driver->command) +		driver->command(client, arg->cmd, arg->arg);  	return 0;  } @@ -1657,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 @@ -1687,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++) { @@ -1697,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); @@ -1912,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); @@ -2492,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) { @@ -2512,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);  | 
