From: Rafael J. Wysocki Date: Wed, 29 Jul 2015 21:57:51 +0000 (+0200) Subject: Merge branch 'acpi-scan' into acpi-pm X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=3431e490b50356b56084305a2e93b3a980802b22;p=linux-beck.git Merge branch 'acpi-scan' into acpi-pm Conflicts: drivers/acpi/scan.c The conflict is resolved by moving the just introduced acpi_device_is_first_physical_node() to bus.c and using the existing acpi_companion_match() from there. There will be an additional commit to combine the two. --- 3431e490b50356b56084305a2e93b3a980802b22 diff --cc drivers/acpi/bus.c index 513e7230e3d0,7a3ad929f095..a23903c8bea9 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@@ -423,6 -423,384 +423,413 @@@ static void acpi_bus_notify(acpi_handl acpi_evaluate_ost(handle, type, ost_code, NULL); } + static void acpi_device_notify(acpi_handle handle, u32 event, void *data) + { + struct acpi_device *device = data; + + device->driver->ops.notify(device, event); + } + + static void acpi_device_notify_fixed(void *data) + { + struct acpi_device *device = data; + + /* Fixed hardware devices have no handles */ + acpi_device_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, device); + } + + static u32 acpi_device_fixed_event(void *data) + { + acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_device_notify_fixed, data); + return ACPI_INTERRUPT_HANDLED; + } + + static int acpi_device_install_notify_handler(struct acpi_device *device) + { + acpi_status status; + + if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) + status = + acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, + acpi_device_fixed_event, + device); + else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) + status = + acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, + acpi_device_fixed_event, + device); + else + status = acpi_install_notify_handler(device->handle, + ACPI_DEVICE_NOTIFY, + acpi_device_notify, + device); + + if (ACPI_FAILURE(status)) + return -EINVAL; + return 0; + } + + static void acpi_device_remove_notify_handler(struct acpi_device *device) + { + if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) + acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, + acpi_device_fixed_event); + else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) + acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, + acpi_device_fixed_event); + else + acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, + acpi_device_notify); + } + + /* -------------------------------------------------------------------------- + Device Matching + -------------------------------------------------------------------------- */ + ++/** ++ * acpi_device_is_first_physical_node - Is given dev first physical node ++ * @adev: ACPI companion device ++ * @dev: Physical device to check ++ * ++ * Function checks if given @dev is the first physical devices attached to ++ * the ACPI companion device. This distinction is needed in some cases ++ * where the same companion device is shared between many physical devices. ++ * ++ * Note that the caller have to provide valid @adev pointer. ++ */ ++bool acpi_device_is_first_physical_node(struct acpi_device *adev, ++ const struct device *dev) ++{ ++ bool ret = false; ++ ++ mutex_lock(&adev->physical_node_lock); ++ if (!list_empty(&adev->physical_node_list)) { ++ 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; ++} ++ + /* + * 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); + + /* -------------------------------------------------------------------------- + ACPI Driver Management + -------------------------------------------------------------------------- */ + + /** + * acpi_bus_register_driver - register a driver with the ACPI bus + * @driver: driver being registered + * + * Registers a driver with the ACPI bus. Searches the namespace for all + * devices that match the driver's criteria and binds. Returns zero for + * success or a negative error status for failure. + */ + int acpi_bus_register_driver(struct acpi_driver *driver) + { + int ret; + + if (acpi_disabled) + return -ENODEV; + driver->drv.name = driver->name; + driver->drv.bus = &acpi_bus_type; + driver->drv.owner = driver->owner; + + ret = driver_register(&driver->drv); + return ret; + } + + EXPORT_SYMBOL(acpi_bus_register_driver); + + /** + * acpi_bus_unregister_driver - unregisters a driver with the ACPI bus + * @driver: driver to unregister + * + * Unregisters a driver with the ACPI bus. Searches the namespace for all + * devices that match the driver's criteria and unbinds. + */ + void acpi_bus_unregister_driver(struct acpi_driver *driver) + { + driver_unregister(&driver->drv); + } + + EXPORT_SYMBOL(acpi_bus_unregister_driver); + + /* -------------------------------------------------------------------------- + ACPI Bus operations + -------------------------------------------------------------------------- */ + + static int acpi_bus_match(struct device *dev, struct device_driver *drv) + { + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_driver *acpi_drv = to_acpi_driver(drv); + + return acpi_dev->flags.match_driver + && !acpi_match_device_ids(acpi_dev, acpi_drv->ids); + } + + static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) + { + return __acpi_device_uevent_modalias(to_acpi_device(dev), env); + } + + static int acpi_device_probe(struct device *dev) + { + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver); + int ret; + + if (acpi_dev->handler && !acpi_is_pnp_device(acpi_dev)) + return -EINVAL; + + if (!acpi_drv->ops.add) + return -ENOSYS; + + ret = acpi_drv->ops.add(acpi_dev); + if (ret) + return ret; + + acpi_dev->driver = acpi_drv; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Driver [%s] successfully bound to device [%s]\n", + acpi_drv->name, acpi_dev->pnp.bus_id)); + + if (acpi_drv->ops.notify) { + ret = acpi_device_install_notify_handler(acpi_dev); + if (ret) { + if (acpi_drv->ops.remove) + acpi_drv->ops.remove(acpi_dev); + + acpi_dev->driver = NULL; + acpi_dev->driver_data = NULL; + return ret; + } + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found driver [%s] for device [%s]\n", + acpi_drv->name, acpi_dev->pnp.bus_id)); + get_device(dev); + return 0; + } + + static int acpi_device_remove(struct device * dev) + { + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_driver *acpi_drv = acpi_dev->driver; + + if (acpi_drv) { + if (acpi_drv->ops.notify) + acpi_device_remove_notify_handler(acpi_dev); + if (acpi_drv->ops.remove) + acpi_drv->ops.remove(acpi_dev); + } + acpi_dev->driver = NULL; + acpi_dev->driver_data = NULL; + + put_device(dev); + return 0; + } + + struct bus_type acpi_bus_type = { + .name = "acpi", + .match = acpi_bus_match, + .probe = acpi_device_probe, + .remove = acpi_device_remove, + .uevent = acpi_device_uevent, + }; + /* -------------------------------------------------------------------------- Initialization/Cleanup -------------------------------------------------------------------------- */ diff --cc drivers/acpi/internal.h index f6aefe984941,c529454532dc..df7f7aff327b --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@@ -97,9 -99,14 +99,16 @@@ void acpi_device_add_finalize(struct ac void acpi_free_pnp_ids(struct acpi_device_pnp *pnp); bool acpi_device_is_present(struct acpi_device *adev); bool acpi_device_is_battery(struct acpi_device *adev); +bool acpi_device_is_first_physical_node(struct acpi_device *adev, + const struct device *dev); + /* -------------------------------------------------------------------------- + Device Matching and Notification + -------------------------------------------------------------------------- */ + struct acpi_device *acpi_companion_match(const struct device *dev); + int __acpi_device_uevent_modalias(struct acpi_device *adev, + struct kobj_uevent_env *env); + /* -------------------------------------------------------------------------- Power Resource -------------------------------------------------------------------------- */ diff --cc drivers/acpi/scan.c index 89ff6d2eef8a,2fe5a37c385c..01136b879038 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@@ -115,278 -115,7 +115,6 @@@ int acpi_scan_add_handler_with_hotplug( return 0; } - /** - * create_pnp_modalias - Create hid/cid(s) string for modalias and uevent - * @acpi_dev: ACPI device object. - * @modalias: Buffer to print into. - * @size: Size of the buffer. - * - * Creates hid/cid(s) string needed for modalias and uevent - * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: - * char *modalias: "acpi:IBM0001:ACPI0001" - * Return: 0: no _HID and no _CID - * -EINVAL: output error - * -ENOMEM: output is truncated - */ - static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias, - int size) - { - int len; - int count; - struct acpi_hardware_id *id; - - /* - * Since we skip ACPI_DT_NAMESPACE_HID from the modalias below, 0 should - * be returned if ACPI_DT_NAMESPACE_HID is the only ACPI/PNP ID in the - * device's list. - */ - count = 0; - list_for_each_entry(id, &acpi_dev->pnp.ids, list) - if (strcmp(id->id, ACPI_DT_NAMESPACE_HID)) - count++; - - if (!count) - return 0; - - len = snprintf(modalias, size, "acpi:"); - if (len <= 0) - return len; - - size -= len; - - list_for_each_entry(id, &acpi_dev->pnp.ids, list) { - if (!strcmp(id->id, ACPI_DT_NAMESPACE_HID)) - continue; - - count = snprintf(&modalias[len], size, "%s:", id->id); - if (count < 0) - return -EINVAL; - - if (count >= size) - return -ENOMEM; - - len += count; - size -= count; - } - modalias[len] = '\0'; - return len; - } - - /** - * create_of_modalias - Creates DT compatible string for modalias and uevent - * @acpi_dev: ACPI device object. - * @modalias: Buffer to print into. - * @size: Size of the buffer. - * - * Expose DT compatible modalias as of:NnameTCcompatible. This function should - * only be called for devices having ACPI_DT_NAMESPACE_HID in their list of - * ACPI/PNP IDs. - */ - static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias, - int size) - { - struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; - const union acpi_object *of_compatible, *obj; - int len, count; - int i, nval; - char *c; - - acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf); - /* DT strings are all in lower case */ - for (c = buf.pointer; *c != '\0'; c++) - *c = tolower(*c); - - len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer); - ACPI_FREE(buf.pointer); - - if (len <= 0) - return len; - - of_compatible = acpi_dev->data.of_compatible; - 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; - } - for (i = 0; i < nval; i++, obj++) { - count = snprintf(&modalias[len], size, "C%s", - obj->string.pointer); - if (count < 0) - return -EINVAL; - - if (count >= size) - return -ENOMEM; - - len += count; - size -= count; - } - modalias[len] = '\0'; - return len; - } - - /** - * acpi_device_is_first_physical_node - Is given dev first physical node - * @adev: ACPI companion device - * @dev: Physical device to check - * - * Function checks if given @dev is the first physical devices attached to - * the ACPI companion device. This distinction is needed in some cases - * where the same companion device is shared between many physical devices. - * - * Note that the caller have to provide valid @adev pointer. - */ - bool acpi_device_is_first_physical_node(struct acpi_device *adev, - const struct device *dev) - { - bool ret = false; - - mutex_lock(&adev->physical_node_lock); - if (!list_empty(&adev->physical_node_list)) { - 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; - } - - /* - * 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). - */ - static struct acpi_device *acpi_companion_match(const struct device *dev) - { - struct acpi_device *adev; - - adev = ACPI_COMPANION(dev); - if (!adev) - return NULL; - - if (list_empty(&adev->pnp.ids)) - return NULL; - - return acpi_device_is_first_physical_node(adev, dev) ? adev : NULL; - } - - static int __acpi_device_uevent_modalias(struct acpi_device *adev, - struct kobj_uevent_env *env) - { - int len; - - if (!adev) - return -ENODEV; - - if (list_empty(&adev->pnp.ids)) - return 0; - - if (add_uevent_var(env, "MODALIAS=")) - return -ENOMEM; - - len = create_pnp_modalias(adev, &env->buf[env->buflen - 1], - sizeof(env->buf) - env->buflen); - if (len < 0) - return len; - - env->buflen += len; - if (!adev->data.of_compatible) - return 0; - - if (len > 0 && add_uevent_var(env, "MODALIAS=")) - return -ENOMEM; - - len = create_of_modalias(adev, &env->buf[env->buflen - 1], - sizeof(env->buf) - env->buflen); - if (len < 0) - return len; - - env->buflen += len; - - return 0; - } - - /* - * 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: - * "acpi:IBM0001:ACPI0001" - */ - int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) - { - return __acpi_device_uevent_modalias(acpi_companion_match(dev), env); - } - EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); - - static int __acpi_device_modalias(struct acpi_device *adev, char *buf, int size) - { - int len, count; - - if (!adev) - return -ENODEV; - - if (list_empty(&adev->pnp.ids)) - return 0; - - len = create_pnp_modalias(adev, buf, size - 1); - if (len < 0) { - return len; - } else if (len > 0) { - buf[len++] = '\n'; - size -= len; - } - if (!adev->data.of_compatible) - return len; - - count = create_of_modalias(adev, buf + len, size - 1); - if (count < 0) { - return count; - } else if (count > 0) { - len += count; - buf[len++] = '\n'; - } - - return len; - } - - /* - * Creates modalias sysfs attribute 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: - * "acpi:IBM0001:ACPI0001" - */ - int acpi_device_modalias(struct device *dev, char *buf, int size) - { - return __acpi_device_modalias(acpi_companion_match(dev), buf, size); - } - EXPORT_SYMBOL_GPL(acpi_device_modalias); - - static ssize_t - acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { - return __acpi_device_modalias(to_acpi_device(dev), buf, 1024); - } - static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); -- bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) { struct acpi_device_physical_node *pn;