diff options
Diffstat (limited to 'drivers/i2c/i2c-core-base.c')
| -rw-r--r-- | drivers/i2c/i2c-core-base.c | 147 | 
1 files changed, 122 insertions, 25 deletions
| diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 38af18645133..d389d4fb0623 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -185,7 +185,7 @@ static int i2c_generic_bus_free(struct i2c_adapter *adap)  int i2c_generic_scl_recovery(struct i2c_adapter *adap)  {  	struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; -	int i = 0, scl = 1, ret; +	int i = 0, scl = 1, ret = 0;  	if (bri->prepare_recovery)  		bri->prepare_recovery(adap); @@ -327,6 +327,8 @@ static int i2c_device_probe(struct device *dev)  		if (client->flags & I2C_CLIENT_HOST_NOTIFY) {  			dev_dbg(dev, "Using Host Notify IRQ\n"); +			/* Keep adapter active when Host Notify is required */ +			pm_runtime_get_sync(&client->adapter->dev);  			irq = i2c_smbus_host_notify_to_irq(client);  		} else if (dev->of_node) {  			irq = of_irq_get_byname(dev->of_node, "irq"); @@ -431,6 +433,8 @@ static int i2c_device_remove(struct device *dev)  	device_init_wakeup(&client->dev, false);  	client->irq = client->init_irq; +	if (client->flags & I2C_CLIENT_HOST_NOTIFY) +		pm_runtime_put(&client->adapter->dev);  	return status;  } @@ -710,7 +714,7 @@ static int i2c_dev_irq_from_resources(const struct resource *resources,  }  /** - * i2c_new_device - instantiate an i2c device + * i2c_new_client_device - instantiate an i2c device   * @adap: the adapter managing the device   * @info: describes one I2C device; bus_num is ignored   * Context: can sleep @@ -723,17 +727,17 @@ static int i2c_dev_irq_from_resources(const struct resource *resources,   * before any i2c_adapter could exist.   *   * This returns the new i2c client, which may be saved for later use with - * i2c_unregister_device(); or NULL to indicate an error. + * i2c_unregister_device(); or an ERR_PTR to describe the error.   */ -struct i2c_client * -i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) +static struct i2c_client * +i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info)  {  	struct i2c_client	*client;  	int			status;  	client = kzalloc(sizeof *client, GFP_KERNEL);  	if (!client) -		return NULL; +		return ERR_PTR(-ENOMEM);  	client->adapter = adap; @@ -799,7 +803,31 @@ out_err:  		client->name, client->addr, status);  out_err_silent:  	kfree(client); -	return NULL; +	return ERR_PTR(status); +} +EXPORT_SYMBOL_GPL(i2c_new_client_device); + +/** + * i2c_new_device - instantiate an i2c device + * @adap: the adapter managing the device + * @info: describes one I2C device; bus_num is ignored + * Context: can sleep + * + * This deprecated function has the same functionality as + * @i2c_new_client_device, it just returns NULL instead of an ERR_PTR in case of + * an error for compatibility with current I2C API. It will be removed once all + * users are converted. + * + * This returns the new i2c client, which may be saved for later use with + * i2c_unregister_device(); or NULL to indicate an error. + */ +struct i2c_client * +i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) +{ +	struct i2c_client *ret; + +	ret = i2c_new_client_device(adap, info); +	return IS_ERR(ret) ? NULL : ret;  }  EXPORT_SYMBOL_GPL(i2c_new_device); @@ -850,7 +878,7 @@ static struct i2c_driver dummy_driver = {  };  /** - * i2c_new_dummy - return a new i2c device bound to a dummy driver + * i2c_new_dummy_device - return a new i2c device bound to a dummy driver   * @adapter: the adapter managing the device   * @address: seven bit address to be used   * Context: can sleep @@ -865,18 +893,86 @@ static struct i2c_driver dummy_driver = {   * different driver.   *   * This returns the new i2c client, which should be saved for later use with - * i2c_unregister_device(); or NULL to indicate an error. + * i2c_unregister_device(); or an ERR_PTR to describe the error.   */ -struct i2c_client *i2c_new_dummy(struct i2c_adapter *adapter, u16 address) +static struct i2c_client * +i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address)  {  	struct i2c_board_info info = {  		I2C_BOARD_INFO("dummy", address),  	}; -	return i2c_new_device(adapter, &info); +	return i2c_new_client_device(adapter, &info); +} +EXPORT_SYMBOL_GPL(i2c_new_dummy_device); + +/** + * i2c_new_dummy - return a new i2c device bound to a dummy driver + * @adapter: the adapter managing the device + * @address: seven bit address to be used + * Context: can sleep + * + * This deprecated function has the same functionality as @i2c_new_dummy_device, + * it just returns NULL instead of an ERR_PTR in case of an error for + * compatibility with current I2C API. It will be removed once all users are + * converted. + * + * This returns the new i2c client, which should be saved for later use with + * i2c_unregister_device(); or NULL to indicate an error. + */ +struct i2c_client *i2c_new_dummy(struct i2c_adapter *adapter, u16 address) +{ +	struct i2c_client *ret; + +	ret = i2c_new_dummy_device(adapter, address); +	return IS_ERR(ret) ? NULL : ret;  }  EXPORT_SYMBOL_GPL(i2c_new_dummy); +struct i2c_dummy_devres { +	struct i2c_client *client; +}; + +static void devm_i2c_release_dummy(struct device *dev, void *res) +{ +	struct i2c_dummy_devres *this = res; + +	i2c_unregister_device(this->client); +} + +/** + * devm_i2c_new_dummy_device - return a new i2c device bound to a dummy driver + * @dev: device the managed resource is bound to + * @adapter: the adapter managing the device + * @address: seven bit address to be used + * Context: can sleep + * + * This is the device-managed version of @i2c_new_dummy_device. It returns the + * new i2c client or an ERR_PTR in case of an error. + */ +struct i2c_client *devm_i2c_new_dummy_device(struct device *dev, +					     struct i2c_adapter *adapter, +					     u16 address) +{ +	struct i2c_dummy_devres *dr; +	struct i2c_client *client; + +	dr = devres_alloc(devm_i2c_release_dummy, sizeof(*dr), GFP_KERNEL); +	if (!dr) +		return ERR_PTR(-ENOMEM); + +	client = i2c_new_dummy_device(adapter, address); +	if (IS_ERR(client)) { +		devres_free(dr); +	} else { +		dr->client = client; +		devres_add(dev, dr); +	} + +	return client; +} +EXPORT_SYMBOL_GPL(devm_i2c_new_dummy_device); +  /**   * i2c_new_secondary_device - Helper to get the instantiated secondary address   * and create the associated device @@ -996,9 +1092,9 @@ i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,  		info.flags |= I2C_CLIENT_SLAVE;  	} -	client = i2c_new_device(adap, &info); -	if (!client) -		return -EINVAL; +	client = i2c_new_client_device(adap, &info); +	if (IS_ERR(client)) +		return PTR_ERR(client);  	/* Keep track of the added device */  	mutex_lock(&adap->userspace_clients_lock); @@ -1867,8 +1963,10 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)  	if (WARN_ON(!msgs || num < 1))  		return -EINVAL; -	if (WARN_ON(test_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags))) -		return -ESHUTDOWN; + +	ret = __i2c_check_suspended(adap); +	if (ret) +		return ret;  	if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))  		return -EOPNOTSUPP; @@ -1890,7 +1988,11 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)  	/* Retry automatically on arbitration loss */  	orig_jiffies = jiffies;  	for (ret = 0, try = 0; try <= adap->retries; try++) { -		ret = adap->algo->master_xfer(adap, msgs, num); +		if (i2c_in_atomic_xfer_mode() && adap->algo->master_xfer_atomic) +			ret = adap->algo->master_xfer_atomic(adap, msgs, num); +		else +			ret = adap->algo->master_xfer(adap, msgs, num); +  		if (ret != -EAGAIN)  			break;  		if (time_after(jiffies, orig_jiffies + adap->timeout)) @@ -1946,14 +2048,9 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)  	 *    one (discarding status on the second message) or errno  	 *    (discarding status on the first one).  	 */ -	if (in_atomic() || irqs_disabled()) { -		ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT); -		if (!ret) -			/* I2C activity is ongoing. */ -			return -EAGAIN; -	} else { -		i2c_lock_bus(adap, I2C_LOCK_SEGMENT); -	} +	ret = __i2c_lock_bus_helper(adap); +	if (ret) +		return ret;  	ret = __i2c_transfer(adap, msgs, num);  	i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); | 
