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
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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,8 +1049,14 @@ 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");
|
||||
__acpi_power_on(resource);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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,48 +99,70 @@ 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);
|
||||
/*
|
||||
* 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);
|
||||
|
||||
if (handle) {
|
||||
acpi_handle scope;
|
||||
acpi_status status;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
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)) {
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
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) {
|
||||
kfree(dn);
|
||||
acpi_handle_debug(handle, "Invalid properties/subnodes data, skipping\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
dn->handle = handle;
|
||||
dn->data.pointer = desc;
|
||||
list_add_tail(&dn->sibling, list);
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* 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);
|
||||
|
||||
kfree(dn);
|
||||
acpi_handle_debug(handle, "Invalid properties/subnodes data, skipping\n");
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool acpi_nondev_subnode_data_ok(acpi_handle handle,
|
||||
const union acpi_object *link,
|
||||
struct list_head *list,
|
||||
struct fwnode_handle *parent)
|
||||
static bool acpi_nondev_subnode_ok(acpi_handle scope,
|
||||
const union acpi_object *link,
|
||||
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;
|
||||
|
||||
status = acpi_get_handle(scope, link->package.elements[1].string.pointer,
|
||||
&handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return false;
|
||||
|
||||
status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf,
|
||||
ACPI_TYPE_PACKAGE);
|
||||
if (ACPI_FAILURE(status))
|
||||
@@ -153,25 +176,6 @@ static bool acpi_nondev_subnode_data_ok(acpi_handle handle,
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool acpi_nondev_subnode_ok(acpi_handle scope,
|
||||
const union acpi_object *link,
|
||||
struct list_head *list,
|
||||
struct fwnode_handle *parent)
|
||||
{
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
|
||||
if (!scope)
|
||||
return false;
|
||||
|
||||
status = acpi_get_handle(scope, link->package.elements[1].string.pointer,
|
||||
&handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return false;
|
||||
|
||||
return acpi_nondev_subnode_data_ok(handle, link, list, parent);
|
||||
}
|
||||
|
||||
static bool acpi_add_nondev_subnodes(acpi_handle scope,
|
||||
union acpi_object *links,
|
||||
struct list_head *list,
|
||||
@@ -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");
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -141,12 +141,23 @@ int __init acpi_parse_spcr(bool enable_earlycon, bool enable_console)
|
||||
case ACPI_DBG2_16550_NVIDIA:
|
||||
uart = "uart";
|
||||
break;
|
||||
case ACPI_DBG2_RISCV_SBI_CON:
|
||||
uart = "sbi";
|
||||
break;
|
||||
default:
|
||||
err = -ENOENT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch (table->baud_rate) {
|
||||
/*
|
||||
* SPCR 1.09 defines Precise Baud Rate Filed contains a specific
|
||||
* non-zero baud rate which overrides the value of the Configured
|
||||
* Baud Rate field. If this field is zero or not present, Configured
|
||||
* Baud Rate is used.
|
||||
*/
|
||||
if (table->precise_baudrate)
|
||||
baud_rate = table->precise_baudrate;
|
||||
else switch (table->baud_rate) {
|
||||
case 0:
|
||||
/*
|
||||
* SPCR 1.04 defines 0 as a preconfigured state of UART.
|
||||
|
||||
Reference in New Issue
Block a user