Commit 66fd6d0b authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'platform-drivers-x86-v6.9-1' of...

Merge tag 'platform-drivers-x86-v6.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86

Pull x86 platform driver updates from Ilpo Järvinen:

 - New acer-wmi HW support

 - Support for new revision of amd/pmf heartbeat notify

 - Correctly handle asus-wmi HW without LEDs

 - fujitsu-laptop battery charge control support

 - Support for new hp-wmi thermal profiles

 - Support ideapad-laptop refresh rate key

 - Put intel/pmc AI accelerator (GNA) into D3 if it has no driver to
   allow entry into low-power modes, and temporarily removed Lunar Lake
   SSRAM support due to breaking FW changes causing probe fail (further
   breaking FW changes are still pending)

 - Report pmc/punit_atom devices that prevent reacing low power levels

 - Surface Fan speed function support

 - Support for more sperial keys and complete the list of models with
   non-standard fan registers in thinkpad_acpi

 - New DMI touchscreen HW support

 - Continued modernization efforts of wmi

 - Removal of obsoleted ledtrig-audio call and the related dependency

 - Debug & metrics interface improvements

 - Miscellaneous cleanups / fixes / improvements

* tag 'platform-drivers-x86-v6.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (87 commits)
  platform/x86/intel/pmc: Improve PKGC residency counters debug
  platform/x86: asus-wmi: Consider device is absent when the read is ~0
  Documentation/x86/amd/hsmp: Updating urls
  platform/mellanox: mlxreg-hotplug: Remove redundant NULL-check
  platform/x86/amd/pmf: Update sps power thermals according to the platform-profiles
  platform/x86/amd/pmf: Add support to get sps default APTS index values
  platform/x86/amd/pmf: Add support to get APTS index numbers for static slider
  platform/x86/amd/pmf: Add support to notify sbios heart beat event
  platform/x86/amd/pmf: Add support to get sbios requests in PMF driver
  platform/x86/amd/pmf: Disable debugfs support for querying power thermals
  platform/x86/amd/pmf: Differentiate PMF ACPI versions
  x86/platform/atom: Check state of Punit managed devices on s2idle
  platform/x86: pmc_atom: Check state of PMC clocks on s2idle
  platform/x86: pmc_atom: Check state of PMC managed devices on s2idle
  platform/x86: pmc_atom: Annotate d3_sts register bit defines
  clk: x86: Move clk-pmc-atom register defines to include/linux/platform_data/x86/pmc_atom.h
  platform/x86: make fw_attr_class constant
  platform/x86/intel/tpmi: Change vsec offset to u64
  platform/x86: intel_scu_pcidrv: Remove unused intel-mid.h
  platform/x86: intel_scu_wdt: Remove unused intel-mid.h
  ...
parents f5c31bcf 16f8091b
......@@ -444,7 +444,9 @@ event code Key Notes
0x1008 0x07 FN+F8 IBM: toggle screen expand
Lenovo: configure UltraNav,
or toggle screen expand
or toggle screen expand.
On newer platforms (2024+)
replaced by 0x131f (see below)
0x1009 0x08 FN+F9 -
......@@ -504,6 +506,9 @@ event code Key Notes
0x1019 0x18 unknown
0x131f ... FN+F8 Platform Mode change.
Implemented in driver.
... ... ...
0x1020 0x1F unknown
......
......@@ -13,7 +13,8 @@ set of mailbox registers.
More details on the interface can be found in chapter
"7 Host System Management Port (HSMP)" of the family/model PPR
Eg: https://www.amd.com/system/files/TechDocs/55898_B1_pub_0.50.zip
Eg: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/programmer-references/55898_B1_pub_0_50.zip
HSMP interface is supported on EPYC server CPU models only.
......@@ -97,8 +98,8 @@ what happened. The transaction returns 0 on success.
More details on the interface and message definitions can be found in chapter
"7 Host System Management Port (HSMP)" of the respective family/model PPR
eg: https://www.amd.com/system/files/TechDocs/55898_B1_pub_0.50.zip
eg: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/programmer-references/55898_B1_pub_0_50.zip
User space C-APIs are made available by linking against the esmi library,
which is provided by the E-SMS project https://developer.amd.com/e-sms/.
which is provided by the E-SMS project https://www.amd.com/en/developer/e-sms.html.
See: https://github.com/amd/esmi_ib_library
......@@ -93,4 +93,7 @@ _WED ACPI method
----------------
Used to retrieve additional WMI event data, its single parameter is a integer
holding the notification ID of the event.
holding the notification ID of the event. This method should be evaluated every
time an ACPI notification is received, since some ACPI implementations use a
queue to store WMI event data items. This queue will overflow after a couple
of WMI events are received without retrieving the associated WMI event data.
......@@ -7,6 +7,9 @@
* Copyright (c) 2015, Intel Corporation.
*/
#define pr_fmt(fmt) "punit_atom: " fmt
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
......@@ -117,6 +120,51 @@ static void punit_dbgfs_unregister(void)
debugfs_remove_recursive(punit_dbg_file);
}
#if defined(CONFIG_ACPI) && defined(CONFIG_SUSPEND)
static const struct punit_device *punit_dev;
static void punit_s2idle_check(void)
{
const struct punit_device *punit_devp;
u32 punit_pwr_status, dstate;
int status;
for (punit_devp = punit_dev; punit_devp->name; punit_devp++) {
/* Skip MIO, it is on till the very last moment */
if (punit_devp->reg == MIO_SS_PM)
continue;
status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
punit_devp->reg, &punit_pwr_status);
if (status) {
pr_err("%s read failed\n", punit_devp->name);
} else {
dstate = (punit_pwr_status >> punit_devp->sss_pos) & 3;
if (!dstate)
pr_err("%s is in D0 prior to s2idle\n", punit_devp->name);
}
}
}
static struct acpi_s2idle_dev_ops punit_s2idle_ops = {
.check = punit_s2idle_check,
};
static void punit_s2idle_check_register(struct punit_device *punit_device)
{
punit_dev = punit_device;
acpi_register_lps0_dev(&punit_s2idle_ops);
}
static void punit_s2idle_check_unregister(void)
{
acpi_unregister_lps0_dev(&punit_s2idle_ops);
}
#else
static void punit_s2idle_check_register(struct punit_device *punit_device) {}
static void punit_s2idle_check_unregister(void) {}
#endif
#define X86_MATCH(model, data) \
X86_MATCH_VENDOR_FAM_MODEL_FEATURE(INTEL, 6, INTEL_FAM6_##model, \
X86_FEATURE_MWAIT, data)
......@@ -131,19 +179,23 @@ MODULE_DEVICE_TABLE(x86cpu, intel_punit_cpu_ids);
static int __init punit_atom_debug_init(void)
{
struct punit_device *punit_device;
const struct x86_cpu_id *id;
id = x86_match_cpu(intel_punit_cpu_ids);
if (!id)
return -ENODEV;
punit_dbgfs_register((struct punit_device *)id->driver_data);
punit_device = (struct punit_device *)id->driver_data;
punit_dbgfs_register(punit_device);
punit_s2idle_check_register(punit_device);
return 0;
}
static void __exit punit_atom_debug_exit(void)
{
punit_s2idle_check_unregister();
punit_dbgfs_unregister();
}
......
......@@ -11,23 +11,12 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/platform_data/x86/clk-pmc-atom.h>
#include <linux/platform_data/x86/pmc_atom.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#define PLT_CLK_NAME_BASE "pmc_plt_clk"
#define PMC_CLK_CTL_OFFSET 0x60
#define PMC_CLK_CTL_SIZE 4
#define PMC_CLK_NUM 6
#define PMC_CLK_CTL_GATED_ON_D3 0x0
#define PMC_CLK_CTL_FORCE_ON 0x1
#define PMC_CLK_CTL_FORCE_OFF 0x2
#define PMC_CLK_CTL_RESERVED 0x3
#define PMC_MASK_CLK_CTL GENMASK(1, 0)
#define PMC_MASK_CLK_FREQ BIT(2)
#define PMC_CLK_FREQ_XTAL (0 << 2) /* 25 MHz */
#define PMC_CLK_FREQ_PLL (1 << 2) /* 19.2 MHz */
struct clk_plt_fixed {
struct clk_hw *clk;
struct clk_lookup *lookup;
......
......@@ -1600,6 +1600,7 @@ static struct wmi_driver dell_smm_wmi_driver = {
},
.id_table = dell_smm_wmi_id_table,
.probe = dell_smm_wmi_probe,
.no_singleton = true,
};
/*
......
......@@ -463,7 +463,7 @@ static ssize_t large_icm_show(struct device *dev,
if (res.a0)
return -EPERM;
return snprintf(buf, PAGE_SIZE, "0x%lx", res.a1);
return sysfs_emit(buf, "0x%lx", res.a1);
}
static ssize_t large_icm_store(struct device *dev,
......@@ -581,7 +581,7 @@ static ssize_t opn_show(struct device *dev,
}
mutex_unlock(&mfg_ops_lock);
return snprintf(buf, PAGE_SIZE, "%s", (char *)opn_data);
return sysfs_emit(buf, "%s", (char *)opn_data);
}
static ssize_t opn_store(struct device *dev,
......@@ -632,7 +632,7 @@ static ssize_t sku_show(struct device *dev,
}
mutex_unlock(&mfg_ops_lock);
return snprintf(buf, PAGE_SIZE, "%s", (char *)sku_data);
return sysfs_emit(buf, "%s", (char *)sku_data);
}
static ssize_t sku_store(struct device *dev,
......@@ -683,7 +683,7 @@ static ssize_t modl_show(struct device *dev,
}
mutex_unlock(&mfg_ops_lock);
return snprintf(buf, PAGE_SIZE, "%s", (char *)modl_data);
return sysfs_emit(buf, "%s", (char *)modl_data);
}
static ssize_t modl_store(struct device *dev,
......@@ -734,7 +734,7 @@ static ssize_t sn_show(struct device *dev,
}
mutex_unlock(&mfg_ops_lock);
return snprintf(buf, PAGE_SIZE, "%s", (char *)sn_data);
return sysfs_emit(buf, "%s", (char *)sn_data);
}
static ssize_t sn_store(struct device *dev,
......@@ -785,7 +785,7 @@ static ssize_t uuid_show(struct device *dev,
}
mutex_unlock(&mfg_ops_lock);
return snprintf(buf, PAGE_SIZE, "%s", (char *)uuid_data);
return sysfs_emit(buf, "%s", (char *)uuid_data);
}
static ssize_t uuid_store(struct device *dev,
......@@ -836,7 +836,7 @@ static ssize_t rev_show(struct device *dev,
}
mutex_unlock(&mfg_ops_lock);
return snprintf(buf, PAGE_SIZE, "%s", (char *)rev_data);
return sysfs_emit(buf, "%s", (char *)rev_data);
}
static ssize_t rev_store(struct device *dev,
......
This diff is collapsed.
......@@ -348,20 +348,6 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv,
u32 regval, bit;
int ret;
/*
* Validate if item related to received signal type is valid.
* It should never happen, excepted the situation when some
* piece of hardware is broken. In such situation just produce
* error message and return. Caller must continue to handle the
* signals from other devices if any.
*/
if (unlikely(!item)) {
dev_err(priv->dev, "False signal: at offset:mask 0x%02x:0x%02x.\n",
item->reg, item->mask);
return;
}
/* Mask event. */
ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_MASK_OFF,
0);
......
......@@ -74,6 +74,12 @@ static const struct software_node ssam_node_tmp_pprof = {
.parent = &ssam_node_root,
};
/* Fan speed function. */
static const struct software_node ssam_node_fan_speed = {
.name = "ssam:01:05:01:01:01",
.parent = &ssam_node_root,
};
/* Tablet-mode switch via KIP subsystem. */
static const struct software_node ssam_node_kip_tablet_switch = {
.name = "ssam:01:0e:01:00:01",
......@@ -305,6 +311,7 @@ static const struct software_node *ssam_node_group_sp9[] = {
&ssam_node_bat_ac,
&ssam_node_bat_main,
&ssam_node_tmp_pprof,
&ssam_node_fan_speed,
&ssam_node_pos_tablet_switch,
&ssam_node_hid_kip_keyboard,
&ssam_node_hid_kip_penstash,
......
......@@ -56,8 +56,6 @@ config HUAWEI_WMI
depends on INPUT
select INPUT_SPARSEKMAP
select LEDS_CLASS
select LEDS_TRIGGERS
select LEDS_TRIGGER_AUDIO
select NEW_LEDS
help
This driver provides support for Huawei WMI hotkeys, battery charge
......@@ -269,8 +267,6 @@ config ASUS_WMI
select INPUT_SPARSEKMAP
select LEDS_CLASS
select NEW_LEDS
select LEDS_TRIGGERS
select LEDS_TRIGGER_AUDIO
select ACPI_PLATFORM_PROFILE
help
Say Y here if you have a WMI aware Asus laptop (like Eee PCs or new
......@@ -374,6 +370,7 @@ config FUJITSU_LAPTOP
depends on ACPI
depends on INPUT
depends on BACKLIGHT_CLASS_DEVICE
depends on ACPI_BATTERY
depends on ACPI_VIDEO || ACPI_VIDEO = n
select INPUT_SPARSEKMAP
select NEW_LEDS
......@@ -507,8 +504,6 @@ config THINKPAD_ACPI
select NVRAM
select NEW_LEDS
select LEDS_CLASS
select LEDS_TRIGGERS
select LEDS_TRIGGER_AUDIO
help
This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
support for Fn-Fx key combinations, Bluetooth control, video
......
......@@ -276,6 +276,7 @@ static bool has_type_aa;
static u16 commun_func_bitmap;
static u8 commun_fn_key_number;
static bool cycle_gaming_thermal_profile = true;
static bool predator_v4;
module_param(mailled, int, 0444);
module_param(brightness, int, 0444);
......@@ -284,6 +285,7 @@ module_param(force_series, int, 0444);
module_param(force_caps, int, 0444);
module_param(ec_raw_mode, bool, 0444);
module_param(cycle_gaming_thermal_profile, bool, 0644);
module_param(predator_v4, bool, 0444);
MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
......@@ -292,6 +294,8 @@ MODULE_PARM_DESC(force_caps, "Force the capability bitmask to this value");
MODULE_PARM_DESC(ec_raw_mode, "Enable EC raw mode");
MODULE_PARM_DESC(cycle_gaming_thermal_profile,
"Set thermal mode key in cycle mode. Disabling it sets the mode key in turbo toggle mode");
MODULE_PARM_DESC(predator_v4,
"Enable features for predator laptops that use predator sense v4");
struct acer_data {
int mailled;
......@@ -584,6 +588,15 @@ static const struct dmi_system_id acer_quirks[] __initconst = {
},
.driver_data = &quirk_acer_predator_v4,
},
{
.callback = dmi_matched,
.ident = "Acer Predator PH16-71",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Predator PH16-71"),
},
.driver_data = &quirk_acer_predator_v4,
},
{
.callback = set_force_caps,
.ident = "Acer Aspire Switch 10E SW3-016",
......@@ -725,7 +738,9 @@ enum acer_predator_v4_thermal_profile_wmi {
/* Find which quirks are needed for a particular vendor/ model pair */
static void __init find_quirks(void)
{
if (!force_series) {
if (predator_v4) {
quirks = &quirk_acer_predator_v4;
} else if (!force_series) {
dmi_check_system(acer_quirks);
dmi_check_system(non_acer_quirks);
} else if (force_series == 2490) {
......
......@@ -8,7 +8,7 @@ source "drivers/platform/x86/amd/pmc/Kconfig"
config AMD_HSMP
tristate "AMD HSMP Driver"
depends on AMD_NB && X86_64
depends on AMD_NB && X86_64 && ACPI
help
The driver provides a way for user space tools to monitor and manage
system management functionality on EPYC server CPUs from AMD.
......
This diff is collapsed.
......@@ -90,12 +90,96 @@ static int apmf_if_call_store_buffer(struct amd_pmf_dev *pdev, int fn, void *des
return err;
}
static union acpi_object *apts_if_call(struct amd_pmf_dev *pdev, u32 state_index)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_handle ahandle = ACPI_HANDLE(pdev->dev);
struct acpi_object_list apts_if_arg_list;
union acpi_object apts_if_args[3];
acpi_status status;
apts_if_arg_list.count = 3;
apts_if_arg_list.pointer = &apts_if_args[0];
apts_if_args[0].type = ACPI_TYPE_INTEGER;
apts_if_args[0].integer.value = 1;
apts_if_args[1].type = ACPI_TYPE_INTEGER;
apts_if_args[1].integer.value = state_index;
apts_if_args[2].type = ACPI_TYPE_INTEGER;
apts_if_args[2].integer.value = 0;
status = acpi_evaluate_object(ahandle, "APTS", &apts_if_arg_list, &buffer);
if (ACPI_FAILURE(status)) {
dev_err(pdev->dev, "APTS state_idx:%u call failed\n", state_index);
kfree(buffer.pointer);
return NULL;
}
return buffer.pointer;
}
static int apts_if_call_store_buffer(struct amd_pmf_dev *pdev,
u32 index, void *data, size_t out_sz)
{
union acpi_object *info;
size_t size;
int err = 0;
info = apts_if_call(pdev, index);
if (!info)
return -EIO;
if (info->type != ACPI_TYPE_BUFFER) {
dev_err(pdev->dev, "object is not a buffer\n");
err = -EINVAL;
goto out;
}
size = *(u16 *)info->buffer.pointer;
if (info->buffer.length < size) {
dev_err(pdev->dev, "buffer smaller than header size %u < %zu\n",
info->buffer.length, size);
err = -EINVAL;
goto out;
}
if (size < out_sz) {
dev_err(pdev->dev, "buffer too small %zu\n", size);
err = -EINVAL;
goto out;
}
memcpy(data, info->buffer.pointer, out_sz);
out:
kfree(info);
return err;
}
int is_apmf_func_supported(struct amd_pmf_dev *pdev, unsigned long index)
{
/* If bit-n is set, that indicates function n+1 is supported */
return !!(pdev->supported_func & BIT(index - 1));
}
int apts_get_static_slider_granular_v2(struct amd_pmf_dev *pdev,
struct amd_pmf_apts_granular_output *data, u32 apts_idx)
{
if (!is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR))
return -EINVAL;
return apts_if_call_store_buffer(pdev, apts_idx, data, sizeof(*data));
}
int apmf_get_static_slider_granular_v2(struct amd_pmf_dev *pdev,
struct apmf_static_slider_granular_output_v2 *data)
{
if (!is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR))
return -EINVAL;
return apmf_if_call_store_buffer(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR,
data, sizeof(*data));
}
int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev,
struct apmf_static_slider_granular_output *data)
{
......@@ -140,6 +224,43 @@ static void apmf_sbios_heartbeat_notify(struct work_struct *work)
kfree(info);
}
int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag)
{
struct sbios_hb_event_v2 args = { };
struct acpi_buffer params;
union acpi_object *info;
args.size = sizeof(args);
switch (flag) {
case ON_LOAD:
args.load = 1;
break;
case ON_UNLOAD:
args.unload = 1;
break;
case ON_SUSPEND:
args.suspend = 1;
break;
case ON_RESUME:
args.resume = 1;
break;
default:
dev_dbg(dev->dev, "Failed to send v2 heartbeat event, flag:0x%x\n", flag);
return -EINVAL;
}
params.length = sizeof(args);
params.pointer = &args;
info = apmf_if_call(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2, &params);
if (!info)
return -EIO;
kfree(info);
return 0;
}
int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx)
{
union acpi_object *info;
......@@ -166,6 +287,11 @@ int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data
return apmf_if_call_store_buffer(pdev, APMF_FUNC_AUTO_MODE, data, sizeof(*data));
}
int apmf_get_sbios_requests_v2(struct amd_pmf_dev *pdev, struct apmf_sbios_req_v2 *req)
{
return apmf_if_call_store_buffer(pdev, APMF_FUNC_SBIOS_REQUESTS, req, sizeof(*req));
}
int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req)
{
return apmf_if_call_store_buffer(pdev, APMF_FUNC_SBIOS_REQUESTS,
......@@ -218,8 +344,10 @@ static int apmf_if_verify_interface(struct amd_pmf_dev *pdev)
return err;
pdev->supported_func = output.supported_functions;
dev_dbg(pdev->dev, "supported functions:0x%x notifications:0x%x\n",
output.supported_functions, output.notification_mask);
dev_dbg(pdev->dev, "supported functions:0x%x notifications:0x%x version:%u\n",
output.supported_functions, output.notification_mask, output.version);
pdev->pmf_if_version = output.version;
return 0;
}
......@@ -320,7 +448,7 @@ void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
{
acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
if (pmf_dev->hb_interval)
if (pmf_dev->hb_interval && pmf_dev->pmf_if_version == PMF_IF_V1)
cancel_delayed_work_sync(&pmf_dev->heart_beat);
if (is_apmf_func_supported(pmf_dev, APMF_FUNC_AUTO_MODE) &&
......@@ -344,7 +472,7 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
goto out;
}
if (pmf_dev->hb_interval) {
if (pmf_dev->hb_interval && pmf_dev->pmf_if_version == PMF_IF_V1) {
/* send heartbeats only if the interval is not zero */
INIT_DELAYED_WORK(&pmf_dev->heart_beat, apmf_sbios_heartbeat_notify);
schedule_delayed_work(&pmf_dev->heart_beat, 0);
......
......@@ -113,8 +113,9 @@ static void amd_pmf_dbgfs_unregister(struct amd_pmf_dev *dev)
static void amd_pmf_dbgfs_register(struct amd_pmf_dev *dev)
{
dev->dbgfs_dir = debugfs_create_dir("amd_pmf", NULL);
debugfs_create_file("current_power_limits", 0644, dev->dbgfs_dir, dev,
&current_power_limits_fops);
if (dev->pmf_if_version == PMF_IF_V1)
debugfs_create_file("current_power_limits", 0644, dev->dbgfs_dir, dev,
&current_power_limits_fops);
}
int amd_pmf_get_power_source(void)
......@@ -299,6 +300,9 @@ static int amd_pmf_suspend_handler(struct device *dev)
if (pdev->smart_pc_enabled)
cancel_delayed_work_sync(&pdev->pb_work);
if (is_apmf_func_supported(pdev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
amd_pmf_notify_sbios_heartbeat_event_v2(pdev, ON_SUSPEND);
return 0;
}
......@@ -313,6 +317,9 @@ static int amd_pmf_resume_handler(struct device *dev)
return ret;
}
if (is_apmf_func_supported(pdev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
amd_pmf_notify_sbios_heartbeat_event_v2(pdev, ON_RESUME);
if (pdev->smart_pc_enabled)
schedule_delayed_work(&pdev->pb_work, msecs_to_jiffies(2000));
......@@ -443,6 +450,8 @@ static int amd_pmf_probe(struct platform_device *pdev)
amd_pmf_dbgfs_register(dev);
amd_pmf_init_features(dev);
apmf_install_handler(dev);
if (is_apmf_func_supported(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_LOAD);
dev_info(dev->dev, "registered PMF device successfully\n");
......@@ -454,6 +463,8 @@ static void amd_pmf_remove(struct platform_device *pdev)
struct amd_pmf_dev *dev = platform_get_drvdata(pdev);
amd_pmf_deinit_features(dev);
if (is_apmf_func_supported(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_UNLOAD);
apmf_acpi_deinit(dev);
amd_pmf_dbgfs_unregister(dev);
mutex_destroy(&dev->lock);
......
......@@ -17,7 +17,11 @@
#define POLICY_BUF_MAX_SZ 0x4b000
#define POLICY_SIGN_COOKIE 0x31535024
#define POLICY_COOKIE_OFFSET 0x10
#define POLICY_COOKIE_LEN 0x14
struct cookie_header {
u32 sign;
u32 length;
} __packed;
/* APMF Functions */
#define APMF_FUNC_VERIFY_INTERFACE 0
......@@ -30,6 +34,7 @@
#define APMF_FUNC_STATIC_SLIDER_GRANULAR 9
#define APMF_FUNC_DYN_SLIDER_AC 11
#define APMF_FUNC_DYN_SLIDER_DC 12
#define APMF_FUNC_SBIOS_HEARTBEAT_V2 16
/* Message Definitions */
#define SET_SPL 0x03 /* SPL: Sustained Power Limit */
......@@ -50,6 +55,8 @@
#define GET_STT_LIMIT_APU 0x20
#define GET_STT_LIMIT_HS2 0x21
#define SET_P3T 0x23 /* P3T: Peak Package Power Limit */
#define SET_PMF_PPT 0x25
#define SET_PMF_PPT_APU_ONLY 0x26
/* OS slider update notification */
#define DC_BEST_PERF 0
......@@ -83,6 +90,47 @@
#define TA_OUTPUT_RESERVED_MEM 906
#define MAX_OPERATION_PARAMS 4
#define PMF_IF_V1 1
#define PMF_IF_V2 2
#define APTS_MAX_STATES 16
/* APTS PMF BIOS Interface */
struct amd_pmf_apts_output {
u16 table_version;
u32 fan_table_idx;
u32 pmf_ppt;
u32 ppt_pmf_apu_only;
u32 stt_min_limit;
u8 stt_skin_temp_limit_apu;
u8 stt_skin_temp_limit_hs2;
} __packed;
struct amd_pmf_apts_granular_output {
u16 size;
struct amd_pmf_apts_output val;
} __packed;
struct amd_pmf_apts_granular {
u16 size;
struct amd_pmf_apts_output val[APTS_MAX_STATES];
};
struct sbios_hb_event_v2 {
u16 size;
u8 load;
u8 unload;
u8 suspend;
u8 resume;
} __packed;
enum sbios_hb_v2 {
ON_LOAD,
ON_UNLOAD,
ON_SUSPEND,
ON_RESUME,
};
/* AMD PMF BIOS interfaces */
struct apmf_verify_interface {
u16 size;
......@@ -114,6 +162,18 @@ struct apmf_sbios_req {
u8 skin_temp_hs2;
} __packed;
struct apmf_sbios_req_v2 {
u16 size;
u32 pending_req;
u8 rsd;
u32 ppt_pmf;
u32 ppt_pmf_apu_only;
u32 stt_min_limit;
u8 skin_temp_apu;
u8 skin_temp_hs2;
u32 custom_policy[10];
} __packed;
struct apmf_fan_idx {
u16 size;
u8 fan_ctl_mode;
......@@ -194,6 +254,14 @@ enum power_modes {
POWER_MODE_MAX,
};
enum power_modes_v2 {
POWER_MODE_BEST_PERFORMANCE,
POWER_MODE_BALANCED,
POWER_MODE_BEST_POWER_EFFICIENCY,
POWER_MODE_ENERGY_SAVE,
POWER_MODE_V2_MAX,
};
struct amd_pmf_dev {
void __iomem *regbase;
void __iomem *smu_virt_addr;
......@@ -229,10 +297,15 @@ struct amd_pmf_dev {
struct delayed_work pb_work;
struct pmf_action_table *prev_data;
u64 policy_addr;
void *policy_base;
void __iomem *policy_base;
bool smart_pc_enabled;
u16 pmf_if_version;
};
struct apmf_sps_prop_granular_v2 {
u8 power_states[POWER_SOURCE_MAX][POWER_MODE_V2_MAX];
} __packed;
struct apmf_sps_prop_granular {
u32 fppt;
u32 sppt;
......@@ -254,6 +327,16 @@ struct amd_pmf_static_slider_granular {
struct apmf_sps_prop_granular prop[POWER_SOURCE_MAX][POWER_MODE_MAX];
};
struct apmf_static_slider_granular_output_v2 {
u16 size;
struct apmf_sps_prop_granular_v2 sps_idx;
} __packed;
struct amd_pmf_static_slider_granular_v2 {
u16 size;
struct apmf_sps_prop_granular_v2 sps_idx;
};
struct os_power_slider {
u16 size;
u8 slider_event;
......@@ -585,6 +668,7 @@ int amd_pmf_get_power_source(void);
int apmf_install_handler(struct amd_pmf_dev *pmf_dev);
int apmf_os_power_slider_update(struct amd_pmf_dev *dev, u8 flag);
int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer);
int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag);
/* SPS Layer */
int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
......@@ -602,6 +686,10 @@ const char *amd_pmf_source_as_str(unsigned int state);
int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx);
int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf);
int apmf_get_static_slider_granular_v2(struct amd_pmf_dev *dev,
struct apmf_static_slider_granular_output_v2 *data);
int apts_get_static_slider_granular_v2(struct amd_pmf_dev *pdev,
struct amd_pmf_apts_granular_output *data, u32 apts_idx);
/* Auto Mode Layer */
int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data);
......@@ -609,6 +697,7 @@ void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req);
int apmf_get_sbios_requests_v2(struct amd_pmf_dev *pdev, struct apmf_sbios_req_v2 *req);
void amd_pmf_update_2_cql(struct amd_pmf_dev *dev, bool is_cql_event);
int amd_pmf_reset_amt(struct amd_pmf_dev *dev);
......
......@@ -10,9 +10,27 @@
#include "pmf.h"
static struct amd_pmf_static_slider_granular_v2 config_store_v2;
static struct amd_pmf_static_slider_granular config_store;
static struct amd_pmf_apts_granular apts_config_store;
#ifdef CONFIG_AMD_PMF_DEBUG
static const char *slider_v2_as_str(unsigned int state)
{
switch (state) {
case POWER_MODE_BEST_PERFORMANCE:
return "Best Performance";
case POWER_MODE_BALANCED:
return "Balanced";
case POWER_MODE_BEST_POWER_EFFICIENCY:
return "Best Power Efficiency";
case POWER_MODE_ENERGY_SAVE:
return "Energy Save";
default:
return "Unknown Power Mode";
}
}
static const char *slider_as_str(unsigned int state)
{
switch (state) {
......@@ -63,10 +81,88 @@ static void amd_pmf_dump_sps_defaults(struct amd_pmf_static_slider_granular *dat
pr_debug("Static Slider Data - END\n");
}
static void amd_pmf_dump_sps_defaults_v2(struct amd_pmf_static_slider_granular_v2 *data)
{
unsigned int i, j;
pr_debug("Static Slider APTS state index data - BEGIN");
pr_debug("size: %u\n", data->size);
for (i = 0; i < POWER_SOURCE_MAX; i++)
for (j = 0; j < POWER_MODE_V2_MAX; j++)
pr_debug("%s %s: %u\n", amd_pmf_source_as_str(i), slider_v2_as_str(j),
data->sps_idx.power_states[i][j]);
pr_debug("Static Slider APTS state index data - END\n");
}
static void amd_pmf_dump_apts_sps_defaults(struct amd_pmf_apts_granular *info)
{
int i;
pr_debug("Static Slider APTS index default values data - BEGIN");
for (i = 0; i < APTS_MAX_STATES; i++) {
pr_debug("Table Version[%d] = %u\n", i, info->val[i].table_version);
pr_debug("Fan Index[%d] = %u\n", i, info->val[i].fan_table_idx);
pr_debug("PPT[%d] = %u\n", i, info->val[i].pmf_ppt);
pr_debug("PPT APU[%d] = %u\n", i, info->val[i].ppt_pmf_apu_only);
pr_debug("STT Min[%d] = %u\n", i, info->val[i].stt_min_limit);
pr_debug("STT APU[%d] = %u\n", i, info->val[i].stt_skin_temp_limit_apu);
pr_debug("STT HS2[%d] = %u\n", i, info->val[i].stt_skin_temp_limit_hs2);
}
pr_debug("Static Slider APTS index default values data - END");
}
#else
static void amd_pmf_dump_sps_defaults(struct amd_pmf_static_slider_granular *data) {}
static void amd_pmf_dump_sps_defaults_v2(struct amd_pmf_static_slider_granular_v2 *data) {}
static void amd_pmf_dump_apts_sps_defaults(struct amd_pmf_apts_granular *info) {}
#endif
static void amd_pmf_load_apts_defaults_sps_v2(struct amd_pmf_dev *pdev)
{
struct amd_pmf_apts_granular_output output;
struct amd_pmf_apts_output *ps;
int i;
memset(&apts_config_store, 0, sizeof(apts_config_store));
ps = apts_config_store.val;
for (i = 0; i < APTS_MAX_STATES; i++) {
apts_get_static_slider_granular_v2(pdev, &output, i);
ps[i].table_version = output.val.table_version;
ps[i].fan_table_idx = output.val.fan_table_idx;
ps[i].pmf_ppt = output.val.pmf_ppt;
ps[i].ppt_pmf_apu_only = output.val.ppt_pmf_apu_only;
ps[i].stt_min_limit = output.val.stt_min_limit;
ps[i].stt_skin_temp_limit_apu = output.val.stt_skin_temp_limit_apu;
ps[i].stt_skin_temp_limit_hs2 = output.val.stt_skin_temp_limit_hs2;
}
amd_pmf_dump_apts_sps_defaults(&apts_config_store);
}
static void amd_pmf_load_defaults_sps_v2(struct amd_pmf_dev *dev)
{
struct apmf_static_slider_granular_output_v2 output;
unsigned int i, j;
memset(&config_store_v2, 0, sizeof(config_store_v2));
apmf_get_static_slider_granular_v2(dev, &output);
config_store_v2.size = output.size;
for (i = 0; i < POWER_SOURCE_MAX; i++)
for (j = 0; j < POWER_MODE_V2_MAX; j++)
config_store_v2.sps_idx.power_states[i][j] =
output.sps_idx.power_states[i][j];
amd_pmf_dump_sps_defaults_v2(&config_store_v2);
}
static void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev)
{
struct apmf_static_slider_granular_output output;
......@@ -94,6 +190,19 @@ static void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev)
amd_pmf_dump_sps_defaults(&config_store);
}
static void amd_pmf_update_slider_v2(struct amd_pmf_dev *dev, int idx)
{
amd_pmf_send_cmd(dev, SET_PMF_PPT, false, apts_config_store.val[idx].pmf_ppt, NULL);
amd_pmf_send_cmd(dev, SET_PMF_PPT_APU_ONLY, false,
apts_config_store.val[idx].ppt_pmf_apu_only, NULL);
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
apts_config_store.val[idx].stt_min_limit, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
apts_config_store.val[idx].stt_skin_temp_limit_apu, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
apts_config_store.val[idx].stt_skin_temp_limit_hs2, NULL);
}
void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
struct amd_pmf_static_slider_granular *table)
{
......@@ -126,6 +235,32 @@ void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
}
}
static int amd_pmf_update_sps_power_limits_v2(struct amd_pmf_dev *pdev, int pwr_mode)
{
int src, index;
src = amd_pmf_get_power_source();
switch (pwr_mode) {
case POWER_MODE_PERFORMANCE:
index = config_store_v2.sps_idx.power_states[src][POWER_MODE_BEST_PERFORMANCE];
amd_pmf_update_slider_v2(pdev, index);
break;
case POWER_MODE_BALANCED_POWER:
index = config_store_v2.sps_idx.power_states[src][POWER_MODE_BALANCED];
amd_pmf_update_slider_v2(pdev, index);
break;
case POWER_MODE_POWER_SAVER:
index = config_store_v2.sps_idx.power_states[src][POWER_MODE_BEST_POWER_EFFICIENCY];
amd_pmf_update_slider_v2(pdev, index);
break;
default:
return -EINVAL;
}
return 0;
}
int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf)
{
int mode;
......@@ -134,6 +269,9 @@ int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf)
if (mode < 0)
return mode;
if (pmf->pmf_if_version == PMF_IF_V2)
return amd_pmf_update_sps_power_limits_v2(pmf, mode);
amd_pmf_update_slider(pmf, SLIDER_OP_SET, mode, NULL);
return 0;
......@@ -256,7 +394,12 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev)
dev->current_profile = PLATFORM_PROFILE_BALANCED;
if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) {
amd_pmf_load_defaults_sps(dev);
if (dev->pmf_if_version == PMF_IF_V2) {
amd_pmf_load_defaults_sps_v2(dev);
amd_pmf_load_apts_defaults_sps_v2(dev);
} else {
amd_pmf_load_defaults_sps(dev);
}
/* update SPS balanced power mode thermals */
amd_pmf_set_sps_power_limits(dev);
......
......@@ -246,19 +246,24 @@ static void amd_pmf_invoke_cmd(struct work_struct *work)
static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev)
{
u32 cookie, length;
struct cookie_header *header;
int res;
cookie = readl(dev->policy_buf + POLICY_COOKIE_OFFSET);
length = readl(dev->policy_buf + POLICY_COOKIE_LEN);
if (dev->policy_sz < POLICY_COOKIE_OFFSET + sizeof(*header))
return -EINVAL;
header = (struct cookie_header *)(dev->policy_buf + POLICY_COOKIE_OFFSET);
if (cookie != POLICY_SIGN_COOKIE || !length) {
if (header->sign != POLICY_SIGN_COOKIE || !header->length) {
dev_dbg(dev->dev, "cookie doesn't match\n");
return -EINVAL;
}
if (dev->policy_sz < header->length + 512)
return -EINVAL;
/* Update the actual length */
dev->policy_sz = length + 512;
dev->policy_sz = header->length + 512;
res = amd_pmf_invoke_cmd_init(dev);
if (res == TA_PMF_TYPE_SUCCESS) {
/* Now its safe to announce that smart pc is enabled */
......@@ -271,7 +276,7 @@ static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev)
} else {
dev_err(dev->dev, "ta invoke cmd init failed err: %x\n", res);
dev->smart_pc_enabled = false;
return res;
return -EIO;
}
return 0;
......@@ -311,8 +316,8 @@ static ssize_t amd_pmf_get_pb_data(struct file *filp, const char __user *buf,
amd_pmf_hex_dump_pb(dev);
ret = amd_pmf_start_policy_engine(dev);
if (ret)
return -EINVAL;
if (ret < 0)
return ret;
return length;
}
......@@ -453,7 +458,7 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev)
goto error;
}
memcpy(dev->policy_buf, dev->policy_base, dev->policy_sz);
memcpy_fromio(dev->policy_buf, dev->policy_base, dev->policy_sz);
amd_pmf_hex_dump_pb(dev);
......
......@@ -101,13 +101,6 @@ module_param(fnlock_default, bool, 0444);
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
#define ASUS_ACPI_UID_ASUSWMI "ASUSWMI"
#define ASUS_ACPI_UID_ATK "ATK"
#define WMI_EVENT_QUEUE_SIZE 0x10
#define WMI_EVENT_QUEUE_END 0x1
#define WMI_EVENT_MASK 0xFFFF
/* The WMI hotkey event value is always the same. */
#define WMI_EVENT_VALUE_ATK 0xFF
#define WMI_EVENT_MASK 0xFFFF
......@@ -219,7 +212,6 @@ struct asus_wmi {
int dsts_id;
int spec;
int sfun;
bool wmi_event_queue;
struct input_dev *inputdev;
struct backlight_device *backlight_device;
......@@ -489,7 +481,17 @@ static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
{
return asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval);
int err;
err = asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval);
if (err)
return err;
if (*retval == ~0)
return -ENODEV;
return 0;
}
static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
......@@ -1620,7 +1622,6 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MICMUTE_LED)) {
asus->micmute_led.name = "platform::micmute";
asus->micmute_led.max_brightness = 1;
asus->micmute_led.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
asus->micmute_led.brightness_set_blocking = micmute_led_set;
asus->micmute_led.default_trigger = "audio-micmute";
......@@ -4020,50 +4021,14 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
static void asus_wmi_notify(u32 value, void *context)
{
struct asus_wmi *asus = context;
int code;
int i;
for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
code = asus_wmi_get_event_code(value);
if (code < 0) {
pr_warn("Failed to get notify code: %d\n", code);
return;
}
if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
return;
int code = asus_wmi_get_event_code(value);
asus_wmi_handle_event_code(code, asus);
/*
* Double check that queue is present:
* ATK (with queue) uses 0xff, ASUSWMI (without) 0xd2.
*/
if (!asus->wmi_event_queue || value != WMI_EVENT_VALUE_ATK)
return;
}
pr_warn("Failed to process event queue, last code: 0x%x\n", code);
}
static int asus_wmi_notify_queue_flush(struct asus_wmi *asus)
{
int code;
int i;
for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
code = asus_wmi_get_event_code(WMI_EVENT_VALUE_ATK);
if (code < 0) {
pr_warn("Failed to get event during flush: %d\n", code);
return code;
}
if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
return 0;
if (code < 0) {
pr_warn("Failed to get notify code: %d\n", code);
return;
}
pr_warn("Failed to flush event queue\n");
return -EIO;
asus_wmi_handle_event_code(code, asus);
}
/* Sysfs **********************************************************************/
......@@ -4303,23 +4268,6 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
asus->dsts_id = ASUS_WMI_METHODID_DSTS;
}
/*
* Some devices can have multiple event codes stored in a queue before
* the module load if it was unloaded intermittently after calling
* the INIT method (enables event handling). The WMI notify handler is
* expected to retrieve all event codes until a retrieved code equals
* queue end marker (One or Ones). Old codes are flushed from the queue
* upon module load. Not enabling this when it should be has minimal
* visible impact so fall back if anything goes wrong.
*/
wmi_uid = wmi_get_acpi_device_uid(asus->driver->event_guid);
if (wmi_uid && !strcmp(wmi_uid, ASUS_ACPI_UID_ATK)) {
dev_info(dev, "Detected ATK, enable event queue\n");
if (!asus_wmi_notify_queue_flush(asus))
asus->wmi_event_queue = true;
}
/* CWAP allow to define the behavior of the Fn+F2 key,
* this method doesn't seems to be present on Eee PCs */
if (asus->driver->quirks->wapf >= 0)
......
......@@ -57,8 +57,6 @@ config DELL_LAPTOP
select POWER_SUPPLY
select LEDS_CLASS
select NEW_LEDS
select LEDS_TRIGGERS
select LEDS_TRIGGER_AUDIO
help
This driver adds support for rfkill and backlight control to Dell
laptops (except for some models covered by the Compal driver).
......@@ -165,7 +163,6 @@ config DELL_WMI
config DELL_WMI_PRIVACY
bool "Dell WMI Hardware Privacy Support"
depends on LEDS_TRIGGER_AUDIO = y || DELL_WMI = LEDS_TRIGGER_AUDIO
depends on DELL_WMI
help
This option adds integration with the "Dell Hardware Privacy"
......
......@@ -2252,7 +2252,6 @@ static int __init dell_init(void)
if (dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE) &&
dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE) &&
!dell_privacy_has_mic_mute()) {
micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev);
if (ret < 0)
goto fail_led;
......@@ -2261,7 +2260,6 @@ static int __init dell_init(void)
if (dell_smbios_find_token(GLOBAL_MUTE_DISABLE) &&
dell_smbios_find_token(GLOBAL_MUTE_ENABLE)) {
mute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MUTE);
ret = led_classdev_register(&platform_device->dev, &mute_led_cdev);
if (ret < 0)
goto fail_backlight;
......
......@@ -882,6 +882,7 @@ static struct wmi_driver dell_wmi_ddv_driver = {
},
.id_table = dell_wmi_ddv_id_table,
.probe = dell_wmi_ddv_probe,
.no_singleton = true,
};
module_wmi_driver(dell_wmi_ddv_driver);
......
......@@ -288,7 +288,6 @@ static int dell_privacy_leds_setup(struct device *dev)
priv->cdev.max_brightness = 1;
priv->cdev.brightness_set_blocking = dell_privacy_micmute_led_set;
priv->cdev.default_trigger = "audio-micmute";
priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
return devm_led_classdev_register(dev, &priv->cdev);
}
......@@ -298,10 +297,6 @@ static int dell_privacy_wmi_probe(struct wmi_device *wdev, const void *context)
struct key_entry *keymap;
int ret, i, j;
ret = wmi_has_guid(DELL_PRIVACY_GUID);
if (!ret)
pr_debug("Unable to detect available Dell privacy devices!\n");
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
......
......@@ -25,7 +25,7 @@ struct wmi_sysman_priv wmi_priv = {
/* reset bios to defaults */
static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"};
static int reset_option = -1;
static struct class *fw_attr_class;
static const struct class *fw_attr_class;
/**
......
......@@ -10,11 +10,11 @@
static DEFINE_MUTEX(fw_attr_lock);
static int fw_attr_inuse;
static struct class firmware_attributes_class = {
static const struct class firmware_attributes_class = {
.name = "firmware-attributes",
};
int fw_attributes_class_get(struct class **fw_attr_class)
int fw_attributes_class_get(const struct class **fw_attr_class)
{
int err;
......
......@@ -5,7 +5,7 @@
#ifndef FW_ATTR_CLASS_H
#define FW_ATTR_CLASS_H
int fw_attributes_class_get(struct class **fw_attr_class);
int fw_attributes_class_get(const struct class **fw_attr_class);
int fw_attributes_class_put(void);
#endif /* FW_ATTR_CLASS_H */
......@@ -49,6 +49,8 @@
#include <linux/kfifo.h>
#include <linux/leds.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <acpi/battery.h>
#include <acpi/video.h>
#define FUJITSU_DRIVER_VERSION "0.6.0"
......@@ -97,6 +99,10 @@
#define BACKLIGHT_OFF (BIT(0) | BIT(1))
#define BACKLIGHT_ON 0
/* FUNC interface - battery control interface */
#define FUNC_S006_METHOD 0x1006
#define CHARGE_CONTROL_RW 0x21
/* Scancodes read from the GIRB register */
#define KEY1_CODE 0x410
#define KEY2_CODE 0x411
......@@ -132,6 +138,7 @@ struct fujitsu_laptop {
spinlock_t fifo_lock;
int flags_supported;
int flags_state;
bool charge_control_supported;
};
static struct acpi_device *fext;
......@@ -164,6 +171,110 @@ static int call_fext_func(struct acpi_device *device,
return value;
}
/* Battery charge control code */
static ssize_t charge_control_end_threshold_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int cc_end_value, s006_cc_return;
int value, ret;
ret = kstrtouint(buf, 10, &value);
if (ret)
return ret;
if (value < 50 || value > 100)
return -EINVAL;
cc_end_value = value * 0x100 + 0x20;
s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD,
CHARGE_CONTROL_RW, cc_end_value, 0x0);
if (s006_cc_return < 0)
return s006_cc_return;
/*
* The S006 0x21 method returns 0x00 in case the provided value
* is invalid.
*/
if (s006_cc_return == 0x00)
return -EINVAL;
return count;
}
static ssize_t charge_control_end_threshold_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int status;
status = call_fext_func(fext, FUNC_S006_METHOD,
CHARGE_CONTROL_RW, 0x21, 0x0);
if (status < 0)
return status;
return sysfs_emit(buf, "%d\n", status);
}
static DEVICE_ATTR_RW(charge_control_end_threshold);
/* ACPI battery hook */
static int fujitsu_battery_add_hook(struct power_supply *battery,
struct acpi_battery_hook *hook)
{
return device_create_file(&battery->dev,
&dev_attr_charge_control_end_threshold);
}
static int fujitsu_battery_remove_hook(struct power_supply *battery,
struct acpi_battery_hook *hook)
{
device_remove_file(&battery->dev,
&dev_attr_charge_control_end_threshold);
return 0;
}
static struct acpi_battery_hook battery_hook = {
.add_battery = fujitsu_battery_add_hook,
.remove_battery = fujitsu_battery_remove_hook,
.name = "Fujitsu Battery Extension",
};
/*
* These functions are intended to be called from acpi_fujitsu_laptop_add and
* acpi_fujitsu_laptop_remove.
*/
static int fujitsu_battery_charge_control_add(struct acpi_device *device)
{
struct fujitsu_laptop *priv = acpi_driver_data(device);
int s006_cc_return;
priv->charge_control_supported = false;
/*
* Check if the S006 0x21 method exists by trying to get the current
* battery charge limit.
*/
s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD,
CHARGE_CONTROL_RW, 0x21, 0x0);
if (s006_cc_return < 0)
return s006_cc_return;
if (s006_cc_return == UNSUPPORTED_CMD)
return -ENODEV;
priv->charge_control_supported = true;
battery_hook_register(&battery_hook);
return 0;
}
static void fujitsu_battery_charge_control_remove(struct acpi_device *device)
{
struct fujitsu_laptop *priv = acpi_driver_data(device);
if (priv->charge_control_supported)
battery_hook_unregister(&battery_hook);
}
/* Hardware access for LCD brightness control */
static int set_lcd_level(struct acpi_device *device, int level)
......@@ -839,6 +950,10 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
if (ret)
goto err_free_fifo;
ret = fujitsu_battery_charge_control_add(device);
if (ret < 0)
pr_warn("Unable to register battery charge control: %d\n", ret);
return 0;
err_free_fifo:
......@@ -851,6 +966,8 @@ static void acpi_fujitsu_laptop_remove(struct acpi_device *device)
{
struct fujitsu_laptop *priv = acpi_driver_data(device);
fujitsu_battery_charge_control_remove(device);
fujitsu_laptop_platform_remove(device);
kfifo_free(&priv->fifo);
......
......@@ -24,7 +24,7 @@ struct bioscfg_priv bioscfg_drv = {
.mutex = __MUTEX_INITIALIZER(bioscfg_drv.mutex),
};
static struct class *fw_attr_class;
static const struct class *fw_attr_class;
ssize_t display_name_language_code_show(struct kobject *kobj,
struct kobj_attribute *attr,
......
......@@ -29,15 +29,19 @@
#include <linux/dmi.h>
MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
MODULE_DESCRIPTION("HP laptop WMI driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
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 HPWMI_BIOS_GUID "5FB7F034-2C63-45E9-BE91-3D44E2C707E4"
#define HP_OMEN_EC_THERMAL_PROFILE_FLAGS_OFFSET 0x62
#define HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET 0x63
#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
......@@ -55,17 +59,25 @@ static const char * const omen_thermal_profile_boards[] = {
"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"
"8917", "8918", "8949", "894A", "89EB", "8BAD", "8A42"
};
/* 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[] = {
static const char * const omen_thermal_profile_force_v0_boards[] = {
"8607", "8746", "8747", "8749", "874A", "8748"
};
/* DMI board names of Omen laptops that have a thermal profile timer which will
* cause the embedded controller to set the thermal profile back to
* "balanced" when reaching zero.
*/
static const char * const omen_timed_thermal_profile_boards[] = {
"8BAD", "8A42"
};
/* DMI Board names of Victus laptops */
static const char * const victus_thermal_profile_boards[] = {
"8A25"
......@@ -182,6 +194,12 @@ enum hp_thermal_profile_omen_v1 {
HP_OMEN_V1_THERMAL_PROFILE_COOL = 0x50,
};
enum hp_thermal_profile_omen_flags {
HP_OMEN_EC_FLAGS_TURBO = 0x04,
HP_OMEN_EC_FLAGS_NOTIMER = 0x02,
HP_OMEN_EC_FLAGS_JUSTSET = 0x01,
};
enum hp_thermal_profile_victus {
HP_VICTUS_THERMAL_PROFILE_DEFAULT = 0x00,
HP_VICTUS_THERMAL_PROFILE_PERFORMANCE = 0x01,
......@@ -449,7 +467,11 @@ static int hp_wmi_get_tablet_mode(void)
static int omen_thermal_profile_set(int mode)
{
char buffer[2] = {0, mode};
/* The Omen Control Center actively sets the first byte of the buffer to
* 255, so let's mimic this behaviour to be as close as possible to
* the original software.
*/
char buffer[2] = {-1, mode};
int ret;
ret = hp_wmi_perform_query(HPWMI_SET_PERFORMANCE_MODE, HPWMI_GM,
......@@ -1201,10 +1223,33 @@ static int platform_profile_omen_get(struct platform_profile_handler *pprof,
return 0;
}
static bool has_omen_thermal_profile_ec_timer(void)
{
const char *board_name = dmi_get_system_info(DMI_BOARD_NAME);
if (!board_name)
return false;
return match_string(omen_timed_thermal_profile_boards,
ARRAY_SIZE(omen_timed_thermal_profile_boards),
board_name) >= 0;
}
inline int omen_thermal_profile_ec_flags_set(enum hp_thermal_profile_omen_flags flags)
{
return ec_write(HP_OMEN_EC_THERMAL_PROFILE_FLAGS_OFFSET, flags);
}
inline int omen_thermal_profile_ec_timer_set(u8 value)
{
return ec_write(HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET, value);
}
static int platform_profile_omen_set(struct platform_profile_handler *pprof,
enum platform_profile_option profile)
{
int err, tp, tp_version;
enum hp_thermal_profile_omen_flags flags = 0;
tp_version = omen_get_thermal_policy_version();
......@@ -1238,6 +1283,20 @@ static int platform_profile_omen_set(struct platform_profile_handler *pprof,
if (err < 0)
return err;
if (has_omen_thermal_profile_ec_timer()) {
err = omen_thermal_profile_ec_timer_set(0);
if (err < 0)
return err;
if (profile == PLATFORM_PROFILE_PERFORMANCE)
flags = HP_OMEN_EC_FLAGS_NOTIMER |
HP_OMEN_EC_FLAGS_TURBO;
err = omen_thermal_profile_ec_flags_set(flags);
if (err < 0)
return err;
}
return 0;
}
......
......@@ -310,7 +310,6 @@ static void huawei_wmi_leds_setup(struct device *dev)
huawei->cdev.max_brightness = 1;
huawei->cdev.brightness_set_blocking = &huawei_wmi_micmute_led_set;
huawei->cdev.default_trigger = "audio-micmute";
huawei->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
huawei->cdev.dev = dev;
huawei->cdev.flags = LED_CORE_SUSPENDRESUME;
......
......@@ -179,7 +179,7 @@ static ssize_t rtl_set_state(struct device *dev,
return ret;
}
static struct bus_type rtl_subsys = {
static const struct bus_type rtl_subsys = {
.name = "ibm_rtl",
.dev_name = "ibm_rtl",
};
......
......@@ -1091,6 +1091,8 @@ static const struct key_entry ideapad_keymap[] = {
{ KE_KEY, 0x07 | IDEAPAD_WMI_KEY, { KEY_HELP } },
{ KE_KEY, 0x0e | IDEAPAD_WMI_KEY, { KEY_PICKUP_PHONE } },
{ KE_KEY, 0x0f | IDEAPAD_WMI_KEY, { KEY_HANGUP_PHONE } },
/* Refresh Rate Toggle (Fn+R) */
{ KE_KEY, 0x10 | IDEAPAD_WMI_KEY, { KEY_REFRESH_RATE_TOGGLE } },
/* Dark mode toggle */
{ KE_KEY, 0x13 | IDEAPAD_WMI_KEY, { KEY_PROG1 } },
/* Sound profile switch */
......@@ -1100,7 +1102,7 @@ static const struct key_entry ideapad_keymap[] = {
/* Lenovo Support */
{ KE_KEY, 0x27 | IDEAPAD_WMI_KEY, { KEY_HELP } },
/* Refresh Rate Toggle */
{ KE_KEY, 0x0a | IDEAPAD_WMI_KEY, { KEY_DISPLAYTOGGLE } },
{ KE_KEY, 0x0a | IDEAPAD_WMI_KEY, { KEY_REFRESH_RATE_TOGGLE } },
{ KE_END },
};
......
......@@ -383,7 +383,7 @@ int ifs_load_firmware(struct device *dev)
unsigned int expected_size;
const struct firmware *fw;
char scan_path[64];
int ret = -EINVAL;
int ret;
snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.scan",
test->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model,
......
......@@ -23,6 +23,12 @@
/* Max retries on the same chunk */
#define MAX_IFS_RETRIES 5
struct run_params {
struct ifs_data *ifsd;
union ifs_scan *activate;
union ifs_status status;
};
/*
* Number of TSC cycles that a logical CPU will wait for the other
* logical CPU on the core in the WRMSR(ACTIVATE_SCAN).
......@@ -134,19 +140,56 @@ static bool can_restart(union ifs_status status)
return false;
}
#define SPINUNIT 100 /* 100 nsec */
static atomic_t array_cpus_in;
static atomic_t scan_cpus_in;
/*
* Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus()
*/
static void wait_for_sibling_cpu(atomic_t *t, long long timeout)
{
int cpu = smp_processor_id();
const struct cpumask *smt_mask = cpu_smt_mask(cpu);
int all_cpus = cpumask_weight(smt_mask);
atomic_inc(t);
while (atomic_read(t) < all_cpus) {
if (timeout < SPINUNIT)
return;
ndelay(SPINUNIT);
timeout -= SPINUNIT;
touch_nmi_watchdog();
}
}
/*
* Execute the scan. Called "simultaneously" on all threads of a core
* at high priority using the stop_cpus mechanism.
*/
static int doscan(void *data)
{
int cpu = smp_processor_id();
u64 *msrs = data;
int cpu = smp_processor_id(), start, stop;
struct run_params *params = data;
union ifs_status status;
struct ifs_data *ifsd;
int first;
ifsd = params->ifsd;
if (ifsd->generation) {
start = params->activate->gen2.start;
stop = params->activate->gen2.stop;
} else {
start = params->activate->gen0.start;
stop = params->activate->gen0.stop;
}
/* Only the first logical CPU on a core reports result */
first = cpumask_first(cpu_smt_mask(cpu));
wait_for_sibling_cpu(&scan_cpus_in, NSEC_PER_SEC);
/*
* This WRMSR will wait for other HT threads to also write
* to this MSR (at most for activate.delay cycles). Then it
......@@ -155,12 +198,14 @@ static int doscan(void *data)
* take up to 200 milliseconds (in the case where all chunks
* are processed in a single pass) before it retires.
*/
wrmsrl(MSR_ACTIVATE_SCAN, msrs[0]);
wrmsrl(MSR_ACTIVATE_SCAN, params->activate->data);
rdmsrl(MSR_SCAN_STATUS, status.data);
if (cpu == first) {
/* Pass back the result of the scan */
rdmsrl(MSR_SCAN_STATUS, msrs[1]);
}
trace_ifs_status(ifsd->cur_batch, start, stop, status.data);
/* Pass back the result of the scan */
if (cpu == first)
params->status = status;
return 0;
}
......@@ -179,7 +224,7 @@ static void ifs_test_core(int cpu, struct device *dev)
struct ifs_data *ifsd;
int to_start, to_stop;
int status_chunk;
u64 msrvals[2];
struct run_params params;
int retries;
ifsd = ifs_get_data(dev);
......@@ -190,6 +235,8 @@ static void ifs_test_core(int cpu, struct device *dev)
to_start = 0;
to_stop = ifsd->valid_chunks - 1;
params.ifsd = ifs_get_data(dev);
if (ifsd->generation) {
activate.gen2.start = to_start;
activate.gen2.stop = to_stop;
......@@ -207,12 +254,11 @@ static void ifs_test_core(int cpu, struct device *dev)
break;
}
msrvals[0] = activate.data;
stop_core_cpuslocked(cpu, doscan, msrvals);
status.data = msrvals[1];
params.activate = &activate;
atomic_set(&scan_cpus_in, 0);
stop_core_cpuslocked(cpu, doscan, &params);
trace_ifs_status(cpu, to_start, to_stop, status.data);
status = params.status;
/* Some cases can be retried, give up for others */
if (!can_restart(status))
......@@ -250,34 +296,14 @@ static void ifs_test_core(int cpu, struct device *dev)
}
}
#define SPINUNIT 100 /* 100 nsec */
static atomic_t array_cpus_out;
/*
* Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus()
*/
static void wait_for_sibling_cpu(atomic_t *t, long long timeout)
{
int cpu = smp_processor_id();
const struct cpumask *smt_mask = cpu_smt_mask(cpu);
int all_cpus = cpumask_weight(smt_mask);
atomic_inc(t);
while (atomic_read(t) < all_cpus) {
if (timeout < SPINUNIT)
return;
ndelay(SPINUNIT);
timeout -= SPINUNIT;
touch_nmi_watchdog();
}
}
static int do_array_test(void *data)
{
union ifs_array *command = data;
int cpu = smp_processor_id();
int first;
wait_for_sibling_cpu(&array_cpus_in, NSEC_PER_SEC);
/*
* Only one logical CPU on a core needs to trigger the Array test via MSR write.
*/
......@@ -289,9 +315,6 @@ static int do_array_test(void *data)
rdmsrl(MSR_ARRAY_BIST, command->data);
}
/* Tests complete faster if the sibling is spinning here */
wait_for_sibling_cpu(&array_cpus_out, NSEC_PER_SEC);
return 0;
}
......@@ -312,7 +335,7 @@ static void ifs_array_test_core(int cpu, struct device *dev)
timed_out = true;
break;
}
atomic_set(&array_cpus_out, 0);
atomic_set(&array_cpus_in, 0);
stop_core_cpuslocked(cpu, do_array_test, &command);
if (command.ctrl_result)
......
......@@ -673,6 +673,7 @@ static struct pmc_info arl_pmc_info_list[] = {
};
#define ARL_NPU_PCI_DEV 0xad1d
#define ARL_GNA_PCI_DEV 0xae4c
/*
* Set power state of select devices that do not have drivers to D3
* so that they do not block Package C entry.
......@@ -680,6 +681,7 @@ static struct pmc_info arl_pmc_info_list[] = {
static void arl_d3_fixup(void)
{
pmc_core_set_device_d3(ARL_NPU_PCI_DEV);
pmc_core_set_device_d3(ARL_GNA_PCI_DEV);
}
static int arl_resume(struct pmc_dev *pmcdev)
......
......@@ -1389,6 +1389,15 @@ static int pmc_core_probe(struct platform_device *pdev)
return -ENOMEM;
pmcdev->pmcs[PMC_IDX_MAIN] = primary_pmc;
/* The last element in msr_map is empty */
pmcdev->num_of_pkgc = ARRAY_SIZE(msr_map) - 1;
pmcdev->pkgc_res_cnt = devm_kcalloc(&pdev->dev,
pmcdev->num_of_pkgc,
sizeof(*pmcdev->pkgc_res_cnt),
GFP_KERNEL);
if (!pmcdev->pkgc_res_cnt)
return -ENOMEM;
/*
* 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
......@@ -1432,6 +1441,7 @@ static __maybe_unused int pmc_core_suspend(struct device *dev)
{
struct pmc_dev *pmcdev = dev_get_drvdata(dev);
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
unsigned int i;
if (pmcdev->suspend)
pmcdev->suspend(pmcdev);
......@@ -1440,9 +1450,11 @@ static __maybe_unused int pmc_core_suspend(struct device *dev)
if (pm_suspend_via_firmware())
return 0;
/* Save PC10 residency for checking later */
if (rdmsrl_safe(MSR_PKG_C10_RESIDENCY, &pmcdev->pc10_counter))
return -EIO;
/* Save PKGC residency for checking later */
for (i = 0; i < pmcdev->num_of_pkgc; i++) {
if (rdmsrl_safe(msr_map[i].bit_mask, &pmcdev->pkgc_res_cnt[i]))
return -EIO;
}
/* Save S0ix residency for checking later */
if (pmc_core_dev_state_get(pmc, &pmcdev->s0ix_counter))
......@@ -1451,14 +1463,15 @@ static __maybe_unused int pmc_core_suspend(struct device *dev)
return 0;
}
static inline bool pmc_core_is_pc10_failed(struct pmc_dev *pmcdev)
static inline bool pmc_core_is_deepest_pkgc_failed(struct pmc_dev *pmcdev)
{
u64 pc10_counter;
u32 deepest_pkgc_msr = msr_map[pmcdev->num_of_pkgc - 1].bit_mask;
u64 deepest_pkgc_residency;
if (rdmsrl_safe(MSR_PKG_C10_RESIDENCY, &pc10_counter))
if (rdmsrl_safe(deepest_pkgc_msr, &deepest_pkgc_residency))
return false;
if (pc10_counter == pmcdev->pc10_counter)
if (deepest_pkgc_residency == pmcdev->pkgc_res_cnt[pmcdev->num_of_pkgc - 1])
return true;
return false;
......@@ -1497,10 +1510,22 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev)
if (!warn_on_s0ix_failures)
return 0;
if (pmc_core_is_pc10_failed(pmcdev)) {
/* S0ix failed because of PC10 entry failure */
dev_info(dev, "CPU did not enter PC10!!! (PC10 cnt=0x%llx)\n",
pmcdev->pc10_counter);
if (pmc_core_is_deepest_pkgc_failed(pmcdev)) {
/* S0ix failed because of deepest PKGC entry failure */
dev_info(dev, "CPU did not enter %s!!! (%s cnt=0x%llx)\n",
msr_map[pmcdev->num_of_pkgc - 1].name,
msr_map[pmcdev->num_of_pkgc - 1].name,
pmcdev->pkgc_res_cnt[pmcdev->num_of_pkgc - 1]);
for (i = 0; i < pmcdev->num_of_pkgc; i++) {
u64 pc_cnt;
if (!rdmsrl_safe(msr_map[i].bit_mask, &pc_cnt)) {
dev_info(dev, "Prev %s cnt = 0x%llx, Current %s cnt = 0x%llx\n",
msr_map[i].name, pmcdev->pkgc_res_cnt[i],
msr_map[i].name, pc_cnt);
}
}
return 0;
}
......
......@@ -385,7 +385,8 @@ struct pmc {
* @pmc_xram_read_bit: flag to indicate whether PMC XRAM shadow registers
* used to read MPHY PG and PLL status are available
* @mutex_lock: mutex to complete one transcation
* @pc10_counter: PC10 residency counter
* @pkgc_res_cnt: Array of PKGC residency counters
* @num_of_pkgc: Number of PKGC
* @s0ix_counter: S0ix residency (step adjusted)
* @num_lpm_modes: Count of enabled modes
* @lpm_en_modes: Array of enabled modes from lowest to highest priority
......@@ -403,13 +404,15 @@ struct pmc_dev {
int pmc_xram_read_bit;
struct mutex lock; /* generic mutex lock for PMC Core */
u64 pc10_counter;
u64 s0ix_counter;
int num_lpm_modes;
int lpm_en_modes[LPM_MAX_NUM_MODES];
void (*suspend)(struct pmc_dev *pmcdev);
int (*resume)(struct pmc_dev *pmcdev);
u64 *pkgc_res_cnt;
u8 num_of_pkgc;
bool has_die_c6;
u32 die_c6_offset;
struct telem_endpoint *punit_ep;
......
......@@ -13,21 +13,6 @@
#include "core.h"
#define SOCM_LPM_REQ_GUID 0x11594920
#define PMC_DEVID_SOCM 0xa87f
static const u8 LNL_LPM_REG_INDEX[] = {0, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20};
static struct pmc_info lnl_pmc_info_list[] = {
{
.guid = SOCM_LPM_REQ_GUID,
.devid = PMC_DEVID_SOCM,
.map = &lnl_socm_reg_map,
},
{}
};
const struct pmc_bit_map lnl_ltr_show_map[] = {
{"SOUTHPORT_A", CNP_PMC_LTR_SPA},
{"SOUTHPORT_B", CNP_PMC_LTR_SPB},
......@@ -490,7 +475,6 @@ const struct pmc_reg_map lnl_socm_reg_map = {
.lpm_sts = lnl_lpm_maps,
.lpm_status_offset = MTL_LPM_STATUS_OFFSET,
.lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET,
.lpm_reg_index = LNL_LPM_REG_INDEX,
};
#define LNL_NPU_PCI_DEV 0x643e
......@@ -517,33 +501,19 @@ static int lnl_resume(struct pmc_dev *pmcdev)
int lnl_core_init(struct pmc_dev *pmcdev)
{
int ret;
int func = 2;
bool ssram_init = true;
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_SOC];
lnl_d3_fixup();
pmcdev->suspend = cnl_suspend;
pmcdev->resume = lnl_resume;
pmcdev->regmap_list = lnl_pmc_info_list;
ret = pmc_core_ssram_init(pmcdev, func);
/* If regbase not assigned, set map and discover using legacy method */
if (ret) {
ssram_init = false;
pmc->map = &lnl_socm_reg_map;
ret = get_primary_reg_base(pmc);
if (ret)
return ret;
}
pmc_core_get_low_power_modes(pmcdev);
pmc->map = &lnl_socm_reg_map;
ret = get_primary_reg_base(pmc);
if (ret)
return ret;
if (ssram_init) {
ret = pmc_core_ssram_get_lpm_reqs(pmcdev);
if (ret)
return ret;
}
pmc_core_get_low_power_modes(pmcdev);
return 0;
}
......@@ -462,10 +462,10 @@ static long isst_if_core_power_state(void __user *argp)
struct tpmi_per_power_domain_info *power_domain_info;
struct isst_core_power core_power;
if (disable_dynamic_sst_features())
if (copy_from_user(&core_power, argp, sizeof(core_power)))
return -EFAULT;
if (copy_from_user(&core_power, argp, sizeof(core_power)))
if (core_power.get_set && disable_dynamic_sst_features())
return -EFAULT;
power_domain_info = get_instance(core_power.socket_id, core_power.power_domain_id);
......
......@@ -96,7 +96,7 @@ struct intel_tpmi_pfs_entry {
*/
struct intel_tpmi_pm_feature {
struct intel_tpmi_pfs_entry pfs_header;
unsigned int vsec_offset;
u64 vsec_offset;
struct intel_vsec_device *vsec_dev;
};
......@@ -376,7 +376,7 @@ static int tpmi_pfs_dbg_show(struct seq_file *s, void *unused)
read_blocked = feature_state.read_blocked ? 'Y' : 'N';
write_blocked = feature_state.write_blocked ? 'Y' : 'N';
}
seq_printf(s, "0x%02x\t\t0x%02x\t\t0x%04x\t\t0x%04x\t\t0x%02x\t\t0x%08x\t%c\t%c\t\t%c\t\t%c\n",
seq_printf(s, "0x%02x\t\t0x%02x\t\t0x%04x\t\t0x%04x\t\t0x%02x\t\t0x%016llx\t%c\t%c\t\t%c\t\t%c\n",
pfs->pfs_header.tpmi_id, pfs->pfs_header.num_entries,
pfs->pfs_header.entry_size, pfs->pfs_header.cap_offset,
pfs->pfs_header.attribute, pfs->vsec_offset, locked, disabled,
......@@ -395,7 +395,8 @@ static int tpmi_mem_dump_show(struct seq_file *s, void *unused)
struct intel_tpmi_pm_feature *pfs = s->private;
int count, ret = 0;
void __iomem *mem;
u32 off, size;
u32 size;
u64 off;
u8 *buffer;
size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs);
......@@ -411,7 +412,7 @@ static int tpmi_mem_dump_show(struct seq_file *s, void *unused)
mutex_lock(&tpmi_dev_lock);
for (count = 0; count < pfs->pfs_header.num_entries; ++count) {
seq_printf(s, "TPMI Instance:%d offset:0x%x\n", count, off);
seq_printf(s, "TPMI Instance:%d offset:0x%llx\n", count, off);
mem = ioremap(off, size);
if (!mem) {
......
......@@ -236,10 +236,7 @@ static bool intel_vsec_walk_header(struct pci_dev *pdev,
for ( ; *header; header++) {
ret = intel_vsec_add_dev(pdev, *header, info);
if (ret)
dev_info(&pdev->dev, "Could not add device for VSEC id %d\n",
(*header)->id);
else
if (!ret)
have_devices = true;
}
......
......@@ -131,6 +131,7 @@ static struct wmi_driver intel_wmi_sbl_fw_update_driver = {
.probe = intel_wmi_sbl_fw_update_probe,
.remove = intel_wmi_sbl_fw_update_remove,
.id_table = intel_wmi_sbl_id_table,
.no_singleton = true,
};
module_wmi_driver(intel_wmi_sbl_fw_update_driver);
......
......@@ -63,6 +63,7 @@ static struct wmi_driver intel_wmi_thunderbolt_driver = {
.dev_groups = tbt_groups,
},
.id_table = intel_wmi_thunderbolt_id_table,
.no_singleton = true,
};
module_wmi_driver(intel_wmi_thunderbolt_driver);
......
......@@ -22,7 +22,7 @@
static int major;
struct intel_scu_ipc_dev *scu;
static struct intel_scu_ipc_dev *scu;
static DEFINE_MUTEX(scu_lock);
/* IOCTL commands */
......
......@@ -11,7 +11,6 @@
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/intel-mid.h>
#include <asm/intel_scu_ipc.h>
static int intel_scu_pci_probe(struct pci_dev *pdev,
......
......@@ -13,7 +13,6 @@
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include <asm/intel-mid.h>
#include <asm/io_apic.h>
#include <asm/hw_irq.h>
......
......@@ -6,6 +6,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/dmi.h>
......@@ -17,6 +18,7 @@
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/seq_file.h>
#include <linux/suspend.h>
struct pmc_bit_map {
const char *name;
......@@ -448,6 +450,82 @@ static int pmc_setup_clks(struct pci_dev *pdev, void __iomem *pmc_regmap,
return 0;
}
#ifdef CONFIG_SUSPEND
static void pmc_dev_state_check(u32 sts, const struct pmc_bit_map *sts_map,
u32 fd, const struct pmc_bit_map *fd_map,
u32 sts_possible_false_pos)
{
int index;
for (index = 0; sts_map[index].name; index++) {
if (!(fd_map[index].bit_mask & fd) &&
!(sts_map[index].bit_mask & sts)) {
if (sts_map[index].bit_mask & sts_possible_false_pos)
pm_pr_dbg("%s is in D0 prior to s2idle\n",
sts_map[index].name);
else
pr_err("%s is in D0 prior to s2idle\n",
sts_map[index].name);
}
}
}
static void pmc_s2idle_check(void)
{
struct pmc_dev *pmc = &pmc_device;
const struct pmc_reg_map *m = pmc->map;
u32 func_dis, func_dis_2;
u32 d3_sts_0, d3_sts_1;
u32 false_pos_sts_0, false_pos_sts_1;
int i;
func_dis = pmc_reg_read(pmc, PMC_FUNC_DIS);
func_dis_2 = pmc_reg_read(pmc, PMC_FUNC_DIS_2);
d3_sts_0 = pmc_reg_read(pmc, PMC_D3_STS_0);
d3_sts_1 = pmc_reg_read(pmc, PMC_D3_STS_1);
/*
* Some blocks are not used on lower-featured versions of the SoC and
* always report D0, add these to false_pos mask to log at debug level.
*/
if (m->d3_sts_1 == byt_d3_sts_1_map) {
/* Bay Trail */
false_pos_sts_0 = BIT_GBE | BIT_SATA | BIT_PCIE_PORT0 |
BIT_PCIE_PORT1 | BIT_PCIE_PORT2 | BIT_PCIE_PORT3 |
BIT_LPSS2_F5_I2C5;
false_pos_sts_1 = BIT_SMB | BIT_USH_SS_PHY | BIT_DFX;
} else {
/* Cherry Trail */
false_pos_sts_0 = BIT_GBE | BIT_SATA | BIT_LPSS2_F7_I2C7;
false_pos_sts_1 = BIT_SMB | BIT_STS_ISH;
}
pmc_dev_state_check(d3_sts_0, m->d3_sts_0, func_dis, m->func_dis, false_pos_sts_0);
pmc_dev_state_check(d3_sts_1, m->d3_sts_1, func_dis_2, m->func_dis_2, false_pos_sts_1);
/* Forced-on PMC clocks prevent S0i3 */
for (i = 0; i < PMC_CLK_NUM; i++) {
u32 ctl = pmc_reg_read(pmc, PMC_CLK_CTL_OFFSET + 4 * i);
if ((ctl & PMC_MASK_CLK_CTL) != PMC_CLK_CTL_FORCE_ON)
continue;
pr_err("clock %d is ON prior to freeze (ctl 0x%08x)\n", i, ctl);
}
}
static struct acpi_s2idle_dev_ops pmc_s2idle_ops = {
.check = pmc_s2idle_check,
};
static void pmc_s2idle_check_register(void)
{
acpi_register_lps0_dev(&pmc_s2idle_ops);
}
#else
static void pmc_s2idle_check_register(void) {}
#endif
static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct pmc_dev *pmc = &pmc_device;
......@@ -485,6 +563,7 @@ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
dev_warn(&pdev->dev, "platform clocks register failed: %d\n",
ret);
pmc_s2idle_check_register();
pmc->init = true;
return ret;
}
......
......@@ -256,12 +256,7 @@ static void silicom_gpio_set(struct gpio_chip *gc,
if (direction == GPIO_LINE_DIRECTION_IN)
return;
if (value)
silicom_mec_port_set(channel, 0);
else if (value == 0)
silicom_mec_port_set(channel, 1);
else
pr_err("Wrong argument value: %d\n", value);
silicom_mec_port_set(channel, !value);
}
static int silicom_gpio_direction_output(struct gpio_chip *gc,
......
......@@ -195,7 +195,7 @@ static const char * const level_options[] = {
[TLMI_LEVEL_MASTER] = "master",
};
static struct think_lmi tlmi_priv;
static struct class *fw_attr_class;
static const struct class *fw_attr_class;
static DEFINE_MUTEX(tlmi_mutex);
/* Convert BIOS WMI error string to suitable error code */
......
This diff is collapsed.
......@@ -1217,6 +1217,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
DMI_MATCH(DMI_BIOS_VERSION, "CHUWI.D86JLBNR"),
},
},
{
/* Chuwi Vi8 dual-boot (CWI506) */
.driver_data = (void *)&chuwi_vi8_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
DMI_MATCH(DMI_PRODUCT_NAME, "i86"),
DMI_MATCH(DMI_BIOS_VERSION, "CHUWI2.D86JHBNR02"),
},
},
{
/* Chuwi Vi8 Plus (CWI519) */
.driver_data = (void *)&chuwi_vi8_plus_data,
......
......@@ -94,6 +94,7 @@ static struct wmi_driver wmi_bmof_driver = {
.probe = wmi_bmof_probe,
.remove = wmi_bmof_remove,
.id_table = wmi_bmof_id_table,
.no_singleton = true,
};
module_wmi_driver(wmi_bmof_driver);
......
This diff is collapsed.
......@@ -43,6 +43,19 @@
BIT_ORED_DEDICATED_IRQ_GPSC | \
BIT_SHARED_IRQ_GPSS)
/* External clk generator settings */
#define PMC_CLK_CTL_OFFSET 0x60
#define PMC_CLK_CTL_SIZE 4
#define PMC_CLK_NUM 6
#define PMC_CLK_CTL_GATED_ON_D3 0x0
#define PMC_CLK_CTL_FORCE_ON 0x1
#define PMC_CLK_CTL_FORCE_OFF 0x2
#define PMC_CLK_CTL_RESERVED 0x3
#define PMC_MASK_CLK_CTL GENMASK(1, 0)
#define PMC_MASK_CLK_FREQ BIT(2)
#define PMC_CLK_FREQ_XTAL (0 << 2) /* 25 MHz */
#define PMC_CLK_FREQ_PLL (1 << 2) /* 19.2 MHz */
/* The timers accumulate time spent in sleep state */
#define PMC_S0IR_TMR 0x80
#define PMC_S0I1_TMR 0x84
......@@ -104,14 +117,14 @@
#define BIT_SCC_SDIO BIT(9)
#define BIT_SCC_SDCARD BIT(10)
#define BIT_SCC_MIPI BIT(11)
#define BIT_HDA BIT(12)
#define BIT_HDA BIT(12) /* CHT datasheet: reserved */
#define BIT_LPE BIT(13)
#define BIT_OTG BIT(14)
#define BIT_USH BIT(15)
#define BIT_GBE BIT(16)
#define BIT_SATA BIT(17)
#define BIT_USB_EHCI BIT(18)
#define BIT_SEC BIT(19)
#define BIT_USH BIT(15) /* CHT datasheet: reserved */
#define BIT_GBE BIT(16) /* CHT datasheet: reserved */
#define BIT_SATA BIT(17) /* CHT datasheet: reserved */
#define BIT_USB_EHCI BIT(18) /* CHT datasheet: XHCI! */
#define BIT_SEC BIT(19) /* BYT datasheet: reserved */
#define BIT_PCIE_PORT0 BIT(20)
#define BIT_PCIE_PORT1 BIT(21)
#define BIT_PCIE_PORT2 BIT(22)
......
......@@ -48,7 +48,8 @@ u8 wmidev_instance_count(struct wmi_device *wdev);
* struct wmi_driver - WMI driver structure
* @driver: Driver model structure
* @id_table: List of WMI GUIDs supported by this driver
* @no_notify_data: WMI events provide no event data
* @no_notify_data: Driver supports WMI events which provide no event data
* @no_singleton: Driver can be instantiated multiple times
* @probe: Callback for device binding
* @remove: Callback for device unbinding
* @notify: Callback for receiving WMI events
......@@ -59,6 +60,7 @@ struct wmi_driver {
struct device_driver driver;
const struct wmi_device_id *id_table;
bool no_notify_data;
bool no_singleton;
int (*probe)(struct wmi_device *wdev, const void *context);
void (*remove)(struct wmi_device *wdev);
......
......@@ -10,26 +10,26 @@
TRACE_EVENT(ifs_status,
TP_PROTO(int cpu, int start, int stop, u64 status),
TP_PROTO(int batch, int start, int stop, u64 status),
TP_ARGS(cpu, start, stop, status),
TP_ARGS(batch, start, stop, status),
TP_STRUCT__entry(
__field( int, batch )
__field( u64, status )
__field( int, cpu )
__field( u16, start )
__field( u16, stop )
),
TP_fast_assign(
__entry->cpu = cpu;
__entry->batch = batch;
__entry->start = start;
__entry->stop = stop;
__entry->status = status;
),
TP_printk("cpu: %d, start: %.4x, stop: %.4x, status: %.16llx",
__entry->cpu,
TP_printk("batch: %.2d, start: %.4x, stop: %.4x, status: %.16llx",
__entry->batch,
__entry->start,
__entry->stop,
__entry->status)
......
......@@ -602,6 +602,7 @@
#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */
#define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */
#define KEY_REFRESH_RATE_TOGGLE 0x232 /* Display refresh rate toggle */
#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */
#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment