diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/ec.c | 31 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 70 |
2 files changed, 75 insertions, 26 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 3d304ff7f095..5f9b74b9b71f 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -126,6 +126,7 @@ static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */ static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */ static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ +static int EC_FLAGS_QUERY_HANDSHAKE; /* Needs QR_EC issued when SCI_EVT set */ /* -------------------------------------------------------------------------- * Transaction Management @@ -236,13 +237,8 @@ static bool advance_transaction(struct acpi_ec *ec) } return wakeup; } else { - /* - * There is firmware refusing to respond QR_EC when SCI_EVT - * is not set, for which case, we complete the QR_EC - * without issuing it to the firmware. - * https://bugzilla.kernel.org/show_bug.cgi?id=86211 - */ - if (!(status & ACPI_EC_FLAG_SCI) && + if (EC_FLAGS_QUERY_HANDSHAKE && + !(status & ACPI_EC_FLAG_SCI) && (t->command == ACPI_EC_COMMAND_QUERY)) { t->flags |= ACPI_EC_COMMAND_POLL; t->rdata[t->ri++] = 0x00; @@ -334,13 +330,13 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, pr_debug("***** Command(%s) started *****\n", acpi_ec_cmd_string(t->command)); start_transaction(ec); - spin_unlock_irqrestore(&ec->lock, tmp); - ret = ec_poll(ec); - spin_lock_irqsave(&ec->lock, tmp); if (ec->curr->command == ACPI_EC_COMMAND_QUERY) { clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); pr_debug("***** Event stopped *****\n"); } + spin_unlock_irqrestore(&ec->lock, tmp); + ret = ec_poll(ec); + spin_lock_irqsave(&ec->lock, tmp); pr_debug("***** Command(%s) stopped *****\n", acpi_ec_cmd_string(t->command)); ec->curr = NULL; @@ -1012,6 +1008,18 @@ static int ec_enlarge_storm_threshold(const struct dmi_system_id *id) } /* + * Acer EC firmware refuses to respond QR_EC when SCI_EVT is not set, for + * which case, we complete the QR_EC without issuing it to the firmware. + * https://bugzilla.kernel.org/show_bug.cgi?id=86211 + */ +static int ec_flag_query_handshake(const struct dmi_system_id *id) +{ + pr_debug("Detected the EC firmware requiring QR_EC issued when SCI_EVT set\n"); + EC_FLAGS_QUERY_HANDSHAKE = 1; + return 0; +} + +/* * On some hardware it is necessary to clear events accumulated by the EC during * sleep. These ECs stop reporting GPEs until they are manually polled, if too * many events are accumulated. (e.g. Samsung Series 5/9 notebooks) @@ -1085,6 +1093,9 @@ static struct dmi_system_id ec_dmi_table[] __initdata = { { ec_clear_on_resume, "Samsung hardware", { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD.")}, NULL}, + { + ec_flag_query_handshake, "Acer hardware", { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), }, NULL}, {}, }; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d670158a26c5..0476e90b2091 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -142,6 +142,53 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias, } /* + * acpi_companion_match() - Can we match via ACPI companion device + * @dev: Device in question + * + * Check if the given device has an ACPI companion and if that companion has + * a valid list of PNP IDs, and if the device is the first (primary) physical + * device associated with it. + * + * If multiple physical devices are attached to a single ACPI companion, we need + * to be careful. The usage scenario for this kind of relationship is that all + * of the physical devices in question use resources provided by the ACPI + * companion. A typical case is an MFD device where all the sub-devices share + * the parent's ACPI companion. In such cases we can only allow the primary + * (first) physical device to be matched with the help of the companion's PNP + * IDs. + * + * Additional physical devices sharing the ACPI companion can still use + * resources available from it but they will be matched normally using functions + * provided by their bus types (and analogously for their modalias). + */ +static bool acpi_companion_match(const struct device *dev) +{ + struct acpi_device *adev; + bool ret; + + adev = ACPI_COMPANION(dev); + if (!adev) + return false; + + if (list_empty(&adev->pnp.ids)) + return false; + + mutex_lock(&adev->physical_node_lock); + if (list_empty(&adev->physical_node_list)) { + ret = false; + } else { + const struct acpi_device_physical_node *node; + + node = list_first_entry(&adev->physical_node_list, + struct acpi_device_physical_node, node); + ret = node->dev == dev; + } + mutex_unlock(&adev->physical_node_lock); + + return ret; +} + +/* * Creates uevent modalias field for ACPI enumerated devices. * Because the other buses does not support ACPI HIDs & CIDs. * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: @@ -149,20 +196,14 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias, */ int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) { - struct acpi_device *acpi_dev; int len; - acpi_dev = ACPI_COMPANION(dev); - if (!acpi_dev) - return -ENODEV; - - /* Fall back to bus specific way of modalias exporting */ - if (list_empty(&acpi_dev->pnp.ids)) + if (!acpi_companion_match(dev)) return -ENODEV; if (add_uevent_var(env, "MODALIAS=")) return -ENOMEM; - len = create_modalias(acpi_dev, &env->buf[env->buflen - 1], + len = create_modalias(ACPI_COMPANION(dev), &env->buf[env->buflen - 1], sizeof(env->buf) - env->buflen); if (len <= 0) return len; @@ -179,18 +220,12 @@ EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); */ int acpi_device_modalias(struct device *dev, char *buf, int size) { - struct acpi_device *acpi_dev; int len; - acpi_dev = ACPI_COMPANION(dev); - if (!acpi_dev) + if (!acpi_companion_match(dev)) return -ENODEV; - /* Fall back to bus specific way of modalias exporting */ - if (list_empty(&acpi_dev->pnp.ids)) - return -ENODEV; - - len = create_modalias(acpi_dev, buf, size -1); + len = create_modalias(ACPI_COMPANION(dev), buf, size -1); if (len <= 0) return len; buf[len++] = '\n'; @@ -853,6 +888,9 @@ const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, if (!ids || !handle || acpi_bus_get_device(handle, &adev)) return NULL; + if (!acpi_companion_match(dev)) + return NULL; + return __acpi_match_device(adev, ids); } EXPORT_SYMBOL_GPL(acpi_match_device); |