platform-drivers-x86 for v6.19-1

Highlights:
 
 - acer-wmi: Add PH16-72, PHN16-72, and PT14-51 fan control support
 
 - acpi: platform_profile: Add max-power profile option (power draw limited
                           by the cooling hardware, may exceed battery power
 			  draw limit when on AC power)
 
 - amd/hsmp: Allow >1 data-fabric per socket
 
 - asus-armoury: Add WMI attributes driver to expose miscellaneous
                 WMI functions through fw_attributes (deprecates
                 the custom BIOS features interface through asus-wmi)
 
 - asus-wmi: Use brightness_set_blocking() for kbd led
 
 - ayaneo-ec: Add Ayaneo Embedded Controller driver
 
 - fs/nls:
 
   - Fix utf16 -> utf8 string conversion in output size restricted cases
 
   - Improve error code consistency for utf8 -> utf32 conversions
 
 - ideapad-laptop: Fast (Rapid Charge) charge type support
 
 - intel/hid: Add Dell Pro Rugged 10/12 tablet to VGBS DMI quirks
 
 - intel/pmc:
 
   - Arrow Lake telemetry GUID improvements
 
   - Add support for Wildcat Lake PMC information
 
 - intel_pmc_ipc: Fix ACPI buffer memleak
 
 - intel/punit_ipc: Fix memory corruption
 
 - intel/vsec: Wildcat Lake PMT telemetry support
 
 - lenovo-wmi-gamezone: Map "Extreme" performance mode to max-power
 
 - lg-laptop: Add support for the HDAP opregion field
 
 - serial-multi-instantiate: Add IRQ_RESOURCE_OPT for IRQ missing projects
 
 - thinkpad-t14s-ec: Improve suspend/resume support (lid LEDs, keyboard
                     backlight)
 
 - uniwill: Add Uniwill laptop driver
 
 - wmi: Move under drivers/platform/wmi as non-x86 WMI support is around
        the corner and other WMI features will require adding more C
        files as well
 
 - tools/power/x86/intel-speed-select: v1.24
 
   - Check feature status to check if the feature enablement was successful
 
   - Reset SST-TF bucket structure to display valid bucket info
 
 - Miscellaneous cleanups / refactoring / improvements
 
 The following is an automated shortlog grouped by driver:
 
 acer-wmi:
  -  Add fan control support
  -  Add support for PHN16-72
  -  Enable fan control for PH16-72 and PT14-51
  -  Fix setting of fan behavior
 
 acpi:
  -  platform_profile - Add max-power profile option
 
 Add Uniwill laptop driver:
  - Add Uniwill laptop driver
 
 alienware-wmi-wmax:
  -  Simplify FW profile to pprof matching
 
 amd: hfi:
  -  Remove redundant assignment to .owner
  -  Remove unused cpumask from cpuinfo struct
 
 amd/hsmp:
  -  Replace amd_num_nodes() with topology_max_packages()
 
 amd/pmf:
  -  Add BIOS_INPUTS_MAX macro to replace hardcoded array size
  -  Refactor repetitive BIOS output handling
  -  Rename IPU metrics fields to NPU for consistency
  -  Replace magic table id with METRICS_TABLE_ID
  -  Use devm_mutex_init() for mutex initialization
  -  Use explicit SET_CMD/GET_CMD flags in amd_pmf_send_cmd()
 
 arm64: thinkpad-t14s-ec:
  -  add suspend handler for keyboard backlight
  -  add system PM hooks
 
 asus-armoury:
  -  add apu-mem control support
  -  add panel_hd_mode attribute
  -  add ppt_* and nv_* tuning knobs
  -  add screen auto-brightness toggle
  -  add support for FA507UV
  -  add support for GA503QR
  -  Fix error code in mini_led_mode_current_value_store()
  -  fix mini-led mode show
  -  fix only DC tunables being available
  -  move existing tunings to asus-armoury module
 
 asus-wmi:
  -  deprecate bios features
  -  export symbols used for read/write WMI
  -  rename ASUS_WMI_DEVID_PPT_FPPT
  -  use brightness_set_blocking() for kbd led
 
 ayaneo-ec:
  -  Add Ayaneo Embedded Controller platform driver
  -  Add charge control support
  -  Add controller power and modules attributes
  -  Add hwmon support
  -  Add suspend hook
  -  Move Ayaneo devices from oxpec to ayaneo-ec
 
 Documentation: laptops:
  -  Add documentation for uniwill laptops
 
 fs/nls:
  -  Fix inconsistency between utf8_to_utf32() and utf32_to_utf8()
  -  Fix utf16 to utf8 conversion
 
 hp-wmi:
  -  Order DMI board name arrays
 
 ideapad-laptop: Add charge_types:
  - Fast (Rapid Charge)
 
 ideapad-laptop:
  -  Protect GBMD/SBMC calls with mutex
  -  Support multiple power_supply_ext definitions
  -  Use str_on_off() helper
 
 intel/hid:
  -  Add Dell Pro Rugged 10/12 tablet to VGBS DMI quirks
 
 intel/pmc:
  -  Add DMU GUID to Arrow Lake U/H
  -  Add support for multiple DMU GUIDs
  -  Enable SSRAM support for Wildcat Lake
 
 intel_pmc_ipc:
  -  fix ACPI buffer memory leak
 
 intel/pmc:
  -  Relocate lpm_req_guid to pmc_reg_map
  -  Remove redundant has_die_c6 variable
  -  Rename PMC index variable to pmc_idx
  -  Update Arrow Lake telemetry GUID
 
 intel/vsec:
  -  Add support for Wildcat Lake
 
 lenovo-wmi-gamezone Use max-power rather than balanced-performance:
  - lenovo-wmi-gamezone Use max-power rather than balanced-performance
 
 lg-laptop:
  -  Add support for the HDAP opregion field
 
 serial-multi-instantiate:
  -  Add IRQ_RESOURCE_OPT for IRQ missing projects
 
 surface: acpi-notify:
  -  add WQ_PERCPU to alloc_workqueue users
 
 surface: aggregator:
  -  Omit a variable reassignment in ssam_serial_hub_probe()
 
 surface:
  -  replace use of system_wq with system_percpu_wq
 
 tools/power/x86/intel-speed-select:
  -  Check feature status
  -  Reset isst_turbo_freq_info for invalid buckets
  -  v1.24 release
 
 uniwill:
  -  Add TUXEDO devices
  -  Handle more WMI events required for TUXEDO devices
 
 wmi-gamezone:
  -  Add Legion Go 2 Quirks
 
 wmi:
  -  Move WMI core code into a separate directory
  -  Remove extern keyword from prototypes
  -  Use correct type when populating ACPI objects
 
 x86-android-tablets:
  -  Omit a variable reassignment in lenovo_yoga_tab2_830_1050_init_codec()
 
 x86:
  -  replace use of system_wq with system_percpu_wq
 
 Merges:
  -  Merge branch 'fixes' of into for-next
  -  Merge branch 'intel-sst' of https://github.com/spandruvada/linux-kernel into for-next
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQSCSUwRdwTNL2MhaBlZrE9hU+XOMQUCaTgl5wAKCRBZrE9hU+XO
 McWDAP4nmj5+EeHsg3Kl0kQ1lqF9llWI4EM2NeRd6MwrV5klxgD9FLW6djGkmP2j
 RjtZ4CULbtQ3QMl7kmXhfAjRwJfiSgk=
 =yFvQ
 -----END PGP SIGNATURE-----

Merge tag 'platform-drivers-x86-v6.19-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86

Pull x86 platform driver updates from Ilpo Järvinen:

 - acer-wmi: Add PH16-72, PHN16-72, and PT14-51 fan control support

 - acpi: platform_profile: Add max-power profile option (power draw
   limited by the cooling hardware, may exceed battery power draw limit
   when on AC power)

 - amd/hsmp: Allow more than one data-fabric per socket

 - asus-armoury: Add WMI attributes driver to expose miscellaneous WMI
   functions through fw_attributes (deprecates the custom BIOS features
   interface through asus-wmi)

 - asus-wmi: Use brightness_set_blocking() for kbd led

 - ayaneo-ec: Add Ayaneo Embedded Controller driver

 - fs/nls:
     - Fix utf16 to utf8 string conversion when output size restricted
     - Improve error code consistency for utf8 to utf32 conversions

 - ideapad-laptop: Fast (Rapid Charge) charge type support

 - intel/hid: Add Dell Pro Rugged 10/12 tablet to VGBS DMI quirks

 - intel/pmc:
     - Arrow Lake telemetry GUID improvements
     - Add support for Wildcat Lake PMC information

 - intel_pmc_ipc: Fix ACPI buffer memleak

 - intel/punit_ipc: Fix memory corruption

 - intel/vsec: Wildcat Lake PMT telemetry support

 - lenovo-wmi-gamezone: Map "Extreme" performance mode to max-power

 - lg-laptop: Add support for the HDAP opregion field

 - serial-multi-instantiate: Add IRQ_RESOURCE_OPT for IRQ missing
   projects

 - thinkpad-t14s-ec: Improve suspend/resume support (lid LEDs, keyboard
   backlight)

 - uniwill: Add Uniwill laptop driver

 - wmi: Move under drivers/platform/wmi as non-x86 WMI support is around
   the corner and other WMI features will require adding more C files as
   well

 - tools/power/x86/intel-speed-select: v1.24
     - Check feature status to check if the feature enablement was
       successful
     - Reset SST-TF bucket structure to display valid bucket info

 - Miscellaneous cleanups / refactoring / improvements

* tag 'platform-drivers-x86-v6.19-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (73 commits)
  tools/power/x86/intel-speed-select: v1.24 release
  tools/power/x86/intel-speed-select: Reset isst_turbo_freq_info for invalid buckets
  tools/power/x86/intel-speed-select: Check feature status
  platform/x86: asus-wmi: use brightness_set_blocking() for kbd led
  fs/nls: Fix inconsistency between utf8_to_utf32() and utf32_to_utf8()
  platform/x86: asus-armoury: add support for GA503QR
  platform/x86: intel_pmc_ipc: fix ACPI buffer memory leak
  platform/x86: hp-wmi: Order DMI board name arrays
  platform/x86/intel/hid: Add Dell Pro Rugged 10/12 tablet to VGBS DMI quirks
  platform: surface: replace use of system_wq with system_percpu_wq
  platform: x86: replace use of system_wq with system_percpu_wq
  platform/surface: acpi-notify: add WQ_PERCPU to alloc_workqueue users
  platform/x86: wmi-gamezone: Add Legion Go 2 Quirks
  platform/x86: lenovo-wmi-gamezone Use max-power rather than balanced-performance
  acpi: platform_profile - Add max-power profile option
  platform/x86/amd/pmf: Use devm_mutex_init() for mutex initialization
  platform/x86/amd/pmf: Add BIOS_INPUTS_MAX macro to replace hardcoded array size
  platform/x86: serial-multi-instantiate: Add IRQ_RESOURCE_OPT for IRQ missing projects
  platform/x86/amd/pmf: Refactor repetitive BIOS output handling
  platform/x86/uniwill: Add TUXEDO devices
  ...
master
Linus Torvalds 2025-12-10 06:38:17 +09:00
commit 9d588a1140
70 changed files with 7111 additions and 672 deletions

View File

@ -23,6 +23,8 @@ Description: This file contains a space-separated list of profiles supported
power consumption with a slight bias
towards performance
performance High performance operation
max-power Higher performance operation that may exceed
internal battery draw limits when on AC power
custom Driver defined custom profile
==================== ========================================

View File

@ -0,0 +1,53 @@
What: /sys/bus/platform/devices/INOU0000:XX/fn_lock_toggle_enable
Date: November 2025
KernelVersion: 6.19
Contact: Armin Wolf <W_Armin@gmx.de>
Description:
Allows userspace applications to enable/disable the FN lock feature
of the integrated keyboard by writing "1"/"0" into this file.
Reading this file returns the current enable status of the FN lock functionality.
What: /sys/bus/platform/devices/INOU0000:XX/super_key_toggle_enable
Date: November 2025
KernelVersion: 6.19
Contact: Armin Wolf <W_Armin@gmx.de>
Description:
Allows userspace applications to enable/disable the super key functionality
of the integrated keyboard by writing "1"/"0" into this file.
Reading this file returns the current enable status of the super key functionality.
What: /sys/bus/platform/devices/INOU0000:XX/touchpad_toggle_enable
Date: November 2025
KernelVersion: 6.19
Contact: Armin Wolf <W_Armin@gmx.de>
Description:
Allows userspace applications to enable/disable the touchpad toggle functionality
of the integrated touchpad by writing "1"/"0" into this file.
Reading this file returns the current enable status of the touchpad toggle
functionality.
What: /sys/bus/platform/devices/INOU0000:XX/rainbow_animation
Date: November 2025
KernelVersion: 6.19
Contact: Armin Wolf <W_Armin@gmx.de>
Description:
Forces the integrated lightbar to display a rainbow animation when the machine
is not suspended. Writing "1"/"0" into this file enables/disables this
functionality.
Reading this file returns the current status of the rainbow animation functionality.
What: /sys/bus/platform/devices/INOU0000:XX/breathing_in_suspend
Date: November 2025
KernelVersion: 6.19
Contact: Armin Wolf <W_Armin@gmx.de>
Description:
Causes the integrated lightbar to display a breathing animation when the machine
has been suspended and is running on AC power. Writing "1"/"0" into this file
enables/disables this functionality.
Reading this file returns the current status of the breathing animation
functionality.

View File

@ -63,6 +63,7 @@ Date: Aug 2022
KernelVersion: 6.1
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Switch the GPU hardware MUX mode. Laptops with this feature can
can be toggled to boot with only the dGPU (discrete mode) or in
standard Optimus/Hybrid mode. On switch a reboot is required:
@ -75,6 +76,7 @@ Date: Aug 2022
KernelVersion: 5.17
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Disable discrete GPU:
* 0 - Enable dGPU,
* 1 - Disable dGPU
@ -84,6 +86,7 @@ Date: Aug 2022
KernelVersion: 5.17
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Enable the external GPU paired with ROG X-Flow laptops.
Toggling this setting will also trigger ACPI to disable the dGPU:
@ -95,6 +98,7 @@ Date: Aug 2022
KernelVersion: 5.17
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Enable an LCD response-time boost to reduce or remove ghosting:
* 0 - Disable,
* 1 - Enable
@ -104,6 +108,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Get the current charging mode being used:
* 1 - Barrel connected charger,
* 2 - USB-C charging
@ -114,6 +119,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Show if the egpu (XG Mobile) is correctly connected:
* 0 - False,
* 1 - True
@ -123,6 +129,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Change the mini-LED mode:
* 0 - Single-zone,
* 1 - Multi-zone
@ -133,6 +140,7 @@ Date: Apr 2024
KernelVersion: 6.10
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
List the available mini-led modes.
What: /sys/devices/platform/<platform>/ppt_pl1_spl
@ -140,6 +148,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Set the Package Power Target total of CPU: PL1 on Intel, SPL on AMD.
Shown on Intel+Nvidia or AMD+Nvidia based systems:
@ -150,6 +159,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Set the Slow Package Power Tracking Limit of CPU: PL2 on Intel, SPPT,
on AMD. Shown on Intel+Nvidia or AMD+Nvidia based systems:
@ -160,6 +170,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Set the Fast Package Power Tracking Limit of CPU. AMD+Nvidia only:
* min=5, max=250
@ -168,6 +179,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Set the APU SPPT limit. Shown on full AMD systems only:
* min=5, max=130
@ -176,6 +188,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Set the platform SPPT limit. Shown on full AMD systems only:
* min=5, max=130
@ -184,6 +197,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Set the dynamic boost limit of the Nvidia dGPU:
* min=5, max=25
@ -192,6 +206,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Set the target temperature limit of the Nvidia dGPU:
* min=75, max=87
@ -200,6 +215,7 @@ Date: Apr 2024
KernelVersion: 6.10
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Set if the BIOS POST sound is played on boot.
* 0 - False,
* 1 - True
@ -209,6 +225,7 @@ Date: Apr 2024
KernelVersion: 6.10
Contact: "Luke Jones" <luke@ljones.dev>
Description:
DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury
Set if the MCU can go in to low-power mode on system sleep
* 0 - False,
* 1 - True

View File

@ -0,0 +1,19 @@
What: /sys/devices/platform/ayaneo-ec/controller_power
Date: Nov 2025
KernelVersion: 6.19
Contact: "Antheas Kapenekakis" <lkml@antheas.dev>
Description:
Current controller power state. Allows turning on and off
the controller power (e.g. for power savings). Write 1 to
turn on, 0 to turn off. File is readable and writable.
What: /sys/devices/platform/ayaneo-ec/controller_modules
Date: Nov 2025
KernelVersion: 6.19
Contact: "Antheas Kapenekakis" <lkml@antheas.dev>
Description:
Shows which controller modules are currently connected to
the device. Possible values are "left", "right" and "both".
File is read-only. The Windows software for this device
will only set controller power to 1 if both module sides
are connected (i.e. this file returns "both").

View File

@ -17,3 +17,4 @@ Laptop Drivers
sonypi
thinkpad-acpi
toshiba_haps
uniwill-laptop

View File

@ -0,0 +1,60 @@
.. SPDX-License-Identifier: GPL-2.0+
Uniwill laptop extra features
=============================
On laptops manufactured by Uniwill (either directly or as ODM), the ``uniwill-laptop`` driver
handles various platform-specific features.
Module Loading
--------------
The ``uniwill-laptop`` driver relies on a DMI table to automatically load on supported devices.
When using the ``force`` module parameter, this DMI check will be omitted, allowing the driver
to be loaded on unsupported devices for testing purposes.
Hotkeys
-------
Usually the FN keys work without a special driver. However as soon as the ``uniwill-laptop`` driver
is loaded, the FN keys need to be handled manually. This is done automatically by the driver itself.
Keyboard settings
-----------------
The ``uniwill-laptop`` driver allows the user to enable/disable:
- the FN and super key lock functionality of the integrated keyboard
- the touchpad toggle functionality of the integrated touchpad
See Documentation/ABI/testing/sysfs-driver-uniwill-laptop for details.
Hwmon interface
---------------
The ``uniwill-laptop`` driver supports reading of the CPU and GPU temperature and supports up to
two fans. Userspace applications can access sensor readings over the hwmon sysfs interface.
Platform profile
----------------
Support for changing the platform performance mode is currently not implemented.
Battery Charging Control
------------------------
The ``uniwill-laptop`` driver supports controlling the battery charge limit. This happens over
the standard ``charge_control_end_threshold`` power supply sysfs attribute. All values
between 1 and 100 percent are supported.
Additionally the driver signals the presence of battery charging issues through the standard
``health`` power supply sysfs attribute.
Lightbar
--------
The ``uniwill-laptop`` driver exposes the lightbar found on some models as a standard multicolor
LED class device. The default name of this LED class device is ``uniwill:multicolor:status``.
See Documentation/ABI/testing/sysfs-driver-uniwill-laptop for details on how to control the various
animation modes of the lightbar.

View File

@ -16,5 +16,5 @@ which will be bound to compatible WMI devices by the driver core.
.. kernel-doc:: include/linux/wmi.h
:internal:
.. kernel-doc:: drivers/platform/x86/wmi.c
.. kernel-doc:: drivers/platform/wmi/core.c
:export:

View File

@ -19,27 +19,26 @@ WMI GUID ``887B54E3-DDDC-4B2C-8B88-68A26A8835D0``
The Gamezone Data WMI interface provides platform-profile and fan curve
settings for devices that fall under the "Gaming Series" of Lenovo devices.
It uses a notifier chain to inform other Lenovo WMI interface drivers of the
current platform profile when it changes.
current platform profile when it changes. The currently set profile can be
determined by the user on the hardware by looking at the color of the power
or profile LED, depending on the model.
The following platform profiles are supported:
- low-power
- balanced
- balanced-performance
- performance
- custom
- low-power, blue LED
- balanced, white LED
- performance, red LED
- max-power, purple LED
- custom, purple LED
Balanced-Performance
Extreme Mode
~~~~~~~~~~~~~~~~~~~~
Some newer Lenovo "Gaming Series" laptops have an "Extreme Mode" profile
enabled in their BIOS. For these devices, the performance platform profile
corresponds to the BIOS Extreme Mode, while the balanced-performance
platform profile corresponds to the BIOS Performance mode. For legacy
devices, the performance platform profile will correspond with the BIOS
Performance mode.
enabled in their BIOS. When available, this mode will be represented by the
max-power platform profile.
For some newer devices the "Extreme Mode" profile is incomplete in the BIOS
and setting it will cause undefined behavior. A BIOS bug quirk table is
provided to ensure these devices cannot set "Extreme Mode" from the driver.
For a subset of these devices the "Extreme Mode" profile is incomplete in
the BIOS and setting it will cause undefined behavior. A BIOS bug quirk table
is provided to ensure these devices cannot set "Extreme Mode" from the driver.
Custom Profile
~~~~~~~~~~~~~~

View File

@ -0,0 +1,198 @@
.. SPDX-License-Identifier: GPL-2.0-or-later
========================================
Uniwill Notebook driver (uniwill-laptop)
========================================
Introduction
============
Many notebooks manufactured by Uniwill (either directly or as ODM) provide a EC interface
for controlling various platform settings like sensors and fan control. This interface is
used by the ``uniwill-laptop`` driver to map those features onto standard kernel interfaces.
EC WMI interface description
============================
The EC WMI interface description can be decoded from the embedded binary MOF (bmof)
data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
::
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"),
Description("Class used to operate methods on a ULong"),
guid("{ABBC0F6F-8EA1-11d1-00A0-C90629100000}")]
class AcpiTest_MULong {
[key, read] string InstanceName;
[read] boolean Active;
[WmiMethodId(1), Implemented, read, write, Description("Return the contents of a ULong")]
void GetULong([out, Description("Ulong Data")] uint32 Data);
[WmiMethodId(2), Implemented, read, write, Description("Set the contents of a ULong")]
void SetULong([in, Description("Ulong Data")] uint32 Data);
[WmiMethodId(3), Implemented, read, write,
Description("Generate an event containing ULong data")]
void FireULong([in, Description("WMI requires a parameter")] uint32 Hack);
[WmiMethodId(4), Implemented, read, write, Description("Get and Set the contents of a ULong")]
void GetSetULong([in, Description("Ulong Data")] uint64 Data,
[out, Description("Ulong Data")] uint32 Return);
[WmiMethodId(5), Implemented, read, write,
Description("Get and Set the contents of a ULong for Dollby button")]
void GetButton([in, Description("Ulong Data")] uint64 Data,
[out, Description("Ulong Data")] uint32 Return);
};
Most of the WMI-related code was copied from the Windows driver samples, which unfortunately means
that the WMI-GUID is not unique. This makes the WMI-GUID unusable for autoloading.
WMI method GetULong()
---------------------
This WMI method was copied from the Windows driver samples and has no function.
WMI method SetULong()
---------------------
This WMI method was copied from the Windows driver samples and has no function.
WMI method FireULong()
----------------------
This WMI method allows to inject a WMI event with a 32-bit payload. Its primary purpose seems
to be debugging.
WMI method GetSetULong()
------------------------
This WMI method is used to communicate with the EC. The ``Data`` argument holds the following
information (starting with the least significant byte):
1. 16-bit address
2. 16-bit data (set to ``0x0000`` when reading)
3. 16-bit operation (``0x0100`` for reading and ``0x0000`` for writing)
4. 16-bit reserved (set to ``0x0000``)
The first 8 bits of the ``Return`` value contain the data returned by the EC when reading.
The special value ``0xFEFEFEFE`` is used to indicate a communication failure with the EC.
WMI method GetButton()
----------------------
This WMI method is not implemented on all machines and has an unknown purpose.
Reverse-Engineering the EC WMI interface
========================================
.. warning:: Randomly poking the EC can potentially cause damage to the machine and other unwanted
side effects, please be careful.
The EC behind the ``GetSetULong`` method is used by the OEM software supplied by the manufacturer.
Reverse-engineering of this software is difficult since it uses an obfuscator, however some parts
are not obfuscated. In this case `dnSpy <https://github.com/dnSpy/dnSpy>`_ could also be helpful.
The EC can be accessed under Windows using powershell (requires admin privileges):
::
> $obj = Get-CimInstance -Namespace root/wmi -ClassName AcpiTest_MULong | Select-Object -First 1
> Invoke-CimMethod -InputObject $obj -MethodName GetSetULong -Arguments @{Data = <input>}
WMI event interface description
===============================
The WMI interface description can also be decoded from the embedded binary MOF (bmof)
data:
::
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"),
Description("Class containing event generated ULong data"),
guid("{ABBC0F72-8EA1-11d1-00A0-C90629100000}")]
class AcpiTest_EventULong : WmiEvent {
[key, read] string InstanceName;
[read] boolean Active;
[WmiDataId(1), read, write, Description("ULong Data")] uint32 ULong;
};
Most of the WMI-related code was again copied from the Windows driver samples, causing this WMI
interface to suffer from the same restrictions as the EC WMI interface described above.
WMI event data
--------------
The WMI event data contains a single 32-bit value which is used to indicate various platform events.
Reverse-Engineering the Uniwill WMI event interface
===================================================
The driver logs debug messages when receiving a WMI event. Thus enabling debug messages will be
useful for finding unknown event codes.
EC ACPI interface description
=============================
The ``INOU0000`` ACPI device is a virtual device used to access various hardware registers
available on notebooks manufactured by Uniwill. Reading and writing those registers happens
by calling ACPI control methods. The ``uniwill-laptop`` driver uses this device to communicate
with the EC because the ACPI control methods are faster than the WMI methods described above.
ACPI control methods used for reading registers take a single ACPI integer containing the address
of the register to read and return a ACPI integer containing the data inside said register. ACPI
control methods used for writing registers however take two ACPI integers, with the additional
ACPI integer containing the data to be written into the register. Such ACPI control methods return
nothing.
System memory
-------------
System memory can be accessed with a granularity of either a single byte (``MMRB`` for reading and
``MMWB`` for writing) or four bytes (``MMRD`` for reading and ``MMWD`` for writing). Those ACPI
control methods are unused because they provide no benefit when compared to the native memory
access functions provided by the kernel.
EC RAM
------
The internal RAM of the EC can be accessed with a granularity of a single byte using the ``ECRR``
(read) and ``ECRW`` (write) ACPI control methods, with the maximum register address being ``0xFFF``.
The OEM software waits 6 ms after calling one of those ACPI control methods, likely to avoid
overwhelming the EC when being connected over LPC.
PCI config space
----------------
The PCI config space can be accessed with a granularity of four bytes using the ``PCRD`` (read) and
``PCWD`` (write) ACPI control methods. The exact address format is unknown, and poking random PCI
devices might confuse the PCI subsystem. Because of this those ACPI control methods are not used.
IO ports
--------
IO ports can be accessed with a granularity of four bytes using the ``IORD`` (read) and ``IOWD``
(write) ACPI control methods. Those ACPI control methods are unused because they provide no benefit
when compared to the native IO port access functions provided by the kernel.
CMOS RAM
--------
The CMOS RAM can be accessed with a granularity of a single byte using the ``RCMS`` (read) and
``WCMS`` ACPI control methods. Using those ACPI methods might interfere with the native CMOS RAM
access functions provided by the kernel due to the usage of indexed IO, so they are unused.
Indexed IO
----------
Indexed IO with IO ports with a granularity of a single byte can be performed using the ``RIOP``
(read) and ``WIOP`` (write) ACPI control methods. Those ACPI methods are unused because they
provide no benifit when compared to the native IO port access functions provided by the kernel.
Special thanks go to github user `pobrn` which developed the
`qc71_laptop <https://github.com/pobrn/qc71_laptop>`_ driver on which this driver is partly based.
The same is true for Tuxedo Computers, which developed the
`tuxedo-drivers <https://gitlab.com/tuxedocomputers/development/packages/tuxedo-drivers>`_ package
which also served as a foundation for this driver.

View File

@ -402,7 +402,7 @@ S: Maintained
F: Documentation/ABI/testing/sysfs-bus-wmi
F: Documentation/driver-api/wmi.rst
F: Documentation/wmi/
F: drivers/platform/x86/wmi.c
F: drivers/platform/wmi/
F: include/uapi/linux/wmi.h
ACRN HYPERVISOR SERVICE MODULE
@ -4262,6 +4262,13 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml
F: drivers/pwm/pwm-axi-pwmgen.c
AYANEO PLATFORM EC DRIVER
M: Antheas Kapenekakis <lkml@antheas.dev>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: Documentation/ABI/testing/sysfs-platform-ayaneo
F: drivers/platform/x86/ayaneo-ec.c
AZ6007 DVB DRIVER
M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-media@vger.kernel.org
@ -26789,6 +26796,17 @@ L: linux-scsi@vger.kernel.org
S: Maintained
F: drivers/ufs/host/ufs-renesas.c
UNIWILL LAPTOP DRIVER
M: Armin Wolf <W_Armin@gmx.de>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: Documentation/ABI/testing/sysfs-driver-uniwill-laptop
F: Documentation/admin-guide/laptops/uniwill-laptop.rst
F: Documentation/wmi/devices/uniwill-laptop.rst
F: drivers/platform/x86/uniwill/uniwill-acpi.c
F: drivers/platform/x86/uniwill/uniwill-wmi.c
F: drivers/platform/x86/uniwill/uniwill-wmi.h
UNSORTED BLOCK IMAGES (UBI)
M: Richard Weinberger <richard@nod.at>
R: Zhihao Cheng <chengzhihao1@huawei.com>

View File

@ -37,6 +37,7 @@ static const char * const profile_names[] = {
[PLATFORM_PROFILE_BALANCED] = "balanced",
[PLATFORM_PROFILE_BALANCED_PERFORMANCE] = "balanced-performance",
[PLATFORM_PROFILE_PERFORMANCE] = "performance",
[PLATFORM_PROFILE_MAX_POWER] = "max-power",
[PLATFORM_PROFILE_CUSTOM] = "custom",
};
static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST);
@ -506,7 +507,8 @@ int platform_profile_cycle(void)
if (err)
return err;
if (profile == PLATFORM_PROFILE_CUSTOM ||
if (profile == PLATFORM_PROFILE_MAX_POWER ||
profile == PLATFORM_PROFILE_CUSTOM ||
profile == PLATFORM_PROFILE_LAST)
return -EINVAL;
@ -515,7 +517,8 @@ int platform_profile_cycle(void)
if (err)
return err;
/* never iterate into a custom if all drivers supported it */
/* never iterate into a custom or max power if all drivers supported it */
clear_bit(PLATFORM_PROFILE_MAX_POWER, data.aggregate);
clear_bit(PLATFORM_PROFILE_CUSTOM, data.aggregate);
next = find_next_bit_wrap(data.aggregate,

View File

@ -27,6 +27,7 @@
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/platform_data/x86/asus-wmi.h>
#include <linux/platform_data/x86/asus-wmi-leds-ids.h>
#include <linux/input/mt.h>
#include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */
#include <linux/power_supply.h>

View File

@ -20,3 +20,5 @@ source "drivers/platform/x86/Kconfig"
source "drivers/platform/arm64/Kconfig"
source "drivers/platform/raspberrypi/Kconfig"
source "drivers/platform/wmi/Kconfig"

View File

@ -14,3 +14,4 @@ obj-$(CONFIG_CZNIC_PLATFORMS) += cznic/
obj-$(CONFIG_SURFACE_PLATFORMS) += surface/
obj-$(CONFIG_ARM64_PLATFORM_DEVICES) += arm64/
obj-$(CONFIG_BCM2835_VCHIQ) += raspberrypi/
obj-$(CONFIG_ACPI_WMI) += wmi/

View File

@ -20,19 +20,23 @@
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/pm.h>
#define T14S_EC_CMD_ECRD 0x02
#define T14S_EC_CMD_ECWR 0x03
#define T14S_EC_CMD_EVT 0xf0
#define T14S_EC_REG_LED 0x0c
#define T14S_EC_REG_KBD_BL1 0x0d
#define T14S_EC_REG_KBD_BL2 0xe1
#define T14S_EC_KBD_BL1_MASK GENMASK_U8(7, 6)
#define T14S_EC_KBD_BL2_MASK GENMASK_U8(3, 2)
#define T14S_EC_REG_AUD 0x30
#define T14S_EC_MIC_MUTE_LED BIT(5)
#define T14S_EC_SPK_MUTE_LED BIT(6)
#define T14S_EC_REG_LED 0x0c
#define T14S_EC_REG_KBD_BL1 0x0d
#define T14S_EC_REG_MODERN_STANDBY 0xe0
#define T14S_EC_MODERN_STANDBY_ENTRY BIT(1)
#define T14S_EC_MODERN_STANDBY_EXIT BIT(0)
#define T14S_EC_REG_KBD_BL2 0xe1
#define T14S_EC_KBD_BL1_MASK GENMASK_U8(7, 6)
#define T14S_EC_KBD_BL2_MASK GENMASK_U8(3, 2)
#define T14S_EC_REG_AUD 0x30
#define T14S_EC_MIC_MUTE_LED BIT(5)
#define T14S_EC_SPK_MUTE_LED BIT(6)
#define T14S_EC_EVT_NONE 0x00
#define T14S_EC_EVT_KEY_FN_4 0x13
@ -202,6 +206,14 @@ out:
return ret;
}
static void t14s_ec_write_sequence(struct t14s_ec *ec, u8 reg, u8 val, u8 cnt)
{
int i;
for (i = 0; i < cnt; i++)
regmap_write(ec->regmap, reg, val);
}
static int t14s_led_set_status(struct t14s_ec *ec,
struct t14s_ec_led_classdev *led,
const enum t14s_ec_led_status_t ledstatus)
@ -554,6 +566,7 @@ static int t14s_ec_probe(struct i2c_client *client)
return -ENOMEM;
ec->dev = dev;
i2c_set_clientdata(client, ec);
ec->regmap = devm_regmap_init(dev, &t14s_ec_regmap_bus,
ec, &t14s_ec_regmap_config);
@ -593,6 +606,30 @@ static int t14s_ec_probe(struct i2c_client *client)
return 0;
}
static int t14s_ec_suspend(struct device *dev)
{
struct t14s_ec *ec = dev_get_drvdata(dev);
led_classdev_suspend(&ec->kbd_backlight);
t14s_ec_write_sequence(ec, T14S_EC_REG_MODERN_STANDBY,
T14S_EC_MODERN_STANDBY_ENTRY, 3);
return 0;
}
static int t14s_ec_resume(struct device *dev)
{
struct t14s_ec *ec = dev_get_drvdata(dev);
t14s_ec_write_sequence(ec, T14S_EC_REG_MODERN_STANDBY,
T14S_EC_MODERN_STANDBY_EXIT, 3);
led_classdev_resume(&ec->kbd_backlight);
return 0;
}
static const struct of_device_id t14s_ec_of_match[] = {
{ .compatible = "lenovo,thinkpad-t14s-ec" },
{}
@ -605,10 +642,15 @@ static const struct i2c_device_id t14s_ec_i2c_id_table[] = {
};
MODULE_DEVICE_TABLE(i2c, t14s_ec_i2c_id_table);
static const struct dev_pm_ops t14s_ec_pm_ops = {
SYSTEM_SLEEP_PM_OPS(t14s_ec_suspend, t14s_ec_resume)
};
static struct i2c_driver t14s_ec_i2c_driver = {
.driver = {
.name = "thinkpad-t14s-ec",
.of_match_table = t14s_ec_of_match,
.pm = &t14s_ec_pm_ops,
},
.probe = t14s_ec_probe,
.id_table = t14s_ec_i2c_id_table,

View File

@ -676,7 +676,7 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
status = ssam_serdev_setup(ssh, serdev);
if (status) {
status = dev_err_probe(dev, status, "failed to setup serdev\n");
dev_err_probe(dev, status, "failed to setup serdev\n");
goto err_devinit;
}

View File

@ -671,7 +671,7 @@ static void ssh_ptl_timeout_reaper_mod(struct ssh_ptl *ptl, ktime_t now,
/* Re-adjust / schedule reaper only if it is above resolution delta. */
if (ktime_before(aexp, ptl->rtx_timeout.expires)) {
ptl->rtx_timeout.expires = expires;
mod_delayed_work(system_wq, &ptl->rtx_timeout.reaper, delta);
mod_delayed_work(system_percpu_wq, &ptl->rtx_timeout.reaper, delta);
}
spin_unlock(&ptl->rtx_timeout.lock);

View File

@ -434,7 +434,7 @@ static void ssh_rtl_timeout_reaper_mod(struct ssh_rtl *rtl, ktime_t now,
/* Re-adjust / schedule reaper only if it is above resolution delta. */
if (ktime_before(aexp, rtl->rtx_timeout.expires)) {
rtl->rtx_timeout.expires = expires;
mod_delayed_work(system_wq, &rtl->rtx_timeout.reaper, delta);
mod_delayed_work(system_percpu_wq, &rtl->rtx_timeout.reaper, delta);
}
spin_unlock(&rtl->rtx_timeout.lock);

View File

@ -862,7 +862,7 @@ static int __init san_init(void)
{
int ret;
san_wq = alloc_workqueue("san_wq", 0, 0);
san_wq = alloc_workqueue("san_wq", WQ_PERCPU, 0);
if (!san_wq)
return -ENOMEM;
ret = platform_driver_register(&surface_acpi_notify);

View File

@ -0,0 +1,34 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# ACPI WMI Core
#
menuconfig ACPI_WMI
tristate "ACPI-WMI support"
depends on ACPI && X86
help
This option enables support for the ACPI-WMI driver core.
The ACPI-WMI interface is a proprietary extension of ACPI allowing
the platform firmware to expose WMI (Windows Management Instrumentation)
objects used for managing various aspects of the underlying system.
Mapping between ACPI control methods and WMI objects happens through
special mapper devices (PNP0C14) defined inside the ACPI tables.
Enabling this option is necessary for building the vendor specific
ACPI-WMI client drivers for Acer, Dell an HP machines (among others).
It is safe to enable this option even for machines that do not contain
any ACPI-WMI mapper devices at all.
if ACPI_WMI
config ACPI_WMI_LEGACY_DEVICE_NAMES
bool "Use legacy WMI device naming scheme"
help
Say Y here to force the WMI driver core to use the old WMI device naming
scheme when creating WMI devices. Doing so might be necessary for some
userspace applications but will cause the registration of WMI devices with
the same GUID to fail in some corner cases.
endif # ACPI_WMI

View File

@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Makefile for linux/drivers/platform/wmi
# ACPI WMI core
#
wmi-y := core.o
obj-$(CONFIG_ACPI_WMI) += wmi.o

View File

@ -142,14 +142,6 @@ static inline void get_acpi_method_name(const struct wmi_block *wblock,
buffer[4] = '\0';
}
static inline acpi_object_type get_param_acpi_type(const struct wmi_block *wblock)
{
if (wblock->gblock.flags & ACPI_WMI_STRING)
return ACPI_TYPE_STRING;
else
return ACPI_TYPE_BUFFER;
}
static int wmidev_match_guid(struct device *dev, const void *data)
{
struct wmi_block *wblock = dev_to_wblock(dev);
@ -351,9 +343,16 @@ acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 met
params[0].integer.value = instance;
params[1].type = ACPI_TYPE_INTEGER;
params[1].integer.value = method_id;
params[2].type = get_param_acpi_type(wblock);
params[2].buffer.length = in->length;
params[2].buffer.pointer = in->pointer;
if (wblock->gblock.flags & ACPI_WMI_STRING) {
params[2].type = ACPI_TYPE_STRING;
params[2].string.length = in->length;
params[2].string.pointer = in->pointer;
} else {
params[2].type = ACPI_TYPE_BUFFER;
params[2].buffer.length = in->length;
params[2].buffer.pointer = in->pointer;
}
get_acpi_method_name(wblock, 'M', method);
@ -519,9 +518,16 @@ acpi_status wmidev_block_set(struct wmi_device *wdev, u8 instance, const struct
input.pointer = params;
params[0].type = ACPI_TYPE_INTEGER;
params[0].integer.value = instance;
params[1].type = get_param_acpi_type(wblock);
params[1].buffer.length = in->length;
params[1].buffer.pointer = in->pointer;
if (wblock->gblock.flags & ACPI_WMI_STRING) {
params[1].type = ACPI_TYPE_STRING;
params[1].string.length = in->length;
params[1].string.pointer = in->pointer;
} else {
params[1].type = ACPI_TYPE_BUFFER;
params[1].buffer.length = in->length;
params[1].buffer.pointer = in->pointer;
}
get_acpi_method_name(wblock, 'S', method);

View File

@ -16,36 +16,6 @@ menuconfig X86_PLATFORM_DEVICES
if X86_PLATFORM_DEVICES
config ACPI_WMI
tristate "WMI"
depends on ACPI
help
This driver adds support for the ACPI-WMI (Windows Management
Instrumentation) mapper device (PNP0C14) found on some systems.
ACPI-WMI is a proprietary extension to ACPI to expose parts of the
ACPI firmware to userspace - this is done through various vendor
defined methods and data blocks in a PNP0C14 device, which are then
made available for userspace to call.
The implementation of this in Linux currently only exposes this to
other kernel space drivers.
This driver is a required dependency to build the firmware specific
drivers needed on many machines, including Acer and HP laptops.
It is safe to enable this driver even if your DSDT doesn't define
any ACPI-WMI devices.
config ACPI_WMI_LEGACY_DEVICE_NAMES
bool "Use legacy WMI device naming scheme"
depends on ACPI_WMI
help
Say Y here to force the WMI driver core to use the old WMI device naming
scheme when creating WMI devices. Doing so might be necessary for some
userspace applications but will cause the registration of WMI devices with
the same GUID to fail in some corner cases.
config WMI_BMOF
tristate "WMI embedded Binary MOF driver"
depends on ACPI_WMI
@ -74,6 +44,8 @@ config HUAWEI_WMI
To compile this driver as a module, choose M here: the module
will be called huawei-wmi.
source "drivers/platform/x86/uniwill/Kconfig"
config UV_SYSFS
tristate "Sysfs structure for UV systems"
depends on X86_UV
@ -262,6 +234,18 @@ config ASUS_WIRELESS
If you choose to compile this driver as a module the module will be
called asus-wireless.
config ASUS_ARMOURY
tristate "ASUS Armoury driver"
depends on ASUS_WMI
select FW_ATTR_CLASS
help
Say Y here if you have a WMI aware Asus machine and would like to use the
firmware_attributes API to control various settings typically exposed in
the ASUS Armoury Crate application available on Windows.
To compile this driver as a module, choose M here: the module will
be called asus-armoury.
config ASUS_WMI
tristate "ASUS WMI Driver"
depends on ACPI_WMI
@ -284,6 +268,17 @@ config ASUS_WMI
To compile this driver as a module, choose M here: the module will
be called asus-wmi.
config ASUS_WMI_DEPRECATED_ATTRS
bool "BIOS option support in WMI platform (DEPRECATED)"
depends on ASUS_WMI
default y
help
Say Y to expose the configurable BIOS options through the asus-wmi
driver.
This can be used with or without the asus-armoury driver which
has the same attributes, but more, and better features.
config ASUS_NB_WMI
tristate "Asus Notebook WMI Driver"
depends on ASUS_WMI
@ -316,6 +311,19 @@ config ASUS_TF103C_DOCK
If you have an Asus TF103C tablet say Y or M here, for a generic x86
distro config say M here.
config AYANEO_EC
tristate "Ayaneo EC platform control"
depends on DMI
depends on ACPI_EC
depends on ACPI_BATTERY
depends on HWMON
help
Enables support for the platform EC of Ayaneo devices. This
includes fan control, fan speed, charge limit, magic
module detection, and controller power control.
If you have an Ayaneo device, say Y or M here.
config MERAKI_MX100
tristate "Cisco Meraki MX100 Platform Driver"
depends on GPIOLIB
@ -1031,9 +1039,7 @@ config OXP_EC
help
Enables support for the platform EC of OneXPlayer and AOKZOE
handheld devices. This includes fan speed, fan controls, and
disabling the default TDP behavior of the device. Due to legacy
reasons, this driver also provides hwmon functionality to Ayaneo
devices and the OrangePi Neo.
disabling the default TDP behavior of the device.
source "drivers/platform/x86/tuxedo/Kconfig"

View File

@ -5,7 +5,6 @@
#
# Windows Management Interface
obj-$(CONFIG_ACPI_WMI) += wmi.o
obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
# WMI drivers
@ -33,12 +32,16 @@ obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
# ASUS
obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
obj-$(CONFIG_ASUS_WIRELESS) += asus-wireless.o
obj-$(CONFIG_ASUS_ARMOURY) += asus-armoury.o
obj-$(CONFIG_ASUS_WMI) += asus-wmi.o
obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o
obj-$(CONFIG_ASUS_TF103C_DOCK) += asus-tf103c-dock.o
obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o
obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o
# Ayaneo
obj-$(CONFIG_AYANEO_EC) += ayaneo-ec.o
# Cisco/Meraki
obj-$(CONFIG_MERAKI_MX100) += meraki-mx100.o
@ -110,6 +113,9 @@ obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o
# before toshiba_acpi initializes
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
# Uniwill
obj-y += uniwill/
# Inspur
obj-$(CONFIG_INSPUR_PLATFORM_PROFILE) += inspur_platform_profile.o

View File

@ -12,10 +12,12 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/dmi.h>
#include <linux/fixp-arith.h>
#include <linux/backlight.h>
#include <linux/leds.h>
#include <linux/platform_device.h>
@ -68,10 +70,27 @@ MODULE_LICENSE("GPL");
#define ACER_WMID_SET_GAMING_LED_METHODID 2
#define ACER_WMID_GET_GAMING_LED_METHODID 4
#define ACER_WMID_GET_GAMING_SYS_INFO_METHODID 5
#define ACER_WMID_SET_GAMING_FAN_BEHAVIOR 14
#define ACER_WMID_SET_GAMING_FAN_BEHAVIOR_METHODID 14
#define ACER_WMID_GET_GAMING_FAN_BEHAVIOR_METHODID 15
#define ACER_WMID_SET_GAMING_FAN_SPEED_METHODID 16
#define ACER_WMID_GET_GAMING_FAN_SPEED_METHODID 17
#define ACER_WMID_SET_GAMING_MISC_SETTING_METHODID 22
#define ACER_WMID_GET_GAMING_MISC_SETTING_METHODID 23
#define ACER_GAMING_FAN_BEHAVIOR_CPU BIT(0)
#define ACER_GAMING_FAN_BEHAVIOR_GPU BIT(3)
#define ACER_GAMING_FAN_BEHAVIOR_STATUS_MASK GENMASK_ULL(7, 0)
#define ACER_GAMING_FAN_BEHAVIOR_ID_MASK GENMASK_ULL(15, 0)
#define ACER_GAMING_FAN_BEHAVIOR_SET_CPU_MODE_MASK GENMASK(17, 16)
#define ACER_GAMING_FAN_BEHAVIOR_SET_GPU_MODE_MASK GENMASK(23, 22)
#define ACER_GAMING_FAN_BEHAVIOR_GET_CPU_MODE_MASK GENMASK(9, 8)
#define ACER_GAMING_FAN_BEHAVIOR_GET_GPU_MODE_MASK GENMASK(15, 14)
#define ACER_GAMING_FAN_SPEED_STATUS_MASK GENMASK_ULL(7, 0)
#define ACER_GAMING_FAN_SPEED_ID_MASK GENMASK_ULL(7, 0)
#define ACER_GAMING_FAN_SPEED_VALUE_MASK GENMASK_ULL(15, 8)
#define ACER_GAMING_MISC_SETTING_STATUS_MASK GENMASK_ULL(7, 0)
#define ACER_GAMING_MISC_SETTING_INDEX_MASK GENMASK_ULL(7, 0)
#define ACER_GAMING_MISC_SETTING_VALUE_MASK GENMASK_ULL(15, 8)
@ -122,6 +141,17 @@ enum acer_wmi_predator_v4_sensor_id {
ACER_WMID_SENSOR_GPU_TEMPERATURE = 0x0A,
};
enum acer_wmi_gaming_fan_id {
ACER_WMID_CPU_FAN = 0x01,
ACER_WMID_GPU_FAN = 0x04,
};
enum acer_wmi_gaming_fan_mode {
ACER_WMID_FAN_MODE_AUTO = 0x01,
ACER_WMID_FAN_MODE_TURBO = 0x02,
ACER_WMID_FAN_MODE_CUSTOM = 0x03,
};
enum acer_wmi_predator_v4_oc {
ACER_WMID_OC_NORMAL = 0x0000,
ACER_WMID_OC_TURBO = 0x0002,
@ -279,6 +309,7 @@ struct hotkey_function_type_aa {
#define ACER_CAP_TURBO_FAN BIT(9)
#define ACER_CAP_PLATFORM_PROFILE BIT(10)
#define ACER_CAP_HWMON BIT(11)
#define ACER_CAP_PWM BIT(12)
/*
* Interface type flags
@ -373,6 +404,7 @@ struct quirk_entry {
u8 cpu_fans;
u8 gpu_fans;
u8 predator_v4;
u8 pwm;
};
static struct quirk_entry *quirks;
@ -392,6 +424,9 @@ static void __init set_quirks(void)
if (quirks->predator_v4)
interface->capability |= ACER_CAP_PLATFORM_PROFILE |
ACER_CAP_HWMON;
if (quirks->pwm)
interface->capability |= ACER_CAP_PWM;
}
static int __init dmi_matched(const struct dmi_system_id *dmi)
@ -431,6 +466,7 @@ static struct quirk_entry quirk_acer_predator_ph16_72 = {
.cpu_fans = 1,
.gpu_fans = 1,
.predator_v4 = 1,
.pwm = 1,
};
static struct quirk_entry quirk_acer_predator_pt14_51 = {
@ -438,6 +474,7 @@ static struct quirk_entry quirk_acer_predator_pt14_51 = {
.cpu_fans = 1,
.gpu_fans = 1,
.predator_v4 = 1,
.pwm = 1,
};
static struct quirk_entry quirk_acer_predator_v4 = {
@ -656,6 +693,15 @@ static const struct dmi_system_id acer_quirks[] __initconst = {
},
.driver_data = &quirk_acer_predator_ph16_72,
},
{
.callback = dmi_matched,
.ident = "Acer Predator Helios Neo 16",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Predator PHN16-72"),
},
.driver_data = &quirk_acer_predator_ph16_72,
},
{
.callback = dmi_matched,
.ident = "Acer Predator PH18-71",
@ -1564,9 +1610,6 @@ static acpi_status WMID_gaming_set_u64(u64 value, u32 cap)
case ACER_CAP_TURBO_LED:
method_id = ACER_WMID_SET_GAMING_LED_METHODID;
break;
case ACER_CAP_TURBO_FAN:
method_id = ACER_WMID_SET_GAMING_FAN_BEHAVIOR;
break;
default:
return AE_BAD_PARAMETER;
}
@ -1617,25 +1660,125 @@ static int WMID_gaming_get_sys_info(u32 command, u64 *out)
return 0;
}
static void WMID_gaming_set_fan_mode(u8 fan_mode)
static int WMID_gaming_set_fan_behavior(u16 fan_bitmap, enum acer_wmi_gaming_fan_mode mode)
{
/* fan_mode = 1 is used for auto, fan_mode = 2 used for turbo*/
u64 gpu_fan_config1 = 0, gpu_fan_config2 = 0;
int i;
acpi_status status;
u64 input = 0;
u64 result;
input |= FIELD_PREP(ACER_GAMING_FAN_BEHAVIOR_ID_MASK, fan_bitmap);
if (fan_bitmap & ACER_GAMING_FAN_BEHAVIOR_CPU)
input |= FIELD_PREP(ACER_GAMING_FAN_BEHAVIOR_SET_CPU_MODE_MASK, mode);
if (fan_bitmap & ACER_GAMING_FAN_BEHAVIOR_GPU)
input |= FIELD_PREP(ACER_GAMING_FAN_BEHAVIOR_SET_GPU_MODE_MASK, mode);
status = WMI_gaming_execute_u64(ACER_WMID_SET_GAMING_FAN_BEHAVIOR_METHODID, input,
&result);
if (ACPI_FAILURE(status))
return -EIO;
/* The return status must be zero for the operation to have succeeded */
if (FIELD_GET(ACER_GAMING_FAN_BEHAVIOR_STATUS_MASK, result))
return -EIO;
return 0;
}
static int WMID_gaming_get_fan_behavior(u16 fan_bitmap, enum acer_wmi_gaming_fan_mode *mode)
{
acpi_status status;
u32 input = 0;
u64 result;
int value;
input |= FIELD_PREP(ACER_GAMING_FAN_BEHAVIOR_ID_MASK, fan_bitmap);
status = WMI_gaming_execute_u32_u64(ACER_WMID_GET_GAMING_FAN_BEHAVIOR_METHODID, input,
&result);
if (ACPI_FAILURE(status))
return -EIO;
/* The return status must be zero for the operation to have succeeded */
if (FIELD_GET(ACER_GAMING_FAN_BEHAVIOR_STATUS_MASK, result))
return -EIO;
/* Theoretically multiple fans can be specified, but this is currently unused */
if (fan_bitmap & ACER_GAMING_FAN_BEHAVIOR_CPU)
value = FIELD_GET(ACER_GAMING_FAN_BEHAVIOR_GET_CPU_MODE_MASK, result);
else if (fan_bitmap & ACER_GAMING_FAN_BEHAVIOR_GPU)
value = FIELD_GET(ACER_GAMING_FAN_BEHAVIOR_GET_GPU_MODE_MASK, result);
else
return -EINVAL;
if (value < ACER_WMID_FAN_MODE_AUTO || value > ACER_WMID_FAN_MODE_CUSTOM)
return -ENXIO;
*mode = value;
return 0;
}
static void WMID_gaming_set_fan_mode(enum acer_wmi_gaming_fan_mode mode)
{
u16 fan_bitmap = 0;
if (quirks->cpu_fans > 0)
gpu_fan_config2 |= 1;
for (i = 0; i < (quirks->cpu_fans + quirks->gpu_fans); ++i)
gpu_fan_config2 |= 1 << (i + 1);
for (i = 0; i < quirks->gpu_fans; ++i)
gpu_fan_config2 |= 1 << (i + 3);
if (quirks->cpu_fans > 0)
gpu_fan_config1 |= fan_mode;
for (i = 0; i < (quirks->cpu_fans + quirks->gpu_fans); ++i)
gpu_fan_config1 |= fan_mode << (2 * i + 2);
for (i = 0; i < quirks->gpu_fans; ++i)
gpu_fan_config1 |= fan_mode << (2 * i + 6);
WMID_gaming_set_u64(gpu_fan_config2 | gpu_fan_config1 << 16, ACER_CAP_TURBO_FAN);
fan_bitmap |= ACER_GAMING_FAN_BEHAVIOR_CPU;
if (quirks->gpu_fans > 0)
fan_bitmap |= ACER_GAMING_FAN_BEHAVIOR_GPU;
WMID_gaming_set_fan_behavior(fan_bitmap, mode);
}
static int WMID_gaming_set_gaming_fan_speed(u8 fan, u8 speed)
{
acpi_status status;
u64 input = 0;
u64 result;
if (speed > 100)
return -EINVAL;
input |= FIELD_PREP(ACER_GAMING_FAN_SPEED_ID_MASK, fan);
input |= FIELD_PREP(ACER_GAMING_FAN_SPEED_VALUE_MASK, speed);
status = WMI_gaming_execute_u64(ACER_WMID_SET_GAMING_FAN_SPEED_METHODID, input, &result);
if (ACPI_FAILURE(status))
return -EIO;
switch (FIELD_GET(ACER_GAMING_FAN_SPEED_STATUS_MASK, result)) {
case 0x00:
return 0;
case 0x01:
return -ENODEV;
case 0x02:
return -EINVAL;
default:
return -ENXIO;
}
}
static int WMID_gaming_get_gaming_fan_speed(u8 fan, u8 *speed)
{
acpi_status status;
u32 input = 0;
u64 result;
input |= FIELD_PREP(ACER_GAMING_FAN_SPEED_ID_MASK, fan);
status = WMI_gaming_execute_u32_u64(ACER_WMID_GET_GAMING_FAN_SPEED_METHODID, input,
&result);
if (ACPI_FAILURE(status))
return -EIO;
if (FIELD_GET(ACER_GAMING_FAN_SPEED_STATUS_MASK, result))
return -ENODEV;
*speed = FIELD_GET(ACER_GAMING_FAN_SPEED_VALUE_MASK, result);
return 0;
}
static int WMID_gaming_set_misc_setting(enum acer_wmi_gaming_misc_setting setting, u8 value)
@ -1922,7 +2065,7 @@ static int acer_toggle_turbo(void)
WMID_gaming_set_u64(0x1, ACER_CAP_TURBO_LED);
/* Set FAN mode to auto */
WMID_gaming_set_fan_mode(0x1);
WMID_gaming_set_fan_mode(ACER_WMID_FAN_MODE_AUTO);
/* Set OC to normal */
if (has_cap(ACER_CAP_TURBO_OC)) {
@ -1936,7 +2079,7 @@ static int acer_toggle_turbo(void)
WMID_gaming_set_u64(0x10001, ACER_CAP_TURBO_LED);
/* Set FAN mode to turbo */
WMID_gaming_set_fan_mode(0x2);
WMID_gaming_set_fan_mode(ACER_WMID_FAN_MODE_TURBO);
/* Set OC to turbo mode */
if (has_cap(ACER_CAP_TURBO_OC)) {
@ -2767,6 +2910,16 @@ static const enum acer_wmi_predator_v4_sensor_id acer_wmi_fan_channel_to_sensor_
[1] = ACER_WMID_SENSOR_GPU_FAN_SPEED,
};
static const enum acer_wmi_gaming_fan_id acer_wmi_fan_channel_to_fan_id[] = {
[0] = ACER_WMID_CPU_FAN,
[1] = ACER_WMID_GPU_FAN,
};
static const u16 acer_wmi_fan_channel_to_fan_bitmap[] = {
[0] = ACER_GAMING_FAN_BEHAVIOR_CPU,
[1] = ACER_GAMING_FAN_BEHAVIOR_GPU,
};
static umode_t acer_wmi_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type, u32 attr,
int channel)
@ -2778,6 +2931,11 @@ static umode_t acer_wmi_hwmon_is_visible(const void *data,
case hwmon_temp:
sensor_id = acer_wmi_temp_channel_to_sensor_id[channel];
break;
case hwmon_pwm:
if (!has_cap(ACER_CAP_PWM))
return 0;
fallthrough;
case hwmon_fan:
sensor_id = acer_wmi_fan_channel_to_sensor_id[channel];
break;
@ -2785,8 +2943,12 @@ static umode_t acer_wmi_hwmon_is_visible(const void *data,
return 0;
}
if (*supported_sensors & BIT(sensor_id - 1))
if (*supported_sensors & BIT(sensor_id - 1)) {
if (type == hwmon_pwm)
return 0644;
return 0444;
}
return 0;
}
@ -2795,6 +2957,9 @@ static int acer_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
u64 command = ACER_WMID_CMD_GET_PREDATOR_V4_SENSOR_READING;
enum acer_wmi_gaming_fan_mode mode;
u16 fan_bitmap;
u8 fan, speed;
u64 result;
int ret;
@ -2820,6 +2985,80 @@ static int acer_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
*val = FIELD_GET(ACER_PREDATOR_V4_SENSOR_READING_BIT_MASK, result);
return 0;
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
fan = acer_wmi_fan_channel_to_fan_id[channel];
ret = WMID_gaming_get_gaming_fan_speed(fan, &speed);
if (ret < 0)
return ret;
*val = fixp_linear_interpolate(0, 0, 100, U8_MAX, speed);
return 0;
case hwmon_pwm_enable:
fan_bitmap = acer_wmi_fan_channel_to_fan_bitmap[channel];
ret = WMID_gaming_get_fan_behavior(fan_bitmap, &mode);
if (ret < 0)
return ret;
switch (mode) {
case ACER_WMID_FAN_MODE_AUTO:
*val = 2;
return 0;
case ACER_WMID_FAN_MODE_TURBO:
*val = 0;
return 0;
case ACER_WMID_FAN_MODE_CUSTOM:
*val = 1;
return 0;
default:
return -ENXIO;
}
default:
return -EOPNOTSUPP;
}
default:
return -EOPNOTSUPP;
}
}
static int acer_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
enum acer_wmi_gaming_fan_mode mode;
u16 fan_bitmap;
u8 fan, speed;
switch (type) {
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
fan = acer_wmi_fan_channel_to_fan_id[channel];
speed = fixp_linear_interpolate(0, 0, U8_MAX, 100,
clamp_val(val, 0, U8_MAX));
return WMID_gaming_set_gaming_fan_speed(fan, speed);
case hwmon_pwm_enable:
fan_bitmap = acer_wmi_fan_channel_to_fan_bitmap[channel];
switch (val) {
case 0:
mode = ACER_WMID_FAN_MODE_TURBO;
break;
case 1:
mode = ACER_WMID_FAN_MODE_CUSTOM;
break;
case 2:
mode = ACER_WMID_FAN_MODE_AUTO;
break;
default:
return -EINVAL;
}
return WMID_gaming_set_fan_behavior(fan_bitmap, mode);
default:
return -EOPNOTSUPP;
}
default:
return -EOPNOTSUPP;
}
@ -2835,11 +3074,16 @@ static const struct hwmon_channel_info *const acer_wmi_hwmon_info[] = {
HWMON_F_INPUT,
HWMON_F_INPUT
),
HWMON_CHANNEL_INFO(pwm,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE
),
NULL
};
static const struct hwmon_ops acer_wmi_hwmon_ops = {
.read = acer_wmi_hwmon_read,
.write = acer_wmi_hwmon_write,
.is_visible = acer_wmi_hwmon_is_visible,
};

View File

@ -12,7 +12,6 @@
#include <linux/acpi.h>
#include <linux/cpu.h>
#include <linux/cpumask.h>
#include <linux/debugfs.h>
#include <linux/gfp.h>
#include <linux/init.h>
@ -95,7 +94,6 @@ struct amd_hfi_classes {
* struct amd_hfi_cpuinfo - HFI workload class info per CPU
* @cpu: CPU index
* @apic_id: APIC id of the current CPU
* @cpus: mask of CPUs associated with amd_hfi_cpuinfo
* @class_index: workload class ID index
* @nr_class: max number of workload class supported
* @ipcc_scores: ipcc scores for each class
@ -106,7 +104,6 @@ struct amd_hfi_classes {
struct amd_hfi_cpuinfo {
int cpu;
u32 apic_id;
cpumask_var_t cpus;
s16 class_index;
u8 nr_class;
int *ipcc_scores;
@ -295,11 +292,6 @@ static int amd_hfi_online(unsigned int cpu)
guard(mutex)(&hfi_cpuinfo_lock);
if (!zalloc_cpumask_var(&hfi_info->cpus, GFP_KERNEL))
return -ENOMEM;
cpumask_set_cpu(cpu, hfi_info->cpus);
ret = amd_hfi_set_state(cpu, true);
if (ret)
pr_err("WCT enable failed for CPU %u\n", cpu);
@ -329,8 +321,6 @@ static int amd_hfi_offline(unsigned int cpu)
if (ret)
pr_err("WCT disable failed for CPU %u\n", cpu);
free_cpumask_var(hfi_info->cpus);
return ret;
}
@ -515,7 +505,6 @@ static int amd_hfi_probe(struct platform_device *pdev)
static struct platform_driver amd_hfi_driver = {
.driver = {
.name = AMD_HFI_DRIVER,
.owner = THIS_MODULE,
.pm = &amd_hfi_pm_ops,
.acpi_match_table = ACPI_PTR(amd_hfi_platform_match),
},

View File

@ -22,12 +22,11 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/sysfs.h>
#include <linux/topology.h>
#include <linux/uuid.h>
#include <uapi/asm-generic/errno-base.h>
#include <asm/amd/node.h>
#include "hsmp.h"
#define DRIVER_NAME "hsmp_acpi"
@ -586,9 +585,9 @@ static int hsmp_acpi_probe(struct platform_device *pdev)
return -ENOMEM;
if (!hsmp_pdev->is_probed) {
hsmp_pdev->num_sockets = amd_num_nodes();
if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES) {
dev_err(&pdev->dev, "Wrong number of sockets\n");
hsmp_pdev->num_sockets = topology_max_packages();
if (!hsmp_pdev->num_sockets) {
dev_err(&pdev->dev, "No CPU sockets detected\n");
return -ENODEV;
}

View File

@ -114,14 +114,14 @@ static void amd_pmf_set_automode(struct amd_pmf_dev *dev, int idx,
{
struct power_table_control *pwr_ctrl = &config_store.mode_set[idx].power_control;
amd_pmf_send_cmd(dev, SET_SPL, false, pwr_ctrl->spl, NULL);
amd_pmf_send_cmd(dev, SET_FPPT, false, pwr_ctrl->fppt, NULL);
amd_pmf_send_cmd(dev, SET_SPPT, false, pwr_ctrl->sppt, NULL);
amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pwr_ctrl->sppt_apu_only, NULL);
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pwr_ctrl->stt_min, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
amd_pmf_send_cmd(dev, SET_SPL, SET_CMD, pwr_ctrl->spl, NULL);
amd_pmf_send_cmd(dev, SET_FPPT, SET_CMD, pwr_ctrl->fppt, NULL);
amd_pmf_send_cmd(dev, SET_SPPT, SET_CMD, pwr_ctrl->sppt, NULL);
amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, SET_CMD, pwr_ctrl->sppt_apu_only, NULL);
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, SET_CMD, pwr_ctrl->stt_min, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, SET_CMD,
fixp_q88_fromint(pwr_ctrl->stt_skin_temp[STT_TEMP_APU]), NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, SET_CMD,
fixp_q88_fromint(pwr_ctrl->stt_skin_temp[STT_TEMP_HS2]), NULL);
if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX))

View File

@ -76,14 +76,14 @@ static int amd_pmf_set_cnqf(struct amd_pmf_dev *dev, int src, int idx,
pc = &config_store.mode_set[src][idx].power_control;
amd_pmf_send_cmd(dev, SET_SPL, false, pc->spl, NULL);
amd_pmf_send_cmd(dev, SET_FPPT, false, pc->fppt, NULL);
amd_pmf_send_cmd(dev, SET_SPPT, false, pc->sppt, NULL);
amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pc->sppt_apu_only, NULL);
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pc->stt_min, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
amd_pmf_send_cmd(dev, SET_SPL, SET_CMD, pc->spl, NULL);
amd_pmf_send_cmd(dev, SET_FPPT, SET_CMD, pc->fppt, NULL);
amd_pmf_send_cmd(dev, SET_SPPT, SET_CMD, pc->sppt, NULL);
amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, SET_CMD, pc->sppt_apu_only, NULL);
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, SET_CMD, pc->stt_min, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, SET_CMD,
fixp_q88_fromint(pc->stt_skin_temp[STT_TEMP_APU]), NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, SET_CMD,
fixp_q88_fromint(pc->stt_skin_temp[STT_TEMP_HS2]), NULL);
if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX))

View File

@ -131,7 +131,7 @@ static void amd_pmf_get_metrics(struct work_struct *work)
/* Transfer table contents */
memset(dev->buf, 0, sizeof(dev->m_table));
amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL);
memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table));
time_elapsed_ms = ktime_to_ms(ktime_get()) - dev->start_time;
@ -289,8 +289,8 @@ int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer)
hi = phys_addr >> 32;
low = phys_addr & GENMASK(31, 0);
amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, 0, hi, NULL);
amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, 0, low, NULL);
amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, SET_CMD, hi, NULL);
amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, SET_CMD, low, NULL);
return 0;
}
@ -465,9 +465,17 @@ static int amd_pmf_probe(struct platform_device *pdev)
if (!dev->regbase)
return -ENOMEM;
mutex_init(&dev->lock);
mutex_init(&dev->update_mutex);
mutex_init(&dev->cb_mutex);
err = devm_mutex_init(dev->dev, &dev->lock);
if (err)
return err;
err = devm_mutex_init(dev->dev, &dev->update_mutex);
if (err)
return err;
err = devm_mutex_init(dev->dev, &dev->cb_mutex);
if (err)
return err;
apmf_acpi_init(dev);
platform_set_drvdata(pdev, dev);
@ -491,9 +499,6 @@ static void amd_pmf_remove(struct platform_device *pdev)
amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_UNLOAD);
apmf_acpi_deinit(dev);
amd_pmf_dbgfs_unregister(dev);
mutex_destroy(&dev->lock);
mutex_destroy(&dev->update_mutex);
mutex_destroy(&dev->cb_mutex);
}
static const struct attribute_group *amd_pmf_driver_groups[] = {

View File

@ -119,6 +119,13 @@ struct cookie_header {
#define APTS_MAX_STATES 16
#define CUSTOM_BIOS_INPUT_BITS GENMASK(16, 7)
#define BIOS_INPUTS_MAX 10
/* amd_pmf_send_cmd() set/get */
#define SET_CMD false
#define GET_CMD true
#define METRICS_TABLE_ID 7
typedef void (*apmf_event_handler_t)(acpi_handle handle, u32 event, void *data);
@ -204,7 +211,7 @@ struct apmf_sbios_req_v1 {
u8 skin_temp_apu;
u8 skin_temp_hs2;
u8 enable_cnqf;
u32 custom_policy[10];
u32 custom_policy[BIOS_INPUTS_MAX];
} __packed;
struct apmf_sbios_req_v2 {
@ -216,7 +223,7 @@ struct apmf_sbios_req_v2 {
u32 stt_min_limit;
u8 skin_temp_apu;
u8 skin_temp_hs2;
u32 custom_policy[10];
u32 custom_policy[BIOS_INPUTS_MAX];
} __packed;
struct apmf_fan_idx {
@ -243,12 +250,12 @@ struct smu_pmf_metrics_v2 {
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 npuclk_freq; /* MHz */
u16 npu_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 */
u16 npu_power; /* mW */
u32 apu_power; /* mW */
u32 gfx_power; /* mW */
u32 dgpu_power; /* mW */
@ -257,9 +264,9 @@ struct smu_pmf_metrics_v2 {
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 */
u16 mpnpuclk_freq; /* MHz */
u16 npu_reads; /* MB/sec */
u16 npu_writes; /* MB/sec */
u32 throttle_residency_prochot;
u32 throttle_residency_spl;
u32 throttle_residency_fppt;
@ -355,7 +362,7 @@ enum power_modes_v2 {
};
struct pmf_bios_inputs_prev {
u32 custom_bios_inputs[10];
u32 custom_bios_inputs[BIOS_INPUTS_MAX];
};
struct amd_pmf_dev {
@ -451,7 +458,7 @@ struct os_power_slider {
struct amd_pmf_notify_smart_pc_update {
u16 size;
u32 pending_req;
u32 custom_bios[10];
u32 custom_bios[BIOS_INPUTS_MAX];
} __packed;
struct fan_table_control {

View File

@ -202,7 +202,7 @@ static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_ta
{
/* Get the updated metrics table data */
memset(dev->buf, 0, dev->mtable_size);
amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL);
switch (dev->cpu_id) {
case AMD_CPU_ID_PS:

View File

@ -192,15 +192,15 @@ static void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev)
static void amd_pmf_update_slider_v2(struct amd_pmf_dev *dev, int idx)
{
amd_pmf_send_cmd(dev, SET_PMF_PPT, false, apts_config_store.val[idx].pmf_ppt, NULL);
amd_pmf_send_cmd(dev, SET_PMF_PPT_APU_ONLY, false,
amd_pmf_send_cmd(dev, SET_PMF_PPT, SET_CMD, apts_config_store.val[idx].pmf_ppt, NULL);
amd_pmf_send_cmd(dev, SET_PMF_PPT_APU_ONLY, SET_CMD,
apts_config_store.val[idx].ppt_pmf_apu_only, NULL);
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, SET_CMD,
apts_config_store.val[idx].stt_min_limit, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, SET_CMD,
fixp_q88_fromint(apts_config_store.val[idx].stt_skin_temp_limit_apu),
NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, SET_CMD,
fixp_q88_fromint(apts_config_store.val[idx].stt_skin_temp_limit_hs2),
NULL);
}
@ -211,30 +211,30 @@ void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
int src = amd_pmf_get_power_source();
if (op == SLIDER_OP_SET) {
amd_pmf_send_cmd(dev, SET_SPL, false, config_store.prop[src][idx].spl, NULL);
amd_pmf_send_cmd(dev, SET_FPPT, false, config_store.prop[src][idx].fppt, NULL);
amd_pmf_send_cmd(dev, SET_SPPT, false, config_store.prop[src][idx].sppt, NULL);
amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false,
amd_pmf_send_cmd(dev, SET_SPL, SET_CMD, config_store.prop[src][idx].spl, NULL);
amd_pmf_send_cmd(dev, SET_FPPT, SET_CMD, config_store.prop[src][idx].fppt, NULL);
amd_pmf_send_cmd(dev, SET_SPPT, SET_CMD, config_store.prop[src][idx].sppt, NULL);
amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, SET_CMD,
config_store.prop[src][idx].sppt_apu_only, NULL);
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, SET_CMD,
config_store.prop[src][idx].stt_min, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, SET_CMD,
fixp_q88_fromint(config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU]),
NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, SET_CMD,
fixp_q88_fromint(config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2]),
NULL);
} else if (op == SLIDER_OP_GET) {
amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE, &table->prop[src][idx].spl);
amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE, &table->prop[src][idx].fppt);
amd_pmf_send_cmd(dev, GET_SPPT, true, ARG_NONE, &table->prop[src][idx].sppt);
amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, true, ARG_NONE,
amd_pmf_send_cmd(dev, GET_SPL, GET_CMD, ARG_NONE, &table->prop[src][idx].spl);
amd_pmf_send_cmd(dev, GET_FPPT, GET_CMD, ARG_NONE, &table->prop[src][idx].fppt);
amd_pmf_send_cmd(dev, GET_SPPT, GET_CMD, ARG_NONE, &table->prop[src][idx].sppt);
amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, GET_CMD, ARG_NONE,
&table->prop[src][idx].sppt_apu_only);
amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, true, ARG_NONE,
amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, GET_CMD, ARG_NONE,
&table->prop[src][idx].stt_min);
amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, true, ARG_NONE,
amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, GET_CMD, ARG_NONE,
(u32 *)&table->prop[src][idx].stt_skin_temp[STT_TEMP_APU]);
amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, true, ARG_NONE,
amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, GET_CMD, ARG_NONE,
(u32 *)&table->prop[src][idx].stt_skin_temp[STT_TEMP_HS2]);
}
}

View File

@ -73,17 +73,56 @@ static void amd_pmf_update_uevents(struct amd_pmf_dev *dev, u16 event)
input_sync(dev->pmf_idev);
}
static int amd_pmf_get_bios_output_idx(u32 action_idx)
{
switch (action_idx) {
case PMF_POLICY_BIOS_OUTPUT_1:
return 0;
case PMF_POLICY_BIOS_OUTPUT_2:
return 1;
case PMF_POLICY_BIOS_OUTPUT_3:
return 2;
case PMF_POLICY_BIOS_OUTPUT_4:
return 3;
case PMF_POLICY_BIOS_OUTPUT_5:
return 4;
case PMF_POLICY_BIOS_OUTPUT_6:
return 5;
case PMF_POLICY_BIOS_OUTPUT_7:
return 6;
case PMF_POLICY_BIOS_OUTPUT_8:
return 7;
case PMF_POLICY_BIOS_OUTPUT_9:
return 8;
case PMF_POLICY_BIOS_OUTPUT_10:
return 9;
default:
return -EINVAL;
}
}
static void amd_pmf_update_bios_output(struct amd_pmf_dev *pdev, struct ta_pmf_action *action)
{
u32 bios_idx;
bios_idx = amd_pmf_get_bios_output_idx(action->action_index);
amd_pmf_smartpc_apply_bios_output(pdev, action->value, BIT(bios_idx), bios_idx);
}
static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_result *out)
{
struct ta_pmf_action *action;
u32 val;
int idx;
for (idx = 0; idx < out->actions_count; idx++) {
val = out->actions_list[idx].value;
switch (out->actions_list[idx].action_index) {
action = &out->actions_list[idx];
val = action->value;
switch (action->action_index) {
case PMF_POLICY_SPL:
if (dev->prev_data->spl != val) {
amd_pmf_send_cmd(dev, SET_SPL, false, val, NULL);
amd_pmf_send_cmd(dev, SET_SPL, SET_CMD, val, NULL);
dev_dbg(dev->dev, "update SPL: %u\n", val);
dev->prev_data->spl = val;
}
@ -91,7 +130,7 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
case PMF_POLICY_SPPT:
if (dev->prev_data->sppt != val) {
amd_pmf_send_cmd(dev, SET_SPPT, false, val, NULL);
amd_pmf_send_cmd(dev, SET_SPPT, SET_CMD, val, NULL);
dev_dbg(dev->dev, "update SPPT: %u\n", val);
dev->prev_data->sppt = val;
}
@ -99,7 +138,7 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
case PMF_POLICY_FPPT:
if (dev->prev_data->fppt != val) {
amd_pmf_send_cmd(dev, SET_FPPT, false, val, NULL);
amd_pmf_send_cmd(dev, SET_FPPT, SET_CMD, val, NULL);
dev_dbg(dev->dev, "update FPPT: %u\n", val);
dev->prev_data->fppt = val;
}
@ -107,7 +146,7 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
case PMF_POLICY_SPPT_APU_ONLY:
if (dev->prev_data->sppt_apuonly != val) {
amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, val, NULL);
amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, SET_CMD, val, NULL);
dev_dbg(dev->dev, "update SPPT_APU_ONLY: %u\n", val);
dev->prev_data->sppt_apuonly = val;
}
@ -115,7 +154,7 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
case PMF_POLICY_STT_MIN:
if (dev->prev_data->stt_minlimit != val) {
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, val, NULL);
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, SET_CMD, val, NULL);
dev_dbg(dev->dev, "update STT_MIN: %u\n", val);
dev->prev_data->stt_minlimit = val;
}
@ -123,7 +162,7 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
case PMF_POLICY_STT_SKINTEMP_APU:
if (dev->prev_data->stt_skintemp_apu != val) {
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, SET_CMD,
fixp_q88_fromint(val), NULL);
dev_dbg(dev->dev, "update STT_SKINTEMP_APU: %u\n", val);
dev->prev_data->stt_skintemp_apu = val;
@ -132,7 +171,7 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
case PMF_POLICY_STT_SKINTEMP_HS2:
if (dev->prev_data->stt_skintemp_hs2 != val) {
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, SET_CMD,
fixp_q88_fromint(val), NULL);
dev_dbg(dev->dev, "update STT_SKINTEMP_HS2: %u\n", val);
dev->prev_data->stt_skintemp_hs2 = val;
@ -141,7 +180,7 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
case PMF_POLICY_P3T:
if (dev->prev_data->p3t_limit != val) {
amd_pmf_send_cmd(dev, SET_P3T, false, val, NULL);
amd_pmf_send_cmd(dev, SET_P3T, SET_CMD, val, NULL);
dev_dbg(dev->dev, "update P3T: %u\n", val);
dev->prev_data->p3t_limit = val;
}
@ -149,7 +188,7 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
case PMF_POLICY_PMF_PPT:
if (dev->prev_data->pmf_ppt != val) {
amd_pmf_send_cmd(dev, SET_PMF_PPT, false, val, NULL);
amd_pmf_send_cmd(dev, SET_PMF_PPT, SET_CMD, val, NULL);
dev_dbg(dev->dev, "update PMF PPT: %u\n", val);
dev->prev_data->pmf_ppt = val;
}
@ -157,7 +196,7 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
case PMF_POLICY_PMF_PPT_APU_ONLY:
if (dev->prev_data->pmf_ppt_apu_only != val) {
amd_pmf_send_cmd(dev, SET_PMF_PPT_APU_ONLY, false, val, NULL);
amd_pmf_send_cmd(dev, SET_PMF_PPT_APU_ONLY, SET_CMD, val, NULL);
dev_dbg(dev->dev, "update PMF PPT APU ONLY: %u\n", val);
dev->prev_data->pmf_ppt_apu_only = val;
}
@ -183,43 +222,16 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
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);
amd_pmf_update_bios_output(dev, action);
break;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@
#include <linux/acpi.h>
#include <linux/backlight.h>
#include <linux/bits.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/dmi.h>
@ -30,6 +31,7 @@
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <linux/platform_data/x86/asus-wmi.h>
#include <linux/platform_data/x86/asus-wmi-leds-ids.h>
#include <linux/platform_device.h>
#include <linux/platform_profile.h>
#include <linux/power_supply.h>
@ -55,8 +57,6 @@ module_param(fnlock_default, bool, 0444);
#define to_asus_wmi_driver(pdrv) \
(container_of((pdrv), struct asus_wmi_driver, platform_driver))
#define ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
#define NOTIFY_BRNUP_MIN 0x11
#define NOTIFY_BRNUP_MAX 0x1f
#define NOTIFY_BRNDOWN_MIN 0x20
@ -105,8 +105,6 @@ module_param(fnlock_default, bool, 0444);
#define USB_INTEL_XUSB2PR 0xD0
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
#define ASUS_ACPI_UID_ASUSWMI "ASUSWMI"
#define WMI_EVENT_MASK 0xFFFF
#define FAN_CURVE_POINTS 8
@ -340,6 +338,13 @@ struct asus_wmi {
/* Global to allow setting externally without requiring driver data */
static enum asus_ally_mcu_hack use_ally_mcu_hack = ASUS_WMI_ALLY_MCU_HACK_INIT;
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
static void asus_wmi_show_deprecated(void)
{
pr_notice_once("Accessing attributes through /sys/bus/platform/asus_wmi is deprecated and will be removed in a future release. Please switch over to /sys/class/firmware_attributes.\n");
}
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
/* WMI ************************************************************************/
static int asus_wmi_evaluate_method3(u32 method_id,
@ -390,7 +395,7 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
{
return asus_wmi_evaluate_method3(method_id, arg0, arg1, 0, retval);
}
EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method);
EXPORT_SYMBOL_NS_GPL(asus_wmi_evaluate_method, "ASUS_WMI");
static int asus_wmi_evaluate_method5(u32 method_id,
u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 *retval)
@ -554,12 +559,52 @@ static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
return 0;
}
int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
u32 *retval)
/**
* asus_wmi_get_devstate_dsts() - Get the WMI function state.
* @dev_id: The WMI method ID to call.
* @retval: A pointer to where to store the value returned from WMI.
*
* Returns:
* * %-ENODEV - method ID is unsupported.
* * %0 - successful and retval is filled.
* * %other - error from WMI call.
*/
int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval)
{
int err;
err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, dev_id, 0, retval);
if (err)
return err;
if ((*retval & ASUS_WMI_DSTS_PRESENCE_BIT) == 0x00)
return -ENODEV;
return 0;
}
EXPORT_SYMBOL_NS_GPL(asus_wmi_get_devstate_dsts, "ASUS_WMI");
/**
* asus_wmi_set_devstate() - Set the WMI function state.
*
* Note: an asus_wmi_set_devstate() call must be paired with a
* asus_wmi_get_devstate_dsts() to check if the WMI function is supported.
*
* @dev_id: The WMI function to call.
* @ctrl_param: The argument to be used for this WMI function.
* @retval: A pointer to where to store the value returned from WMI.
*
* Returns:
* * %-ENODEV - method ID is unsupported.
* * %0 - successful and retval is filled.
* * %other - error from WMI call.
*/
int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval)
{
return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id,
ctrl_param, retval);
}
EXPORT_SYMBOL_NS_GPL(asus_wmi_set_devstate, "ASUS_WMI");
/* Helper for special devices with magic return codes */
static int asus_wmi_get_devstate_bits(struct asus_wmi *asus,
@ -692,6 +737,7 @@ static void asus_wmi_tablet_mode_get_state(struct asus_wmi *asus)
}
/* Charging mode, 1=Barrel, 2=USB ******************************************/
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
static ssize_t charge_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -702,12 +748,16 @@ static ssize_t charge_mode_show(struct device *dev,
if (result < 0)
return result;
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%d\n", value & 0xff);
}
static DEVICE_ATTR_RO(charge_mode);
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
/* dGPU ********************************************************************/
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
static ssize_t dgpu_disable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -718,6 +768,8 @@ static ssize_t dgpu_disable_show(struct device *dev,
if (result < 0)
return result;
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%d\n", result);
}
@ -771,8 +823,10 @@ static ssize_t dgpu_disable_store(struct device *dev,
return count;
}
static DEVICE_ATTR_RW(dgpu_disable);
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
/* eGPU ********************************************************************/
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
static ssize_t egpu_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -783,6 +837,8 @@ static ssize_t egpu_enable_show(struct device *dev,
if (result < 0)
return result;
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%d\n", result);
}
@ -839,8 +895,10 @@ static ssize_t egpu_enable_store(struct device *dev,
return count;
}
static DEVICE_ATTR_RW(egpu_enable);
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
/* Is eGPU connected? *********************************************************/
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
static ssize_t egpu_connected_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -851,12 +909,16 @@ static ssize_t egpu_connected_show(struct device *dev,
if (result < 0)
return result;
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%d\n", result);
}
static DEVICE_ATTR_RO(egpu_connected);
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
/* gpu mux switch *************************************************************/
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
static ssize_t gpu_mux_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -867,6 +929,8 @@ static ssize_t gpu_mux_mode_show(struct device *dev,
if (result < 0)
return result;
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%d\n", result);
}
@ -925,6 +989,7 @@ static ssize_t gpu_mux_mode_store(struct device *dev,
return count;
}
static DEVICE_ATTR_RW(gpu_mux_mode);
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
/* TUF Laptop Keyboard RGB Modes **********************************************/
static ssize_t kbd_rgb_mode_store(struct device *dev,
@ -1048,6 +1113,7 @@ static const struct attribute_group *kbd_rgb_mode_groups[] = {
};
/* Tunable: PPT: Intel=PL1, AMD=SPPT *****************************************/
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
static ssize_t ppt_pl2_sppt_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@ -1086,6 +1152,8 @@ static ssize_t ppt_pl2_sppt_show(struct device *dev,
{
struct asus_wmi *asus = dev_get_drvdata(dev);
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%u\n", asus->ppt_pl2_sppt);
}
static DEVICE_ATTR_RW(ppt_pl2_sppt);
@ -1128,6 +1196,8 @@ static ssize_t ppt_pl1_spl_show(struct device *dev,
{
struct asus_wmi *asus = dev_get_drvdata(dev);
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%u\n", asus->ppt_pl1_spl);
}
static DEVICE_ATTR_RW(ppt_pl1_spl);
@ -1148,7 +1218,7 @@ static ssize_t ppt_fppt_store(struct device *dev,
if (value < PPT_TOTAL_MIN || value > PPT_TOTAL_MAX)
return -EINVAL;
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_FPPT, value, &result);
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_PL3_FPPT, value, &result);
if (err) {
pr_warn("Failed to set ppt_fppt: %d\n", err);
return err;
@ -1171,6 +1241,8 @@ static ssize_t ppt_fppt_show(struct device *dev,
{
struct asus_wmi *asus = dev_get_drvdata(dev);
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%u\n", asus->ppt_fppt);
}
static DEVICE_ATTR_RW(ppt_fppt);
@ -1214,6 +1286,8 @@ static ssize_t ppt_apu_sppt_show(struct device *dev,
{
struct asus_wmi *asus = dev_get_drvdata(dev);
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%u\n", asus->ppt_apu_sppt);
}
static DEVICE_ATTR_RW(ppt_apu_sppt);
@ -1257,6 +1331,8 @@ static ssize_t ppt_platform_sppt_show(struct device *dev,
{
struct asus_wmi *asus = dev_get_drvdata(dev);
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%u\n", asus->ppt_platform_sppt);
}
static DEVICE_ATTR_RW(ppt_platform_sppt);
@ -1300,6 +1376,8 @@ static ssize_t nv_dynamic_boost_show(struct device *dev,
{
struct asus_wmi *asus = dev_get_drvdata(dev);
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%u\n", asus->nv_dynamic_boost);
}
static DEVICE_ATTR_RW(nv_dynamic_boost);
@ -1343,9 +1421,12 @@ static ssize_t nv_temp_target_show(struct device *dev,
{
struct asus_wmi *asus = dev_get_drvdata(dev);
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%u\n", asus->nv_temp_target);
}
static DEVICE_ATTR_RW(nv_temp_target);
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
/* Ally MCU Powersave ********************************************************/
@ -1386,6 +1467,7 @@ void set_ally_mcu_powersave(bool enabled)
}
EXPORT_SYMBOL_NS_GPL(set_ally_mcu_powersave, "ASUS_WMI");
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
static ssize_t mcu_powersave_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -1396,6 +1478,8 @@ static ssize_t mcu_powersave_show(struct device *dev,
if (result < 0)
return result;
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%d\n", result);
}
@ -1431,6 +1515,7 @@ static ssize_t mcu_powersave_store(struct device *dev,
return count;
}
static DEVICE_ATTR_RW(mcu_powersave);
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
/* Battery ********************************************************************/
@ -1619,14 +1704,14 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value)
kbd_led_update(asus);
}
static void kbd_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
static int kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value)
{
/* Prevent disabling keyboard backlight on module unregister */
if (led_cdev->flags & LED_UNREGISTERING)
return;
return 0;
do_kbd_led_set(led_cdev, value);
return 0;
}
static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value)
@ -1802,7 +1887,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
asus->kbd_led_wk = led_val;
asus->kbd_led.name = "asus::kbd_backlight";
asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
asus->kbd_led.brightness_set = kbd_led_set;
asus->kbd_led.brightness_set_blocking = kbd_led_set;
asus->kbd_led.brightness_get = kbd_led_get;
asus->kbd_led.max_brightness = 3;
@ -2304,6 +2389,7 @@ exit:
}
/* Panel Overdrive ************************************************************/
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
static ssize_t panel_od_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -2314,6 +2400,8 @@ static ssize_t panel_od_show(struct device *dev,
if (result < 0)
return result;
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%d\n", result);
}
@ -2350,9 +2438,10 @@ static ssize_t panel_od_store(struct device *dev,
return count;
}
static DEVICE_ATTR_RW(panel_od);
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
/* Bootup sound ***************************************************************/
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
static ssize_t boot_sound_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -2363,6 +2452,8 @@ static ssize_t boot_sound_show(struct device *dev,
if (result < 0)
return result;
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%d\n", result);
}
@ -2398,8 +2489,10 @@ static ssize_t boot_sound_store(struct device *dev,
return count;
}
static DEVICE_ATTR_RW(boot_sound);
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
/* Mini-LED mode **************************************************************/
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
static ssize_t mini_led_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -2430,6 +2523,8 @@ static ssize_t mini_led_mode_show(struct device *dev,
}
}
asus_wmi_show_deprecated();
return sysfs_emit(buf, "%d\n", value);
}
@ -2500,10 +2595,13 @@ static ssize_t available_mini_led_mode_show(struct device *dev,
return sysfs_emit(buf, "0 1 2\n");
}
asus_wmi_show_deprecated();
return sysfs_emit(buf, "0\n");
}
static DEVICE_ATTR_RO(available_mini_led_mode);
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
/* Quirks *********************************************************************/
@ -3791,6 +3889,7 @@ static int throttle_thermal_policy_set_default(struct asus_wmi *asus)
return throttle_thermal_policy_write(asus);
}
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
static ssize_t throttle_thermal_policy_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -3834,6 +3933,7 @@ static ssize_t throttle_thermal_policy_store(struct device *dev,
* Throttle thermal policy: 0 - default, 1 - overboost, 2 - silent
*/
static DEVICE_ATTR_RW(throttle_thermal_policy);
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
/* Platform profile ***********************************************************/
static int asus_wmi_platform_profile_get(struct device *dev,
@ -4435,27 +4535,29 @@ static struct attribute *platform_attributes[] = {
&dev_attr_camera.attr,
&dev_attr_cardr.attr,
&dev_attr_touchpad.attr,
&dev_attr_charge_mode.attr,
&dev_attr_egpu_enable.attr,
&dev_attr_egpu_connected.attr,
&dev_attr_dgpu_disable.attr,
&dev_attr_gpu_mux_mode.attr,
&dev_attr_lid_resume.attr,
&dev_attr_als_enable.attr,
&dev_attr_fan_boost_mode.attr,
&dev_attr_throttle_thermal_policy.attr,
&dev_attr_ppt_pl2_sppt.attr,
&dev_attr_ppt_pl1_spl.attr,
&dev_attr_ppt_fppt.attr,
&dev_attr_ppt_apu_sppt.attr,
&dev_attr_ppt_platform_sppt.attr,
&dev_attr_nv_dynamic_boost.attr,
&dev_attr_nv_temp_target.attr,
&dev_attr_mcu_powersave.attr,
&dev_attr_boot_sound.attr,
&dev_attr_panel_od.attr,
&dev_attr_mini_led_mode.attr,
&dev_attr_available_mini_led_mode.attr,
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
&dev_attr_charge_mode.attr,
&dev_attr_egpu_enable.attr,
&dev_attr_egpu_connected.attr,
&dev_attr_dgpu_disable.attr,
&dev_attr_gpu_mux_mode.attr,
&dev_attr_ppt_pl2_sppt.attr,
&dev_attr_ppt_pl1_spl.attr,
&dev_attr_ppt_fppt.attr,
&dev_attr_ppt_apu_sppt.attr,
&dev_attr_ppt_platform_sppt.attr,
&dev_attr_nv_dynamic_boost.attr,
&dev_attr_nv_temp_target.attr,
&dev_attr_mcu_powersave.attr,
&dev_attr_boot_sound.attr,
&dev_attr_panel_od.attr,
&dev_attr_mini_led_mode.attr,
&dev_attr_available_mini_led_mode.attr,
&dev_attr_throttle_thermal_policy.attr,
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
NULL
};
@ -4477,7 +4579,11 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
devid = ASUS_WMI_DEVID_LID_RESUME;
else if (attr == &dev_attr_als_enable.attr)
devid = ASUS_WMI_DEVID_ALS_ENABLE;
else if (attr == &dev_attr_charge_mode.attr)
else if (attr == &dev_attr_fan_boost_mode.attr)
ok = asus->fan_boost_mode_available;
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
if (attr == &dev_attr_charge_mode.attr)
devid = ASUS_WMI_DEVID_CHARGE_MODE;
else if (attr == &dev_attr_egpu_enable.attr)
ok = asus->egpu_enable_available;
@ -4496,7 +4602,7 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
else if (attr == &dev_attr_ppt_pl1_spl.attr)
devid = ASUS_WMI_DEVID_PPT_PL1_SPL;
else if (attr == &dev_attr_ppt_fppt.attr)
devid = ASUS_WMI_DEVID_PPT_FPPT;
devid = ASUS_WMI_DEVID_PPT_PL3_FPPT;
else if (attr == &dev_attr_ppt_apu_sppt.attr)
devid = ASUS_WMI_DEVID_PPT_APU_SPPT;
else if (attr == &dev_attr_ppt_platform_sppt.attr)
@ -4515,6 +4621,7 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
ok = asus->mini_led_dev_id != 0;
else if (attr == &dev_attr_available_mini_led_mode.attr)
ok = asus->mini_led_dev_id != 0;
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
if (devid != -1) {
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
@ -4770,6 +4877,7 @@ static int asus_wmi_add(struct platform_device *pdev)
}
/* ensure defaults for tunables */
#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
asus->ppt_pl2_sppt = 5;
asus->ppt_pl1_spl = 5;
asus->ppt_apu_sppt = 5;
@ -4792,17 +4900,18 @@ static int asus_wmi_add(struct platform_device *pdev)
asus->gpu_mux_dev = ASUS_WMI_DEVID_GPU_MUX;
else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_GPU_MUX_VIVO))
asus->gpu_mux_dev = ASUS_WMI_DEVID_GPU_MUX_VIVO;
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE))
asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE;
else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE2))
asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE2;
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
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;
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE))
asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE;
else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE2))
asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE2;
err = fan_boost_mode_check_present(asus);
if (err)
goto fail_fan_boost_mode;

View File

@ -0,0 +1,593 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Platform driver for the Embedded Controller (EC) of Ayaneo devices. Handles
* hwmon (fan speed, fan control), battery charge limits, and magic module
* control (connected modules, controller disconnection).
*
* Copyright (C) 2025 Antheas Kapenekakis <lkml@antheas.dev>
*/
#include <linux/acpi.h>
#include <linux/bits.h>
#include <linux/dmi.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/power_supply.h>
#include <linux/sysfs.h>
#include <acpi/battery.h>
#define AYANEO_PWM_ENABLE_REG 0x4A
#define AYANEO_PWM_REG 0x4B
#define AYANEO_PWM_MODE_AUTO 0x00
#define AYANEO_PWM_MODE_MANUAL 0x01
#define AYANEO_FAN_REG 0x76
#define EC_CHARGE_CONTROL_BEHAVIOURS \
(BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) | \
BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE))
#define AYANEO_CHARGE_REG 0x1e
#define AYANEO_CHARGE_VAL_AUTO 0xaa
#define AYANEO_CHARGE_VAL_INHIBIT 0x55
#define AYANEO_POWER_REG 0x2d
#define AYANEO_POWER_OFF 0xfe
#define AYANEO_POWER_ON 0xff
#define AYANEO_MODULE_REG 0x2f
#define AYANEO_MODULE_LEFT BIT(0)
#define AYANEO_MODULE_RIGHT BIT(1)
#define AYANEO_MODULE_MASK (AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT)
struct ayaneo_ec_quirk {
bool has_fan_control;
bool has_charge_control;
bool has_magic_modules;
};
struct ayaneo_ec_platform_data {
struct platform_device *pdev;
struct ayaneo_ec_quirk *quirks;
struct acpi_battery_hook battery_hook;
// Protects access to restore_pwm
struct mutex hwmon_lock;
bool restore_charge_limit;
bool restore_pwm;
};
static const struct ayaneo_ec_quirk quirk_fan = {
.has_fan_control = true,
};
static const struct ayaneo_ec_quirk quirk_charge_limit = {
.has_fan_control = true,
.has_charge_control = true,
};
static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
.has_fan_control = true,
.has_charge_control = true,
.has_magic_modules = true,
};
static const struct dmi_system_id dmi_table[] = {
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_MATCH(DMI_BOARD_NAME, "AYANEO 2"),
},
.driver_data = (void *)&quirk_fan,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_MATCH(DMI_BOARD_NAME, "FLIP"),
},
.driver_data = (void *)&quirk_fan,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_MATCH(DMI_BOARD_NAME, "GEEK"),
},
.driver_data = (void *)&quirk_fan,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"),
},
.driver_data = (void *)&quirk_charge_limit,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR 1S"),
},
.driver_data = (void *)&quirk_charge_limit,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "AB05-Mendocino"),
},
.driver_data = (void *)&quirk_charge_limit,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"),
},
.driver_data = (void *)&quirk_charge_limit,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "KUN"),
},
.driver_data = (void *)&quirk_charge_limit,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 3"),
},
.driver_data = (void *)&quirk_ayaneo3,
},
{},
};
/* Callbacks for hwmon interface */
static umode_t ayaneo_ec_hwmon_is_visible(const void *drvdata,
enum hwmon_sensor_types type, u32 attr,
int channel)
{
switch (type) {
case hwmon_fan:
return 0444;
case hwmon_pwm:
return 0644;
default:
return 0;
}
}
static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
u8 tmp;
int ret;
switch (type) {
case hwmon_fan:
switch (attr) {
case hwmon_fan_input:
ret = ec_read(AYANEO_FAN_REG, &tmp);
if (ret)
return ret;
*val = tmp << 8;
ret = ec_read(AYANEO_FAN_REG + 1, &tmp);
if (ret)
return ret;
*val |= tmp;
return 0;
default:
break;
}
break;
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
ret = ec_read(AYANEO_PWM_REG, &tmp);
if (ret)
return ret;
if (tmp > 100)
return -EIO;
*val = (255 * tmp) / 100;
return 0;
case hwmon_pwm_enable:
ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp);
if (ret)
return ret;
if (tmp == AYANEO_PWM_MODE_MANUAL)
*val = 1;
else if (tmp == AYANEO_PWM_MODE_AUTO)
*val = 2;
else
return -EIO;
return 0;
default:
break;
}
break;
default:
break;
}
return -EOPNOTSUPP;
}
static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
struct ayaneo_ec_platform_data *data = dev_get_drvdata(dev);
int ret;
guard(mutex)(&data->hwmon_lock);
switch (type) {
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_enable:
data->restore_pwm = false;
switch (val) {
case 1:
return ec_write(AYANEO_PWM_ENABLE_REG,
AYANEO_PWM_MODE_MANUAL);
case 2:
return ec_write(AYANEO_PWM_ENABLE_REG,
AYANEO_PWM_MODE_AUTO);
default:
return -EINVAL;
}
case hwmon_pwm_input:
if (val < 0 || val > 255)
return -EINVAL;
if (data->restore_pwm) {
/*
* Defer restoring PWM control to after
* userspace resumes successfully
*/
ret = ec_write(AYANEO_PWM_ENABLE_REG,
AYANEO_PWM_MODE_MANUAL);
if (ret)
return ret;
data->restore_pwm = false;
}
return ec_write(AYANEO_PWM_REG, (val * 100) / 255);
default:
break;
}
break;
default:
break;
}
return -EOPNOTSUPP;
}
static const struct hwmon_ops ayaneo_ec_hwmon_ops = {
.is_visible = ayaneo_ec_hwmon_is_visible,
.read = ayaneo_ec_read,
.write = ayaneo_ec_write,
};
static const struct hwmon_channel_info *const ayaneo_ec_sensors[] = {
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
NULL,
};
static const struct hwmon_chip_info ayaneo_ec_chip_info = {
.ops = &ayaneo_ec_hwmon_ops,
.info = ayaneo_ec_sensors,
};
static int ayaneo_psy_ext_get_prop(struct power_supply *psy,
const struct power_supply_ext *ext,
void *data,
enum power_supply_property psp,
union power_supply_propval *val)
{
int ret;
u8 tmp;
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
ret = ec_read(AYANEO_CHARGE_REG, &tmp);
if (ret)
return ret;
if (tmp == AYANEO_CHARGE_VAL_INHIBIT)
val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
else
val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
return 0;
default:
return -EINVAL;
}
}
static int ayaneo_psy_ext_set_prop(struct power_supply *psy,
const struct power_supply_ext *ext,
void *data,
enum power_supply_property psp,
const union power_supply_propval *val)
{
u8 raw_val;
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
switch (val->intval) {
case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
raw_val = AYANEO_CHARGE_VAL_AUTO;
break;
case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
raw_val = AYANEO_CHARGE_VAL_INHIBIT;
break;
default:
return -EINVAL;
}
return ec_write(AYANEO_CHARGE_REG, raw_val);
default:
return -EINVAL;
}
}
static int ayaneo_psy_prop_is_writeable(struct power_supply *psy,
const struct power_supply_ext *ext,
void *data,
enum power_supply_property psp)
{
return true;
}
static const enum power_supply_property ayaneo_psy_ext_props[] = {
POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
};
static const struct power_supply_ext ayaneo_psy_ext = {
.name = "ayaneo-charge-control",
.properties = ayaneo_psy_ext_props,
.num_properties = ARRAY_SIZE(ayaneo_psy_ext_props),
.charge_behaviours = EC_CHARGE_CONTROL_BEHAVIOURS,
.get_property = ayaneo_psy_ext_get_prop,
.set_property = ayaneo_psy_ext_set_prop,
.property_is_writeable = ayaneo_psy_prop_is_writeable,
};
static int ayaneo_add_battery(struct power_supply *battery,
struct acpi_battery_hook *hook)
{
struct ayaneo_ec_platform_data *data =
container_of(hook, struct ayaneo_ec_platform_data, battery_hook);
return power_supply_register_extension(battery, &ayaneo_psy_ext,
&data->pdev->dev, NULL);
}
static int ayaneo_remove_battery(struct power_supply *battery,
struct acpi_battery_hook *hook)
{
power_supply_unregister_extension(battery, &ayaneo_psy_ext);
return 0;
}
static ssize_t controller_power_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
bool value;
int ret;
ret = kstrtobool(buf, &value);
if (ret)
return ret;
ret = ec_write(AYANEO_POWER_REG, value ? AYANEO_POWER_ON : AYANEO_POWER_OFF);
if (ret)
return ret;
return count;
}
static ssize_t controller_power_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
u8 val;
ret = ec_read(AYANEO_POWER_REG, &val);
if (ret)
return ret;
return sysfs_emit(buf, "%d\n", val == AYANEO_POWER_ON);
}
static DEVICE_ATTR_RW(controller_power);
static ssize_t controller_modules_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
u8 unconnected_modules;
char *out;
int ret;
ret = ec_read(AYANEO_MODULE_REG, &unconnected_modules);
if (ret)
return ret;
switch (~unconnected_modules & AYANEO_MODULE_MASK) {
case AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT:
out = "both";
break;
case AYANEO_MODULE_LEFT:
out = "left";
break;
case AYANEO_MODULE_RIGHT:
out = "right";
break;
default:
out = "none";
break;
}
return sysfs_emit(buf, "%s\n", out);
}
static DEVICE_ATTR_RO(controller_modules);
static struct attribute *aya_mm_attrs[] = {
&dev_attr_controller_power.attr,
&dev_attr_controller_modules.attr,
NULL
};
static umode_t aya_mm_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct platform_device *pdev = to_platform_device(dev);
struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
if (data->quirks->has_magic_modules)
return attr->mode;
return 0;
}
static const struct attribute_group aya_mm_attribute_group = {
.is_visible = aya_mm_is_visible,
.attrs = aya_mm_attrs,
};
static const struct attribute_group *ayaneo_ec_groups[] = {
&aya_mm_attribute_group,
NULL
};
static int ayaneo_ec_probe(struct platform_device *pdev)
{
const struct dmi_system_id *dmi_entry;
struct ayaneo_ec_platform_data *data;
struct device *hwdev;
int ret;
dmi_entry = dmi_first_match(dmi_table);
if (!dmi_entry)
return -ENODEV;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->pdev = pdev;
data->quirks = dmi_entry->driver_data;
ret = devm_mutex_init(&pdev->dev, &data->hwmon_lock);
if (ret)
return ret;
platform_set_drvdata(pdev, data);
if (data->quirks->has_fan_control) {
hwdev = devm_hwmon_device_register_with_info(&pdev->dev,
"ayaneo_ec", data, &ayaneo_ec_chip_info, NULL);
if (IS_ERR(hwdev))
return PTR_ERR(hwdev);
}
if (data->quirks->has_charge_control) {
data->battery_hook.add_battery = ayaneo_add_battery;
data->battery_hook.remove_battery = ayaneo_remove_battery;
data->battery_hook.name = "Ayaneo Battery";
ret = devm_battery_hook_register(&pdev->dev, &data->battery_hook);
if (ret)
return ret;
}
return 0;
}
static int ayaneo_freeze(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
int ret;
u8 tmp;
if (data->quirks->has_charge_control) {
ret = ec_read(AYANEO_CHARGE_REG, &tmp);
if (ret)
return ret;
data->restore_charge_limit = tmp == AYANEO_CHARGE_VAL_INHIBIT;
}
if (data->quirks->has_fan_control) {
ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp);
if (ret)
return ret;
data->restore_pwm = tmp == AYANEO_PWM_MODE_MANUAL;
/*
* Release the fan when entering hibernation to avoid
* overheating if hibernation fails and hangs.
*/
if (data->restore_pwm) {
ret = ec_write(AYANEO_PWM_ENABLE_REG, AYANEO_PWM_MODE_AUTO);
if (ret)
return ret;
}
}
return 0;
}
static int ayaneo_restore(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
int ret;
if (data->quirks->has_charge_control && data->restore_charge_limit) {
ret = ec_write(AYANEO_CHARGE_REG, AYANEO_CHARGE_VAL_INHIBIT);
if (ret)
return ret;
}
return 0;
}
static const struct dev_pm_ops ayaneo_pm_ops = {
.freeze = ayaneo_freeze,
.restore = ayaneo_restore,
};
static struct platform_driver ayaneo_platform_driver = {
.driver = {
.name = "ayaneo-ec",
.dev_groups = ayaneo_ec_groups,
.pm = pm_sleep_ptr(&ayaneo_pm_ops),
},
.probe = ayaneo_ec_probe,
};
static struct platform_device *ayaneo_platform_device;
static int __init ayaneo_ec_init(void)
{
ayaneo_platform_device =
platform_create_bundle(&ayaneo_platform_driver,
ayaneo_ec_probe, NULL, 0, NULL, 0);
return PTR_ERR_OR_ZERO(ayaneo_platform_device);
}
static void __exit ayaneo_ec_exit(void)
{
platform_device_unregister(ayaneo_platform_device);
platform_driver_unregister(&ayaneo_platform_driver);
}
MODULE_DEVICE_TABLE(dmi, dmi_table);
module_init(ayaneo_ec_init);
module_exit(ayaneo_ec_exit);
MODULE_AUTHOR("Antheas Kapenekakis <lkml@antheas.dev>");
MODULE_DESCRIPTION("Ayaneo Embedded Controller (EC) platform features");
MODULE_LICENSE("GPL");

View File

@ -235,11 +235,6 @@ enum AWCC_THERMAL_TABLES {
AWCC_THERMAL_TABLE_USTT = 0xA,
};
enum AWCC_SPECIAL_THERMAL_CODES {
AWCC_SPECIAL_PROFILE_CUSTOM = 0x00,
AWCC_SPECIAL_PROFILE_GMODE = 0xAB,
};
enum AWCC_TEMP_SENSOR_TYPES {
AWCC_TEMP_SENSOR_CPU = 0x01,
AWCC_TEMP_SENSOR_FRONT = 0x03,
@ -266,17 +261,18 @@ enum AWCC_FAN_TYPES {
};
enum awcc_thermal_profile {
AWCC_PROFILE_USTT_BALANCED,
AWCC_PROFILE_USTT_BALANCED_PERFORMANCE,
AWCC_PROFILE_USTT_COOL,
AWCC_PROFILE_USTT_QUIET,
AWCC_PROFILE_USTT_PERFORMANCE,
AWCC_PROFILE_USTT_LOW_POWER,
AWCC_PROFILE_LEGACY_QUIET,
AWCC_PROFILE_LEGACY_BALANCED,
AWCC_PROFILE_LEGACY_BALANCED_PERFORMANCE,
AWCC_PROFILE_LEGACY_PERFORMANCE,
AWCC_PROFILE_LAST,
AWCC_PROFILE_SPECIAL_CUSTOM = 0x00,
AWCC_PROFILE_LEGACY_QUIET = 0x96,
AWCC_PROFILE_LEGACY_BALANCED = 0x97,
AWCC_PROFILE_LEGACY_BALANCED_PERFORMANCE = 0x98,
AWCC_PROFILE_LEGACY_PERFORMANCE = 0x99,
AWCC_PROFILE_USTT_BALANCED = 0xA0,
AWCC_PROFILE_USTT_BALANCED_PERFORMANCE = 0xA1,
AWCC_PROFILE_USTT_COOL = 0xA2,
AWCC_PROFILE_USTT_QUIET = 0xA3,
AWCC_PROFILE_USTT_PERFORMANCE = 0xA4,
AWCC_PROFILE_USTT_LOW_POWER = 0xA5,
AWCC_PROFILE_SPECIAL_GMODE = 0xAB,
};
struct wmax_led_args {
@ -332,19 +328,6 @@ struct awcc_priv {
u32 gpio_count;
};
static const enum platform_profile_option awcc_mode_to_platform_profile[AWCC_PROFILE_LAST] = {
[AWCC_PROFILE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED,
[AWCC_PROFILE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
[AWCC_PROFILE_USTT_COOL] = PLATFORM_PROFILE_COOL,
[AWCC_PROFILE_USTT_QUIET] = PLATFORM_PROFILE_QUIET,
[AWCC_PROFILE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
[AWCC_PROFILE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER,
[AWCC_PROFILE_LEGACY_QUIET] = PLATFORM_PROFILE_QUIET,
[AWCC_PROFILE_LEGACY_BALANCED] = PLATFORM_PROFILE_BALANCED,
[AWCC_PROFILE_LEGACY_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
[AWCC_PROFILE_LEGACY_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
};
static struct awcc_quirks *awcc;
/*
@ -562,21 +545,41 @@ const struct attribute_group wmax_deepsleep_attribute_group = {
/*
* AWCC Helpers
*/
static bool is_awcc_thermal_profile_id(u8 code)
static int awcc_profile_to_pprof(enum awcc_thermal_profile profile,
enum platform_profile_option *pprof)
{
u8 table = FIELD_GET(AWCC_THERMAL_TABLE_MASK, code);
u8 mode = FIELD_GET(AWCC_THERMAL_MODE_MASK, code);
switch (profile) {
case AWCC_PROFILE_SPECIAL_CUSTOM:
*pprof = PLATFORM_PROFILE_CUSTOM;
break;
case AWCC_PROFILE_LEGACY_QUIET:
case AWCC_PROFILE_USTT_QUIET:
*pprof = PLATFORM_PROFILE_QUIET;
break;
case AWCC_PROFILE_LEGACY_BALANCED:
case AWCC_PROFILE_USTT_BALANCED:
*pprof = PLATFORM_PROFILE_BALANCED;
break;
case AWCC_PROFILE_LEGACY_BALANCED_PERFORMANCE:
case AWCC_PROFILE_USTT_BALANCED_PERFORMANCE:
*pprof = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
break;
case AWCC_PROFILE_LEGACY_PERFORMANCE:
case AWCC_PROFILE_USTT_PERFORMANCE:
case AWCC_PROFILE_SPECIAL_GMODE:
*pprof = PLATFORM_PROFILE_PERFORMANCE;
break;
case AWCC_PROFILE_USTT_COOL:
*pprof = PLATFORM_PROFILE_COOL;
break;
case AWCC_PROFILE_USTT_LOW_POWER:
*pprof = PLATFORM_PROFILE_LOW_POWER;
break;
default:
return -EINVAL;
}
if (mode >= AWCC_PROFILE_LAST)
return false;
if (table == AWCC_THERMAL_TABLE_LEGACY && mode >= AWCC_PROFILE_LEGACY_QUIET)
return true;
if (table == AWCC_THERMAL_TABLE_USTT && mode <= AWCC_PROFILE_USTT_LOW_POWER)
return true;
return false;
return 0;
}
static int awcc_wmi_command(struct wmi_device *wdev, u32 method_id,
@ -1225,24 +1228,7 @@ static int awcc_platform_profile_get(struct device *dev,
if (ret)
return ret;
switch (out_data) {
case AWCC_SPECIAL_PROFILE_CUSTOM:
*profile = PLATFORM_PROFILE_CUSTOM;
return 0;
case AWCC_SPECIAL_PROFILE_GMODE:
*profile = PLATFORM_PROFILE_PERFORMANCE;
return 0;
default:
break;
}
if (!is_awcc_thermal_profile_id(out_data))
return -ENODATA;
out_data = FIELD_GET(AWCC_THERMAL_MODE_MASK, out_data);
*profile = awcc_mode_to_platform_profile[out_data];
return 0;
return awcc_profile_to_pprof(out_data, profile);
}
static int awcc_platform_profile_set(struct device *dev,
@ -1279,7 +1265,6 @@ static int awcc_platform_profile_probe(void *drvdata, unsigned long *choices)
{
enum platform_profile_option profile;
struct awcc_priv *priv = drvdata;
enum awcc_thermal_profile mode;
u8 id, offset = 0;
int ret;
@ -1301,15 +1286,20 @@ static int awcc_platform_profile_probe(void *drvdata, unsigned long *choices)
if (ret)
return ret;
if (!is_awcc_thermal_profile_id(id)) {
/*
* G-Mode profile ID is not listed consistently across modeles
* that support it, therefore we handle it through quirks.
*/
if (id == AWCC_PROFILE_SPECIAL_GMODE)
continue;
ret = awcc_profile_to_pprof(id, &profile);
if (ret) {
dev_dbg(&priv->wdev->dev, "Unmapped thermal profile ID 0x%02x\n", id);
continue;
}
mode = FIELD_GET(AWCC_THERMAL_MODE_MASK, id);
profile = awcc_mode_to_platform_profile[mode];
priv->supported_profiles[profile] = id;
__set_bit(profile, choices);
}
@ -1318,14 +1308,14 @@ static int awcc_platform_profile_probe(void *drvdata, unsigned long *choices)
if (awcc->gmode) {
priv->supported_profiles[PLATFORM_PROFILE_PERFORMANCE] =
AWCC_SPECIAL_PROFILE_GMODE;
AWCC_PROFILE_SPECIAL_GMODE;
__set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
}
/* Every model supports the "custom" profile */
priv->supported_profiles[PLATFORM_PROFILE_CUSTOM] =
AWCC_SPECIAL_PROFILE_CUSTOM;
AWCC_PROFILE_SPECIAL_CUSTOM;
__set_bit(PLATFORM_PROFILE_CUSTOM, choices);

View File

@ -112,14 +112,14 @@ set_speed:
gpd_pocket_fan_set_speed(fan, speed);
/* When mostly idle (low temp/speed), slow down the poll interval. */
queue_delayed_work(system_wq, &fan->work,
queue_delayed_work(system_percpu_wq, &fan->work,
msecs_to_jiffies(4000 / (speed + 1)));
}
static void gpd_pocket_fan_force_update(struct gpd_pocket_fan_data *fan)
{
fan->last_speed = -1;
mod_delayed_work(system_wq, &fan->work, 0);
mod_delayed_work(system_percpu_wq, &fan->work, 0);
}
static int gpd_pocket_fan_probe(struct platform_device *pdev)

View File

@ -63,12 +63,16 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4");
* contains "PerformanceControl".
*/
static const char * const omen_thermal_profile_boards[] = {
"84DA", "84DB", "84DC", "8574", "8575", "860A", "87B5", "8572", "8573",
"8600", "8601", "8602", "8605", "8606", "8607", "8746", "8747", "8749",
"874A", "8603", "8604", "8748", "886B", "886C", "878A", "878B", "878C",
"88C8", "88CB", "8786", "8787", "8788", "88D1", "88D2", "88F4", "88FD",
"88F5", "88F6", "88F7", "88FE", "88FF", "8900", "8901", "8902", "8912",
"8917", "8918", "8949", "894A", "89EB", "8BAD", "8A42", "8A15"
"84DA", "84DB", "84DC",
"8572", "8573", "8574", "8575",
"8600", "8601", "8602", "8603", "8604", "8605", "8606", "8607", "860A",
"8746", "8747", "8748", "8749", "874A", "8786", "8787", "8788", "878A",
"878B", "878C", "87B5",
"886B", "886C", "88C8", "88CB", "88D1", "88D2", "88F4", "88F5", "88F6",
"88F7", "88FD", "88FE", "88FF",
"8900", "8901", "8902", "8912", "8917", "8918", "8949", "894A", "89EB",
"8A15", "8A42",
"8BAD",
};
/* DMI Board names of Omen laptops that are specifically set to be thermal
@ -76,7 +80,8 @@ static const char * const omen_thermal_profile_boards[] = {
* the get system design information WMI call returns
*/
static const char * const omen_thermal_profile_force_v0_boards[] = {
"8607", "8746", "8747", "8749", "874A", "8748"
"8607",
"8746", "8747", "8748", "8749", "874A",
};
/* DMI board names of Omen laptops that have a thermal profile timer which will
@ -84,12 +89,13 @@ static const char * const omen_thermal_profile_force_v0_boards[] = {
* "balanced" when reaching zero.
*/
static const char * const omen_timed_thermal_profile_boards[] = {
"8BAD", "8A42", "8A15"
"8A15", "8A42",
"8BAD",
};
/* DMI Board names of Victus 16-d1xxx laptops */
static const char * const victus_thermal_profile_boards[] = {
"8A25"
"8A25",
};
/* DMI Board names of Victus 16-r and Victus 16-s laptops */

View File

@ -177,6 +177,18 @@ static const struct dmi_system_id dmi_vgbs_allow_list[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "HP Elite Dragonfly G2 Notebook PC"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Dell Pro Rugged 10 Tablet RA00260"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Dell Pro Rugged 12 Tablet RA02260"),
},
},
{ }
};

View File

@ -281,6 +281,7 @@ static const struct pmc_reg_map arl_socs_reg_map = {
.etr3_offset = ETR3_OFFSET,
.pson_residency_offset = TGL_PSON_RESIDENCY_OFFSET,
.pson_residency_counter_step = TGL_PSON_RES_COUNTER_STEP,
.lpm_req_guid = SOCS_LPM_REQ_GUID,
};
static const struct pmc_bit_map arl_pchs_ltr_show_map[] = {
@ -648,26 +649,23 @@ static const struct pmc_reg_map arl_pchs_reg_map = {
.lpm_num_maps = ADL_LPM_NUM_MAPS,
.lpm_reg_index = ARL_LPM_REG_INDEX,
.etr3_offset = ETR3_OFFSET,
.lpm_req_guid = PCHS_LPM_REQ_GUID,
};
static struct pmc_info arl_pmc_info_list[] = {
{
.guid = IOEP_LPM_REQ_GUID,
.devid = PMC_DEVID_ARL_IOEP,
.map = &mtl_ioep_reg_map,
},
{
.guid = SOCS_LPM_REQ_GUID,
.devid = PMC_DEVID_ARL_SOCS,
.map = &arl_socs_reg_map,
},
{
.guid = PCHS_LPM_REQ_GUID,
.devid = PMC_DEVID_ARL_PCHS,
.map = &arl_pchs_reg_map,
},
{
.guid = SOCM_LPM_REQ_GUID,
.devid = PMC_DEVID_ARL_SOCM,
.map = &mtl_socm_reg_map,
},
@ -720,9 +718,10 @@ static int arl_h_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_
return generic_core_init(pmcdev, pmc_dev_info);
}
static u32 ARL_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0};
struct pmc_dev_info arl_pmc_dev = {
.pci_func = 0,
.dmu_guid = ARL_PMT_DMU_GUID,
.dmu_guids = ARL_PMT_DMU_GUIDS,
.regmap_list = arl_pmc_info_list,
.map = &arl_socs_reg_map,
.sub_req_show = &pmc_core_substate_req_regs_fops,
@ -732,9 +731,10 @@ struct pmc_dev_info arl_pmc_dev = {
.sub_req = pmc_core_pmt_get_lpm_req,
};
static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, ARL_H_PMT_DMU_GUID, 0x0};
struct pmc_dev_info arl_h_pmc_dev = {
.pci_func = 2,
.dmu_guid = ARL_PMT_DMU_GUID,
.dmu_guids = ARL_H_PMT_DMU_GUIDS,
.regmap_list = arl_pmc_info_list,
.map = &mtl_socm_reg_map,
.sub_req_show = &pmc_core_substate_req_regs_fops,

View File

@ -20,6 +20,7 @@ enum header_type {
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pci.h>
@ -311,20 +312,20 @@ static inline u8 pmc_core_reg_read_byte(struct pmc *pmc, int offset)
}
static void pmc_core_display_map(struct seq_file *s, int index, int idx, int ip,
int pmc_index, u8 pf_reg, const struct pmc_bit_map **pf_map)
int pmc_idx, u8 pf_reg, const struct pmc_bit_map **pf_map)
{
seq_printf(s, "PMC%d:PCH IP: %-2d - %-32s\tState: %s\n",
pmc_index, ip, pf_map[idx][index].name,
pmc_idx, ip, pf_map[idx][index].name,
pf_map[idx][index].bit_mask & pf_reg ? "Off" : "On");
}
static int pmc_core_ppfear_show(struct seq_file *s, void *unused)
{
struct pmc_dev *pmcdev = s->private;
unsigned int i;
unsigned int pmc_idx;
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
struct pmc *pmc = pmcdev->pmcs[i];
for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) {
struct pmc *pmc = pmcdev->pmcs[pmc_idx];
const struct pmc_bit_map **maps;
u8 pf_regs[PPFEAR_MAX_NUM_ENTRIES];
unsigned int index, iter, idx, ip = 0;
@ -342,7 +343,7 @@ static int pmc_core_ppfear_show(struct seq_file *s, void *unused)
for (idx = 0; maps[idx]; idx++) {
for (index = 0; maps[idx][index].name &&
index < pmc->map->ppfear_buckets * 8; ip++, index++)
pmc_core_display_map(s, index, idx, ip, i,
pmc_core_display_map(s, index, idx, ip, pmc_idx,
pf_regs[index / 8], maps);
}
}
@ -471,7 +472,7 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore)
struct pmc *pmc;
const struct pmc_reg_map *map;
u32 reg;
unsigned int pmc_index;
unsigned int pmc_idx;
int ltr_index;
ltr_index = value;
@ -479,8 +480,8 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore)
* is based on the contiguous indexes from ltr_show output.
* pmc index and ltr index needs to be calculated from it.
*/
for (pmc_index = 0; pmc_index < ARRAY_SIZE(pmcdev->pmcs) && ltr_index >= 0; pmc_index++) {
pmc = pmcdev->pmcs[pmc_index];
for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs) && ltr_index >= 0; pmc_idx++) {
pmc = pmcdev->pmcs[pmc_idx];
if (!pmc)
continue;
@ -497,10 +498,10 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore)
ltr_index = ltr_index - (map->ltr_ignore_max + 2) - 1;
}
if (pmc_index >= ARRAY_SIZE(pmcdev->pmcs) || ltr_index < 0)
if (pmc_idx >= ARRAY_SIZE(pmcdev->pmcs) || ltr_index < 0)
return -EINVAL;
pr_debug("ltr_ignore for pmc%d: ltr_index:%d\n", pmc_index, ltr_index);
pr_debug("ltr_ignore for pmc%d: ltr_index:%d\n", pmc_idx, ltr_index);
guard(mutex)(&pmcdev->lock);
@ -635,14 +636,14 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused)
u64 decoded_snoop_ltr, decoded_non_snoop_ltr, val;
u32 ltr_raw_data, scale;
u16 snoop_ltr, nonsnoop_ltr;
unsigned int i, index, ltr_index = 0;
unsigned int pmc_idx, index, ltr_index = 0;
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) {
struct pmc *pmc;
const struct pmc_bit_map *map;
u32 ltr_ign_reg;
pmc = pmcdev->pmcs[i];
pmc = pmcdev->pmcs[pmc_idx];
if (!pmc)
continue;
@ -676,7 +677,7 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused)
}
seq_printf(s, "%d\tPMC%d:%-32s\tLTR: RAW: 0x%-16x\tNon-Snoop(ns): %-16llu\tSnoop(ns): %-16llu\tLTR_IGNORE: %d\n",
ltr_index, i, map[index].name, ltr_raw_data,
ltr_index, pmc_idx, map[index].name, ltr_raw_data,
decoded_non_snoop_ltr,
decoded_snoop_ltr, ltr_ign_data);
ltr_index++;
@ -689,15 +690,15 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_ltr);
static int pmc_core_s0ix_blocker_show(struct seq_file *s, void *unused)
{
struct pmc_dev *pmcdev = s->private;
unsigned int pmcidx;
unsigned int pmc_idx;
for (pmcidx = 0; pmcidx < ARRAY_SIZE(pmcdev->pmcs); pmcidx++) {
for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) {
const struct pmc_bit_map **maps;
unsigned int arr_size, r_idx;
u32 offset, counter;
struct pmc *pmc;
pmc = pmcdev->pmcs[pmcidx];
pmc = pmcdev->pmcs[pmc_idx];
if (!pmc)
continue;
maps = pmc->map->s0ix_blocker_maps;
@ -711,7 +712,7 @@ static int pmc_core_s0ix_blocker_show(struct seq_file *s, void *unused)
if (!map->blk)
continue;
counter = pmc_core_reg_read(pmc, offset);
seq_printf(s, "PMC%d:%-30s %-30d\n", pmcidx,
seq_printf(s, "PMC%d:%-30s %-30d\n", pmc_idx,
map->name, counter);
offset += map->blk * S0IX_BLK_SIZE;
}
@ -723,13 +724,13 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_s0ix_blocker);
static void pmc_core_ltr_ignore_all(struct pmc_dev *pmcdev)
{
unsigned int i;
unsigned int pmc_idx;
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); i++) {
for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) {
struct pmc *pmc;
u32 ltr_ign;
pmc = pmcdev->pmcs[i];
pmc = pmcdev->pmcs[pmc_idx];
if (!pmc)
continue;
@ -750,12 +751,12 @@ static void pmc_core_ltr_ignore_all(struct pmc_dev *pmcdev)
static void pmc_core_ltr_restore_all(struct pmc_dev *pmcdev)
{
unsigned int i;
unsigned int pmc_idx;
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); i++) {
for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) {
struct pmc *pmc;
pmc = pmcdev->pmcs[i];
pmc = pmcdev->pmcs[pmc_idx];
if (!pmc)
continue;
@ -794,10 +795,10 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_res);
static int pmc_core_substate_sts_regs_show(struct seq_file *s, void *unused)
{
struct pmc_dev *pmcdev = s->private;
unsigned int i;
unsigned int pmc_idx;
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
struct pmc *pmc = pmcdev->pmcs[i];
for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) {
struct pmc *pmc = pmcdev->pmcs[pmc_idx];
const struct pmc_bit_map **maps;
u32 offset;
@ -805,7 +806,7 @@ static int pmc_core_substate_sts_regs_show(struct seq_file *s, void *unused)
continue;
maps = pmc->map->lpm_sts;
offset = pmc->map->lpm_status_offset;
pmc_core_lpm_display(pmc, NULL, s, offset, i, "STATUS", maps);
pmc_core_lpm_display(pmc, NULL, s, offset, pmc_idx, "STATUS", maps);
}
return 0;
@ -815,10 +816,10 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_sts_regs);
static int pmc_core_substate_l_sts_regs_show(struct seq_file *s, void *unused)
{
struct pmc_dev *pmcdev = s->private;
unsigned int i;
unsigned int pmc_idx;
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
struct pmc *pmc = pmcdev->pmcs[i];
for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) {
struct pmc *pmc = pmcdev->pmcs[pmc_idx];
const struct pmc_bit_map **maps;
u32 offset;
@ -826,7 +827,7 @@ static int pmc_core_substate_l_sts_regs_show(struct seq_file *s, void *unused)
continue;
maps = pmc->map->lpm_sts;
offset = pmc->map->lpm_live_status_offset;
pmc_core_lpm_display(pmc, NULL, s, offset, i, "LIVE_STATUS", maps);
pmc_core_lpm_display(pmc, NULL, s, offset, pmc_idx, "LIVE_STATUS", maps);
}
return 0;
@ -919,11 +920,11 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
u32 sts_offset;
u32 sts_offset_live;
u32 *lpm_req_regs;
unsigned int mp, pmc_index;
unsigned int mp, pmc_idx;
int num_maps;
for (pmc_index = 0; pmc_index < ARRAY_SIZE(pmcdev->pmcs); ++pmc_index) {
struct pmc *pmc = pmcdev->pmcs[pmc_index];
for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) {
struct pmc *pmc = pmcdev->pmcs[pmc_idx];
const struct pmc_bit_map **maps;
if (!pmc)
@ -944,7 +945,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
continue;
/* Display the header */
pmc_core_substate_req_header_show(s, pmc_index, HEADER_STATUS);
pmc_core_substate_req_header_show(s, pmc_idx, HEADER_STATUS);
/* Loop over maps */
for (mp = 0; mp < num_maps; mp++) {
@ -982,7 +983,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
}
/* Display the element name in the first column */
seq_printf(s, "pmc%d: %34s |", pmc_index, map[i].name);
seq_printf(s, "pmc%d: %34s |", pmc_idx, map[i].name);
/* Loop over the enabled states and display if required */
pmc_for_each_mode(mode, pmcdev) {
@ -1281,7 +1282,20 @@ int get_primary_reg_base(struct pmc *pmc)
return 0;
}
void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid)
static struct telem_endpoint *pmc_core_register_endpoint(struct pci_dev *pcidev, u32 *guids)
{
struct telem_endpoint *ep;
unsigned int i;
for (i = 0; guids[i]; i++) {
ep = pmt_telem_find_and_register_endpoint(pcidev, guids[i], 0);
if (!IS_ERR(ep))
return ep;
}
return ERR_PTR(-ENODEV);
}
void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids)
{
struct telem_endpoint *ep;
struct pci_dev *pcidev;
@ -1292,7 +1306,7 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid)
return;
}
ep = pmt_telem_find_and_register_endpoint(pcidev, guid, 0);
ep = pmc_core_register_endpoint(pcidev, guids);
pci_dev_put(pcidev);
if (IS_ERR(ep)) {
dev_err(&pmcdev->pdev->dev,
@ -1302,8 +1316,6 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid)
}
pmcdev->punit_ep = ep;
pmcdev->has_die_c6 = true;
pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET;
}
@ -1423,22 +1435,13 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev, struct pmc_dev_info
pmcdev->dbgfs_dir, primary_pmc, &pmc_core_pson_residency);
}
if (pmcdev->has_die_c6) {
if (pmcdev->punit_ep) {
debugfs_create_file("die_c6_us_show", 0444,
pmcdev->dbgfs_dir, pmcdev,
&pmc_core_die_c6_us_fops);
}
}
static u32 pmc_core_find_guid(struct pmc_info *list, const struct pmc_reg_map *map)
{
for (; list->map; ++list)
if (list->map == map)
return list->guid;
return 0;
}
/*
* This function retrieves low power mode requirement data from PMC Low
* Power Mode (LPM) table.
@ -1553,26 +1556,24 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info *
{
struct pci_dev *pcidev __free(pci_dev_put) = NULL;
struct telem_endpoint *ep;
unsigned int i;
u32 guid;
unsigned int pmc_idx;
int ret;
pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, pmc_dev_info->pci_func));
if (!pcidev)
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) {
struct pmc *pmc;
pmc = pmcdev->pmcs[i];
pmc = pmcdev->pmcs[pmc_idx];
if (!pmc)
continue;
guid = pmc_core_find_guid(pmcdev->regmap_list, pmc->map);
if (!guid)
if (!pmc->map->lpm_req_guid)
return -ENXIO;
ep = pmt_telem_find_and_register_endpoint(pcidev, guid, 0);
ep = pmt_telem_find_and_register_endpoint(pcidev, pmc->map->lpm_req_guid, 0);
if (IS_ERR(ep)) {
dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %pe", ep);
return -EPROBE_DEFER;
@ -1596,7 +1597,7 @@ static const struct pmc_reg_map *pmc_core_find_regmap(struct pmc_info *list, u16
return NULL;
}
static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index)
static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_idx)
{
struct pmc_ssram_telemetry pmc_ssram_telemetry;
@ -1604,7 +1605,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index)
struct pmc *pmc;
int ret;
ret = pmc_ssram_telemetry_get_pmc_info(pmc_index, &pmc_ssram_telemetry);
ret = pmc_ssram_telemetry_get_pmc_info(pmc_idx, &pmc_ssram_telemetry);
if (ret)
return ret;
@ -1612,7 +1613,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index)
if (!map)
return -ENODEV;
pmc = pmcdev->pmcs[pmc_index];
pmc = pmcdev->pmcs[pmc_idx];
/* Memory for primary PMC has been allocated */
if (!pmc) {
pmc = devm_kzalloc(&pmcdev->pdev->dev, sizeof(*pmc), GFP_KERNEL);
@ -1629,7 +1630,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index)
return -ENOMEM;
}
pmcdev->pmcs[pmc_index] = pmc;
pmcdev->pmcs[pmc_idx] = pmc;
return 0;
}
@ -1689,8 +1690,8 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
}
pmc_core_get_low_power_modes(pmcdev);
if (pmc_dev_info->dmu_guid)
pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guid);
if (pmc_dev_info->dmu_guids)
pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guids);
if (ssram) {
ret = pmc_core_get_telem_info(pmcdev, pmc_dev_info);
@ -1701,8 +1702,8 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
return 0;
unmap_regbase:
for (unsigned int i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
struct pmc *pmc = pmcdev->pmcs[i];
for (unsigned int pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) {
struct pmc *pmc = pmcdev->pmcs[pmc_idx];
if (pmc && pmc->regbase)
iounmap(pmc->regbase);
@ -1795,10 +1796,10 @@ static void pmc_core_do_dmi_quirks(struct pmc *pmc)
static void pmc_core_clean_structure(struct platform_device *pdev)
{
struct pmc_dev *pmcdev = platform_get_drvdata(pdev);
unsigned int i;
unsigned int pmc_idx;
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
struct pmc *pmc = pmcdev->pmcs[i];
for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) {
struct pmc *pmc = pmcdev->pmcs[pmc_idx];
if (pmc && pmc->regbase)
iounmap(pmc->regbase);
@ -1958,7 +1959,7 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev)
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
const struct pmc_bit_map **maps = pmc->map->lpm_sts;
int offset = pmc->map->lpm_status_offset;
unsigned int i;
unsigned int pmc_idx, i;
/* Check if the syspend used S0ix */
if (pm_suspend_via_firmware())
@ -1996,13 +1997,13 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev)
if (pmc->map->slps0_dbg_maps)
pmc_core_slps0_display(pmc, dev, NULL);
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
struct pmc *pmc = pmcdev->pmcs[i];
for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) {
struct pmc *pmc = pmcdev->pmcs[pmc_idx];
if (!pmc)
continue;
if (pmc->map->lpm_sts)
pmc_core_lpm_display(pmc, dev, NULL, offset, i, "STATUS", maps);
pmc_core_lpm_display(pmc, dev, NULL, offset, pmc_idx, "STATUS", maps);
}
return 0;

View File

@ -282,7 +282,8 @@ enum ppfear_regs {
/* Die C6 from PUNIT telemetry */
#define MTL_PMT_DMU_DIE_C6_OFFSET 15
#define MTL_PMT_DMU_GUID 0x1A067102
#define ARL_PMT_DMU_GUID 0x1A06A000
#define ARL_PMT_DMU_GUID 0x1A06A102
#define ARL_H_PMT_DMU_GUID 0x1A06A101
#define LNL_PMC_MMIO_REG_LEN 0x2708
#define LNL_PMC_LTR_OSSE 0x1B88
@ -303,6 +304,8 @@ enum ppfear_regs {
/* Wildcat Lake */
#define WCL_PMC_LTR_RESERVED 0x1B64
#define WCL_PCD_PMC_MMIO_REG_LEN 0x3178
#define WCL_NUM_S0IX_BLOCKER 94
#define WCL_BLK_REQ_OFFSET 50
/* SSRAM PMC Device ID */
/* LNL */
@ -355,6 +358,7 @@ struct pmc_bit_map {
* @s0ix_blocker_offset PWRMBASE offset to S0ix blocker counter
* @num_s0ix_blocker: Number of S0ix blockers
* @blocker_req_offset: Telemetry offset to S0ix blocker low power mode substate requirement table
* @lpm_req_guid: Telemetry GUID to read low power mode substate requirement table
*
* Each PCH has unique set of register offsets and bit indexes. This structure
* captures them to have a common implementation.
@ -396,6 +400,8 @@ struct pmc_reg_map {
const u8 *lpm_reg_index;
const u32 pson_residency_offset;
const u32 pson_residency_counter_step;
/* GUID for telemetry regions */
const u32 lpm_req_guid;
};
/**
@ -405,7 +411,6 @@ struct pmc_reg_map {
* specific attributes
*/
struct pmc_info {
u32 guid;
u16 devid;
const struct pmc_reg_map *map;
};
@ -465,7 +470,6 @@ struct pmc_dev {
u64 *pkgc_res_cnt;
u8 num_of_pkgc;
bool has_die_c6;
u32 die_c6_offset;
struct telem_endpoint *punit_ep;
struct pmc_info *regmap_list;
@ -481,7 +485,7 @@ enum pmc_index {
/**
* struct pmc_dev_info - Structure to keep PMC device info
* @pci_func: Function number of the primary PMC
* @dmu_guid: Die Management Unit GUID
* @dmu_guids: List of Die Management Unit GUID
* @regmap_list: Pointer to a list of pmc_info structure that could be
* available for the platform. When set, this field implies
* SSRAM support.
@ -495,7 +499,7 @@ enum pmc_index {
*/
struct pmc_dev_info {
u8 pci_func;
u32 dmu_guid;
u32 *dmu_guids;
struct pmc_info *regmap_list;
const struct pmc_reg_map *map;
const struct file_operations *sub_req_show;
@ -532,7 +536,7 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore);
int pmc_core_resume_common(struct pmc_dev *pmcdev);
int get_primary_reg_base(struct pmc *pmc);
void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev);
void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid);
void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids);
void pmc_core_set_device_d3(unsigned int device);
int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info);

View File

@ -533,11 +533,11 @@ static const struct pmc_reg_map lnl_socm_reg_map = {
.s0ix_blocker_maps = lnl_blk_maps,
.s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET,
.lpm_reg_index = LNL_LPM_REG_INDEX,
.lpm_req_guid = SOCM_LPM_REQ_GUID,
};
static struct pmc_info lnl_pmc_info_list[] = {
{
.guid = SOCM_LPM_REQ_GUID,
.devid = PMC_DEVID_LNL_SOCM,
.map = &lnl_socm_reg_map,
},

View File

@ -473,6 +473,7 @@ const struct pmc_reg_map mtl_socm_reg_map = {
.lpm_status_offset = MTL_LPM_STATUS_OFFSET,
.lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET,
.lpm_reg_index = MTL_LPM_REG_INDEX,
.lpm_req_guid = SOCP_LPM_REQ_GUID,
};
static const struct pmc_bit_map mtl_ioep_pfear_map[] = {
@ -797,6 +798,7 @@ const struct pmc_reg_map mtl_ioep_reg_map = {
.lpm_en_offset = MTL_LPM_EN_OFFSET,
.lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET,
.lpm_reg_index = MTL_LPM_REG_INDEX,
.lpm_req_guid = IOEP_LPM_REQ_GUID,
};
static const struct pmc_bit_map mtl_ioem_pfear_map[] = {
@ -944,21 +946,19 @@ static const struct pmc_reg_map mtl_ioem_reg_map = {
.lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2,
.lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET,
.lpm_reg_index = MTL_LPM_REG_INDEX,
.lpm_req_guid = IOEM_LPM_REQ_GUID,
};
static struct pmc_info mtl_pmc_info_list[] = {
{
.guid = SOCP_LPM_REQ_GUID,
.devid = PMC_DEVID_MTL_SOCM,
.map = &mtl_socm_reg_map,
},
{
.guid = IOEP_LPM_REQ_GUID,
.devid = PMC_DEVID_MTL_IOEP,
.map = &mtl_ioep_reg_map,
},
{
.guid = IOEM_LPM_REQ_GUID,
.devid = PMC_DEVID_MTL_IOEM,
.map = &mtl_ioem_reg_map
},
@ -992,9 +992,10 @@ static int mtl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
return generic_core_init(pmcdev, pmc_dev_info);
}
static u32 MTL_PMT_DMU_GUIDS[] = {MTL_PMT_DMU_GUID, 0x0};
struct pmc_dev_info mtl_pmc_dev = {
.pci_func = 2,
.dmu_guid = MTL_PMT_DMU_GUID,
.dmu_guids = MTL_PMT_DMU_GUIDS,
.regmap_list = mtl_pmc_info_list,
.map = &mtl_socm_reg_map,
.sub_req_show = &pmc_core_substate_req_regs_fops,

View File

@ -528,16 +528,15 @@ static const struct pmc_reg_map ptl_pcdp_reg_map = {
.s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET,
.num_s0ix_blocker = PTL_NUM_S0IX_BLOCKER,
.blocker_req_offset = PTL_BLK_REQ_OFFSET,
.lpm_req_guid = PCDP_LPM_REQ_GUID,
};
static struct pmc_info ptl_pmc_info_list[] = {
{
.guid = PCDP_LPM_REQ_GUID,
.devid = PMC_DEVID_PTL_PCDH,
.map = &ptl_pcdp_reg_map,
},
{
.guid = PCDP_LPM_REQ_GUID,
.devid = PMC_DEVID_PTL_PCDP,
.map = &ptl_pcdp_reg_map,
},

View File

@ -11,6 +11,9 @@
#include "core.h"
/* PMC SSRAM PMT Telemetry GUIDS */
#define PCDN_LPM_REQ_GUID 0x33747648
static const struct pmc_bit_map wcl_pcdn_pfear_map[] = {
{"PMC_0", BIT(0)},
{"FUSE_OSSE", BIT(1)},
@ -453,6 +456,17 @@ static const struct pmc_reg_map wcl_pcdn_reg_map = {
.lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET,
.s0ix_blocker_maps = wcl_pcdn_blk_maps,
.s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET,
.num_s0ix_blocker = WCL_NUM_S0IX_BLOCKER,
.blocker_req_offset = WCL_BLK_REQ_OFFSET,
.lpm_req_guid = PCDN_LPM_REQ_GUID,
};
static struct pmc_info wcl_pmc_info_list[] = {
{
.devid = PMC_DEVID_WCL_PCDN,
.map = &wcl_pcdn_reg_map,
},
{}
};
#define WCL_NPU_PCI_DEV 0xfd3e
@ -479,8 +493,12 @@ static int wcl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
}
struct pmc_dev_info wcl_pmc_dev = {
.pci_func = 2,
.regmap_list = wcl_pmc_info_list,
.map = &wcl_pcdn_reg_map,
.sub_req_show = &pmc_core_substate_blk_req_fops,
.suspend = cnl_suspend,
.resume = wcl_resume,
.init = wcl_core_init,
.sub_req = pmc_core_pmt_get_blk_sub_req,
};

View File

@ -765,6 +765,7 @@ static const struct intel_vsec_platform_info lnl_info = {
#define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d
#define PCI_DEVICE_ID_INTEL_VSEC_LNL_M 0x647d
#define PCI_DEVICE_ID_INTEL_VSEC_PTL 0xb07d
#define PCI_DEVICE_ID_INTEL_VSEC_WCL 0xfd7d
static const struct pci_device_id intel_vsec_pci_ids[] = {
{ PCI_DEVICE_DATA(INTEL, VSEC_ADL, &tgl_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) },
@ -776,6 +777,7 @@ static const struct pci_device_id intel_vsec_pci_ids[] = {
{ PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_LNL_M, &lnl_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_PTL, &mtl_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_WCL, &mtl_info) },
{ }
};
MODULE_DEVICE_TABLE(pci, intel_vsec_pci_ids);

View File

@ -31,6 +31,7 @@
#include <linux/power_supply.h>
#include <linux/rfkill.h>
#include <linux/seq_file.h>
#include <linux/string_choices.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#include <linux/wmi.h>
@ -62,13 +63,27 @@ enum {
CFG_OSD_CAM_BIT = 31,
};
/*
* There are two charge modes supported by the GBMD/SBMC interface:
* - "Rapid Charge": increase power to speed up charging
* - "Conservation Mode": stop charging at 60-80% (depends on model)
*
* The interface doesn't prohibit enabling both modes at the same time.
* However, doing so is essentially meaningless, and the manufacturer utilities
* on Windows always make them mutually exclusive.
*/
enum {
GBMD_RAPID_CHARGE_STATE_BIT = 2,
GBMD_CONSERVATION_STATE_BIT = 5,
GBMD_RAPID_CHARGE_SUPPORTED_BIT = 17,
};
enum {
SBMC_CONSERVATION_ON = 3,
SBMC_CONSERVATION_OFF = 5,
SBMC_RAPID_CHARGE_ON = 7,
SBMC_RAPID_CHARGE_OFF = 8,
};
enum {
@ -158,6 +173,7 @@ struct ideapad_rfk_priv {
struct ideapad_private {
struct acpi_device *adev;
struct mutex vpc_mutex; /* protects the VPC calls */
struct mutex gbmd_sbmc_mutex; /* protects GBMD/SBMC calls */
struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
struct platform_device *platform_device;
@ -166,9 +182,11 @@ struct ideapad_private {
struct ideapad_dytc_priv *dytc;
struct dentry *debug;
struct acpi_battery_hook battery_hook;
const struct power_supply_ext *battery_ext;
unsigned long cfg;
unsigned long r_touchpad_val;
struct {
bool rapid_charge : 1;
bool conservation_mode : 1;
bool dytc : 1;
bool fan_mode : 1;
@ -455,37 +473,40 @@ static int debugfs_status_show(struct seq_file *s, void *data)
struct ideapad_private *priv = s->private;
unsigned long value;
guard(mutex)(&priv->vpc_mutex);
scoped_guard(mutex, &priv->vpc_mutex) {
if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))
seq_printf(s, "Backlight max: %lu\n", value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
seq_printf(s, "Backlight now: %lu\n", value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))
seq_printf(s, "BL power value: %s (%lu)\n", str_on_off(value), value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))
seq_printf(s, "Backlight max: %lu\n", value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
seq_printf(s, "Backlight now: %lu\n", value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))
seq_printf(s, "BL power value: %s (%lu)\n", value ? "on" : "off", value);
seq_puts(s, "=====================\n");
if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
seq_printf(s, "Radio status: %s (%lu)\n", str_on_off(value), value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))
seq_printf(s, "Wifi status: %s (%lu)\n", str_on_off(value), value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))
seq_printf(s, "BT status: %s (%lu)\n", str_on_off(value), value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))
seq_printf(s, "3G status: %s (%lu)\n", str_on_off(value), value);
seq_puts(s, "=====================\n");
if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
seq_printf(s, "Touchpad status: %s (%lu)\n", str_on_off(value), value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
seq_printf(s, "Camera status: %s (%lu)\n", str_on_off(value), value);
}
seq_puts(s, "=====================\n");
if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
seq_printf(s, "Radio status: %s (%lu)\n", value ? "on" : "off", value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))
seq_printf(s, "Wifi status: %s (%lu)\n", value ? "on" : "off", value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))
seq_printf(s, "BT status: %s (%lu)\n", value ? "on" : "off", value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))
seq_printf(s, "3G status: %s (%lu)\n", value ? "on" : "off", value);
scoped_guard(mutex, &priv->gbmd_sbmc_mutex) {
if (!eval_gbmd(priv->adev->handle, &value))
seq_printf(s, "GBMD: %#010lx\n", value);
}
seq_puts(s, "=====================\n");
if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
seq_printf(s, "Touchpad status: %s (%lu)\n", value ? "on" : "off", value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
seq_printf(s, "Camera status: %s (%lu)\n", value ? "on" : "off", value);
seq_puts(s, "=====================\n");
if (!eval_gbmd(priv->adev->handle, &value))
seq_printf(s, "GBMD: %#010lx\n", value);
if (!eval_hals(priv->adev->handle, &value))
seq_printf(s, "HALS: %#010lx\n", value);
@ -622,10 +643,16 @@ static ssize_t conservation_mode_show(struct device *dev,
show_conservation_mode_deprecation_warning(dev);
err = eval_gbmd(priv->adev->handle, &result);
if (err)
return err;
scoped_guard(mutex, &priv->gbmd_sbmc_mutex) {
err = eval_gbmd(priv->adev->handle, &result);
if (err)
return err;
}
/*
* For backward compatibility, ignore Rapid Charge while reporting the
* state of Conservation Mode.
*/
return sysfs_emit(buf, "%d\n", !!test_bit(GBMD_CONSERVATION_STATE_BIT, &result));
}
@ -643,6 +670,18 @@ static ssize_t conservation_mode_store(struct device *dev,
if (err)
return err;
guard(mutex)(&priv->gbmd_sbmc_mutex);
/*
* Prevent mutually exclusive modes from being set at the same time,
* but do not disable Rapid Charge while disabling Conservation Mode.
*/
if (priv->features.rapid_charge && state) {
err = exec_sbmc(priv->adev->handle, SBMC_RAPID_CHARGE_OFF);
if (err)
return err;
}
err = exec_sbmc(priv->adev->handle, state ? SBMC_CONSERVATION_ON : SBMC_CONSERVATION_OFF);
if (err)
return err;
@ -2007,15 +2046,39 @@ static int ideapad_psy_ext_set_prop(struct power_supply *psy,
const union power_supply_propval *val)
{
struct ideapad_private *priv = ext_data;
unsigned long op1, op2;
int err;
switch (val->intval) {
case POWER_SUPPLY_CHARGE_TYPE_FAST:
if (WARN_ON(!priv->features.rapid_charge))
return -EINVAL;
op1 = SBMC_CONSERVATION_OFF;
op2 = SBMC_RAPID_CHARGE_ON;
break;
case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE:
return exec_sbmc(priv->adev->handle, SBMC_CONSERVATION_ON);
op1 = SBMC_RAPID_CHARGE_OFF;
op2 = SBMC_CONSERVATION_ON;
break;
case POWER_SUPPLY_CHARGE_TYPE_STANDARD:
return exec_sbmc(priv->adev->handle, SBMC_CONSERVATION_OFF);
op1 = SBMC_RAPID_CHARGE_OFF;
op2 = SBMC_CONSERVATION_OFF;
break;
default:
return -EINVAL;
}
guard(mutex)(&priv->gbmd_sbmc_mutex);
/* If !rapid_charge, op1 must be SBMC_RAPID_CHARGE_OFF. Skip it. */
if (priv->features.rapid_charge) {
err = exec_sbmc(priv->adev->handle, op1);
if (err)
return err;
}
return exec_sbmc(priv->adev->handle, op2);
}
static int ideapad_psy_ext_get_prop(struct power_supply *psy,
@ -2025,14 +2088,29 @@ static int ideapad_psy_ext_get_prop(struct power_supply *psy,
union power_supply_propval *val)
{
struct ideapad_private *priv = ext_data;
bool is_rapid_charge, is_conservation;
unsigned long result;
int err;
err = eval_gbmd(priv->adev->handle, &result);
if (err)
return err;
scoped_guard(mutex, &priv->gbmd_sbmc_mutex) {
err = eval_gbmd(priv->adev->handle, &result);
if (err)
return err;
}
if (test_bit(GBMD_CONSERVATION_STATE_BIT, &result))
is_rapid_charge = (priv->features.rapid_charge &&
test_bit(GBMD_RAPID_CHARGE_STATE_BIT, &result));
is_conservation = test_bit(GBMD_CONSERVATION_STATE_BIT, &result);
if (unlikely(is_rapid_charge && is_conservation)) {
dev_err(&priv->platform_device->dev,
"unexpected charge_types: both [Fast] and [Long_Life] are enabled\n");
return -EINVAL;
}
if (is_rapid_charge)
val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
else if (is_conservation)
val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE;
else
val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
@ -2052,29 +2130,42 @@ static const enum power_supply_property ideapad_power_supply_props[] = {
POWER_SUPPLY_PROP_CHARGE_TYPES,
};
static const struct power_supply_ext ideapad_battery_ext = {
.name = "ideapad_laptop",
.properties = ideapad_power_supply_props,
.num_properties = ARRAY_SIZE(ideapad_power_supply_props),
.charge_types = (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |
BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)),
.get_property = ideapad_psy_ext_get_prop,
.set_property = ideapad_psy_ext_set_prop,
.property_is_writeable = ideapad_psy_prop_is_writeable,
};
#define DEFINE_IDEAPAD_POWER_SUPPLY_EXTENSION(_name, _charge_types) \
static const struct power_supply_ext _name = { \
.name = "ideapad_laptop", \
.properties = ideapad_power_supply_props, \
.num_properties = ARRAY_SIZE(ideapad_power_supply_props), \
.charge_types = _charge_types, \
.get_property = ideapad_psy_ext_get_prop, \
.set_property = ideapad_psy_ext_set_prop, \
.property_is_writeable = ideapad_psy_prop_is_writeable, \
}
DEFINE_IDEAPAD_POWER_SUPPLY_EXTENSION(ideapad_battery_ext_v1,
(BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |
BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE))
);
DEFINE_IDEAPAD_POWER_SUPPLY_EXTENSION(ideapad_battery_ext_v2,
(BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |
BIT(POWER_SUPPLY_CHARGE_TYPE_FAST) |
BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE))
);
static int ideapad_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)
{
struct ideapad_private *priv = container_of(hook, struct ideapad_private, battery_hook);
return power_supply_register_extension(battery, &ideapad_battery_ext,
return power_supply_register_extension(battery, priv->battery_ext,
&priv->platform_device->dev, priv);
}
static int ideapad_battery_remove(struct power_supply *battery,
struct acpi_battery_hook *hook)
{
power_supply_unregister_extension(battery, &ideapad_battery_ext);
struct ideapad_private *priv = container_of(hook, struct ideapad_private, battery_hook);
power_supply_unregister_extension(battery, priv->battery_ext);
return 0;
}
@ -2099,14 +2190,25 @@ static int ideapad_check_features(struct ideapad_private *priv)
priv->features.fan_mode = true;
if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) {
priv->features.conservation_mode = true;
priv->battery_hook.add_battery = ideapad_battery_add;
priv->battery_hook.remove_battery = ideapad_battery_remove;
priv->battery_hook.name = "Ideapad Battery Extension";
/* Not acquiring gbmd_sbmc_mutex as race condition is impossible on init */
if (!eval_gbmd(handle, &val)) {
priv->features.conservation_mode = true;
priv->features.rapid_charge = test_bit(GBMD_RAPID_CHARGE_SUPPORTED_BIT,
&val);
err = devm_battery_hook_register(&priv->platform_device->dev, &priv->battery_hook);
if (err)
return err;
priv->battery_ext = priv->features.rapid_charge
? &ideapad_battery_ext_v2
: &ideapad_battery_ext_v1;
priv->battery_hook.add_battery = ideapad_battery_add;
priv->battery_hook.remove_battery = ideapad_battery_remove;
priv->battery_hook.name = "Ideapad Battery Extension";
err = devm_battery_hook_register(&priv->platform_device->dev,
&priv->battery_hook);
if (err)
return err;
}
}
if (acpi_has_method(handle, "DYTC"))
@ -2292,6 +2394,10 @@ static int ideapad_acpi_add(struct platform_device *pdev)
if (err)
return err;
err = devm_mutex_init(&pdev->dev, &priv->gbmd_sbmc_mutex);
if (err)
return err;
err = ideapad_check_features(priv);
if (err)
return err;

View File

@ -171,14 +171,10 @@ static int lwmi_gz_profile_get(struct device *dev,
*profile = PLATFORM_PROFILE_BALANCED;
break;
case LWMI_GZ_THERMAL_MODE_PERFORMANCE:
if (priv->extreme_supported) {
*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
break;
}
*profile = PLATFORM_PROFILE_PERFORMANCE;
break;
case LWMI_GZ_THERMAL_MODE_EXTREME:
*profile = PLATFORM_PROFILE_PERFORMANCE;
*profile = PLATFORM_PROFILE_MAX_POWER;
break;
case LWMI_GZ_THERMAL_MODE_CUSTOM:
*profile = PLATFORM_PROFILE_CUSTOM;
@ -218,15 +214,11 @@ static int lwmi_gz_profile_set(struct device *dev,
case PLATFORM_PROFILE_BALANCED:
mode = LWMI_GZ_THERMAL_MODE_BALANCED;
break;
case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
case PLATFORM_PROFILE_PERFORMANCE:
mode = LWMI_GZ_THERMAL_MODE_PERFORMANCE;
break;
case PLATFORM_PROFILE_PERFORMANCE:
if (priv->extreme_supported) {
mode = LWMI_GZ_THERMAL_MODE_EXTREME;
break;
}
mode = LWMI_GZ_THERMAL_MODE_PERFORMANCE;
case PLATFORM_PROFILE_MAX_POWER:
mode = LWMI_GZ_THERMAL_MODE_EXTREME;
break;
case PLATFORM_PROFILE_CUSTOM:
mode = LWMI_GZ_THERMAL_MODE_CUSTOM;
@ -274,8 +266,23 @@ static const struct dmi_system_id fwbug_list[] = {
},
.driver_data = &quirk_no_extreme_bug,
},
{
.ident = "Legion Go 8ASP2",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Legion Go 8ASP2"),
},
.driver_data = &quirk_no_extreme_bug,
},
{
.ident = "Legion Go 8AHP2",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Legion Go 8AHP2"),
},
.driver_data = &quirk_no_extreme_bug,
},
{},
};
/**
@ -338,7 +345,7 @@ static int lwmi_gz_platform_profile_probe(void *drvdata, unsigned long *choices)
priv->extreme_supported = lwmi_gz_extreme_supported(profile_support_ver);
if (priv->extreme_supported)
set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
set_bit(PLATFORM_PROFILE_MAX_POWER, choices);
return 0;
}

View File

@ -19,6 +19,7 @@
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/string_choices.h>
#include <linux/types.h>
#include <acpi/battery.h>
@ -42,6 +43,7 @@ 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_HD_AUDIO_POWER_ADDR 0x01
#define LG_ADDRESS_SPACE_FAN_MODE_ADR 0x03
#define LG_ADDRESS_SPACE_DTTM_FLAG_ADR 0x20
@ -668,6 +670,15 @@ static acpi_status lg_laptop_address_space_write(struct device *dev, acpi_physic
byte = value & 0xFF;
switch (address) {
case LG_ADDRESS_SPACE_HD_AUDIO_POWER_ADDR:
/*
* The HD audio power field is not affected by the DTTM flag,
* so we have to manually check fw_debug.
*/
if (fw_debug)
dev_dbg(dev, "HD audio power %s\n", str_enabled_disabled(byte));
return AE_OK;
case LG_ADDRESS_SPACE_FAN_MODE_ADR:
/*
* The fan mode field is not affected by the DTTM flag, so we

View File

@ -1,8 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Platform driver for OneXPlayer and AOKZOE devices. For the time being,
* it also exposes fan controls for AYANEO, and OrangePi Handhelds via
* hwmon sysfs.
* Platform driver for OneXPlayer and AOKZOE devices.
*
* Fan control is provided via pwm interface in the range [0-255].
* Old AMD boards use [0-100] as range in the EC, the written value is
@ -43,14 +41,6 @@ static bool unlock_global_acpi_lock(void)
enum oxp_board {
aok_zoe_a1 = 1,
aya_neo_2,
aya_neo_air,
aya_neo_air_1s,
aya_neo_air_plus_mendo,
aya_neo_air_pro,
aya_neo_flip,
aya_neo_geek,
aya_neo_kun,
orange_pi_neo,
oxp_2,
oxp_fly,
@ -131,62 +121,6 @@ static const struct dmi_system_id dmi_table[] = {
},
.driver_data = (void *)oxp_fly,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_MATCH(DMI_BOARD_NAME, "AYANEO 2"),
},
.driver_data = (void *)aya_neo_2,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"),
},
.driver_data = (void *)aya_neo_air,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR 1S"),
},
.driver_data = (void *)aya_neo_air_1s,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "AB05-Mendocino"),
},
.driver_data = (void *)aya_neo_air_plus_mendo,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"),
},
.driver_data = (void *)aya_neo_air_pro,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_MATCH(DMI_BOARD_NAME, "FLIP"),
},
.driver_data = (void *)aya_neo_flip,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_MATCH(DMI_BOARD_NAME, "GEEK"),
},
.driver_data = (void *)aya_neo_geek,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "KUN"),
},
.driver_data = (void *)aya_neo_kun,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "OrangePi"),
@ -672,13 +606,6 @@ static int oxp_pwm_enable(void)
case orange_pi_neo:
return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
case aok_zoe_a1:
case aya_neo_2:
case aya_neo_air:
case aya_neo_air_plus_mendo:
case aya_neo_air_pro:
case aya_neo_flip:
case aya_neo_geek:
case aya_neo_kun:
case oxp_2:
case oxp_fly:
case oxp_mini_amd:
@ -699,14 +626,6 @@ static int oxp_pwm_disable(void)
case orange_pi_neo:
return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
case aok_zoe_a1:
case aya_neo_2:
case aya_neo_air:
case aya_neo_air_1s:
case aya_neo_air_plus_mendo:
case aya_neo_air_pro:
case aya_neo_flip:
case aya_neo_geek:
case aya_neo_kun:
case oxp_2:
case oxp_fly:
case oxp_mini_amd:
@ -727,14 +646,6 @@ static int oxp_pwm_read(long *val)
case orange_pi_neo:
return read_from_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, 1, val);
case aok_zoe_a1:
case aya_neo_2:
case aya_neo_air:
case aya_neo_air_1s:
case aya_neo_air_plus_mendo:
case aya_neo_air_pro:
case aya_neo_flip:
case aya_neo_geek:
case aya_neo_kun:
case oxp_2:
case oxp_fly:
case oxp_mini_amd:
@ -774,14 +685,6 @@ static int oxp_pwm_fan_speed(long *val)
case oxp_g1_i:
return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val);
case aok_zoe_a1:
case aya_neo_2:
case aya_neo_air:
case aya_neo_air_1s:
case aya_neo_air_plus_mendo:
case aya_neo_air_pro:
case aya_neo_flip:
case aya_neo_geek:
case aya_neo_kun:
case oxp_fly:
case oxp_mini_amd:
case oxp_mini_amd_a07:
@ -810,14 +713,6 @@ static int oxp_pwm_input_write(long val)
/* scale to range [0-184] */
val = (val * 184) / 255;
return write_to_ec(OXP_SENSOR_PWM_REG, val);
case aya_neo_2:
case aya_neo_air:
case aya_neo_air_1s:
case aya_neo_air_plus_mendo:
case aya_neo_air_pro:
case aya_neo_flip:
case aya_neo_geek:
case aya_neo_kun:
case oxp_mini_amd:
case oxp_mini_amd_a07:
/* scale to range [0-100] */
@ -854,14 +749,6 @@ static int oxp_pwm_input_read(long *val)
/* scale from range [0-184] */
*val = (*val * 255) / 184;
break;
case aya_neo_2:
case aya_neo_air:
case aya_neo_air_1s:
case aya_neo_air_plus_mendo:
case aya_neo_air_pro:
case aya_neo_flip:
case aya_neo_geek:
case aya_neo_kun:
case oxp_mini_amd:
case oxp_mini_amd_a07:
ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);

View File

@ -22,6 +22,7 @@
#define IRQ_RESOURCE_GPIO 1
#define IRQ_RESOURCE_APIC 2
#define IRQ_RESOURCE_AUTO 3
#define IRQ_RESOURCE_OPT BIT(2)
enum smi_bus_type {
SMI_I2C,
@ -64,6 +65,10 @@ static int smi_get_irq(struct platform_device *pdev, struct acpi_device *adev,
dev_dbg(&pdev->dev, "Using platform irq\n");
break;
}
if (inst->flags & IRQ_RESOURCE_OPT) {
dev_dbg(&pdev->dev, "No irq\n");
return 0;
}
break;
case IRQ_RESOURCE_GPIO:
ret = acpi_dev_gpio_irq_get(adev, inst->irq_idx);
@ -386,10 +391,10 @@ static const struct smi_node cs35l57_hda = {
static const struct smi_node tas2781_hda = {
.instances = {
{ "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
{ "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
{ "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
{ "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
{ "tas2781-hda", IRQ_RESOURCE_AUTO | IRQ_RESOURCE_OPT, 0 },
{ "tas2781-hda", IRQ_RESOURCE_AUTO | IRQ_RESOURCE_OPT, 0 },
{ "tas2781-hda", IRQ_RESOURCE_AUTO | IRQ_RESOURCE_OPT, 0 },
{ "tas2781-hda", IRQ_RESOURCE_AUTO | IRQ_RESOURCE_OPT, 0 },
{}
},
.bus_type = SMI_AUTO_DETECT,

View File

@ -0,0 +1,38 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Uniwill X86 Platform Specific Drivers
#
menuconfig X86_PLATFORM_DRIVERS_UNIWILL
bool "Uniwill X86 Platform Specific Device Drivers"
depends on X86_PLATFORM_DEVICES
help
Say Y here to see options for device drivers for various
Uniwill x86 platforms, including many OEM laptops originally
manufactured by Uniwill.
This option alone does not add any kernel code.
If you say N, all options in this submenu will be skipped and disabled.
if X86_PLATFORM_DRIVERS_UNIWILL
config UNIWILL_LAPTOP
tristate "Uniwill Laptop Extras"
default m
depends on ACPI
depends on ACPI_WMI
depends on ACPI_BATTERY
depends on HWMON
depends on INPUT
depends on LEDS_CLASS_MULTICOLOR
depends on DMI
select REGMAP
select INPUT_SPARSEKMAP
help
This driver adds support for various extra features found on Uniwill laptops,
like the lightbar, hwmon sensors and hotkeys. It also supports many OEM laptops
originally manufactured by Uniwill.
If you have such a laptop, say Y or M here.
endif

View File

@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Makefile for linux/drivers/platform/x86/uniwill
# Uniwill X86 Platform Specific Drivers
#
obj-$(CONFIG_UNIWILL_LAPTOP) += uniwill-laptop.o
uniwill-laptop-y := uniwill-acpi.o uniwill-wmi.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Linux hotkey driver for Uniwill notebooks.
*
* Special thanks go to Pőcze Barnabás, Christoffer Sandberg and Werner Sembach
* for supporting the development of this driver either through prior work or
* by answering questions regarding the underlying WMI interface.
*
* Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
#include <linux/notifier.h>
#include <linux/printk.h>
#include <linux/types.h>
#include <linux/wmi.h>
#include "uniwill-wmi.h"
#define DRIVER_NAME "uniwill-wmi"
#define UNIWILL_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000"
static BLOCKING_NOTIFIER_HEAD(uniwill_wmi_chain_head);
static void devm_uniwill_wmi_unregister_notifier(void *data)
{
struct notifier_block *nb = data;
blocking_notifier_chain_unregister(&uniwill_wmi_chain_head, nb);
}
int devm_uniwill_wmi_register_notifier(struct device *dev, struct notifier_block *nb)
{
int ret;
ret = blocking_notifier_chain_register(&uniwill_wmi_chain_head, nb);
if (ret < 0)
return ret;
return devm_add_action_or_reset(dev, devm_uniwill_wmi_unregister_notifier, nb);
}
static void uniwill_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
{
u32 value;
if (obj->type != ACPI_TYPE_INTEGER)
return;
value = obj->integer.value;
dev_dbg(&wdev->dev, "Received WMI event %u\n", value);
blocking_notifier_call_chain(&uniwill_wmi_chain_head, value, NULL);
}
/*
* We cannot fully trust this GUID since Uniwill just copied the WMI GUID
* from the Windows driver example, and others probably did the same.
*
* Because of this we cannot use this WMI GUID for autoloading. Instead the
* associated driver will be registered manually after matching a DMI table.
*/
static const struct wmi_device_id uniwill_wmi_id_table[] = {
{ UNIWILL_EVENT_GUID, NULL },
{ }
};
static struct wmi_driver uniwill_wmi_driver = {
.driver = {
.name = DRIVER_NAME,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = uniwill_wmi_id_table,
.notify = uniwill_wmi_notify,
.no_singleton = true,
};
int __init uniwill_wmi_register_driver(void)
{
return wmi_driver_register(&uniwill_wmi_driver);
}
void __exit uniwill_wmi_unregister_driver(void)
{
wmi_driver_unregister(&uniwill_wmi_driver);
}

View File

@ -0,0 +1,129 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Linux hotkey driver for Uniwill notebooks.
*
* Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de>
*/
#ifndef UNIWILL_WMI_H
#define UNIWILL_WMI_H
#include <linux/init.h>
#define UNIWILL_OSD_CAPSLOCK 0x01
#define UNIWILL_OSD_NUMLOCK 0x02
#define UNIWILL_OSD_SCROLLLOCK 0x03
#define UNIWILL_OSD_TOUCHPAD_ON 0x04
#define UNIWILL_OSD_TOUCHPAD_OFF 0x05
#define UNIWILL_OSD_SILENT_MODE_ON 0x06
#define UNIWILL_OSD_SILENT_MODE_OFF 0x07
#define UNIWILL_OSD_WLAN_ON 0x08
#define UNIWILL_OSD_WLAN_OFF 0x09
#define UNIWILL_OSD_WIMAX_ON 0x0A
#define UNIWILL_OSD_WIMAX_OFF 0x0B
#define UNIWILL_OSD_BLUETOOTH_ON 0x0C
#define UNIWILL_OSD_BLUETOOTH_OFF 0x0D
#define UNIWILL_OSD_RF_ON 0x0E
#define UNIWILL_OSD_RF_OFF 0x0F
#define UNIWILL_OSD_3G_ON 0x10
#define UNIWILL_OSD_3G_OFF 0x11
#define UNIWILL_OSD_WEBCAM_ON 0x12
#define UNIWILL_OSD_WEBCAM_OFF 0x13
#define UNIWILL_OSD_BRIGHTNESSUP 0x14
#define UNIWILL_OSD_BRIGHTNESSDOWN 0x15
#define UNIWILL_OSD_RADIOON 0x1A
#define UNIWILL_OSD_RADIOOFF 0x1B
#define UNIWILL_OSD_POWERSAVE_ON 0x31
#define UNIWILL_OSD_POWERSAVE_OFF 0x32
#define UNIWILL_OSD_MENU 0x34
#define UNIWILL_OSD_MUTE 0x35
#define UNIWILL_OSD_VOLUMEDOWN 0x36
#define UNIWILL_OSD_VOLUMEUP 0x37
#define UNIWILL_OSD_MENU_2 0x38
#define UNIWILL_OSD_LIGHTBAR_ON 0x39
#define UNIWILL_OSD_LIGHTBAR_OFF 0x3A
#define UNIWILL_OSD_KB_LED_LEVEL0 0x3B
#define UNIWILL_OSD_KB_LED_LEVEL1 0x3C
#define UNIWILL_OSD_KB_LED_LEVEL2 0x3D
#define UNIWILL_OSD_KB_LED_LEVEL3 0x3E
#define UNIWILL_OSD_KB_LED_LEVEL4 0x3F
#define UNIWILL_OSD_SUPER_KEY_LOCK_ENABLE 0x40
#define UNIWILL_OSD_SUPER_KEY_LOCK_DISABLE 0x41
#define UNIWILL_OSD_MENU_JP 0x42
#define UNIWILL_OSD_CAMERA_ON 0x90
#define UNIWILL_OSD_CAMERA_OFF 0x91
#define UNIWILL_OSD_RFKILL 0xA4
#define UNIWILL_OSD_SUPER_KEY_LOCK_CHANGED 0xA5
#define UNIWILL_OSD_LIGHTBAR_STATE_CHANGED 0xA6
#define UNIWILL_OSD_FAN_BOOST_STATE_CHANGED 0xA7
#define UNIWILL_OSD_LCD_SW 0xA9
#define UNIWILL_OSD_FAN_OVERTEMP 0xAA
#define UNIWILL_OSD_DC_ADAPTER_CHANGED 0xAB
#define UNIWILL_OSD_BAT_HP_OFF 0xAC
#define UNIWILL_OSD_FAN_DOWN_TEMP 0xAD
#define UNIWILL_OSD_BATTERY_ALERT 0xAE
#define UNIWILL_OSD_TIMAP_HAIERLB_SW 0xAF
#define UNIWILL_OSD_PERFORMANCE_MODE_TOGGLE 0xB0
#define UNIWILL_OSD_KBDILLUMDOWN 0xB1
#define UNIWILL_OSD_KBDILLUMUP 0xB2
#define UNIWILL_OSD_BACKLIGHT_LEVEL_CHANGE 0xB3
#define UNIWILL_OSD_BACKLIGHT_POWER_CHANGE 0xB4
#define UNIWILL_OSD_MIC_MUTE 0xB7
#define UNIWILL_OSD_FN_LOCK 0xB8
#define UNIWILL_OSD_KBDILLUMTOGGLE 0xB9
#define UNIWILL_OSD_BAT_CHARGE_FULL_24_H 0xBE
#define UNIWILL_OSD_BAT_ERM_UPDATE 0xBF
#define UNIWILL_OSD_BENCHMARK_MODE_TOGGLE 0xC0
#define UNIWILL_OSD_WEBCAM_TOGGLE 0xCF
#define UNIWILL_OSD_KBD_BACKLIGHT_CHANGED 0xF0
struct device;
struct notifier_block;
int devm_uniwill_wmi_register_notifier(struct device *dev, struct notifier_block *nb);
int __init uniwill_wmi_register_driver(void);
void __exit uniwill_wmi_unregister_driver(void);
#endif /* UNIWILL_WMI_H */

View File

@ -543,7 +543,7 @@ static int __init lenovo_yoga_tab2_830_1050_init_codec(void)
ret = device_add_software_node(codec_dev, &lenovo_yoga_tab2_830_1050_wm5102);
if (ret) {
ret = dev_err_probe(codec_dev, ret, "adding software node\n");
dev_err_probe(codec_dev, ret, "adding software node\n");
goto err_put_pinctrl;
}

View File

@ -183,7 +183,7 @@ static void atla10_ec_external_power_changed(struct power_supply *psy)
struct atla10_ec_data *data = power_supply_get_drvdata(psy);
/* After charger plug in/out wait 0.5s for things to stabilize */
mod_delayed_work(system_wq, &data->work, HZ / 2);
mod_delayed_work(system_percpu_wq, &data->work, HZ / 2);
}
static const enum power_supply_property atla10_ec_psy_props[] = {

View File

@ -67,19 +67,22 @@ int utf8_to_utf32(const u8 *s, int inlen, unicode_t *pu)
l &= t->lmask;
if (l < t->lval || l > UNICODE_MAX ||
(l & SURROGATE_MASK) == SURROGATE_PAIR)
return -1;
return -EILSEQ;
*pu = (unicode_t) l;
return nc;
}
if (inlen <= nc)
return -1;
return -EOVERFLOW;
s++;
c = (*s ^ 0x80) & 0xFF;
if (c & 0xC0)
return -1;
return -EILSEQ;
l = (l << 6) | c;
}
return -1;
return -EILSEQ;
}
EXPORT_SYMBOL(utf8_to_utf32);
@ -94,7 +97,7 @@ int utf32_to_utf8(unicode_t u, u8 *s, int maxout)
l = u;
if (l > UNICODE_MAX || (l & SURROGATE_MASK) == SURROGATE_PAIR)
return -1;
return -EILSEQ;
nc = 0;
for (t = utf8_table; t->cmask && maxout; t++, maxout--) {
@ -110,7 +113,7 @@ int utf32_to_utf8(unicode_t u, u8 *s, int maxout)
return nc;
}
}
return -1;
return -EOVERFLOW;
}
EXPORT_SYMBOL(utf32_to_utf8);
@ -217,8 +220,16 @@ int utf16s_to_utf8s(const wchar_t *pwcs, int inlen, enum utf16_endian endian,
inlen--;
}
size = utf32_to_utf8(u, op, maxout);
if (size == -1) {
/* Ignore character and move on */
if (size < 0) {
if (size == -EILSEQ) {
/* Ignore character and move on */
continue;
}
/*
* Stop filling the buffer with data once a character
* does not fit anymore.
*/
break;
} else {
op += size;
maxout -= size;

View File

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H
#define __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H
#include <linux/dmi.h>
#include <linux/types.h>
/* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */
#if IS_REACHABLE(CONFIG_ASUS_WMI) || IS_REACHABLE(CONFIG_HID_ASUS)
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_PRODUCT_FAMILY, "ProArt P16"),
},
},
{
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "GA403U"),
},
},
{
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "GU605M"),
},
},
{
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "RC71L"),
},
},
{ },
};
#endif
#endif /* __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H */

View File

@ -4,7 +4,9 @@
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/dmi.h>
#define ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
#define ASUS_ACPI_UID_ASUSWMI "ASUSWMI"
/* WMI Methods */
#define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */
@ -73,12 +75,14 @@
#define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO 0x00110019
/* Misc */
#define ASUS_WMI_DEVID_PANEL_HD 0x0005001C
#define ASUS_WMI_DEVID_PANEL_OD 0x00050019
#define ASUS_WMI_DEVID_CAMERA 0x00060013
#define ASUS_WMI_DEVID_LID_FLIP 0x00060062
#define ASUS_WMI_DEVID_LID_FLIP_ROG 0x00060077
#define ASUS_WMI_DEVID_MINI_LED_MODE 0x0005001E
#define ASUS_WMI_DEVID_MINI_LED_MODE2 0x0005002E
#define ASUS_WMI_DEVID_SCREEN_AUTO_BRIGHTNESS 0x0005002A
/* Storage */
#define ASUS_WMI_DEVID_CARDREADER 0x00080013
@ -103,7 +107,7 @@
#define ASUS_WMI_DEVID_PPT_PL1_SPL 0x001200A3
#define ASUS_WMI_DEVID_PPT_APU_SPPT 0x001200B0
#define ASUS_WMI_DEVID_PPT_PLAT_SPPT 0x001200B1
#define ASUS_WMI_DEVID_PPT_FPPT 0x001200C1
#define ASUS_WMI_DEVID_PPT_PL3_FPPT 0x001200C1
#define ASUS_WMI_DEVID_NV_DYN_BOOST 0x001200C0
#define ASUS_WMI_DEVID_NV_THERM_TARGET 0x001200C2
@ -133,6 +137,11 @@
/* dgpu on/off */
#define ASUS_WMI_DEVID_DGPU 0x00090020
#define ASUS_WMI_DEVID_APU_MEM 0x000600C1
#define ASUS_WMI_DEVID_DGPU_BASE_TGP 0x00120099
#define ASUS_WMI_DEVID_DGPU_SET_TGP 0x00120098
/* gpu mux switch, 0 = dGPU, 1 = Optimus */
#define ASUS_WMI_DEVID_GPU_MUX 0x00090016
#define ASUS_WMI_DEVID_GPU_MUX_VIVO 0x00090026
@ -166,6 +175,7 @@ enum asus_ally_mcu_hack {
#if IS_REACHABLE(CONFIG_ASUS_WMI)
void set_ally_mcu_hack(enum asus_ally_mcu_hack status);
void set_ally_mcu_powersave(bool enabled);
int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval);
int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval);
int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval);
#else
@ -179,6 +189,10 @@ static inline int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval)
{
return -ENODEV;
}
static inline int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval)
{
return -ENODEV;
}
static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
u32 *retval)
{
@ -186,44 +200,4 @@ static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
}
#endif
/* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */
static const struct dmi_system_id asus_use_hid_led_dmi_ids[] = {
{
.matches = {
DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Zephyrus"),
},
},
{
.matches = {
DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Strix"),
},
},
{
.matches = {
DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Flow"),
},
},
{
.matches = {
DMI_MATCH(DMI_PRODUCT_FAMILY, "ProArt P16"),
},
},
{
.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 */

View File

@ -9,6 +9,7 @@
#ifndef INTEL_PMC_IPC_H
#define INTEL_PMC_IPC_H
#include <linux/acpi.h>
#include <linux/cleanup.h>
#define IPC_SOC_REGISTER_ACCESS 0xAA
#define IPC_SOC_SUB_CMD_READ 0x00
@ -48,7 +49,6 @@ static inline int intel_pmc_ipc(struct pmc_ipc_cmd *ipc_cmd, struct pmc_ipc_rbuf
{.type = ACPI_TYPE_INTEGER,},
};
struct acpi_object_list arg_list = { PMC_IPCS_PARAM_COUNT, params };
union acpi_object *obj;
int status;
if (!ipc_cmd || !rbuf)
@ -72,7 +72,7 @@ static inline int intel_pmc_ipc(struct pmc_ipc_cmd *ipc_cmd, struct pmc_ipc_rbuf
if (ACPI_FAILURE(status))
return -ENODEV;
obj = buffer.pointer;
union acpi_object *obj __free(kfree) = buffer.pointer;
if (obj && obj->type == ACPI_TYPE_PACKAGE &&
obj->package.count == VALID_IPC_RESPONSE) {

View File

@ -24,6 +24,7 @@ enum platform_profile_option {
PLATFORM_PROFILE_BALANCED,
PLATFORM_PROFILE_BALANCED_PERFORMANCE,
PLATFORM_PROFILE_PERFORMANCE,
PLATFORM_PROFILE_MAX_POWER,
PLATFORM_PROFILE_CUSTOM,
PLATFORM_PROFILE_LAST, /*must always be last */
};

View File

@ -36,13 +36,10 @@ struct wmi_device {
*/
#define to_wmi_device(device) container_of_const(device, struct wmi_device, dev)
extern acpi_status wmidev_evaluate_method(struct wmi_device *wdev,
u8 instance, u32 method_id,
const struct acpi_buffer *in,
struct acpi_buffer *out);
acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 method_id,
const struct acpi_buffer *in, struct acpi_buffer *out);
extern union acpi_object *wmidev_block_query(struct wmi_device *wdev,
u8 instance);
union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance);
acpi_status wmidev_block_set(struct wmi_device *wdev, u8 instance, const struct acpi_buffer *in);
@ -81,9 +78,9 @@ struct wmi_driver {
*/
#define to_wmi_driver(drv) container_of_const(drv, struct wmi_driver, driver)
extern int __must_check __wmi_driver_register(struct wmi_driver *driver,
struct module *owner);
extern void wmi_driver_unregister(struct wmi_driver *driver);
int __must_check __wmi_driver_register(struct wmi_driver *driver, struct module *owner);
void wmi_driver_unregister(struct wmi_driver *driver);
/**
* wmi_driver_register() - Helper macro to register a WMI driver

View File

@ -16,7 +16,7 @@ struct process_cmd_struct {
int arg;
};
static const char *version_str = "v1.23";
static const char *version_str = "v1.24";
static const int supported_api_ver = 3;
static struct isst_if_platform_info isst_platform_info;

View File

@ -452,13 +452,16 @@ static int tpmi_get_pbf_info(struct isst_id *id, int level,
return _pbf_get_coremask_info(id, level, pbf_info);
}
#define FEATURE_ENABLE_WAIT_US 1000
#define FEATURE_ENABLE_RETRIES 5
static int tpmi_set_pbf_fact_status(struct isst_id *id, int pbf, int enable)
{
struct isst_pkg_ctdp pkg_dev;
struct isst_pkg_ctdp_level_info ctdp_level;
int current_level;
struct isst_perf_feature_control info;
int ret;
int ret, i;
ret = isst_get_ctdp_levels(id, &pkg_dev);
if (ret)
@ -503,6 +506,30 @@ static int tpmi_set_pbf_fact_status(struct isst_id *id, int pbf, int enable)
if (ret == -1)
return ret;
for (i = 0; i < FEATURE_ENABLE_RETRIES; ++i) {
usleep(FEATURE_ENABLE_WAIT_US);
/* Check status */
ret = isst_get_ctdp_control(id, current_level, &ctdp_level);
if (ret)
return ret;
debug_printf("pbf_enabled:%d fact_enabled:%d\n",
ctdp_level.pbf_enabled, ctdp_level.fact_enabled);
if (pbf) {
if (ctdp_level.pbf_enabled == enable)
break;
} else {
if (ctdp_level.fact_enabled == enable)
break;
}
}
if (i == FEATURE_ENABLE_RETRIES)
return -1;
return 0;
}
@ -513,6 +540,7 @@ static int tpmi_get_fact_info(struct isst_id *id, int level, int fact_bucket,
int i, j;
int ret;
memset(&info, 0, sizeof(info));
info.socket_id = id->pkg;
info.power_domain_id = id->punit;
info.level = level;
@ -659,7 +687,8 @@ static int tpmi_pm_qos_config(struct isst_id *id, int enable_clos,
int priority_type)
{
struct isst_core_power info;
int i, ret, saved_punit;
int cp_state = 0, cp_cap = 0;
int i, j, ret, saved_punit;
info.get_set = 1;
info.socket_id = id->pkg;
@ -679,6 +708,19 @@ static int tpmi_pm_qos_config(struct isst_id *id, int enable_clos,
id->punit = saved_punit;
return ret;
}
/* Get status */
for (j = 0; j < FEATURE_ENABLE_RETRIES; ++j) {
usleep(FEATURE_ENABLE_WAIT_US);
ret = tpmi_read_pm_config(id, &cp_state, &cp_cap);
debug_printf("ret:%d cp_state:%d enable_clos:%d\n", ret,
cp_state, enable_clos);
if (ret || cp_state == enable_clos)
break;
}
if (j == FEATURE_ENABLE_RETRIES) {
id->punit = saved_punit;
return -1;
}
}
}