diff options
Diffstat (limited to 'drivers/i2c/i2c-core.c')
| -rw-r--r-- | drivers/i2c/i2c-core.c | 340 | 
1 files changed, 244 insertions, 96 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index af11b658984d..da3a02ef4a31 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -27,6 +27,8 @@     I2C slave support (c) 2014 by Wolfram Sang <[email protected]>   */ +#define pr_fmt(fmt) "i2c-core: " fmt +  #include <dt-bindings/i2c/i2c.h>  #include <asm/uaccess.h>  #include <linux/acpi.h> @@ -107,12 +109,11 @@ struct acpi_i2c_lookup {  	acpi_handle device_handle;  }; -static int acpi_i2c_find_address(struct acpi_resource *ares, void *data) +static int acpi_i2c_fill_info(struct acpi_resource *ares, void *data)  {  	struct acpi_i2c_lookup *lookup = data;  	struct i2c_board_info *info = lookup->info;  	struct acpi_resource_i2c_serialbus *sb; -	acpi_handle adapter_handle;  	acpi_status status;  	if (info->addr || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) @@ -122,80 +123,102 @@ static int acpi_i2c_find_address(struct acpi_resource *ares, void *data)  	if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C)  		return 1; -	/* -	 * Extract the ResourceSource and make sure that the handle matches -	 * with the I2C adapter handle. -	 */  	status = acpi_get_handle(lookup->device_handle,  				 sb->resource_source.string_ptr, -				 &adapter_handle); -	if (ACPI_SUCCESS(status) && adapter_handle == lookup->adapter_handle) { -		info->addr = sb->slave_address; -		if (sb->access_mode == ACPI_I2C_10BIT_MODE) -			info->flags |= I2C_CLIENT_TEN; -	} +				 &lookup->adapter_handle); +	if (!ACPI_SUCCESS(status)) +		return 1; + +	info->addr = sb->slave_address; +	if (sb->access_mode == ACPI_I2C_10BIT_MODE) +		info->flags |= I2C_CLIENT_TEN;  	return 1;  } -static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, -				       void *data, void **return_value) +static int acpi_i2c_get_info(struct acpi_device *adev, +			     struct i2c_board_info *info, +			     acpi_handle *adapter_handle)  { -	struct i2c_adapter *adapter = data;  	struct list_head resource_list; -	struct acpi_i2c_lookup lookup;  	struct resource_entry *entry; -	struct i2c_board_info info; -	struct acpi_device *adev; +	struct acpi_i2c_lookup lookup;  	int ret; -	if (acpi_bus_get_device(handle, &adev)) -		return AE_OK; -	if (acpi_bus_get_status(adev) || !adev->status.present) -		return AE_OK; +	if (acpi_bus_get_status(adev) || !adev->status.present || +	    acpi_device_enumerated(adev)) +		return -EINVAL; -	memset(&info, 0, sizeof(info)); -	info.fwnode = acpi_fwnode_handle(adev); +	memset(info, 0, sizeof(*info)); +	info->fwnode = acpi_fwnode_handle(adev);  	memset(&lookup, 0, sizeof(lookup)); -	lookup.adapter_handle = ACPI_HANDLE(&adapter->dev); -	lookup.device_handle = handle; -	lookup.info = &info; +	lookup.device_handle = acpi_device_handle(adev); +	lookup.info = info; -	/* -	 * Look up for I2cSerialBus resource with ResourceSource that -	 * matches with this adapter. -	 */ +	/* Look up for I2cSerialBus resource */  	INIT_LIST_HEAD(&resource_list);  	ret = acpi_dev_get_resources(adev, &resource_list, -				     acpi_i2c_find_address, &lookup); +				     acpi_i2c_fill_info, &lookup);  	acpi_dev_free_resource_list(&resource_list); -	if (ret < 0 || !info.addr) -		return AE_OK; +	if (ret < 0 || !info->addr) +		return -EINVAL; + +	*adapter_handle = lookup.adapter_handle;  	/* Then fill IRQ number if any */  	ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);  	if (ret < 0) -		return AE_OK; +		return -EINVAL;  	resource_list_for_each_entry(entry, &resource_list) {  		if (resource_type(entry->res) == IORESOURCE_IRQ) { -			info.irq = entry->res->start; +			info->irq = entry->res->start;  			break;  		}  	}  	acpi_dev_free_resource_list(&resource_list); +	strlcpy(info->type, dev_name(&adev->dev), sizeof(info->type)); + +	return 0; +} + +static void acpi_i2c_register_device(struct i2c_adapter *adapter, +				     struct acpi_device *adev, +				     struct i2c_board_info *info) +{  	adev->power.flags.ignore_parent = true; -	strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type)); -	if (!i2c_new_device(adapter, &info)) { +	acpi_device_set_enumerated(adev); + +	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));  	} +} + +static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, +				       void *data, void **return_value) +{ +	struct i2c_adapter *adapter = data; +	struct acpi_device *adev; +	acpi_handle adapter_handle; +	struct i2c_board_info info; + +	if (acpi_bus_get_device(handle, &adev)) +		return AE_OK; + +	if (acpi_i2c_get_info(adev, &info, &adapter_handle)) +		return AE_OK; + +	if (adapter_handle != ACPI_HANDLE(&adapter->dev)) +		return AE_OK; + +	acpi_i2c_register_device(adapter, adev, &info);  	return AE_OK;  } @@ -225,8 +248,80 @@ static void acpi_i2c_register_devices(struct i2c_adapter *adap)  		dev_warn(&adap->dev, "failed to enumerate I2C slaves\n");  } +static int acpi_i2c_match_adapter(struct device *dev, void *data) +{ +	struct i2c_adapter *adapter = i2c_verify_adapter(dev); + +	if (!adapter) +		return 0; + +	return ACPI_HANDLE(dev) == (acpi_handle)data; +} + +static int acpi_i2c_match_device(struct device *dev, void *data) +{ +	return ACPI_COMPANION(dev) == data; +} + +static struct i2c_adapter *acpi_i2c_find_adapter_by_handle(acpi_handle handle) +{ +	struct device *dev; + +	dev = bus_find_device(&i2c_bus_type, NULL, handle, +			      acpi_i2c_match_adapter); +	return dev ? i2c_verify_adapter(dev) : NULL; +} + +static struct i2c_client *acpi_i2c_find_client_by_adev(struct acpi_device *adev) +{ +	struct device *dev; + +	dev = bus_find_device(&i2c_bus_type, NULL, adev, acpi_i2c_match_device); +	return dev ? i2c_verify_client(dev) : NULL; +} + +static int acpi_i2c_notify(struct notifier_block *nb, unsigned long value, +			   void *arg) +{ +	struct acpi_device *adev = arg; +	struct i2c_board_info info; +	acpi_handle adapter_handle; +	struct i2c_adapter *adapter; +	struct i2c_client *client; + +	switch (value) { +	case ACPI_RECONFIG_DEVICE_ADD: +		if (acpi_i2c_get_info(adev, &info, &adapter_handle)) +			break; + +		adapter = acpi_i2c_find_adapter_by_handle(adapter_handle); +		if (!adapter) +			break; + +		acpi_i2c_register_device(adapter, adev, &info); +		break; +	case ACPI_RECONFIG_DEVICE_REMOVE: +		if (!acpi_device_enumerated(adev)) +			break; + +		client = acpi_i2c_find_client_by_adev(adev); +		if (!client) +			break; + +		i2c_unregister_device(client); +		put_device(&client->dev); +		break; +	} + +	return NOTIFY_OK; +} + +static struct notifier_block i2c_acpi_notifier = { +	.notifier_call = acpi_i2c_notify, +};  #else /* CONFIG_ACPI */  static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) { } +extern struct notifier_block i2c_acpi_notifier;  #endif /* CONFIG_ACPI */  #ifdef CONFIG_ACPI_I2C_OPREGION @@ -400,7 +495,8 @@ acpi_i2c_space_handler(u32 function, acpi_physical_address command,  		break;  	default: -		pr_info("protocol(0x%02x) is not supported.\n", accessor_type); +		dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n", +			 accessor_type, client->addr);  		ret = AE_BAD_PARAMETER;  		goto err;  	} @@ -666,6 +762,47 @@ int i2c_recover_bus(struct i2c_adapter *adap)  }  EXPORT_SYMBOL_GPL(i2c_recover_bus); +static void i2c_init_recovery(struct i2c_adapter *adap) +{ +	struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; +	char *err_str; + +	if (!bri) +		return; + +	if (!bri->recover_bus) { +		err_str = "no recover_bus() found"; +		goto err; +	} + +	/* Generic GPIO recovery */ +	if (bri->recover_bus == i2c_generic_gpio_recovery) { +		if (!gpio_is_valid(bri->scl_gpio)) { +			err_str = "invalid SCL gpio"; +			goto err; +		} + +		if (gpio_is_valid(bri->sda_gpio)) +			bri->get_sda = get_sda_gpio_value; +		else +			bri->get_sda = NULL; + +		bri->get_scl = get_scl_gpio_value; +		bri->set_scl = set_scl_gpio_value; +	} else if (bri->recover_bus == i2c_generic_scl_recovery) { +		/* Generic SCL recovery */ +		if (!bri->set_scl || !bri->get_scl) { +			err_str = "no {get|set}_scl() found"; +			goto err; +		} +	} + +	return; + err: +	dev_err(&adap->dev, "Not using recovery: %s\n", err_str); +	adap->bus_recovery_info = NULL; +} +  static int i2c_device_probe(struct device *dev)  {  	struct i2c_client	*client = i2c_verify_client(dev); @@ -1089,6 +1226,8 @@ void i2c_unregister_device(struct i2c_client *client)  {  	if (client->dev.of_node)  		of_node_clear_flag(client->dev.of_node, OF_POPULATED); +	if (ACPI_COMPANION(&client->dev)) +		acpi_device_clear_enumerated(ACPI_COMPANION(&client->dev));  	device_unregister(&client->dev);  }  EXPORT_SYMBOL_GPL(i2c_unregister_device); @@ -1145,6 +1284,47 @@ struct i2c_client *i2c_new_dummy(struct i2c_adapter *adapter, u16 address)  }  EXPORT_SYMBOL_GPL(i2c_new_dummy); +/** + * i2c_new_secondary_device - Helper to get the instantiated secondary address + * and create the associated device + * @client: Handle to the primary client + * @name: Handle to specify which secondary address to get + * @default_addr: Used as a fallback if no secondary address was specified + * Context: can sleep + * + * I2C clients can be composed of multiple I2C slaves bound together in a single + * component. The I2C client driver then binds to the master I2C slave and needs + * to create I2C dummy clients to communicate with all the other slaves. + * + * This function creates and returns an I2C dummy client whose I2C address is + * retrieved from the platform firmware based on the given slave name. If no + * address is specified by the firmware default_addr is used. + * + * On DT-based platforms the address is retrieved from the "reg" property entry + * cell whose "reg-names" value matches the slave name. + * + * 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_secondary_device(struct i2c_client *client, +						const char *name, +						u16 default_addr) +{ +	struct device_node *np = client->dev.of_node; +	u32 addr = default_addr; +	int i; + +	if (np) { +		i = of_property_match_string(np, "reg-names", name); +		if (i >= 0) +			of_property_read_u32_index(np, "reg", i, &addr); +	} + +	dev_dbg(&client->adapter->dev, "Address for %s : 0x%x\n", name, addr); +	return i2c_new_dummy(client->adapter, addr); +} +EXPORT_SYMBOL_GPL(i2c_new_secondary_device); +  /* ------------------------------------------------------------------------- */  /* I2C bus adapters -- one roots each I2C or SMBUS segment */ @@ -1513,7 +1693,7 @@ static int __process_new_adapter(struct device_driver *d, void *data)  static int i2c_register_adapter(struct i2c_adapter *adap)  { -	int res = 0; +	int res = -EINVAL;  	/* Can't register until after driver model init */  	if (WARN_ON(!is_registered)) { @@ -1522,15 +1702,12 @@ static int i2c_register_adapter(struct i2c_adapter *adap)  	}  	/* Sanity checks */ -	if (unlikely(adap->name[0] == '\0')) { -		pr_err("i2c-core: Attempt to register an adapter with " -		       "no name!\n"); -		return -EINVAL; -	} -	if (unlikely(!adap->algo)) { -		pr_err("i2c-core: Attempt to register adapter '%s' with " -		       "no algo!\n", adap->name); -		return -EINVAL; +	if (WARN(!adap->name[0], "i2c adapter has no name")) +		goto out_list; + +	if (!adap->algo) { +		pr_err("adapter '%s': no algo supplied!\n", adap->name); +		goto out_list;  	}  	if (!adap->lock_bus) { @@ -1552,8 +1729,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap)  	adap->dev.bus = &i2c_bus_type;  	adap->dev.type = &i2c_adapter_type;  	res = device_register(&adap->dev); -	if (res) +	if (res) { +		pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);  		goto out_list; +	}  	dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); @@ -1569,41 +1748,8 @@ static int i2c_register_adapter(struct i2c_adapter *adap)  			 "Failed to create compatibility class link\n");  #endif -	/* bus recovery specific initialization */ -	if (adap->bus_recovery_info) { -		struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; - -		if (!bri->recover_bus) { -			dev_err(&adap->dev, "No recover_bus() found, not using recovery\n"); -			adap->bus_recovery_info = NULL; -			goto exit_recovery; -		} - -		/* Generic GPIO recovery */ -		if (bri->recover_bus == i2c_generic_gpio_recovery) { -			if (!gpio_is_valid(bri->scl_gpio)) { -				dev_err(&adap->dev, "Invalid SCL gpio, not using recovery\n"); -				adap->bus_recovery_info = NULL; -				goto exit_recovery; -			} +	i2c_init_recovery(adap); -			if (gpio_is_valid(bri->sda_gpio)) -				bri->get_sda = get_sda_gpio_value; -			else -				bri->get_sda = NULL; - -			bri->get_scl = get_scl_gpio_value; -			bri->set_scl = set_scl_gpio_value; -		} else if (bri->recover_bus == i2c_generic_scl_recovery) { -			/* Generic SCL recovery */ -			if (!bri->set_scl || !bri->get_scl) { -				dev_err(&adap->dev, "No {get|set}_scl() found, not using recovery\n"); -				adap->bus_recovery_info = NULL; -			} -		} -	} - -exit_recovery:  	/* create pre-declared device nodes */  	of_i2c_register_devices(adap);  	acpi_i2c_register_devices(adap); @@ -1635,13 +1781,12 @@ out_list:   */  static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)  { -	int	id; +	int id;  	mutex_lock(&core_lock); -	id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, -		       GFP_KERNEL); +	id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL);  	mutex_unlock(&core_lock); -	if (id < 0) +	if (WARN(id < 0, "couldn't get idr"))  		return id == -ENOSPC ? -EBUSY : id;  	return i2c_register_adapter(adap); @@ -1678,7 +1823,7 @@ int i2c_add_adapter(struct i2c_adapter *adapter)  	id = idr_alloc(&i2c_adapter_idr, adapter,  		       __i2c_first_dynamic_bus_num, 0, GFP_KERNEL);  	mutex_unlock(&core_lock); -	if (id < 0) +	if (WARN(id < 0, "couldn't get idr"))  		return id;  	adapter->nr = id; @@ -1776,8 +1921,7 @@ void i2c_del_adapter(struct i2c_adapter *adap)  	found = idr_find(&i2c_adapter_idr, adap->nr);  	mutex_unlock(&core_lock);  	if (found != adap) { -		pr_debug("i2c-core: attempting to delete unregistered " -			 "adapter [%s]\n", adap->name); +		pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name);  		return;  	} @@ -1937,7 +2081,7 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)  	if (res)  		return res; -	pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name); +	pr_debug("driver [%s] registered\n", driver->driver.name);  	INIT_LIST_HEAD(&driver->clients);  	/* Walk the adapters that are already present */ @@ -1964,7 +2108,7 @@ void i2c_del_driver(struct i2c_driver *driver)  	i2c_for_each_dev(driver, __process_removed_driver);  	driver_unregister(&driver->driver); -	pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name); +	pr_debug("driver [%s] unregistered\n", driver->driver.name);  }  EXPORT_SYMBOL(i2c_del_driver); @@ -2055,8 +2199,8 @@ static int of_i2c_notify(struct notifier_block *nb, unsigned long action,  		put_device(&adap->dev);  		if (IS_ERR(client)) { -			pr_err("%s: failed to create for '%s'\n", -					__func__, rd->dn->full_name); +			dev_err(&adap->dev, "failed to create client for '%s'\n", +				 rd->dn->full_name);  			return notifier_from_errno(PTR_ERR(client));  		}  		break; @@ -2117,6 +2261,8 @@ static int __init i2c_init(void)  	if (IS_ENABLED(CONFIG_OF_DYNAMIC))  		WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier)); +	if (IS_ENABLED(CONFIG_ACPI)) +		WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier));  	return 0; @@ -2132,6 +2278,8 @@ bus_err:  static void __exit i2c_exit(void)  { +	if (IS_ENABLED(CONFIG_ACPI)) +		WARN_ON(acpi_reconfig_notifier_unregister(&i2c_acpi_notifier));  	if (IS_ENABLED(CONFIG_OF_DYNAMIC))  		WARN_ON(of_reconfig_notifier_unregister(&i2c_of_notifier));  	i2c_del_driver(&dummy_driver); @@ -2673,7 +2821,7 @@ static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg)  	cpec = i2c_smbus_msg_pec(cpec, msg);  	if (rpec != cpec) { -		pr_debug("i2c-core: Bad PEC 0x%02x vs. 0x%02x\n", +		pr_debug("Bad PEC 0x%02x vs. 0x%02x\n",  			rpec, cpec);  		return -EBADMSG;  	}  |