Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ACPI / PM: Attach ACPI power domain only once

Some devices, like MFD subdevices, share a single ACPI companion device so
that they are able to access their resources and children. However,
currently all these subdevices are attached to the ACPI power domain and
this might cause that the power methods for the companion device get called
more than once.

In order to solve this we attach the ACPI power domain only to the first
physical device that is bound to the ACPI companion device. In case of MFD
devices, this is the parent MFD device itself.

Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>

authored by

Mika Westerberg and committed by
Lee Jones
712e960f 13b2c4a0

+40 -16
+8
drivers/acpi/device_pm.c
··· 1123 1123 if (dev->pm_domain) 1124 1124 return -EEXIST; 1125 1125 1126 + /* 1127 + * Only attach the power domain to the first device if the 1128 + * companion is shared by multiple. This is to prevent doing power 1129 + * management twice. 1130 + */ 1131 + if (!acpi_device_is_first_physical_node(adev, dev)) 1132 + return -EBUSY; 1133 + 1126 1134 acpi_add_pm_notifier(adev, dev, acpi_pm_notify_work_func); 1127 1135 dev->pm_domain = &acpi_general_pm_domain; 1128 1136 if (power_on) {
+2
drivers/acpi/internal.h
··· 97 97 void acpi_free_pnp_ids(struct acpi_device_pnp *pnp); 98 98 bool acpi_device_is_present(struct acpi_device *adev); 99 99 bool acpi_device_is_battery(struct acpi_device *adev); 100 + bool acpi_device_is_first_physical_node(struct acpi_device *adev, 101 + const struct device *dev); 100 102 101 103 /* -------------------------------------------------------------------------- 102 104 Power Resource
+30 -16
drivers/acpi/scan.c
··· 226 226 return len; 227 227 } 228 228 229 + /** 230 + * acpi_device_is_first_physical_node - Is given dev first physical node 231 + * @adev: ACPI companion device 232 + * @dev: Physical device to check 233 + * 234 + * Function checks if given @dev is the first physical devices attached to 235 + * the ACPI companion device. This distinction is needed in some cases 236 + * where the same companion device is shared between many physical devices. 237 + * 238 + * Note that the caller have to provide valid @adev pointer. 239 + */ 240 + bool acpi_device_is_first_physical_node(struct acpi_device *adev, 241 + const struct device *dev) 242 + { 243 + bool ret = false; 244 + 245 + mutex_lock(&adev->physical_node_lock); 246 + if (!list_empty(&adev->physical_node_list)) { 247 + const struct acpi_device_physical_node *node; 248 + 249 + node = list_first_entry(&adev->physical_node_list, 250 + struct acpi_device_physical_node, node); 251 + ret = node->dev == dev; 252 + } 253 + mutex_unlock(&adev->physical_node_lock); 254 + 255 + return ret; 256 + } 257 + 229 258 /* 230 259 * acpi_companion_match() - Can we match via ACPI companion device 231 260 * @dev: Device in question ··· 279 250 static struct acpi_device *acpi_companion_match(const struct device *dev) 280 251 { 281 252 struct acpi_device *adev; 282 - struct mutex *physical_node_lock; 283 253 284 254 adev = ACPI_COMPANION(dev); 285 255 if (!adev) ··· 287 259 if (list_empty(&adev->pnp.ids)) 288 260 return NULL; 289 261 290 - physical_node_lock = &adev->physical_node_lock; 291 - mutex_lock(physical_node_lock); 292 - if (list_empty(&adev->physical_node_list)) { 293 - adev = NULL; 294 - } else { 295 - const struct acpi_device_physical_node *node; 296 - 297 - node = list_first_entry(&adev->physical_node_list, 298 - struct acpi_device_physical_node, node); 299 - if (node->dev != dev) 300 - adev = NULL; 301 - } 302 - mutex_unlock(physical_node_lock); 303 - 304 - return adev; 262 + return acpi_device_is_first_physical_node(adev, dev) ? adev : NULL; 305 263 } 306 264 307 265 static int __acpi_device_uevent_modalias(struct acpi_device *adev,