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.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86

Pull x86 platform drivers from Ilpo Järvinen:

- alienware: Add more precise labels to fans

- amd/hsmp: Improve misleading probe errors (make the legacy driver
aware when HSMP is supported through the ACPI driver)

- amd/pmc: Add Lenovo Yoga 6 13ALCL6 to pmc quirk list

- drm/xe: Correct (D)VSEC information to support PMT crashlog feature

- fujitsu: Clamp charge threshold instead of returning an error

- ideapad: Expore change types

- intel/pmt:
- Add PMT Discovery driver
- Add API to retrieve telemetry regions by feature
- Fix crashlog NULL access
- Support Battlemage GPU (BMG) crashlog

- intel/vsec:
- Add Discovery feature
- Add feature dependency support using device links

- lenovo:
- Move lenovo drivers under drivers/platform/x86/lenovo/
- Add WMI drivers for Lenovo Gaming series
- Improve DMI handling

- oxpec:
- Add support for OneXPlayer X1 Mini Pro (Strix Point variant)
- Fix EC registers for G1 AMD

- samsung-laptop: Expose change types

- wmi: Fix WMI device naming issue (same GUID corner cases)

- x86-android-tables: Add ovc-capacity-table to generic battery nodes

- Miscellaneous cleanups / refactoring / improvements

* tag 'platform-drivers-x86-v6.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (63 commits)
platform/x86: oxpec: Add support for OneXPlayer X1 Mini Pro (Strix Point)
platform/x86: oxpec: Fix turbo register for G1 AMD
platform/x86/intel/pmt: support BMG crashlog
platform/x86/intel/pmt: use a version struct
platform/x86/intel/pmt: refactor base parameter
platform/x86/intel/pmt: add register access helpers
platform/x86/intel/pmt: decouple sysfs and namespace
platform/x86/intel/pmt: correct types
platform/x86/intel/pmt: re-order trigger logic
platform/x86/intel/pmt: use guard(mutex)
platform/x86/intel/pmt: mutex clean up
platform/x86/intel/pmt: white space cleanup
drm/xe: Correct BMG VSEC header sizing
drm/xe: Correct the rev value for the DVSEC entries
platform/x86/intel/pmt: fix a crashlog NULL pointer access
platform/x86: samsung-laptop: Expose charge_types
platform/x86/amd: pmc: Add Lenovo Yoga 6 13ALC6 to pmc quirk list
platform/x86: dell-uart-backlight: Use blacklight power constant
platform/x86/intel/pmt: fix build dependency for kunit test
platform/x86: lenovo: gamezone needs "other mode"
...

+5192 -1065
+10
Documentation/ABI/obsolete/sysfs-driver-samsung-laptop
··· 1 + What: /sys/devices/platform/samsung/battery_life_extender 2 + Date: December 1, 2011 3 + KernelVersion: 3.3 4 + Contact: Corentin Chary <corentin.chary@gmail.com> 5 + Description: Max battery charge level can be modified, battery cycle 6 + life can be extended by reducing the max battery charge 7 + level. 8 + 9 + - 0 means normal battery mode (100% charge) 10 + - 1 means battery life extender mode (80% charge)
+8
Documentation/ABI/obsolete/sysfs-platform-ideapad-laptop
··· 1 + What: /sys/bus/platform/devices/VPC2004:*/conservation_mode 2 + Date: Aug 2017 3 + KernelVersion: 4.14 4 + Contact: platform-driver-x86@vger.kernel.org 5 + Description: 6 + Controls whether the conservation mode is enabled or not. 7 + This feature limits the maximum battery charge percentage to 8 + around 50-60% in order to prolong the lifetime of the battery.
+134
Documentation/ABI/testing/sysfs-class-intel_pmt-features
··· 1 + What: /sys/class/intel_pmt/features-<PCI BDF>/ 2 + Date: 2025-04-24 3 + KernelVersion: 6.16 4 + Contact: david.e.box@linux.intel.com 5 + Description: 6 + The `features-<PCI BDF>/` directory represents the "features" 7 + capability exposed by Intel PMT (Platform Monitoring Technology) 8 + for the given PCI device. 9 + 10 + Each directory corresponds to a PMT feature and contains 11 + attributes describing the available telemetry, monitoring, or 12 + control functionalities. 13 + 14 + Directory Structure: 15 + 16 + /sys/class/intel_pmt/features-<PCI BDF>/ 17 + ├── accelerator_telemetry/ # Per-accelerator telemetry data 18 + ├── crash_log/ # Contains system crash telemetry logs 19 + ├── per_core_environment_telemetry/ # Environmental telemetry per core 20 + ├── per_core_performance_telemetry/ # Performance telemetry per core 21 + ├── per_rmid_energy_telemetry/ # Energy telemetry for RMIDs 22 + ├── per_rmid_perf_telemetry/ # Performance telemetry for RMIDs 23 + ├── tpmi_control/ # TPMI-related controls and telemetry 24 + ├── tracing/ # PMT tracing features 25 + └── uncore_telemetry/ # Uncore telemetry data 26 + 27 + Common Files (Present in all feature directories): 28 + 29 + caps 30 + - Read-only 31 + - Lists available capabilities for this feature. 32 + 33 + guids 34 + - Read-only 35 + - Lists GUIDs associated with this feature. 36 + 37 + Additional Attributes (Conditional Presence): 38 + 39 + max_command_size 40 + - Read-only 41 + - Present if the feature supports out-of-band MCTP access. 42 + - Maximum supported MCTP command size for out-of-band PMT access (bytes). 43 + 44 + max_stream_size 45 + - Read-only 46 + - Present if the feature supports out-of-band MCTP access. 47 + - Maximum supported MCTP stream size (bytes). 48 + 49 + min_watcher_period_ms 50 + - Read-only 51 + - Present if the feature supports the watcher API. 52 + The watcher API provides a writable control interface that allows user 53 + configuration of monitoring behavior, such as setting the sampling or 54 + reporting interval. 55 + - Minimum supported time period for the watcher interface (milliseconds). 56 + 57 + num_rmids 58 + - Read-only 59 + - Present if the feature supports RMID (Resource Monitoring ID) telemetry. 60 + RMIDs are identifiers used by hardware to track and report resource usage, 61 + such as memory bandwidth or energy consumption, on a per-logical-entity 62 + basis (e.g., per core, thread, or process group). 63 + - Maximum number of RMIDs tracked simultaneously. 64 + 65 + Example: 66 + For a device with PCI BDF `0000:00:03.1`, the directory tree could look like: 67 + 68 + /sys/class/intel_pmt/features-0000:00:03.1/ 69 + ├── accelerator_telemetry/ 70 + │ ├── caps 71 + │ ├── guids 72 + │ ├── max_command_size 73 + │ ├── max_stream_size 74 + │ ├── min_watcher_period_ms 75 + ├── crash_log/ 76 + │ ├── caps 77 + │ ├── guids 78 + │ ├── max_command_size 79 + │ ├── max_stream_size 80 + ├── per_core_environment_telemetry/ 81 + │ ├── caps 82 + │ ├── guids 83 + │ ├── max_command_size 84 + │ ├── max_stream_size 85 + │ ├── min_watcher_period_ms 86 + ├── per_rmid_energy_telemetry/ 87 + │ ├── caps 88 + │ ├── guids 89 + │ ├── max_command_size 90 + │ ├── max_stream_size 91 + │ ├── min_watcher_period_ms 92 + │ ├── num_rmids 93 + ├── tpmi_control/ 94 + │ ├── caps 95 + │ ├── guids 96 + ├── tracing/ 97 + │ ├── caps 98 + │ ├── guids 99 + ├── uncore_telemetry/ 100 + │ ├── caps 101 + │ ├── guids 102 + │ ├── max_command_size 103 + │ ├── max_stream_size 104 + │ ├── min_watcher_period_ms 105 + 106 + Notes: 107 + - Some attributes are only present if the corresponding feature supports 108 + the capability (e.g., `max_command_size` for MCTP-capable features). 109 + - Features supporting RMIDs include `num_rmids`. 110 + - Features supporting the watcher API include `min_watcher_period_ms`. 111 + - The `caps` file provides additional information about the functionality 112 + of the feature. 113 + 114 + Example 'caps' content for the 'tracing' feature: 115 + 116 + /sys/class/intel_pmt/features-0000:00:03.1/ 117 + ├── tracing/ 118 + │ ├── caps 119 + 120 + telemetry Available: No 121 + watcher Available: Yes 122 + crashlog Available: No 123 + streaming Available: No 124 + threashold Available: No 125 + window Available: No 126 + config Available: Yes 127 + tracing Available: No 128 + inband Available: Yes 129 + oob Available: Yes 130 + secure_chan Available: No 131 + pmt_sp Available: Yes 132 + pmt_sp_policy Available: Yes 133 + mailbox Available: Yes 134 + bios_lock Available: Yes
-11
Documentation/ABI/testing/sysfs-driver-samsung-laptop
··· 20 20 and it's still unknown if this value even changes 21 21 anything, other than making the user feel a bit better. 22 22 23 - What: /sys/devices/platform/samsung/battery_life_extender 24 - Date: December 1, 2011 25 - KernelVersion: 3.3 26 - Contact: Corentin Chary <corentin.chary@gmail.com> 27 - Description: Max battery charge level can be modified, battery cycle 28 - life can be extended by reducing the max battery charge 29 - level. 30 - 31 - - 0 means normal battery mode (100% charge) 32 - - 1 means battery life extender mode (80% charge) 33 - 34 23 What: /sys/devices/platform/samsung/usb_charge 35 24 Date: December 1, 2011 36 25 KernelVersion: 3.3
+4 -4
Documentation/ABI/testing/sysfs-platform-dell-privacy-wmi
··· 1 - What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_supported_type 1 + What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919[-X]/dell_privacy_supported_type 2 2 Date: Apr 2021 3 3 KernelVersion: 5.13 4 4 Contact: "<perry.yuan@dell.com>" ··· 29 29 30 30 For example to check which privacy devices are supported:: 31 31 32 - # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_supported_type 32 + # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919*/dell_privacy_supported_type 33 33 [Microphone Mute] [supported] 34 34 [Camera Shutter] [supported] 35 35 [ePrivacy Screen] [unsupported] 36 36 37 - What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_current_state 37 + What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919[-X]/dell_privacy_current_state 38 38 Date: Apr 2021 39 39 KernelVersion: 5.13 40 40 Contact: "<perry.yuan@dell.com>" ··· 66 66 67 67 For example to check all supported current privacy device states:: 68 68 69 - # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_current_state 69 + # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919*/dell_privacy_current_state 70 70 [Microphone] [unmuted] 71 71 [Camera Shutter] [unmuted]
-9
Documentation/ABI/testing/sysfs-platform-ideapad-laptop
··· 27 27 * 1 -> Switched On 28 28 * 0 -> Switched Off 29 29 30 - What: /sys/bus/platform/devices/VPC2004:*/conservation_mode 31 - Date: Aug 2017 32 - KernelVersion: 4.14 33 - Contact: platform-driver-x86@vger.kernel.org 34 - Description: 35 - Controls whether the conservation mode is enabled or not. 36 - This feature limits the maximum battery charge percentage to 37 - around 50-60% in order to prolong the lifetime of the battery. 38 - 39 30 What: /sys/bus/platform/devices/VPC2004:*/fn_lock 40 31 Date: May 2018 41 32 KernelVersion: 4.18
+1 -1
Documentation/ABI/testing/sysfs-platform-intel-wmi-sbl-fw-update
··· 1 - What: /sys/bus/wmi/devices/44FADEB1-B204-40F2-8581-394BBDC1B651/firmware_update_request 1 + What: /sys/bus/wmi/devices/44FADEB1-B204-40F2-8581-394BBDC1B651[-X]/firmware_update_request 2 2 Date: April 2020 3 3 KernelVersion: 5.7 4 4 Contact: "Jithu Joseph" <jithu.joseph@intel.com>
+1 -1
Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt
··· 1 - What: /sys/devices/platform/<platform>/force_power 1 + What: /sys/bus/wmi/devices/86CCFD48-205E-4A77-9C48-2021CBEDE341[-X]/force_power 2 2 Date: September 2017 3 3 KernelVersion: 4.15 4 4 Contact: "Mario Limonciello" <mario.limonciello@outlook.com>
+2 -7
Documentation/admin-guide/thunderbolt.rst
··· 358 358 Many OEMs include a method that can be used to force the power of a 359 359 Thunderbolt controller to an "On" state even if nothing is connected. 360 360 If supported by your machine this will be exposed by the WMI bus with 361 - a sysfs attribute called "force_power". 362 - 363 - For example the intel-wmi-thunderbolt driver exposes this attribute in: 364 - /sys/bus/wmi/devices/86CCFD48-205E-4A77-9C48-2021CBEDE341/force_power 365 - 366 - To force the power to on, write 1 to this attribute file. 367 - To disable force power, write 0 to this attribute file. 361 + a sysfs attribute called "force_power", see 362 + Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt for details. 368 363 369 364 Note: it's currently not possible to query the force power state of a platform.
+203
Documentation/wmi/devices/lenovo-wmi-gamezone.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0-or-later 2 + 3 + ========================================================== 4 + Lenovo WMI Interface Gamezone Driver (lenovo-wmi-gamezone) 5 + ========================================================== 6 + 7 + Introduction 8 + ============ 9 + The Lenovo WMI gamezone interface is broken up into multiple GUIDs, 10 + The primary "Gamezone" GUID provides advanced features such as fan 11 + profiles and overclocking. It is paired with multiple event GUIDs 12 + and data block GUIDs that provide context for the various methods. 13 + 14 + Gamezone Data 15 + ------------- 16 + 17 + WMI GUID ``887B54E3-DDDC-4B2C-8B88-68A26A8835D0`` 18 + 19 + The Gamezone Data WMI interface provides platform-profile and fan curve 20 + settings for devices that fall under the "Gaming Series" of Lenovo devices. 21 + It uses a notifier chain to inform other Lenovo WMI interface drivers of the 22 + current platform profile when it changes. 23 + 24 + The following platform profiles are supported: 25 + - low-power 26 + - balanced 27 + - balanced-performance 28 + - performance 29 + - custom 30 + 31 + Balanced-Performance 32 + ~~~~~~~~~~~~~~~~~~~~ 33 + 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. 39 + 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. 43 + 44 + Custom Profile 45 + ~~~~~~~~~~~~~~ 46 + The custom profile represents a hardware mode on Lenovo devices that enables 47 + user modifications to Package Power Tracking (PPT) and fan curve settings. 48 + When an attribute exposed by the Other Mode WMI interface is to be modified, 49 + the Gamezone driver must first be switched to the "custom" profile manually, 50 + or the setting will have no effect. If another profile is set from the list 51 + of supported profiles, the BIOS will override any user PPT settings when 52 + switching to that profile. 53 + 54 + Gamezone Thermal Mode Event 55 + --------------------------- 56 + 57 + WMI GUID ``D320289E-8FEA-41E0-86F9-911D83151B5F`` 58 + 59 + The Gamezone Thermal Mode Event interface notifies the system when the platform 60 + profile has changed, either through the hardware event (Fn+Q for laptops or 61 + Legion + Y for Go Series), or through the Gamezone WMI interface. This event is 62 + implemented in the Lenovo WMI Events driver (lenovo-wmi-events). 63 + 64 + 65 + WMI interface description 66 + ========================= 67 + 68 + The WMI interface description can be decoded from the embedded binary MOF (bmof) 69 + data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility: 70 + 71 + :: 72 + 73 + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO_GAMEZONE_DATA class"), guid("{887B54E3-DDDC-4B2C-8B88-68A26A8835D0}")] 74 + class LENOVO_GAMEZONE_DATA { 75 + [key, read] string InstanceName; 76 + [read] boolean Active; 77 + 78 + [WmiMethodId(4), Implemented, Description("Is SupportGpu OverClock")] void IsSupportGpuOC([out, Description("Is SupportGpu OverClock")] uint32 Data); 79 + [WmiMethodId(11), Implemented, Description("Get AslCode Version")] void GetVersion ([out, Description("AslCode version")] UINT32 Data); 80 + [WmiMethodId(12), Implemented, Description("Fan cooling capability")] void IsSupportFanCooling([out, Description("Fan cooling capability")] UINT32 Data); 81 + [WmiMethodId(13), Implemented, Description("Set Fan cooling on/off")] void SetFanCooling ([in, Description("Set Fan cooling on/off")] UINT32 Data); 82 + [WmiMethodId(14), Implemented, Description("cpu oc capability")] void IsSupportCpuOC ([out, Description("cpu oc capability")] UINT32 Data); 83 + [WmiMethodId(15), Implemented, Description("bios has overclock capability")] void IsBIOSSupportOC ([out, Description("bios has overclock capability")] UINT32 Data); 84 + [WmiMethodId(16), Implemented, Description("enable or disable overclock in bios")] void SetBIOSOC ([in, Description("enable or disable overclock in bios")] UINT32 Data); 85 + [WmiMethodId(18), Implemented, Description("Get CPU temperature")] void GetCPUTemp ([out, Description("Get CPU temperature")] UINT32 Data); 86 + [WmiMethodId(19), Implemented, Description("Get GPU temperature")] void GetGPUTemp ([out, Description("Get GPU temperature")] UINT32 Data); 87 + [WmiMethodId(20), Implemented, Description("Get Fan cooling on/off status")] void GetFanCoolingStatus ([out, Description("Get Fan cooling on/off status")] UINT32 Data); 88 + [WmiMethodId(21), Implemented, Description("EC support disable windows key capability")] void IsSupportDisableWinKey ([out, Description("EC support disable windows key capability")] UINT32 Data); 89 + [WmiMethodId(22), Implemented, Description("Set windows key disable/enable")] void SetWinKeyStatus ([in, Description("Set windows key disable/enable")] UINT32 Data); 90 + [WmiMethodId(23), Implemented, Description("Get windows key disable/enable status")] void GetWinKeyStatus ([out, Description("Get windows key disable/enable status")] UINT32 Data); 91 + [WmiMethodId(24), Implemented, Description("EC support disable touchpad capability")] void IsSupportDisableTP ([out, Description("EC support disable touchpad capability")] UINT32 Data); 92 + [WmiMethodId(25), Implemented, Description("Set touchpad disable/enable")] void SetTPStatus ([in, Description("Set touchpad disable/enable")] UINT32 Data); 93 + [WmiMethodId(26), Implemented, Description("Get touchpad disable/enable status")] void GetTPStatus ([out, Description("Get touchpad disable/enable status")] UINT32 Data); 94 + [WmiMethodId(30), Implemented, Description("Get Keyboard feature list")] void GetKeyboardfeaturelist ([out, Description("Get Keyboard feature list")] UINT32 Data); 95 + [WmiMethodId(31), Implemented, Description("Get Memory OC Information")] void GetMemoryOCInfo ([out, Description("Get Memory OC Information")] UINT32 Data); 96 + [WmiMethodId(32), Implemented, Description("Water Cooling feature capability")] void IsSupportWaterCooling ([out, Description("Water Cooling feature capability")] UINT32 Data); 97 + [WmiMethodId(33), Implemented, Description("Set Water Cooling status")] void SetWaterCoolingStatus ([in, Description("Set Water Cooling status")] UINT32 Data); 98 + [WmiMethodId(34), Implemented, Description("Get Water Cooling status")] void GetWaterCoolingStatus ([out, Description("Get Water Cooling status")] UINT32 Data); 99 + [WmiMethodId(35), Implemented, Description("Lighting feature capability")] void IsSupportLightingFeature ([out, Description("Lighting feature capability")] UINT32 Data); 100 + [WmiMethodId(36), Implemented, Description("Set keyboard light off or on to max")] void SetKeyboardLight ([in, Description("keyboard light off or on switch")] UINT32 Data); 101 + [WmiMethodId(37), Implemented, Description("Get keyboard light on/off status")] void GetKeyboardLight ([out, Description("Get keyboard light on/off status")] UINT32 Data); 102 + [WmiMethodId(38), Implemented, Description("Get Macrokey scan code")] void GetMacrokeyScancode ([in, Description("Macrokey index")] UINT32 idx, [out, Description("Scan code")] UINT32 scancode); 103 + [WmiMethodId(39), Implemented, Description("Get Macrokey count")] void GetMacrokeyCount ([out, Description("Macrokey count")] UINT32 Data); 104 + [WmiMethodId(40), Implemented, Description("Support G-Sync feature")] void IsSupportGSync ([out, Description("Support G-Sync feature")] UINT32 Data); 105 + [WmiMethodId(41), Implemented, Description("Get G-Sync Status")] void GetGSyncStatus ([out, Description("Get G-Sync Status")] UINT32 Data); 106 + [WmiMethodId(42), Implemented, Description("Set G-Sync Status")] void SetGSyncStatus ([in, Description("Set G-Sync Status")] UINT32 Data); 107 + [WmiMethodId(43), Implemented, Description("Support Smart Fan feature")] void IsSupportSmartFan ([out, Description("Support Smart Fan feature")] UINT32 Data); 108 + [WmiMethodId(44), Implemented, Description("Set Smart Fan Mode")] void SetSmartFanMode ([in, Description("Set Smart Fan Mode")] UINT32 Data); 109 + [WmiMethodId(45), Implemented, Description("Get Smart Fan Mode")] void GetSmartFanMode ([out, Description("Get Smart Fan Mode")] UINT32 Data); 110 + [WmiMethodId(46), Implemented, Description("Get Smart Fan Setting Mode")] void GetSmartFanSetting ([out, Description("Get Smart Setting Mode")] UINT32 Data); 111 + [WmiMethodId(47), Implemented, Description("Get Power Charge Mode")] void GetPowerChargeMode ([out, Description("Get Power Charge Mode")] UINT32 Data); 112 + [WmiMethodId(48), Implemented, Description("Get Gaming Product Info")] void GetProductInfo ([out, Description("Get Gaming Product Info")] UINT32 Data); 113 + [WmiMethodId(49), Implemented, Description("Over Drive feature capability")] void IsSupportOD ([out, Description("Over Drive feature capability")] UINT32 Data); 114 + [WmiMethodId(50), Implemented, Description("Get Over Drive status")] void GetODStatus ([out, Description("Get Over Drive status")] UINT32 Data); 115 + [WmiMethodId(51), Implemented, Description("Set Over Drive status")] void SetODStatus ([in, Description("Set Over Drive status")] UINT32 Data); 116 + [WmiMethodId(52), Implemented, Description("Set Light Control Owner")] void SetLightControlOwner ([in, Description("Set Light Control Owner")] UINT32 Data); 117 + [WmiMethodId(53), Implemented, Description("Set DDS Control Owner")] void SetDDSControlOwner ([in, Description("Set DDS Control Owner")] UINT32 Data); 118 + [WmiMethodId(54), Implemented, Description("Get the flag of restore OC value")] void IsRestoreOCValue ([in, Description("Clean this flag")] UINT32 idx, [out, Description("Restore oc value flag")] UINT32 Data); 119 + [WmiMethodId(55), Implemented, Description("Get Real Thremal Mode")] void GetThermalMode ([out, Description("Real Thremal Mode")] UINT32 Data); 120 + [WmiMethodId(56), Implemented, Description("Get the OC switch status in BIOS")] void GetBIOSOCMode ([out, Description("OC Mode")] UINT32 Data); 121 + [WmiMethodId(59), Implemented, Description("Get hardware info support version")] void GetHardwareInfoSupportVersion ([out, Description("version")] UINT32 Data); 122 + [WmiMethodId(60), Implemented, Description("Get Cpu core 0 max frequency")] void GetCpuFrequency ([out, Description("frequency")] UINT32 Data); 123 + [WmiMethodId(62), Implemented, Description("Check the Adapter type fit for OC")] void IsACFitForOC ([out, Description("AC check result")] UINT32 Data); 124 + [WmiMethodId(63), Implemented, Description("Is support IGPU mode")] void IsSupportIGPUMode ([out, Description("IGPU modes")] UINT32 Data); 125 + [WmiMethodId(64), Implemented, Description("Get IGPU Mode Status")] void GetIGPUModeStatus([out, Description("IGPU Mode Status")] UINT32 Data); 126 + [WmiMethodId(65), Implemented, Description("Set IGPU Mode")] void SetIGPUModeStatus([in, Description("IGPU Mode")] UINT32 mode, [out, Description("return code")] UINT32 Data); 127 + [WmiMethodId(66), Implemented, Description("Notify DGPU Status")] void NotifyDGPUStatus([in, Description("DGPU status")] UINT32 status, [out, Description("return code")] UINT32 Data); 128 + [WmiMethodId(67), Implemented, Description("Is changed Y log")] void IsChangedYLog([out, Description("Is changed Y Log")] UINT32 Data); 129 + [WmiMethodId(68), Implemented, Description("Get DGPU Hardwawre ID")] void GetDGPUHWId([out, Description("Get DGPU Hardware ID")] string Data); 130 + }; 131 + 132 + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Definition of CPU OC parameter list"), guid("{B7F3CA0A-ACDC-42D2-9217-77C6C628FBD2}")] 133 + class LENOVO_GAMEZONE_CPU_OC_DATA { 134 + [key, read] string InstanceName; 135 + [read] boolean Active; 136 + 137 + [WmiDataId(1), read, Description("OC tune id.")] uint32 Tuneid; 138 + [WmiDataId(2), read, Description("Default value.")] uint32 DefaultValue; 139 + [WmiDataId(3), read, Description("OC Value.")] uint32 OCValue; 140 + [WmiDataId(4), read, Description("Min Value.")] uint32 MinValue; 141 + [WmiDataId(5), read, Description("Max Value.")] uint32 MaxValue; 142 + [WmiDataId(6), read, Description("Scale Value.")] uint32 ScaleValue; 143 + [WmiDataId(7), read, Description("OC Order id.")] uint32 OCOrderid; 144 + [WmiDataId(8), read, Description("NON-OC Order id.")] uint32 NOCOrderid; 145 + [WmiDataId(9), read, Description("Delay time in ms.")] uint32 Interval; 146 + }; 147 + 148 + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Definition of GPU OC parameter list"), guid("{887B54E2-DDDC-4B2C-8B88-68A26A8835D0}")] 149 + class LENOVO_GAMEZONE_GPU_OC_DATA { 150 + [key, read] string InstanceName; 151 + [read] boolean Active; 152 + 153 + [WmiDataId(1), read, Description("P-State ID.")] uint32 PStateID; 154 + [WmiDataId(2), read, Description("CLOCK ID.")] uint32 ClockID; 155 + [WmiDataId(3), read, Description("Default value.")] uint32 defaultvalue; 156 + [WmiDataId(4), read, Description("OC Offset freqency.")] uint32 OCOffsetFreq; 157 + [WmiDataId(5), read, Description("OC Min offset value.")] uint32 OCMinOffset; 158 + [WmiDataId(6), read, Description("OC Max offset value.")] uint32 OCMaxOffset; 159 + [WmiDataId(7), read, Description("OC Offset Scale.")] uint32 OCOffsetScale; 160 + [WmiDataId(8), read, Description("OC Order id.")] uint32 OCOrderid; 161 + [WmiDataId(9), read, Description("NON-OC Order id.")] uint32 NOCOrderid; 162 + }; 163 + 164 + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Fancooling finish event"), guid("{BC72A435-E8C1-4275-B3E2-D8B8074ABA59}")] 165 + class LENOVO_GAMEZONE_FAN_COOLING_EVENT: WMIEvent { 166 + [key, read] string InstanceName; 167 + [read] boolean Active; 168 + 169 + [WmiDataId(1), read, Description("Fancooling clean finish event")] uint32 EventId; 170 + }; 171 + 172 + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Smart Fan mode change event"), guid("{D320289E-8FEA-41E0-86F9-611D83151B5F}")] 173 + class LENOVO_GAMEZONE_SMART_FAN_MODE_EVENT: WMIEvent { 174 + [key, read] string InstanceName; 175 + [read] boolean Active; 176 + 177 + [WmiDataId(1), read, Description("Smart Fan Mode change event")] uint32 mode; 178 + [WmiDataId(2), read, Description("version of FN+Q")] uint32 version; 179 + }; 180 + 181 + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Smart Fan setting mode change event"), guid("{D320289E-8FEA-41E1-86F9-611D83151B5F}")] 182 + class LENOVO_GAMEZONE_SMART_FAN_SETTING_EVENT: WMIEvent { 183 + [key, read] string InstanceName; 184 + [read] boolean Active; 185 + 186 + [WmiDataId(1), read, Description("Smart Fan Setting mode change event")] uint32 mode; 187 + }; 188 + 189 + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("POWER CHARGE MODE Change EVENT"), guid("{D320289E-8FEA-41E0-86F9-711D83151B5F}")] 190 + class LENOVO_GAMEZONE_POWER_CHARGE_MODE_EVENT: WMIEvent { 191 + [key, read] string InstanceName; 192 + [read] boolean Active; 193 + 194 + [WmiDataId(1), read, Description("POWER CHARGE MODE Change EVENT")] uint32 mode; 195 + }; 196 + 197 + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Thermal Mode Real Mode change event"), guid("{D320289E-8FEA-41E0-86F9-911D83151B5F}")] 198 + class LENOVO_GAMEZONE_THERMAL_MODE_EVENT: WMIEvent { 199 + [key, read] string InstanceName; 200 + [read] boolean Active; 201 + 202 + [WmiDataId(1), read, Description("Thermal Mode Real Mode")] uint32 mode; 203 + };
+108
Documentation/wmi/devices/lenovo-wmi-other.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0-or-later 2 + 3 + =========================================================== 4 + Lenovo WMI Interface Other Mode Driver (lenovo-wmi-other) 5 + =========================================================== 6 + 7 + Introduction 8 + ============ 9 + Lenovo WMI Other Mode interface is broken up into multiple GUIDs, 10 + The primary Other Mode interface provides advanced power tuning features 11 + such as Package Power Tracking (PPT). It is paired with multiple data block 12 + GUIDs that provide context for the various methods. 13 + 14 + 15 + Other Mode 16 + ---------- 17 + 18 + WMI GUID ``DC2A8805-3A8C-41BA-A6F7-092E0089CD3B`` 19 + 20 + The Other Mode WMI interface uses the firmware_attributes class to expose 21 + various WMI attributes provided by the interface in the sysfs. This enables 22 + CPU and GPU power limit tuning as well as various other attributes for 23 + devices that fall under the "Gaming Series" of Lenovo devices. Each 24 + attribute exposed by the Other Mode interface has corresponding 25 + capability data blocks which allow the driver to probe details about the 26 + attribute. Each attribute has multiple pages, one for each of the platform 27 + profiles managed by the Gamezone interface. Attributes are exposed in sysfs 28 + under the following path: 29 + 30 + :: 31 + 32 + /sys/class/firmware-attributes/lenovo-wmi-other/attributes/<attribute>/ 33 + 34 + LENOVO_CAPABILITY_DATA_01 35 + ------------------------- 36 + 37 + WMI GUID ``7A8F5407-CB67-4D6E-B547-39B3BE018154`` 38 + 39 + The LENOVO_CAPABILITY_DATA_01 interface provides information on various 40 + power limits of integrated CPU and GPU components. 41 + 42 + Each attribute has the following properties: 43 + - current_value 44 + - default_value 45 + - display_name 46 + - max_value 47 + - min_value 48 + - scalar_increment 49 + - type 50 + 51 + The following attributes are implemented: 52 + - ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit 53 + - ppt_pl2_sppt: Platform Profile Tracking Slow Package Power Tracking 54 + - ppt_pl3_fppt: Platform Profile Tracking Fast Package Power Tracking 55 + 56 + 57 + WMI interface description 58 + ========================= 59 + 60 + The WMI interface description can be decoded from the embedded binary MOF (bmof) 61 + data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility: 62 + 63 + :: 64 + 65 + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO_OTHER_METHOD class"), guid("{dc2a8805-3a8c-41ba-a6f7-092e0089cd3b}")] 66 + class LENOVO_OTHER_METHOD { 67 + [key, read] string InstanceName; 68 + [read] boolean Active; 69 + 70 + [WmiMethodId(17), Implemented, Description("Get Feature Value ")] void GetFeatureValue([in] uint32 IDs, [out] uint32 value); 71 + [WmiMethodId(18), Implemented, Description("Set Feature Value ")] void SetFeatureValue([in] uint32 IDs, [in] uint32 value); 72 + [WmiMethodId(19), Implemented, Description("Get Data By Command ")] void GetDataByCommand([in] uint32 IDs, [in] uint32 Command, [out] uint32 DataSize, [out, WmiSizeIs("DataSize")] uint32 Data[]); 73 + [WmiMethodId(99), Implemented, Description("Get Data By Package for TAC")] void GetDataByPackage([in, Max(40)] uint8 Input[], [out] uint32 DataSize, [out, WmiSizeIs("DataSize")] uint8 Data[]); 74 + }; 75 + 76 + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO CAPABILITY DATA 00"), guid("{362a3afe-3d96-4665-8530-96dad5bb300e}")] 77 + class LENOVO_CAPABILITY_DATA_00 { 78 + [key, read] string InstanceName; 79 + [read] boolean Active; 80 + 81 + [WmiDataId(1), read, Description(" IDs.")] uint32 IDs; 82 + [WmiDataId(2), read, Description("Capability.")] uint32 Capability; 83 + [WmiDataId(3), read, Description("Capability Default Value.")] uint32 DefaultValue; 84 + }; 85 + 86 + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO CAPABILITY DATA 01"), guid("{7a8f5407-cb67-4d6e-b547-39b3be018154}")] 87 + class LENOVO_CAPABILITY_DATA_01 { 88 + [key, read] string InstanceName; 89 + [read] boolean Active; 90 + 91 + [WmiDataId(1), read, Description(" IDs.")] uint32 IDs; 92 + [WmiDataId(2), read, Description("Capability.")] uint32 Capability; 93 + [WmiDataId(3), read, Description("Default Value.")] uint32 DefaultValue; 94 + [WmiDataId(4), read, Description("Step.")] uint32 Step; 95 + [WmiDataId(5), read, Description("Minimum Value.")] uint32 MinValue; 96 + [WmiDataId(6), read, Description("Maximum Value.")] uint32 MaxValue; 97 + }; 98 + 99 + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO CAPABILITY DATA 02"), guid("{bbf1f790-6c2f-422b-bc8c-4e7369c7f6ab}")] 100 + class LENOVO_CAPABILITY_DATA_02 { 101 + [key, read] string InstanceName; 102 + [read] boolean Active; 103 + 104 + [WmiDataId(1), read, Description(" IDs.")] uint32 IDs; 105 + [WmiDataId(2), read, Description("Capability.")] uint32 Capability; 106 + [WmiDataId(3), read, Description("Data Size.")] uint32 DataSize; 107 + [WmiDataId(4), read, Description("Default Value"), WmiSizeIs("DataSize")] uint8 DefaultValue[]; 108 + };
+16 -5
MAINTAINERS
··· 11634 11634 L: platform-driver-x86@vger.kernel.org 11635 11635 S: Maintained 11636 11636 W: http://launchpad.net/ideapad-laptop 11637 - F: drivers/platform/x86/ideapad-laptop.c 11637 + F: drivers/platform/x86/lenovo/ideapad-laptop.c 11638 11638 11639 11639 IDEAPAD LAPTOP SLIDEBAR DRIVER 11640 11640 M: Andrey Moiseev <o2g.org.ru@gmail.com> ··· 12407 12407 INTEL PMT DRIVERS 12408 12408 M: David E. Box <david.e.box@linux.intel.com> 12409 12409 S: Supported 12410 + F: Documentation/ABI/testing/sysfs-class-intel_pmt 12411 + F: Documentation/ABI/testing/sysfs-class-intel_pmt-features 12410 12412 F: drivers/platform/x86/intel/pmt/ 12411 12413 12412 12414 INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT ··· 13699 13697 W: http://legousb.sourceforge.net/ 13700 13698 F: drivers/usb/misc/legousbtower.c 13701 13699 13700 + LENOVO drivers 13701 + M: Mark Pearson <mpearson-lenovo@squebb.ca> 13702 + M: Derek J. Clark <derekjohn.clark@gmail.com> 13703 + L: platform-driver-x86@vger.kernel.org 13704 + S: Maintained 13705 + F: Documentation/wmi/devices/lenovo-wmi-gamezone.rst 13706 + F: Documentation/wmi/devices/lenovo-wmi-other.rst 13707 + F: drivers/platform/x86/lenovo/* 13708 + 13702 13709 LENOVO WMI HOTKEY UTILITIES DRIVER 13703 13710 M: Jackie Dong <xy-jackie@139.com> 13704 13711 L: platform-driver-x86@vger.kernel.org 13705 13712 S: Maintained 13706 - F: drivers/platform/x86/lenovo-wmi-hotkey-utilities.c 13713 + F: drivers/platform/x86/lenovo/wmi-hotkey-utilities.c 13707 13714 13708 13715 LETSKETCH HID TABLET DRIVER 13709 13716 M: Hans de Goede <hansg@kernel.org> ··· 24740 24729 W: http://ibm-acpi.sourceforge.net 24741 24730 W: http://thinkwiki.org/wiki/Ibm-acpi 24742 24731 T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git 24743 - F: drivers/platform/x86/thinkpad_acpi.c 24732 + F: drivers/platform/x86/lenovo/thinkpad_acpi.c 24744 24733 24745 24734 THINKPAD LMI DRIVER 24746 - M: Mark Pearson <markpearson@lenovo.com> 24735 + M: Mark Pearson <mpearson-lenovo@squebb.ca> 24747 24736 L: platform-driver-x86@vger.kernel.org 24748 24737 S: Maintained 24749 24738 F: Documentation/ABI/testing/sysfs-class-firmware-attributes 24750 - F: drivers/platform/x86/think-lmi.? 24739 + F: drivers/platform/x86/lenovo/think-lmi.? 24751 24740 24752 24741 THP7312 ISP DRIVER 24753 24742 M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-37
arch/x86/include/asm/intel_telemetry.h
··· 59 59 }; 60 60 61 61 struct telemetry_core_ops { 62 - int (*get_sampling_period)(u8 *pss_min_period, u8 *pss_max_period, 63 - u8 *ioss_min_period, u8 *ioss_max_period); 64 - 65 - int (*get_eventconfig)(struct telemetry_evtconfig *pss_evtconfig, 66 - struct telemetry_evtconfig *ioss_evtconfig, 67 - int pss_len, int ioss_len); 68 - 69 - int (*update_events)(struct telemetry_evtconfig pss_evtconfig, 70 - struct telemetry_evtconfig ioss_evtconfig); 71 - 72 - int (*set_sampling_period)(u8 pss_period, u8 ioss_period); 73 - 74 62 int (*get_trace_verbosity)(enum telemetry_unit telem_unit, 75 63 u32 *verbosity); 76 64 ··· 72 84 int (*read_eventlog)(enum telemetry_unit telem_unit, 73 85 struct telemetry_evtlog *evtlog, 74 86 int len, int log_all_evts); 75 - 76 - int (*add_events)(u8 num_pss_evts, u8 num_ioss_evts, 77 - u32 *pss_evtmap, u32 *ioss_evtmap); 78 - 79 - int (*reset_events)(void); 80 87 }; 81 88 82 89 int telemetry_set_pltdata(const struct telemetry_core_ops *ops, ··· 84 101 int telemetry_get_evtname(enum telemetry_unit telem_unit, 85 102 const char **name, int len); 86 103 87 - int telemetry_update_events(struct telemetry_evtconfig pss_evtconfig, 88 - struct telemetry_evtconfig ioss_evtconfig); 89 - 90 - int telemetry_add_events(u8 num_pss_evts, u8 num_ioss_evts, 91 - u32 *pss_evtmap, u32 *ioss_evtmap); 92 - 93 - int telemetry_reset_events(void); 94 - 95 - int telemetry_get_eventconfig(struct telemetry_evtconfig *pss_config, 96 - struct telemetry_evtconfig *ioss_config, 97 - int pss_len, int ioss_len); 98 - 99 104 int telemetry_read_events(enum telemetry_unit telem_unit, 100 105 struct telemetry_evtlog *evtlog, int len); 101 - 102 - int telemetry_raw_read_events(enum telemetry_unit telem_unit, 103 - struct telemetry_evtlog *evtlog, int len); 104 106 105 107 int telemetry_read_eventlog(enum telemetry_unit telem_unit, 106 108 struct telemetry_evtlog *evtlog, int len); 107 109 108 110 int telemetry_raw_read_eventlog(enum telemetry_unit telem_unit, 109 111 struct telemetry_evtlog *evtlog, int len); 110 - 111 - int telemetry_get_sampling_period(u8 *pss_min_period, u8 *pss_max_period, 112 - u8 *ioss_min_period, u8 *ioss_max_period); 113 - 114 - int telemetry_set_sampling_period(u8 pss_period, u8 ioss_period); 115 112 116 113 int telemetry_set_trace_verbosity(enum telemetry_unit telem_unit, 117 114 u32 verbosity);
+6 -14
drivers/gpu/drm/xe/xe_vsec.c
··· 24 24 #define BMG_DEVICE_ID 0xE2F8 25 25 26 26 static struct intel_vsec_header bmg_telemetry = { 27 + .rev = 1, 27 28 .length = 0x10, 28 29 .id = VSEC_ID_TELEMETRY, 29 30 .num_entries = 2, ··· 33 32 .offset = BMG_DISCOVERY_OFFSET, 34 33 }; 35 34 36 - static struct intel_vsec_header bmg_punit_crashlog = { 35 + static struct intel_vsec_header bmg_crashlog = { 36 + .rev = 1, 37 37 .length = 0x10, 38 38 .id = VSEC_ID_CRASHLOG, 39 - .num_entries = 1, 40 - .entry_size = 4, 39 + .num_entries = 2, 40 + .entry_size = 6, 41 41 .tbir = 0, 42 42 .offset = BMG_DISCOVERY_OFFSET + 0x60, 43 43 }; 44 44 45 - static struct intel_vsec_header bmg_oobmsm_crashlog = { 46 - .length = 0x10, 47 - .id = VSEC_ID_CRASHLOG, 48 - .num_entries = 1, 49 - .entry_size = 4, 50 - .tbir = 0, 51 - .offset = BMG_DISCOVERY_OFFSET + 0x78, 52 - }; 53 - 54 45 static struct intel_vsec_header *bmg_capabilities[] = { 55 46 &bmg_telemetry, 56 - &bmg_punit_crashlog, 57 - &bmg_oobmsm_crashlog, 47 + &bmg_crashlog, 58 48 NULL 59 49 }; 60 50
+3 -37
drivers/platform/arm64/lenovo-yoga-c630.c
··· 191 191 } 192 192 EXPORT_SYMBOL_GPL(yoga_c630_ec_unregister_notify); 193 193 194 - static void yoga_c630_aux_release(struct device *dev) 195 - { 196 - struct auxiliary_device *adev = to_auxiliary_dev(dev); 197 - 198 - kfree(adev); 199 - } 200 - 201 - static void yoga_c630_aux_remove(void *data) 202 - { 203 - struct auxiliary_device *adev = data; 204 - 205 - auxiliary_device_delete(adev); 206 - auxiliary_device_uninit(adev); 207 - } 208 - 209 194 static int yoga_c630_aux_init(struct device *parent, const char *name, 210 195 struct yoga_c630_ec *ec) 211 196 { 212 197 struct auxiliary_device *adev; 213 - int ret; 214 198 215 - adev = kzalloc(sizeof(*adev), GFP_KERNEL); 199 + adev = devm_auxiliary_device_create(parent, name, ec); 216 200 if (!adev) 217 - return -ENOMEM; 201 + return -ENODEV; 218 202 219 - adev->name = name; 220 - adev->id = 0; 221 - adev->dev.parent = parent; 222 - adev->dev.release = yoga_c630_aux_release; 223 - adev->dev.platform_data = ec; 224 - 225 - ret = auxiliary_device_init(adev); 226 - if (ret) { 227 - kfree(adev); 228 - return ret; 229 - } 230 - 231 - ret = auxiliary_device_add(adev); 232 - if (ret) { 233 - auxiliary_device_uninit(adev); 234 - return ret; 235 - } 236 - 237 - return devm_add_action_or_reset(parent, yoga_c630_aux_remove, adev); 203 + return 0; 238 204 } 239 205 240 206 static int yoga_c630_ec_probe(struct i2c_client *client)
+11 -228
drivers/platform/x86/Kconfig
··· 37 37 It is safe to enable this driver even if your DSDT doesn't define 38 38 any ACPI-WMI devices. 39 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 + 40 49 config WMI_BMOF 41 50 tristate "WMI embedded Binary MOF driver" 42 51 depends on ACPI_WMI ··· 128 119 129 120 To compile this driver as a module, choose M here: the module will 130 121 be called gigabyte-wmi. 131 - 132 - config YOGABOOK 133 - tristate "Lenovo Yoga Book tablet key driver" 134 - depends on ACPI_WMI 135 - depends on INPUT 136 - depends on I2C 137 - select LEDS_CLASS 138 - select NEW_LEDS 139 - help 140 - Say Y here if you want to support the 'Pen' key and keyboard backlight 141 - control on the Lenovo Yoga Book tablets. 142 - 143 - To compile this driver as a module, choose M here: the module will 144 - be called lenovo-yogabook. 145 - 146 - config YT2_1380 147 - tristate "Lenovo Yoga Tablet 2 1380 fast charge driver" 148 - depends on SERIAL_DEV_BUS 149 - depends on EXTCON 150 - depends on ACPI 151 - help 152 - Say Y here to enable support for the custom fast charging protocol 153 - found on the Lenovo Yoga Tablet 2 1380F / 1380L models. 154 - 155 - To compile this driver as a module, choose M here: the module will 156 - be called lenovo-yogabook. 157 122 158 123 config ACERHDF 159 124 tristate "Acer Aspire One temperature and fan driver" ··· 442 459 state = 0 (BIOS SMIs on) 443 460 state = 1 (BIOS SMIs off) 444 461 445 - config IDEAPAD_LAPTOP 446 - tristate "Lenovo IdeaPad Laptop Extras" 447 - depends on ACPI 448 - depends on RFKILL && INPUT 449 - depends on SERIO_I8042 450 - depends on BACKLIGHT_CLASS_DEVICE 451 - depends on ACPI_VIDEO || ACPI_VIDEO = n 452 - depends on ACPI_WMI || ACPI_WMI = n 453 - select ACPI_PLATFORM_PROFILE 454 - select INPUT_SPARSEKMAP 455 - select NEW_LEDS 456 - select LEDS_CLASS 457 - help 458 - This is a driver for Lenovo IdeaPad netbooks contains drivers for 459 - rfkill switch, hotkey, fan control and backlight control. 460 - 461 - config LENOVO_WMI_HOTKEY_UTILITIES 462 - tristate "Lenovo Hotkey Utility WMI extras driver" 463 - depends on ACPI_WMI 464 - select NEW_LEDS 465 - select LEDS_CLASS 466 - imply IDEAPAD_LAPTOP 467 - help 468 - This driver provides WMI support for Lenovo customized hotkeys function, 469 - such as LED control for audio/mic mute event for Ideapad, YOGA, XiaoXin, 470 - Gaming, ThinkBook and so on. 471 - 472 - config LENOVO_YMC 473 - tristate "Lenovo Yoga Tablet Mode Control" 474 - depends on ACPI_WMI 475 - depends on INPUT 476 - depends on IDEAPAD_LAPTOP 477 - select INPUT_SPARSEKMAP 478 - help 479 - This driver maps the Tablet Mode Control switch to SW_TABLET_MODE input 480 - events for Lenovo Yoga notebooks. 481 - 482 462 config SENSORS_HDAPS 483 463 tristate "Thinkpad Hard Drive Active Protection System (hdaps)" 484 464 depends on INPUT ··· 460 514 Say Y here if you have an applicable laptop and want to experience 461 515 the awesome power of hdaps. 462 516 463 - config THINKPAD_ACPI 464 - tristate "ThinkPad ACPI Laptop Extras" 465 - depends on ACPI_EC 466 - depends on ACPI_BATTERY 467 - depends on INPUT 468 - depends on RFKILL || RFKILL = n 469 - depends on ACPI_VIDEO || ACPI_VIDEO = n 470 - depends on BACKLIGHT_CLASS_DEVICE 471 - depends on I2C 472 - depends on DRM 473 - select ACPI_PLATFORM_PROFILE 474 - select DRM_PRIVACY_SCREEN 475 - select HWMON 476 - select NVRAM 477 - select NEW_LEDS 478 - select LEDS_CLASS 479 - select INPUT_SPARSEKMAP 480 - help 481 - This is a driver for the IBM and Lenovo ThinkPad laptops. It adds 482 - support for Fn-Fx key combinations, Bluetooth control, video 483 - output switching, ThinkLight control, UltraBay eject and more. 484 - For more information about this driver see 485 - <file:Documentation/admin-guide/laptops/thinkpad-acpi.rst> and 486 - <http://ibm-acpi.sf.net/> . 487 - 488 - This driver was formerly known as ibm-acpi. 489 - 490 - Extra functionality will be available if the rfkill (CONFIG_RFKILL) 491 - and/or ALSA (CONFIG_SND) subsystems are available in the kernel. 492 - Note that if you want ThinkPad-ACPI to be built-in instead of 493 - modular, ALSA and rfkill will also have to be built-in. 494 - 495 - If you have an IBM or Lenovo ThinkPad laptop, say Y or M here. 496 - 497 - config THINKPAD_ACPI_ALSA_SUPPORT 498 - bool "Console audio control ALSA interface" 499 - depends on THINKPAD_ACPI 500 - depends on SND 501 - depends on SND = y || THINKPAD_ACPI = SND 502 - default y 503 - help 504 - Enables monitoring of the built-in console audio output control 505 - (headphone and speakers), which is operated by the mute and (in 506 - some ThinkPad models) volume hotkeys. 507 - 508 - If this option is enabled, ThinkPad-ACPI will export an ALSA card 509 - with a single read-only mixer control, which should be used for 510 - on-screen-display feedback purposes by the Desktop Environment. 511 - 512 - Optionally, the driver will also allow software control (the 513 - ALSA mixer will be made read-write). Please refer to the driver 514 - documentation for details. 515 - 516 - All IBM models have both volume and mute control. Newer Lenovo 517 - models only have mute control (the volume hotkeys are just normal 518 - keys and volume control is done through the main HDA mixer). 519 - 520 - config THINKPAD_ACPI_DEBUGFACILITIES 521 - bool "Maintainer debug facilities" 522 - depends on THINKPAD_ACPI 523 - help 524 - Enables extra stuff in the thinkpad-acpi which is completely useless 525 - for normal use. Read the driver source to find out what it does. 526 - 527 - Say N here, unless you were told by a kernel maintainer to do 528 - otherwise. 529 - 530 - config THINKPAD_ACPI_DEBUG 531 - bool "Verbose debug mode" 532 - depends on THINKPAD_ACPI 533 - help 534 - Enables extra debugging information, at the expense of a slightly 535 - increase in driver size. 536 - 537 - If you are not sure, say N here. 538 - 539 - config THINKPAD_ACPI_UNSAFE_LEDS 540 - bool "Allow control of important LEDs (unsafe)" 541 - depends on THINKPAD_ACPI 542 - help 543 - Overriding LED state on ThinkPads can mask important 544 - firmware alerts (like critical battery condition), or misled 545 - the user into damaging the hardware (undocking or ejecting 546 - the bay while buses are still active), etc. 547 - 548 - LED control on the ThinkPad is write-only (with very few 549 - exceptions on very ancient models), which makes it 550 - impossible to know beforehand if important information will 551 - be lost when one changes LED state. 552 - 553 - Users that know what they are doing can enable this option 554 - and the driver will allow control of every LED, including 555 - the ones on the dock stations. 556 - 557 - Never enable this option on a distribution kernel. 558 - 559 - Say N here, unless you are building a kernel for your own 560 - use, and need to control the important firmware LEDs. 561 - 562 - config THINKPAD_ACPI_VIDEO 563 - bool "Video output control support" 564 - depends on THINKPAD_ACPI 565 - default y 566 - help 567 - Allows the thinkpad_acpi driver to provide an interface to control 568 - the various video output ports. 569 - 570 - This feature often won't work well, depending on ThinkPad model, 571 - display state, video output devices in use, whether there is a X 572 - server running, phase of the moon, and the current mood of 573 - Schroedinger's cat. If you can use X.org's RandR to control 574 - your ThinkPad's video output ports instead of this feature, 575 - don't think twice: do it and say N here to save memory and avoid 576 - bad interactions with X.org. 577 - 578 - NOTE: access to this feature is limited to processes with the 579 - CAP_SYS_ADMIN capability, to avoid local DoS issues in platforms 580 - where it interacts badly with X.org. 581 - 582 - If you are not sure, say Y here but do try to check if you could 583 - be using X.org RandR instead. 584 - 585 - config THINKPAD_ACPI_HOTKEY_POLL 586 - bool "Support NVRAM polling for hot keys" 587 - depends on THINKPAD_ACPI 588 - default y 589 - help 590 - Some thinkpad models benefit from NVRAM polling to detect a few of 591 - the hot key press events. If you know your ThinkPad model does not 592 - need to do NVRAM polling to support any of the hot keys you use, 593 - unselecting this option will save about 1kB of memory. 594 - 595 - ThinkPads T40 and newer, R52 and newer, and X31 and newer are 596 - unlikely to need NVRAM polling in their latest BIOS versions. 597 - 598 - NVRAM polling can detect at most the following keys: ThinkPad/Access 599 - IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute, 600 - Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12). 601 - 602 - If you are not sure, say Y here. The driver enables polling only if 603 - it is strictly necessary to do so. 604 - 605 - config THINKPAD_LMI 606 - tristate "Lenovo WMI-based systems management driver" 607 - depends on ACPI_WMI 608 - select FW_ATTR_CLASS 609 - help 610 - This driver allows changing BIOS settings on Lenovo machines whose 611 - BIOS support the WMI interface. 612 - 613 - To compile this driver as a module, choose M here: the module will 614 - be called think-lmi. 615 - 616 517 source "drivers/platform/x86/intel/Kconfig" 518 + source "drivers/platform/x86/lenovo/Kconfig" 617 519 618 520 config ACPI_QUICKSTART 619 521 tristate "ACPI Quickstart button driver" ··· 619 825 tristate "Samsung Laptop driver" 620 826 depends on RFKILL || RFKILL = n 621 827 depends on ACPI_VIDEO || ACPI_VIDEO = n 828 + depends on ACPI_BATTERY 622 829 depends on BACKLIGHT_CLASS_DEVICE 623 830 select LEDS_CLASS 624 831 select NEW_LEDS ··· 872 1077 873 1078 To compile this driver as a module, choose M here: the module 874 1079 will be called inspur-platform-profile. 875 - 876 - config LENOVO_WMI_CAMERA 877 - tristate "Lenovo WMI Camera Button driver" 878 - depends on ACPI_WMI 879 - depends on INPUT 880 - help 881 - This driver provides support for Lenovo camera button. The Camera 882 - button is a GPIO device. This driver receives ACPI notifications when 883 - the camera button is switched on/off. 884 - 885 - To compile this driver as a module, choose M here: the module 886 - will be called lenovo-wmi-camera. 887 1080 888 1081 config DASHARO_ACPI 889 1082 tristate "Dasharo ACPI Platform Driver"
+4 -9
drivers/platform/x86/Makefile
··· 60 60 61 61 obj-$(CONFIG_FW_ATTR_CLASS) += firmware_attributes_class.o 62 62 63 - # IBM Thinkpad and Lenovo 63 + # IBM Thinkpad (before 2005) 64 64 obj-$(CONFIG_IBM_RTL) += ibm_rtl.o 65 - obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o 66 - obj-$(CONFIG_LENOVO_WMI_HOTKEY_UTILITIES) += lenovo-wmi-hotkey-utilities.o 67 - obj-$(CONFIG_LENOVO_YMC) += lenovo-ymc.o 68 65 obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o 69 - obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o 70 - obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o 71 - obj-$(CONFIG_YOGABOOK) += lenovo-yogabook.o 72 - obj-$(CONFIG_YT2_1380) += lenovo-yoga-tab2-pro-1380-fastcharger.o 73 - obj-$(CONFIG_LENOVO_WMI_CAMERA) += lenovo-wmi-camera.o 66 + 67 + # Lenovo 68 + obj-y += lenovo/ 74 69 75 70 # Intel 76 71 obj-y += intel/
+7 -2
drivers/platform/x86/amd/hsmp/acpi.c
··· 587 587 588 588 if (!hsmp_pdev->is_probed) { 589 589 hsmp_pdev->num_sockets = amd_num_nodes(); 590 - if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES) 590 + if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES) { 591 + dev_err(&pdev->dev, "Wrong number of sockets\n"); 591 592 return -ENODEV; 593 + } 592 594 593 595 hsmp_pdev->sock = devm_kcalloc(&pdev->dev, hsmp_pdev->num_sockets, 594 596 sizeof(*hsmp_pdev->sock), ··· 607 605 608 606 if (!hsmp_pdev->is_probed) { 609 607 ret = hsmp_misc_register(&pdev->dev); 610 - if (ret) 608 + if (ret) { 609 + dev_err(&pdev->dev, "Failed to register misc device\n"); 611 610 return ret; 611 + } 612 612 hsmp_pdev->is_probed = true; 613 + dev_dbg(&pdev->dev, "AMD HSMP ACPI is probed successfully\n"); 613 614 } 614 615 615 616 return 0;
+2 -1
drivers/platform/x86/amd/hsmp/hsmp.h
··· 13 13 #include <linux/compiler_types.h> 14 14 #include <linux/device.h> 15 15 #include <linux/hwmon.h> 16 + #include <linux/kconfig.h> 16 17 #include <linux/miscdevice.h> 17 18 #include <linux/pci.h> 18 19 #include <linux/semaphore.h> ··· 65 64 int hsmp_get_tbl_dram_base(u16 sock_ind); 66 65 ssize_t hsmp_metric_tbl_read(struct hsmp_socket *sock, char *buf, size_t size); 67 66 struct hsmp_plat_device *get_hsmp_pdev(void); 68 - #if IS_REACHABLE(CONFIG_HWMON) 67 + #if IS_ENABLED(CONFIG_HWMON) 69 68 int hsmp_create_sensor(struct device *dev, u16 sock_ind); 70 69 #else 71 70 static inline int hsmp_create_sensor(struct device *dev, u16 sock_ind) { return 0; }
+22 -6
drivers/platform/x86/amd/hsmp/plat.c
··· 14 14 #include <linux/acpi.h> 15 15 #include <linux/build_bug.h> 16 16 #include <linux/device.h> 17 + #include <linux/dev_printk.h> 18 + #include <linux/kconfig.h> 17 19 #include <linux/module.h> 18 20 #include <linux/pci.h> 19 21 #include <linux/platform_device.h> ··· 217 215 return ret; 218 216 } 219 217 220 - return hsmp_misc_register(&pdev->dev); 218 + ret = hsmp_misc_register(&pdev->dev); 219 + if (ret) { 220 + dev_err(&pdev->dev, "Failed to register misc device\n"); 221 + return ret; 222 + } 223 + 224 + dev_dbg(&pdev->dev, "AMD HSMP is probed successfully\n"); 225 + return 0; 221 226 } 222 227 223 228 static void hsmp_pltdrv_remove(struct platform_device *pdev) ··· 296 287 { 297 288 int ret = -ENODEV; 298 289 290 + if (acpi_dev_present(ACPI_HSMP_DEVICE_HID, NULL, -1)) { 291 + if (IS_ENABLED(CONFIG_AMD_HSMP_ACPI)) 292 + pr_debug("HSMP is supported through ACPI on this platform, please use hsmp_acpi.ko\n"); 293 + else 294 + pr_info("HSMP is supported through ACPI on this platform, please enable AMD_HSMP_ACPI config\n"); 295 + return -ENODEV; 296 + } 297 + 299 298 if (!legacy_hsmp_support()) { 300 - pr_info("HSMP is not supported on Family:%x model:%x\n", 299 + pr_info("HSMP interface is either disabled or not supported on family:%x model:%x\n", 301 300 boot_cpu_data.x86, boot_cpu_data.x86_model); 302 301 return ret; 303 302 } 304 - 305 - if (acpi_dev_present(ACPI_HSMP_DEVICE_HID, NULL, -1)) 306 - return -ENODEV; 307 303 308 304 hsmp_pdev = get_hsmp_pdev(); 309 305 if (!hsmp_pdev) ··· 319 305 * if we have N SMN/DF interfaces that ideally means N sockets 320 306 */ 321 307 hsmp_pdev->num_sockets = amd_num_nodes(); 322 - if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES) 308 + if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES) { 309 + pr_err("Wrong number of sockets\n"); 323 310 return ret; 311 + } 324 312 325 313 ret = platform_driver_register(&amd_hsmp_driver); 326 314 if (ret)
+9
drivers/platform/x86/amd/pmc/pmc-quirks.c
··· 190 190 DMI_MATCH(DMI_PRODUCT_NAME, "82XQ"), 191 191 } 192 192 }, 193 + /* https://gitlab.freedesktop.org/drm/amd/-/issues/4434 */ 194 + { 195 + .ident = "Lenovo Yoga 6 13ALC6", 196 + .driver_data = &quirk_s2idle_bug, 197 + .matches = { 198 + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), 199 + DMI_MATCH(DMI_PRODUCT_NAME, "82ND"), 200 + } 201 + }, 193 202 /* https://gitlab.freedesktop.org/drm/amd/-/issues/2684 */ 194 203 { 195 204 .ident = "HP Laptop 15s-eq2xxx",
+63 -37
drivers/platform/x86/dell/alienware-wmi-wmax.c
··· 290 290 291 291 enum AWCC_TEMP_SENSOR_TYPES { 292 292 AWCC_TEMP_SENSOR_CPU = 0x01, 293 + AWCC_TEMP_SENSOR_FRONT = 0x03, 293 294 AWCC_TEMP_SENSOR_GPU = 0x06, 295 + }; 296 + 297 + enum AWCC_FAN_TYPES { 298 + AWCC_FAN_CPU_1 = 0x32, 299 + AWCC_FAN_GPU_1 = 0x33, 300 + AWCC_FAN_PCI = 0x34, 301 + AWCC_FAN_MID = 0x35, 302 + AWCC_FAN_TOP_1 = 0x36, 303 + AWCC_FAN_SIDE = 0x37, 304 + AWCC_FAN_U2_1 = 0x38, 305 + AWCC_FAN_U2_2 = 0x39, 306 + AWCC_FAN_FRONT_1 = 0x3A, 307 + AWCC_FAN_CPU_2 = 0x3B, 308 + AWCC_FAN_GPU_2 = 0x3C, 309 + AWCC_FAN_TOP_2 = 0x3D, 310 + AWCC_FAN_TOP_3 = 0x3E, 311 + AWCC_FAN_FRONT_2 = 0x3F, 312 + AWCC_FAN_BOTTOM_1 = 0x40, 313 + AWCC_FAN_BOTTOM_2 = 0x41, 294 314 }; 295 315 296 316 enum awcc_thermal_profile { ··· 351 331 352 332 struct awcc_fan_data { 353 333 unsigned long auto_channels_temp; 354 - const char *label; 355 334 u32 min_rpm; 356 335 u32 max_rpm; 357 336 u8 suspend_cache; ··· 932 913 case AWCC_TEMP_SENSOR_CPU: 933 914 *str = "CPU"; 934 915 break; 916 + case AWCC_TEMP_SENSOR_FRONT: 917 + *str = "Front"; 918 + break; 935 919 case AWCC_TEMP_SENSOR_GPU: 936 920 *str = "GPU"; 937 921 break; ··· 945 923 946 924 break; 947 925 case hwmon_fan: 948 - *str = priv->fan_data[channel]->label; 926 + switch (priv->fan_data[channel]->id) { 927 + case AWCC_FAN_CPU_1: 928 + case AWCC_FAN_CPU_2: 929 + *str = "CPU Fan"; 930 + break; 931 + case AWCC_FAN_GPU_1: 932 + case AWCC_FAN_GPU_2: 933 + *str = "GPU Fan"; 934 + break; 935 + case AWCC_FAN_PCI: 936 + *str = "PCI Fan"; 937 + break; 938 + case AWCC_FAN_MID: 939 + *str = "Mid Fan"; 940 + break; 941 + case AWCC_FAN_TOP_1: 942 + case AWCC_FAN_TOP_2: 943 + case AWCC_FAN_TOP_3: 944 + *str = "Top Fan"; 945 + break; 946 + case AWCC_FAN_SIDE: 947 + *str = "Side Fan"; 948 + break; 949 + case AWCC_FAN_U2_1: 950 + case AWCC_FAN_U2_2: 951 + *str = "U.2 Fan"; 952 + break; 953 + case AWCC_FAN_FRONT_1: 954 + case AWCC_FAN_FRONT_2: 955 + *str = "Front Fan"; 956 + break; 957 + case AWCC_FAN_BOTTOM_1: 958 + case AWCC_FAN_BOTTOM_2: 959 + *str = "Bottom Fan"; 960 + break; 961 + default: 962 + *str = "Unknown Fan"; 963 + break; 964 + } 965 + 949 966 break; 950 967 default: 951 968 return -EOPNOTSUPP; ··· 1129 1068 return 0; 1130 1069 } 1131 1070 1132 - static char *awcc_get_fan_label(unsigned long *fan_temps) 1133 - { 1134 - unsigned int temp_count = bitmap_weight(fan_temps, AWCC_ID_BITMAP_SIZE); 1135 - char *label; 1136 - u8 temp_id; 1137 - 1138 - switch (temp_count) { 1139 - case 0: 1140 - label = "Independent Fan"; 1141 - break; 1142 - case 1: 1143 - temp_id = find_first_bit(fan_temps, AWCC_ID_BITMAP_SIZE); 1144 - 1145 - switch (temp_id) { 1146 - case AWCC_TEMP_SENSOR_CPU: 1147 - label = "Processor Fan"; 1148 - break; 1149 - case AWCC_TEMP_SENSOR_GPU: 1150 - label = "Video Fan"; 1151 - break; 1152 - default: 1153 - label = "Unknown Fan"; 1154 - break; 1155 - } 1156 - 1157 - break; 1158 - default: 1159 - label = "Shared Fan"; 1160 - break; 1161 - } 1162 - 1163 - return label; 1164 - } 1165 - 1166 1071 static int awcc_hwmon_fans_init(struct wmi_device *wdev) 1167 1072 { 1168 1073 struct awcc_priv *priv = dev_get_drvdata(&wdev->dev); ··· 1182 1155 fan_data->id = id; 1183 1156 fan_data->min_rpm = min_rpm; 1184 1157 fan_data->max_rpm = max_rpm; 1185 - fan_data->label = awcc_get_fan_label(fan_temps); 1186 1158 bitmap_gather(gather, fan_temps, priv->temp_sensors, AWCC_ID_BITMAP_SIZE); 1187 1159 bitmap_copy(&fan_data->auto_channels_temp, gather, BITS_PER_LONG); 1188 1160 priv->fan_data[i] = fan_data;
+1 -1
drivers/platform/x86/dell/dell-uart-backlight.c
··· 305 305 dev_dbg(dev, "Firmware version: %.*s\n", resp[RESP_LEN] - 3, resp + RESP_DATA); 306 306 307 307 /* Initialize bl_power to a known value */ 308 - ret = dell_uart_set_bl_power(dell_bl, FB_BLANK_UNBLANK); 308 + ret = dell_uart_set_bl_power(dell_bl, BACKLIGHT_POWER_ON); 309 309 if (ret) 310 310 return ret; 311 311
+5 -5
drivers/platform/x86/dell/dell_rbu.c
··· 77 77 int ordernum; 78 78 }; 79 79 80 - static struct packet_data packet_data_head; 80 + static struct list_head packet_data_list; 81 81 82 82 static struct platform_device *rbu_device; 83 83 static int context; 84 84 85 85 static void init_packet_head(void) 86 86 { 87 - INIT_LIST_HEAD(&packet_data_head.list); 87 + INIT_LIST_HEAD(&packet_data_list); 88 88 rbu_data.packet_read_count = 0; 89 89 rbu_data.num_packets = 0; 90 90 rbu_data.packetsize = 0; ··· 183 183 184 184 /* initialize the newly created packet headers */ 185 185 INIT_LIST_HEAD(&newpacket->list); 186 - list_add_tail(&newpacket->list, &packet_data_head.list); 186 + list_add_tail(&newpacket->list, &packet_data_list); 187 187 188 188 memcpy(newpacket->data, data, length); 189 189 ··· 292 292 remaining_bytes = *pread_length; 293 293 bytes_read = rbu_data.packet_read_count; 294 294 295 - list_for_each_entry(newpacket, &packet_data_head.list, list) { 295 + list_for_each_entry(newpacket, &packet_data_list, list) { 296 296 bytes_copied = do_packet_read(pdest, newpacket, 297 297 remaining_bytes, bytes_read, &temp_count); 298 298 remaining_bytes -= bytes_copied; ··· 315 315 { 316 316 struct packet_data *newpacket, *tmp; 317 317 318 - list_for_each_entry_safe(newpacket, tmp, &packet_data_head.list, list) { 318 + list_for_each_entry_safe(newpacket, tmp, &packet_data_list, list) { 319 319 list_del(&newpacket->list); 320 320 321 321 /*
+6 -2
drivers/platform/x86/fujitsu-laptop.c
··· 180 180 const char *buf, size_t count) 181 181 { 182 182 int cc_end_value, s006_cc_return; 183 - int value, ret; 183 + unsigned int value; 184 + int ret; 184 185 185 186 ret = kstrtouint(buf, 10, &value); 186 187 if (ret) 187 188 return ret; 188 189 189 - if (value < 50 || value > 100) 190 + if (value > 100) 190 191 return -EINVAL; 192 + 193 + if (value < 50) 194 + value = 50; 191 195 192 196 cc_end_value = value * 0x100 + 0x20; 193 197 s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD,
+107 -3
drivers/platform/x86/ideapad-laptop.c drivers/platform/x86/lenovo/ideapad-laptop.c
··· 28 28 #include <linux/module.h> 29 29 #include <linux/platform_device.h> 30 30 #include <linux/platform_profile.h> 31 + #include <linux/power_supply.h> 31 32 #include <linux/rfkill.h> 32 33 #include <linux/seq_file.h> 33 34 #include <linux/sysfs.h> ··· 36 35 #include <linux/wmi.h> 37 36 #include "ideapad-laptop.h" 38 37 38 + #include <acpi/battery.h> 39 39 #include <acpi/video.h> 40 40 41 41 #include <dt-bindings/leds/common.h> ··· 165 163 struct backlight_device *blightdev; 166 164 struct ideapad_dytc_priv *dytc; 167 165 struct dentry *debug; 166 + struct acpi_battery_hook battery_hook; 168 167 unsigned long cfg; 169 168 unsigned long r_touchpad_val; 170 169 struct { ··· 607 604 608 605 static DEVICE_ATTR_RW(camera_power); 609 606 607 + static void show_conservation_mode_deprecation_warning(struct device *dev) 608 + { 609 + dev_warn_once(dev, "conservation_mode attribute has been deprecated, see charge_types.\n"); 610 + } 611 + 610 612 static ssize_t conservation_mode_show(struct device *dev, 611 613 struct device_attribute *attr, 612 614 char *buf) ··· 619 611 struct ideapad_private *priv = dev_get_drvdata(dev); 620 612 unsigned long result; 621 613 int err; 614 + 615 + show_conservation_mode_deprecation_warning(dev); 622 616 623 617 err = eval_gbmd(priv->adev->handle, &result); 624 618 if (err) ··· 636 626 struct ideapad_private *priv = dev_get_drvdata(dev); 637 627 bool state; 638 628 int err; 629 + 630 + show_conservation_mode_deprecation_warning(dev); 639 631 640 632 err = kstrtobool(buf, &state); 641 633 if (err) ··· 2000 1988 {} 2001 1989 }; 2002 1990 2003 - static void ideapad_check_features(struct ideapad_private *priv) 1991 + static int ideapad_psy_ext_set_prop(struct power_supply *psy, 1992 + const struct power_supply_ext *ext, 1993 + void *ext_data, 1994 + enum power_supply_property psp, 1995 + const union power_supply_propval *val) 1996 + { 1997 + struct ideapad_private *priv = ext_data; 1998 + 1999 + switch (val->intval) { 2000 + case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE: 2001 + return exec_sbmc(priv->adev->handle, SBMC_CONSERVATION_ON); 2002 + case POWER_SUPPLY_CHARGE_TYPE_STANDARD: 2003 + return exec_sbmc(priv->adev->handle, SBMC_CONSERVATION_OFF); 2004 + default: 2005 + return -EINVAL; 2006 + } 2007 + } 2008 + 2009 + static int ideapad_psy_ext_get_prop(struct power_supply *psy, 2010 + const struct power_supply_ext *ext, 2011 + void *ext_data, 2012 + enum power_supply_property psp, 2013 + union power_supply_propval *val) 2014 + { 2015 + struct ideapad_private *priv = ext_data; 2016 + unsigned long result; 2017 + int err; 2018 + 2019 + err = eval_gbmd(priv->adev->handle, &result); 2020 + if (err) 2021 + return err; 2022 + 2023 + if (test_bit(GBMD_CONSERVATION_STATE_BIT, &result)) 2024 + val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE; 2025 + else 2026 + val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; 2027 + 2028 + return 0; 2029 + } 2030 + 2031 + static int ideapad_psy_prop_is_writeable(struct power_supply *psy, 2032 + const struct power_supply_ext *ext, 2033 + void *data, 2034 + enum power_supply_property psp) 2035 + { 2036 + return true; 2037 + } 2038 + 2039 + static const enum power_supply_property ideapad_power_supply_props[] = { 2040 + POWER_SUPPLY_PROP_CHARGE_TYPES, 2041 + }; 2042 + 2043 + static const struct power_supply_ext ideapad_battery_ext = { 2044 + .name = "ideapad_laptop", 2045 + .properties = ideapad_power_supply_props, 2046 + .num_properties = ARRAY_SIZE(ideapad_power_supply_props), 2047 + .charge_types = (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) | 2048 + BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)), 2049 + .get_property = ideapad_psy_ext_get_prop, 2050 + .set_property = ideapad_psy_ext_set_prop, 2051 + .property_is_writeable = ideapad_psy_prop_is_writeable, 2052 + }; 2053 + 2054 + static int ideapad_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) 2055 + { 2056 + struct ideapad_private *priv = container_of(hook, struct ideapad_private, battery_hook); 2057 + 2058 + return power_supply_register_extension(battery, &ideapad_battery_ext, 2059 + &priv->platform_device->dev, priv); 2060 + } 2061 + 2062 + static int ideapad_battery_remove(struct power_supply *battery, 2063 + struct acpi_battery_hook *hook) 2064 + { 2065 + power_supply_unregister_extension(battery, &ideapad_battery_ext); 2066 + 2067 + return 0; 2068 + } 2069 + 2070 + static int ideapad_check_features(struct ideapad_private *priv) 2004 2071 { 2005 2072 acpi_handle handle = priv->adev->handle; 2006 2073 unsigned long val; 2074 + int err; 2007 2075 2008 2076 priv->features.set_fn_lock_led = 2009 2077 set_fn_lock_led || dmi_check_system(set_fn_lock_led_list); ··· 2098 2006 if (!read_ec_data(handle, VPCCMD_R_FAN, &val)) 2099 2007 priv->features.fan_mode = true; 2100 2008 2101 - if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) 2009 + if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) { 2102 2010 priv->features.conservation_mode = true; 2011 + priv->battery_hook.add_battery = ideapad_battery_add; 2012 + priv->battery_hook.remove_battery = ideapad_battery_remove; 2013 + priv->battery_hook.name = "Ideapad Battery Extension"; 2014 + 2015 + err = devm_battery_hook_register(&priv->platform_device->dev, &priv->battery_hook); 2016 + if (err) 2017 + return err; 2018 + } 2103 2019 2104 2020 if (acpi_has_method(handle, "DYTC")) 2105 2021 priv->features.dytc = true; ··· 2142 2042 } 2143 2043 } 2144 2044 } 2045 + 2046 + return 0; 2145 2047 } 2146 2048 2147 2049 #if IS_ENABLED(CONFIG_ACPI_WMI) ··· 2292 2190 if (err) 2293 2191 return err; 2294 2192 2295 - ideapad_check_features(priv); 2193 + err = ideapad_check_features(priv); 2194 + if (err) 2195 + return err; 2296 2196 2297 2197 ideapad_debugfs_init(priv); 2298 2198
drivers/platform/x86/ideapad-laptop.h drivers/platform/x86/lenovo/ideapad-laptop.h
+2 -1
drivers/platform/x86/intel/plr_tpmi.c
··· 14 14 #include <linux/err.h> 15 15 #include <linux/gfp_types.h> 16 16 #include <linux/intel_tpmi.h> 17 + #include <linux/intel_vsec.h> 17 18 #include <linux/io.h> 18 19 #include <linux/iopoll.h> 19 20 #include <linux/kstrtox.h> ··· 257 256 258 257 static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) 259 258 { 260 - struct intel_tpmi_plat_info *plat_info; 259 + struct oobmsm_plat_info *plat_info; 261 260 struct dentry *dentry; 262 261 int i, num_resources; 263 262 struct resource *res;
+28
drivers/platform/x86/intel/pmt/Kconfig
··· 18 18 config INTEL_PMT_TELEMETRY 19 19 tristate "Intel Platform Monitoring Technology (PMT) Telemetry driver" 20 20 depends on INTEL_VSEC 21 + select INTEL_PMT_DISCOVERY 21 22 select INTEL_PMT_CLASS 22 23 help 23 24 The Intel Platform Monitory Technology (PMT) Telemetry driver provides ··· 39 38 40 39 To compile this driver as a module, choose M here: the module 41 40 will be called intel_pmt_crashlog. 41 + 42 + config INTEL_PMT_DISCOVERY 43 + tristate "Intel Platform Monitoring Technology (PMT) Discovery driver" 44 + depends on INTEL_VSEC 45 + select INTEL_PMT_CLASS 46 + help 47 + The Intel Platform Monitoring Technology (PMT) discovery driver provides 48 + access to details about the various PMT features and feature specific 49 + attributes. 50 + 51 + To compile this driver as a module, choose M here: the module 52 + will be called pmt_discovery. 53 + 54 + config INTEL_PMT_KUNIT_TEST 55 + tristate "KUnit tests for Intel PMT driver" 56 + depends on INTEL_PMT_DISCOVERY 57 + depends on INTEL_PMT_TELEMETRY || !INTEL_PMT_TELEMETRY 58 + depends on KUNIT 59 + help 60 + Enable this option to compile and run a suite of KUnit tests for the Intel 61 + Platform Monitoring Technology (PMT) driver. These tests are designed to 62 + validate the driver's functionality, error handling, and overall stability, 63 + helping developers catch regressions and ensure code quality during changes. 64 + 65 + This option is intended for development and testing environments. It is 66 + recommended to disable it in production builds. To compile this driver as a 67 + module, choose M here: the module will be called pmt-discovery-kunit.
+4
drivers/platform/x86/intel/pmt/Makefile
··· 10 10 pmt_telemetry-y := telemetry.o 11 11 obj-$(CONFIG_INTEL_PMT_CRASHLOG) += pmt_crashlog.o 12 12 pmt_crashlog-y := crashlog.o 13 + obj-$(CONFIG_INTEL_PMT_DISCOVERY) += pmt_discovery.o 14 + pmt_discovery-y := discovery.o features.o 15 + obj-$(CONFIG_INTEL_PMT_KUNIT_TEST) += pmt-discovery-kunit.o 16 + pmt-discovery-kunit-y := discovery-kunit.o
+41 -9
drivers/platform/x86/intel/pmt/class.c
··· 9 9 */ 10 10 11 11 #include <linux/kernel.h> 12 + #include <linux/log2.h> 12 13 #include <linux/intel_vsec.h> 13 14 #include <linux/io-64-nonatomic-lo-hi.h> 14 15 #include <linux/module.h> 15 16 #include <linux/mm.h> 16 17 #include <linux/pci.h> 18 + #include <linux/sysfs.h> 17 19 18 20 #include "class.h" 19 21 ··· 99 97 if (count > entry->size - off) 100 98 count = entry->size - off; 101 99 102 - count = pmt_telem_read_mmio(entry->ep->pcidev, entry->cb, entry->header.guid, buf, 100 + count = pmt_telem_read_mmio(entry->pcidev, entry->cb, entry->header.guid, buf, 103 101 entry->base, off, count); 104 102 105 103 return count; ··· 168 166 &dev_attr_offset.attr, 169 167 NULL 170 168 }; 171 - ATTRIBUTE_GROUPS(intel_pmt); 172 169 173 - static struct class intel_pmt_class = { 170 + static umode_t intel_pmt_attr_visible(struct kobject *kobj, 171 + struct attribute *attr, int n) 172 + { 173 + struct device *dev = container_of(kobj, struct device, kobj); 174 + struct auxiliary_device *auxdev = to_auxiliary_dev(dev->parent); 175 + struct intel_vsec_device *ivdev = auxdev_to_ivdev(auxdev); 176 + 177 + /* 178 + * Place the discovery features folder in /sys/class/intel_pmt, but 179 + * exclude the common attributes as they are not applicable. 180 + */ 181 + if (ivdev->cap_id == ilog2(VSEC_CAP_DISCOVERY)) 182 + return 0; 183 + 184 + return attr->mode; 185 + } 186 + 187 + static bool intel_pmt_group_visible(struct kobject *kobj) 188 + { 189 + return true; 190 + } 191 + DEFINE_SYSFS_GROUP_VISIBLE(intel_pmt); 192 + 193 + static const struct attribute_group intel_pmt_group = { 194 + .attrs = intel_pmt_attrs, 195 + .is_visible = SYSFS_GROUP_VISIBLE(intel_pmt), 196 + }; 197 + __ATTRIBUTE_GROUPS(intel_pmt); 198 + 199 + struct class intel_pmt_class = { 174 200 .name = "intel_pmt", 175 201 .dev_groups = intel_pmt_groups, 176 202 }; 203 + EXPORT_SYMBOL_GPL(intel_pmt_class); 177 204 178 205 static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, 179 206 struct intel_vsec_device *ivdev, ··· 283 252 return -EINVAL; 284 253 } 285 254 255 + entry->pcidev = pci_dev; 286 256 entry->guid = header->guid; 287 257 entry->size = header->size; 288 258 entry->cb = ivdev->priv_data; ··· 316 284 317 285 entry->kobj = &dev->kobj; 318 286 319 - if (ns->attr_grp) { 320 - ret = sysfs_create_group(entry->kobj, ns->attr_grp); 287 + if (entry->attr_grp) { 288 + ret = sysfs_create_group(entry->kobj, entry->attr_grp); 321 289 if (ret) 322 290 goto fail_sysfs_create_group; 323 291 } ··· 358 326 fail_add_endpoint: 359 327 sysfs_remove_bin_file(entry->kobj, &entry->pmt_bin_attr); 360 328 fail_ioremap: 361 - if (ns->attr_grp) 362 - sysfs_remove_group(entry->kobj, ns->attr_grp); 329 + if (entry->attr_grp) 330 + sysfs_remove_group(entry->kobj, entry->attr_grp); 363 331 fail_sysfs_create_group: 364 332 device_unregister(dev); 365 333 fail_dev_create: ··· 401 369 if (entry->size) 402 370 sysfs_remove_bin_file(entry->kobj, &entry->pmt_bin_attr); 403 371 404 - if (ns->attr_grp) 405 - sysfs_remove_group(entry->kobj, ns->attr_grp); 372 + if (entry->attr_grp) 373 + sysfs_remove_group(entry->kobj, entry->attr_grp); 406 374 407 375 device_unregister(dev); 408 376 xa_erase(ns->xa, entry->devid);
+11 -1
drivers/platform/x86/intel/pmt/class.h
··· 20 20 #define GET_ADDRESS(v) ((v) & GENMASK(31, 3)) 21 21 22 22 struct pci_dev; 23 + extern struct class intel_pmt_class; 23 24 24 25 struct telem_endpoint { 25 26 struct pci_dev *pcidev; ··· 40 39 41 40 struct intel_pmt_entry { 42 41 struct telem_endpoint *ep; 42 + struct pci_dev *pcidev; 43 43 struct intel_pmt_header header; 44 44 struct bin_attribute pmt_bin_attr; 45 + const struct attribute_group *attr_grp; 45 46 struct kobject *kobj; 46 47 void __iomem *disc_table; 47 48 void __iomem *base; 48 49 struct pmt_callbacks *cb; 49 50 unsigned long base_addr; 50 51 size_t size; 52 + u64 feature_flags; 51 53 u32 guid; 54 + u32 num_rmids; /* Number of Resource Monitoring IDs */ 52 55 int devid; 53 56 }; 54 57 55 58 struct intel_pmt_namespace { 56 59 const char *name; 57 60 struct xarray *xa; 58 - const struct attribute_group *attr_grp; 59 61 int (*pmt_header_decode)(struct intel_pmt_entry *entry, 60 62 struct device *dev); 61 63 int (*pmt_add_endpoint)(struct intel_vsec_device *ivdev, ··· 73 69 struct intel_vsec_device *dev, int idx); 74 70 void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, 75 71 struct intel_pmt_namespace *ns); 72 + #if IS_ENABLED(CONFIG_INTEL_PMT_DISCOVERY) 73 + void intel_pmt_get_features(struct intel_pmt_entry *entry); 74 + #else 75 + static inline void intel_pmt_get_features(struct intel_pmt_entry *entry) {} 76 + #endif 77 + 76 78 #endif
+376 -93
drivers/platform/x86/intel/pmt/crashlog.c
··· 9 9 */ 10 10 11 11 #include <linux/auxiliary_bus.h> 12 + #include <linux/cleanup.h> 12 13 #include <linux/intel_vsec.h> 13 14 #include <linux/kernel.h> 14 15 #include <linux/module.h> 16 + #include <linux/mutex.h> 15 17 #include <linux/pci.h> 16 18 #include <linux/slab.h> 17 19 #include <linux/uaccess.h> ··· 23 21 24 22 /* Crashlog discovery header types */ 25 23 #define CRASH_TYPE_OOBMSM 1 26 - 27 - /* Control Flags */ 28 - #define CRASHLOG_FLAG_DISABLE BIT(28) 29 - 30 - /* 31 - * Bits 29 and 30 control the state of bit 31. 32 - * 33 - * Bit 29 will clear bit 31, if set, allowing a new crashlog to be captured. 34 - * Bit 30 will immediately trigger a crashlog to be generated, setting bit 31. 35 - * Bit 31 is the read-only status with a 1 indicating log is complete. 36 - */ 37 - #define CRASHLOG_FLAG_TRIGGER_CLEAR BIT(29) 38 - #define CRASHLOG_FLAG_TRIGGER_EXECUTE BIT(30) 39 - #define CRASHLOG_FLAG_TRIGGER_COMPLETE BIT(31) 40 - #define CRASHLOG_FLAG_TRIGGER_MASK GENMASK(31, 28) 41 24 42 25 /* Crashlog Discovery Header */ 43 26 #define CONTROL_OFFSET 0x0 ··· 35 48 /* size is in bytes */ 36 49 #define GET_SIZE(v) ((v) * sizeof(u32)) 37 50 51 + /* 52 + * Type 1 Version 0 53 + * status and control registers are combined. 54 + * 55 + * Bits 29 and 30 control the state of bit 31. 56 + * Bit 29 will clear bit 31, if set, allowing a new crashlog to be captured. 57 + * Bit 30 will immediately trigger a crashlog to be generated, setting bit 31. 58 + * Bit 31 is the read-only status with a 1 indicating log is complete. 59 + */ 60 + #define TYPE1_VER0_STATUS_OFFSET 0x00 61 + #define TYPE1_VER0_CONTROL_OFFSET 0x00 62 + 63 + #define TYPE1_VER0_DISABLE BIT(28) 64 + #define TYPE1_VER0_CLEAR BIT(29) 65 + #define TYPE1_VER0_EXECUTE BIT(30) 66 + #define TYPE1_VER0_COMPLETE BIT(31) 67 + #define TYPE1_VER0_TRIGGER_MASK GENMASK(31, 28) 68 + 69 + /* 70 + * Type 1 Version 2 71 + * status and control are different registers 72 + */ 73 + #define TYPE1_VER2_STATUS_OFFSET 0x00 74 + #define TYPE1_VER2_CONTROL_OFFSET 0x14 75 + 76 + /* status register */ 77 + #define TYPE1_VER2_CLEAR_SUPPORT BIT(20) 78 + #define TYPE1_VER2_REARMED BIT(25) 79 + #define TYPE1_VER2_ERROR BIT(26) 80 + #define TYPE1_VER2_CONSUMED BIT(27) 81 + #define TYPE1_VER2_DISABLED BIT(28) 82 + #define TYPE1_VER2_CLEARED BIT(29) 83 + #define TYPE1_VER2_IN_PROGRESS BIT(30) 84 + #define TYPE1_VER2_COMPLETE BIT(31) 85 + 86 + /* control register */ 87 + #define TYPE1_VER2_CONSUME BIT(25) 88 + #define TYPE1_VER2_REARM BIT(28) 89 + #define TYPE1_VER2_EXECUTE BIT(29) 90 + #define TYPE1_VER2_CLEAR BIT(30) 91 + #define TYPE1_VER2_DISABLE BIT(31) 92 + #define TYPE1_VER2_TRIGGER_MASK \ 93 + (TYPE1_VER2_EXECUTE | TYPE1_VER2_CLEAR | TYPE1_VER2_DISABLE) 94 + 95 + /* After offset, order alphabetically, not bit ordered */ 96 + struct crashlog_status { 97 + u32 offset; 98 + u32 clear_supported; 99 + u32 cleared; 100 + u32 complete; 101 + u32 consumed; 102 + u32 disabled; 103 + u32 error; 104 + u32 in_progress; 105 + u32 rearmed; 106 + }; 107 + 108 + struct crashlog_control { 109 + u32 offset; 110 + u32 trigger_mask; 111 + u32 clear; 112 + u32 consume; 113 + u32 disable; 114 + u32 manual; 115 + u32 rearm; 116 + }; 117 + 118 + struct crashlog_info { 119 + const struct crashlog_status status; 120 + const struct crashlog_control control; 121 + const struct attribute_group *attr_grp; 122 + }; 123 + 38 124 struct crashlog_entry { 39 125 /* entry must be first member of struct */ 40 126 struct intel_pmt_entry entry; 41 127 struct mutex control_mutex; 128 + const struct crashlog_info *info; 42 129 }; 43 130 44 131 struct pmt_crashlog_priv { ··· 123 62 /* 124 63 * I/O 125 64 */ 126 - static bool pmt_crashlog_complete(struct intel_pmt_entry *entry) 127 - { 128 - u32 control = readl(entry->disc_table + CONTROL_OFFSET); 129 65 66 + /* Read, modify, write the control register, setting or clearing @bit based on @set */ 67 + static void pmt_crashlog_rmw(struct crashlog_entry *crashlog, u32 bit, bool set) 68 + { 69 + const struct crashlog_control *control = &crashlog->info->control; 70 + struct intel_pmt_entry *entry = &crashlog->entry; 71 + u32 reg = readl(entry->disc_table + control->offset); 72 + 73 + reg &= ~control->trigger_mask; 74 + 75 + if (set) 76 + reg |= bit; 77 + else 78 + reg &= ~bit; 79 + 80 + writel(reg, entry->disc_table + control->offset); 81 + } 82 + 83 + /* Read the status register and see if the specified @bit is set */ 84 + static bool pmt_crashlog_rc(struct crashlog_entry *crashlog, u32 bit) 85 + { 86 + const struct crashlog_status *status = &crashlog->info->status; 87 + u32 reg = readl(crashlog->entry.disc_table + status->offset); 88 + 89 + return !!(reg & bit); 90 + } 91 + 92 + static bool pmt_crashlog_complete(struct crashlog_entry *crashlog) 93 + { 130 94 /* return current value of the crashlog complete flag */ 131 - return !!(control & CRASHLOG_FLAG_TRIGGER_COMPLETE); 95 + return pmt_crashlog_rc(crashlog, crashlog->info->status.complete); 132 96 } 133 97 134 - static bool pmt_crashlog_disabled(struct intel_pmt_entry *entry) 98 + static bool pmt_crashlog_disabled(struct crashlog_entry *crashlog) 135 99 { 136 - u32 control = readl(entry->disc_table + CONTROL_OFFSET); 137 - 138 100 /* return current value of the crashlog disabled flag */ 139 - return !!(control & CRASHLOG_FLAG_DISABLE); 101 + return pmt_crashlog_rc(crashlog, crashlog->info->status.disabled); 140 102 } 141 103 142 - static bool pmt_crashlog_supported(struct intel_pmt_entry *entry) 104 + static bool pmt_crashlog_supported(struct intel_pmt_entry *entry, u32 *crash_type, u32 *version) 143 105 { 144 106 u32 discovery_header = readl(entry->disc_table + CONTROL_OFFSET); 145 - u32 crash_type, version; 146 107 147 - crash_type = GET_TYPE(discovery_header); 148 - version = GET_VERSION(discovery_header); 108 + *crash_type = GET_TYPE(discovery_header); 109 + *version = GET_VERSION(discovery_header); 149 110 150 111 /* 151 - * Currently we only recognize OOBMSM version 0 devices. 152 - * We can ignore all other crashlog devices in the system. 112 + * Currently we only recognize OOBMSM (type 1) and version 0 or 2 113 + * devices. 114 + * 115 + * Ignore all other crashlog devices in the system. 153 116 */ 154 - return crash_type == CRASH_TYPE_OOBMSM && version == 0; 117 + if (*crash_type == CRASH_TYPE_OOBMSM && (*version == 0 || *version == 2)) 118 + return true; 119 + 120 + return false; 155 121 } 156 122 157 - static void pmt_crashlog_set_disable(struct intel_pmt_entry *entry, 123 + static void pmt_crashlog_set_disable(struct crashlog_entry *crashlog, 158 124 bool disable) 159 125 { 160 - u32 control = readl(entry->disc_table + CONTROL_OFFSET); 161 - 162 - /* clear trigger bits so we are only modifying disable flag */ 163 - control &= ~CRASHLOG_FLAG_TRIGGER_MASK; 164 - 165 - if (disable) 166 - control |= CRASHLOG_FLAG_DISABLE; 167 - else 168 - control &= ~CRASHLOG_FLAG_DISABLE; 169 - 170 - writel(control, entry->disc_table + CONTROL_OFFSET); 126 + pmt_crashlog_rmw(crashlog, crashlog->info->control.disable, disable); 171 127 } 172 128 173 - static void pmt_crashlog_set_clear(struct intel_pmt_entry *entry) 129 + static void pmt_crashlog_set_clear(struct crashlog_entry *crashlog) 174 130 { 175 - u32 control = readl(entry->disc_table + CONTROL_OFFSET); 176 - 177 - control &= ~CRASHLOG_FLAG_TRIGGER_MASK; 178 - control |= CRASHLOG_FLAG_TRIGGER_CLEAR; 179 - 180 - writel(control, entry->disc_table + CONTROL_OFFSET); 131 + pmt_crashlog_rmw(crashlog, crashlog->info->control.clear, true); 181 132 } 182 133 183 - static void pmt_crashlog_set_execute(struct intel_pmt_entry *entry) 134 + static void pmt_crashlog_set_execute(struct crashlog_entry *crashlog) 184 135 { 185 - u32 control = readl(entry->disc_table + CONTROL_OFFSET); 136 + pmt_crashlog_rmw(crashlog, crashlog->info->control.manual, true); 137 + } 186 138 187 - control &= ~CRASHLOG_FLAG_TRIGGER_MASK; 188 - control |= CRASHLOG_FLAG_TRIGGER_EXECUTE; 139 + static bool pmt_crashlog_cleared(struct crashlog_entry *crashlog) 140 + { 141 + return pmt_crashlog_rc(crashlog, crashlog->info->status.cleared); 142 + } 189 143 190 - writel(control, entry->disc_table + CONTROL_OFFSET); 144 + static bool pmt_crashlog_consumed(struct crashlog_entry *crashlog) 145 + { 146 + return pmt_crashlog_rc(crashlog, crashlog->info->status.consumed); 147 + } 148 + 149 + static void pmt_crashlog_set_consumed(struct crashlog_entry *crashlog) 150 + { 151 + pmt_crashlog_rmw(crashlog, crashlog->info->control.consume, true); 152 + } 153 + 154 + static bool pmt_crashlog_error(struct crashlog_entry *crashlog) 155 + { 156 + return pmt_crashlog_rc(crashlog, crashlog->info->status.error); 157 + } 158 + 159 + static bool pmt_crashlog_rearm(struct crashlog_entry *crashlog) 160 + { 161 + return pmt_crashlog_rc(crashlog, crashlog->info->status.rearmed); 162 + } 163 + 164 + static void pmt_crashlog_set_rearm(struct crashlog_entry *crashlog) 165 + { 166 + pmt_crashlog_rmw(crashlog, crashlog->info->control.rearm, true); 191 167 } 192 168 193 169 /* 194 170 * sysfs 195 171 */ 196 172 static ssize_t 173 + clear_show(struct device *dev, struct device_attribute *attr, char *buf) 174 + { 175 + struct crashlog_entry *crashlog = dev_get_drvdata(dev); 176 + bool cleared = pmt_crashlog_cleared(crashlog); 177 + 178 + return sysfs_emit(buf, "%d\n", cleared); 179 + } 180 + 181 + static ssize_t 182 + clear_store(struct device *dev, struct device_attribute *attr, 183 + const char *buf, size_t count) 184 + { 185 + struct crashlog_entry *crashlog; 186 + bool clear; 187 + int result; 188 + 189 + crashlog = dev_get_drvdata(dev); 190 + 191 + result = kstrtobool(buf, &clear); 192 + if (result) 193 + return result; 194 + 195 + /* set bit only */ 196 + if (!clear) 197 + return -EINVAL; 198 + 199 + guard(mutex)(&crashlog->control_mutex); 200 + 201 + pmt_crashlog_set_clear(crashlog); 202 + 203 + return count; 204 + } 205 + static DEVICE_ATTR_RW(clear); 206 + 207 + static ssize_t 208 + consumed_show(struct device *dev, struct device_attribute *attr, char *buf) 209 + { 210 + struct crashlog_entry *crashlog = dev_get_drvdata(dev); 211 + bool consumed = pmt_crashlog_consumed(crashlog); 212 + 213 + return sysfs_emit(buf, "%d\n", consumed); 214 + } 215 + 216 + static ssize_t 217 + consumed_store(struct device *dev, struct device_attribute *attr, const char *buf, 218 + size_t count) 219 + { 220 + struct crashlog_entry *crashlog; 221 + bool consumed; 222 + int result; 223 + 224 + crashlog = dev_get_drvdata(dev); 225 + 226 + result = kstrtobool(buf, &consumed); 227 + if (result) 228 + return result; 229 + 230 + /* set bit only */ 231 + if (!consumed) 232 + return -EINVAL; 233 + 234 + guard(mutex)(&crashlog->control_mutex); 235 + 236 + if (pmt_crashlog_disabled(crashlog)) 237 + return -EBUSY; 238 + 239 + if (!pmt_crashlog_complete(crashlog)) 240 + return -EEXIST; 241 + 242 + pmt_crashlog_set_consumed(crashlog); 243 + 244 + return count; 245 + } 246 + static DEVICE_ATTR_RW(consumed); 247 + 248 + static ssize_t 197 249 enable_show(struct device *dev, struct device_attribute *attr, char *buf) 198 250 { 199 - struct intel_pmt_entry *entry = dev_get_drvdata(dev); 200 - int enabled = !pmt_crashlog_disabled(entry); 251 + struct crashlog_entry *crashlog = dev_get_drvdata(dev); 252 + bool enabled = !pmt_crashlog_disabled(crashlog); 201 253 202 254 return sprintf(buf, "%d\n", enabled); 203 255 } 204 256 205 257 static ssize_t 206 258 enable_store(struct device *dev, struct device_attribute *attr, 207 - const char *buf, size_t count) 259 + const char *buf, size_t count) 208 260 { 209 - struct crashlog_entry *entry; 261 + struct crashlog_entry *crashlog; 210 262 bool enabled; 211 263 int result; 212 264 213 - entry = dev_get_drvdata(dev); 265 + crashlog = dev_get_drvdata(dev); 214 266 215 267 result = kstrtobool(buf, &enabled); 216 268 if (result) 217 269 return result; 218 270 219 - mutex_lock(&entry->control_mutex); 220 - pmt_crashlog_set_disable(&entry->entry, !enabled); 221 - mutex_unlock(&entry->control_mutex); 271 + guard(mutex)(&crashlog->control_mutex); 272 + 273 + pmt_crashlog_set_disable(crashlog, !enabled); 222 274 223 275 return count; 224 276 } 225 277 static DEVICE_ATTR_RW(enable); 226 278 227 279 static ssize_t 280 + error_show(struct device *dev, struct device_attribute *attr, char *buf) 281 + { 282 + struct crashlog_entry *crashlog = dev_get_drvdata(dev); 283 + bool error = pmt_crashlog_error(crashlog); 284 + 285 + return sysfs_emit(buf, "%d\n", error); 286 + } 287 + static DEVICE_ATTR_RO(error); 288 + 289 + static ssize_t 290 + rearm_show(struct device *dev, struct device_attribute *attr, char *buf) 291 + { 292 + struct crashlog_entry *crashlog = dev_get_drvdata(dev); 293 + int rearmed = pmt_crashlog_rearm(crashlog); 294 + 295 + return sysfs_emit(buf, "%d\n", rearmed); 296 + } 297 + 298 + static ssize_t 299 + rearm_store(struct device *dev, struct device_attribute *attr, const char *buf, 300 + size_t count) 301 + { 302 + struct crashlog_entry *crashlog; 303 + bool rearm; 304 + int result; 305 + 306 + crashlog = dev_get_drvdata(dev); 307 + 308 + result = kstrtobool(buf, &rearm); 309 + if (result) 310 + return result; 311 + 312 + /* set only */ 313 + if (!rearm) 314 + return -EINVAL; 315 + 316 + guard(mutex)(&crashlog->control_mutex); 317 + 318 + pmt_crashlog_set_rearm(crashlog); 319 + 320 + return count; 321 + } 322 + static DEVICE_ATTR_RW(rearm); 323 + 324 + static ssize_t 228 325 trigger_show(struct device *dev, struct device_attribute *attr, char *buf) 229 326 { 230 - struct intel_pmt_entry *entry; 231 - int trigger; 327 + struct crashlog_entry *crashlog; 328 + bool trigger; 232 329 233 - entry = dev_get_drvdata(dev); 234 - trigger = pmt_crashlog_complete(entry); 330 + crashlog = dev_get_drvdata(dev); 331 + trigger = pmt_crashlog_complete(crashlog); 235 332 236 333 return sprintf(buf, "%d\n", trigger); 237 334 } 238 335 239 336 static ssize_t 240 337 trigger_store(struct device *dev, struct device_attribute *attr, 241 - const char *buf, size_t count) 338 + const char *buf, size_t count) 242 339 { 243 - struct crashlog_entry *entry; 340 + struct crashlog_entry *crashlog; 244 341 bool trigger; 245 342 int result; 246 343 247 - entry = dev_get_drvdata(dev); 344 + crashlog = dev_get_drvdata(dev); 248 345 249 346 result = kstrtobool(buf, &trigger); 250 347 if (result) 251 348 return result; 252 349 253 - mutex_lock(&entry->control_mutex); 350 + guard(mutex)(&crashlog->control_mutex); 351 + 352 + /* if device is currently disabled, return busy */ 353 + if (pmt_crashlog_disabled(crashlog)) 354 + return -EBUSY; 254 355 255 356 if (!trigger) { 256 - pmt_crashlog_set_clear(&entry->entry); 257 - } else if (pmt_crashlog_complete(&entry->entry)) { 258 - /* we cannot trigger a new crash if one is still pending */ 259 - result = -EEXIST; 260 - goto err; 261 - } else if (pmt_crashlog_disabled(&entry->entry)) { 262 - /* if device is currently disabled, return busy */ 263 - result = -EBUSY; 264 - goto err; 265 - } else { 266 - pmt_crashlog_set_execute(&entry->entry); 357 + pmt_crashlog_set_clear(crashlog); 358 + return count; 267 359 } 268 360 269 - result = count; 270 - err: 271 - mutex_unlock(&entry->control_mutex); 272 - return result; 361 + /* we cannot trigger a new crash if one is still pending */ 362 + if (pmt_crashlog_complete(crashlog)) 363 + return -EEXIST; 364 + 365 + pmt_crashlog_set_execute(crashlog); 366 + 367 + return count; 273 368 } 274 369 static DEVICE_ATTR_RW(trigger); 275 370 276 - static struct attribute *pmt_crashlog_attrs[] = { 371 + static struct attribute *pmt_crashlog_type1_ver0_attrs[] = { 277 372 &dev_attr_enable.attr, 278 373 &dev_attr_trigger.attr, 279 374 NULL 280 375 }; 281 376 282 - static const struct attribute_group pmt_crashlog_group = { 283 - .attrs = pmt_crashlog_attrs, 377 + static struct attribute *pmt_crashlog_type1_ver2_attrs[] = { 378 + &dev_attr_clear.attr, 379 + &dev_attr_consumed.attr, 380 + &dev_attr_enable.attr, 381 + &dev_attr_error.attr, 382 + &dev_attr_rearm.attr, 383 + &dev_attr_trigger.attr, 384 + NULL 284 385 }; 386 + 387 + static const struct attribute_group pmt_crashlog_type1_ver0_group = { 388 + .attrs = pmt_crashlog_type1_ver0_attrs, 389 + }; 390 + 391 + static const struct attribute_group pmt_crashlog_type1_ver2_group = { 392 + .attrs = pmt_crashlog_type1_ver2_attrs, 393 + }; 394 + 395 + static const struct crashlog_info crashlog_type1_ver0 = { 396 + .status.offset = TYPE1_VER0_STATUS_OFFSET, 397 + .status.cleared = TYPE1_VER0_CLEAR, 398 + .status.complete = TYPE1_VER0_COMPLETE, 399 + .status.disabled = TYPE1_VER0_DISABLE, 400 + 401 + .control.offset = TYPE1_VER0_CONTROL_OFFSET, 402 + .control.trigger_mask = TYPE1_VER0_TRIGGER_MASK, 403 + .control.clear = TYPE1_VER0_CLEAR, 404 + .control.disable = TYPE1_VER0_DISABLE, 405 + .control.manual = TYPE1_VER0_EXECUTE, 406 + .attr_grp = &pmt_crashlog_type1_ver0_group, 407 + }; 408 + 409 + static const struct crashlog_info crashlog_type1_ver2 = { 410 + .status.offset = TYPE1_VER2_STATUS_OFFSET, 411 + .status.clear_supported = TYPE1_VER2_CLEAR_SUPPORT, 412 + .status.cleared = TYPE1_VER2_CLEARED, 413 + .status.complete = TYPE1_VER2_COMPLETE, 414 + .status.consumed = TYPE1_VER2_CONSUMED, 415 + .status.disabled = TYPE1_VER2_DISABLED, 416 + .status.error = TYPE1_VER2_ERROR, 417 + .status.in_progress = TYPE1_VER2_IN_PROGRESS, 418 + .status.rearmed = TYPE1_VER2_REARMED, 419 + 420 + .control.offset = TYPE1_VER2_CONTROL_OFFSET, 421 + .control.trigger_mask = TYPE1_VER2_TRIGGER_MASK, 422 + .control.clear = TYPE1_VER2_CLEAR, 423 + .control.consume = TYPE1_VER2_CONSUME, 424 + .control.disable = TYPE1_VER2_DISABLE, 425 + .control.manual = TYPE1_VER2_EXECUTE, 426 + .control.rearm = TYPE1_VER2_REARM, 427 + .attr_grp = &pmt_crashlog_type1_ver2_group, 428 + }; 429 + 430 + static const struct crashlog_info *select_crashlog_info(u32 type, u32 version) 431 + { 432 + if (version == 0) 433 + return &crashlog_type1_ver0; 434 + 435 + return &crashlog_type1_ver2; 436 + } 285 437 286 438 static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, 287 439 struct device *dev) ··· 502 228 void __iomem *disc_table = entry->disc_table; 503 229 struct intel_pmt_header *header = &entry->header; 504 230 struct crashlog_entry *crashlog; 231 + u32 version; 232 + u32 type; 505 233 506 - if (!pmt_crashlog_supported(entry)) 234 + if (!pmt_crashlog_supported(entry, &type, &version)) 507 235 return 1; 508 236 509 - /* initialize control mutex */ 237 + /* initialize the crashlog struct */ 510 238 crashlog = container_of(entry, struct crashlog_entry, entry); 511 239 mutex_init(&crashlog->control_mutex); 240 + 241 + crashlog->info = select_crashlog_info(type, version); 512 242 513 243 header->access_type = GET_ACCESS(readl(disc_table)); 514 244 header->guid = readl(disc_table + GUID_OFFSET); ··· 521 243 /* Size is measured in DWORDS, but accessor returns bytes */ 522 244 header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET)); 523 245 246 + entry->attr_grp = crashlog->info->attr_grp; 247 + 524 248 return 0; 525 249 } 526 250 ··· 530 250 static struct intel_pmt_namespace pmt_crashlog_ns = { 531 251 .name = "crashlog", 532 252 .xa = &crashlog_array, 533 - .attr_grp = &pmt_crashlog_group, 534 253 .pmt_header_decode = pmt_crashlog_header_decode, 535 254 }; 536 255 ··· 541 262 struct pmt_crashlog_priv *priv = auxiliary_get_drvdata(auxdev); 542 263 int i; 543 264 544 - for (i = 0; i < priv->num_entries; i++) 545 - intel_pmt_dev_destroy(&priv->entry[i].entry, &pmt_crashlog_ns); 265 + for (i = 0; i < priv->num_entries; i++) { 266 + struct crashlog_entry *crashlog = &priv->entry[i]; 267 + 268 + intel_pmt_dev_destroy(&crashlog->entry, &pmt_crashlog_ns); 269 + mutex_destroy(&crashlog->control_mutex); 270 + } 546 271 } 547 272 548 273 static int pmt_crashlog_probe(struct auxiliary_device *auxdev,
+116
drivers/platform/x86/intel/pmt/discovery-kunit.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Intel Platform Monitory Technology Discovery KUNIT tests 4 + * 5 + * Copyright (c) 2025, Intel Corporation. 6 + * All Rights Reserved. 7 + */ 8 + 9 + #include <kunit/test.h> 10 + #include <linux/err.h> 11 + #include <linux/intel_pmt_features.h> 12 + #include <linux/intel_vsec.h> 13 + #include <linux/module.h> 14 + #include <linux/slab.h> 15 + 16 + #define PMT_FEATURE_COUNT (FEATURE_MAX + 1) 17 + 18 + static void 19 + validate_pmt_regions(struct kunit *test, struct pmt_feature_group *feature_group, int feature_id) 20 + { 21 + int i; 22 + 23 + kunit_info(test, "Feature ID %d [%s] has %d regions.\n", feature_id, 24 + pmt_feature_names[feature_id], feature_group->count); 25 + 26 + for (i = 0; i < feature_group->count; i++) { 27 + struct telemetry_region *region = &feature_group->regions[i]; 28 + 29 + kunit_info(test, " - Region %d: cdie_mask=%u, package_id=%u, partition=%u, segment=%u,", 30 + i, region->plat_info.cdie_mask, region->plat_info.package_id, 31 + region->plat_info.partition, region->plat_info.segment); 32 + kunit_info(test, "\t\tbus=%u, device=%u, function=%u, guid=0x%x,", 33 + region->plat_info.bus_number, region->plat_info.device_number, 34 + region->plat_info.function_number, region->guid); 35 + kunit_info(test, "\t\taddr=%p, size=%zu, num_rmids=%u", region->addr, region->size, 36 + region->num_rmids); 37 + 38 + 39 + KUNIT_ASSERT_GE(test, region->plat_info.cdie_mask, 0); 40 + KUNIT_ASSERT_GE(test, region->plat_info.package_id, 0); 41 + KUNIT_ASSERT_GE(test, region->plat_info.partition, 0); 42 + KUNIT_ASSERT_GE(test, region->plat_info.segment, 0); 43 + KUNIT_ASSERT_GE(test, region->plat_info.bus_number, 0); 44 + KUNIT_ASSERT_GE(test, region->plat_info.device_number, 0); 45 + KUNIT_ASSERT_GE(test, region->plat_info.function_number, 0); 46 + 47 + KUNIT_ASSERT_NE(test, region->guid, 0); 48 + 49 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, (__force const void *)region->addr); 50 + } 51 + } 52 + 53 + static void linebreak(struct kunit *test) 54 + { 55 + kunit_info(test, "*****************************************************************************\n"); 56 + } 57 + 58 + static void test_intel_pmt_get_regions_by_feature(struct kunit *test) 59 + { 60 + struct pmt_feature_group *feature_group; 61 + int num_available = 0; 62 + int feature_id; 63 + 64 + /* Iterate through all possible feature IDs */ 65 + for (feature_id = 1; feature_id < PMT_FEATURE_COUNT; feature_id++, linebreak(test)) { 66 + const char *name; 67 + 68 + if (!pmt_feature_id_is_valid(feature_id)) 69 + continue; 70 + 71 + name = pmt_feature_names[feature_id]; 72 + 73 + feature_group = intel_pmt_get_regions_by_feature(feature_id); 74 + if (IS_ERR(feature_group)) { 75 + if (PTR_ERR(feature_group) == -ENOENT) 76 + kunit_warn(test, "intel_pmt_get_regions_by_feature() reporting feature %d [%s] is not present.\n", 77 + feature_id, name); 78 + else 79 + kunit_warn(test, "intel_pmt_get_regions_by_feature() returned error %ld while attempt to lookup %d [%s].\n", 80 + PTR_ERR(feature_group), feature_id, name); 81 + 82 + continue; 83 + } 84 + 85 + if (!feature_group) { 86 + kunit_warn(test, "Feature ID %d: %s is not available.\n", feature_id, name); 87 + continue; 88 + } 89 + 90 + num_available++; 91 + 92 + validate_pmt_regions(test, feature_group, feature_id); 93 + 94 + intel_pmt_put_feature_group(feature_group); 95 + } 96 + 97 + if (num_available == 0) 98 + kunit_warn(test, "No PMT region groups were available for any feature ID (0-10).\n"); 99 + } 100 + 101 + static struct kunit_case intel_pmt_discovery_test_cases[] = { 102 + KUNIT_CASE(test_intel_pmt_get_regions_by_feature), 103 + {} 104 + }; 105 + 106 + static struct kunit_suite intel_pmt_discovery_test_suite = { 107 + .name = "pmt_discovery_test", 108 + .test_cases = intel_pmt_discovery_test_cases, 109 + }; 110 + 111 + kunit_test_suite(intel_pmt_discovery_test_suite); 112 + 113 + MODULE_IMPORT_NS("INTEL_PMT_DISCOVERY"); 114 + MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 115 + MODULE_DESCRIPTION("Intel PMT Discovery KUNIT test driver"); 116 + MODULE_LICENSE("GPL");
+635
drivers/platform/x86/intel/pmt/discovery.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Intel Platform Monitory Technology Discovery driver 4 + * 5 + * Copyright (c) 2025, Intel Corporation. 6 + * All Rights Reserved. 7 + */ 8 + 9 + #include <linux/auxiliary_bus.h> 10 + #include <linux/bitfield.h> 11 + #include <linux/bits.h> 12 + #include <linux/bug.h> 13 + #include <linux/cleanup.h> 14 + #include <linux/container_of.h> 15 + #include <linux/device.h> 16 + #include <linux/err.h> 17 + #include <linux/io.h> 18 + #include <linux/ioport.h> 19 + #include <linux/kdev_t.h> 20 + #include <linux/kobject.h> 21 + #include <linux/list.h> 22 + #include <linux/module.h> 23 + #include <linux/mutex.h> 24 + #include <linux/overflow.h> 25 + #include <linux/pci.h> 26 + #include <linux/slab.h> 27 + #include <linux/string_choices.h> 28 + #include <linux/sysfs.h> 29 + #include <linux/types.h> 30 + #include <linux/uaccess.h> 31 + 32 + #include <linux/intel_pmt_features.h> 33 + #include <linux/intel_vsec.h> 34 + 35 + #include "class.h" 36 + 37 + #define MAX_FEATURE_VERSION 0 38 + #define DT_TBIR GENMASK(2, 0) 39 + #define FEAT_ATTR_SIZE(x) ((x) * sizeof(u32)) 40 + #define PMT_GUID_SIZE(x) ((x) * sizeof(u32)) 41 + #define PMT_ACCESS_TYPE_RSVD 0xF 42 + #define SKIP_FEATURE 1 43 + 44 + struct feature_discovery_table { 45 + u32 access_type:4; 46 + u32 version:8; 47 + u32 size:16; 48 + u32 reserved:4; 49 + u32 id; 50 + u32 offset; 51 + u32 reserved2; 52 + }; 53 + 54 + /* Common feature table header */ 55 + struct feature_header { 56 + u32 attr_size:8; 57 + u32 num_guids:8; 58 + u32 reserved:16; 59 + }; 60 + 61 + /* Feature attribute fields */ 62 + struct caps { 63 + u32 caps; 64 + }; 65 + 66 + struct command { 67 + u32 max_stream_size:16; 68 + u32 max_command_size:16; 69 + }; 70 + 71 + struct watcher { 72 + u32 reserved:21; 73 + u32 period:11; 74 + struct command command; 75 + }; 76 + 77 + struct rmid { 78 + u32 num_rmids:16; /* Number of Resource Monitoring IDs */ 79 + u32 reserved:16; 80 + struct watcher watcher; 81 + }; 82 + 83 + struct feature_table { 84 + struct feature_header header; 85 + struct caps caps; 86 + union { 87 + struct command command; 88 + struct watcher watcher; 89 + struct rmid rmid; 90 + }; 91 + u32 *guids; 92 + }; 93 + 94 + /* For backreference in struct feature */ 95 + struct pmt_features_priv; 96 + 97 + struct feature { 98 + struct feature_table table; 99 + struct kobject kobj; 100 + struct pmt_features_priv *priv; 101 + struct list_head list; 102 + const struct attribute_group *attr_group; 103 + enum pmt_feature_id id; 104 + }; 105 + 106 + struct pmt_features_priv { 107 + struct device *parent; 108 + struct device *dev; 109 + int count; 110 + u32 mask; 111 + struct feature feature[]; 112 + }; 113 + 114 + static LIST_HEAD(pmt_feature_list); 115 + static DEFINE_MUTEX(feature_list_lock); 116 + 117 + #define to_pmt_feature(x) container_of(x, struct feature, kobj) 118 + static void pmt_feature_release(struct kobject *kobj) 119 + { 120 + } 121 + 122 + static ssize_t caps_show(struct kobject *kobj, struct kobj_attribute *attr, 123 + char *buf) 124 + { 125 + struct feature *feature = to_pmt_feature(kobj); 126 + struct pmt_cap **pmt_caps; 127 + u32 caps = feature->table.caps.caps; 128 + ssize_t ret = 0; 129 + 130 + switch (feature->id) { 131 + case FEATURE_PER_CORE_PERF_TELEM: 132 + pmt_caps = pmt_caps_pcpt; 133 + break; 134 + case FEATURE_PER_CORE_ENV_TELEM: 135 + pmt_caps = pmt_caps_pcet; 136 + break; 137 + case FEATURE_PER_RMID_PERF_TELEM: 138 + pmt_caps = pmt_caps_rmid_perf; 139 + break; 140 + case FEATURE_ACCEL_TELEM: 141 + pmt_caps = pmt_caps_accel; 142 + break; 143 + case FEATURE_UNCORE_TELEM: 144 + pmt_caps = pmt_caps_uncore; 145 + break; 146 + case FEATURE_CRASH_LOG: 147 + pmt_caps = pmt_caps_crashlog; 148 + break; 149 + case FEATURE_PETE_LOG: 150 + pmt_caps = pmt_caps_pete; 151 + break; 152 + case FEATURE_TPMI_CTRL: 153 + pmt_caps = pmt_caps_tpmi; 154 + break; 155 + case FEATURE_TRACING: 156 + pmt_caps = pmt_caps_tracing; 157 + break; 158 + case FEATURE_PER_RMID_ENERGY_TELEM: 159 + pmt_caps = pmt_caps_rmid_energy; 160 + break; 161 + default: 162 + return -EINVAL; 163 + } 164 + 165 + while (*pmt_caps) { 166 + struct pmt_cap *pmt_cap = *pmt_caps; 167 + 168 + while (pmt_cap->name) { 169 + ret += sysfs_emit_at(buf, ret, "%-40s Available: %s\n", pmt_cap->name, 170 + str_yes_no(pmt_cap->mask & caps)); 171 + pmt_cap++; 172 + } 173 + pmt_caps++; 174 + } 175 + 176 + return ret; 177 + } 178 + static struct kobj_attribute caps_attribute = __ATTR_RO(caps); 179 + 180 + static struct watcher *get_watcher(struct feature *feature) 181 + { 182 + switch (feature_layout[feature->id]) { 183 + case LAYOUT_RMID: 184 + return &feature->table.rmid.watcher; 185 + case LAYOUT_WATCHER: 186 + return &feature->table.watcher; 187 + default: 188 + return ERR_PTR(-EINVAL); 189 + } 190 + } 191 + 192 + static struct command *get_command(struct feature *feature) 193 + { 194 + switch (feature_layout[feature->id]) { 195 + case LAYOUT_RMID: 196 + return &feature->table.rmid.watcher.command; 197 + case LAYOUT_WATCHER: 198 + return &feature->table.watcher.command; 199 + case LAYOUT_COMMAND: 200 + return &feature->table.command; 201 + default: 202 + return ERR_PTR(-EINVAL); 203 + } 204 + } 205 + 206 + static ssize_t num_rmids_show(struct kobject *kobj, 207 + struct kobj_attribute *attr, char *buf) 208 + { 209 + struct feature *feature = to_pmt_feature(kobj); 210 + 211 + return sysfs_emit(buf, "%u\n", feature->table.rmid.num_rmids); 212 + } 213 + static struct kobj_attribute num_rmids_attribute = __ATTR_RO(num_rmids); 214 + 215 + static ssize_t min_watcher_period_ms_show(struct kobject *kobj, 216 + struct kobj_attribute *attr, char *buf) 217 + { 218 + struct feature *feature = to_pmt_feature(kobj); 219 + struct watcher *watcher = get_watcher(feature); 220 + 221 + if (IS_ERR(watcher)) 222 + return PTR_ERR(watcher); 223 + 224 + return sysfs_emit(buf, "%u\n", watcher->period); 225 + } 226 + static struct kobj_attribute min_watcher_period_ms_attribute = 227 + __ATTR_RO(min_watcher_period_ms); 228 + 229 + static ssize_t max_stream_size_show(struct kobject *kobj, 230 + struct kobj_attribute *attr, char *buf) 231 + { 232 + struct feature *feature = to_pmt_feature(kobj); 233 + struct command *command = get_command(feature); 234 + 235 + if (IS_ERR(command)) 236 + return PTR_ERR(command); 237 + 238 + return sysfs_emit(buf, "%u\n", command->max_stream_size); 239 + } 240 + static struct kobj_attribute max_stream_size_attribute = 241 + __ATTR_RO(max_stream_size); 242 + 243 + static ssize_t max_command_size_show(struct kobject *kobj, 244 + struct kobj_attribute *attr, char *buf) 245 + { 246 + struct feature *feature = to_pmt_feature(kobj); 247 + struct command *command = get_command(feature); 248 + 249 + if (IS_ERR(command)) 250 + return PTR_ERR(command); 251 + 252 + return sysfs_emit(buf, "%u\n", command->max_command_size); 253 + } 254 + static struct kobj_attribute max_command_size_attribute = 255 + __ATTR_RO(max_command_size); 256 + 257 + static ssize_t guids_show(struct kobject *kobj, struct kobj_attribute *attr, 258 + char *buf) 259 + { 260 + struct feature *feature = to_pmt_feature(kobj); 261 + int i, count = 0; 262 + 263 + for (i = 0; i < feature->table.header.num_guids; i++) 264 + count += sysfs_emit_at(buf, count, "0x%x\n", 265 + feature->table.guids[i]); 266 + 267 + return count; 268 + } 269 + static struct kobj_attribute guids_attribute = __ATTR_RO(guids); 270 + 271 + static struct attribute *pmt_feature_rmid_attrs[] = { 272 + &caps_attribute.attr, 273 + &num_rmids_attribute.attr, 274 + &min_watcher_period_ms_attribute.attr, 275 + &max_stream_size_attribute.attr, 276 + &max_command_size_attribute.attr, 277 + &guids_attribute.attr, 278 + NULL 279 + }; 280 + ATTRIBUTE_GROUPS(pmt_feature_rmid); 281 + 282 + static const struct kobj_type pmt_feature_rmid_ktype = { 283 + .sysfs_ops = &kobj_sysfs_ops, 284 + .release = pmt_feature_release, 285 + .default_groups = pmt_feature_rmid_groups, 286 + }; 287 + 288 + static struct attribute *pmt_feature_watcher_attrs[] = { 289 + &caps_attribute.attr, 290 + &min_watcher_period_ms_attribute.attr, 291 + &max_stream_size_attribute.attr, 292 + &max_command_size_attribute.attr, 293 + &guids_attribute.attr, 294 + NULL 295 + }; 296 + ATTRIBUTE_GROUPS(pmt_feature_watcher); 297 + 298 + static const struct kobj_type pmt_feature_watcher_ktype = { 299 + .sysfs_ops = &kobj_sysfs_ops, 300 + .release = pmt_feature_release, 301 + .default_groups = pmt_feature_watcher_groups, 302 + }; 303 + 304 + static struct attribute *pmt_feature_command_attrs[] = { 305 + &caps_attribute.attr, 306 + &max_stream_size_attribute.attr, 307 + &max_command_size_attribute.attr, 308 + &guids_attribute.attr, 309 + NULL 310 + }; 311 + ATTRIBUTE_GROUPS(pmt_feature_command); 312 + 313 + static const struct kobj_type pmt_feature_command_ktype = { 314 + .sysfs_ops = &kobj_sysfs_ops, 315 + .release = pmt_feature_release, 316 + .default_groups = pmt_feature_command_groups, 317 + }; 318 + 319 + static struct attribute *pmt_feature_guids_attrs[] = { 320 + &caps_attribute.attr, 321 + &guids_attribute.attr, 322 + NULL 323 + }; 324 + ATTRIBUTE_GROUPS(pmt_feature_guids); 325 + 326 + static const struct kobj_type pmt_feature_guids_ktype = { 327 + .sysfs_ops = &kobj_sysfs_ops, 328 + .release = pmt_feature_release, 329 + .default_groups = pmt_feature_guids_groups, 330 + }; 331 + 332 + static int 333 + pmt_feature_get_disc_table(struct pmt_features_priv *priv, 334 + struct resource *disc_res, 335 + struct feature_discovery_table *disc_tbl) 336 + { 337 + void __iomem *disc_base; 338 + 339 + disc_base = devm_ioremap_resource(priv->dev, disc_res); 340 + if (IS_ERR(disc_base)) 341 + return PTR_ERR(disc_base); 342 + 343 + memcpy_fromio(disc_tbl, disc_base, sizeof(*disc_tbl)); 344 + 345 + devm_iounmap(priv->dev, disc_base); 346 + 347 + if (priv->mask & BIT(disc_tbl->id)) 348 + return dev_err_probe(priv->dev, -EINVAL, "Duplicate feature: %s\n", 349 + pmt_feature_names[disc_tbl->id]); 350 + 351 + /* 352 + * Some devices may expose non-functioning entries that are 353 + * reserved for future use. They have zero size. Do not fail 354 + * probe for these. Just ignore them. 355 + */ 356 + if (disc_tbl->size == 0 || disc_tbl->access_type == PMT_ACCESS_TYPE_RSVD) 357 + return SKIP_FEATURE; 358 + 359 + if (disc_tbl->version > MAX_FEATURE_VERSION) 360 + return SKIP_FEATURE; 361 + 362 + if (!pmt_feature_id_is_valid(disc_tbl->id)) 363 + return SKIP_FEATURE; 364 + 365 + priv->mask |= BIT(disc_tbl->id); 366 + 367 + return 0; 368 + } 369 + 370 + static int 371 + pmt_feature_get_feature_table(struct pmt_features_priv *priv, 372 + struct feature *feature, 373 + struct feature_discovery_table *disc_tbl, 374 + struct resource *disc_res) 375 + { 376 + struct feature_table *feat_tbl = &feature->table; 377 + struct feature_header *header; 378 + struct resource res = {}; 379 + resource_size_t res_size; 380 + void __iomem *feat_base, *feat_offset; 381 + void *tbl_offset; 382 + size_t size; 383 + u32 *guids; 384 + u8 tbir; 385 + 386 + tbir = FIELD_GET(DT_TBIR, disc_tbl->offset); 387 + 388 + switch (disc_tbl->access_type) { 389 + case ACCESS_LOCAL: 390 + if (tbir) 391 + return dev_err_probe(priv->dev, -EINVAL, 392 + "Unsupported BAR index %u for access type %u\n", 393 + tbir, disc_tbl->access_type); 394 + 395 + 396 + /* 397 + * For access_type LOCAL, the base address is as follows: 398 + * base address = end of discovery region + base offset + 1 399 + */ 400 + res = DEFINE_RES_MEM(disc_res->end + disc_tbl->offset + 1, 401 + disc_tbl->size * sizeof(u32)); 402 + break; 403 + 404 + default: 405 + return dev_err_probe(priv->dev, -EINVAL, "Unrecognized access_type %u\n", 406 + disc_tbl->access_type); 407 + } 408 + 409 + feature->id = disc_tbl->id; 410 + 411 + /* Get the feature table */ 412 + feat_base = devm_ioremap_resource(priv->dev, &res); 413 + if (IS_ERR(feat_base)) 414 + return PTR_ERR(feat_base); 415 + 416 + feat_offset = feat_base; 417 + tbl_offset = feat_tbl; 418 + 419 + /* Get the header */ 420 + header = &feat_tbl->header; 421 + memcpy_fromio(header, feat_offset, sizeof(*header)); 422 + 423 + /* Validate fields fit within mapped resource */ 424 + size = sizeof(*header) + FEAT_ATTR_SIZE(header->attr_size) + 425 + PMT_GUID_SIZE(header->num_guids); 426 + res_size = resource_size(&res); 427 + if (WARN(size > res_size, "Bad table size %zu > %pa", size, &res_size)) 428 + return -EINVAL; 429 + 430 + /* Get the feature attributes, including capability fields */ 431 + tbl_offset += sizeof(*header); 432 + feat_offset += sizeof(*header); 433 + 434 + memcpy_fromio(tbl_offset, feat_offset, FEAT_ATTR_SIZE(header->attr_size)); 435 + 436 + /* Finally, get the guids */ 437 + guids = devm_kmalloc(priv->dev, PMT_GUID_SIZE(header->num_guids), GFP_KERNEL); 438 + if (!guids) 439 + return -ENOMEM; 440 + 441 + feat_offset += FEAT_ATTR_SIZE(header->attr_size); 442 + 443 + memcpy_fromio(guids, feat_offset, PMT_GUID_SIZE(header->num_guids)); 444 + 445 + feat_tbl->guids = guids; 446 + 447 + devm_iounmap(priv->dev, feat_base); 448 + 449 + return 0; 450 + } 451 + 452 + static void pmt_features_add_feat(struct feature *feature) 453 + { 454 + guard(mutex)(&feature_list_lock); 455 + list_add(&feature->list, &pmt_feature_list); 456 + } 457 + 458 + static void pmt_features_remove_feat(struct feature *feature) 459 + { 460 + guard(mutex)(&feature_list_lock); 461 + list_del(&feature->list); 462 + } 463 + 464 + /* Get the discovery table and use it to get the feature table */ 465 + static int pmt_features_discovery(struct pmt_features_priv *priv, 466 + struct feature *feature, 467 + struct intel_vsec_device *ivdev, 468 + int idx) 469 + { 470 + struct feature_discovery_table disc_tbl = {}; /* Avoid false warning */ 471 + struct resource *disc_res = &ivdev->resource[idx]; 472 + const struct kobj_type *ktype; 473 + int ret; 474 + 475 + ret = pmt_feature_get_disc_table(priv, disc_res, &disc_tbl); 476 + if (ret) 477 + return ret; 478 + 479 + ret = pmt_feature_get_feature_table(priv, feature, &disc_tbl, disc_res); 480 + if (ret) 481 + return ret; 482 + 483 + switch (feature_layout[feature->id]) { 484 + case LAYOUT_RMID: 485 + ktype = &pmt_feature_rmid_ktype; 486 + feature->attr_group = &pmt_feature_rmid_group; 487 + break; 488 + case LAYOUT_WATCHER: 489 + ktype = &pmt_feature_watcher_ktype; 490 + feature->attr_group = &pmt_feature_watcher_group; 491 + break; 492 + case LAYOUT_COMMAND: 493 + ktype = &pmt_feature_command_ktype; 494 + feature->attr_group = &pmt_feature_command_group; 495 + break; 496 + case LAYOUT_CAPS_ONLY: 497 + ktype = &pmt_feature_guids_ktype; 498 + feature->attr_group = &pmt_feature_guids_group; 499 + break; 500 + default: 501 + return -EINVAL; 502 + } 503 + 504 + ret = kobject_init_and_add(&feature->kobj, ktype, &priv->dev->kobj, 505 + "%s", pmt_feature_names[feature->id]); 506 + if (ret) 507 + return ret; 508 + 509 + kobject_uevent(&feature->kobj, KOBJ_ADD); 510 + pmt_features_add_feat(feature); 511 + 512 + return 0; 513 + } 514 + 515 + static void pmt_features_remove(struct auxiliary_device *auxdev) 516 + { 517 + struct pmt_features_priv *priv = auxiliary_get_drvdata(auxdev); 518 + int i; 519 + 520 + for (i = 0; i < priv->count; i++) { 521 + struct feature *feature = &priv->feature[i]; 522 + 523 + pmt_features_remove_feat(feature); 524 + sysfs_remove_group(&feature->kobj, feature->attr_group); 525 + kobject_put(&feature->kobj); 526 + } 527 + 528 + device_unregister(priv->dev); 529 + } 530 + 531 + static int pmt_features_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) 532 + { 533 + struct intel_vsec_device *ivdev = auxdev_to_ivdev(auxdev); 534 + struct pmt_features_priv *priv; 535 + size_t size; 536 + int ret, i; 537 + 538 + size = struct_size(priv, feature, ivdev->num_resources); 539 + priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL); 540 + if (!priv) 541 + return -ENOMEM; 542 + 543 + priv->parent = &ivdev->pcidev->dev; 544 + auxiliary_set_drvdata(auxdev, priv); 545 + 546 + priv->dev = device_create(&intel_pmt_class, &auxdev->dev, MKDEV(0, 0), priv, 547 + "%s-%s", "features", dev_name(priv->parent)); 548 + if (IS_ERR(priv->dev)) 549 + return dev_err_probe(priv->dev, PTR_ERR(priv->dev), 550 + "Could not create %s-%s device node\n", 551 + "features", dev_name(priv->dev)); 552 + 553 + /* Initialize each feature */ 554 + for (i = 0; i < ivdev->num_resources; i++) { 555 + struct feature *feature = &priv->feature[priv->count]; 556 + 557 + ret = pmt_features_discovery(priv, feature, ivdev, i); 558 + if (ret == SKIP_FEATURE) 559 + continue; 560 + if (ret != 0) 561 + goto abort_probe; 562 + 563 + feature->priv = priv; 564 + priv->count++; 565 + } 566 + 567 + return 0; 568 + 569 + abort_probe: 570 + /* 571 + * Only fully initialized features are tracked in priv->count, which is 572 + * incremented only after a feature is completely set up (i.e., after 573 + * discovery and sysfs registration). If feature initialization fails, 574 + * the failing feature's state is local and does not require rollback. 575 + * 576 + * Therefore, on error, we can safely call the driver's remove() routine 577 + * pmt_features_remove() to clean up only those features that were 578 + * fully initialized and counted. All other resources are device-managed 579 + * and will be cleaned up automatically during device_unregister(). 580 + */ 581 + pmt_features_remove(auxdev); 582 + 583 + return ret; 584 + } 585 + 586 + static void pmt_get_features(struct intel_pmt_entry *entry, struct feature *f) 587 + { 588 + int num_guids = f->table.header.num_guids; 589 + int i; 590 + 591 + for (i = 0; i < num_guids; i++) { 592 + if (f->table.guids[i] != entry->guid) 593 + continue; 594 + 595 + entry->feature_flags |= BIT(f->id); 596 + 597 + if (feature_layout[f->id] == LAYOUT_RMID) 598 + entry->num_rmids = f->table.rmid.num_rmids; 599 + else 600 + entry->num_rmids = 0; /* entry is kzalloc but set anyway */ 601 + } 602 + } 603 + 604 + void intel_pmt_get_features(struct intel_pmt_entry *entry) 605 + { 606 + struct feature *feature; 607 + 608 + mutex_lock(&feature_list_lock); 609 + list_for_each_entry(feature, &pmt_feature_list, list) { 610 + if (feature->priv->parent != &entry->ep->pcidev->dev) 611 + continue; 612 + 613 + pmt_get_features(entry, feature); 614 + } 615 + mutex_unlock(&feature_list_lock); 616 + } 617 + EXPORT_SYMBOL_NS_GPL(intel_pmt_get_features, "INTEL_PMT"); 618 + 619 + static const struct auxiliary_device_id pmt_features_id_table[] = { 620 + { .name = "intel_vsec.discovery" }, 621 + {} 622 + }; 623 + MODULE_DEVICE_TABLE(auxiliary, pmt_features_id_table); 624 + 625 + static struct auxiliary_driver pmt_features_aux_driver = { 626 + .id_table = pmt_features_id_table, 627 + .remove = pmt_features_remove, 628 + .probe = pmt_features_probe, 629 + }; 630 + module_auxiliary_driver(pmt_features_aux_driver); 631 + 632 + MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 633 + MODULE_DESCRIPTION("Intel PMT Discovery driver"); 634 + MODULE_LICENSE("GPL"); 635 + MODULE_IMPORT_NS("INTEL_PMT");
+205
drivers/platform/x86/intel/pmt/features.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (c) 2025, Intel Corporation. 4 + * All Rights Reserved. 5 + * 6 + * Author: "David E. Box" <david.e.box@linux.intel.com> 7 + */ 8 + 9 + #include <linux/export.h> 10 + #include <linux/types.h> 11 + 12 + #include <linux/intel_pmt_features.h> 13 + 14 + const char * const pmt_feature_names[] = { 15 + [FEATURE_PER_CORE_PERF_TELEM] = "per_core_performance_telemetry", 16 + [FEATURE_PER_CORE_ENV_TELEM] = "per_core_environment_telemetry", 17 + [FEATURE_PER_RMID_PERF_TELEM] = "per_rmid_perf_telemetry", 18 + [FEATURE_ACCEL_TELEM] = "accelerator_telemetry", 19 + [FEATURE_UNCORE_TELEM] = "uncore_telemetry", 20 + [FEATURE_CRASH_LOG] = "crash_log", 21 + [FEATURE_PETE_LOG] = "pete_log", 22 + [FEATURE_TPMI_CTRL] = "tpmi_control", 23 + [FEATURE_TRACING] = "tracing", 24 + [FEATURE_PER_RMID_ENERGY_TELEM] = "per_rmid_energy_telemetry", 25 + }; 26 + EXPORT_SYMBOL_NS_GPL(pmt_feature_names, "INTEL_PMT_DISCOVERY"); 27 + 28 + enum feature_layout feature_layout[] = { 29 + [FEATURE_PER_CORE_PERF_TELEM] = LAYOUT_WATCHER, 30 + [FEATURE_PER_CORE_ENV_TELEM] = LAYOUT_WATCHER, 31 + [FEATURE_PER_RMID_PERF_TELEM] = LAYOUT_RMID, 32 + [FEATURE_ACCEL_TELEM] = LAYOUT_WATCHER, 33 + [FEATURE_UNCORE_TELEM] = LAYOUT_WATCHER, 34 + [FEATURE_CRASH_LOG] = LAYOUT_COMMAND, 35 + [FEATURE_PETE_LOG] = LAYOUT_COMMAND, 36 + [FEATURE_TPMI_CTRL] = LAYOUT_CAPS_ONLY, 37 + [FEATURE_TRACING] = LAYOUT_CAPS_ONLY, 38 + [FEATURE_PER_RMID_ENERGY_TELEM] = LAYOUT_RMID, 39 + }; 40 + 41 + struct pmt_cap pmt_cap_common[] = { 42 + {PMT_CAP_TELEM, "telemetry"}, 43 + {PMT_CAP_WATCHER, "watcher"}, 44 + {PMT_CAP_CRASHLOG, "crashlog"}, 45 + {PMT_CAP_STREAMING, "streaming"}, 46 + {PMT_CAP_THRESHOLD, "threshold"}, 47 + {PMT_CAP_WINDOW, "window"}, 48 + {PMT_CAP_CONFIG, "config"}, 49 + {PMT_CAP_TRACING, "tracing"}, 50 + {PMT_CAP_INBAND, "inband"}, 51 + {PMT_CAP_OOB, "oob"}, 52 + {PMT_CAP_SECURED_CHAN, "secure_chan"}, 53 + {PMT_CAP_PMT_SP, "pmt_sp"}, 54 + {PMT_CAP_PMT_SP_POLICY, "pmt_sp_policy"}, 55 + {} 56 + }; 57 + 58 + struct pmt_cap pmt_cap_pcpt[] = { 59 + {PMT_CAP_PCPT_CORE_PERF, "core_performance"}, 60 + {PMT_CAP_PCPT_CORE_C0_RES, "core_c0_residency"}, 61 + {PMT_CAP_PCPT_CORE_ACTIVITY, "core_activity"}, 62 + {PMT_CAP_PCPT_CACHE_PERF, "cache_performance"}, 63 + {PMT_CAP_PCPT_QUALITY_TELEM, "quality_telemetry"}, 64 + {} 65 + }; 66 + 67 + struct pmt_cap *pmt_caps_pcpt[] = { 68 + pmt_cap_common, 69 + pmt_cap_pcpt, 70 + NULL 71 + }; 72 + 73 + struct pmt_cap pmt_cap_pcet[] = { 74 + {PMT_CAP_PCET_WORKPOINT_HIST, "workpoint_histogram"}, 75 + {PMT_CAP_PCET_CORE_CURR_TEMP, "core_current_temp"}, 76 + {PMT_CAP_PCET_CORE_INST_RES, "core_inst_residency"}, 77 + {PMT_CAP_PCET_QUALITY_TELEM, "quality_telemetry"}, 78 + {PMT_CAP_PCET_CORE_CDYN_LVL, "core_cdyn_level"}, 79 + {PMT_CAP_PCET_CORE_STRESS_LVL, "core_stress_level"}, 80 + {PMT_CAP_PCET_CORE_DAS, "core_digital_aging_sensor"}, 81 + {PMT_CAP_PCET_FIVR_HEALTH, "fivr_health"}, 82 + {PMT_CAP_PCET_ENERGY, "energy"}, 83 + {PMT_CAP_PCET_PEM_STATUS, "pem_status"}, 84 + {PMT_CAP_PCET_CORE_C_STATE, "core_c_state"}, 85 + {} 86 + }; 87 + 88 + struct pmt_cap *pmt_caps_pcet[] = { 89 + pmt_cap_common, 90 + pmt_cap_pcet, 91 + NULL 92 + }; 93 + 94 + struct pmt_cap pmt_cap_rmid_perf[] = { 95 + {PMT_CAP_RMID_CORES_PERF, "core_performance"}, 96 + {PMT_CAP_RMID_CACHE_PERF, "cache_performance"}, 97 + {PMT_CAP_RMID_PERF_QUAL, "performance_quality"}, 98 + {} 99 + }; 100 + 101 + struct pmt_cap *pmt_caps_rmid_perf[] = { 102 + pmt_cap_common, 103 + pmt_cap_rmid_perf, 104 + NULL 105 + }; 106 + 107 + struct pmt_cap pmt_cap_accel[] = { 108 + {PMT_CAP_ACCEL_CPM_TELEM, "content_processing_module"}, 109 + {PMT_CAP_ACCEL_TIP_TELEM, "content_turbo_ip"}, 110 + {} 111 + }; 112 + 113 + struct pmt_cap *pmt_caps_accel[] = { 114 + pmt_cap_common, 115 + pmt_cap_accel, 116 + NULL 117 + }; 118 + 119 + struct pmt_cap pmt_cap_uncore[] = { 120 + {PMT_CAP_UNCORE_IO_CA_TELEM, "io_ca"}, 121 + {PMT_CAP_UNCORE_RMID_TELEM, "rmid"}, 122 + {PMT_CAP_UNCORE_D2D_ULA_TELEM, "d2d_ula"}, 123 + {PMT_CAP_UNCORE_PKGC_TELEM, "package_c"}, 124 + {} 125 + }; 126 + 127 + struct pmt_cap *pmt_caps_uncore[] = { 128 + pmt_cap_common, 129 + pmt_cap_uncore, 130 + NULL 131 + }; 132 + 133 + struct pmt_cap pmt_cap_crashlog[] = { 134 + {PMT_CAP_CRASHLOG_MAN_TRIG, "manual_trigger"}, 135 + {PMT_CAP_CRASHLOG_CORE, "core"}, 136 + {PMT_CAP_CRASHLOG_UNCORE, "uncore"}, 137 + {PMT_CAP_CRASHLOG_TOR, "tor"}, 138 + {PMT_CAP_CRASHLOG_S3M, "s3m"}, 139 + {PMT_CAP_CRASHLOG_PERSISTENCY, "persistency"}, 140 + {PMT_CAP_CRASHLOG_CLIP_GPIO, "crashlog_in_progress"}, 141 + {PMT_CAP_CRASHLOG_PRE_RESET, "pre_reset_extraction"}, 142 + {PMT_CAP_CRASHLOG_POST_RESET, "post_reset_extraction"}, 143 + {} 144 + }; 145 + 146 + struct pmt_cap *pmt_caps_crashlog[] = { 147 + pmt_cap_common, 148 + pmt_cap_crashlog, 149 + NULL 150 + }; 151 + 152 + struct pmt_cap pmt_cap_pete[] = { 153 + {PMT_CAP_PETE_MAN_TRIG, "manual_trigger"}, 154 + {PMT_CAP_PETE_ENCRYPTION, "encryption"}, 155 + {PMT_CAP_PETE_PERSISTENCY, "persistency"}, 156 + {PMT_CAP_PETE_REQ_TOKENS, "required_tokens"}, 157 + {PMT_CAP_PETE_PROD_ENABLED, "production_enabled"}, 158 + {PMT_CAP_PETE_DEBUG_ENABLED, "debug_enabled"}, 159 + {} 160 + }; 161 + 162 + struct pmt_cap *pmt_caps_pete[] = { 163 + pmt_cap_common, 164 + pmt_cap_pete, 165 + NULL 166 + }; 167 + 168 + struct pmt_cap pmt_cap_tpmi[] = { 169 + {PMT_CAP_TPMI_MAILBOX, "mailbox"}, 170 + {PMT_CAP_TPMI_LOCK, "bios_lock"}, 171 + {} 172 + }; 173 + 174 + struct pmt_cap *pmt_caps_tpmi[] = { 175 + pmt_cap_common, 176 + pmt_cap_tpmi, 177 + NULL 178 + }; 179 + 180 + struct pmt_cap pmt_cap_tracing[] = { 181 + {PMT_CAP_TRACE_SRAR, "srar_errors"}, 182 + {PMT_CAP_TRACE_CORRECTABLE, "correctable_errors"}, 183 + {PMT_CAP_TRACE_MCTP, "mctp"}, 184 + {PMT_CAP_TRACE_MRT, "memory_resiliency"}, 185 + {} 186 + }; 187 + 188 + struct pmt_cap *pmt_caps_tracing[] = { 189 + pmt_cap_common, 190 + pmt_cap_tracing, 191 + NULL 192 + }; 193 + 194 + struct pmt_cap pmt_cap_rmid_energy[] = { 195 + {PMT_CAP_RMID_ENERGY, "energy"}, 196 + {PMT_CAP_RMID_ACTIVITY, "activity"}, 197 + {PMT_CAP_RMID_ENERGY_QUAL, "energy_quality"}, 198 + {} 199 + }; 200 + 201 + struct pmt_cap *pmt_caps_rmid_energy[] = { 202 + pmt_cap_common, 203 + pmt_cap_rmid_energy, 204 + NULL 205 + };
+93 -1
drivers/platform/x86/intel/pmt/telemetry.c
··· 9 9 */ 10 10 11 11 #include <linux/auxiliary_bus.h> 12 + #include <linux/bitops.h> 13 + #include <linux/cleanup.h> 14 + #include <linux/err.h> 15 + #include <linux/intel_pmt_features.h> 12 16 #include <linux/intel_vsec.h> 13 17 #include <linux/kernel.h> 18 + #include <linux/kref.h> 14 19 #include <linux/module.h> 20 + #include <linux/mutex.h> 21 + #include <linux/overflow.h> 15 22 #include <linux/pci.h> 16 23 #include <linux/slab.h> 24 + #include <linux/types.h> 17 25 #include <linux/uaccess.h> 18 - #include <linux/overflow.h> 26 + #include <linux/xarray.h> 19 27 20 28 #include "class.h" 21 29 ··· 214 206 } 215 207 EXPORT_SYMBOL_NS_GPL(pmt_telem_get_endpoint_info, "INTEL_PMT_TELEMETRY"); 216 208 209 + static int pmt_copy_region(struct telemetry_region *region, 210 + struct intel_pmt_entry *entry) 211 + { 212 + 213 + struct oobmsm_plat_info *plat_info; 214 + 215 + plat_info = intel_vsec_get_mapping(entry->ep->pcidev); 216 + if (IS_ERR(plat_info)) 217 + return PTR_ERR(plat_info); 218 + 219 + region->plat_info = *plat_info; 220 + region->guid = entry->guid; 221 + region->addr = entry->ep->base; 222 + region->size = entry->size; 223 + region->num_rmids = entry->num_rmids; 224 + 225 + return 0; 226 + } 227 + 228 + static void pmt_feature_group_release(struct kref *kref) 229 + { 230 + struct pmt_feature_group *feature_group; 231 + 232 + feature_group = container_of(kref, struct pmt_feature_group, kref); 233 + kfree(feature_group); 234 + } 235 + 236 + struct pmt_feature_group *intel_pmt_get_regions_by_feature(enum pmt_feature_id id) 237 + { 238 + struct pmt_feature_group *feature_group __free(kfree) = NULL; 239 + struct telemetry_region *region; 240 + struct intel_pmt_entry *entry; 241 + unsigned long idx; 242 + int count = 0; 243 + size_t size; 244 + 245 + if (!pmt_feature_id_is_valid(id)) 246 + return ERR_PTR(-EINVAL); 247 + 248 + guard(mutex)(&ep_lock); 249 + xa_for_each(&telem_array, idx, entry) { 250 + if (entry->feature_flags & BIT(id)) 251 + count++; 252 + } 253 + 254 + if (!count) 255 + return ERR_PTR(-ENOENT); 256 + 257 + size = struct_size(feature_group, regions, count); 258 + feature_group = kzalloc(size, GFP_KERNEL); 259 + if (!feature_group) 260 + return ERR_PTR(-ENOMEM); 261 + 262 + feature_group->count = count; 263 + 264 + region = feature_group->regions; 265 + xa_for_each(&telem_array, idx, entry) { 266 + int ret; 267 + 268 + if (!(entry->feature_flags & BIT(id))) 269 + continue; 270 + 271 + ret = pmt_copy_region(region, entry); 272 + if (ret) 273 + return ERR_PTR(ret); 274 + 275 + region++; 276 + } 277 + 278 + kref_init(&feature_group->kref); 279 + 280 + return no_free_ptr(feature_group); 281 + } 282 + EXPORT_SYMBOL(intel_pmt_get_regions_by_feature); 283 + 284 + void intel_pmt_put_feature_group(struct pmt_feature_group *feature_group) 285 + { 286 + kref_put(&feature_group->kref, pmt_feature_group_release); 287 + } 288 + EXPORT_SYMBOL(intel_pmt_put_feature_group); 289 + 217 290 int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count) 218 291 { 219 292 u32 offset, size; ··· 400 311 continue; 401 312 402 313 priv->num_entries++; 314 + 315 + intel_pmt_get_features(entry); 403 316 } 404 317 405 318 return 0; ··· 439 348 MODULE_DESCRIPTION("Intel PMT Telemetry driver"); 440 349 MODULE_LICENSE("GPL v2"); 441 350 MODULE_IMPORT_NS("INTEL_PMT"); 351 + MODULE_IMPORT_NS("INTEL_VSEC");
+5 -4
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c
··· 22 22 #include <linux/auxiliary_bus.h> 23 23 #include <linux/delay.h> 24 24 #include <linux/intel_tpmi.h> 25 + #include <linux/intel_vsec.h> 25 26 #include <linux/fs.h> 26 27 #include <linux/io.h> 27 28 #include <linux/kernel.h> ··· 1547 1546 { 1548 1547 struct tpmi_per_power_domain_info *pd_info; 1549 1548 bool read_blocked = 0, write_blocked = 0; 1550 - struct intel_tpmi_plat_info *plat_info; 1549 + struct oobmsm_plat_info *plat_info; 1551 1550 struct device *dev = &auxdev->dev; 1552 1551 struct tpmi_sst_struct *tpmi_sst; 1553 1552 u8 i, num_resources, io_die_cnt; ··· 1699 1698 void tpmi_sst_dev_remove(struct auxiliary_device *auxdev) 1700 1699 { 1701 1700 struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev); 1702 - struct intel_tpmi_plat_info *plat_info; 1701 + struct oobmsm_plat_info *plat_info; 1703 1702 1704 1703 plat_info = tpmi_get_platform_data(auxdev); 1705 1704 if (!plat_info) ··· 1721 1720 { 1722 1721 struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev); 1723 1722 struct tpmi_per_power_domain_info *power_domain_info; 1724 - struct intel_tpmi_plat_info *plat_info; 1723 + struct oobmsm_plat_info *plat_info; 1725 1724 void __iomem *cp_base; 1726 1725 1727 1726 plat_info = tpmi_get_platform_data(auxdev); ··· 1749 1748 { 1750 1749 struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev); 1751 1750 struct tpmi_per_power_domain_info *power_domain_info; 1752 - struct intel_tpmi_plat_info *plat_info; 1751 + struct oobmsm_plat_info *plat_info; 1753 1752 void __iomem *cp_base; 1754 1753 1755 1754 plat_info = tpmi_get_platform_data(auxdev);
-177
drivers/platform/x86/intel/telemetry/core.c
··· 21 21 22 22 static struct telemetry_core_config telm_core_conf; 23 23 24 - static int telemetry_def_update_events(struct telemetry_evtconfig pss_evtconfig, 25 - struct telemetry_evtconfig ioss_evtconfig) 26 - { 27 - return 0; 28 - } 29 - 30 - static int telemetry_def_set_sampling_period(u8 pss_period, u8 ioss_period) 31 - { 32 - return 0; 33 - } 34 - 35 - static int telemetry_def_get_sampling_period(u8 *pss_min_period, 36 - u8 *pss_max_period, 37 - u8 *ioss_min_period, 38 - u8 *ioss_max_period) 39 - { 40 - return 0; 41 - } 42 - 43 - static int telemetry_def_get_eventconfig( 44 - struct telemetry_evtconfig *pss_evtconfig, 45 - struct telemetry_evtconfig *ioss_evtconfig, 46 - int pss_len, int ioss_len) 47 - { 48 - return 0; 49 - } 50 - 51 24 static int telemetry_def_get_trace_verbosity(enum telemetry_unit telem_unit, 52 25 u32 *verbosity) 53 26 { ··· 48 75 return 0; 49 76 } 50 77 51 - static int telemetry_def_add_events(u8 num_pss_evts, u8 num_ioss_evts, 52 - u32 *pss_evtmap, u32 *ioss_evtmap) 53 - { 54 - return 0; 55 - } 56 - 57 - static int telemetry_def_reset_events(void) 58 - { 59 - return 0; 60 - } 61 - 62 78 static const struct telemetry_core_ops telm_defpltops = { 63 - .set_sampling_period = telemetry_def_set_sampling_period, 64 - .get_sampling_period = telemetry_def_get_sampling_period, 65 79 .get_trace_verbosity = telemetry_def_get_trace_verbosity, 66 80 .set_trace_verbosity = telemetry_def_set_trace_verbosity, 67 81 .raw_read_eventlog = telemetry_def_raw_read_eventlog, 68 - .get_eventconfig = telemetry_def_get_eventconfig, 69 82 .read_eventlog = telemetry_def_read_eventlog, 70 - .update_events = telemetry_def_update_events, 71 - .reset_events = telemetry_def_reset_events, 72 - .add_events = telemetry_def_add_events, 73 83 }; 74 - 75 - /** 76 - * telemetry_update_events() - Update telemetry Configuration 77 - * @pss_evtconfig: PSS related config. No change if num_evts = 0. 78 - * @ioss_evtconfig: IOSS related config. No change if num_evts = 0. 79 - * 80 - * This API updates the IOSS & PSS Telemetry configuration. Old config 81 - * is overwritten. Call telemetry_reset_events when logging is over 82 - * All sample period values should be in the form of: 83 - * bits[6:3] -> value; bits [0:2]-> Exponent; Period = (Value *16^Exponent) 84 - * 85 - * Return: 0 success, < 0 for failure 86 - */ 87 - int telemetry_update_events(struct telemetry_evtconfig pss_evtconfig, 88 - struct telemetry_evtconfig ioss_evtconfig) 89 - { 90 - return telm_core_conf.telem_ops->update_events(pss_evtconfig, 91 - ioss_evtconfig); 92 - } 93 - EXPORT_SYMBOL_GPL(telemetry_update_events); 94 - 95 - 96 - /** 97 - * telemetry_set_sampling_period() - Sets the IOSS & PSS sampling period 98 - * @pss_period: placeholder for PSS Period to be set. 99 - * Set to 0 if not required to be updated 100 - * @ioss_period: placeholder for IOSS Period to be set 101 - * Set to 0 if not required to be updated 102 - * 103 - * All values should be in the form of: 104 - * bits[6:3] -> value; bits [0:2]-> Exponent; Period = (Value *16^Exponent) 105 - * 106 - * Return: 0 success, < 0 for failure 107 - */ 108 - int telemetry_set_sampling_period(u8 pss_period, u8 ioss_period) 109 - { 110 - return telm_core_conf.telem_ops->set_sampling_period(pss_period, 111 - ioss_period); 112 - } 113 - EXPORT_SYMBOL_GPL(telemetry_set_sampling_period); 114 - 115 - /** 116 - * telemetry_get_sampling_period() - Get IOSS & PSS min & max sampling period 117 - * @pss_min_period: placeholder for PSS Min Period supported 118 - * @pss_max_period: placeholder for PSS Max Period supported 119 - * @ioss_min_period: placeholder for IOSS Min Period supported 120 - * @ioss_max_period: placeholder for IOSS Max Period supported 121 - * 122 - * All values should be in the form of: 123 - * bits[6:3] -> value; bits [0:2]-> Exponent; Period = (Value *16^Exponent) 124 - * 125 - * Return: 0 success, < 0 for failure 126 - */ 127 - int telemetry_get_sampling_period(u8 *pss_min_period, u8 *pss_max_period, 128 - u8 *ioss_min_period, u8 *ioss_max_period) 129 - { 130 - return telm_core_conf.telem_ops->get_sampling_period(pss_min_period, 131 - pss_max_period, 132 - ioss_min_period, 133 - ioss_max_period); 134 - } 135 - EXPORT_SYMBOL_GPL(telemetry_get_sampling_period); 136 - 137 - 138 - /** 139 - * telemetry_reset_events() - Restore the IOSS & PSS configuration to default 140 - * 141 - * Return: 0 success, < 0 for failure 142 - */ 143 - int telemetry_reset_events(void) 144 - { 145 - return telm_core_conf.telem_ops->reset_events(); 146 - } 147 - EXPORT_SYMBOL_GPL(telemetry_reset_events); 148 - 149 - /** 150 - * telemetry_get_eventconfig() - Returns the pss and ioss events enabled 151 - * @pss_evtconfig: Pointer to PSS related configuration. 152 - * @ioss_evtconfig: Pointer to IOSS related configuration. 153 - * @pss_len: Number of u32 elements allocated for pss_evtconfig array 154 - * @ioss_len: Number of u32 elements allocated for ioss_evtconfig array 155 - * 156 - * Return: 0 success, < 0 for failure 157 - */ 158 - int telemetry_get_eventconfig(struct telemetry_evtconfig *pss_evtconfig, 159 - struct telemetry_evtconfig *ioss_evtconfig, 160 - int pss_len, int ioss_len) 161 - { 162 - return telm_core_conf.telem_ops->get_eventconfig(pss_evtconfig, 163 - ioss_evtconfig, 164 - pss_len, ioss_len); 165 - } 166 - EXPORT_SYMBOL_GPL(telemetry_get_eventconfig); 167 - 168 - /** 169 - * telemetry_add_events() - Add IOSS & PSS configuration to existing settings. 170 - * @num_pss_evts: Number of PSS Events (<29) in pss_evtmap. Can be 0. 171 - * @num_ioss_evts: Number of IOSS Events (<29) in ioss_evtmap. Can be 0. 172 - * @pss_evtmap: Array of PSS Event-IDs to Enable 173 - * @ioss_evtmap: Array of PSS Event-IDs to Enable 174 - * 175 - * Events are appended to Old Configuration. In case of total events > 28, it 176 - * returns error. Call telemetry_reset_events to reset after eventlog done 177 - * 178 - * Return: 0 success, < 0 for failure 179 - */ 180 - int telemetry_add_events(u8 num_pss_evts, u8 num_ioss_evts, 181 - u32 *pss_evtmap, u32 *ioss_evtmap) 182 - { 183 - return telm_core_conf.telem_ops->add_events(num_pss_evts, 184 - num_ioss_evts, pss_evtmap, 185 - ioss_evtmap); 186 - } 187 - EXPORT_SYMBOL_GPL(telemetry_add_events); 188 84 189 85 /** 190 86 * telemetry_read_events() - Fetches samples as specified by evtlog.telem_evt_id ··· 71 229 len, 0); 72 230 } 73 231 EXPORT_SYMBOL_GPL(telemetry_read_events); 74 - 75 - /** 76 - * telemetry_raw_read_events() - Fetch samples specified by evtlog.telem_evt_id 77 - * @telem_unit: Specify whether IOSS or PSS Read 78 - * @evtlog: Array of telemetry_evtlog structs to fill data 79 - * evtlog.telem_evt_id specifies the ids to read 80 - * @len: Length of array of evtlog 81 - * 82 - * The caller must take care of locking in this case. 83 - * 84 - * Return: number of eventlogs read for success, < 0 for failure 85 - */ 86 - int telemetry_raw_read_events(enum telemetry_unit telem_unit, 87 - struct telemetry_evtlog *evtlog, int len) 88 - { 89 - return telm_core_conf.telem_ops->raw_read_eventlog(telem_unit, evtlog, 90 - len, 0); 91 - } 92 - EXPORT_SYMBOL_GPL(telemetry_raw_read_events); 93 232 94 233 /** 95 234 * telemetry_read_eventlog() - Fetch the Telemetry log from PSS or IOSS
-231
drivers/platform/x86/intel/telemetry/pltdrv.c
··· 639 639 return 0; 640 640 } 641 641 642 - static int telemetry_plt_update_events(struct telemetry_evtconfig pss_evtconfig, 643 - struct telemetry_evtconfig ioss_evtconfig) 644 - { 645 - int ret; 646 - 647 - if ((pss_evtconfig.num_evts > 0) && 648 - (TELEM_SAMPLE_PERIOD_INVALID(pss_evtconfig.period))) { 649 - pr_err("PSS Sampling Period Out of Range\n"); 650 - return -EINVAL; 651 - } 652 - 653 - if ((ioss_evtconfig.num_evts > 0) && 654 - (TELEM_SAMPLE_PERIOD_INVALID(ioss_evtconfig.period))) { 655 - pr_err("IOSS Sampling Period Out of Range\n"); 656 - return -EINVAL; 657 - } 658 - 659 - ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, 660 - TELEM_UPDATE); 661 - if (ret) 662 - pr_err("TELEMETRY Config Failed\n"); 663 - 664 - return ret; 665 - } 666 - 667 - 668 - static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period) 669 - { 670 - u32 telem_ctrl = 0; 671 - int ret = 0; 672 - 673 - mutex_lock(&(telm_conf->telem_lock)); 674 - if (ioss_period) { 675 - struct intel_scu_ipc_dev *scu = telm_conf->scu; 676 - 677 - if (TELEM_SAMPLE_PERIOD_INVALID(ioss_period)) { 678 - pr_err("IOSS Sampling Period Out of Range\n"); 679 - ret = -EINVAL; 680 - goto out; 681 - } 682 - 683 - /* Get telemetry EVENT CTL */ 684 - ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM, 685 - IOSS_TELEM_EVENT_CTL_READ, NULL, 0, 686 - &telem_ctrl, sizeof(telem_ctrl)); 687 - if (ret) { 688 - pr_err("IOSS TELEM_CTRL Read Failed\n"); 689 - goto out; 690 - } 691 - 692 - /* Disable Telemetry */ 693 - TELEM_DISABLE(telem_ctrl); 694 - 695 - ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM, 696 - IOSS_TELEM_EVENT_CTL_WRITE, 697 - &telem_ctrl, sizeof(telem_ctrl), 698 - NULL, 0); 699 - if (ret) { 700 - pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n"); 701 - goto out; 702 - } 703 - 704 - /* Enable Periodic Telemetry Events and enable SRAM trace */ 705 - TELEM_CLEAR_SAMPLE_PERIOD(telem_ctrl); 706 - TELEM_ENABLE_SRAM_EVT_TRACE(telem_ctrl); 707 - TELEM_ENABLE_PERIODIC(telem_ctrl); 708 - telem_ctrl |= ioss_period; 709 - 710 - ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM, 711 - IOSS_TELEM_EVENT_CTL_WRITE, 712 - &telem_ctrl, sizeof(telem_ctrl), 713 - NULL, 0); 714 - if (ret) { 715 - pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n"); 716 - goto out; 717 - } 718 - telm_conf->ioss_config.curr_period = ioss_period; 719 - } 720 - 721 - if (pss_period) { 722 - if (TELEM_SAMPLE_PERIOD_INVALID(pss_period)) { 723 - pr_err("PSS Sampling Period Out of Range\n"); 724 - ret = -EINVAL; 725 - goto out; 726 - } 727 - 728 - /* Get telemetry EVENT CTL */ 729 - ret = intel_punit_ipc_command( 730 - IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 731 - 0, 0, NULL, &telem_ctrl); 732 - if (ret) { 733 - pr_err("PSS TELEM_CTRL Read Failed\n"); 734 - goto out; 735 - } 736 - 737 - /* Disable Telemetry */ 738 - TELEM_DISABLE(telem_ctrl); 739 - ret = intel_punit_ipc_command( 740 - IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 741 - 0, 0, &telem_ctrl, NULL); 742 - if (ret) { 743 - pr_err("PSS TELEM_CTRL Event Disable Write Failed\n"); 744 - goto out; 745 - } 746 - 747 - /* Enable Periodic Telemetry Events and enable SRAM trace */ 748 - TELEM_CLEAR_SAMPLE_PERIOD(telem_ctrl); 749 - TELEM_ENABLE_SRAM_EVT_TRACE(telem_ctrl); 750 - TELEM_ENABLE_PERIODIC(telem_ctrl); 751 - telem_ctrl |= pss_period; 752 - 753 - ret = intel_punit_ipc_command( 754 - IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 755 - 0, 0, &telem_ctrl, NULL); 756 - if (ret) { 757 - pr_err("PSS TELEM_CTRL Event Enable Write Failed\n"); 758 - goto out; 759 - } 760 - telm_conf->pss_config.curr_period = pss_period; 761 - } 762 - 763 - out: 764 - mutex_unlock(&(telm_conf->telem_lock)); 765 - return ret; 766 - } 767 - 768 - 769 - static int telemetry_plt_get_sampling_period(u8 *pss_min_period, 770 - u8 *pss_max_period, 771 - u8 *ioss_min_period, 772 - u8 *ioss_max_period) 773 - { 774 - *pss_min_period = telm_conf->pss_config.min_period; 775 - *pss_max_period = telm_conf->pss_config.max_period; 776 - *ioss_min_period = telm_conf->ioss_config.min_period; 777 - *ioss_max_period = telm_conf->ioss_config.max_period; 778 - 779 - return 0; 780 - } 781 - 782 - 783 - static int telemetry_plt_reset_events(void) 784 - { 785 - struct telemetry_evtconfig pss_evtconfig, ioss_evtconfig; 786 - int ret; 787 - 788 - pss_evtconfig.evtmap = NULL; 789 - pss_evtconfig.num_evts = TELEM_MAX_OS_ALLOCATED_EVENTS; 790 - pss_evtconfig.period = TELEM_SAMPLING_DEFAULT_PERIOD; 791 - 792 - ioss_evtconfig.evtmap = NULL; 793 - ioss_evtconfig.num_evts = TELEM_MAX_OS_ALLOCATED_EVENTS; 794 - ioss_evtconfig.period = TELEM_SAMPLING_DEFAULT_PERIOD; 795 - 796 - ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, 797 - TELEM_RESET); 798 - if (ret) 799 - pr_err("TELEMETRY Reset Failed\n"); 800 - 801 - return ret; 802 - } 803 - 804 - 805 - static int telemetry_plt_get_eventconfig(struct telemetry_evtconfig *pss_config, 806 - struct telemetry_evtconfig *ioss_config, 807 - int pss_len, int ioss_len) 808 - { 809 - u32 *pss_evtmap, *ioss_evtmap; 810 - u32 index; 811 - 812 - pss_evtmap = pss_config->evtmap; 813 - ioss_evtmap = ioss_config->evtmap; 814 - 815 - mutex_lock(&(telm_conf->telem_lock)); 816 - pss_config->num_evts = telm_conf->pss_config.ssram_evts_used; 817 - ioss_config->num_evts = telm_conf->ioss_config.ssram_evts_used; 818 - 819 - pss_config->period = telm_conf->pss_config.curr_period; 820 - ioss_config->period = telm_conf->ioss_config.curr_period; 821 - 822 - if ((pss_len < telm_conf->pss_config.ssram_evts_used) || 823 - (ioss_len < telm_conf->ioss_config.ssram_evts_used)) { 824 - mutex_unlock(&(telm_conf->telem_lock)); 825 - return -EINVAL; 826 - } 827 - 828 - for (index = 0; index < telm_conf->pss_config.ssram_evts_used; 829 - index++) { 830 - pss_evtmap[index] = 831 - telm_conf->pss_config.telem_evts[index].evt_id; 832 - } 833 - 834 - for (index = 0; index < telm_conf->ioss_config.ssram_evts_used; 835 - index++) { 836 - ioss_evtmap[index] = 837 - telm_conf->ioss_config.telem_evts[index].evt_id; 838 - } 839 - 840 - mutex_unlock(&(telm_conf->telem_lock)); 841 - return 0; 842 - } 843 - 844 - 845 - static int telemetry_plt_add_events(u8 num_pss_evts, u8 num_ioss_evts, 846 - u32 *pss_evtmap, u32 *ioss_evtmap) 847 - { 848 - struct telemetry_evtconfig pss_evtconfig, ioss_evtconfig; 849 - int ret; 850 - 851 - pss_evtconfig.evtmap = pss_evtmap; 852 - pss_evtconfig.num_evts = num_pss_evts; 853 - pss_evtconfig.period = telm_conf->pss_config.curr_period; 854 - 855 - ioss_evtconfig.evtmap = ioss_evtmap; 856 - ioss_evtconfig.num_evts = num_ioss_evts; 857 - ioss_evtconfig.period = telm_conf->ioss_config.curr_period; 858 - 859 - ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, 860 - TELEM_ADD); 861 - if (ret) 862 - pr_err("TELEMETRY ADD Failed\n"); 863 - 864 - return ret; 865 - } 866 - 867 642 static int telem_evtlog_read(enum telemetry_unit telem_unit, 868 643 struct telem_ssram_region *ssram_region, u8 len) 869 644 { ··· 868 1093 static const struct telemetry_core_ops telm_pltops = { 869 1094 .get_trace_verbosity = telemetry_plt_get_trace_verbosity, 870 1095 .set_trace_verbosity = telemetry_plt_set_trace_verbosity, 871 - .set_sampling_period = telemetry_plt_set_sampling_period, 872 - .get_sampling_period = telemetry_plt_get_sampling_period, 873 1096 .raw_read_eventlog = telemetry_plt_raw_read_eventlog, 874 - .get_eventconfig = telemetry_plt_get_eventconfig, 875 - .update_events = telemetry_plt_update_events, 876 1097 .read_eventlog = telemetry_plt_read_eventlog, 877 - .reset_events = telemetry_plt_reset_events, 878 - .add_events = telemetry_plt_add_events, 879 1098 }; 880 1099 881 1100 static int telemetry_pltdrv_probe(struct platform_device *pdev)
+4 -3
drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c
··· 22 22 #include <linux/auxiliary_bus.h> 23 23 #include <linux/bitfield.h> 24 24 #include <linux/bits.h> 25 + #include <linux/intel_tpmi.h> 26 + #include <linux/intel_vsec.h> 25 27 #include <linux/io.h> 26 28 #include <linux/module.h> 27 - #include <linux/intel_tpmi.h> 28 29 29 30 #include "../tpmi_power_domains.h" 30 31 #include "uncore-frequency-common.h" ··· 449 448 } 450 449 451 450 static void set_cdie_id(int domain_id, struct tpmi_uncore_cluster_info *cluster_info, 452 - struct intel_tpmi_plat_info *plat_info) 451 + struct oobmsm_plat_info *plat_info) 453 452 { 454 453 455 454 cluster_info->cdie_id = domain_id; ··· 466 465 static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) 467 466 { 468 467 bool read_blocked = 0, write_blocked = 0; 469 - struct intel_tpmi_plat_info *plat_info; 468 + struct oobmsm_plat_info *plat_info; 470 469 struct tpmi_uncore_struct *tpmi_uncore; 471 470 bool uncore_sysfs_added = false; 472 471 int ret, i, pkg = 0;
+352 -20
drivers/platform/x86/intel/vsec.c
··· 15 15 16 16 #include <linux/auxiliary_bus.h> 17 17 #include <linux/bits.h> 18 + #include <linux/bitops.h> 19 + #include <linux/bug.h> 18 20 #include <linux/cleanup.h> 19 21 #include <linux/delay.h> 20 22 #include <linux/idr.h> 23 + #include <linux/log2.h> 21 24 #include <linux/intel_vsec.h> 22 25 #include <linux/kernel.h> 23 26 #include <linux/module.h> ··· 34 31 static DEFINE_IDA(intel_vsec_ida); 35 32 static DEFINE_IDA(intel_vsec_sdsi_ida); 36 33 static DEFINE_XARRAY_ALLOC(auxdev_array); 34 + 35 + enum vsec_device_state { 36 + STATE_NOT_FOUND, 37 + STATE_REGISTERED, 38 + STATE_SKIP, 39 + }; 40 + 41 + struct vsec_priv { 42 + struct intel_vsec_platform_info *info; 43 + struct device *suppliers[VSEC_FEATURE_COUNT]; 44 + struct oobmsm_plat_info plat_info; 45 + enum vsec_device_state state[VSEC_FEATURE_COUNT]; 46 + unsigned long found_caps; 47 + }; 37 48 38 49 static const char *intel_vsec_name(enum intel_vsec_id id) 39 50 { ··· 67 50 case VSEC_ID_TPMI: 68 51 return "tpmi"; 69 52 53 + case VSEC_ID_DISCOVERY: 54 + return "discovery"; 55 + 70 56 default: 71 57 return NULL; 72 58 } ··· 88 68 return !!(caps & VSEC_CAP_SDSI); 89 69 case VSEC_ID_TPMI: 90 70 return !!(caps & VSEC_CAP_TPMI); 71 + case VSEC_ID_DISCOVERY: 72 + return !!(caps & VSEC_CAP_DISCOVERY); 91 73 default: 92 74 return false; 93 75 } ··· 111 89 112 90 kfree(intel_vsec_dev->resource); 113 91 kfree(intel_vsec_dev); 92 + } 93 + 94 + static const struct vsec_feature_dependency * 95 + get_consumer_dependencies(struct vsec_priv *priv, int cap_id) 96 + { 97 + const struct vsec_feature_dependency *deps = priv->info->deps; 98 + int consumer_id = priv->info->num_deps; 99 + 100 + if (!deps) 101 + return NULL; 102 + 103 + while (consumer_id--) 104 + if (deps[consumer_id].feature == BIT(cap_id)) 105 + return &deps[consumer_id]; 106 + 107 + return NULL; 108 + } 109 + 110 + static bool vsec_driver_present(int cap_id) 111 + { 112 + unsigned long bit = BIT(cap_id); 113 + 114 + switch (bit) { 115 + case VSEC_CAP_TELEMETRY: 116 + return IS_ENABLED(CONFIG_INTEL_PMT_TELEMETRY); 117 + case VSEC_CAP_WATCHER: 118 + return IS_ENABLED(CONFIG_INTEL_PMT_WATCHER); 119 + case VSEC_CAP_CRASHLOG: 120 + return IS_ENABLED(CONFIG_INTEL_PMT_CRASHLOG); 121 + case VSEC_CAP_SDSI: 122 + return IS_ENABLED(CONFIG_INTEL_SDSI); 123 + case VSEC_CAP_TPMI: 124 + return IS_ENABLED(CONFIG_INTEL_TPMI); 125 + case VSEC_CAP_DISCOVERY: 126 + return IS_ENABLED(CONFIG_INTEL_PMT_DISCOVERY); 127 + default: 128 + return false; 129 + } 130 + } 131 + 132 + /* 133 + * Although pci_device_id table is available in the pdev, this prototype is 134 + * necessary because the code using it can be called by an exported API that 135 + * might pass a different pdev. 136 + */ 137 + static const struct pci_device_id intel_vsec_pci_ids[]; 138 + 139 + static int intel_vsec_link_devices(struct pci_dev *pdev, struct device *dev, 140 + int consumer_id) 141 + { 142 + const struct vsec_feature_dependency *deps; 143 + enum vsec_device_state *state; 144 + struct device **suppliers; 145 + struct vsec_priv *priv; 146 + int supplier_id; 147 + 148 + if (!consumer_id) 149 + return 0; 150 + 151 + if (!pci_match_id(intel_vsec_pci_ids, pdev)) 152 + return 0; 153 + 154 + priv = pci_get_drvdata(pdev); 155 + state = priv->state; 156 + suppliers = priv->suppliers; 157 + 158 + priv->suppliers[consumer_id] = dev; 159 + 160 + deps = get_consumer_dependencies(priv, consumer_id); 161 + if (!deps) 162 + return 0; 163 + 164 + for_each_set_bit(supplier_id, &deps->supplier_bitmap, VSEC_FEATURE_COUNT) { 165 + struct device_link *link; 166 + 167 + if (state[supplier_id] != STATE_REGISTERED || 168 + !vsec_driver_present(supplier_id)) 169 + continue; 170 + 171 + if (!suppliers[supplier_id]) { 172 + dev_err(dev, "Bad supplier list\n"); 173 + return -EINVAL; 174 + } 175 + 176 + link = device_link_add(dev, suppliers[supplier_id], 177 + DL_FLAG_AUTOPROBE_CONSUMER); 178 + if (!link) 179 + return -EINVAL; 180 + } 181 + 182 + return 0; 114 183 } 115 184 116 185 int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, ··· 241 128 return ret; 242 129 } 243 130 131 + /* 132 + * Assign a name now to ensure that the device link doesn't contain 133 + * a null string for the consumer name. This is a problem when a supplier 134 + * supplies more than one consumer and can lead to a duplicate name error 135 + * when the link is created in sysfs. 136 + */ 137 + ret = dev_set_name(&auxdev->dev, "%s.%s.%d", KBUILD_MODNAME, auxdev->name, 138 + auxdev->id); 139 + if (ret) 140 + goto cleanup_aux; 141 + 142 + ret = intel_vsec_link_devices(pdev, &auxdev->dev, intel_vsec_dev->cap_id); 143 + if (ret) 144 + goto cleanup_aux; 145 + 244 146 ret = auxiliary_device_add(auxdev); 245 - if (ret < 0) { 246 - auxiliary_device_uninit(auxdev); 247 - return ret; 248 - } 147 + if (ret) 148 + goto cleanup_aux; 249 149 250 150 return devm_add_action_or_reset(parent, intel_vsec_remove_aux, 251 151 auxdev); 152 + 153 + cleanup_aux: 154 + auxiliary_device_uninit(auxdev); 155 + return ret; 252 156 } 253 157 EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, "INTEL_VSEC"); 254 158 255 159 static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, 256 - struct intel_vsec_platform_info *info) 160 + struct intel_vsec_platform_info *info, 161 + unsigned long cap_id) 257 162 { 258 163 struct intel_vsec_device __free(kfree) *intel_vsec_dev = NULL; 259 164 struct resource __free(kfree) *res = NULL; ··· 338 207 intel_vsec_dev->quirks = info->quirks; 339 208 intel_vsec_dev->base_addr = info->base_addr; 340 209 intel_vsec_dev->priv_data = info->priv_data; 210 + intel_vsec_dev->cap_id = cap_id; 341 211 342 212 if (header->id == VSEC_ID_SDSI) 343 213 intel_vsec_dev->ida = &intel_vsec_sdsi_ida; ··· 353 221 intel_vsec_name(header->id)); 354 222 } 355 223 224 + static bool suppliers_ready(struct vsec_priv *priv, 225 + const struct vsec_feature_dependency *consumer_deps, 226 + int cap_id) 227 + { 228 + enum vsec_device_state *state = priv->state; 229 + int supplier_id; 230 + 231 + if (WARN_ON_ONCE(consumer_deps->feature != BIT(cap_id))) 232 + return false; 233 + 234 + /* 235 + * Verify that all required suppliers have been found. Return false 236 + * immediately if any are still missing. 237 + */ 238 + for_each_set_bit(supplier_id, &consumer_deps->supplier_bitmap, VSEC_FEATURE_COUNT) { 239 + if (state[supplier_id] == STATE_SKIP) 240 + continue; 241 + 242 + if (state[supplier_id] == STATE_NOT_FOUND) 243 + return false; 244 + } 245 + 246 + /* 247 + * All suppliers have been found and the consumer is ready to be 248 + * registered. 249 + */ 250 + return true; 251 + } 252 + 253 + static int get_cap_id(u32 header_id, unsigned long *cap_id) 254 + { 255 + switch (header_id) { 256 + case VSEC_ID_TELEMETRY: 257 + *cap_id = ilog2(VSEC_CAP_TELEMETRY); 258 + break; 259 + case VSEC_ID_WATCHER: 260 + *cap_id = ilog2(VSEC_CAP_WATCHER); 261 + break; 262 + case VSEC_ID_CRASHLOG: 263 + *cap_id = ilog2(VSEC_CAP_CRASHLOG); 264 + break; 265 + case VSEC_ID_SDSI: 266 + *cap_id = ilog2(VSEC_CAP_SDSI); 267 + break; 268 + case VSEC_ID_TPMI: 269 + *cap_id = ilog2(VSEC_CAP_TPMI); 270 + break; 271 + case VSEC_ID_DISCOVERY: 272 + *cap_id = ilog2(VSEC_CAP_DISCOVERY); 273 + break; 274 + default: 275 + return -EINVAL; 276 + } 277 + 278 + return 0; 279 + } 280 + 281 + static int intel_vsec_register_device(struct pci_dev *pdev, 282 + struct intel_vsec_header *header, 283 + struct intel_vsec_platform_info *info) 284 + { 285 + const struct vsec_feature_dependency *consumer_deps; 286 + struct vsec_priv *priv; 287 + unsigned long cap_id; 288 + int ret; 289 + 290 + ret = get_cap_id(header->id, &cap_id); 291 + if (ret) 292 + return ret; 293 + 294 + /* 295 + * Only track dependencies for devices probed by the VSEC driver. 296 + * For others using the exported APIs, add the device directly. 297 + */ 298 + if (!pci_match_id(intel_vsec_pci_ids, pdev)) 299 + return intel_vsec_add_dev(pdev, header, info, cap_id); 300 + 301 + priv = pci_get_drvdata(pdev); 302 + if (priv->state[cap_id] == STATE_REGISTERED || 303 + priv->state[cap_id] == STATE_SKIP) 304 + return -EEXIST; 305 + 306 + priv->found_caps |= BIT(cap_id); 307 + 308 + if (!vsec_driver_present(cap_id)) { 309 + priv->state[cap_id] = STATE_SKIP; 310 + return -ENODEV; 311 + } 312 + 313 + consumer_deps = get_consumer_dependencies(priv, cap_id); 314 + if (!consumer_deps || suppliers_ready(priv, consumer_deps, cap_id)) { 315 + ret = intel_vsec_add_dev(pdev, header, info, cap_id); 316 + if (ret) 317 + priv->state[cap_id] = STATE_SKIP; 318 + else 319 + priv->state[cap_id] = STATE_REGISTERED; 320 + 321 + return ret; 322 + } 323 + 324 + return -EAGAIN; 325 + } 326 + 356 327 static bool intel_vsec_walk_header(struct pci_dev *pdev, 357 328 struct intel_vsec_platform_info *info) 358 329 { ··· 464 229 int ret; 465 230 466 231 for ( ; *header; header++) { 467 - ret = intel_vsec_add_dev(pdev, *header, info); 232 + ret = intel_vsec_register_device(pdev, *header, info); 468 233 if (!ret) 469 234 have_devices = true; 470 235 } ··· 512 277 pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr); 513 278 header.id = PCI_DVSEC_HEADER2_ID(hdr); 514 279 515 - ret = intel_vsec_add_dev(pdev, &header, info); 280 + ret = intel_vsec_register_device(pdev, &header, info); 516 281 if (ret) 517 282 continue; 518 283 ··· 557 322 header.tbir = INTEL_DVSEC_TABLE_BAR(table); 558 323 header.offset = INTEL_DVSEC_TABLE_OFFSET(table); 559 324 560 - ret = intel_vsec_add_dev(pdev, &header, info); 325 + ret = intel_vsec_register_device(pdev, &header, info); 561 326 if (ret) 562 327 continue; 563 328 ··· 580 345 } 581 346 EXPORT_SYMBOL_NS_GPL(intel_vsec_register, "INTEL_VSEC"); 582 347 348 + static bool intel_vsec_get_features(struct pci_dev *pdev, 349 + struct intel_vsec_platform_info *info) 350 + { 351 + bool found = false; 352 + 353 + /* 354 + * Both DVSEC and VSEC capabilities can exist on the same device, 355 + * so both intel_vsec_walk_dvsec() and intel_vsec_walk_vsec() must be 356 + * called independently. Additionally, intel_vsec_walk_header() is 357 + * needed for devices that do not have VSEC/DVSEC but provide the 358 + * information via device_data. 359 + */ 360 + if (intel_vsec_walk_dvsec(pdev, info)) 361 + found = true; 362 + 363 + if (intel_vsec_walk_vsec(pdev, info)) 364 + found = true; 365 + 366 + if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) && 367 + intel_vsec_walk_header(pdev, info)) 368 + found = true; 369 + 370 + return found; 371 + } 372 + 373 + static void intel_vsec_skip_missing_dependencies(struct pci_dev *pdev) 374 + { 375 + struct vsec_priv *priv = pci_get_drvdata(pdev); 376 + const struct vsec_feature_dependency *deps = priv->info->deps; 377 + int consumer_id = priv->info->num_deps; 378 + 379 + while (consumer_id--) { 380 + int supplier_id; 381 + 382 + deps = &priv->info->deps[consumer_id]; 383 + 384 + for_each_set_bit(supplier_id, &deps->supplier_bitmap, VSEC_FEATURE_COUNT) { 385 + if (!(BIT(supplier_id) & priv->found_caps)) 386 + priv->state[supplier_id] = STATE_SKIP; 387 + } 388 + } 389 + } 390 + 583 391 static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 584 392 { 585 393 struct intel_vsec_platform_info *info; 586 - bool have_devices = false; 587 - int ret; 394 + struct vsec_priv *priv; 395 + int num_caps, ret; 396 + int run_once = 0; 397 + bool found_any = false; 588 398 589 399 ret = pcim_enable_device(pdev); 590 400 if (ret) ··· 640 360 if (!info) 641 361 return -EINVAL; 642 362 643 - if (intel_vsec_walk_dvsec(pdev, info)) 644 - have_devices = true; 363 + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 364 + if (!priv) 365 + return -ENOMEM; 645 366 646 - if (intel_vsec_walk_vsec(pdev, info)) 647 - have_devices = true; 367 + priv->info = info; 368 + pci_set_drvdata(pdev, priv); 648 369 649 - if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) && 650 - intel_vsec_walk_header(pdev, info)) 651 - have_devices = true; 370 + num_caps = hweight_long(info->caps); 371 + while (num_caps--) { 372 + found_any |= intel_vsec_get_features(pdev, info); 652 373 653 - if (!have_devices) 374 + if (priv->found_caps == info->caps) 375 + break; 376 + 377 + if (!run_once) { 378 + intel_vsec_skip_missing_dependencies(pdev); 379 + run_once = 1; 380 + } 381 + } 382 + 383 + if (!found_any) 654 384 return -ENODEV; 655 385 656 386 return 0; 657 387 } 388 + 389 + int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info, 390 + struct intel_vsec_device *vsec_dev) 391 + { 392 + struct vsec_priv *priv; 393 + 394 + priv = pci_get_drvdata(vsec_dev->pcidev); 395 + if (!priv) 396 + return -EINVAL; 397 + 398 + priv->plat_info = *plat_info; 399 + 400 + return 0; 401 + } 402 + EXPORT_SYMBOL_NS_GPL(intel_vsec_set_mapping, "INTEL_VSEC"); 403 + 404 + struct oobmsm_plat_info *intel_vsec_get_mapping(struct pci_dev *pdev) 405 + { 406 + struct vsec_priv *priv; 407 + 408 + if (!pci_match_id(intel_vsec_pci_ids, pdev)) 409 + return ERR_PTR(-EINVAL); 410 + 411 + priv = pci_get_drvdata(pdev); 412 + if (!priv) 413 + return ERR_PTR(-EINVAL); 414 + 415 + return &priv->plat_info; 416 + } 417 + EXPORT_SYMBOL_NS_GPL(intel_vsec_get_mapping, "INTEL_VSEC"); 658 418 659 419 /* DG1 info */ 660 420 static struct intel_vsec_header dg1_header = { ··· 722 402 .caps = VSEC_CAP_TELEMETRY, 723 403 }; 724 404 405 + static const struct vsec_feature_dependency oobmsm_deps[] = { 406 + { 407 + .feature = VSEC_CAP_TELEMETRY, 408 + .supplier_bitmap = VSEC_CAP_DISCOVERY | VSEC_CAP_TPMI, 409 + }, 410 + }; 411 + 725 412 /* OOBMSM info */ 726 413 static const struct intel_vsec_platform_info oobmsm_info = { 727 - .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_SDSI | VSEC_CAP_TPMI, 414 + .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_SDSI | VSEC_CAP_TPMI | 415 + VSEC_CAP_DISCOVERY, 416 + .deps = oobmsm_deps, 417 + .num_deps = ARRAY_SIZE(oobmsm_deps), 728 418 }; 729 419 730 420 /* DMR OOBMSM info */ 731 421 static const struct intel_vsec_platform_info dmr_oobmsm_info = { 732 - .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_TPMI, 422 + .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_TPMI | VSEC_CAP_DISCOVERY, 423 + .deps = oobmsm_deps, 424 + .num_deps = ARRAY_SIZE(oobmsm_deps), 733 425 }; 734 426 735 427 /* TGL info */
+6 -2
drivers/platform/x86/intel/vsec_tpmi.c
··· 116 116 struct intel_vsec_device *vsec_dev; 117 117 int feature_count; 118 118 u64 pfs_start; 119 - struct intel_tpmi_plat_info plat_info; 119 + struct oobmsm_plat_info plat_info; 120 120 void __iomem *tpmi_control_mem; 121 121 struct dentry *dbgfs_dir; 122 122 }; ··· 187 187 /* Used during auxbus device creation */ 188 188 static DEFINE_IDA(intel_vsec_tpmi_ida); 189 189 190 - struct intel_tpmi_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev) 190 + struct oobmsm_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev) 191 191 { 192 192 struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev); 193 193 ··· 797 797 */ 798 798 if (pfs->pfs_header.tpmi_id == TPMI_INFO_ID) { 799 799 ret = tpmi_process_info(tpmi_info, pfs); 800 + if (ret) 801 + return ret; 802 + 803 + ret = intel_vsec_set_mapping(&tpmi_info->plat_info, vsec_dev); 800 804 if (ret) 801 805 return ret; 802 806 }
drivers/platform/x86/lenovo-wmi-camera.c drivers/platform/x86/lenovo/wmi-camera.c
drivers/platform/x86/lenovo-wmi-hotkey-utilities.c drivers/platform/x86/lenovo/wmi-hotkey-utilities.c
drivers/platform/x86/lenovo-ymc.c drivers/platform/x86/lenovo/ymc.c
+12 -23
drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c drivers/platform/x86/lenovo/yoga-tab2-pro-1380-fastcharger.c
··· 21 21 #include <linux/time.h> 22 22 #include <linux/types.h> 23 23 #include <linux/workqueue.h> 24 - #include "serdev_helpers.h" 24 + #include "../serdev_helpers.h" 25 25 26 26 #define YT2_1380_FC_PDEV_NAME "lenovo-yoga-tab2-pro-1380-fastcharger" 27 27 #define YT2_1380_FC_SERDEV_CTRL "serial0" ··· 240 240 int ret; 241 241 242 242 /* Register pinctrl mappings for setting the UART3 pins mode */ 243 - ret = pinctrl_register_mappings(yt2_1380_fc_pinctrl_map, 244 - ARRAY_SIZE(yt2_1380_fc_pinctrl_map)); 243 + ret = devm_pinctrl_register_mappings(&pdev->dev, yt2_1380_fc_pinctrl_map, 244 + ARRAY_SIZE(yt2_1380_fc_pinctrl_map)); 245 245 if (ret) 246 246 return ret; 247 247 248 248 /* And create the serdev to talk to the charger over the UART3 pins */ 249 249 ctrl_dev = get_serdev_controller("PNP0501", "1", 0, YT2_1380_FC_SERDEV_CTRL); 250 - if (IS_ERR(ctrl_dev)) { 251 - ret = PTR_ERR(ctrl_dev); 252 - goto out_pinctrl_unregister_mappings; 253 - } 250 + if (IS_ERR(ctrl_dev)) 251 + return PTR_ERR(ctrl_dev); 254 252 255 253 serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev)); 256 254 put_device(ctrl_dev); 257 - if (!serdev) { 258 - ret = -ENOMEM; 259 - goto out_pinctrl_unregister_mappings; 260 - } 255 + if (!serdev) 256 + return -ENOMEM; 261 257 262 258 ret = serdev_device_add(serdev); 263 259 if (ret) { 264 - dev_err_probe(&pdev->dev, ret, "adding serdev\n"); 265 260 serdev_device_put(serdev); 266 - goto out_pinctrl_unregister_mappings; 261 + return dev_err_probe(&pdev->dev, ret, "adding serdev\n"); 267 262 } 268 263 269 264 /* ··· 268 273 ret = device_driver_attach(&yt2_1380_fc_serdev_driver.driver, &serdev->dev); 269 274 if (ret) { 270 275 /* device_driver_attach() maps EPROBE_DEFER to EAGAIN, map it back */ 271 - ret = (ret == -EAGAIN) ? -EPROBE_DEFER : ret; 272 - dev_err_probe(&pdev->dev, ret, "attaching serdev driver\n"); 273 - goto out_serdev_device_remove; 276 + serdev_device_remove(serdev); 277 + return dev_err_probe(&pdev->dev, 278 + (ret == -EAGAIN) ? -EPROBE_DEFER : ret, 279 + "attaching serdev driver\n"); 274 280 } 275 281 276 282 /* So that yt2_1380_fc_pdev_remove() can remove the serdev */ 277 283 platform_set_drvdata(pdev, serdev); 278 284 return 0; 279 - 280 - out_serdev_device_remove: 281 - serdev_device_remove(serdev); 282 - out_pinctrl_unregister_mappings: 283 - pinctrl_unregister_mappings(yt2_1380_fc_pinctrl_map); 284 - return ret; 285 285 } 286 286 287 287 static void yt2_1380_fc_pdev_remove(struct platform_device *pdev) ··· 284 294 struct serdev_device *serdev = platform_get_drvdata(pdev); 285 295 286 296 serdev_device_remove(serdev); 287 - pinctrl_unregister_mappings(yt2_1380_fc_pinctrl_map); 288 297 } 289 298 290 299 static struct platform_driver yt2_1380_fc_pdev_driver = {
drivers/platform/x86/lenovo-yogabook.c drivers/platform/x86/lenovo/yogabook.c
+276
drivers/platform/x86/lenovo/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + # 3 + # Lenovo X86 Platform Specific Drivers 4 + # 5 + 6 + config IDEAPAD_LAPTOP 7 + tristate "Lenovo IdeaPad Laptop Extras" 8 + depends on ACPI 9 + depends on ACPI_BATTERY 10 + depends on RFKILL && INPUT 11 + depends on SERIO_I8042 12 + depends on BACKLIGHT_CLASS_DEVICE 13 + depends on ACPI_VIDEO || ACPI_VIDEO = n 14 + depends on ACPI_WMI || ACPI_WMI = n 15 + select ACPI_PLATFORM_PROFILE 16 + select INPUT_SPARSEKMAP 17 + select NEW_LEDS 18 + select LEDS_CLASS 19 + help 20 + This is a driver for Lenovo IdeaPad netbooks contains drivers for 21 + rfkill switch, hotkey, fan control and backlight control. 22 + 23 + config LENOVO_WMI_HOTKEY_UTILITIES 24 + tristate "Lenovo Hotkey Utility WMI extras driver" 25 + depends on ACPI_WMI 26 + select NEW_LEDS 27 + select LEDS_CLASS 28 + imply IDEAPAD_LAPTOP 29 + help 30 + This driver provides WMI support for Lenovo customized hotkeys function, 31 + such as LED control for audio/mic mute event for Ideapad, YOGA, XiaoXin, 32 + Gaming, ThinkBook and so on. 33 + 34 + config LENOVO_WMI_CAMERA 35 + tristate "Lenovo WMI Camera Button driver" 36 + depends on ACPI_WMI 37 + depends on INPUT 38 + help 39 + This driver provides support for Lenovo camera button. The Camera 40 + button is a GPIO device. This driver receives ACPI notifications when 41 + the camera button is switched on/off. 42 + 43 + To compile this driver as a module, choose M here: the module 44 + will be called lenovo-wmi-camera. 45 + 46 + config LENOVO_YMC 47 + tristate "Lenovo Yoga Tablet Mode Control" 48 + depends on ACPI_WMI 49 + depends on INPUT 50 + depends on IDEAPAD_LAPTOP 51 + select INPUT_SPARSEKMAP 52 + help 53 + This driver maps the Tablet Mode Control switch to SW_TABLET_MODE input 54 + events for Lenovo Yoga notebooks. 55 + 56 + config THINKPAD_ACPI 57 + tristate "ThinkPad ACPI Laptop Extras" 58 + depends on ACPI_EC 59 + depends on ACPI_BATTERY 60 + depends on INPUT 61 + depends on RFKILL || RFKILL = n 62 + depends on ACPI_VIDEO || ACPI_VIDEO = n 63 + depends on BACKLIGHT_CLASS_DEVICE 64 + depends on I2C 65 + depends on DRM 66 + select ACPI_PLATFORM_PROFILE 67 + select DRM_PRIVACY_SCREEN 68 + select HWMON 69 + select NVRAM 70 + select NEW_LEDS 71 + select LEDS_CLASS 72 + select INPUT_SPARSEKMAP 73 + help 74 + This is a driver for the IBM and Lenovo ThinkPad laptops. It adds 75 + support for Fn-Fx key combinations, Bluetooth control, video 76 + output switching, ThinkLight control, UltraBay eject and more. 77 + For more information about this driver see 78 + <file:Documentation/admin-guide/laptops/thinkpad-acpi.rst> and 79 + <http://ibm-acpi.sf.net/> . 80 + 81 + This driver was formerly known as ibm-acpi. 82 + 83 + Extra functionality will be available if the rfkill (CONFIG_RFKILL) 84 + and/or ALSA (CONFIG_SND) subsystems are available in the kernel. 85 + Note that if you want ThinkPad-ACPI to be built-in instead of 86 + modular, ALSA and rfkill will also have to be built-in. 87 + 88 + If you have an IBM or Lenovo ThinkPad laptop, say Y or M here. 89 + 90 + config THINKPAD_ACPI_ALSA_SUPPORT 91 + bool "Console audio control ALSA interface" 92 + depends on THINKPAD_ACPI 93 + depends on SND 94 + depends on SND = y || THINKPAD_ACPI = SND 95 + default y 96 + help 97 + Enables monitoring of the built-in console audio output control 98 + (headphone and speakers), which is operated by the mute and (in 99 + some ThinkPad models) volume hotkeys. 100 + 101 + If this option is enabled, ThinkPad-ACPI will export an ALSA card 102 + with a single read-only mixer control, which should be used for 103 + on-screen-display feedback purposes by the Desktop Environment. 104 + 105 + Optionally, the driver will also allow software control (the 106 + ALSA mixer will be made read-write). Please refer to the driver 107 + documentation for details. 108 + 109 + All IBM models have both volume and mute control. Newer Lenovo 110 + models only have mute control (the volume hotkeys are just normal 111 + keys and volume control is done through the main HDA mixer). 112 + 113 + config THINKPAD_ACPI_DEBUGFACILITIES 114 + bool "Maintainer debug facilities" 115 + depends on THINKPAD_ACPI 116 + help 117 + Enables extra stuff in the thinkpad-acpi which is completely useless 118 + for normal use. Read the driver source to find out what it does. 119 + 120 + Say N here, unless you were told by a kernel maintainer to do 121 + otherwise. 122 + 123 + config THINKPAD_ACPI_DEBUG 124 + bool "Verbose debug mode" 125 + depends on THINKPAD_ACPI 126 + help 127 + Enables extra debugging information, at the expense of a slightly 128 + increase in driver size. 129 + 130 + If you are not sure, say N here. 131 + 132 + config THINKPAD_ACPI_UNSAFE_LEDS 133 + bool "Allow control of important LEDs (unsafe)" 134 + depends on THINKPAD_ACPI 135 + help 136 + Overriding LED state on ThinkPads can mask important 137 + firmware alerts (like critical battery condition), or misled 138 + the user into damaging the hardware (undocking or ejecting 139 + the bay while buses are still active), etc. 140 + 141 + LED control on the ThinkPad is write-only (with very few 142 + exceptions on very ancient models), which makes it 143 + impossible to know beforehand if important information will 144 + be lost when one changes LED state. 145 + 146 + Users that know what they are doing can enable this option 147 + and the driver will allow control of every LED, including 148 + the ones on the dock stations. 149 + 150 + Never enable this option on a distribution kernel. 151 + 152 + Say N here, unless you are building a kernel for your own 153 + use, and need to control the important firmware LEDs. 154 + 155 + config THINKPAD_ACPI_VIDEO 156 + bool "Video output control support" 157 + depends on THINKPAD_ACPI 158 + default y 159 + help 160 + Allows the thinkpad_acpi driver to provide an interface to control 161 + the various video output ports. 162 + 163 + This feature often won't work well, depending on ThinkPad model, 164 + display state, video output devices in use, whether there is a X 165 + server running, phase of the moon, and the current mood of 166 + Schroedinger's cat. If you can use X.org's RandR to control 167 + your ThinkPad's video output ports instead of this feature, 168 + don't think twice: do it and say N here to save memory and avoid 169 + bad interactions with X.org. 170 + 171 + NOTE: access to this feature is limited to processes with the 172 + CAP_SYS_ADMIN capability, to avoid local DoS issues in platforms 173 + where it interacts badly with X.org. 174 + 175 + If you are not sure, say Y here but do try to check if you could 176 + be using X.org RandR instead. 177 + 178 + config THINKPAD_ACPI_HOTKEY_POLL 179 + bool "Support NVRAM polling for hot keys" 180 + depends on THINKPAD_ACPI 181 + default y 182 + help 183 + Some thinkpad models benefit from NVRAM polling to detect a few of 184 + the hot key press events. If you know your ThinkPad model does not 185 + need to do NVRAM polling to support any of the hot keys you use, 186 + unselecting this option will save about 1kB of memory. 187 + 188 + ThinkPads T40 and newer, R52 and newer, and X31 and newer are 189 + unlikely to need NVRAM polling in their latest BIOS versions. 190 + 191 + NVRAM polling can detect at most the following keys: ThinkPad/Access 192 + IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute, 193 + Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12). 194 + 195 + If you are not sure, say Y here. The driver enables polling only if 196 + it is strictly necessary to do so. 197 + 198 + config THINKPAD_LMI 199 + tristate "Lenovo WMI-based systems management driver" 200 + depends on ACPI_WMI 201 + depends on DMI 202 + select FW_ATTR_CLASS 203 + help 204 + This driver allows changing BIOS settings on Lenovo machines whose 205 + BIOS support the WMI interface. 206 + 207 + To compile this driver as a module, choose M here: the module will 208 + be called think-lmi. 209 + 210 + config YOGABOOK 211 + tristate "Lenovo Yoga Book tablet key driver" 212 + depends on ACPI_WMI 213 + depends on INPUT 214 + depends on I2C 215 + select LEDS_CLASS 216 + select NEW_LEDS 217 + help 218 + Say Y here if you want to support the 'Pen' key and keyboard backlight 219 + control on the Lenovo Yoga Book tablets. 220 + 221 + To compile this driver as a module, choose M here: the module will 222 + be called lenovo-yogabook. 223 + 224 + config YT2_1380 225 + tristate "Lenovo Yoga Tablet 2 1380 fast charge driver" 226 + depends on SERIAL_DEV_BUS 227 + depends on EXTCON 228 + depends on ACPI 229 + help 230 + Say Y here to enable support for the custom fast charging protocol 231 + found on the Lenovo Yoga Tablet 2 1380F / 1380L models. 232 + 233 + To compile this driver as a module, choose M here: the module will 234 + be called lenovo-yogabook. 235 + 236 + config LENOVO_WMI_DATA01 237 + tristate 238 + depends on ACPI_WMI 239 + 240 + config LENOVO_WMI_EVENTS 241 + tristate 242 + depends on ACPI_WMI 243 + 244 + config LENOVO_WMI_HELPERS 245 + tristate 246 + depends on ACPI_WMI 247 + 248 + config LENOVO_WMI_GAMEZONE 249 + tristate "Lenovo GameZone WMI Driver" 250 + depends on ACPI_WMI 251 + depends on DMI 252 + select ACPI_PLATFORM_PROFILE 253 + select LENOVO_WMI_EVENTS 254 + select LENOVO_WMI_HELPERS 255 + select LENOVO_WMI_TUNING 256 + help 257 + Say Y here if you have a WMI aware Lenovo Legion device and would like to use the 258 + platform-profile firmware interface to manage power usage. 259 + 260 + To compile this driver as a module, choose M here: the module will 261 + be called lenovo-wmi-gamezone. 262 + 263 + config LENOVO_WMI_TUNING 264 + tristate "Lenovo Other Mode WMI Driver" 265 + depends on ACPI_WMI 266 + select FW_ATTR_CLASS 267 + select LENOVO_WMI_DATA01 268 + select LENOVO_WMI_EVENTS 269 + select LENOVO_WMI_HELPERS 270 + help 271 + Say Y here if you have a WMI aware Lenovo Legion device and would like to use the 272 + firmware_attributes API to control various tunable settings typically exposed by 273 + Lenovo software in Windows. 274 + 275 + To compile this driver as a module, choose M here: the module will 276 + be called lenovo-wmi-other.
+28
drivers/platform/x86/lenovo/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # 3 + # Makefile for linux/drivers/platform/x86/lenovo 4 + # Lenovo x86 Platform Specific Drivers 5 + # 6 + obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o 7 + obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o 8 + obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o 9 + 10 + lenovo-target-$(CONFIG_LENOVO_WMI_HOTKEY_UTILITIES) += wmi-hotkey-utilities.o 11 + lenovo-target-$(CONFIG_LENOVO_YMC) += ymc.o 12 + lenovo-target-$(CONFIG_YOGABOOK) += yogabook.o 13 + lenovo-target-$(CONFIG_YT2_1380) += yoga-tab2-pro-1380-fastcharger.o 14 + lenovo-target-$(CONFIG_LENOVO_WMI_CAMERA) += wmi-camera.o 15 + lenovo-target-$(CONFIG_LENOVO_WMI_DATA01) += wmi-capdata01.o 16 + lenovo-target-$(CONFIG_LENOVO_WMI_EVENTS) += wmi-events.o 17 + lenovo-target-$(CONFIG_LENOVO_WMI_HELPERS) += wmi-helpers.o 18 + lenovo-target-$(CONFIG_LENOVO_WMI_GAMEZONE) += wmi-gamezone.o 19 + lenovo-target-$(CONFIG_LENOVO_WMI_TUNING) += wmi-other.o 20 + 21 + # Add 'lenovo' prefix to each module listed in lenovo-target-* 22 + define LENOVO_OBJ_TARGET 23 + lenovo-$(1)-y := $(1).o 24 + obj-$(2) += lenovo-$(1).o 25 + endef 26 + 27 + $(foreach target, $(basename $(lenovo-target-y)), $(eval $(call LENOVO_OBJ_TARGET,$(target),y))) 28 + $(foreach target, $(basename $(lenovo-target-m)), $(eval $(call LENOVO_OBJ_TARGET,$(target),m)))
+302
drivers/platform/x86/lenovo/wmi-capdata01.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Lenovo Capability Data 01 WMI Data Block driver. 4 + * 5 + * Lenovo Capability Data 01 provides information on tunable attributes used by 6 + * the "Other Mode" WMI interface. The data includes if the attribute is 7 + * supported by the hardware, the default_value, max_value, min_value, and step 8 + * increment. Each attribute has multiple pages, one for each of the thermal 9 + * modes managed by the Gamezone interface. 10 + * 11 + * Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> 12 + */ 13 + 14 + #include <linux/acpi.h> 15 + #include <linux/cleanup.h> 16 + #include <linux/component.h> 17 + #include <linux/container_of.h> 18 + #include <linux/device.h> 19 + #include <linux/export.h> 20 + #include <linux/gfp_types.h> 21 + #include <linux/module.h> 22 + #include <linux/mutex.h> 23 + #include <linux/mutex_types.h> 24 + #include <linux/notifier.h> 25 + #include <linux/overflow.h> 26 + #include <linux/types.h> 27 + #include <linux/wmi.h> 28 + 29 + #include "wmi-capdata01.h" 30 + 31 + #define LENOVO_CAPABILITY_DATA_01_GUID "7A8F5407-CB67-4D6E-B547-39B3BE018154" 32 + 33 + #define ACPI_AC_CLASS "ac_adapter" 34 + #define ACPI_AC_NOTIFY_STATUS 0x80 35 + 36 + struct lwmi_cd01_priv { 37 + struct notifier_block acpi_nb; /* ACPI events */ 38 + struct wmi_device *wdev; 39 + struct cd01_list *list; 40 + }; 41 + 42 + struct cd01_list { 43 + struct mutex list_mutex; /* list R/W mutex */ 44 + u8 count; 45 + struct capdata01 data[]; 46 + }; 47 + 48 + /** 49 + * lwmi_cd01_component_bind() - Bind component to master device. 50 + * @cd01_dev: Pointer to the lenovo-wmi-capdata01 driver parent device. 51 + * @om_dev: Pointer to the lenovo-wmi-other driver parent device. 52 + * @data: capdata01_list object pointer used to return the capability data. 53 + * 54 + * On lenovo-wmi-other's master bind, provide a pointer to the local capdata01 55 + * list. This is used to call lwmi_cd01_get_data to look up attribute data 56 + * from the lenovo-wmi-other driver. 57 + * 58 + * Return: 0 59 + */ 60 + static int lwmi_cd01_component_bind(struct device *cd01_dev, 61 + struct device *om_dev, void *data) 62 + { 63 + struct lwmi_cd01_priv *priv = dev_get_drvdata(cd01_dev); 64 + struct cd01_list **cd01_list = data; 65 + 66 + *cd01_list = priv->list; 67 + 68 + return 0; 69 + } 70 + 71 + static const struct component_ops lwmi_cd01_component_ops = { 72 + .bind = lwmi_cd01_component_bind, 73 + }; 74 + 75 + /** 76 + * lwmi_cd01_get_data - Get the data of the specified attribute 77 + * @list: The lenovo-wmi-capdata01 pointer to its cd01_list struct. 78 + * @attribute_id: The capdata attribute ID to be found. 79 + * @output: Pointer to a capdata01 struct to return the data. 80 + * 81 + * Retrieves the capability data 01 struct pointer for the given 82 + * attribute for its specified thermal mode. 83 + * 84 + * Return: 0 on success, or -EINVAL. 85 + */ 86 + int lwmi_cd01_get_data(struct cd01_list *list, u32 attribute_id, struct capdata01 *output) 87 + { 88 + u8 idx; 89 + 90 + guard(mutex)(&list->list_mutex); 91 + for (idx = 0; idx < list->count; idx++) { 92 + if (list->data[idx].id != attribute_id) 93 + continue; 94 + memcpy(output, &list->data[idx], sizeof(list->data[idx])); 95 + return 0; 96 + }; 97 + 98 + return -EINVAL; 99 + } 100 + EXPORT_SYMBOL_NS_GPL(lwmi_cd01_get_data, "LENOVO_WMI_CD01"); 101 + 102 + /** 103 + * lwmi_cd01_cache() - Cache all WMI data block information 104 + * @priv: lenovo-wmi-capdata01 driver data. 105 + * 106 + * Loop through each WMI data block and cache the data. 107 + * 108 + * Return: 0 on success, or an error. 109 + */ 110 + static int lwmi_cd01_cache(struct lwmi_cd01_priv *priv) 111 + { 112 + int idx; 113 + 114 + guard(mutex)(&priv->list->list_mutex); 115 + for (idx = 0; idx < priv->list->count; idx++) { 116 + union acpi_object *ret_obj __free(kfree) = NULL; 117 + 118 + ret_obj = wmidev_block_query(priv->wdev, idx); 119 + if (!ret_obj) 120 + return -ENODEV; 121 + 122 + if (ret_obj->type != ACPI_TYPE_BUFFER || 123 + ret_obj->buffer.length < sizeof(priv->list->data[idx])) 124 + continue; 125 + 126 + memcpy(&priv->list->data[idx], ret_obj->buffer.pointer, 127 + ret_obj->buffer.length); 128 + } 129 + 130 + return 0; 131 + } 132 + 133 + /** 134 + * lwmi_cd01_alloc() - Allocate a cd01_list struct in drvdata 135 + * @priv: lenovo-wmi-capdata01 driver data. 136 + * 137 + * Allocate a cd01_list struct large enough to contain data from all WMI data 138 + * blocks provided by the interface. 139 + * 140 + * Return: 0 on success, or an error. 141 + */ 142 + static int lwmi_cd01_alloc(struct lwmi_cd01_priv *priv) 143 + { 144 + struct cd01_list *list; 145 + size_t list_size; 146 + int count, ret; 147 + 148 + count = wmidev_instance_count(priv->wdev); 149 + list_size = struct_size(list, data, count); 150 + 151 + list = devm_kzalloc(&priv->wdev->dev, list_size, GFP_KERNEL); 152 + if (!list) 153 + return -ENOMEM; 154 + 155 + ret = devm_mutex_init(&priv->wdev->dev, &list->list_mutex); 156 + if (ret) 157 + return ret; 158 + 159 + list->count = count; 160 + priv->list = list; 161 + 162 + return 0; 163 + } 164 + 165 + /** 166 + * lwmi_cd01_setup() - Cache all WMI data block information 167 + * @priv: lenovo-wmi-capdata01 driver data. 168 + * 169 + * Allocate a cd01_list struct large enough to contain data from all WMI data 170 + * blocks provided by the interface. Then loop through each data block and 171 + * cache the data. 172 + * 173 + * Return: 0 on success, or an error code. 174 + */ 175 + static int lwmi_cd01_setup(struct lwmi_cd01_priv *priv) 176 + { 177 + int ret; 178 + 179 + ret = lwmi_cd01_alloc(priv); 180 + if (ret) 181 + return ret; 182 + 183 + return lwmi_cd01_cache(priv); 184 + } 185 + 186 + /** 187 + * lwmi_cd01_notifier_call() - Call method for lenovo-wmi-capdata01 driver notifier. 188 + * block call chain. 189 + * @nb: The notifier_block registered to lenovo-wmi-events driver. 190 + * @action: Unused. 191 + * @data: The ACPI event. 192 + * 193 + * For LWMI_EVENT_THERMAL_MODE, set current_mode and notify platform_profile 194 + * of a change. 195 + * 196 + * Return: notifier_block status. 197 + */ 198 + static int lwmi_cd01_notifier_call(struct notifier_block *nb, unsigned long action, 199 + void *data) 200 + { 201 + struct acpi_bus_event *event = data; 202 + struct lwmi_cd01_priv *priv; 203 + int ret; 204 + 205 + if (strcmp(event->device_class, ACPI_AC_CLASS) != 0) 206 + return NOTIFY_DONE; 207 + 208 + priv = container_of(nb, struct lwmi_cd01_priv, acpi_nb); 209 + 210 + switch (event->type) { 211 + case ACPI_AC_NOTIFY_STATUS: 212 + ret = lwmi_cd01_cache(priv); 213 + if (ret) 214 + return NOTIFY_BAD; 215 + 216 + return NOTIFY_OK; 217 + default: 218 + return NOTIFY_DONE; 219 + } 220 + } 221 + 222 + /** 223 + * lwmi_cd01_unregister() - Unregister the cd01 ACPI notifier_block. 224 + * @data: The ACPI event notifier_block to unregister. 225 + */ 226 + static void lwmi_cd01_unregister(void *data) 227 + { 228 + struct notifier_block *acpi_nb = data; 229 + 230 + unregister_acpi_notifier(acpi_nb); 231 + } 232 + 233 + static int lwmi_cd01_probe(struct wmi_device *wdev, const void *context) 234 + 235 + { 236 + struct lwmi_cd01_priv *priv; 237 + int ret; 238 + 239 + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); 240 + if (!priv) 241 + return -ENOMEM; 242 + 243 + priv->wdev = wdev; 244 + dev_set_drvdata(&wdev->dev, priv); 245 + 246 + ret = lwmi_cd01_setup(priv); 247 + if (ret) 248 + return ret; 249 + 250 + priv->acpi_nb.notifier_call = lwmi_cd01_notifier_call; 251 + 252 + ret = register_acpi_notifier(&priv->acpi_nb); 253 + if (ret) 254 + return ret; 255 + 256 + ret = devm_add_action_or_reset(&wdev->dev, lwmi_cd01_unregister, &priv->acpi_nb); 257 + if (ret) 258 + return ret; 259 + 260 + return component_add(&wdev->dev, &lwmi_cd01_component_ops); 261 + } 262 + 263 + static void lwmi_cd01_remove(struct wmi_device *wdev) 264 + { 265 + component_del(&wdev->dev, &lwmi_cd01_component_ops); 266 + } 267 + 268 + static const struct wmi_device_id lwmi_cd01_id_table[] = { 269 + { LENOVO_CAPABILITY_DATA_01_GUID, NULL }, 270 + {} 271 + }; 272 + 273 + static struct wmi_driver lwmi_cd01_driver = { 274 + .driver = { 275 + .name = "lenovo_wmi_cd01", 276 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 277 + }, 278 + .id_table = lwmi_cd01_id_table, 279 + .probe = lwmi_cd01_probe, 280 + .remove = lwmi_cd01_remove, 281 + .no_singleton = true, 282 + }; 283 + 284 + /** 285 + * lwmi_cd01_match() - Match rule for the master driver. 286 + * @dev: Pointer to the capability data 01 parent device. 287 + * @data: Unused void pointer for passing match criteria. 288 + * 289 + * Return: int. 290 + */ 291 + int lwmi_cd01_match(struct device *dev, void *data) 292 + { 293 + return dev->driver == &lwmi_cd01_driver.driver; 294 + } 295 + EXPORT_SYMBOL_NS_GPL(lwmi_cd01_match, "LENOVO_WMI_CD01"); 296 + 297 + module_wmi_driver(lwmi_cd01_driver); 298 + 299 + MODULE_DEVICE_TABLE(wmi, lwmi_cd01_id_table); 300 + MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>"); 301 + MODULE_DESCRIPTION("Lenovo Capability Data 01 WMI Driver"); 302 + MODULE_LICENSE("GPL");
+25
drivers/platform/x86/lenovo/wmi-capdata01.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + /* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */ 4 + 5 + #ifndef _LENOVO_WMI_CAPDATA01_H_ 6 + #define _LENOVO_WMI_CAPDATA01_H_ 7 + 8 + #include <linux/types.h> 9 + 10 + struct device; 11 + struct cd01_list; 12 + 13 + struct capdata01 { 14 + u32 id; 15 + u32 supported; 16 + u32 default_value; 17 + u32 step; 18 + u32 min_value; 19 + u32 max_value; 20 + }; 21 + 22 + int lwmi_cd01_get_data(struct cd01_list *list, u32 attribute_id, struct capdata01 *output); 23 + int lwmi_cd01_match(struct device *dev, void *data); 24 + 25 + #endif /* !_LENOVO_WMI_CAPDATA01_H_ */
+196
drivers/platform/x86/lenovo/wmi-events.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Lenovo WMI Events driver. Lenovo WMI interfaces provide various 4 + * hardware triggered events that many drivers need to have propagated. 5 + * This driver provides a uniform entrypoint for these events so that 6 + * any driver that needs to respond to these events can subscribe to a 7 + * notifier chain. 8 + * 9 + * Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> 10 + */ 11 + 12 + #include <linux/acpi.h> 13 + #include <linux/export.h> 14 + #include <linux/module.h> 15 + #include <linux/notifier.h> 16 + #include <linux/types.h> 17 + #include <linux/wmi.h> 18 + 19 + #include "wmi-events.h" 20 + #include "wmi-gamezone.h" 21 + 22 + #define THERMAL_MODE_EVENT_GUID "D320289E-8FEA-41E0-86F9-911D83151B5F" 23 + 24 + #define LWMI_EVENT_DEVICE(guid, type) \ 25 + .guid_string = (guid), .context = &(enum lwmi_events_type) \ 26 + { \ 27 + type \ 28 + } 29 + 30 + static BLOCKING_NOTIFIER_HEAD(events_chain_head); 31 + 32 + struct lwmi_events_priv { 33 + struct wmi_device *wdev; 34 + enum lwmi_events_type type; 35 + }; 36 + 37 + /** 38 + * lwmi_events_register_notifier() - Add a notifier to the notifier chain. 39 + * @nb: The notifier_block struct to register 40 + * 41 + * Call blocking_notifier_chain_register to register the notifier block to the 42 + * lenovo-wmi-events driver blocking notifier chain. 43 + * 44 + * Return: 0 on success, %-EEXIST on error. 45 + */ 46 + int lwmi_events_register_notifier(struct notifier_block *nb) 47 + { 48 + return blocking_notifier_chain_register(&events_chain_head, nb); 49 + } 50 + EXPORT_SYMBOL_NS_GPL(lwmi_events_register_notifier, "LENOVO_WMI_EVENTS"); 51 + 52 + /** 53 + * lwmi_events_unregister_notifier() - Remove a notifier from the notifier 54 + * chain. 55 + * @nb: The notifier_block struct to unregister 56 + * 57 + * Call blocking_notifier_chain_unregister to unregister the notifier block 58 + * from the lenovo-wmi-events driver blocking notifier chain. 59 + * 60 + * Return: 0 on success, %-ENOENT on error. 61 + */ 62 + int lwmi_events_unregister_notifier(struct notifier_block *nb) 63 + { 64 + return blocking_notifier_chain_unregister(&events_chain_head, nb); 65 + } 66 + EXPORT_SYMBOL_NS_GPL(lwmi_events_unregister_notifier, "LENOVO_WMI_EVENTS"); 67 + 68 + /** 69 + * devm_lwmi_events_unregister_notifier() - Remove a notifier from the notifier 70 + * chain. 71 + * @data: Void pointer to the notifier_block struct to unregister. 72 + * 73 + * Call lwmi_events_unregister_notifier to unregister the notifier block from 74 + * the lenovo-wmi-events driver blocking notifier chain. 75 + * 76 + * Return: 0 on success, %-ENOENT on error. 77 + */ 78 + static void devm_lwmi_events_unregister_notifier(void *data) 79 + { 80 + struct notifier_block *nb = data; 81 + 82 + lwmi_events_unregister_notifier(nb); 83 + } 84 + 85 + /** 86 + * devm_lwmi_events_register_notifier() - Add a notifier to the notifier chain. 87 + * @dev: The parent device of the notifier_block struct. 88 + * @nb: The notifier_block struct to register 89 + * 90 + * Call lwmi_events_register_notifier to register the notifier block to the 91 + * lenovo-wmi-events driver blocking notifier chain. Then add, as a device 92 + * managed action, unregister_notifier to automatically unregister the 93 + * notifier block upon its parent device removal. 94 + * 95 + * Return: 0 on success, or an error code. 96 + */ 97 + int devm_lwmi_events_register_notifier(struct device *dev, 98 + struct notifier_block *nb) 99 + { 100 + int ret; 101 + 102 + ret = lwmi_events_register_notifier(nb); 103 + if (ret < 0) 104 + return ret; 105 + 106 + return devm_add_action_or_reset(dev, devm_lwmi_events_unregister_notifier, nb); 107 + } 108 + EXPORT_SYMBOL_NS_GPL(devm_lwmi_events_register_notifier, "LENOVO_WMI_EVENTS"); 109 + 110 + /** 111 + * lwmi_events_notify() - Call functions for the notifier call chain. 112 + * @wdev: The parent WMI device of the driver. 113 + * @obj: ACPI object passed by the registered WMI Event. 114 + * 115 + * Validate WMI event data and notify all registered drivers of the event and 116 + * its output. 117 + * 118 + * Return: 0 on success, or an error code. 119 + */ 120 + static void lwmi_events_notify(struct wmi_device *wdev, union acpi_object *obj) 121 + { 122 + struct lwmi_events_priv *priv = dev_get_drvdata(&wdev->dev); 123 + int sel_prof; 124 + int ret; 125 + 126 + switch (priv->type) { 127 + case LWMI_EVENT_THERMAL_MODE: 128 + if (obj->type != ACPI_TYPE_INTEGER) 129 + return; 130 + 131 + sel_prof = obj->integer.value; 132 + 133 + switch (sel_prof) { 134 + case LWMI_GZ_THERMAL_MODE_QUIET: 135 + case LWMI_GZ_THERMAL_MODE_BALANCED: 136 + case LWMI_GZ_THERMAL_MODE_PERFORMANCE: 137 + case LWMI_GZ_THERMAL_MODE_EXTREME: 138 + case LWMI_GZ_THERMAL_MODE_CUSTOM: 139 + ret = blocking_notifier_call_chain(&events_chain_head, 140 + LWMI_EVENT_THERMAL_MODE, 141 + &sel_prof); 142 + if (ret == NOTIFY_BAD) 143 + dev_err(&wdev->dev, 144 + "Failed to send notification to call chain for WMI Events\n"); 145 + return; 146 + default: 147 + dev_err(&wdev->dev, "Got invalid thermal mode: %x", 148 + sel_prof); 149 + return; 150 + } 151 + break; 152 + default: 153 + return; 154 + } 155 + } 156 + 157 + static int lwmi_events_probe(struct wmi_device *wdev, const void *context) 158 + { 159 + struct lwmi_events_priv *priv; 160 + 161 + if (!context) 162 + return -EINVAL; 163 + 164 + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); 165 + if (!priv) 166 + return -ENOMEM; 167 + 168 + priv->wdev = wdev; 169 + priv->type = *(enum lwmi_events_type *)context; 170 + dev_set_drvdata(&wdev->dev, priv); 171 + 172 + return 0; 173 + } 174 + 175 + static const struct wmi_device_id lwmi_events_id_table[] = { 176 + { LWMI_EVENT_DEVICE(THERMAL_MODE_EVENT_GUID, LWMI_EVENT_THERMAL_MODE) }, 177 + {} 178 + }; 179 + 180 + static struct wmi_driver lwmi_events_driver = { 181 + .driver = { 182 + .name = "lenovo_wmi_events", 183 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 184 + }, 185 + .id_table = lwmi_events_id_table, 186 + .probe = lwmi_events_probe, 187 + .notify = lwmi_events_notify, 188 + .no_singleton = true, 189 + }; 190 + 191 + module_wmi_driver(lwmi_events_driver); 192 + 193 + MODULE_DEVICE_TABLE(wmi, lwmi_events_id_table); 194 + MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>"); 195 + MODULE_DESCRIPTION("Lenovo WMI Events Driver"); 196 + MODULE_LICENSE("GPL");
+20
drivers/platform/x86/lenovo/wmi-events.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + /* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */ 4 + 5 + #ifndef _LENOVO_WMI_EVENTS_H_ 6 + #define _LENOVO_WMI_EVENTS_H_ 7 + 8 + struct device; 9 + struct notifier_block; 10 + 11 + enum lwmi_events_type { 12 + LWMI_EVENT_THERMAL_MODE = 1, 13 + }; 14 + 15 + int lwmi_events_register_notifier(struct notifier_block *nb); 16 + int lwmi_events_unregister_notifier(struct notifier_block *nb); 17 + int devm_lwmi_events_register_notifier(struct device *dev, 18 + struct notifier_block *nb); 19 + 20 + #endif /* !_LENOVO_WMI_EVENTS_H_ */
+407
drivers/platform/x86/lenovo/wmi-gamezone.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Lenovo GameZone WMI interface driver. 4 + * 5 + * The GameZone WMI interface provides platform profile and fan curve settings 6 + * for devices that fall under the "Gaming Series" of Lenovo Legion devices. 7 + * 8 + * Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> 9 + */ 10 + 11 + #include <linux/acpi.h> 12 + #include <linux/dmi.h> 13 + #include <linux/export.h> 14 + #include <linux/list.h> 15 + #include <linux/module.h> 16 + #include <linux/notifier.h> 17 + #include <linux/platform_profile.h> 18 + #include <linux/spinlock.h> 19 + #include <linux/spinlock_types.h> 20 + #include <linux/types.h> 21 + #include <linux/wmi.h> 22 + 23 + #include "wmi-events.h" 24 + #include "wmi-gamezone.h" 25 + #include "wmi-helpers.h" 26 + #include "wmi-other.h" 27 + 28 + #define LENOVO_GAMEZONE_GUID "887B54E3-DDDC-4B2C-8B88-68A26A8835D0" 29 + 30 + #define LWMI_GZ_METHOD_ID_SMARTFAN_SUP 43 31 + #define LWMI_GZ_METHOD_ID_SMARTFAN_SET 44 32 + #define LWMI_GZ_METHOD_ID_SMARTFAN_GET 45 33 + 34 + static BLOCKING_NOTIFIER_HEAD(gz_chain_head); 35 + 36 + struct lwmi_gz_priv { 37 + enum thermal_mode current_mode; 38 + struct notifier_block event_nb; 39 + struct notifier_block mode_nb; 40 + spinlock_t gz_mode_lock; /* current_mode lock */ 41 + struct wmi_device *wdev; 42 + int extreme_supported; 43 + struct device *ppdev; 44 + }; 45 + 46 + struct quirk_entry { 47 + bool extreme_supported; 48 + }; 49 + 50 + static struct quirk_entry quirk_no_extreme_bug = { 51 + .extreme_supported = false, 52 + }; 53 + 54 + /** 55 + * lwmi_gz_mode_call() - Call method for lenovo-wmi-other driver notifier. 56 + * 57 + * @nb: The notifier_block registered to lenovo-wmi-other driver. 58 + * @cmd: The event type. 59 + * @data: Thermal mode enum pointer pointer for returning the thermal mode. 60 + * 61 + * For LWMI_GZ_GET_THERMAL_MODE, retrieve the current thermal mode. 62 + * 63 + * Return: Notifier_block status. 64 + */ 65 + static int lwmi_gz_mode_call(struct notifier_block *nb, unsigned long cmd, 66 + void *data) 67 + { 68 + enum thermal_mode **mode = data; 69 + struct lwmi_gz_priv *priv; 70 + 71 + priv = container_of(nb, struct lwmi_gz_priv, mode_nb); 72 + 73 + switch (cmd) { 74 + case LWMI_GZ_GET_THERMAL_MODE: 75 + scoped_guard(spinlock, &priv->gz_mode_lock) { 76 + **mode = priv->current_mode; 77 + } 78 + return NOTIFY_OK; 79 + default: 80 + return NOTIFY_DONE; 81 + } 82 + } 83 + 84 + /** 85 + * lwmi_gz_event_call() - Call method for lenovo-wmi-events driver notifier. 86 + * block call chain. 87 + * @nb: The notifier_block registered to lenovo-wmi-events driver. 88 + * @cmd: The event type. 89 + * @data: The data to be updated by the event. 90 + * 91 + * For LWMI_EVENT_THERMAL_MODE, set current_mode and notify platform_profile 92 + * of a change. 93 + * 94 + * Return: notifier_block status. 95 + */ 96 + static int lwmi_gz_event_call(struct notifier_block *nb, unsigned long cmd, 97 + void *data) 98 + { 99 + enum thermal_mode *mode = data; 100 + struct lwmi_gz_priv *priv; 101 + 102 + priv = container_of(nb, struct lwmi_gz_priv, event_nb); 103 + 104 + switch (cmd) { 105 + case LWMI_EVENT_THERMAL_MODE: 106 + scoped_guard(spinlock, &priv->gz_mode_lock) { 107 + priv->current_mode = *mode; 108 + } 109 + platform_profile_notify(priv->ppdev); 110 + return NOTIFY_STOP; 111 + default: 112 + return NOTIFY_DONE; 113 + } 114 + } 115 + 116 + /** 117 + * lwmi_gz_thermal_mode_supported() - Get the version of the WMI 118 + * interface to determine the support level. 119 + * @wdev: The Gamezone WMI device. 120 + * @supported: Pointer to return the support level with. 121 + * 122 + * Return: 0 on success, or an error code. 123 + */ 124 + static int lwmi_gz_thermal_mode_supported(struct wmi_device *wdev, 125 + int *supported) 126 + { 127 + return lwmi_dev_evaluate_int(wdev, 0x0, LWMI_GZ_METHOD_ID_SMARTFAN_SUP, 128 + NULL, 0, supported); 129 + } 130 + 131 + /** 132 + * lwmi_gz_thermal_mode_get() - Get the current thermal mode. 133 + * @wdev: The Gamezone interface WMI device. 134 + * @mode: Pointer to return the thermal mode with. 135 + * 136 + * Return: 0 on success, or an error code. 137 + */ 138 + static int lwmi_gz_thermal_mode_get(struct wmi_device *wdev, 139 + enum thermal_mode *mode) 140 + { 141 + return lwmi_dev_evaluate_int(wdev, 0x0, LWMI_GZ_METHOD_ID_SMARTFAN_GET, 142 + NULL, 0, mode); 143 + } 144 + 145 + /** 146 + * lwmi_gz_profile_get() - Get the current platform profile. 147 + * @dev: the Gamezone interface parent device. 148 + * @profile: Pointer to provide the current platform profile with. 149 + * 150 + * Call lwmi_gz_thermal_mode_get and convert the thermal mode into a platform 151 + * profile based on the support level of the interface. 152 + * 153 + * Return: 0 on success, or an error code. 154 + */ 155 + static int lwmi_gz_profile_get(struct device *dev, 156 + enum platform_profile_option *profile) 157 + { 158 + struct lwmi_gz_priv *priv = dev_get_drvdata(dev); 159 + enum thermal_mode mode; 160 + int ret; 161 + 162 + ret = lwmi_gz_thermal_mode_get(priv->wdev, &mode); 163 + if (ret) 164 + return ret; 165 + 166 + switch (mode) { 167 + case LWMI_GZ_THERMAL_MODE_QUIET: 168 + *profile = PLATFORM_PROFILE_LOW_POWER; 169 + break; 170 + case LWMI_GZ_THERMAL_MODE_BALANCED: 171 + *profile = PLATFORM_PROFILE_BALANCED; 172 + break; 173 + case LWMI_GZ_THERMAL_MODE_PERFORMANCE: 174 + if (priv->extreme_supported) { 175 + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; 176 + break; 177 + } 178 + *profile = PLATFORM_PROFILE_PERFORMANCE; 179 + break; 180 + case LWMI_GZ_THERMAL_MODE_EXTREME: 181 + *profile = PLATFORM_PROFILE_PERFORMANCE; 182 + break; 183 + case LWMI_GZ_THERMAL_MODE_CUSTOM: 184 + *profile = PLATFORM_PROFILE_CUSTOM; 185 + break; 186 + default: 187 + return -EINVAL; 188 + } 189 + 190 + guard(spinlock)(&priv->gz_mode_lock); 191 + priv->current_mode = mode; 192 + 193 + return 0; 194 + } 195 + 196 + /** 197 + * lwmi_gz_profile_set() - Set the current platform profile. 198 + * @dev: The Gamezone interface parent device. 199 + * @profile: Pointer to the desired platform profile. 200 + * 201 + * Convert the given platform profile into a thermal mode based on the support 202 + * level of the interface, then call the WMI method to set the thermal mode. 203 + * 204 + * Return: 0 on success, or an error code. 205 + */ 206 + static int lwmi_gz_profile_set(struct device *dev, 207 + enum platform_profile_option profile) 208 + { 209 + struct lwmi_gz_priv *priv = dev_get_drvdata(dev); 210 + struct wmi_method_args_32 args; 211 + enum thermal_mode mode; 212 + int ret; 213 + 214 + switch (profile) { 215 + case PLATFORM_PROFILE_LOW_POWER: 216 + mode = LWMI_GZ_THERMAL_MODE_QUIET; 217 + break; 218 + case PLATFORM_PROFILE_BALANCED: 219 + mode = LWMI_GZ_THERMAL_MODE_BALANCED; 220 + break; 221 + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: 222 + mode = LWMI_GZ_THERMAL_MODE_PERFORMANCE; 223 + break; 224 + case PLATFORM_PROFILE_PERFORMANCE: 225 + if (priv->extreme_supported) { 226 + mode = LWMI_GZ_THERMAL_MODE_EXTREME; 227 + break; 228 + } 229 + mode = LWMI_GZ_THERMAL_MODE_PERFORMANCE; 230 + break; 231 + case PLATFORM_PROFILE_CUSTOM: 232 + mode = LWMI_GZ_THERMAL_MODE_CUSTOM; 233 + break; 234 + default: 235 + return -EOPNOTSUPP; 236 + } 237 + 238 + args.arg0 = mode; 239 + 240 + ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, 241 + LWMI_GZ_METHOD_ID_SMARTFAN_SET, 242 + (u8 *)&args, sizeof(args), NULL); 243 + if (ret) 244 + return ret; 245 + 246 + guard(spinlock)(&priv->gz_mode_lock); 247 + priv->current_mode = mode; 248 + 249 + return 0; 250 + } 251 + 252 + static const struct dmi_system_id fwbug_list[] = { 253 + { 254 + .ident = "Legion Go 8APU1", 255 + .matches = { 256 + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 257 + DMI_MATCH(DMI_PRODUCT_VERSION, "Legion Go 8APU1"), 258 + }, 259 + .driver_data = &quirk_no_extreme_bug, 260 + }, 261 + { 262 + .ident = "Legion Go S 8APU1", 263 + .matches = { 264 + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 265 + DMI_MATCH(DMI_PRODUCT_VERSION, "Legion Go S 8APU1"), 266 + }, 267 + .driver_data = &quirk_no_extreme_bug, 268 + }, 269 + { 270 + .ident = "Legion Go S 8ARP1", 271 + .matches = { 272 + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 273 + DMI_MATCH(DMI_PRODUCT_VERSION, "Legion Go S 8ARP1"), 274 + }, 275 + .driver_data = &quirk_no_extreme_bug, 276 + }, 277 + {}, 278 + 279 + }; 280 + 281 + /** 282 + * lwmi_gz_extreme_supported() - Evaluate if a device supports extreme thermal mode. 283 + * @profile_support_ver: Version of the WMI interface. 284 + * 285 + * Determine if the extreme thermal mode is supported by the hardware. 286 + * Anything version 5 or lower does not. For devices with a version 6 or 287 + * greater do a DMI check, as some devices report a version that supports 288 + * extreme mode but have an incomplete entry in the BIOS. To ensure this 289 + * cannot be set, quirk them to prevent assignment. 290 + * 291 + * Return: bool. 292 + */ 293 + static bool lwmi_gz_extreme_supported(int profile_support_ver) 294 + { 295 + const struct dmi_system_id *dmi_id; 296 + struct quirk_entry *quirks; 297 + 298 + if (profile_support_ver < 6) 299 + return false; 300 + 301 + dmi_id = dmi_first_match(fwbug_list); 302 + if (!dmi_id) 303 + return true; 304 + 305 + quirks = dmi_id->driver_data; 306 + 307 + return quirks->extreme_supported; 308 + } 309 + 310 + /** 311 + * lwmi_gz_platform_profile_probe - Enable and set up the platform profile 312 + * device. 313 + * @drvdata: Driver data for the interface. 314 + * @choices: Container for enabled platform profiles. 315 + * 316 + * Determine if thermal mode is supported, and if so to what feature level. 317 + * Then enable all supported platform profiles. 318 + * 319 + * Return: 0 on success, or an error code. 320 + */ 321 + static int lwmi_gz_platform_profile_probe(void *drvdata, unsigned long *choices) 322 + { 323 + struct lwmi_gz_priv *priv = drvdata; 324 + int profile_support_ver; 325 + int ret; 326 + 327 + ret = lwmi_gz_thermal_mode_supported(priv->wdev, &profile_support_ver); 328 + if (ret) 329 + return ret; 330 + 331 + if (profile_support_ver < 1) 332 + return -ENODEV; 333 + 334 + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); 335 + set_bit(PLATFORM_PROFILE_BALANCED, choices); 336 + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); 337 + set_bit(PLATFORM_PROFILE_CUSTOM, choices); 338 + 339 + priv->extreme_supported = lwmi_gz_extreme_supported(profile_support_ver); 340 + if (priv->extreme_supported) 341 + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices); 342 + 343 + return 0; 344 + } 345 + 346 + static const struct platform_profile_ops lwmi_gz_platform_profile_ops = { 347 + .probe = lwmi_gz_platform_profile_probe, 348 + .profile_get = lwmi_gz_profile_get, 349 + .profile_set = lwmi_gz_profile_set, 350 + }; 351 + 352 + static int lwmi_gz_probe(struct wmi_device *wdev, const void *context) 353 + { 354 + struct lwmi_gz_priv *priv; 355 + int ret; 356 + 357 + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); 358 + if (!priv) 359 + return -ENOMEM; 360 + 361 + priv->wdev = wdev; 362 + dev_set_drvdata(&wdev->dev, priv); 363 + 364 + priv->ppdev = devm_platform_profile_register(&wdev->dev, "lenovo-wmi-gamezone", 365 + priv, &lwmi_gz_platform_profile_ops); 366 + if (IS_ERR(priv->ppdev)) 367 + return -ENODEV; 368 + 369 + spin_lock_init(&priv->gz_mode_lock); 370 + 371 + ret = lwmi_gz_thermal_mode_get(wdev, &priv->current_mode); 372 + if (ret) 373 + return ret; 374 + 375 + priv->event_nb.notifier_call = lwmi_gz_event_call; 376 + ret = devm_lwmi_events_register_notifier(&wdev->dev, &priv->event_nb); 377 + if (ret) 378 + return ret; 379 + 380 + priv->mode_nb.notifier_call = lwmi_gz_mode_call; 381 + return devm_lwmi_om_register_notifier(&wdev->dev, &priv->mode_nb); 382 + } 383 + 384 + static const struct wmi_device_id lwmi_gz_id_table[] = { 385 + { LENOVO_GAMEZONE_GUID, NULL }, 386 + {} 387 + }; 388 + 389 + static struct wmi_driver lwmi_gz_driver = { 390 + .driver = { 391 + .name = "lenovo_wmi_gamezone", 392 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 393 + }, 394 + .id_table = lwmi_gz_id_table, 395 + .probe = lwmi_gz_probe, 396 + .no_singleton = true, 397 + }; 398 + 399 + module_wmi_driver(lwmi_gz_driver); 400 + 401 + MODULE_IMPORT_NS("LENOVO_WMI_EVENTS"); 402 + MODULE_IMPORT_NS("LENOVO_WMI_HELPERS"); 403 + MODULE_IMPORT_NS("LENOVO_WMI_OTHER"); 404 + MODULE_DEVICE_TABLE(wmi, lwmi_gz_id_table); 405 + MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>"); 406 + MODULE_DESCRIPTION("Lenovo GameZone WMI Driver"); 407 + MODULE_LICENSE("GPL");
+20
drivers/platform/x86/lenovo/wmi-gamezone.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + /* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */ 4 + 5 + #ifndef _LENOVO_WMI_GAMEZONE_H_ 6 + #define _LENOVO_WMI_GAMEZONE_H_ 7 + 8 + enum gamezone_events_type { 9 + LWMI_GZ_GET_THERMAL_MODE = 1, 10 + }; 11 + 12 + enum thermal_mode { 13 + LWMI_GZ_THERMAL_MODE_QUIET = 0x01, 14 + LWMI_GZ_THERMAL_MODE_BALANCED = 0x02, 15 + LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03, 16 + LWMI_GZ_THERMAL_MODE_EXTREME = 0xE0, /* Ver 6+ */ 17 + LWMI_GZ_THERMAL_MODE_CUSTOM = 0xFF, 18 + }; 19 + 20 + #endif /* !_LENOVO_WMI_GAMEZONE_H_ */
+74
drivers/platform/x86/lenovo/wmi-helpers.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Lenovo Legion WMI helpers driver. 4 + * 5 + * The Lenovo Legion WMI interface is broken up into multiple GUID interfaces 6 + * that require cross-references between GUID's for some functionality. The 7 + * "Custom Mode" interface is a legacy interface for managing and displaying 8 + * CPU & GPU power and hwmon settings and readings. The "Other Mode" interface 9 + * is a modern interface that replaces or extends the "Custom Mode" interface 10 + * methods. The "Gamezone" interface adds advanced features such as fan 11 + * profiles and overclocking. The "Lighting" interface adds control of various 12 + * status lights related to different hardware components. Each of these 13 + * drivers uses a common procedure to get data from the WMI interface, 14 + * enumerated here. 15 + * 16 + * Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> 17 + */ 18 + 19 + #include <linux/acpi.h> 20 + #include <linux/cleanup.h> 21 + #include <linux/errno.h> 22 + #include <linux/export.h> 23 + #include <linux/module.h> 24 + #include <linux/wmi.h> 25 + 26 + #include "wmi-helpers.h" 27 + 28 + /** 29 + * lwmi_dev_evaluate_int() - Helper function for calling WMI methods that 30 + * return an integer. 31 + * @wdev: Pointer to the WMI device to be called. 32 + * @instance: Instance of the called method. 33 + * @method_id: WMI Method ID for the method to be called. 34 + * @buf: Buffer of all arguments for the given method_id. 35 + * @size: Length of the buffer. 36 + * @retval: Pointer for the return value to be assigned. 37 + * 38 + * Calls wmidev_evaluate_method for Lenovo WMI devices that return an ACPI 39 + * integer. Validates the return value type and assigns the value to the 40 + * retval pointer. 41 + * 42 + * Return: 0 on success, or an error code. 43 + */ 44 + int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id, 45 + unsigned char *buf, size_t size, u32 *retval) 46 + { 47 + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 48 + union acpi_object *ret_obj __free(kfree) = NULL; 49 + struct acpi_buffer input = { size, buf }; 50 + acpi_status status; 51 + 52 + status = wmidev_evaluate_method(wdev, instance, method_id, &input, 53 + &output); 54 + if (ACPI_FAILURE(status)) 55 + return -EIO; 56 + 57 + if (retval) { 58 + ret_obj = output.pointer; 59 + if (!ret_obj) 60 + return -ENODATA; 61 + 62 + if (ret_obj->type != ACPI_TYPE_INTEGER) 63 + return -ENXIO; 64 + 65 + *retval = (u32)ret_obj->integer.value; 66 + } 67 + 68 + return 0; 69 + }; 70 + EXPORT_SYMBOL_NS_GPL(lwmi_dev_evaluate_int, "LENOVO_WMI_HELPERS"); 71 + 72 + MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>"); 73 + MODULE_DESCRIPTION("Lenovo WMI Helpers Driver"); 74 + MODULE_LICENSE("GPL");
+20
drivers/platform/x86/lenovo/wmi-helpers.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + /* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */ 4 + 5 + #ifndef _LENOVO_WMI_HELPERS_H_ 6 + #define _LENOVO_WMI_HELPERS_H_ 7 + 8 + #include <linux/types.h> 9 + 10 + struct wmi_device; 11 + 12 + struct wmi_method_args_32 { 13 + u32 arg0; 14 + u32 arg1; 15 + }; 16 + 17 + int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id, 18 + unsigned char *buf, size_t size, u32 *retval); 19 + 20 + #endif /* !_LENOVO_WMI_HELPERS_H_ */
+665
drivers/platform/x86/lenovo/wmi-other.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Lenovo Other Mode WMI interface driver. 4 + * 5 + * This driver uses the fw_attributes class to expose the various WMI functions 6 + * provided by the "Other Mode" WMI interface. This enables CPU and GPU power 7 + * limit as well as various other attributes for devices that fall under the 8 + * "Gaming Series" of Lenovo laptop devices. Each attribute exposed by the 9 + * "Other Mode" interface has a corresponding Capability Data struct that 10 + * allows the driver to probe details about the attribute such as if it is 11 + * supported by the hardware, the default_value, max_value, min_value, and step 12 + * increment. 13 + * 14 + * These attributes typically don't fit anywhere else in the sysfs and are set 15 + * in Windows using one of Lenovo's multiple user applications. 16 + * 17 + * Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> 18 + */ 19 + 20 + #include <linux/acpi.h> 21 + #include <linux/bitfield.h> 22 + #include <linux/cleanup.h> 23 + #include <linux/component.h> 24 + #include <linux/container_of.h> 25 + #include <linux/device.h> 26 + #include <linux/export.h> 27 + #include <linux/gfp_types.h> 28 + #include <linux/idr.h> 29 + #include <linux/kdev_t.h> 30 + #include <linux/kobject.h> 31 + #include <linux/module.h> 32 + #include <linux/notifier.h> 33 + #include <linux/platform_profile.h> 34 + #include <linux/types.h> 35 + #include <linux/wmi.h> 36 + 37 + #include "wmi-capdata01.h" 38 + #include "wmi-events.h" 39 + #include "wmi-gamezone.h" 40 + #include "wmi-helpers.h" 41 + #include "wmi-other.h" 42 + #include "../firmware_attributes_class.h" 43 + 44 + #define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B" 45 + 46 + #define LWMI_DEVICE_ID_CPU 0x01 47 + 48 + #define LWMI_FEATURE_ID_CPU_SPPT 0x01 49 + #define LWMI_FEATURE_ID_CPU_SPL 0x02 50 + #define LWMI_FEATURE_ID_CPU_FPPT 0x03 51 + 52 + #define LWMI_TYPE_ID_NONE 0x00 53 + 54 + #define LWMI_FEATURE_VALUE_GET 17 55 + #define LWMI_FEATURE_VALUE_SET 18 56 + 57 + #define LWMI_ATTR_DEV_ID_MASK GENMASK(31, 24) 58 + #define LWMI_ATTR_FEAT_ID_MASK GENMASK(23, 16) 59 + #define LWMI_ATTR_MODE_ID_MASK GENMASK(15, 8) 60 + #define LWMI_ATTR_TYPE_ID_MASK GENMASK(7, 0) 61 + 62 + #define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other" 63 + 64 + static BLOCKING_NOTIFIER_HEAD(om_chain_head); 65 + static DEFINE_IDA(lwmi_om_ida); 66 + 67 + enum attribute_property { 68 + DEFAULT_VAL, 69 + MAX_VAL, 70 + MIN_VAL, 71 + STEP_VAL, 72 + SUPPORTED, 73 + }; 74 + 75 + struct lwmi_om_priv { 76 + struct component_master_ops *ops; 77 + struct cd01_list *cd01_list; /* only valid after capdata01 bind */ 78 + struct device *fw_attr_dev; 79 + struct kset *fw_attr_kset; 80 + struct notifier_block nb; 81 + struct wmi_device *wdev; 82 + int ida_id; 83 + }; 84 + 85 + struct tunable_attr_01 { 86 + struct capdata01 *capdata; 87 + struct device *dev; 88 + u32 feature_id; 89 + u32 device_id; 90 + u32 type_id; 91 + }; 92 + 93 + static struct tunable_attr_01 ppt_pl1_spl = { 94 + .device_id = LWMI_DEVICE_ID_CPU, 95 + .feature_id = LWMI_FEATURE_ID_CPU_SPL, 96 + .type_id = LWMI_TYPE_ID_NONE, 97 + }; 98 + 99 + static struct tunable_attr_01 ppt_pl2_sppt = { 100 + .device_id = LWMI_DEVICE_ID_CPU, 101 + .feature_id = LWMI_FEATURE_ID_CPU_SPPT, 102 + .type_id = LWMI_TYPE_ID_NONE, 103 + }; 104 + 105 + static struct tunable_attr_01 ppt_pl3_fppt = { 106 + .device_id = LWMI_DEVICE_ID_CPU, 107 + .feature_id = LWMI_FEATURE_ID_CPU_FPPT, 108 + .type_id = LWMI_TYPE_ID_NONE, 109 + }; 110 + 111 + struct capdata01_attr_group { 112 + const struct attribute_group *attr_group; 113 + struct tunable_attr_01 *tunable_attr; 114 + }; 115 + 116 + /** 117 + * lwmi_om_register_notifier() - Add a notifier to the blocking notifier chain 118 + * @nb: The notifier_block struct to register 119 + * 120 + * Call blocking_notifier_chain_register to register the notifier block to the 121 + * lenovo-wmi-other driver notifier chain. 122 + * 123 + * Return: 0 on success, %-EEXIST on error. 124 + */ 125 + int lwmi_om_register_notifier(struct notifier_block *nb) 126 + { 127 + return blocking_notifier_chain_register(&om_chain_head, nb); 128 + } 129 + EXPORT_SYMBOL_NS_GPL(lwmi_om_register_notifier, "LENOVO_WMI_OTHER"); 130 + 131 + /** 132 + * lwmi_om_unregister_notifier() - Remove a notifier from the blocking notifier 133 + * chain. 134 + * @nb: The notifier_block struct to register 135 + * 136 + * Call blocking_notifier_chain_unregister to unregister the notifier block from the 137 + * lenovo-wmi-other driver notifier chain. 138 + * 139 + * Return: 0 on success, %-ENOENT on error. 140 + */ 141 + int lwmi_om_unregister_notifier(struct notifier_block *nb) 142 + { 143 + return blocking_notifier_chain_unregister(&om_chain_head, nb); 144 + } 145 + EXPORT_SYMBOL_NS_GPL(lwmi_om_unregister_notifier, "LENOVO_WMI_OTHER"); 146 + 147 + /** 148 + * devm_lwmi_om_unregister_notifier() - Remove a notifier from the blocking 149 + * notifier chain. 150 + * @data: Void pointer to the notifier_block struct to register. 151 + * 152 + * Call lwmi_om_unregister_notifier to unregister the notifier block from the 153 + * lenovo-wmi-other driver notifier chain. 154 + * 155 + * Return: 0 on success, %-ENOENT on error. 156 + */ 157 + static void devm_lwmi_om_unregister_notifier(void *data) 158 + { 159 + struct notifier_block *nb = data; 160 + 161 + lwmi_om_unregister_notifier(nb); 162 + } 163 + 164 + /** 165 + * devm_lwmi_om_register_notifier() - Add a notifier to the blocking notifier 166 + * chain. 167 + * @dev: The parent device of the notifier_block struct. 168 + * @nb: The notifier_block struct to register 169 + * 170 + * Call lwmi_om_register_notifier to register the notifier block to the 171 + * lenovo-wmi-other driver notifier chain. Then add devm_lwmi_om_unregister_notifier 172 + * as a device managed action to automatically unregister the notifier block 173 + * upon parent device removal. 174 + * 175 + * Return: 0 on success, or an error code. 176 + */ 177 + int devm_lwmi_om_register_notifier(struct device *dev, 178 + struct notifier_block *nb) 179 + { 180 + int ret; 181 + 182 + ret = lwmi_om_register_notifier(nb); 183 + if (ret < 0) 184 + return ret; 185 + 186 + return devm_add_action_or_reset(dev, devm_lwmi_om_unregister_notifier, 187 + nb); 188 + } 189 + EXPORT_SYMBOL_NS_GPL(devm_lwmi_om_register_notifier, "LENOVO_WMI_OTHER"); 190 + 191 + /** 192 + * lwmi_om_notifier_call() - Call functions for the notifier call chain. 193 + * @mode: Pointer to a thermal mode enum to retrieve the data from. 194 + * 195 + * Call blocking_notifier_call_chain to retrieve the thermal mode from the 196 + * lenovo-wmi-gamezone driver. 197 + * 198 + * Return: 0 on success, or an error code. 199 + */ 200 + static int lwmi_om_notifier_call(enum thermal_mode *mode) 201 + { 202 + int ret; 203 + 204 + ret = blocking_notifier_call_chain(&om_chain_head, 205 + LWMI_GZ_GET_THERMAL_MODE, &mode); 206 + if ((ret & ~NOTIFY_STOP_MASK) != NOTIFY_OK) 207 + return -EINVAL; 208 + 209 + return 0; 210 + } 211 + 212 + /* Attribute Methods */ 213 + 214 + /** 215 + * int_type_show() - Emit the data type for an integer attribute 216 + * @kobj: Pointer to the driver object. 217 + * @kattr: Pointer to the attribute calling this function. 218 + * @buf: The buffer to write to. 219 + * 220 + * Return: Number of characters written to buf. 221 + */ 222 + static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *kattr, 223 + char *buf) 224 + { 225 + return sysfs_emit(buf, "integer\n"); 226 + } 227 + 228 + /** 229 + * attr_capdata01_show() - Get the value of the specified attribute property 230 + * 231 + * @kobj: Pointer to the driver object. 232 + * @kattr: Pointer to the attribute calling this function. 233 + * @buf: The buffer to write to. 234 + * @tunable_attr: The attribute to be read. 235 + * @prop: The property of this attribute to be read. 236 + * 237 + * Retrieves the given property from the capability data 01 struct for the 238 + * specified attribute's "custom" thermal mode. This function is intended 239 + * to be generic so it can be called from any integer attributes "_show" 240 + * function. 241 + * 242 + * If the WMI is success the sysfs attribute is notified. 243 + * 244 + * Return: Either number of characters written to buf, or an error code. 245 + */ 246 + static ssize_t attr_capdata01_show(struct kobject *kobj, 247 + struct kobj_attribute *kattr, char *buf, 248 + struct tunable_attr_01 *tunable_attr, 249 + enum attribute_property prop) 250 + { 251 + struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev); 252 + struct capdata01 capdata; 253 + u32 attribute_id; 254 + int value, ret; 255 + 256 + attribute_id = 257 + FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) | 258 + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | 259 + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, 260 + LWMI_GZ_THERMAL_MODE_CUSTOM) | 261 + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id); 262 + 263 + ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata); 264 + if (ret) 265 + return ret; 266 + 267 + switch (prop) { 268 + case DEFAULT_VAL: 269 + value = capdata.default_value; 270 + break; 271 + case MAX_VAL: 272 + value = capdata.max_value; 273 + break; 274 + case MIN_VAL: 275 + value = capdata.min_value; 276 + break; 277 + case STEP_VAL: 278 + value = capdata.step; 279 + break; 280 + default: 281 + return -EINVAL; 282 + } 283 + 284 + return sysfs_emit(buf, "%d\n", value); 285 + } 286 + 287 + /** 288 + * attr_current_value_store() - Set the current value of the given attribute 289 + * @kobj: Pointer to the driver object. 290 + * @kattr: Pointer to the attribute calling this function. 291 + * @buf: The buffer to read from, this is parsed to `int` type. 292 + * @count: Required by sysfs attribute macros, pass in from the callee attr. 293 + * @tunable_attr: The attribute to be stored. 294 + * 295 + * Sets the value of the given attribute when operating under the "custom" 296 + * smartfan profile. The current smartfan profile is retrieved from the 297 + * lenovo-wmi-gamezone driver and error is returned if the result is not 298 + * "custom". This function is intended to be generic so it can be called from 299 + * any integer attribute's "_store" function. The integer to be sent to the WMI 300 + * method is range checked and an error code is returned if out of range. 301 + * 302 + * If the value is valid and WMI is success, then the sysfs attribute is 303 + * notified. 304 + * 305 + * Return: Either count, or an error code. 306 + */ 307 + static ssize_t attr_current_value_store(struct kobject *kobj, 308 + struct kobj_attribute *kattr, 309 + const char *buf, size_t count, 310 + struct tunable_attr_01 *tunable_attr) 311 + { 312 + struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev); 313 + struct wmi_method_args_32 args; 314 + struct capdata01 capdata; 315 + enum thermal_mode mode; 316 + u32 attribute_id; 317 + u32 value; 318 + int ret; 319 + 320 + ret = lwmi_om_notifier_call(&mode); 321 + if (ret) 322 + return ret; 323 + 324 + if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM) 325 + return -EBUSY; 326 + 327 + attribute_id = 328 + FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) | 329 + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | 330 + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) | 331 + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id); 332 + 333 + ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata); 334 + if (ret) 335 + return ret; 336 + 337 + ret = kstrtouint(buf, 10, &value); 338 + if (ret) 339 + return ret; 340 + 341 + if (value < capdata.min_value || value > capdata.max_value) 342 + return -EINVAL; 343 + 344 + args.arg0 = attribute_id; 345 + args.arg1 = value; 346 + 347 + ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET, 348 + (unsigned char *)&args, sizeof(args), NULL); 349 + if (ret) 350 + return ret; 351 + 352 + return count; 353 + }; 354 + 355 + /** 356 + * attr_current_value_show() - Get the current value of the given attribute 357 + * @kobj: Pointer to the driver object. 358 + * @kattr: Pointer to the attribute calling this function. 359 + * @buf: The buffer to write to. 360 + * @tunable_attr: The attribute to be read. 361 + * 362 + * Retrieves the value of the given attribute for the current smartfan profile. 363 + * The current smartfan profile is retrieved from the lenovo-wmi-gamezone driver. 364 + * This function is intended to be generic so it can be called from any integer 365 + * attribute's "_show" function. 366 + * 367 + * If the WMI is success the sysfs attribute is notified. 368 + * 369 + * Return: Either number of characters written to buf, or an error code. 370 + */ 371 + static ssize_t attr_current_value_show(struct kobject *kobj, 372 + struct kobj_attribute *kattr, char *buf, 373 + struct tunable_attr_01 *tunable_attr) 374 + { 375 + struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev); 376 + struct wmi_method_args_32 args; 377 + enum thermal_mode mode; 378 + u32 attribute_id; 379 + int retval; 380 + int ret; 381 + 382 + ret = lwmi_om_notifier_call(&mode); 383 + if (ret) 384 + return ret; 385 + 386 + attribute_id = 387 + FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) | 388 + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | 389 + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) | 390 + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id); 391 + 392 + args.arg0 = attribute_id; 393 + 394 + ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET, 395 + (unsigned char *)&args, sizeof(args), 396 + &retval); 397 + if (ret) 398 + return ret; 399 + 400 + return sysfs_emit(buf, "%d\n", retval); 401 + } 402 + 403 + /* Lenovo WMI Other Mode Attribute macros */ 404 + #define __LWMI_ATTR_RO(_func, _name) \ 405 + { \ 406 + .attr = { .name = __stringify(_name), .mode = 0444 }, \ 407 + .show = _func##_##_name##_show, \ 408 + } 409 + 410 + #define __LWMI_ATTR_RO_AS(_name, _show) \ 411 + { \ 412 + .attr = { .name = __stringify(_name), .mode = 0444 }, \ 413 + .show = _show, \ 414 + } 415 + 416 + #define __LWMI_ATTR_RW(_func, _name) \ 417 + __ATTR(_name, 0644, _func##_##_name##_show, _func##_##_name##_store) 418 + 419 + /* Shows a formatted static variable */ 420 + #define __LWMI_ATTR_SHOW_FMT(_prop, _attrname, _fmt, _val) \ 421 + static ssize_t _attrname##_##_prop##_show( \ 422 + struct kobject *kobj, struct kobj_attribute *kattr, char *buf) \ 423 + { \ 424 + return sysfs_emit(buf, _fmt, _val); \ 425 + } \ 426 + static struct kobj_attribute attr_##_attrname##_##_prop = \ 427 + __LWMI_ATTR_RO(_attrname, _prop) 428 + 429 + /* Attribute current value read/write */ 430 + #define __LWMI_TUNABLE_CURRENT_VALUE_CAP01(_attrname) \ 431 + static ssize_t _attrname##_current_value_store( \ 432 + struct kobject *kobj, struct kobj_attribute *kattr, \ 433 + const char *buf, size_t count) \ 434 + { \ 435 + return attr_current_value_store(kobj, kattr, buf, count, \ 436 + &_attrname); \ 437 + } \ 438 + static ssize_t _attrname##_current_value_show( \ 439 + struct kobject *kobj, struct kobj_attribute *kattr, char *buf) \ 440 + { \ 441 + return attr_current_value_show(kobj, kattr, buf, &_attrname); \ 442 + } \ 443 + static struct kobj_attribute attr_##_attrname##_current_value = \ 444 + __LWMI_ATTR_RW(_attrname, current_value) 445 + 446 + /* Attribute property read only */ 447 + #define __LWMI_TUNABLE_RO_CAP01(_prop, _attrname, _prop_type) \ 448 + static ssize_t _attrname##_##_prop##_show( \ 449 + struct kobject *kobj, struct kobj_attribute *kattr, char *buf) \ 450 + { \ 451 + return attr_capdata01_show(kobj, kattr, buf, &_attrname, \ 452 + _prop_type); \ 453 + } \ 454 + static struct kobj_attribute attr_##_attrname##_##_prop = \ 455 + __LWMI_ATTR_RO(_attrname, _prop) 456 + 457 + #define LWMI_ATTR_GROUP_TUNABLE_CAP01(_attrname, _fsname, _dispname) \ 458 + __LWMI_TUNABLE_CURRENT_VALUE_CAP01(_attrname); \ 459 + __LWMI_TUNABLE_RO_CAP01(default_value, _attrname, DEFAULT_VAL); \ 460 + __LWMI_ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \ 461 + __LWMI_TUNABLE_RO_CAP01(max_value, _attrname, MAX_VAL); \ 462 + __LWMI_TUNABLE_RO_CAP01(min_value, _attrname, MIN_VAL); \ 463 + __LWMI_TUNABLE_RO_CAP01(scalar_increment, _attrname, STEP_VAL); \ 464 + static struct kobj_attribute attr_##_attrname##_type = \ 465 + __LWMI_ATTR_RO_AS(type, int_type_show); \ 466 + static struct attribute *_attrname##_attrs[] = { \ 467 + &attr_##_attrname##_current_value.attr, \ 468 + &attr_##_attrname##_default_value.attr, \ 469 + &attr_##_attrname##_display_name.attr, \ 470 + &attr_##_attrname##_max_value.attr, \ 471 + &attr_##_attrname##_min_value.attr, \ 472 + &attr_##_attrname##_scalar_increment.attr, \ 473 + &attr_##_attrname##_type.attr, \ 474 + NULL, \ 475 + }; \ 476 + static const struct attribute_group _attrname##_attr_group = { \ 477 + .name = _fsname, .attrs = _attrname##_attrs \ 478 + } 479 + 480 + LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl, "ppt_pl1_spl", 481 + "Set the CPU sustained power limit"); 482 + LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt, "ppt_pl2_sppt", 483 + "Set the CPU slow package power tracking limit"); 484 + LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt, "ppt_pl3_fppt", 485 + "Set the CPU fast package power tracking limit"); 486 + 487 + static struct capdata01_attr_group cd01_attr_groups[] = { 488 + { &ppt_pl1_spl_attr_group, &ppt_pl1_spl }, 489 + { &ppt_pl2_sppt_attr_group, &ppt_pl2_sppt }, 490 + { &ppt_pl3_fppt_attr_group, &ppt_pl3_fppt }, 491 + {}, 492 + }; 493 + 494 + /** 495 + * lwmi_om_fw_attr_add() - Register all firmware_attributes_class members 496 + * @priv: The Other Mode driver data. 497 + * 498 + * Return: Either 0, or an error code. 499 + */ 500 + static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv) 501 + { 502 + unsigned int i; 503 + int err; 504 + 505 + priv->ida_id = ida_alloc(&lwmi_om_ida, GFP_KERNEL); 506 + if (priv->ida_id < 0) 507 + return priv->ida_id; 508 + 509 + priv->fw_attr_dev = device_create(&firmware_attributes_class, NULL, 510 + MKDEV(0, 0), NULL, "%s-%u", 511 + LWMI_OM_FW_ATTR_BASE_PATH, 512 + priv->ida_id); 513 + if (IS_ERR(priv->fw_attr_dev)) { 514 + err = PTR_ERR(priv->fw_attr_dev); 515 + goto err_free_ida; 516 + } 517 + 518 + priv->fw_attr_kset = kset_create_and_add("attributes", NULL, 519 + &priv->fw_attr_dev->kobj); 520 + if (!priv->fw_attr_kset) { 521 + err = -ENOMEM; 522 + goto err_destroy_classdev; 523 + } 524 + 525 + for (i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) { 526 + err = sysfs_create_group(&priv->fw_attr_kset->kobj, 527 + cd01_attr_groups[i].attr_group); 528 + if (err) 529 + goto err_remove_groups; 530 + 531 + cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev; 532 + } 533 + return 0; 534 + 535 + err_remove_groups: 536 + while (i--) 537 + sysfs_remove_group(&priv->fw_attr_kset->kobj, 538 + cd01_attr_groups[i].attr_group); 539 + 540 + kset_unregister(priv->fw_attr_kset); 541 + 542 + err_destroy_classdev: 543 + device_unregister(priv->fw_attr_dev); 544 + 545 + err_free_ida: 546 + ida_free(&lwmi_om_ida, priv->ida_id); 547 + return err; 548 + } 549 + 550 + /** 551 + * lwmi_om_fw_attr_remove() - Unregister all capability data attribute groups 552 + * @priv: the lenovo-wmi-other driver data. 553 + */ 554 + static void lwmi_om_fw_attr_remove(struct lwmi_om_priv *priv) 555 + { 556 + for (unsigned int i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) 557 + sysfs_remove_group(&priv->fw_attr_kset->kobj, 558 + cd01_attr_groups[i].attr_group); 559 + 560 + kset_unregister(priv->fw_attr_kset); 561 + device_unregister(priv->fw_attr_dev); 562 + } 563 + 564 + /** 565 + * lwmi_om_master_bind() - Bind all components of the other mode driver 566 + * @dev: The lenovo-wmi-other driver basic device. 567 + * 568 + * Call component_bind_all to bind the lenovo-wmi-capdata01 driver to the 569 + * lenovo-wmi-other master driver. On success, assign the capability data 01 570 + * list pointer to the driver data struct for later access. This pointer 571 + * is only valid while the capdata01 interface exists. Finally, register all 572 + * firmware attribute groups. 573 + * 574 + * Return: 0 on success, or an error code. 575 + */ 576 + static int lwmi_om_master_bind(struct device *dev) 577 + { 578 + struct lwmi_om_priv *priv = dev_get_drvdata(dev); 579 + struct cd01_list *tmp_list; 580 + int ret; 581 + 582 + ret = component_bind_all(dev, &tmp_list); 583 + if (ret) 584 + return ret; 585 + 586 + priv->cd01_list = tmp_list; 587 + if (!priv->cd01_list) 588 + return -ENODEV; 589 + 590 + return lwmi_om_fw_attr_add(priv); 591 + } 592 + 593 + /** 594 + * lwmi_om_master_unbind() - Unbind all components of the other mode driver 595 + * @dev: The lenovo-wmi-other driver basic device 596 + * 597 + * Unregister all capability data attribute groups. Then call 598 + * component_unbind_all to unbind the lenovo-wmi-capdata01 driver from the 599 + * lenovo-wmi-other master driver. Finally, free the IDA for this device. 600 + */ 601 + static void lwmi_om_master_unbind(struct device *dev) 602 + { 603 + struct lwmi_om_priv *priv = dev_get_drvdata(dev); 604 + 605 + lwmi_om_fw_attr_remove(priv); 606 + component_unbind_all(dev, NULL); 607 + } 608 + 609 + static const struct component_master_ops lwmi_om_master_ops = { 610 + .bind = lwmi_om_master_bind, 611 + .unbind = lwmi_om_master_unbind, 612 + }; 613 + 614 + static int lwmi_other_probe(struct wmi_device *wdev, const void *context) 615 + { 616 + struct component_match *master_match = NULL; 617 + struct lwmi_om_priv *priv; 618 + 619 + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); 620 + if (!priv) 621 + return -ENOMEM; 622 + 623 + priv->wdev = wdev; 624 + dev_set_drvdata(&wdev->dev, priv); 625 + 626 + component_match_add(&wdev->dev, &master_match, lwmi_cd01_match, NULL); 627 + if (IS_ERR(master_match)) 628 + return PTR_ERR(master_match); 629 + 630 + return component_master_add_with_match(&wdev->dev, &lwmi_om_master_ops, 631 + master_match); 632 + } 633 + 634 + static void lwmi_other_remove(struct wmi_device *wdev) 635 + { 636 + struct lwmi_om_priv *priv = dev_get_drvdata(&wdev->dev); 637 + 638 + component_master_del(&wdev->dev, &lwmi_om_master_ops); 639 + ida_free(&lwmi_om_ida, priv->ida_id); 640 + } 641 + 642 + static const struct wmi_device_id lwmi_other_id_table[] = { 643 + { LENOVO_OTHER_MODE_GUID, NULL }, 644 + {} 645 + }; 646 + 647 + static struct wmi_driver lwmi_other_driver = { 648 + .driver = { 649 + .name = "lenovo_wmi_other", 650 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 651 + }, 652 + .id_table = lwmi_other_id_table, 653 + .probe = lwmi_other_probe, 654 + .remove = lwmi_other_remove, 655 + .no_singleton = true, 656 + }; 657 + 658 + module_wmi_driver(lwmi_other_driver); 659 + 660 + MODULE_IMPORT_NS("LENOVO_WMI_CD01"); 661 + MODULE_IMPORT_NS("LENOVO_WMI_HELPERS"); 662 + MODULE_DEVICE_TABLE(wmi, lwmi_other_id_table); 663 + MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>"); 664 + MODULE_DESCRIPTION("Lenovo Other Mode WMI Driver"); 665 + MODULE_LICENSE("GPL");
+16
drivers/platform/x86/lenovo/wmi-other.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + /* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */ 4 + 5 + #ifndef _LENOVO_WMI_OTHER_H_ 6 + #define _LENOVO_WMI_OTHER_H_ 7 + 8 + struct device; 9 + struct notifier_block; 10 + 11 + int lwmi_om_register_notifier(struct notifier_block *nb); 12 + int lwmi_om_unregister_notifier(struct notifier_block *nb); 13 + int devm_lwmi_om_register_notifier(struct device *dev, 14 + struct notifier_block *nb); 15 + 16 + #endif /* !_LENOVO_WMI_OTHER_H_ */
+31 -13
drivers/platform/x86/oxpec.c
··· 58 58 oxp_mini_amd_a07, 59 59 oxp_mini_amd_pro, 60 60 oxp_x1, 61 - oxp_g1, 61 + oxp_g1_i, 62 + oxp_g1_a, 62 63 }; 63 64 64 65 static enum oxp_board board; ··· 248 247 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), 249 248 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER G1 A"), 250 249 }, 251 - .driver_data = (void *)oxp_g1, 250 + .driver_data = (void *)oxp_g1_a, 252 251 }, 253 252 { 254 253 .matches = { 255 254 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), 256 255 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER G1 i"), 257 256 }, 258 - .driver_data = (void *)oxp_g1, 257 + .driver_data = (void *)oxp_g1_i, 259 258 }, 260 259 { 261 260 .matches = { ··· 289 288 .matches = { 290 289 DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), 291 290 DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1 mini"), 291 + }, 292 + .driver_data = (void *)oxp_x1, 293 + }, 294 + { 295 + .matches = { 296 + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), 297 + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1Mini Pro"), 292 298 }, 293 299 .driver_data = (void *)oxp_x1, 294 300 }, ··· 360 352 case oxp_mini_amd_a07: 361 353 case oxp_mini_amd_pro: 362 354 case oxp_x1: 363 - case oxp_g1: 355 + case oxp_g1_i: 356 + case oxp_g1_a: 364 357 return attr->mode; 365 358 default: 366 359 break; ··· 390 381 case aok_zoe_a1: 391 382 case oxp_fly: 392 383 case oxp_mini_amd_pro: 384 + case oxp_g1_a: 393 385 reg = OXP_TURBO_SWITCH_REG; 394 386 mask = OXP_TURBO_TAKE_VAL; 395 387 break; 396 388 case oxp_2: 397 389 case oxp_x1: 398 - case oxp_g1: 390 + case oxp_g1_i: 399 391 reg = OXP_2_TURBO_SWITCH_REG; 400 392 mask = OXP_TURBO_TAKE_VAL; 401 393 break; ··· 436 426 case aok_zoe_a1: 437 427 case oxp_fly: 438 428 case oxp_mini_amd_pro: 429 + case oxp_g1_a: 439 430 reg = OXP_TURBO_SWITCH_REG; 440 431 mask = OXP_TURBO_TAKE_VAL; 441 432 break; 442 433 case oxp_2: 443 434 case oxp_x1: 444 - case oxp_g1: 435 + case oxp_g1_i: 445 436 reg = OXP_2_TURBO_SWITCH_REG; 446 437 mask = OXP_TURBO_TAKE_VAL; 447 438 break; ··· 531 520 { 532 521 switch (board) { 533 522 case oxp_x1: 534 - case oxp_g1: 523 + case oxp_g1_i: 524 + case oxp_g1_a: 535 525 case oxp_fly: 536 526 return true; 537 527 default: ··· 671 659 case oxp_mini_amd_a07: 672 660 case oxp_mini_amd_pro: 673 661 case oxp_x1: 674 - case oxp_g1: 662 + case oxp_g1_i: 663 + case oxp_g1_a: 675 664 return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL); 676 665 default: 677 666 return -EINVAL; ··· 699 686 case oxp_mini_amd_a07: 700 687 case oxp_mini_amd_pro: 701 688 case oxp_x1: 702 - case oxp_g1: 689 + case oxp_g1_i: 690 + case oxp_g1_a: 703 691 return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO); 704 692 default: 705 693 return -EINVAL; ··· 727 713 case oxp_mini_amd_a07: 728 714 case oxp_mini_amd_pro: 729 715 case oxp_x1: 730 - case oxp_g1: 716 + case oxp_g1_i: 717 + case oxp_g1_a: 731 718 return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val); 732 719 default: 733 720 return -EOPNOTSUPP; ··· 757 742 return read_from_ec(ORANGEPI_SENSOR_FAN_REG, 2, val); 758 743 case oxp_2: 759 744 case oxp_x1: 760 - case oxp_g1: 745 + case oxp_g1_i: 761 746 return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val); 762 747 case aok_zoe_a1: 763 748 case aya_neo_2: ··· 772 757 case oxp_mini_amd: 773 758 case oxp_mini_amd_a07: 774 759 case oxp_mini_amd_pro: 760 + case oxp_g1_a: 775 761 return read_from_ec(OXP_SENSOR_FAN_REG, 2, val); 776 762 default: 777 763 return -EOPNOTSUPP; ··· 792 776 return write_to_ec(ORANGEPI_SENSOR_PWM_REG, val); 793 777 case oxp_2: 794 778 case oxp_x1: 795 - case oxp_g1: 779 + case oxp_g1_i: 796 780 /* scale to range [0-184] */ 797 781 val = (val * 184) / 255; 798 782 return write_to_ec(OXP_SENSOR_PWM_REG, val); ··· 812 796 case aok_zoe_a1: 813 797 case oxp_fly: 814 798 case oxp_mini_amd_pro: 799 + case oxp_g1_a: 815 800 return write_to_ec(OXP_SENSOR_PWM_REG, val); 816 801 default: 817 802 return -EOPNOTSUPP; ··· 833 816 break; 834 817 case oxp_2: 835 818 case oxp_x1: 836 - case oxp_g1: 819 + case oxp_g1_i: 837 820 ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); 838 821 if (ret) 839 822 return ret; ··· 859 842 case aok_zoe_a1: 860 843 case oxp_fly: 861 844 case oxp_mini_amd_pro: 845 + case oxp_g1_a: 862 846 default: 863 847 ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); 864 848 if (ret)
+110
drivers/platform/x86/samsung-laptop.c
··· 16 16 #include <linux/leds.h> 17 17 #include <linux/dmi.h> 18 18 #include <linux/platform_device.h> 19 + #include <linux/power_supply.h> 19 20 #include <linux/rfkill.h> 20 21 #include <linux/acpi.h> 21 22 #include <linux/seq_file.h> ··· 24 23 #include <linux/ctype.h> 25 24 #include <linux/efi.h> 26 25 #include <linux/suspend.h> 26 + #include <acpi/battery.h> 27 27 #include <acpi/video.h> 28 28 29 29 /* ··· 349 347 struct samsung_quirks *quirks; 350 348 351 349 struct notifier_block pm_nb; 350 + 351 + struct acpi_battery_hook battery_hook; 352 352 353 353 bool handle_backlight; 354 354 bool has_stepping_quirk; ··· 701 697 static DEVICE_ATTR(performance_level, 0644, 702 698 get_performance_level, set_performance_level); 703 699 700 + static void show_battery_life_extender_deprecation_warning(struct device *dev) 701 + { 702 + dev_warn_once(dev, "battery_life_extender attribute has been deprecated, see charge_types.\n"); 703 + } 704 + 704 705 static int read_battery_life_extender(struct samsung_laptop *samsung) 705 706 { 706 707 const struct sabi_commands *commands = &samsung->config->commands; ··· 748 739 struct samsung_laptop *samsung = dev_get_drvdata(dev); 749 740 int ret; 750 741 742 + show_battery_life_extender_deprecation_warning(dev); 743 + 751 744 ret = read_battery_life_extender(samsung); 752 745 if (ret < 0) 753 746 return ret; ··· 764 753 struct samsung_laptop *samsung = dev_get_drvdata(dev); 765 754 int ret, value; 766 755 756 + show_battery_life_extender_deprecation_warning(dev); 757 + 767 758 if (!count || kstrtoint(buf, 0, &value) != 0) 768 759 return -EINVAL; 769 760 ··· 778 765 779 766 static DEVICE_ATTR(battery_life_extender, 0644, 780 767 get_battery_life_extender, set_battery_life_extender); 768 + 769 + static int samsung_psy_ext_set_prop(struct power_supply *psy, 770 + const struct power_supply_ext *ext, 771 + void *ext_data, 772 + enum power_supply_property psp, 773 + const union power_supply_propval *val) 774 + { 775 + struct samsung_laptop *samsung = ext_data; 776 + 777 + switch (val->intval) { 778 + case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE: 779 + return write_battery_life_extender(samsung, 1); 780 + case POWER_SUPPLY_CHARGE_TYPE_STANDARD: 781 + return write_battery_life_extender(samsung, 0); 782 + default: 783 + return -EINVAL; 784 + } 785 + } 786 + 787 + static int samsung_psy_ext_get_prop(struct power_supply *psy, 788 + const struct power_supply_ext *ext, 789 + void *ext_data, 790 + enum power_supply_property psp, 791 + union power_supply_propval *val) 792 + { 793 + struct samsung_laptop *samsung = ext_data; 794 + int ret; 795 + 796 + ret = read_battery_life_extender(samsung); 797 + if (ret < 0) 798 + return ret; 799 + 800 + if (ret == 1) 801 + val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE; 802 + else 803 + val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; 804 + 805 + return 0; 806 + } 807 + 808 + static int samsung_psy_prop_is_writeable(struct power_supply *psy, 809 + const struct power_supply_ext *ext, 810 + void *data, 811 + enum power_supply_property psp) 812 + { 813 + return true; 814 + } 815 + 816 + static const enum power_supply_property samsung_power_supply_props[] = { 817 + POWER_SUPPLY_PROP_CHARGE_TYPES, 818 + }; 819 + 820 + static const struct power_supply_ext samsung_battery_ext = { 821 + .name = "samsung_laptop", 822 + .properties = samsung_power_supply_props, 823 + .num_properties = ARRAY_SIZE(samsung_power_supply_props), 824 + .charge_types = (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) | 825 + BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)), 826 + .get_property = samsung_psy_ext_get_prop, 827 + .set_property = samsung_psy_ext_set_prop, 828 + .property_is_writeable = samsung_psy_prop_is_writeable, 829 + }; 830 + 831 + static int samsung_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) 832 + { 833 + struct samsung_laptop *samsung = container_of(hook, struct samsung_laptop, battery_hook); 834 + 835 + return power_supply_register_extension(battery, &samsung_battery_ext, 836 + &samsung->platform_device->dev, samsung); 837 + } 838 + 839 + static int samsung_battery_remove(struct power_supply *battery, 840 + struct acpi_battery_hook *hook) 841 + { 842 + power_supply_unregister_extension(battery, &samsung_battery_ext); 843 + 844 + return 0; 845 + } 781 846 782 847 static int read_usb_charge(struct samsung_laptop *samsung) 783 848 { ··· 1130 1039 1131 1040 if (samsung->quirks->lid_handling) 1132 1041 retval = write_lid_handling(samsung, 1); 1042 + 1043 + return retval; 1044 + } 1045 + 1046 + static int __init samsung_battery_hook_init(struct samsung_laptop *samsung) 1047 + { 1048 + int retval = 0; 1049 + 1050 + if (samsung->config->commands.get_battery_life_extender != 0xFFFF) { 1051 + samsung->battery_hook.add_battery = samsung_battery_add; 1052 + samsung->battery_hook.remove_battery = samsung_battery_remove; 1053 + samsung->battery_hook.name = "Samsung Battery Extension"; 1054 + retval = devm_battery_hook_register(&samsung->platform_device->dev, 1055 + &samsung->battery_hook); 1056 + } 1133 1057 1134 1058 return retval; 1135 1059 } ··· 1707 1601 goto error_leds; 1708 1602 1709 1603 ret = samsung_lid_handling_init(samsung); 1604 + if (ret) 1605 + goto error_lid_handling; 1606 + 1607 + ret = samsung_battery_hook_init(samsung); 1710 1608 if (ret) 1711 1609 goto error_lid_handling; 1712 1610
-4
drivers/platform/x86/silicom-platform.c
··· 248 248 static int silicom_gpio_set(struct gpio_chip *gc, unsigned int offset, 249 249 int value) 250 250 { 251 - int direction = silicom_gpio_get_direction(gc, offset); 252 251 u8 *channels = gpiochip_get_data(gc); 253 252 int channel = channels[offset]; 254 - 255 - if (direction == GPIO_LINE_DIRECTION_IN) 256 - return -EPERM; 257 253 258 254 silicom_mec_port_set(channel, !value); 259 255
+6 -4
drivers/platform/x86/think-lmi.c drivers/platform/x86/lenovo/think-lmi.c
··· 20 20 #include <linux/types.h> 21 21 #include <linux/dmi.h> 22 22 #include <linux/wmi.h> 23 - #include "firmware_attributes_class.h" 23 + #include "../firmware_attributes_class.h" 24 24 #include "think-lmi.h" 25 25 26 26 static bool debug_support; ··· 772 772 struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 773 773 enum cert_install_mode install_mode = TLMI_CERT_INSTALL; 774 774 char *auth_str, *new_cert; 775 + const char *serial; 775 776 char *signature; 776 777 char *guid; 777 778 int ret; ··· 790 789 return -EACCES; 791 790 792 791 /* Format: 'serial#, signature' */ 793 - auth_str = cert_command(setting, 794 - dmi_get_system_info(DMI_PRODUCT_SERIAL), 795 - setting->signature); 792 + serial = dmi_get_system_info(DMI_PRODUCT_SERIAL); 793 + if (!serial) 794 + return -ENODEV; 795 + auth_str = cert_command(setting, serial, setting->signature); 796 796 if (!auth_str) 797 797 return -ENOMEM; 798 798
drivers/platform/x86/think-lmi.h drivers/platform/x86/lenovo/think-lmi.h
+3 -3
drivers/platform/x86/thinkpad_acpi.c drivers/platform/x86/lenovo/thinkpad_acpi.c
··· 81 81 #include <sound/core.h> 82 82 #include <sound/initval.h> 83 83 84 - #include "dual_accel_detect.h" 84 + #include "../dual_accel_detect.h" 85 85 86 86 /* ThinkPad CMOS commands */ 87 87 #define TP_CMOS_VOLUME_DOWN 0 ··· 559 559 return 0; 560 560 } 561 561 562 - static inline bool __pure __init tpacpi_is_lenovo(void) 562 + static __always_inline bool __pure __init tpacpi_is_lenovo(void) 563 563 { 564 564 return thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO; 565 565 } 566 566 567 - static inline bool __pure __init tpacpi_is_ibm(void) 567 + static __always_inline bool __pure __init tpacpi_is_ibm(void) 568 568 { 569 569 return thinkpad_id.vendor == PCI_VENDOR_ID_IBM; 570 570 }
+32 -5
drivers/platform/x86/wmi.c
··· 20 20 #include <linux/bits.h> 21 21 #include <linux/build_bug.h> 22 22 #include <linux/device.h> 23 + #include <linux/idr.h> 23 24 #include <linux/init.h> 24 25 #include <linux/kernel.h> 25 26 #include <linux/module.h> ··· 74 73 const guid_t *guid; 75 74 int count; 76 75 }; 76 + 77 + static DEFINE_IDA(wmi_ida); 77 78 78 79 /* 79 80 * If the GUID data block is marked as expensive, we must enable and ··· 987 984 return context.count; 988 985 } 989 986 987 + static int wmi_dev_set_name(struct wmi_block *wblock, int count) 988 + { 989 + if (IS_ENABLED(CONFIG_ACPI_WMI_LEGACY_DEVICE_NAMES)) { 990 + if (count) 991 + return dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, 992 + count); 993 + else 994 + return dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid); 995 + } 996 + 997 + return dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, wblock->dev.dev.id); 998 + } 999 + 990 1000 static int wmi_create_device(struct device *wmi_bus_dev, 991 1001 struct wmi_block *wblock, 992 1002 struct acpi_device *device) ··· 1008 992 struct acpi_device_info *info; 1009 993 acpi_handle method_handle; 1010 994 acpi_status status; 1011 - int count; 995 + int count, ret; 1012 996 1013 997 if (wblock->gblock.flags & ACPI_WMI_EVENT) { 1014 998 wblock->dev.dev.type = &wmi_type_event; ··· 1079 1063 if (count < 0) 1080 1064 return count; 1081 1065 1082 - if (count) { 1083 - dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, count); 1066 + if (count) 1084 1067 set_bit(WMI_GUID_DUPLICATED, &wblock->flags); 1085 - } else { 1086 - dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid); 1068 + 1069 + ret = ida_alloc(&wmi_ida, GFP_KERNEL); 1070 + if (ret < 0) 1071 + return ret; 1072 + 1073 + wblock->dev.dev.id = ret; 1074 + ret = wmi_dev_set_name(wblock, count); 1075 + if (ret < 0) { 1076 + ida_free(&wmi_ida, wblock->dev.dev.id); 1077 + return ret; 1087 1078 } 1088 1079 1089 1080 device_initialize(&wblock->dev.dev); ··· 1176 1153 dev_err(wmi_bus_dev, "failed to register %pUL\n", 1177 1154 &wblock->gblock.guid); 1178 1155 1156 + ida_free(&wmi_ida, wblock->dev.dev.id); 1179 1157 put_device(&wblock->dev.dev); 1180 1158 } 1181 1159 } ··· 1276 1252 1277 1253 static int wmi_remove_device(struct device *dev, void *data) 1278 1254 { 1255 + int id = dev->id; 1256 + 1279 1257 device_unregister(dev); 1258 + ida_free(&wmi_ida, id); 1280 1259 1281 1260 return 0; 1282 1261 }
+3 -18
drivers/platform/x86/x86-android-tablets/asus.c
··· 206 206 .properties = asus_tf103c_touchscreen_props, 207 207 }; 208 208 209 - static const struct property_entry asus_tf103c_battery_props[] = { 210 - PROPERTY_ENTRY_STRING("compatible", "simple-battery"), 211 - PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion-polymer"), 212 - PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), 213 - PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), 214 - PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 2048000), 215 - PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4208000), 216 - PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), 217 - { } 218 - }; 219 - 220 - static const struct software_node asus_tf103c_battery_node = { 221 - .properties = asus_tf103c_battery_props, 222 - }; 223 - 224 209 static const struct property_entry asus_tf103c_bq24190_props[] = { 225 210 PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1), 226 - PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), 211 + PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_4v2_battery_node), 227 212 PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), 228 213 PROPERTY_ENTRY_BOOL("omit-battery-class"), 229 214 PROPERTY_ENTRY_BOOL("disable-reset"), ··· 221 236 222 237 static const struct property_entry asus_tf103c_ug3105_props[] = { 223 238 PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", bq24190_psy, 1), 224 - PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), 239 + PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_4v2_battery_node), 225 240 PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 5000), 226 241 { } 227 242 }; ··· 306 321 .gpio_button = &asus_me176c_tf103c_lid, 307 322 .gpio_button_count = 1, 308 323 .gpiod_lookup_tables = asus_tf103c_gpios, 309 - .bat_swnode = &asus_tf103c_battery_node, 324 + .bat_swnode = &generic_lipo_4v2_battery_node, 310 325 .modules = bq24190_modules, 311 326 };
+76
drivers/platform/x86/x86-android-tablets/shared-psy-info.c
··· 39 39 .properties = fg_bq25890_supply_props, 40 40 }; 41 41 42 + static const u32 generic_lipo_battery_ovc_cap_celcius[] = { 25 }; 43 + 44 + static const u32 generic_lipo_4v2_battery_ovc_cap_table0[] = { 45 + 4200000, 100, 46 + 4150000, 95, 47 + 4110000, 90, 48 + 4075000, 85, 49 + 4020000, 80, 50 + 3982500, 75, 51 + 3945000, 70, 52 + 3907500, 65, 53 + 3870000, 60, 54 + 3853333, 55, 55 + 3836667, 50, 56 + 3820000, 45, 57 + 3803333, 40, 58 + 3786667, 35, 59 + 3770000, 30, 60 + 3750000, 25, 61 + 3730000, 20, 62 + 3710000, 15, 63 + 3690000, 10, 64 + 3610000, 5, 65 + 3350000, 0 66 + }; 67 + 68 + static const u32 generic_lipo_hv_4v35_battery_ovc_cap_table0[] = { 69 + 4300000, 100, 70 + 4250000, 96, 71 + 4200000, 91, 72 + 4150000, 86, 73 + 4110000, 82, 74 + 4075000, 77, 75 + 4020000, 73, 76 + 3982500, 68, 77 + 3945000, 64, 78 + 3907500, 59, 79 + 3870000, 55, 80 + 3853333, 50, 81 + 3836667, 45, 82 + 3820000, 41, 83 + 3803333, 36, 84 + 3786667, 32, 85 + 3770000, 27, 86 + 3750000, 23, 87 + 3730000, 18, 88 + 3710000, 14, 89 + 3690000, 9, 90 + 3610000, 5, 91 + 3350000, 0 92 + }; 93 + 94 + /* Standard LiPo (max 4.2V) settings used by most devs with a LiPo battery */ 95 + static const struct property_entry generic_lipo_4v2_battery_props[] = { 96 + PROPERTY_ENTRY_STRING("compatible", "simple-battery"), 97 + PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion-polymer"), 98 + PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), 99 + PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), 100 + PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 2048000), 101 + PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4208000), 102 + PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), 103 + PROPERTY_ENTRY_U32_ARRAY("ocv-capacity-celsius", 104 + generic_lipo_battery_ovc_cap_celcius), 105 + PROPERTY_ENTRY_U32_ARRAY("ocv-capacity-table-0", 106 + generic_lipo_4v2_battery_ovc_cap_table0), 107 + { } 108 + }; 109 + 110 + const struct software_node generic_lipo_4v2_battery_node = { 111 + .properties = generic_lipo_4v2_battery_props, 112 + }; 113 + 42 114 /* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV battery */ 43 115 static const struct property_entry generic_lipo_hv_4v35_battery_props[] = { 44 116 PROPERTY_ENTRY_STRING("compatible", "simple-battery"), ··· 120 48 PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 1856000), 121 49 PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4352000), 122 50 PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), 51 + PROPERTY_ENTRY_U32_ARRAY("ocv-capacity-celsius", 52 + generic_lipo_battery_ovc_cap_celcius), 53 + PROPERTY_ENTRY_U32_ARRAY("ocv-capacity-table-0", 54 + generic_lipo_hv_4v35_battery_ovc_cap_table0), 123 55 { } 124 56 }; 125 57
+1
drivers/platform/x86/x86-android-tablets/shared-psy-info.h
··· 21 21 22 22 extern const struct software_node fg_bq24190_supply_node; 23 23 extern const struct software_node fg_bq25890_supply_node; 24 + extern const struct software_node generic_lipo_4v2_battery_node; 24 25 extern const struct software_node generic_lipo_hv_4v35_battery_node; 25 26 26 27 extern struct bq24190_platform_data bq24190_pdata;
+5 -4
drivers/powercap/intel_rapl_tpmi.c
··· 9 9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 10 10 11 11 #include <linux/auxiliary_bus.h> 12 - #include <linux/io.h> 13 - #include <linux/intel_tpmi.h> 14 12 #include <linux/intel_rapl.h> 13 + #include <linux/intel_tpmi.h> 14 + #include <linux/intel_vsec.h> 15 + #include <linux/io.h> 15 16 #include <linux/module.h> 16 17 #include <linux/slab.h> 17 18 ··· 49 48 50 49 struct tpmi_rapl_package { 51 50 struct rapl_if_priv priv; 52 - struct intel_tpmi_plat_info *tpmi_info; 51 + struct oobmsm_plat_info *tpmi_info; 53 52 struct rapl_package *rp; 54 53 void __iomem *base; 55 54 struct list_head node; ··· 254 253 const struct auxiliary_device_id *id) 255 254 { 256 255 struct tpmi_rapl_package *trp; 257 - struct intel_tpmi_plat_info *info; 256 + struct oobmsm_plat_info *info; 258 257 struct resource *res; 259 258 u32 offset; 260 259 int ret;
+157
include/linux/intel_pmt_features.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _FEATURES_H 3 + #define _FEATURES_H 4 + 5 + #include <linux/bits.h> 6 + #include <linux/types.h> 7 + 8 + /* Common masks */ 9 + #define PMT_CAP_TELEM BIT(0) 10 + #define PMT_CAP_WATCHER BIT(1) 11 + #define PMT_CAP_CRASHLOG BIT(2) 12 + #define PMT_CAP_STREAMING BIT(3) 13 + #define PMT_CAP_THRESHOLD BIT(4) 14 + #define PMT_CAP_WINDOW BIT(5) 15 + #define PMT_CAP_CONFIG BIT(6) 16 + #define PMT_CAP_TRACING BIT(7) 17 + #define PMT_CAP_INBAND BIT(8) 18 + #define PMT_CAP_OOB BIT(9) 19 + #define PMT_CAP_SECURED_CHAN BIT(10) 20 + 21 + #define PMT_CAP_PMT_SP BIT(11) 22 + #define PMT_CAP_PMT_SP_POLICY GENMASK(17, 12) 23 + 24 + /* Per Core Performance Telemetry (PCPT) specific masks */ 25 + #define PMT_CAP_PCPT_CORE_PERF BIT(18) 26 + #define PMT_CAP_PCPT_CORE_C0_RES BIT(19) 27 + #define PMT_CAP_PCPT_CORE_ACTIVITY BIT(20) 28 + #define PMT_CAP_PCPT_CACHE_PERF BIT(21) 29 + #define PMT_CAP_PCPT_QUALITY_TELEM BIT(22) 30 + 31 + /* Per Core Environmental Telemetry (PCET) specific masks */ 32 + #define PMT_CAP_PCET_WORKPOINT_HIST BIT(18) 33 + #define PMT_CAP_PCET_CORE_CURR_TEMP BIT(19) 34 + #define PMT_CAP_PCET_CORE_INST_RES BIT(20) 35 + #define PMT_CAP_PCET_QUALITY_TELEM BIT(21) /* Same as PMT_CAP_PCPT */ 36 + #define PMT_CAP_PCET_CORE_CDYN_LVL BIT(22) 37 + #define PMT_CAP_PCET_CORE_STRESS_LVL BIT(23) 38 + #define PMT_CAP_PCET_CORE_DAS BIT(24) 39 + #define PMT_CAP_PCET_FIVR_HEALTH BIT(25) 40 + #define PMT_CAP_PCET_ENERGY BIT(26) 41 + #define PMT_CAP_PCET_PEM_STATUS BIT(27) 42 + #define PMT_CAP_PCET_CORE_C_STATE BIT(28) 43 + 44 + /* Per RMID Performance Telemetry specific masks */ 45 + #define PMT_CAP_RMID_CORES_PERF BIT(18) 46 + #define PMT_CAP_RMID_CACHE_PERF BIT(19) 47 + #define PMT_CAP_RMID_PERF_QUAL BIT(20) 48 + 49 + /* Accelerator Telemetry specific masks */ 50 + #define PMT_CAP_ACCEL_CPM_TELEM BIT(18) 51 + #define PMT_CAP_ACCEL_TIP_TELEM BIT(19) 52 + 53 + /* Uncore Telemetry specific masks */ 54 + #define PMT_CAP_UNCORE_IO_CA_TELEM BIT(18) 55 + #define PMT_CAP_UNCORE_RMID_TELEM BIT(19) 56 + #define PMT_CAP_UNCORE_D2D_ULA_TELEM BIT(20) 57 + #define PMT_CAP_UNCORE_PKGC_TELEM BIT(21) 58 + 59 + /* Crash Log specific masks */ 60 + #define PMT_CAP_CRASHLOG_MAN_TRIG BIT(11) 61 + #define PMT_CAP_CRASHLOG_CORE BIT(12) 62 + #define PMT_CAP_CRASHLOG_UNCORE BIT(13) 63 + #define PMT_CAP_CRASHLOG_TOR BIT(14) 64 + #define PMT_CAP_CRASHLOG_S3M BIT(15) 65 + #define PMT_CAP_CRASHLOG_PERSISTENCY BIT(16) 66 + #define PMT_CAP_CRASHLOG_CLIP_GPIO BIT(17) 67 + #define PMT_CAP_CRASHLOG_PRE_RESET BIT(18) 68 + #define PMT_CAP_CRASHLOG_POST_RESET BIT(19) 69 + 70 + /* PeTe Log specific masks */ 71 + #define PMT_CAP_PETE_MAN_TRIG BIT(11) 72 + #define PMT_CAP_PETE_ENCRYPTION BIT(12) 73 + #define PMT_CAP_PETE_PERSISTENCY BIT(13) 74 + #define PMT_CAP_PETE_REQ_TOKENS BIT(14) 75 + #define PMT_CAP_PETE_PROD_ENABLED BIT(15) 76 + #define PMT_CAP_PETE_DEBUG_ENABLED BIT(16) 77 + 78 + /* TPMI control specific masks */ 79 + #define PMT_CAP_TPMI_MAILBOX BIT(11) 80 + #define PMT_CAP_TPMI_LOCK BIT(12) 81 + 82 + /* Tracing specific masks */ 83 + #define PMT_CAP_TRACE_SRAR BIT(11) 84 + #define PMT_CAP_TRACE_CORRECTABLE BIT(12) 85 + #define PMT_CAP_TRACE_MCTP BIT(13) 86 + #define PMT_CAP_TRACE_MRT BIT(14) 87 + 88 + /* Per RMID Energy Telemetry specific masks */ 89 + #define PMT_CAP_RMID_ENERGY BIT(18) 90 + #define PMT_CAP_RMID_ACTIVITY BIT(19) 91 + #define PMT_CAP_RMID_ENERGY_QUAL BIT(20) 92 + 93 + enum pmt_feature_id { 94 + FEATURE_INVALID = 0x0, 95 + FEATURE_PER_CORE_PERF_TELEM = 0x1, 96 + FEATURE_PER_CORE_ENV_TELEM = 0x2, 97 + FEATURE_PER_RMID_PERF_TELEM = 0x3, 98 + FEATURE_ACCEL_TELEM = 0x4, 99 + FEATURE_UNCORE_TELEM = 0x5, 100 + FEATURE_CRASH_LOG = 0x6, 101 + FEATURE_PETE_LOG = 0x7, 102 + FEATURE_TPMI_CTRL = 0x8, 103 + FEATURE_RESERVED = 0x9, 104 + FEATURE_TRACING = 0xA, 105 + FEATURE_PER_RMID_ENERGY_TELEM = 0xB, 106 + FEATURE_MAX = 0xB, 107 + }; 108 + 109 + enum feature_layout { 110 + LAYOUT_RMID, 111 + LAYOUT_WATCHER, 112 + LAYOUT_COMMAND, 113 + LAYOUT_CAPS_ONLY, 114 + }; 115 + 116 + struct pmt_cap { 117 + u32 mask; 118 + const char *name; 119 + }; 120 + 121 + extern const char * const pmt_feature_names[]; 122 + extern enum feature_layout feature_layout[]; 123 + extern struct pmt_cap pmt_cap_common[]; 124 + extern struct pmt_cap pmt_cap_pcpt[]; 125 + extern struct pmt_cap *pmt_caps_pcpt[]; 126 + extern struct pmt_cap pmt_cap_pcet[]; 127 + extern struct pmt_cap *pmt_caps_pcet[]; 128 + extern struct pmt_cap pmt_cap_rmid_perf[]; 129 + extern struct pmt_cap *pmt_caps_rmid_perf[]; 130 + extern struct pmt_cap pmt_cap_accel[]; 131 + extern struct pmt_cap *pmt_caps_accel[]; 132 + extern struct pmt_cap pmt_cap_uncore[]; 133 + extern struct pmt_cap *pmt_caps_uncore[]; 134 + extern struct pmt_cap pmt_cap_crashlog[]; 135 + extern struct pmt_cap *pmt_caps_crashlog[]; 136 + extern struct pmt_cap pmt_cap_pete[]; 137 + extern struct pmt_cap *pmt_caps_pete[]; 138 + extern struct pmt_cap pmt_cap_tpmi[]; 139 + extern struct pmt_cap *pmt_caps_tpmi[]; 140 + extern struct pmt_cap pmt_cap_s3m[]; 141 + extern struct pmt_cap *pmt_caps_s3m[]; 142 + extern struct pmt_cap pmt_cap_tracing[]; 143 + extern struct pmt_cap *pmt_caps_tracing[]; 144 + extern struct pmt_cap pmt_cap_rmid_energy[]; 145 + extern struct pmt_cap *pmt_caps_rmid_energy[]; 146 + 147 + static inline bool pmt_feature_id_is_valid(enum pmt_feature_id id) 148 + { 149 + if (id > FEATURE_MAX) 150 + return false; 151 + 152 + if (id == FEATURE_INVALID || id == FEATURE_RESERVED) 153 + return false; 154 + 155 + return true; 156 + } 157 + #endif
+3 -24
include/linux/intel_tpmi.h
··· 8 8 9 9 #include <linux/bitfield.h> 10 10 11 + struct oobmsm_plat_info; 12 + 11 13 #define TPMI_VERSION_INVALID 0xff 12 14 #define TPMI_MINOR_VERSION(val) FIELD_GET(GENMASK(4, 0), val) 13 15 #define TPMI_MAJOR_VERSION(val) FIELD_GET(GENMASK(7, 5), val) ··· 28 26 TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */ 29 27 }; 30 28 31 - /** 32 - * struct intel_tpmi_plat_info - Platform information for a TPMI device instance 33 - * @cdie_mask: Mask of all compute dies in the partition 34 - * @package_id: CPU Package id 35 - * @partition: Package partition id when multiple VSEC PCI devices per package 36 - * @segment: PCI segment ID 37 - * @bus_number: PCI bus number 38 - * @device_number: PCI device number 39 - * @function_number: PCI function number 40 - * 41 - * Structure to store platform data for a TPMI device instance. This 42 - * struct is used to return data via tpmi_get_platform_data(). 43 - */ 44 - struct intel_tpmi_plat_info { 45 - u16 cdie_mask; 46 - u8 package_id; 47 - u8 partition; 48 - u8 segment; 49 - u8 bus_number; 50 - u8 device_number; 51 - u8 function_number; 52 - }; 53 - 54 - struct intel_tpmi_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev); 29 + struct oobmsm_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev); 55 30 struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int index); 56 31 int tpmi_get_resource_count(struct auxiliary_device *auxdev); 57 32 int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, bool *read_blocked,
+93 -5
include/linux/intel_vsec.h
··· 4 4 5 5 #include <linux/auxiliary_bus.h> 6 6 #include <linux/bits.h> 7 + #include <linux/err.h> 8 + #include <linux/intel_pmt_features.h> 7 9 8 - #define VSEC_CAP_TELEMETRY BIT(0) 9 - #define VSEC_CAP_WATCHER BIT(1) 10 - #define VSEC_CAP_CRASHLOG BIT(2) 11 - #define VSEC_CAP_SDSI BIT(3) 12 - #define VSEC_CAP_TPMI BIT(4) 10 + /* 11 + * VSEC_CAP_UNUSED is reserved. It exists to prevent zero initialized 12 + * intel_vsec devices from being automatically set to a known 13 + * capability with ID 0 14 + */ 15 + #define VSEC_CAP_UNUSED BIT(0) 16 + #define VSEC_CAP_TELEMETRY BIT(1) 17 + #define VSEC_CAP_WATCHER BIT(2) 18 + #define VSEC_CAP_CRASHLOG BIT(3) 19 + #define VSEC_CAP_SDSI BIT(4) 20 + #define VSEC_CAP_TPMI BIT(5) 21 + #define VSEC_CAP_DISCOVERY BIT(6) 22 + #define VSEC_FEATURE_COUNT 7 13 23 14 24 /* Intel DVSEC offsets */ 15 25 #define INTEL_DVSEC_ENTRIES 0xA ··· 36 26 VSEC_ID_TELEMETRY = 2, 37 27 VSEC_ID_WATCHER = 3, 38 28 VSEC_ID_CRASHLOG = 4, 29 + VSEC_ID_DISCOVERY = 12, 39 30 VSEC_ID_SDSI = 65, 40 31 VSEC_ID_TPMI = 66, 41 32 }; ··· 92 81 int (*read_telem)(struct pci_dev *pdev, u32 guid, u64 *data, loff_t off, u32 count); 93 82 }; 94 83 84 + struct vsec_feature_dependency { 85 + unsigned long feature; 86 + unsigned long supplier_bitmap; 87 + }; 88 + 95 89 /** 96 90 * struct intel_vsec_platform_info - Platform specific data 97 91 * @parent: parent device in the auxbus chain 98 92 * @headers: list of headers to define the PMT client devices to create 93 + * @deps: array of feature dependencies 99 94 * @priv_data: private data, usable by parent devices, currently a callback 100 95 * @caps: bitmask of PMT capabilities for the given headers 101 96 * @quirks: bitmask of VSEC device quirks 102 97 * @base_addr: allow a base address to be specified (rather than derived) 98 + * @num_deps: Count feature dependencies 103 99 */ 104 100 struct intel_vsec_platform_info { 105 101 struct device *parent; 106 102 struct intel_vsec_header **headers; 103 + const struct vsec_feature_dependency *deps; 107 104 void *priv_data; 108 105 unsigned long caps; 109 106 unsigned long quirks; 110 107 u64 base_addr; 108 + int num_deps; 111 109 }; 112 110 113 111 /** ··· 130 110 * @priv_data: any private data needed 131 111 * @quirks: specified quirks 132 112 * @base_addr: base address of entries (if specified) 113 + * @cap_id: the enumerated id of the vsec feature 133 114 */ 134 115 struct intel_vsec_device { 135 116 struct auxiliary_device auxdev; ··· 143 122 size_t priv_data_size; 144 123 unsigned long quirks; 145 124 u64 base_addr; 125 + unsigned long cap_id; 126 + }; 127 + 128 + /** 129 + * struct oobmsm_plat_info - Platform information for a device instance 130 + * @cdie_mask: Mask of all compute dies in the partition 131 + * @package_id: CPU Package id 132 + * @partition: Package partition id when multiple VSEC PCI devices per package 133 + * @segment: PCI segment ID 134 + * @bus_number: PCI bus number 135 + * @device_number: PCI device number 136 + * @function_number: PCI function number 137 + * 138 + * Structure to store platform data for a OOBMSM device instance. 139 + */ 140 + struct oobmsm_plat_info { 141 + u16 cdie_mask; 142 + u8 package_id; 143 + u8 partition; 144 + u8 segment; 145 + u8 bus_number; 146 + u8 device_number; 147 + u8 function_number; 148 + }; 149 + 150 + struct telemetry_region { 151 + struct oobmsm_plat_info plat_info; 152 + void __iomem *addr; 153 + size_t size; 154 + u32 guid; 155 + u32 num_rmids; 156 + }; 157 + 158 + struct pmt_feature_group { 159 + enum pmt_feature_id id; 160 + int count; 161 + struct kref kref; 162 + struct telemetry_region regions[]; 146 163 }; 147 164 148 165 int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, ··· 200 141 #if IS_ENABLED(CONFIG_INTEL_VSEC) 201 142 int intel_vsec_register(struct pci_dev *pdev, 202 143 struct intel_vsec_platform_info *info); 144 + int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info, 145 + struct intel_vsec_device *vsec_dev); 146 + struct oobmsm_plat_info *intel_vsec_get_mapping(struct pci_dev *pdev); 203 147 #else 204 148 static inline int intel_vsec_register(struct pci_dev *pdev, 205 149 struct intel_vsec_platform_info *info) 206 150 { 207 151 return -ENODEV; 208 152 } 153 + static inline int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info, 154 + struct intel_vsec_device *vsec_dev) 155 + { 156 + return -ENODEV; 157 + } 158 + static inline struct oobmsm_plat_info *intel_vsec_get_mapping(struct pci_dev *pdev) 159 + { 160 + return ERR_PTR(-ENODEV); 161 + } 209 162 #endif 163 + 164 + #if IS_ENABLED(CONFIG_INTEL_PMT_TELEMETRY) 165 + struct pmt_feature_group * 166 + intel_pmt_get_regions_by_feature(enum pmt_feature_id id); 167 + 168 + void intel_pmt_put_feature_group(struct pmt_feature_group *feature_group); 169 + #else 170 + static inline struct pmt_feature_group * 171 + intel_pmt_get_regions_by_feature(enum pmt_feature_id id) 172 + { 173 + return ERR_PTR(-ENODEV); 174 + } 175 + 176 + static inline void 177 + intel_pmt_put_feature_group(struct pmt_feature_group *feature_group) {} 178 + #endif 179 + 210 180 #endif