From 19c8b5241425948daeb0f91fe7a07cf1a4b76239 Mon Sep 17 00:00:00 2001 From: Muhammad Usama Anjum Date: Tue, 4 Oct 2022 13:10:19 +0500 Subject: platform/x86/amd/pmf: pass the struct by reference The out structure should be passed by reference instead of passing by value. This saves the extra copy of the structure. Signed-off-by: Muhammad Usama Anjum Link: https://lore.kernel.org/r/20221004081019.619193-1-usama.anjum@collabora.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/cnqf.c | 92 ++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 46 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c index 668c7c0fea83..3f9731a2ac28 100644 --- a/drivers/platform/x86/amd/pmf/cnqf.c +++ b/drivers/platform/x86/amd/pmf/cnqf.c @@ -158,100 +158,100 @@ int amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_l return 0; } -static void amd_pmf_update_trans_data(int idx, struct apmf_dyn_slider_output out) +static void amd_pmf_update_trans_data(int idx, struct apmf_dyn_slider_output *out) { struct cnqf_tran_params *tp; tp = &config_store.trans_param[idx][CNQF_TRANSITION_TO_QUIET]; - tp->time_constant = out.t_balanced_to_quiet; + tp->time_constant = out->t_balanced_to_quiet; tp->target_mode = CNQF_MODE_QUIET; tp->shifting_up = false; tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_BALANCE_TO_PERFORMANCE]; - tp->time_constant = out.t_balanced_to_perf; + tp->time_constant = out->t_balanced_to_perf; tp->target_mode = CNQF_MODE_PERFORMANCE; tp->shifting_up = true; tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_QUIET_TO_BALANCE]; - tp->time_constant = out.t_quiet_to_balanced; + tp->time_constant = out->t_quiet_to_balanced; tp->target_mode = CNQF_MODE_BALANCE; tp->shifting_up = true; tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_PERFORMANCE_TO_BALANCE]; - tp->time_constant = out.t_perf_to_balanced; + tp->time_constant = out->t_perf_to_balanced; tp->target_mode = CNQF_MODE_BALANCE; tp->shifting_up = false; tp = &config_store.trans_param[idx][CNQF_TRANSITION_FROM_TURBO_TO_PERFORMANCE]; - tp->time_constant = out.t_turbo_to_perf; + tp->time_constant = out->t_turbo_to_perf; tp->target_mode = CNQF_MODE_PERFORMANCE; tp->shifting_up = false; tp = &config_store.trans_param[idx][CNQF_TRANSITION_TO_TURBO]; - tp->time_constant = out.t_perf_to_turbo; + tp->time_constant = out->t_perf_to_turbo; tp->target_mode = CNQF_MODE_TURBO; tp->shifting_up = true; } -static void amd_pmf_update_mode_set(int idx, struct apmf_dyn_slider_output out) +static void amd_pmf_update_mode_set(int idx, struct apmf_dyn_slider_output *out) { struct cnqf_mode_settings *ms; /* Quiet Mode */ ms = &config_store.mode_set[idx][CNQF_MODE_QUIET]; - ms->power_floor = out.ps[APMF_CNQF_QUIET].pfloor; - ms->power_control.fppt = out.ps[APMF_CNQF_QUIET].fppt; - ms->power_control.sppt = out.ps[APMF_CNQF_QUIET].sppt; - ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_QUIET].sppt_apu_only; - ms->power_control.spl = out.ps[APMF_CNQF_QUIET].spl; - ms->power_control.stt_min = out.ps[APMF_CNQF_QUIET].stt_min_limit; + ms->power_floor = out->ps[APMF_CNQF_QUIET].pfloor; + ms->power_control.fppt = out->ps[APMF_CNQF_QUIET].fppt; + ms->power_control.sppt = out->ps[APMF_CNQF_QUIET].sppt; + ms->power_control.sppt_apu_only = out->ps[APMF_CNQF_QUIET].sppt_apu_only; + ms->power_control.spl = out->ps[APMF_CNQF_QUIET].spl; + ms->power_control.stt_min = out->ps[APMF_CNQF_QUIET].stt_min_limit; ms->power_control.stt_skin_temp[STT_TEMP_APU] = - out.ps[APMF_CNQF_QUIET].stt_skintemp[STT_TEMP_APU]; + out->ps[APMF_CNQF_QUIET].stt_skintemp[STT_TEMP_APU]; ms->power_control.stt_skin_temp[STT_TEMP_HS2] = - out.ps[APMF_CNQF_QUIET].stt_skintemp[STT_TEMP_HS2]; - ms->fan_control.fan_id = out.ps[APMF_CNQF_QUIET].fan_id; + out->ps[APMF_CNQF_QUIET].stt_skintemp[STT_TEMP_HS2]; + ms->fan_control.fan_id = out->ps[APMF_CNQF_QUIET].fan_id; /* Balance Mode */ ms = &config_store.mode_set[idx][CNQF_MODE_BALANCE]; - ms->power_floor = out.ps[APMF_CNQF_BALANCE].pfloor; - ms->power_control.fppt = out.ps[APMF_CNQF_BALANCE].fppt; - ms->power_control.sppt = out.ps[APMF_CNQF_BALANCE].sppt; - ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_BALANCE].sppt_apu_only; - ms->power_control.spl = out.ps[APMF_CNQF_BALANCE].spl; - ms->power_control.stt_min = out.ps[APMF_CNQF_BALANCE].stt_min_limit; + ms->power_floor = out->ps[APMF_CNQF_BALANCE].pfloor; + ms->power_control.fppt = out->ps[APMF_CNQF_BALANCE].fppt; + ms->power_control.sppt = out->ps[APMF_CNQF_BALANCE].sppt; + ms->power_control.sppt_apu_only = out->ps[APMF_CNQF_BALANCE].sppt_apu_only; + ms->power_control.spl = out->ps[APMF_CNQF_BALANCE].spl; + ms->power_control.stt_min = out->ps[APMF_CNQF_BALANCE].stt_min_limit; ms->power_control.stt_skin_temp[STT_TEMP_APU] = - out.ps[APMF_CNQF_BALANCE].stt_skintemp[STT_TEMP_APU]; + out->ps[APMF_CNQF_BALANCE].stt_skintemp[STT_TEMP_APU]; ms->power_control.stt_skin_temp[STT_TEMP_HS2] = - out.ps[APMF_CNQF_BALANCE].stt_skintemp[STT_TEMP_HS2]; - ms->fan_control.fan_id = out.ps[APMF_CNQF_BALANCE].fan_id; + out->ps[APMF_CNQF_BALANCE].stt_skintemp[STT_TEMP_HS2]; + ms->fan_control.fan_id = out->ps[APMF_CNQF_BALANCE].fan_id; /* Performance Mode */ ms = &config_store.mode_set[idx][CNQF_MODE_PERFORMANCE]; - ms->power_floor = out.ps[APMF_CNQF_PERFORMANCE].pfloor; - ms->power_control.fppt = out.ps[APMF_CNQF_PERFORMANCE].fppt; - ms->power_control.sppt = out.ps[APMF_CNQF_PERFORMANCE].sppt; - ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_PERFORMANCE].sppt_apu_only; - ms->power_control.spl = out.ps[APMF_CNQF_PERFORMANCE].spl; - ms->power_control.stt_min = out.ps[APMF_CNQF_PERFORMANCE].stt_min_limit; + ms->power_floor = out->ps[APMF_CNQF_PERFORMANCE].pfloor; + ms->power_control.fppt = out->ps[APMF_CNQF_PERFORMANCE].fppt; + ms->power_control.sppt = out->ps[APMF_CNQF_PERFORMANCE].sppt; + ms->power_control.sppt_apu_only = out->ps[APMF_CNQF_PERFORMANCE].sppt_apu_only; + ms->power_control.spl = out->ps[APMF_CNQF_PERFORMANCE].spl; + ms->power_control.stt_min = out->ps[APMF_CNQF_PERFORMANCE].stt_min_limit; ms->power_control.stt_skin_temp[STT_TEMP_APU] = - out.ps[APMF_CNQF_PERFORMANCE].stt_skintemp[STT_TEMP_APU]; + out->ps[APMF_CNQF_PERFORMANCE].stt_skintemp[STT_TEMP_APU]; ms->power_control.stt_skin_temp[STT_TEMP_HS2] = - out.ps[APMF_CNQF_PERFORMANCE].stt_skintemp[STT_TEMP_HS2]; - ms->fan_control.fan_id = out.ps[APMF_CNQF_PERFORMANCE].fan_id; + out->ps[APMF_CNQF_PERFORMANCE].stt_skintemp[STT_TEMP_HS2]; + ms->fan_control.fan_id = out->ps[APMF_CNQF_PERFORMANCE].fan_id; /* Turbo Mode */ ms = &config_store.mode_set[idx][CNQF_MODE_TURBO]; - ms->power_floor = out.ps[APMF_CNQF_TURBO].pfloor; - ms->power_control.fppt = out.ps[APMF_CNQF_TURBO].fppt; - ms->power_control.sppt = out.ps[APMF_CNQF_TURBO].sppt; - ms->power_control.sppt_apu_only = out.ps[APMF_CNQF_TURBO].sppt_apu_only; - ms->power_control.spl = out.ps[APMF_CNQF_TURBO].spl; - ms->power_control.stt_min = out.ps[APMF_CNQF_TURBO].stt_min_limit; + ms->power_floor = out->ps[APMF_CNQF_TURBO].pfloor; + ms->power_control.fppt = out->ps[APMF_CNQF_TURBO].fppt; + ms->power_control.sppt = out->ps[APMF_CNQF_TURBO].sppt; + ms->power_control.sppt_apu_only = out->ps[APMF_CNQF_TURBO].sppt_apu_only; + ms->power_control.spl = out->ps[APMF_CNQF_TURBO].spl; + ms->power_control.stt_min = out->ps[APMF_CNQF_TURBO].stt_min_limit; ms->power_control.stt_skin_temp[STT_TEMP_APU] = - out.ps[APMF_CNQF_TURBO].stt_skintemp[STT_TEMP_APU]; + out->ps[APMF_CNQF_TURBO].stt_skintemp[STT_TEMP_APU]; ms->power_control.stt_skin_temp[STT_TEMP_HS2] = - out.ps[APMF_CNQF_TURBO].stt_skintemp[STT_TEMP_HS2]; - ms->fan_control.fan_id = out.ps[APMF_CNQF_TURBO].fan_id; + out->ps[APMF_CNQF_TURBO].stt_skintemp[STT_TEMP_HS2]; + ms->fan_control.fan_id = out->ps[APMF_CNQF_TURBO].fan_id; } static int amd_pmf_check_flags(struct amd_pmf_dev *dev) @@ -284,8 +284,8 @@ static int amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev) return ret; } - amd_pmf_update_mode_set(i, out); - amd_pmf_update_trans_data(i, out); + amd_pmf_update_mode_set(i, &out); + amd_pmf_update_trans_data(i, &out); amd_pmf_update_power_threshold(i); for (j = 0; j < CNQF_MODE_MAX; j++) { -- cgit v1.2.3-58-ga151 From 878a82c23469d6626b27b44ade4d8f9438de51ab Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 27 Sep 2022 22:45:20 +0200 Subject: ACPI: battery: Pass battery hook pointer to hook callbacks Right now, is impossible for battery hook callbacks to access instance-specific data, forcing most drivers to provide some sort of global state. This however is difficult for drivers which can be instantiated multiple times and/or are hotplug-capable. Pass a pointer to the battery hook to those callbacks for usage with container_of(). Signed-off-by: Armin Wolf Acked-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20220927204521.601887-2-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/acpi/battery.c | 8 ++++---- drivers/platform/x86/asus-wmi.c | 4 ++-- drivers/platform/x86/huawei-wmi.c | 4 ++-- drivers/platform/x86/lg-laptop.c | 4 ++-- drivers/platform/x86/system76_acpi.c | 4 ++-- drivers/platform/x86/thinkpad_acpi.c | 4 ++-- drivers/platform/x86/toshiba_acpi.c | 4 ++-- include/acpi/battery.h | 4 ++-- 8 files changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 306513fec1e1..9482b0b6eadc 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -696,7 +696,7 @@ static void __battery_hook_unregister(struct acpi_battery_hook *hook, int lock) if (lock) mutex_lock(&hook_mutex); list_for_each_entry(battery, &acpi_battery_list, list) { - hook->remove_battery(battery->bat); + hook->remove_battery(battery->bat, hook); } list_del(&hook->list); if (lock) @@ -724,7 +724,7 @@ void battery_hook_register(struct acpi_battery_hook *hook) * its attributes. */ list_for_each_entry(battery, &acpi_battery_list, list) { - if (hook->add_battery(battery->bat)) { + if (hook->add_battery(battery->bat, hook)) { /* * If a add-battery returns non-zero, * the registration of the extension has failed, @@ -762,7 +762,7 @@ static void battery_hook_add_battery(struct acpi_battery *battery) * during the battery module initialization. */ list_for_each_entry_safe(hook_node, tmp, &battery_hook_list, list) { - if (hook_node->add_battery(battery->bat)) { + if (hook_node->add_battery(battery->bat, hook_node)) { /* * The notification of the extensions has failed, to * prevent further errors we will unload the extension. @@ -785,7 +785,7 @@ static void battery_hook_remove_battery(struct acpi_battery *battery) * custom attributes from the battery. */ list_for_each_entry(hook, &battery_hook_list, list) { - hook->remove_battery(battery->bat); + hook->remove_battery(battery->bat, hook); } /* Then, just remove the battery from the list */ list_del(&battery->list); diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 872efc1d5b36..6f81b2844dcb 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -883,7 +883,7 @@ static ssize_t charge_control_end_threshold_show(struct device *device, static DEVICE_ATTR_RW(charge_control_end_threshold); -static int asus_wmi_battery_add(struct power_supply *battery) +static int asus_wmi_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) { /* The WMI method does not provide a way to specific a battery, so we * just assume it is the first battery. @@ -910,7 +910,7 @@ static int asus_wmi_battery_add(struct power_supply *battery) return 0; } -static int asus_wmi_battery_remove(struct power_supply *battery) +static int asus_wmi_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook) { device_remove_file(&battery->dev, &dev_attr_charge_control_end_threshold); diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c index 5873c2663a65..415ccb3a95de 100644 --- a/drivers/platform/x86/huawei-wmi.c +++ b/drivers/platform/x86/huawei-wmi.c @@ -468,7 +468,7 @@ static DEVICE_ATTR_RW(charge_control_start_threshold); static DEVICE_ATTR_RW(charge_control_end_threshold); static DEVICE_ATTR_RW(charge_control_thresholds); -static int huawei_wmi_battery_add(struct power_supply *battery) +static int huawei_wmi_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) { int err = 0; @@ -483,7 +483,7 @@ static int huawei_wmi_battery_add(struct power_supply *battery) return err; } -static int huawei_wmi_battery_remove(struct power_supply *battery) +static int huawei_wmi_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook) { device_remove_file(&battery->dev, &dev_attr_charge_control_start_threshold); device_remove_file(&battery->dev, &dev_attr_charge_control_end_threshold); diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c index 332868b140ed..d662b64b0ba9 100644 --- a/drivers/platform/x86/lg-laptop.c +++ b/drivers/platform/x86/lg-laptop.c @@ -546,7 +546,7 @@ static DEVICE_ATTR_RW(fn_lock); static DEVICE_ATTR_RW(charge_control_end_threshold); static DEVICE_ATTR_RW(battery_care_limit); -static int lg_battery_add(struct power_supply *battery) +static int lg_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) { if (device_create_file(&battery->dev, &dev_attr_charge_control_end_threshold)) @@ -555,7 +555,7 @@ static int lg_battery_add(struct power_supply *battery) return 0; } -static int lg_battery_remove(struct power_supply *battery) +static int lg_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook) { device_remove_file(&battery->dev, &dev_attr_charge_control_end_threshold); diff --git a/drivers/platform/x86/system76_acpi.c b/drivers/platform/x86/system76_acpi.c index 958df41ad509..9031bd53253f 100644 --- a/drivers/platform/x86/system76_acpi.c +++ b/drivers/platform/x86/system76_acpi.c @@ -254,7 +254,7 @@ static struct attribute *system76_battery_attrs[] = { ATTRIBUTE_GROUPS(system76_battery); -static int system76_battery_add(struct power_supply *battery) +static int system76_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) { // System76 EC only supports 1 battery if (strcmp(battery->desc->name, "BAT0") != 0) @@ -266,7 +266,7 @@ static int system76_battery_add(struct power_supply *battery) return 0; } -static int system76_battery_remove(struct power_supply *battery) +static int system76_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook) { device_remove_groups(&battery->dev, system76_battery_groups); return 0; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 8476dfef4e62..4b12808bddab 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -9907,7 +9907,7 @@ ATTRIBUTE_GROUPS(tpacpi_battery); /* ACPI battery hooking */ -static int tpacpi_battery_add(struct power_supply *battery) +static int tpacpi_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) { int batteryid = tpacpi_battery_get_id(battery->desc->name); @@ -9918,7 +9918,7 @@ static int tpacpi_battery_add(struct power_supply *battery) return 0; } -static int tpacpi_battery_remove(struct power_supply *battery) +static int tpacpi_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook) { device_remove_groups(&battery->dev, tpacpi_battery_groups); return 0; diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 160abd3b3af8..13628d41799a 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -3113,7 +3113,7 @@ static struct attribute *toshiba_acpi_battery_attrs[] = { ATTRIBUTE_GROUPS(toshiba_acpi_battery); -static int toshiba_acpi_battery_add(struct power_supply *battery) +static int toshiba_acpi_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) { if (toshiba_acpi == NULL) { pr_err("Init order issue\n"); @@ -3126,7 +3126,7 @@ static int toshiba_acpi_battery_add(struct power_supply *battery) return 0; } -static int toshiba_acpi_battery_remove(struct power_supply *battery) +static int toshiba_acpi_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook) { device_remove_groups(&battery->dev, toshiba_acpi_battery_groups); return 0; diff --git a/include/acpi/battery.h b/include/acpi/battery.h index b8d56b702c7a..611a2561a014 100644 --- a/include/acpi/battery.h +++ b/include/acpi/battery.h @@ -12,8 +12,8 @@ struct acpi_battery_hook { const char *name; - int (*add_battery)(struct power_supply *battery); - int (*remove_battery)(struct power_supply *battery); + int (*add_battery)(struct power_supply *battery, struct acpi_battery_hook *hook); + int (*remove_battery)(struct power_supply *battery, struct acpi_battery_hook *hook); struct list_head list; }; -- cgit v1.2.3-58-ga151 From a77272c16041862c5c8d450c8701139887e1bb05 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 27 Sep 2022 22:45:21 +0200 Subject: platform/x86: dell: Add new dell-wmi-ddv driver The dell-wmi-ddv driver adds support for reading the current temperature and ePPID of ACPI batteries on supported Dell machines. Since the WMI interface used by this driver does not do any input validation and thus cannot be used for probing, the driver depends on the ACPI battery extension machanism to discover batteries. The driver also supports a debugfs interface for retrieving buffers containing fan and thermal sensor information. Since the meaing of the content of those buffers is currently unknown, the interface is meant for reverse-engineering and will likely be replaced with an hwmon interface once the meaning has been understood. The driver was tested on a Dell Inspiron 3505. Signed-off-by: Armin Wolf Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220927204521.601887-3-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- Documentation/ABI/testing/debugfs-dell-wmi-ddv | 21 ++ .../ABI/testing/sysfs-platform-dell-wmi-ddv | 7 + MAINTAINERS | 7 + drivers/platform/x86/dell/Kconfig | 13 + drivers/platform/x86/dell/Makefile | 1 + drivers/platform/x86/dell/dell-wmi-ddv.c | 364 +++++++++++++++++++++ drivers/platform/x86/wmi.c | 1 + 7 files changed, 414 insertions(+) create mode 100644 Documentation/ABI/testing/debugfs-dell-wmi-ddv create mode 100644 Documentation/ABI/testing/sysfs-platform-dell-wmi-ddv create mode 100644 drivers/platform/x86/dell/dell-wmi-ddv.c (limited to 'drivers/platform') diff --git a/Documentation/ABI/testing/debugfs-dell-wmi-ddv b/Documentation/ABI/testing/debugfs-dell-wmi-ddv new file mode 100644 index 000000000000..fbcc5d6f7388 --- /dev/null +++ b/Documentation/ABI/testing/debugfs-dell-wmi-ddv @@ -0,0 +1,21 @@ +What: /sys/kernel/debug/dell-wmi-ddv-/fan_sensor_information +Date: September 2022 +KernelVersion: 6.1 +Contact: Armin Wolf +Description: + This file contains the contents of the fan sensor information buffer, + which contains fan sensor entries and a terminating character (0xFF). + + Each fan sensor entry consists of three bytes with an unknown meaning, + interested people may use this file for reverse-engineering. + +What: /sys/kernel/debug/dell-wmi-ddv-/thermal_sensor_information +Date: September 2022 +KernelVersion: 6.1 +Contact: Armin Wolf +Description: + This file contains the contents of the thermal sensor information buffer, + which contains thermal sensor entries and a terminating character (0xFF). + + Each thermal sensor entry consists of five bytes with an unknown meaning, + interested people may use this file for reverse-engineering. diff --git a/Documentation/ABI/testing/sysfs-platform-dell-wmi-ddv b/Documentation/ABI/testing/sysfs-platform-dell-wmi-ddv new file mode 100644 index 000000000000..1d97ad615c66 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-dell-wmi-ddv @@ -0,0 +1,7 @@ +What: /sys/class/power_supply//eppid +Date: September 2022 +KernelVersion: 6.1 +Contact: Armin Wolf +Description: + Reports the Dell ePPID (electronic Dell Piece Part Identification) + of the ACPI battery. diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..f8fa72d1452e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5851,6 +5851,13 @@ L: Dell.Client.Kernel@dell.com S: Maintained F: drivers/platform/x86/dell/dell-wmi-descriptor.c +DELL WMI DDV DRIVER +M: Armin Wolf +S: Maintained +F: Documentation/ABI/testing/debugfs-dell-wmi-ddv +F: Documentation/ABI/testing/sysfs-platform-dell-wmi-ddv +F: drivers/platform/x86/dell/dell-wmi-ddv.c + DELL WMI SYSMAN DRIVER M: Divya Bharathi M: Prasanth Ksr diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig index 25421e061c47..d319de8f2132 100644 --- a/drivers/platform/x86/dell/Kconfig +++ b/drivers/platform/x86/dell/Kconfig @@ -189,6 +189,19 @@ config DELL_WMI_DESCRIPTOR default n depends on ACPI_WMI +config DELL_WMI_DDV + tristate "Dell WMI sensors Support" + default m + depends on ACPI_BATTERY + depends on ACPI_WMI + help + This option adds support for WMI-based sensors like + battery temperature sensors found on some Dell notebooks. + It also supports reading of the battery ePPID. + + To compile this drivers as a module, choose M here: the module will + be called dell-wmi-ddv. + config DELL_WMI_LED tristate "External LED on Dell Business Netbooks" default m diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile index ddba1df71e80..1b8942426622 100644 --- a/drivers/platform/x86/dell/Makefile +++ b/drivers/platform/x86/dell/Makefile @@ -19,5 +19,6 @@ dell-wmi-objs := dell-wmi-base.o dell-wmi-$(CONFIG_DELL_WMI_PRIVACY) += dell-wmi-privacy.o obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o +obj-$(CONFIG_DELL_WMI_DDV) += dell-wmi-ddv.o obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman/ diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c new file mode 100644 index 000000000000..699feae3c435 --- /dev/null +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux driver for WMI sensor information on Dell notebooks. + * + * Copyright (C) 2022 Armin Wolf + */ + +#define pr_format(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER_NAME "dell-wmi-ddv" + +#define DELL_DDV_SUPPORTED_INTERFACE 2 +#define DELL_DDV_GUID "8A42EA14-4F2A-FD45-6422-0087F7A7E608" + +enum dell_ddv_method { + DELL_DDV_BATTERY_DESIGN_CAPACITY = 0x01, + DELL_DDV_BATTERY_FULL_CHARGE_CAPACITY = 0x02, + DELL_DDV_BATTERY_MANUFACTURE_NAME = 0x03, + DELL_DDV_BATTERY_MANUFACTURE_DATE = 0x04, + DELL_DDV_BATTERY_SERIAL_NUMBER = 0x05, + DELL_DDV_BATTERY_CHEMISTRY_VALUE = 0x06, + DELL_DDV_BATTERY_TEMPERATURE = 0x07, + DELL_DDV_BATTERY_CURRENT = 0x08, + DELL_DDV_BATTERY_VOLTAGE = 0x09, + DELL_DDV_BATTERY_MANUFACTURER_ACCESS = 0x0A, + DELL_DDV_BATTERY_RELATIVE_CHARGE_STATE = 0x0B, + DELL_DDV_BATTERY_CYCLE_COUNT = 0x0C, + DELL_DDV_BATTERY_EPPID = 0x0D, + DELL_DDV_BATTERY_RAW_ANALYTICS_START = 0x0E, + DELL_DDV_BATTERY_RAW_ANALYTICS = 0x0F, + DELL_DDV_BATTERY_DESIGN_VOLTAGE = 0x10, + + DELL_DDV_INTERFACE_VERSION = 0x12, + + DELL_DDV_FAN_SENSOR_INFORMATION = 0x20, + DELL_DDV_THERMAL_SENSOR_INFORMATION = 0x22, +}; + +struct dell_wmi_ddv_data { + struct acpi_battery_hook hook; + struct device_attribute temp_attr; + struct device_attribute eppid_attr; + struct wmi_device *wdev; +}; + +static int dell_wmi_ddv_query_type(struct wmi_device *wdev, enum dell_ddv_method method, u32 arg, + union acpi_object **result, acpi_object_type type) +{ + struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; + const struct acpi_buffer in = { + .length = sizeof(arg), + .pointer = &arg, + }; + union acpi_object *obj; + acpi_status ret; + + ret = wmidev_evaluate_method(wdev, 0x0, method, &in, &out); + if (ACPI_FAILURE(ret)) + return -EIO; + + obj = out.pointer; + if (!obj) + return -ENODATA; + + if (obj->type != type) { + kfree(obj); + return -EIO; + } + + *result = obj; + + return 0; +} + +static int dell_wmi_ddv_query_integer(struct wmi_device *wdev, enum dell_ddv_method method, + u32 arg, u32 *res) +{ + union acpi_object *obj; + int ret; + + ret = dell_wmi_ddv_query_type(wdev, method, arg, &obj, ACPI_TYPE_INTEGER); + if (ret < 0) + return ret; + + if (obj->integer.value <= U32_MAX) + *res = (u32)obj->integer.value; + else + ret = -ERANGE; + + kfree(obj); + + return ret; +} + +static int dell_wmi_ddv_query_buffer(struct wmi_device *wdev, enum dell_ddv_method method, + u32 arg, union acpi_object **result) +{ + union acpi_object *obj; + u64 buffer_size; + int ret; + + ret = dell_wmi_ddv_query_type(wdev, method, arg, &obj, ACPI_TYPE_PACKAGE); + if (ret < 0) + return ret; + + if (obj->package.count != 2) + goto err_free; + + if (obj->package.elements[0].type != ACPI_TYPE_INTEGER) + goto err_free; + + buffer_size = obj->package.elements[0].integer.value; + + if (obj->package.elements[1].type != ACPI_TYPE_BUFFER) + goto err_free; + + if (buffer_size != obj->package.elements[1].buffer.length) { + dev_warn(&wdev->dev, + FW_WARN "ACPI buffer size (%llu) does not match WMI buffer size (%d)\n", + buffer_size, obj->package.elements[1].buffer.length); + + goto err_free; + } + + *result = obj; + + return 0; + +err_free: + kfree(obj); + + return -EIO; +} + +static int dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_method method, + u32 arg, union acpi_object **result) +{ + return dell_wmi_ddv_query_type(wdev, method, arg, result, ACPI_TYPE_STRING); +} + +static int dell_wmi_ddv_battery_index(struct acpi_device *acpi_dev, u32 *index) +{ + const char *uid_str; + + uid_str = acpi_device_uid(acpi_dev); + if (!uid_str) + return -ENODEV; + + return kstrtou32(uid_str, 10, index); +} + +static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dell_wmi_ddv_data *data = container_of(attr, struct dell_wmi_ddv_data, temp_attr); + u32 index, value; + int ret; + + ret = dell_wmi_ddv_battery_index(to_acpi_device(dev->parent), &index); + if (ret < 0) + return ret; + + ret = dell_wmi_ddv_query_integer(data->wdev, DELL_DDV_BATTERY_TEMPERATURE, index, &value); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%d\n", DIV_ROUND_CLOSEST(value, 10)); +} + +static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dell_wmi_ddv_data *data = container_of(attr, struct dell_wmi_ddv_data, eppid_attr); + union acpi_object *obj; + u32 index; + int ret; + + ret = dell_wmi_ddv_battery_index(to_acpi_device(dev->parent), &index); + if (ret < 0) + return ret; + + ret = dell_wmi_ddv_query_string(data->wdev, DELL_DDV_BATTERY_EPPID, index, &obj); + if (ret < 0) + return ret; + + ret = sysfs_emit(buf, "%s\n", obj->string.pointer); + + kfree(obj); + + return ret; +} + +static int dell_wmi_ddv_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook) +{ + struct dell_wmi_ddv_data *data = container_of(hook, struct dell_wmi_ddv_data, hook); + u32 index; + int ret; + + /* Return 0 instead of error to avoid being unloaded */ + ret = dell_wmi_ddv_battery_index(to_acpi_device(battery->dev.parent), &index); + if (ret < 0) + return 0; + + ret = device_create_file(&battery->dev, &data->temp_attr); + if (ret < 0) + return ret; + + ret = device_create_file(&battery->dev, &data->eppid_attr); + if (ret < 0) { + device_remove_file(&battery->dev, &data->temp_attr); + + return ret; + } + + return 0; +} + +static int dell_wmi_ddv_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook) +{ + struct dell_wmi_ddv_data *data = container_of(hook, struct dell_wmi_ddv_data, hook); + + device_remove_file(&battery->dev, &data->temp_attr); + device_remove_file(&battery->dev, &data->eppid_attr); + + return 0; +} + +static void dell_wmi_ddv_battery_remove(void *data) +{ + struct acpi_battery_hook *hook = data; + + battery_hook_unregister(hook); +} + +static int dell_wmi_ddv_battery_add(struct dell_wmi_ddv_data *data) +{ + data->hook.name = "Dell DDV Battery Extension"; + data->hook.add_battery = dell_wmi_ddv_add_battery; + data->hook.remove_battery = dell_wmi_ddv_remove_battery; + + sysfs_attr_init(&data->temp_attr.attr); + data->temp_attr.attr.name = "temp"; + data->temp_attr.attr.mode = 0444; + data->temp_attr.show = temp_show; + + sysfs_attr_init(&data->eppid_attr.attr); + data->eppid_attr.attr.name = "eppid"; + data->eppid_attr.attr.mode = 0444; + data->eppid_attr.show = eppid_show; + + battery_hook_register(&data->hook); + + return devm_add_action_or_reset(&data->wdev->dev, dell_wmi_ddv_battery_remove, &data->hook); +} + +static int dell_wmi_ddv_buffer_read(struct seq_file *seq, enum dell_ddv_method method) +{ + struct device *dev = seq->private; + struct dell_wmi_ddv_data *data = dev_get_drvdata(dev); + union acpi_object *obj; + union acpi_object buf; + int ret; + + ret = dell_wmi_ddv_query_buffer(data->wdev, method, 0, &obj); + if (ret < 0) + return ret; + + buf = obj->package.elements[1]; + ret = seq_write(seq, buf.buffer.pointer, buf.buffer.length); + kfree(obj); + + return ret; +} + +static int dell_wmi_ddv_fan_read(struct seq_file *seq, void *offset) +{ + return dell_wmi_ddv_buffer_read(seq, DELL_DDV_FAN_SENSOR_INFORMATION); +} + +static int dell_wmi_ddv_temp_read(struct seq_file *seq, void *offset) +{ + return dell_wmi_ddv_buffer_read(seq, DELL_DDV_THERMAL_SENSOR_INFORMATION); +} + +static void dell_wmi_ddv_debugfs_remove(void *data) +{ + struct dentry *entry = data; + + debugfs_remove(entry); +} + +static void dell_wmi_ddv_debugfs_init(struct wmi_device *wdev) +{ + struct dentry *entry; + char name[64]; + + scnprintf(name, ARRAY_SIZE(name), "%s-%s", DRIVER_NAME, dev_name(&wdev->dev)); + entry = debugfs_create_dir(name, NULL); + + debugfs_create_devm_seqfile(&wdev->dev, "fan_sensor_information", entry, + dell_wmi_ddv_fan_read); + debugfs_create_devm_seqfile(&wdev->dev, "thermal_sensor_information", entry, + dell_wmi_ddv_temp_read); + + devm_add_action_or_reset(&wdev->dev, dell_wmi_ddv_debugfs_remove, entry); +} + +static int dell_wmi_ddv_probe(struct wmi_device *wdev, const void *context) +{ + struct dell_wmi_ddv_data *data; + u32 version; + int ret; + + ret = dell_wmi_ddv_query_integer(wdev, DELL_DDV_INTERFACE_VERSION, 0, &version); + if (ret < 0) + return ret; + + dev_dbg(&wdev->dev, "WMI interface version: %d\n", version); + if (version != DELL_DDV_SUPPORTED_INTERFACE) + return -ENODEV; + + data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + dev_set_drvdata(&wdev->dev, data); + data->wdev = wdev; + + dell_wmi_ddv_debugfs_init(wdev); + + return dell_wmi_ddv_battery_add(data); +} + +static const struct wmi_device_id dell_wmi_ddv_id_table[] = { + { DELL_DDV_GUID, NULL }, + { } +}; +MODULE_DEVICE_TABLE(wmi, dell_wmi_ddv_id_table); + +static struct wmi_driver dell_wmi_ddv_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .id_table = dell_wmi_ddv_id_table, + .probe = dell_wmi_ddv_probe, +}; +module_wmi_driver(dell_wmi_ddv_driver); + +MODULE_AUTHOR("Armin Wolf "); +MODULE_DESCRIPTION("Dell WMI sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 223550a10d4d..5ffc00480aef 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -105,6 +105,7 @@ MODULE_DEVICE_TABLE(acpi, wmi_device_ids); /* allow duplicate GUIDs as these device drivers use struct wmi_driver */ static const char * const allow_duplicates[] = { "05901221-D566-11D1-B2F0-00A0C9062910", /* wmi-bmof */ + "8A42EA14-4F2A-FD45-6422-0087F7A7E608", /* dell-wmi-ddv */ NULL }; -- cgit v1.2.3-58-ga151 From d6fef93258f3b902f5166297b05d72746e16e1f7 Mon Sep 17 00:00:00 2001 From: Barnabás Pőcze Date: Wed, 5 Oct 2022 15:00:39 +0000 Subject: platform/x86: huawei-wmi: do not hard-code sizes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use `sizeof()` and `ARRAY_SIZE()` instead of hard-coding buffer sizes and indices. Signed-off-by: Barnabás Pőcze Link: https://lore.kernel.org/r/20221005150032.173198-1-pobrn@protonmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/huawei-wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c index 415ccb3a95de..2b48e86448b3 100644 --- a/drivers/platform/x86/huawei-wmi.c +++ b/drivers/platform/x86/huawei-wmi.c @@ -323,12 +323,12 @@ static int huawei_wmi_battery_get(int *start, int *end) u8 ret[0x100]; int err, i; - err = huawei_wmi_cmd(BATTERY_THRESH_GET, ret, 0x100); + err = huawei_wmi_cmd(BATTERY_THRESH_GET, ret, sizeof(ret)); if (err) return err; /* Find the last two non-zero values. Return status is ignored. */ - i = 0xff; + i = ARRAY_SIZE(ret) - 1; do { if (start) *start = ret[i-1]; -- cgit v1.2.3-58-ga151 From 0b9a1dcdb6a2c841899389bf2dd7a3e0e2aa0e99 Mon Sep 17 00:00:00 2001 From: Barnabás Pőcze Date: Wed, 5 Oct 2022 15:00:45 +0000 Subject: platform/x86: huawei-wmi: fix return value calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, `huawei_wmi_input_setup()` returned the result of logical or-ing the return values of two functions that return negative errno-style error codes and one that returns `acpi_status`. If this returned value was non-zero, then it was propagated from the platform driver's probe function. That function should return a negative errno-style error code, so the result of the logical or that `huawei_wmi_input_setup()` returned was not appropriate. Fix that by checking each function separately and returning the error code unmodified. Fixes: 1ac9abeb2e5b ("platform/x86: huawei-wmi: Move to platform driver") Signed-off-by: Barnabás Pőcze Link: https://lore.kernel.org/r/20221005150032.173198-2-pobrn@protonmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/huawei-wmi.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c index 2b48e86448b3..981420f7f5d7 100644 --- a/drivers/platform/x86/huawei-wmi.c +++ b/drivers/platform/x86/huawei-wmi.c @@ -760,6 +760,9 @@ static int huawei_wmi_input_setup(struct device *dev, const char *guid, struct input_dev **idev) { + acpi_status status; + int err; + *idev = devm_input_allocate_device(dev); if (!*idev) return -ENOMEM; @@ -769,10 +772,19 @@ static int huawei_wmi_input_setup(struct device *dev, (*idev)->id.bustype = BUS_HOST; (*idev)->dev.parent = dev; - return sparse_keymap_setup(*idev, huawei_wmi_keymap, NULL) || - input_register_device(*idev) || - wmi_install_notify_handler(guid, huawei_wmi_input_notify, - *idev); + err = sparse_keymap_setup(*idev, huawei_wmi_keymap, NULL); + if (err) + return err; + + err = input_register_device(*idev); + if (err) + return err; + + status = wmi_install_notify_handler(guid, huawei_wmi_input_notify, *idev); + if (ACPI_FAILURE(status)) + return -EIO; + + return 0; } static void huawei_wmi_input_exit(struct device *dev, const char *guid) -- cgit v1.2.3-58-ga151 From e57d58ee1868b1ad3e8040335a2941506feb52e4 Mon Sep 17 00:00:00 2001 From: Barnabás Pőcze Date: Wed, 5 Oct 2022 15:00:51 +0000 Subject: platform/x86: huawei-wmi: remove unnecessary member MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `huawei_wmi::idev` array is not actually used by the driver, so remove it. The piece of code that - I believe - was supposed to fill the array is flawed, it did not actually set any of the values inside the array. This was most likely masked by the fact that the input devices are devm managed and that the only function that needs a reference to the input devices is `huawei_wmi_input_notify()`, however, that does not access the appropriate input device via the `huawei_wmi` object. Signed-off-by: Barnabás Pőcze Link: https://lore.kernel.org/r/20221005150032.173198-3-pobrn@protonmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/huawei-wmi.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c index 981420f7f5d7..2df1b2d5e3ea 100644 --- a/drivers/platform/x86/huawei-wmi.c +++ b/drivers/platform/x86/huawei-wmi.c @@ -63,7 +63,6 @@ struct huawei_wmi { bool fn_lock_available; struct huawei_wmi_debug debug; - struct input_dev *idev[2]; struct led_classdev cdev; struct device *dev; @@ -756,31 +755,30 @@ static void huawei_wmi_input_notify(u32 value, void *context) kfree(response.pointer); } -static int huawei_wmi_input_setup(struct device *dev, - const char *guid, - struct input_dev **idev) +static int huawei_wmi_input_setup(struct device *dev, const char *guid) { + struct input_dev *idev; acpi_status status; int err; - *idev = devm_input_allocate_device(dev); - if (!*idev) + idev = devm_input_allocate_device(dev); + if (!idev) return -ENOMEM; - (*idev)->name = "Huawei WMI hotkeys"; - (*idev)->phys = "wmi/input0"; - (*idev)->id.bustype = BUS_HOST; - (*idev)->dev.parent = dev; + idev->name = "Huawei WMI hotkeys"; + idev->phys = "wmi/input0"; + idev->id.bustype = BUS_HOST; + idev->dev.parent = dev; - err = sparse_keymap_setup(*idev, huawei_wmi_keymap, NULL); + err = sparse_keymap_setup(idev, huawei_wmi_keymap, NULL); if (err) return err; - err = input_register_device(*idev); + err = input_register_device(idev); if (err) return err; - status = wmi_install_notify_handler(guid, huawei_wmi_input_notify, *idev); + status = wmi_install_notify_handler(guid, huawei_wmi_input_notify, idev); if (ACPI_FAILURE(status)) return -EIO; @@ -809,17 +807,14 @@ static int huawei_wmi_probe(struct platform_device *pdev) huawei_wmi->dev = &pdev->dev; while (*guid->guid_string) { - struct input_dev *idev = *huawei_wmi->idev; - if (wmi_has_guid(guid->guid_string)) { - err = huawei_wmi_input_setup(&pdev->dev, guid->guid_string, &idev); + err = huawei_wmi_input_setup(&pdev->dev, guid->guid_string); if (err) { dev_err(&pdev->dev, "Failed to setup input on %s\n", guid->guid_string); return err; } } - idev++; guid++; } -- cgit v1.2.3-58-ga151 From db5e2a4ca0a7a5fe54f410590292ea2e91de6798 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 7 Nov 2022 13:43:22 +0100 Subject: platform/x86: thinkpad_acpi: Fix max_brightness of thinklight MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thinklight has only two values, on/off so it's reasonable for max_brightness to be 0 and 1 as if you write anything between 0 and 255 it will be 255 anyway so there's no point for it to be 255. This may look like it is a userspace API change, but writes with a value larget then the new max_brightness will still be accepted, these will be silently clamped to the new max_brightness by led_set_brightness_nosleep(). So no userspace API problems are expected. Reported-by: Michał Szczepaniak Link: https://lore.kernel.org/platform-driver-x86/55400326-e64f-5444-94e5-22b8214d00b6@gmail.com/ Signed-off-by: Hans de Goede --- drivers/platform/x86/thinkpad_acpi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 4b12808bddab..cfeed1c45b54 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -5572,6 +5572,7 @@ static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev) static struct tpacpi_led_classdev tpacpi_led_thinklight = { .led_classdev = { .name = "tpacpi::thinklight", + .max_brightness = 1, .brightness_set_blocking = &light_sysfs_set, .brightness_get = &light_sysfs_get, } -- cgit v1.2.3-58-ga151 From bc05ea63b39420fd561dcffba00753da90338ea8 Mon Sep 17 00:00:00 2001 From: Liming Sun Date: Tue, 18 Oct 2022 09:33:03 -0400 Subject: platform/mellanox: Add BlueField-3 support in the tmfifo driver BlueField-3 uses the same control registers in tmfifo access but at different addresses. This commit replaces the offset reference with pointers, and set up these pointers in the probe functions accordingly. Signed-off-by: Liming Sun Reviewed-by: David Thompson Reviewed-by: Vadim Pasternak Link: https://lore.kernel.org/r/20221018133303.243920-1-limings@nvidia.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/mellanox/mlxbf-tmfifo-regs.h | 10 ++++ drivers/platform/mellanox/mlxbf-tmfifo.c | 86 ++++++++++++++++++++------- 2 files changed, 74 insertions(+), 22 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/mellanox/mlxbf-tmfifo-regs.h b/drivers/platform/mellanox/mlxbf-tmfifo-regs.h index e4f0d2eda714..44fb8c5b1484 100644 --- a/drivers/platform/mellanox/mlxbf-tmfifo-regs.h +++ b/drivers/platform/mellanox/mlxbf-tmfifo-regs.h @@ -60,4 +60,14 @@ #define MLXBF_TMFIFO_RX_CTL__MAX_ENTRIES_RMASK GENMASK_ULL(8, 0) #define MLXBF_TMFIFO_RX_CTL__MAX_ENTRIES_MASK GENMASK_ULL(40, 32) +/* BF3 register offsets within resource 0. */ +#define MLXBF_TMFIFO_RX_DATA_BF3 0x0000 +#define MLXBF_TMFIFO_TX_DATA_BF3 0x1000 + +/* BF3 register offsets within resource 1. */ +#define MLXBF_TMFIFO_RX_STS_BF3 0x0000 +#define MLXBF_TMFIFO_RX_CTL_BF3 0x0008 +#define MLXBF_TMFIFO_TX_STS_BF3 0x0100 +#define MLXBF_TMFIFO_TX_CTL_BF3 0x0108 + #endif /* !defined(__MLXBF_TMFIFO_REGS_H__) */ diff --git a/drivers/platform/mellanox/mlxbf-tmfifo.c b/drivers/platform/mellanox/mlxbf-tmfifo.c index 1ae3c56b66b0..91a077c35b8b 100644 --- a/drivers/platform/mellanox/mlxbf-tmfifo.c +++ b/drivers/platform/mellanox/mlxbf-tmfifo.c @@ -47,6 +47,9 @@ /* Message with data needs at least two words (for header & data). */ #define MLXBF_TMFIFO_DATA_MIN_WORDS 2 +/* ACPI UID for BlueField-3. */ +#define TMFIFO_BF3_UID 1 + struct mlxbf_tmfifo; /** @@ -136,12 +139,26 @@ struct mlxbf_tmfifo_irq_info { int index; }; +/** + * mlxbf_tmfifo_io - Structure of the TmFifo IO resource (for both rx & tx) + * @ctl: control register offset (TMFIFO_RX_CTL / TMFIFO_TX_CTL) + * @sts: status register offset (TMFIFO_RX_STS / TMFIFO_TX_STS) + * @data: data register offset (TMFIFO_RX_DATA / TMFIFO_TX_DATA) + */ +struct mlxbf_tmfifo_io { + void __iomem *ctl; + void __iomem *sts; + void __iomem *data; +}; + /** * mlxbf_tmfifo - Structure of the TmFifo * @vdev: array of the virtual devices running over the TmFifo * @lock: lock to protect the TmFifo access - * @rx_base: mapped register base address for the Rx FIFO - * @tx_base: mapped register base address for the Tx FIFO + * @res0: mapped resource block 0 + * @res1: mapped resource block 1 + * @rx: rx io resource + * @tx: tx io resource * @rx_fifo_size: number of entries of the Rx FIFO * @tx_fifo_size: number of entries of the Tx FIFO * @pend_events: pending bits for deferred events @@ -155,8 +172,10 @@ struct mlxbf_tmfifo_irq_info { struct mlxbf_tmfifo { struct mlxbf_tmfifo_vdev *vdev[MLXBF_TMFIFO_VDEV_MAX]; struct mutex lock; /* TmFifo lock */ - void __iomem *rx_base; - void __iomem *tx_base; + void __iomem *res0; + void __iomem *res1; + struct mlxbf_tmfifo_io rx; + struct mlxbf_tmfifo_io tx; int rx_fifo_size; int tx_fifo_size; unsigned long pend_events; @@ -472,7 +491,7 @@ static int mlxbf_tmfifo_get_rx_avail(struct mlxbf_tmfifo *fifo) { u64 sts; - sts = readq(fifo->rx_base + MLXBF_TMFIFO_RX_STS); + sts = readq(fifo->rx.sts); return FIELD_GET(MLXBF_TMFIFO_RX_STS__COUNT_MASK, sts); } @@ -489,7 +508,7 @@ static int mlxbf_tmfifo_get_tx_avail(struct mlxbf_tmfifo *fifo, int vdev_id) else tx_reserve = 1; - sts = readq(fifo->tx_base + MLXBF_TMFIFO_TX_STS); + sts = readq(fifo->tx.sts); count = FIELD_GET(MLXBF_TMFIFO_TX_STS__COUNT_MASK, sts); return fifo->tx_fifo_size - tx_reserve - count; } @@ -525,7 +544,7 @@ static void mlxbf_tmfifo_console_tx(struct mlxbf_tmfifo *fifo, int avail) /* Write header. */ hdr.type = VIRTIO_ID_CONSOLE; hdr.len = htons(size); - writeq(*(u64 *)&hdr, fifo->tx_base + MLXBF_TMFIFO_TX_DATA); + writeq(*(u64 *)&hdr, fifo->tx.data); /* Use spin-lock to protect the 'cons->tx_buf'. */ spin_lock_irqsave(&fifo->spin_lock[0], flags); @@ -542,7 +561,7 @@ static void mlxbf_tmfifo_console_tx(struct mlxbf_tmfifo *fifo, int avail) memcpy((u8 *)&data + seg, cons->tx_buf.buf, sizeof(u64) - seg); } - writeq(data, fifo->tx_base + MLXBF_TMFIFO_TX_DATA); + writeq(data, fifo->tx.data); if (size >= sizeof(u64)) { cons->tx_buf.tail = (cons->tx_buf.tail + sizeof(u64)) % @@ -573,7 +592,7 @@ static void mlxbf_tmfifo_rxtx_word(struct mlxbf_tmfifo_vring *vring, /* Read a word from FIFO for Rx. */ if (is_rx) - data = readq(fifo->rx_base + MLXBF_TMFIFO_RX_DATA); + data = readq(fifo->rx.data); if (vring->cur_len + sizeof(u64) <= len) { /* The whole word. */ @@ -595,7 +614,7 @@ static void mlxbf_tmfifo_rxtx_word(struct mlxbf_tmfifo_vring *vring, /* Write the word into FIFO for Tx. */ if (!is_rx) - writeq(data, fifo->tx_base + MLXBF_TMFIFO_TX_DATA); + writeq(data, fifo->tx.data); } /* @@ -617,7 +636,7 @@ static void mlxbf_tmfifo_rxtx_header(struct mlxbf_tmfifo_vring *vring, /* Read/Write packet header. */ if (is_rx) { /* Drain one word from the FIFO. */ - *(u64 *)&hdr = readq(fifo->rx_base + MLXBF_TMFIFO_RX_DATA); + *(u64 *)&hdr = readq(fifo->rx.data); /* Skip the length 0 packets (keepalive). */ if (hdr.len == 0) @@ -661,7 +680,7 @@ static void mlxbf_tmfifo_rxtx_header(struct mlxbf_tmfifo_vring *vring, hdr.type = (vring->vdev_id == VIRTIO_ID_NET) ? VIRTIO_ID_NET : VIRTIO_ID_CONSOLE; hdr.len = htons(vring->pkt_len - hdr_len); - writeq(*(u64 *)&hdr, fifo->tx_base + MLXBF_TMFIFO_TX_DATA); + writeq(*(u64 *)&hdr, fifo->tx.data); } vring->cur_len = hdr_len; @@ -1157,7 +1176,7 @@ static void mlxbf_tmfifo_set_threshold(struct mlxbf_tmfifo *fifo) u64 ctl; /* Get Tx FIFO size and set the low/high watermark. */ - ctl = readq(fifo->tx_base + MLXBF_TMFIFO_TX_CTL); + ctl = readq(fifo->tx.ctl); fifo->tx_fifo_size = FIELD_GET(MLXBF_TMFIFO_TX_CTL__MAX_ENTRIES_MASK, ctl); ctl = (ctl & ~MLXBF_TMFIFO_TX_CTL__LWM_MASK) | @@ -1166,17 +1185,17 @@ static void mlxbf_tmfifo_set_threshold(struct mlxbf_tmfifo *fifo) ctl = (ctl & ~MLXBF_TMFIFO_TX_CTL__HWM_MASK) | FIELD_PREP(MLXBF_TMFIFO_TX_CTL__HWM_MASK, fifo->tx_fifo_size - 1); - writeq(ctl, fifo->tx_base + MLXBF_TMFIFO_TX_CTL); + writeq(ctl, fifo->tx.ctl); /* Get Rx FIFO size and set the low/high watermark. */ - ctl = readq(fifo->rx_base + MLXBF_TMFIFO_RX_CTL); + ctl = readq(fifo->rx.ctl); fifo->rx_fifo_size = FIELD_GET(MLXBF_TMFIFO_RX_CTL__MAX_ENTRIES_MASK, ctl); ctl = (ctl & ~MLXBF_TMFIFO_RX_CTL__LWM_MASK) | FIELD_PREP(MLXBF_TMFIFO_RX_CTL__LWM_MASK, 0); ctl = (ctl & ~MLXBF_TMFIFO_RX_CTL__HWM_MASK) | FIELD_PREP(MLXBF_TMFIFO_RX_CTL__HWM_MASK, 1); - writeq(ctl, fifo->rx_base + MLXBF_TMFIFO_RX_CTL); + writeq(ctl, fifo->rx.ctl); } static void mlxbf_tmfifo_cleanup(struct mlxbf_tmfifo *fifo) @@ -1197,8 +1216,15 @@ static int mlxbf_tmfifo_probe(struct platform_device *pdev) struct virtio_net_config net_config; struct device *dev = &pdev->dev; struct mlxbf_tmfifo *fifo; + u64 dev_id; int i, rc; + rc = acpi_dev_uid_to_integer(ACPI_COMPANION(dev), &dev_id); + if (rc) { + dev_err(dev, "Cannot retrieve UID\n"); + return rc; + } + fifo = devm_kzalloc(dev, sizeof(*fifo), GFP_KERNEL); if (!fifo) return -ENOMEM; @@ -1209,14 +1235,30 @@ static int mlxbf_tmfifo_probe(struct platform_device *pdev) mutex_init(&fifo->lock); /* Get the resource of the Rx FIFO. */ - fifo->rx_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(fifo->rx_base)) - return PTR_ERR(fifo->rx_base); + fifo->res0 = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(fifo->res0)) + return PTR_ERR(fifo->res0); /* Get the resource of the Tx FIFO. */ - fifo->tx_base = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(fifo->tx_base)) - return PTR_ERR(fifo->tx_base); + fifo->res1 = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(fifo->res1)) + return PTR_ERR(fifo->res1); + + if (dev_id == TMFIFO_BF3_UID) { + fifo->rx.ctl = fifo->res1 + MLXBF_TMFIFO_RX_CTL_BF3; + fifo->rx.sts = fifo->res1 + MLXBF_TMFIFO_RX_STS_BF3; + fifo->rx.data = fifo->res0 + MLXBF_TMFIFO_RX_DATA_BF3; + fifo->tx.ctl = fifo->res1 + MLXBF_TMFIFO_TX_CTL_BF3; + fifo->tx.sts = fifo->res1 + MLXBF_TMFIFO_TX_STS_BF3; + fifo->tx.data = fifo->res0 + MLXBF_TMFIFO_TX_DATA_BF3; + } else { + fifo->rx.ctl = fifo->res0 + MLXBF_TMFIFO_RX_CTL; + fifo->rx.sts = fifo->res0 + MLXBF_TMFIFO_RX_STS; + fifo->rx.data = fifo->res0 + MLXBF_TMFIFO_RX_DATA; + fifo->tx.ctl = fifo->res1 + MLXBF_TMFIFO_TX_CTL; + fifo->tx.sts = fifo->res1 + MLXBF_TMFIFO_TX_STS; + fifo->tx.data = fifo->res1 + MLXBF_TMFIFO_TX_DATA; + } platform_set_drvdata(pdev, fifo); -- cgit v1.2.3-58-ga151 From 26174aaee3832d681dcc0744b005021c7480a8f6 Mon Sep 17 00:00:00 2001 From: Jilin Yuan Date: Wed, 19 Oct 2022 20:53:55 +0800 Subject: platform/x86/intel: pmc: Fix repeated word in comment Delete the redundant word 'to'. Signed-off-by: Jilin Yuan Link: https://lore.kernel.org/r/20221019125355.50674-1-yuanjilin@cdjrlc.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 7a059e02c265..4c7e77f1ccac 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -264,7 +264,7 @@ struct pmc_bit_map { * @slp_s0_offset: PWRMBASE offset to read SLP_S0 residency * @ltr_ignore_offset: PWRMBASE offset to read/write LTR ignore bit * @regmap_length: Length of memory to map from PWRMBASE address to access - * @ppfear0_offset: PWRMBASE offset to to read PPFEAR* + * @ppfear0_offset: PWRMBASE offset to read PPFEAR* * @ppfear_buckets: Number of 8 bits blocks to read all IP blocks from * PPFEAR * @pm_cfg_offset: PWRMBASE offset to PM_CFG register -- cgit v1.2.3-58-ga151 From 6e9b8992b122cb12688bd259fc99e67d1be234eb Mon Sep 17 00:00:00 2001 From: Jorge Lopez Date: Thu, 20 Oct 2022 15:10:28 -0500 Subject: platform/x86: Move existing HP drivers to a new hp subdir The purpose of this patch is to provide a central location where all HP related drivers are found. HP drivers will recide under drivers/platform/x86/hp directory. Introduce changes to Kconfig file to list all HP driver under "HP X86 Platform Specific Device Drivers" menu option. Additional changes include update MAINTAINERS file to indicate hp related drivers new path. Signed-off-by: Jorge Lopez Link: https://lore.kernel.org/r/20221020201033.12790-2-jorge.lopez2@hp.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- MAINTAINERS | 4 +- drivers/platform/x86/Kconfig | 43 +- drivers/platform/x86/Makefile | 4 +- drivers/platform/x86/hp-wmi.c | 1566 ---------------------------------- drivers/platform/x86/hp/Kconfig | 63 ++ drivers/platform/x86/hp/Makefile | 10 + drivers/platform/x86/hp/hp-wmi.c | 1566 ++++++++++++++++++++++++++++++++++ drivers/platform/x86/hp/hp_accel.c | 387 +++++++++ drivers/platform/x86/hp/tc1100-wmi.c | 265 ++++++ drivers/platform/x86/hp_accel.c | 387 --------- drivers/platform/x86/tc1100-wmi.c | 265 ------ 11 files changed, 2295 insertions(+), 2265 deletions(-) delete mode 100644 drivers/platform/x86/hp-wmi.c create mode 100644 drivers/platform/x86/hp/Kconfig create mode 100644 drivers/platform/x86/hp/Makefile create mode 100644 drivers/platform/x86/hp/hp-wmi.c create mode 100644 drivers/platform/x86/hp/hp_accel.c create mode 100644 drivers/platform/x86/hp/tc1100-wmi.c delete mode 100644 drivers/platform/x86/hp_accel.c delete mode 100644 drivers/platform/x86/tc1100-wmi.c (limited to 'drivers/platform') diff --git a/MAINTAINERS b/MAINTAINERS index f8fa72d1452e..893c63e8beef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9366,7 +9366,7 @@ F: drivers/net/wireless/intersil/hostap/ HP COMPAQ TC1100 TABLET WMI EXTRAS DRIVER L: platform-driver-x86@vger.kernel.org S: Orphan -F: drivers/platform/x86/tc1100-wmi.c +F: drivers/platform/x86/hp/tc1100-wmi.c HPET: High Precision Event Timers driver M: Clemens Ladisch @@ -11856,7 +11856,7 @@ M: Eric Piel S: Maintained F: Documentation/misc-devices/lis3lv02d.rst F: drivers/misc/lis3lv02d/ -F: drivers/platform/x86/hp_accel.c +F: drivers/platform/x86/hp/hp_accel.c LIST KUNIT TEST M: David Gow diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index f5312f51de19..5692385e2d26 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -424,24 +424,7 @@ config GPD_POCKET_FAN of the CPU temperature. Say Y or M if the kernel may be used on a GPD pocket. -config HP_ACCEL - tristate "HP laptop accelerometer" - depends on INPUT && ACPI - depends on SERIO_I8042 - select SENSORS_LIS3LV02D - select NEW_LEDS - select LEDS_CLASS - help - This driver provides support for the "Mobile Data Protection System 3D" - or "3D DriveGuard" feature of HP laptops. On such systems the driver - should load automatically (via ACPI alias). - - Support for a led indicating disk protection will be provided as - hp::hddprotect. For more information on the feature, refer to - Documentation/misc-devices/lis3lv02d.rst. - - To compile this driver as a module, choose M here: the module will - be called hp_accel. +source "drivers/platform/x86/hp/Kconfig" config WIRELESS_HOTKEY tristate "Wireless hotkey button" @@ -455,30 +438,6 @@ config WIRELESS_HOTKEY To compile this driver as a module, choose M here: the module will be called wireless-hotkey. -config HP_WMI - tristate "HP WMI extras" - depends on ACPI_WMI - depends on INPUT - depends on RFKILL || RFKILL = n - select INPUT_SPARSEKMAP - select ACPI_PLATFORM_PROFILE - select HWMON - help - Say Y here if you want to support WMI-based hotkeys on HP laptops and - to read data from WMI such as docking or ambient light sensor state. - - To compile this driver as a module, choose M here: the module will - be called hp-wmi. - -config TC1100_WMI - tristate "HP Compaq TC1100 Tablet WMI Extras" - depends on !X86_64 - depends on ACPI - depends on ACPI_WMI - help - This is a driver for the WMI extensions (wireless and bluetooth power - control) of the HP Compaq TC1100 tablet. - config IBM_RTL tristate "Device driver to enable PRTL support" depends on PCI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 5a428caa654a..1d3d1b02541b 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -55,9 +55,7 @@ obj-$(CONFIG_FUJITSU_TABLET) += fujitsu-tablet.o obj-$(CONFIG_GPD_POCKET_FAN) += gpd-pocket-fan.o # Hewlett Packard -obj-$(CONFIG_HP_ACCEL) += hp_accel.o -obj-$(CONFIG_HP_WMI) += hp-wmi.o -obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o +obj-$(CONFIG_X86_PLATFORM_DRIVERS_HP) += hp/ # Hewlett Packard Enterprise obj-$(CONFIG_UV_SYSFS) += uv_sysfs.o diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c deleted file mode 100644 index 0a99058be813..000000000000 --- a/drivers/platform/x86/hp-wmi.c +++ /dev/null @@ -1,1566 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HP WMI hotkeys - * - * Copyright (C) 2008 Red Hat - * Copyright (C) 2010, 2011 Anssi Hannula - * - * Portions based on wistron_btns.c: - * Copyright (C) 2005 Miloslav Trmac - * Copyright (C) 2005 Bernhard Rosenkraenzer - * Copyright (C) 2005 Dmitry Torokhov - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Matthew Garrett "); -MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); -MODULE_LICENSE("GPL"); - -MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C"); -MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); - -#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" -#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4" -#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95 -#define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required - -/* DMI board names of devices that should use the omen specific path for - * thermal profiles. - * This was obtained by taking a look in the windows omen command center - * app and parsing a json file that they use to figure out what capabilities - * the device should have. - * A device is considered an omen if the DisplayName in that list contains - * "OMEN", and it can use the thermal profile stuff if the "Feature" array - * contains "PerformanceControl". - */ -static const char * const omen_thermal_profile_boards[] = { - "84DA", "84DB", "84DC", "8574", "8575", "860A", "87B5", "8572", "8573", - "8600", "8601", "8602", "8605", "8606", "8607", "8746", "8747", "8749", - "874A", "8603", "8604", "8748", "886B", "886C", "878A", "878B", "878C", - "88C8", "88CB", "8786", "8787", "8788", "88D1", "88D2", "88F4", "88FD", - "88F5", "88F6", "88F7", "88FE", "88FF", "8900", "8901", "8902", "8912", - "8917", "8918", "8949", "894A", "89EB" -}; - -/* DMI Board names of Omen laptops that are specifically set to be thermal - * profile version 0 by the Omen Command Center app, regardless of what - * the get system design information WMI call returns - */ -static const char *const omen_thermal_profile_force_v0_boards[] = { - "8607", "8746", "8747", "8749", "874A", "8748" -}; - -enum hp_wmi_radio { - HPWMI_WIFI = 0x0, - HPWMI_BLUETOOTH = 0x1, - HPWMI_WWAN = 0x2, - HPWMI_GPS = 0x3, -}; - -enum hp_wmi_event_ids { - HPWMI_DOCK_EVENT = 0x01, - HPWMI_PARK_HDD = 0x02, - HPWMI_SMART_ADAPTER = 0x03, - HPWMI_BEZEL_BUTTON = 0x04, - HPWMI_WIRELESS = 0x05, - HPWMI_CPU_BATTERY_THROTTLE = 0x06, - HPWMI_LOCK_SWITCH = 0x07, - HPWMI_LID_SWITCH = 0x08, - HPWMI_SCREEN_ROTATION = 0x09, - HPWMI_COOLSENSE_SYSTEM_MOBILE = 0x0A, - HPWMI_COOLSENSE_SYSTEM_HOT = 0x0B, - HPWMI_PROXIMITY_SENSOR = 0x0C, - HPWMI_BACKLIT_KB_BRIGHTNESS = 0x0D, - HPWMI_PEAKSHIFT_PERIOD = 0x0F, - HPWMI_BATTERY_CHARGE_PERIOD = 0x10, - HPWMI_SANITIZATION_MODE = 0x17, - HPWMI_SMART_EXPERIENCE_APP = 0x21, -}; - -/* - * struct bios_args buffer is dynamically allocated. New WMI command types - * were introduced that exceeds 128-byte data size. Changes to handle - * the data size allocation scheme were kept in hp_wmi_perform_qurey function. - */ -struct bios_args { - u32 signature; - u32 command; - u32 commandtype; - u32 datasize; - u8 data[]; -}; - -enum hp_wmi_commandtype { - HPWMI_DISPLAY_QUERY = 0x01, - HPWMI_HDDTEMP_QUERY = 0x02, - HPWMI_ALS_QUERY = 0x03, - HPWMI_HARDWARE_QUERY = 0x04, - HPWMI_WIRELESS_QUERY = 0x05, - HPWMI_BATTERY_QUERY = 0x07, - HPWMI_BIOS_QUERY = 0x09, - HPWMI_FEATURE_QUERY = 0x0b, - HPWMI_HOTKEY_QUERY = 0x0c, - HPWMI_FEATURE2_QUERY = 0x0d, - HPWMI_WIRELESS2_QUERY = 0x1b, - HPWMI_POSTCODEERROR_QUERY = 0x2a, - HPWMI_SYSTEM_DEVICE_MODE = 0x40, - HPWMI_THERMAL_PROFILE_QUERY = 0x4c, -}; - -enum hp_wmi_gm_commandtype { - HPWMI_FAN_SPEED_GET_QUERY = 0x11, - HPWMI_SET_PERFORMANCE_MODE = 0x1A, - HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26, - HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27, - HPWMI_GET_SYSTEM_DESIGN_DATA = 0x28, -}; - -enum hp_wmi_command { - HPWMI_READ = 0x01, - HPWMI_WRITE = 0x02, - HPWMI_ODM = 0x03, - HPWMI_GM = 0x20008, -}; - -enum hp_wmi_hardware_mask { - HPWMI_DOCK_MASK = 0x01, - HPWMI_TABLET_MASK = 0x04, -}; - -struct bios_return { - u32 sigpass; - u32 return_code; -}; - -enum hp_return_value { - HPWMI_RET_WRONG_SIGNATURE = 0x02, - HPWMI_RET_UNKNOWN_COMMAND = 0x03, - HPWMI_RET_UNKNOWN_CMDTYPE = 0x04, - HPWMI_RET_INVALID_PARAMETERS = 0x05, -}; - -enum hp_wireless2_bits { - HPWMI_POWER_STATE = 0x01, - HPWMI_POWER_SOFT = 0x02, - HPWMI_POWER_BIOS = 0x04, - HPWMI_POWER_HARD = 0x08, - HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD, -}; - -enum hp_thermal_profile_omen_v0 { - HP_OMEN_V0_THERMAL_PROFILE_DEFAULT = 0x00, - HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE = 0x01, - HP_OMEN_V0_THERMAL_PROFILE_COOL = 0x02, -}; - -enum hp_thermal_profile_omen_v1 { - HP_OMEN_V1_THERMAL_PROFILE_DEFAULT = 0x30, - HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE = 0x31, - HP_OMEN_V1_THERMAL_PROFILE_COOL = 0x50, -}; - -enum hp_thermal_profile { - HP_THERMAL_PROFILE_PERFORMANCE = 0x00, - HP_THERMAL_PROFILE_DEFAULT = 0x01, - HP_THERMAL_PROFILE_COOL = 0x02, - HP_THERMAL_PROFILE_QUIET = 0x03, -}; - -#define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW) -#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT) - -struct bios_rfkill2_device_state { - u8 radio_type; - u8 bus_type; - u16 vendor_id; - u16 product_id; - u16 subsys_vendor_id; - u16 subsys_product_id; - u8 rfkill_id; - u8 power; - u8 unknown[4]; -}; - -/* 7 devices fit into the 128 byte buffer */ -#define HPWMI_MAX_RFKILL2_DEVICES 7 - -struct bios_rfkill2_state { - u8 unknown[7]; - u8 count; - u8 pad[8]; - struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES]; -}; - -static const struct key_entry hp_wmi_keymap[] = { - { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, - { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, - { KE_KEY, 0x20e6, { KEY_PROG1 } }, - { KE_KEY, 0x20e8, { KEY_MEDIA } }, - { KE_KEY, 0x2142, { KEY_MEDIA } }, - { KE_KEY, 0x213b, { KEY_INFO } }, - { KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } }, - { KE_KEY, 0x216a, { KEY_SETUP } }, - { KE_KEY, 0x21a9, { KEY_TOUCHPAD_OFF } }, - { KE_KEY, 0x121a9, { KEY_TOUCHPAD_ON } }, - { KE_KEY, 0x231b, { KEY_HELP } }, - { KE_END, 0 } -}; - -static struct input_dev *hp_wmi_input_dev; -static struct platform_device *hp_wmi_platform_dev; -static struct platform_profile_handler platform_profile_handler; -static bool platform_profile_support; -static bool zero_insize_support; - -static struct rfkill *wifi_rfkill; -static struct rfkill *bluetooth_rfkill; -static struct rfkill *wwan_rfkill; - -struct rfkill2_device { - u8 id; - int num; - struct rfkill *rfkill; -}; - -static int rfkill2_count; -static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES]; - -/* - * Chassis Types values were obtained from SMBIOS reference - * specification version 3.00. A complete list of system enclosures - * and chassis types is available on Table 17. - */ -static const char * const tablet_chassis_types[] = { - "30", /* Tablet*/ - "31", /* Convertible */ - "32" /* Detachable */ -}; - -#define DEVICE_MODE_TABLET 0x06 - -/* map output size to the corresponding WMI method id */ -static inline int encode_outsize_for_pvsz(int outsize) -{ - if (outsize > 4096) - return -EINVAL; - if (outsize > 1024) - return 5; - if (outsize > 128) - return 4; - if (outsize > 4) - return 3; - if (outsize > 0) - return 2; - return 1; -} - -/* - * hp_wmi_perform_query - * - * query: The commandtype (enum hp_wmi_commandtype) - * write: The command (enum hp_wmi_command) - * buffer: Buffer used as input and/or output - * insize: Size of input buffer - * outsize: Size of output buffer - * - * returns zero on success - * an HP WMI query specific error code (which is positive) - * -EINVAL if the query was not successful at all - * -EINVAL if the output buffer size exceeds buffersize - * - * Note: The buffersize must at least be the maximum of the input and output - * size. E.g. Battery info query is defined to have 1 byte input - * and 128 byte output. The caller would do: - * buffer = kzalloc(128, GFP_KERNEL); - * ret = hp_wmi_perform_query(HPWMI_BATTERY_QUERY, HPWMI_READ, buffer, 1, 128) - */ -static int hp_wmi_perform_query(int query, enum hp_wmi_command command, - void *buffer, int insize, int outsize) -{ - struct acpi_buffer input, output = { ACPI_ALLOCATE_BUFFER, NULL }; - struct bios_return *bios_return; - union acpi_object *obj = NULL; - struct bios_args *args = NULL; - int mid, actual_insize, actual_outsize; - size_t bios_args_size; - int ret; - - mid = encode_outsize_for_pvsz(outsize); - if (WARN_ON(mid < 0)) - return mid; - - actual_insize = max(insize, 128); - bios_args_size = struct_size(args, data, actual_insize); - args = kmalloc(bios_args_size, GFP_KERNEL); - if (!args) - return -ENOMEM; - - input.length = bios_args_size; - input.pointer = args; - - args->signature = 0x55434553; - args->command = command; - args->commandtype = query; - args->datasize = insize; - memcpy(args->data, buffer, flex_array_size(args, data, insize)); - - ret = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output); - if (ret) - goto out_free; - - obj = output.pointer; - if (!obj) { - ret = -EINVAL; - goto out_free; - } - - if (obj->type != ACPI_TYPE_BUFFER) { - pr_warn("query 0x%x returned an invalid object 0x%x\n", query, ret); - ret = -EINVAL; - goto out_free; - } - - bios_return = (struct bios_return *)obj->buffer.pointer; - ret = bios_return->return_code; - - if (ret) { - if (ret != HPWMI_RET_UNKNOWN_COMMAND && - ret != HPWMI_RET_UNKNOWN_CMDTYPE) - pr_warn("query 0x%x returned error 0x%x\n", query, ret); - goto out_free; - } - - /* Ignore output data of zero size */ - if (!outsize) - goto out_free; - - actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return))); - memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize); - memset(buffer + actual_outsize, 0, outsize - actual_outsize); - -out_free: - kfree(obj); - kfree(args); - return ret; -} - -static int hp_wmi_get_fan_speed(int fan) -{ - u8 fsh, fsl; - char fan_data[4] = { fan, 0, 0, 0 }; - - int ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_GET_QUERY, HPWMI_GM, - &fan_data, sizeof(char), - sizeof(fan_data)); - - if (ret != 0) - return -EINVAL; - - fsh = fan_data[2]; - fsl = fan_data[3]; - - return (fsh << 8) | fsl; -} - -static int hp_wmi_read_int(int query) -{ - int val = 0, ret; - - ret = hp_wmi_perform_query(query, HPWMI_READ, &val, - zero_if_sup(val), sizeof(val)); - - if (ret) - return ret < 0 ? ret : -EINVAL; - - return val; -} - -static int hp_wmi_get_dock_state(void) -{ - int state = hp_wmi_read_int(HPWMI_HARDWARE_QUERY); - - if (state < 0) - return state; - - return !!(state & HPWMI_DOCK_MASK); -} - -static int hp_wmi_get_tablet_mode(void) -{ - char system_device_mode[4] = { 0 }; - const char *chassis_type; - bool tablet_found; - int ret; - - chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE); - if (!chassis_type) - return -ENODEV; - - tablet_found = match_string(tablet_chassis_types, - ARRAY_SIZE(tablet_chassis_types), - chassis_type) >= 0; - if (!tablet_found) - return -ENODEV; - - ret = hp_wmi_perform_query(HPWMI_SYSTEM_DEVICE_MODE, HPWMI_READ, - system_device_mode, zero_if_sup(system_device_mode), - sizeof(system_device_mode)); - if (ret < 0) - return ret; - - return system_device_mode[0] == DEVICE_MODE_TABLET; -} - -static int omen_thermal_profile_set(int mode) -{ - char buffer[2] = {0, mode}; - int ret; - - ret = hp_wmi_perform_query(HPWMI_SET_PERFORMANCE_MODE, HPWMI_GM, - &buffer, sizeof(buffer), 0); - - if (ret) - return ret < 0 ? ret : -EINVAL; - - return mode; -} - -static bool is_omen_thermal_profile(void) -{ - const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); - - if (!board_name) - return false; - - return match_string(omen_thermal_profile_boards, - ARRAY_SIZE(omen_thermal_profile_boards), - board_name) >= 0; -} - -static int omen_get_thermal_policy_version(void) -{ - unsigned char buffer[8] = { 0 }; - int ret; - - const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); - - if (board_name) { - int matches = match_string(omen_thermal_profile_force_v0_boards, - ARRAY_SIZE(omen_thermal_profile_force_v0_boards), - board_name); - if (matches >= 0) - return 0; - } - - ret = hp_wmi_perform_query(HPWMI_GET_SYSTEM_DESIGN_DATA, HPWMI_GM, - &buffer, sizeof(buffer), sizeof(buffer)); - - if (ret) - return ret < 0 ? ret : -EINVAL; - - return buffer[3]; -} - -static int omen_thermal_profile_get(void) -{ - u8 data; - - int ret = ec_read(HP_OMEN_EC_THERMAL_PROFILE_OFFSET, &data); - - if (ret) - return ret; - - return data; -} - -static int hp_wmi_fan_speed_max_set(int enabled) -{ - int ret; - - ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_SET_QUERY, HPWMI_GM, - &enabled, sizeof(enabled), 0); - - if (ret) - return ret < 0 ? ret : -EINVAL; - - return enabled; -} - -static int hp_wmi_fan_speed_max_get(void) -{ - int val = 0, ret; - - ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_GET_QUERY, HPWMI_GM, - &val, zero_if_sup(val), sizeof(val)); - - if (ret) - return ret < 0 ? ret : -EINVAL; - - return val; -} - -static int __init hp_wmi_bios_2008_later(void) -{ - int state = 0; - int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, HPWMI_READ, &state, - zero_if_sup(state), sizeof(state)); - if (!ret) - return 1; - - return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; -} - -static int __init hp_wmi_bios_2009_later(void) -{ - u8 state[128]; - int ret = hp_wmi_perform_query(HPWMI_FEATURE2_QUERY, HPWMI_READ, &state, - zero_if_sup(state), sizeof(state)); - if (!ret) - return 1; - - return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; -} - -static int __init hp_wmi_enable_hotkeys(void) -{ - int value = 0x6e; - int ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, HPWMI_WRITE, &value, - sizeof(value), 0); - - return ret <= 0 ? ret : -EINVAL; -} - -static int hp_wmi_set_block(void *data, bool blocked) -{ - enum hp_wmi_radio r = (enum hp_wmi_radio) data; - int query = BIT(r + 8) | ((!blocked) << r); - int ret; - - ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, - &query, sizeof(query), 0); - - return ret <= 0 ? ret : -EINVAL; -} - -static const struct rfkill_ops hp_wmi_rfkill_ops = { - .set_block = hp_wmi_set_block, -}; - -static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) -{ - int mask = 0x200 << (r * 8); - - int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); - - /* TBD: Pass error */ - WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY"); - - return !(wireless & mask); -} - -static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) -{ - int mask = 0x800 << (r * 8); - - int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); - - /* TBD: Pass error */ - WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY"); - - return !(wireless & mask); -} - -static int hp_wmi_rfkill2_set_block(void *data, bool blocked) -{ - int rfkill_id = (int)(long)data; - char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked }; - int ret; - - ret = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_WRITE, - buffer, sizeof(buffer), 0); - - return ret <= 0 ? ret : -EINVAL; -} - -static const struct rfkill_ops hp_wmi_rfkill2_ops = { - .set_block = hp_wmi_rfkill2_set_block, -}; - -static int hp_wmi_rfkill2_refresh(void) -{ - struct bios_rfkill2_state state; - int err, i; - - err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, - zero_if_sup(state), sizeof(state)); - if (err) - return err; - - for (i = 0; i < rfkill2_count; i++) { - int num = rfkill2[i].num; - struct bios_rfkill2_device_state *devstate; - - devstate = &state.device[num]; - - if (num >= state.count || - devstate->rfkill_id != rfkill2[i].id) { - pr_warn("power configuration of the wireless devices unexpectedly changed\n"); - continue; - } - - rfkill_set_states(rfkill2[i].rfkill, - IS_SWBLOCKED(devstate->power), - IS_HWBLOCKED(devstate->power)); - } - - return 0; -} - -static ssize_t display_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int value = hp_wmi_read_int(HPWMI_DISPLAY_QUERY); - - if (value < 0) - return value; - return sprintf(buf, "%d\n", value); -} - -static ssize_t hddtemp_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int value = hp_wmi_read_int(HPWMI_HDDTEMP_QUERY); - - if (value < 0) - return value; - return sprintf(buf, "%d\n", value); -} - -static ssize_t als_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int value = hp_wmi_read_int(HPWMI_ALS_QUERY); - - if (value < 0) - return value; - return sprintf(buf, "%d\n", value); -} - -static ssize_t dock_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int value = hp_wmi_get_dock_state(); - - if (value < 0) - return value; - return sprintf(buf, "%d\n", value); -} - -static ssize_t tablet_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int value = hp_wmi_get_tablet_mode(); - - if (value < 0) - return value; - return sprintf(buf, "%d\n", value); -} - -static ssize_t postcode_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - /* Get the POST error code of previous boot failure. */ - int value = hp_wmi_read_int(HPWMI_POSTCODEERROR_QUERY); - - if (value < 0) - return value; - return sprintf(buf, "0x%x\n", value); -} - -static ssize_t als_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - u32 tmp; - int ret; - - ret = kstrtou32(buf, 10, &tmp); - if (ret) - return ret; - - ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp, - sizeof(tmp), 0); - if (ret) - return ret < 0 ? ret : -EINVAL; - - return count; -} - -static ssize_t postcode_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - u32 tmp = 1; - bool clear; - int ret; - - ret = kstrtobool(buf, &clear); - if (ret) - return ret; - - if (clear == false) - return -EINVAL; - - /* Clear the POST error code. It is kept until cleared. */ - ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_WRITE, &tmp, - sizeof(tmp), 0); - if (ret) - return ret < 0 ? ret : -EINVAL; - - return count; -} - -static DEVICE_ATTR_RO(display); -static DEVICE_ATTR_RO(hddtemp); -static DEVICE_ATTR_RW(als); -static DEVICE_ATTR_RO(dock); -static DEVICE_ATTR_RO(tablet); -static DEVICE_ATTR_RW(postcode); - -static struct attribute *hp_wmi_attrs[] = { - &dev_attr_display.attr, - &dev_attr_hddtemp.attr, - &dev_attr_als.attr, - &dev_attr_dock.attr, - &dev_attr_tablet.attr, - &dev_attr_postcode.attr, - NULL, -}; -ATTRIBUTE_GROUPS(hp_wmi); - -static void hp_wmi_notify(u32 value, void *context) -{ - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; - u32 event_id, event_data; - union acpi_object *obj; - acpi_status status; - u32 *location; - int key_code; - - status = wmi_get_event_data(value, &response); - if (status != AE_OK) { - pr_info("bad event status 0x%x\n", status); - return; - } - - obj = (union acpi_object *)response.pointer; - - if (!obj) - return; - if (obj->type != ACPI_TYPE_BUFFER) { - pr_info("Unknown response received %d\n", obj->type); - kfree(obj); - return; - } - - /* - * Depending on ACPI version the concatenation of id and event data - * inside _WED function will result in a 8 or 16 byte buffer. - */ - location = (u32 *)obj->buffer.pointer; - if (obj->buffer.length == 8) { - event_id = *location; - event_data = *(location + 1); - } else if (obj->buffer.length == 16) { - event_id = *location; - event_data = *(location + 2); - } else { - pr_info("Unknown buffer length %d\n", obj->buffer.length); - kfree(obj); - return; - } - kfree(obj); - - switch (event_id) { - case HPWMI_DOCK_EVENT: - if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit)) - input_report_switch(hp_wmi_input_dev, SW_DOCK, - hp_wmi_get_dock_state()); - if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit)) - input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, - hp_wmi_get_tablet_mode()); - input_sync(hp_wmi_input_dev); - break; - case HPWMI_PARK_HDD: - break; - case HPWMI_SMART_ADAPTER: - break; - case HPWMI_BEZEL_BUTTON: - key_code = hp_wmi_read_int(HPWMI_HOTKEY_QUERY); - if (key_code < 0) - break; - - if (!sparse_keymap_report_event(hp_wmi_input_dev, - key_code, 1, true)) - pr_info("Unknown key code - 0x%x\n", key_code); - break; - case HPWMI_WIRELESS: - if (rfkill2_count) { - hp_wmi_rfkill2_refresh(); - break; - } - - if (wifi_rfkill) - rfkill_set_states(wifi_rfkill, - hp_wmi_get_sw_state(HPWMI_WIFI), - hp_wmi_get_hw_state(HPWMI_WIFI)); - if (bluetooth_rfkill) - rfkill_set_states(bluetooth_rfkill, - hp_wmi_get_sw_state(HPWMI_BLUETOOTH), - hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); - if (wwan_rfkill) - rfkill_set_states(wwan_rfkill, - hp_wmi_get_sw_state(HPWMI_WWAN), - hp_wmi_get_hw_state(HPWMI_WWAN)); - break; - case HPWMI_CPU_BATTERY_THROTTLE: - pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n"); - break; - case HPWMI_LOCK_SWITCH: - break; - case HPWMI_LID_SWITCH: - break; - case HPWMI_SCREEN_ROTATION: - break; - case HPWMI_COOLSENSE_SYSTEM_MOBILE: - break; - case HPWMI_COOLSENSE_SYSTEM_HOT: - break; - case HPWMI_PROXIMITY_SENSOR: - break; - case HPWMI_BACKLIT_KB_BRIGHTNESS: - break; - case HPWMI_PEAKSHIFT_PERIOD: - break; - case HPWMI_BATTERY_CHARGE_PERIOD: - break; - case HPWMI_SANITIZATION_MODE: - break; - case HPWMI_SMART_EXPERIENCE_APP: - break; - default: - pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data); - break; - } -} - -static int __init hp_wmi_input_setup(void) -{ - acpi_status status; - int err, val; - - hp_wmi_input_dev = input_allocate_device(); - if (!hp_wmi_input_dev) - return -ENOMEM; - - hp_wmi_input_dev->name = "HP WMI hotkeys"; - hp_wmi_input_dev->phys = "wmi/input0"; - hp_wmi_input_dev->id.bustype = BUS_HOST; - - __set_bit(EV_SW, hp_wmi_input_dev->evbit); - - /* Dock */ - val = hp_wmi_get_dock_state(); - if (!(val < 0)) { - __set_bit(SW_DOCK, hp_wmi_input_dev->swbit); - input_report_switch(hp_wmi_input_dev, SW_DOCK, val); - } - - /* Tablet mode */ - val = hp_wmi_get_tablet_mode(); - if (!(val < 0)) { - __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); - input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, val); - } - - err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL); - if (err) - goto err_free_dev; - - /* Set initial hardware state */ - input_sync(hp_wmi_input_dev); - - if (!hp_wmi_bios_2009_later() && hp_wmi_bios_2008_later()) - hp_wmi_enable_hotkeys(); - - status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); - if (ACPI_FAILURE(status)) { - err = -EIO; - goto err_free_dev; - } - - err = input_register_device(hp_wmi_input_dev); - if (err) - goto err_uninstall_notifier; - - return 0; - - err_uninstall_notifier: - wmi_remove_notify_handler(HPWMI_EVENT_GUID); - err_free_dev: - input_free_device(hp_wmi_input_dev); - return err; -} - -static void hp_wmi_input_destroy(void) -{ - wmi_remove_notify_handler(HPWMI_EVENT_GUID); - input_unregister_device(hp_wmi_input_dev); -} - -static int __init hp_wmi_rfkill_setup(struct platform_device *device) -{ - int err, wireless; - - wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); - if (wireless < 0) - return wireless; - - err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, &wireless, - sizeof(wireless), 0); - if (err) - return err; - - if (wireless & 0x1) { - wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, - RFKILL_TYPE_WLAN, - &hp_wmi_rfkill_ops, - (void *) HPWMI_WIFI); - if (!wifi_rfkill) - return -ENOMEM; - rfkill_init_sw_state(wifi_rfkill, - hp_wmi_get_sw_state(HPWMI_WIFI)); - rfkill_set_hw_state(wifi_rfkill, - hp_wmi_get_hw_state(HPWMI_WIFI)); - err = rfkill_register(wifi_rfkill); - if (err) - goto register_wifi_error; - } - - if (wireless & 0x2) { - bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev, - RFKILL_TYPE_BLUETOOTH, - &hp_wmi_rfkill_ops, - (void *) HPWMI_BLUETOOTH); - if (!bluetooth_rfkill) { - err = -ENOMEM; - goto register_bluetooth_error; - } - rfkill_init_sw_state(bluetooth_rfkill, - hp_wmi_get_sw_state(HPWMI_BLUETOOTH)); - rfkill_set_hw_state(bluetooth_rfkill, - hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); - err = rfkill_register(bluetooth_rfkill); - if (err) - goto register_bluetooth_error; - } - - if (wireless & 0x4) { - wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev, - RFKILL_TYPE_WWAN, - &hp_wmi_rfkill_ops, - (void *) HPWMI_WWAN); - if (!wwan_rfkill) { - err = -ENOMEM; - goto register_wwan_error; - } - rfkill_init_sw_state(wwan_rfkill, - hp_wmi_get_sw_state(HPWMI_WWAN)); - rfkill_set_hw_state(wwan_rfkill, - hp_wmi_get_hw_state(HPWMI_WWAN)); - err = rfkill_register(wwan_rfkill); - if (err) - goto register_wwan_error; - } - - return 0; - -register_wwan_error: - rfkill_destroy(wwan_rfkill); - wwan_rfkill = NULL; - if (bluetooth_rfkill) - rfkill_unregister(bluetooth_rfkill); -register_bluetooth_error: - rfkill_destroy(bluetooth_rfkill); - bluetooth_rfkill = NULL; - if (wifi_rfkill) - rfkill_unregister(wifi_rfkill); -register_wifi_error: - rfkill_destroy(wifi_rfkill); - wifi_rfkill = NULL; - return err; -} - -static int __init hp_wmi_rfkill2_setup(struct platform_device *device) -{ - struct bios_rfkill2_state state; - int err, i; - - err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, - zero_if_sup(state), sizeof(state)); - if (err) - return err < 0 ? err : -EINVAL; - - if (state.count > HPWMI_MAX_RFKILL2_DEVICES) { - pr_warn("unable to parse 0x1b query output\n"); - return -EINVAL; - } - - for (i = 0; i < state.count; i++) { - struct rfkill *rfkill; - enum rfkill_type type; - char *name; - - switch (state.device[i].radio_type) { - case HPWMI_WIFI: - type = RFKILL_TYPE_WLAN; - name = "hp-wifi"; - break; - case HPWMI_BLUETOOTH: - type = RFKILL_TYPE_BLUETOOTH; - name = "hp-bluetooth"; - break; - case HPWMI_WWAN: - type = RFKILL_TYPE_WWAN; - name = "hp-wwan"; - break; - case HPWMI_GPS: - type = RFKILL_TYPE_GPS; - name = "hp-gps"; - break; - default: - pr_warn("unknown device type 0x%x\n", - state.device[i].radio_type); - continue; - } - - if (!state.device[i].vendor_id) { - pr_warn("zero device %d while %d reported\n", - i, state.count); - continue; - } - - rfkill = rfkill_alloc(name, &device->dev, type, - &hp_wmi_rfkill2_ops, (void *)(long)i); - if (!rfkill) { - err = -ENOMEM; - goto fail; - } - - rfkill2[rfkill2_count].id = state.device[i].rfkill_id; - rfkill2[rfkill2_count].num = i; - rfkill2[rfkill2_count].rfkill = rfkill; - - rfkill_init_sw_state(rfkill, - IS_SWBLOCKED(state.device[i].power)); - rfkill_set_hw_state(rfkill, - IS_HWBLOCKED(state.device[i].power)); - - if (!(state.device[i].power & HPWMI_POWER_BIOS)) - pr_info("device %s blocked by BIOS\n", name); - - err = rfkill_register(rfkill); - if (err) { - rfkill_destroy(rfkill); - goto fail; - } - - rfkill2_count++; - } - - return 0; -fail: - for (; rfkill2_count > 0; rfkill2_count--) { - rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill); - rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill); - } - return err; -} - -static int platform_profile_omen_get(struct platform_profile_handler *pprof, - enum platform_profile_option *profile) -{ - int tp; - - tp = omen_thermal_profile_get(); - if (tp < 0) - return tp; - - switch (tp) { - case HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE: - case HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE: - *profile = PLATFORM_PROFILE_PERFORMANCE; - break; - case HP_OMEN_V0_THERMAL_PROFILE_DEFAULT: - case HP_OMEN_V1_THERMAL_PROFILE_DEFAULT: - *profile = PLATFORM_PROFILE_BALANCED; - break; - case HP_OMEN_V0_THERMAL_PROFILE_COOL: - case HP_OMEN_V1_THERMAL_PROFILE_COOL: - *profile = PLATFORM_PROFILE_COOL; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int platform_profile_omen_set(struct platform_profile_handler *pprof, - enum platform_profile_option profile) -{ - int err, tp, tp_version; - - tp_version = omen_get_thermal_policy_version(); - - if (tp_version < 0 || tp_version > 1) - return -EOPNOTSUPP; - - switch (profile) { - case PLATFORM_PROFILE_PERFORMANCE: - if (tp_version == 0) - tp = HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE; - else - tp = HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE; - break; - case PLATFORM_PROFILE_BALANCED: - if (tp_version == 0) - tp = HP_OMEN_V0_THERMAL_PROFILE_DEFAULT; - else - tp = HP_OMEN_V1_THERMAL_PROFILE_DEFAULT; - break; - case PLATFORM_PROFILE_COOL: - if (tp_version == 0) - tp = HP_OMEN_V0_THERMAL_PROFILE_COOL; - else - tp = HP_OMEN_V1_THERMAL_PROFILE_COOL; - break; - default: - return -EOPNOTSUPP; - } - - err = omen_thermal_profile_set(tp); - if (err < 0) - return err; - - return 0; -} - -static int thermal_profile_get(void) -{ - return hp_wmi_read_int(HPWMI_THERMAL_PROFILE_QUERY); -} - -static int thermal_profile_set(int thermal_profile) -{ - return hp_wmi_perform_query(HPWMI_THERMAL_PROFILE_QUERY, HPWMI_WRITE, &thermal_profile, - sizeof(thermal_profile), 0); -} - -static int hp_wmi_platform_profile_get(struct platform_profile_handler *pprof, - enum platform_profile_option *profile) -{ - int tp; - - tp = thermal_profile_get(); - if (tp < 0) - return tp; - - switch (tp) { - case HP_THERMAL_PROFILE_PERFORMANCE: - *profile = PLATFORM_PROFILE_PERFORMANCE; - break; - case HP_THERMAL_PROFILE_DEFAULT: - *profile = PLATFORM_PROFILE_BALANCED; - break; - case HP_THERMAL_PROFILE_COOL: - *profile = PLATFORM_PROFILE_COOL; - break; - case HP_THERMAL_PROFILE_QUIET: - *profile = PLATFORM_PROFILE_QUIET; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int hp_wmi_platform_profile_set(struct platform_profile_handler *pprof, - enum platform_profile_option profile) -{ - int err, tp; - - switch (profile) { - case PLATFORM_PROFILE_PERFORMANCE: - tp = HP_THERMAL_PROFILE_PERFORMANCE; - break; - case PLATFORM_PROFILE_BALANCED: - tp = HP_THERMAL_PROFILE_DEFAULT; - break; - case PLATFORM_PROFILE_COOL: - tp = HP_THERMAL_PROFILE_COOL; - break; - case PLATFORM_PROFILE_QUIET: - tp = HP_THERMAL_PROFILE_QUIET; - break; - default: - return -EOPNOTSUPP; - } - - err = thermal_profile_set(tp); - if (err) - return err; - - return 0; -} - -static int thermal_profile_setup(void) -{ - int err, tp; - - if (is_omen_thermal_profile()) { - tp = omen_thermal_profile_get(); - if (tp < 0) - return tp; - - /* - * call thermal profile write command to ensure that the - * firmware correctly sets the OEM variables - */ - - err = omen_thermal_profile_set(tp); - if (err < 0) - return err; - - platform_profile_handler.profile_get = platform_profile_omen_get; - platform_profile_handler.profile_set = platform_profile_omen_set; - } else { - tp = thermal_profile_get(); - - if (tp < 0) - return tp; - - /* - * call thermal profile write command to ensure that the - * firmware correctly sets the OEM variables for the DPTF - */ - err = thermal_profile_set(tp); - if (err) - return err; - - platform_profile_handler.profile_get = hp_wmi_platform_profile_get; - platform_profile_handler.profile_set = hp_wmi_platform_profile_set; - - set_bit(PLATFORM_PROFILE_QUIET, platform_profile_handler.choices); - } - - set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices); - set_bit(PLATFORM_PROFILE_BALANCED, platform_profile_handler.choices); - set_bit(PLATFORM_PROFILE_PERFORMANCE, platform_profile_handler.choices); - - err = platform_profile_register(&platform_profile_handler); - if (err) - return err; - - platform_profile_support = true; - - return 0; -} - -static int hp_wmi_hwmon_init(void); - -static int __init hp_wmi_bios_setup(struct platform_device *device) -{ - int err; - /* clear detected rfkill devices */ - wifi_rfkill = NULL; - bluetooth_rfkill = NULL; - wwan_rfkill = NULL; - rfkill2_count = 0; - - /* - * In pre-2009 BIOS, command 1Bh return 0x4 to indicate that - * BIOS no longer controls the power for the wireless - * devices. All features supported by this command will no - * longer be supported. - */ - if (!hp_wmi_bios_2009_later()) { - if (hp_wmi_rfkill_setup(device)) - hp_wmi_rfkill2_setup(device); - } - - err = hp_wmi_hwmon_init(); - - if (err < 0) - return err; - - thermal_profile_setup(); - - return 0; -} - -static int __exit hp_wmi_bios_remove(struct platform_device *device) -{ - int i; - - for (i = 0; i < rfkill2_count; i++) { - rfkill_unregister(rfkill2[i].rfkill); - rfkill_destroy(rfkill2[i].rfkill); - } - - if (wifi_rfkill) { - rfkill_unregister(wifi_rfkill); - rfkill_destroy(wifi_rfkill); - } - if (bluetooth_rfkill) { - rfkill_unregister(bluetooth_rfkill); - rfkill_destroy(bluetooth_rfkill); - } - if (wwan_rfkill) { - rfkill_unregister(wwan_rfkill); - rfkill_destroy(wwan_rfkill); - } - - if (platform_profile_support) - platform_profile_remove(); - - return 0; -} - -static int hp_wmi_resume_handler(struct device *device) -{ - /* - * Hardware state may have changed while suspended, so trigger - * input events for the current state. As this is a switch, - * the input layer will only actually pass it on if the state - * changed. - */ - if (hp_wmi_input_dev) { - if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit)) - input_report_switch(hp_wmi_input_dev, SW_DOCK, - hp_wmi_get_dock_state()); - if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit)) - input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, - hp_wmi_get_tablet_mode()); - input_sync(hp_wmi_input_dev); - } - - if (rfkill2_count) - hp_wmi_rfkill2_refresh(); - - if (wifi_rfkill) - rfkill_set_states(wifi_rfkill, - hp_wmi_get_sw_state(HPWMI_WIFI), - hp_wmi_get_hw_state(HPWMI_WIFI)); - if (bluetooth_rfkill) - rfkill_set_states(bluetooth_rfkill, - hp_wmi_get_sw_state(HPWMI_BLUETOOTH), - hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); - if (wwan_rfkill) - rfkill_set_states(wwan_rfkill, - hp_wmi_get_sw_state(HPWMI_WWAN), - hp_wmi_get_hw_state(HPWMI_WWAN)); - - return 0; -} - -static const struct dev_pm_ops hp_wmi_pm_ops = { - .resume = hp_wmi_resume_handler, - .restore = hp_wmi_resume_handler, -}; - -static struct platform_driver hp_wmi_driver = { - .driver = { - .name = "hp-wmi", - .pm = &hp_wmi_pm_ops, - .dev_groups = hp_wmi_groups, - }, - .remove = __exit_p(hp_wmi_bios_remove), -}; - -static umode_t hp_wmi_hwmon_is_visible(const void *data, - enum hwmon_sensor_types type, - u32 attr, int channel) -{ - switch (type) { - case hwmon_pwm: - return 0644; - case hwmon_fan: - if (hp_wmi_get_fan_speed(channel) >= 0) - return 0444; - break; - default: - return 0; - } - - return 0; -} - -static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long *val) -{ - int ret; - - switch (type) { - case hwmon_fan: - ret = hp_wmi_get_fan_speed(channel); - - if (ret < 0) - return ret; - *val = ret; - return 0; - case hwmon_pwm: - switch (hp_wmi_fan_speed_max_get()) { - case 0: - /* 0 is automatic fan, which is 2 for hwmon */ - *val = 2; - return 0; - case 1: - /* 1 is max fan, which is 0 - * (no fan speed control) for hwmon - */ - *val = 0; - return 0; - default: - /* shouldn't happen */ - return -ENODATA; - } - default: - return -EINVAL; - } -} - -static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long val) -{ - switch (type) { - case hwmon_pwm: - switch (val) { - case 0: - /* 0 is no fan speed control (max), which is 1 for us */ - return hp_wmi_fan_speed_max_set(1); - case 2: - /* 2 is automatic speed control, which is 0 for us */ - return hp_wmi_fan_speed_max_set(0); - default: - /* we don't support manual fan speed control */ - return -EINVAL; - } - default: - return -EOPNOTSUPP; - } -} - -static const struct hwmon_channel_info *info[] = { - HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT), - HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE), - NULL -}; - -static const struct hwmon_ops ops = { - .is_visible = hp_wmi_hwmon_is_visible, - .read = hp_wmi_hwmon_read, - .write = hp_wmi_hwmon_write, -}; - -static const struct hwmon_chip_info chip_info = { - .ops = &ops, - .info = info, -}; - -static int hp_wmi_hwmon_init(void) -{ - struct device *dev = &hp_wmi_platform_dev->dev; - struct device *hwmon; - - hwmon = devm_hwmon_device_register_with_info(dev, "hp", &hp_wmi_driver, - &chip_info, NULL); - - if (IS_ERR(hwmon)) { - dev_err(dev, "Could not register hp hwmon device\n"); - return PTR_ERR(hwmon); - } - - return 0; -} - -static int __init hp_wmi_init(void) -{ - int event_capable = wmi_has_guid(HPWMI_EVENT_GUID); - int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); - int err, tmp = 0; - - if (!bios_capable && !event_capable) - return -ENODEV; - - if (hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, HPWMI_READ, &tmp, - sizeof(tmp), sizeof(tmp)) == HPWMI_RET_INVALID_PARAMETERS) - zero_insize_support = true; - - if (event_capable) { - err = hp_wmi_input_setup(); - if (err) - return err; - } - - if (bios_capable) { - hp_wmi_platform_dev = - platform_device_register_simple("hp-wmi", PLATFORM_DEVID_NONE, NULL, 0); - if (IS_ERR(hp_wmi_platform_dev)) { - err = PTR_ERR(hp_wmi_platform_dev); - goto err_destroy_input; - } - - err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup); - if (err) - goto err_unregister_device; - } - - return 0; - -err_unregister_device: - platform_device_unregister(hp_wmi_platform_dev); -err_destroy_input: - if (event_capable) - hp_wmi_input_destroy(); - - return err; -} -module_init(hp_wmi_init); - -static void __exit hp_wmi_exit(void) -{ - if (wmi_has_guid(HPWMI_EVENT_GUID)) - hp_wmi_input_destroy(); - - if (hp_wmi_platform_dev) { - platform_device_unregister(hp_wmi_platform_dev); - platform_driver_unregister(&hp_wmi_driver); - } -} -module_exit(hp_wmi_exit); diff --git a/drivers/platform/x86/hp/Kconfig b/drivers/platform/x86/hp/Kconfig new file mode 100644 index 000000000000..ae165955311c --- /dev/null +++ b/drivers/platform/x86/hp/Kconfig @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# X86 Platform Specific Drivers +# +menuconfig X86_PLATFORM_DRIVERS_HP + bool "HP X86 Platform Specific Device Drivers" + depends on X86_PLATFORM_DEVICES + help + Say Y here to get to see options for device drivers for various + HP x86 platforms, including vendor-specific laptop extension drivers. + This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and disabled. + +if X86_PLATFORM_DRIVERS_HP + +config HP_ACCEL + tristate "HP laptop accelerometer" + default m + depends on INPUT && ACPI + depends on SERIO_I8042 + select SENSORS_LIS3LV02D + select NEW_LEDS + select LEDS_CLASS + help + This driver provides support for the "Mobile Data Protection System 3D" + or "3D DriveGuard" feature of HP laptops. On such systems the driver + should load automatically (via ACPI alias). + + Support for a led indicating disk protection will be provided as + hp::hddprotect. For more information on the feature, refer to + Documentation/misc-devices/lis3lv02d.rst. + + To compile this driver as a module, choose M here: the module will + be called hp_accel. + +config HP_WMI + tristate "HP WMI extras" + default m + depends on ACPI_WMI + depends on INPUT + depends on RFKILL || RFKILL = n + select INPUT_SPARSEKMAP + select ACPI_PLATFORM_PROFILE + select HWMON + help + Say Y here if you want to support WMI-based hotkeys on HP laptops and + to read data from WMI such as docking or ambient light sensor state. + + To compile this driver as a module, choose M here: the module will + be called hp-wmi. + +config TC1100_WMI + tristate "HP Compaq TC1100 Tablet WMI Extras" + default m + depends on !X86_64 + depends on ACPI + depends on ACPI_WMI + help + This is a driver for the WMI extensions (wireless and bluetooth power + control) of the HP Compaq TC1100 tablet. + +endif # X86_PLATFORM_DRIVERS_HP diff --git a/drivers/platform/x86/hp/Makefile b/drivers/platform/x86/hp/Makefile new file mode 100644 index 000000000000..db1eed4cd7c7 --- /dev/null +++ b/drivers/platform/x86/hp/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for linux/drivers/platform/x86/hp +# HP x86 Platform-Specific Drivers +# + +# Hewlett Packard +obj-$(CONFIG_HP_ACCEL) += hp_accel.o +obj-$(CONFIG_HP_WMI) += hp-wmi.o +obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c new file mode 100644 index 000000000000..0a99058be813 --- /dev/null +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -0,0 +1,1566 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HP WMI hotkeys + * + * Copyright (C) 2008 Red Hat + * Copyright (C) 2010, 2011 Anssi Hannula + * + * Portions based on wistron_btns.c: + * Copyright (C) 2005 Miloslav Trmac + * Copyright (C) 2005 Bernhard Rosenkraenzer + * Copyright (C) 2005 Dmitry Torokhov + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Matthew Garrett "); +MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); +MODULE_LICENSE("GPL"); + +MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C"); +MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); + +#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" +#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4" +#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95 +#define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required + +/* DMI board names of devices that should use the omen specific path for + * thermal profiles. + * This was obtained by taking a look in the windows omen command center + * app and parsing a json file that they use to figure out what capabilities + * the device should have. + * A device is considered an omen if the DisplayName in that list contains + * "OMEN", and it can use the thermal profile stuff if the "Feature" array + * contains "PerformanceControl". + */ +static const char * const omen_thermal_profile_boards[] = { + "84DA", "84DB", "84DC", "8574", "8575", "860A", "87B5", "8572", "8573", + "8600", "8601", "8602", "8605", "8606", "8607", "8746", "8747", "8749", + "874A", "8603", "8604", "8748", "886B", "886C", "878A", "878B", "878C", + "88C8", "88CB", "8786", "8787", "8788", "88D1", "88D2", "88F4", "88FD", + "88F5", "88F6", "88F7", "88FE", "88FF", "8900", "8901", "8902", "8912", + "8917", "8918", "8949", "894A", "89EB" +}; + +/* DMI Board names of Omen laptops that are specifically set to be thermal + * profile version 0 by the Omen Command Center app, regardless of what + * the get system design information WMI call returns + */ +static const char *const omen_thermal_profile_force_v0_boards[] = { + "8607", "8746", "8747", "8749", "874A", "8748" +}; + +enum hp_wmi_radio { + HPWMI_WIFI = 0x0, + HPWMI_BLUETOOTH = 0x1, + HPWMI_WWAN = 0x2, + HPWMI_GPS = 0x3, +}; + +enum hp_wmi_event_ids { + HPWMI_DOCK_EVENT = 0x01, + HPWMI_PARK_HDD = 0x02, + HPWMI_SMART_ADAPTER = 0x03, + HPWMI_BEZEL_BUTTON = 0x04, + HPWMI_WIRELESS = 0x05, + HPWMI_CPU_BATTERY_THROTTLE = 0x06, + HPWMI_LOCK_SWITCH = 0x07, + HPWMI_LID_SWITCH = 0x08, + HPWMI_SCREEN_ROTATION = 0x09, + HPWMI_COOLSENSE_SYSTEM_MOBILE = 0x0A, + HPWMI_COOLSENSE_SYSTEM_HOT = 0x0B, + HPWMI_PROXIMITY_SENSOR = 0x0C, + HPWMI_BACKLIT_KB_BRIGHTNESS = 0x0D, + HPWMI_PEAKSHIFT_PERIOD = 0x0F, + HPWMI_BATTERY_CHARGE_PERIOD = 0x10, + HPWMI_SANITIZATION_MODE = 0x17, + HPWMI_SMART_EXPERIENCE_APP = 0x21, +}; + +/* + * struct bios_args buffer is dynamically allocated. New WMI command types + * were introduced that exceeds 128-byte data size. Changes to handle + * the data size allocation scheme were kept in hp_wmi_perform_qurey function. + */ +struct bios_args { + u32 signature; + u32 command; + u32 commandtype; + u32 datasize; + u8 data[]; +}; + +enum hp_wmi_commandtype { + HPWMI_DISPLAY_QUERY = 0x01, + HPWMI_HDDTEMP_QUERY = 0x02, + HPWMI_ALS_QUERY = 0x03, + HPWMI_HARDWARE_QUERY = 0x04, + HPWMI_WIRELESS_QUERY = 0x05, + HPWMI_BATTERY_QUERY = 0x07, + HPWMI_BIOS_QUERY = 0x09, + HPWMI_FEATURE_QUERY = 0x0b, + HPWMI_HOTKEY_QUERY = 0x0c, + HPWMI_FEATURE2_QUERY = 0x0d, + HPWMI_WIRELESS2_QUERY = 0x1b, + HPWMI_POSTCODEERROR_QUERY = 0x2a, + HPWMI_SYSTEM_DEVICE_MODE = 0x40, + HPWMI_THERMAL_PROFILE_QUERY = 0x4c, +}; + +enum hp_wmi_gm_commandtype { + HPWMI_FAN_SPEED_GET_QUERY = 0x11, + HPWMI_SET_PERFORMANCE_MODE = 0x1A, + HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26, + HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27, + HPWMI_GET_SYSTEM_DESIGN_DATA = 0x28, +}; + +enum hp_wmi_command { + HPWMI_READ = 0x01, + HPWMI_WRITE = 0x02, + HPWMI_ODM = 0x03, + HPWMI_GM = 0x20008, +}; + +enum hp_wmi_hardware_mask { + HPWMI_DOCK_MASK = 0x01, + HPWMI_TABLET_MASK = 0x04, +}; + +struct bios_return { + u32 sigpass; + u32 return_code; +}; + +enum hp_return_value { + HPWMI_RET_WRONG_SIGNATURE = 0x02, + HPWMI_RET_UNKNOWN_COMMAND = 0x03, + HPWMI_RET_UNKNOWN_CMDTYPE = 0x04, + HPWMI_RET_INVALID_PARAMETERS = 0x05, +}; + +enum hp_wireless2_bits { + HPWMI_POWER_STATE = 0x01, + HPWMI_POWER_SOFT = 0x02, + HPWMI_POWER_BIOS = 0x04, + HPWMI_POWER_HARD = 0x08, + HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD, +}; + +enum hp_thermal_profile_omen_v0 { + HP_OMEN_V0_THERMAL_PROFILE_DEFAULT = 0x00, + HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE = 0x01, + HP_OMEN_V0_THERMAL_PROFILE_COOL = 0x02, +}; + +enum hp_thermal_profile_omen_v1 { + HP_OMEN_V1_THERMAL_PROFILE_DEFAULT = 0x30, + HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE = 0x31, + HP_OMEN_V1_THERMAL_PROFILE_COOL = 0x50, +}; + +enum hp_thermal_profile { + HP_THERMAL_PROFILE_PERFORMANCE = 0x00, + HP_THERMAL_PROFILE_DEFAULT = 0x01, + HP_THERMAL_PROFILE_COOL = 0x02, + HP_THERMAL_PROFILE_QUIET = 0x03, +}; + +#define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW) +#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT) + +struct bios_rfkill2_device_state { + u8 radio_type; + u8 bus_type; + u16 vendor_id; + u16 product_id; + u16 subsys_vendor_id; + u16 subsys_product_id; + u8 rfkill_id; + u8 power; + u8 unknown[4]; +}; + +/* 7 devices fit into the 128 byte buffer */ +#define HPWMI_MAX_RFKILL2_DEVICES 7 + +struct bios_rfkill2_state { + u8 unknown[7]; + u8 count; + u8 pad[8]; + struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES]; +}; + +static const struct key_entry hp_wmi_keymap[] = { + { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 0x20e6, { KEY_PROG1 } }, + { KE_KEY, 0x20e8, { KEY_MEDIA } }, + { KE_KEY, 0x2142, { KEY_MEDIA } }, + { KE_KEY, 0x213b, { KEY_INFO } }, + { KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } }, + { KE_KEY, 0x216a, { KEY_SETUP } }, + { KE_KEY, 0x21a9, { KEY_TOUCHPAD_OFF } }, + { KE_KEY, 0x121a9, { KEY_TOUCHPAD_ON } }, + { KE_KEY, 0x231b, { KEY_HELP } }, + { KE_END, 0 } +}; + +static struct input_dev *hp_wmi_input_dev; +static struct platform_device *hp_wmi_platform_dev; +static struct platform_profile_handler platform_profile_handler; +static bool platform_profile_support; +static bool zero_insize_support; + +static struct rfkill *wifi_rfkill; +static struct rfkill *bluetooth_rfkill; +static struct rfkill *wwan_rfkill; + +struct rfkill2_device { + u8 id; + int num; + struct rfkill *rfkill; +}; + +static int rfkill2_count; +static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES]; + +/* + * Chassis Types values were obtained from SMBIOS reference + * specification version 3.00. A complete list of system enclosures + * and chassis types is available on Table 17. + */ +static const char * const tablet_chassis_types[] = { + "30", /* Tablet*/ + "31", /* Convertible */ + "32" /* Detachable */ +}; + +#define DEVICE_MODE_TABLET 0x06 + +/* map output size to the corresponding WMI method id */ +static inline int encode_outsize_for_pvsz(int outsize) +{ + if (outsize > 4096) + return -EINVAL; + if (outsize > 1024) + return 5; + if (outsize > 128) + return 4; + if (outsize > 4) + return 3; + if (outsize > 0) + return 2; + return 1; +} + +/* + * hp_wmi_perform_query + * + * query: The commandtype (enum hp_wmi_commandtype) + * write: The command (enum hp_wmi_command) + * buffer: Buffer used as input and/or output + * insize: Size of input buffer + * outsize: Size of output buffer + * + * returns zero on success + * an HP WMI query specific error code (which is positive) + * -EINVAL if the query was not successful at all + * -EINVAL if the output buffer size exceeds buffersize + * + * Note: The buffersize must at least be the maximum of the input and output + * size. E.g. Battery info query is defined to have 1 byte input + * and 128 byte output. The caller would do: + * buffer = kzalloc(128, GFP_KERNEL); + * ret = hp_wmi_perform_query(HPWMI_BATTERY_QUERY, HPWMI_READ, buffer, 1, 128) + */ +static int hp_wmi_perform_query(int query, enum hp_wmi_command command, + void *buffer, int insize, int outsize) +{ + struct acpi_buffer input, output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct bios_return *bios_return; + union acpi_object *obj = NULL; + struct bios_args *args = NULL; + int mid, actual_insize, actual_outsize; + size_t bios_args_size; + int ret; + + mid = encode_outsize_for_pvsz(outsize); + if (WARN_ON(mid < 0)) + return mid; + + actual_insize = max(insize, 128); + bios_args_size = struct_size(args, data, actual_insize); + args = kmalloc(bios_args_size, GFP_KERNEL); + if (!args) + return -ENOMEM; + + input.length = bios_args_size; + input.pointer = args; + + args->signature = 0x55434553; + args->command = command; + args->commandtype = query; + args->datasize = insize; + memcpy(args->data, buffer, flex_array_size(args, data, insize)); + + ret = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output); + if (ret) + goto out_free; + + obj = output.pointer; + if (!obj) { + ret = -EINVAL; + goto out_free; + } + + if (obj->type != ACPI_TYPE_BUFFER) { + pr_warn("query 0x%x returned an invalid object 0x%x\n", query, ret); + ret = -EINVAL; + goto out_free; + } + + bios_return = (struct bios_return *)obj->buffer.pointer; + ret = bios_return->return_code; + + if (ret) { + if (ret != HPWMI_RET_UNKNOWN_COMMAND && + ret != HPWMI_RET_UNKNOWN_CMDTYPE) + pr_warn("query 0x%x returned error 0x%x\n", query, ret); + goto out_free; + } + + /* Ignore output data of zero size */ + if (!outsize) + goto out_free; + + actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return))); + memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize); + memset(buffer + actual_outsize, 0, outsize - actual_outsize); + +out_free: + kfree(obj); + kfree(args); + return ret; +} + +static int hp_wmi_get_fan_speed(int fan) +{ + u8 fsh, fsl; + char fan_data[4] = { fan, 0, 0, 0 }; + + int ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_GET_QUERY, HPWMI_GM, + &fan_data, sizeof(char), + sizeof(fan_data)); + + if (ret != 0) + return -EINVAL; + + fsh = fan_data[2]; + fsl = fan_data[3]; + + return (fsh << 8) | fsl; +} + +static int hp_wmi_read_int(int query) +{ + int val = 0, ret; + + ret = hp_wmi_perform_query(query, HPWMI_READ, &val, + zero_if_sup(val), sizeof(val)); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return val; +} + +static int hp_wmi_get_dock_state(void) +{ + int state = hp_wmi_read_int(HPWMI_HARDWARE_QUERY); + + if (state < 0) + return state; + + return !!(state & HPWMI_DOCK_MASK); +} + +static int hp_wmi_get_tablet_mode(void) +{ + char system_device_mode[4] = { 0 }; + const char *chassis_type; + bool tablet_found; + int ret; + + chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE); + if (!chassis_type) + return -ENODEV; + + tablet_found = match_string(tablet_chassis_types, + ARRAY_SIZE(tablet_chassis_types), + chassis_type) >= 0; + if (!tablet_found) + return -ENODEV; + + ret = hp_wmi_perform_query(HPWMI_SYSTEM_DEVICE_MODE, HPWMI_READ, + system_device_mode, zero_if_sup(system_device_mode), + sizeof(system_device_mode)); + if (ret < 0) + return ret; + + return system_device_mode[0] == DEVICE_MODE_TABLET; +} + +static int omen_thermal_profile_set(int mode) +{ + char buffer[2] = {0, mode}; + int ret; + + ret = hp_wmi_perform_query(HPWMI_SET_PERFORMANCE_MODE, HPWMI_GM, + &buffer, sizeof(buffer), 0); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return mode; +} + +static bool is_omen_thermal_profile(void) +{ + const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); + + if (!board_name) + return false; + + return match_string(omen_thermal_profile_boards, + ARRAY_SIZE(omen_thermal_profile_boards), + board_name) >= 0; +} + +static int omen_get_thermal_policy_version(void) +{ + unsigned char buffer[8] = { 0 }; + int ret; + + const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); + + if (board_name) { + int matches = match_string(omen_thermal_profile_force_v0_boards, + ARRAY_SIZE(omen_thermal_profile_force_v0_boards), + board_name); + if (matches >= 0) + return 0; + } + + ret = hp_wmi_perform_query(HPWMI_GET_SYSTEM_DESIGN_DATA, HPWMI_GM, + &buffer, sizeof(buffer), sizeof(buffer)); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return buffer[3]; +} + +static int omen_thermal_profile_get(void) +{ + u8 data; + + int ret = ec_read(HP_OMEN_EC_THERMAL_PROFILE_OFFSET, &data); + + if (ret) + return ret; + + return data; +} + +static int hp_wmi_fan_speed_max_set(int enabled) +{ + int ret; + + ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_SET_QUERY, HPWMI_GM, + &enabled, sizeof(enabled), 0); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return enabled; +} + +static int hp_wmi_fan_speed_max_get(void) +{ + int val = 0, ret; + + ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_GET_QUERY, HPWMI_GM, + &val, zero_if_sup(val), sizeof(val)); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return val; +} + +static int __init hp_wmi_bios_2008_later(void) +{ + int state = 0; + int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, HPWMI_READ, &state, + zero_if_sup(state), sizeof(state)); + if (!ret) + return 1; + + return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; +} + +static int __init hp_wmi_bios_2009_later(void) +{ + u8 state[128]; + int ret = hp_wmi_perform_query(HPWMI_FEATURE2_QUERY, HPWMI_READ, &state, + zero_if_sup(state), sizeof(state)); + if (!ret) + return 1; + + return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; +} + +static int __init hp_wmi_enable_hotkeys(void) +{ + int value = 0x6e; + int ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, HPWMI_WRITE, &value, + sizeof(value), 0); + + return ret <= 0 ? ret : -EINVAL; +} + +static int hp_wmi_set_block(void *data, bool blocked) +{ + enum hp_wmi_radio r = (enum hp_wmi_radio) data; + int query = BIT(r + 8) | ((!blocked) << r); + int ret; + + ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, + &query, sizeof(query), 0); + + return ret <= 0 ? ret : -EINVAL; +} + +static const struct rfkill_ops hp_wmi_rfkill_ops = { + .set_block = hp_wmi_set_block, +}; + +static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) +{ + int mask = 0x200 << (r * 8); + + int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); + + /* TBD: Pass error */ + WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY"); + + return !(wireless & mask); +} + +static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) +{ + int mask = 0x800 << (r * 8); + + int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); + + /* TBD: Pass error */ + WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY"); + + return !(wireless & mask); +} + +static int hp_wmi_rfkill2_set_block(void *data, bool blocked) +{ + int rfkill_id = (int)(long)data; + char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked }; + int ret; + + ret = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_WRITE, + buffer, sizeof(buffer), 0); + + return ret <= 0 ? ret : -EINVAL; +} + +static const struct rfkill_ops hp_wmi_rfkill2_ops = { + .set_block = hp_wmi_rfkill2_set_block, +}; + +static int hp_wmi_rfkill2_refresh(void) +{ + struct bios_rfkill2_state state; + int err, i; + + err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, + zero_if_sup(state), sizeof(state)); + if (err) + return err; + + for (i = 0; i < rfkill2_count; i++) { + int num = rfkill2[i].num; + struct bios_rfkill2_device_state *devstate; + + devstate = &state.device[num]; + + if (num >= state.count || + devstate->rfkill_id != rfkill2[i].id) { + pr_warn("power configuration of the wireless devices unexpectedly changed\n"); + continue; + } + + rfkill_set_states(rfkill2[i].rfkill, + IS_SWBLOCKED(devstate->power), + IS_HWBLOCKED(devstate->power)); + } + + return 0; +} + +static ssize_t display_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_read_int(HPWMI_DISPLAY_QUERY); + + if (value < 0) + return value; + return sprintf(buf, "%d\n", value); +} + +static ssize_t hddtemp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_read_int(HPWMI_HDDTEMP_QUERY); + + if (value < 0) + return value; + return sprintf(buf, "%d\n", value); +} + +static ssize_t als_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_read_int(HPWMI_ALS_QUERY); + + if (value < 0) + return value; + return sprintf(buf, "%d\n", value); +} + +static ssize_t dock_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_get_dock_state(); + + if (value < 0) + return value; + return sprintf(buf, "%d\n", value); +} + +static ssize_t tablet_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_get_tablet_mode(); + + if (value < 0) + return value; + return sprintf(buf, "%d\n", value); +} + +static ssize_t postcode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + /* Get the POST error code of previous boot failure. */ + int value = hp_wmi_read_int(HPWMI_POSTCODEERROR_QUERY); + + if (value < 0) + return value; + return sprintf(buf, "0x%x\n", value); +} + +static ssize_t als_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 tmp; + int ret; + + ret = kstrtou32(buf, 10, &tmp); + if (ret) + return ret; + + ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp, + sizeof(tmp), 0); + if (ret) + return ret < 0 ? ret : -EINVAL; + + return count; +} + +static ssize_t postcode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 tmp = 1; + bool clear; + int ret; + + ret = kstrtobool(buf, &clear); + if (ret) + return ret; + + if (clear == false) + return -EINVAL; + + /* Clear the POST error code. It is kept until cleared. */ + ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_WRITE, &tmp, + sizeof(tmp), 0); + if (ret) + return ret < 0 ? ret : -EINVAL; + + return count; +} + +static DEVICE_ATTR_RO(display); +static DEVICE_ATTR_RO(hddtemp); +static DEVICE_ATTR_RW(als); +static DEVICE_ATTR_RO(dock); +static DEVICE_ATTR_RO(tablet); +static DEVICE_ATTR_RW(postcode); + +static struct attribute *hp_wmi_attrs[] = { + &dev_attr_display.attr, + &dev_attr_hddtemp.attr, + &dev_attr_als.attr, + &dev_attr_dock.attr, + &dev_attr_tablet.attr, + &dev_attr_postcode.attr, + NULL, +}; +ATTRIBUTE_GROUPS(hp_wmi); + +static void hp_wmi_notify(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + u32 event_id, event_data; + union acpi_object *obj; + acpi_status status; + u32 *location; + int key_code; + + status = wmi_get_event_data(value, &response); + if (status != AE_OK) { + pr_info("bad event status 0x%x\n", status); + return; + } + + obj = (union acpi_object *)response.pointer; + + if (!obj) + return; + if (obj->type != ACPI_TYPE_BUFFER) { + pr_info("Unknown response received %d\n", obj->type); + kfree(obj); + return; + } + + /* + * Depending on ACPI version the concatenation of id and event data + * inside _WED function will result in a 8 or 16 byte buffer. + */ + location = (u32 *)obj->buffer.pointer; + if (obj->buffer.length == 8) { + event_id = *location; + event_data = *(location + 1); + } else if (obj->buffer.length == 16) { + event_id = *location; + event_data = *(location + 2); + } else { + pr_info("Unknown buffer length %d\n", obj->buffer.length); + kfree(obj); + return; + } + kfree(obj); + + switch (event_id) { + case HPWMI_DOCK_EVENT: + if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit)) + input_report_switch(hp_wmi_input_dev, SW_DOCK, + hp_wmi_get_dock_state()); + if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit)) + input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, + hp_wmi_get_tablet_mode()); + input_sync(hp_wmi_input_dev); + break; + case HPWMI_PARK_HDD: + break; + case HPWMI_SMART_ADAPTER: + break; + case HPWMI_BEZEL_BUTTON: + key_code = hp_wmi_read_int(HPWMI_HOTKEY_QUERY); + if (key_code < 0) + break; + + if (!sparse_keymap_report_event(hp_wmi_input_dev, + key_code, 1, true)) + pr_info("Unknown key code - 0x%x\n", key_code); + break; + case HPWMI_WIRELESS: + if (rfkill2_count) { + hp_wmi_rfkill2_refresh(); + break; + } + + if (wifi_rfkill) + rfkill_set_states(wifi_rfkill, + hp_wmi_get_sw_state(HPWMI_WIFI), + hp_wmi_get_hw_state(HPWMI_WIFI)); + if (bluetooth_rfkill) + rfkill_set_states(bluetooth_rfkill, + hp_wmi_get_sw_state(HPWMI_BLUETOOTH), + hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); + if (wwan_rfkill) + rfkill_set_states(wwan_rfkill, + hp_wmi_get_sw_state(HPWMI_WWAN), + hp_wmi_get_hw_state(HPWMI_WWAN)); + break; + case HPWMI_CPU_BATTERY_THROTTLE: + pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n"); + break; + case HPWMI_LOCK_SWITCH: + break; + case HPWMI_LID_SWITCH: + break; + case HPWMI_SCREEN_ROTATION: + break; + case HPWMI_COOLSENSE_SYSTEM_MOBILE: + break; + case HPWMI_COOLSENSE_SYSTEM_HOT: + break; + case HPWMI_PROXIMITY_SENSOR: + break; + case HPWMI_BACKLIT_KB_BRIGHTNESS: + break; + case HPWMI_PEAKSHIFT_PERIOD: + break; + case HPWMI_BATTERY_CHARGE_PERIOD: + break; + case HPWMI_SANITIZATION_MODE: + break; + case HPWMI_SMART_EXPERIENCE_APP: + break; + default: + pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data); + break; + } +} + +static int __init hp_wmi_input_setup(void) +{ + acpi_status status; + int err, val; + + hp_wmi_input_dev = input_allocate_device(); + if (!hp_wmi_input_dev) + return -ENOMEM; + + hp_wmi_input_dev->name = "HP WMI hotkeys"; + hp_wmi_input_dev->phys = "wmi/input0"; + hp_wmi_input_dev->id.bustype = BUS_HOST; + + __set_bit(EV_SW, hp_wmi_input_dev->evbit); + + /* Dock */ + val = hp_wmi_get_dock_state(); + if (!(val < 0)) { + __set_bit(SW_DOCK, hp_wmi_input_dev->swbit); + input_report_switch(hp_wmi_input_dev, SW_DOCK, val); + } + + /* Tablet mode */ + val = hp_wmi_get_tablet_mode(); + if (!(val < 0)) { + __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); + input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, val); + } + + err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL); + if (err) + goto err_free_dev; + + /* Set initial hardware state */ + input_sync(hp_wmi_input_dev); + + if (!hp_wmi_bios_2009_later() && hp_wmi_bios_2008_later()) + hp_wmi_enable_hotkeys(); + + status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); + if (ACPI_FAILURE(status)) { + err = -EIO; + goto err_free_dev; + } + + err = input_register_device(hp_wmi_input_dev); + if (err) + goto err_uninstall_notifier; + + return 0; + + err_uninstall_notifier: + wmi_remove_notify_handler(HPWMI_EVENT_GUID); + err_free_dev: + input_free_device(hp_wmi_input_dev); + return err; +} + +static void hp_wmi_input_destroy(void) +{ + wmi_remove_notify_handler(HPWMI_EVENT_GUID); + input_unregister_device(hp_wmi_input_dev); +} + +static int __init hp_wmi_rfkill_setup(struct platform_device *device) +{ + int err, wireless; + + wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); + if (wireless < 0) + return wireless; + + err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, &wireless, + sizeof(wireless), 0); + if (err) + return err; + + if (wireless & 0x1) { + wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, + RFKILL_TYPE_WLAN, + &hp_wmi_rfkill_ops, + (void *) HPWMI_WIFI); + if (!wifi_rfkill) + return -ENOMEM; + rfkill_init_sw_state(wifi_rfkill, + hp_wmi_get_sw_state(HPWMI_WIFI)); + rfkill_set_hw_state(wifi_rfkill, + hp_wmi_get_hw_state(HPWMI_WIFI)); + err = rfkill_register(wifi_rfkill); + if (err) + goto register_wifi_error; + } + + if (wireless & 0x2) { + bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev, + RFKILL_TYPE_BLUETOOTH, + &hp_wmi_rfkill_ops, + (void *) HPWMI_BLUETOOTH); + if (!bluetooth_rfkill) { + err = -ENOMEM; + goto register_bluetooth_error; + } + rfkill_init_sw_state(bluetooth_rfkill, + hp_wmi_get_sw_state(HPWMI_BLUETOOTH)); + rfkill_set_hw_state(bluetooth_rfkill, + hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); + err = rfkill_register(bluetooth_rfkill); + if (err) + goto register_bluetooth_error; + } + + if (wireless & 0x4) { + wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev, + RFKILL_TYPE_WWAN, + &hp_wmi_rfkill_ops, + (void *) HPWMI_WWAN); + if (!wwan_rfkill) { + err = -ENOMEM; + goto register_wwan_error; + } + rfkill_init_sw_state(wwan_rfkill, + hp_wmi_get_sw_state(HPWMI_WWAN)); + rfkill_set_hw_state(wwan_rfkill, + hp_wmi_get_hw_state(HPWMI_WWAN)); + err = rfkill_register(wwan_rfkill); + if (err) + goto register_wwan_error; + } + + return 0; + +register_wwan_error: + rfkill_destroy(wwan_rfkill); + wwan_rfkill = NULL; + if (bluetooth_rfkill) + rfkill_unregister(bluetooth_rfkill); +register_bluetooth_error: + rfkill_destroy(bluetooth_rfkill); + bluetooth_rfkill = NULL; + if (wifi_rfkill) + rfkill_unregister(wifi_rfkill); +register_wifi_error: + rfkill_destroy(wifi_rfkill); + wifi_rfkill = NULL; + return err; +} + +static int __init hp_wmi_rfkill2_setup(struct platform_device *device) +{ + struct bios_rfkill2_state state; + int err, i; + + err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, + zero_if_sup(state), sizeof(state)); + if (err) + return err < 0 ? err : -EINVAL; + + if (state.count > HPWMI_MAX_RFKILL2_DEVICES) { + pr_warn("unable to parse 0x1b query output\n"); + return -EINVAL; + } + + for (i = 0; i < state.count; i++) { + struct rfkill *rfkill; + enum rfkill_type type; + char *name; + + switch (state.device[i].radio_type) { + case HPWMI_WIFI: + type = RFKILL_TYPE_WLAN; + name = "hp-wifi"; + break; + case HPWMI_BLUETOOTH: + type = RFKILL_TYPE_BLUETOOTH; + name = "hp-bluetooth"; + break; + case HPWMI_WWAN: + type = RFKILL_TYPE_WWAN; + name = "hp-wwan"; + break; + case HPWMI_GPS: + type = RFKILL_TYPE_GPS; + name = "hp-gps"; + break; + default: + pr_warn("unknown device type 0x%x\n", + state.device[i].radio_type); + continue; + } + + if (!state.device[i].vendor_id) { + pr_warn("zero device %d while %d reported\n", + i, state.count); + continue; + } + + rfkill = rfkill_alloc(name, &device->dev, type, + &hp_wmi_rfkill2_ops, (void *)(long)i); + if (!rfkill) { + err = -ENOMEM; + goto fail; + } + + rfkill2[rfkill2_count].id = state.device[i].rfkill_id; + rfkill2[rfkill2_count].num = i; + rfkill2[rfkill2_count].rfkill = rfkill; + + rfkill_init_sw_state(rfkill, + IS_SWBLOCKED(state.device[i].power)); + rfkill_set_hw_state(rfkill, + IS_HWBLOCKED(state.device[i].power)); + + if (!(state.device[i].power & HPWMI_POWER_BIOS)) + pr_info("device %s blocked by BIOS\n", name); + + err = rfkill_register(rfkill); + if (err) { + rfkill_destroy(rfkill); + goto fail; + } + + rfkill2_count++; + } + + return 0; +fail: + for (; rfkill2_count > 0; rfkill2_count--) { + rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill); + rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill); + } + return err; +} + +static int platform_profile_omen_get(struct platform_profile_handler *pprof, + enum platform_profile_option *profile) +{ + int tp; + + tp = omen_thermal_profile_get(); + if (tp < 0) + return tp; + + switch (tp) { + case HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE: + case HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE: + *profile = PLATFORM_PROFILE_PERFORMANCE; + break; + case HP_OMEN_V0_THERMAL_PROFILE_DEFAULT: + case HP_OMEN_V1_THERMAL_PROFILE_DEFAULT: + *profile = PLATFORM_PROFILE_BALANCED; + break; + case HP_OMEN_V0_THERMAL_PROFILE_COOL: + case HP_OMEN_V1_THERMAL_PROFILE_COOL: + *profile = PLATFORM_PROFILE_COOL; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int platform_profile_omen_set(struct platform_profile_handler *pprof, + enum platform_profile_option profile) +{ + int err, tp, tp_version; + + tp_version = omen_get_thermal_policy_version(); + + if (tp_version < 0 || tp_version > 1) + return -EOPNOTSUPP; + + switch (profile) { + case PLATFORM_PROFILE_PERFORMANCE: + if (tp_version == 0) + tp = HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE; + else + tp = HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE; + break; + case PLATFORM_PROFILE_BALANCED: + if (tp_version == 0) + tp = HP_OMEN_V0_THERMAL_PROFILE_DEFAULT; + else + tp = HP_OMEN_V1_THERMAL_PROFILE_DEFAULT; + break; + case PLATFORM_PROFILE_COOL: + if (tp_version == 0) + tp = HP_OMEN_V0_THERMAL_PROFILE_COOL; + else + tp = HP_OMEN_V1_THERMAL_PROFILE_COOL; + break; + default: + return -EOPNOTSUPP; + } + + err = omen_thermal_profile_set(tp); + if (err < 0) + return err; + + return 0; +} + +static int thermal_profile_get(void) +{ + return hp_wmi_read_int(HPWMI_THERMAL_PROFILE_QUERY); +} + +static int thermal_profile_set(int thermal_profile) +{ + return hp_wmi_perform_query(HPWMI_THERMAL_PROFILE_QUERY, HPWMI_WRITE, &thermal_profile, + sizeof(thermal_profile), 0); +} + +static int hp_wmi_platform_profile_get(struct platform_profile_handler *pprof, + enum platform_profile_option *profile) +{ + int tp; + + tp = thermal_profile_get(); + if (tp < 0) + return tp; + + switch (tp) { + case HP_THERMAL_PROFILE_PERFORMANCE: + *profile = PLATFORM_PROFILE_PERFORMANCE; + break; + case HP_THERMAL_PROFILE_DEFAULT: + *profile = PLATFORM_PROFILE_BALANCED; + break; + case HP_THERMAL_PROFILE_COOL: + *profile = PLATFORM_PROFILE_COOL; + break; + case HP_THERMAL_PROFILE_QUIET: + *profile = PLATFORM_PROFILE_QUIET; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int hp_wmi_platform_profile_set(struct platform_profile_handler *pprof, + enum platform_profile_option profile) +{ + int err, tp; + + switch (profile) { + case PLATFORM_PROFILE_PERFORMANCE: + tp = HP_THERMAL_PROFILE_PERFORMANCE; + break; + case PLATFORM_PROFILE_BALANCED: + tp = HP_THERMAL_PROFILE_DEFAULT; + break; + case PLATFORM_PROFILE_COOL: + tp = HP_THERMAL_PROFILE_COOL; + break; + case PLATFORM_PROFILE_QUIET: + tp = HP_THERMAL_PROFILE_QUIET; + break; + default: + return -EOPNOTSUPP; + } + + err = thermal_profile_set(tp); + if (err) + return err; + + return 0; +} + +static int thermal_profile_setup(void) +{ + int err, tp; + + if (is_omen_thermal_profile()) { + tp = omen_thermal_profile_get(); + if (tp < 0) + return tp; + + /* + * call thermal profile write command to ensure that the + * firmware correctly sets the OEM variables + */ + + err = omen_thermal_profile_set(tp); + if (err < 0) + return err; + + platform_profile_handler.profile_get = platform_profile_omen_get; + platform_profile_handler.profile_set = platform_profile_omen_set; + } else { + tp = thermal_profile_get(); + + if (tp < 0) + return tp; + + /* + * call thermal profile write command to ensure that the + * firmware correctly sets the OEM variables for the DPTF + */ + err = thermal_profile_set(tp); + if (err) + return err; + + platform_profile_handler.profile_get = hp_wmi_platform_profile_get; + platform_profile_handler.profile_set = hp_wmi_platform_profile_set; + + set_bit(PLATFORM_PROFILE_QUIET, platform_profile_handler.choices); + } + + set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices); + set_bit(PLATFORM_PROFILE_BALANCED, platform_profile_handler.choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, platform_profile_handler.choices); + + err = platform_profile_register(&platform_profile_handler); + if (err) + return err; + + platform_profile_support = true; + + return 0; +} + +static int hp_wmi_hwmon_init(void); + +static int __init hp_wmi_bios_setup(struct platform_device *device) +{ + int err; + /* clear detected rfkill devices */ + wifi_rfkill = NULL; + bluetooth_rfkill = NULL; + wwan_rfkill = NULL; + rfkill2_count = 0; + + /* + * In pre-2009 BIOS, command 1Bh return 0x4 to indicate that + * BIOS no longer controls the power for the wireless + * devices. All features supported by this command will no + * longer be supported. + */ + if (!hp_wmi_bios_2009_later()) { + if (hp_wmi_rfkill_setup(device)) + hp_wmi_rfkill2_setup(device); + } + + err = hp_wmi_hwmon_init(); + + if (err < 0) + return err; + + thermal_profile_setup(); + + return 0; +} + +static int __exit hp_wmi_bios_remove(struct platform_device *device) +{ + int i; + + for (i = 0; i < rfkill2_count; i++) { + rfkill_unregister(rfkill2[i].rfkill); + rfkill_destroy(rfkill2[i].rfkill); + } + + if (wifi_rfkill) { + rfkill_unregister(wifi_rfkill); + rfkill_destroy(wifi_rfkill); + } + if (bluetooth_rfkill) { + rfkill_unregister(bluetooth_rfkill); + rfkill_destroy(bluetooth_rfkill); + } + if (wwan_rfkill) { + rfkill_unregister(wwan_rfkill); + rfkill_destroy(wwan_rfkill); + } + + if (platform_profile_support) + platform_profile_remove(); + + return 0; +} + +static int hp_wmi_resume_handler(struct device *device) +{ + /* + * Hardware state may have changed while suspended, so trigger + * input events for the current state. As this is a switch, + * the input layer will only actually pass it on if the state + * changed. + */ + if (hp_wmi_input_dev) { + if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit)) + input_report_switch(hp_wmi_input_dev, SW_DOCK, + hp_wmi_get_dock_state()); + if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit)) + input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, + hp_wmi_get_tablet_mode()); + input_sync(hp_wmi_input_dev); + } + + if (rfkill2_count) + hp_wmi_rfkill2_refresh(); + + if (wifi_rfkill) + rfkill_set_states(wifi_rfkill, + hp_wmi_get_sw_state(HPWMI_WIFI), + hp_wmi_get_hw_state(HPWMI_WIFI)); + if (bluetooth_rfkill) + rfkill_set_states(bluetooth_rfkill, + hp_wmi_get_sw_state(HPWMI_BLUETOOTH), + hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); + if (wwan_rfkill) + rfkill_set_states(wwan_rfkill, + hp_wmi_get_sw_state(HPWMI_WWAN), + hp_wmi_get_hw_state(HPWMI_WWAN)); + + return 0; +} + +static const struct dev_pm_ops hp_wmi_pm_ops = { + .resume = hp_wmi_resume_handler, + .restore = hp_wmi_resume_handler, +}; + +static struct platform_driver hp_wmi_driver = { + .driver = { + .name = "hp-wmi", + .pm = &hp_wmi_pm_ops, + .dev_groups = hp_wmi_groups, + }, + .remove = __exit_p(hp_wmi_bios_remove), +}; + +static umode_t hp_wmi_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_pwm: + return 0644; + case hwmon_fan: + if (hp_wmi_get_fan_speed(channel) >= 0) + return 0444; + break; + default: + return 0; + } + + return 0; +} + +static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + int ret; + + switch (type) { + case hwmon_fan: + ret = hp_wmi_get_fan_speed(channel); + + if (ret < 0) + return ret; + *val = ret; + return 0; + case hwmon_pwm: + switch (hp_wmi_fan_speed_max_get()) { + case 0: + /* 0 is automatic fan, which is 2 for hwmon */ + *val = 2; + return 0; + case 1: + /* 1 is max fan, which is 0 + * (no fan speed control) for hwmon + */ + *val = 0; + return 0; + default: + /* shouldn't happen */ + return -ENODATA; + } + default: + return -EINVAL; + } +} + +static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_pwm: + switch (val) { + case 0: + /* 0 is no fan speed control (max), which is 1 for us */ + return hp_wmi_fan_speed_max_set(1); + case 2: + /* 2 is automatic speed control, which is 0 for us */ + return hp_wmi_fan_speed_max_set(0); + default: + /* we don't support manual fan speed control */ + return -EINVAL; + } + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_channel_info *info[] = { + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT), + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE), + NULL +}; + +static const struct hwmon_ops ops = { + .is_visible = hp_wmi_hwmon_is_visible, + .read = hp_wmi_hwmon_read, + .write = hp_wmi_hwmon_write, +}; + +static const struct hwmon_chip_info chip_info = { + .ops = &ops, + .info = info, +}; + +static int hp_wmi_hwmon_init(void) +{ + struct device *dev = &hp_wmi_platform_dev->dev; + struct device *hwmon; + + hwmon = devm_hwmon_device_register_with_info(dev, "hp", &hp_wmi_driver, + &chip_info, NULL); + + if (IS_ERR(hwmon)) { + dev_err(dev, "Could not register hp hwmon device\n"); + return PTR_ERR(hwmon); + } + + return 0; +} + +static int __init hp_wmi_init(void) +{ + int event_capable = wmi_has_guid(HPWMI_EVENT_GUID); + int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); + int err, tmp = 0; + + if (!bios_capable && !event_capable) + return -ENODEV; + + if (hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, HPWMI_READ, &tmp, + sizeof(tmp), sizeof(tmp)) == HPWMI_RET_INVALID_PARAMETERS) + zero_insize_support = true; + + if (event_capable) { + err = hp_wmi_input_setup(); + if (err) + return err; + } + + if (bios_capable) { + hp_wmi_platform_dev = + platform_device_register_simple("hp-wmi", PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(hp_wmi_platform_dev)) { + err = PTR_ERR(hp_wmi_platform_dev); + goto err_destroy_input; + } + + err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup); + if (err) + goto err_unregister_device; + } + + return 0; + +err_unregister_device: + platform_device_unregister(hp_wmi_platform_dev); +err_destroy_input: + if (event_capable) + hp_wmi_input_destroy(); + + return err; +} +module_init(hp_wmi_init); + +static void __exit hp_wmi_exit(void) +{ + if (wmi_has_guid(HPWMI_EVENT_GUID)) + hp_wmi_input_destroy(); + + if (hp_wmi_platform_dev) { + platform_device_unregister(hp_wmi_platform_dev); + platform_driver_unregister(&hp_wmi_driver); + } +} +module_exit(hp_wmi_exit); diff --git a/drivers/platform/x86/hp/hp_accel.c b/drivers/platform/x86/hp/hp_accel.c new file mode 100644 index 000000000000..6477591747cf --- /dev/null +++ b/drivers/platform/x86/hp/hp_accel.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * hp_accel.c - Interface between LIS3LV02DL driver and HP ACPI BIOS + * + * Copyright (C) 2007-2008 Yan Burman + * Copyright (C) 2008 Eric Piel + * Copyright (C) 2008-2009 Pavel Machek + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../misc/lis3lv02d/lis3lv02d.h" + +/* Delayed LEDs infrastructure ------------------------------------ */ + +/* Special LED class that can defer work */ +struct delayed_led_classdev { + struct led_classdev led_classdev; + struct work_struct work; + enum led_brightness new_brightness; + + unsigned int led; /* For driver */ + void (*set_brightness)(struct delayed_led_classdev *data, enum led_brightness value); +}; + +static inline void delayed_set_status_worker(struct work_struct *work) +{ + struct delayed_led_classdev *data = + container_of(work, struct delayed_led_classdev, work); + + data->set_brightness(data, data->new_brightness); +} + +static inline void delayed_sysfs_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct delayed_led_classdev *data = container_of(led_cdev, + struct delayed_led_classdev, led_classdev); + data->new_brightness = brightness; + schedule_work(&data->work); +} + +/* HP-specific accelerometer driver ------------------------------------ */ + +/* e0 25, e0 26, e0 27, e0 28 are scan codes that the accelerometer with acpi id + * HPQ6000 sends through the keyboard bus */ +#define ACCEL_1 0x25 +#define ACCEL_2 0x26 +#define ACCEL_3 0x27 +#define ACCEL_4 0x28 + +/* For automatic insertion of the module */ +static const struct acpi_device_id lis3lv02d_device_ids[] = { + {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ + {"HPQ6000", 0}, /* HP Mobile Data Protection System PNP */ + {"HPQ6007", 0}, /* HP Mobile Data Protection System PNP */ + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); + +/** + * lis3lv02d_acpi_init - initialize the device for ACPI + * @lis3: pointer to the device struct + * + * Returns 0 on success. + */ +static int lis3lv02d_acpi_init(struct lis3lv02d *lis3) +{ + return 0; +} + +/** + * lis3lv02d_acpi_read - ACPI ALRD method: read a register + * @lis3: pointer to the device struct + * @reg: the register to read + * @ret: result of the operation + * + * Returns 0 on success. + */ +static int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret) +{ + struct acpi_device *dev = lis3->bus_priv; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + unsigned long long lret; + acpi_status status; + + arg0.integer.value = reg; + + status = acpi_evaluate_integer(dev->handle, "ALRD", &args, &lret); + if (ACPI_FAILURE(status)) + return -EINVAL; + *ret = lret; + return 0; +} + +/** + * lis3lv02d_acpi_write - ACPI ALWR method: write to a register + * @lis3: pointer to the device struct + * @reg: the register to write to + * @val: the value to write + * + * Returns 0 on success. + */ +static int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val) +{ + struct acpi_device *dev = lis3->bus_priv; + unsigned long long ret; /* Not used when writting */ + union acpi_object in_obj[2]; + struct acpi_object_list args = { 2, in_obj }; + + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = reg; + in_obj[1].type = ACPI_TYPE_INTEGER; + in_obj[1].integer.value = val; + + if (acpi_evaluate_integer(dev->handle, "ALWR", &args, &ret) != AE_OK) + return -EINVAL; + + return 0; +} + +static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) +{ + lis3_dev.ac = *((union axis_conversion *)dmi->driver_data); + pr_info("hardware type %s found\n", dmi->ident); + + return 1; +} + +/* Represents, for each axis seen by userspace, the corresponding hw axis (+1). + * If the value is negative, the opposite of the hw value is used. */ +#define DEFINE_CONV(name, x, y, z) \ + static union axis_conversion lis3lv02d_axis_##name = \ + { .as_array = { x, y, z } } +DEFINE_CONV(normal, 1, 2, 3); +DEFINE_CONV(y_inverted, 1, -2, 3); +DEFINE_CONV(x_inverted, -1, 2, 3); +DEFINE_CONV(x_inverted_usd, -1, 2, -3); +DEFINE_CONV(z_inverted, 1, 2, -3); +DEFINE_CONV(xy_swap, 2, 1, 3); +DEFINE_CONV(xy_rotated_left, -2, 1, 3); +DEFINE_CONV(xy_rotated_left_usd, -2, 1, -3); +DEFINE_CONV(xy_swap_inverted, -2, -1, 3); +DEFINE_CONV(xy_rotated_right, 2, -1, 3); +DEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3); + +#define AXIS_DMI_MATCH(_ident, _name, _axis) { \ + .ident = _ident, \ + .callback = lis3lv02d_dmi_matched, \ + .matches = { \ + DMI_MATCH(DMI_PRODUCT_NAME, _name) \ + }, \ + .driver_data = &lis3lv02d_axis_##_axis \ +} + +#define AXIS_DMI_MATCH2(_ident, _class1, _name1, \ + _class2, _name2, \ + _axis) { \ + .ident = _ident, \ + .callback = lis3lv02d_dmi_matched, \ + .matches = { \ + DMI_MATCH(DMI_##_class1, _name1), \ + DMI_MATCH(DMI_##_class2, _name2), \ + }, \ + .driver_data = &lis3lv02d_axis_##_axis \ +} +static const struct dmi_system_id lis3lv02d_dmi_ids[] = { + /* product names are truncated to match all kinds of a same model */ + AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), + AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), + AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), + AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), + AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), + AXIS_DMI_MATCH("NC2710", "HP Compaq 2710", xy_swap), + AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), + AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), + AXIS_DMI_MATCH("HP2140", "HP 2140", xy_swap_inverted), + AXIS_DMI_MATCH("NC653x", "HP Compaq 653", xy_rotated_left_usd), + AXIS_DMI_MATCH("NC6730b", "HP Compaq 6730b", xy_rotated_left_usd), + AXIS_DMI_MATCH("NC6730s", "HP Compaq 6730s", xy_swap), + AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right), + AXIS_DMI_MATCH("NC6710x", "HP Compaq 6710", xy_swap_yz_inverted), + AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted), + AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right), + AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap), + AXIS_DMI_MATCH("NC854xx", "HP EliteBook 854", y_inverted), + AXIS_DMI_MATCH("NC273xx", "HP EliteBook 273", y_inverted), + /* Intel-based HP Pavilion dv5 */ + AXIS_DMI_MATCH2("HPDV5_I", + PRODUCT_NAME, "HP Pavilion dv5", + BOARD_NAME, "3603", + x_inverted), + /* AMD-based HP Pavilion dv5 */ + AXIS_DMI_MATCH2("HPDV5_A", + PRODUCT_NAME, "HP Pavilion dv5", + BOARD_NAME, "3600", + y_inverted), + AXIS_DMI_MATCH("DV7", "HP Pavilion dv7", x_inverted), + AXIS_DMI_MATCH("HP8710", "HP Compaq 8710", y_inverted), + AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted), + AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left), + AXIS_DMI_MATCH("HPB440G3", "HP ProBook 440 G3", x_inverted_usd), + AXIS_DMI_MATCH("HPB440G4", "HP ProBook 440 G4", x_inverted), + AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left), + AXIS_DMI_MATCH("HPB450G0", "HP ProBook 450 G0", x_inverted), + AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), + AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), + AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), + AXIS_DMI_MATCH("HPB655x", "HP ProBook 655", xy_swap_inverted), + AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), + AXIS_DMI_MATCH("HPB63xx", "HP ProBook 63", xy_swap), + AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap), + AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap), + AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted), + AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted), + AXIS_DMI_MATCH("HPZBook17G5", "HP ZBook 17 G5", x_inverted), + AXIS_DMI_MATCH("HPZBook17", "HP ZBook 17", xy_swap_yz_inverted), + { NULL, } +/* Laptop models without axis info (yet): + * "NC6910" "HP Compaq 6910" + * "NC2400" "HP Compaq nc2400" + * "NX74x0" "HP Compaq nx74" + * "NX6325" "HP Compaq nx6325" + * "NC4400" "HP Compaq nc4400" + */ +}; + +static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value) +{ + struct acpi_device *dev = lis3_dev.bus_priv; + unsigned long long ret; /* Not used when writing */ + union acpi_object in_obj[1]; + struct acpi_object_list args = { 1, in_obj }; + + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = !!value; + + acpi_evaluate_integer(dev->handle, "ALED", &args, &ret); +} + +static struct delayed_led_classdev hpled_led = { + .led_classdev = { + .name = "hp::hddprotect", + .default_trigger = "none", + .brightness_set = delayed_sysfs_set, + .flags = LED_CORE_SUSPENDRESUME, + }, + .set_brightness = hpled_set, +}; + +static bool hp_accel_i8042_filter(unsigned char data, unsigned char str, + struct serio *port) +{ + static bool extended; + + if (str & I8042_STR_AUXDATA) + return false; + + if (data == 0xe0) { + extended = true; + return true; + } else if (unlikely(extended)) { + extended = false; + + switch (data) { + case ACCEL_1: + case ACCEL_2: + case ACCEL_3: + case ACCEL_4: + return true; + default: + serio_interrupt(port, 0xe0, 0); + return false; + } + } + + return false; +} + +static int lis3lv02d_probe(struct platform_device *device) +{ + int ret; + + lis3_dev.bus_priv = ACPI_COMPANION(&device->dev); + lis3_dev.init = lis3lv02d_acpi_init; + lis3_dev.read = lis3lv02d_acpi_read; + lis3_dev.write = lis3lv02d_acpi_write; + + /* obtain IRQ number of our device from ACPI */ + ret = platform_get_irq_optional(device, 0); + if (ret > 0) + lis3_dev.irq = ret; + + /* If possible use a "standard" axes order */ + if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) { + pr_info("Using custom axes %d,%d,%d\n", + lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z); + } else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { + pr_info("laptop model unknown, using default axes configuration\n"); + lis3_dev.ac = lis3lv02d_axis_normal; + } + + /* call the core layer do its init */ + ret = lis3lv02d_init_device(&lis3_dev); + if (ret) + return ret; + + /* filter to remove HPQ6000 accelerometer data + * from keyboard bus stream */ + if (strstr(dev_name(&device->dev), "HPQ6000")) + i8042_install_filter(hp_accel_i8042_filter); + + INIT_WORK(&hpled_led.work, delayed_set_status_worker); + ret = led_classdev_register(NULL, &hpled_led.led_classdev); + if (ret) { + i8042_remove_filter(hp_accel_i8042_filter); + lis3lv02d_joystick_disable(&lis3_dev); + lis3lv02d_poweroff(&lis3_dev); + flush_work(&hpled_led.work); + lis3lv02d_remove_fs(&lis3_dev); + return ret; + } + + return ret; +} + +static int lis3lv02d_remove(struct platform_device *device) +{ + i8042_remove_filter(hp_accel_i8042_filter); + lis3lv02d_joystick_disable(&lis3_dev); + lis3lv02d_poweroff(&lis3_dev); + + led_classdev_unregister(&hpled_led.led_classdev); + flush_work(&hpled_led.work); + + lis3lv02d_remove_fs(&lis3_dev); + return 0; +} + +static int __maybe_unused lis3lv02d_suspend(struct device *dev) +{ + /* make sure the device is off when we suspend */ + lis3lv02d_poweroff(&lis3_dev); + return 0; +} + +static int __maybe_unused lis3lv02d_resume(struct device *dev) +{ + lis3lv02d_poweron(&lis3_dev); + return 0; +} + +static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume); + +/* For the HP MDPS aka 3D Driveguard */ +static struct platform_driver lis3lv02d_driver = { + .probe = lis3lv02d_probe, + .remove = lis3lv02d_remove, + .driver = { + .name = "hp_accel", + .pm = &hp_accel_pm, + .acpi_match_table = lis3lv02d_device_ids, + }, +}; +module_platform_driver(lis3lv02d_driver); + +MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS and support for disk protection LED."); +MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/hp/tc1100-wmi.c b/drivers/platform/x86/hp/tc1100-wmi.c new file mode 100644 index 000000000000..ded26213c420 --- /dev/null +++ b/drivers/platform/x86/hp/tc1100-wmi.c @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HP Compaq TC1100 Tablet WMI Extras Driver + * + * Copyright (C) 2007 Carlos Corbacho + * Copyright (C) 2004 Jamey Hicks + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#define GUID "C364AC71-36DB-495A-8494-B439D472A505" + +#define TC1100_INSTANCE_WIRELESS 1 +#define TC1100_INSTANCE_JOGDIAL 2 + +MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho"); +MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505"); + +static struct platform_device *tc1100_device; + +struct tc1100_data { + u32 wireless; + u32 jogdial; +}; + +#ifdef CONFIG_PM +static struct tc1100_data suspend_data; +#endif + +/* -------------------------------------------------------------------------- + Device Management + -------------------------------------------------------------------------- */ + +static int get_state(u32 *out, u8 instance) +{ + u32 tmp; + acpi_status status; + struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + + if (!out) + return -EINVAL; + + if (instance > 2) + return -ENODEV; + + status = wmi_query_block(GUID, instance, &result); + if (ACPI_FAILURE(status)) + return -ENODEV; + + obj = (union acpi_object *) result.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) { + tmp = obj->integer.value; + } else { + tmp = 0; + } + + if (result.length > 0) + kfree(result.pointer); + + switch (instance) { + case TC1100_INSTANCE_WIRELESS: + *out = (tmp == 3) ? 1 : 0; + return 0; + case TC1100_INSTANCE_JOGDIAL: + *out = (tmp == 1) ? 0 : 1; + return 0; + default: + return -ENODEV; + } +} + +static int set_state(u32 *in, u8 instance) +{ + u32 value; + acpi_status status; + struct acpi_buffer input; + + if (!in) + return -EINVAL; + + if (instance > 2) + return -ENODEV; + + switch (instance) { + case TC1100_INSTANCE_WIRELESS: + value = (*in) ? 1 : 2; + break; + case TC1100_INSTANCE_JOGDIAL: + value = (*in) ? 0 : 1; + break; + default: + return -ENODEV; + } + + input.length = sizeof(u32); + input.pointer = &value; + + status = wmi_set_block(GUID, instance, &input); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return 0; +} + +/* -------------------------------------------------------------------------- + FS Interface (/sys) + -------------------------------------------------------------------------- */ + +/* + * Read/ write bool sysfs macro + */ +#define show_set_bool(value, instance) \ +static ssize_t \ +show_bool_##value(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + u32 result; \ + acpi_status status = get_state(&result, instance); \ + if (ACPI_SUCCESS(status)) \ + return sprintf(buf, "%d\n", result); \ + return sprintf(buf, "Read error\n"); \ +} \ +\ +static ssize_t \ +set_bool_##value(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + u32 tmp = simple_strtoul(buf, NULL, 10); \ + acpi_status status = set_state(&tmp, instance); \ + if (ACPI_FAILURE(status)) \ + return -EINVAL; \ + return count; \ +} \ +static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, \ + show_bool_##value, set_bool_##value); + +show_set_bool(wireless, TC1100_INSTANCE_WIRELESS); +show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL); + +static struct attribute *tc1100_attributes[] = { + &dev_attr_wireless.attr, + &dev_attr_jogdial.attr, + NULL +}; + +static const struct attribute_group tc1100_attribute_group = { + .attrs = tc1100_attributes, +}; + +/* -------------------------------------------------------------------------- + Driver Model + -------------------------------------------------------------------------- */ + +static int __init tc1100_probe(struct platform_device *device) +{ + return sysfs_create_group(&device->dev.kobj, &tc1100_attribute_group); +} + + +static int tc1100_remove(struct platform_device *device) +{ + sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group); + + return 0; +} + +#ifdef CONFIG_PM +static int tc1100_suspend(struct device *dev) +{ + int ret; + + ret = get_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS); + if (ret) + return ret; + + ret = get_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL); + if (ret) + return ret; + + return 0; +} + +static int tc1100_resume(struct device *dev) +{ + int ret; + + ret = set_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS); + if (ret) + return ret; + + ret = set_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL); + if (ret) + return ret; + + return 0; +} + +static const struct dev_pm_ops tc1100_pm_ops = { + .suspend = tc1100_suspend, + .resume = tc1100_resume, + .freeze = tc1100_suspend, + .restore = tc1100_resume, +}; +#endif + +static struct platform_driver tc1100_driver = { + .driver = { + .name = "tc1100-wmi", +#ifdef CONFIG_PM + .pm = &tc1100_pm_ops, +#endif + }, + .remove = tc1100_remove, +}; + +static int __init tc1100_init(void) +{ + int error; + + if (!wmi_has_guid(GUID)) + return -ENODEV; + + tc1100_device = platform_device_alloc("tc1100-wmi", PLATFORM_DEVID_NONE); + if (!tc1100_device) + return -ENOMEM; + + error = platform_device_add(tc1100_device); + if (error) + goto err_device_put; + + error = platform_driver_probe(&tc1100_driver, tc1100_probe); + if (error) + goto err_device_del; + + pr_info("HP Compaq TC1100 Tablet WMI Extras loaded\n"); + return 0; + + err_device_del: + platform_device_del(tc1100_device); + err_device_put: + platform_device_put(tc1100_device); + return error; +} + +static void __exit tc1100_exit(void) +{ + platform_device_unregister(tc1100_device); + platform_driver_unregister(&tc1100_driver); +} + +module_init(tc1100_init); +module_exit(tc1100_exit); diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c deleted file mode 100644 index e9f852f7c27f..000000000000 --- a/drivers/platform/x86/hp_accel.c +++ /dev/null @@ -1,387 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * hp_accel.c - Interface between LIS3LV02DL driver and HP ACPI BIOS - * - * Copyright (C) 2007-2008 Yan Burman - * Copyright (C) 2008 Eric Piel - * Copyright (C) 2008-2009 Pavel Machek - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../../misc/lis3lv02d/lis3lv02d.h" - -/* Delayed LEDs infrastructure ------------------------------------ */ - -/* Special LED class that can defer work */ -struct delayed_led_classdev { - struct led_classdev led_classdev; - struct work_struct work; - enum led_brightness new_brightness; - - unsigned int led; /* For driver */ - void (*set_brightness)(struct delayed_led_classdev *data, enum led_brightness value); -}; - -static inline void delayed_set_status_worker(struct work_struct *work) -{ - struct delayed_led_classdev *data = - container_of(work, struct delayed_led_classdev, work); - - data->set_brightness(data, data->new_brightness); -} - -static inline void delayed_sysfs_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct delayed_led_classdev *data = container_of(led_cdev, - struct delayed_led_classdev, led_classdev); - data->new_brightness = brightness; - schedule_work(&data->work); -} - -/* HP-specific accelerometer driver ------------------------------------ */ - -/* e0 25, e0 26, e0 27, e0 28 are scan codes that the accelerometer with acpi id - * HPQ6000 sends through the keyboard bus */ -#define ACCEL_1 0x25 -#define ACCEL_2 0x26 -#define ACCEL_3 0x27 -#define ACCEL_4 0x28 - -/* For automatic insertion of the module */ -static const struct acpi_device_id lis3lv02d_device_ids[] = { - {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ - {"HPQ6000", 0}, /* HP Mobile Data Protection System PNP */ - {"HPQ6007", 0}, /* HP Mobile Data Protection System PNP */ - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); - -/** - * lis3lv02d_acpi_init - initialize the device for ACPI - * @lis3: pointer to the device struct - * - * Returns 0 on success. - */ -static int lis3lv02d_acpi_init(struct lis3lv02d *lis3) -{ - return 0; -} - -/** - * lis3lv02d_acpi_read - ACPI ALRD method: read a register - * @lis3: pointer to the device struct - * @reg: the register to read - * @ret: result of the operation - * - * Returns 0 on success. - */ -static int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret) -{ - struct acpi_device *dev = lis3->bus_priv; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list args = { 1, &arg0 }; - unsigned long long lret; - acpi_status status; - - arg0.integer.value = reg; - - status = acpi_evaluate_integer(dev->handle, "ALRD", &args, &lret); - if (ACPI_FAILURE(status)) - return -EINVAL; - *ret = lret; - return 0; -} - -/** - * lis3lv02d_acpi_write - ACPI ALWR method: write to a register - * @lis3: pointer to the device struct - * @reg: the register to write to - * @val: the value to write - * - * Returns 0 on success. - */ -static int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val) -{ - struct acpi_device *dev = lis3->bus_priv; - unsigned long long ret; /* Not used when writting */ - union acpi_object in_obj[2]; - struct acpi_object_list args = { 2, in_obj }; - - in_obj[0].type = ACPI_TYPE_INTEGER; - in_obj[0].integer.value = reg; - in_obj[1].type = ACPI_TYPE_INTEGER; - in_obj[1].integer.value = val; - - if (acpi_evaluate_integer(dev->handle, "ALWR", &args, &ret) != AE_OK) - return -EINVAL; - - return 0; -} - -static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) -{ - lis3_dev.ac = *((union axis_conversion *)dmi->driver_data); - pr_info("hardware type %s found\n", dmi->ident); - - return 1; -} - -/* Represents, for each axis seen by userspace, the corresponding hw axis (+1). - * If the value is negative, the opposite of the hw value is used. */ -#define DEFINE_CONV(name, x, y, z) \ - static union axis_conversion lis3lv02d_axis_##name = \ - { .as_array = { x, y, z } } -DEFINE_CONV(normal, 1, 2, 3); -DEFINE_CONV(y_inverted, 1, -2, 3); -DEFINE_CONV(x_inverted, -1, 2, 3); -DEFINE_CONV(x_inverted_usd, -1, 2, -3); -DEFINE_CONV(z_inverted, 1, 2, -3); -DEFINE_CONV(xy_swap, 2, 1, 3); -DEFINE_CONV(xy_rotated_left, -2, 1, 3); -DEFINE_CONV(xy_rotated_left_usd, -2, 1, -3); -DEFINE_CONV(xy_swap_inverted, -2, -1, 3); -DEFINE_CONV(xy_rotated_right, 2, -1, 3); -DEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3); - -#define AXIS_DMI_MATCH(_ident, _name, _axis) { \ - .ident = _ident, \ - .callback = lis3lv02d_dmi_matched, \ - .matches = { \ - DMI_MATCH(DMI_PRODUCT_NAME, _name) \ - }, \ - .driver_data = &lis3lv02d_axis_##_axis \ -} - -#define AXIS_DMI_MATCH2(_ident, _class1, _name1, \ - _class2, _name2, \ - _axis) { \ - .ident = _ident, \ - .callback = lis3lv02d_dmi_matched, \ - .matches = { \ - DMI_MATCH(DMI_##_class1, _name1), \ - DMI_MATCH(DMI_##_class2, _name2), \ - }, \ - .driver_data = &lis3lv02d_axis_##_axis \ -} -static const struct dmi_system_id lis3lv02d_dmi_ids[] = { - /* product names are truncated to match all kinds of a same model */ - AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), - AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), - AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), - AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), - AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), - AXIS_DMI_MATCH("NC2710", "HP Compaq 2710", xy_swap), - AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), - AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), - AXIS_DMI_MATCH("HP2140", "HP 2140", xy_swap_inverted), - AXIS_DMI_MATCH("NC653x", "HP Compaq 653", xy_rotated_left_usd), - AXIS_DMI_MATCH("NC6730b", "HP Compaq 6730b", xy_rotated_left_usd), - AXIS_DMI_MATCH("NC6730s", "HP Compaq 6730s", xy_swap), - AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right), - AXIS_DMI_MATCH("NC6710x", "HP Compaq 6710", xy_swap_yz_inverted), - AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted), - AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right), - AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap), - AXIS_DMI_MATCH("NC854xx", "HP EliteBook 854", y_inverted), - AXIS_DMI_MATCH("NC273xx", "HP EliteBook 273", y_inverted), - /* Intel-based HP Pavilion dv5 */ - AXIS_DMI_MATCH2("HPDV5_I", - PRODUCT_NAME, "HP Pavilion dv5", - BOARD_NAME, "3603", - x_inverted), - /* AMD-based HP Pavilion dv5 */ - AXIS_DMI_MATCH2("HPDV5_A", - PRODUCT_NAME, "HP Pavilion dv5", - BOARD_NAME, "3600", - y_inverted), - AXIS_DMI_MATCH("DV7", "HP Pavilion dv7", x_inverted), - AXIS_DMI_MATCH("HP8710", "HP Compaq 8710", y_inverted), - AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted), - AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left), - AXIS_DMI_MATCH("HPB440G3", "HP ProBook 440 G3", x_inverted_usd), - AXIS_DMI_MATCH("HPB440G4", "HP ProBook 440 G4", x_inverted), - AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left), - AXIS_DMI_MATCH("HPB450G0", "HP ProBook 450 G0", x_inverted), - AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), - AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), - AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), - AXIS_DMI_MATCH("HPB655x", "HP ProBook 655", xy_swap_inverted), - AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), - AXIS_DMI_MATCH("HPB63xx", "HP ProBook 63", xy_swap), - AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap), - AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap), - AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted), - AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted), - AXIS_DMI_MATCH("HPZBook17G5", "HP ZBook 17 G5", x_inverted), - AXIS_DMI_MATCH("HPZBook17", "HP ZBook 17", xy_swap_yz_inverted), - { NULL, } -/* Laptop models without axis info (yet): - * "NC6910" "HP Compaq 6910" - * "NC2400" "HP Compaq nc2400" - * "NX74x0" "HP Compaq nx74" - * "NX6325" "HP Compaq nx6325" - * "NC4400" "HP Compaq nc4400" - */ -}; - -static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value) -{ - struct acpi_device *dev = lis3_dev.bus_priv; - unsigned long long ret; /* Not used when writing */ - union acpi_object in_obj[1]; - struct acpi_object_list args = { 1, in_obj }; - - in_obj[0].type = ACPI_TYPE_INTEGER; - in_obj[0].integer.value = !!value; - - acpi_evaluate_integer(dev->handle, "ALED", &args, &ret); -} - -static struct delayed_led_classdev hpled_led = { - .led_classdev = { - .name = "hp::hddprotect", - .default_trigger = "none", - .brightness_set = delayed_sysfs_set, - .flags = LED_CORE_SUSPENDRESUME, - }, - .set_brightness = hpled_set, -}; - -static bool hp_accel_i8042_filter(unsigned char data, unsigned char str, - struct serio *port) -{ - static bool extended; - - if (str & I8042_STR_AUXDATA) - return false; - - if (data == 0xe0) { - extended = true; - return true; - } else if (unlikely(extended)) { - extended = false; - - switch (data) { - case ACCEL_1: - case ACCEL_2: - case ACCEL_3: - case ACCEL_4: - return true; - default: - serio_interrupt(port, 0xe0, 0); - return false; - } - } - - return false; -} - -static int lis3lv02d_probe(struct platform_device *device) -{ - int ret; - - lis3_dev.bus_priv = ACPI_COMPANION(&device->dev); - lis3_dev.init = lis3lv02d_acpi_init; - lis3_dev.read = lis3lv02d_acpi_read; - lis3_dev.write = lis3lv02d_acpi_write; - - /* obtain IRQ number of our device from ACPI */ - ret = platform_get_irq_optional(device, 0); - if (ret > 0) - lis3_dev.irq = ret; - - /* If possible use a "standard" axes order */ - if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) { - pr_info("Using custom axes %d,%d,%d\n", - lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z); - } else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { - pr_info("laptop model unknown, using default axes configuration\n"); - lis3_dev.ac = lis3lv02d_axis_normal; - } - - /* call the core layer do its init */ - ret = lis3lv02d_init_device(&lis3_dev); - if (ret) - return ret; - - /* filter to remove HPQ6000 accelerometer data - * from keyboard bus stream */ - if (strstr(dev_name(&device->dev), "HPQ6000")) - i8042_install_filter(hp_accel_i8042_filter); - - INIT_WORK(&hpled_led.work, delayed_set_status_worker); - ret = led_classdev_register(NULL, &hpled_led.led_classdev); - if (ret) { - i8042_remove_filter(hp_accel_i8042_filter); - lis3lv02d_joystick_disable(&lis3_dev); - lis3lv02d_poweroff(&lis3_dev); - flush_work(&hpled_led.work); - lis3lv02d_remove_fs(&lis3_dev); - return ret; - } - - return ret; -} - -static int lis3lv02d_remove(struct platform_device *device) -{ - i8042_remove_filter(hp_accel_i8042_filter); - lis3lv02d_joystick_disable(&lis3_dev); - lis3lv02d_poweroff(&lis3_dev); - - led_classdev_unregister(&hpled_led.led_classdev); - flush_work(&hpled_led.work); - - lis3lv02d_remove_fs(&lis3_dev); - return 0; -} - -static int __maybe_unused lis3lv02d_suspend(struct device *dev) -{ - /* make sure the device is off when we suspend */ - lis3lv02d_poweroff(&lis3_dev); - return 0; -} - -static int __maybe_unused lis3lv02d_resume(struct device *dev) -{ - lis3lv02d_poweron(&lis3_dev); - return 0; -} - -static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume); - -/* For the HP MDPS aka 3D Driveguard */ -static struct platform_driver lis3lv02d_driver = { - .probe = lis3lv02d_probe, - .remove = lis3lv02d_remove, - .driver = { - .name = "hp_accel", - .pm = &hp_accel_pm, - .acpi_match_table = lis3lv02d_device_ids, - }, -}; -module_platform_driver(lis3lv02d_driver); - -MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS and support for disk protection LED."); -MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); -MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c deleted file mode 100644 index ded26213c420..000000000000 --- a/drivers/platform/x86/tc1100-wmi.c +++ /dev/null @@ -1,265 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HP Compaq TC1100 Tablet WMI Extras Driver - * - * Copyright (C) 2007 Carlos Corbacho - * Copyright (C) 2004 Jamey Hicks - * Copyright (C) 2001, 2002 Andy Grover - * Copyright (C) 2001, 2002 Paul Diefenbaugh - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include - -#define GUID "C364AC71-36DB-495A-8494-B439D472A505" - -#define TC1100_INSTANCE_WIRELESS 1 -#define TC1100_INSTANCE_JOGDIAL 2 - -MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho"); -MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505"); - -static struct platform_device *tc1100_device; - -struct tc1100_data { - u32 wireless; - u32 jogdial; -}; - -#ifdef CONFIG_PM -static struct tc1100_data suspend_data; -#endif - -/* -------------------------------------------------------------------------- - Device Management - -------------------------------------------------------------------------- */ - -static int get_state(u32 *out, u8 instance) -{ - u32 tmp; - acpi_status status; - struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - - if (!out) - return -EINVAL; - - if (instance > 2) - return -ENODEV; - - status = wmi_query_block(GUID, instance, &result); - if (ACPI_FAILURE(status)) - return -ENODEV; - - obj = (union acpi_object *) result.pointer; - if (obj && obj->type == ACPI_TYPE_INTEGER) { - tmp = obj->integer.value; - } else { - tmp = 0; - } - - if (result.length > 0) - kfree(result.pointer); - - switch (instance) { - case TC1100_INSTANCE_WIRELESS: - *out = (tmp == 3) ? 1 : 0; - return 0; - case TC1100_INSTANCE_JOGDIAL: - *out = (tmp == 1) ? 0 : 1; - return 0; - default: - return -ENODEV; - } -} - -static int set_state(u32 *in, u8 instance) -{ - u32 value; - acpi_status status; - struct acpi_buffer input; - - if (!in) - return -EINVAL; - - if (instance > 2) - return -ENODEV; - - switch (instance) { - case TC1100_INSTANCE_WIRELESS: - value = (*in) ? 1 : 2; - break; - case TC1100_INSTANCE_JOGDIAL: - value = (*in) ? 0 : 1; - break; - default: - return -ENODEV; - } - - input.length = sizeof(u32); - input.pointer = &value; - - status = wmi_set_block(GUID, instance, &input); - if (ACPI_FAILURE(status)) - return -ENODEV; - - return 0; -} - -/* -------------------------------------------------------------------------- - FS Interface (/sys) - -------------------------------------------------------------------------- */ - -/* - * Read/ write bool sysfs macro - */ -#define show_set_bool(value, instance) \ -static ssize_t \ -show_bool_##value(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - u32 result; \ - acpi_status status = get_state(&result, instance); \ - if (ACPI_SUCCESS(status)) \ - return sprintf(buf, "%d\n", result); \ - return sprintf(buf, "Read error\n"); \ -} \ -\ -static ssize_t \ -set_bool_##value(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - u32 tmp = simple_strtoul(buf, NULL, 10); \ - acpi_status status = set_state(&tmp, instance); \ - if (ACPI_FAILURE(status)) \ - return -EINVAL; \ - return count; \ -} \ -static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, \ - show_bool_##value, set_bool_##value); - -show_set_bool(wireless, TC1100_INSTANCE_WIRELESS); -show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL); - -static struct attribute *tc1100_attributes[] = { - &dev_attr_wireless.attr, - &dev_attr_jogdial.attr, - NULL -}; - -static const struct attribute_group tc1100_attribute_group = { - .attrs = tc1100_attributes, -}; - -/* -------------------------------------------------------------------------- - Driver Model - -------------------------------------------------------------------------- */ - -static int __init tc1100_probe(struct platform_device *device) -{ - return sysfs_create_group(&device->dev.kobj, &tc1100_attribute_group); -} - - -static int tc1100_remove(struct platform_device *device) -{ - sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group); - - return 0; -} - -#ifdef CONFIG_PM -static int tc1100_suspend(struct device *dev) -{ - int ret; - - ret = get_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS); - if (ret) - return ret; - - ret = get_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL); - if (ret) - return ret; - - return 0; -} - -static int tc1100_resume(struct device *dev) -{ - int ret; - - ret = set_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS); - if (ret) - return ret; - - ret = set_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL); - if (ret) - return ret; - - return 0; -} - -static const struct dev_pm_ops tc1100_pm_ops = { - .suspend = tc1100_suspend, - .resume = tc1100_resume, - .freeze = tc1100_suspend, - .restore = tc1100_resume, -}; -#endif - -static struct platform_driver tc1100_driver = { - .driver = { - .name = "tc1100-wmi", -#ifdef CONFIG_PM - .pm = &tc1100_pm_ops, -#endif - }, - .remove = tc1100_remove, -}; - -static int __init tc1100_init(void) -{ - int error; - - if (!wmi_has_guid(GUID)) - return -ENODEV; - - tc1100_device = platform_device_alloc("tc1100-wmi", PLATFORM_DEVID_NONE); - if (!tc1100_device) - return -ENOMEM; - - error = platform_device_add(tc1100_device); - if (error) - goto err_device_put; - - error = platform_driver_probe(&tc1100_driver, tc1100_probe); - if (error) - goto err_device_del; - - pr_info("HP Compaq TC1100 Tablet WMI Extras loaded\n"); - return 0; - - err_device_del: - platform_device_del(tc1100_device); - err_device_put: - platform_device_put(tc1100_device); - return error; -} - -static void __exit tc1100_exit(void) -{ - platform_device_unregister(tc1100_device); - platform_driver_unregister(&tc1100_driver); -} - -module_init(tc1100_init); -module_exit(tc1100_exit); -- cgit v1.2.3-58-ga151 From 2621779604c995c4f0e4a08e4c352d524b930fdf Mon Sep 17 00:00:00 2001 From: Barnabás Pőcze Date: Fri, 21 Oct 2022 18:17:28 +0000 Subject: platform/x86: thinkpad_acpi: use strstarts() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a function, `strstarts()`, in linux/string.h to check if a string is prefix of another. So remove the custom version from the driver. Signed-off-by: Barnabás Pőcze Link: https://lore.kernel.org/r/20221021180007.55535-1-pobrn@protonmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/thinkpad_acpi.c | 58 +++++++++++++++++------------------- 1 file changed, 27 insertions(+), 31 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index cfeed1c45b54..1195293b22fd 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -265,9 +265,6 @@ enum tpacpi_hkey_event_t { #define FAN_NOT_PRESENT 65535 -#define strlencmp(a, b) (strncmp((a), (b), strlen(b))) - - /**************************************************************************** * Driver-wide structs and misc. variables */ @@ -1335,9 +1332,9 @@ static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf) return -ENODEV; while ((cmd = strsep(&buf, ","))) { - if (strlencmp(cmd, "enable") == 0) + if (strstarts(cmd, "enable")) status = TPACPI_RFK_RADIO_ON; - else if (strlencmp(cmd, "disable") == 0) + else if (strstarts(cmd, "disable")) status = TPACPI_RFK_RADIO_OFF; else return -EINVAL; @@ -4198,12 +4195,12 @@ static int hotkey_write(char *buf) res = 0; while ((cmd = strsep(&buf, ","))) { - if (strlencmp(cmd, "enable") == 0) { + if (strstarts(cmd, "enable")) { hotkey_enabledisable_warn(1); - } else if (strlencmp(cmd, "disable") == 0) { + } else if (strstarts(cmd, "disable")) { hotkey_enabledisable_warn(0); res = -EPERM; - } else if (strlencmp(cmd, "reset") == 0) { + } else if (strstarts(cmd, "reset")) { mask = (hotkey_all_mask | hotkey_source_mask) & ~hotkey_reserved_mask; } else if (sscanf(cmd, "0x%x", &mask) == 1) { @@ -5233,33 +5230,33 @@ static int video_write(char *buf) disable = 0; while ((cmd = strsep(&buf, ","))) { - if (strlencmp(cmd, "lcd_enable") == 0) { + if (strstarts(cmd, "lcd_enable")) { enable |= TP_ACPI_VIDEO_S_LCD; - } else if (strlencmp(cmd, "lcd_disable") == 0) { + } else if (strstarts(cmd, "lcd_disable")) { disable |= TP_ACPI_VIDEO_S_LCD; - } else if (strlencmp(cmd, "crt_enable") == 0) { + } else if (strstarts(cmd, "crt_enable")) { enable |= TP_ACPI_VIDEO_S_CRT; - } else if (strlencmp(cmd, "crt_disable") == 0) { + } else if (strstarts(cmd, "crt_disable")) { disable |= TP_ACPI_VIDEO_S_CRT; } else if (video_supported == TPACPI_VIDEO_NEW && - strlencmp(cmd, "dvi_enable") == 0) { + strstarts(cmd, "dvi_enable")) { enable |= TP_ACPI_VIDEO_S_DVI; } else if (video_supported == TPACPI_VIDEO_NEW && - strlencmp(cmd, "dvi_disable") == 0) { + strstarts(cmd, "dvi_disable")) { disable |= TP_ACPI_VIDEO_S_DVI; - } else if (strlencmp(cmd, "auto_enable") == 0) { + } else if (strstarts(cmd, "auto_enable")) { res = video_autosw_set(1); if (res) return res; - } else if (strlencmp(cmd, "auto_disable") == 0) { + } else if (strstarts(cmd, "auto_disable")) { res = video_autosw_set(0); if (res) return res; - } else if (strlencmp(cmd, "video_switch") == 0) { + } else if (strstarts(cmd, "video_switch")) { res = video_outputsw_cycle(); if (res) return res; - } else if (strlencmp(cmd, "expand_toggle") == 0) { + } else if (strstarts(cmd, "expand_toggle")) { res = video_expand_toggle(); if (res) return res; @@ -5653,9 +5650,9 @@ static int light_write(char *buf) return -ENODEV; while ((cmd = strsep(&buf, ","))) { - if (strlencmp(cmd, "on") == 0) { + if (strstarts(cmd, "on")) { newstatus = 1; - } else if (strlencmp(cmd, "off") == 0) { + } else if (strstarts(cmd, "off")) { newstatus = 0; } else return -EINVAL; @@ -7126,10 +7123,10 @@ static int brightness_write(char *buf) return level; while ((cmd = strsep(&buf, ","))) { - if (strlencmp(cmd, "up") == 0) { + if (strstarts(cmd, "up")) { if (level < bright_maxlvl) level++; - } else if (strlencmp(cmd, "down") == 0) { + } else if (strstarts(cmd, "down")) { if (level > 0) level--; } else if (sscanf(cmd, "level %d", &level) == 1 && @@ -7878,13 +7875,13 @@ static int volume_write(char *buf) while ((cmd = strsep(&buf, ","))) { if (!tp_features.mixer_no_level_control) { - if (strlencmp(cmd, "up") == 0) { + if (strstarts(cmd, "up")) { if (new_mute) new_mute = 0; else if (new_level < TP_EC_VOLUME_MAX) new_level++; continue; - } else if (strlencmp(cmd, "down") == 0) { + } else if (strstarts(cmd, "down")) { if (new_mute) new_mute = 0; else if (new_level > 0) @@ -7896,9 +7893,9 @@ static int volume_write(char *buf) continue; } } - if (strlencmp(cmd, "mute") == 0) + if (strstarts(cmd, "mute")) new_mute = TP_EC_AUDIO_MUTESW_MSK; - else if (strlencmp(cmd, "unmute") == 0) + else if (strstarts(cmd, "unmute")) new_mute = 0; else return -EINVAL; @@ -9121,10 +9118,9 @@ static int fan_write_cmd_level(const char *cmd, int *rc) { int level; - if (strlencmp(cmd, "level auto") == 0) + if (strstarts(cmd, "level auto")) level = TP_EC_FAN_AUTO; - else if ((strlencmp(cmd, "level disengaged") == 0) || - (strlencmp(cmd, "level full-speed") == 0)) + else if (strstarts(cmd, "level disengaged") || strstarts(cmd, "level full-speed")) level = TP_EC_FAN_FULLSPEED; else if (sscanf(cmd, "level %d", &level) != 1) return 0; @@ -9142,7 +9138,7 @@ static int fan_write_cmd_level(const char *cmd, int *rc) static int fan_write_cmd_enable(const char *cmd, int *rc) { - if (strlencmp(cmd, "enable") != 0) + if (!strstarts(cmd, "enable")) return 0; *rc = fan_set_enable(); @@ -9157,7 +9153,7 @@ static int fan_write_cmd_enable(const char *cmd, int *rc) static int fan_write_cmd_disable(const char *cmd, int *rc) { - if (strlencmp(cmd, "disable") != 0) + if (!strstarts(cmd, "disable")) return 0; *rc = fan_set_disable(); -- cgit v1.2.3-58-ga151 From 93b962ddd69a11c956e40b26e494132c514608a9 Mon Sep 17 00:00:00 2001 From: chen zhang Date: Thu, 3 Nov 2022 09:33:13 +0800 Subject: platform/x86: ISST: Fix typo in comments Fix spelling typo in comments. Reported-by: k2ci Signed-off-by: chen zhang Acked-by: Randy Dunlap Link: https://lore.kernel.org/r/20221103013313.13278-1-chenzhang@kylinos.cn Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/speed_select_if/isst_if_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c index fd102678c75f..a7e02b24a87a 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c @@ -623,7 +623,7 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd, /* Lock to prevent module registration when already opened by user space */ static DEFINE_MUTEX(punit_misc_dev_open_lock); -/* Lock to allow one share misc device for all ISST interace */ +/* Lock to allow one shared misc device for all ISST interfaces */ static DEFINE_MUTEX(punit_misc_dev_reg_lock); static int misc_usage_count; static int misc_device_ret; -- cgit v1.2.3-58-ga151 From 84fa20c2a56a9d941fef68dd2c1d489a5f24daa7 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 2 Nov 2022 22:23:35 +0100 Subject: platform/x86: dell-ddv: Improve buffer handling When the DDV interface returns a buffer, it actually returns a acpi buffer containing an integer (buffer size) and another acpi buffer (buffer content). The size of the buffer may be smaller than the size of the buffer content, which is perfectly valid and should not be treated as an error. Also use the buffer size instead of the buffer content size when accessing the buffer to prevent accessing bogus data. Tested on a Dell Inspiron 3505. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20221102212336.380257-1-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/dell/dell-wmi-ddv.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c index 699feae3c435..1a001296e8c6 100644 --- a/drivers/platform/x86/dell/dell-wmi-ddv.c +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c @@ -129,9 +129,9 @@ static int dell_wmi_ddv_query_buffer(struct wmi_device *wdev, enum dell_ddv_meth if (obj->package.elements[1].type != ACPI_TYPE_BUFFER) goto err_free; - if (buffer_size != obj->package.elements[1].buffer.length) { + if (buffer_size > obj->package.elements[1].buffer.length) { dev_warn(&wdev->dev, - FW_WARN "ACPI buffer size (%llu) does not match WMI buffer size (%d)\n", + FW_WARN "WMI buffer size (%llu) exceeds ACPI buffer size (%d)\n", buffer_size, obj->package.elements[1].buffer.length); goto err_free; @@ -271,15 +271,17 @@ static int dell_wmi_ddv_buffer_read(struct seq_file *seq, enum dell_ddv_method m struct device *dev = seq->private; struct dell_wmi_ddv_data *data = dev_get_drvdata(dev); union acpi_object *obj; - union acpi_object buf; + u64 size; + u8 *buf; int ret; ret = dell_wmi_ddv_query_buffer(data->wdev, method, 0, &obj); if (ret < 0) return ret; - buf = obj->package.elements[1]; - ret = seq_write(seq, buf.buffer.pointer, buf.buffer.length); + size = obj->package.elements[0].integer.value; + buf = obj->package.elements[1].buffer.pointer; + ret = seq_write(seq, buf, size); kfree(obj); return ret; -- cgit v1.2.3-58-ga151 From c7891884a55effcf3e54e25835ef9fac39ef48f3 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 2 Nov 2022 22:23:36 +0100 Subject: platform/x86: dell-ddv: Warn if ePPID has a suspicious length On some systems (like the Dell Inspiron 3505), the acpi operation region holding the ePPID string is two bytes too short, causing acpi functions like ToString() to omit the last two bytes. This does not happen on Windows, supposedly due to their implementation of ToString() ignoring buffer boundaries. Inform users if the ePPID length differs from the Dell specification so they can complain to Dell to fix their BIOS. Tested on a Dell Inspiron 3505. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20221102212336.380257-2-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/dell/dell-wmi-ddv.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c index 1a001296e8c6..2bb449845d14 100644 --- a/drivers/platform/x86/dell/dell-wmi-ddv.c +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c @@ -10,12 +10,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -27,6 +29,9 @@ #define DELL_DDV_SUPPORTED_INTERFACE 2 #define DELL_DDV_GUID "8A42EA14-4F2A-FD45-6422-0087F7A7E608" +#define DELL_EPPID_LENGTH 20 +#define DELL_EPPID_EXT_LENGTH 23 + enum dell_ddv_method { DELL_DDV_BATTERY_DESIGN_CAPACITY = 0x01, DELL_DDV_BATTERY_FULL_CHARGE_CAPACITY = 0x02, @@ -196,6 +201,10 @@ static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, cha if (ret < 0) return ret; + if (obj->string.length != DELL_EPPID_LENGTH && obj->string.length != DELL_EPPID_EXT_LENGTH) + dev_info_once(&data->wdev->dev, FW_INFO "Suspicious ePPID length (%d)\n", + obj->string.length); + ret = sysfs_emit(buf, "%s\n", obj->string.pointer); kfree(obj); -- cgit v1.2.3-58-ga151 From 5831882880e9a1749553e78f9d8369fe33116aaf Mon Sep 17 00:00:00 2001 From: Eray Orçunus Date: Sat, 29 Oct 2022 15:03:06 +0300 Subject: platform/x86: ideapad-laptop: Revert "check for touchpad support in _CFG" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Last 8 bit of _CFG started being used in later IdeaPads, thus 30th bit doesn't always show whether device supports touchpad or touchpad switch. Remove checking bit 30 of _CFG, so older IdeaPads like S10-3 can switch touchpad again via touchpad attribute. This reverts commit b3ed1b7fe378 ("platform/x86: ideapad-laptop: check for touchpad support in _CFG"). Signed-off-by: Eray Orçunus Acked-by: Ike Panhc Link: https://lore.kernel.org/r/20221029120311.11152-2-erayorcunus@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/ideapad-laptop.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 3ea8fc6a9ca3..7192e0d2a14f 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -46,11 +46,10 @@ static const char *const ideapad_wmi_fnesc_events[] = { #endif enum { - CFG_CAP_BT_BIT = 16, - CFG_CAP_3G_BIT = 17, - CFG_CAP_WIFI_BIT = 18, - CFG_CAP_CAM_BIT = 19, - CFG_CAP_TOUCHPAD_BIT = 30, + CFG_CAP_BT_BIT = 16, + CFG_CAP_3G_BIT = 17, + CFG_CAP_WIFI_BIT = 18, + CFG_CAP_CAM_BIT = 19, }; enum { @@ -386,8 +385,6 @@ static int debugfs_cfg_show(struct seq_file *s, void *data) seq_puts(s, " wifi"); if (test_bit(CFG_CAP_CAM_BIT, &priv->cfg)) seq_puts(s, " camera"); - if (test_bit(CFG_CAP_TOUCHPAD_BIT, &priv->cfg)) - seq_puts(s, " touchpad"); seq_puts(s, "\n"); seq_puts(s, "Graphics: "); @@ -680,8 +677,7 @@ static umode_t ideapad_is_visible(struct kobject *kobj, else if (attr == &dev_attr_fn_lock.attr) supported = priv->features.fn_lock; else if (attr == &dev_attr_touchpad.attr) - supported = priv->features.touchpad_ctrl_via_ec && - test_bit(CFG_CAP_TOUCHPAD_BIT, &priv->cfg); + supported = priv->features.touchpad_ctrl_via_ec; else if (attr == &dev_attr_usb_charging.attr) supported = priv->features.usb_charging; -- cgit v1.2.3-58-ga151 From be5dd7d8359de9fb22115a63f09981cdf689db4f Mon Sep 17 00:00:00 2001 From: Eray Orçunus Date: Sat, 29 Oct 2022 15:03:09 +0300 Subject: platform/x86: ideapad-laptop: Add new _CFG bit numbers for future use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Later IdeaPads report various things in last 8 bits of _CFG, at least 5 of them represent supported on-screen-displays. Add those bit numbers to the enum, and use CFG_OSD_ as prefix of their names. Also expose the values of these bits to debugfs, since they can be useful. Signed-off-by: Eray Orçunus Acked-by: Ike Panhc Link: https://lore.kernel.org/r/20221029120311.11152-5-erayorcunus@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/ideapad-laptop.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 7192e0d2a14f..125b4534424f 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -46,10 +46,22 @@ static const char *const ideapad_wmi_fnesc_events[] = { #endif enum { - CFG_CAP_BT_BIT = 16, - CFG_CAP_3G_BIT = 17, - CFG_CAP_WIFI_BIT = 18, - CFG_CAP_CAM_BIT = 19, + CFG_CAP_BT_BIT = 16, + CFG_CAP_3G_BIT = 17, + CFG_CAP_WIFI_BIT = 18, + CFG_CAP_CAM_BIT = 19, + + /* + * These are OnScreenDisplay support bits that can be useful to determine + * whether a hotkey exists/should show OSD. But they aren't particularly + * meaningful since they were introduced later, i.e. 2010 IdeaPads + * don't have these, but they still have had OSD for hotkeys. + */ + CFG_OSD_NUMLK_BIT = 27, + CFG_OSD_CAPSLK_BIT = 28, + CFG_OSD_MICMUTE_BIT = 29, + CFG_OSD_TOUCHPAD_BIT = 30, + CFG_OSD_CAM_BIT = 31, }; enum { @@ -387,6 +399,19 @@ static int debugfs_cfg_show(struct seq_file *s, void *data) seq_puts(s, " camera"); seq_puts(s, "\n"); + seq_puts(s, "OSD support:"); + if (test_bit(CFG_OSD_NUMLK_BIT, &priv->cfg)) + seq_puts(s, " num-lock"); + if (test_bit(CFG_OSD_CAPSLK_BIT, &priv->cfg)) + seq_puts(s, " caps-lock"); + if (test_bit(CFG_OSD_MICMUTE_BIT, &priv->cfg)) + seq_puts(s, " mic-mute"); + if (test_bit(CFG_OSD_TOUCHPAD_BIT, &priv->cfg)) + seq_puts(s, " touchpad"); + if (test_bit(CFG_OSD_CAM_BIT, &priv->cfg)) + seq_puts(s, " camera"); + seq_puts(s, "\n"); + seq_puts(s, "Graphics: "); switch (priv->cfg & 0x700) { case 0x100: -- cgit v1.2.3-58-ga151 From f32e02417614d3588a3954dab2a70320c43d1010 Mon Sep 17 00:00:00 2001 From: Philipp Jungkamp Date: Wed, 16 Nov 2022 12:06:47 +0100 Subject: platform/x86: ideapad-laptop: support for more special keys in WMI The event data of the WMI event 0xD0, which is assumed to be the fn_lock, is used to indicate several special keys on newer Yoga 7/9 laptops. The notify_id 0xD0 is non-unique in the DSDT of the Yoga 9 14IAP7, this causes wmi_get_event_data() to report wrong values. Port the ideapad-laptop WMI code to the wmi bus infrastructure which does not suffer from the shortcomings of wmi_get_event_data(). Signed-off-by: Philipp Jungkamp Link: https://lore.kernel.org/r/20221116110647.3438-1-p.jungkamp@gmx.net Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/ideapad-laptop.c | 261 ++++++++++++++++++++++++++-------- 1 file changed, 202 insertions(+), 59 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 125b4534424f..13e3ae731fd8 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -37,14 +38,6 @@ #define IDEAPAD_RFKILL_DEV_NUM 3 -#if IS_ENABLED(CONFIG_ACPI_WMI) -static const char *const ideapad_wmi_fnesc_events[] = { - "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6", /* Yoga 3 */ - "56322276-8493-4CE8-A783-98C991274F5E", /* Yoga 700 */ - "8FC0DE0C-B4E4-43FD-B0F3-8871711C1294", /* Legion 5 */ -}; -#endif - enum { CFG_CAP_BT_BIT = 16, CFG_CAP_3G_BIT = 17, @@ -141,7 +134,6 @@ struct ideapad_private { struct ideapad_dytc_priv *dytc; struct dentry *debug; unsigned long cfg; - const char *fnesc_guid; struct { bool conservation_mode : 1; bool dytc : 1; @@ -182,6 +174,42 @@ MODULE_PARM_DESC(set_fn_lock_led, "Enable driver based updates of the fn-lock LED on fn-lock changes. " "If you need this please report this to: platform-driver-x86@vger.kernel.org"); +/* + * shared data + */ + +static struct ideapad_private *ideapad_shared; +static DEFINE_MUTEX(ideapad_shared_mutex); + +static int ideapad_shared_init(struct ideapad_private *priv) +{ + int ret; + + mutex_lock(&ideapad_shared_mutex); + + if (!ideapad_shared) { + ideapad_shared = priv; + ret = 0; + } else { + dev_warn(&priv->adev->dev, "found multiple platform devices\n"); + ret = -EINVAL; + } + + mutex_unlock(&ideapad_shared_mutex); + + return ret; +} + +static void ideapad_shared_exit(struct ideapad_private *priv) +{ + mutex_lock(&ideapad_shared_mutex); + + if (ideapad_shared == priv) + ideapad_shared = NULL; + + mutex_unlock(&ideapad_shared_mutex); +} + /* * ACPI Helpers */ @@ -1110,6 +1138,8 @@ static void ideapad_sysfs_exit(struct ideapad_private *priv) /* * input device */ +#define IDEAPAD_WMI_KEY 0x100 + static const struct key_entry ideapad_keymap[] = { { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } }, { KE_KEY, 7, { KEY_CAMERA } }, @@ -1123,6 +1153,28 @@ static const struct key_entry ideapad_keymap[] = { { KE_KEY, 66, { KEY_TOUCHPAD_OFF } }, { KE_KEY, 67, { KEY_TOUCHPAD_ON } }, { KE_KEY, 128, { KEY_ESC } }, + + /* + * WMI keys + */ + + /* FnLock (handled by the firmware) */ + { KE_IGNORE, 0x02 | IDEAPAD_WMI_KEY }, + /* Esc (handled by the firmware) */ + { KE_IGNORE, 0x03 | IDEAPAD_WMI_KEY }, + /* Customizable Lenovo Hotkey ("star" with 'S' inside) */ + { KE_KEY, 0x01 | IDEAPAD_WMI_KEY, { KEY_FAVORITES } }, + /* Dark mode toggle */ + { KE_KEY, 0x13 | IDEAPAD_WMI_KEY, { KEY_PROG1 } }, + /* Sound profile switch */ + { KE_KEY, 0x12 | IDEAPAD_WMI_KEY, { KEY_PROG2 } }, + /* Lenovo Virtual Background application */ + { KE_KEY, 0x28 | IDEAPAD_WMI_KEY, { KEY_PROG3 } }, + /* Lenovo Support */ + { KE_KEY, 0x27 | IDEAPAD_WMI_KEY, { KEY_HELP } }, + /* Refresh Rate Toggle */ + { KE_KEY, 0x0a | IDEAPAD_WMI_KEY, { KEY_DISPLAYTOGGLE } }, + { KE_END }, }; @@ -1526,33 +1578,6 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) } } -#if IS_ENABLED(CONFIG_ACPI_WMI) -static void ideapad_wmi_notify(u32 value, void *context) -{ - struct ideapad_private *priv = context; - unsigned long result; - - switch (value) { - case 128: - ideapad_input_report(priv, value); - break; - case 208: - if (!priv->features.set_fn_lock_led) - break; - - if (!eval_hals(priv->adev->handle, &result)) { - bool state = test_bit(HALS_FNLOCK_STATE_BIT, &result); - - exec_sals(priv->adev->handle, state ? SALS_FNLOCK_ON : SALS_FNLOCK_OFF); - } - break; - default: - dev_info(&priv->platform_device->dev, - "Unknown WMI event: %u\n", value); - } -} -#endif - /* On some models we need to call exec_sals(SALS_FNLOCK_ON/OFF) to set the LED */ static const struct dmi_system_id set_fn_lock_led_list[] = { { @@ -1643,6 +1668,118 @@ static void ideapad_check_features(struct ideapad_private *priv) } } +#if IS_ENABLED(CONFIG_ACPI_WMI) +/* + * WMI driver + */ +enum ideapad_wmi_event_type { + IDEAPAD_WMI_EVENT_ESC, + IDEAPAD_WMI_EVENT_FN_KEYS, +}; + +struct ideapad_wmi_private { + enum ideapad_wmi_event_type event; +}; + +static int ideapad_wmi_probe(struct wmi_device *wdev, const void *context) +{ + struct ideapad_wmi_private *wpriv; + + wpriv = devm_kzalloc(&wdev->dev, sizeof(*wpriv), GFP_KERNEL); + if (!wpriv) + return -ENOMEM; + + *wpriv = *(const struct ideapad_wmi_private *)context; + + dev_set_drvdata(&wdev->dev, wpriv); + return 0; +} + +static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data) +{ + struct ideapad_wmi_private *wpriv = dev_get_drvdata(&wdev->dev); + struct ideapad_private *priv; + unsigned long result; + + mutex_lock(&ideapad_shared_mutex); + + priv = ideapad_shared; + if (!priv) + goto unlock; + + switch (wpriv->event) { + case IDEAPAD_WMI_EVENT_ESC: + ideapad_input_report(priv, 128); + break; + case IDEAPAD_WMI_EVENT_FN_KEYS: + if (priv->features.set_fn_lock_led && + !eval_hals(priv->adev->handle, &result)) { + bool state = test_bit(HALS_FNLOCK_STATE_BIT, &result); + + exec_sals(priv->adev->handle, state ? SALS_FNLOCK_ON : SALS_FNLOCK_OFF); + } + + if (data->type != ACPI_TYPE_INTEGER) { + dev_warn(&wdev->dev, + "WMI event data is not an integer\n"); + break; + } + + dev_dbg(&wdev->dev, "WMI fn-key event: 0x%llx\n", + data->integer.value); + + ideapad_input_report(priv, + data->integer.value | IDEAPAD_WMI_KEY); + + break; + } +unlock: + mutex_unlock(&ideapad_shared_mutex); +} + +static const struct ideapad_wmi_private ideapad_wmi_context_esc = { + .event = IDEAPAD_WMI_EVENT_ESC +}; + +static const struct ideapad_wmi_private ideapad_wmi_context_fn_keys = { + .event = IDEAPAD_WMI_EVENT_FN_KEYS +}; + +static const struct wmi_device_id ideapad_wmi_ids[] = { + { "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6", &ideapad_wmi_context_esc }, /* Yoga 3 */ + { "56322276-8493-4CE8-A783-98C991274F5E", &ideapad_wmi_context_esc }, /* Yoga 700 */ + { "8FC0DE0C-B4E4-43FD-B0F3-8871711C1294", &ideapad_wmi_context_fn_keys }, /* Legion 5 */ + {}, +}; +MODULE_DEVICE_TABLE(wmi, ideapad_wmi_ids); + +static struct wmi_driver ideapad_wmi_driver = { + .driver = { + .name = "ideapad_wmi", + }, + .id_table = ideapad_wmi_ids, + .probe = ideapad_wmi_probe, + .notify = ideapad_wmi_notify, +}; + +static int ideapad_wmi_driver_register(void) +{ + return wmi_driver_register(&ideapad_wmi_driver); +} + +static void ideapad_wmi_driver_unregister(void) +{ + return wmi_driver_unregister(&ideapad_wmi_driver); +} + +#else +static inline int ideapad_wmi_driver_register(void) { return 0; } +static inline void ideapad_wmi_driver_unregister(void) { } +#endif + +/* + * ACPI driver + */ static int ideapad_acpi_add(struct platform_device *pdev) { struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); @@ -1724,30 +1861,16 @@ static int ideapad_acpi_add(struct platform_device *pdev) goto notification_failed; } -#if IS_ENABLED(CONFIG_ACPI_WMI) - for (i = 0; i < ARRAY_SIZE(ideapad_wmi_fnesc_events); i++) { - status = wmi_install_notify_handler(ideapad_wmi_fnesc_events[i], - ideapad_wmi_notify, priv); - if (ACPI_SUCCESS(status)) { - priv->fnesc_guid = ideapad_wmi_fnesc_events[i]; - break; - } - } - - if (ACPI_FAILURE(status) && status != AE_NOT_EXIST) { - err = -EIO; - goto notification_failed_wmi; - } -#endif + err = ideapad_shared_init(priv); + if (err) + goto shared_init_failed; return 0; -#if IS_ENABLED(CONFIG_ACPI_WMI) -notification_failed_wmi: +shared_init_failed: acpi_remove_notify_handler(priv->adev->handle, ACPI_DEVICE_NOTIFY, ideapad_acpi_notify); -#endif notification_failed: ideapad_backlight_exit(priv); @@ -1773,10 +1896,7 @@ static int ideapad_acpi_remove(struct platform_device *pdev) struct ideapad_private *priv = dev_get_drvdata(&pdev->dev); int i; -#if IS_ENABLED(CONFIG_ACPI_WMI) - if (priv->fnesc_guid) - wmi_remove_notify_handler(priv->fnesc_guid); -#endif + ideapad_shared_exit(priv); acpi_remove_notify_handler(priv->adev->handle, ACPI_DEVICE_NOTIFY, @@ -1828,7 +1948,30 @@ static struct platform_driver ideapad_acpi_driver = { }, }; -module_platform_driver(ideapad_acpi_driver); +static int __init ideapad_laptop_init(void) +{ + int err; + + err = ideapad_wmi_driver_register(); + if (err) + return err; + + err = platform_driver_register(&ideapad_acpi_driver); + if (err) { + ideapad_wmi_driver_unregister(); + return err; + } + + return 0; +} +module_init(ideapad_laptop_init) + +static void __exit ideapad_laptop_exit(void) +{ + ideapad_wmi_driver_unregister(); + platform_driver_unregister(&ideapad_acpi_driver); +} +module_exit(ideapad_laptop_exit) MODULE_AUTHOR("David Woodhouse "); MODULE_DESCRIPTION("IdeaPad ACPI Extras"); -- cgit v1.2.3-58-ga151 From 260ad3de718301ed8c22e28558e3a31c99f54cf6 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 16 Nov 2022 09:43:41 -0600 Subject: platform/x86/amd: pmc: Add a workaround for an s0i3 issue on Cezanne Cezanne platforms under the right circumstances have a synchronization problem where attempting to enter s2idle may fail if the x86 cores are put into HLT before hardware resume from the previous attempt has completed. To avoid this issue add a 10-20ms delay before entering s2idle another time. This workaround will only be applied on interrupts that wake the hardware but don't break the s2idle loop. Cc: stable@vger.kernel.org # 6.1 Cc: "Mahapatra, Rajib" Cc: "Raul Rangel" Signed-off-by: Mario Limonciello Link: https://lore.kernel.org/r/20221116154341.13382-1-mario.limonciello@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmc.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c index ef4ae977b8e0..439d282aafd1 100644 --- a/drivers/platform/x86/amd/pmc.c +++ b/drivers/platform/x86/amd/pmc.c @@ -739,8 +739,14 @@ static void amd_pmc_s2idle_prepare(void) static void amd_pmc_s2idle_check(void) { struct amd_pmc_dev *pdev = &pmc; + struct smu_metrics table; int rc; + /* CZN: Ensure that future s0i3 entry attempts at least 10ms passed */ + if (pdev->cpu_id == AMD_CPU_ID_CZN && !get_metrics_table(pdev, &table) && + table.s0i3_last_entry_status) + usleep_range(10000, 20000); + /* Dump the IdleMask before we add to the STB */ amd_pmc_idlemask_read(pdev, pdev->dev, NULL); -- cgit v1.2.3-58-ga151 From 284c01b72ac9cad03b972324a72782759ef980af Mon Sep 17 00:00:00 2001 From: Gayatri Kammela Date: Mon, 14 Nov 2022 10:32:50 -0800 Subject: platform/x86: intel/pmc: Replace all the reg_map with init functions The current implementation of pmc core driver has the reg_map assigned to the CPUID of each platform. Replace the reg_map with init functions that are defined for each platform. This is a preparatory patch for redesigning the pmc core driver. Cc: David E Box Reviewed-by: "David E. Box" Signed-off-by: Gayatri Kammela Signed-off-by: "David E. Box" Link: https://lore.kernel.org/r/20221114183257.2067662-2-gayatri.kammela@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core.c | 114 +++++++++++++++++++++++----------- drivers/platform/x86/intel/pmc/core.h | 13 ++++ 2 files changed, 90 insertions(+), 37 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 17ec5825d13d..b434cf5b094b 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -1895,27 +1894,73 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev) } } +void spt_core_init(struct pmc_dev *pmcdev) +{ + pmcdev->map = &spt_reg_map; +} + +void cnp_core_init(struct pmc_dev *pmcdev) +{ + pmcdev->map = &cnp_reg_map; +} + +void icl_core_init(struct pmc_dev *pmcdev) +{ + pmcdev->map = &icl_reg_map; +} + +void tgl_core_configure(struct pmc_dev *pmcdev) +{ + pmc_core_get_tgl_lpm_reqs(pmcdev->pdev); + /* Due to a hardware limitation, the GBE LTR blocks PC10 + * when a cable is attached. Tell the PMC to ignore it. + */ + dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); + pmc_core_send_ltr_ignore(pmcdev, 3); +} + +void tgl_core_init(struct pmc_dev *pmcdev) +{ + pmcdev->map = &tgl_reg_map; + pmcdev->core_configure = tgl_core_configure; +} + +void adl_core_configure(struct pmc_dev *pmcdev) +{ + /* Due to a hardware limitation, the GBE LTR blocks PC10 + * when a cable is attached. Tell the PMC to ignore it. + */ + dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); + pmc_core_send_ltr_ignore(pmcdev, 3); +} + +void adl_core_init(struct pmc_dev *pmcdev) +{ + pmcdev->map = &adl_reg_map; + pmcdev->core_configure = adl_core_configure; +} + static const struct x86_cpu_id intel_pmc_core_ids[] = { - X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, &spt_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, &spt_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, &spt_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, &spt_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(CANNONLAKE_L, &cnp_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L, &icl_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_NNPI, &icl_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, &cnp_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE_L, &cnp_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, &tgl_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, &tgl_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT, &tgl_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_L, &icl_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(ROCKETLAKE, &tgl_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, &tgl_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_N, &tgl_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, &adl_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, &tgl_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, &adl_reg_map), - X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, &adl_reg_map), + X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, spt_core_init), + X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, spt_core_init), + X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, spt_core_init), + X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, spt_core_init), + X86_MATCH_INTEL_FAM6_MODEL(CANNONLAKE_L, cnp_core_init), + X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L, icl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_NNPI, icl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, cnp_core_init), + X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE_L, cnp_core_init), + X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, tgl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, tgl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT, tgl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_L, icl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(ROCKETLAKE, tgl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, tgl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_N, tgl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, adl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, tgl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, adl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, adl_core_init), {} }; @@ -1975,6 +2020,7 @@ static int pmc_core_probe(struct platform_device *pdev) static bool device_initialized; struct pmc_dev *pmcdev; const struct x86_cpu_id *cpu_id; + void (*core_init)(struct pmc_dev *pmcdev); u64 slp_s0_addr; if (device_initialized) @@ -1985,20 +2031,25 @@ static int pmc_core_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, pmcdev); + pmcdev->pdev = pdev; cpu_id = x86_match_cpu(intel_pmc_core_ids); if (!cpu_id) return -ENODEV; - pmcdev->map = (struct pmc_reg_map *)cpu_id->driver_data; + core_init = (void (*)(struct pmc_dev *))cpu_id->driver_data; /* * Coffee Lake has CPU ID of Kaby Lake and Cannon Lake PCH. So here * Sunrisepoint PCH regmap can't be used. Use Cannon Lake PCH regmap * in this case. */ - if (pmcdev->map == &spt_reg_map && !pci_dev_present(pmc_pci_ids)) - pmcdev->map = &cnp_reg_map; + if (core_init == spt_core_init && !pci_dev_present(pmc_pci_ids)) + core_init = cnp_core_init; + + mutex_init(&pmcdev->lock); + core_init(pmcdev); + if (lpit_read_residency_count_address(&slp_s0_addr)) { pmcdev->base_addr = PMC_BASE_ADDR_DEFAULT; @@ -2014,24 +2065,13 @@ static int pmc_core_probe(struct platform_device *pdev) if (!pmcdev->regbase) return -ENOMEM; - mutex_init(&pmcdev->lock); + if (pmcdev->core_configure) + pmcdev->core_configure(pmcdev); pmcdev->pmc_xram_read_bit = pmc_core_check_read_lock_bit(pmcdev); pmc_core_get_low_power_modes(pdev); pmc_core_do_dmi_quirks(pmcdev); - if (pmcdev->map == &tgl_reg_map) - pmc_core_get_tgl_lpm_reqs(pdev); - - /* - * On TGL and ADL, due to a hardware limitation, the GBE LTR blocks PC10 - * when a cable is attached. Tell the PMC to ignore it. - */ - if (pmcdev->map == &tgl_reg_map || pmcdev->map == &adl_reg_map) { - dev_dbg(&pdev->dev, "ignoring GBE LTR\n"); - pmc_core_send_ltr_ignore(pmcdev, 3); - } - pmc_core_dbgfs_register(pmcdev); device_initialized = true; diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 4c7e77f1ccac..e64b33e46397 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -13,6 +13,7 @@ #define PMC_CORE_H #include +#include #define PMC_BASE_ADDR_DEFAULT 0xFE000000 @@ -312,6 +313,7 @@ struct pmc_reg_map { * @regbase: pointer to io-remapped memory location * @map: pointer to pmc_reg_map struct that contains platform * specific attributes + * @pdev: pointer to platform_device struct * @dbgfs_dir: path to debugfs interface * @pmc_xram_read_bit: flag to indicate whether PMC XRAM shadow registers * used to read MPHY PG and PLL status are available @@ -322,6 +324,7 @@ struct pmc_reg_map { * @num_lpm_modes: Count of enabled modes * @lpm_en_modes: Array of enabled modes from lowest to highest priority * @lpm_req_regs: List of substate requirements + * @core_configure: Function pointer to configure the platform * * pmc_dev contains info about power management controller device. */ @@ -330,6 +333,7 @@ struct pmc_dev { void __iomem *regbase; const struct pmc_reg_map *map; struct dentry *dbgfs_dir; + struct platform_device *pdev; int pmc_xram_read_bit; struct mutex lock; /* generic mutex lock for PMC Core */ @@ -339,8 +343,17 @@ struct pmc_dev { int num_lpm_modes; int lpm_en_modes[LPM_MAX_NUM_MODES]; u32 *lpm_req_regs; + void (*core_configure)(struct pmc_dev *pmcdev); }; +void spt_core_init(struct pmc_dev *pmcdev); +void cnp_core_init(struct pmc_dev *pmcdev); +void icl_core_init(struct pmc_dev *pmcdev); +void tgl_core_init(struct pmc_dev *pmcdev); +void adl_core_init(struct pmc_dev *pmcdev); +void tgl_core_configure(struct pmc_dev *pmcdev); +void adl_core_configure(struct pmc_dev *pmcdev); + #define pmc_for_each_mode(i, mode, pmcdev) \ for (i = 0, mode = pmcdev->lpm_en_modes[i]; \ i < pmcdev->num_lpm_modes; \ -- cgit v1.2.3-58-ga151 From 03c58a1e949d16faa922dd0501db1f2585d66eb3 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 14 Nov 2022 10:32:51 -0800 Subject: platform/x86: intel/pmc: Move variable declarations and definitions to header and core.c Move the msr_map variable declaration to core.h and move the pmc_lpm_modes definition to core.c. This is a prepartory patch for redesigning the pmc core driver as the variables will be used in multiple PCH specific files. Cc: David E Box Reviewed-by: "David E. Box" Signed-off-by: Xi Pardee Signed-off-by: "David E. Box" Link: https://lore.kernel.org/r/20221114183257.2067662-3-gayatri.kammela@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core.c | 15 ++++++++++++++- drivers/platform/x86/intel/pmc/core.h | 13 ++----------- 2 files changed, 16 insertions(+), 12 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index b434cf5b094b..cfa654672cba 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -35,8 +35,21 @@ #define ACPI_S0IX_DSM_UUID "57a6512e-3979-4e9d-9708-ff13b2508972" #define ACPI_GET_LOW_MODE_REGISTERS 1 +/* Maximum number of modes supported by platfoms that has low power mode capability */ +const char *pmc_lpm_modes[] = { + "S0i2.0", + "S0i2.1", + "S0i2.2", + "S0i3.0", + "S0i3.1", + "S0i3.2", + "S0i3.3", + "S0i3.4", + NULL +}; + /* PKGC MSRs are common across Intel Core SoCs */ -static const struct pmc_bit_map msr_map[] = { +const struct pmc_bit_map msr_map[] = { {"Package C2", MSR_PKG_C2_RESIDENCY}, {"Package C3", MSR_PKG_C3_RESIDENCY}, {"Package C6", MSR_PKG_C6_RESIDENCY}, diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index e64b33e46397..b4279ed59bbe 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -237,17 +237,7 @@ enum ppfear_regs { #define ADL_LPM_STATUS_LATCH_EN_OFFSET 0x1704 #define ADL_LPM_LIVE_STATUS_OFFSET 0x1764 -static const char *pmc_lpm_modes[] = { - "S0i2.0", - "S0i2.1", - "S0i2.2", - "S0i3.0", - "S0i3.1", - "S0i3.2", - "S0i3.3", - "S0i3.4", - NULL -}; +extern const char *pmc_lpm_modes[]; struct pmc_bit_map { const char *name; @@ -346,6 +336,7 @@ struct pmc_dev { void (*core_configure)(struct pmc_dev *pmcdev); }; +extern const struct pmc_bit_map msr_map[]; void spt_core_init(struct pmc_dev *pmcdev); void cnp_core_init(struct pmc_dev *pmcdev); void icl_core_init(struct pmc_dev *pmcdev); -- cgit v1.2.3-58-ga151 From f23e21a345956e3bd234c6d5a3d29818604dfe74 Mon Sep 17 00:00:00 2001 From: Rajvi Jingar Date: Mon, 14 Nov 2022 10:32:52 -0800 Subject: platform/x86: intel/pmc: Relocate Sunrise Point PCH support Create spt.c for Sunrise Point PCH specific structures and init(). This file supports Sky Lake and Kaby Lake platforms. There are no functional changes involved. Cc: David E Box Reviewed-by: "David E. Box" Signed-off-by: Rajvi Jingar Signed-off-by: "David E. Box" Link: https://lore.kernel.org/r/20221114183257.2067662-4-gayatri.kammela@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/Makefile | 2 +- drivers/platform/x86/intel/pmc/core.c | 129 ----------------------------- drivers/platform/x86/intel/pmc/core.h | 7 ++ drivers/platform/x86/intel/pmc/spt.c | 140 ++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 130 deletions(-) create mode 100644 drivers/platform/x86/intel/pmc/spt.c (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile index 8966fcdc0e1d..1b2104e0c97f 100644 --- a/drivers/platform/x86/intel/pmc/Makefile +++ b/drivers/platform/x86/intel/pmc/Makefile @@ -3,7 +3,7 @@ # Intel x86 Platform-Specific Drivers # -intel_pmc_core-y := core.o +intel_pmc_core-y := core.o spt.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv-y := pltdrv.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index cfa654672cba..18b6e87f7c73 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -60,130 +60,6 @@ const struct pmc_bit_map msr_map[] = { {} }; -static const struct pmc_bit_map spt_pll_map[] = { - {"MIPI PLL", SPT_PMC_BIT_MPHY_CMN_LANE0}, - {"GEN2 USB2PCIE2 PLL", SPT_PMC_BIT_MPHY_CMN_LANE1}, - {"DMIPCIE3 PLL", SPT_PMC_BIT_MPHY_CMN_LANE2}, - {"SATA PLL", SPT_PMC_BIT_MPHY_CMN_LANE3}, - {} -}; - -static const struct pmc_bit_map spt_mphy_map[] = { - {"MPHY CORE LANE 0", SPT_PMC_BIT_MPHY_LANE0}, - {"MPHY CORE LANE 1", SPT_PMC_BIT_MPHY_LANE1}, - {"MPHY CORE LANE 2", SPT_PMC_BIT_MPHY_LANE2}, - {"MPHY CORE LANE 3", SPT_PMC_BIT_MPHY_LANE3}, - {"MPHY CORE LANE 4", SPT_PMC_BIT_MPHY_LANE4}, - {"MPHY CORE LANE 5", SPT_PMC_BIT_MPHY_LANE5}, - {"MPHY CORE LANE 6", SPT_PMC_BIT_MPHY_LANE6}, - {"MPHY CORE LANE 7", SPT_PMC_BIT_MPHY_LANE7}, - {"MPHY CORE LANE 8", SPT_PMC_BIT_MPHY_LANE8}, - {"MPHY CORE LANE 9", SPT_PMC_BIT_MPHY_LANE9}, - {"MPHY CORE LANE 10", SPT_PMC_BIT_MPHY_LANE10}, - {"MPHY CORE LANE 11", SPT_PMC_BIT_MPHY_LANE11}, - {"MPHY CORE LANE 12", SPT_PMC_BIT_MPHY_LANE12}, - {"MPHY CORE LANE 13", SPT_PMC_BIT_MPHY_LANE13}, - {"MPHY CORE LANE 14", SPT_PMC_BIT_MPHY_LANE14}, - {"MPHY CORE LANE 15", SPT_PMC_BIT_MPHY_LANE15}, - {} -}; - -static const struct pmc_bit_map spt_pfear_map[] = { - {"PMC", SPT_PMC_BIT_PMC}, - {"OPI-DMI", SPT_PMC_BIT_OPI}, - {"SPI / eSPI", SPT_PMC_BIT_SPI}, - {"XHCI", SPT_PMC_BIT_XHCI}, - {"SPA", SPT_PMC_BIT_SPA}, - {"SPB", SPT_PMC_BIT_SPB}, - {"SPC", SPT_PMC_BIT_SPC}, - {"GBE", SPT_PMC_BIT_GBE}, - {"SATA", SPT_PMC_BIT_SATA}, - {"HDA-PGD0", SPT_PMC_BIT_HDA_PGD0}, - {"HDA-PGD1", SPT_PMC_BIT_HDA_PGD1}, - {"HDA-PGD2", SPT_PMC_BIT_HDA_PGD2}, - {"HDA-PGD3", SPT_PMC_BIT_HDA_PGD3}, - {"RSVD", SPT_PMC_BIT_RSVD_0B}, - {"LPSS", SPT_PMC_BIT_LPSS}, - {"LPC", SPT_PMC_BIT_LPC}, - {"SMB", SPT_PMC_BIT_SMB}, - {"ISH", SPT_PMC_BIT_ISH}, - {"P2SB", SPT_PMC_BIT_P2SB}, - {"DFX", SPT_PMC_BIT_DFX}, - {"SCC", SPT_PMC_BIT_SCC}, - {"RSVD", SPT_PMC_BIT_RSVD_0C}, - {"FUSE", SPT_PMC_BIT_FUSE}, - {"CAMERA", SPT_PMC_BIT_CAMREA}, - {"RSVD", SPT_PMC_BIT_RSVD_0D}, - {"USB3-OTG", SPT_PMC_BIT_USB3_OTG}, - {"EXI", SPT_PMC_BIT_EXI}, - {"CSE", SPT_PMC_BIT_CSE}, - {"CSME_KVM", SPT_PMC_BIT_CSME_KVM}, - {"CSME_PMT", SPT_PMC_BIT_CSME_PMT}, - {"CSME_CLINK", SPT_PMC_BIT_CSME_CLINK}, - {"CSME_PTIO", SPT_PMC_BIT_CSME_PTIO}, - {"CSME_USBR", SPT_PMC_BIT_CSME_USBR}, - {"CSME_SUSRAM", SPT_PMC_BIT_CSME_SUSRAM}, - {"CSME_SMT", SPT_PMC_BIT_CSME_SMT}, - {"RSVD", SPT_PMC_BIT_RSVD_1A}, - {"CSME_SMS2", SPT_PMC_BIT_CSME_SMS2}, - {"CSME_SMS1", SPT_PMC_BIT_CSME_SMS1}, - {"CSME_RTC", SPT_PMC_BIT_CSME_RTC}, - {"CSME_PSF", SPT_PMC_BIT_CSME_PSF}, - {} -}; - -static const struct pmc_bit_map *ext_spt_pfear_map[] = { - /* - * Check intel_pmc_core_ids[] users of spt_reg_map for - * a list of core SoCs using this. - */ - spt_pfear_map, - NULL -}; - -static const struct pmc_bit_map spt_ltr_show_map[] = { - {"SOUTHPORT_A", SPT_PMC_LTR_SPA}, - {"SOUTHPORT_B", SPT_PMC_LTR_SPB}, - {"SATA", SPT_PMC_LTR_SATA}, - {"GIGABIT_ETHERNET", SPT_PMC_LTR_GBE}, - {"XHCI", SPT_PMC_LTR_XHCI}, - {"Reserved", SPT_PMC_LTR_RESERVED}, - {"ME", SPT_PMC_LTR_ME}, - /* EVA is Enterprise Value Add, doesn't really exist on PCH */ - {"EVA", SPT_PMC_LTR_EVA}, - {"SOUTHPORT_C", SPT_PMC_LTR_SPC}, - {"HD_AUDIO", SPT_PMC_LTR_AZ}, - {"LPSS", SPT_PMC_LTR_LPSS}, - {"SOUTHPORT_D", SPT_PMC_LTR_SPD}, - {"SOUTHPORT_E", SPT_PMC_LTR_SPE}, - {"CAMERA", SPT_PMC_LTR_CAM}, - {"ESPI", SPT_PMC_LTR_ESPI}, - {"SCC", SPT_PMC_LTR_SCC}, - {"ISH", SPT_PMC_LTR_ISH}, - /* Below two cannot be used for LTR_IGNORE */ - {"CURRENT_PLATFORM", SPT_PMC_LTR_CUR_PLT}, - {"AGGREGATED_SYSTEM", SPT_PMC_LTR_CUR_ASLT}, - {} -}; - -static const struct pmc_reg_map spt_reg_map = { - .pfear_sts = ext_spt_pfear_map, - .mphy_sts = spt_mphy_map, - .pll_sts = spt_pll_map, - .ltr_show_sts = spt_ltr_show_map, - .msr_sts = msr_map, - .slp_s0_offset = SPT_PMC_SLP_S0_RES_COUNTER_OFFSET, - .slp_s0_res_counter_step = SPT_PMC_SLP_S0_RES_COUNTER_STEP, - .ltr_ignore_offset = SPT_PMC_LTR_IGNORE_OFFSET, - .regmap_length = SPT_PMC_MMIO_REG_LEN, - .ppfear0_offset = SPT_PMC_XRAM_PPFEAR0A, - .ppfear_buckets = SPT_PPFEAR_NUM_ENTRIES, - .pm_cfg_offset = SPT_PMC_PM_CFG_OFFSET, - .pm_read_disable_bit = SPT_PMC_READ_DISABLE_BIT, - .ltr_ignore_max = SPT_NUM_IP_IGN_ALLOWED, - .pm_vric1_offset = SPT_PMC_VRIC1_OFFSET, -}; - /* Cannon Lake: PGD PFET Enable Ack Status Register(s) bitmap */ static const struct pmc_bit_map cnp_pfear_map[] = { {"PMC", BIT(0)}, @@ -1907,11 +1783,6 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev) } } -void spt_core_init(struct pmc_dev *pmcdev) -{ - pmcdev->map = &spt_reg_map; -} - void cnp_core_init(struct pmc_dev *pmcdev) { pmcdev->map = &cnp_reg_map; diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index b4279ed59bbe..65189353cc2b 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -337,6 +337,13 @@ struct pmc_dev { }; extern const struct pmc_bit_map msr_map[]; +extern const struct pmc_bit_map spt_pll_map[]; +extern const struct pmc_bit_map spt_mphy_map[]; +extern const struct pmc_bit_map spt_pfear_map[]; +extern const struct pmc_bit_map *ext_spt_pfear_map[]; +extern const struct pmc_bit_map spt_ltr_show_map[]; +extern const struct pmc_reg_map spt_reg_map; + void spt_core_init(struct pmc_dev *pmcdev); void cnp_core_init(struct pmc_dev *pmcdev); void icl_core_init(struct pmc_dev *pmcdev); diff --git a/drivers/platform/x86/intel/pmc/spt.c b/drivers/platform/x86/intel/pmc/spt.c new file mode 100644 index 000000000000..e16982236778 --- /dev/null +++ b/drivers/platform/x86/intel/pmc/spt.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file contains platform specific structure definitions + * and init function used by Sunrise Point PCH. + * + * Copyright (c) 2022, Intel Corporation. + * All Rights Reserved. + * + */ + +#include "core.h" + +const struct pmc_bit_map spt_pll_map[] = { + {"MIPI PLL", SPT_PMC_BIT_MPHY_CMN_LANE0}, + {"GEN2 USB2PCIE2 PLL", SPT_PMC_BIT_MPHY_CMN_LANE1}, + {"DMIPCIE3 PLL", SPT_PMC_BIT_MPHY_CMN_LANE2}, + {"SATA PLL", SPT_PMC_BIT_MPHY_CMN_LANE3}, + {} +}; + +const struct pmc_bit_map spt_mphy_map[] = { + {"MPHY CORE LANE 0", SPT_PMC_BIT_MPHY_LANE0}, + {"MPHY CORE LANE 1", SPT_PMC_BIT_MPHY_LANE1}, + {"MPHY CORE LANE 2", SPT_PMC_BIT_MPHY_LANE2}, + {"MPHY CORE LANE 3", SPT_PMC_BIT_MPHY_LANE3}, + {"MPHY CORE LANE 4", SPT_PMC_BIT_MPHY_LANE4}, + {"MPHY CORE LANE 5", SPT_PMC_BIT_MPHY_LANE5}, + {"MPHY CORE LANE 6", SPT_PMC_BIT_MPHY_LANE6}, + {"MPHY CORE LANE 7", SPT_PMC_BIT_MPHY_LANE7}, + {"MPHY CORE LANE 8", SPT_PMC_BIT_MPHY_LANE8}, + {"MPHY CORE LANE 9", SPT_PMC_BIT_MPHY_LANE9}, + {"MPHY CORE LANE 10", SPT_PMC_BIT_MPHY_LANE10}, + {"MPHY CORE LANE 11", SPT_PMC_BIT_MPHY_LANE11}, + {"MPHY CORE LANE 12", SPT_PMC_BIT_MPHY_LANE12}, + {"MPHY CORE LANE 13", SPT_PMC_BIT_MPHY_LANE13}, + {"MPHY CORE LANE 14", SPT_PMC_BIT_MPHY_LANE14}, + {"MPHY CORE LANE 15", SPT_PMC_BIT_MPHY_LANE15}, + {} +}; + +const struct pmc_bit_map spt_pfear_map[] = { + {"PMC", SPT_PMC_BIT_PMC}, + {"OPI-DMI", SPT_PMC_BIT_OPI}, + {"SPI / eSPI", SPT_PMC_BIT_SPI}, + {"XHCI", SPT_PMC_BIT_XHCI}, + {"SPA", SPT_PMC_BIT_SPA}, + {"SPB", SPT_PMC_BIT_SPB}, + {"SPC", SPT_PMC_BIT_SPC}, + {"GBE", SPT_PMC_BIT_GBE}, + {"SATA", SPT_PMC_BIT_SATA}, + {"HDA-PGD0", SPT_PMC_BIT_HDA_PGD0}, + {"HDA-PGD1", SPT_PMC_BIT_HDA_PGD1}, + {"HDA-PGD2", SPT_PMC_BIT_HDA_PGD2}, + {"HDA-PGD3", SPT_PMC_BIT_HDA_PGD3}, + {"RSVD", SPT_PMC_BIT_RSVD_0B}, + {"LPSS", SPT_PMC_BIT_LPSS}, + {"LPC", SPT_PMC_BIT_LPC}, + {"SMB", SPT_PMC_BIT_SMB}, + {"ISH", SPT_PMC_BIT_ISH}, + {"P2SB", SPT_PMC_BIT_P2SB}, + {"DFX", SPT_PMC_BIT_DFX}, + {"SCC", SPT_PMC_BIT_SCC}, + {"RSVD", SPT_PMC_BIT_RSVD_0C}, + {"FUSE", SPT_PMC_BIT_FUSE}, + {"CAMERA", SPT_PMC_BIT_CAMREA}, + {"RSVD", SPT_PMC_BIT_RSVD_0D}, + {"USB3-OTG", SPT_PMC_BIT_USB3_OTG}, + {"EXI", SPT_PMC_BIT_EXI}, + {"CSE", SPT_PMC_BIT_CSE}, + {"CSME_KVM", SPT_PMC_BIT_CSME_KVM}, + {"CSME_PMT", SPT_PMC_BIT_CSME_PMT}, + {"CSME_CLINK", SPT_PMC_BIT_CSME_CLINK}, + {"CSME_PTIO", SPT_PMC_BIT_CSME_PTIO}, + {"CSME_USBR", SPT_PMC_BIT_CSME_USBR}, + {"CSME_SUSRAM", SPT_PMC_BIT_CSME_SUSRAM}, + {"CSME_SMT", SPT_PMC_BIT_CSME_SMT}, + {"RSVD", SPT_PMC_BIT_RSVD_1A}, + {"CSME_SMS2", SPT_PMC_BIT_CSME_SMS2}, + {"CSME_SMS1", SPT_PMC_BIT_CSME_SMS1}, + {"CSME_RTC", SPT_PMC_BIT_CSME_RTC}, + {"CSME_PSF", SPT_PMC_BIT_CSME_PSF}, + {} +}; + +const struct pmc_bit_map *ext_spt_pfear_map[] = { + /* + * Check intel_pmc_core_ids[] users of spt_reg_map for + * a list of core SoCs using this. + */ + spt_pfear_map, + NULL +}; + +const struct pmc_bit_map spt_ltr_show_map[] = { + {"SOUTHPORT_A", SPT_PMC_LTR_SPA}, + {"SOUTHPORT_B", SPT_PMC_LTR_SPB}, + {"SATA", SPT_PMC_LTR_SATA}, + {"GIGABIT_ETHERNET", SPT_PMC_LTR_GBE}, + {"XHCI", SPT_PMC_LTR_XHCI}, + {"Reserved", SPT_PMC_LTR_RESERVED}, + {"ME", SPT_PMC_LTR_ME}, + /* EVA is Enterprise Value Add, doesn't really exist on PCH */ + {"EVA", SPT_PMC_LTR_EVA}, + {"SOUTHPORT_C", SPT_PMC_LTR_SPC}, + {"HD_AUDIO", SPT_PMC_LTR_AZ}, + {"LPSS", SPT_PMC_LTR_LPSS}, + {"SOUTHPORT_D", SPT_PMC_LTR_SPD}, + {"SOUTHPORT_E", SPT_PMC_LTR_SPE}, + {"CAMERA", SPT_PMC_LTR_CAM}, + {"ESPI", SPT_PMC_LTR_ESPI}, + {"SCC", SPT_PMC_LTR_SCC}, + {"ISH", SPT_PMC_LTR_ISH}, + /* Below two cannot be used for LTR_IGNORE */ + {"CURRENT_PLATFORM", SPT_PMC_LTR_CUR_PLT}, + {"AGGREGATED_SYSTEM", SPT_PMC_LTR_CUR_ASLT}, + {} +}; + +const struct pmc_reg_map spt_reg_map = { + .pfear_sts = ext_spt_pfear_map, + .mphy_sts = spt_mphy_map, + .pll_sts = spt_pll_map, + .ltr_show_sts = spt_ltr_show_map, + .msr_sts = msr_map, + .slp_s0_offset = SPT_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = SPT_PMC_SLP_S0_RES_COUNTER_STEP, + .ltr_ignore_offset = SPT_PMC_LTR_IGNORE_OFFSET, + .regmap_length = SPT_PMC_MMIO_REG_LEN, + .ppfear0_offset = SPT_PMC_XRAM_PPFEAR0A, + .ppfear_buckets = SPT_PPFEAR_NUM_ENTRIES, + .pm_cfg_offset = SPT_PMC_PM_CFG_OFFSET, + .pm_read_disable_bit = SPT_PMC_READ_DISABLE_BIT, + .ltr_ignore_max = SPT_NUM_IP_IGN_ALLOWED, + .pm_vric1_offset = SPT_PMC_VRIC1_OFFSET, +}; + +void spt_core_init(struct pmc_dev *pmcdev) +{ + pmcdev->map = &spt_reg_map; +} -- cgit v1.2.3-58-ga151 From d6cd0cc8d16dadf2a3c0ddc93e21cd1d13b02fbb Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 14 Nov 2022 10:32:53 -0800 Subject: platform/x86: intel/pmc: Relocate Cannon Lake Point PCH support Create cnp.c for Cannon Lake Point PCH specific structures and init(). This file supports Cannon Lake and Comet Lake platforms. There are no functional changes involved. Cc: David E Box Reviewed-by: "David E. Box" Signed-off-by: Xi Pardee Signed-off-by: "David E. Box" Link: https://lore.kernel.org/r/20221114183257.2067662-5-gayatri.kammela@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/Makefile | 2 +- drivers/platform/x86/intel/pmc/cnp.c | 210 ++++++++++++++++++++++++++++++++ drivers/platform/x86/intel/pmc/core.c | 199 ------------------------------ drivers/platform/x86/intel/pmc/core.h | 8 ++ 4 files changed, 219 insertions(+), 200 deletions(-) create mode 100644 drivers/platform/x86/intel/pmc/cnp.c (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile index 1b2104e0c97f..2b6d4a8dd2bb 100644 --- a/drivers/platform/x86/intel/pmc/Makefile +++ b/drivers/platform/x86/intel/pmc/Makefile @@ -3,7 +3,7 @@ # Intel x86 Platform-Specific Drivers # -intel_pmc_core-y := core.o spt.o +intel_pmc_core-y := core.o spt.o cnp.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv-y := pltdrv.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o diff --git a/drivers/platform/x86/intel/pmc/cnp.c b/drivers/platform/x86/intel/pmc/cnp.c new file mode 100644 index 000000000000..7fb38815c4eb --- /dev/null +++ b/drivers/platform/x86/intel/pmc/cnp.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file contains platform specific structure definitions + * and init function used by Cannon Lake Point PCH. + * + * Copyright (c) 2022, Intel Corporation. + * All Rights Reserved. + * + */ + +#include "core.h" + +/* Cannon Lake: PGD PFET Enable Ack Status Register(s) bitmap */ +const struct pmc_bit_map cnp_pfear_map[] = { + {"PMC", BIT(0)}, + {"OPI-DMI", BIT(1)}, + {"SPI/eSPI", BIT(2)}, + {"XHCI", BIT(3)}, + {"SPA", BIT(4)}, + {"SPB", BIT(5)}, + {"SPC", BIT(6)}, + {"GBE", BIT(7)}, + + {"SATA", BIT(0)}, + {"HDA_PGD0", BIT(1)}, + {"HDA_PGD1", BIT(2)}, + {"HDA_PGD2", BIT(3)}, + {"HDA_PGD3", BIT(4)}, + {"SPD", BIT(5)}, + {"LPSS", BIT(6)}, + {"LPC", BIT(7)}, + + {"SMB", BIT(0)}, + {"ISH", BIT(1)}, + {"P2SB", BIT(2)}, + {"NPK_VNN", BIT(3)}, + {"SDX", BIT(4)}, + {"SPE", BIT(5)}, + {"Fuse", BIT(6)}, + {"SBR8", BIT(7)}, + + {"CSME_FSC", BIT(0)}, + {"USB3_OTG", BIT(1)}, + {"EXI", BIT(2)}, + {"CSE", BIT(3)}, + {"CSME_KVM", BIT(4)}, + {"CSME_PMT", BIT(5)}, + {"CSME_CLINK", BIT(6)}, + {"CSME_PTIO", BIT(7)}, + + {"CSME_USBR", BIT(0)}, + {"CSME_SUSRAM", BIT(1)}, + {"CSME_SMT1", BIT(2)}, + {"CSME_SMT4", BIT(3)}, + {"CSME_SMS2", BIT(4)}, + {"CSME_SMS1", BIT(5)}, + {"CSME_RTC", BIT(6)}, + {"CSME_PSF", BIT(7)}, + + {"SBR0", BIT(0)}, + {"SBR1", BIT(1)}, + {"SBR2", BIT(2)}, + {"SBR3", BIT(3)}, + {"SBR4", BIT(4)}, + {"SBR5", BIT(5)}, + {"CSME_PECI", BIT(6)}, + {"PSF1", BIT(7)}, + + {"PSF2", BIT(0)}, + {"PSF3", BIT(1)}, + {"PSF4", BIT(2)}, + {"CNVI", BIT(3)}, + {"UFS0", BIT(4)}, + {"EMMC", BIT(5)}, + {"SPF", BIT(6)}, + {"SBR6", BIT(7)}, + + {"SBR7", BIT(0)}, + {"NPK_AON", BIT(1)}, + {"HDA_PGD4", BIT(2)}, + {"HDA_PGD5", BIT(3)}, + {"HDA_PGD6", BIT(4)}, + {"PSF6", BIT(5)}, + {"PSF7", BIT(6)}, + {"PSF8", BIT(7)}, + {} +}; + +const struct pmc_bit_map *ext_cnp_pfear_map[] = { + /* + * Check intel_pmc_core_ids[] users of cnp_reg_map for + * a list of core SoCs using this. + */ + cnp_pfear_map, + NULL +}; + +const struct pmc_bit_map cnp_slps0_dbg0_map[] = { + {"AUDIO_D3", BIT(0)}, + {"OTG_D3", BIT(1)}, + {"XHCI_D3", BIT(2)}, + {"LPIO_D3", BIT(3)}, + {"SDX_D3", BIT(4)}, + {"SATA_D3", BIT(5)}, + {"UFS0_D3", BIT(6)}, + {"UFS1_D3", BIT(7)}, + {"EMMC_D3", BIT(8)}, + {} +}; + +const struct pmc_bit_map cnp_slps0_dbg1_map[] = { + {"SDIO_PLL_OFF", BIT(0)}, + {"USB2_PLL_OFF", BIT(1)}, + {"AUDIO_PLL_OFF", BIT(2)}, + {"OC_PLL_OFF", BIT(3)}, + {"MAIN_PLL_OFF", BIT(4)}, + {"XOSC_OFF", BIT(5)}, + {"LPC_CLKS_GATED", BIT(6)}, + {"PCIE_CLKREQS_IDLE", BIT(7)}, + {"AUDIO_ROSC_OFF", BIT(8)}, + {"HPET_XOSC_CLK_REQ", BIT(9)}, + {"PMC_ROSC_SLOW_CLK", BIT(10)}, + {"AON2_ROSC_GATED", BIT(11)}, + {"CLKACKS_DEASSERTED", BIT(12)}, + {} +}; + +const struct pmc_bit_map cnp_slps0_dbg2_map[] = { + {"MPHY_CORE_GATED", BIT(0)}, + {"CSME_GATED", BIT(1)}, + {"USB2_SUS_GATED", BIT(2)}, + {"DYN_FLEX_IO_IDLE", BIT(3)}, + {"GBE_NO_LINK", BIT(4)}, + {"THERM_SEN_DISABLED", BIT(5)}, + {"PCIE_LOW_POWER", BIT(6)}, + {"ISH_VNNAON_REQ_ACT", BIT(7)}, + {"ISH_VNN_REQ_ACT", BIT(8)}, + {"CNV_VNNAON_REQ_ACT", BIT(9)}, + {"CNV_VNN_REQ_ACT", BIT(10)}, + {"NPK_VNNON_REQ_ACT", BIT(11)}, + {"PMSYNC_STATE_IDLE", BIT(12)}, + {"ALST_GT_THRES", BIT(13)}, + {"PMC_ARC_PG_READY", BIT(14)}, + {} +}; + +const struct pmc_bit_map *cnp_slps0_dbg_maps[] = { + cnp_slps0_dbg0_map, + cnp_slps0_dbg1_map, + cnp_slps0_dbg2_map, + NULL +}; + +const struct pmc_bit_map cnp_ltr_show_map[] = { + {"SOUTHPORT_A", CNP_PMC_LTR_SPA}, + {"SOUTHPORT_B", CNP_PMC_LTR_SPB}, + {"SATA", CNP_PMC_LTR_SATA}, + {"GIGABIT_ETHERNET", CNP_PMC_LTR_GBE}, + {"XHCI", CNP_PMC_LTR_XHCI}, + {"Reserved", CNP_PMC_LTR_RESERVED}, + {"ME", CNP_PMC_LTR_ME}, + /* EVA is Enterprise Value Add, doesn't really exist on PCH */ + {"EVA", CNP_PMC_LTR_EVA}, + {"SOUTHPORT_C", CNP_PMC_LTR_SPC}, + {"HD_AUDIO", CNP_PMC_LTR_AZ}, + {"CNV", CNP_PMC_LTR_CNV}, + {"LPSS", CNP_PMC_LTR_LPSS}, + {"SOUTHPORT_D", CNP_PMC_LTR_SPD}, + {"SOUTHPORT_E", CNP_PMC_LTR_SPE}, + {"CAMERA", CNP_PMC_LTR_CAM}, + {"ESPI", CNP_PMC_LTR_ESPI}, + {"SCC", CNP_PMC_LTR_SCC}, + {"ISH", CNP_PMC_LTR_ISH}, + {"UFSX2", CNP_PMC_LTR_UFSX2}, + {"EMMC", CNP_PMC_LTR_EMMC}, + /* + * Check intel_pmc_core_ids[] users of cnp_reg_map for + * a list of core SoCs using this. + */ + {"WIGIG", ICL_PMC_LTR_WIGIG}, + {"THC0", TGL_PMC_LTR_THC0}, + {"THC1", TGL_PMC_LTR_THC1}, + /* Below two cannot be used for LTR_IGNORE */ + {"CURRENT_PLATFORM", CNP_PMC_LTR_CUR_PLT}, + {"AGGREGATED_SYSTEM", CNP_PMC_LTR_CUR_ASLT}, + {} +}; + +const struct pmc_reg_map cnp_reg_map = { + .pfear_sts = ext_cnp_pfear_map, + .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = SPT_PMC_SLP_S0_RES_COUNTER_STEP, + .slps0_dbg_maps = cnp_slps0_dbg_maps, + .ltr_show_sts = cnp_ltr_show_map, + .msr_sts = msr_map, + .slps0_dbg_offset = CNP_PMC_SLPS0_DBG_OFFSET, + .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, + .regmap_length = CNP_PMC_MMIO_REG_LEN, + .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, + .ppfear_buckets = CNP_PPFEAR_NUM_ENTRIES, + .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, + .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, + .ltr_ignore_max = CNP_NUM_IP_IGN_ALLOWED, + .etr3_offset = ETR3_OFFSET, +}; + +void cnp_core_init(struct pmc_dev *pmcdev) +{ + pmcdev->map = &cnp_reg_map; +} diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 18b6e87f7c73..1cce586035f9 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -60,91 +60,6 @@ const struct pmc_bit_map msr_map[] = { {} }; -/* Cannon Lake: PGD PFET Enable Ack Status Register(s) bitmap */ -static const struct pmc_bit_map cnp_pfear_map[] = { - {"PMC", BIT(0)}, - {"OPI-DMI", BIT(1)}, - {"SPI/eSPI", BIT(2)}, - {"XHCI", BIT(3)}, - {"SPA", BIT(4)}, - {"SPB", BIT(5)}, - {"SPC", BIT(6)}, - {"GBE", BIT(7)}, - - {"SATA", BIT(0)}, - {"HDA_PGD0", BIT(1)}, - {"HDA_PGD1", BIT(2)}, - {"HDA_PGD2", BIT(3)}, - {"HDA_PGD3", BIT(4)}, - {"SPD", BIT(5)}, - {"LPSS", BIT(6)}, - {"LPC", BIT(7)}, - - {"SMB", BIT(0)}, - {"ISH", BIT(1)}, - {"P2SB", BIT(2)}, - {"NPK_VNN", BIT(3)}, - {"SDX", BIT(4)}, - {"SPE", BIT(5)}, - {"Fuse", BIT(6)}, - {"SBR8", BIT(7)}, - - {"CSME_FSC", BIT(0)}, - {"USB3_OTG", BIT(1)}, - {"EXI", BIT(2)}, - {"CSE", BIT(3)}, - {"CSME_KVM", BIT(4)}, - {"CSME_PMT", BIT(5)}, - {"CSME_CLINK", BIT(6)}, - {"CSME_PTIO", BIT(7)}, - - {"CSME_USBR", BIT(0)}, - {"CSME_SUSRAM", BIT(1)}, - {"CSME_SMT1", BIT(2)}, - {"CSME_SMT4", BIT(3)}, - {"CSME_SMS2", BIT(4)}, - {"CSME_SMS1", BIT(5)}, - {"CSME_RTC", BIT(6)}, - {"CSME_PSF", BIT(7)}, - - {"SBR0", BIT(0)}, - {"SBR1", BIT(1)}, - {"SBR2", BIT(2)}, - {"SBR3", BIT(3)}, - {"SBR4", BIT(4)}, - {"SBR5", BIT(5)}, - {"CSME_PECI", BIT(6)}, - {"PSF1", BIT(7)}, - - {"PSF2", BIT(0)}, - {"PSF3", BIT(1)}, - {"PSF4", BIT(2)}, - {"CNVI", BIT(3)}, - {"UFS0", BIT(4)}, - {"EMMC", BIT(5)}, - {"SPF", BIT(6)}, - {"SBR6", BIT(7)}, - - {"SBR7", BIT(0)}, - {"NPK_AON", BIT(1)}, - {"HDA_PGD4", BIT(2)}, - {"HDA_PGD5", BIT(3)}, - {"HDA_PGD6", BIT(4)}, - {"PSF6", BIT(5)}, - {"PSF7", BIT(6)}, - {"PSF8", BIT(7)}, - {} -}; - -static const struct pmc_bit_map *ext_cnp_pfear_map[] = { - /* - * Check intel_pmc_core_ids[] users of cnp_reg_map for - * a list of core SoCs using this. - */ - cnp_pfear_map, - NULL -}; - static const struct pmc_bit_map icl_pfear_map[] = { {"RES_65", BIT(0)}, {"RES_66", BIT(1)}, @@ -188,115 +103,6 @@ static const struct pmc_bit_map *ext_tgl_pfear_map[] = { NULL }; -static const struct pmc_bit_map cnp_slps0_dbg0_map[] = { - {"AUDIO_D3", BIT(0)}, - {"OTG_D3", BIT(1)}, - {"XHCI_D3", BIT(2)}, - {"LPIO_D3", BIT(3)}, - {"SDX_D3", BIT(4)}, - {"SATA_D3", BIT(5)}, - {"UFS0_D3", BIT(6)}, - {"UFS1_D3", BIT(7)}, - {"EMMC_D3", BIT(8)}, - {} -}; - -static const struct pmc_bit_map cnp_slps0_dbg1_map[] = { - {"SDIO_PLL_OFF", BIT(0)}, - {"USB2_PLL_OFF", BIT(1)}, - {"AUDIO_PLL_OFF", BIT(2)}, - {"OC_PLL_OFF", BIT(3)}, - {"MAIN_PLL_OFF", BIT(4)}, - {"XOSC_OFF", BIT(5)}, - {"LPC_CLKS_GATED", BIT(6)}, - {"PCIE_CLKREQS_IDLE", BIT(7)}, - {"AUDIO_ROSC_OFF", BIT(8)}, - {"HPET_XOSC_CLK_REQ", BIT(9)}, - {"PMC_ROSC_SLOW_CLK", BIT(10)}, - {"AON2_ROSC_GATED", BIT(11)}, - {"CLKACKS_DEASSERTED", BIT(12)}, - {} -}; - -static const struct pmc_bit_map cnp_slps0_dbg2_map[] = { - {"MPHY_CORE_GATED", BIT(0)}, - {"CSME_GATED", BIT(1)}, - {"USB2_SUS_GATED", BIT(2)}, - {"DYN_FLEX_IO_IDLE", BIT(3)}, - {"GBE_NO_LINK", BIT(4)}, - {"THERM_SEN_DISABLED", BIT(5)}, - {"PCIE_LOW_POWER", BIT(6)}, - {"ISH_VNNAON_REQ_ACT", BIT(7)}, - {"ISH_VNN_REQ_ACT", BIT(8)}, - {"CNV_VNNAON_REQ_ACT", BIT(9)}, - {"CNV_VNN_REQ_ACT", BIT(10)}, - {"NPK_VNNON_REQ_ACT", BIT(11)}, - {"PMSYNC_STATE_IDLE", BIT(12)}, - {"ALST_GT_THRES", BIT(13)}, - {"PMC_ARC_PG_READY", BIT(14)}, - {} -}; - -static const struct pmc_bit_map *cnp_slps0_dbg_maps[] = { - cnp_slps0_dbg0_map, - cnp_slps0_dbg1_map, - cnp_slps0_dbg2_map, - NULL -}; - -static const struct pmc_bit_map cnp_ltr_show_map[] = { - {"SOUTHPORT_A", CNP_PMC_LTR_SPA}, - {"SOUTHPORT_B", CNP_PMC_LTR_SPB}, - {"SATA", CNP_PMC_LTR_SATA}, - {"GIGABIT_ETHERNET", CNP_PMC_LTR_GBE}, - {"XHCI", CNP_PMC_LTR_XHCI}, - {"Reserved", CNP_PMC_LTR_RESERVED}, - {"ME", CNP_PMC_LTR_ME}, - /* EVA is Enterprise Value Add, doesn't really exist on PCH */ - {"EVA", CNP_PMC_LTR_EVA}, - {"SOUTHPORT_C", CNP_PMC_LTR_SPC}, - {"HD_AUDIO", CNP_PMC_LTR_AZ}, - {"CNV", CNP_PMC_LTR_CNV}, - {"LPSS", CNP_PMC_LTR_LPSS}, - {"SOUTHPORT_D", CNP_PMC_LTR_SPD}, - {"SOUTHPORT_E", CNP_PMC_LTR_SPE}, - {"CAMERA", CNP_PMC_LTR_CAM}, - {"ESPI", CNP_PMC_LTR_ESPI}, - {"SCC", CNP_PMC_LTR_SCC}, - {"ISH", CNP_PMC_LTR_ISH}, - {"UFSX2", CNP_PMC_LTR_UFSX2}, - {"EMMC", CNP_PMC_LTR_EMMC}, - /* - * Check intel_pmc_core_ids[] users of cnp_reg_map for - * a list of core SoCs using this. - */ - {"WIGIG", ICL_PMC_LTR_WIGIG}, - {"THC0", TGL_PMC_LTR_THC0}, - {"THC1", TGL_PMC_LTR_THC1}, - /* Below two cannot be used for LTR_IGNORE */ - {"CURRENT_PLATFORM", CNP_PMC_LTR_CUR_PLT}, - {"AGGREGATED_SYSTEM", CNP_PMC_LTR_CUR_ASLT}, - {} -}; - -static const struct pmc_reg_map cnp_reg_map = { - .pfear_sts = ext_cnp_pfear_map, - .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, - .slp_s0_res_counter_step = SPT_PMC_SLP_S0_RES_COUNTER_STEP, - .slps0_dbg_maps = cnp_slps0_dbg_maps, - .ltr_show_sts = cnp_ltr_show_map, - .msr_sts = msr_map, - .slps0_dbg_offset = CNP_PMC_SLPS0_DBG_OFFSET, - .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, - .regmap_length = CNP_PMC_MMIO_REG_LEN, - .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, - .ppfear_buckets = CNP_PPFEAR_NUM_ENTRIES, - .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, - .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, - .ltr_ignore_max = CNP_NUM_IP_IGN_ALLOWED, - .etr3_offset = ETR3_OFFSET, -}; - static const struct pmc_reg_map icl_reg_map = { .pfear_sts = ext_icl_pfear_map, .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, @@ -1783,11 +1589,6 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev) } } -void cnp_core_init(struct pmc_dev *pmcdev) -{ - pmcdev->map = &cnp_reg_map; -} - void icl_core_init(struct pmc_dev *pmcdev) { pmcdev->map = &icl_reg_map; diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 65189353cc2b..2b7d681588cb 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -343,6 +343,14 @@ extern const struct pmc_bit_map spt_pfear_map[]; extern const struct pmc_bit_map *ext_spt_pfear_map[]; extern const struct pmc_bit_map spt_ltr_show_map[]; extern const struct pmc_reg_map spt_reg_map; +extern const struct pmc_bit_map cnp_pfear_map[]; +extern const struct pmc_bit_map *ext_cnp_pfear_map[]; +extern const struct pmc_bit_map cnp_slps0_dbg0_map[]; +extern const struct pmc_bit_map cnp_slps0_dbg1_map[]; +extern const struct pmc_bit_map cnp_slps0_dbg2_map[]; +extern const struct pmc_bit_map *cnp_slps0_dbg_maps[]; +extern const struct pmc_bit_map cnp_ltr_show_map[]; +extern const struct pmc_reg_map cnp_reg_map; void spt_core_init(struct pmc_dev *pmcdev); void cnp_core_init(struct pmc_dev *pmcdev); -- cgit v1.2.3-58-ga151 From fd2ed6dbc34979fbb2a745a259f2a3b201f64299 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 14 Nov 2022 10:32:54 -0800 Subject: platform/x86: intel/pmc: Relocate Ice Lake PCH support Create icl.c for Ice Lake PCH specific structures and init(). This file supports Ice Lake, Ice Lake NNPI and Jasper Lake platforms. There are no functional changes involved. Cc: David E Box Reviewed-by: "David E. Box" Signed-off-by: Xi Pardee Signed-off-by: "David E. Box" Link: https://lore.kernel.org/r/20221114183257.2067662-6-gayatri.kammela@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/Makefile | 2 +- drivers/platform/x86/intel/pmc/core.c | 45 -------------------------- drivers/platform/x86/intel/pmc/core.h | 3 ++ drivers/platform/x86/intel/pmc/icl.c | 56 +++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 46 deletions(-) create mode 100644 drivers/platform/x86/intel/pmc/icl.c (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile index 2b6d4a8dd2bb..6d153e368da1 100644 --- a/drivers/platform/x86/intel/pmc/Makefile +++ b/drivers/platform/x86/intel/pmc/Makefile @@ -3,7 +3,7 @@ # Intel x86 Platform-Specific Drivers # -intel_pmc_core-y := core.o spt.o cnp.o +intel_pmc_core-y := core.o spt.o cnp.o icl.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv-y := pltdrv.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 1cce586035f9..d75e8cbaf40c 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -60,28 +60,6 @@ const struct pmc_bit_map msr_map[] = { {} }; -static const struct pmc_bit_map icl_pfear_map[] = { - {"RES_65", BIT(0)}, - {"RES_66", BIT(1)}, - {"RES_67", BIT(2)}, - {"TAM", BIT(3)}, - {"GBETSN", BIT(4)}, - {"TBTLSX", BIT(5)}, - {"RES_71", BIT(6)}, - {"RES_72", BIT(7)}, - {} -}; - -static const struct pmc_bit_map *ext_icl_pfear_map[] = { - /* - * Check intel_pmc_core_ids[] users of icl_reg_map for - * a list of core SoCs using this. - */ - cnp_pfear_map, - icl_pfear_map, - NULL -}; - static const struct pmc_bit_map tgl_pfear_map[] = { {"PSF9", BIT(0)}, {"RES_66", BIT(1)}, @@ -103,24 +81,6 @@ static const struct pmc_bit_map *ext_tgl_pfear_map[] = { NULL }; -static const struct pmc_reg_map icl_reg_map = { - .pfear_sts = ext_icl_pfear_map, - .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, - .slp_s0_res_counter_step = ICL_PMC_SLP_S0_RES_COUNTER_STEP, - .slps0_dbg_maps = cnp_slps0_dbg_maps, - .ltr_show_sts = cnp_ltr_show_map, - .msr_sts = msr_map, - .slps0_dbg_offset = CNP_PMC_SLPS0_DBG_OFFSET, - .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, - .regmap_length = CNP_PMC_MMIO_REG_LEN, - .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, - .ppfear_buckets = ICL_PPFEAR_NUM_ENTRIES, - .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, - .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, - .ltr_ignore_max = ICL_NUM_IP_IGN_ALLOWED, - .etr3_offset = ETR3_OFFSET, -}; - static const struct pmc_bit_map tgl_clocksource_status_map[] = { {"USB2PLL_OFF_STS", BIT(18)}, {"PCIe/USB3.1_Gen2PLL_OFF_STS", BIT(19)}, @@ -1589,11 +1549,6 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev) } } -void icl_core_init(struct pmc_dev *pmcdev) -{ - pmcdev->map = &icl_reg_map; -} - void tgl_core_configure(struct pmc_dev *pmcdev) { pmc_core_get_tgl_lpm_reqs(pmcdev->pdev); diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 2b7d681588cb..2ca564878341 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -351,6 +351,9 @@ extern const struct pmc_bit_map cnp_slps0_dbg2_map[]; extern const struct pmc_bit_map *cnp_slps0_dbg_maps[]; extern const struct pmc_bit_map cnp_ltr_show_map[]; extern const struct pmc_reg_map cnp_reg_map; +extern const struct pmc_bit_map icl_pfear_map[]; +extern const struct pmc_bit_map *ext_icl_pfear_map[]; +extern const struct pmc_reg_map icl_reg_map; void spt_core_init(struct pmc_dev *pmcdev); void cnp_core_init(struct pmc_dev *pmcdev); diff --git a/drivers/platform/x86/intel/pmc/icl.c b/drivers/platform/x86/intel/pmc/icl.c new file mode 100644 index 000000000000..2f11b1a6daeb --- /dev/null +++ b/drivers/platform/x86/intel/pmc/icl.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file contains platform specific structure definitions + * and init function used by Ice Lake PCH. + * + * Copyright (c) 2022, Intel Corporation. + * All Rights Reserved. + * + */ + +#include "core.h" + +const struct pmc_bit_map icl_pfear_map[] = { + {"RES_65", BIT(0)}, + {"RES_66", BIT(1)}, + {"RES_67", BIT(2)}, + {"TAM", BIT(3)}, + {"GBETSN", BIT(4)}, + {"TBTLSX", BIT(5)}, + {"RES_71", BIT(6)}, + {"RES_72", BIT(7)}, + {} +}; + +const struct pmc_bit_map *ext_icl_pfear_map[] = { + /* + * Check intel_pmc_core_ids[] users of icl_reg_map for + * a list of core SoCs using this. + */ + cnp_pfear_map, + icl_pfear_map, + NULL +}; + +const struct pmc_reg_map icl_reg_map = { + .pfear_sts = ext_icl_pfear_map, + .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = ICL_PMC_SLP_S0_RES_COUNTER_STEP, + .slps0_dbg_maps = cnp_slps0_dbg_maps, + .ltr_show_sts = cnp_ltr_show_map, + .msr_sts = msr_map, + .slps0_dbg_offset = CNP_PMC_SLPS0_DBG_OFFSET, + .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, + .regmap_length = CNP_PMC_MMIO_REG_LEN, + .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, + .ppfear_buckets = ICL_PPFEAR_NUM_ENTRIES, + .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, + .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, + .ltr_ignore_max = ICL_NUM_IP_IGN_ALLOWED, + .etr3_offset = ETR3_OFFSET, +}; + +void icl_core_init(struct pmc_dev *pmcdev) +{ + pmcdev->map = &icl_reg_map; +} -- cgit v1.2.3-58-ga151 From 92f530edd7c9557c225edd6db051e00b3ef9a2ac Mon Sep 17 00:00:00 2001 From: Gayatri Kammela Date: Mon, 14 Nov 2022 10:32:55 -0800 Subject: platform/x86: intel/pmc: Relocate Tiger Lake PCH support Create tgl.c for Tiger Lake PCH specific structures and init(). This file supports Tiger Lake, Elkhart Lake, Rocket Lake, Alder Lake mobile, Alder Lake N and Raptor Lake P platforms. There are no functional changes involved. Cc: David E Box Reviewed-by: "David E. Box" Signed-off-by: Gayatri Kammela Signed-off-by: "David E. Box" Link: https://lore.kernel.org/r/20221114183257.2067662-7-gayatri.kammela@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/Makefile | 2 +- drivers/platform/x86/intel/pmc/core.c | 264 +------------------------------ drivers/platform/x86/intel/pmc/core.h | 14 ++ drivers/platform/x86/intel/pmc/tgl.c | 269 ++++++++++++++++++++++++++++++++ 4 files changed, 285 insertions(+), 264 deletions(-) create mode 100644 drivers/platform/x86/intel/pmc/tgl.c (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile index 6d153e368da1..ad34c8255584 100644 --- a/drivers/platform/x86/intel/pmc/Makefile +++ b/drivers/platform/x86/intel/pmc/Makefile @@ -3,7 +3,7 @@ # Intel x86 Platform-Specific Drivers # -intel_pmc_core-y := core.o spt.o cnp.o icl.o +intel_pmc_core-y := core.o spt.o cnp.o icl.o tgl.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv-y := pltdrv.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index d75e8cbaf40c..4ff176eabe3f 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -11,7 +11,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include #include #include #include @@ -21,10 +20,7 @@ #include #include #include -#include -#include -#include #include #include #include @@ -32,9 +28,6 @@ #include "core.h" -#define ACPI_S0IX_DSM_UUID "57a6512e-3979-4e9d-9708-ff13b2508972" -#define ACPI_GET_LOW_MODE_REGISTERS 1 - /* Maximum number of modes supported by platfoms that has low power mode capability */ const char *pmc_lpm_modes[] = { "S0i2.0", @@ -60,245 +53,6 @@ const struct pmc_bit_map msr_map[] = { {} }; -static const struct pmc_bit_map tgl_pfear_map[] = { - {"PSF9", BIT(0)}, - {"RES_66", BIT(1)}, - {"RES_67", BIT(2)}, - {"RES_68", BIT(3)}, - {"RES_69", BIT(4)}, - {"RES_70", BIT(5)}, - {"TBTLSX", BIT(6)}, - {} -}; - -static const struct pmc_bit_map *ext_tgl_pfear_map[] = { - /* - * Check intel_pmc_core_ids[] users of tgl_reg_map for - * a list of core SoCs using this. - */ - cnp_pfear_map, - tgl_pfear_map, - NULL -}; - -static const struct pmc_bit_map tgl_clocksource_status_map[] = { - {"USB2PLL_OFF_STS", BIT(18)}, - {"PCIe/USB3.1_Gen2PLL_OFF_STS", BIT(19)}, - {"PCIe_Gen3PLL_OFF_STS", BIT(20)}, - {"OPIOPLL_OFF_STS", BIT(21)}, - {"OCPLL_OFF_STS", BIT(22)}, - {"MainPLL_OFF_STS", BIT(23)}, - {"MIPIPLL_OFF_STS", BIT(24)}, - {"Fast_XTAL_Osc_OFF_STS", BIT(25)}, - {"AC_Ring_Osc_OFF_STS", BIT(26)}, - {"MC_Ring_Osc_OFF_STS", BIT(27)}, - {"SATAPLL_OFF_STS", BIT(29)}, - {"XTAL_USB2PLL_OFF_STS", BIT(31)}, - {} -}; - -static const struct pmc_bit_map tgl_power_gating_status_map[] = { - {"CSME_PG_STS", BIT(0)}, - {"SATA_PG_STS", BIT(1)}, - {"xHCI_PG_STS", BIT(2)}, - {"UFSX2_PG_STS", BIT(3)}, - {"OTG_PG_STS", BIT(5)}, - {"SPA_PG_STS", BIT(6)}, - {"SPB_PG_STS", BIT(7)}, - {"SPC_PG_STS", BIT(8)}, - {"SPD_PG_STS", BIT(9)}, - {"SPE_PG_STS", BIT(10)}, - {"SPF_PG_STS", BIT(11)}, - {"LSX_PG_STS", BIT(13)}, - {"P2SB_PG_STS", BIT(14)}, - {"PSF_PG_STS", BIT(15)}, - {"SBR_PG_STS", BIT(16)}, - {"OPIDMI_PG_STS", BIT(17)}, - {"THC0_PG_STS", BIT(18)}, - {"THC1_PG_STS", BIT(19)}, - {"GBETSN_PG_STS", BIT(20)}, - {"GBE_PG_STS", BIT(21)}, - {"LPSS_PG_STS", BIT(22)}, - {"MMP_UFSX2_PG_STS", BIT(23)}, - {"MMP_UFSX2B_PG_STS", BIT(24)}, - {"FIA_PG_STS", BIT(25)}, - {} -}; - -static const struct pmc_bit_map tgl_d3_status_map[] = { - {"ADSP_D3_STS", BIT(0)}, - {"SATA_D3_STS", BIT(1)}, - {"xHCI0_D3_STS", BIT(2)}, - {"xDCI1_D3_STS", BIT(5)}, - {"SDX_D3_STS", BIT(6)}, - {"EMMC_D3_STS", BIT(7)}, - {"IS_D3_STS", BIT(8)}, - {"THC0_D3_STS", BIT(9)}, - {"THC1_D3_STS", BIT(10)}, - {"GBE_D3_STS", BIT(11)}, - {"GBE_TSN_D3_STS", BIT(12)}, - {} -}; - -static const struct pmc_bit_map tgl_vnn_req_status_map[] = { - {"GPIO_COM0_VNN_REQ_STS", BIT(1)}, - {"GPIO_COM1_VNN_REQ_STS", BIT(2)}, - {"GPIO_COM2_VNN_REQ_STS", BIT(3)}, - {"GPIO_COM3_VNN_REQ_STS", BIT(4)}, - {"GPIO_COM4_VNN_REQ_STS", BIT(5)}, - {"GPIO_COM5_VNN_REQ_STS", BIT(6)}, - {"Audio_VNN_REQ_STS", BIT(7)}, - {"ISH_VNN_REQ_STS", BIT(8)}, - {"CNVI_VNN_REQ_STS", BIT(9)}, - {"eSPI_VNN_REQ_STS", BIT(10)}, - {"Display_VNN_REQ_STS", BIT(11)}, - {"DTS_VNN_REQ_STS", BIT(12)}, - {"SMBUS_VNN_REQ_STS", BIT(14)}, - {"CSME_VNN_REQ_STS", BIT(15)}, - {"SMLINK0_VNN_REQ_STS", BIT(16)}, - {"SMLINK1_VNN_REQ_STS", BIT(17)}, - {"CLINK_VNN_REQ_STS", BIT(20)}, - {"DCI_VNN_REQ_STS", BIT(21)}, - {"ITH_VNN_REQ_STS", BIT(22)}, - {"CSME_VNN_REQ_STS", BIT(24)}, - {"GBE_VNN_REQ_STS", BIT(25)}, - {} -}; - -static const struct pmc_bit_map tgl_vnn_misc_status_map[] = { - {"CPU_C10_REQ_STS_0", BIT(0)}, - {"PCIe_LPM_En_REQ_STS_3", BIT(3)}, - {"ITH_REQ_STS_5", BIT(5)}, - {"CNVI_REQ_STS_6", BIT(6)}, - {"ISH_REQ_STS_7", BIT(7)}, - {"USB2_SUS_PG_Sys_REQ_STS_10", BIT(10)}, - {"PCIe_Clk_REQ_STS_12", BIT(12)}, - {"MPHY_Core_DL_REQ_STS_16", BIT(16)}, - {"Break-even_En_REQ_STS_17", BIT(17)}, - {"Auto-demo_En_REQ_STS_18", BIT(18)}, - {"MPHY_SUS_REQ_STS_22", BIT(22)}, - {"xDCI_attached_REQ_STS_24", BIT(24)}, - {} -}; - -static const struct pmc_bit_map tgl_signal_status_map[] = { - {"LSX_Wake0_En_STS", BIT(0)}, - {"LSX_Wake0_Pol_STS", BIT(1)}, - {"LSX_Wake1_En_STS", BIT(2)}, - {"LSX_Wake1_Pol_STS", BIT(3)}, - {"LSX_Wake2_En_STS", BIT(4)}, - {"LSX_Wake2_Pol_STS", BIT(5)}, - {"LSX_Wake3_En_STS", BIT(6)}, - {"LSX_Wake3_Pol_STS", BIT(7)}, - {"LSX_Wake4_En_STS", BIT(8)}, - {"LSX_Wake4_Pol_STS", BIT(9)}, - {"LSX_Wake5_En_STS", BIT(10)}, - {"LSX_Wake5_Pol_STS", BIT(11)}, - {"LSX_Wake6_En_STS", BIT(12)}, - {"LSX_Wake6_Pol_STS", BIT(13)}, - {"LSX_Wake7_En_STS", BIT(14)}, - {"LSX_Wake7_Pol_STS", BIT(15)}, - {"Intel_Se_IO_Wake0_En_STS", BIT(16)}, - {"Intel_Se_IO_Wake0_Pol_STS", BIT(17)}, - {"Intel_Se_IO_Wake1_En_STS", BIT(18)}, - {"Intel_Se_IO_Wake1_Pol_STS", BIT(19)}, - {"Int_Timer_SS_Wake0_En_STS", BIT(20)}, - {"Int_Timer_SS_Wake0_Pol_STS", BIT(21)}, - {"Int_Timer_SS_Wake1_En_STS", BIT(22)}, - {"Int_Timer_SS_Wake1_Pol_STS", BIT(23)}, - {"Int_Timer_SS_Wake2_En_STS", BIT(24)}, - {"Int_Timer_SS_Wake2_Pol_STS", BIT(25)}, - {"Int_Timer_SS_Wake3_En_STS", BIT(26)}, - {"Int_Timer_SS_Wake3_Pol_STS", BIT(27)}, - {"Int_Timer_SS_Wake4_En_STS", BIT(28)}, - {"Int_Timer_SS_Wake4_Pol_STS", BIT(29)}, - {"Int_Timer_SS_Wake5_En_STS", BIT(30)}, - {"Int_Timer_SS_Wake5_Pol_STS", BIT(31)}, - {} -}; - -static const struct pmc_bit_map *tgl_lpm_maps[] = { - tgl_clocksource_status_map, - tgl_power_gating_status_map, - tgl_d3_status_map, - tgl_vnn_req_status_map, - tgl_vnn_misc_status_map, - tgl_signal_status_map, - NULL -}; - -static const struct pmc_reg_map tgl_reg_map = { - .pfear_sts = ext_tgl_pfear_map, - .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, - .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, - .ltr_show_sts = cnp_ltr_show_map, - .msr_sts = msr_map, - .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, - .regmap_length = CNP_PMC_MMIO_REG_LEN, - .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, - .ppfear_buckets = ICL_PPFEAR_NUM_ENTRIES, - .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, - .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, - .ltr_ignore_max = TGL_NUM_IP_IGN_ALLOWED, - .lpm_num_maps = TGL_LPM_NUM_MAPS, - .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, - .lpm_sts_latch_en_offset = TGL_LPM_STS_LATCH_EN_OFFSET, - .lpm_en_offset = TGL_LPM_EN_OFFSET, - .lpm_priority_offset = TGL_LPM_PRI_OFFSET, - .lpm_residency_offset = TGL_LPM_RESIDENCY_OFFSET, - .lpm_sts = tgl_lpm_maps, - .lpm_status_offset = TGL_LPM_STATUS_OFFSET, - .lpm_live_status_offset = TGL_LPM_LIVE_STATUS_OFFSET, - .etr3_offset = ETR3_OFFSET, -}; - -static void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev) -{ - struct pmc_dev *pmcdev = platform_get_drvdata(pdev); - const int num_maps = pmcdev->map->lpm_num_maps; - u32 lpm_size = LPM_MAX_NUM_MODES * num_maps * 4; - union acpi_object *out_obj; - struct acpi_device *adev; - guid_t s0ix_dsm_guid; - u32 *lpm_req_regs, *addr; - - adev = ACPI_COMPANION(&pdev->dev); - if (!adev) - return; - - guid_parse(ACPI_S0IX_DSM_UUID, &s0ix_dsm_guid); - - out_obj = acpi_evaluate_dsm(adev->handle, &s0ix_dsm_guid, 0, - ACPI_GET_LOW_MODE_REGISTERS, NULL); - if (out_obj && out_obj->type == ACPI_TYPE_BUFFER) { - u32 size = out_obj->buffer.length; - - if (size != lpm_size) { - acpi_handle_debug(adev->handle, - "_DSM returned unexpected buffer size, have %u, expect %u\n", - size, lpm_size); - goto free_acpi_obj; - } - } else { - acpi_handle_debug(adev->handle, - "_DSM function 0 evaluation failed\n"); - goto free_acpi_obj; - } - - addr = (u32 *)out_obj->buffer.pointer; - - lpm_req_regs = devm_kzalloc(&pdev->dev, lpm_size * sizeof(u32), - GFP_KERNEL); - if (!lpm_req_regs) - goto free_acpi_obj; - - memcpy(lpm_req_regs, addr, lpm_size); - pmcdev->lpm_req_regs = lpm_req_regs; - -free_acpi_obj: - ACPI_FREE(out_obj); -} - /* Alder Lake: PGD PFET Enable Ack Status Register(s) bitmap */ static const struct pmc_bit_map adl_pfear_map[] = { {"SPI/eSPI", BIT(2)}, @@ -981,7 +735,7 @@ out_unlock: } DEFINE_SHOW_ATTRIBUTE(pmc_core_pll); -static int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value) +int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value) { const struct pmc_reg_map *map = pmcdev->map; u32 reg; @@ -1549,22 +1303,6 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev) } } -void tgl_core_configure(struct pmc_dev *pmcdev) -{ - pmc_core_get_tgl_lpm_reqs(pmcdev->pdev); - /* Due to a hardware limitation, the GBE LTR blocks PC10 - * when a cable is attached. Tell the PMC to ignore it. - */ - dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); - pmc_core_send_ltr_ignore(pmcdev, 3); -} - -void tgl_core_init(struct pmc_dev *pmcdev) -{ - pmcdev->map = &tgl_reg_map; - pmcdev->core_configure = tgl_core_configure; -} - void adl_core_configure(struct pmc_dev *pmcdev) { /* Due to a hardware limitation, the GBE LTR blocks PC10 diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 2ca564878341..38fca2a54d23 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -12,6 +12,7 @@ #ifndef PMC_CORE_H #define PMC_CORE_H +#include #include #include @@ -354,6 +355,19 @@ extern const struct pmc_reg_map cnp_reg_map; extern const struct pmc_bit_map icl_pfear_map[]; extern const struct pmc_bit_map *ext_icl_pfear_map[]; extern const struct pmc_reg_map icl_reg_map; +extern const struct pmc_bit_map tgl_pfear_map[]; +extern const struct pmc_bit_map *ext_tgl_pfear_map[]; +extern const struct pmc_bit_map tgl_clocksource_status_map[]; +extern const struct pmc_bit_map tgl_power_gating_status_map[]; +extern const struct pmc_bit_map tgl_d3_status_map[]; +extern const struct pmc_bit_map tgl_vnn_req_status_map[]; +extern const struct pmc_bit_map tgl_vnn_misc_status_map[]; +extern const struct pmc_bit_map tgl_signal_status_map[]; +extern const struct pmc_bit_map *tgl_lpm_maps[]; +extern const struct pmc_reg_map tgl_reg_map; + +extern void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev); +extern int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value); void spt_core_init(struct pmc_dev *pmcdev); void cnp_core_init(struct pmc_dev *pmcdev); diff --git a/drivers/platform/x86/intel/pmc/tgl.c b/drivers/platform/x86/intel/pmc/tgl.c new file mode 100644 index 000000000000..e3e50538465d --- /dev/null +++ b/drivers/platform/x86/intel/pmc/tgl.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file contains platform specific structure definitions + * and init function used by Tiger Lake PCH. + * + * Copyright (c) 2022, Intel Corporation. + * All Rights Reserved. + * + */ + +#include "core.h" + +#define ACPI_S0IX_DSM_UUID "57a6512e-3979-4e9d-9708-ff13b2508972" +#define ACPI_GET_LOW_MODE_REGISTERS 1 + +const struct pmc_bit_map tgl_pfear_map[] = { + {"PSF9", BIT(0)}, + {"RES_66", BIT(1)}, + {"RES_67", BIT(2)}, + {"RES_68", BIT(3)}, + {"RES_69", BIT(4)}, + {"RES_70", BIT(5)}, + {"TBTLSX", BIT(6)}, + {} +}; + +const struct pmc_bit_map *ext_tgl_pfear_map[] = { + /* + * Check intel_pmc_core_ids[] users of tgl_reg_map for + * a list of core SoCs using this. + */ + cnp_pfear_map, + tgl_pfear_map, + NULL +}; + +const struct pmc_bit_map tgl_clocksource_status_map[] = { + {"USB2PLL_OFF_STS", BIT(18)}, + {"PCIe/USB3.1_Gen2PLL_OFF_STS", BIT(19)}, + {"PCIe_Gen3PLL_OFF_STS", BIT(20)}, + {"OPIOPLL_OFF_STS", BIT(21)}, + {"OCPLL_OFF_STS", BIT(22)}, + {"MainPLL_OFF_STS", BIT(23)}, + {"MIPIPLL_OFF_STS", BIT(24)}, + {"Fast_XTAL_Osc_OFF_STS", BIT(25)}, + {"AC_Ring_Osc_OFF_STS", BIT(26)}, + {"MC_Ring_Osc_OFF_STS", BIT(27)}, + {"SATAPLL_OFF_STS", BIT(29)}, + {"XTAL_USB2PLL_OFF_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map tgl_power_gating_status_map[] = { + {"CSME_PG_STS", BIT(0)}, + {"SATA_PG_STS", BIT(1)}, + {"xHCI_PG_STS", BIT(2)}, + {"UFSX2_PG_STS", BIT(3)}, + {"OTG_PG_STS", BIT(5)}, + {"SPA_PG_STS", BIT(6)}, + {"SPB_PG_STS", BIT(7)}, + {"SPC_PG_STS", BIT(8)}, + {"SPD_PG_STS", BIT(9)}, + {"SPE_PG_STS", BIT(10)}, + {"SPF_PG_STS", BIT(11)}, + {"LSX_PG_STS", BIT(13)}, + {"P2SB_PG_STS", BIT(14)}, + {"PSF_PG_STS", BIT(15)}, + {"SBR_PG_STS", BIT(16)}, + {"OPIDMI_PG_STS", BIT(17)}, + {"THC0_PG_STS", BIT(18)}, + {"THC1_PG_STS", BIT(19)}, + {"GBETSN_PG_STS", BIT(20)}, + {"GBE_PG_STS", BIT(21)}, + {"LPSS_PG_STS", BIT(22)}, + {"MMP_UFSX2_PG_STS", BIT(23)}, + {"MMP_UFSX2B_PG_STS", BIT(24)}, + {"FIA_PG_STS", BIT(25)}, + {} +}; + +const struct pmc_bit_map tgl_d3_status_map[] = { + {"ADSP_D3_STS", BIT(0)}, + {"SATA_D3_STS", BIT(1)}, + {"xHCI0_D3_STS", BIT(2)}, + {"xDCI1_D3_STS", BIT(5)}, + {"SDX_D3_STS", BIT(6)}, + {"EMMC_D3_STS", BIT(7)}, + {"IS_D3_STS", BIT(8)}, + {"THC0_D3_STS", BIT(9)}, + {"THC1_D3_STS", BIT(10)}, + {"GBE_D3_STS", BIT(11)}, + {"GBE_TSN_D3_STS", BIT(12)}, + {} +}; + +const struct pmc_bit_map tgl_vnn_req_status_map[] = { + {"GPIO_COM0_VNN_REQ_STS", BIT(1)}, + {"GPIO_COM1_VNN_REQ_STS", BIT(2)}, + {"GPIO_COM2_VNN_REQ_STS", BIT(3)}, + {"GPIO_COM3_VNN_REQ_STS", BIT(4)}, + {"GPIO_COM4_VNN_REQ_STS", BIT(5)}, + {"GPIO_COM5_VNN_REQ_STS", BIT(6)}, + {"Audio_VNN_REQ_STS", BIT(7)}, + {"ISH_VNN_REQ_STS", BIT(8)}, + {"CNVI_VNN_REQ_STS", BIT(9)}, + {"eSPI_VNN_REQ_STS", BIT(10)}, + {"Display_VNN_REQ_STS", BIT(11)}, + {"DTS_VNN_REQ_STS", BIT(12)}, + {"SMBUS_VNN_REQ_STS", BIT(14)}, + {"CSME_VNN_REQ_STS", BIT(15)}, + {"SMLINK0_VNN_REQ_STS", BIT(16)}, + {"SMLINK1_VNN_REQ_STS", BIT(17)}, + {"CLINK_VNN_REQ_STS", BIT(20)}, + {"DCI_VNN_REQ_STS", BIT(21)}, + {"ITH_VNN_REQ_STS", BIT(22)}, + {"CSME_VNN_REQ_STS", BIT(24)}, + {"GBE_VNN_REQ_STS", BIT(25)}, + {} +}; + +const struct pmc_bit_map tgl_vnn_misc_status_map[] = { + {"CPU_C10_REQ_STS_0", BIT(0)}, + {"PCIe_LPM_En_REQ_STS_3", BIT(3)}, + {"ITH_REQ_STS_5", BIT(5)}, + {"CNVI_REQ_STS_6", BIT(6)}, + {"ISH_REQ_STS_7", BIT(7)}, + {"USB2_SUS_PG_Sys_REQ_STS_10", BIT(10)}, + {"PCIe_Clk_REQ_STS_12", BIT(12)}, + {"MPHY_Core_DL_REQ_STS_16", BIT(16)}, + {"Break-even_En_REQ_STS_17", BIT(17)}, + {"Auto-demo_En_REQ_STS_18", BIT(18)}, + {"MPHY_SUS_REQ_STS_22", BIT(22)}, + {"xDCI_attached_REQ_STS_24", BIT(24)}, + {} +}; + +const struct pmc_bit_map tgl_signal_status_map[] = { + {"LSX_Wake0_En_STS", BIT(0)}, + {"LSX_Wake0_Pol_STS", BIT(1)}, + {"LSX_Wake1_En_STS", BIT(2)}, + {"LSX_Wake1_Pol_STS", BIT(3)}, + {"LSX_Wake2_En_STS", BIT(4)}, + {"LSX_Wake2_Pol_STS", BIT(5)}, + {"LSX_Wake3_En_STS", BIT(6)}, + {"LSX_Wake3_Pol_STS", BIT(7)}, + {"LSX_Wake4_En_STS", BIT(8)}, + {"LSX_Wake4_Pol_STS", BIT(9)}, + {"LSX_Wake5_En_STS", BIT(10)}, + {"LSX_Wake5_Pol_STS", BIT(11)}, + {"LSX_Wake6_En_STS", BIT(12)}, + {"LSX_Wake6_Pol_STS", BIT(13)}, + {"LSX_Wake7_En_STS", BIT(14)}, + {"LSX_Wake7_Pol_STS", BIT(15)}, + {"Intel_Se_IO_Wake0_En_STS", BIT(16)}, + {"Intel_Se_IO_Wake0_Pol_STS", BIT(17)}, + {"Intel_Se_IO_Wake1_En_STS", BIT(18)}, + {"Intel_Se_IO_Wake1_Pol_STS", BIT(19)}, + {"Int_Timer_SS_Wake0_En_STS", BIT(20)}, + {"Int_Timer_SS_Wake0_Pol_STS", BIT(21)}, + {"Int_Timer_SS_Wake1_En_STS", BIT(22)}, + {"Int_Timer_SS_Wake1_Pol_STS", BIT(23)}, + {"Int_Timer_SS_Wake2_En_STS", BIT(24)}, + {"Int_Timer_SS_Wake2_Pol_STS", BIT(25)}, + {"Int_Timer_SS_Wake3_En_STS", BIT(26)}, + {"Int_Timer_SS_Wake3_Pol_STS", BIT(27)}, + {"Int_Timer_SS_Wake4_En_STS", BIT(28)}, + {"Int_Timer_SS_Wake4_Pol_STS", BIT(29)}, + {"Int_Timer_SS_Wake5_En_STS", BIT(30)}, + {"Int_Timer_SS_Wake5_Pol_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map *tgl_lpm_maps[] = { + tgl_clocksource_status_map, + tgl_power_gating_status_map, + tgl_d3_status_map, + tgl_vnn_req_status_map, + tgl_vnn_misc_status_map, + tgl_signal_status_map, + NULL +}; + +const struct pmc_reg_map tgl_reg_map = { + .pfear_sts = ext_tgl_pfear_map, + .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, + .ltr_show_sts = cnp_ltr_show_map, + .msr_sts = msr_map, + .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, + .regmap_length = CNP_PMC_MMIO_REG_LEN, + .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, + .ppfear_buckets = ICL_PPFEAR_NUM_ENTRIES, + .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, + .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, + .ltr_ignore_max = TGL_NUM_IP_IGN_ALLOWED, + .lpm_num_maps = TGL_LPM_NUM_MAPS, + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .lpm_sts_latch_en_offset = TGL_LPM_STS_LATCH_EN_OFFSET, + .lpm_en_offset = TGL_LPM_EN_OFFSET, + .lpm_priority_offset = TGL_LPM_PRI_OFFSET, + .lpm_residency_offset = TGL_LPM_RESIDENCY_OFFSET, + .lpm_sts = tgl_lpm_maps, + .lpm_status_offset = TGL_LPM_STATUS_OFFSET, + .lpm_live_status_offset = TGL_LPM_LIVE_STATUS_OFFSET, + .etr3_offset = ETR3_OFFSET, +}; + +void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev) +{ + struct pmc_dev *pmcdev = platform_get_drvdata(pdev); + const int num_maps = pmcdev->map->lpm_num_maps; + u32 lpm_size = LPM_MAX_NUM_MODES * num_maps * 4; + union acpi_object *out_obj; + struct acpi_device *adev; + guid_t s0ix_dsm_guid; + u32 *lpm_req_regs, *addr; + + adev = ACPI_COMPANION(&pdev->dev); + if (!adev) + return; + + guid_parse(ACPI_S0IX_DSM_UUID, &s0ix_dsm_guid); + + out_obj = acpi_evaluate_dsm(adev->handle, &s0ix_dsm_guid, 0, + ACPI_GET_LOW_MODE_REGISTERS, NULL); + if (out_obj && out_obj->type == ACPI_TYPE_BUFFER) { + u32 size = out_obj->buffer.length; + + if (size != lpm_size) { + acpi_handle_debug(adev->handle, + "_DSM returned unexpected buffer size, have %u, expect %u\n", + size, lpm_size); + goto free_acpi_obj; + } + } else { + acpi_handle_debug(adev->handle, + "_DSM function 0 evaluation failed\n"); + goto free_acpi_obj; + } + + addr = (u32 *)out_obj->buffer.pointer; + + lpm_req_regs = devm_kzalloc(&pdev->dev, lpm_size * sizeof(u32), + GFP_KERNEL); + if (!lpm_req_regs) + goto free_acpi_obj; + + memcpy(lpm_req_regs, addr, lpm_size); + pmcdev->lpm_req_regs = lpm_req_regs; + +free_acpi_obj: + ACPI_FREE(out_obj); +} + +void tgl_core_configure(struct pmc_dev *pmcdev) +{ + pmc_core_get_tgl_lpm_reqs(pmcdev->pdev); + /* Due to a hardware limitation, the GBE LTR blocks PC10 + * when a cable is attached. Tell the PMC to ignore it. + */ + dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); + pmc_core_send_ltr_ignore(pmcdev, 3); +} + +void tgl_core_init(struct pmc_dev *pmcdev) +{ + pmcdev->map = &tgl_reg_map; + pmcdev->core_configure = tgl_core_configure; +} -- cgit v1.2.3-58-ga151 From 08876884b9eac0666a510ef4f7e7a7c2d377aae5 Mon Sep 17 00:00:00 2001 From: Gayatri Kammela Date: Mon, 14 Nov 2022 10:32:56 -0800 Subject: platform/x86: intel/pmc: Relocate Alder Lake PCH support Create adl.c for Alder Lake PCH specific structures and init(). This file supports Alder Lake, Raptor Lake and Raptor Lake S platforms There are no functional changes involved. Cc: David E Box Reviewed-by: "David E. Box" Signed-off-by: Gayatri Kammela Signed-off-by: "David E. Box" Link: https://lore.kernel.org/r/20221114183257.2067662-8-gayatri.kammela@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/Makefile | 3 +- drivers/platform/x86/intel/pmc/adl.c | 325 ++++++++++++++++++++++++++++++++ drivers/platform/x86/intel/pmc/core.c | 315 ------------------------------- drivers/platform/x86/intel/pmc/core.h | 18 ++ 4 files changed, 345 insertions(+), 316 deletions(-) create mode 100644 drivers/platform/x86/intel/pmc/adl.c (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile index ad34c8255584..0135918ea718 100644 --- a/drivers/platform/x86/intel/pmc/Makefile +++ b/drivers/platform/x86/intel/pmc/Makefile @@ -3,7 +3,8 @@ # Intel x86 Platform-Specific Drivers # -intel_pmc_core-y := core.o spt.o cnp.o icl.o tgl.o +intel_pmc_core-y := core.o spt.o cnp.o icl.o tgl.o \ + adl.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv-y := pltdrv.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o diff --git a/drivers/platform/x86/intel/pmc/adl.c b/drivers/platform/x86/intel/pmc/adl.c new file mode 100644 index 000000000000..5cbd40979f2a --- /dev/null +++ b/drivers/platform/x86/intel/pmc/adl.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file contains platform specific structure definitions + * and init function used by Alder Lake PCH. + * + * Copyright (c) 2022, Intel Corporation. + * All Rights Reserved. + * + */ + +#include "core.h" + +/* Alder Lake: PGD PFET Enable Ack Status Register(s) bitmap */ +const struct pmc_bit_map adl_pfear_map[] = { + {"SPI/eSPI", BIT(2)}, + {"XHCI", BIT(3)}, + {"SPA", BIT(4)}, + {"SPB", BIT(5)}, + {"SPC", BIT(6)}, + {"GBE", BIT(7)}, + + {"SATA", BIT(0)}, + {"HDA_PGD0", BIT(1)}, + {"HDA_PGD1", BIT(2)}, + {"HDA_PGD2", BIT(3)}, + {"HDA_PGD3", BIT(4)}, + {"SPD", BIT(5)}, + {"LPSS", BIT(6)}, + + {"SMB", BIT(0)}, + {"ISH", BIT(1)}, + {"ITH", BIT(3)}, + + {"XDCI", BIT(1)}, + {"DCI", BIT(2)}, + {"CSE", BIT(3)}, + {"CSME_KVM", BIT(4)}, + {"CSME_PMT", BIT(5)}, + {"CSME_CLINK", BIT(6)}, + {"CSME_PTIO", BIT(7)}, + + {"CSME_USBR", BIT(0)}, + {"CSME_SUSRAM", BIT(1)}, + {"CSME_SMT1", BIT(2)}, + {"CSME_SMS2", BIT(4)}, + {"CSME_SMS1", BIT(5)}, + {"CSME_RTC", BIT(6)}, + {"CSME_PSF", BIT(7)}, + + {"CNVI", BIT(3)}, + {"HDA_PGD4", BIT(2)}, + {"HDA_PGD5", BIT(3)}, + {"HDA_PGD6", BIT(4)}, + {} +}; + +const struct pmc_bit_map *ext_adl_pfear_map[] = { + /* + * Check intel_pmc_core_ids[] users of cnp_reg_map for + * a list of core SoCs using this. + */ + adl_pfear_map, + NULL +}; + +const struct pmc_bit_map adl_ltr_show_map[] = { + {"SOUTHPORT_A", CNP_PMC_LTR_SPA}, + {"SOUTHPORT_B", CNP_PMC_LTR_SPB}, + {"SATA", CNP_PMC_LTR_SATA}, + {"GIGABIT_ETHERNET", CNP_PMC_LTR_GBE}, + {"XHCI", CNP_PMC_LTR_XHCI}, + {"SOUTHPORT_F", ADL_PMC_LTR_SPF}, + {"ME", CNP_PMC_LTR_ME}, + /* EVA is Enterprise Value Add, doesn't really exist on PCH */ + {"SATA1", CNP_PMC_LTR_EVA}, + {"SOUTHPORT_C", CNP_PMC_LTR_SPC}, + {"HD_AUDIO", CNP_PMC_LTR_AZ}, + {"CNV", CNP_PMC_LTR_CNV}, + {"LPSS", CNP_PMC_LTR_LPSS}, + {"SOUTHPORT_D", CNP_PMC_LTR_SPD}, + {"SOUTHPORT_E", CNP_PMC_LTR_SPE}, + {"SATA2", CNP_PMC_LTR_CAM}, + {"ESPI", CNP_PMC_LTR_ESPI}, + {"SCC", CNP_PMC_LTR_SCC}, + {"ISH", CNP_PMC_LTR_ISH}, + {"UFSX2", CNP_PMC_LTR_UFSX2}, + {"EMMC", CNP_PMC_LTR_EMMC}, + /* + * Check intel_pmc_core_ids[] users of cnp_reg_map for + * a list of core SoCs using this. + */ + {"WIGIG", ICL_PMC_LTR_WIGIG}, + {"THC0", TGL_PMC_LTR_THC0}, + {"THC1", TGL_PMC_LTR_THC1}, + {"SOUTHPORT_G", CNP_PMC_LTR_RESERVED}, + + /* Below two cannot be used for LTR_IGNORE */ + {"CURRENT_PLATFORM", CNP_PMC_LTR_CUR_PLT}, + {"AGGREGATED_SYSTEM", CNP_PMC_LTR_CUR_ASLT}, + {} +}; + +const struct pmc_bit_map adl_clocksource_status_map[] = { + {"CLKPART1_OFF_STS", BIT(0)}, + {"CLKPART2_OFF_STS", BIT(1)}, + {"CLKPART3_OFF_STS", BIT(2)}, + {"CLKPART4_OFF_STS", BIT(3)}, + {"CLKPART5_OFF_STS", BIT(4)}, + {"CLKPART6_OFF_STS", BIT(5)}, + {"CLKPART7_OFF_STS", BIT(6)}, + {"CLKPART8_OFF_STS", BIT(7)}, + {"PCIE0PLL_OFF_STS", BIT(10)}, + {"PCIE1PLL_OFF_STS", BIT(11)}, + {"PCIE2PLL_OFF_STS", BIT(12)}, + {"PCIE3PLL_OFF_STS", BIT(13)}, + {"PCIE4PLL_OFF_STS", BIT(14)}, + {"PCIE5PLL_OFF_STS", BIT(15)}, + {"PCIE6PLL_OFF_STS", BIT(16)}, + {"USB2PLL_OFF_STS", BIT(18)}, + {"OCPLL_OFF_STS", BIT(22)}, + {"AUDIOPLL_OFF_STS", BIT(23)}, + {"GBEPLL_OFF_STS", BIT(24)}, + {"Fast_XTAL_Osc_OFF_STS", BIT(25)}, + {"AC_Ring_Osc_OFF_STS", BIT(26)}, + {"MC_Ring_Osc_OFF_STS", BIT(27)}, + {"SATAPLL_OFF_STS", BIT(29)}, + {"USB3PLL_OFF_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map adl_power_gating_status_0_map[] = { + {"PMC_PGD0_PG_STS", BIT(0)}, + {"DMI_PGD0_PG_STS", BIT(1)}, + {"ESPISPI_PGD0_PG_STS", BIT(2)}, + {"XHCI_PGD0_PG_STS", BIT(3)}, + {"SPA_PGD0_PG_STS", BIT(4)}, + {"SPB_PGD0_PG_STS", BIT(5)}, + {"SPC_PGD0_PG_STS", BIT(6)}, + {"GBE_PGD0_PG_STS", BIT(7)}, + {"SATA_PGD0_PG_STS", BIT(8)}, + {"DSP_PGD0_PG_STS", BIT(9)}, + {"DSP_PGD1_PG_STS", BIT(10)}, + {"DSP_PGD2_PG_STS", BIT(11)}, + {"DSP_PGD3_PG_STS", BIT(12)}, + {"SPD_PGD0_PG_STS", BIT(13)}, + {"LPSS_PGD0_PG_STS", BIT(14)}, + {"SMB_PGD0_PG_STS", BIT(16)}, + {"ISH_PGD0_PG_STS", BIT(17)}, + {"NPK_PGD0_PG_STS", BIT(19)}, + {"PECI_PGD0_PG_STS", BIT(21)}, + {"XDCI_PGD0_PG_STS", BIT(25)}, + {"EXI_PGD0_PG_STS", BIT(26)}, + {"CSE_PGD0_PG_STS", BIT(27)}, + {"KVMCC_PGD0_PG_STS", BIT(28)}, + {"PMT_PGD0_PG_STS", BIT(29)}, + {"CLINK_PGD0_PG_STS", BIT(30)}, + {"PTIO_PGD0_PG_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map adl_power_gating_status_1_map[] = { + {"USBR0_PGD0_PG_STS", BIT(0)}, + {"SMT1_PGD0_PG_STS", BIT(2)}, + {"CSMERTC_PGD0_PG_STS", BIT(6)}, + {"CSMEPSF_PGD0_PG_STS", BIT(7)}, + {"CNVI_PGD0_PG_STS", BIT(19)}, + {"DSP_PGD4_PG_STS", BIT(26)}, + {"SPG_PGD0_PG_STS", BIT(27)}, + {"SPE_PGD0_PG_STS", BIT(28)}, + {} +}; + +const struct pmc_bit_map adl_power_gating_status_2_map[] = { + {"THC0_PGD0_PG_STS", BIT(7)}, + {"THC1_PGD0_PG_STS", BIT(8)}, + {"SPF_PGD0_PG_STS", BIT(14)}, + {} +}; + +const struct pmc_bit_map adl_d3_status_0_map[] = { + {"ISH_D3_STS", BIT(2)}, + {"LPSS_D3_STS", BIT(3)}, + {"XDCI_D3_STS", BIT(4)}, + {"XHCI_D3_STS", BIT(5)}, + {"SPA_D3_STS", BIT(12)}, + {"SPB_D3_STS", BIT(13)}, + {"SPC_D3_STS", BIT(14)}, + {"SPD_D3_STS", BIT(15)}, + {"SPE_D3_STS", BIT(16)}, + {"DSP_D3_STS", BIT(19)}, + {"SATA_D3_STS", BIT(20)}, + {"DMI_D3_STS", BIT(22)}, + {} +}; + +const struct pmc_bit_map adl_d3_status_1_map[] = { + {"GBE_D3_STS", BIT(19)}, + {"CNVI_D3_STS", BIT(27)}, + {} +}; + +const struct pmc_bit_map adl_d3_status_2_map[] = { + {"CSMERTC_D3_STS", BIT(1)}, + {"CSE_D3_STS", BIT(4)}, + {"KVMCC_D3_STS", BIT(5)}, + {"USBR0_D3_STS", BIT(6)}, + {"SMT1_D3_STS", BIT(8)}, + {"PTIO_D3_STS", BIT(16)}, + {"PMT_D3_STS", BIT(17)}, + {} +}; + +const struct pmc_bit_map adl_d3_status_3_map[] = { + {"THC0_D3_STS", BIT(14)}, + {"THC1_D3_STS", BIT(15)}, + {} +}; + +const struct pmc_bit_map adl_vnn_req_status_0_map[] = { + {"ISH_VNN_REQ_STS", BIT(2)}, + {"ESPISPI_VNN_REQ_STS", BIT(18)}, + {"DSP_VNN_REQ_STS", BIT(19)}, + {} +}; + +const struct pmc_bit_map adl_vnn_req_status_1_map[] = { + {"NPK_VNN_REQ_STS", BIT(4)}, + {"EXI_VNN_REQ_STS", BIT(9)}, + {"GBE_VNN_REQ_STS", BIT(19)}, + {"SMB_VNN_REQ_STS", BIT(25)}, + {"CNVI_VNN_REQ_STS", BIT(27)}, + {} +}; + +const struct pmc_bit_map adl_vnn_req_status_2_map[] = { + {"CSMERTC_VNN_REQ_STS", BIT(1)}, + {"CSE_VNN_REQ_STS", BIT(4)}, + {"SMT1_VNN_REQ_STS", BIT(8)}, + {"CLINK_VNN_REQ_STS", BIT(14)}, + {"GPIOCOM4_VNN_REQ_STS", BIT(20)}, + {"GPIOCOM3_VNN_REQ_STS", BIT(21)}, + {"GPIOCOM2_VNN_REQ_STS", BIT(22)}, + {"GPIOCOM1_VNN_REQ_STS", BIT(23)}, + {"GPIOCOM0_VNN_REQ_STS", BIT(24)}, + {} +}; + +const struct pmc_bit_map adl_vnn_req_status_3_map[] = { + {"GPIOCOM5_VNN_REQ_STS", BIT(11)}, + {} +}; + +const struct pmc_bit_map adl_vnn_misc_status_map[] = { + {"CPU_C10_REQ_STS", BIT(0)}, + {"PCIe_LPM_En_REQ_STS", BIT(3)}, + {"ITH_REQ_STS", BIT(5)}, + {"CNVI_REQ_STS", BIT(6)}, + {"ISH_REQ_STS", BIT(7)}, + {"USB2_SUS_PG_Sys_REQ_STS", BIT(10)}, + {"PCIe_Clk_REQ_STS", BIT(12)}, + {"MPHY_Core_DL_REQ_STS", BIT(16)}, + {"Break-even_En_REQ_STS", BIT(17)}, + {"MPHY_SUS_REQ_STS", BIT(22)}, + {"xDCI_attached_REQ_STS", BIT(24)}, + {} +}; + +const struct pmc_bit_map *adl_lpm_maps[] = { + adl_clocksource_status_map, + adl_power_gating_status_0_map, + adl_power_gating_status_1_map, + adl_power_gating_status_2_map, + adl_d3_status_0_map, + adl_d3_status_1_map, + adl_d3_status_2_map, + adl_d3_status_3_map, + adl_vnn_req_status_0_map, + adl_vnn_req_status_1_map, + adl_vnn_req_status_2_map, + adl_vnn_req_status_3_map, + adl_vnn_misc_status_map, + tgl_signal_status_map, + NULL +}; + +const struct pmc_reg_map adl_reg_map = { + .pfear_sts = ext_adl_pfear_map, + .slp_s0_offset = ADL_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, + .ltr_show_sts = adl_ltr_show_map, + .msr_sts = msr_map, + .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, + .regmap_length = CNP_PMC_MMIO_REG_LEN, + .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, + .ppfear_buckets = CNP_PPFEAR_NUM_ENTRIES, + .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, + .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, + .ltr_ignore_max = ADL_NUM_IP_IGN_ALLOWED, + .lpm_num_modes = ADL_LPM_NUM_MODES, + .lpm_num_maps = ADL_LPM_NUM_MAPS, + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .etr3_offset = ETR3_OFFSET, + .lpm_sts_latch_en_offset = ADL_LPM_STATUS_LATCH_EN_OFFSET, + .lpm_priority_offset = ADL_LPM_PRI_OFFSET, + .lpm_en_offset = ADL_LPM_EN_OFFSET, + .lpm_residency_offset = ADL_LPM_RESIDENCY_OFFSET, + .lpm_sts = adl_lpm_maps, + .lpm_status_offset = ADL_LPM_STATUS_OFFSET, + .lpm_live_status_offset = ADL_LPM_LIVE_STATUS_OFFSET, +}; + +void adl_core_configure(struct pmc_dev *pmcdev) +{ + /* Due to a hardware limitation, the GBE LTR blocks PC10 + * when a cable is attached. Tell the PMC to ignore it. + */ + dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); + pmc_core_send_ltr_ignore(pmcdev, 3); +} + +void adl_core_init(struct pmc_dev *pmcdev) +{ + pmcdev->map = &adl_reg_map; + pmcdev->core_configure = adl_core_configure; +} diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 4ff176eabe3f..14dfeac73521 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -53,306 +53,6 @@ const struct pmc_bit_map msr_map[] = { {} }; -/* Alder Lake: PGD PFET Enable Ack Status Register(s) bitmap */ -static const struct pmc_bit_map adl_pfear_map[] = { - {"SPI/eSPI", BIT(2)}, - {"XHCI", BIT(3)}, - {"SPA", BIT(4)}, - {"SPB", BIT(5)}, - {"SPC", BIT(6)}, - {"GBE", BIT(7)}, - - {"SATA", BIT(0)}, - {"HDA_PGD0", BIT(1)}, - {"HDA_PGD1", BIT(2)}, - {"HDA_PGD2", BIT(3)}, - {"HDA_PGD3", BIT(4)}, - {"SPD", BIT(5)}, - {"LPSS", BIT(6)}, - - {"SMB", BIT(0)}, - {"ISH", BIT(1)}, - {"ITH", BIT(3)}, - - {"XDCI", BIT(1)}, - {"DCI", BIT(2)}, - {"CSE", BIT(3)}, - {"CSME_KVM", BIT(4)}, - {"CSME_PMT", BIT(5)}, - {"CSME_CLINK", BIT(6)}, - {"CSME_PTIO", BIT(7)}, - - {"CSME_USBR", BIT(0)}, - {"CSME_SUSRAM", BIT(1)}, - {"CSME_SMT1", BIT(2)}, - {"CSME_SMS2", BIT(4)}, - {"CSME_SMS1", BIT(5)}, - {"CSME_RTC", BIT(6)}, - {"CSME_PSF", BIT(7)}, - - {"CNVI", BIT(3)}, - - {"HDA_PGD4", BIT(2)}, - {"HDA_PGD5", BIT(3)}, - {"HDA_PGD6", BIT(4)}, - {} -}; - -static const struct pmc_bit_map *ext_adl_pfear_map[] = { - /* - * Check intel_pmc_core_ids[] users of cnp_reg_map for - * a list of core SoCs using this. - */ - adl_pfear_map, - NULL -}; - -static const struct pmc_bit_map adl_ltr_show_map[] = { - {"SOUTHPORT_A", CNP_PMC_LTR_SPA}, - {"SOUTHPORT_B", CNP_PMC_LTR_SPB}, - {"SATA", CNP_PMC_LTR_SATA}, - {"GIGABIT_ETHERNET", CNP_PMC_LTR_GBE}, - {"XHCI", CNP_PMC_LTR_XHCI}, - {"SOUTHPORT_F", ADL_PMC_LTR_SPF}, - {"ME", CNP_PMC_LTR_ME}, - /* EVA is Enterprise Value Add, doesn't really exist on PCH */ - {"SATA1", CNP_PMC_LTR_EVA}, - {"SOUTHPORT_C", CNP_PMC_LTR_SPC}, - {"HD_AUDIO", CNP_PMC_LTR_AZ}, - {"CNV", CNP_PMC_LTR_CNV}, - {"LPSS", CNP_PMC_LTR_LPSS}, - {"SOUTHPORT_D", CNP_PMC_LTR_SPD}, - {"SOUTHPORT_E", CNP_PMC_LTR_SPE}, - {"SATA2", CNP_PMC_LTR_CAM}, - {"ESPI", CNP_PMC_LTR_ESPI}, - {"SCC", CNP_PMC_LTR_SCC}, - {"ISH", CNP_PMC_LTR_ISH}, - {"UFSX2", CNP_PMC_LTR_UFSX2}, - {"EMMC", CNP_PMC_LTR_EMMC}, - /* - * Check intel_pmc_core_ids[] users of cnp_reg_map for - * a list of core SoCs using this. - */ - {"WIGIG", ICL_PMC_LTR_WIGIG}, - {"THC0", TGL_PMC_LTR_THC0}, - {"THC1", TGL_PMC_LTR_THC1}, - {"SOUTHPORT_G", CNP_PMC_LTR_RESERVED}, - - /* Below two cannot be used for LTR_IGNORE */ - {"CURRENT_PLATFORM", CNP_PMC_LTR_CUR_PLT}, - {"AGGREGATED_SYSTEM", CNP_PMC_LTR_CUR_ASLT}, - {} -}; - -static const struct pmc_bit_map adl_clocksource_status_map[] = { - {"CLKPART1_OFF_STS", BIT(0)}, - {"CLKPART2_OFF_STS", BIT(1)}, - {"CLKPART3_OFF_STS", BIT(2)}, - {"CLKPART4_OFF_STS", BIT(3)}, - {"CLKPART5_OFF_STS", BIT(4)}, - {"CLKPART6_OFF_STS", BIT(5)}, - {"CLKPART7_OFF_STS", BIT(6)}, - {"CLKPART8_OFF_STS", BIT(7)}, - {"PCIE0PLL_OFF_STS", BIT(10)}, - {"PCIE1PLL_OFF_STS", BIT(11)}, - {"PCIE2PLL_OFF_STS", BIT(12)}, - {"PCIE3PLL_OFF_STS", BIT(13)}, - {"PCIE4PLL_OFF_STS", BIT(14)}, - {"PCIE5PLL_OFF_STS", BIT(15)}, - {"PCIE6PLL_OFF_STS", BIT(16)}, - {"USB2PLL_OFF_STS", BIT(18)}, - {"OCPLL_OFF_STS", BIT(22)}, - {"AUDIOPLL_OFF_STS", BIT(23)}, - {"GBEPLL_OFF_STS", BIT(24)}, - {"Fast_XTAL_Osc_OFF_STS", BIT(25)}, - {"AC_Ring_Osc_OFF_STS", BIT(26)}, - {"MC_Ring_Osc_OFF_STS", BIT(27)}, - {"SATAPLL_OFF_STS", BIT(29)}, - {"USB3PLL_OFF_STS", BIT(31)}, - {} -}; - -static const struct pmc_bit_map adl_power_gating_status_0_map[] = { - {"PMC_PGD0_PG_STS", BIT(0)}, - {"DMI_PGD0_PG_STS", BIT(1)}, - {"ESPISPI_PGD0_PG_STS", BIT(2)}, - {"XHCI_PGD0_PG_STS", BIT(3)}, - {"SPA_PGD0_PG_STS", BIT(4)}, - {"SPB_PGD0_PG_STS", BIT(5)}, - {"SPC_PGD0_PG_STS", BIT(6)}, - {"GBE_PGD0_PG_STS", BIT(7)}, - {"SATA_PGD0_PG_STS", BIT(8)}, - {"DSP_PGD0_PG_STS", BIT(9)}, - {"DSP_PGD1_PG_STS", BIT(10)}, - {"DSP_PGD2_PG_STS", BIT(11)}, - {"DSP_PGD3_PG_STS", BIT(12)}, - {"SPD_PGD0_PG_STS", BIT(13)}, - {"LPSS_PGD0_PG_STS", BIT(14)}, - {"SMB_PGD0_PG_STS", BIT(16)}, - {"ISH_PGD0_PG_STS", BIT(17)}, - {"NPK_PGD0_PG_STS", BIT(19)}, - {"PECI_PGD0_PG_STS", BIT(21)}, - {"XDCI_PGD0_PG_STS", BIT(25)}, - {"EXI_PGD0_PG_STS", BIT(26)}, - {"CSE_PGD0_PG_STS", BIT(27)}, - {"KVMCC_PGD0_PG_STS", BIT(28)}, - {"PMT_PGD0_PG_STS", BIT(29)}, - {"CLINK_PGD0_PG_STS", BIT(30)}, - {"PTIO_PGD0_PG_STS", BIT(31)}, - {} -}; - -static const struct pmc_bit_map adl_power_gating_status_1_map[] = { - {"USBR0_PGD0_PG_STS", BIT(0)}, - {"SMT1_PGD0_PG_STS", BIT(2)}, - {"CSMERTC_PGD0_PG_STS", BIT(6)}, - {"CSMEPSF_PGD0_PG_STS", BIT(7)}, - {"CNVI_PGD0_PG_STS", BIT(19)}, - {"DSP_PGD4_PG_STS", BIT(26)}, - {"SPG_PGD0_PG_STS", BIT(27)}, - {"SPE_PGD0_PG_STS", BIT(28)}, - {} -}; - -static const struct pmc_bit_map adl_power_gating_status_2_map[] = { - {"THC0_PGD0_PG_STS", BIT(7)}, - {"THC1_PGD0_PG_STS", BIT(8)}, - {"SPF_PGD0_PG_STS", BIT(14)}, - {} -}; - -static const struct pmc_bit_map adl_d3_status_0_map[] = { - {"ISH_D3_STS", BIT(2)}, - {"LPSS_D3_STS", BIT(3)}, - {"XDCI_D3_STS", BIT(4)}, - {"XHCI_D3_STS", BIT(5)}, - {"SPA_D3_STS", BIT(12)}, - {"SPB_D3_STS", BIT(13)}, - {"SPC_D3_STS", BIT(14)}, - {"SPD_D3_STS", BIT(15)}, - {"SPE_D3_STS", BIT(16)}, - {"DSP_D3_STS", BIT(19)}, - {"SATA_D3_STS", BIT(20)}, - {"DMI_D3_STS", BIT(22)}, - {} -}; - -static const struct pmc_bit_map adl_d3_status_1_map[] = { - {"GBE_D3_STS", BIT(19)}, - {"CNVI_D3_STS", BIT(27)}, - {} -}; - -static const struct pmc_bit_map adl_d3_status_2_map[] = { - {"CSMERTC_D3_STS", BIT(1)}, - {"CSE_D3_STS", BIT(4)}, - {"KVMCC_D3_STS", BIT(5)}, - {"USBR0_D3_STS", BIT(6)}, - {"SMT1_D3_STS", BIT(8)}, - {"PTIO_D3_STS", BIT(16)}, - {"PMT_D3_STS", BIT(17)}, - {} -}; - -static const struct pmc_bit_map adl_d3_status_3_map[] = { - {"THC0_D3_STS", BIT(14)}, - {"THC1_D3_STS", BIT(15)}, - {} -}; - -static const struct pmc_bit_map adl_vnn_req_status_0_map[] = { - {"ISH_VNN_REQ_STS", BIT(2)}, - {"ESPISPI_VNN_REQ_STS", BIT(18)}, - {"DSP_VNN_REQ_STS", BIT(19)}, - {} -}; - -static const struct pmc_bit_map adl_vnn_req_status_1_map[] = { - {"NPK_VNN_REQ_STS", BIT(4)}, - {"EXI_VNN_REQ_STS", BIT(9)}, - {"GBE_VNN_REQ_STS", BIT(19)}, - {"SMB_VNN_REQ_STS", BIT(25)}, - {"CNVI_VNN_REQ_STS", BIT(27)}, - {} -}; - -static const struct pmc_bit_map adl_vnn_req_status_2_map[] = { - {"CSMERTC_VNN_REQ_STS", BIT(1)}, - {"CSE_VNN_REQ_STS", BIT(4)}, - {"SMT1_VNN_REQ_STS", BIT(8)}, - {"CLINK_VNN_REQ_STS", BIT(14)}, - {"GPIOCOM4_VNN_REQ_STS", BIT(20)}, - {"GPIOCOM3_VNN_REQ_STS", BIT(21)}, - {"GPIOCOM2_VNN_REQ_STS", BIT(22)}, - {"GPIOCOM1_VNN_REQ_STS", BIT(23)}, - {"GPIOCOM0_VNN_REQ_STS", BIT(24)}, - {} -}; - -static const struct pmc_bit_map adl_vnn_req_status_3_map[] = { - {"GPIOCOM5_VNN_REQ_STS", BIT(11)}, - {} -}; - -static const struct pmc_bit_map adl_vnn_misc_status_map[] = { - {"CPU_C10_REQ_STS", BIT(0)}, - {"PCIe_LPM_En_REQ_STS", BIT(3)}, - {"ITH_REQ_STS", BIT(5)}, - {"CNVI_REQ_STS", BIT(6)}, - {"ISH_REQ_STS", BIT(7)}, - {"USB2_SUS_PG_Sys_REQ_STS", BIT(10)}, - {"PCIe_Clk_REQ_STS", BIT(12)}, - {"MPHY_Core_DL_REQ_STS", BIT(16)}, - {"Break-even_En_REQ_STS", BIT(17)}, - {"MPHY_SUS_REQ_STS", BIT(22)}, - {"xDCI_attached_REQ_STS", BIT(24)}, - {} -}; - -static const struct pmc_bit_map *adl_lpm_maps[] = { - adl_clocksource_status_map, - adl_power_gating_status_0_map, - adl_power_gating_status_1_map, - adl_power_gating_status_2_map, - adl_d3_status_0_map, - adl_d3_status_1_map, - adl_d3_status_2_map, - adl_d3_status_3_map, - adl_vnn_req_status_0_map, - adl_vnn_req_status_1_map, - adl_vnn_req_status_2_map, - adl_vnn_req_status_3_map, - adl_vnn_misc_status_map, - tgl_signal_status_map, - NULL -}; - -static const struct pmc_reg_map adl_reg_map = { - .pfear_sts = ext_adl_pfear_map, - .slp_s0_offset = ADL_PMC_SLP_S0_RES_COUNTER_OFFSET, - .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, - .ltr_show_sts = adl_ltr_show_map, - .msr_sts = msr_map, - .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, - .regmap_length = CNP_PMC_MMIO_REG_LEN, - .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, - .ppfear_buckets = CNP_PPFEAR_NUM_ENTRIES, - .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, - .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, - .ltr_ignore_max = ADL_NUM_IP_IGN_ALLOWED, - .lpm_num_modes = ADL_LPM_NUM_MODES, - .lpm_num_maps = ADL_LPM_NUM_MAPS, - .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, - .etr3_offset = ETR3_OFFSET, - .lpm_sts_latch_en_offset = ADL_LPM_STATUS_LATCH_EN_OFFSET, - .lpm_priority_offset = ADL_LPM_PRI_OFFSET, - .lpm_en_offset = ADL_LPM_EN_OFFSET, - .lpm_residency_offset = ADL_LPM_RESIDENCY_OFFSET, - .lpm_sts = adl_lpm_maps, - .lpm_status_offset = ADL_LPM_STATUS_OFFSET, - .lpm_live_status_offset = ADL_LPM_LIVE_STATUS_OFFSET, -}; - static inline u32 pmc_core_reg_read(struct pmc_dev *pmcdev, int reg_offset) { return readl(pmcdev->regbase + reg_offset); @@ -1303,21 +1003,6 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev) } } -void adl_core_configure(struct pmc_dev *pmcdev) -{ - /* Due to a hardware limitation, the GBE LTR blocks PC10 - * when a cable is attached. Tell the PMC to ignore it. - */ - dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); - pmc_core_send_ltr_ignore(pmcdev, 3); -} - -void adl_core_init(struct pmc_dev *pmcdev) -{ - pmcdev->map = &adl_reg_map; - pmcdev->core_configure = adl_core_configure; -} - static const struct x86_cpu_id intel_pmc_core_ids[] = { X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, spt_core_init), X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, spt_core_init), diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 38fca2a54d23..129c0c98f1a6 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -365,6 +365,24 @@ extern const struct pmc_bit_map tgl_vnn_misc_status_map[]; extern const struct pmc_bit_map tgl_signal_status_map[]; extern const struct pmc_bit_map *tgl_lpm_maps[]; extern const struct pmc_reg_map tgl_reg_map; +extern const struct pmc_bit_map adl_pfear_map[]; +extern const struct pmc_bit_map *ext_adl_pfear_map[]; +extern const struct pmc_bit_map adl_ltr_show_map[]; +extern const struct pmc_bit_map adl_clocksource_status_map[]; +extern const struct pmc_bit_map adl_power_gating_status_0_map[]; +extern const struct pmc_bit_map adl_power_gating_status_1_map[]; +extern const struct pmc_bit_map adl_power_gating_status_2_map[]; +extern const struct pmc_bit_map adl_d3_status_0_map[]; +extern const struct pmc_bit_map adl_d3_status_1_map[]; +extern const struct pmc_bit_map adl_d3_status_2_map[]; +extern const struct pmc_bit_map adl_d3_status_3_map[]; +extern const struct pmc_bit_map adl_vnn_req_status_0_map[]; +extern const struct pmc_bit_map adl_vnn_req_status_1_map[]; +extern const struct pmc_bit_map adl_vnn_req_status_2_map[]; +extern const struct pmc_bit_map adl_vnn_req_status_3_map[]; +extern const struct pmc_bit_map adl_vnn_misc_status_map[]; +extern const struct pmc_bit_map *adl_lpm_maps[]; +extern const struct pmc_reg_map adl_reg_map; extern void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev); extern int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value); -- cgit v1.2.3-58-ga151 From c5ad454a12c6a6d08dd67c971a0b12adc9953304 Mon Sep 17 00:00:00 2001 From: Gayatri Kammela Date: Mon, 14 Nov 2022 10:32:57 -0800 Subject: platform/x86: intel/pmc/core: Add Meteor Lake support to pmc core driver Add Meteor Lake client and mobile support to pmc core driver. This patch adds legacy support. Cc: David E Box Suggested-by: David E Box Reviewed-by: "David E. Box" Signed-off-by: Sukumar Ghorai Signed-off-by: Gayatri Kammela Signed-off-by: "David E. Box" Link: https://lore.kernel.org/r/20221114183257.2067662-9-gayatri.kammela@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/Makefile | 2 +- drivers/platform/x86/intel/pmc/core.c | 7 ++++- drivers/platform/x86/intel/pmc/core.h | 13 +++++++++ drivers/platform/x86/intel/pmc/mtl.c | 52 +++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 drivers/platform/x86/intel/pmc/mtl.c (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile index 0135918ea718..f96bc2e19503 100644 --- a/drivers/platform/x86/intel/pmc/Makefile +++ b/drivers/platform/x86/intel/pmc/Makefile @@ -4,7 +4,7 @@ # intel_pmc_core-y := core.o spt.o cnp.o icl.o tgl.o \ - adl.o + adl.o mtl.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv-y := pltdrv.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 14dfeac73521..f1d802f6ec3f 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -901,7 +901,11 @@ static void pmc_core_get_low_power_modes(struct platform_device *pdev) return; lpm_en = pmc_core_reg_read(pmcdev, pmcdev->map->lpm_en_offset); - pmcdev->num_lpm_modes = hweight32(lpm_en); + /* For MTL, BIT 31 is not an lpm mode but a enable bit. + * Lower byte is enough to cover the number of lpm modes for all + * platforms and hence mask the upper 3 bytes. + */ + pmcdev->num_lpm_modes = hweight32(lpm_en & 0xFF); /* Read 32 bit LPM_PRI register */ lpm_pri = pmc_core_reg_read(pmcdev, pmcdev->map->lpm_priority_offset); @@ -1024,6 +1028,7 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = { X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, tgl_core_init), X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, adl_core_init), X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, adl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE, mtl_core_init), {} }; diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 129c0c98f1a6..810204d758ab 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -238,6 +238,16 @@ enum ppfear_regs { #define ADL_LPM_STATUS_LATCH_EN_OFFSET 0x1704 #define ADL_LPM_LIVE_STATUS_OFFSET 0x1764 +/* Meteor Lake Power Management Controller register offsets */ +#define MTL_LPM_EN_OFFSET 0x1798 +#define MTL_LPM_RESIDENCY_OFFSET 0x17A0 + +/* Meteor Lake Low Power Mode debug registers */ +#define MTL_LPM_PRI_OFFSET 0x179C +#define MTL_LPM_STATUS_LATCH_EN_OFFSET 0x16F8 +#define MTL_LPM_STATUS_OFFSET 0x1700 +#define MTL_LPM_LIVE_STATUS_OFFSET 0x175C + extern const char *pmc_lpm_modes[]; struct pmc_bit_map { @@ -383,6 +393,7 @@ extern const struct pmc_bit_map adl_vnn_req_status_3_map[]; extern const struct pmc_bit_map adl_vnn_misc_status_map[]; extern const struct pmc_bit_map *adl_lpm_maps[]; extern const struct pmc_reg_map adl_reg_map; +extern const struct pmc_reg_map mtl_reg_map; extern void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev); extern int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value); @@ -392,8 +403,10 @@ void cnp_core_init(struct pmc_dev *pmcdev); void icl_core_init(struct pmc_dev *pmcdev); void tgl_core_init(struct pmc_dev *pmcdev); void adl_core_init(struct pmc_dev *pmcdev); +void mtl_core_init(struct pmc_dev *pmcdev); void tgl_core_configure(struct pmc_dev *pmcdev); void adl_core_configure(struct pmc_dev *pmcdev); +void mtl_core_configure(struct pmc_dev *pmcdev); #define pmc_for_each_mode(i, mode, pmcdev) \ for (i = 0, mode = pmcdev->lpm_en_modes[i]; \ diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c new file mode 100644 index 000000000000..eeb3bd8c2502 --- /dev/null +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file contains platform specific structure definitions + * and init function used by Meteor Lake PCH. + * + * Copyright (c) 2022, Intel Corporation. + * All Rights Reserved. + * + */ + +#include "core.h" + +const struct pmc_reg_map mtl_reg_map = { + .pfear_sts = ext_tgl_pfear_map, + .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, + .ltr_show_sts = adl_ltr_show_map, + .msr_sts = msr_map, + .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, + .regmap_length = CNP_PMC_MMIO_REG_LEN, + .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, + .ppfear_buckets = ICL_PPFEAR_NUM_ENTRIES, + .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, + .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, + .ltr_ignore_max = ADL_NUM_IP_IGN_ALLOWED, + .lpm_num_modes = ADL_LPM_NUM_MODES, + .lpm_num_maps = ADL_LPM_NUM_MAPS, + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .etr3_offset = ETR3_OFFSET, + .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, + .lpm_priority_offset = MTL_LPM_PRI_OFFSET, + .lpm_en_offset = MTL_LPM_EN_OFFSET, + .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, + .lpm_sts = adl_lpm_maps, + .lpm_status_offset = MTL_LPM_STATUS_OFFSET, + .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, +}; + +void mtl_core_configure(struct pmc_dev *pmcdev) +{ + /* Due to a hardware limitation, the GBE LTR blocks PC10 + * when a cable is attached. Tell the PMC to ignore it. + */ + dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); + pmc_core_send_ltr_ignore(pmcdev, 3); +} + +void mtl_core_init(struct pmc_dev *pmcdev) +{ + pmcdev->map = &mtl_reg_map; + pmcdev->core_configure = mtl_core_configure; +} -- cgit v1.2.3-58-ga151 From 4ea629155c29051dd1323ab9eded21c545b50c2c Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 18 Nov 2022 16:23:35 -0800 Subject: platform/x86/intel/sdsi: Add Intel On Demand text Intel Software Defined Silicon (SDSi) is now officially known as Intel On Demand. Add On Demand to the description in the kconfig, documentation, and driver source. Signed-off-by: David E. Box Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20221119002343.1281885-2-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- Documentation/ABI/testing/sysfs-driver-intel_sdsi | 37 ++++++++++++----------- drivers/platform/x86/intel/Kconfig | 8 ++--- drivers/platform/x86/intel/sdsi.c | 4 +-- 3 files changed, 25 insertions(+), 24 deletions(-) (limited to 'drivers/platform') diff --git a/Documentation/ABI/testing/sysfs-driver-intel_sdsi b/Documentation/ABI/testing/sysfs-driver-intel_sdsi index 96b92c105ec4..9d77f30d9b9a 100644 --- a/Documentation/ABI/testing/sysfs-driver-intel_sdsi +++ b/Documentation/ABI/testing/sysfs-driver-intel_sdsi @@ -4,21 +4,21 @@ KernelVersion: 5.18 Contact: "David E. Box" Description: This directory contains interface files for accessing Intel - Software Defined Silicon (SDSi) features on a CPU. X - represents the socket instance (though not the socket ID). - The socket ID is determined by reading the registers file - and decoding it per the specification. + On Demand (formerly Software Defined Silicon or SDSi) features + on a CPU. X represents the socket instance (though not the + socket ID). The socket ID is determined by reading the + registers file and decoding it per the specification. - Some files communicate with SDSi hardware through a mailbox. - Should the operation fail, one of the following error codes - may be returned: + Some files communicate with On Demand hardware through a + mailbox. Should the operation fail, one of the following error + codes may be returned: ========== ===== Error Code Cause ========== ===== EIO General mailbox failure. Log may indicate cause. EBUSY Mailbox is owned by another agent. - EPERM SDSI capability is not enabled in hardware. + EPERM On Demand capability is not enabled in hardware. EPROTO Failure in mailbox protocol detected by driver. See log for details. EOVERFLOW For provision commands, the size of the data @@ -54,8 +54,8 @@ KernelVersion: 5.18 Contact: "David E. Box" Description: (WO) Used to write an Authentication Key Certificate (AKC) to - the SDSi NVRAM for the CPU. The AKC is used to authenticate a - Capability Activation Payload. Mailbox command. + the On Demand NVRAM for the CPU. The AKC is used to authenticate + a Capability Activation Payload. Mailbox command. What: /sys/bus/auxiliary/devices/intel_vsec.sdsi.X/provision_cap Date: Feb 2022 @@ -63,17 +63,18 @@ KernelVersion: 5.18 Contact: "David E. Box" Description: (WO) Used to write a Capability Activation Payload (CAP) to the - SDSi NVRAM for the CPU. CAPs are used to activate a given CPU - feature. A CAP is validated by SDSi hardware using a previously - provisioned AKC file. Upon successful authentication, the CPU - configuration is updated. A cold reboot is required to fully - activate the feature. Mailbox command. + On Demand NVRAM for the CPU. CAPs are used to activate a given + CPU feature. A CAP is validated by On Demand hardware using a + previously provisioned AKC file. Upon successful authentication, + the CPU configuration is updated. A cold reboot is required to + fully activate the feature. Mailbox command. What: /sys/bus/auxiliary/devices/intel_vsec.sdsi.X/state_certificate Date: Feb 2022 KernelVersion: 5.18 Contact: "David E. Box" Description: - (RO) Used to read back the current State Certificate for the CPU - from SDSi hardware. The State Certificate contains information - about the current licenses on the CPU. Mailbox command. + (RO) Used to read back the current state certificate for the CPU + from On Demand hardware. The state certificate contains + information about the current licenses on the CPU. Mailbox + command. diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig index 794968bda115..d5a33473e838 100644 --- a/drivers/platform/x86/intel/Kconfig +++ b/drivers/platform/x86/intel/Kconfig @@ -157,13 +157,13 @@ config INTEL_RST as usual. config INTEL_SDSI - tristate "Intel Software Defined Silicon Driver" + tristate "Intel On Demand (Software Defined Silicon) Driver" depends on INTEL_VSEC depends on X86_64 help - This driver enables access to the Intel Software Defined Silicon - interface used to provision silicon features with an authentication - certificate and capability license. + This driver enables access to the Intel On Demand (formerly Software + Defined Silicon) interface used to provision silicon features with an + authentication certificate and capability license. To compile this driver as a module, choose M here: the module will be called intel_sdsi. diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c index c830e98dfa38..32793919473d 100644 --- a/drivers/platform/x86/intel/sdsi.c +++ b/drivers/platform/x86/intel/sdsi.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Intel Software Defined Silicon driver + * Intel On Demand (Software Defined Silicon) driver * * Copyright (c) 2022, Intel Corporation. * All Rights Reserved. @@ -586,5 +586,5 @@ static struct auxiliary_driver sdsi_aux_driver = { module_auxiliary_driver(sdsi_aux_driver); MODULE_AUTHOR("David E. Box "); -MODULE_DESCRIPTION("Intel Software Defined Silicon driver"); +MODULE_DESCRIPTION("Intel On Demand (SDSi) driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3-58-ga151 From aa546b28163f0bd12e1129cd0fb05b89089485e9 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 18 Nov 2022 16:23:36 -0800 Subject: platform/x86/intel/sdsi: Hide attributes if hardware doesn't support Provisioning capabilities are enabled by a bit set by BIOS. Read this bit and hide the provisioning attributes if the On Demand feature is not enabled. Also, remove the sdsi_enabled boolean from private and instead add a features register since this will be used for future features. Signed-off-by: David E. Box Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20221119002343.1281885-3-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/sdsi.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c index 32793919473d..bca05b4dd983 100644 --- a/drivers/platform/x86/intel/sdsi.c +++ b/drivers/platform/x86/intel/sdsi.c @@ -41,7 +41,8 @@ #define SDSI_SIZE_READ_MSG (SDSI_SIZE_MAILBOX * 4) #define SDSI_ENABLED_FEATURES_OFFSET 16 -#define SDSI_ENABLED BIT(3) +#define SDSI_FEATURE_SDSI BIT(3) + #define SDSI_SOCKET_ID_OFFSET 64 #define SDSI_SOCKET_ID GENMASK(3, 0) @@ -100,7 +101,7 @@ struct sdsi_priv { void __iomem *mbox_addr; void __iomem *regs_addr; u32 guid; - bool sdsi_enabled; + u32 features; }; /* SDSi mailbox operations must be performed using 64bit mov instructions */ @@ -332,9 +333,6 @@ static ssize_t sdsi_provision(struct sdsi_priv *priv, char *buf, size_t count, struct sdsi_mbox_info info; int ret; - if (!priv->sdsi_enabled) - return -EPERM; - if (count > (SDSI_SIZE_WRITE_MSG - SDSI_SIZE_CMD)) return -EOVERFLOW; @@ -405,9 +403,6 @@ static long state_certificate_read(struct file *filp, struct kobject *kobj, size_t size; int ret; - if (!priv->sdsi_enabled) - return -EPERM; - if (off) return 0; @@ -464,6 +459,23 @@ static struct bin_attribute *sdsi_bin_attrs[] = { NULL }; +static umode_t +sdsi_battr_is_visible(struct kobject *kobj, struct bin_attribute *attr, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct sdsi_priv *priv = dev_get_drvdata(dev); + + /* Registers file is always readable if the device is present */ + if (attr == &bin_attr_registers) + return attr->attr.mode; + + /* All other attributes not visible if BIOS has not enabled On Demand */ + if (!(priv->features & SDSI_FEATURE_SDSI)) + return 0; + + return attr->attr.mode; +} + static ssize_t guid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sdsi_priv *priv = dev_get_drvdata(dev); @@ -480,6 +492,7 @@ static struct attribute *sdsi_attrs[] = { static const struct attribute_group sdsi_group = { .attrs = sdsi_attrs, .bin_attrs = sdsi_bin_attrs, + .is_bin_visible = sdsi_battr_is_visible, }; __ATTRIBUTE_GROUPS(sdsi); @@ -490,7 +503,6 @@ static int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct pci_dev *paren u32 size = FIELD_GET(DT_SIZE, disc_table->access_info); u32 tbir = FIELD_GET(DT_TBIR, disc_table->offset); u32 offset = DT_OFFSET(disc_table->offset); - u32 features_offset; struct resource res = {}; /* Starting location of SDSi MMIO region based on access type */ @@ -528,8 +540,7 @@ static int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct pci_dev *paren priv->mbox_addr = priv->control_addr + SDSI_SIZE_CONTROL; priv->regs_addr = priv->mbox_addr + SDSI_SIZE_MAILBOX; - features_offset = readq(priv->regs_addr + SDSI_ENABLED_FEATURES_OFFSET); - priv->sdsi_enabled = !!(features_offset & SDSI_ENABLED); + priv->features = readq(priv->regs_addr + SDSI_ENABLED_FEATURES_OFFSET); return 0; } -- cgit v1.2.3-58-ga151 From 25612c0fb22d20142363765ab99b6f87ea3c45cf Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 18 Nov 2022 16:23:37 -0800 Subject: platform/x86/intel/sdsi: Support different GUIDs Newer versions of Intel On Demand hardware may have an expanded list of registers to support new features. The register layout is identified by a unique GUID that's read during driver probe. Add support for handling different GUIDs and add support for current GUIDs [1]. Link: https://github.com/intel/intel-sdsi/blob/master/os-interface.rst [1] Signed-off-by: David E. Box Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20221119002343.1281885-4-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/sdsi.c | 50 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c index bca05b4dd983..9cd4339a7370 100644 --- a/drivers/platform/x86/intel/sdsi.c +++ b/drivers/platform/x86/intel/sdsi.c @@ -27,9 +27,8 @@ #define ACCESS_TYPE_LOCAL 3 #define SDSI_MIN_SIZE_DWORDS 276 -#define SDSI_SIZE_CONTROL 8 #define SDSI_SIZE_MAILBOX 1024 -#define SDSI_SIZE_REGS 72 +#define SDSI_SIZE_REGS 80 #define SDSI_SIZE_CMD sizeof(u64) /* @@ -76,6 +75,13 @@ #define DT_TBIR GENMASK(2, 0) #define DT_OFFSET(v) ((v) & GENMASK(31, 3)) +#define SDSI_GUID_V1 0x006DD191 +#define GUID_V1_CNTRL_SIZE 8 +#define GUID_V1_REGS_SIZE 72 +#define SDSI_GUID_V2 0xF210D9EF +#define GUID_V2_CNTRL_SIZE 16 +#define GUID_V2_REGS_SIZE 80 + enum sdsi_command { SDSI_CMD_PROVISION_AKC = 0x04, SDSI_CMD_PROVISION_CAP = 0x08, @@ -100,6 +106,9 @@ struct sdsi_priv { void __iomem *control_addr; void __iomem *mbox_addr; void __iomem *regs_addr; + int control_size; + int maibox_size; + int registers_size; u32 guid; u32 features; }; @@ -444,6 +453,18 @@ static ssize_t registers_read(struct file *filp, struct kobject *kobj, struct device *dev = kobj_to_dev(kobj); struct sdsi_priv *priv = dev_get_drvdata(dev); void __iomem *addr = priv->regs_addr; + int size = priv->registers_size; + + /* + * The check below is performed by the sysfs caller based on the static + * file size. But this may be greater than the actual size which is based + * on the GUID. So check here again based on actual size before reading. + */ + if (off >= size) + return 0; + + if (off + count > size) + count = size - off; memcpy_fromio(buf, addr + off, count); @@ -496,6 +517,24 @@ static const struct attribute_group sdsi_group = { }; __ATTRIBUTE_GROUPS(sdsi); +static int sdsi_get_layout(struct sdsi_priv *priv, struct disc_table *table) +{ + switch (table->guid) { + case SDSI_GUID_V1: + priv->control_size = GUID_V1_CNTRL_SIZE; + priv->registers_size = GUID_V1_REGS_SIZE; + break; + case SDSI_GUID_V2: + priv->control_size = GUID_V2_CNTRL_SIZE; + priv->registers_size = GUID_V2_REGS_SIZE; + break; + default: + dev_err(priv->dev, "Unrecognized GUID 0x%x\n", table->guid); + return -EINVAL; + } + return 0; +} + static int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct pci_dev *parent, struct disc_table *disc_table, struct resource *disc_res) { @@ -537,7 +576,7 @@ static int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct pci_dev *paren if (IS_ERR(priv->control_addr)) return PTR_ERR(priv->control_addr); - priv->mbox_addr = priv->control_addr + SDSI_SIZE_CONTROL; + priv->mbox_addr = priv->control_addr + priv->control_size; priv->regs_addr = priv->mbox_addr + SDSI_SIZE_MAILBOX; priv->features = readq(priv->regs_addr + SDSI_ENABLED_FEATURES_OFFSET); @@ -572,6 +611,11 @@ static int sdsi_probe(struct auxiliary_device *auxdev, const struct auxiliary_de priv->guid = disc_table.guid; + /* Get guid based layout info */ + ret = sdsi_get_layout(priv, &disc_table); + if (ret) + return ret; + /* Map the SDSi mailbox registers */ ret = sdsi_map_mbox_registers(priv, intel_cap_dev->pcidev, &disc_table, disc_res); if (ret) -- cgit v1.2.3-58-ga151 From a96f1b9c48e56bc82a8cba11020ab29c28bfab51 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 18 Nov 2022 16:23:38 -0800 Subject: platform/x86/intel/sdsi: Add meter certificate support Add support for reading the meter certificate from Intel On Demand hardware. The meter certificate [1] is used to access the utilization metrics of enabled features in support of the Intel On Demand consumption model. Similar to the state certificate, the meter certificate is read by mailbox command. While making similar changes also use the BIN_ATTR_ADMIN_RO helper to create the 'registers' sysfs file. Link: https://github.com/intel-sandbox/debox1.intel_sdsi/blob/gnr-review/meter-certificate.rst [1] Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20221119002343.1281885-5-david.e.box@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- Documentation/ABI/testing/sysfs-driver-intel_sdsi | 10 +++++ drivers/platform/x86/intel/sdsi.c | 49 ++++++++++++++++++----- 2 files changed, 48 insertions(+), 11 deletions(-) (limited to 'drivers/platform') diff --git a/Documentation/ABI/testing/sysfs-driver-intel_sdsi b/Documentation/ABI/testing/sysfs-driver-intel_sdsi index 9d77f30d9b9a..f8afed127107 100644 --- a/Documentation/ABI/testing/sysfs-driver-intel_sdsi +++ b/Documentation/ABI/testing/sysfs-driver-intel_sdsi @@ -69,6 +69,16 @@ Description: the CPU configuration is updated. A cold reboot is required to fully activate the feature. Mailbox command. +What: /sys/bus/auxiliary/devices/intel_vsec.sdsi.X/meter_certificate +Date: Nov 2022 +KernelVersion: 6.2 +Contact: "David E. Box" +Description: + (RO) Used to read back the current meter certificate for the CPU + from Intel On Demand hardware. The meter certificate contains + utilization metrics of On Demand enabled features. Mailbox + command. + What: /sys/bus/auxiliary/devices/intel_vsec.sdsi.X/state_certificate Date: Feb 2022 KernelVersion: 5.18 diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c index 9cd4339a7370..9e0ea2cdd704 100644 --- a/drivers/platform/x86/intel/sdsi.c +++ b/drivers/platform/x86/intel/sdsi.c @@ -41,6 +41,7 @@ #define SDSI_ENABLED_FEATURES_OFFSET 16 #define SDSI_FEATURE_SDSI BIT(3) +#define SDSI_FEATURE_METERING BIT(26) #define SDSI_SOCKET_ID_OFFSET 64 #define SDSI_SOCKET_ID GENMASK(3, 0) @@ -83,9 +84,10 @@ #define GUID_V2_REGS_SIZE 80 enum sdsi_command { - SDSI_CMD_PROVISION_AKC = 0x04, - SDSI_CMD_PROVISION_CAP = 0x08, - SDSI_CMD_READ_STATE = 0x10, + SDSI_CMD_PROVISION_AKC = 0x0004, + SDSI_CMD_PROVISION_CAP = 0x0008, + SDSI_CMD_READ_STATE = 0x0010, + SDSI_CMD_READ_METER = 0x0014, }; struct sdsi_mbox_info { @@ -401,13 +403,10 @@ static ssize_t provision_cap_write(struct file *filp, struct kobject *kobj, } static BIN_ATTR_WO(provision_cap, SDSI_SIZE_WRITE_MSG); -static long state_certificate_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, - size_t count) +static ssize_t +certificate_read(u64 command, struct sdsi_priv *priv, char *buf, loff_t off, + size_t count) { - struct device *dev = kobj_to_dev(kobj); - struct sdsi_priv *priv = dev_get_drvdata(dev); - u64 command = SDSI_CMD_READ_STATE; struct sdsi_mbox_info info; size_t size; int ret; @@ -444,7 +443,30 @@ free_buffer: return size; } -static BIN_ATTR(state_certificate, 0400, state_certificate_read, NULL, SDSI_SIZE_READ_MSG); + +static ssize_t +state_certificate_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, + size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct sdsi_priv *priv = dev_get_drvdata(dev); + + return certificate_read(SDSI_CMD_READ_STATE, priv, buf, off, count); +} +static BIN_ATTR_ADMIN_RO(state_certificate, SDSI_SIZE_READ_MSG); + +static ssize_t +meter_certificate_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, + size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct sdsi_priv *priv = dev_get_drvdata(dev); + + return certificate_read(SDSI_CMD_READ_METER, priv, buf, off, count); +} +static BIN_ATTR_ADMIN_RO(meter_certificate, SDSI_SIZE_READ_MSG); static ssize_t registers_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, @@ -470,11 +492,12 @@ static ssize_t registers_read(struct file *filp, struct kobject *kobj, return count; } -static BIN_ATTR(registers, 0400, registers_read, NULL, SDSI_SIZE_REGS); +static BIN_ATTR_ADMIN_RO(registers, SDSI_SIZE_REGS); static struct bin_attribute *sdsi_bin_attrs[] = { &bin_attr_registers, &bin_attr_state_certificate, + &bin_attr_meter_certificate, &bin_attr_provision_akc, &bin_attr_provision_cap, NULL @@ -494,6 +517,10 @@ sdsi_battr_is_visible(struct kobject *kobj, struct bin_attribute *attr, int n) if (!(priv->features & SDSI_FEATURE_SDSI)) return 0; + if (attr == &bin_attr_meter_certificate) + return (priv->features & SDSI_FEATURE_METERING) ? + attr->attr.mode : 0; + return attr->attr.mode; } -- cgit v1.2.3-58-ga151 From 289a59895e7a380cdc7fe2780d3073f4b9237020 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Nov 2022 12:02:39 +0100 Subject: platform/x86: ideapad-laptop: Refactor ideapad_sync_touchpad_state() Add an error exit for read_ec_data() failing instead of putting the main body in an if (success) block. Signed-off-by: Hans de Goede Reviewed-by: Jiaxun Yang Tested-by: Jiaxun Yang Tested-by: Maxim Mikityanskiy Link: https://lore.kernel.org/r/20221117110244.67811-2-hdegoede@redhat.com --- drivers/platform/x86/ideapad-laptop.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 13e3ae731fd8..dcb3a82024da 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -1490,23 +1490,26 @@ static void ideapad_kbd_bl_exit(struct ideapad_private *priv) static void ideapad_sync_touchpad_state(struct ideapad_private *priv) { unsigned long value; + unsigned char param; + int ret; if (!priv->features.touchpad_ctrl_via_ec) return; /* Without reading from EC touchpad LED doesn't switch state */ - if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) { - unsigned char param; - /* - * Some IdeaPads don't really turn off touchpad - they only - * switch the LED state. We (de)activate KBC AUX port to turn - * touchpad off and on. We send KEY_TOUCHPAD_OFF and - * KEY_TOUCHPAD_ON to not to get out of sync with LED - */ - i8042_command(¶m, value ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE); - ideapad_input_report(priv, value ? 67 : 66); - sysfs_notify(&priv->platform_device->dev.kobj, NULL, "touchpad"); - } + ret = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value); + if (ret) + return; + + /* + * Some IdeaPads don't really turn off touchpad - they only + * switch the LED state. We (de)activate KBC AUX port to turn + * touchpad off and on. We send KEY_TOUCHPAD_OFF and + * KEY_TOUCHPAD_ON to not to get out of sync with LED + */ + i8042_command(¶m, value ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE); + ideapad_input_report(priv, value ? 67 : 66); + sysfs_notify(&priv->platform_device->dev.kobj, NULL, "touchpad"); } static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) -- cgit v1.2.3-58-ga151 From f4dd8c44bb831ff885680bc77111fa39c193a93f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Nov 2022 12:02:40 +0100 Subject: platform/x86: ideapad-laptop: Do not send KEY_TOUCHPAD* events on probe / resume The sending of KEY_TOUCHPAD* events is causing spurious touchpad OSD showing on resume. Disable the sending of events on probe / resume to fix this. Signed-off-by: Hans de Goede Reviewed-by: Jiaxun Yang Tested-by: Jiaxun Yang Tested-by: Maxim Mikityanskiy Link: https://lore.kernel.org/r/20221117110244.67811-3-hdegoede@redhat.com --- drivers/platform/x86/ideapad-laptop.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index dcb3a82024da..eb0b1ec32c13 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -1487,7 +1487,7 @@ static void ideapad_kbd_bl_exit(struct ideapad_private *priv) /* * module init/exit */ -static void ideapad_sync_touchpad_state(struct ideapad_private *priv) +static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_events) { unsigned long value; unsigned char param; @@ -1508,8 +1508,11 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv) * KEY_TOUCHPAD_ON to not to get out of sync with LED */ i8042_command(¶m, value ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE); - ideapad_input_report(priv, value ? 67 : 66); - sysfs_notify(&priv->platform_device->dev.kobj, NULL, "touchpad"); + + if (send_events) { + ideapad_input_report(priv, value ? 67 : 66); + sysfs_notify(&priv->platform_device->dev.kobj, NULL, "touchpad"); + } } static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) @@ -1550,7 +1553,7 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) ideapad_sync_rfk_state(priv); break; case 5: - ideapad_sync_touchpad_state(priv); + ideapad_sync_touchpad_state(priv, true); break; case 4: ideapad_backlight_notify_brightness(priv); @@ -1840,7 +1843,7 @@ static int ideapad_acpi_add(struct platform_device *pdev) ideapad_register_rfkill(priv, i); ideapad_sync_rfk_state(priv); - ideapad_sync_touchpad_state(priv); + ideapad_sync_touchpad_state(priv, false); err = ideapad_dytc_profile_init(priv); if (err) { @@ -1925,7 +1928,7 @@ static int ideapad_acpi_resume(struct device *dev) struct ideapad_private *priv = dev_get_drvdata(dev); ideapad_sync_rfk_state(priv); - ideapad_sync_touchpad_state(priv); + ideapad_sync_touchpad_state(priv, false); if (priv->dytc) dytc_profile_refresh(priv); -- cgit v1.2.3-58-ga151 From c69e7d843d2c34b80b8731a5dc57c34ea04a3edf Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Nov 2022 12:02:41 +0100 Subject: platform/x86: ideapad-laptop: Only toggle ps2 aux port on/off on select models Recently there have been multiple patches to disable the ideapad-laptop's touchpad control code, because it is causing issues on various laptops: Commit d69cd7eea93e ("platform/x86: ideapad-laptop: Disable touchpad_switch for ELAN0634") Commit a231224a601c ("platform/x86: ideapad-laptop: Disable touchpad_switch") The turning on/off of the ps2 aux port was added specifically for the IdeaPad Z570, where the EC does toggle the touchpad on/off LED and toggles the value returned by reading VPCCMD_R_TOUCHPAD, but it does not actually turn on/off the touchpad. The ideapad-laptop code really should not be messing with the i8042 controller on all devices just for this special case. Add a new ctrl_ps2_aux_port flag set based on a DMI based allow-list for devices which need this workaround, populating it with just the Ideapad Z570 for now. This also adds a module parameter so that this behavior can easily be enabled on other models which may need it. Signed-off-by: Hans de Goede Reviewed-by: Jiaxun Yang Tested-by: Jiaxun Yang Tested-by: Maxim Mikityanskiy Link: https://lore.kernel.org/r/20221117110244.67811-4-hdegoede@redhat.com --- drivers/platform/x86/ideapad-laptop.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index eb0b1ec32c13..1d86fb988d56 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -143,6 +143,7 @@ struct ideapad_private { bool hw_rfkill_switch : 1; bool kbd_bl : 1; bool touchpad_ctrl_via_ec : 1; + bool ctrl_ps2_aux_port : 1; bool usb_charging : 1; } features; struct { @@ -174,6 +175,12 @@ MODULE_PARM_DESC(set_fn_lock_led, "Enable driver based updates of the fn-lock LED on fn-lock changes. " "If you need this please report this to: platform-driver-x86@vger.kernel.org"); +static bool ctrl_ps2_aux_port; +module_param(ctrl_ps2_aux_port, bool, 0444); +MODULE_PARM_DESC(ctrl_ps2_aux_port, + "Enable driver based PS/2 aux port en-/dis-abling on touchpad on/off toggle. " + "If you need this please report this to: platform-driver-x86@vger.kernel.org"); + /* * shared data */ @@ -1507,7 +1514,8 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_ * touchpad off and on. We send KEY_TOUCHPAD_OFF and * KEY_TOUCHPAD_ON to not to get out of sync with LED */ - i8042_command(¶m, value ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE); + if (priv->features.ctrl_ps2_aux_port) + i8042_command(¶m, value ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE); if (send_events) { ideapad_input_report(priv, value ? 67 : 66); @@ -1615,6 +1623,23 @@ static const struct dmi_system_id hw_rfkill_list[] = { {} }; +/* + * On some models the EC toggles the touchpad muted LED on touchpad toggle + * hotkey presses, but the EC does not actually disable the touchpad itself. + * On these models the driver needs to explicitly enable/disable the i8042 + * (PS/2) aux port. + */ +static const struct dmi_system_id ctrl_ps2_aux_port_list[] = { + { + /* Lenovo Ideapad Z570 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Ideapad Z570"), + }, + }, + {} +}; + static const struct dmi_system_id no_touchpad_switch_list[] = { { .ident = "Lenovo Yoga 3 Pro 1370", @@ -1642,6 +1667,8 @@ static void ideapad_check_features(struct ideapad_private *priv) set_fn_lock_led || dmi_check_system(set_fn_lock_led_list); priv->features.hw_rfkill_switch = hw_rfkill_switch || dmi_check_system(hw_rfkill_list); + priv->features.ctrl_ps2_aux_port = + ctrl_ps2_aux_port || dmi_check_system(ctrl_ps2_aux_port_list); /* Most ideapads with ELAN0634 touchpad don't use EC touchpad switch */ if (acpi_dev_present("ELAN0634", NULL, -1)) -- cgit v1.2.3-58-ga151 From 5829f8a897e4f030cd2d32a930eea8954ab5dcd3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Nov 2022 12:02:42 +0100 Subject: platform/x86: ideapad-laptop: Send KEY_TOUCHPAD_TOGGLE on some models On recent Ideapad models the EC does not control the touchpad at all, so instead of sending KEY_TOUCHPAD_ON/ _OFF on touchpad toggle hotkey events, ideapad-laptop should send KEY_TOUCHPAD_TOGGLE and let userspace handle the toggling. Check for this by checking if the value read from VPCCMD_R_TOUCHPAD actually changes when receiving a touchpad-toggle hotkey event; and if it does not change send KEY_TOUCHPAD_TOGGLE to userspace to let userspace enable/disable the touchpad in software. Note this also drops the priv->features.touchpad_ctrl_via_ec check from ideapad_sync_touchpad_state() so that KEY_TOUCHPAD_TOGGLE will be send on laptops where this is not set too. This can be safely dropped now because the i8042_command(I8042_CMD_AUX_ENABLE/_DISABLE) call is now guarded by its own feature flag. Signed-off-by: Hans de Goede Reviewed-by: Jiaxun Yang Tested-by: Jiaxun Yang Tested-by: Maxim Mikityanskiy Link: https://lore.kernel.org/r/20221117110244.67811-5-hdegoede@redhat.com --- drivers/platform/x86/ideapad-laptop.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 1d86fb988d56..9b36cfddd36f 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -134,6 +134,7 @@ struct ideapad_private { struct ideapad_dytc_priv *dytc; struct dentry *debug; unsigned long cfg; + unsigned long r_touchpad_val; struct { bool conservation_mode : 1; bool dytc : 1; @@ -650,6 +651,8 @@ static ssize_t touchpad_show(struct device *dev, if (err) return err; + priv->r_touchpad_val = result; + return sysfs_emit(buf, "%d\n", !!result); } @@ -669,6 +672,8 @@ static ssize_t touchpad_store(struct device *dev, if (err) return err; + priv->r_touchpad_val = state; + return count; } @@ -1159,6 +1164,7 @@ static const struct key_entry ideapad_keymap[] = { { KE_KEY, 65, { KEY_PROG4 } }, { KE_KEY, 66, { KEY_TOUCHPAD_OFF } }, { KE_KEY, 67, { KEY_TOUCHPAD_ON } }, + { KE_KEY, 68, { KEY_TOUCHPAD_TOGGLE } }, { KE_KEY, 128, { KEY_ESC } }, /* @@ -1500,9 +1506,6 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_ unsigned char param; int ret; - if (!priv->features.touchpad_ctrl_via_ec) - return; - /* Without reading from EC touchpad LED doesn't switch state */ ret = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value); if (ret) @@ -1518,9 +1521,20 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_ i8042_command(¶m, value ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE); if (send_events) { - ideapad_input_report(priv, value ? 67 : 66); - sysfs_notify(&priv->platform_device->dev.kobj, NULL, "touchpad"); + /* + * On older models the EC controls the touchpad and toggles it + * on/off itself, in this case we report KEY_TOUCHPAD_ON/_OFF. + * If the EC did not toggle, report KEY_TOUCHPAD_TOGGLE. + */ + if (value != priv->r_touchpad_val) { + ideapad_input_report(priv, value ? 67 : 66); + sysfs_notify(&priv->platform_device->dev.kobj, NULL, "touchpad"); + } else { + ideapad_input_report(priv, 68); + } } + + priv->r_touchpad_val = value; } static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) -- cgit v1.2.3-58-ga151 From a10ba160d427e78ffa2ab15a86cacaec291fa58a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Nov 2022 12:02:43 +0100 Subject: platform/x86: ideapad-laptop: Stop writing VPCCMD_W_TOUCHPAD at probe time Commit d69cd7eea93e ("platform/x86: ideapad-laptop: Disable touchpad_switch for ELAN0634") from Janary 2021 added a flag hiding the touchpad sysfs-attr and disabling ideapad_sync_touchpad_state() because some devices "do not use EC to switch touchpad". At the same time this added a write(VPCCMD_W_TOUCHPAD, 1) call at probe time on these same devices. This seems to be copied from the rfkill code which does something similar when hw rfkill support is disabled. But for the rfkill code this is known to be necessary on some models, where as for the touchpad control no motivation is given for doing this and prior to this patch there were no reports of needing to do this. So this seems unnecessary; and it is best to avoid poking the hardware unnecessary to avoid unwanted side effects, so remove this. Signed-off-by: Hans de Goede Reviewed-by: Jiaxun Yang Tested-by: Jiaxun Yang Tested-by: Maxim Mikityanskiy Link: https://lore.kernel.org/r/20221117110244.67811-6-hdegoede@redhat.com --- drivers/platform/x86/ideapad-laptop.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 9b36cfddd36f..fc3d47a75944 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -1875,10 +1875,6 @@ static int ideapad_acpi_add(struct platform_device *pdev) if (!priv->features.hw_rfkill_switch) write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1); - /* The same for Touchpad */ - if (!priv->features.touchpad_ctrl_via_ec) - write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, 1); - for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg)) ideapad_register_rfkill(priv, i); -- cgit v1.2.3-58-ga151 From 301e0d766d7018a579063f539837f0f1fa1b2948 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Nov 2022 12:02:44 +0100 Subject: platform/x86: ideapad-laptop: Make touchpad_ctrl_via_ec a module option Remove the ACPI-HID + DMI-id deny-lists for touchpad_ctrl_via_ec and instead make it a module option which defaults to false. The touchpad sysfs attribute allowing directly writing VPCCMD_W_TOUCHPAD from userspace has been leading to a lot of bug-reports / patches adding both ACPI HID + dmi-id based deny-lists for it which then need to be expanded all the time going forward leading to a high maintenance load. At the same time the touchpad sysfs attribute is not a standard Linux userspace API. So it is not used in standard desktop-enviroments, instead it is only used in the following 2 rare circumstances: 1. Ideapad specific control-panel like applets 2. Custom scripts written by users For 1. these applets need to already deal with the touchpad sysfs attr sometimes not being there because of the existing deny lists so hiding it be default should not cause an issue; and most desktop environments already have a touchpad-disable option in their native control-panel, so having an ideapad specific toggle for this is not necessary. For 2. since these users are already customizing their systems they can add the module option if they want to keep using the touchpad sysfs attribute. Signed-off-by: Hans de Goede Reviewed-by: Jiaxun Yang Tested-by: Jiaxun Yang Tested-by: Maxim Mikityanskiy Link: https://lore.kernel.org/r/20221117110244.67811-7-hdegoede@redhat.com --- drivers/platform/x86/ideapad-laptop.c | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index fc3d47a75944..435d2d3d903b 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -182,6 +182,12 @@ MODULE_PARM_DESC(ctrl_ps2_aux_port, "Enable driver based PS/2 aux port en-/dis-abling on touchpad on/off toggle. " "If you need this please report this to: platform-driver-x86@vger.kernel.org"); +static bool touchpad_ctrl_via_ec; +module_param(touchpad_ctrl_via_ec, bool, 0444); +MODULE_PARM_DESC(touchpad_ctrl_via_ec, + "Enable registering a 'touchpad' sysfs-attribute which can be used to manually " + "tell the EC to enable/disable the touchpad. This may not work on all models."); + /* * shared data */ @@ -1654,24 +1660,6 @@ static const struct dmi_system_id ctrl_ps2_aux_port_list[] = { {} }; -static const struct dmi_system_id no_touchpad_switch_list[] = { - { - .ident = "Lenovo Yoga 3 Pro 1370", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 3"), - }, - }, - { - .ident = "ZhaoYang K4e-IML", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ZhaoYang K4e-IML"), - }, - }, - {} -}; - static void ideapad_check_features(struct ideapad_private *priv) { acpi_handle handle = priv->adev->handle; @@ -1683,14 +1671,7 @@ static void ideapad_check_features(struct ideapad_private *priv) hw_rfkill_switch || dmi_check_system(hw_rfkill_list); priv->features.ctrl_ps2_aux_port = ctrl_ps2_aux_port || dmi_check_system(ctrl_ps2_aux_port_list); - - /* Most ideapads with ELAN0634 touchpad don't use EC touchpad switch */ - if (acpi_dev_present("ELAN0634", NULL, -1)) - priv->features.touchpad_ctrl_via_ec = 0; - else if (dmi_check_system(no_touchpad_switch_list)) - priv->features.touchpad_ctrl_via_ec = 0; - else - priv->features.touchpad_ctrl_via_ec = 1; + priv->features.touchpad_ctrl_via_ec = touchpad_ctrl_via_ec; if (!read_ec_data(handle, VPCCMD_R_FAN, &val)) priv->features.fan_mode = true; -- cgit v1.2.3-58-ga151 From e32354bb8fe3394ab37faa86c11ff4e06e296f72 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 20 Nov 2022 23:48:20 +0100 Subject: platform/x86/intel/hid: Add module-params for 5 button array + SW_TABLET_MODE reporting The driver has DMI-quirk tables for force-enabling 5 button array support and for 2 different ways of enabling SW_TABLET_MODE reporting. Add module parameters to allow user to enable the driver behavior currently only available through DMI quirks. This is useful for users to test this in bug-reports and for users to use as a workaround while new DMI quirks find their way upstream. Link: https://gitlab.freedesktop.org/libinput/libinput/-/issues/822 Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20221120224820.746478-1-hdegoede@redhat.com --- drivers/platform/x86/intel/hid.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c index b6313ecd190c..b6c06b37862e 100644 --- a/drivers/platform/x86/intel/hid.c +++ b/drivers/platform/x86/intel/hid.c @@ -16,6 +16,25 @@ #include #include "../dual_accel_detect.h" +enum intel_hid_tablet_sw_mode { + TABLET_SW_AUTO = -1, + TABLET_SW_OFF = 0, + TABLET_SW_AT_EVENT, + TABLET_SW_AT_PROBE, +}; + +static bool enable_5_button_array; +module_param(enable_5_button_array, bool, 0444); +MODULE_PARM_DESC(enable_5_button_array, + "Enable 5 Button Array support. " + "If you need this please report this to: platform-driver-x86@vger.kernel.org"); + +static int enable_sw_tablet_mode = TABLET_SW_AUTO; +module_param(enable_sw_tablet_mode, int, 0444); +MODULE_PARM_DESC(enable_sw_tablet_mode, + "Enable SW_TABLET_MODE reporting -1:auto 0:off 1:at-first-event 2:at-probe. " + "If you need this please report this to: platform-driver-x86@vger.kernel.org"); + /* When NOT in tablet mode, VGBS returns with the flag 0x40 */ #define TABLET_MODE_FLAG BIT(6) @@ -157,7 +176,6 @@ struct intel_hid_priv { struct input_dev *array; struct input_dev *switches; bool wakeup_mode; - bool auto_add_switch; }; #define HID_EVENT_FILTER_UUID "eeec56b3-4442-408f-a792-4edd4d758054" @@ -487,7 +505,8 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) * SW_TABLET_MODE report, in these cases we enable support when receiving * the first event instead of during driver setup. */ - if (!priv->switches && priv->auto_add_switch && (event == 0xcc || event == 0xcd)) { + if (!priv->switches && enable_sw_tablet_mode == TABLET_SW_AT_EVENT && + (event == 0xcc || event == 0xcd)) { dev_info(&device->dev, "switch event received, enable switches supports\n"); err = intel_hid_switches_setup(device); if (err) @@ -592,7 +611,7 @@ static bool button_array_present(struct platform_device *device) return true; } - if (dmi_check_system(button_array_table)) + if (enable_5_button_array || dmi_check_system(button_array_table)) return true; return false; @@ -629,7 +648,14 @@ static int intel_hid_probe(struct platform_device *device) dev_set_drvdata(&device->dev, priv); /* See dual_accel_detect.h for more info on the dual_accel check. */ - priv->auto_add_switch = dmi_check_system(dmi_auto_add_switch) && !dual_accel_detect(); + if (enable_sw_tablet_mode == TABLET_SW_AUTO) { + if (dmi_check_system(dmi_vgbs_allow_list)) + enable_sw_tablet_mode = TABLET_SW_AT_PROBE; + else if (dmi_check_system(dmi_auto_add_switch) && !dual_accel_detect()) + enable_sw_tablet_mode = TABLET_SW_AT_EVENT; + else + enable_sw_tablet_mode = TABLET_SW_OFF; + } err = intel_hid_input_setup(device); if (err) { @@ -646,7 +672,7 @@ static int intel_hid_probe(struct platform_device *device) } /* Setup switches for devices that we know VGBS return correctly */ - if (dmi_check_system(dmi_vgbs_allow_list)) { + if (enable_sw_tablet_mode == TABLET_SW_AT_PROBE) { dev_info(&device->dev, "platform supports switches\n"); err = intel_hid_switches_setup(device); if (err) -- cgit v1.2.3-58-ga151 From 6f70a53afa21984e947e63fcc8919e9eaecd31ac Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Sat, 26 Nov 2022 17:46:17 -0700 Subject: platform/x86: wireless-hotkey: use ACPI HID as phys Removed the hardcoded "hpq6001" as phys but uses ACPI HID instead. Signed-off-by: Alex Hung Link: https://lore.kernel.org/r/20221127004617.722553-1-alex.hung@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/wireless-hotkey.c | 60 ++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 20 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/wireless-hotkey.c b/drivers/platform/x86/wireless-hotkey.c index 11c60a273446..eb48ca060bad 100644 --- a/drivers/platform/x86/wireless-hotkey.c +++ b/drivers/platform/x86/wireless-hotkey.c @@ -20,7 +20,10 @@ MODULE_ALIAS("acpi*:HPQ6001:*"); MODULE_ALIAS("acpi*:WSTADEF:*"); MODULE_ALIAS("acpi*:AMDI0051:*"); -static struct input_dev *wl_input_dev; +struct wl_button { + struct input_dev *input_dev; + char phys[32]; +}; static const struct acpi_device_id wl_ids[] = { {"HPQ6001", 0}, @@ -29,63 +32,80 @@ static const struct acpi_device_id wl_ids[] = { {"", 0}, }; -static int wireless_input_setup(void) +static int wireless_input_setup(struct acpi_device *device) { + struct wl_button *button = acpi_driver_data(device); int err; - wl_input_dev = input_allocate_device(); - if (!wl_input_dev) + button->input_dev = input_allocate_device(); + if (!button->input_dev) return -ENOMEM; - wl_input_dev->name = "Wireless hotkeys"; - wl_input_dev->phys = "hpq6001/input0"; - wl_input_dev->id.bustype = BUS_HOST; - wl_input_dev->evbit[0] = BIT(EV_KEY); - set_bit(KEY_RFKILL, wl_input_dev->keybit); + snprintf(button->phys, sizeof(button->phys), "%s/input0", acpi_device_hid(device)); + + button->input_dev->name = "Wireless hotkeys"; + button->input_dev->phys = button->phys; + button->input_dev->id.bustype = BUS_HOST; + button->input_dev->evbit[0] = BIT(EV_KEY); + set_bit(KEY_RFKILL, button->input_dev->keybit); - err = input_register_device(wl_input_dev); + err = input_register_device(button->input_dev); if (err) goto err_free_dev; return 0; err_free_dev: - input_free_device(wl_input_dev); + input_free_device(button->input_dev); return err; } -static void wireless_input_destroy(void) +static void wireless_input_destroy(struct acpi_device *device) { - input_unregister_device(wl_input_dev); + struct wl_button *button = acpi_driver_data(device); + + input_unregister_device(button->input_dev); + kfree(button); } static void wl_notify(struct acpi_device *acpi_dev, u32 event) { + struct wl_button *button = acpi_driver_data(acpi_dev); + if (event != 0x80) { pr_info("Received unknown event (0x%x)\n", event); return; } - input_report_key(wl_input_dev, KEY_RFKILL, 1); - input_sync(wl_input_dev); - input_report_key(wl_input_dev, KEY_RFKILL, 0); - input_sync(wl_input_dev); + input_report_key(button->input_dev, KEY_RFKILL, 1); + input_sync(button->input_dev); + input_report_key(button->input_dev, KEY_RFKILL, 0); + input_sync(button->input_dev); } static int wl_add(struct acpi_device *device) { + struct wl_button *button; int err; - err = wireless_input_setup(); - if (err) + button = kzalloc(sizeof(struct wl_button), GFP_KERNEL); + if (!button) + return -ENOMEM; + + device->driver_data = button; + + err = wireless_input_setup(device); + if (err) { pr_err("Failed to setup wireless hotkeys\n"); + kfree(button); + } return err; } static int wl_remove(struct acpi_device *device) { - wireless_input_destroy(); + wireless_input_destroy(device); return 0; } -- cgit v1.2.3-58-ga151 From 902ce18ab1f4444ff9d49865bea35a07adcc03fd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 8 Dec 2022 12:02:24 +0100 Subject: platform/x86: x86-android-tablets: Add Medion Lifetab S10346 data The Medion Lifetab S10346 is a x86 ACPI tablet which ships with Android x86 as factory OS. Its DSDT contains a bunch of I2C devices which are not actually there, causing various resource conflicts. Enumeration of these is skipped through the acpi_quirk_skip_i2c_client_enumeration(). Add support for manually instantiating the I2C devices which are actually present on this tablet by adding the necessary device info to the x86-android-tablets module. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20221208110224.107354-1-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets.c | 92 ++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets.c b/drivers/platform/x86/x86-android-tablets.c index 4acd6fa8d43b..f04e06eeb958 100644 --- a/drivers/platform/x86/x86-android-tablets.c +++ b/drivers/platform/x86/x86-android-tablets.c @@ -987,6 +987,88 @@ static void lenovo_yoga_tab2_830_1050_exit(void) } } +/* Medion Lifetab S10346 tablets have an Android factory img with everything hardcoded */ +static const char * const medion_lifetab_s10346_accel_mount_matrix[] = { + "0", "1", "0", + "1", "0", "0", + "0", "0", "1" +}; + +static const struct property_entry medion_lifetab_s10346_accel_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", medion_lifetab_s10346_accel_mount_matrix), + { } +}; + +static const struct software_node medion_lifetab_s10346_accel_node = { + .properties = medion_lifetab_s10346_accel_props, +}; + +/* Note the LCD panel is mounted upside down, this is correctly indicated in the VBT */ +static const struct property_entry medion_lifetab_s10346_touchscreen_props[] = { + PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + { } +}; + +static const struct software_node medion_lifetab_s10346_touchscreen_node = { + .properties = medion_lifetab_s10346_touchscreen_props, +}; + +static const struct x86_i2c_client_info medion_lifetab_s10346_i2c_clients[] __initconst = { + { + /* kxtj21009 accel */ + .board_info = { + .type = "kxtj21009", + .addr = 0x0f, + .dev_name = "kxtj21009", + .swnode = &medion_lifetab_s10346_accel_node, + }, + .adapter_path = "\\_SB_.I2C3", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 23, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, { + /* goodix touchscreen */ + .board_info = { + .type = "GDIX1001:00", + .addr = 0x14, + .dev_name = "goodix_ts", + .swnode = &medion_lifetab_s10346_touchscreen_node, + }, + .adapter_path = "\\_SB_.I2C4", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_APIC, + .index = 0x44, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, +}; + +static struct gpiod_lookup_table medion_lifetab_s10346_goodix_gpios = { + .dev_id = "i2c-goodix_ts", + .table = { + GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH), + { } + }, +}; + +static struct gpiod_lookup_table * const medion_lifetab_s10346_gpios[] = { + &medion_lifetab_s10346_goodix_gpios, + NULL +}; + +static const struct x86_dev_info medion_lifetab_s10346_info __initconst = { + .i2c_client_info = medion_lifetab_s10346_i2c_clients, + .i2c_client_count = ARRAY_SIZE(medion_lifetab_s10346_i2c_clients), + .gpiod_lookup_tables = medion_lifetab_s10346_gpios, +}; + /* Nextbook Ares 8 tablets have an Android factory img with everything hardcoded */ static const char * const nextbook_ares8_accel_mount_matrix[] = { "0", "-1", "0", @@ -1245,6 +1327,16 @@ static const struct dmi_system_id x86_android_tablet_ids[] __initconst = { }, .driver_data = (void *)&lenovo_yoga_tab2_830_1050_info, }, + { + /* Medion Lifetab S10346 */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), + /* Above strings are much too generic, also match on BIOS date */ + DMI_MATCH(DMI_BIOS_DATE, "10/22/2015"), + }, + .driver_data = (void *)&medion_lifetab_s10346_info, + }, { /* Nextbook Ares 8 */ .matches = { -- cgit v1.2.3-58-ga151 From b6c14ff1deaafd30036ec36d5205acd5a578b1cd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 27 Nov 2022 19:24:58 +0100 Subject: platform/x86: x86-android-tablets: Add Lenovo Yoga Tab 3 (YT3-X90F) charger + fuel-gauge data The Lenovo Yoga Tab 3 (YT3-X90F) is an Intel Cherry Trail based tablet which ships with Android as Factory OS. Its DSDT contains a bunch of I2C devices which are not actually there, causing various resource conflicts. Use acpi_quirk_skip_i2c_client_enumeration() to not enumerate these. The YT3-X90F has quite a bit of exotic hardware, this adds initial support by manually instantiating the i2c-clients for the 2 charger + 2 fuel-gauge chips used for the 2 batteries. Support for other parts of the hw will be added by follow-up patches. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20221127182458.104528-1-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets.c | 135 ++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets.c b/drivers/platform/x86/x86-android-tablets.c index f04e06eeb958..bbfae1395e18 100644 --- a/drivers/platform/x86/x86-android-tablets.c +++ b/drivers/platform/x86/x86-android-tablets.c @@ -5,7 +5,7 @@ * devices typically have a bunch of things hardcoded, rather than specified * in their DSDT. * - * Copyright (C) 2021 Hans de Goede + * Copyright (C) 2021-2022 Hans de Goede */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -987,6 +987,130 @@ static void lenovo_yoga_tab2_830_1050_exit(void) } } +/* Lenovo Yoga Tab 3 Pro YT3-X90F */ + +/* + * There are 2 batteries, with 2 bq27500 fuel-gauges and 2 bq25892 chargers, + * "bq25890-charger-1" is instantiated from: drivers/i2c/busses/i2c-cht-wc.c. + */ +static const char * const lenovo_yt3_bq25892_0_suppliers[] = { "cht_wcove_pwrsrc" }; +static const char * const bq25890_1_psy[] = { "bq25890-charger-1" }; + +static const struct property_entry fg_bq25890_1_supply_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq25890_1_psy), + { } +}; + +static const struct software_node fg_bq25890_1_supply_node = { + .properties = fg_bq25890_1_supply_props, +}; + +/* bq25892 charger settings for the flat lipo battery behind the screen */ +static const struct property_entry lenovo_yt3_bq25892_0_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lenovo_yt3_bq25892_0_suppliers), + PROPERTY_ENTRY_STRING("linux,power-supply-name", "bq25892-second-chrg"), + PROPERTY_ENTRY_U32("linux,iinlim-percentage", 40), + PROPERTY_ENTRY_BOOL("linux,skip-reset"), + /* Values taken from Android Factory Image */ + PROPERTY_ENTRY_U32("ti,charge-current", 2048000), + PROPERTY_ENTRY_U32("ti,battery-regulation-voltage", 4352000), + PROPERTY_ENTRY_U32("ti,termination-current", 128000), + PROPERTY_ENTRY_U32("ti,precharge-current", 128000), + PROPERTY_ENTRY_U32("ti,minimum-sys-voltage", 3700000), + PROPERTY_ENTRY_U32("ti,boost-voltage", 4998000), + PROPERTY_ENTRY_U32("ti,boost-max-current", 500000), + PROPERTY_ENTRY_BOOL("ti,use-ilim-pin"), + { } +}; + +static const struct software_node lenovo_yt3_bq25892_0_node = { + .properties = lenovo_yt3_bq25892_0_props, +}; + +static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { + { + /* bq27500 fuel-gauge for the flat lipo battery behind the screen */ + .board_info = { + .type = "bq27500", + .addr = 0x55, + .dev_name = "bq27500_0", + .swnode = &fg_bq25890_supply_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C1", + }, { + /* bq25892 charger for the flat lipo battery behind the screen */ + .board_info = { + .type = "bq25892", + .addr = 0x6b, + .dev_name = "bq25892_0", + .swnode = &lenovo_yt3_bq25892_0_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C1", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FF:01", + .index = 5, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, { + /* bq27500 fuel-gauge for the round li-ion cells in the hinge */ + .board_info = { + .type = "bq27500", + .addr = 0x55, + .dev_name = "bq27500_1", + .swnode = &fg_bq25890_1_supply_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C2", + } +}; + +static int __init lenovo_yt3_init(void) +{ + struct gpio_desc *gpiod; + int ret; + + /* + * The "bq25892_0" charger IC has its /CE (Charge-Enable) and OTG pins + * connected to GPIOs, rather then having them hardwired to the correct + * values as is normally done. + * + * The bq25890_charger driver controls these through I2C, but this only + * works if not overridden by the pins. Set these pins here: + * 1. Set /CE to 0 to allow charging. + * 2. Set OTG to 0 disable V5 boost output since the 5V boost output of + * the main "bq25892_1" charger is used when necessary. + */ + + /* /CE pin */ + ret = x86_android_tablet_get_gpiod("INT33FF:02", 22, &gpiod); + if (ret < 0) + return ret; + + /* + * The gpio_desc returned by x86_android_tablet_get_gpiod() is a "raw" + * gpio_desc, that is there is no way to pass lookup-flags like + * GPIO_ACTIVE_LOW. Set the GPIO to 0 here to enable charging since + * the /CE pin is active-low, but not marked as such in the gpio_desc. + */ + gpiod_set_value(gpiod, 0); + + /* OTG pin */ + ret = x86_android_tablet_get_gpiod("INT33FF:03", 19, &gpiod); + if (ret < 0) + return ret; + + gpiod_set_value(gpiod, 0); + + return 0; +} + +static const struct x86_dev_info lenovo_yt3_info __initconst = { + .i2c_client_info = lenovo_yt3_i2c_clients, + .i2c_client_count = ARRAY_SIZE(lenovo_yt3_i2c_clients), + .init = lenovo_yt3_init, +}; + /* Medion Lifetab S10346 tablets have an Android factory img with everything hardcoded */ static const char * const medion_lifetab_s10346_accel_mount_matrix[] = { "0", "1", "0", @@ -1327,6 +1451,15 @@ static const struct dmi_system_id x86_android_tablet_ids[] __initconst = { }, .driver_data = (void *)&lenovo_yoga_tab2_830_1050_info, }, + { + /* Lenovo Yoga Tab 3 Pro YT3-X90F */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"), + }, + .driver_data = (void *)&lenovo_yt3_info, + }, { /* Medion Lifetab S10346 */ .matches = { -- cgit v1.2.3-58-ga151 From b03ae77e7e057f4b3b858f10c840557e71448a91 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 27 Nov 2022 23:19:28 +0100 Subject: platform/x86: x86-android-tablets: Add Advantech MICA-071 extra button The Advantech MICA-071 is a standard Windows tablet, but it has an extra "quick launch" button which is not described in the ACPI tables in anyway. Use the x86-android-tablets infra to create a gpio-button device for this. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20221127221928.123660-1-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets.c | 58 ++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets.c b/drivers/platform/x86/x86-android-tablets.c index bbfae1395e18..123a4618db55 100644 --- a/drivers/platform/x86/x86-android-tablets.c +++ b/drivers/platform/x86/x86-android-tablets.c @@ -265,6 +265,56 @@ static struct gpiod_lookup_table int3496_gpo2_pin22_gpios = { }, }; +/* + * Advantech MICA-071 + * This is a standard Windows tablet, but it has an extra "quick launch" button + * which is not described in the ACPI tables in anyway. + * Use the x86-android-tablets infra to create a gpio-button device for this. + */ +static struct gpio_keys_button advantech_mica_071_button = { + .code = KEY_PROG1, + /* .gpio gets filled in by advantech_mica_071_init() */ + .active_low = true, + .desc = "prog1_key", + .type = EV_KEY, + .wakeup = false, + .debounce_interval = 50, +}; + +static const struct gpio_keys_platform_data advantech_mica_071_button_pdata __initconst = { + .buttons = &advantech_mica_071_button, + .nbuttons = 1, + .name = "prog1_key", +}; + +static const struct platform_device_info advantech_mica_071_pdevs[] __initconst = { + { + .name = "gpio-keys", + .id = PLATFORM_DEVID_AUTO, + .data = &advantech_mica_071_button_pdata, + .size_data = sizeof(advantech_mica_071_button_pdata), + }, +}; + +static int __init advantech_mica_071_init(void) +{ + struct gpio_desc *gpiod; + int ret; + + ret = x86_android_tablet_get_gpiod("INT33FC:00", 2, &gpiod); + if (ret < 0) + return ret; + advantech_mica_071_button.gpio = desc_to_gpio(gpiod); + + return 0; +} + +static const struct x86_dev_info advantech_mica_071_info __initconst = { + .pdev_info = advantech_mica_071_pdevs, + .pdev_count = ARRAY_SIZE(advantech_mica_071_pdevs), + .init = advantech_mica_071_init, +}; + /* Asus ME176C and TF103C tablets shared data */ static struct gpio_keys_button asus_me176c_tf103c_lid = { .code = SW_LID, @@ -1385,6 +1435,14 @@ static const struct x86_dev_info xiaomi_mipad2_info __initconst = { }; static const struct dmi_system_id x86_android_tablet_ids[] __initconst = { + { + /* Advantech MICA-071 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Advantech"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MICA-071"), + }, + .driver_data = (void *)&advantech_mica_071_info, + }, { /* Asus MeMO Pad 7 ME176C */ .matches = { -- cgit v1.2.3-58-ga151 From 727cc0147f5066e359aca65cc6cc5e6d64cc15d8 Mon Sep 17 00:00:00 2001 From: Yu Liao Date: Tue, 29 Nov 2022 09:11:01 +0800 Subject: platform/x86: mxm-wmi: fix memleak in mxm_wmi_call_mx[ds|mx]() The ACPI buffer memory (out.pointer) returned by wmi_evaluate_method() is not freed after the call, so it leads to memory leak. The method results in ACPI buffer is not used, so just pass NULL to wmi_evaluate_method() which fixes the memory leak. Fixes: 99b38b4acc0d ("platform/x86: add MXM WMI driver.") Signed-off-by: Yu Liao Link: https://lore.kernel.org/r/20221129011101.2042315-1-liaoyu15@huawei.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/mxm-wmi.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/mxm-wmi.c b/drivers/platform/x86/mxm-wmi.c index 9a19fbd2f734..9a457956025a 100644 --- a/drivers/platform/x86/mxm-wmi.c +++ b/drivers/platform/x86/mxm-wmi.c @@ -35,13 +35,11 @@ int mxm_wmi_call_mxds(int adapter) .xarg = 1, }; struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; - struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_status status; printk("calling mux switch %d\n", adapter); - status = wmi_evaluate_method(MXM_WMMX_GUID, 0x0, adapter, &input, - &output); + status = wmi_evaluate_method(MXM_WMMX_GUID, 0x0, adapter, &input, NULL); if (ACPI_FAILURE(status)) return status; @@ -60,13 +58,11 @@ int mxm_wmi_call_mxmx(int adapter) .xarg = 1, }; struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; - struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_status status; printk("calling mux switch %d\n", adapter); - status = wmi_evaluate_method(MXM_WMMX_GUID, 0x0, adapter, &input, - &output); + status = wmi_evaluate_method(MXM_WMMX_GUID, 0x0, adapter, &input, NULL); if (ACPI_FAILURE(status)) return status; -- cgit v1.2.3-58-ga151 From d240849c75f4efc931f38d3529dcbcbd76adbd81 Mon Sep 17 00:00:00 2001 From: ye xingchen Date: Fri, 2 Dec 2022 17:05:12 +0800 Subject: platform/x86: uv_sysfs: Use sysfs_emit() instead of scnprintf() Follow the advice of the Documentation/filesystems/sysfs.rst and show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. Signed-off-by: ye xingchen Reviewed-by: Justin Ernst Link: https://lore.kernel.org/r/202212021705128095546@zte.com.cn Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/uv_sysfs.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/uv_sysfs.c b/drivers/platform/x86/uv_sysfs.c index 625b0b79d185..73fc38ee7430 100644 --- a/drivers/platform/x86/uv_sysfs.c +++ b/drivers/platform/x86/uv_sysfs.c @@ -119,12 +119,12 @@ struct uv_hub { static ssize_t hub_name_show(struct uv_bios_hub_info *hub_info, char *buf) { - return scnprintf(buf, PAGE_SIZE, "%s\n", hub_info->name); + return sysfs_emit(buf, "%s\n", hub_info->name); } static ssize_t hub_location_show(struct uv_bios_hub_info *hub_info, char *buf) { - return scnprintf(buf, PAGE_SIZE, "%s\n", hub_info->location); + return sysfs_emit(buf, "%s\n", hub_info->location); } static ssize_t hub_partition_show(struct uv_bios_hub_info *hub_info, char *buf) @@ -460,12 +460,12 @@ struct uv_pci_top_obj { static ssize_t uv_pci_type_show(struct uv_pci_top_obj *top_obj, char *buf) { - return scnprintf(buf, PAGE_SIZE, "%s\n", top_obj->type); + return sysfs_emit(buf, "%s\n", top_obj->type); } static ssize_t uv_pci_location_show(struct uv_pci_top_obj *top_obj, char *buf) { - return scnprintf(buf, PAGE_SIZE, "%s\n", top_obj->location); + return sysfs_emit(buf, "%s\n", top_obj->location); } static ssize_t uv_pci_iio_stack_show(struct uv_pci_top_obj *top_obj, char *buf) @@ -475,7 +475,7 @@ static ssize_t uv_pci_iio_stack_show(struct uv_pci_top_obj *top_obj, char *buf) static ssize_t uv_pci_ppb_addr_show(struct uv_pci_top_obj *top_obj, char *buf) { - return scnprintf(buf, PAGE_SIZE, "%s\n", top_obj->ppb_addr); + return sysfs_emit(buf, "%s\n", top_obj->ppb_addr); } static ssize_t uv_pci_slot_show(struct uv_pci_top_obj *top_obj, char *buf) @@ -737,7 +737,7 @@ static ssize_t coherence_id_show(struct kobject *kobj, static ssize_t uv_type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return scnprintf(buf, PAGE_SIZE, "%s\n", uv_type_string()); + return sysfs_emit(buf, "%s\n", uv_type_string()); } static ssize_t uv_archtype_show(struct kobject *kobj, @@ -749,13 +749,13 @@ static ssize_t uv_archtype_show(struct kobject *kobj, static ssize_t uv_hub_type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return scnprintf(buf, PAGE_SIZE, "0x%x\n", uv_hub_type()); + return sysfs_emit(buf, "0x%x\n", uv_hub_type()); } static ssize_t uv_hubless_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return scnprintf(buf, PAGE_SIZE, "0x%x\n", uv_get_hubless_system()); + return sysfs_emit(buf, "0x%x\n", uv_get_hubless_system()); } static struct kobj_attribute partition_id_attr = -- cgit v1.2.3-58-ga151 From e84cfa364339e28f3f4bcc1dcc265dbb210bceea Mon Sep 17 00:00:00 2001 From: ye xingchen Date: Fri, 2 Dec 2022 17:21:54 +0800 Subject: platform/x86/dell: alienware-wmi: Use sysfs_emit() instead of scnprintf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow the advice of the Documentation/filesystems/sysfs.rst and show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. Signed-off-by: ye xingchen Link: https://lore.kernel.org/r/202212021721543696124@zte.com.cn Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/dell/alienware-wmi.c | 41 ++++++++++++------------------- 1 file changed, 16 insertions(+), 25 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c index a34e07ef2c79..a9477e5432e4 100644 --- a/drivers/platform/x86/dell/alienware-wmi.c +++ b/drivers/platform/x86/dell/alienware-wmi.c @@ -398,10 +398,10 @@ static ssize_t show_control_state(struct device *dev, struct device_attribute *attr, char *buf) { if (lighting_control_state == LEGACY_BOOTING) - return scnprintf(buf, PAGE_SIZE, "[booting] running suspend\n"); + return sysfs_emit(buf, "[booting] running suspend\n"); else if (lighting_control_state == LEGACY_SUSPEND) - return scnprintf(buf, PAGE_SIZE, "booting running [suspend]\n"); - return scnprintf(buf, PAGE_SIZE, "booting [running] suspend\n"); + return sysfs_emit(buf, "booting running [suspend]\n"); + return sysfs_emit(buf, "booting [running] suspend\n"); } static ssize_t store_control_state(struct device *dev, @@ -547,14 +547,12 @@ static ssize_t show_hdmi_cable(struct device *dev, (u32 *) &out_data); if (ACPI_SUCCESS(status)) { if (out_data == 0) - return scnprintf(buf, PAGE_SIZE, - "[unconnected] connected unknown\n"); + return sysfs_emit(buf, "[unconnected] connected unknown\n"); else if (out_data == 1) - return scnprintf(buf, PAGE_SIZE, - "unconnected [connected] unknown\n"); + return sysfs_emit(buf, "unconnected [connected] unknown\n"); } pr_err("alienware-wmi: unknown HDMI cable status: %d\n", status); - return scnprintf(buf, PAGE_SIZE, "unconnected connected [unknown]\n"); + return sysfs_emit(buf, "unconnected connected [unknown]\n"); } static ssize_t show_hdmi_source(struct device *dev, @@ -571,14 +569,12 @@ static ssize_t show_hdmi_source(struct device *dev, if (ACPI_SUCCESS(status)) { if (out_data == 1) - return scnprintf(buf, PAGE_SIZE, - "[input] gpu unknown\n"); + return sysfs_emit(buf, "[input] gpu unknown\n"); else if (out_data == 2) - return scnprintf(buf, PAGE_SIZE, - "input [gpu] unknown\n"); + return sysfs_emit(buf, "input [gpu] unknown\n"); } pr_err("alienware-wmi: unknown HDMI source status: %u\n", status); - return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n"); + return sysfs_emit(buf, "input gpu [unknown]\n"); } static ssize_t toggle_hdmi_source(struct device *dev, @@ -652,14 +648,12 @@ static ssize_t show_amplifier_status(struct device *dev, (u32 *) &out_data); if (ACPI_SUCCESS(status)) { if (out_data == 0) - return scnprintf(buf, PAGE_SIZE, - "[unconnected] connected unknown\n"); + return sysfs_emit(buf, "[unconnected] connected unknown\n"); else if (out_data == 1) - return scnprintf(buf, PAGE_SIZE, - "unconnected [connected] unknown\n"); + return sysfs_emit(buf, "unconnected [connected] unknown\n"); } pr_err("alienware-wmi: unknown amplifier cable status: %d\n", status); - return scnprintf(buf, PAGE_SIZE, "unconnected connected [unknown]\n"); + return sysfs_emit(buf, "unconnected connected [unknown]\n"); } static DEVICE_ATTR(status, S_IRUGO, show_amplifier_status, NULL); @@ -706,17 +700,14 @@ static ssize_t show_deepsleep_status(struct device *dev, (u32 *) &out_data); if (ACPI_SUCCESS(status)) { if (out_data == 0) - return scnprintf(buf, PAGE_SIZE, - "[disabled] s5 s5_s4\n"); + return sysfs_emit(buf, "[disabled] s5 s5_s4\n"); else if (out_data == 1) - return scnprintf(buf, PAGE_SIZE, - "disabled [s5] s5_s4\n"); + return sysfs_emit(buf, "disabled [s5] s5_s4\n"); else if (out_data == 2) - return scnprintf(buf, PAGE_SIZE, - "disabled s5 [s5_s4]\n"); + return sysfs_emit(buf, "disabled s5 [s5_s4]\n"); } pr_err("alienware-wmi: unknown deep sleep status: %d\n", status); - return scnprintf(buf, PAGE_SIZE, "disabled s5 s5_s4 [unknown]\n"); + return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n"); } static ssize_t toggle_deepsleep(struct device *dev, -- cgit v1.2.3-58-ga151 From e80355dd25aeab0e260911829e1495e17219bf1a Mon Sep 17 00:00:00 2001 From: ye xingchen Date: Thu, 8 Dec 2022 15:45:17 +0800 Subject: platform/x86: sony-laptop: Convert to use sysfs_emit_at() API Follow the advice of the Documentation/filesystems/sysfs.rst and show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. Signed-off-by: ye xingchen Link: https://lore.kernel.org/r/202212081545178689771@zte.com.cn Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/sony-laptop.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 765fcaba4d12..cd0f11eeed1a 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -820,10 +820,9 @@ static ssize_t sony_nc_handles_show(struct device *dev, int i; for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { - len += scnprintf(buffer + len, PAGE_SIZE - len, "0x%.4x ", - handles->cap[i]); + len += sysfs_emit_at(buffer, len, "0x%.4x ", handles->cap[i]); } - len += scnprintf(buffer + len, PAGE_SIZE - len, "\n"); + len += sysfs_emit_at(buffer, len, "\n"); return len; } @@ -2173,10 +2172,9 @@ static ssize_t sony_nc_thermal_profiles_show(struct device *dev, for (cnt = 0; cnt < THM_PROFILE_MAX; cnt++) { if (!cnt || (th_handle->profiles & cnt)) - idx += scnprintf(buffer + idx, PAGE_SIZE - idx, "%s ", - snc_thermal_profiles[cnt]); + idx += sysfs_emit_at(buffer, idx, "%s ", snc_thermal_profiles[cnt]); } - idx += scnprintf(buffer + idx, PAGE_SIZE - idx, "\n"); + idx += sysfs_emit_at(buffer, idx, "\n"); return idx; } -- cgit v1.2.3-58-ga151 From 0b3d0cb7c0bed2fd6454f77ed75e7a662c6efd12 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 8 Dec 2022 23:19:16 +0800 Subject: platform/x86: intel_scu_ipc: fix possible name leak in __intel_scu_ipc_register() In some error paths before device_register(), the names allocated by dev_set_name() are not freed. Move dev_set_name() front to device_register(), so the name can be freed while calling put_device(). Fixes: 54b34aa0a729 ("platform/x86: intel_scu_ipc: Split out SCU IPC functionality from the SCU driver") Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20221208151916.2404977-1-yangyingliang@huawei.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel_scu_ipc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 7cc9089d1e14..e7a3e3402817 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -583,7 +583,6 @@ __intel_scu_ipc_register(struct device *parent, scu->dev.parent = parent; scu->dev.class = &intel_scu_ipc_class; scu->dev.release = intel_scu_ipc_release; - dev_set_name(&scu->dev, "intel_scu_ipc"); if (!request_mem_region(scu_data->mem.start, resource_size(&scu_data->mem), "intel_scu_ipc")) { @@ -612,6 +611,7 @@ __intel_scu_ipc_register(struct device *parent, * After this point intel_scu_ipc_release() takes care of * releasing the SCU IPC resources once refcount drops to zero. */ + dev_set_name(&scu->dev, "intel_scu_ipc"); err = device_register(&scu->dev); if (err) { put_device(&scu->dev); -- cgit v1.2.3-58-ga151 From b0b698b80c56b0712f0d4346d51bf0363ba03068 Mon Sep 17 00:00:00 2001 From: James Hurley Date: Fri, 9 Dec 2022 09:35:19 -0500 Subject: platform/mellanox: mlxbf-pmc: Fix event typo Had a duplicate event typo, so just fixed the 1 character typo. Fixes: 1a218d312e65 ("platform/mellanox: mlxbf-pmc: Add Mellanox BlueField PMC driver") Signed-off-by: James Hurley Reviewed-by: David Thompson Reviewed-by: Shravan Kumar Ramani Link: https://lore.kernel.org/r/aadacdbbd3186c55e74ea9456fe011b77938eb6c.1670535330.git.jahurley@nvidia.com Signed-off-by: Hans de Goede --- drivers/platform/mellanox/mlxbf-pmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c index 65b4a819f1bd..c2c9b0d3244c 100644 --- a/drivers/platform/mellanox/mlxbf-pmc.c +++ b/drivers/platform/mellanox/mlxbf-pmc.c @@ -358,7 +358,7 @@ static const struct mlxbf_pmc_events mlxbf_pmc_hnfnet_events[] = { { 0x32, "DDN_DIAG_W_INGRESS" }, { 0x33, "DDN_DIAG_C_INGRESS" }, { 0x34, "DDN_DIAG_CORE_SENT" }, - { 0x35, "NDN_DIAG_S_OUT_OF_CRED" }, + { 0x35, "NDN_DIAG_N_OUT_OF_CRED" }, { 0x36, "NDN_DIAG_S_OUT_OF_CRED" }, { 0x37, "NDN_DIAG_E_OUT_OF_CRED" }, { 0x38, "NDN_DIAG_W_OUT_OF_CRED" }, -- cgit v1.2.3-58-ga151