Merge branches 'acpi-battery', 'acpi-button', 'acpi-soc' and 'acpi-pfrut'

Merge updates of assorted ACPI drivers for 7.3-rc1:

 - Protect all battery properties with a separated mutex in the ACPI
   battery driver to prevent race conditions from occurring and avoid
   evaluating the _BST ACPI control method multiple times in parallel
   for the same battery device (Rong Zhang)

 - Add DMI quirk for Razer Blade Pro 17 early 2020 lid switch to the
   ACPI button driver (Robin Everaars)

 - Convert fixed clock rates in the ACPI driver for AMD SoCs (APD) to
   use HZ_PER_MHZ and add a clock frequency for the HJMC01 I2C
   controller to it (Hongnan Li and Xiangyang Yu)

 - Fix a stack buffer overflow in query_capability() in the ACPI
   platform firmware runtime update driver (Anirudh Prasad)

* acpi-battery:
  ACPI: battery: Protect all properties with a separated mutex

* acpi-button:
  ACPI: button: Add DMI quirk for Razer Blade Pro 17 early 2020 lid switch

* acpi-soc:
  ACPI: APD: Add clock frequency for HJMC01 I2C controller
  ACPI: APD: Convert fixed clock rates to use HZ_PER_MHZ

* acpi-pfrut:
  ACPI: pfr_update: fix stack buffer overflow in query_capability()
This commit is contained in:
Rafael J. Wysocki 2026-08-26 19:26:49 +02:00
4 changed files with 157 additions and 81 deletions

View File

@ -14,6 +14,7 @@
#include <linux/io.h>
#include <linux/platform_data/clk-fch.h>
#include <linux/platform_device.h>
#include <linux/units.h>
#include "internal.h"
@ -110,17 +111,17 @@ static int fch_misc_setup(struct apd_private_data *pdata)
static const struct apd_device_desc cz_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 133000000,
.fixed_clk_rate = 133 * HZ_PER_MHZ,
};
static const struct apd_device_desc wt_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 150000000,
.fixed_clk_rate = 150 * HZ_PER_MHZ,
};
static const struct apd_device_desc wt_i3c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 125000000,
.fixed_clk_rate = 125 * HZ_PER_MHZ,
};
static struct property_entry uart_properties[] = {
@ -132,7 +133,7 @@ static struct property_entry uart_properties[] = {
static const struct apd_device_desc cz_uart_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 48000000,
.fixed_clk_rate = 48 * HZ_PER_MHZ,
.properties = uart_properties,
};
@ -144,52 +145,57 @@ static const struct apd_device_desc fch_misc_desc = {
#ifdef CONFIG_ARM64
static const struct apd_device_desc xgene_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 100000000,
.fixed_clk_rate = 100 * HZ_PER_MHZ,
};
static const struct apd_device_desc vulcan_spi_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 133000000,
.fixed_clk_rate = 133 * HZ_PER_MHZ,
};
static const struct apd_device_desc hip07_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 200000000,
.fixed_clk_rate = 200 * HZ_PER_MHZ,
};
static const struct apd_device_desc hip08_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 250000000,
.fixed_clk_rate = 250 * HZ_PER_MHZ,
};
static const struct apd_device_desc hip08_lite_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 125000000,
.fixed_clk_rate = 125 * HZ_PER_MHZ,
};
static const struct apd_device_desc thunderx2_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 125000000,
.fixed_clk_rate = 125 * HZ_PER_MHZ,
};
static const struct apd_device_desc nxp_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 350000000,
.fixed_clk_rate = 350 * HZ_PER_MHZ,
};
static const struct apd_device_desc hip08_spi_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 250000000,
.fixed_clk_rate = 250 * HZ_PER_MHZ,
};
static const struct apd_device_desc leca_spi_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 400000000,
.fixed_clk_rate = 400 * HZ_PER_MHZ,
};
static const struct apd_device_desc leca_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 250000000,
.fixed_clk_rate = 250 * HZ_PER_MHZ,
};
static const struct apd_device_desc hjmc_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 200 * HZ_PER_MHZ,
};
#endif /* CONFIG_ARM64 */
@ -262,6 +268,7 @@ static const struct acpi_device_id acpi_apd_device_ids[] = {
{ "HISI02A2", APD_ADDR(hip08_i2c_desc) },
{ "HISI02A3", APD_ADDR(hip08_lite_i2c_desc) },
{ "HISI0173", APD_ADDR(hip08_spi_desc) },
{ "HJMC3001", APD_ADDR(hjmc_i2c_desc) },
{ "LECA0002", APD_ADDR(leca_spi_desc) },
{ "LECA0003", APD_ADDR(leca_i2c_desc) },
{ "NXP0001", APD_ADDR(nxp_i2c_desc) },

View File

@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/kfifo.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
@ -105,6 +106,9 @@ struct acpi_battery {
struct delayed_work acpi_notif_dwork;
struct notifier_block pm_nb;
struct list_head list;
unsigned long flags;
struct mutex property_lock; /* Protects properties below. */
unsigned long update_time;
int revision;
int rate_now;
@ -131,7 +135,6 @@ struct acpi_battery {
char oem_info[MAX_STRING_LENGTH];
int state;
int power_unit;
unsigned long flags;
};
#define to_acpi_battery(x) power_supply_get_drvdata(x)
@ -189,20 +192,6 @@ static bool acpi_battery_is_degraded(struct acpi_battery *battery)
battery->full_charge_capacity < battery->design_capacity;
}
static int acpi_battery_handle_discharging(struct acpi_battery *battery)
{
/*
* Some devices wrongly report discharging if the battery's charge level
* was above the device's start charging threshold atm the AC adapter
* was plugged in and the device thus did not start a new charge cycle.
*/
if ((battery_ac_is_broken || power_supply_is_system_supplied()) &&
battery->rate_now == 0)
return POWER_SUPPLY_STATUS_NOT_CHARGING;
return POWER_SUPPLY_STATUS_DISCHARGING;
}
static int acpi_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@ -210,15 +199,41 @@ static int acpi_battery_get_property(struct power_supply *psy,
int full_capacity = ACPI_BATTERY_VALUE_UNKNOWN, ret = 0;
struct acpi_battery *battery = to_acpi_battery(psy);
if (acpi_battery_present(battery)) {
/* run battery update only if it is present */
acpi_battery_get_state(battery);
} else if (psp != POWER_SUPPLY_PROP_PRESENT)
return -ENODEV;
/* run battery update only if it is present */
if (!acpi_battery_present(battery)) {
switch (psp) {
case POWER_SUPPLY_PROP_PRESENT:
val->intval = 0;
return 0;
default:
return -ENODEV;
}
}
mutex_lock(&battery->property_lock);
acpi_battery_get_state(battery);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
/*
* Some devices wrongly report discharging if the battery's charge level
* was above the device's start charging threshold atm the AC adapter
* was plugged in and the device thus did not start a new charge cycle.
*/
if (battery->state & ACPI_BATTERY_STATE_DISCHARGING)
val->intval = acpi_battery_handle_discharging(battery);
if (battery->rate_now != 0) {
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
} else if (battery_ac_is_broken) {
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
} else {
mutex_unlock(&battery->property_lock);
val->intval = power_supply_is_system_supplied()
? POWER_SUPPLY_STATUS_NOT_CHARGING
: POWER_SUPPLY_STATUS_DISCHARGING;
return 0;
}
else if (battery->state & ACPI_BATTERY_STATE_CHARGING)
/* Check the rate and capacity to validate the status. */
if (!acpi_battery_is_full(battery) ||
@ -321,6 +336,8 @@ static int acpi_battery_get_property(struct power_supply *psy,
default:
ret = -EINVAL;
}
mutex_unlock(&battery->property_lock);
return ret;
}
@ -556,6 +573,8 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
int use_bix;
int result = -ENODEV;
lockdep_assert_held(&battery->property_lock);
if (!acpi_battery_present(battery))
return 0;
@ -595,6 +614,8 @@ static int acpi_battery_get_state(struct acpi_battery *battery)
acpi_status status = 0;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
lockdep_assert_held(&battery->property_lock);
if (!acpi_battery_present(battery))
return 0;
@ -648,6 +669,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery)
{
acpi_status status = 0;
lockdep_assert_held(&battery->property_lock);
if (!acpi_battery_present(battery) ||
!test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags))
return -ENODEV;
@ -665,6 +688,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery)
static int acpi_battery_init_alarm(struct acpi_battery *battery)
{
lockdep_assert_held(&battery->property_lock);
/* See if alarms are supported, and if so, set default */
if (!acpi_has_method(battery->device->handle, "_BTP")) {
clear_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags);
@ -682,6 +707,8 @@ static ssize_t acpi_battery_alarm_show(struct device *dev,
{
struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
guard(mutex)(&battery->property_lock);
return sysfs_emit(buf, "%d\n", battery->alarm * 1000);
}
@ -697,6 +724,8 @@ static ssize_t acpi_battery_alarm_store(struct device *dev,
if (err)
return err;
guard(mutex)(&battery->property_lock);
battery->alarm = x / 1000;
if (acpi_battery_present(battery))
acpi_battery_set_alarm(battery);
@ -881,12 +910,17 @@ static int sysfs_add_battery(struct acpi_battery *battery)
.no_wakeup_source = true,
};
bool full_cap_broken = false;
int power_unit;
if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) &&
!ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity))
full_cap_broken = true;
scoped_guard(mutex, &battery->property_lock) {
power_unit = battery->power_unit;
if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) &&
!ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity))
full_cap_broken = true;
}
if (power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
if (full_cap_broken) {
battery->bat_desc.properties =
charge_battery_full_cap_broken_props;
@ -940,6 +974,9 @@ static void sysfs_remove_battery(struct acpi_battery *battery)
static void find_battery(const struct dmi_header *dm, void *private)
{
struct acpi_battery *battery = (struct acpi_battery *)private;
lockdep_assert_held(&battery->property_lock);
/* Note: the hardcoded offsets below have been extracted from
* the source code of dmidecode.
*/
@ -971,6 +1008,8 @@ static void find_battery(const struct dmi_header *dm, void *private)
*/
static void acpi_battery_quirks(struct acpi_battery *battery)
{
lockdep_assert_held(&battery->property_lock);
if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
return;
@ -1023,30 +1062,38 @@ static void acpi_battery_quirks(struct acpi_battery *battery)
static int acpi_battery_update(struct acpi_battery *battery, bool resume)
{
int result = acpi_battery_get_status(battery);
bool wakeup;
if (result)
return result;
if (!acpi_battery_present(battery)) {
sysfs_remove_battery(battery);
battery->update_time = 0;
scoped_guard(mutex, &battery->property_lock)
battery->update_time = 0;
return 0;
}
if (resume)
return 0;
if (!battery->update_time) {
result = acpi_battery_get_info(battery);
scoped_guard(mutex, &battery->property_lock) {
if (!battery->update_time) {
result = acpi_battery_get_info(battery);
if (result)
return result;
acpi_battery_init_alarm(battery);
}
result = acpi_battery_get_state(battery);
if (result)
return result;
acpi_battery_init_alarm(battery);
}
acpi_battery_quirks(battery);
result = acpi_battery_get_state(battery);
if (result)
return result;
acpi_battery_quirks(battery);
wakeup = ((battery->state & ACPI_BATTERY_STATE_CRITICAL) ||
(test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
(battery->capacity_now <= battery->alarm)));
}
if (!battery->bat) {
result = sysfs_add_battery(battery);
@ -1058,9 +1105,7 @@ static int acpi_battery_update(struct acpi_battery *battery, bool resume)
* Wakeup the system if battery is critical low
* or lower than the alarm level
*/
if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) ||
(test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
(battery->capacity_now <= battery->alarm)))
if (wakeup)
acpi_pm_wakeup_event(battery->phys_dev);
return result;
@ -1073,12 +1118,14 @@ static void acpi_battery_refresh(struct acpi_battery *battery)
if (!battery->bat)
return;
power_unit = battery->power_unit;
scoped_guard(mutex, &battery->property_lock) {
power_unit = battery->power_unit;
acpi_battery_get_info(battery);
acpi_battery_get_info(battery);
if (power_unit == battery->power_unit)
return;
if (power_unit == battery->power_unit)
return;
}
/* The battery has changed its reporting units. */
sysfs_remove_battery(battery);
@ -1170,17 +1217,21 @@ static int battery_notify(struct notifier_block *nb,
} else {
int result;
result = acpi_battery_get_info(battery);
if (result)
return result;
scoped_guard(mutex, &battery->property_lock) {
result = acpi_battery_get_info(battery);
if (result)
return result;
}
result = sysfs_add_battery(battery);
if (result)
return result;
}
acpi_battery_init_alarm(battery);
acpi_battery_get_state(battery);
scoped_guard(mutex, &battery->property_lock) {
acpi_battery_init_alarm(battery);
acpi_battery_get_state(battery);
}
}
return 0;
@ -1345,6 +1396,10 @@ static int acpi_battery_probe(struct platform_device *pdev)
if (result)
return result;
result = devm_mutex_init(&pdev->dev, &battery->property_lock);
if (result)
return result;
if (acpi_has_method(battery->device->handle, "_BIX"))
set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);

View File

@ -133,6 +133,17 @@ static const struct dmi_system_id dmi_lid_quirks[] = {
},
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
},
{
/*
* Razer Blade Pro 17 early 2020, notification of the LID device
* only happens on close, not on open and _LID keeps returning closed.
*/
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Razer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Blade Pro 17 (Early 2020) - RZ09-0329"),
},
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
},
{
/*
* Samsung galaxybook2 ,initial _LID device notification returns

View File

@ -120,7 +120,7 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr,
struct pfru_device *pfru_dev)
{
acpi_handle handle = ACPI_HANDLE(pfru_dev->parent_dev);
union acpi_object *out_obj;
union acpi_object *out_obj, *elem;
int ret = -EINVAL;
out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid,
@ -150,7 +150,9 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr,
goto free_acpi_buffer;
}
cap_hdr->status = out_obj->package.elements[CAP_STATUS_IDX].integer.value;
elem = out_obj->package.elements;
cap_hdr->status = elem[CAP_STATUS_IDX].integer.value;
if (cap_hdr->status != DSM_SUCCEED) {
ret = -EBUSY;
dev_dbg(pfru_dev->parent_dev, "Query cap Error Status:%d\n",
@ -158,29 +160,30 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr,
goto free_acpi_buffer;
}
cap_hdr->update_cap = out_obj->package.elements[CAP_UPDATE_IDX].integer.value;
if (elem[CAP_CODE_TYPE_IDX].buffer.length > sizeof(cap_hdr->code_type) ||
elem[CAP_DRV_TYPE_IDX].buffer.length > sizeof(cap_hdr->drv_type) ||
elem[CAP_PLAT_ID_IDX].buffer.length > sizeof(cap_hdr->platform_id) ||
elem[CAP_OEM_ID_IDX].buffer.length > sizeof(cap_hdr->oem_id))
goto free_acpi_buffer;
cap_hdr->update_cap = elem[CAP_UPDATE_IDX].integer.value;
memcpy(&cap_hdr->code_type,
out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.pointer,
out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.length);
cap_hdr->fw_version =
out_obj->package.elements[CAP_FW_VER_IDX].integer.value;
cap_hdr->code_rt_version =
out_obj->package.elements[CAP_CODE_RT_VER_IDX].integer.value;
elem[CAP_CODE_TYPE_IDX].buffer.pointer,
elem[CAP_CODE_TYPE_IDX].buffer.length);
cap_hdr->fw_version = elem[CAP_FW_VER_IDX].integer.value;
cap_hdr->code_rt_version = elem[CAP_CODE_RT_VER_IDX].integer.value;
memcpy(&cap_hdr->drv_type,
out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.pointer,
out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.length);
cap_hdr->drv_rt_version =
out_obj->package.elements[CAP_DRV_RT_VER_IDX].integer.value;
cap_hdr->drv_svn =
out_obj->package.elements[CAP_DRV_SVN_IDX].integer.value;
elem[CAP_DRV_TYPE_IDX].buffer.pointer,
elem[CAP_DRV_TYPE_IDX].buffer.length);
cap_hdr->drv_rt_version = elem[CAP_DRV_RT_VER_IDX].integer.value;
cap_hdr->drv_svn = elem[CAP_DRV_SVN_IDX].integer.value;
memcpy(&cap_hdr->platform_id,
out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.pointer,
out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.length);
elem[CAP_PLAT_ID_IDX].buffer.pointer,
elem[CAP_PLAT_ID_IDX].buffer.length);
memcpy(&cap_hdr->oem_id,
out_obj->package.elements[CAP_OEM_ID_IDX].buffer.pointer,
out_obj->package.elements[CAP_OEM_ID_IDX].buffer.length);
cap_hdr->oem_info_len =
out_obj->package.elements[CAP_OEM_INFO_IDX].buffer.length;
elem[CAP_OEM_ID_IDX].buffer.pointer,
elem[CAP_OEM_ID_IDX].buffer.length);
cap_hdr->oem_info_len = elem[CAP_OEM_INFO_IDX].buffer.length;
ret = 0;