diff --git a/Documentation/firmware-guide/acpi/index.rst b/Documentation/firmware-guide/acpi/index.rst index a99ee402b212..b053b0c3d696 100644 --- a/Documentation/firmware-guide/acpi/index.rst +++ b/Documentation/firmware-guide/acpi/index.rst @@ -26,5 +26,6 @@ ACPI Support acpi-lid lpit video_extension + non-d0-probe extcon-intel-int3496 intel-pmc-mux diff --git a/Documentation/firmware-guide/acpi/non-d0-probe.rst b/Documentation/firmware-guide/acpi/non-d0-probe.rst new file mode 100644 index 000000000000..7afd16701a02 --- /dev/null +++ b/Documentation/firmware-guide/acpi/non-d0-probe.rst @@ -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) + } + } diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 0028b6b51c87..19b33c028f35 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -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 */ diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index e629e891d1bb..a6366d3f0c78 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -133,7 +133,7 @@ static unsigned int ec_storm_threshold __read_mostly = 8; module_param(ec_storm_threshold, uint, 0644); MODULE_PARM_DESC(ec_storm_threshold, "Maxim false GPE numbers not considered as GPE storm"); -static bool ec_freeze_events __read_mostly = false; +static bool ec_freeze_events __read_mostly; module_param(ec_freeze_events, bool, 0644); MODULE_PARM_DESC(ec_freeze_events, "Disabling event handling during suspend/resume"); @@ -177,7 +177,7 @@ struct acpi_ec *first_ec; EXPORT_SYMBOL(first_ec); static struct acpi_ec *boot_ec; -static bool boot_ec_is_ecdt = false; +static bool boot_ec_is_ecdt; static struct workqueue_struct *ec_wq; static struct workqueue_struct *ec_query_wq; @@ -2152,6 +2152,13 @@ static const struct dmi_system_id acpi_ec_no_wakeup[] = { DMI_MATCH(DMI_PRODUCT_FAMILY, "ThinkPad X1 Yoga 3rd"), }, }, + { + .ident = "HP ZHAN 66 Pro", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_FAMILY, "103C_5336AN HP ZHAN 66 Pro"), + }, + }, { }, }; diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c index a371f273f99d..9cde299eba88 100644 --- a/drivers/acpi/pmic/intel_pmic.c +++ b/drivers/acpi/pmic/intel_pmic.c @@ -211,31 +211,36 @@ static acpi_status intel_pmic_regs_handler(u32 function, void *handler_context, void *region_context) { struct intel_pmic_opregion *opregion = region_context; - int result = 0; + int result = -EINVAL; - switch (address) { - case 0: - return AE_OK; - case 1: - opregion->ctx.addr |= (*value64 & 0xff) << 8; - return AE_OK; - case 2: - opregion->ctx.addr |= *value64 & 0xff; - return AE_OK; - case 3: - opregion->ctx.val = *value64 & 0xff; - return AE_OK; - case 4: - if (*value64) { - result = regmap_write(opregion->regmap, opregion->ctx.addr, - opregion->ctx.val); - } else { - result = regmap_read(opregion->regmap, opregion->ctx.addr, - &opregion->ctx.val); - if (result == 0) - *value64 = opregion->ctx.val; + if (function == ACPI_WRITE) { + switch (address) { + case 0: + return AE_OK; + case 1: + opregion->ctx.addr |= (*value64 & 0xff) << 8; + return AE_OK; + case 2: + opregion->ctx.addr |= *value64 & 0xff; + return AE_OK; + case 3: + opregion->ctx.val = *value64 & 0xff; + return AE_OK; + case 4: + if (*value64) { + result = regmap_write(opregion->regmap, opregion->ctx.addr, + opregion->ctx.val); + } else { + result = regmap_read(opregion->regmap, opregion->ctx.addr, + &opregion->ctx.val); + } + opregion->ctx.addr = 0; } - memset(&opregion->ctx, 0x00, sizeof(opregion->ctx)); + } + + if (function == ACPI_READ && address == 3) { + *value64 = opregion->ctx.val; + return AE_OK; } if (result < 0) { diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 112256154880..5dcb02ededbc 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -757,13 +757,11 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) mutex_lock(&acpi_device_lock); - if (dev->wakeup.prepare_count > 1) { - dev->wakeup.prepare_count--; - goto out; - } - /* Do nothing if wakeup power has not been enabled for this device. */ - if (!dev->wakeup.prepare_count) + if (dev->wakeup.prepare_count <= 0) + goto out; + + if (--dev->wakeup.prepare_count > 0) goto out; err = acpi_device_sleep_wake(dev, 0, 0, 0); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index dce2c291b982..a50f1967c73d 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -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 */ diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 33474fd96991..068e393ea0c6 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -115,7 +115,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { */ { .callback = video_detect_force_vendor, - .ident = "X360", + /* X360 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), DMI_MATCH(DMI_PRODUCT_NAME, "X360"), @@ -124,7 +124,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_vendor, - .ident = "Asus UL30VT", + /* Asus UL30VT */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "UL30VT"), @@ -132,7 +132,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_vendor, - .ident = "Asus UL30A", + /* Asus UL30A */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "UL30A"), @@ -140,7 +140,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_vendor, - .ident = "GIGABYTE GB-BXBT-2807", + /* GIGABYTE GB-BXBT-2807 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), DMI_MATCH(DMI_PRODUCT_NAME, "GB-BXBT-2807"), @@ -148,12 +148,20 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_vendor, - .ident = "Sony VPCEH3U1E", + /* Sony VPCEH3U1E */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), DMI_MATCH(DMI_PRODUCT_NAME, "VPCEH3U1E"), }, }, + { + .callback = video_detect_force_vendor, + /* Xiaomi Mi Pad 2 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"), + DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"), + }, + }, /* * These models have a working acpi_video backlight control, and using @@ -164,7 +172,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { */ { .callback = video_detect_force_video, - .ident = "ThinkPad T420", + /* ThinkPad T420 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T420"), @@ -172,7 +180,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_video, - .ident = "ThinkPad T520", + /* ThinkPad T520 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T520"), @@ -180,7 +188,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_video, - .ident = "ThinkPad X201s", + /* ThinkPad X201s */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"), @@ -188,7 +196,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_video, - .ident = "ThinkPad X201T", + /* ThinkPad X201T */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201T"), @@ -199,7 +207,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { { /* https://bugs.freedesktop.org/show_bug.cgi?id=81515 */ .callback = video_detect_force_video, - .ident = "HP ENVY 15 Notebook", + /* HP ENVY 15 Notebook */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY 15 Notebook PC"), @@ -207,7 +215,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_video, - .ident = "SAMSUNG 870Z5E/880Z5E/680Z5E", + /* SAMSUNG 870Z5E/880Z5E/680Z5E */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), DMI_MATCH(DMI_PRODUCT_NAME, "870Z5E/880Z5E/680Z5E"), @@ -215,7 +223,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_video, - .ident = "SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V", + /* SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), DMI_MATCH(DMI_PRODUCT_NAME, @@ -225,7 +233,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { { /* https://bugzilla.redhat.com/show_bug.cgi?id=1186097 */ .callback = video_detect_force_video, - .ident = "SAMSUNG 3570R/370R/470R/450R/510R/4450RV", + /* SAMSUNG 3570R/370R/470R/450R/510R/4450RV */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), DMI_MATCH(DMI_PRODUCT_NAME, @@ -235,7 +243,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { { /* https://bugzilla.redhat.com/show_bug.cgi?id=1557060 */ .callback = video_detect_force_video, - .ident = "SAMSUNG 670Z5E", + /* SAMSUNG 670Z5E */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), DMI_MATCH(DMI_PRODUCT_NAME, "670Z5E"), @@ -244,7 +252,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { { /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */ .callback = video_detect_force_video, - .ident = "SAMSUNG 730U3E/740U3E", + /* SAMSUNG 730U3E/740U3E */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), @@ -253,7 +261,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { { /* https://bugs.freedesktop.org/show_bug.cgi?id=87286 */ .callback = video_detect_force_video, - .ident = "SAMSUNG 900X3C/900X3D/900X3E/900X4C/900X4D", + /* SAMSUNG 900X3C/900X3D/900X3E/900X4C/900X4D */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), DMI_MATCH(DMI_PRODUCT_NAME, @@ -263,7 +271,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { { /* https://bugzilla.redhat.com/show_bug.cgi?id=1272633 */ .callback = video_detect_force_video, - .ident = "Dell XPS14 L421X", + /* Dell XPS14 L421X */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "XPS L421X"), @@ -272,7 +280,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { { /* https://bugzilla.redhat.com/show_bug.cgi?id=1163574 */ .callback = video_detect_force_video, - .ident = "Dell XPS15 L521X", + /* Dell XPS15 L521X */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"), @@ -281,7 +289,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { { /* https://bugzilla.kernel.org/show_bug.cgi?id=108971 */ .callback = video_detect_force_video, - .ident = "SAMSUNG 530U4E/540U4E", + /* SAMSUNG 530U4E/540U4E */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), DMI_MATCH(DMI_PRODUCT_NAME, "530U4E/540U4E"), @@ -290,7 +298,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { /* https://bugs.launchpad.net/bugs/1894667 */ { .callback = video_detect_force_video, - .ident = "HP 635 Notebook", + /* HP 635 Notebook */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "HP 635 Notebook PC"), @@ -301,7 +309,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { { /* https://bugzilla.redhat.com/show_bug.cgi?id=1201530 */ .callback = video_detect_force_native, - .ident = "Lenovo Ideapad S405", + /* Lenovo Ideapad S405 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_BOARD_NAME, "Lenovo IdeaPad S405"), @@ -310,7 +318,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { { /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */ .callback = video_detect_force_native, - .ident = "Lenovo Ideapad Z570", + /* Lenovo Ideapad Z570 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "102434U"), @@ -318,7 +326,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_native, - .ident = "Lenovo E41-25", + /* Lenovo E41-25 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "81FS"), @@ -326,7 +334,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_native, - .ident = "Lenovo E41-45", + /* Lenovo E41-45 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "82BK"), @@ -335,7 +343,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { { /* https://bugzilla.redhat.com/show_bug.cgi?id=1217249 */ .callback = video_detect_force_native, - .ident = "Apple MacBook Pro 12,1", + /* Apple MacBook Pro 12,1 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"), @@ -343,7 +351,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_native, - .ident = "Dell Vostro V131", + /* Dell Vostro V131 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), @@ -352,7 +360,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { { /* https://bugzilla.redhat.com/show_bug.cgi?id=1123661 */ .callback = video_detect_force_native, - .ident = "Dell XPS 17 L702X", + /* Dell XPS 17 L702X */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L702X"), @@ -360,7 +368,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_native, - .ident = "Dell Precision 7510", + /* Dell Precision 7510 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Precision 7510"), @@ -368,7 +376,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_native, - .ident = "Acer Aspire 5738z", + /* Acer Aspire 5738z */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5738"), @@ -378,7 +386,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { { /* https://bugzilla.kernel.org/show_bug.cgi?id=207835 */ .callback = video_detect_force_native, - .ident = "Acer TravelMate 5735Z", + /* Acer TravelMate 5735Z */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5735Z"), @@ -387,7 +395,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_native, - .ident = "ASUSTeK COMPUTER INC. GA401", + /* ASUSTeK COMPUTER INC. GA401 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "GA401"), @@ -395,7 +403,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_native, - .ident = "ASUSTeK COMPUTER INC. GA502", + /* ASUSTeK COMPUTER INC. GA502 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "GA502"), @@ -403,7 +411,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_native, - .ident = "ASUSTeK COMPUTER INC. GA503", + /* ASUSTeK COMPUTER INC. GA503 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "GA503"), @@ -416,7 +424,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { */ { .callback = video_detect_force_none, - .ident = "Dell OptiPlex 9020M", + /* Dell OptiPlex 9020M */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 9020M"), @@ -424,7 +432,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, { .callback = video_detect_force_none, - .ident = "MSI MS-7721", + /* MSI MS-7721 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MSI"), DMI_MATCH(DMI_PRODUCT_NAME, "MS-7721"), diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 80631f93ad2f..92c1cc07ed46 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -522,6 +522,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) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 54964fbe3f03..f193f9058584 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -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); diff --git a/drivers/media/i2c/imx319.c b/drivers/media/i2c/imx319.c index dba0854ab5aa..daa976858e29 100644 --- a/drivers/media/i2c/imx319.c +++ b/drivers/media/i2c/imx319.c @@ -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); diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 305ffad131a2..49ab656e8a96 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -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) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 53b6e9f9de7b..480f9207a4c6 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -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 { diff --git a/include/acpi/platform/acgcc.h b/include/acpi/platform/acgcc.h index 20ecb004f5a4..33ad282bd338 100644 --- a/include/acpi/platform/acgcc.h +++ b/include/acpi/platform/acgcc.h @@ -10,25 +10,12 @@ #ifndef __ACGCC_H__ #define __ACGCC_H__ -/* - * Use compiler specific is a good practice for even when - * -nostdinc is specified (i.e., ACPI_USE_STANDARD_HEADERS undefined. - */ #ifndef va_arg -#ifdef ACPI_USE_BUILTIN_STDARG -typedef __builtin_va_list va_list; -#define va_start(v, l) __builtin_va_start(v, l) -#define va_end(v) __builtin_va_end(v) -#define va_arg(v, l) __builtin_va_arg(v, l) -#define va_copy(d, s) __builtin_va_copy(d, s) -#else #ifdef __KERNEL__ #include #else -/* Used to build acpi tools */ #include #endif /* __KERNEL__ */ -#endif /* ACPI_USE_BUILTIN_STDARG */ #endif /* ! va_arg */ #define ACPI_INLINE __inline__ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 375715b0535f..143ce7e0bee1 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1014,6 +1014,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; } @@ -1025,6 +1026,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) diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 2ce3efbe9198..16119ac1aa97 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -11,6 +11,7 @@ #define _LINUX_I2C_H #include /* for acpi_handle */ +#include #include #include /* for struct device */ #include /* 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 */