diff options
Diffstat (limited to 'drivers/i2c/i2c-core.c')
| -rw-r--r-- | drivers/i2c/i2c-core.c | 168 | 
1 files changed, 127 insertions, 41 deletions
| diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 1cca2631e5b..6649176de94 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -20,7 +20,9 @@  /* 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 <khali@linux-fr.org> +   Mux support by Rodolfo Giometti <giometti@enneenne.com> and +   Michael Lawnick <michael.lawnick.ext@nsn.com> */  #include <linux/module.h>  #include <linux/kernel.h> @@ -30,6 +32,8 @@  #include <linux/init.h>  #include <linux/idr.h>  #include <linux/mutex.h> +#include <linux/of_i2c.h> +#include <linux/of_device.h>  #include <linux/completion.h>  #include <linux/hardirq.h>  #include <linux/irqflags.h> @@ -70,6 +74,10 @@ static int i2c_device_match(struct device *dev, struct device_driver *drv)  	if (!client)  		return 0; +	/* Attempt an OF style match */ +	if (of_driver_match_device(dev, drv)) +		return 1; +  	driver = to_i2c_driver(drv);  	/* match on an id table if there is one */  	if (driver->id_table) @@ -417,11 +425,87 @@ static int __i2c_check_addr_busy(struct device *dev, void *addrp)  	return 0;  } +/* walk up mux tree */ +static int i2c_check_mux_parents(struct i2c_adapter *adapter, int addr) +{ +	int result; + +	result = device_for_each_child(&adapter->dev, &addr, +					__i2c_check_addr_busy); + +	if (!result && i2c_parent_is_i2c_adapter(adapter)) +		result = i2c_check_mux_parents( +				    to_i2c_adapter(adapter->dev.parent), addr); + +	return result; +} + +/* recurse down mux tree */ +static int i2c_check_mux_children(struct device *dev, void *addrp) +{ +	int result; + +	if (dev->type == &i2c_adapter_type) +		result = device_for_each_child(dev, addrp, +						i2c_check_mux_children); +	else +		result = __i2c_check_addr_busy(dev, addrp); + +	return result; +} +  static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr)  { -	return device_for_each_child(&adapter->dev, &addr, -				     __i2c_check_addr_busy); +	int result = 0; + +	if (i2c_parent_is_i2c_adapter(adapter)) +		result = i2c_check_mux_parents( +				    to_i2c_adapter(adapter->dev.parent), addr); + +	if (!result) +		result = device_for_each_child(&adapter->dev, &addr, +						i2c_check_mux_children); + +	return result; +} + +/** + * i2c_lock_adapter - Get exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + */ +void i2c_lock_adapter(struct i2c_adapter *adapter) +{ +	if (i2c_parent_is_i2c_adapter(adapter)) +		i2c_lock_adapter(to_i2c_adapter(adapter->dev.parent)); +	else +		rt_mutex_lock(&adapter->bus_lock); +} +EXPORT_SYMBOL_GPL(i2c_lock_adapter); + +/** + * i2c_trylock_adapter - Try to get exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + */ +static int i2c_trylock_adapter(struct i2c_adapter *adapter) +{ +	if (i2c_parent_is_i2c_adapter(adapter)) +		return i2c_trylock_adapter(to_i2c_adapter(adapter->dev.parent)); +	else +		return rt_mutex_trylock(&adapter->bus_lock); +} + +/** + * i2c_unlock_adapter - Release exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + */ +void i2c_unlock_adapter(struct i2c_adapter *adapter) +{ +	if (i2c_parent_is_i2c_adapter(adapter)) +		i2c_unlock_adapter(to_i2c_adapter(adapter->dev.parent)); +	else +		rt_mutex_unlock(&adapter->bus_lock);  } +EXPORT_SYMBOL_GPL(i2c_unlock_adapter);  /**   * i2c_new_device - instantiate an i2c device @@ -627,9 +711,9 @@ i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,  		return -EINVAL;  	/* Keep track of the added device */ -	i2c_lock_adapter(adap); +	mutex_lock(&adap->userspace_clients_lock);  	list_add_tail(&client->detected, &adap->userspace_clients); -	i2c_unlock_adapter(adap); +	mutex_unlock(&adap->userspace_clients_lock);  	dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",  		 info.type, info.addr); @@ -668,7 +752,7 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,  	/* Make sure the device was added through sysfs */  	res = -ENOENT; -	i2c_lock_adapter(adap); +	mutex_lock(&adap->userspace_clients_lock);  	list_for_each_entry_safe(client, next, &adap->userspace_clients,  				 detected) {  		if (client->addr == addr) { @@ -681,7 +765,7 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,  			break;  		}  	} -	i2c_unlock_adapter(adap); +	mutex_unlock(&adap->userspace_clients_lock);  	if (res < 0)  		dev_err(dev, "%s: Can't find device in list\n", @@ -708,10 +792,11 @@ static const struct attribute_group *i2c_adapter_attr_groups[] = {  	NULL  }; -static struct device_type i2c_adapter_type = { +struct device_type i2c_adapter_type = {  	.groups		= i2c_adapter_attr_groups,  	.release	= i2c_adapter_dev_release,  }; +EXPORT_SYMBOL_GPL(i2c_adapter_type);  #ifdef CONFIG_I2C_COMPAT  static struct class_compat *i2c_adapter_compat_class; @@ -754,7 +839,7 @@ static int __process_new_adapter(struct device_driver *d, void *data)  static int i2c_register_adapter(struct i2c_adapter *adap)  { -	int res = 0, dummy; +	int res = 0;  	/* Can't register until after driver model init */  	if (unlikely(WARN_ON(!i2c_bus_type.p))) { @@ -763,6 +848,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)  	}  	rt_mutex_init(&adap->bus_lock); +	mutex_init(&adap->userspace_clients_lock);  	INIT_LIST_HEAD(&adap->userspace_clients);  	/* Set default timeout to 1 second if not already set */ @@ -790,10 +876,12 @@ static int i2c_register_adapter(struct i2c_adapter *adap)  	if (adap->nr < __i2c_first_dynamic_bus_num)  		i2c_scan_static_board_info(adap); +	/* Register devices from the device tree */ +	of_i2c_register_devices(adap); +  	/* Notify drivers */  	mutex_lock(&core_lock); -	dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap, -				 __process_new_adapter); +	bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);  	mutex_unlock(&core_lock);  	return 0; @@ -966,7 +1054,7 @@ int i2c_del_adapter(struct i2c_adapter *adap)  		return res;  	/* Remove devices instantiated from sysfs */ -	i2c_lock_adapter(adap); +	mutex_lock(&adap->userspace_clients_lock);  	list_for_each_entry_safe(client, next, &adap->userspace_clients,  				 detected) {  		dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name, @@ -974,7 +1062,7 @@ int i2c_del_adapter(struct i2c_adapter *adap)  		list_del(&client->detected);  		i2c_unregister_device(client);  	} -	i2c_unlock_adapter(adap); +	mutex_unlock(&adap->userspace_clients_lock);  	/* Detach any active clients. This can't fail, thus we do not  	   checking the returned value. */ @@ -1229,12 +1317,12 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)  #endif  		if (in_atomic() || irqs_disabled()) { -			ret = rt_mutex_trylock(&adap->bus_lock); +			ret = i2c_trylock_adapter(adap);  			if (!ret)  				/* I2C activity is ongoing. */  				return -EAGAIN;  		} else { -			rt_mutex_lock(&adap->bus_lock); +			i2c_lock_adapter(adap);  		}  		/* Retry automatically on arbitration loss */ @@ -1246,7 +1334,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)  			if (time_after(jiffies, orig_jiffies + adap->timeout))  				break;  		} -		rt_mutex_unlock(&adap->bus_lock); +		i2c_unlock_adapter(adap);  		return ret;  	} else { @@ -1341,13 +1429,17 @@ static int i2c_default_probe(struct i2c_adapter *adap, unsigned short addr)  				     I2C_SMBUS_BYTE_DATA, &dummy);  	else  #endif -	if ((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50 -	 || !i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK)) -		err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, -				     I2C_SMBUS_BYTE, &dummy); -	else +	if (!((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50) +	 && i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK))  		err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0,  				     I2C_SMBUS_QUICK, NULL); +	else if (i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) +		err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, +				     I2C_SMBUS_BYTE, &dummy); +	else { +		dev_warn(&adap->dev, "No suitable probing method supported\n"); +		err = -EOPNOTSUPP; +	}  	return err >= 0;  } @@ -1428,17 +1520,6 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)  	if (!(adapter->class & driver->class))  		goto exit_free; -	/* Stop here if we can't use SMBUS_QUICK */ -	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) { -		if (address_list[0] == I2C_CLIENT_END) -			goto exit_free; - -		dev_warn(&adapter->dev, "SMBus Quick command not supported, " -			 "can't probe for chips\n"); -		err = -EOPNOTSUPP; -		goto exit_free; -	} -  	for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {  		dev_dbg(&adapter->dev, "found normal entry for adapter %d, "  			"addr 0x%02x\n", adap_id, address_list[i]); @@ -1453,18 +1534,23 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)  	return err;  } +int i2c_probe_func_quick_read(struct i2c_adapter *adap, unsigned short addr) +{ +	return i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, +			      I2C_SMBUS_QUICK, NULL) >= 0; +} +EXPORT_SYMBOL_GPL(i2c_probe_func_quick_read); +  struct i2c_client *  i2c_new_probed_device(struct i2c_adapter *adap,  		      struct i2c_board_info *info, -		      unsigned short const *addr_list) +		      unsigned short const *addr_list, +		      int (*probe)(struct i2c_adapter *, unsigned short addr))  {  	int i; -	/* Stop here if the bus doesn't support probing */ -	if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) { -		dev_err(&adap->dev, "Probing not supported\n"); -		return NULL; -	} +	if (!probe) +		probe = i2c_default_probe;  	for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) {  		/* Check address validity */ @@ -1482,7 +1568,7 @@ i2c_new_probed_device(struct i2c_adapter *adap,  		}  		/* Test address responsiveness */ -		if (i2c_default_probe(adap, addr_list[i])) +		if (probe(adap, addr_list[i]))  			break;  	} @@ -1994,7 +2080,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,  	flags &= I2C_M_TEN | I2C_CLIENT_PEC;  	if (adapter->algo->smbus_xfer) { -		rt_mutex_lock(&adapter->bus_lock); +		i2c_lock_adapter(adapter);  		/* Retry automatically on arbitration loss */  		orig_jiffies = jiffies; @@ -2008,7 +2094,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,  				       orig_jiffies + adapter->timeout))  				break;  		} -		rt_mutex_unlock(&adapter->bus_lock); +		i2c_unlock_adapter(adapter);  	} else  		res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,  					      command, protocol, data); | 
