forked from Minki/linux
Merge branch 'acpi-dsc'
Merge new ACPI device configuration object _DSC support for 5.16-rc1. * acpi-dsc: Documentation: ACPI: Fix non-D0 probe _DSC object example at24: Support probing while in non-zero ACPI D state media: i2c: imx319: Support device probe in non-zero ACPI D state ACPI: Add a convenience function to tell a device is in D0 state Documentation: ACPI: Document _DSC object usage for enum power state i2c: Allow an ACPI driver to manage the device's power state during probe ACPI: scan: Obtain device's desired enumeration power state
This commit is contained in:
commit
2c49dabad8
@ -26,5 +26,6 @@ ACPI Support
|
||||
acpi-lid
|
||||
lpit
|
||||
video_extension
|
||||
non-d0-probe
|
||||
extcon-intel-int3496
|
||||
intel-pmc-mux
|
||||
|
78
Documentation/firmware-guide/acpi/non-d0-probe.rst
Normal file
78
Documentation/firmware-guide/acpi/non-d0-probe.rst
Normal file
@ -0,0 +1,78 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
========================================
|
||||
Probing devices in other D states than 0
|
||||
========================================
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
In some cases it may be preferred to leave certain devices powered off for the
|
||||
entire system bootup if powering on these devices has adverse side effects,
|
||||
beyond just powering on the said device.
|
||||
|
||||
How it works
|
||||
============
|
||||
|
||||
The _DSC (Device State for Configuration) object that evaluates to an integer
|
||||
may be used to tell Linux the highest allowed D state for a device during
|
||||
probe. The support for _DSC requires support from the kernel bus type if the
|
||||
bus driver normally sets the device in D0 state for probe.
|
||||
|
||||
The downside of using _DSC is that as the device is not powered on, even if
|
||||
there's a problem with the device, the driver likely probes just fine but the
|
||||
first user will find out the device doesn't work, instead of a failure at probe
|
||||
time. This feature should thus be used sparingly.
|
||||
|
||||
I²C
|
||||
---
|
||||
|
||||
If an I²C driver indicates its support for this by setting the
|
||||
I2C_DRV_ACPI_WAIVE_D0_PROBE flag in struct i2c_driver.flags field and the
|
||||
_DSC object evaluates to integer higher than the D state of the device,
|
||||
the device will not be powered on (put in D0 state) for probe.
|
||||
|
||||
D states
|
||||
--------
|
||||
|
||||
The D states and thus also the allowed values for _DSC are listed below. Refer
|
||||
to [1] for more information on device power states.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
Number State Description
|
||||
0 D0 Device fully powered on
|
||||
1 D1
|
||||
2 D2
|
||||
3 D3hot
|
||||
4 D3cold Off
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
[1] https://uefi.org/specifications/ACPI/6.4/02_Definition_of_Terms/Definition_of_Terms.html#device-power-state-definitions
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
An ASL example describing an ACPI device using _DSC object to tell Operating
|
||||
System the device should remain powered off during probe looks like this. Some
|
||||
objects not relevant from the example point of view have been omitted.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
Device (CAM0)
|
||||
{
|
||||
Name (_HID, "SONY319A")
|
||||
Name (_UID, Zero)
|
||||
Name (_CRS, ResourceTemplate ()
|
||||
{
|
||||
I2cSerialBus(0x0020, ControllerInitiated, 0x00061A80,
|
||||
AddressingMode7Bit, "\\_SB.PCI0.I2C0",
|
||||
0x00, ResourceConsumer)
|
||||
})
|
||||
Method (_DSC, 0, NotSerialized)
|
||||
{
|
||||
Return (0x4)
|
||||
}
|
||||
}
|
@ -1400,4 +1400,30 @@ bool acpi_storage_d3(struct device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_storage_d3);
|
||||
|
||||
/**
|
||||
* acpi_dev_state_d0 - Tell if the device is in D0 power state
|
||||
* @dev: Physical device the ACPI power state of which to check
|
||||
*
|
||||
* On a system without ACPI, return true. On a system with ACPI, return true if
|
||||
* the current ACPI power state of the device is D0, or false otherwise.
|
||||
*
|
||||
* Note that the power state of a device is not well-defined after it has been
|
||||
* passed to acpi_device_set_power() and before that function returns, so it is
|
||||
* not valid to ask for the ACPI power state of the device in that time frame.
|
||||
*
|
||||
* This function is intended to be used in a driver's probe or remove
|
||||
* function. See Documentation/firmware-guide/acpi/low-power-probe.rst for
|
||||
* more information.
|
||||
*/
|
||||
bool acpi_dev_state_d0(struct device *dev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(dev);
|
||||
|
||||
if (!adev)
|
||||
return true;
|
||||
|
||||
return adev->power.state == ACPI_STATE_D0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_state_d0);
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
@ -1017,6 +1017,7 @@ static void acpi_bus_init_power_state(struct acpi_device *device, int state)
|
||||
|
||||
static void acpi_bus_get_power_flags(struct acpi_device *device)
|
||||
{
|
||||
unsigned long long dsc = ACPI_STATE_D0;
|
||||
u32 i;
|
||||
|
||||
/* Presence of _PS0|_PR0 indicates 'power manageable' */
|
||||
@ -1038,6 +1039,9 @@ static void acpi_bus_get_power_flags(struct acpi_device *device)
|
||||
if (acpi_has_method(device->handle, "_DSW"))
|
||||
device->power.flags.dsw_present = 1;
|
||||
|
||||
acpi_evaluate_integer(device->handle, "_DSC", NULL, &dsc);
|
||||
device->power.state_for_enumeration = dsc;
|
||||
|
||||
/*
|
||||
* Enumerate supported power management states
|
||||
*/
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
@ -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);
|
||||
@ -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);
|
||||
|
||||
|
@ -140,6 +140,8 @@ struct imx319 {
|
||||
|
||||
/* Streaming on/off */
|
||||
bool streaming;
|
||||
/* True if the device has been identified */
|
||||
bool identified;
|
||||
};
|
||||
|
||||
static const struct imx319_reg imx319_global_regs[] = {
|
||||
@ -2084,6 +2086,31 @@ imx319_set_pad_format(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Verify chip ID */
|
||||
static int imx319_identify_module(struct imx319 *imx319)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(&imx319->sd);
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
if (imx319->identified)
|
||||
return 0;
|
||||
|
||||
ret = imx319_read_reg(imx319, IMX319_REG_CHIP_ID, 2, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val != IMX319_CHIP_ID) {
|
||||
dev_err(&client->dev, "chip id mismatch: %x!=%x",
|
||||
IMX319_CHIP_ID, val);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
imx319->identified = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Start streaming */
|
||||
static int imx319_start_streaming(struct imx319 *imx319)
|
||||
{
|
||||
@ -2091,6 +2118,10 @@ static int imx319_start_streaming(struct imx319 *imx319)
|
||||
const struct imx319_reg_list *reg_list;
|
||||
int ret;
|
||||
|
||||
ret = imx319_identify_module(imx319);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Global Setting */
|
||||
reg_list = &imx319_global_setting;
|
||||
ret = imx319_write_regs(imx319, reg_list->regs, reg_list->num_of_regs);
|
||||
@ -2206,26 +2237,6 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Verify chip ID */
|
||||
static int imx319_identify_module(struct imx319 *imx319)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(&imx319->sd);
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = imx319_read_reg(imx319, IMX319_REG_CHIP_ID, 2, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val != IMX319_CHIP_ID) {
|
||||
dev_err(&client->dev, "chip id mismatch: %x!=%x",
|
||||
IMX319_CHIP_ID, val);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_core_ops imx319_subdev_core_ops = {
|
||||
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
|
||||
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
|
||||
@ -2420,6 +2431,7 @@ out_err:
|
||||
static int imx319_probe(struct i2c_client *client)
|
||||
{
|
||||
struct imx319 *imx319;
|
||||
bool full_power;
|
||||
int ret;
|
||||
u32 i;
|
||||
|
||||
@ -2432,11 +2444,14 @@ static int imx319_probe(struct i2c_client *client)
|
||||
/* Initialize subdev */
|
||||
v4l2_i2c_subdev_init(&imx319->sd, client, &imx319_subdev_ops);
|
||||
|
||||
/* Check module identity */
|
||||
ret = imx319_identify_module(imx319);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to find sensor: %d", ret);
|
||||
goto error_probe;
|
||||
full_power = acpi_dev_state_d0(&client->dev);
|
||||
if (full_power) {
|
||||
/* Check module identity */
|
||||
ret = imx319_identify_module(imx319);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to find sensor: %d", ret);
|
||||
goto error_probe;
|
||||
}
|
||||
}
|
||||
|
||||
imx319->hwcfg = imx319_get_hwcfg(&client->dev);
|
||||
@ -2488,11 +2503,9 @@ static int imx319_probe(struct i2c_client *client)
|
||||
if (ret < 0)
|
||||
goto error_media_entity;
|
||||
|
||||
/*
|
||||
* Device is already turned on by i2c-core with ACPI domain PM.
|
||||
* Enable runtime PM and turn off the device.
|
||||
*/
|
||||
pm_runtime_set_active(&client->dev);
|
||||
/* Set the device's state to active if it's in D0 state. */
|
||||
if (full_power)
|
||||
pm_runtime_set_active(&client->dev);
|
||||
pm_runtime_enable(&client->dev);
|
||||
pm_runtime_idle(&client->dev);
|
||||
|
||||
@ -2545,6 +2558,7 @@ static struct i2c_driver imx319_i2c_driver = {
|
||||
},
|
||||
.probe_new = imx319_probe,
|
||||
.remove = imx319_remove,
|
||||
.flags = I2C_DRV_ACPI_WAIVE_D0_PROBE,
|
||||
};
|
||||
module_i2c_driver(imx319_i2c_driver);
|
||||
|
||||
|
@ -595,6 +595,7 @@ static int at24_probe(struct i2c_client *client)
|
||||
bool i2c_fn_i2c, i2c_fn_block;
|
||||
unsigned int i, num_addresses;
|
||||
struct at24_data *at24;
|
||||
bool full_power;
|
||||
struct regmap *regmap;
|
||||
bool writable;
|
||||
u8 test_byte;
|
||||
@ -747,14 +748,16 @@ static int at24_probe(struct i2c_client *client)
|
||||
|
||||
i2c_set_clientdata(client, at24);
|
||||
|
||||
err = regulator_enable(at24->vcc_reg);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to enable vcc regulator\n");
|
||||
return err;
|
||||
}
|
||||
full_power = acpi_dev_state_d0(&client->dev);
|
||||
if (full_power) {
|
||||
err = regulator_enable(at24->vcc_reg);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to enable vcc regulator\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* enable runtime pm */
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
}
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
at24->nvmem = devm_nvmem_register(dev, &nvmem_config);
|
||||
@ -766,15 +769,18 @@ static int at24_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform a one-byte test read to verify that the
|
||||
* chip is functional.
|
||||
* Perform a one-byte test read to verify that the chip is functional,
|
||||
* unless powering on the device is to be avoided during probe (i.e.
|
||||
* it's powered off right now).
|
||||
*/
|
||||
err = at24_read(at24, 0, &test_byte, 1);
|
||||
if (err) {
|
||||
pm_runtime_disable(dev);
|
||||
if (!pm_runtime_status_suspended(dev))
|
||||
regulator_disable(at24->vcc_reg);
|
||||
return -ENODEV;
|
||||
if (full_power) {
|
||||
err = at24_read(at24, 0, &test_byte, 1);
|
||||
if (err) {
|
||||
pm_runtime_disable(dev);
|
||||
if (!pm_runtime_status_suspended(dev))
|
||||
regulator_disable(at24->vcc_reg);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
pm_runtime_idle(dev);
|
||||
@ -794,9 +800,11 @@ static int at24_remove(struct i2c_client *client)
|
||||
struct at24_data *at24 = i2c_get_clientdata(client);
|
||||
|
||||
pm_runtime_disable(&client->dev);
|
||||
if (!pm_runtime_status_suspended(&client->dev))
|
||||
regulator_disable(at24->vcc_reg);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
if (acpi_dev_state_d0(&client->dev)) {
|
||||
if (!pm_runtime_status_suspended(&client->dev))
|
||||
regulator_disable(at24->vcc_reg);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -833,6 +841,7 @@ static struct i2c_driver at24_driver = {
|
||||
.probe_new = at24_probe,
|
||||
.remove = at24_remove,
|
||||
.id_table = at24_ids,
|
||||
.flags = I2C_DRV_ACPI_WAIVE_D0_PROBE,
|
||||
};
|
||||
|
||||
static int __init at24_init(void)
|
||||
|
@ -278,6 +278,7 @@ struct acpi_device_power {
|
||||
int state; /* Current state */
|
||||
struct acpi_device_power_flags flags;
|
||||
struct acpi_device_power_state states[ACPI_D_STATE_COUNT]; /* Power states (D0-D3Cold) */
|
||||
u8 state_for_enumeration; /* Deepest power state for enumeration */
|
||||
};
|
||||
|
||||
struct acpi_dep_data {
|
||||
|
@ -1016,6 +1016,7 @@ int acpi_subsys_runtime_suspend(struct device *dev);
|
||||
int acpi_subsys_runtime_resume(struct device *dev);
|
||||
int acpi_dev_pm_attach(struct device *dev, bool power_on);
|
||||
bool acpi_storage_d3(struct device *dev);
|
||||
bool acpi_dev_state_d0(struct device *dev);
|
||||
#else
|
||||
static inline int acpi_subsys_runtime_suspend(struct device *dev) { return 0; }
|
||||
static inline int acpi_subsys_runtime_resume(struct device *dev) { return 0; }
|
||||
@ -1027,6 +1028,10 @@ static inline bool acpi_storage_d3(struct device *dev)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static inline bool acpi_dev_state_d0(struct device *dev)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ACPI) && defined(CONFIG_PM_SLEEP)
|
||||
|
@ -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 */
|
||||
@ -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)
|
||||
@ -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.
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user