Skip to content

Commit

Permalink
i2c: Allow an ACPI driver to manage the device's power state during p…
Browse files Browse the repository at this point in the history
…robe

Enable drivers to tell ACPI that there's no need to power on a device for
probe. Drivers should still perform this by themselves if there's a need
to. In some cases powering on the device during probe is undesirable, and
this change enables a driver to choose what fits best for it.

Add a field called "flags" into struct i2c_driver for driver flags, and a
flag I2C_DRV_ACPI_WAIVE_D0_PROBE to tell a driver supports probe in ACPI D
states other than 0.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Tomasz Figa <tfiga@chromium.org>
Acked-by: Wolfram Sang <wsa@kernel.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
  • Loading branch information
Sakari Ailus authored and Rafael J. Wysocki committed Nov 3, 2021
1 parent b340c7d commit b18c1ad
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 3 deletions.
10 changes: 10 additions & 0 deletions drivers/i2c/i2c-core-acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,16 @@ struct i2c_client *i2c_acpi_new_device(struct device *dev, int index,
}
EXPORT_SYMBOL_GPL(i2c_acpi_new_device);

bool i2c_acpi_waive_d0_probe(struct device *dev)
{
struct i2c_driver *driver = to_i2c_driver(dev->driver);
struct acpi_device *adev = ACPI_COMPANION(dev);

return driver->flags & I2C_DRV_ACPI_WAIVE_D0_PROBE &&
adev && adev->power.state_for_enumeration >= adev->power.state;
}
EXPORT_SYMBOL_GPL(i2c_acpi_waive_d0_probe);

#ifdef CONFIG_ACPI_I2C_OPREGION
static int acpi_gsb_i2c_read_bytes(struct i2c_client *client,
u8 cmd, u8 *data, u8 data_len)
Expand Down
7 changes: 4 additions & 3 deletions drivers/i2c/i2c-core-base.c
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,8 @@ static int i2c_device_probe(struct device *dev)
if (status < 0)
goto err_clear_wakeup_irq;

status = dev_pm_domain_attach(&client->dev, true);
status = dev_pm_domain_attach(&client->dev,
!i2c_acpi_waive_d0_probe(dev));
if (status)
goto err_clear_wakeup_irq;

Expand Down Expand Up @@ -590,7 +591,7 @@ static int i2c_device_probe(struct device *dev)
err_release_driver_resources:
devres_release_group(&client->dev, client->devres_group_id);
err_detach_pm_domain:
dev_pm_domain_detach(&client->dev, true);
dev_pm_domain_detach(&client->dev, !i2c_acpi_waive_d0_probe(dev));
err_clear_wakeup_irq:
dev_pm_clear_wake_irq(&client->dev);
device_init_wakeup(&client->dev, false);
Expand Down Expand Up @@ -621,7 +622,7 @@ static void i2c_device_remove(struct device *dev)

devres_release_group(&client->dev, client->devres_group_id);

dev_pm_domain_detach(&client->dev, true);
dev_pm_domain_detach(&client->dev, !i2c_acpi_waive_d0_probe(dev));
if (!pm_runtime_status_suspended(&client->dev) && adap->bus_regulator)
regulator_disable(adap->bus_regulator);

Expand Down
18 changes: 18 additions & 0 deletions include/linux/i2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#define _LINUX_I2C_H

#include <linux/acpi.h> /* for acpi_handle */
#include <linux/bits.h>
#include <linux/mod_devicetable.h>
#include <linux/device.h> /* for struct device */
#include <linux/sched.h> /* for completion */
Expand Down Expand Up @@ -222,6 +223,15 @@ enum i2c_alert_protocol {
I2C_PROTOCOL_SMBUS_HOST_NOTIFY,
};

/**
* enum i2c_driver_flags - Flags for an I2C device driver
*
* @I2C_DRV_ACPI_WAIVE_D0_PROBE: Don't put the device in D0 state for probe
*/
enum i2c_driver_flags {
I2C_DRV_ACPI_WAIVE_D0_PROBE = BIT(0),
};

/**
* struct i2c_driver - represent an I2C device driver
* @class: What kind of i2c device we instantiate (for detect)
Expand All @@ -236,6 +246,7 @@ enum i2c_alert_protocol {
* @detect: Callback for device detection
* @address_list: The I2C addresses to probe (for detect)
* @clients: List of detected clients we created (for i2c-core use only)
* @flags: A bitmask of flags defined in &enum i2c_driver_flags
*
* The driver.owner field should be set to the module owner of this driver.
* The driver.name field should be set to the name of this driver.
Expand Down Expand Up @@ -294,6 +305,8 @@ struct i2c_driver {
int (*detect)(struct i2c_client *client, struct i2c_board_info *info);
const unsigned short *address_list;
struct list_head clients;

u32 flags;
};
#define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)

Expand Down Expand Up @@ -1015,6 +1028,7 @@ u32 i2c_acpi_find_bus_speed(struct device *dev);
struct i2c_client *i2c_acpi_new_device(struct device *dev, int index,
struct i2c_board_info *info);
struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle);
bool i2c_acpi_waive_d0_probe(struct device *dev);
#else
static inline bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares,
struct acpi_resource_i2c_serialbus **i2c)
Expand All @@ -1038,6 +1052,10 @@ static inline struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle ha
{
return NULL;
}
static inline bool i2c_acpi_waive_d0_probe(struct device *dev)
{
return false;
}
#endif /* CONFIG_ACPI */

#endif /* _LINUX_I2C_H */

0 comments on commit b18c1ad

Please sign in to comment.