Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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
...

+7111 -672
+2
Documentation/ABI/testing/sysfs-class-platform-profile
··· 23 23 power consumption with a slight bias 24 24 towards performance 25 25 performance High performance operation 26 + max-power Higher performance operation that may exceed 27 + internal battery draw limits when on AC power 26 28 custom Driver defined custom profile 27 29 ==================== ======================================== 28 30
+53
Documentation/ABI/testing/sysfs-driver-uniwill-laptop
··· 1 + What: /sys/bus/platform/devices/INOU0000:XX/fn_lock_toggle_enable 2 + Date: November 2025 3 + KernelVersion: 6.19 4 + Contact: Armin Wolf <W_Armin@gmx.de> 5 + Description: 6 + Allows userspace applications to enable/disable the FN lock feature 7 + of the integrated keyboard by writing "1"/"0" into this file. 8 + 9 + Reading this file returns the current enable status of the FN lock functionality. 10 + 11 + What: /sys/bus/platform/devices/INOU0000:XX/super_key_toggle_enable 12 + Date: November 2025 13 + KernelVersion: 6.19 14 + Contact: Armin Wolf <W_Armin@gmx.de> 15 + Description: 16 + Allows userspace applications to enable/disable the super key functionality 17 + of the integrated keyboard by writing "1"/"0" into this file. 18 + 19 + Reading this file returns the current enable status of the super key functionality. 20 + 21 + What: /sys/bus/platform/devices/INOU0000:XX/touchpad_toggle_enable 22 + Date: November 2025 23 + KernelVersion: 6.19 24 + Contact: Armin Wolf <W_Armin@gmx.de> 25 + Description: 26 + Allows userspace applications to enable/disable the touchpad toggle functionality 27 + of the integrated touchpad by writing "1"/"0" into this file. 28 + 29 + Reading this file returns the current enable status of the touchpad toggle 30 + functionality. 31 + 32 + What: /sys/bus/platform/devices/INOU0000:XX/rainbow_animation 33 + Date: November 2025 34 + KernelVersion: 6.19 35 + Contact: Armin Wolf <W_Armin@gmx.de> 36 + Description: 37 + Forces the integrated lightbar to display a rainbow animation when the machine 38 + is not suspended. Writing "1"/"0" into this file enables/disables this 39 + functionality. 40 + 41 + Reading this file returns the current status of the rainbow animation functionality. 42 + 43 + What: /sys/bus/platform/devices/INOU0000:XX/breathing_in_suspend 44 + Date: November 2025 45 + KernelVersion: 6.19 46 + Contact: Armin Wolf <W_Armin@gmx.de> 47 + Description: 48 + Causes the integrated lightbar to display a breathing animation when the machine 49 + has been suspended and is running on AC power. Writing "1"/"0" into this file 50 + enables/disables this functionality. 51 + 52 + Reading this file returns the current status of the breathing animation 53 + functionality.
+17
Documentation/ABI/testing/sysfs-platform-asus-wmi
··· 63 63 KernelVersion: 6.1 64 64 Contact: "Luke Jones" <luke@ljones.dev> 65 65 Description: 66 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 66 67 Switch the GPU hardware MUX mode. Laptops with this feature can 67 68 can be toggled to boot with only the dGPU (discrete mode) or in 68 69 standard Optimus/Hybrid mode. On switch a reboot is required: ··· 76 75 KernelVersion: 5.17 77 76 Contact: "Luke Jones" <luke@ljones.dev> 78 77 Description: 78 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 79 79 Disable discrete GPU: 80 80 * 0 - Enable dGPU, 81 81 * 1 - Disable dGPU ··· 86 84 KernelVersion: 5.17 87 85 Contact: "Luke Jones" <luke@ljones.dev> 88 86 Description: 87 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 89 88 Enable the external GPU paired with ROG X-Flow laptops. 90 89 Toggling this setting will also trigger ACPI to disable the dGPU: 91 90 ··· 98 95 KernelVersion: 5.17 99 96 Contact: "Luke Jones" <luke@ljones.dev> 100 97 Description: 98 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 101 99 Enable an LCD response-time boost to reduce or remove ghosting: 102 100 * 0 - Disable, 103 101 * 1 - Enable ··· 108 104 KernelVersion: 6.5 109 105 Contact: "Luke Jones" <luke@ljones.dev> 110 106 Description: 107 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 111 108 Get the current charging mode being used: 112 109 * 1 - Barrel connected charger, 113 110 * 2 - USB-C charging ··· 119 114 KernelVersion: 6.5 120 115 Contact: "Luke Jones" <luke@ljones.dev> 121 116 Description: 117 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 122 118 Show if the egpu (XG Mobile) is correctly connected: 123 119 * 0 - False, 124 120 * 1 - True ··· 129 123 KernelVersion: 6.5 130 124 Contact: "Luke Jones" <luke@ljones.dev> 131 125 Description: 126 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 132 127 Change the mini-LED mode: 133 128 * 0 - Single-zone, 134 129 * 1 - Multi-zone ··· 140 133 KernelVersion: 6.10 141 134 Contact: "Luke Jones" <luke@ljones.dev> 142 135 Description: 136 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 143 137 List the available mini-led modes. 144 138 145 139 What: /sys/devices/platform/<platform>/ppt_pl1_spl ··· 148 140 KernelVersion: 6.5 149 141 Contact: "Luke Jones" <luke@ljones.dev> 150 142 Description: 143 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 151 144 Set the Package Power Target total of CPU: PL1 on Intel, SPL on AMD. 152 145 Shown on Intel+Nvidia or AMD+Nvidia based systems: 153 146 ··· 159 150 KernelVersion: 6.5 160 151 Contact: "Luke Jones" <luke@ljones.dev> 161 152 Description: 153 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 162 154 Set the Slow Package Power Tracking Limit of CPU: PL2 on Intel, SPPT, 163 155 on AMD. Shown on Intel+Nvidia or AMD+Nvidia based systems: 164 156 ··· 170 160 KernelVersion: 6.5 171 161 Contact: "Luke Jones" <luke@ljones.dev> 172 162 Description: 163 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 173 164 Set the Fast Package Power Tracking Limit of CPU. AMD+Nvidia only: 174 165 * min=5, max=250 175 166 ··· 179 168 KernelVersion: 6.5 180 169 Contact: "Luke Jones" <luke@ljones.dev> 181 170 Description: 171 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 182 172 Set the APU SPPT limit. Shown on full AMD systems only: 183 173 * min=5, max=130 184 174 ··· 188 176 KernelVersion: 6.5 189 177 Contact: "Luke Jones" <luke@ljones.dev> 190 178 Description: 179 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 191 180 Set the platform SPPT limit. Shown on full AMD systems only: 192 181 * min=5, max=130 193 182 ··· 197 184 KernelVersion: 6.5 198 185 Contact: "Luke Jones" <luke@ljones.dev> 199 186 Description: 187 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 200 188 Set the dynamic boost limit of the Nvidia dGPU: 201 189 * min=5, max=25 202 190 ··· 206 192 KernelVersion: 6.5 207 193 Contact: "Luke Jones" <luke@ljones.dev> 208 194 Description: 195 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 209 196 Set the target temperature limit of the Nvidia dGPU: 210 197 * min=75, max=87 211 198 ··· 215 200 KernelVersion: 6.10 216 201 Contact: "Luke Jones" <luke@ljones.dev> 217 202 Description: 203 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 218 204 Set if the BIOS POST sound is played on boot. 219 205 * 0 - False, 220 206 * 1 - True ··· 225 209 KernelVersion: 6.10 226 210 Contact: "Luke Jones" <luke@ljones.dev> 227 211 Description: 212 + DEPRECATED, WILL BE REMOVED SOON: please use asus-armoury 228 213 Set if the MCU can go in to low-power mode on system sleep 229 214 * 0 - False, 230 215 * 1 - True
+19
Documentation/ABI/testing/sysfs-platform-ayaneo-ec
··· 1 + What: /sys/devices/platform/ayaneo-ec/controller_power 2 + Date: Nov 2025 3 + KernelVersion: 6.19 4 + Contact: "Antheas Kapenekakis" <lkml@antheas.dev> 5 + Description: 6 + Current controller power state. Allows turning on and off 7 + the controller power (e.g. for power savings). Write 1 to 8 + turn on, 0 to turn off. File is readable and writable. 9 + 10 + What: /sys/devices/platform/ayaneo-ec/controller_modules 11 + Date: Nov 2025 12 + KernelVersion: 6.19 13 + Contact: "Antheas Kapenekakis" <lkml@antheas.dev> 14 + Description: 15 + Shows which controller modules are currently connected to 16 + the device. Possible values are "left", "right" and "both". 17 + File is read-only. The Windows software for this device 18 + will only set controller power to 1 if both module sides 19 + are connected (i.e. this file returns "both").
+1
Documentation/admin-guide/laptops/index.rst
··· 17 17 sonypi 18 18 thinkpad-acpi 19 19 toshiba_haps 20 + uniwill-laptop
+60
Documentation/admin-guide/laptops/uniwill-laptop.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0+ 2 + 3 + Uniwill laptop extra features 4 + ============================= 5 + 6 + On laptops manufactured by Uniwill (either directly or as ODM), the ``uniwill-laptop`` driver 7 + handles various platform-specific features. 8 + 9 + Module Loading 10 + -------------- 11 + 12 + The ``uniwill-laptop`` driver relies on a DMI table to automatically load on supported devices. 13 + When using the ``force`` module parameter, this DMI check will be omitted, allowing the driver 14 + to be loaded on unsupported devices for testing purposes. 15 + 16 + Hotkeys 17 + ------- 18 + 19 + Usually the FN keys work without a special driver. However as soon as the ``uniwill-laptop`` driver 20 + is loaded, the FN keys need to be handled manually. This is done automatically by the driver itself. 21 + 22 + Keyboard settings 23 + ----------------- 24 + 25 + The ``uniwill-laptop`` driver allows the user to enable/disable: 26 + 27 + - the FN and super key lock functionality of the integrated keyboard 28 + - the touchpad toggle functionality of the integrated touchpad 29 + 30 + See Documentation/ABI/testing/sysfs-driver-uniwill-laptop for details. 31 + 32 + Hwmon interface 33 + --------------- 34 + 35 + The ``uniwill-laptop`` driver supports reading of the CPU and GPU temperature and supports up to 36 + two fans. Userspace applications can access sensor readings over the hwmon sysfs interface. 37 + 38 + Platform profile 39 + ---------------- 40 + 41 + Support for changing the platform performance mode is currently not implemented. 42 + 43 + Battery Charging Control 44 + ------------------------ 45 + 46 + The ``uniwill-laptop`` driver supports controlling the battery charge limit. This happens over 47 + the standard ``charge_control_end_threshold`` power supply sysfs attribute. All values 48 + between 1 and 100 percent are supported. 49 + 50 + Additionally the driver signals the presence of battery charging issues through the standard 51 + ``health`` power supply sysfs attribute. 52 + 53 + Lightbar 54 + -------- 55 + 56 + The ``uniwill-laptop`` driver exposes the lightbar found on some models as a standard multicolor 57 + LED class device. The default name of this LED class device is ``uniwill:multicolor:status``. 58 + 59 + See Documentation/ABI/testing/sysfs-driver-uniwill-laptop for details on how to control the various 60 + animation modes of the lightbar.
+1 -1
Documentation/driver-api/wmi.rst
··· 16 16 .. kernel-doc:: include/linux/wmi.h 17 17 :internal: 18 18 19 - .. kernel-doc:: drivers/platform/x86/wmi.c 19 + .. kernel-doc:: drivers/platform/wmi/core.c 20 20 :export:
+14 -15
Documentation/wmi/devices/lenovo-wmi-gamezone.rst
··· 19 19 The Gamezone Data WMI interface provides platform-profile and fan curve 20 20 settings for devices that fall under the "Gaming Series" of Lenovo devices. 21 21 It uses a notifier chain to inform other Lenovo WMI interface drivers of the 22 - current platform profile when it changes. 22 + current platform profile when it changes. The currently set profile can be 23 + determined by the user on the hardware by looking at the color of the power 24 + or profile LED, depending on the model. 23 25 24 26 The following platform profiles are supported: 25 - - low-power 26 - - balanced 27 - - balanced-performance 28 - - performance 29 - - custom 27 + - low-power, blue LED 28 + - balanced, white LED 29 + - performance, red LED 30 + - max-power, purple LED 31 + - custom, purple LED 30 32 31 - Balanced-Performance 33 + Extreme Mode 32 34 ~~~~~~~~~~~~~~~~~~~~ 33 35 Some newer Lenovo "Gaming Series" laptops have an "Extreme Mode" profile 34 - enabled in their BIOS. For these devices, the performance platform profile 35 - corresponds to the BIOS Extreme Mode, while the balanced-performance 36 - platform profile corresponds to the BIOS Performance mode. For legacy 37 - devices, the performance platform profile will correspond with the BIOS 38 - Performance mode. 36 + enabled in their BIOS. When available, this mode will be represented by the 37 + max-power platform profile. 39 38 40 - For some newer devices the "Extreme Mode" profile is incomplete in the BIOS 41 - and setting it will cause undefined behavior. A BIOS bug quirk table is 42 - provided to ensure these devices cannot set "Extreme Mode" from the driver. 39 + For a subset of these devices the "Extreme Mode" profile is incomplete in 40 + the BIOS and setting it will cause undefined behavior. A BIOS bug quirk table 41 + is provided to ensure these devices cannot set "Extreme Mode" from the driver. 43 42 44 43 Custom Profile 45 44 ~~~~~~~~~~~~~~
+198
Documentation/wmi/devices/uniwill-laptop.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0-or-later 2 + 3 + ======================================== 4 + Uniwill Notebook driver (uniwill-laptop) 5 + ======================================== 6 + 7 + Introduction 8 + ============ 9 + 10 + Many notebooks manufactured by Uniwill (either directly or as ODM) provide a EC interface 11 + for controlling various platform settings like sensors and fan control. This interface is 12 + used by the ``uniwill-laptop`` driver to map those features onto standard kernel interfaces. 13 + 14 + EC WMI interface description 15 + ============================ 16 + 17 + The EC WMI interface description can be decoded from the embedded binary MOF (bmof) 18 + data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility: 19 + 20 + :: 21 + 22 + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), 23 + Description("Class used to operate methods on a ULong"), 24 + guid("{ABBC0F6F-8EA1-11d1-00A0-C90629100000}")] 25 + class AcpiTest_MULong { 26 + [key, read] string InstanceName; 27 + [read] boolean Active; 28 + 29 + [WmiMethodId(1), Implemented, read, write, Description("Return the contents of a ULong")] 30 + void GetULong([out, Description("Ulong Data")] uint32 Data); 31 + 32 + [WmiMethodId(2), Implemented, read, write, Description("Set the contents of a ULong")] 33 + void SetULong([in, Description("Ulong Data")] uint32 Data); 34 + 35 + [WmiMethodId(3), Implemented, read, write, 36 + Description("Generate an event containing ULong data")] 37 + void FireULong([in, Description("WMI requires a parameter")] uint32 Hack); 38 + 39 + [WmiMethodId(4), Implemented, read, write, Description("Get and Set the contents of a ULong")] 40 + void GetSetULong([in, Description("Ulong Data")] uint64 Data, 41 + [out, Description("Ulong Data")] uint32 Return); 42 + 43 + [WmiMethodId(5), Implemented, read, write, 44 + Description("Get and Set the contents of a ULong for Dollby button")] 45 + void GetButton([in, Description("Ulong Data")] uint64 Data, 46 + [out, Description("Ulong Data")] uint32 Return); 47 + }; 48 + 49 + Most of the WMI-related code was copied from the Windows driver samples, which unfortunately means 50 + that the WMI-GUID is not unique. This makes the WMI-GUID unusable for autoloading. 51 + 52 + WMI method GetULong() 53 + --------------------- 54 + 55 + This WMI method was copied from the Windows driver samples and has no function. 56 + 57 + WMI method SetULong() 58 + --------------------- 59 + 60 + This WMI method was copied from the Windows driver samples and has no function. 61 + 62 + WMI method FireULong() 63 + ---------------------- 64 + 65 + This WMI method allows to inject a WMI event with a 32-bit payload. Its primary purpose seems 66 + to be debugging. 67 + 68 + WMI method GetSetULong() 69 + ------------------------ 70 + 71 + This WMI method is used to communicate with the EC. The ``Data`` argument holds the following 72 + information (starting with the least significant byte): 73 + 74 + 1. 16-bit address 75 + 2. 16-bit data (set to ``0x0000`` when reading) 76 + 3. 16-bit operation (``0x0100`` for reading and ``0x0000`` for writing) 77 + 4. 16-bit reserved (set to ``0x0000``) 78 + 79 + The first 8 bits of the ``Return`` value contain the data returned by the EC when reading. 80 + The special value ``0xFEFEFEFE`` is used to indicate a communication failure with the EC. 81 + 82 + WMI method GetButton() 83 + ---------------------- 84 + 85 + This WMI method is not implemented on all machines and has an unknown purpose. 86 + 87 + Reverse-Engineering the EC WMI interface 88 + ======================================== 89 + 90 + .. warning:: Randomly poking the EC can potentially cause damage to the machine and other unwanted 91 + side effects, please be careful. 92 + 93 + The EC behind the ``GetSetULong`` method is used by the OEM software supplied by the manufacturer. 94 + Reverse-engineering of this software is difficult since it uses an obfuscator, however some parts 95 + are not obfuscated. In this case `dnSpy <https://github.com/dnSpy/dnSpy>`_ could also be helpful. 96 + 97 + The EC can be accessed under Windows using powershell (requires admin privileges): 98 + 99 + :: 100 + 101 + > $obj = Get-CimInstance -Namespace root/wmi -ClassName AcpiTest_MULong | Select-Object -First 1 102 + > Invoke-CimMethod -InputObject $obj -MethodName GetSetULong -Arguments @{Data = <input>} 103 + 104 + WMI event interface description 105 + =============================== 106 + 107 + The WMI interface description can also be decoded from the embedded binary MOF (bmof) 108 + data: 109 + 110 + :: 111 + 112 + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), 113 + Description("Class containing event generated ULong data"), 114 + guid("{ABBC0F72-8EA1-11d1-00A0-C90629100000}")] 115 + class AcpiTest_EventULong : WmiEvent { 116 + [key, read] string InstanceName; 117 + [read] boolean Active; 118 + 119 + [WmiDataId(1), read, write, Description("ULong Data")] uint32 ULong; 120 + }; 121 + 122 + Most of the WMI-related code was again copied from the Windows driver samples, causing this WMI 123 + interface to suffer from the same restrictions as the EC WMI interface described above. 124 + 125 + WMI event data 126 + -------------- 127 + 128 + The WMI event data contains a single 32-bit value which is used to indicate various platform events. 129 + 130 + Reverse-Engineering the Uniwill WMI event interface 131 + =================================================== 132 + 133 + The driver logs debug messages when receiving a WMI event. Thus enabling debug messages will be 134 + useful for finding unknown event codes. 135 + 136 + EC ACPI interface description 137 + ============================= 138 + 139 + The ``INOU0000`` ACPI device is a virtual device used to access various hardware registers 140 + available on notebooks manufactured by Uniwill. Reading and writing those registers happens 141 + by calling ACPI control methods. The ``uniwill-laptop`` driver uses this device to communicate 142 + with the EC because the ACPI control methods are faster than the WMI methods described above. 143 + 144 + ACPI control methods used for reading registers take a single ACPI integer containing the address 145 + of the register to read and return a ACPI integer containing the data inside said register. ACPI 146 + control methods used for writing registers however take two ACPI integers, with the additional 147 + ACPI integer containing the data to be written into the register. Such ACPI control methods return 148 + nothing. 149 + 150 + System memory 151 + ------------- 152 + 153 + System memory can be accessed with a granularity of either a single byte (``MMRB`` for reading and 154 + ``MMWB`` for writing) or four bytes (``MMRD`` for reading and ``MMWD`` for writing). Those ACPI 155 + control methods are unused because they provide no benefit when compared to the native memory 156 + access functions provided by the kernel. 157 + 158 + EC RAM 159 + ------ 160 + 161 + The internal RAM of the EC can be accessed with a granularity of a single byte using the ``ECRR`` 162 + (read) and ``ECRW`` (write) ACPI control methods, with the maximum register address being ``0xFFF``. 163 + The OEM software waits 6 ms after calling one of those ACPI control methods, likely to avoid 164 + overwhelming the EC when being connected over LPC. 165 + 166 + PCI config space 167 + ---------------- 168 + 169 + The PCI config space can be accessed with a granularity of four bytes using the ``PCRD`` (read) and 170 + ``PCWD`` (write) ACPI control methods. The exact address format is unknown, and poking random PCI 171 + devices might confuse the PCI subsystem. Because of this those ACPI control methods are not used. 172 + 173 + IO ports 174 + -------- 175 + 176 + IO ports can be accessed with a granularity of four bytes using the ``IORD`` (read) and ``IOWD`` 177 + (write) ACPI control methods. Those ACPI control methods are unused because they provide no benefit 178 + when compared to the native IO port access functions provided by the kernel. 179 + 180 + CMOS RAM 181 + -------- 182 + 183 + The CMOS RAM can be accessed with a granularity of a single byte using the ``RCMS`` (read) and 184 + ``WCMS`` ACPI control methods. Using those ACPI methods might interfere with the native CMOS RAM 185 + access functions provided by the kernel due to the usage of indexed IO, so they are unused. 186 + 187 + Indexed IO 188 + ---------- 189 + 190 + Indexed IO with IO ports with a granularity of a single byte can be performed using the ``RIOP`` 191 + (read) and ``WIOP`` (write) ACPI control methods. Those ACPI methods are unused because they 192 + provide no benifit when compared to the native IO port access functions provided by the kernel. 193 + 194 + Special thanks go to github user `pobrn` which developed the 195 + `qc71_laptop <https://github.com/pobrn/qc71_laptop>`_ driver on which this driver is partly based. 196 + The same is true for Tuxedo Computers, which developed the 197 + `tuxedo-drivers <https://gitlab.com/tuxedocomputers/development/packages/tuxedo-drivers>`_ package 198 + which also served as a foundation for this driver.
+19 -1
MAINTAINERS
··· 402 402 F: Documentation/ABI/testing/sysfs-bus-wmi 403 403 F: Documentation/driver-api/wmi.rst 404 404 F: Documentation/wmi/ 405 - F: drivers/platform/x86/wmi.c 405 + F: drivers/platform/wmi/ 406 406 F: include/uapi/linux/wmi.h 407 407 408 408 ACRN HYPERVISOR SERVICE MODULE ··· 4261 4261 W: https://ez.analog.com/linux-software-drivers 4262 4262 F: Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml 4263 4263 F: drivers/pwm/pwm-axi-pwmgen.c 4264 + 4265 + AYANEO PLATFORM EC DRIVER 4266 + M: Antheas Kapenekakis <lkml@antheas.dev> 4267 + L: platform-driver-x86@vger.kernel.org 4268 + S: Maintained 4269 + F: Documentation/ABI/testing/sysfs-platform-ayaneo 4270 + F: drivers/platform/x86/ayaneo-ec.c 4264 4271 4265 4272 AZ6007 DVB DRIVER 4266 4273 M: Mauro Carvalho Chehab <mchehab@kernel.org> ··· 26795 26788 L: linux-scsi@vger.kernel.org 26796 26789 S: Maintained 26797 26790 F: drivers/ufs/host/ufs-renesas.c 26791 + 26792 + UNIWILL LAPTOP DRIVER 26793 + M: Armin Wolf <W_Armin@gmx.de> 26794 + L: platform-driver-x86@vger.kernel.org 26795 + S: Maintained 26796 + F: Documentation/ABI/testing/sysfs-driver-uniwill-laptop 26797 + F: Documentation/admin-guide/laptops/uniwill-laptop.rst 26798 + F: Documentation/wmi/devices/uniwill-laptop.rst 26799 + F: drivers/platform/x86/uniwill/uniwill-acpi.c 26800 + F: drivers/platform/x86/uniwill/uniwill-wmi.c 26801 + F: drivers/platform/x86/uniwill/uniwill-wmi.h 26798 26802 26799 26803 UNSORTED BLOCK IMAGES (UBI) 26800 26804 M: Richard Weinberger <richard@nod.at>
+5 -2
drivers/acpi/platform_profile.c
··· 37 37 [PLATFORM_PROFILE_BALANCED] = "balanced", 38 38 [PLATFORM_PROFILE_BALANCED_PERFORMANCE] = "balanced-performance", 39 39 [PLATFORM_PROFILE_PERFORMANCE] = "performance", 40 + [PLATFORM_PROFILE_MAX_POWER] = "max-power", 40 41 [PLATFORM_PROFILE_CUSTOM] = "custom", 41 42 }; 42 43 static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST); ··· 507 506 if (err) 508 507 return err; 509 508 510 - if (profile == PLATFORM_PROFILE_CUSTOM || 509 + if (profile == PLATFORM_PROFILE_MAX_POWER || 510 + profile == PLATFORM_PROFILE_CUSTOM || 511 511 profile == PLATFORM_PROFILE_LAST) 512 512 return -EINVAL; 513 513 ··· 517 515 if (err) 518 516 return err; 519 517 520 - /* never iterate into a custom if all drivers supported it */ 518 + /* never iterate into a custom or max power if all drivers supported it */ 519 + clear_bit(PLATFORM_PROFILE_MAX_POWER, data.aggregate); 521 520 clear_bit(PLATFORM_PROFILE_CUSTOM, data.aggregate); 522 521 523 522 next = find_next_bit_wrap(data.aggregate,
+1
drivers/hid/hid-asus.c
··· 27 27 #include <linux/hid.h> 28 28 #include <linux/module.h> 29 29 #include <linux/platform_data/x86/asus-wmi.h> 30 + #include <linux/platform_data/x86/asus-wmi-leds-ids.h> 30 31 #include <linux/input/mt.h> 31 32 #include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */ 32 33 #include <linux/power_supply.h>
+2
drivers/platform/Kconfig
··· 20 20 source "drivers/platform/arm64/Kconfig" 21 21 22 22 source "drivers/platform/raspberrypi/Kconfig" 23 + 24 + source "drivers/platform/wmi/Kconfig"
+1
drivers/platform/Makefile
··· 14 14 obj-$(CONFIG_SURFACE_PLATFORMS) += surface/ 15 15 obj-$(CONFIG_ARM64_PLATFORM_DEVICES) += arm64/ 16 16 obj-$(CONFIG_BCM2835_VCHIQ) += raspberrypi/ 17 + obj-$(CONFIG_ACPI_WMI) += wmi/
+50 -8
drivers/platform/arm64/lenovo-thinkpad-t14s.c
··· 20 20 #include <linux/module.h> 21 21 #include <linux/regmap.h> 22 22 #include <linux/slab.h> 23 + #include <linux/pm.h> 23 24 24 25 #define T14S_EC_CMD_ECRD 0x02 25 26 #define T14S_EC_CMD_ECWR 0x03 26 27 #define T14S_EC_CMD_EVT 0xf0 27 28 28 - #define T14S_EC_REG_LED 0x0c 29 - #define T14S_EC_REG_KBD_BL1 0x0d 30 - #define T14S_EC_REG_KBD_BL2 0xe1 31 - #define T14S_EC_KBD_BL1_MASK GENMASK_U8(7, 6) 32 - #define T14S_EC_KBD_BL2_MASK GENMASK_U8(3, 2) 33 - #define T14S_EC_REG_AUD 0x30 34 - #define T14S_EC_MIC_MUTE_LED BIT(5) 35 - #define T14S_EC_SPK_MUTE_LED BIT(6) 29 + #define T14S_EC_REG_LED 0x0c 30 + #define T14S_EC_REG_KBD_BL1 0x0d 31 + #define T14S_EC_REG_MODERN_STANDBY 0xe0 32 + #define T14S_EC_MODERN_STANDBY_ENTRY BIT(1) 33 + #define T14S_EC_MODERN_STANDBY_EXIT BIT(0) 34 + #define T14S_EC_REG_KBD_BL2 0xe1 35 + #define T14S_EC_KBD_BL1_MASK GENMASK_U8(7, 6) 36 + #define T14S_EC_KBD_BL2_MASK GENMASK_U8(3, 2) 37 + #define T14S_EC_REG_AUD 0x30 38 + #define T14S_EC_MIC_MUTE_LED BIT(5) 39 + #define T14S_EC_SPK_MUTE_LED BIT(6) 36 40 37 41 #define T14S_EC_EVT_NONE 0x00 38 42 #define T14S_EC_EVT_KEY_FN_4 0x13 ··· 204 200 out: 205 201 i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); 206 202 return ret; 203 + } 204 + 205 + static void t14s_ec_write_sequence(struct t14s_ec *ec, u8 reg, u8 val, u8 cnt) 206 + { 207 + int i; 208 + 209 + for (i = 0; i < cnt; i++) 210 + regmap_write(ec->regmap, reg, val); 207 211 } 208 212 209 213 static int t14s_led_set_status(struct t14s_ec *ec, ··· 566 554 return -ENOMEM; 567 555 568 556 ec->dev = dev; 557 + i2c_set_clientdata(client, ec); 569 558 570 559 ec->regmap = devm_regmap_init(dev, &t14s_ec_regmap_bus, 571 560 ec, &t14s_ec_regmap_config); ··· 606 593 return 0; 607 594 } 608 595 596 + static int t14s_ec_suspend(struct device *dev) 597 + { 598 + struct t14s_ec *ec = dev_get_drvdata(dev); 599 + 600 + led_classdev_suspend(&ec->kbd_backlight); 601 + 602 + t14s_ec_write_sequence(ec, T14S_EC_REG_MODERN_STANDBY, 603 + T14S_EC_MODERN_STANDBY_ENTRY, 3); 604 + 605 + return 0; 606 + } 607 + 608 + static int t14s_ec_resume(struct device *dev) 609 + { 610 + struct t14s_ec *ec = dev_get_drvdata(dev); 611 + 612 + t14s_ec_write_sequence(ec, T14S_EC_REG_MODERN_STANDBY, 613 + T14S_EC_MODERN_STANDBY_EXIT, 3); 614 + 615 + led_classdev_resume(&ec->kbd_backlight); 616 + 617 + return 0; 618 + } 619 + 609 620 static const struct of_device_id t14s_ec_of_match[] = { 610 621 { .compatible = "lenovo,thinkpad-t14s-ec" }, 611 622 {} ··· 642 605 }; 643 606 MODULE_DEVICE_TABLE(i2c, t14s_ec_i2c_id_table); 644 607 608 + static const struct dev_pm_ops t14s_ec_pm_ops = { 609 + SYSTEM_SLEEP_PM_OPS(t14s_ec_suspend, t14s_ec_resume) 610 + }; 611 + 645 612 static struct i2c_driver t14s_ec_i2c_driver = { 646 613 .driver = { 647 614 .name = "thinkpad-t14s-ec", 648 615 .of_match_table = t14s_ec_of_match, 616 + .pm = &t14s_ec_pm_ops, 649 617 }, 650 618 .probe = t14s_ec_probe, 651 619 .id_table = t14s_ec_i2c_id_table,
+1 -1
drivers/platform/surface/aggregator/core.c
··· 676 676 677 677 status = ssam_serdev_setup(ssh, serdev); 678 678 if (status) { 679 - status = dev_err_probe(dev, status, "failed to setup serdev\n"); 679 + dev_err_probe(dev, status, "failed to setup serdev\n"); 680 680 goto err_devinit; 681 681 } 682 682
+1 -1
drivers/platform/surface/aggregator/ssh_packet_layer.c
··· 671 671 /* Re-adjust / schedule reaper only if it is above resolution delta. */ 672 672 if (ktime_before(aexp, ptl->rtx_timeout.expires)) { 673 673 ptl->rtx_timeout.expires = expires; 674 - mod_delayed_work(system_wq, &ptl->rtx_timeout.reaper, delta); 674 + mod_delayed_work(system_percpu_wq, &ptl->rtx_timeout.reaper, delta); 675 675 } 676 676 677 677 spin_unlock(&ptl->rtx_timeout.lock);
+1 -1
drivers/platform/surface/aggregator/ssh_request_layer.c
··· 434 434 /* Re-adjust / schedule reaper only if it is above resolution delta. */ 435 435 if (ktime_before(aexp, rtl->rtx_timeout.expires)) { 436 436 rtl->rtx_timeout.expires = expires; 437 - mod_delayed_work(system_wq, &rtl->rtx_timeout.reaper, delta); 437 + mod_delayed_work(system_percpu_wq, &rtl->rtx_timeout.reaper, delta); 438 438 } 439 439 440 440 spin_unlock(&rtl->rtx_timeout.lock);
+1 -1
drivers/platform/surface/surface_acpi_notify.c
··· 862 862 { 863 863 int ret; 864 864 865 - san_wq = alloc_workqueue("san_wq", 0, 0); 865 + san_wq = alloc_workqueue("san_wq", WQ_PERCPU, 0); 866 866 if (!san_wq) 867 867 return -ENOMEM; 868 868 ret = platform_driver_register(&surface_acpi_notify);
+34
drivers/platform/wmi/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-or-later 2 + # 3 + # ACPI WMI Core 4 + # 5 + 6 + menuconfig ACPI_WMI 7 + tristate "ACPI-WMI support" 8 + depends on ACPI && X86 9 + help 10 + This option enables support for the ACPI-WMI driver core. 11 + 12 + The ACPI-WMI interface is a proprietary extension of ACPI allowing 13 + the platform firmware to expose WMI (Windows Management Instrumentation) 14 + objects used for managing various aspects of the underlying system. 15 + Mapping between ACPI control methods and WMI objects happens through 16 + special mapper devices (PNP0C14) defined inside the ACPI tables. 17 + 18 + Enabling this option is necessary for building the vendor specific 19 + ACPI-WMI client drivers for Acer, Dell an HP machines (among others). 20 + 21 + It is safe to enable this option even for machines that do not contain 22 + any ACPI-WMI mapper devices at all. 23 + 24 + if ACPI_WMI 25 + 26 + config ACPI_WMI_LEGACY_DEVICE_NAMES 27 + bool "Use legacy WMI device naming scheme" 28 + help 29 + Say Y here to force the WMI driver core to use the old WMI device naming 30 + scheme when creating WMI devices. Doing so might be necessary for some 31 + userspace applications but will cause the registration of WMI devices with 32 + the same GUID to fail in some corner cases. 33 + 34 + endif # ACPI_WMI
+8
drivers/platform/wmi/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-or-later 2 + # 3 + # Makefile for linux/drivers/platform/wmi 4 + # ACPI WMI core 5 + # 6 + 7 + wmi-y := core.o 8 + obj-$(CONFIG_ACPI_WMI) += wmi.o
+39 -33
drivers/platform/x86/Kconfig
··· 16 16 17 17 if X86_PLATFORM_DEVICES 18 18 19 - config ACPI_WMI 20 - tristate "WMI" 21 - depends on ACPI 22 - help 23 - This driver adds support for the ACPI-WMI (Windows Management 24 - Instrumentation) mapper device (PNP0C14) found on some systems. 25 - 26 - ACPI-WMI is a proprietary extension to ACPI to expose parts of the 27 - ACPI firmware to userspace - this is done through various vendor 28 - defined methods and data blocks in a PNP0C14 device, which are then 29 - made available for userspace to call. 30 - 31 - The implementation of this in Linux currently only exposes this to 32 - other kernel space drivers. 33 - 34 - This driver is a required dependency to build the firmware specific 35 - drivers needed on many machines, including Acer and HP laptops. 36 - 37 - It is safe to enable this driver even if your DSDT doesn't define 38 - any ACPI-WMI devices. 39 - 40 - config ACPI_WMI_LEGACY_DEVICE_NAMES 41 - bool "Use legacy WMI device naming scheme" 42 - depends on ACPI_WMI 43 - help 44 - Say Y here to force the WMI driver core to use the old WMI device naming 45 - scheme when creating WMI devices. Doing so might be necessary for some 46 - userspace applications but will cause the registration of WMI devices with 47 - the same GUID to fail in some corner cases. 48 - 49 19 config WMI_BMOF 50 20 tristate "WMI embedded Binary MOF driver" 51 21 depends on ACPI_WMI ··· 43 73 44 74 To compile this driver as a module, choose M here: the module 45 75 will be called huawei-wmi. 76 + 77 + source "drivers/platform/x86/uniwill/Kconfig" 46 78 47 79 config UV_SYSFS 48 80 tristate "Sysfs structure for UV systems" ··· 234 262 If you choose to compile this driver as a module the module will be 235 263 called asus-wireless. 236 264 265 + config ASUS_ARMOURY 266 + tristate "ASUS Armoury driver" 267 + depends on ASUS_WMI 268 + select FW_ATTR_CLASS 269 + help 270 + Say Y here if you have a WMI aware Asus machine and would like to use the 271 + firmware_attributes API to control various settings typically exposed in 272 + the ASUS Armoury Crate application available on Windows. 273 + 274 + To compile this driver as a module, choose M here: the module will 275 + be called asus-armoury. 276 + 237 277 config ASUS_WMI 238 278 tristate "ASUS WMI Driver" 239 279 depends on ACPI_WMI ··· 267 283 268 284 To compile this driver as a module, choose M here: the module will 269 285 be called asus-wmi. 286 + 287 + config ASUS_WMI_DEPRECATED_ATTRS 288 + bool "BIOS option support in WMI platform (DEPRECATED)" 289 + depends on ASUS_WMI 290 + default y 291 + help 292 + Say Y to expose the configurable BIOS options through the asus-wmi 293 + driver. 294 + 295 + This can be used with or without the asus-armoury driver which 296 + has the same attributes, but more, and better features. 270 297 271 298 config ASUS_NB_WMI 272 299 tristate "Asus Notebook WMI Driver" ··· 310 315 311 316 If you have an Asus TF103C tablet say Y or M here, for a generic x86 312 317 distro config say M here. 318 + 319 + config AYANEO_EC 320 + tristate "Ayaneo EC platform control" 321 + depends on DMI 322 + depends on ACPI_EC 323 + depends on ACPI_BATTERY 324 + depends on HWMON 325 + help 326 + Enables support for the platform EC of Ayaneo devices. This 327 + includes fan control, fan speed, charge limit, magic 328 + module detection, and controller power control. 329 + 330 + If you have an Ayaneo device, say Y or M here. 313 331 314 332 config MERAKI_MX100 315 333 tristate "Cisco Meraki MX100 Platform Driver" ··· 1039 1031 help 1040 1032 Enables support for the platform EC of OneXPlayer and AOKZOE 1041 1033 handheld devices. This includes fan speed, fan controls, and 1042 - disabling the default TDP behavior of the device. Due to legacy 1043 - reasons, this driver also provides hwmon functionality to Ayaneo 1044 - devices and the OrangePi Neo. 1034 + disabling the default TDP behavior of the device. 1045 1035 1046 1036 source "drivers/platform/x86/tuxedo/Kconfig" 1047 1037
+7 -1
drivers/platform/x86/Makefile
··· 5 5 # 6 6 7 7 # Windows Management Interface 8 - obj-$(CONFIG_ACPI_WMI) += wmi.o 9 8 obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o 10 9 11 10 # WMI drivers ··· 32 33 # ASUS 33 34 obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o 34 35 obj-$(CONFIG_ASUS_WIRELESS) += asus-wireless.o 36 + obj-$(CONFIG_ASUS_ARMOURY) += asus-armoury.o 35 37 obj-$(CONFIG_ASUS_WMI) += asus-wmi.o 36 38 obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o 37 39 obj-$(CONFIG_ASUS_TF103C_DOCK) += asus-tf103c-dock.o 38 40 obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o 39 41 obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o 42 + 43 + # Ayaneo 44 + obj-$(CONFIG_AYANEO_EC) += ayaneo-ec.o 40 45 41 46 # Cisco/Meraki 42 47 obj-$(CONFIG_MERAKI_MX100) += meraki-mx100.o ··· 112 109 # toshiba_acpi must link after wmi to ensure that wmi devices are found 113 110 # before toshiba_acpi initializes 114 111 obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o 112 + 113 + # Uniwill 114 + obj-y += uniwill/ 115 115 116 116 # Inspur 117 117 obj-$(CONFIG_INSPUR_PLATFORM_PROFILE) += inspur_platform_profile.o
+267 -23
drivers/platform/x86/acer-wmi.c
··· 12 12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13 13 14 14 #include <linux/kernel.h> 15 + #include <linux/minmax.h> 15 16 #include <linux/module.h> 16 17 #include <linux/init.h> 17 18 #include <linux/types.h> 18 19 #include <linux/dmi.h> 20 + #include <linux/fixp-arith.h> 19 21 #include <linux/backlight.h> 20 22 #include <linux/leds.h> 21 23 #include <linux/platform_device.h> ··· 70 68 #define ACER_WMID_SET_GAMING_LED_METHODID 2 71 69 #define ACER_WMID_GET_GAMING_LED_METHODID 4 72 70 #define ACER_WMID_GET_GAMING_SYS_INFO_METHODID 5 73 - #define ACER_WMID_SET_GAMING_FAN_BEHAVIOR 14 71 + #define ACER_WMID_SET_GAMING_FAN_BEHAVIOR_METHODID 14 72 + #define ACER_WMID_GET_GAMING_FAN_BEHAVIOR_METHODID 15 73 + #define ACER_WMID_SET_GAMING_FAN_SPEED_METHODID 16 74 + #define ACER_WMID_GET_GAMING_FAN_SPEED_METHODID 17 74 75 #define ACER_WMID_SET_GAMING_MISC_SETTING_METHODID 22 75 76 #define ACER_WMID_GET_GAMING_MISC_SETTING_METHODID 23 77 + 78 + #define ACER_GAMING_FAN_BEHAVIOR_CPU BIT(0) 79 + #define ACER_GAMING_FAN_BEHAVIOR_GPU BIT(3) 80 + 81 + #define ACER_GAMING_FAN_BEHAVIOR_STATUS_MASK GENMASK_ULL(7, 0) 82 + #define ACER_GAMING_FAN_BEHAVIOR_ID_MASK GENMASK_ULL(15, 0) 83 + #define ACER_GAMING_FAN_BEHAVIOR_SET_CPU_MODE_MASK GENMASK(17, 16) 84 + #define ACER_GAMING_FAN_BEHAVIOR_SET_GPU_MODE_MASK GENMASK(23, 22) 85 + #define ACER_GAMING_FAN_BEHAVIOR_GET_CPU_MODE_MASK GENMASK(9, 8) 86 + #define ACER_GAMING_FAN_BEHAVIOR_GET_GPU_MODE_MASK GENMASK(15, 14) 87 + 88 + #define ACER_GAMING_FAN_SPEED_STATUS_MASK GENMASK_ULL(7, 0) 89 + #define ACER_GAMING_FAN_SPEED_ID_MASK GENMASK_ULL(7, 0) 90 + #define ACER_GAMING_FAN_SPEED_VALUE_MASK GENMASK_ULL(15, 8) 76 91 77 92 #define ACER_GAMING_MISC_SETTING_STATUS_MASK GENMASK_ULL(7, 0) 78 93 #define ACER_GAMING_MISC_SETTING_INDEX_MASK GENMASK_ULL(7, 0) ··· 139 120 ACER_WMID_SENSOR_EXTERNAL_TEMPERATURE_2 = 0x03, 140 121 ACER_WMID_SENSOR_GPU_FAN_SPEED = 0x06, 141 122 ACER_WMID_SENSOR_GPU_TEMPERATURE = 0x0A, 123 + }; 124 + 125 + enum acer_wmi_gaming_fan_id { 126 + ACER_WMID_CPU_FAN = 0x01, 127 + ACER_WMID_GPU_FAN = 0x04, 128 + }; 129 + 130 + enum acer_wmi_gaming_fan_mode { 131 + ACER_WMID_FAN_MODE_AUTO = 0x01, 132 + ACER_WMID_FAN_MODE_TURBO = 0x02, 133 + ACER_WMID_FAN_MODE_CUSTOM = 0x03, 142 134 }; 143 135 144 136 enum acer_wmi_predator_v4_oc { ··· 309 279 #define ACER_CAP_TURBO_FAN BIT(9) 310 280 #define ACER_CAP_PLATFORM_PROFILE BIT(10) 311 281 #define ACER_CAP_HWMON BIT(11) 282 + #define ACER_CAP_PWM BIT(12) 312 283 313 284 /* 314 285 * Interface type flags ··· 404 373 u8 cpu_fans; 405 374 u8 gpu_fans; 406 375 u8 predator_v4; 376 + u8 pwm; 407 377 }; 408 378 409 379 static struct quirk_entry *quirks; ··· 424 392 if (quirks->predator_v4) 425 393 interface->capability |= ACER_CAP_PLATFORM_PROFILE | 426 394 ACER_CAP_HWMON; 395 + 396 + if (quirks->pwm) 397 + interface->capability |= ACER_CAP_PWM; 427 398 } 428 399 429 400 static int __init dmi_matched(const struct dmi_system_id *dmi) ··· 466 431 .cpu_fans = 1, 467 432 .gpu_fans = 1, 468 433 .predator_v4 = 1, 434 + .pwm = 1, 469 435 }; 470 436 471 437 static struct quirk_entry quirk_acer_predator_pt14_51 = { ··· 474 438 .cpu_fans = 1, 475 439 .gpu_fans = 1, 476 440 .predator_v4 = 1, 441 + .pwm = 1, 477 442 }; 478 443 479 444 static struct quirk_entry quirk_acer_predator_v4 = { ··· 690 653 .matches = { 691 654 DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 692 655 DMI_MATCH(DMI_PRODUCT_NAME, "Predator PH16-72"), 656 + }, 657 + .driver_data = &quirk_acer_predator_ph16_72, 658 + }, 659 + { 660 + .callback = dmi_matched, 661 + .ident = "Acer Predator Helios Neo 16", 662 + .matches = { 663 + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 664 + DMI_MATCH(DMI_PRODUCT_NAME, "Predator PHN16-72"), 693 665 }, 694 666 .driver_data = &quirk_acer_predator_ph16_72, 695 667 }, ··· 1610 1564 case ACER_CAP_TURBO_LED: 1611 1565 method_id = ACER_WMID_SET_GAMING_LED_METHODID; 1612 1566 break; 1613 - case ACER_CAP_TURBO_FAN: 1614 - method_id = ACER_WMID_SET_GAMING_FAN_BEHAVIOR; 1615 - break; 1616 1567 default: 1617 1568 return AE_BAD_PARAMETER; 1618 1569 } ··· 1660 1617 return 0; 1661 1618 } 1662 1619 1663 - static void WMID_gaming_set_fan_mode(u8 fan_mode) 1620 + static int WMID_gaming_set_fan_behavior(u16 fan_bitmap, enum acer_wmi_gaming_fan_mode mode) 1664 1621 { 1665 - /* fan_mode = 1 is used for auto, fan_mode = 2 used for turbo*/ 1666 - u64 gpu_fan_config1 = 0, gpu_fan_config2 = 0; 1667 - int i; 1622 + acpi_status status; 1623 + u64 input = 0; 1624 + u64 result; 1625 + 1626 + input |= FIELD_PREP(ACER_GAMING_FAN_BEHAVIOR_ID_MASK, fan_bitmap); 1627 + 1628 + if (fan_bitmap & ACER_GAMING_FAN_BEHAVIOR_CPU) 1629 + input |= FIELD_PREP(ACER_GAMING_FAN_BEHAVIOR_SET_CPU_MODE_MASK, mode); 1630 + 1631 + if (fan_bitmap & ACER_GAMING_FAN_BEHAVIOR_GPU) 1632 + input |= FIELD_PREP(ACER_GAMING_FAN_BEHAVIOR_SET_GPU_MODE_MASK, mode); 1633 + 1634 + status = WMI_gaming_execute_u64(ACER_WMID_SET_GAMING_FAN_BEHAVIOR_METHODID, input, 1635 + &result); 1636 + if (ACPI_FAILURE(status)) 1637 + return -EIO; 1638 + 1639 + /* The return status must be zero for the operation to have succeeded */ 1640 + if (FIELD_GET(ACER_GAMING_FAN_BEHAVIOR_STATUS_MASK, result)) 1641 + return -EIO; 1642 + 1643 + return 0; 1644 + } 1645 + 1646 + static int WMID_gaming_get_fan_behavior(u16 fan_bitmap, enum acer_wmi_gaming_fan_mode *mode) 1647 + { 1648 + acpi_status status; 1649 + u32 input = 0; 1650 + u64 result; 1651 + int value; 1652 + 1653 + input |= FIELD_PREP(ACER_GAMING_FAN_BEHAVIOR_ID_MASK, fan_bitmap); 1654 + status = WMI_gaming_execute_u32_u64(ACER_WMID_GET_GAMING_FAN_BEHAVIOR_METHODID, input, 1655 + &result); 1656 + if (ACPI_FAILURE(status)) 1657 + return -EIO; 1658 + 1659 + /* The return status must be zero for the operation to have succeeded */ 1660 + if (FIELD_GET(ACER_GAMING_FAN_BEHAVIOR_STATUS_MASK, result)) 1661 + return -EIO; 1662 + 1663 + /* Theoretically multiple fans can be specified, but this is currently unused */ 1664 + if (fan_bitmap & ACER_GAMING_FAN_BEHAVIOR_CPU) 1665 + value = FIELD_GET(ACER_GAMING_FAN_BEHAVIOR_GET_CPU_MODE_MASK, result); 1666 + else if (fan_bitmap & ACER_GAMING_FAN_BEHAVIOR_GPU) 1667 + value = FIELD_GET(ACER_GAMING_FAN_BEHAVIOR_GET_GPU_MODE_MASK, result); 1668 + else 1669 + return -EINVAL; 1670 + 1671 + if (value < ACER_WMID_FAN_MODE_AUTO || value > ACER_WMID_FAN_MODE_CUSTOM) 1672 + return -ENXIO; 1673 + 1674 + *mode = value; 1675 + 1676 + return 0; 1677 + } 1678 + 1679 + static void WMID_gaming_set_fan_mode(enum acer_wmi_gaming_fan_mode mode) 1680 + { 1681 + u16 fan_bitmap = 0; 1668 1682 1669 1683 if (quirks->cpu_fans > 0) 1670 - gpu_fan_config2 |= 1; 1671 - for (i = 0; i < (quirks->cpu_fans + quirks->gpu_fans); ++i) 1672 - gpu_fan_config2 |= 1 << (i + 1); 1673 - for (i = 0; i < quirks->gpu_fans; ++i) 1674 - gpu_fan_config2 |= 1 << (i + 3); 1675 - if (quirks->cpu_fans > 0) 1676 - gpu_fan_config1 |= fan_mode; 1677 - for (i = 0; i < (quirks->cpu_fans + quirks->gpu_fans); ++i) 1678 - gpu_fan_config1 |= fan_mode << (2 * i + 2); 1679 - for (i = 0; i < quirks->gpu_fans; ++i) 1680 - gpu_fan_config1 |= fan_mode << (2 * i + 6); 1681 - WMID_gaming_set_u64(gpu_fan_config2 | gpu_fan_config1 << 16, ACER_CAP_TURBO_FAN); 1684 + fan_bitmap |= ACER_GAMING_FAN_BEHAVIOR_CPU; 1685 + 1686 + if (quirks->gpu_fans > 0) 1687 + fan_bitmap |= ACER_GAMING_FAN_BEHAVIOR_GPU; 1688 + 1689 + WMID_gaming_set_fan_behavior(fan_bitmap, mode); 1690 + } 1691 + 1692 + static int WMID_gaming_set_gaming_fan_speed(u8 fan, u8 speed) 1693 + { 1694 + acpi_status status; 1695 + u64 input = 0; 1696 + u64 result; 1697 + 1698 + if (speed > 100) 1699 + return -EINVAL; 1700 + 1701 + input |= FIELD_PREP(ACER_GAMING_FAN_SPEED_ID_MASK, fan); 1702 + input |= FIELD_PREP(ACER_GAMING_FAN_SPEED_VALUE_MASK, speed); 1703 + 1704 + status = WMI_gaming_execute_u64(ACER_WMID_SET_GAMING_FAN_SPEED_METHODID, input, &result); 1705 + if (ACPI_FAILURE(status)) 1706 + return -EIO; 1707 + 1708 + switch (FIELD_GET(ACER_GAMING_FAN_SPEED_STATUS_MASK, result)) { 1709 + case 0x00: 1710 + return 0; 1711 + case 0x01: 1712 + return -ENODEV; 1713 + case 0x02: 1714 + return -EINVAL; 1715 + default: 1716 + return -ENXIO; 1717 + } 1718 + } 1719 + 1720 + static int WMID_gaming_get_gaming_fan_speed(u8 fan, u8 *speed) 1721 + { 1722 + acpi_status status; 1723 + u32 input = 0; 1724 + u64 result; 1725 + 1726 + input |= FIELD_PREP(ACER_GAMING_FAN_SPEED_ID_MASK, fan); 1727 + 1728 + status = WMI_gaming_execute_u32_u64(ACER_WMID_GET_GAMING_FAN_SPEED_METHODID, input, 1729 + &result); 1730 + if (ACPI_FAILURE(status)) 1731 + return -EIO; 1732 + 1733 + if (FIELD_GET(ACER_GAMING_FAN_SPEED_STATUS_MASK, result)) 1734 + return -ENODEV; 1735 + 1736 + *speed = FIELD_GET(ACER_GAMING_FAN_SPEED_VALUE_MASK, result); 1737 + 1738 + return 0; 1682 1739 } 1683 1740 1684 1741 static int WMID_gaming_set_misc_setting(enum acer_wmi_gaming_misc_setting setting, u8 value) ··· 2065 1922 WMID_gaming_set_u64(0x1, ACER_CAP_TURBO_LED); 2066 1923 2067 1924 /* Set FAN mode to auto */ 2068 - WMID_gaming_set_fan_mode(0x1); 1925 + WMID_gaming_set_fan_mode(ACER_WMID_FAN_MODE_AUTO); 2069 1926 2070 1927 /* Set OC to normal */ 2071 1928 if (has_cap(ACER_CAP_TURBO_OC)) { ··· 2079 1936 WMID_gaming_set_u64(0x10001, ACER_CAP_TURBO_LED); 2080 1937 2081 1938 /* Set FAN mode to turbo */ 2082 - WMID_gaming_set_fan_mode(0x2); 1939 + WMID_gaming_set_fan_mode(ACER_WMID_FAN_MODE_TURBO); 2083 1940 2084 1941 /* Set OC to turbo mode */ 2085 1942 if (has_cap(ACER_CAP_TURBO_OC)) { ··· 2910 2767 [1] = ACER_WMID_SENSOR_GPU_FAN_SPEED, 2911 2768 }; 2912 2769 2770 + static const enum acer_wmi_gaming_fan_id acer_wmi_fan_channel_to_fan_id[] = { 2771 + [0] = ACER_WMID_CPU_FAN, 2772 + [1] = ACER_WMID_GPU_FAN, 2773 + }; 2774 + 2775 + static const u16 acer_wmi_fan_channel_to_fan_bitmap[] = { 2776 + [0] = ACER_GAMING_FAN_BEHAVIOR_CPU, 2777 + [1] = ACER_GAMING_FAN_BEHAVIOR_GPU, 2778 + }; 2779 + 2913 2780 static umode_t acer_wmi_hwmon_is_visible(const void *data, 2914 2781 enum hwmon_sensor_types type, u32 attr, 2915 2782 int channel) ··· 2931 2778 case hwmon_temp: 2932 2779 sensor_id = acer_wmi_temp_channel_to_sensor_id[channel]; 2933 2780 break; 2781 + case hwmon_pwm: 2782 + if (!has_cap(ACER_CAP_PWM)) 2783 + return 0; 2784 + 2785 + fallthrough; 2934 2786 case hwmon_fan: 2935 2787 sensor_id = acer_wmi_fan_channel_to_sensor_id[channel]; 2936 2788 break; ··· 2943 2785 return 0; 2944 2786 } 2945 2787 2946 - if (*supported_sensors & BIT(sensor_id - 1)) 2788 + if (*supported_sensors & BIT(sensor_id - 1)) { 2789 + if (type == hwmon_pwm) 2790 + return 0644; 2791 + 2947 2792 return 0444; 2793 + } 2948 2794 2949 2795 return 0; 2950 2796 } ··· 2957 2795 u32 attr, int channel, long *val) 2958 2796 { 2959 2797 u64 command = ACER_WMID_CMD_GET_PREDATOR_V4_SENSOR_READING; 2798 + enum acer_wmi_gaming_fan_mode mode; 2799 + u16 fan_bitmap; 2800 + u8 fan, speed; 2960 2801 u64 result; 2961 2802 int ret; 2962 2803 ··· 2985 2820 2986 2821 *val = FIELD_GET(ACER_PREDATOR_V4_SENSOR_READING_BIT_MASK, result); 2987 2822 return 0; 2823 + case hwmon_pwm: 2824 + switch (attr) { 2825 + case hwmon_pwm_input: 2826 + fan = acer_wmi_fan_channel_to_fan_id[channel]; 2827 + ret = WMID_gaming_get_gaming_fan_speed(fan, &speed); 2828 + if (ret < 0) 2829 + return ret; 2830 + 2831 + *val = fixp_linear_interpolate(0, 0, 100, U8_MAX, speed); 2832 + return 0; 2833 + case hwmon_pwm_enable: 2834 + fan_bitmap = acer_wmi_fan_channel_to_fan_bitmap[channel]; 2835 + ret = WMID_gaming_get_fan_behavior(fan_bitmap, &mode); 2836 + if (ret < 0) 2837 + return ret; 2838 + 2839 + switch (mode) { 2840 + case ACER_WMID_FAN_MODE_AUTO: 2841 + *val = 2; 2842 + return 0; 2843 + case ACER_WMID_FAN_MODE_TURBO: 2844 + *val = 0; 2845 + return 0; 2846 + case ACER_WMID_FAN_MODE_CUSTOM: 2847 + *val = 1; 2848 + return 0; 2849 + default: 2850 + return -ENXIO; 2851 + } 2852 + default: 2853 + return -EOPNOTSUPP; 2854 + } 2855 + default: 2856 + return -EOPNOTSUPP; 2857 + } 2858 + } 2859 + 2860 + static int acer_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, 2861 + u32 attr, int channel, long val) 2862 + { 2863 + enum acer_wmi_gaming_fan_mode mode; 2864 + u16 fan_bitmap; 2865 + u8 fan, speed; 2866 + 2867 + switch (type) { 2868 + case hwmon_pwm: 2869 + switch (attr) { 2870 + case hwmon_pwm_input: 2871 + fan = acer_wmi_fan_channel_to_fan_id[channel]; 2872 + speed = fixp_linear_interpolate(0, 0, U8_MAX, 100, 2873 + clamp_val(val, 0, U8_MAX)); 2874 + 2875 + return WMID_gaming_set_gaming_fan_speed(fan, speed); 2876 + case hwmon_pwm_enable: 2877 + fan_bitmap = acer_wmi_fan_channel_to_fan_bitmap[channel]; 2878 + 2879 + switch (val) { 2880 + case 0: 2881 + mode = ACER_WMID_FAN_MODE_TURBO; 2882 + break; 2883 + case 1: 2884 + mode = ACER_WMID_FAN_MODE_CUSTOM; 2885 + break; 2886 + case 2: 2887 + mode = ACER_WMID_FAN_MODE_AUTO; 2888 + break; 2889 + default: 2890 + return -EINVAL; 2891 + } 2892 + 2893 + return WMID_gaming_set_fan_behavior(fan_bitmap, mode); 2894 + default: 2895 + return -EOPNOTSUPP; 2896 + } 2988 2897 default: 2989 2898 return -EOPNOTSUPP; 2990 2899 } ··· 3074 2835 HWMON_F_INPUT, 3075 2836 HWMON_F_INPUT 3076 2837 ), 2838 + HWMON_CHANNEL_INFO(pwm, 2839 + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, 2840 + HWMON_PWM_INPUT | HWMON_PWM_ENABLE 2841 + ), 3077 2842 NULL 3078 2843 }; 3079 2844 3080 2845 static const struct hwmon_ops acer_wmi_hwmon_ops = { 3081 2846 .read = acer_wmi_hwmon_read, 2847 + .write = acer_wmi_hwmon_write, 3082 2848 .is_visible = acer_wmi_hwmon_is_visible, 3083 2849 }; 3084 2850
-11
drivers/platform/x86/amd/hfi/hfi.c
··· 12 12 13 13 #include <linux/acpi.h> 14 14 #include <linux/cpu.h> 15 - #include <linux/cpumask.h> 16 15 #include <linux/debugfs.h> 17 16 #include <linux/gfp.h> 18 17 #include <linux/init.h> ··· 94 95 * struct amd_hfi_cpuinfo - HFI workload class info per CPU 95 96 * @cpu: CPU index 96 97 * @apic_id: APIC id of the current CPU 97 - * @cpus: mask of CPUs associated with amd_hfi_cpuinfo 98 98 * @class_index: workload class ID index 99 99 * @nr_class: max number of workload class supported 100 100 * @ipcc_scores: ipcc scores for each class ··· 104 106 struct amd_hfi_cpuinfo { 105 107 int cpu; 106 108 u32 apic_id; 107 - cpumask_var_t cpus; 108 109 s16 class_index; 109 110 u8 nr_class; 110 111 int *ipcc_scores; ··· 292 295 293 296 guard(mutex)(&hfi_cpuinfo_lock); 294 297 295 - if (!zalloc_cpumask_var(&hfi_info->cpus, GFP_KERNEL)) 296 - return -ENOMEM; 297 - 298 - cpumask_set_cpu(cpu, hfi_info->cpus); 299 - 300 298 ret = amd_hfi_set_state(cpu, true); 301 299 if (ret) 302 300 pr_err("WCT enable failed for CPU %u\n", cpu); ··· 320 328 ret = amd_hfi_set_state(cpu, false); 321 329 if (ret) 322 330 pr_err("WCT disable failed for CPU %u\n", cpu); 323 - 324 - free_cpumask_var(hfi_info->cpus); 325 331 326 332 return ret; 327 333 } ··· 505 515 static struct platform_driver amd_hfi_driver = { 506 516 .driver = { 507 517 .name = AMD_HFI_DRIVER, 508 - .owner = THIS_MODULE, 509 518 .pm = &amd_hfi_pm_ops, 510 519 .acpi_match_table = ACPI_PTR(amd_hfi_platform_match), 511 520 },
+4 -5
drivers/platform/x86/amd/hsmp/acpi.c
··· 22 22 #include <linux/module.h> 23 23 #include <linux/platform_device.h> 24 24 #include <linux/sysfs.h> 25 + #include <linux/topology.h> 25 26 #include <linux/uuid.h> 26 27 27 28 #include <uapi/asm-generic/errno-base.h> 28 - 29 - #include <asm/amd/node.h> 30 29 31 30 #include "hsmp.h" 32 31 ··· 585 586 return -ENOMEM; 586 587 587 588 if (!hsmp_pdev->is_probed) { 588 - hsmp_pdev->num_sockets = amd_num_nodes(); 589 - if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES) { 590 - dev_err(&pdev->dev, "Wrong number of sockets\n"); 589 + hsmp_pdev->num_sockets = topology_max_packages(); 590 + if (!hsmp_pdev->num_sockets) { 591 + dev_err(&pdev->dev, "No CPU sockets detected\n"); 591 592 return -ENODEV; 592 593 } 593 594
+7 -7
drivers/platform/x86/amd/pmf/auto-mode.c
··· 114 114 { 115 115 struct power_table_control *pwr_ctrl = &config_store.mode_set[idx].power_control; 116 116 117 - amd_pmf_send_cmd(dev, SET_SPL, false, pwr_ctrl->spl, NULL); 118 - amd_pmf_send_cmd(dev, SET_FPPT, false, pwr_ctrl->fppt, NULL); 119 - amd_pmf_send_cmd(dev, SET_SPPT, false, pwr_ctrl->sppt, NULL); 120 - amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pwr_ctrl->sppt_apu_only, NULL); 121 - amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pwr_ctrl->stt_min, NULL); 122 - amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, 117 + amd_pmf_send_cmd(dev, SET_SPL, SET_CMD, pwr_ctrl->spl, NULL); 118 + amd_pmf_send_cmd(dev, SET_FPPT, SET_CMD, pwr_ctrl->fppt, NULL); 119 + amd_pmf_send_cmd(dev, SET_SPPT, SET_CMD, pwr_ctrl->sppt, NULL); 120 + amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, SET_CMD, pwr_ctrl->sppt_apu_only, NULL); 121 + amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, SET_CMD, pwr_ctrl->stt_min, NULL); 122 + amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, SET_CMD, 123 123 fixp_q88_fromint(pwr_ctrl->stt_skin_temp[STT_TEMP_APU]), NULL); 124 - amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, 124 + amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, SET_CMD, 125 125 fixp_q88_fromint(pwr_ctrl->stt_skin_temp[STT_TEMP_HS2]), NULL); 126 126 127 127 if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX))
+7 -7
drivers/platform/x86/amd/pmf/cnqf.c
··· 76 76 77 77 pc = &config_store.mode_set[src][idx].power_control; 78 78 79 - amd_pmf_send_cmd(dev, SET_SPL, false, pc->spl, NULL); 80 - amd_pmf_send_cmd(dev, SET_FPPT, false, pc->fppt, NULL); 81 - amd_pmf_send_cmd(dev, SET_SPPT, false, pc->sppt, NULL); 82 - amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pc->sppt_apu_only, NULL); 83 - amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pc->stt_min, NULL); 84 - amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, 79 + amd_pmf_send_cmd(dev, SET_SPL, SET_CMD, pc->spl, NULL); 80 + amd_pmf_send_cmd(dev, SET_FPPT, SET_CMD, pc->fppt, NULL); 81 + amd_pmf_send_cmd(dev, SET_SPPT, SET_CMD, pc->sppt, NULL); 82 + amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, SET_CMD, pc->sppt_apu_only, NULL); 83 + amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, SET_CMD, pc->stt_min, NULL); 84 + amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, SET_CMD, 85 85 fixp_q88_fromint(pc->stt_skin_temp[STT_TEMP_APU]), NULL); 86 - amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, 86 + amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, SET_CMD, 87 87 fixp_q88_fromint(pc->stt_skin_temp[STT_TEMP_HS2]), NULL); 88 88 89 89 if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX))
+14 -9
drivers/platform/x86/amd/pmf/core.c
··· 131 131 132 132 /* Transfer table contents */ 133 133 memset(dev->buf, 0, sizeof(dev->m_table)); 134 - amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL); 134 + amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL); 135 135 memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table)); 136 136 137 137 time_elapsed_ms = ktime_to_ms(ktime_get()) - dev->start_time; ··· 289 289 hi = phys_addr >> 32; 290 290 low = phys_addr & GENMASK(31, 0); 291 291 292 - amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, 0, hi, NULL); 293 - amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, 0, low, NULL); 292 + amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, SET_CMD, hi, NULL); 293 + amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, SET_CMD, low, NULL); 294 294 295 295 return 0; 296 296 } ··· 465 465 if (!dev->regbase) 466 466 return -ENOMEM; 467 467 468 - mutex_init(&dev->lock); 469 - mutex_init(&dev->update_mutex); 470 - mutex_init(&dev->cb_mutex); 468 + err = devm_mutex_init(dev->dev, &dev->lock); 469 + if (err) 470 + return err; 471 + 472 + err = devm_mutex_init(dev->dev, &dev->update_mutex); 473 + if (err) 474 + return err; 475 + 476 + err = devm_mutex_init(dev->dev, &dev->cb_mutex); 477 + if (err) 478 + return err; 471 479 472 480 apmf_acpi_init(dev); 473 481 platform_set_drvdata(pdev, dev); ··· 499 491 amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_UNLOAD); 500 492 apmf_acpi_deinit(dev); 501 493 amd_pmf_dbgfs_unregister(dev); 502 - mutex_destroy(&dev->lock); 503 - mutex_destroy(&dev->update_mutex); 504 - mutex_destroy(&dev->cb_mutex); 505 494 } 506 495 507 496 static const struct attribute_group *amd_pmf_driver_groups[] = {
+17 -10
drivers/platform/x86/amd/pmf/pmf.h
··· 119 119 120 120 #define APTS_MAX_STATES 16 121 121 #define CUSTOM_BIOS_INPUT_BITS GENMASK(16, 7) 122 + #define BIOS_INPUTS_MAX 10 123 + 124 + /* amd_pmf_send_cmd() set/get */ 125 + #define SET_CMD false 126 + #define GET_CMD true 127 + 128 + #define METRICS_TABLE_ID 7 122 129 123 130 typedef void (*apmf_event_handler_t)(acpi_handle handle, u32 event, void *data); 124 131 ··· 211 204 u8 skin_temp_apu; 212 205 u8 skin_temp_hs2; 213 206 u8 enable_cnqf; 214 - u32 custom_policy[10]; 207 + u32 custom_policy[BIOS_INPUTS_MAX]; 215 208 } __packed; 216 209 217 210 struct apmf_sbios_req_v2 { ··· 223 216 u32 stt_min_limit; 224 217 u8 skin_temp_apu; 225 218 u8 skin_temp_hs2; 226 - u32 custom_policy[10]; 219 + u32 custom_policy[BIOS_INPUTS_MAX]; 227 220 } __packed; 228 221 229 222 struct apmf_fan_idx { ··· 250 243 u16 vclk_freq; /* MHz */ 251 244 u16 vcn_activity; /* VCN busy % [0-100] */ 252 245 u16 vpeclk_freq; /* MHz */ 253 - u16 ipuclk_freq; /* MHz */ 254 - u16 ipu_busy[8]; /* NPU busy % [0-100] */ 246 + u16 npuclk_freq; /* MHz */ 247 + u16 npu_busy[8]; /* NPU busy % [0-100] */ 255 248 u16 dram_reads; /* MB/sec */ 256 249 u16 dram_writes; /* MB/sec */ 257 250 u16 core_c0residency[16]; /* C0 residency % [0-100] */ 258 - u16 ipu_power; /* mW */ 251 + u16 npu_power; /* mW */ 259 252 u32 apu_power; /* mW */ 260 253 u32 gfx_power; /* mW */ 261 254 u32 dgpu_power; /* mW */ ··· 264 257 u32 filter_alpha_value; /* time constant [us] */ 265 258 u32 metrics_counter; 266 259 u16 memclk_freq; /* MHz */ 267 - u16 mpipuclk_freq; /* MHz */ 268 - u16 ipu_reads; /* MB/sec */ 269 - u16 ipu_writes; /* MB/sec */ 260 + u16 mpnpuclk_freq; /* MHz */ 261 + u16 npu_reads; /* MB/sec */ 262 + u16 npu_writes; /* MB/sec */ 270 263 u32 throttle_residency_prochot; 271 264 u32 throttle_residency_spl; 272 265 u32 throttle_residency_fppt; ··· 362 355 }; 363 356 364 357 struct pmf_bios_inputs_prev { 365 - u32 custom_bios_inputs[10]; 358 + u32 custom_bios_inputs[BIOS_INPUTS_MAX]; 366 359 }; 367 360 368 361 struct amd_pmf_dev { ··· 458 451 struct amd_pmf_notify_smart_pc_update { 459 452 u16 size; 460 453 u32 pending_req; 461 - u32 custom_bios[10]; 454 + u32 custom_bios[BIOS_INPUTS_MAX]; 462 455 } __packed; 463 456 464 457 struct fan_table_control {
+1 -1
drivers/platform/x86/amd/pmf/spc.c
··· 202 202 { 203 203 /* Get the updated metrics table data */ 204 204 memset(dev->buf, 0, dev->mtable_size); 205 - amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL); 205 + amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL); 206 206 207 207 switch (dev->cpu_id) { 208 208 case AMD_CPU_ID_PS:
+19 -19
drivers/platform/x86/amd/pmf/sps.c
··· 192 192 193 193 static void amd_pmf_update_slider_v2(struct amd_pmf_dev *dev, int idx) 194 194 { 195 - amd_pmf_send_cmd(dev, SET_PMF_PPT, false, apts_config_store.val[idx].pmf_ppt, NULL); 196 - amd_pmf_send_cmd(dev, SET_PMF_PPT_APU_ONLY, false, 195 + amd_pmf_send_cmd(dev, SET_PMF_PPT, SET_CMD, apts_config_store.val[idx].pmf_ppt, NULL); 196 + amd_pmf_send_cmd(dev, SET_PMF_PPT_APU_ONLY, SET_CMD, 197 197 apts_config_store.val[idx].ppt_pmf_apu_only, NULL); 198 - amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, 198 + amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, SET_CMD, 199 199 apts_config_store.val[idx].stt_min_limit, NULL); 200 - amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, 200 + amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, SET_CMD, 201 201 fixp_q88_fromint(apts_config_store.val[idx].stt_skin_temp_limit_apu), 202 202 NULL); 203 - amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, 203 + amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, SET_CMD, 204 204 fixp_q88_fromint(apts_config_store.val[idx].stt_skin_temp_limit_hs2), 205 205 NULL); 206 206 } ··· 211 211 int src = amd_pmf_get_power_source(); 212 212 213 213 if (op == SLIDER_OP_SET) { 214 - amd_pmf_send_cmd(dev, SET_SPL, false, config_store.prop[src][idx].spl, NULL); 215 - amd_pmf_send_cmd(dev, SET_FPPT, false, config_store.prop[src][idx].fppt, NULL); 216 - amd_pmf_send_cmd(dev, SET_SPPT, false, config_store.prop[src][idx].sppt, NULL); 217 - amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, 214 + amd_pmf_send_cmd(dev, SET_SPL, SET_CMD, config_store.prop[src][idx].spl, NULL); 215 + amd_pmf_send_cmd(dev, SET_FPPT, SET_CMD, config_store.prop[src][idx].fppt, NULL); 216 + amd_pmf_send_cmd(dev, SET_SPPT, SET_CMD, config_store.prop[src][idx].sppt, NULL); 217 + amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, SET_CMD, 218 218 config_store.prop[src][idx].sppt_apu_only, NULL); 219 - amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, 219 + amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, SET_CMD, 220 220 config_store.prop[src][idx].stt_min, NULL); 221 - amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, 221 + amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, SET_CMD, 222 222 fixp_q88_fromint(config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU]), 223 223 NULL); 224 - amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, 224 + amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, SET_CMD, 225 225 fixp_q88_fromint(config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2]), 226 226 NULL); 227 227 } else if (op == SLIDER_OP_GET) { 228 - amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE, &table->prop[src][idx].spl); 229 - amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE, &table->prop[src][idx].fppt); 230 - amd_pmf_send_cmd(dev, GET_SPPT, true, ARG_NONE, &table->prop[src][idx].sppt); 231 - amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, true, ARG_NONE, 228 + amd_pmf_send_cmd(dev, GET_SPL, GET_CMD, ARG_NONE, &table->prop[src][idx].spl); 229 + amd_pmf_send_cmd(dev, GET_FPPT, GET_CMD, ARG_NONE, &table->prop[src][idx].fppt); 230 + amd_pmf_send_cmd(dev, GET_SPPT, GET_CMD, ARG_NONE, &table->prop[src][idx].sppt); 231 + amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, GET_CMD, ARG_NONE, 232 232 &table->prop[src][idx].sppt_apu_only); 233 - amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, true, ARG_NONE, 233 + amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, GET_CMD, ARG_NONE, 234 234 &table->prop[src][idx].stt_min); 235 - amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, true, ARG_NONE, 235 + amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, GET_CMD, ARG_NONE, 236 236 (u32 *)&table->prop[src][idx].stt_skin_temp[STT_TEMP_APU]); 237 - amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, true, ARG_NONE, 237 + amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, GET_CMD, ARG_NONE, 238 238 (u32 *)&table->prop[src][idx].stt_skin_temp[STT_TEMP_HS2]); 239 239 } 240 240 }
+52 -40
drivers/platform/x86/amd/pmf/tee-if.c
··· 73 73 input_sync(dev->pmf_idev); 74 74 } 75 75 76 + static int amd_pmf_get_bios_output_idx(u32 action_idx) 77 + { 78 + switch (action_idx) { 79 + case PMF_POLICY_BIOS_OUTPUT_1: 80 + return 0; 81 + case PMF_POLICY_BIOS_OUTPUT_2: 82 + return 1; 83 + case PMF_POLICY_BIOS_OUTPUT_3: 84 + return 2; 85 + case PMF_POLICY_BIOS_OUTPUT_4: 86 + return 3; 87 + case PMF_POLICY_BIOS_OUTPUT_5: 88 + return 4; 89 + case PMF_POLICY_BIOS_OUTPUT_6: 90 + return 5; 91 + case PMF_POLICY_BIOS_OUTPUT_7: 92 + return 6; 93 + case PMF_POLICY_BIOS_OUTPUT_8: 94 + return 7; 95 + case PMF_POLICY_BIOS_OUTPUT_9: 96 + return 8; 97 + case PMF_POLICY_BIOS_OUTPUT_10: 98 + return 9; 99 + default: 100 + return -EINVAL; 101 + } 102 + } 103 + 104 + static void amd_pmf_update_bios_output(struct amd_pmf_dev *pdev, struct ta_pmf_action *action) 105 + { 106 + u32 bios_idx; 107 + 108 + bios_idx = amd_pmf_get_bios_output_idx(action->action_index); 109 + 110 + amd_pmf_smartpc_apply_bios_output(pdev, action->value, BIT(bios_idx), bios_idx); 111 + } 112 + 76 113 static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_result *out) 77 114 { 115 + struct ta_pmf_action *action; 78 116 u32 val; 79 117 int idx; 80 118 81 119 for (idx = 0; idx < out->actions_count; idx++) { 82 - val = out->actions_list[idx].value; 83 - switch (out->actions_list[idx].action_index) { 120 + action = &out->actions_list[idx]; 121 + val = action->value; 122 + switch (action->action_index) { 84 123 case PMF_POLICY_SPL: 85 124 if (dev->prev_data->spl != val) { 86 - amd_pmf_send_cmd(dev, SET_SPL, false, val, NULL); 125 + amd_pmf_send_cmd(dev, SET_SPL, SET_CMD, val, NULL); 87 126 dev_dbg(dev->dev, "update SPL: %u\n", val); 88 127 dev->prev_data->spl = val; 89 128 } ··· 130 91 131 92 case PMF_POLICY_SPPT: 132 93 if (dev->prev_data->sppt != val) { 133 - amd_pmf_send_cmd(dev, SET_SPPT, false, val, NULL); 94 + amd_pmf_send_cmd(dev, SET_SPPT, SET_CMD, val, NULL); 134 95 dev_dbg(dev->dev, "update SPPT: %u\n", val); 135 96 dev->prev_data->sppt = val; 136 97 } ··· 138 99 139 100 case PMF_POLICY_FPPT: 140 101 if (dev->prev_data->fppt != val) { 141 - amd_pmf_send_cmd(dev, SET_FPPT, false, val, NULL); 102 + amd_pmf_send_cmd(dev, SET_FPPT, SET_CMD, val, NULL); 142 103 dev_dbg(dev->dev, "update FPPT: %u\n", val); 143 104 dev->prev_data->fppt = val; 144 105 } ··· 146 107 147 108 case PMF_POLICY_SPPT_APU_ONLY: 148 109 if (dev->prev_data->sppt_apuonly != val) { 149 - amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, val, NULL); 110 + amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, SET_CMD, val, NULL); 150 111 dev_dbg(dev->dev, "update SPPT_APU_ONLY: %u\n", val); 151 112 dev->prev_data->sppt_apuonly = val; 152 113 } ··· 154 115 155 116 case PMF_POLICY_STT_MIN: 156 117 if (dev->prev_data->stt_minlimit != val) { 157 - amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, val, NULL); 118 + amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, SET_CMD, val, NULL); 158 119 dev_dbg(dev->dev, "update STT_MIN: %u\n", val); 159 120 dev->prev_data->stt_minlimit = val; 160 121 } ··· 162 123 163 124 case PMF_POLICY_STT_SKINTEMP_APU: 164 125 if (dev->prev_data->stt_skintemp_apu != val) { 165 - amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, 126 + amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, SET_CMD, 166 127 fixp_q88_fromint(val), NULL); 167 128 dev_dbg(dev->dev, "update STT_SKINTEMP_APU: %u\n", val); 168 129 dev->prev_data->stt_skintemp_apu = val; ··· 171 132 172 133 case PMF_POLICY_STT_SKINTEMP_HS2: 173 134 if (dev->prev_data->stt_skintemp_hs2 != val) { 174 - amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, 135 + amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, SET_CMD, 175 136 fixp_q88_fromint(val), NULL); 176 137 dev_dbg(dev->dev, "update STT_SKINTEMP_HS2: %u\n", val); 177 138 dev->prev_data->stt_skintemp_hs2 = val; ··· 180 141 181 142 case PMF_POLICY_P3T: 182 143 if (dev->prev_data->p3t_limit != val) { 183 - amd_pmf_send_cmd(dev, SET_P3T, false, val, NULL); 144 + amd_pmf_send_cmd(dev, SET_P3T, SET_CMD, val, NULL); 184 145 dev_dbg(dev->dev, "update P3T: %u\n", val); 185 146 dev->prev_data->p3t_limit = val; 186 147 } ··· 188 149 189 150 case PMF_POLICY_PMF_PPT: 190 151 if (dev->prev_data->pmf_ppt != val) { 191 - amd_pmf_send_cmd(dev, SET_PMF_PPT, false, val, NULL); 152 + amd_pmf_send_cmd(dev, SET_PMF_PPT, SET_CMD, val, NULL); 192 153 dev_dbg(dev->dev, "update PMF PPT: %u\n", val); 193 154 dev->prev_data->pmf_ppt = val; 194 155 } ··· 196 157 197 158 case PMF_POLICY_PMF_PPT_APU_ONLY: 198 159 if (dev->prev_data->pmf_ppt_apu_only != val) { 199 - amd_pmf_send_cmd(dev, SET_PMF_PPT_APU_ONLY, false, val, NULL); 160 + amd_pmf_send_cmd(dev, SET_PMF_PPT_APU_ONLY, SET_CMD, val, NULL); 200 161 dev_dbg(dev->dev, "update PMF PPT APU ONLY: %u\n", val); 201 162 dev->prev_data->pmf_ppt_apu_only = val; 202 163 } ··· 222 183 break; 223 184 224 185 case PMF_POLICY_BIOS_OUTPUT_1: 225 - amd_pmf_smartpc_apply_bios_output(dev, val, BIT(0), 0); 226 - break; 227 - 228 186 case PMF_POLICY_BIOS_OUTPUT_2: 229 - amd_pmf_smartpc_apply_bios_output(dev, val, BIT(1), 1); 230 - break; 231 - 232 187 case PMF_POLICY_BIOS_OUTPUT_3: 233 - amd_pmf_smartpc_apply_bios_output(dev, val, BIT(2), 2); 234 - break; 235 - 236 188 case PMF_POLICY_BIOS_OUTPUT_4: 237 - amd_pmf_smartpc_apply_bios_output(dev, val, BIT(3), 3); 238 - break; 239 - 240 189 case PMF_POLICY_BIOS_OUTPUT_5: 241 - amd_pmf_smartpc_apply_bios_output(dev, val, BIT(4), 4); 242 - break; 243 - 244 190 case PMF_POLICY_BIOS_OUTPUT_6: 245 - amd_pmf_smartpc_apply_bios_output(dev, val, BIT(5), 5); 246 - break; 247 - 248 191 case PMF_POLICY_BIOS_OUTPUT_7: 249 - amd_pmf_smartpc_apply_bios_output(dev, val, BIT(6), 6); 250 - break; 251 - 252 192 case PMF_POLICY_BIOS_OUTPUT_8: 253 - amd_pmf_smartpc_apply_bios_output(dev, val, BIT(7), 7); 254 - break; 255 - 256 193 case PMF_POLICY_BIOS_OUTPUT_9: 257 - amd_pmf_smartpc_apply_bios_output(dev, val, BIT(8), 8); 258 - break; 259 - 260 194 case PMF_POLICY_BIOS_OUTPUT_10: 261 - amd_pmf_smartpc_apply_bios_output(dev, val, BIT(9), 9); 195 + amd_pmf_update_bios_output(dev, action); 262 196 break; 263 197 } 264 198 }
+1161
drivers/platform/x86/asus-armoury.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Asus Armoury (WMI) attributes driver. 4 + * 5 + * This driver uses the fw_attributes class to expose various WMI functions 6 + * that are present in many gaming and some non-gaming ASUS laptops. 7 + * 8 + * These typically don't fit anywhere else in the sysfs such as under LED class, 9 + * hwmon or others, and are set in Windows using the ASUS Armoury Crate tool. 10 + * 11 + * Copyright(C) 2024 Luke Jones <luke@ljones.dev> 12 + */ 13 + 14 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 15 + 16 + #include <linux/acpi.h> 17 + #include <linux/array_size.h> 18 + #include <linux/bitfield.h> 19 + #include <linux/device.h> 20 + #include <linux/dmi.h> 21 + #include <linux/err.h> 22 + #include <linux/errno.h> 23 + #include <linux/fs.h> 24 + #include <linux/kernel.h> 25 + #include <linux/kmod.h> 26 + #include <linux/kobject.h> 27 + #include <linux/kstrtox.h> 28 + #include <linux/module.h> 29 + #include <linux/mutex.h> 30 + #include <linux/pci.h> 31 + #include <linux/platform_data/x86/asus-wmi.h> 32 + #include <linux/printk.h> 33 + #include <linux/power_supply.h> 34 + #include <linux/sysfs.h> 35 + 36 + #include "asus-armoury.h" 37 + #include "firmware_attributes_class.h" 38 + 39 + #define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" 40 + 41 + #define ASUS_MINI_LED_MODE_MASK GENMASK(1, 0) 42 + /* Standard modes for devices with only on/off */ 43 + #define ASUS_MINI_LED_OFF 0x00 44 + #define ASUS_MINI_LED_ON 0x01 45 + /* Like "on" but the effect is more vibrant or brighter */ 46 + #define ASUS_MINI_LED_STRONG_MODE 0x02 47 + /* New modes for devices with 3 mini-led mode types */ 48 + #define ASUS_MINI_LED_2024_WEAK 0x00 49 + #define ASUS_MINI_LED_2024_STRONG 0x01 50 + #define ASUS_MINI_LED_2024_OFF 0x02 51 + 52 + /* Power tunable attribute name defines */ 53 + #define ATTR_PPT_PL1_SPL "ppt_pl1_spl" 54 + #define ATTR_PPT_PL2_SPPT "ppt_pl2_sppt" 55 + #define ATTR_PPT_PL3_FPPT "ppt_pl3_fppt" 56 + #define ATTR_PPT_APU_SPPT "ppt_apu_sppt" 57 + #define ATTR_PPT_PLATFORM_SPPT "ppt_platform_sppt" 58 + #define ATTR_NV_DYNAMIC_BOOST "nv_dynamic_boost" 59 + #define ATTR_NV_TEMP_TARGET "nv_temp_target" 60 + #define ATTR_NV_BASE_TGP "nv_base_tgp" 61 + #define ATTR_NV_TGP "nv_tgp" 62 + 63 + #define ASUS_ROG_TUNABLE_DC 0 64 + #define ASUS_ROG_TUNABLE_AC 1 65 + 66 + struct rog_tunables { 67 + const struct power_limits *power_limits; 68 + u32 ppt_pl1_spl; // cpu 69 + u32 ppt_pl2_sppt; // cpu 70 + u32 ppt_pl3_fppt; // cpu 71 + u32 ppt_apu_sppt; // plat 72 + u32 ppt_platform_sppt; // plat 73 + 74 + u32 nv_dynamic_boost; 75 + u32 nv_temp_target; 76 + u32 nv_tgp; 77 + }; 78 + 79 + struct asus_armoury_priv { 80 + struct device *fw_attr_dev; 81 + struct kset *fw_attr_kset; 82 + 83 + /* 84 + * Mutex to protect eGPU activation/deactivation 85 + * sequences and dGPU connection status: 86 + * do not allow concurrent changes or changes 87 + * before a reboot if dGPU got disabled. 88 + */ 89 + struct mutex egpu_mutex; 90 + 91 + /* Index 0 for DC, 1 for AC */ 92 + struct rog_tunables *rog_tunables[2]; 93 + 94 + u32 mini_led_dev_id; 95 + u32 gpu_mux_dev_id; 96 + }; 97 + 98 + static struct asus_armoury_priv asus_armoury = { 99 + .egpu_mutex = __MUTEX_INITIALIZER(asus_armoury.egpu_mutex), 100 + }; 101 + 102 + struct fw_attrs_group { 103 + bool pending_reboot; 104 + }; 105 + 106 + static struct fw_attrs_group fw_attrs = { 107 + .pending_reboot = false, 108 + }; 109 + 110 + struct asus_attr_group { 111 + const struct attribute_group *attr_group; 112 + u32 wmi_devid; 113 + }; 114 + 115 + static void asus_set_reboot_and_signal_event(void) 116 + { 117 + fw_attrs.pending_reboot = true; 118 + kobject_uevent(&asus_armoury.fw_attr_dev->kobj, KOBJ_CHANGE); 119 + } 120 + 121 + static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 122 + { 123 + return sysfs_emit(buf, "%d\n", fw_attrs.pending_reboot); 124 + } 125 + 126 + static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot); 127 + 128 + static bool asus_bios_requires_reboot(struct kobj_attribute *attr) 129 + { 130 + return !strcmp(attr->attr.name, "gpu_mux_mode") || 131 + !strcmp(attr->attr.name, "panel_hd_mode"); 132 + } 133 + 134 + /** 135 + * armoury_has_devstate() - Check presence of the WMI function state. 136 + * 137 + * @dev_id: The WMI method ID to check for presence. 138 + * 139 + * Returns: true iif method is supported. 140 + */ 141 + static bool armoury_has_devstate(u32 dev_id) 142 + { 143 + u32 retval; 144 + int status; 145 + 146 + status = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, dev_id, 0, &retval); 147 + pr_debug("%s called (0x%08x), retval: 0x%08x\n", __func__, dev_id, retval); 148 + 149 + return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT); 150 + } 151 + 152 + /** 153 + * armoury_get_devstate() - Get the WMI function state. 154 + * @attr: NULL or the kobj_attribute associated to called WMI function. 155 + * @dev_id: The WMI method ID to call. 156 + * @retval: 157 + * * non-NULL pointer to where to store the value returned from WMI 158 + * * with the function presence bit cleared. 159 + * 160 + * Intended usage is from sysfs attribute checking associated WMI function. 161 + * 162 + * Returns: 163 + * * %-ENODEV - method ID is unsupported. 164 + * * %0 - successful and retval is filled. 165 + * * %other - error from WMI call. 166 + */ 167 + static int armoury_get_devstate(struct kobj_attribute *attr, u32 *retval, u32 dev_id) 168 + { 169 + int err; 170 + 171 + err = asus_wmi_get_devstate_dsts(dev_id, retval); 172 + if (err) { 173 + if (attr) 174 + pr_err("Failed to get %s: %d\n", attr->attr.name, err); 175 + else 176 + pr_err("Failed to get devstate for 0x%x: %d\n", dev_id, err); 177 + 178 + return err; 179 + } 180 + 181 + /* 182 + * asus_wmi_get_devstate_dsts will populate retval with WMI return, but 183 + * the true value is expressed when ASUS_WMI_DSTS_PRESENCE_BIT is clear. 184 + */ 185 + *retval &= ~ASUS_WMI_DSTS_PRESENCE_BIT; 186 + 187 + return 0; 188 + } 189 + 190 + /** 191 + * armoury_set_devstate() - Set the WMI function state. 192 + * @attr: The kobj_attribute associated to called WMI function. 193 + * @dev_id: The WMI method ID to call. 194 + * @value: The new value to be set. 195 + * @retval: Where to store the value returned from WMI or NULL. 196 + * 197 + * Intended usage is from sysfs attribute setting associated WMI function. 198 + * Before calling the presence of the function should be checked. 199 + * 200 + * Every WMI write MUST go through this function to enforce safety checks. 201 + * 202 + * Results !1 is usually considered a fail by ASUS, but some WMI methods 203 + * (like eGPU or CPU cores) do use > 1 to return a status code or similar: 204 + * in these cases caller is interested in the actual return value 205 + * and should perform relevant checks. 206 + * 207 + * Returns: 208 + * * %-EINVAL - attempt to set a dangerous or unsupported value. 209 + * * %-EIO - WMI function returned an error. 210 + * * %0 - successful and retval is filled. 211 + * * %other - error from WMI call. 212 + */ 213 + static int armoury_set_devstate(struct kobj_attribute *attr, 214 + u32 value, u32 *retval, u32 dev_id) 215 + { 216 + u32 result; 217 + int err; 218 + 219 + /* 220 + * Prevent developers from bricking devices or issuing dangerous 221 + * commands that can be difficult or impossible to recover from. 222 + */ 223 + switch (dev_id) { 224 + case ASUS_WMI_DEVID_APU_MEM: 225 + /* 226 + * A hard reset might suffice to save the device, 227 + * but there is no value in sending these commands. 228 + */ 229 + if (value == 0x100 || value == 0x101) { 230 + pr_err("Refusing to set APU memory to unsafe value: 0x%x\n", value); 231 + return -EINVAL; 232 + } 233 + break; 234 + default: 235 + /* No problems are known for this dev_id */ 236 + break; 237 + } 238 + 239 + err = asus_wmi_set_devstate(dev_id, value, retval ? retval : &result); 240 + if (err) { 241 + if (attr) 242 + pr_err("Failed to set %s: %d\n", attr->attr.name, err); 243 + else 244 + pr_err("Failed to set devstate for 0x%x: %d\n", dev_id, err); 245 + 246 + return err; 247 + } 248 + 249 + /* 250 + * If retval == NULL caller is uninterested in return value: 251 + * perform the most common result check here. 252 + */ 253 + if ((retval == NULL) && (result == 0)) { 254 + pr_err("Failed to set %s: (result): 0x%x\n", attr->attr.name, result); 255 + return -EIO; 256 + } 257 + 258 + return 0; 259 + } 260 + 261 + static int armoury_attr_enum_list(char *buf, size_t enum_values) 262 + { 263 + size_t i; 264 + int len = 0; 265 + 266 + for (i = 0; i < enum_values; i++) { 267 + if (i == 0) 268 + len += sysfs_emit_at(buf, len, "%zu", i); 269 + else 270 + len += sysfs_emit_at(buf, len, ";%zu", i); 271 + } 272 + len += sysfs_emit_at(buf, len, "\n"); 273 + 274 + return len; 275 + } 276 + 277 + ssize_t armoury_attr_uint_store(struct kobject *kobj, struct kobj_attribute *attr, 278 + const char *buf, size_t count, u32 min, u32 max, 279 + u32 *store_value, u32 wmi_dev) 280 + { 281 + u32 value; 282 + int err; 283 + 284 + err = kstrtou32(buf, 10, &value); 285 + if (err) 286 + return err; 287 + 288 + if (value < min || value > max) 289 + return -EINVAL; 290 + 291 + err = armoury_set_devstate(attr, value, NULL, wmi_dev); 292 + if (err) 293 + return err; 294 + 295 + if (store_value != NULL) 296 + *store_value = value; 297 + sysfs_notify(kobj, NULL, attr->attr.name); 298 + 299 + if (asus_bios_requires_reboot(attr)) 300 + asus_set_reboot_and_signal_event(); 301 + 302 + return count; 303 + } 304 + 305 + ssize_t armoury_attr_uint_show(struct kobject *kobj, struct kobj_attribute *attr, 306 + char *buf, u32 wmi_dev) 307 + { 308 + u32 result; 309 + int err; 310 + 311 + err = armoury_get_devstate(attr, &result, wmi_dev); 312 + if (err) 313 + return err; 314 + 315 + return sysfs_emit(buf, "%u\n", result); 316 + } 317 + 318 + static ssize_t enum_type_show(struct kobject *kobj, struct kobj_attribute *attr, 319 + char *buf) 320 + { 321 + return sysfs_emit(buf, "enumeration\n"); 322 + } 323 + 324 + static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *attr, 325 + char *buf) 326 + { 327 + return sysfs_emit(buf, "integer\n"); 328 + } 329 + 330 + /* Mini-LED mode **************************************************************/ 331 + 332 + /* Values map for mini-led modes on 2023 and earlier models. */ 333 + static u32 mini_led_mode1_map[] = { 334 + [0] = ASUS_MINI_LED_OFF, 335 + [1] = ASUS_MINI_LED_ON, 336 + }; 337 + 338 + /* Values map for mini-led modes on 2024 and later models. */ 339 + static u32 mini_led_mode2_map[] = { 340 + [0] = ASUS_MINI_LED_2024_OFF, 341 + [1] = ASUS_MINI_LED_2024_WEAK, 342 + [2] = ASUS_MINI_LED_2024_STRONG, 343 + }; 344 + 345 + static ssize_t mini_led_mode_current_value_show(struct kobject *kobj, 346 + struct kobj_attribute *attr, char *buf) 347 + { 348 + u32 *mini_led_mode_map; 349 + size_t mini_led_mode_map_size; 350 + u32 i, mode; 351 + int err; 352 + 353 + switch (asus_armoury.mini_led_dev_id) { 354 + case ASUS_WMI_DEVID_MINI_LED_MODE: 355 + mini_led_mode_map = mini_led_mode1_map; 356 + mini_led_mode_map_size = ARRAY_SIZE(mini_led_mode1_map); 357 + break; 358 + 359 + case ASUS_WMI_DEVID_MINI_LED_MODE2: 360 + mini_led_mode_map = mini_led_mode2_map; 361 + mini_led_mode_map_size = ARRAY_SIZE(mini_led_mode2_map); 362 + break; 363 + 364 + default: 365 + pr_err("Unrecognized mini-LED device: %u\n", asus_armoury.mini_led_dev_id); 366 + return -ENODEV; 367 + } 368 + 369 + err = armoury_get_devstate(attr, &mode, asus_armoury.mini_led_dev_id); 370 + if (err) 371 + return err; 372 + 373 + mode = FIELD_GET(ASUS_MINI_LED_MODE_MASK, 0); 374 + 375 + for (i = 0; i < mini_led_mode_map_size; i++) 376 + if (mode == mini_led_mode_map[i]) 377 + return sysfs_emit(buf, "%u\n", i); 378 + 379 + pr_warn("Unrecognized mini-LED mode: %u", mode); 380 + return -EINVAL; 381 + } 382 + 383 + static ssize_t mini_led_mode_current_value_store(struct kobject *kobj, 384 + struct kobj_attribute *attr, 385 + const char *buf, size_t count) 386 + { 387 + u32 *mini_led_mode_map; 388 + size_t mini_led_mode_map_size; 389 + u32 mode; 390 + int err; 391 + 392 + err = kstrtou32(buf, 10, &mode); 393 + if (err) 394 + return err; 395 + 396 + switch (asus_armoury.mini_led_dev_id) { 397 + case ASUS_WMI_DEVID_MINI_LED_MODE: 398 + mini_led_mode_map = mini_led_mode1_map; 399 + mini_led_mode_map_size = ARRAY_SIZE(mini_led_mode1_map); 400 + break; 401 + 402 + case ASUS_WMI_DEVID_MINI_LED_MODE2: 403 + mini_led_mode_map = mini_led_mode2_map; 404 + mini_led_mode_map_size = ARRAY_SIZE(mini_led_mode2_map); 405 + break; 406 + 407 + default: 408 + pr_err("Unrecognized mini-LED devid: %u\n", asus_armoury.mini_led_dev_id); 409 + return -EINVAL; 410 + } 411 + 412 + if (mode >= mini_led_mode_map_size) { 413 + pr_warn("mini-LED mode unrecognized device: %u\n", mode); 414 + return -ENODEV; 415 + } 416 + 417 + return armoury_attr_uint_store(kobj, attr, buf, count, 418 + 0, mini_led_mode_map[mode], 419 + NULL, asus_armoury.mini_led_dev_id); 420 + } 421 + 422 + static ssize_t mini_led_mode_possible_values_show(struct kobject *kobj, 423 + struct kobj_attribute *attr, char *buf) 424 + { 425 + switch (asus_armoury.mini_led_dev_id) { 426 + case ASUS_WMI_DEVID_MINI_LED_MODE: 427 + return armoury_attr_enum_list(buf, ARRAY_SIZE(mini_led_mode1_map)); 428 + case ASUS_WMI_DEVID_MINI_LED_MODE2: 429 + return armoury_attr_enum_list(buf, ARRAY_SIZE(mini_led_mode2_map)); 430 + default: 431 + return -ENODEV; 432 + } 433 + } 434 + ASUS_ATTR_GROUP_ENUM(mini_led_mode, "mini_led_mode", "Set the mini-LED backlight mode"); 435 + 436 + static ssize_t gpu_mux_mode_current_value_store(struct kobject *kobj, 437 + struct kobj_attribute *attr, 438 + const char *buf, size_t count) 439 + { 440 + int result, err; 441 + bool optimus; 442 + 443 + err = kstrtobool(buf, &optimus); 444 + if (err) 445 + return err; 446 + 447 + if (armoury_has_devstate(ASUS_WMI_DEVID_DGPU)) { 448 + err = armoury_get_devstate(NULL, &result, ASUS_WMI_DEVID_DGPU); 449 + if (err) 450 + return err; 451 + if (result && !optimus) { 452 + pr_warn("Cannot switch MUX to dGPU mode when dGPU is disabled: %02X\n", 453 + result); 454 + return -ENODEV; 455 + } 456 + } 457 + 458 + if (armoury_has_devstate(ASUS_WMI_DEVID_EGPU)) { 459 + err = armoury_get_devstate(NULL, &result, ASUS_WMI_DEVID_EGPU); 460 + if (err) 461 + return err; 462 + if (result && !optimus) { 463 + pr_warn("Cannot switch MUX to dGPU mode when eGPU is enabled\n"); 464 + return -EBUSY; 465 + } 466 + } 467 + 468 + err = armoury_set_devstate(attr, optimus ? 1 : 0, NULL, asus_armoury.gpu_mux_dev_id); 469 + if (err) 470 + return err; 471 + 472 + sysfs_notify(kobj, NULL, attr->attr.name); 473 + asus_set_reboot_and_signal_event(); 474 + 475 + return count; 476 + } 477 + ASUS_WMI_SHOW_INT(gpu_mux_mode_current_value, asus_armoury.gpu_mux_dev_id); 478 + ASUS_ATTR_GROUP_BOOL(gpu_mux_mode, "gpu_mux_mode", "Set the GPU display MUX mode"); 479 + 480 + static ssize_t dgpu_disable_current_value_store(struct kobject *kobj, 481 + struct kobj_attribute *attr, const char *buf, 482 + size_t count) 483 + { 484 + int result, err; 485 + bool disable; 486 + 487 + err = kstrtobool(buf, &disable); 488 + if (err) 489 + return err; 490 + 491 + if (asus_armoury.gpu_mux_dev_id) { 492 + err = armoury_get_devstate(NULL, &result, asus_armoury.gpu_mux_dev_id); 493 + if (err) 494 + return err; 495 + if (!result && disable) { 496 + pr_warn("Cannot disable dGPU when the MUX is in dGPU mode\n"); 497 + return -EBUSY; 498 + } 499 + } 500 + 501 + scoped_guard(mutex, &asus_armoury.egpu_mutex) { 502 + err = armoury_set_devstate(attr, disable ? 1 : 0, NULL, ASUS_WMI_DEVID_DGPU); 503 + if (err) 504 + return err; 505 + } 506 + 507 + sysfs_notify(kobj, NULL, attr->attr.name); 508 + 509 + return count; 510 + } 511 + ASUS_WMI_SHOW_INT(dgpu_disable_current_value, ASUS_WMI_DEVID_DGPU); 512 + ASUS_ATTR_GROUP_BOOL(dgpu_disable, "dgpu_disable", "Disable the dGPU"); 513 + 514 + /* Values map for eGPU activation requests. */ 515 + static u32 egpu_status_map[] = { 516 + [0] = 0x00000000U, 517 + [1] = 0x00000001U, 518 + [2] = 0x00000101U, 519 + [3] = 0x00000201U, 520 + }; 521 + 522 + /* 523 + * armoury_pci_rescan() - Performs a PCI rescan 524 + * 525 + * Bring up any GPU that has been hotplugged in the system. 526 + */ 527 + static void armoury_pci_rescan(void) 528 + { 529 + struct pci_bus *b = NULL; 530 + 531 + pci_lock_rescan_remove(); 532 + while ((b = pci_find_next_bus(b)) != NULL) 533 + pci_rescan_bus(b); 534 + pci_unlock_rescan_remove(); 535 + } 536 + 537 + /* 538 + * The ACPI call to enable the eGPU might also disable the internal dGPU, 539 + * but this is not always the case and on certain models enabling the eGPU 540 + * when the dGPU is either still active or has been disabled without rebooting 541 + * will make both GPUs malfunction and the kernel will detect many 542 + * PCI AER unrecoverable errors. 543 + */ 544 + static ssize_t egpu_enable_current_value_store(struct kobject *kobj, struct kobj_attribute *attr, 545 + const char *buf, size_t count) 546 + { 547 + int err; 548 + u32 requested, enable, result; 549 + 550 + err = kstrtou32(buf, 10, &requested); 551 + if (err) 552 + return err; 553 + 554 + if (requested >= ARRAY_SIZE(egpu_status_map)) 555 + return -EINVAL; 556 + enable = egpu_status_map[requested]; 557 + 558 + scoped_guard(mutex, &asus_armoury.egpu_mutex) { 559 + /* Ensure the eGPU is connected before attempting to activate it. */ 560 + if (enable) { 561 + err = armoury_get_devstate(NULL, &result, ASUS_WMI_DEVID_EGPU_CONNECTED); 562 + if (err) { 563 + pr_warn("Failed to get eGPU connection status: %d\n", err); 564 + return err; 565 + } 566 + if (!result) { 567 + pr_warn("Cannot activate eGPU while undetected\n"); 568 + return -ENOENT; 569 + } 570 + } 571 + 572 + if (asus_armoury.gpu_mux_dev_id) { 573 + err = armoury_get_devstate(NULL, &result, asus_armoury.gpu_mux_dev_id); 574 + if (err) 575 + return err; 576 + 577 + if (!result && enable) { 578 + pr_warn("Cannot enable eGPU when the MUX is in dGPU mode\n"); 579 + return -ENODEV; 580 + } 581 + } 582 + 583 + err = armoury_set_devstate(attr, enable, &result, ASUS_WMI_DEVID_EGPU); 584 + if (err) { 585 + pr_err("Failed to set %s: %d\n", attr->attr.name, err); 586 + return err; 587 + } 588 + 589 + /* 590 + * ACPI returns value 0x01 on success and 0x02 on a partial activation: 591 + * performing a pci rescan will bring up the device in pci-e 3.0 speed, 592 + * after a reboot the device will work at full speed. 593 + */ 594 + switch (result) { 595 + case 0x01: 596 + /* 597 + * When a GPU is in use it does not get disconnected even if 598 + * the ACPI call returns a success. 599 + */ 600 + if (!enable) { 601 + err = armoury_get_devstate(attr, &result, ASUS_WMI_DEVID_EGPU); 602 + if (err) { 603 + pr_warn("Failed to ensure eGPU is deactivated: %d\n", err); 604 + return err; 605 + } 606 + 607 + if (result != 0) 608 + return -EBUSY; 609 + } 610 + 611 + pr_debug("Success changing the eGPU status\n"); 612 + break; 613 + case 0x02: 614 + pr_info("Success changing the eGPU status, a reboot is strongly advised\n"); 615 + asus_set_reboot_and_signal_event(); 616 + break; 617 + default: 618 + pr_err("Failed to change the eGPU status: wmi result is 0x%x\n", result); 619 + return -EIO; 620 + } 621 + } 622 + 623 + /* 624 + * Perform a PCI rescan: on every tested model this is necessary 625 + * to make the eGPU visible on the bus without rebooting. 626 + */ 627 + armoury_pci_rescan(); 628 + 629 + sysfs_notify(kobj, NULL, attr->attr.name); 630 + 631 + return count; 632 + } 633 + 634 + static ssize_t egpu_enable_current_value_show(struct kobject *kobj, struct kobj_attribute *attr, 635 + char *buf) 636 + { 637 + int i, err; 638 + u32 status; 639 + 640 + scoped_guard(mutex, &asus_armoury.egpu_mutex) { 641 + err = armoury_get_devstate(attr, &status, ASUS_WMI_DEVID_EGPU); 642 + if (err) 643 + return err; 644 + } 645 + 646 + for (i = 0; i < ARRAY_SIZE(egpu_status_map); i++) { 647 + if (egpu_status_map[i] == status) 648 + return sysfs_emit(buf, "%u\n", i); 649 + } 650 + 651 + return -EIO; 652 + } 653 + 654 + static ssize_t egpu_enable_possible_values_show(struct kobject *kobj, struct kobj_attribute *attr, 655 + char *buf) 656 + { 657 + return armoury_attr_enum_list(buf, ARRAY_SIZE(egpu_status_map)); 658 + } 659 + ASUS_ATTR_GROUP_ENUM(egpu_enable, "egpu_enable", "Enable the eGPU (also disables dGPU)"); 660 + 661 + /* Device memory available to APU */ 662 + 663 + /* 664 + * Values map for APU reserved memory (index + 1 number of GB). 665 + * Some looks out of order, but are actually correct. 666 + */ 667 + static u32 apu_mem_map[] = { 668 + [0] = 0x000, /* called "AUTO" on the BIOS, is the minimum available */ 669 + [1] = 0x102, 670 + [2] = 0x103, 671 + [3] = 0x104, 672 + [4] = 0x105, 673 + [5] = 0x107, 674 + [6] = 0x108, 675 + [7] = 0x109, 676 + [8] = 0x106, 677 + }; 678 + 679 + static ssize_t apu_mem_current_value_show(struct kobject *kobj, struct kobj_attribute *attr, 680 + char *buf) 681 + { 682 + int err; 683 + u32 mem; 684 + 685 + err = armoury_get_devstate(attr, &mem, ASUS_WMI_DEVID_APU_MEM); 686 + if (err) 687 + return err; 688 + 689 + /* After 0x000 is set, a read will return 0x100 */ 690 + if (mem == 0x100) 691 + return sysfs_emit(buf, "0\n"); 692 + 693 + for (unsigned int i = 0; i < ARRAY_SIZE(apu_mem_map); i++) { 694 + if (apu_mem_map[i] == mem) 695 + return sysfs_emit(buf, "%u\n", i); 696 + } 697 + 698 + pr_warn("Unrecognised value for APU mem 0x%08x\n", mem); 699 + return -EIO; 700 + } 701 + 702 + static ssize_t apu_mem_current_value_store(struct kobject *kobj, struct kobj_attribute *attr, 703 + const char *buf, size_t count) 704 + { 705 + int result, err; 706 + u32 requested, mem; 707 + 708 + result = kstrtou32(buf, 10, &requested); 709 + if (result) 710 + return result; 711 + 712 + if (requested >= ARRAY_SIZE(apu_mem_map)) 713 + return -EINVAL; 714 + mem = apu_mem_map[requested]; 715 + 716 + err = armoury_set_devstate(attr, mem, NULL, ASUS_WMI_DEVID_APU_MEM); 717 + if (err) { 718 + pr_warn("Failed to set apu_mem 0x%x: %d\n", mem, err); 719 + return err; 720 + } 721 + 722 + pr_info("APU memory changed to %uGB, reboot required\n", requested + 1); 723 + sysfs_notify(kobj, NULL, attr->attr.name); 724 + 725 + asus_set_reboot_and_signal_event(); 726 + 727 + return count; 728 + } 729 + 730 + static ssize_t apu_mem_possible_values_show(struct kobject *kobj, struct kobj_attribute *attr, 731 + char *buf) 732 + { 733 + return armoury_attr_enum_list(buf, ARRAY_SIZE(apu_mem_map)); 734 + } 735 + ASUS_ATTR_GROUP_ENUM(apu_mem, "apu_mem", "Set available system RAM (in GB) for the APU to use"); 736 + 737 + /* Define helper to access the current power mode tunable values */ 738 + static inline struct rog_tunables *get_current_tunables(void) 739 + { 740 + if (power_supply_is_system_supplied()) 741 + return asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC]; 742 + 743 + return asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC]; 744 + } 745 + 746 + /* Simple attribute creation */ 747 + ASUS_ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE, "0;1;2\n", 748 + "Show the current mode of charging"); 749 + ASUS_ATTR_GROUP_BOOL_RW(boot_sound, "boot_sound", ASUS_WMI_DEVID_BOOT_SOUND, 750 + "Set the boot POST sound"); 751 + ASUS_ATTR_GROUP_BOOL_RW(mcu_powersave, "mcu_powersave", ASUS_WMI_DEVID_MCU_POWERSAVE, 752 + "Set MCU powersaving mode"); 753 + ASUS_ATTR_GROUP_BOOL_RW(panel_od, "panel_overdrive", ASUS_WMI_DEVID_PANEL_OD, 754 + "Set the panel refresh overdrive"); 755 + ASUS_ATTR_GROUP_BOOL_RW(panel_hd_mode, "panel_hd_mode", ASUS_WMI_DEVID_PANEL_HD, 756 + "Set the panel HD mode to UHD<0> or FHD<1>"); 757 + ASUS_ATTR_GROUP_BOOL_RW(screen_auto_brightness, "screen_auto_brightness", 758 + ASUS_WMI_DEVID_SCREEN_AUTO_BRIGHTNESS, 759 + "Set the panel brightness to Off<0> or On<1>"); 760 + ASUS_ATTR_GROUP_BOOL_RO(egpu_connected, "egpu_connected", ASUS_WMI_DEVID_EGPU_CONNECTED, 761 + "Show the eGPU connection status"); 762 + ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_pl1_spl, ATTR_PPT_PL1_SPL, ASUS_WMI_DEVID_PPT_PL1_SPL, 763 + "Set the CPU slow package limit"); 764 + ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_pl2_sppt, ATTR_PPT_PL2_SPPT, ASUS_WMI_DEVID_PPT_PL2_SPPT, 765 + "Set the CPU fast package limit"); 766 + ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_pl3_fppt, ATTR_PPT_PL3_FPPT, ASUS_WMI_DEVID_PPT_PL3_FPPT, 767 + "Set the CPU fastest package limit"); 768 + ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_apu_sppt, ATTR_PPT_APU_SPPT, ASUS_WMI_DEVID_PPT_APU_SPPT, 769 + "Set the APU package limit"); 770 + ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_platform_sppt, ATTR_PPT_PLATFORM_SPPT, ASUS_WMI_DEVID_PPT_PLAT_SPPT, 771 + "Set the platform package limit"); 772 + ASUS_ATTR_GROUP_ROG_TUNABLE(nv_dynamic_boost, ATTR_NV_DYNAMIC_BOOST, ASUS_WMI_DEVID_NV_DYN_BOOST, 773 + "Set the Nvidia dynamic boost limit"); 774 + ASUS_ATTR_GROUP_ROG_TUNABLE(nv_temp_target, ATTR_NV_TEMP_TARGET, ASUS_WMI_DEVID_NV_THERM_TARGET, 775 + "Set the Nvidia max thermal limit"); 776 + ASUS_ATTR_GROUP_ROG_TUNABLE(nv_tgp, "nv_tgp", ASUS_WMI_DEVID_DGPU_SET_TGP, 777 + "Set the additional TGP on top of the base TGP"); 778 + ASUS_ATTR_GROUP_INT_VALUE_ONLY_RO(nv_base_tgp, ATTR_NV_BASE_TGP, ASUS_WMI_DEVID_DGPU_BASE_TGP, 779 + "Read the base TGP value"); 780 + 781 + /* If an attribute does not require any special case handling add it here */ 782 + static const struct asus_attr_group armoury_attr_groups[] = { 783 + { &egpu_connected_attr_group, ASUS_WMI_DEVID_EGPU_CONNECTED }, 784 + { &egpu_enable_attr_group, ASUS_WMI_DEVID_EGPU }, 785 + { &dgpu_disable_attr_group, ASUS_WMI_DEVID_DGPU }, 786 + { &apu_mem_attr_group, ASUS_WMI_DEVID_APU_MEM }, 787 + 788 + { &ppt_pl1_spl_attr_group, ASUS_WMI_DEVID_PPT_PL1_SPL }, 789 + { &ppt_pl2_sppt_attr_group, ASUS_WMI_DEVID_PPT_PL2_SPPT }, 790 + { &ppt_pl3_fppt_attr_group, ASUS_WMI_DEVID_PPT_PL3_FPPT }, 791 + { &ppt_apu_sppt_attr_group, ASUS_WMI_DEVID_PPT_APU_SPPT }, 792 + { &ppt_platform_sppt_attr_group, ASUS_WMI_DEVID_PPT_PLAT_SPPT }, 793 + { &nv_dynamic_boost_attr_group, ASUS_WMI_DEVID_NV_DYN_BOOST }, 794 + { &nv_temp_target_attr_group, ASUS_WMI_DEVID_NV_THERM_TARGET }, 795 + { &nv_base_tgp_attr_group, ASUS_WMI_DEVID_DGPU_BASE_TGP }, 796 + { &nv_tgp_attr_group, ASUS_WMI_DEVID_DGPU_SET_TGP }, 797 + 798 + { &charge_mode_attr_group, ASUS_WMI_DEVID_CHARGE_MODE }, 799 + { &boot_sound_attr_group, ASUS_WMI_DEVID_BOOT_SOUND }, 800 + { &mcu_powersave_attr_group, ASUS_WMI_DEVID_MCU_POWERSAVE }, 801 + { &panel_od_attr_group, ASUS_WMI_DEVID_PANEL_OD }, 802 + { &panel_hd_mode_attr_group, ASUS_WMI_DEVID_PANEL_HD }, 803 + { &screen_auto_brightness_attr_group, ASUS_WMI_DEVID_SCREEN_AUTO_BRIGHTNESS }, 804 + }; 805 + 806 + /** 807 + * is_power_tunable_attr - Determines if an attribute is a power-related tunable 808 + * @name: The name of the attribute to check 809 + * 810 + * This function checks if the given attribute name is related to power tuning. 811 + * 812 + * Return: true if the attribute is a power-related tunable, false otherwise 813 + */ 814 + static bool is_power_tunable_attr(const char *name) 815 + { 816 + static const char * const power_tunable_attrs[] = { 817 + ATTR_PPT_PL1_SPL, ATTR_PPT_PL2_SPPT, 818 + ATTR_PPT_PL3_FPPT, ATTR_PPT_APU_SPPT, 819 + ATTR_PPT_PLATFORM_SPPT, ATTR_NV_DYNAMIC_BOOST, 820 + ATTR_NV_TEMP_TARGET, ATTR_NV_BASE_TGP, 821 + ATTR_NV_TGP 822 + }; 823 + 824 + for (unsigned int i = 0; i < ARRAY_SIZE(power_tunable_attrs); i++) { 825 + if (!strcmp(name, power_tunable_attrs[i])) 826 + return true; 827 + } 828 + 829 + return false; 830 + } 831 + 832 + /** 833 + * has_valid_limit - Checks if a power-related attribute has a valid limit value 834 + * @name: The name of the attribute to check 835 + * @limits: Pointer to the power_limits structure containing limit values 836 + * 837 + * This function checks if a power-related attribute has a valid limit value. 838 + * It returns false if limits is NULL or if the corresponding limit value is zero. 839 + * 840 + * Return: true if the attribute has a valid limit value, false otherwise 841 + */ 842 + static bool has_valid_limit(const char *name, const struct power_limits *limits) 843 + { 844 + u32 limit_value = 0; 845 + 846 + if (!limits) 847 + return false; 848 + 849 + if (!strcmp(name, ATTR_PPT_PL1_SPL)) 850 + limit_value = limits->ppt_pl1_spl_max; 851 + else if (!strcmp(name, ATTR_PPT_PL2_SPPT)) 852 + limit_value = limits->ppt_pl2_sppt_max; 853 + else if (!strcmp(name, ATTR_PPT_PL3_FPPT)) 854 + limit_value = limits->ppt_pl3_fppt_max; 855 + else if (!strcmp(name, ATTR_PPT_APU_SPPT)) 856 + limit_value = limits->ppt_apu_sppt_max; 857 + else if (!strcmp(name, ATTR_PPT_PLATFORM_SPPT)) 858 + limit_value = limits->ppt_platform_sppt_max; 859 + else if (!strcmp(name, ATTR_NV_DYNAMIC_BOOST)) 860 + limit_value = limits->nv_dynamic_boost_max; 861 + else if (!strcmp(name, ATTR_NV_TEMP_TARGET)) 862 + limit_value = limits->nv_temp_target_max; 863 + else if (!strcmp(name, ATTR_NV_BASE_TGP) || 864 + !strcmp(name, ATTR_NV_TGP)) 865 + limit_value = limits->nv_tgp_max; 866 + 867 + return limit_value > 0; 868 + } 869 + 870 + static int asus_fw_attr_add(void) 871 + { 872 + const struct rog_tunables *const ac_rog_tunables = 873 + asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC]; 874 + const struct power_limits *limits; 875 + bool should_create; 876 + const char *name; 877 + int err, i; 878 + 879 + asus_armoury.fw_attr_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0), 880 + NULL, "%s", DRIVER_NAME); 881 + if (IS_ERR(asus_armoury.fw_attr_dev)) { 882 + err = PTR_ERR(asus_armoury.fw_attr_dev); 883 + goto fail_class_get; 884 + } 885 + 886 + asus_armoury.fw_attr_kset = kset_create_and_add("attributes", NULL, 887 + &asus_armoury.fw_attr_dev->kobj); 888 + if (!asus_armoury.fw_attr_kset) { 889 + err = -ENOMEM; 890 + goto err_destroy_classdev; 891 + } 892 + 893 + err = sysfs_create_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr); 894 + if (err) { 895 + pr_err("Failed to create sysfs level attributes\n"); 896 + goto err_destroy_kset; 897 + } 898 + 899 + asus_armoury.mini_led_dev_id = 0; 900 + if (armoury_has_devstate(ASUS_WMI_DEVID_MINI_LED_MODE)) 901 + asus_armoury.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE; 902 + else if (armoury_has_devstate(ASUS_WMI_DEVID_MINI_LED_MODE2)) 903 + asus_armoury.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE2; 904 + 905 + if (asus_armoury.mini_led_dev_id) { 906 + err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj, 907 + &mini_led_mode_attr_group); 908 + if (err) { 909 + pr_err("Failed to create sysfs-group for mini_led\n"); 910 + goto err_remove_file; 911 + } 912 + } 913 + 914 + asus_armoury.gpu_mux_dev_id = 0; 915 + if (armoury_has_devstate(ASUS_WMI_DEVID_GPU_MUX)) 916 + asus_armoury.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX; 917 + else if (armoury_has_devstate(ASUS_WMI_DEVID_GPU_MUX_VIVO)) 918 + asus_armoury.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX_VIVO; 919 + 920 + if (asus_armoury.gpu_mux_dev_id) { 921 + err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj, 922 + &gpu_mux_mode_attr_group); 923 + if (err) { 924 + pr_err("Failed to create sysfs-group for gpu_mux\n"); 925 + goto err_remove_mini_led_group; 926 + } 927 + } 928 + 929 + for (i = 0; i < ARRAY_SIZE(armoury_attr_groups); i++) { 930 + if (!armoury_has_devstate(armoury_attr_groups[i].wmi_devid)) 931 + continue; 932 + 933 + /* Always create by default, unless PPT is not present */ 934 + should_create = true; 935 + name = armoury_attr_groups[i].attr_group->name; 936 + 937 + /* Check if this is a power-related tunable requiring limits */ 938 + if (ac_rog_tunables && ac_rog_tunables->power_limits && 939 + is_power_tunable_attr(name)) { 940 + limits = ac_rog_tunables->power_limits; 941 + /* Check only AC: if not present then DC won't be either */ 942 + should_create = has_valid_limit(name, limits); 943 + if (!should_create) 944 + pr_debug("Missing max value for tunable %s\n", name); 945 + } 946 + 947 + if (should_create) { 948 + err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj, 949 + armoury_attr_groups[i].attr_group); 950 + if (err) { 951 + pr_err("Failed to create sysfs-group for %s\n", 952 + armoury_attr_groups[i].attr_group->name); 953 + goto err_remove_groups; 954 + } 955 + } 956 + } 957 + 958 + return 0; 959 + 960 + err_remove_groups: 961 + while (i--) { 962 + if (armoury_has_devstate(armoury_attr_groups[i].wmi_devid)) 963 + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, 964 + armoury_attr_groups[i].attr_group); 965 + } 966 + if (asus_armoury.gpu_mux_dev_id) 967 + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &gpu_mux_mode_attr_group); 968 + err_remove_mini_led_group: 969 + if (asus_armoury.mini_led_dev_id) 970 + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &mini_led_mode_attr_group); 971 + err_remove_file: 972 + sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr); 973 + err_destroy_kset: 974 + kset_unregister(asus_armoury.fw_attr_kset); 975 + err_destroy_classdev: 976 + fail_class_get: 977 + device_destroy(&firmware_attributes_class, MKDEV(0, 0)); 978 + return err; 979 + } 980 + 981 + /* Init / exit ****************************************************************/ 982 + 983 + /* Set up the min/max and defaults for ROG tunables */ 984 + static void init_rog_tunables(void) 985 + { 986 + const struct power_limits *ac_limits, *dc_limits; 987 + struct rog_tunables *ac_rog_tunables = NULL, *dc_rog_tunables = NULL; 988 + const struct power_data *power_data; 989 + const struct dmi_system_id *dmi_id; 990 + 991 + /* Match the system against the power_limits table */ 992 + dmi_id = dmi_first_match(power_limits); 993 + if (!dmi_id) { 994 + pr_warn("No matching power limits found for this system\n"); 995 + return; 996 + } 997 + 998 + /* Get the power data for this system */ 999 + power_data = dmi_id->driver_data; 1000 + if (!power_data) { 1001 + pr_info("No power data available for this system\n"); 1002 + return; 1003 + } 1004 + 1005 + /* Initialize AC power tunables */ 1006 + ac_limits = power_data->ac_data; 1007 + if (ac_limits) { 1008 + ac_rog_tunables = kzalloc(sizeof(*asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC]), 1009 + GFP_KERNEL); 1010 + if (!ac_rog_tunables) 1011 + goto err_nomem; 1012 + 1013 + asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC] = ac_rog_tunables; 1014 + ac_rog_tunables->power_limits = ac_limits; 1015 + 1016 + /* Set initial AC values */ 1017 + ac_rog_tunables->ppt_pl1_spl = 1018 + ac_limits->ppt_pl1_spl_def ? 1019 + ac_limits->ppt_pl1_spl_def : 1020 + ac_limits->ppt_pl1_spl_max; 1021 + 1022 + ac_rog_tunables->ppt_pl2_sppt = 1023 + ac_limits->ppt_pl2_sppt_def ? 1024 + ac_limits->ppt_pl2_sppt_def : 1025 + ac_limits->ppt_pl2_sppt_max; 1026 + 1027 + ac_rog_tunables->ppt_pl3_fppt = 1028 + ac_limits->ppt_pl3_fppt_def ? 1029 + ac_limits->ppt_pl3_fppt_def : 1030 + ac_limits->ppt_pl3_fppt_max; 1031 + 1032 + ac_rog_tunables->ppt_apu_sppt = 1033 + ac_limits->ppt_apu_sppt_def ? 1034 + ac_limits->ppt_apu_sppt_def : 1035 + ac_limits->ppt_apu_sppt_max; 1036 + 1037 + ac_rog_tunables->ppt_platform_sppt = 1038 + ac_limits->ppt_platform_sppt_def ? 1039 + ac_limits->ppt_platform_sppt_def : 1040 + ac_limits->ppt_platform_sppt_max; 1041 + 1042 + ac_rog_tunables->nv_dynamic_boost = 1043 + ac_limits->nv_dynamic_boost_max; 1044 + ac_rog_tunables->nv_temp_target = 1045 + ac_limits->nv_temp_target_max; 1046 + ac_rog_tunables->nv_tgp = ac_limits->nv_tgp_max; 1047 + 1048 + pr_debug("AC power limits initialized for %s\n", dmi_id->matches[0].substr); 1049 + } else { 1050 + pr_debug("No AC PPT limits defined\n"); 1051 + } 1052 + 1053 + /* Initialize DC power tunables */ 1054 + dc_limits = power_data->dc_data; 1055 + if (dc_limits) { 1056 + dc_rog_tunables = kzalloc(sizeof(*asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC]), 1057 + GFP_KERNEL); 1058 + if (!dc_rog_tunables) { 1059 + kfree(ac_rog_tunables); 1060 + goto err_nomem; 1061 + } 1062 + 1063 + asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC] = dc_rog_tunables; 1064 + dc_rog_tunables->power_limits = dc_limits; 1065 + 1066 + /* Set initial DC values */ 1067 + dc_rog_tunables->ppt_pl1_spl = 1068 + dc_limits->ppt_pl1_spl_def ? 1069 + dc_limits->ppt_pl1_spl_def : 1070 + dc_limits->ppt_pl1_spl_max; 1071 + 1072 + dc_rog_tunables->ppt_pl2_sppt = 1073 + dc_limits->ppt_pl2_sppt_def ? 1074 + dc_limits->ppt_pl2_sppt_def : 1075 + dc_limits->ppt_pl2_sppt_max; 1076 + 1077 + dc_rog_tunables->ppt_pl3_fppt = 1078 + dc_limits->ppt_pl3_fppt_def ? 1079 + dc_limits->ppt_pl3_fppt_def : 1080 + dc_limits->ppt_pl3_fppt_max; 1081 + 1082 + dc_rog_tunables->ppt_apu_sppt = 1083 + dc_limits->ppt_apu_sppt_def ? 1084 + dc_limits->ppt_apu_sppt_def : 1085 + dc_limits->ppt_apu_sppt_max; 1086 + 1087 + dc_rog_tunables->ppt_platform_sppt = 1088 + dc_limits->ppt_platform_sppt_def ? 1089 + dc_limits->ppt_platform_sppt_def : 1090 + dc_limits->ppt_platform_sppt_max; 1091 + 1092 + dc_rog_tunables->nv_dynamic_boost = 1093 + dc_limits->nv_dynamic_boost_max; 1094 + dc_rog_tunables->nv_temp_target = 1095 + dc_limits->nv_temp_target_max; 1096 + dc_rog_tunables->nv_tgp = dc_limits->nv_tgp_max; 1097 + 1098 + pr_debug("DC power limits initialized for %s\n", dmi_id->matches[0].substr); 1099 + } else { 1100 + pr_debug("No DC PPT limits defined\n"); 1101 + } 1102 + 1103 + return; 1104 + 1105 + err_nomem: 1106 + pr_err("Failed to allocate memory for tunables\n"); 1107 + } 1108 + 1109 + static int __init asus_fw_init(void) 1110 + { 1111 + char *wmi_uid; 1112 + 1113 + wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID); 1114 + if (!wmi_uid) 1115 + return -ENODEV; 1116 + 1117 + /* 1118 + * if equal to "ASUSWMI" then it's DCTS that can't be used for this 1119 + * driver, DSTS is required. 1120 + */ 1121 + if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI)) 1122 + return -ENODEV; 1123 + 1124 + init_rog_tunables(); 1125 + 1126 + /* Must always be last step to ensure data is available */ 1127 + return asus_fw_attr_add(); 1128 + } 1129 + 1130 + static void __exit asus_fw_exit(void) 1131 + { 1132 + int i; 1133 + 1134 + for (i = ARRAY_SIZE(armoury_attr_groups) - 1; i >= 0; i--) { 1135 + if (armoury_has_devstate(armoury_attr_groups[i].wmi_devid)) 1136 + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, 1137 + armoury_attr_groups[i].attr_group); 1138 + } 1139 + 1140 + if (asus_armoury.gpu_mux_dev_id) 1141 + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &gpu_mux_mode_attr_group); 1142 + 1143 + if (asus_armoury.mini_led_dev_id) 1144 + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &mini_led_mode_attr_group); 1145 + 1146 + sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr); 1147 + kset_unregister(asus_armoury.fw_attr_kset); 1148 + device_destroy(&firmware_attributes_class, MKDEV(0, 0)); 1149 + 1150 + kfree(asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC]); 1151 + kfree(asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC]); 1152 + } 1153 + 1154 + module_init(asus_fw_init); 1155 + module_exit(asus_fw_exit); 1156 + 1157 + MODULE_IMPORT_NS("ASUS_WMI"); 1158 + MODULE_AUTHOR("Luke Jones <luke@ljones.dev>"); 1159 + MODULE_DESCRIPTION("ASUS BIOS Configuration Driver"); 1160 + MODULE_LICENSE("GPL"); 1161 + MODULE_ALIAS("wmi:" ASUS_NB_WMI_EVENT_GUID);
+1541
drivers/platform/x86/asus-armoury.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 2 + * 3 + * Definitions for kernel modules using asus-armoury driver 4 + * 5 + * Copyright (c) 2024 Luke Jones <luke@ljones.dev> 6 + */ 7 + 8 + #ifndef _ASUS_ARMOURY_H_ 9 + #define _ASUS_ARMOURY_H_ 10 + 11 + #include <linux/dmi.h> 12 + #include <linux/platform_device.h> 13 + #include <linux/sysfs.h> 14 + #include <linux/types.h> 15 + 16 + #define DRIVER_NAME "asus-armoury" 17 + 18 + /** 19 + * armoury_attr_uint_store() - Send an uint to WMI method if within min/max. 20 + * @kobj: Pointer to the driver object. 21 + * @attr: Pointer to the attribute calling this function. 22 + * @buf: The buffer to read from, this is parsed to `uint` type. 23 + * @count: Required by sysfs attribute macros, pass in from the callee attr. 24 + * @min: Minimum accepted value. Below this returns -EINVAL. 25 + * @max: Maximum accepted value. Above this returns -EINVAL. 26 + * @store_value: Pointer to where the parsed value should be stored. 27 + * @wmi_dev: The WMI function ID to use. 28 + * 29 + * This function is intended to be generic so it can be called from any "_store" 30 + * attribute which works only with integers. 31 + * 32 + * Integers to be sent to the WMI method is inclusive range checked and 33 + * an error returned if out of range. 34 + * 35 + * If the value is valid and WMI is success then the sysfs attribute is notified 36 + * and if asus_bios_requires_reboot() is true then reboot attribute 37 + * is also notified. 38 + * 39 + * Returns: Either count, or an error. 40 + */ 41 + ssize_t armoury_attr_uint_store(struct kobject *kobj, struct kobj_attribute *attr, 42 + const char *buf, size_t count, u32 min, u32 max, 43 + u32 *store_value, u32 wmi_dev); 44 + 45 + /** 46 + * armoury_attr_uint_show() - Receive an uint from a WMI method. 47 + * @kobj: Pointer to the driver object. 48 + * @attr: Pointer to the attribute calling this function. 49 + * @buf: The buffer to write to, as an `uint` type. 50 + * @wmi_dev: The WMI function ID to use. 51 + * 52 + * This function is intended to be generic so it can be called from any "_show" 53 + * attribute which works only with integers. 54 + * 55 + * Returns: Either count, or an error. 56 + */ 57 + ssize_t armoury_attr_uint_show(struct kobject *kobj, struct kobj_attribute *attr, 58 + char *buf, u32 wmi_dev); 59 + 60 + #define __ASUS_ATTR_RO(_func, _name) \ 61 + { \ 62 + .attr = { .name = __stringify(_name), .mode = 0444 }, \ 63 + .show = _func##_##_name##_show, \ 64 + } 65 + 66 + #define __ASUS_ATTR_RO_AS(_name, _show) \ 67 + { \ 68 + .attr = { .name = __stringify(_name), .mode = 0444 }, \ 69 + .show = _show, \ 70 + } 71 + 72 + #define __ASUS_ATTR_RW(_func, _name) \ 73 + __ATTR(_name, 0644, _func##_##_name##_show, _func##_##_name##_store) 74 + 75 + #define __WMI_STORE_INT(_attr, _min, _max, _wmi) \ 76 + static ssize_t _attr##_store(struct kobject *kobj, \ 77 + struct kobj_attribute *attr, \ 78 + const char *buf, size_t count) \ 79 + { \ 80 + return armoury_attr_uint_store(kobj, attr, buf, count, _min, \ 81 + _max, NULL, _wmi); \ 82 + } 83 + 84 + #define ASUS_WMI_SHOW_INT(_attr, _wmi) \ 85 + static ssize_t _attr##_show(struct kobject *kobj, \ 86 + struct kobj_attribute *attr, char *buf) \ 87 + { \ 88 + return armoury_attr_uint_show(kobj, attr, buf, _wmi); \ 89 + } 90 + 91 + /* Create functions and attributes for use in other macros or on their own */ 92 + 93 + /* Shows a formatted static variable */ 94 + #define __ATTR_SHOW_FMT(_prop, _attrname, _fmt, _val) \ 95 + static ssize_t _attrname##_##_prop##_show( \ 96 + struct kobject *kobj, struct kobj_attribute *attr, char *buf) \ 97 + { \ 98 + return sysfs_emit(buf, _fmt, _val); \ 99 + } \ 100 + static struct kobj_attribute attr_##_attrname##_##_prop = \ 101 + __ASUS_ATTR_RO(_attrname, _prop) 102 + 103 + #define __ATTR_RO_INT_GROUP_ENUM(_attrname, _wmi, _fsname, _possible, _dispname)\ 104 + ASUS_WMI_SHOW_INT(_attrname##_current_value, _wmi); \ 105 + static struct kobj_attribute attr_##_attrname##_current_value = \ 106 + __ASUS_ATTR_RO(_attrname, current_value); \ 107 + __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \ 108 + __ATTR_SHOW_FMT(possible_values, _attrname, "%s\n", _possible); \ 109 + static struct kobj_attribute attr_##_attrname##_type = \ 110 + __ASUS_ATTR_RO_AS(type, enum_type_show); \ 111 + static struct attribute *_attrname##_attrs[] = { \ 112 + &attr_##_attrname##_current_value.attr, \ 113 + &attr_##_attrname##_display_name.attr, \ 114 + &attr_##_attrname##_possible_values.attr, \ 115 + &attr_##_attrname##_type.attr, \ 116 + NULL \ 117 + }; \ 118 + static const struct attribute_group _attrname##_attr_group = { \ 119 + .name = _fsname, .attrs = _attrname##_attrs \ 120 + } 121 + 122 + #define __ATTR_RW_INT_GROUP_ENUM(_attrname, _minv, _maxv, _wmi, _fsname,\ 123 + _possible, _dispname) \ 124 + __WMI_STORE_INT(_attrname##_current_value, _minv, _maxv, _wmi); \ 125 + ASUS_WMI_SHOW_INT(_attrname##_current_value, _wmi); \ 126 + static struct kobj_attribute attr_##_attrname##_current_value = \ 127 + __ASUS_ATTR_RW(_attrname, current_value); \ 128 + __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \ 129 + __ATTR_SHOW_FMT(possible_values, _attrname, "%s\n", _possible); \ 130 + static struct kobj_attribute attr_##_attrname##_type = \ 131 + __ASUS_ATTR_RO_AS(type, enum_type_show); \ 132 + static struct attribute *_attrname##_attrs[] = { \ 133 + &attr_##_attrname##_current_value.attr, \ 134 + &attr_##_attrname##_display_name.attr, \ 135 + &attr_##_attrname##_possible_values.attr, \ 136 + &attr_##_attrname##_type.attr, \ 137 + NULL \ 138 + }; \ 139 + static const struct attribute_group _attrname##_attr_group = { \ 140 + .name = _fsname, .attrs = _attrname##_attrs \ 141 + } 142 + 143 + /* Boolean style enumeration, base macro. Requires adding show/store */ 144 + #define __ATTR_GROUP_ENUM(_attrname, _fsname, _possible, _dispname) \ 145 + __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \ 146 + __ATTR_SHOW_FMT(possible_values, _attrname, "%s\n", _possible); \ 147 + static struct kobj_attribute attr_##_attrname##_type = \ 148 + __ASUS_ATTR_RO_AS(type, enum_type_show); \ 149 + static struct attribute *_attrname##_attrs[] = { \ 150 + &attr_##_attrname##_current_value.attr, \ 151 + &attr_##_attrname##_display_name.attr, \ 152 + &attr_##_attrname##_possible_values.attr, \ 153 + &attr_##_attrname##_type.attr, \ 154 + NULL \ 155 + }; \ 156 + static const struct attribute_group _attrname##_attr_group = { \ 157 + .name = _fsname, .attrs = _attrname##_attrs \ 158 + } 159 + 160 + #define ASUS_ATTR_GROUP_BOOL_RO(_attrname, _fsname, _wmi, _dispname) \ 161 + __ATTR_RO_INT_GROUP_ENUM(_attrname, _wmi, _fsname, "0;1", _dispname) 162 + 163 + 164 + #define ASUS_ATTR_GROUP_BOOL_RW(_attrname, _fsname, _wmi, _dispname) \ 165 + __ATTR_RW_INT_GROUP_ENUM(_attrname, 0, 1, _wmi, _fsname, "0;1", _dispname) 166 + 167 + #define ASUS_ATTR_GROUP_ENUM_INT_RO(_attrname, _fsname, _wmi, _possible, _dispname) \ 168 + __ATTR_RO_INT_GROUP_ENUM(_attrname, _wmi, _fsname, _possible, _dispname) 169 + 170 + /* 171 + * Requires <name>_current_value_show(), <name>_current_value_show() 172 + */ 173 + #define ASUS_ATTR_GROUP_BOOL(_attrname, _fsname, _dispname) \ 174 + static struct kobj_attribute attr_##_attrname##_current_value = \ 175 + __ASUS_ATTR_RW(_attrname, current_value); \ 176 + __ATTR_GROUP_ENUM(_attrname, _fsname, "0;1", _dispname) 177 + 178 + /* 179 + * Requires <name>_current_value_show(), <name>_current_value_show() 180 + * and <name>_possible_values_show() 181 + */ 182 + #define ASUS_ATTR_GROUP_ENUM(_attrname, _fsname, _dispname) \ 183 + __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \ 184 + static struct kobj_attribute attr_##_attrname##_current_value = \ 185 + __ASUS_ATTR_RW(_attrname, current_value); \ 186 + static struct kobj_attribute attr_##_attrname##_possible_values = \ 187 + __ASUS_ATTR_RO(_attrname, possible_values); \ 188 + static struct kobj_attribute attr_##_attrname##_type = \ 189 + __ASUS_ATTR_RO_AS(type, enum_type_show); \ 190 + static struct attribute *_attrname##_attrs[] = { \ 191 + &attr_##_attrname##_current_value.attr, \ 192 + &attr_##_attrname##_display_name.attr, \ 193 + &attr_##_attrname##_possible_values.attr, \ 194 + &attr_##_attrname##_type.attr, \ 195 + NULL \ 196 + }; \ 197 + static const struct attribute_group _attrname##_attr_group = { \ 198 + .name = _fsname, .attrs = _attrname##_attrs \ 199 + } 200 + 201 + #define ASUS_ATTR_GROUP_INT_VALUE_ONLY_RO(_attrname, _fsname, _wmi, _dispname) \ 202 + ASUS_WMI_SHOW_INT(_attrname##_current_value, _wmi); \ 203 + static struct kobj_attribute attr_##_attrname##_current_value = \ 204 + __ASUS_ATTR_RO(_attrname, current_value); \ 205 + __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \ 206 + static struct kobj_attribute attr_##_attrname##_type = \ 207 + __ASUS_ATTR_RO_AS(type, int_type_show); \ 208 + static struct attribute *_attrname##_attrs[] = { \ 209 + &attr_##_attrname##_current_value.attr, \ 210 + &attr_##_attrname##_display_name.attr, \ 211 + &attr_##_attrname##_type.attr, NULL \ 212 + }; \ 213 + static const struct attribute_group _attrname##_attr_group = { \ 214 + .name = _fsname, .attrs = _attrname##_attrs \ 215 + } 216 + 217 + /* 218 + * ROG PPT attributes need a little different in setup as they 219 + * require rog_tunables members. 220 + */ 221 + 222 + #define __ROG_TUNABLE_SHOW(_prop, _attrname, _val) \ 223 + static ssize_t _attrname##_##_prop##_show( \ 224 + struct kobject *kobj, struct kobj_attribute *attr, char *buf) \ 225 + { \ 226 + struct rog_tunables *tunables = get_current_tunables(); \ 227 + \ 228 + if (!tunables || !tunables->power_limits) \ 229 + return -ENODEV; \ 230 + \ 231 + return sysfs_emit(buf, "%d\n", tunables->power_limits->_val); \ 232 + } \ 233 + static struct kobj_attribute attr_##_attrname##_##_prop = \ 234 + __ASUS_ATTR_RO(_attrname, _prop) 235 + 236 + #define __ROG_TUNABLE_SHOW_DEFAULT(_attrname) \ 237 + static ssize_t _attrname##_default_value_show( \ 238 + struct kobject *kobj, struct kobj_attribute *attr, char *buf) \ 239 + { \ 240 + struct rog_tunables *tunables = get_current_tunables(); \ 241 + \ 242 + if (!tunables || !tunables->power_limits) \ 243 + return -ENODEV; \ 244 + \ 245 + return sysfs_emit( \ 246 + buf, "%d\n", \ 247 + tunables->power_limits->_attrname##_def ? \ 248 + tunables->power_limits->_attrname##_def : \ 249 + tunables->power_limits->_attrname##_max); \ 250 + } \ 251 + static struct kobj_attribute attr_##_attrname##_default_value = \ 252 + __ASUS_ATTR_RO(_attrname, default_value) 253 + 254 + #define __ROG_TUNABLE_RW(_attr, _wmi) \ 255 + static ssize_t _attr##_current_value_store( \ 256 + struct kobject *kobj, struct kobj_attribute *attr, \ 257 + const char *buf, size_t count) \ 258 + { \ 259 + struct rog_tunables *tunables = get_current_tunables(); \ 260 + \ 261 + if (!tunables || !tunables->power_limits) \ 262 + return -ENODEV; \ 263 + \ 264 + if (tunables->power_limits->_attr##_min == \ 265 + tunables->power_limits->_attr##_max) \ 266 + return -EINVAL; \ 267 + \ 268 + return armoury_attr_uint_store(kobj, attr, buf, count, \ 269 + tunables->power_limits->_attr##_min, \ 270 + tunables->power_limits->_attr##_max, \ 271 + &tunables->_attr, _wmi); \ 272 + } \ 273 + static ssize_t _attr##_current_value_show( \ 274 + struct kobject *kobj, struct kobj_attribute *attr, char *buf) \ 275 + { \ 276 + struct rog_tunables *tunables = get_current_tunables(); \ 277 + \ 278 + if (!tunables) \ 279 + return -ENODEV; \ 280 + \ 281 + return sysfs_emit(buf, "%u\n", tunables->_attr); \ 282 + } \ 283 + static struct kobj_attribute attr_##_attr##_current_value = \ 284 + __ASUS_ATTR_RW(_attr, current_value) 285 + 286 + #define ASUS_ATTR_GROUP_ROG_TUNABLE(_attrname, _fsname, _wmi, _dispname) \ 287 + __ROG_TUNABLE_RW(_attrname, _wmi); \ 288 + __ROG_TUNABLE_SHOW_DEFAULT(_attrname); \ 289 + __ROG_TUNABLE_SHOW(min_value, _attrname, _attrname##_min); \ 290 + __ROG_TUNABLE_SHOW(max_value, _attrname, _attrname##_max); \ 291 + __ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", 1); \ 292 + __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \ 293 + static struct kobj_attribute attr_##_attrname##_type = \ 294 + __ASUS_ATTR_RO_AS(type, int_type_show); \ 295 + static struct attribute *_attrname##_attrs[] = { \ 296 + &attr_##_attrname##_current_value.attr, \ 297 + &attr_##_attrname##_default_value.attr, \ 298 + &attr_##_attrname##_min_value.attr, \ 299 + &attr_##_attrname##_max_value.attr, \ 300 + &attr_##_attrname##_scalar_increment.attr, \ 301 + &attr_##_attrname##_display_name.attr, \ 302 + &attr_##_attrname##_type.attr, \ 303 + NULL \ 304 + }; \ 305 + static const struct attribute_group _attrname##_attr_group = { \ 306 + .name = _fsname, .attrs = _attrname##_attrs \ 307 + } 308 + 309 + /* Default is always the maximum value unless *_def is specified */ 310 + struct power_limits { 311 + u8 ppt_pl1_spl_min; 312 + u8 ppt_pl1_spl_def; 313 + u8 ppt_pl1_spl_max; 314 + u8 ppt_pl2_sppt_min; 315 + u8 ppt_pl2_sppt_def; 316 + u8 ppt_pl2_sppt_max; 317 + u8 ppt_pl3_fppt_min; 318 + u8 ppt_pl3_fppt_def; 319 + u8 ppt_pl3_fppt_max; 320 + u8 ppt_apu_sppt_min; 321 + u8 ppt_apu_sppt_def; 322 + u8 ppt_apu_sppt_max; 323 + u8 ppt_platform_sppt_min; 324 + u8 ppt_platform_sppt_def; 325 + u8 ppt_platform_sppt_max; 326 + /* Nvidia GPU specific, default is always max */ 327 + u8 nv_dynamic_boost_def; // unused. exists for macro 328 + u8 nv_dynamic_boost_min; 329 + u8 nv_dynamic_boost_max; 330 + u8 nv_temp_target_def; // unused. exists for macro 331 + u8 nv_temp_target_min; 332 + u8 nv_temp_target_max; 333 + u8 nv_tgp_def; // unused. exists for macro 334 + u8 nv_tgp_min; 335 + u8 nv_tgp_max; 336 + }; 337 + 338 + struct power_data { 339 + const struct power_limits *ac_data; 340 + const struct power_limits *dc_data; 341 + bool requires_fan_curve; 342 + }; 343 + 344 + /* 345 + * For each available attribute there must be a min and a max. 346 + * _def is not required and will be assumed to be default == max if missing. 347 + */ 348 + static const struct dmi_system_id power_limits[] = { 349 + { 350 + .matches = { 351 + DMI_MATCH(DMI_BOARD_NAME, "FA401W"), 352 + }, 353 + .driver_data = &(struct power_data) { 354 + .ac_data = &(struct power_limits) { 355 + .ppt_pl1_spl_min = 15, 356 + .ppt_pl1_spl_max = 80, 357 + .ppt_pl2_sppt_min = 35, 358 + .ppt_pl2_sppt_max = 80, 359 + .ppt_pl3_fppt_min = 35, 360 + .ppt_pl3_fppt_max = 80, 361 + .nv_dynamic_boost_min = 5, 362 + .nv_dynamic_boost_max = 25, 363 + .nv_temp_target_min = 75, 364 + .nv_temp_target_max = 87, 365 + .nv_tgp_min = 55, 366 + .nv_tgp_max = 75, 367 + }, 368 + .dc_data = &(struct power_limits) { 369 + .ppt_pl1_spl_min = 25, 370 + .ppt_pl1_spl_max = 30, 371 + .ppt_pl2_sppt_min = 31, 372 + .ppt_pl2_sppt_max = 44, 373 + .ppt_pl3_fppt_min = 45, 374 + .ppt_pl3_fppt_max = 65, 375 + .nv_temp_target_min = 75, 376 + .nv_temp_target_max = 87, 377 + }, 378 + }, 379 + }, 380 + { 381 + .matches = { 382 + DMI_MATCH(DMI_BOARD_NAME, "FA507N"), 383 + }, 384 + .driver_data = &(struct power_data) { 385 + .ac_data = &(struct power_limits) { 386 + .ppt_pl1_spl_min = 15, 387 + .ppt_pl1_spl_max = 80, 388 + .ppt_pl2_sppt_min = 35, 389 + .ppt_pl2_sppt_max = 80, 390 + .ppt_pl3_fppt_min = 35, 391 + .ppt_pl3_fppt_max = 80, 392 + .nv_dynamic_boost_min = 5, 393 + .nv_dynamic_boost_max = 25, 394 + .nv_temp_target_min = 75, 395 + .nv_temp_target_max = 87, 396 + }, 397 + .dc_data = &(struct power_limits) { 398 + .ppt_pl1_spl_min = 15, 399 + .ppt_pl1_spl_def = 45, 400 + .ppt_pl1_spl_max = 65, 401 + .ppt_pl2_sppt_min = 35, 402 + .ppt_pl2_sppt_def = 54, 403 + .ppt_pl2_sppt_max = 65, 404 + .ppt_pl3_fppt_min = 35, 405 + .ppt_pl3_fppt_max = 65, 406 + .nv_temp_target_min = 75, 407 + .nv_temp_target_max = 87, 408 + }, 409 + }, 410 + }, 411 + { 412 + .matches = { 413 + DMI_MATCH(DMI_BOARD_NAME, "FA507UV"), 414 + }, 415 + .driver_data = &(struct power_data) { 416 + .ac_data = &(struct power_limits) { 417 + .ppt_pl1_spl_min = 15, 418 + .ppt_pl1_spl_max = 80, 419 + .ppt_pl2_sppt_min = 35, 420 + .ppt_pl2_sppt_max = 80, 421 + .ppt_pl3_fppt_min = 35, 422 + .ppt_pl3_fppt_max = 80, 423 + .nv_dynamic_boost_min = 5, 424 + .nv_dynamic_boost_max = 25, 425 + .nv_temp_target_min = 75, 426 + .nv_temp_target_max = 87, 427 + .nv_tgp_min = 55, 428 + .nv_tgp_max = 115, 429 + }, 430 + .dc_data = &(struct power_limits) { 431 + .ppt_pl1_spl_min = 15, 432 + .ppt_pl1_spl_def = 45, 433 + .ppt_pl1_spl_max = 65, 434 + .ppt_pl2_sppt_min = 35, 435 + .ppt_pl2_sppt_def = 54, 436 + .ppt_pl2_sppt_max = 65, 437 + .ppt_pl3_fppt_min = 35, 438 + .ppt_pl3_fppt_max = 65, 439 + .nv_temp_target_min = 75, 440 + .nv_temp_target_max = 87, 441 + }, 442 + }, 443 + }, 444 + { 445 + .matches = { 446 + DMI_MATCH(DMI_BOARD_NAME, "FA507R"), 447 + }, 448 + .driver_data = &(struct power_data) { 449 + .ac_data = &(struct power_limits) { 450 + .ppt_pl1_spl_min = 15, 451 + .ppt_pl1_spl_max = 80, 452 + .ppt_pl2_sppt_min = 25, 453 + .ppt_pl2_sppt_max = 80, 454 + .ppt_pl3_fppt_min = 35, 455 + .ppt_pl3_fppt_max = 80 456 + }, 457 + .dc_data = NULL, 458 + }, 459 + }, 460 + { 461 + .matches = { 462 + DMI_MATCH(DMI_BOARD_NAME, "FA507X"), 463 + }, 464 + .driver_data = &(struct power_data) { 465 + .ac_data = &(struct power_limits) { 466 + .ppt_pl1_spl_min = 15, 467 + .ppt_pl1_spl_max = 80, 468 + .ppt_pl2_sppt_min = 35, 469 + .ppt_pl2_sppt_max = 80, 470 + .ppt_pl3_fppt_min = 35, 471 + .ppt_pl3_fppt_max = 80, 472 + .nv_dynamic_boost_min = 5, 473 + .nv_dynamic_boost_max = 20, 474 + .nv_temp_target_min = 75, 475 + .nv_temp_target_max = 87, 476 + .nv_tgp_min = 55, 477 + .nv_tgp_max = 85, 478 + }, 479 + .dc_data = &(struct power_limits) { 480 + .ppt_pl1_spl_min = 15, 481 + .ppt_pl1_spl_def = 45, 482 + .ppt_pl1_spl_max = 65, 483 + .ppt_pl2_sppt_min = 35, 484 + .ppt_pl2_sppt_def = 54, 485 + .ppt_pl2_sppt_max = 65, 486 + .ppt_pl3_fppt_min = 35, 487 + .ppt_pl3_fppt_max = 65, 488 + .nv_temp_target_min = 75, 489 + .nv_temp_target_max = 87, 490 + }, 491 + }, 492 + }, 493 + { 494 + .matches = { 495 + DMI_MATCH(DMI_BOARD_NAME, "FA507Z"), 496 + }, 497 + .driver_data = &(struct power_data) { 498 + .ac_data = &(struct power_limits) { 499 + .ppt_pl1_spl_min = 28, 500 + .ppt_pl1_spl_max = 65, 501 + .ppt_pl2_sppt_min = 28, 502 + .ppt_pl2_sppt_max = 105, 503 + .nv_dynamic_boost_min = 5, 504 + .nv_dynamic_boost_max = 15, 505 + .nv_temp_target_min = 75, 506 + .nv_temp_target_max = 87, 507 + .nv_tgp_min = 55, 508 + .nv_tgp_max = 85, 509 + }, 510 + .dc_data = &(struct power_limits) { 511 + .ppt_pl1_spl_min = 25, 512 + .ppt_pl1_spl_max = 45, 513 + .ppt_pl2_sppt_min = 35, 514 + .ppt_pl2_sppt_max = 60, 515 + .nv_temp_target_min = 75, 516 + .nv_temp_target_max = 87, 517 + }, 518 + }, 519 + }, 520 + { 521 + .matches = { 522 + DMI_MATCH(DMI_BOARD_NAME, "FA607P"), 523 + }, 524 + .driver_data = &(struct power_data) { 525 + .ac_data = &(struct power_limits) { 526 + .ppt_pl1_spl_min = 30, 527 + .ppt_pl1_spl_def = 100, 528 + .ppt_pl1_spl_max = 135, 529 + .ppt_pl2_sppt_min = 30, 530 + .ppt_pl2_sppt_def = 115, 531 + .ppt_pl2_sppt_max = 135, 532 + .ppt_pl3_fppt_min = 30, 533 + .ppt_pl3_fppt_max = 135, 534 + .nv_dynamic_boost_min = 5, 535 + .nv_dynamic_boost_max = 25, 536 + .nv_temp_target_min = 75, 537 + .nv_temp_target_max = 87, 538 + .nv_tgp_min = 55, 539 + .nv_tgp_max = 115, 540 + }, 541 + .dc_data = &(struct power_limits) { 542 + .ppt_pl1_spl_min = 25, 543 + .ppt_pl1_spl_def = 45, 544 + .ppt_pl1_spl_max = 80, 545 + .ppt_pl2_sppt_min = 25, 546 + .ppt_pl2_sppt_def = 60, 547 + .ppt_pl2_sppt_max = 80, 548 + .ppt_pl3_fppt_min = 25, 549 + .ppt_pl3_fppt_max = 80, 550 + .nv_temp_target_min = 75, 551 + .nv_temp_target_max = 87, 552 + }, 553 + }, 554 + }, 555 + { 556 + .matches = { 557 + DMI_MATCH(DMI_BOARD_NAME, "FA608WI"), 558 + }, 559 + .driver_data = &(struct power_data) { 560 + .ac_data = &(struct power_limits) { 561 + .ppt_pl1_spl_min = 15, 562 + .ppt_pl1_spl_def = 90, 563 + .ppt_pl1_spl_max = 90, 564 + .ppt_pl2_sppt_min = 35, 565 + .ppt_pl2_sppt_def = 90, 566 + .ppt_pl2_sppt_max = 90, 567 + .ppt_pl3_fppt_min = 35, 568 + .ppt_pl3_fppt_def = 90, 569 + .ppt_pl3_fppt_max = 90, 570 + .nv_dynamic_boost_min = 5, 571 + .nv_dynamic_boost_max = 25, 572 + .nv_temp_target_min = 75, 573 + .nv_temp_target_max = 87, 574 + .nv_tgp_min = 55, 575 + .nv_tgp_max = 115, 576 + }, 577 + .dc_data = &(struct power_limits) { 578 + .ppt_pl1_spl_min = 15, 579 + .ppt_pl1_spl_def = 45, 580 + .ppt_pl1_spl_max = 65, 581 + .ppt_pl2_sppt_min = 35, 582 + .ppt_pl2_sppt_def = 54, 583 + .ppt_pl2_sppt_max = 65, 584 + .ppt_pl3_fppt_min = 35, 585 + .ppt_pl3_fppt_def = 65, 586 + .ppt_pl3_fppt_max = 65, 587 + .nv_temp_target_min = 75, 588 + .nv_temp_target_max = 87, 589 + }, 590 + }, 591 + }, 592 + { 593 + .matches = { 594 + DMI_MATCH(DMI_BOARD_NAME, "FA617NS"), 595 + }, 596 + .driver_data = &(struct power_data) { 597 + .ac_data = &(struct power_limits) { 598 + .ppt_apu_sppt_min = 15, 599 + .ppt_apu_sppt_max = 80, 600 + .ppt_platform_sppt_min = 30, 601 + .ppt_platform_sppt_max = 120, 602 + }, 603 + .dc_data = &(struct power_limits) { 604 + .ppt_apu_sppt_min = 25, 605 + .ppt_apu_sppt_max = 35, 606 + .ppt_platform_sppt_min = 45, 607 + .ppt_platform_sppt_max = 100, 608 + }, 609 + }, 610 + }, 611 + { 612 + .matches = { 613 + DMI_MATCH(DMI_BOARD_NAME, "FA617NT"), 614 + }, 615 + .driver_data = &(struct power_data) { 616 + .ac_data = &(struct power_limits) { 617 + .ppt_apu_sppt_min = 15, 618 + .ppt_apu_sppt_max = 80, 619 + .ppt_platform_sppt_min = 30, 620 + .ppt_platform_sppt_max = 115, 621 + }, 622 + .dc_data = &(struct power_limits) { 623 + .ppt_apu_sppt_min = 15, 624 + .ppt_apu_sppt_max = 45, 625 + .ppt_platform_sppt_min = 30, 626 + .ppt_platform_sppt_max = 50, 627 + }, 628 + }, 629 + }, 630 + { 631 + .matches = { 632 + DMI_MATCH(DMI_BOARD_NAME, "FA617XS"), 633 + }, 634 + .driver_data = &(struct power_data) { 635 + .ac_data = &(struct power_limits) { 636 + .ppt_apu_sppt_min = 15, 637 + .ppt_apu_sppt_max = 80, 638 + .ppt_platform_sppt_min = 30, 639 + .ppt_platform_sppt_max = 120, 640 + .nv_temp_target_min = 75, 641 + .nv_temp_target_max = 87, 642 + }, 643 + .dc_data = &(struct power_limits) { 644 + .ppt_apu_sppt_min = 25, 645 + .ppt_apu_sppt_max = 35, 646 + .ppt_platform_sppt_min = 45, 647 + .ppt_platform_sppt_max = 100, 648 + .nv_temp_target_min = 75, 649 + .nv_temp_target_max = 87, 650 + }, 651 + }, 652 + }, 653 + { 654 + .matches = { 655 + DMI_MATCH(DMI_BOARD_NAME, "FX507VI"), 656 + }, 657 + .driver_data = &(struct power_data) { 658 + .ac_data = &(struct power_limits) { 659 + .ppt_pl1_spl_min = 28, 660 + .ppt_pl1_spl_max = 135, 661 + .ppt_pl2_sppt_min = 28, 662 + .ppt_pl2_sppt_max = 135, 663 + .nv_dynamic_boost_min = 5, 664 + .nv_dynamic_boost_max = 25, 665 + .nv_temp_target_min = 75, 666 + .nv_temp_target_max = 87, 667 + }, 668 + .dc_data = &(struct power_limits) { 669 + .ppt_pl1_spl_min = 25, 670 + .ppt_pl1_spl_max = 45, 671 + .ppt_pl2_sppt_min = 35, 672 + .ppt_pl2_sppt_max = 60, 673 + .nv_temp_target_min = 75, 674 + .nv_temp_target_max = 87, 675 + }, 676 + .requires_fan_curve = true, 677 + }, 678 + }, 679 + { 680 + .matches = { 681 + DMI_MATCH(DMI_BOARD_NAME, "FX507VV"), 682 + }, 683 + .driver_data = &(struct power_data) { 684 + .ac_data = &(struct power_limits) { 685 + .ppt_pl1_spl_min = 28, 686 + .ppt_pl1_spl_def = 115, 687 + .ppt_pl1_spl_max = 135, 688 + .ppt_pl2_sppt_min = 28, 689 + .ppt_pl2_sppt_max = 135, 690 + .nv_dynamic_boost_min = 5, 691 + .nv_dynamic_boost_max = 25, 692 + .nv_temp_target_min = 75, 693 + .nv_temp_target_max = 87, 694 + }, 695 + .dc_data = &(struct power_limits) { 696 + .ppt_pl1_spl_min = 25, 697 + .ppt_pl1_spl_max = 45, 698 + .ppt_pl2_sppt_min = 35, 699 + .ppt_pl2_sppt_max = 60, 700 + .nv_temp_target_min = 75, 701 + .nv_temp_target_max = 87, 702 + }, 703 + .requires_fan_curve = true, 704 + }, 705 + }, 706 + { 707 + .matches = { 708 + DMI_MATCH(DMI_BOARD_NAME, "FX507Z"), 709 + }, 710 + .driver_data = &(struct power_data) { 711 + .ac_data = &(struct power_limits) { 712 + .ppt_pl1_spl_min = 28, 713 + .ppt_pl1_spl_max = 90, 714 + .ppt_pl2_sppt_min = 28, 715 + .ppt_pl2_sppt_max = 135, 716 + .nv_dynamic_boost_min = 5, 717 + .nv_dynamic_boost_max = 15, 718 + }, 719 + .dc_data = &(struct power_limits) { 720 + .ppt_pl1_spl_min = 25, 721 + .ppt_pl1_spl_max = 45, 722 + .ppt_pl2_sppt_min = 35, 723 + .ppt_pl2_sppt_max = 60, 724 + }, 725 + .requires_fan_curve = true, 726 + }, 727 + }, 728 + { 729 + .matches = { 730 + DMI_MATCH(DMI_BOARD_NAME, "GA401Q"), 731 + }, 732 + .driver_data = &(struct power_data) { 733 + .ac_data = &(struct power_limits) { 734 + .ppt_pl1_spl_min = 15, 735 + .ppt_pl1_spl_max = 80, 736 + .ppt_pl2_sppt_min = 15, 737 + .ppt_pl2_sppt_max = 80, 738 + }, 739 + .dc_data = NULL, 740 + }, 741 + }, 742 + { 743 + .matches = { 744 + // This model is full AMD. No Nvidia dGPU. 745 + DMI_MATCH(DMI_BOARD_NAME, "GA402R"), 746 + }, 747 + .driver_data = &(struct power_data) { 748 + .ac_data = &(struct power_limits) { 749 + .ppt_apu_sppt_min = 15, 750 + .ppt_apu_sppt_max = 80, 751 + .ppt_platform_sppt_min = 30, 752 + .ppt_platform_sppt_max = 115, 753 + }, 754 + .dc_data = &(struct power_limits) { 755 + .ppt_apu_sppt_min = 25, 756 + .ppt_apu_sppt_def = 30, 757 + .ppt_apu_sppt_max = 45, 758 + .ppt_platform_sppt_min = 40, 759 + .ppt_platform_sppt_max = 60, 760 + }, 761 + }, 762 + }, 763 + { 764 + .matches = { 765 + DMI_MATCH(DMI_BOARD_NAME, "GA402X"), 766 + }, 767 + .driver_data = &(struct power_data) { 768 + .ac_data = &(struct power_limits) { 769 + .ppt_pl1_spl_min = 15, 770 + .ppt_pl1_spl_def = 35, 771 + .ppt_pl1_spl_max = 80, 772 + .ppt_pl2_sppt_min = 25, 773 + .ppt_pl2_sppt_def = 65, 774 + .ppt_pl2_sppt_max = 80, 775 + .ppt_pl3_fppt_min = 35, 776 + .ppt_pl3_fppt_max = 80, 777 + .nv_temp_target_min = 75, 778 + .nv_temp_target_max = 87, 779 + }, 780 + .dc_data = &(struct power_limits) { 781 + .ppt_pl1_spl_min = 15, 782 + .ppt_pl1_spl_max = 35, 783 + .ppt_pl2_sppt_min = 25, 784 + .ppt_pl2_sppt_max = 35, 785 + .ppt_pl3_fppt_min = 35, 786 + .ppt_pl3_fppt_max = 65, 787 + .nv_temp_target_min = 75, 788 + .nv_temp_target_max = 87, 789 + }, 790 + .requires_fan_curve = true, 791 + }, 792 + }, 793 + { 794 + .matches = { 795 + DMI_MATCH(DMI_BOARD_NAME, "GA403U"), 796 + }, 797 + .driver_data = &(struct power_data) { 798 + .ac_data = &(struct power_limits) { 799 + .ppt_pl1_spl_min = 15, 800 + .ppt_pl1_spl_max = 80, 801 + .ppt_pl2_sppt_min = 25, 802 + .ppt_pl2_sppt_max = 80, 803 + .ppt_pl3_fppt_min = 35, 804 + .ppt_pl3_fppt_max = 80, 805 + .nv_dynamic_boost_min = 5, 806 + .nv_dynamic_boost_max = 25, 807 + .nv_temp_target_min = 75, 808 + .nv_temp_target_max = 87, 809 + .nv_tgp_min = 55, 810 + .nv_tgp_max = 65, 811 + }, 812 + .dc_data = &(struct power_limits) { 813 + .ppt_pl1_spl_min = 15, 814 + .ppt_pl1_spl_max = 35, 815 + .ppt_pl2_sppt_min = 25, 816 + .ppt_pl2_sppt_max = 35, 817 + .ppt_pl3_fppt_min = 35, 818 + .ppt_pl3_fppt_max = 65, 819 + .nv_temp_target_min = 75, 820 + .nv_temp_target_max = 87, 821 + }, 822 + .requires_fan_curve = true, 823 + }, 824 + }, 825 + { 826 + .matches = { 827 + DMI_MATCH(DMI_BOARD_NAME, "GA503QR"), 828 + }, 829 + .driver_data = &(struct power_data) { 830 + .ac_data = &(struct power_limits) { 831 + .ppt_pl1_spl_min = 15, 832 + .ppt_pl1_spl_def = 35, 833 + .ppt_pl1_spl_max = 80, 834 + .ppt_pl2_sppt_min = 65, 835 + .ppt_pl2_sppt_max = 80, 836 + }, 837 + }, 838 + }, 839 + { 840 + .matches = { 841 + DMI_MATCH(DMI_BOARD_NAME, "GA503R"), 842 + }, 843 + .driver_data = &(struct power_data) { 844 + .ac_data = &(struct power_limits) { 845 + .ppt_pl1_spl_min = 15, 846 + .ppt_pl1_spl_def = 35, 847 + .ppt_pl1_spl_max = 80, 848 + .ppt_pl2_sppt_min = 35, 849 + .ppt_pl2_sppt_def = 65, 850 + .ppt_pl2_sppt_max = 80, 851 + .ppt_pl3_fppt_min = 35, 852 + .ppt_pl3_fppt_max = 80, 853 + .nv_dynamic_boost_min = 5, 854 + .nv_dynamic_boost_max = 20, 855 + .nv_temp_target_min = 75, 856 + .nv_temp_target_max = 87, 857 + }, 858 + .dc_data = &(struct power_limits) { 859 + .ppt_pl1_spl_min = 15, 860 + .ppt_pl1_spl_def = 25, 861 + .ppt_pl1_spl_max = 65, 862 + .ppt_pl2_sppt_min = 35, 863 + .ppt_pl2_sppt_def = 54, 864 + .ppt_pl2_sppt_max = 60, 865 + .ppt_pl3_fppt_min = 35, 866 + .ppt_pl3_fppt_max = 65, 867 + }, 868 + }, 869 + }, 870 + { 871 + .matches = { 872 + DMI_MATCH(DMI_BOARD_NAME, "GA605W"), 873 + }, 874 + .driver_data = &(struct power_data) { 875 + .ac_data = &(struct power_limits) { 876 + .ppt_pl1_spl_min = 15, 877 + .ppt_pl1_spl_max = 80, 878 + .ppt_pl2_sppt_min = 35, 879 + .ppt_pl2_sppt_max = 80, 880 + .ppt_pl3_fppt_min = 35, 881 + .ppt_pl3_fppt_max = 80, 882 + .nv_dynamic_boost_min = 5, 883 + .nv_dynamic_boost_max = 20, 884 + .nv_temp_target_min = 75, 885 + .nv_temp_target_max = 87, 886 + .nv_tgp_min = 55, 887 + .nv_tgp_max = 85, 888 + }, 889 + .dc_data = &(struct power_limits) { 890 + .ppt_pl1_spl_min = 25, 891 + .ppt_pl1_spl_max = 35, 892 + .ppt_pl2_sppt_min = 31, 893 + .ppt_pl2_sppt_max = 44, 894 + .ppt_pl3_fppt_min = 45, 895 + .ppt_pl3_fppt_max = 65, 896 + .nv_temp_target_min = 75, 897 + .nv_temp_target_max = 87, 898 + }, 899 + .requires_fan_curve = true, 900 + }, 901 + }, 902 + { 903 + .matches = { 904 + DMI_MATCH(DMI_BOARD_NAME, "GU603Z"), 905 + }, 906 + .driver_data = &(struct power_data) { 907 + .ac_data = &(struct power_limits) { 908 + .ppt_pl1_spl_min = 25, 909 + .ppt_pl1_spl_max = 60, 910 + .ppt_pl2_sppt_min = 25, 911 + .ppt_pl2_sppt_max = 135, 912 + .nv_dynamic_boost_min = 5, 913 + .nv_dynamic_boost_max = 20, 914 + .nv_temp_target_min = 75, 915 + .nv_temp_target_max = 87, 916 + }, 917 + .dc_data = &(struct power_limits) { 918 + .ppt_pl1_spl_min = 25, 919 + .ppt_pl1_spl_max = 40, 920 + .ppt_pl2_sppt_min = 25, 921 + .ppt_pl2_sppt_max = 40, 922 + .nv_temp_target_min = 75, 923 + .nv_temp_target_max = 87, 924 + } 925 + }, 926 + }, 927 + { 928 + .matches = { 929 + DMI_MATCH(DMI_BOARD_NAME, "GU604V"), 930 + }, 931 + .driver_data = &(struct power_data) { 932 + .ac_data = &(struct power_limits) { 933 + .ppt_pl1_spl_min = 65, 934 + .ppt_pl1_spl_max = 120, 935 + .ppt_pl2_sppt_min = 65, 936 + .ppt_pl2_sppt_max = 150, 937 + .nv_dynamic_boost_min = 5, 938 + .nv_dynamic_boost_max = 25, 939 + .nv_temp_target_min = 75, 940 + .nv_temp_target_max = 87, 941 + }, 942 + .dc_data = &(struct power_limits) { 943 + .ppt_pl1_spl_min = 25, 944 + .ppt_pl1_spl_max = 40, 945 + .ppt_pl2_sppt_min = 35, 946 + .ppt_pl2_sppt_def = 40, 947 + .ppt_pl2_sppt_max = 60, 948 + .nv_temp_target_min = 75, 949 + .nv_temp_target_max = 87, 950 + }, 951 + }, 952 + }, 953 + { 954 + .matches = { 955 + DMI_MATCH(DMI_BOARD_NAME, "GU605CW"), 956 + }, 957 + .driver_data = &(struct power_data) { 958 + .ac_data = &(struct power_limits) { 959 + .ppt_pl1_spl_min = 45, 960 + .ppt_pl1_spl_max = 85, 961 + .ppt_pl2_sppt_min = 56, 962 + .ppt_pl2_sppt_max = 110, 963 + .nv_dynamic_boost_min = 5, 964 + .nv_dynamic_boost_max = 20, 965 + .nv_temp_target_min = 75, 966 + .nv_temp_target_max = 87, 967 + .nv_tgp_min = 80, 968 + .nv_tgp_def = 90, 969 + .nv_tgp_max = 110, 970 + }, 971 + .dc_data = &(struct power_limits) { 972 + .ppt_pl1_spl_min = 25, 973 + .ppt_pl1_spl_max = 85, 974 + .ppt_pl2_sppt_min = 32, 975 + .ppt_pl2_sppt_max = 110, 976 + .nv_temp_target_min = 75, 977 + .nv_temp_target_max = 87, 978 + }, 979 + .requires_fan_curve = true, 980 + }, 981 + }, 982 + { 983 + .matches = { 984 + DMI_MATCH(DMI_BOARD_NAME, "GU605CX"), 985 + }, 986 + .driver_data = &(struct power_data) { 987 + .ac_data = &(struct power_limits) { 988 + .ppt_pl1_spl_min = 45, 989 + .ppt_pl1_spl_max = 85, 990 + .ppt_pl2_sppt_min = 56, 991 + .ppt_pl2_sppt_max = 110, 992 + .nv_dynamic_boost_min = 5, 993 + .nv_dynamic_boost_max = 20, 994 + .nv_temp_target_min = 7, 995 + .nv_temp_target_max = 87, 996 + .nv_tgp_min = 95, 997 + .nv_tgp_def = 100, 998 + .nv_tgp_max = 110, 999 + }, 1000 + .dc_data = &(struct power_limits) { 1001 + .ppt_pl1_spl_min = 25, 1002 + .ppt_pl1_spl_max = 85, 1003 + .ppt_pl2_sppt_min = 32, 1004 + .ppt_pl2_sppt_max = 110, 1005 + .nv_temp_target_min = 75, 1006 + .nv_temp_target_max = 87, 1007 + }, 1008 + .requires_fan_curve = true, 1009 + }, 1010 + }, 1011 + { 1012 + .matches = { 1013 + DMI_MATCH(DMI_BOARD_NAME, "GU605M"), 1014 + }, 1015 + .driver_data = &(struct power_data) { 1016 + .ac_data = &(struct power_limits) { 1017 + .ppt_pl1_spl_min = 28, 1018 + .ppt_pl1_spl_max = 90, 1019 + .ppt_pl2_sppt_min = 28, 1020 + .ppt_pl2_sppt_max = 135, 1021 + .nv_dynamic_boost_min = 5, 1022 + .nv_dynamic_boost_max = 20, 1023 + .nv_temp_target_min = 75, 1024 + .nv_temp_target_max = 87, 1025 + }, 1026 + .dc_data = &(struct power_limits) { 1027 + .ppt_pl1_spl_min = 25, 1028 + .ppt_pl1_spl_max = 35, 1029 + .ppt_pl2_sppt_min = 38, 1030 + .ppt_pl2_sppt_max = 53, 1031 + .nv_temp_target_min = 75, 1032 + .nv_temp_target_max = 87, 1033 + }, 1034 + .requires_fan_curve = true, 1035 + }, 1036 + }, 1037 + { 1038 + .matches = { 1039 + DMI_MATCH(DMI_BOARD_NAME, "GV301Q"), 1040 + }, 1041 + .driver_data = &(struct power_data) { 1042 + .ac_data = &(struct power_limits) { 1043 + .ppt_pl1_spl_min = 15, 1044 + .ppt_pl1_spl_max = 45, 1045 + .ppt_pl2_sppt_min = 65, 1046 + .ppt_pl2_sppt_max = 80, 1047 + }, 1048 + .dc_data = NULL, 1049 + }, 1050 + }, 1051 + { 1052 + .matches = { 1053 + DMI_MATCH(DMI_BOARD_NAME, "GV301R"), 1054 + }, 1055 + .driver_data = &(struct power_data) { 1056 + .ac_data = &(struct power_limits) { 1057 + .ppt_pl1_spl_min = 15, 1058 + .ppt_pl1_spl_max = 45, 1059 + .ppt_pl2_sppt_min = 25, 1060 + .ppt_pl2_sppt_max = 54, 1061 + .ppt_pl3_fppt_min = 35, 1062 + .ppt_pl3_fppt_max = 65, 1063 + .nv_temp_target_min = 75, 1064 + .nv_temp_target_max = 87, 1065 + }, 1066 + .dc_data = &(struct power_limits) { 1067 + .ppt_pl1_spl_min = 15, 1068 + .ppt_pl1_spl_max = 35, 1069 + .ppt_pl2_sppt_min = 25, 1070 + .ppt_pl2_sppt_max = 35, 1071 + .ppt_pl3_fppt_min = 35, 1072 + .ppt_pl3_fppt_max = 65, 1073 + .nv_temp_target_min = 75, 1074 + .nv_temp_target_max = 87, 1075 + }, 1076 + }, 1077 + }, 1078 + { 1079 + .matches = { 1080 + DMI_MATCH(DMI_BOARD_NAME, "GV601R"), 1081 + }, 1082 + .driver_data = &(struct power_data) { 1083 + .ac_data = &(struct power_limits) { 1084 + .ppt_pl1_spl_min = 15, 1085 + .ppt_pl1_spl_def = 35, 1086 + .ppt_pl1_spl_max = 90, 1087 + .ppt_pl2_sppt_min = 35, 1088 + .ppt_pl2_sppt_def = 54, 1089 + .ppt_pl2_sppt_max = 100, 1090 + .ppt_pl3_fppt_min = 35, 1091 + .ppt_pl3_fppt_def = 80, 1092 + .ppt_pl3_fppt_max = 125, 1093 + .nv_dynamic_boost_min = 5, 1094 + .nv_dynamic_boost_max = 25, 1095 + .nv_temp_target_min = 75, 1096 + .nv_temp_target_max = 87, 1097 + }, 1098 + .dc_data = &(struct power_limits) { 1099 + .ppt_pl1_spl_min = 15, 1100 + .ppt_pl1_spl_def = 28, 1101 + .ppt_pl1_spl_max = 65, 1102 + .ppt_pl2_sppt_min = 35, 1103 + .ppt_pl2_sppt_def = 54, 1104 + .ppt_pl2_sppt_max = 60, 1105 + .ppt_pl3_fppt_min = 35, 1106 + .ppt_pl3_fppt_def = 80, 1107 + .ppt_pl3_fppt_max = 65, 1108 + .nv_temp_target_min = 75, 1109 + .nv_temp_target_max = 87, 1110 + }, 1111 + }, 1112 + }, 1113 + { 1114 + .matches = { 1115 + DMI_MATCH(DMI_BOARD_NAME, "GV601V"), 1116 + }, 1117 + .driver_data = &(struct power_data) { 1118 + .ac_data = &(struct power_limits) { 1119 + .ppt_pl1_spl_min = 28, 1120 + .ppt_pl1_spl_def = 100, 1121 + .ppt_pl1_spl_max = 110, 1122 + .ppt_pl2_sppt_min = 28, 1123 + .ppt_pl2_sppt_max = 135, 1124 + .nv_dynamic_boost_min = 5, 1125 + .nv_dynamic_boost_max = 20, 1126 + .nv_temp_target_min = 75, 1127 + .nv_temp_target_max = 87, 1128 + }, 1129 + .dc_data = &(struct power_limits) { 1130 + .ppt_pl1_spl_min = 25, 1131 + .ppt_pl1_spl_max = 40, 1132 + .ppt_pl2_sppt_min = 35, 1133 + .ppt_pl2_sppt_def = 40, 1134 + .ppt_pl2_sppt_max = 60, 1135 + .nv_temp_target_min = 75, 1136 + .nv_temp_target_max = 87, 1137 + }, 1138 + }, 1139 + }, 1140 + { 1141 + .matches = { 1142 + DMI_MATCH(DMI_BOARD_NAME, "GX650P"), 1143 + }, 1144 + .driver_data = &(struct power_data) { 1145 + .ac_data = &(struct power_limits) { 1146 + .ppt_pl1_spl_min = 15, 1147 + .ppt_pl1_spl_def = 110, 1148 + .ppt_pl1_spl_max = 130, 1149 + .ppt_pl2_sppt_min = 35, 1150 + .ppt_pl2_sppt_def = 125, 1151 + .ppt_pl2_sppt_max = 130, 1152 + .ppt_pl3_fppt_min = 35, 1153 + .ppt_pl3_fppt_def = 125, 1154 + .ppt_pl3_fppt_max = 135, 1155 + .nv_dynamic_boost_min = 5, 1156 + .nv_dynamic_boost_max = 25, 1157 + .nv_temp_target_min = 75, 1158 + .nv_temp_target_max = 87, 1159 + }, 1160 + .dc_data = &(struct power_limits) { 1161 + .ppt_pl1_spl_min = 15, 1162 + .ppt_pl1_spl_def = 25, 1163 + .ppt_pl1_spl_max = 65, 1164 + .ppt_pl2_sppt_min = 35, 1165 + .ppt_pl2_sppt_def = 35, 1166 + .ppt_pl2_sppt_max = 65, 1167 + .ppt_pl3_fppt_min = 35, 1168 + .ppt_pl3_fppt_def = 42, 1169 + .ppt_pl3_fppt_max = 65, 1170 + .nv_temp_target_min = 75, 1171 + .nv_temp_target_max = 87, 1172 + }, 1173 + }, 1174 + }, 1175 + { 1176 + .matches = { 1177 + DMI_MATCH(DMI_BOARD_NAME, "G513I"), 1178 + }, 1179 + .driver_data = &(struct power_data) { 1180 + .ac_data = &(struct power_limits) { 1181 + /* Yes this laptop is very limited */ 1182 + .ppt_pl1_spl_min = 15, 1183 + .ppt_pl1_spl_max = 80, 1184 + .ppt_pl2_sppt_min = 15, 1185 + .ppt_pl2_sppt_max = 80, 1186 + }, 1187 + .dc_data = NULL, 1188 + .requires_fan_curve = true, 1189 + }, 1190 + }, 1191 + { 1192 + .matches = { 1193 + DMI_MATCH(DMI_BOARD_NAME, "G513QM"), 1194 + }, 1195 + .driver_data = &(struct power_data) { 1196 + .ac_data = &(struct power_limits) { 1197 + /* Yes this laptop is very limited */ 1198 + .ppt_pl1_spl_min = 15, 1199 + .ppt_pl1_spl_max = 100, 1200 + .ppt_pl2_sppt_min = 15, 1201 + .ppt_pl2_sppt_max = 190, 1202 + }, 1203 + .dc_data = NULL, 1204 + .requires_fan_curve = true, 1205 + }, 1206 + }, 1207 + { 1208 + .matches = { 1209 + DMI_MATCH(DMI_BOARD_NAME, "G513R"), 1210 + }, 1211 + .driver_data = &(struct power_data) { 1212 + .ac_data = &(struct power_limits) { 1213 + .ppt_pl1_spl_min = 35, 1214 + .ppt_pl1_spl_max = 90, 1215 + .ppt_pl2_sppt_min = 54, 1216 + .ppt_pl2_sppt_max = 100, 1217 + .ppt_pl3_fppt_min = 54, 1218 + .ppt_pl3_fppt_max = 125, 1219 + .nv_dynamic_boost_min = 5, 1220 + .nv_dynamic_boost_max = 25, 1221 + .nv_temp_target_min = 75, 1222 + .nv_temp_target_max = 87, 1223 + }, 1224 + .dc_data = &(struct power_limits) { 1225 + .ppt_pl1_spl_min = 28, 1226 + .ppt_pl1_spl_max = 50, 1227 + .ppt_pl2_sppt_min = 28, 1228 + .ppt_pl2_sppt_max = 50, 1229 + .ppt_pl3_fppt_min = 28, 1230 + .ppt_pl3_fppt_max = 65, 1231 + .nv_temp_target_min = 75, 1232 + .nv_temp_target_max = 87, 1233 + }, 1234 + .requires_fan_curve = true, 1235 + }, 1236 + }, 1237 + { 1238 + .matches = { 1239 + DMI_MATCH(DMI_BOARD_NAME, "G614J"), 1240 + }, 1241 + .driver_data = &(struct power_data) { 1242 + .ac_data = &(struct power_limits) { 1243 + .ppt_pl1_spl_min = 28, 1244 + .ppt_pl1_spl_max = 140, 1245 + .ppt_pl2_sppt_min = 28, 1246 + .ppt_pl2_sppt_max = 175, 1247 + .nv_temp_target_min = 75, 1248 + .nv_temp_target_max = 87, 1249 + .nv_dynamic_boost_min = 5, 1250 + .nv_dynamic_boost_max = 25, 1251 + }, 1252 + .dc_data = &(struct power_limits) { 1253 + .ppt_pl1_spl_min = 25, 1254 + .ppt_pl1_spl_max = 55, 1255 + .ppt_pl2_sppt_min = 25, 1256 + .ppt_pl2_sppt_max = 70, 1257 + .nv_temp_target_min = 75, 1258 + .nv_temp_target_max = 87, 1259 + }, 1260 + .requires_fan_curve = true, 1261 + }, 1262 + }, 1263 + { 1264 + .matches = { 1265 + DMI_MATCH(DMI_BOARD_NAME, "G634J"), 1266 + }, 1267 + .driver_data = &(struct power_data) { 1268 + .ac_data = &(struct power_limits) { 1269 + .ppt_pl1_spl_min = 28, 1270 + .ppt_pl1_spl_max = 140, 1271 + .ppt_pl2_sppt_min = 28, 1272 + .ppt_pl2_sppt_max = 175, 1273 + .nv_temp_target_min = 75, 1274 + .nv_temp_target_max = 87, 1275 + .nv_dynamic_boost_min = 5, 1276 + .nv_dynamic_boost_max = 25, 1277 + }, 1278 + .dc_data = &(struct power_limits) { 1279 + .ppt_pl1_spl_min = 25, 1280 + .ppt_pl1_spl_max = 55, 1281 + .ppt_pl2_sppt_min = 25, 1282 + .ppt_pl2_sppt_max = 70, 1283 + .nv_temp_target_min = 75, 1284 + .nv_temp_target_max = 87, 1285 + }, 1286 + .requires_fan_curve = true, 1287 + }, 1288 + }, 1289 + { 1290 + .matches = { 1291 + DMI_MATCH(DMI_BOARD_NAME, "G713PV"), 1292 + }, 1293 + .driver_data = &(struct power_data) { 1294 + .ac_data = &(struct power_limits) { 1295 + .ppt_pl1_spl_min = 30, 1296 + .ppt_pl1_spl_def = 120, 1297 + .ppt_pl1_spl_max = 130, 1298 + .ppt_pl2_sppt_min = 65, 1299 + .ppt_pl2_sppt_def = 125, 1300 + .ppt_pl2_sppt_max = 130, 1301 + .ppt_pl3_fppt_min = 65, 1302 + .ppt_pl3_fppt_def = 125, 1303 + .ppt_pl3_fppt_max = 130, 1304 + .nv_temp_target_min = 75, 1305 + .nv_temp_target_max = 87, 1306 + .nv_dynamic_boost_min = 5, 1307 + .nv_dynamic_boost_max = 25, 1308 + }, 1309 + .dc_data = &(struct power_limits) { 1310 + .ppt_pl1_spl_min = 25, 1311 + .ppt_pl1_spl_max = 65, 1312 + .ppt_pl2_sppt_min = 25, 1313 + .ppt_pl2_sppt_max = 65, 1314 + .ppt_pl3_fppt_min = 35, 1315 + .ppt_pl3_fppt_max = 75, 1316 + .nv_temp_target_min = 75, 1317 + .nv_temp_target_max = 87, 1318 + }, 1319 + .requires_fan_curve = true, 1320 + }, 1321 + }, 1322 + { 1323 + .matches = { 1324 + DMI_MATCH(DMI_BOARD_NAME, "G733C"), 1325 + }, 1326 + .driver_data = &(struct power_data) { 1327 + .ac_data = &(struct power_limits) { 1328 + .ppt_pl1_spl_min = 28, 1329 + .ppt_pl1_spl_max = 170, 1330 + .ppt_pl2_sppt_min = 28, 1331 + .ppt_pl2_sppt_max = 175, 1332 + .nv_temp_target_min = 75, 1333 + .nv_temp_target_max = 87, 1334 + .nv_dynamic_boost_min = 5, 1335 + .nv_dynamic_boost_max = 25, 1336 + }, 1337 + .dc_data = &(struct power_limits) { 1338 + .ppt_pl1_spl_min = 28, 1339 + .ppt_pl1_spl_max = 35, 1340 + .ppt_pl2_sppt_min = 28, 1341 + .ppt_pl2_sppt_max = 35, 1342 + .nv_temp_target_min = 75, 1343 + .nv_temp_target_max = 87, 1344 + }, 1345 + .requires_fan_curve = true, 1346 + }, 1347 + }, 1348 + { 1349 + .matches = { 1350 + DMI_MATCH(DMI_BOARD_NAME, "G733P"), 1351 + }, 1352 + .driver_data = &(struct power_data) { 1353 + .ac_data = &(struct power_limits) { 1354 + .ppt_pl1_spl_min = 30, 1355 + .ppt_pl1_spl_def = 100, 1356 + .ppt_pl1_spl_max = 130, 1357 + .ppt_pl2_sppt_min = 65, 1358 + .ppt_pl2_sppt_def = 125, 1359 + .ppt_pl2_sppt_max = 130, 1360 + .ppt_pl3_fppt_min = 65, 1361 + .ppt_pl3_fppt_def = 125, 1362 + .ppt_pl3_fppt_max = 130, 1363 + .nv_temp_target_min = 75, 1364 + .nv_temp_target_max = 87, 1365 + .nv_dynamic_boost_min = 5, 1366 + .nv_dynamic_boost_max = 25, 1367 + }, 1368 + .dc_data = &(struct power_limits) { 1369 + .ppt_pl1_spl_min = 25, 1370 + .ppt_pl1_spl_max = 65, 1371 + .ppt_pl2_sppt_min = 25, 1372 + .ppt_pl2_sppt_max = 65, 1373 + .ppt_pl3_fppt_min = 35, 1374 + .ppt_pl3_fppt_max = 75, 1375 + .nv_temp_target_min = 75, 1376 + .nv_temp_target_max = 87, 1377 + }, 1378 + .requires_fan_curve = true, 1379 + }, 1380 + }, 1381 + { 1382 + .matches = { 1383 + DMI_MATCH(DMI_BOARD_NAME, "G814J"), 1384 + }, 1385 + .driver_data = &(struct power_data) { 1386 + .ac_data = &(struct power_limits) { 1387 + .ppt_pl1_spl_min = 28, 1388 + .ppt_pl1_spl_max = 140, 1389 + .ppt_pl2_sppt_min = 28, 1390 + .ppt_pl2_sppt_max = 140, 1391 + .nv_dynamic_boost_min = 5, 1392 + .nv_dynamic_boost_max = 25, 1393 + }, 1394 + .dc_data = &(struct power_limits) { 1395 + .ppt_pl1_spl_min = 25, 1396 + .ppt_pl1_spl_max = 55, 1397 + .ppt_pl2_sppt_min = 25, 1398 + .ppt_pl2_sppt_max = 70, 1399 + }, 1400 + .requires_fan_curve = true, 1401 + }, 1402 + }, 1403 + { 1404 + .matches = { 1405 + DMI_MATCH(DMI_BOARD_NAME, "G834J"), 1406 + }, 1407 + .driver_data = &(struct power_data) { 1408 + .ac_data = &(struct power_limits) { 1409 + .ppt_pl1_spl_min = 28, 1410 + .ppt_pl1_spl_max = 140, 1411 + .ppt_pl2_sppt_min = 28, 1412 + .ppt_pl2_sppt_max = 175, 1413 + .nv_dynamic_boost_min = 5, 1414 + .nv_dynamic_boost_max = 25, 1415 + .nv_temp_target_min = 75, 1416 + .nv_temp_target_max = 87, 1417 + }, 1418 + .dc_data = &(struct power_limits) { 1419 + .ppt_pl1_spl_min = 25, 1420 + .ppt_pl1_spl_max = 55, 1421 + .ppt_pl2_sppt_min = 25, 1422 + .ppt_pl2_sppt_max = 70, 1423 + .nv_temp_target_min = 75, 1424 + .nv_temp_target_max = 87, 1425 + }, 1426 + .requires_fan_curve = true, 1427 + }, 1428 + }, 1429 + { 1430 + .matches = { 1431 + DMI_MATCH(DMI_BOARD_NAME, "H7606W"), 1432 + }, 1433 + .driver_data = &(struct power_data) { 1434 + .ac_data = &(struct power_limits) { 1435 + .ppt_pl1_spl_min = 15, 1436 + .ppt_pl1_spl_max = 80, 1437 + .ppt_pl2_sppt_min = 35, 1438 + .ppt_pl2_sppt_max = 80, 1439 + .ppt_pl3_fppt_min = 35, 1440 + .ppt_pl3_fppt_max = 80, 1441 + .nv_dynamic_boost_min = 5, 1442 + .nv_dynamic_boost_max = 20, 1443 + .nv_temp_target_min = 75, 1444 + .nv_temp_target_max = 87, 1445 + .nv_tgp_min = 55, 1446 + .nv_tgp_max = 85, 1447 + }, 1448 + .dc_data = &(struct power_limits) { 1449 + .ppt_pl1_spl_min = 25, 1450 + .ppt_pl1_spl_max = 35, 1451 + .ppt_pl2_sppt_min = 31, 1452 + .ppt_pl2_sppt_max = 44, 1453 + .ppt_pl3_fppt_min = 45, 1454 + .ppt_pl3_fppt_max = 65, 1455 + .nv_temp_target_min = 75, 1456 + .nv_temp_target_max = 87, 1457 + }, 1458 + }, 1459 + }, 1460 + { 1461 + .matches = { 1462 + DMI_MATCH(DMI_BOARD_NAME, "RC71"), 1463 + }, 1464 + .driver_data = &(struct power_data) { 1465 + .ac_data = &(struct power_limits) { 1466 + .ppt_pl1_spl_min = 7, 1467 + .ppt_pl1_spl_max = 30, 1468 + .ppt_pl2_sppt_min = 15, 1469 + .ppt_pl2_sppt_max = 43, 1470 + .ppt_pl3_fppt_min = 15, 1471 + .ppt_pl3_fppt_max = 53, 1472 + }, 1473 + .dc_data = &(struct power_limits) { 1474 + .ppt_pl1_spl_min = 7, 1475 + .ppt_pl1_spl_def = 15, 1476 + .ppt_pl1_spl_max = 25, 1477 + .ppt_pl2_sppt_min = 15, 1478 + .ppt_pl2_sppt_def = 20, 1479 + .ppt_pl2_sppt_max = 30, 1480 + .ppt_pl3_fppt_min = 15, 1481 + .ppt_pl3_fppt_def = 25, 1482 + .ppt_pl3_fppt_max = 35, 1483 + }, 1484 + }, 1485 + }, 1486 + { 1487 + .matches = { 1488 + DMI_MATCH(DMI_BOARD_NAME, "RC72"), 1489 + }, 1490 + .driver_data = &(struct power_data) { 1491 + .ac_data = &(struct power_limits) { 1492 + .ppt_pl1_spl_min = 7, 1493 + .ppt_pl1_spl_max = 30, 1494 + .ppt_pl2_sppt_min = 15, 1495 + .ppt_pl2_sppt_max = 43, 1496 + .ppt_pl3_fppt_min = 15, 1497 + .ppt_pl3_fppt_max = 53, 1498 + }, 1499 + .dc_data = &(struct power_limits) { 1500 + .ppt_pl1_spl_min = 7, 1501 + .ppt_pl1_spl_def = 17, 1502 + .ppt_pl1_spl_max = 25, 1503 + .ppt_pl2_sppt_min = 15, 1504 + .ppt_pl2_sppt_def = 24, 1505 + .ppt_pl2_sppt_max = 30, 1506 + .ppt_pl3_fppt_min = 15, 1507 + .ppt_pl3_fppt_def = 30, 1508 + .ppt_pl3_fppt_max = 35, 1509 + }, 1510 + }, 1511 + }, 1512 + { 1513 + .matches = { 1514 + DMI_MATCH(DMI_BOARD_NAME, "RC73XA"), 1515 + }, 1516 + .driver_data = &(struct power_data) { 1517 + .ac_data = &(struct power_limits) { 1518 + .ppt_pl1_spl_min = 7, 1519 + .ppt_pl1_spl_max = 35, 1520 + .ppt_pl2_sppt_min = 14, 1521 + .ppt_pl2_sppt_max = 45, 1522 + .ppt_pl3_fppt_min = 19, 1523 + .ppt_pl3_fppt_max = 55, 1524 + }, 1525 + .dc_data = &(struct power_limits) { 1526 + .ppt_pl1_spl_min = 7, 1527 + .ppt_pl1_spl_def = 17, 1528 + .ppt_pl1_spl_max = 35, 1529 + .ppt_pl2_sppt_min = 13, 1530 + .ppt_pl2_sppt_def = 21, 1531 + .ppt_pl2_sppt_max = 45, 1532 + .ppt_pl3_fppt_min = 19, 1533 + .ppt_pl3_fppt_def = 26, 1534 + .ppt_pl3_fppt_max = 55, 1535 + }, 1536 + }, 1537 + }, 1538 + {} 1539 + }; 1540 + 1541 + #endif /* _ASUS_ARMOURY_H_ */
+147 -38
drivers/platform/x86/asus-wmi.c
··· 15 15 16 16 #include <linux/acpi.h> 17 17 #include <linux/backlight.h> 18 + #include <linux/bits.h> 18 19 #include <linux/debugfs.h> 19 20 #include <linux/delay.h> 20 21 #include <linux/dmi.h> ··· 31 30 #include <linux/pci.h> 32 31 #include <linux/pci_hotplug.h> 33 32 #include <linux/platform_data/x86/asus-wmi.h> 33 + #include <linux/platform_data/x86/asus-wmi-leds-ids.h> 34 34 #include <linux/platform_device.h> 35 35 #include <linux/platform_profile.h> 36 36 #include <linux/power_supply.h> ··· 56 54 57 55 #define to_asus_wmi_driver(pdrv) \ 58 56 (container_of((pdrv), struct asus_wmi_driver, platform_driver)) 59 - 60 - #define ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" 61 57 62 58 #define NOTIFY_BRNUP_MIN 0x11 63 59 #define NOTIFY_BRNUP_MAX 0x1f ··· 104 104 105 105 #define USB_INTEL_XUSB2PR 0xD0 106 106 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 107 - 108 - #define ASUS_ACPI_UID_ASUSWMI "ASUSWMI" 109 107 110 108 #define WMI_EVENT_MASK 0xFFFF 111 109 ··· 338 340 /* Global to allow setting externally without requiring driver data */ 339 341 static enum asus_ally_mcu_hack use_ally_mcu_hack = ASUS_WMI_ALLY_MCU_HACK_INIT; 340 342 343 + #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) 344 + static void asus_wmi_show_deprecated(void) 345 + { 346 + 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"); 347 + } 348 + #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ 349 + 341 350 /* WMI ************************************************************************/ 342 351 343 352 static int asus_wmi_evaluate_method3(u32 method_id, ··· 395 390 { 396 391 return asus_wmi_evaluate_method3(method_id, arg0, arg1, 0, retval); 397 392 } 398 - EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method); 393 + EXPORT_SYMBOL_NS_GPL(asus_wmi_evaluate_method, "ASUS_WMI"); 399 394 400 395 static int asus_wmi_evaluate_method5(u32 method_id, 401 396 u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 *retval) ··· 559 554 return 0; 560 555 } 561 556 562 - int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, 563 - u32 *retval) 557 + /** 558 + * asus_wmi_get_devstate_dsts() - Get the WMI function state. 559 + * @dev_id: The WMI method ID to call. 560 + * @retval: A pointer to where to store the value returned from WMI. 561 + * 562 + * Returns: 563 + * * %-ENODEV - method ID is unsupported. 564 + * * %0 - successful and retval is filled. 565 + * * %other - error from WMI call. 566 + */ 567 + int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval) 568 + { 569 + int err; 570 + 571 + err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, dev_id, 0, retval); 572 + if (err) 573 + return err; 574 + 575 + if ((*retval & ASUS_WMI_DSTS_PRESENCE_BIT) == 0x00) 576 + return -ENODEV; 577 + 578 + return 0; 579 + } 580 + EXPORT_SYMBOL_NS_GPL(asus_wmi_get_devstate_dsts, "ASUS_WMI"); 581 + 582 + /** 583 + * asus_wmi_set_devstate() - Set the WMI function state. 584 + * 585 + * Note: an asus_wmi_set_devstate() call must be paired with a 586 + * asus_wmi_get_devstate_dsts() to check if the WMI function is supported. 587 + * 588 + * @dev_id: The WMI function to call. 589 + * @ctrl_param: The argument to be used for this WMI function. 590 + * @retval: A pointer to where to store the value returned from WMI. 591 + * 592 + * Returns: 593 + * * %-ENODEV - method ID is unsupported. 594 + * * %0 - successful and retval is filled. 595 + * * %other - error from WMI call. 596 + */ 597 + int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval) 564 598 { 565 599 return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id, 566 600 ctrl_param, retval); 567 601 } 602 + EXPORT_SYMBOL_NS_GPL(asus_wmi_set_devstate, "ASUS_WMI"); 568 603 569 604 /* Helper for special devices with magic return codes */ 570 605 static int asus_wmi_get_devstate_bits(struct asus_wmi *asus, ··· 737 692 } 738 693 739 694 /* Charging mode, 1=Barrel, 2=USB ******************************************/ 695 + #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) 740 696 static ssize_t charge_mode_show(struct device *dev, 741 697 struct device_attribute *attr, char *buf) 742 698 { ··· 748 702 if (result < 0) 749 703 return result; 750 704 705 + asus_wmi_show_deprecated(); 706 + 751 707 return sysfs_emit(buf, "%d\n", value & 0xff); 752 708 } 753 709 754 710 static DEVICE_ATTR_RO(charge_mode); 711 + #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ 755 712 756 713 /* dGPU ********************************************************************/ 714 + #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) 757 715 static ssize_t dgpu_disable_show(struct device *dev, 758 716 struct device_attribute *attr, char *buf) 759 717 { ··· 767 717 result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_DGPU); 768 718 if (result < 0) 769 719 return result; 720 + 721 + asus_wmi_show_deprecated(); 770 722 771 723 return sysfs_emit(buf, "%d\n", result); 772 724 } ··· 823 771 return count; 824 772 } 825 773 static DEVICE_ATTR_RW(dgpu_disable); 774 + #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ 826 775 827 776 /* eGPU ********************************************************************/ 777 + #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) 828 778 static ssize_t egpu_enable_show(struct device *dev, 829 779 struct device_attribute *attr, char *buf) 830 780 { ··· 836 782 result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_EGPU); 837 783 if (result < 0) 838 784 return result; 785 + 786 + asus_wmi_show_deprecated(); 839 787 840 788 return sysfs_emit(buf, "%d\n", result); 841 789 } ··· 895 839 return count; 896 840 } 897 841 static DEVICE_ATTR_RW(egpu_enable); 842 + #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ 898 843 899 844 /* Is eGPU connected? *********************************************************/ 845 + #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) 900 846 static ssize_t egpu_connected_show(struct device *dev, 901 847 struct device_attribute *attr, char *buf) 902 848 { ··· 909 851 if (result < 0) 910 852 return result; 911 853 854 + asus_wmi_show_deprecated(); 855 + 912 856 return sysfs_emit(buf, "%d\n", result); 913 857 } 914 858 915 859 static DEVICE_ATTR_RO(egpu_connected); 860 + #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ 916 861 917 862 /* gpu mux switch *************************************************************/ 863 + #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) 918 864 static ssize_t gpu_mux_mode_show(struct device *dev, 919 865 struct device_attribute *attr, char *buf) 920 866 { ··· 928 866 result = asus_wmi_get_devstate_simple(asus, asus->gpu_mux_dev); 929 867 if (result < 0) 930 868 return result; 869 + 870 + asus_wmi_show_deprecated(); 931 871 932 872 return sysfs_emit(buf, "%d\n", result); 933 873 } ··· 989 925 return count; 990 926 } 991 927 static DEVICE_ATTR_RW(gpu_mux_mode); 928 + #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ 992 929 993 930 /* TUF Laptop Keyboard RGB Modes **********************************************/ 994 931 static ssize_t kbd_rgb_mode_store(struct device *dev, ··· 1113 1048 }; 1114 1049 1115 1050 /* Tunable: PPT: Intel=PL1, AMD=SPPT *****************************************/ 1051 + #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) 1116 1052 static ssize_t ppt_pl2_sppt_store(struct device *dev, 1117 1053 struct device_attribute *attr, 1118 1054 const char *buf, size_t count) ··· 1151 1085 char *buf) 1152 1086 { 1153 1087 struct asus_wmi *asus = dev_get_drvdata(dev); 1088 + 1089 + asus_wmi_show_deprecated(); 1154 1090 1155 1091 return sysfs_emit(buf, "%u\n", asus->ppt_pl2_sppt); 1156 1092 } ··· 1196 1128 { 1197 1129 struct asus_wmi *asus = dev_get_drvdata(dev); 1198 1130 1131 + asus_wmi_show_deprecated(); 1132 + 1199 1133 return sysfs_emit(buf, "%u\n", asus->ppt_pl1_spl); 1200 1134 } 1201 1135 static DEVICE_ATTR_RW(ppt_pl1_spl); ··· 1218 1148 if (value < PPT_TOTAL_MIN || value > PPT_TOTAL_MAX) 1219 1149 return -EINVAL; 1220 1150 1221 - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_FPPT, value, &result); 1151 + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_PL3_FPPT, value, &result); 1222 1152 if (err) { 1223 1153 pr_warn("Failed to set ppt_fppt: %d\n", err); 1224 1154 return err; ··· 1240 1170 char *buf) 1241 1171 { 1242 1172 struct asus_wmi *asus = dev_get_drvdata(dev); 1173 + 1174 + asus_wmi_show_deprecated(); 1243 1175 1244 1176 return sysfs_emit(buf, "%u\n", asus->ppt_fppt); 1245 1177 } ··· 1286 1214 { 1287 1215 struct asus_wmi *asus = dev_get_drvdata(dev); 1288 1216 1217 + asus_wmi_show_deprecated(); 1218 + 1289 1219 return sysfs_emit(buf, "%u\n", asus->ppt_apu_sppt); 1290 1220 } 1291 1221 static DEVICE_ATTR_RW(ppt_apu_sppt); ··· 1330 1256 char *buf) 1331 1257 { 1332 1258 struct asus_wmi *asus = dev_get_drvdata(dev); 1259 + 1260 + asus_wmi_show_deprecated(); 1333 1261 1334 1262 return sysfs_emit(buf, "%u\n", asus->ppt_platform_sppt); 1335 1263 } ··· 1376 1300 { 1377 1301 struct asus_wmi *asus = dev_get_drvdata(dev); 1378 1302 1303 + asus_wmi_show_deprecated(); 1304 + 1379 1305 return sysfs_emit(buf, "%u\n", asus->nv_dynamic_boost); 1380 1306 } 1381 1307 static DEVICE_ATTR_RW(nv_dynamic_boost); ··· 1421 1343 { 1422 1344 struct asus_wmi *asus = dev_get_drvdata(dev); 1423 1345 1346 + asus_wmi_show_deprecated(); 1347 + 1424 1348 return sysfs_emit(buf, "%u\n", asus->nv_temp_target); 1425 1349 } 1426 1350 static DEVICE_ATTR_RW(nv_temp_target); 1351 + #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ 1427 1352 1428 1353 /* Ally MCU Powersave ********************************************************/ 1429 1354 ··· 1467 1386 } 1468 1387 EXPORT_SYMBOL_NS_GPL(set_ally_mcu_powersave, "ASUS_WMI"); 1469 1388 1389 + #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) 1470 1390 static ssize_t mcu_powersave_show(struct device *dev, 1471 1391 struct device_attribute *attr, char *buf) 1472 1392 { ··· 1477 1395 result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_MCU_POWERSAVE); 1478 1396 if (result < 0) 1479 1397 return result; 1398 + 1399 + asus_wmi_show_deprecated(); 1480 1400 1481 1401 return sysfs_emit(buf, "%d\n", result); 1482 1402 } ··· 1515 1431 return count; 1516 1432 } 1517 1433 static DEVICE_ATTR_RW(mcu_powersave); 1434 + #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ 1518 1435 1519 1436 /* Battery ********************************************************************/ 1520 1437 ··· 1704 1619 kbd_led_update(asus); 1705 1620 } 1706 1621 1707 - static void kbd_led_set(struct led_classdev *led_cdev, 1708 - enum led_brightness value) 1622 + static int kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value) 1709 1623 { 1710 1624 /* Prevent disabling keyboard backlight on module unregister */ 1711 1625 if (led_cdev->flags & LED_UNREGISTERING) 1712 - return; 1626 + return 0; 1713 1627 1714 1628 do_kbd_led_set(led_cdev, value); 1629 + return 0; 1715 1630 } 1716 1631 1717 1632 static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value) ··· 1887 1802 asus->kbd_led_wk = led_val; 1888 1803 asus->kbd_led.name = "asus::kbd_backlight"; 1889 1804 asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; 1890 - asus->kbd_led.brightness_set = kbd_led_set; 1805 + asus->kbd_led.brightness_set_blocking = kbd_led_set; 1891 1806 asus->kbd_led.brightness_get = kbd_led_get; 1892 1807 asus->kbd_led.max_brightness = 3; 1893 1808 ··· 2389 2304 } 2390 2305 2391 2306 /* Panel Overdrive ************************************************************/ 2307 + #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) 2392 2308 static ssize_t panel_od_show(struct device *dev, 2393 2309 struct device_attribute *attr, char *buf) 2394 2310 { ··· 2399 2313 result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_PANEL_OD); 2400 2314 if (result < 0) 2401 2315 return result; 2316 + 2317 + asus_wmi_show_deprecated(); 2402 2318 2403 2319 return sysfs_emit(buf, "%d\n", result); 2404 2320 } ··· 2438 2350 return count; 2439 2351 } 2440 2352 static DEVICE_ATTR_RW(panel_od); 2353 + #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ 2441 2354 2442 2355 /* Bootup sound ***************************************************************/ 2443 - 2356 + #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) 2444 2357 static ssize_t boot_sound_show(struct device *dev, 2445 2358 struct device_attribute *attr, char *buf) 2446 2359 { ··· 2451 2362 result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_BOOT_SOUND); 2452 2363 if (result < 0) 2453 2364 return result; 2365 + 2366 + asus_wmi_show_deprecated(); 2454 2367 2455 2368 return sysfs_emit(buf, "%d\n", result); 2456 2369 } ··· 2489 2398 return count; 2490 2399 } 2491 2400 static DEVICE_ATTR_RW(boot_sound); 2401 + #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ 2492 2402 2493 2403 /* Mini-LED mode **************************************************************/ 2404 + #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) 2494 2405 static ssize_t mini_led_mode_show(struct device *dev, 2495 2406 struct device_attribute *attr, char *buf) 2496 2407 { ··· 2522 2429 break; 2523 2430 } 2524 2431 } 2432 + 2433 + asus_wmi_show_deprecated(); 2525 2434 2526 2435 return sysfs_emit(buf, "%d\n", value); 2527 2436 } ··· 2595 2500 return sysfs_emit(buf, "0 1 2\n"); 2596 2501 } 2597 2502 2503 + asus_wmi_show_deprecated(); 2504 + 2598 2505 return sysfs_emit(buf, "0\n"); 2599 2506 } 2600 2507 2601 2508 static DEVICE_ATTR_RO(available_mini_led_mode); 2509 + #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ 2602 2510 2603 2511 /* Quirks *********************************************************************/ 2604 2512 ··· 3889 3791 return throttle_thermal_policy_write(asus); 3890 3792 } 3891 3793 3794 + #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) 3892 3795 static ssize_t throttle_thermal_policy_show(struct device *dev, 3893 3796 struct device_attribute *attr, char *buf) 3894 3797 { ··· 3933 3834 * Throttle thermal policy: 0 - default, 1 - overboost, 2 - silent 3934 3835 */ 3935 3836 static DEVICE_ATTR_RW(throttle_thermal_policy); 3837 + #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ 3936 3838 3937 3839 /* Platform profile ***********************************************************/ 3938 3840 static int asus_wmi_platform_profile_get(struct device *dev, ··· 4535 4435 &dev_attr_camera.attr, 4536 4436 &dev_attr_cardr.attr, 4537 4437 &dev_attr_touchpad.attr, 4538 - &dev_attr_charge_mode.attr, 4539 - &dev_attr_egpu_enable.attr, 4540 - &dev_attr_egpu_connected.attr, 4541 - &dev_attr_dgpu_disable.attr, 4542 - &dev_attr_gpu_mux_mode.attr, 4543 4438 &dev_attr_lid_resume.attr, 4544 4439 &dev_attr_als_enable.attr, 4545 4440 &dev_attr_fan_boost_mode.attr, 4546 - &dev_attr_throttle_thermal_policy.attr, 4547 - &dev_attr_ppt_pl2_sppt.attr, 4548 - &dev_attr_ppt_pl1_spl.attr, 4549 - &dev_attr_ppt_fppt.attr, 4550 - &dev_attr_ppt_apu_sppt.attr, 4551 - &dev_attr_ppt_platform_sppt.attr, 4552 - &dev_attr_nv_dynamic_boost.attr, 4553 - &dev_attr_nv_temp_target.attr, 4554 - &dev_attr_mcu_powersave.attr, 4555 - &dev_attr_boot_sound.attr, 4556 - &dev_attr_panel_od.attr, 4557 - &dev_attr_mini_led_mode.attr, 4558 - &dev_attr_available_mini_led_mode.attr, 4441 + #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) 4442 + &dev_attr_charge_mode.attr, 4443 + &dev_attr_egpu_enable.attr, 4444 + &dev_attr_egpu_connected.attr, 4445 + &dev_attr_dgpu_disable.attr, 4446 + &dev_attr_gpu_mux_mode.attr, 4447 + &dev_attr_ppt_pl2_sppt.attr, 4448 + &dev_attr_ppt_pl1_spl.attr, 4449 + &dev_attr_ppt_fppt.attr, 4450 + &dev_attr_ppt_apu_sppt.attr, 4451 + &dev_attr_ppt_platform_sppt.attr, 4452 + &dev_attr_nv_dynamic_boost.attr, 4453 + &dev_attr_nv_temp_target.attr, 4454 + &dev_attr_mcu_powersave.attr, 4455 + &dev_attr_boot_sound.attr, 4456 + &dev_attr_panel_od.attr, 4457 + &dev_attr_mini_led_mode.attr, 4458 + &dev_attr_available_mini_led_mode.attr, 4459 + &dev_attr_throttle_thermal_policy.attr, 4460 + #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ 4559 4461 NULL 4560 4462 }; 4561 4463 ··· 4579 4477 devid = ASUS_WMI_DEVID_LID_RESUME; 4580 4478 else if (attr == &dev_attr_als_enable.attr) 4581 4479 devid = ASUS_WMI_DEVID_ALS_ENABLE; 4582 - else if (attr == &dev_attr_charge_mode.attr) 4480 + else if (attr == &dev_attr_fan_boost_mode.attr) 4481 + ok = asus->fan_boost_mode_available; 4482 + 4483 + #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) 4484 + if (attr == &dev_attr_charge_mode.attr) 4583 4485 devid = ASUS_WMI_DEVID_CHARGE_MODE; 4584 4486 else if (attr == &dev_attr_egpu_enable.attr) 4585 4487 ok = asus->egpu_enable_available; ··· 4602 4496 else if (attr == &dev_attr_ppt_pl1_spl.attr) 4603 4497 devid = ASUS_WMI_DEVID_PPT_PL1_SPL; 4604 4498 else if (attr == &dev_attr_ppt_fppt.attr) 4605 - devid = ASUS_WMI_DEVID_PPT_FPPT; 4499 + devid = ASUS_WMI_DEVID_PPT_PL3_FPPT; 4606 4500 else if (attr == &dev_attr_ppt_apu_sppt.attr) 4607 4501 devid = ASUS_WMI_DEVID_PPT_APU_SPPT; 4608 4502 else if (attr == &dev_attr_ppt_platform_sppt.attr) ··· 4621 4515 ok = asus->mini_led_dev_id != 0; 4622 4516 else if (attr == &dev_attr_available_mini_led_mode.attr) 4623 4517 ok = asus->mini_led_dev_id != 0; 4518 + #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ 4624 4519 4625 4520 if (devid != -1) { 4626 4521 ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); ··· 4877 4770 } 4878 4771 4879 4772 /* ensure defaults for tunables */ 4773 + #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) 4880 4774 asus->ppt_pl2_sppt = 5; 4881 4775 asus->ppt_pl1_spl = 5; 4882 4776 asus->ppt_apu_sppt = 5; ··· 4900 4792 asus->gpu_mux_dev = ASUS_WMI_DEVID_GPU_MUX; 4901 4793 else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_GPU_MUX_VIVO)) 4902 4794 asus->gpu_mux_dev = ASUS_WMI_DEVID_GPU_MUX_VIVO; 4903 - 4904 - if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE)) 4905 - asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE; 4906 - else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE2)) 4907 - asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE2; 4795 + #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ 4908 4796 4909 4797 if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY)) 4910 4798 asus->throttle_thermal_policy_dev = ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY; 4911 4799 else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO)) 4912 4800 asus->throttle_thermal_policy_dev = ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO; 4801 + 4802 + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE)) 4803 + asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE; 4804 + else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE2)) 4805 + asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE2; 4913 4806 4914 4807 err = fan_boost_mode_check_present(asus); 4915 4808 if (err)
+593
drivers/platform/x86/ayaneo-ec.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Platform driver for the Embedded Controller (EC) of Ayaneo devices. Handles 4 + * hwmon (fan speed, fan control), battery charge limits, and magic module 5 + * control (connected modules, controller disconnection). 6 + * 7 + * Copyright (C) 2025 Antheas Kapenekakis <lkml@antheas.dev> 8 + */ 9 + 10 + #include <linux/acpi.h> 11 + #include <linux/bits.h> 12 + #include <linux/dmi.h> 13 + #include <linux/err.h> 14 + #include <linux/hwmon.h> 15 + #include <linux/init.h> 16 + #include <linux/kernel.h> 17 + #include <linux/module.h> 18 + #include <linux/platform_device.h> 19 + #include <linux/pm.h> 20 + #include <linux/power_supply.h> 21 + #include <linux/sysfs.h> 22 + #include <acpi/battery.h> 23 + 24 + #define AYANEO_PWM_ENABLE_REG 0x4A 25 + #define AYANEO_PWM_REG 0x4B 26 + #define AYANEO_PWM_MODE_AUTO 0x00 27 + #define AYANEO_PWM_MODE_MANUAL 0x01 28 + 29 + #define AYANEO_FAN_REG 0x76 30 + 31 + #define EC_CHARGE_CONTROL_BEHAVIOURS \ 32 + (BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) | \ 33 + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE)) 34 + #define AYANEO_CHARGE_REG 0x1e 35 + #define AYANEO_CHARGE_VAL_AUTO 0xaa 36 + #define AYANEO_CHARGE_VAL_INHIBIT 0x55 37 + 38 + #define AYANEO_POWER_REG 0x2d 39 + #define AYANEO_POWER_OFF 0xfe 40 + #define AYANEO_POWER_ON 0xff 41 + #define AYANEO_MODULE_REG 0x2f 42 + #define AYANEO_MODULE_LEFT BIT(0) 43 + #define AYANEO_MODULE_RIGHT BIT(1) 44 + #define AYANEO_MODULE_MASK (AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT) 45 + 46 + struct ayaneo_ec_quirk { 47 + bool has_fan_control; 48 + bool has_charge_control; 49 + bool has_magic_modules; 50 + }; 51 + 52 + struct ayaneo_ec_platform_data { 53 + struct platform_device *pdev; 54 + struct ayaneo_ec_quirk *quirks; 55 + struct acpi_battery_hook battery_hook; 56 + 57 + // Protects access to restore_pwm 58 + struct mutex hwmon_lock; 59 + bool restore_charge_limit; 60 + bool restore_pwm; 61 + }; 62 + 63 + static const struct ayaneo_ec_quirk quirk_fan = { 64 + .has_fan_control = true, 65 + }; 66 + 67 + static const struct ayaneo_ec_quirk quirk_charge_limit = { 68 + .has_fan_control = true, 69 + .has_charge_control = true, 70 + }; 71 + 72 + static const struct ayaneo_ec_quirk quirk_ayaneo3 = { 73 + .has_fan_control = true, 74 + .has_charge_control = true, 75 + .has_magic_modules = true, 76 + }; 77 + 78 + static const struct dmi_system_id dmi_table[] = { 79 + { 80 + .matches = { 81 + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 82 + DMI_MATCH(DMI_BOARD_NAME, "AYANEO 2"), 83 + }, 84 + .driver_data = (void *)&quirk_fan, 85 + }, 86 + { 87 + .matches = { 88 + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 89 + DMI_MATCH(DMI_BOARD_NAME, "FLIP"), 90 + }, 91 + .driver_data = (void *)&quirk_fan, 92 + }, 93 + { 94 + .matches = { 95 + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 96 + DMI_MATCH(DMI_BOARD_NAME, "GEEK"), 97 + }, 98 + .driver_data = (void *)&quirk_fan, 99 + }, 100 + { 101 + .matches = { 102 + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 103 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"), 104 + }, 105 + .driver_data = (void *)&quirk_charge_limit, 106 + }, 107 + { 108 + .matches = { 109 + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 110 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR 1S"), 111 + }, 112 + .driver_data = (void *)&quirk_charge_limit, 113 + }, 114 + { 115 + .matches = { 116 + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 117 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AB05-Mendocino"), 118 + }, 119 + .driver_data = (void *)&quirk_charge_limit, 120 + }, 121 + { 122 + .matches = { 123 + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 124 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"), 125 + }, 126 + .driver_data = (void *)&quirk_charge_limit, 127 + }, 128 + { 129 + .matches = { 130 + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 131 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "KUN"), 132 + }, 133 + .driver_data = (void *)&quirk_charge_limit, 134 + }, 135 + { 136 + .matches = { 137 + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 138 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 3"), 139 + }, 140 + .driver_data = (void *)&quirk_ayaneo3, 141 + }, 142 + {}, 143 + }; 144 + 145 + /* Callbacks for hwmon interface */ 146 + static umode_t ayaneo_ec_hwmon_is_visible(const void *drvdata, 147 + enum hwmon_sensor_types type, u32 attr, 148 + int channel) 149 + { 150 + switch (type) { 151 + case hwmon_fan: 152 + return 0444; 153 + case hwmon_pwm: 154 + return 0644; 155 + default: 156 + return 0; 157 + } 158 + } 159 + 160 + static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type, 161 + u32 attr, int channel, long *val) 162 + { 163 + u8 tmp; 164 + int ret; 165 + 166 + switch (type) { 167 + case hwmon_fan: 168 + switch (attr) { 169 + case hwmon_fan_input: 170 + ret = ec_read(AYANEO_FAN_REG, &tmp); 171 + if (ret) 172 + return ret; 173 + *val = tmp << 8; 174 + ret = ec_read(AYANEO_FAN_REG + 1, &tmp); 175 + if (ret) 176 + return ret; 177 + *val |= tmp; 178 + return 0; 179 + default: 180 + break; 181 + } 182 + break; 183 + case hwmon_pwm: 184 + switch (attr) { 185 + case hwmon_pwm_input: 186 + ret = ec_read(AYANEO_PWM_REG, &tmp); 187 + if (ret) 188 + return ret; 189 + if (tmp > 100) 190 + return -EIO; 191 + *val = (255 * tmp) / 100; 192 + return 0; 193 + case hwmon_pwm_enable: 194 + ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp); 195 + if (ret) 196 + return ret; 197 + if (tmp == AYANEO_PWM_MODE_MANUAL) 198 + *val = 1; 199 + else if (tmp == AYANEO_PWM_MODE_AUTO) 200 + *val = 2; 201 + else 202 + return -EIO; 203 + return 0; 204 + default: 205 + break; 206 + } 207 + break; 208 + default: 209 + break; 210 + } 211 + return -EOPNOTSUPP; 212 + } 213 + 214 + static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type, 215 + u32 attr, int channel, long val) 216 + { 217 + struct ayaneo_ec_platform_data *data = dev_get_drvdata(dev); 218 + int ret; 219 + 220 + guard(mutex)(&data->hwmon_lock); 221 + 222 + switch (type) { 223 + case hwmon_pwm: 224 + switch (attr) { 225 + case hwmon_pwm_enable: 226 + data->restore_pwm = false; 227 + switch (val) { 228 + case 1: 229 + return ec_write(AYANEO_PWM_ENABLE_REG, 230 + AYANEO_PWM_MODE_MANUAL); 231 + case 2: 232 + return ec_write(AYANEO_PWM_ENABLE_REG, 233 + AYANEO_PWM_MODE_AUTO); 234 + default: 235 + return -EINVAL; 236 + } 237 + case hwmon_pwm_input: 238 + if (val < 0 || val > 255) 239 + return -EINVAL; 240 + if (data->restore_pwm) { 241 + /* 242 + * Defer restoring PWM control to after 243 + * userspace resumes successfully 244 + */ 245 + ret = ec_write(AYANEO_PWM_ENABLE_REG, 246 + AYANEO_PWM_MODE_MANUAL); 247 + if (ret) 248 + return ret; 249 + data->restore_pwm = false; 250 + } 251 + return ec_write(AYANEO_PWM_REG, (val * 100) / 255); 252 + default: 253 + break; 254 + } 255 + break; 256 + default: 257 + break; 258 + } 259 + return -EOPNOTSUPP; 260 + } 261 + 262 + static const struct hwmon_ops ayaneo_ec_hwmon_ops = { 263 + .is_visible = ayaneo_ec_hwmon_is_visible, 264 + .read = ayaneo_ec_read, 265 + .write = ayaneo_ec_write, 266 + }; 267 + 268 + static const struct hwmon_channel_info *const ayaneo_ec_sensors[] = { 269 + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT), 270 + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE), 271 + NULL, 272 + }; 273 + 274 + static const struct hwmon_chip_info ayaneo_ec_chip_info = { 275 + .ops = &ayaneo_ec_hwmon_ops, 276 + .info = ayaneo_ec_sensors, 277 + }; 278 + 279 + static int ayaneo_psy_ext_get_prop(struct power_supply *psy, 280 + const struct power_supply_ext *ext, 281 + void *data, 282 + enum power_supply_property psp, 283 + union power_supply_propval *val) 284 + { 285 + int ret; 286 + u8 tmp; 287 + 288 + switch (psp) { 289 + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: 290 + ret = ec_read(AYANEO_CHARGE_REG, &tmp); 291 + if (ret) 292 + return ret; 293 + 294 + if (tmp == AYANEO_CHARGE_VAL_INHIBIT) 295 + val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; 296 + else 297 + val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; 298 + return 0; 299 + default: 300 + return -EINVAL; 301 + } 302 + } 303 + 304 + static int ayaneo_psy_ext_set_prop(struct power_supply *psy, 305 + const struct power_supply_ext *ext, 306 + void *data, 307 + enum power_supply_property psp, 308 + const union power_supply_propval *val) 309 + { 310 + u8 raw_val; 311 + 312 + switch (psp) { 313 + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: 314 + switch (val->intval) { 315 + case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: 316 + raw_val = AYANEO_CHARGE_VAL_AUTO; 317 + break; 318 + case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE: 319 + raw_val = AYANEO_CHARGE_VAL_INHIBIT; 320 + break; 321 + default: 322 + return -EINVAL; 323 + } 324 + return ec_write(AYANEO_CHARGE_REG, raw_val); 325 + default: 326 + return -EINVAL; 327 + } 328 + } 329 + 330 + static int ayaneo_psy_prop_is_writeable(struct power_supply *psy, 331 + const struct power_supply_ext *ext, 332 + void *data, 333 + enum power_supply_property psp) 334 + { 335 + return true; 336 + } 337 + 338 + static const enum power_supply_property ayaneo_psy_ext_props[] = { 339 + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, 340 + }; 341 + 342 + static const struct power_supply_ext ayaneo_psy_ext = { 343 + .name = "ayaneo-charge-control", 344 + .properties = ayaneo_psy_ext_props, 345 + .num_properties = ARRAY_SIZE(ayaneo_psy_ext_props), 346 + .charge_behaviours = EC_CHARGE_CONTROL_BEHAVIOURS, 347 + .get_property = ayaneo_psy_ext_get_prop, 348 + .set_property = ayaneo_psy_ext_set_prop, 349 + .property_is_writeable = ayaneo_psy_prop_is_writeable, 350 + }; 351 + 352 + static int ayaneo_add_battery(struct power_supply *battery, 353 + struct acpi_battery_hook *hook) 354 + { 355 + struct ayaneo_ec_platform_data *data = 356 + container_of(hook, struct ayaneo_ec_platform_data, battery_hook); 357 + 358 + return power_supply_register_extension(battery, &ayaneo_psy_ext, 359 + &data->pdev->dev, NULL); 360 + } 361 + 362 + static int ayaneo_remove_battery(struct power_supply *battery, 363 + struct acpi_battery_hook *hook) 364 + { 365 + power_supply_unregister_extension(battery, &ayaneo_psy_ext); 366 + return 0; 367 + } 368 + 369 + static ssize_t controller_power_store(struct device *dev, 370 + struct device_attribute *attr, 371 + const char *buf, 372 + size_t count) 373 + { 374 + bool value; 375 + int ret; 376 + 377 + ret = kstrtobool(buf, &value); 378 + if (ret) 379 + return ret; 380 + 381 + ret = ec_write(AYANEO_POWER_REG, value ? AYANEO_POWER_ON : AYANEO_POWER_OFF); 382 + if (ret) 383 + return ret; 384 + 385 + return count; 386 + } 387 + 388 + static ssize_t controller_power_show(struct device *dev, 389 + struct device_attribute *attr, 390 + char *buf) 391 + { 392 + int ret; 393 + u8 val; 394 + 395 + ret = ec_read(AYANEO_POWER_REG, &val); 396 + if (ret) 397 + return ret; 398 + 399 + return sysfs_emit(buf, "%d\n", val == AYANEO_POWER_ON); 400 + } 401 + 402 + static DEVICE_ATTR_RW(controller_power); 403 + 404 + static ssize_t controller_modules_show(struct device *dev, 405 + struct device_attribute *attr, char *buf) 406 + { 407 + u8 unconnected_modules; 408 + char *out; 409 + int ret; 410 + 411 + ret = ec_read(AYANEO_MODULE_REG, &unconnected_modules); 412 + if (ret) 413 + return ret; 414 + 415 + switch (~unconnected_modules & AYANEO_MODULE_MASK) { 416 + case AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT: 417 + out = "both"; 418 + break; 419 + case AYANEO_MODULE_LEFT: 420 + out = "left"; 421 + break; 422 + case AYANEO_MODULE_RIGHT: 423 + out = "right"; 424 + break; 425 + default: 426 + out = "none"; 427 + break; 428 + } 429 + 430 + return sysfs_emit(buf, "%s\n", out); 431 + } 432 + 433 + static DEVICE_ATTR_RO(controller_modules); 434 + 435 + static struct attribute *aya_mm_attrs[] = { 436 + &dev_attr_controller_power.attr, 437 + &dev_attr_controller_modules.attr, 438 + NULL 439 + }; 440 + 441 + static umode_t aya_mm_is_visible(struct kobject *kobj, 442 + struct attribute *attr, int n) 443 + { 444 + struct device *dev = kobj_to_dev(kobj); 445 + struct platform_device *pdev = to_platform_device(dev); 446 + struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev); 447 + 448 + if (data->quirks->has_magic_modules) 449 + return attr->mode; 450 + return 0; 451 + } 452 + 453 + static const struct attribute_group aya_mm_attribute_group = { 454 + .is_visible = aya_mm_is_visible, 455 + .attrs = aya_mm_attrs, 456 + }; 457 + 458 + static const struct attribute_group *ayaneo_ec_groups[] = { 459 + &aya_mm_attribute_group, 460 + NULL 461 + }; 462 + 463 + static int ayaneo_ec_probe(struct platform_device *pdev) 464 + { 465 + const struct dmi_system_id *dmi_entry; 466 + struct ayaneo_ec_platform_data *data; 467 + struct device *hwdev; 468 + int ret; 469 + 470 + dmi_entry = dmi_first_match(dmi_table); 471 + if (!dmi_entry) 472 + return -ENODEV; 473 + 474 + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 475 + if (!data) 476 + return -ENOMEM; 477 + 478 + data->pdev = pdev; 479 + data->quirks = dmi_entry->driver_data; 480 + ret = devm_mutex_init(&pdev->dev, &data->hwmon_lock); 481 + if (ret) 482 + return ret; 483 + platform_set_drvdata(pdev, data); 484 + 485 + if (data->quirks->has_fan_control) { 486 + hwdev = devm_hwmon_device_register_with_info(&pdev->dev, 487 + "ayaneo_ec", data, &ayaneo_ec_chip_info, NULL); 488 + if (IS_ERR(hwdev)) 489 + return PTR_ERR(hwdev); 490 + } 491 + 492 + if (data->quirks->has_charge_control) { 493 + data->battery_hook.add_battery = ayaneo_add_battery; 494 + data->battery_hook.remove_battery = ayaneo_remove_battery; 495 + data->battery_hook.name = "Ayaneo Battery"; 496 + ret = devm_battery_hook_register(&pdev->dev, &data->battery_hook); 497 + if (ret) 498 + return ret; 499 + } 500 + 501 + return 0; 502 + } 503 + 504 + static int ayaneo_freeze(struct device *dev) 505 + { 506 + struct platform_device *pdev = to_platform_device(dev); 507 + struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev); 508 + int ret; 509 + u8 tmp; 510 + 511 + if (data->quirks->has_charge_control) { 512 + ret = ec_read(AYANEO_CHARGE_REG, &tmp); 513 + if (ret) 514 + return ret; 515 + 516 + data->restore_charge_limit = tmp == AYANEO_CHARGE_VAL_INHIBIT; 517 + } 518 + 519 + if (data->quirks->has_fan_control) { 520 + ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp); 521 + if (ret) 522 + return ret; 523 + 524 + data->restore_pwm = tmp == AYANEO_PWM_MODE_MANUAL; 525 + 526 + /* 527 + * Release the fan when entering hibernation to avoid 528 + * overheating if hibernation fails and hangs. 529 + */ 530 + if (data->restore_pwm) { 531 + ret = ec_write(AYANEO_PWM_ENABLE_REG, AYANEO_PWM_MODE_AUTO); 532 + if (ret) 533 + return ret; 534 + } 535 + } 536 + 537 + return 0; 538 + } 539 + 540 + static int ayaneo_restore(struct device *dev) 541 + { 542 + struct platform_device *pdev = to_platform_device(dev); 543 + struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev); 544 + int ret; 545 + 546 + if (data->quirks->has_charge_control && data->restore_charge_limit) { 547 + ret = ec_write(AYANEO_CHARGE_REG, AYANEO_CHARGE_VAL_INHIBIT); 548 + if (ret) 549 + return ret; 550 + } 551 + 552 + return 0; 553 + } 554 + 555 + static const struct dev_pm_ops ayaneo_pm_ops = { 556 + .freeze = ayaneo_freeze, 557 + .restore = ayaneo_restore, 558 + }; 559 + 560 + static struct platform_driver ayaneo_platform_driver = { 561 + .driver = { 562 + .name = "ayaneo-ec", 563 + .dev_groups = ayaneo_ec_groups, 564 + .pm = pm_sleep_ptr(&ayaneo_pm_ops), 565 + }, 566 + .probe = ayaneo_ec_probe, 567 + }; 568 + 569 + static struct platform_device *ayaneo_platform_device; 570 + 571 + static int __init ayaneo_ec_init(void) 572 + { 573 + ayaneo_platform_device = 574 + platform_create_bundle(&ayaneo_platform_driver, 575 + ayaneo_ec_probe, NULL, 0, NULL, 0); 576 + 577 + return PTR_ERR_OR_ZERO(ayaneo_platform_device); 578 + } 579 + 580 + static void __exit ayaneo_ec_exit(void) 581 + { 582 + platform_device_unregister(ayaneo_platform_device); 583 + platform_driver_unregister(&ayaneo_platform_driver); 584 + } 585 + 586 + MODULE_DEVICE_TABLE(dmi, dmi_table); 587 + 588 + module_init(ayaneo_ec_init); 589 + module_exit(ayaneo_ec_exit); 590 + 591 + MODULE_AUTHOR("Antheas Kapenekakis <lkml@antheas.dev>"); 592 + MODULE_DESCRIPTION("Ayaneo Embedded Controller (EC) platform features"); 593 + MODULE_LICENSE("GPL");
+57 -67
drivers/platform/x86/dell/alienware-wmi-wmax.c
··· 235 235 AWCC_THERMAL_TABLE_USTT = 0xA, 236 236 }; 237 237 238 - enum AWCC_SPECIAL_THERMAL_CODES { 239 - AWCC_SPECIAL_PROFILE_CUSTOM = 0x00, 240 - AWCC_SPECIAL_PROFILE_GMODE = 0xAB, 241 - }; 242 - 243 238 enum AWCC_TEMP_SENSOR_TYPES { 244 239 AWCC_TEMP_SENSOR_CPU = 0x01, 245 240 AWCC_TEMP_SENSOR_FRONT = 0x03, ··· 261 266 }; 262 267 263 268 enum awcc_thermal_profile { 264 - AWCC_PROFILE_USTT_BALANCED, 265 - AWCC_PROFILE_USTT_BALANCED_PERFORMANCE, 266 - AWCC_PROFILE_USTT_COOL, 267 - AWCC_PROFILE_USTT_QUIET, 268 - AWCC_PROFILE_USTT_PERFORMANCE, 269 - AWCC_PROFILE_USTT_LOW_POWER, 270 - AWCC_PROFILE_LEGACY_QUIET, 271 - AWCC_PROFILE_LEGACY_BALANCED, 272 - AWCC_PROFILE_LEGACY_BALANCED_PERFORMANCE, 273 - AWCC_PROFILE_LEGACY_PERFORMANCE, 274 - AWCC_PROFILE_LAST, 269 + AWCC_PROFILE_SPECIAL_CUSTOM = 0x00, 270 + AWCC_PROFILE_LEGACY_QUIET = 0x96, 271 + AWCC_PROFILE_LEGACY_BALANCED = 0x97, 272 + AWCC_PROFILE_LEGACY_BALANCED_PERFORMANCE = 0x98, 273 + AWCC_PROFILE_LEGACY_PERFORMANCE = 0x99, 274 + AWCC_PROFILE_USTT_BALANCED = 0xA0, 275 + AWCC_PROFILE_USTT_BALANCED_PERFORMANCE = 0xA1, 276 + AWCC_PROFILE_USTT_COOL = 0xA2, 277 + AWCC_PROFILE_USTT_QUIET = 0xA3, 278 + AWCC_PROFILE_USTT_PERFORMANCE = 0xA4, 279 + AWCC_PROFILE_USTT_LOW_POWER = 0xA5, 280 + AWCC_PROFILE_SPECIAL_GMODE = 0xAB, 275 281 }; 276 282 277 283 struct wmax_led_args { ··· 326 330 unsigned long temp_sensors[AWCC_ID_BITMAP_LONGS]; 327 331 328 332 u32 gpio_count; 329 - }; 330 - 331 - static const enum platform_profile_option awcc_mode_to_platform_profile[AWCC_PROFILE_LAST] = { 332 - [AWCC_PROFILE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED, 333 - [AWCC_PROFILE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE, 334 - [AWCC_PROFILE_USTT_COOL] = PLATFORM_PROFILE_COOL, 335 - [AWCC_PROFILE_USTT_QUIET] = PLATFORM_PROFILE_QUIET, 336 - [AWCC_PROFILE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE, 337 - [AWCC_PROFILE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER, 338 - [AWCC_PROFILE_LEGACY_QUIET] = PLATFORM_PROFILE_QUIET, 339 - [AWCC_PROFILE_LEGACY_BALANCED] = PLATFORM_PROFILE_BALANCED, 340 - [AWCC_PROFILE_LEGACY_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE, 341 - [AWCC_PROFILE_LEGACY_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE, 342 333 }; 343 334 344 335 static struct awcc_quirks *awcc; ··· 545 562 /* 546 563 * AWCC Helpers 547 564 */ 548 - static bool is_awcc_thermal_profile_id(u8 code) 565 + static int awcc_profile_to_pprof(enum awcc_thermal_profile profile, 566 + enum platform_profile_option *pprof) 549 567 { 550 - u8 table = FIELD_GET(AWCC_THERMAL_TABLE_MASK, code); 551 - u8 mode = FIELD_GET(AWCC_THERMAL_MODE_MASK, code); 568 + switch (profile) { 569 + case AWCC_PROFILE_SPECIAL_CUSTOM: 570 + *pprof = PLATFORM_PROFILE_CUSTOM; 571 + break; 572 + case AWCC_PROFILE_LEGACY_QUIET: 573 + case AWCC_PROFILE_USTT_QUIET: 574 + *pprof = PLATFORM_PROFILE_QUIET; 575 + break; 576 + case AWCC_PROFILE_LEGACY_BALANCED: 577 + case AWCC_PROFILE_USTT_BALANCED: 578 + *pprof = PLATFORM_PROFILE_BALANCED; 579 + break; 580 + case AWCC_PROFILE_LEGACY_BALANCED_PERFORMANCE: 581 + case AWCC_PROFILE_USTT_BALANCED_PERFORMANCE: 582 + *pprof = PLATFORM_PROFILE_BALANCED_PERFORMANCE; 583 + break; 584 + case AWCC_PROFILE_LEGACY_PERFORMANCE: 585 + case AWCC_PROFILE_USTT_PERFORMANCE: 586 + case AWCC_PROFILE_SPECIAL_GMODE: 587 + *pprof = PLATFORM_PROFILE_PERFORMANCE; 588 + break; 589 + case AWCC_PROFILE_USTT_COOL: 590 + *pprof = PLATFORM_PROFILE_COOL; 591 + break; 592 + case AWCC_PROFILE_USTT_LOW_POWER: 593 + *pprof = PLATFORM_PROFILE_LOW_POWER; 594 + break; 595 + default: 596 + return -EINVAL; 597 + } 552 598 553 - if (mode >= AWCC_PROFILE_LAST) 554 - return false; 555 - 556 - if (table == AWCC_THERMAL_TABLE_LEGACY && mode >= AWCC_PROFILE_LEGACY_QUIET) 557 - return true; 558 - 559 - if (table == AWCC_THERMAL_TABLE_USTT && mode <= AWCC_PROFILE_USTT_LOW_POWER) 560 - return true; 561 - 562 - return false; 599 + return 0; 563 600 } 564 601 565 602 static int awcc_wmi_command(struct wmi_device *wdev, u32 method_id, ··· 1228 1225 if (ret) 1229 1226 return ret; 1230 1227 1231 - switch (out_data) { 1232 - case AWCC_SPECIAL_PROFILE_CUSTOM: 1233 - *profile = PLATFORM_PROFILE_CUSTOM; 1234 - return 0; 1235 - case AWCC_SPECIAL_PROFILE_GMODE: 1236 - *profile = PLATFORM_PROFILE_PERFORMANCE; 1237 - return 0; 1238 - default: 1239 - break; 1240 - } 1241 - 1242 - if (!is_awcc_thermal_profile_id(out_data)) 1243 - return -ENODATA; 1244 - 1245 - out_data = FIELD_GET(AWCC_THERMAL_MODE_MASK, out_data); 1246 - *profile = awcc_mode_to_platform_profile[out_data]; 1247 - 1248 - return 0; 1228 + return awcc_profile_to_pprof(out_data, profile); 1249 1229 } 1250 1230 1251 1231 static int awcc_platform_profile_set(struct device *dev, ··· 1265 1279 { 1266 1280 enum platform_profile_option profile; 1267 1281 struct awcc_priv *priv = drvdata; 1268 - enum awcc_thermal_profile mode; 1269 1282 u8 id, offset = 0; 1270 1283 int ret; 1271 1284 ··· 1286 1301 if (ret) 1287 1302 return ret; 1288 1303 1289 - if (!is_awcc_thermal_profile_id(id)) { 1304 + /* 1305 + * G-Mode profile ID is not listed consistently across modeles 1306 + * that support it, therefore we handle it through quirks. 1307 + */ 1308 + if (id == AWCC_PROFILE_SPECIAL_GMODE) 1309 + continue; 1310 + 1311 + ret = awcc_profile_to_pprof(id, &profile); 1312 + if (ret) { 1290 1313 dev_dbg(&priv->wdev->dev, "Unmapped thermal profile ID 0x%02x\n", id); 1291 1314 continue; 1292 1315 } 1293 1316 1294 - mode = FIELD_GET(AWCC_THERMAL_MODE_MASK, id); 1295 - profile = awcc_mode_to_platform_profile[mode]; 1296 1317 priv->supported_profiles[profile] = id; 1297 - 1298 1318 __set_bit(profile, choices); 1299 1319 } 1300 1320 ··· 1308 1318 1309 1319 if (awcc->gmode) { 1310 1320 priv->supported_profiles[PLATFORM_PROFILE_PERFORMANCE] = 1311 - AWCC_SPECIAL_PROFILE_GMODE; 1321 + AWCC_PROFILE_SPECIAL_GMODE; 1312 1322 1313 1323 __set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); 1314 1324 } 1315 1325 1316 1326 /* Every model supports the "custom" profile */ 1317 1327 priv->supported_profiles[PLATFORM_PROFILE_CUSTOM] = 1318 - AWCC_SPECIAL_PROFILE_CUSTOM; 1328 + AWCC_PROFILE_SPECIAL_CUSTOM; 1319 1329 1320 1330 __set_bit(PLATFORM_PROFILE_CUSTOM, choices); 1321 1331
+2 -2
drivers/platform/x86/gpd-pocket-fan.c
··· 112 112 gpd_pocket_fan_set_speed(fan, speed); 113 113 114 114 /* When mostly idle (low temp/speed), slow down the poll interval. */ 115 - queue_delayed_work(system_wq, &fan->work, 115 + queue_delayed_work(system_percpu_wq, &fan->work, 116 116 msecs_to_jiffies(4000 / (speed + 1))); 117 117 } 118 118 119 119 static void gpd_pocket_fan_force_update(struct gpd_pocket_fan_data *fan) 120 120 { 121 121 fan->last_speed = -1; 122 - mod_delayed_work(system_wq, &fan->work, 0); 122 + mod_delayed_work(system_percpu_wq, &fan->work, 0); 123 123 } 124 124 125 125 static int gpd_pocket_fan_probe(struct platform_device *pdev)
+15 -9
drivers/platform/x86/hp/hp-wmi.c
··· 63 63 * contains "PerformanceControl". 64 64 */ 65 65 static const char * const omen_thermal_profile_boards[] = { 66 - "84DA", "84DB", "84DC", "8574", "8575", "860A", "87B5", "8572", "8573", 67 - "8600", "8601", "8602", "8605", "8606", "8607", "8746", "8747", "8749", 68 - "874A", "8603", "8604", "8748", "886B", "886C", "878A", "878B", "878C", 69 - "88C8", "88CB", "8786", "8787", "8788", "88D1", "88D2", "88F4", "88FD", 70 - "88F5", "88F6", "88F7", "88FE", "88FF", "8900", "8901", "8902", "8912", 71 - "8917", "8918", "8949", "894A", "89EB", "8BAD", "8A42", "8A15" 66 + "84DA", "84DB", "84DC", 67 + "8572", "8573", "8574", "8575", 68 + "8600", "8601", "8602", "8603", "8604", "8605", "8606", "8607", "860A", 69 + "8746", "8747", "8748", "8749", "874A", "8786", "8787", "8788", "878A", 70 + "878B", "878C", "87B5", 71 + "886B", "886C", "88C8", "88CB", "88D1", "88D2", "88F4", "88F5", "88F6", 72 + "88F7", "88FD", "88FE", "88FF", 73 + "8900", "8901", "8902", "8912", "8917", "8918", "8949", "894A", "89EB", 74 + "8A15", "8A42", 75 + "8BAD", 72 76 }; 73 77 74 78 /* DMI Board names of Omen laptops that are specifically set to be thermal ··· 80 76 * the get system design information WMI call returns 81 77 */ 82 78 static const char * const omen_thermal_profile_force_v0_boards[] = { 83 - "8607", "8746", "8747", "8749", "874A", "8748" 79 + "8607", 80 + "8746", "8747", "8748", "8749", "874A", 84 81 }; 85 82 86 83 /* DMI board names of Omen laptops that have a thermal profile timer which will ··· 89 84 * "balanced" when reaching zero. 90 85 */ 91 86 static const char * const omen_timed_thermal_profile_boards[] = { 92 - "8BAD", "8A42", "8A15" 87 + "8A15", "8A42", 88 + "8BAD", 93 89 }; 94 90 95 91 /* DMI Board names of Victus 16-d1xxx laptops */ 96 92 static const char * const victus_thermal_profile_boards[] = { 97 - "8A25" 93 + "8A25", 98 94 }; 99 95 100 96 /* DMI Board names of Victus 16-r and Victus 16-s laptops */
+12
drivers/platform/x86/intel/hid.c
··· 177 177 DMI_MATCH(DMI_PRODUCT_NAME, "HP Elite Dragonfly G2 Notebook PC"), 178 178 }, 179 179 }, 180 + { 181 + .matches = { 182 + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 183 + DMI_MATCH(DMI_PRODUCT_NAME, "Dell Pro Rugged 10 Tablet RA00260"), 184 + }, 185 + }, 186 + { 187 + .matches = { 188 + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 189 + DMI_MATCH(DMI_PRODUCT_NAME, "Dell Pro Rugged 12 Tablet RA02260"), 190 + }, 191 + }, 180 192 { } 181 193 }; 182 194
+6 -6
drivers/platform/x86/intel/pmc/arl.c
··· 281 281 .etr3_offset = ETR3_OFFSET, 282 282 .pson_residency_offset = TGL_PSON_RESIDENCY_OFFSET, 283 283 .pson_residency_counter_step = TGL_PSON_RES_COUNTER_STEP, 284 + .lpm_req_guid = SOCS_LPM_REQ_GUID, 284 285 }; 285 286 286 287 static const struct pmc_bit_map arl_pchs_ltr_show_map[] = { ··· 649 648 .lpm_num_maps = ADL_LPM_NUM_MAPS, 650 649 .lpm_reg_index = ARL_LPM_REG_INDEX, 651 650 .etr3_offset = ETR3_OFFSET, 651 + .lpm_req_guid = PCHS_LPM_REQ_GUID, 652 652 }; 653 653 654 654 static struct pmc_info arl_pmc_info_list[] = { 655 655 { 656 - .guid = IOEP_LPM_REQ_GUID, 657 656 .devid = PMC_DEVID_ARL_IOEP, 658 657 .map = &mtl_ioep_reg_map, 659 658 }, 660 659 { 661 - .guid = SOCS_LPM_REQ_GUID, 662 660 .devid = PMC_DEVID_ARL_SOCS, 663 661 .map = &arl_socs_reg_map, 664 662 }, 665 663 { 666 - .guid = PCHS_LPM_REQ_GUID, 667 664 .devid = PMC_DEVID_ARL_PCHS, 668 665 .map = &arl_pchs_reg_map, 669 666 }, 670 667 { 671 - .guid = SOCM_LPM_REQ_GUID, 672 668 .devid = PMC_DEVID_ARL_SOCM, 673 669 .map = &mtl_socm_reg_map, 674 670 }, ··· 718 720 return generic_core_init(pmcdev, pmc_dev_info); 719 721 } 720 722 723 + static u32 ARL_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0}; 721 724 struct pmc_dev_info arl_pmc_dev = { 722 725 .pci_func = 0, 723 - .dmu_guid = ARL_PMT_DMU_GUID, 726 + .dmu_guids = ARL_PMT_DMU_GUIDS, 724 727 .regmap_list = arl_pmc_info_list, 725 728 .map = &arl_socs_reg_map, 726 729 .sub_req_show = &pmc_core_substate_req_regs_fops, ··· 731 732 .sub_req = pmc_core_pmt_get_lpm_req, 732 733 }; 733 734 735 + static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, ARL_H_PMT_DMU_GUID, 0x0}; 734 736 struct pmc_dev_info arl_h_pmc_dev = { 735 737 .pci_func = 2, 736 - .dmu_guid = ARL_PMT_DMU_GUID, 738 + .dmu_guids = ARL_H_PMT_DMU_GUIDS, 737 739 .regmap_list = arl_pmc_info_list, 738 740 .map = &mtl_socm_reg_map, 739 741 .sub_req_show = &pmc_core_substate_req_regs_fops,
+75 -74
drivers/platform/x86/intel/pmc/core.c
··· 20 20 #include <linux/debugfs.h> 21 21 #include <linux/delay.h> 22 22 #include <linux/dmi.h> 23 + #include <linux/err.h> 23 24 #include <linux/io.h> 24 25 #include <linux/module.h> 25 26 #include <linux/pci.h> ··· 312 311 } 313 312 314 313 static void pmc_core_display_map(struct seq_file *s, int index, int idx, int ip, 315 - int pmc_index, u8 pf_reg, const struct pmc_bit_map **pf_map) 314 + int pmc_idx, u8 pf_reg, const struct pmc_bit_map **pf_map) 316 315 { 317 316 seq_printf(s, "PMC%d:PCH IP: %-2d - %-32s\tState: %s\n", 318 - pmc_index, ip, pf_map[idx][index].name, 317 + pmc_idx, ip, pf_map[idx][index].name, 319 318 pf_map[idx][index].bit_mask & pf_reg ? "Off" : "On"); 320 319 } 321 320 322 321 static int pmc_core_ppfear_show(struct seq_file *s, void *unused) 323 322 { 324 323 struct pmc_dev *pmcdev = s->private; 325 - unsigned int i; 324 + unsigned int pmc_idx; 326 325 327 - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { 328 - struct pmc *pmc = pmcdev->pmcs[i]; 326 + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { 327 + struct pmc *pmc = pmcdev->pmcs[pmc_idx]; 329 328 const struct pmc_bit_map **maps; 330 329 u8 pf_regs[PPFEAR_MAX_NUM_ENTRIES]; 331 330 unsigned int index, iter, idx, ip = 0; ··· 343 342 for (idx = 0; maps[idx]; idx++) { 344 343 for (index = 0; maps[idx][index].name && 345 344 index < pmc->map->ppfear_buckets * 8; ip++, index++) 346 - pmc_core_display_map(s, index, idx, ip, i, 345 + pmc_core_display_map(s, index, idx, ip, pmc_idx, 347 346 pf_regs[index / 8], maps); 348 347 } 349 348 } ··· 472 471 struct pmc *pmc; 473 472 const struct pmc_reg_map *map; 474 473 u32 reg; 475 - unsigned int pmc_index; 474 + unsigned int pmc_idx; 476 475 int ltr_index; 477 476 478 477 ltr_index = value; ··· 480 479 * is based on the contiguous indexes from ltr_show output. 481 480 * pmc index and ltr index needs to be calculated from it. 482 481 */ 483 - for (pmc_index = 0; pmc_index < ARRAY_SIZE(pmcdev->pmcs) && ltr_index >= 0; pmc_index++) { 484 - pmc = pmcdev->pmcs[pmc_index]; 482 + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs) && ltr_index >= 0; pmc_idx++) { 483 + pmc = pmcdev->pmcs[pmc_idx]; 485 484 486 485 if (!pmc) 487 486 continue; ··· 498 497 ltr_index = ltr_index - (map->ltr_ignore_max + 2) - 1; 499 498 } 500 499 501 - if (pmc_index >= ARRAY_SIZE(pmcdev->pmcs) || ltr_index < 0) 500 + if (pmc_idx >= ARRAY_SIZE(pmcdev->pmcs) || ltr_index < 0) 502 501 return -EINVAL; 503 502 504 - pr_debug("ltr_ignore for pmc%d: ltr_index:%d\n", pmc_index, ltr_index); 503 + pr_debug("ltr_ignore for pmc%d: ltr_index:%d\n", pmc_idx, ltr_index); 505 504 506 505 guard(mutex)(&pmcdev->lock); 507 506 ··· 636 635 u64 decoded_snoop_ltr, decoded_non_snoop_ltr, val; 637 636 u32 ltr_raw_data, scale; 638 637 u16 snoop_ltr, nonsnoop_ltr; 639 - unsigned int i, index, ltr_index = 0; 638 + unsigned int pmc_idx, index, ltr_index = 0; 640 639 641 - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { 640 + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { 642 641 struct pmc *pmc; 643 642 const struct pmc_bit_map *map; 644 643 u32 ltr_ign_reg; 645 644 646 - pmc = pmcdev->pmcs[i]; 645 + pmc = pmcdev->pmcs[pmc_idx]; 647 646 if (!pmc) 648 647 continue; 649 648 ··· 677 676 } 678 677 679 678 seq_printf(s, "%d\tPMC%d:%-32s\tLTR: RAW: 0x%-16x\tNon-Snoop(ns): %-16llu\tSnoop(ns): %-16llu\tLTR_IGNORE: %d\n", 680 - ltr_index, i, map[index].name, ltr_raw_data, 679 + ltr_index, pmc_idx, map[index].name, ltr_raw_data, 681 680 decoded_non_snoop_ltr, 682 681 decoded_snoop_ltr, ltr_ign_data); 683 682 ltr_index++; ··· 690 689 static int pmc_core_s0ix_blocker_show(struct seq_file *s, void *unused) 691 690 { 692 691 struct pmc_dev *pmcdev = s->private; 693 - unsigned int pmcidx; 692 + unsigned int pmc_idx; 694 693 695 - for (pmcidx = 0; pmcidx < ARRAY_SIZE(pmcdev->pmcs); pmcidx++) { 694 + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) { 696 695 const struct pmc_bit_map **maps; 697 696 unsigned int arr_size, r_idx; 698 697 u32 offset, counter; 699 698 struct pmc *pmc; 700 699 701 - pmc = pmcdev->pmcs[pmcidx]; 700 + pmc = pmcdev->pmcs[pmc_idx]; 702 701 if (!pmc) 703 702 continue; 704 703 maps = pmc->map->s0ix_blocker_maps; ··· 712 711 if (!map->blk) 713 712 continue; 714 713 counter = pmc_core_reg_read(pmc, offset); 715 - seq_printf(s, "PMC%d:%-30s %-30d\n", pmcidx, 714 + seq_printf(s, "PMC%d:%-30s %-30d\n", pmc_idx, 716 715 map->name, counter); 717 716 offset += map->blk * S0IX_BLK_SIZE; 718 717 } ··· 724 723 725 724 static void pmc_core_ltr_ignore_all(struct pmc_dev *pmcdev) 726 725 { 727 - unsigned int i; 726 + unsigned int pmc_idx; 728 727 729 - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); i++) { 728 + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) { 730 729 struct pmc *pmc; 731 730 u32 ltr_ign; 732 731 733 - pmc = pmcdev->pmcs[i]; 732 + pmc = pmcdev->pmcs[pmc_idx]; 734 733 if (!pmc) 735 734 continue; 736 735 ··· 751 750 752 751 static void pmc_core_ltr_restore_all(struct pmc_dev *pmcdev) 753 752 { 754 - unsigned int i; 753 + unsigned int pmc_idx; 755 754 756 - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); i++) { 755 + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) { 757 756 struct pmc *pmc; 758 757 759 - pmc = pmcdev->pmcs[i]; 758 + pmc = pmcdev->pmcs[pmc_idx]; 760 759 if (!pmc) 761 760 continue; 762 761 ··· 795 794 static int pmc_core_substate_sts_regs_show(struct seq_file *s, void *unused) 796 795 { 797 796 struct pmc_dev *pmcdev = s->private; 798 - unsigned int i; 797 + unsigned int pmc_idx; 799 798 800 - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { 801 - struct pmc *pmc = pmcdev->pmcs[i]; 799 + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { 800 + struct pmc *pmc = pmcdev->pmcs[pmc_idx]; 802 801 const struct pmc_bit_map **maps; 803 802 u32 offset; 804 803 ··· 806 805 continue; 807 806 maps = pmc->map->lpm_sts; 808 807 offset = pmc->map->lpm_status_offset; 809 - pmc_core_lpm_display(pmc, NULL, s, offset, i, "STATUS", maps); 808 + pmc_core_lpm_display(pmc, NULL, s, offset, pmc_idx, "STATUS", maps); 810 809 } 811 810 812 811 return 0; ··· 816 815 static int pmc_core_substate_l_sts_regs_show(struct seq_file *s, void *unused) 817 816 { 818 817 struct pmc_dev *pmcdev = s->private; 819 - unsigned int i; 818 + unsigned int pmc_idx; 820 819 821 - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { 822 - struct pmc *pmc = pmcdev->pmcs[i]; 820 + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { 821 + struct pmc *pmc = pmcdev->pmcs[pmc_idx]; 823 822 const struct pmc_bit_map **maps; 824 823 u32 offset; 825 824 ··· 827 826 continue; 828 827 maps = pmc->map->lpm_sts; 829 828 offset = pmc->map->lpm_live_status_offset; 830 - pmc_core_lpm_display(pmc, NULL, s, offset, i, "LIVE_STATUS", maps); 829 + pmc_core_lpm_display(pmc, NULL, s, offset, pmc_idx, "LIVE_STATUS", maps); 831 830 } 832 831 833 832 return 0; ··· 920 919 u32 sts_offset; 921 920 u32 sts_offset_live; 922 921 u32 *lpm_req_regs; 923 - unsigned int mp, pmc_index; 922 + unsigned int mp, pmc_idx; 924 923 int num_maps; 925 924 926 - for (pmc_index = 0; pmc_index < ARRAY_SIZE(pmcdev->pmcs); ++pmc_index) { 927 - struct pmc *pmc = pmcdev->pmcs[pmc_index]; 925 + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { 926 + struct pmc *pmc = pmcdev->pmcs[pmc_idx]; 928 927 const struct pmc_bit_map **maps; 929 928 930 929 if (!pmc) ··· 945 944 continue; 946 945 947 946 /* Display the header */ 948 - pmc_core_substate_req_header_show(s, pmc_index, HEADER_STATUS); 947 + pmc_core_substate_req_header_show(s, pmc_idx, HEADER_STATUS); 949 948 950 949 /* Loop over maps */ 951 950 for (mp = 0; mp < num_maps; mp++) { ··· 983 982 } 984 983 985 984 /* Display the element name in the first column */ 986 - seq_printf(s, "pmc%d: %34s |", pmc_index, map[i].name); 985 + seq_printf(s, "pmc%d: %34s |", pmc_idx, map[i].name); 987 986 988 987 /* Loop over the enabled states and display if required */ 989 988 pmc_for_each_mode(mode, pmcdev) { ··· 1282 1281 return 0; 1283 1282 } 1284 1283 1285 - void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid) 1284 + static struct telem_endpoint *pmc_core_register_endpoint(struct pci_dev *pcidev, u32 *guids) 1285 + { 1286 + struct telem_endpoint *ep; 1287 + unsigned int i; 1288 + 1289 + for (i = 0; guids[i]; i++) { 1290 + ep = pmt_telem_find_and_register_endpoint(pcidev, guids[i], 0); 1291 + if (!IS_ERR(ep)) 1292 + return ep; 1293 + } 1294 + return ERR_PTR(-ENODEV); 1295 + } 1296 + 1297 + void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids) 1286 1298 { 1287 1299 struct telem_endpoint *ep; 1288 1300 struct pci_dev *pcidev; ··· 1306 1292 return; 1307 1293 } 1308 1294 1309 - ep = pmt_telem_find_and_register_endpoint(pcidev, guid, 0); 1295 + ep = pmc_core_register_endpoint(pcidev, guids); 1310 1296 pci_dev_put(pcidev); 1311 1297 if (IS_ERR(ep)) { 1312 1298 dev_err(&pmcdev->pdev->dev, ··· 1316 1302 } 1317 1303 1318 1304 pmcdev->punit_ep = ep; 1319 - 1320 - pmcdev->has_die_c6 = true; 1321 1305 pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET; 1322 1306 } 1323 1307 ··· 1435 1423 pmcdev->dbgfs_dir, primary_pmc, &pmc_core_pson_residency); 1436 1424 } 1437 1425 1438 - if (pmcdev->has_die_c6) { 1426 + if (pmcdev->punit_ep) { 1439 1427 debugfs_create_file("die_c6_us_show", 0444, 1440 1428 pmcdev->dbgfs_dir, pmcdev, 1441 1429 &pmc_core_die_c6_us_fops); 1442 1430 } 1443 - } 1444 - 1445 - static u32 pmc_core_find_guid(struct pmc_info *list, const struct pmc_reg_map *map) 1446 - { 1447 - for (; list->map; ++list) 1448 - if (list->map == map) 1449 - return list->guid; 1450 - 1451 - return 0; 1452 1431 } 1453 1432 1454 1433 /* ··· 1556 1553 { 1557 1554 struct pci_dev *pcidev __free(pci_dev_put) = NULL; 1558 1555 struct telem_endpoint *ep; 1559 - unsigned int i; 1560 - u32 guid; 1556 + unsigned int pmc_idx; 1561 1557 int ret; 1562 1558 1563 1559 pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, pmc_dev_info->pci_func)); 1564 1560 if (!pcidev) 1565 1561 return -ENODEV; 1566 1562 1567 - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { 1563 + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { 1568 1564 struct pmc *pmc; 1569 1565 1570 - pmc = pmcdev->pmcs[i]; 1566 + pmc = pmcdev->pmcs[pmc_idx]; 1571 1567 if (!pmc) 1572 1568 continue; 1573 1569 1574 - guid = pmc_core_find_guid(pmcdev->regmap_list, pmc->map); 1575 - if (!guid) 1570 + if (!pmc->map->lpm_req_guid) 1576 1571 return -ENXIO; 1577 1572 1578 - ep = pmt_telem_find_and_register_endpoint(pcidev, guid, 0); 1573 + ep = pmt_telem_find_and_register_endpoint(pcidev, pmc->map->lpm_req_guid, 0); 1579 1574 if (IS_ERR(ep)) { 1580 1575 dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %pe", ep); 1581 1576 return -EPROBE_DEFER; ··· 1597 1596 return NULL; 1598 1597 } 1599 1598 1600 - static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index) 1599 + static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_idx) 1601 1600 1602 1601 { 1603 1602 struct pmc_ssram_telemetry pmc_ssram_telemetry; ··· 1605 1604 struct pmc *pmc; 1606 1605 int ret; 1607 1606 1608 - ret = pmc_ssram_telemetry_get_pmc_info(pmc_index, &pmc_ssram_telemetry); 1607 + ret = pmc_ssram_telemetry_get_pmc_info(pmc_idx, &pmc_ssram_telemetry); 1609 1608 if (ret) 1610 1609 return ret; 1611 1610 ··· 1613 1612 if (!map) 1614 1613 return -ENODEV; 1615 1614 1616 - pmc = pmcdev->pmcs[pmc_index]; 1615 + pmc = pmcdev->pmcs[pmc_idx]; 1617 1616 /* Memory for primary PMC has been allocated */ 1618 1617 if (!pmc) { 1619 1618 pmc = devm_kzalloc(&pmcdev->pdev->dev, sizeof(*pmc), GFP_KERNEL); ··· 1630 1629 return -ENOMEM; 1631 1630 } 1632 1631 1633 - pmcdev->pmcs[pmc_index] = pmc; 1632 + pmcdev->pmcs[pmc_idx] = pmc; 1634 1633 1635 1634 return 0; 1636 1635 } ··· 1690 1689 } 1691 1690 1692 1691 pmc_core_get_low_power_modes(pmcdev); 1693 - if (pmc_dev_info->dmu_guid) 1694 - pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guid); 1692 + if (pmc_dev_info->dmu_guids) 1693 + pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guids); 1695 1694 1696 1695 if (ssram) { 1697 1696 ret = pmc_core_get_telem_info(pmcdev, pmc_dev_info); ··· 1702 1701 return 0; 1703 1702 1704 1703 unmap_regbase: 1705 - for (unsigned int i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { 1706 - struct pmc *pmc = pmcdev->pmcs[i]; 1704 + for (unsigned int pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { 1705 + struct pmc *pmc = pmcdev->pmcs[pmc_idx]; 1707 1706 1708 1707 if (pmc && pmc->regbase) 1709 1708 iounmap(pmc->regbase); ··· 1796 1795 static void pmc_core_clean_structure(struct platform_device *pdev) 1797 1796 { 1798 1797 struct pmc_dev *pmcdev = platform_get_drvdata(pdev); 1799 - unsigned int i; 1798 + unsigned int pmc_idx; 1800 1799 1801 - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { 1802 - struct pmc *pmc = pmcdev->pmcs[i]; 1800 + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { 1801 + struct pmc *pmc = pmcdev->pmcs[pmc_idx]; 1803 1802 1804 1803 if (pmc && pmc->regbase) 1805 1804 iounmap(pmc->regbase); ··· 1959 1958 struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; 1960 1959 const struct pmc_bit_map **maps = pmc->map->lpm_sts; 1961 1960 int offset = pmc->map->lpm_status_offset; 1962 - unsigned int i; 1961 + unsigned int pmc_idx, i; 1963 1962 1964 1963 /* Check if the syspend used S0ix */ 1965 1964 if (pm_suspend_via_firmware()) ··· 1997 1996 if (pmc->map->slps0_dbg_maps) 1998 1997 pmc_core_slps0_display(pmc, dev, NULL); 1999 1998 2000 - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { 2001 - struct pmc *pmc = pmcdev->pmcs[i]; 1999 + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { 2000 + struct pmc *pmc = pmcdev->pmcs[pmc_idx]; 2002 2001 2003 2002 if (!pmc) 2004 2003 continue; 2005 2004 if (pmc->map->lpm_sts) 2006 - pmc_core_lpm_display(pmc, dev, NULL, offset, i, "STATUS", maps); 2005 + pmc_core_lpm_display(pmc, dev, NULL, offset, pmc_idx, "STATUS", maps); 2007 2006 } 2008 2007 2009 2008 return 0;
+10 -6
drivers/platform/x86/intel/pmc/core.h
··· 282 282 /* Die C6 from PUNIT telemetry */ 283 283 #define MTL_PMT_DMU_DIE_C6_OFFSET 15 284 284 #define MTL_PMT_DMU_GUID 0x1A067102 285 - #define ARL_PMT_DMU_GUID 0x1A06A000 285 + #define ARL_PMT_DMU_GUID 0x1A06A102 286 + #define ARL_H_PMT_DMU_GUID 0x1A06A101 286 287 287 288 #define LNL_PMC_MMIO_REG_LEN 0x2708 288 289 #define LNL_PMC_LTR_OSSE 0x1B88 ··· 304 303 /* Wildcat Lake */ 305 304 #define WCL_PMC_LTR_RESERVED 0x1B64 306 305 #define WCL_PCD_PMC_MMIO_REG_LEN 0x3178 306 + #define WCL_NUM_S0IX_BLOCKER 94 307 + #define WCL_BLK_REQ_OFFSET 50 307 308 308 309 /* SSRAM PMC Device ID */ 309 310 /* LNL */ ··· 358 355 * @s0ix_blocker_offset PWRMBASE offset to S0ix blocker counter 359 356 * @num_s0ix_blocker: Number of S0ix blockers 360 357 * @blocker_req_offset: Telemetry offset to S0ix blocker low power mode substate requirement table 358 + * @lpm_req_guid: Telemetry GUID to read low power mode substate requirement table 361 359 * 362 360 * Each PCH has unique set of register offsets and bit indexes. This structure 363 361 * captures them to have a common implementation. ··· 400 396 const u8 *lpm_reg_index; 401 397 const u32 pson_residency_offset; 402 398 const u32 pson_residency_counter_step; 399 + /* GUID for telemetry regions */ 400 + const u32 lpm_req_guid; 403 401 }; 404 402 405 403 /** ··· 411 405 * specific attributes 412 406 */ 413 407 struct pmc_info { 414 - u32 guid; 415 408 u16 devid; 416 409 const struct pmc_reg_map *map; 417 410 }; ··· 470 465 u64 *pkgc_res_cnt; 471 466 u8 num_of_pkgc; 472 467 473 - bool has_die_c6; 474 468 u32 die_c6_offset; 475 469 struct telem_endpoint *punit_ep; 476 470 struct pmc_info *regmap_list; ··· 485 481 /** 486 482 * struct pmc_dev_info - Structure to keep PMC device info 487 483 * @pci_func: Function number of the primary PMC 488 - * @dmu_guid: Die Management Unit GUID 484 + * @dmu_guids: List of Die Management Unit GUID 489 485 * @regmap_list: Pointer to a list of pmc_info structure that could be 490 486 * available for the platform. When set, this field implies 491 487 * SSRAM support. ··· 499 495 */ 500 496 struct pmc_dev_info { 501 497 u8 pci_func; 502 - u32 dmu_guid; 498 + u32 *dmu_guids; 503 499 struct pmc_info *regmap_list; 504 500 const struct pmc_reg_map *map; 505 501 const struct file_operations *sub_req_show; ··· 536 532 int pmc_core_resume_common(struct pmc_dev *pmcdev); 537 533 int get_primary_reg_base(struct pmc *pmc); 538 534 void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev); 539 - void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid); 535 + void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids); 540 536 void pmc_core_set_device_d3(unsigned int device); 541 537 542 538 int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info);
+1 -1
drivers/platform/x86/intel/pmc/lnl.c
··· 533 533 .s0ix_blocker_maps = lnl_blk_maps, 534 534 .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, 535 535 .lpm_reg_index = LNL_LPM_REG_INDEX, 536 + .lpm_req_guid = SOCM_LPM_REQ_GUID, 536 537 }; 537 538 538 539 static struct pmc_info lnl_pmc_info_list[] = { 539 540 { 540 - .guid = SOCM_LPM_REQ_GUID, 541 541 .devid = PMC_DEVID_LNL_SOCM, 542 542 .map = &lnl_socm_reg_map, 543 543 },
+5 -4
drivers/platform/x86/intel/pmc/mtl.c
··· 473 473 .lpm_status_offset = MTL_LPM_STATUS_OFFSET, 474 474 .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, 475 475 .lpm_reg_index = MTL_LPM_REG_INDEX, 476 + .lpm_req_guid = SOCP_LPM_REQ_GUID, 476 477 }; 477 478 478 479 static const struct pmc_bit_map mtl_ioep_pfear_map[] = { ··· 798 797 .lpm_en_offset = MTL_LPM_EN_OFFSET, 799 798 .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, 800 799 .lpm_reg_index = MTL_LPM_REG_INDEX, 800 + .lpm_req_guid = IOEP_LPM_REQ_GUID, 801 801 }; 802 802 803 803 static const struct pmc_bit_map mtl_ioem_pfear_map[] = { ··· 946 944 .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, 947 945 .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, 948 946 .lpm_reg_index = MTL_LPM_REG_INDEX, 947 + .lpm_req_guid = IOEM_LPM_REQ_GUID, 949 948 }; 950 949 951 950 static struct pmc_info mtl_pmc_info_list[] = { 952 951 { 953 - .guid = SOCP_LPM_REQ_GUID, 954 952 .devid = PMC_DEVID_MTL_SOCM, 955 953 .map = &mtl_socm_reg_map, 956 954 }, 957 955 { 958 - .guid = IOEP_LPM_REQ_GUID, 959 956 .devid = PMC_DEVID_MTL_IOEP, 960 957 .map = &mtl_ioep_reg_map, 961 958 }, 962 959 { 963 - .guid = IOEM_LPM_REQ_GUID, 964 960 .devid = PMC_DEVID_MTL_IOEM, 965 961 .map = &mtl_ioem_reg_map 966 962 }, ··· 992 992 return generic_core_init(pmcdev, pmc_dev_info); 993 993 } 994 994 995 + static u32 MTL_PMT_DMU_GUIDS[] = {MTL_PMT_DMU_GUID, 0x0}; 995 996 struct pmc_dev_info mtl_pmc_dev = { 996 997 .pci_func = 2, 997 - .dmu_guid = MTL_PMT_DMU_GUID, 998 + .dmu_guids = MTL_PMT_DMU_GUIDS, 998 999 .regmap_list = mtl_pmc_info_list, 999 1000 .map = &mtl_socm_reg_map, 1000 1001 .sub_req_show = &pmc_core_substate_req_regs_fops,
+1 -2
drivers/platform/x86/intel/pmc/ptl.c
··· 528 528 .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, 529 529 .num_s0ix_blocker = PTL_NUM_S0IX_BLOCKER, 530 530 .blocker_req_offset = PTL_BLK_REQ_OFFSET, 531 + .lpm_req_guid = PCDP_LPM_REQ_GUID, 531 532 }; 532 533 533 534 static struct pmc_info ptl_pmc_info_list[] = { 534 535 { 535 - .guid = PCDP_LPM_REQ_GUID, 536 536 .devid = PMC_DEVID_PTL_PCDH, 537 537 .map = &ptl_pcdp_reg_map, 538 538 }, 539 539 { 540 - .guid = PCDP_LPM_REQ_GUID, 541 540 .devid = PMC_DEVID_PTL_PCDP, 542 541 .map = &ptl_pcdp_reg_map, 543 542 },
+18
drivers/platform/x86/intel/pmc/wcl.c
··· 11 11 12 12 #include "core.h" 13 13 14 + /* PMC SSRAM PMT Telemetry GUIDS */ 15 + #define PCDN_LPM_REQ_GUID 0x33747648 16 + 14 17 static const struct pmc_bit_map wcl_pcdn_pfear_map[] = { 15 18 {"PMC_0", BIT(0)}, 16 19 {"FUSE_OSSE", BIT(1)}, ··· 456 453 .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, 457 454 .s0ix_blocker_maps = wcl_pcdn_blk_maps, 458 455 .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, 456 + .num_s0ix_blocker = WCL_NUM_S0IX_BLOCKER, 457 + .blocker_req_offset = WCL_BLK_REQ_OFFSET, 458 + .lpm_req_guid = PCDN_LPM_REQ_GUID, 459 + }; 460 + 461 + static struct pmc_info wcl_pmc_info_list[] = { 462 + { 463 + .devid = PMC_DEVID_WCL_PCDN, 464 + .map = &wcl_pcdn_reg_map, 465 + }, 466 + {} 459 467 }; 460 468 461 469 #define WCL_NPU_PCI_DEV 0xfd3e ··· 493 479 } 494 480 495 481 struct pmc_dev_info wcl_pmc_dev = { 482 + .pci_func = 2, 483 + .regmap_list = wcl_pmc_info_list, 496 484 .map = &wcl_pcdn_reg_map, 485 + .sub_req_show = &pmc_core_substate_blk_req_fops, 497 486 .suspend = cnl_suspend, 498 487 .resume = wcl_resume, 499 488 .init = wcl_core_init, 489 + .sub_req = pmc_core_pmt_get_blk_sub_req, 500 490 };
+2
drivers/platform/x86/intel/vsec.c
··· 765 765 #define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d 766 766 #define PCI_DEVICE_ID_INTEL_VSEC_LNL_M 0x647d 767 767 #define PCI_DEVICE_ID_INTEL_VSEC_PTL 0xb07d 768 + #define PCI_DEVICE_ID_INTEL_VSEC_WCL 0xfd7d 768 769 static const struct pci_device_id intel_vsec_pci_ids[] = { 769 770 { PCI_DEVICE_DATA(INTEL, VSEC_ADL, &tgl_info) }, 770 771 { PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) }, ··· 777 776 { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, 778 777 { PCI_DEVICE_DATA(INTEL, VSEC_LNL_M, &lnl_info) }, 779 778 { PCI_DEVICE_DATA(INTEL, VSEC_PTL, &mtl_info) }, 779 + { PCI_DEVICE_DATA(INTEL, VSEC_WCL, &mtl_info) }, 780 780 { } 781 781 }; 782 782 MODULE_DEVICE_TABLE(pci, intel_vsec_pci_ids);
+160 -54
drivers/platform/x86/lenovo/ideapad-laptop.c
··· 31 31 #include <linux/power_supply.h> 32 32 #include <linux/rfkill.h> 33 33 #include <linux/seq_file.h> 34 + #include <linux/string_choices.h> 34 35 #include <linux/sysfs.h> 35 36 #include <linux/types.h> 36 37 #include <linux/wmi.h> ··· 63 62 CFG_OSD_CAM_BIT = 31, 64 63 }; 65 64 65 + /* 66 + * There are two charge modes supported by the GBMD/SBMC interface: 67 + * - "Rapid Charge": increase power to speed up charging 68 + * - "Conservation Mode": stop charging at 60-80% (depends on model) 69 + * 70 + * The interface doesn't prohibit enabling both modes at the same time. 71 + * However, doing so is essentially meaningless, and the manufacturer utilities 72 + * on Windows always make them mutually exclusive. 73 + */ 74 + 66 75 enum { 76 + GBMD_RAPID_CHARGE_STATE_BIT = 2, 67 77 GBMD_CONSERVATION_STATE_BIT = 5, 78 + GBMD_RAPID_CHARGE_SUPPORTED_BIT = 17, 68 79 }; 69 80 70 81 enum { 71 82 SBMC_CONSERVATION_ON = 3, 72 83 SBMC_CONSERVATION_OFF = 5, 84 + SBMC_RAPID_CHARGE_ON = 7, 85 + SBMC_RAPID_CHARGE_OFF = 8, 73 86 }; 74 87 75 88 enum { ··· 173 158 struct ideapad_private { 174 159 struct acpi_device *adev; 175 160 struct mutex vpc_mutex; /* protects the VPC calls */ 161 + struct mutex gbmd_sbmc_mutex; /* protects GBMD/SBMC calls */ 176 162 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; 177 163 struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM]; 178 164 struct platform_device *platform_device; ··· 182 166 struct ideapad_dytc_priv *dytc; 183 167 struct dentry *debug; 184 168 struct acpi_battery_hook battery_hook; 169 + const struct power_supply_ext *battery_ext; 185 170 unsigned long cfg; 186 171 unsigned long r_touchpad_val; 187 172 struct { 173 + bool rapid_charge : 1; 188 174 bool conservation_mode : 1; 189 175 bool dytc : 1; 190 176 bool fan_mode : 1; ··· 473 455 struct ideapad_private *priv = s->private; 474 456 unsigned long value; 475 457 476 - guard(mutex)(&priv->vpc_mutex); 458 + scoped_guard(mutex, &priv->vpc_mutex) { 459 + if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value)) 460 + seq_printf(s, "Backlight max: %lu\n", value); 461 + if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value)) 462 + seq_printf(s, "Backlight now: %lu\n", value); 463 + if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value)) 464 + seq_printf(s, "BL power value: %s (%lu)\n", str_on_off(value), value); 477 465 478 - if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value)) 479 - seq_printf(s, "Backlight max: %lu\n", value); 480 - if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value)) 481 - seq_printf(s, "Backlight now: %lu\n", value); 482 - if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value)) 483 - seq_printf(s, "BL power value: %s (%lu)\n", value ? "on" : "off", value); 466 + seq_puts(s, "=====================\n"); 467 + 468 + if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value)) 469 + seq_printf(s, "Radio status: %s (%lu)\n", str_on_off(value), value); 470 + if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value)) 471 + seq_printf(s, "Wifi status: %s (%lu)\n", str_on_off(value), value); 472 + if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value)) 473 + seq_printf(s, "BT status: %s (%lu)\n", str_on_off(value), value); 474 + if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value)) 475 + seq_printf(s, "3G status: %s (%lu)\n", str_on_off(value), value); 476 + 477 + seq_puts(s, "=====================\n"); 478 + 479 + if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) 480 + seq_printf(s, "Touchpad status: %s (%lu)\n", str_on_off(value), value); 481 + if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value)) 482 + seq_printf(s, "Camera status: %s (%lu)\n", str_on_off(value), value); 483 + } 484 484 485 485 seq_puts(s, "=====================\n"); 486 486 487 - if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value)) 488 - seq_printf(s, "Radio status: %s (%lu)\n", value ? "on" : "off", value); 489 - if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value)) 490 - seq_printf(s, "Wifi status: %s (%lu)\n", value ? "on" : "off", value); 491 - if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value)) 492 - seq_printf(s, "BT status: %s (%lu)\n", value ? "on" : "off", value); 493 - if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value)) 494 - seq_printf(s, "3G status: %s (%lu)\n", value ? "on" : "off", value); 487 + scoped_guard(mutex, &priv->gbmd_sbmc_mutex) { 488 + if (!eval_gbmd(priv->adev->handle, &value)) 489 + seq_printf(s, "GBMD: %#010lx\n", value); 490 + } 495 491 496 - seq_puts(s, "=====================\n"); 497 - 498 - if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) 499 - seq_printf(s, "Touchpad status: %s (%lu)\n", value ? "on" : "off", value); 500 - if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value)) 501 - seq_printf(s, "Camera status: %s (%lu)\n", value ? "on" : "off", value); 502 - 503 - seq_puts(s, "=====================\n"); 504 - 505 - if (!eval_gbmd(priv->adev->handle, &value)) 506 - seq_printf(s, "GBMD: %#010lx\n", value); 507 492 if (!eval_hals(priv->adev->handle, &value)) 508 493 seq_printf(s, "HALS: %#010lx\n", value); 509 494 ··· 643 622 644 623 show_conservation_mode_deprecation_warning(dev); 645 624 646 - err = eval_gbmd(priv->adev->handle, &result); 647 - if (err) 648 - return err; 625 + scoped_guard(mutex, &priv->gbmd_sbmc_mutex) { 626 + err = eval_gbmd(priv->adev->handle, &result); 627 + if (err) 628 + return err; 629 + } 649 630 631 + /* 632 + * For backward compatibility, ignore Rapid Charge while reporting the 633 + * state of Conservation Mode. 634 + */ 650 635 return sysfs_emit(buf, "%d\n", !!test_bit(GBMD_CONSERVATION_STATE_BIT, &result)); 651 636 } 652 637 ··· 669 642 err = kstrtobool(buf, &state); 670 643 if (err) 671 644 return err; 645 + 646 + guard(mutex)(&priv->gbmd_sbmc_mutex); 647 + 648 + /* 649 + * Prevent mutually exclusive modes from being set at the same time, 650 + * but do not disable Rapid Charge while disabling Conservation Mode. 651 + */ 652 + if (priv->features.rapid_charge && state) { 653 + err = exec_sbmc(priv->adev->handle, SBMC_RAPID_CHARGE_OFF); 654 + if (err) 655 + return err; 656 + } 672 657 673 658 err = exec_sbmc(priv->adev->handle, state ? SBMC_CONSERVATION_ON : SBMC_CONSERVATION_OFF); 674 659 if (err) ··· 2046 2007 const union power_supply_propval *val) 2047 2008 { 2048 2009 struct ideapad_private *priv = ext_data; 2010 + unsigned long op1, op2; 2011 + int err; 2049 2012 2050 2013 switch (val->intval) { 2014 + case POWER_SUPPLY_CHARGE_TYPE_FAST: 2015 + if (WARN_ON(!priv->features.rapid_charge)) 2016 + return -EINVAL; 2017 + 2018 + op1 = SBMC_CONSERVATION_OFF; 2019 + op2 = SBMC_RAPID_CHARGE_ON; 2020 + break; 2051 2021 case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE: 2052 - return exec_sbmc(priv->adev->handle, SBMC_CONSERVATION_ON); 2022 + op1 = SBMC_RAPID_CHARGE_OFF; 2023 + op2 = SBMC_CONSERVATION_ON; 2024 + break; 2053 2025 case POWER_SUPPLY_CHARGE_TYPE_STANDARD: 2054 - return exec_sbmc(priv->adev->handle, SBMC_CONSERVATION_OFF); 2026 + op1 = SBMC_RAPID_CHARGE_OFF; 2027 + op2 = SBMC_CONSERVATION_OFF; 2028 + break; 2055 2029 default: 2056 2030 return -EINVAL; 2057 2031 } 2032 + 2033 + guard(mutex)(&priv->gbmd_sbmc_mutex); 2034 + 2035 + /* If !rapid_charge, op1 must be SBMC_RAPID_CHARGE_OFF. Skip it. */ 2036 + if (priv->features.rapid_charge) { 2037 + err = exec_sbmc(priv->adev->handle, op1); 2038 + if (err) 2039 + return err; 2040 + } 2041 + 2042 + return exec_sbmc(priv->adev->handle, op2); 2058 2043 } 2059 2044 2060 2045 static int ideapad_psy_ext_get_prop(struct power_supply *psy, ··· 2088 2025 union power_supply_propval *val) 2089 2026 { 2090 2027 struct ideapad_private *priv = ext_data; 2028 + bool is_rapid_charge, is_conservation; 2091 2029 unsigned long result; 2092 2030 int err; 2093 2031 2094 - err = eval_gbmd(priv->adev->handle, &result); 2095 - if (err) 2096 - return err; 2032 + scoped_guard(mutex, &priv->gbmd_sbmc_mutex) { 2033 + err = eval_gbmd(priv->adev->handle, &result); 2034 + if (err) 2035 + return err; 2036 + } 2097 2037 2098 - if (test_bit(GBMD_CONSERVATION_STATE_BIT, &result)) 2038 + is_rapid_charge = (priv->features.rapid_charge && 2039 + test_bit(GBMD_RAPID_CHARGE_STATE_BIT, &result)); 2040 + is_conservation = test_bit(GBMD_CONSERVATION_STATE_BIT, &result); 2041 + 2042 + if (unlikely(is_rapid_charge && is_conservation)) { 2043 + dev_err(&priv->platform_device->dev, 2044 + "unexpected charge_types: both [Fast] and [Long_Life] are enabled\n"); 2045 + return -EINVAL; 2046 + } 2047 + 2048 + if (is_rapid_charge) 2049 + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; 2050 + else if (is_conservation) 2099 2051 val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE; 2100 2052 else 2101 2053 val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; ··· 2130 2052 POWER_SUPPLY_PROP_CHARGE_TYPES, 2131 2053 }; 2132 2054 2133 - static const struct power_supply_ext ideapad_battery_ext = { 2134 - .name = "ideapad_laptop", 2135 - .properties = ideapad_power_supply_props, 2136 - .num_properties = ARRAY_SIZE(ideapad_power_supply_props), 2137 - .charge_types = (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) | 2138 - BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)), 2139 - .get_property = ideapad_psy_ext_get_prop, 2140 - .set_property = ideapad_psy_ext_set_prop, 2141 - .property_is_writeable = ideapad_psy_prop_is_writeable, 2142 - }; 2055 + #define DEFINE_IDEAPAD_POWER_SUPPLY_EXTENSION(_name, _charge_types) \ 2056 + static const struct power_supply_ext _name = { \ 2057 + .name = "ideapad_laptop", \ 2058 + .properties = ideapad_power_supply_props, \ 2059 + .num_properties = ARRAY_SIZE(ideapad_power_supply_props), \ 2060 + .charge_types = _charge_types, \ 2061 + .get_property = ideapad_psy_ext_get_prop, \ 2062 + .set_property = ideapad_psy_ext_set_prop, \ 2063 + .property_is_writeable = ideapad_psy_prop_is_writeable, \ 2064 + } 2065 + 2066 + DEFINE_IDEAPAD_POWER_SUPPLY_EXTENSION(ideapad_battery_ext_v1, 2067 + (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) | 2068 + BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)) 2069 + ); 2070 + 2071 + DEFINE_IDEAPAD_POWER_SUPPLY_EXTENSION(ideapad_battery_ext_v2, 2072 + (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) | 2073 + BIT(POWER_SUPPLY_CHARGE_TYPE_FAST) | 2074 + BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)) 2075 + ); 2143 2076 2144 2077 static int ideapad_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) 2145 2078 { 2146 2079 struct ideapad_private *priv = container_of(hook, struct ideapad_private, battery_hook); 2147 2080 2148 - return power_supply_register_extension(battery, &ideapad_battery_ext, 2081 + return power_supply_register_extension(battery, priv->battery_ext, 2149 2082 &priv->platform_device->dev, priv); 2150 2083 } 2151 2084 2152 2085 static int ideapad_battery_remove(struct power_supply *battery, 2153 2086 struct acpi_battery_hook *hook) 2154 2087 { 2155 - power_supply_unregister_extension(battery, &ideapad_battery_ext); 2088 + struct ideapad_private *priv = container_of(hook, struct ideapad_private, battery_hook); 2089 + 2090 + power_supply_unregister_extension(battery, priv->battery_ext); 2156 2091 2157 2092 return 0; 2158 2093 } ··· 2190 2099 priv->features.fan_mode = true; 2191 2100 2192 2101 if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) { 2193 - priv->features.conservation_mode = true; 2194 - priv->battery_hook.add_battery = ideapad_battery_add; 2195 - priv->battery_hook.remove_battery = ideapad_battery_remove; 2196 - priv->battery_hook.name = "Ideapad Battery Extension"; 2102 + /* Not acquiring gbmd_sbmc_mutex as race condition is impossible on init */ 2103 + if (!eval_gbmd(handle, &val)) { 2104 + priv->features.conservation_mode = true; 2105 + priv->features.rapid_charge = test_bit(GBMD_RAPID_CHARGE_SUPPORTED_BIT, 2106 + &val); 2197 2107 2198 - err = devm_battery_hook_register(&priv->platform_device->dev, &priv->battery_hook); 2199 - if (err) 2200 - return err; 2108 + priv->battery_ext = priv->features.rapid_charge 2109 + ? &ideapad_battery_ext_v2 2110 + : &ideapad_battery_ext_v1; 2111 + 2112 + priv->battery_hook.add_battery = ideapad_battery_add; 2113 + priv->battery_hook.remove_battery = ideapad_battery_remove; 2114 + priv->battery_hook.name = "Ideapad Battery Extension"; 2115 + 2116 + err = devm_battery_hook_register(&priv->platform_device->dev, 2117 + &priv->battery_hook); 2118 + if (err) 2119 + return err; 2120 + } 2201 2121 } 2202 2122 2203 2123 if (acpi_has_method(handle, "DYTC")) ··· 2391 2289 priv->platform_device = pdev; 2392 2290 2393 2291 err = devm_mutex_init(&pdev->dev, &priv->vpc_mutex); 2292 + if (err) 2293 + return err; 2294 + 2295 + err = devm_mutex_init(&pdev->dev, &priv->gbmd_sbmc_mutex); 2394 2296 if (err) 2395 2297 return err; 2396 2298
+21 -14
drivers/platform/x86/lenovo/wmi-gamezone.c
··· 171 171 *profile = PLATFORM_PROFILE_BALANCED; 172 172 break; 173 173 case LWMI_GZ_THERMAL_MODE_PERFORMANCE: 174 - if (priv->extreme_supported) { 175 - *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; 176 - break; 177 - } 178 174 *profile = PLATFORM_PROFILE_PERFORMANCE; 179 175 break; 180 176 case LWMI_GZ_THERMAL_MODE_EXTREME: 181 - *profile = PLATFORM_PROFILE_PERFORMANCE; 177 + *profile = PLATFORM_PROFILE_MAX_POWER; 182 178 break; 183 179 case LWMI_GZ_THERMAL_MODE_CUSTOM: 184 180 *profile = PLATFORM_PROFILE_CUSTOM; ··· 214 218 case PLATFORM_PROFILE_BALANCED: 215 219 mode = LWMI_GZ_THERMAL_MODE_BALANCED; 216 220 break; 217 - case PLATFORM_PROFILE_BALANCED_PERFORMANCE: 221 + case PLATFORM_PROFILE_PERFORMANCE: 218 222 mode = LWMI_GZ_THERMAL_MODE_PERFORMANCE; 219 223 break; 220 - case PLATFORM_PROFILE_PERFORMANCE: 221 - if (priv->extreme_supported) { 222 - mode = LWMI_GZ_THERMAL_MODE_EXTREME; 223 - break; 224 - } 225 - mode = LWMI_GZ_THERMAL_MODE_PERFORMANCE; 224 + case PLATFORM_PROFILE_MAX_POWER: 225 + mode = LWMI_GZ_THERMAL_MODE_EXTREME; 226 226 break; 227 227 case PLATFORM_PROFILE_CUSTOM: 228 228 mode = LWMI_GZ_THERMAL_MODE_CUSTOM; ··· 266 274 }, 267 275 .driver_data = &quirk_no_extreme_bug, 268 276 }, 277 + { 278 + .ident = "Legion Go 8ASP2", 279 + .matches = { 280 + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 281 + DMI_MATCH(DMI_PRODUCT_VERSION, "Legion Go 8ASP2"), 282 + }, 283 + .driver_data = &quirk_no_extreme_bug, 284 + }, 285 + { 286 + .ident = "Legion Go 8AHP2", 287 + .matches = { 288 + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 289 + DMI_MATCH(DMI_PRODUCT_VERSION, "Legion Go 8AHP2"), 290 + }, 291 + .driver_data = &quirk_no_extreme_bug, 292 + }, 269 293 {}, 270 - 271 294 }; 272 295 273 296 /** ··· 345 338 346 339 priv->extreme_supported = lwmi_gz_extreme_supported(profile_support_ver); 347 340 if (priv->extreme_supported) 348 - set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices); 341 + set_bit(PLATFORM_PROFILE_MAX_POWER, choices); 349 342 350 343 return 0; 351 344 }
+11
drivers/platform/x86/lg-laptop.c
··· 19 19 #include <linux/leds.h> 20 20 #include <linux/module.h> 21 21 #include <linux/platform_device.h> 22 + #include <linux/string_choices.h> 22 23 #include <linux/types.h> 23 24 24 25 #include <acpi/battery.h> ··· 43 42 #define LG_ADDRESS_SPACE_ID 0x8F 44 43 45 44 #define LG_ADDRESS_SPACE_DEBUG_FLAG_ADR 0x00 45 + #define LG_ADDRESS_SPACE_HD_AUDIO_POWER_ADDR 0x01 46 46 #define LG_ADDRESS_SPACE_FAN_MODE_ADR 0x03 47 47 48 48 #define LG_ADDRESS_SPACE_DTTM_FLAG_ADR 0x20 ··· 670 668 byte = value & 0xFF; 671 669 672 670 switch (address) { 671 + case LG_ADDRESS_SPACE_HD_AUDIO_POWER_ADDR: 672 + /* 673 + * The HD audio power field is not affected by the DTTM flag, 674 + * so we have to manually check fw_debug. 675 + */ 676 + if (fw_debug) 677 + dev_dbg(dev, "HD audio power %s\n", str_enabled_disabled(byte)); 678 + 679 + return AE_OK; 673 680 case LG_ADDRESS_SPACE_FAN_MODE_ADR: 674 681 /* 675 682 * The fan mode field is not affected by the DTTM flag, so we
+1 -114
drivers/platform/x86/oxpec.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0+ 2 2 /* 3 - * Platform driver for OneXPlayer and AOKZOE devices. For the time being, 4 - * it also exposes fan controls for AYANEO, and OrangePi Handhelds via 5 - * hwmon sysfs. 3 + * Platform driver for OneXPlayer and AOKZOE devices. 6 4 * 7 5 * Fan control is provided via pwm interface in the range [0-255]. 8 6 * Old AMD boards use [0-100] as range in the EC, the written value is ··· 41 43 42 44 enum oxp_board { 43 45 aok_zoe_a1 = 1, 44 - aya_neo_2, 45 - aya_neo_air, 46 - aya_neo_air_1s, 47 - aya_neo_air_plus_mendo, 48 - aya_neo_air_pro, 49 - aya_neo_flip, 50 - aya_neo_geek, 51 - aya_neo_kun, 52 46 orange_pi_neo, 53 47 oxp_2, 54 48 oxp_fly, ··· 120 130 DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1X"), 121 131 }, 122 132 .driver_data = (void *)oxp_fly, 123 - }, 124 - { 125 - .matches = { 126 - DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 127 - DMI_MATCH(DMI_BOARD_NAME, "AYANEO 2"), 128 - }, 129 - .driver_data = (void *)aya_neo_2, 130 - }, 131 - { 132 - .matches = { 133 - DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 134 - DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"), 135 - }, 136 - .driver_data = (void *)aya_neo_air, 137 - }, 138 - { 139 - .matches = { 140 - DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 141 - DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR 1S"), 142 - }, 143 - .driver_data = (void *)aya_neo_air_1s, 144 - }, 145 - { 146 - .matches = { 147 - DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 148 - DMI_EXACT_MATCH(DMI_BOARD_NAME, "AB05-Mendocino"), 149 - }, 150 - .driver_data = (void *)aya_neo_air_plus_mendo, 151 - }, 152 - { 153 - .matches = { 154 - DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 155 - DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"), 156 - }, 157 - .driver_data = (void *)aya_neo_air_pro, 158 - }, 159 - { 160 - .matches = { 161 - DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 162 - DMI_MATCH(DMI_BOARD_NAME, "FLIP"), 163 - }, 164 - .driver_data = (void *)aya_neo_flip, 165 - }, 166 - { 167 - .matches = { 168 - DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 169 - DMI_MATCH(DMI_BOARD_NAME, "GEEK"), 170 - }, 171 - .driver_data = (void *)aya_neo_geek, 172 - }, 173 - { 174 - .matches = { 175 - DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), 176 - DMI_EXACT_MATCH(DMI_BOARD_NAME, "KUN"), 177 - }, 178 - .driver_data = (void *)aya_neo_kun, 179 133 }, 180 134 { 181 135 .matches = { ··· 606 672 case orange_pi_neo: 607 673 return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL); 608 674 case aok_zoe_a1: 609 - case aya_neo_2: 610 - case aya_neo_air: 611 - case aya_neo_air_plus_mendo: 612 - case aya_neo_air_pro: 613 - case aya_neo_flip: 614 - case aya_neo_geek: 615 - case aya_neo_kun: 616 675 case oxp_2: 617 676 case oxp_fly: 618 677 case oxp_mini_amd: ··· 626 699 case orange_pi_neo: 627 700 return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO); 628 701 case aok_zoe_a1: 629 - case aya_neo_2: 630 - case aya_neo_air: 631 - case aya_neo_air_1s: 632 - case aya_neo_air_plus_mendo: 633 - case aya_neo_air_pro: 634 - case aya_neo_flip: 635 - case aya_neo_geek: 636 - case aya_neo_kun: 637 702 case oxp_2: 638 703 case oxp_fly: 639 704 case oxp_mini_amd: ··· 646 727 case orange_pi_neo: 647 728 return read_from_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, 1, val); 648 729 case aok_zoe_a1: 649 - case aya_neo_2: 650 - case aya_neo_air: 651 - case aya_neo_air_1s: 652 - case aya_neo_air_plus_mendo: 653 - case aya_neo_air_pro: 654 - case aya_neo_flip: 655 - case aya_neo_geek: 656 - case aya_neo_kun: 657 730 case oxp_2: 658 731 case oxp_fly: 659 732 case oxp_mini_amd: ··· 685 774 case oxp_g1_i: 686 775 return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val); 687 776 case aok_zoe_a1: 688 - case aya_neo_2: 689 - case aya_neo_air: 690 - case aya_neo_air_1s: 691 - case aya_neo_air_plus_mendo: 692 - case aya_neo_air_pro: 693 - case aya_neo_flip: 694 - case aya_neo_geek: 695 - case aya_neo_kun: 696 777 case oxp_fly: 697 778 case oxp_mini_amd: 698 779 case oxp_mini_amd_a07: ··· 713 810 /* scale to range [0-184] */ 714 811 val = (val * 184) / 255; 715 812 return write_to_ec(OXP_SENSOR_PWM_REG, val); 716 - case aya_neo_2: 717 - case aya_neo_air: 718 - case aya_neo_air_1s: 719 - case aya_neo_air_plus_mendo: 720 - case aya_neo_air_pro: 721 - case aya_neo_flip: 722 - case aya_neo_geek: 723 - case aya_neo_kun: 724 813 case oxp_mini_amd: 725 814 case oxp_mini_amd_a07: 726 815 /* scale to range [0-100] */ ··· 749 854 /* scale from range [0-184] */ 750 855 *val = (*val * 255) / 184; 751 856 break; 752 - case aya_neo_2: 753 - case aya_neo_air: 754 - case aya_neo_air_1s: 755 - case aya_neo_air_plus_mendo: 756 - case aya_neo_air_pro: 757 - case aya_neo_flip: 758 - case aya_neo_geek: 759 - case aya_neo_kun: 760 857 case oxp_mini_amd: 761 858 case oxp_mini_amd_a07: 762 859 ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
+9 -4
drivers/platform/x86/serial-multi-instantiate.c
··· 22 22 #define IRQ_RESOURCE_GPIO 1 23 23 #define IRQ_RESOURCE_APIC 2 24 24 #define IRQ_RESOURCE_AUTO 3 25 + #define IRQ_RESOURCE_OPT BIT(2) 25 26 26 27 enum smi_bus_type { 27 28 SMI_I2C, ··· 64 63 if (ret > 0) { 65 64 dev_dbg(&pdev->dev, "Using platform irq\n"); 66 65 break; 66 + } 67 + if (inst->flags & IRQ_RESOURCE_OPT) { 68 + dev_dbg(&pdev->dev, "No irq\n"); 69 + return 0; 67 70 } 68 71 break; 69 72 case IRQ_RESOURCE_GPIO: ··· 391 386 392 387 static const struct smi_node tas2781_hda = { 393 388 .instances = { 394 - { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 }, 395 - { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 }, 396 - { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 }, 397 - { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 }, 389 + { "tas2781-hda", IRQ_RESOURCE_AUTO | IRQ_RESOURCE_OPT, 0 }, 390 + { "tas2781-hda", IRQ_RESOURCE_AUTO | IRQ_RESOURCE_OPT, 0 }, 391 + { "tas2781-hda", IRQ_RESOURCE_AUTO | IRQ_RESOURCE_OPT, 0 }, 392 + { "tas2781-hda", IRQ_RESOURCE_AUTO | IRQ_RESOURCE_OPT, 0 }, 398 393 {} 399 394 }, 400 395 .bus_type = SMI_AUTO_DETECT,
+38
drivers/platform/x86/uniwill/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-or-later 2 + # 3 + # Uniwill X86 Platform Specific Drivers 4 + # 5 + 6 + menuconfig X86_PLATFORM_DRIVERS_UNIWILL 7 + bool "Uniwill X86 Platform Specific Device Drivers" 8 + depends on X86_PLATFORM_DEVICES 9 + help 10 + Say Y here to see options for device drivers for various 11 + Uniwill x86 platforms, including many OEM laptops originally 12 + manufactured by Uniwill. 13 + This option alone does not add any kernel code. 14 + 15 + If you say N, all options in this submenu will be skipped and disabled. 16 + 17 + if X86_PLATFORM_DRIVERS_UNIWILL 18 + 19 + config UNIWILL_LAPTOP 20 + tristate "Uniwill Laptop Extras" 21 + default m 22 + depends on ACPI 23 + depends on ACPI_WMI 24 + depends on ACPI_BATTERY 25 + depends on HWMON 26 + depends on INPUT 27 + depends on LEDS_CLASS_MULTICOLOR 28 + depends on DMI 29 + select REGMAP 30 + select INPUT_SPARSEKMAP 31 + help 32 + This driver adds support for various extra features found on Uniwill laptops, 33 + like the lightbar, hwmon sensors and hotkeys. It also supports many OEM laptops 34 + originally manufactured by Uniwill. 35 + 36 + If you have such a laptop, say Y or M here. 37 + 38 + endif
+8
drivers/platform/x86/uniwill/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-or-later 2 + # 3 + # Makefile for linux/drivers/platform/x86/uniwill 4 + # Uniwill X86 Platform Specific Drivers 5 + # 6 + 7 + obj-$(CONFIG_UNIWILL_LAPTOP) += uniwill-laptop.o 8 + uniwill-laptop-y := uniwill-acpi.o uniwill-wmi.o
+1912
drivers/platform/x86/uniwill/uniwill-acpi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Linux driver for Uniwill notebooks. 4 + * 5 + * Special thanks go to Pőcze Barnabás, Christoffer Sandberg and Werner Sembach 6 + * for supporting the development of this driver either through prior work or 7 + * by answering questions regarding the underlying ACPI and WMI interfaces. 8 + * 9 + * Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de> 10 + */ 11 + 12 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13 + 14 + #include <linux/acpi.h> 15 + #include <linux/array_size.h> 16 + #include <linux/bits.h> 17 + #include <linux/bitfield.h> 18 + #include <linux/cleanup.h> 19 + #include <linux/debugfs.h> 20 + #include <linux/delay.h> 21 + #include <linux/device.h> 22 + #include <linux/device/driver.h> 23 + #include <linux/dmi.h> 24 + #include <linux/errno.h> 25 + #include <linux/fixp-arith.h> 26 + #include <linux/hwmon.h> 27 + #include <linux/hwmon-sysfs.h> 28 + #include <linux/init.h> 29 + #include <linux/input.h> 30 + #include <linux/input/sparse-keymap.h> 31 + #include <linux/kernel.h> 32 + #include <linux/kstrtox.h> 33 + #include <linux/leds.h> 34 + #include <linux/led-class-multicolor.h> 35 + #include <linux/limits.h> 36 + #include <linux/list.h> 37 + #include <linux/minmax.h> 38 + #include <linux/module.h> 39 + #include <linux/mutex.h> 40 + #include <linux/notifier.h> 41 + #include <linux/platform_device.h> 42 + #include <linux/pm.h> 43 + #include <linux/printk.h> 44 + #include <linux/regmap.h> 45 + #include <linux/string.h> 46 + #include <linux/sysfs.h> 47 + #include <linux/types.h> 48 + #include <linux/units.h> 49 + 50 + #include <acpi/battery.h> 51 + 52 + #include "uniwill-wmi.h" 53 + 54 + #define EC_ADDR_BAT_POWER_UNIT_1 0x0400 55 + 56 + #define EC_ADDR_BAT_POWER_UNIT_2 0x0401 57 + 58 + #define EC_ADDR_BAT_DESIGN_CAPACITY_1 0x0402 59 + 60 + #define EC_ADDR_BAT_DESIGN_CAPACITY_2 0x0403 61 + 62 + #define EC_ADDR_BAT_FULL_CAPACITY_1 0x0404 63 + 64 + #define EC_ADDR_BAT_FULL_CAPACITY_2 0x0405 65 + 66 + #define EC_ADDR_BAT_DESIGN_VOLTAGE_1 0x0408 67 + 68 + #define EC_ADDR_BAT_DESIGN_VOLTAGE_2 0x0409 69 + 70 + #define EC_ADDR_BAT_STATUS_1 0x0432 71 + #define BAT_DISCHARGING BIT(0) 72 + 73 + #define EC_ADDR_BAT_STATUS_2 0x0433 74 + 75 + #define EC_ADDR_BAT_CURRENT_1 0x0434 76 + 77 + #define EC_ADDR_BAT_CURRENT_2 0x0435 78 + 79 + #define EC_ADDR_BAT_REMAIN_CAPACITY_1 0x0436 80 + 81 + #define EC_ADDR_BAT_REMAIN_CAPACITY_2 0x0437 82 + 83 + #define EC_ADDR_BAT_VOLTAGE_1 0x0438 84 + 85 + #define EC_ADDR_BAT_VOLTAGE_2 0x0439 86 + 87 + #define EC_ADDR_CPU_TEMP 0x043E 88 + 89 + #define EC_ADDR_GPU_TEMP 0x044F 90 + 91 + #define EC_ADDR_MAIN_FAN_RPM_1 0x0464 92 + 93 + #define EC_ADDR_MAIN_FAN_RPM_2 0x0465 94 + 95 + #define EC_ADDR_SECOND_FAN_RPM_1 0x046C 96 + 97 + #define EC_ADDR_SECOND_FAN_RPM_2 0x046D 98 + 99 + #define EC_ADDR_DEVICE_STATUS 0x047B 100 + #define WIFI_STATUS_ON BIT(7) 101 + /* BIT(5) is also unset depending on the rfkill state (bluetooth?) */ 102 + 103 + #define EC_ADDR_BAT_ALERT 0x0494 104 + 105 + #define EC_ADDR_BAT_CYCLE_COUNT_1 0x04A6 106 + 107 + #define EC_ADDR_BAT_CYCLE_COUNT_2 0x04A7 108 + 109 + #define EC_ADDR_PROJECT_ID 0x0740 110 + 111 + #define EC_ADDR_AP_OEM 0x0741 112 + #define ENABLE_MANUAL_CTRL BIT(0) 113 + #define ITE_KBD_EFFECT_REACTIVE BIT(3) 114 + #define FAN_ABNORMAL BIT(5) 115 + 116 + #define EC_ADDR_SUPPORT_5 0x0742 117 + #define FAN_TURBO_SUPPORTED BIT(4) 118 + #define FAN_SUPPORT BIT(5) 119 + 120 + #define EC_ADDR_CTGP_DB_CTRL 0x0743 121 + #define CTGP_DB_GENERAL_ENABLE BIT(0) 122 + #define CTGP_DB_DB_ENABLE BIT(1) 123 + #define CTGP_DB_CTGP_ENABLE BIT(2) 124 + 125 + #define EC_ADDR_CTGP_OFFSET 0x0744 126 + 127 + #define EC_ADDR_TPP_OFFSET 0x0745 128 + 129 + #define EC_ADDR_MAX_TGP 0x0746 130 + 131 + #define EC_ADDR_LIGHTBAR_AC_CTRL 0x0748 132 + #define LIGHTBAR_APP_EXISTS BIT(0) 133 + #define LIGHTBAR_POWER_SAVE BIT(1) 134 + #define LIGHTBAR_S0_OFF BIT(2) 135 + #define LIGHTBAR_S3_OFF BIT(3) // Breathing animation when suspended 136 + #define LIGHTBAR_WELCOME BIT(7) // Rainbow animation 137 + 138 + #define EC_ADDR_LIGHTBAR_AC_RED 0x0749 139 + 140 + #define EC_ADDR_LIGHTBAR_AC_GREEN 0x074A 141 + 142 + #define EC_ADDR_LIGHTBAR_AC_BLUE 0x074B 143 + 144 + #define EC_ADDR_BIOS_OEM 0x074E 145 + #define FN_LOCK_STATUS BIT(4) 146 + 147 + #define EC_ADDR_MANUAL_FAN_CTRL 0x0751 148 + #define FAN_LEVEL_MASK GENMASK(2, 0) 149 + #define FAN_MODE_TURBO BIT(4) 150 + #define FAN_MODE_HIGH BIT(5) 151 + #define FAN_MODE_BOOST BIT(6) 152 + #define FAN_MODE_USER BIT(7) 153 + 154 + #define EC_ADDR_PWM_1 0x075B 155 + 156 + #define EC_ADDR_PWM_2 0x075C 157 + 158 + /* Unreliable */ 159 + #define EC_ADDR_SUPPORT_1 0x0765 160 + #define AIRPLANE_MODE BIT(0) 161 + #define GPS_SWITCH BIT(1) 162 + #define OVERCLOCK BIT(2) 163 + #define MACRO_KEY BIT(3) 164 + #define SHORTCUT_KEY BIT(4) 165 + #define SUPER_KEY_LOCK BIT(5) 166 + #define LIGHTBAR BIT(6) 167 + #define FAN_BOOST BIT(7) 168 + 169 + #define EC_ADDR_SUPPORT_2 0x0766 170 + #define SILENT_MODE BIT(0) 171 + #define USB_CHARGING BIT(1) 172 + #define RGB_KEYBOARD BIT(2) 173 + #define CHINA_MODE BIT(5) 174 + #define MY_BATTERY BIT(6) 175 + 176 + #define EC_ADDR_TRIGGER 0x0767 177 + #define TRIGGER_SUPER_KEY_LOCK BIT(0) 178 + #define TRIGGER_LIGHTBAR BIT(1) 179 + #define TRIGGER_FAN_BOOST BIT(2) 180 + #define TRIGGER_SILENT_MODE BIT(3) 181 + #define TRIGGER_USB_CHARGING BIT(4) 182 + #define RGB_APPLY_COLOR BIT(5) 183 + #define RGB_LOGO_EFFECT BIT(6) 184 + #define RGB_RAINBOW_EFFECT BIT(7) 185 + 186 + #define EC_ADDR_SWITCH_STATUS 0x0768 187 + #define SUPER_KEY_LOCK_STATUS BIT(0) 188 + #define LIGHTBAR_STATUS BIT(1) 189 + #define FAN_BOOST_STATUS BIT(2) 190 + #define MACRO_KEY_STATUS BIT(3) 191 + #define MY_BAT_POWER_BAT_STATUS BIT(4) 192 + 193 + #define EC_ADDR_RGB_RED 0x0769 194 + 195 + #define EC_ADDR_RGB_GREEN 0x076A 196 + 197 + #define EC_ADDR_RGB_BLUE 0x076B 198 + 199 + #define EC_ADDR_ROMID_START 0x0770 200 + #define ROMID_LENGTH 14 201 + 202 + #define EC_ADDR_ROMID_EXTRA_1 0x077E 203 + 204 + #define EC_ADDR_ROMID_EXTRA_2 0x077F 205 + 206 + #define EC_ADDR_BIOS_OEM_2 0x0782 207 + #define FAN_V2_NEW BIT(0) 208 + #define FAN_QKEY BIT(1) 209 + #define FAN_TABLE_OFFICE_MODE BIT(2) 210 + #define FAN_V3 BIT(3) 211 + #define DEFAULT_MODE BIT(4) 212 + 213 + #define EC_ADDR_PL1_SETTING 0x0783 214 + 215 + #define EC_ADDR_PL2_SETTING 0x0784 216 + 217 + #define EC_ADDR_PL4_SETTING 0x0785 218 + 219 + #define EC_ADDR_FAN_DEFAULT 0x0786 220 + #define FAN_CURVE_LENGTH 5 221 + 222 + #define EC_ADDR_KBD_STATUS 0x078C 223 + #define KBD_WHITE_ONLY BIT(0) // ~single color 224 + #define KBD_SINGLE_COLOR_OFF BIT(1) 225 + #define KBD_TURBO_LEVEL_MASK GENMASK(3, 2) 226 + #define KBD_APPLY BIT(4) 227 + #define KBD_BRIGHTNESS GENMASK(7, 5) 228 + 229 + #define EC_ADDR_FAN_CTRL 0x078E 230 + #define FAN3P5 BIT(1) 231 + #define CHARGING_PROFILE BIT(3) 232 + #define UNIVERSAL_FAN_CTRL BIT(6) 233 + 234 + #define EC_ADDR_BIOS_OEM_3 0x07A3 235 + #define FAN_REDUCED_DURY_CYCLE BIT(5) 236 + #define FAN_ALWAYS_ON BIT(6) 237 + 238 + #define EC_ADDR_BIOS_BYTE 0x07A4 239 + #define FN_LOCK_SWITCH BIT(3) 240 + 241 + #define EC_ADDR_OEM_3 0x07A5 242 + #define POWER_LED_MASK GENMASK(1, 0) 243 + #define POWER_LED_LEFT 0x00 244 + #define POWER_LED_BOTH 0x01 245 + #define POWER_LED_NONE 0x02 246 + #define FAN_QUIET BIT(2) 247 + #define OVERBOOST BIT(4) 248 + #define HIGH_POWER BIT(7) 249 + 250 + #define EC_ADDR_OEM_4 0x07A6 251 + #define OVERBOOST_DYN_TEMP_OFF BIT(1) 252 + #define TOUCHPAD_TOGGLE_OFF BIT(6) 253 + 254 + #define EC_ADDR_CHARGE_CTRL 0x07B9 255 + #define CHARGE_CTRL_MASK GENMASK(6, 0) 256 + #define CHARGE_CTRL_REACHED BIT(7) 257 + 258 + #define EC_ADDR_UNIVERSAL_FAN_CTRL 0x07C5 259 + #define SPLIT_TABLES BIT(7) 260 + 261 + #define EC_ADDR_AP_OEM_6 0x07C6 262 + #define ENABLE_UNIVERSAL_FAN_CTRL BIT(2) 263 + #define BATTERY_CHARGE_FULL_OVER_24H BIT(3) 264 + #define BATTERY_ERM_STATUS_REACHED BIT(4) 265 + 266 + #define EC_ADDR_CHARGE_PRIO 0x07CC 267 + #define CHARGING_PERFORMANCE BIT(7) 268 + 269 + /* Same bits as EC_ADDR_LIGHTBAR_AC_CTRL except LIGHTBAR_S3_OFF */ 270 + #define EC_ADDR_LIGHTBAR_BAT_CTRL 0x07E2 271 + 272 + #define EC_ADDR_LIGHTBAR_BAT_RED 0x07E3 273 + 274 + #define EC_ADDR_LIGHTBAR_BAT_GREEN 0x07E4 275 + 276 + #define EC_ADDR_LIGHTBAR_BAT_BLUE 0x07E5 277 + 278 + #define EC_ADDR_CPU_TEMP_END_TABLE 0x0F00 279 + 280 + #define EC_ADDR_CPU_TEMP_START_TABLE 0x0F10 281 + 282 + #define EC_ADDR_CPU_FAN_SPEED_TABLE 0x0F20 283 + 284 + #define EC_ADDR_GPU_TEMP_END_TABLE 0x0F30 285 + 286 + #define EC_ADDR_GPU_TEMP_START_TABLE 0x0F40 287 + 288 + #define EC_ADDR_GPU_FAN_SPEED_TABLE 0x0F50 289 + 290 + /* 291 + * Those two registers technically allow for manual fan control, 292 + * but are unstable on some models and are likely not meant to 293 + * be used by applications as they are only accessible when using 294 + * the WMI interface. 295 + */ 296 + #define EC_ADDR_PWM_1_WRITEABLE 0x1804 297 + 298 + #define EC_ADDR_PWM_2_WRITEABLE 0x1809 299 + 300 + #define DRIVER_NAME "uniwill" 301 + 302 + /* 303 + * The OEM software always sleeps up to 6 ms after reading/writing EC 304 + * registers, so we emulate this behaviour for maximum compatibility. 305 + */ 306 + #define UNIWILL_EC_DELAY_US 6000 307 + 308 + #define PWM_MAX 200 309 + #define FAN_TABLE_LENGTH 16 310 + 311 + #define LED_CHANNELS 3 312 + #define LED_MAX_BRIGHTNESS 200 313 + 314 + #define UNIWILL_FEATURE_FN_LOCK_TOGGLE BIT(0) 315 + #define UNIWILL_FEATURE_SUPER_KEY_TOGGLE BIT(1) 316 + #define UNIWILL_FEATURE_TOUCHPAD_TOGGLE BIT(2) 317 + #define UNIWILL_FEATURE_LIGHTBAR BIT(3) 318 + #define UNIWILL_FEATURE_BATTERY BIT(4) 319 + #define UNIWILL_FEATURE_HWMON BIT(5) 320 + 321 + struct uniwill_data { 322 + struct device *dev; 323 + acpi_handle handle; 324 + struct regmap *regmap; 325 + struct acpi_battery_hook hook; 326 + unsigned int last_charge_ctrl; 327 + struct mutex battery_lock; /* Protects the list of currently registered batteries */ 328 + unsigned int last_switch_status; 329 + struct mutex super_key_lock; /* Protects the toggling of the super key lock state */ 330 + struct list_head batteries; 331 + struct mutex led_lock; /* Protects writes to the lightbar registers */ 332 + struct led_classdev_mc led_mc_cdev; 333 + struct mc_subled led_mc_subled_info[LED_CHANNELS]; 334 + struct mutex input_lock; /* Protects input sequence during notify */ 335 + struct input_dev *input_device; 336 + struct notifier_block nb; 337 + }; 338 + 339 + struct uniwill_battery_entry { 340 + struct list_head head; 341 + struct power_supply *battery; 342 + }; 343 + 344 + static bool force; 345 + module_param_unsafe(force, bool, 0); 346 + MODULE_PARM_DESC(force, "Force loading without checking for supported devices\n"); 347 + 348 + /* Feature bitmask since the associated registers are not reliable */ 349 + static unsigned int supported_features; 350 + 351 + static const char * const uniwill_temp_labels[] = { 352 + "CPU", 353 + "GPU", 354 + }; 355 + 356 + static const char * const uniwill_fan_labels[] = { 357 + "Main", 358 + "Secondary", 359 + }; 360 + 361 + static const struct key_entry uniwill_keymap[] = { 362 + /* Reported via keyboard controller */ 363 + { KE_IGNORE, UNIWILL_OSD_CAPSLOCK, { KEY_CAPSLOCK }}, 364 + { KE_IGNORE, UNIWILL_OSD_NUMLOCK, { KEY_NUMLOCK }}, 365 + 366 + /* Reported when the user locks/unlocks the super key */ 367 + { KE_IGNORE, UNIWILL_OSD_SUPER_KEY_LOCK_ENABLE, { KEY_UNKNOWN }}, 368 + { KE_IGNORE, UNIWILL_OSD_SUPER_KEY_LOCK_DISABLE, { KEY_UNKNOWN }}, 369 + /* Optional, might not be reported by all devices */ 370 + { KE_IGNORE, UNIWILL_OSD_SUPER_KEY_LOCK_CHANGED, { KEY_UNKNOWN }}, 371 + 372 + /* Reported in manual mode when toggling the airplane mode status */ 373 + { KE_KEY, UNIWILL_OSD_RFKILL, { KEY_RFKILL }}, 374 + { KE_IGNORE, UNIWILL_OSD_RADIOON, { KEY_UNKNOWN }}, 375 + { KE_IGNORE, UNIWILL_OSD_RADIOOFF, { KEY_UNKNOWN }}, 376 + 377 + /* Reported when user wants to cycle the platform profile */ 378 + { KE_KEY, UNIWILL_OSD_PERFORMANCE_MODE_TOGGLE, { KEY_F14 }}, 379 + 380 + /* Reported when the user wants to adjust the brightness of the keyboard */ 381 + { KE_KEY, UNIWILL_OSD_KBDILLUMDOWN, { KEY_KBDILLUMDOWN }}, 382 + { KE_KEY, UNIWILL_OSD_KBDILLUMUP, { KEY_KBDILLUMUP }}, 383 + 384 + /* Reported when the user wants to toggle the microphone mute status */ 385 + { KE_KEY, UNIWILL_OSD_MIC_MUTE, { KEY_MICMUTE }}, 386 + 387 + /* Reported when the user wants to toggle the mute status */ 388 + { KE_IGNORE, UNIWILL_OSD_MUTE, { KEY_MUTE }}, 389 + 390 + /* Reported when the user locks/unlocks the Fn key */ 391 + { KE_IGNORE, UNIWILL_OSD_FN_LOCK, { KEY_FN_ESC }}, 392 + 393 + /* Reported when the user wants to toggle the brightness of the keyboard */ 394 + { KE_KEY, UNIWILL_OSD_KBDILLUMTOGGLE, { KEY_KBDILLUMTOGGLE }}, 395 + { KE_KEY, UNIWILL_OSD_KB_LED_LEVEL0, { KEY_KBDILLUMTOGGLE }}, 396 + { KE_KEY, UNIWILL_OSD_KB_LED_LEVEL1, { KEY_KBDILLUMTOGGLE }}, 397 + { KE_KEY, UNIWILL_OSD_KB_LED_LEVEL2, { KEY_KBDILLUMTOGGLE }}, 398 + { KE_KEY, UNIWILL_OSD_KB_LED_LEVEL3, { KEY_KBDILLUMTOGGLE }}, 399 + { KE_KEY, UNIWILL_OSD_KB_LED_LEVEL4, { KEY_KBDILLUMTOGGLE }}, 400 + 401 + /* FIXME: find out the exact meaning of those events */ 402 + { KE_IGNORE, UNIWILL_OSD_BAT_CHARGE_FULL_24_H, { KEY_UNKNOWN }}, 403 + { KE_IGNORE, UNIWILL_OSD_BAT_ERM_UPDATE, { KEY_UNKNOWN }}, 404 + 405 + /* Reported when the user wants to toggle the benchmark mode status */ 406 + { KE_IGNORE, UNIWILL_OSD_BENCHMARK_MODE_TOGGLE, { KEY_UNKNOWN }}, 407 + 408 + /* Reported when the user wants to toggle the webcam */ 409 + { KE_IGNORE, UNIWILL_OSD_WEBCAM_TOGGLE, { KEY_UNKNOWN }}, 410 + 411 + { KE_END } 412 + }; 413 + 414 + static int uniwill_ec_reg_write(void *context, unsigned int reg, unsigned int val) 415 + { 416 + union acpi_object params[2] = { 417 + { 418 + .integer = { 419 + .type = ACPI_TYPE_INTEGER, 420 + .value = reg, 421 + }, 422 + }, 423 + { 424 + .integer = { 425 + .type = ACPI_TYPE_INTEGER, 426 + .value = val, 427 + }, 428 + }, 429 + }; 430 + struct uniwill_data *data = context; 431 + struct acpi_object_list input = { 432 + .count = ARRAY_SIZE(params), 433 + .pointer = params, 434 + }; 435 + acpi_status status; 436 + 437 + status = acpi_evaluate_object(data->handle, "ECRW", &input, NULL); 438 + if (ACPI_FAILURE(status)) 439 + return -EIO; 440 + 441 + usleep_range(UNIWILL_EC_DELAY_US, UNIWILL_EC_DELAY_US * 2); 442 + 443 + return 0; 444 + } 445 + 446 + static int uniwill_ec_reg_read(void *context, unsigned int reg, unsigned int *val) 447 + { 448 + union acpi_object params[1] = { 449 + { 450 + .integer = { 451 + .type = ACPI_TYPE_INTEGER, 452 + .value = reg, 453 + }, 454 + }, 455 + }; 456 + struct uniwill_data *data = context; 457 + struct acpi_object_list input = { 458 + .count = ARRAY_SIZE(params), 459 + .pointer = params, 460 + }; 461 + unsigned long long output; 462 + acpi_status status; 463 + 464 + status = acpi_evaluate_integer(data->handle, "ECRR", &input, &output); 465 + if (ACPI_FAILURE(status)) 466 + return -EIO; 467 + 468 + if (output > U8_MAX) 469 + return -ENXIO; 470 + 471 + usleep_range(UNIWILL_EC_DELAY_US, UNIWILL_EC_DELAY_US * 2); 472 + 473 + *val = output; 474 + 475 + return 0; 476 + } 477 + 478 + static const struct regmap_bus uniwill_ec_bus = { 479 + .reg_write = uniwill_ec_reg_write, 480 + .reg_read = uniwill_ec_reg_read, 481 + .reg_format_endian_default = REGMAP_ENDIAN_LITTLE, 482 + .val_format_endian_default = REGMAP_ENDIAN_LITTLE, 483 + }; 484 + 485 + static bool uniwill_writeable_reg(struct device *dev, unsigned int reg) 486 + { 487 + switch (reg) { 488 + case EC_ADDR_AP_OEM: 489 + case EC_ADDR_LIGHTBAR_AC_CTRL: 490 + case EC_ADDR_LIGHTBAR_AC_RED: 491 + case EC_ADDR_LIGHTBAR_AC_GREEN: 492 + case EC_ADDR_LIGHTBAR_AC_BLUE: 493 + case EC_ADDR_BIOS_OEM: 494 + case EC_ADDR_TRIGGER: 495 + case EC_ADDR_OEM_4: 496 + case EC_ADDR_CHARGE_CTRL: 497 + case EC_ADDR_LIGHTBAR_BAT_CTRL: 498 + case EC_ADDR_LIGHTBAR_BAT_RED: 499 + case EC_ADDR_LIGHTBAR_BAT_GREEN: 500 + case EC_ADDR_LIGHTBAR_BAT_BLUE: 501 + return true; 502 + default: 503 + return false; 504 + } 505 + } 506 + 507 + static bool uniwill_readable_reg(struct device *dev, unsigned int reg) 508 + { 509 + switch (reg) { 510 + case EC_ADDR_CPU_TEMP: 511 + case EC_ADDR_GPU_TEMP: 512 + case EC_ADDR_MAIN_FAN_RPM_1: 513 + case EC_ADDR_MAIN_FAN_RPM_2: 514 + case EC_ADDR_SECOND_FAN_RPM_1: 515 + case EC_ADDR_SECOND_FAN_RPM_2: 516 + case EC_ADDR_BAT_ALERT: 517 + case EC_ADDR_PROJECT_ID: 518 + case EC_ADDR_AP_OEM: 519 + case EC_ADDR_LIGHTBAR_AC_CTRL: 520 + case EC_ADDR_LIGHTBAR_AC_RED: 521 + case EC_ADDR_LIGHTBAR_AC_GREEN: 522 + case EC_ADDR_LIGHTBAR_AC_BLUE: 523 + case EC_ADDR_BIOS_OEM: 524 + case EC_ADDR_PWM_1: 525 + case EC_ADDR_PWM_2: 526 + case EC_ADDR_TRIGGER: 527 + case EC_ADDR_SWITCH_STATUS: 528 + case EC_ADDR_OEM_4: 529 + case EC_ADDR_CHARGE_CTRL: 530 + case EC_ADDR_LIGHTBAR_BAT_CTRL: 531 + case EC_ADDR_LIGHTBAR_BAT_RED: 532 + case EC_ADDR_LIGHTBAR_BAT_GREEN: 533 + case EC_ADDR_LIGHTBAR_BAT_BLUE: 534 + return true; 535 + default: 536 + return false; 537 + } 538 + } 539 + 540 + static bool uniwill_volatile_reg(struct device *dev, unsigned int reg) 541 + { 542 + switch (reg) { 543 + case EC_ADDR_CPU_TEMP: 544 + case EC_ADDR_GPU_TEMP: 545 + case EC_ADDR_MAIN_FAN_RPM_1: 546 + case EC_ADDR_MAIN_FAN_RPM_2: 547 + case EC_ADDR_SECOND_FAN_RPM_1: 548 + case EC_ADDR_SECOND_FAN_RPM_2: 549 + case EC_ADDR_BAT_ALERT: 550 + case EC_ADDR_PWM_1: 551 + case EC_ADDR_PWM_2: 552 + case EC_ADDR_TRIGGER: 553 + case EC_ADDR_SWITCH_STATUS: 554 + case EC_ADDR_CHARGE_CTRL: 555 + return true; 556 + default: 557 + return false; 558 + } 559 + } 560 + 561 + static const struct regmap_config uniwill_ec_config = { 562 + .reg_bits = 16, 563 + .val_bits = 8, 564 + .writeable_reg = uniwill_writeable_reg, 565 + .readable_reg = uniwill_readable_reg, 566 + .volatile_reg = uniwill_volatile_reg, 567 + .can_sleep = true, 568 + .max_register = 0xFFF, 569 + .cache_type = REGCACHE_MAPLE, 570 + .use_single_read = true, 571 + .use_single_write = true, 572 + }; 573 + 574 + static ssize_t fn_lock_toggle_enable_store(struct device *dev, struct device_attribute *attr, 575 + const char *buf, size_t count) 576 + { 577 + struct uniwill_data *data = dev_get_drvdata(dev); 578 + unsigned int value; 579 + bool enable; 580 + int ret; 581 + 582 + ret = kstrtobool(buf, &enable); 583 + if (ret < 0) 584 + return ret; 585 + 586 + if (enable) 587 + value = FN_LOCK_STATUS; 588 + else 589 + value = 0; 590 + 591 + ret = regmap_update_bits(data->regmap, EC_ADDR_BIOS_OEM, FN_LOCK_STATUS, value); 592 + if (ret < 0) 593 + return ret; 594 + 595 + return count; 596 + } 597 + 598 + static ssize_t fn_lock_toggle_enable_show(struct device *dev, struct device_attribute *attr, 599 + char *buf) 600 + { 601 + struct uniwill_data *data = dev_get_drvdata(dev); 602 + unsigned int value; 603 + int ret; 604 + 605 + ret = regmap_read(data->regmap, EC_ADDR_BIOS_OEM, &value); 606 + if (ret < 0) 607 + return ret; 608 + 609 + return sysfs_emit(buf, "%d\n", !!(value & FN_LOCK_STATUS)); 610 + } 611 + 612 + static DEVICE_ATTR_RW(fn_lock_toggle_enable); 613 + 614 + static ssize_t super_key_toggle_enable_store(struct device *dev, struct device_attribute *attr, 615 + const char *buf, size_t count) 616 + { 617 + struct uniwill_data *data = dev_get_drvdata(dev); 618 + unsigned int value; 619 + bool enable; 620 + int ret; 621 + 622 + ret = kstrtobool(buf, &enable); 623 + if (ret < 0) 624 + return ret; 625 + 626 + guard(mutex)(&data->super_key_lock); 627 + 628 + ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value); 629 + if (ret < 0) 630 + return ret; 631 + 632 + /* 633 + * We can only toggle the super key lock, so we return early if the setting 634 + * is already in the correct state. 635 + */ 636 + if (enable == !(value & SUPER_KEY_LOCK_STATUS)) 637 + return count; 638 + 639 + ret = regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, TRIGGER_SUPER_KEY_LOCK, 640 + TRIGGER_SUPER_KEY_LOCK); 641 + if (ret < 0) 642 + return ret; 643 + 644 + return count; 645 + } 646 + 647 + static ssize_t super_key_toggle_enable_show(struct device *dev, struct device_attribute *attr, 648 + char *buf) 649 + { 650 + struct uniwill_data *data = dev_get_drvdata(dev); 651 + unsigned int value; 652 + int ret; 653 + 654 + ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value); 655 + if (ret < 0) 656 + return ret; 657 + 658 + return sysfs_emit(buf, "%d\n", !(value & SUPER_KEY_LOCK_STATUS)); 659 + } 660 + 661 + static DEVICE_ATTR_RW(super_key_toggle_enable); 662 + 663 + static ssize_t touchpad_toggle_enable_store(struct device *dev, struct device_attribute *attr, 664 + const char *buf, size_t count) 665 + { 666 + struct uniwill_data *data = dev_get_drvdata(dev); 667 + unsigned int value; 668 + bool enable; 669 + int ret; 670 + 671 + ret = kstrtobool(buf, &enable); 672 + if (ret < 0) 673 + return ret; 674 + 675 + if (enable) 676 + value = 0; 677 + else 678 + value = TOUCHPAD_TOGGLE_OFF; 679 + 680 + ret = regmap_update_bits(data->regmap, EC_ADDR_OEM_4, TOUCHPAD_TOGGLE_OFF, value); 681 + if (ret < 0) 682 + return ret; 683 + 684 + return count; 685 + } 686 + 687 + static ssize_t touchpad_toggle_enable_show(struct device *dev, struct device_attribute *attr, 688 + char *buf) 689 + { 690 + struct uniwill_data *data = dev_get_drvdata(dev); 691 + unsigned int value; 692 + int ret; 693 + 694 + ret = regmap_read(data->regmap, EC_ADDR_OEM_4, &value); 695 + if (ret < 0) 696 + return ret; 697 + 698 + return sysfs_emit(buf, "%d\n", !(value & TOUCHPAD_TOGGLE_OFF)); 699 + } 700 + 701 + static DEVICE_ATTR_RW(touchpad_toggle_enable); 702 + 703 + static ssize_t rainbow_animation_store(struct device *dev, struct device_attribute *attr, 704 + const char *buf, size_t count) 705 + { 706 + struct uniwill_data *data = dev_get_drvdata(dev); 707 + unsigned int value; 708 + bool enable; 709 + int ret; 710 + 711 + ret = kstrtobool(buf, &enable); 712 + if (ret < 0) 713 + return ret; 714 + 715 + if (enable) 716 + value = LIGHTBAR_WELCOME; 717 + else 718 + value = 0; 719 + 720 + guard(mutex)(&data->led_lock); 721 + 722 + ret = regmap_update_bits(data->regmap, EC_ADDR_LIGHTBAR_AC_CTRL, LIGHTBAR_WELCOME, value); 723 + if (ret < 0) 724 + return ret; 725 + 726 + ret = regmap_update_bits(data->regmap, EC_ADDR_LIGHTBAR_BAT_CTRL, LIGHTBAR_WELCOME, value); 727 + if (ret < 0) 728 + return ret; 729 + 730 + return count; 731 + } 732 + 733 + static ssize_t rainbow_animation_show(struct device *dev, struct device_attribute *attr, char *buf) 734 + { 735 + struct uniwill_data *data = dev_get_drvdata(dev); 736 + unsigned int value; 737 + int ret; 738 + 739 + ret = regmap_read(data->regmap, EC_ADDR_LIGHTBAR_AC_CTRL, &value); 740 + if (ret < 0) 741 + return ret; 742 + 743 + return sysfs_emit(buf, "%d\n", !!(value & LIGHTBAR_WELCOME)); 744 + } 745 + 746 + static DEVICE_ATTR_RW(rainbow_animation); 747 + 748 + static ssize_t breathing_in_suspend_store(struct device *dev, struct device_attribute *attr, 749 + const char *buf, size_t count) 750 + { 751 + struct uniwill_data *data = dev_get_drvdata(dev); 752 + unsigned int value; 753 + bool enable; 754 + int ret; 755 + 756 + ret = kstrtobool(buf, &enable); 757 + if (ret < 0) 758 + return ret; 759 + 760 + if (enable) 761 + value = 0; 762 + else 763 + value = LIGHTBAR_S3_OFF; 764 + 765 + /* We only access a single register here, so we do not need to use data->led_lock */ 766 + ret = regmap_update_bits(data->regmap, EC_ADDR_LIGHTBAR_AC_CTRL, LIGHTBAR_S3_OFF, value); 767 + if (ret < 0) 768 + return ret; 769 + 770 + return count; 771 + } 772 + 773 + static ssize_t breathing_in_suspend_show(struct device *dev, struct device_attribute *attr, 774 + char *buf) 775 + { 776 + struct uniwill_data *data = dev_get_drvdata(dev); 777 + unsigned int value; 778 + int ret; 779 + 780 + ret = regmap_read(data->regmap, EC_ADDR_LIGHTBAR_AC_CTRL, &value); 781 + if (ret < 0) 782 + return ret; 783 + 784 + return sysfs_emit(buf, "%d\n", !(value & LIGHTBAR_S3_OFF)); 785 + } 786 + 787 + static DEVICE_ATTR_RW(breathing_in_suspend); 788 + 789 + static struct attribute *uniwill_attrs[] = { 790 + /* Keyboard-related */ 791 + &dev_attr_fn_lock_toggle_enable.attr, 792 + &dev_attr_super_key_toggle_enable.attr, 793 + &dev_attr_touchpad_toggle_enable.attr, 794 + /* Lightbar-related */ 795 + &dev_attr_rainbow_animation.attr, 796 + &dev_attr_breathing_in_suspend.attr, 797 + NULL 798 + }; 799 + 800 + static umode_t uniwill_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) 801 + { 802 + if (attr == &dev_attr_fn_lock_toggle_enable.attr) { 803 + if (supported_features & UNIWILL_FEATURE_FN_LOCK_TOGGLE) 804 + return attr->mode; 805 + } 806 + 807 + if (attr == &dev_attr_super_key_toggle_enable.attr) { 808 + if (supported_features & UNIWILL_FEATURE_SUPER_KEY_TOGGLE) 809 + return attr->mode; 810 + } 811 + 812 + if (attr == &dev_attr_touchpad_toggle_enable.attr) { 813 + if (supported_features & UNIWILL_FEATURE_TOUCHPAD_TOGGLE) 814 + return attr->mode; 815 + } 816 + 817 + if (attr == &dev_attr_rainbow_animation.attr || 818 + attr == &dev_attr_breathing_in_suspend.attr) { 819 + if (supported_features & UNIWILL_FEATURE_LIGHTBAR) 820 + return attr->mode; 821 + } 822 + 823 + return 0; 824 + } 825 + 826 + static const struct attribute_group uniwill_group = { 827 + .is_visible = uniwill_attr_is_visible, 828 + .attrs = uniwill_attrs, 829 + }; 830 + 831 + static const struct attribute_group *uniwill_groups[] = { 832 + &uniwill_group, 833 + NULL 834 + }; 835 + 836 + static int uniwill_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, 837 + long *val) 838 + { 839 + struct uniwill_data *data = dev_get_drvdata(dev); 840 + unsigned int value; 841 + __be16 rpm; 842 + int ret; 843 + 844 + switch (type) { 845 + case hwmon_temp: 846 + switch (channel) { 847 + case 0: 848 + ret = regmap_read(data->regmap, EC_ADDR_CPU_TEMP, &value); 849 + break; 850 + case 1: 851 + ret = regmap_read(data->regmap, EC_ADDR_GPU_TEMP, &value); 852 + break; 853 + default: 854 + return -EOPNOTSUPP; 855 + } 856 + 857 + if (ret < 0) 858 + return ret; 859 + 860 + *val = value * MILLIDEGREE_PER_DEGREE; 861 + return 0; 862 + case hwmon_fan: 863 + switch (channel) { 864 + case 0: 865 + ret = regmap_bulk_read(data->regmap, EC_ADDR_MAIN_FAN_RPM_1, &rpm, 866 + sizeof(rpm)); 867 + break; 868 + case 1: 869 + ret = regmap_bulk_read(data->regmap, EC_ADDR_SECOND_FAN_RPM_1, &rpm, 870 + sizeof(rpm)); 871 + break; 872 + default: 873 + return -EOPNOTSUPP; 874 + } 875 + 876 + if (ret < 0) 877 + return ret; 878 + 879 + *val = be16_to_cpu(rpm); 880 + return 0; 881 + case hwmon_pwm: 882 + switch (channel) { 883 + case 0: 884 + ret = regmap_read(data->regmap, EC_ADDR_PWM_1, &value); 885 + break; 886 + case 1: 887 + ret = regmap_read(data->regmap, EC_ADDR_PWM_2, &value); 888 + break; 889 + default: 890 + return -EOPNOTSUPP; 891 + } 892 + 893 + if (ret < 0) 894 + return ret; 895 + 896 + *val = fixp_linear_interpolate(0, 0, PWM_MAX, U8_MAX, value); 897 + return 0; 898 + default: 899 + return -EOPNOTSUPP; 900 + } 901 + } 902 + 903 + static int uniwill_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, 904 + int channel, const char **str) 905 + { 906 + switch (type) { 907 + case hwmon_temp: 908 + *str = uniwill_temp_labels[channel]; 909 + return 0; 910 + case hwmon_fan: 911 + *str = uniwill_fan_labels[channel]; 912 + return 0; 913 + default: 914 + return -EOPNOTSUPP; 915 + } 916 + } 917 + 918 + static const struct hwmon_ops uniwill_ops = { 919 + .visible = 0444, 920 + .read = uniwill_read, 921 + .read_string = uniwill_read_string, 922 + }; 923 + 924 + static const struct hwmon_channel_info * const uniwill_info[] = { 925 + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), 926 + HWMON_CHANNEL_INFO(temp, 927 + HWMON_T_INPUT | HWMON_T_LABEL, 928 + HWMON_T_INPUT | HWMON_T_LABEL), 929 + HWMON_CHANNEL_INFO(fan, 930 + HWMON_F_INPUT | HWMON_F_LABEL, 931 + HWMON_F_INPUT | HWMON_F_LABEL), 932 + HWMON_CHANNEL_INFO(pwm, 933 + HWMON_PWM_INPUT, 934 + HWMON_PWM_INPUT), 935 + NULL 936 + }; 937 + 938 + static const struct hwmon_chip_info uniwill_chip_info = { 939 + .ops = &uniwill_ops, 940 + .info = uniwill_info, 941 + }; 942 + 943 + static int uniwill_hwmon_init(struct uniwill_data *data) 944 + { 945 + struct device *hdev; 946 + 947 + if (!(supported_features & UNIWILL_FEATURE_HWMON)) 948 + return 0; 949 + 950 + hdev = devm_hwmon_device_register_with_info(data->dev, "uniwill", data, 951 + &uniwill_chip_info, NULL); 952 + 953 + return PTR_ERR_OR_ZERO(hdev); 954 + } 955 + 956 + static const unsigned int uniwill_led_channel_to_bat_reg[LED_CHANNELS] = { 957 + EC_ADDR_LIGHTBAR_BAT_RED, 958 + EC_ADDR_LIGHTBAR_BAT_GREEN, 959 + EC_ADDR_LIGHTBAR_BAT_BLUE, 960 + }; 961 + 962 + static const unsigned int uniwill_led_channel_to_ac_reg[LED_CHANNELS] = { 963 + EC_ADDR_LIGHTBAR_AC_RED, 964 + EC_ADDR_LIGHTBAR_AC_GREEN, 965 + EC_ADDR_LIGHTBAR_AC_BLUE, 966 + }; 967 + 968 + static int uniwill_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) 969 + { 970 + struct led_classdev_mc *led_mc_cdev = lcdev_to_mccdev(led_cdev); 971 + struct uniwill_data *data = container_of(led_mc_cdev, struct uniwill_data, led_mc_cdev); 972 + unsigned int value; 973 + int ret; 974 + 975 + ret = led_mc_calc_color_components(led_mc_cdev, brightness); 976 + if (ret < 0) 977 + return ret; 978 + 979 + guard(mutex)(&data->led_lock); 980 + 981 + for (int i = 0; i < LED_CHANNELS; i++) { 982 + /* Prevent the brightness values from overflowing */ 983 + value = min(LED_MAX_BRIGHTNESS, data->led_mc_subled_info[i].brightness); 984 + ret = regmap_write(data->regmap, uniwill_led_channel_to_ac_reg[i], value); 985 + if (ret < 0) 986 + return ret; 987 + 988 + ret = regmap_write(data->regmap, uniwill_led_channel_to_bat_reg[i], value); 989 + if (ret < 0) 990 + return ret; 991 + } 992 + 993 + if (brightness) 994 + value = 0; 995 + else 996 + value = LIGHTBAR_S0_OFF; 997 + 998 + ret = regmap_update_bits(data->regmap, EC_ADDR_LIGHTBAR_AC_CTRL, LIGHTBAR_S0_OFF, value); 999 + if (ret < 0) 1000 + return ret; 1001 + 1002 + return regmap_update_bits(data->regmap, EC_ADDR_LIGHTBAR_BAT_CTRL, LIGHTBAR_S0_OFF, value); 1003 + } 1004 + 1005 + #define LIGHTBAR_MASK (LIGHTBAR_APP_EXISTS | LIGHTBAR_S0_OFF | LIGHTBAR_S3_OFF | LIGHTBAR_WELCOME) 1006 + 1007 + static int uniwill_led_init(struct uniwill_data *data) 1008 + { 1009 + struct led_init_data init_data = { 1010 + .devicename = DRIVER_NAME, 1011 + .default_label = "multicolor:" LED_FUNCTION_STATUS, 1012 + .devname_mandatory = true, 1013 + }; 1014 + unsigned int color_indices[3] = { 1015 + LED_COLOR_ID_RED, 1016 + LED_COLOR_ID_GREEN, 1017 + LED_COLOR_ID_BLUE, 1018 + }; 1019 + unsigned int value; 1020 + int ret; 1021 + 1022 + if (!(supported_features & UNIWILL_FEATURE_LIGHTBAR)) 1023 + return 0; 1024 + 1025 + ret = devm_mutex_init(data->dev, &data->led_lock); 1026 + if (ret < 0) 1027 + return ret; 1028 + 1029 + /* 1030 + * The EC has separate lightbar settings for AC and battery mode, 1031 + * so we have to ensure that both settings are the same. 1032 + */ 1033 + ret = regmap_read(data->regmap, EC_ADDR_LIGHTBAR_AC_CTRL, &value); 1034 + if (ret < 0) 1035 + return ret; 1036 + 1037 + value |= LIGHTBAR_APP_EXISTS; 1038 + ret = regmap_write(data->regmap, EC_ADDR_LIGHTBAR_AC_CTRL, value); 1039 + if (ret < 0) 1040 + return ret; 1041 + 1042 + /* 1043 + * The breathing animation during suspend is not supported when 1044 + * running on battery power. 1045 + */ 1046 + value |= LIGHTBAR_S3_OFF; 1047 + ret = regmap_update_bits(data->regmap, EC_ADDR_LIGHTBAR_BAT_CTRL, LIGHTBAR_MASK, value); 1048 + if (ret < 0) 1049 + return ret; 1050 + 1051 + data->led_mc_cdev.led_cdev.color = LED_COLOR_ID_MULTI; 1052 + data->led_mc_cdev.led_cdev.max_brightness = LED_MAX_BRIGHTNESS; 1053 + data->led_mc_cdev.led_cdev.flags = LED_REJECT_NAME_CONFLICT; 1054 + data->led_mc_cdev.led_cdev.brightness_set_blocking = uniwill_led_brightness_set; 1055 + 1056 + if (value & LIGHTBAR_S0_OFF) 1057 + data->led_mc_cdev.led_cdev.brightness = 0; 1058 + else 1059 + data->led_mc_cdev.led_cdev.brightness = LED_MAX_BRIGHTNESS; 1060 + 1061 + for (int i = 0; i < LED_CHANNELS; i++) { 1062 + data->led_mc_subled_info[i].color_index = color_indices[i]; 1063 + 1064 + ret = regmap_read(data->regmap, uniwill_led_channel_to_ac_reg[i], &value); 1065 + if (ret < 0) 1066 + return ret; 1067 + 1068 + /* 1069 + * Make sure that the initial intensity value is not greater than 1070 + * the maximum brightness. 1071 + */ 1072 + value = min(LED_MAX_BRIGHTNESS, value); 1073 + ret = regmap_write(data->regmap, uniwill_led_channel_to_ac_reg[i], value); 1074 + if (ret < 0) 1075 + return ret; 1076 + 1077 + ret = regmap_write(data->regmap, uniwill_led_channel_to_bat_reg[i], value); 1078 + if (ret < 0) 1079 + return ret; 1080 + 1081 + data->led_mc_subled_info[i].intensity = value; 1082 + data->led_mc_subled_info[i].channel = i; 1083 + } 1084 + 1085 + data->led_mc_cdev.subled_info = data->led_mc_subled_info; 1086 + data->led_mc_cdev.num_colors = LED_CHANNELS; 1087 + 1088 + return devm_led_classdev_multicolor_register_ext(data->dev, &data->led_mc_cdev, 1089 + &init_data); 1090 + } 1091 + 1092 + static int uniwill_get_property(struct power_supply *psy, const struct power_supply_ext *ext, 1093 + void *drvdata, enum power_supply_property psp, 1094 + union power_supply_propval *val) 1095 + { 1096 + struct uniwill_data *data = drvdata; 1097 + union power_supply_propval prop; 1098 + unsigned int regval; 1099 + int ret; 1100 + 1101 + switch (psp) { 1102 + case POWER_SUPPLY_PROP_HEALTH: 1103 + ret = power_supply_get_property_direct(psy, POWER_SUPPLY_PROP_PRESENT, &prop); 1104 + if (ret < 0) 1105 + return ret; 1106 + 1107 + if (!prop.intval) { 1108 + val->intval = POWER_SUPPLY_HEALTH_NO_BATTERY; 1109 + return 0; 1110 + } 1111 + 1112 + ret = power_supply_get_property_direct(psy, POWER_SUPPLY_PROP_STATUS, &prop); 1113 + if (ret < 0) 1114 + return ret; 1115 + 1116 + if (prop.intval == POWER_SUPPLY_STATUS_UNKNOWN) { 1117 + val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; 1118 + return 0; 1119 + } 1120 + 1121 + ret = regmap_read(data->regmap, EC_ADDR_BAT_ALERT, &regval); 1122 + if (ret < 0) 1123 + return ret; 1124 + 1125 + if (regval) { 1126 + /* Charging issue */ 1127 + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 1128 + return 0; 1129 + } 1130 + 1131 + val->intval = POWER_SUPPLY_HEALTH_GOOD; 1132 + return 0; 1133 + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: 1134 + ret = regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &regval); 1135 + if (ret < 0) 1136 + return ret; 1137 + 1138 + val->intval = clamp_val(FIELD_GET(CHARGE_CTRL_MASK, regval), 0, 100); 1139 + return 0; 1140 + default: 1141 + return -EINVAL; 1142 + } 1143 + } 1144 + 1145 + static int uniwill_set_property(struct power_supply *psy, const struct power_supply_ext *ext, 1146 + void *drvdata, enum power_supply_property psp, 1147 + const union power_supply_propval *val) 1148 + { 1149 + struct uniwill_data *data = drvdata; 1150 + 1151 + switch (psp) { 1152 + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: 1153 + if (val->intval < 1 || val->intval > 100) 1154 + return -EINVAL; 1155 + 1156 + return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, CHARGE_CTRL_MASK, 1157 + val->intval); 1158 + default: 1159 + return -EINVAL; 1160 + } 1161 + } 1162 + 1163 + static int uniwill_property_is_writeable(struct power_supply *psy, 1164 + const struct power_supply_ext *ext, void *drvdata, 1165 + enum power_supply_property psp) 1166 + { 1167 + if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD) 1168 + return true; 1169 + 1170 + return false; 1171 + } 1172 + 1173 + static const enum power_supply_property uniwill_properties[] = { 1174 + POWER_SUPPLY_PROP_HEALTH, 1175 + POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, 1176 + }; 1177 + 1178 + static const struct power_supply_ext uniwill_extension = { 1179 + .name = DRIVER_NAME, 1180 + .properties = uniwill_properties, 1181 + .num_properties = ARRAY_SIZE(uniwill_properties), 1182 + .get_property = uniwill_get_property, 1183 + .set_property = uniwill_set_property, 1184 + .property_is_writeable = uniwill_property_is_writeable, 1185 + }; 1186 + 1187 + static int uniwill_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook) 1188 + { 1189 + struct uniwill_data *data = container_of(hook, struct uniwill_data, hook); 1190 + struct uniwill_battery_entry *entry; 1191 + int ret; 1192 + 1193 + entry = kzalloc(sizeof(*entry), GFP_KERNEL); 1194 + if (!entry) 1195 + return -ENOMEM; 1196 + 1197 + ret = power_supply_register_extension(battery, &uniwill_extension, data->dev, data); 1198 + if (ret < 0) { 1199 + kfree(entry); 1200 + return ret; 1201 + } 1202 + 1203 + guard(mutex)(&data->battery_lock); 1204 + 1205 + entry->battery = battery; 1206 + list_add(&entry->head, &data->batteries); 1207 + 1208 + return 0; 1209 + } 1210 + 1211 + static int uniwill_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook) 1212 + { 1213 + struct uniwill_data *data = container_of(hook, struct uniwill_data, hook); 1214 + struct uniwill_battery_entry *entry, *tmp; 1215 + 1216 + scoped_guard(mutex, &data->battery_lock) { 1217 + list_for_each_entry_safe(entry, tmp, &data->batteries, head) { 1218 + if (entry->battery == battery) { 1219 + list_del(&entry->head); 1220 + kfree(entry); 1221 + break; 1222 + } 1223 + } 1224 + } 1225 + 1226 + power_supply_unregister_extension(battery, &uniwill_extension); 1227 + 1228 + return 0; 1229 + } 1230 + 1231 + static int uniwill_battery_init(struct uniwill_data *data) 1232 + { 1233 + int ret; 1234 + 1235 + if (!(supported_features & UNIWILL_FEATURE_BATTERY)) 1236 + return 0; 1237 + 1238 + ret = devm_mutex_init(data->dev, &data->battery_lock); 1239 + if (ret < 0) 1240 + return ret; 1241 + 1242 + INIT_LIST_HEAD(&data->batteries); 1243 + data->hook.name = "Uniwill Battery Extension"; 1244 + data->hook.add_battery = uniwill_add_battery; 1245 + data->hook.remove_battery = uniwill_remove_battery; 1246 + 1247 + return devm_battery_hook_register(data->dev, &data->hook); 1248 + } 1249 + 1250 + static int uniwill_notifier_call(struct notifier_block *nb, unsigned long action, void *dummy) 1251 + { 1252 + struct uniwill_data *data = container_of(nb, struct uniwill_data, nb); 1253 + struct uniwill_battery_entry *entry; 1254 + 1255 + switch (action) { 1256 + case UNIWILL_OSD_BATTERY_ALERT: 1257 + mutex_lock(&data->battery_lock); 1258 + list_for_each_entry(entry, &data->batteries, head) { 1259 + power_supply_changed(entry->battery); 1260 + } 1261 + mutex_unlock(&data->battery_lock); 1262 + 1263 + return NOTIFY_OK; 1264 + case UNIWILL_OSD_DC_ADAPTER_CHANGED: 1265 + /* noop for the time being, will change once charging priority 1266 + * gets implemented. 1267 + */ 1268 + 1269 + return NOTIFY_OK; 1270 + default: 1271 + mutex_lock(&data->input_lock); 1272 + sparse_keymap_report_event(data->input_device, action, 1, true); 1273 + mutex_unlock(&data->input_lock); 1274 + 1275 + return NOTIFY_OK; 1276 + } 1277 + } 1278 + 1279 + static int uniwill_input_init(struct uniwill_data *data) 1280 + { 1281 + int ret; 1282 + 1283 + ret = devm_mutex_init(data->dev, &data->input_lock); 1284 + if (ret < 0) 1285 + return ret; 1286 + 1287 + data->input_device = devm_input_allocate_device(data->dev); 1288 + if (!data->input_device) 1289 + return -ENOMEM; 1290 + 1291 + ret = sparse_keymap_setup(data->input_device, uniwill_keymap, NULL); 1292 + if (ret < 0) 1293 + return ret; 1294 + 1295 + data->input_device->name = "Uniwill WMI hotkeys"; 1296 + data->input_device->phys = "wmi/input0"; 1297 + data->input_device->id.bustype = BUS_HOST; 1298 + ret = input_register_device(data->input_device); 1299 + if (ret < 0) 1300 + return ret; 1301 + 1302 + data->nb.notifier_call = uniwill_notifier_call; 1303 + 1304 + return devm_uniwill_wmi_register_notifier(data->dev, &data->nb); 1305 + } 1306 + 1307 + static void uniwill_disable_manual_control(void *context) 1308 + { 1309 + struct uniwill_data *data = context; 1310 + 1311 + regmap_clear_bits(data->regmap, EC_ADDR_AP_OEM, ENABLE_MANUAL_CTRL); 1312 + } 1313 + 1314 + static int uniwill_ec_init(struct uniwill_data *data) 1315 + { 1316 + unsigned int value; 1317 + int ret; 1318 + 1319 + ret = regmap_read(data->regmap, EC_ADDR_PROJECT_ID, &value); 1320 + if (ret < 0) 1321 + return ret; 1322 + 1323 + dev_dbg(data->dev, "Project ID: %u\n", value); 1324 + 1325 + ret = regmap_set_bits(data->regmap, EC_ADDR_AP_OEM, ENABLE_MANUAL_CTRL); 1326 + if (ret < 0) 1327 + return ret; 1328 + 1329 + return devm_add_action_or_reset(data->dev, uniwill_disable_manual_control, data); 1330 + } 1331 + 1332 + static int uniwill_probe(struct platform_device *pdev) 1333 + { 1334 + struct uniwill_data *data; 1335 + struct regmap *regmap; 1336 + acpi_handle handle; 1337 + int ret; 1338 + 1339 + handle = ACPI_HANDLE(&pdev->dev); 1340 + if (!handle) 1341 + return -ENODEV; 1342 + 1343 + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 1344 + if (!data) 1345 + return -ENOMEM; 1346 + 1347 + data->dev = &pdev->dev; 1348 + data->handle = handle; 1349 + platform_set_drvdata(pdev, data); 1350 + 1351 + regmap = devm_regmap_init(&pdev->dev, &uniwill_ec_bus, data, &uniwill_ec_config); 1352 + if (IS_ERR(regmap)) 1353 + return PTR_ERR(regmap); 1354 + 1355 + data->regmap = regmap; 1356 + ret = devm_mutex_init(&pdev->dev, &data->super_key_lock); 1357 + if (ret < 0) 1358 + return ret; 1359 + 1360 + ret = uniwill_ec_init(data); 1361 + if (ret < 0) 1362 + return ret; 1363 + 1364 + ret = uniwill_battery_init(data); 1365 + if (ret < 0) 1366 + return ret; 1367 + 1368 + ret = uniwill_led_init(data); 1369 + if (ret < 0) 1370 + return ret; 1371 + 1372 + ret = uniwill_hwmon_init(data); 1373 + if (ret < 0) 1374 + return ret; 1375 + 1376 + return uniwill_input_init(data); 1377 + } 1378 + 1379 + static void uniwill_shutdown(struct platform_device *pdev) 1380 + { 1381 + struct uniwill_data *data = platform_get_drvdata(pdev); 1382 + 1383 + regmap_clear_bits(data->regmap, EC_ADDR_AP_OEM, ENABLE_MANUAL_CTRL); 1384 + } 1385 + 1386 + static int uniwill_suspend_keyboard(struct uniwill_data *data) 1387 + { 1388 + if (!(supported_features & UNIWILL_FEATURE_SUPER_KEY_TOGGLE)) 1389 + return 0; 1390 + 1391 + /* 1392 + * The EC_ADDR_SWITCH_STATUS is marked as volatile, so we have to restore it 1393 + * ourselves. 1394 + */ 1395 + return regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &data->last_switch_status); 1396 + } 1397 + 1398 + static int uniwill_suspend_battery(struct uniwill_data *data) 1399 + { 1400 + if (!(supported_features & UNIWILL_FEATURE_BATTERY)) 1401 + return 0; 1402 + 1403 + /* 1404 + * Save the current charge limit in order to restore it during resume. 1405 + * We cannot use the regmap code for that since this register needs to 1406 + * be declared as volatile due to CHARGE_CTRL_REACHED. 1407 + */ 1408 + return regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &data->last_charge_ctrl); 1409 + } 1410 + 1411 + static int uniwill_suspend(struct device *dev) 1412 + { 1413 + struct uniwill_data *data = dev_get_drvdata(dev); 1414 + int ret; 1415 + 1416 + ret = uniwill_suspend_keyboard(data); 1417 + if (ret < 0) 1418 + return ret; 1419 + 1420 + ret = uniwill_suspend_battery(data); 1421 + if (ret < 0) 1422 + return ret; 1423 + 1424 + regcache_cache_only(data->regmap, true); 1425 + regcache_mark_dirty(data->regmap); 1426 + 1427 + return 0; 1428 + } 1429 + 1430 + static int uniwill_resume_keyboard(struct uniwill_data *data) 1431 + { 1432 + unsigned int value; 1433 + int ret; 1434 + 1435 + if (!(supported_features & UNIWILL_FEATURE_SUPER_KEY_TOGGLE)) 1436 + return 0; 1437 + 1438 + ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value); 1439 + if (ret < 0) 1440 + return ret; 1441 + 1442 + if ((data->last_switch_status & SUPER_KEY_LOCK_STATUS) == (value & SUPER_KEY_LOCK_STATUS)) 1443 + return 0; 1444 + 1445 + return regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, TRIGGER_SUPER_KEY_LOCK, 1446 + TRIGGER_SUPER_KEY_LOCK); 1447 + } 1448 + 1449 + static int uniwill_resume_battery(struct uniwill_data *data) 1450 + { 1451 + if (!(supported_features & UNIWILL_FEATURE_BATTERY)) 1452 + return 0; 1453 + 1454 + return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, CHARGE_CTRL_MASK, 1455 + data->last_charge_ctrl); 1456 + } 1457 + 1458 + static int uniwill_resume(struct device *dev) 1459 + { 1460 + struct uniwill_data *data = dev_get_drvdata(dev); 1461 + int ret; 1462 + 1463 + regcache_cache_only(data->regmap, false); 1464 + 1465 + ret = regcache_sync(data->regmap); 1466 + if (ret < 0) 1467 + return ret; 1468 + 1469 + ret = uniwill_resume_keyboard(data); 1470 + if (ret < 0) 1471 + return ret; 1472 + 1473 + return uniwill_resume_battery(data); 1474 + } 1475 + 1476 + static DEFINE_SIMPLE_DEV_PM_OPS(uniwill_pm_ops, uniwill_suspend, uniwill_resume); 1477 + 1478 + /* 1479 + * We only use the DMI table for auoloading because the ACPI device itself 1480 + * does not guarantee that the underlying EC implementation is supported. 1481 + */ 1482 + static const struct acpi_device_id uniwill_id_table[] = { 1483 + { "INOU0000" }, 1484 + { }, 1485 + }; 1486 + 1487 + static struct platform_driver uniwill_driver = { 1488 + .driver = { 1489 + .name = DRIVER_NAME, 1490 + .dev_groups = uniwill_groups, 1491 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 1492 + .acpi_match_table = uniwill_id_table, 1493 + .pm = pm_sleep_ptr(&uniwill_pm_ops), 1494 + }, 1495 + .probe = uniwill_probe, 1496 + .shutdown = uniwill_shutdown, 1497 + }; 1498 + 1499 + static const struct dmi_system_id uniwill_dmi_table[] __initconst = { 1500 + { 1501 + .ident = "XMG FUSION 15", 1502 + .matches = { 1503 + DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"), 1504 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71A"), 1505 + }, 1506 + }, 1507 + { 1508 + .ident = "XMG FUSION 15", 1509 + .matches = { 1510 + DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"), 1511 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71B"), 1512 + }, 1513 + }, 1514 + { 1515 + .ident = "Intel NUC x15", 1516 + .matches = { 1517 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"), 1518 + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LAPAC71H"), 1519 + }, 1520 + .driver_data = (void *)(UNIWILL_FEATURE_FN_LOCK_TOGGLE | 1521 + UNIWILL_FEATURE_SUPER_KEY_TOGGLE | 1522 + UNIWILL_FEATURE_TOUCHPAD_TOGGLE | 1523 + UNIWILL_FEATURE_BATTERY | 1524 + UNIWILL_FEATURE_HWMON), 1525 + }, 1526 + { 1527 + .ident = "Intel NUC x15", 1528 + .matches = { 1529 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"), 1530 + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LAPKC71F"), 1531 + }, 1532 + .driver_data = (void *)(UNIWILL_FEATURE_FN_LOCK_TOGGLE | 1533 + UNIWILL_FEATURE_SUPER_KEY_TOGGLE | 1534 + UNIWILL_FEATURE_TOUCHPAD_TOGGLE | 1535 + UNIWILL_FEATURE_LIGHTBAR | 1536 + UNIWILL_FEATURE_BATTERY | 1537 + UNIWILL_FEATURE_HWMON), 1538 + }, 1539 + { 1540 + .ident = "TUXEDO InfinityBook Pro 14 Gen6 Intel", 1541 + .matches = { 1542 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1543 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxTxX1"), 1544 + }, 1545 + }, 1546 + { 1547 + .ident = "TUXEDO InfinityBook Pro 14 Gen6 Intel", 1548 + .matches = { 1549 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1550 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxTQx1"), 1551 + }, 1552 + }, 1553 + { 1554 + .ident = "TUXEDO InfinityBook Pro 14/16 Gen7 Intel", 1555 + .matches = { 1556 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1557 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxARX1_PHxAQF1"), 1558 + }, 1559 + }, 1560 + { 1561 + .ident = "TUXEDO InfinityBook Pro 16 Gen7 Intel/Commodore Omnia-Book Pro Gen 7", 1562 + .matches = { 1563 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1564 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH6AG01_PH6AQ71_PH6AQI1"), 1565 + }, 1566 + }, 1567 + { 1568 + .ident = "TUXEDO InfinityBook Pro 14/16 Gen8 Intel/Commodore Omnia-Book Pro Gen 8", 1569 + .matches = { 1570 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1571 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH4PRX1_PH6PRX1"), 1572 + }, 1573 + }, 1574 + { 1575 + .ident = "TUXEDO InfinityBook Pro 14 Gen8 Intel/Commodore Omnia-Book Pro Gen 8", 1576 + .matches = { 1577 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1578 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH4PG31"), 1579 + }, 1580 + }, 1581 + { 1582 + .ident = "TUXEDO InfinityBook Pro 16 Gen8 Intel", 1583 + .matches = { 1584 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1585 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH6PG01_PH6PG71"), 1586 + }, 1587 + }, 1588 + { 1589 + .ident = "TUXEDO InfinityBook Pro 14/15 Gen9 AMD", 1590 + .matches = { 1591 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1592 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GXxHRXx"), 1593 + }, 1594 + }, 1595 + { 1596 + .ident = "TUXEDO InfinityBook Pro 14/15 Gen9 Intel/Commodore Omnia-Book 15 Gen9", 1597 + .matches = { 1598 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1599 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GXxMRXx"), 1600 + }, 1601 + }, 1602 + { 1603 + .ident = "TUXEDO InfinityBook Pro 14/15 Gen10 AMD", 1604 + .matches = { 1605 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1606 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxHP4NAx"), 1607 + }, 1608 + }, 1609 + { 1610 + .ident = "TUXEDO InfinityBook Pro 14/15 Gen10 AMD", 1611 + .matches = { 1612 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1613 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxKK4NAx_XxSP4NAx"), 1614 + }, 1615 + }, 1616 + { 1617 + .ident = "TUXEDO InfinityBook Pro 15 Gen10 Intel", 1618 + .matches = { 1619 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1620 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxAR4NAx"), 1621 + }, 1622 + }, 1623 + { 1624 + .ident = "TUXEDO InfinityBook Max 15 Gen10 AMD", 1625 + .matches = { 1626 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1627 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "X5KK45xS_X5SP45xS"), 1628 + }, 1629 + }, 1630 + { 1631 + .ident = "TUXEDO InfinityBook Max 16 Gen10 AMD", 1632 + .matches = { 1633 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1634 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6HP45xU"), 1635 + }, 1636 + }, 1637 + { 1638 + .ident = "TUXEDO InfinityBook Max 16 Gen10 AMD", 1639 + .matches = { 1640 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1641 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6KK45xU_X6SP45xU"), 1642 + }, 1643 + }, 1644 + { 1645 + .ident = "TUXEDO InfinityBook Max 15 Gen10 Intel", 1646 + .matches = { 1647 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1648 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "X5AR45xS"), 1649 + }, 1650 + }, 1651 + { 1652 + .ident = "TUXEDO InfinityBook Max 16 Gen10 Intel", 1653 + .matches = { 1654 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1655 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR55xU"), 1656 + }, 1657 + }, 1658 + { 1659 + .ident = "TUXEDO Polaris 15 Gen1 AMD", 1660 + .matches = { 1661 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1662 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501A1650TI"), 1663 + }, 1664 + }, 1665 + { 1666 + .ident = "TUXEDO Polaris 15 Gen1 AMD", 1667 + .matches = { 1668 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1669 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501A2060"), 1670 + }, 1671 + }, 1672 + { 1673 + .ident = "TUXEDO Polaris 17 Gen1 AMD", 1674 + .matches = { 1675 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1676 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701A1650TI"), 1677 + }, 1678 + }, 1679 + { 1680 + .ident = "TUXEDO Polaris 17 Gen1 AMD", 1681 + .matches = { 1682 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1683 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701A2060"), 1684 + }, 1685 + }, 1686 + { 1687 + .ident = "TUXEDO Polaris 15 Gen1 Intel", 1688 + .matches = { 1689 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1690 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501I1650TI"), 1691 + }, 1692 + }, 1693 + { 1694 + .ident = "TUXEDO Polaris 15 Gen1 Intel", 1695 + .matches = { 1696 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1697 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501I2060"), 1698 + }, 1699 + }, 1700 + { 1701 + .ident = "TUXEDO Polaris 17 Gen1 Intel", 1702 + .matches = { 1703 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1704 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701I1650TI"), 1705 + }, 1706 + }, 1707 + { 1708 + .ident = "TUXEDO Polaris 17 Gen1 Intel", 1709 + .matches = { 1710 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1711 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701I2060"), 1712 + }, 1713 + }, 1714 + { 1715 + .ident = "TUXEDO Trinity 15 Intel Gen1", 1716 + .matches = { 1717 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1718 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "TRINITY1501I"), 1719 + }, 1720 + }, 1721 + { 1722 + .ident = "TUXEDO Trinity 17 Intel Gen1", 1723 + .matches = { 1724 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1725 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "TRINITY1701I"), 1726 + }, 1727 + }, 1728 + { 1729 + .ident = "TUXEDO Polaris 15/17 Gen2 AMD", 1730 + .matches = { 1731 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1732 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxMGxx"), 1733 + }, 1734 + }, 1735 + { 1736 + .ident = "TUXEDO Polaris 15/17 Gen2 Intel", 1737 + .matches = { 1738 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1739 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxNGxx"), 1740 + }, 1741 + }, 1742 + { 1743 + .ident = "TUXEDO Stellaris/Polaris 15/17 Gen3 AMD", 1744 + .matches = { 1745 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1746 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxZGxx"), 1747 + }, 1748 + }, 1749 + { 1750 + .ident = "TUXEDO Stellaris/Polaris 15/17 Gen3 Intel", 1751 + .matches = { 1752 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1753 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxTGxx"), 1754 + }, 1755 + }, 1756 + { 1757 + .ident = "TUXEDO Stellaris/Polaris 15/17 Gen4 AMD", 1758 + .matches = { 1759 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1760 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxRGxx"), 1761 + }, 1762 + }, 1763 + { 1764 + .ident = "TUXEDO Stellaris 15 Gen4 Intel", 1765 + .matches = { 1766 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1767 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxAGxx"), 1768 + }, 1769 + }, 1770 + { 1771 + .ident = "TUXEDO Polaris 15/17 Gen5 AMD", 1772 + .matches = { 1773 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1774 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxXGxx"), 1775 + }, 1776 + }, 1777 + { 1778 + .ident = "TUXEDO Stellaris 16 Gen5 AMD", 1779 + .matches = { 1780 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1781 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6XGxX"), 1782 + }, 1783 + }, 1784 + { 1785 + .ident = "TUXEDO Stellaris 16/17 Gen5 Intel/Commodore ORION Gen 5", 1786 + .matches = { 1787 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1788 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxPXxx"), 1789 + }, 1790 + }, 1791 + { 1792 + .ident = "TUXEDO Stellaris Slim 15 Gen6 AMD", 1793 + .matches = { 1794 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1795 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxHGxx"), 1796 + }, 1797 + }, 1798 + { 1799 + .ident = "TUXEDO Stellaris Slim 15 Gen6 Intel/Commodore ORION Slim 15 Gen6", 1800 + .matches = { 1801 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1802 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM5IXxA"), 1803 + }, 1804 + }, 1805 + { 1806 + .ident = "TUXEDO Stellaris 16 Gen6 Intel/Commodore ORION 16 Gen6", 1807 + .matches = { 1808 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1809 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6IXxB_MB1"), 1810 + }, 1811 + }, 1812 + { 1813 + .ident = "TUXEDO Stellaris 16 Gen6 Intel/Commodore ORION 16 Gen6", 1814 + .matches = { 1815 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1816 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6IXxB_MB2"), 1817 + }, 1818 + }, 1819 + { 1820 + .ident = "TUXEDO Stellaris 17 Gen6 Intel/Commodore ORION 17 Gen6", 1821 + .matches = { 1822 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1823 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM7IXxN"), 1824 + }, 1825 + }, 1826 + { 1827 + .ident = "TUXEDO Stellaris 16 Gen7 AMD", 1828 + .matches = { 1829 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1830 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6FR5xxY"), 1831 + }, 1832 + }, 1833 + { 1834 + .ident = "TUXEDO Stellaris 16 Gen7 Intel", 1835 + .matches = { 1836 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1837 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR5xxY"), 1838 + }, 1839 + }, 1840 + { 1841 + .ident = "TUXEDO Stellaris 16 Gen7 Intel", 1842 + .matches = { 1843 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1844 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR5xxY_mLED"), 1845 + }, 1846 + }, 1847 + { 1848 + .ident = "TUXEDO Pulse 14 Gen1 AMD", 1849 + .matches = { 1850 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1851 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "PULSE1401"), 1852 + }, 1853 + }, 1854 + { 1855 + .ident = "TUXEDO Pulse 15 Gen1 AMD", 1856 + .matches = { 1857 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1858 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "PULSE1501"), 1859 + }, 1860 + }, 1861 + { 1862 + .ident = "TUXEDO Pulse 15 Gen2 AMD", 1863 + .matches = { 1864 + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), 1865 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "PF5LUXG"), 1866 + }, 1867 + }, 1868 + { } 1869 + }; 1870 + MODULE_DEVICE_TABLE(dmi, uniwill_dmi_table); 1871 + 1872 + static int __init uniwill_init(void) 1873 + { 1874 + const struct dmi_system_id *id; 1875 + int ret; 1876 + 1877 + id = dmi_first_match(uniwill_dmi_table); 1878 + if (!id) { 1879 + if (!force) 1880 + return -ENODEV; 1881 + 1882 + /* Assume that the device supports all features */ 1883 + supported_features = UINT_MAX; 1884 + pr_warn("Loading on a potentially unsupported device\n"); 1885 + } else { 1886 + supported_features = (uintptr_t)id->driver_data; 1887 + } 1888 + 1889 + ret = platform_driver_register(&uniwill_driver); 1890 + if (ret < 0) 1891 + return ret; 1892 + 1893 + ret = uniwill_wmi_register_driver(); 1894 + if (ret < 0) { 1895 + platform_driver_unregister(&uniwill_driver); 1896 + return ret; 1897 + } 1898 + 1899 + return 0; 1900 + } 1901 + module_init(uniwill_init); 1902 + 1903 + static void __exit uniwill_exit(void) 1904 + { 1905 + uniwill_wmi_unregister_driver(); 1906 + platform_driver_unregister(&uniwill_driver); 1907 + } 1908 + module_exit(uniwill_exit); 1909 + 1910 + MODULE_AUTHOR("Armin Wolf <W_Armin@gmx.de>"); 1911 + MODULE_DESCRIPTION("Uniwill notebook driver"); 1912 + MODULE_LICENSE("GPL");
+92
drivers/platform/x86/uniwill/uniwill-wmi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Linux hotkey driver for Uniwill notebooks. 4 + * 5 + * Special thanks go to Pőcze Barnabás, Christoffer Sandberg and Werner Sembach 6 + * for supporting the development of this driver either through prior work or 7 + * by answering questions regarding the underlying WMI interface. 8 + * 9 + * Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de> 10 + */ 11 + 12 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13 + 14 + #include <linux/acpi.h> 15 + #include <linux/device.h> 16 + #include <linux/init.h> 17 + #include <linux/mod_devicetable.h> 18 + #include <linux/notifier.h> 19 + #include <linux/printk.h> 20 + #include <linux/types.h> 21 + #include <linux/wmi.h> 22 + 23 + #include "uniwill-wmi.h" 24 + 25 + #define DRIVER_NAME "uniwill-wmi" 26 + #define UNIWILL_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" 27 + 28 + static BLOCKING_NOTIFIER_HEAD(uniwill_wmi_chain_head); 29 + 30 + static void devm_uniwill_wmi_unregister_notifier(void *data) 31 + { 32 + struct notifier_block *nb = data; 33 + 34 + blocking_notifier_chain_unregister(&uniwill_wmi_chain_head, nb); 35 + } 36 + 37 + int devm_uniwill_wmi_register_notifier(struct device *dev, struct notifier_block *nb) 38 + { 39 + int ret; 40 + 41 + ret = blocking_notifier_chain_register(&uniwill_wmi_chain_head, nb); 42 + if (ret < 0) 43 + return ret; 44 + 45 + return devm_add_action_or_reset(dev, devm_uniwill_wmi_unregister_notifier, nb); 46 + } 47 + 48 + static void uniwill_wmi_notify(struct wmi_device *wdev, union acpi_object *obj) 49 + { 50 + u32 value; 51 + 52 + if (obj->type != ACPI_TYPE_INTEGER) 53 + return; 54 + 55 + value = obj->integer.value; 56 + 57 + dev_dbg(&wdev->dev, "Received WMI event %u\n", value); 58 + 59 + blocking_notifier_call_chain(&uniwill_wmi_chain_head, value, NULL); 60 + } 61 + 62 + /* 63 + * We cannot fully trust this GUID since Uniwill just copied the WMI GUID 64 + * from the Windows driver example, and others probably did the same. 65 + * 66 + * Because of this we cannot use this WMI GUID for autoloading. Instead the 67 + * associated driver will be registered manually after matching a DMI table. 68 + */ 69 + static const struct wmi_device_id uniwill_wmi_id_table[] = { 70 + { UNIWILL_EVENT_GUID, NULL }, 71 + { } 72 + }; 73 + 74 + static struct wmi_driver uniwill_wmi_driver = { 75 + .driver = { 76 + .name = DRIVER_NAME, 77 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 78 + }, 79 + .id_table = uniwill_wmi_id_table, 80 + .notify = uniwill_wmi_notify, 81 + .no_singleton = true, 82 + }; 83 + 84 + int __init uniwill_wmi_register_driver(void) 85 + { 86 + return wmi_driver_register(&uniwill_wmi_driver); 87 + } 88 + 89 + void __exit uniwill_wmi_unregister_driver(void) 90 + { 91 + wmi_driver_unregister(&uniwill_wmi_driver); 92 + }
+129
drivers/platform/x86/uniwill/uniwill-wmi.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + /* 3 + * Linux hotkey driver for Uniwill notebooks. 4 + * 5 + * Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de> 6 + */ 7 + 8 + #ifndef UNIWILL_WMI_H 9 + #define UNIWILL_WMI_H 10 + 11 + #include <linux/init.h> 12 + 13 + #define UNIWILL_OSD_CAPSLOCK 0x01 14 + #define UNIWILL_OSD_NUMLOCK 0x02 15 + #define UNIWILL_OSD_SCROLLLOCK 0x03 16 + 17 + #define UNIWILL_OSD_TOUCHPAD_ON 0x04 18 + #define UNIWILL_OSD_TOUCHPAD_OFF 0x05 19 + 20 + #define UNIWILL_OSD_SILENT_MODE_ON 0x06 21 + #define UNIWILL_OSD_SILENT_MODE_OFF 0x07 22 + 23 + #define UNIWILL_OSD_WLAN_ON 0x08 24 + #define UNIWILL_OSD_WLAN_OFF 0x09 25 + 26 + #define UNIWILL_OSD_WIMAX_ON 0x0A 27 + #define UNIWILL_OSD_WIMAX_OFF 0x0B 28 + 29 + #define UNIWILL_OSD_BLUETOOTH_ON 0x0C 30 + #define UNIWILL_OSD_BLUETOOTH_OFF 0x0D 31 + 32 + #define UNIWILL_OSD_RF_ON 0x0E 33 + #define UNIWILL_OSD_RF_OFF 0x0F 34 + 35 + #define UNIWILL_OSD_3G_ON 0x10 36 + #define UNIWILL_OSD_3G_OFF 0x11 37 + 38 + #define UNIWILL_OSD_WEBCAM_ON 0x12 39 + #define UNIWILL_OSD_WEBCAM_OFF 0x13 40 + 41 + #define UNIWILL_OSD_BRIGHTNESSUP 0x14 42 + #define UNIWILL_OSD_BRIGHTNESSDOWN 0x15 43 + 44 + #define UNIWILL_OSD_RADIOON 0x1A 45 + #define UNIWILL_OSD_RADIOOFF 0x1B 46 + 47 + #define UNIWILL_OSD_POWERSAVE_ON 0x31 48 + #define UNIWILL_OSD_POWERSAVE_OFF 0x32 49 + 50 + #define UNIWILL_OSD_MENU 0x34 51 + 52 + #define UNIWILL_OSD_MUTE 0x35 53 + #define UNIWILL_OSD_VOLUMEDOWN 0x36 54 + #define UNIWILL_OSD_VOLUMEUP 0x37 55 + 56 + #define UNIWILL_OSD_MENU_2 0x38 57 + 58 + #define UNIWILL_OSD_LIGHTBAR_ON 0x39 59 + #define UNIWILL_OSD_LIGHTBAR_OFF 0x3A 60 + 61 + #define UNIWILL_OSD_KB_LED_LEVEL0 0x3B 62 + #define UNIWILL_OSD_KB_LED_LEVEL1 0x3C 63 + #define UNIWILL_OSD_KB_LED_LEVEL2 0x3D 64 + #define UNIWILL_OSD_KB_LED_LEVEL3 0x3E 65 + #define UNIWILL_OSD_KB_LED_LEVEL4 0x3F 66 + 67 + #define UNIWILL_OSD_SUPER_KEY_LOCK_ENABLE 0x40 68 + #define UNIWILL_OSD_SUPER_KEY_LOCK_DISABLE 0x41 69 + 70 + #define UNIWILL_OSD_MENU_JP 0x42 71 + 72 + #define UNIWILL_OSD_CAMERA_ON 0x90 73 + #define UNIWILL_OSD_CAMERA_OFF 0x91 74 + 75 + #define UNIWILL_OSD_RFKILL 0xA4 76 + 77 + #define UNIWILL_OSD_SUPER_KEY_LOCK_CHANGED 0xA5 78 + 79 + #define UNIWILL_OSD_LIGHTBAR_STATE_CHANGED 0xA6 80 + 81 + #define UNIWILL_OSD_FAN_BOOST_STATE_CHANGED 0xA7 82 + 83 + #define UNIWILL_OSD_LCD_SW 0xA9 84 + 85 + #define UNIWILL_OSD_FAN_OVERTEMP 0xAA 86 + 87 + #define UNIWILL_OSD_DC_ADAPTER_CHANGED 0xAB 88 + 89 + #define UNIWILL_OSD_BAT_HP_OFF 0xAC 90 + 91 + #define UNIWILL_OSD_FAN_DOWN_TEMP 0xAD 92 + 93 + #define UNIWILL_OSD_BATTERY_ALERT 0xAE 94 + 95 + #define UNIWILL_OSD_TIMAP_HAIERLB_SW 0xAF 96 + 97 + #define UNIWILL_OSD_PERFORMANCE_MODE_TOGGLE 0xB0 98 + 99 + #define UNIWILL_OSD_KBDILLUMDOWN 0xB1 100 + #define UNIWILL_OSD_KBDILLUMUP 0xB2 101 + 102 + #define UNIWILL_OSD_BACKLIGHT_LEVEL_CHANGE 0xB3 103 + #define UNIWILL_OSD_BACKLIGHT_POWER_CHANGE 0xB4 104 + 105 + #define UNIWILL_OSD_MIC_MUTE 0xB7 106 + 107 + #define UNIWILL_OSD_FN_LOCK 0xB8 108 + #define UNIWILL_OSD_KBDILLUMTOGGLE 0xB9 109 + 110 + #define UNIWILL_OSD_BAT_CHARGE_FULL_24_H 0xBE 111 + 112 + #define UNIWILL_OSD_BAT_ERM_UPDATE 0xBF 113 + 114 + #define UNIWILL_OSD_BENCHMARK_MODE_TOGGLE 0xC0 115 + 116 + #define UNIWILL_OSD_WEBCAM_TOGGLE 0xCF 117 + 118 + #define UNIWILL_OSD_KBD_BACKLIGHT_CHANGED 0xF0 119 + 120 + struct device; 121 + struct notifier_block; 122 + 123 + int devm_uniwill_wmi_register_notifier(struct device *dev, struct notifier_block *nb); 124 + 125 + int __init uniwill_wmi_register_driver(void); 126 + 127 + void __exit uniwill_wmi_unregister_driver(void); 128 + 129 + #endif /* UNIWILL_WMI_H */
+20 -14
drivers/platform/x86/wmi.c drivers/platform/wmi/core.c
··· 142 142 buffer[4] = '\0'; 143 143 } 144 144 145 - static inline acpi_object_type get_param_acpi_type(const struct wmi_block *wblock) 146 - { 147 - if (wblock->gblock.flags & ACPI_WMI_STRING) 148 - return ACPI_TYPE_STRING; 149 - else 150 - return ACPI_TYPE_BUFFER; 151 - } 152 - 153 145 static int wmidev_match_guid(struct device *dev, const void *data) 154 146 { 155 147 struct wmi_block *wblock = dev_to_wblock(dev); ··· 343 351 params[0].integer.value = instance; 344 352 params[1].type = ACPI_TYPE_INTEGER; 345 353 params[1].integer.value = method_id; 346 - params[2].type = get_param_acpi_type(wblock); 347 - params[2].buffer.length = in->length; 348 - params[2].buffer.pointer = in->pointer; 354 + 355 + if (wblock->gblock.flags & ACPI_WMI_STRING) { 356 + params[2].type = ACPI_TYPE_STRING; 357 + params[2].string.length = in->length; 358 + params[2].string.pointer = in->pointer; 359 + } else { 360 + params[2].type = ACPI_TYPE_BUFFER; 361 + params[2].buffer.length = in->length; 362 + params[2].buffer.pointer = in->pointer; 363 + } 349 364 350 365 get_acpi_method_name(wblock, 'M', method); 351 366 ··· 518 519 input.pointer = params; 519 520 params[0].type = ACPI_TYPE_INTEGER; 520 521 params[0].integer.value = instance; 521 - params[1].type = get_param_acpi_type(wblock); 522 - params[1].buffer.length = in->length; 523 - params[1].buffer.pointer = in->pointer; 522 + 523 + if (wblock->gblock.flags & ACPI_WMI_STRING) { 524 + params[1].type = ACPI_TYPE_STRING; 525 + params[1].string.length = in->length; 526 + params[1].string.pointer = in->pointer; 527 + } else { 528 + params[1].type = ACPI_TYPE_BUFFER; 529 + params[1].buffer.length = in->length; 530 + params[1].buffer.pointer = in->pointer; 531 + } 524 532 525 533 get_acpi_method_name(wblock, 'S', method); 526 534
+1 -1
drivers/platform/x86/x86-android-tablets/lenovo.c
··· 543 543 544 544 ret = device_add_software_node(codec_dev, &lenovo_yoga_tab2_830_1050_wm5102); 545 545 if (ret) { 546 - ret = dev_err_probe(codec_dev, ret, "adding software node\n"); 546 + dev_err_probe(codec_dev, ret, "adding software node\n"); 547 547 goto err_put_pinctrl; 548 548 } 549 549
+1 -1
drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c
··· 183 183 struct atla10_ec_data *data = power_supply_get_drvdata(psy); 184 184 185 185 /* After charger plug in/out wait 0.5s for things to stabilize */ 186 - mod_delayed_work(system_wq, &data->work, HZ / 2); 186 + mod_delayed_work(system_percpu_wq, &data->work, HZ / 2); 187 187 } 188 188 189 189 static const enum power_supply_property atla10_ec_psy_props[] = {
+19 -8
fs/nls/nls_base.c
··· 67 67 l &= t->lmask; 68 68 if (l < t->lval || l > UNICODE_MAX || 69 69 (l & SURROGATE_MASK) == SURROGATE_PAIR) 70 - return -1; 70 + return -EILSEQ; 71 + 71 72 *pu = (unicode_t) l; 72 73 return nc; 73 74 } 74 75 if (inlen <= nc) 75 - return -1; 76 + return -EOVERFLOW; 77 + 76 78 s++; 77 79 c = (*s ^ 0x80) & 0xFF; 78 80 if (c & 0xC0) 79 - return -1; 81 + return -EILSEQ; 82 + 80 83 l = (l << 6) | c; 81 84 } 82 - return -1; 85 + return -EILSEQ; 83 86 } 84 87 EXPORT_SYMBOL(utf8_to_utf32); 85 88 ··· 97 94 98 95 l = u; 99 96 if (l > UNICODE_MAX || (l & SURROGATE_MASK) == SURROGATE_PAIR) 100 - return -1; 97 + return -EILSEQ; 101 98 102 99 nc = 0; 103 100 for (t = utf8_table; t->cmask && maxout; t++, maxout--) { ··· 113 110 return nc; 114 111 } 115 112 } 116 - return -1; 113 + return -EOVERFLOW; 117 114 } 118 115 EXPORT_SYMBOL(utf32_to_utf8); 119 116 ··· 220 217 inlen--; 221 218 } 222 219 size = utf32_to_utf8(u, op, maxout); 223 - if (size == -1) { 224 - /* Ignore character and move on */ 220 + if (size < 0) { 221 + if (size == -EILSEQ) { 222 + /* Ignore character and move on */ 223 + continue; 224 + } 225 + /* 226 + * Stop filling the buffer with data once a character 227 + * does not fit anymore. 228 + */ 229 + break; 225 230 } else { 226 231 op += size; 227 232 maxout -= size;
+50
include/linux/platform_data/x86/asus-wmi-leds-ids.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H 3 + #define __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H 4 + 5 + #include <linux/dmi.h> 6 + #include <linux/types.h> 7 + 8 + /* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */ 9 + #if IS_REACHABLE(CONFIG_ASUS_WMI) || IS_REACHABLE(CONFIG_HID_ASUS) 10 + static const struct dmi_system_id asus_use_hid_led_dmi_ids[] = { 11 + { 12 + .matches = { 13 + DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Zephyrus"), 14 + }, 15 + }, 16 + { 17 + .matches = { 18 + DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Strix"), 19 + }, 20 + }, 21 + { 22 + .matches = { 23 + DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Flow"), 24 + }, 25 + }, 26 + { 27 + .matches = { 28 + DMI_MATCH(DMI_PRODUCT_FAMILY, "ProArt P16"), 29 + }, 30 + }, 31 + { 32 + .matches = { 33 + DMI_MATCH(DMI_BOARD_NAME, "GA403U"), 34 + }, 35 + }, 36 + { 37 + .matches = { 38 + DMI_MATCH(DMI_BOARD_NAME, "GU605M"), 39 + }, 40 + }, 41 + { 42 + .matches = { 43 + DMI_MATCH(DMI_BOARD_NAME, "RC71L"), 44 + }, 45 + }, 46 + { }, 47 + }; 48 + #endif 49 + 50 + #endif /* __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H */
+16 -42
include/linux/platform_data/x86/asus-wmi.h
··· 4 4 5 5 #include <linux/errno.h> 6 6 #include <linux/types.h> 7 - #include <linux/dmi.h> 7 + 8 + #define ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" 9 + #define ASUS_ACPI_UID_ASUSWMI "ASUSWMI" 8 10 9 11 /* WMI Methods */ 10 12 #define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */ ··· 75 73 #define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO 0x00110019 76 74 77 75 /* Misc */ 76 + #define ASUS_WMI_DEVID_PANEL_HD 0x0005001C 78 77 #define ASUS_WMI_DEVID_PANEL_OD 0x00050019 79 78 #define ASUS_WMI_DEVID_CAMERA 0x00060013 80 79 #define ASUS_WMI_DEVID_LID_FLIP 0x00060062 81 80 #define ASUS_WMI_DEVID_LID_FLIP_ROG 0x00060077 82 81 #define ASUS_WMI_DEVID_MINI_LED_MODE 0x0005001E 83 82 #define ASUS_WMI_DEVID_MINI_LED_MODE2 0x0005002E 83 + #define ASUS_WMI_DEVID_SCREEN_AUTO_BRIGHTNESS 0x0005002A 84 84 85 85 /* Storage */ 86 86 #define ASUS_WMI_DEVID_CARDREADER 0x00080013 ··· 107 103 #define ASUS_WMI_DEVID_PPT_PL1_SPL 0x001200A3 108 104 #define ASUS_WMI_DEVID_PPT_APU_SPPT 0x001200B0 109 105 #define ASUS_WMI_DEVID_PPT_PLAT_SPPT 0x001200B1 110 - #define ASUS_WMI_DEVID_PPT_FPPT 0x001200C1 106 + #define ASUS_WMI_DEVID_PPT_PL3_FPPT 0x001200C1 111 107 #define ASUS_WMI_DEVID_NV_DYN_BOOST 0x001200C0 112 108 #define ASUS_WMI_DEVID_NV_THERM_TARGET 0x001200C2 113 109 ··· 136 132 137 133 /* dgpu on/off */ 138 134 #define ASUS_WMI_DEVID_DGPU 0x00090020 135 + 136 + #define ASUS_WMI_DEVID_APU_MEM 0x000600C1 137 + 138 + #define ASUS_WMI_DEVID_DGPU_BASE_TGP 0x00120099 139 + #define ASUS_WMI_DEVID_DGPU_SET_TGP 0x00120098 139 140 140 141 /* gpu mux switch, 0 = dGPU, 1 = Optimus */ 141 142 #define ASUS_WMI_DEVID_GPU_MUX 0x00090016 ··· 175 166 #if IS_REACHABLE(CONFIG_ASUS_WMI) 176 167 void set_ally_mcu_hack(enum asus_ally_mcu_hack status); 177 168 void set_ally_mcu_powersave(bool enabled); 169 + int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval); 178 170 int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval); 179 171 int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval); 180 172 #else ··· 189 179 { 190 180 return -ENODEV; 191 181 } 182 + static inline int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval) 183 + { 184 + return -ENODEV; 185 + } 192 186 static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, 193 187 u32 *retval) 194 188 { 195 189 return -ENODEV; 196 190 } 197 191 #endif 198 - 199 - /* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */ 200 - static const struct dmi_system_id asus_use_hid_led_dmi_ids[] = { 201 - { 202 - .matches = { 203 - DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Zephyrus"), 204 - }, 205 - }, 206 - { 207 - .matches = { 208 - DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Strix"), 209 - }, 210 - }, 211 - { 212 - .matches = { 213 - DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Flow"), 214 - }, 215 - }, 216 - { 217 - .matches = { 218 - DMI_MATCH(DMI_PRODUCT_FAMILY, "ProArt P16"), 219 - }, 220 - }, 221 - { 222 - .matches = { 223 - DMI_MATCH(DMI_BOARD_NAME, "GA403U"), 224 - }, 225 - }, 226 - { 227 - .matches = { 228 - DMI_MATCH(DMI_BOARD_NAME, "GU605M"), 229 - }, 230 - }, 231 - { 232 - .matches = { 233 - DMI_MATCH(DMI_BOARD_NAME, "RC71L"), 234 - }, 235 - }, 236 - { }, 237 - }; 238 192 239 193 #endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */
+2 -2
include/linux/platform_data/x86/intel_pmc_ipc.h
··· 9 9 #ifndef INTEL_PMC_IPC_H 10 10 #define INTEL_PMC_IPC_H 11 11 #include <linux/acpi.h> 12 + #include <linux/cleanup.h> 12 13 13 14 #define IPC_SOC_REGISTER_ACCESS 0xAA 14 15 #define IPC_SOC_SUB_CMD_READ 0x00 ··· 49 48 {.type = ACPI_TYPE_INTEGER,}, 50 49 }; 51 50 struct acpi_object_list arg_list = { PMC_IPCS_PARAM_COUNT, params }; 52 - union acpi_object *obj; 53 51 int status; 54 52 55 53 if (!ipc_cmd || !rbuf) ··· 72 72 if (ACPI_FAILURE(status)) 73 73 return -ENODEV; 74 74 75 - obj = buffer.pointer; 75 + union acpi_object *obj __free(kfree) = buffer.pointer; 76 76 77 77 if (obj && obj->type == ACPI_TYPE_PACKAGE && 78 78 obj->package.count == VALID_IPC_RESPONSE) {
+1
include/linux/platform_profile.h
··· 24 24 PLATFORM_PROFILE_BALANCED, 25 25 PLATFORM_PROFILE_BALANCED_PERFORMANCE, 26 26 PLATFORM_PROFILE_PERFORMANCE, 27 + PLATFORM_PROFILE_MAX_POWER, 27 28 PLATFORM_PROFILE_CUSTOM, 28 29 PLATFORM_PROFILE_LAST, /*must always be last */ 29 30 };
+6 -9
include/linux/wmi.h
··· 36 36 */ 37 37 #define to_wmi_device(device) container_of_const(device, struct wmi_device, dev) 38 38 39 - extern acpi_status wmidev_evaluate_method(struct wmi_device *wdev, 40 - u8 instance, u32 method_id, 41 - const struct acpi_buffer *in, 42 - struct acpi_buffer *out); 39 + acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 method_id, 40 + const struct acpi_buffer *in, struct acpi_buffer *out); 43 41 44 - extern union acpi_object *wmidev_block_query(struct wmi_device *wdev, 45 - u8 instance); 42 + union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance); 46 43 47 44 acpi_status wmidev_block_set(struct wmi_device *wdev, u8 instance, const struct acpi_buffer *in); 48 45 ··· 78 81 */ 79 82 #define to_wmi_driver(drv) container_of_const(drv, struct wmi_driver, driver) 80 83 81 - extern int __must_check __wmi_driver_register(struct wmi_driver *driver, 82 - struct module *owner); 83 - extern void wmi_driver_unregister(struct wmi_driver *driver); 84 + int __must_check __wmi_driver_register(struct wmi_driver *driver, struct module *owner); 85 + 86 + void wmi_driver_unregister(struct wmi_driver *driver); 84 87 85 88 /** 86 89 * wmi_driver_register() - Helper macro to register a WMI driver
+1 -1
tools/power/x86/intel-speed-select/isst-config.c
··· 16 16 int arg; 17 17 }; 18 18 19 - static const char *version_str = "v1.23"; 19 + static const char *version_str = "v1.24"; 20 20 21 21 static const int supported_api_ver = 3; 22 22 static struct isst_if_platform_info isst_platform_info;
+44 -2
tools/power/x86/intel-speed-select/isst-core-tpmi.c
··· 452 452 return _pbf_get_coremask_info(id, level, pbf_info); 453 453 } 454 454 455 + #define FEATURE_ENABLE_WAIT_US 1000 456 + #define FEATURE_ENABLE_RETRIES 5 457 + 455 458 static int tpmi_set_pbf_fact_status(struct isst_id *id, int pbf, int enable) 456 459 { 457 460 struct isst_pkg_ctdp pkg_dev; 458 461 struct isst_pkg_ctdp_level_info ctdp_level; 459 462 int current_level; 460 463 struct isst_perf_feature_control info; 461 - int ret; 464 + int ret, i; 462 465 463 466 ret = isst_get_ctdp_levels(id, &pkg_dev); 464 467 if (ret) ··· 506 503 if (ret == -1) 507 504 return ret; 508 505 506 + for (i = 0; i < FEATURE_ENABLE_RETRIES; ++i) { 507 + 508 + usleep(FEATURE_ENABLE_WAIT_US); 509 + 510 + /* Check status */ 511 + ret = isst_get_ctdp_control(id, current_level, &ctdp_level); 512 + if (ret) 513 + return ret; 514 + 515 + debug_printf("pbf_enabled:%d fact_enabled:%d\n", 516 + ctdp_level.pbf_enabled, ctdp_level.fact_enabled); 517 + 518 + if (pbf) { 519 + if (ctdp_level.pbf_enabled == enable) 520 + break; 521 + } else { 522 + if (ctdp_level.fact_enabled == enable) 523 + break; 524 + } 525 + } 526 + 527 + if (i == FEATURE_ENABLE_RETRIES) 528 + return -1; 529 + 509 530 return 0; 510 531 } 511 532 ··· 540 513 int i, j; 541 514 int ret; 542 515 516 + memset(&info, 0, sizeof(info)); 543 517 info.socket_id = id->pkg; 544 518 info.power_domain_id = id->punit; 545 519 info.level = level; ··· 687 659 int priority_type) 688 660 { 689 661 struct isst_core_power info; 690 - int i, ret, saved_punit; 662 + int cp_state = 0, cp_cap = 0; 663 + int i, j, ret, saved_punit; 691 664 692 665 info.get_set = 1; 693 666 info.socket_id = id->pkg; ··· 707 678 if (ret == -1) { 708 679 id->punit = saved_punit; 709 680 return ret; 681 + } 682 + /* Get status */ 683 + for (j = 0; j < FEATURE_ENABLE_RETRIES; ++j) { 684 + usleep(FEATURE_ENABLE_WAIT_US); 685 + ret = tpmi_read_pm_config(id, &cp_state, &cp_cap); 686 + debug_printf("ret:%d cp_state:%d enable_clos:%d\n", ret, 687 + cp_state, enable_clos); 688 + if (ret || cp_state == enable_clos) 689 + break; 690 + } 691 + if (j == FEATURE_ENABLE_RETRIES) { 692 + id->punit = saved_punit; 693 + return -1; 710 694 } 711 695 } 712 696 }