platform-drivers-x86 for v6.17-1
Highlights
- 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
The following is an automated shortlog grouped by driver:
Add Lenovo Capability Data 01 WMI Driver:
- Add Lenovo Capability Data 01 WMI Driver
Add Lenovo Gamezone WMI Driver:
- Add Lenovo Gamezone WMI Driver
Add Lenovo Other Mode WMI Driver:
- Add Lenovo Other Mode WMI Driver
Add lenovo-wmi-* driver Documentation:
- Add lenovo-wmi-* driver Documentation
Add Lenovo WMI Events Driver:
- Add Lenovo WMI Events Driver
Add lenovo-wmi-helpers:
- Add lenovo-wmi-helpers
alienware-wmi-wmax:
- Add appropriate labels to fans
amd/hsmp:
- Enhance the print messages to prevent confusion
- Use IS_ENABLED() instead of IS_REACHABLE()
amd: pmc:
- Add Lenovo Yoga 6 13ALC6 to pmc quirk list
arm64: lenovo-yoga-c630:
- use the auxiliary device creation helper
dell_rbu:
- Remove unused struct
dell-uart-backlight:
- Use blacklight power constant
docs:
- Add ABI documentation for intel_pmt feature directories
Documentation: ABI:
- Update WMI device paths in ABI docs
drm/xe:
- Correct BMG VSEC header sizing
- Correct the rev value for the DVSEC entries
fujitsu:
- clamp charge_control_end_threshold values to 50
- use unsigned int for kstrtounit
ideapad:
- Expose charge_types
intel/pmt:
- Add PMT Discovery driver
- add register access helpers
- correct types
- decouple sysfs and namespace
intel/pmt/discovery:
- fix format string warning
- Fix size_t specifiers for 32-bit
- Get telemetry attributes
intel/pmt:
- fix a crashlog NULL pointer access
- fix build dependency for kunit test
- KUNIT test for PMT Enhanced Discovery API
- mutex clean up
- refactor base parameter
- re-order trigger logic
- support BMG crashlog
intel/pmt/telemetry:
- Add API to retrieve telemetry regions by feature
intel/pmt:
- use a version struct
- use guard(mutex)
- white space cleanup
intel_telemetry:
- Remove unused telemetry_*_events()
- Remove unused telemetry_[gs]et_sampling_period()
- Remove unused telemetry_raw_read_events()
intel/tpmi:
- Get OOBMSM CPU mapping from TPMI
- Relocate platform info to intel_vsec.h
intel/vsec:
- Add device links to enforce dependencies
- Add new Discovery feature
- Add private data for per-device data
- Create wrapper to walk PCI config space
- Set OOBMSM to CPU mapping
- Skip absent features during initialization
- Skip driverless features
lenovo:
- gamezone needs "other mode"
lenovo-yoga-tab2-pro-1380-fastcharger:
- Use devm_pinctrl_register_mappings()
MAINTAINERS:
- Add link to documentation of Intel PMT ABI
Move Lenovo files into lenovo subdir:
- Move Lenovo files into lenovo subdir
oxpec:
- Add support for OneXPlayer X1 Mini Pro (Strix Point)
- Fix turbo register for G1 AMD
samsung-laptop:
- Expose charge_types
silicom:
- remove unnecessary GPIO line direction check
thinklmi:
- improved DMI handling
thinkpad_acpi:
- Handle KCOV __init vs inline mismatches
wmi:
- Fix WMI device naming issue
x86-android-tablets:
- Add generic_lipo_4v2_battery info
- Add ovc-capacity-table info
Merges:
- Merge branch 'fixes' into 'for-next'
- Merge branch 'fixes' into for-next
-----BEGIN PGP SIGNATURE-----
iHUEABYIAB0WIQSCSUwRdwTNL2MhaBlZrE9hU+XOMQUCaIdbygAKCRBZrE9hU+XO
MbqTAQCqqczU2YXRnq7TIvw/yl40+scIKMXobjX0EEpmgqhlHwEAkWwjQG0ytS2j
hzES5gog1xT6A4TIjVr0Up5MUj3crwU=
=+TRi
-----END PGP SIGNATURE-----
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"
...
pull/1309/head
commit
9669b2499e
|
|
@ -0,0 +1,10 @@
|
||||||
|
What: /sys/devices/platform/samsung/battery_life_extender
|
||||||
|
Date: December 1, 2011
|
||||||
|
KernelVersion: 3.3
|
||||||
|
Contact: Corentin Chary <corentin.chary@gmail.com>
|
||||||
|
Description: Max battery charge level can be modified, battery cycle
|
||||||
|
life can be extended by reducing the max battery charge
|
||||||
|
level.
|
||||||
|
|
||||||
|
- 0 means normal battery mode (100% charge)
|
||||||
|
- 1 means battery life extender mode (80% charge)
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
What: /sys/bus/platform/devices/VPC2004:*/conservation_mode
|
||||||
|
Date: Aug 2017
|
||||||
|
KernelVersion: 4.14
|
||||||
|
Contact: platform-driver-x86@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
Controls whether the conservation mode is enabled or not.
|
||||||
|
This feature limits the maximum battery charge percentage to
|
||||||
|
around 50-60% in order to prolong the lifetime of the battery.
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
What: /sys/class/intel_pmt/features-<PCI BDF>/
|
||||||
|
Date: 2025-04-24
|
||||||
|
KernelVersion: 6.16
|
||||||
|
Contact: david.e.box@linux.intel.com
|
||||||
|
Description:
|
||||||
|
The `features-<PCI BDF>/` directory represents the "features"
|
||||||
|
capability exposed by Intel PMT (Platform Monitoring Technology)
|
||||||
|
for the given PCI device.
|
||||||
|
|
||||||
|
Each directory corresponds to a PMT feature and contains
|
||||||
|
attributes describing the available telemetry, monitoring, or
|
||||||
|
control functionalities.
|
||||||
|
|
||||||
|
Directory Structure:
|
||||||
|
|
||||||
|
/sys/class/intel_pmt/features-<PCI BDF>/
|
||||||
|
├── accelerator_telemetry/ # Per-accelerator telemetry data
|
||||||
|
├── crash_log/ # Contains system crash telemetry logs
|
||||||
|
├── per_core_environment_telemetry/ # Environmental telemetry per core
|
||||||
|
├── per_core_performance_telemetry/ # Performance telemetry per core
|
||||||
|
├── per_rmid_energy_telemetry/ # Energy telemetry for RMIDs
|
||||||
|
├── per_rmid_perf_telemetry/ # Performance telemetry for RMIDs
|
||||||
|
├── tpmi_control/ # TPMI-related controls and telemetry
|
||||||
|
├── tracing/ # PMT tracing features
|
||||||
|
└── uncore_telemetry/ # Uncore telemetry data
|
||||||
|
|
||||||
|
Common Files (Present in all feature directories):
|
||||||
|
|
||||||
|
caps
|
||||||
|
- Read-only
|
||||||
|
- Lists available capabilities for this feature.
|
||||||
|
|
||||||
|
guids
|
||||||
|
- Read-only
|
||||||
|
- Lists GUIDs associated with this feature.
|
||||||
|
|
||||||
|
Additional Attributes (Conditional Presence):
|
||||||
|
|
||||||
|
max_command_size
|
||||||
|
- Read-only
|
||||||
|
- Present if the feature supports out-of-band MCTP access.
|
||||||
|
- Maximum supported MCTP command size for out-of-band PMT access (bytes).
|
||||||
|
|
||||||
|
max_stream_size
|
||||||
|
- Read-only
|
||||||
|
- Present if the feature supports out-of-band MCTP access.
|
||||||
|
- Maximum supported MCTP stream size (bytes).
|
||||||
|
|
||||||
|
min_watcher_period_ms
|
||||||
|
- Read-only
|
||||||
|
- Present if the feature supports the watcher API.
|
||||||
|
The watcher API provides a writable control interface that allows user
|
||||||
|
configuration of monitoring behavior, such as setting the sampling or
|
||||||
|
reporting interval.
|
||||||
|
- Minimum supported time period for the watcher interface (milliseconds).
|
||||||
|
|
||||||
|
num_rmids
|
||||||
|
- Read-only
|
||||||
|
- Present if the feature supports RMID (Resource Monitoring ID) telemetry.
|
||||||
|
RMIDs are identifiers used by hardware to track and report resource usage,
|
||||||
|
such as memory bandwidth or energy consumption, on a per-logical-entity
|
||||||
|
basis (e.g., per core, thread, or process group).
|
||||||
|
- Maximum number of RMIDs tracked simultaneously.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
For a device with PCI BDF `0000:00:03.1`, the directory tree could look like:
|
||||||
|
|
||||||
|
/sys/class/intel_pmt/features-0000:00:03.1/
|
||||||
|
├── accelerator_telemetry/
|
||||||
|
│ ├── caps
|
||||||
|
│ ├── guids
|
||||||
|
│ ├── max_command_size
|
||||||
|
│ ├── max_stream_size
|
||||||
|
│ ├── min_watcher_period_ms
|
||||||
|
├── crash_log/
|
||||||
|
│ ├── caps
|
||||||
|
│ ├── guids
|
||||||
|
│ ├── max_command_size
|
||||||
|
│ ├── max_stream_size
|
||||||
|
├── per_core_environment_telemetry/
|
||||||
|
│ ├── caps
|
||||||
|
│ ├── guids
|
||||||
|
│ ├── max_command_size
|
||||||
|
│ ├── max_stream_size
|
||||||
|
│ ├── min_watcher_period_ms
|
||||||
|
├── per_rmid_energy_telemetry/
|
||||||
|
│ ├── caps
|
||||||
|
│ ├── guids
|
||||||
|
│ ├── max_command_size
|
||||||
|
│ ├── max_stream_size
|
||||||
|
│ ├── min_watcher_period_ms
|
||||||
|
│ ├── num_rmids
|
||||||
|
├── tpmi_control/
|
||||||
|
│ ├── caps
|
||||||
|
│ ├── guids
|
||||||
|
├── tracing/
|
||||||
|
│ ├── caps
|
||||||
|
│ ├── guids
|
||||||
|
├── uncore_telemetry/
|
||||||
|
│ ├── caps
|
||||||
|
│ ├── guids
|
||||||
|
│ ├── max_command_size
|
||||||
|
│ ├── max_stream_size
|
||||||
|
│ ├── min_watcher_period_ms
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- Some attributes are only present if the corresponding feature supports
|
||||||
|
the capability (e.g., `max_command_size` for MCTP-capable features).
|
||||||
|
- Features supporting RMIDs include `num_rmids`.
|
||||||
|
- Features supporting the watcher API include `min_watcher_period_ms`.
|
||||||
|
- The `caps` file provides additional information about the functionality
|
||||||
|
of the feature.
|
||||||
|
|
||||||
|
Example 'caps' content for the 'tracing' feature:
|
||||||
|
|
||||||
|
/sys/class/intel_pmt/features-0000:00:03.1/
|
||||||
|
├── tracing/
|
||||||
|
│ ├── caps
|
||||||
|
|
||||||
|
telemetry Available: No
|
||||||
|
watcher Available: Yes
|
||||||
|
crashlog Available: No
|
||||||
|
streaming Available: No
|
||||||
|
threashold Available: No
|
||||||
|
window Available: No
|
||||||
|
config Available: Yes
|
||||||
|
tracing Available: No
|
||||||
|
inband Available: Yes
|
||||||
|
oob Available: Yes
|
||||||
|
secure_chan Available: No
|
||||||
|
pmt_sp Available: Yes
|
||||||
|
pmt_sp_policy Available: Yes
|
||||||
|
mailbox Available: Yes
|
||||||
|
bios_lock Available: Yes
|
||||||
|
|
@ -20,17 +20,6 @@ Description: Some Samsung laptops have different "performance levels"
|
||||||
and it's still unknown if this value even changes
|
and it's still unknown if this value even changes
|
||||||
anything, other than making the user feel a bit better.
|
anything, other than making the user feel a bit better.
|
||||||
|
|
||||||
What: /sys/devices/platform/samsung/battery_life_extender
|
|
||||||
Date: December 1, 2011
|
|
||||||
KernelVersion: 3.3
|
|
||||||
Contact: Corentin Chary <corentin.chary@gmail.com>
|
|
||||||
Description: Max battery charge level can be modified, battery cycle
|
|
||||||
life can be extended by reducing the max battery charge
|
|
||||||
level.
|
|
||||||
|
|
||||||
- 0 means normal battery mode (100% charge)
|
|
||||||
- 1 means battery life extender mode (80% charge)
|
|
||||||
|
|
||||||
What: /sys/devices/platform/samsung/usb_charge
|
What: /sys/devices/platform/samsung/usb_charge
|
||||||
Date: December 1, 2011
|
Date: December 1, 2011
|
||||||
KernelVersion: 3.3
|
KernelVersion: 3.3
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_supported_type
|
What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919[-X]/dell_privacy_supported_type
|
||||||
Date: Apr 2021
|
Date: Apr 2021
|
||||||
KernelVersion: 5.13
|
KernelVersion: 5.13
|
||||||
Contact: "<perry.yuan@dell.com>"
|
Contact: "<perry.yuan@dell.com>"
|
||||||
|
|
@ -29,12 +29,12 @@ Description:
|
||||||
|
|
||||||
For example to check which privacy devices are supported::
|
For example to check which privacy devices are supported::
|
||||||
|
|
||||||
# cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_supported_type
|
# cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919*/dell_privacy_supported_type
|
||||||
[Microphone Mute] [supported]
|
[Microphone Mute] [supported]
|
||||||
[Camera Shutter] [supported]
|
[Camera Shutter] [supported]
|
||||||
[ePrivacy Screen] [unsupported]
|
[ePrivacy Screen] [unsupported]
|
||||||
|
|
||||||
What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_current_state
|
What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919[-X]/dell_privacy_current_state
|
||||||
Date: Apr 2021
|
Date: Apr 2021
|
||||||
KernelVersion: 5.13
|
KernelVersion: 5.13
|
||||||
Contact: "<perry.yuan@dell.com>"
|
Contact: "<perry.yuan@dell.com>"
|
||||||
|
|
@ -66,6 +66,6 @@ Description:
|
||||||
|
|
||||||
For example to check all supported current privacy device states::
|
For example to check all supported current privacy device states::
|
||||||
|
|
||||||
# cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_current_state
|
# cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919*/dell_privacy_current_state
|
||||||
[Microphone] [unmuted]
|
[Microphone] [unmuted]
|
||||||
[Camera Shutter] [unmuted]
|
[Camera Shutter] [unmuted]
|
||||||
|
|
|
||||||
|
|
@ -27,15 +27,6 @@ Description:
|
||||||
* 1 -> Switched On
|
* 1 -> Switched On
|
||||||
* 0 -> Switched Off
|
* 0 -> Switched Off
|
||||||
|
|
||||||
What: /sys/bus/platform/devices/VPC2004:*/conservation_mode
|
|
||||||
Date: Aug 2017
|
|
||||||
KernelVersion: 4.14
|
|
||||||
Contact: platform-driver-x86@vger.kernel.org
|
|
||||||
Description:
|
|
||||||
Controls whether the conservation mode is enabled or not.
|
|
||||||
This feature limits the maximum battery charge percentage to
|
|
||||||
around 50-60% in order to prolong the lifetime of the battery.
|
|
||||||
|
|
||||||
What: /sys/bus/platform/devices/VPC2004:*/fn_lock
|
What: /sys/bus/platform/devices/VPC2004:*/fn_lock
|
||||||
Date: May 2018
|
Date: May 2018
|
||||||
KernelVersion: 4.18
|
KernelVersion: 4.18
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
What: /sys/bus/wmi/devices/44FADEB1-B204-40F2-8581-394BBDC1B651/firmware_update_request
|
What: /sys/bus/wmi/devices/44FADEB1-B204-40F2-8581-394BBDC1B651[-X]/firmware_update_request
|
||||||
Date: April 2020
|
Date: April 2020
|
||||||
KernelVersion: 5.7
|
KernelVersion: 5.7
|
||||||
Contact: "Jithu Joseph" <jithu.joseph@intel.com>
|
Contact: "Jithu Joseph" <jithu.joseph@intel.com>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
What: /sys/devices/platform/<platform>/force_power
|
What: /sys/bus/wmi/devices/86CCFD48-205E-4A77-9C48-2021CBEDE341[-X]/force_power
|
||||||
Date: September 2017
|
Date: September 2017
|
||||||
KernelVersion: 4.15
|
KernelVersion: 4.15
|
||||||
Contact: "Mario Limonciello" <mario.limonciello@outlook.com>
|
Contact: "Mario Limonciello" <mario.limonciello@outlook.com>
|
||||||
|
|
|
||||||
|
|
@ -358,12 +358,7 @@ Forcing power
|
||||||
Many OEMs include a method that can be used to force the power of a
|
Many OEMs include a method that can be used to force the power of a
|
||||||
Thunderbolt controller to an "On" state even if nothing is connected.
|
Thunderbolt controller to an "On" state even if nothing is connected.
|
||||||
If supported by your machine this will be exposed by the WMI bus with
|
If supported by your machine this will be exposed by the WMI bus with
|
||||||
a sysfs attribute called "force_power".
|
a sysfs attribute called "force_power", see
|
||||||
|
Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt for details.
|
||||||
For example the intel-wmi-thunderbolt driver exposes this attribute in:
|
|
||||||
/sys/bus/wmi/devices/86CCFD48-205E-4A77-9C48-2021CBEDE341/force_power
|
|
||||||
|
|
||||||
To force the power to on, write 1 to this attribute file.
|
|
||||||
To disable force power, write 0 to this attribute file.
|
|
||||||
|
|
||||||
Note: it's currently not possible to query the force power state of a platform.
|
Note: it's currently not possible to query the force power state of a platform.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,203 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
==========================================================
|
||||||
|
Lenovo WMI Interface Gamezone Driver (lenovo-wmi-gamezone)
|
||||||
|
==========================================================
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
The Lenovo WMI gamezone interface is broken up into multiple GUIDs,
|
||||||
|
The primary "Gamezone" GUID provides advanced features such as fan
|
||||||
|
profiles and overclocking. It is paired with multiple event GUIDs
|
||||||
|
and data block GUIDs that provide context for the various methods.
|
||||||
|
|
||||||
|
Gamezone Data
|
||||||
|
-------------
|
||||||
|
|
||||||
|
WMI GUID ``887B54E3-DDDC-4B2C-8B88-68A26A8835D0``
|
||||||
|
|
||||||
|
The Gamezone Data WMI interface provides platform-profile and fan curve
|
||||||
|
settings for devices that fall under the "Gaming Series" of Lenovo devices.
|
||||||
|
It uses a notifier chain to inform other Lenovo WMI interface drivers of the
|
||||||
|
current platform profile when it changes.
|
||||||
|
|
||||||
|
The following platform profiles are supported:
|
||||||
|
- low-power
|
||||||
|
- balanced
|
||||||
|
- balanced-performance
|
||||||
|
- performance
|
||||||
|
- custom
|
||||||
|
|
||||||
|
Balanced-Performance
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Some newer Lenovo "Gaming Series" laptops have an "Extreme Mode" profile
|
||||||
|
enabled in their BIOS. For these devices, the performance platform profile
|
||||||
|
corresponds to the BIOS Extreme Mode, while the balanced-performance
|
||||||
|
platform profile corresponds to the BIOS Performance mode. For legacy
|
||||||
|
devices, the performance platform profile will correspond with the BIOS
|
||||||
|
Performance mode.
|
||||||
|
|
||||||
|
For some newer devices the "Extreme Mode" profile is incomplete in the BIOS
|
||||||
|
and setting it will cause undefined behavior. A BIOS bug quirk table is
|
||||||
|
provided to ensure these devices cannot set "Extreme Mode" from the driver.
|
||||||
|
|
||||||
|
Custom Profile
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
The custom profile represents a hardware mode on Lenovo devices that enables
|
||||||
|
user modifications to Package Power Tracking (PPT) and fan curve settings.
|
||||||
|
When an attribute exposed by the Other Mode WMI interface is to be modified,
|
||||||
|
the Gamezone driver must first be switched to the "custom" profile manually,
|
||||||
|
or the setting will have no effect. If another profile is set from the list
|
||||||
|
of supported profiles, the BIOS will override any user PPT settings when
|
||||||
|
switching to that profile.
|
||||||
|
|
||||||
|
Gamezone Thermal Mode Event
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
WMI GUID ``D320289E-8FEA-41E0-86F9-911D83151B5F``
|
||||||
|
|
||||||
|
The Gamezone Thermal Mode Event interface notifies the system when the platform
|
||||||
|
profile has changed, either through the hardware event (Fn+Q for laptops or
|
||||||
|
Legion + Y for Go Series), or through the Gamezone WMI interface. This event is
|
||||||
|
implemented in the Lenovo WMI Events driver (lenovo-wmi-events).
|
||||||
|
|
||||||
|
|
||||||
|
WMI interface description
|
||||||
|
=========================
|
||||||
|
|
||||||
|
The WMI interface description can be decoded from the embedded binary MOF (bmof)
|
||||||
|
data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO_GAMEZONE_DATA class"), guid("{887B54E3-DDDC-4B2C-8B88-68A26A8835D0}")]
|
||||||
|
class LENOVO_GAMEZONE_DATA {
|
||||||
|
[key, read] string InstanceName;
|
||||||
|
[read] boolean Active;
|
||||||
|
|
||||||
|
[WmiMethodId(4), Implemented, Description("Is SupportGpu OverClock")] void IsSupportGpuOC([out, Description("Is SupportGpu OverClock")] uint32 Data);
|
||||||
|
[WmiMethodId(11), Implemented, Description("Get AslCode Version")] void GetVersion ([out, Description("AslCode version")] UINT32 Data);
|
||||||
|
[WmiMethodId(12), Implemented, Description("Fan cooling capability")] void IsSupportFanCooling([out, Description("Fan cooling capability")] UINT32 Data);
|
||||||
|
[WmiMethodId(13), Implemented, Description("Set Fan cooling on/off")] void SetFanCooling ([in, Description("Set Fan cooling on/off")] UINT32 Data);
|
||||||
|
[WmiMethodId(14), Implemented, Description("cpu oc capability")] void IsSupportCpuOC ([out, Description("cpu oc capability")] UINT32 Data);
|
||||||
|
[WmiMethodId(15), Implemented, Description("bios has overclock capability")] void IsBIOSSupportOC ([out, Description("bios has overclock capability")] UINT32 Data);
|
||||||
|
[WmiMethodId(16), Implemented, Description("enable or disable overclock in bios")] void SetBIOSOC ([in, Description("enable or disable overclock in bios")] UINT32 Data);
|
||||||
|
[WmiMethodId(18), Implemented, Description("Get CPU temperature")] void GetCPUTemp ([out, Description("Get CPU temperature")] UINT32 Data);
|
||||||
|
[WmiMethodId(19), Implemented, Description("Get GPU temperature")] void GetGPUTemp ([out, Description("Get GPU temperature")] UINT32 Data);
|
||||||
|
[WmiMethodId(20), Implemented, Description("Get Fan cooling on/off status")] void GetFanCoolingStatus ([out, Description("Get Fan cooling on/off status")] UINT32 Data);
|
||||||
|
[WmiMethodId(21), Implemented, Description("EC support disable windows key capability")] void IsSupportDisableWinKey ([out, Description("EC support disable windows key capability")] UINT32 Data);
|
||||||
|
[WmiMethodId(22), Implemented, Description("Set windows key disable/enable")] void SetWinKeyStatus ([in, Description("Set windows key disable/enable")] UINT32 Data);
|
||||||
|
[WmiMethodId(23), Implemented, Description("Get windows key disable/enable status")] void GetWinKeyStatus ([out, Description("Get windows key disable/enable status")] UINT32 Data);
|
||||||
|
[WmiMethodId(24), Implemented, Description("EC support disable touchpad capability")] void IsSupportDisableTP ([out, Description("EC support disable touchpad capability")] UINT32 Data);
|
||||||
|
[WmiMethodId(25), Implemented, Description("Set touchpad disable/enable")] void SetTPStatus ([in, Description("Set touchpad disable/enable")] UINT32 Data);
|
||||||
|
[WmiMethodId(26), Implemented, Description("Get touchpad disable/enable status")] void GetTPStatus ([out, Description("Get touchpad disable/enable status")] UINT32 Data);
|
||||||
|
[WmiMethodId(30), Implemented, Description("Get Keyboard feature list")] void GetKeyboardfeaturelist ([out, Description("Get Keyboard feature list")] UINT32 Data);
|
||||||
|
[WmiMethodId(31), Implemented, Description("Get Memory OC Information")] void GetMemoryOCInfo ([out, Description("Get Memory OC Information")] UINT32 Data);
|
||||||
|
[WmiMethodId(32), Implemented, Description("Water Cooling feature capability")] void IsSupportWaterCooling ([out, Description("Water Cooling feature capability")] UINT32 Data);
|
||||||
|
[WmiMethodId(33), Implemented, Description("Set Water Cooling status")] void SetWaterCoolingStatus ([in, Description("Set Water Cooling status")] UINT32 Data);
|
||||||
|
[WmiMethodId(34), Implemented, Description("Get Water Cooling status")] void GetWaterCoolingStatus ([out, Description("Get Water Cooling status")] UINT32 Data);
|
||||||
|
[WmiMethodId(35), Implemented, Description("Lighting feature capability")] void IsSupportLightingFeature ([out, Description("Lighting feature capability")] UINT32 Data);
|
||||||
|
[WmiMethodId(36), Implemented, Description("Set keyboard light off or on to max")] void SetKeyboardLight ([in, Description("keyboard light off or on switch")] UINT32 Data);
|
||||||
|
[WmiMethodId(37), Implemented, Description("Get keyboard light on/off status")] void GetKeyboardLight ([out, Description("Get keyboard light on/off status")] UINT32 Data);
|
||||||
|
[WmiMethodId(38), Implemented, Description("Get Macrokey scan code")] void GetMacrokeyScancode ([in, Description("Macrokey index")] UINT32 idx, [out, Description("Scan code")] UINT32 scancode);
|
||||||
|
[WmiMethodId(39), Implemented, Description("Get Macrokey count")] void GetMacrokeyCount ([out, Description("Macrokey count")] UINT32 Data);
|
||||||
|
[WmiMethodId(40), Implemented, Description("Support G-Sync feature")] void IsSupportGSync ([out, Description("Support G-Sync feature")] UINT32 Data);
|
||||||
|
[WmiMethodId(41), Implemented, Description("Get G-Sync Status")] void GetGSyncStatus ([out, Description("Get G-Sync Status")] UINT32 Data);
|
||||||
|
[WmiMethodId(42), Implemented, Description("Set G-Sync Status")] void SetGSyncStatus ([in, Description("Set G-Sync Status")] UINT32 Data);
|
||||||
|
[WmiMethodId(43), Implemented, Description("Support Smart Fan feature")] void IsSupportSmartFan ([out, Description("Support Smart Fan feature")] UINT32 Data);
|
||||||
|
[WmiMethodId(44), Implemented, Description("Set Smart Fan Mode")] void SetSmartFanMode ([in, Description("Set Smart Fan Mode")] UINT32 Data);
|
||||||
|
[WmiMethodId(45), Implemented, Description("Get Smart Fan Mode")] void GetSmartFanMode ([out, Description("Get Smart Fan Mode")] UINT32 Data);
|
||||||
|
[WmiMethodId(46), Implemented, Description("Get Smart Fan Setting Mode")] void GetSmartFanSetting ([out, Description("Get Smart Setting Mode")] UINT32 Data);
|
||||||
|
[WmiMethodId(47), Implemented, Description("Get Power Charge Mode")] void GetPowerChargeMode ([out, Description("Get Power Charge Mode")] UINT32 Data);
|
||||||
|
[WmiMethodId(48), Implemented, Description("Get Gaming Product Info")] void GetProductInfo ([out, Description("Get Gaming Product Info")] UINT32 Data);
|
||||||
|
[WmiMethodId(49), Implemented, Description("Over Drive feature capability")] void IsSupportOD ([out, Description("Over Drive feature capability")] UINT32 Data);
|
||||||
|
[WmiMethodId(50), Implemented, Description("Get Over Drive status")] void GetODStatus ([out, Description("Get Over Drive status")] UINT32 Data);
|
||||||
|
[WmiMethodId(51), Implemented, Description("Set Over Drive status")] void SetODStatus ([in, Description("Set Over Drive status")] UINT32 Data);
|
||||||
|
[WmiMethodId(52), Implemented, Description("Set Light Control Owner")] void SetLightControlOwner ([in, Description("Set Light Control Owner")] UINT32 Data);
|
||||||
|
[WmiMethodId(53), Implemented, Description("Set DDS Control Owner")] void SetDDSControlOwner ([in, Description("Set DDS Control Owner")] UINT32 Data);
|
||||||
|
[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);
|
||||||
|
[WmiMethodId(55), Implemented, Description("Get Real Thremal Mode")] void GetThermalMode ([out, Description("Real Thremal Mode")] UINT32 Data);
|
||||||
|
[WmiMethodId(56), Implemented, Description("Get the OC switch status in BIOS")] void GetBIOSOCMode ([out, Description("OC Mode")] UINT32 Data);
|
||||||
|
[WmiMethodId(59), Implemented, Description("Get hardware info support version")] void GetHardwareInfoSupportVersion ([out, Description("version")] UINT32 Data);
|
||||||
|
[WmiMethodId(60), Implemented, Description("Get Cpu core 0 max frequency")] void GetCpuFrequency ([out, Description("frequency")] UINT32 Data);
|
||||||
|
[WmiMethodId(62), Implemented, Description("Check the Adapter type fit for OC")] void IsACFitForOC ([out, Description("AC check result")] UINT32 Data);
|
||||||
|
[WmiMethodId(63), Implemented, Description("Is support IGPU mode")] void IsSupportIGPUMode ([out, Description("IGPU modes")] UINT32 Data);
|
||||||
|
[WmiMethodId(64), Implemented, Description("Get IGPU Mode Status")] void GetIGPUModeStatus([out, Description("IGPU Mode Status")] UINT32 Data);
|
||||||
|
[WmiMethodId(65), Implemented, Description("Set IGPU Mode")] void SetIGPUModeStatus([in, Description("IGPU Mode")] UINT32 mode, [out, Description("return code")] UINT32 Data);
|
||||||
|
[WmiMethodId(66), Implemented, Description("Notify DGPU Status")] void NotifyDGPUStatus([in, Description("DGPU status")] UINT32 status, [out, Description("return code")] UINT32 Data);
|
||||||
|
[WmiMethodId(67), Implemented, Description("Is changed Y log")] void IsChangedYLog([out, Description("Is changed Y Log")] UINT32 Data);
|
||||||
|
[WmiMethodId(68), Implemented, Description("Get DGPU Hardwawre ID")] void GetDGPUHWId([out, Description("Get DGPU Hardware ID")] string Data);
|
||||||
|
};
|
||||||
|
|
||||||
|
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Definition of CPU OC parameter list"), guid("{B7F3CA0A-ACDC-42D2-9217-77C6C628FBD2}")]
|
||||||
|
class LENOVO_GAMEZONE_CPU_OC_DATA {
|
||||||
|
[key, read] string InstanceName;
|
||||||
|
[read] boolean Active;
|
||||||
|
|
||||||
|
[WmiDataId(1), read, Description("OC tune id.")] uint32 Tuneid;
|
||||||
|
[WmiDataId(2), read, Description("Default value.")] uint32 DefaultValue;
|
||||||
|
[WmiDataId(3), read, Description("OC Value.")] uint32 OCValue;
|
||||||
|
[WmiDataId(4), read, Description("Min Value.")] uint32 MinValue;
|
||||||
|
[WmiDataId(5), read, Description("Max Value.")] uint32 MaxValue;
|
||||||
|
[WmiDataId(6), read, Description("Scale Value.")] uint32 ScaleValue;
|
||||||
|
[WmiDataId(7), read, Description("OC Order id.")] uint32 OCOrderid;
|
||||||
|
[WmiDataId(8), read, Description("NON-OC Order id.")] uint32 NOCOrderid;
|
||||||
|
[WmiDataId(9), read, Description("Delay time in ms.")] uint32 Interval;
|
||||||
|
};
|
||||||
|
|
||||||
|
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Definition of GPU OC parameter list"), guid("{887B54E2-DDDC-4B2C-8B88-68A26A8835D0}")]
|
||||||
|
class LENOVO_GAMEZONE_GPU_OC_DATA {
|
||||||
|
[key, read] string InstanceName;
|
||||||
|
[read] boolean Active;
|
||||||
|
|
||||||
|
[WmiDataId(1), read, Description("P-State ID.")] uint32 PStateID;
|
||||||
|
[WmiDataId(2), read, Description("CLOCK ID.")] uint32 ClockID;
|
||||||
|
[WmiDataId(3), read, Description("Default value.")] uint32 defaultvalue;
|
||||||
|
[WmiDataId(4), read, Description("OC Offset freqency.")] uint32 OCOffsetFreq;
|
||||||
|
[WmiDataId(5), read, Description("OC Min offset value.")] uint32 OCMinOffset;
|
||||||
|
[WmiDataId(6), read, Description("OC Max offset value.")] uint32 OCMaxOffset;
|
||||||
|
[WmiDataId(7), read, Description("OC Offset Scale.")] uint32 OCOffsetScale;
|
||||||
|
[WmiDataId(8), read, Description("OC Order id.")] uint32 OCOrderid;
|
||||||
|
[WmiDataId(9), read, Description("NON-OC Order id.")] uint32 NOCOrderid;
|
||||||
|
};
|
||||||
|
|
||||||
|
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Fancooling finish event"), guid("{BC72A435-E8C1-4275-B3E2-D8B8074ABA59}")]
|
||||||
|
class LENOVO_GAMEZONE_FAN_COOLING_EVENT: WMIEvent {
|
||||||
|
[key, read] string InstanceName;
|
||||||
|
[read] boolean Active;
|
||||||
|
|
||||||
|
[WmiDataId(1), read, Description("Fancooling clean finish event")] uint32 EventId;
|
||||||
|
};
|
||||||
|
|
||||||
|
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Smart Fan mode change event"), guid("{D320289E-8FEA-41E0-86F9-611D83151B5F}")]
|
||||||
|
class LENOVO_GAMEZONE_SMART_FAN_MODE_EVENT: WMIEvent {
|
||||||
|
[key, read] string InstanceName;
|
||||||
|
[read] boolean Active;
|
||||||
|
|
||||||
|
[WmiDataId(1), read, Description("Smart Fan Mode change event")] uint32 mode;
|
||||||
|
[WmiDataId(2), read, Description("version of FN+Q")] uint32 version;
|
||||||
|
};
|
||||||
|
|
||||||
|
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Smart Fan setting mode change event"), guid("{D320289E-8FEA-41E1-86F9-611D83151B5F}")]
|
||||||
|
class LENOVO_GAMEZONE_SMART_FAN_SETTING_EVENT: WMIEvent {
|
||||||
|
[key, read] string InstanceName;
|
||||||
|
[read] boolean Active;
|
||||||
|
|
||||||
|
[WmiDataId(1), read, Description("Smart Fan Setting mode change event")] uint32 mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("POWER CHARGE MODE Change EVENT"), guid("{D320289E-8FEA-41E0-86F9-711D83151B5F}")]
|
||||||
|
class LENOVO_GAMEZONE_POWER_CHARGE_MODE_EVENT: WMIEvent {
|
||||||
|
[key, read] string InstanceName;
|
||||||
|
[read] boolean Active;
|
||||||
|
|
||||||
|
[WmiDataId(1), read, Description("POWER CHARGE MODE Change EVENT")] uint32 mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Thermal Mode Real Mode change event"), guid("{D320289E-8FEA-41E0-86F9-911D83151B5F}")]
|
||||||
|
class LENOVO_GAMEZONE_THERMAL_MODE_EVENT: WMIEvent {
|
||||||
|
[key, read] string InstanceName;
|
||||||
|
[read] boolean Active;
|
||||||
|
|
||||||
|
[WmiDataId(1), read, Description("Thermal Mode Real Mode")] uint32 mode;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
===========================================================
|
||||||
|
Lenovo WMI Interface Other Mode Driver (lenovo-wmi-other)
|
||||||
|
===========================================================
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
Lenovo WMI Other Mode interface is broken up into multiple GUIDs,
|
||||||
|
The primary Other Mode interface provides advanced power tuning features
|
||||||
|
such as Package Power Tracking (PPT). It is paired with multiple data block
|
||||||
|
GUIDs that provide context for the various methods.
|
||||||
|
|
||||||
|
|
||||||
|
Other Mode
|
||||||
|
----------
|
||||||
|
|
||||||
|
WMI GUID ``DC2A8805-3A8C-41BA-A6F7-092E0089CD3B``
|
||||||
|
|
||||||
|
The Other Mode WMI interface uses the firmware_attributes class to expose
|
||||||
|
various WMI attributes provided by the interface in the sysfs. This enables
|
||||||
|
CPU and GPU power limit tuning as well as various other attributes for
|
||||||
|
devices that fall under the "Gaming Series" of Lenovo devices. Each
|
||||||
|
attribute exposed by the Other Mode interface has corresponding
|
||||||
|
capability data blocks which allow the driver to probe details about the
|
||||||
|
attribute. Each attribute has multiple pages, one for each of the platform
|
||||||
|
profiles managed by the Gamezone interface. Attributes are exposed in sysfs
|
||||||
|
under the following path:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
/sys/class/firmware-attributes/lenovo-wmi-other/attributes/<attribute>/
|
||||||
|
|
||||||
|
LENOVO_CAPABILITY_DATA_01
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
WMI GUID ``7A8F5407-CB67-4D6E-B547-39B3BE018154``
|
||||||
|
|
||||||
|
The LENOVO_CAPABILITY_DATA_01 interface provides information on various
|
||||||
|
power limits of integrated CPU and GPU components.
|
||||||
|
|
||||||
|
Each attribute has the following properties:
|
||||||
|
- current_value
|
||||||
|
- default_value
|
||||||
|
- display_name
|
||||||
|
- max_value
|
||||||
|
- min_value
|
||||||
|
- scalar_increment
|
||||||
|
- type
|
||||||
|
|
||||||
|
The following attributes are implemented:
|
||||||
|
- ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit
|
||||||
|
- ppt_pl2_sppt: Platform Profile Tracking Slow Package Power Tracking
|
||||||
|
- ppt_pl3_fppt: Platform Profile Tracking Fast Package Power Tracking
|
||||||
|
|
||||||
|
|
||||||
|
WMI interface description
|
||||||
|
=========================
|
||||||
|
|
||||||
|
The WMI interface description can be decoded from the embedded binary MOF (bmof)
|
||||||
|
data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO_OTHER_METHOD class"), guid("{dc2a8805-3a8c-41ba-a6f7-092e0089cd3b}")]
|
||||||
|
class LENOVO_OTHER_METHOD {
|
||||||
|
[key, read] string InstanceName;
|
||||||
|
[read] boolean Active;
|
||||||
|
|
||||||
|
[WmiMethodId(17), Implemented, Description("Get Feature Value ")] void GetFeatureValue([in] uint32 IDs, [out] uint32 value);
|
||||||
|
[WmiMethodId(18), Implemented, Description("Set Feature Value ")] void SetFeatureValue([in] uint32 IDs, [in] uint32 value);
|
||||||
|
[WmiMethodId(19), Implemented, Description("Get Data By Command ")] void GetDataByCommand([in] uint32 IDs, [in] uint32 Command, [out] uint32 DataSize, [out, WmiSizeIs("DataSize")] uint32 Data[]);
|
||||||
|
[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[]);
|
||||||
|
};
|
||||||
|
|
||||||
|
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO CAPABILITY DATA 00"), guid("{362a3afe-3d96-4665-8530-96dad5bb300e}")]
|
||||||
|
class LENOVO_CAPABILITY_DATA_00 {
|
||||||
|
[key, read] string InstanceName;
|
||||||
|
[read] boolean Active;
|
||||||
|
|
||||||
|
[WmiDataId(1), read, Description(" IDs.")] uint32 IDs;
|
||||||
|
[WmiDataId(2), read, Description("Capability.")] uint32 Capability;
|
||||||
|
[WmiDataId(3), read, Description("Capability Default Value.")] uint32 DefaultValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO CAPABILITY DATA 01"), guid("{7a8f5407-cb67-4d6e-b547-39b3be018154}")]
|
||||||
|
class LENOVO_CAPABILITY_DATA_01 {
|
||||||
|
[key, read] string InstanceName;
|
||||||
|
[read] boolean Active;
|
||||||
|
|
||||||
|
[WmiDataId(1), read, Description(" IDs.")] uint32 IDs;
|
||||||
|
[WmiDataId(2), read, Description("Capability.")] uint32 Capability;
|
||||||
|
[WmiDataId(3), read, Description("Default Value.")] uint32 DefaultValue;
|
||||||
|
[WmiDataId(4), read, Description("Step.")] uint32 Step;
|
||||||
|
[WmiDataId(5), read, Description("Minimum Value.")] uint32 MinValue;
|
||||||
|
[WmiDataId(6), read, Description("Maximum Value.")] uint32 MaxValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO CAPABILITY DATA 02"), guid("{bbf1f790-6c2f-422b-bc8c-4e7369c7f6ab}")]
|
||||||
|
class LENOVO_CAPABILITY_DATA_02 {
|
||||||
|
[key, read] string InstanceName;
|
||||||
|
[read] boolean Active;
|
||||||
|
|
||||||
|
[WmiDataId(1), read, Description(" IDs.")] uint32 IDs;
|
||||||
|
[WmiDataId(2), read, Description("Capability.")] uint32 Capability;
|
||||||
|
[WmiDataId(3), read, Description("Data Size.")] uint32 DataSize;
|
||||||
|
[WmiDataId(4), read, Description("Default Value"), WmiSizeIs("DataSize")] uint8 DefaultValue[];
|
||||||
|
};
|
||||||
21
MAINTAINERS
21
MAINTAINERS
|
|
@ -11634,7 +11634,7 @@ M: Ike Panhc <ikepanhc@gmail.com>
|
||||||
L: platform-driver-x86@vger.kernel.org
|
L: platform-driver-x86@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
W: http://launchpad.net/ideapad-laptop
|
W: http://launchpad.net/ideapad-laptop
|
||||||
F: drivers/platform/x86/ideapad-laptop.c
|
F: drivers/platform/x86/lenovo/ideapad-laptop.c
|
||||||
|
|
||||||
IDEAPAD LAPTOP SLIDEBAR DRIVER
|
IDEAPAD LAPTOP SLIDEBAR DRIVER
|
||||||
M: Andrey Moiseev <o2g.org.ru@gmail.com>
|
M: Andrey Moiseev <o2g.org.ru@gmail.com>
|
||||||
|
|
@ -12407,6 +12407,8 @@ F: include/linux/mfd/intel_soc_pmic*
|
||||||
INTEL PMT DRIVERS
|
INTEL PMT DRIVERS
|
||||||
M: David E. Box <david.e.box@linux.intel.com>
|
M: David E. Box <david.e.box@linux.intel.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
|
F: Documentation/ABI/testing/sysfs-class-intel_pmt
|
||||||
|
F: Documentation/ABI/testing/sysfs-class-intel_pmt-features
|
||||||
F: drivers/platform/x86/intel/pmt/
|
F: drivers/platform/x86/intel/pmt/
|
||||||
|
|
||||||
INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT
|
INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT
|
||||||
|
|
@ -13697,11 +13699,20 @@ S: Maintained
|
||||||
W: http://legousb.sourceforge.net/
|
W: http://legousb.sourceforge.net/
|
||||||
F: drivers/usb/misc/legousbtower.c
|
F: drivers/usb/misc/legousbtower.c
|
||||||
|
|
||||||
|
LENOVO drivers
|
||||||
|
M: Mark Pearson <mpearson-lenovo@squebb.ca>
|
||||||
|
M: Derek J. Clark <derekjohn.clark@gmail.com>
|
||||||
|
L: platform-driver-x86@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: Documentation/wmi/devices/lenovo-wmi-gamezone.rst
|
||||||
|
F: Documentation/wmi/devices/lenovo-wmi-other.rst
|
||||||
|
F: drivers/platform/x86/lenovo/*
|
||||||
|
|
||||||
LENOVO WMI HOTKEY UTILITIES DRIVER
|
LENOVO WMI HOTKEY UTILITIES DRIVER
|
||||||
M: Jackie Dong <xy-jackie@139.com>
|
M: Jackie Dong <xy-jackie@139.com>
|
||||||
L: platform-driver-x86@vger.kernel.org
|
L: platform-driver-x86@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/platform/x86/lenovo-wmi-hotkey-utilities.c
|
F: drivers/platform/x86/lenovo/wmi-hotkey-utilities.c
|
||||||
|
|
||||||
LETSKETCH HID TABLET DRIVER
|
LETSKETCH HID TABLET DRIVER
|
||||||
M: Hans de Goede <hansg@kernel.org>
|
M: Hans de Goede <hansg@kernel.org>
|
||||||
|
|
@ -24729,14 +24740,14 @@ S: Maintained
|
||||||
W: http://ibm-acpi.sourceforge.net
|
W: http://ibm-acpi.sourceforge.net
|
||||||
W: http://thinkwiki.org/wiki/Ibm-acpi
|
W: http://thinkwiki.org/wiki/Ibm-acpi
|
||||||
T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git
|
T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git
|
||||||
F: drivers/platform/x86/thinkpad_acpi.c
|
F: drivers/platform/x86/lenovo/thinkpad_acpi.c
|
||||||
|
|
||||||
THINKPAD LMI DRIVER
|
THINKPAD LMI DRIVER
|
||||||
M: Mark Pearson <markpearson@lenovo.com>
|
M: Mark Pearson <mpearson-lenovo@squebb.ca>
|
||||||
L: platform-driver-x86@vger.kernel.org
|
L: platform-driver-x86@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: Documentation/ABI/testing/sysfs-class-firmware-attributes
|
F: Documentation/ABI/testing/sysfs-class-firmware-attributes
|
||||||
F: drivers/platform/x86/think-lmi.?
|
F: drivers/platform/x86/lenovo/think-lmi.?
|
||||||
|
|
||||||
THP7312 ISP DRIVER
|
THP7312 ISP DRIVER
|
||||||
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||||
|
|
|
||||||
|
|
@ -59,18 +59,6 @@ struct telemetry_plt_config {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct telemetry_core_ops {
|
struct telemetry_core_ops {
|
||||||
int (*get_sampling_period)(u8 *pss_min_period, u8 *pss_max_period,
|
|
||||||
u8 *ioss_min_period, u8 *ioss_max_period);
|
|
||||||
|
|
||||||
int (*get_eventconfig)(struct telemetry_evtconfig *pss_evtconfig,
|
|
||||||
struct telemetry_evtconfig *ioss_evtconfig,
|
|
||||||
int pss_len, int ioss_len);
|
|
||||||
|
|
||||||
int (*update_events)(struct telemetry_evtconfig pss_evtconfig,
|
|
||||||
struct telemetry_evtconfig ioss_evtconfig);
|
|
||||||
|
|
||||||
int (*set_sampling_period)(u8 pss_period, u8 ioss_period);
|
|
||||||
|
|
||||||
int (*get_trace_verbosity)(enum telemetry_unit telem_unit,
|
int (*get_trace_verbosity)(enum telemetry_unit telem_unit,
|
||||||
u32 *verbosity);
|
u32 *verbosity);
|
||||||
|
|
||||||
|
|
@ -84,11 +72,6 @@ struct telemetry_core_ops {
|
||||||
int (*read_eventlog)(enum telemetry_unit telem_unit,
|
int (*read_eventlog)(enum telemetry_unit telem_unit,
|
||||||
struct telemetry_evtlog *evtlog,
|
struct telemetry_evtlog *evtlog,
|
||||||
int len, int log_all_evts);
|
int len, int log_all_evts);
|
||||||
|
|
||||||
int (*add_events)(u8 num_pss_evts, u8 num_ioss_evts,
|
|
||||||
u32 *pss_evtmap, u32 *ioss_evtmap);
|
|
||||||
|
|
||||||
int (*reset_events)(void);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int telemetry_set_pltdata(const struct telemetry_core_ops *ops,
|
int telemetry_set_pltdata(const struct telemetry_core_ops *ops,
|
||||||
|
|
@ -101,35 +84,15 @@ struct telemetry_plt_config *telemetry_get_pltdata(void);
|
||||||
int telemetry_get_evtname(enum telemetry_unit telem_unit,
|
int telemetry_get_evtname(enum telemetry_unit telem_unit,
|
||||||
const char **name, int len);
|
const char **name, int len);
|
||||||
|
|
||||||
int telemetry_update_events(struct telemetry_evtconfig pss_evtconfig,
|
|
||||||
struct telemetry_evtconfig ioss_evtconfig);
|
|
||||||
|
|
||||||
int telemetry_add_events(u8 num_pss_evts, u8 num_ioss_evts,
|
|
||||||
u32 *pss_evtmap, u32 *ioss_evtmap);
|
|
||||||
|
|
||||||
int telemetry_reset_events(void);
|
|
||||||
|
|
||||||
int telemetry_get_eventconfig(struct telemetry_evtconfig *pss_config,
|
|
||||||
struct telemetry_evtconfig *ioss_config,
|
|
||||||
int pss_len, int ioss_len);
|
|
||||||
|
|
||||||
int telemetry_read_events(enum telemetry_unit telem_unit,
|
int telemetry_read_events(enum telemetry_unit telem_unit,
|
||||||
struct telemetry_evtlog *evtlog, int len);
|
struct telemetry_evtlog *evtlog, int len);
|
||||||
|
|
||||||
int telemetry_raw_read_events(enum telemetry_unit telem_unit,
|
|
||||||
struct telemetry_evtlog *evtlog, int len);
|
|
||||||
|
|
||||||
int telemetry_read_eventlog(enum telemetry_unit telem_unit,
|
int telemetry_read_eventlog(enum telemetry_unit telem_unit,
|
||||||
struct telemetry_evtlog *evtlog, int len);
|
struct telemetry_evtlog *evtlog, int len);
|
||||||
|
|
||||||
int telemetry_raw_read_eventlog(enum telemetry_unit telem_unit,
|
int telemetry_raw_read_eventlog(enum telemetry_unit telem_unit,
|
||||||
struct telemetry_evtlog *evtlog, int len);
|
struct telemetry_evtlog *evtlog, int len);
|
||||||
|
|
||||||
int telemetry_get_sampling_period(u8 *pss_min_period, u8 *pss_max_period,
|
|
||||||
u8 *ioss_min_period, u8 *ioss_max_period);
|
|
||||||
|
|
||||||
int telemetry_set_sampling_period(u8 pss_period, u8 ioss_period);
|
|
||||||
|
|
||||||
int telemetry_set_trace_verbosity(enum telemetry_unit telem_unit,
|
int telemetry_set_trace_verbosity(enum telemetry_unit telem_unit,
|
||||||
u32 verbosity);
|
u32 verbosity);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
#define BMG_DEVICE_ID 0xE2F8
|
#define BMG_DEVICE_ID 0xE2F8
|
||||||
|
|
||||||
static struct intel_vsec_header bmg_telemetry = {
|
static struct intel_vsec_header bmg_telemetry = {
|
||||||
|
.rev = 1,
|
||||||
.length = 0x10,
|
.length = 0x10,
|
||||||
.id = VSEC_ID_TELEMETRY,
|
.id = VSEC_ID_TELEMETRY,
|
||||||
.num_entries = 2,
|
.num_entries = 2,
|
||||||
|
|
@ -32,28 +33,19 @@ static struct intel_vsec_header bmg_telemetry = {
|
||||||
.offset = BMG_DISCOVERY_OFFSET,
|
.offset = BMG_DISCOVERY_OFFSET,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct intel_vsec_header bmg_punit_crashlog = {
|
static struct intel_vsec_header bmg_crashlog = {
|
||||||
|
.rev = 1,
|
||||||
.length = 0x10,
|
.length = 0x10,
|
||||||
.id = VSEC_ID_CRASHLOG,
|
.id = VSEC_ID_CRASHLOG,
|
||||||
.num_entries = 1,
|
.num_entries = 2,
|
||||||
.entry_size = 4,
|
.entry_size = 6,
|
||||||
.tbir = 0,
|
.tbir = 0,
|
||||||
.offset = BMG_DISCOVERY_OFFSET + 0x60,
|
.offset = BMG_DISCOVERY_OFFSET + 0x60,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct intel_vsec_header bmg_oobmsm_crashlog = {
|
|
||||||
.length = 0x10,
|
|
||||||
.id = VSEC_ID_CRASHLOG,
|
|
||||||
.num_entries = 1,
|
|
||||||
.entry_size = 4,
|
|
||||||
.tbir = 0,
|
|
||||||
.offset = BMG_DISCOVERY_OFFSET + 0x78,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct intel_vsec_header *bmg_capabilities[] = {
|
static struct intel_vsec_header *bmg_capabilities[] = {
|
||||||
&bmg_telemetry,
|
&bmg_telemetry,
|
||||||
&bmg_punit_crashlog,
|
&bmg_crashlog,
|
||||||
&bmg_oobmsm_crashlog,
|
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -191,50 +191,16 @@ void yoga_c630_ec_unregister_notify(struct yoga_c630_ec *ec, struct notifier_blo
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(yoga_c630_ec_unregister_notify);
|
EXPORT_SYMBOL_GPL(yoga_c630_ec_unregister_notify);
|
||||||
|
|
||||||
static void yoga_c630_aux_release(struct device *dev)
|
|
||||||
{
|
|
||||||
struct auxiliary_device *adev = to_auxiliary_dev(dev);
|
|
||||||
|
|
||||||
kfree(adev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void yoga_c630_aux_remove(void *data)
|
|
||||||
{
|
|
||||||
struct auxiliary_device *adev = data;
|
|
||||||
|
|
||||||
auxiliary_device_delete(adev);
|
|
||||||
auxiliary_device_uninit(adev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int yoga_c630_aux_init(struct device *parent, const char *name,
|
static int yoga_c630_aux_init(struct device *parent, const char *name,
|
||||||
struct yoga_c630_ec *ec)
|
struct yoga_c630_ec *ec)
|
||||||
{
|
{
|
||||||
struct auxiliary_device *adev;
|
struct auxiliary_device *adev;
|
||||||
int ret;
|
|
||||||
|
|
||||||
adev = kzalloc(sizeof(*adev), GFP_KERNEL);
|
adev = devm_auxiliary_device_create(parent, name, ec);
|
||||||
if (!adev)
|
if (!adev)
|
||||||
return -ENOMEM;
|
return -ENODEV;
|
||||||
|
|
||||||
adev->name = name;
|
return 0;
|
||||||
adev->id = 0;
|
|
||||||
adev->dev.parent = parent;
|
|
||||||
adev->dev.release = yoga_c630_aux_release;
|
|
||||||
adev->dev.platform_data = ec;
|
|
||||||
|
|
||||||
ret = auxiliary_device_init(adev);
|
|
||||||
if (ret) {
|
|
||||||
kfree(adev);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = auxiliary_device_add(adev);
|
|
||||||
if (ret) {
|
|
||||||
auxiliary_device_uninit(adev);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return devm_add_action_or_reset(parent, yoga_c630_aux_remove, adev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int yoga_c630_ec_probe(struct i2c_client *client)
|
static int yoga_c630_ec_probe(struct i2c_client *client)
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,15 @@ config ACPI_WMI
|
||||||
It is safe to enable this driver even if your DSDT doesn't define
|
It is safe to enable this driver even if your DSDT doesn't define
|
||||||
any ACPI-WMI devices.
|
any ACPI-WMI devices.
|
||||||
|
|
||||||
|
config ACPI_WMI_LEGACY_DEVICE_NAMES
|
||||||
|
bool "Use legacy WMI device naming scheme"
|
||||||
|
depends on ACPI_WMI
|
||||||
|
help
|
||||||
|
Say Y here to force the WMI driver core to use the old WMI device naming
|
||||||
|
scheme when creating WMI devices. Doing so might be necessary for some
|
||||||
|
userspace applications but will cause the registration of WMI devices with
|
||||||
|
the same GUID to fail in some corner cases.
|
||||||
|
|
||||||
config WMI_BMOF
|
config WMI_BMOF
|
||||||
tristate "WMI embedded Binary MOF driver"
|
tristate "WMI embedded Binary MOF driver"
|
||||||
depends on ACPI_WMI
|
depends on ACPI_WMI
|
||||||
|
|
@ -120,32 +129,6 @@ config GIGABYTE_WMI
|
||||||
To compile this driver as a module, choose M here: the module will
|
To compile this driver as a module, choose M here: the module will
|
||||||
be called gigabyte-wmi.
|
be called gigabyte-wmi.
|
||||||
|
|
||||||
config YOGABOOK
|
|
||||||
tristate "Lenovo Yoga Book tablet key driver"
|
|
||||||
depends on ACPI_WMI
|
|
||||||
depends on INPUT
|
|
||||||
depends on I2C
|
|
||||||
select LEDS_CLASS
|
|
||||||
select NEW_LEDS
|
|
||||||
help
|
|
||||||
Say Y here if you want to support the 'Pen' key and keyboard backlight
|
|
||||||
control on the Lenovo Yoga Book tablets.
|
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the module will
|
|
||||||
be called lenovo-yogabook.
|
|
||||||
|
|
||||||
config YT2_1380
|
|
||||||
tristate "Lenovo Yoga Tablet 2 1380 fast charge driver"
|
|
||||||
depends on SERIAL_DEV_BUS
|
|
||||||
depends on EXTCON
|
|
||||||
depends on ACPI
|
|
||||||
help
|
|
||||||
Say Y here to enable support for the custom fast charging protocol
|
|
||||||
found on the Lenovo Yoga Tablet 2 1380F / 1380L models.
|
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the module will
|
|
||||||
be called lenovo-yogabook.
|
|
||||||
|
|
||||||
config ACERHDF
|
config ACERHDF
|
||||||
tristate "Acer Aspire One temperature and fan driver"
|
tristate "Acer Aspire One temperature and fan driver"
|
||||||
depends on ACPI_EC && THERMAL
|
depends on ACPI_EC && THERMAL
|
||||||
|
|
@ -459,43 +442,6 @@ config IBM_RTL
|
||||||
state = 0 (BIOS SMIs on)
|
state = 0 (BIOS SMIs on)
|
||||||
state = 1 (BIOS SMIs off)
|
state = 1 (BIOS SMIs off)
|
||||||
|
|
||||||
config IDEAPAD_LAPTOP
|
|
||||||
tristate "Lenovo IdeaPad Laptop Extras"
|
|
||||||
depends on ACPI
|
|
||||||
depends on RFKILL && INPUT
|
|
||||||
depends on SERIO_I8042
|
|
||||||
depends on BACKLIGHT_CLASS_DEVICE
|
|
||||||
depends on ACPI_VIDEO || ACPI_VIDEO = n
|
|
||||||
depends on ACPI_WMI || ACPI_WMI = n
|
|
||||||
select ACPI_PLATFORM_PROFILE
|
|
||||||
select INPUT_SPARSEKMAP
|
|
||||||
select NEW_LEDS
|
|
||||||
select LEDS_CLASS
|
|
||||||
help
|
|
||||||
This is a driver for Lenovo IdeaPad netbooks contains drivers for
|
|
||||||
rfkill switch, hotkey, fan control and backlight control.
|
|
||||||
|
|
||||||
config LENOVO_WMI_HOTKEY_UTILITIES
|
|
||||||
tristate "Lenovo Hotkey Utility WMI extras driver"
|
|
||||||
depends on ACPI_WMI
|
|
||||||
select NEW_LEDS
|
|
||||||
select LEDS_CLASS
|
|
||||||
imply IDEAPAD_LAPTOP
|
|
||||||
help
|
|
||||||
This driver provides WMI support for Lenovo customized hotkeys function,
|
|
||||||
such as LED control for audio/mic mute event for Ideapad, YOGA, XiaoXin,
|
|
||||||
Gaming, ThinkBook and so on.
|
|
||||||
|
|
||||||
config LENOVO_YMC
|
|
||||||
tristate "Lenovo Yoga Tablet Mode Control"
|
|
||||||
depends on ACPI_WMI
|
|
||||||
depends on INPUT
|
|
||||||
depends on IDEAPAD_LAPTOP
|
|
||||||
select INPUT_SPARSEKMAP
|
|
||||||
help
|
|
||||||
This driver maps the Tablet Mode Control switch to SW_TABLET_MODE input
|
|
||||||
events for Lenovo Yoga notebooks.
|
|
||||||
|
|
||||||
config SENSORS_HDAPS
|
config SENSORS_HDAPS
|
||||||
tristate "Thinkpad Hard Drive Active Protection System (hdaps)"
|
tristate "Thinkpad Hard Drive Active Protection System (hdaps)"
|
||||||
depends on INPUT
|
depends on INPUT
|
||||||
|
|
@ -514,160 +460,8 @@ config SENSORS_HDAPS
|
||||||
Say Y here if you have an applicable laptop and want to experience
|
Say Y here if you have an applicable laptop and want to experience
|
||||||
the awesome power of hdaps.
|
the awesome power of hdaps.
|
||||||
|
|
||||||
config THINKPAD_ACPI
|
|
||||||
tristate "ThinkPad ACPI Laptop Extras"
|
|
||||||
depends on ACPI_EC
|
|
||||||
depends on ACPI_BATTERY
|
|
||||||
depends on INPUT
|
|
||||||
depends on RFKILL || RFKILL = n
|
|
||||||
depends on ACPI_VIDEO || ACPI_VIDEO = n
|
|
||||||
depends on BACKLIGHT_CLASS_DEVICE
|
|
||||||
depends on I2C
|
|
||||||
depends on DRM
|
|
||||||
select ACPI_PLATFORM_PROFILE
|
|
||||||
select DRM_PRIVACY_SCREEN
|
|
||||||
select HWMON
|
|
||||||
select NVRAM
|
|
||||||
select NEW_LEDS
|
|
||||||
select LEDS_CLASS
|
|
||||||
select INPUT_SPARSEKMAP
|
|
||||||
help
|
|
||||||
This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
|
|
||||||
support for Fn-Fx key combinations, Bluetooth control, video
|
|
||||||
output switching, ThinkLight control, UltraBay eject and more.
|
|
||||||
For more information about this driver see
|
|
||||||
<file:Documentation/admin-guide/laptops/thinkpad-acpi.rst> and
|
|
||||||
<http://ibm-acpi.sf.net/> .
|
|
||||||
|
|
||||||
This driver was formerly known as ibm-acpi.
|
|
||||||
|
|
||||||
Extra functionality will be available if the rfkill (CONFIG_RFKILL)
|
|
||||||
and/or ALSA (CONFIG_SND) subsystems are available in the kernel.
|
|
||||||
Note that if you want ThinkPad-ACPI to be built-in instead of
|
|
||||||
modular, ALSA and rfkill will also have to be built-in.
|
|
||||||
|
|
||||||
If you have an IBM or Lenovo ThinkPad laptop, say Y or M here.
|
|
||||||
|
|
||||||
config THINKPAD_ACPI_ALSA_SUPPORT
|
|
||||||
bool "Console audio control ALSA interface"
|
|
||||||
depends on THINKPAD_ACPI
|
|
||||||
depends on SND
|
|
||||||
depends on SND = y || THINKPAD_ACPI = SND
|
|
||||||
default y
|
|
||||||
help
|
|
||||||
Enables monitoring of the built-in console audio output control
|
|
||||||
(headphone and speakers), which is operated by the mute and (in
|
|
||||||
some ThinkPad models) volume hotkeys.
|
|
||||||
|
|
||||||
If this option is enabled, ThinkPad-ACPI will export an ALSA card
|
|
||||||
with a single read-only mixer control, which should be used for
|
|
||||||
on-screen-display feedback purposes by the Desktop Environment.
|
|
||||||
|
|
||||||
Optionally, the driver will also allow software control (the
|
|
||||||
ALSA mixer will be made read-write). Please refer to the driver
|
|
||||||
documentation for details.
|
|
||||||
|
|
||||||
All IBM models have both volume and mute control. Newer Lenovo
|
|
||||||
models only have mute control (the volume hotkeys are just normal
|
|
||||||
keys and volume control is done through the main HDA mixer).
|
|
||||||
|
|
||||||
config THINKPAD_ACPI_DEBUGFACILITIES
|
|
||||||
bool "Maintainer debug facilities"
|
|
||||||
depends on THINKPAD_ACPI
|
|
||||||
help
|
|
||||||
Enables extra stuff in the thinkpad-acpi which is completely useless
|
|
||||||
for normal use. Read the driver source to find out what it does.
|
|
||||||
|
|
||||||
Say N here, unless you were told by a kernel maintainer to do
|
|
||||||
otherwise.
|
|
||||||
|
|
||||||
config THINKPAD_ACPI_DEBUG
|
|
||||||
bool "Verbose debug mode"
|
|
||||||
depends on THINKPAD_ACPI
|
|
||||||
help
|
|
||||||
Enables extra debugging information, at the expense of a slightly
|
|
||||||
increase in driver size.
|
|
||||||
|
|
||||||
If you are not sure, say N here.
|
|
||||||
|
|
||||||
config THINKPAD_ACPI_UNSAFE_LEDS
|
|
||||||
bool "Allow control of important LEDs (unsafe)"
|
|
||||||
depends on THINKPAD_ACPI
|
|
||||||
help
|
|
||||||
Overriding LED state on ThinkPads can mask important
|
|
||||||
firmware alerts (like critical battery condition), or misled
|
|
||||||
the user into damaging the hardware (undocking or ejecting
|
|
||||||
the bay while buses are still active), etc.
|
|
||||||
|
|
||||||
LED control on the ThinkPad is write-only (with very few
|
|
||||||
exceptions on very ancient models), which makes it
|
|
||||||
impossible to know beforehand if important information will
|
|
||||||
be lost when one changes LED state.
|
|
||||||
|
|
||||||
Users that know what they are doing can enable this option
|
|
||||||
and the driver will allow control of every LED, including
|
|
||||||
the ones on the dock stations.
|
|
||||||
|
|
||||||
Never enable this option on a distribution kernel.
|
|
||||||
|
|
||||||
Say N here, unless you are building a kernel for your own
|
|
||||||
use, and need to control the important firmware LEDs.
|
|
||||||
|
|
||||||
config THINKPAD_ACPI_VIDEO
|
|
||||||
bool "Video output control support"
|
|
||||||
depends on THINKPAD_ACPI
|
|
||||||
default y
|
|
||||||
help
|
|
||||||
Allows the thinkpad_acpi driver to provide an interface to control
|
|
||||||
the various video output ports.
|
|
||||||
|
|
||||||
This feature often won't work well, depending on ThinkPad model,
|
|
||||||
display state, video output devices in use, whether there is a X
|
|
||||||
server running, phase of the moon, and the current mood of
|
|
||||||
Schroedinger's cat. If you can use X.org's RandR to control
|
|
||||||
your ThinkPad's video output ports instead of this feature,
|
|
||||||
don't think twice: do it and say N here to save memory and avoid
|
|
||||||
bad interactions with X.org.
|
|
||||||
|
|
||||||
NOTE: access to this feature is limited to processes with the
|
|
||||||
CAP_SYS_ADMIN capability, to avoid local DoS issues in platforms
|
|
||||||
where it interacts badly with X.org.
|
|
||||||
|
|
||||||
If you are not sure, say Y here but do try to check if you could
|
|
||||||
be using X.org RandR instead.
|
|
||||||
|
|
||||||
config THINKPAD_ACPI_HOTKEY_POLL
|
|
||||||
bool "Support NVRAM polling for hot keys"
|
|
||||||
depends on THINKPAD_ACPI
|
|
||||||
default y
|
|
||||||
help
|
|
||||||
Some thinkpad models benefit from NVRAM polling to detect a few of
|
|
||||||
the hot key press events. If you know your ThinkPad model does not
|
|
||||||
need to do NVRAM polling to support any of the hot keys you use,
|
|
||||||
unselecting this option will save about 1kB of memory.
|
|
||||||
|
|
||||||
ThinkPads T40 and newer, R52 and newer, and X31 and newer are
|
|
||||||
unlikely to need NVRAM polling in their latest BIOS versions.
|
|
||||||
|
|
||||||
NVRAM polling can detect at most the following keys: ThinkPad/Access
|
|
||||||
IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute,
|
|
||||||
Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12).
|
|
||||||
|
|
||||||
If you are not sure, say Y here. The driver enables polling only if
|
|
||||||
it is strictly necessary to do so.
|
|
||||||
|
|
||||||
config THINKPAD_LMI
|
|
||||||
tristate "Lenovo WMI-based systems management driver"
|
|
||||||
depends on ACPI_WMI
|
|
||||||
select FW_ATTR_CLASS
|
|
||||||
help
|
|
||||||
This driver allows changing BIOS settings on Lenovo machines whose
|
|
||||||
BIOS support the WMI interface.
|
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the module will
|
|
||||||
be called think-lmi.
|
|
||||||
|
|
||||||
source "drivers/platform/x86/intel/Kconfig"
|
source "drivers/platform/x86/intel/Kconfig"
|
||||||
|
source "drivers/platform/x86/lenovo/Kconfig"
|
||||||
|
|
||||||
config ACPI_QUICKSTART
|
config ACPI_QUICKSTART
|
||||||
tristate "ACPI Quickstart button driver"
|
tristate "ACPI Quickstart button driver"
|
||||||
|
|
@ -825,6 +619,7 @@ config SAMSUNG_LAPTOP
|
||||||
tristate "Samsung Laptop driver"
|
tristate "Samsung Laptop driver"
|
||||||
depends on RFKILL || RFKILL = n
|
depends on RFKILL || RFKILL = n
|
||||||
depends on ACPI_VIDEO || ACPI_VIDEO = n
|
depends on ACPI_VIDEO || ACPI_VIDEO = n
|
||||||
|
depends on ACPI_BATTERY
|
||||||
depends on BACKLIGHT_CLASS_DEVICE
|
depends on BACKLIGHT_CLASS_DEVICE
|
||||||
select LEDS_CLASS
|
select LEDS_CLASS
|
||||||
select NEW_LEDS
|
select NEW_LEDS
|
||||||
|
|
@ -1078,18 +873,6 @@ config INSPUR_PLATFORM_PROFILE
|
||||||
To compile this driver as a module, choose M here: the module
|
To compile this driver as a module, choose M here: the module
|
||||||
will be called inspur-platform-profile.
|
will be called inspur-platform-profile.
|
||||||
|
|
||||||
config LENOVO_WMI_CAMERA
|
|
||||||
tristate "Lenovo WMI Camera Button driver"
|
|
||||||
depends on ACPI_WMI
|
|
||||||
depends on INPUT
|
|
||||||
help
|
|
||||||
This driver provides support for Lenovo camera button. The Camera
|
|
||||||
button is a GPIO device. This driver receives ACPI notifications when
|
|
||||||
the camera button is switched on/off.
|
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the module
|
|
||||||
will be called lenovo-wmi-camera.
|
|
||||||
|
|
||||||
config DASHARO_ACPI
|
config DASHARO_ACPI
|
||||||
tristate "Dasharo ACPI Platform Driver"
|
tristate "Dasharo ACPI Platform Driver"
|
||||||
depends on ACPI
|
depends on ACPI
|
||||||
|
|
|
||||||
|
|
@ -60,17 +60,12 @@ obj-$(CONFIG_UV_SYSFS) += uv_sysfs.o
|
||||||
|
|
||||||
obj-$(CONFIG_FW_ATTR_CLASS) += firmware_attributes_class.o
|
obj-$(CONFIG_FW_ATTR_CLASS) += firmware_attributes_class.o
|
||||||
|
|
||||||
# IBM Thinkpad and Lenovo
|
# IBM Thinkpad (before 2005)
|
||||||
obj-$(CONFIG_IBM_RTL) += ibm_rtl.o
|
obj-$(CONFIG_IBM_RTL) += ibm_rtl.o
|
||||||
obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o
|
|
||||||
obj-$(CONFIG_LENOVO_WMI_HOTKEY_UTILITIES) += lenovo-wmi-hotkey-utilities.o
|
|
||||||
obj-$(CONFIG_LENOVO_YMC) += lenovo-ymc.o
|
|
||||||
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
|
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
|
||||||
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
|
|
||||||
obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o
|
# Lenovo
|
||||||
obj-$(CONFIG_YOGABOOK) += lenovo-yogabook.o
|
obj-y += lenovo/
|
||||||
obj-$(CONFIG_YT2_1380) += lenovo-yoga-tab2-pro-1380-fastcharger.o
|
|
||||||
obj-$(CONFIG_LENOVO_WMI_CAMERA) += lenovo-wmi-camera.o
|
|
||||||
|
|
||||||
# Intel
|
# Intel
|
||||||
obj-y += intel/
|
obj-y += intel/
|
||||||
|
|
|
||||||
|
|
@ -587,8 +587,10 @@ static int hsmp_acpi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
if (!hsmp_pdev->is_probed) {
|
if (!hsmp_pdev->is_probed) {
|
||||||
hsmp_pdev->num_sockets = amd_num_nodes();
|
hsmp_pdev->num_sockets = amd_num_nodes();
|
||||||
if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES)
|
if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES) {
|
||||||
|
dev_err(&pdev->dev, "Wrong number of sockets\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
hsmp_pdev->sock = devm_kcalloc(&pdev->dev, hsmp_pdev->num_sockets,
|
hsmp_pdev->sock = devm_kcalloc(&pdev->dev, hsmp_pdev->num_sockets,
|
||||||
sizeof(*hsmp_pdev->sock),
|
sizeof(*hsmp_pdev->sock),
|
||||||
|
|
@ -605,9 +607,12 @@ static int hsmp_acpi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
if (!hsmp_pdev->is_probed) {
|
if (!hsmp_pdev->is_probed) {
|
||||||
ret = hsmp_misc_register(&pdev->dev);
|
ret = hsmp_misc_register(&pdev->dev);
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Failed to register misc device\n");
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
hsmp_pdev->is_probed = true;
|
hsmp_pdev->is_probed = true;
|
||||||
|
dev_dbg(&pdev->dev, "AMD HSMP ACPI is probed successfully\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
#include <linux/compiler_types.h>
|
#include <linux/compiler_types.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/hwmon.h>
|
#include <linux/hwmon.h>
|
||||||
|
#include <linux/kconfig.h>
|
||||||
#include <linux/miscdevice.h>
|
#include <linux/miscdevice.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/semaphore.h>
|
#include <linux/semaphore.h>
|
||||||
|
|
@ -64,7 +65,7 @@ int hsmp_misc_register(struct device *dev);
|
||||||
int hsmp_get_tbl_dram_base(u16 sock_ind);
|
int hsmp_get_tbl_dram_base(u16 sock_ind);
|
||||||
ssize_t hsmp_metric_tbl_read(struct hsmp_socket *sock, char *buf, size_t size);
|
ssize_t hsmp_metric_tbl_read(struct hsmp_socket *sock, char *buf, size_t size);
|
||||||
struct hsmp_plat_device *get_hsmp_pdev(void);
|
struct hsmp_plat_device *get_hsmp_pdev(void);
|
||||||
#if IS_REACHABLE(CONFIG_HWMON)
|
#if IS_ENABLED(CONFIG_HWMON)
|
||||||
int hsmp_create_sensor(struct device *dev, u16 sock_ind);
|
int hsmp_create_sensor(struct device *dev, u16 sock_ind);
|
||||||
#else
|
#else
|
||||||
static inline int hsmp_create_sensor(struct device *dev, u16 sock_ind) { return 0; }
|
static inline int hsmp_create_sensor(struct device *dev, u16 sock_ind) { return 0; }
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/build_bug.h>
|
#include <linux/build_bug.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
#include <linux/dev_printk.h>
|
||||||
|
#include <linux/kconfig.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
@ -215,7 +217,14 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return hsmp_misc_register(&pdev->dev);
|
ret = hsmp_misc_register(&pdev->dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Failed to register misc device\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "AMD HSMP is probed successfully\n");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hsmp_pltdrv_remove(struct platform_device *pdev)
|
static void hsmp_pltdrv_remove(struct platform_device *pdev)
|
||||||
|
|
@ -287,15 +296,20 @@ static int __init hsmp_plt_init(void)
|
||||||
{
|
{
|
||||||
int ret = -ENODEV;
|
int ret = -ENODEV;
|
||||||
|
|
||||||
|
if (acpi_dev_present(ACPI_HSMP_DEVICE_HID, NULL, -1)) {
|
||||||
|
if (IS_ENABLED(CONFIG_AMD_HSMP_ACPI))
|
||||||
|
pr_debug("HSMP is supported through ACPI on this platform, please use hsmp_acpi.ko\n");
|
||||||
|
else
|
||||||
|
pr_info("HSMP is supported through ACPI on this platform, please enable AMD_HSMP_ACPI config\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
if (!legacy_hsmp_support()) {
|
if (!legacy_hsmp_support()) {
|
||||||
pr_info("HSMP is not supported on Family:%x model:%x\n",
|
pr_info("HSMP interface is either disabled or not supported on family:%x model:%x\n",
|
||||||
boot_cpu_data.x86, boot_cpu_data.x86_model);
|
boot_cpu_data.x86, boot_cpu_data.x86_model);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (acpi_dev_present(ACPI_HSMP_DEVICE_HID, NULL, -1))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
hsmp_pdev = get_hsmp_pdev();
|
hsmp_pdev = get_hsmp_pdev();
|
||||||
if (!hsmp_pdev)
|
if (!hsmp_pdev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
@ -305,8 +319,10 @@ static int __init hsmp_plt_init(void)
|
||||||
* if we have N SMN/DF interfaces that ideally means N sockets
|
* if we have N SMN/DF interfaces that ideally means N sockets
|
||||||
*/
|
*/
|
||||||
hsmp_pdev->num_sockets = amd_num_nodes();
|
hsmp_pdev->num_sockets = amd_num_nodes();
|
||||||
if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES)
|
if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES) {
|
||||||
|
pr_err("Wrong number of sockets\n");
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
ret = platform_driver_register(&amd_hsmp_driver);
|
ret = platform_driver_register(&amd_hsmp_driver);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,15 @@ static const struct dmi_system_id fwbug_list[] = {
|
||||||
DMI_MATCH(DMI_PRODUCT_NAME, "82XQ"),
|
DMI_MATCH(DMI_PRODUCT_NAME, "82XQ"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/* https://gitlab.freedesktop.org/drm/amd/-/issues/4434 */
|
||||||
|
{
|
||||||
|
.ident = "Lenovo Yoga 6 13ALC6",
|
||||||
|
.driver_data = &quirk_s2idle_bug,
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "82ND"),
|
||||||
|
}
|
||||||
|
},
|
||||||
/* https://gitlab.freedesktop.org/drm/amd/-/issues/2684 */
|
/* https://gitlab.freedesktop.org/drm/amd/-/issues/2684 */
|
||||||
{
|
{
|
||||||
.ident = "HP Laptop 15s-eq2xxx",
|
.ident = "HP Laptop 15s-eq2xxx",
|
||||||
|
|
|
||||||
|
|
@ -290,9 +290,29 @@ enum AWCC_SPECIAL_THERMAL_CODES {
|
||||||
|
|
||||||
enum AWCC_TEMP_SENSOR_TYPES {
|
enum AWCC_TEMP_SENSOR_TYPES {
|
||||||
AWCC_TEMP_SENSOR_CPU = 0x01,
|
AWCC_TEMP_SENSOR_CPU = 0x01,
|
||||||
|
AWCC_TEMP_SENSOR_FRONT = 0x03,
|
||||||
AWCC_TEMP_SENSOR_GPU = 0x06,
|
AWCC_TEMP_SENSOR_GPU = 0x06,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum AWCC_FAN_TYPES {
|
||||||
|
AWCC_FAN_CPU_1 = 0x32,
|
||||||
|
AWCC_FAN_GPU_1 = 0x33,
|
||||||
|
AWCC_FAN_PCI = 0x34,
|
||||||
|
AWCC_FAN_MID = 0x35,
|
||||||
|
AWCC_FAN_TOP_1 = 0x36,
|
||||||
|
AWCC_FAN_SIDE = 0x37,
|
||||||
|
AWCC_FAN_U2_1 = 0x38,
|
||||||
|
AWCC_FAN_U2_2 = 0x39,
|
||||||
|
AWCC_FAN_FRONT_1 = 0x3A,
|
||||||
|
AWCC_FAN_CPU_2 = 0x3B,
|
||||||
|
AWCC_FAN_GPU_2 = 0x3C,
|
||||||
|
AWCC_FAN_TOP_2 = 0x3D,
|
||||||
|
AWCC_FAN_TOP_3 = 0x3E,
|
||||||
|
AWCC_FAN_FRONT_2 = 0x3F,
|
||||||
|
AWCC_FAN_BOTTOM_1 = 0x40,
|
||||||
|
AWCC_FAN_BOTTOM_2 = 0x41,
|
||||||
|
};
|
||||||
|
|
||||||
enum awcc_thermal_profile {
|
enum awcc_thermal_profile {
|
||||||
AWCC_PROFILE_USTT_BALANCED,
|
AWCC_PROFILE_USTT_BALANCED,
|
||||||
AWCC_PROFILE_USTT_BALANCED_PERFORMANCE,
|
AWCC_PROFILE_USTT_BALANCED_PERFORMANCE,
|
||||||
|
|
@ -331,7 +351,6 @@ struct wmax_u32_args {
|
||||||
|
|
||||||
struct awcc_fan_data {
|
struct awcc_fan_data {
|
||||||
unsigned long auto_channels_temp;
|
unsigned long auto_channels_temp;
|
||||||
const char *label;
|
|
||||||
u32 min_rpm;
|
u32 min_rpm;
|
||||||
u32 max_rpm;
|
u32 max_rpm;
|
||||||
u8 suspend_cache;
|
u8 suspend_cache;
|
||||||
|
|
@ -913,6 +932,9 @@ static int awcc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types ty
|
||||||
case AWCC_TEMP_SENSOR_CPU:
|
case AWCC_TEMP_SENSOR_CPU:
|
||||||
*str = "CPU";
|
*str = "CPU";
|
||||||
break;
|
break;
|
||||||
|
case AWCC_TEMP_SENSOR_FRONT:
|
||||||
|
*str = "Front";
|
||||||
|
break;
|
||||||
case AWCC_TEMP_SENSOR_GPU:
|
case AWCC_TEMP_SENSOR_GPU:
|
||||||
*str = "GPU";
|
*str = "GPU";
|
||||||
break;
|
break;
|
||||||
|
|
@ -923,7 +945,46 @@ static int awcc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types ty
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case hwmon_fan:
|
case hwmon_fan:
|
||||||
*str = priv->fan_data[channel]->label;
|
switch (priv->fan_data[channel]->id) {
|
||||||
|
case AWCC_FAN_CPU_1:
|
||||||
|
case AWCC_FAN_CPU_2:
|
||||||
|
*str = "CPU Fan";
|
||||||
|
break;
|
||||||
|
case AWCC_FAN_GPU_1:
|
||||||
|
case AWCC_FAN_GPU_2:
|
||||||
|
*str = "GPU Fan";
|
||||||
|
break;
|
||||||
|
case AWCC_FAN_PCI:
|
||||||
|
*str = "PCI Fan";
|
||||||
|
break;
|
||||||
|
case AWCC_FAN_MID:
|
||||||
|
*str = "Mid Fan";
|
||||||
|
break;
|
||||||
|
case AWCC_FAN_TOP_1:
|
||||||
|
case AWCC_FAN_TOP_2:
|
||||||
|
case AWCC_FAN_TOP_3:
|
||||||
|
*str = "Top Fan";
|
||||||
|
break;
|
||||||
|
case AWCC_FAN_SIDE:
|
||||||
|
*str = "Side Fan";
|
||||||
|
break;
|
||||||
|
case AWCC_FAN_U2_1:
|
||||||
|
case AWCC_FAN_U2_2:
|
||||||
|
*str = "U.2 Fan";
|
||||||
|
break;
|
||||||
|
case AWCC_FAN_FRONT_1:
|
||||||
|
case AWCC_FAN_FRONT_2:
|
||||||
|
*str = "Front Fan";
|
||||||
|
break;
|
||||||
|
case AWCC_FAN_BOTTOM_1:
|
||||||
|
case AWCC_FAN_BOTTOM_2:
|
||||||
|
*str = "Bottom Fan";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
*str = "Unknown Fan";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
@ -1068,40 +1129,6 @@ static int awcc_hwmon_temps_init(struct wmi_device *wdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *awcc_get_fan_label(unsigned long *fan_temps)
|
|
||||||
{
|
|
||||||
unsigned int temp_count = bitmap_weight(fan_temps, AWCC_ID_BITMAP_SIZE);
|
|
||||||
char *label;
|
|
||||||
u8 temp_id;
|
|
||||||
|
|
||||||
switch (temp_count) {
|
|
||||||
case 0:
|
|
||||||
label = "Independent Fan";
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
temp_id = find_first_bit(fan_temps, AWCC_ID_BITMAP_SIZE);
|
|
||||||
|
|
||||||
switch (temp_id) {
|
|
||||||
case AWCC_TEMP_SENSOR_CPU:
|
|
||||||
label = "Processor Fan";
|
|
||||||
break;
|
|
||||||
case AWCC_TEMP_SENSOR_GPU:
|
|
||||||
label = "Video Fan";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
label = "Unknown Fan";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
label = "Shared Fan";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return label;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int awcc_hwmon_fans_init(struct wmi_device *wdev)
|
static int awcc_hwmon_fans_init(struct wmi_device *wdev)
|
||||||
{
|
{
|
||||||
struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
|
struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||||
|
|
@ -1155,7 +1182,6 @@ static int awcc_hwmon_fans_init(struct wmi_device *wdev)
|
||||||
fan_data->id = id;
|
fan_data->id = id;
|
||||||
fan_data->min_rpm = min_rpm;
|
fan_data->min_rpm = min_rpm;
|
||||||
fan_data->max_rpm = max_rpm;
|
fan_data->max_rpm = max_rpm;
|
||||||
fan_data->label = awcc_get_fan_label(fan_temps);
|
|
||||||
bitmap_gather(gather, fan_temps, priv->temp_sensors, AWCC_ID_BITMAP_SIZE);
|
bitmap_gather(gather, fan_temps, priv->temp_sensors, AWCC_ID_BITMAP_SIZE);
|
||||||
bitmap_copy(&fan_data->auto_channels_temp, gather, BITS_PER_LONG);
|
bitmap_copy(&fan_data->auto_channels_temp, gather, BITS_PER_LONG);
|
||||||
priv->fan_data[i] = fan_data;
|
priv->fan_data[i] = fan_data;
|
||||||
|
|
|
||||||
|
|
@ -305,7 +305,7 @@ static int dell_uart_bl_serdev_probe(struct serdev_device *serdev)
|
||||||
dev_dbg(dev, "Firmware version: %.*s\n", resp[RESP_LEN] - 3, resp + RESP_DATA);
|
dev_dbg(dev, "Firmware version: %.*s\n", resp[RESP_LEN] - 3, resp + RESP_DATA);
|
||||||
|
|
||||||
/* Initialize bl_power to a known value */
|
/* Initialize bl_power to a known value */
|
||||||
ret = dell_uart_set_bl_power(dell_bl, FB_BLANK_UNBLANK);
|
ret = dell_uart_set_bl_power(dell_bl, BACKLIGHT_POWER_ON);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,14 +77,14 @@ struct packet_data {
|
||||||
int ordernum;
|
int ordernum;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct packet_data packet_data_head;
|
static struct list_head packet_data_list;
|
||||||
|
|
||||||
static struct platform_device *rbu_device;
|
static struct platform_device *rbu_device;
|
||||||
static int context;
|
static int context;
|
||||||
|
|
||||||
static void init_packet_head(void)
|
static void init_packet_head(void)
|
||||||
{
|
{
|
||||||
INIT_LIST_HEAD(&packet_data_head.list);
|
INIT_LIST_HEAD(&packet_data_list);
|
||||||
rbu_data.packet_read_count = 0;
|
rbu_data.packet_read_count = 0;
|
||||||
rbu_data.num_packets = 0;
|
rbu_data.num_packets = 0;
|
||||||
rbu_data.packetsize = 0;
|
rbu_data.packetsize = 0;
|
||||||
|
|
@ -183,7 +183,7 @@ static int create_packet(void *data, size_t length) __must_hold(&rbu_data.lock)
|
||||||
|
|
||||||
/* initialize the newly created packet headers */
|
/* initialize the newly created packet headers */
|
||||||
INIT_LIST_HEAD(&newpacket->list);
|
INIT_LIST_HEAD(&newpacket->list);
|
||||||
list_add_tail(&newpacket->list, &packet_data_head.list);
|
list_add_tail(&newpacket->list, &packet_data_list);
|
||||||
|
|
||||||
memcpy(newpacket->data, data, length);
|
memcpy(newpacket->data, data, length);
|
||||||
|
|
||||||
|
|
@ -292,7 +292,7 @@ static int packet_read_list(char *data, size_t * pread_length)
|
||||||
remaining_bytes = *pread_length;
|
remaining_bytes = *pread_length;
|
||||||
bytes_read = rbu_data.packet_read_count;
|
bytes_read = rbu_data.packet_read_count;
|
||||||
|
|
||||||
list_for_each_entry(newpacket, &packet_data_head.list, list) {
|
list_for_each_entry(newpacket, &packet_data_list, list) {
|
||||||
bytes_copied = do_packet_read(pdest, newpacket,
|
bytes_copied = do_packet_read(pdest, newpacket,
|
||||||
remaining_bytes, bytes_read, &temp_count);
|
remaining_bytes, bytes_read, &temp_count);
|
||||||
remaining_bytes -= bytes_copied;
|
remaining_bytes -= bytes_copied;
|
||||||
|
|
@ -315,7 +315,7 @@ static void packet_empty_list(void)
|
||||||
{
|
{
|
||||||
struct packet_data *newpacket, *tmp;
|
struct packet_data *newpacket, *tmp;
|
||||||
|
|
||||||
list_for_each_entry_safe(newpacket, tmp, &packet_data_head.list, list) {
|
list_for_each_entry_safe(newpacket, tmp, &packet_data_list, list) {
|
||||||
list_del(&newpacket->list);
|
list_del(&newpacket->list);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -180,15 +180,19 @@ static ssize_t charge_control_end_threshold_store(struct device *dev,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
int cc_end_value, s006_cc_return;
|
int cc_end_value, s006_cc_return;
|
||||||
int value, ret;
|
unsigned int value;
|
||||||
|
int ret;
|
||||||
|
|
||||||
ret = kstrtouint(buf, 10, &value);
|
ret = kstrtouint(buf, 10, &value);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (value < 50 || value > 100)
|
if (value > 100)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (value < 50)
|
||||||
|
value = 50;
|
||||||
|
|
||||||
cc_end_value = value * 0x100 + 0x20;
|
cc_end_value = value * 0x100 + 0x20;
|
||||||
s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD,
|
s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD,
|
||||||
CHARGE_CONTROL_RW, cc_end_value, 0x0);
|
CHARGE_CONTROL_RW, cc_end_value, 0x0);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/gfp_types.h>
|
#include <linux/gfp_types.h>
|
||||||
#include <linux/intel_tpmi.h>
|
#include <linux/intel_tpmi.h>
|
||||||
|
#include <linux/intel_vsec.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/iopoll.h>
|
#include <linux/iopoll.h>
|
||||||
#include <linux/kstrtox.h>
|
#include <linux/kstrtox.h>
|
||||||
|
|
@ -256,7 +257,7 @@ DEFINE_SHOW_STORE_ATTRIBUTE(plr_status);
|
||||||
|
|
||||||
static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
|
static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
|
||||||
{
|
{
|
||||||
struct intel_tpmi_plat_info *plat_info;
|
struct oobmsm_plat_info *plat_info;
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
int i, num_resources;
|
int i, num_resources;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ config INTEL_PMT_CLASS
|
||||||
config INTEL_PMT_TELEMETRY
|
config INTEL_PMT_TELEMETRY
|
||||||
tristate "Intel Platform Monitoring Technology (PMT) Telemetry driver"
|
tristate "Intel Platform Monitoring Technology (PMT) Telemetry driver"
|
||||||
depends on INTEL_VSEC
|
depends on INTEL_VSEC
|
||||||
|
select INTEL_PMT_DISCOVERY
|
||||||
select INTEL_PMT_CLASS
|
select INTEL_PMT_CLASS
|
||||||
help
|
help
|
||||||
The Intel Platform Monitory Technology (PMT) Telemetry driver provides
|
The Intel Platform Monitory Technology (PMT) Telemetry driver provides
|
||||||
|
|
@ -38,3 +39,30 @@ config INTEL_PMT_CRASHLOG
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the module
|
To compile this driver as a module, choose M here: the module
|
||||||
will be called intel_pmt_crashlog.
|
will be called intel_pmt_crashlog.
|
||||||
|
|
||||||
|
config INTEL_PMT_DISCOVERY
|
||||||
|
tristate "Intel Platform Monitoring Technology (PMT) Discovery driver"
|
||||||
|
depends on INTEL_VSEC
|
||||||
|
select INTEL_PMT_CLASS
|
||||||
|
help
|
||||||
|
The Intel Platform Monitoring Technology (PMT) discovery driver provides
|
||||||
|
access to details about the various PMT features and feature specific
|
||||||
|
attributes.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module
|
||||||
|
will be called pmt_discovery.
|
||||||
|
|
||||||
|
config INTEL_PMT_KUNIT_TEST
|
||||||
|
tristate "KUnit tests for Intel PMT driver"
|
||||||
|
depends on INTEL_PMT_DISCOVERY
|
||||||
|
depends on INTEL_PMT_TELEMETRY || !INTEL_PMT_TELEMETRY
|
||||||
|
depends on KUNIT
|
||||||
|
help
|
||||||
|
Enable this option to compile and run a suite of KUnit tests for the Intel
|
||||||
|
Platform Monitoring Technology (PMT) driver. These tests are designed to
|
||||||
|
validate the driver's functionality, error handling, and overall stability,
|
||||||
|
helping developers catch regressions and ensure code quality during changes.
|
||||||
|
|
||||||
|
This option is intended for development and testing environments. It is
|
||||||
|
recommended to disable it in production builds. To compile this driver as a
|
||||||
|
module, choose M here: the module will be called pmt-discovery-kunit.
|
||||||
|
|
|
||||||
|
|
@ -10,3 +10,7 @@ obj-$(CONFIG_INTEL_PMT_TELEMETRY) += pmt_telemetry.o
|
||||||
pmt_telemetry-y := telemetry.o
|
pmt_telemetry-y := telemetry.o
|
||||||
obj-$(CONFIG_INTEL_PMT_CRASHLOG) += pmt_crashlog.o
|
obj-$(CONFIG_INTEL_PMT_CRASHLOG) += pmt_crashlog.o
|
||||||
pmt_crashlog-y := crashlog.o
|
pmt_crashlog-y := crashlog.o
|
||||||
|
obj-$(CONFIG_INTEL_PMT_DISCOVERY) += pmt_discovery.o
|
||||||
|
pmt_discovery-y := discovery.o features.o
|
||||||
|
obj-$(CONFIG_INTEL_PMT_KUNIT_TEST) += pmt-discovery-kunit.o
|
||||||
|
pmt-discovery-kunit-y := discovery-kunit.o
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/log2.h>
|
||||||
#include <linux/intel_vsec.h>
|
#include <linux/intel_vsec.h>
|
||||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
|
#include <linux/sysfs.h>
|
||||||
|
|
||||||
#include "class.h"
|
#include "class.h"
|
||||||
|
|
||||||
|
|
@ -97,7 +99,7 @@ intel_pmt_read(struct file *filp, struct kobject *kobj,
|
||||||
if (count > entry->size - off)
|
if (count > entry->size - off)
|
||||||
count = entry->size - off;
|
count = entry->size - off;
|
||||||
|
|
||||||
count = pmt_telem_read_mmio(entry->ep->pcidev, entry->cb, entry->header.guid, buf,
|
count = pmt_telem_read_mmio(entry->pcidev, entry->cb, entry->header.guid, buf,
|
||||||
entry->base, off, count);
|
entry->base, off, count);
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
|
|
@ -166,12 +168,41 @@ static struct attribute *intel_pmt_attrs[] = {
|
||||||
&dev_attr_offset.attr,
|
&dev_attr_offset.attr,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
ATTRIBUTE_GROUPS(intel_pmt);
|
|
||||||
|
|
||||||
static struct class intel_pmt_class = {
|
static umode_t intel_pmt_attr_visible(struct kobject *kobj,
|
||||||
|
struct attribute *attr, int n)
|
||||||
|
{
|
||||||
|
struct device *dev = container_of(kobj, struct device, kobj);
|
||||||
|
struct auxiliary_device *auxdev = to_auxiliary_dev(dev->parent);
|
||||||
|
struct intel_vsec_device *ivdev = auxdev_to_ivdev(auxdev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Place the discovery features folder in /sys/class/intel_pmt, but
|
||||||
|
* exclude the common attributes as they are not applicable.
|
||||||
|
*/
|
||||||
|
if (ivdev->cap_id == ilog2(VSEC_CAP_DISCOVERY))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return attr->mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool intel_pmt_group_visible(struct kobject *kobj)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
DEFINE_SYSFS_GROUP_VISIBLE(intel_pmt);
|
||||||
|
|
||||||
|
static const struct attribute_group intel_pmt_group = {
|
||||||
|
.attrs = intel_pmt_attrs,
|
||||||
|
.is_visible = SYSFS_GROUP_VISIBLE(intel_pmt),
|
||||||
|
};
|
||||||
|
__ATTRIBUTE_GROUPS(intel_pmt);
|
||||||
|
|
||||||
|
struct class intel_pmt_class = {
|
||||||
.name = "intel_pmt",
|
.name = "intel_pmt",
|
||||||
.dev_groups = intel_pmt_groups,
|
.dev_groups = intel_pmt_groups,
|
||||||
};
|
};
|
||||||
|
EXPORT_SYMBOL_GPL(intel_pmt_class);
|
||||||
|
|
||||||
static int intel_pmt_populate_entry(struct intel_pmt_entry *entry,
|
static int intel_pmt_populate_entry(struct intel_pmt_entry *entry,
|
||||||
struct intel_vsec_device *ivdev,
|
struct intel_vsec_device *ivdev,
|
||||||
|
|
@ -252,6 +283,7 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entry->pcidev = pci_dev;
|
||||||
entry->guid = header->guid;
|
entry->guid = header->guid;
|
||||||
entry->size = header->size;
|
entry->size = header->size;
|
||||||
entry->cb = ivdev->priv_data;
|
entry->cb = ivdev->priv_data;
|
||||||
|
|
@ -284,8 +316,8 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry,
|
||||||
|
|
||||||
entry->kobj = &dev->kobj;
|
entry->kobj = &dev->kobj;
|
||||||
|
|
||||||
if (ns->attr_grp) {
|
if (entry->attr_grp) {
|
||||||
ret = sysfs_create_group(entry->kobj, ns->attr_grp);
|
ret = sysfs_create_group(entry->kobj, entry->attr_grp);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto fail_sysfs_create_group;
|
goto fail_sysfs_create_group;
|
||||||
}
|
}
|
||||||
|
|
@ -326,8 +358,8 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry,
|
||||||
fail_add_endpoint:
|
fail_add_endpoint:
|
||||||
sysfs_remove_bin_file(entry->kobj, &entry->pmt_bin_attr);
|
sysfs_remove_bin_file(entry->kobj, &entry->pmt_bin_attr);
|
||||||
fail_ioremap:
|
fail_ioremap:
|
||||||
if (ns->attr_grp)
|
if (entry->attr_grp)
|
||||||
sysfs_remove_group(entry->kobj, ns->attr_grp);
|
sysfs_remove_group(entry->kobj, entry->attr_grp);
|
||||||
fail_sysfs_create_group:
|
fail_sysfs_create_group:
|
||||||
device_unregister(dev);
|
device_unregister(dev);
|
||||||
fail_dev_create:
|
fail_dev_create:
|
||||||
|
|
@ -369,8 +401,8 @@ void intel_pmt_dev_destroy(struct intel_pmt_entry *entry,
|
||||||
if (entry->size)
|
if (entry->size)
|
||||||
sysfs_remove_bin_file(entry->kobj, &entry->pmt_bin_attr);
|
sysfs_remove_bin_file(entry->kobj, &entry->pmt_bin_attr);
|
||||||
|
|
||||||
if (ns->attr_grp)
|
if (entry->attr_grp)
|
||||||
sysfs_remove_group(entry->kobj, ns->attr_grp);
|
sysfs_remove_group(entry->kobj, entry->attr_grp);
|
||||||
|
|
||||||
device_unregister(dev);
|
device_unregister(dev);
|
||||||
xa_erase(ns->xa, entry->devid);
|
xa_erase(ns->xa, entry->devid);
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
#define GET_ADDRESS(v) ((v) & GENMASK(31, 3))
|
#define GET_ADDRESS(v) ((v) & GENMASK(31, 3))
|
||||||
|
|
||||||
struct pci_dev;
|
struct pci_dev;
|
||||||
|
extern struct class intel_pmt_class;
|
||||||
|
|
||||||
struct telem_endpoint {
|
struct telem_endpoint {
|
||||||
struct pci_dev *pcidev;
|
struct pci_dev *pcidev;
|
||||||
|
|
@ -39,22 +40,25 @@ struct intel_pmt_header {
|
||||||
|
|
||||||
struct intel_pmt_entry {
|
struct intel_pmt_entry {
|
||||||
struct telem_endpoint *ep;
|
struct telem_endpoint *ep;
|
||||||
|
struct pci_dev *pcidev;
|
||||||
struct intel_pmt_header header;
|
struct intel_pmt_header header;
|
||||||
struct bin_attribute pmt_bin_attr;
|
struct bin_attribute pmt_bin_attr;
|
||||||
|
const struct attribute_group *attr_grp;
|
||||||
struct kobject *kobj;
|
struct kobject *kobj;
|
||||||
void __iomem *disc_table;
|
void __iomem *disc_table;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
struct pmt_callbacks *cb;
|
struct pmt_callbacks *cb;
|
||||||
unsigned long base_addr;
|
unsigned long base_addr;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
u64 feature_flags;
|
||||||
u32 guid;
|
u32 guid;
|
||||||
|
u32 num_rmids; /* Number of Resource Monitoring IDs */
|
||||||
int devid;
|
int devid;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct intel_pmt_namespace {
|
struct intel_pmt_namespace {
|
||||||
const char *name;
|
const char *name;
|
||||||
struct xarray *xa;
|
struct xarray *xa;
|
||||||
const struct attribute_group *attr_grp;
|
|
||||||
int (*pmt_header_decode)(struct intel_pmt_entry *entry,
|
int (*pmt_header_decode)(struct intel_pmt_entry *entry,
|
||||||
struct device *dev);
|
struct device *dev);
|
||||||
int (*pmt_add_endpoint)(struct intel_vsec_device *ivdev,
|
int (*pmt_add_endpoint)(struct intel_vsec_device *ivdev,
|
||||||
|
|
@ -69,4 +73,10 @@ int intel_pmt_dev_create(struct intel_pmt_entry *entry,
|
||||||
struct intel_vsec_device *dev, int idx);
|
struct intel_vsec_device *dev, int idx);
|
||||||
void intel_pmt_dev_destroy(struct intel_pmt_entry *entry,
|
void intel_pmt_dev_destroy(struct intel_pmt_entry *entry,
|
||||||
struct intel_pmt_namespace *ns);
|
struct intel_pmt_namespace *ns);
|
||||||
|
#if IS_ENABLED(CONFIG_INTEL_PMT_DISCOVERY)
|
||||||
|
void intel_pmt_get_features(struct intel_pmt_entry *entry);
|
||||||
|
#else
|
||||||
|
static inline void intel_pmt_get_features(struct intel_pmt_entry *entry) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/auxiliary_bus.h>
|
#include <linux/auxiliary_bus.h>
|
||||||
|
#include <linux/cleanup.h>
|
||||||
#include <linux/intel_vsec.h>
|
#include <linux/intel_vsec.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
|
@ -22,21 +24,6 @@
|
||||||
/* Crashlog discovery header types */
|
/* Crashlog discovery header types */
|
||||||
#define CRASH_TYPE_OOBMSM 1
|
#define CRASH_TYPE_OOBMSM 1
|
||||||
|
|
||||||
/* Control Flags */
|
|
||||||
#define CRASHLOG_FLAG_DISABLE BIT(28)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Bits 29 and 30 control the state of bit 31.
|
|
||||||
*
|
|
||||||
* Bit 29 will clear bit 31, if set, allowing a new crashlog to be captured.
|
|
||||||
* Bit 30 will immediately trigger a crashlog to be generated, setting bit 31.
|
|
||||||
* Bit 31 is the read-only status with a 1 indicating log is complete.
|
|
||||||
*/
|
|
||||||
#define CRASHLOG_FLAG_TRIGGER_CLEAR BIT(29)
|
|
||||||
#define CRASHLOG_FLAG_TRIGGER_EXECUTE BIT(30)
|
|
||||||
#define CRASHLOG_FLAG_TRIGGER_COMPLETE BIT(31)
|
|
||||||
#define CRASHLOG_FLAG_TRIGGER_MASK GENMASK(31, 28)
|
|
||||||
|
|
||||||
/* Crashlog Discovery Header */
|
/* Crashlog Discovery Header */
|
||||||
#define CONTROL_OFFSET 0x0
|
#define CONTROL_OFFSET 0x0
|
||||||
#define GUID_OFFSET 0x4
|
#define GUID_OFFSET 0x4
|
||||||
|
|
@ -48,10 +35,84 @@
|
||||||
/* size is in bytes */
|
/* size is in bytes */
|
||||||
#define GET_SIZE(v) ((v) * sizeof(u32))
|
#define GET_SIZE(v) ((v) * sizeof(u32))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Type 1 Version 0
|
||||||
|
* status and control registers are combined.
|
||||||
|
*
|
||||||
|
* Bits 29 and 30 control the state of bit 31.
|
||||||
|
* Bit 29 will clear bit 31, if set, allowing a new crashlog to be captured.
|
||||||
|
* Bit 30 will immediately trigger a crashlog to be generated, setting bit 31.
|
||||||
|
* Bit 31 is the read-only status with a 1 indicating log is complete.
|
||||||
|
*/
|
||||||
|
#define TYPE1_VER0_STATUS_OFFSET 0x00
|
||||||
|
#define TYPE1_VER0_CONTROL_OFFSET 0x00
|
||||||
|
|
||||||
|
#define TYPE1_VER0_DISABLE BIT(28)
|
||||||
|
#define TYPE1_VER0_CLEAR BIT(29)
|
||||||
|
#define TYPE1_VER0_EXECUTE BIT(30)
|
||||||
|
#define TYPE1_VER0_COMPLETE BIT(31)
|
||||||
|
#define TYPE1_VER0_TRIGGER_MASK GENMASK(31, 28)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Type 1 Version 2
|
||||||
|
* status and control are different registers
|
||||||
|
*/
|
||||||
|
#define TYPE1_VER2_STATUS_OFFSET 0x00
|
||||||
|
#define TYPE1_VER2_CONTROL_OFFSET 0x14
|
||||||
|
|
||||||
|
/* status register */
|
||||||
|
#define TYPE1_VER2_CLEAR_SUPPORT BIT(20)
|
||||||
|
#define TYPE1_VER2_REARMED BIT(25)
|
||||||
|
#define TYPE1_VER2_ERROR BIT(26)
|
||||||
|
#define TYPE1_VER2_CONSUMED BIT(27)
|
||||||
|
#define TYPE1_VER2_DISABLED BIT(28)
|
||||||
|
#define TYPE1_VER2_CLEARED BIT(29)
|
||||||
|
#define TYPE1_VER2_IN_PROGRESS BIT(30)
|
||||||
|
#define TYPE1_VER2_COMPLETE BIT(31)
|
||||||
|
|
||||||
|
/* control register */
|
||||||
|
#define TYPE1_VER2_CONSUME BIT(25)
|
||||||
|
#define TYPE1_VER2_REARM BIT(28)
|
||||||
|
#define TYPE1_VER2_EXECUTE BIT(29)
|
||||||
|
#define TYPE1_VER2_CLEAR BIT(30)
|
||||||
|
#define TYPE1_VER2_DISABLE BIT(31)
|
||||||
|
#define TYPE1_VER2_TRIGGER_MASK \
|
||||||
|
(TYPE1_VER2_EXECUTE | TYPE1_VER2_CLEAR | TYPE1_VER2_DISABLE)
|
||||||
|
|
||||||
|
/* After offset, order alphabetically, not bit ordered */
|
||||||
|
struct crashlog_status {
|
||||||
|
u32 offset;
|
||||||
|
u32 clear_supported;
|
||||||
|
u32 cleared;
|
||||||
|
u32 complete;
|
||||||
|
u32 consumed;
|
||||||
|
u32 disabled;
|
||||||
|
u32 error;
|
||||||
|
u32 in_progress;
|
||||||
|
u32 rearmed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct crashlog_control {
|
||||||
|
u32 offset;
|
||||||
|
u32 trigger_mask;
|
||||||
|
u32 clear;
|
||||||
|
u32 consume;
|
||||||
|
u32 disable;
|
||||||
|
u32 manual;
|
||||||
|
u32 rearm;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct crashlog_info {
|
||||||
|
const struct crashlog_status status;
|
||||||
|
const struct crashlog_control control;
|
||||||
|
const struct attribute_group *attr_grp;
|
||||||
|
};
|
||||||
|
|
||||||
struct crashlog_entry {
|
struct crashlog_entry {
|
||||||
/* entry must be first member of struct */
|
/* entry must be first member of struct */
|
||||||
struct intel_pmt_entry entry;
|
struct intel_pmt_entry entry;
|
||||||
struct mutex control_mutex;
|
struct mutex control_mutex;
|
||||||
|
const struct crashlog_info *info;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pmt_crashlog_priv {
|
struct pmt_crashlog_priv {
|
||||||
|
|
@ -62,180 +123,397 @@ struct pmt_crashlog_priv {
|
||||||
/*
|
/*
|
||||||
* I/O
|
* I/O
|
||||||
*/
|
*/
|
||||||
static bool pmt_crashlog_complete(struct intel_pmt_entry *entry)
|
|
||||||
{
|
|
||||||
u32 control = readl(entry->disc_table + CONTROL_OFFSET);
|
|
||||||
|
|
||||||
|
/* Read, modify, write the control register, setting or clearing @bit based on @set */
|
||||||
|
static void pmt_crashlog_rmw(struct crashlog_entry *crashlog, u32 bit, bool set)
|
||||||
|
{
|
||||||
|
const struct crashlog_control *control = &crashlog->info->control;
|
||||||
|
struct intel_pmt_entry *entry = &crashlog->entry;
|
||||||
|
u32 reg = readl(entry->disc_table + control->offset);
|
||||||
|
|
||||||
|
reg &= ~control->trigger_mask;
|
||||||
|
|
||||||
|
if (set)
|
||||||
|
reg |= bit;
|
||||||
|
else
|
||||||
|
reg &= ~bit;
|
||||||
|
|
||||||
|
writel(reg, entry->disc_table + control->offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the status register and see if the specified @bit is set */
|
||||||
|
static bool pmt_crashlog_rc(struct crashlog_entry *crashlog, u32 bit)
|
||||||
|
{
|
||||||
|
const struct crashlog_status *status = &crashlog->info->status;
|
||||||
|
u32 reg = readl(crashlog->entry.disc_table + status->offset);
|
||||||
|
|
||||||
|
return !!(reg & bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pmt_crashlog_complete(struct crashlog_entry *crashlog)
|
||||||
|
{
|
||||||
/* return current value of the crashlog complete flag */
|
/* return current value of the crashlog complete flag */
|
||||||
return !!(control & CRASHLOG_FLAG_TRIGGER_COMPLETE);
|
return pmt_crashlog_rc(crashlog, crashlog->info->status.complete);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool pmt_crashlog_disabled(struct intel_pmt_entry *entry)
|
static bool pmt_crashlog_disabled(struct crashlog_entry *crashlog)
|
||||||
{
|
{
|
||||||
u32 control = readl(entry->disc_table + CONTROL_OFFSET);
|
|
||||||
|
|
||||||
/* return current value of the crashlog disabled flag */
|
/* return current value of the crashlog disabled flag */
|
||||||
return !!(control & CRASHLOG_FLAG_DISABLE);
|
return pmt_crashlog_rc(crashlog, crashlog->info->status.disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool pmt_crashlog_supported(struct intel_pmt_entry *entry)
|
static bool pmt_crashlog_supported(struct intel_pmt_entry *entry, u32 *crash_type, u32 *version)
|
||||||
{
|
{
|
||||||
u32 discovery_header = readl(entry->disc_table + CONTROL_OFFSET);
|
u32 discovery_header = readl(entry->disc_table + CONTROL_OFFSET);
|
||||||
u32 crash_type, version;
|
|
||||||
|
|
||||||
crash_type = GET_TYPE(discovery_header);
|
*crash_type = GET_TYPE(discovery_header);
|
||||||
version = GET_VERSION(discovery_header);
|
*version = GET_VERSION(discovery_header);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Currently we only recognize OOBMSM version 0 devices.
|
* Currently we only recognize OOBMSM (type 1) and version 0 or 2
|
||||||
* We can ignore all other crashlog devices in the system.
|
* devices.
|
||||||
|
*
|
||||||
|
* Ignore all other crashlog devices in the system.
|
||||||
*/
|
*/
|
||||||
return crash_type == CRASH_TYPE_OOBMSM && version == 0;
|
if (*crash_type == CRASH_TYPE_OOBMSM && (*version == 0 || *version == 2))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pmt_crashlog_set_disable(struct intel_pmt_entry *entry,
|
static void pmt_crashlog_set_disable(struct crashlog_entry *crashlog,
|
||||||
bool disable)
|
bool disable)
|
||||||
{
|
{
|
||||||
u32 control = readl(entry->disc_table + CONTROL_OFFSET);
|
pmt_crashlog_rmw(crashlog, crashlog->info->control.disable, disable);
|
||||||
|
|
||||||
/* clear trigger bits so we are only modifying disable flag */
|
|
||||||
control &= ~CRASHLOG_FLAG_TRIGGER_MASK;
|
|
||||||
|
|
||||||
if (disable)
|
|
||||||
control |= CRASHLOG_FLAG_DISABLE;
|
|
||||||
else
|
|
||||||
control &= ~CRASHLOG_FLAG_DISABLE;
|
|
||||||
|
|
||||||
writel(control, entry->disc_table + CONTROL_OFFSET);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pmt_crashlog_set_clear(struct intel_pmt_entry *entry)
|
static void pmt_crashlog_set_clear(struct crashlog_entry *crashlog)
|
||||||
{
|
{
|
||||||
u32 control = readl(entry->disc_table + CONTROL_OFFSET);
|
pmt_crashlog_rmw(crashlog, crashlog->info->control.clear, true);
|
||||||
|
|
||||||
control &= ~CRASHLOG_FLAG_TRIGGER_MASK;
|
|
||||||
control |= CRASHLOG_FLAG_TRIGGER_CLEAR;
|
|
||||||
|
|
||||||
writel(control, entry->disc_table + CONTROL_OFFSET);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pmt_crashlog_set_execute(struct intel_pmt_entry *entry)
|
static void pmt_crashlog_set_execute(struct crashlog_entry *crashlog)
|
||||||
{
|
{
|
||||||
u32 control = readl(entry->disc_table + CONTROL_OFFSET);
|
pmt_crashlog_rmw(crashlog, crashlog->info->control.manual, true);
|
||||||
|
}
|
||||||
|
|
||||||
control &= ~CRASHLOG_FLAG_TRIGGER_MASK;
|
static bool pmt_crashlog_cleared(struct crashlog_entry *crashlog)
|
||||||
control |= CRASHLOG_FLAG_TRIGGER_EXECUTE;
|
{
|
||||||
|
return pmt_crashlog_rc(crashlog, crashlog->info->status.cleared);
|
||||||
|
}
|
||||||
|
|
||||||
writel(control, entry->disc_table + CONTROL_OFFSET);
|
static bool pmt_crashlog_consumed(struct crashlog_entry *crashlog)
|
||||||
|
{
|
||||||
|
return pmt_crashlog_rc(crashlog, crashlog->info->status.consumed);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pmt_crashlog_set_consumed(struct crashlog_entry *crashlog)
|
||||||
|
{
|
||||||
|
pmt_crashlog_rmw(crashlog, crashlog->info->control.consume, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pmt_crashlog_error(struct crashlog_entry *crashlog)
|
||||||
|
{
|
||||||
|
return pmt_crashlog_rc(crashlog, crashlog->info->status.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pmt_crashlog_rearm(struct crashlog_entry *crashlog)
|
||||||
|
{
|
||||||
|
return pmt_crashlog_rc(crashlog, crashlog->info->status.rearmed);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pmt_crashlog_set_rearm(struct crashlog_entry *crashlog)
|
||||||
|
{
|
||||||
|
pmt_crashlog_rmw(crashlog, crashlog->info->control.rearm, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sysfs
|
* sysfs
|
||||||
*/
|
*/
|
||||||
|
static ssize_t
|
||||||
|
clear_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct crashlog_entry *crashlog = dev_get_drvdata(dev);
|
||||||
|
bool cleared = pmt_crashlog_cleared(crashlog);
|
||||||
|
|
||||||
|
return sysfs_emit(buf, "%d\n", cleared);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
clear_store(struct device *dev, struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct crashlog_entry *crashlog;
|
||||||
|
bool clear;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
crashlog = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
result = kstrtobool(buf, &clear);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
/* set bit only */
|
||||||
|
if (!clear)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
guard(mutex)(&crashlog->control_mutex);
|
||||||
|
|
||||||
|
pmt_crashlog_set_clear(crashlog);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
static DEVICE_ATTR_RW(clear);
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
consumed_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct crashlog_entry *crashlog = dev_get_drvdata(dev);
|
||||||
|
bool consumed = pmt_crashlog_consumed(crashlog);
|
||||||
|
|
||||||
|
return sysfs_emit(buf, "%d\n", consumed);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
consumed_store(struct device *dev, struct device_attribute *attr, const char *buf,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
struct crashlog_entry *crashlog;
|
||||||
|
bool consumed;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
crashlog = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
result = kstrtobool(buf, &consumed);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
/* set bit only */
|
||||||
|
if (!consumed)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
guard(mutex)(&crashlog->control_mutex);
|
||||||
|
|
||||||
|
if (pmt_crashlog_disabled(crashlog))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
if (!pmt_crashlog_complete(crashlog))
|
||||||
|
return -EEXIST;
|
||||||
|
|
||||||
|
pmt_crashlog_set_consumed(crashlog);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
static DEVICE_ATTR_RW(consumed);
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
enable_show(struct device *dev, struct device_attribute *attr, char *buf)
|
enable_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct intel_pmt_entry *entry = dev_get_drvdata(dev);
|
struct crashlog_entry *crashlog = dev_get_drvdata(dev);
|
||||||
int enabled = !pmt_crashlog_disabled(entry);
|
bool enabled = !pmt_crashlog_disabled(crashlog);
|
||||||
|
|
||||||
return sprintf(buf, "%d\n", enabled);
|
return sprintf(buf, "%d\n", enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
enable_store(struct device *dev, struct device_attribute *attr,
|
enable_store(struct device *dev, struct device_attribute *attr,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct crashlog_entry *entry;
|
struct crashlog_entry *crashlog;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
entry = dev_get_drvdata(dev);
|
crashlog = dev_get_drvdata(dev);
|
||||||
|
|
||||||
result = kstrtobool(buf, &enabled);
|
result = kstrtobool(buf, &enabled);
|
||||||
if (result)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
mutex_lock(&entry->control_mutex);
|
guard(mutex)(&crashlog->control_mutex);
|
||||||
pmt_crashlog_set_disable(&entry->entry, !enabled);
|
|
||||||
mutex_unlock(&entry->control_mutex);
|
pmt_crashlog_set_disable(crashlog, !enabled);
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
static DEVICE_ATTR_RW(enable);
|
static DEVICE_ATTR_RW(enable);
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
error_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct crashlog_entry *crashlog = dev_get_drvdata(dev);
|
||||||
|
bool error = pmt_crashlog_error(crashlog);
|
||||||
|
|
||||||
|
return sysfs_emit(buf, "%d\n", error);
|
||||||
|
}
|
||||||
|
static DEVICE_ATTR_RO(error);
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
rearm_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct crashlog_entry *crashlog = dev_get_drvdata(dev);
|
||||||
|
int rearmed = pmt_crashlog_rearm(crashlog);
|
||||||
|
|
||||||
|
return sysfs_emit(buf, "%d\n", rearmed);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
rearm_store(struct device *dev, struct device_attribute *attr, const char *buf,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
struct crashlog_entry *crashlog;
|
||||||
|
bool rearm;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
crashlog = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
result = kstrtobool(buf, &rearm);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
/* set only */
|
||||||
|
if (!rearm)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
guard(mutex)(&crashlog->control_mutex);
|
||||||
|
|
||||||
|
pmt_crashlog_set_rearm(crashlog);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
static DEVICE_ATTR_RW(rearm);
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
trigger_show(struct device *dev, struct device_attribute *attr, char *buf)
|
trigger_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct intel_pmt_entry *entry;
|
struct crashlog_entry *crashlog;
|
||||||
int trigger;
|
bool trigger;
|
||||||
|
|
||||||
entry = dev_get_drvdata(dev);
|
crashlog = dev_get_drvdata(dev);
|
||||||
trigger = pmt_crashlog_complete(entry);
|
trigger = pmt_crashlog_complete(crashlog);
|
||||||
|
|
||||||
return sprintf(buf, "%d\n", trigger);
|
return sprintf(buf, "%d\n", trigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
trigger_store(struct device *dev, struct device_attribute *attr,
|
trigger_store(struct device *dev, struct device_attribute *attr,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct crashlog_entry *entry;
|
struct crashlog_entry *crashlog;
|
||||||
bool trigger;
|
bool trigger;
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
entry = dev_get_drvdata(dev);
|
crashlog = dev_get_drvdata(dev);
|
||||||
|
|
||||||
result = kstrtobool(buf, &trigger);
|
result = kstrtobool(buf, &trigger);
|
||||||
if (result)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
mutex_lock(&entry->control_mutex);
|
guard(mutex)(&crashlog->control_mutex);
|
||||||
|
|
||||||
|
/* if device is currently disabled, return busy */
|
||||||
|
if (pmt_crashlog_disabled(crashlog))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
if (!trigger) {
|
if (!trigger) {
|
||||||
pmt_crashlog_set_clear(&entry->entry);
|
pmt_crashlog_set_clear(crashlog);
|
||||||
} else if (pmt_crashlog_complete(&entry->entry)) {
|
return count;
|
||||||
/* we cannot trigger a new crash if one is still pending */
|
|
||||||
result = -EEXIST;
|
|
||||||
goto err;
|
|
||||||
} else if (pmt_crashlog_disabled(&entry->entry)) {
|
|
||||||
/* if device is currently disabled, return busy */
|
|
||||||
result = -EBUSY;
|
|
||||||
goto err;
|
|
||||||
} else {
|
|
||||||
pmt_crashlog_set_execute(&entry->entry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result = count;
|
/* we cannot trigger a new crash if one is still pending */
|
||||||
err:
|
if (pmt_crashlog_complete(crashlog))
|
||||||
mutex_unlock(&entry->control_mutex);
|
return -EEXIST;
|
||||||
return result;
|
|
||||||
|
pmt_crashlog_set_execute(crashlog);
|
||||||
|
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
static DEVICE_ATTR_RW(trigger);
|
static DEVICE_ATTR_RW(trigger);
|
||||||
|
|
||||||
static struct attribute *pmt_crashlog_attrs[] = {
|
static struct attribute *pmt_crashlog_type1_ver0_attrs[] = {
|
||||||
&dev_attr_enable.attr,
|
&dev_attr_enable.attr,
|
||||||
&dev_attr_trigger.attr,
|
&dev_attr_trigger.attr,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct attribute_group pmt_crashlog_group = {
|
static struct attribute *pmt_crashlog_type1_ver2_attrs[] = {
|
||||||
.attrs = pmt_crashlog_attrs,
|
&dev_attr_clear.attr,
|
||||||
|
&dev_attr_consumed.attr,
|
||||||
|
&dev_attr_enable.attr,
|
||||||
|
&dev_attr_error.attr,
|
||||||
|
&dev_attr_rearm.attr,
|
||||||
|
&dev_attr_trigger.attr,
|
||||||
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct attribute_group pmt_crashlog_type1_ver0_group = {
|
||||||
|
.attrs = pmt_crashlog_type1_ver0_attrs,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct attribute_group pmt_crashlog_type1_ver2_group = {
|
||||||
|
.attrs = pmt_crashlog_type1_ver2_attrs,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct crashlog_info crashlog_type1_ver0 = {
|
||||||
|
.status.offset = TYPE1_VER0_STATUS_OFFSET,
|
||||||
|
.status.cleared = TYPE1_VER0_CLEAR,
|
||||||
|
.status.complete = TYPE1_VER0_COMPLETE,
|
||||||
|
.status.disabled = TYPE1_VER0_DISABLE,
|
||||||
|
|
||||||
|
.control.offset = TYPE1_VER0_CONTROL_OFFSET,
|
||||||
|
.control.trigger_mask = TYPE1_VER0_TRIGGER_MASK,
|
||||||
|
.control.clear = TYPE1_VER0_CLEAR,
|
||||||
|
.control.disable = TYPE1_VER0_DISABLE,
|
||||||
|
.control.manual = TYPE1_VER0_EXECUTE,
|
||||||
|
.attr_grp = &pmt_crashlog_type1_ver0_group,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct crashlog_info crashlog_type1_ver2 = {
|
||||||
|
.status.offset = TYPE1_VER2_STATUS_OFFSET,
|
||||||
|
.status.clear_supported = TYPE1_VER2_CLEAR_SUPPORT,
|
||||||
|
.status.cleared = TYPE1_VER2_CLEARED,
|
||||||
|
.status.complete = TYPE1_VER2_COMPLETE,
|
||||||
|
.status.consumed = TYPE1_VER2_CONSUMED,
|
||||||
|
.status.disabled = TYPE1_VER2_DISABLED,
|
||||||
|
.status.error = TYPE1_VER2_ERROR,
|
||||||
|
.status.in_progress = TYPE1_VER2_IN_PROGRESS,
|
||||||
|
.status.rearmed = TYPE1_VER2_REARMED,
|
||||||
|
|
||||||
|
.control.offset = TYPE1_VER2_CONTROL_OFFSET,
|
||||||
|
.control.trigger_mask = TYPE1_VER2_TRIGGER_MASK,
|
||||||
|
.control.clear = TYPE1_VER2_CLEAR,
|
||||||
|
.control.consume = TYPE1_VER2_CONSUME,
|
||||||
|
.control.disable = TYPE1_VER2_DISABLE,
|
||||||
|
.control.manual = TYPE1_VER2_EXECUTE,
|
||||||
|
.control.rearm = TYPE1_VER2_REARM,
|
||||||
|
.attr_grp = &pmt_crashlog_type1_ver2_group,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct crashlog_info *select_crashlog_info(u32 type, u32 version)
|
||||||
|
{
|
||||||
|
if (version == 0)
|
||||||
|
return &crashlog_type1_ver0;
|
||||||
|
|
||||||
|
return &crashlog_type1_ver2;
|
||||||
|
}
|
||||||
|
|
||||||
static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry,
|
static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry,
|
||||||
struct device *dev)
|
struct device *dev)
|
||||||
{
|
{
|
||||||
void __iomem *disc_table = entry->disc_table;
|
void __iomem *disc_table = entry->disc_table;
|
||||||
struct intel_pmt_header *header = &entry->header;
|
struct intel_pmt_header *header = &entry->header;
|
||||||
struct crashlog_entry *crashlog;
|
struct crashlog_entry *crashlog;
|
||||||
|
u32 version;
|
||||||
|
u32 type;
|
||||||
|
|
||||||
if (!pmt_crashlog_supported(entry))
|
if (!pmt_crashlog_supported(entry, &type, &version))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* initialize control mutex */
|
/* initialize the crashlog struct */
|
||||||
crashlog = container_of(entry, struct crashlog_entry, entry);
|
crashlog = container_of(entry, struct crashlog_entry, entry);
|
||||||
mutex_init(&crashlog->control_mutex);
|
mutex_init(&crashlog->control_mutex);
|
||||||
|
|
||||||
|
crashlog->info = select_crashlog_info(type, version);
|
||||||
|
|
||||||
header->access_type = GET_ACCESS(readl(disc_table));
|
header->access_type = GET_ACCESS(readl(disc_table));
|
||||||
header->guid = readl(disc_table + GUID_OFFSET);
|
header->guid = readl(disc_table + GUID_OFFSET);
|
||||||
header->base_offset = readl(disc_table + BASE_OFFSET);
|
header->base_offset = readl(disc_table + BASE_OFFSET);
|
||||||
|
|
@ -243,6 +521,8 @@ static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry,
|
||||||
/* Size is measured in DWORDS, but accessor returns bytes */
|
/* Size is measured in DWORDS, but accessor returns bytes */
|
||||||
header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET));
|
header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET));
|
||||||
|
|
||||||
|
entry->attr_grp = crashlog->info->attr_grp;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -250,7 +530,6 @@ static DEFINE_XARRAY_ALLOC(crashlog_array);
|
||||||
static struct intel_pmt_namespace pmt_crashlog_ns = {
|
static struct intel_pmt_namespace pmt_crashlog_ns = {
|
||||||
.name = "crashlog",
|
.name = "crashlog",
|
||||||
.xa = &crashlog_array,
|
.xa = &crashlog_array,
|
||||||
.attr_grp = &pmt_crashlog_group,
|
|
||||||
.pmt_header_decode = pmt_crashlog_header_decode,
|
.pmt_header_decode = pmt_crashlog_header_decode,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -262,8 +541,12 @@ static void pmt_crashlog_remove(struct auxiliary_device *auxdev)
|
||||||
struct pmt_crashlog_priv *priv = auxiliary_get_drvdata(auxdev);
|
struct pmt_crashlog_priv *priv = auxiliary_get_drvdata(auxdev);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < priv->num_entries; i++)
|
for (i = 0; i < priv->num_entries; i++) {
|
||||||
intel_pmt_dev_destroy(&priv->entry[i].entry, &pmt_crashlog_ns);
|
struct crashlog_entry *crashlog = &priv->entry[i];
|
||||||
|
|
||||||
|
intel_pmt_dev_destroy(&crashlog->entry, &pmt_crashlog_ns);
|
||||||
|
mutex_destroy(&crashlog->control_mutex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pmt_crashlog_probe(struct auxiliary_device *auxdev,
|
static int pmt_crashlog_probe(struct auxiliary_device *auxdev,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Intel Platform Monitory Technology Discovery KUNIT tests
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025, Intel Corporation.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <kunit/test.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/intel_pmt_features.h>
|
||||||
|
#include <linux/intel_vsec.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#define PMT_FEATURE_COUNT (FEATURE_MAX + 1)
|
||||||
|
|
||||||
|
static void
|
||||||
|
validate_pmt_regions(struct kunit *test, struct pmt_feature_group *feature_group, int feature_id)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
kunit_info(test, "Feature ID %d [%s] has %d regions.\n", feature_id,
|
||||||
|
pmt_feature_names[feature_id], feature_group->count);
|
||||||
|
|
||||||
|
for (i = 0; i < feature_group->count; i++) {
|
||||||
|
struct telemetry_region *region = &feature_group->regions[i];
|
||||||
|
|
||||||
|
kunit_info(test, " - Region %d: cdie_mask=%u, package_id=%u, partition=%u, segment=%u,",
|
||||||
|
i, region->plat_info.cdie_mask, region->plat_info.package_id,
|
||||||
|
region->plat_info.partition, region->plat_info.segment);
|
||||||
|
kunit_info(test, "\t\tbus=%u, device=%u, function=%u, guid=0x%x,",
|
||||||
|
region->plat_info.bus_number, region->plat_info.device_number,
|
||||||
|
region->plat_info.function_number, region->guid);
|
||||||
|
kunit_info(test, "\t\taddr=%p, size=%zu, num_rmids=%u", region->addr, region->size,
|
||||||
|
region->num_rmids);
|
||||||
|
|
||||||
|
|
||||||
|
KUNIT_ASSERT_GE(test, region->plat_info.cdie_mask, 0);
|
||||||
|
KUNIT_ASSERT_GE(test, region->plat_info.package_id, 0);
|
||||||
|
KUNIT_ASSERT_GE(test, region->plat_info.partition, 0);
|
||||||
|
KUNIT_ASSERT_GE(test, region->plat_info.segment, 0);
|
||||||
|
KUNIT_ASSERT_GE(test, region->plat_info.bus_number, 0);
|
||||||
|
KUNIT_ASSERT_GE(test, region->plat_info.device_number, 0);
|
||||||
|
KUNIT_ASSERT_GE(test, region->plat_info.function_number, 0);
|
||||||
|
|
||||||
|
KUNIT_ASSERT_NE(test, region->guid, 0);
|
||||||
|
|
||||||
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, (__force const void *)region->addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void linebreak(struct kunit *test)
|
||||||
|
{
|
||||||
|
kunit_info(test, "*****************************************************************************\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_intel_pmt_get_regions_by_feature(struct kunit *test)
|
||||||
|
{
|
||||||
|
struct pmt_feature_group *feature_group;
|
||||||
|
int num_available = 0;
|
||||||
|
int feature_id;
|
||||||
|
|
||||||
|
/* Iterate through all possible feature IDs */
|
||||||
|
for (feature_id = 1; feature_id < PMT_FEATURE_COUNT; feature_id++, linebreak(test)) {
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
if (!pmt_feature_id_is_valid(feature_id))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
name = pmt_feature_names[feature_id];
|
||||||
|
|
||||||
|
feature_group = intel_pmt_get_regions_by_feature(feature_id);
|
||||||
|
if (IS_ERR(feature_group)) {
|
||||||
|
if (PTR_ERR(feature_group) == -ENOENT)
|
||||||
|
kunit_warn(test, "intel_pmt_get_regions_by_feature() reporting feature %d [%s] is not present.\n",
|
||||||
|
feature_id, name);
|
||||||
|
else
|
||||||
|
kunit_warn(test, "intel_pmt_get_regions_by_feature() returned error %ld while attempt to lookup %d [%s].\n",
|
||||||
|
PTR_ERR(feature_group), feature_id, name);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!feature_group) {
|
||||||
|
kunit_warn(test, "Feature ID %d: %s is not available.\n", feature_id, name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
num_available++;
|
||||||
|
|
||||||
|
validate_pmt_regions(test, feature_group, feature_id);
|
||||||
|
|
||||||
|
intel_pmt_put_feature_group(feature_group);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_available == 0)
|
||||||
|
kunit_warn(test, "No PMT region groups were available for any feature ID (0-10).\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct kunit_case intel_pmt_discovery_test_cases[] = {
|
||||||
|
KUNIT_CASE(test_intel_pmt_get_regions_by_feature),
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct kunit_suite intel_pmt_discovery_test_suite = {
|
||||||
|
.name = "pmt_discovery_test",
|
||||||
|
.test_cases = intel_pmt_discovery_test_cases,
|
||||||
|
};
|
||||||
|
|
||||||
|
kunit_test_suite(intel_pmt_discovery_test_suite);
|
||||||
|
|
||||||
|
MODULE_IMPORT_NS("INTEL_PMT_DISCOVERY");
|
||||||
|
MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
|
||||||
|
MODULE_DESCRIPTION("Intel PMT Discovery KUNIT test driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
@ -0,0 +1,635 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Intel Platform Monitory Technology Discovery driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025, Intel Corporation.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/auxiliary_bus.h>
|
||||||
|
#include <linux/bitfield.h>
|
||||||
|
#include <linux/bits.h>
|
||||||
|
#include <linux/bug.h>
|
||||||
|
#include <linux/cleanup.h>
|
||||||
|
#include <linux/container_of.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/ioport.h>
|
||||||
|
#include <linux/kdev_t.h>
|
||||||
|
#include <linux/kobject.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/overflow.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/string_choices.h>
|
||||||
|
#include <linux/sysfs.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
|
||||||
|
#include <linux/intel_pmt_features.h>
|
||||||
|
#include <linux/intel_vsec.h>
|
||||||
|
|
||||||
|
#include "class.h"
|
||||||
|
|
||||||
|
#define MAX_FEATURE_VERSION 0
|
||||||
|
#define DT_TBIR GENMASK(2, 0)
|
||||||
|
#define FEAT_ATTR_SIZE(x) ((x) * sizeof(u32))
|
||||||
|
#define PMT_GUID_SIZE(x) ((x) * sizeof(u32))
|
||||||
|
#define PMT_ACCESS_TYPE_RSVD 0xF
|
||||||
|
#define SKIP_FEATURE 1
|
||||||
|
|
||||||
|
struct feature_discovery_table {
|
||||||
|
u32 access_type:4;
|
||||||
|
u32 version:8;
|
||||||
|
u32 size:16;
|
||||||
|
u32 reserved:4;
|
||||||
|
u32 id;
|
||||||
|
u32 offset;
|
||||||
|
u32 reserved2;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Common feature table header */
|
||||||
|
struct feature_header {
|
||||||
|
u32 attr_size:8;
|
||||||
|
u32 num_guids:8;
|
||||||
|
u32 reserved:16;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Feature attribute fields */
|
||||||
|
struct caps {
|
||||||
|
u32 caps;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct command {
|
||||||
|
u32 max_stream_size:16;
|
||||||
|
u32 max_command_size:16;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct watcher {
|
||||||
|
u32 reserved:21;
|
||||||
|
u32 period:11;
|
||||||
|
struct command command;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rmid {
|
||||||
|
u32 num_rmids:16; /* Number of Resource Monitoring IDs */
|
||||||
|
u32 reserved:16;
|
||||||
|
struct watcher watcher;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct feature_table {
|
||||||
|
struct feature_header header;
|
||||||
|
struct caps caps;
|
||||||
|
union {
|
||||||
|
struct command command;
|
||||||
|
struct watcher watcher;
|
||||||
|
struct rmid rmid;
|
||||||
|
};
|
||||||
|
u32 *guids;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* For backreference in struct feature */
|
||||||
|
struct pmt_features_priv;
|
||||||
|
|
||||||
|
struct feature {
|
||||||
|
struct feature_table table;
|
||||||
|
struct kobject kobj;
|
||||||
|
struct pmt_features_priv *priv;
|
||||||
|
struct list_head list;
|
||||||
|
const struct attribute_group *attr_group;
|
||||||
|
enum pmt_feature_id id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_features_priv {
|
||||||
|
struct device *parent;
|
||||||
|
struct device *dev;
|
||||||
|
int count;
|
||||||
|
u32 mask;
|
||||||
|
struct feature feature[];
|
||||||
|
};
|
||||||
|
|
||||||
|
static LIST_HEAD(pmt_feature_list);
|
||||||
|
static DEFINE_MUTEX(feature_list_lock);
|
||||||
|
|
||||||
|
#define to_pmt_feature(x) container_of(x, struct feature, kobj)
|
||||||
|
static void pmt_feature_release(struct kobject *kobj)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t caps_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct feature *feature = to_pmt_feature(kobj);
|
||||||
|
struct pmt_cap **pmt_caps;
|
||||||
|
u32 caps = feature->table.caps.caps;
|
||||||
|
ssize_t ret = 0;
|
||||||
|
|
||||||
|
switch (feature->id) {
|
||||||
|
case FEATURE_PER_CORE_PERF_TELEM:
|
||||||
|
pmt_caps = pmt_caps_pcpt;
|
||||||
|
break;
|
||||||
|
case FEATURE_PER_CORE_ENV_TELEM:
|
||||||
|
pmt_caps = pmt_caps_pcet;
|
||||||
|
break;
|
||||||
|
case FEATURE_PER_RMID_PERF_TELEM:
|
||||||
|
pmt_caps = pmt_caps_rmid_perf;
|
||||||
|
break;
|
||||||
|
case FEATURE_ACCEL_TELEM:
|
||||||
|
pmt_caps = pmt_caps_accel;
|
||||||
|
break;
|
||||||
|
case FEATURE_UNCORE_TELEM:
|
||||||
|
pmt_caps = pmt_caps_uncore;
|
||||||
|
break;
|
||||||
|
case FEATURE_CRASH_LOG:
|
||||||
|
pmt_caps = pmt_caps_crashlog;
|
||||||
|
break;
|
||||||
|
case FEATURE_PETE_LOG:
|
||||||
|
pmt_caps = pmt_caps_pete;
|
||||||
|
break;
|
||||||
|
case FEATURE_TPMI_CTRL:
|
||||||
|
pmt_caps = pmt_caps_tpmi;
|
||||||
|
break;
|
||||||
|
case FEATURE_TRACING:
|
||||||
|
pmt_caps = pmt_caps_tracing;
|
||||||
|
break;
|
||||||
|
case FEATURE_PER_RMID_ENERGY_TELEM:
|
||||||
|
pmt_caps = pmt_caps_rmid_energy;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*pmt_caps) {
|
||||||
|
struct pmt_cap *pmt_cap = *pmt_caps;
|
||||||
|
|
||||||
|
while (pmt_cap->name) {
|
||||||
|
ret += sysfs_emit_at(buf, ret, "%-40s Available: %s\n", pmt_cap->name,
|
||||||
|
str_yes_no(pmt_cap->mask & caps));
|
||||||
|
pmt_cap++;
|
||||||
|
}
|
||||||
|
pmt_caps++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
static struct kobj_attribute caps_attribute = __ATTR_RO(caps);
|
||||||
|
|
||||||
|
static struct watcher *get_watcher(struct feature *feature)
|
||||||
|
{
|
||||||
|
switch (feature_layout[feature->id]) {
|
||||||
|
case LAYOUT_RMID:
|
||||||
|
return &feature->table.rmid.watcher;
|
||||||
|
case LAYOUT_WATCHER:
|
||||||
|
return &feature->table.watcher;
|
||||||
|
default:
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct command *get_command(struct feature *feature)
|
||||||
|
{
|
||||||
|
switch (feature_layout[feature->id]) {
|
||||||
|
case LAYOUT_RMID:
|
||||||
|
return &feature->table.rmid.watcher.command;
|
||||||
|
case LAYOUT_WATCHER:
|
||||||
|
return &feature->table.watcher.command;
|
||||||
|
case LAYOUT_COMMAND:
|
||||||
|
return &feature->table.command;
|
||||||
|
default:
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t num_rmids_show(struct kobject *kobj,
|
||||||
|
struct kobj_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct feature *feature = to_pmt_feature(kobj);
|
||||||
|
|
||||||
|
return sysfs_emit(buf, "%u\n", feature->table.rmid.num_rmids);
|
||||||
|
}
|
||||||
|
static struct kobj_attribute num_rmids_attribute = __ATTR_RO(num_rmids);
|
||||||
|
|
||||||
|
static ssize_t min_watcher_period_ms_show(struct kobject *kobj,
|
||||||
|
struct kobj_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct feature *feature = to_pmt_feature(kobj);
|
||||||
|
struct watcher *watcher = get_watcher(feature);
|
||||||
|
|
||||||
|
if (IS_ERR(watcher))
|
||||||
|
return PTR_ERR(watcher);
|
||||||
|
|
||||||
|
return sysfs_emit(buf, "%u\n", watcher->period);
|
||||||
|
}
|
||||||
|
static struct kobj_attribute min_watcher_period_ms_attribute =
|
||||||
|
__ATTR_RO(min_watcher_period_ms);
|
||||||
|
|
||||||
|
static ssize_t max_stream_size_show(struct kobject *kobj,
|
||||||
|
struct kobj_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct feature *feature = to_pmt_feature(kobj);
|
||||||
|
struct command *command = get_command(feature);
|
||||||
|
|
||||||
|
if (IS_ERR(command))
|
||||||
|
return PTR_ERR(command);
|
||||||
|
|
||||||
|
return sysfs_emit(buf, "%u\n", command->max_stream_size);
|
||||||
|
}
|
||||||
|
static struct kobj_attribute max_stream_size_attribute =
|
||||||
|
__ATTR_RO(max_stream_size);
|
||||||
|
|
||||||
|
static ssize_t max_command_size_show(struct kobject *kobj,
|
||||||
|
struct kobj_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct feature *feature = to_pmt_feature(kobj);
|
||||||
|
struct command *command = get_command(feature);
|
||||||
|
|
||||||
|
if (IS_ERR(command))
|
||||||
|
return PTR_ERR(command);
|
||||||
|
|
||||||
|
return sysfs_emit(buf, "%u\n", command->max_command_size);
|
||||||
|
}
|
||||||
|
static struct kobj_attribute max_command_size_attribute =
|
||||||
|
__ATTR_RO(max_command_size);
|
||||||
|
|
||||||
|
static ssize_t guids_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct feature *feature = to_pmt_feature(kobj);
|
||||||
|
int i, count = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < feature->table.header.num_guids; i++)
|
||||||
|
count += sysfs_emit_at(buf, count, "0x%x\n",
|
||||||
|
feature->table.guids[i]);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
static struct kobj_attribute guids_attribute = __ATTR_RO(guids);
|
||||||
|
|
||||||
|
static struct attribute *pmt_feature_rmid_attrs[] = {
|
||||||
|
&caps_attribute.attr,
|
||||||
|
&num_rmids_attribute.attr,
|
||||||
|
&min_watcher_period_ms_attribute.attr,
|
||||||
|
&max_stream_size_attribute.attr,
|
||||||
|
&max_command_size_attribute.attr,
|
||||||
|
&guids_attribute.attr,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
ATTRIBUTE_GROUPS(pmt_feature_rmid);
|
||||||
|
|
||||||
|
static const struct kobj_type pmt_feature_rmid_ktype = {
|
||||||
|
.sysfs_ops = &kobj_sysfs_ops,
|
||||||
|
.release = pmt_feature_release,
|
||||||
|
.default_groups = pmt_feature_rmid_groups,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct attribute *pmt_feature_watcher_attrs[] = {
|
||||||
|
&caps_attribute.attr,
|
||||||
|
&min_watcher_period_ms_attribute.attr,
|
||||||
|
&max_stream_size_attribute.attr,
|
||||||
|
&max_command_size_attribute.attr,
|
||||||
|
&guids_attribute.attr,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
ATTRIBUTE_GROUPS(pmt_feature_watcher);
|
||||||
|
|
||||||
|
static const struct kobj_type pmt_feature_watcher_ktype = {
|
||||||
|
.sysfs_ops = &kobj_sysfs_ops,
|
||||||
|
.release = pmt_feature_release,
|
||||||
|
.default_groups = pmt_feature_watcher_groups,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct attribute *pmt_feature_command_attrs[] = {
|
||||||
|
&caps_attribute.attr,
|
||||||
|
&max_stream_size_attribute.attr,
|
||||||
|
&max_command_size_attribute.attr,
|
||||||
|
&guids_attribute.attr,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
ATTRIBUTE_GROUPS(pmt_feature_command);
|
||||||
|
|
||||||
|
static const struct kobj_type pmt_feature_command_ktype = {
|
||||||
|
.sysfs_ops = &kobj_sysfs_ops,
|
||||||
|
.release = pmt_feature_release,
|
||||||
|
.default_groups = pmt_feature_command_groups,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct attribute *pmt_feature_guids_attrs[] = {
|
||||||
|
&caps_attribute.attr,
|
||||||
|
&guids_attribute.attr,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
ATTRIBUTE_GROUPS(pmt_feature_guids);
|
||||||
|
|
||||||
|
static const struct kobj_type pmt_feature_guids_ktype = {
|
||||||
|
.sysfs_ops = &kobj_sysfs_ops,
|
||||||
|
.release = pmt_feature_release,
|
||||||
|
.default_groups = pmt_feature_guids_groups,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
pmt_feature_get_disc_table(struct pmt_features_priv *priv,
|
||||||
|
struct resource *disc_res,
|
||||||
|
struct feature_discovery_table *disc_tbl)
|
||||||
|
{
|
||||||
|
void __iomem *disc_base;
|
||||||
|
|
||||||
|
disc_base = devm_ioremap_resource(priv->dev, disc_res);
|
||||||
|
if (IS_ERR(disc_base))
|
||||||
|
return PTR_ERR(disc_base);
|
||||||
|
|
||||||
|
memcpy_fromio(disc_tbl, disc_base, sizeof(*disc_tbl));
|
||||||
|
|
||||||
|
devm_iounmap(priv->dev, disc_base);
|
||||||
|
|
||||||
|
if (priv->mask & BIT(disc_tbl->id))
|
||||||
|
return dev_err_probe(priv->dev, -EINVAL, "Duplicate feature: %s\n",
|
||||||
|
pmt_feature_names[disc_tbl->id]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some devices may expose non-functioning entries that are
|
||||||
|
* reserved for future use. They have zero size. Do not fail
|
||||||
|
* probe for these. Just ignore them.
|
||||||
|
*/
|
||||||
|
if (disc_tbl->size == 0 || disc_tbl->access_type == PMT_ACCESS_TYPE_RSVD)
|
||||||
|
return SKIP_FEATURE;
|
||||||
|
|
||||||
|
if (disc_tbl->version > MAX_FEATURE_VERSION)
|
||||||
|
return SKIP_FEATURE;
|
||||||
|
|
||||||
|
if (!pmt_feature_id_is_valid(disc_tbl->id))
|
||||||
|
return SKIP_FEATURE;
|
||||||
|
|
||||||
|
priv->mask |= BIT(disc_tbl->id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
pmt_feature_get_feature_table(struct pmt_features_priv *priv,
|
||||||
|
struct feature *feature,
|
||||||
|
struct feature_discovery_table *disc_tbl,
|
||||||
|
struct resource *disc_res)
|
||||||
|
{
|
||||||
|
struct feature_table *feat_tbl = &feature->table;
|
||||||
|
struct feature_header *header;
|
||||||
|
struct resource res = {};
|
||||||
|
resource_size_t res_size;
|
||||||
|
void __iomem *feat_base, *feat_offset;
|
||||||
|
void *tbl_offset;
|
||||||
|
size_t size;
|
||||||
|
u32 *guids;
|
||||||
|
u8 tbir;
|
||||||
|
|
||||||
|
tbir = FIELD_GET(DT_TBIR, disc_tbl->offset);
|
||||||
|
|
||||||
|
switch (disc_tbl->access_type) {
|
||||||
|
case ACCESS_LOCAL:
|
||||||
|
if (tbir)
|
||||||
|
return dev_err_probe(priv->dev, -EINVAL,
|
||||||
|
"Unsupported BAR index %u for access type %u\n",
|
||||||
|
tbir, disc_tbl->access_type);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For access_type LOCAL, the base address is as follows:
|
||||||
|
* base address = end of discovery region + base offset + 1
|
||||||
|
*/
|
||||||
|
res = DEFINE_RES_MEM(disc_res->end + disc_tbl->offset + 1,
|
||||||
|
disc_tbl->size * sizeof(u32));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return dev_err_probe(priv->dev, -EINVAL, "Unrecognized access_type %u\n",
|
||||||
|
disc_tbl->access_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
feature->id = disc_tbl->id;
|
||||||
|
|
||||||
|
/* Get the feature table */
|
||||||
|
feat_base = devm_ioremap_resource(priv->dev, &res);
|
||||||
|
if (IS_ERR(feat_base))
|
||||||
|
return PTR_ERR(feat_base);
|
||||||
|
|
||||||
|
feat_offset = feat_base;
|
||||||
|
tbl_offset = feat_tbl;
|
||||||
|
|
||||||
|
/* Get the header */
|
||||||
|
header = &feat_tbl->header;
|
||||||
|
memcpy_fromio(header, feat_offset, sizeof(*header));
|
||||||
|
|
||||||
|
/* Validate fields fit within mapped resource */
|
||||||
|
size = sizeof(*header) + FEAT_ATTR_SIZE(header->attr_size) +
|
||||||
|
PMT_GUID_SIZE(header->num_guids);
|
||||||
|
res_size = resource_size(&res);
|
||||||
|
if (WARN(size > res_size, "Bad table size %zu > %pa", size, &res_size))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Get the feature attributes, including capability fields */
|
||||||
|
tbl_offset += sizeof(*header);
|
||||||
|
feat_offset += sizeof(*header);
|
||||||
|
|
||||||
|
memcpy_fromio(tbl_offset, feat_offset, FEAT_ATTR_SIZE(header->attr_size));
|
||||||
|
|
||||||
|
/* Finally, get the guids */
|
||||||
|
guids = devm_kmalloc(priv->dev, PMT_GUID_SIZE(header->num_guids), GFP_KERNEL);
|
||||||
|
if (!guids)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
feat_offset += FEAT_ATTR_SIZE(header->attr_size);
|
||||||
|
|
||||||
|
memcpy_fromio(guids, feat_offset, PMT_GUID_SIZE(header->num_guids));
|
||||||
|
|
||||||
|
feat_tbl->guids = guids;
|
||||||
|
|
||||||
|
devm_iounmap(priv->dev, feat_base);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pmt_features_add_feat(struct feature *feature)
|
||||||
|
{
|
||||||
|
guard(mutex)(&feature_list_lock);
|
||||||
|
list_add(&feature->list, &pmt_feature_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pmt_features_remove_feat(struct feature *feature)
|
||||||
|
{
|
||||||
|
guard(mutex)(&feature_list_lock);
|
||||||
|
list_del(&feature->list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the discovery table and use it to get the feature table */
|
||||||
|
static int pmt_features_discovery(struct pmt_features_priv *priv,
|
||||||
|
struct feature *feature,
|
||||||
|
struct intel_vsec_device *ivdev,
|
||||||
|
int idx)
|
||||||
|
{
|
||||||
|
struct feature_discovery_table disc_tbl = {}; /* Avoid false warning */
|
||||||
|
struct resource *disc_res = &ivdev->resource[idx];
|
||||||
|
const struct kobj_type *ktype;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pmt_feature_get_disc_table(priv, disc_res, &disc_tbl);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = pmt_feature_get_feature_table(priv, feature, &disc_tbl, disc_res);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
switch (feature_layout[feature->id]) {
|
||||||
|
case LAYOUT_RMID:
|
||||||
|
ktype = &pmt_feature_rmid_ktype;
|
||||||
|
feature->attr_group = &pmt_feature_rmid_group;
|
||||||
|
break;
|
||||||
|
case LAYOUT_WATCHER:
|
||||||
|
ktype = &pmt_feature_watcher_ktype;
|
||||||
|
feature->attr_group = &pmt_feature_watcher_group;
|
||||||
|
break;
|
||||||
|
case LAYOUT_COMMAND:
|
||||||
|
ktype = &pmt_feature_command_ktype;
|
||||||
|
feature->attr_group = &pmt_feature_command_group;
|
||||||
|
break;
|
||||||
|
case LAYOUT_CAPS_ONLY:
|
||||||
|
ktype = &pmt_feature_guids_ktype;
|
||||||
|
feature->attr_group = &pmt_feature_guids_group;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = kobject_init_and_add(&feature->kobj, ktype, &priv->dev->kobj,
|
||||||
|
"%s", pmt_feature_names[feature->id]);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
kobject_uevent(&feature->kobj, KOBJ_ADD);
|
||||||
|
pmt_features_add_feat(feature);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pmt_features_remove(struct auxiliary_device *auxdev)
|
||||||
|
{
|
||||||
|
struct pmt_features_priv *priv = auxiliary_get_drvdata(auxdev);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < priv->count; i++) {
|
||||||
|
struct feature *feature = &priv->feature[i];
|
||||||
|
|
||||||
|
pmt_features_remove_feat(feature);
|
||||||
|
sysfs_remove_group(&feature->kobj, feature->attr_group);
|
||||||
|
kobject_put(&feature->kobj);
|
||||||
|
}
|
||||||
|
|
||||||
|
device_unregister(priv->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pmt_features_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
|
||||||
|
{
|
||||||
|
struct intel_vsec_device *ivdev = auxdev_to_ivdev(auxdev);
|
||||||
|
struct pmt_features_priv *priv;
|
||||||
|
size_t size;
|
||||||
|
int ret, i;
|
||||||
|
|
||||||
|
size = struct_size(priv, feature, ivdev->num_resources);
|
||||||
|
priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priv->parent = &ivdev->pcidev->dev;
|
||||||
|
auxiliary_set_drvdata(auxdev, priv);
|
||||||
|
|
||||||
|
priv->dev = device_create(&intel_pmt_class, &auxdev->dev, MKDEV(0, 0), priv,
|
||||||
|
"%s-%s", "features", dev_name(priv->parent));
|
||||||
|
if (IS_ERR(priv->dev))
|
||||||
|
return dev_err_probe(priv->dev, PTR_ERR(priv->dev),
|
||||||
|
"Could not create %s-%s device node\n",
|
||||||
|
"features", dev_name(priv->dev));
|
||||||
|
|
||||||
|
/* Initialize each feature */
|
||||||
|
for (i = 0; i < ivdev->num_resources; i++) {
|
||||||
|
struct feature *feature = &priv->feature[priv->count];
|
||||||
|
|
||||||
|
ret = pmt_features_discovery(priv, feature, ivdev, i);
|
||||||
|
if (ret == SKIP_FEATURE)
|
||||||
|
continue;
|
||||||
|
if (ret != 0)
|
||||||
|
goto abort_probe;
|
||||||
|
|
||||||
|
feature->priv = priv;
|
||||||
|
priv->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
abort_probe:
|
||||||
|
/*
|
||||||
|
* Only fully initialized features are tracked in priv->count, which is
|
||||||
|
* incremented only after a feature is completely set up (i.e., after
|
||||||
|
* discovery and sysfs registration). If feature initialization fails,
|
||||||
|
* the failing feature's state is local and does not require rollback.
|
||||||
|
*
|
||||||
|
* Therefore, on error, we can safely call the driver's remove() routine
|
||||||
|
* pmt_features_remove() to clean up only those features that were
|
||||||
|
* fully initialized and counted. All other resources are device-managed
|
||||||
|
* and will be cleaned up automatically during device_unregister().
|
||||||
|
*/
|
||||||
|
pmt_features_remove(auxdev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pmt_get_features(struct intel_pmt_entry *entry, struct feature *f)
|
||||||
|
{
|
||||||
|
int num_guids = f->table.header.num_guids;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < num_guids; i++) {
|
||||||
|
if (f->table.guids[i] != entry->guid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
entry->feature_flags |= BIT(f->id);
|
||||||
|
|
||||||
|
if (feature_layout[f->id] == LAYOUT_RMID)
|
||||||
|
entry->num_rmids = f->table.rmid.num_rmids;
|
||||||
|
else
|
||||||
|
entry->num_rmids = 0; /* entry is kzalloc but set anyway */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void intel_pmt_get_features(struct intel_pmt_entry *entry)
|
||||||
|
{
|
||||||
|
struct feature *feature;
|
||||||
|
|
||||||
|
mutex_lock(&feature_list_lock);
|
||||||
|
list_for_each_entry(feature, &pmt_feature_list, list) {
|
||||||
|
if (feature->priv->parent != &entry->ep->pcidev->dev)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pmt_get_features(entry, feature);
|
||||||
|
}
|
||||||
|
mutex_unlock(&feature_list_lock);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS_GPL(intel_pmt_get_features, "INTEL_PMT");
|
||||||
|
|
||||||
|
static const struct auxiliary_device_id pmt_features_id_table[] = {
|
||||||
|
{ .name = "intel_vsec.discovery" },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(auxiliary, pmt_features_id_table);
|
||||||
|
|
||||||
|
static struct auxiliary_driver pmt_features_aux_driver = {
|
||||||
|
.id_table = pmt_features_id_table,
|
||||||
|
.remove = pmt_features_remove,
|
||||||
|
.probe = pmt_features_probe,
|
||||||
|
};
|
||||||
|
module_auxiliary_driver(pmt_features_aux_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
|
||||||
|
MODULE_DESCRIPTION("Intel PMT Discovery driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_IMPORT_NS("INTEL_PMT");
|
||||||
|
|
@ -0,0 +1,205 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Intel Corporation.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Author: "David E. Box" <david.e.box@linux.intel.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include <linux/intel_pmt_features.h>
|
||||||
|
|
||||||
|
const char * const pmt_feature_names[] = {
|
||||||
|
[FEATURE_PER_CORE_PERF_TELEM] = "per_core_performance_telemetry",
|
||||||
|
[FEATURE_PER_CORE_ENV_TELEM] = "per_core_environment_telemetry",
|
||||||
|
[FEATURE_PER_RMID_PERF_TELEM] = "per_rmid_perf_telemetry",
|
||||||
|
[FEATURE_ACCEL_TELEM] = "accelerator_telemetry",
|
||||||
|
[FEATURE_UNCORE_TELEM] = "uncore_telemetry",
|
||||||
|
[FEATURE_CRASH_LOG] = "crash_log",
|
||||||
|
[FEATURE_PETE_LOG] = "pete_log",
|
||||||
|
[FEATURE_TPMI_CTRL] = "tpmi_control",
|
||||||
|
[FEATURE_TRACING] = "tracing",
|
||||||
|
[FEATURE_PER_RMID_ENERGY_TELEM] = "per_rmid_energy_telemetry",
|
||||||
|
};
|
||||||
|
EXPORT_SYMBOL_NS_GPL(pmt_feature_names, "INTEL_PMT_DISCOVERY");
|
||||||
|
|
||||||
|
enum feature_layout feature_layout[] = {
|
||||||
|
[FEATURE_PER_CORE_PERF_TELEM] = LAYOUT_WATCHER,
|
||||||
|
[FEATURE_PER_CORE_ENV_TELEM] = LAYOUT_WATCHER,
|
||||||
|
[FEATURE_PER_RMID_PERF_TELEM] = LAYOUT_RMID,
|
||||||
|
[FEATURE_ACCEL_TELEM] = LAYOUT_WATCHER,
|
||||||
|
[FEATURE_UNCORE_TELEM] = LAYOUT_WATCHER,
|
||||||
|
[FEATURE_CRASH_LOG] = LAYOUT_COMMAND,
|
||||||
|
[FEATURE_PETE_LOG] = LAYOUT_COMMAND,
|
||||||
|
[FEATURE_TPMI_CTRL] = LAYOUT_CAPS_ONLY,
|
||||||
|
[FEATURE_TRACING] = LAYOUT_CAPS_ONLY,
|
||||||
|
[FEATURE_PER_RMID_ENERGY_TELEM] = LAYOUT_RMID,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap pmt_cap_common[] = {
|
||||||
|
{PMT_CAP_TELEM, "telemetry"},
|
||||||
|
{PMT_CAP_WATCHER, "watcher"},
|
||||||
|
{PMT_CAP_CRASHLOG, "crashlog"},
|
||||||
|
{PMT_CAP_STREAMING, "streaming"},
|
||||||
|
{PMT_CAP_THRESHOLD, "threshold"},
|
||||||
|
{PMT_CAP_WINDOW, "window"},
|
||||||
|
{PMT_CAP_CONFIG, "config"},
|
||||||
|
{PMT_CAP_TRACING, "tracing"},
|
||||||
|
{PMT_CAP_INBAND, "inband"},
|
||||||
|
{PMT_CAP_OOB, "oob"},
|
||||||
|
{PMT_CAP_SECURED_CHAN, "secure_chan"},
|
||||||
|
{PMT_CAP_PMT_SP, "pmt_sp"},
|
||||||
|
{PMT_CAP_PMT_SP_POLICY, "pmt_sp_policy"},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap pmt_cap_pcpt[] = {
|
||||||
|
{PMT_CAP_PCPT_CORE_PERF, "core_performance"},
|
||||||
|
{PMT_CAP_PCPT_CORE_C0_RES, "core_c0_residency"},
|
||||||
|
{PMT_CAP_PCPT_CORE_ACTIVITY, "core_activity"},
|
||||||
|
{PMT_CAP_PCPT_CACHE_PERF, "cache_performance"},
|
||||||
|
{PMT_CAP_PCPT_QUALITY_TELEM, "quality_telemetry"},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap *pmt_caps_pcpt[] = {
|
||||||
|
pmt_cap_common,
|
||||||
|
pmt_cap_pcpt,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap pmt_cap_pcet[] = {
|
||||||
|
{PMT_CAP_PCET_WORKPOINT_HIST, "workpoint_histogram"},
|
||||||
|
{PMT_CAP_PCET_CORE_CURR_TEMP, "core_current_temp"},
|
||||||
|
{PMT_CAP_PCET_CORE_INST_RES, "core_inst_residency"},
|
||||||
|
{PMT_CAP_PCET_QUALITY_TELEM, "quality_telemetry"},
|
||||||
|
{PMT_CAP_PCET_CORE_CDYN_LVL, "core_cdyn_level"},
|
||||||
|
{PMT_CAP_PCET_CORE_STRESS_LVL, "core_stress_level"},
|
||||||
|
{PMT_CAP_PCET_CORE_DAS, "core_digital_aging_sensor"},
|
||||||
|
{PMT_CAP_PCET_FIVR_HEALTH, "fivr_health"},
|
||||||
|
{PMT_CAP_PCET_ENERGY, "energy"},
|
||||||
|
{PMT_CAP_PCET_PEM_STATUS, "pem_status"},
|
||||||
|
{PMT_CAP_PCET_CORE_C_STATE, "core_c_state"},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap *pmt_caps_pcet[] = {
|
||||||
|
pmt_cap_common,
|
||||||
|
pmt_cap_pcet,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap pmt_cap_rmid_perf[] = {
|
||||||
|
{PMT_CAP_RMID_CORES_PERF, "core_performance"},
|
||||||
|
{PMT_CAP_RMID_CACHE_PERF, "cache_performance"},
|
||||||
|
{PMT_CAP_RMID_PERF_QUAL, "performance_quality"},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap *pmt_caps_rmid_perf[] = {
|
||||||
|
pmt_cap_common,
|
||||||
|
pmt_cap_rmid_perf,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap pmt_cap_accel[] = {
|
||||||
|
{PMT_CAP_ACCEL_CPM_TELEM, "content_processing_module"},
|
||||||
|
{PMT_CAP_ACCEL_TIP_TELEM, "content_turbo_ip"},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap *pmt_caps_accel[] = {
|
||||||
|
pmt_cap_common,
|
||||||
|
pmt_cap_accel,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap pmt_cap_uncore[] = {
|
||||||
|
{PMT_CAP_UNCORE_IO_CA_TELEM, "io_ca"},
|
||||||
|
{PMT_CAP_UNCORE_RMID_TELEM, "rmid"},
|
||||||
|
{PMT_CAP_UNCORE_D2D_ULA_TELEM, "d2d_ula"},
|
||||||
|
{PMT_CAP_UNCORE_PKGC_TELEM, "package_c"},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap *pmt_caps_uncore[] = {
|
||||||
|
pmt_cap_common,
|
||||||
|
pmt_cap_uncore,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap pmt_cap_crashlog[] = {
|
||||||
|
{PMT_CAP_CRASHLOG_MAN_TRIG, "manual_trigger"},
|
||||||
|
{PMT_CAP_CRASHLOG_CORE, "core"},
|
||||||
|
{PMT_CAP_CRASHLOG_UNCORE, "uncore"},
|
||||||
|
{PMT_CAP_CRASHLOG_TOR, "tor"},
|
||||||
|
{PMT_CAP_CRASHLOG_S3M, "s3m"},
|
||||||
|
{PMT_CAP_CRASHLOG_PERSISTENCY, "persistency"},
|
||||||
|
{PMT_CAP_CRASHLOG_CLIP_GPIO, "crashlog_in_progress"},
|
||||||
|
{PMT_CAP_CRASHLOG_PRE_RESET, "pre_reset_extraction"},
|
||||||
|
{PMT_CAP_CRASHLOG_POST_RESET, "post_reset_extraction"},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap *pmt_caps_crashlog[] = {
|
||||||
|
pmt_cap_common,
|
||||||
|
pmt_cap_crashlog,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap pmt_cap_pete[] = {
|
||||||
|
{PMT_CAP_PETE_MAN_TRIG, "manual_trigger"},
|
||||||
|
{PMT_CAP_PETE_ENCRYPTION, "encryption"},
|
||||||
|
{PMT_CAP_PETE_PERSISTENCY, "persistency"},
|
||||||
|
{PMT_CAP_PETE_REQ_TOKENS, "required_tokens"},
|
||||||
|
{PMT_CAP_PETE_PROD_ENABLED, "production_enabled"},
|
||||||
|
{PMT_CAP_PETE_DEBUG_ENABLED, "debug_enabled"},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap *pmt_caps_pete[] = {
|
||||||
|
pmt_cap_common,
|
||||||
|
pmt_cap_pete,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap pmt_cap_tpmi[] = {
|
||||||
|
{PMT_CAP_TPMI_MAILBOX, "mailbox"},
|
||||||
|
{PMT_CAP_TPMI_LOCK, "bios_lock"},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap *pmt_caps_tpmi[] = {
|
||||||
|
pmt_cap_common,
|
||||||
|
pmt_cap_tpmi,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap pmt_cap_tracing[] = {
|
||||||
|
{PMT_CAP_TRACE_SRAR, "srar_errors"},
|
||||||
|
{PMT_CAP_TRACE_CORRECTABLE, "correctable_errors"},
|
||||||
|
{PMT_CAP_TRACE_MCTP, "mctp"},
|
||||||
|
{PMT_CAP_TRACE_MRT, "memory_resiliency"},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap *pmt_caps_tracing[] = {
|
||||||
|
pmt_cap_common,
|
||||||
|
pmt_cap_tracing,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap pmt_cap_rmid_energy[] = {
|
||||||
|
{PMT_CAP_RMID_ENERGY, "energy"},
|
||||||
|
{PMT_CAP_RMID_ACTIVITY, "activity"},
|
||||||
|
{PMT_CAP_RMID_ENERGY_QUAL, "energy_quality"},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap *pmt_caps_rmid_energy[] = {
|
||||||
|
pmt_cap_common,
|
||||||
|
pmt_cap_rmid_energy,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
@ -9,13 +9,21 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/auxiliary_bus.h>
|
#include <linux/auxiliary_bus.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/cleanup.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/intel_pmt_features.h>
|
||||||
#include <linux/intel_vsec.h>
|
#include <linux/intel_vsec.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/kref.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/overflow.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/types.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/overflow.h>
|
#include <linux/xarray.h>
|
||||||
|
|
||||||
#include "class.h"
|
#include "class.h"
|
||||||
|
|
||||||
|
|
@ -206,6 +214,87 @@ unlock:
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_NS_GPL(pmt_telem_get_endpoint_info, "INTEL_PMT_TELEMETRY");
|
EXPORT_SYMBOL_NS_GPL(pmt_telem_get_endpoint_info, "INTEL_PMT_TELEMETRY");
|
||||||
|
|
||||||
|
static int pmt_copy_region(struct telemetry_region *region,
|
||||||
|
struct intel_pmt_entry *entry)
|
||||||
|
{
|
||||||
|
|
||||||
|
struct oobmsm_plat_info *plat_info;
|
||||||
|
|
||||||
|
plat_info = intel_vsec_get_mapping(entry->ep->pcidev);
|
||||||
|
if (IS_ERR(plat_info))
|
||||||
|
return PTR_ERR(plat_info);
|
||||||
|
|
||||||
|
region->plat_info = *plat_info;
|
||||||
|
region->guid = entry->guid;
|
||||||
|
region->addr = entry->ep->base;
|
||||||
|
region->size = entry->size;
|
||||||
|
region->num_rmids = entry->num_rmids;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pmt_feature_group_release(struct kref *kref)
|
||||||
|
{
|
||||||
|
struct pmt_feature_group *feature_group;
|
||||||
|
|
||||||
|
feature_group = container_of(kref, struct pmt_feature_group, kref);
|
||||||
|
kfree(feature_group);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pmt_feature_group *intel_pmt_get_regions_by_feature(enum pmt_feature_id id)
|
||||||
|
{
|
||||||
|
struct pmt_feature_group *feature_group __free(kfree) = NULL;
|
||||||
|
struct telemetry_region *region;
|
||||||
|
struct intel_pmt_entry *entry;
|
||||||
|
unsigned long idx;
|
||||||
|
int count = 0;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
if (!pmt_feature_id_is_valid(id))
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
guard(mutex)(&ep_lock);
|
||||||
|
xa_for_each(&telem_array, idx, entry) {
|
||||||
|
if (entry->feature_flags & BIT(id))
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!count)
|
||||||
|
return ERR_PTR(-ENOENT);
|
||||||
|
|
||||||
|
size = struct_size(feature_group, regions, count);
|
||||||
|
feature_group = kzalloc(size, GFP_KERNEL);
|
||||||
|
if (!feature_group)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
feature_group->count = count;
|
||||||
|
|
||||||
|
region = feature_group->regions;
|
||||||
|
xa_for_each(&telem_array, idx, entry) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!(entry->feature_flags & BIT(id)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = pmt_copy_region(region, entry);
|
||||||
|
if (ret)
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
|
||||||
|
region++;
|
||||||
|
}
|
||||||
|
|
||||||
|
kref_init(&feature_group->kref);
|
||||||
|
|
||||||
|
return no_free_ptr(feature_group);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(intel_pmt_get_regions_by_feature);
|
||||||
|
|
||||||
|
void intel_pmt_put_feature_group(struct pmt_feature_group *feature_group)
|
||||||
|
{
|
||||||
|
kref_put(&feature_group->kref, pmt_feature_group_release);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(intel_pmt_put_feature_group);
|
||||||
|
|
||||||
int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count)
|
int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count)
|
||||||
{
|
{
|
||||||
u32 offset, size;
|
u32 offset, size;
|
||||||
|
|
@ -311,6 +400,8 @@ static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxilia
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
priv->num_entries++;
|
priv->num_entries++;
|
||||||
|
|
||||||
|
intel_pmt_get_features(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -348,3 +439,4 @@ MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
|
||||||
MODULE_DESCRIPTION("Intel PMT Telemetry driver");
|
MODULE_DESCRIPTION("Intel PMT Telemetry driver");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
MODULE_IMPORT_NS("INTEL_PMT");
|
MODULE_IMPORT_NS("INTEL_PMT");
|
||||||
|
MODULE_IMPORT_NS("INTEL_VSEC");
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
#include <linux/auxiliary_bus.h>
|
#include <linux/auxiliary_bus.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/intel_tpmi.h>
|
#include <linux/intel_tpmi.h>
|
||||||
|
#include <linux/intel_vsec.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
|
@ -1546,7 +1547,7 @@ int tpmi_sst_dev_add(struct auxiliary_device *auxdev)
|
||||||
{
|
{
|
||||||
struct tpmi_per_power_domain_info *pd_info;
|
struct tpmi_per_power_domain_info *pd_info;
|
||||||
bool read_blocked = 0, write_blocked = 0;
|
bool read_blocked = 0, write_blocked = 0;
|
||||||
struct intel_tpmi_plat_info *plat_info;
|
struct oobmsm_plat_info *plat_info;
|
||||||
struct device *dev = &auxdev->dev;
|
struct device *dev = &auxdev->dev;
|
||||||
struct tpmi_sst_struct *tpmi_sst;
|
struct tpmi_sst_struct *tpmi_sst;
|
||||||
u8 i, num_resources, io_die_cnt;
|
u8 i, num_resources, io_die_cnt;
|
||||||
|
|
@ -1698,7 +1699,7 @@ EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_add, "INTEL_TPMI_SST");
|
||||||
void tpmi_sst_dev_remove(struct auxiliary_device *auxdev)
|
void tpmi_sst_dev_remove(struct auxiliary_device *auxdev)
|
||||||
{
|
{
|
||||||
struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev);
|
struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev);
|
||||||
struct intel_tpmi_plat_info *plat_info;
|
struct oobmsm_plat_info *plat_info;
|
||||||
|
|
||||||
plat_info = tpmi_get_platform_data(auxdev);
|
plat_info = tpmi_get_platform_data(auxdev);
|
||||||
if (!plat_info)
|
if (!plat_info)
|
||||||
|
|
@ -1720,7 +1721,7 @@ void tpmi_sst_dev_suspend(struct auxiliary_device *auxdev)
|
||||||
{
|
{
|
||||||
struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev);
|
struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev);
|
||||||
struct tpmi_per_power_domain_info *power_domain_info;
|
struct tpmi_per_power_domain_info *power_domain_info;
|
||||||
struct intel_tpmi_plat_info *plat_info;
|
struct oobmsm_plat_info *plat_info;
|
||||||
void __iomem *cp_base;
|
void __iomem *cp_base;
|
||||||
|
|
||||||
plat_info = tpmi_get_platform_data(auxdev);
|
plat_info = tpmi_get_platform_data(auxdev);
|
||||||
|
|
@ -1748,7 +1749,7 @@ void tpmi_sst_dev_resume(struct auxiliary_device *auxdev)
|
||||||
{
|
{
|
||||||
struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev);
|
struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev);
|
||||||
struct tpmi_per_power_domain_info *power_domain_info;
|
struct tpmi_per_power_domain_info *power_domain_info;
|
||||||
struct intel_tpmi_plat_info *plat_info;
|
struct oobmsm_plat_info *plat_info;
|
||||||
void __iomem *cp_base;
|
void __iomem *cp_base;
|
||||||
|
|
||||||
plat_info = tpmi_get_platform_data(auxdev);
|
plat_info = tpmi_get_platform_data(auxdev);
|
||||||
|
|
|
||||||
|
|
@ -21,33 +21,6 @@ struct telemetry_core_config {
|
||||||
|
|
||||||
static struct telemetry_core_config telm_core_conf;
|
static struct telemetry_core_config telm_core_conf;
|
||||||
|
|
||||||
static int telemetry_def_update_events(struct telemetry_evtconfig pss_evtconfig,
|
|
||||||
struct telemetry_evtconfig ioss_evtconfig)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int telemetry_def_set_sampling_period(u8 pss_period, u8 ioss_period)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int telemetry_def_get_sampling_period(u8 *pss_min_period,
|
|
||||||
u8 *pss_max_period,
|
|
||||||
u8 *ioss_min_period,
|
|
||||||
u8 *ioss_max_period)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int telemetry_def_get_eventconfig(
|
|
||||||
struct telemetry_evtconfig *pss_evtconfig,
|
|
||||||
struct telemetry_evtconfig *ioss_evtconfig,
|
|
||||||
int pss_len, int ioss_len)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int telemetry_def_get_trace_verbosity(enum telemetry_unit telem_unit,
|
static int telemetry_def_get_trace_verbosity(enum telemetry_unit telem_unit,
|
||||||
u32 *verbosity)
|
u32 *verbosity)
|
||||||
{
|
{
|
||||||
|
|
@ -75,144 +48,13 @@ static int telemetry_def_read_eventlog(enum telemetry_unit telem_unit,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int telemetry_def_add_events(u8 num_pss_evts, u8 num_ioss_evts,
|
|
||||||
u32 *pss_evtmap, u32 *ioss_evtmap)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int telemetry_def_reset_events(void)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct telemetry_core_ops telm_defpltops = {
|
static const struct telemetry_core_ops telm_defpltops = {
|
||||||
.set_sampling_period = telemetry_def_set_sampling_period,
|
|
||||||
.get_sampling_period = telemetry_def_get_sampling_period,
|
|
||||||
.get_trace_verbosity = telemetry_def_get_trace_verbosity,
|
.get_trace_verbosity = telemetry_def_get_trace_verbosity,
|
||||||
.set_trace_verbosity = telemetry_def_set_trace_verbosity,
|
.set_trace_verbosity = telemetry_def_set_trace_verbosity,
|
||||||
.raw_read_eventlog = telemetry_def_raw_read_eventlog,
|
.raw_read_eventlog = telemetry_def_raw_read_eventlog,
|
||||||
.get_eventconfig = telemetry_def_get_eventconfig,
|
|
||||||
.read_eventlog = telemetry_def_read_eventlog,
|
.read_eventlog = telemetry_def_read_eventlog,
|
||||||
.update_events = telemetry_def_update_events,
|
|
||||||
.reset_events = telemetry_def_reset_events,
|
|
||||||
.add_events = telemetry_def_add_events,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* telemetry_update_events() - Update telemetry Configuration
|
|
||||||
* @pss_evtconfig: PSS related config. No change if num_evts = 0.
|
|
||||||
* @ioss_evtconfig: IOSS related config. No change if num_evts = 0.
|
|
||||||
*
|
|
||||||
* This API updates the IOSS & PSS Telemetry configuration. Old config
|
|
||||||
* is overwritten. Call telemetry_reset_events when logging is over
|
|
||||||
* All sample period values should be in the form of:
|
|
||||||
* bits[6:3] -> value; bits [0:2]-> Exponent; Period = (Value *16^Exponent)
|
|
||||||
*
|
|
||||||
* Return: 0 success, < 0 for failure
|
|
||||||
*/
|
|
||||||
int telemetry_update_events(struct telemetry_evtconfig pss_evtconfig,
|
|
||||||
struct telemetry_evtconfig ioss_evtconfig)
|
|
||||||
{
|
|
||||||
return telm_core_conf.telem_ops->update_events(pss_evtconfig,
|
|
||||||
ioss_evtconfig);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(telemetry_update_events);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* telemetry_set_sampling_period() - Sets the IOSS & PSS sampling period
|
|
||||||
* @pss_period: placeholder for PSS Period to be set.
|
|
||||||
* Set to 0 if not required to be updated
|
|
||||||
* @ioss_period: placeholder for IOSS Period to be set
|
|
||||||
* Set to 0 if not required to be updated
|
|
||||||
*
|
|
||||||
* All values should be in the form of:
|
|
||||||
* bits[6:3] -> value; bits [0:2]-> Exponent; Period = (Value *16^Exponent)
|
|
||||||
*
|
|
||||||
* Return: 0 success, < 0 for failure
|
|
||||||
*/
|
|
||||||
int telemetry_set_sampling_period(u8 pss_period, u8 ioss_period)
|
|
||||||
{
|
|
||||||
return telm_core_conf.telem_ops->set_sampling_period(pss_period,
|
|
||||||
ioss_period);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(telemetry_set_sampling_period);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* telemetry_get_sampling_period() - Get IOSS & PSS min & max sampling period
|
|
||||||
* @pss_min_period: placeholder for PSS Min Period supported
|
|
||||||
* @pss_max_period: placeholder for PSS Max Period supported
|
|
||||||
* @ioss_min_period: placeholder for IOSS Min Period supported
|
|
||||||
* @ioss_max_period: placeholder for IOSS Max Period supported
|
|
||||||
*
|
|
||||||
* All values should be in the form of:
|
|
||||||
* bits[6:3] -> value; bits [0:2]-> Exponent; Period = (Value *16^Exponent)
|
|
||||||
*
|
|
||||||
* Return: 0 success, < 0 for failure
|
|
||||||
*/
|
|
||||||
int telemetry_get_sampling_period(u8 *pss_min_period, u8 *pss_max_period,
|
|
||||||
u8 *ioss_min_period, u8 *ioss_max_period)
|
|
||||||
{
|
|
||||||
return telm_core_conf.telem_ops->get_sampling_period(pss_min_period,
|
|
||||||
pss_max_period,
|
|
||||||
ioss_min_period,
|
|
||||||
ioss_max_period);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(telemetry_get_sampling_period);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* telemetry_reset_events() - Restore the IOSS & PSS configuration to default
|
|
||||||
*
|
|
||||||
* Return: 0 success, < 0 for failure
|
|
||||||
*/
|
|
||||||
int telemetry_reset_events(void)
|
|
||||||
{
|
|
||||||
return telm_core_conf.telem_ops->reset_events();
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(telemetry_reset_events);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* telemetry_get_eventconfig() - Returns the pss and ioss events enabled
|
|
||||||
* @pss_evtconfig: Pointer to PSS related configuration.
|
|
||||||
* @ioss_evtconfig: Pointer to IOSS related configuration.
|
|
||||||
* @pss_len: Number of u32 elements allocated for pss_evtconfig array
|
|
||||||
* @ioss_len: Number of u32 elements allocated for ioss_evtconfig array
|
|
||||||
*
|
|
||||||
* Return: 0 success, < 0 for failure
|
|
||||||
*/
|
|
||||||
int telemetry_get_eventconfig(struct telemetry_evtconfig *pss_evtconfig,
|
|
||||||
struct telemetry_evtconfig *ioss_evtconfig,
|
|
||||||
int pss_len, int ioss_len)
|
|
||||||
{
|
|
||||||
return telm_core_conf.telem_ops->get_eventconfig(pss_evtconfig,
|
|
||||||
ioss_evtconfig,
|
|
||||||
pss_len, ioss_len);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(telemetry_get_eventconfig);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* telemetry_add_events() - Add IOSS & PSS configuration to existing settings.
|
|
||||||
* @num_pss_evts: Number of PSS Events (<29) in pss_evtmap. Can be 0.
|
|
||||||
* @num_ioss_evts: Number of IOSS Events (<29) in ioss_evtmap. Can be 0.
|
|
||||||
* @pss_evtmap: Array of PSS Event-IDs to Enable
|
|
||||||
* @ioss_evtmap: Array of PSS Event-IDs to Enable
|
|
||||||
*
|
|
||||||
* Events are appended to Old Configuration. In case of total events > 28, it
|
|
||||||
* returns error. Call telemetry_reset_events to reset after eventlog done
|
|
||||||
*
|
|
||||||
* Return: 0 success, < 0 for failure
|
|
||||||
*/
|
|
||||||
int telemetry_add_events(u8 num_pss_evts, u8 num_ioss_evts,
|
|
||||||
u32 *pss_evtmap, u32 *ioss_evtmap)
|
|
||||||
{
|
|
||||||
return telm_core_conf.telem_ops->add_events(num_pss_evts,
|
|
||||||
num_ioss_evts, pss_evtmap,
|
|
||||||
ioss_evtmap);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(telemetry_add_events);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* telemetry_read_events() - Fetches samples as specified by evtlog.telem_evt_id
|
* telemetry_read_events() - Fetches samples as specified by evtlog.telem_evt_id
|
||||||
* @telem_unit: Specify whether IOSS or PSS Read
|
* @telem_unit: Specify whether IOSS or PSS Read
|
||||||
|
|
@ -230,25 +72,6 @@ int telemetry_read_events(enum telemetry_unit telem_unit,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(telemetry_read_events);
|
EXPORT_SYMBOL_GPL(telemetry_read_events);
|
||||||
|
|
||||||
/**
|
|
||||||
* telemetry_raw_read_events() - Fetch samples specified by evtlog.telem_evt_id
|
|
||||||
* @telem_unit: Specify whether IOSS or PSS Read
|
|
||||||
* @evtlog: Array of telemetry_evtlog structs to fill data
|
|
||||||
* evtlog.telem_evt_id specifies the ids to read
|
|
||||||
* @len: Length of array of evtlog
|
|
||||||
*
|
|
||||||
* The caller must take care of locking in this case.
|
|
||||||
*
|
|
||||||
* Return: number of eventlogs read for success, < 0 for failure
|
|
||||||
*/
|
|
||||||
int telemetry_raw_read_events(enum telemetry_unit telem_unit,
|
|
||||||
struct telemetry_evtlog *evtlog, int len)
|
|
||||||
{
|
|
||||||
return telm_core_conf.telem_ops->raw_read_eventlog(telem_unit, evtlog,
|
|
||||||
len, 0);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(telemetry_raw_read_events);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* telemetry_read_eventlog() - Fetch the Telemetry log from PSS or IOSS
|
* telemetry_read_eventlog() - Fetch the Telemetry log from PSS or IOSS
|
||||||
* @telem_unit: Specify whether IOSS or PSS Read
|
* @telem_unit: Specify whether IOSS or PSS Read
|
||||||
|
|
|
||||||
|
|
@ -639,231 +639,6 @@ static int telemetry_setup(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int telemetry_plt_update_events(struct telemetry_evtconfig pss_evtconfig,
|
|
||||||
struct telemetry_evtconfig ioss_evtconfig)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if ((pss_evtconfig.num_evts > 0) &&
|
|
||||||
(TELEM_SAMPLE_PERIOD_INVALID(pss_evtconfig.period))) {
|
|
||||||
pr_err("PSS Sampling Period Out of Range\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ioss_evtconfig.num_evts > 0) &&
|
|
||||||
(TELEM_SAMPLE_PERIOD_INVALID(ioss_evtconfig.period))) {
|
|
||||||
pr_err("IOSS Sampling Period Out of Range\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig,
|
|
||||||
TELEM_UPDATE);
|
|
||||||
if (ret)
|
|
||||||
pr_err("TELEMETRY Config Failed\n");
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
|
|
||||||
{
|
|
||||||
u32 telem_ctrl = 0;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
mutex_lock(&(telm_conf->telem_lock));
|
|
||||||
if (ioss_period) {
|
|
||||||
struct intel_scu_ipc_dev *scu = telm_conf->scu;
|
|
||||||
|
|
||||||
if (TELEM_SAMPLE_PERIOD_INVALID(ioss_period)) {
|
|
||||||
pr_err("IOSS Sampling Period Out of Range\n");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get telemetry EVENT CTL */
|
|
||||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
|
||||||
IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
|
|
||||||
&telem_ctrl, sizeof(telem_ctrl));
|
|
||||||
if (ret) {
|
|
||||||
pr_err("IOSS TELEM_CTRL Read Failed\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disable Telemetry */
|
|
||||||
TELEM_DISABLE(telem_ctrl);
|
|
||||||
|
|
||||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
|
||||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
|
||||||
&telem_ctrl, sizeof(telem_ctrl),
|
|
||||||
NULL, 0);
|
|
||||||
if (ret) {
|
|
||||||
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enable Periodic Telemetry Events and enable SRAM trace */
|
|
||||||
TELEM_CLEAR_SAMPLE_PERIOD(telem_ctrl);
|
|
||||||
TELEM_ENABLE_SRAM_EVT_TRACE(telem_ctrl);
|
|
||||||
TELEM_ENABLE_PERIODIC(telem_ctrl);
|
|
||||||
telem_ctrl |= ioss_period;
|
|
||||||
|
|
||||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
|
||||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
|
||||||
&telem_ctrl, sizeof(telem_ctrl),
|
|
||||||
NULL, 0);
|
|
||||||
if (ret) {
|
|
||||||
pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
telm_conf->ioss_config.curr_period = ioss_period;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pss_period) {
|
|
||||||
if (TELEM_SAMPLE_PERIOD_INVALID(pss_period)) {
|
|
||||||
pr_err("PSS Sampling Period Out of Range\n");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get telemetry EVENT CTL */
|
|
||||||
ret = intel_punit_ipc_command(
|
|
||||||
IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
|
|
||||||
0, 0, NULL, &telem_ctrl);
|
|
||||||
if (ret) {
|
|
||||||
pr_err("PSS TELEM_CTRL Read Failed\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disable Telemetry */
|
|
||||||
TELEM_DISABLE(telem_ctrl);
|
|
||||||
ret = intel_punit_ipc_command(
|
|
||||||
IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
|
|
||||||
0, 0, &telem_ctrl, NULL);
|
|
||||||
if (ret) {
|
|
||||||
pr_err("PSS TELEM_CTRL Event Disable Write Failed\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enable Periodic Telemetry Events and enable SRAM trace */
|
|
||||||
TELEM_CLEAR_SAMPLE_PERIOD(telem_ctrl);
|
|
||||||
TELEM_ENABLE_SRAM_EVT_TRACE(telem_ctrl);
|
|
||||||
TELEM_ENABLE_PERIODIC(telem_ctrl);
|
|
||||||
telem_ctrl |= pss_period;
|
|
||||||
|
|
||||||
ret = intel_punit_ipc_command(
|
|
||||||
IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
|
|
||||||
0, 0, &telem_ctrl, NULL);
|
|
||||||
if (ret) {
|
|
||||||
pr_err("PSS TELEM_CTRL Event Enable Write Failed\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
telm_conf->pss_config.curr_period = pss_period;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
mutex_unlock(&(telm_conf->telem_lock));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int telemetry_plt_get_sampling_period(u8 *pss_min_period,
|
|
||||||
u8 *pss_max_period,
|
|
||||||
u8 *ioss_min_period,
|
|
||||||
u8 *ioss_max_period)
|
|
||||||
{
|
|
||||||
*pss_min_period = telm_conf->pss_config.min_period;
|
|
||||||
*pss_max_period = telm_conf->pss_config.max_period;
|
|
||||||
*ioss_min_period = telm_conf->ioss_config.min_period;
|
|
||||||
*ioss_max_period = telm_conf->ioss_config.max_period;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int telemetry_plt_reset_events(void)
|
|
||||||
{
|
|
||||||
struct telemetry_evtconfig pss_evtconfig, ioss_evtconfig;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
pss_evtconfig.evtmap = NULL;
|
|
||||||
pss_evtconfig.num_evts = TELEM_MAX_OS_ALLOCATED_EVENTS;
|
|
||||||
pss_evtconfig.period = TELEM_SAMPLING_DEFAULT_PERIOD;
|
|
||||||
|
|
||||||
ioss_evtconfig.evtmap = NULL;
|
|
||||||
ioss_evtconfig.num_evts = TELEM_MAX_OS_ALLOCATED_EVENTS;
|
|
||||||
ioss_evtconfig.period = TELEM_SAMPLING_DEFAULT_PERIOD;
|
|
||||||
|
|
||||||
ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig,
|
|
||||||
TELEM_RESET);
|
|
||||||
if (ret)
|
|
||||||
pr_err("TELEMETRY Reset Failed\n");
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int telemetry_plt_get_eventconfig(struct telemetry_evtconfig *pss_config,
|
|
||||||
struct telemetry_evtconfig *ioss_config,
|
|
||||||
int pss_len, int ioss_len)
|
|
||||||
{
|
|
||||||
u32 *pss_evtmap, *ioss_evtmap;
|
|
||||||
u32 index;
|
|
||||||
|
|
||||||
pss_evtmap = pss_config->evtmap;
|
|
||||||
ioss_evtmap = ioss_config->evtmap;
|
|
||||||
|
|
||||||
mutex_lock(&(telm_conf->telem_lock));
|
|
||||||
pss_config->num_evts = telm_conf->pss_config.ssram_evts_used;
|
|
||||||
ioss_config->num_evts = telm_conf->ioss_config.ssram_evts_used;
|
|
||||||
|
|
||||||
pss_config->period = telm_conf->pss_config.curr_period;
|
|
||||||
ioss_config->period = telm_conf->ioss_config.curr_period;
|
|
||||||
|
|
||||||
if ((pss_len < telm_conf->pss_config.ssram_evts_used) ||
|
|
||||||
(ioss_len < telm_conf->ioss_config.ssram_evts_used)) {
|
|
||||||
mutex_unlock(&(telm_conf->telem_lock));
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (index = 0; index < telm_conf->pss_config.ssram_evts_used;
|
|
||||||
index++) {
|
|
||||||
pss_evtmap[index] =
|
|
||||||
telm_conf->pss_config.telem_evts[index].evt_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (index = 0; index < telm_conf->ioss_config.ssram_evts_used;
|
|
||||||
index++) {
|
|
||||||
ioss_evtmap[index] =
|
|
||||||
telm_conf->ioss_config.telem_evts[index].evt_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_unlock(&(telm_conf->telem_lock));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int telemetry_plt_add_events(u8 num_pss_evts, u8 num_ioss_evts,
|
|
||||||
u32 *pss_evtmap, u32 *ioss_evtmap)
|
|
||||||
{
|
|
||||||
struct telemetry_evtconfig pss_evtconfig, ioss_evtconfig;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
pss_evtconfig.evtmap = pss_evtmap;
|
|
||||||
pss_evtconfig.num_evts = num_pss_evts;
|
|
||||||
pss_evtconfig.period = telm_conf->pss_config.curr_period;
|
|
||||||
|
|
||||||
ioss_evtconfig.evtmap = ioss_evtmap;
|
|
||||||
ioss_evtconfig.num_evts = num_ioss_evts;
|
|
||||||
ioss_evtconfig.period = telm_conf->ioss_config.curr_period;
|
|
||||||
|
|
||||||
ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig,
|
|
||||||
TELEM_ADD);
|
|
||||||
if (ret)
|
|
||||||
pr_err("TELEMETRY ADD Failed\n");
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int telem_evtlog_read(enum telemetry_unit telem_unit,
|
static int telem_evtlog_read(enum telemetry_unit telem_unit,
|
||||||
struct telem_ssram_region *ssram_region, u8 len)
|
struct telem_ssram_region *ssram_region, u8 len)
|
||||||
{
|
{
|
||||||
|
|
@ -1093,14 +868,8 @@ out:
|
||||||
static const struct telemetry_core_ops telm_pltops = {
|
static const struct telemetry_core_ops telm_pltops = {
|
||||||
.get_trace_verbosity = telemetry_plt_get_trace_verbosity,
|
.get_trace_verbosity = telemetry_plt_get_trace_verbosity,
|
||||||
.set_trace_verbosity = telemetry_plt_set_trace_verbosity,
|
.set_trace_verbosity = telemetry_plt_set_trace_verbosity,
|
||||||
.set_sampling_period = telemetry_plt_set_sampling_period,
|
|
||||||
.get_sampling_period = telemetry_plt_get_sampling_period,
|
|
||||||
.raw_read_eventlog = telemetry_plt_raw_read_eventlog,
|
.raw_read_eventlog = telemetry_plt_raw_read_eventlog,
|
||||||
.get_eventconfig = telemetry_plt_get_eventconfig,
|
|
||||||
.update_events = telemetry_plt_update_events,
|
|
||||||
.read_eventlog = telemetry_plt_read_eventlog,
|
.read_eventlog = telemetry_plt_read_eventlog,
|
||||||
.reset_events = telemetry_plt_reset_events,
|
|
||||||
.add_events = telemetry_plt_add_events,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int telemetry_pltdrv_probe(struct platform_device *pdev)
|
static int telemetry_pltdrv_probe(struct platform_device *pdev)
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,10 @@
|
||||||
#include <linux/auxiliary_bus.h>
|
#include <linux/auxiliary_bus.h>
|
||||||
#include <linux/bitfield.h>
|
#include <linux/bitfield.h>
|
||||||
#include <linux/bits.h>
|
#include <linux/bits.h>
|
||||||
|
#include <linux/intel_tpmi.h>
|
||||||
|
#include <linux/intel_vsec.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/intel_tpmi.h>
|
|
||||||
|
|
||||||
#include "../tpmi_power_domains.h"
|
#include "../tpmi_power_domains.h"
|
||||||
#include "uncore-frequency-common.h"
|
#include "uncore-frequency-common.h"
|
||||||
|
|
@ -448,7 +449,7 @@ static void remove_cluster_entries(struct tpmi_uncore_struct *tpmi_uncore)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_cdie_id(int domain_id, struct tpmi_uncore_cluster_info *cluster_info,
|
static void set_cdie_id(int domain_id, struct tpmi_uncore_cluster_info *cluster_info,
|
||||||
struct intel_tpmi_plat_info *plat_info)
|
struct oobmsm_plat_info *plat_info)
|
||||||
{
|
{
|
||||||
|
|
||||||
cluster_info->cdie_id = domain_id;
|
cluster_info->cdie_id = domain_id;
|
||||||
|
|
@ -465,7 +466,7 @@ static void set_cdie_id(int domain_id, struct tpmi_uncore_cluster_info *cluster_
|
||||||
static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
|
static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
|
||||||
{
|
{
|
||||||
bool read_blocked = 0, write_blocked = 0;
|
bool read_blocked = 0, write_blocked = 0;
|
||||||
struct intel_tpmi_plat_info *plat_info;
|
struct oobmsm_plat_info *plat_info;
|
||||||
struct tpmi_uncore_struct *tpmi_uncore;
|
struct tpmi_uncore_struct *tpmi_uncore;
|
||||||
bool uncore_sysfs_added = false;
|
bool uncore_sysfs_added = false;
|
||||||
int ret, i, pkg = 0;
|
int ret, i, pkg = 0;
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,12 @@
|
||||||
|
|
||||||
#include <linux/auxiliary_bus.h>
|
#include <linux/auxiliary_bus.h>
|
||||||
#include <linux/bits.h>
|
#include <linux/bits.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/bug.h>
|
||||||
#include <linux/cleanup.h>
|
#include <linux/cleanup.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
|
#include <linux/log2.h>
|
||||||
#include <linux/intel_vsec.h>
|
#include <linux/intel_vsec.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
|
@ -32,6 +35,20 @@ static DEFINE_IDA(intel_vsec_ida);
|
||||||
static DEFINE_IDA(intel_vsec_sdsi_ida);
|
static DEFINE_IDA(intel_vsec_sdsi_ida);
|
||||||
static DEFINE_XARRAY_ALLOC(auxdev_array);
|
static DEFINE_XARRAY_ALLOC(auxdev_array);
|
||||||
|
|
||||||
|
enum vsec_device_state {
|
||||||
|
STATE_NOT_FOUND,
|
||||||
|
STATE_REGISTERED,
|
||||||
|
STATE_SKIP,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vsec_priv {
|
||||||
|
struct intel_vsec_platform_info *info;
|
||||||
|
struct device *suppliers[VSEC_FEATURE_COUNT];
|
||||||
|
struct oobmsm_plat_info plat_info;
|
||||||
|
enum vsec_device_state state[VSEC_FEATURE_COUNT];
|
||||||
|
unsigned long found_caps;
|
||||||
|
};
|
||||||
|
|
||||||
static const char *intel_vsec_name(enum intel_vsec_id id)
|
static const char *intel_vsec_name(enum intel_vsec_id id)
|
||||||
{
|
{
|
||||||
switch (id) {
|
switch (id) {
|
||||||
|
|
@ -50,6 +67,9 @@ static const char *intel_vsec_name(enum intel_vsec_id id)
|
||||||
case VSEC_ID_TPMI:
|
case VSEC_ID_TPMI:
|
||||||
return "tpmi";
|
return "tpmi";
|
||||||
|
|
||||||
|
case VSEC_ID_DISCOVERY:
|
||||||
|
return "discovery";
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -68,6 +88,8 @@ static bool intel_vsec_supported(u16 id, unsigned long caps)
|
||||||
return !!(caps & VSEC_CAP_SDSI);
|
return !!(caps & VSEC_CAP_SDSI);
|
||||||
case VSEC_ID_TPMI:
|
case VSEC_ID_TPMI:
|
||||||
return !!(caps & VSEC_CAP_TPMI);
|
return !!(caps & VSEC_CAP_TPMI);
|
||||||
|
case VSEC_ID_DISCOVERY:
|
||||||
|
return !!(caps & VSEC_CAP_DISCOVERY);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -91,6 +113,97 @@ static void intel_vsec_dev_release(struct device *dev)
|
||||||
kfree(intel_vsec_dev);
|
kfree(intel_vsec_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct vsec_feature_dependency *
|
||||||
|
get_consumer_dependencies(struct vsec_priv *priv, int cap_id)
|
||||||
|
{
|
||||||
|
const struct vsec_feature_dependency *deps = priv->info->deps;
|
||||||
|
int consumer_id = priv->info->num_deps;
|
||||||
|
|
||||||
|
if (!deps)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
while (consumer_id--)
|
||||||
|
if (deps[consumer_id].feature == BIT(cap_id))
|
||||||
|
return &deps[consumer_id];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool vsec_driver_present(int cap_id)
|
||||||
|
{
|
||||||
|
unsigned long bit = BIT(cap_id);
|
||||||
|
|
||||||
|
switch (bit) {
|
||||||
|
case VSEC_CAP_TELEMETRY:
|
||||||
|
return IS_ENABLED(CONFIG_INTEL_PMT_TELEMETRY);
|
||||||
|
case VSEC_CAP_WATCHER:
|
||||||
|
return IS_ENABLED(CONFIG_INTEL_PMT_WATCHER);
|
||||||
|
case VSEC_CAP_CRASHLOG:
|
||||||
|
return IS_ENABLED(CONFIG_INTEL_PMT_CRASHLOG);
|
||||||
|
case VSEC_CAP_SDSI:
|
||||||
|
return IS_ENABLED(CONFIG_INTEL_SDSI);
|
||||||
|
case VSEC_CAP_TPMI:
|
||||||
|
return IS_ENABLED(CONFIG_INTEL_TPMI);
|
||||||
|
case VSEC_CAP_DISCOVERY:
|
||||||
|
return IS_ENABLED(CONFIG_INTEL_PMT_DISCOVERY);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Although pci_device_id table is available in the pdev, this prototype is
|
||||||
|
* necessary because the code using it can be called by an exported API that
|
||||||
|
* might pass a different pdev.
|
||||||
|
*/
|
||||||
|
static const struct pci_device_id intel_vsec_pci_ids[];
|
||||||
|
|
||||||
|
static int intel_vsec_link_devices(struct pci_dev *pdev, struct device *dev,
|
||||||
|
int consumer_id)
|
||||||
|
{
|
||||||
|
const struct vsec_feature_dependency *deps;
|
||||||
|
enum vsec_device_state *state;
|
||||||
|
struct device **suppliers;
|
||||||
|
struct vsec_priv *priv;
|
||||||
|
int supplier_id;
|
||||||
|
|
||||||
|
if (!consumer_id)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!pci_match_id(intel_vsec_pci_ids, pdev))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
priv = pci_get_drvdata(pdev);
|
||||||
|
state = priv->state;
|
||||||
|
suppliers = priv->suppliers;
|
||||||
|
|
||||||
|
priv->suppliers[consumer_id] = dev;
|
||||||
|
|
||||||
|
deps = get_consumer_dependencies(priv, consumer_id);
|
||||||
|
if (!deps)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for_each_set_bit(supplier_id, &deps->supplier_bitmap, VSEC_FEATURE_COUNT) {
|
||||||
|
struct device_link *link;
|
||||||
|
|
||||||
|
if (state[supplier_id] != STATE_REGISTERED ||
|
||||||
|
!vsec_driver_present(supplier_id))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!suppliers[supplier_id]) {
|
||||||
|
dev_err(dev, "Bad supplier list\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
link = device_link_add(dev, suppliers[supplier_id],
|
||||||
|
DL_FLAG_AUTOPROBE_CONSUMER);
|
||||||
|
if (!link)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent,
|
int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent,
|
||||||
struct intel_vsec_device *intel_vsec_dev,
|
struct intel_vsec_device *intel_vsec_dev,
|
||||||
const char *name)
|
const char *name)
|
||||||
|
|
@ -128,19 +241,37 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assign a name now to ensure that the device link doesn't contain
|
||||||
|
* a null string for the consumer name. This is a problem when a supplier
|
||||||
|
* supplies more than one consumer and can lead to a duplicate name error
|
||||||
|
* when the link is created in sysfs.
|
||||||
|
*/
|
||||||
|
ret = dev_set_name(&auxdev->dev, "%s.%s.%d", KBUILD_MODNAME, auxdev->name,
|
||||||
|
auxdev->id);
|
||||||
|
if (ret)
|
||||||
|
goto cleanup_aux;
|
||||||
|
|
||||||
|
ret = intel_vsec_link_devices(pdev, &auxdev->dev, intel_vsec_dev->cap_id);
|
||||||
|
if (ret)
|
||||||
|
goto cleanup_aux;
|
||||||
|
|
||||||
ret = auxiliary_device_add(auxdev);
|
ret = auxiliary_device_add(auxdev);
|
||||||
if (ret < 0) {
|
if (ret)
|
||||||
auxiliary_device_uninit(auxdev);
|
goto cleanup_aux;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return devm_add_action_or_reset(parent, intel_vsec_remove_aux,
|
return devm_add_action_or_reset(parent, intel_vsec_remove_aux,
|
||||||
auxdev);
|
auxdev);
|
||||||
|
|
||||||
|
cleanup_aux:
|
||||||
|
auxiliary_device_uninit(auxdev);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, "INTEL_VSEC");
|
EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, "INTEL_VSEC");
|
||||||
|
|
||||||
static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header,
|
static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header,
|
||||||
struct intel_vsec_platform_info *info)
|
struct intel_vsec_platform_info *info,
|
||||||
|
unsigned long cap_id)
|
||||||
{
|
{
|
||||||
struct intel_vsec_device __free(kfree) *intel_vsec_dev = NULL;
|
struct intel_vsec_device __free(kfree) *intel_vsec_dev = NULL;
|
||||||
struct resource __free(kfree) *res = NULL;
|
struct resource __free(kfree) *res = NULL;
|
||||||
|
|
@ -207,6 +338,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he
|
||||||
intel_vsec_dev->quirks = info->quirks;
|
intel_vsec_dev->quirks = info->quirks;
|
||||||
intel_vsec_dev->base_addr = info->base_addr;
|
intel_vsec_dev->base_addr = info->base_addr;
|
||||||
intel_vsec_dev->priv_data = info->priv_data;
|
intel_vsec_dev->priv_data = info->priv_data;
|
||||||
|
intel_vsec_dev->cap_id = cap_id;
|
||||||
|
|
||||||
if (header->id == VSEC_ID_SDSI)
|
if (header->id == VSEC_ID_SDSI)
|
||||||
intel_vsec_dev->ida = &intel_vsec_sdsi_ida;
|
intel_vsec_dev->ida = &intel_vsec_sdsi_ida;
|
||||||
|
|
@ -221,6 +353,109 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he
|
||||||
intel_vsec_name(header->id));
|
intel_vsec_name(header->id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool suppliers_ready(struct vsec_priv *priv,
|
||||||
|
const struct vsec_feature_dependency *consumer_deps,
|
||||||
|
int cap_id)
|
||||||
|
{
|
||||||
|
enum vsec_device_state *state = priv->state;
|
||||||
|
int supplier_id;
|
||||||
|
|
||||||
|
if (WARN_ON_ONCE(consumer_deps->feature != BIT(cap_id)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verify that all required suppliers have been found. Return false
|
||||||
|
* immediately if any are still missing.
|
||||||
|
*/
|
||||||
|
for_each_set_bit(supplier_id, &consumer_deps->supplier_bitmap, VSEC_FEATURE_COUNT) {
|
||||||
|
if (state[supplier_id] == STATE_SKIP)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (state[supplier_id] == STATE_NOT_FOUND)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All suppliers have been found and the consumer is ready to be
|
||||||
|
* registered.
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_cap_id(u32 header_id, unsigned long *cap_id)
|
||||||
|
{
|
||||||
|
switch (header_id) {
|
||||||
|
case VSEC_ID_TELEMETRY:
|
||||||
|
*cap_id = ilog2(VSEC_CAP_TELEMETRY);
|
||||||
|
break;
|
||||||
|
case VSEC_ID_WATCHER:
|
||||||
|
*cap_id = ilog2(VSEC_CAP_WATCHER);
|
||||||
|
break;
|
||||||
|
case VSEC_ID_CRASHLOG:
|
||||||
|
*cap_id = ilog2(VSEC_CAP_CRASHLOG);
|
||||||
|
break;
|
||||||
|
case VSEC_ID_SDSI:
|
||||||
|
*cap_id = ilog2(VSEC_CAP_SDSI);
|
||||||
|
break;
|
||||||
|
case VSEC_ID_TPMI:
|
||||||
|
*cap_id = ilog2(VSEC_CAP_TPMI);
|
||||||
|
break;
|
||||||
|
case VSEC_ID_DISCOVERY:
|
||||||
|
*cap_id = ilog2(VSEC_CAP_DISCOVERY);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intel_vsec_register_device(struct pci_dev *pdev,
|
||||||
|
struct intel_vsec_header *header,
|
||||||
|
struct intel_vsec_platform_info *info)
|
||||||
|
{
|
||||||
|
const struct vsec_feature_dependency *consumer_deps;
|
||||||
|
struct vsec_priv *priv;
|
||||||
|
unsigned long cap_id;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = get_cap_id(header->id, &cap_id);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only track dependencies for devices probed by the VSEC driver.
|
||||||
|
* For others using the exported APIs, add the device directly.
|
||||||
|
*/
|
||||||
|
if (!pci_match_id(intel_vsec_pci_ids, pdev))
|
||||||
|
return intel_vsec_add_dev(pdev, header, info, cap_id);
|
||||||
|
|
||||||
|
priv = pci_get_drvdata(pdev);
|
||||||
|
if (priv->state[cap_id] == STATE_REGISTERED ||
|
||||||
|
priv->state[cap_id] == STATE_SKIP)
|
||||||
|
return -EEXIST;
|
||||||
|
|
||||||
|
priv->found_caps |= BIT(cap_id);
|
||||||
|
|
||||||
|
if (!vsec_driver_present(cap_id)) {
|
||||||
|
priv->state[cap_id] = STATE_SKIP;
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
consumer_deps = get_consumer_dependencies(priv, cap_id);
|
||||||
|
if (!consumer_deps || suppliers_ready(priv, consumer_deps, cap_id)) {
|
||||||
|
ret = intel_vsec_add_dev(pdev, header, info, cap_id);
|
||||||
|
if (ret)
|
||||||
|
priv->state[cap_id] = STATE_SKIP;
|
||||||
|
else
|
||||||
|
priv->state[cap_id] = STATE_REGISTERED;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
static bool intel_vsec_walk_header(struct pci_dev *pdev,
|
static bool intel_vsec_walk_header(struct pci_dev *pdev,
|
||||||
struct intel_vsec_platform_info *info)
|
struct intel_vsec_platform_info *info)
|
||||||
{
|
{
|
||||||
|
|
@ -229,7 +464,7 @@ static bool intel_vsec_walk_header(struct pci_dev *pdev,
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
for ( ; *header; header++) {
|
for ( ; *header; header++) {
|
||||||
ret = intel_vsec_add_dev(pdev, *header, info);
|
ret = intel_vsec_register_device(pdev, *header, info);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
have_devices = true;
|
have_devices = true;
|
||||||
}
|
}
|
||||||
|
|
@ -277,7 +512,7 @@ static bool intel_vsec_walk_dvsec(struct pci_dev *pdev,
|
||||||
pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr);
|
pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr);
|
||||||
header.id = PCI_DVSEC_HEADER2_ID(hdr);
|
header.id = PCI_DVSEC_HEADER2_ID(hdr);
|
||||||
|
|
||||||
ret = intel_vsec_add_dev(pdev, &header, info);
|
ret = intel_vsec_register_device(pdev, &header, info);
|
||||||
if (ret)
|
if (ret)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -322,7 +557,7 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev,
|
||||||
header.tbir = INTEL_DVSEC_TABLE_BAR(table);
|
header.tbir = INTEL_DVSEC_TABLE_BAR(table);
|
||||||
header.offset = INTEL_DVSEC_TABLE_OFFSET(table);
|
header.offset = INTEL_DVSEC_TABLE_OFFSET(table);
|
||||||
|
|
||||||
ret = intel_vsec_add_dev(pdev, &header, info);
|
ret = intel_vsec_register_device(pdev, &header, info);
|
||||||
if (ret)
|
if (ret)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -345,11 +580,56 @@ int intel_vsec_register(struct pci_dev *pdev,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_NS_GPL(intel_vsec_register, "INTEL_VSEC");
|
EXPORT_SYMBOL_NS_GPL(intel_vsec_register, "INTEL_VSEC");
|
||||||
|
|
||||||
|
static bool intel_vsec_get_features(struct pci_dev *pdev,
|
||||||
|
struct intel_vsec_platform_info *info)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Both DVSEC and VSEC capabilities can exist on the same device,
|
||||||
|
* so both intel_vsec_walk_dvsec() and intel_vsec_walk_vsec() must be
|
||||||
|
* called independently. Additionally, intel_vsec_walk_header() is
|
||||||
|
* needed for devices that do not have VSEC/DVSEC but provide the
|
||||||
|
* information via device_data.
|
||||||
|
*/
|
||||||
|
if (intel_vsec_walk_dvsec(pdev, info))
|
||||||
|
found = true;
|
||||||
|
|
||||||
|
if (intel_vsec_walk_vsec(pdev, info))
|
||||||
|
found = true;
|
||||||
|
|
||||||
|
if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) &&
|
||||||
|
intel_vsec_walk_header(pdev, info))
|
||||||
|
found = true;
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void intel_vsec_skip_missing_dependencies(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
struct vsec_priv *priv = pci_get_drvdata(pdev);
|
||||||
|
const struct vsec_feature_dependency *deps = priv->info->deps;
|
||||||
|
int consumer_id = priv->info->num_deps;
|
||||||
|
|
||||||
|
while (consumer_id--) {
|
||||||
|
int supplier_id;
|
||||||
|
|
||||||
|
deps = &priv->info->deps[consumer_id];
|
||||||
|
|
||||||
|
for_each_set_bit(supplier_id, &deps->supplier_bitmap, VSEC_FEATURE_COUNT) {
|
||||||
|
if (!(BIT(supplier_id) & priv->found_caps))
|
||||||
|
priv->state[supplier_id] = STATE_SKIP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||||
{
|
{
|
||||||
struct intel_vsec_platform_info *info;
|
struct intel_vsec_platform_info *info;
|
||||||
bool have_devices = false;
|
struct vsec_priv *priv;
|
||||||
int ret;
|
int num_caps, ret;
|
||||||
|
int run_once = 0;
|
||||||
|
bool found_any = false;
|
||||||
|
|
||||||
ret = pcim_enable_device(pdev);
|
ret = pcim_enable_device(pdev);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
@ -360,22 +640,62 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id
|
||||||
if (!info)
|
if (!info)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (intel_vsec_walk_dvsec(pdev, info))
|
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||||
have_devices = true;
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
if (intel_vsec_walk_vsec(pdev, info))
|
priv->info = info;
|
||||||
have_devices = true;
|
pci_set_drvdata(pdev, priv);
|
||||||
|
|
||||||
if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) &&
|
num_caps = hweight_long(info->caps);
|
||||||
intel_vsec_walk_header(pdev, info))
|
while (num_caps--) {
|
||||||
have_devices = true;
|
found_any |= intel_vsec_get_features(pdev, info);
|
||||||
|
|
||||||
if (!have_devices)
|
if (priv->found_caps == info->caps)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!run_once) {
|
||||||
|
intel_vsec_skip_missing_dependencies(pdev);
|
||||||
|
run_once = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_any)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info,
|
||||||
|
struct intel_vsec_device *vsec_dev)
|
||||||
|
{
|
||||||
|
struct vsec_priv *priv;
|
||||||
|
|
||||||
|
priv = pci_get_drvdata(vsec_dev->pcidev);
|
||||||
|
if (!priv)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
priv->plat_info = *plat_info;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS_GPL(intel_vsec_set_mapping, "INTEL_VSEC");
|
||||||
|
|
||||||
|
struct oobmsm_plat_info *intel_vsec_get_mapping(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
struct vsec_priv *priv;
|
||||||
|
|
||||||
|
if (!pci_match_id(intel_vsec_pci_ids, pdev))
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
priv = pci_get_drvdata(pdev);
|
||||||
|
if (!priv)
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
return &priv->plat_info;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS_GPL(intel_vsec_get_mapping, "INTEL_VSEC");
|
||||||
|
|
||||||
/* DG1 info */
|
/* DG1 info */
|
||||||
static struct intel_vsec_header dg1_header = {
|
static struct intel_vsec_header dg1_header = {
|
||||||
.length = 0x10,
|
.length = 0x10,
|
||||||
|
|
@ -402,14 +722,26 @@ static const struct intel_vsec_platform_info mtl_info = {
|
||||||
.caps = VSEC_CAP_TELEMETRY,
|
.caps = VSEC_CAP_TELEMETRY,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct vsec_feature_dependency oobmsm_deps[] = {
|
||||||
|
{
|
||||||
|
.feature = VSEC_CAP_TELEMETRY,
|
||||||
|
.supplier_bitmap = VSEC_CAP_DISCOVERY | VSEC_CAP_TPMI,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/* OOBMSM info */
|
/* OOBMSM info */
|
||||||
static const struct intel_vsec_platform_info oobmsm_info = {
|
static const struct intel_vsec_platform_info oobmsm_info = {
|
||||||
.caps = VSEC_CAP_TELEMETRY | VSEC_CAP_SDSI | VSEC_CAP_TPMI,
|
.caps = VSEC_CAP_TELEMETRY | VSEC_CAP_SDSI | VSEC_CAP_TPMI |
|
||||||
|
VSEC_CAP_DISCOVERY,
|
||||||
|
.deps = oobmsm_deps,
|
||||||
|
.num_deps = ARRAY_SIZE(oobmsm_deps),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* DMR OOBMSM info */
|
/* DMR OOBMSM info */
|
||||||
static const struct intel_vsec_platform_info dmr_oobmsm_info = {
|
static const struct intel_vsec_platform_info dmr_oobmsm_info = {
|
||||||
.caps = VSEC_CAP_TELEMETRY | VSEC_CAP_TPMI,
|
.caps = VSEC_CAP_TELEMETRY | VSEC_CAP_TPMI | VSEC_CAP_DISCOVERY,
|
||||||
|
.deps = oobmsm_deps,
|
||||||
|
.num_deps = ARRAY_SIZE(oobmsm_deps),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* TGL info */
|
/* TGL info */
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ struct intel_tpmi_info {
|
||||||
struct intel_vsec_device *vsec_dev;
|
struct intel_vsec_device *vsec_dev;
|
||||||
int feature_count;
|
int feature_count;
|
||||||
u64 pfs_start;
|
u64 pfs_start;
|
||||||
struct intel_tpmi_plat_info plat_info;
|
struct oobmsm_plat_info plat_info;
|
||||||
void __iomem *tpmi_control_mem;
|
void __iomem *tpmi_control_mem;
|
||||||
struct dentry *dbgfs_dir;
|
struct dentry *dbgfs_dir;
|
||||||
};
|
};
|
||||||
|
|
@ -187,7 +187,7 @@ struct tpmi_feature_state {
|
||||||
/* Used during auxbus device creation */
|
/* Used during auxbus device creation */
|
||||||
static DEFINE_IDA(intel_vsec_tpmi_ida);
|
static DEFINE_IDA(intel_vsec_tpmi_ida);
|
||||||
|
|
||||||
struct intel_tpmi_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev)
|
struct oobmsm_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev)
|
||||||
{
|
{
|
||||||
struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev);
|
struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev);
|
||||||
|
|
||||||
|
|
@ -799,6 +799,10 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev)
|
||||||
ret = tpmi_process_info(tpmi_info, pfs);
|
ret = tpmi_process_info(tpmi_info, pfs);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
ret = intel_vsec_set_mapping(&tpmi_info->plat_info, vsec_dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pfs->pfs_header.tpmi_id == TPMI_CONTROL_ID)
|
if (pfs->pfs_header.tpmi_id == TPMI_CONTROL_ID)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,276 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
#
|
||||||
|
# Lenovo X86 Platform Specific Drivers
|
||||||
|
#
|
||||||
|
|
||||||
|
config IDEAPAD_LAPTOP
|
||||||
|
tristate "Lenovo IdeaPad Laptop Extras"
|
||||||
|
depends on ACPI
|
||||||
|
depends on ACPI_BATTERY
|
||||||
|
depends on RFKILL && INPUT
|
||||||
|
depends on SERIO_I8042
|
||||||
|
depends on BACKLIGHT_CLASS_DEVICE
|
||||||
|
depends on ACPI_VIDEO || ACPI_VIDEO = n
|
||||||
|
depends on ACPI_WMI || ACPI_WMI = n
|
||||||
|
select ACPI_PLATFORM_PROFILE
|
||||||
|
select INPUT_SPARSEKMAP
|
||||||
|
select NEW_LEDS
|
||||||
|
select LEDS_CLASS
|
||||||
|
help
|
||||||
|
This is a driver for Lenovo IdeaPad netbooks contains drivers for
|
||||||
|
rfkill switch, hotkey, fan control and backlight control.
|
||||||
|
|
||||||
|
config LENOVO_WMI_HOTKEY_UTILITIES
|
||||||
|
tristate "Lenovo Hotkey Utility WMI extras driver"
|
||||||
|
depends on ACPI_WMI
|
||||||
|
select NEW_LEDS
|
||||||
|
select LEDS_CLASS
|
||||||
|
imply IDEAPAD_LAPTOP
|
||||||
|
help
|
||||||
|
This driver provides WMI support for Lenovo customized hotkeys function,
|
||||||
|
such as LED control for audio/mic mute event for Ideapad, YOGA, XiaoXin,
|
||||||
|
Gaming, ThinkBook and so on.
|
||||||
|
|
||||||
|
config LENOVO_WMI_CAMERA
|
||||||
|
tristate "Lenovo WMI Camera Button driver"
|
||||||
|
depends on ACPI_WMI
|
||||||
|
depends on INPUT
|
||||||
|
help
|
||||||
|
This driver provides support for Lenovo camera button. The Camera
|
||||||
|
button is a GPIO device. This driver receives ACPI notifications when
|
||||||
|
the camera button is switched on/off.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module
|
||||||
|
will be called lenovo-wmi-camera.
|
||||||
|
|
||||||
|
config LENOVO_YMC
|
||||||
|
tristate "Lenovo Yoga Tablet Mode Control"
|
||||||
|
depends on ACPI_WMI
|
||||||
|
depends on INPUT
|
||||||
|
depends on IDEAPAD_LAPTOP
|
||||||
|
select INPUT_SPARSEKMAP
|
||||||
|
help
|
||||||
|
This driver maps the Tablet Mode Control switch to SW_TABLET_MODE input
|
||||||
|
events for Lenovo Yoga notebooks.
|
||||||
|
|
||||||
|
config THINKPAD_ACPI
|
||||||
|
tristate "ThinkPad ACPI Laptop Extras"
|
||||||
|
depends on ACPI_EC
|
||||||
|
depends on ACPI_BATTERY
|
||||||
|
depends on INPUT
|
||||||
|
depends on RFKILL || RFKILL = n
|
||||||
|
depends on ACPI_VIDEO || ACPI_VIDEO = n
|
||||||
|
depends on BACKLIGHT_CLASS_DEVICE
|
||||||
|
depends on I2C
|
||||||
|
depends on DRM
|
||||||
|
select ACPI_PLATFORM_PROFILE
|
||||||
|
select DRM_PRIVACY_SCREEN
|
||||||
|
select HWMON
|
||||||
|
select NVRAM
|
||||||
|
select NEW_LEDS
|
||||||
|
select LEDS_CLASS
|
||||||
|
select INPUT_SPARSEKMAP
|
||||||
|
help
|
||||||
|
This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
|
||||||
|
support for Fn-Fx key combinations, Bluetooth control, video
|
||||||
|
output switching, ThinkLight control, UltraBay eject and more.
|
||||||
|
For more information about this driver see
|
||||||
|
<file:Documentation/admin-guide/laptops/thinkpad-acpi.rst> and
|
||||||
|
<http://ibm-acpi.sf.net/> .
|
||||||
|
|
||||||
|
This driver was formerly known as ibm-acpi.
|
||||||
|
|
||||||
|
Extra functionality will be available if the rfkill (CONFIG_RFKILL)
|
||||||
|
and/or ALSA (CONFIG_SND) subsystems are available in the kernel.
|
||||||
|
Note that if you want ThinkPad-ACPI to be built-in instead of
|
||||||
|
modular, ALSA and rfkill will also have to be built-in.
|
||||||
|
|
||||||
|
If you have an IBM or Lenovo ThinkPad laptop, say Y or M here.
|
||||||
|
|
||||||
|
config THINKPAD_ACPI_ALSA_SUPPORT
|
||||||
|
bool "Console audio control ALSA interface"
|
||||||
|
depends on THINKPAD_ACPI
|
||||||
|
depends on SND
|
||||||
|
depends on SND = y || THINKPAD_ACPI = SND
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Enables monitoring of the built-in console audio output control
|
||||||
|
(headphone and speakers), which is operated by the mute and (in
|
||||||
|
some ThinkPad models) volume hotkeys.
|
||||||
|
|
||||||
|
If this option is enabled, ThinkPad-ACPI will export an ALSA card
|
||||||
|
with a single read-only mixer control, which should be used for
|
||||||
|
on-screen-display feedback purposes by the Desktop Environment.
|
||||||
|
|
||||||
|
Optionally, the driver will also allow software control (the
|
||||||
|
ALSA mixer will be made read-write). Please refer to the driver
|
||||||
|
documentation for details.
|
||||||
|
|
||||||
|
All IBM models have both volume and mute control. Newer Lenovo
|
||||||
|
models only have mute control (the volume hotkeys are just normal
|
||||||
|
keys and volume control is done through the main HDA mixer).
|
||||||
|
|
||||||
|
config THINKPAD_ACPI_DEBUGFACILITIES
|
||||||
|
bool "Maintainer debug facilities"
|
||||||
|
depends on THINKPAD_ACPI
|
||||||
|
help
|
||||||
|
Enables extra stuff in the thinkpad-acpi which is completely useless
|
||||||
|
for normal use. Read the driver source to find out what it does.
|
||||||
|
|
||||||
|
Say N here, unless you were told by a kernel maintainer to do
|
||||||
|
otherwise.
|
||||||
|
|
||||||
|
config THINKPAD_ACPI_DEBUG
|
||||||
|
bool "Verbose debug mode"
|
||||||
|
depends on THINKPAD_ACPI
|
||||||
|
help
|
||||||
|
Enables extra debugging information, at the expense of a slightly
|
||||||
|
increase in driver size.
|
||||||
|
|
||||||
|
If you are not sure, say N here.
|
||||||
|
|
||||||
|
config THINKPAD_ACPI_UNSAFE_LEDS
|
||||||
|
bool "Allow control of important LEDs (unsafe)"
|
||||||
|
depends on THINKPAD_ACPI
|
||||||
|
help
|
||||||
|
Overriding LED state on ThinkPads can mask important
|
||||||
|
firmware alerts (like critical battery condition), or misled
|
||||||
|
the user into damaging the hardware (undocking or ejecting
|
||||||
|
the bay while buses are still active), etc.
|
||||||
|
|
||||||
|
LED control on the ThinkPad is write-only (with very few
|
||||||
|
exceptions on very ancient models), which makes it
|
||||||
|
impossible to know beforehand if important information will
|
||||||
|
be lost when one changes LED state.
|
||||||
|
|
||||||
|
Users that know what they are doing can enable this option
|
||||||
|
and the driver will allow control of every LED, including
|
||||||
|
the ones on the dock stations.
|
||||||
|
|
||||||
|
Never enable this option on a distribution kernel.
|
||||||
|
|
||||||
|
Say N here, unless you are building a kernel for your own
|
||||||
|
use, and need to control the important firmware LEDs.
|
||||||
|
|
||||||
|
config THINKPAD_ACPI_VIDEO
|
||||||
|
bool "Video output control support"
|
||||||
|
depends on THINKPAD_ACPI
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Allows the thinkpad_acpi driver to provide an interface to control
|
||||||
|
the various video output ports.
|
||||||
|
|
||||||
|
This feature often won't work well, depending on ThinkPad model,
|
||||||
|
display state, video output devices in use, whether there is a X
|
||||||
|
server running, phase of the moon, and the current mood of
|
||||||
|
Schroedinger's cat. If you can use X.org's RandR to control
|
||||||
|
your ThinkPad's video output ports instead of this feature,
|
||||||
|
don't think twice: do it and say N here to save memory and avoid
|
||||||
|
bad interactions with X.org.
|
||||||
|
|
||||||
|
NOTE: access to this feature is limited to processes with the
|
||||||
|
CAP_SYS_ADMIN capability, to avoid local DoS issues in platforms
|
||||||
|
where it interacts badly with X.org.
|
||||||
|
|
||||||
|
If you are not sure, say Y here but do try to check if you could
|
||||||
|
be using X.org RandR instead.
|
||||||
|
|
||||||
|
config THINKPAD_ACPI_HOTKEY_POLL
|
||||||
|
bool "Support NVRAM polling for hot keys"
|
||||||
|
depends on THINKPAD_ACPI
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Some thinkpad models benefit from NVRAM polling to detect a few of
|
||||||
|
the hot key press events. If you know your ThinkPad model does not
|
||||||
|
need to do NVRAM polling to support any of the hot keys you use,
|
||||||
|
unselecting this option will save about 1kB of memory.
|
||||||
|
|
||||||
|
ThinkPads T40 and newer, R52 and newer, and X31 and newer are
|
||||||
|
unlikely to need NVRAM polling in their latest BIOS versions.
|
||||||
|
|
||||||
|
NVRAM polling can detect at most the following keys: ThinkPad/Access
|
||||||
|
IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute,
|
||||||
|
Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12).
|
||||||
|
|
||||||
|
If you are not sure, say Y here. The driver enables polling only if
|
||||||
|
it is strictly necessary to do so.
|
||||||
|
|
||||||
|
config THINKPAD_LMI
|
||||||
|
tristate "Lenovo WMI-based systems management driver"
|
||||||
|
depends on ACPI_WMI
|
||||||
|
depends on DMI
|
||||||
|
select FW_ATTR_CLASS
|
||||||
|
help
|
||||||
|
This driver allows changing BIOS settings on Lenovo machines whose
|
||||||
|
BIOS support the WMI interface.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will
|
||||||
|
be called think-lmi.
|
||||||
|
|
||||||
|
config YOGABOOK
|
||||||
|
tristate "Lenovo Yoga Book tablet key driver"
|
||||||
|
depends on ACPI_WMI
|
||||||
|
depends on INPUT
|
||||||
|
depends on I2C
|
||||||
|
select LEDS_CLASS
|
||||||
|
select NEW_LEDS
|
||||||
|
help
|
||||||
|
Say Y here if you want to support the 'Pen' key and keyboard backlight
|
||||||
|
control on the Lenovo Yoga Book tablets.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will
|
||||||
|
be called lenovo-yogabook.
|
||||||
|
|
||||||
|
config YT2_1380
|
||||||
|
tristate "Lenovo Yoga Tablet 2 1380 fast charge driver"
|
||||||
|
depends on SERIAL_DEV_BUS
|
||||||
|
depends on EXTCON
|
||||||
|
depends on ACPI
|
||||||
|
help
|
||||||
|
Say Y here to enable support for the custom fast charging protocol
|
||||||
|
found on the Lenovo Yoga Tablet 2 1380F / 1380L models.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will
|
||||||
|
be called lenovo-yogabook.
|
||||||
|
|
||||||
|
config LENOVO_WMI_DATA01
|
||||||
|
tristate
|
||||||
|
depends on ACPI_WMI
|
||||||
|
|
||||||
|
config LENOVO_WMI_EVENTS
|
||||||
|
tristate
|
||||||
|
depends on ACPI_WMI
|
||||||
|
|
||||||
|
config LENOVO_WMI_HELPERS
|
||||||
|
tristate
|
||||||
|
depends on ACPI_WMI
|
||||||
|
|
||||||
|
config LENOVO_WMI_GAMEZONE
|
||||||
|
tristate "Lenovo GameZone WMI Driver"
|
||||||
|
depends on ACPI_WMI
|
||||||
|
depends on DMI
|
||||||
|
select ACPI_PLATFORM_PROFILE
|
||||||
|
select LENOVO_WMI_EVENTS
|
||||||
|
select LENOVO_WMI_HELPERS
|
||||||
|
select LENOVO_WMI_TUNING
|
||||||
|
help
|
||||||
|
Say Y here if you have a WMI aware Lenovo Legion device and would like to use the
|
||||||
|
platform-profile firmware interface to manage power usage.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will
|
||||||
|
be called lenovo-wmi-gamezone.
|
||||||
|
|
||||||
|
config LENOVO_WMI_TUNING
|
||||||
|
tristate "Lenovo Other Mode WMI Driver"
|
||||||
|
depends on ACPI_WMI
|
||||||
|
select FW_ATTR_CLASS
|
||||||
|
select LENOVO_WMI_DATA01
|
||||||
|
select LENOVO_WMI_EVENTS
|
||||||
|
select LENOVO_WMI_HELPERS
|
||||||
|
help
|
||||||
|
Say Y here if you have a WMI aware Lenovo Legion device and would like to use the
|
||||||
|
firmware_attributes API to control various tunable settings typically exposed by
|
||||||
|
Lenovo software in Windows.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will
|
||||||
|
be called lenovo-wmi-other.
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
#
|
||||||
|
# Makefile for linux/drivers/platform/x86/lenovo
|
||||||
|
# Lenovo x86 Platform Specific Drivers
|
||||||
|
#
|
||||||
|
obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o
|
||||||
|
obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o
|
||||||
|
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
|
||||||
|
|
||||||
|
lenovo-target-$(CONFIG_LENOVO_WMI_HOTKEY_UTILITIES) += wmi-hotkey-utilities.o
|
||||||
|
lenovo-target-$(CONFIG_LENOVO_YMC) += ymc.o
|
||||||
|
lenovo-target-$(CONFIG_YOGABOOK) += yogabook.o
|
||||||
|
lenovo-target-$(CONFIG_YT2_1380) += yoga-tab2-pro-1380-fastcharger.o
|
||||||
|
lenovo-target-$(CONFIG_LENOVO_WMI_CAMERA) += wmi-camera.o
|
||||||
|
lenovo-target-$(CONFIG_LENOVO_WMI_DATA01) += wmi-capdata01.o
|
||||||
|
lenovo-target-$(CONFIG_LENOVO_WMI_EVENTS) += wmi-events.o
|
||||||
|
lenovo-target-$(CONFIG_LENOVO_WMI_HELPERS) += wmi-helpers.o
|
||||||
|
lenovo-target-$(CONFIG_LENOVO_WMI_GAMEZONE) += wmi-gamezone.o
|
||||||
|
lenovo-target-$(CONFIG_LENOVO_WMI_TUNING) += wmi-other.o
|
||||||
|
|
||||||
|
# Add 'lenovo' prefix to each module listed in lenovo-target-*
|
||||||
|
define LENOVO_OBJ_TARGET
|
||||||
|
lenovo-$(1)-y := $(1).o
|
||||||
|
obj-$(2) += lenovo-$(1).o
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(foreach target, $(basename $(lenovo-target-y)), $(eval $(call LENOVO_OBJ_TARGET,$(target),y)))
|
||||||
|
$(foreach target, $(basename $(lenovo-target-m)), $(eval $(call LENOVO_OBJ_TARGET,$(target),m)))
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/platform_profile.h>
|
#include <linux/platform_profile.h>
|
||||||
|
#include <linux/power_supply.h>
|
||||||
#include <linux/rfkill.h>
|
#include <linux/rfkill.h>
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
|
|
@ -35,6 +36,7 @@
|
||||||
#include <linux/wmi.h>
|
#include <linux/wmi.h>
|
||||||
#include "ideapad-laptop.h"
|
#include "ideapad-laptop.h"
|
||||||
|
|
||||||
|
#include <acpi/battery.h>
|
||||||
#include <acpi/video.h>
|
#include <acpi/video.h>
|
||||||
|
|
||||||
#include <dt-bindings/leds/common.h>
|
#include <dt-bindings/leds/common.h>
|
||||||
|
|
@ -163,6 +165,7 @@ struct ideapad_private {
|
||||||
struct backlight_device *blightdev;
|
struct backlight_device *blightdev;
|
||||||
struct ideapad_dytc_priv *dytc;
|
struct ideapad_dytc_priv *dytc;
|
||||||
struct dentry *debug;
|
struct dentry *debug;
|
||||||
|
struct acpi_battery_hook battery_hook;
|
||||||
unsigned long cfg;
|
unsigned long cfg;
|
||||||
unsigned long r_touchpad_val;
|
unsigned long r_touchpad_val;
|
||||||
struct {
|
struct {
|
||||||
|
|
@ -604,6 +607,11 @@ static ssize_t camera_power_store(struct device *dev,
|
||||||
|
|
||||||
static DEVICE_ATTR_RW(camera_power);
|
static DEVICE_ATTR_RW(camera_power);
|
||||||
|
|
||||||
|
static void show_conservation_mode_deprecation_warning(struct device *dev)
|
||||||
|
{
|
||||||
|
dev_warn_once(dev, "conservation_mode attribute has been deprecated, see charge_types.\n");
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t conservation_mode_show(struct device *dev,
|
static ssize_t conservation_mode_show(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
|
|
@ -612,6 +620,8 @@ static ssize_t conservation_mode_show(struct device *dev,
|
||||||
unsigned long result;
|
unsigned long result;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
show_conservation_mode_deprecation_warning(dev);
|
||||||
|
|
||||||
err = eval_gbmd(priv->adev->handle, &result);
|
err = eval_gbmd(priv->adev->handle, &result);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
@ -627,6 +637,8 @@ static ssize_t conservation_mode_store(struct device *dev,
|
||||||
bool state;
|
bool state;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
show_conservation_mode_deprecation_warning(dev);
|
||||||
|
|
||||||
err = kstrtobool(buf, &state);
|
err = kstrtobool(buf, &state);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
@ -1988,10 +2000,90 @@ static const struct dmi_system_id ctrl_ps2_aux_port_list[] = {
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static void ideapad_check_features(struct ideapad_private *priv)
|
static int ideapad_psy_ext_set_prop(struct power_supply *psy,
|
||||||
|
const struct power_supply_ext *ext,
|
||||||
|
void *ext_data,
|
||||||
|
enum power_supply_property psp,
|
||||||
|
const union power_supply_propval *val)
|
||||||
|
{
|
||||||
|
struct ideapad_private *priv = ext_data;
|
||||||
|
|
||||||
|
switch (val->intval) {
|
||||||
|
case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE:
|
||||||
|
return exec_sbmc(priv->adev->handle, SBMC_CONSERVATION_ON);
|
||||||
|
case POWER_SUPPLY_CHARGE_TYPE_STANDARD:
|
||||||
|
return exec_sbmc(priv->adev->handle, SBMC_CONSERVATION_OFF);
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ideapad_psy_ext_get_prop(struct power_supply *psy,
|
||||||
|
const struct power_supply_ext *ext,
|
||||||
|
void *ext_data,
|
||||||
|
enum power_supply_property psp,
|
||||||
|
union power_supply_propval *val)
|
||||||
|
{
|
||||||
|
struct ideapad_private *priv = ext_data;
|
||||||
|
unsigned long result;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = eval_gbmd(priv->adev->handle, &result);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (test_bit(GBMD_CONSERVATION_STATE_BIT, &result))
|
||||||
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE;
|
||||||
|
else
|
||||||
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ideapad_psy_prop_is_writeable(struct power_supply *psy,
|
||||||
|
const struct power_supply_ext *ext,
|
||||||
|
void *data,
|
||||||
|
enum power_supply_property psp)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const enum power_supply_property ideapad_power_supply_props[] = {
|
||||||
|
POWER_SUPPLY_PROP_CHARGE_TYPES,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct power_supply_ext ideapad_battery_ext = {
|
||||||
|
.name = "ideapad_laptop",
|
||||||
|
.properties = ideapad_power_supply_props,
|
||||||
|
.num_properties = ARRAY_SIZE(ideapad_power_supply_props),
|
||||||
|
.charge_types = (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |
|
||||||
|
BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)),
|
||||||
|
.get_property = ideapad_psy_ext_get_prop,
|
||||||
|
.set_property = ideapad_psy_ext_set_prop,
|
||||||
|
.property_is_writeable = ideapad_psy_prop_is_writeable,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ideapad_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)
|
||||||
|
{
|
||||||
|
struct ideapad_private *priv = container_of(hook, struct ideapad_private, battery_hook);
|
||||||
|
|
||||||
|
return power_supply_register_extension(battery, &ideapad_battery_ext,
|
||||||
|
&priv->platform_device->dev, priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ideapad_battery_remove(struct power_supply *battery,
|
||||||
|
struct acpi_battery_hook *hook)
|
||||||
|
{
|
||||||
|
power_supply_unregister_extension(battery, &ideapad_battery_ext);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ideapad_check_features(struct ideapad_private *priv)
|
||||||
{
|
{
|
||||||
acpi_handle handle = priv->adev->handle;
|
acpi_handle handle = priv->adev->handle;
|
||||||
unsigned long val;
|
unsigned long val;
|
||||||
|
int err;
|
||||||
|
|
||||||
priv->features.set_fn_lock_led =
|
priv->features.set_fn_lock_led =
|
||||||
set_fn_lock_led || dmi_check_system(set_fn_lock_led_list);
|
set_fn_lock_led || dmi_check_system(set_fn_lock_led_list);
|
||||||
|
|
@ -2006,8 +2098,16 @@ static void ideapad_check_features(struct ideapad_private *priv)
|
||||||
if (!read_ec_data(handle, VPCCMD_R_FAN, &val))
|
if (!read_ec_data(handle, VPCCMD_R_FAN, &val))
|
||||||
priv->features.fan_mode = true;
|
priv->features.fan_mode = true;
|
||||||
|
|
||||||
if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC"))
|
if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) {
|
||||||
priv->features.conservation_mode = true;
|
priv->features.conservation_mode = true;
|
||||||
|
priv->battery_hook.add_battery = ideapad_battery_add;
|
||||||
|
priv->battery_hook.remove_battery = ideapad_battery_remove;
|
||||||
|
priv->battery_hook.name = "Ideapad Battery Extension";
|
||||||
|
|
||||||
|
err = devm_battery_hook_register(&priv->platform_device->dev, &priv->battery_hook);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
if (acpi_has_method(handle, "DYTC"))
|
if (acpi_has_method(handle, "DYTC"))
|
||||||
priv->features.dytc = true;
|
priv->features.dytc = true;
|
||||||
|
|
@ -2042,6 +2142,8 @@ static void ideapad_check_features(struct ideapad_private *priv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_ACPI_WMI)
|
#if IS_ENABLED(CONFIG_ACPI_WMI)
|
||||||
|
|
@ -2190,7 +2292,9 @@ static int ideapad_acpi_add(struct platform_device *pdev)
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
ideapad_check_features(priv);
|
err = ideapad_check_features(priv);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
ideapad_debugfs_init(priv);
|
ideapad_debugfs_init(priv);
|
||||||
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/dmi.h>
|
#include <linux/dmi.h>
|
||||||
#include <linux/wmi.h>
|
#include <linux/wmi.h>
|
||||||
#include "firmware_attributes_class.h"
|
#include "../firmware_attributes_class.h"
|
||||||
#include "think-lmi.h"
|
#include "think-lmi.h"
|
||||||
|
|
||||||
static bool debug_support;
|
static bool debug_support;
|
||||||
|
|
@ -772,6 +772,7 @@ static ssize_t certificate_store(struct kobject *kobj,
|
||||||
struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
|
struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
|
||||||
enum cert_install_mode install_mode = TLMI_CERT_INSTALL;
|
enum cert_install_mode install_mode = TLMI_CERT_INSTALL;
|
||||||
char *auth_str, *new_cert;
|
char *auth_str, *new_cert;
|
||||||
|
const char *serial;
|
||||||
char *signature;
|
char *signature;
|
||||||
char *guid;
|
char *guid;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
@ -789,9 +790,10 @@ static ssize_t certificate_store(struct kobject *kobj,
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
|
|
||||||
/* Format: 'serial#, signature' */
|
/* Format: 'serial#, signature' */
|
||||||
auth_str = cert_command(setting,
|
serial = dmi_get_system_info(DMI_PRODUCT_SERIAL);
|
||||||
dmi_get_system_info(DMI_PRODUCT_SERIAL),
|
if (!serial)
|
||||||
setting->signature);
|
return -ENODEV;
|
||||||
|
auth_str = cert_command(setting, serial, setting->signature);
|
||||||
if (!auth_str)
|
if (!auth_str)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
|
@ -81,7 +81,7 @@
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/initval.h>
|
#include <sound/initval.h>
|
||||||
|
|
||||||
#include "dual_accel_detect.h"
|
#include "../dual_accel_detect.h"
|
||||||
|
|
||||||
/* ThinkPad CMOS commands */
|
/* ThinkPad CMOS commands */
|
||||||
#define TP_CMOS_VOLUME_DOWN 0
|
#define TP_CMOS_VOLUME_DOWN 0
|
||||||
|
|
@ -559,12 +559,12 @@ static unsigned long __init tpacpi_check_quirks(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool __pure __init tpacpi_is_lenovo(void)
|
static __always_inline bool __pure __init tpacpi_is_lenovo(void)
|
||||||
{
|
{
|
||||||
return thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO;
|
return thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool __pure __init tpacpi_is_ibm(void)
|
static __always_inline bool __pure __init tpacpi_is_ibm(void)
|
||||||
{
|
{
|
||||||
return thinkpad_id.vendor == PCI_VENDOR_ID_IBM;
|
return thinkpad_id.vendor == PCI_VENDOR_ID_IBM;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,302 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Lenovo Capability Data 01 WMI Data Block driver.
|
||||||
|
*
|
||||||
|
* Lenovo Capability Data 01 provides information on tunable attributes used by
|
||||||
|
* the "Other Mode" WMI interface. The data includes if the attribute is
|
||||||
|
* supported by the hardware, the default_value, max_value, min_value, and step
|
||||||
|
* increment. Each attribute has multiple pages, one for each of the thermal
|
||||||
|
* modes managed by the Gamezone interface.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/cleanup.h>
|
||||||
|
#include <linux/component.h>
|
||||||
|
#include <linux/container_of.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/gfp_types.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/mutex_types.h>
|
||||||
|
#include <linux/notifier.h>
|
||||||
|
#include <linux/overflow.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/wmi.h>
|
||||||
|
|
||||||
|
#include "wmi-capdata01.h"
|
||||||
|
|
||||||
|
#define LENOVO_CAPABILITY_DATA_01_GUID "7A8F5407-CB67-4D6E-B547-39B3BE018154"
|
||||||
|
|
||||||
|
#define ACPI_AC_CLASS "ac_adapter"
|
||||||
|
#define ACPI_AC_NOTIFY_STATUS 0x80
|
||||||
|
|
||||||
|
struct lwmi_cd01_priv {
|
||||||
|
struct notifier_block acpi_nb; /* ACPI events */
|
||||||
|
struct wmi_device *wdev;
|
||||||
|
struct cd01_list *list;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cd01_list {
|
||||||
|
struct mutex list_mutex; /* list R/W mutex */
|
||||||
|
u8 count;
|
||||||
|
struct capdata01 data[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_cd01_component_bind() - Bind component to master device.
|
||||||
|
* @cd01_dev: Pointer to the lenovo-wmi-capdata01 driver parent device.
|
||||||
|
* @om_dev: Pointer to the lenovo-wmi-other driver parent device.
|
||||||
|
* @data: capdata01_list object pointer used to return the capability data.
|
||||||
|
*
|
||||||
|
* On lenovo-wmi-other's master bind, provide a pointer to the local capdata01
|
||||||
|
* list. This is used to call lwmi_cd01_get_data to look up attribute data
|
||||||
|
* from the lenovo-wmi-other driver.
|
||||||
|
*
|
||||||
|
* Return: 0
|
||||||
|
*/
|
||||||
|
static int lwmi_cd01_component_bind(struct device *cd01_dev,
|
||||||
|
struct device *om_dev, void *data)
|
||||||
|
{
|
||||||
|
struct lwmi_cd01_priv *priv = dev_get_drvdata(cd01_dev);
|
||||||
|
struct cd01_list **cd01_list = data;
|
||||||
|
|
||||||
|
*cd01_list = priv->list;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct component_ops lwmi_cd01_component_ops = {
|
||||||
|
.bind = lwmi_cd01_component_bind,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_cd01_get_data - Get the data of the specified attribute
|
||||||
|
* @list: The lenovo-wmi-capdata01 pointer to its cd01_list struct.
|
||||||
|
* @attribute_id: The capdata attribute ID to be found.
|
||||||
|
* @output: Pointer to a capdata01 struct to return the data.
|
||||||
|
*
|
||||||
|
* Retrieves the capability data 01 struct pointer for the given
|
||||||
|
* attribute for its specified thermal mode.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or -EINVAL.
|
||||||
|
*/
|
||||||
|
int lwmi_cd01_get_data(struct cd01_list *list, u32 attribute_id, struct capdata01 *output)
|
||||||
|
{
|
||||||
|
u8 idx;
|
||||||
|
|
||||||
|
guard(mutex)(&list->list_mutex);
|
||||||
|
for (idx = 0; idx < list->count; idx++) {
|
||||||
|
if (list->data[idx].id != attribute_id)
|
||||||
|
continue;
|
||||||
|
memcpy(output, &list->data[idx], sizeof(list->data[idx]));
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS_GPL(lwmi_cd01_get_data, "LENOVO_WMI_CD01");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_cd01_cache() - Cache all WMI data block information
|
||||||
|
* @priv: lenovo-wmi-capdata01 driver data.
|
||||||
|
*
|
||||||
|
* Loop through each WMI data block and cache the data.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or an error.
|
||||||
|
*/
|
||||||
|
static int lwmi_cd01_cache(struct lwmi_cd01_priv *priv)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
guard(mutex)(&priv->list->list_mutex);
|
||||||
|
for (idx = 0; idx < priv->list->count; idx++) {
|
||||||
|
union acpi_object *ret_obj __free(kfree) = NULL;
|
||||||
|
|
||||||
|
ret_obj = wmidev_block_query(priv->wdev, idx);
|
||||||
|
if (!ret_obj)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (ret_obj->type != ACPI_TYPE_BUFFER ||
|
||||||
|
ret_obj->buffer.length < sizeof(priv->list->data[idx]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
memcpy(&priv->list->data[idx], ret_obj->buffer.pointer,
|
||||||
|
ret_obj->buffer.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_cd01_alloc() - Allocate a cd01_list struct in drvdata
|
||||||
|
* @priv: lenovo-wmi-capdata01 driver data.
|
||||||
|
*
|
||||||
|
* Allocate a cd01_list struct large enough to contain data from all WMI data
|
||||||
|
* blocks provided by the interface.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or an error.
|
||||||
|
*/
|
||||||
|
static int lwmi_cd01_alloc(struct lwmi_cd01_priv *priv)
|
||||||
|
{
|
||||||
|
struct cd01_list *list;
|
||||||
|
size_t list_size;
|
||||||
|
int count, ret;
|
||||||
|
|
||||||
|
count = wmidev_instance_count(priv->wdev);
|
||||||
|
list_size = struct_size(list, data, count);
|
||||||
|
|
||||||
|
list = devm_kzalloc(&priv->wdev->dev, list_size, GFP_KERNEL);
|
||||||
|
if (!list)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = devm_mutex_init(&priv->wdev->dev, &list->list_mutex);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
list->count = count;
|
||||||
|
priv->list = list;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_cd01_setup() - Cache all WMI data block information
|
||||||
|
* @priv: lenovo-wmi-capdata01 driver data.
|
||||||
|
*
|
||||||
|
* Allocate a cd01_list struct large enough to contain data from all WMI data
|
||||||
|
* blocks provided by the interface. Then loop through each data block and
|
||||||
|
* cache the data.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or an error code.
|
||||||
|
*/
|
||||||
|
static int lwmi_cd01_setup(struct lwmi_cd01_priv *priv)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = lwmi_cd01_alloc(priv);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return lwmi_cd01_cache(priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_cd01_notifier_call() - Call method for lenovo-wmi-capdata01 driver notifier.
|
||||||
|
* block call chain.
|
||||||
|
* @nb: The notifier_block registered to lenovo-wmi-events driver.
|
||||||
|
* @action: Unused.
|
||||||
|
* @data: The ACPI event.
|
||||||
|
*
|
||||||
|
* For LWMI_EVENT_THERMAL_MODE, set current_mode and notify platform_profile
|
||||||
|
* of a change.
|
||||||
|
*
|
||||||
|
* Return: notifier_block status.
|
||||||
|
*/
|
||||||
|
static int lwmi_cd01_notifier_call(struct notifier_block *nb, unsigned long action,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct acpi_bus_event *event = data;
|
||||||
|
struct lwmi_cd01_priv *priv;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (strcmp(event->device_class, ACPI_AC_CLASS) != 0)
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
|
||||||
|
priv = container_of(nb, struct lwmi_cd01_priv, acpi_nb);
|
||||||
|
|
||||||
|
switch (event->type) {
|
||||||
|
case ACPI_AC_NOTIFY_STATUS:
|
||||||
|
ret = lwmi_cd01_cache(priv);
|
||||||
|
if (ret)
|
||||||
|
return NOTIFY_BAD;
|
||||||
|
|
||||||
|
return NOTIFY_OK;
|
||||||
|
default:
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_cd01_unregister() - Unregister the cd01 ACPI notifier_block.
|
||||||
|
* @data: The ACPI event notifier_block to unregister.
|
||||||
|
*/
|
||||||
|
static void lwmi_cd01_unregister(void *data)
|
||||||
|
{
|
||||||
|
struct notifier_block *acpi_nb = data;
|
||||||
|
|
||||||
|
unregister_acpi_notifier(acpi_nb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lwmi_cd01_probe(struct wmi_device *wdev, const void *context)
|
||||||
|
|
||||||
|
{
|
||||||
|
struct lwmi_cd01_priv *priv;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priv->wdev = wdev;
|
||||||
|
dev_set_drvdata(&wdev->dev, priv);
|
||||||
|
|
||||||
|
ret = lwmi_cd01_setup(priv);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
priv->acpi_nb.notifier_call = lwmi_cd01_notifier_call;
|
||||||
|
|
||||||
|
ret = register_acpi_notifier(&priv->acpi_nb);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = devm_add_action_or_reset(&wdev->dev, lwmi_cd01_unregister, &priv->acpi_nb);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return component_add(&wdev->dev, &lwmi_cd01_component_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lwmi_cd01_remove(struct wmi_device *wdev)
|
||||||
|
{
|
||||||
|
component_del(&wdev->dev, &lwmi_cd01_component_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wmi_device_id lwmi_cd01_id_table[] = {
|
||||||
|
{ LENOVO_CAPABILITY_DATA_01_GUID, NULL },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct wmi_driver lwmi_cd01_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "lenovo_wmi_cd01",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
|
},
|
||||||
|
.id_table = lwmi_cd01_id_table,
|
||||||
|
.probe = lwmi_cd01_probe,
|
||||||
|
.remove = lwmi_cd01_remove,
|
||||||
|
.no_singleton = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_cd01_match() - Match rule for the master driver.
|
||||||
|
* @dev: Pointer to the capability data 01 parent device.
|
||||||
|
* @data: Unused void pointer for passing match criteria.
|
||||||
|
*
|
||||||
|
* Return: int.
|
||||||
|
*/
|
||||||
|
int lwmi_cd01_match(struct device *dev, void *data)
|
||||||
|
{
|
||||||
|
return dev->driver == &lwmi_cd01_driver.driver;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS_GPL(lwmi_cd01_match, "LENOVO_WMI_CD01");
|
||||||
|
|
||||||
|
module_wmi_driver(lwmi_cd01_driver);
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(wmi, lwmi_cd01_id_table);
|
||||||
|
MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("Lenovo Capability Data 01 WMI Driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
/* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */
|
||||||
|
|
||||||
|
#ifndef _LENOVO_WMI_CAPDATA01_H_
|
||||||
|
#define _LENOVO_WMI_CAPDATA01_H_
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
struct device;
|
||||||
|
struct cd01_list;
|
||||||
|
|
||||||
|
struct capdata01 {
|
||||||
|
u32 id;
|
||||||
|
u32 supported;
|
||||||
|
u32 default_value;
|
||||||
|
u32 step;
|
||||||
|
u32 min_value;
|
||||||
|
u32 max_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
int lwmi_cd01_get_data(struct cd01_list *list, u32 attribute_id, struct capdata01 *output);
|
||||||
|
int lwmi_cd01_match(struct device *dev, void *data);
|
||||||
|
|
||||||
|
#endif /* !_LENOVO_WMI_CAPDATA01_H_ */
|
||||||
|
|
@ -0,0 +1,196 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Lenovo WMI Events driver. Lenovo WMI interfaces provide various
|
||||||
|
* hardware triggered events that many drivers need to have propagated.
|
||||||
|
* This driver provides a uniform entrypoint for these events so that
|
||||||
|
* any driver that needs to respond to these events can subscribe to a
|
||||||
|
* notifier chain.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/notifier.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/wmi.h>
|
||||||
|
|
||||||
|
#include "wmi-events.h"
|
||||||
|
#include "wmi-gamezone.h"
|
||||||
|
|
||||||
|
#define THERMAL_MODE_EVENT_GUID "D320289E-8FEA-41E0-86F9-911D83151B5F"
|
||||||
|
|
||||||
|
#define LWMI_EVENT_DEVICE(guid, type) \
|
||||||
|
.guid_string = (guid), .context = &(enum lwmi_events_type) \
|
||||||
|
{ \
|
||||||
|
type \
|
||||||
|
}
|
||||||
|
|
||||||
|
static BLOCKING_NOTIFIER_HEAD(events_chain_head);
|
||||||
|
|
||||||
|
struct lwmi_events_priv {
|
||||||
|
struct wmi_device *wdev;
|
||||||
|
enum lwmi_events_type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_events_register_notifier() - Add a notifier to the notifier chain.
|
||||||
|
* @nb: The notifier_block struct to register
|
||||||
|
*
|
||||||
|
* Call blocking_notifier_chain_register to register the notifier block to the
|
||||||
|
* lenovo-wmi-events driver blocking notifier chain.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, %-EEXIST on error.
|
||||||
|
*/
|
||||||
|
int lwmi_events_register_notifier(struct notifier_block *nb)
|
||||||
|
{
|
||||||
|
return blocking_notifier_chain_register(&events_chain_head, nb);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS_GPL(lwmi_events_register_notifier, "LENOVO_WMI_EVENTS");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_events_unregister_notifier() - Remove a notifier from the notifier
|
||||||
|
* chain.
|
||||||
|
* @nb: The notifier_block struct to unregister
|
||||||
|
*
|
||||||
|
* Call blocking_notifier_chain_unregister to unregister the notifier block
|
||||||
|
* from the lenovo-wmi-events driver blocking notifier chain.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, %-ENOENT on error.
|
||||||
|
*/
|
||||||
|
int lwmi_events_unregister_notifier(struct notifier_block *nb)
|
||||||
|
{
|
||||||
|
return blocking_notifier_chain_unregister(&events_chain_head, nb);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS_GPL(lwmi_events_unregister_notifier, "LENOVO_WMI_EVENTS");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* devm_lwmi_events_unregister_notifier() - Remove a notifier from the notifier
|
||||||
|
* chain.
|
||||||
|
* @data: Void pointer to the notifier_block struct to unregister.
|
||||||
|
*
|
||||||
|
* Call lwmi_events_unregister_notifier to unregister the notifier block from
|
||||||
|
* the lenovo-wmi-events driver blocking notifier chain.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, %-ENOENT on error.
|
||||||
|
*/
|
||||||
|
static void devm_lwmi_events_unregister_notifier(void *data)
|
||||||
|
{
|
||||||
|
struct notifier_block *nb = data;
|
||||||
|
|
||||||
|
lwmi_events_unregister_notifier(nb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* devm_lwmi_events_register_notifier() - Add a notifier to the notifier chain.
|
||||||
|
* @dev: The parent device of the notifier_block struct.
|
||||||
|
* @nb: The notifier_block struct to register
|
||||||
|
*
|
||||||
|
* Call lwmi_events_register_notifier to register the notifier block to the
|
||||||
|
* lenovo-wmi-events driver blocking notifier chain. Then add, as a device
|
||||||
|
* managed action, unregister_notifier to automatically unregister the
|
||||||
|
* notifier block upon its parent device removal.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or an error code.
|
||||||
|
*/
|
||||||
|
int devm_lwmi_events_register_notifier(struct device *dev,
|
||||||
|
struct notifier_block *nb)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = lwmi_events_register_notifier(nb);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return devm_add_action_or_reset(dev, devm_lwmi_events_unregister_notifier, nb);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS_GPL(devm_lwmi_events_register_notifier, "LENOVO_WMI_EVENTS");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_events_notify() - Call functions for the notifier call chain.
|
||||||
|
* @wdev: The parent WMI device of the driver.
|
||||||
|
* @obj: ACPI object passed by the registered WMI Event.
|
||||||
|
*
|
||||||
|
* Validate WMI event data and notify all registered drivers of the event and
|
||||||
|
* its output.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or an error code.
|
||||||
|
*/
|
||||||
|
static void lwmi_events_notify(struct wmi_device *wdev, union acpi_object *obj)
|
||||||
|
{
|
||||||
|
struct lwmi_events_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||||
|
int sel_prof;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (priv->type) {
|
||||||
|
case LWMI_EVENT_THERMAL_MODE:
|
||||||
|
if (obj->type != ACPI_TYPE_INTEGER)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sel_prof = obj->integer.value;
|
||||||
|
|
||||||
|
switch (sel_prof) {
|
||||||
|
case LWMI_GZ_THERMAL_MODE_QUIET:
|
||||||
|
case LWMI_GZ_THERMAL_MODE_BALANCED:
|
||||||
|
case LWMI_GZ_THERMAL_MODE_PERFORMANCE:
|
||||||
|
case LWMI_GZ_THERMAL_MODE_EXTREME:
|
||||||
|
case LWMI_GZ_THERMAL_MODE_CUSTOM:
|
||||||
|
ret = blocking_notifier_call_chain(&events_chain_head,
|
||||||
|
LWMI_EVENT_THERMAL_MODE,
|
||||||
|
&sel_prof);
|
||||||
|
if (ret == NOTIFY_BAD)
|
||||||
|
dev_err(&wdev->dev,
|
||||||
|
"Failed to send notification to call chain for WMI Events\n");
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
dev_err(&wdev->dev, "Got invalid thermal mode: %x",
|
||||||
|
sel_prof);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lwmi_events_probe(struct wmi_device *wdev, const void *context)
|
||||||
|
{
|
||||||
|
struct lwmi_events_priv *priv;
|
||||||
|
|
||||||
|
if (!context)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priv->wdev = wdev;
|
||||||
|
priv->type = *(enum lwmi_events_type *)context;
|
||||||
|
dev_set_drvdata(&wdev->dev, priv);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wmi_device_id lwmi_events_id_table[] = {
|
||||||
|
{ LWMI_EVENT_DEVICE(THERMAL_MODE_EVENT_GUID, LWMI_EVENT_THERMAL_MODE) },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct wmi_driver lwmi_events_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "lenovo_wmi_events",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
|
},
|
||||||
|
.id_table = lwmi_events_id_table,
|
||||||
|
.probe = lwmi_events_probe,
|
||||||
|
.notify = lwmi_events_notify,
|
||||||
|
.no_singleton = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_wmi_driver(lwmi_events_driver);
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(wmi, lwmi_events_id_table);
|
||||||
|
MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("Lenovo WMI Events Driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
/* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */
|
||||||
|
|
||||||
|
#ifndef _LENOVO_WMI_EVENTS_H_
|
||||||
|
#define _LENOVO_WMI_EVENTS_H_
|
||||||
|
|
||||||
|
struct device;
|
||||||
|
struct notifier_block;
|
||||||
|
|
||||||
|
enum lwmi_events_type {
|
||||||
|
LWMI_EVENT_THERMAL_MODE = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
int lwmi_events_register_notifier(struct notifier_block *nb);
|
||||||
|
int lwmi_events_unregister_notifier(struct notifier_block *nb);
|
||||||
|
int devm_lwmi_events_register_notifier(struct device *dev,
|
||||||
|
struct notifier_block *nb);
|
||||||
|
|
||||||
|
#endif /* !_LENOVO_WMI_EVENTS_H_ */
|
||||||
|
|
@ -0,0 +1,407 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Lenovo GameZone WMI interface driver.
|
||||||
|
*
|
||||||
|
* The GameZone WMI interface provides platform profile and fan curve settings
|
||||||
|
* for devices that fall under the "Gaming Series" of Lenovo Legion devices.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/dmi.h>
|
||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/notifier.h>
|
||||||
|
#include <linux/platform_profile.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/spinlock_types.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/wmi.h>
|
||||||
|
|
||||||
|
#include "wmi-events.h"
|
||||||
|
#include "wmi-gamezone.h"
|
||||||
|
#include "wmi-helpers.h"
|
||||||
|
#include "wmi-other.h"
|
||||||
|
|
||||||
|
#define LENOVO_GAMEZONE_GUID "887B54E3-DDDC-4B2C-8B88-68A26A8835D0"
|
||||||
|
|
||||||
|
#define LWMI_GZ_METHOD_ID_SMARTFAN_SUP 43
|
||||||
|
#define LWMI_GZ_METHOD_ID_SMARTFAN_SET 44
|
||||||
|
#define LWMI_GZ_METHOD_ID_SMARTFAN_GET 45
|
||||||
|
|
||||||
|
static BLOCKING_NOTIFIER_HEAD(gz_chain_head);
|
||||||
|
|
||||||
|
struct lwmi_gz_priv {
|
||||||
|
enum thermal_mode current_mode;
|
||||||
|
struct notifier_block event_nb;
|
||||||
|
struct notifier_block mode_nb;
|
||||||
|
spinlock_t gz_mode_lock; /* current_mode lock */
|
||||||
|
struct wmi_device *wdev;
|
||||||
|
int extreme_supported;
|
||||||
|
struct device *ppdev;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct quirk_entry {
|
||||||
|
bool extreme_supported;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct quirk_entry quirk_no_extreme_bug = {
|
||||||
|
.extreme_supported = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_gz_mode_call() - Call method for lenovo-wmi-other driver notifier.
|
||||||
|
*
|
||||||
|
* @nb: The notifier_block registered to lenovo-wmi-other driver.
|
||||||
|
* @cmd: The event type.
|
||||||
|
* @data: Thermal mode enum pointer pointer for returning the thermal mode.
|
||||||
|
*
|
||||||
|
* For LWMI_GZ_GET_THERMAL_MODE, retrieve the current thermal mode.
|
||||||
|
*
|
||||||
|
* Return: Notifier_block status.
|
||||||
|
*/
|
||||||
|
static int lwmi_gz_mode_call(struct notifier_block *nb, unsigned long cmd,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
enum thermal_mode **mode = data;
|
||||||
|
struct lwmi_gz_priv *priv;
|
||||||
|
|
||||||
|
priv = container_of(nb, struct lwmi_gz_priv, mode_nb);
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case LWMI_GZ_GET_THERMAL_MODE:
|
||||||
|
scoped_guard(spinlock, &priv->gz_mode_lock) {
|
||||||
|
**mode = priv->current_mode;
|
||||||
|
}
|
||||||
|
return NOTIFY_OK;
|
||||||
|
default:
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_gz_event_call() - Call method for lenovo-wmi-events driver notifier.
|
||||||
|
* block call chain.
|
||||||
|
* @nb: The notifier_block registered to lenovo-wmi-events driver.
|
||||||
|
* @cmd: The event type.
|
||||||
|
* @data: The data to be updated by the event.
|
||||||
|
*
|
||||||
|
* For LWMI_EVENT_THERMAL_MODE, set current_mode and notify platform_profile
|
||||||
|
* of a change.
|
||||||
|
*
|
||||||
|
* Return: notifier_block status.
|
||||||
|
*/
|
||||||
|
static int lwmi_gz_event_call(struct notifier_block *nb, unsigned long cmd,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
enum thermal_mode *mode = data;
|
||||||
|
struct lwmi_gz_priv *priv;
|
||||||
|
|
||||||
|
priv = container_of(nb, struct lwmi_gz_priv, event_nb);
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case LWMI_EVENT_THERMAL_MODE:
|
||||||
|
scoped_guard(spinlock, &priv->gz_mode_lock) {
|
||||||
|
priv->current_mode = *mode;
|
||||||
|
}
|
||||||
|
platform_profile_notify(priv->ppdev);
|
||||||
|
return NOTIFY_STOP;
|
||||||
|
default:
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_gz_thermal_mode_supported() - Get the version of the WMI
|
||||||
|
* interface to determine the support level.
|
||||||
|
* @wdev: The Gamezone WMI device.
|
||||||
|
* @supported: Pointer to return the support level with.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or an error code.
|
||||||
|
*/
|
||||||
|
static int lwmi_gz_thermal_mode_supported(struct wmi_device *wdev,
|
||||||
|
int *supported)
|
||||||
|
{
|
||||||
|
return lwmi_dev_evaluate_int(wdev, 0x0, LWMI_GZ_METHOD_ID_SMARTFAN_SUP,
|
||||||
|
NULL, 0, supported);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_gz_thermal_mode_get() - Get the current thermal mode.
|
||||||
|
* @wdev: The Gamezone interface WMI device.
|
||||||
|
* @mode: Pointer to return the thermal mode with.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or an error code.
|
||||||
|
*/
|
||||||
|
static int lwmi_gz_thermal_mode_get(struct wmi_device *wdev,
|
||||||
|
enum thermal_mode *mode)
|
||||||
|
{
|
||||||
|
return lwmi_dev_evaluate_int(wdev, 0x0, LWMI_GZ_METHOD_ID_SMARTFAN_GET,
|
||||||
|
NULL, 0, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_gz_profile_get() - Get the current platform profile.
|
||||||
|
* @dev: the Gamezone interface parent device.
|
||||||
|
* @profile: Pointer to provide the current platform profile with.
|
||||||
|
*
|
||||||
|
* Call lwmi_gz_thermal_mode_get and convert the thermal mode into a platform
|
||||||
|
* profile based on the support level of the interface.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or an error code.
|
||||||
|
*/
|
||||||
|
static int lwmi_gz_profile_get(struct device *dev,
|
||||||
|
enum platform_profile_option *profile)
|
||||||
|
{
|
||||||
|
struct lwmi_gz_priv *priv = dev_get_drvdata(dev);
|
||||||
|
enum thermal_mode mode;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = lwmi_gz_thermal_mode_get(priv->wdev, &mode);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case LWMI_GZ_THERMAL_MODE_QUIET:
|
||||||
|
*profile = PLATFORM_PROFILE_LOW_POWER;
|
||||||
|
break;
|
||||||
|
case LWMI_GZ_THERMAL_MODE_BALANCED:
|
||||||
|
*profile = PLATFORM_PROFILE_BALANCED;
|
||||||
|
break;
|
||||||
|
case LWMI_GZ_THERMAL_MODE_PERFORMANCE:
|
||||||
|
if (priv->extreme_supported) {
|
||||||
|
*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*profile = PLATFORM_PROFILE_PERFORMANCE;
|
||||||
|
break;
|
||||||
|
case LWMI_GZ_THERMAL_MODE_EXTREME:
|
||||||
|
*profile = PLATFORM_PROFILE_PERFORMANCE;
|
||||||
|
break;
|
||||||
|
case LWMI_GZ_THERMAL_MODE_CUSTOM:
|
||||||
|
*profile = PLATFORM_PROFILE_CUSTOM;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
guard(spinlock)(&priv->gz_mode_lock);
|
||||||
|
priv->current_mode = mode;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_gz_profile_set() - Set the current platform profile.
|
||||||
|
* @dev: The Gamezone interface parent device.
|
||||||
|
* @profile: Pointer to the desired platform profile.
|
||||||
|
*
|
||||||
|
* Convert the given platform profile into a thermal mode based on the support
|
||||||
|
* level of the interface, then call the WMI method to set the thermal mode.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or an error code.
|
||||||
|
*/
|
||||||
|
static int lwmi_gz_profile_set(struct device *dev,
|
||||||
|
enum platform_profile_option profile)
|
||||||
|
{
|
||||||
|
struct lwmi_gz_priv *priv = dev_get_drvdata(dev);
|
||||||
|
struct wmi_method_args_32 args;
|
||||||
|
enum thermal_mode mode;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (profile) {
|
||||||
|
case PLATFORM_PROFILE_LOW_POWER:
|
||||||
|
mode = LWMI_GZ_THERMAL_MODE_QUIET;
|
||||||
|
break;
|
||||||
|
case PLATFORM_PROFILE_BALANCED:
|
||||||
|
mode = LWMI_GZ_THERMAL_MODE_BALANCED;
|
||||||
|
break;
|
||||||
|
case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
|
||||||
|
mode = LWMI_GZ_THERMAL_MODE_PERFORMANCE;
|
||||||
|
break;
|
||||||
|
case PLATFORM_PROFILE_PERFORMANCE:
|
||||||
|
if (priv->extreme_supported) {
|
||||||
|
mode = LWMI_GZ_THERMAL_MODE_EXTREME;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mode = LWMI_GZ_THERMAL_MODE_PERFORMANCE;
|
||||||
|
break;
|
||||||
|
case PLATFORM_PROFILE_CUSTOM:
|
||||||
|
mode = LWMI_GZ_THERMAL_MODE_CUSTOM;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.arg0 = mode;
|
||||||
|
|
||||||
|
ret = lwmi_dev_evaluate_int(priv->wdev, 0x0,
|
||||||
|
LWMI_GZ_METHOD_ID_SMARTFAN_SET,
|
||||||
|
(u8 *)&args, sizeof(args), NULL);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
guard(spinlock)(&priv->gz_mode_lock);
|
||||||
|
priv->current_mode = mode;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dmi_system_id fwbug_list[] = {
|
||||||
|
{
|
||||||
|
.ident = "Legion Go 8APU1",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_VERSION, "Legion Go 8APU1"),
|
||||||
|
},
|
||||||
|
.driver_data = &quirk_no_extreme_bug,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "Legion Go S 8APU1",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_VERSION, "Legion Go S 8APU1"),
|
||||||
|
},
|
||||||
|
.driver_data = &quirk_no_extreme_bug,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "Legion Go S 8ARP1",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_VERSION, "Legion Go S 8ARP1"),
|
||||||
|
},
|
||||||
|
.driver_data = &quirk_no_extreme_bug,
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_gz_extreme_supported() - Evaluate if a device supports extreme thermal mode.
|
||||||
|
* @profile_support_ver: Version of the WMI interface.
|
||||||
|
*
|
||||||
|
* Determine if the extreme thermal mode is supported by the hardware.
|
||||||
|
* Anything version 5 or lower does not. For devices with a version 6 or
|
||||||
|
* greater do a DMI check, as some devices report a version that supports
|
||||||
|
* extreme mode but have an incomplete entry in the BIOS. To ensure this
|
||||||
|
* cannot be set, quirk them to prevent assignment.
|
||||||
|
*
|
||||||
|
* Return: bool.
|
||||||
|
*/
|
||||||
|
static bool lwmi_gz_extreme_supported(int profile_support_ver)
|
||||||
|
{
|
||||||
|
const struct dmi_system_id *dmi_id;
|
||||||
|
struct quirk_entry *quirks;
|
||||||
|
|
||||||
|
if (profile_support_ver < 6)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dmi_id = dmi_first_match(fwbug_list);
|
||||||
|
if (!dmi_id)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
quirks = dmi_id->driver_data;
|
||||||
|
|
||||||
|
return quirks->extreme_supported;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_gz_platform_profile_probe - Enable and set up the platform profile
|
||||||
|
* device.
|
||||||
|
* @drvdata: Driver data for the interface.
|
||||||
|
* @choices: Container for enabled platform profiles.
|
||||||
|
*
|
||||||
|
* Determine if thermal mode is supported, and if so to what feature level.
|
||||||
|
* Then enable all supported platform profiles.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or an error code.
|
||||||
|
*/
|
||||||
|
static int lwmi_gz_platform_profile_probe(void *drvdata, unsigned long *choices)
|
||||||
|
{
|
||||||
|
struct lwmi_gz_priv *priv = drvdata;
|
||||||
|
int profile_support_ver;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = lwmi_gz_thermal_mode_supported(priv->wdev, &profile_support_ver);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (profile_support_ver < 1)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
|
||||||
|
set_bit(PLATFORM_PROFILE_BALANCED, choices);
|
||||||
|
set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
|
||||||
|
set_bit(PLATFORM_PROFILE_CUSTOM, choices);
|
||||||
|
|
||||||
|
priv->extreme_supported = lwmi_gz_extreme_supported(profile_support_ver);
|
||||||
|
if (priv->extreme_supported)
|
||||||
|
set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct platform_profile_ops lwmi_gz_platform_profile_ops = {
|
||||||
|
.probe = lwmi_gz_platform_profile_probe,
|
||||||
|
.profile_get = lwmi_gz_profile_get,
|
||||||
|
.profile_set = lwmi_gz_profile_set,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int lwmi_gz_probe(struct wmi_device *wdev, const void *context)
|
||||||
|
{
|
||||||
|
struct lwmi_gz_priv *priv;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priv->wdev = wdev;
|
||||||
|
dev_set_drvdata(&wdev->dev, priv);
|
||||||
|
|
||||||
|
priv->ppdev = devm_platform_profile_register(&wdev->dev, "lenovo-wmi-gamezone",
|
||||||
|
priv, &lwmi_gz_platform_profile_ops);
|
||||||
|
if (IS_ERR(priv->ppdev))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
spin_lock_init(&priv->gz_mode_lock);
|
||||||
|
|
||||||
|
ret = lwmi_gz_thermal_mode_get(wdev, &priv->current_mode);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
priv->event_nb.notifier_call = lwmi_gz_event_call;
|
||||||
|
ret = devm_lwmi_events_register_notifier(&wdev->dev, &priv->event_nb);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
priv->mode_nb.notifier_call = lwmi_gz_mode_call;
|
||||||
|
return devm_lwmi_om_register_notifier(&wdev->dev, &priv->mode_nb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wmi_device_id lwmi_gz_id_table[] = {
|
||||||
|
{ LENOVO_GAMEZONE_GUID, NULL },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct wmi_driver lwmi_gz_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "lenovo_wmi_gamezone",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
|
},
|
||||||
|
.id_table = lwmi_gz_id_table,
|
||||||
|
.probe = lwmi_gz_probe,
|
||||||
|
.no_singleton = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_wmi_driver(lwmi_gz_driver);
|
||||||
|
|
||||||
|
MODULE_IMPORT_NS("LENOVO_WMI_EVENTS");
|
||||||
|
MODULE_IMPORT_NS("LENOVO_WMI_HELPERS");
|
||||||
|
MODULE_IMPORT_NS("LENOVO_WMI_OTHER");
|
||||||
|
MODULE_DEVICE_TABLE(wmi, lwmi_gz_id_table);
|
||||||
|
MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("Lenovo GameZone WMI Driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
/* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */
|
||||||
|
|
||||||
|
#ifndef _LENOVO_WMI_GAMEZONE_H_
|
||||||
|
#define _LENOVO_WMI_GAMEZONE_H_
|
||||||
|
|
||||||
|
enum gamezone_events_type {
|
||||||
|
LWMI_GZ_GET_THERMAL_MODE = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum thermal_mode {
|
||||||
|
LWMI_GZ_THERMAL_MODE_QUIET = 0x01,
|
||||||
|
LWMI_GZ_THERMAL_MODE_BALANCED = 0x02,
|
||||||
|
LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
|
||||||
|
LWMI_GZ_THERMAL_MODE_EXTREME = 0xE0, /* Ver 6+ */
|
||||||
|
LWMI_GZ_THERMAL_MODE_CUSTOM = 0xFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* !_LENOVO_WMI_GAMEZONE_H_ */
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Lenovo Legion WMI helpers driver.
|
||||||
|
*
|
||||||
|
* The Lenovo Legion WMI interface is broken up into multiple GUID interfaces
|
||||||
|
* that require cross-references between GUID's for some functionality. The
|
||||||
|
* "Custom Mode" interface is a legacy interface for managing and displaying
|
||||||
|
* CPU & GPU power and hwmon settings and readings. The "Other Mode" interface
|
||||||
|
* is a modern interface that replaces or extends the "Custom Mode" interface
|
||||||
|
* methods. The "Gamezone" interface adds advanced features such as fan
|
||||||
|
* profiles and overclocking. The "Lighting" interface adds control of various
|
||||||
|
* status lights related to different hardware components. Each of these
|
||||||
|
* drivers uses a common procedure to get data from the WMI interface,
|
||||||
|
* enumerated here.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/cleanup.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/wmi.h>
|
||||||
|
|
||||||
|
#include "wmi-helpers.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_dev_evaluate_int() - Helper function for calling WMI methods that
|
||||||
|
* return an integer.
|
||||||
|
* @wdev: Pointer to the WMI device to be called.
|
||||||
|
* @instance: Instance of the called method.
|
||||||
|
* @method_id: WMI Method ID for the method to be called.
|
||||||
|
* @buf: Buffer of all arguments for the given method_id.
|
||||||
|
* @size: Length of the buffer.
|
||||||
|
* @retval: Pointer for the return value to be assigned.
|
||||||
|
*
|
||||||
|
* Calls wmidev_evaluate_method for Lenovo WMI devices that return an ACPI
|
||||||
|
* integer. Validates the return value type and assigns the value to the
|
||||||
|
* retval pointer.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or an error code.
|
||||||
|
*/
|
||||||
|
int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
|
||||||
|
unsigned char *buf, size_t size, u32 *retval)
|
||||||
|
{
|
||||||
|
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||||
|
union acpi_object *ret_obj __free(kfree) = NULL;
|
||||||
|
struct acpi_buffer input = { size, buf };
|
||||||
|
acpi_status status;
|
||||||
|
|
||||||
|
status = wmidev_evaluate_method(wdev, instance, method_id, &input,
|
||||||
|
&output);
|
||||||
|
if (ACPI_FAILURE(status))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if (retval) {
|
||||||
|
ret_obj = output.pointer;
|
||||||
|
if (!ret_obj)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
if (ret_obj->type != ACPI_TYPE_INTEGER)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
*retval = (u32)ret_obj->integer.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
EXPORT_SYMBOL_NS_GPL(lwmi_dev_evaluate_int, "LENOVO_WMI_HELPERS");
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("Lenovo WMI Helpers Driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
/* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */
|
||||||
|
|
||||||
|
#ifndef _LENOVO_WMI_HELPERS_H_
|
||||||
|
#define _LENOVO_WMI_HELPERS_H_
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
struct wmi_device;
|
||||||
|
|
||||||
|
struct wmi_method_args_32 {
|
||||||
|
u32 arg0;
|
||||||
|
u32 arg1;
|
||||||
|
};
|
||||||
|
|
||||||
|
int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
|
||||||
|
unsigned char *buf, size_t size, u32 *retval);
|
||||||
|
|
||||||
|
#endif /* !_LENOVO_WMI_HELPERS_H_ */
|
||||||
|
|
@ -0,0 +1,665 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Lenovo Other Mode WMI interface driver.
|
||||||
|
*
|
||||||
|
* This driver uses the fw_attributes class to expose the various WMI functions
|
||||||
|
* provided by the "Other Mode" WMI interface. This enables CPU and GPU power
|
||||||
|
* limit as well as various other attributes for devices that fall under the
|
||||||
|
* "Gaming Series" of Lenovo laptop devices. Each attribute exposed by the
|
||||||
|
* "Other Mode" interface has a corresponding Capability Data struct that
|
||||||
|
* allows the driver to probe details about the attribute such as if it is
|
||||||
|
* supported by the hardware, the default_value, max_value, min_value, and step
|
||||||
|
* increment.
|
||||||
|
*
|
||||||
|
* These attributes typically don't fit anywhere else in the sysfs and are set
|
||||||
|
* in Windows using one of Lenovo's multiple user applications.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/bitfield.h>
|
||||||
|
#include <linux/cleanup.h>
|
||||||
|
#include <linux/component.h>
|
||||||
|
#include <linux/container_of.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/gfp_types.h>
|
||||||
|
#include <linux/idr.h>
|
||||||
|
#include <linux/kdev_t.h>
|
||||||
|
#include <linux/kobject.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/notifier.h>
|
||||||
|
#include <linux/platform_profile.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/wmi.h>
|
||||||
|
|
||||||
|
#include "wmi-capdata01.h"
|
||||||
|
#include "wmi-events.h"
|
||||||
|
#include "wmi-gamezone.h"
|
||||||
|
#include "wmi-helpers.h"
|
||||||
|
#include "wmi-other.h"
|
||||||
|
#include "../firmware_attributes_class.h"
|
||||||
|
|
||||||
|
#define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B"
|
||||||
|
|
||||||
|
#define LWMI_DEVICE_ID_CPU 0x01
|
||||||
|
|
||||||
|
#define LWMI_FEATURE_ID_CPU_SPPT 0x01
|
||||||
|
#define LWMI_FEATURE_ID_CPU_SPL 0x02
|
||||||
|
#define LWMI_FEATURE_ID_CPU_FPPT 0x03
|
||||||
|
|
||||||
|
#define LWMI_TYPE_ID_NONE 0x00
|
||||||
|
|
||||||
|
#define LWMI_FEATURE_VALUE_GET 17
|
||||||
|
#define LWMI_FEATURE_VALUE_SET 18
|
||||||
|
|
||||||
|
#define LWMI_ATTR_DEV_ID_MASK GENMASK(31, 24)
|
||||||
|
#define LWMI_ATTR_FEAT_ID_MASK GENMASK(23, 16)
|
||||||
|
#define LWMI_ATTR_MODE_ID_MASK GENMASK(15, 8)
|
||||||
|
#define LWMI_ATTR_TYPE_ID_MASK GENMASK(7, 0)
|
||||||
|
|
||||||
|
#define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other"
|
||||||
|
|
||||||
|
static BLOCKING_NOTIFIER_HEAD(om_chain_head);
|
||||||
|
static DEFINE_IDA(lwmi_om_ida);
|
||||||
|
|
||||||
|
enum attribute_property {
|
||||||
|
DEFAULT_VAL,
|
||||||
|
MAX_VAL,
|
||||||
|
MIN_VAL,
|
||||||
|
STEP_VAL,
|
||||||
|
SUPPORTED,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lwmi_om_priv {
|
||||||
|
struct component_master_ops *ops;
|
||||||
|
struct cd01_list *cd01_list; /* only valid after capdata01 bind */
|
||||||
|
struct device *fw_attr_dev;
|
||||||
|
struct kset *fw_attr_kset;
|
||||||
|
struct notifier_block nb;
|
||||||
|
struct wmi_device *wdev;
|
||||||
|
int ida_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tunable_attr_01 {
|
||||||
|
struct capdata01 *capdata;
|
||||||
|
struct device *dev;
|
||||||
|
u32 feature_id;
|
||||||
|
u32 device_id;
|
||||||
|
u32 type_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct tunable_attr_01 ppt_pl1_spl = {
|
||||||
|
.device_id = LWMI_DEVICE_ID_CPU,
|
||||||
|
.feature_id = LWMI_FEATURE_ID_CPU_SPL,
|
||||||
|
.type_id = LWMI_TYPE_ID_NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct tunable_attr_01 ppt_pl2_sppt = {
|
||||||
|
.device_id = LWMI_DEVICE_ID_CPU,
|
||||||
|
.feature_id = LWMI_FEATURE_ID_CPU_SPPT,
|
||||||
|
.type_id = LWMI_TYPE_ID_NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct tunable_attr_01 ppt_pl3_fppt = {
|
||||||
|
.device_id = LWMI_DEVICE_ID_CPU,
|
||||||
|
.feature_id = LWMI_FEATURE_ID_CPU_FPPT,
|
||||||
|
.type_id = LWMI_TYPE_ID_NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct capdata01_attr_group {
|
||||||
|
const struct attribute_group *attr_group;
|
||||||
|
struct tunable_attr_01 *tunable_attr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_om_register_notifier() - Add a notifier to the blocking notifier chain
|
||||||
|
* @nb: The notifier_block struct to register
|
||||||
|
*
|
||||||
|
* Call blocking_notifier_chain_register to register the notifier block to the
|
||||||
|
* lenovo-wmi-other driver notifier chain.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, %-EEXIST on error.
|
||||||
|
*/
|
||||||
|
int lwmi_om_register_notifier(struct notifier_block *nb)
|
||||||
|
{
|
||||||
|
return blocking_notifier_chain_register(&om_chain_head, nb);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS_GPL(lwmi_om_register_notifier, "LENOVO_WMI_OTHER");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_om_unregister_notifier() - Remove a notifier from the blocking notifier
|
||||||
|
* chain.
|
||||||
|
* @nb: The notifier_block struct to register
|
||||||
|
*
|
||||||
|
* Call blocking_notifier_chain_unregister to unregister the notifier block from the
|
||||||
|
* lenovo-wmi-other driver notifier chain.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, %-ENOENT on error.
|
||||||
|
*/
|
||||||
|
int lwmi_om_unregister_notifier(struct notifier_block *nb)
|
||||||
|
{
|
||||||
|
return blocking_notifier_chain_unregister(&om_chain_head, nb);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS_GPL(lwmi_om_unregister_notifier, "LENOVO_WMI_OTHER");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* devm_lwmi_om_unregister_notifier() - Remove a notifier from the blocking
|
||||||
|
* notifier chain.
|
||||||
|
* @data: Void pointer to the notifier_block struct to register.
|
||||||
|
*
|
||||||
|
* Call lwmi_om_unregister_notifier to unregister the notifier block from the
|
||||||
|
* lenovo-wmi-other driver notifier chain.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, %-ENOENT on error.
|
||||||
|
*/
|
||||||
|
static void devm_lwmi_om_unregister_notifier(void *data)
|
||||||
|
{
|
||||||
|
struct notifier_block *nb = data;
|
||||||
|
|
||||||
|
lwmi_om_unregister_notifier(nb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* devm_lwmi_om_register_notifier() - Add a notifier to the blocking notifier
|
||||||
|
* chain.
|
||||||
|
* @dev: The parent device of the notifier_block struct.
|
||||||
|
* @nb: The notifier_block struct to register
|
||||||
|
*
|
||||||
|
* Call lwmi_om_register_notifier to register the notifier block to the
|
||||||
|
* lenovo-wmi-other driver notifier chain. Then add devm_lwmi_om_unregister_notifier
|
||||||
|
* as a device managed action to automatically unregister the notifier block
|
||||||
|
* upon parent device removal.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or an error code.
|
||||||
|
*/
|
||||||
|
int devm_lwmi_om_register_notifier(struct device *dev,
|
||||||
|
struct notifier_block *nb)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = lwmi_om_register_notifier(nb);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return devm_add_action_or_reset(dev, devm_lwmi_om_unregister_notifier,
|
||||||
|
nb);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS_GPL(devm_lwmi_om_register_notifier, "LENOVO_WMI_OTHER");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_om_notifier_call() - Call functions for the notifier call chain.
|
||||||
|
* @mode: Pointer to a thermal mode enum to retrieve the data from.
|
||||||
|
*
|
||||||
|
* Call blocking_notifier_call_chain to retrieve the thermal mode from the
|
||||||
|
* lenovo-wmi-gamezone driver.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or an error code.
|
||||||
|
*/
|
||||||
|
static int lwmi_om_notifier_call(enum thermal_mode *mode)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = blocking_notifier_call_chain(&om_chain_head,
|
||||||
|
LWMI_GZ_GET_THERMAL_MODE, &mode);
|
||||||
|
if ((ret & ~NOTIFY_STOP_MASK) != NOTIFY_OK)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attribute Methods */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* int_type_show() - Emit the data type for an integer attribute
|
||||||
|
* @kobj: Pointer to the driver object.
|
||||||
|
* @kattr: Pointer to the attribute calling this function.
|
||||||
|
* @buf: The buffer to write to.
|
||||||
|
*
|
||||||
|
* Return: Number of characters written to buf.
|
||||||
|
*/
|
||||||
|
static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *kattr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
return sysfs_emit(buf, "integer\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* attr_capdata01_show() - Get the value of the specified attribute property
|
||||||
|
*
|
||||||
|
* @kobj: Pointer to the driver object.
|
||||||
|
* @kattr: Pointer to the attribute calling this function.
|
||||||
|
* @buf: The buffer to write to.
|
||||||
|
* @tunable_attr: The attribute to be read.
|
||||||
|
* @prop: The property of this attribute to be read.
|
||||||
|
*
|
||||||
|
* Retrieves the given property from the capability data 01 struct for the
|
||||||
|
* specified attribute's "custom" thermal mode. This function is intended
|
||||||
|
* to be generic so it can be called from any integer attributes "_show"
|
||||||
|
* function.
|
||||||
|
*
|
||||||
|
* If the WMI is success the sysfs attribute is notified.
|
||||||
|
*
|
||||||
|
* Return: Either number of characters written to buf, or an error code.
|
||||||
|
*/
|
||||||
|
static ssize_t attr_capdata01_show(struct kobject *kobj,
|
||||||
|
struct kobj_attribute *kattr, char *buf,
|
||||||
|
struct tunable_attr_01 *tunable_attr,
|
||||||
|
enum attribute_property prop)
|
||||||
|
{
|
||||||
|
struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
|
||||||
|
struct capdata01 capdata;
|
||||||
|
u32 attribute_id;
|
||||||
|
int value, ret;
|
||||||
|
|
||||||
|
attribute_id =
|
||||||
|
FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
|
||||||
|
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
|
||||||
|
FIELD_PREP(LWMI_ATTR_MODE_ID_MASK,
|
||||||
|
LWMI_GZ_THERMAL_MODE_CUSTOM) |
|
||||||
|
FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
|
||||||
|
|
||||||
|
ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
switch (prop) {
|
||||||
|
case DEFAULT_VAL:
|
||||||
|
value = capdata.default_value;
|
||||||
|
break;
|
||||||
|
case MAX_VAL:
|
||||||
|
value = capdata.max_value;
|
||||||
|
break;
|
||||||
|
case MIN_VAL:
|
||||||
|
value = capdata.min_value;
|
||||||
|
break;
|
||||||
|
case STEP_VAL:
|
||||||
|
value = capdata.step;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sysfs_emit(buf, "%d\n", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* attr_current_value_store() - Set the current value of the given attribute
|
||||||
|
* @kobj: Pointer to the driver object.
|
||||||
|
* @kattr: Pointer to the attribute calling this function.
|
||||||
|
* @buf: The buffer to read from, this is parsed to `int` type.
|
||||||
|
* @count: Required by sysfs attribute macros, pass in from the callee attr.
|
||||||
|
* @tunable_attr: The attribute to be stored.
|
||||||
|
*
|
||||||
|
* Sets the value of the given attribute when operating under the "custom"
|
||||||
|
* smartfan profile. The current smartfan profile is retrieved from the
|
||||||
|
* lenovo-wmi-gamezone driver and error is returned if the result is not
|
||||||
|
* "custom". This function is intended to be generic so it can be called from
|
||||||
|
* any integer attribute's "_store" function. The integer to be sent to the WMI
|
||||||
|
* method is range checked and an error code is returned if out of range.
|
||||||
|
*
|
||||||
|
* If the value is valid and WMI is success, then the sysfs attribute is
|
||||||
|
* notified.
|
||||||
|
*
|
||||||
|
* Return: Either count, or an error code.
|
||||||
|
*/
|
||||||
|
static ssize_t attr_current_value_store(struct kobject *kobj,
|
||||||
|
struct kobj_attribute *kattr,
|
||||||
|
const char *buf, size_t count,
|
||||||
|
struct tunable_attr_01 *tunable_attr)
|
||||||
|
{
|
||||||
|
struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
|
||||||
|
struct wmi_method_args_32 args;
|
||||||
|
struct capdata01 capdata;
|
||||||
|
enum thermal_mode mode;
|
||||||
|
u32 attribute_id;
|
||||||
|
u32 value;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = lwmi_om_notifier_call(&mode);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
attribute_id =
|
||||||
|
FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
|
||||||
|
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
|
||||||
|
FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
|
||||||
|
FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
|
||||||
|
|
||||||
|
ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = kstrtouint(buf, 10, &value);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (value < capdata.min_value || value > capdata.max_value)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
args.arg0 = attribute_id;
|
||||||
|
args.arg1 = value;
|
||||||
|
|
||||||
|
ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
|
||||||
|
(unsigned char *)&args, sizeof(args), NULL);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* attr_current_value_show() - Get the current value of the given attribute
|
||||||
|
* @kobj: Pointer to the driver object.
|
||||||
|
* @kattr: Pointer to the attribute calling this function.
|
||||||
|
* @buf: The buffer to write to.
|
||||||
|
* @tunable_attr: The attribute to be read.
|
||||||
|
*
|
||||||
|
* Retrieves the value of the given attribute for the current smartfan profile.
|
||||||
|
* The current smartfan profile is retrieved from the lenovo-wmi-gamezone driver.
|
||||||
|
* This function is intended to be generic so it can be called from any integer
|
||||||
|
* attribute's "_show" function.
|
||||||
|
*
|
||||||
|
* If the WMI is success the sysfs attribute is notified.
|
||||||
|
*
|
||||||
|
* Return: Either number of characters written to buf, or an error code.
|
||||||
|
*/
|
||||||
|
static ssize_t attr_current_value_show(struct kobject *kobj,
|
||||||
|
struct kobj_attribute *kattr, char *buf,
|
||||||
|
struct tunable_attr_01 *tunable_attr)
|
||||||
|
{
|
||||||
|
struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
|
||||||
|
struct wmi_method_args_32 args;
|
||||||
|
enum thermal_mode mode;
|
||||||
|
u32 attribute_id;
|
||||||
|
int retval;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = lwmi_om_notifier_call(&mode);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
attribute_id =
|
||||||
|
FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
|
||||||
|
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
|
||||||
|
FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
|
||||||
|
FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
|
||||||
|
|
||||||
|
args.arg0 = attribute_id;
|
||||||
|
|
||||||
|
ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
|
||||||
|
(unsigned char *)&args, sizeof(args),
|
||||||
|
&retval);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return sysfs_emit(buf, "%d\n", retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lenovo WMI Other Mode Attribute macros */
|
||||||
|
#define __LWMI_ATTR_RO(_func, _name) \
|
||||||
|
{ \
|
||||||
|
.attr = { .name = __stringify(_name), .mode = 0444 }, \
|
||||||
|
.show = _func##_##_name##_show, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define __LWMI_ATTR_RO_AS(_name, _show) \
|
||||||
|
{ \
|
||||||
|
.attr = { .name = __stringify(_name), .mode = 0444 }, \
|
||||||
|
.show = _show, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define __LWMI_ATTR_RW(_func, _name) \
|
||||||
|
__ATTR(_name, 0644, _func##_##_name##_show, _func##_##_name##_store)
|
||||||
|
|
||||||
|
/* Shows a formatted static variable */
|
||||||
|
#define __LWMI_ATTR_SHOW_FMT(_prop, _attrname, _fmt, _val) \
|
||||||
|
static ssize_t _attrname##_##_prop##_show( \
|
||||||
|
struct kobject *kobj, struct kobj_attribute *kattr, char *buf) \
|
||||||
|
{ \
|
||||||
|
return sysfs_emit(buf, _fmt, _val); \
|
||||||
|
} \
|
||||||
|
static struct kobj_attribute attr_##_attrname##_##_prop = \
|
||||||
|
__LWMI_ATTR_RO(_attrname, _prop)
|
||||||
|
|
||||||
|
/* Attribute current value read/write */
|
||||||
|
#define __LWMI_TUNABLE_CURRENT_VALUE_CAP01(_attrname) \
|
||||||
|
static ssize_t _attrname##_current_value_store( \
|
||||||
|
struct kobject *kobj, struct kobj_attribute *kattr, \
|
||||||
|
const char *buf, size_t count) \
|
||||||
|
{ \
|
||||||
|
return attr_current_value_store(kobj, kattr, buf, count, \
|
||||||
|
&_attrname); \
|
||||||
|
} \
|
||||||
|
static ssize_t _attrname##_current_value_show( \
|
||||||
|
struct kobject *kobj, struct kobj_attribute *kattr, char *buf) \
|
||||||
|
{ \
|
||||||
|
return attr_current_value_show(kobj, kattr, buf, &_attrname); \
|
||||||
|
} \
|
||||||
|
static struct kobj_attribute attr_##_attrname##_current_value = \
|
||||||
|
__LWMI_ATTR_RW(_attrname, current_value)
|
||||||
|
|
||||||
|
/* Attribute property read only */
|
||||||
|
#define __LWMI_TUNABLE_RO_CAP01(_prop, _attrname, _prop_type) \
|
||||||
|
static ssize_t _attrname##_##_prop##_show( \
|
||||||
|
struct kobject *kobj, struct kobj_attribute *kattr, char *buf) \
|
||||||
|
{ \
|
||||||
|
return attr_capdata01_show(kobj, kattr, buf, &_attrname, \
|
||||||
|
_prop_type); \
|
||||||
|
} \
|
||||||
|
static struct kobj_attribute attr_##_attrname##_##_prop = \
|
||||||
|
__LWMI_ATTR_RO(_attrname, _prop)
|
||||||
|
|
||||||
|
#define LWMI_ATTR_GROUP_TUNABLE_CAP01(_attrname, _fsname, _dispname) \
|
||||||
|
__LWMI_TUNABLE_CURRENT_VALUE_CAP01(_attrname); \
|
||||||
|
__LWMI_TUNABLE_RO_CAP01(default_value, _attrname, DEFAULT_VAL); \
|
||||||
|
__LWMI_ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
|
||||||
|
__LWMI_TUNABLE_RO_CAP01(max_value, _attrname, MAX_VAL); \
|
||||||
|
__LWMI_TUNABLE_RO_CAP01(min_value, _attrname, MIN_VAL); \
|
||||||
|
__LWMI_TUNABLE_RO_CAP01(scalar_increment, _attrname, STEP_VAL); \
|
||||||
|
static struct kobj_attribute attr_##_attrname##_type = \
|
||||||
|
__LWMI_ATTR_RO_AS(type, int_type_show); \
|
||||||
|
static struct attribute *_attrname##_attrs[] = { \
|
||||||
|
&attr_##_attrname##_current_value.attr, \
|
||||||
|
&attr_##_attrname##_default_value.attr, \
|
||||||
|
&attr_##_attrname##_display_name.attr, \
|
||||||
|
&attr_##_attrname##_max_value.attr, \
|
||||||
|
&attr_##_attrname##_min_value.attr, \
|
||||||
|
&attr_##_attrname##_scalar_increment.attr, \
|
||||||
|
&attr_##_attrname##_type.attr, \
|
||||||
|
NULL, \
|
||||||
|
}; \
|
||||||
|
static const struct attribute_group _attrname##_attr_group = { \
|
||||||
|
.name = _fsname, .attrs = _attrname##_attrs \
|
||||||
|
}
|
||||||
|
|
||||||
|
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl, "ppt_pl1_spl",
|
||||||
|
"Set the CPU sustained power limit");
|
||||||
|
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt, "ppt_pl2_sppt",
|
||||||
|
"Set the CPU slow package power tracking limit");
|
||||||
|
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt, "ppt_pl3_fppt",
|
||||||
|
"Set the CPU fast package power tracking limit");
|
||||||
|
|
||||||
|
static struct capdata01_attr_group cd01_attr_groups[] = {
|
||||||
|
{ &ppt_pl1_spl_attr_group, &ppt_pl1_spl },
|
||||||
|
{ &ppt_pl2_sppt_attr_group, &ppt_pl2_sppt },
|
||||||
|
{ &ppt_pl3_fppt_attr_group, &ppt_pl3_fppt },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_om_fw_attr_add() - Register all firmware_attributes_class members
|
||||||
|
* @priv: The Other Mode driver data.
|
||||||
|
*
|
||||||
|
* Return: Either 0, or an error code.
|
||||||
|
*/
|
||||||
|
static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
priv->ida_id = ida_alloc(&lwmi_om_ida, GFP_KERNEL);
|
||||||
|
if (priv->ida_id < 0)
|
||||||
|
return priv->ida_id;
|
||||||
|
|
||||||
|
priv->fw_attr_dev = device_create(&firmware_attributes_class, NULL,
|
||||||
|
MKDEV(0, 0), NULL, "%s-%u",
|
||||||
|
LWMI_OM_FW_ATTR_BASE_PATH,
|
||||||
|
priv->ida_id);
|
||||||
|
if (IS_ERR(priv->fw_attr_dev)) {
|
||||||
|
err = PTR_ERR(priv->fw_attr_dev);
|
||||||
|
goto err_free_ida;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->fw_attr_kset = kset_create_and_add("attributes", NULL,
|
||||||
|
&priv->fw_attr_dev->kobj);
|
||||||
|
if (!priv->fw_attr_kset) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto err_destroy_classdev;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) {
|
||||||
|
err = sysfs_create_group(&priv->fw_attr_kset->kobj,
|
||||||
|
cd01_attr_groups[i].attr_group);
|
||||||
|
if (err)
|
||||||
|
goto err_remove_groups;
|
||||||
|
|
||||||
|
cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_remove_groups:
|
||||||
|
while (i--)
|
||||||
|
sysfs_remove_group(&priv->fw_attr_kset->kobj,
|
||||||
|
cd01_attr_groups[i].attr_group);
|
||||||
|
|
||||||
|
kset_unregister(priv->fw_attr_kset);
|
||||||
|
|
||||||
|
err_destroy_classdev:
|
||||||
|
device_unregister(priv->fw_attr_dev);
|
||||||
|
|
||||||
|
err_free_ida:
|
||||||
|
ida_free(&lwmi_om_ida, priv->ida_id);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_om_fw_attr_remove() - Unregister all capability data attribute groups
|
||||||
|
* @priv: the lenovo-wmi-other driver data.
|
||||||
|
*/
|
||||||
|
static void lwmi_om_fw_attr_remove(struct lwmi_om_priv *priv)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++)
|
||||||
|
sysfs_remove_group(&priv->fw_attr_kset->kobj,
|
||||||
|
cd01_attr_groups[i].attr_group);
|
||||||
|
|
||||||
|
kset_unregister(priv->fw_attr_kset);
|
||||||
|
device_unregister(priv->fw_attr_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_om_master_bind() - Bind all components of the other mode driver
|
||||||
|
* @dev: The lenovo-wmi-other driver basic device.
|
||||||
|
*
|
||||||
|
* Call component_bind_all to bind the lenovo-wmi-capdata01 driver to the
|
||||||
|
* lenovo-wmi-other master driver. On success, assign the capability data 01
|
||||||
|
* list pointer to the driver data struct for later access. This pointer
|
||||||
|
* is only valid while the capdata01 interface exists. Finally, register all
|
||||||
|
* firmware attribute groups.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or an error code.
|
||||||
|
*/
|
||||||
|
static int lwmi_om_master_bind(struct device *dev)
|
||||||
|
{
|
||||||
|
struct lwmi_om_priv *priv = dev_get_drvdata(dev);
|
||||||
|
struct cd01_list *tmp_list;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = component_bind_all(dev, &tmp_list);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
priv->cd01_list = tmp_list;
|
||||||
|
if (!priv->cd01_list)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
return lwmi_om_fw_attr_add(priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lwmi_om_master_unbind() - Unbind all components of the other mode driver
|
||||||
|
* @dev: The lenovo-wmi-other driver basic device
|
||||||
|
*
|
||||||
|
* Unregister all capability data attribute groups. Then call
|
||||||
|
* component_unbind_all to unbind the lenovo-wmi-capdata01 driver from the
|
||||||
|
* lenovo-wmi-other master driver. Finally, free the IDA for this device.
|
||||||
|
*/
|
||||||
|
static void lwmi_om_master_unbind(struct device *dev)
|
||||||
|
{
|
||||||
|
struct lwmi_om_priv *priv = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
lwmi_om_fw_attr_remove(priv);
|
||||||
|
component_unbind_all(dev, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct component_master_ops lwmi_om_master_ops = {
|
||||||
|
.bind = lwmi_om_master_bind,
|
||||||
|
.unbind = lwmi_om_master_unbind,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int lwmi_other_probe(struct wmi_device *wdev, const void *context)
|
||||||
|
{
|
||||||
|
struct component_match *master_match = NULL;
|
||||||
|
struct lwmi_om_priv *priv;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priv->wdev = wdev;
|
||||||
|
dev_set_drvdata(&wdev->dev, priv);
|
||||||
|
|
||||||
|
component_match_add(&wdev->dev, &master_match, lwmi_cd01_match, NULL);
|
||||||
|
if (IS_ERR(master_match))
|
||||||
|
return PTR_ERR(master_match);
|
||||||
|
|
||||||
|
return component_master_add_with_match(&wdev->dev, &lwmi_om_master_ops,
|
||||||
|
master_match);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lwmi_other_remove(struct wmi_device *wdev)
|
||||||
|
{
|
||||||
|
struct lwmi_om_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||||
|
|
||||||
|
component_master_del(&wdev->dev, &lwmi_om_master_ops);
|
||||||
|
ida_free(&lwmi_om_ida, priv->ida_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wmi_device_id lwmi_other_id_table[] = {
|
||||||
|
{ LENOVO_OTHER_MODE_GUID, NULL },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct wmi_driver lwmi_other_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "lenovo_wmi_other",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
|
},
|
||||||
|
.id_table = lwmi_other_id_table,
|
||||||
|
.probe = lwmi_other_probe,
|
||||||
|
.remove = lwmi_other_remove,
|
||||||
|
.no_singleton = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_wmi_driver(lwmi_other_driver);
|
||||||
|
|
||||||
|
MODULE_IMPORT_NS("LENOVO_WMI_CD01");
|
||||||
|
MODULE_IMPORT_NS("LENOVO_WMI_HELPERS");
|
||||||
|
MODULE_DEVICE_TABLE(wmi, lwmi_other_id_table);
|
||||||
|
MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("Lenovo Other Mode WMI Driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
/* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */
|
||||||
|
|
||||||
|
#ifndef _LENOVO_WMI_OTHER_H_
|
||||||
|
#define _LENOVO_WMI_OTHER_H_
|
||||||
|
|
||||||
|
struct device;
|
||||||
|
struct notifier_block;
|
||||||
|
|
||||||
|
int lwmi_om_register_notifier(struct notifier_block *nb);
|
||||||
|
int lwmi_om_unregister_notifier(struct notifier_block *nb);
|
||||||
|
int devm_lwmi_om_register_notifier(struct device *dev,
|
||||||
|
struct notifier_block *nb);
|
||||||
|
|
||||||
|
#endif /* !_LENOVO_WMI_OTHER_H_ */
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
#include <linux/time.h>
|
#include <linux/time.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include "serdev_helpers.h"
|
#include "../serdev_helpers.h"
|
||||||
|
|
||||||
#define YT2_1380_FC_PDEV_NAME "lenovo-yoga-tab2-pro-1380-fastcharger"
|
#define YT2_1380_FC_PDEV_NAME "lenovo-yoga-tab2-pro-1380-fastcharger"
|
||||||
#define YT2_1380_FC_SERDEV_CTRL "serial0"
|
#define YT2_1380_FC_SERDEV_CTRL "serial0"
|
||||||
|
|
@ -240,30 +240,25 @@ static int yt2_1380_fc_pdev_probe(struct platform_device *pdev)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Register pinctrl mappings for setting the UART3 pins mode */
|
/* Register pinctrl mappings for setting the UART3 pins mode */
|
||||||
ret = pinctrl_register_mappings(yt2_1380_fc_pinctrl_map,
|
ret = devm_pinctrl_register_mappings(&pdev->dev, yt2_1380_fc_pinctrl_map,
|
||||||
ARRAY_SIZE(yt2_1380_fc_pinctrl_map));
|
ARRAY_SIZE(yt2_1380_fc_pinctrl_map));
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* And create the serdev to talk to the charger over the UART3 pins */
|
/* And create the serdev to talk to the charger over the UART3 pins */
|
||||||
ctrl_dev = get_serdev_controller("PNP0501", "1", 0, YT2_1380_FC_SERDEV_CTRL);
|
ctrl_dev = get_serdev_controller("PNP0501", "1", 0, YT2_1380_FC_SERDEV_CTRL);
|
||||||
if (IS_ERR(ctrl_dev)) {
|
if (IS_ERR(ctrl_dev))
|
||||||
ret = PTR_ERR(ctrl_dev);
|
return PTR_ERR(ctrl_dev);
|
||||||
goto out_pinctrl_unregister_mappings;
|
|
||||||
}
|
|
||||||
|
|
||||||
serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev));
|
serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev));
|
||||||
put_device(ctrl_dev);
|
put_device(ctrl_dev);
|
||||||
if (!serdev) {
|
if (!serdev)
|
||||||
ret = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto out_pinctrl_unregister_mappings;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = serdev_device_add(serdev);
|
ret = serdev_device_add(serdev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err_probe(&pdev->dev, ret, "adding serdev\n");
|
|
||||||
serdev_device_put(serdev);
|
serdev_device_put(serdev);
|
||||||
goto out_pinctrl_unregister_mappings;
|
return dev_err_probe(&pdev->dev, ret, "adding serdev\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -273,20 +268,15 @@ static int yt2_1380_fc_pdev_probe(struct platform_device *pdev)
|
||||||
ret = device_driver_attach(&yt2_1380_fc_serdev_driver.driver, &serdev->dev);
|
ret = device_driver_attach(&yt2_1380_fc_serdev_driver.driver, &serdev->dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
/* device_driver_attach() maps EPROBE_DEFER to EAGAIN, map it back */
|
/* device_driver_attach() maps EPROBE_DEFER to EAGAIN, map it back */
|
||||||
ret = (ret == -EAGAIN) ? -EPROBE_DEFER : ret;
|
serdev_device_remove(serdev);
|
||||||
dev_err_probe(&pdev->dev, ret, "attaching serdev driver\n");
|
return dev_err_probe(&pdev->dev,
|
||||||
goto out_serdev_device_remove;
|
(ret == -EAGAIN) ? -EPROBE_DEFER : ret,
|
||||||
|
"attaching serdev driver\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* So that yt2_1380_fc_pdev_remove() can remove the serdev */
|
/* So that yt2_1380_fc_pdev_remove() can remove the serdev */
|
||||||
platform_set_drvdata(pdev, serdev);
|
platform_set_drvdata(pdev, serdev);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_serdev_device_remove:
|
|
||||||
serdev_device_remove(serdev);
|
|
||||||
out_pinctrl_unregister_mappings:
|
|
||||||
pinctrl_unregister_mappings(yt2_1380_fc_pinctrl_map);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void yt2_1380_fc_pdev_remove(struct platform_device *pdev)
|
static void yt2_1380_fc_pdev_remove(struct platform_device *pdev)
|
||||||
|
|
@ -294,7 +284,6 @@ static void yt2_1380_fc_pdev_remove(struct platform_device *pdev)
|
||||||
struct serdev_device *serdev = platform_get_drvdata(pdev);
|
struct serdev_device *serdev = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
serdev_device_remove(serdev);
|
serdev_device_remove(serdev);
|
||||||
pinctrl_unregister_mappings(yt2_1380_fc_pinctrl_map);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver yt2_1380_fc_pdev_driver = {
|
static struct platform_driver yt2_1380_fc_pdev_driver = {
|
||||||
|
|
@ -58,7 +58,8 @@ enum oxp_board {
|
||||||
oxp_mini_amd_a07,
|
oxp_mini_amd_a07,
|
||||||
oxp_mini_amd_pro,
|
oxp_mini_amd_pro,
|
||||||
oxp_x1,
|
oxp_x1,
|
||||||
oxp_g1,
|
oxp_g1_i,
|
||||||
|
oxp_g1_a,
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum oxp_board board;
|
static enum oxp_board board;
|
||||||
|
|
@ -247,14 +248,14 @@ static const struct dmi_system_id dmi_table[] = {
|
||||||
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
|
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
|
||||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER G1 A"),
|
DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER G1 A"),
|
||||||
},
|
},
|
||||||
.driver_data = (void *)oxp_g1,
|
.driver_data = (void *)oxp_g1_a,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.matches = {
|
.matches = {
|
||||||
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
|
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
|
||||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER G1 i"),
|
DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER G1 i"),
|
||||||
},
|
},
|
||||||
.driver_data = (void *)oxp_g1,
|
.driver_data = (void *)oxp_g1_i,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.matches = {
|
.matches = {
|
||||||
|
|
@ -291,6 +292,13 @@ static const struct dmi_system_id dmi_table[] = {
|
||||||
},
|
},
|
||||||
.driver_data = (void *)oxp_x1,
|
.driver_data = (void *)oxp_x1,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
|
||||||
|
DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1Mini Pro"),
|
||||||
|
},
|
||||||
|
.driver_data = (void *)oxp_x1,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.matches = {
|
.matches = {
|
||||||
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
|
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
|
||||||
|
|
@ -352,7 +360,8 @@ static umode_t tt_toggle_is_visible(struct kobject *kobj,
|
||||||
case oxp_mini_amd_a07:
|
case oxp_mini_amd_a07:
|
||||||
case oxp_mini_amd_pro:
|
case oxp_mini_amd_pro:
|
||||||
case oxp_x1:
|
case oxp_x1:
|
||||||
case oxp_g1:
|
case oxp_g1_i:
|
||||||
|
case oxp_g1_a:
|
||||||
return attr->mode;
|
return attr->mode;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
@ -381,12 +390,13 @@ static ssize_t tt_toggle_store(struct device *dev,
|
||||||
case aok_zoe_a1:
|
case aok_zoe_a1:
|
||||||
case oxp_fly:
|
case oxp_fly:
|
||||||
case oxp_mini_amd_pro:
|
case oxp_mini_amd_pro:
|
||||||
|
case oxp_g1_a:
|
||||||
reg = OXP_TURBO_SWITCH_REG;
|
reg = OXP_TURBO_SWITCH_REG;
|
||||||
mask = OXP_TURBO_TAKE_VAL;
|
mask = OXP_TURBO_TAKE_VAL;
|
||||||
break;
|
break;
|
||||||
case oxp_2:
|
case oxp_2:
|
||||||
case oxp_x1:
|
case oxp_x1:
|
||||||
case oxp_g1:
|
case oxp_g1_i:
|
||||||
reg = OXP_2_TURBO_SWITCH_REG;
|
reg = OXP_2_TURBO_SWITCH_REG;
|
||||||
mask = OXP_TURBO_TAKE_VAL;
|
mask = OXP_TURBO_TAKE_VAL;
|
||||||
break;
|
break;
|
||||||
|
|
@ -426,12 +436,13 @@ static ssize_t tt_toggle_show(struct device *dev,
|
||||||
case aok_zoe_a1:
|
case aok_zoe_a1:
|
||||||
case oxp_fly:
|
case oxp_fly:
|
||||||
case oxp_mini_amd_pro:
|
case oxp_mini_amd_pro:
|
||||||
|
case oxp_g1_a:
|
||||||
reg = OXP_TURBO_SWITCH_REG;
|
reg = OXP_TURBO_SWITCH_REG;
|
||||||
mask = OXP_TURBO_TAKE_VAL;
|
mask = OXP_TURBO_TAKE_VAL;
|
||||||
break;
|
break;
|
||||||
case oxp_2:
|
case oxp_2:
|
||||||
case oxp_x1:
|
case oxp_x1:
|
||||||
case oxp_g1:
|
case oxp_g1_i:
|
||||||
reg = OXP_2_TURBO_SWITCH_REG;
|
reg = OXP_2_TURBO_SWITCH_REG;
|
||||||
mask = OXP_TURBO_TAKE_VAL;
|
mask = OXP_TURBO_TAKE_VAL;
|
||||||
break;
|
break;
|
||||||
|
|
@ -520,7 +531,8 @@ static bool oxp_psy_ext_supported(void)
|
||||||
{
|
{
|
||||||
switch (board) {
|
switch (board) {
|
||||||
case oxp_x1:
|
case oxp_x1:
|
||||||
case oxp_g1:
|
case oxp_g1_i:
|
||||||
|
case oxp_g1_a:
|
||||||
case oxp_fly:
|
case oxp_fly:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
|
|
@ -659,7 +671,8 @@ static int oxp_pwm_enable(void)
|
||||||
case oxp_mini_amd_a07:
|
case oxp_mini_amd_a07:
|
||||||
case oxp_mini_amd_pro:
|
case oxp_mini_amd_pro:
|
||||||
case oxp_x1:
|
case oxp_x1:
|
||||||
case oxp_g1:
|
case oxp_g1_i:
|
||||||
|
case oxp_g1_a:
|
||||||
return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
|
return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
@ -686,7 +699,8 @@ static int oxp_pwm_disable(void)
|
||||||
case oxp_mini_amd_a07:
|
case oxp_mini_amd_a07:
|
||||||
case oxp_mini_amd_pro:
|
case oxp_mini_amd_pro:
|
||||||
case oxp_x1:
|
case oxp_x1:
|
||||||
case oxp_g1:
|
case oxp_g1_i:
|
||||||
|
case oxp_g1_a:
|
||||||
return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
|
return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
@ -713,7 +727,8 @@ static int oxp_pwm_read(long *val)
|
||||||
case oxp_mini_amd_a07:
|
case oxp_mini_amd_a07:
|
||||||
case oxp_mini_amd_pro:
|
case oxp_mini_amd_pro:
|
||||||
case oxp_x1:
|
case oxp_x1:
|
||||||
case oxp_g1:
|
case oxp_g1_i:
|
||||||
|
case oxp_g1_a:
|
||||||
return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
|
return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
|
||||||
default:
|
default:
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
@ -742,7 +757,7 @@ static int oxp_pwm_fan_speed(long *val)
|
||||||
return read_from_ec(ORANGEPI_SENSOR_FAN_REG, 2, val);
|
return read_from_ec(ORANGEPI_SENSOR_FAN_REG, 2, val);
|
||||||
case oxp_2:
|
case oxp_2:
|
||||||
case oxp_x1:
|
case oxp_x1:
|
||||||
case oxp_g1:
|
case oxp_g1_i:
|
||||||
return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val);
|
return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val);
|
||||||
case aok_zoe_a1:
|
case aok_zoe_a1:
|
||||||
case aya_neo_2:
|
case aya_neo_2:
|
||||||
|
|
@ -757,6 +772,7 @@ static int oxp_pwm_fan_speed(long *val)
|
||||||
case oxp_mini_amd:
|
case oxp_mini_amd:
|
||||||
case oxp_mini_amd_a07:
|
case oxp_mini_amd_a07:
|
||||||
case oxp_mini_amd_pro:
|
case oxp_mini_amd_pro:
|
||||||
|
case oxp_g1_a:
|
||||||
return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
|
return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
|
||||||
default:
|
default:
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
@ -776,7 +792,7 @@ static int oxp_pwm_input_write(long val)
|
||||||
return write_to_ec(ORANGEPI_SENSOR_PWM_REG, val);
|
return write_to_ec(ORANGEPI_SENSOR_PWM_REG, val);
|
||||||
case oxp_2:
|
case oxp_2:
|
||||||
case oxp_x1:
|
case oxp_x1:
|
||||||
case oxp_g1:
|
case oxp_g1_i:
|
||||||
/* scale to range [0-184] */
|
/* scale to range [0-184] */
|
||||||
val = (val * 184) / 255;
|
val = (val * 184) / 255;
|
||||||
return write_to_ec(OXP_SENSOR_PWM_REG, val);
|
return write_to_ec(OXP_SENSOR_PWM_REG, val);
|
||||||
|
|
@ -796,6 +812,7 @@ static int oxp_pwm_input_write(long val)
|
||||||
case aok_zoe_a1:
|
case aok_zoe_a1:
|
||||||
case oxp_fly:
|
case oxp_fly:
|
||||||
case oxp_mini_amd_pro:
|
case oxp_mini_amd_pro:
|
||||||
|
case oxp_g1_a:
|
||||||
return write_to_ec(OXP_SENSOR_PWM_REG, val);
|
return write_to_ec(OXP_SENSOR_PWM_REG, val);
|
||||||
default:
|
default:
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
@ -816,7 +833,7 @@ static int oxp_pwm_input_read(long *val)
|
||||||
break;
|
break;
|
||||||
case oxp_2:
|
case oxp_2:
|
||||||
case oxp_x1:
|
case oxp_x1:
|
||||||
case oxp_g1:
|
case oxp_g1_i:
|
||||||
ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
|
ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -842,6 +859,7 @@ static int oxp_pwm_input_read(long *val)
|
||||||
case aok_zoe_a1:
|
case aok_zoe_a1:
|
||||||
case oxp_fly:
|
case oxp_fly:
|
||||||
case oxp_mini_amd_pro:
|
case oxp_mini_amd_pro:
|
||||||
|
case oxp_g1_a:
|
||||||
default:
|
default:
|
||||||
ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
|
ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
#include <linux/dmi.h>
|
#include <linux/dmi.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/power_supply.h>
|
||||||
#include <linux/rfkill.h>
|
#include <linux/rfkill.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
|
|
@ -23,6 +24,7 @@
|
||||||
#include <linux/ctype.h>
|
#include <linux/ctype.h>
|
||||||
#include <linux/efi.h>
|
#include <linux/efi.h>
|
||||||
#include <linux/suspend.h>
|
#include <linux/suspend.h>
|
||||||
|
#include <acpi/battery.h>
|
||||||
#include <acpi/video.h>
|
#include <acpi/video.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -348,6 +350,8 @@ struct samsung_laptop {
|
||||||
|
|
||||||
struct notifier_block pm_nb;
|
struct notifier_block pm_nb;
|
||||||
|
|
||||||
|
struct acpi_battery_hook battery_hook;
|
||||||
|
|
||||||
bool handle_backlight;
|
bool handle_backlight;
|
||||||
bool has_stepping_quirk;
|
bool has_stepping_quirk;
|
||||||
|
|
||||||
|
|
@ -697,6 +701,11 @@ static ssize_t set_performance_level(struct device *dev,
|
||||||
static DEVICE_ATTR(performance_level, 0644,
|
static DEVICE_ATTR(performance_level, 0644,
|
||||||
get_performance_level, set_performance_level);
|
get_performance_level, set_performance_level);
|
||||||
|
|
||||||
|
static void show_battery_life_extender_deprecation_warning(struct device *dev)
|
||||||
|
{
|
||||||
|
dev_warn_once(dev, "battery_life_extender attribute has been deprecated, see charge_types.\n");
|
||||||
|
}
|
||||||
|
|
||||||
static int read_battery_life_extender(struct samsung_laptop *samsung)
|
static int read_battery_life_extender(struct samsung_laptop *samsung)
|
||||||
{
|
{
|
||||||
const struct sabi_commands *commands = &samsung->config->commands;
|
const struct sabi_commands *commands = &samsung->config->commands;
|
||||||
|
|
@ -739,6 +748,8 @@ static ssize_t get_battery_life_extender(struct device *dev,
|
||||||
struct samsung_laptop *samsung = dev_get_drvdata(dev);
|
struct samsung_laptop *samsung = dev_get_drvdata(dev);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
show_battery_life_extender_deprecation_warning(dev);
|
||||||
|
|
||||||
ret = read_battery_life_extender(samsung);
|
ret = read_battery_life_extender(samsung);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -753,6 +764,8 @@ static ssize_t set_battery_life_extender(struct device *dev,
|
||||||
struct samsung_laptop *samsung = dev_get_drvdata(dev);
|
struct samsung_laptop *samsung = dev_get_drvdata(dev);
|
||||||
int ret, value;
|
int ret, value;
|
||||||
|
|
||||||
|
show_battery_life_extender_deprecation_warning(dev);
|
||||||
|
|
||||||
if (!count || kstrtoint(buf, 0, &value) != 0)
|
if (!count || kstrtoint(buf, 0, &value) != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
|
@ -766,6 +779,84 @@ static ssize_t set_battery_life_extender(struct device *dev,
|
||||||
static DEVICE_ATTR(battery_life_extender, 0644,
|
static DEVICE_ATTR(battery_life_extender, 0644,
|
||||||
get_battery_life_extender, set_battery_life_extender);
|
get_battery_life_extender, set_battery_life_extender);
|
||||||
|
|
||||||
|
static int samsung_psy_ext_set_prop(struct power_supply *psy,
|
||||||
|
const struct power_supply_ext *ext,
|
||||||
|
void *ext_data,
|
||||||
|
enum power_supply_property psp,
|
||||||
|
const union power_supply_propval *val)
|
||||||
|
{
|
||||||
|
struct samsung_laptop *samsung = ext_data;
|
||||||
|
|
||||||
|
switch (val->intval) {
|
||||||
|
case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE:
|
||||||
|
return write_battery_life_extender(samsung, 1);
|
||||||
|
case POWER_SUPPLY_CHARGE_TYPE_STANDARD:
|
||||||
|
return write_battery_life_extender(samsung, 0);
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int samsung_psy_ext_get_prop(struct power_supply *psy,
|
||||||
|
const struct power_supply_ext *ext,
|
||||||
|
void *ext_data,
|
||||||
|
enum power_supply_property psp,
|
||||||
|
union power_supply_propval *val)
|
||||||
|
{
|
||||||
|
struct samsung_laptop *samsung = ext_data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = read_battery_life_extender(samsung);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (ret == 1)
|
||||||
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE;
|
||||||
|
else
|
||||||
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int samsung_psy_prop_is_writeable(struct power_supply *psy,
|
||||||
|
const struct power_supply_ext *ext,
|
||||||
|
void *data,
|
||||||
|
enum power_supply_property psp)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const enum power_supply_property samsung_power_supply_props[] = {
|
||||||
|
POWER_SUPPLY_PROP_CHARGE_TYPES,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct power_supply_ext samsung_battery_ext = {
|
||||||
|
.name = "samsung_laptop",
|
||||||
|
.properties = samsung_power_supply_props,
|
||||||
|
.num_properties = ARRAY_SIZE(samsung_power_supply_props),
|
||||||
|
.charge_types = (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |
|
||||||
|
BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)),
|
||||||
|
.get_property = samsung_psy_ext_get_prop,
|
||||||
|
.set_property = samsung_psy_ext_set_prop,
|
||||||
|
.property_is_writeable = samsung_psy_prop_is_writeable,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int samsung_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)
|
||||||
|
{
|
||||||
|
struct samsung_laptop *samsung = container_of(hook, struct samsung_laptop, battery_hook);
|
||||||
|
|
||||||
|
return power_supply_register_extension(battery, &samsung_battery_ext,
|
||||||
|
&samsung->platform_device->dev, samsung);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int samsung_battery_remove(struct power_supply *battery,
|
||||||
|
struct acpi_battery_hook *hook)
|
||||||
|
{
|
||||||
|
power_supply_unregister_extension(battery, &samsung_battery_ext);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int read_usb_charge(struct samsung_laptop *samsung)
|
static int read_usb_charge(struct samsung_laptop *samsung)
|
||||||
{
|
{
|
||||||
const struct sabi_commands *commands = &samsung->config->commands;
|
const struct sabi_commands *commands = &samsung->config->commands;
|
||||||
|
|
@ -1043,6 +1134,21 @@ static int __init samsung_lid_handling_init(struct samsung_laptop *samsung)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __init samsung_battery_hook_init(struct samsung_laptop *samsung)
|
||||||
|
{
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
if (samsung->config->commands.get_battery_life_extender != 0xFFFF) {
|
||||||
|
samsung->battery_hook.add_battery = samsung_battery_add;
|
||||||
|
samsung->battery_hook.remove_battery = samsung_battery_remove;
|
||||||
|
samsung->battery_hook.name = "Samsung Battery Extension";
|
||||||
|
retval = devm_battery_hook_register(&samsung->platform_device->dev,
|
||||||
|
&samsung->battery_hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
static int kbd_backlight_enable(struct samsung_laptop *samsung)
|
static int kbd_backlight_enable(struct samsung_laptop *samsung)
|
||||||
{
|
{
|
||||||
const struct sabi_commands *commands = &samsung->config->commands;
|
const struct sabi_commands *commands = &samsung->config->commands;
|
||||||
|
|
@ -1604,6 +1710,10 @@ static int __init samsung_init(void)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_lid_handling;
|
goto error_lid_handling;
|
||||||
|
|
||||||
|
ret = samsung_battery_hook_init(samsung);
|
||||||
|
if (ret)
|
||||||
|
goto error_lid_handling;
|
||||||
|
|
||||||
samsung_debugfs_init(samsung);
|
samsung_debugfs_init(samsung);
|
||||||
|
|
||||||
samsung->pm_nb.notifier_call = samsung_pm_notification;
|
samsung->pm_nb.notifier_call = samsung_pm_notification;
|
||||||
|
|
|
||||||
|
|
@ -248,13 +248,9 @@ static int silicom_gpio_direction_input(struct gpio_chip *gc,
|
||||||
static int silicom_gpio_set(struct gpio_chip *gc, unsigned int offset,
|
static int silicom_gpio_set(struct gpio_chip *gc, unsigned int offset,
|
||||||
int value)
|
int value)
|
||||||
{
|
{
|
||||||
int direction = silicom_gpio_get_direction(gc, offset);
|
|
||||||
u8 *channels = gpiochip_get_data(gc);
|
u8 *channels = gpiochip_get_data(gc);
|
||||||
int channel = channels[offset];
|
int channel = channels[offset];
|
||||||
|
|
||||||
if (direction == GPIO_LINE_DIRECTION_IN)
|
|
||||||
return -EPERM;
|
|
||||||
|
|
||||||
silicom_mec_port_set(channel, !value);
|
silicom_mec_port_set(channel, !value);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/bits.h>
|
#include <linux/bits.h>
|
||||||
#include <linux/build_bug.h>
|
#include <linux/build_bug.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
#include <linux/idr.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
|
@ -74,6 +75,8 @@ struct wmi_guid_count_context {
|
||||||
int count;
|
int count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static DEFINE_IDA(wmi_ida);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the GUID data block is marked as expensive, we must enable and
|
* If the GUID data block is marked as expensive, we must enable and
|
||||||
* explicitily disable data collection.
|
* explicitily disable data collection.
|
||||||
|
|
@ -984,6 +987,19 @@ static int guid_count(const guid_t *guid)
|
||||||
return context.count;
|
return context.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int wmi_dev_set_name(struct wmi_block *wblock, int count)
|
||||||
|
{
|
||||||
|
if (IS_ENABLED(CONFIG_ACPI_WMI_LEGACY_DEVICE_NAMES)) {
|
||||||
|
if (count)
|
||||||
|
return dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid,
|
||||||
|
count);
|
||||||
|
else
|
||||||
|
return dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, wblock->dev.dev.id);
|
||||||
|
}
|
||||||
|
|
||||||
static int wmi_create_device(struct device *wmi_bus_dev,
|
static int wmi_create_device(struct device *wmi_bus_dev,
|
||||||
struct wmi_block *wblock,
|
struct wmi_block *wblock,
|
||||||
struct acpi_device *device)
|
struct acpi_device *device)
|
||||||
|
|
@ -992,7 +1008,7 @@ static int wmi_create_device(struct device *wmi_bus_dev,
|
||||||
struct acpi_device_info *info;
|
struct acpi_device_info *info;
|
||||||
acpi_handle method_handle;
|
acpi_handle method_handle;
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
int count;
|
int count, ret;
|
||||||
|
|
||||||
if (wblock->gblock.flags & ACPI_WMI_EVENT) {
|
if (wblock->gblock.flags & ACPI_WMI_EVENT) {
|
||||||
wblock->dev.dev.type = &wmi_type_event;
|
wblock->dev.dev.type = &wmi_type_event;
|
||||||
|
|
@ -1063,11 +1079,18 @@ static int wmi_create_device(struct device *wmi_bus_dev,
|
||||||
if (count < 0)
|
if (count < 0)
|
||||||
return count;
|
return count;
|
||||||
|
|
||||||
if (count) {
|
if (count)
|
||||||
dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, count);
|
|
||||||
set_bit(WMI_GUID_DUPLICATED, &wblock->flags);
|
set_bit(WMI_GUID_DUPLICATED, &wblock->flags);
|
||||||
} else {
|
|
||||||
dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid);
|
ret = ida_alloc(&wmi_ida, GFP_KERNEL);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
wblock->dev.dev.id = ret;
|
||||||
|
ret = wmi_dev_set_name(wblock, count);
|
||||||
|
if (ret < 0) {
|
||||||
|
ida_free(&wmi_ida, wblock->dev.dev.id);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
device_initialize(&wblock->dev.dev);
|
device_initialize(&wblock->dev.dev);
|
||||||
|
|
@ -1153,6 +1176,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev)
|
||||||
dev_err(wmi_bus_dev, "failed to register %pUL\n",
|
dev_err(wmi_bus_dev, "failed to register %pUL\n",
|
||||||
&wblock->gblock.guid);
|
&wblock->gblock.guid);
|
||||||
|
|
||||||
|
ida_free(&wmi_ida, wblock->dev.dev.id);
|
||||||
put_device(&wblock->dev.dev);
|
put_device(&wblock->dev.dev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1252,7 +1276,10 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, void *context
|
||||||
|
|
||||||
static int wmi_remove_device(struct device *dev, void *data)
|
static int wmi_remove_device(struct device *dev, void *data)
|
||||||
{
|
{
|
||||||
|
int id = dev->id;
|
||||||
|
|
||||||
device_unregister(dev);
|
device_unregister(dev);
|
||||||
|
ida_free(&wmi_ida, id);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -206,24 +206,9 @@ static const struct software_node asus_tf103c_touchscreen_node = {
|
||||||
.properties = asus_tf103c_touchscreen_props,
|
.properties = asus_tf103c_touchscreen_props,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct property_entry asus_tf103c_battery_props[] = {
|
|
||||||
PROPERTY_ENTRY_STRING("compatible", "simple-battery"),
|
|
||||||
PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion-polymer"),
|
|
||||||
PROPERTY_ENTRY_U32("precharge-current-microamp", 256000),
|
|
||||||
PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000),
|
|
||||||
PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 2048000),
|
|
||||||
PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4208000),
|
|
||||||
PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000),
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct software_node asus_tf103c_battery_node = {
|
|
||||||
.properties = asus_tf103c_battery_props,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct property_entry asus_tf103c_bq24190_props[] = {
|
static const struct property_entry asus_tf103c_bq24190_props[] = {
|
||||||
PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1),
|
PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1),
|
||||||
PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node),
|
PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_4v2_battery_node),
|
||||||
PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000),
|
PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000),
|
||||||
PROPERTY_ENTRY_BOOL("omit-battery-class"),
|
PROPERTY_ENTRY_BOOL("omit-battery-class"),
|
||||||
PROPERTY_ENTRY_BOOL("disable-reset"),
|
PROPERTY_ENTRY_BOOL("disable-reset"),
|
||||||
|
|
@ -236,7 +221,7 @@ static const struct software_node asus_tf103c_bq24190_node = {
|
||||||
|
|
||||||
static const struct property_entry asus_tf103c_ug3105_props[] = {
|
static const struct property_entry asus_tf103c_ug3105_props[] = {
|
||||||
PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", bq24190_psy, 1),
|
PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", bq24190_psy, 1),
|
||||||
PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node),
|
PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_4v2_battery_node),
|
||||||
PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 5000),
|
PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 5000),
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
@ -321,6 +306,6 @@ const struct x86_dev_info asus_tf103c_info __initconst = {
|
||||||
.gpio_button = &asus_me176c_tf103c_lid,
|
.gpio_button = &asus_me176c_tf103c_lid,
|
||||||
.gpio_button_count = 1,
|
.gpio_button_count = 1,
|
||||||
.gpiod_lookup_tables = asus_tf103c_gpios,
|
.gpiod_lookup_tables = asus_tf103c_gpios,
|
||||||
.bat_swnode = &asus_tf103c_battery_node,
|
.bat_swnode = &generic_lipo_4v2_battery_node,
|
||||||
.modules = bq24190_modules,
|
.modules = bq24190_modules,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,78 @@ const struct software_node fg_bq25890_supply_node = {
|
||||||
.properties = fg_bq25890_supply_props,
|
.properties = fg_bq25890_supply_props,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const u32 generic_lipo_battery_ovc_cap_celcius[] = { 25 };
|
||||||
|
|
||||||
|
static const u32 generic_lipo_4v2_battery_ovc_cap_table0[] = {
|
||||||
|
4200000, 100,
|
||||||
|
4150000, 95,
|
||||||
|
4110000, 90,
|
||||||
|
4075000, 85,
|
||||||
|
4020000, 80,
|
||||||
|
3982500, 75,
|
||||||
|
3945000, 70,
|
||||||
|
3907500, 65,
|
||||||
|
3870000, 60,
|
||||||
|
3853333, 55,
|
||||||
|
3836667, 50,
|
||||||
|
3820000, 45,
|
||||||
|
3803333, 40,
|
||||||
|
3786667, 35,
|
||||||
|
3770000, 30,
|
||||||
|
3750000, 25,
|
||||||
|
3730000, 20,
|
||||||
|
3710000, 15,
|
||||||
|
3690000, 10,
|
||||||
|
3610000, 5,
|
||||||
|
3350000, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u32 generic_lipo_hv_4v35_battery_ovc_cap_table0[] = {
|
||||||
|
4300000, 100,
|
||||||
|
4250000, 96,
|
||||||
|
4200000, 91,
|
||||||
|
4150000, 86,
|
||||||
|
4110000, 82,
|
||||||
|
4075000, 77,
|
||||||
|
4020000, 73,
|
||||||
|
3982500, 68,
|
||||||
|
3945000, 64,
|
||||||
|
3907500, 59,
|
||||||
|
3870000, 55,
|
||||||
|
3853333, 50,
|
||||||
|
3836667, 45,
|
||||||
|
3820000, 41,
|
||||||
|
3803333, 36,
|
||||||
|
3786667, 32,
|
||||||
|
3770000, 27,
|
||||||
|
3750000, 23,
|
||||||
|
3730000, 18,
|
||||||
|
3710000, 14,
|
||||||
|
3690000, 9,
|
||||||
|
3610000, 5,
|
||||||
|
3350000, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Standard LiPo (max 4.2V) settings used by most devs with a LiPo battery */
|
||||||
|
static const struct property_entry generic_lipo_4v2_battery_props[] = {
|
||||||
|
PROPERTY_ENTRY_STRING("compatible", "simple-battery"),
|
||||||
|
PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion-polymer"),
|
||||||
|
PROPERTY_ENTRY_U32("precharge-current-microamp", 256000),
|
||||||
|
PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000),
|
||||||
|
PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 2048000),
|
||||||
|
PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4208000),
|
||||||
|
PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000),
|
||||||
|
PROPERTY_ENTRY_U32_ARRAY("ocv-capacity-celsius",
|
||||||
|
generic_lipo_battery_ovc_cap_celcius),
|
||||||
|
PROPERTY_ENTRY_U32_ARRAY("ocv-capacity-table-0",
|
||||||
|
generic_lipo_4v2_battery_ovc_cap_table0),
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct software_node generic_lipo_4v2_battery_node = {
|
||||||
|
.properties = generic_lipo_4v2_battery_props,
|
||||||
|
};
|
||||||
|
|
||||||
/* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV battery */
|
/* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV battery */
|
||||||
static const struct property_entry generic_lipo_hv_4v35_battery_props[] = {
|
static const struct property_entry generic_lipo_hv_4v35_battery_props[] = {
|
||||||
PROPERTY_ENTRY_STRING("compatible", "simple-battery"),
|
PROPERTY_ENTRY_STRING("compatible", "simple-battery"),
|
||||||
|
|
@ -48,6 +120,10 @@ static const struct property_entry generic_lipo_hv_4v35_battery_props[] = {
|
||||||
PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 1856000),
|
PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 1856000),
|
||||||
PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4352000),
|
PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4352000),
|
||||||
PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000),
|
PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000),
|
||||||
|
PROPERTY_ENTRY_U32_ARRAY("ocv-capacity-celsius",
|
||||||
|
generic_lipo_battery_ovc_cap_celcius),
|
||||||
|
PROPERTY_ENTRY_U32_ARRAY("ocv-capacity-table-0",
|
||||||
|
generic_lipo_hv_4v35_battery_ovc_cap_table0),
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ extern const char * const bq25890_psy[];
|
||||||
|
|
||||||
extern const struct software_node fg_bq24190_supply_node;
|
extern const struct software_node fg_bq24190_supply_node;
|
||||||
extern const struct software_node fg_bq25890_supply_node;
|
extern const struct software_node fg_bq25890_supply_node;
|
||||||
|
extern const struct software_node generic_lipo_4v2_battery_node;
|
||||||
extern const struct software_node generic_lipo_hv_4v35_battery_node;
|
extern const struct software_node generic_lipo_hv_4v35_battery_node;
|
||||||
|
|
||||||
extern struct bq24190_platform_data bq24190_pdata;
|
extern struct bq24190_platform_data bq24190_pdata;
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,10 @@
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/auxiliary_bus.h>
|
#include <linux/auxiliary_bus.h>
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/intel_tpmi.h>
|
|
||||||
#include <linux/intel_rapl.h>
|
#include <linux/intel_rapl.h>
|
||||||
|
#include <linux/intel_tpmi.h>
|
||||||
|
#include <linux/intel_vsec.h>
|
||||||
|
#include <linux/io.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
|
@ -48,7 +49,7 @@ enum tpmi_rapl_register {
|
||||||
|
|
||||||
struct tpmi_rapl_package {
|
struct tpmi_rapl_package {
|
||||||
struct rapl_if_priv priv;
|
struct rapl_if_priv priv;
|
||||||
struct intel_tpmi_plat_info *tpmi_info;
|
struct oobmsm_plat_info *tpmi_info;
|
||||||
struct rapl_package *rp;
|
struct rapl_package *rp;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
|
|
@ -253,7 +254,7 @@ static int intel_rapl_tpmi_probe(struct auxiliary_device *auxdev,
|
||||||
const struct auxiliary_device_id *id)
|
const struct auxiliary_device_id *id)
|
||||||
{
|
{
|
||||||
struct tpmi_rapl_package *trp;
|
struct tpmi_rapl_package *trp;
|
||||||
struct intel_tpmi_plat_info *info;
|
struct oobmsm_plat_info *info;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
u32 offset;
|
u32 offset;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,157 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
#ifndef _FEATURES_H
|
||||||
|
#define _FEATURES_H
|
||||||
|
|
||||||
|
#include <linux/bits.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
/* Common masks */
|
||||||
|
#define PMT_CAP_TELEM BIT(0)
|
||||||
|
#define PMT_CAP_WATCHER BIT(1)
|
||||||
|
#define PMT_CAP_CRASHLOG BIT(2)
|
||||||
|
#define PMT_CAP_STREAMING BIT(3)
|
||||||
|
#define PMT_CAP_THRESHOLD BIT(4)
|
||||||
|
#define PMT_CAP_WINDOW BIT(5)
|
||||||
|
#define PMT_CAP_CONFIG BIT(6)
|
||||||
|
#define PMT_CAP_TRACING BIT(7)
|
||||||
|
#define PMT_CAP_INBAND BIT(8)
|
||||||
|
#define PMT_CAP_OOB BIT(9)
|
||||||
|
#define PMT_CAP_SECURED_CHAN BIT(10)
|
||||||
|
|
||||||
|
#define PMT_CAP_PMT_SP BIT(11)
|
||||||
|
#define PMT_CAP_PMT_SP_POLICY GENMASK(17, 12)
|
||||||
|
|
||||||
|
/* Per Core Performance Telemetry (PCPT) specific masks */
|
||||||
|
#define PMT_CAP_PCPT_CORE_PERF BIT(18)
|
||||||
|
#define PMT_CAP_PCPT_CORE_C0_RES BIT(19)
|
||||||
|
#define PMT_CAP_PCPT_CORE_ACTIVITY BIT(20)
|
||||||
|
#define PMT_CAP_PCPT_CACHE_PERF BIT(21)
|
||||||
|
#define PMT_CAP_PCPT_QUALITY_TELEM BIT(22)
|
||||||
|
|
||||||
|
/* Per Core Environmental Telemetry (PCET) specific masks */
|
||||||
|
#define PMT_CAP_PCET_WORKPOINT_HIST BIT(18)
|
||||||
|
#define PMT_CAP_PCET_CORE_CURR_TEMP BIT(19)
|
||||||
|
#define PMT_CAP_PCET_CORE_INST_RES BIT(20)
|
||||||
|
#define PMT_CAP_PCET_QUALITY_TELEM BIT(21) /* Same as PMT_CAP_PCPT */
|
||||||
|
#define PMT_CAP_PCET_CORE_CDYN_LVL BIT(22)
|
||||||
|
#define PMT_CAP_PCET_CORE_STRESS_LVL BIT(23)
|
||||||
|
#define PMT_CAP_PCET_CORE_DAS BIT(24)
|
||||||
|
#define PMT_CAP_PCET_FIVR_HEALTH BIT(25)
|
||||||
|
#define PMT_CAP_PCET_ENERGY BIT(26)
|
||||||
|
#define PMT_CAP_PCET_PEM_STATUS BIT(27)
|
||||||
|
#define PMT_CAP_PCET_CORE_C_STATE BIT(28)
|
||||||
|
|
||||||
|
/* Per RMID Performance Telemetry specific masks */
|
||||||
|
#define PMT_CAP_RMID_CORES_PERF BIT(18)
|
||||||
|
#define PMT_CAP_RMID_CACHE_PERF BIT(19)
|
||||||
|
#define PMT_CAP_RMID_PERF_QUAL BIT(20)
|
||||||
|
|
||||||
|
/* Accelerator Telemetry specific masks */
|
||||||
|
#define PMT_CAP_ACCEL_CPM_TELEM BIT(18)
|
||||||
|
#define PMT_CAP_ACCEL_TIP_TELEM BIT(19)
|
||||||
|
|
||||||
|
/* Uncore Telemetry specific masks */
|
||||||
|
#define PMT_CAP_UNCORE_IO_CA_TELEM BIT(18)
|
||||||
|
#define PMT_CAP_UNCORE_RMID_TELEM BIT(19)
|
||||||
|
#define PMT_CAP_UNCORE_D2D_ULA_TELEM BIT(20)
|
||||||
|
#define PMT_CAP_UNCORE_PKGC_TELEM BIT(21)
|
||||||
|
|
||||||
|
/* Crash Log specific masks */
|
||||||
|
#define PMT_CAP_CRASHLOG_MAN_TRIG BIT(11)
|
||||||
|
#define PMT_CAP_CRASHLOG_CORE BIT(12)
|
||||||
|
#define PMT_CAP_CRASHLOG_UNCORE BIT(13)
|
||||||
|
#define PMT_CAP_CRASHLOG_TOR BIT(14)
|
||||||
|
#define PMT_CAP_CRASHLOG_S3M BIT(15)
|
||||||
|
#define PMT_CAP_CRASHLOG_PERSISTENCY BIT(16)
|
||||||
|
#define PMT_CAP_CRASHLOG_CLIP_GPIO BIT(17)
|
||||||
|
#define PMT_CAP_CRASHLOG_PRE_RESET BIT(18)
|
||||||
|
#define PMT_CAP_CRASHLOG_POST_RESET BIT(19)
|
||||||
|
|
||||||
|
/* PeTe Log specific masks */
|
||||||
|
#define PMT_CAP_PETE_MAN_TRIG BIT(11)
|
||||||
|
#define PMT_CAP_PETE_ENCRYPTION BIT(12)
|
||||||
|
#define PMT_CAP_PETE_PERSISTENCY BIT(13)
|
||||||
|
#define PMT_CAP_PETE_REQ_TOKENS BIT(14)
|
||||||
|
#define PMT_CAP_PETE_PROD_ENABLED BIT(15)
|
||||||
|
#define PMT_CAP_PETE_DEBUG_ENABLED BIT(16)
|
||||||
|
|
||||||
|
/* TPMI control specific masks */
|
||||||
|
#define PMT_CAP_TPMI_MAILBOX BIT(11)
|
||||||
|
#define PMT_CAP_TPMI_LOCK BIT(12)
|
||||||
|
|
||||||
|
/* Tracing specific masks */
|
||||||
|
#define PMT_CAP_TRACE_SRAR BIT(11)
|
||||||
|
#define PMT_CAP_TRACE_CORRECTABLE BIT(12)
|
||||||
|
#define PMT_CAP_TRACE_MCTP BIT(13)
|
||||||
|
#define PMT_CAP_TRACE_MRT BIT(14)
|
||||||
|
|
||||||
|
/* Per RMID Energy Telemetry specific masks */
|
||||||
|
#define PMT_CAP_RMID_ENERGY BIT(18)
|
||||||
|
#define PMT_CAP_RMID_ACTIVITY BIT(19)
|
||||||
|
#define PMT_CAP_RMID_ENERGY_QUAL BIT(20)
|
||||||
|
|
||||||
|
enum pmt_feature_id {
|
||||||
|
FEATURE_INVALID = 0x0,
|
||||||
|
FEATURE_PER_CORE_PERF_TELEM = 0x1,
|
||||||
|
FEATURE_PER_CORE_ENV_TELEM = 0x2,
|
||||||
|
FEATURE_PER_RMID_PERF_TELEM = 0x3,
|
||||||
|
FEATURE_ACCEL_TELEM = 0x4,
|
||||||
|
FEATURE_UNCORE_TELEM = 0x5,
|
||||||
|
FEATURE_CRASH_LOG = 0x6,
|
||||||
|
FEATURE_PETE_LOG = 0x7,
|
||||||
|
FEATURE_TPMI_CTRL = 0x8,
|
||||||
|
FEATURE_RESERVED = 0x9,
|
||||||
|
FEATURE_TRACING = 0xA,
|
||||||
|
FEATURE_PER_RMID_ENERGY_TELEM = 0xB,
|
||||||
|
FEATURE_MAX = 0xB,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum feature_layout {
|
||||||
|
LAYOUT_RMID,
|
||||||
|
LAYOUT_WATCHER,
|
||||||
|
LAYOUT_COMMAND,
|
||||||
|
LAYOUT_CAPS_ONLY,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_cap {
|
||||||
|
u32 mask;
|
||||||
|
const char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const char * const pmt_feature_names[];
|
||||||
|
extern enum feature_layout feature_layout[];
|
||||||
|
extern struct pmt_cap pmt_cap_common[];
|
||||||
|
extern struct pmt_cap pmt_cap_pcpt[];
|
||||||
|
extern struct pmt_cap *pmt_caps_pcpt[];
|
||||||
|
extern struct pmt_cap pmt_cap_pcet[];
|
||||||
|
extern struct pmt_cap *pmt_caps_pcet[];
|
||||||
|
extern struct pmt_cap pmt_cap_rmid_perf[];
|
||||||
|
extern struct pmt_cap *pmt_caps_rmid_perf[];
|
||||||
|
extern struct pmt_cap pmt_cap_accel[];
|
||||||
|
extern struct pmt_cap *pmt_caps_accel[];
|
||||||
|
extern struct pmt_cap pmt_cap_uncore[];
|
||||||
|
extern struct pmt_cap *pmt_caps_uncore[];
|
||||||
|
extern struct pmt_cap pmt_cap_crashlog[];
|
||||||
|
extern struct pmt_cap *pmt_caps_crashlog[];
|
||||||
|
extern struct pmt_cap pmt_cap_pete[];
|
||||||
|
extern struct pmt_cap *pmt_caps_pete[];
|
||||||
|
extern struct pmt_cap pmt_cap_tpmi[];
|
||||||
|
extern struct pmt_cap *pmt_caps_tpmi[];
|
||||||
|
extern struct pmt_cap pmt_cap_s3m[];
|
||||||
|
extern struct pmt_cap *pmt_caps_s3m[];
|
||||||
|
extern struct pmt_cap pmt_cap_tracing[];
|
||||||
|
extern struct pmt_cap *pmt_caps_tracing[];
|
||||||
|
extern struct pmt_cap pmt_cap_rmid_energy[];
|
||||||
|
extern struct pmt_cap *pmt_caps_rmid_energy[];
|
||||||
|
|
||||||
|
static inline bool pmt_feature_id_is_valid(enum pmt_feature_id id)
|
||||||
|
{
|
||||||
|
if (id > FEATURE_MAX)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (id == FEATURE_INVALID || id == FEATURE_RESERVED)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include <linux/bitfield.h>
|
#include <linux/bitfield.h>
|
||||||
|
|
||||||
|
struct oobmsm_plat_info;
|
||||||
|
|
||||||
#define TPMI_VERSION_INVALID 0xff
|
#define TPMI_VERSION_INVALID 0xff
|
||||||
#define TPMI_MINOR_VERSION(val) FIELD_GET(GENMASK(4, 0), val)
|
#define TPMI_MINOR_VERSION(val) FIELD_GET(GENMASK(4, 0), val)
|
||||||
#define TPMI_MAJOR_VERSION(val) FIELD_GET(GENMASK(7, 5), val)
|
#define TPMI_MAJOR_VERSION(val) FIELD_GET(GENMASK(7, 5), val)
|
||||||
|
|
@ -26,30 +28,7 @@ enum intel_tpmi_id {
|
||||||
TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */
|
TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
struct oobmsm_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev);
|
||||||
* struct intel_tpmi_plat_info - Platform information for a TPMI device instance
|
|
||||||
* @cdie_mask: Mask of all compute dies in the partition
|
|
||||||
* @package_id: CPU Package id
|
|
||||||
* @partition: Package partition id when multiple VSEC PCI devices per package
|
|
||||||
* @segment: PCI segment ID
|
|
||||||
* @bus_number: PCI bus number
|
|
||||||
* @device_number: PCI device number
|
|
||||||
* @function_number: PCI function number
|
|
||||||
*
|
|
||||||
* Structure to store platform data for a TPMI device instance. This
|
|
||||||
* struct is used to return data via tpmi_get_platform_data().
|
|
||||||
*/
|
|
||||||
struct intel_tpmi_plat_info {
|
|
||||||
u16 cdie_mask;
|
|
||||||
u8 package_id;
|
|
||||||
u8 partition;
|
|
||||||
u8 segment;
|
|
||||||
u8 bus_number;
|
|
||||||
u8 device_number;
|
|
||||||
u8 function_number;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct intel_tpmi_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev);
|
|
||||||
struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int index);
|
struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int index);
|
||||||
int tpmi_get_resource_count(struct auxiliary_device *auxdev);
|
int tpmi_get_resource_count(struct auxiliary_device *auxdev);
|
||||||
int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, bool *read_blocked,
|
int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, bool *read_blocked,
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,22 @@
|
||||||
|
|
||||||
#include <linux/auxiliary_bus.h>
|
#include <linux/auxiliary_bus.h>
|
||||||
#include <linux/bits.h>
|
#include <linux/bits.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/intel_pmt_features.h>
|
||||||
|
|
||||||
#define VSEC_CAP_TELEMETRY BIT(0)
|
/*
|
||||||
#define VSEC_CAP_WATCHER BIT(1)
|
* VSEC_CAP_UNUSED is reserved. It exists to prevent zero initialized
|
||||||
#define VSEC_CAP_CRASHLOG BIT(2)
|
* intel_vsec devices from being automatically set to a known
|
||||||
#define VSEC_CAP_SDSI BIT(3)
|
* capability with ID 0
|
||||||
#define VSEC_CAP_TPMI BIT(4)
|
*/
|
||||||
|
#define VSEC_CAP_UNUSED BIT(0)
|
||||||
|
#define VSEC_CAP_TELEMETRY BIT(1)
|
||||||
|
#define VSEC_CAP_WATCHER BIT(2)
|
||||||
|
#define VSEC_CAP_CRASHLOG BIT(3)
|
||||||
|
#define VSEC_CAP_SDSI BIT(4)
|
||||||
|
#define VSEC_CAP_TPMI BIT(5)
|
||||||
|
#define VSEC_CAP_DISCOVERY BIT(6)
|
||||||
|
#define VSEC_FEATURE_COUNT 7
|
||||||
|
|
||||||
/* Intel DVSEC offsets */
|
/* Intel DVSEC offsets */
|
||||||
#define INTEL_DVSEC_ENTRIES 0xA
|
#define INTEL_DVSEC_ENTRIES 0xA
|
||||||
|
|
@ -26,6 +36,7 @@ enum intel_vsec_id {
|
||||||
VSEC_ID_TELEMETRY = 2,
|
VSEC_ID_TELEMETRY = 2,
|
||||||
VSEC_ID_WATCHER = 3,
|
VSEC_ID_WATCHER = 3,
|
||||||
VSEC_ID_CRASHLOG = 4,
|
VSEC_ID_CRASHLOG = 4,
|
||||||
|
VSEC_ID_DISCOVERY = 12,
|
||||||
VSEC_ID_SDSI = 65,
|
VSEC_ID_SDSI = 65,
|
||||||
VSEC_ID_TPMI = 66,
|
VSEC_ID_TPMI = 66,
|
||||||
};
|
};
|
||||||
|
|
@ -81,22 +92,31 @@ struct pmt_callbacks {
|
||||||
int (*read_telem)(struct pci_dev *pdev, u32 guid, u64 *data, loff_t off, u32 count);
|
int (*read_telem)(struct pci_dev *pdev, u32 guid, u64 *data, loff_t off, u32 count);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct vsec_feature_dependency {
|
||||||
|
unsigned long feature;
|
||||||
|
unsigned long supplier_bitmap;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct intel_vsec_platform_info - Platform specific data
|
* struct intel_vsec_platform_info - Platform specific data
|
||||||
* @parent: parent device in the auxbus chain
|
* @parent: parent device in the auxbus chain
|
||||||
* @headers: list of headers to define the PMT client devices to create
|
* @headers: list of headers to define the PMT client devices to create
|
||||||
|
* @deps: array of feature dependencies
|
||||||
* @priv_data: private data, usable by parent devices, currently a callback
|
* @priv_data: private data, usable by parent devices, currently a callback
|
||||||
* @caps: bitmask of PMT capabilities for the given headers
|
* @caps: bitmask of PMT capabilities for the given headers
|
||||||
* @quirks: bitmask of VSEC device quirks
|
* @quirks: bitmask of VSEC device quirks
|
||||||
* @base_addr: allow a base address to be specified (rather than derived)
|
* @base_addr: allow a base address to be specified (rather than derived)
|
||||||
|
* @num_deps: Count feature dependencies
|
||||||
*/
|
*/
|
||||||
struct intel_vsec_platform_info {
|
struct intel_vsec_platform_info {
|
||||||
struct device *parent;
|
struct device *parent;
|
||||||
struct intel_vsec_header **headers;
|
struct intel_vsec_header **headers;
|
||||||
|
const struct vsec_feature_dependency *deps;
|
||||||
void *priv_data;
|
void *priv_data;
|
||||||
unsigned long caps;
|
unsigned long caps;
|
||||||
unsigned long quirks;
|
unsigned long quirks;
|
||||||
u64 base_addr;
|
u64 base_addr;
|
||||||
|
int num_deps;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -110,6 +130,7 @@ struct intel_vsec_platform_info {
|
||||||
* @priv_data: any private data needed
|
* @priv_data: any private data needed
|
||||||
* @quirks: specified quirks
|
* @quirks: specified quirks
|
||||||
* @base_addr: base address of entries (if specified)
|
* @base_addr: base address of entries (if specified)
|
||||||
|
* @cap_id: the enumerated id of the vsec feature
|
||||||
*/
|
*/
|
||||||
struct intel_vsec_device {
|
struct intel_vsec_device {
|
||||||
struct auxiliary_device auxdev;
|
struct auxiliary_device auxdev;
|
||||||
|
|
@ -122,6 +143,44 @@ struct intel_vsec_device {
|
||||||
size_t priv_data_size;
|
size_t priv_data_size;
|
||||||
unsigned long quirks;
|
unsigned long quirks;
|
||||||
u64 base_addr;
|
u64 base_addr;
|
||||||
|
unsigned long cap_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct oobmsm_plat_info - Platform information for a device instance
|
||||||
|
* @cdie_mask: Mask of all compute dies in the partition
|
||||||
|
* @package_id: CPU Package id
|
||||||
|
* @partition: Package partition id when multiple VSEC PCI devices per package
|
||||||
|
* @segment: PCI segment ID
|
||||||
|
* @bus_number: PCI bus number
|
||||||
|
* @device_number: PCI device number
|
||||||
|
* @function_number: PCI function number
|
||||||
|
*
|
||||||
|
* Structure to store platform data for a OOBMSM device instance.
|
||||||
|
*/
|
||||||
|
struct oobmsm_plat_info {
|
||||||
|
u16 cdie_mask;
|
||||||
|
u8 package_id;
|
||||||
|
u8 partition;
|
||||||
|
u8 segment;
|
||||||
|
u8 bus_number;
|
||||||
|
u8 device_number;
|
||||||
|
u8 function_number;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct telemetry_region {
|
||||||
|
struct oobmsm_plat_info plat_info;
|
||||||
|
void __iomem *addr;
|
||||||
|
size_t size;
|
||||||
|
u32 guid;
|
||||||
|
u32 num_rmids;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmt_feature_group {
|
||||||
|
enum pmt_feature_id id;
|
||||||
|
int count;
|
||||||
|
struct kref kref;
|
||||||
|
struct telemetry_region regions[];
|
||||||
};
|
};
|
||||||
|
|
||||||
int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent,
|
int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent,
|
||||||
|
|
@ -141,11 +200,40 @@ static inline struct intel_vsec_device *auxdev_to_ivdev(struct auxiliary_device
|
||||||
#if IS_ENABLED(CONFIG_INTEL_VSEC)
|
#if IS_ENABLED(CONFIG_INTEL_VSEC)
|
||||||
int intel_vsec_register(struct pci_dev *pdev,
|
int intel_vsec_register(struct pci_dev *pdev,
|
||||||
struct intel_vsec_platform_info *info);
|
struct intel_vsec_platform_info *info);
|
||||||
|
int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info,
|
||||||
|
struct intel_vsec_device *vsec_dev);
|
||||||
|
struct oobmsm_plat_info *intel_vsec_get_mapping(struct pci_dev *pdev);
|
||||||
#else
|
#else
|
||||||
static inline int intel_vsec_register(struct pci_dev *pdev,
|
static inline int intel_vsec_register(struct pci_dev *pdev,
|
||||||
struct intel_vsec_platform_info *info)
|
struct intel_vsec_platform_info *info)
|
||||||
{
|
{
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
static inline int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info,
|
||||||
|
struct intel_vsec_device *vsec_dev)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
static inline struct oobmsm_plat_info *intel_vsec_get_mapping(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
return ERR_PTR(-ENODEV);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_INTEL_PMT_TELEMETRY)
|
||||||
|
struct pmt_feature_group *
|
||||||
|
intel_pmt_get_regions_by_feature(enum pmt_feature_id id);
|
||||||
|
|
||||||
|
void intel_pmt_put_feature_group(struct pmt_feature_group *feature_group);
|
||||||
|
#else
|
||||||
|
static inline struct pmt_feature_group *
|
||||||
|
intel_pmt_get_regions_by_feature(enum pmt_feature_id id)
|
||||||
|
{
|
||||||
|
return ERR_PTR(-ENODEV);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
intel_pmt_put_feature_group(struct pmt_feature_group *feature_group) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue