Commit de8fbac5 authored by Eugene Shalygin's avatar Eugene Shalygin Committed by Guenter Roeck

hwmon: (asus-ec-sensors) implement locking via the ACPI global lock

For some board models ASUS uses the global ACPI lock to guard access to
the hardware, so do we.
Signed-off-by: default avatarEugene Shalygin <eugene.shalygin@gmail.com>
Link: https://lore.kernel.org/r/20220427143001.1443605-3-eugene.shalygin@gmail.comSigned-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
parent 5cd29012
...@@ -53,3 +53,5 @@ Module Parameters ...@@ -53,3 +53,5 @@ Module Parameters
the path is mostly identical for them). If ASUS changes this path the path is mostly identical for them). If ASUS changes this path
in a future BIOS update, this parameter can be used to override in a future BIOS update, this parameter can be used to override
the stored in the driver value until it gets updated. the stored in the driver value until it gets updated.
A special string ":GLOBAL_LOCK" can be passed to use the ACPI
global lock instead of a dedicated mutex.
...@@ -56,6 +56,9 @@ static char *mutex_path_override; ...@@ -56,6 +56,9 @@ static char *mutex_path_override;
#define MAX_IDENTICAL_BOARD_VARIATIONS 2 #define MAX_IDENTICAL_BOARD_VARIATIONS 2
/* Moniker for the ACPI global lock (':' is not allowed in ASL identifiers) */
#define ACPI_GLOBAL_LOCK_PSEUDO_PATH ":GLOBAL_LOCK"
typedef union { typedef union {
u32 value; u32 value;
struct { struct {
...@@ -166,6 +169,14 @@ static const struct ec_sensor_info known_ec_sensors[] = { ...@@ -166,6 +169,14 @@ static const struct ec_sensor_info known_ec_sensors[] = {
struct ec_board_info { struct ec_board_info {
const char *board_names[MAX_IDENTICAL_BOARD_VARIATIONS]; const char *board_names[MAX_IDENTICAL_BOARD_VARIATIONS];
unsigned long sensors; unsigned long sensors;
/*
* Defines which mutex to use for guarding access to the state and the
* hardware. Can be either a full path to an AML mutex or the
* pseudo-path ACPI_GLOBAL_LOCK_PSEUDO_PATH to use the global ACPI lock,
* or left empty to use a regular mutex object, in which case access to
* the hardware is not guarded.
*/
const char *mutex_path;
}; };
static const struct ec_board_info board_info[] = { static const struct ec_board_info board_info[] = {
...@@ -173,6 +184,7 @@ static const struct ec_board_info board_info[] = { ...@@ -173,6 +184,7 @@ static const struct ec_board_info board_info[] = {
.board_names = {"PRIME X570-PRO"}, .board_names = {"PRIME X570-PRO"},
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM | .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET, SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET,
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
}, },
{ {
.board_names = {"ProArt X570-CREATOR WIFI"}, .board_names = {"ProArt X570-CREATOR WIFI"},
...@@ -185,6 +197,7 @@ static const struct ec_board_info board_info[] = { ...@@ -185,6 +197,7 @@ static const struct ec_board_info board_info[] = {
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM | .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET | SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET |
SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE, SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
}, },
{ {
.board_names = {"ROG CROSSHAIR VIII DARK HERO"}, .board_names = {"ROG CROSSHAIR VIII DARK HERO"},
...@@ -193,6 +206,7 @@ static const struct ec_board_info board_info[] = { ...@@ -193,6 +206,7 @@ static const struct ec_board_info board_info[] = {
SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER | SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW | SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW |
SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE, SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
}, },
{ {
.board_names = {"ROG CROSSHAIR VIII FORMULA"}, .board_names = {"ROG CROSSHAIR VIII FORMULA"},
...@@ -200,6 +214,7 @@ static const struct ec_board_info board_info[] = { ...@@ -200,6 +214,7 @@ static const struct ec_board_info board_info[] = {
SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET | SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET |
SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE, SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
}, },
{ {
.board_names = { .board_names = {
...@@ -212,6 +227,7 @@ static const struct ec_board_info board_info[] = { ...@@ -212,6 +227,7 @@ static const struct ec_board_info board_info[] = {
SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET | SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET |
SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU | SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU |
SENSOR_IN_CPU_CORE, SENSOR_IN_CPU_CORE,
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
}, },
{ {
.board_names = {"ROG CROSSHAIR VIII IMPACT"}, .board_names = {"ROG CROSSHAIR VIII IMPACT"},
...@@ -219,12 +235,14 @@ static const struct ec_board_info board_info[] = { ...@@ -219,12 +235,14 @@ static const struct ec_board_info board_info[] = {
SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU | SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
SENSOR_IN_CPU_CORE, SENSOR_IN_CPU_CORE,
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
}, },
{ {
.board_names = {"ROG STRIX B550-E GAMING"}, .board_names = {"ROG STRIX B550-E GAMING"},
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
SENSOR_FAN_CPU_OPT, SENSOR_FAN_CPU_OPT,
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
}, },
{ {
.board_names = {"ROG STRIX B550-I GAMING"}, .board_names = {"ROG STRIX B550-I GAMING"},
...@@ -232,6 +250,7 @@ static const struct ec_board_info board_info[] = { ...@@ -232,6 +250,7 @@ static const struct ec_board_info board_info[] = {
SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
SENSOR_FAN_VRM_HS | SENSOR_CURR_CPU | SENSOR_FAN_VRM_HS | SENSOR_CURR_CPU |
SENSOR_IN_CPU_CORE, SENSOR_IN_CPU_CORE,
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
}, },
{ {
.board_names = {"ROG STRIX X570-E GAMING"}, .board_names = {"ROG STRIX X570-E GAMING"},
...@@ -239,17 +258,20 @@ static const struct ec_board_info board_info[] = { ...@@ -239,17 +258,20 @@ static const struct ec_board_info board_info[] = {
SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU | SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
SENSOR_IN_CPU_CORE, SENSOR_IN_CPU_CORE,
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
}, },
{ {
.board_names = {"ROG STRIX X570-F GAMING"}, .board_names = {"ROG STRIX X570-F GAMING"},
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET, SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET,
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
}, },
{ {
.board_names = {"ROG STRIX X570-I GAMING"}, .board_names = {"ROG STRIX X570-I GAMING"},
.sensors = SENSOR_TEMP_T_SENSOR | SENSOR_FAN_VRM_HS | .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_FAN_VRM_HS |
SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU | SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
SENSOR_IN_CPU_CORE, SENSOR_IN_CPU_CORE,
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
}, },
{} {}
}; };
...@@ -259,6 +281,46 @@ struct ec_sensor { ...@@ -259,6 +281,46 @@ struct ec_sensor {
s32 cached_value; s32 cached_value;
}; };
struct lock_data {
union {
acpi_handle aml;
/* global lock handle */
u32 glk;
} mutex;
bool (*lock)(struct lock_data *data);
bool (*unlock)(struct lock_data *data);
};
/*
* The next function pairs implement options for locking access to the
* state and the EC
*/
static bool lock_via_acpi_mutex(struct lock_data *data)
{
/*
* ASUS DSDT does not specify that access to the EC has to be guarded,
* but firmware does access it via ACPI
*/
return ACPI_SUCCESS(acpi_acquire_mutex(data->mutex.aml,
NULL, ACPI_LOCK_DELAY_MS));
}
static bool unlock_acpi_mutex(struct lock_data *data)
{
return ACPI_SUCCESS(acpi_release_mutex(data->mutex.aml, NULL));
}
static bool lock_via_global_acpi_lock(struct lock_data *data)
{
return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS,
&data->mutex.glk));
}
static bool unlock_global_acpi_lock(struct lock_data *data)
{
return ACPI_SUCCESS(acpi_release_global_lock(data->mutex.glk));
}
struct ec_sensors_data { struct ec_sensors_data {
const struct ec_board_info *board_info; const struct ec_board_info *board_info;
struct ec_sensor *sensors; struct ec_sensor *sensors;
...@@ -269,7 +331,7 @@ struct ec_sensors_data { ...@@ -269,7 +331,7 @@ struct ec_sensors_data {
u8 banks[ASUS_EC_MAX_BANK + 1]; u8 banks[ASUS_EC_MAX_BANK + 1];
/* in jiffies */ /* in jiffies */
unsigned long last_updated; unsigned long last_updated;
acpi_handle aml_mutex; struct lock_data lock_data;
/* number of board EC sensors */ /* number of board EC sensors */
u8 nr_sensors; u8 nr_sensors;
/* /*
...@@ -373,23 +435,36 @@ static void __init fill_ec_registers(struct ec_sensors_data *ec) ...@@ -373,23 +435,36 @@ static void __init fill_ec_registers(struct ec_sensors_data *ec)
} }
} }
static acpi_handle __init asus_hw_access_mutex(struct device *dev) static int __init setup_lock_data(struct device *dev)
{ {
const char *mutex_path; const char *mutex_path;
acpi_handle res;
int status; int status;
struct ec_sensors_data *state = dev_get_drvdata(dev);
mutex_path = mutex_path_override ? mutex_path = mutex_path_override ?
mutex_path_override : ASUS_HW_ACCESS_MUTEX_ASMX; mutex_path_override : state->board_info->mutex_path;
status = acpi_get_handle(NULL, (acpi_string)mutex_path, &res); if (!mutex_path || !strlen(mutex_path)) {
dev_err(dev, "Hardware access guard mutex name is empty");
return -EINVAL;
}
if (!strcmp(mutex_path, ACPI_GLOBAL_LOCK_PSEUDO_PATH)) {
state->lock_data.mutex.glk = 0;
state->lock_data.lock = lock_via_global_acpi_lock;
state->lock_data.unlock = unlock_global_acpi_lock;
} else {
status = acpi_get_handle(NULL, (acpi_string)mutex_path,
&state->lock_data.mutex.aml);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
dev_err(dev, dev_err(dev,
"Could not get hardware access guard mutex '%s': error %d", "Failed to get hardware access guard AML mutex '%s': error %d",
mutex_path, status); mutex_path, status);
return NULL; return -ENOENT;
}
state->lock_data.lock = lock_via_acpi_mutex;
state->lock_data.unlock = unlock_acpi_mutex;
} }
return res; return 0;
} }
static int asus_ec_bank_switch(u8 bank, u8 *old) static int asus_ec_bank_switch(u8 bank, u8 *old)
...@@ -492,15 +567,9 @@ static int update_ec_sensors(const struct device *dev, ...@@ -492,15 +567,9 @@ static int update_ec_sensors(const struct device *dev,
{ {
int status; int status;
/* if (!ec->lock_data.lock(&ec->lock_data)) {
* ASUS DSDT does not specify that access to the EC has to be guarded, dev_warn(dev, "Failed to acquire mutex");
* but firmware does access it via ACPI return -EBUSY;
*/
if (ACPI_FAILURE(acpi_acquire_mutex(ec->aml_mutex, NULL,
ACPI_LOCK_DELAY_MS))) {
dev_err(dev, "Failed to acquire AML mutex");
status = -EBUSY;
goto cleanup;
} }
status = asus_ec_block_read(dev, ec); status = asus_ec_block_read(dev, ec);
...@@ -508,10 +577,10 @@ static int update_ec_sensors(const struct device *dev, ...@@ -508,10 +577,10 @@ static int update_ec_sensors(const struct device *dev,
if (!status) { if (!status) {
update_sensor_values(ec, ec->read_buffer); update_sensor_values(ec, ec->read_buffer);
} }
if (ACPI_FAILURE(acpi_release_mutex(ec->aml_mutex, NULL))) {
dev_err(dev, "Failed to release AML mutex"); if (!ec->lock_data.unlock(&ec->lock_data))
} dev_err(dev, "Failed to release mutex");
cleanup:
return status; return status;
} }
...@@ -651,6 +720,7 @@ static int __init asus_ec_probe(struct platform_device *pdev) ...@@ -651,6 +720,7 @@ static int __init asus_ec_probe(struct platform_device *pdev)
enum hwmon_sensor_types type; enum hwmon_sensor_types type;
struct device *hwdev; struct device *hwdev;
unsigned int i; unsigned int i;
int status;
pboard_info = get_board_info(); pboard_info = get_board_info();
if (!pboard_info) if (!pboard_info)
...@@ -667,6 +737,11 @@ static int __init asus_ec_probe(struct platform_device *pdev) ...@@ -667,6 +737,11 @@ static int __init asus_ec_probe(struct platform_device *pdev)
ec_data->sensors = devm_kcalloc(dev, ec_data->nr_sensors, ec_data->sensors = devm_kcalloc(dev, ec_data->nr_sensors,
sizeof(struct ec_sensor), GFP_KERNEL); sizeof(struct ec_sensor), GFP_KERNEL);
status = setup_lock_data(dev);
if (status) {
dev_err(dev, "Failed to setup state/EC locking: %d", status);
return status;
}
setup_sensor_data(ec_data); setup_sensor_data(ec_data);
ec_data->registers = devm_kcalloc(dev, ec_data->nr_registers, ec_data->registers = devm_kcalloc(dev, ec_data->nr_registers,
sizeof(u16), GFP_KERNEL); sizeof(u16), GFP_KERNEL);
...@@ -678,8 +753,6 @@ static int __init asus_ec_probe(struct platform_device *pdev) ...@@ -678,8 +753,6 @@ static int __init asus_ec_probe(struct platform_device *pdev)
fill_ec_registers(ec_data); fill_ec_registers(ec_data);
ec_data->aml_mutex = asus_hw_access_mutex(dev);
for (i = 0; i < ec_data->nr_sensors; ++i) { for (i = 0; i < ec_data->nr_sensors; ++i) {
si = get_sensor_info(ec_data, i); si = get_sensor_info(ec_data, i);
if (!nr_count[si->type]) if (!nr_count[si->type])
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment