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:
Linus Torvalds 2024-03-14 10:44:09 -07:00
commit 66fd6d0bd7
58 changed files with 1843 additions and 708 deletions

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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();
}

View file

@ -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;

View file

@ -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,
};
/*

View file

@ -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,

View file

@ -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;
}

View file

@ -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);

View file

@ -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,

View file

@ -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

View file

@ -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) {

View file

@ -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.

View file

@ -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;
}

View file

@ -90,12 +90,96 @@ static int apmf_if_call_store_buffer(struct amd_pmf_dev *pdev, int fn, void *des
return err;
}
static union acpi_object *apts_if_call(struct amd_pmf_dev *pdev, u32 state_index)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_handle ahandle = ACPI_HANDLE(pdev->dev);
struct acpi_object_list apts_if_arg_list;
union acpi_object apts_if_args[3];
acpi_status status;
apts_if_arg_list.count = 3;
apts_if_arg_list.pointer = &apts_if_args[0];
apts_if_args[0].type = ACPI_TYPE_INTEGER;
apts_if_args[0].integer.value = 1;
apts_if_args[1].type = ACPI_TYPE_INTEGER;
apts_if_args[1].integer.value = state_index;
apts_if_args[2].type = ACPI_TYPE_INTEGER;
apts_if_args[2].integer.value = 0;
status = acpi_evaluate_object(ahandle, "APTS", &apts_if_arg_list, &buffer);
if (ACPI_FAILURE(status)) {
dev_err(pdev->dev, "APTS state_idx:%u call failed\n", state_index);
kfree(buffer.pointer);
return NULL;
}
return buffer.pointer;
}
static int apts_if_call_store_buffer(struct amd_pmf_dev *pdev,
u32 index, void *data, size_t out_sz)
{
union acpi_object *info;
size_t size;
int err = 0;
info = apts_if_call(pdev, index);
if (!info)
return -EIO;
if (info->type != ACPI_TYPE_BUFFER) {
dev_err(pdev->dev, "object is not a buffer\n");
err = -EINVAL;
goto out;
}
size = *(u16 *)info->buffer.pointer;
if (info->buffer.length < size) {
dev_err(pdev->dev, "buffer smaller than header size %u < %zu\n",
info->buffer.length, size);
err = -EINVAL;
goto out;
}
if (size < out_sz) {
dev_err(pdev->dev, "buffer too small %zu\n", size);
err = -EINVAL;
goto out;
}
memcpy(data, info->buffer.pointer, out_sz);
out:
kfree(info);
return err;
}
int is_apmf_func_supported(struct amd_pmf_dev *pdev, unsigned long index)
{
/* If bit-n is set, that indicates function n+1 is supported */
return !!(pdev->supported_func & BIT(index - 1));
}
int apts_get_static_slider_granular_v2(struct amd_pmf_dev *pdev,
struct amd_pmf_apts_granular_output *data, u32 apts_idx)
{
if (!is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR))
return -EINVAL;
return apts_if_call_store_buffer(pdev, apts_idx, data, sizeof(*data));
}
int apmf_get_static_slider_granular_v2(struct amd_pmf_dev *pdev,
struct apmf_static_slider_granular_output_v2 *data)
{
if (!is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR))
return -EINVAL;
return apmf_if_call_store_buffer(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR,
data, sizeof(*data));
}
int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev,
struct apmf_static_slider_granular_output *data)
{
@ -140,6 +224,43 @@ static void apmf_sbios_heartbeat_notify(struct work_struct *work)
kfree(info);
}
int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag)
{
struct sbios_hb_event_v2 args = { };
struct acpi_buffer params;
union acpi_object *info;
args.size = sizeof(args);
switch (flag) {
case ON_LOAD:
args.load = 1;
break;
case ON_UNLOAD:
args.unload = 1;
break;
case ON_SUSPEND:
args.suspend = 1;
break;
case ON_RESUME:
args.resume = 1;
break;
default:
dev_dbg(dev->dev, "Failed to send v2 heartbeat event, flag:0x%x\n", flag);
return -EINVAL;
}
params.length = sizeof(args);
params.pointer = &args;
info = apmf_if_call(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2, &params);
if (!info)
return -EIO;
kfree(info);
return 0;
}
int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx)
{
union acpi_object *info;
@ -166,6 +287,11 @@ int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data
return apmf_if_call_store_buffer(pdev, APMF_FUNC_AUTO_MODE, data, sizeof(*data));
}
int apmf_get_sbios_requests_v2(struct amd_pmf_dev *pdev, struct apmf_sbios_req_v2 *req)
{
return apmf_if_call_store_buffer(pdev, APMF_FUNC_SBIOS_REQUESTS, req, sizeof(*req));
}
int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req)
{
return apmf_if_call_store_buffer(pdev, APMF_FUNC_SBIOS_REQUESTS,
@ -218,8 +344,10 @@ static int apmf_if_verify_interface(struct amd_pmf_dev *pdev)
return err;
pdev->supported_func = output.supported_functions;
dev_dbg(pdev->dev, "supported functions:0x%x notifications:0x%x\n",
output.supported_functions, output.notification_mask);
dev_dbg(pdev->dev, "supported functions:0x%x notifications:0x%x version:%u\n",
output.supported_functions, output.notification_mask, output.version);
pdev->pmf_if_version = output.version;
return 0;
}
@ -320,7 +448,7 @@ void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
{
acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
if (pmf_dev->hb_interval)
if (pmf_dev->hb_interval && pmf_dev->pmf_if_version == PMF_IF_V1)
cancel_delayed_work_sync(&pmf_dev->heart_beat);
if (is_apmf_func_supported(pmf_dev, APMF_FUNC_AUTO_MODE) &&
@ -344,7 +472,7 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
goto out;
}
if (pmf_dev->hb_interval) {
if (pmf_dev->hb_interval && pmf_dev->pmf_if_version == PMF_IF_V1) {
/* send heartbeats only if the interval is not zero */
INIT_DELAYED_WORK(&pmf_dev->heart_beat, apmf_sbios_heartbeat_notify);
schedule_delayed_work(&pmf_dev->heart_beat, 0);

View file

@ -113,8 +113,9 @@ static void amd_pmf_dbgfs_unregister(struct amd_pmf_dev *dev)
static void amd_pmf_dbgfs_register(struct amd_pmf_dev *dev)
{
dev->dbgfs_dir = debugfs_create_dir("amd_pmf", NULL);
debugfs_create_file("current_power_limits", 0644, dev->dbgfs_dir, dev,
&current_power_limits_fops);
if (dev->pmf_if_version == PMF_IF_V1)
debugfs_create_file("current_power_limits", 0644, dev->dbgfs_dir, dev,
&current_power_limits_fops);
}
int amd_pmf_get_power_source(void)
@ -299,6 +300,9 @@ static int amd_pmf_suspend_handler(struct device *dev)
if (pdev->smart_pc_enabled)
cancel_delayed_work_sync(&pdev->pb_work);
if (is_apmf_func_supported(pdev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
amd_pmf_notify_sbios_heartbeat_event_v2(pdev, ON_SUSPEND);
return 0;
}
@ -313,6 +317,9 @@ static int amd_pmf_resume_handler(struct device *dev)
return ret;
}
if (is_apmf_func_supported(pdev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
amd_pmf_notify_sbios_heartbeat_event_v2(pdev, ON_RESUME);
if (pdev->smart_pc_enabled)
schedule_delayed_work(&pdev->pb_work, msecs_to_jiffies(2000));
@ -443,6 +450,8 @@ static int amd_pmf_probe(struct platform_device *pdev)
amd_pmf_dbgfs_register(dev);
amd_pmf_init_features(dev);
apmf_install_handler(dev);
if (is_apmf_func_supported(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_LOAD);
dev_info(dev->dev, "registered PMF device successfully\n");
@ -454,6 +463,8 @@ static void amd_pmf_remove(struct platform_device *pdev)
struct amd_pmf_dev *dev = platform_get_drvdata(pdev);
amd_pmf_deinit_features(dev);
if (is_apmf_func_supported(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_UNLOAD);
apmf_acpi_deinit(dev);
amd_pmf_dbgfs_unregister(dev);
mutex_destroy(&dev->lock);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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)

View file

@ -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"

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;
/**

View file

@ -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;

View file

@ -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 */

View file

@ -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);

View file

@ -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,

View file

@ -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;
}

View file

@ -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;

View file

@ -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",
};

View file

@ -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 },
};

View file

@ -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,

View file

@ -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, &params);
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)

View file

@ -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)

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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);

View file

@ -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) {

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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 */

View file

@ -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,

View file

@ -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>

View file

@ -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;
}

View file

@ -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,

View file

@ -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 */

View file

@ -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)

View file

@ -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,

View file

@ -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);

View file

@ -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;
}

View file

@ -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)

View file

@ -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);

View file

@ -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)

View file

@ -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 */