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:
Linus Torvalds 2024-09-19 09:16:04 +02:00
commit 84bbfe6b64
101 changed files with 2723 additions and 1060 deletions

View File

@ -113,3 +113,62 @@ to apply at each uncore* level.
Support for "current_freq_khz" is available only at each fabric cluster Support for "current_freq_khz" is available only at each fabric cluster
level (i.e., in uncore* directory). 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

View File

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

View File

@ -88,7 +88,7 @@ properties:
TX FIFO threshold configuration (in bytes). TX FIFO threshold configuration (in bytes).
patternProperties: patternProperties:
"^(bluetooth|bluetooth-gnss|gnss|gps|mcu|onewire)$": "^(bluetooth|bluetooth-gnss|embedded-controller|gnss|gps|mcu|onewire)$":
if: if:
type: object type: object
then: then:

View File

@ -11552,6 +11552,24 @@ S: Maintained
F: Documentation/ABI/testing/sysfs-driver-intel-m10-bmc-sec-update F: Documentation/ABI/testing/sysfs-driver-intel-m10-bmc-sec-update
F: drivers/fpga/intel-m10-bmc-sec-update.c 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 INTEL P-Unit IPC DRIVER
M: Zha Qipeng <qipeng.zha@intel.com> M: Zha Qipeng <qipeng.zha@intel.com>
L: platform-driver-x86@vger.kernel.org L: platform-driver-x86@vger.kernel.org
@ -11614,8 +11632,8 @@ F: drivers/counter/intel-qep.c
INTEL SCU DRIVERS INTEL SCU DRIVERS
M: Mika Westerberg <mika.westerberg@linux.intel.com> M: Mika Westerberg <mika.westerberg@linux.intel.com>
S: Maintained S: Maintained
F: arch/x86/include/asm/intel_scu_ipc.h
F: drivers/platform/x86/intel_scu_* F: drivers/platform/x86/intel_scu_*
F: include/linux/platform_data/x86/intel_scu_ipc.h
INTEL SDSI DRIVER INTEL SDSI DRIVER
M: David E. Box <david.e.box@linux.intel.com> 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 INTEL VENDOR SPECIFIC EXTENDED CAPABILITIES DRIVER
M: David E. Box <david.e.box@linux.intel.com> M: David E. Box <david.e.box@linux.intel.com>
S: Supported 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 INTEL VIRTUAL BUTTON DRIVER
M: AceLan Kao <acelan.kao@canonical.com> M: AceLan Kao <acelan.kao@canonical.com>

View File

@ -3076,9 +3076,13 @@ config OLPC_XO15_SCI
- AC adapter status updates - AC adapter status updates
- Battery status updates - Battery status updates
config GEODE_COMMON
bool
config ALIX config ALIX
bool "PCEngines ALIX System Support (LED setup)" bool "PCEngines ALIX System Support (LED setup)"
select GPIOLIB select GPIOLIB
select GEODE_COMMON
help help
This option enables system support for the PCEngines ALIX. This option enables system support for the PCEngines ALIX.
At present this just sets up LEDs for GPIO control on At present this just sets up LEDs for GPIO control on
@ -3093,12 +3097,14 @@ config ALIX
config NET5501 config NET5501
bool "Soekris Engineering net5501 System Support (LEDS, GPIO, etc)" bool "Soekris Engineering net5501 System Support (LEDS, GPIO, etc)"
select GPIOLIB select GPIOLIB
select GEODE_COMMON
help help
This option enables system support for the Soekris Engineering net5501. This option enables system support for the Soekris Engineering net5501.
config GEOS config GEOS
bool "Traverse Technologies GEOS System Support (LEDS, GPIO, etc)" bool "Traverse Technologies GEOS System Support (LEDS, GPIO, etc)"
select GPIOLIB select GPIOLIB
select GEODE_COMMON
depends on DMI depends on DMI
help help
This option enables system support for the Traverse Technologies GEOS. This option enables system support for the Traverse Technologies GEOS.

View File

@ -10,7 +10,7 @@
#define TELEM_MAX_EVENTS_SRAM 28 #define TELEM_MAX_EVENTS_SRAM 28
#define TELEM_MAX_OS_ALLOCATED_EVENTS 20 #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 { enum telemetry_unit {
TELEM_PSS = 0, TELEM_PSS = 0,

View File

@ -247,6 +247,8 @@
#define MSR_INTEGRITY_CAPS_ARRAY_BIST BIT(MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT) #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 4
#define MSR_INTEGRITY_CAPS_PERIODIC_BIST BIT(MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT) #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_INTEGRITY_CAPS_SAF_GEN_MASK GENMASK_ULL(10, 9)
#define MSR_LBR_NHM_FROM 0x00000680 #define MSR_LBR_NHM_FROM 0x00000680

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_GEODE_COMMON) += geode-common.o
obj-$(CONFIG_ALIX) += alix.o obj-$(CONFIG_ALIX) += alix.o
obj-$(CONFIG_NET5501) += net5501.o obj-$(CONFIG_NET5501) += net5501.o
obj-$(CONFIG_GEOS) += geos.o obj-$(CONFIG_GEOS) += geos.o

View File

@ -18,15 +18,12 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/moduleparam.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 <linux/dmi.h>
#include <asm/geode.h> #include <asm/geode.h>
#include "geode-common.h"
#define BIOS_SIGNATURE_TINYBIOS 0xf0000 #define BIOS_SIGNATURE_TINYBIOS 0xf0000
#define BIOS_SIGNATURE_COREBOOT 0x500 #define BIOS_SIGNATURE_COREBOOT 0x500
#define BIOS_REGION_SIZE 0x10000 #define BIOS_REGION_SIZE 0x10000
@ -41,79 +38,16 @@ module_param(force, bool, 0444);
/* FIXME: Award bios is not automatically detected as Alix platform */ /* FIXME: Award bios is not automatically detected as Alix platform */
MODULE_PARM_DESC(force, "Force detection as ALIX.2/ALIX.3 platform"); MODULE_PARM_DESC(force, "Force detection as ALIX.2/ALIX.3 platform");
static struct gpio_keys_button alix_gpio_buttons[] = { static const struct geode_led alix_leds[] __initconst = {
{ { 6, true },
.code = KEY_RESTART, { 25, false },
.gpio = 24, { 27, false },
.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 void __init register_alix(void) static void __init register_alix(void)
{ {
/* Setup LED control through leds-gpio driver */ geode_create_restart_key(24);
gpiod_add_lookup_table(&alix_leds_gpio_table); geode_create_leds("alix", alix_leds, ARRAY_SIZE(alix_leds));
platform_add_devices(alix_devs, ARRAY_SIZE(alix_devs));
} }
static bool __init alix_present(unsigned long bios_phys, static bool __init alix_present(unsigned long bios_phys,

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

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

View File

@ -16,88 +16,22 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/string.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 <linux/dmi.h>
#include <asm/geode.h> #include <asm/geode.h>
static struct gpio_keys_button geos_gpio_buttons[] = { #include "geode-common.h"
{
.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,
};
static struct platform_device geos_buttons_dev = { static const struct geode_led geos_leds[] __initconst = {
.name = "gpio-keys-polled", { 6, true },
.id = 1, { 25, false },
.dev = { { 27, false },
.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 void __init register_geos(void) static void __init register_geos(void)
{ {
/* Setup LED control through leds-gpio driver */ geode_create_restart_key(3);
gpiod_add_lookup_table(&geos_leds_gpio_table); geode_create_leds("geos", geos_leds, ARRAY_SIZE(geos_leds));
platform_add_devices(geos_devs, ARRAY_SIZE(geos_devs));
} }
static int __init geos_init(void) static int __init geos_init(void)

View File

@ -16,80 +16,25 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/leds.h>
#include <linux/platform_device.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/gpio/machine.h> #include <linux/gpio/machine.h>
#include <linux/gpio/property.h>
#include <asm/geode.h> #include <asm/geode.h>
#include "geode-common.h"
#define BIOS_REGION_BASE 0xffff0000 #define BIOS_REGION_BASE 0xffff0000
#define BIOS_REGION_SIZE 0x00010000 #define BIOS_REGION_SIZE 0x00010000
static struct gpio_keys_button net5501_gpio_buttons[] = { static const struct geode_led net5501_leds[] __initconst = {
{ { 6, true },
.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 void __init register_net5501(void) static void __init register_net5501(void)
{ {
/* Setup LED control through leds-gpio driver */ geode_create_restart_key(24);
gpiod_add_lookup_table(&net5501_leds_gpio_table); geode_create_leds("net5501", net5501_leds, ARRAY_SIZE(net5501_leds));
platform_add_devices(net5501_devs, ARRAY_SIZE(net5501_devs));
} }
struct net5501_board { struct net5501_board {

View File

@ -27,9 +27,10 @@
#include <asm/intel-mid.h> #include <asm/intel-mid.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/i8259.h> #include <asm/i8259.h>
#include <asm/intel_scu_ipc.h>
#include <asm/reboot.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_OFF 0x80 /* Only for Tangier */
#define IPCMSG_COLD_RESET 0xF1 #define IPCMSG_COLD_RESET 0xF1

View File

@ -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) static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
{ {
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
u32 value; u32 value;
int ret; int ret;
if (!IS_ENABLED(CONFIG_ASUS_WMI)) if (!IS_ENABLED(CONFIG_ASUS_WMI))
return false; 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, ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS,
ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value); ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value);
hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value); hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value);

View File

@ -1597,15 +1597,13 @@ static void hp_wmi_devm_notify_remove(void *ignored)
} }
/* hp_wmi_notify - WMI event notification handler */ /* 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 hp_wmi_info *temp_info[HP_WMI_MAX_INSTANCES] = {};
struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
struct hp_wmi_sensors *state = context; struct hp_wmi_sensors *state = context;
struct device *dev = &state->wdev->dev; struct device *dev = &state->wdev->dev;
struct hp_wmi_event event = {}; struct hp_wmi_event event = {};
struct hp_wmi_info *fan_info; struct hp_wmi_info *fan_info;
union acpi_object *wobj;
acpi_status err; acpi_status err;
int event_type; int event_type;
u8 count; u8 count;
@ -1630,20 +1628,15 @@ static void hp_wmi_notify(u32 value, void *context)
* HPBIOS_BIOSEvent instance. * 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) if (!wobj)
goto out_unlock; return;
mutex_lock(&state->lock);
err = populate_event_from_wobj(dev, &event, wobj); err = populate_event_from_wobj(dev, &event, wobj);
if (err) { if (err) {
dev_warn(dev, "Bad event data (ACPI type %d)\n", wobj->type); 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); event_type = classify_event(event.name, event.category);
@ -1668,13 +1661,10 @@ static void hp_wmi_notify(u32 value, void *context)
break; break;
} }
out_free_wobj: out_free:
kfree(wobj);
devm_kfree(dev, event.name); devm_kfree(dev, event.name);
devm_kfree(dev, event.description); devm_kfree(dev, event.description);
out_unlock:
mutex_unlock(&state->lock); mutex_unlock(&state->lock);
} }

View File

@ -23,8 +23,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/platform_data/itco_wdt.h> #include <linux/platform_data/itco_wdt.h>
#include <linux/platform_data/x86/intel_scu_ipc.h>
#include <asm/intel_scu_ipc.h>
/* Residency with clock rate at 19.2MHz to usecs */ /* Residency with clock rate at 19.2MHz to usecs */
#define S0IX_RESIDENCY_IN_USECS(d, s) \ #define S0IX_RESIDENCY_IN_USECS(d, s) \

View File

@ -15,8 +15,7 @@
#include <linux/mfd/intel_soc_pmic.h> #include <linux/mfd/intel_soc_pmic.h>
#include <linux/mfd/intel_soc_pmic_bxtwc.h> #include <linux/mfd/intel_soc_pmic_bxtwc.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_data/x86/intel_scu_ipc.h>
#include <asm/intel_scu_ipc.h>
/* PMIC device registers */ /* PMIC device registers */
#define REG_ADDR_MASK GENMASK(15, 8) #define REG_ADDR_MASK GENMASK(15, 8)

View File

@ -12,11 +12,10 @@
#include <linux/mfd/intel_soc_pmic.h> #include <linux/mfd/intel_soc_pmic.h>
#include <linux/mfd/intel_soc_pmic_mrfld.h> #include <linux/mfd/intel_soc_pmic_mrfld.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_data/x86/intel_scu_ipc.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <asm/intel_scu_ipc.h>
/* /*
* Level 2 IRQs * Level 2 IRQs
* *

View File

@ -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 */ /* "event_list" sysfs to list events supported by the block */
attr = &pmc->block[blk_num].attr_event_list; attr = &pmc->block[blk_num].attr_event_list;
sysfs_attr_init(&attr->dev_attr.attr);
attr->dev_attr.attr.mode = 0444; attr->dev_attr.attr.mode = 0444;
attr->dev_attr.show = mlxbf_pmc_event_list_show; attr->dev_attr.show = mlxbf_pmc_event_list_show;
attr->nr = blk_num; 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") || if (strstr(pmc->block_name[blk_num], "l3cache") ||
((pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE))) { ((pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE))) {
attr = &pmc->block[blk_num].attr_enable; attr = &pmc->block[blk_num].attr_enable;
sysfs_attr_init(&attr->dev_attr.attr);
attr->dev_attr.attr.mode = 0644; attr->dev_attr.attr.mode = 0644;
attr->dev_attr.show = mlxbf_pmc_enable_show; attr->dev_attr.show = mlxbf_pmc_enable_show;
attr->dev_attr.store = mlxbf_pmc_enable_store; 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 */ /* "eventX" and "counterX" sysfs to program and read counter values */
for (j = 0; j < pmc->block[blk_num].counters; ++j) { for (j = 0; j < pmc->block[blk_num].counters; ++j) {
attr = &pmc->block[blk_num].attr_counter[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.attr.mode = 0644;
attr->dev_attr.show = mlxbf_pmc_counter_show; attr->dev_attr.show = mlxbf_pmc_counter_show;
attr->dev_attr.store = mlxbf_pmc_counter_store; 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 = NULL;
attr = &pmc->block[blk_num].attr_event[j]; attr = &pmc->block[blk_num].attr_event[j];
sysfs_attr_init(&attr->dev_attr.attr);
attr->dev_attr.attr.mode = 0644; attr->dev_attr.attr.mode = 0644;
attr->dev_attr.show = mlxbf_pmc_event_show; attr->dev_attr.show = mlxbf_pmc_event_show;
attr->dev_attr.store = mlxbf_pmc_event_store; 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) { while (count > 0) {
--count; --count;
attr = &pmc->block[blk_num].attr_event[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.attr.mode = 0644;
attr->dev_attr.show = mlxbf_pmc_counter_show; attr->dev_attr.show = mlxbf_pmc_counter_show;
attr->dev_attr.store = mlxbf_pmc_counter_store; attr->dev_attr.store = mlxbf_pmc_counter_store;

View File

@ -332,9 +332,6 @@ static struct dentry *olpc_ec_setup_debugfs(void)
struct dentry *dbgfs_dir; struct dentry *dbgfs_dir;
dbgfs_dir = debugfs_create_dir("olpc-ec", NULL); 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); debugfs_create_file("cmd", 0600, dbgfs_dir, NULL, &ec_dbgfs_ops);
return dbgfs_dir; return dbgfs_dir;

View File

@ -6,6 +6,7 @@
*/ */
#include <linux/device.h> #include <linux/device.h>
#include <linux/of.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/slab.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.parent = parent;
sdev->dev.fwnode = fwnode_handle_get(node); sdev->dev.fwnode = fwnode_handle_get(node);
sdev->dev.of_node = to_of_node(node);
status = ssam_device_add(sdev); status = ssam_device_add(sdev);
if (status) if (status)

View File

@ -1104,13 +1104,6 @@ int ssam_controller_caps_load_from_acpi(acpi_handle handle,
u64 funcs; u64 funcs;
int status; 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. */ /* Pre-load supported DSM functions. */
status = ssam_dsm_get_functions(handle, &funcs); status = ssam_dsm_get_functions(handle, &funcs);
if (status) if (status)
@ -1149,6 +1142,52 @@ int ssam_controller_caps_load_from_acpi(acpi_handle handle,
return 0; 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. * ssam_controller_init() - Initialize SSAM controller.
* @ctrl: The controller to initialize. * @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, int ssam_controller_init(struct ssam_controller *ctrl,
struct serdev_device *serdev) struct serdev_device *serdev)
{ {
acpi_handle handle = ACPI_HANDLE(&serdev->dev);
int status; int status;
init_rwsem(&ctrl->lock); init_rwsem(&ctrl->lock);
kref_init(&ctrl->kref); 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) if (status)
return 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; const int irqf = IRQF_ONESHOT | IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN;
gpiod = gpiod_get(dev, "ssam_wakeup-int", GPIOD_ASIS); gpiod = gpiod_get(dev, "ssam_wakeup-int", GPIOD_ASIS);
if (IS_ERR(gpiod)) if (IS_ERR(gpiod)) {
return PTR_ERR(gpiod); irq = fwnode_irq_get(dev_fwnode(dev), 0);
} else {
irq = gpiod_to_irq(gpiod); irq = gpiod_to_irq(gpiod);
gpiod_put(gpiod); gpiod_put(gpiod);
}
if (irq < 0) if (irq < 0)
return irq; return irq;

View File

@ -17,9 +17,12 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/serdev.h> #include <linux/serdev.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/units.h>
#include <linux/surface_aggregator/controller.h> #include <linux/surface_aggregator/controller.h>
#include <linux/surface_aggregator/device.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, static acpi_status ssam_serdev_setup_via_acpi_crs(struct acpi_resource *rsc,
void *ctx) void *ctx)
@ -352,13 +355,28 @@ static acpi_status ssam_serdev_setup_via_acpi_crs(struct acpi_resource *rsc,
return AE_CTRL_TERMINATE; return AE_CTRL_TERMINATE;
} }
static acpi_status ssam_serdev_setup_via_acpi(acpi_handle handle, static int ssam_serdev_setup_via_acpi(struct serdev_device *serdev, acpi_handle handle)
struct serdev_device *serdev)
{ {
return acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_status status;
ssam_serdev_setup_via_acpi_crs, serdev);
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. ----------------------------------------------------- */ /* -- Power management. ----------------------------------------------------- */
@ -621,16 +639,17 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
struct device *dev = &serdev->dev; struct device *dev = &serdev->dev;
struct acpi_device *ssh = ACPI_COMPANION(dev); struct acpi_device *ssh = ACPI_COMPANION(dev);
struct ssam_controller *ctrl; struct ssam_controller *ctrl;
acpi_status astatus;
int status; int status;
status = gpiod_count(dev, NULL); if (ssh) {
if (status < 0) status = gpiod_count(dev, NULL);
return dev_err_probe(dev, status, "no GPIO found\n"); if (status < 0)
return dev_err_probe(dev, status, "no GPIO found\n");
status = devm_acpi_dev_add_driver_gpios(dev, ssam_acpi_gpios); status = devm_acpi_dev_add_driver_gpios(dev, ssam_acpi_gpios);
if (status) if (status)
return status; return status;
}
/* Allocate controller. */ /* Allocate controller. */
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
@ -655,9 +674,9 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
goto err_devopen; goto err_devopen;
} }
astatus = ssam_serdev_setup_via_acpi(ssh->handle, serdev); status = ssam_serdev_setup(ssh, serdev);
if (ACPI_FAILURE(astatus)) { if (status) {
status = dev_err_probe(dev, -ENXIO, "failed to setup serdev\n"); status = dev_err_probe(dev, status, "failed to setup serdev\n");
goto err_devinit; 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. * For now let's thus default power/wakeup to false.
*/ */
device_set_wakeup_capable(dev, true); 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; return 0;
@ -782,18 +817,27 @@ static void ssam_serial_hub_remove(struct serdev_device *serdev)
device_set_wakeup_capable(&serdev->dev, false); 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 }, { "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 = { static struct serdev_device_driver ssam_serial_hub = {
.probe = ssam_serial_hub_probe, .probe = ssam_serial_hub_probe,
.remove = ssam_serial_hub_remove, .remove = ssam_serial_hub_remove,
.driver = { .driver = {
.name = "surface_serial_hub", .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, .pm = &ssam_serial_hub_pm_ops,
.shutdown = ssam_serial_hub_shutdown, .shutdown = ssam_serial_hub_shutdown,
.probe_type = PROBE_PREFER_ASYNCHRONOUS, .probe_type = PROBE_PREFER_ASYNCHRONOUS,

View File

@ -12,6 +12,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/types.h> #include <linux/types.h>
@ -291,6 +292,18 @@ static const struct software_node *ssam_node_group_sl6[] = {
NULL, 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. */ /* Devices for Surface Laptop Studio 1. */
static const struct software_node *ssam_node_group_sls1[] = { static const struct software_node *ssam_node_group_sls1[] = {
&ssam_node_root, &ssam_node_root,
@ -380,7 +393,7 @@ static const struct software_node *ssam_node_group_sp9[] = {
/* -- SSAM platform/meta-hub driver. ---------------------------------------- */ /* -- 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) */ /* Surface Pro 4, 5, and 6 (OMBR < 0x10) */
{ "MSHW0081", (unsigned long)ssam_node_group_gen5 }, { "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) static int ssam_platform_hub_probe(struct platform_device *pdev)
{ {
const struct software_node **nodes; const struct software_node **nodes;
const struct of_device_id *match;
struct device_node *fdt_root;
struct ssam_controller *ctrl; struct ssam_controller *ctrl;
struct fwnode_handle *root; struct fwnode_handle *root;
int status; int status;
nodes = (const struct software_node **)acpi_device_get_match_data(&pdev->dev); nodes = (const struct software_node **)acpi_device_get_match_data(&pdev->dev);
if (!nodes) if (!nodes) {
return -ENODEV; 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 * 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, .remove_new = ssam_platform_hub_remove,
.driver = { .driver = {
.name = "surface_aggregator_platform_hub", .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, .probe_type = PROBE_PREFER_ASYNCHRONOUS,
}, },
}; };
module_platform_driver(ssam_platform_hub_driver); module_platform_driver(ssam_platform_hub_driver);
MODULE_ALIAS("platform:surface_aggregator_platform_hub");
MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>"); MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
MODULE_DESCRIPTION("Device-registry for Surface System Aggregator Module"); MODULE_DESCRIPTION("Device-registry for Surface System Aggregator Module");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -1000,7 +1000,8 @@ config TOPSTAR_LAPTOP
config SERIAL_MULTI_INSTANTIATE config SERIAL_MULTI_INSTANTIATE
tristate "Serial bus multi instantiate pseudo device driver" 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 help
Some ACPI-based systems list multiple devices in a single ACPI Some ACPI-based systems list multiple devices in a single ACPI
firmware-node. This driver will instantiate separate clients firmware-node. This driver will instantiate separate clients

View File

@ -16,7 +16,6 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/fb.h>
#include <linux/backlight.h> #include <linux/backlight.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
@ -1685,7 +1684,7 @@ static int acer_backlight_init(struct device *dev)
acer_backlight_device = bd; acer_backlight_device = bd;
bd->props.power = FB_BLANK_UNBLANK; bd->props.power = BACKLIGHT_POWER_ON;
bd->props.brightness = read_brightness(bd); bd->props.brightness = read_brightness(bd);
backlight_update_status(bd); backlight_update_status(bd);
return 0; 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; struct event_return_value return_value;
acpi_status status;
u16 device_state; u16 device_state;
const struct key_entry *key; const struct key_entry *key;
u32 scancode; 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) if (!obj)
return; return;
if (obj->type != ACPI_TYPE_BUFFER) { if (obj->type != ACPI_TYPE_BUFFER) {
pr_warn("Unknown response received %d\n", obj->type); pr_warn("Unknown response received %d\n", obj->type);
kfree(obj);
return; return;
} }
if (obj->buffer.length != 8) { if (obj->buffer.length != 8) {
pr_warn("Unknown buffer length %d\n", obj->buffer.length); pr_warn("Unknown buffer length %d\n", obj->buffer.length);
kfree(obj);
return; return;
} }
return_value = *((struct event_return_value *)obj->buffer.pointer); return_value = *((struct event_return_value *)obj->buffer.pointer);
kfree(obj);
switch (return_value.function) { switch (return_value.function) {
case WMID_HOTKEY_EVENT: case WMID_HOTKEY_EVENT:

View File

@ -282,6 +282,29 @@ int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx)
return 0; 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, &params);
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) 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)); 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; 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) void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
{ {
acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev); acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);

View File

@ -37,12 +37,6 @@
#define AMD_PMF_RESULT_CMD_UNKNOWN 0xFE #define AMD_PMF_RESULT_CMD_UNKNOWN 0xFE
#define AMD_PMF_RESULT_FAILED 0xFF #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 PMF_MSG_DELAY_MIN_US 50
#define RESPONSE_REGISTER_LOOP_MAX 20000 #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 */ /* Get Metrics Table Address */
if (alloc_buffer) { 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) if (!dev->buf)
return -ENOMEM; return -ENOMEM;
} }

View File

@ -37,6 +37,14 @@ static const struct dmi_system_id fwbug_list[] = {
}, },
.driver_data = &quirk_no_sps_bug, .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,
},
{} {}
}; };

View File

@ -19,6 +19,12 @@
#define POLICY_SIGN_COOKIE 0x31535024 #define POLICY_SIGN_COOKIE 0x31535024
#define POLICY_COOKIE_OFFSET 0x10 #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 { struct cookie_header {
u32 sign; u32 sign;
u32 length; u32 length;
@ -35,6 +41,7 @@ struct cookie_header {
#define APMF_FUNC_STATIC_SLIDER_GRANULAR 9 #define APMF_FUNC_STATIC_SLIDER_GRANULAR 9
#define APMF_FUNC_DYN_SLIDER_AC 11 #define APMF_FUNC_DYN_SLIDER_AC 11
#define APMF_FUNC_DYN_SLIDER_DC 12 #define APMF_FUNC_DYN_SLIDER_DC 12
#define APMF_FUNC_NOTIFY_SMART_PC_UPDATES 14
#define APMF_FUNC_SBIOS_HEARTBEAT_V2 16 #define APMF_FUNC_SBIOS_HEARTBEAT_V2 16
/* Message Definitions */ /* Message Definitions */
@ -82,7 +89,17 @@ struct cookie_header {
#define PMF_POLICY_STT_SKINTEMP_APU 7 #define PMF_POLICY_STT_SKINTEMP_APU 7
#define PMF_POLICY_STT_SKINTEMP_HS2 8 #define PMF_POLICY_STT_SKINTEMP_HS2 8
#define PMF_POLICY_SYSTEM_STATE 9 #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_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 */ /* TA macros */
#define PMF_TA_IF_VERSION_MAJOR 1 #define PMF_TA_IF_VERSION_MAJOR 1
@ -181,6 +198,53 @@ struct apmf_fan_idx {
u32 fan_ctl_idx; u32 fan_ctl_idx;
} __packed; } __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 { struct smu_pmf_metrics {
u16 gfxclk_freq; /* in MHz */ u16 gfxclk_freq; /* in MHz */
u16 socclk_freq; /* in MHz */ u16 socclk_freq; /* in MHz */
@ -278,6 +342,7 @@ struct amd_pmf_dev {
int hb_interval; /* SBIOS heartbeat interval */ int hb_interval; /* SBIOS heartbeat interval */
struct delayed_work heart_beat; struct delayed_work heart_beat;
struct smu_pmf_metrics m_table; struct smu_pmf_metrics m_table;
struct smu_pmf_metrics_v2 m_table_v2;
struct delayed_work work_buffer; struct delayed_work work_buffer;
ktime_t start_time; ktime_t start_time;
int socket_power_history[AVG_SAMPLE_SIZE]; int socket_power_history[AVG_SAMPLE_SIZE];
@ -302,6 +367,7 @@ struct amd_pmf_dev {
bool smart_pc_enabled; bool smart_pc_enabled;
u16 pmf_if_version; u16 pmf_if_version;
struct input_dev *pmf_idev; struct input_dev *pmf_idev;
size_t mtable_size;
}; };
struct apmf_sps_prop_granular_v2 { struct apmf_sps_prop_granular_v2 {
@ -344,6 +410,12 @@ struct os_power_slider {
u8 slider_event; u8 slider_event;
} __packed; } __packed;
struct amd_pmf_notify_smart_pc_update {
u16 size;
u32 pending_req;
u32 custom_bios[10];
} __packed;
struct fan_table_control { struct fan_table_control {
bool manual; bool manual;
unsigned long fan_id; 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); int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev);
void amd_pmf_deinit_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 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 */ /* Smart PC - TA interfaces */
void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in); void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in);

View File

@ -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) {} void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {}
#endif #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; u16 max, avg = 0;
int i; 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 */ /* Get the avg and max C0 residency of all the cores */
max = dev->m_table.avg_core_c0residency[0]; max = *core_res;
for (i = 0; i < ARRAY_SIZE(dev->m_table.avg_core_c0residency); i++) { for (i = 0; i < size; i++) {
avg += dev->m_table.avg_core_c0residency[i]; avg += core_res[i];
if (dev->m_table.avg_core_c0residency[i] > max) if (core_res[i] > max)
max = dev->m_table.avg_core_c0residency[i]; max = core_res[i];
} }
avg = DIV_ROUND_CLOSEST(avg, size);
avg = DIV_ROUND_CLOSEST(avg, ARRAY_SIZE(dev->m_table.avg_core_c0residency));
in->ev_info.avg_c0residency = avg; in->ev_info.avg_c0residency = avg;
in->ev_info.max_c0residency = max; 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[] = { static const char * const pmf_battery_supply_name[] = {

View File

@ -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", dev_dbg(dev->dev, "update SYSTEM_STATE: %s\n",
amd_pmf_uevent_as_str(val)); amd_pmf_uevent_as_str(val));
break; 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;
} }
} }
} }

View File

@ -28,7 +28,6 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/backlight.h> #include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
@ -818,7 +817,7 @@ static int asus_backlight_init(struct asus_laptop *asus)
asus->backlight_device = bd; asus->backlight_device = bd;
bd->props.brightness = asus_read_brightness(bd); bd->props.brightness = asus_read_brightness(bd);
bd->props.power = FB_BLANK_UNBLANK; bd->props.power = BACKLIGHT_POWER_ON;
backlight_update_status(bd); backlight_update_status(bd);
return 0; return 0;
} }

View File

@ -7,12 +7,12 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/backlight.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/sparse-keymap.h> #include <linux/input/sparse-keymap.h>
#include <linux/fb.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/i8042.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); dmi_check_system(asus_quirks);
driver->quirks = 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 */ /* overwrite the wapf setting if the wapf paramater is specified */
if (wapf != -1) if (wapf != -1)

View File

@ -18,7 +18,6 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/fb.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h> #include <linux/hwmon-sysfs.h>
#include <linux/init.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_OVERBOOST 1
#define ASUS_THROTTLE_THERMAL_POLICY_SILENT 2 #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 USB_INTEL_XUSB2PR 0xD0
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
@ -300,8 +305,8 @@ struct asus_wmi {
u32 kbd_rgb_dev; u32 kbd_rgb_dev;
bool kbd_rgb_state_available; bool kbd_rgb_state_available;
bool throttle_thermal_policy_available;
u8 throttle_thermal_policy_mode; u8 throttle_thermal_policy_mode;
u32 throttle_thermal_policy_dev;
bool cpu_fan_curve_available; bool cpu_fan_curve_available;
bool gpu_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, status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
&input, &output); &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; return -EIO;
}
obj = (union acpi_object *)output.pointer; obj = (union acpi_object *)output.pointer;
if (obj && obj->type == ACPI_TYPE_INTEGER) if (obj && obj->type == ACPI_TYPE_INTEGER)
tmp = (u32) obj->integer.value; tmp = (u32) obj->integer.value;
pr_debug("Result: 0x%08x\n", tmp);
if (retval) if (retval)
*retval = tmp; *retval = tmp;
kfree(obj); 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 -ENODEV;
}
return 0; 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, status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
&input, &output); &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; return -EIO;
}
obj = (union acpi_object *)output.pointer; obj = (union acpi_object *)output.pointer;
if (obj && obj->type == ACPI_TYPE_INTEGER) if (obj && obj->type == ACPI_TYPE_INTEGER)
tmp = (u32) obj->integer.value; tmp = (u32) obj->integer.value;
pr_debug("Result: %x\n", tmp);
if (retval) if (retval)
*retval = tmp; *retval = tmp;
kfree(obj); 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 -ENODEV;
}
return 0; 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, status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
&input, &output); &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; return -EIO;
}
obj = (union acpi_object *)output.pointer; obj = (union acpi_object *)output.pointer;
@ -468,8 +496,11 @@ static int asus_wmi_evaluate_method_buf(u32 method_id,
kfree(obj); 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 err;
}
return 0; return 0;
} }
@ -557,6 +588,7 @@ static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id)
{ {
u32 retval; u32 retval;
int status = asus_wmi_get_devstate(asus, dev_id, &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); 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; 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_wk = led_val;
asus->kbd_led.name = "asus::kbd_backlight"; asus->kbd_led.name = "asus::kbd_backlight";
asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; 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; int err, fan_idx;
u8 mode = 0; u8 mode = 0;
if (asus->throttle_thermal_policy_available) if (asus->throttle_thermal_policy_dev)
mode = asus->throttle_thermal_policy_mode; mode = asus->throttle_thermal_policy_mode;
/* DEVID_<C/G>PU_FAN_CURVE is switched for OVERBOOST vs SILENT */ /* DEVID_<C/G>PU_FAN_CURVE is switched for OVERBOOST vs SILENT */
if (mode == 2) 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 * For machines with throttle this is the only way to reset fans
* to default mode of operation (does not erase curve data). * 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); err = throttle_thermal_policy_write(asus);
if (err) if (err)
return err; return err;
@ -3610,8 +3643,8 @@ static const struct attribute_group asus_fan_curve_attr_group = {
__ATTRIBUTE_GROUPS(asus_fan_curve_attr); __ATTRIBUTE_GROUPS(asus_fan_curve_attr);
/* /*
* Must be initialised after throttle_thermal_policy_check_present() as * Must be initialised after throttle_thermal_policy_dev is set as
* we check the status of throttle_thermal_policy_available during init. * we check the status of throttle_thermal_policy_dev during init.
*/ */
static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus) 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, err = fan_curve_check_present(asus, &asus->cpu_fan_curve_available,
ASUS_WMI_DEVID_CPU_FAN_CURVE); 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; return err;
}
err = fan_curve_check_present(asus, &asus->gpu_fan_curve_available, err = fan_curve_check_present(asus, &asus->gpu_fan_curve_available,
ASUS_WMI_DEVID_GPU_FAN_CURVE); 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; return err;
}
err = fan_curve_check_present(asus, &asus->mid_fan_curve_available, err = fan_curve_check_present(asus, &asus->mid_fan_curve_available,
ASUS_WMI_DEVID_MID_FAN_CURVE); 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; return err;
}
if (!asus->cpu_fan_curve_available if (!asus->cpu_fan_curve_available
&& !asus->gpu_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 ****************************************************/ /* 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) static int throttle_thermal_policy_write(struct asus_wmi *asus)
{ {
int err; u8 value = asus->throttle_thermal_policy_mode;
u8 value;
u32 retval; u32 retval;
int err;
value = asus->throttle_thermal_policy_mode; err = asus_wmi_set_devstate(asus->throttle_thermal_policy_dev,
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
value, &retval); value, &retval);
sysfs_notify(&asus->platform_device->dev.kobj, NULL, 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) 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; return 0;
asus->throttle_thermal_policy_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT; 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; u8 new_mode = asus->throttle_thermal_policy_mode + 1;
int err; int err;
if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT) if (new_mode > PLATFORM_PROFILE_MAX)
new_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT; new_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
asus->throttle_thermal_policy_mode = new_mode; asus->throttle_thermal_policy_mode = new_mode;
@ -3764,7 +3781,7 @@ static ssize_t throttle_thermal_policy_store(struct device *dev,
if (result < 0) if (result < 0)
return result; return result;
if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT) if (new_mode > PLATFORM_PROFILE_MAX)
return -EINVAL; return -EINVAL;
asus->throttle_thermal_policy_mode = new_mode; asus->throttle_thermal_policy_mode = new_mode;
@ -3781,10 +3798,52 @@ static ssize_t throttle_thermal_policy_store(struct device *dev,
return count; 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); static DEVICE_ATTR_RW(throttle_thermal_policy);
/* Platform profile ***********************************************************/ /* 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, static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof,
enum platform_profile_option *profile) enum platform_profile_option *profile)
{ {
@ -3792,10 +3851,9 @@ static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof,
int tp; int tp;
asus = container_of(pprof, struct asus_wmi, platform_profile_handler); asus = container_of(pprof, struct asus_wmi, platform_profile_handler);
tp = asus->throttle_thermal_policy_mode; tp = asus->throttle_thermal_policy_mode;
switch (tp) { switch (asus_wmi_platform_profile_mode_from_vivo(asus, tp)) {
case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT: case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT:
*profile = PLATFORM_PROFILE_BALANCED; *profile = PLATFORM_PROFILE_BALANCED;
break; break;
@ -3834,7 +3892,7 @@ static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof,
return -EOPNOTSUPP; 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); 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 * Not an error if a component platform_profile relies on is unavailable
* so early return, skipping the setup of platform_profile. * so early return, skipping the setup of platform_profile.
*/ */
if (!asus->throttle_thermal_policy_available) if (!asus->throttle_thermal_policy_dev)
return 0; return 0;
dev_info(dev, "Using throttle_thermal_policy for platform_profile support\n"); 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); asus->platform_profile_handler.choices);
err = platform_profile_register(&asus->platform_profile_handler); 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; return err;
}
asus->platform_profile_support = true; asus->platform_profile_support = true;
return 0; return 0;
@ -3884,7 +3947,7 @@ static int read_backlight_power(struct asus_wmi *asus)
if (ret < 0) if (ret < 0)
return ret; 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) 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); power = read_backlight_power(asus);
if (power != -ENODEV && bd->props.power != power) { 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, err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT,
ctrl_param, NULL); ctrl_param, NULL);
if (asus->driver->quirks->store_backlight_power) 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); power = read_backlight_power(asus);
if (power == -ENODEV) if (power == -ENODEV)
power = FB_BLANK_UNBLANK; power = BACKLIGHT_POWER_ON;
else if (power < 0) else if (power < 0)
return power; return power;
@ -4060,7 +4123,7 @@ static int read_screenpad_backlight_power(struct asus_wmi *asus)
if (ret < 0) if (ret < 0)
return ret; return ret;
/* 1 == powered */ /* 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) 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) if (err < 0)
return err; return err;
/* The device brightness can only be read if powered, so return stored */ /* 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; return asus->driver->screenpad_brightness - ASUS_SCREENPAD_BRIGHT_MIN;
err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &retval); 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; return power;
if (bd->props.power != power) { if (bd->props.power != power) {
if (power != FB_BLANK_UNBLANK) { if (power != BACKLIGHT_POWER_ON) {
/* Only brightness > 0 can power it back on */ /* Only brightness > 0 can power it back on */
ctrl_param = asus->driver->screenpad_brightness - ASUS_SCREENPAD_BRIGHT_MIN; ctrl_param = asus->driver->screenpad_brightness - ASUS_SCREENPAD_BRIGHT_MIN;
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT, 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 { } else {
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_POWER, 0, NULL); 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 */ /* Only set brightness if powered on or we get invalid/unsync state */
ctrl_param = bd->props.brightness + ASUS_SCREENPAD_BRIGHT_MIN; ctrl_param = bd->props.brightness + ASUS_SCREENPAD_BRIGHT_MIN;
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT, ctrl_param, NULL); 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) if (power < 0)
return power; return power;
if (power != FB_BLANK_POWERDOWN) { if (power != BACKLIGHT_POWER_OFF) {
err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &brightness); err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &brightness);
if (err < 0) if (err < 0)
return err; return err;
@ -4189,28 +4252,15 @@ static void asus_wmi_fnlock_update(struct asus_wmi *asus)
/* WMI events *****************************************************************/ /* 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; 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) if (obj && obj->type == ACPI_TYPE_INTEGER)
code = (int)(obj->integer.value & WMI_EVENT_MASK); code = (int)(obj->integer.value & WMI_EVENT_MASK);
else else
code = -EIO; code = -EIO;
kfree(obj);
return code; 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 (code == NOTIFY_KBD_FBM || code == NOTIFY_KBD_TTP) {
if (asus->fan_boost_mode_available) if (asus->fan_boost_mode_available)
fan_boost_mode_switch_next(asus); fan_boost_mode_switch_next(asus);
if (asus->throttle_thermal_policy_available) if (asus->throttle_thermal_policy_dev)
throttle_thermal_policy_switch_next(asus); throttle_thermal_policy_switch_next(asus);
return; 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); 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; struct asus_wmi *asus = context;
int code = asus_wmi_get_event_code(value); int code = asus_wmi_get_event_code(obj);
if (code < 0) { if (code < 0) {
pr_warn("Failed to get notify code: %d\n", code); 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) else if (attr == &dev_attr_fan_boost_mode.attr)
ok = asus->fan_boost_mode_available; ok = asus->fan_boost_mode_available;
else if (attr == &dev_attr_throttle_thermal_policy.attr) 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) else if (attr == &dev_attr_ppt_pl2_sppt.attr)
devid = ASUS_WMI_DEVID_PPT_PL2_SPPT; devid = ASUS_WMI_DEVID_PPT_PL2_SPPT;
else if (attr == &dev_attr_ppt_pl1_spl.attr) 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) else if (attr == &dev_attr_available_mini_led_mode.attr)
ok = asus->mini_led_dev_id != 0; ok = asus->mini_led_dev_id != 0;
if (devid != -1) if (devid != -1) {
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); 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; 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)) else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE2))
asus->kbd_rgb_dev = 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); err = fan_boost_mode_check_present(asus);
if (err) if (err)
goto fail_fan_boost_mode; 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); err = platform_profile_setup(asus);
if (err) if (err)
goto fail_platform_profile_setup; goto fail_platform_profile_setup;
@ -4830,7 +4881,6 @@ fail_hwmon:
fail_input: fail_input:
asus_wmi_sysfs_exit(asus->platform_device); asus_wmi_sysfs_exit(asus->platform_device);
fail_sysfs: fail_sysfs:
fail_throttle_thermal_policy:
fail_custom_fan_curve: fail_custom_fan_curve:
fail_platform_profile_setup: fail_platform_profile_setup:
if (asus->platform_profile_support) if (asus->platform_profile_support)

View File

@ -49,6 +49,7 @@ config DELL_LAPTOP
default m default m
depends on DMI depends on DMI
depends on BACKLIGHT_CLASS_DEVICE depends on BACKLIGHT_CLASS_DEVICE
depends on ACPI_BATTERY
depends on ACPI_VIDEO || ACPI_VIDEO = n depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on RFKILL || RFKILL = n depends on RFKILL || RFKILL = n
depends on DELL_WMI || DELL_WMI = n depends on DELL_WMI || DELL_WMI = n

View File

@ -22,11 +22,13 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/rfkill.h> #include <linux/rfkill.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/sysfs.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/i8042.h> #include <linux/i8042.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <acpi/battery.h>
#include <acpi/video.h> #include <acpi/video.h>
#include "dell-rbtn.h" #include "dell-rbtn.h"
#include "dell-smbios.h" #include "dell-smbios.h"
@ -99,6 +101,20 @@ static bool force_rfkill;
static bool micmute_led_registered; static bool micmute_led_registered;
static bool mute_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_param(force_rfkill, bool, 0444);
MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models"); 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: * 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) static int dell_send_intensity(struct backlight_device *bd)
{ {
struct calling_interface_buffer buffer; struct calling_interface_buffer buffer;
struct calling_interface_token *token; u16 select;
int ret;
token = dell_smbios_find_token(BRIGHTNESS_TOKEN); select = power_supply_is_system_supplied() > 0 ?
if (!token) SELECT_TOKEN_AC : SELECT_TOKEN_BAT;
return -ENODEV; return dell_send_request_for_tokenid(&buffer, CLASS_TOKEN_WRITE,
select, BRIGHTNESS_TOKEN, bd->props.brightness);
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;
} }
static int dell_get_intensity(struct backlight_device *bd) static int dell_get_intensity(struct backlight_device *bd)
{ {
struct calling_interface_buffer buffer; struct calling_interface_buffer buffer;
struct calling_interface_token *token;
int ret; int ret;
u16 select;
token = dell_smbios_find_token(BRIGHTNESS_TOKEN); select = power_supply_is_system_supplied() > 0 ?
if (!token) SELECT_TOKEN_AC : SELECT_TOKEN_BAT;
return -ENODEV; ret = dell_send_request_for_tokenid(&buffer, CLASS_TOKEN_READ,
select, BRIGHTNESS_TOKEN, 0);
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);
if (ret == 0) if (ret == 0)
ret = buffer.output[1]; 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) static int kbd_set_token_bit(u8 bit)
{ {
struct calling_interface_buffer buffer; struct calling_interface_buffer buffer;
struct calling_interface_token *token;
int ret;
if (bit >= ARRAY_SIZE(kbd_tokens)) if (bit >= ARRAY_SIZE(kbd_tokens))
return -EINVAL; return -EINVAL;
token = dell_smbios_find_token(kbd_tokens[bit]); return dell_set_std_token_value(&buffer, kbd_tokens[bit], USE_TVAL);
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;
} }
static int kbd_get_token_bit(u8 bit) 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); dell_fill_request(&buffer, token->location, 0, 0, 0);
ret = dell_send_request(&buffer, CLASS_TOKEN_READ, SELECT_TOKEN_STD); ret = dell_send_request(&buffer, CLASS_TOKEN_READ, SELECT_TOKEN_STD);
val = buffer.output[1];
if (ret) if (ret)
return ret; return ret;
val = buffer.output[1];
return (val == token->value); 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; int i;
@ -1506,7 +1519,7 @@ static inline void kbd_init_tokens(void)
kbd_token_bits |= BIT(i); kbd_token_bits |= BIT(i);
} }
static void kbd_init(void) static void __init kbd_init(void)
{ {
int ret; int ret;
@ -2131,21 +2144,11 @@ static int micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct calling_interface_buffer buffer; struct calling_interface_buffer buffer;
struct calling_interface_token *token; u32 tokenid;
int state = brightness != LED_OFF;
if (state == 0) tokenid = brightness == LED_OFF ?
token = dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE); GLOBAL_MIC_MUTE_DISABLE : GLOBAL_MIC_MUTE_ENABLE;
else return dell_set_std_token_value(&buffer, tokenid, USE_TVAL);
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;
} }
static struct led_classdev micmute_led_cdev = { 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) enum led_brightness brightness)
{ {
struct calling_interface_buffer buffer; struct calling_interface_buffer buffer;
struct calling_interface_token *token; u32 tokenid;
int state = brightness != LED_OFF;
if (state == 0) tokenid = brightness == LED_OFF ?
token = dell_smbios_find_token(GLOBAL_MUTE_DISABLE); GLOBAL_MUTE_DISABLE : GLOBAL_MUTE_ENABLE;
else return dell_set_std_token_value(&buffer, tokenid, USE_TVAL);
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;
} }
static struct led_classdev mute_led_cdev = { static struct led_classdev mute_led_cdev = {
@ -2183,9 +2176,274 @@ static struct led_classdev mute_led_cdev = {
.default_trigger = "audio-mute", .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; 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 max_intensity = 0;
int ret; int ret;
@ -2219,6 +2477,7 @@ static int __init dell_init(void)
touchpad_led_init(&platform_device->dev); touchpad_led_init(&platform_device->dev);
kbd_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); dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
debugfs_create_file("rfkill", 0444, dell_laptop_dir, 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) if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
return 0; return 0;
token = dell_smbios_find_token(BRIGHTNESS_TOKEN); ret = dell_send_request_for_tokenid(&buffer, CLASS_TOKEN_READ,
if (token) { SELECT_TOKEN_AC, BRIGHTNESS_TOKEN, 0);
struct calling_interface_buffer buffer; if (ret == 0)
max_intensity = buffer.output[3];
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];
}
if (max_intensity) { if (max_intensity) {
struct backlight_properties props; struct backlight_properties props;
@ -2293,6 +2546,7 @@ fail_backlight:
if (mute_led_registered) if (mute_led_registered)
led_classdev_unregister(&mute_led_cdev); led_classdev_unregister(&mute_led_cdev);
fail_led: fail_led:
dell_battery_exit();
dell_cleanup_rfkill(); dell_cleanup_rfkill();
fail_rfkill: fail_rfkill:
platform_device_del(platform_device); platform_device_del(platform_device);
@ -2311,6 +2565,7 @@ static void __exit dell_exit(void)
if (quirks && quirks->touchpad_led) if (quirks && quirks->touchpad_led)
touchpad_led_exit(); touchpad_led_exit();
kbd_led_exit(); kbd_led_exit();
dell_battery_exit();
backlight_device_unregister(dell_backlight_device); backlight_device_unregister(dell_backlight_device);
if (micmute_led_registered) if (micmute_led_registered)
led_classdev_unregister(&micmute_led_cdev); led_classdev_unregister(&micmute_led_cdev);

View File

@ -33,6 +33,13 @@
#define KBD_LED_AUTO_50_TOKEN 0x02EB #define KBD_LED_AUTO_50_TOKEN 0x02EB
#define KBD_LED_AUTO_75_TOKEN 0x02EC #define KBD_LED_AUTO_75_TOKEN 0x02EC
#define KBD_LED_AUTO_100_TOKEN 0x02F6 #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_ENABLE 0x0364
#define GLOBAL_MIC_MUTE_DISABLE 0x0365 #define GLOBAL_MIC_MUTE_DISABLE 0x0365
#define GLOBAL_MUTE_ENABLE 0x058C #define GLOBAL_MUTE_ENABLE 0x058C

View File

@ -70,20 +70,10 @@ static bool dell_wmi_aio_event_check(u8 *buffer, int length)
return false; 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; 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) { if (obj) {
unsigned int scancode = 0; unsigned int scancode = 0;
@ -114,7 +104,6 @@ static void dell_wmi_aio_notify(u32 value, void *context)
break; break;
} }
} }
kfree(obj);
} }
static int __init dell_wmi_aio_input_setup(void) static int __init dell_wmi_aio_input_setup(void)

View File

@ -15,7 +15,6 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/backlight.h> #include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h> #include <linux/hwmon-sysfs.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -1137,7 +1136,7 @@ static int eeepc_backlight_init(struct eeepc_laptop *eeepc)
} }
eeepc->backlight_device = bd; eeepc->backlight_device = bd;
bd->props.brightness = read_brightness(bd); bd->props.brightness = read_brightness(bd);
bd->props.power = FB_BLANK_UNBLANK; bd->props.power = BACKLIGHT_POWER_ON;
backlight_update_status(bd); backlight_update_status(bd);
return 0; return 0;
} }

View File

@ -13,13 +13,13 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/backlight.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/sparse-keymap.h> #include <linux/input/sparse-keymap.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/fb.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include "asus-wmi.h" #include "asus-wmi.h"
@ -192,7 +192,7 @@ static void eeepc_wmi_quirks(struct asus_wmi_driver *driver)
driver->quirks = quirks; driver->quirks = quirks;
driver->quirks->wapf = -1; driver->quirks->wapf = -1;
driver->panel_power = FB_BLANK_UNBLANK; driver->panel_power = BACKLIGHT_POWER_ON;
} }
static struct asus_wmi_driver asus_wmi_driver = { static struct asus_wmi_driver asus_wmi_driver = {

View File

@ -43,7 +43,6 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/backlight.h> #include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/sparse-keymap.h> #include <linux/input/sparse-keymap.h>
#include <linux/kfifo.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); 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) 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); struct acpi_device *device = bl_get_data(b);
if (fext) { if (fext) {
if (b->props.power == FB_BLANK_POWERDOWN) if (b->props.power == BACKLIGHT_POWER_OFF)
call_fext_func(fext, FUNC_BACKLIGHT, 0x1, call_fext_func(fext, FUNC_BACKLIGHT, 0x1,
BACKLIGHT_PARAM_POWER, BACKLIGHT_OFF); BACKLIGHT_PARAM_POWER, BACKLIGHT_OFF);
else else
@ -933,9 +932,9 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
acpi_video_get_backlight_type() == acpi_backlight_vendor) { acpi_video_get_backlight_type() == acpi_backlight_vendor) {
if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2, if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2,
BACKLIGHT_PARAM_POWER, 0x0) == BACKLIGHT_OFF) 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 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); ret = acpi_fujitsu_laptop_input_setup(device);

View File

@ -834,28 +834,16 @@ static struct attribute *hp_wmi_attrs[] = {
}; };
ATTRIBUTE_GROUPS(hp_wmi); 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; u32 event_id, event_data;
union acpi_object *obj;
acpi_status status;
u32 *location; u32 *location;
int key_code; 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) if (!obj)
return; return;
if (obj->type != ACPI_TYPE_BUFFER) { if (obj->type != ACPI_TYPE_BUFFER) {
pr_info("Unknown response received %d\n", obj->type); pr_info("Unknown response received %d\n", obj->type);
kfree(obj);
return; return;
} }
@ -872,10 +860,8 @@ static void hp_wmi_notify(u32 value, void *context)
event_data = *(location + 2); event_data = *(location + 2);
} else { } else {
pr_info("Unknown buffer length %d\n", obj->buffer.length); pr_info("Unknown buffer length %d\n", obj->buffer.length);
kfree(obj);
return; return;
} }
kfree(obj);
switch (event_id) { switch (event_id) {
case HPWMI_DOCK_EVENT: case HPWMI_DOCK_EVENT:

View File

@ -734,26 +734,14 @@ static void huawei_wmi_process_key(struct input_dev *idev, int code)
sparse_keymap_report_entry(idev, key, 1, true); 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 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) if (obj && obj->type == ACPI_TYPE_INTEGER)
huawei_wmi_process_key(idev, obj->integer.value); huawei_wmi_process_key(idev, obj->integer.value);
else else
dev_err(&idev->dev, "Bad response type\n"); dev_err(&idev->dev, "Bad response type\n");
kfree(response.pointer);
} }
static int huawei_wmi_input_setup(struct device *dev, const char *guid) static int huawei_wmi_input_setup(struct device *dev, const char *guid)

View File

@ -17,11 +17,11 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/fb.h>
#include <linux/i8042.h> #include <linux/i8042.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/sparse-keymap.h> #include <linux/input/sparse-keymap.h>
#include <linux/jiffies.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/module.h> #include <linux/module.h>
@ -87,6 +87,34 @@ enum {
SALS_FNLOCK_OFF = 0xf, 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 * These correspond to the number of supported states - 1
* Future keyboard types may need a new system, if there's a collision * 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 * ACPI Helpers
*/ */
#define IDEAPAD_EC_TIMEOUT 200 /* in ms */
static int eval_int(acpi_handle handle, const char *name, unsigned long *res) 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; 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, &params, &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) 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); 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); 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", &params, 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 * debugfs
*/ */
@ -419,13 +554,14 @@ static ssize_t camera_power_show(struct device *dev,
char *buf) char *buf)
{ {
struct ideapad_private *priv = dev_get_drvdata(dev); struct ideapad_private *priv = dev_get_drvdata(dev);
unsigned long result; unsigned long result = 0;
int err; 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); err = read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result);
if (err) if (err)
return err; return err;
}
return sysfs_emit(buf, "%d\n", !!result); return sysfs_emit(buf, "%d\n", !!result);
} }
@ -442,10 +578,11 @@ static ssize_t camera_power_store(struct device *dev,
if (err) if (err)
return 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); err = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);
if (err) if (err)
return err; return err;
}
return count; return count;
} }
@ -493,13 +630,14 @@ static ssize_t fan_mode_show(struct device *dev,
char *buf) char *buf)
{ {
struct ideapad_private *priv = dev_get_drvdata(dev); struct ideapad_private *priv = dev_get_drvdata(dev);
unsigned long result; unsigned long result = 0;
int err; 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); err = read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result);
if (err) if (err)
return err; return err;
}
return sysfs_emit(buf, "%lu\n", result); 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) if (state > 4 || state == 3)
return -EINVAL; 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); err = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);
if (err) if (err)
return err; return err;
}
return count; return count;
} }
@ -602,13 +741,14 @@ static ssize_t touchpad_show(struct device *dev,
char *buf) char *buf)
{ {
struct ideapad_private *priv = dev_get_drvdata(dev); struct ideapad_private *priv = dev_get_drvdata(dev);
unsigned long result; unsigned long result = 0;
int err; 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); err = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result);
if (err) if (err)
return err; return err;
}
priv->r_touchpad_val = result; priv->r_touchpad_val = result;
@ -627,10 +767,11 @@ static ssize_t touchpad_store(struct device *dev,
if (err) if (err)
return 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); err = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state);
if (err) if (err)
return err; return err;
}
priv->r_touchpad_val = state; priv->r_touchpad_val = state;
@ -1282,7 +1423,7 @@ static int ideapad_backlight_update_status(struct backlight_device *blightdev)
return err; return err;
err = write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER, 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) if (err)
return err; return err;
@ -1332,7 +1473,7 @@ static int ideapad_backlight_init(struct ideapad_private *priv)
priv->blightdev = blightdev; priv->blightdev = blightdev;
blightdev->props.brightness = now; 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); 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)) if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
return; 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) static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)

View File

@ -9,9 +9,6 @@
#ifndef _IDEAPAD_LAPTOP_H_ #ifndef _IDEAPAD_LAPTOP_H_
#define _IDEAPAD_LAPTOP_H_ #define _IDEAPAD_LAPTOP_H_
#include <linux/acpi.h>
#include <linux/jiffies.h>
#include <linux/errno.h>
#include <linux/notifier.h> #include <linux/notifier.h>
enum ideapad_laptop_notifier_actions { 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); int ideapad_laptop_unregister_notifier(struct notifier_block *nb);
void ideapad_laptop_call_notifier(unsigned long action, void *data); 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, &params, &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", &params, 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_ */ #endif /* !_IDEAPAD_LAPTOP_H_ */

View File

@ -13,6 +13,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/string_choices.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include "../dual_accel_detect.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); acpi_handle handle = ACPI_HANDLE(device);
/* Enable|disable features - power button is always enabled */ /* Enable|disable features - power button is always enabled */
if (!intel_hid_execute_method(handle, INTEL_HID_DSM_HDSM_FN, if (!intel_hid_execute_method(handle, INTEL_HID_DSM_HDSM_FN, enable)) {
enable)) { dev_warn(device, "failed to %s hotkeys\n", str_enable_disable(enable));
dev_warn(device, "failed to %sable hotkeys\n",
enable ? "en" : "dis");
return -EIO; return -EIO;
} }

View File

@ -32,6 +32,7 @@ bool *ifs_pkg_auth;
static const struct ifs_test_caps scan_test = { static const struct ifs_test_caps scan_test = {
.integrity_cap_bit = MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT, .integrity_cap_bit = MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT,
.test_num = IFS_TYPE_SAF, .test_num = IFS_TYPE_SAF,
.image_suffix = "scan",
}; };
static const struct ifs_test_caps array_test = { 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, .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[] = { static struct ifs_device ifs_devices[] = {
[IFS_TYPE_SAF] = { [IFS_TYPE_SAF] = {
.test_caps = &scan_test, .test_caps = &scan_test,
.test_msrs = &scan_msrs,
.misc = { .misc = {
.name = "intel_ifs_0", .name = "intel_ifs_0",
.minor = MISC_DYNAMIC_MINOR, .minor = MISC_DYNAMIC_MINOR,
@ -56,6 +80,15 @@ static struct ifs_device ifs_devices[] = {
.groups = plat_ifs_array_groups, .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) #define IFS_NUMTESTS ARRAY_SIZE(ifs_devices)

View File

@ -126,11 +126,40 @@
* The driver does not make use of this, it only tests one core at a time. * The driver does not make use of this, it only tests one core at a time.
* *
* .. [#f1] https://github.com/intel/TBD * .. [#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/device.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#define MSR_ARRAY_BIST 0x00000105 #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_COPY_SCAN_HASHES 0x000002c2
#define MSR_SCAN_HASHES_STATUS 0x000002c3 #define MSR_SCAN_HASHES_STATUS 0x000002c3
#define MSR_AUTHENTICATE_AND_COPY_CHUNK 0x000002c4 #define MSR_AUTHENTICATE_AND_COPY_CHUNK 0x000002c4
@ -140,6 +169,7 @@
#define MSR_ARRAY_TRIGGER 0x000002d6 #define MSR_ARRAY_TRIGGER 0x000002d6
#define MSR_ARRAY_STATUS 0x000002d7 #define MSR_ARRAY_STATUS 0x000002d7
#define MSR_SAF_CTRL 0x000004f0 #define MSR_SAF_CTRL 0x000004f0
#define MSR_SBAF_CTRL 0x000004f8
#define SCAN_NOT_TESTED 0 #define SCAN_NOT_TESTED 0
#define SCAN_TEST_PASS 1 #define SCAN_TEST_PASS 1
@ -147,6 +177,7 @@
#define IFS_TYPE_SAF 0 #define IFS_TYPE_SAF 0
#define IFS_TYPE_ARRAY_BIST 1 #define IFS_TYPE_ARRAY_BIST 1
#define IFS_TYPE_SBAF 2
#define ARRAY_GEN0 0 #define ARRAY_GEN0 0
#define ARRAY_GEN1 1 #define ARRAY_GEN1 1
@ -196,7 +227,8 @@ union ifs_chunks_auth_status_gen2 {
u16 valid_chunks; u16 valid_chunks;
u16 total_chunks; u16 total_chunks;
u32 error_code :8; 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 * Driver populated error-codes
* 0xFD: Test timed out before completing all the chunks. * 0xFD: Test timed out before completing all the chunks.
@ -261,9 +321,28 @@ union ifs_array {
#define IFS_SW_TIMEOUT 0xFD #define IFS_SW_TIMEOUT 0xFD
#define IFS_SW_PARTIAL_COMPLETION 0xFE #define IFS_SW_PARTIAL_COMPLETION 0xFE
#define IFS_SUFFIX_SZ 5
struct ifs_test_caps { struct ifs_test_caps {
int integrity_cap_bit; int integrity_cap_bit;
int test_num; 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 * @generation: IFS test generation enumerated by hardware
* @chunk_size: size of a test chunk * @chunk_size: size of a test chunk
* @array_gen: test generation of array test * @array_gen: test generation of array test
* @max_bundle: maximum bundle index
*/ */
struct ifs_data { struct ifs_data {
int loaded_version; int loaded_version;
@ -290,6 +370,7 @@ struct ifs_data {
u32 generation; u32 generation;
u32 chunk_size; u32 chunk_size;
u32 array_gen; u32 array_gen;
u32 max_bundle;
}; };
struct ifs_work { struct ifs_work {
@ -299,6 +380,7 @@ struct ifs_work {
struct ifs_device { struct ifs_device {
const struct ifs_test_caps *test_caps; const struct ifs_test_caps *test_caps;
const struct ifs_test_msrs *test_msrs;
struct ifs_data rw_data; struct ifs_data rw_data;
struct miscdevice misc; 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; 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; extern bool *ifs_pkg_auth;
int ifs_load_firmware(struct device *dev); int ifs_load_firmware(struct device *dev);
int do_core_test(int cpu, struct device *dev); int do_core_test(int cpu, struct device *dev);

View File

@ -118,15 +118,17 @@ static void copy_hashes_authenticate_chunks(struct work_struct *work)
union ifs_scan_hashes_status hashes_status; union ifs_scan_hashes_status hashes_status;
union ifs_chunks_auth_status chunk_status; union ifs_chunks_auth_status chunk_status;
struct device *dev = local_work->dev; struct device *dev = local_work->dev;
const struct ifs_test_msrs *msrs;
int i, num_chunks, chunk_size; int i, num_chunks, chunk_size;
struct ifs_data *ifsd; struct ifs_data *ifsd;
u64 linear_addr, base; u64 linear_addr, base;
u32 err_code; u32 err_code;
ifsd = ifs_get_data(dev); ifsd = ifs_get_data(dev);
msrs = ifs_get_test_msrs(dev);
/* run scan hash copy */ /* run scan hash copy */
wrmsrl(MSR_COPY_SCAN_HASHES, ifs_hash_ptr); wrmsrl(msrs->copy_hashes, ifs_hash_ptr);
rdmsrl(MSR_SCAN_HASHES_STATUS, hashes_status.data); rdmsrl(msrs->copy_hashes_status, hashes_status.data);
/* enumerate the scan image information */ /* enumerate the scan image information */
num_chunks = hashes_status.num_chunks; 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 = base + i * chunk_size;
linear_addr |= i; linear_addr |= i;
wrmsrl(MSR_AUTHENTICATE_AND_COPY_CHUNK, linear_addr); wrmsrl(msrs->copy_chunks, linear_addr);
rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data); rdmsrl(msrs->copy_chunks_status, chunk_status.data);
ifsd->valid_chunks = chunk_status.valid_chunks; ifsd->valid_chunks = chunk_status.valid_chunks;
err_code = chunk_status.error_code; 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_scan_hashes_status_gen2 hashes_status;
union ifs_chunks_auth_status_gen2 chunk_status; union ifs_chunks_auth_status_gen2 chunk_status;
u32 err_code, valid_chunks, total_chunks; u32 err_code, valid_chunks, total_chunks;
const struct ifs_test_msrs *msrs;
int i, num_chunks, chunk_size; int i, num_chunks, chunk_size;
union meta_data *ifs_meta; union meta_data *ifs_meta;
int starting_chunk_nr; int starting_chunk_nr;
@ -189,10 +192,11 @@ static int copy_hashes_authenticate_chunks_gen2(struct device *dev)
int retry_count; int retry_count;
ifsd = ifs_get_data(dev); ifsd = ifs_get_data(dev);
msrs = ifs_get_test_msrs(dev);
if (need_copy_scan_hashes(ifsd)) { if (need_copy_scan_hashes(ifsd)) {
wrmsrl(MSR_COPY_SCAN_HASHES, ifs_hash_ptr); wrmsrl(msrs->copy_hashes, ifs_hash_ptr);
rdmsrl(MSR_SCAN_HASHES_STATUS, hashes_status.data); rdmsrl(msrs->copy_hashes_status, hashes_status.data);
/* enumerate the scan image information */ /* enumerate the scan image information */
chunk_size = hashes_status.chunk_size * SZ_1K; 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) { if (ifsd->generation >= IFS_GEN_STRIDE_AWARE) {
wrmsrl(MSR_SAF_CTRL, INVALIDATE_STRIDE); wrmsrl(msrs->test_ctrl, INVALIDATE_STRIDE);
rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data); rdmsrl(msrs->copy_chunks_status, chunk_status.data);
if (chunk_status.valid_chunks != 0) { if (chunk_status.valid_chunks != 0) {
dev_err(dev, "Couldn't invalidate installed stride - %d\n", dev_err(dev, "Couldn't invalidate installed stride - %d\n",
chunk_status.valid_chunks); chunk_status.valid_chunks);
@ -234,9 +238,9 @@ static int copy_hashes_authenticate_chunks_gen2(struct device *dev)
chunk_table[1] = linear_addr; chunk_table[1] = linear_addr;
do { do {
local_irq_disable(); local_irq_disable();
wrmsrl(MSR_AUTHENTICATE_AND_COPY_CHUNK, (u64)chunk_table); wrmsrl(msrs->copy_chunks, (u64)chunk_table);
local_irq_enable(); 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; err_code = chunk_status.error_code;
} while (err_code == AUTH_INTERRUPTED_ERROR && --retry_count); } while (err_code == AUTH_INTERRUPTED_ERROR && --retry_count);
@ -257,20 +261,22 @@ static int copy_hashes_authenticate_chunks_gen2(struct device *dev)
return -EIO; return -EIO;
} }
ifsd->valid_chunks = valid_chunks; ifsd->valid_chunks = valid_chunks;
ifsd->max_bundle = chunk_status.max_bundle;
return 0; return 0;
} }
static int validate_ifs_metadata(struct device *dev) 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); struct ifs_data *ifsd = ifs_get_data(dev);
union meta_data *ifs_meta; union meta_data *ifs_meta;
char test_file[64]; char test_file[64];
int ret = -EINVAL; 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, 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); ifs_meta = (union meta_data *)find_meta_data(ifs_header_ptr, META_TYPE_IFS);
if (!ifs_meta) { if (!ifs_meta) {
@ -300,6 +306,12 @@ static int validate_ifs_metadata(struct device *dev)
return ret; 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; return 0;
} }
@ -387,9 +399,9 @@ int ifs_load_firmware(struct device *dev)
char scan_path[64]; char scan_path[64];
int ret; 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, 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); ret = request_firmware_direct(&fw, scan_path, dev);
if (ret) { if (ret) {

View File

@ -29,6 +29,13 @@ struct run_params {
union ifs_status status; 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 * Number of TSC cycles that a logical CPU will wait for the other
* logical CPU on the core in the WRMSR(ACTIVATE_SCAN). * 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 */ #define SPINUNIT 100 /* 100 nsec */
static atomic_t array_cpus_in; static atomic_t array_cpus_in;
static atomic_t scan_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() * 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; 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 * 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 * 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 else
ifs_array_test_gen1(cpu, dev); ifs_array_test_gen1(cpu, dev);
break; break;
case IFS_TYPE_SBAF:
if (!ifsd->loaded)
ret = -EPERM;
else
ifs_sbaf_test_core(cpu, dev);
break;
default: default:
ret = -EINVAL; ret = -EINVAL;
} }

View File

@ -1,4 +1,7 @@
obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472_discrete.o \ obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472_discrete.o \
intel_skl_int3472_tps68470.o intel_skl_int3472_tps68470.o \
intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o led.o common.o intel_skl_int3472_common.o
intel_skl_int3472_tps68470-y := tps68470.o tps68470_board_data.o 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

View File

@ -29,6 +29,7 @@ union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, char *i
return obj; return obj;
} }
EXPORT_SYMBOL_GPL(skl_int3472_get_acpi_buffer);
int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb) int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb)
{ {
@ -52,6 +53,7 @@ out_free_obj:
kfree(obj); kfree(obj);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(skl_int3472_fill_cldb);
/* sensor_adev_ret may be NULL, name_ret must not be NULL */ /* sensor_adev_ret may be NULL, name_ret must not be NULL */
int skl_int3472_get_sensor_adev_and_name(struct device *dev, 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; 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");

View File

@ -11,6 +11,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/overflow.h> #include <linux/overflow.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/string_choices.h>
#include <linux/uuid.h> #include <linux/uuid.h>
#include "common.h" #include "common.h"
@ -69,11 +70,7 @@ static int skl_int3472_fill_gpiod_lookup(struct gpiod_lookup *table_entry,
if (!adev) if (!adev)
return -ENODEV; return -ENODEV;
table_entry->key = acpi_dev_name(adev); *table_entry = GPIO_LOOKUP(acpi_dev_name(adev), agpio->pin_table[0], func, polarity);
table_entry->chip_hwnum = agpio->pin_table[0];
table_entry->con_id = func;
table_entry->idx = 0;
table_entry->flags = polarity;
return 0; 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, dev_dbg(int3472->dev, "%s %s pin %d active-%s\n", func,
agpio->resource_source.string_ptr, agpio->pin_table[0], agpio->resource_source.string_ptr, agpio->pin_table[0],
(polarity == GPIO_ACTIVE_HIGH) ? "high" : "low"); str_high_low(polarity == GPIO_ACTIVE_HIGH));
switch (type) { switch (type) {
case INT3472_GPIO_TYPE_RESET: case INT3472_GPIO_TYPE_RESET:

View File

@ -28,7 +28,6 @@
#include <linux/backlight.h> #include <linux/backlight.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/fb.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
@ -250,7 +249,7 @@ static int oaktrail_backlight_init(void)
oaktrail_bl_device = bd; oaktrail_bl_device = bd;
bd->props.brightness = get_backlight_brightness(bd); bd->props.brightness = get_backlight_brightness(bd);
bd->props.power = FB_BLANK_UNBLANK; bd->props.power = BACKLIGHT_POWER_ON;
backlight_update_status(bd); backlight_update_status(bd);
return 0; return 0;

View File

@ -715,6 +715,49 @@ static int pmc_core_s0ix_blocker_show(struct seq_file *s, void *unused)
} }
DEFINE_SHOW_ATTRIBUTE(pmc_core_s0ix_blocker); 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, static inline u64 adjust_lpm_residency(struct pmc *pmc, u32 offset,
const int lpm_adj_x2) 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]; struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
const int lpm_adj_x2 = pmc->map->lpm_res_counter_step_x2; const int lpm_adj_x2 = pmc->map->lpm_res_counter_step_x2;
u32 offset = pmc->map->lpm_residency_offset; u32 offset = pmc->map->lpm_residency_offset;
unsigned int i;
int mode; int mode;
seq_printf(s, "%-10s %-15s\n", "Substate", "Residency"); 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], seq_printf(s, "%-10s %-15llu\n", pmc_lpm_modes[mode],
adjust_lpm_residency(pmc, offset + (4 * mode), lpm_adj_x2)); 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) static void pmc_core_substate_req_header_show(struct seq_file *s, int pmc_index)
{ {
struct pmc_dev *pmcdev = s->private; struct pmc_dev *pmcdev = s->private;
unsigned int i;
int mode; int mode;
seq_printf(s, "%30s |", "Element"); 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 |", 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) static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
{ {
struct pmc_dev *pmcdev = s->private; struct pmc_dev *pmcdev = s->private;
u32 sts_offset; u32 sts_offset;
u32 sts_offset_live;
u32 *lpm_req_regs; u32 *lpm_req_regs;
unsigned int mp, pmc_index; unsigned int mp, pmc_index;
int num_maps; 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; maps = pmc->map->lpm_sts;
num_maps = pmc->map->lpm_num_maps; num_maps = pmc->map->lpm_num_maps;
sts_offset = pmc->map->lpm_status_offset; sts_offset = pmc->map->lpm_status_offset;
sts_offset_live = pmc->map->lpm_live_status_offset;
lpm_req_regs = pmc->lpm_req_regs; 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++) { for (mp = 0; mp < num_maps; mp++) {
u32 req_mask = 0; u32 req_mask = 0;
u32 lpm_status; u32 lpm_status;
u32 lpm_status_live;
const struct pmc_bit_map *map; 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 * Capture the requirements and create a mask so that we only
* show an element if it's required for at least one of the * show an element if it's required for at least one of the
* enabled low power modes * 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)]; req_mask |= lpm_req_regs[mp + (mode * num_maps)];
/* Get the last latched status for this map */ /* Get the last latched status for this map */
lpm_status = pmc_core_reg_read(pmc, sts_offset + (mp * 4)); 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 */ /* Loop over elements in this map */
map = maps[mp]; map = maps[mp];
for (i = 0; map[i].name && i < len; i++) { 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); seq_printf(s, "pmc%d: %26s |", pmc_index, map[i].name);
/* Loop over the enabled states and display if required */ /* 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)] & bool required = lpm_req_regs[mp + (mode * num_maps)] &
bit_mask; bit_mask;
seq_printf(s, " %9s |", required ? "Required" : " "); 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 */ /* In Status column, show the last captured state of this agent */
seq_printf(s, " %9s |", lpm_status & bit_mask ? "Yes" : " "); 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"); 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_dev *pmcdev = s->private;
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
unsigned int idx;
bool c10; bool c10;
u32 reg; u32 reg;
int mode; int mode;
@ -940,7 +990,7 @@ static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused)
c10 = true; c10 = true;
} }
pmc_for_each_mode(idx, mode, pmcdev) { pmc_for_each_mode(mode, pmcdev) {
if ((BIT(mode) & reg) && !c10) if ((BIT(mode) & reg) && !c10)
seq_printf(s, " [%s]", pmc_lpm_modes[mode]); seq_printf(s, " [%s]", pmc_lpm_modes[mode]);
else 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]; struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
bool clear = false, c10 = false; bool clear = false, c10 = false;
unsigned char buf[8]; unsigned char buf[8];
unsigned int idx;
int m, mode; int m, mode;
u32 reg; 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); mode = sysfs_match_string(pmc_lpm_modes, buf);
/* Check string matches enabled mode */ /* Check string matches enabled mode */
pmc_for_each_mode(idx, m, pmcdev) pmc_for_each_mode(m, pmcdev)
if (mode == m) if (mode == m)
break; break;
@ -1524,6 +1573,10 @@ static bool warn_on_s0ix_failures;
module_param(warn_on_s0ix_failures, bool, 0644); module_param(warn_on_s0ix_failures, bool, 0644);
MODULE_PARM_DESC(warn_on_s0ix_failures, "Check and warn for S0ix failures"); 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) static __maybe_unused int pmc_core_suspend(struct device *dev)
{ {
struct pmc_dev *pmcdev = dev_get_drvdata(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) if (pmcdev->suspend)
pmcdev->suspend(pmcdev); pmcdev->suspend(pmcdev);
if (ltr_ignore_all_suspend)
pmc_core_ltr_ignore_all(pmcdev);
/* Check if the syspend will actually use S0ix */ /* Check if the syspend will actually use S0ix */
if (pm_suspend_via_firmware()) if (pm_suspend_via_firmware())
return 0; return 0;
@ -1639,6 +1695,9 @@ static __maybe_unused int pmc_core_resume(struct device *dev)
{ {
struct pmc_dev *pmcdev = dev_get_drvdata(dev); struct pmc_dev *pmcdev = dev_get_drvdata(dev);
if (ltr_ignore_all_suspend)
pmc_core_ltr_restore_all(pmcdev);
if (pmcdev->resume) if (pmcdev->resume)
return pmcdev->resume(pmcdev); return pmcdev->resume(pmcdev);

View File

@ -378,6 +378,7 @@ struct pmc_info {
* @map: pointer to pmc_reg_map struct that contains platform * @map: pointer to pmc_reg_map struct that contains platform
* specific attributes * specific attributes
* @lpm_req_regs: List of substate requirements * @lpm_req_regs: List of substate requirements
* @ltr_ign: Holds LTR ignore data while suspended
* *
* pmc contains info about one power management controller device. * pmc contains info about one power management controller device.
*/ */
@ -386,6 +387,7 @@ struct pmc {
void __iomem *regbase; void __iomem *regbase;
const struct pmc_reg_map *map; const struct pmc_reg_map *map;
u32 *lpm_req_regs; 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); void cnl_suspend(struct pmc_dev *pmcdev);
int cnl_resume(struct pmc_dev *pmcdev); int cnl_resume(struct pmc_dev *pmcdev);
#define pmc_for_each_mode(i, mode, pmcdev) \ #define pmc_for_each_mode(mode, pmcdev) \
for (i = 0, mode = pmcdev->lpm_en_modes[i]; \ for (unsigned int __i = 0, __cond; \
i < pmcdev->num_lpm_modes; \ __cond = __i < (pmcdev)->num_lpm_modes, \
i++, mode = pmcdev->lpm_en_modes[i]) __cond && ((mode) = (pmcdev)->lpm_en_modes[__i]), \
__cond; \
__i++)
#define DEFINE_PMC_CORE_ATTR_WRITE(__name) \ #define DEFINE_PMC_CORE_ATTR_WRITE(__name) \
static int __name ## _open(struct inode *inode, struct file *file) \ static int __name ## _open(struct inode *inode, struct file *file) \

View File

@ -9,11 +9,11 @@
*/ */
#include <linux/cleanup.h> #include <linux/cleanup.h>
#include <linux/intel_vsec.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/io-64-nonatomic-lo-hi.h> #include <linux/io-64-nonatomic-lo-hi.h>
#include "core.h" #include "core.h"
#include "../vsec.h"
#include "../pmt/telemetry.h" #include "../pmt/telemetry.h"
#define SSRAM_HDR_SIZE 0x100 #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; struct telem_endpoint *ep;
const u8 *lpm_indices; const u8 *lpm_indices;
int num_maps, mode_offset = 0; int num_maps, mode_offset = 0;
int ret, mode, i; int ret, mode;
int lpm_size; int lpm_size;
u32 guid; 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; 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); u32 *req_offset = pmc->lpm_req_regs + (mode * num_maps);
int m; int m;

View File

@ -9,12 +9,12 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/intel_vsec.h>
#include <linux/io-64-nonatomic-lo-hi.h> #include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/pci.h> #include <linux/pci.h>
#include "../vsec.h"
#include "class.h" #include "class.h"
#define PMT_XA_START 1 #define PMT_XA_START 1
@ -58,6 +58,22 @@ pmt_memcpy64_fromio(void *to, const u64 __iomem *from, size_t count)
return 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 * sysfs
*/ */
@ -79,11 +95,8 @@ intel_pmt_read(struct file *filp, struct kobject *kobj,
if (count > entry->size - off) if (count > entry->size - off)
count = entry->size - off; count = entry->size - off;
if (entry->guid == GUID_SPR_PUNIT) count = pmt_telem_read_mmio(entry->ep->pcidev, entry->cb, entry->header.guid, buf,
/* PUNIT on SPR only supports aligned 64-bit read */ entry->base + off, count);
count = pmt_memcpy64_fromio(buf, entry->base + off, count);
else
memcpy_fromio(buf, entry->base + off, count);
return count; return count;
} }
@ -239,6 +252,7 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry,
entry->guid = header->guid; entry->guid = header->guid;
entry->size = header->size; entry->size = header->size;
entry->cb = ivdev->priv_data;
return 0; return 0;
} }
@ -300,7 +314,7 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry,
goto fail_ioremap; goto fail_ioremap;
if (ns->pmt_add_endpoint) { if (ns->pmt_add_endpoint) {
ret = ns->pmt_add_endpoint(entry, ivdev->pcidev); ret = ns->pmt_add_endpoint(ivdev, entry);
if (ret) if (ret)
goto fail_add_endpoint; goto fail_add_endpoint;
} }

View File

@ -2,13 +2,13 @@
#ifndef _INTEL_PMT_CLASS_H #ifndef _INTEL_PMT_CLASS_H
#define _INTEL_PMT_CLASS_H #define _INTEL_PMT_CLASS_H
#include <linux/intel_vsec.h>
#include <linux/xarray.h> #include <linux/xarray.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include "../vsec.h"
#include "telemetry.h" #include "telemetry.h"
/* PMT access types */ /* PMT access types */
@ -24,6 +24,7 @@ struct pci_dev;
struct telem_endpoint { struct telem_endpoint {
struct pci_dev *pcidev; struct pci_dev *pcidev;
struct telem_header header; struct telem_header header;
struct pmt_callbacks *cb;
void __iomem *base; void __iomem *base;
bool present; bool present;
struct kref kref; struct kref kref;
@ -43,6 +44,7 @@ struct intel_pmt_entry {
struct kobject *kobj; struct kobject *kobj;
void __iomem *disc_table; void __iomem *disc_table;
void __iomem *base; void __iomem *base;
struct pmt_callbacks *cb;
unsigned long base_addr; unsigned long base_addr;
size_t size; size_t size;
u32 guid; u32 guid;
@ -55,10 +57,12 @@ struct intel_pmt_namespace {
const struct attribute_group *attr_grp; const struct attribute_group *attr_grp;
int (*pmt_header_decode)(struct intel_pmt_entry *entry, int (*pmt_header_decode)(struct intel_pmt_entry *entry,
struct device *dev); struct device *dev);
int (*pmt_add_endpoint)(struct intel_pmt_entry *entry, int (*pmt_add_endpoint)(struct intel_vsec_device *ivdev,
struct pci_dev *pdev); 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); bool intel_pmt_is_early_client_hw(struct device *dev);
int intel_pmt_dev_create(struct intel_pmt_entry *entry, int intel_pmt_dev_create(struct intel_pmt_entry *entry,
struct intel_pmt_namespace *ns, struct intel_pmt_namespace *ns,

View File

@ -9,6 +9,7 @@
*/ */
#include <linux/auxiliary_bus.h> #include <linux/auxiliary_bus.h>
#include <linux/intel_vsec.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
@ -16,7 +17,6 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/overflow.h> #include <linux/overflow.h>
#include "../vsec.h"
#include "class.h" #include "class.h"
/* Crashlog discovery header types */ /* Crashlog discovery header types */

View File

@ -9,6 +9,7 @@
*/ */
#include <linux/auxiliary_bus.h> #include <linux/auxiliary_bus.h>
#include <linux/intel_vsec.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
@ -16,7 +17,6 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/overflow.h> #include <linux/overflow.h>
#include "../vsec.h"
#include "class.h" #include "class.h"
#define TELEM_SIZE_OFFSET 0x0 #define TELEM_SIZE_OFFSET 0x0
@ -93,8 +93,8 @@ static int pmt_telem_header_decode(struct intel_pmt_entry *entry,
return 0; return 0;
} }
static int pmt_telem_add_endpoint(struct intel_pmt_entry *entry, static int pmt_telem_add_endpoint(struct intel_vsec_device *ivdev,
struct pci_dev *pdev) struct intel_pmt_entry *entry)
{ {
struct telem_endpoint *ep; struct telem_endpoint *ep;
@ -104,13 +104,14 @@ static int pmt_telem_add_endpoint(struct intel_pmt_entry *entry,
return -ENOMEM; return -ENOMEM;
ep = entry->ep; ep = entry->ep;
ep->pcidev = pdev; ep->pcidev = ivdev->pcidev;
ep->header.access_type = entry->header.access_type; ep->header.access_type = entry->header.access_type;
ep->header.guid = entry->header.guid; ep->header.guid = entry->header.guid;
ep->header.base_offset = entry->header.base_offset; ep->header.base_offset = entry->header.base_offset;
ep->header.size = entry->header.size; ep->header.size = entry->header.size;
ep->base = entry->base; ep->base = entry->base;
ep->present = true; ep->present = true;
ep->cb = ivdev->priv_data;
kref_init(&ep->kref); 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) if (offset + NUM_BYTES_QWORD(count) > size)
return -EINVAL; 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; return ep->present ? 0 : -EPIPE;
} }

View File

@ -12,6 +12,7 @@
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/bitfield.h> #include <linux/bitfield.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/intel_vsec.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
@ -22,8 +23,6 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include "vsec.h"
#define ACCESS_TYPE_BARID 2 #define ACCESS_TYPE_BARID 2
#define ACCESS_TYPE_LOCAL 3 #define ACCESS_TYPE_LOCAL 3

View File

@ -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 */ /* Lock to prevent module registration when already opened by user space */
static DEFINE_MUTEX(punit_misc_dev_open_lock); 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 misc_device_open;
static int isst_if_open(struct inode *inode, struct file *file) 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) static int isst_misc_reg(void)
{ {
mutex_lock(&punit_misc_dev_reg_lock); int ret;
if (misc_device_ret)
goto unlock_exit;
if (!misc_usage_count) { ret = isst_if_cpu_info_init();
misc_device_ret = isst_if_cpu_info_init(); if (ret)
if (misc_device_ret) return ret;
goto unlock_exit;
misc_device_ret = misc_register(&isst_if_char_driver); ret = misc_register(&isst_if_char_driver);
if (misc_device_ret) { if (ret)
isst_if_cpu_info_exit(); isst_if_cpu_info_exit();
goto unlock_exit;
}
}
misc_usage_count++;
unlock_exit: return ret;
mutex_unlock(&punit_misc_dev_reg_lock);
return misc_device_ret;
} }
static void isst_misc_unreg(void) static void isst_misc_unreg(void)
{ {
mutex_lock(&punit_misc_dev_reg_lock); misc_deregister(&isst_if_char_driver);
if (misc_usage_count) isst_if_cpu_info_exit();
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);
} }
/** /**

View File

@ -51,6 +51,7 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/intel_tpmi.h> #include <linux/intel_tpmi.h>
#include <linux/intel_vsec.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/module.h> #include <linux/module.h>
@ -59,8 +60,6 @@
#include <linux/sizes.h> #include <linux/sizes.h>
#include <linux/string_helpers.h> #include <linux/string_helpers.h>
#include "vsec.h"
/** /**
* struct intel_tpmi_pfs_entry - TPMI PM Feature Structure (PFS) entry * struct intel_tpmi_pfs_entry - TPMI PM Feature Structure (PFS) entry
* @tpmi_id: TPMI feature identifier (what the feature is and its data format). * @tpmi_id: TPMI feature identifier (what the feature is and its data format).

View File

@ -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, static ssize_t store_attr(struct uncore_data *data, const char *buf, ssize_t count,
enum uncore_index index) enum uncore_index index)
{ {
unsigned int input; unsigned int input = 0;
int ret; int ret;
if (kstrtouint(buf, 10, &input)) if (index == UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE) {
return -EINVAL; if (kstrtobool(buf, (bool *)&input))
return -EINVAL;
} else {
if (kstrtouint(buf, 10, &input))
return -EINVAL;
}
mutex_lock(&uncore_lock); mutex_lock(&uncore_lock);
ret = uncore_write(data, input, index); 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); 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) \ #define show_uncore_data(member_name) \
static ssize_t show_##member_name(struct kobject *kobj, \ static ssize_t show_##member_name(struct kobject *kobj, \
struct kobj_attribute *attr, char *buf)\ 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) 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(max_freq_khz);
init_attribute_rw(min_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_min_freq_khz_kobj_attr.attr;
data->uncore_attrs[index++] = &data->initial_max_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) if (!ret)
data->uncore_attrs[index++] = &data->current_freq_khz_kobj_attr.attr; 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_attrs[index] = NULL;
data->uncore_attr_group.name = name; data->uncore_attr_group.name = name;

View File

@ -34,6 +34,13 @@
* @domain_id_kobj_attr: Storage for kobject attribute domain_id * @domain_id_kobj_attr: Storage for kobject attribute domain_id
* @fabric_cluster_id_kobj_attr: Storage for kobject attribute fabric_cluster_id * @fabric_cluster_id_kobj_attr: Storage for kobject attribute fabric_cluster_id
* @package_id_kobj_attr: Storage for kobject attribute package_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 * @uncore_attrs: Attribute storage for group creation
* *
* This structure is used to encapsulate all data related to uncore sysfs * 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 domain_id_kobj_attr;
struct kobj_attribute fabric_cluster_id_kobj_attr; struct kobj_attribute fabric_cluster_id_kobj_attr;
struct kobj_attribute package_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 #define UNCORE_DOMAIN_ID_INVALID -1
@ -70,6 +81,10 @@ enum uncore_index {
UNCORE_INDEX_MIN_FREQ, UNCORE_INDEX_MIN_FREQ,
UNCORE_INDEX_MAX_FREQ, UNCORE_INDEX_MAX_FREQ,
UNCORE_INDEX_CURRENT_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, int uncore_freq_common_init(int (*read)(struct uncore_data *data, unsigned int *value,

View File

@ -30,6 +30,7 @@
#define UNCORE_MAJOR_VERSION 0 #define UNCORE_MAJOR_VERSION 0
#define UNCORE_MINOR_VERSION 2 #define UNCORE_MINOR_VERSION 2
#define UNCORE_ELC_SUPPORTED_VERSION 2
#define UNCORE_HEADER_INDEX 0 #define UNCORE_HEADER_INDEX 0
#define UNCORE_FABRIC_CLUSTER_OFFSET 8 #define UNCORE_FABRIC_CLUSTER_OFFSET 8
@ -46,6 +47,7 @@ struct tpmi_uncore_struct;
/* Information for each cluster */ /* Information for each cluster */
struct tpmi_uncore_cluster_info { struct tpmi_uncore_cluster_info {
bool root_domain; bool root_domain;
bool elc_supported;
u8 __iomem *cluster_base; u8 __iomem *cluster_base;
struct uncore_data uncore_data; struct uncore_data uncore_data;
struct tpmi_uncore_struct *uncore_root; struct tpmi_uncore_struct *uncore_root;
@ -75,6 +77,10 @@ struct tpmi_uncore_struct {
/* Bit definitions for CONTROL register */ /* Bit definitions for CONTROL register */
#define UNCORE_MAX_RATIO_MASK GENMASK_ULL(14, 8) #define UNCORE_MAX_RATIO_MASK GENMASK_ULL(14, 8)
#define UNCORE_MIN_RATIO_MASK GENMASK_ULL(21, 15) #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 */ /* Helper function to read MMIO offset for max/min control frequency */
static void read_control_freq(struct tpmi_uncore_cluster_info *cluster_info, 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; *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) #define UNCORE_MAX_RATIO FIELD_MAX(UNCORE_MAX_RATIO_MASK)
/* Helper for sysfs read for max/min frequencies. Called under mutex locks */ /* 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; 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 */ /* 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, static void write_control_freq(struct tpmi_uncore_cluster_info *cluster_info, unsigned int input,
unsigned int index) 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)); 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, static int uncore_write_control_freq(struct uncore_data *data, unsigned int input,
enum uncore_index index) 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: case UNCORE_INDEX_CURRENT_FREQ:
return uncore_read_freq(data, value); 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: default:
break; break;
} }
@ -291,7 +442,7 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_
return -EINVAL; return -EINVAL;
/* Register callbacks to uncore core */ /* 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) if (ret)
return ret; return ret;
@ -409,6 +560,9 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_
cluster_info->uncore_root = tpmi_uncore; 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); ret = uncore_freq_add_entry(&cluster_info->uncore_data, 0);
if (ret) { if (ret) {
cluster_info->cluster_base = NULL; 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); 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.root_domain = true;
tpmi_uncore->root_cluster.uncore_root = tpmi_uncore; 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); 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); remove_cluster_entries(tpmi_uncore);
uncore_freq_common_exit(); uncore_freq_common_exit();

View File

@ -17,14 +17,13 @@
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/cleanup.h> #include <linux/cleanup.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/intel_vsec.h>
#include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/types.h> #include <linux/types.h>
#include "vsec.h"
#define PMT_XA_START 0 #define PMT_XA_START 0
#define PMT_XA_MAX INT_MAX #define PMT_XA_MAX INT_MAX
#define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_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->num_resources = header->num_entries;
intel_vsec_dev->quirks = info->quirks; intel_vsec_dev->quirks = info->quirks;
intel_vsec_dev->base_addr = info->base_addr; intel_vsec_dev->base_addr = info->base_addr;
intel_vsec_dev->priv_data = info->priv_data;
if (header->id == VSEC_ID_SDSI) if (header->id == VSEC_ID_SDSI)
intel_vsec_dev->ida = &intel_vsec_sdsi_ida; 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, void intel_vsec_register(struct pci_dev *pdev,
struct intel_vsec_platform_info *info) struct intel_vsec_platform_info *info)
{ {
if (!pdev || !info) if (!pdev || !info || !info->headers)
return; return;
intel_vsec_walk_header(pdev, info); intel_vsec_walk_header(pdev, info);

View File

@ -23,7 +23,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.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 */ /* IPC defines the following message types */
#define IPCMSG_PCNTRL 0xff /* Power controller unit read/write */ #define IPCMSG_PCNTRL 0xff /* Power controller unit read/write */

View File

@ -18,7 +18,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/intel_scu_ipc.h> #include <linux/platform_data/x86/intel_scu_ipc.h>
static int major; static int major;

View File

@ -11,7 +11,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/pci.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, static int intel_scu_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id) const struct pci_device_id *id)

View File

@ -15,7 +15,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.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) static int intel_scu_platform_probe(struct platform_device *pdev)
{ {

View File

@ -9,13 +9,14 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/platform_data/intel-mid_wdt.h>
#include <asm/cpu_device_id.h> #include <asm/cpu_device_id.h>
#include <asm/intel-family.h> #include <asm/intel-family.h>
#include <asm/io_apic.h> #include <asm/io_apic.h>
#include <asm/hw_irq.h> #include <asm/hw_irq.h>
#include <linux/platform_data/x86/intel-mid_wdt.h>
#define TANGIER_EXT_TIMER0_MSI 12 #define TANGIER_EXT_TIMER0_MSI 12
static struct platform_device wdt_dev = { static struct platform_device wdt_dev = {

View File

@ -43,6 +43,8 @@ struct lenovo_ymc_private {
}; };
static const struct key_entry lenovo_ymc_keymap[] = { static const struct key_entry lenovo_ymc_keymap[] = {
/* Ignore the uninitialized state */
{ KE_IGNORE, 0x00 },
/* Laptop */ /* Laptop */
{ KE_SW, 0x01, { .sw = { SW_TABLET_MODE, 0 } } }, { KE_SW, 0x01, { .sw = { SW_TABLET_MODE, 0 } } },
/* Tablet */ /* Tablet */

View File

@ -8,6 +8,9 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/dev_printk.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/sparse-keymap.h> #include <linux/input/sparse-keymap.h>
@ -31,6 +34,26 @@ MODULE_AUTHOR("Matan Ziv-Av");
MODULE_DESCRIPTION("LG WMI Hotkey Driver"); MODULE_DESCRIPTION("LG WMI Hotkey Driver");
MODULE_LICENSE("GPL"); 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_GUID0 "E4FB94F9-7F2B-4173-AD1A-CD1D95086248"
#define WMI_EVENT_GUID1 "023B133E-49D1-4E10-B313-698220140DC2" #define WMI_EVENT_GUID1 "023B133E-49D1-4E10-B313-698220140DC2"
#define WMI_EVENT_GUID2 "37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6" #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; 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; long data = (long)context;
pr_debug("event guid %li\n", data); 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) if (!obj)
return; return;
@ -218,7 +231,6 @@ static void wmi_notify(u32 value, void *context)
pr_debug("Type: %i Eventcode: 0x%llx\n", obj->type, pr_debug("Type: %i Eventcode: 0x%llx\n", obj->type,
obj->integer.value); obj->integer.value);
kfree(response.pointer);
} }
static void wmi_input_setup(void) 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) static int acpi_add(struct acpi_device *device)
{ {
struct platform_device_info pdev_info = { struct platform_device_info pdev_info = {
@ -653,6 +766,7 @@ static int acpi_add(struct acpi_device *device)
.name = PLATFORM_NAME, .name = PLATFORM_NAME,
.id = PLATFORM_DEVID_NONE, .id = PLATFORM_DEVID_NONE,
}; };
acpi_status status;
int ret; int ret;
const char *product; const char *product;
int year = 2017; int year = 2017;
@ -660,6 +774,17 @@ static int acpi_add(struct acpi_device *device)
if (pf_device) if (pf_device)
return 0; 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); ret = platform_driver_register(&pf_driver);
if (ret) if (ret)
return ret; return ret;

View File

@ -170,20 +170,9 @@ static const struct backlight_ops msi_backlight_ops = {
.update_status = bl_set_status, .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; 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) { if (obj && obj->type == ACPI_TYPE_INTEGER) {
int eventcode = obj->integer.value; int eventcode = obj->integer.value;
@ -192,7 +181,7 @@ static void msi_wmi_notify(u32 value, void *context)
eventcode); eventcode);
if (!key) { if (!key) {
pr_info("Unknown key pressed - %x\n", eventcode); pr_info("Unknown key pressed - %x\n", eventcode);
goto msi_wmi_notify_exit; return;
} }
if (event_wmi->quirk_last_pressed) { 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 - " pr_debug("Suppressed key event 0x%X - "
"Last press was %lld us ago\n", "Last press was %lld us ago\n",
key->code, ktime_to_us(diff)); key->code, ktime_to_us(diff));
goto msi_wmi_notify_exit; return;
} }
last_pressed = cur; last_pressed = cur;
} }
@ -221,9 +210,6 @@ static void msi_wmi_notify(u32 value, void *context)
} }
} else } else
pr_info("Unknown event received\n"); pr_info("Unknown event received\n");
msi_wmi_notify_exit:
kfree(response.pointer);
} }
static int __init msi_wmi_backlight_setup(void) static int __init msi_wmi_backlight_setup(void)

View File

@ -121,6 +121,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/backlight.h> #include <linux/backlight.h>
#include <linux/bits.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/i8042.h> #include <linux/i8042.h>
#include <linux/init.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, 8, { KEY_PROG1 } }, /* Change CPU boost */
{ KE_KEY, 9, { KEY_BATTERY } }, { KE_KEY, 9, { KEY_BATTERY } },
{ KE_KEY, 10, { KEY_SUSPEND } }, { 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 } { KE_END, 0 }
}; };
@ -830,8 +842,8 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
return; return;
} }
key = result & 0xf; key = result & GENMASK(6, 0);
updown = result & 0x80; /* 0x80 == key down; 0x00 = key up */ updown = result & BIT(7); /* 0x80 == key down; 0x00 = key up */
/* hack: some firmware sends no key down for sleep / hibernate */ /* hack: some firmware sends no key down for sleep / hibernate */
if (key == 7 || key == 10) { if (key == 7 || key == 10) {

View File

@ -14,7 +14,6 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/backlight.h> #include <linux/backlight.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/fb.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/rfkill.h> #include <linux/rfkill.h>
@ -554,7 +553,7 @@ static int update_status(struct backlight_device *bd)
set_brightness(samsung, bd->props.brightness); 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); sabi_set_commandb(samsung, commands->set_backlight, 1);
else else
sabi_set_commandb(samsung, commands->set_backlight, 0); 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 = bd;
samsung->backlight_device->props.brightness = read_brightness(samsung); 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); backlight_update_status(samsung->backlight_device);
return 0; return 0;

View File

@ -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) static void smi_devs_unregister(struct smi *smi)
{ {
#if IS_REACHABLE(CONFIG_I2C)
while (smi->i2c_num--) while (smi->i2c_num--)
i2c_unregister_device(smi->i2c_devs[smi->i2c_num]); i2c_unregister_device(smi->i2c_devs[smi->i2c_num]);
#endif
while (smi->spi_num--) if (IS_REACHABLE(CONFIG_SPI)) {
spi_unregister_device(smi->spi_devs[smi->spi_num]); 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) { switch (node->bus_type) {
case SMI_I2C: 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: 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: case SMI_AUTO_DETECT:
/* /*
* For backwards-compatibility with the existing nodes I2C * 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 * SpiSerialBus nodes that were previously ignored, and this
* preserves that behavior. * preserves that behavior.
*/ */
ret = smi_i2c_probe(pdev, smi, node->instances); if (IS_REACHABLE(CONFIG_I2C)) {
if (ret != -ENOENT) ret = smi_i2c_probe(pdev, smi, node->instances);
return ret; if (ret != -ENOENT)
return smi_spi_probe(pdev, smi, node->instances); return ret;
}
if (IS_REACHABLE(CONFIG_SPI))
return smi_spi_probe(pdev, smi, node->instances);
return -ENODEV;
default: default:
return -EINVAL; return -EINVAL;
} }

View File

@ -7749,6 +7749,28 @@ static struct ibm_struct volume_driver_data = {
* EC 0x2f (HFSP) might be available *for reading*, but do not use * EC 0x2f (HFSP) might be available *for reading*, but do not use
* it for writing. * 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: * TPACPI_FAN_WR_TPEC:
* ThinkPad EC register 0x2f (HFSP): fan control loop mode * ThinkPad EC register 0x2f (HFSP): fan control loop mode
* Supported on almost all ThinkPads * Supported on almost all ThinkPads
@ -7882,6 +7904,7 @@ enum { /* Fan control constants */
enum fan_status_access_mode { enum fan_status_access_mode {
TPACPI_FAN_NONE = 0, /* No fan status or control */ TPACPI_FAN_NONE = 0, /* No fan status or control */
TPACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ 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, /* Use ACPI EC regs 0x2f, 0x84-0x85 */
TPACPI_FAN_RD_TPEC_NS, /* Use non-standard ACPI EC regs (eg: L13 Yoga gen2 etc.) */ 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 { enum fan_control_access_mode {
TPACPI_FAN_WR_NONE = 0, /* No fan control */ TPACPI_FAN_WR_NONE = 0, /* No fan control */
TPACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */ 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_TPEC, /* Use ACPI EC reg 0x2f */
TPACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and 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 */ TPACPI_HANDLE(gfan, ec, "GFAN", /* 570 */
"\\FSPD", /* 600e/x, 770e, 770x */ "\\FSPD", /* 600e/x, 770e, 770x */
); /* all others */ ); /* all others */
TPACPI_HANDLE(fang, ec, "FANG", /* E531 */
); /* all others */
TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */
"JFNS", /* 770x-JL */ "JFNS", /* 770x-JL */
); /* all others */ ); /* all others */
TPACPI_HANDLE(fanw, ec, "FANW", /* E531 */
); /* all others */
/* /*
* Unitialized HFSP quirk: ACPI DSDT and EC fail to initialize the * Unitialized HFSP quirk: ACPI DSDT and EC fail to initialize the
@ -8031,6 +8059,23 @@ static int fan_get_status(u8 *status)
break; 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: case TPACPI_FAN_RD_TPEC:
/* all except 570, 600e/x, 770e, 770x */ /* all except 570, 600e/x, 770e, 770x */
if (unlikely(!acpi_ec_read(fan_status_offset, &s))) if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
@ -8145,6 +8190,17 @@ static int fan2_get_speed(unsigned int *speed)
if (speed) if (speed)
*speed = lo ? FAN_RPM_CAL_CONST / lo : 0; *speed = lo ? FAN_RPM_CAL_CONST / lo : 0;
break; 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: default:
return -ENXIO; return -ENXIO;
@ -8204,6 +8260,32 @@ static int fan_set_level(int level)
tp_features.fan_ctrl_status_undef = 0; tp_features.fan_ctrl_status_undef = 0;
break; 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: default:
return -ENXIO; return -ENXIO;
} }
@ -8236,7 +8318,7 @@ static int fan_set_level_safe(int level)
static int fan_set_enable(void) static int fan_set_enable(void)
{ {
u8 s; u8 s = 0;
int rc; int rc;
if (!fan_control_allowed) if (!fan_control_allowed)
@ -8282,6 +8364,19 @@ static int fan_set_enable(void)
rc = 0; rc = 0;
break; 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: default:
rc = -ENXIO; rc = -ENXIO;
} }
@ -8324,6 +8419,22 @@ static int fan_set_disable(void)
fan_control_desired_level = 0; fan_control_desired_level = 0;
break; 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: default:
rc = -ENXIO; rc = -ENXIO;
} }
@ -8357,6 +8468,23 @@ static int fan_set_speed(int speed)
rc = -EINVAL; rc = -EINVAL;
break; 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: default:
rc = -ENXIO; rc = -ENXIO;
} }
@ -8699,6 +8827,10 @@ static int __init fan_init(struct ibm_init_struct *iibm)
TPACPI_ACPIHANDLE_INIT(gfan); TPACPI_ACPIHANDLE_INIT(gfan);
TPACPI_ACPIHANDLE_INIT(sfan); TPACPI_ACPIHANDLE_INIT(sfan);
} }
if (tpacpi_is_lenovo()) {
TPACPI_ACPIHANDLE_INIT(fang);
TPACPI_ACPIHANDLE_INIT(fanw);
}
quirks = tpacpi_check_quirks(fan_quirk_table, quirks = tpacpi_check_quirks(fan_quirk_table,
ARRAY_SIZE(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) { if (gfan_handle) {
/* 570, 600e/x, 770e, 770x */ /* 570, 600e/x, 770e, 770x */
fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN; fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
} else if (fang_handle) {
/* E531 */
fan_status_access_mode = TPACPI_FAN_RD_ACPI_FANG;
} else { } else {
/* all other ThinkPads: note that even old-style /* all other ThinkPads: note that even old-style
* ThinkPad ECs supports the fan control register */ * 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_access_mode = TPACPI_FAN_WR_ACPI_SFAN;
fan_control_commands |= fan_control_commands |=
TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE; 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 { } else {
if (!gfan_handle) { if (!gfan_handle) {
/* gfan without sfan means no fan control */ /* 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_NS:
case TPACPI_FAN_RD_TPEC: case TPACPI_FAN_RD_TPEC:
case TPACPI_FAN_RD_ACPI_FANG:
/* all except 570, 600e/x, 770e, 770x */ /* all except 570, 600e/x, 770e, 770x */
rc = fan_get_status_safe(&status); rc = fan_get_status_safe(&status);
if (rc) if (rc)
@ -8935,7 +9076,7 @@ static int fan_read(struct seq_file *m)
* No other levels settings available * No other levels settings available
*/ */
seq_printf(m, "level:\t\t%s\n", status & FAN_NS_CTRL ? "unknown" : "auto"); 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) if (status & TP_EC_FAN_FULLSPEED)
/* Disengaged mode takes precedence */ /* Disengaged mode takes precedence */
seq_printf(m, "level:\t\tdisengaged\n"); seq_printf(m, "level:\t\tdisengaged\n");

View File

@ -32,26 +32,13 @@ static const struct key_entry toshiba_wmi_keymap[] __initconst = {
{ KE_END, 0 } { 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) if (!obj)
return; return;
/* TODO: Add proper checks once we have data */ /* TODO: Add proper checks once we have data */
pr_debug("Unknown event received, obj type %x\n", obj->type); 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 = { static const struct dmi_system_id toshiba_wmi_dmi_table[] __initconst = {

View File

@ -840,6 +840,21 @@ static const struct ts_dmi_data rwc_nanote_p8_data = {
.properties = rwc_nanote_p8_props, .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[] = { static const struct property_entry schneider_sct101ctm_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1715), PROPERTY_ENTRY_U32("touchscreen-size-x", 1715),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), 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") 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 */ /* Schneider SCT101CTM */
.driver_data = (void *)&schneider_sct101ctm_data, .driver_data = (void *)&schneider_sct101ctm_data,

View File

@ -166,22 +166,6 @@ static inline acpi_object_type get_param_acpi_type(const struct wmi_block *wbloc
return ACPI_TYPE_BUFFER; 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 = &param,
};
return acpi_evaluate_object(wblock->acpi_device->handle, "_WED", &input, out);
}
static int wmidev_match_guid(struct device *dev, const void *data) static int wmidev_match_guid(struct device *dev, const void *data)
{ {
struct wmi_block *wblock = dev_to_wblock(dev); 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; 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 const struct bus_type wmi_bus_type;
static struct wmi_device *wmi_find_device_by_guid(const char *guid_string) 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); 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, &notify_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) static void wmi_device_put(struct wmi_device *wdev)
{ {
put_device(&wdev->dev); put_device(&wdev->dev);
@ -649,35 +605,6 @@ acpi_status wmi_remove_notify_handler(const char *guid)
} }
EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); 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 * wmi_has_guid - Check if a GUID is available
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba * @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) static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj)
{ {
struct acpi_buffer data = { ACPI_ALLOCATE_BUFFER, NULL }; 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 = &param,
};
acpi_status status; acpi_status status;
if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags)) { status = acpi_evaluate_object(wblock->acpi_device->handle, "_WED", &input, &data);
*obj = NULL;
return 0;
}
status = get_event_data(wblock, &data);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
dev_warn(&wblock->dev.dev, "Failed to get event data\n"); dev_warn(&wblock->dev.dev, "Failed to get event data\n");
return -EIO; 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) static int wmi_notify_device(struct device *dev, void *data)
{ {
struct wmi_block *wblock = dev_to_wblock(dev); struct wmi_block *wblock = dev_to_wblock(dev);
union acpi_object *obj; union acpi_object *obj = NULL;
u32 *event = data; u32 *event = data;
int ret; int ret;
if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event)) if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event))
return 0; return 0;
down_read(&wblock->notify_lock); /* The ACPI WMI specification says that _WED should be
/* The WMI driver notify handler conflicts with the legacy WMI handler. * evaluated every time an notification is received, even
* Because of this the WMI driver notify handler takes precedence. * 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); ret = wmi_get_notify_data(wblock, &obj);
if (ret >= 0) { if (ret < 0)
wmi_notify_driver(wblock, obj); return -EIO;
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);
}
} }
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); up_read(&wblock->notify_lock);
kfree(obj);
acpi_bus_generate_netlink_event("wmi", acpi_dev_name(wblock->acpi_device), *event, 0); acpi_bus_generate_netlink_event("wmi", acpi_dev_name(wblock->acpi_device), *event, 0);
return -EBUSY; return -EBUSY;

View File

@ -20,4 +20,4 @@ config X86_ANDROID_TABLETS
are missing from the DSDT. are missing from the DSDT.
If you have a x86 Android tablet say Y or M here, for a generic x86 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.

View File

@ -37,7 +37,7 @@ static const struct x86_gpio_button asus_me176c_tf103c_lid __initconst = {
.pin = 12, .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[] = { static const char * const asus_me176c_accel_mount_matrix[] = {
"-1", "0", "0", "-1", "0", "0",
"0", "1", "0", "0", "1", "0",
@ -112,7 +112,7 @@ static const struct x86_i2c_client_info asus_me176c_i2c_clients[] __initconst =
}, },
.adapter_path = "\\_SB_.I2C5", .adapter_path = "\\_SB_.I2C5",
}, { }, {
/* kxtj21009 accel */ /* kxtj21009 accelerometer */
.board_info = { .board_info = {
.type = "kxtj21009", .type = "kxtj21009",
.addr = 0x0f, .addr = 0x0f,
@ -181,7 +181,7 @@ const struct x86_dev_info asus_me176c_info __initconst = {
.modules = bq24190_modules, .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[] = { static const char * const asus_tf103c_accel_mount_matrix[] = {
"0", "-1", "0", "0", "-1", "0",
"-1", "0", "0", "-1", "0", "0",
@ -280,7 +280,7 @@ static const struct x86_i2c_client_info asus_tf103c_i2c_clients[] __initconst =
}, },
.adapter_path = "\\_SB_.I2C5", .adapter_path = "\\_SB_.I2C5",
}, { }, {
/* kxtj21009 accel */ /* kxtj21009 accelerometer */
.board_info = { .board_info = {
.type = "kxtj21009", .type = "kxtj21009",
.addr = 0x0f, .addr = 0x0f,

View File

@ -26,19 +26,19 @@
static struct platform_device *x86_android_tablet_device; static struct platform_device *x86_android_tablet_device;
/* /*
* This helper allows getting a gpio_desc *before* the actual device consuming * This helper allows getting a GPIO descriptor *before* the actual device
* the GPIO has been instantiated. This function _must_ only be used to handle * consuming it has been instantiated. This function MUST only be used to
* this special case such as e.g. : * handle this special case such as, e.g.:
* *
* 1. Getting an IRQ from a GPIO for i2c_board_info.irq which is passed to * 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 * 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. * platform_data which still uses old style GPIO numbers.
* *
* Since the consuming device has not been instatiated yet a dynamic lookup * Since the consuming device has not been instantiated yet a dynamic lookup
* is generated using the special x86_android_tablet dev for dev_id. * 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, int x86_android_tablet_get_gpiod(const char *chip, int pin, const char *con_id,
bool active_low, enum gpiod_flags dflags, 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 * The DSDT may already reference the GSI in a device skipped by
* acpi_quirk_skip_i2c_client_enumeration(). Unregister the GSI * 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); acpi_unregister_gsi(data->index);
irq = acpi_register_gsi(NULL, data->index, data->trigger, data->polarity); 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); pdevs = kcalloc(dev_info->pdev_count + 1, sizeof(*pdevs), GFP_KERNEL);
if (!pdevs) { if (!pdevs) {
x86_android_tablet_remove(pdev); 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] = dev_info->gpio_button[i].button;
buttons[i].gpio = desc_to_gpio(gpiod); 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); devm_gpiod_put(&x86_android_tablet_device->dev, gpiod);
} }

View File

@ -99,17 +99,17 @@ const struct dmi_system_id x86_android_tablet_ids[] __initconst = {
{ {
/* Lenovo Yoga Book X91F / X91L */ /* Lenovo Yoga Book X91F / X91L */
.matches = { .matches = {
/* Non exact match to match F + L versions */ /* Inexact match to match F + L versions */
DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91"), DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91"),
}, },
.driver_data = (void *)&lenovo_yogabook_x91_info, .driver_data = (void *)&lenovo_yogabook_x91_info,
}, },
{ {
/* /*
* Lenovo Yoga Tablet 2 Pro 1380F/L (13") This has more or less * Lenovo Yoga Tablet 2 Pro 1380F/L (13")
* the same BIOS as the 830F/L or 1050F/L (8" and 10") below, * This has more or less the same BIOS as the 830F/L or 1050F/L
* but unlike the 8" / 10" models which share the same mainboard * (8" and 10") below, but unlike the 8"/10" models which share
* this model has a different mainboard. * the same mainboard this model has a different mainboard.
* This match for the 13" model MUST come before the 8" + 10" * This match for the 13" model MUST come before the 8" + 10"
* match since that one will also match the 13" model! * 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 830F/L or 1050F/L
* Lenovo Yoga Tablet 2 use the same mainboard) * The 8" and 10" Lenovo Yoga Tablet 2 use the same mainboard.
*/ */
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."), 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, .driver_data = (void *)&nextbook_ares8_info,
}, },
{ {
/* Nextbook Ares 8A (CHT version)*/ /* Nextbook Ares 8A (CHT version) */
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"), DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"),

View File

@ -59,7 +59,7 @@ static struct lp855x_platform_data lenovo_lp8557_reg_only_pdata = {
.initial_brightness = 128, .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[] = { static const struct property_entry lenovo_yb1_x90_wacom_props[] = {
PROPERTY_ENTRY_U32("hid-descr-addr", 0x0001), 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, .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 = { static const struct x86_i2c_client_info lenovo_yogabook_x91_i2c_clients[] __initconst = {
{ {
/* BQ27542 fuel-gauge */ /* 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), .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[] = { 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_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1),
PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), 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 * 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 * followed by a normal 3 second press to recover. Avoid this by doing an EFI
* poweroff instead. * poweroff instead.
*/ */
@ -546,7 +546,7 @@ static int __init lenovo_yoga_tab2_830_1050_init(struct device *dev)
if (ret) if (ret)
return 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 = lenovo_yoga_tab2_830_1050_sys_off_handler =
register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1, register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1,
lenovo_yoga_tab2_830_1050_power_off, NULL); 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) if (ret)
return 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 = lenovo_yoga_tab2_830_1050_sys_off_handler =
register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1, register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1,
lenovo_yoga_tab2_830_1050_power_off, NULL); 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, .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[] = { static const struct property_entry lenovo_yt3_bq25892_0_props[] = {
PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lenovo_yt3_bq25892_0_suppliers), PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lenovo_yt3_bq25892_0_suppliers),
PROPERTY_ENTRY_U32("linux,iinlim-percentage", 40), 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 = { 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 = { .board_info = {
.type = "bq27500", .type = "bq27500",
.addr = 0x55, .addr = 0x55,
@ -842,7 +842,7 @@ static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = {
}, },
.adapter_path = "\\_SB_.PCI0.I2C1", .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 = { .board_info = {
.type = "bq25892", .type = "bq25892",
.addr = 0x6b, .addr = 0x6b,
@ -859,7 +859,7 @@ static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = {
.con_id = "bq25892_0_irq", .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 = { .board_info = {
.type = "bq27500", .type = "bq27500",
.addr = 0x55, .addr = 0x55,

View File

@ -20,7 +20,7 @@
#include "shared-psy-info.h" #include "shared-psy-info.h"
#include "x86-android-tablets.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[] = { static const char * const acer_b1_750_mount_matrix[] = {
"-1", "0", "0", "-1", "0", "0",
"0", "1", "0", "0", "1", "0",
@ -98,7 +98,7 @@ const struct x86_dev_info acer_b1_750_info __initconst = {
* Advantech MICA-071 * Advantech MICA-071
* This is a standard Windows tablet, but it has an extra "quick launch" button * This is a standard Windows tablet, but it has an extra "quick launch" button
* which is not described in the ACPI tables in anyway. * 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 = { static const struct x86_gpio_button advantech_mica_071_button __initconst = {
.button = { .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 * 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 * 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. * 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 = { 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, .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[] = { static const char * const medion_lifetab_s10346_accel_mount_matrix[] = {
"0", "1", "0", "0", "1", "0",
"1", "0", "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 = { static const struct x86_i2c_client_info medion_lifetab_s10346_i2c_clients[] __initconst = {
{ {
/* kxtj21009 accel */ /* kxtj21009 accelerometer */
.board_info = { .board_info = {
.type = "kxtj21009", .type = "kxtj21009",
.addr = 0x0f, .addr = 0x0f,
@ -359,7 +359,7 @@ const struct x86_dev_info medion_lifetab_s10346_info __initconst = {
.gpiod_lookup_tables = medion_lifetab_s10346_gpios, .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[] = { static const char * const nextbook_ares8_accel_mount_matrix[] = {
"0", "-1", "0", "0", "-1", "0",
"-1", "0", "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 = { static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst = {
{ {
/* Freescale MMA8653FC accel */ /* Freescale MMA8653FC accelerometer */
.board_info = { .board_info = {
.type = "mma8653", .type = "mma8653",
.addr = 0x1d, .addr = 0x1d,
@ -428,7 +428,7 @@ const struct x86_dev_info nextbook_ares8_info __initconst = {
.gpiod_lookup_tables = nextbook_ares8_gpios, .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[] = { static const char * const nextbook_ares8a_accel_mount_matrix[] = {
"1", "0", "0", "1", "0", "0",
"0", "-1", "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 = { static const struct x86_i2c_client_info nextbook_ares8a_i2c_clients[] __initconst = {
{ {
/* Freescale MMA8653FC accel */ /* Freescale MMA8653FC accelerometer */
.board_info = { .board_info = {
.type = "mma8653", .type = "mma8653",
.addr = 0x1d, .addr = 0x1d,
@ -497,7 +497,7 @@ const struct x86_dev_info nextbook_ares8a_info __initconst = {
* Peaq C1010 * Peaq C1010
* This is a standard Windows tablet, but it has a special Dolby button. * 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 * 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 = { static const struct x86_gpio_button peaq_c1010_button __initconst = {
.button = { .button = {
@ -521,7 +521,7 @@ const struct x86_dev_info peaq_c1010_info __initconst = {
* Whitelabel (sold as various brands) TM800A550L tablets. * Whitelabel (sold as various brands) TM800A550L tablets.
* These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices * These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices
* (removed through acpi_quirk_skip_i2c_client_enumeration()) and * (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[] = { static const char * const whitelabel_tm800a550l_accel_mount_matrix[] = {
"-1", "0", "0", "-1", "0", "0",
@ -566,7 +566,7 @@ static const struct x86_i2c_client_info whitelabel_tm800a550l_i2c_clients[] __in
.polarity = ACPI_ACTIVE_HIGH, .polarity = ACPI_ACTIVE_HIGH,
}, },
}, { }, {
/* kxcj91008 accel */ /* kxcj91008 accelerometer */
.board_info = { .board_info = {
.type = "kxcj91008", .type = "kxcj91008",
.addr = 0x0f, .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 * 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. * "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 = { static const struct software_node ktd2026_node = {
.name = "ktd2026", .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 * the bottom bezel. These are attached to a TPS61158 LED controller which
* is controlled by the "pwm_soc_lpss_2" PWM output. * is controlled by the "pwm_soc_lpss_2" PWM output.
*/ */
#define XIAOMI_MIPAD2_LED_PERIOD_NS 19200 #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; 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 = { struct pwm_state state = {
.period = XIAOMI_MIPAD2_LED_PERIOD_NS, .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 */ /* Always set PWM enabled to avoid the pin floating */
.enabled = true, .enabled = true,
}; };
@ -701,11 +701,11 @@ static int __init xiaomi_mipad2_init(struct device *dev)
return -ENOMEM; return -ENOMEM;
led_cdev->name = "mipad2:white:touch-buttons-backlight"; led_cdev->name = "mipad2:white:touch-buttons-backlight";
led_cdev->max_brightness = XIAOMI_MIPAD2_LED_PERIOD_NS; led_cdev->max_brightness = LED_FULL;
/* "input-events" trigger uses blink_brightness */
led_cdev->blink_brightness = XIAOMI_MIPAD2_LED_DEFAULT_DUTY;
led_cdev->default_trigger = "input-events"; led_cdev->default_trigger = "input-events";
led_cdev->brightness_set_blocking = xiaomi_mipad2_brightness_set; 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); ret = devm_led_classdev_register(dev, led_cdev);
if (ret) if (ret)

View File

@ -39,7 +39,7 @@ const struct software_node fg_bq25890_supply_node = {
.properties = fg_bq25890_supply_props, .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[] = { static const struct property_entry generic_lipo_hv_4v35_battery_props[] = {
PROPERTY_ENTRY_STRING("compatible", "simple-battery"), PROPERTY_ENTRY_STRING("compatible", "simple-battery"),
PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion"), PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion"),
@ -80,7 +80,7 @@ const char * const bq24190_modules[] __initconst = {
NULL 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 = { const struct platform_device_info int3496_pdevs[] __initconst = {
{ {
/* For micro USB ID pin handling */ /* For micro USB ID pin handling */

View File

@ -61,7 +61,7 @@ struct x86_serdev_info {
const char *ctrl_uid; const char *ctrl_uid;
const char *ctrl_devname; 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 * Android x86 tablets DSDTs have usable serdev nodes, but sometimes
* under the wrong controller. So we just tie the existing serdev ACPI * under the wrong controller. So we just tie the existing serdev ACPI
* node to the right controller. * node to the right controller.

View File

@ -8,6 +8,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_data/x86/intel_scu_ipc.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/usb/pd.h> #include <linux/usb/pd.h>
@ -18,8 +19,6 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <asm/intel_scu_ipc.h>
#define PMC_USBC_CMD 0xa7 #define PMC_USBC_CMD 0xa7
/* Response status bits */ /* Response status bits */

View File

@ -20,9 +20,8 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#include <linux/platform_data/intel-mid_wdt.h> #include <linux/platform_data/x86/intel-mid_wdt.h>
#include <linux/platform_data/x86/intel_scu_ipc.h>
#include <asm/intel_scu_ipc.h>
#define IPC_WATCHDOG 0xf8 #define IPC_WATCHDOG 0xf8

View File

@ -387,7 +387,7 @@ extern bool acpi_is_pnp_device(struct acpi_device *);
#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) #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); 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, extern acpi_status wmi_install_notify_handler(const char *guid,
wmi_notify_handler handler, void *data); wmi_notify_handler handler, void *data);
extern acpi_status wmi_remove_notify_handler(const char *guid); 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 bool wmi_has_guid(const char *guid);
extern char *wmi_get_acpi_device_uid(const char *guid); extern char *wmi_get_acpi_device_uid(const char *guid);

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */ /* SPDX-License-Identifier: GPL-2.0 */
#ifndef _VSEC_H #ifndef _INTEL_VSEC_H
#define _VSEC_H #define _INTEL_VSEC_H
#include <linux/auxiliary_bus.h> #include <linux/auxiliary_bus.h>
#include <linux/bits.h> #include <linux/bits.h>
@ -67,15 +67,49 @@ enum intel_vsec_quirks {
VSEC_QUIRK_EARLY_HW = BIT(4), 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 intel_vsec_platform_info {
struct device *parent; struct device *parent;
struct intel_vsec_header **headers; struct intel_vsec_header **headers;
void *priv_data;
unsigned long caps; unsigned long caps;
unsigned long quirks; unsigned long quirks;
u64 base_addr; 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 intel_vsec_device {
struct auxiliary_device auxdev; struct auxiliary_device auxdev;
struct pci_dev *pcidev; 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); return container_of(auxdev, struct intel_vsec_device, auxdev);
} }
#if IS_ENABLED(CONFIG_INTEL_VSEC)
void intel_vsec_register(struct pci_dev *pdev, void intel_vsec_register(struct pci_dev *pdev,
struct intel_vsec_platform_info *info); 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 #endif

View File

@ -4,6 +4,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/dmi.h>
/* WMI Methods */ /* WMI Methods */
#define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */ #define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */
@ -69,6 +70,7 @@
#define ASUS_WMI_DEVID_SCREENPAD_LIGHT 0x00050032 #define ASUS_WMI_DEVID_SCREENPAD_LIGHT 0x00050032
#define ASUS_WMI_DEVID_FAN_BOOST_MODE 0x00110018 #define ASUS_WMI_DEVID_FAN_BOOST_MODE 0x00110018
#define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075 #define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075
#define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO 0x00110019
/* Misc */ /* Misc */
#define ASUS_WMI_DEVID_PANEL_OD 0x00050019 #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 #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 */ #endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */

View File

@ -6,8 +6,8 @@
* Contact: David Cohen <david.a.cohen@linux.intel.com> * Contact: David Cohen <david.a.cohen@linux.intel.com>
*/ */
#ifndef __INTEL_MID_WDT_H__ #ifndef __PLATFORM_X86_INTEL_MID_WDT_H_
#define __INTEL_MID_WDT_H__ #define __PLATFORM_X86_INTEL_MID_WDT_H_
#include <linux/platform_device.h> #include <linux/platform_device.h>
@ -16,4 +16,4 @@ struct intel_mid_wdt_pdata {
int (*probe)(struct platform_device *pdev); int (*probe)(struct platform_device *pdev);
}; };
#endif /*__INTEL_MID_WDT_H__*/ #endif /* __PLATFORM_X86_INTEL_MID_WDT_H_ */

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */ /* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_X86_INTEL_SCU_IPC_H_ #ifndef __PLATFORM_X86_INTEL_SCU_IPC_H_
#define _ASM_X86_INTEL_SCU_IPC_H_ #define __PLATFORM_X86_INTEL_SCU_IPC_H_
#include <linux/ioport.h> #include <linux/ioport.h>

Some files were not shown because too many files have changed in this diff Show More