mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-15 21:23:23 +00:00
platform-drivers-x86 for v6.12-1
Highlights: - asus-wmi: Add support for vivobook fan profiles - dell-laptop: Add knobs to change battery charge settings - lg-laptop: Add operation region support - intel-uncore-freq: Add support for efficiency latency control - intel/ifs: Add SBAF test support - intel/pmc: Ignore all LTRs during suspend - platform/surface: Support for arm64 based Surface devices - wmi: Pass event data directly to legacy notify handlers - x86/platform/geode: switch GPIO buttons and LEDs to software properties - bunch of small cleanups, fixes, hw-id additions, etc. The following is an automated git shortlog grouped by driver: Documentation: - admin-guide: pm: Add efficiency vs. latency tradeoff to uncore documentation ISST: - Simplify isst_misc_reg() and isst_misc_unreg() MAINTAINERS: - adjust file entry in INTEL MID PLATFORM - Add Intel MID section Merge tag 'hwmon-for-v6.11-rc7' into review-hans: - Merge tag 'hwmon-for-v6.11-rc7' into review-hans Merge tag 'platform-drivers-x86-v6.11-3' into review-hans: - Merge tag 'platform-drivers-x86-v6.11-3' into review-hans acer-wmi: - Use backlight power constants asus-laptop: - Use backlight power constants asus-nb-wmi: - Use backlight power constants asus-wmi: - don't fail if platform_profile already registered - add debug print in more key places - Use backlight power constants - add support for vivobook fan profiles dell-laptop: - remove duplicate code w/ battery function - Add knobs to change battery charge settings dt-bindings: - platform: Add Surface System Aggregator Module - serial: Allow embedded-controller as child node eeepc-laptop: - Use backlight power constants eeepc-wmi: - Use backlight power constants fujitsu-laptop: - Use backlight power constants hid-asus: - use hid for brightness control on keyboard ideapad-laptop: - Make the scope_guard() clear of its scope - move ACPI helpers from header to source file - Use backlight power constants int3472: - Use str_high_low() - Use GPIO_LOOKUP() macro - make common part a separate module intel-hid: - Use string_choices API instead of ternary operator intel/pmc: - Ignore all LTRs during suspend - Remove unused param idx from pmc_for_each_mode() intel_scu_ipc: - Move intel_scu_ipc.h out of arch/x86/include/asm intel_scu_wdt: - Move intel_scu_wdt.h to x86 subfolder lenovo-ymc: - Ignore the 0x0 state lg-laptop: - Add operation region support oaktrail: - Use backlight power constants panasonic-laptop: - Add support for programmable buttons platform/mellanox: - mlxbf-pmc: fix lockdep warning platform/olpc: - Remove redundant null pointer checks in olpc_ec_setup_debugfs() platform/surface: - Add OF support platform/x86/amd: - pmf: Add quirk for TUF Gaming A14 platform/x86/amd/pmf: - Update SMU metrics table for 1AH family series - Relocate CPU ID macros to the PMF header - Add support for notifying Smart PC Solution updates platform/x86/intel-uncore-freq: - Add efficiency latency control to sysfs interface - Add support for efficiency latency control - Do not present separate package-die domain platform/x86/intel/ifs: - Fix SBAF title underline length - Add SBAF test support - Add SBAF test image loading support - Refactor MSR usage in IFS test code platform/x86/intel/pmc: - Show live substate requirements platform/x86/intel/pmt: - Use PMT callbacks platform/x86/intel/vsec: - Add PMT read callbacks platform/x86/intel/vsec.h: - Move to include/linux samsung-laptop: - Use backlight power constants serial-multi-instantiate: - Don't require both I2C and SPI thinkpad_acpi: - Fix uninitialized symbol 's' warning - Add Thinkpad Edge E531 fan support touchscreen_dmi: - add nanote-next quirk trace: - platform/x86/intel/ifs: Add SBAF trace support wmi: - Call both legacy and WMI driver notify handlers - Merge get_event_data() with wmi_get_notify_data() - Remove wmi_get_event_data() - Pass event data directly to legacy notify handlers x86-android-tablets: - Adjust Xiaomi Pad 2 bottom bezel touch buttons LED - Fix spelling in the comments x86/platform/geode: - switch GPIO buttons and LEDs to software properties -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEEuvA7XScYQRpenhd+kuxHeUQDJ9wFAmbq2tYUHGhkZWdvZWRl QHJlZGhhdC5jb20ACgkQkuxHeUQDJ9xKYAgAoXZt1MjBDA1mP813i4bj8CYQHWO+ YnugVhEccucxgC6sBGzQeRLBNuG/VaBN6tyJ1pKYMpWV5gSthq1Iop+DZbno2ciM QAnSSzioHB/dhYBXuKmZatkMsKLjLjtfcexUed9DfwKapqFl3XQMb6cEYasM37hH 197K4yAFF3oqQImlACwQDxN1q3eCG6bdIbEAByZW7yH644IC5zH8/CiFjTCwUx/F aFIHQlLLzt1kjhD8AbRHhRcsGbzG2ejHsC3yrQddEJSOkInDO8baR0aDyhBTUFPE lztuekFfaJ1Xcyoc/Zf4pi3ab1Djt+Htck3CHLO/xcl0YYMlM5vcs1QlhQ== =sAk7 -----END PGP SIGNATURE----- Merge tag 'platform-drivers-x86-v6.12-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86 Pull x86 platform drivers updates from Hans de Goede: - asus-wmi: Add support for vivobook fan profiles - dell-laptop: Add knobs to change battery charge settings - lg-laptop: Add operation region support - intel-uncore-freq: Add support for efficiency latency control - intel/ifs: Add SBAF test support - intel/pmc: Ignore all LTRs during suspend - platform/surface: Support for arm64 based Surface devices - wmi: Pass event data directly to legacy notify handlers - x86/platform/geode: switch GPIO buttons and LEDs to software properties - bunch of small cleanups, fixes, hw-id additions, etc. * tag 'platform-drivers-x86-v6.12-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (65 commits) MAINTAINERS: adjust file entry in INTEL MID PLATFORM platform/x86: x86-android-tablets: Adjust Xiaomi Pad 2 bottom bezel touch buttons LED platform/mellanox: mlxbf-pmc: fix lockdep warning platform/x86/amd: pmf: Add quirk for TUF Gaming A14 platform/x86: touchscreen_dmi: add nanote-next quirk platform/x86: asus-wmi: don't fail if platform_profile already registered platform/x86: asus-wmi: add debug print in more key places platform/x86: intel_scu_wdt: Move intel_scu_wdt.h to x86 subfolder platform/x86: intel_scu_ipc: Move intel_scu_ipc.h out of arch/x86/include/asm MAINTAINERS: Add Intel MID section platform/x86: panasonic-laptop: Add support for programmable buttons platform/olpc: Remove redundant null pointer checks in olpc_ec_setup_debugfs() platform/x86: intel/pmc: Ignore all LTRs during suspend platform/x86: wmi: Call both legacy and WMI driver notify handlers platform/x86: wmi: Merge get_event_data() with wmi_get_notify_data() platform/x86: wmi: Remove wmi_get_event_data() platform/x86: wmi: Pass event data directly to legacy notify handlers platform/x86: thinkpad_acpi: Fix uninitialized symbol 's' warning platform/x86: x86-android-tablets: Fix spelling in the comments platform/x86: ideapad-laptop: Make the scope_guard() clear of its scope ...
This commit is contained in:
commit
84bbfe6b64
@ -113,3 +113,62 @@ to apply at each uncore* level.
|
||||
|
||||
Support for "current_freq_khz" is available only at each fabric cluster
|
||||
level (i.e., in uncore* directory).
|
||||
|
||||
Efficiency vs. Latency Tradeoff
|
||||
-------------------------------
|
||||
|
||||
The Efficiency Latency Control (ELC) feature improves performance
|
||||
per watt. With this feature hardware power management algorithms
|
||||
optimize trade-off between latency and power consumption. For some
|
||||
latency sensitive workloads further tuning can be done by SW to
|
||||
get desired performance.
|
||||
|
||||
The hardware monitors the average CPU utilization across all cores
|
||||
in a power domain at regular intervals and decides an uncore frequency.
|
||||
While this may result in the best performance per watt, workload may be
|
||||
expecting higher performance at the expense of power. Consider an
|
||||
application that intermittently wakes up to perform memory reads on an
|
||||
otherwise idle system. In such cases, if hardware lowers uncore
|
||||
frequency, then there may be delay in ramp up of frequency to meet
|
||||
target performance.
|
||||
|
||||
The ELC control defines some parameters which can be changed from SW.
|
||||
If the average CPU utilization is below a user-defined threshold
|
||||
(elc_low_threshold_percent attribute below), the user-defined uncore
|
||||
floor frequency will be used (elc_floor_freq_khz attribute below)
|
||||
instead of hardware calculated minimum.
|
||||
|
||||
Similarly in high load scenario where the CPU utilization goes above
|
||||
the high threshold value (elc_high_threshold_percent attribute below)
|
||||
instead of jumping to maximum uncore frequency, frequency is increased
|
||||
in 100MHz steps. This avoids consuming unnecessarily high power
|
||||
immediately with CPU utilization spikes.
|
||||
|
||||
Attributes for efficiency latency control:
|
||||
|
||||
``elc_floor_freq_khz``
|
||||
This attribute is used to get/set the efficiency latency floor frequency.
|
||||
If this variable is lower than the 'min_freq_khz', it is ignored by
|
||||
the firmware.
|
||||
|
||||
``elc_low_threshold_percent``
|
||||
This attribute is used to get/set the efficiency latency control low
|
||||
threshold. This attribute is in percentages of CPU utilization.
|
||||
|
||||
``elc_high_threshold_percent``
|
||||
This attribute is used to get/set the efficiency latency control high
|
||||
threshold. This attribute is in percentages of CPU utilization.
|
||||
|
||||
``elc_high_threshold_enable``
|
||||
This attribute is used to enable/disable the efficiency latency control
|
||||
high threshold. Write '1' to enable, '0' to disable.
|
||||
|
||||
Example system configuration below, which does following:
|
||||
* when CPU utilization is less than 10%: sets uncore frequency to 800MHz
|
||||
* when CPU utilization is higher than 95%: increases uncore frequency in
|
||||
100MHz steps, until power limit is reached
|
||||
|
||||
elc_floor_freq_khz:800000
|
||||
elc_high_threshold_percent:95
|
||||
elc_high_threshold_enable:1
|
||||
elc_low_threshold_percent:10
|
||||
|
@ -0,0 +1,47 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/platform/microsoft,surface-sam.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Surface System Aggregator Module (SAM, SSAM)
|
||||
|
||||
maintainers:
|
||||
- Konrad Dybcio <konradybcio@kernel.org>
|
||||
|
||||
description: |
|
||||
Surface devices use a standardized embedded controller to let the
|
||||
operating system interface with various hardware functions. The
|
||||
specific functionalities are modeled as subdevices and matched on
|
||||
five levels: domain, category, target, instance and function.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: microsoft,surface-sam
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
current-speed: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
uart {
|
||||
embedded-controller {
|
||||
compatible = "microsoft,surface-sam";
|
||||
|
||||
interrupts-extended = <&tlmm 91 IRQ_TYPE_EDGE_RISING>;
|
||||
|
||||
pinctrl-0 = <&ssam_state>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
current-speed = <4000000>;
|
||||
};
|
||||
};
|
@ -88,7 +88,7 @@ properties:
|
||||
TX FIFO threshold configuration (in bytes).
|
||||
|
||||
patternProperties:
|
||||
"^(bluetooth|bluetooth-gnss|gnss|gps|mcu|onewire)$":
|
||||
"^(bluetooth|bluetooth-gnss|embedded-controller|gnss|gps|mcu|onewire)$":
|
||||
if:
|
||||
type: object
|
||||
then:
|
||||
|
23
MAINTAINERS
23
MAINTAINERS
@ -11552,6 +11552,24 @@ S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-driver-intel-m10-bmc-sec-update
|
||||
F: drivers/fpga/intel-m10-bmc-sec-update.c
|
||||
|
||||
INTEL MID (Mobile Internet Device) PLATFORM
|
||||
M: Andy Shevchenko <andy@kernel.org>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Supported
|
||||
F: arch/x86/include/asm/intel-mid.h
|
||||
F: arch/x86/pci/intel_mid_pci.c
|
||||
F: arch/x86/platform/intel-mid/
|
||||
F: drivers/extcon/extcon-intel-mrfld.c
|
||||
F: drivers/iio/adc/intel_mrfld_adc.c
|
||||
F: drivers/mfd/intel_soc_pmic_mrfld.c
|
||||
F: drivers/platform/x86/intel/mrfld_pwrbtn.c
|
||||
F: drivers/platform/x86/intel_scu_*
|
||||
F: drivers/staging/media/atomisp/
|
||||
F: drivers/watchdog/intel-mid_wdt.c
|
||||
F: include/linux/mfd/intel_soc_pmic_mrfld.h
|
||||
F: include/linux/platform_data/x86/intel-mid_wdt.h
|
||||
F: include/linux/platform_data/x86/intel_scu_ipc.h
|
||||
|
||||
INTEL P-Unit IPC DRIVER
|
||||
M: Zha Qipeng <qipeng.zha@intel.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
@ -11614,8 +11632,8 @@ F: drivers/counter/intel-qep.c
|
||||
INTEL SCU DRIVERS
|
||||
M: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
S: Maintained
|
||||
F: arch/x86/include/asm/intel_scu_ipc.h
|
||||
F: drivers/platform/x86/intel_scu_*
|
||||
F: include/linux/platform_data/x86/intel_scu_ipc.h
|
||||
|
||||
INTEL SDSI DRIVER
|
||||
M: David E. Box <david.e.box@linux.intel.com>
|
||||
@ -11690,7 +11708,8 @@ F: drivers/platform/x86/intel/uncore-frequency/
|
||||
INTEL VENDOR SPECIFIC EXTENDED CAPABILITIES DRIVER
|
||||
M: David E. Box <david.e.box@linux.intel.com>
|
||||
S: Supported
|
||||
F: drivers/platform/x86/intel/vsec.*
|
||||
F: drivers/platform/x86/intel/vsec.c
|
||||
F: include/linux/intel_vsec.h
|
||||
|
||||
INTEL VIRTUAL BUTTON DRIVER
|
||||
M: AceLan Kao <acelan.kao@canonical.com>
|
||||
|
@ -3076,9 +3076,13 @@ config OLPC_XO15_SCI
|
||||
- AC adapter status updates
|
||||
- Battery status updates
|
||||
|
||||
config GEODE_COMMON
|
||||
bool
|
||||
|
||||
config ALIX
|
||||
bool "PCEngines ALIX System Support (LED setup)"
|
||||
select GPIOLIB
|
||||
select GEODE_COMMON
|
||||
help
|
||||
This option enables system support for the PCEngines ALIX.
|
||||
At present this just sets up LEDs for GPIO control on
|
||||
@ -3093,12 +3097,14 @@ config ALIX
|
||||
config NET5501
|
||||
bool "Soekris Engineering net5501 System Support (LEDS, GPIO, etc)"
|
||||
select GPIOLIB
|
||||
select GEODE_COMMON
|
||||
help
|
||||
This option enables system support for the Soekris Engineering net5501.
|
||||
|
||||
config GEOS
|
||||
bool "Traverse Technologies GEOS System Support (LEDS, GPIO, etc)"
|
||||
select GPIOLIB
|
||||
select GEODE_COMMON
|
||||
depends on DMI
|
||||
help
|
||||
This option enables system support for the Traverse Technologies GEOS.
|
||||
|
@ -10,7 +10,7 @@
|
||||
#define TELEM_MAX_EVENTS_SRAM 28
|
||||
#define TELEM_MAX_OS_ALLOCATED_EVENTS 20
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
#include <linux/platform_data/x86/intel_scu_ipc.h>
|
||||
|
||||
enum telemetry_unit {
|
||||
TELEM_PSS = 0,
|
||||
|
@ -247,6 +247,8 @@
|
||||
#define MSR_INTEGRITY_CAPS_ARRAY_BIST BIT(MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT)
|
||||
#define MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT 4
|
||||
#define MSR_INTEGRITY_CAPS_PERIODIC_BIST BIT(MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT)
|
||||
#define MSR_INTEGRITY_CAPS_SBAF_BIT 8
|
||||
#define MSR_INTEGRITY_CAPS_SBAF BIT(MSR_INTEGRITY_CAPS_SBAF_BIT)
|
||||
#define MSR_INTEGRITY_CAPS_SAF_GEN_MASK GENMASK_ULL(10, 9)
|
||||
|
||||
#define MSR_LBR_NHM_FROM 0x00000680
|
||||
|
@ -1,4 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_GEODE_COMMON) += geode-common.o
|
||||
obj-$(CONFIG_ALIX) += alix.o
|
||||
obj-$(CONFIG_NET5501) += net5501.o
|
||||
obj-$(CONFIG_GEOS) += geos.o
|
||||
|
@ -18,15 +18,12 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/dmi.h>
|
||||
|
||||
#include <asm/geode.h>
|
||||
|
||||
#include "geode-common.h"
|
||||
|
||||
#define BIOS_SIGNATURE_TINYBIOS 0xf0000
|
||||
#define BIOS_SIGNATURE_COREBOOT 0x500
|
||||
#define BIOS_REGION_SIZE 0x10000
|
||||
@ -41,79 +38,16 @@ module_param(force, bool, 0444);
|
||||
/* FIXME: Award bios is not automatically detected as Alix platform */
|
||||
MODULE_PARM_DESC(force, "Force detection as ALIX.2/ALIX.3 platform");
|
||||
|
||||
static struct gpio_keys_button alix_gpio_buttons[] = {
|
||||
{
|
||||
.code = KEY_RESTART,
|
||||
.gpio = 24,
|
||||
.active_low = 1,
|
||||
.desc = "Reset button",
|
||||
.type = EV_KEY,
|
||||
.wakeup = 0,
|
||||
.debounce_interval = 100,
|
||||
.can_disable = 0,
|
||||
}
|
||||
};
|
||||
static struct gpio_keys_platform_data alix_buttons_data = {
|
||||
.buttons = alix_gpio_buttons,
|
||||
.nbuttons = ARRAY_SIZE(alix_gpio_buttons),
|
||||
.poll_interval = 20,
|
||||
};
|
||||
|
||||
static struct platform_device alix_buttons_dev = {
|
||||
.name = "gpio-keys-polled",
|
||||
.id = 1,
|
||||
.dev = {
|
||||
.platform_data = &alix_buttons_data,
|
||||
}
|
||||
};
|
||||
|
||||
static struct gpio_led alix_leds[] = {
|
||||
{
|
||||
.name = "alix:1",
|
||||
.default_trigger = "default-on",
|
||||
},
|
||||
{
|
||||
.name = "alix:2",
|
||||
.default_trigger = "default-off",
|
||||
},
|
||||
{
|
||||
.name = "alix:3",
|
||||
.default_trigger = "default-off",
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpio_led_platform_data alix_leds_data = {
|
||||
.num_leds = ARRAY_SIZE(alix_leds),
|
||||
.leds = alix_leds,
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table alix_leds_gpio_table = {
|
||||
.dev_id = "leds-gpio",
|
||||
.table = {
|
||||
/* The Geode GPIOs should be on the CS5535 companion chip */
|
||||
GPIO_LOOKUP_IDX("cs5535-gpio", 6, NULL, 0, GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP_IDX("cs5535-gpio", 25, NULL, 1, GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP_IDX("cs5535-gpio", 27, NULL, 2, GPIO_ACTIVE_LOW),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device alix_leds_dev = {
|
||||
.name = "leds-gpio",
|
||||
.id = -1,
|
||||
.dev.platform_data = &alix_leds_data,
|
||||
};
|
||||
|
||||
static struct platform_device *alix_devs[] __initdata = {
|
||||
&alix_buttons_dev,
|
||||
&alix_leds_dev,
|
||||
static const struct geode_led alix_leds[] __initconst = {
|
||||
{ 6, true },
|
||||
{ 25, false },
|
||||
{ 27, false },
|
||||
};
|
||||
|
||||
static void __init register_alix(void)
|
||||
{
|
||||
/* Setup LED control through leds-gpio driver */
|
||||
gpiod_add_lookup_table(&alix_leds_gpio_table);
|
||||
platform_add_devices(alix_devs, ARRAY_SIZE(alix_devs));
|
||||
geode_create_restart_key(24);
|
||||
geode_create_leds("alix", alix_leds, ARRAY_SIZE(alix_leds));
|
||||
}
|
||||
|
||||
static bool __init alix_present(unsigned long bios_phys,
|
||||
|
178
arch/x86/platform/geode/geode-common.c
Normal file
178
arch/x86/platform/geode/geode-common.c
Normal file
@ -0,0 +1,178 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Shared helpers to register GPIO-connected buttons and LEDs
|
||||
* on AMD Geode boards.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/gpio/property.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "geode-common.h"
|
||||
|
||||
static const struct software_node geode_gpiochip_node = {
|
||||
.name = "cs5535-gpio",
|
||||
};
|
||||
|
||||
static const struct property_entry geode_gpio_keys_props[] = {
|
||||
PROPERTY_ENTRY_U32("poll-interval", 20),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node geode_gpio_keys_node = {
|
||||
.name = "geode-gpio-keys",
|
||||
.properties = geode_gpio_keys_props,
|
||||
};
|
||||
|
||||
static struct property_entry geode_restart_key_props[] = {
|
||||
{ /* Placeholder for GPIO property */ },
|
||||
PROPERTY_ENTRY_U32("linux,code", KEY_RESTART),
|
||||
PROPERTY_ENTRY_STRING("label", "Reset button"),
|
||||
PROPERTY_ENTRY_U32("debounce-interval", 100),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node geode_restart_key_node = {
|
||||
.parent = &geode_gpio_keys_node,
|
||||
.properties = geode_restart_key_props,
|
||||
};
|
||||
|
||||
static const struct software_node *geode_gpio_keys_swnodes[] __initconst = {
|
||||
&geode_gpiochip_node,
|
||||
&geode_gpio_keys_node,
|
||||
&geode_restart_key_node,
|
||||
NULL
|
||||
};
|
||||
|
||||
/*
|
||||
* Creates gpio-keys-polled device for the restart key.
|
||||
*
|
||||
* Note that it needs to be called first, before geode_create_leds(),
|
||||
* because it registers gpiochip software node used by both gpio-keys and
|
||||
* leds-gpio devices.
|
||||
*/
|
||||
int __init geode_create_restart_key(unsigned int pin)
|
||||
{
|
||||
struct platform_device_info keys_info = {
|
||||
.name = "gpio-keys-polled",
|
||||
.id = 1,
|
||||
};
|
||||
struct platform_device *pd;
|
||||
int err;
|
||||
|
||||
geode_restart_key_props[0] = PROPERTY_ENTRY_GPIO("gpios",
|
||||
&geode_gpiochip_node,
|
||||
pin, GPIO_ACTIVE_LOW);
|
||||
|
||||
err = software_node_register_node_group(geode_gpio_keys_swnodes);
|
||||
if (err) {
|
||||
pr_err("failed to register gpio-keys software nodes: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
keys_info.fwnode = software_node_fwnode(&geode_gpio_keys_node);
|
||||
|
||||
pd = platform_device_register_full(&keys_info);
|
||||
err = PTR_ERR_OR_ZERO(pd);
|
||||
if (err) {
|
||||
pr_err("failed to create gpio-keys device: %d\n", err);
|
||||
software_node_unregister_node_group(geode_gpio_keys_swnodes);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct software_node geode_gpio_leds_node = {
|
||||
.name = "geode-leds",
|
||||
};
|
||||
|
||||
#define MAX_LEDS 3
|
||||
|
||||
int __init geode_create_leds(const char *label, const struct geode_led *leds,
|
||||
unsigned int n_leds)
|
||||
{
|
||||
const struct software_node *group[MAX_LEDS + 2] = { 0 };
|
||||
struct software_node *swnodes;
|
||||
struct property_entry *props;
|
||||
struct platform_device_info led_info = {
|
||||
.name = "leds-gpio",
|
||||
.id = PLATFORM_DEVID_NONE,
|
||||
};
|
||||
struct platform_device *led_dev;
|
||||
const char *node_name;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
if (n_leds > MAX_LEDS) {
|
||||
pr_err("%s: too many LEDs\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
swnodes = kcalloc(n_leds, sizeof(*swnodes), GFP_KERNEL);
|
||||
if (!swnodes)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Each LED is represented by 3 properties: "gpios",
|
||||
* "linux,default-trigger", and am empty terminator.
|
||||
*/
|
||||
props = kcalloc(n_leds * 3, sizeof(*props), GFP_KERNEL);
|
||||
if (!props) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_swnodes;
|
||||
}
|
||||
|
||||
group[0] = &geode_gpio_leds_node;
|
||||
for (i = 0; i < n_leds; i++) {
|
||||
node_name = kasprintf(GFP_KERNEL, "%s:%d", label, i);
|
||||
if (!node_name) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_names;
|
||||
}
|
||||
|
||||
props[i * 3 + 0] =
|
||||
PROPERTY_ENTRY_GPIO("gpios", &geode_gpiochip_node,
|
||||
leds[i].pin, GPIO_ACTIVE_LOW);
|
||||
props[i * 3 + 1] =
|
||||
PROPERTY_ENTRY_STRING("linux,default-trigger",
|
||||
leds[i].default_on ?
|
||||
"default-on" : "default-off");
|
||||
/* props[i * 3 + 2] is an empty terminator */
|
||||
|
||||
swnodes[i] = SOFTWARE_NODE(node_name, &props[i * 3],
|
||||
&geode_gpio_leds_node);
|
||||
group[i + 1] = &swnodes[i];
|
||||
}
|
||||
|
||||
err = software_node_register_node_group(group);
|
||||
if (err) {
|
||||
pr_err("failed to register LED software nodes: %d\n", err);
|
||||
goto err_free_names;
|
||||
}
|
||||
|
||||
led_info.fwnode = software_node_fwnode(&geode_gpio_leds_node);
|
||||
|
||||
led_dev = platform_device_register_full(&led_info);
|
||||
err = PTR_ERR_OR_ZERO(led_dev);
|
||||
if (err) {
|
||||
pr_err("failed to create LED device: %d\n", err);
|
||||
goto err_unregister_group;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_group:
|
||||
software_node_unregister_node_group(group);
|
||||
err_free_names:
|
||||
while (--i >= 0)
|
||||
kfree(swnodes[i].name);
|
||||
kfree(props);
|
||||
err_free_swnodes:
|
||||
kfree(swnodes);
|
||||
return err;
|
||||
}
|
21
arch/x86/platform/geode/geode-common.h
Normal file
21
arch/x86/platform/geode/geode-common.h
Normal file
@ -0,0 +1,21 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Shared helpers to register GPIO-connected buttons and LEDs
|
||||
* on AMD Geode boards.
|
||||
*/
|
||||
|
||||
#ifndef __PLATFORM_GEODE_COMMON_H
|
||||
#define __PLATFORM_GEODE_COMMON_H
|
||||
|
||||
#include <linux/property.h>
|
||||
|
||||
struct geode_led {
|
||||
unsigned int pin;
|
||||
bool default_on;
|
||||
};
|
||||
|
||||
int geode_create_restart_key(unsigned int pin);
|
||||
int geode_create_leds(const char *label, const struct geode_led *leds,
|
||||
unsigned int n_leds);
|
||||
|
||||
#endif /* __PLATFORM_GEODE_COMMON_H */
|
@ -16,88 +16,22 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/dmi.h>
|
||||
|
||||
#include <asm/geode.h>
|
||||
|
||||
static struct gpio_keys_button geos_gpio_buttons[] = {
|
||||
{
|
||||
.code = KEY_RESTART,
|
||||
.gpio = 3,
|
||||
.active_low = 1,
|
||||
.desc = "Reset button",
|
||||
.type = EV_KEY,
|
||||
.wakeup = 0,
|
||||
.debounce_interval = 100,
|
||||
.can_disable = 0,
|
||||
}
|
||||
};
|
||||
static struct gpio_keys_platform_data geos_buttons_data = {
|
||||
.buttons = geos_gpio_buttons,
|
||||
.nbuttons = ARRAY_SIZE(geos_gpio_buttons),
|
||||
.poll_interval = 20,
|
||||
};
|
||||
#include "geode-common.h"
|
||||
|
||||
static struct platform_device geos_buttons_dev = {
|
||||
.name = "gpio-keys-polled",
|
||||
.id = 1,
|
||||
.dev = {
|
||||
.platform_data = &geos_buttons_data,
|
||||
}
|
||||
};
|
||||
|
||||
static struct gpio_led geos_leds[] = {
|
||||
{
|
||||
.name = "geos:1",
|
||||
.default_trigger = "default-on",
|
||||
},
|
||||
{
|
||||
.name = "geos:2",
|
||||
.default_trigger = "default-off",
|
||||
},
|
||||
{
|
||||
.name = "geos:3",
|
||||
.default_trigger = "default-off",
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpio_led_platform_data geos_leds_data = {
|
||||
.num_leds = ARRAY_SIZE(geos_leds),
|
||||
.leds = geos_leds,
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table geos_leds_gpio_table = {
|
||||
.dev_id = "leds-gpio",
|
||||
.table = {
|
||||
/* The Geode GPIOs should be on the CS5535 companion chip */
|
||||
GPIO_LOOKUP_IDX("cs5535-gpio", 6, NULL, 0, GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP_IDX("cs5535-gpio", 25, NULL, 1, GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP_IDX("cs5535-gpio", 27, NULL, 2, GPIO_ACTIVE_LOW),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device geos_leds_dev = {
|
||||
.name = "leds-gpio",
|
||||
.id = -1,
|
||||
.dev.platform_data = &geos_leds_data,
|
||||
};
|
||||
|
||||
static struct platform_device *geos_devs[] __initdata = {
|
||||
&geos_buttons_dev,
|
||||
&geos_leds_dev,
|
||||
static const struct geode_led geos_leds[] __initconst = {
|
||||
{ 6, true },
|
||||
{ 25, false },
|
||||
{ 27, false },
|
||||
};
|
||||
|
||||
static void __init register_geos(void)
|
||||
{
|
||||
/* Setup LED control through leds-gpio driver */
|
||||
gpiod_add_lookup_table(&geos_leds_gpio_table);
|
||||
platform_add_devices(geos_devs, ARRAY_SIZE(geos_devs));
|
||||
geode_create_restart_key(3);
|
||||
geode_create_leds("geos", geos_leds, ARRAY_SIZE(geos_leds));
|
||||
}
|
||||
|
||||
static int __init geos_init(void)
|
||||
|
@ -16,80 +16,25 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/gpio/property.h>
|
||||
|
||||
#include <asm/geode.h>
|
||||
|
||||
#include "geode-common.h"
|
||||
|
||||
#define BIOS_REGION_BASE 0xffff0000
|
||||
#define BIOS_REGION_SIZE 0x00010000
|
||||
|
||||
static struct gpio_keys_button net5501_gpio_buttons[] = {
|
||||
{
|
||||
.code = KEY_RESTART,
|
||||
.gpio = 24,
|
||||
.active_low = 1,
|
||||
.desc = "Reset button",
|
||||
.type = EV_KEY,
|
||||
.wakeup = 0,
|
||||
.debounce_interval = 100,
|
||||
.can_disable = 0,
|
||||
}
|
||||
};
|
||||
static struct gpio_keys_platform_data net5501_buttons_data = {
|
||||
.buttons = net5501_gpio_buttons,
|
||||
.nbuttons = ARRAY_SIZE(net5501_gpio_buttons),
|
||||
.poll_interval = 20,
|
||||
};
|
||||
|
||||
static struct platform_device net5501_buttons_dev = {
|
||||
.name = "gpio-keys-polled",
|
||||
.id = 1,
|
||||
.dev = {
|
||||
.platform_data = &net5501_buttons_data,
|
||||
}
|
||||
};
|
||||
|
||||
static struct gpio_led net5501_leds[] = {
|
||||
{
|
||||
.name = "net5501:1",
|
||||
.default_trigger = "default-on",
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpio_led_platform_data net5501_leds_data = {
|
||||
.num_leds = ARRAY_SIZE(net5501_leds),
|
||||
.leds = net5501_leds,
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table net5501_leds_gpio_table = {
|
||||
.dev_id = "leds-gpio",
|
||||
.table = {
|
||||
/* The Geode GPIOs should be on the CS5535 companion chip */
|
||||
GPIO_LOOKUP_IDX("cs5535-gpio", 6, NULL, 0, GPIO_ACTIVE_HIGH),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device net5501_leds_dev = {
|
||||
.name = "leds-gpio",
|
||||
.id = -1,
|
||||
.dev.platform_data = &net5501_leds_data,
|
||||
};
|
||||
|
||||
static struct platform_device *net5501_devs[] __initdata = {
|
||||
&net5501_buttons_dev,
|
||||
&net5501_leds_dev,
|
||||
static const struct geode_led net5501_leds[] __initconst = {
|
||||
{ 6, true },
|
||||
};
|
||||
|
||||
static void __init register_net5501(void)
|
||||
{
|
||||
/* Setup LED control through leds-gpio driver */
|
||||
gpiod_add_lookup_table(&net5501_leds_gpio_table);
|
||||
platform_add_devices(net5501_devs, ARRAY_SIZE(net5501_devs));
|
||||
geode_create_restart_key(24);
|
||||
geode_create_leds("net5501", net5501_leds, ARRAY_SIZE(net5501_leds));
|
||||
}
|
||||
|
||||
struct net5501_board {
|
||||
|
@ -27,9 +27,10 @@
|
||||
#include <asm/intel-mid.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/i8259.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
#include <asm/reboot.h>
|
||||
|
||||
#include <linux/platform_data/x86/intel_scu_ipc.h>
|
||||
|
||||
#define IPCMSG_COLD_OFF 0x80 /* Only for Tangier */
|
||||
#define IPCMSG_COLD_RESET 0xF1
|
||||
|
||||
|
@ -492,12 +492,19 @@ static void asus_kbd_backlight_work(struct work_struct *work)
|
||||
*/
|
||||
static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
|
||||
{
|
||||
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_ASUS_WMI))
|
||||
return false;
|
||||
|
||||
if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD &&
|
||||
dmi_check_system(asus_use_hid_led_dmi_ids)) {
|
||||
hid_info(hdev, "using HID for asus::kbd_backlight\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS,
|
||||
ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value);
|
||||
hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value);
|
||||
|
@ -1597,15 +1597,13 @@ static void hp_wmi_devm_notify_remove(void *ignored)
|
||||
}
|
||||
|
||||
/* hp_wmi_notify - WMI event notification handler */
|
||||
static void hp_wmi_notify(u32 value, void *context)
|
||||
static void hp_wmi_notify(union acpi_object *wobj, void *context)
|
||||
{
|
||||
struct hp_wmi_info *temp_info[HP_WMI_MAX_INSTANCES] = {};
|
||||
struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
struct hp_wmi_sensors *state = context;
|
||||
struct device *dev = &state->wdev->dev;
|
||||
struct hp_wmi_event event = {};
|
||||
struct hp_wmi_info *fan_info;
|
||||
union acpi_object *wobj;
|
||||
acpi_status err;
|
||||
int event_type;
|
||||
u8 count;
|
||||
@ -1630,20 +1628,15 @@ static void hp_wmi_notify(u32 value, void *context)
|
||||
* HPBIOS_BIOSEvent instance.
|
||||
*/
|
||||
|
||||
mutex_lock(&state->lock);
|
||||
|
||||
err = wmi_get_event_data(value, &out);
|
||||
if (ACPI_FAILURE(err))
|
||||
goto out_unlock;
|
||||
|
||||
wobj = out.pointer;
|
||||
if (!wobj)
|
||||
goto out_unlock;
|
||||
return;
|
||||
|
||||
mutex_lock(&state->lock);
|
||||
|
||||
err = populate_event_from_wobj(dev, &event, wobj);
|
||||
if (err) {
|
||||
dev_warn(dev, "Bad event data (ACPI type %d)\n", wobj->type);
|
||||
goto out_free_wobj;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
event_type = classify_event(event.name, event.category);
|
||||
@ -1668,13 +1661,10 @@ static void hp_wmi_notify(u32 value, void *context)
|
||||
break;
|
||||
}
|
||||
|
||||
out_free_wobj:
|
||||
kfree(wobj);
|
||||
|
||||
out_free:
|
||||
devm_kfree(dev, event.name);
|
||||
devm_kfree(dev, event.description);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&state->lock);
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/itco_wdt.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
#include <linux/platform_data/x86/intel_scu_ipc.h>
|
||||
|
||||
/* Residency with clock rate at 19.2MHz to usecs */
|
||||
#define S0IX_RESIDENCY_IN_USECS(d, s) \
|
||||
|
@ -15,8 +15,7 @@
|
||||
#include <linux/mfd/intel_soc_pmic.h>
|
||||
#include <linux/mfd/intel_soc_pmic_bxtwc.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
#include <linux/platform_data/x86/intel_scu_ipc.h>
|
||||
|
||||
/* PMIC device registers */
|
||||
#define REG_ADDR_MASK GENMASK(15, 8)
|
||||
|
@ -12,11 +12,10 @@
|
||||
#include <linux/mfd/intel_soc_pmic.h>
|
||||
#include <linux/mfd/intel_soc_pmic_mrfld.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_data/x86/intel_scu_ipc.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
/*
|
||||
* Level 2 IRQs
|
||||
*
|
||||
|
@ -1774,6 +1774,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_
|
||||
|
||||
/* "event_list" sysfs to list events supported by the block */
|
||||
attr = &pmc->block[blk_num].attr_event_list;
|
||||
sysfs_attr_init(&attr->dev_attr.attr);
|
||||
attr->dev_attr.attr.mode = 0444;
|
||||
attr->dev_attr.show = mlxbf_pmc_event_list_show;
|
||||
attr->nr = blk_num;
|
||||
@ -1787,6 +1788,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_
|
||||
if (strstr(pmc->block_name[blk_num], "l3cache") ||
|
||||
((pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE))) {
|
||||
attr = &pmc->block[blk_num].attr_enable;
|
||||
sysfs_attr_init(&attr->dev_attr.attr);
|
||||
attr->dev_attr.attr.mode = 0644;
|
||||
attr->dev_attr.show = mlxbf_pmc_enable_show;
|
||||
attr->dev_attr.store = mlxbf_pmc_enable_store;
|
||||
@ -1814,6 +1816,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_
|
||||
/* "eventX" and "counterX" sysfs to program and read counter values */
|
||||
for (j = 0; j < pmc->block[blk_num].counters; ++j) {
|
||||
attr = &pmc->block[blk_num].attr_counter[j];
|
||||
sysfs_attr_init(&attr->dev_attr.attr);
|
||||
attr->dev_attr.attr.mode = 0644;
|
||||
attr->dev_attr.show = mlxbf_pmc_counter_show;
|
||||
attr->dev_attr.store = mlxbf_pmc_counter_store;
|
||||
@ -1826,6 +1829,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_
|
||||
attr = NULL;
|
||||
|
||||
attr = &pmc->block[blk_num].attr_event[j];
|
||||
sysfs_attr_init(&attr->dev_attr.attr);
|
||||
attr->dev_attr.attr.mode = 0644;
|
||||
attr->dev_attr.show = mlxbf_pmc_event_show;
|
||||
attr->dev_attr.store = mlxbf_pmc_event_store;
|
||||
@ -1861,6 +1865,7 @@ static int mlxbf_pmc_init_perftype_reg(struct device *dev, unsigned int blk_num)
|
||||
while (count > 0) {
|
||||
--count;
|
||||
attr = &pmc->block[blk_num].attr_event[count];
|
||||
sysfs_attr_init(&attr->dev_attr.attr);
|
||||
attr->dev_attr.attr.mode = 0644;
|
||||
attr->dev_attr.show = mlxbf_pmc_counter_show;
|
||||
attr->dev_attr.store = mlxbf_pmc_counter_store;
|
||||
|
@ -332,9 +332,6 @@ static struct dentry *olpc_ec_setup_debugfs(void)
|
||||
struct dentry *dbgfs_dir;
|
||||
|
||||
dbgfs_dir = debugfs_create_dir("olpc-ec", NULL);
|
||||
if (IS_ERR_OR_NULL(dbgfs_dir))
|
||||
return NULL;
|
||||
|
||||
debugfs_create_file("cmd", 0600, dbgfs_dir, NULL, &ec_dbgfs_ops);
|
||||
|
||||
return dbgfs_dir;
|
||||
|
@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
@ -441,6 +442,7 @@ static int ssam_add_client_device(struct device *parent, struct ssam_controller
|
||||
|
||||
sdev->dev.parent = parent;
|
||||
sdev->dev.fwnode = fwnode_handle_get(node);
|
||||
sdev->dev.of_node = to_of_node(node);
|
||||
|
||||
status = ssam_device_add(sdev);
|
||||
if (status)
|
||||
|
@ -1104,13 +1104,6 @@ int ssam_controller_caps_load_from_acpi(acpi_handle handle,
|
||||
u64 funcs;
|
||||
int status;
|
||||
|
||||
/* Set defaults. */
|
||||
caps->ssh_power_profile = U32_MAX;
|
||||
caps->screen_on_sleep_idle_timeout = U32_MAX;
|
||||
caps->screen_off_sleep_idle_timeout = U32_MAX;
|
||||
caps->d3_closes_handle = false;
|
||||
caps->ssh_buffer_size = U32_MAX;
|
||||
|
||||
/* Pre-load supported DSM functions. */
|
||||
status = ssam_dsm_get_functions(handle, &funcs);
|
||||
if (status)
|
||||
@ -1149,6 +1142,52 @@ int ssam_controller_caps_load_from_acpi(acpi_handle handle,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ssam_controller_caps_load_from_of() - Load controller capabilities from OF/DT.
|
||||
* @dev: A pointer to the controller device
|
||||
* @caps: Where to store the capabilities in.
|
||||
*
|
||||
* Return: Returns zero on success, a negative error code on failure.
|
||||
*/
|
||||
static int ssam_controller_caps_load_from_of(struct device *dev, struct ssam_controller_caps *caps)
|
||||
{
|
||||
/*
|
||||
* Every device starting with Surface Pro X through Laptop 7 uses these
|
||||
* identical values, which makes them good defaults.
|
||||
*/
|
||||
caps->d3_closes_handle = true;
|
||||
caps->screen_on_sleep_idle_timeout = 5000;
|
||||
caps->screen_off_sleep_idle_timeout = 30;
|
||||
caps->ssh_buffer_size = 48;
|
||||
/* TODO: figure out power profile */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ssam_controller_caps_load() - Load controller capabilities
|
||||
* @dev: A pointer to the controller device
|
||||
* @caps: Where to store the capabilities in.
|
||||
*
|
||||
* Return: Returns zero on success, a negative error code on failure.
|
||||
*/
|
||||
static int ssam_controller_caps_load(struct device *dev, struct ssam_controller_caps *caps)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(dev);
|
||||
|
||||
/* Set defaults. */
|
||||
caps->ssh_power_profile = U32_MAX;
|
||||
caps->screen_on_sleep_idle_timeout = U32_MAX;
|
||||
caps->screen_off_sleep_idle_timeout = U32_MAX;
|
||||
caps->d3_closes_handle = false;
|
||||
caps->ssh_buffer_size = U32_MAX;
|
||||
|
||||
if (handle)
|
||||
return ssam_controller_caps_load_from_acpi(handle, caps);
|
||||
else
|
||||
return ssam_controller_caps_load_from_of(dev, caps);
|
||||
}
|
||||
|
||||
/**
|
||||
* ssam_controller_init() - Initialize SSAM controller.
|
||||
* @ctrl: The controller to initialize.
|
||||
@ -1165,13 +1204,12 @@ int ssam_controller_caps_load_from_acpi(acpi_handle handle,
|
||||
int ssam_controller_init(struct ssam_controller *ctrl,
|
||||
struct serdev_device *serdev)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(&serdev->dev);
|
||||
int status;
|
||||
|
||||
init_rwsem(&ctrl->lock);
|
||||
kref_init(&ctrl->kref);
|
||||
|
||||
status = ssam_controller_caps_load_from_acpi(handle, &ctrl->caps);
|
||||
status = ssam_controller_caps_load(&serdev->dev, &ctrl->caps);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
@ -2716,11 +2754,12 @@ int ssam_irq_setup(struct ssam_controller *ctrl)
|
||||
const int irqf = IRQF_ONESHOT | IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN;
|
||||
|
||||
gpiod = gpiod_get(dev, "ssam_wakeup-int", GPIOD_ASIS);
|
||||
if (IS_ERR(gpiod))
|
||||
return PTR_ERR(gpiod);
|
||||
|
||||
irq = gpiod_to_irq(gpiod);
|
||||
gpiod_put(gpiod);
|
||||
if (IS_ERR(gpiod)) {
|
||||
irq = fwnode_irq_get(dev_fwnode(dev), 0);
|
||||
} else {
|
||||
irq = gpiod_to_irq(gpiod);
|
||||
gpiod_put(gpiod);
|
||||
}
|
||||
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
@ -17,9 +17,12 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/serdev.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include <linux/surface_aggregator/controller.h>
|
||||
#include <linux/surface_aggregator/device.h>
|
||||
@ -299,7 +302,7 @@ static const struct attribute_group ssam_sam_group = {
|
||||
};
|
||||
|
||||
|
||||
/* -- ACPI based device setup. ---------------------------------------------- */
|
||||
/* -- Serial device setup. -------------------------------------------------- */
|
||||
|
||||
static acpi_status ssam_serdev_setup_via_acpi_crs(struct acpi_resource *rsc,
|
||||
void *ctx)
|
||||
@ -352,13 +355,28 @@ static acpi_status ssam_serdev_setup_via_acpi_crs(struct acpi_resource *rsc,
|
||||
return AE_CTRL_TERMINATE;
|
||||
}
|
||||
|
||||
static acpi_status ssam_serdev_setup_via_acpi(acpi_handle handle,
|
||||
struct serdev_device *serdev)
|
||||
static int ssam_serdev_setup_via_acpi(struct serdev_device *serdev, acpi_handle handle)
|
||||
{
|
||||
return acpi_walk_resources(handle, METHOD_NAME__CRS,
|
||||
ssam_serdev_setup_via_acpi_crs, serdev);
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_walk_resources(handle, METHOD_NAME__CRS,
|
||||
ssam_serdev_setup_via_acpi_crs, serdev);
|
||||
|
||||
return status ? -ENXIO : 0;
|
||||
}
|
||||
|
||||
static int ssam_serdev_setup(struct acpi_device *ssh, struct serdev_device *serdev)
|
||||
{
|
||||
if (ssh)
|
||||
return ssam_serdev_setup_via_acpi(serdev, ssh->handle);
|
||||
|
||||
/* TODO: these values may differ per board/implementation */
|
||||
serdev_device_set_baudrate(serdev, 4 * HZ_PER_MHZ);
|
||||
serdev_device_set_flow_control(serdev, true);
|
||||
serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -- Power management. ----------------------------------------------------- */
|
||||
|
||||
@ -621,16 +639,17 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
|
||||
struct device *dev = &serdev->dev;
|
||||
struct acpi_device *ssh = ACPI_COMPANION(dev);
|
||||
struct ssam_controller *ctrl;
|
||||
acpi_status astatus;
|
||||
int status;
|
||||
|
||||
status = gpiod_count(dev, NULL);
|
||||
if (status < 0)
|
||||
return dev_err_probe(dev, status, "no GPIO found\n");
|
||||
if (ssh) {
|
||||
status = gpiod_count(dev, NULL);
|
||||
if (status < 0)
|
||||
return dev_err_probe(dev, status, "no GPIO found\n");
|
||||
|
||||
status = devm_acpi_dev_add_driver_gpios(dev, ssam_acpi_gpios);
|
||||
if (status)
|
||||
return status;
|
||||
status = devm_acpi_dev_add_driver_gpios(dev, ssam_acpi_gpios);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Allocate controller. */
|
||||
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
|
||||
@ -655,9 +674,9 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
|
||||
goto err_devopen;
|
||||
}
|
||||
|
||||
astatus = ssam_serdev_setup_via_acpi(ssh->handle, serdev);
|
||||
if (ACPI_FAILURE(astatus)) {
|
||||
status = dev_err_probe(dev, -ENXIO, "failed to setup serdev\n");
|
||||
status = ssam_serdev_setup(ssh, serdev);
|
||||
if (status) {
|
||||
status = dev_err_probe(dev, status, "failed to setup serdev\n");
|
||||
goto err_devinit;
|
||||
}
|
||||
|
||||
@ -717,7 +736,23 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
|
||||
* For now let's thus default power/wakeup to false.
|
||||
*/
|
||||
device_set_wakeup_capable(dev, true);
|
||||
acpi_dev_clear_dependencies(ssh);
|
||||
|
||||
/*
|
||||
* When using DT, we have to register the platform hub driver manually,
|
||||
* as it can't be matched based on top-level board compatible (like it
|
||||
* does the ACPI case).
|
||||
*/
|
||||
if (!ssh) {
|
||||
struct platform_device *ph_pdev =
|
||||
platform_device_register_simple("surface_aggregator_platform_hub",
|
||||
0, NULL, 0);
|
||||
if (IS_ERR(ph_pdev))
|
||||
return dev_err_probe(dev, PTR_ERR(ph_pdev),
|
||||
"Failed to register the platform hub driver\n");
|
||||
}
|
||||
|
||||
if (ssh)
|
||||
acpi_dev_clear_dependencies(ssh);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -782,18 +817,27 @@ static void ssam_serial_hub_remove(struct serdev_device *serdev)
|
||||
device_set_wakeup_capable(&serdev->dev, false);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id ssam_serial_hub_match[] = {
|
||||
static const struct acpi_device_id ssam_serial_hub_acpi_match[] = {
|
||||
{ "MSHW0084", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ssam_serial_hub_match);
|
||||
MODULE_DEVICE_TABLE(acpi, ssam_serial_hub_acpi_match);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id ssam_serial_hub_of_match[] = {
|
||||
{ .compatible = "microsoft,surface-sam", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ssam_serial_hub_of_match);
|
||||
#endif
|
||||
|
||||
static struct serdev_device_driver ssam_serial_hub = {
|
||||
.probe = ssam_serial_hub_probe,
|
||||
.remove = ssam_serial_hub_remove,
|
||||
.driver = {
|
||||
.name = "surface_serial_hub",
|
||||
.acpi_match_table = ssam_serial_hub_match,
|
||||
.acpi_match_table = ACPI_PTR(ssam_serial_hub_acpi_match),
|
||||
.of_match_table = of_match_ptr(ssam_serial_hub_of_match),
|
||||
.pm = &ssam_serial_hub_pm_ops,
|
||||
.shutdown = ssam_serial_hub_shutdown,
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/types.h>
|
||||
@ -291,6 +292,18 @@ static const struct software_node *ssam_node_group_sl6[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Devices for Surface Laptop 7. */
|
||||
static const struct software_node *ssam_node_group_sl7[] = {
|
||||
&ssam_node_root,
|
||||
&ssam_node_bat_ac,
|
||||
&ssam_node_bat_main,
|
||||
&ssam_node_tmp_perf_profile_with_fan,
|
||||
&ssam_node_fan_speed,
|
||||
&ssam_node_hid_sam_keyboard,
|
||||
/* TODO: evaluate thermal sensors devices when we get a driver for that */
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Devices for Surface Laptop Studio 1. */
|
||||
static const struct software_node *ssam_node_group_sls1[] = {
|
||||
&ssam_node_root,
|
||||
@ -380,7 +393,7 @@ static const struct software_node *ssam_node_group_sp9[] = {
|
||||
|
||||
/* -- SSAM platform/meta-hub driver. ---------------------------------------- */
|
||||
|
||||
static const struct acpi_device_id ssam_platform_hub_match[] = {
|
||||
static const struct acpi_device_id ssam_platform_hub_acpi_match[] = {
|
||||
/* Surface Pro 4, 5, and 6 (OMBR < 0x10) */
|
||||
{ "MSHW0081", (unsigned long)ssam_node_group_gen5 },
|
||||
|
||||
@ -446,18 +459,39 @@ static const struct acpi_device_id ssam_platform_hub_match[] = {
|
||||
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ssam_platform_hub_match);
|
||||
MODULE_DEVICE_TABLE(acpi, ssam_platform_hub_acpi_match);
|
||||
|
||||
static const struct of_device_id ssam_platform_hub_of_match[] __maybe_unused = {
|
||||
/* Surface Laptop 7 */
|
||||
{ .compatible = "microsoft,romulus13", (void *)ssam_node_group_sl7 },
|
||||
{ .compatible = "microsoft,romulus15", (void *)ssam_node_group_sl7 },
|
||||
{ },
|
||||
};
|
||||
|
||||
static int ssam_platform_hub_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct software_node **nodes;
|
||||
const struct of_device_id *match;
|
||||
struct device_node *fdt_root;
|
||||
struct ssam_controller *ctrl;
|
||||
struct fwnode_handle *root;
|
||||
int status;
|
||||
|
||||
nodes = (const struct software_node **)acpi_device_get_match_data(&pdev->dev);
|
||||
if (!nodes)
|
||||
return -ENODEV;
|
||||
if (!nodes) {
|
||||
fdt_root = of_find_node_by_path("/");
|
||||
if (!fdt_root)
|
||||
return -ENODEV;
|
||||
|
||||
match = of_match_node(ssam_platform_hub_of_match, fdt_root);
|
||||
of_node_put(fdt_root);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
nodes = (const struct software_node **)match->data;
|
||||
if (!nodes)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* As we're adding the SSAM client devices as children under this device
|
||||
@ -506,12 +540,13 @@ static struct platform_driver ssam_platform_hub_driver = {
|
||||
.remove_new = ssam_platform_hub_remove,
|
||||
.driver = {
|
||||
.name = "surface_aggregator_platform_hub",
|
||||
.acpi_match_table = ssam_platform_hub_match,
|
||||
.acpi_match_table = ssam_platform_hub_acpi_match,
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ssam_platform_hub_driver);
|
||||
|
||||
MODULE_ALIAS("platform:surface_aggregator_platform_hub");
|
||||
MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
|
||||
MODULE_DESCRIPTION("Device-registry for Surface System Aggregator Module");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1000,7 +1000,8 @@ config TOPSTAR_LAPTOP
|
||||
|
||||
config SERIAL_MULTI_INSTANTIATE
|
||||
tristate "Serial bus multi instantiate pseudo device driver"
|
||||
depends on I2C && SPI && ACPI
|
||||
depends on ACPI
|
||||
depends on (I2C && !SPI) || (!I2C && SPI) || (I2C && SPI)
|
||||
help
|
||||
Some ACPI-based systems list multiple devices in a single ACPI
|
||||
firmware-node. This driver will instantiate separate clients
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -1685,7 +1684,7 @@ static int acer_backlight_init(struct device *dev)
|
||||
|
||||
acer_backlight_device = bd;
|
||||
|
||||
bd->props.power = FB_BLANK_UNBLANK;
|
||||
bd->props.power = BACKLIGHT_POWER_ON;
|
||||
bd->props.brightness = read_brightness(bd);
|
||||
backlight_update_status(bd);
|
||||
return 0;
|
||||
@ -2224,39 +2223,25 @@ static void acer_rfkill_exit(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void acer_wmi_notify(u32 value, void *context)
|
||||
static void acer_wmi_notify(union acpi_object *obj, void *context)
|
||||
{
|
||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
struct event_return_value return_value;
|
||||
acpi_status status;
|
||||
u16 device_state;
|
||||
const struct key_entry *key;
|
||||
u32 scancode;
|
||||
|
||||
status = wmi_get_event_data(value, &response);
|
||||
if (status != AE_OK) {
|
||||
pr_warn("bad event status 0x%x\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)response.pointer;
|
||||
|
||||
if (!obj)
|
||||
return;
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
pr_warn("Unknown response received %d\n", obj->type);
|
||||
kfree(obj);
|
||||
return;
|
||||
}
|
||||
if (obj->buffer.length != 8) {
|
||||
pr_warn("Unknown buffer length %d\n", obj->buffer.length);
|
||||
kfree(obj);
|
||||
return;
|
||||
}
|
||||
|
||||
return_value = *((struct event_return_value *)obj->buffer.pointer);
|
||||
kfree(obj);
|
||||
|
||||
switch (return_value.function) {
|
||||
case WMID_HOTKEY_EVENT:
|
||||
|
@ -282,6 +282,29 @@ int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apmf_notify_smart_pc_update(struct amd_pmf_dev *pdev, u32 val, u32 preq, u32 index)
|
||||
{
|
||||
struct amd_pmf_notify_smart_pc_update args;
|
||||
struct acpi_buffer params;
|
||||
union acpi_object *info;
|
||||
|
||||
args.size = sizeof(args);
|
||||
args.pending_req = preq;
|
||||
args.custom_bios[index] = val;
|
||||
|
||||
params.length = sizeof(args);
|
||||
params.pointer = &args;
|
||||
|
||||
info = apmf_if_call(pdev, APMF_FUNC_NOTIFY_SMART_PC_UPDATES, ¶ms);
|
||||
if (!info)
|
||||
return -EIO;
|
||||
|
||||
kfree(info);
|
||||
dev_dbg(pdev->dev, "Notify smart pc update, val: %u\n", val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data)
|
||||
{
|
||||
return apmf_if_call_store_buffer(pdev, APMF_FUNC_AUTO_MODE, data, sizeof(*data));
|
||||
@ -447,6 +470,14 @@ int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amd_pmf_smartpc_apply_bios_output(struct amd_pmf_dev *dev, u32 val, u32 preq, u32 idx)
|
||||
{
|
||||
if (!is_apmf_func_supported(dev, APMF_FUNC_NOTIFY_SMART_PC_UPDATES))
|
||||
return -EINVAL;
|
||||
|
||||
return apmf_notify_smart_pc_update(dev, val, preq, idx);
|
||||
}
|
||||
|
||||
void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
|
||||
{
|
||||
acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
|
||||
|
@ -37,12 +37,6 @@
|
||||
#define AMD_PMF_RESULT_CMD_UNKNOWN 0xFE
|
||||
#define AMD_PMF_RESULT_FAILED 0xFF
|
||||
|
||||
/* List of supported CPU ids */
|
||||
#define AMD_CPU_ID_RMB 0x14b5
|
||||
#define AMD_CPU_ID_PS 0x14e8
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122
|
||||
|
||||
#define PMF_MSG_DELAY_MIN_US 50
|
||||
#define RESPONSE_REGISTER_LOOP_MAX 20000
|
||||
|
||||
@ -261,7 +255,19 @@ int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer)
|
||||
|
||||
/* Get Metrics Table Address */
|
||||
if (alloc_buffer) {
|
||||
dev->buf = kzalloc(sizeof(dev->m_table), GFP_KERNEL);
|
||||
switch (dev->cpu_id) {
|
||||
case AMD_CPU_ID_PS:
|
||||
case AMD_CPU_ID_RMB:
|
||||
dev->mtable_size = sizeof(dev->m_table);
|
||||
break;
|
||||
case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
|
||||
dev->mtable_size = sizeof(dev->m_table_v2);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev->dev, "Invalid CPU id: 0x%x", dev->cpu_id);
|
||||
}
|
||||
|
||||
dev->buf = kzalloc(dev->mtable_size, GFP_KERNEL);
|
||||
if (!dev->buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
@ -37,6 +37,14 @@ static const struct dmi_system_id fwbug_list[] = {
|
||||
},
|
||||
.driver_data = &quirk_no_sps_bug,
|
||||
},
|
||||
{
|
||||
.ident = "ASUS TUF Gaming A14",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "FA401W"),
|
||||
},
|
||||
.driver_data = &quirk_no_sps_bug,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -19,6 +19,12 @@
|
||||
#define POLICY_SIGN_COOKIE 0x31535024
|
||||
#define POLICY_COOKIE_OFFSET 0x10
|
||||
|
||||
/* List of supported CPU ids */
|
||||
#define AMD_CPU_ID_RMB 0x14b5
|
||||
#define AMD_CPU_ID_PS 0x14e8
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122
|
||||
|
||||
struct cookie_header {
|
||||
u32 sign;
|
||||
u32 length;
|
||||
@ -35,6 +41,7 @@ struct cookie_header {
|
||||
#define APMF_FUNC_STATIC_SLIDER_GRANULAR 9
|
||||
#define APMF_FUNC_DYN_SLIDER_AC 11
|
||||
#define APMF_FUNC_DYN_SLIDER_DC 12
|
||||
#define APMF_FUNC_NOTIFY_SMART_PC_UPDATES 14
|
||||
#define APMF_FUNC_SBIOS_HEARTBEAT_V2 16
|
||||
|
||||
/* Message Definitions */
|
||||
@ -82,7 +89,17 @@ struct cookie_header {
|
||||
#define PMF_POLICY_STT_SKINTEMP_APU 7
|
||||
#define PMF_POLICY_STT_SKINTEMP_HS2 8
|
||||
#define PMF_POLICY_SYSTEM_STATE 9
|
||||
#define PMF_POLICY_BIOS_OUTPUT_1 10
|
||||
#define PMF_POLICY_BIOS_OUTPUT_2 11
|
||||
#define PMF_POLICY_P3T 38
|
||||
#define PMF_POLICY_BIOS_OUTPUT_3 57
|
||||
#define PMF_POLICY_BIOS_OUTPUT_4 58
|
||||
#define PMF_POLICY_BIOS_OUTPUT_5 59
|
||||
#define PMF_POLICY_BIOS_OUTPUT_6 60
|
||||
#define PMF_POLICY_BIOS_OUTPUT_7 61
|
||||
#define PMF_POLICY_BIOS_OUTPUT_8 62
|
||||
#define PMF_POLICY_BIOS_OUTPUT_9 63
|
||||
#define PMF_POLICY_BIOS_OUTPUT_10 64
|
||||
|
||||
/* TA macros */
|
||||
#define PMF_TA_IF_VERSION_MAJOR 1
|
||||
@ -181,6 +198,53 @@ struct apmf_fan_idx {
|
||||
u32 fan_ctl_idx;
|
||||
} __packed;
|
||||
|
||||
struct smu_pmf_metrics_v2 {
|
||||
u16 core_frequency[16]; /* MHz */
|
||||
u16 core_power[16]; /* mW */
|
||||
u16 core_temp[16]; /* centi-C */
|
||||
u16 gfx_temp; /* centi-C */
|
||||
u16 soc_temp; /* centi-C */
|
||||
u16 stapm_opn_limit; /* mW */
|
||||
u16 stapm_cur_limit; /* mW */
|
||||
u16 infra_cpu_maxfreq; /* MHz */
|
||||
u16 infra_gfx_maxfreq; /* MHz */
|
||||
u16 skin_temp; /* centi-C */
|
||||
u16 gfxclk_freq; /* MHz */
|
||||
u16 fclk_freq; /* MHz */
|
||||
u16 gfx_activity; /* GFX busy % [0-100] */
|
||||
u16 socclk_freq; /* MHz */
|
||||
u16 vclk_freq; /* MHz */
|
||||
u16 vcn_activity; /* VCN busy % [0-100] */
|
||||
u16 vpeclk_freq; /* MHz */
|
||||
u16 ipuclk_freq; /* MHz */
|
||||
u16 ipu_busy[8]; /* NPU busy % [0-100] */
|
||||
u16 dram_reads; /* MB/sec */
|
||||
u16 dram_writes; /* MB/sec */
|
||||
u16 core_c0residency[16]; /* C0 residency % [0-100] */
|
||||
u16 ipu_power; /* mW */
|
||||
u32 apu_power; /* mW */
|
||||
u32 gfx_power; /* mW */
|
||||
u32 dgpu_power; /* mW */
|
||||
u32 socket_power; /* mW */
|
||||
u32 all_core_power; /* mW */
|
||||
u32 filter_alpha_value; /* time constant [us] */
|
||||
u32 metrics_counter;
|
||||
u16 memclk_freq; /* MHz */
|
||||
u16 mpipuclk_freq; /* MHz */
|
||||
u16 ipu_reads; /* MB/sec */
|
||||
u16 ipu_writes; /* MB/sec */
|
||||
u32 throttle_residency_prochot;
|
||||
u32 throttle_residency_spl;
|
||||
u32 throttle_residency_fppt;
|
||||
u32 throttle_residency_sppt;
|
||||
u32 throttle_residency_thm_core;
|
||||
u32 throttle_residency_thm_gfx;
|
||||
u32 throttle_residency_thm_soc;
|
||||
u16 psys;
|
||||
u16 spare1;
|
||||
u32 spare[6];
|
||||
} __packed;
|
||||
|
||||
struct smu_pmf_metrics {
|
||||
u16 gfxclk_freq; /* in MHz */
|
||||
u16 socclk_freq; /* in MHz */
|
||||
@ -278,6 +342,7 @@ struct amd_pmf_dev {
|
||||
int hb_interval; /* SBIOS heartbeat interval */
|
||||
struct delayed_work heart_beat;
|
||||
struct smu_pmf_metrics m_table;
|
||||
struct smu_pmf_metrics_v2 m_table_v2;
|
||||
struct delayed_work work_buffer;
|
||||
ktime_t start_time;
|
||||
int socket_power_history[AVG_SAMPLE_SIZE];
|
||||
@ -302,6 +367,7 @@ struct amd_pmf_dev {
|
||||
bool smart_pc_enabled;
|
||||
u16 pmf_if_version;
|
||||
struct input_dev *pmf_idev;
|
||||
size_t mtable_size;
|
||||
};
|
||||
|
||||
struct apmf_sps_prop_granular_v2 {
|
||||
@ -344,6 +410,12 @@ struct os_power_slider {
|
||||
u8 slider_event;
|
||||
} __packed;
|
||||
|
||||
struct amd_pmf_notify_smart_pc_update {
|
||||
u16 size;
|
||||
u32 pending_req;
|
||||
u32 custom_bios[10];
|
||||
} __packed;
|
||||
|
||||
struct fan_table_control {
|
||||
bool manual;
|
||||
unsigned long fan_id;
|
||||
@ -717,6 +789,7 @@ extern const struct attribute_group cnqf_feature_attribute_group;
|
||||
int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev);
|
||||
void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev);
|
||||
int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev);
|
||||
int amd_pmf_smartpc_apply_bios_output(struct amd_pmf_dev *dev, u32 val, u32 preq, u32 idx);
|
||||
|
||||
/* Smart PC - TA interfaces */
|
||||
void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in);
|
||||
|
@ -53,30 +53,49 @@ void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *
|
||||
void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {}
|
||||
#endif
|
||||
|
||||
static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
|
||||
static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in)
|
||||
{
|
||||
u16 max, avg = 0;
|
||||
int i;
|
||||
|
||||
memset(dev->buf, 0, sizeof(dev->m_table));
|
||||
amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
|
||||
memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table));
|
||||
|
||||
in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
|
||||
in->ev_info.skin_temperature = dev->m_table.skin_temp;
|
||||
|
||||
/* Get the avg and max C0 residency of all the cores */
|
||||
max = dev->m_table.avg_core_c0residency[0];
|
||||
for (i = 0; i < ARRAY_SIZE(dev->m_table.avg_core_c0residency); i++) {
|
||||
avg += dev->m_table.avg_core_c0residency[i];
|
||||
if (dev->m_table.avg_core_c0residency[i] > max)
|
||||
max = dev->m_table.avg_core_c0residency[i];
|
||||
max = *core_res;
|
||||
for (i = 0; i < size; i++) {
|
||||
avg += core_res[i];
|
||||
if (core_res[i] > max)
|
||||
max = core_res[i];
|
||||
}
|
||||
|
||||
avg = DIV_ROUND_CLOSEST(avg, ARRAY_SIZE(dev->m_table.avg_core_c0residency));
|
||||
avg = DIV_ROUND_CLOSEST(avg, size);
|
||||
in->ev_info.avg_c0residency = avg;
|
||||
in->ev_info.max_c0residency = max;
|
||||
in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity;
|
||||
}
|
||||
|
||||
static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
|
||||
{
|
||||
/* Get the updated metrics table data */
|
||||
memset(dev->buf, 0, dev->mtable_size);
|
||||
amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
|
||||
|
||||
switch (dev->cpu_id) {
|
||||
case AMD_CPU_ID_PS:
|
||||
memcpy(&dev->m_table, dev->buf, dev->mtable_size);
|
||||
in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
|
||||
in->ev_info.skin_temperature = dev->m_table.skin_temp;
|
||||
in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity;
|
||||
amd_pmf_get_c0_residency(dev->m_table.avg_core_c0residency,
|
||||
ARRAY_SIZE(dev->m_table.avg_core_c0residency), in);
|
||||
break;
|
||||
case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
|
||||
memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size);
|
||||
in->ev_info.socket_power = dev->m_table_v2.apu_power + dev->m_table_v2.dgpu_power;
|
||||
in->ev_info.skin_temperature = dev->m_table_v2.skin_temp;
|
||||
in->ev_info.gfx_busy = dev->m_table_v2.gfx_activity;
|
||||
amd_pmf_get_c0_residency(dev->m_table_v2.core_c0residency,
|
||||
ARRAY_SIZE(dev->m_table_v2.core_c0residency), in);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev->dev, "Unsupported CPU id: 0x%x", dev->cpu_id);
|
||||
}
|
||||
}
|
||||
|
||||
static const char * const pmf_battery_supply_name[] = {
|
||||
|
@ -160,6 +160,46 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
|
||||
dev_dbg(dev->dev, "update SYSTEM_STATE: %s\n",
|
||||
amd_pmf_uevent_as_str(val));
|
||||
break;
|
||||
|
||||
case PMF_POLICY_BIOS_OUTPUT_1:
|
||||
amd_pmf_smartpc_apply_bios_output(dev, val, BIT(0), 0);
|
||||
break;
|
||||
|
||||
case PMF_POLICY_BIOS_OUTPUT_2:
|
||||
amd_pmf_smartpc_apply_bios_output(dev, val, BIT(1), 1);
|
||||
break;
|
||||
|
||||
case PMF_POLICY_BIOS_OUTPUT_3:
|
||||
amd_pmf_smartpc_apply_bios_output(dev, val, BIT(2), 2);
|
||||
break;
|
||||
|
||||
case PMF_POLICY_BIOS_OUTPUT_4:
|
||||
amd_pmf_smartpc_apply_bios_output(dev, val, BIT(3), 3);
|
||||
break;
|
||||
|
||||
case PMF_POLICY_BIOS_OUTPUT_5:
|
||||
amd_pmf_smartpc_apply_bios_output(dev, val, BIT(4), 4);
|
||||
break;
|
||||
|
||||
case PMF_POLICY_BIOS_OUTPUT_6:
|
||||
amd_pmf_smartpc_apply_bios_output(dev, val, BIT(5), 5);
|
||||
break;
|
||||
|
||||
case PMF_POLICY_BIOS_OUTPUT_7:
|
||||
amd_pmf_smartpc_apply_bios_output(dev, val, BIT(6), 6);
|
||||
break;
|
||||
|
||||
case PMF_POLICY_BIOS_OUTPUT_8:
|
||||
amd_pmf_smartpc_apply_bios_output(dev, val, BIT(7), 7);
|
||||
break;
|
||||
|
||||
case PMF_POLICY_BIOS_OUTPUT_9:
|
||||
amd_pmf_smartpc_apply_bios_output(dev, val, BIT(8), 8);
|
||||
break;
|
||||
|
||||
case PMF_POLICY_BIOS_OUTPUT_10:
|
||||
amd_pmf_smartpc_apply_bios_output(dev, val, BIT(9), 9);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/uaccess.h>
|
||||
@ -818,7 +817,7 @@ static int asus_backlight_init(struct asus_laptop *asus)
|
||||
|
||||
asus->backlight_device = bd;
|
||||
bd->props.brightness = asus_read_brightness(bd);
|
||||
bd->props.power = FB_BLANK_UNBLANK;
|
||||
bd->props.power = BACKLIGHT_POWER_ON;
|
||||
backlight_update_status(bd);
|
||||
return 0;
|
||||
}
|
||||
|
@ -7,12 +7,12 @@
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/i8042.h>
|
||||
|
||||
@ -538,7 +538,7 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)
|
||||
dmi_check_system(asus_quirks);
|
||||
|
||||
driver->quirks = quirks;
|
||||
driver->panel_power = FB_BLANK_UNBLANK;
|
||||
driver->panel_power = BACKLIGHT_POWER_ON;
|
||||
|
||||
/* overwrite the wapf setting if the wapf paramater is specified */
|
||||
if (wapf != -1)
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/init.h>
|
||||
@ -97,6 +96,12 @@ module_param(fnlock_default, bool, 0444);
|
||||
#define ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST 1
|
||||
#define ASUS_THROTTLE_THERMAL_POLICY_SILENT 2
|
||||
|
||||
#define ASUS_THROTTLE_THERMAL_POLICY_DEFAULT_VIVO 0
|
||||
#define ASUS_THROTTLE_THERMAL_POLICY_SILENT_VIVO 1
|
||||
#define ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST_VIVO 2
|
||||
|
||||
#define PLATFORM_PROFILE_MAX 2
|
||||
|
||||
#define USB_INTEL_XUSB2PR 0xD0
|
||||
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
|
||||
|
||||
@ -300,8 +305,8 @@ struct asus_wmi {
|
||||
u32 kbd_rgb_dev;
|
||||
bool kbd_rgb_state_available;
|
||||
|
||||
bool throttle_thermal_policy_available;
|
||||
u8 throttle_thermal_policy_mode;
|
||||
u32 throttle_thermal_policy_dev;
|
||||
|
||||
bool cpu_fan_curve_available;
|
||||
bool gpu_fan_curve_available;
|
||||
@ -349,20 +354,29 @@ static int asus_wmi_evaluate_method3(u32 method_id,
|
||||
status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
|
||||
&input, &output);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
pr_debug("%s called (0x%08x) with args: 0x%08x, 0x%08x, 0x%08x\n",
|
||||
__func__, method_id, arg0, arg1, arg2);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n",
|
||||
__func__, method_id, arg0, -EIO);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)output.pointer;
|
||||
if (obj && obj->type == ACPI_TYPE_INTEGER)
|
||||
tmp = (u32) obj->integer.value;
|
||||
|
||||
pr_debug("Result: 0x%08x\n", tmp);
|
||||
if (retval)
|
||||
*retval = tmp;
|
||||
|
||||
kfree(obj);
|
||||
|
||||
if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
|
||||
if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) {
|
||||
pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n",
|
||||
__func__, method_id, arg0, -ENODEV);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -392,20 +406,29 @@ static int asus_wmi_evaluate_method5(u32 method_id,
|
||||
status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
|
||||
&input, &output);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
pr_debug("%s called (0x%08x) with args: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
|
||||
__func__, method_id, arg0, arg1, arg2, arg3, arg4);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n",
|
||||
__func__, method_id, arg0, -EIO);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)output.pointer;
|
||||
if (obj && obj->type == ACPI_TYPE_INTEGER)
|
||||
tmp = (u32) obj->integer.value;
|
||||
|
||||
pr_debug("Result: %x\n", tmp);
|
||||
if (retval)
|
||||
*retval = tmp;
|
||||
|
||||
kfree(obj);
|
||||
|
||||
if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
|
||||
if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) {
|
||||
pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n",
|
||||
__func__, method_id, arg0, -ENODEV);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -431,8 +454,13 @@ static int asus_wmi_evaluate_method_buf(u32 method_id,
|
||||
status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
|
||||
&input, &output);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
pr_debug("%s called (0x%08x) with args: 0x%08x, 0x%08x\n",
|
||||
__func__, method_id, arg0, arg1);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n",
|
||||
__func__, method_id, arg0, -EIO);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)output.pointer;
|
||||
|
||||
@ -468,8 +496,11 @@ static int asus_wmi_evaluate_method_buf(u32 method_id,
|
||||
|
||||
kfree(obj);
|
||||
|
||||
if (err)
|
||||
if (err) {
|
||||
pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n",
|
||||
__func__, method_id, arg0, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -557,6 +588,7 @@ static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id)
|
||||
{
|
||||
u32 retval;
|
||||
int status = asus_wmi_get_devstate(asus, dev_id, &retval);
|
||||
pr_debug("%s called (0x%08x), retval: 0x%08x\n", __func__, dev_id, retval);
|
||||
|
||||
return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT);
|
||||
}
|
||||
@ -1722,7 +1754,8 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!kbd_led_read(asus, &led_val, NULL)) {
|
||||
if (!kbd_led_read(asus, &led_val, NULL) && !dmi_check_system(asus_use_hid_led_dmi_ids)) {
|
||||
pr_info("using asus-wmi for asus::kbd_backlight\n");
|
||||
asus->kbd_led_wk = led_val;
|
||||
asus->kbd_led.name = "asus::kbd_backlight";
|
||||
asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
|
||||
@ -3186,7 +3219,7 @@ static int fan_curve_get_factory_default(struct asus_wmi *asus, u32 fan_dev)
|
||||
int err, fan_idx;
|
||||
u8 mode = 0;
|
||||
|
||||
if (asus->throttle_thermal_policy_available)
|
||||
if (asus->throttle_thermal_policy_dev)
|
||||
mode = asus->throttle_thermal_policy_mode;
|
||||
/* DEVID_<C/G>PU_FAN_CURVE is switched for OVERBOOST vs SILENT */
|
||||
if (mode == 2)
|
||||
@ -3393,7 +3426,7 @@ static ssize_t fan_curve_enable_store(struct device *dev,
|
||||
* For machines with throttle this is the only way to reset fans
|
||||
* to default mode of operation (does not erase curve data).
|
||||
*/
|
||||
if (asus->throttle_thermal_policy_available) {
|
||||
if (asus->throttle_thermal_policy_dev) {
|
||||
err = throttle_thermal_policy_write(asus);
|
||||
if (err)
|
||||
return err;
|
||||
@ -3610,8 +3643,8 @@ static const struct attribute_group asus_fan_curve_attr_group = {
|
||||
__ATTRIBUTE_GROUPS(asus_fan_curve_attr);
|
||||
|
||||
/*
|
||||
* Must be initialised after throttle_thermal_policy_check_present() as
|
||||
* we check the status of throttle_thermal_policy_available during init.
|
||||
* Must be initialised after throttle_thermal_policy_dev is set as
|
||||
* we check the status of throttle_thermal_policy_dev during init.
|
||||
*/
|
||||
static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus)
|
||||
{
|
||||
@ -3621,18 +3654,27 @@ static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus)
|
||||
|
||||
err = fan_curve_check_present(asus, &asus->cpu_fan_curve_available,
|
||||
ASUS_WMI_DEVID_CPU_FAN_CURVE);
|
||||
if (err)
|
||||
if (err) {
|
||||
pr_debug("%s, checked 0x%08x, failed: %d\n",
|
||||
__func__, ASUS_WMI_DEVID_CPU_FAN_CURVE, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = fan_curve_check_present(asus, &asus->gpu_fan_curve_available,
|
||||
ASUS_WMI_DEVID_GPU_FAN_CURVE);
|
||||
if (err)
|
||||
if (err) {
|
||||
pr_debug("%s, checked 0x%08x, failed: %d\n",
|
||||
__func__, ASUS_WMI_DEVID_GPU_FAN_CURVE, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = fan_curve_check_present(asus, &asus->mid_fan_curve_available,
|
||||
ASUS_WMI_DEVID_MID_FAN_CURVE);
|
||||
if (err)
|
||||
if (err) {
|
||||
pr_debug("%s, checked 0x%08x, failed: %d\n",
|
||||
__func__, ASUS_WMI_DEVID_MID_FAN_CURVE, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!asus->cpu_fan_curve_available
|
||||
&& !asus->gpu_fan_curve_available
|
||||
@ -3652,38 +3694,13 @@ static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus)
|
||||
}
|
||||
|
||||
/* Throttle thermal policy ****************************************************/
|
||||
|
||||
static int throttle_thermal_policy_check_present(struct asus_wmi *asus)
|
||||
{
|
||||
u32 result;
|
||||
int err;
|
||||
|
||||
asus->throttle_thermal_policy_available = false;
|
||||
|
||||
err = asus_wmi_get_devstate(asus,
|
||||
ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
|
||||
&result);
|
||||
if (err) {
|
||||
if (err == -ENODEV)
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
if (result & ASUS_WMI_DSTS_PRESENCE_BIT)
|
||||
asus->throttle_thermal_policy_available = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int throttle_thermal_policy_write(struct asus_wmi *asus)
|
||||
{
|
||||
int err;
|
||||
u8 value;
|
||||
u8 value = asus->throttle_thermal_policy_mode;
|
||||
u32 retval;
|
||||
int err;
|
||||
|
||||
value = asus->throttle_thermal_policy_mode;
|
||||
|
||||
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
|
||||
err = asus_wmi_set_devstate(asus->throttle_thermal_policy_dev,
|
||||
value, &retval);
|
||||
|
||||
sysfs_notify(&asus->platform_device->dev.kobj, NULL,
|
||||
@ -3713,7 +3730,7 @@ static int throttle_thermal_policy_write(struct asus_wmi *asus)
|
||||
|
||||
static int throttle_thermal_policy_set_default(struct asus_wmi *asus)
|
||||
{
|
||||
if (!asus->throttle_thermal_policy_available)
|
||||
if (!asus->throttle_thermal_policy_dev)
|
||||
return 0;
|
||||
|
||||
asus->throttle_thermal_policy_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
|
||||
@ -3725,7 +3742,7 @@ static int throttle_thermal_policy_switch_next(struct asus_wmi *asus)
|
||||
u8 new_mode = asus->throttle_thermal_policy_mode + 1;
|
||||
int err;
|
||||
|
||||
if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT)
|
||||
if (new_mode > PLATFORM_PROFILE_MAX)
|
||||
new_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
|
||||
|
||||
asus->throttle_thermal_policy_mode = new_mode;
|
||||
@ -3764,7 +3781,7 @@ static ssize_t throttle_thermal_policy_store(struct device *dev,
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT)
|
||||
if (new_mode > PLATFORM_PROFILE_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
asus->throttle_thermal_policy_mode = new_mode;
|
||||
@ -3781,10 +3798,52 @@ static ssize_t throttle_thermal_policy_store(struct device *dev,
|
||||
return count;
|
||||
}
|
||||
|
||||
// Throttle thermal policy: 0 - default, 1 - overboost, 2 - silent
|
||||
/*
|
||||
* Throttle thermal policy: 0 - default, 1 - overboost, 2 - silent
|
||||
*/
|
||||
static DEVICE_ATTR_RW(throttle_thermal_policy);
|
||||
|
||||
/* Platform profile ***********************************************************/
|
||||
static int asus_wmi_platform_profile_to_vivo(struct asus_wmi *asus, int mode)
|
||||
{
|
||||
bool vivo;
|
||||
|
||||
vivo = asus->throttle_thermal_policy_dev == ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO;
|
||||
|
||||
if (vivo) {
|
||||
switch (mode) {
|
||||
case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT:
|
||||
return ASUS_THROTTLE_THERMAL_POLICY_DEFAULT_VIVO;
|
||||
case ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST:
|
||||
return ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST_VIVO;
|
||||
case ASUS_THROTTLE_THERMAL_POLICY_SILENT:
|
||||
return ASUS_THROTTLE_THERMAL_POLICY_SILENT_VIVO;
|
||||
}
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static int asus_wmi_platform_profile_mode_from_vivo(struct asus_wmi *asus, int mode)
|
||||
{
|
||||
bool vivo;
|
||||
|
||||
vivo = asus->throttle_thermal_policy_dev == ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO;
|
||||
|
||||
if (vivo) {
|
||||
switch (mode) {
|
||||
case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT_VIVO:
|
||||
return ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
|
||||
case ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST_VIVO:
|
||||
return ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST;
|
||||
case ASUS_THROTTLE_THERMAL_POLICY_SILENT_VIVO:
|
||||
return ASUS_THROTTLE_THERMAL_POLICY_SILENT;
|
||||
}
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
@ -3792,10 +3851,9 @@ static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof,
|
||||
int tp;
|
||||
|
||||
asus = container_of(pprof, struct asus_wmi, platform_profile_handler);
|
||||
|
||||
tp = asus->throttle_thermal_policy_mode;
|
||||
|
||||
switch (tp) {
|
||||
switch (asus_wmi_platform_profile_mode_from_vivo(asus, tp)) {
|
||||
case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT:
|
||||
*profile = PLATFORM_PROFILE_BALANCED;
|
||||
break;
|
||||
@ -3834,7 +3892,7 @@ static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof,
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
asus->throttle_thermal_policy_mode = tp;
|
||||
asus->throttle_thermal_policy_mode = asus_wmi_platform_profile_to_vivo(asus, tp);
|
||||
return throttle_thermal_policy_write(asus);
|
||||
}
|
||||
|
||||
@ -3847,7 +3905,7 @@ static int platform_profile_setup(struct asus_wmi *asus)
|
||||
* Not an error if a component platform_profile relies on is unavailable
|
||||
* so early return, skipping the setup of platform_profile.
|
||||
*/
|
||||
if (!asus->throttle_thermal_policy_available)
|
||||
if (!asus->throttle_thermal_policy_dev)
|
||||
return 0;
|
||||
|
||||
dev_info(dev, "Using throttle_thermal_policy for platform_profile support\n");
|
||||
@ -3862,8 +3920,13 @@ static int platform_profile_setup(struct asus_wmi *asus)
|
||||
asus->platform_profile_handler.choices);
|
||||
|
||||
err = platform_profile_register(&asus->platform_profile_handler);
|
||||
if (err)
|
||||
if (err == -EEXIST) {
|
||||
pr_warn("%s, a platform_profile handler is already registered\n", __func__);
|
||||
return 0;
|
||||
} else if (err) {
|
||||
pr_err("%s, failed at platform_profile_register: %d\n", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
asus->platform_profile_support = true;
|
||||
return 0;
|
||||
@ -3884,7 +3947,7 @@ static int read_backlight_power(struct asus_wmi *asus)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
|
||||
return ret ? BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF;
|
||||
}
|
||||
|
||||
static int read_brightness_max(struct asus_wmi *asus)
|
||||
@ -3943,7 +4006,7 @@ static int update_bl_status(struct backlight_device *bd)
|
||||
|
||||
power = read_backlight_power(asus);
|
||||
if (power != -ENODEV && bd->props.power != power) {
|
||||
ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK);
|
||||
ctrl_param = !!(bd->props.power == BACKLIGHT_POWER_ON);
|
||||
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT,
|
||||
ctrl_param, NULL);
|
||||
if (asus->driver->quirks->store_backlight_power)
|
||||
@ -4002,7 +4065,7 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus)
|
||||
|
||||
power = read_backlight_power(asus);
|
||||
if (power == -ENODEV)
|
||||
power = FB_BLANK_UNBLANK;
|
||||
power = BACKLIGHT_POWER_ON;
|
||||
else if (power < 0)
|
||||
return power;
|
||||
|
||||
@ -4060,7 +4123,7 @@ static int read_screenpad_backlight_power(struct asus_wmi *asus)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* 1 == powered */
|
||||
return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
|
||||
return ret ? BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF;
|
||||
}
|
||||
|
||||
static int read_screenpad_brightness(struct backlight_device *bd)
|
||||
@ -4073,7 +4136,7 @@ static int read_screenpad_brightness(struct backlight_device *bd)
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* The device brightness can only be read if powered, so return stored */
|
||||
if (err == FB_BLANK_POWERDOWN)
|
||||
if (err == BACKLIGHT_POWER_OFF)
|
||||
return asus->driver->screenpad_brightness - ASUS_SCREENPAD_BRIGHT_MIN;
|
||||
|
||||
err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &retval);
|
||||
@ -4094,7 +4157,7 @@ static int update_screenpad_bl_status(struct backlight_device *bd)
|
||||
return power;
|
||||
|
||||
if (bd->props.power != power) {
|
||||
if (power != FB_BLANK_UNBLANK) {
|
||||
if (power != BACKLIGHT_POWER_ON) {
|
||||
/* Only brightness > 0 can power it back on */
|
||||
ctrl_param = asus->driver->screenpad_brightness - ASUS_SCREENPAD_BRIGHT_MIN;
|
||||
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT,
|
||||
@ -4102,7 +4165,7 @@ static int update_screenpad_bl_status(struct backlight_device *bd)
|
||||
} else {
|
||||
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_POWER, 0, NULL);
|
||||
}
|
||||
} else if (power == FB_BLANK_UNBLANK) {
|
||||
} else if (power == BACKLIGHT_POWER_ON) {
|
||||
/* Only set brightness if powered on or we get invalid/unsync state */
|
||||
ctrl_param = bd->props.brightness + ASUS_SCREENPAD_BRIGHT_MIN;
|
||||
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT, ctrl_param, NULL);
|
||||
@ -4132,7 +4195,7 @@ static int asus_screenpad_init(struct asus_wmi *asus)
|
||||
if (power < 0)
|
||||
return power;
|
||||
|
||||
if (power != FB_BLANK_POWERDOWN) {
|
||||
if (power != BACKLIGHT_POWER_OFF) {
|
||||
err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &brightness);
|
||||
if (err < 0)
|
||||
return err;
|
||||
@ -4189,28 +4252,15 @@ static void asus_wmi_fnlock_update(struct asus_wmi *asus)
|
||||
|
||||
/* WMI events *****************************************************************/
|
||||
|
||||
static int asus_wmi_get_event_code(u32 value)
|
||||
static int asus_wmi_get_event_code(union acpi_object *obj)
|
||||
{
|
||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
int code;
|
||||
|
||||
status = wmi_get_event_data(value, &response);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_warn("Failed to get WMI notify code: %s\n",
|
||||
acpi_format_exception(status));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)response.pointer;
|
||||
|
||||
if (obj && obj->type == ACPI_TYPE_INTEGER)
|
||||
code = (int)(obj->integer.value & WMI_EVENT_MASK);
|
||||
else
|
||||
code = -EIO;
|
||||
|
||||
kfree(obj);
|
||||
return code;
|
||||
}
|
||||
|
||||
@ -4262,7 +4312,7 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
|
||||
if (code == NOTIFY_KBD_FBM || code == NOTIFY_KBD_TTP) {
|
||||
if (asus->fan_boost_mode_available)
|
||||
fan_boost_mode_switch_next(asus);
|
||||
if (asus->throttle_thermal_policy_available)
|
||||
if (asus->throttle_thermal_policy_dev)
|
||||
throttle_thermal_policy_switch_next(asus);
|
||||
return;
|
||||
|
||||
@ -4276,10 +4326,10 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
|
||||
pr_info("Unknown key code 0x%x\n", code);
|
||||
}
|
||||
|
||||
static void asus_wmi_notify(u32 value, void *context)
|
||||
static void asus_wmi_notify(union acpi_object *obj, void *context)
|
||||
{
|
||||
struct asus_wmi *asus = context;
|
||||
int code = asus_wmi_get_event_code(value);
|
||||
int code = asus_wmi_get_event_code(obj);
|
||||
|
||||
if (code < 0) {
|
||||
pr_warn("Failed to get notify code: %d\n", code);
|
||||
@ -4434,7 +4484,7 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
|
||||
else if (attr == &dev_attr_fan_boost_mode.attr)
|
||||
ok = asus->fan_boost_mode_available;
|
||||
else if (attr == &dev_attr_throttle_thermal_policy.attr)
|
||||
ok = asus->throttle_thermal_policy_available;
|
||||
ok = asus->throttle_thermal_policy_dev != 0;
|
||||
else if (attr == &dev_attr_ppt_pl2_sppt.attr)
|
||||
devid = ASUS_WMI_DEVID_PPT_PL2_SPPT;
|
||||
else if (attr == &dev_attr_ppt_pl1_spl.attr)
|
||||
@ -4460,8 +4510,10 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
|
||||
else if (attr == &dev_attr_available_mini_led_mode.attr)
|
||||
ok = asus->mini_led_dev_id != 0;
|
||||
|
||||
if (devid != -1)
|
||||
if (devid != -1) {
|
||||
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
|
||||
pr_debug("%s called 0x%08x, ok: %x\n", __func__, devid, ok);
|
||||
}
|
||||
|
||||
return ok ? attr->mode : 0;
|
||||
}
|
||||
@ -4726,16 +4778,15 @@ static int asus_wmi_add(struct platform_device *pdev)
|
||||
else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE2))
|
||||
asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE2;
|
||||
|
||||
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY))
|
||||
asus->throttle_thermal_policy_dev = ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY;
|
||||
else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO))
|
||||
asus->throttle_thermal_policy_dev = ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO;
|
||||
|
||||
err = fan_boost_mode_check_present(asus);
|
||||
if (err)
|
||||
goto fail_fan_boost_mode;
|
||||
|
||||
err = throttle_thermal_policy_check_present(asus);
|
||||
if (err)
|
||||
goto fail_throttle_thermal_policy;
|
||||
else
|
||||
throttle_thermal_policy_set_default(asus);
|
||||
|
||||
err = platform_profile_setup(asus);
|
||||
if (err)
|
||||
goto fail_platform_profile_setup;
|
||||
@ -4830,7 +4881,6 @@ fail_hwmon:
|
||||
fail_input:
|
||||
asus_wmi_sysfs_exit(asus->platform_device);
|
||||
fail_sysfs:
|
||||
fail_throttle_thermal_policy:
|
||||
fail_custom_fan_curve:
|
||||
fail_platform_profile_setup:
|
||||
if (asus->platform_profile_support)
|
||||
|
@ -49,6 +49,7 @@ config DELL_LAPTOP
|
||||
default m
|
||||
depends on DMI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
depends on ACPI_BATTERY
|
||||
depends on ACPI_VIDEO || ACPI_VIDEO = n
|
||||
depends on RFKILL || RFKILL = n
|
||||
depends on DELL_WMI || DELL_WMI = n
|
||||
|
@ -22,11 +22,13 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/i8042.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <acpi/battery.h>
|
||||
#include <acpi/video.h>
|
||||
#include "dell-rbtn.h"
|
||||
#include "dell-smbios.h"
|
||||
@ -99,6 +101,20 @@ static bool force_rfkill;
|
||||
static bool micmute_led_registered;
|
||||
static bool mute_led_registered;
|
||||
|
||||
struct battery_mode_info {
|
||||
int token;
|
||||
const char *label;
|
||||
};
|
||||
|
||||
static const struct battery_mode_info battery_modes[] = {
|
||||
{ BAT_PRI_AC_MODE_TOKEN, "Trickle" },
|
||||
{ BAT_EXPRESS_MODE_TOKEN, "Fast" },
|
||||
{ BAT_STANDARD_MODE_TOKEN, "Standard" },
|
||||
{ BAT_ADAPTIVE_MODE_TOKEN, "Adaptive" },
|
||||
{ BAT_CUSTOM_MODE_TOKEN, "Custom" },
|
||||
};
|
||||
static u32 battery_supported_modes;
|
||||
|
||||
module_param(force_rfkill, bool, 0444);
|
||||
MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models");
|
||||
|
||||
@ -353,6 +369,32 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
|
||||
{ }
|
||||
};
|
||||
|
||||
/* -1 is a sentinel value, telling us to use token->value */
|
||||
#define USE_TVAL ((u32) -1)
|
||||
static int dell_send_request_for_tokenid(struct calling_interface_buffer *buffer,
|
||||
u16 class, u16 select, u16 tokenid,
|
||||
u32 val)
|
||||
{
|
||||
struct calling_interface_token *token;
|
||||
|
||||
token = dell_smbios_find_token(tokenid);
|
||||
if (!token)
|
||||
return -ENODEV;
|
||||
|
||||
if (val == USE_TVAL)
|
||||
val = token->value;
|
||||
|
||||
dell_fill_request(buffer, token->location, val, 0, 0);
|
||||
return dell_send_request(buffer, class, select);
|
||||
}
|
||||
|
||||
static inline int dell_set_std_token_value(struct calling_interface_buffer *buffer,
|
||||
u16 tokenid, u32 value)
|
||||
{
|
||||
return dell_send_request_for_tokenid(buffer, CLASS_TOKEN_WRITE,
|
||||
SELECT_TOKEN_STD, tokenid, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Derived from information in smbios-wireless-ctl:
|
||||
*
|
||||
@ -895,43 +937,24 @@ static void dell_cleanup_rfkill(void)
|
||||
static int dell_send_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
struct calling_interface_token *token;
|
||||
int ret;
|
||||
u16 select;
|
||||
|
||||
token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
|
||||
if (!token)
|
||||
return -ENODEV;
|
||||
|
||||
dell_fill_request(&buffer,
|
||||
token->location, bd->props.brightness, 0, 0);
|
||||
if (power_supply_is_system_supplied() > 0)
|
||||
ret = dell_send_request(&buffer,
|
||||
CLASS_TOKEN_WRITE, SELECT_TOKEN_AC);
|
||||
else
|
||||
ret = dell_send_request(&buffer,
|
||||
CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT);
|
||||
|
||||
return ret;
|
||||
select = power_supply_is_system_supplied() > 0 ?
|
||||
SELECT_TOKEN_AC : SELECT_TOKEN_BAT;
|
||||
return dell_send_request_for_tokenid(&buffer, CLASS_TOKEN_WRITE,
|
||||
select, BRIGHTNESS_TOKEN, bd->props.brightness);
|
||||
}
|
||||
|
||||
static int dell_get_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
struct calling_interface_token *token;
|
||||
int ret;
|
||||
u16 select;
|
||||
|
||||
token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
|
||||
if (!token)
|
||||
return -ENODEV;
|
||||
|
||||
dell_fill_request(&buffer, token->location, 0, 0, 0);
|
||||
if (power_supply_is_system_supplied() > 0)
|
||||
ret = dell_send_request(&buffer,
|
||||
CLASS_TOKEN_READ, SELECT_TOKEN_AC);
|
||||
else
|
||||
ret = dell_send_request(&buffer,
|
||||
CLASS_TOKEN_READ, SELECT_TOKEN_BAT);
|
||||
|
||||
select = power_supply_is_system_supplied() > 0 ?
|
||||
SELECT_TOKEN_AC : SELECT_TOKEN_BAT;
|
||||
ret = dell_send_request_for_tokenid(&buffer, CLASS_TOKEN_READ,
|
||||
select, BRIGHTNESS_TOKEN, 0);
|
||||
if (ret == 0)
|
||||
ret = buffer.output[1];
|
||||
|
||||
@ -1355,20 +1378,11 @@ static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
|
||||
static int kbd_set_token_bit(u8 bit)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
struct calling_interface_token *token;
|
||||
int ret;
|
||||
|
||||
if (bit >= ARRAY_SIZE(kbd_tokens))
|
||||
return -EINVAL;
|
||||
|
||||
token = dell_smbios_find_token(kbd_tokens[bit]);
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
dell_fill_request(&buffer, token->location, token->value, 0, 0);
|
||||
ret = dell_send_request(&buffer, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
|
||||
|
||||
return ret;
|
||||
return dell_set_std_token_value(&buffer, kbd_tokens[bit], USE_TVAL);
|
||||
}
|
||||
|
||||
static int kbd_get_token_bit(u8 bit)
|
||||
@ -1387,11 +1401,10 @@ static int kbd_get_token_bit(u8 bit)
|
||||
|
||||
dell_fill_request(&buffer, token->location, 0, 0, 0);
|
||||
ret = dell_send_request(&buffer, CLASS_TOKEN_READ, SELECT_TOKEN_STD);
|
||||
val = buffer.output[1];
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = buffer.output[1];
|
||||
return (val == token->value);
|
||||
}
|
||||
|
||||
@ -1497,7 +1510,7 @@ static inline int kbd_init_info(void)
|
||||
|
||||
}
|
||||
|
||||
static inline void kbd_init_tokens(void)
|
||||
static inline void __init kbd_init_tokens(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -1506,7 +1519,7 @@ static inline void kbd_init_tokens(void)
|
||||
kbd_token_bits |= BIT(i);
|
||||
}
|
||||
|
||||
static void kbd_init(void)
|
||||
static void __init kbd_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -2131,21 +2144,11 @@ static int micmute_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
struct calling_interface_token *token;
|
||||
int state = brightness != LED_OFF;
|
||||
u32 tokenid;
|
||||
|
||||
if (state == 0)
|
||||
token = dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE);
|
||||
else
|
||||
token = dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE);
|
||||
|
||||
if (!token)
|
||||
return -ENODEV;
|
||||
|
||||
dell_fill_request(&buffer, token->location, token->value, 0, 0);
|
||||
dell_send_request(&buffer, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
|
||||
|
||||
return 0;
|
||||
tokenid = brightness == LED_OFF ?
|
||||
GLOBAL_MIC_MUTE_DISABLE : GLOBAL_MIC_MUTE_ENABLE;
|
||||
return dell_set_std_token_value(&buffer, tokenid, USE_TVAL);
|
||||
}
|
||||
|
||||
static struct led_classdev micmute_led_cdev = {
|
||||
@ -2159,21 +2162,11 @@ static int mute_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
struct calling_interface_token *token;
|
||||
int state = brightness != LED_OFF;
|
||||
u32 tokenid;
|
||||
|
||||
if (state == 0)
|
||||
token = dell_smbios_find_token(GLOBAL_MUTE_DISABLE);
|
||||
else
|
||||
token = dell_smbios_find_token(GLOBAL_MUTE_ENABLE);
|
||||
|
||||
if (!token)
|
||||
return -ENODEV;
|
||||
|
||||
dell_fill_request(&buffer, token->location, token->value, 0, 0);
|
||||
dell_send_request(&buffer, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
|
||||
|
||||
return 0;
|
||||
tokenid = brightness == LED_OFF ?
|
||||
GLOBAL_MUTE_DISABLE : GLOBAL_MUTE_ENABLE;
|
||||
return dell_set_std_token_value(&buffer, tokenid, USE_TVAL);
|
||||
}
|
||||
|
||||
static struct led_classdev mute_led_cdev = {
|
||||
@ -2183,9 +2176,274 @@ static struct led_classdev mute_led_cdev = {
|
||||
.default_trigger = "audio-mute",
|
||||
};
|
||||
|
||||
static int __init dell_init(void)
|
||||
static int dell_battery_set_mode(const u16 tokenid)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
|
||||
return dell_set_std_token_value(&buffer, tokenid, USE_TVAL);
|
||||
}
|
||||
|
||||
static int dell_battery_read(const u16 tokenid)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
int err;
|
||||
|
||||
err = dell_send_request_for_tokenid(&buffer, CLASS_TOKEN_READ,
|
||||
SELECT_TOKEN_STD, tokenid, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (buffer.output[1] > INT_MAX)
|
||||
return -EIO;
|
||||
|
||||
return buffer.output[1];
|
||||
}
|
||||
|
||||
static bool dell_battery_mode_is_active(const u16 tokenid)
|
||||
{
|
||||
struct calling_interface_token *token;
|
||||
int ret;
|
||||
|
||||
ret = dell_battery_read(tokenid);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
token = dell_smbios_find_token(tokenid);
|
||||
/* token's already verified by dell_battery_read() */
|
||||
|
||||
return token->value == (u16) ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The rules: the minimum start charging value is 50%. The maximum
|
||||
* start charging value is 95%. The minimum end charging value is
|
||||
* 55%. The maximum end charging value is 100%. And finally, there
|
||||
* has to be at least a 5% difference between start & end values.
|
||||
*/
|
||||
#define CHARGE_START_MIN 50
|
||||
#define CHARGE_START_MAX 95
|
||||
#define CHARGE_END_MIN 55
|
||||
#define CHARGE_END_MAX 100
|
||||
#define CHARGE_MIN_DIFF 5
|
||||
|
||||
static int dell_battery_set_custom_charge_start(int start)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
int end;
|
||||
|
||||
start = clamp(start, CHARGE_START_MIN, CHARGE_START_MAX);
|
||||
end = dell_battery_read(BAT_CUSTOM_CHARGE_END);
|
||||
if (end < 0)
|
||||
return end;
|
||||
if ((end - start) < CHARGE_MIN_DIFF)
|
||||
start = end - CHARGE_MIN_DIFF;
|
||||
|
||||
return dell_set_std_token_value(&buffer, BAT_CUSTOM_CHARGE_START,
|
||||
start);
|
||||
}
|
||||
|
||||
static int dell_battery_set_custom_charge_end(int end)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
int start;
|
||||
|
||||
end = clamp(end, CHARGE_END_MIN, CHARGE_END_MAX);
|
||||
start = dell_battery_read(BAT_CUSTOM_CHARGE_START);
|
||||
if (start < 0)
|
||||
return start;
|
||||
if ((end - start) < CHARGE_MIN_DIFF)
|
||||
end = start + CHARGE_MIN_DIFF;
|
||||
|
||||
return dell_set_std_token_value(&buffer, BAT_CUSTOM_CHARGE_END, end);
|
||||
}
|
||||
|
||||
static ssize_t charge_types_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t count = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(battery_modes); i++) {
|
||||
bool active;
|
||||
|
||||
if (!(battery_supported_modes & BIT(i)))
|
||||
continue;
|
||||
|
||||
active = dell_battery_mode_is_active(battery_modes[i].token);
|
||||
count += sysfs_emit_at(buf, count, active ? "[%s] " : "%s ",
|
||||
battery_modes[i].label);
|
||||
}
|
||||
|
||||
/* convert the last space to a newline */
|
||||
if (count > 0)
|
||||
count--;
|
||||
count += sysfs_emit_at(buf, count, "\n");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t charge_types_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
bool matched = false;
|
||||
int err, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(battery_modes); i++) {
|
||||
if (!(battery_supported_modes & BIT(i)))
|
||||
continue;
|
||||
|
||||
if (sysfs_streq(battery_modes[i].label, buf)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched)
|
||||
return -EINVAL;
|
||||
|
||||
err = dell_battery_set_mode(battery_modes[i].token);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t charge_control_start_threshold_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int start;
|
||||
|
||||
start = dell_battery_read(BAT_CUSTOM_CHARGE_START);
|
||||
if (start < 0)
|
||||
return start;
|
||||
|
||||
if (start > CHARGE_START_MAX)
|
||||
return -EIO;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", start);
|
||||
}
|
||||
|
||||
static ssize_t charge_control_start_threshold_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
int ret, start;
|
||||
|
||||
ret = kstrtoint(buf, 10, &start);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (start < 0 || start > 100)
|
||||
return -EINVAL;
|
||||
|
||||
ret = dell_battery_set_custom_charge_start(start);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t charge_control_end_threshold_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int end;
|
||||
|
||||
end = dell_battery_read(BAT_CUSTOM_CHARGE_END);
|
||||
if (end < 0)
|
||||
return end;
|
||||
|
||||
if (end > CHARGE_END_MAX)
|
||||
return -EIO;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", end);
|
||||
}
|
||||
|
||||
static ssize_t charge_control_end_threshold_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
int ret, end;
|
||||
|
||||
ret = kstrtouint(buf, 10, &end);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (end < 0 || end > 100)
|
||||
return -EINVAL;
|
||||
|
||||
ret = dell_battery_set_custom_charge_end(end);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(charge_control_start_threshold);
|
||||
static DEVICE_ATTR_RW(charge_control_end_threshold);
|
||||
static DEVICE_ATTR_RW(charge_types);
|
||||
|
||||
static struct attribute *dell_battery_attrs[] = {
|
||||
&dev_attr_charge_control_start_threshold.attr,
|
||||
&dev_attr_charge_control_end_threshold.attr,
|
||||
&dev_attr_charge_types.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(dell_battery);
|
||||
|
||||
static int dell_battery_add(struct power_supply *battery,
|
||||
struct acpi_battery_hook *hook)
|
||||
{
|
||||
/* this currently only supports the primary battery */
|
||||
if (strcmp(battery->desc->name, "BAT0") != 0)
|
||||
return -ENODEV;
|
||||
|
||||
return device_add_groups(&battery->dev, dell_battery_groups);
|
||||
}
|
||||
|
||||
static int dell_battery_remove(struct power_supply *battery,
|
||||
struct acpi_battery_hook *hook)
|
||||
{
|
||||
device_remove_groups(&battery->dev, dell_battery_groups);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct acpi_battery_hook dell_battery_hook = {
|
||||
.add_battery = dell_battery_add,
|
||||
.remove_battery = dell_battery_remove,
|
||||
.name = "Dell Primary Battery Extension",
|
||||
};
|
||||
|
||||
static u32 __init battery_get_supported_modes(void)
|
||||
{
|
||||
u32 modes = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(battery_modes); i++) {
|
||||
if (dell_smbios_find_token(battery_modes[i].token))
|
||||
modes |= BIT(i);
|
||||
}
|
||||
|
||||
return modes;
|
||||
}
|
||||
|
||||
static void __init dell_battery_init(struct device *dev)
|
||||
{
|
||||
battery_supported_modes = battery_get_supported_modes();
|
||||
|
||||
if (battery_supported_modes != 0)
|
||||
battery_hook_register(&dell_battery_hook);
|
||||
}
|
||||
|
||||
static void dell_battery_exit(void)
|
||||
{
|
||||
if (battery_supported_modes != 0)
|
||||
battery_hook_unregister(&dell_battery_hook);
|
||||
}
|
||||
|
||||
static int __init dell_init(void)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
int max_intensity = 0;
|
||||
int ret;
|
||||
|
||||
@ -2219,6 +2477,7 @@ static int __init dell_init(void)
|
||||
touchpad_led_init(&platform_device->dev);
|
||||
|
||||
kbd_led_init(&platform_device->dev);
|
||||
dell_battery_init(&platform_device->dev);
|
||||
|
||||
dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
|
||||
debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
|
||||
@ -2246,16 +2505,10 @@ static int __init dell_init(void)
|
||||
if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
|
||||
return 0;
|
||||
|
||||
token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
|
||||
if (token) {
|
||||
struct calling_interface_buffer buffer;
|
||||
|
||||
dell_fill_request(&buffer, token->location, 0, 0, 0);
|
||||
ret = dell_send_request(&buffer,
|
||||
CLASS_TOKEN_READ, SELECT_TOKEN_AC);
|
||||
if (ret == 0)
|
||||
max_intensity = buffer.output[3];
|
||||
}
|
||||
ret = dell_send_request_for_tokenid(&buffer, CLASS_TOKEN_READ,
|
||||
SELECT_TOKEN_AC, BRIGHTNESS_TOKEN, 0);
|
||||
if (ret == 0)
|
||||
max_intensity = buffer.output[3];
|
||||
|
||||
if (max_intensity) {
|
||||
struct backlight_properties props;
|
||||
@ -2293,6 +2546,7 @@ fail_backlight:
|
||||
if (mute_led_registered)
|
||||
led_classdev_unregister(&mute_led_cdev);
|
||||
fail_led:
|
||||
dell_battery_exit();
|
||||
dell_cleanup_rfkill();
|
||||
fail_rfkill:
|
||||
platform_device_del(platform_device);
|
||||
@ -2311,6 +2565,7 @@ static void __exit dell_exit(void)
|
||||
if (quirks && quirks->touchpad_led)
|
||||
touchpad_led_exit();
|
||||
kbd_led_exit();
|
||||
dell_battery_exit();
|
||||
backlight_device_unregister(dell_backlight_device);
|
||||
if (micmute_led_registered)
|
||||
led_classdev_unregister(&micmute_led_cdev);
|
||||
|
@ -33,6 +33,13 @@
|
||||
#define KBD_LED_AUTO_50_TOKEN 0x02EB
|
||||
#define KBD_LED_AUTO_75_TOKEN 0x02EC
|
||||
#define KBD_LED_AUTO_100_TOKEN 0x02F6
|
||||
#define BAT_PRI_AC_MODE_TOKEN 0x0341
|
||||
#define BAT_ADAPTIVE_MODE_TOKEN 0x0342
|
||||
#define BAT_CUSTOM_MODE_TOKEN 0x0343
|
||||
#define BAT_STANDARD_MODE_TOKEN 0x0346
|
||||
#define BAT_EXPRESS_MODE_TOKEN 0x0347
|
||||
#define BAT_CUSTOM_CHARGE_START 0x0349
|
||||
#define BAT_CUSTOM_CHARGE_END 0x034A
|
||||
#define GLOBAL_MIC_MUTE_ENABLE 0x0364
|
||||
#define GLOBAL_MIC_MUTE_DISABLE 0x0365
|
||||
#define GLOBAL_MUTE_ENABLE 0x058C
|
||||
|
@ -70,20 +70,10 @@ static bool dell_wmi_aio_event_check(u8 *buffer, int length)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void dell_wmi_aio_notify(u32 value, void *context)
|
||||
static void dell_wmi_aio_notify(union acpi_object *obj, void *context)
|
||||
{
|
||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
struct dell_wmi_event *event;
|
||||
acpi_status status;
|
||||
|
||||
status = wmi_get_event_data(value, &response);
|
||||
if (status != AE_OK) {
|
||||
pr_info("bad event status 0x%x\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)response.pointer;
|
||||
if (obj) {
|
||||
unsigned int scancode = 0;
|
||||
|
||||
@ -114,7 +104,6 @@ static void dell_wmi_aio_notify(u32 value, void *context)
|
||||
break;
|
||||
}
|
||||
}
|
||||
kfree(obj);
|
||||
}
|
||||
|
||||
static int __init dell_wmi_aio_input_setup(void)
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/slab.h>
|
||||
@ -1137,7 +1136,7 @@ static int eeepc_backlight_init(struct eeepc_laptop *eeepc)
|
||||
}
|
||||
eeepc->backlight_device = bd;
|
||||
bd->props.brightness = read_brightness(bd);
|
||||
bd->props.power = FB_BLANK_UNBLANK;
|
||||
bd->props.power = BACKLIGHT_POWER_ON;
|
||||
backlight_update_status(bd);
|
||||
return 0;
|
||||
}
|
||||
|
@ -13,13 +13,13 @@
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include "asus-wmi.h"
|
||||
@ -192,7 +192,7 @@ static void eeepc_wmi_quirks(struct asus_wmi_driver *driver)
|
||||
|
||||
driver->quirks = quirks;
|
||||
driver->quirks->wapf = -1;
|
||||
driver->panel_power = FB_BLANK_UNBLANK;
|
||||
driver->panel_power = BACKLIGHT_POWER_ON;
|
||||
}
|
||||
|
||||
static struct asus_wmi_driver asus_wmi_driver = {
|
||||
|
@ -43,7 +43,6 @@
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/kfifo.h>
|
||||
@ -356,7 +355,7 @@ static int bl_get_brightness(struct backlight_device *b)
|
||||
{
|
||||
struct acpi_device *device = bl_get_data(b);
|
||||
|
||||
return b->props.power == FB_BLANK_POWERDOWN ? 0 : get_lcd_level(device);
|
||||
return b->props.power == BACKLIGHT_POWER_OFF ? 0 : get_lcd_level(device);
|
||||
}
|
||||
|
||||
static int bl_update_status(struct backlight_device *b)
|
||||
@ -364,7 +363,7 @@ static int bl_update_status(struct backlight_device *b)
|
||||
struct acpi_device *device = bl_get_data(b);
|
||||
|
||||
if (fext) {
|
||||
if (b->props.power == FB_BLANK_POWERDOWN)
|
||||
if (b->props.power == BACKLIGHT_POWER_OFF)
|
||||
call_fext_func(fext, FUNC_BACKLIGHT, 0x1,
|
||||
BACKLIGHT_PARAM_POWER, BACKLIGHT_OFF);
|
||||
else
|
||||
@ -933,9 +932,9 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
|
||||
acpi_video_get_backlight_type() == acpi_backlight_vendor) {
|
||||
if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2,
|
||||
BACKLIGHT_PARAM_POWER, 0x0) == BACKLIGHT_OFF)
|
||||
fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN;
|
||||
fujitsu_bl->bl_device->props.power = BACKLIGHT_POWER_OFF;
|
||||
else
|
||||
fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK;
|
||||
fujitsu_bl->bl_device->props.power = BACKLIGHT_POWER_ON;
|
||||
}
|
||||
|
||||
ret = acpi_fujitsu_laptop_input_setup(device);
|
||||
|
@ -834,28 +834,16 @@ static struct attribute *hp_wmi_attrs[] = {
|
||||
};
|
||||
ATTRIBUTE_GROUPS(hp_wmi);
|
||||
|
||||
static void hp_wmi_notify(u32 value, void *context)
|
||||
static void hp_wmi_notify(union acpi_object *obj, void *context)
|
||||
{
|
||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
u32 event_id, event_data;
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
u32 *location;
|
||||
int key_code;
|
||||
|
||||
status = wmi_get_event_data(value, &response);
|
||||
if (status != AE_OK) {
|
||||
pr_info("bad event status 0x%x\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)response.pointer;
|
||||
|
||||
if (!obj)
|
||||
return;
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
pr_info("Unknown response received %d\n", obj->type);
|
||||
kfree(obj);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -872,10 +860,8 @@ static void hp_wmi_notify(u32 value, void *context)
|
||||
event_data = *(location + 2);
|
||||
} else {
|
||||
pr_info("Unknown buffer length %d\n", obj->buffer.length);
|
||||
kfree(obj);
|
||||
return;
|
||||
}
|
||||
kfree(obj);
|
||||
|
||||
switch (event_id) {
|
||||
case HPWMI_DOCK_EVENT:
|
||||
|
@ -734,26 +734,14 @@ static void huawei_wmi_process_key(struct input_dev *idev, int code)
|
||||
sparse_keymap_report_entry(idev, key, 1, true);
|
||||
}
|
||||
|
||||
static void huawei_wmi_input_notify(u32 value, void *context)
|
||||
static void huawei_wmi_input_notify(union acpi_object *obj, void *context)
|
||||
{
|
||||
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_err(&idev->dev, "Bad response type\n");
|
||||
|
||||
kfree(response.pointer);
|
||||
}
|
||||
|
||||
static int huawei_wmi_input_setup(struct device *dev, const char *guid)
|
||||
|
@ -17,11 +17,11 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/i8042.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
@ -87,6 +87,34 @@ enum {
|
||||
SALS_FNLOCK_OFF = 0xf,
|
||||
};
|
||||
|
||||
enum {
|
||||
VPCCMD_R_VPC1 = 0x10,
|
||||
VPCCMD_R_BL_MAX,
|
||||
VPCCMD_R_BL,
|
||||
VPCCMD_W_BL,
|
||||
VPCCMD_R_WIFI,
|
||||
VPCCMD_W_WIFI,
|
||||
VPCCMD_R_BT,
|
||||
VPCCMD_W_BT,
|
||||
VPCCMD_R_BL_POWER,
|
||||
VPCCMD_R_NOVO,
|
||||
VPCCMD_R_VPC2,
|
||||
VPCCMD_R_TOUCHPAD,
|
||||
VPCCMD_W_TOUCHPAD,
|
||||
VPCCMD_R_CAMERA,
|
||||
VPCCMD_W_CAMERA,
|
||||
VPCCMD_R_3G,
|
||||
VPCCMD_W_3G,
|
||||
VPCCMD_R_ODD, /* 0x21 */
|
||||
VPCCMD_W_FAN,
|
||||
VPCCMD_R_RF,
|
||||
VPCCMD_W_RF,
|
||||
VPCCMD_W_YMC = 0x2A,
|
||||
VPCCMD_R_FAN = 0x2B,
|
||||
VPCCMD_R_SPECIAL_BUTTONS = 0x31,
|
||||
VPCCMD_W_BL_POWER = 0x33,
|
||||
};
|
||||
|
||||
/*
|
||||
* These correspond to the number of supported states - 1
|
||||
* Future keyboard types may need a new system, if there's a collision
|
||||
@ -237,6 +265,7 @@ static void ideapad_shared_exit(struct ideapad_private *priv)
|
||||
/*
|
||||
* ACPI Helpers
|
||||
*/
|
||||
#define IDEAPAD_EC_TIMEOUT 200 /* in ms */
|
||||
|
||||
static int eval_int(acpi_handle handle, const char *name, unsigned long *res)
|
||||
{
|
||||
@ -252,6 +281,29 @@ static int eval_int(acpi_handle handle, const char *name, unsigned long *res)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eval_int_with_arg(acpi_handle handle, const char *name, unsigned long arg,
|
||||
unsigned long *res)
|
||||
{
|
||||
struct acpi_object_list params;
|
||||
unsigned long long result;
|
||||
union acpi_object in_obj;
|
||||
acpi_status status;
|
||||
|
||||
params.count = 1;
|
||||
params.pointer = &in_obj;
|
||||
in_obj.type = ACPI_TYPE_INTEGER;
|
||||
in_obj.integer.value = arg;
|
||||
|
||||
status = acpi_evaluate_integer(handle, (char *)name, ¶ms, &result);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
if (res)
|
||||
*res = result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exec_simple_method(acpi_handle handle, const char *name, unsigned long arg)
|
||||
{
|
||||
acpi_status status = acpi_execute_simple_method(handle, (char *)name, arg);
|
||||
@ -294,6 +346,89 @@ static int eval_dytc(acpi_handle handle, unsigned long cmd, unsigned long *res)
|
||||
return eval_int_with_arg(handle, "DYTC", cmd, res);
|
||||
}
|
||||
|
||||
static int eval_vpcr(acpi_handle handle, unsigned long cmd, unsigned long *res)
|
||||
{
|
||||
return eval_int_with_arg(handle, "VPCR", cmd, res);
|
||||
}
|
||||
|
||||
static int eval_vpcw(acpi_handle handle, unsigned long cmd, unsigned long data)
|
||||
{
|
||||
struct acpi_object_list params;
|
||||
union acpi_object in_obj[2];
|
||||
acpi_status status;
|
||||
|
||||
params.count = 2;
|
||||
params.pointer = in_obj;
|
||||
in_obj[0].type = ACPI_TYPE_INTEGER;
|
||||
in_obj[0].integer.value = cmd;
|
||||
in_obj[1].type = ACPI_TYPE_INTEGER;
|
||||
in_obj[1].integer.value = data;
|
||||
|
||||
status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *data)
|
||||
{
|
||||
unsigned long end_jiffies, val;
|
||||
int err;
|
||||
|
||||
err = eval_vpcw(handle, 1, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
|
||||
|
||||
while (time_before(jiffies, end_jiffies)) {
|
||||
schedule();
|
||||
|
||||
err = eval_vpcr(handle, 1, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (val == 0)
|
||||
return eval_vpcr(handle, 0, data);
|
||||
}
|
||||
|
||||
acpi_handle_err(handle, "timeout in %s\n", __func__);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long data)
|
||||
{
|
||||
unsigned long end_jiffies, val;
|
||||
int err;
|
||||
|
||||
err = eval_vpcw(handle, 0, data);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = eval_vpcw(handle, 1, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
|
||||
|
||||
while (time_before(jiffies, end_jiffies)) {
|
||||
schedule();
|
||||
|
||||
err = eval_vpcr(handle, 1, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (val == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
acpi_handle_err(handle, "timeout in %s\n", __func__);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* debugfs
|
||||
*/
|
||||
@ -419,13 +554,14 @@ static ssize_t camera_power_show(struct device *dev,
|
||||
char *buf)
|
||||
{
|
||||
struct ideapad_private *priv = dev_get_drvdata(dev);
|
||||
unsigned long result;
|
||||
unsigned long result = 0;
|
||||
int err;
|
||||
|
||||
scoped_guard(mutex, &priv->vpc_mutex)
|
||||
scoped_guard(mutex, &priv->vpc_mutex) {
|
||||
err = read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result);
|
||||
if (err)
|
||||
return err;
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return sysfs_emit(buf, "%d\n", !!result);
|
||||
}
|
||||
@ -442,10 +578,11 @@ static ssize_t camera_power_store(struct device *dev,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
scoped_guard(mutex, &priv->vpc_mutex)
|
||||
scoped_guard(mutex, &priv->vpc_mutex) {
|
||||
err = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);
|
||||
if (err)
|
||||
return err;
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
@ -493,13 +630,14 @@ static ssize_t fan_mode_show(struct device *dev,
|
||||
char *buf)
|
||||
{
|
||||
struct ideapad_private *priv = dev_get_drvdata(dev);
|
||||
unsigned long result;
|
||||
unsigned long result = 0;
|
||||
int err;
|
||||
|
||||
scoped_guard(mutex, &priv->vpc_mutex)
|
||||
scoped_guard(mutex, &priv->vpc_mutex) {
|
||||
err = read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result);
|
||||
if (err)
|
||||
return err;
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return sysfs_emit(buf, "%lu\n", result);
|
||||
}
|
||||
@ -519,10 +657,11 @@ static ssize_t fan_mode_store(struct device *dev,
|
||||
if (state > 4 || state == 3)
|
||||
return -EINVAL;
|
||||
|
||||
scoped_guard(mutex, &priv->vpc_mutex)
|
||||
scoped_guard(mutex, &priv->vpc_mutex) {
|
||||
err = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);
|
||||
if (err)
|
||||
return err;
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
@ -602,13 +741,14 @@ static ssize_t touchpad_show(struct device *dev,
|
||||
char *buf)
|
||||
{
|
||||
struct ideapad_private *priv = dev_get_drvdata(dev);
|
||||
unsigned long result;
|
||||
unsigned long result = 0;
|
||||
int err;
|
||||
|
||||
scoped_guard(mutex, &priv->vpc_mutex)
|
||||
scoped_guard(mutex, &priv->vpc_mutex) {
|
||||
err = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result);
|
||||
if (err)
|
||||
return err;
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
priv->r_touchpad_val = result;
|
||||
|
||||
@ -627,10 +767,11 @@ static ssize_t touchpad_store(struct device *dev,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
scoped_guard(mutex, &priv->vpc_mutex)
|
||||
scoped_guard(mutex, &priv->vpc_mutex) {
|
||||
err = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state);
|
||||
if (err)
|
||||
return err;
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
priv->r_touchpad_val = state;
|
||||
|
||||
@ -1282,7 +1423,7 @@ static int ideapad_backlight_update_status(struct backlight_device *blightdev)
|
||||
return err;
|
||||
|
||||
err = write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER,
|
||||
blightdev->props.power != FB_BLANK_POWERDOWN);
|
||||
blightdev->props.power != BACKLIGHT_POWER_OFF);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -1332,7 +1473,7 @@ static int ideapad_backlight_init(struct ideapad_private *priv)
|
||||
|
||||
priv->blightdev = blightdev;
|
||||
blightdev->props.brightness = now;
|
||||
blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
|
||||
blightdev->props.power = power ? BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF;
|
||||
|
||||
backlight_update_status(blightdev);
|
||||
|
||||
@ -1358,7 +1499,7 @@ static void ideapad_backlight_notify_power(struct ideapad_private *priv)
|
||||
if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
|
||||
return;
|
||||
|
||||
blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
|
||||
blightdev->props.power = power ? BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF;
|
||||
}
|
||||
|
||||
static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
|
||||
|
@ -9,9 +9,6 @@
|
||||
#ifndef _IDEAPAD_LAPTOP_H_
|
||||
#define _IDEAPAD_LAPTOP_H_
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
enum ideapad_laptop_notifier_actions {
|
||||
@ -22,140 +19,4 @@ int ideapad_laptop_register_notifier(struct notifier_block *nb);
|
||||
int ideapad_laptop_unregister_notifier(struct notifier_block *nb);
|
||||
void ideapad_laptop_call_notifier(unsigned long action, void *data);
|
||||
|
||||
enum {
|
||||
VPCCMD_R_VPC1 = 0x10,
|
||||
VPCCMD_R_BL_MAX,
|
||||
VPCCMD_R_BL,
|
||||
VPCCMD_W_BL,
|
||||
VPCCMD_R_WIFI,
|
||||
VPCCMD_W_WIFI,
|
||||
VPCCMD_R_BT,
|
||||
VPCCMD_W_BT,
|
||||
VPCCMD_R_BL_POWER,
|
||||
VPCCMD_R_NOVO,
|
||||
VPCCMD_R_VPC2,
|
||||
VPCCMD_R_TOUCHPAD,
|
||||
VPCCMD_W_TOUCHPAD,
|
||||
VPCCMD_R_CAMERA,
|
||||
VPCCMD_W_CAMERA,
|
||||
VPCCMD_R_3G,
|
||||
VPCCMD_W_3G,
|
||||
VPCCMD_R_ODD, /* 0x21 */
|
||||
VPCCMD_W_FAN,
|
||||
VPCCMD_R_RF,
|
||||
VPCCMD_W_RF,
|
||||
VPCCMD_W_YMC = 0x2A,
|
||||
VPCCMD_R_FAN = 0x2B,
|
||||
VPCCMD_R_SPECIAL_BUTTONS = 0x31,
|
||||
VPCCMD_W_BL_POWER = 0x33,
|
||||
};
|
||||
|
||||
static inline int eval_int_with_arg(acpi_handle handle, const char *name, unsigned long arg, unsigned long *res)
|
||||
{
|
||||
struct acpi_object_list params;
|
||||
unsigned long long result;
|
||||
union acpi_object in_obj;
|
||||
acpi_status status;
|
||||
|
||||
params.count = 1;
|
||||
params.pointer = &in_obj;
|
||||
in_obj.type = ACPI_TYPE_INTEGER;
|
||||
in_obj.integer.value = arg;
|
||||
|
||||
status = acpi_evaluate_integer(handle, (char *)name, ¶ms, &result);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
if (res)
|
||||
*res = result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int eval_vpcr(acpi_handle handle, unsigned long cmd, unsigned long *res)
|
||||
{
|
||||
return eval_int_with_arg(handle, "VPCR", cmd, res);
|
||||
}
|
||||
|
||||
static inline int eval_vpcw(acpi_handle handle, unsigned long cmd, unsigned long data)
|
||||
{
|
||||
struct acpi_object_list params;
|
||||
union acpi_object in_obj[2];
|
||||
acpi_status status;
|
||||
|
||||
params.count = 2;
|
||||
params.pointer = in_obj;
|
||||
in_obj[0].type = ACPI_TYPE_INTEGER;
|
||||
in_obj[0].integer.value = cmd;
|
||||
in_obj[1].type = ACPI_TYPE_INTEGER;
|
||||
in_obj[1].integer.value = data;
|
||||
|
||||
status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define IDEAPAD_EC_TIMEOUT 200 /* in ms */
|
||||
|
||||
static inline int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *data)
|
||||
{
|
||||
unsigned long end_jiffies, val;
|
||||
int err;
|
||||
|
||||
err = eval_vpcw(handle, 1, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
|
||||
|
||||
while (time_before(jiffies, end_jiffies)) {
|
||||
schedule();
|
||||
|
||||
err = eval_vpcr(handle, 1, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (val == 0)
|
||||
return eval_vpcr(handle, 0, data);
|
||||
}
|
||||
|
||||
acpi_handle_err(handle, "timeout in %s\n", __func__);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static inline int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long data)
|
||||
{
|
||||
unsigned long end_jiffies, val;
|
||||
int err;
|
||||
|
||||
err = eval_vpcw(handle, 0, data);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = eval_vpcw(handle, 1, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
|
||||
|
||||
while (time_before(jiffies, end_jiffies)) {
|
||||
schedule();
|
||||
|
||||
err = eval_vpcr(handle, 1, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (val == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
acpi_handle_err(handle, "timeout in %s\n", __func__);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
#undef IDEAPAD_EC_TIMEOUT
|
||||
#endif /* !_IDEAPAD_LAPTOP_H_ */
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/string_choices.h>
|
||||
#include <linux/suspend.h>
|
||||
#include "../dual_accel_detect.h"
|
||||
|
||||
@ -331,10 +332,8 @@ static int intel_hid_set_enable(struct device *device, bool enable)
|
||||
acpi_handle handle = ACPI_HANDLE(device);
|
||||
|
||||
/* Enable|disable features - power button is always enabled */
|
||||
if (!intel_hid_execute_method(handle, INTEL_HID_DSM_HDSM_FN,
|
||||
enable)) {
|
||||
dev_warn(device, "failed to %sable hotkeys\n",
|
||||
enable ? "en" : "dis");
|
||||
if (!intel_hid_execute_method(handle, INTEL_HID_DSM_HDSM_FN, enable)) {
|
||||
dev_warn(device, "failed to %s hotkeys\n", str_enable_disable(enable));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ bool *ifs_pkg_auth;
|
||||
static const struct ifs_test_caps scan_test = {
|
||||
.integrity_cap_bit = MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT,
|
||||
.test_num = IFS_TYPE_SAF,
|
||||
.image_suffix = "scan",
|
||||
};
|
||||
|
||||
static const struct ifs_test_caps array_test = {
|
||||
@ -39,9 +40,32 @@ static const struct ifs_test_caps array_test = {
|
||||
.test_num = IFS_TYPE_ARRAY_BIST,
|
||||
};
|
||||
|
||||
static const struct ifs_test_msrs scan_msrs = {
|
||||
.copy_hashes = MSR_COPY_SCAN_HASHES,
|
||||
.copy_hashes_status = MSR_SCAN_HASHES_STATUS,
|
||||
.copy_chunks = MSR_AUTHENTICATE_AND_COPY_CHUNK,
|
||||
.copy_chunks_status = MSR_CHUNKS_AUTHENTICATION_STATUS,
|
||||
.test_ctrl = MSR_SAF_CTRL,
|
||||
};
|
||||
|
||||
static const struct ifs_test_msrs sbaf_msrs = {
|
||||
.copy_hashes = MSR_COPY_SBAF_HASHES,
|
||||
.copy_hashes_status = MSR_SBAF_HASHES_STATUS,
|
||||
.copy_chunks = MSR_AUTHENTICATE_AND_COPY_SBAF_CHUNK,
|
||||
.copy_chunks_status = MSR_SBAF_CHUNKS_AUTHENTICATION_STATUS,
|
||||
.test_ctrl = MSR_SBAF_CTRL,
|
||||
};
|
||||
|
||||
static const struct ifs_test_caps sbaf_test = {
|
||||
.integrity_cap_bit = MSR_INTEGRITY_CAPS_SBAF_BIT,
|
||||
.test_num = IFS_TYPE_SBAF,
|
||||
.image_suffix = "sbft",
|
||||
};
|
||||
|
||||
static struct ifs_device ifs_devices[] = {
|
||||
[IFS_TYPE_SAF] = {
|
||||
.test_caps = &scan_test,
|
||||
.test_msrs = &scan_msrs,
|
||||
.misc = {
|
||||
.name = "intel_ifs_0",
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
@ -56,6 +80,15 @@ static struct ifs_device ifs_devices[] = {
|
||||
.groups = plat_ifs_array_groups,
|
||||
},
|
||||
},
|
||||
[IFS_TYPE_SBAF] = {
|
||||
.test_caps = &sbaf_test,
|
||||
.test_msrs = &sbaf_msrs,
|
||||
.misc = {
|
||||
.name = "intel_ifs_2",
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.groups = plat_ifs_groups,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
#define IFS_NUMTESTS ARRAY_SIZE(ifs_devices)
|
||||
|
@ -126,11 +126,40 @@
|
||||
* The driver does not make use of this, it only tests one core at a time.
|
||||
*
|
||||
* .. [#f1] https://github.com/intel/TBD
|
||||
*
|
||||
*
|
||||
* Structural Based Functional Test at Field (SBAF):
|
||||
* -------------------------------------------------
|
||||
*
|
||||
* SBAF is a new type of testing that provides comprehensive core test
|
||||
* coverage complementing Scan at Field (SAF) testing. SBAF mimics the
|
||||
* manufacturing screening environment and leverages the same test suite.
|
||||
* It makes use of Design For Test (DFT) observation sites and features
|
||||
* to maximize coverage in minimum time.
|
||||
*
|
||||
* Similar to the SAF test, SBAF isolates the core under test from the
|
||||
* rest of the system during execution. Upon completion, the core
|
||||
* seamlessly resets to its pre-test state and resumes normal operation.
|
||||
* Any machine checks or hangs encountered during the test are confined to
|
||||
* the isolated core, preventing disruption to the overall system.
|
||||
*
|
||||
* Like the SAF test, the SBAF test is also divided into multiple batches,
|
||||
* and each batch test can take hundreds of milliseconds (100-200 ms) to
|
||||
* complete. If such a lengthy interruption is undesirable, it is
|
||||
* recommended to relocate the time-sensitive applications to other cores.
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/miscdevice.h>
|
||||
|
||||
#define MSR_ARRAY_BIST 0x00000105
|
||||
|
||||
#define MSR_COPY_SBAF_HASHES 0x000002b8
|
||||
#define MSR_SBAF_HASHES_STATUS 0x000002b9
|
||||
#define MSR_AUTHENTICATE_AND_COPY_SBAF_CHUNK 0x000002ba
|
||||
#define MSR_SBAF_CHUNKS_AUTHENTICATION_STATUS 0x000002bb
|
||||
#define MSR_ACTIVATE_SBAF 0x000002bc
|
||||
#define MSR_SBAF_STATUS 0x000002bd
|
||||
|
||||
#define MSR_COPY_SCAN_HASHES 0x000002c2
|
||||
#define MSR_SCAN_HASHES_STATUS 0x000002c3
|
||||
#define MSR_AUTHENTICATE_AND_COPY_CHUNK 0x000002c4
|
||||
@ -140,6 +169,7 @@
|
||||
#define MSR_ARRAY_TRIGGER 0x000002d6
|
||||
#define MSR_ARRAY_STATUS 0x000002d7
|
||||
#define MSR_SAF_CTRL 0x000004f0
|
||||
#define MSR_SBAF_CTRL 0x000004f8
|
||||
|
||||
#define SCAN_NOT_TESTED 0
|
||||
#define SCAN_TEST_PASS 1
|
||||
@ -147,6 +177,7 @@
|
||||
|
||||
#define IFS_TYPE_SAF 0
|
||||
#define IFS_TYPE_ARRAY_BIST 1
|
||||
#define IFS_TYPE_SBAF 2
|
||||
|
||||
#define ARRAY_GEN0 0
|
||||
#define ARRAY_GEN1 1
|
||||
@ -196,7 +227,8 @@ union ifs_chunks_auth_status_gen2 {
|
||||
u16 valid_chunks;
|
||||
u16 total_chunks;
|
||||
u32 error_code :8;
|
||||
u32 rsvd2 :24;
|
||||
u32 rsvd2 :8;
|
||||
u32 max_bundle :16;
|
||||
};
|
||||
};
|
||||
|
||||
@ -253,6 +285,34 @@ union ifs_array {
|
||||
};
|
||||
};
|
||||
|
||||
/* MSR_ACTIVATE_SBAF bit fields */
|
||||
union ifs_sbaf {
|
||||
u64 data;
|
||||
struct {
|
||||
u32 bundle_idx :9;
|
||||
u32 rsvd1 :5;
|
||||
u32 pgm_idx :2;
|
||||
u32 rsvd2 :16;
|
||||
u32 delay :31;
|
||||
u32 sigmce :1;
|
||||
};
|
||||
};
|
||||
|
||||
/* MSR_SBAF_STATUS bit fields */
|
||||
union ifs_sbaf_status {
|
||||
u64 data;
|
||||
struct {
|
||||
u32 bundle_idx :9;
|
||||
u32 rsvd1 :5;
|
||||
u32 pgm_idx :2;
|
||||
u32 rsvd2 :16;
|
||||
u32 error_code :8;
|
||||
u32 rsvd3 :21;
|
||||
u32 test_fail :1;
|
||||
u32 sbaf_status :2;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Driver populated error-codes
|
||||
* 0xFD: Test timed out before completing all the chunks.
|
||||
@ -261,9 +321,28 @@ union ifs_array {
|
||||
#define IFS_SW_TIMEOUT 0xFD
|
||||
#define IFS_SW_PARTIAL_COMPLETION 0xFE
|
||||
|
||||
#define IFS_SUFFIX_SZ 5
|
||||
|
||||
struct ifs_test_caps {
|
||||
int integrity_cap_bit;
|
||||
int test_num;
|
||||
char image_suffix[IFS_SUFFIX_SZ];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ifs_test_msrs - MSRs used in IFS tests
|
||||
* @copy_hashes: Copy test hash data
|
||||
* @copy_hashes_status: Status of copied test hash data
|
||||
* @copy_chunks: Copy chunks of the test data
|
||||
* @copy_chunks_status: Status of the copied test data chunks
|
||||
* @test_ctrl: Control the test attributes
|
||||
*/
|
||||
struct ifs_test_msrs {
|
||||
u32 copy_hashes;
|
||||
u32 copy_hashes_status;
|
||||
u32 copy_chunks;
|
||||
u32 copy_chunks_status;
|
||||
u32 test_ctrl;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -278,6 +357,7 @@ struct ifs_test_caps {
|
||||
* @generation: IFS test generation enumerated by hardware
|
||||
* @chunk_size: size of a test chunk
|
||||
* @array_gen: test generation of array test
|
||||
* @max_bundle: maximum bundle index
|
||||
*/
|
||||
struct ifs_data {
|
||||
int loaded_version;
|
||||
@ -290,6 +370,7 @@ struct ifs_data {
|
||||
u32 generation;
|
||||
u32 chunk_size;
|
||||
u32 array_gen;
|
||||
u32 max_bundle;
|
||||
};
|
||||
|
||||
struct ifs_work {
|
||||
@ -299,6 +380,7 @@ struct ifs_work {
|
||||
|
||||
struct ifs_device {
|
||||
const struct ifs_test_caps *test_caps;
|
||||
const struct ifs_test_msrs *test_msrs;
|
||||
struct ifs_data rw_data;
|
||||
struct miscdevice misc;
|
||||
};
|
||||
@ -319,6 +401,14 @@ static inline const struct ifs_test_caps *ifs_get_test_caps(struct device *dev)
|
||||
return d->test_caps;
|
||||
}
|
||||
|
||||
static inline const struct ifs_test_msrs *ifs_get_test_msrs(struct device *dev)
|
||||
{
|
||||
struct miscdevice *m = dev_get_drvdata(dev);
|
||||
struct ifs_device *d = container_of(m, struct ifs_device, misc);
|
||||
|
||||
return d->test_msrs;
|
||||
}
|
||||
|
||||
extern bool *ifs_pkg_auth;
|
||||
int ifs_load_firmware(struct device *dev);
|
||||
int do_core_test(int cpu, struct device *dev);
|
||||
|
@ -118,15 +118,17 @@ static void copy_hashes_authenticate_chunks(struct work_struct *work)
|
||||
union ifs_scan_hashes_status hashes_status;
|
||||
union ifs_chunks_auth_status chunk_status;
|
||||
struct device *dev = local_work->dev;
|
||||
const struct ifs_test_msrs *msrs;
|
||||
int i, num_chunks, chunk_size;
|
||||
struct ifs_data *ifsd;
|
||||
u64 linear_addr, base;
|
||||
u32 err_code;
|
||||
|
||||
ifsd = ifs_get_data(dev);
|
||||
msrs = ifs_get_test_msrs(dev);
|
||||
/* run scan hash copy */
|
||||
wrmsrl(MSR_COPY_SCAN_HASHES, ifs_hash_ptr);
|
||||
rdmsrl(MSR_SCAN_HASHES_STATUS, hashes_status.data);
|
||||
wrmsrl(msrs->copy_hashes, ifs_hash_ptr);
|
||||
rdmsrl(msrs->copy_hashes_status, hashes_status.data);
|
||||
|
||||
/* enumerate the scan image information */
|
||||
num_chunks = hashes_status.num_chunks;
|
||||
@ -147,8 +149,8 @@ static void copy_hashes_authenticate_chunks(struct work_struct *work)
|
||||
linear_addr = base + i * chunk_size;
|
||||
linear_addr |= i;
|
||||
|
||||
wrmsrl(MSR_AUTHENTICATE_AND_COPY_CHUNK, linear_addr);
|
||||
rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data);
|
||||
wrmsrl(msrs->copy_chunks, linear_addr);
|
||||
rdmsrl(msrs->copy_chunks_status, chunk_status.data);
|
||||
|
||||
ifsd->valid_chunks = chunk_status.valid_chunks;
|
||||
err_code = chunk_status.error_code;
|
||||
@ -180,6 +182,7 @@ static int copy_hashes_authenticate_chunks_gen2(struct device *dev)
|
||||
union ifs_scan_hashes_status_gen2 hashes_status;
|
||||
union ifs_chunks_auth_status_gen2 chunk_status;
|
||||
u32 err_code, valid_chunks, total_chunks;
|
||||
const struct ifs_test_msrs *msrs;
|
||||
int i, num_chunks, chunk_size;
|
||||
union meta_data *ifs_meta;
|
||||
int starting_chunk_nr;
|
||||
@ -189,10 +192,11 @@ static int copy_hashes_authenticate_chunks_gen2(struct device *dev)
|
||||
int retry_count;
|
||||
|
||||
ifsd = ifs_get_data(dev);
|
||||
msrs = ifs_get_test_msrs(dev);
|
||||
|
||||
if (need_copy_scan_hashes(ifsd)) {
|
||||
wrmsrl(MSR_COPY_SCAN_HASHES, ifs_hash_ptr);
|
||||
rdmsrl(MSR_SCAN_HASHES_STATUS, hashes_status.data);
|
||||
wrmsrl(msrs->copy_hashes, ifs_hash_ptr);
|
||||
rdmsrl(msrs->copy_hashes_status, hashes_status.data);
|
||||
|
||||
/* enumerate the scan image information */
|
||||
chunk_size = hashes_status.chunk_size * SZ_1K;
|
||||
@ -212,8 +216,8 @@ static int copy_hashes_authenticate_chunks_gen2(struct device *dev)
|
||||
}
|
||||
|
||||
if (ifsd->generation >= IFS_GEN_STRIDE_AWARE) {
|
||||
wrmsrl(MSR_SAF_CTRL, INVALIDATE_STRIDE);
|
||||
rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data);
|
||||
wrmsrl(msrs->test_ctrl, INVALIDATE_STRIDE);
|
||||
rdmsrl(msrs->copy_chunks_status, chunk_status.data);
|
||||
if (chunk_status.valid_chunks != 0) {
|
||||
dev_err(dev, "Couldn't invalidate installed stride - %d\n",
|
||||
chunk_status.valid_chunks);
|
||||
@ -234,9 +238,9 @@ static int copy_hashes_authenticate_chunks_gen2(struct device *dev)
|
||||
chunk_table[1] = linear_addr;
|
||||
do {
|
||||
local_irq_disable();
|
||||
wrmsrl(MSR_AUTHENTICATE_AND_COPY_CHUNK, (u64)chunk_table);
|
||||
wrmsrl(msrs->copy_chunks, (u64)chunk_table);
|
||||
local_irq_enable();
|
||||
rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data);
|
||||
rdmsrl(msrs->copy_chunks_status, chunk_status.data);
|
||||
err_code = chunk_status.error_code;
|
||||
} while (err_code == AUTH_INTERRUPTED_ERROR && --retry_count);
|
||||
|
||||
@ -257,20 +261,22 @@ static int copy_hashes_authenticate_chunks_gen2(struct device *dev)
|
||||
return -EIO;
|
||||
}
|
||||
ifsd->valid_chunks = valid_chunks;
|
||||
ifsd->max_bundle = chunk_status.max_bundle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_ifs_metadata(struct device *dev)
|
||||
{
|
||||
const struct ifs_test_caps *test = ifs_get_test_caps(dev);
|
||||
struct ifs_data *ifsd = ifs_get_data(dev);
|
||||
union meta_data *ifs_meta;
|
||||
char test_file[64];
|
||||
int ret = -EINVAL;
|
||||
|
||||
snprintf(test_file, sizeof(test_file), "%02x-%02x-%02x-%02x.scan",
|
||||
snprintf(test_file, sizeof(test_file), "%02x-%02x-%02x-%02x.%s",
|
||||
boot_cpu_data.x86, boot_cpu_data.x86_model,
|
||||
boot_cpu_data.x86_stepping, ifsd->cur_batch);
|
||||
boot_cpu_data.x86_stepping, ifsd->cur_batch, test->image_suffix);
|
||||
|
||||
ifs_meta = (union meta_data *)find_meta_data(ifs_header_ptr, META_TYPE_IFS);
|
||||
if (!ifs_meta) {
|
||||
@ -300,6 +306,12 @@ static int validate_ifs_metadata(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ifs_meta->test_type != test->test_num) {
|
||||
dev_warn(dev, "Metadata test_type %d mismatches with device type\n",
|
||||
ifs_meta->test_type);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -387,9 +399,9 @@ int ifs_load_firmware(struct device *dev)
|
||||
char scan_path[64];
|
||||
int ret;
|
||||
|
||||
snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.scan",
|
||||
snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.%s",
|
||||
test->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model,
|
||||
boot_cpu_data.x86_stepping, ifsd->cur_batch);
|
||||
boot_cpu_data.x86_stepping, ifsd->cur_batch, test->image_suffix);
|
||||
|
||||
ret = request_firmware_direct(&fw, scan_path, dev);
|
||||
if (ret) {
|
||||
|
@ -29,6 +29,13 @@ struct run_params {
|
||||
union ifs_status status;
|
||||
};
|
||||
|
||||
struct sbaf_run_params {
|
||||
struct ifs_data *ifsd;
|
||||
int *retry_cnt;
|
||||
union ifs_sbaf *activate;
|
||||
union ifs_sbaf_status status;
|
||||
};
|
||||
|
||||
/*
|
||||
* Number of TSC cycles that a logical CPU will wait for the other
|
||||
* logical CPU on the core in the WRMSR(ACTIVATE_SCAN).
|
||||
@ -146,6 +153,7 @@ static bool can_restart(union ifs_status status)
|
||||
#define SPINUNIT 100 /* 100 nsec */
|
||||
static atomic_t array_cpus_in;
|
||||
static atomic_t scan_cpus_in;
|
||||
static atomic_t sbaf_cpus_in;
|
||||
|
||||
/*
|
||||
* Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus()
|
||||
@ -387,6 +395,225 @@ static void ifs_array_test_gen1(int cpu, struct device *dev)
|
||||
ifsd->status = SCAN_TEST_PASS;
|
||||
}
|
||||
|
||||
#define SBAF_STATUS_PASS 0
|
||||
#define SBAF_STATUS_SIGN_FAIL 1
|
||||
#define SBAF_STATUS_INTR 2
|
||||
#define SBAF_STATUS_TEST_FAIL 3
|
||||
|
||||
enum sbaf_status_err_code {
|
||||
IFS_SBAF_NO_ERROR = 0,
|
||||
IFS_SBAF_OTHER_THREAD_COULD_NOT_JOIN = 1,
|
||||
IFS_SBAF_INTERRUPTED_BEFORE_RENDEZVOUS = 2,
|
||||
IFS_SBAF_UNASSIGNED_ERROR_CODE3 = 3,
|
||||
IFS_SBAF_INVALID_BUNDLE_INDEX = 4,
|
||||
IFS_SBAF_MISMATCH_ARGS_BETWEEN_THREADS = 5,
|
||||
IFS_SBAF_CORE_NOT_CAPABLE_CURRENTLY = 6,
|
||||
IFS_SBAF_UNASSIGNED_ERROR_CODE7 = 7,
|
||||
IFS_SBAF_EXCEED_NUMBER_OF_THREADS_CONCURRENT = 8,
|
||||
IFS_SBAF_INTERRUPTED_DURING_EXECUTION = 9,
|
||||
IFS_SBAF_INVALID_PROGRAM_INDEX = 0xA,
|
||||
IFS_SBAF_CORRUPTED_CHUNK = 0xB,
|
||||
IFS_SBAF_DID_NOT_START = 0xC,
|
||||
};
|
||||
|
||||
static const char * const sbaf_test_status[] = {
|
||||
[IFS_SBAF_NO_ERROR] = "SBAF no error",
|
||||
[IFS_SBAF_OTHER_THREAD_COULD_NOT_JOIN] = "Other thread could not join.",
|
||||
[IFS_SBAF_INTERRUPTED_BEFORE_RENDEZVOUS] = "Interrupt occurred prior to SBAF coordination.",
|
||||
[IFS_SBAF_UNASSIGNED_ERROR_CODE3] = "Unassigned error code 0x3",
|
||||
[IFS_SBAF_INVALID_BUNDLE_INDEX] = "Non-valid sbaf bundles. Reload test image",
|
||||
[IFS_SBAF_MISMATCH_ARGS_BETWEEN_THREADS] = "Mismatch in arguments between threads T0/T1.",
|
||||
[IFS_SBAF_CORE_NOT_CAPABLE_CURRENTLY] = "Core not capable of performing SBAF currently",
|
||||
[IFS_SBAF_UNASSIGNED_ERROR_CODE7] = "Unassigned error code 0x7",
|
||||
[IFS_SBAF_EXCEED_NUMBER_OF_THREADS_CONCURRENT] = "Exceeded number of Logical Processors (LP) allowed to run Scan-At-Field concurrently",
|
||||
[IFS_SBAF_INTERRUPTED_DURING_EXECUTION] = "Interrupt occurred prior to SBAF start",
|
||||
[IFS_SBAF_INVALID_PROGRAM_INDEX] = "SBAF program index not valid",
|
||||
[IFS_SBAF_CORRUPTED_CHUNK] = "SBAF operation aborted due to corrupted chunk",
|
||||
[IFS_SBAF_DID_NOT_START] = "SBAF operation did not start",
|
||||
};
|
||||
|
||||
static void sbaf_message_not_tested(struct device *dev, int cpu, u64 status_data)
|
||||
{
|
||||
union ifs_sbaf_status status = (union ifs_sbaf_status)status_data;
|
||||
|
||||
if (status.error_code < ARRAY_SIZE(sbaf_test_status)) {
|
||||
dev_info(dev, "CPU(s) %*pbl: SBAF operation did not start. %s\n",
|
||||
cpumask_pr_args(cpu_smt_mask(cpu)),
|
||||
sbaf_test_status[status.error_code]);
|
||||
} else if (status.error_code == IFS_SW_TIMEOUT) {
|
||||
dev_info(dev, "CPU(s) %*pbl: software timeout during scan\n",
|
||||
cpumask_pr_args(cpu_smt_mask(cpu)));
|
||||
} else if (status.error_code == IFS_SW_PARTIAL_COMPLETION) {
|
||||
dev_info(dev, "CPU(s) %*pbl: %s\n",
|
||||
cpumask_pr_args(cpu_smt_mask(cpu)),
|
||||
"Not all SBAF bundles executed. Maximum forward progress retries exceeded");
|
||||
} else {
|
||||
dev_info(dev, "CPU(s) %*pbl: SBAF unknown status %llx\n",
|
||||
cpumask_pr_args(cpu_smt_mask(cpu)), status.data);
|
||||
}
|
||||
}
|
||||
|
||||
static void sbaf_message_fail(struct device *dev, int cpu, union ifs_sbaf_status status)
|
||||
{
|
||||
/* Failed signature check is set when SBAF signature did not match the expected value */
|
||||
if (status.sbaf_status == SBAF_STATUS_SIGN_FAIL) {
|
||||
dev_err(dev, "CPU(s) %*pbl: Failed signature check\n",
|
||||
cpumask_pr_args(cpu_smt_mask(cpu)));
|
||||
}
|
||||
|
||||
/* Failed to reach end of test */
|
||||
if (status.sbaf_status == SBAF_STATUS_TEST_FAIL) {
|
||||
dev_err(dev, "CPU(s) %*pbl: Failed to complete test\n",
|
||||
cpumask_pr_args(cpu_smt_mask(cpu)));
|
||||
}
|
||||
}
|
||||
|
||||
static bool sbaf_bundle_completed(union ifs_sbaf_status status)
|
||||
{
|
||||
return !(status.sbaf_status || status.error_code);
|
||||
}
|
||||
|
||||
static bool sbaf_can_restart(union ifs_sbaf_status status)
|
||||
{
|
||||
enum sbaf_status_err_code err_code = status.error_code;
|
||||
|
||||
/* Signature for chunk is bad, or scan test failed */
|
||||
if (status.sbaf_status == SBAF_STATUS_SIGN_FAIL ||
|
||||
status.sbaf_status == SBAF_STATUS_TEST_FAIL)
|
||||
return false;
|
||||
|
||||
switch (err_code) {
|
||||
case IFS_SBAF_NO_ERROR:
|
||||
case IFS_SBAF_OTHER_THREAD_COULD_NOT_JOIN:
|
||||
case IFS_SBAF_INTERRUPTED_BEFORE_RENDEZVOUS:
|
||||
case IFS_SBAF_EXCEED_NUMBER_OF_THREADS_CONCURRENT:
|
||||
case IFS_SBAF_INTERRUPTED_DURING_EXECUTION:
|
||||
return true;
|
||||
case IFS_SBAF_UNASSIGNED_ERROR_CODE3:
|
||||
case IFS_SBAF_INVALID_BUNDLE_INDEX:
|
||||
case IFS_SBAF_MISMATCH_ARGS_BETWEEN_THREADS:
|
||||
case IFS_SBAF_CORE_NOT_CAPABLE_CURRENTLY:
|
||||
case IFS_SBAF_UNASSIGNED_ERROR_CODE7:
|
||||
case IFS_SBAF_INVALID_PROGRAM_INDEX:
|
||||
case IFS_SBAF_CORRUPTED_CHUNK:
|
||||
case IFS_SBAF_DID_NOT_START:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute the SBAF test. Called "simultaneously" on all threads of a core
|
||||
* at high priority using the stop_cpus mechanism.
|
||||
*/
|
||||
static int dosbaf(void *data)
|
||||
{
|
||||
struct sbaf_run_params *run_params = data;
|
||||
int cpu = smp_processor_id();
|
||||
union ifs_sbaf_status status;
|
||||
struct ifs_data *ifsd;
|
||||
int first;
|
||||
|
||||
ifsd = run_params->ifsd;
|
||||
|
||||
/* Only the first logical CPU on a core reports result */
|
||||
first = cpumask_first(cpu_smt_mask(cpu));
|
||||
wait_for_sibling_cpu(&sbaf_cpus_in, NSEC_PER_SEC);
|
||||
|
||||
/*
|
||||
* This WRMSR will wait for other HT threads to also write
|
||||
* to this MSR (at most for activate.delay cycles). Then it
|
||||
* starts scan of each requested bundle. The core test happens
|
||||
* during the "execution" of the WRMSR.
|
||||
*/
|
||||
wrmsrl(MSR_ACTIVATE_SBAF, run_params->activate->data);
|
||||
rdmsrl(MSR_SBAF_STATUS, status.data);
|
||||
trace_ifs_sbaf(ifsd->cur_batch, *run_params->activate, status);
|
||||
|
||||
/* Pass back the result of the test */
|
||||
if (cpu == first)
|
||||
run_params->status = status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ifs_sbaf_test_core(int cpu, struct device *dev)
|
||||
{
|
||||
struct sbaf_run_params run_params;
|
||||
union ifs_sbaf_status status = {};
|
||||
union ifs_sbaf activate;
|
||||
unsigned long timeout;
|
||||
struct ifs_data *ifsd;
|
||||
int stop_bundle;
|
||||
int retries;
|
||||
|
||||
ifsd = ifs_get_data(dev);
|
||||
|
||||
activate.data = 0;
|
||||
activate.delay = IFS_THREAD_WAIT;
|
||||
|
||||
timeout = jiffies + 2 * HZ;
|
||||
retries = MAX_IFS_RETRIES;
|
||||
activate.bundle_idx = 0;
|
||||
stop_bundle = ifsd->max_bundle;
|
||||
|
||||
while (activate.bundle_idx <= stop_bundle) {
|
||||
if (time_after(jiffies, timeout)) {
|
||||
status.error_code = IFS_SW_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
atomic_set(&sbaf_cpus_in, 0);
|
||||
|
||||
run_params.ifsd = ifsd;
|
||||
run_params.activate = &activate;
|
||||
run_params.retry_cnt = &retries;
|
||||
stop_core_cpuslocked(cpu, dosbaf, &run_params);
|
||||
|
||||
status = run_params.status;
|
||||
|
||||
if (sbaf_bundle_completed(status)) {
|
||||
activate.bundle_idx = status.bundle_idx + 1;
|
||||
activate.pgm_idx = 0;
|
||||
retries = MAX_IFS_RETRIES;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Some cases can be retried, give up for others */
|
||||
if (!sbaf_can_restart(status))
|
||||
break;
|
||||
|
||||
if (status.pgm_idx == activate.pgm_idx) {
|
||||
/* If no progress retry */
|
||||
if (--retries == 0) {
|
||||
if (status.error_code == IFS_NO_ERROR)
|
||||
status.error_code = IFS_SW_PARTIAL_COMPLETION;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* if some progress, more pgms remaining in bundle, reset retries */
|
||||
retries = MAX_IFS_RETRIES;
|
||||
activate.bundle_idx = status.bundle_idx;
|
||||
activate.pgm_idx = status.pgm_idx;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update status for this core */
|
||||
ifsd->scan_details = status.data;
|
||||
|
||||
if (status.sbaf_status == SBAF_STATUS_SIGN_FAIL ||
|
||||
status.sbaf_status == SBAF_STATUS_TEST_FAIL) {
|
||||
ifsd->status = SCAN_TEST_FAIL;
|
||||
sbaf_message_fail(dev, cpu, status);
|
||||
} else if (status.error_code || status.sbaf_status == SBAF_STATUS_INTR ||
|
||||
(activate.bundle_idx < stop_bundle)) {
|
||||
ifsd->status = SCAN_NOT_TESTED;
|
||||
sbaf_message_not_tested(dev, cpu, status.data);
|
||||
} else {
|
||||
ifsd->status = SCAN_TEST_PASS;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initiate per core test. It wakes up work queue threads on the target cpu and
|
||||
* its sibling cpu. Once all sibling threads wake up, the scan test gets executed and
|
||||
@ -420,6 +647,12 @@ int do_core_test(int cpu, struct device *dev)
|
||||
else
|
||||
ifs_array_test_gen1(cpu, dev);
|
||||
break;
|
||||
case IFS_TYPE_SBAF:
|
||||
if (!ifsd->loaded)
|
||||
ret = -EPERM;
|
||||
else
|
||||
ifs_sbaf_test_core(cpu, dev);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472_discrete.o \
|
||||
intel_skl_int3472_tps68470.o
|
||||
intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o led.o common.o
|
||||
intel_skl_int3472_tps68470-y := tps68470.o tps68470_board_data.o common.o
|
||||
intel_skl_int3472_tps68470.o \
|
||||
intel_skl_int3472_common.o
|
||||
intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o led.o
|
||||
intel_skl_int3472_tps68470-y := tps68470.o tps68470_board_data.o
|
||||
|
||||
intel_skl_int3472_common-y += common.o
|
||||
|
@ -29,6 +29,7 @@ union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, char *i
|
||||
|
||||
return obj;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_int3472_get_acpi_buffer);
|
||||
|
||||
int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb)
|
||||
{
|
||||
@ -52,6 +53,7 @@ out_free_obj:
|
||||
kfree(obj);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_int3472_fill_cldb);
|
||||
|
||||
/* sensor_adev_ret may be NULL, name_ret must not be NULL */
|
||||
int skl_int3472_get_sensor_adev_and_name(struct device *dev,
|
||||
@ -80,3 +82,8 @@ int skl_int3472_get_sensor_adev_and_name(struct device *dev,
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_int3472_get_sensor_adev_and_name);
|
||||
|
||||
MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI Device Driver library");
|
||||
MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/string_choices.h>
|
||||
#include <linux/uuid.h>
|
||||
|
||||
#include "common.h"
|
||||
@ -69,11 +70,7 @@ static int skl_int3472_fill_gpiod_lookup(struct gpiod_lookup *table_entry,
|
||||
if (!adev)
|
||||
return -ENODEV;
|
||||
|
||||
table_entry->key = acpi_dev_name(adev);
|
||||
table_entry->chip_hwnum = agpio->pin_table[0];
|
||||
table_entry->con_id = func;
|
||||
table_entry->idx = 0;
|
||||
table_entry->flags = polarity;
|
||||
*table_entry = GPIO_LOOKUP(acpi_dev_name(adev), agpio->pin_table[0], func, polarity);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -234,7 +231,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
|
||||
|
||||
dev_dbg(int3472->dev, "%s %s pin %d active-%s\n", func,
|
||||
agpio->resource_source.string_ptr, agpio->pin_table[0],
|
||||
(polarity == GPIO_ACTIVE_HIGH) ? "high" : "low");
|
||||
str_high_low(polarity == GPIO_ACTIVE_HIGH));
|
||||
|
||||
switch (type) {
|
||||
case INT3472_GPIO_TYPE_RESET:
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
@ -250,7 +249,7 @@ static int oaktrail_backlight_init(void)
|
||||
oaktrail_bl_device = bd;
|
||||
|
||||
bd->props.brightness = get_backlight_brightness(bd);
|
||||
bd->props.power = FB_BLANK_UNBLANK;
|
||||
bd->props.power = BACKLIGHT_POWER_ON;
|
||||
backlight_update_status(bd);
|
||||
|
||||
return 0;
|
||||
|
@ -715,6 +715,49 @@ static int pmc_core_s0ix_blocker_show(struct seq_file *s, void *unused)
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(pmc_core_s0ix_blocker);
|
||||
|
||||
static void pmc_core_ltr_ignore_all(struct pmc_dev *pmcdev)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); i++) {
|
||||
struct pmc *pmc;
|
||||
u32 ltr_ign;
|
||||
|
||||
pmc = pmcdev->pmcs[i];
|
||||
if (!pmc)
|
||||
continue;
|
||||
|
||||
guard(mutex)(&pmcdev->lock);
|
||||
pmc->ltr_ign = pmc_core_reg_read(pmc, pmc->map->ltr_ignore_offset);
|
||||
|
||||
/* ltr_ignore_max is the max index value for LTR ignore register */
|
||||
ltr_ign = pmc->ltr_ign | GENMASK(pmc->map->ltr_ignore_max, 0);
|
||||
pmc_core_reg_write(pmc, pmc->map->ltr_ignore_offset, ltr_ign);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignoring ME during suspend is blocking platforms with ADL PCH to get to
|
||||
* deeper S0ix substate.
|
||||
*/
|
||||
pmc_core_send_ltr_ignore(pmcdev, 6, 0);
|
||||
}
|
||||
|
||||
static void pmc_core_ltr_restore_all(struct pmc_dev *pmcdev)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); i++) {
|
||||
struct pmc *pmc;
|
||||
|
||||
pmc = pmcdev->pmcs[i];
|
||||
if (!pmc)
|
||||
continue;
|
||||
|
||||
guard(mutex)(&pmcdev->lock);
|
||||
pmc_core_reg_write(pmc, pmc->map->ltr_ignore_offset, pmc->ltr_ign);
|
||||
}
|
||||
}
|
||||
|
||||
static inline u64 adjust_lpm_residency(struct pmc *pmc, u32 offset,
|
||||
const int lpm_adj_x2)
|
||||
{
|
||||
@ -729,12 +772,11 @@ static int pmc_core_substate_res_show(struct seq_file *s, void *unused)
|
||||
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
|
||||
const int lpm_adj_x2 = pmc->map->lpm_res_counter_step_x2;
|
||||
u32 offset = pmc->map->lpm_residency_offset;
|
||||
unsigned int i;
|
||||
int mode;
|
||||
|
||||
seq_printf(s, "%-10s %-15s\n", "Substate", "Residency");
|
||||
|
||||
pmc_for_each_mode(i, mode, pmcdev) {
|
||||
pmc_for_each_mode(mode, pmcdev) {
|
||||
seq_printf(s, "%-10s %-15llu\n", pmc_lpm_modes[mode],
|
||||
adjust_lpm_residency(pmc, offset + (4 * mode), lpm_adj_x2));
|
||||
}
|
||||
@ -788,20 +830,21 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_l_sts_regs);
|
||||
static void pmc_core_substate_req_header_show(struct seq_file *s, int pmc_index)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
unsigned int i;
|
||||
int mode;
|
||||
|
||||
seq_printf(s, "%30s |", "Element");
|
||||
pmc_for_each_mode(i, mode, pmcdev)
|
||||
pmc_for_each_mode(mode, pmcdev)
|
||||
seq_printf(s, " %9s |", pmc_lpm_modes[mode]);
|
||||
|
||||
seq_printf(s, " %9s |\n", "Status");
|
||||
seq_printf(s, " %9s |", "Status");
|
||||
seq_printf(s, " %11s |\n", "Live Status");
|
||||
}
|
||||
|
||||
static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
u32 sts_offset;
|
||||
u32 sts_offset_live;
|
||||
u32 *lpm_req_regs;
|
||||
unsigned int mp, pmc_index;
|
||||
int num_maps;
|
||||
@ -816,6 +859,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
|
||||
maps = pmc->map->lpm_sts;
|
||||
num_maps = pmc->map->lpm_num_maps;
|
||||
sts_offset = pmc->map->lpm_status_offset;
|
||||
sts_offset_live = pmc->map->lpm_live_status_offset;
|
||||
lpm_req_regs = pmc->lpm_req_regs;
|
||||
|
||||
/*
|
||||
@ -833,20 +877,24 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
|
||||
for (mp = 0; mp < num_maps; mp++) {
|
||||
u32 req_mask = 0;
|
||||
u32 lpm_status;
|
||||
u32 lpm_status_live;
|
||||
const struct pmc_bit_map *map;
|
||||
int mode, idx, i, len = 32;
|
||||
int mode, i, len = 32;
|
||||
|
||||
/*
|
||||
* Capture the requirements and create a mask so that we only
|
||||
* show an element if it's required for at least one of the
|
||||
* enabled low power modes
|
||||
*/
|
||||
pmc_for_each_mode(idx, mode, pmcdev)
|
||||
pmc_for_each_mode(mode, pmcdev)
|
||||
req_mask |= lpm_req_regs[mp + (mode * num_maps)];
|
||||
|
||||
/* Get the last latched status for this map */
|
||||
lpm_status = pmc_core_reg_read(pmc, sts_offset + (mp * 4));
|
||||
|
||||
/* Get the runtime status for this map */
|
||||
lpm_status_live = pmc_core_reg_read(pmc, sts_offset_live + (mp * 4));
|
||||
|
||||
/* Loop over elements in this map */
|
||||
map = maps[mp];
|
||||
for (i = 0; map[i].name && i < len; i++) {
|
||||
@ -864,7 +912,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
|
||||
seq_printf(s, "pmc%d: %26s |", pmc_index, map[i].name);
|
||||
|
||||
/* Loop over the enabled states and display if required */
|
||||
pmc_for_each_mode(idx, mode, pmcdev) {
|
||||
pmc_for_each_mode(mode, pmcdev) {
|
||||
bool required = lpm_req_regs[mp + (mode * num_maps)] &
|
||||
bit_mask;
|
||||
seq_printf(s, " %9s |", required ? "Required" : " ");
|
||||
@ -873,6 +921,9 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
|
||||
/* In Status column, show the last captured state of this agent */
|
||||
seq_printf(s, " %9s |", lpm_status & bit_mask ? "Yes" : " ");
|
||||
|
||||
/* In Live status column, show the live state of this agent */
|
||||
seq_printf(s, " %11s |", lpm_status_live & bit_mask ? "Yes" : " ");
|
||||
|
||||
seq_puts(s, "\n");
|
||||
}
|
||||
}
|
||||
@ -926,7 +977,6 @@ static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
|
||||
unsigned int idx;
|
||||
bool c10;
|
||||
u32 reg;
|
||||
int mode;
|
||||
@ -940,7 +990,7 @@ static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused)
|
||||
c10 = true;
|
||||
}
|
||||
|
||||
pmc_for_each_mode(idx, mode, pmcdev) {
|
||||
pmc_for_each_mode(mode, pmcdev) {
|
||||
if ((BIT(mode) & reg) && !c10)
|
||||
seq_printf(s, " [%s]", pmc_lpm_modes[mode]);
|
||||
else
|
||||
@ -961,7 +1011,6 @@ static ssize_t pmc_core_lpm_latch_mode_write(struct file *file,
|
||||
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
|
||||
bool clear = false, c10 = false;
|
||||
unsigned char buf[8];
|
||||
unsigned int idx;
|
||||
int m, mode;
|
||||
u32 reg;
|
||||
|
||||
@ -980,7 +1029,7 @@ static ssize_t pmc_core_lpm_latch_mode_write(struct file *file,
|
||||
mode = sysfs_match_string(pmc_lpm_modes, buf);
|
||||
|
||||
/* Check string matches enabled mode */
|
||||
pmc_for_each_mode(idx, m, pmcdev)
|
||||
pmc_for_each_mode(m, pmcdev)
|
||||
if (mode == m)
|
||||
break;
|
||||
|
||||
@ -1524,6 +1573,10 @@ static bool warn_on_s0ix_failures;
|
||||
module_param(warn_on_s0ix_failures, bool, 0644);
|
||||
MODULE_PARM_DESC(warn_on_s0ix_failures, "Check and warn for S0ix failures");
|
||||
|
||||
static bool ltr_ignore_all_suspend = true;
|
||||
module_param(ltr_ignore_all_suspend, bool, 0644);
|
||||
MODULE_PARM_DESC(ltr_ignore_all_suspend, "Ignore all LTRs during suspend");
|
||||
|
||||
static __maybe_unused int pmc_core_suspend(struct device *dev)
|
||||
{
|
||||
struct pmc_dev *pmcdev = dev_get_drvdata(dev);
|
||||
@ -1533,6 +1586,9 @@ static __maybe_unused int pmc_core_suspend(struct device *dev)
|
||||
if (pmcdev->suspend)
|
||||
pmcdev->suspend(pmcdev);
|
||||
|
||||
if (ltr_ignore_all_suspend)
|
||||
pmc_core_ltr_ignore_all(pmcdev);
|
||||
|
||||
/* Check if the syspend will actually use S0ix */
|
||||
if (pm_suspend_via_firmware())
|
||||
return 0;
|
||||
@ -1639,6 +1695,9 @@ static __maybe_unused int pmc_core_resume(struct device *dev)
|
||||
{
|
||||
struct pmc_dev *pmcdev = dev_get_drvdata(dev);
|
||||
|
||||
if (ltr_ignore_all_suspend)
|
||||
pmc_core_ltr_restore_all(pmcdev);
|
||||
|
||||
if (pmcdev->resume)
|
||||
return pmcdev->resume(pmcdev);
|
||||
|
||||
|
@ -378,6 +378,7 @@ struct pmc_info {
|
||||
* @map: pointer to pmc_reg_map struct that contains platform
|
||||
* specific attributes
|
||||
* @lpm_req_regs: List of substate requirements
|
||||
* @ltr_ign: Holds LTR ignore data while suspended
|
||||
*
|
||||
* pmc contains info about one power management controller device.
|
||||
*/
|
||||
@ -386,6 +387,7 @@ struct pmc {
|
||||
void __iomem *regbase;
|
||||
const struct pmc_reg_map *map;
|
||||
u32 *lpm_req_regs;
|
||||
u32 ltr_ign;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -612,10 +614,12 @@ int lnl_core_init(struct pmc_dev *pmcdev);
|
||||
void cnl_suspend(struct pmc_dev *pmcdev);
|
||||
int cnl_resume(struct pmc_dev *pmcdev);
|
||||
|
||||
#define pmc_for_each_mode(i, mode, pmcdev) \
|
||||
for (i = 0, mode = pmcdev->lpm_en_modes[i]; \
|
||||
i < pmcdev->num_lpm_modes; \
|
||||
i++, mode = pmcdev->lpm_en_modes[i])
|
||||
#define pmc_for_each_mode(mode, pmcdev) \
|
||||
for (unsigned int __i = 0, __cond; \
|
||||
__cond = __i < (pmcdev)->num_lpm_modes, \
|
||||
__cond && ((mode) = (pmcdev)->lpm_en_modes[__i]), \
|
||||
__cond; \
|
||||
__i++)
|
||||
|
||||
#define DEFINE_PMC_CORE_ATTR_WRITE(__name) \
|
||||
static int __name ## _open(struct inode *inode, struct file *file) \
|
||||
|
@ -9,11 +9,11 @@
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/intel_vsec.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "../vsec.h"
|
||||
#include "../pmt/telemetry.h"
|
||||
|
||||
#define SSRAM_HDR_SIZE 0x100
|
||||
@ -45,7 +45,7 @@ static int pmc_core_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc)
|
||||
struct telem_endpoint *ep;
|
||||
const u8 *lpm_indices;
|
||||
int num_maps, mode_offset = 0;
|
||||
int ret, mode, i;
|
||||
int ret, mode;
|
||||
int lpm_size;
|
||||
u32 guid;
|
||||
|
||||
@ -116,7 +116,7 @@ static int pmc_core_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc)
|
||||
*
|
||||
*/
|
||||
mode_offset = LPM_HEADER_OFFSET + LPM_MODE_OFFSET;
|
||||
pmc_for_each_mode(i, mode, pmcdev) {
|
||||
pmc_for_each_mode(mode, pmcdev) {
|
||||
u32 *req_offset = pmc->lpm_req_regs + (mode * num_maps);
|
||||
int m;
|
||||
|
||||
|
@ -9,12 +9,12 @@
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/intel_vsec.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../vsec.h"
|
||||
#include "class.h"
|
||||
|
||||
#define PMT_XA_START 1
|
||||
@ -58,6 +58,22 @@ pmt_memcpy64_fromio(void *to, const u64 __iomem *from, size_t count)
|
||||
return count;
|
||||
}
|
||||
|
||||
int pmt_telem_read_mmio(struct pci_dev *pdev, struct pmt_callbacks *cb, u32 guid, void *buf,
|
||||
void __iomem *addr, u32 count)
|
||||
{
|
||||
if (cb && cb->read_telem)
|
||||
return cb->read_telem(pdev, guid, buf, count);
|
||||
|
||||
if (guid == GUID_SPR_PUNIT)
|
||||
/* PUNIT on SPR only supports aligned 64-bit read */
|
||||
return pmt_memcpy64_fromio(buf, addr, count);
|
||||
|
||||
memcpy_fromio(buf, addr, count);
|
||||
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(pmt_telem_read_mmio, INTEL_PMT);
|
||||
|
||||
/*
|
||||
* sysfs
|
||||
*/
|
||||
@ -79,11 +95,8 @@ intel_pmt_read(struct file *filp, struct kobject *kobj,
|
||||
if (count > entry->size - off)
|
||||
count = entry->size - off;
|
||||
|
||||
if (entry->guid == GUID_SPR_PUNIT)
|
||||
/* PUNIT on SPR only supports aligned 64-bit read */
|
||||
count = pmt_memcpy64_fromio(buf, entry->base + off, count);
|
||||
else
|
||||
memcpy_fromio(buf, entry->base + off, count);
|
||||
count = pmt_telem_read_mmio(entry->ep->pcidev, entry->cb, entry->header.guid, buf,
|
||||
entry->base + off, count);
|
||||
|
||||
return count;
|
||||
}
|
||||
@ -239,6 +252,7 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry,
|
||||
|
||||
entry->guid = header->guid;
|
||||
entry->size = header->size;
|
||||
entry->cb = ivdev->priv_data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -300,7 +314,7 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry,
|
||||
goto fail_ioremap;
|
||||
|
||||
if (ns->pmt_add_endpoint) {
|
||||
ret = ns->pmt_add_endpoint(entry, ivdev->pcidev);
|
||||
ret = ns->pmt_add_endpoint(ivdev, entry);
|
||||
if (ret)
|
||||
goto fail_add_endpoint;
|
||||
}
|
||||
|
@ -2,13 +2,13 @@
|
||||
#ifndef _INTEL_PMT_CLASS_H
|
||||
#define _INTEL_PMT_CLASS_H
|
||||
|
||||
#include <linux/intel_vsec.h>
|
||||
#include <linux/xarray.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "../vsec.h"
|
||||
#include "telemetry.h"
|
||||
|
||||
/* PMT access types */
|
||||
@ -24,6 +24,7 @@ struct pci_dev;
|
||||
struct telem_endpoint {
|
||||
struct pci_dev *pcidev;
|
||||
struct telem_header header;
|
||||
struct pmt_callbacks *cb;
|
||||
void __iomem *base;
|
||||
bool present;
|
||||
struct kref kref;
|
||||
@ -43,6 +44,7 @@ struct intel_pmt_entry {
|
||||
struct kobject *kobj;
|
||||
void __iomem *disc_table;
|
||||
void __iomem *base;
|
||||
struct pmt_callbacks *cb;
|
||||
unsigned long base_addr;
|
||||
size_t size;
|
||||
u32 guid;
|
||||
@ -55,10 +57,12 @@ struct intel_pmt_namespace {
|
||||
const struct attribute_group *attr_grp;
|
||||
int (*pmt_header_decode)(struct intel_pmt_entry *entry,
|
||||
struct device *dev);
|
||||
int (*pmt_add_endpoint)(struct intel_pmt_entry *entry,
|
||||
struct pci_dev *pdev);
|
||||
int (*pmt_add_endpoint)(struct intel_vsec_device *ivdev,
|
||||
struct intel_pmt_entry *entry);
|
||||
};
|
||||
|
||||
int pmt_telem_read_mmio(struct pci_dev *pdev, struct pmt_callbacks *cb, u32 guid, void *buf,
|
||||
void __iomem *addr, u32 count);
|
||||
bool intel_pmt_is_early_client_hw(struct device *dev);
|
||||
int intel_pmt_dev_create(struct intel_pmt_entry *entry,
|
||||
struct intel_pmt_namespace *ns,
|
||||
|
@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/intel_vsec.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
@ -16,7 +17,6 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/overflow.h>
|
||||
|
||||
#include "../vsec.h"
|
||||
#include "class.h"
|
||||
|
||||
/* Crashlog discovery header types */
|
||||
|
@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/intel_vsec.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
@ -16,7 +17,6 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/overflow.h>
|
||||
|
||||
#include "../vsec.h"
|
||||
#include "class.h"
|
||||
|
||||
#define TELEM_SIZE_OFFSET 0x0
|
||||
@ -93,8 +93,8 @@ static int pmt_telem_header_decode(struct intel_pmt_entry *entry,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmt_telem_add_endpoint(struct intel_pmt_entry *entry,
|
||||
struct pci_dev *pdev)
|
||||
static int pmt_telem_add_endpoint(struct intel_vsec_device *ivdev,
|
||||
struct intel_pmt_entry *entry)
|
||||
{
|
||||
struct telem_endpoint *ep;
|
||||
|
||||
@ -104,13 +104,14 @@ static int pmt_telem_add_endpoint(struct intel_pmt_entry *entry,
|
||||
return -ENOMEM;
|
||||
|
||||
ep = entry->ep;
|
||||
ep->pcidev = pdev;
|
||||
ep->pcidev = ivdev->pcidev;
|
||||
ep->header.access_type = entry->header.access_type;
|
||||
ep->header.guid = entry->header.guid;
|
||||
ep->header.base_offset = entry->header.base_offset;
|
||||
ep->header.size = entry->header.size;
|
||||
ep->base = entry->base;
|
||||
ep->present = true;
|
||||
ep->cb = ivdev->priv_data;
|
||||
|
||||
kref_init(&ep->kref);
|
||||
|
||||
@ -218,7 +219,8 @@ int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count)
|
||||
if (offset + NUM_BYTES_QWORD(count) > size)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy_fromio(data, ep->base + offset, NUM_BYTES_QWORD(count));
|
||||
pmt_telem_read_mmio(ep->pcidev, ep->cb, ep->header.guid, data, ep->base + offset,
|
||||
NUM_BYTES_QWORD(count));
|
||||
|
||||
return ep->present ? 0 : -EPIPE;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/intel_vsec.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
@ -22,8 +23,6 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "vsec.h"
|
||||
|
||||
#define ACCESS_TYPE_BARID 2
|
||||
#define ACCESS_TYPE_LOCAL 3
|
||||
|
||||
|
@ -651,10 +651,6 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
|
||||
|
||||
/* Lock to prevent module registration when already opened by user space */
|
||||
static DEFINE_MUTEX(punit_misc_dev_open_lock);
|
||||
/* Lock to allow one shared misc device for all ISST interfaces */
|
||||
static DEFINE_MUTEX(punit_misc_dev_reg_lock);
|
||||
static int misc_usage_count;
|
||||
static int misc_device_ret;
|
||||
static int misc_device_open;
|
||||
|
||||
static int isst_if_open(struct inode *inode, struct file *file)
|
||||
@ -720,39 +716,23 @@ static struct miscdevice isst_if_char_driver = {
|
||||
|
||||
static int isst_misc_reg(void)
|
||||
{
|
||||
mutex_lock(&punit_misc_dev_reg_lock);
|
||||
if (misc_device_ret)
|
||||
goto unlock_exit;
|
||||
int ret;
|
||||
|
||||
if (!misc_usage_count) {
|
||||
misc_device_ret = isst_if_cpu_info_init();
|
||||
if (misc_device_ret)
|
||||
goto unlock_exit;
|
||||
ret = isst_if_cpu_info_init();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
misc_device_ret = misc_register(&isst_if_char_driver);
|
||||
if (misc_device_ret) {
|
||||
isst_if_cpu_info_exit();
|
||||
goto unlock_exit;
|
||||
}
|
||||
}
|
||||
misc_usage_count++;
|
||||
ret = misc_register(&isst_if_char_driver);
|
||||
if (ret)
|
||||
isst_if_cpu_info_exit();
|
||||
|
||||
unlock_exit:
|
||||
mutex_unlock(&punit_misc_dev_reg_lock);
|
||||
|
||||
return misc_device_ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void isst_misc_unreg(void)
|
||||
{
|
||||
mutex_lock(&punit_misc_dev_reg_lock);
|
||||
if (misc_usage_count)
|
||||
misc_usage_count--;
|
||||
if (!misc_usage_count && !misc_device_ret) {
|
||||
misc_deregister(&isst_if_char_driver);
|
||||
isst_if_cpu_info_exit();
|
||||
}
|
||||
mutex_unlock(&punit_misc_dev_reg_lock);
|
||||
misc_deregister(&isst_if_char_driver);
|
||||
isst_if_cpu_info_exit();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/intel_tpmi.h>
|
||||
#include <linux/intel_vsec.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
@ -59,8 +60,6 @@
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/string_helpers.h>
|
||||
|
||||
#include "vsec.h"
|
||||
|
||||
/**
|
||||
* struct intel_tpmi_pfs_entry - TPMI PM Feature Structure (PFS) entry
|
||||
* @tpmi_id: TPMI feature identifier (what the feature is and its data format).
|
||||
|
@ -60,11 +60,16 @@ static ssize_t show_attr(struct uncore_data *data, char *buf, enum uncore_index
|
||||
static ssize_t store_attr(struct uncore_data *data, const char *buf, ssize_t count,
|
||||
enum uncore_index index)
|
||||
{
|
||||
unsigned int input;
|
||||
unsigned int input = 0;
|
||||
int ret;
|
||||
|
||||
if (kstrtouint(buf, 10, &input))
|
||||
return -EINVAL;
|
||||
if (index == UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE) {
|
||||
if (kstrtobool(buf, (bool *)&input))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (kstrtouint(buf, 10, &input))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&uncore_lock);
|
||||
ret = uncore_write(data, input, index);
|
||||
@ -103,6 +108,18 @@ show_uncore_attr(max_freq_khz, UNCORE_INDEX_MAX_FREQ);
|
||||
|
||||
show_uncore_attr(current_freq_khz, UNCORE_INDEX_CURRENT_FREQ);
|
||||
|
||||
store_uncore_attr(elc_low_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD);
|
||||
store_uncore_attr(elc_high_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD);
|
||||
store_uncore_attr(elc_high_threshold_enable,
|
||||
UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE);
|
||||
store_uncore_attr(elc_floor_freq_khz, UNCORE_INDEX_EFF_LAT_CTRL_FREQ);
|
||||
|
||||
show_uncore_attr(elc_low_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD);
|
||||
show_uncore_attr(elc_high_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD);
|
||||
show_uncore_attr(elc_high_threshold_enable,
|
||||
UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE);
|
||||
show_uncore_attr(elc_floor_freq_khz, UNCORE_INDEX_EFF_LAT_CTRL_FREQ);
|
||||
|
||||
#define show_uncore_data(member_name) \
|
||||
static ssize_t show_##member_name(struct kobject *kobj, \
|
||||
struct kobj_attribute *attr, char *buf)\
|
||||
@ -146,7 +163,8 @@ show_uncore_data(initial_max_freq_khz);
|
||||
|
||||
static int create_attr_group(struct uncore_data *data, char *name)
|
||||
{
|
||||
int ret, freq, index = 0;
|
||||
int ret, index = 0;
|
||||
unsigned int val;
|
||||
|
||||
init_attribute_rw(max_freq_khz);
|
||||
init_attribute_rw(min_freq_khz);
|
||||
@ -168,10 +186,24 @@ static int create_attr_group(struct uncore_data *data, char *name)
|
||||
data->uncore_attrs[index++] = &data->initial_min_freq_khz_kobj_attr.attr;
|
||||
data->uncore_attrs[index++] = &data->initial_max_freq_khz_kobj_attr.attr;
|
||||
|
||||
ret = uncore_read(data, &freq, UNCORE_INDEX_CURRENT_FREQ);
|
||||
ret = uncore_read(data, &val, UNCORE_INDEX_CURRENT_FREQ);
|
||||
if (!ret)
|
||||
data->uncore_attrs[index++] = &data->current_freq_khz_kobj_attr.attr;
|
||||
|
||||
ret = uncore_read(data, &val, UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD);
|
||||
if (!ret) {
|
||||
init_attribute_rw(elc_low_threshold_percent);
|
||||
init_attribute_rw(elc_high_threshold_percent);
|
||||
init_attribute_rw(elc_high_threshold_enable);
|
||||
init_attribute_rw(elc_floor_freq_khz);
|
||||
|
||||
data->uncore_attrs[index++] = &data->elc_low_threshold_percent_kobj_attr.attr;
|
||||
data->uncore_attrs[index++] = &data->elc_high_threshold_percent_kobj_attr.attr;
|
||||
data->uncore_attrs[index++] =
|
||||
&data->elc_high_threshold_enable_kobj_attr.attr;
|
||||
data->uncore_attrs[index++] = &data->elc_floor_freq_khz_kobj_attr.attr;
|
||||
}
|
||||
|
||||
data->uncore_attrs[index] = NULL;
|
||||
|
||||
data->uncore_attr_group.name = name;
|
||||
|
@ -34,6 +34,13 @@
|
||||
* @domain_id_kobj_attr: Storage for kobject attribute domain_id
|
||||
* @fabric_cluster_id_kobj_attr: Storage for kobject attribute fabric_cluster_id
|
||||
* @package_id_kobj_attr: Storage for kobject attribute package_id
|
||||
* @elc_low_threshold_percent_kobj_attr:
|
||||
Storage for kobject attribute elc_low_threshold_percent
|
||||
* @elc_high_threshold_percent_kobj_attr:
|
||||
Storage for kobject attribute elc_high_threshold_percent
|
||||
* @elc_high_threshold_enable_kobj_attr:
|
||||
Storage for kobject attribute elc_high_threshold_enable
|
||||
* @elc_floor_freq_khz_kobj_attr: Storage for kobject attribute elc_floor_freq_khz
|
||||
* @uncore_attrs: Attribute storage for group creation
|
||||
*
|
||||
* This structure is used to encapsulate all data related to uncore sysfs
|
||||
@ -61,7 +68,11 @@ struct uncore_data {
|
||||
struct kobj_attribute domain_id_kobj_attr;
|
||||
struct kobj_attribute fabric_cluster_id_kobj_attr;
|
||||
struct kobj_attribute package_id_kobj_attr;
|
||||
struct attribute *uncore_attrs[9];
|
||||
struct kobj_attribute elc_low_threshold_percent_kobj_attr;
|
||||
struct kobj_attribute elc_high_threshold_percent_kobj_attr;
|
||||
struct kobj_attribute elc_high_threshold_enable_kobj_attr;
|
||||
struct kobj_attribute elc_floor_freq_khz_kobj_attr;
|
||||
struct attribute *uncore_attrs[13];
|
||||
};
|
||||
|
||||
#define UNCORE_DOMAIN_ID_INVALID -1
|
||||
@ -70,6 +81,10 @@ enum uncore_index {
|
||||
UNCORE_INDEX_MIN_FREQ,
|
||||
UNCORE_INDEX_MAX_FREQ,
|
||||
UNCORE_INDEX_CURRENT_FREQ,
|
||||
UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD,
|
||||
UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD,
|
||||
UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE,
|
||||
UNCORE_INDEX_EFF_LAT_CTRL_FREQ,
|
||||
};
|
||||
|
||||
int uncore_freq_common_init(int (*read)(struct uncore_data *data, unsigned int *value,
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#define UNCORE_MAJOR_VERSION 0
|
||||
#define UNCORE_MINOR_VERSION 2
|
||||
#define UNCORE_ELC_SUPPORTED_VERSION 2
|
||||
#define UNCORE_HEADER_INDEX 0
|
||||
#define UNCORE_FABRIC_CLUSTER_OFFSET 8
|
||||
|
||||
@ -46,6 +47,7 @@ struct tpmi_uncore_struct;
|
||||
/* Information for each cluster */
|
||||
struct tpmi_uncore_cluster_info {
|
||||
bool root_domain;
|
||||
bool elc_supported;
|
||||
u8 __iomem *cluster_base;
|
||||
struct uncore_data uncore_data;
|
||||
struct tpmi_uncore_struct *uncore_root;
|
||||
@ -75,6 +77,10 @@ struct tpmi_uncore_struct {
|
||||
/* Bit definitions for CONTROL register */
|
||||
#define UNCORE_MAX_RATIO_MASK GENMASK_ULL(14, 8)
|
||||
#define UNCORE_MIN_RATIO_MASK GENMASK_ULL(21, 15)
|
||||
#define UNCORE_EFF_LAT_CTRL_RATIO_MASK GENMASK_ULL(28, 22)
|
||||
#define UNCORE_EFF_LAT_CTRL_LOW_THRESHOLD_MASK GENMASK_ULL(38, 32)
|
||||
#define UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE BIT(39)
|
||||
#define UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_MASK GENMASK_ULL(46, 40)
|
||||
|
||||
/* Helper function to read MMIO offset for max/min control frequency */
|
||||
static void read_control_freq(struct tpmi_uncore_cluster_info *cluster_info,
|
||||
@ -89,6 +95,48 @@ static void read_control_freq(struct tpmi_uncore_cluster_info *cluster_info,
|
||||
*value = FIELD_GET(UNCORE_MIN_RATIO_MASK, control) * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
}
|
||||
|
||||
/* Helper function to read efficiency latency control values over MMIO */
|
||||
static int read_eff_lat_ctrl(struct uncore_data *data, unsigned int *val, enum uncore_index index)
|
||||
{
|
||||
struct tpmi_uncore_cluster_info *cluster_info;
|
||||
u64 ctrl;
|
||||
|
||||
cluster_info = container_of(data, struct tpmi_uncore_cluster_info, uncore_data);
|
||||
if (cluster_info->root_domain)
|
||||
return -ENODATA;
|
||||
|
||||
if (!cluster_info->elc_supported)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ctrl = readq(cluster_info->cluster_base + UNCORE_CONTROL_INDEX);
|
||||
|
||||
switch (index) {
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD:
|
||||
*val = FIELD_GET(UNCORE_EFF_LAT_CTRL_LOW_THRESHOLD_MASK, ctrl);
|
||||
*val *= 100;
|
||||
*val = DIV_ROUND_UP(*val, FIELD_MAX(UNCORE_EFF_LAT_CTRL_LOW_THRESHOLD_MASK));
|
||||
break;
|
||||
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD:
|
||||
*val = FIELD_GET(UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_MASK, ctrl);
|
||||
*val *= 100;
|
||||
*val = DIV_ROUND_UP(*val, FIELD_MAX(UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_MASK));
|
||||
break;
|
||||
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE:
|
||||
*val = FIELD_GET(UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE, ctrl);
|
||||
break;
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_FREQ:
|
||||
*val = FIELD_GET(UNCORE_EFF_LAT_CTRL_RATIO_MASK, ctrl) * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define UNCORE_MAX_RATIO FIELD_MAX(UNCORE_MAX_RATIO_MASK)
|
||||
|
||||
/* Helper for sysfs read for max/min frequencies. Called under mutex locks */
|
||||
@ -137,6 +185,82 @@ static int uncore_read_control_freq(struct uncore_data *data, unsigned int *valu
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper function for writing efficiency latency control values over MMIO */
|
||||
static int write_eff_lat_ctrl(struct uncore_data *data, unsigned int val, enum uncore_index index)
|
||||
{
|
||||
struct tpmi_uncore_cluster_info *cluster_info;
|
||||
u64 control;
|
||||
|
||||
cluster_info = container_of(data, struct tpmi_uncore_cluster_info, uncore_data);
|
||||
|
||||
if (cluster_info->root_domain)
|
||||
return -ENODATA;
|
||||
|
||||
if (!cluster_info->elc_supported)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (index) {
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD:
|
||||
if (val > 100)
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD:
|
||||
if (val > 100)
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE:
|
||||
if (val > 1)
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_FREQ:
|
||||
val /= UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
if (val > FIELD_MAX(UNCORE_EFF_LAT_CTRL_RATIO_MASK))
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
control = readq(cluster_info->cluster_base + UNCORE_CONTROL_INDEX);
|
||||
|
||||
switch (index) {
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD:
|
||||
val *= FIELD_MAX(UNCORE_EFF_LAT_CTRL_LOW_THRESHOLD_MASK);
|
||||
val /= 100;
|
||||
control &= ~UNCORE_EFF_LAT_CTRL_LOW_THRESHOLD_MASK;
|
||||
control |= FIELD_PREP(UNCORE_EFF_LAT_CTRL_LOW_THRESHOLD_MASK, val);
|
||||
break;
|
||||
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD:
|
||||
val *= FIELD_MAX(UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_MASK);
|
||||
val /= 100;
|
||||
control &= ~UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_MASK;
|
||||
control |= FIELD_PREP(UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_MASK, val);
|
||||
break;
|
||||
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE:
|
||||
control &= ~UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE;
|
||||
control |= FIELD_PREP(UNCORE_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE, val);
|
||||
break;
|
||||
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_FREQ:
|
||||
control &= ~UNCORE_EFF_LAT_CTRL_RATIO_MASK;
|
||||
control |= FIELD_PREP(UNCORE_EFF_LAT_CTRL_RATIO_MASK, val);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
writeq(control, cluster_info->cluster_base + UNCORE_CONTROL_INDEX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper function to write MMIO offset for max/min control frequency */
|
||||
static void write_control_freq(struct tpmi_uncore_cluster_info *cluster_info, unsigned int input,
|
||||
unsigned int index)
|
||||
@ -156,7 +280,7 @@ static void write_control_freq(struct tpmi_uncore_cluster_info *cluster_info, un
|
||||
writeq(control, (cluster_info->cluster_base + UNCORE_CONTROL_INDEX));
|
||||
}
|
||||
|
||||
/* Callback for sysfs write for max/min frequencies. Called under mutex locks */
|
||||
/* Helper for sysfs write for max/min frequencies. Called under mutex locks */
|
||||
static int uncore_write_control_freq(struct uncore_data *data, unsigned int input,
|
||||
enum uncore_index index)
|
||||
{
|
||||
@ -234,6 +358,33 @@ static int uncore_read(struct uncore_data *data, unsigned int *value, enum uncor
|
||||
case UNCORE_INDEX_CURRENT_FREQ:
|
||||
return uncore_read_freq(data, value);
|
||||
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD:
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD:
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE:
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_FREQ:
|
||||
return read_eff_lat_ctrl(data, value, index);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Callback for sysfs write for TPMI uncore data. Called under mutex locks. */
|
||||
static int uncore_write(struct uncore_data *data, unsigned int value, enum uncore_index index)
|
||||
{
|
||||
switch (index) {
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD:
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD:
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE:
|
||||
case UNCORE_INDEX_EFF_LAT_CTRL_FREQ:
|
||||
return write_eff_lat_ctrl(data, value, index);
|
||||
|
||||
case UNCORE_INDEX_MIN_FREQ:
|
||||
case UNCORE_INDEX_MAX_FREQ:
|
||||
return uncore_write_control_freq(data, value, index);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -291,7 +442,7 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_
|
||||
return -EINVAL;
|
||||
|
||||
/* Register callbacks to uncore core */
|
||||
ret = uncore_freq_common_init(uncore_read, uncore_write_control_freq);
|
||||
ret = uncore_freq_common_init(uncore_read, uncore_write);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -409,6 +560,9 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_
|
||||
|
||||
cluster_info->uncore_root = tpmi_uncore;
|
||||
|
||||
if (TPMI_MINOR_VERSION(pd_info->ufs_header_ver) >= UNCORE_ELC_SUPPORTED_VERSION)
|
||||
cluster_info->elc_supported = true;
|
||||
|
||||
ret = uncore_freq_add_entry(&cluster_info->uncore_data, 0);
|
||||
if (ret) {
|
||||
cluster_info->cluster_base = NULL;
|
||||
@ -427,6 +581,9 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_
|
||||
|
||||
auxiliary_set_drvdata(auxdev, tpmi_uncore);
|
||||
|
||||
if (topology_max_dies_per_package() > 1)
|
||||
return 0;
|
||||
|
||||
tpmi_uncore->root_cluster.root_domain = true;
|
||||
tpmi_uncore->root_cluster.uncore_root = tpmi_uncore;
|
||||
|
||||
@ -450,7 +607,9 @@ static void uncore_remove(struct auxiliary_device *auxdev)
|
||||
{
|
||||
struct tpmi_uncore_struct *tpmi_uncore = auxiliary_get_drvdata(auxdev);
|
||||
|
||||
uncore_freq_remove_die_entry(&tpmi_uncore->root_cluster.uncore_data);
|
||||
if (tpmi_uncore->root_cluster.root_domain)
|
||||
uncore_freq_remove_die_entry(&tpmi_uncore->root_cluster.uncore_data);
|
||||
|
||||
remove_cluster_entries(tpmi_uncore);
|
||||
|
||||
uncore_freq_common_exit();
|
||||
|
@ -17,14 +17,13 @@
|
||||
#include <linux/bits.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/intel_vsec.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "vsec.h"
|
||||
|
||||
#define PMT_XA_START 0
|
||||
#define PMT_XA_MAX INT_MAX
|
||||
#define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX)
|
||||
@ -213,6 +212,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he
|
||||
intel_vsec_dev->num_resources = header->num_entries;
|
||||
intel_vsec_dev->quirks = info->quirks;
|
||||
intel_vsec_dev->base_addr = info->base_addr;
|
||||
intel_vsec_dev->priv_data = info->priv_data;
|
||||
|
||||
if (header->id == VSEC_ID_SDSI)
|
||||
intel_vsec_dev->ida = &intel_vsec_sdsi_ida;
|
||||
@ -341,7 +341,7 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev,
|
||||
void intel_vsec_register(struct pci_dev *pdev,
|
||||
struct intel_vsec_platform_info *info)
|
||||
{
|
||||
if (!pdev || !info)
|
||||
if (!pdev || !info || !info->headers)
|
||||
return;
|
||||
|
||||
intel_vsec_walk_header(pdev, info);
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
#include <linux/platform_data/x86/intel_scu_ipc.h>
|
||||
|
||||
/* IPC defines the following message types */
|
||||
#define IPCMSG_PCNTRL 0xff /* Power controller unit read/write */
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
#include <linux/platform_data/x86/intel_scu_ipc.h>
|
||||
|
||||
static int major;
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
#include <linux/platform_data/x86/intel_scu_ipc.h>
|
||||
|
||||
static int intel_scu_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
#include <linux/platform_data/x86/intel_scu_ipc.h>
|
||||
|
||||
static int intel_scu_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -9,13 +9,14 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/intel-mid_wdt.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/io_apic.h>
|
||||
#include <asm/hw_irq.h>
|
||||
|
||||
#include <linux/platform_data/x86/intel-mid_wdt.h>
|
||||
|
||||
#define TANGIER_EXT_TIMER0_MSI 12
|
||||
|
||||
static struct platform_device wdt_dev = {
|
||||
|
@ -43,6 +43,8 @@ struct lenovo_ymc_private {
|
||||
};
|
||||
|
||||
static const struct key_entry lenovo_ymc_keymap[] = {
|
||||
/* Ignore the uninitialized state */
|
||||
{ KE_IGNORE, 0x00 },
|
||||
/* Laptop */
|
||||
{ KE_SW, 0x01, { .sw = { SW_TABLET_MODE, 0 } } },
|
||||
/* Tablet */
|
||||
|
@ -8,6 +8,9 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
@ -31,6 +34,26 @@ MODULE_AUTHOR("Matan Ziv-Av");
|
||||
MODULE_DESCRIPTION("LG WMI Hotkey Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static bool fw_debug;
|
||||
module_param(fw_debug, bool, 0);
|
||||
MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages");
|
||||
|
||||
#define LG_ADDRESS_SPACE_ID 0x8F
|
||||
|
||||
#define LG_ADDRESS_SPACE_DEBUG_FLAG_ADR 0x00
|
||||
#define LG_ADDRESS_SPACE_FAN_MODE_ADR 0x03
|
||||
|
||||
#define LG_ADDRESS_SPACE_DTTM_FLAG_ADR 0x20
|
||||
#define LG_ADDRESS_SPACE_CPU_TEMP_ADR 0x21
|
||||
#define LG_ADDRESS_SPACE_CPU_TRIP_LOW_ADR 0x22
|
||||
#define LG_ADDRESS_SPACE_CPU_TRIP_HIGH_ADR 0x23
|
||||
#define LG_ADDRESS_SPACE_MB_TEMP_ADR 0x24
|
||||
#define LG_ADDRESS_SPACE_MB_TRIP_LOW_ADR 0x25
|
||||
#define LG_ADDRESS_SPACE_MB_TRIP_HIGH_ADR 0x26
|
||||
|
||||
#define LG_ADDRESS_SPACE_DEBUG_MSG_START_ADR 0x3E8
|
||||
#define LG_ADDRESS_SPACE_DEBUG_MSG_END_ADR 0x5E8
|
||||
|
||||
#define WMI_EVENT_GUID0 "E4FB94F9-7F2B-4173-AD1A-CD1D95086248"
|
||||
#define WMI_EVENT_GUID1 "023B133E-49D1-4E10-B313-698220140DC2"
|
||||
#define WMI_EVENT_GUID2 "37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6"
|
||||
@ -182,21 +205,11 @@ static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u
|
||||
return (union acpi_object *)buffer.pointer;
|
||||
}
|
||||
|
||||
static void wmi_notify(u32 value, void *context)
|
||||
static void wmi_notify(union acpi_object *obj, void *context)
|
||||
{
|
||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
long data = (long)context;
|
||||
|
||||
pr_debug("event guid %li\n", data);
|
||||
status = wmi_get_event_data(value, &response);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_err("Bad event status 0x%x\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)response.pointer;
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
@ -218,7 +231,6 @@ static void wmi_notify(u32 value, void *context)
|
||||
|
||||
pr_debug("Type: %i Eventcode: 0x%llx\n", obj->type,
|
||||
obj->integer.value);
|
||||
kfree(response.pointer);
|
||||
}
|
||||
|
||||
static void wmi_input_setup(void)
|
||||
@ -646,6 +658,107 @@ static struct platform_driver pf_driver = {
|
||||
}
|
||||
};
|
||||
|
||||
static acpi_status lg_laptop_address_space_write(struct device *dev, acpi_physical_address address,
|
||||
size_t size, u64 value)
|
||||
{
|
||||
u8 byte;
|
||||
|
||||
/* Ignore any debug messages */
|
||||
if (address >= LG_ADDRESS_SPACE_DEBUG_MSG_START_ADR &&
|
||||
address <= LG_ADDRESS_SPACE_DEBUG_MSG_END_ADR)
|
||||
return AE_OK;
|
||||
|
||||
if (size != sizeof(byte))
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
byte = value & 0xFF;
|
||||
|
||||
switch (address) {
|
||||
case LG_ADDRESS_SPACE_FAN_MODE_ADR:
|
||||
/*
|
||||
* The fan mode field is not affected by the DTTM flag, so we
|
||||
* have to manually check fw_debug.
|
||||
*/
|
||||
if (fw_debug)
|
||||
dev_dbg(dev, "Fan mode set to mode %u\n", byte);
|
||||
|
||||
return AE_OK;
|
||||
case LG_ADDRESS_SPACE_CPU_TEMP_ADR:
|
||||
dev_dbg(dev, "CPU temperature is %u °C\n", byte);
|
||||
return AE_OK;
|
||||
case LG_ADDRESS_SPACE_CPU_TRIP_LOW_ADR:
|
||||
dev_dbg(dev, "CPU lower trip point set to %u °C\n", byte);
|
||||
return AE_OK;
|
||||
case LG_ADDRESS_SPACE_CPU_TRIP_HIGH_ADR:
|
||||
dev_dbg(dev, "CPU higher trip point set to %u °C\n", byte);
|
||||
return AE_OK;
|
||||
case LG_ADDRESS_SPACE_MB_TEMP_ADR:
|
||||
dev_dbg(dev, "Motherboard temperature is %u °C\n", byte);
|
||||
return AE_OK;
|
||||
case LG_ADDRESS_SPACE_MB_TRIP_LOW_ADR:
|
||||
dev_dbg(dev, "Motherboard lower trip point set to %u °C\n", byte);
|
||||
return AE_OK;
|
||||
case LG_ADDRESS_SPACE_MB_TRIP_HIGH_ADR:
|
||||
dev_dbg(dev, "Motherboard higher trip point set to %u °C\n", byte);
|
||||
return AE_OK;
|
||||
default:
|
||||
dev_notice_ratelimited(dev, "Ignoring write to unknown opregion address %llu\n",
|
||||
address);
|
||||
return AE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static acpi_status lg_laptop_address_space_read(struct device *dev, acpi_physical_address address,
|
||||
size_t size, u64 *value)
|
||||
{
|
||||
if (size != 1)
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
switch (address) {
|
||||
case LG_ADDRESS_SPACE_DEBUG_FLAG_ADR:
|
||||
/* Debug messages are already printed using the standard ACPI Debug object */
|
||||
*value = 0x00;
|
||||
return AE_OK;
|
||||
case LG_ADDRESS_SPACE_DTTM_FLAG_ADR:
|
||||
*value = fw_debug;
|
||||
return AE_OK;
|
||||
default:
|
||||
dev_notice_ratelimited(dev, "Attempt to read unknown opregion address %llu\n",
|
||||
address);
|
||||
return AE_BAD_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
static acpi_status lg_laptop_address_space_handler(u32 function, acpi_physical_address address,
|
||||
u32 bits, u64 *value, void *handler_context,
|
||||
void *region_context)
|
||||
{
|
||||
struct device *dev = handler_context;
|
||||
size_t size;
|
||||
|
||||
if (bits % BITS_PER_BYTE)
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
size = bits / BITS_PER_BYTE;
|
||||
|
||||
switch (function) {
|
||||
case ACPI_READ:
|
||||
return lg_laptop_address_space_read(dev, address, size, value);
|
||||
case ACPI_WRITE:
|
||||
return lg_laptop_address_space_write(dev, address, size, *value);
|
||||
default:
|
||||
return AE_BAD_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
static void lg_laptop_remove_address_space_handler(void *data)
|
||||
{
|
||||
struct acpi_device *device = data;
|
||||
|
||||
acpi_remove_address_space_handler(device->handle, LG_ADDRESS_SPACE_ID,
|
||||
&lg_laptop_address_space_handler);
|
||||
}
|
||||
|
||||
static int acpi_add(struct acpi_device *device)
|
||||
{
|
||||
struct platform_device_info pdev_info = {
|
||||
@ -653,6 +766,7 @@ static int acpi_add(struct acpi_device *device)
|
||||
.name = PLATFORM_NAME,
|
||||
.id = PLATFORM_DEVID_NONE,
|
||||
};
|
||||
acpi_status status;
|
||||
int ret;
|
||||
const char *product;
|
||||
int year = 2017;
|
||||
@ -660,6 +774,17 @@ static int acpi_add(struct acpi_device *device)
|
||||
if (pf_device)
|
||||
return 0;
|
||||
|
||||
status = acpi_install_address_space_handler(device->handle, LG_ADDRESS_SPACE_ID,
|
||||
&lg_laptop_address_space_handler,
|
||||
NULL, &device->dev);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
ret = devm_add_action_or_reset(&device->dev, lg_laptop_remove_address_space_handler,
|
||||
device);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = platform_driver_register(&pf_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -170,20 +170,9 @@ static const struct backlight_ops msi_backlight_ops = {
|
||||
.update_status = bl_set_status,
|
||||
};
|
||||
|
||||
static void msi_wmi_notify(u32 value, void *context)
|
||||
static void msi_wmi_notify(union acpi_object *obj, void *context)
|
||||
{
|
||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
struct key_entry *key;
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
|
||||
status = wmi_get_event_data(value, &response);
|
||||
if (status != AE_OK) {
|
||||
pr_info("bad event status 0x%x\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)response.pointer;
|
||||
|
||||
if (obj && obj->type == ACPI_TYPE_INTEGER) {
|
||||
int eventcode = obj->integer.value;
|
||||
@ -192,7 +181,7 @@ static void msi_wmi_notify(u32 value, void *context)
|
||||
eventcode);
|
||||
if (!key) {
|
||||
pr_info("Unknown key pressed - %x\n", eventcode);
|
||||
goto msi_wmi_notify_exit;
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_wmi->quirk_last_pressed) {
|
||||
@ -204,7 +193,7 @@ static void msi_wmi_notify(u32 value, void *context)
|
||||
pr_debug("Suppressed key event 0x%X - "
|
||||
"Last press was %lld us ago\n",
|
||||
key->code, ktime_to_us(diff));
|
||||
goto msi_wmi_notify_exit;
|
||||
return;
|
||||
}
|
||||
last_pressed = cur;
|
||||
}
|
||||
@ -221,9 +210,6 @@ static void msi_wmi_notify(u32 value, void *context)
|
||||
}
|
||||
} else
|
||||
pr_info("Unknown event received\n");
|
||||
|
||||
msi_wmi_notify_exit:
|
||||
kfree(response.pointer);
|
||||
}
|
||||
|
||||
static int __init msi_wmi_backlight_setup(void)
|
||||
|
@ -121,6 +121,7 @@
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/i8042.h>
|
||||
#include <linux/init.h>
|
||||
@ -224,6 +225,17 @@ static const struct key_entry panasonic_keymap[] = {
|
||||
{ KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */
|
||||
{ KE_KEY, 9, { KEY_BATTERY } },
|
||||
{ KE_KEY, 10, { KEY_SUSPEND } },
|
||||
{ KE_KEY, 21, { KEY_MACRO1 } },
|
||||
{ KE_KEY, 22, { KEY_MACRO2 } },
|
||||
{ KE_KEY, 24, { KEY_MACRO3 } },
|
||||
{ KE_KEY, 25, { KEY_MACRO4 } },
|
||||
{ KE_KEY, 34, { KEY_MACRO5 } },
|
||||
{ KE_KEY, 35, { KEY_MACRO6 } },
|
||||
{ KE_KEY, 36, { KEY_MACRO7 } },
|
||||
{ KE_KEY, 37, { KEY_MACRO8 } },
|
||||
{ KE_KEY, 41, { KEY_MACRO9 } },
|
||||
{ KE_KEY, 42, { KEY_MACRO10 } },
|
||||
{ KE_KEY, 43, { KEY_MACRO11 } },
|
||||
{ KE_END, 0 }
|
||||
};
|
||||
|
||||
@ -830,8 +842,8 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
|
||||
return;
|
||||
}
|
||||
|
||||
key = result & 0xf;
|
||||
updown = result & 0x80; /* 0x80 == key down; 0x00 = key up */
|
||||
key = result & GENMASK(6, 0);
|
||||
updown = result & BIT(7); /* 0x80 == key down; 0x00 = key up */
|
||||
|
||||
/* hack: some firmware sends no key down for sleep / hibernate */
|
||||
if (key == 7 || key == 10) {
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rfkill.h>
|
||||
@ -554,7 +553,7 @@ static int update_status(struct backlight_device *bd)
|
||||
|
||||
set_brightness(samsung, bd->props.brightness);
|
||||
|
||||
if (bd->props.power == FB_BLANK_UNBLANK)
|
||||
if (bd->props.power == BACKLIGHT_POWER_ON)
|
||||
sabi_set_commandb(samsung, commands->set_backlight, 1);
|
||||
else
|
||||
sabi_set_commandb(samsung, commands->set_backlight, 0);
|
||||
@ -1189,7 +1188,7 @@ static int __init samsung_backlight_init(struct samsung_laptop *samsung)
|
||||
|
||||
samsung->backlight_device = bd;
|
||||
samsung->backlight_device->props.brightness = read_brightness(samsung);
|
||||
samsung->backlight_device->props.power = FB_BLANK_UNBLANK;
|
||||
samsung->backlight_device->props.power = BACKLIGHT_POWER_ON;
|
||||
backlight_update_status(samsung->backlight_device);
|
||||
|
||||
return 0;
|
||||
|
@ -83,11 +83,15 @@ static int smi_get_irq(struct platform_device *pdev, struct acpi_device *adev,
|
||||
|
||||
static void smi_devs_unregister(struct smi *smi)
|
||||
{
|
||||
#if IS_REACHABLE(CONFIG_I2C)
|
||||
while (smi->i2c_num--)
|
||||
i2c_unregister_device(smi->i2c_devs[smi->i2c_num]);
|
||||
#endif
|
||||
|
||||
while (smi->spi_num--)
|
||||
spi_unregister_device(smi->spi_devs[smi->spi_num]);
|
||||
if (IS_REACHABLE(CONFIG_SPI)) {
|
||||
while (smi->spi_num--)
|
||||
spi_unregister_device(smi->spi_devs[smi->spi_num]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -258,9 +262,15 @@ static int smi_probe(struct platform_device *pdev)
|
||||
|
||||
switch (node->bus_type) {
|
||||
case SMI_I2C:
|
||||
return smi_i2c_probe(pdev, smi, node->instances);
|
||||
if (IS_REACHABLE(CONFIG_I2C))
|
||||
return smi_i2c_probe(pdev, smi, node->instances);
|
||||
|
||||
return -ENODEV;
|
||||
case SMI_SPI:
|
||||
return smi_spi_probe(pdev, smi, node->instances);
|
||||
if (IS_REACHABLE(CONFIG_SPI))
|
||||
return smi_spi_probe(pdev, smi, node->instances);
|
||||
|
||||
return -ENODEV;
|
||||
case SMI_AUTO_DETECT:
|
||||
/*
|
||||
* For backwards-compatibility with the existing nodes I2C
|
||||
@ -270,10 +280,16 @@ static int smi_probe(struct platform_device *pdev)
|
||||
* SpiSerialBus nodes that were previously ignored, and this
|
||||
* preserves that behavior.
|
||||
*/
|
||||
ret = smi_i2c_probe(pdev, smi, node->instances);
|
||||
if (ret != -ENOENT)
|
||||
return ret;
|
||||
return smi_spi_probe(pdev, smi, node->instances);
|
||||
if (IS_REACHABLE(CONFIG_I2C)) {
|
||||
ret = smi_i2c_probe(pdev, smi, node->instances);
|
||||
if (ret != -ENOENT)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (IS_REACHABLE(CONFIG_SPI))
|
||||
return smi_spi_probe(pdev, smi, node->instances);
|
||||
|
||||
return -ENODEV;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -7749,6 +7749,28 @@ static struct ibm_struct volume_driver_data = {
|
||||
* EC 0x2f (HFSP) might be available *for reading*, but do not use
|
||||
* it for writing.
|
||||
*
|
||||
* TPACPI_FAN_RD_ACPI_FANG:
|
||||
* ACPI FANG method: returns fan control register
|
||||
*
|
||||
* Takes one parameter which is 0x8100 plus the offset to EC memory
|
||||
* address 0xf500 and returns the byte at this address.
|
||||
*
|
||||
* 0xf500:
|
||||
* When the value is less than 9 automatic mode is enabled
|
||||
* 0xf502:
|
||||
* Contains the current fan speed from 0-100%
|
||||
* 0xf506:
|
||||
* Bit 7 has to be set in order to enable manual control by
|
||||
* writing a value >= 9 to 0xf500
|
||||
*
|
||||
* TPACPI_FAN_WR_ACPI_FANW:
|
||||
* ACPI FANW method: sets fan control registers
|
||||
*
|
||||
* Takes 0x8100 plus the offset to EC memory address 0xf500 and the
|
||||
* value to be written there as parameters.
|
||||
*
|
||||
* see TPACPI_FAN_RD_ACPI_FANG
|
||||
*
|
||||
* TPACPI_FAN_WR_TPEC:
|
||||
* ThinkPad EC register 0x2f (HFSP): fan control loop mode
|
||||
* Supported on almost all ThinkPads
|
||||
@ -7882,6 +7904,7 @@ enum { /* Fan control constants */
|
||||
enum fan_status_access_mode {
|
||||
TPACPI_FAN_NONE = 0, /* No fan status or control */
|
||||
TPACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */
|
||||
TPACPI_FAN_RD_ACPI_FANG, /* Use ACPI FANG */
|
||||
TPACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */
|
||||
TPACPI_FAN_RD_TPEC_NS, /* Use non-standard ACPI EC regs (eg: L13 Yoga gen2 etc.) */
|
||||
};
|
||||
@ -7889,6 +7912,7 @@ enum fan_status_access_mode {
|
||||
enum fan_control_access_mode {
|
||||
TPACPI_FAN_WR_NONE = 0, /* No fan control */
|
||||
TPACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */
|
||||
TPACPI_FAN_WR_ACPI_FANW, /* Use ACPI FANW */
|
||||
TPACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */
|
||||
TPACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */
|
||||
};
|
||||
@ -7922,9 +7946,13 @@ TPACPI_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */
|
||||
TPACPI_HANDLE(gfan, ec, "GFAN", /* 570 */
|
||||
"\\FSPD", /* 600e/x, 770e, 770x */
|
||||
); /* all others */
|
||||
TPACPI_HANDLE(fang, ec, "FANG", /* E531 */
|
||||
); /* all others */
|
||||
TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */
|
||||
"JFNS", /* 770x-JL */
|
||||
); /* all others */
|
||||
TPACPI_HANDLE(fanw, ec, "FANW", /* E531 */
|
||||
); /* all others */
|
||||
|
||||
/*
|
||||
* Unitialized HFSP quirk: ACPI DSDT and EC fail to initialize the
|
||||
@ -8031,6 +8059,23 @@ static int fan_get_status(u8 *status)
|
||||
|
||||
break;
|
||||
}
|
||||
case TPACPI_FAN_RD_ACPI_FANG: {
|
||||
/* E531 */
|
||||
int mode, speed;
|
||||
|
||||
if (unlikely(!acpi_evalf(fang_handle, &mode, NULL, "dd", 0x8100)))
|
||||
return -EIO;
|
||||
if (unlikely(!acpi_evalf(fang_handle, &speed, NULL, "dd", 0x8102)))
|
||||
return -EIO;
|
||||
|
||||
if (likely(status)) {
|
||||
*status = speed * 7 / 100;
|
||||
if (mode < 9)
|
||||
*status |= TP_EC_FAN_AUTO;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case TPACPI_FAN_RD_TPEC:
|
||||
/* all except 570, 600e/x, 770e, 770x */
|
||||
if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
|
||||
@ -8145,6 +8190,17 @@ static int fan2_get_speed(unsigned int *speed)
|
||||
if (speed)
|
||||
*speed = lo ? FAN_RPM_CAL_CONST / lo : 0;
|
||||
break;
|
||||
case TPACPI_FAN_RD_ACPI_FANG: {
|
||||
/* E531 */
|
||||
int speed_tmp;
|
||||
|
||||
if (unlikely(!acpi_evalf(fang_handle, &speed_tmp, NULL, "dd", 0x8102)))
|
||||
return -EIO;
|
||||
|
||||
if (likely(speed))
|
||||
*speed = speed_tmp * 65535 / 100;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -ENXIO;
|
||||
@ -8204,6 +8260,32 @@ static int fan_set_level(int level)
|
||||
tp_features.fan_ctrl_status_undef = 0;
|
||||
break;
|
||||
|
||||
case TPACPI_FAN_WR_ACPI_FANW:
|
||||
if (!(level & TP_EC_FAN_AUTO) && (level < 0 || level > 7))
|
||||
return -EINVAL;
|
||||
if (level & TP_EC_FAN_FULLSPEED)
|
||||
return -EINVAL;
|
||||
|
||||
if (level & TP_EC_FAN_AUTO) {
|
||||
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x05)) {
|
||||
return -EIO;
|
||||
}
|
||||
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0x00)) {
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) {
|
||||
return -EIO;
|
||||
}
|
||||
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) {
|
||||
return -EIO;
|
||||
}
|
||||
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8102, level * 100 / 7)) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
@ -8236,7 +8318,7 @@ static int fan_set_level_safe(int level)
|
||||
|
||||
static int fan_set_enable(void)
|
||||
{
|
||||
u8 s;
|
||||
u8 s = 0;
|
||||
int rc;
|
||||
|
||||
if (!fan_control_allowed)
|
||||
@ -8282,6 +8364,19 @@ static int fan_set_enable(void)
|
||||
rc = 0;
|
||||
break;
|
||||
|
||||
case TPACPI_FAN_WR_ACPI_FANW:
|
||||
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x05)) {
|
||||
rc = -EIO;
|
||||
break;
|
||||
}
|
||||
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0x00)) {
|
||||
rc = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = -ENXIO;
|
||||
}
|
||||
@ -8324,6 +8419,22 @@ static int fan_set_disable(void)
|
||||
fan_control_desired_level = 0;
|
||||
break;
|
||||
|
||||
case TPACPI_FAN_WR_ACPI_FANW:
|
||||
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) {
|
||||
rc = -EIO;
|
||||
break;
|
||||
}
|
||||
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) {
|
||||
rc = -EIO;
|
||||
break;
|
||||
}
|
||||
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8102, 0x00)) {
|
||||
rc = -EIO;
|
||||
break;
|
||||
}
|
||||
rc = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = -ENXIO;
|
||||
}
|
||||
@ -8357,6 +8468,23 @@ static int fan_set_speed(int speed)
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
|
||||
case TPACPI_FAN_WR_ACPI_FANW:
|
||||
if (speed >= 0 && speed <= 65535) {
|
||||
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) {
|
||||
rc = -EIO;
|
||||
break;
|
||||
}
|
||||
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) {
|
||||
rc = -EIO;
|
||||
break;
|
||||
}
|
||||
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd",
|
||||
0x8102, speed * 100 / 65535))
|
||||
rc = -EIO;
|
||||
} else
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = -ENXIO;
|
||||
}
|
||||
@ -8699,6 +8827,10 @@ static int __init fan_init(struct ibm_init_struct *iibm)
|
||||
TPACPI_ACPIHANDLE_INIT(gfan);
|
||||
TPACPI_ACPIHANDLE_INIT(sfan);
|
||||
}
|
||||
if (tpacpi_is_lenovo()) {
|
||||
TPACPI_ACPIHANDLE_INIT(fang);
|
||||
TPACPI_ACPIHANDLE_INIT(fanw);
|
||||
}
|
||||
|
||||
quirks = tpacpi_check_quirks(fan_quirk_table,
|
||||
ARRAY_SIZE(fan_quirk_table));
|
||||
@ -8718,6 +8850,9 @@ static int __init fan_init(struct ibm_init_struct *iibm)
|
||||
if (gfan_handle) {
|
||||
/* 570, 600e/x, 770e, 770x */
|
||||
fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
|
||||
} else if (fang_handle) {
|
||||
/* E531 */
|
||||
fan_status_access_mode = TPACPI_FAN_RD_ACPI_FANG;
|
||||
} else {
|
||||
/* all other ThinkPads: note that even old-style
|
||||
* ThinkPad ECs supports the fan control register */
|
||||
@ -8764,6 +8899,11 @@ static int __init fan_init(struct ibm_init_struct *iibm)
|
||||
fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN;
|
||||
fan_control_commands |=
|
||||
TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE;
|
||||
} else if (fanw_handle) {
|
||||
/* E531 */
|
||||
fan_control_access_mode = TPACPI_FAN_WR_ACPI_FANW;
|
||||
fan_control_commands |=
|
||||
TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_SPEED | TPACPI_FAN_CMD_ENABLE;
|
||||
} else {
|
||||
if (!gfan_handle) {
|
||||
/* gfan without sfan means no fan control */
|
||||
@ -8915,6 +9055,7 @@ static int fan_read(struct seq_file *m)
|
||||
|
||||
case TPACPI_FAN_RD_TPEC_NS:
|
||||
case TPACPI_FAN_RD_TPEC:
|
||||
case TPACPI_FAN_RD_ACPI_FANG:
|
||||
/* all except 570, 600e/x, 770e, 770x */
|
||||
rc = fan_get_status_safe(&status);
|
||||
if (rc)
|
||||
@ -8935,7 +9076,7 @@ static int fan_read(struct seq_file *m)
|
||||
* No other levels settings available
|
||||
*/
|
||||
seq_printf(m, "level:\t\t%s\n", status & FAN_NS_CTRL ? "unknown" : "auto");
|
||||
} else {
|
||||
} else if (fan_status_access_mode == TPACPI_FAN_RD_TPEC) {
|
||||
if (status & TP_EC_FAN_FULLSPEED)
|
||||
/* Disengaged mode takes precedence */
|
||||
seq_printf(m, "level:\t\tdisengaged\n");
|
||||
|
@ -32,26 +32,13 @@ static const struct key_entry toshiba_wmi_keymap[] __initconst = {
|
||||
{ KE_END, 0 }
|
||||
};
|
||||
|
||||
static void toshiba_wmi_notify(u32 value, void *context)
|
||||
static void toshiba_wmi_notify(union acpi_object *obj, void *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)) {
|
||||
pr_err("Bad event status 0x%x\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)response.pointer;
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
/* TODO: Add proper checks once we have data */
|
||||
pr_debug("Unknown event received, obj type %x\n", obj->type);
|
||||
|
||||
kfree(response.pointer);
|
||||
}
|
||||
|
||||
static const struct dmi_system_id toshiba_wmi_dmi_table[] __initconst = {
|
||||
|
@ -840,6 +840,21 @@ static const struct ts_dmi_data rwc_nanote_p8_data = {
|
||||
.properties = rwc_nanote_p8_props,
|
||||
};
|
||||
|
||||
static const struct property_entry rwc_nanote_next_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-min-x", 5),
|
||||
PROPERTY_ENTRY_U32("touchscreen-min-y", 5),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1785),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1145),
|
||||
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
|
||||
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-rwc-nanote-next.fw"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct ts_dmi_data rwc_nanote_next_data = {
|
||||
.acpi_name = "MSSL1680:00",
|
||||
.properties = rwc_nanote_next_props,
|
||||
};
|
||||
|
||||
static const struct property_entry schneider_sct101ctm_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1715),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
|
||||
@ -1589,6 +1604,17 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
|
||||
DMI_MATCH(DMI_PRODUCT_SKU, "0001")
|
||||
},
|
||||
},
|
||||
{
|
||||
/* RWC NANOTE NEXT */
|
||||
.driver_data = (void *)&rwc_nanote_next_data,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "To be filled by O.E.M."),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "To be filled by O.E.M."),
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."),
|
||||
/* Above matches are too generic, add bios-version match */
|
||||
DMI_MATCH(DMI_BIOS_VERSION, "S8A70R100-V005"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Schneider SCT101CTM */
|
||||
.driver_data = (void *)&schneider_sct101ctm_data,
|
||||
|
@ -166,22 +166,6 @@ static inline acpi_object_type get_param_acpi_type(const struct wmi_block *wbloc
|
||||
return ACPI_TYPE_BUFFER;
|
||||
}
|
||||
|
||||
static acpi_status get_event_data(const struct wmi_block *wblock, struct acpi_buffer *out)
|
||||
{
|
||||
union acpi_object param = {
|
||||
.integer = {
|
||||
.type = ACPI_TYPE_INTEGER,
|
||||
.value = wblock->gblock.notify_id,
|
||||
}
|
||||
};
|
||||
struct acpi_object_list input = {
|
||||
.count = 1,
|
||||
.pointer = ¶m,
|
||||
};
|
||||
|
||||
return acpi_evaluate_object(wblock->acpi_device->handle, "_WED", &input, out);
|
||||
}
|
||||
|
||||
static int wmidev_match_guid(struct device *dev, const void *data)
|
||||
{
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
@ -199,23 +183,6 @@ static int wmidev_match_guid(struct device *dev, const void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wmidev_match_notify_id(struct device *dev, const void *data)
|
||||
{
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
const u32 *notify_id = data;
|
||||
|
||||
/* Legacy GUID-based functions are restricted to only see
|
||||
* a single WMI device for each GUID.
|
||||
*/
|
||||
if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags))
|
||||
return 0;
|
||||
|
||||
if (wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *notify_id)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct bus_type wmi_bus_type;
|
||||
|
||||
static struct wmi_device *wmi_find_device_by_guid(const char *guid_string)
|
||||
@ -235,17 +202,6 @@ static struct wmi_device *wmi_find_device_by_guid(const char *guid_string)
|
||||
return dev_to_wdev(dev);
|
||||
}
|
||||
|
||||
static struct wmi_device *wmi_find_event_by_notify_id(const u32 notify_id)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
dev = bus_find_device(&wmi_bus_type, NULL, ¬ify_id, wmidev_match_notify_id);
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return to_wmi_device(dev);
|
||||
}
|
||||
|
||||
static void wmi_device_put(struct wmi_device *wdev)
|
||||
{
|
||||
put_device(&wdev->dev);
|
||||
@ -649,35 +605,6 @@ acpi_status wmi_remove_notify_handler(const char *guid)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
|
||||
|
||||
/**
|
||||
* wmi_get_event_data - Get WMI data associated with an event (deprecated)
|
||||
*
|
||||
* @event: Event to find
|
||||
* @out: Buffer to hold event data
|
||||
*
|
||||
* Get extra data associated with an WMI event, the caller needs to free @out.
|
||||
*
|
||||
* Return: acpi_status signaling success or error.
|
||||
*/
|
||||
acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
|
||||
{
|
||||
struct wmi_block *wblock;
|
||||
struct wmi_device *wdev;
|
||||
acpi_status status;
|
||||
|
||||
wdev = wmi_find_event_by_notify_id(event);
|
||||
if (IS_ERR(wdev))
|
||||
return AE_NOT_FOUND;
|
||||
|
||||
wblock = container_of(wdev, struct wmi_block, dev);
|
||||
status = get_event_data(wblock, out);
|
||||
|
||||
wmi_device_put(wdev);
|
||||
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wmi_get_event_data);
|
||||
|
||||
/**
|
||||
* wmi_has_guid - Check if a GUID is available
|
||||
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
|
||||
@ -1186,14 +1113,19 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev)
|
||||
static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj)
|
||||
{
|
||||
struct acpi_buffer data = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object param = {
|
||||
.integer = {
|
||||
.type = ACPI_TYPE_INTEGER,
|
||||
.value = wblock->gblock.notify_id,
|
||||
}
|
||||
};
|
||||
struct acpi_object_list input = {
|
||||
.count = 1,
|
||||
.pointer = ¶m,
|
||||
};
|
||||
acpi_status status;
|
||||
|
||||
if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags)) {
|
||||
*obj = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
status = get_event_data(wblock, &data);
|
||||
status = acpi_evaluate_object(wblock->acpi_device->handle, "_WED", &input, &data);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_warn(&wblock->dev.dev, "Failed to get event data\n");
|
||||
return -EIO;
|
||||
@ -1220,47 +1152,40 @@ static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj)
|
||||
static int wmi_notify_device(struct device *dev, void *data)
|
||||
{
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
union acpi_object *obj;
|
||||
union acpi_object *obj = NULL;
|
||||
u32 *event = data;
|
||||
int ret;
|
||||
|
||||
if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event))
|
||||
return 0;
|
||||
|
||||
down_read(&wblock->notify_lock);
|
||||
/* The WMI driver notify handler conflicts with the legacy WMI handler.
|
||||
* Because of this the WMI driver notify handler takes precedence.
|
||||
/* The ACPI WMI specification says that _WED should be
|
||||
* evaluated every time an notification is received, even
|
||||
* if no consumers are present.
|
||||
*
|
||||
* Some firmware implementations actually depend on this
|
||||
* by using a queue for events which will fill up if the
|
||||
* WMI driver core stops evaluating _WED due to missing
|
||||
* WMI event consumers.
|
||||
*/
|
||||
if (wblock->dev.dev.driver && wblock->driver_ready) {
|
||||
if (!test_bit(WMI_NO_EVENT_DATA, &wblock->flags)) {
|
||||
ret = wmi_get_notify_data(wblock, &obj);
|
||||
if (ret >= 0) {
|
||||
wmi_notify_driver(wblock, obj);
|
||||
kfree(obj);
|
||||
}
|
||||
} else {
|
||||
if (wblock->handler) {
|
||||
wblock->handler(*event, wblock->handler_data);
|
||||
} else {
|
||||
/* The ACPI WMI specification says that _WED should be
|
||||
* evaluated every time an notification is received, even
|
||||
* if no consumers are present.
|
||||
*
|
||||
* Some firmware implementations actually depend on this
|
||||
* by using a queue for events which will fill up if the
|
||||
* WMI driver core stops evaluating _WED due to missing
|
||||
* WMI event consumers.
|
||||
*
|
||||
* Because of this we need this seemingly useless call to
|
||||
* wmi_get_notify_data() which in turn evaluates _WED.
|
||||
*/
|
||||
ret = wmi_get_notify_data(wblock, &obj);
|
||||
if (ret >= 0)
|
||||
kfree(obj);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
down_read(&wblock->notify_lock);
|
||||
|
||||
if (wblock->dev.dev.driver && wblock->driver_ready)
|
||||
wmi_notify_driver(wblock, obj);
|
||||
|
||||
if (wblock->handler)
|
||||
wblock->handler(obj, wblock->handler_data);
|
||||
|
||||
up_read(&wblock->notify_lock);
|
||||
|
||||
kfree(obj);
|
||||
|
||||
acpi_bus_generate_netlink_event("wmi", acpi_dev_name(wblock->acpi_device), *event, 0);
|
||||
|
||||
return -EBUSY;
|
||||
|
@ -20,4 +20,4 @@ config X86_ANDROID_TABLETS
|
||||
are missing from the DSDT.
|
||||
|
||||
If you have a x86 Android tablet say Y or M here, for a generic x86
|
||||
distro config say M here.
|
||||
distro configuration say M here.
|
||||
|
@ -37,7 +37,7 @@ static const struct x86_gpio_button asus_me176c_tf103c_lid __initconst = {
|
||||
.pin = 12,
|
||||
};
|
||||
|
||||
/* Asus ME176C tablets have an Android factory img with everything hardcoded */
|
||||
/* Asus ME176C tablets have an Android factory image with everything hardcoded */
|
||||
static const char * const asus_me176c_accel_mount_matrix[] = {
|
||||
"-1", "0", "0",
|
||||
"0", "1", "0",
|
||||
@ -112,7 +112,7 @@ static const struct x86_i2c_client_info asus_me176c_i2c_clients[] __initconst =
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C5",
|
||||
}, {
|
||||
/* kxtj21009 accel */
|
||||
/* kxtj21009 accelerometer */
|
||||
.board_info = {
|
||||
.type = "kxtj21009",
|
||||
.addr = 0x0f,
|
||||
@ -181,7 +181,7 @@ const struct x86_dev_info asus_me176c_info __initconst = {
|
||||
.modules = bq24190_modules,
|
||||
};
|
||||
|
||||
/* Asus TF103C tablets have an Android factory img with everything hardcoded */
|
||||
/* Asus TF103C tablets have an Android factory image with everything hardcoded */
|
||||
static const char * const asus_tf103c_accel_mount_matrix[] = {
|
||||
"0", "-1", "0",
|
||||
"-1", "0", "0",
|
||||
@ -280,7 +280,7 @@ static const struct x86_i2c_client_info asus_tf103c_i2c_clients[] __initconst =
|
||||
},
|
||||
.adapter_path = "\\_SB_.I2C5",
|
||||
}, {
|
||||
/* kxtj21009 accel */
|
||||
/* kxtj21009 accelerometer */
|
||||
.board_info = {
|
||||
.type = "kxtj21009",
|
||||
.addr = 0x0f,
|
||||
|
@ -26,19 +26,19 @@
|
||||
static struct platform_device *x86_android_tablet_device;
|
||||
|
||||
/*
|
||||
* This helper allows getting a gpio_desc *before* the actual device consuming
|
||||
* the GPIO has been instantiated. This function _must_ only be used to handle
|
||||
* this special case such as e.g. :
|
||||
* This helper allows getting a GPIO descriptor *before* the actual device
|
||||
* consuming it has been instantiated. This function MUST only be used to
|
||||
* handle this special case such as, e.g.:
|
||||
*
|
||||
* 1. Getting an IRQ from a GPIO for i2c_board_info.irq which is passed to
|
||||
* i2c_client_new() to instantiate i2c_client-s; or
|
||||
* 2. Calling desc_to_gpio() to get an old style GPIO number for gpio_keys
|
||||
* 2. Calling desc_to_gpio() to get an old style GPIO number for gpio-keys
|
||||
* platform_data which still uses old style GPIO numbers.
|
||||
*
|
||||
* Since the consuming device has not been instatiated yet a dynamic lookup
|
||||
* is generated using the special x86_android_tablet dev for dev_id.
|
||||
* Since the consuming device has not been instantiated yet a dynamic lookup
|
||||
* is generated using the special x86_android_tablet device for dev_id.
|
||||
*
|
||||
* For normal GPIO lookups a standard static gpiod_lookup_table _must_ be used.
|
||||
* For normal GPIO lookups a standard static struct gpiod_lookup_table MUST be used.
|
||||
*/
|
||||
int x86_android_tablet_get_gpiod(const char *chip, int pin, const char *con_id,
|
||||
bool active_low, enum gpiod_flags dflags,
|
||||
@ -87,7 +87,7 @@ int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data)
|
||||
/*
|
||||
* The DSDT may already reference the GSI in a device skipped by
|
||||
* acpi_quirk_skip_i2c_client_enumeration(). Unregister the GSI
|
||||
* to avoid EBUSY errors in this case.
|
||||
* to avoid -EBUSY errors in this case.
|
||||
*/
|
||||
acpi_unregister_gsi(data->index);
|
||||
irq = acpi_register_gsi(NULL, data->index, data->trigger, data->polarity);
|
||||
@ -379,7 +379,7 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
/* + 1 to make space for (optional) gpio_keys_button pdev */
|
||||
/* + 1 to make space for the (optional) gpio_keys_button platform device */
|
||||
pdevs = kcalloc(dev_info->pdev_count + 1, sizeof(*pdevs), GFP_KERNEL);
|
||||
if (!pdevs) {
|
||||
x86_android_tablet_remove(pdev);
|
||||
@ -432,7 +432,7 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev)
|
||||
|
||||
buttons[i] = dev_info->gpio_button[i].button;
|
||||
buttons[i].gpio = desc_to_gpio(gpiod);
|
||||
/* Release gpiod so that gpio-keys can request it */
|
||||
/* Release GPIO descriptor so that gpio-keys can request it */
|
||||
devm_gpiod_put(&x86_android_tablet_device->dev, gpiod);
|
||||
}
|
||||
|
||||
|
@ -99,17 +99,17 @@ const struct dmi_system_id x86_android_tablet_ids[] __initconst = {
|
||||
{
|
||||
/* Lenovo Yoga Book X91F / X91L */
|
||||
.matches = {
|
||||
/* Non exact match to match F + L versions */
|
||||
/* Inexact match to match F + L versions */
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91"),
|
||||
},
|
||||
.driver_data = (void *)&lenovo_yogabook_x91_info,
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Lenovo Yoga Tablet 2 Pro 1380F/L (13") This has more or less
|
||||
* the same BIOS as the 830F/L or 1050F/L (8" and 10") below,
|
||||
* but unlike the 8" / 10" models which share the same mainboard
|
||||
* this model has a different mainboard.
|
||||
* Lenovo Yoga Tablet 2 Pro 1380F/L (13")
|
||||
* This has more or less the same BIOS as the 830F/L or 1050F/L
|
||||
* (8" and 10") below, but unlike the 8"/10" models which share
|
||||
* the same mainboard this model has a different mainboard.
|
||||
* This match for the 13" model MUST come before the 8" + 10"
|
||||
* match since that one will also match the 13" model!
|
||||
*/
|
||||
@ -124,8 +124,8 @@ const struct dmi_system_id x86_android_tablet_ids[] __initconst = {
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Lenovo Yoga Tablet 2 830F/L or 1050F/L (The 8" and 10"
|
||||
* Lenovo Yoga Tablet 2 use the same mainboard)
|
||||
* Lenovo Yoga Tablet 2 830F/L or 1050F/L
|
||||
* The 8" and 10" Lenovo Yoga Tablet 2 use the same mainboard.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."),
|
||||
@ -163,7 +163,7 @@ const struct dmi_system_id x86_android_tablet_ids[] __initconst = {
|
||||
.driver_data = (void *)&nextbook_ares8_info,
|
||||
},
|
||||
{
|
||||
/* Nextbook Ares 8A (CHT version)*/
|
||||
/* Nextbook Ares 8A (CHT version) */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"),
|
||||
|
@ -59,7 +59,7 @@ static struct lp855x_platform_data lenovo_lp8557_reg_only_pdata = {
|
||||
.initial_brightness = 128,
|
||||
};
|
||||
|
||||
/* Lenovo Yoga Book X90F / X90L's Android factory img has everything hardcoded */
|
||||
/* Lenovo Yoga Book X90F / X90L's Android factory image has everything hardcoded */
|
||||
|
||||
static const struct property_entry lenovo_yb1_x90_wacom_props[] = {
|
||||
PROPERTY_ENTRY_U32("hid-descr-addr", 0x0001),
|
||||
@ -262,7 +262,7 @@ const struct x86_dev_info lenovo_yogabook_x90_info __initconst = {
|
||||
.init = lenovo_yb1_x90_init,
|
||||
};
|
||||
|
||||
/* Lenovo Yoga Book X91F/L Windows tablet needs manual instantiation of the fg client */
|
||||
/* Lenovo Yoga Book X91F/L Windows tablet needs manual instantiation of the fuel-gauge client */
|
||||
static const struct x86_i2c_client_info lenovo_yogabook_x91_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* BQ27542 fuel-gauge */
|
||||
@ -281,7 +281,7 @@ const struct x86_dev_info lenovo_yogabook_x91_info __initconst = {
|
||||
.i2c_client_count = ARRAY_SIZE(lenovo_yogabook_x91_i2c_clients),
|
||||
};
|
||||
|
||||
/* Lenovo Yoga Tablet 2 1050F/L's Android factory img has everything hardcoded */
|
||||
/* Lenovo Yoga Tablet 2 1050F/L's Android factory image has everything hardcoded */
|
||||
static const struct property_entry lenovo_yoga_tab2_830_1050_bq24190_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1),
|
||||
PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node),
|
||||
@ -521,9 +521,9 @@ err_put_device:
|
||||
}
|
||||
|
||||
/*
|
||||
* These tablet's DSDT does not set acpi_gbl_reduced_hardware, so acpi_power_off
|
||||
* These tablet's DSDT does not set acpi_gbl_reduced_hardware, so acpi_power_off()
|
||||
* gets used as pm_power_off handler. This causes "poweroff" on these tablets
|
||||
* to hang hard. Requiring pressing the powerbutton for 30 seconds *twice*
|
||||
* to hang hard. Requiring pressing the power button for 30 seconds *twice*
|
||||
* followed by a normal 3 second press to recover. Avoid this by doing an EFI
|
||||
* poweroff instead.
|
||||
*/
|
||||
@ -546,7 +546,7 @@ static int __init lenovo_yoga_tab2_830_1050_init(struct device *dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off */
|
||||
/* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off() */
|
||||
lenovo_yoga_tab2_830_1050_sys_off_handler =
|
||||
register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1,
|
||||
lenovo_yoga_tab2_830_1050_power_off, NULL);
|
||||
@ -742,7 +742,7 @@ static int __init lenovo_yoga_tab2_1380_init(struct device *dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off */
|
||||
/* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off() */
|
||||
lenovo_yoga_tab2_830_1050_sys_off_handler =
|
||||
register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1,
|
||||
lenovo_yoga_tab2_830_1050_power_off, NULL);
|
||||
@ -799,7 +799,7 @@ static const struct software_node fg_bq25890_1_supply_node = {
|
||||
.properties = fg_bq25890_1_supply_props,
|
||||
};
|
||||
|
||||
/* bq25892 charger settings for the flat lipo battery behind the screen */
|
||||
/* bq25892 charger settings for the flat LiPo battery behind the screen */
|
||||
static const struct property_entry lenovo_yt3_bq25892_0_props[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lenovo_yt3_bq25892_0_suppliers),
|
||||
PROPERTY_ENTRY_U32("linux,iinlim-percentage", 40),
|
||||
@ -833,7 +833,7 @@ static const struct software_node lenovo_yt3_hideep_ts_node = {
|
||||
|
||||
static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* bq27500 fuel-gauge for the flat lipo battery behind the screen */
|
||||
/* bq27500 fuel-gauge for the flat LiPo battery behind the screen */
|
||||
.board_info = {
|
||||
.type = "bq27500",
|
||||
.addr = 0x55,
|
||||
@ -842,7 +842,7 @@ static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = {
|
||||
},
|
||||
.adapter_path = "\\_SB_.PCI0.I2C1",
|
||||
}, {
|
||||
/* bq25892 charger for the flat lipo battery behind the screen */
|
||||
/* bq25892 charger for the flat LiPo battery behind the screen */
|
||||
.board_info = {
|
||||
.type = "bq25892",
|
||||
.addr = 0x6b,
|
||||
@ -859,7 +859,7 @@ static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = {
|
||||
.con_id = "bq25892_0_irq",
|
||||
},
|
||||
}, {
|
||||
/* bq27500 fuel-gauge for the round li-ion cells in the hinge */
|
||||
/* bq27500 fuel-gauge for the round Li-ion cells in the hinge */
|
||||
.board_info = {
|
||||
.type = "bq27500",
|
||||
.addr = 0x55,
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "shared-psy-info.h"
|
||||
#include "x86-android-tablets.h"
|
||||
|
||||
/* Acer Iconia One 7 B1-750 has an Android factory img with everything hardcoded */
|
||||
/* Acer Iconia One 7 B1-750 has an Android factory image with everything hardcoded */
|
||||
static const char * const acer_b1_750_mount_matrix[] = {
|
||||
"-1", "0", "0",
|
||||
"0", "1", "0",
|
||||
@ -98,7 +98,7 @@ const struct x86_dev_info acer_b1_750_info __initconst = {
|
||||
* Advantech MICA-071
|
||||
* This is a standard Windows tablet, but it has an extra "quick launch" button
|
||||
* which is not described in the ACPI tables in anyway.
|
||||
* Use the x86-android-tablets infra to create a gpio-button device for this.
|
||||
* Use the x86-android-tablets infra to create a gpio-keys device for this.
|
||||
*/
|
||||
static const struct x86_gpio_button advantech_mica_071_button __initconst = {
|
||||
.button = {
|
||||
@ -209,7 +209,7 @@ const struct x86_dev_info chuwi_hi8_info __initconst = {
|
||||
* This comes in both Windows and Android versions and even on Android
|
||||
* the DSDT is mostly sane. This tablet has 2 extra general purpose buttons
|
||||
* in the button row with the power + volume-buttons labeled P and F.
|
||||
* Use the x86-android-tablets infra to create a gpio-button device for these.
|
||||
* Use the x86-android-tablets infra to create a gpio-keys device for these.
|
||||
*/
|
||||
static const struct x86_gpio_button cyberbook_t116_buttons[] __initconst = {
|
||||
{
|
||||
@ -276,7 +276,7 @@ const struct x86_dev_info czc_p10t __initconst = {
|
||||
.init = czc_p10t_init,
|
||||
};
|
||||
|
||||
/* Medion Lifetab S10346 tablets have an Android factory img with everything hardcoded */
|
||||
/* Medion Lifetab S10346 tablets have an Android factory image with everything hardcoded */
|
||||
static const char * const medion_lifetab_s10346_accel_mount_matrix[] = {
|
||||
"0", "1", "0",
|
||||
"1", "0", "0",
|
||||
@ -305,7 +305,7 @@ static const struct software_node medion_lifetab_s10346_touchscreen_node = {
|
||||
|
||||
static const struct x86_i2c_client_info medion_lifetab_s10346_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* kxtj21009 accel */
|
||||
/* kxtj21009 accelerometer */
|
||||
.board_info = {
|
||||
.type = "kxtj21009",
|
||||
.addr = 0x0f,
|
||||
@ -359,7 +359,7 @@ const struct x86_dev_info medion_lifetab_s10346_info __initconst = {
|
||||
.gpiod_lookup_tables = medion_lifetab_s10346_gpios,
|
||||
};
|
||||
|
||||
/* Nextbook Ares 8 (BYT) tablets have an Android factory img with everything hardcoded */
|
||||
/* Nextbook Ares 8 (BYT) tablets have an Android factory image with everything hardcoded */
|
||||
static const char * const nextbook_ares8_accel_mount_matrix[] = {
|
||||
"0", "-1", "0",
|
||||
"-1", "0", "0",
|
||||
@ -387,7 +387,7 @@ static const struct software_node nextbook_ares8_touchscreen_node = {
|
||||
|
||||
static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* Freescale MMA8653FC accel */
|
||||
/* Freescale MMA8653FC accelerometer */
|
||||
.board_info = {
|
||||
.type = "mma8653",
|
||||
.addr = 0x1d,
|
||||
@ -428,7 +428,7 @@ const struct x86_dev_info nextbook_ares8_info __initconst = {
|
||||
.gpiod_lookup_tables = nextbook_ares8_gpios,
|
||||
};
|
||||
|
||||
/* Nextbook Ares 8A (CHT) tablets have an Android factory img with everything hardcoded */
|
||||
/* Nextbook Ares 8A (CHT) tablets have an Android factory image with everything hardcoded */
|
||||
static const char * const nextbook_ares8a_accel_mount_matrix[] = {
|
||||
"1", "0", "0",
|
||||
"0", "-1", "0",
|
||||
@ -446,7 +446,7 @@ static const struct software_node nextbook_ares8a_accel_node = {
|
||||
|
||||
static const struct x86_i2c_client_info nextbook_ares8a_i2c_clients[] __initconst = {
|
||||
{
|
||||
/* Freescale MMA8653FC accel */
|
||||
/* Freescale MMA8653FC accelerometer */
|
||||
.board_info = {
|
||||
.type = "mma8653",
|
||||
.addr = 0x1d,
|
||||
@ -497,7 +497,7 @@ const struct x86_dev_info nextbook_ares8a_info __initconst = {
|
||||
* Peaq C1010
|
||||
* This is a standard Windows tablet, but it has a special Dolby button.
|
||||
* This button has a WMI interface, but that is broken. Instead of trying to
|
||||
* use the broken WMI interface, instantiate a gpio_keys device for this.
|
||||
* use the broken WMI interface, instantiate a gpio-keys device for this.
|
||||
*/
|
||||
static const struct x86_gpio_button peaq_c1010_button __initconst = {
|
||||
.button = {
|
||||
@ -521,7 +521,7 @@ const struct x86_dev_info peaq_c1010_info __initconst = {
|
||||
* Whitelabel (sold as various brands) TM800A550L tablets.
|
||||
* These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices
|
||||
* (removed through acpi_quirk_skip_i2c_client_enumeration()) and
|
||||
* the touchscreen fwnode has the wrong GPIOs.
|
||||
* the touchscreen firmware node has the wrong GPIOs.
|
||||
*/
|
||||
static const char * const whitelabel_tm800a550l_accel_mount_matrix[] = {
|
||||
"-1", "0", "0",
|
||||
@ -566,7 +566,7 @@ static const struct x86_i2c_client_info whitelabel_tm800a550l_i2c_clients[] __in
|
||||
.polarity = ACPI_ACTIVE_HIGH,
|
||||
},
|
||||
}, {
|
||||
/* kxcj91008 accel */
|
||||
/* kxcj91008 accelerometer */
|
||||
.board_info = {
|
||||
.type = "kxcj91008",
|
||||
.addr = 0x0f,
|
||||
@ -598,12 +598,12 @@ const struct x86_dev_info whitelabel_tm800a550l_info __initconst = {
|
||||
};
|
||||
|
||||
/*
|
||||
* The fwnode for ktd2026 on Xaomi pad2. It composed of a RGB LED node
|
||||
* The firmware node for ktd2026 on Xaomi pad2. It composed of a RGB LED node
|
||||
* with three subnodes for each color (B/G/R). The RGB LED node is named
|
||||
* "multi-led" to align with the name in the device tree.
|
||||
*/
|
||||
|
||||
/* main fwnode for ktd2026 */
|
||||
/* Main firmware node for ktd2026 */
|
||||
static const struct software_node ktd2026_node = {
|
||||
.name = "ktd2026",
|
||||
};
|
||||
@ -665,12 +665,12 @@ static const struct software_node *ktd2026_node_group[] = {
|
||||
};
|
||||
|
||||
/*
|
||||
* For the LEDs which backlight the menu / home / back capacitive buttons on
|
||||
* For the LEDs which backlight the Menu / Home / Back capacitive buttons on
|
||||
* the bottom bezel. These are attached to a TPS61158 LED controller which
|
||||
* is controlled by the "pwm_soc_lpss_2" PWM output.
|
||||
*/
|
||||
#define XIAOMI_MIPAD2_LED_PERIOD_NS 19200
|
||||
#define XIAOMI_MIPAD2_LED_DEFAULT_DUTY 6000 /* From Android kernel */
|
||||
#define XIAOMI_MIPAD2_LED_MAX_DUTY_NS 6000 /* From Android kernel */
|
||||
|
||||
static struct pwm_device *xiaomi_mipad2_led_pwm;
|
||||
|
||||
@ -679,7 +679,7 @@ static int xiaomi_mipad2_brightness_set(struct led_classdev *led_cdev,
|
||||
{
|
||||
struct pwm_state state = {
|
||||
.period = XIAOMI_MIPAD2_LED_PERIOD_NS,
|
||||
.duty_cycle = val,
|
||||
.duty_cycle = XIAOMI_MIPAD2_LED_MAX_DUTY_NS * val / LED_FULL,
|
||||
/* Always set PWM enabled to avoid the pin floating */
|
||||
.enabled = true,
|
||||
};
|
||||
@ -701,11 +701,11 @@ static int __init xiaomi_mipad2_init(struct device *dev)
|
||||
return -ENOMEM;
|
||||
|
||||
led_cdev->name = "mipad2:white:touch-buttons-backlight";
|
||||
led_cdev->max_brightness = XIAOMI_MIPAD2_LED_PERIOD_NS;
|
||||
/* "input-events" trigger uses blink_brightness */
|
||||
led_cdev->blink_brightness = XIAOMI_MIPAD2_LED_DEFAULT_DUTY;
|
||||
led_cdev->max_brightness = LED_FULL;
|
||||
led_cdev->default_trigger = "input-events";
|
||||
led_cdev->brightness_set_blocking = xiaomi_mipad2_brightness_set;
|
||||
/* Turn LED off during suspend */
|
||||
led_cdev->flags = LED_CORE_SUSPENDRESUME;
|
||||
|
||||
ret = devm_led_classdev_register(dev, led_cdev);
|
||||
if (ret)
|
||||
|
@ -39,7 +39,7 @@ const struct software_node fg_bq25890_supply_node = {
|
||||
.properties = fg_bq25890_supply_props,
|
||||
};
|
||||
|
||||
/* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV bat. */
|
||||
/* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV battery */
|
||||
static const struct property_entry generic_lipo_hv_4v35_battery_props[] = {
|
||||
PROPERTY_ENTRY_STRING("compatible", "simple-battery"),
|
||||
PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion"),
|
||||
@ -80,7 +80,7 @@ const char * const bq24190_modules[] __initconst = {
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Generic pdevs array and gpio-lookups for micro USB ID pin handling */
|
||||
/* Generic platform device array and GPIO lookup table for micro USB ID pin handling */
|
||||
const struct platform_device_info int3496_pdevs[] __initconst = {
|
||||
{
|
||||
/* For micro USB ID pin handling */
|
||||
|
@ -61,7 +61,7 @@ struct x86_serdev_info {
|
||||
const char *ctrl_uid;
|
||||
const char *ctrl_devname;
|
||||
/*
|
||||
* ATM the serdev core only supports of or ACPI matching; and sofar all
|
||||
* ATM the serdev core only supports of or ACPI matching; and so far all
|
||||
* Android x86 tablets DSDTs have usable serdev nodes, but sometimes
|
||||
* under the wrong controller. So we just tie the existing serdev ACPI
|
||||
* node to the right controller.
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_data/x86/intel_scu_ipc.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/usb/pd.h>
|
||||
@ -18,8 +19,6 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
#define PMC_USBC_CMD 0xa7
|
||||
|
||||
/* Response status bits */
|
||||
|
@ -20,9 +20,8 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#include <linux/platform_data/intel-mid_wdt.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
#include <linux/platform_data/x86/intel-mid_wdt.h>
|
||||
#include <linux/platform_data/x86/intel_scu_ipc.h>
|
||||
|
||||
#define IPC_WATCHDOG 0xf8
|
||||
|
||||
|
@ -387,7 +387,7 @@ extern bool acpi_is_pnp_device(struct acpi_device *);
|
||||
|
||||
#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
|
||||
|
||||
typedef void (*wmi_notify_handler) (u32 value, void *context);
|
||||
typedef void (*wmi_notify_handler) (union acpi_object *data, void *context);
|
||||
|
||||
int wmi_instance_count(const char *guid);
|
||||
|
||||
@ -402,7 +402,6 @@ extern acpi_status wmi_set_block(const char *guid, u8 instance,
|
||||
extern acpi_status wmi_install_notify_handler(const char *guid,
|
||||
wmi_notify_handler handler, void *data);
|
||||
extern acpi_status wmi_remove_notify_handler(const char *guid);
|
||||
extern acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out);
|
||||
extern bool wmi_has_guid(const char *guid);
|
||||
extern char *wmi_get_acpi_device_uid(const char *guid);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _VSEC_H
|
||||
#define _VSEC_H
|
||||
#ifndef _INTEL_VSEC_H
|
||||
#define _INTEL_VSEC_H
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/bits.h>
|
||||
@ -67,15 +67,49 @@ enum intel_vsec_quirks {
|
||||
VSEC_QUIRK_EARLY_HW = BIT(4),
|
||||
};
|
||||
|
||||
/* Platform specific data */
|
||||
/**
|
||||
* struct pmt_callbacks - Callback infrastructure for PMT devices
|
||||
* ->read_telem() when specified, called by client driver to access PMT data (instead
|
||||
* of direct copy).
|
||||
* @pdev: PCI device reference for the callback's use
|
||||
* @guid: ID of data to acccss
|
||||
* @data: buffer for the data to be copied
|
||||
* @count: size of buffer
|
||||
*/
|
||||
struct pmt_callbacks {
|
||||
int (*read_telem)(struct pci_dev *pdev, u32 guid, u64 *data, u32 count);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct intel_vsec_platform_info - Platform specific data
|
||||
* @parent: parent device in the auxbus chain
|
||||
* @headers: list of headers to define the PMT client devices to create
|
||||
* @priv_data: private data, usable by parent devices, currently a callback
|
||||
* @caps: bitmask of PMT capabilities for the given headers
|
||||
* @quirks: bitmask of VSEC device quirks
|
||||
* @base_addr: allow a base address to be specified (rather than derived)
|
||||
*/
|
||||
struct intel_vsec_platform_info {
|
||||
struct device *parent;
|
||||
struct intel_vsec_header **headers;
|
||||
void *priv_data;
|
||||
unsigned long caps;
|
||||
unsigned long quirks;
|
||||
u64 base_addr;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct intel_sec_device - Auxbus specific device information
|
||||
* @auxdev: auxbus device struct for auxbus access
|
||||
* @pcidev: pci device associated with the device
|
||||
* @resource: any resources shared by the parent
|
||||
* @ida: id reference
|
||||
* @num_resources: number of resources
|
||||
* @id: xarray id
|
||||
* @priv_data: any private data needed
|
||||
* @quirks: specified quirks
|
||||
* @base_addr: base address of entries (if specified)
|
||||
*/
|
||||
struct intel_vsec_device {
|
||||
struct auxiliary_device auxdev;
|
||||
struct pci_dev *pcidev;
|
||||
@ -103,6 +137,13 @@ static inline struct intel_vsec_device *auxdev_to_ivdev(struct auxiliary_device
|
||||
return container_of(auxdev, struct intel_vsec_device, auxdev);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_INTEL_VSEC)
|
||||
void intel_vsec_register(struct pci_dev *pdev,
|
||||
struct intel_vsec_platform_info *info);
|
||||
#else
|
||||
static inline void intel_vsec_register(struct pci_dev *pdev,
|
||||
struct intel_vsec_platform_info *info)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
#endif
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/dmi.h>
|
||||
|
||||
/* WMI Methods */
|
||||
#define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */
|
||||
@ -69,6 +70,7 @@
|
||||
#define ASUS_WMI_DEVID_SCREENPAD_LIGHT 0x00050032
|
||||
#define ASUS_WMI_DEVID_FAN_BOOST_MODE 0x00110018
|
||||
#define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075
|
||||
#define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO 0x00110019
|
||||
|
||||
/* Misc */
|
||||
#define ASUS_WMI_DEVID_PANEL_OD 0x00050019
|
||||
@ -165,4 +167,39 @@ static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
|
||||
}
|
||||
#endif
|
||||
|
||||
/* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */
|
||||
static const struct dmi_system_id asus_use_hid_led_dmi_ids[] = {
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Zephyrus"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Strix"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Flow"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "GA403U"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "GU605M"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "RC71L"),
|
||||
},
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
#endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */
|
||||
|
@ -6,8 +6,8 @@
|
||||
* Contact: David Cohen <david.a.cohen@linux.intel.com>
|
||||
*/
|
||||
|
||||
#ifndef __INTEL_MID_WDT_H__
|
||||
#define __INTEL_MID_WDT_H__
|
||||
#ifndef __PLATFORM_X86_INTEL_MID_WDT_H_
|
||||
#define __PLATFORM_X86_INTEL_MID_WDT_H_
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
@ -16,4 +16,4 @@ struct intel_mid_wdt_pdata {
|
||||
int (*probe)(struct platform_device *pdev);
|
||||
};
|
||||
|
||||
#endif /*__INTEL_MID_WDT_H__*/
|
||||
#endif /* __PLATFORM_X86_INTEL_MID_WDT_H_ */
|
@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _ASM_X86_INTEL_SCU_IPC_H_
|
||||
#define _ASM_X86_INTEL_SCU_IPC_H_
|
||||
#ifndef __PLATFORM_X86_INTEL_SCU_IPC_H_
|
||||
#define __PLATFORM_X86_INTEL_SCU_IPC_H_
|
||||
|
||||
#include <linux/ioport.h>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user