mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-15 16:53:54 +08:00
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:
commit
67b8ed29e0
58
Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
Normal file
58
Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
Normal 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).
|
10
MAINTAINERS
10
MAINTAINERS
@ -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
|
||||
|
@ -44,4 +44,16 @@ config MLXBF_TMFIFO
|
||||
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
|
||||
|
@ -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
|
||||
|
321
drivers/platform/mellanox/mlxbf-bootctl.c
Normal file
321
drivers/platform/mellanox/mlxbf-bootctl.c
Normal 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");
|
103
drivers/platform/mellanox/mlxbf-bootctl.h
Normal file
103
drivers/platform/mellanox/mlxbf-bootctl.h
Normal 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__ */
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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*:");
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
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 ;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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 @@ out:
|
||||
|
||||
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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,17 +82,184 @@ 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 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)
|
||||
{
|
||||
acpi_status status;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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 *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;
|
||||
|
||||
in.length = sizeof(arg);
|
||||
in.pointer = &arg;
|
||||
|
||||
/* 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;
|
||||
|
||||
obj = out.pointer;
|
||||
if (!obj) {
|
||||
err = -EIO;
|
||||
goto fail_cmd;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* LEDs */
|
||||
|
||||
static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
|
||||
/* 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 = {
|
||||
@ -55,55 +267,429 @@ static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
|
||||
.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 (strcmp(priv->acpi_method, "SPIN") == 0) {
|
||||
if (acpi_has_method(handle, "SPIN")) {
|
||||
acpi_method = "SPIN";
|
||||
args[0].integer.value = 0;
|
||||
args[2].integer.value = brightness ? 1 : 0;
|
||||
} else if (strcmp(priv->acpi_method, "WPIN") == 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 -EINVAL;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
status = acpi_evaluate_object(priv->handle, priv->acpi_method, &arg_list, NULL);
|
||||
status = acpi_evaluate_object(handle, acpi_method, &arg_list, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENXIO;
|
||||
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_leds_setup(struct wmi_device *wdev)
|
||||
static int huawei_wmi_battery_set(int start, int end)
|
||||
{
|
||||
struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
union hwmi_arg arg;
|
||||
int err;
|
||||
|
||||
priv->handle = ec_get_handle();
|
||||
if (!priv->handle)
|
||||
return 0;
|
||||
if (start < 0 || end < 0 || start > 100 || end > 100)
|
||||
return -EINVAL;
|
||||
|
||||
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;
|
||||
arg.cmd = BATTERY_THRESH_SET;
|
||||
arg.args[2] = start;
|
||||
arg.args[3] = end;
|
||||
|
||||
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;
|
||||
/* 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;
|
||||
|
||||
return devm_led_classdev_register(&wdev->dev, &priv->cdev);
|
||||
msleep(1000);
|
||||
}
|
||||
|
||||
static void huawei_wmi_process_key(struct wmi_device *wdev, int code)
|
||||
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");
|
||||
|
147
drivers/platform/x86/intel_cht_int33fe_common.c
Normal file
147
drivers/platform/x86/intel_cht_int33fe_common.c
Normal 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");
|
41
drivers/platform/x86/intel_cht_int33fe_common.h
Normal file
41
drivers/platform/x86/intel_cht_int33fe_common.h
Normal 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 */
|
57
drivers/platform/x86/intel_cht_int33fe_microb.c
Normal file
57
drivers/platform/x86/intel_cht_int33fe_microb.c
Normal 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;
|
||||
}
|
@ -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 @@ out_remove_nodes:
|
||||
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");
|
@ -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;
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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)
|
||||
|
@ -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);
|
||||
|
384
drivers/platform/x86/system76_acpi.c
Normal file
384
drivers/platform/x86/system76_acpi.c
Normal 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");
|
@ -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,
|
||||
|
@ -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
@ -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,15 +646,44 @@ 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);
|
||||
ctdp_level->die_id = get_physical_die_id(cpu);
|
||||
|
||||
ret = isst_get_ctdp_control(cpu, i, ctdp_level);
|
||||
if (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;
|
||||
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);
|
||||
}
|
||||
|
||||
if (ctdp_level->fact_support) {
|
||||
ret = isst_get_fact_info(cpu, i,
|
||||
&ctdp_level->fact_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -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,6 +315,7 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
|
||||
char value[256];
|
||||
int i, base_level = 1;
|
||||
|
||||
if (pkg_dev->processed)
|
||||
print_package_info(cpu, outf);
|
||||
|
||||
for (i = 0; i <= pkg_dev->levels; ++i) {
|
||||
@ -334,6 +336,7 @@ 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);
|
||||
|
||||
if (ctdp_level->core_cpumask_size) {
|
||||
snprintf(header, sizeof(header), "enable-cpu-mask");
|
||||
printcpumask(sizeof(value), value,
|
||||
ctdp_level->core_cpumask_size,
|
||||
@ -345,16 +348,54 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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,7 +456,9 @@ 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");
|
||||
|
||||
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);
|
||||
@ -412,14 +468,13 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -430,13 +485,12 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
|
||||
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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user