|
@@ -423,6 +423,200 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
|
|
acpi_evaluate_ost(handle, type, ost_code, NULL);
|
|
acpi_evaluate_ost(handle, type, ost_code, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* --------------------------------------------------------------------------
|
|
|
|
+ Device Matching
|
|
|
|
+ -------------------------------------------------------------------------- */
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * 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. Return the companion pointer if that's the case
|
|
|
|
+ * or NULL otherwise.
|
|
|
|
+ *
|
|
|
|
+ * 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).
|
|
|
|
+ */
|
|
|
|
+struct acpi_device *acpi_companion_match(const struct device *dev)
|
|
|
|
+{
|
|
|
|
+ struct acpi_device *adev;
|
|
|
|
+ struct mutex *physical_node_lock;
|
|
|
|
+
|
|
|
|
+ adev = ACPI_COMPANION(dev);
|
|
|
|
+ if (!adev)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ if (list_empty(&adev->pnp.ids))
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ physical_node_lock = &adev->physical_node_lock;
|
|
|
|
+ mutex_lock(physical_node_lock);
|
|
|
|
+ if (list_empty(&adev->physical_node_list)) {
|
|
|
|
+ adev = NULL;
|
|
|
|
+ } else {
|
|
|
|
+ const struct acpi_device_physical_node *node;
|
|
|
|
+
|
|
|
|
+ node = list_first_entry(&adev->physical_node_list,
|
|
|
|
+ struct acpi_device_physical_node, node);
|
|
|
|
+ if (node->dev != dev)
|
|
|
|
+ adev = NULL;
|
|
|
|
+ }
|
|
|
|
+ mutex_unlock(physical_node_lock);
|
|
|
|
+
|
|
|
|
+ return adev;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * acpi_of_match_device - Match device object using the "compatible" property.
|
|
|
|
+ * @adev: ACPI device object to match.
|
|
|
|
+ * @of_match_table: List of device IDs to match against.
|
|
|
|
+ *
|
|
|
|
+ * If @dev has an ACPI companion which has ACPI_DT_NAMESPACE_HID in its list of
|
|
|
|
+ * identifiers and a _DSD object with the "compatible" property, use that
|
|
|
|
+ * property to match against the given list of identifiers.
|
|
|
|
+ */
|
|
|
|
+static bool acpi_of_match_device(struct acpi_device *adev,
|
|
|
|
+ const struct of_device_id *of_match_table)
|
|
|
|
+{
|
|
|
|
+ const union acpi_object *of_compatible, *obj;
|
|
|
|
+ int i, nval;
|
|
|
|
+
|
|
|
|
+ if (!adev)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ of_compatible = adev->data.of_compatible;
|
|
|
|
+ if (!of_match_table || !of_compatible)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ if (of_compatible->type == ACPI_TYPE_PACKAGE) {
|
|
|
|
+ nval = of_compatible->package.count;
|
|
|
|
+ obj = of_compatible->package.elements;
|
|
|
|
+ } else { /* Must be ACPI_TYPE_STRING. */
|
|
|
|
+ nval = 1;
|
|
|
|
+ obj = of_compatible;
|
|
|
|
+ }
|
|
|
|
+ /* Now we can look for the driver DT compatible strings */
|
|
|
|
+ for (i = 0; i < nval; i++, obj++) {
|
|
|
|
+ const struct of_device_id *id;
|
|
|
|
+
|
|
|
|
+ for (id = of_match_table; id->compatible[0]; id++)
|
|
|
|
+ if (!strcasecmp(obj->string.pointer, id->compatible))
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool __acpi_match_device_cls(const struct acpi_device_id *id,
|
|
|
|
+ struct acpi_hardware_id *hwid)
|
|
|
|
+{
|
|
|
|
+ int i, msk, byte_shift;
|
|
|
|
+ char buf[3];
|
|
|
|
+
|
|
|
|
+ if (!id->cls)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ /* Apply class-code bitmask, before checking each class-code byte */
|
|
|
|
+ for (i = 1; i <= 3; i++) {
|
|
|
|
+ byte_shift = 8 * (3 - i);
|
|
|
|
+ msk = (id->cls_msk >> byte_shift) & 0xFF;
|
|
|
|
+ if (!msk)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ sprintf(buf, "%02x", (id->cls >> byte_shift) & msk);
|
|
|
|
+ if (strncmp(buf, &hwid->id[(i - 1) * 2], 2))
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const struct acpi_device_id *__acpi_match_device(
|
|
|
|
+ struct acpi_device *device,
|
|
|
|
+ const struct acpi_device_id *ids,
|
|
|
|
+ const struct of_device_id *of_ids)
|
|
|
|
+{
|
|
|
|
+ const struct acpi_device_id *id;
|
|
|
|
+ struct acpi_hardware_id *hwid;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * If the device is not present, it is unnecessary to load device
|
|
|
|
+ * driver for it.
|
|
|
|
+ */
|
|
|
|
+ if (!device || !device->status.present)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ list_for_each_entry(hwid, &device->pnp.ids, list) {
|
|
|
|
+ /* First, check the ACPI/PNP IDs provided by the caller. */
|
|
|
|
+ for (id = ids; id->id[0] || id->cls; id++) {
|
|
|
|
+ if (id->id[0] && !strcmp((char *) id->id, hwid->id))
|
|
|
|
+ return id;
|
|
|
|
+ else if (id->cls && __acpi_match_device_cls(id, hwid))
|
|
|
|
+ return id;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Next, check ACPI_DT_NAMESPACE_HID and try to match the
|
|
|
|
+ * "compatible" property if found.
|
|
|
|
+ *
|
|
|
|
+ * The id returned by the below is not valid, but the only
|
|
|
|
+ * caller passing non-NULL of_ids here is only interested in
|
|
|
|
+ * whether or not the return value is NULL.
|
|
|
|
+ */
|
|
|
|
+ if (!strcmp(ACPI_DT_NAMESPACE_HID, hwid->id)
|
|
|
|
+ && acpi_of_match_device(device, of_ids))
|
|
|
|
+ return id;
|
|
|
|
+ }
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * acpi_match_device - Match a struct device against a given list of ACPI IDs
|
|
|
|
+ * @ids: Array of struct acpi_device_id object to match against.
|
|
|
|
+ * @dev: The device structure to match.
|
|
|
|
+ *
|
|
|
|
+ * Check if @dev has a valid ACPI handle and if there is a struct acpi_device
|
|
|
|
+ * object for that handle and use that object to match against a given list of
|
|
|
|
+ * device IDs.
|
|
|
|
+ *
|
|
|
|
+ * Return a pointer to the first matching ID on success or %NULL on failure.
|
|
|
|
+ */
|
|
|
|
+const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids,
|
|
|
|
+ const struct device *dev)
|
|
|
|
+{
|
|
|
|
+ return __acpi_match_device(acpi_companion_match(dev), ids, NULL);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(acpi_match_device);
|
|
|
|
+
|
|
|
|
+int acpi_match_device_ids(struct acpi_device *device,
|
|
|
|
+ const struct acpi_device_id *ids)
|
|
|
|
+{
|
|
|
|
+ return __acpi_match_device(device, ids, NULL) ? 0 : -ENOENT;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(acpi_match_device_ids);
|
|
|
|
+
|
|
|
|
+bool acpi_driver_match_device(struct device *dev,
|
|
|
|
+ const struct device_driver *drv)
|
|
|
|
+{
|
|
|
|
+ if (!drv->acpi_match_table)
|
|
|
|
+ return acpi_of_match_device(ACPI_COMPANION(dev),
|
|
|
|
+ drv->of_match_table);
|
|
|
|
+
|
|
|
|
+ return !!__acpi_match_device(acpi_companion_match(dev),
|
|
|
|
+ drv->acpi_match_table, drv->of_match_table);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(acpi_driver_match_device);
|
|
|
|
+
|
|
/* --------------------------------------------------------------------------
|
|
/* --------------------------------------------------------------------------
|
|
Initialization/Cleanup
|
|
Initialization/Cleanup
|
|
-------------------------------------------------------------------------- */
|
|
-------------------------------------------------------------------------- */
|