platform-drivers-x86 for v5.5-1

* New bootctl driver for Mellanox BlueField SoC.
 
 * New driver to support System76 laptops.
 
 * Temperature monitoring and fan control on Acer Aspire 7551
   is now supported.
 
 * Previously the Huawei driver handled only hotkeys.
   After the conversion to WMI it has been expanded
   to support newer laptop models.
 
 * Big refactoring of intel-speed-select tools allows to
   use it on Intel CascadeLake-N systems.
 
 * Touchscreen support for ezpad 6 m4 and
   Schneider SCT101CTM tablets
 
 * Miscellaneous clean ups and fixes here and there.
 
 The following is an automated git shortlog grouped by driver:
 
 acerhdf:
  -  Add support for Acer Aspire 7551
  -  Rename Peter Feuerer to Peter Kaestle
 
 Add System76 ACPI driver:
  - Add System76 ACPI driver
 
 asus-laptop:
  -  switch to using polled mode of input devices
 
 classmate-laptop:
  -  remove unused variable
 
 dell-laptop:
  -  disable kbd backlight on Inspiron 10xx
 
 hdaps:
  -  switch to using polled mode of input devices
 
 hp-wmi:
  -  Fix ACPI errors caused by passing 0 as input size
  -  Fix ACPI errors caused by too small buffer
 
 huawei-wmi:
  -  Remove unnecessary battery mutex
  -  No need to check for battery name
  -  Stricter battery thresholds set
  -  Fix a precision vs width printf bug
  -  Avoid use of global variable when possible
  -  No need to keep pointer to platform device
  -  Don't leak memory on the exit
  -  huawei_wmi can be static
  -  Add debugfs support
  -  Add fn-lock support
  -  Add battery charging thresholds
  -  Implement huawei wmi management
  -  Add quirks and module parameters
  -  Move to platform driver
 
 i2c-multi-instantiate:
  -  Fail the probe if no IRQ provided
 
 intel_cht_int33fe:
  -  Split code to Micro-B and Type-C
 
 intel_int0002_vgpio:
  -  Pass irqchip when adding gpiochip
 
 intel_pmc_core:
  -  Add Comet Lake (CML) platform support to intel_pmc_core driver
  -  Fix the SoC naming inconsistency
 
 intel_punit_ipc:
  -  Drop useless label
  -  use devm_platform_ioremap_resource() to simplify code
  -  Avoid error message when retrieving IRQ
 
 peaq-wmi:
  -  switch to using polled mode of input devices
 
 platform/mellanox:
  -  Fix Kconfig indentation
  -  Add bootctl driver for Mellanox BlueField Soc
 
 tools/power/x86/intel-speed-select:
  -  Display TRL buckets for just base config level
  -  Ignore missing config level
  -  Increment version
  -  Use core count for base-freq mask
  -  Support platform with limited Intel(R) Speed Select
  -  Use Frequency weight for CLOS
  -  Make CLOS frequency in MHz
  -  Use mailbox for CLOS_PM_QOS_CONFIG
  -  Auto mode for CLX
  -  Correct CLX-N frequency units
  -  Change display of "avx" to "avx2"
  -  Extend command set for perf-profile
  -  Implement base-freq commands on CascadeLake-N
  -  Implement 'perf-profile info' on CascadeLake-N
  -  Implement CascadeLake-N help and command functions structures
  -  Add check for CascadeLake-N models
  -  Make process_command generic
  -  Add int argument to command functions
  -  Refuse to disable core-power when getting used
  -  Turbo-freq feature auto mode
  -  Base-freq feature auto mode
  -  Remove warning for unused result
 
 toshiba_acpi:
  -  do not select INPUT_POLLDEV
 
 touchscreen_dmi:
  -  Add info for the ezpad 6 m4 tablet
  -  Add touchscreen platform data for the Schneider SCT101CTM tablet
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEqaflIX74DDDzMJJtb7wzTHR8rCgFAl3f20sACgkQb7wzTHR8
 rChciA//cvV45JxTLdbadW55L3nCDqa0poqCVqoCWOl3HvpEYw0uDakzD7AaxayL
 E4Vow+5puH2O0qqhkJSY3ZXMzKVB+h7lbe/97HfBOKJbXENgI9uVSX7TRcJJ1Bfj
 lelcA2GYtEm2Ig+YhsPCVVGU1NEF7+qvtDFR8WCExVfz7KLAQFQpm3bKqsOzBE4/
 atMjZmdU3gFW8e6AVyGP28N8lSGKZeY5i9mGv/tRG2aRS6y1gZjRPFi81awk/Ptb
 ujl+KmyQX/h31hU9Nxr1jEaAVjSEKyie9lLMKobmZldfzrpqE8sA3xe6BCfZfrdL
 WV4Db/92KI78LJljdoUlTg4JNZMn+Mi/N7jgs7ufbaXP9z7nrICZ6DKSYGW4MPXg
 j6A7iejXCohhUwvEtK0Zk0FlVhUreh5JHFyDKFlVR/A0sG0o9KrzyqUcmsySLIqX
 0p9abBFBgzplyUm8unryg1gKIrH1Mw0/41qzsJ8Tt7TPQBY6EKNtc28TGKtdIihe
 QNArtJwOorE5V7Hlp0Kbvn7cOeMa/w+gyQ7Fm4i2m8EpXICukYDCER85hnhc+JLs
 hGxIrdEZlN6jYhbMzQ7ue7m19wsbVgiB4zjy9tCRQUwg+/Y5QtKQ3nLWXrP22cR2
 Kw0BsE0hByzJ5hFJl/RLo3G0vBDYdh5s+lNVGDatkj0Y6b9dqSw=
 =KkPQ
 -----END PGP SIGNATURE-----

Merge tag 'platform-drivers-x86-v5.5-1' of git://git.infradead.org/linux-platform-drivers-x86

Pull x86 platform driver updates from Andy Shevchenko:

 - New bootctl driver for Mellanox BlueField SoC.

 - New driver to support System76 laptops.

 - Temperature monitoring and fan control on Acer Aspire 7551 is now
   supported.

 - Previously the Huawei driver handled only hotkeys. After the
   conversion to WMI it has been expanded to support newer laptop
   models.

 - Big refactoring of intel-speed-select tools allows to use it on Intel
   CascadeLake-N systems.

 - Touchscreen support for ezpad 6 m4 and Schneider SCT101CTM tablets

 - Miscellaneous clean ups and fixes here and there.

* tag 'platform-drivers-x86-v5.5-1' of git://git.infradead.org/linux-platform-drivers-x86: (59 commits)
  platform/x86: hp-wmi: Fix ACPI errors caused by passing 0 as input size
  platform/x86: hp-wmi: Fix ACPI errors caused by too small buffer
  platform/x86: intel_pmc_core: Add Comet Lake (CML) platform support to intel_pmc_core driver
  platform/x86: intel_pmc_core: Fix the SoC naming inconsistency
  platform/mellanox: Fix Kconfig indentation
  tools/power/x86/intel-speed-select: Display TRL buckets for just base config level
  tools/power/x86/intel-speed-select: Ignore missing config level
  platform/x86: touchscreen_dmi: Add info for the ezpad 6 m4 tablet
  tools/power/x86/intel-speed-select: Increment version
  tools/power/x86/intel-speed-select: Use core count for base-freq mask
  tools/power/x86/intel-speed-select: Support platform with limited Intel(R) Speed Select
  tools/power/x86/intel-speed-select: Use Frequency weight for CLOS
  tools/power/x86/intel-speed-select: Make CLOS frequency in MHz
  tools/power/x86/intel-speed-select: Use mailbox for CLOS_PM_QOS_CONFIG
  tools/power/x86/intel-speed-select: Auto mode for CLX
  tools/power/x86/intel-speed-select: Correct CLX-N frequency units
  tools/power/x86/intel-speed-select: Change display of "avx" to "avx2"
  tools/power/x86/intel-speed-select: Extend command set for perf-profile
  Add touchscreen platform data for the Schneider SCT101CTM tablet
  platform/x86: intel_int0002_vgpio: Pass irqchip when adding gpiochip
  ...
This commit is contained in:
Linus Torvalds 2019-12-01 18:24:25 -08:00
commit 67b8ed29e0
29 changed files with 3283 additions and 589 deletions

View File

@ -0,0 +1,58 @@
What: /sys/bus/platform/devices/MLNXBF04:00/driver/lifecycle_state
Date: Oct 2019
KernelVersion: 5.5
Contact: "Liming Sun <lsun@mellanox.com>"
Description:
The Life-cycle state of the SoC, which could be one of the
following values.
Production - Production state and can be updated to secure
GA Secured - Secure chip and not able to change state
GA Non-Secured - Non-Secure chip and not able to change state
RMA - Return Merchandise Authorization
What: /sys/bus/platform/devices/MLNXBF04:00/driver/post_reset_wdog
Date: Oct 2019
KernelVersion: 5.5
Contact: "Liming Sun <lsun@mellanox.com>"
Description:
The watchdog setting in seconds for the next booting. It's used
to reboot the chip and recover it to the old state if the new
boot partition fails.
What: /sys/bus/platform/devices/MLNXBF04:00/driver/reset_action
Date: Oct 2019
KernelVersion: 5.5
Contact: "Liming Sun <lsun@mellanox.com>"
Description:
The source of the boot stream for the next reset. It could be
one of the following values.
external - boot from external source (USB or PCIe)
emmc - boot from the onchip eMMC
emmc_legacy - boot from the onchip eMMC in legacy (slow) mode
What: /sys/bus/platform/devices/MLNXBF04:00/driver/second_reset_action
Date: Oct 2019
KernelVersion: 5.5
Contact: "Liming Sun <lsun@mellanox.com>"
Description:
Update the source of the boot stream after next reset. It could
be one of the following values and will be applied after next
reset.
external - boot from external source (USB or PCIe)
emmc - boot from the onchip eMMC
emmc_legacy - boot from the onchip eMMC in legacy (slow) mode
swap_emmc - swap the primary / secondary boot partition
none - cancel the action
What: /sys/bus/platform/devices/MLNXBF04:00/driver/secure_boot_fuse_state
Date: Oct 2019
KernelVersion: 5.5
Contact: "Liming Sun <lsun@mellanox.com>"
Description:
The state of eFuse versions with the following values.
InUse - burnt, valid and currently in use
Used - burnt and valid
Free - not burnt and free to use
Skipped - not burnt but not free (skipped)
Wasted - burnt and invalid
Invalid - not burnt but marked as valid (error state).

View File

@ -295,7 +295,7 @@ S: Maintained
F: drivers/net/ethernet/alteon/acenic*
ACER ASPIRE ONE TEMPERATURE AND FAN DRIVER
M: Peter Feuerer <peter@piie.net>
M: Peter Kaestle <peter@piie.net>
L: platform-driver-x86@vger.kernel.org
W: http://piie.net/?section=acerhdf
S: Maintained
@ -10577,6 +10577,7 @@ M: Darren Hart <dvhart@infradead.org>
M: Vadim Pasternak <vadimp@mellanox.com>
L: platform-driver-x86@vger.kernel.org
S: Supported
F: Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
F: drivers/platform/mellanox/
F: include/linux/platform_data/mlxreg.h
@ -15948,6 +15949,13 @@ F: drivers/hwtracing/stm/
F: include/linux/stm.h
F: include/uapi/linux/stm.h
SYSTEM76 ACPI DRIVER
M: Jeremy Soller <jeremy@system76.com>
M: System76 Product Development <productdev@system76.com>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/system76_acpi.c
SYSV FILESYSTEM
M: Christoph Hellwig <hch@infradead.org>
S: Maintained

View File

@ -41,7 +41,19 @@ config MLXBF_TMFIFO
depends on VIRTIO_CONSOLE && VIRTIO_NET
help
Say y here to enable TmFifo support. The TmFifo driver provides
platform driver support for the TmFifo which supports console
and networking based on the virtio framework.
platform driver support for the TmFifo which supports console
and networking based on the virtio framework.
config MLXBF_BOOTCTL
tristate "Mellanox BlueField Firmware Boot Control driver"
depends on ARM64
depends on ACPI
help
The Mellanox BlueField firmware implements functionality to
request swapping the primary and alternate eMMC boot partition,
and to set up a watchdog that can undo that swap if the system
does not boot up correctly. This driver provides sysfs access
to the userspace tools, to be used in conjunction with the eMMC
device driver to do necessary initial swap of the boot partition.
endif # MELLANOX_PLATFORM

View File

@ -3,6 +3,7 @@
# Makefile for linux/drivers/platform/mellanox
# Mellanox Platform-Specific Drivers
#
obj-$(CONFIG_MLXBF_BOOTCTL) += mlxbf-bootctl.o
obj-$(CONFIG_MLXBF_TMFIFO) += mlxbf-tmfifo.o
obj-$(CONFIG_MLXREG_HOTPLUG) += mlxreg-hotplug.o
obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o

View File

@ -0,0 +1,321 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Mellanox boot control driver
*
* This driver provides a sysfs interface for systems management
* software to manage reset-time actions.
*
* Copyright (C) 2019 Mellanox Technologies
*/
#include <linux/acpi.h>
#include <linux/arm-smccc.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "mlxbf-bootctl.h"
#define MLXBF_BOOTCTL_SB_SECURE_MASK 0x03
#define MLXBF_BOOTCTL_SB_TEST_MASK 0x0c
#define MLXBF_SB_KEY_NUM 4
/* UUID used to probe ATF service. */
static const char *mlxbf_bootctl_svc_uuid_str =
"89c036b4-e7d7-11e6-8797-001aca00bfc4";
struct mlxbf_bootctl_name {
u32 value;
const char *name;
};
static struct mlxbf_bootctl_name boot_names[] = {
{ MLXBF_BOOTCTL_EXTERNAL, "external" },
{ MLXBF_BOOTCTL_EMMC, "emmc" },
{ MLNX_BOOTCTL_SWAP_EMMC, "swap_emmc" },
{ MLXBF_BOOTCTL_EMMC_LEGACY, "emmc_legacy" },
{ MLXBF_BOOTCTL_NONE, "none" },
};
static const char * const mlxbf_bootctl_lifecycle_states[] = {
[0] = "Production",
[1] = "GA Secured",
[2] = "GA Non-Secured",
[3] = "RMA",
};
/* ARM SMC call which is atomic and no need for lock. */
static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg)
{
struct arm_smccc_res res;
arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res);
return res.a0;
}
/* Return the action in integer or an error code. */
static int mlxbf_bootctl_reset_action_to_val(const char *action)
{
int i;
for (i = 0; i < ARRAY_SIZE(boot_names); i++)
if (sysfs_streq(boot_names[i].name, action))
return boot_names[i].value;
return -EINVAL;
}
/* Return the action in string. */
static const char *mlxbf_bootctl_action_to_string(int action)
{
int i;
for (i = 0; i < ARRAY_SIZE(boot_names); i++)
if (boot_names[i].value == action)
return boot_names[i].name;
return "invalid action";
}
static ssize_t post_reset_wdog_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_POST_RESET_WDOG, 0);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", ret);
}
static ssize_t post_reset_wdog_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long value;
int ret;
ret = kstrtoul(buf, 10, &value);
if (ret)
return ret;
ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_POST_RESET_WDOG, value);
if (ret < 0)
return ret;
return count;
}
static ssize_t mlxbf_bootctl_show(int smc_op, char *buf)
{
int action;
action = mlxbf_bootctl_smc(smc_op, 0);
if (action < 0)
return action;
return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action));
}
static int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count)
{
int ret, action;
action = mlxbf_bootctl_reset_action_to_val(buf);
if (action < 0)
return action;
ret = mlxbf_bootctl_smc(smc_op, action);
if (ret < 0)
return ret;
return count;
}
static ssize_t reset_action_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_RESET_ACTION, buf);
}
static ssize_t reset_action_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_RESET_ACTION, buf, count);
}
static ssize_t second_reset_action_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION, buf);
}
static ssize_t second_reset_action_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION, buf,
count);
}
static ssize_t lifecycle_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int lc_state;
lc_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
if (lc_state < 0)
return lc_state;
lc_state &=
MLXBF_BOOTCTL_SB_TEST_MASK | MLXBF_BOOTCTL_SB_SECURE_MASK;
/*
* If the test bits are set, we specify that the current state may be
* due to using the test bits.
*/
if (lc_state & MLXBF_BOOTCTL_SB_TEST_MASK) {
lc_state &= MLXBF_BOOTCTL_SB_SECURE_MASK;
return sprintf(buf, "%s(test)\n",
mlxbf_bootctl_lifecycle_states[lc_state]);
}
return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
}
static ssize_t secure_boot_fuse_state_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int burnt, valid, key, key_state, buf_len = 0, upper_key_used = 0;
const char *status;
key_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
if (key_state < 0)
return key_state;
/*
* key_state contains the bits for 4 Key versions, loaded from eFuses
* after a hard reset. Lower 4 bits are a thermometer code indicating
* key programming has started for key n (0000 = none, 0001 = version 0,
* 0011 = version 1, 0111 = version 2, 1111 = version 3). Upper 4 bits
* are a thermometer code indicating key programming has completed for
* key n (same encodings as the start bits). This allows for detection
* of an interruption in the progamming process which has left the key
* partially programmed (and thus invalid). The process is to burn the
* eFuse for the new key start bit, burn the key eFuses, then burn the
* eFuse for the new key complete bit.
*
* For example 0000_0000: no key valid, 0001_0001: key version 0 valid,
* 0011_0011: key 1 version valid, 0011_0111: key version 2 started
* programming but did not complete, etc. The most recent key for which
* both start and complete bit is set is loaded. On soft reset, this
* register is not modified.
*/
for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
burnt = key_state & BIT(key);
valid = key_state & BIT(key + MLXBF_SB_KEY_NUM);
if (burnt && valid)
upper_key_used = 1;
if (upper_key_used) {
if (burnt)
status = valid ? "Used" : "Wasted";
else
status = valid ? "Invalid" : "Skipped";
} else {
if (burnt)
status = valid ? "InUse" : "Incomplete";
else
status = valid ? "Invalid" : "Free";
}
buf_len += sprintf(buf + buf_len, "%d:%s ", key, status);
}
buf_len += sprintf(buf + buf_len, "\n");
return buf_len;
}
static DEVICE_ATTR_RW(post_reset_wdog);
static DEVICE_ATTR_RW(reset_action);
static DEVICE_ATTR_RW(second_reset_action);
static DEVICE_ATTR_RO(lifecycle_state);
static DEVICE_ATTR_RO(secure_boot_fuse_state);
static struct attribute *mlxbf_bootctl_attrs[] = {
&dev_attr_post_reset_wdog.attr,
&dev_attr_reset_action.attr,
&dev_attr_second_reset_action.attr,
&dev_attr_lifecycle_state.attr,
&dev_attr_secure_boot_fuse_state.attr,
NULL
};
ATTRIBUTE_GROUPS(mlxbf_bootctl);
static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
{"MLNXBF04", 0},
{}
};
MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
static bool mlxbf_bootctl_guid_match(const guid_t *guid,
const struct arm_smccc_res *res)
{
guid_t id = GUID_INIT(res->a0, res->a1, res->a1 >> 16,
res->a2, res->a2 >> 8, res->a2 >> 16,
res->a2 >> 24, res->a3, res->a3 >> 8,
res->a3 >> 16, res->a3 >> 24);
return guid_equal(guid, &id);
}
static int mlxbf_bootctl_probe(struct platform_device *pdev)
{
struct arm_smccc_res res = { 0 };
guid_t guid;
int ret;
/* Ensure we have the UUID we expect for this service. */
arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res);
guid_parse(mlxbf_bootctl_svc_uuid_str, &guid);
if (!mlxbf_bootctl_guid_match(&guid, &res))
return -ENODEV;
/*
* When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_SWAP_EMMC
* in case of boot failures. However it doesn't clear the state if there
* is no failure. Restore the default boot mode here to avoid any
* unnecessary boot partition swapping.
*/
ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_RESET_ACTION,
MLXBF_BOOTCTL_EMMC);
if (ret < 0)
dev_warn(&pdev->dev, "Unable to reset the EMMC boot mode\n");
return 0;
}
static struct platform_driver mlxbf_bootctl_driver = {
.probe = mlxbf_bootctl_probe,
.driver = {
.name = "mlxbf-bootctl",
.groups = mlxbf_bootctl_groups,
.acpi_match_table = mlxbf_bootctl_acpi_ids,
}
};
module_platform_driver(mlxbf_bootctl_driver);
MODULE_DESCRIPTION("Mellanox boot control driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Mellanox Technologies");

View File

@ -0,0 +1,103 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019, Mellanox Technologies. All rights reserved.
*/
#ifndef __MLXBF_BOOTCTL_H__
#define __MLXBF_BOOTCTL_H__
/*
* Request that the on-chip watchdog be enabled, or disabled, after
* the next chip soft reset. This call does not affect the current
* status of the on-chip watchdog. If non-zero, the argument
* specifies the watchdog interval in seconds. If zero, the watchdog
* will not be enabled after the next soft reset. Non-zero errors are
* returned as documented below.
*/
#define MLXBF_BOOTCTL_SET_POST_RESET_WDOG 0x82000000
/*
* Query the status which has been requested for the on-chip watchdog
* after the next chip soft reset. Returns the interval as set by
* MLXBF_BOOTCTL_SET_POST_RESET_WDOG.
*/
#define MLXBF_BOOTCTL_GET_POST_RESET_WDOG 0x82000001
/*
* Request that a specific boot action be taken at the next soft
* reset. By default, the boot action is set by external chip pins,
* which are sampled on hard reset. Note that the boot action
* requested by this call will persist on subsequent resets unless
* this service, or the MLNX_SET_SECOND_RESET_ACTION service, is
* invoked. See below for the available MLNX_BOOT_xxx parameter
* values. Non-zero errors are returned as documented below.
*/
#define MLXBF_BOOTCTL_SET_RESET_ACTION 0x82000002
/*
* Return the specific boot action which will be taken at the next
* soft reset. Returns the reset action (see below for the parameter
* values for MLXBF_BOOTCTL_SET_RESET_ACTION).
*/
#define MLXBF_BOOTCTL_GET_RESET_ACTION 0x82000003
/*
* Request that a specific boot action be taken at the soft reset
* after the next soft reset. For a specified valid boot mode, the
* effect of this call is identical to that of invoking
* MLXBF_BOOTCTL_SET_RESET_ACTION after the next chip soft reset; in
* particular, after that reset, the action for the now next reset can
* be queried with MLXBF_BOOTCTL_GET_RESET_ACTION and modified with
* MLXBF_BOOTCTL_SET_RESET_ACTION. You may also specify the parameter as
* MLNX_BOOT_NONE, which is equivalent to specifying that no call to
* MLXBF_BOOTCTL_SET_RESET_ACTION be taken after the next chip soft reset.
* This call does not affect the action to be taken at the next soft
* reset. Non-zero errors are returned as documented below.
*/
#define MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION 0x82000004
/*
* Return the specific boot action which will be taken at the soft
* reset after the next soft reset; this will be one of the valid
* actions for MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION.
*/
#define MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION 0x82000005
/*
* Return the fuse status of the current chip. The caller should specify
* with the second argument if the state of the lifecycle fuses or the
* version of secure boot fuse keys left should be returned.
*/
#define MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS 0x82000006
/* Reset eMMC by programming the RST_N register. */
#define MLXBF_BOOTCTL_SET_EMMC_RST_N 0x82000007
#define MLXBF_BOOTCTL_GET_DIMM_INFO 0x82000008
/* SMC function IDs for SiP Service queries */
#define MLXBF_BOOTCTL_SIP_SVC_CALL_COUNT 0x8200ff00
#define MLXBF_BOOTCTL_SIP_SVC_UID 0x8200ff01
#define MLXBF_BOOTCTL_SIP_SVC_VERSION 0x8200ff03
/* ARM Standard Service Calls version numbers */
#define MLXBF_BOOTCTL_SVC_VERSION_MAJOR 0x0
#define MLXBF_BOOTCTL_SVC_VERSION_MINOR 0x2
/* Number of svc calls defined. */
#define MLXBF_BOOTCTL_NUM_SVC_CALLS 12
/* Valid reset actions for MLXBF_BOOTCTL_SET_RESET_ACTION. */
#define MLXBF_BOOTCTL_EXTERNAL 0 /* Not boot from eMMC */
#define MLXBF_BOOTCTL_EMMC 1 /* From primary eMMC boot partition */
#define MLNX_BOOTCTL_SWAP_EMMC 2 /* Swap eMMC boot partitions and reboot */
#define MLXBF_BOOTCTL_EMMC_LEGACY 3 /* From primary eMMC in legacy mode */
/* Valid arguments for requesting the fuse status. */
#define MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE 0 /* Return lifecycle status. */
#define MLXBF_BOOTCTL_FUSE_STATUS_KEYS 1 /* Return secure boot key status */
/* Additional value to disable the MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION. */
#define MLXBF_BOOTCTL_NONE 0x7fffffff /* Don't change next boot action */
#endif /* __MLXBF_BOOTCTL_H__ */

View File

@ -94,7 +94,6 @@ config ASUS_LAPTOP
depends on RFKILL || RFKILL = n
depends on ACPI_VIDEO || ACPI_VIDEO = n
select INPUT_SPARSEKMAP
select INPUT_POLLDEV
---help---
This is a driver for Asus laptops, Lenovo SL and the Pegatron
Lucid tablet. It may also support some MEDION, JVC or VICTOR
@ -623,7 +622,6 @@ config THINKPAD_ACPI_HOTKEY_POLL
config SENSORS_HDAPS
tristate "Thinkpad Hard Drive Active Protection System (hdaps)"
depends on INPUT
select INPUT_POLLDEV
help
This driver provides support for the IBM Hard Drive Active Protection
System (hdaps), which provides an accelerometer and other misc. data.
@ -806,7 +804,6 @@ config PEAQ_WMI
tristate "PEAQ 2-in-1 WMI hotkey driver"
depends on ACPI_WMI
depends on INPUT
select INPUT_POLLDEV
help
Say Y here if you want to support WMI-based hotkeys on PEAQ 2-in-1s.
@ -834,7 +831,6 @@ config ACPI_TOSHIBA
depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on RFKILL || RFKILL = n
depends on IIO
select INPUT_POLLDEV
select INPUT_SPARSEKMAP
---help---
This driver adds support for access to certain system settings
@ -931,14 +927,20 @@ config INTEL_CHT_INT33FE
This driver add support for the INT33FE ACPI device found on
some Intel Cherry Trail devices.
There are two kinds of INT33FE ACPI device possible: for hardware
with USB Type-C and Micro-B connectors. This driver supports both.
The INT33FE ACPI device has a CRS table with I2cSerialBusV2
resources for 3 devices: Maxim MAX17047 Fuel Gauge Controller,
resources for Fuel Gauge Controller and (in the Type-C variant)
FUSB302 USB Type-C Controller and PI3USB30532 USB switch.
This driver instantiates i2c-clients for these, so that standard
i2c drivers for these chips can bind to the them.
If you enable this driver it is advised to also select
CONFIG_TYPEC_FUSB302=m and CONFIG_BATTERY_MAX17042=m.
CONFIG_BATTERY_BQ27XXX=m or CONFIG_BATTERY_BQ27XXX_I2C=m for Micro-B
device and CONFIG_TYPEC_FUSB302=m and CONFIG_BATTERY_MAX17042=m
for Type-C device.
config INTEL_INT0002_VGPIO
tristate "Intel ACPI INT0002 Virtual GPIO driver"
@ -1305,7 +1307,8 @@ config INTEL_ATOMISP2_PM
will be called intel_atomisp2_pm.
config HUAWEI_WMI
tristate "Huawei WMI hotkeys driver"
tristate "Huawei WMI laptop extras driver"
depends on ACPI_BATTERY
depends on ACPI_WMI
depends on INPUT
select INPUT_SPARSEKMAP
@ -1314,9 +1317,8 @@ config HUAWEI_WMI
select LEDS_TRIGGER_AUDIO
select NEW_LEDS
help
This driver provides support for Huawei WMI hotkeys.
It enables the missing keys and adds support to the micmute
LED found on some of these laptops.
This driver provides support for Huawei WMI hotkeys, battery charge
control, fn-lock, mic-mute LED, and other extra features.
To compile this driver as a module, choose M here: the module
will be called huawei-wmi.
@ -1337,6 +1339,19 @@ config PCENGINES_APU2
source "drivers/platform/x86/intel_speed_select_if/Kconfig"
config SYSTEM76_ACPI
tristate "System76 ACPI Driver"
depends on ACPI
select NEW_LEDS
select LEDS_CLASS
select LEDS_TRIGGERS
help
This is a driver for System76 laptops running open firmware. It adds
support for Fn-Fx key combinations, keyboard backlight, and airplane mode
LEDs.
If you have a System76 laptop running open firmware, say Y or M here.
endif # X86_PLATFORM_DEVICES
config PMC_ATOM

View File

@ -61,6 +61,10 @@ obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o
obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o
obj-$(CONFIG_INTEL_CHT_INT33FE) += intel_cht_int33fe.o
intel_cht_int33fe-objs := intel_cht_int33fe_common.o \
intel_cht_int33fe_typec.o \
intel_cht_int33fe_microb.o
obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o
obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o
obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o
@ -100,3 +104,4 @@ obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o
obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o
obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += intel_speed_select_if/
obj-$(CONFIG_SYSTEM76_ACPI) += system76_acpi.o

View File

@ -4,7 +4,7 @@
* of the aspire one netbook, turns on/off the fan
* as soon as the upper/lower threshold is reached.
*
* (C) 2009 - Peter Feuerer peter (a) piie.net
* (C) 2009 - Peter Kaestle peter (a) piie.net
* http://piie.net
* 2009 Borislav Petkov bp (a) alien8.de
*
@ -224,6 +224,8 @@ static const struct bios_settings bios_tbl[] __initconst = {
{"Acer", "Aspire 5739G", "V1.3311", 0x55, 0x58, {0x20, 0x00}, 0},
/* Acer TravelMate 7730 */
{"Acer", "TravelMate 7730G", "v0.3509", 0x55, 0x58, {0xaf, 0x00}, 0},
/* Acer Aspire 7551 */
{"Acer", "Aspire 7551", "V1.18", 0x93, 0xa8, {0x14, 0x04}, 1},
/* Acer TravelMate TM8573T */
{"Acer", "TM8573T", "V1.13", 0x93, 0xa8, {0x14, 0x04}, 1},
/* Gateway */
@ -801,7 +803,7 @@ static void __exit acerhdf_exit(void)
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Peter Feuerer");
MODULE_AUTHOR("Peter Kaestle");
MODULE_DESCRIPTION("Aspire One temperature and fan driver");
MODULE_ALIAS("dmi:*:*Acer*:pnAOA*:");
MODULE_ALIAS("dmi:*:*Acer*:pnAO751h*:");
@ -815,6 +817,7 @@ MODULE_ALIAS("dmi:*:*Acer*:pnAspire*5739G:");
MODULE_ALIAS("dmi:*:*Acer*:pnAspire*One*753:");
MODULE_ALIAS("dmi:*:*Acer*:pnAspire*5315:");
MODULE_ALIAS("dmi:*:*Acer*:TravelMate*7730G:");
MODULE_ALIAS("dmi:*:*Acer*:pnAspire*7551:");
MODULE_ALIAS("dmi:*:*Acer*:TM8573T:");
MODULE_ALIAS("dmi:*:*Gateway*:pnAOA*:");
MODULE_ALIAS("dmi:*:*Gateway*:pnLT31*:");

View File

@ -34,7 +34,6 @@
#include <linux/uaccess.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/input-polldev.h>
#include <linux/rfkill.h>
#include <linux/slab.h>
#include <linux/dmi.h>
@ -244,7 +243,7 @@ struct asus_laptop {
struct input_dev *inputdev;
struct key_entry *keymap;
struct input_polled_dev *pega_accel_poll;
struct input_dev *pega_accel_poll;
struct asus_led wled;
struct asus_led bled;
@ -446,9 +445,9 @@ static int pega_acc_axis(struct asus_laptop *asus, int curr, char *method)
return clamp_val((short)val, -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP);
}
static void pega_accel_poll(struct input_polled_dev *ipd)
static void pega_accel_poll(struct input_dev *input)
{
struct device *parent = ipd->input->dev.parent;
struct device *parent = input->dev.parent;
struct asus_laptop *asus = dev_get_drvdata(parent);
/* In some cases, the very first call to poll causes a
@ -457,10 +456,10 @@ static void pega_accel_poll(struct input_polled_dev *ipd)
* device, and perhaps a firmware bug. Fake the first report. */
if (!asus->pega_acc_live) {
asus->pega_acc_live = true;
input_report_abs(ipd->input, ABS_X, 0);
input_report_abs(ipd->input, ABS_Y, 0);
input_report_abs(ipd->input, ABS_Z, 0);
input_sync(ipd->input);
input_report_abs(input, ABS_X, 0);
input_report_abs(input, ABS_Y, 0);
input_report_abs(input, ABS_Z, 0);
input_sync(input);
return;
}
@ -471,25 +470,24 @@ static void pega_accel_poll(struct input_polled_dev *ipd)
/* Note transform, convert to "right/up/out" in the native
* landscape orientation (i.e. the vector is the direction of
* "real up" in the device's cartiesian coordinates). */
input_report_abs(ipd->input, ABS_X, -asus->pega_acc_x);
input_report_abs(ipd->input, ABS_Y, -asus->pega_acc_y);
input_report_abs(ipd->input, ABS_Z, asus->pega_acc_z);
input_sync(ipd->input);
input_report_abs(input, ABS_X, -asus->pega_acc_x);
input_report_abs(input, ABS_Y, -asus->pega_acc_y);
input_report_abs(input, ABS_Z, asus->pega_acc_z);
input_sync(input);
}
static void pega_accel_exit(struct asus_laptop *asus)
{
if (asus->pega_accel_poll) {
input_unregister_polled_device(asus->pega_accel_poll);
input_free_polled_device(asus->pega_accel_poll);
input_unregister_device(asus->pega_accel_poll);
asus->pega_accel_poll = NULL;
}
asus->pega_accel_poll = NULL;
}
static int pega_accel_init(struct asus_laptop *asus)
{
int err;
struct input_polled_dev *ipd;
struct input_dev *input;
if (!asus->is_pega_lucid)
return -ENODEV;
@ -499,37 +497,39 @@ static int pega_accel_init(struct asus_laptop *asus)
acpi_check_handle(asus->handle, METHOD_XLRZ, NULL))
return -ENODEV;
ipd = input_allocate_polled_device();
if (!ipd)
input = input_allocate_device();
if (!input)
return -ENOMEM;
ipd->poll = pega_accel_poll;
ipd->poll_interval = 125;
ipd->poll_interval_min = 50;
ipd->poll_interval_max = 2000;
input->name = PEGA_ACCEL_DESC;
input->phys = PEGA_ACCEL_NAME "/input0";
input->dev.parent = &asus->platform_device->dev;
input->id.bustype = BUS_HOST;
ipd->input->name = PEGA_ACCEL_DESC;
ipd->input->phys = PEGA_ACCEL_NAME "/input0";
ipd->input->dev.parent = &asus->platform_device->dev;
ipd->input->id.bustype = BUS_HOST;
set_bit(EV_ABS, ipd->input->evbit);
input_set_abs_params(ipd->input, ABS_X,
input_set_abs_params(input, ABS_X,
-PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
input_set_abs_params(ipd->input, ABS_Y,
input_set_abs_params(input, ABS_Y,
-PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
input_set_abs_params(ipd->input, ABS_Z,
input_set_abs_params(input, ABS_Z,
-PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
err = input_register_polled_device(ipd);
err = input_setup_polling(input, pega_accel_poll);
if (err)
goto exit;
asus->pega_accel_poll = ipd;
input_set_poll_interval(input, 125);
input_set_min_poll_interval(input, 50);
input_set_max_poll_interval(input, 2000);
err = input_register_device(input);
if (err)
goto exit;
asus->pega_accel_poll = input;
return 0;
exit:
input_free_polled_device(ipd);
input_free_device(input);
return err;
}
@ -1550,8 +1550,7 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)
/* Accelerometer "coarse orientation change" event */
if (asus->pega_accel_poll && event == 0xEA) {
kobject_uevent(&asus->pega_accel_poll->input->dev.kobj,
KOBJ_CHANGE);
kobject_uevent(&asus->pega_accel_poll->dev.kobj, KOBJ_CHANGE);
return ;
}

View File

@ -33,6 +33,7 @@
struct quirk_entry {
bool touchpad_led;
bool kbd_led_not_present;
bool kbd_led_levels_off_1;
bool kbd_missing_ac_tag;
@ -73,6 +74,10 @@ static struct quirk_entry quirk_dell_latitude_e6410 = {
.kbd_led_levels_off_1 = true,
};
static struct quirk_entry quirk_dell_inspiron_1012 = {
.kbd_led_not_present = true,
};
static struct platform_driver platform_driver = {
.driver = {
.name = "dell-laptop",
@ -310,6 +315,24 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
},
.driver_data = &quirk_dell_latitude_e6410,
},
{
.callback = dmi_matched,
.ident = "Dell Inspiron 1012",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"),
},
.driver_data = &quirk_dell_inspiron_1012,
},
{
.callback = dmi_matched,
.ident = "Dell Inspiron 1018",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1018"),
},
.driver_data = &quirk_dell_inspiron_1012,
},
{ }
};
@ -1493,6 +1516,9 @@ static void kbd_init(void)
{
int ret;
if (quirks && quirks->kbd_led_not_present)
return;
ret = kbd_init_info();
kbd_init_tokens();

View File

@ -18,7 +18,7 @@
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input-polldev.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/module.h>
@ -59,7 +59,7 @@
#define HDAPS_BOTH_AXES (HDAPS_X_AXIS | HDAPS_Y_AXIS)
static struct platform_device *pdev;
static struct input_polled_dev *hdaps_idev;
static struct input_dev *hdaps_idev;
static unsigned int hdaps_invert;
static u8 km_activity;
static int rest_x;
@ -318,9 +318,8 @@ static void hdaps_calibrate(void)
__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y);
}
static void hdaps_mousedev_poll(struct input_polled_dev *dev)
static void hdaps_mousedev_poll(struct input_dev *input_dev)
{
struct input_dev *input_dev = dev->input;
int x, y;
mutex_lock(&hdaps_mtx);
@ -531,7 +530,6 @@ static const struct dmi_system_id hdaps_whitelist[] __initconst = {
static int __init hdaps_init(void)
{
struct input_dev *idev;
int ret;
if (!dmi_check_system(hdaps_whitelist)) {
@ -559,31 +557,32 @@ static int __init hdaps_init(void)
if (ret)
goto out_device;
hdaps_idev = input_allocate_polled_device();
hdaps_idev = input_allocate_device();
if (!hdaps_idev) {
ret = -ENOMEM;
goto out_group;
}
hdaps_idev->poll = hdaps_mousedev_poll;
hdaps_idev->poll_interval = HDAPS_POLL_INTERVAL;
/* initial calibrate for the input device */
hdaps_calibrate();
/* initialize the input class */
idev = hdaps_idev->input;
idev->name = "hdaps";
idev->phys = "isa1600/input0";
idev->id.bustype = BUS_ISA;
idev->dev.parent = &pdev->dev;
idev->evbit[0] = BIT_MASK(EV_ABS);
input_set_abs_params(idev, ABS_X,
hdaps_idev->name = "hdaps";
hdaps_idev->phys = "isa1600/input0";
hdaps_idev->id.bustype = BUS_ISA;
hdaps_idev->dev.parent = &pdev->dev;
input_set_abs_params(hdaps_idev, ABS_X,
-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
input_set_abs_params(idev, ABS_Y,
input_set_abs_params(hdaps_idev, ABS_Y,
-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
ret = input_register_polled_device(hdaps_idev);
ret = input_setup_polling(hdaps_idev, hdaps_mousedev_poll);
if (ret)
goto out_idev;
input_set_poll_interval(hdaps_idev, HDAPS_POLL_INTERVAL);
ret = input_register_device(hdaps_idev);
if (ret)
goto out_idev;
@ -591,7 +590,7 @@ static int __init hdaps_init(void)
return 0;
out_idev:
input_free_polled_device(hdaps_idev);
input_free_device(hdaps_idev);
out_group:
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
out_device:
@ -607,8 +606,7 @@ static int __init hdaps_init(void)
static void __exit hdaps_exit(void)
{
input_unregister_polled_device(hdaps_idev);
input_free_polled_device(hdaps_idev);
input_unregister_device(hdaps_idev);
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
platform_device_unregister(pdev);
platform_driver_unregister(&hdaps_driver);

View File

@ -65,7 +65,7 @@ struct bios_args {
u32 command;
u32 commandtype;
u32 datasize;
u32 data;
u8 data[128];
};
enum hp_wmi_commandtype {
@ -216,7 +216,7 @@ static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
.command = command,
.commandtype = query,
.datasize = insize,
.data = 0,
.data = { 0 },
};
struct acpi_buffer input = { sizeof(struct bios_args), &args };
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
@ -228,7 +228,7 @@ static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
if (WARN_ON(insize > sizeof(args.data)))
return -EINVAL;
memcpy(&args.data, buffer, insize);
memcpy(&args.data[0], buffer, insize);
wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output);
@ -380,7 +380,7 @@ static int hp_wmi_rfkill2_refresh(void)
int err, i;
err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state,
0, sizeof(state));
sizeof(state), sizeof(state));
if (err)
return err;
@ -778,7 +778,7 @@ static int __init hp_wmi_rfkill2_setup(struct platform_device *device)
int err, i;
err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state,
0, sizeof(state));
sizeof(state), sizeof(state));
if (err)
return err < 0 ? err : -EINVAL;

View File

@ -1,32 +1,77 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Huawei WMI hotkeys
* Huawei WMI laptop extras driver
*
* Copyright (C) 2018 Ayman Bagabas <ayman.bagabas@gmail.com>
*/
#include <linux/acpi.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/sysfs.h>
#include <linux/wmi.h>
#include <acpi/battery.h>
/*
* Huawei WMI GUIDs
*/
#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000"
#define HWMI_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C90629100000"
#define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000"
/* Legacy GUIDs */
#define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
struct huawei_wmi_priv {
struct input_dev *idev;
struct led_classdev cdev;
acpi_handle handle;
char *acpi_method;
/* HWMI commands */
enum {
BATTERY_THRESH_GET = 0x00001103, /* \GBTT */
BATTERY_THRESH_SET = 0x00001003, /* \SBTT */
FN_LOCK_GET = 0x00000604, /* \GFRS */
FN_LOCK_SET = 0x00000704, /* \SFRS */
MICMUTE_LED_SET = 0x00000b04, /* \SMLS */
};
union hwmi_arg {
u64 cmd;
u8 args[8];
};
struct quirk_entry {
bool battery_reset;
bool ec_micmute;
bool report_brightness;
};
static struct quirk_entry *quirks;
struct huawei_wmi_debug {
struct dentry *root;
u64 arg;
};
struct huawei_wmi {
bool battery_available;
bool fn_lock_available;
struct huawei_wmi_debug debug;
struct input_dev *idev[2];
struct led_classdev cdev;
struct device *dev;
struct mutex wmi_lock;
};
static struct huawei_wmi *huawei_wmi;
static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY, 0x281, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY, 0x282, { KEY_BRIGHTNESSUP } },
@ -37,73 +82,614 @@ static const struct key_entry huawei_wmi_keymap[] = {
{ KE_KEY, 0x289, { KEY_WLAN } },
// Huawei |M| key
{ KE_KEY, 0x28a, { KEY_CONFIG } },
// Keyboard backlight
// Keyboard backlit
{ KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
{ KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
{ KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
{ KE_END, 0 }
};
static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
static int battery_reset = -1;
static int report_brightness = -1;
module_param(battery_reset, bint, 0444);
MODULE_PARM_DESC(battery_reset,
"Reset battery charge values to (0-0) before disabling it using (0-100)");
module_param(report_brightness, bint, 0444);
MODULE_PARM_DESC(report_brightness,
"Report brightness keys.");
/* Quirks */
static int __init dmi_matched(const struct dmi_system_id *dmi)
{
quirks = dmi->driver_data;
return 1;
}
static struct quirk_entry quirk_unknown = {
};
static struct quirk_entry quirk_battery_reset = {
.battery_reset = true,
};
static struct quirk_entry quirk_matebook_x = {
.ec_micmute = true,
.report_brightness = true,
};
static const struct dmi_system_id huawei_quirks[] = {
{
.callback = dmi_matched,
.ident = "Huawei MACH-WX9",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
DMI_MATCH(DMI_PRODUCT_NAME, "MACH-WX9"),
},
.driver_data = &quirk_battery_reset
},
{
.callback = dmi_matched,
.ident = "Huawei MateBook X",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
DMI_MATCH(DMI_PRODUCT_NAME, "HUAWEI MateBook X")
},
.driver_data = &quirk_matebook_x
},
{ }
};
/* Utils */
static int huawei_wmi_call(struct huawei_wmi *huawei,
struct acpi_buffer *in, struct acpi_buffer *out)
{
struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
acpi_status status;
union acpi_object args[3];
struct acpi_object_list arg_list = {
.pointer = args,
.count = ARRAY_SIZE(args),
};
args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
args[1].integer.value = 0x04;
if (strcmp(priv->acpi_method, "SPIN") == 0) {
args[0].integer.value = 0;
args[2].integer.value = brightness ? 1 : 0;
} else if (strcmp(priv->acpi_method, "WPIN") == 0) {
args[0].integer.value = 1;
args[2].integer.value = brightness ? 0 : 1;
} else {
return -EINVAL;
mutex_lock(&huawei->wmi_lock);
status = wmi_evaluate_method(HWMI_METHOD_GUID, 0, 1, in, out);
mutex_unlock(&huawei->wmi_lock);
if (ACPI_FAILURE(status)) {
dev_err(huawei->dev, "Failed to evaluate wmi method\n");
return -ENODEV;
}
status = acpi_evaluate_object(priv->handle, priv->acpi_method, &arg_list, NULL);
if (ACPI_FAILURE(status))
return -ENXIO;
return 0;
}
static int huawei_wmi_leds_setup(struct wmi_device *wdev)
/* HWMI takes a 64 bit input and returns either a package with 2 buffers, one of
* 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes.
* The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got a
* package, or skip the first 4 if a buffer of 0x104 is used. The first byte of
* the remaining 0x100 sized buffer has the return status of every call. In case
* the return status is non-zero, we return -ENODEV but still copy the returned
* buffer to the given buffer parameter (buf).
*/
static int huawei_wmi_cmd(u64 arg, u8 *buf, size_t buflen)
{
struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
struct huawei_wmi *huawei = huawei_wmi;
struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_buffer in;
union acpi_object *obj;
size_t len;
int err, i;
priv->handle = ec_get_handle();
if (!priv->handle)
return 0;
in.length = sizeof(arg);
in.pointer = &arg;
if (acpi_has_method(priv->handle, "SPIN"))
priv->acpi_method = "SPIN";
else if (acpi_has_method(priv->handle, "WPIN"))
priv->acpi_method = "WPIN";
else
return 0;
/* Some models require calling HWMI twice to execute a command. We evaluate
* HWMI and if we get a non-zero return status we evaluate it again.
*/
for (i = 0; i < 2; i++) {
err = huawei_wmi_call(huawei, &in, &out);
if (err)
goto fail_cmd;
priv->cdev.name = "platform::micmute";
priv->cdev.max_brightness = 1;
priv->cdev.brightness_set_blocking = huawei_wmi_micmute_led_set;
priv->cdev.default_trigger = "audio-micmute";
priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
priv->cdev.dev = &wdev->dev;
priv->cdev.flags = LED_CORE_SUSPENDRESUME;
obj = out.pointer;
if (!obj) {
err = -EIO;
goto fail_cmd;
}
return devm_led_classdev_register(&wdev->dev, &priv->cdev);
switch (obj->type) {
/* Models that implement both "legacy" and HWMI tend to return a 0x104
* sized buffer instead of a package of 0x4 and 0x100 buffers.
*/
case ACPI_TYPE_BUFFER:
if (obj->buffer.length == 0x104) {
// Skip the first 4 bytes.
obj->buffer.pointer += 4;
len = 0x100;
} else {
dev_err(huawei->dev, "Bad buffer length, got %d\n", obj->buffer.length);
err = -EIO;
goto fail_cmd;
}
break;
/* HWMI returns a package with 2 buffer elements, one of 4 bytes and the
* other is 256 bytes.
*/
case ACPI_TYPE_PACKAGE:
if (obj->package.count != 2) {
dev_err(huawei->dev, "Bad package count, got %d\n", obj->package.count);
err = -EIO;
goto fail_cmd;
}
obj = &obj->package.elements[1];
if (obj->type != ACPI_TYPE_BUFFER) {
dev_err(huawei->dev, "Bad package element type, got %d\n", obj->type);
err = -EIO;
goto fail_cmd;
}
len = obj->buffer.length;
break;
/* Shouldn't get here! */
default:
dev_err(huawei->dev, "Unexpected obj type, got: %d\n", obj->type);
err = -EIO;
goto fail_cmd;
}
if (!*obj->buffer.pointer)
break;
}
err = (*obj->buffer.pointer) ? -ENODEV : 0;
if (buf) {
len = min(buflen, len);
memcpy(buf, obj->buffer.pointer, len);
}
fail_cmd:
kfree(out.pointer);
return err;
}
static void huawei_wmi_process_key(struct wmi_device *wdev, int code)
/* LEDs */
static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
/* This is a workaround until the "legacy" interface is implemented. */
if (quirks && quirks->ec_micmute) {
char *acpi_method;
acpi_handle handle;
acpi_status status;
union acpi_object args[3];
struct acpi_object_list arg_list = {
.pointer = args,
.count = ARRAY_SIZE(args),
};
handle = ec_get_handle();
if (!handle)
return -ENODEV;
args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
args[1].integer.value = 0x04;
if (acpi_has_method(handle, "SPIN")) {
acpi_method = "SPIN";
args[0].integer.value = 0;
args[2].integer.value = brightness ? 1 : 0;
} else if (acpi_has_method(handle, "WPIN")) {
acpi_method = "WPIN";
args[0].integer.value = 1;
args[2].integer.value = brightness ? 0 : 1;
} else {
return -ENODEV;
}
status = acpi_evaluate_object(handle, acpi_method, &arg_list, NULL);
if (ACPI_FAILURE(status))
return -ENODEV;
return 0;
} else {
union hwmi_arg arg;
arg.cmd = MICMUTE_LED_SET;
arg.args[2] = brightness;
return huawei_wmi_cmd(arg.cmd, NULL, 0);
}
}
static void huawei_wmi_leds_setup(struct device *dev)
{
struct huawei_wmi *huawei = dev_get_drvdata(dev);
huawei->cdev.name = "platform::micmute";
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;
devm_led_classdev_register(dev, &huawei->cdev);
}
/* Battery protection */
static int huawei_wmi_battery_get(int *start, int *end)
{
u8 ret[0x100];
int err, i;
err = huawei_wmi_cmd(BATTERY_THRESH_GET, ret, 0x100);
if (err)
return err;
/* Find the last two non-zero values. Return status is ignored. */
i = 0xff;
do {
if (start)
*start = ret[i-1];
if (end)
*end = ret[i];
} while (i > 2 && !ret[i--]);
return 0;
}
static int huawei_wmi_battery_set(int start, int end)
{
union hwmi_arg arg;
int err;
if (start < 0 || end < 0 || start > 100 || end > 100)
return -EINVAL;
arg.cmd = BATTERY_THRESH_SET;
arg.args[2] = start;
arg.args[3] = end;
/* This is an edge case were some models turn battery protection
* off without changing their thresholds values. We clear the
* values before turning off protection. Sometimes we need a sleep delay to
* make sure these values make their way to EC memory.
*/
if (quirks && quirks->battery_reset && start == 0 && end == 100) {
err = huawei_wmi_battery_set(0, 0);
if (err)
return err;
msleep(1000);
}
err = huawei_wmi_cmd(arg.cmd, NULL, 0);
return err;
}
static ssize_t charge_control_start_threshold_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int err, start;
err = huawei_wmi_battery_get(&start, NULL);
if (err)
return err;
return sprintf(buf, "%d\n", start);
}
static ssize_t charge_control_end_threshold_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int err, end;
err = huawei_wmi_battery_get(NULL, &end);
if (err)
return err;
return sprintf(buf, "%d\n", end);
}
static ssize_t charge_control_thresholds_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int err, start, end;
err = huawei_wmi_battery_get(&start, &end);
if (err)
return err;
return sprintf(buf, "%d %d\n", start, end);
}
static ssize_t charge_control_start_threshold_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int err, start, end;
err = huawei_wmi_battery_get(NULL, &end);
if (err)
return err;
if (sscanf(buf, "%d", &start) != 1)
return -EINVAL;
err = huawei_wmi_battery_set(start, end);
if (err)
return err;
return size;
}
static ssize_t charge_control_end_threshold_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int err, start, end;
err = huawei_wmi_battery_get(&start, NULL);
if (err)
return err;
if (sscanf(buf, "%d", &end) != 1)
return -EINVAL;
err = huawei_wmi_battery_set(start, end);
if (err)
return err;
return size;
}
static ssize_t charge_control_thresholds_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int err, start, end;
if (sscanf(buf, "%d %d", &start, &end) != 2)
return -EINVAL;
err = huawei_wmi_battery_set(start, end);
if (err)
return err;
return size;
}
static DEVICE_ATTR_RW(charge_control_start_threshold);
static DEVICE_ATTR_RW(charge_control_end_threshold);
static DEVICE_ATTR_RW(charge_control_thresholds);
static int huawei_wmi_battery_add(struct power_supply *battery)
{
device_create_file(&battery->dev, &dev_attr_charge_control_start_threshold);
device_create_file(&battery->dev, &dev_attr_charge_control_end_threshold);
return 0;
}
static int huawei_wmi_battery_remove(struct power_supply *battery)
{
device_remove_file(&battery->dev, &dev_attr_charge_control_start_threshold);
device_remove_file(&battery->dev, &dev_attr_charge_control_end_threshold);
return 0;
}
static struct acpi_battery_hook huawei_wmi_battery_hook = {
.add_battery = huawei_wmi_battery_add,
.remove_battery = huawei_wmi_battery_remove,
.name = "Huawei Battery Extension"
};
static void huawei_wmi_battery_setup(struct device *dev)
{
struct huawei_wmi *huawei = dev_get_drvdata(dev);
huawei->battery_available = true;
if (huawei_wmi_battery_get(NULL, NULL)) {
huawei->battery_available = false;
return;
}
battery_hook_register(&huawei_wmi_battery_hook);
device_create_file(dev, &dev_attr_charge_control_thresholds);
}
static void huawei_wmi_battery_exit(struct device *dev)
{
struct huawei_wmi *huawei = dev_get_drvdata(dev);
if (huawei->battery_available) {
battery_hook_unregister(&huawei_wmi_battery_hook);
device_remove_file(dev, &dev_attr_charge_control_thresholds);
}
}
/* Fn lock */
static int huawei_wmi_fn_lock_get(int *on)
{
u8 ret[0x100] = { 0 };
int err, i;
err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
if (err)
return err;
/* Find the first non-zero value. Return status is ignored. */
i = 1;
do {
if (on)
*on = ret[i] - 1; // -1 undefined, 0 off, 1 on.
} while (i < 0xff && !ret[i++]);
return 0;
}
static int huawei_wmi_fn_lock_set(int on)
{
union hwmi_arg arg;
arg.cmd = FN_LOCK_SET;
arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
return huawei_wmi_cmd(arg.cmd, NULL, 0);
}
static ssize_t fn_lock_state_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int err, on;
err = huawei_wmi_fn_lock_get(&on);
if (err)
return err;
return sprintf(buf, "%d\n", on);
}
static ssize_t fn_lock_state_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int on, err;
if (kstrtoint(buf, 10, &on) ||
on < 0 || on > 1)
return -EINVAL;
err = huawei_wmi_fn_lock_set(on);
if (err)
return err;
return size;
}
static DEVICE_ATTR_RW(fn_lock_state);
static void huawei_wmi_fn_lock_setup(struct device *dev)
{
struct huawei_wmi *huawei = dev_get_drvdata(dev);
huawei->fn_lock_available = true;
if (huawei_wmi_fn_lock_get(NULL)) {
huawei->fn_lock_available = false;
return;
}
device_create_file(dev, &dev_attr_fn_lock_state);
}
static void huawei_wmi_fn_lock_exit(struct device *dev)
{
struct huawei_wmi *huawei = dev_get_drvdata(dev);
if (huawei->fn_lock_available)
device_remove_file(dev, &dev_attr_fn_lock_state);
}
/* debugfs */
static void huawei_wmi_debugfs_call_dump(struct seq_file *m, void *data,
union acpi_object *obj)
{
struct huawei_wmi *huawei = m->private;
int i;
switch (obj->type) {
case ACPI_TYPE_INTEGER:
seq_printf(m, "0x%llx", obj->integer.value);
break;
case ACPI_TYPE_STRING:
seq_printf(m, "\"%.*s\"", obj->string.length, obj->string.pointer);
break;
case ACPI_TYPE_BUFFER:
seq_puts(m, "{");
for (i = 0; i < obj->buffer.length; i++) {
seq_printf(m, "0x%02x", obj->buffer.pointer[i]);
if (i < obj->buffer.length - 1)
seq_puts(m, ",");
}
seq_puts(m, "}");
break;
case ACPI_TYPE_PACKAGE:
seq_puts(m, "[");
for (i = 0; i < obj->package.count; i++) {
huawei_wmi_debugfs_call_dump(m, huawei, &obj->package.elements[i]);
if (i < obj->package.count - 1)
seq_puts(m, ",");
}
seq_puts(m, "]");
break;
default:
dev_err(huawei->dev, "Unexpected obj type, got %d\n", obj->type);
return;
}
}
static int huawei_wmi_debugfs_call_show(struct seq_file *m, void *data)
{
struct huawei_wmi *huawei = m->private;
struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_buffer in;
union acpi_object *obj;
int err;
in.length = sizeof(u64);
in.pointer = &huawei->debug.arg;
err = huawei_wmi_call(huawei, &in, &out);
if (err)
return err;
obj = out.pointer;
if (!obj) {
err = -EIO;
goto fail_debugfs_call;
}
huawei_wmi_debugfs_call_dump(m, huawei, obj);
fail_debugfs_call:
kfree(out.pointer);
return err;
}
DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call);
static void huawei_wmi_debugfs_setup(struct device *dev)
{
struct huawei_wmi *huawei = dev_get_drvdata(dev);
huawei->debug.root = debugfs_create_dir("huawei-wmi", NULL);
debugfs_create_x64("arg", 0644, huawei->debug.root,
&huawei->debug.arg);
debugfs_create_file("call", 0400,
huawei->debug.root, huawei, &huawei_wmi_debugfs_call_fops);
}
static void huawei_wmi_debugfs_exit(struct device *dev)
{
struct huawei_wmi *huawei = dev_get_drvdata(dev);
debugfs_remove_recursive(huawei->debug.root);
}
/* Input */
static void huawei_wmi_process_key(struct input_dev *idev, int code)
{
struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
const struct key_entry *key;
/*
@ -127,81 +713,187 @@ static void huawei_wmi_process_key(struct wmi_device *wdev, int code)
kfree(response.pointer);
}
key = sparse_keymap_entry_from_scancode(priv->idev, code);
key = sparse_keymap_entry_from_scancode(idev, code);
if (!key) {
dev_info(&wdev->dev, "Unknown key pressed, code: 0x%04x\n", code);
dev_info(&idev->dev, "Unknown key pressed, code: 0x%04x\n", code);
return;
}
sparse_keymap_report_entry(priv->idev, key, 1, true);
if (quirks && !quirks->report_brightness &&
(key->sw.code == KEY_BRIGHTNESSDOWN ||
key->sw.code == KEY_BRIGHTNESSUP))
return;
sparse_keymap_report_entry(idev, key, 1, true);
}
static void huawei_wmi_notify(struct wmi_device *wdev,
union acpi_object *obj)
static void huawei_wmi_input_notify(u32 value, void *context)
{
if (obj->type == ACPI_TYPE_INTEGER)
huawei_wmi_process_key(wdev, obj->integer.value);
struct input_dev *idev = (struct input_dev *)context;
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
status = wmi_get_event_data(value, &response);
if (ACPI_FAILURE(status)) {
dev_err(&idev->dev, "Unable to get event data\n");
return;
}
obj = (union acpi_object *)response.pointer;
if (obj && obj->type == ACPI_TYPE_INTEGER)
huawei_wmi_process_key(idev, obj->integer.value);
else
dev_info(&wdev->dev, "Bad response type %d\n", obj->type);
dev_err(&idev->dev, "Bad response type\n");
kfree(response.pointer);
}
static int huawei_wmi_input_setup(struct wmi_device *wdev)
static int huawei_wmi_input_setup(struct device *dev,
const char *guid,
struct input_dev **idev)
{
struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
int err;
priv->idev = devm_input_allocate_device(&wdev->dev);
if (!priv->idev)
*idev = devm_input_allocate_device(dev);
if (!*idev)
return -ENOMEM;
priv->idev->name = "Huawei WMI hotkeys";
priv->idev->phys = "wmi/input0";
priv->idev->id.bustype = BUS_HOST;
priv->idev->dev.parent = &wdev->dev;
(*idev)->name = "Huawei WMI hotkeys";
(*idev)->phys = "wmi/input0";
(*idev)->id.bustype = BUS_HOST;
(*idev)->dev.parent = dev;
err = sparse_keymap_setup(priv->idev, huawei_wmi_keymap, NULL);
if (err)
return err;
return input_register_device(priv->idev);
return sparse_keymap_setup(*idev, huawei_wmi_keymap, NULL) ||
input_register_device(*idev) ||
wmi_install_notify_handler(guid, huawei_wmi_input_notify,
*idev);
}
static int huawei_wmi_probe(struct wmi_device *wdev, const void *context)
static void huawei_wmi_input_exit(struct device *dev, const char *guid)
{
struct huawei_wmi_priv *priv;
int err;
priv = devm_kzalloc(&wdev->dev, sizeof(struct huawei_wmi_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
dev_set_drvdata(&wdev->dev, priv);
err = huawei_wmi_input_setup(wdev);
if (err)
return err;
return huawei_wmi_leds_setup(wdev);
wmi_remove_notify_handler(guid);
}
static const struct wmi_device_id huawei_wmi_id_table[] = {
/* Huawei driver */
static const struct wmi_device_id huawei_wmi_events_id_table[] = {
{ .guid_string = WMI0_EVENT_GUID },
{ .guid_string = AMW0_EVENT_GUID },
{ .guid_string = HWMI_EVENT_GUID },
{ }
};
static struct wmi_driver huawei_wmi_driver = {
static int huawei_wmi_probe(struct platform_device *pdev)
{
const struct wmi_device_id *guid = huawei_wmi_events_id_table;
int err;
platform_set_drvdata(pdev, huawei_wmi);
huawei_wmi->dev = &pdev->dev;
while (*guid->guid_string) {
struct input_dev *idev = *huawei_wmi->idev;
if (wmi_has_guid(guid->guid_string)) {
err = huawei_wmi_input_setup(&pdev->dev, guid->guid_string, &idev);
if (err) {
dev_err(&pdev->dev, "Failed to setup input on %s\n", guid->guid_string);
return err;
}
}
idev++;
guid++;
}
if (wmi_has_guid(HWMI_METHOD_GUID)) {
mutex_init(&huawei_wmi->wmi_lock);
huawei_wmi_leds_setup(&pdev->dev);
huawei_wmi_fn_lock_setup(&pdev->dev);
huawei_wmi_battery_setup(&pdev->dev);
huawei_wmi_debugfs_setup(&pdev->dev);
}
return 0;
}
static int huawei_wmi_remove(struct platform_device *pdev)
{
const struct wmi_device_id *guid = huawei_wmi_events_id_table;
while (*guid->guid_string) {
if (wmi_has_guid(guid->guid_string))
huawei_wmi_input_exit(&pdev->dev, guid->guid_string);
guid++;
}
if (wmi_has_guid(HWMI_METHOD_GUID)) {
huawei_wmi_debugfs_exit(&pdev->dev);
huawei_wmi_battery_exit(&pdev->dev);
huawei_wmi_fn_lock_exit(&pdev->dev);
}
return 0;
}
static struct platform_driver huawei_wmi_driver = {
.driver = {
.name = "huawei-wmi",
},
.id_table = huawei_wmi_id_table,
.probe = huawei_wmi_probe,
.notify = huawei_wmi_notify,
.remove = huawei_wmi_remove,
};
module_wmi_driver(huawei_wmi_driver);
static __init int huawei_wmi_init(void)
{
struct platform_device *pdev;
int err;
MODULE_DEVICE_TABLE(wmi, huawei_wmi_id_table);
huawei_wmi = kzalloc(sizeof(struct huawei_wmi), GFP_KERNEL);
if (!huawei_wmi)
return -ENOMEM;
quirks = &quirk_unknown;
dmi_check_system(huawei_quirks);
if (battery_reset != -1)
quirks->battery_reset = battery_reset;
if (report_brightness != -1)
quirks->report_brightness = report_brightness;
err = platform_driver_register(&huawei_wmi_driver);
if (err)
goto pdrv_err;
pdev = platform_device_register_simple("huawei-wmi", -1, NULL, 0);
if (IS_ERR(pdev)) {
err = PTR_ERR(pdev);
goto pdev_err;
}
return 0;
pdev_err:
platform_driver_unregister(&huawei_wmi_driver);
pdrv_err:
kfree(huawei_wmi);
return err;
}
static __exit void huawei_wmi_exit(void)
{
struct platform_device *pdev = to_platform_device(huawei_wmi->dev);
platform_device_unregister(pdev);
platform_driver_unregister(&huawei_wmi_driver);
kfree(huawei_wmi);
}
module_init(huawei_wmi_init);
module_exit(huawei_wmi_exit);
MODULE_ALIAS("wmi:"HWMI_METHOD_GUID);
MODULE_DEVICE_TABLE(wmi, huawei_wmi_events_id_table);
MODULE_AUTHOR("Ayman Bagabas <ayman.bagabas@gmail.com>");
MODULE_DESCRIPTION("Huawei WMI hotkeys");
MODULE_DESCRIPTION("Huawei WMI laptop extras driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Common code for Intel Cherry Trail ACPI INT33FE pseudo device drivers
* (USB Micro-B and Type-C connector variants).
*
* Copyright (c) 2019 Yauhen Kharuzhy <jekhor@gmail.com>
*/
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "intel_cht_int33fe_common.h"
#define EXPECTED_PTYPE 4
static int cht_int33fe_i2c_res_filter(struct acpi_resource *ares, void *data)
{
struct acpi_resource_i2c_serialbus *sb;
int *count = data;
if (i2c_acpi_get_i2c_resource(ares, &sb))
(*count)++;
return 1;
}
static int cht_int33fe_count_i2c_clients(struct device *dev)
{
struct acpi_device *adev;
LIST_HEAD(resource_list);
int count = 0;
adev = ACPI_COMPANION(dev);
if (!adev)
return -EINVAL;
acpi_dev_get_resources(adev, &resource_list,
cht_int33fe_i2c_res_filter, &count);
acpi_dev_free_resource_list(&resource_list);
return count;
}
static int cht_int33fe_check_hw_type(struct device *dev)
{
unsigned long long ptyp;
acpi_status status;
int ret;
status = acpi_evaluate_integer(ACPI_HANDLE(dev), "PTYP", NULL, &ptyp);
if (ACPI_FAILURE(status)) {
dev_err(dev, "Error getting PTYPE\n");
return -ENODEV;
}
/*
* The same ACPI HID is used for different configurations check PTYP
* to ensure that we are dealing with the expected config.
*/
if (ptyp != EXPECTED_PTYPE)
return -ENODEV;
/* Check presence of INT34D3 (hardware-rev 3) expected for ptype == 4 */
if (!acpi_dev_present("INT34D3", "1", 3)) {
dev_err(dev, "Error PTYPE == %d, but no INT34D3 device\n",
EXPECTED_PTYPE);
return -ENODEV;
}
ret = cht_int33fe_count_i2c_clients(dev);
if (ret < 0)
return ret;
switch (ret) {
case 2:
return INT33FE_HW_MICROB;
case 4:
return INT33FE_HW_TYPEC;
default:
return -ENODEV;
}
}
static int cht_int33fe_probe(struct platform_device *pdev)
{
struct cht_int33fe_data *data;
struct device *dev = &pdev->dev;
int ret;
ret = cht_int33fe_check_hw_type(dev);
if (ret < 0)
return ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->dev = dev;
switch (ret) {
case INT33FE_HW_MICROB:
data->probe = cht_int33fe_microb_probe;
data->remove = cht_int33fe_microb_remove;
break;
case INT33FE_HW_TYPEC:
data->probe = cht_int33fe_typec_probe;
data->remove = cht_int33fe_typec_remove;
break;
}
platform_set_drvdata(pdev, data);
return data->probe(data);
}
static int cht_int33fe_remove(struct platform_device *pdev)
{
struct cht_int33fe_data *data = platform_get_drvdata(pdev);
return data->remove(data);
}
static const struct acpi_device_id cht_int33fe_acpi_ids[] = {
{ "INT33FE", },
{ }
};
MODULE_DEVICE_TABLE(acpi, cht_int33fe_acpi_ids);
static struct platform_driver cht_int33fe_driver = {
.driver = {
.name = "Intel Cherry Trail ACPI INT33FE driver",
.acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids),
},
.probe = cht_int33fe_probe,
.remove = cht_int33fe_remove,
};
module_platform_driver(cht_int33fe_driver);
MODULE_DESCRIPTION("Intel Cherry Trail ACPI INT33FE pseudo device driver");
MODULE_AUTHOR("Yauhen Kharuzhy <jekhor@gmail.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Common code for Intel Cherry Trail ACPI INT33FE pseudo device drivers
* (USB Micro-B and Type-C connector variants), header file
*
* Copyright (c) 2019 Yauhen Kharuzhy <jekhor@gmail.com>
*/
#ifndef _INTEL_CHT_INT33FE_COMMON_H
#define _INTEL_CHT_INT33FE_COMMON_H
#include <linux/device.h>
#include <linux/fwnode.h>
#include <linux/i2c.h>
enum int33fe_hw_type {
INT33FE_HW_MICROB,
INT33FE_HW_TYPEC,
};
struct cht_int33fe_data {
struct device *dev;
int (*probe)(struct cht_int33fe_data *data);
int (*remove)(struct cht_int33fe_data *data);
struct i2c_client *battery_fg;
/* Type-C only */
struct i2c_client *fusb302;
struct i2c_client *pi3usb30532;
struct fwnode_handle *dp;
};
int cht_int33fe_microb_probe(struct cht_int33fe_data *data);
int cht_int33fe_microb_remove(struct cht_int33fe_data *data);
int cht_int33fe_typec_probe(struct cht_int33fe_data *data);
int cht_int33fe_typec_remove(struct cht_int33fe_data *data);
#endif /* _INTEL_CHT_INT33FE_COMMON_H */

View File

@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel Cherry Trail ACPI INT33FE pseudo device driver for devices with
* USB Micro-B connector (e.g. without of FUSB302 USB Type-C controller)
*
* Copyright (C) 2019 Yauhen Kharuzhy <jekhor@gmail.com>
*
* At least one Intel Cherry Trail based device which ship with Windows 10
* (Lenovo YogaBook YB1-X91L/F tablet), have this weird INT33FE ACPI device
* with a CRS table with 2 I2cSerialBusV2 resources, for 2 different chips
* attached to various i2c busses:
* 1. The Whiskey Cove PMIC, which is also described by the INT34D3 ACPI device
* 2. TI BQ27542 Fuel Gauge Controller
*
* So this driver is a stub / pseudo driver whose only purpose is to
* instantiate i2c-client for battery fuel gauge, so that standard i2c driver
* for these chip can bind to the it.
*/
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/usb/pd.h>
#include "intel_cht_int33fe_common.h"
static const char * const bq27xxx_suppliers[] = { "bq25890-charger" };
static const struct property_entry bq27xxx_props[] = {
PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq27xxx_suppliers),
{ }
};
int cht_int33fe_microb_probe(struct cht_int33fe_data *data)
{
struct device *dev = data->dev;
struct i2c_board_info board_info;
memset(&board_info, 0, sizeof(board_info));
strscpy(board_info.type, "bq27542", ARRAY_SIZE(board_info.type));
board_info.dev_name = "bq27542";
board_info.properties = bq27xxx_props;
data->battery_fg = i2c_acpi_new_device(dev, 1, &board_info);
return PTR_ERR_OR_ZERO(data->battery_fg);
}
int cht_int33fe_microb_remove(struct cht_int33fe_data *data)
{
i2c_unregister_device(data->battery_fg);
return 0;
}

View File

@ -17,17 +17,15 @@
* for these chips can bind to the them.
*/
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/usb/pd.h>
#define EXPECTED_PTYPE 4
#include "intel_cht_int33fe_common.h"
enum {
INT33FE_NODE_FUSB302,
@ -38,14 +36,6 @@ enum {
INT33FE_NODE_MAX,
};
struct cht_int33fe_data {
struct i2c_client *max17047;
struct i2c_client *fusb302;
struct i2c_client *pi3usb30532;
struct fwnode_handle *dp;
};
static const struct software_node nodes[];
static const struct software_node_ref_args pi3usb30532_ref = {
@ -251,43 +241,20 @@ cht_int33fe_register_max17047(struct device *dev, struct cht_int33fe_data *data)
strlcpy(board_info.type, "max17047", I2C_NAME_SIZE);
board_info.dev_name = "max17047";
board_info.fwnode = fwnode;
data->max17047 = i2c_acpi_new_device(dev, 1, &board_info);
data->battery_fg = i2c_acpi_new_device(dev, 1, &board_info);
return PTR_ERR_OR_ZERO(data->max17047);
return PTR_ERR_OR_ZERO(data->battery_fg);
}
static int cht_int33fe_probe(struct platform_device *pdev)
int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
{
struct device *dev = &pdev->dev;
struct device *dev = data->dev;
struct i2c_board_info board_info;
struct cht_int33fe_data *data;
struct fwnode_handle *fwnode;
struct regulator *regulator;
unsigned long long ptyp;
acpi_status status;
int fusb302_irq;
int ret;
status = acpi_evaluate_integer(ACPI_HANDLE(dev), "PTYP", NULL, &ptyp);
if (ACPI_FAILURE(status)) {
dev_err(dev, "Error getting PTYPE\n");
return -ENODEV;
}
/*
* The same ACPI HID is used for different configurations check PTYP
* to ensure that we are dealing with the expected config.
*/
if (ptyp != EXPECTED_PTYPE)
return -ENODEV;
/* Check presence of INT34D3 (hardware-rev 3) expected for ptype == 4 */
if (!acpi_dev_present("INT34D3", "1", 3)) {
dev_err(dev, "Error PTYPE == %d, but no INT34D3 device\n",
EXPECTED_PTYPE);
return -ENODEV;
}
/*
* We expect the WC PMIC to be paired with a TI bq24292i charger-IC.
* We check for the bq24292i vbus regulator here, this has 2 purposes:
@ -317,10 +284,6 @@ static int cht_int33fe_probe(struct platform_device *pdev)
return fusb302_irq;
}
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
ret = cht_int33fe_add_nodes(data);
if (ret)
return ret;
@ -365,15 +328,13 @@ static int cht_int33fe_probe(struct platform_device *pdev)
goto out_unregister_fusb302;
}
platform_set_drvdata(pdev, data);
return 0;
out_unregister_fusb302:
i2c_unregister_device(data->fusb302);
out_unregister_max17047:
i2c_unregister_device(data->max17047);
i2c_unregister_device(data->battery_fg);
out_remove_nodes:
cht_int33fe_remove_nodes(data);
@ -381,36 +342,13 @@ static int cht_int33fe_probe(struct platform_device *pdev)
return ret;
}
static int cht_int33fe_remove(struct platform_device *pdev)
int cht_int33fe_typec_remove(struct cht_int33fe_data *data)
{
struct cht_int33fe_data *data = platform_get_drvdata(pdev);
i2c_unregister_device(data->pi3usb30532);
i2c_unregister_device(data->fusb302);
i2c_unregister_device(data->max17047);
i2c_unregister_device(data->battery_fg);
cht_int33fe_remove_nodes(data);
return 0;
}
static const struct acpi_device_id cht_int33fe_acpi_ids[] = {
{ "INT33FE", },
{ }
};
MODULE_DEVICE_TABLE(acpi, cht_int33fe_acpi_ids);
static struct platform_driver cht_int33fe_driver = {
.driver = {
.name = "Intel Cherry Trail ACPI INT33FE driver",
.acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids),
},
.probe = cht_int33fe_probe,
.remove = cht_int33fe_remove,
};
module_platform_driver(cht_int33fe_driver);
MODULE_DESCRIPTION("Intel Cherry Trail ACPI INT33FE pseudo device driver");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_LICENSE("GPL v2");

View File

@ -164,8 +164,8 @@ static int int0002_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct x86_cpu_id *cpu_id;
struct irq_chip *irq_chip;
struct gpio_chip *chip;
struct gpio_irq_chip *girq;
int irq, ret;
/* Menlow has a different INT0002 device? <sigh> */
@ -192,15 +192,11 @@ static int int0002_probe(struct platform_device *pdev)
chip->ngpio = GPE0A_PME_B0_VIRT_GPIO_PIN + 1;
chip->irq.init_valid_mask = int0002_init_irq_valid_mask;
ret = devm_gpiochip_add_data(&pdev->dev, chip, NULL);
if (ret) {
dev_err(dev, "Error adding gpio chip: %d\n", ret);
return ret;
}
/*
* We manually request the irq here instead of passing a flow-handler
* We directly request the irq here instead of passing a flow-handler
* to gpiochip_set_chained_irqchip, because the irq is shared.
* FIXME: augment this if we managed to pull handling of shared
* IRQs into gpiolib.
*/
ret = devm_request_irq(dev, irq, int0002_irq,
IRQF_SHARED, "INT0002", chip);
@ -209,17 +205,21 @@ static int int0002_probe(struct platform_device *pdev)
return ret;
}
irq_chip = (struct irq_chip *)cpu_id->driver_data;
girq = &chip->irq;
girq->chip = (struct irq_chip *)cpu_id->driver_data;
/* This let us handle the parent IRQ in the driver */
girq->parent_handler = NULL;
girq->num_parents = 0;
girq->parents = NULL;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_edge_irq;
ret = gpiochip_irqchip_add(chip, irq_chip, 0, handle_edge_irq,
IRQ_TYPE_NONE);
ret = devm_gpiochip_add_data(dev, chip, NULL);
if (ret) {
dev_err(dev, "Error adding irqchip: %d\n", ret);
dev_err(dev, "Error adding gpio chip: %d\n", ret);
return ret;
}
gpiochip_set_chained_irqchip(chip, irq_chip, irq, NULL);
device_init_wakeup(dev, true);
return 0;
}

View File

@ -158,8 +158,9 @@ static const struct pmc_reg_map spt_reg_map = {
.pm_vric1_offset = SPT_PMC_VRIC1_OFFSET,
};
/* Cannonlake: PGD PFET Enable Ack Status Register(s) bitmap */
/* Cannon Lake: PGD PFET Enable Ack Status Register(s) bitmap */
static const struct pmc_bit_map cnp_pfear_map[] = {
/* Reserved for Cannon Lake but valid for Comet Lake */
{"PMC", BIT(0)},
{"OPI-DMI", BIT(1)},
{"SPI/eSPI", BIT(2)},
@ -185,7 +186,7 @@ static const struct pmc_bit_map cnp_pfear_map[] = {
{"SDX", BIT(4)},
{"SPE", BIT(5)},
{"Fuse", BIT(6)},
/* Reserved for Cannonlake but valid for Icelake */
/* Reserved for Cannon Lake but valid for Ice Lake and Comet Lake */
{"SBR8", BIT(7)},
{"CSME_FSC", BIT(0)},
@ -229,12 +230,12 @@ static const struct pmc_bit_map cnp_pfear_map[] = {
{"HDA_PGD4", BIT(2)},
{"HDA_PGD5", BIT(3)},
{"HDA_PGD6", BIT(4)},
/* Reserved for Cannonlake but valid for Icelake */
/* Reserved for Cannon Lake but valid for Ice Lake and Comet Lake */
{"PSF6", BIT(5)},
{"PSF7", BIT(6)},
{"PSF8", BIT(7)},
/* Icelake generation onwards only */
/* Ice Lake generation onwards only */
{"RES_65", BIT(0)},
{"RES_66", BIT(1)},
{"RES_67", BIT(2)},
@ -324,7 +325,7 @@ static const struct pmc_bit_map cnp_ltr_show_map[] = {
{"ISH", CNP_PMC_LTR_ISH},
{"UFSX2", CNP_PMC_LTR_UFSX2},
{"EMMC", CNP_PMC_LTR_EMMC},
/* Reserved for Cannonlake but valid for Icelake */
/* Reserved for Cannon Lake but valid for Ice Lake */
{"WIGIG", ICL_PMC_LTR_WIGIG},
/* Below two cannot be used for LTR_IGNORE */
{"CURRENT_PLATFORM", CNP_PMC_LTR_CUR_PLT},
@ -813,6 +814,8 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = {
INTEL_CPU_FAM6(CANNONLAKE_L, cnp_reg_map),
INTEL_CPU_FAM6(ICELAKE_L, icl_reg_map),
INTEL_CPU_FAM6(ICELAKE_NNPI, icl_reg_map),
INTEL_CPU_FAM6(COMETLAKE, cnp_reg_map),
INTEL_CPU_FAM6(COMETLAKE_L, cnp_reg_map),
{}
};
@ -871,8 +874,8 @@ static int pmc_core_probe(struct platform_device *pdev)
pmcdev->map = (struct pmc_reg_map *)cpu_id->driver_data;
/*
* Coffeelake has CPU ID of Kabylake and Cannonlake PCH. So here
* Sunrisepoint PCH regmap can't be used. Use Cannonlake PCH regmap
* Coffee Lake has CPU ID of Kaby Lake and Cannon Lake PCH. So here
* Sunrisepoint PCH regmap can't be used. Use Cannon Lake PCH regmap
* in this case.
*/
if (pmcdev->map == &spt_reg_map && !pci_dev_present(pmc_pci_ids))

View File

@ -224,7 +224,6 @@ static irqreturn_t intel_punit_ioc(int irq, void *dev_id)
static int intel_punit_get_bars(struct platform_device *pdev)
{
struct resource *res;
void __iomem *addr;
/*
@ -232,14 +231,12 @@ static int intel_punit_get_bars(struct platform_device *pdev)
* - BIOS_IPC BASE_DATA
* - BIOS_IPC BASE_IFACE
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
addr = devm_ioremap_resource(&pdev->dev, res);
addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(addr))
return PTR_ERR(addr);
punit_ipcdev->base[BIOS_IPC][BASE_DATA] = addr;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
addr = devm_ioremap_resource(&pdev->dev, res);
addr = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(addr))
return PTR_ERR(addr);
punit_ipcdev->base[BIOS_IPC][BASE_IFACE] = addr;
@ -251,33 +248,21 @@ static int intel_punit_get_bars(struct platform_device *pdev)
* - GTDRIVER_IPC BASE_DATA
* - GTDRIVER_IPC BASE_IFACE
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
if (res) {
addr = devm_ioremap_resource(&pdev->dev, res);
if (!IS_ERR(addr))
punit_ipcdev->base[ISPDRIVER_IPC][BASE_DATA] = addr;
}
addr = devm_platform_ioremap_resource(pdev, 2);
if (!IS_ERR(addr))
punit_ipcdev->base[ISPDRIVER_IPC][BASE_DATA] = addr;
res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
if (res) {
addr = devm_ioremap_resource(&pdev->dev, res);
if (!IS_ERR(addr))
punit_ipcdev->base[ISPDRIVER_IPC][BASE_IFACE] = addr;
}
addr = devm_platform_ioremap_resource(pdev, 3);
if (!IS_ERR(addr))
punit_ipcdev->base[ISPDRIVER_IPC][BASE_IFACE] = addr;
res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
if (res) {
addr = devm_ioremap_resource(&pdev->dev, res);
if (!IS_ERR(addr))
punit_ipcdev->base[GTDRIVER_IPC][BASE_DATA] = addr;
}
addr = devm_platform_ioremap_resource(pdev, 4);
if (!IS_ERR(addr))
punit_ipcdev->base[GTDRIVER_IPC][BASE_DATA] = addr;
res = platform_get_resource(pdev, IORESOURCE_MEM, 5);
if (res) {
addr = devm_ioremap_resource(&pdev->dev, res);
if (!IS_ERR(addr))
punit_ipcdev->base[GTDRIVER_IPC][BASE_IFACE] = addr;
}
addr = devm_platform_ioremap_resource(pdev, 5);
if (!IS_ERR(addr))
punit_ipcdev->base[GTDRIVER_IPC][BASE_IFACE] = addr;
return 0;
}
@ -309,14 +294,13 @@ static int intel_punit_ipc_probe(struct platform_device *pdev)
ret = intel_punit_get_bars(pdev);
if (ret)
goto out;
return ret;
punit_ipcdev->dev = &pdev->dev;
mutex_init(&punit_ipcdev->lock);
init_completion(&punit_ipcdev->cmd_complete);
out:
return ret;
return 0;
}
static int intel_punit_ipc_remove(struct platform_device *pdev)

View File

@ -6,7 +6,7 @@
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/input-polldev.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/module.h>
@ -18,8 +18,7 @@
MODULE_ALIAS("wmi:"PEAQ_DOLBY_BUTTON_GUID);
static unsigned int peaq_ignore_events_counter;
static struct input_polled_dev *peaq_poll_dev;
static struct input_dev *peaq_poll_dev;
/*
* The Dolby button (yes really a Dolby button) causes an ACPI variable to get
@ -28,8 +27,10 @@ static struct input_polled_dev *peaq_poll_dev;
* (if polling after the release) or twice (polling between press and release).
* We ignore events for 0.5s after the first event to avoid reporting 2 presses.
*/
static void peaq_wmi_poll(struct input_polled_dev *dev)
static void peaq_wmi_poll(struct input_dev *input_dev)
{
static unsigned long last_event_time;
static bool had_events;
union acpi_object obj;
acpi_status status;
u32 dummy = 0;
@ -44,22 +45,25 @@ static void peaq_wmi_poll(struct input_polled_dev *dev)
return;
if (obj.type != ACPI_TYPE_INTEGER) {
dev_err(&peaq_poll_dev->input->dev,
dev_err(&input_dev->dev,
"Error WMBC did not return an integer\n");
return;
}
if (peaq_ignore_events_counter && peaq_ignore_events_counter--)
if (!obj.integer.value)
return;
if (obj.integer.value) {
input_event(peaq_poll_dev->input, EV_KEY, KEY_SOUND, 1);
input_sync(peaq_poll_dev->input);
input_event(peaq_poll_dev->input, EV_KEY, KEY_SOUND, 0);
input_sync(peaq_poll_dev->input);
peaq_ignore_events_counter = max(1u,
PEAQ_POLL_IGNORE_MS / peaq_poll_dev->poll_interval);
}
if (had_events && time_before(jiffies, last_event_time +
msecs_to_jiffies(PEAQ_POLL_IGNORE_MS)))
return;
input_event(input_dev, EV_KEY, KEY_SOUND, 1);
input_sync(input_dev);
input_event(input_dev, EV_KEY, KEY_SOUND, 0);
input_sync(input_dev);
last_event_time = jiffies;
had_events = true;
}
/* Some other devices (Shuttle XS35) use the same WMI GUID for other purposes */
@ -75,6 +79,8 @@ static const struct dmi_system_id peaq_dmi_table[] __initconst = {
static int __init peaq_wmi_init(void)
{
int err;
/* WMI GUID is not unique, also check for a DMI match */
if (!dmi_check_system(peaq_dmi_table))
return -ENODEV;
@ -82,24 +88,36 @@ static int __init peaq_wmi_init(void)
if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID))
return -ENODEV;
peaq_poll_dev = input_allocate_polled_device();
peaq_poll_dev = input_allocate_device();
if (!peaq_poll_dev)
return -ENOMEM;
peaq_poll_dev->poll = peaq_wmi_poll;
peaq_poll_dev->poll_interval = PEAQ_POLL_INTERVAL_MS;
peaq_poll_dev->poll_interval_max = PEAQ_POLL_MAX_MS;
peaq_poll_dev->input->name = "PEAQ WMI hotkeys";
peaq_poll_dev->input->phys = "wmi/input0";
peaq_poll_dev->input->id.bustype = BUS_HOST;
input_set_capability(peaq_poll_dev->input, EV_KEY, KEY_SOUND);
peaq_poll_dev->name = "PEAQ WMI hotkeys";
peaq_poll_dev->phys = "wmi/input0";
peaq_poll_dev->id.bustype = BUS_HOST;
input_set_capability(peaq_poll_dev, EV_KEY, KEY_SOUND);
return input_register_polled_device(peaq_poll_dev);
err = input_setup_polling(peaq_poll_dev, peaq_wmi_poll);
if (err)
goto err_out;
input_set_poll_interval(peaq_poll_dev, PEAQ_POLL_INTERVAL_MS);
input_set_max_poll_interval(peaq_poll_dev, PEAQ_POLL_MAX_MS);
err = input_register_device(peaq_poll_dev);
if (err)
goto err_out;
return 0;
err_out:
input_free_device(peaq_poll_dev);
return err;
}
static void __exit peaq_wmi_exit(void)
{
input_unregister_polled_device(peaq_poll_dev);
input_unregister_device(peaq_poll_dev);
}
module_init(peaq_wmi_init);

View File

@ -0,0 +1,384 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* System76 ACPI Driver
*
* Copyright (C) 2019 System76
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/pci_ids.h>
#include <linux/types.h>
struct system76_data {
struct acpi_device *acpi_dev;
struct led_classdev ap_led;
struct led_classdev kb_led;
enum led_brightness kb_brightness;
enum led_brightness kb_toggle_brightness;
int kb_color;
};
static const struct acpi_device_id device_ids[] = {
{"17761776", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, device_ids);
// Array of keyboard LED brightness levels
static const enum led_brightness kb_levels[] = {
48,
72,
96,
144,
192,
255
};
// Array of keyboard LED colors in 24-bit RGB format
static const int kb_colors[] = {
0xFFFFFF,
0x0000FF,
0xFF0000,
0xFF00FF,
0x00FF00,
0x00FFFF,
0xFFFF00
};
// Get a System76 ACPI device value by name
static int system76_get(struct system76_data *data, char *method)
{
acpi_handle handle;
acpi_status status;
unsigned long long ret = 0;
handle = acpi_device_handle(data->acpi_dev);
status = acpi_evaluate_integer(handle, method, NULL, &ret);
if (ACPI_SUCCESS(status))
return (int)ret;
else
return -1;
}
// Set a System76 ACPI device value by name
static int system76_set(struct system76_data *data, char *method, int value)
{
union acpi_object obj;
struct acpi_object_list obj_list;
acpi_handle handle;
acpi_status status;
obj.type = ACPI_TYPE_INTEGER;
obj.integer.value = value;
obj_list.count = 1;
obj_list.pointer = &obj;
handle = acpi_device_handle(data->acpi_dev);
status = acpi_evaluate_object(handle, method, &obj_list, NULL);
if (ACPI_SUCCESS(status))
return 0;
else
return -1;
}
// Get the airplane mode LED brightness
static enum led_brightness ap_led_get(struct led_classdev *led)
{
struct system76_data *data;
int value;
data = container_of(led, struct system76_data, ap_led);
value = system76_get(data, "GAPL");
if (value > 0)
return (enum led_brightness)value;
else
return LED_OFF;
}
// Set the airplane mode LED brightness
static void ap_led_set(struct led_classdev *led, enum led_brightness value)
{
struct system76_data *data;
data = container_of(led, struct system76_data, ap_led);
system76_set(data, "SAPL", value == LED_OFF ? 0 : 1);
}
// Get the last set keyboard LED brightness
static enum led_brightness kb_led_get(struct led_classdev *led)
{
struct system76_data *data;
data = container_of(led, struct system76_data, kb_led);
return data->kb_brightness;
}
// Set the keyboard LED brightness
static void kb_led_set(struct led_classdev *led, enum led_brightness value)
{
struct system76_data *data;
data = container_of(led, struct system76_data, kb_led);
data->kb_brightness = value;
system76_set(data, "SKBL", (int)data->kb_brightness);
}
// Get the last set keyboard LED color
static ssize_t kb_led_color_show(
struct device *dev,
struct device_attribute *dev_attr,
char *buf)
{
struct led_classdev *led;
struct system76_data *data;
led = (struct led_classdev *)dev->driver_data;
data = container_of(led, struct system76_data, kb_led);
return sprintf(buf, "%06X\n", data->kb_color);
}
// Set the keyboard LED color
static ssize_t kb_led_color_store(
struct device *dev,
struct device_attribute *dev_attr,
const char *buf,
size_t size)
{
struct led_classdev *led;
struct system76_data *data;
unsigned int val;
int ret;
led = (struct led_classdev *)dev->driver_data;
data = container_of(led, struct system76_data, kb_led);
ret = kstrtouint(buf, 16, &val);
if (ret)
return ret;
if (val > 0xFFFFFF)
return -EINVAL;
data->kb_color = (int)val;
system76_set(data, "SKBC", data->kb_color);
return size;
}
static const struct device_attribute kb_led_color_dev_attr = {
.attr = {
.name = "color",
.mode = 0644,
},
.show = kb_led_color_show,
.store = kb_led_color_store,
};
// Notify that the keyboard LED was changed by hardware
static void kb_led_notify(struct system76_data *data)
{
led_classdev_notify_brightness_hw_changed(
&data->kb_led,
data->kb_brightness
);
}
// Read keyboard LED brightness as set by hardware
static void kb_led_hotkey_hardware(struct system76_data *data)
{
int value;
value = system76_get(data, "GKBL");
if (value < 0)
return;
data->kb_brightness = value;
kb_led_notify(data);
}
// Toggle the keyboard LED
static void kb_led_hotkey_toggle(struct system76_data *data)
{
if (data->kb_brightness > 0) {
data->kb_toggle_brightness = data->kb_brightness;
kb_led_set(&data->kb_led, 0);
} else {
kb_led_set(&data->kb_led, data->kb_toggle_brightness);
}
kb_led_notify(data);
}
// Decrease the keyboard LED brightness
static void kb_led_hotkey_down(struct system76_data *data)
{
int i;
if (data->kb_brightness > 0) {
for (i = ARRAY_SIZE(kb_levels); i > 0; i--) {
if (kb_levels[i - 1] < data->kb_brightness) {
kb_led_set(&data->kb_led, kb_levels[i - 1]);
break;
}
}
} else {
kb_led_set(&data->kb_led, data->kb_toggle_brightness);
}
kb_led_notify(data);
}
// Increase the keyboard LED brightness
static void kb_led_hotkey_up(struct system76_data *data)
{
int i;
if (data->kb_brightness > 0) {
for (i = 0; i < ARRAY_SIZE(kb_levels); i++) {
if (kb_levels[i] > data->kb_brightness) {
kb_led_set(&data->kb_led, kb_levels[i]);
break;
}
}
} else {
kb_led_set(&data->kb_led, data->kb_toggle_brightness);
}
kb_led_notify(data);
}
// Cycle the keyboard LED color
static void kb_led_hotkey_color(struct system76_data *data)
{
int i;
if (data->kb_color < 0)
return;
if (data->kb_brightness > 0) {
for (i = 0; i < ARRAY_SIZE(kb_colors); i++) {
if (kb_colors[i] == data->kb_color)
break;
}
i += 1;
if (i >= ARRAY_SIZE(kb_colors))
i = 0;
data->kb_color = kb_colors[i];
system76_set(data, "SKBC", data->kb_color);
} else {
kb_led_set(&data->kb_led, data->kb_toggle_brightness);
}
kb_led_notify(data);
}
// Handle ACPI notification
static void system76_notify(struct acpi_device *acpi_dev, u32 event)
{
struct system76_data *data;
data = acpi_driver_data(acpi_dev);
switch (event) {
case 0x80:
kb_led_hotkey_hardware(data);
break;
case 0x81:
kb_led_hotkey_toggle(data);
break;
case 0x82:
kb_led_hotkey_down(data);
break;
case 0x83:
kb_led_hotkey_up(data);
break;
case 0x84:
kb_led_hotkey_color(data);
break;
}
}
// Add a System76 ACPI device
static int system76_add(struct acpi_device *acpi_dev)
{
struct system76_data *data;
int err;
data = devm_kzalloc(&acpi_dev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
acpi_dev->driver_data = data;
data->acpi_dev = acpi_dev;
err = system76_get(data, "INIT");
if (err)
return err;
data->ap_led.name = "system76_acpi::airplane";
data->ap_led.flags = LED_CORE_SUSPENDRESUME;
data->ap_led.brightness_get = ap_led_get;
data->ap_led.brightness_set = ap_led_set;
data->ap_led.max_brightness = 1;
data->ap_led.default_trigger = "rfkill-none";
err = devm_led_classdev_register(&acpi_dev->dev, &data->ap_led);
if (err)
return err;
data->kb_led.name = "system76_acpi::kbd_backlight";
data->kb_led.flags = LED_BRIGHT_HW_CHANGED | LED_CORE_SUSPENDRESUME;
data->kb_led.brightness_get = kb_led_get;
data->kb_led.brightness_set = kb_led_set;
if (acpi_has_method(acpi_device_handle(data->acpi_dev), "SKBC")) {
data->kb_led.max_brightness = 255;
data->kb_toggle_brightness = 72;
data->kb_color = 0xffffff;
system76_set(data, "SKBC", data->kb_color);
} else {
data->kb_led.max_brightness = 5;
data->kb_color = -1;
}
err = devm_led_classdev_register(&acpi_dev->dev, &data->kb_led);
if (err)
return err;
if (data->kb_color >= 0) {
err = device_create_file(
data->kb_led.dev,
&kb_led_color_dev_attr
);
if (err)
return err;
}
return 0;
}
// Remove a System76 ACPI device
static int system76_remove(struct acpi_device *acpi_dev)
{
struct system76_data *data;
data = acpi_driver_data(acpi_dev);
if (data->kb_color >= 0)
device_remove_file(data->kb_led.dev, &kb_led_color_dev_attr);
devm_led_classdev_unregister(&acpi_dev->dev, &data->ap_led);
devm_led_classdev_unregister(&acpi_dev->dev, &data->kb_led);
system76_get(data, "FINI");
return 0;
}
static struct acpi_driver system76_driver = {
.name = "System76 ACPI Driver",
.class = "hotkey",
.ids = device_ids,
.ops = {
.add = system76_add,
.remove = system76_remove,
.notify = system76_notify,
},
};
module_acpi_driver(system76_driver);
MODULE_DESCRIPTION("System76 ACPI Driver");
MODULE_AUTHOR("Jeremy Soller <jeremy@system76.com>");
MODULE_LICENSE("GPL");

View File

@ -310,6 +310,22 @@ static const struct ts_dmi_data jumper_ezpad_6_pro_b_data = {
.properties = jumper_ezpad_6_pro_b_props,
};
static const struct property_entry jumper_ezpad_6_m4_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 35),
PROPERTY_ENTRY_U32("touchscreen-min-y", 15),
PROPERTY_ENTRY_U32("touchscreen-size-x", 1950),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1525),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-jumper-ezpad-6-m4.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
static const struct ts_dmi_data jumper_ezpad_6_m4_data = {
.acpi_name = "MSSL1680:00",
.properties = jumper_ezpad_6_m4_props,
};
static const struct property_entry jumper_ezpad_mini3_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 23),
PROPERTY_ENTRY_U32("touchscreen-min-y", 16),
@ -498,6 +514,24 @@ static const struct ts_dmi_data pov_mobii_wintab_p1006w_v10_data = {
.properties = pov_mobii_wintab_p1006w_v10_props,
};
static const struct property_entry schneider_sct101ctm_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1715),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name",
"gsl1680-schneider-sct101ctm.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
static const struct ts_dmi_data schneider_sct101ctm_data = {
.acpi_name = "MSSL1680:00",
.properties = schneider_sct101ctm_props,
};
static const struct property_entry teclast_x3_plus_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1980),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1500),
@ -788,6 +822,16 @@ static const struct dmi_system_id touchscreen_dmi_table[] = {
DMI_MATCH(DMI_BIOS_DATE, "04/24/2018"),
},
},
{
/* Jumper EZpad 6 m4 */
.driver_data = (void *)&jumper_ezpad_6_m4_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "jumper"),
DMI_MATCH(DMI_PRODUCT_NAME, "EZpad"),
/* Jumper8.S106x.A00C.1066 with the version dropped */
DMI_MATCH(DMI_BIOS_VERSION, "Jumper8.S106x"),
},
},
{
/* Jumper EZpad mini3 */
.driver_data = (void *)&jumper_ezpad_mini3_data,
@ -908,6 +952,14 @@ static const struct dmi_system_id touchscreen_dmi_table[] = {
DMI_EXACT_MATCH(DMI_BOARD_NAME, "0E57"),
},
},
{
/* Schneider SCT101CTM */
.driver_data = (void *)&schneider_sct101ctm_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Default string"),
DMI_MATCH(DMI_PRODUCT_NAME, "SCT101CTM"),
},
},
{
/* Teclast X3 Plus */
.driver_data = (void *)&teclast_x3_plus_data,

View File

@ -2,7 +2,7 @@
/*
* gov_bang_bang.c - A simple thermal throttling governor using hysteresis
*
* Copyright (C) 2014 Peter Feuerer <peter@piie.net>
* Copyright (C) 2014 Peter Kaestle <peter@piie.net>
*
* Based on step_wise.c with following Copyrights:
* Copyright (C) 2012 Intel Corp

File diff suppressed because it is too large Load Diff

View File

@ -13,8 +13,14 @@ int isst_get_ctdp_levels(int cpu, struct isst_pkg_ctdp *pkg_dev)
ret = isst_send_mbox_command(cpu, CONFIG_TDP,
CONFIG_TDP_GET_LEVELS_INFO, 0, 0, &resp);
if (ret)
return ret;
if (ret) {
pkg_dev->levels = 0;
pkg_dev->locked = 1;
pkg_dev->current_level = 0;
pkg_dev->version = 0;
pkg_dev->enabled = 0;
return 0;
}
debug_printf("cpu:%d CONFIG_TDP_GET_LEVELS_INFO resp:%x\n", cpu, resp);
@ -95,6 +101,69 @@ int isst_get_pwr_info(int cpu, int config_index,
return 0;
}
void isst_get_uncore_p0_p1_info(int cpu, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
unsigned int resp;
int ret;
ret = isst_send_mbox_command(cpu, CONFIG_TDP,
CONFIG_TDP_GET_UNCORE_P0_P1_INFO, 0,
config_index, &resp);
if (ret) {
ctdp_level->uncore_p0 = 0;
ctdp_level->uncore_p1 = 0;
return;
}
ctdp_level->uncore_p0 = resp & GENMASK(7, 0);
ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8;
debug_printf(
"cpu:%d ctdp:%d CONFIG_TDP_GET_UNCORE_P0_P1_INFO resp:%x uncore p0:%d uncore p1:%d\n",
cpu, config_index, resp, ctdp_level->uncore_p0,
ctdp_level->uncore_p1);
}
void isst_get_p1_info(int cpu, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
unsigned int resp;
int ret;
ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_GET_P1_INFO, 0,
config_index, &resp);
if (ret) {
ctdp_level->sse_p1 = 0;
ctdp_level->avx2_p1 = 0;
ctdp_level->avx512_p1 = 0;
return;
}
ctdp_level->sse_p1 = resp & GENMASK(7, 0);
ctdp_level->avx2_p1 = (resp & GENMASK(15, 8)) >> 8;
ctdp_level->avx512_p1 = (resp & GENMASK(23, 16)) >> 16;
debug_printf(
"cpu:%d ctdp:%d CONFIG_TDP_GET_P1_INFO resp:%x sse_p1:%d avx2_p1:%d avx512_p1:%d\n",
cpu, config_index, resp, ctdp_level->sse_p1,
ctdp_level->avx2_p1, ctdp_level->avx512_p1);
}
void isst_get_uncore_mem_freq(int cpu, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
unsigned int resp;
int ret;
ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_GET_MEM_FREQ,
0, config_index, &resp);
if (ret) {
ctdp_level->mem_freq = 0;
return;
}
ctdp_level->mem_freq = resp & GENMASK(7, 0);
debug_printf(
"cpu:%d ctdp:%d CONFIG_TDP_GET_MEM_FREQ resp:%x uncore mem_freq:%d\n",
cpu, config_index, resp, ctdp_level->mem_freq);
}
int isst_get_tjmax_info(int cpu, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
@ -149,6 +218,27 @@ int isst_get_coremask_info(int cpu, int config_index,
return 0;
}
int isst_get_get_trl_from_msr(int cpu, int *trl)
{
unsigned long long msr_trl;
int ret;
ret = isst_send_msr_command(cpu, 0x1AD, 0, &msr_trl);
if (ret)
return ret;
trl[0] = msr_trl & GENMASK(7, 0);
trl[1] = (msr_trl & GENMASK(15, 8)) >> 8;
trl[2] = (msr_trl & GENMASK(23, 16)) >> 16;
trl[3] = (msr_trl & GENMASK(31, 24)) >> 24;
trl[4] = (msr_trl & GENMASK(39, 32)) >> 32;
trl[5] = (msr_trl & GENMASK(47, 40)) >> 40;
trl[6] = (msr_trl & GENMASK(55, 48)) >> 48;
trl[7] = (msr_trl & GENMASK(63, 56)) >> 56;
return 0;
}
int isst_get_get_trl(int cpu, int level, int avx_level, int *trl)
{
unsigned int req, resp;
@ -245,12 +335,15 @@ int isst_set_tdp_level(int cpu, int tdp_level)
int isst_get_pbf_info(int cpu, int level, struct isst_pbf_info *pbf_info)
{
int i, ret, core_cnt, max;
unsigned int req, resp;
int i, ret;
pbf_info->core_cpumask_size = alloc_cpu_set(&pbf_info->core_cpumask);
for (i = 0; i < 2; ++i) {
core_cnt = get_core_count(get_physical_package_id(cpu), get_physical_die_id(cpu));
max = core_cnt > 32 ? 2 : 1;
for (i = 0; i < max; ++i) {
unsigned long long mask;
int count;
@ -258,7 +351,7 @@ int isst_get_pbf_info(int cpu, int level, struct isst_pbf_info *pbf_info)
CONFIG_TDP_PBF_GET_CORE_MASK_INFO,
0, (i << 8) | level, &resp);
if (ret)
return ret;
break;
debug_printf(
"cpu:%d CONFIG_TDP_PBF_GET_CORE_MASK_INFO resp:%x\n",
@ -323,7 +416,7 @@ int isst_set_pbf_fact_status(int cpu, int pbf, int enable)
ret = isst_get_ctdp_levels(cpu, &pkg_dev);
if (ret)
return ret;
debug_printf("cpu:%d No support for dynamic ISST\n", cpu);
current_level = pkg_dev.current_level;
@ -553,7 +646,6 @@ int isst_get_process_ctdp(int cpu, int tdp_level, struct isst_pkg_ctdp *pkg_dev)
i);
ctdp_level = &pkg_dev->ctdp_level[i];
ctdp_level->processed = 1;
ctdp_level->level = i;
ctdp_level->control_cpu = cpu;
ctdp_level->pkg_id = get_physical_package_id(cpu);
@ -561,7 +653,37 @@ int isst_get_process_ctdp(int cpu, int tdp_level, struct isst_pkg_ctdp *pkg_dev)
ret = isst_get_ctdp_control(cpu, i, ctdp_level);
if (ret)
return ret;
continue;
pkg_dev->processed = 1;
ctdp_level->processed = 1;
if (ctdp_level->pbf_support) {
ret = isst_get_pbf_info(cpu, i, &ctdp_level->pbf_info);
if (!ret)
ctdp_level->pbf_found = 1;
}
if (ctdp_level->fact_support) {
ret = isst_get_fact_info(cpu, i,
&ctdp_level->fact_info);
if (ret)
return ret;
}
if (!pkg_dev->enabled) {
int freq;
freq = get_cpufreq_base_freq(cpu);
if (freq > 0) {
ctdp_level->sse_p1 = freq / 100000;
ctdp_level->tdp_ratio = ctdp_level->sse_p1;
}
isst_get_get_trl_from_msr(cpu, ctdp_level->trl_sse_active_cores);
isst_get_trl_bucket_info(cpu, &ctdp_level->buckets_info);
continue;
}
ret = isst_get_tdp_info(cpu, i, ctdp_level);
if (ret)
@ -600,22 +722,11 @@ int isst_get_process_ctdp(int cpu, int tdp_level, struct isst_pkg_ctdp *pkg_dev)
if (ret)
return ret;
if (ctdp_level->pbf_support) {
ret = isst_get_pbf_info(cpu, i, &ctdp_level->pbf_info);
if (!ret)
ctdp_level->pbf_found = 1;
}
if (ctdp_level->fact_support) {
ret = isst_get_fact_info(cpu, i,
&ctdp_level->fact_info);
if (ret)
return ret;
}
isst_get_uncore_p0_p1_info(cpu, i, ctdp_level);
isst_get_p1_info(cpu, i, ctdp_level);
isst_get_uncore_mem_freq(cpu, i, ctdp_level);
}
pkg_dev->processed = 1;
return 0;
}
@ -649,6 +760,27 @@ int isst_pm_qos_config(int cpu, int enable_clos, int priority_type)
unsigned int req, resp;
int ret;
if (!enable_clos) {
struct isst_pkg_ctdp pkg_dev;
struct isst_pkg_ctdp_level_info ctdp_level;
ret = isst_get_ctdp_levels(cpu, &pkg_dev);
if (ret) {
debug_printf("isst_get_ctdp_levels\n");
return ret;
}
ret = isst_get_ctdp_control(cpu, pkg_dev.current_level,
&ctdp_level);
if (ret)
return ret;
if (ctdp_level.fact_enabled) {
debug_printf("Turbo-freq feature must be disabled first\n");
return -EINVAL;
}
}
ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0,
&resp);
if (ret)

View File

@ -6,8 +6,6 @@
#include "isst.h"
#define DISP_FREQ_MULTIPLIER 100
static void printcpulist(int str_len, char *str, int mask_size,
cpu_set_t *cpu_mask)
{
@ -204,6 +202,9 @@ static void _isst_pbf_display_information(int cpu, FILE *outf, int level,
pbf_info->p1_low * DISP_FREQ_MULTIPLIER);
format_and_print(outf, disp_level + 1, header, value);
if (is_clx_n_platform())
return;
snprintf(header, sizeof(header), "tjunction-temperature(C)");
snprintf(value, sizeof(value), "%d", pbf_info->t_prochot);
format_and_print(outf, disp_level + 1, header, value);
@ -314,7 +315,8 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
char value[256];
int i, base_level = 1;
print_package_info(cpu, outf);
if (pkg_dev->processed)
print_package_info(cpu, outf);
for (i = 0; i <= pkg_dev->levels; ++i) {
struct isst_pkg_ctdp_level_info *ctdp_level;
@ -334,27 +336,66 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
snprintf(value, sizeof(value), "%d", j);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "enable-cpu-mask");
printcpumask(sizeof(value), value,
ctdp_level->core_cpumask_size,
ctdp_level->core_cpumask);
format_and_print(outf, base_level + 4, header, value);
if (ctdp_level->core_cpumask_size) {
snprintf(header, sizeof(header), "enable-cpu-mask");
printcpumask(sizeof(value), value,
ctdp_level->core_cpumask_size,
ctdp_level->core_cpumask);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "enable-cpu-list");
printcpulist(sizeof(value), value,
ctdp_level->core_cpumask_size,
ctdp_level->core_cpumask);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "enable-cpu-list");
printcpulist(sizeof(value), value,
ctdp_level->core_cpumask_size,
ctdp_level->core_cpumask);
format_and_print(outf, base_level + 4, header, value);
}
snprintf(header, sizeof(header), "thermal-design-power-ratio");
snprintf(value, sizeof(value), "%d", ctdp_level->tdp_ratio);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "base-frequency(MHz)");
if (!ctdp_level->sse_p1)
ctdp_level->sse_p1 = ctdp_level->tdp_ratio;
snprintf(value, sizeof(value), "%d",
ctdp_level->tdp_ratio * DISP_FREQ_MULTIPLIER);
ctdp_level->sse_p1 * DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 4, header, value);
if (ctdp_level->avx2_p1) {
snprintf(header, sizeof(header), "base-frequency-avx2(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->avx2_p1 * DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 4, header, value);
}
if (ctdp_level->avx512_p1) {
snprintf(header, sizeof(header), "base-frequency-avx512(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->avx512_p1 * DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 4, header, value);
}
if (ctdp_level->uncore_p1) {
snprintf(header, sizeof(header), "uncore-frequency-min(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->uncore_p1 * DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 4, header, value);
}
if (ctdp_level->uncore_p0) {
snprintf(header, sizeof(header), "uncore-frequency-max(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->uncore_p0 * DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 4, header, value);
}
if (ctdp_level->mem_freq) {
snprintf(header, sizeof(header), "mem-frequency(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->mem_freq * DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 4, header, value);
}
snprintf(header, sizeof(header),
"speed-select-turbo-freq");
if (ctdp_level->fact_support) {
@ -377,13 +418,26 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
snprintf(value, sizeof(value), "unsupported");
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "thermal-design-power(W)");
snprintf(value, sizeof(value), "%d", ctdp_level->pkg_tdp);
format_and_print(outf, base_level + 4, header, value);
if (is_clx_n_platform()) {
if (ctdp_level->pbf_support)
_isst_pbf_display_information(cpu, outf,
tdp_level,
&ctdp_level->pbf_info,
base_level + 4);
continue;
}
snprintf(header, sizeof(header), "tjunction-max(C)");
snprintf(value, sizeof(value), "%d", ctdp_level->t_proc_hot);
format_and_print(outf, base_level + 4, header, value);
if (ctdp_level->pkg_tdp) {
snprintf(header, sizeof(header), "thermal-design-power(W)");
snprintf(value, sizeof(value), "%d", ctdp_level->pkg_tdp);
format_and_print(outf, base_level + 4, header, value);
}
if (ctdp_level->t_proc_hot) {
snprintf(header, sizeof(header), "tjunction-max(C)");
snprintf(value, sizeof(value), "%d", ctdp_level->t_proc_hot);
format_and_print(outf, base_level + 4, header, value);
}
snprintf(header, sizeof(header), "turbo-ratio-limits-sse");
format_and_print(outf, base_level + 4, header, NULL);
@ -402,41 +456,41 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 6, header, value);
}
snprintf(header, sizeof(header), "turbo-ratio-limits-avx");
format_and_print(outf, base_level + 4, header, NULL);
for (j = 0; j < 8; ++j) {
snprintf(header, sizeof(header), "bucket-%d", j);
format_and_print(outf, base_level + 5, header, NULL);
snprintf(header, sizeof(header), "core-count");
snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff);
format_and_print(outf, base_level + 6, header, value);
if (ctdp_level->trl_avx_active_cores[0]) {
snprintf(header, sizeof(header), "turbo-ratio-limits-avx2");
format_and_print(outf, base_level + 4, header, NULL);
for (j = 0; j < 8; ++j) {
snprintf(header, sizeof(header), "bucket-%d", j);
format_and_print(outf, base_level + 5, header, NULL);
snprintf(header, sizeof(header),
"max-turbo-frequency(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->trl_avx_active_cores[j] *
DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 6, header, value);
snprintf(header, sizeof(header), "core-count");
snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff);
format_and_print(outf, base_level + 6, header, value);
snprintf(header, sizeof(header), "max-turbo-frequency(MHz)");
snprintf(value, sizeof(value), "%d", ctdp_level->trl_avx_active_cores[j] * DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 6, header, value);
}
}
snprintf(header, sizeof(header), "turbo-ratio-limits-avx512");
format_and_print(outf, base_level + 4, header, NULL);
for (j = 0; j < 8; ++j) {
snprintf(header, sizeof(header), "bucket-%d", j);
format_and_print(outf, base_level + 5, header, NULL);
if (ctdp_level->trl_avx_512_active_cores[0]) {
snprintf(header, sizeof(header), "turbo-ratio-limits-avx512");
format_and_print(outf, base_level + 4, header, NULL);
for (j = 0; j < 8; ++j) {
snprintf(header, sizeof(header), "bucket-%d", j);
format_and_print(outf, base_level + 5, header, NULL);
snprintf(header, sizeof(header), "core-count");
snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff);
format_and_print(outf, base_level + 6, header, value);
snprintf(header, sizeof(header), "core-count");
snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff);
format_and_print(outf, base_level + 6, header, value);
snprintf(header, sizeof(header),
"max-turbo-frequency(MHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->trl_avx_512_active_cores[j] *
DISP_FREQ_MULTIPLIER);
snprintf(header, sizeof(header), "max-turbo-frequency(MHz)");
snprintf(value, sizeof(value), "%d", ctdp_level->trl_avx_512_active_cores[j] * DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 6, header, value);
}
}
if (ctdp_level->pbf_support)
_isst_pbf_display_information(cpu, outf, i,
&ctdp_level->pbf_info,
@ -509,15 +563,15 @@ void isst_clos_display_information(int cpu, FILE *outf, int clos,
format_and_print(outf, 5, header, value);
snprintf(header, sizeof(header), "clos-min");
snprintf(value, sizeof(value), "%d", clos_config->clos_min);
snprintf(value, sizeof(value), "%d MHz", clos_config->clos_min * DISP_FREQ_MULTIPLIER);
format_and_print(outf, 5, header, value);
snprintf(header, sizeof(header), "clos-max");
snprintf(value, sizeof(value), "%d", clos_config->clos_max);
snprintf(value, sizeof(value), "%d MHz", clos_config->clos_max * DISP_FREQ_MULTIPLIER);
format_and_print(outf, 5, header, value);
snprintf(header, sizeof(header), "clos-desired");
snprintf(value, sizeof(value), "%d", clos_config->clos_desired);
snprintf(value, sizeof(value), "%d MHz", clos_config->clos_desired * DISP_FREQ_MULTIPLIER);
format_and_print(outf, 5, header, value);
format_and_print(outf, 1, NULL, NULL);

View File

@ -69,6 +69,8 @@
#define PM_CLOS_OFFSET 0x08
#define PQR_ASSOC_OFFSET 0x20
#define DISP_FREQ_MULTIPLIER 100
struct isst_clos_config {
int pkg_id;
int die_id;
@ -161,6 +163,7 @@ struct isst_pkg_ctdp {
extern int get_topo_max_cpus(void);
extern int get_cpu_count(int pkg_id, int die_id);
extern int get_core_count(int pkg_id, int die_id);
/* Common interfaces */
extern void debug_printf(const char *format, ...);
@ -237,4 +240,6 @@ extern void isst_display_result(int cpu, FILE *outf, char *feature, char *cmd,
extern int isst_clos_get_clos_information(int cpu, int *enable, int *type);
extern void isst_clos_display_clos_information(int cpu, FILE *outf,
int clos_enable, int type);
extern int is_clx_n_platform(void);
extern int get_cpufreq_base_freq(int cpu);
#endif