platform-drivers-x86 for v6.9-1
Highlights: - acer-wmi: New HW support - amd/pmf: Support for new revision of heartbeat notify - asus-wmi: Correctly handle HW without LEDs - fujitsu-laptop: Battery charge control support - hp-wmi: Support for new thermal profiles - ideapad-laptop: Support for refresh rate key - intel/pmc: Put 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) - pmc/punit_atom: Report devices that prevent reacing low power levels - surface: Fan speed function support - thinkpad_acpi: Support for more sperial keys and complete the list of models with non-standard fan registers - touchscreen_dmi: New HW support - wmi: Continued modernization efforts - Removal of obsoleted ledtrig-audio call and the related dependency - Debug & metrics interface improvements - Miscellaneous cleanups / fixes / improvements The following is an automated shortlog grouped by driver: acer-wmi: - Add predator_v4 module parameter - Add support for Acer PH16-71 amd/hsmp: - Add support for ACPI based probing - Cache pci_dev in struct hsmp_socket - Change devm_kzalloc() to devm_kcalloc() - Check num_sockets against MAX_AMD_SOCKETS - Create static func to handle platdev - Define a struct to hold mailbox regs - Move dev from platdev to hsmp_socket - Move hsmp_test to probe - Non-ACPI support for AMD F1A_M00~0Fh - Remove extra parenthesis and add a space - Restructure sysfs group creation amd/pmf: - Add missing __iomem attribute to policy_base - Add support to get APTS index numbers for static slider - Add support to get sbios requests in PMF driver - Add support to get sps default APTS index values - Add support to notify sbios heart beat event - Differentiate PMF ACPI versions - Disable debugfs support for querying power thermals - Do not use readl() for policy buffer access - Fix possible out-of-bound memory accesses - Fix return value of amd_pmf_start_policy_engine() - Update sps power thermals according to the platform-profiles - Use struct for cookie header asus-wmi: - Consider device is absent when the read is ~0 - Revert: Support WMI event queue clk: x86: - Move clk-pmc-atom register defines to include/linux/platform_data/x86/pmc_atom.h dell-privacy: - Remove usage of wmi_has_guid() Documentation/x86/amd/hsmp: - Updating urls drivers/mellanox: - Convert snprintf to sysfs_emit fujitsu-laptop: - Add battery charge control support hp-wmi: - Add thermal profile support for 8BAD boards - Tidy up module source code ideapad-laptop: - map Fn + R key to KEY_REFRESH_RATE_TOGGLE - support Fn+R dual-function key Input: - allocate keycode for Display refresh rate toggle intel/ifs: - Add an entry rendezvous for SAF - Add current batch number to trace output - Remove unnecessary initialization of 'ret' - Replace the exit rendezvous with an entry rendezvous for ARRAY_BIST - Trace on all HT threads when executing a test intel/pmc/arl: - Put GNA device in D3 intel/pmc: - Improve PKGC residency counters debug intel/pmc/lnl: - Remove SSRAM support intel_scu_ipcutil: - Make scu static intel_scu_pcidrv: - Remove unused intel-mid.h intel_scu_wdt: - Remove unused intel-mid.h intel/tpmi: - Change vsec offset to u64 intel/vsec: - Remove nuisance message ISST: - Allow reading core-power state on HWP disabled systems mlxbf-pmc: - Cleanup signed/unsigned mix-up - fix signedness bugs - Ignore unsupported performance blocks mlxbf-pmc: mlxbf_pmc_event_list(): - make size ptr optional mlxbf-pmc: - Replace uintN_t with kernel-style types mlxreg-hotplug: - Remove redundant NULL-check pmc_atom: - Annotate d3_sts register bit defines - Check state of PMC clocks on s2idle - Check state of PMC managed devices on s2idle silicom-platform: - clean up a check surface: aggregator_registry: - add entry for fan speed thinkpad_acpi: - Add more ThinkPads with non-standard reg address for fan - Fix to correct wrong temp reporting on some ThinkPads - remove redundant assignment to variable i - Simplify thermal mode checking - Support for mode FN key touchscreen_dmi: - Add an extra entry for a variant of the Chuwi Vi8 tablet wmi: - Always evaluate _WED when receiving an event - Check if event data is not NULL - Check if WMxx control method exists - Do not instantiate older WMI drivers multiple times - Ignore duplicated GUIDs in legacy matches - Make input buffer mandatory when evaluating methods - Prevent incompatible event driver from probing - Remove obsolete duplicate GUID allowlist - Remove unnecessary out-of-memory message - Replace pr_err() with dev_err() - Stop using ACPI device class - Update documentation regarding _WED - Use ACPI device name in netlink event - Use FW_BUG when warning about missing control methods x86/atom: - Check state of Punit managed devices on s2idle x86: ibm_rtl: - make rtl_subsys const x86: wmi: - make wmi_bus_type const platform/x86: - make fw_attr_class constant - remove obsolete calls to ledtrig_audio_get Merges: - Merge tag 'platform-drivers-x86-v6.8-2' into pdx/for-next - Merge tag 'platform-drivers-x86-v6.8-4' into pdx86/for-next -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSCSUwRdwTNL2MhaBlZrE9hU+XOMQUCZfLZKgAKCRBZrE9hU+XO MWqnAQCZW0KiSzXbJkTN4GWlMOqnlaJsiflnPeVNxH59bDUTeQEA/OdSzyiDUqKr zJcGnOyILuQ3wCvQ5SuqRCwjFHXOQg0= =8y6r -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
66fd6d0bd7
@ -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,
|
||||
|
@ -99,8 +99,8 @@
|
||||
*/
|
||||
struct mlxbf_pmc_attribute {
|
||||
struct device_attribute dev_attr;
|
||||
int index;
|
||||
int nr;
|
||||
unsigned int index;
|
||||
unsigned int nr;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -121,7 +121,7 @@ struct mlxbf_pmc_block_info {
|
||||
void __iomem *mmio_base;
|
||||
size_t blk_size;
|
||||
size_t counters;
|
||||
int type;
|
||||
unsigned int type;
|
||||
struct mlxbf_pmc_attribute *attr_counter;
|
||||
struct mlxbf_pmc_attribute *attr_event;
|
||||
struct mlxbf_pmc_attribute attr_event_list;
|
||||
@ -149,17 +149,17 @@ struct mlxbf_pmc_block_info {
|
||||
*/
|
||||
struct mlxbf_pmc_context {
|
||||
struct platform_device *pdev;
|
||||
uint32_t total_blocks;
|
||||
uint32_t tile_count;
|
||||
uint8_t llt_enable;
|
||||
uint8_t mss_enable;
|
||||
uint32_t group_num;
|
||||
u32 total_blocks;
|
||||
u32 tile_count;
|
||||
u8 llt_enable;
|
||||
u8 mss_enable;
|
||||
u32 group_num;
|
||||
struct device *hwmon_dev;
|
||||
const char *block_name[MLXBF_PMC_MAX_BLOCKS];
|
||||
struct mlxbf_pmc_block_info block[MLXBF_PMC_MAX_BLOCKS];
|
||||
const struct attribute_group *groups[MLXBF_PMC_MAX_BLOCKS];
|
||||
bool svc_sreg_support;
|
||||
uint32_t sreg_tbl_perf;
|
||||
u32 sreg_tbl_perf;
|
||||
unsigned int event_set;
|
||||
};
|
||||
|
||||
@ -169,7 +169,7 @@ struct mlxbf_pmc_context {
|
||||
* @evt_name: Name of the event
|
||||
*/
|
||||
struct mlxbf_pmc_events {
|
||||
int evt_num;
|
||||
u32 evt_num;
|
||||
char *evt_name;
|
||||
};
|
||||
|
||||
@ -865,8 +865,7 @@ static struct mlxbf_pmc_context *pmc;
|
||||
static const char *mlxbf_pmc_svc_uuid_str = "89c036b4-e7d7-11e6-8797-001aca00bfc4";
|
||||
|
||||
/* Calls an SMC to access a performance register */
|
||||
static int mlxbf_pmc_secure_read(void __iomem *addr, uint32_t command,
|
||||
uint64_t *result)
|
||||
static int mlxbf_pmc_secure_read(void __iomem *addr, u32 command, u64 *result)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
int status, err = 0;
|
||||
@ -892,8 +891,7 @@ static int mlxbf_pmc_secure_read(void __iomem *addr, uint32_t command,
|
||||
}
|
||||
|
||||
/* Read from a performance counter */
|
||||
static int mlxbf_pmc_read(void __iomem *addr, uint32_t command,
|
||||
uint64_t *result)
|
||||
static int mlxbf_pmc_read(void __iomem *addr, u32 command, u64 *result)
|
||||
{
|
||||
if (pmc->svc_sreg_support)
|
||||
return mlxbf_pmc_secure_read(addr, command, result);
|
||||
@ -907,22 +905,21 @@ static int mlxbf_pmc_read(void __iomem *addr, uint32_t command,
|
||||
}
|
||||
|
||||
/* Convenience function for 32-bit reads */
|
||||
static int mlxbf_pmc_readl(void __iomem *addr, uint32_t *result)
|
||||
static int mlxbf_pmc_readl(void __iomem *addr, u32 *result)
|
||||
{
|
||||
uint64_t read_out;
|
||||
u64 read_out;
|
||||
int status;
|
||||
|
||||
status = mlxbf_pmc_read(addr, MLXBF_PMC_READ_REG_32, &read_out);
|
||||
if (status)
|
||||
return status;
|
||||
*result = (uint32_t)read_out;
|
||||
*result = (u32)read_out;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Calls an SMC to access a performance register */
|
||||
static int mlxbf_pmc_secure_write(void __iomem *addr, uint32_t command,
|
||||
uint64_t value)
|
||||
static int mlxbf_pmc_secure_write(void __iomem *addr, u32 command, u64 value)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
int status, err = 0;
|
||||
@ -945,7 +942,7 @@ static int mlxbf_pmc_secure_write(void __iomem *addr, uint32_t command,
|
||||
}
|
||||
|
||||
/* Write to a performance counter */
|
||||
static int mlxbf_pmc_write(void __iomem *addr, int command, uint64_t value)
|
||||
static int mlxbf_pmc_write(void __iomem *addr, int command, u64 value)
|
||||
{
|
||||
if (pmc->svc_sreg_support)
|
||||
return mlxbf_pmc_secure_write(addr, command, value);
|
||||
@ -959,7 +956,7 @@ static int mlxbf_pmc_write(void __iomem *addr, int command, uint64_t value)
|
||||
}
|
||||
|
||||
/* Check if the register offset is within the mapped region for the block */
|
||||
static bool mlxbf_pmc_valid_range(int blk_num, uint32_t offset)
|
||||
static bool mlxbf_pmc_valid_range(unsigned int blk_num, u32 offset)
|
||||
{
|
||||
if ((offset >= 0) && !(offset % MLXBF_PMC_REG_SIZE) &&
|
||||
(offset + MLXBF_PMC_REG_SIZE <= pmc->block[blk_num].blk_size))
|
||||
@ -969,33 +966,33 @@ static bool mlxbf_pmc_valid_range(int blk_num, uint32_t offset)
|
||||
}
|
||||
|
||||
/* Get the event list corresponding to a certain block */
|
||||
static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk,
|
||||
int *size)
|
||||
static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, size_t *psize)
|
||||
{
|
||||
const struct mlxbf_pmc_events *events;
|
||||
size_t size;
|
||||
|
||||
if (strstr(blk, "tilenet")) {
|
||||
events = mlxbf_pmc_hnfnet_events;
|
||||
*size = ARRAY_SIZE(mlxbf_pmc_hnfnet_events);
|
||||
size = ARRAY_SIZE(mlxbf_pmc_hnfnet_events);
|
||||
} else if (strstr(blk, "tile")) {
|
||||
events = mlxbf_pmc_hnf_events;
|
||||
*size = ARRAY_SIZE(mlxbf_pmc_hnf_events);
|
||||
size = ARRAY_SIZE(mlxbf_pmc_hnf_events);
|
||||
} else if (strstr(blk, "triogen")) {
|
||||
events = mlxbf_pmc_smgen_events;
|
||||
*size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
|
||||
size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
|
||||
} else if (strstr(blk, "trio")) {
|
||||
switch (pmc->event_set) {
|
||||
case MLXBF_PMC_EVENT_SET_BF1:
|
||||
events = mlxbf_pmc_trio_events_1;
|
||||
*size = ARRAY_SIZE(mlxbf_pmc_trio_events_1);
|
||||
size = ARRAY_SIZE(mlxbf_pmc_trio_events_1);
|
||||
break;
|
||||
case MLXBF_PMC_EVENT_SET_BF2:
|
||||
events = mlxbf_pmc_trio_events_2;
|
||||
*size = ARRAY_SIZE(mlxbf_pmc_trio_events_2);
|
||||
size = ARRAY_SIZE(mlxbf_pmc_trio_events_2);
|
||||
break;
|
||||
default:
|
||||
events = NULL;
|
||||
*size = 0;
|
||||
size = 0;
|
||||
break;
|
||||
}
|
||||
} else if (strstr(blk, "mss")) {
|
||||
@ -1003,51 +1000,60 @@ static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk,
|
||||
case MLXBF_PMC_EVENT_SET_BF1:
|
||||
case MLXBF_PMC_EVENT_SET_BF2:
|
||||
events = mlxbf_pmc_mss_events_1;
|
||||
*size = ARRAY_SIZE(mlxbf_pmc_mss_events_1);
|
||||
size = ARRAY_SIZE(mlxbf_pmc_mss_events_1);
|
||||
break;
|
||||
case MLXBF_PMC_EVENT_SET_BF3:
|
||||
events = mlxbf_pmc_mss_events_3;
|
||||
*size = ARRAY_SIZE(mlxbf_pmc_mss_events_3);
|
||||
size = ARRAY_SIZE(mlxbf_pmc_mss_events_3);
|
||||
break;
|
||||
default:
|
||||
events = NULL;
|
||||
*size = 0;
|
||||
size = 0;
|
||||
break;
|
||||
}
|
||||
} else if (strstr(blk, "ecc")) {
|
||||
events = mlxbf_pmc_ecc_events;
|
||||
*size = ARRAY_SIZE(mlxbf_pmc_ecc_events);
|
||||
size = ARRAY_SIZE(mlxbf_pmc_ecc_events);
|
||||
} else if (strstr(blk, "pcie")) {
|
||||
events = mlxbf_pmc_pcie_events;
|
||||
*size = ARRAY_SIZE(mlxbf_pmc_pcie_events);
|
||||
size = ARRAY_SIZE(mlxbf_pmc_pcie_events);
|
||||
} else if (strstr(blk, "l3cache")) {
|
||||
events = mlxbf_pmc_l3c_events;
|
||||
*size = ARRAY_SIZE(mlxbf_pmc_l3c_events);
|
||||
size = ARRAY_SIZE(mlxbf_pmc_l3c_events);
|
||||
} else if (strstr(blk, "gic")) {
|
||||
events = mlxbf_pmc_smgen_events;
|
||||
*size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
|
||||
size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
|
||||
} else if (strstr(blk, "smmu")) {
|
||||
events = mlxbf_pmc_smgen_events;
|
||||
*size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
|
||||
size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
|
||||
} else if (strstr(blk, "llt_miss")) {
|
||||
events = mlxbf_pmc_llt_miss_events;
|
||||
*size = ARRAY_SIZE(mlxbf_pmc_llt_miss_events);
|
||||
size = ARRAY_SIZE(mlxbf_pmc_llt_miss_events);
|
||||
} else if (strstr(blk, "llt")) {
|
||||
events = mlxbf_pmc_llt_events;
|
||||
*size = ARRAY_SIZE(mlxbf_pmc_llt_events);
|
||||
size = ARRAY_SIZE(mlxbf_pmc_llt_events);
|
||||
} else {
|
||||
events = NULL;
|
||||
*size = 0;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
if (psize)
|
||||
*psize = size;
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
static bool mlxbf_pmc_event_supported(const char *blk)
|
||||
{
|
||||
return !!mlxbf_pmc_event_list(blk, NULL);
|
||||
}
|
||||
|
||||
/* Get the event number given the name */
|
||||
static int mlxbf_pmc_get_event_num(const char *blk, const char *evt)
|
||||
{
|
||||
const struct mlxbf_pmc_events *events;
|
||||
int i, size;
|
||||
unsigned int i;
|
||||
size_t size;
|
||||
|
||||
events = mlxbf_pmc_event_list(blk, &size);
|
||||
if (!events)
|
||||
@ -1062,10 +1068,11 @@ static int mlxbf_pmc_get_event_num(const char *blk, const char *evt)
|
||||
}
|
||||
|
||||
/* Get the event number given the name */
|
||||
static char *mlxbf_pmc_get_event_name(const char *blk, int evt)
|
||||
static char *mlxbf_pmc_get_event_name(const char *blk, u32 evt)
|
||||
{
|
||||
const struct mlxbf_pmc_events *events;
|
||||
int i, size;
|
||||
unsigned int i;
|
||||
size_t size;
|
||||
|
||||
events = mlxbf_pmc_event_list(blk, &size);
|
||||
if (!events)
|
||||
@ -1080,9 +1087,9 @@ static char *mlxbf_pmc_get_event_name(const char *blk, int evt)
|
||||
}
|
||||
|
||||
/* Method to enable/disable/reset l3cache counters */
|
||||
static int mlxbf_pmc_config_l3_counters(int blk_num, bool enable, bool reset)
|
||||
static int mlxbf_pmc_config_l3_counters(unsigned int blk_num, bool enable, bool reset)
|
||||
{
|
||||
uint32_t perfcnt_cfg = 0;
|
||||
u32 perfcnt_cfg = 0;
|
||||
|
||||
if (enable)
|
||||
perfcnt_cfg |= MLXBF_PMC_L3C_PERF_CNT_CFG_EN;
|
||||
@ -1095,12 +1102,9 @@ static int mlxbf_pmc_config_l3_counters(int blk_num, bool enable, bool reset)
|
||||
}
|
||||
|
||||
/* Method to handle l3cache counter programming */
|
||||
static int mlxbf_pmc_program_l3_counter(int blk_num, uint32_t cnt_num,
|
||||
uint32_t evt)
|
||||
static int mlxbf_pmc_program_l3_counter(unsigned int blk_num, u32 cnt_num, u32 evt)
|
||||
{
|
||||
uint32_t perfcnt_sel_1 = 0;
|
||||
uint32_t perfcnt_sel = 0;
|
||||
uint32_t *wordaddr;
|
||||
u32 perfcnt_sel_1 = 0, perfcnt_sel = 0, *wordaddr;
|
||||
void __iomem *pmcaddr;
|
||||
int ret;
|
||||
|
||||
@ -1162,11 +1166,10 @@ static int mlxbf_pmc_program_l3_counter(int blk_num, uint32_t cnt_num,
|
||||
}
|
||||
|
||||
/* Method to handle crspace counter programming */
|
||||
static int mlxbf_pmc_program_crspace_counter(int blk_num, uint32_t cnt_num,
|
||||
uint32_t evt)
|
||||
static int mlxbf_pmc_program_crspace_counter(unsigned int blk_num, u32 cnt_num, u32 evt)
|
||||
{
|
||||
uint32_t word;
|
||||
void *addr;
|
||||
u32 word;
|
||||
int ret;
|
||||
|
||||
addr = pmc->block[blk_num].mmio_base +
|
||||
@ -1187,7 +1190,7 @@ static int mlxbf_pmc_program_crspace_counter(int blk_num, uint32_t cnt_num,
|
||||
}
|
||||
|
||||
/* Method to clear crspace counter value */
|
||||
static int mlxbf_pmc_clear_crspace_counter(int blk_num, uint32_t cnt_num)
|
||||
static int mlxbf_pmc_clear_crspace_counter(unsigned int blk_num, u32 cnt_num)
|
||||
{
|
||||
void *addr;
|
||||
|
||||
@ -1199,10 +1202,9 @@ static int mlxbf_pmc_clear_crspace_counter(int blk_num, uint32_t cnt_num)
|
||||
}
|
||||
|
||||
/* Method to program a counter to monitor an event */
|
||||
static int mlxbf_pmc_program_counter(int blk_num, uint32_t cnt_num,
|
||||
uint32_t evt, bool is_l3)
|
||||
static int mlxbf_pmc_program_counter(unsigned int blk_num, u32 cnt_num, u32 evt, bool is_l3)
|
||||
{
|
||||
uint64_t perfctl, perfevt, perfmon_cfg;
|
||||
u64 perfctl, perfevt, perfmon_cfg;
|
||||
|
||||
if (cnt_num >= pmc->block[blk_num].counters)
|
||||
return -ENODEV;
|
||||
@ -1263,12 +1265,11 @@ static int mlxbf_pmc_program_counter(int blk_num, uint32_t cnt_num,
|
||||
}
|
||||
|
||||
/* Method to handle l3 counter reads */
|
||||
static int mlxbf_pmc_read_l3_counter(int blk_num, uint32_t cnt_num,
|
||||
uint64_t *result)
|
||||
static int mlxbf_pmc_read_l3_counter(unsigned int blk_num, u32 cnt_num, u64 *result)
|
||||
{
|
||||
uint32_t perfcnt_low = 0, perfcnt_high = 0;
|
||||
uint64_t value;
|
||||
u32 perfcnt_low = 0, perfcnt_high = 0;
|
||||
int status;
|
||||
u64 value;
|
||||
|
||||
status = mlxbf_pmc_readl(pmc->block[blk_num].mmio_base +
|
||||
MLXBF_PMC_L3C_PERF_CNT_LOW +
|
||||
@ -1295,11 +1296,10 @@ static int mlxbf_pmc_read_l3_counter(int blk_num, uint32_t cnt_num,
|
||||
}
|
||||
|
||||
/* Method to handle crspace counter reads */
|
||||
static int mlxbf_pmc_read_crspace_counter(int blk_num, uint32_t cnt_num,
|
||||
uint64_t *result)
|
||||
static int mlxbf_pmc_read_crspace_counter(unsigned int blk_num, u32 cnt_num, u64 *result)
|
||||
{
|
||||
uint32_t value;
|
||||
int status = 0;
|
||||
u32 value;
|
||||
|
||||
status = mlxbf_pmc_readl(pmc->block[blk_num].mmio_base +
|
||||
MLXBF_PMC_CRSPACE_PERFMON_VAL0(pmc->block[blk_num].counters) +
|
||||
@ -1313,11 +1313,10 @@ static int mlxbf_pmc_read_crspace_counter(int blk_num, uint32_t cnt_num,
|
||||
}
|
||||
|
||||
/* Method to read the counter value */
|
||||
static int mlxbf_pmc_read_counter(int blk_num, uint32_t cnt_num, bool is_l3,
|
||||
uint64_t *result)
|
||||
static int mlxbf_pmc_read_counter(unsigned int blk_num, u32 cnt_num, bool is_l3, u64 *result)
|
||||
{
|
||||
uint32_t perfcfg_offset, perfval_offset;
|
||||
uint64_t perfmon_cfg;
|
||||
u32 perfcfg_offset, perfval_offset;
|
||||
u64 perfmon_cfg;
|
||||
int status;
|
||||
|
||||
if (cnt_num >= pmc->block[blk_num].counters)
|
||||
@ -1351,13 +1350,11 @@ static int mlxbf_pmc_read_counter(int blk_num, uint32_t cnt_num, bool is_l3,
|
||||
}
|
||||
|
||||
/* Method to read L3 block event */
|
||||
static int mlxbf_pmc_read_l3_event(int blk_num, uint32_t cnt_num,
|
||||
uint64_t *result)
|
||||
static int mlxbf_pmc_read_l3_event(unsigned int blk_num, u32 cnt_num, u64 *result)
|
||||
{
|
||||
uint32_t perfcnt_sel = 0, perfcnt_sel_1 = 0;
|
||||
uint32_t *wordaddr;
|
||||
u32 perfcnt_sel = 0, perfcnt_sel_1 = 0, *wordaddr;
|
||||
void __iomem *pmcaddr;
|
||||
uint64_t evt;
|
||||
u64 evt;
|
||||
|
||||
/* Select appropriate register information */
|
||||
switch (cnt_num) {
|
||||
@ -1405,10 +1402,9 @@ static int mlxbf_pmc_read_l3_event(int blk_num, uint32_t cnt_num,
|
||||
}
|
||||
|
||||
/* Method to read crspace block event */
|
||||
static int mlxbf_pmc_read_crspace_event(int blk_num, uint32_t cnt_num,
|
||||
uint64_t *result)
|
||||
static int mlxbf_pmc_read_crspace_event(unsigned int blk_num, u32 cnt_num, u64 *result)
|
||||
{
|
||||
uint32_t word, evt;
|
||||
u32 word, evt;
|
||||
void *addr;
|
||||
int ret;
|
||||
|
||||
@ -1429,11 +1425,10 @@ static int mlxbf_pmc_read_crspace_event(int blk_num, uint32_t cnt_num,
|
||||
}
|
||||
|
||||
/* Method to find the event currently being monitored by a counter */
|
||||
static int mlxbf_pmc_read_event(int blk_num, uint32_t cnt_num, bool is_l3,
|
||||
uint64_t *result)
|
||||
static int mlxbf_pmc_read_event(unsigned int blk_num, u32 cnt_num, bool is_l3, u64 *result)
|
||||
{
|
||||
uint32_t perfcfg_offset, perfval_offset;
|
||||
uint64_t perfmon_cfg, perfevt;
|
||||
u32 perfcfg_offset, perfval_offset;
|
||||
u64 perfmon_cfg, perfevt;
|
||||
|
||||
if (cnt_num >= pmc->block[blk_num].counters)
|
||||
return -EINVAL;
|
||||
@ -1469,9 +1464,9 @@ static int mlxbf_pmc_read_event(int blk_num, uint32_t cnt_num, bool is_l3,
|
||||
}
|
||||
|
||||
/* Method to read a register */
|
||||
static int mlxbf_pmc_read_reg(int blk_num, uint32_t offset, uint64_t *result)
|
||||
static int mlxbf_pmc_read_reg(unsigned int blk_num, u32 offset, u64 *result)
|
||||
{
|
||||
uint32_t ecc_out;
|
||||
u32 ecc_out;
|
||||
|
||||
if (strstr(pmc->block_name[blk_num], "ecc")) {
|
||||
if (mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + offset,
|
||||
@ -1490,7 +1485,7 @@ static int mlxbf_pmc_read_reg(int blk_num, uint32_t offset, uint64_t *result)
|
||||
}
|
||||
|
||||
/* Method to write to a register */
|
||||
static int mlxbf_pmc_write_reg(int blk_num, uint32_t offset, uint64_t data)
|
||||
static int mlxbf_pmc_write_reg(unsigned int blk_num, u32 offset, u64 data)
|
||||
{
|
||||
if (strstr(pmc->block_name[blk_num], "ecc")) {
|
||||
return mlxbf_pmc_write(pmc->block[blk_num].mmio_base + offset,
|
||||
@ -1510,9 +1505,10 @@ static ssize_t mlxbf_pmc_counter_show(struct device *dev,
|
||||
{
|
||||
struct mlxbf_pmc_attribute *attr_counter = container_of(
|
||||
attr, struct mlxbf_pmc_attribute, dev_attr);
|
||||
int blk_num, cnt_num, offset;
|
||||
unsigned int blk_num, cnt_num;
|
||||
bool is_l3 = false;
|
||||
uint64_t value;
|
||||
int offset;
|
||||
u64 value;
|
||||
|
||||
blk_num = attr_counter->nr;
|
||||
cnt_num = attr_counter->index;
|
||||
@ -1544,14 +1540,16 @@ static ssize_t mlxbf_pmc_counter_store(struct device *dev,
|
||||
{
|
||||
struct mlxbf_pmc_attribute *attr_counter = container_of(
|
||||
attr, struct mlxbf_pmc_attribute, dev_attr);
|
||||
int blk_num, cnt_num, offset, err, data;
|
||||
unsigned int blk_num, cnt_num, data;
|
||||
bool is_l3 = false;
|
||||
uint64_t evt_num;
|
||||
u64 evt_num;
|
||||
int offset;
|
||||
int err;
|
||||
|
||||
blk_num = attr_counter->nr;
|
||||
cnt_num = attr_counter->index;
|
||||
|
||||
err = kstrtoint(buf, 0, &data);
|
||||
err = kstrtouint(buf, 0, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -1580,7 +1578,7 @@ static ssize_t mlxbf_pmc_counter_store(struct device *dev,
|
||||
if (err)
|
||||
return err;
|
||||
} else if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE) {
|
||||
if (sscanf(attr->attr.name, "counter%d", &cnt_num) != 1)
|
||||
if (sscanf(attr->attr.name, "counter%u", &cnt_num) != 1)
|
||||
return -EINVAL;
|
||||
err = mlxbf_pmc_clear_crspace_counter(blk_num, cnt_num);
|
||||
} else
|
||||
@ -1595,10 +1593,11 @@ static ssize_t mlxbf_pmc_event_show(struct device *dev,
|
||||
{
|
||||
struct mlxbf_pmc_attribute *attr_event = container_of(
|
||||
attr, struct mlxbf_pmc_attribute, dev_attr);
|
||||
int blk_num, cnt_num, err;
|
||||
unsigned int blk_num, cnt_num;
|
||||
bool is_l3 = false;
|
||||
uint64_t evt_num;
|
||||
char *evt_name;
|
||||
u64 evt_num;
|
||||
int err;
|
||||
|
||||
blk_num = attr_event->nr;
|
||||
cnt_num = attr_event->index;
|
||||
@ -1624,8 +1623,10 @@ static ssize_t mlxbf_pmc_event_store(struct device *dev,
|
||||
{
|
||||
struct mlxbf_pmc_attribute *attr_event = container_of(
|
||||
attr, struct mlxbf_pmc_attribute, dev_attr);
|
||||
int blk_num, cnt_num, evt_num, err;
|
||||
unsigned int blk_num, cnt_num;
|
||||
bool is_l3 = false;
|
||||
int evt_num;
|
||||
int err;
|
||||
|
||||
blk_num = attr_event->nr;
|
||||
cnt_num = attr_event->index;
|
||||
@ -1636,7 +1637,7 @@ static ssize_t mlxbf_pmc_event_store(struct device *dev,
|
||||
if (evt_num < 0)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
err = kstrtoint(buf, 0, &evt_num);
|
||||
err = kstrtouint(buf, 0, &evt_num);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
@ -1658,9 +1659,11 @@ static ssize_t mlxbf_pmc_event_list_show(struct device *dev,
|
||||
{
|
||||
struct mlxbf_pmc_attribute *attr_event_list = container_of(
|
||||
attr, struct mlxbf_pmc_attribute, dev_attr);
|
||||
int blk_num, i, size, len = 0, ret = 0;
|
||||
const struct mlxbf_pmc_events *events;
|
||||
char e_info[MLXBF_PMC_EVENT_INFO_LEN];
|
||||
unsigned int blk_num, i, len = 0;
|
||||
size_t size;
|
||||
int ret = 0;
|
||||
|
||||
blk_num = attr_event_list->nr;
|
||||
|
||||
@ -1686,8 +1689,8 @@ static ssize_t mlxbf_pmc_enable_show(struct device *dev,
|
||||
{
|
||||
struct mlxbf_pmc_attribute *attr_enable = container_of(
|
||||
attr, struct mlxbf_pmc_attribute, dev_attr);
|
||||
uint32_t perfcnt_cfg, word;
|
||||
int blk_num, value;
|
||||
unsigned int blk_num, value;
|
||||
u32 perfcnt_cfg, word;
|
||||
|
||||
blk_num = attr_enable->nr;
|
||||
|
||||
@ -1707,7 +1710,7 @@ static ssize_t mlxbf_pmc_enable_show(struct device *dev,
|
||||
value = FIELD_GET(MLXBF_PMC_L3C_PERF_CNT_CFG_EN, perfcnt_cfg);
|
||||
}
|
||||
|
||||
return sysfs_emit(buf, "%d\n", value);
|
||||
return sysfs_emit(buf, "%u\n", value);
|
||||
}
|
||||
|
||||
/* Store function for "enable" sysfs files - only for l3cache & crspace */
|
||||
@ -1717,12 +1720,13 @@ static ssize_t mlxbf_pmc_enable_store(struct device *dev,
|
||||
{
|
||||
struct mlxbf_pmc_attribute *attr_enable = container_of(
|
||||
attr, struct mlxbf_pmc_attribute, dev_attr);
|
||||
int err, en, blk_num;
|
||||
uint32_t word;
|
||||
unsigned int en, blk_num;
|
||||
u32 word;
|
||||
int err;
|
||||
|
||||
blk_num = attr_enable->nr;
|
||||
|
||||
err = kstrtoint(buf, 0, &en);
|
||||
err = kstrtouint(buf, 0, &en);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -1760,10 +1764,13 @@ static ssize_t mlxbf_pmc_enable_store(struct device *dev,
|
||||
}
|
||||
|
||||
/* Populate attributes for blocks with counters to monitor performance */
|
||||
static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num)
|
||||
static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_num)
|
||||
{
|
||||
struct mlxbf_pmc_attribute *attr;
|
||||
int i = 0, j = 0;
|
||||
unsigned int i = 0, j = 0;
|
||||
|
||||
if (!mlxbf_pmc_event_supported(pmc->block_name[blk_num]))
|
||||
return -ENOENT;
|
||||
|
||||
/* "event_list" sysfs to list events supported by the block */
|
||||
attr = &pmc->block[blk_num].attr_event_list;
|
||||
@ -1812,8 +1819,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num)
|
||||
attr->dev_attr.store = mlxbf_pmc_counter_store;
|
||||
attr->index = j;
|
||||
attr->nr = blk_num;
|
||||
attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
|
||||
"counter%d", j);
|
||||
attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, "counter%u", j);
|
||||
if (!attr->dev_attr.attr.name)
|
||||
return -ENOMEM;
|
||||
pmc->block[blk_num].block_attr[++i] = &attr->dev_attr.attr;
|
||||
@ -1825,8 +1831,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num)
|
||||
attr->dev_attr.store = mlxbf_pmc_event_store;
|
||||
attr->index = j;
|
||||
attr->nr = blk_num;
|
||||
attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
|
||||
"event%d", j);
|
||||
attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, "event%u", j);
|
||||
if (!attr->dev_attr.attr.name)
|
||||
return -ENOMEM;
|
||||
pmc->block[blk_num].block_attr[++i] = &attr->dev_attr.attr;
|
||||
@ -1837,30 +1842,31 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num)
|
||||
}
|
||||
|
||||
/* Populate attributes for blocks with registers to monitor performance */
|
||||
static int mlxbf_pmc_init_perftype_reg(struct device *dev, int blk_num)
|
||||
static int mlxbf_pmc_init_perftype_reg(struct device *dev, unsigned int blk_num)
|
||||
{
|
||||
struct mlxbf_pmc_attribute *attr;
|
||||
const struct mlxbf_pmc_events *events;
|
||||
int i = 0, j = 0;
|
||||
struct mlxbf_pmc_attribute *attr;
|
||||
unsigned int i = 0;
|
||||
size_t count = 0;
|
||||
|
||||
events = mlxbf_pmc_event_list(pmc->block_name[blk_num], &j);
|
||||
events = mlxbf_pmc_event_list(pmc->block_name[blk_num], &count);
|
||||
if (!events)
|
||||
return -EINVAL;
|
||||
return -ENOENT;
|
||||
|
||||
pmc->block[blk_num].attr_event = devm_kcalloc(
|
||||
dev, j, sizeof(struct mlxbf_pmc_attribute), GFP_KERNEL);
|
||||
dev, count, sizeof(struct mlxbf_pmc_attribute), GFP_KERNEL);
|
||||
if (!pmc->block[blk_num].attr_event)
|
||||
return -ENOMEM;
|
||||
|
||||
while (j > 0) {
|
||||
--j;
|
||||
attr = &pmc->block[blk_num].attr_event[j];
|
||||
while (count > 0) {
|
||||
--count;
|
||||
attr = &pmc->block[blk_num].attr_event[count];
|
||||
attr->dev_attr.attr.mode = 0644;
|
||||
attr->dev_attr.show = mlxbf_pmc_counter_show;
|
||||
attr->dev_attr.store = mlxbf_pmc_counter_store;
|
||||
attr->nr = blk_num;
|
||||
attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
|
||||
events[j].evt_name);
|
||||
events[count].evt_name);
|
||||
if (!attr->dev_attr.attr.name)
|
||||
return -ENOMEM;
|
||||
pmc->block[blk_num].block_attr[i] = &attr->dev_attr.attr;
|
||||
@ -1872,7 +1878,7 @@ static int mlxbf_pmc_init_perftype_reg(struct device *dev, int blk_num)
|
||||
}
|
||||
|
||||
/* Helper to create the bfperf sysfs sub-directories and files */
|
||||
static int mlxbf_pmc_create_groups(struct device *dev, int blk_num)
|
||||
static int mlxbf_pmc_create_groups(struct device *dev, unsigned int blk_num)
|
||||
{
|
||||
int err;
|
||||
|
||||
@ -1883,7 +1889,7 @@ static int mlxbf_pmc_create_groups(struct device *dev, int blk_num)
|
||||
else if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_REGISTER)
|
||||
err = mlxbf_pmc_init_perftype_reg(dev, blk_num);
|
||||
else
|
||||
err = -EINVAL;
|
||||
err = -ENOENT;
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
@ -1914,19 +1920,20 @@ static bool mlxbf_pmc_guid_match(const guid_t *guid,
|
||||
/* Helper to map the Performance Counters from the varios blocks */
|
||||
static int mlxbf_pmc_map_counters(struct device *dev)
|
||||
{
|
||||
uint64_t info[MLXBF_PMC_INFO_SZ];
|
||||
int i, tile_num, ret;
|
||||
u64 info[MLXBF_PMC_INFO_SZ];
|
||||
unsigned int tile_num, i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < pmc->total_blocks; ++i) {
|
||||
/* Create sysfs for tiles only if block number < tile_count */
|
||||
if (strstr(pmc->block_name[i], "tilenet")) {
|
||||
if (sscanf(pmc->block_name[i], "tilenet%d", &tile_num) != 1)
|
||||
if (sscanf(pmc->block_name[i], "tilenet%u", &tile_num) != 1)
|
||||
continue;
|
||||
|
||||
if (tile_num >= pmc->tile_count)
|
||||
continue;
|
||||
} else if (strstr(pmc->block_name[i], "tile")) {
|
||||
if (sscanf(pmc->block_name[i], "tile%d", &tile_num) != 1)
|
||||
if (sscanf(pmc->block_name[i], "tile%u", &tile_num) != 1)
|
||||
continue;
|
||||
|
||||
if (tile_num >= pmc->tile_count)
|
||||
@ -1936,9 +1943,9 @@ static int mlxbf_pmc_map_counters(struct device *dev)
|
||||
/* Create sysfs only for enabled MSS blocks */
|
||||
if (strstr(pmc->block_name[i], "mss") &&
|
||||
pmc->event_set == MLXBF_PMC_EVENT_SET_BF3) {
|
||||
int mss_num;
|
||||
unsigned int mss_num;
|
||||
|
||||
if (sscanf(pmc->block_name[i], "mss%d", &mss_num) != 1)
|
||||
if (sscanf(pmc->block_name[i], "mss%u", &mss_num) != 1)
|
||||
continue;
|
||||
|
||||
if (!((pmc->mss_enable >> mss_num) & 0x1))
|
||||
@ -1947,17 +1954,17 @@ static int mlxbf_pmc_map_counters(struct device *dev)
|
||||
|
||||
/* Create sysfs only for enabled LLT blocks */
|
||||
if (strstr(pmc->block_name[i], "llt_miss")) {
|
||||
int llt_num;
|
||||
unsigned int llt_num;
|
||||
|
||||
if (sscanf(pmc->block_name[i], "llt_miss%d", &llt_num) != 1)
|
||||
if (sscanf(pmc->block_name[i], "llt_miss%u", &llt_num) != 1)
|
||||
continue;
|
||||
|
||||
if (!((pmc->llt_enable >> llt_num) & 0x1))
|
||||
continue;
|
||||
} else if (strstr(pmc->block_name[i], "llt")) {
|
||||
int llt_num;
|
||||
unsigned int llt_num;
|
||||
|
||||
if (sscanf(pmc->block_name[i], "llt%d", &llt_num) != 1)
|
||||
if (sscanf(pmc->block_name[i], "llt%u", &llt_num) != 1)
|
||||
continue;
|
||||
|
||||
if (!((pmc->llt_enable >> llt_num) & 0x1))
|
||||
@ -1987,6 +1994,10 @@ static int mlxbf_pmc_map_counters(struct device *dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = mlxbf_pmc_create_groups(dev, i);
|
||||
if (ret == -ENOENT) {
|
||||
dev_warn(dev, "ignoring unsupported block: '%s'\n", pmc->block_name[i]);
|
||||
continue;
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -18,9 +18,11 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#define DRIVER_NAME "amd_hsmp"
|
||||
#define DRIVER_VERSION "2.0"
|
||||
#define DRIVER_VERSION "2.2"
|
||||
#define ACPI_HSMP_DEVICE_HID "AMDI0097"
|
||||
|
||||
/* HSMP Status / Error codes */
|
||||
#define HSMP_STATUS_NOT_READY 0x00
|
||||
@ -40,9 +42,11 @@
|
||||
* register into the SMN_INDEX register, and reads/writes the SMN_DATA reg.
|
||||
* Below are required SMN address for HSMP Mailbox register offsets in SMU address space
|
||||
*/
|
||||
#define SMN_HSMP_MSG_ID 0x3B10534
|
||||
#define SMN_HSMP_MSG_RESP 0x3B10980
|
||||
#define SMN_HSMP_MSG_DATA 0x3B109E0
|
||||
#define SMN_HSMP_BASE 0x3B00000
|
||||
#define SMN_HSMP_MSG_ID 0x0010534
|
||||
#define SMN_HSMP_MSG_ID_F1A_M0H 0x0010934
|
||||
#define SMN_HSMP_MSG_RESP 0x0010980
|
||||
#define SMN_HSMP_MSG_DATA 0x00109E0
|
||||
|
||||
#define HSMP_INDEX_REG 0xc4
|
||||
#define HSMP_DATA_REG 0xc8
|
||||
@ -53,41 +57,86 @@
|
||||
|
||||
#define HSMP_ATTR_GRP_NAME_SIZE 10
|
||||
|
||||
/* These are the strings specified in ACPI table */
|
||||
#define MSG_IDOFF_STR "MsgIdOffset"
|
||||
#define MSG_ARGOFF_STR "MsgArgOffset"
|
||||
#define MSG_RESPOFF_STR "MsgRspOffset"
|
||||
|
||||
#define MAX_AMD_SOCKETS 8
|
||||
|
||||
struct hsmp_mbaddr_info {
|
||||
u32 base_addr;
|
||||
u32 msg_id_off;
|
||||
u32 msg_resp_off;
|
||||
u32 msg_arg_off;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct hsmp_socket {
|
||||
struct bin_attribute hsmp_attr;
|
||||
struct hsmp_mbaddr_info mbinfo;
|
||||
void __iomem *metric_tbl_addr;
|
||||
void __iomem *virt_base_addr;
|
||||
struct semaphore hsmp_sem;
|
||||
char name[HSMP_ATTR_GRP_NAME_SIZE];
|
||||
struct pci_dev *root;
|
||||
struct device *dev;
|
||||
u16 sock_ind;
|
||||
};
|
||||
|
||||
struct hsmp_plat_device {
|
||||
struct miscdevice hsmp_device;
|
||||
struct hsmp_socket *sock;
|
||||
struct device *dev;
|
||||
u32 proto_ver;
|
||||
u16 num_sockets;
|
||||
bool is_acpi_device;
|
||||
bool is_probed;
|
||||
};
|
||||
|
||||
static struct hsmp_plat_device plat_dev;
|
||||
|
||||
static int amd_hsmp_rdwr(struct pci_dev *root, u32 address,
|
||||
u32 *value, bool write)
|
||||
static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset,
|
||||
u32 *value, bool write)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pci_write_config_dword(root, HSMP_INDEX_REG, address);
|
||||
if (!sock->root)
|
||||
return -ENODEV;
|
||||
|
||||
ret = pci_write_config_dword(sock->root, HSMP_INDEX_REG,
|
||||
sock->mbinfo.base_addr + offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = (write ? pci_write_config_dword(root, HSMP_DATA_REG, *value)
|
||||
: pci_read_config_dword(root, HSMP_DATA_REG, value));
|
||||
ret = (write ? pci_write_config_dword(sock->root, HSMP_DATA_REG, *value)
|
||||
: pci_read_config_dword(sock->root, HSMP_DATA_REG, value));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void amd_hsmp_acpi_rdwr(struct hsmp_socket *sock, u32 offset,
|
||||
u32 *value, bool write)
|
||||
{
|
||||
if (write)
|
||||
iowrite32(*value, sock->virt_base_addr + offset);
|
||||
else
|
||||
*value = ioread32(sock->virt_base_addr + offset);
|
||||
}
|
||||
|
||||
static int amd_hsmp_rdwr(struct hsmp_socket *sock, u32 offset,
|
||||
u32 *value, bool write)
|
||||
{
|
||||
if (plat_dev.is_acpi_device)
|
||||
amd_hsmp_acpi_rdwr(sock, offset, value, write);
|
||||
else
|
||||
return amd_hsmp_pci_rdwr(sock, offset, value, write);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a message to the HSMP port via PCI-e config space registers.
|
||||
* Send a message to the HSMP port via PCI-e config space registers
|
||||
* or by writing to MMIO space.
|
||||
*
|
||||
* The caller is expected to zero out any unused arguments.
|
||||
* If a response is expected, the number of response words should be greater than 0.
|
||||
@ -95,16 +144,19 @@ static int amd_hsmp_rdwr(struct pci_dev *root, u32 address,
|
||||
* Returns 0 for success and populates the requested number of arguments.
|
||||
* Returns a negative error code for failure.
|
||||
*/
|
||||
static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
|
||||
static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *msg)
|
||||
{
|
||||
struct hsmp_mbaddr_info *mbinfo;
|
||||
unsigned long timeout, short_sleep;
|
||||
u32 mbox_status;
|
||||
u32 index;
|
||||
int ret;
|
||||
|
||||
mbinfo = &sock->mbinfo;
|
||||
|
||||
/* Clear the status register */
|
||||
mbox_status = HSMP_STATUS_NOT_READY;
|
||||
ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_WR);
|
||||
ret = amd_hsmp_rdwr(sock, mbinfo->msg_resp_off, &mbox_status, HSMP_WR);
|
||||
if (ret) {
|
||||
pr_err("Error %d clearing mailbox status register\n", ret);
|
||||
return ret;
|
||||
@ -113,7 +165,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
|
||||
index = 0;
|
||||
/* Write any message arguments */
|
||||
while (index < msg->num_args) {
|
||||
ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_DATA + (index << 2),
|
||||
ret = amd_hsmp_rdwr(sock, mbinfo->msg_arg_off + (index << 2),
|
||||
&msg->args[index], HSMP_WR);
|
||||
if (ret) {
|
||||
pr_err("Error %d writing message argument %d\n", ret, index);
|
||||
@ -123,7 +175,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
|
||||
}
|
||||
|
||||
/* Write the message ID which starts the operation */
|
||||
ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_ID, &msg->msg_id, HSMP_WR);
|
||||
ret = amd_hsmp_rdwr(sock, mbinfo->msg_id_off, &msg->msg_id, HSMP_WR);
|
||||
if (ret) {
|
||||
pr_err("Error %d writing message ID %u\n", ret, msg->msg_id);
|
||||
return ret;
|
||||
@ -140,7 +192,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
|
||||
timeout = jiffies + msecs_to_jiffies(HSMP_MSG_TIMEOUT);
|
||||
|
||||
while (time_before(jiffies, timeout)) {
|
||||
ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_RD);
|
||||
ret = amd_hsmp_rdwr(sock, mbinfo->msg_resp_off, &mbox_status, HSMP_RD);
|
||||
if (ret) {
|
||||
pr_err("Error %d reading mailbox status\n", ret);
|
||||
return ret;
|
||||
@ -175,7 +227,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
|
||||
*/
|
||||
index = 0;
|
||||
while (index < msg->response_sz) {
|
||||
ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_DATA + (index << 2),
|
||||
ret = amd_hsmp_rdwr(sock, mbinfo->msg_arg_off + (index << 2),
|
||||
&msg->args[index], HSMP_RD);
|
||||
if (ret) {
|
||||
pr_err("Error %d reading response %u for message ID:%u\n",
|
||||
@ -208,21 +260,19 @@ static int validate_message(struct hsmp_message *msg)
|
||||
|
||||
int hsmp_send_message(struct hsmp_message *msg)
|
||||
{
|
||||
struct hsmp_socket *sock = &plat_dev.sock[msg->sock_ind];
|
||||
struct amd_northbridge *nb;
|
||||
struct hsmp_socket *sock;
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
return -EINVAL;
|
||||
|
||||
nb = node_to_amd_nb(msg->sock_ind);
|
||||
if (!nb || !nb->root)
|
||||
return -ENODEV;
|
||||
|
||||
ret = validate_message(msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!plat_dev.sock || msg->sock_ind >= plat_dev.num_sockets)
|
||||
return -ENODEV;
|
||||
sock = &plat_dev.sock[msg->sock_ind];
|
||||
|
||||
/*
|
||||
* The time taken by smu operation to complete is between
|
||||
* 10us to 1ms. Sometime it may take more time.
|
||||
@ -233,7 +283,7 @@ int hsmp_send_message(struct hsmp_message *msg)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = __hsmp_send_message(nb->root, msg);
|
||||
ret = __hsmp_send_message(sock, msg);
|
||||
|
||||
up(&sock->hsmp_sem);
|
||||
|
||||
@ -244,12 +294,7 @@ EXPORT_SYMBOL_GPL(hsmp_send_message);
|
||||
static int hsmp_test(u16 sock_ind, u32 value)
|
||||
{
|
||||
struct hsmp_message msg = { 0 };
|
||||
struct amd_northbridge *nb;
|
||||
int ret = -ENODEV;
|
||||
|
||||
nb = node_to_amd_nb(sock_ind);
|
||||
if (!nb || !nb->root)
|
||||
return ret;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Test the hsmp port by performing TEST command. The test message
|
||||
@ -261,14 +306,15 @@ static int hsmp_test(u16 sock_ind, u32 value)
|
||||
msg.args[0] = value;
|
||||
msg.sock_ind = sock_ind;
|
||||
|
||||
ret = __hsmp_send_message(nb->root, &msg);
|
||||
ret = hsmp_send_message(&msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Check the response value */
|
||||
if (msg.args[0] != (value + 1)) {
|
||||
pr_err("Socket %d test message failed, Expected 0x%08X, received 0x%08X\n",
|
||||
sock_ind, (value + 1), msg.args[0]);
|
||||
dev_err(plat_dev.sock[sock_ind].dev,
|
||||
"Socket %d test message failed, Expected 0x%08X, received 0x%08X\n",
|
||||
sock_ind, (value + 1), msg.args[0]);
|
||||
return -EBADE;
|
||||
}
|
||||
|
||||
@ -337,6 +383,181 @@ static const struct file_operations hsmp_fops = {
|
||||
.compat_ioctl = hsmp_ioctl,
|
||||
};
|
||||
|
||||
/* This is the UUID used for HSMP */
|
||||
static const guid_t acpi_hsmp_uuid = GUID_INIT(0xb74d619d, 0x5707, 0x48bd,
|
||||
0xa6, 0x9f, 0x4e, 0xa2,
|
||||
0x87, 0x1f, 0xc2, 0xf6);
|
||||
|
||||
static inline bool is_acpi_hsmp_uuid(union acpi_object *obj)
|
||||
{
|
||||
if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == UUID_SIZE)
|
||||
return guid_equal((guid_t *)obj->buffer.pointer, &acpi_hsmp_uuid);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int hsmp_get_uid(struct device *dev, u16 *sock_ind)
|
||||
{
|
||||
char *uid;
|
||||
|
||||
/*
|
||||
* UID (ID00, ID01..IDXX) is used for differentiating sockets,
|
||||
* read it and strip the "ID" part of it and convert the remaining
|
||||
* bytes to integer.
|
||||
*/
|
||||
uid = acpi_device_uid(ACPI_COMPANION(dev));
|
||||
|
||||
return kstrtou16(uid + 2, 10, sock_ind);
|
||||
}
|
||||
|
||||
static acpi_status hsmp_resource(struct acpi_resource *res, void *data)
|
||||
{
|
||||
struct hsmp_socket *sock = data;
|
||||
struct resource r;
|
||||
|
||||
switch (res->type) {
|
||||
case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
|
||||
if (!acpi_dev_resource_memory(res, &r))
|
||||
return AE_ERROR;
|
||||
if (!r.start || r.end < r.start || !(r.flags & IORESOURCE_MEM_WRITEABLE))
|
||||
return AE_ERROR;
|
||||
sock->mbinfo.base_addr = r.start;
|
||||
sock->mbinfo.size = resource_size(&r);
|
||||
break;
|
||||
case ACPI_RESOURCE_TYPE_END_TAG:
|
||||
break;
|
||||
default:
|
||||
return AE_ERROR;
|
||||
}
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static int hsmp_read_acpi_dsd(struct hsmp_socket *sock)
|
||||
{
|
||||
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *guid, *mailbox_package;
|
||||
union acpi_object *dsd;
|
||||
acpi_status status;
|
||||
int ret = 0;
|
||||
int j;
|
||||
|
||||
status = acpi_evaluate_object_typed(ACPI_HANDLE(sock->dev), "_DSD", NULL,
|
||||
&buf, ACPI_TYPE_PACKAGE);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_err(sock->dev, "Failed to read mailbox reg offsets from DSD table, err: %s\n",
|
||||
acpi_format_exception(status));
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dsd = buf.pointer;
|
||||
|
||||
/* HSMP _DSD property should contain 2 objects.
|
||||
* 1. guid which is an acpi object of type ACPI_TYPE_BUFFER
|
||||
* 2. mailbox which is an acpi object of type ACPI_TYPE_PACKAGE
|
||||
* This mailbox object contains 3 more acpi objects of type
|
||||
* ACPI_TYPE_PACKAGE for holding msgid, msgresp, msgarg offsets
|
||||
* these packages inturn contain 2 acpi objects of type
|
||||
* ACPI_TYPE_STRING and ACPI_TYPE_INTEGER
|
||||
*/
|
||||
if (!dsd || dsd->type != ACPI_TYPE_PACKAGE || dsd->package.count != 2) {
|
||||
ret = -EINVAL;
|
||||
goto free_buf;
|
||||
}
|
||||
|
||||
guid = &dsd->package.elements[0];
|
||||
mailbox_package = &dsd->package.elements[1];
|
||||
if (!is_acpi_hsmp_uuid(guid) || mailbox_package->type != ACPI_TYPE_PACKAGE) {
|
||||
dev_err(sock->dev, "Invalid hsmp _DSD table data\n");
|
||||
ret = -EINVAL;
|
||||
goto free_buf;
|
||||
}
|
||||
|
||||
for (j = 0; j < mailbox_package->package.count; j++) {
|
||||
union acpi_object *msgobj, *msgstr, *msgint;
|
||||
|
||||
msgobj = &mailbox_package->package.elements[j];
|
||||
msgstr = &msgobj->package.elements[0];
|
||||
msgint = &msgobj->package.elements[1];
|
||||
|
||||
/* package should have 1 string and 1 integer object */
|
||||
if (msgobj->type != ACPI_TYPE_PACKAGE ||
|
||||
msgstr->type != ACPI_TYPE_STRING ||
|
||||
msgint->type != ACPI_TYPE_INTEGER) {
|
||||
ret = -EINVAL;
|
||||
goto free_buf;
|
||||
}
|
||||
|
||||
if (!strncmp(msgstr->string.pointer, MSG_IDOFF_STR,
|
||||
msgstr->string.length)) {
|
||||
sock->mbinfo.msg_id_off = msgint->integer.value;
|
||||
} else if (!strncmp(msgstr->string.pointer, MSG_RESPOFF_STR,
|
||||
msgstr->string.length)) {
|
||||
sock->mbinfo.msg_resp_off = msgint->integer.value;
|
||||
} else if (!strncmp(msgstr->string.pointer, MSG_ARGOFF_STR,
|
||||
msgstr->string.length)) {
|
||||
sock->mbinfo.msg_arg_off = msgint->integer.value;
|
||||
} else {
|
||||
ret = -ENOENT;
|
||||
goto free_buf;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sock->mbinfo.msg_id_off || !sock->mbinfo.msg_resp_off ||
|
||||
!sock->mbinfo.msg_arg_off)
|
||||
ret = -EINVAL;
|
||||
|
||||
free_buf:
|
||||
ACPI_FREE(buf.pointer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hsmp_read_acpi_crs(struct hsmp_socket *sock)
|
||||
{
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_walk_resources(ACPI_HANDLE(sock->dev), METHOD_NAME__CRS,
|
||||
hsmp_resource, sock);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_err(sock->dev, "Failed to look up MP1 base address from CRS method, err: %s\n",
|
||||
acpi_format_exception(status));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!sock->mbinfo.base_addr || !sock->mbinfo.size)
|
||||
return -EINVAL;
|
||||
|
||||
/* The mapped region should be un cached */
|
||||
sock->virt_base_addr = devm_ioremap_uc(sock->dev, sock->mbinfo.base_addr,
|
||||
sock->mbinfo.size);
|
||||
if (!sock->virt_base_addr) {
|
||||
dev_err(sock->dev, "Failed to ioremap MP1 base address\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse the ACPI table to read the data */
|
||||
static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind)
|
||||
{
|
||||
struct hsmp_socket *sock = &plat_dev.sock[sock_ind];
|
||||
int ret;
|
||||
|
||||
sock->sock_ind = sock_ind;
|
||||
sock->dev = dev;
|
||||
plat_dev.is_acpi_device = true;
|
||||
|
||||
sema_init(&sock->hsmp_sem, 1);
|
||||
|
||||
/* Read MP1 base address from CRS method */
|
||||
ret = hsmp_read_acpi_crs(sock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Read mailbox offsets from DSD table */
|
||||
return hsmp_read_acpi_dsd(sock);
|
||||
}
|
||||
|
||||
static ssize_t hsmp_metric_tbl_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
@ -345,14 +566,12 @@ static ssize_t hsmp_metric_tbl_read(struct file *filp, struct kobject *kobj,
|
||||
struct hsmp_message msg = { 0 };
|
||||
int ret;
|
||||
|
||||
if (!sock)
|
||||
return -EINVAL;
|
||||
|
||||
/* Do not support lseek(), reads entire metric table */
|
||||
if (count < bin_attr->size) {
|
||||
dev_err(plat_dev.dev, "Wrong buffer size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!sock) {
|
||||
dev_err(plat_dev.dev, "Failed to read attribute private data\n");
|
||||
dev_err(sock->dev, "Wrong buffer size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -388,13 +607,13 @@ static int hsmp_get_tbl_dram_base(u16 sock_ind)
|
||||
*/
|
||||
dram_addr = msg.args[0] | ((u64)(msg.args[1]) << 32);
|
||||
if (!dram_addr) {
|
||||
dev_err(plat_dev.dev, "Invalid DRAM address for metric table\n");
|
||||
dev_err(sock->dev, "Invalid DRAM address for metric table\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
sock->metric_tbl_addr = devm_ioremap(plat_dev.dev, dram_addr,
|
||||
sock->metric_tbl_addr = devm_ioremap(sock->dev, dram_addr,
|
||||
sizeof(struct hsmp_metric_table));
|
||||
if (!sock->metric_tbl_addr) {
|
||||
dev_err(plat_dev.dev, "Failed to ioremap metric table addr\n");
|
||||
dev_err(sock->dev, "Failed to ioremap metric table addr\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
@ -422,65 +641,91 @@ static int hsmp_init_metric_tbl_bin_attr(struct bin_attribute **hattrs, u16 sock
|
||||
hattrs[0] = hattr;
|
||||
|
||||
if (plat_dev.proto_ver == HSMP_PROTO_VER6)
|
||||
return (hsmp_get_tbl_dram_base(sock_ind));
|
||||
return hsmp_get_tbl_dram_base(sock_ind);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* One bin sysfs for metrics table*/
|
||||
/* One bin sysfs for metrics table */
|
||||
#define NUM_HSMP_ATTRS 1
|
||||
|
||||
static int hsmp_create_sysfs_interface(void)
|
||||
static int hsmp_create_attr_list(struct attribute_group *attr_grp,
|
||||
struct device *dev, u16 sock_ind)
|
||||
{
|
||||
struct bin_attribute **hsmp_bin_attrs;
|
||||
|
||||
/* Null terminated list of attributes */
|
||||
hsmp_bin_attrs = devm_kcalloc(dev, NUM_HSMP_ATTRS + 1,
|
||||
sizeof(*hsmp_bin_attrs),
|
||||
GFP_KERNEL);
|
||||
if (!hsmp_bin_attrs)
|
||||
return -ENOMEM;
|
||||
|
||||
attr_grp->bin_attrs = hsmp_bin_attrs;
|
||||
|
||||
return hsmp_init_metric_tbl_bin_attr(hsmp_bin_attrs, sock_ind);
|
||||
}
|
||||
|
||||
static int hsmp_create_non_acpi_sysfs_if(struct device *dev)
|
||||
{
|
||||
const struct attribute_group **hsmp_attr_grps;
|
||||
struct bin_attribute **hsmp_bin_attrs;
|
||||
struct attribute_group *attr_grp;
|
||||
int ret;
|
||||
u16 i;
|
||||
|
||||
/* String formatting is currently limited to u8 sockets */
|
||||
if (WARN_ON(plat_dev.num_sockets > U8_MAX))
|
||||
return -ERANGE;
|
||||
|
||||
hsmp_attr_grps = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group *) *
|
||||
(plat_dev.num_sockets + 1), GFP_KERNEL);
|
||||
hsmp_attr_grps = devm_kcalloc(dev, plat_dev.num_sockets + 1,
|
||||
sizeof(*hsmp_attr_grps),
|
||||
GFP_KERNEL);
|
||||
if (!hsmp_attr_grps)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Create a sysfs directory for each socket */
|
||||
for (i = 0; i < plat_dev.num_sockets; i++) {
|
||||
attr_grp = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group), GFP_KERNEL);
|
||||
attr_grp = devm_kzalloc(dev, sizeof(struct attribute_group),
|
||||
GFP_KERNEL);
|
||||
if (!attr_grp)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(plat_dev.sock[i].name, HSMP_ATTR_GRP_NAME_SIZE, "socket%u", (u8)i);
|
||||
attr_grp->name = plat_dev.sock[i].name;
|
||||
|
||||
/* Null terminated list of attributes */
|
||||
hsmp_bin_attrs = devm_kzalloc(plat_dev.dev, sizeof(struct bin_attribute *) *
|
||||
(NUM_HSMP_ATTRS + 1), GFP_KERNEL);
|
||||
if (!hsmp_bin_attrs)
|
||||
return -ENOMEM;
|
||||
|
||||
attr_grp->bin_attrs = hsmp_bin_attrs;
|
||||
attr_grp->name = plat_dev.sock[i].name;
|
||||
attr_grp->is_bin_visible = hsmp_is_sock_attr_visible;
|
||||
hsmp_attr_grps[i] = attr_grp;
|
||||
|
||||
/* Now create the leaf nodes */
|
||||
ret = hsmp_init_metric_tbl_bin_attr(hsmp_bin_attrs, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
hsmp_create_attr_list(attr_grp, dev, i);
|
||||
}
|
||||
return devm_device_add_groups(plat_dev.dev, hsmp_attr_grps);
|
||||
|
||||
return devm_device_add_groups(dev, hsmp_attr_grps);
|
||||
}
|
||||
|
||||
static int hsmp_cache_proto_ver(void)
|
||||
static int hsmp_create_acpi_sysfs_if(struct device *dev)
|
||||
{
|
||||
struct attribute_group *attr_grp;
|
||||
u16 sock_ind;
|
||||
int ret;
|
||||
|
||||
attr_grp = devm_kzalloc(dev, sizeof(struct attribute_group), GFP_KERNEL);
|
||||
if (!attr_grp)
|
||||
return -ENOMEM;
|
||||
|
||||
attr_grp->is_bin_visible = hsmp_is_sock_attr_visible;
|
||||
|
||||
ret = hsmp_get_uid(dev, &sock_ind);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = hsmp_create_attr_list(attr_grp, dev, sock_ind);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_device_add_group(dev, attr_grp);
|
||||
}
|
||||
|
||||
static int hsmp_cache_proto_ver(u16 sock_ind)
|
||||
{
|
||||
struct hsmp_message msg = { 0 };
|
||||
int ret;
|
||||
|
||||
msg.msg_id = HSMP_GET_PROTO_VER;
|
||||
msg.sock_ind = 0;
|
||||
msg.sock_ind = sock_ind;
|
||||
msg.response_sz = hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz;
|
||||
|
||||
ret = hsmp_send_message(&msg);
|
||||
@ -490,45 +735,150 @@ static int hsmp_cache_proto_ver(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hsmp_pltdrv_probe(struct platform_device *pdev)
|
||||
static inline bool is_f1a_m0h(void)
|
||||
{
|
||||
if (boot_cpu_data.x86 == 0x1A && boot_cpu_data.x86_model <= 0x0F)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int init_platform_device(struct device *dev)
|
||||
{
|
||||
struct hsmp_socket *sock;
|
||||
int ret, i;
|
||||
|
||||
plat_dev.sock = devm_kzalloc(&pdev->dev,
|
||||
(plat_dev.num_sockets * sizeof(struct hsmp_socket)),
|
||||
GFP_KERNEL);
|
||||
if (!plat_dev.sock)
|
||||
return -ENOMEM;
|
||||
plat_dev.dev = &pdev->dev;
|
||||
|
||||
for (i = 0; i < plat_dev.num_sockets; i++) {
|
||||
sema_init(&plat_dev.sock[i].hsmp_sem, 1);
|
||||
plat_dev.sock[i].sock_ind = i;
|
||||
if (!node_to_amd_nb(i))
|
||||
return -ENODEV;
|
||||
sock = &plat_dev.sock[i];
|
||||
sock->root = node_to_amd_nb(i)->root;
|
||||
sock->sock_ind = i;
|
||||
sock->dev = dev;
|
||||
sock->mbinfo.base_addr = SMN_HSMP_BASE;
|
||||
|
||||
/*
|
||||
* This is a transitional change from non-ACPI to ACPI, only
|
||||
* family 0x1A, model 0x00 platform is supported for both ACPI and non-ACPI.
|
||||
*/
|
||||
if (is_f1a_m0h())
|
||||
sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID_F1A_M0H;
|
||||
else
|
||||
sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID;
|
||||
|
||||
sock->mbinfo.msg_resp_off = SMN_HSMP_MSG_RESP;
|
||||
sock->mbinfo.msg_arg_off = SMN_HSMP_MSG_DATA;
|
||||
sema_init(&sock->hsmp_sem, 1);
|
||||
|
||||
/* Test the hsmp interface on each socket */
|
||||
ret = hsmp_test(i, 0xDEADBEEF);
|
||||
if (ret) {
|
||||
dev_err(dev, "HSMP test message failed on Fam:%x model:%x\n",
|
||||
boot_cpu_data.x86, boot_cpu_data.x86_model);
|
||||
dev_err(dev, "Is HSMP disabled in BIOS ?\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
plat_dev.hsmp_device.name = HSMP_CDEV_NAME;
|
||||
plat_dev.hsmp_device.minor = MISC_DYNAMIC_MINOR;
|
||||
plat_dev.hsmp_device.fops = &hsmp_fops;
|
||||
plat_dev.hsmp_device.parent = &pdev->dev;
|
||||
plat_dev.hsmp_device.nodename = HSMP_DEVNODE_NAME;
|
||||
plat_dev.hsmp_device.mode = 0644;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = hsmp_cache_proto_ver();
|
||||
static const struct acpi_device_id amd_hsmp_acpi_ids[] = {
|
||||
{ACPI_HSMP_DEVICE_HID, 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, amd_hsmp_acpi_ids);
|
||||
|
||||
static int hsmp_pltdrv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *adev;
|
||||
u16 sock_ind = 0;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* On ACPI supported BIOS, there is an ACPI HSMP device added for
|
||||
* each socket, so the per socket probing, but the memory allocated for
|
||||
* sockets should be contiguous to access it as an array,
|
||||
* Hence allocate memory for all the sockets at once instead of allocating
|
||||
* on each probe.
|
||||
*/
|
||||
if (!plat_dev.is_probed) {
|
||||
plat_dev.sock = devm_kcalloc(&pdev->dev, plat_dev.num_sockets,
|
||||
sizeof(*plat_dev.sock),
|
||||
GFP_KERNEL);
|
||||
if (!plat_dev.sock)
|
||||
return -ENOMEM;
|
||||
}
|
||||
adev = ACPI_COMPANION(&pdev->dev);
|
||||
if (adev && !acpi_match_device_ids(adev, amd_hsmp_acpi_ids)) {
|
||||
ret = hsmp_get_uid(&pdev->dev, &sock_ind);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (sock_ind >= plat_dev.num_sockets)
|
||||
return -EINVAL;
|
||||
ret = hsmp_parse_acpi_table(&pdev->dev, sock_ind);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to parse ACPI table\n");
|
||||
return ret;
|
||||
}
|
||||
/* Test the hsmp interface */
|
||||
ret = hsmp_test(sock_ind, 0xDEADBEEF);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "HSMP test message failed on Fam:%x model:%x\n",
|
||||
boot_cpu_data.x86, boot_cpu_data.x86_model);
|
||||
dev_err(&pdev->dev, "Is HSMP disabled in BIOS ?\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = init_platform_device(&pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to init HSMP mailbox\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = hsmp_cache_proto_ver(sock_ind);
|
||||
if (ret) {
|
||||
dev_err(plat_dev.dev, "Failed to read HSMP protocol version\n");
|
||||
dev_err(&pdev->dev, "Failed to read HSMP protocol version\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hsmp_create_sysfs_interface();
|
||||
if (plat_dev.is_acpi_device)
|
||||
ret = hsmp_create_acpi_sysfs_if(&pdev->dev);
|
||||
else
|
||||
ret = hsmp_create_non_acpi_sysfs_if(&pdev->dev);
|
||||
if (ret)
|
||||
dev_err(plat_dev.dev, "Failed to create HSMP sysfs interface\n");
|
||||
dev_err(&pdev->dev, "Failed to create HSMP sysfs interface\n");
|
||||
|
||||
if (!plat_dev.is_probed) {
|
||||
plat_dev.hsmp_device.name = HSMP_CDEV_NAME;
|
||||
plat_dev.hsmp_device.minor = MISC_DYNAMIC_MINOR;
|
||||
plat_dev.hsmp_device.fops = &hsmp_fops;
|
||||
plat_dev.hsmp_device.parent = &pdev->dev;
|
||||
plat_dev.hsmp_device.nodename = HSMP_DEVNODE_NAME;
|
||||
plat_dev.hsmp_device.mode = 0644;
|
||||
|
||||
ret = misc_register(&plat_dev.hsmp_device);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
plat_dev.is_probed = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
return misc_register(&plat_dev.hsmp_device);
|
||||
}
|
||||
|
||||
static void hsmp_pltdrv_remove(struct platform_device *pdev)
|
||||
{
|
||||
misc_deregister(&plat_dev.hsmp_device);
|
||||
/*
|
||||
* We register only one misc_device even on multi socket system.
|
||||
* So, deregister should happen only once.
|
||||
*/
|
||||
if (plat_dev.is_probed) {
|
||||
misc_deregister(&plat_dev.hsmp_device);
|
||||
plat_dev.is_probed = false;
|
||||
}
|
||||
}
|
||||
|
||||
static struct platform_driver amd_hsmp_driver = {
|
||||
@ -536,15 +886,30 @@ static struct platform_driver amd_hsmp_driver = {
|
||||
.remove_new = hsmp_pltdrv_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.acpi_match_table = amd_hsmp_acpi_ids,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device *amd_hsmp_platdev;
|
||||
|
||||
static int hsmp_plat_dev_register(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE);
|
||||
if (!amd_hsmp_platdev)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = platform_device_add(amd_hsmp_platdev);
|
||||
if (ret)
|
||||
platform_device_put(amd_hsmp_platdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init hsmp_plt_init(void)
|
||||
{
|
||||
int ret = -ENODEV;
|
||||
int i;
|
||||
|
||||
if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD || boot_cpu_data.x86 < 0x19) {
|
||||
pr_err("HSMP is not supported on Family:%x model:%x\n",
|
||||
@ -557,40 +922,19 @@ static int __init hsmp_plt_init(void)
|
||||
* if we have N SMN/DF interfaces that ideally means N sockets
|
||||
*/
|
||||
plat_dev.num_sockets = amd_nb_num();
|
||||
if (plat_dev.num_sockets == 0)
|
||||
if (plat_dev.num_sockets == 0 || plat_dev.num_sockets > MAX_AMD_SOCKETS)
|
||||
return ret;
|
||||
|
||||
/* Test the hsmp interface on each socket */
|
||||
for (i = 0; i < plat_dev.num_sockets; i++) {
|
||||
ret = hsmp_test(i, 0xDEADBEEF);
|
||||
if (ret) {
|
||||
pr_err("HSMP test message failed on Fam:%x model:%x\n",
|
||||
boot_cpu_data.x86, boot_cpu_data.x86_model);
|
||||
pr_err("Is HSMP disabled in BIOS ?\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = platform_driver_register(&amd_hsmp_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE);
|
||||
if (!amd_hsmp_platdev) {
|
||||
ret = -ENOMEM;
|
||||
goto drv_unregister;
|
||||
if (!plat_dev.is_acpi_device) {
|
||||
ret = hsmp_plat_dev_register();
|
||||
if (ret)
|
||||
platform_driver_unregister(&amd_hsmp_driver);
|
||||
}
|
||||
|
||||
ret = platform_device_add(amd_hsmp_platdev);
|
||||
if (ret) {
|
||||
platform_device_put(amd_hsmp_platdev);
|
||||
goto drv_unregister;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
drv_unregister:
|
||||
platform_driver_unregister(&amd_hsmp_driver);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -90,12 +90,96 @@ out:
|
||||
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, ¶ms);
|
||||
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,
|
||||
¤t_power_limits_fops);
|
||||
if (dev->pmf_if_version == PMF_IF_V1)
|
||||
debugfs_create_file("current_power_limits", 0644, dev->dbgfs_dir, dev,
|
||||
¤t_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;
|
||||
|
||||
if (cookie != POLICY_SIGN_COOKIE || !length) {
|
||||
header = (struct cookie_header *)(dev->policy_buf + POLICY_COOKIE_OFFSET);
|
||||
|
||||
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;
|
||||
int code = asus_wmi_get_event_code(value);
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
if (code < 0) {
|
||||
pr_warn("Failed to get notify code: %d\n", code);
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
params.activate = &activate;
|
||||
atomic_set(&scan_cpus_in, 0);
|
||||
stop_core_cpuslocked(cpu, doscan, ¶ms);
|
||||
|
||||
status.data = msrvals[1];
|
||||
|
||||
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->map = &lnl_socm_reg_map;
|
||||
ret = get_primary_reg_base(pmc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pmc_core_get_low_power_modes(pmcdev);
|
||||
|
||||
if (ssram_init) {
|
||||
ret = pmc_core_ssram_get_lpm_reqs(pmcdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
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 */
|
||||
|
@ -69,6 +69,7 @@
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/units.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <acpi/battery.h>
|
||||
@ -166,6 +167,7 @@ enum tpacpi_hkey_event_t {
|
||||
TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */
|
||||
TP_HKEY_EV_PRIVACYGUARD_TOGGLE = 0x130f, /* Toggle priv.guard on/off */
|
||||
TP_HKEY_EV_AMT_TOGGLE = 0x131a, /* Toggle AMT on/off */
|
||||
TP_HKEY_EV_PROFILE_TOGGLE = 0x131f, /* Toggle platform profile */
|
||||
|
||||
/* Reasons for waking up from S3/S4 */
|
||||
TP_HKEY_EV_WKUP_S3_UNDOCK = 0x2304, /* undock requested, S3 */
|
||||
@ -3731,6 +3733,7 @@ static bool hotkey_notify_extended_hotkey(const u32 hkey)
|
||||
switch (hkey) {
|
||||
case TP_HKEY_EV_PRIVACYGUARD_TOGGLE:
|
||||
case TP_HKEY_EV_AMT_TOGGLE:
|
||||
case TP_HKEY_EV_PROFILE_TOGGLE:
|
||||
tpacpi_driver_event(hkey);
|
||||
return true;
|
||||
}
|
||||
@ -6126,12 +6129,15 @@ enum thermal_access_mode {
|
||||
TPACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */
|
||||
TPACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */
|
||||
TPACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */
|
||||
TPACPI_THERMAL_TPEC_12, /* Use ACPI EC regs, 12 sensors */
|
||||
TPACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */
|
||||
};
|
||||
|
||||
enum { /* TPACPI_THERMAL_TPEC_* */
|
||||
TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */
|
||||
TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */
|
||||
TP_EC_THERMAL_TMP0_NS = 0xA8, /* ACPI EC Non-Standard regs TMP 0..7 */
|
||||
TP_EC_THERMAL_TMP8_NS = 0xB8, /* ACPI EC Non-standard regs TMP 8..11 */
|
||||
TP_EC_FUNCREV = 0xEF, /* ACPI EC Functional revision */
|
||||
TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */
|
||||
|
||||
@ -6144,8 +6150,104 @@ struct ibm_thermal_sensors_struct {
|
||||
s32 temp[TPACPI_MAX_THERMAL_SENSORS];
|
||||
};
|
||||
|
||||
static const struct tpacpi_quirk thermal_quirk_table[] __initconst = {
|
||||
/* Non-standard address for thermal registers on some ThinkPads */
|
||||
TPACPI_Q_LNV3('R', '1', 'F', true), /* L13 Yoga Gen 2 */
|
||||
TPACPI_Q_LNV3('N', '2', 'U', true), /* X13 Yoga Gen 2*/
|
||||
TPACPI_Q_LNV3('R', '0', 'R', true), /* L380 */
|
||||
TPACPI_Q_LNV3('R', '1', '5', true), /* L13 Yoga Gen 1*/
|
||||
TPACPI_Q_LNV3('R', '1', '0', true), /* L390 */
|
||||
TPACPI_Q_LNV3('N', '2', 'L', true), /* X13 Yoga Gen 1*/
|
||||
TPACPI_Q_LNV3('R', '0', 'T', true), /* 11e Gen5 GL*/
|
||||
TPACPI_Q_LNV3('R', '1', 'D', true), /* 11e Gen5 GL-R*/
|
||||
TPACPI_Q_LNV3('R', '0', 'V', true), /* 11e Gen5 KL-Y*/
|
||||
};
|
||||
|
||||
static enum thermal_access_mode thermal_read_mode;
|
||||
static bool thermal_use_labels;
|
||||
static bool thermal_with_ns_address; /* Non-standard thermal reg address */
|
||||
|
||||
/* Function to check thermal read mode */
|
||||
static enum thermal_access_mode __init thermal_read_mode_check(void)
|
||||
{
|
||||
u8 t, ta1, ta2, ver = 0;
|
||||
int i;
|
||||
int acpi_tmp7;
|
||||
|
||||
acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
|
||||
|
||||
if (thinkpad_id.ec_model) {
|
||||
/*
|
||||
* Direct EC access mode: sensors at registers 0x78-0x7F,
|
||||
* 0xC0-0xC7. Registers return 0x00 for non-implemented,
|
||||
* thermal sensors return 0x80 when not available.
|
||||
*
|
||||
* In some special cases (when Power Supply ID is 0xC2)
|
||||
* above rule causes thermal control issues. Offset 0xEF
|
||||
* determines EC version. 0xC0-0xC7 are not thermal registers
|
||||
* in Ver 3.
|
||||
*/
|
||||
if (!acpi_ec_read(TP_EC_FUNCREV, &ver))
|
||||
pr_warn("Thinkpad ACPI EC unable to access EC version\n");
|
||||
|
||||
/* Quirks to check non-standard EC */
|
||||
thermal_with_ns_address = tpacpi_check_quirks(thermal_quirk_table,
|
||||
ARRAY_SIZE(thermal_quirk_table));
|
||||
|
||||
/* Support for Thinkpads with non-standard address */
|
||||
if (thermal_with_ns_address) {
|
||||
pr_info("ECFW with non-standard thermal registers found\n");
|
||||
return TPACPI_THERMAL_TPEC_12;
|
||||
}
|
||||
|
||||
ta1 = ta2 = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) {
|
||||
ta1 |= t;
|
||||
} else {
|
||||
ta1 = 0;
|
||||
break;
|
||||
}
|
||||
if (ver < 3) {
|
||||
if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
|
||||
ta2 |= t;
|
||||
} else {
|
||||
ta1 = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ta1 == 0) {
|
||||
/* This is sheer paranoia, but we handle it anyway */
|
||||
if (acpi_tmp7) {
|
||||
pr_err("ThinkPad ACPI EC access misbehaving, falling back to ACPI TMPx access mode\n");
|
||||
return TPACPI_THERMAL_ACPI_TMP07;
|
||||
}
|
||||
pr_err("ThinkPad ACPI EC access misbehaving, disabling thermal sensors access\n");
|
||||
return TPACPI_THERMAL_NONE;
|
||||
}
|
||||
|
||||
if (ver >= 3) {
|
||||
thermal_use_labels = true;
|
||||
return TPACPI_THERMAL_TPEC_8;
|
||||
}
|
||||
|
||||
return (ta2 != 0) ? TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
|
||||
}
|
||||
|
||||
if (acpi_tmp7) {
|
||||
if (tpacpi_is_ibm() && acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
|
||||
/* 600e/x, 770e, 770x */
|
||||
return TPACPI_THERMAL_ACPI_UPDT;
|
||||
}
|
||||
/* IBM/LENOVO DSDT EC.TMPx access, max 8 sensors */
|
||||
return TPACPI_THERMAL_ACPI_TMP07;
|
||||
}
|
||||
|
||||
/* temperatures not supported on 570, G4x, R30, R31, R32 */
|
||||
return TPACPI_THERMAL_NONE;
|
||||
}
|
||||
|
||||
/* idx is zero-based */
|
||||
static int thermal_get_sensor(int idx, s32 *value)
|
||||
@ -6174,6 +6276,20 @@ static int thermal_get_sensor(int idx, s32 *value)
|
||||
}
|
||||
break;
|
||||
|
||||
/* The Non-standard EC uses 12 Thermal areas */
|
||||
case TPACPI_THERMAL_TPEC_12:
|
||||
if (idx >= 12)
|
||||
return -EINVAL;
|
||||
|
||||
t = idx < 8 ? TP_EC_THERMAL_TMP0_NS + idx :
|
||||
TP_EC_THERMAL_TMP8_NS + (idx - 8);
|
||||
|
||||
if (!acpi_ec_read(t, &tmp))
|
||||
return -EIO;
|
||||
|
||||
*value = tmp * MILLIDEGREE_PER_DEGREE;
|
||||
return 0;
|
||||
|
||||
case TPACPI_THERMAL_ACPI_UPDT:
|
||||
if (idx <= 7) {
|
||||
snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
|
||||
@ -6208,17 +6324,17 @@ static int thermal_get_sensor(int idx, s32 *value)
|
||||
|
||||
static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
|
||||
{
|
||||
int res, i;
|
||||
int n;
|
||||
|
||||
n = 8;
|
||||
i = 0;
|
||||
int res, i, n;
|
||||
|
||||
if (!s)
|
||||
return -EINVAL;
|
||||
|
||||
if (thermal_read_mode == TPACPI_THERMAL_TPEC_16)
|
||||
n = 16;
|
||||
else if (thermal_read_mode == TPACPI_THERMAL_TPEC_12)
|
||||
n = 12;
|
||||
else
|
||||
n = 8;
|
||||
|
||||
for (i = 0 ; i < n; i++) {
|
||||
res = thermal_get_sensor(i, &s->temp[i]);
|
||||
@ -6317,18 +6433,36 @@ static struct attribute *thermal_temp_input_attr[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
|
||||
|
||||
static umode_t thermal_attr_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
if (thermal_read_mode == TPACPI_THERMAL_NONE)
|
||||
struct device_attribute *dev_attr = to_dev_attr(attr);
|
||||
struct sensor_device_attribute *sensor_attr =
|
||||
to_sensor_dev_attr(dev_attr);
|
||||
|
||||
int idx = sensor_attr->index;
|
||||
|
||||
switch (thermal_read_mode) {
|
||||
case TPACPI_THERMAL_NONE:
|
||||
return 0;
|
||||
|
||||
if (attr == THERMAL_ATTRS(8) || attr == THERMAL_ATTRS(9) ||
|
||||
attr == THERMAL_ATTRS(10) || attr == THERMAL_ATTRS(11) ||
|
||||
attr == THERMAL_ATTRS(12) || attr == THERMAL_ATTRS(13) ||
|
||||
attr == THERMAL_ATTRS(14) || attr == THERMAL_ATTRS(15)) {
|
||||
if (thermal_read_mode != TPACPI_THERMAL_TPEC_16)
|
||||
case TPACPI_THERMAL_ACPI_TMP07:
|
||||
case TPACPI_THERMAL_ACPI_UPDT:
|
||||
case TPACPI_THERMAL_TPEC_8:
|
||||
if (idx >= 8)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case TPACPI_THERMAL_TPEC_12:
|
||||
if (idx >= 12)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return attr->mode;
|
||||
@ -6375,78 +6509,9 @@ static const struct attribute_group temp_label_attr_group = {
|
||||
|
||||
static int __init thermal_init(struct ibm_init_struct *iibm)
|
||||
{
|
||||
u8 t, ta1, ta2, ver = 0;
|
||||
int i;
|
||||
int acpi_tmp7;
|
||||
|
||||
vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n");
|
||||
|
||||
acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
|
||||
|
||||
if (thinkpad_id.ec_model) {
|
||||
/*
|
||||
* Direct EC access mode: sensors at registers
|
||||
* 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for
|
||||
* non-implemented, thermal sensors return 0x80 when
|
||||
* not available
|
||||
* The above rule is unfortunately flawed. This has been seen with
|
||||
* 0xC2 (power supply ID) causing thermal control problems.
|
||||
* The EC version can be determined by offset 0xEF and at least for
|
||||
* version 3 the Lenovo firmware team confirmed that registers 0xC0-0xC7
|
||||
* are not thermal registers.
|
||||
*/
|
||||
if (!acpi_ec_read(TP_EC_FUNCREV, &ver))
|
||||
pr_warn("Thinkpad ACPI EC unable to access EC version\n");
|
||||
|
||||
ta1 = ta2 = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) {
|
||||
ta1 |= t;
|
||||
} else {
|
||||
ta1 = 0;
|
||||
break;
|
||||
}
|
||||
if (ver < 3) {
|
||||
if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
|
||||
ta2 |= t;
|
||||
} else {
|
||||
ta1 = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ta1 == 0) {
|
||||
/* This is sheer paranoia, but we handle it anyway */
|
||||
if (acpi_tmp7) {
|
||||
pr_err("ThinkPad ACPI EC access misbehaving, falling back to ACPI TMPx access mode\n");
|
||||
thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
|
||||
} else {
|
||||
pr_err("ThinkPad ACPI EC access misbehaving, disabling thermal sensors access\n");
|
||||
thermal_read_mode = TPACPI_THERMAL_NONE;
|
||||
}
|
||||
} else {
|
||||
if (ver >= 3) {
|
||||
thermal_read_mode = TPACPI_THERMAL_TPEC_8;
|
||||
thermal_use_labels = true;
|
||||
} else {
|
||||
thermal_read_mode =
|
||||
(ta2 != 0) ?
|
||||
TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
|
||||
}
|
||||
}
|
||||
} else if (acpi_tmp7) {
|
||||
if (tpacpi_is_ibm() &&
|
||||
acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
|
||||
/* 600e/x, 770e, 770x */
|
||||
thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT;
|
||||
} else {
|
||||
/* IBM/LENOVO DSDT EC.TMPx access, max 8 sensors */
|
||||
thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
|
||||
}
|
||||
} else {
|
||||
/* temperatures not supported on 570, G4x, R30, R31, R32 */
|
||||
thermal_read_mode = TPACPI_THERMAL_NONE;
|
||||
}
|
||||
thermal_read_mode = thermal_read_mode_check();
|
||||
|
||||
vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n",
|
||||
str_supported(thermal_read_mode != TPACPI_THERMAL_NONE),
|
||||
@ -8767,6 +8832,13 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
|
||||
TPACPI_Q_LNV3('N', '3', '7', TPACPI_FAN_2CTL), /* T15g (2nd gen) */
|
||||
TPACPI_Q_LNV3('R', '1', 'F', TPACPI_FAN_NS), /* L13 Yoga Gen 2 */
|
||||
TPACPI_Q_LNV3('N', '2', 'U', TPACPI_FAN_NS), /* X13 Yoga Gen 2*/
|
||||
TPACPI_Q_LNV3('R', '0', 'R', TPACPI_FAN_NS), /* L380 */
|
||||
TPACPI_Q_LNV3('R', '1', '5', TPACPI_FAN_NS), /* L13 Yoga Gen 1 */
|
||||
TPACPI_Q_LNV3('R', '1', '0', TPACPI_FAN_NS), /* L390 */
|
||||
TPACPI_Q_LNV3('N', '2', 'L', TPACPI_FAN_NS), /* X13 Yoga Gen 1 */
|
||||
TPACPI_Q_LNV3('R', '0', 'T', TPACPI_FAN_NS), /* 11e Gen5 GL */
|
||||
TPACPI_Q_LNV3('R', '1', 'D', TPACPI_FAN_NS), /* 11e Gen5 GL-R */
|
||||
TPACPI_Q_LNV3('R', '0', 'V', TPACPI_FAN_NS), /* 11e Gen5 KL-Y */
|
||||
TPACPI_Q_LNV3('N', '1', 'O', TPACPI_FAN_NOFAN), /* X1 Tablet (2nd gen) */
|
||||
};
|
||||
|
||||
@ -9285,7 +9357,6 @@ static int mute_led_init(struct ibm_init_struct *iibm)
|
||||
continue;
|
||||
}
|
||||
|
||||
mute_led_cdev[i].brightness = ledtrig_audio_get(i);
|
||||
err = led_classdev_register(&tpacpi_pdev->dev, &mute_led_cdev[i]);
|
||||
if (err < 0) {
|
||||
while (i--)
|
||||
@ -11119,7 +11190,23 @@ static void tpacpi_driver_event(const unsigned int hkey_event)
|
||||
else
|
||||
dytc_control_amt(!dytc_amt_active);
|
||||
}
|
||||
|
||||
if (hkey_event == TP_HKEY_EV_PROFILE_TOGGLE) {
|
||||
switch (dytc_current_profile) {
|
||||
case PLATFORM_PROFILE_LOW_POWER:
|
||||
dytc_profile_set(NULL, PLATFORM_PROFILE_BALANCED);
|
||||
break;
|
||||
case PLATFORM_PROFILE_BALANCED:
|
||||
dytc_profile_set(NULL, PLATFORM_PROFILE_PERFORMANCE);
|
||||
break;
|
||||
case PLATFORM_PROFILE_PERFORMANCE:
|
||||
dytc_profile_set(NULL, PLATFORM_PROFILE_LOW_POWER);
|
||||
break;
|
||||
default:
|
||||
pr_warn("Profile HKEY unexpected profile %d", dytc_current_profile);
|
||||
}
|
||||
/* Notify user space the profile changed */
|
||||
platform_profile_notify();
|
||||
}
|
||||
}
|
||||
|
||||
static void hotkey_driver_event(const unsigned int scancode)
|
||||
|
@ -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);
|
||||
|
@ -57,6 +57,8 @@ static_assert(__alignof__(struct guid_block) == 1);
|
||||
|
||||
enum { /* wmi_block flags */
|
||||
WMI_READ_TAKES_NO_ARGS,
|
||||
WMI_GUID_DUPLICATED,
|
||||
WMI_NO_EVENT_DATA,
|
||||
};
|
||||
|
||||
struct wmi_block {
|
||||
@ -88,16 +90,6 @@ static const struct acpi_device_id wmi_device_ids[] = {
|
||||
};
|
||||
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 */
|
||||
"44FADEB1-B204-40F2-8581-394BBDC1B651", /* intel-wmi-sbl-fw-update */
|
||||
"86CCFD48-205E-4A77-9C48-2021CBEDE341", /* intel-wmi-thunderbolt */
|
||||
"F1DDEE52-063C-4784-A11E-8A06684B9B01", /* dell-smm-hwmon */
|
||||
NULL
|
||||
};
|
||||
|
||||
#define dev_to_wblock(__dev) container_of_const(__dev, struct wmi_block, dev.dev)
|
||||
#define dev_to_wdev(__dev) container_of_const(__dev, struct wmi_device, dev)
|
||||
|
||||
@ -132,26 +124,6 @@ static const void *find_guid_context(struct wmi_block *wblock,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int get_subobj_info(acpi_handle handle, const char *pathname,
|
||||
struct acpi_device_info **info)
|
||||
{
|
||||
acpi_handle subobj_handle;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_get_handle(handle, pathname, &subobj_handle);
|
||||
if (status == AE_NOT_FOUND)
|
||||
return -ENOENT;
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
status = acpi_get_object_info(subobj_handle, info);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static acpi_status wmi_method_enable(struct wmi_block *wblock, bool enable)
|
||||
{
|
||||
struct guid_block *block;
|
||||
@ -215,6 +187,12 @@ static int wmidev_match_guid(struct device *dev, const void *data)
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
const guid_t *guid = data;
|
||||
|
||||
/* Legacy GUID-based functions are restricted to only see
|
||||
* a single WMI device for each GUID.
|
||||
*/
|
||||
if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags))
|
||||
return 0;
|
||||
|
||||
if (guid_equal(guid, &wblock->gblock.guid))
|
||||
return 1;
|
||||
|
||||
@ -226,13 +204,19 @@ static int wmidev_match_notify_id(struct device *dev, const void *data)
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
const u32 *notify_id = data;
|
||||
|
||||
/* Legacy GUID-based functions are restricted to only see
|
||||
* a single WMI device for each GUID.
|
||||
*/
|
||||
if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags))
|
||||
return 0;
|
||||
|
||||
if (wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *notify_id)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bus_type wmi_bus_type;
|
||||
static const struct bus_type wmi_bus_type;
|
||||
|
||||
static struct wmi_device *wmi_find_device_by_guid(const char *guid_string)
|
||||
{
|
||||
@ -316,7 +300,7 @@ EXPORT_SYMBOL_GPL(wmidev_instance_count);
|
||||
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
|
||||
* @instance: Instance index
|
||||
* @method_id: Method ID to call
|
||||
* @in: Buffer containing input for the method call
|
||||
* @in: Mandatory buffer containing input for the method call
|
||||
* @out: Empty buffer to return the method results
|
||||
*
|
||||
* Call an ACPI-WMI method, the caller must free @out.
|
||||
@ -346,7 +330,7 @@ EXPORT_SYMBOL_GPL(wmi_evaluate_method);
|
||||
* @wdev: A wmi bus device from a driver
|
||||
* @instance: Instance index
|
||||
* @method_id: Method ID to call
|
||||
* @in: Buffer containing input for the method call
|
||||
* @in: Mandatory buffer containing input for the method call
|
||||
* @out: Empty buffer to return the method results
|
||||
*
|
||||
* Call an ACPI-WMI method, the caller must free @out.
|
||||
@ -367,26 +351,25 @@ acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 met
|
||||
block = &wblock->gblock;
|
||||
handle = wblock->acpi_device->handle;
|
||||
|
||||
if (!in)
|
||||
return AE_BAD_DATA;
|
||||
|
||||
if (!(block->flags & ACPI_WMI_METHOD))
|
||||
return AE_BAD_DATA;
|
||||
|
||||
if (block->instance_count <= instance)
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
input.count = 2;
|
||||
input.count = 3;
|
||||
input.pointer = params;
|
||||
|
||||
params[0].type = ACPI_TYPE_INTEGER;
|
||||
params[0].integer.value = instance;
|
||||
params[1].type = ACPI_TYPE_INTEGER;
|
||||
params[1].integer.value = method_id;
|
||||
|
||||
if (in) {
|
||||
input.count = 3;
|
||||
|
||||
params[2].type = get_param_acpi_type(wblock);
|
||||
params[2].buffer.length = in->length;
|
||||
params[2].buffer.pointer = in->pointer;
|
||||
}
|
||||
params[2].type = get_param_acpi_type(wblock);
|
||||
params[2].buffer.length = in->length;
|
||||
params[2].buffer.pointer = in->pointer;
|
||||
|
||||
get_acpi_method_name(wblock, 'M', method);
|
||||
|
||||
@ -890,6 +873,23 @@ static int wmi_dev_probe(struct device *dev)
|
||||
struct wmi_driver *wdriver = drv_to_wdrv(dev->driver);
|
||||
int ret = 0;
|
||||
|
||||
/* Some older WMI drivers will break if instantiated multiple times,
|
||||
* so they are blocked from probing WMI devices with a duplicated GUID.
|
||||
*
|
||||
* New WMI drivers should support being instantiated multiple times.
|
||||
*/
|
||||
if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags) && !wdriver->no_singleton) {
|
||||
dev_warn(dev, "Legacy driver %s cannot be instantiated multiple times\n",
|
||||
dev->driver->name);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (wdriver->notify) {
|
||||
if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && !wdriver->no_notify_data)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (ACPI_FAILURE(wmi_method_enable(wblock, true)))
|
||||
dev_warn(dev, "failed to enable device -- probing anyway\n");
|
||||
|
||||
@ -931,7 +931,7 @@ static struct class wmi_bus_class = {
|
||||
.name = "wmi_bus",
|
||||
};
|
||||
|
||||
static struct bus_type wmi_bus_type = {
|
||||
static const struct bus_type wmi_bus_type = {
|
||||
.name = "wmi",
|
||||
.dev_groups = wmi_groups,
|
||||
.match = wmi_dev_match,
|
||||
@ -979,9 +979,10 @@ static int wmi_create_device(struct device *wmi_bus_dev,
|
||||
struct wmi_block *wblock,
|
||||
struct acpi_device *device)
|
||||
{
|
||||
struct acpi_device_info *info;
|
||||
char method[WMI_ACPI_METHOD_NAME_SIZE];
|
||||
int result;
|
||||
struct acpi_device_info *info;
|
||||
acpi_handle method_handle;
|
||||
acpi_status status;
|
||||
uint count;
|
||||
|
||||
if (wblock->gblock.flags & ACPI_WMI_EVENT) {
|
||||
@ -990,6 +991,15 @@ static int wmi_create_device(struct device *wmi_bus_dev,
|
||||
}
|
||||
|
||||
if (wblock->gblock.flags & ACPI_WMI_METHOD) {
|
||||
get_acpi_method_name(wblock, 'M', method);
|
||||
if (!acpi_has_method(device->handle, method)) {
|
||||
dev_warn(wmi_bus_dev,
|
||||
FW_BUG "%s method block execution control method not found\n",
|
||||
method);
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
wblock->dev.dev.type = &wmi_type_method;
|
||||
goto out_init;
|
||||
}
|
||||
@ -1000,15 +1010,19 @@ static int wmi_create_device(struct device *wmi_bus_dev,
|
||||
* we ignore this data block.
|
||||
*/
|
||||
get_acpi_method_name(wblock, 'Q', method);
|
||||
result = get_subobj_info(device->handle, method, &info);
|
||||
|
||||
if (result) {
|
||||
status = acpi_get_handle(device->handle, method, &method_handle);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_warn(wmi_bus_dev,
|
||||
"%s data block query control method not found\n",
|
||||
FW_BUG "%s data block query control method not found\n",
|
||||
method);
|
||||
return result;
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
status = acpi_get_object_info(method_handle, &info);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
wblock->dev.dev.type = &wmi_type_data;
|
||||
|
||||
/*
|
||||
@ -1037,10 +1051,12 @@ static int wmi_create_device(struct device *wmi_bus_dev,
|
||||
wblock->dev.dev.parent = wmi_bus_dev;
|
||||
|
||||
count = guid_count(&wblock->gblock.guid);
|
||||
if (count)
|
||||
if (count) {
|
||||
dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, count);
|
||||
else
|
||||
set_bit(WMI_GUID_DUPLICATED, &wblock->flags);
|
||||
} else {
|
||||
dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid);
|
||||
}
|
||||
|
||||
device_initialize(&wblock->dev.dev);
|
||||
|
||||
@ -1067,32 +1083,6 @@ static int wmi_add_device(struct platform_device *pdev, struct wmi_device *wdev)
|
||||
return device_add(&wdev->dev);
|
||||
}
|
||||
|
||||
static bool guid_already_parsed_for_legacy(struct acpi_device *device, const guid_t *guid)
|
||||
{
|
||||
struct wmi_block *wblock;
|
||||
|
||||
list_for_each_entry(wblock, &wmi_block_list, list) {
|
||||
/* skip warning and register if we know the driver will use struct wmi_driver */
|
||||
for (int i = 0; allow_duplicates[i] != NULL; i++) {
|
||||
if (guid_parse_and_compare(allow_duplicates[i], guid))
|
||||
return false;
|
||||
}
|
||||
if (guid_equal(&wblock->gblock.guid, guid)) {
|
||||
/*
|
||||
* Because we historically didn't track the relationship
|
||||
* between GUIDs and ACPI nodes, we don't know whether
|
||||
* we need to suppress GUIDs that are unique on a
|
||||
* given node but duplicated across nodes.
|
||||
*/
|
||||
dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n",
|
||||
guid, dev_name(&wblock->acpi_device->dev));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the _WDG method for the GUID data blocks
|
||||
*/
|
||||
@ -1101,6 +1091,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev)
|
||||
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
|
||||
struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
const struct guid_block *gblock;
|
||||
bool event_data_available;
|
||||
struct wmi_block *wblock;
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
@ -1120,6 +1111,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
event_data_available = acpi_has_method(device->handle, "_WED");
|
||||
gblock = (const struct guid_block *)obj->buffer.pointer;
|
||||
total = obj->buffer.length / sizeof(struct guid_block);
|
||||
|
||||
@ -1129,17 +1121,14 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (guid_already_parsed_for_legacy(device, &gblock[i].guid))
|
||||
continue;
|
||||
|
||||
wblock = kzalloc(sizeof(*wblock), GFP_KERNEL);
|
||||
if (!wblock) {
|
||||
dev_err(wmi_bus_dev, "Failed to allocate %pUL\n", &gblock[i].guid);
|
||||
if (!wblock)
|
||||
continue;
|
||||
}
|
||||
|
||||
wblock->acpi_device = device;
|
||||
wblock->gblock = gblock[i];
|
||||
if (gblock[i].flags & ACPI_WMI_EVENT && !event_data_available)
|
||||
set_bit(WMI_NO_EVENT_DATA, &wblock->flags);
|
||||
|
||||
retval = wmi_create_device(wmi_bus_dev, wblock, device);
|
||||
if (retval) {
|
||||
@ -1205,30 +1194,46 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
|
||||
}
|
||||
}
|
||||
|
||||
static void wmi_notify_driver(struct wmi_block *wblock)
|
||||
static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj)
|
||||
{
|
||||
struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver);
|
||||
struct acpi_buffer data = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
acpi_status status;
|
||||
|
||||
if (!driver->no_notify_data) {
|
||||
status = get_event_data(wblock, &data);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_warn(&wblock->dev.dev, "Failed to get event data\n");
|
||||
return;
|
||||
}
|
||||
if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags)) {
|
||||
*obj = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
status = get_event_data(wblock, &data);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_warn(&wblock->dev.dev, "Failed to get event data\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*obj = data.pointer;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj)
|
||||
{
|
||||
struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver);
|
||||
|
||||
if (!obj && !driver->no_notify_data) {
|
||||
dev_warn(&wblock->dev.dev, "Event contains no event data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (driver->notify)
|
||||
driver->notify(&wblock->dev, data.pointer);
|
||||
|
||||
kfree(data.pointer);
|
||||
driver->notify(&wblock->dev, obj);
|
||||
}
|
||||
|
||||
static int wmi_notify_device(struct device *dev, void *data)
|
||||
{
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
union acpi_object *obj;
|
||||
u32 *event = data;
|
||||
int ret;
|
||||
|
||||
if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event))
|
||||
return 0;
|
||||
@ -1238,15 +1243,36 @@ static int wmi_notify_device(struct device *dev, void *data)
|
||||
* Because of this the WMI driver notify handler takes precedence.
|
||||
*/
|
||||
if (wblock->dev.dev.driver && wblock->driver_ready) {
|
||||
wmi_notify_driver(wblock);
|
||||
ret = wmi_get_notify_data(wblock, &obj);
|
||||
if (ret >= 0) {
|
||||
wmi_notify_driver(wblock, obj);
|
||||
kfree(obj);
|
||||
}
|
||||
} else {
|
||||
if (wblock->handler)
|
||||
if (wblock->handler) {
|
||||
wblock->handler(*event, wblock->handler_data);
|
||||
} else {
|
||||
/* The ACPI WMI specification says that _WED should be
|
||||
* evaluated every time an notification is received, even
|
||||
* if no consumers are present.
|
||||
*
|
||||
* Some firmware implementations actually depend on this
|
||||
* by using a queue for events which will fill up if the
|
||||
* WMI driver core stops evaluating _WED due to missing
|
||||
* WMI event consumers.
|
||||
*
|
||||
* Because of this we need this seemingly useless call to
|
||||
* wmi_get_notify_data() which in turn evaluates _WED.
|
||||
*/
|
||||
ret = wmi_get_notify_data(wblock, &obj);
|
||||
if (ret >= 0)
|
||||
kfree(obj);
|
||||
}
|
||||
|
||||
}
|
||||
up_read(&wblock->notify_lock);
|
||||
|
||||
acpi_bus_generate_netlink_event(wblock->acpi_device->pnp.device_class,
|
||||
dev_name(&wblock->dev.dev), *event, 0);
|
||||
acpi_bus_generate_netlink_event("wmi", acpi_dev_name(wblock->acpi_device), *event, 0);
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
@ -1347,7 +1373,7 @@ static int acpi_wmi_probe(struct platform_device *device)
|
||||
|
||||
error = parse_wdg(wmi_bus_dev, device);
|
||||
if (error) {
|
||||
pr_err("Failed to parse WDG method\n");
|
||||
dev_err(&device->dev, "Failed to parse _WDG method\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user