Commit 6173176c authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branches 'acpi-property', 'acpi-resource', 'acpi-pm' and 'acpi-tables'

Merge updates of the ACPI device properties management code, ACPI
resources management code, ACPI power management, and ACPI data tables
parsing code for 6.18-rc1:

 - Fix ACPI buffer properties extraction for data-only subnodes
   represented as _DSD-equivalent packages (Rafael Wysocki)

 - Fix handling of ACPI data-only subnodes represented as _DSD-equivalent
   packages in the case when they are embedded in larger _DSD-equivalent
   packages and clean up acpi_nondev_subnode_extract() (Rafael Wysocki)

 - Skip ACPI IRQ override on ASUS Vivobook Pro N6506CU (Sam van Kampen)

 - Add power resource init function and use it for introducing an HP
   EliteBook 855 G7 WWAN modem power resource quirk (Maciej Szmigiero)

 - Add support for DBG2 RISC-V SBI port subtype and Precise Baud Rate
   field to the ACPI SPCR table parser (Chen Pei)

* acpi-property:
  ACPI: property: Adjust failure handling in acpi_nondev_subnode_extract()
  ACPI: property: Do not pass NULL handles to acpi_attach_data()
  ACPI: property: Add code comments explaining what is going on
  ACPI: property: Disregard references in data-only subnode lists
  ACPI: property: Fix buffer properties extraction for subnodes

* acpi-resource:
  ACPI: resource: Skip IRQ override on ASUS Vivobook Pro N6506CU

* acpi-pm:
  ACPI: PM: Add HP EliteBook 855 G7 WWAN modem power resource quirk
  ACPI: PM: Add power resource init function

* acpi-tables:
  ACPI: SPCR: Support Precise Baud Rate field
  ACPI: SPCR: Add support for DBG2 RISC-V SBI port subtype
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -140,6 +140,7 @@ int __acpi_device_uevent_modalias(const struct acpi_device *adev,
/* --------------------------------------------------------------------------
                                  Power Resource
   -------------------------------------------------------------------------- */
void acpi_power_resources_init(void);
void acpi_power_resources_list_free(struct list_head *list);
int acpi_extract_power_resources(union acpi_object *package, unsigned int start,
				 struct list_head *list);
+87 −3
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@

#define pr_fmt(fmt) "ACPI: PM: " fmt

#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -63,6 +64,9 @@ struct acpi_power_resource_entry {
	struct acpi_power_resource *resource;
};

static bool hp_eb_gp12pxp_quirk;
static bool unused_power_resources_quirk;

static LIST_HEAD(acpi_power_resource_list);
static DEFINE_MUTEX(power_resource_list_lock);

@@ -992,6 +996,38 @@ struct acpi_device *acpi_add_power_resource(acpi_handle handle)
}

#ifdef CONFIG_ACPI_SLEEP
static bool resource_is_gp12pxp(acpi_handle handle)
{
	const char *path;
	bool ret;

	path = acpi_handle_path(handle);
	ret = path && strcmp(path, "\\_SB_.PCI0.GP12.PXP_") == 0;
	kfree(path);

	return ret;
}

static void acpi_resume_on_eb_gp12pxp(struct acpi_power_resource *resource)
{
	acpi_handle_notice(resource->device.handle,
			   "HP EB quirk - turning OFF then ON\n");

	__acpi_power_off(resource);
	__acpi_power_on(resource);

	/*
	 * Use the same delay as DSDT uses in modem _RST method.
	 *
	 * Otherwise we get "Unable to change power state from unknown to D0,
	 * device inaccessible" error for the modem PCI device after thaw.
	 *
	 * This power resource is normally being enabled only during thaw (once)
	 * so this wait is not a performance issue.
	 */
	msleep(200);
}

void acpi_resume_power_resources(void)
{
	struct acpi_power_resource *resource;
@@ -1013,9 +1049,15 @@ void acpi_resume_power_resources(void)

		if (state == ACPI_POWER_RESOURCE_STATE_OFF
		    && resource->ref_count) {
			acpi_handle_debug(resource->device.handle, "Turning ON\n");
			if (hp_eb_gp12pxp_quirk &&
			    resource_is_gp12pxp(resource->device.handle)) {
				acpi_resume_on_eb_gp12pxp(resource);
			} else {
				acpi_handle_debug(resource->device.handle,
						  "Turning ON\n");
				__acpi_power_on(resource);
			}
		}

		mutex_unlock(&resource->resource_lock);
	}
@@ -1024,6 +1066,41 @@ void acpi_resume_power_resources(void)
}
#endif

static const struct dmi_system_id dmi_hp_elitebook_gp12pxp_quirk[] = {
/*
 * This laptop (and possibly similar models too) has power resource called
 * "GP12.PXP_" for its WWAN modem.
 *
 * For this power resource to turn ON power for the modem it needs certain
 * internal flag called "ONEN" to be set.
 * This flag only gets set from this power resource "_OFF" method, while the
 * actual modem power gets turned off during suspend by "GP12.PTS" method
 * called from the global "_PTS" (Prepare To Sleep) method.
 * On the other hand, this power resource "_OFF" method implementation just
 * sets the aforementioned flag without actually doing anything else (it
 * doesn't contain any code to actually turn off power).
 *
 * The above means that when upon hibernation finish we try to set this
 * power resource back ON since its "_STA" method returns 0 (while the resource
 * is still considered in use) its "_ON" method won't do anything since
 * that "ONEN" flag is not set.
 * Overall, this means the modem is dead until laptop is rebooted since its
 * power has been cut by "_PTS" and its PCI configuration was lost and not able
 * to be restored.
 *
 * The easiest way to workaround the issue is to call this power resource
 * "_OFF" method before calling the "_ON" method to make sure the "ONEN"
 * flag gets properly set.
 */
	{
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "HP"),
			DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 855 G7 Notebook PC"),
		},
	},
	{}
};

static const struct dmi_system_id dmi_leave_unused_power_resources_on[] = {
	{
		/*
@@ -1046,7 +1123,7 @@ void acpi_turn_off_unused_power_resources(void)
{
	struct acpi_power_resource *resource;

	if (dmi_check_system(dmi_leave_unused_power_resources_on))
	if (unused_power_resources_quirk)
		return;

	mutex_lock(&power_resource_list_lock);
@@ -1065,3 +1142,10 @@ void acpi_turn_off_unused_power_resources(void)

	mutex_unlock(&power_resource_list_lock);
}

void __init acpi_power_resources_init(void)
{
	hp_eb_gp12pxp_quirk = dmi_check_system(dmi_hp_elitebook_gp12pxp_quirk);
	unused_power_resources_quirk =
		dmi_check_system(dmi_leave_unused_power_resources_on);
}
+96 −56
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ static bool acpi_nondev_subnode_extract(union acpi_object *desc,
					struct fwnode_handle *parent)
{
	struct acpi_data_node *dn;
	acpi_handle scope = NULL;
	bool result;

	if (acpi_graph_ignore_port(handle))
@@ -98,59 +99,45 @@ static bool acpi_nondev_subnode_extract(union acpi_object *desc,
	INIT_LIST_HEAD(&dn->data.properties);
	INIT_LIST_HEAD(&dn->data.subnodes);

	result = acpi_extract_properties(handle, desc, &dn->data);

	if (handle) {
		acpi_handle scope;
		acpi_status status;
	/*
	 * The scope for the completion of relative pathname segments and
	 * subnode object lookup is the one of the namespace node (device)
	 * containing the object that has returned the package.  That is, it's
	 * the scope of that object's parent device.
	 */
	if (handle)
		acpi_get_parent(handle, &scope);

	/*
		 * The scope for the subnode object lookup is the one of the
		 * namespace node (device) containing the object that has
		 * returned the package.  That is, it's the scope of that
		 * object's parent.
	 * Extract properties from the _DSD-equivalent package pointed to by
	 * desc and use scope (if not NULL) for the completion of relative
	 * pathname segments.
	 *
	 * The extracted properties will be held in the new data node dn.
	 */
		status = acpi_get_parent(handle, &scope);
		if (ACPI_SUCCESS(status)
		    && acpi_enumerate_nondev_subnodes(scope, desc, &dn->data,
						      &dn->fwnode))
			result = true;
	} else if (acpi_enumerate_nondev_subnodes(NULL, desc, &dn->data,
						  &dn->fwnode)) {
	result = acpi_extract_properties(scope, desc, &dn->data);
	/*
	 * Look for subnodes in the _DSD-equivalent package pointed to by desc
	 * and create child nodes of dn if there are any.
	 */
	if (acpi_enumerate_nondev_subnodes(scope, desc, &dn->data, &dn->fwnode))
		result = true;
	}

	if (result) {
		dn->handle = handle;
		dn->data.pointer = desc;
		list_add_tail(&dn->sibling, list);
		return true;
	}

	if (!result) {
		kfree(dn);
		acpi_handle_debug(handle, "Invalid properties/subnodes data, skipping\n");
		return false;
	}

static bool acpi_nondev_subnode_data_ok(acpi_handle handle,
					const union acpi_object *link,
					struct list_head *list,
					struct fwnode_handle *parent)
{
	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
	acpi_status status;

	status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf,
					    ACPI_TYPE_PACKAGE);
	if (ACPI_FAILURE(status))
		return false;
	/*
	 * This will be NULL if the desc package is embedded in an outer
	 * _DSD-equivalent package and its scope cannot be determined.
	 */
	dn->handle = handle;
	dn->data.pointer = desc;
	list_add_tail(&dn->sibling, list);

	if (acpi_nondev_subnode_extract(buf.pointer, handle, link, list,
					parent))
	return true;

	ACPI_FREE(buf.pointer);
	return false;
}

static bool acpi_nondev_subnode_ok(acpi_handle scope,
@@ -158,9 +145,16 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope,
				   struct list_head *list,
				   struct fwnode_handle *parent)
{
	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
	acpi_handle handle;
	acpi_status status;

	/*
	 * If the scope is unknown, the _DSD-equivalent package being parsed
	 * was embedded in an outer _DSD-equivalent package as a result of
	 * direct evaluation of an object pointed to by a reference.  In that
	 * case, using a pathname as the target object pointer is invalid.
	 */
	if (!scope)
		return false;

@@ -169,7 +163,17 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope,
	if (ACPI_FAILURE(status))
		return false;

	return acpi_nondev_subnode_data_ok(handle, link, list, parent);
	status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf,
					    ACPI_TYPE_PACKAGE);
	if (ACPI_FAILURE(status))
		return false;

	if (acpi_nondev_subnode_extract(buf.pointer, handle, link, list,
					parent))
		return true;

	ACPI_FREE(buf.pointer);
	return false;
}

static bool acpi_add_nondev_subnodes(acpi_handle scope,
@@ -180,9 +184,12 @@ static bool acpi_add_nondev_subnodes(acpi_handle scope,
	bool ret = false;
	int i;

	/*
	 * Every element in the links package is expected to represent a link
	 * to a non-device node in a tree containing device-specific data.
	 */
	for (i = 0; i < links->package.count; i++) {
		union acpi_object *link, *desc;
		acpi_handle handle;
		bool result;

		link = &links->package.elements[i];
@@ -190,26 +197,53 @@ static bool acpi_add_nondev_subnodes(acpi_handle scope,
		if (link->package.count != 2)
			continue;

		/* The first one must be a string. */
		/* The first one (the key) must be a string. */
		if (link->package.elements[0].type != ACPI_TYPE_STRING)
			continue;

		/* The second one may be a string, a reference or a package. */
		/* The second one (the target) may be a string or a package. */
		switch (link->package.elements[1].type) {
		case ACPI_TYPE_STRING:
			/*
			 * The string is expected to be a full pathname or a
			 * pathname segment relative to the given scope.  That
			 * pathname is expected to point to an object returning
			 * a package that contains _DSD-equivalent information.
			 */
			result = acpi_nondev_subnode_ok(scope, link, list,
							 parent);
			break;
		case ACPI_TYPE_LOCAL_REFERENCE:
			handle = link->package.elements[1].reference.handle;
			result = acpi_nondev_subnode_data_ok(handle, link, list,
							     parent);
			break;
		case ACPI_TYPE_PACKAGE:
			/*
			 * This happens when a reference is used in AML to
			 * point to the target.  Since the target is expected
			 * to be a named object, a reference to it will cause it
			 * to be avaluated in place and its return package will
			 * be embedded in the links package at the location of
			 * the reference.
			 *
			 * The target package is expected to contain _DSD-
			 * equivalent information, but the scope in which it
			 * is located in the original AML is unknown.  Thus
			 * it cannot contain pathname segments represented as
			 * strings because there is no way to build full
			 * pathnames out of them.
			 */
			acpi_handle_debug(scope, "subnode %s: Unknown scope\n",
					  link->package.elements[0].string.pointer);
			desc = &link->package.elements[1];
			result = acpi_nondev_subnode_extract(desc, NULL, link,
							     list, parent);
			break;
		case ACPI_TYPE_LOCAL_REFERENCE:
			/*
			 * It is not expected to see any local references in
			 * the links package because referencing a named object
			 * should cause it to be evaluated in place.
			 */
			acpi_handle_info(scope, "subnode %s: Unexpected reference\n",
					 link->package.elements[0].string.pointer);
			fallthrough;
		default:
			result = false;
			break;
@@ -369,6 +403,9 @@ static void acpi_untie_nondev_subnodes(struct acpi_device_data *data)
	struct acpi_data_node *dn;

	list_for_each_entry(dn, &data->subnodes, sibling) {
		if (!dn->handle)
			continue;

		acpi_detach_data(dn->handle, acpi_nondev_subnode_tag);

		acpi_untie_nondev_subnodes(&dn->data);
@@ -383,6 +420,9 @@ static bool acpi_tie_nondev_subnodes(struct acpi_device_data *data)
		acpi_status status;
		bool ret;

		if (!dn->handle)
			continue;

		status = acpi_attach_data(dn->handle, acpi_nondev_subnode_tag, dn);
		if (ACPI_FAILURE(status) && status != AE_ALREADY_EXISTS) {
			acpi_handle_err(dn->handle, "Can't tag data node\n");
+7 −0
Original line number Diff line number Diff line
@@ -510,6 +510,13 @@ static const struct dmi_system_id irq1_level_low_skip_override[] = {
			DMI_MATCH(DMI_BOARD_NAME, "N6506M"),
		},
	},
	{
		/* Asus Vivobook Pro N6506CU* */
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
			DMI_MATCH(DMI_BOARD_NAME, "N6506CU"),
		},
	},
	{
		/* LG Electronics 17U70P */
		.matches = {
+1 −0
Original line number Diff line number Diff line
@@ -2704,6 +2704,7 @@ void __init acpi_scan_init(void)
	acpi_memory_hotplug_init();
	acpi_watchdog_init();
	acpi_pnp_init();
	acpi_power_resources_init();
	acpi_int340x_thermal_init();
	acpi_init_lpit();

Loading