History

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

Pull x86 platform driver updates from Ilpo Järvinen: "Highlights: - amd/pmf: - Avoid overwriting BIOS input values when events occur rapidly - Fix PMF driver issues related to S4 (in part on crypto/ccp side) - Add NPU metrics API (for accel side consumers) - Allow disabling Smart PC function through a module parameter - asus-wmi & HID/asus: - Unification of backlight control (replaces quirks) - Support multiple interfaces for controlling keyboard/RGB brightness - Simplify init sequence - hp-wmi: - Add manual fan control for Victus S models - Add fan mode keep-alive - Fix platform profile values for Omen 16-wf1xxx - Add EC offset to get the thermal profile - intel/pmc: Show substate residencies also for non-primary PMCs - intel/ISST: - Store and restore data for all domains - Write interface improvements - lenovo-wmi: - Support multiple Capability Data - Add HWMON reporting and tuning support - mellanox/mlx-platform: Add HI173 & HI174 support - surface/aggregator_registry: Add Surface Pro 11 (QCOM) - thinkpad_acpi: Add support for HW damage detection capability - uniwill: Implement cTGP setting - wmi: - Introduce marshalling support - Convert a few drivers to use the new buffer-based WMI API - tools/power/x86/intel-speed-select: Allow read operations for non-root - Miscellaneous cleanups / refactoring / improvements" * tag 'platform-drivers-x86-v7.0-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (68 commits) platform/x86: lenovo-wmi-{capdata,other}: Fix HWMON channel visibility platform/x86: hp-wmi: Add EC offsets to read Victus S thermal profile platform: mellanox: mlx-platform: Add support DGX flavor of next-generation 800GB/s ethernet switch. platform: mellanox: mlx-platform: Add support for new Nvidia DGX system based on class VMOD0010 HID: asus: add support for the asus-wmi brightness handler platform/x86: asus-wmi: add keyboard brightness event handler platform/x86: asus-wmi: remove unused keyboard backlight quirk HID: asus: listen to the asus-wmi brightness device instead of creating one platform/x86: asus-wmi: Add support for multiple kbd led handlers HID: asus: early return for ROG devices HID: asus: move vendor initialization to probe HID: asus: fortify keyboard handshake HID: asus: use same report_id in response HID: asus: initialize additional endpoints only for certain devices HID: asus: simplify RGB init sequence platform/wmi: string-kunit: Add missing oversized string test case platform/x86/amd/pmf: Added a module parameter to disable the Smart PC function platform/x86/uniwill: Implement cTGP setting platform/x86: uniwill-laptop: Introduce device descriptor system platform/x86/amd: Use scope-based cleanup for wbrf_record() ...

Linus Torvalds committed 9 months ago

Commit f50822f2 parents 1b49e36 5a5203a61 files · +5390 −953Browse files

Unified · Split

61 files
  1. Documentation/admin-guide/laptops/thinkpad-acpi.rst +37 −0
  2. Documentation/driver-api/wmi.rst +3 −0
  3. Documentation/wmi/acpi-interface.rst +68 −0
  4. Documentation/wmi/devices/lenovo-wmi-other.rst +43 −3
  5. Documentation/wmi/driver-development-guide.rst +53 −23
  6. drivers/crypto/ccp/psp-dev.c +11 −0
  7. drivers/crypto/ccp/sp-dev.c +12 −0
  8. drivers/crypto/ccp/sp-dev.h +3 −0
  9. drivers/crypto/ccp/sp-pci.c +15 −1
  10. drivers/crypto/ccp/tee-dev.c +44 −12
  11. drivers/crypto/ccp/tee-dev.h +1 −0
  12. drivers/hid/hid-asus.c +104 −117
  13. drivers/platform/mellanox/mlx-platform.c +484 −0
  14. drivers/platform/surface/surface_aggregator_registry.c +18 −0
  15. drivers/platform/surface/surfacepro3_button.c +5 −6
  16. drivers/platform/wmi/Kconfig +3 −0
  17. drivers/platform/wmi/Makefile +4 −1
  18. drivers/platform/wmi/core.c +156 −4
  19. drivers/platform/wmi/internal.h +17 −0
  20. drivers/platform/wmi/marshalling.c +247 −0
  21. drivers/platform/wmi/string.c +92 −0
  22. drivers/platform/wmi/tests/Kconfig +27 −0
  23. drivers/platform/wmi/tests/Makefile +11 −0
  24. drivers/platform/wmi/tests/marshalling_kunit.c +452 −0
  25. drivers/platform/wmi/tests/string_kunit.c +296 −0
  26. drivers/platform/x86/amd/pmf/acpi.c +40 −0
  27. drivers/platform/x86/amd/pmf/core.c +155 −6
  28. drivers/platform/x86/amd/pmf/pmf.h +33 −0
  29. drivers/platform/x86/amd/pmf/spc.c +19 −14
  30. drivers/platform/x86/amd/pmf/tee-if.c +5 −9
  31. drivers/platform/x86/amd/wbrf.c +8 −17
  32. drivers/platform/x86/asus-wmi.c +196 −27
  33. drivers/platform/x86/hp/hp-wmi.c +489 −114
  34. drivers/platform/x86/intel/pmc/core.c +56 −29
  35. drivers/platform/x86/intel/pmc/core.h +7 −8
  36. drivers/platform/x86/intel/pmt/class.c +3 −3
  37. drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +56 −23
  38. drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +7 −6
  39. drivers/platform/x86/intel/wmi/sbl-fw-update.c +18 −25
  40. drivers/platform/x86/intel/wmi/thunderbolt.c +11 −15
  41. drivers/platform/x86/lenovo/Kconfig +3 −2
  42. drivers/platform/x86/lenovo/Makefile +1 −1
  43. drivers/platform/x86/lenovo/ideapad-laptop.c +16 −23
  44. drivers/platform/x86/lenovo/thinkpad_acpi.c +206 −0
  45. drivers/platform/x86/lenovo/wmi-capdata.c +829 −0
  46. drivers/platform/x86/lenovo/wmi-capdata.h +65 −0
  47. drivers/platform/x86/lenovo/wmi-capdata01.c +0 −302
  48. drivers/platform/x86/lenovo/wmi-capdata01.h +0 −25
  49. drivers/platform/x86/lenovo/wmi-helpers.c +18 −3
  50. drivers/platform/x86/lenovo/wmi-other.c +507 −21
  51. drivers/platform/x86/lenovo/yogabook.c +7 −5
  52. drivers/platform/x86/uniwill/uniwill-acpi.c +272 −30
  53. drivers/platform/x86/wmi-bmof.c +15 −19
  54. drivers/platform/x86/xiaomi-wmi.c +2 −3
  55. include/linux/amd-pmf-io.h +21 −0
  56. include/linux/platform_data/x86/asus-wmi-leds-ids.h +0 −50
  57. include/linux/platform_data/x86/asus-wmi.h +28 −0
  58. include/linux/psp.h +1 −0
  59. include/linux/wmi.h +43 −2
  60. tools/power/x86/intel-speed-select/Makefile +7 −1
  61. tools/power/x86/intel-speed-select/isst-config.c +40 −3
Documentation/admin-guide/laptops/thinkpad-acpi.rst+37−0
@@ -54,6 +54,7 @@ detailed description): · 53 unmodified lines
54 - Setting keyboard language54 - Setting keyboard language
55 - WWAN Antenna type55 - WWAN Antenna type
56 - Auxmac56 - Auxmac
57 - Hardware damage detection capability
57 58
58A compatibility table by model and feature is maintained on the web59A compatibility table by model and feature is maintained on the web
59site, http://ibm-acpi.sf.net/. I appreciate any success or failure60site, http://ibm-acpi.sf.net/. I appreciate any success or failure
@@ -1576,6 +1577,42 @@ percentage level, above which charging will stop. · 1516 unmodified lines
1576The exact semantics of the attributes may be found in1577The exact semantics of the attributes may be found in
1577Documentation/ABI/testing/sysfs-class-power.1578Documentation/ABI/testing/sysfs-class-power.
1578 1579
1580Hardware damage detection capability
1581------------------------------------
1582
1583sysfs attributes: hwdd_status, hwdd_detail
1584
1585Thinkpads are adding the ability to detect and report hardware damage.
1586Add new sysfs interface to identify the damaged device status.
1587Initial support is available for the USB-C replaceable connector.
1588
1589The command to check device damaged status is::
1590
1591 cat /sys/devices/platform/thinkpad_acpi/hwdd_status
1592
1593This value displays status of device damaged.
1594
1595- 0 = Not Damaged
1596- 1 = Damaged
1597
1598The command to check location of damaged device is::
1599
1600 cat /sys/devices/platform/thinkpad_acpi/hwdd_detail
1601
1602This value displays location of damaged device having 1 line per damaged "item".
1603For example:
1604
1605if no damage is detected:
1606
1607- No damage detected
1608
1609if damage detected:
1610
1611- TYPE-C: Base, Right side, Center port
1612
1613The property is read-only. If feature is not supported then sysfs
1614attribute is not created.
1615
1579Multiple Commands, Module Parameters1616Multiple Commands, Module Parameters
1580------------------------------------1617------------------------------------
1581 1618
Documentation/driver-api/wmi.rst+3−0
@@ -16,5 +16,8 @@ which will be bound to compatible WMI devices by the driver core. · 15 unmodified lines
16.. kernel-doc:: include/linux/wmi.h16.. kernel-doc:: include/linux/wmi.h
17 :internal:17 :internal:
18 18
19.. kernel-doc:: drivers/platform/wmi/string.c
20 :export:
21
19.. kernel-doc:: drivers/platform/wmi/core.c22.. kernel-doc:: drivers/platform/wmi/core.c
20 :export:23 :export:
Documentation/wmi/acpi-interface.rst+68−0
@@ -104,3 +104,71 @@ holding the notification ID of the event. This method should be evaluated every · 103 unmodified lines
104time an ACPI notification is received, since some ACPI implementations use a104time an ACPI notification is received, since some ACPI implementations use a
105queue to store WMI event data items. This queue will overflow after a couple105queue to store WMI event data items. This queue will overflow after a couple
106of WMI events are received without retrieving the associated WMI event data.106of WMI events are received without retrieving the associated WMI event data.
107
108Conversion rules for ACPI data types
109------------------------------------
110
111Consumers of the ACPI-WMI interface use binary buffers to exchange data with the WMI driver core,
112with the internal structure of the buffer being only know to the consumers. The WMI driver core is
113thus responsible for converting the data inside the buffer into an appropriate ACPI data type for
114consumption by the ACPI firmware. Additionally, any data returned by the various ACPI methods needs
115to be converted back into a binary buffer.
116
117The layout of said buffers is defined by the MOF description of the WMI method or data block in
118question [1]_:
119
120=============== ======================================================================= =========
121Data Type Layout Alignment
122=============== ======================================================================= =========
123``string`` Starts with an unsigned 16-bit little endian integer specifying 2 bytes
124 the length of the string data in bytes, followed by the string data
125 encoded as UTF-16LE with **optional** NULL termination and padding.
126 Keep in mind that some firmware implementations might depend on the
127 terminating NULL character to be present. Also the padding should
128 always be performed with NULL characters.
129``boolean`` Single byte where 0 means ``false`` and nonzero means ``true``. 1 byte
130``sint8`` Signed 8-bit integer. 1 byte
131``uint8`` Unsigned 8-bit integer. 1 byte
132``sint16`` Signed 16-bit little endian integer. 2 bytes
133``uint16`` Unsigned 16-bit little endian integer. 2 bytes
134``sint32`` Signed 32-bit little endian integer. 4 bytes
135``uint32`` Unsigned 32-bit little endian integer. 4 bytes
136``sint64`` Signed 64-bit little endian integer. 8 bytes
137``uint64`` Unsigned 64-bit little endian integer. 8 bytes
138``datetime`` A fixed-length 25-character UTF-16LE string with the format 2 bytes
139 *yyyymmddhhmmss.mmmmmmsutc* where *yyyy* is the 4-digit year, *mm* is
140 the 2-digit month, *dd* is the 2-digit day, *hh* is the 2-digit hour
141 based on a 24-hour clock, *mm* is the 2-digit minute, *ss* is the
142 2-digit second, *mmmmmm* is the 6-digit microsecond, *s* is a plus or
143 minus character depending on whether *utc* is a positive or negative
144 offset from UTC (or a colon if the date is an interval). Unpopulated
145 fields should be filled with asterisks.
146=============== ======================================================================= =========
147
148Arrays should be aligned based on the alignment of their base type, while objects should be
149aligned based on the largest alignment of an element inside them.
150
151All buffers returned by the WMI driver core are 8-byte aligned. When converting ACPI data types
152into such buffers the following conversion rules apply:
153
154=============== ============================================================
155ACPI Data Type Converted into
156=============== ============================================================
157Buffer Copied as-is.
158Integer Converted into a ``uint32``.
159String Converted into a ``string`` with a terminating NULL character
160 to match the behavior the of the Windows driver.
161Package Each element inside the package is converted with alignment
162 of the resulting data types being respected. Nested packages
163 are not allowed.
164=============== ============================================================
165
166The Windows driver does attempt to handle nested packages, but this results in internal data
167structures (``_ACPI_METHOD_ARGUMENT_V1``) erroneously being copied into the resulting buffer.
168ACPI firmware implementations should thus not return nested packages from ACPI methods
169associated with the ACPI-WMI interface.
170
171References
172==========
173
174.. [1] https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/driver-defined-wmi-data-items
Documentation/wmi/devices/lenovo-wmi-other.rst+43−3
@@ -31,13 +31,32 @@ under the following path: · 30 unmodified lines
31 31
32 /sys/class/firmware-attributes/lenovo-wmi-other/attributes/<attribute>/32 /sys/class/firmware-attributes/lenovo-wmi-other/attributes/<attribute>/
33 33
34Additionally, this driver also exports attributes to HWMON.
35
36LENOVO_CAPABILITY_DATA_00
37-------------------------
38
39WMI GUID ``362A3AFE-3D96-4665-8530-96DAD5BB300E``
40
41The LENOVO_CAPABILITY_DATA_00 interface provides various information that
42does not rely on the gamezone thermal mode.
43
44The following HWMON attributes are implemented:
45 - fanX_div: internal RPM divisor
46 - fanX_input: current RPM
47 - fanX_target: target RPM (tunable, 0=auto)
48
49Due to the internal RPM divisor, the current/target RPMs are rounded down to
50its nearest multiple. The divisor itself is not necessary to be a power of two.
51
34LENOVO_CAPABILITY_DATA_0152LENOVO_CAPABILITY_DATA_01
35-------------------------53-------------------------
36 54
37WMI GUID ``7A8F5407-CB67-4D6E-B547-39B3BE018154``55WMI GUID ``7A8F5407-CB67-4D6E-B547-39B3BE018154``
38 56
39The LENOVO_CAPABILITY_DATA_01 interface provides information on various57The LENOVO_CAPABILITY_DATA_01 interface provides various information that
40power limits of integrated CPU and GPU components.58relies on the gamezone thermal mode, including power limits of integrated
59CPU and GPU components.
41 60
42Each attribute has the following properties:61Each attribute has the following properties:
43 - current_value62 - current_value
@@ -48,11 +67,22 @@ Each attribute has the following properties: · 4 unmodified lines
48 - scalar_increment67 - scalar_increment
49 - type68 - type
50 69
51The following attributes are implemented:70The following firmware-attributes are implemented:
52 - ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit71 - ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit
53 - ppt_pl2_sppt: Platform Profile Tracking Slow Package Power Tracking72 - ppt_pl2_sppt: Platform Profile Tracking Slow Package Power Tracking
54 - ppt_pl3_fppt: Platform Profile Tracking Fast Package Power Tracking73 - ppt_pl3_fppt: Platform Profile Tracking Fast Package Power Tracking
55 74
75LENOVO_FAN_TEST_DATA
76-------------------------
77
78WMI GUID ``B642801B-3D21-45DE-90AE-6E86F164FB21``
79
80The LENOVO_FAN_TEST_DATA interface provides reference data for self-test of
81cooling fans.
82
83The following HWMON attributes are implemented:
84 - fanX_max: maximum RPM
85 - fanX_min: minimum RPM
56 86
57WMI interface description87WMI interface description
58=========================88=========================
@@ -106,3 +136,13 @@ data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility: · 47 unmodified lines
106 [WmiDataId(3), read, Description("Data Size.")] uint32 DataSize;136 [WmiDataId(3), read, Description("Data Size.")] uint32 DataSize;
107 [WmiDataId(4), read, Description("Default Value"), WmiSizeIs("DataSize")] uint8 DefaultValue[];137 [WmiDataId(4), read, Description("Default Value"), WmiSizeIs("DataSize")] uint8 DefaultValue[];
108 };138 };
139
140 [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Definition of Fan Test Data"), guid("{B642801B-3D21-45DE-90AE-6E86F164FB21}")]
141 class LENOVO_FAN_TEST_DATA {
142 [key, read] string InstanceName;
143 [read] boolean Active;
144 [WmiDataId(1), read, Description("Mode.")] uint32 NumOfFans;
145 [WmiDataId(2), read, Description("Fan ID."), WmiSizeIs("NumOfFans")] uint32 FanId[];
146 [WmiDataId(3), read, Description("Maximum Fan Speed."), WmiSizeIs("NumOfFans")] uint32 FanMaxSpeed[];
147 [WmiDataId(4), read, Description("Minumum Fan Speed."), WmiSizeIs("NumOfFans")] uint32 FanMinSpeed[];
148 };
Documentation/wmi/driver-development-guide.rst+53−23
@@ -70,7 +70,7 @@ to matching WMI devices using a struct wmi_device_id table: · 69 unmodified lines
70 .probe = foo_probe,70 .probe = foo_probe,
71 .remove = foo_remove, /* optional, devres is preferred */71 .remove = foo_remove, /* optional, devres is preferred */
72 .shutdown = foo_shutdown, /* optional, called during shutdown */72 .shutdown = foo_shutdown, /* optional, called during shutdown */
73 .notify = foo_notify, /* optional, for event handling */73 .notify_new = foo_notify, /* optional, for event handling */
74 .no_notify_data = true, /* optional, enables events containing no additional data */74 .no_notify_data = true, /* optional, enables events containing no additional data */
75 .no_singleton = true, /* required for new WMI drivers */75 .no_singleton = true, /* required for new WMI drivers */
76 };76 };
@@ -90,9 +90,9 @@ the WMI device and put it in a well-known state for the WMI driver to pick up la · 13 unmodified lines
90or kexec. Most WMI drivers need no special shutdown handling and can thus omit this callback.90or kexec. Most WMI drivers need no special shutdown handling and can thus omit this callback.
91 91
92Please note that new WMI drivers are required to be able to be instantiated multiple times,92Please note that new WMI drivers are required to be able to be instantiated multiple times,
93and are forbidden from using any deprecated GUID-based WMI functions. This means that the93and are forbidden from using any deprecated GUID-based or ACPI-based WMI functions. This means
94WMI driver should be prepared for the scenario that multiple matching WMI devices are present94that the WMI driver should be prepared for the scenario that multiple matching WMI devices are
95on a given machine.95present on a given machine.
96 96
97Because of this, WMI drivers should use the state container design pattern as described in97Because of this, WMI drivers should use the state container design pattern as described in
98Documentation/driver-api/driver-model/design-patterns.rst.98Documentation/driver-api/driver-model/design-patterns.rst.
@@ -104,38 +104,37 @@ Documentation/driver-api/driver-model/design-patterns.rst. · 5 unmodified lines
104WMI method drivers104WMI method drivers
105------------------105------------------
106 106
107WMI drivers can call WMI device methods using wmidev_evaluate_method(), the107WMI drivers can call WMI device methods using wmidev_invoke_method(). For each WMI method
108structure of the ACPI buffer passed to this function is device-specific and usually108invocation the WMI driver needs to provide the instance number and the method ID, as well as
109needs some tinkering to get right. Looking at the ACPI tables containing the WMI109a buffer with the method arguments and optionally a buffer for the results.
110device usually helps here. The method id and instance number passed to this function
111are also device-specific, looking at the decoded Binary MOF is usually enough to
112find the right values.
113 110
114The maximum instance number can be retrieved during runtime using wmidev_instance_count().111The layout of said buffers is device-specific and described by the Binary MOF data associated
112with a given WMI device. Said Binary MOF data also describes the method ID of a given WMI method
113with the ``WmiMethodId`` qualifier. WMI devices exposing WMI methods usually expose only a single
114instance (instance number 0), but in theory can expose multiple instances as well. In such a case
115the number of instances can be retrieved using wmidev_instance_count().
115 116
116Take a look at drivers/platform/x86/inspur_platform_profile.c for an example WMI method driver.117Take a look at drivers/platform/x86/intel/wmi/thunderbolt.c for an example WMI method driver.
117 118
118WMI data block drivers119WMI data block drivers
119----------------------120----------------------
120 121
121WMI drivers can query WMI device data blocks using wmidev_block_query(), the122WMI drivers can query WMI data blocks using wmidev_query_block(), the layout of the returned
122structure of the returned ACPI object is again device-specific. Some WMI devices123buffer is again device-specific and described by the Binary MOF data. Some WMI data blocks are
123also allow for setting data blocks using wmidev_block_set().124also writeable and can be set using wmidev_set_block(). The number of data block instances can
125again be retrieved using wmidev_instance_count().
124 126
125The maximum instance number can also be retrieved using wmidev_instance_count().127Take a look at drivers/platform/x86/intel/wmi/sbl-fw-update.c for an example WMI data block driver.
126
127Take a look at drivers/platform/x86/intel/wmi/sbl-fw-update.c for an example
128WMI data block driver.
129 128
130WMI event drivers129WMI event drivers
131-----------------130-----------------
132 131
133WMI drivers can receive WMI events via the notify() callback inside the struct wmi_driver.132WMI drivers can receive WMI events via the notify_new() callback inside the struct wmi_driver.
134The WMI subsystem will then take care of setting up the WMI event accordingly. Please note that133The WMI subsystem will then take care of setting up the WMI event accordingly. Please note that
135the structure of the ACPI object passed to this callback is device-specific, and freeing the134the layout of the buffer passed to this callback is device-specific, and freeing of the buffer
136ACPI object is being done by the WMI subsystem, not the driver.135is done by the WMI subsystem itself, not the driver.
137 136
138The WMI driver core will take care that the notify() callback will only be called after137The WMI driver core will take care that the notify_new() callback will only be called after
139the probe() callback has been called, and that no events are being received by the driver138the probe() callback has been called, and that no events are being received by the driver
140right before and after calling its remove() or shutdown() callback.139right before and after calling its remove() or shutdown() callback.
141 140
@@ -147,6 +146,36 @@ the ``no_notify_data`` flag inside struct wmi_driver should be set to ``true``. · 5 unmodified lines
147 146
148Take a look at drivers/platform/x86/xiaomi-wmi.c for an example WMI event driver.147Take a look at drivers/platform/x86/xiaomi-wmi.c for an example WMI event driver.
149 148
149Exchanging data with the WMI driver core
150----------------------------------------
151
152WMI drivers can exchange data with the WMI driver core using struct wmi_buffer. The internal
153structure of those buffers is device-specific and only known by the WMI driver. Because of this
154the WMI driver itself is responsible for parsing and validating the data received from its
155WMI device.
156
157The structure of said buffers is described by the MOF data associated with the WMI device in
158question. When such a buffer contains multiple data items it usually makes sense to define a
159C structure and use it during parsing. Since the WMI driver core guarantees that all buffers
160received from a WMI device are aligned on an 8-byte boundary, WMI drivers can simply perform
161a cast between the WMI buffer data and this C structure.
162
163This however should only be done after the size of the buffer was verified to be large enough
164to hold the whole C structure. WMI drivers should reject undersized buffers as they are usually
165sent by the WMI device to signal an internal error. Oversized buffers however should be accepted
166to emulate the behavior of the Windows WMI implementation.
167
168When defining a C structure for parsing WMI buffers the alignment of the data items should be
169respected. This is especially important for 64-bit integers as those have different alignments
170on 64-bit (8-byte alignment) and 32-bit (4-byte alignment) architectures. It is thus a good idea
171to manually specify the alignment of such data items or mark the whole structure as packed when
172appropriate. Integer data items in general are little-endian integers and should be marked as
173such using ``__le64`` and friends. When parsing WMI string data items the struct wmi_string should
174be used as WMI strings have a different layout than C strings.
175
176See Documentation/wmi/acpi-interface.rst for more information regarding the binary format
177of WMI data items.
178
150Handling multiple WMI devices at once179Handling multiple WMI devices at once
151-------------------------------------180-------------------------------------
152 181
@@ -171,6 +200,7 @@ Things to avoid · 18 unmodified lines
171When developing WMI drivers, there are a couple of things which should be avoided:200When developing WMI drivers, there are a couple of things which should be avoided:
172 201
173- usage of the deprecated GUID-based WMI interface which uses GUIDs instead of WMI device structs202- usage of the deprecated GUID-based WMI interface which uses GUIDs instead of WMI device structs
203- usage of the deprecated ACPI-based WMI interface which uses ACPI objects instead of plain buffers
174- bypassing of the WMI subsystem when talking to WMI devices204- bypassing of the WMI subsystem when talking to WMI devices
175- WMI drivers which cannot be instantiated multiple times.205- WMI drivers which cannot be instantiated multiple times.
176 206
drivers/crypto/ccp/psp-dev.c+11−0
@@ -351,6 +351,17 @@ struct psp_device *psp_get_master_device(void) · 350 unmodified lines
351 return sp ? sp->psp_data : NULL;351 return sp ? sp->psp_data : NULL;
352}352}
353 353
354int psp_restore(struct sp_device *sp)
355{
356 struct psp_device *psp = sp->psp_data;
357 int ret = 0;
358
359 if (psp->tee_data)
360 ret = tee_restore(psp);
361
362 return ret;
363}
364
354void psp_pci_init(void)365void psp_pci_init(void)
355{366{
356 psp_master = psp_get_master_device();367 psp_master = psp_get_master_device();
drivers/crypto/ccp/sp-dev.c+12−0
@@ -230,6 +230,18 @@ int sp_resume(struct sp_device *sp) · 229 unmodified lines
230 return 0;230 return 0;
231}231}
232 232
233int sp_restore(struct sp_device *sp)
234{
235 if (sp->psp_data) {
236 int ret = psp_restore(sp);
237
238 if (ret)
239 return ret;
240 }
241
242 return sp_resume(sp);
243}
244
233struct sp_device *sp_get_psp_master_device(void)245struct sp_device *sp_get_psp_master_device(void)
234{246{
235 struct sp_device *i, *ret = NULL;247 struct sp_device *i, *ret = NULL;
drivers/crypto/ccp/sp-dev.h+3−0
@@ -141,6 +141,7 @@ void sp_destroy(struct sp_device *sp); · 140 unmodified lines
141 141
142int sp_suspend(struct sp_device *sp);142int sp_suspend(struct sp_device *sp);
143int sp_resume(struct sp_device *sp);143int sp_resume(struct sp_device *sp);
144int sp_restore(struct sp_device *sp);
144int sp_request_ccp_irq(struct sp_device *sp, irq_handler_t handler,145int sp_request_ccp_irq(struct sp_device *sp, irq_handler_t handler,
145 const char *name, void *data);146 const char *name, void *data);
146void sp_free_ccp_irq(struct sp_device *sp, void *data);147void sp_free_ccp_irq(struct sp_device *sp, void *data);
@@ -174,6 +175,7 @@ int psp_dev_init(struct sp_device *sp); · 27 unmodified lines
174void psp_pci_init(void);175void psp_pci_init(void);
175void psp_dev_destroy(struct sp_device *sp);176void psp_dev_destroy(struct sp_device *sp);
176void psp_pci_exit(void);177void psp_pci_exit(void);
178int psp_restore(struct sp_device *sp);
177 179
178#else /* !CONFIG_CRYPTO_DEV_SP_PSP */180#else /* !CONFIG_CRYPTO_DEV_SP_PSP */
179 181
@@ -181,6 +183,7 @@ static inline int psp_dev_init(struct sp_device *sp) { return 0; } · 1 unmodified line
181static inline void psp_pci_init(void) { }183static inline void psp_pci_init(void) { }
182static inline void psp_dev_destroy(struct sp_device *sp) { }184static inline void psp_dev_destroy(struct sp_device *sp) { }
183static inline void psp_pci_exit(void) { }185static inline void psp_pci_exit(void) { }
186static inline int psp_restore(struct sp_device *sp) { return 0; }
184 187
185#endif /* CONFIG_CRYPTO_DEV_SP_PSP */188#endif /* CONFIG_CRYPTO_DEV_SP_PSP */
186 189
drivers/crypto/ccp/sp-pci.c+15−1
@@ -353,6 +353,13 @@ static int __maybe_unused sp_pci_resume(struct device *dev) · 352 unmodified lines
353 return sp_resume(sp);353 return sp_resume(sp);
354}354}
355 355
356static int __maybe_unused sp_pci_restore(struct device *dev)
357{
358 struct sp_device *sp = dev_get_drvdata(dev);
359
360 return sp_restore(sp);
361}
362
356#ifdef CONFIG_CRYPTO_DEV_SP_PSP363#ifdef CONFIG_CRYPTO_DEV_SP_PSP
357static const struct sev_vdata sevv1 = {364static const struct sev_vdata sevv1 = {
358 .cmdresp_reg = 0x10580, /* C2PMSG_32 */365 .cmdresp_reg = 0x10580, /* C2PMSG_32 */
@@ -563,7 +570,14 @@ static const struct pci_device_id sp_pci_table[] = { · 204 unmodified lines
563};570};
564MODULE_DEVICE_TABLE(pci, sp_pci_table);571MODULE_DEVICE_TABLE(pci, sp_pci_table);
565 572
566static SIMPLE_DEV_PM_OPS(sp_pci_pm_ops, sp_pci_suspend, sp_pci_resume);573static const struct dev_pm_ops sp_pci_pm_ops = {
574 .suspend = pm_sleep_ptr(sp_pci_suspend),
575 .resume = pm_sleep_ptr(sp_pci_resume),
576 .freeze = pm_sleep_ptr(sp_pci_suspend),
577 .thaw = pm_sleep_ptr(sp_pci_resume),
578 .poweroff = pm_sleep_ptr(sp_pci_suspend),
579 .restore_early = pm_sleep_ptr(sp_pci_restore),
580};
567 581
568static struct pci_driver sp_pci_driver = {582static struct pci_driver sp_pci_driver = {
569 .name = "ccp",583 .name = "ccp",
drivers/crypto/ccp/tee-dev.c+44−12
@@ -86,10 +86,34 @@ static inline void tee_free_cmd_buffer(struct tee_init_ring_cmd *cmd) · 85 unmodified lines
86 kfree(cmd);86 kfree(cmd);
87}87}
88 88
89static bool tee_send_destroy_cmd(struct psp_tee_device *tee)
90{
91 unsigned int reg;
92 int ret;
93
94 ret = psp_mailbox_command(tee->psp, PSP_CMD_TEE_RING_DESTROY, NULL,
95 TEE_DEFAULT_CMD_TIMEOUT, &reg);
96 if (ret) {
97 dev_err(tee->dev, "tee: ring destroy command timed out, disabling TEE support\n");
98 psp_dead = true;
99 return false;
100 }
101
102 if (FIELD_GET(PSP_CMDRESP_STS, reg)) {
103 dev_err(tee->dev, "tee: ring destroy command failed (%#010lx)\n",
104 FIELD_GET(PSP_CMDRESP_STS, reg));
105 psp_dead = true;
106 return false;
107 }
108
109 return true;
110}
111
89static int tee_init_ring(struct psp_tee_device *tee)112static int tee_init_ring(struct psp_tee_device *tee)
90{113{
91 int ring_size = MAX_RING_BUFFER_ENTRIES * sizeof(struct tee_ring_cmd);114 int ring_size = MAX_RING_BUFFER_ENTRIES * sizeof(struct tee_ring_cmd);
92 struct tee_init_ring_cmd *cmd;115 struct tee_init_ring_cmd *cmd;
116 bool retry = false;
93 unsigned int reg;117 unsigned int reg;
94 int ret;118 int ret;
95 119
@@ -112,6 +136,7 @@ static int tee_init_ring(struct psp_tee_device *tee) · 16 unmodified lines
112 /* Send command buffer details to Trusted OS by writing to136 /* Send command buffer details to Trusted OS by writing to
113 * CPU-PSP message registers137 * CPU-PSP message registers
114 */138 */
139retry_init:
115 ret = psp_mailbox_command(tee->psp, PSP_CMD_TEE_RING_INIT, cmd,140 ret = psp_mailbox_command(tee->psp, PSP_CMD_TEE_RING_INIT, cmd,
116 TEE_DEFAULT_CMD_TIMEOUT, &reg);141 TEE_DEFAULT_CMD_TIMEOUT, &reg);
117 if (ret) {142 if (ret) {
@@ -122,9 +147,22 @@ static int tee_init_ring(struct psp_tee_device *tee) · 4 unmodified lines
122 }147 }
123 148
124 if (FIELD_GET(PSP_CMDRESP_STS, reg)) {149 if (FIELD_GET(PSP_CMDRESP_STS, reg)) {
150 /*
151 * During the hibernate resume sequence driver may have gotten loaded
152 * but the ring not properly destroyed. If the ring doesn't work, try
153 * to destroy and re-init once.
154 */
155 if (!retry && FIELD_GET(PSP_CMDRESP_STS, reg) == PSP_TEE_STS_RING_BUSY) {
156 dev_info(tee->dev, "tee: ring init command failed with busy status, retrying\n");
157 if (tee_send_destroy_cmd(tee)) {
158 retry = true;
159 goto retry_init;
160 }
161 }
125 dev_err(tee->dev, "tee: ring init command failed (%#010lx)\n",162 dev_err(tee->dev, "tee: ring init command failed (%#010lx)\n",
126 FIELD_GET(PSP_CMDRESP_STS, reg));163 FIELD_GET(PSP_CMDRESP_STS, reg));
127 tee_free_ring(tee);164 tee_free_ring(tee);
165 psp_dead = true;
128 ret = -EIO;166 ret = -EIO;
129 }167 }
130 168
@@ -136,24 +174,13 @@ free_buf: · 5 unmodified lines
136 174
137static void tee_destroy_ring(struct psp_tee_device *tee)175static void tee_destroy_ring(struct psp_tee_device *tee)
138{176{
139 unsigned int reg;
140 int ret;
141
142 if (!tee->rb_mgr.ring_start)177 if (!tee->rb_mgr.ring_start)
143 return;178 return;
144 179
145 if (psp_dead)180 if (psp_dead)
146 goto free_ring;181 goto free_ring;
147 182
148 ret = psp_mailbox_command(tee->psp, PSP_CMD_TEE_RING_DESTROY, NULL,183 tee_send_destroy_cmd(tee);
149 TEE_DEFAULT_CMD_TIMEOUT, &reg);
150 if (ret) {
151 dev_err(tee->dev, "tee: ring destroy command timed out, disabling TEE support\n");
152 psp_dead = true;
153 } else if (FIELD_GET(PSP_CMDRESP_STS, reg)) {
154 dev_err(tee->dev, "tee: ring destroy command failed (%#010lx)\n",
155 FIELD_GET(PSP_CMDRESP_STS, reg));
156 }
157 184
158free_ring:185free_ring:
159 tee_free_ring(tee);186 tee_free_ring(tee);
@@ -365,3 +392,8 @@ int psp_check_tee_status(void) · 205 unmodified lines
365 return 0;392 return 0;
366}393}
367EXPORT_SYMBOL(psp_check_tee_status);394EXPORT_SYMBOL(psp_check_tee_status);
395
396int tee_restore(struct psp_device *psp)
397{
398 return tee_init_ring(psp->tee_data);
399}
drivers/crypto/ccp/tee-dev.h+1−0
@@ -111,5 +111,6 @@ struct tee_ring_cmd { · 110 unmodified lines
111 111
112int tee_dev_init(struct psp_device *psp);112int tee_dev_init(struct psp_device *psp);
113void tee_dev_destroy(struct psp_device *psp);113void tee_dev_destroy(struct psp_device *psp);
114int tee_restore(struct psp_device *psp);
114 115
115#endif /* __TEE_DEV_H__ */116#endif /* __TEE_DEV_H__ */
drivers/hid/hid-asus.c+104−117
@@ -28,7 +28,6 @@ · 27 unmodified lines
28#include <linux/hid.h>28#include <linux/hid.h>
29#include <linux/module.h>29#include <linux/module.h>
30#include <linux/platform_data/x86/asus-wmi.h>30#include <linux/platform_data/x86/asus-wmi.h>
31#include <linux/platform_data/x86/asus-wmi-leds-ids.h>
32#include <linux/input/mt.h>31#include <linux/input/mt.h>
33#include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */32#include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */
34#include <linux/power_supply.h>33#include <linux/power_supply.h>
@@ -50,7 +49,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); · 15 unmodified lines
50#define FEATURE_REPORT_ID 0x0d49#define FEATURE_REPORT_ID 0x0d
51#define INPUT_REPORT_ID 0x5d50#define INPUT_REPORT_ID 0x5d
52#define FEATURE_KBD_REPORT_ID 0x5a51#define FEATURE_KBD_REPORT_ID 0x5a
53#define FEATURE_KBD_REPORT_SIZE 1652#define FEATURE_KBD_REPORT_SIZE 64
54#define FEATURE_KBD_LED_REPORT_ID1 0x5d53#define FEATURE_KBD_LED_REPORT_ID1 0x5d
55#define FEATURE_KBD_LED_REPORT_ID2 0x5e54#define FEATURE_KBD_LED_REPORT_ID2 0x5e
56 55
@@ -99,9 +98,10 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); · 42 unmodified lines
99#define QUIRK_T90CHI BIT(9)98#define QUIRK_T90CHI BIT(9)
100#define QUIRK_MEDION_E1239T BIT(10)99#define QUIRK_MEDION_E1239T BIT(10)
101#define QUIRK_ROG_NKEY_KEYBOARD BIT(11)100#define QUIRK_ROG_NKEY_KEYBOARD BIT(11)
102#define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12)101#define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12)
103#define QUIRK_ROG_ALLY_XPAD BIT(13)102#define QUIRK_ROG_ALLY_XPAD BIT(13)
104#define QUIRK_HID_FN_LOCK BIT(14)103#define QUIRK_HID_FN_LOCK BIT(14)
104#define QUIRK_ROG_NKEY_ID1ID2_INIT BIT(15)
105 105
106#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \106#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
107 QUIRK_NO_INIT_REPORTS | \107 QUIRK_NO_INIT_REPORTS | \
@@ -113,7 +113,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); · 5 unmodified lines
113#define TRKID_SGN ((TRKID_MAX + 1) >> 1)113#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
114 114
115struct asus_kbd_leds {115struct asus_kbd_leds {
116 struct led_classdev cdev;116 struct asus_hid_listener listener;
117 struct hid_device *hdev;117 struct hid_device *hdev;
118 struct work_struct work;118 struct work_struct work;
119 unsigned int brightness;119 unsigned int brightness;
@@ -138,7 +138,6 @@ struct asus_drvdata { · 18 unmodified lines
138 struct input_dev *tp_kbd_input;138 struct input_dev *tp_kbd_input;
139 struct asus_kbd_leds *kbd_backlight;139 struct asus_kbd_leds *kbd_backlight;
140 const struct asus_touchpad_info *tp;140 const struct asus_touchpad_info *tp;
141 bool enable_backlight;
142 struct power_supply *battery;141 struct power_supply *battery;
143 struct power_supply_desc battery_desc;142 struct power_supply_desc battery_desc;
144 int battery_capacity;143 int battery_capacity;
@@ -363,10 +362,21 @@ static int asus_event(struct hid_device *hdev, struct hid_field *field, · 218 unmodified lines
363 usage->hid & HID_USAGE);362 usage->hid & HID_USAGE);
364 }363 }
365 364
366 if (drvdata->quirks & QUIRK_HID_FN_LOCK &&365 if (usage->type == EV_KEY && value) {
367 usage->type == EV_KEY && usage->code == KEY_FN_ESC && value == 1) {366 switch (usage->code) {
368 drvdata->fn_lock = !drvdata->fn_lock;367 case KEY_KBDILLUMUP:
369 schedule_work(&drvdata->fn_lock_sync_work);368 return !asus_hid_event(ASUS_EV_BRTUP);
369 case KEY_KBDILLUMDOWN:
370 return !asus_hid_event(ASUS_EV_BRTDOWN);
371 case KEY_KBDILLUMTOGGLE:
372 return !asus_hid_event(ASUS_EV_BRTTOGGLE);
373 case KEY_FN_ESC:
374 if (drvdata->quirks & QUIRK_HID_FN_LOCK) {
375 drvdata->fn_lock = !drvdata->fn_lock;
376 schedule_work(&drvdata->fn_lock_sync_work);
377 }
378 break;
379 }
370 }380 }
371 381
372 return 0;382 return 0;
@@ -476,15 +486,41 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu · 103 unmodified lines
476 486
477static int asus_kbd_init(struct hid_device *hdev, u8 report_id)487static int asus_kbd_init(struct hid_device *hdev, u8 report_id)
478{488{
489 /*
490 * The handshake is first sent as a set_report, then retrieved
491 * from a get_report. They should be equal.
492 */
479 const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,493 const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,
480 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };494 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
481 int ret;495 int ret;
482 496
483 ret = asus_kbd_set_report(hdev, buf, sizeof(buf));497 ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
484 if (ret < 0)498 if (ret < 0) {
485 hid_err(hdev, "Asus failed to send init command: %d\n", ret);499 hid_err(hdev, "Asus handshake %02x failed to send: %d\n",
500 report_id, ret);
501 return ret;
502 }
486 503
487 return ret;504 u8 *readbuf __free(kfree) = kzalloc(FEATURE_KBD_REPORT_SIZE, GFP_KERNEL);
505 if (!readbuf)
506 return -ENOMEM;
507
508 ret = hid_hw_raw_request(hdev, report_id, readbuf,
509 FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT,
510 HID_REQ_GET_REPORT);
511 if (ret < 0) {
512 hid_warn(hdev, "Asus handshake %02x failed to receive ack: %d\n",
513 report_id, ret);
514 } else if (memcmp(readbuf, buf, sizeof(buf)) != 0) {
515 hid_warn(hdev, "Asus handshake %02x returned invalid response: %*ph\n",
516 report_id, FEATURE_KBD_REPORT_SIZE, readbuf);
517 }
518
519 /*
520 * Do not return error if handshake is wrong until this is
521 * verified to work for all devices.
522 */
523 return 0;
488}524}
489 525
490static int asus_kbd_get_functions(struct hid_device *hdev,526static int asus_kbd_get_functions(struct hid_device *hdev,
@@ -505,7 +541,7 @@ static int asus_kbd_get_functions(struct hid_device *hdev, · 14 unmodified lines
505 if (!readbuf)541 if (!readbuf)
506 return -ENOMEM;542 return -ENOMEM;
507 543
508 ret = hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, readbuf,544 ret = hid_hw_raw_request(hdev, report_id, readbuf,
509 FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT,545 FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT,
510 HID_REQ_GET_REPORT);546 HID_REQ_GET_REPORT);
511 if (ret < 0) {547 if (ret < 0) {
@@ -565,11 +601,11 @@ static void asus_schedule_work(struct asus_kbd_leds *led) · 53 unmodified lines
565 spin_unlock_irqrestore(&led->lock, flags);601 spin_unlock_irqrestore(&led->lock, flags);
566}602}
567 603
568static void asus_kbd_backlight_set(struct led_classdev *led_cdev,604static void asus_kbd_backlight_set(struct asus_hid_listener *listener,
569 enum led_brightness brightness)605 int brightness)
570{606{
571 struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds,607 struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds,
572 cdev);608 listener);
573 unsigned long flags;609 unsigned long flags;
574 610
575 spin_lock_irqsave(&led->lock, flags);611 spin_lock_irqsave(&led->lock, flags);
@@ -579,20 +615,6 @@ static void asus_kbd_backlight_set(struct led_classdev *led_cdev, · 3 unmodified lines
579 asus_schedule_work(led);615 asus_schedule_work(led);
580}616}
581 617
582static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev)
583{
584 struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds,
585 cdev);
586 enum led_brightness brightness;
587 unsigned long flags;
588
589 spin_lock_irqsave(&led->lock, flags);
590 brightness = led->brightness;
591 spin_unlock_irqrestore(&led->lock, flags);
592
593 return brightness;
594}
595
596static void asus_kbd_backlight_work(struct work_struct *work)618static void asus_kbd_backlight_work(struct work_struct *work)
597{619{
598 struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);620 struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);
@@ -609,34 +631,6 @@ static void asus_kbd_backlight_work(struct work_struct *work) · 10 unmodified lines
609 hid_err(led->hdev, "Asus failed to set keyboard backlight: %d\n", ret);631 hid_err(led->hdev, "Asus failed to set keyboard backlight: %d\n", ret);
610}632}
611 633
612/* WMI-based keyboard backlight LED control (via asus-wmi driver) takes
613 * precedence. We only activate HID-based backlight control when the
614 * WMI control is not available.
615 */
616static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
617{
618 struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
619 u32 value;
620 int ret;
621
622 if (!IS_ENABLED(CONFIG_ASUS_WMI))
623 return false;
624
625 if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD &&
626 dmi_check_system(asus_use_hid_led_dmi_ids)) {
627 hid_info(hdev, "using HID for asus::kbd_backlight\n");
628 return false;
629 }
630
631 ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS,
632 ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value);
633 hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value);
634 if (ret)
635 return false;
636
637 return !!(value & ASUS_WMI_DSTS_PRESENCE_BIT);
638}
639
640/*634/*
641 * We don't care about any other part of the string except the version section.635 * We don't care about any other part of the string except the version section.
642 * Example strings: FGA80100.RC72LA.312_T01, FGA80100.RC71LS.318_T01636 * Example strings: FGA80100.RC72LA.312_T01, FGA80100.RC71LS.318_T01
@@ -736,48 +730,35 @@ static int asus_kbd_register_leds(struct hid_device *hdev) · 93 unmodified lines
736 unsigned char kbd_func;730 unsigned char kbd_func;
737 int ret;731 int ret;
738 732
739 if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {733 ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);
740 /* Initialize keyboard */734 if (ret < 0)
741 ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);735 return ret;
742 if (ret < 0)
743 return ret;
744
745 /* The LED endpoint is initialised in two HID */
746 ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID1);
747 if (ret < 0)
748 return ret;
749
750 ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID2);
751 if (ret < 0)
752 return ret;
753 736
754 if (dmi_match(DMI_PRODUCT_FAMILY, "ProArt P16")) {737 /* Get keyboard functions */
755 ret = asus_kbd_disable_oobe(hdev);738 ret = asus_kbd_get_functions(hdev, &kbd_func, FEATURE_KBD_REPORT_ID);
756 if (ret < 0)739 if (ret < 0)
757 return ret;740 return ret;
758 }
759 741
760 if (drvdata->quirks & QUIRK_ROG_ALLY_XPAD) {742 /* Check for backlight support */
761 intf = to_usb_interface(hdev->dev.parent);743 if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
762 udev = interface_to_usbdev(intf);744 return -ENODEV;
763 validate_mcu_fw_version(hdev,
764 le16_to_cpu(udev->descriptor.idProduct));
765 }
766 745
767 } else {746 if (drvdata->quirks & QUIRK_ROG_NKEY_ID1ID2_INIT) {
768 /* Initialize keyboard */747 asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID1);
769 ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);748 asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID2);
770 if (ret < 0)749 }
771 return ret;
772 750
773 /* Get keyboard functions */751 if (dmi_match(DMI_PRODUCT_FAMILY, "ProArt P16")) {
774 ret = asus_kbd_get_functions(hdev, &kbd_func, FEATURE_KBD_REPORT_ID);752 ret = asus_kbd_disable_oobe(hdev);
775 if (ret < 0)753 if (ret < 0)
776 return ret;754 return ret;
755 }
777 756
778 /* Check for backlight support */757 if (drvdata->quirks & QUIRK_ROG_ALLY_XPAD) {
779 if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))758 intf = to_usb_interface(hdev->dev.parent);
780 return -ENODEV;759 udev = interface_to_usbdev(intf);
760 validate_mcu_fw_version(hdev,
761 le16_to_cpu(udev->descriptor.idProduct));
781 }762 }
782 763
783 drvdata->kbd_backlight = devm_kzalloc(&hdev->dev,764 drvdata->kbd_backlight = devm_kzalloc(&hdev->dev,
@@ -789,14 +770,11 @@ static int asus_kbd_register_leds(struct hid_device *hdev) · 5 unmodified lines
789 drvdata->kbd_backlight->removed = false;770 drvdata->kbd_backlight->removed = false;
790 drvdata->kbd_backlight->brightness = 0;771 drvdata->kbd_backlight->brightness = 0;
791 drvdata->kbd_backlight->hdev = hdev;772 drvdata->kbd_backlight->hdev = hdev;
792 drvdata->kbd_backlight->cdev.name = "asus::kbd_backlight";773 drvdata->kbd_backlight->listener.brightness_set = asus_kbd_backlight_set;
793 drvdata->kbd_backlight->cdev.max_brightness = 3;
794 drvdata->kbd_backlight->cdev.brightness_set = asus_kbd_backlight_set;
795 drvdata->kbd_backlight->cdev.brightness_get = asus_kbd_backlight_get;
796 INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_backlight_work);774 INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_backlight_work);
797 spin_lock_init(&drvdata->kbd_backlight->lock);775 spin_lock_init(&drvdata->kbd_backlight->lock);
798 776
799 ret = devm_led_classdev_register(&hdev->dev, &drvdata->kbd_backlight->cdev);777 ret = asus_hid_register_listener(&drvdata->kbd_backlight->listener);
800 if (ret < 0) {778 if (ret < 0) {
801 /* No need to have this still around */779 /* No need to have this still around */
802 devm_kfree(&hdev->dev, drvdata->kbd_backlight);780 devm_kfree(&hdev->dev, drvdata->kbd_backlight);
@@ -1021,11 +999,6 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi) · 218 unmodified lines
1021 999
1022 drvdata->input = input;1000 drvdata->input = input;
1023 1001
1024 if (drvdata->enable_backlight &&
1025 !asus_kbd_wmi_led_control_present(hdev) &&
1026 asus_kbd_register_leds(hdev))
1027 hid_warn(hdev, "Failed to initialize backlight.\n");
1028
1029 if (drvdata->quirks & QUIRK_HID_FN_LOCK) {1002 if (drvdata->quirks & QUIRK_HID_FN_LOCK) {
1030 drvdata->fn_lock = true;1003 drvdata->fn_lock = true;
1031 INIT_WORK(&drvdata->fn_lock_sync_work, asus_sync_fn_lock);1004 INIT_WORK(&drvdata->fn_lock_sync_work, asus_sync_fn_lock);
@@ -1104,15 +1077,6 @@ static int asus_input_mapping(struct hid_device *hdev, · 72 unmodified lines
1104 return -1;1077 return -1;
1105 }1078 }
1106 1079
1107 /*
1108 * Check and enable backlight only on devices with UsagePage ==
1109 * 0xff31 to avoid initializing the keyboard firmware multiple
1110 * times on devices with multiple HID descriptors but same
1111 * PID/VID.
1112 */
1113 if (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT)
1114 drvdata->enable_backlight = true;
1115
1116 set_bit(EV_REP, hi->input->evbit);1080 set_bit(EV_REP, hi->input->evbit);
1117 return 1;1081 return 1;
1118 }1082 }
@@ -1205,7 +1169,7 @@ static int __maybe_unused asus_resume(struct hid_device *hdev) { · 86 unmodified lines
1205 1169
1206 if (drvdata->kbd_backlight) {1170 if (drvdata->kbd_backlight) {
1207 const u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4,1171 const u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4,
1208 drvdata->kbd_backlight->cdev.brightness };1172 drvdata->kbd_backlight->brightness };
1209 ret = asus_kbd_set_report(hdev, buf, sizeof(buf));1173 ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
1210 if (ret < 0) {1174 if (ret < 0) {
1211 hid_err(hdev, "Asus failed to set keyboard backlight: %d\n", ret);1175 hid_err(hdev, "Asus failed to set keyboard backlight: %d\n", ret);
@@ -1229,8 +1193,11 @@ static int __maybe_unused asus_reset_resume(struct hid_device *hdev) · 17 unmodified lines
1229 1193
1230static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)1194static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
1231{1195{
1232 int ret;1196 struct hid_report_enum *rep_enum;
1233 struct asus_drvdata *drvdata;1197 struct asus_drvdata *drvdata;
1198 struct hid_report *rep;
1199 bool is_vendor = false;
1200 int ret;
1234 1201
1235 drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);1202 drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
1236 if (drvdata == NULL) {1203 if (drvdata == NULL) {
@@ -1314,12 +1281,30 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) · 77 unmodified lines
1314 return ret;1281 return ret;
1315 }1282 }
1316 1283
1284 /* Check for vendor for RGB init and handle generic devices properly. */
1285 rep_enum = &hdev->report_enum[HID_INPUT_REPORT];
1286 list_for_each_entry(rep, &rep_enum->report_list, list) {
1287 if ((rep->application & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR)
1288 is_vendor = true;
1289 }
1290
1317 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);1291 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
1318 if (ret) {1292 if (ret) {
1319 hid_err(hdev, "Asus hw start failed: %d\n", ret);1293 hid_err(hdev, "Asus hw start failed: %d\n", ret);
1320 return ret;1294 return ret;
1321 }1295 }
1322 1296
1297 if (is_vendor && (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) &&
1298 asus_kbd_register_leds(hdev))
1299 hid_warn(hdev, "Failed to initialize backlight.\n");
1300
1301 /*
1302 * For ROG keyboards, skip rename for consistency and ->input check as
1303 * some devices do not have inputs.
1304 */
1305 if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD)
1306 return 0;
1307
1323 /*1308 /*
1324 * Check that input registration succeeded. Checking that1309 * Check that input registration succeeded. Checking that
1325 * HID_CLAIMED_INPUT is set prevents a UAF when all input devices1310 * HID_CLAIMED_INPUT is set prevents a UAF when all input devices
@@ -1356,6 +1341,8 @@ static void asus_remove(struct hid_device *hdev) · 30 unmodified lines
1356 unsigned long flags;1341 unsigned long flags;
1357 1342
1358 if (drvdata->kbd_backlight) {1343 if (drvdata->kbd_backlight) {
1344 asus_hid_unregister_listener(&drvdata->kbd_backlight->listener);
1345
1359 spin_lock_irqsave(&drvdata->kbd_backlight->lock, flags);1346 spin_lock_irqsave(&drvdata->kbd_backlight->lock, flags);
1360 drvdata->kbd_backlight->removed = true;1347 drvdata->kbd_backlight->removed = true;
1361 spin_unlock_irqrestore(&drvdata->kbd_backlight->lock, flags);1348 spin_unlock_irqrestore(&drvdata->kbd_backlight->lock, flags);
@@ -1490,10 +1477,10 @@ static const struct hid_device_id asus_devices[] = { · 128 unmodified lines
1490 QUIRK_USE_KBD_BACKLIGHT },1477 QUIRK_USE_KBD_BACKLIGHT },
1491 { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,1478 { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
1492 USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD),1479 USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD),
1493 QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },1480 QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_ID1ID2_INIT },
1494 { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,1481 { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
1495 USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2),1482 USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2),
1496 QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_HID_FN_LOCK },1483 QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_HID_FN_LOCK | QUIRK_ROG_NKEY_ID1ID2_INIT },
1497 { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,1484 { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
1498 USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR),1485 USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR),
1499 QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },1486 QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
drivers/platform/mellanox/mlx-platform.c+484−0
@@ -6,6 +6,8 @@ · 5 unmodified lines
6 * Copyright (C) 2016-2018 Vadim Pasternak <vadimp@mellanox.com>6 * Copyright (C) 2016-2018 Vadim Pasternak <vadimp@mellanox.com>
7 */7 */
8 8
9#include <linux/array_size.h>
10#include <linux/bits.h>
9#include <linux/device.h>11#include <linux/device.h>
10#include <linux/dmi.h>12#include <linux/dmi.h>
11#include <linux/i2c.h>13#include <linux/i2c.h>
@@ -727,6 +729,16 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_psu_items_data[] = { · 715 unmodified lines
727 },729 },
728};730};
729 731
732/* Platform hotplug dgx data */
733static struct mlxreg_core_data mlxplat_mlxcpld_dgx_pdb_items_data[] = {
734 {
735 .label = "pdb1",
736 .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
737 .mask = BIT(0),
738 .hpdev.nr = MLXPLAT_CPLD_NR_NONE,
739 },
740};
741
730static struct mlxreg_core_data mlxplat_mlxcpld_default_pwr_items_data[] = {742static struct mlxreg_core_data mlxplat_mlxcpld_default_pwr_items_data[] = {
731 {743 {
732 .label = "pwr1",744 .label = "pwr1",
@@ -776,6 +788,15 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_pwr_ng800_items_data[] = · 43 unmodified lines
776 },788 },
777};789};
778 790
791static struct mlxreg_core_data mlxplat_mlxcpld_dgx_pwr_items_data[] = {
792 {
793 .label = "pwr1",
794 .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
795 .mask = BIT(0),
796 .hpdev.nr = MLXPLAT_CPLD_NR_NONE,
797 },
798};
799
779static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_items_data[] = {800static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_items_data[] = {
780 {801 {
781 .label = "fan1",802 .label = "fan1",
@@ -1399,6 +1420,45 @@ static struct mlxreg_core_item mlxplat_mlxcpld_ext_items[] = { · 617 unmodified lines
1399 }1420 }
1400};1421};
1401 1422
1423static struct mlxreg_core_item mlxplat_mlxcpld_ext_dgx_items[] = {
1424 {
1425 .data = mlxplat_mlxcpld_dgx_pdb_items_data,
1426 .aggr_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF,
1427 .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET,
1428 .mask = MLXPLAT_CPLD_PSU_MASK,
1429 .count = ARRAY_SIZE(mlxplat_mlxcpld_dgx_pdb_items_data),
1430 .inversed = 1,
1431 .health = false,
1432 },
1433 {
1434 .data = mlxplat_mlxcpld_dgx_pwr_items_data,
1435 .aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF,
1436 .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET,
1437 .mask = MLXPLAT_CPLD_PWR_MASK,
1438 .count = ARRAY_SIZE(mlxplat_mlxcpld_dgx_pwr_items_data),
1439 .inversed = 0,
1440 .health = false,
1441 },
1442 {
1443 .data = mlxplat_mlxcpld_default_ng_fan_items_data,
1444 .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
1445 .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET,
1446 .mask = MLXPLAT_CPLD_FAN_NG_MASK,
1447 .count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_fan_items_data),
1448 .inversed = 1,
1449 .health = false,
1450 },
1451 {
1452 .data = mlxplat_mlxcpld_default_asic_items_data,
1453 .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF,
1454 .reg = MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET,
1455 .mask = MLXPLAT_CPLD_ASIC_MASK,
1456 .count = ARRAY_SIZE(mlxplat_mlxcpld_default_asic_items_data),
1457 .inversed = 0,
1458 .health = true,
1459 },
1460};
1461
1402static struct mlxreg_core_item mlxplat_mlxcpld_ng800_items[] = {1462static struct mlxreg_core_item mlxplat_mlxcpld_ng800_items[] = {
1403 {1463 {
1404 .data = mlxplat_mlxcpld_default_ng_psu_items_data,1464 .data = mlxplat_mlxcpld_default_ng_psu_items_data,
@@ -1450,6 +1510,16 @@ struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_ext_data = { · 45 unmodified lines
1450 .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW | MLXPLAT_CPLD_LOW_AGGR_MASK_ASIC2,1510 .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW | MLXPLAT_CPLD_LOW_AGGR_MASK_ASIC2,
1451};1511};
1452 1512
1513static
1514struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_dgx_ext_data = {
1515 .items = mlxplat_mlxcpld_ext_dgx_items,
1516 .count = ARRAY_SIZE(mlxplat_mlxcpld_ext_dgx_items),
1517 .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
1518 .mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF | MLXPLAT_CPLD_AGGR_MASK_COMEX,
1519 .cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
1520 .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW | MLXPLAT_CPLD_LOW_AGGR_MASK_ASIC2,
1521};
1522
1453static1523static
1454struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_ng800_data = {1524struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_ng800_data = {
1455 .items = mlxplat_mlxcpld_ng800_items,1525 .items = mlxplat_mlxcpld_ng800_items,
@@ -4625,6 +4695,359 @@ static struct mlxreg_core_platform_data mlxplat_default_ng_regs_io_data = { · 3169 unmodified lines
4625 .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_regs_io_data),4695 .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_regs_io_data),
4626};4696};
4627 4697
4698/* Platform register access for next generation systems families data */
4699static struct mlxreg_core_data mlxplat_mlxcpld_dgx_ng_regs_io_data[] = {
4700 {
4701 .label = "cpld1_version",
4702 .reg = MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFFSET,
4703 .bit = GENMASK(7, 0),
4704 .mode = 0444,
4705 },
4706 {
4707 .label = "cpld2_version",
4708 .reg = MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFFSET,
4709 .bit = GENMASK(7, 0),
4710 .mode = 0444,
4711 },
4712 {
4713 .label = "cpld3_version",
4714 .reg = MLXPLAT_CPLD_LPC_REG_CPLD3_VER_OFFSET,
4715 .bit = GENMASK(7, 0),
4716 .mode = 0444,
4717 },
4718 {
4719 .label = "cpld4_version",
4720 .reg = MLXPLAT_CPLD_LPC_REG_CPLD4_VER_OFFSET,
4721 .bit = GENMASK(7, 0),
4722 .mode = 0444,
4723 },
4724 {
4725 .label = "cpld1_pn",
4726 .reg = MLXPLAT_CPLD_LPC_REG_CPLD1_PN_OFFSET,
4727 .bit = GENMASK(15, 0),
4728 .mode = 0444,
4729 .regnum = 2,
4730 },
4731 {
4732 .label = "cpld2_pn",
4733 .reg = MLXPLAT_CPLD_LPC_REG_CPLD2_PN_OFFSET,
4734 .bit = GENMASK(15, 0),
4735 .mode = 0444,
4736 .regnum = 2,
4737 },
4738 {
4739 .label = "cpld3_pn",
4740 .reg = MLXPLAT_CPLD_LPC_REG_CPLD3_PN_OFFSET,
4741 .bit = GENMASK(15, 0),
4742 .mode = 0444,
4743 .regnum = 2,
4744 },
4745 {
4746 .label = "cpld4_pn",
4747 .reg = MLXPLAT_CPLD_LPC_REG_CPLD4_PN_OFFSET,
4748 .bit = GENMASK(15, 0),
4749 .mode = 0444,
4750 .regnum = 2,
4751 },
4752 {
4753 .label = "cpld1_version_min",
4754 .reg = MLXPLAT_CPLD_LPC_REG_CPLD1_MVER_OFFSET,
4755 .bit = GENMASK(7, 0),
4756 .mode = 0444,
4757 },
4758 {
4759 .label = "cpld2_version_min",
4760 .reg = MLXPLAT_CPLD_LPC_REG_CPLD2_MVER_OFFSET,
4761 .bit = GENMASK(7, 0),
4762 .mode = 0444,
4763 },
4764 {
4765 .label = "cpld3_version_min",
4766 .reg = MLXPLAT_CPLD_LPC_REG_CPLD3_MVER_OFFSET,
4767 .bit = GENMASK(7, 0),
4768 .mode = 0444,
4769 },
4770 {
4771 .label = "cpld4_version_min",
4772 .reg = MLXPLAT_CPLD_LPC_REG_CPLD4_MVER_OFFSET,
4773 .bit = GENMASK(7, 0),
4774 .mode = 0444,
4775 },
4776 {
4777 .label = "asic_reset",
4778 .reg = MLXPLAT_CPLD_LPC_REG_RESET_GP2_OFFSET,
4779 .mask = GENMASK(7, 0) & ~BIT(3),
4780 .mode = 0200,
4781 },
4782 {
4783 .label = "reset_long_pb",
4784 .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
4785 .mask = GENMASK(7, 0) & ~BIT(0),
4786 .mode = 0444,
4787 },
4788 {
4789 .label = "reset_short_pb",
4790 .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
4791 .mask = GENMASK(7, 0) & ~BIT(1),
4792 .mode = 0444,
4793 },
4794 {
4795 .label = "reset_aux_pwr_or_ref",
4796 .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
4797 .mask = GENMASK(7, 0) & ~BIT(2),
4798 .mode = 0444,
4799 },
4800 {
4801 .label = "reset_swb_dc_dc_pwr_fail",
4802 .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
4803 .mask = GENMASK(7, 0) & ~BIT(3),
4804 .mode = 0444,
4805 },
4806 {
4807 .label = "reset_from_asic",
4808 .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
4809 .mask = GENMASK(7, 0) & ~BIT(5),
4810 .mode = 0444,
4811 },
4812 {
4813 .label = "reset_swb_wd",
4814 .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
4815 .mask = GENMASK(7, 0) & ~BIT(6),
4816 .mode = 0444,
4817 },
4818 {
4819 .label = "reset_asic_thermal",
4820 .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
4821 .mask = GENMASK(7, 0) & ~BIT(7),
4822 .mode = 0444,
4823 },
4824 {
4825 .label = "reset_sw_reset",
4826 .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET,
4827 .mask = GENMASK(7, 0) & ~BIT(0),
4828 .mode = 0444,
4829 },
4830 {
4831 .label = "reset_comex_pwr_fail",
4832 .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET,
4833 .mask = GENMASK(7, 0) & ~BIT(3),
4834 .mode = 0444,
4835 },
4836 {
4837 .label = "reset_platform",
4838 .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET,
4839 .mask = GENMASK(7, 0) & ~BIT(4),
4840 .mode = 0444,
4841 },
4842 {
4843 .label = "reset_soc",
4844 .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET,
4845 .mask = GENMASK(7, 0) & ~BIT(5),
4846 .mode = 0444,
4847 },
4848 {
4849 .label = "reset_comex_wd",
4850 .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET,
4851 .mask = GENMASK(7, 0) & ~BIT(6),
4852 .mode = 0444,
4853 },
4854 {
4855 .label = "reset_system",
4856 .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
4857 .mask = GENMASK(7, 0) & ~BIT(1),
4858 .mode = 0444,
4859 },
4860 {
4861 .label = "reset_sw_pwr_off",
4862 .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
4863 .mask = GENMASK(7, 0) & ~BIT(2),
4864 .mode = 0444,
4865 },
4866 {
4867 .label = "reset_comex_thermal",
4868 .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
4869 .mask = GENMASK(7, 0) & ~BIT(3),
4870 .mode = 0444,
4871 },
4872 {
4873 .label = "reset_reload_bios",
4874 .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
4875 .mask = GENMASK(7, 0) & ~BIT(5),
4876 .mode = 0444,
4877 },
4878 {
4879 .label = "reset_pdb_pwr_fail",
4880 .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
4881 .mask = GENMASK(7, 0) & ~BIT(6),
4882 .mode = 0444,
4883 },
4884 {
4885 .label = "pdb_reset_stby",
4886 .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET,
4887 .mask = GENMASK(7, 0) & ~BIT(0),
4888 .mode = 0200,
4889 },
4890 {
4891 .label = "pwr_cycle",
4892 .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET,
4893 .mask = GENMASK(7, 0) & ~BIT(2),
4894 .mode = 0200,
4895 },
4896 {
4897 .label = "pwr_down",
4898 .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET,
4899 .mask = GENMASK(7, 0) & ~BIT(3),
4900 .mode = 0200,
4901 },
4902 {
4903 .label = "deep_pwr_cycle",
4904 .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET,
4905 .mask = GENMASK(7, 0) & ~BIT(5),
4906 .mode = 0200,
4907 },
4908 {
4909 .label = "latch_reset",
4910 .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET,
4911 .mask = GENMASK(7, 0) & ~BIT(6),
4912 .mode = 0200,
4913 },
4914 {
4915 .label = "jtag_cap",
4916 .reg = MLXPLAT_CPLD_LPC_REG_FU_CAP_OFFSET,
4917 .mask = MLXPLAT_CPLD_FU_CAP_MASK,
4918 .bit = 1,
4919 .mode = 0444,
4920 },
4921 {
4922 .label = "jtag_enable",
4923 .reg = MLXPLAT_CPLD_LPC_REG_GP2_OFFSET,
4924 .mask = GENMASK(7, 0) & ~BIT(4),
4925 .mode = 0644,
4926 },
4927 {
4928 .label = "dbg1",
4929 .reg = MLXPLAT_CPLD_LPC_REG_DBG1_OFFSET,
4930 .bit = GENMASK(7, 0),
4931 .mode = 0644,
4932 },
4933 {
4934 .label = "dbg2",
4935 .reg = MLXPLAT_CPLD_LPC_REG_DBG2_OFFSET,
4936 .bit = GENMASK(7, 0),
4937 .mode = 0644,
4938 },
4939 {
4940 .label = "dbg3",
4941 .reg = MLXPLAT_CPLD_LPC_REG_DBG3_OFFSET,
4942 .bit = GENMASK(7, 0),
4943 .mode = 0644,
4944 },
4945 {
4946 .label = "dbg4",
4947 .reg = MLXPLAT_CPLD_LPC_REG_DBG4_OFFSET,
4948 .bit = GENMASK(7, 0),
4949 .mode = 0644,
4950 },
4951 {
4952 .label = "asic_health",
4953 .reg = MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET,
4954 .mask = MLXPLAT_CPLD_ASIC_MASK,
4955 .bit = 1,
4956 .mode = 0444,
4957 },
4958 {
4959 .label = "fan_dir",
4960 .reg = MLXPLAT_CPLD_LPC_REG_FAN_DIRECTION,
4961 .bit = GENMASK(7, 0),
4962 .mode = 0444,
4963 },
4964 {
4965 .label = "bios_safe_mode",
4966 .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET,
4967 .mask = GENMASK(7, 0) & ~BIT(4),
4968 .mode = 0444,
4969 },
4970 {
4971 .label = "bios_active_image",
4972 .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET,
4973 .mask = GENMASK(7, 0) & ~BIT(5),
4974 .mode = 0444,
4975 },
4976 {
4977 .label = "bios_auth_fail",
4978 .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET,
4979 .mask = GENMASK(7, 0) & ~BIT(6),
4980 .mode = 0444,
4981 },
4982 {
4983 .label = "bios_upgrade_fail",
4984 .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET,
4985 .mask = GENMASK(7, 0) & ~BIT(7),
4986 .mode = 0444,
4987 },
4988 {
4989 .label = "voltreg_update_status",
4990 .reg = MLXPLAT_CPLD_LPC_REG_GP0_RO_OFFSET,
4991 .mask = MLXPLAT_CPLD_VOLTREG_UPD_MASK,
4992 .bit = 5,
4993 .mode = 0444,
4994 },
4995 {
4996 .label = "pwr_converter_prog_en",
4997 .reg = MLXPLAT_CPLD_LPC_REG_GP0_OFFSET,
4998 .mask = GENMASK(7, 0) & ~BIT(0),
4999 .mode = 0644,
5000 .secured = 1,
5001 },
5002 {
5003 .label = "vpd_wp",
5004 .reg = MLXPLAT_CPLD_LPC_REG_GP0_OFFSET,
5005 .mask = GENMASK(7, 0) & ~BIT(3),
5006 .mode = 0644,
5007 },
5008 {
5009 .label = "pcie_asic_reset_dis",
5010 .reg = MLXPLAT_CPLD_LPC_REG_GP0_OFFSET,
5011 .mask = GENMASK(7, 0) & ~BIT(4),
5012 .mode = 0644,
5013 },
5014 {
5015 .label = "shutdown_unlock",
5016 .reg = MLXPLAT_CPLD_LPC_REG_GP0_OFFSET,
5017 .mask = GENMASK(7, 0) & ~BIT(5),
5018 .mode = 0644,
5019 },
5020 {
5021 .label = "config1",
5022 .reg = MLXPLAT_CPLD_LPC_REG_CONFIG1_OFFSET,
5023 .bit = GENMASK(7, 0),
5024 .mode = 0444,
5025 },
5026 {
5027 .label = "config2",
5028 .reg = MLXPLAT_CPLD_LPC_REG_CONFIG2_OFFSET,
5029 .bit = GENMASK(7, 0),
5030 .mode = 0444,
5031 },
5032 {
5033 .label = "config3",
5034 .reg = MLXPLAT_CPLD_LPC_REG_CONFIG3_OFFSET,
5035 .bit = GENMASK(7, 0),
5036 .mode = 0444,
5037 },
5038 {
5039 .label = "ufm_version",
5040 .reg = MLXPLAT_CPLD_LPC_REG_UFM_VERSION_OFFSET,
5041 .bit = GENMASK(7, 0),
5042 .mode = 0444,
5043 },
5044};
5045
5046static struct mlxreg_core_platform_data mlxplat_dgx_ng_regs_io_data = {
5047 .data = mlxplat_mlxcpld_dgx_ng_regs_io_data,
5048 .counter = ARRAY_SIZE(mlxplat_mlxcpld_dgx_ng_regs_io_data),
5049};
5050
4628/* Platform register access for modular systems families data */5051/* Platform register access for modular systems families data */
4629static struct mlxreg_core_data mlxplat_mlxcpld_modular_regs_io_data[] = {5052static struct mlxreg_core_data mlxplat_mlxcpld_modular_regs_io_data[] = {
4630 {5053 {
@@ -7239,6 +7662,32 @@ static int __init mlxplat_dmi_ng400_matched(const struct dmi_system_id *dmi) · 2608 unmodified lines
7239 return mlxplat_register_platform_device();7662 return mlxplat_register_platform_device();
7240}7663}
7241 7664
7665static int __init mlxplat_dmi_ng400_dgx_matched(const struct dmi_system_id *dmi)
7666{
7667 int i;
7668
7669 mlxplat_max_adap_num = MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM;
7670 mlxplat_mux_num = ARRAY_SIZE(mlxplat_default_mux_data);
7671 mlxplat_mux_data = mlxplat_default_mux_data;
7672 for (i = 0; i < mlxplat_mux_num; i++) {
7673 mlxplat_mux_data[i].values = mlxplat_msn21xx_channels;
7674 mlxplat_mux_data[i].n_values =
7675 ARRAY_SIZE(mlxplat_msn21xx_channels);
7676 }
7677 mlxplat_hotplug = &mlxplat_mlxcpld_dgx_ext_data;
7678 mlxplat_hotplug->deferred_nr =
7679 mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
7680 mlxplat_led = &mlxplat_default_ng_led_data;
7681 mlxplat_regs_io = &mlxplat_dgx_ng_regs_io_data;
7682 mlxplat_fan = &mlxplat_default_fan_data;
7683 for (i = 0; i < ARRAY_SIZE(mlxplat_mlxcpld_wd_set_type2); i++)
7684 mlxplat_wd_data[i] = &mlxplat_mlxcpld_wd_set_type2[i];
7685 mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data;
7686 mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_ng400;
7687
7688 return mlxplat_register_platform_device();
7689}
7690
7242static int __init mlxplat_dmi_modular_matched(const struct dmi_system_id *dmi)7691static int __init mlxplat_dmi_modular_matched(const struct dmi_system_id *dmi)
7243{7692{
7244 int i;7693 int i;
@@ -7323,6 +7772,27 @@ static int __init mlxplat_dmi_ng800_matched(const struct dmi_system_id *dmi) · 78 unmodified lines
7323 return mlxplat_register_platform_device();7772 return mlxplat_register_platform_device();
7324}7773}
7325 7774
7775static int __init mlxplat_dmi_ng800_dgx_matched(const struct dmi_system_id *dmi)
7776{
7777 int i;
7778
7779 mlxplat_max_adap_num = MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM;
7780 mlxplat_mux_num = ARRAY_SIZE(mlxplat_ng800_mux_data);
7781 mlxplat_mux_data = mlxplat_ng800_mux_data;
7782 mlxplat_hotplug = &mlxplat_mlxcpld_dgx_ext_data;
7783 mlxplat_hotplug->deferred_nr =
7784 mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
7785 mlxplat_led = &mlxplat_default_ng_led_data;
7786 mlxplat_regs_io = &mlxplat_dgx_ng_regs_io_data;
7787 mlxplat_fan = &mlxplat_default_fan_data;
7788 for (i = 0; i < ARRAY_SIZE(mlxplat_mlxcpld_wd_set_type2); i++)
7789 mlxplat_wd_data[i] = &mlxplat_mlxcpld_wd_set_type2[i];
7790 mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data;
7791 mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_ng400;
7792
7793 return mlxplat_register_platform_device();
7794}
7795
7326static int __init mlxplat_dmi_l1_switch_matched(const struct dmi_system_id *dmi)7796static int __init mlxplat_dmi_l1_switch_matched(const struct dmi_system_id *dmi)
7327{7797{
7328 int i;7798 int i;
@@ -7458,6 +7928,13 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { · 129 unmodified lines
7458 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI142"),7928 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI142"),
7459 },7929 },
7460 },7930 },
7931 {
7932 .callback = mlxplat_dmi_ng400_dgx_matched,
7933 .matches = {
7934 DMI_MATCH(DMI_BOARD_NAME, "VMOD0010"),
7935 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI173"),
7936 },
7937 },
7461 {7938 {
7462 .callback = mlxplat_dmi_ng400_matched,7939 .callback = mlxplat_dmi_ng400_matched,
7463 .matches = {7940 .matches = {
@@ -7470,6 +7947,13 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { · 6 unmodified lines
7470 DMI_MATCH(DMI_BOARD_NAME, "VMOD0011"),7947 DMI_MATCH(DMI_BOARD_NAME, "VMOD0011"),
7471 },7948 },
7472 },7949 },
7950 {
7951 .callback = mlxplat_dmi_ng800_dgx_matched,
7952 .matches = {
7953 DMI_MATCH(DMI_BOARD_NAME, "VMOD0013"),
7954 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI174"),
7955 },
7956 },
7473 {7957 {
7474 .callback = mlxplat_dmi_ng800_matched,7958 .callback = mlxplat_dmi_ng800_matched,
7475 .matches = {7959 .matches = {
drivers/platform/surface/surface_aggregator_registry.c+18−0
@@ -406,6 +406,22 @@ static const struct software_node *ssam_node_group_sp9_5g[] = { · 405 unmodified lines
406 NULL,406 NULL,
407};407};
408 408
409/* Devices for Surface Pro 11 (ARM/QCOM) */
410static const struct software_node *ssam_node_group_sp11[] = {
411 &ssam_node_root,
412 &ssam_node_hub_kip,
413 &ssam_node_bat_ac,
414 &ssam_node_bat_main,
415 &ssam_node_tmp_sensors,
416 &ssam_node_hid_kip_keyboard,
417 &ssam_node_hid_kip_penstash,
418 &ssam_node_hid_kip_touchpad,
419 &ssam_node_hid_kip_fwupd,
420 &ssam_node_hid_sam_sensors,
421 &ssam_node_kip_tablet_switch,
422 NULL,
423};
424
409/* -- SSAM platform/meta-hub driver. ---------------------------------------- */425/* -- SSAM platform/meta-hub driver. ---------------------------------------- */
410 426
411static const struct acpi_device_id ssam_platform_hub_acpi_match[] = {427static const struct acpi_device_id ssam_platform_hub_acpi_match[] = {
@@ -482,6 +498,8 @@ MODULE_DEVICE_TABLE(acpi, ssam_platform_hub_acpi_match); · 70 unmodified lines
482static const struct of_device_id ssam_platform_hub_of_match[] __maybe_unused = {498static const struct of_device_id ssam_platform_hub_of_match[] __maybe_unused = {
483 /* Surface Pro 9 5G (ARM/QCOM) */499 /* Surface Pro 9 5G (ARM/QCOM) */
484 { .compatible = "microsoft,arcata", (void *)ssam_node_group_sp9_5g },500 { .compatible = "microsoft,arcata", (void *)ssam_node_group_sp9_5g },
501 /* Surface Pro 11 (ARM/QCOM) */
502 { .compatible = "microsoft,denali", (void *)ssam_node_group_sp11 },
485 /* Surface Laptop 7 */503 /* Surface Laptop 7 */
486 { .compatible = "microsoft,romulus13", (void *)ssam_node_group_sl7 },504 { .compatible = "microsoft,romulus13", (void *)ssam_node_group_sl7 },
487 { .compatible = "microsoft,romulus15", (void *)ssam_node_group_sl7 },505 { .compatible = "microsoft,romulus15", (void *)ssam_node_group_sl7 },
drivers/platform/surface/surfacepro3_button.c+5−6
@@ -10,6 +10,7 @@ · 9 unmodified lines
10#include <linux/kernel.h>10#include <linux/kernel.h>
11#include <linux/module.h>11#include <linux/module.h>
12#include <linux/init.h>12#include <linux/init.h>
13#include <linux/string.h>
13#include <linux/types.h>14#include <linux/types.h>
14#include <linux/input.h>15#include <linux/input.h>
15#include <linux/acpi.h>16#include <linux/acpi.h>
@@ -189,7 +190,6 @@ static int surface_button_add(struct acpi_device *device) · 173 unmodified lines
189 struct surface_button *button;190 struct surface_button *button;
190 struct input_dev *input;191 struct input_dev *input;
191 const char *hid = acpi_device_hid(device);192 const char *hid = acpi_device_hid(device);
192 char *name;
193 int error;193 int error;
194 194
195 if (strncmp(acpi_device_bid(device), SURFACE_BUTTON_OBJ_NAME,195 if (strncmp(acpi_device_bid(device), SURFACE_BUTTON_OBJ_NAME,
@@ -210,11 +210,10 @@ static int surface_button_add(struct acpi_device *device) · 14 unmodified lines
210 goto err_free_button;210 goto err_free_button;
211 }211 }
212 212
213 name = acpi_device_name(device);213 strscpy(acpi_device_name(device), SURFACE_BUTTON_DEVICE_NAME);
214 strcpy(name, SURFACE_BUTTON_DEVICE_NAME);
215 snprintf(button->phys, sizeof(button->phys), "%s/buttons", hid);214 snprintf(button->phys, sizeof(button->phys), "%s/buttons", hid);
216 215
217 input->name = name;216 input->name = acpi_device_name(device);
218 input->phys = button->phys;217 input->phys = button->phys;
219 input->id.bustype = BUS_HOST;218 input->id.bustype = BUS_HOST;
220 input->dev.parent = &device->dev;219 input->dev.parent = &device->dev;
@@ -228,8 +227,8 @@ static int surface_button_add(struct acpi_device *device) · 7 unmodified lines
228 goto err_free_input;227 goto err_free_input;
229 228
230 device_init_wakeup(&device->dev, true);229 device_init_wakeup(&device->dev, true);
231 dev_info(&device->dev,230 dev_info(&device->dev, "%s [%s]\n", acpi_device_name(device),
232 "%s [%s]\n", name, acpi_device_bid(device));231 acpi_device_bid(device));
233 return 0;232 return 0;
234 233
235 err_free_input:234 err_free_input:
drivers/platform/wmi/Kconfig+3−0
@@ -6,6 +6,7 @@ · 5 unmodified lines
6menuconfig ACPI_WMI6menuconfig ACPI_WMI
7 tristate "ACPI-WMI support"7 tristate "ACPI-WMI support"
8 depends on ACPI && X868 depends on ACPI && X86
9 select NLS
9 help10 help
10 This option enables support for the ACPI-WMI driver core.11 This option enables support for the ACPI-WMI driver core.
11 12
@@ -31,4 +32,6 @@ config ACPI_WMI_LEGACY_DEVICE_NAMES · 19 unmodified lines
31 userspace applications but will cause the registration of WMI devices with32 userspace applications but will cause the registration of WMI devices with
32 the same GUID to fail in some corner cases.33 the same GUID to fail in some corner cases.
33 34
35source "drivers/platform/wmi/tests/Kconfig"
36
34endif # ACPI_WMI37endif # ACPI_WMI
drivers/platform/wmi/Makefile+4−1
@@ -4,5 +4,8 @@ · 3 unmodified lines
4# ACPI WMI core4# ACPI WMI core
5#5#
6 6
7wmi-y := core.o7wmi-y := core.o marshalling.o string.o
8obj-$(CONFIG_ACPI_WMI) += wmi.o8obj-$(CONFIG_ACPI_WMI) += wmi.o
9
10# Unit tests
11obj-y += tests/
drivers/platform/wmi/core.c+156−4
@@ -23,6 +23,7 @@ · 22 unmodified lines
23#include <linux/idr.h>23#include <linux/idr.h>
24#include <linux/init.h>24#include <linux/init.h>
25#include <linux/kernel.h>25#include <linux/kernel.h>
26#include <linux/limits.h>
26#include <linux/module.h>27#include <linux/module.h>
27#include <linux/platform_device.h>28#include <linux/platform_device.h>
28#include <linux/rwsem.h>29#include <linux/rwsem.h>
@@ -33,6 +34,8 @@ · 4 unmodified lines
33#include <linux/wmi.h>34#include <linux/wmi.h>
34#include <linux/fs.h>35#include <linux/fs.h>
35 36
37#include "internal.h"
38
36MODULE_AUTHOR("Carlos Corbacho");39MODULE_AUTHOR("Carlos Corbacho");
37MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");40MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
38MODULE_LICENSE("GPL");41MODULE_LICENSE("GPL");
@@ -302,7 +305,7 @@ acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, u32 method · 263 unmodified lines
302EXPORT_SYMBOL_GPL(wmi_evaluate_method);305EXPORT_SYMBOL_GPL(wmi_evaluate_method);
303 306
304/**307/**
305 * wmidev_evaluate_method - Evaluate a WMI method308 * wmidev_evaluate_method - Evaluate a WMI method (deprecated)
306 * @wdev: A wmi bus device from a driver309 * @wdev: A wmi bus device from a driver
307 * @instance: Instance index310 * @instance: Instance index
308 * @method_id: Method ID to call311 * @method_id: Method ID to call
@@ -360,6 +363,70 @@ acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 met · 51 unmodified lines
360}363}
361EXPORT_SYMBOL_GPL(wmidev_evaluate_method);364EXPORT_SYMBOL_GPL(wmidev_evaluate_method);
362 365
366/**
367 * wmidev_invoke_method - Invoke a WMI method
368 * @wdev: A wmi bus device from a driver
369 * @instance: Instance index
370 * @method_id: Method ID to call
371 * @in: Mandatory WMI buffer containing input for the method call
372 * @out: Optional WMI buffer to return the method results
373 *
374 * Invoke a WMI method, the caller must free the resulting data inside @out.
375 * Said data is guaranteed to be aligned on a 8-byte boundary.
376 *
377 * Return: 0 on success or negative error code on failure.
378 */
379int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id,
380 const struct wmi_buffer *in, struct wmi_buffer *out)
381{
382 struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev);
383 struct acpi_buffer aout = { ACPI_ALLOCATE_BUFFER, NULL };
384 struct acpi_buffer ain;
385 union acpi_object *obj;
386 acpi_status status;
387 int ret;
388
389 if (wblock->gblock.flags & ACPI_WMI_STRING) {
390 ret = wmi_marshal_string(in, &ain);
391 if (ret < 0)
392 return ret;
393 } else {
394 if (in->length > U32_MAX)
395 return -E2BIG;
396
397 ain.length = in->length;
398 ain.pointer = in->data;
399 }
400
401 if (out)
402 status = wmidev_evaluate_method(wdev, instance, method_id, &ain, &aout);
403 else
404 status = wmidev_evaluate_method(wdev, instance, method_id, &ain, NULL);
405
406 if (wblock->gblock.flags & ACPI_WMI_STRING)
407 kfree(ain.pointer);
408
409 if (ACPI_FAILURE(status))
410 return -EIO;
411
412 if (!out)
413 return 0;
414
415 obj = aout.pointer;
416 if (!obj) {
417 out->length = 0;
418 out->data = ZERO_SIZE_PTR;
419
420 return 0;
421 }
422
423 ret = wmi_unmarshal_acpi_object(obj, out);
424 kfree(obj);
425
426 return ret;
427}
428EXPORT_SYMBOL_GPL(wmidev_invoke_method);
429
363static acpi_status __query_block(struct wmi_block *wblock, u8 instance,430static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
364 struct acpi_buffer *out)431 struct acpi_buffer *out)
365{432{
@@ -432,7 +499,7 @@ acpi_status wmi_query_block(const char *guid_string, u8 instance, · 66 unmodified lines
432EXPORT_SYMBOL_GPL(wmi_query_block);499EXPORT_SYMBOL_GPL(wmi_query_block);
433 500
434/**501/**
435 * wmidev_block_query - Return contents of a WMI block502 * wmidev_block_query - Return contents of a WMI block (deprectated)
436 * @wdev: A wmi bus device from a driver503 * @wdev: A wmi bus device from a driver
437 * @instance: Instance index504 * @instance: Instance index
438 *505 *
@@ -452,6 +519,33 @@ union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance) · 13 unmodified lines
452}519}
453EXPORT_SYMBOL_GPL(wmidev_block_query);520EXPORT_SYMBOL_GPL(wmidev_block_query);
454 521
522/**
523 * wmidev_query_block - Return contents of a WMI data block
524 * @wdev: A wmi bus device from a driver
525 * @instance: Instance index
526 * @out: WMI buffer to fill
527 *
528 * Query a WMI data block, the caller must free the resulting data inside @out.
529 * Said data is guaranteed to be aligned on a 8-byte boundary.
530 *
531 * Return: 0 on success or a negative error code on failure.
532 */
533int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out)
534{
535 union acpi_object *obj;
536 int ret;
537
538 obj = wmidev_block_query(wdev, instance);
539 if (!obj)
540 return -EIO;
541
542 ret = wmi_unmarshal_acpi_object(obj, out);
543 kfree(obj);
544
545 return ret;
546}
547EXPORT_SYMBOL_GPL(wmidev_query_block);
548
455/**549/**
456 * wmi_set_block - Write to a WMI block (deprecated)550 * wmi_set_block - Write to a WMI block (deprecated)
457 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba551 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
@@ -486,7 +580,7 @@ acpi_status wmi_set_block(const char *guid_string, u8 instance, const struct acp · 28 unmodified lines
486EXPORT_SYMBOL_GPL(wmi_set_block);580EXPORT_SYMBOL_GPL(wmi_set_block);
487 581
488/**582/**
489 * wmidev_block_set - Write to a WMI block583 * wmidev_block_set - Write to a WMI block (deprecated)
490 * @wdev: A wmi bus device from a driver584 * @wdev: A wmi bus device from a driver
491 * @instance: Instance index585 * @instance: Instance index
492 * @in: Buffer containing new values for the data block586 * @in: Buffer containing new values for the data block
@@ -535,6 +629,46 @@ acpi_status wmidev_block_set(struct wmi_device *wdev, u8 instance, const struct · 42 unmodified lines
535}629}
536EXPORT_SYMBOL_GPL(wmidev_block_set);630EXPORT_SYMBOL_GPL(wmidev_block_set);
537 631
632/**
633 * wmidev_set_block - Write to a WMI data block
634 * @wdev: A wmi bus device from a driver
635 * @instance: Instance index
636 * @in: WMI buffer containing new values for the data block
637 *
638 * Write the content of @in into a WMI data block.
639 *
640 * Return: 0 on success or negative error code on failure.
641 */
642int wmidev_set_block(struct wmi_device *wdev, u8 instance, const struct wmi_buffer *in)
643{
644 struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev);
645 struct acpi_buffer buffer;
646 acpi_status status;
647 int ret;
648
649 if (wblock->gblock.flags & ACPI_WMI_STRING) {
650 ret = wmi_marshal_string(in, &buffer);
651 if (ret < 0)
652 return ret;
653 } else {
654 if (in->length > U32_MAX)
655 return -E2BIG;
656
657 buffer.length = in->length;
658 buffer.pointer = in->data;
659 }
660
661 status = wmidev_block_set(wdev, instance, &buffer);
662 if (wblock->gblock.flags & ACPI_WMI_STRING)
663 kfree(buffer.pointer);
664
665 if (ACPI_FAILURE(status))
666 return -EIO;
667
668 return 0;
669}
670EXPORT_SYMBOL_GPL(wmidev_set_block);
671
538/**672/**
539 * wmi_install_notify_handler - Register handler for WMI events (deprecated)673 * wmi_install_notify_handler - Register handler for WMI events (deprecated)
540 * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba674 * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
@@ -862,7 +996,7 @@ static int wmi_dev_probe(struct device *dev) · 321 unmodified lines
862 return -ENODEV;996 return -ENODEV;
863 }997 }
864 998
865 if (wdriver->notify) {999 if (wdriver->notify || wdriver->notify_new) {
866 if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && !wdriver->no_notify_data)1000 if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && !wdriver->no_notify_data)
867 return -ENODEV;1001 return -ENODEV;
868 }1002 }
@@ -1221,6 +1355,8 @@ static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj · 352 unmodified lines
1221static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj)1355static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj)
1222{1356{
1223 struct wmi_driver *driver = to_wmi_driver(wblock->dev.dev.driver);1357 struct wmi_driver *driver = to_wmi_driver(wblock->dev.dev.driver);
1358 struct wmi_buffer buffer;
1359 int ret;
1224 1360
1225 if (!obj && !driver->no_notify_data) {1361 if (!obj && !driver->no_notify_data) {
1226 dev_warn(&wblock->dev.dev, "Event contains no event data\n");1362 dev_warn(&wblock->dev.dev, "Event contains no event data\n");
@@ -1229,6 +1365,22 @@ static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj) · 2 unmodified lines
1229 1365
1230 if (driver->notify)1366 if (driver->notify)
1231 driver->notify(&wblock->dev, obj);1367 driver->notify(&wblock->dev, obj);
1368
1369 if (driver->notify_new) {
1370 if (!obj) {
1371 driver->notify_new(&wblock->dev, NULL);
1372 return;
1373 }
1374
1375 ret = wmi_unmarshal_acpi_object(obj, &buffer);
1376 if (ret < 0) {
1377 dev_warn(&wblock->dev.dev, "Failed to unmarshal event data: %d\n", ret);
1378 return;
1379 }
1380
1381 driver->notify_new(&wblock->dev, &buffer);
1382 kfree(buffer.data);
1383 }
1232}1384}
1233 1385
1234static int wmi_notify_device(struct device *dev, void *data)1386static int wmi_notify_device(struct device *dev, void *data)
drivers/platform/wmi/internal.h+17−0
@@ -0,0 +1,17 @@
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*
3 * Internal interfaces used by the WMI core.
4 *
5 * Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de>
6 */
7
8#ifndef _WMI_INTERNAL_H_
9#define _WMI_INTERNAL_H_
10
11union acpi_object;
12struct wmi_buffer;
13
14int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer);
15int wmi_marshal_string(const struct wmi_buffer *buffer, struct acpi_buffer *out);
16
17#endif /* _WMI_INTERNAL_H_ */
drivers/platform/wmi/marshalling.c+247−0
@@ -0,0 +1,247 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * ACPI-WMI buffer marshalling.
4 *
5 * Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de>
6 */
7
8#include <linux/acpi.h>
9#include <linux/align.h>
10#include <linux/math.h>
11#include <linux/overflow.h>
12#include <linux/slab.h>
13#include <linux/unaligned.h>
14#include <linux/wmi.h>
15
16#include <kunit/visibility.h>
17
18#include "internal.h"
19
20static int wmi_adjust_buffer_length(size_t *length, const union acpi_object *obj)
21{
22 size_t alignment, size;
23
24 switch (obj->type) {
25 case ACPI_TYPE_INTEGER:
26 /*
27 * Integers are threated as 32 bit even if the ACPI DSDT
28 * declares 64 bit integer width.
29 */
30 alignment = 4;
31 size = sizeof(u32);
32 break;
33 case ACPI_TYPE_STRING:
34 /*
35 * Strings begin with a single little-endian 16-bit field containing
36 * the string length in bytes and are encoded as UTF-16LE with a terminating
37 * nul character.
38 */
39 if (obj->string.length + 1 > U16_MAX / 2)
40 return -EOVERFLOW;
41
42 alignment = 2;
43 size = struct_size_t(struct wmi_string, chars, obj->string.length + 1);
44 break;
45 case ACPI_TYPE_BUFFER:
46 /*
47 * Buffers are copied as-is.
48 */
49 alignment = 1;
50 size = obj->buffer.length;
51 break;
52 default:
53 return -EPROTO;
54 }
55
56 *length = size_add(ALIGN(*length, alignment), size);
57
58 return 0;
59}
60
61static int wmi_obj_get_buffer_length(const union acpi_object *obj, size_t *length)
62{
63 size_t total = 0;
64 int ret;
65
66 if (obj->type == ACPI_TYPE_PACKAGE) {
67 for (int i = 0; i < obj->package.count; i++) {
68 ret = wmi_adjust_buffer_length(&total, &obj->package.elements[i]);
69 if (ret < 0)
70 return ret;
71 }
72 } else {
73 ret = wmi_adjust_buffer_length(&total, obj);
74 if (ret < 0)
75 return ret;
76 }
77
78 *length = total;
79
80 return 0;
81}
82
83static int wmi_obj_transform_simple(const union acpi_object *obj, u8 *buffer, size_t *consumed)
84{
85 struct wmi_string *string;
86 size_t length;
87 __le32 value;
88 u8 *aligned;
89
90 switch (obj->type) {
91 case ACPI_TYPE_INTEGER:
92 aligned = PTR_ALIGN(buffer, 4);
93 length = sizeof(value);
94
95 value = cpu_to_le32(obj->integer.value);
96 memcpy(aligned, &value, length);
97 break;
98 case ACPI_TYPE_STRING:
99 aligned = PTR_ALIGN(buffer, 2);
100 string = (struct wmi_string *)aligned;
101 length = struct_size(string, chars, obj->string.length + 1);
102
103 /* We do not have to worry about unaligned accesses here as the WMI
104 * string will already be aligned on a two-byte boundary.
105 */
106 string->length = cpu_to_le16((obj->string.length + 1) * 2);
107 for (int i = 0; i < obj->string.length; i++)
108 string->chars[i] = cpu_to_le16(obj->string.pointer[i]);
109
110 /*
111 * The Windows WMI-ACPI driver always emits a terminating nul character,
112 * so we emulate this behavior here as well.
113 */
114 string->chars[obj->string.length] = '\0';
115 break;
116 case ACPI_TYPE_BUFFER:
117 aligned = buffer;
118 length = obj->buffer.length;
119
120 memcpy(aligned, obj->buffer.pointer, length);
121 break;
122 default:
123 return -EPROTO;
124 }
125
126 *consumed = (aligned - buffer) + length;
127
128 return 0;
129}
130
131static int wmi_obj_transform(const union acpi_object *obj, u8 *buffer)
132{
133 size_t consumed;
134 int ret;
135
136 if (obj->type == ACPI_TYPE_PACKAGE) {
137 for (int i = 0; i < obj->package.count; i++) {
138 ret = wmi_obj_transform_simple(&obj->package.elements[i], buffer,
139 &consumed);
140 if (ret < 0)
141 return ret;
142
143 buffer += consumed;
144 }
145 } else {
146 ret = wmi_obj_transform_simple(obj, buffer, &consumed);
147 if (ret < 0)
148 return ret;
149 }
150
151 return 0;
152}
153
154int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer)
155{
156 size_t length, alloc_length;
157 u8 *data;
158 int ret;
159
160 ret = wmi_obj_get_buffer_length(obj, &length);
161 if (ret < 0)
162 return ret;
163
164 if (ARCH_KMALLOC_MINALIGN < 8) {
165 /*
166 * kmalloc() guarantees that the alignment of the resulting memory allocation is at
167 * least the largest power-of-two divisor of the allocation size. The WMI buffer
168 * data needs to be aligned on a 8 byte boundary to properly support 64-bit WMI
169 * integers, so we have to round the allocation size to the next multiple of 8.
170 */
171 alloc_length = round_up(length, 8);
172 } else {
173 alloc_length = length;
174 }
175
176 data = kzalloc(alloc_length, GFP_KERNEL);
177 if (!data)
178 return -ENOMEM;
179
180 ret = wmi_obj_transform(obj, data);
181 if (ret < 0) {
182 kfree(data);
183 return ret;
184 }
185
186 buffer->length = length;
187 buffer->data = data;
188
189 return 0;
190}
191EXPORT_SYMBOL_IF_KUNIT(wmi_unmarshal_acpi_object);
192
193int wmi_marshal_string(const struct wmi_buffer *buffer, struct acpi_buffer *out)
194{
195 const struct wmi_string *string;
196 u16 length, value;
197 size_t chars;
198 char *str;
199
200 if (buffer->length < sizeof(*string))
201 return -ENODATA;
202
203 string = buffer->data;
204 length = get_unaligned_le16(&string->length);
205 if (buffer->length < sizeof(*string) + length)
206 return -ENODATA;
207
208 /* Each character needs to be 16 bits long */
209 if (length % 2)
210 return -EINVAL;
211
212 chars = length / 2;
213 str = kmalloc(chars + 1, GFP_KERNEL);
214 if (!str)
215 return -ENOMEM;
216
217 for (int i = 0; i < chars; i++) {
218 value = get_unaligned_le16(&string->chars[i]);
219
220 /* ACPI only accepts ASCII strings */
221 if (value > 0x7F) {
222 kfree(str);
223 return -EINVAL;
224 }
225
226 str[i] = value & 0xFF;
227
228 /*
229 * ACPI strings should only contain a single nul character at the end.
230 * Because of this we must not copy any padding from the WMI string.
231 */
232 if (!value) {
233 /* ACPICA wants the length of the string without the nul character */
234 out->length = i;
235 out->pointer = str;
236 return 0;
237 }
238 }
239
240 str[chars] = '\0';
241
242 out->length = chars;
243 out->pointer = str;
244
245 return 0;
246}
247EXPORT_SYMBOL_IF_KUNIT(wmi_marshal_string);
drivers/platform/wmi/string.c+92−0
@@ -0,0 +1,92 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * WMI string utility functions.
4 *
5 * Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de>
6 */
7
8#include <linux/build_bug.h>
9#include <linux/compiler_types.h>
10#include <linux/err.h>
11#include <linux/export.h>
12#include <linux/nls.h>
13#include <linux/limits.h>
14#include <linux/types.h>
15#include <linux/wmi.h>
16
17#include <asm/byteorder.h>
18
19static_assert(sizeof(__le16) == sizeof(wchar_t));
20
21/**
22 * wmi_string_to_utf8s - Convert a WMI string into a UTF8 string.
23 * @str: WMI string representation
24 * @dst: Buffer to fill with UTF8 characters
25 * @length: Length of the destination buffer
26 *
27 * Convert as WMI string into a standard UTF8 string. The conversion will stop
28 * once a NUL character is detected or when the buffer is full. Any invalid UTF16
29 * characters will be ignored. The resulting UTF8 string will always be NUL-terminated
30 * when this function returns successfully.
31 *
32 * Return: Length of the resulting UTF8 string or negative errno code on failure.
33 */
34ssize_t wmi_string_to_utf8s(const struct wmi_string *str, u8 *dst, size_t length)
35{
36 /* Contains the maximum number of UTF16 code points to read */
37 int inlen = le16_to_cpu(str->length) / 2;
38 int ret;
39
40 if (length < 1)
41 return -EINVAL;
42
43 /* We must leave room for the NUL character at the end of the destination buffer */
44 ret = utf16s_to_utf8s((__force const wchar_t *)str->chars, inlen, UTF16_LITTLE_ENDIAN, dst,
45 length - 1);
46 if (ret < 0)
47 return ret;
48
49 dst[ret] = '\0';
50
51 return ret;
52}
53EXPORT_SYMBOL_GPL(wmi_string_to_utf8s);
54
55/**
56 * wmi_string_from_utf8s - Convert a UTF8 string into a WMI string.
57 * @str: WMI string representation
58 * @max_chars: Maximum number of UTF16 code points to store inside the WMI string
59 * @src: UTF8 string to convert
60 * @src_length: Length of the source string without any trailing NUL-characters
61 *
62 * Convert a UTF8 string into a WMI string. The conversion will stop when the WMI string is
63 * full. The resulting WMI string will always be NUL-terminated and have its length field set
64 * to and appropriate value when this function returns successfully.
65 *
66 * Return: Number of UTF16 code points inside the WMI string or negative errno code on failure.
67 */
68ssize_t wmi_string_from_utf8s(struct wmi_string *str, size_t max_chars, const u8 *src,
69 size_t src_length)
70{
71 size_t str_length;
72 int ret;
73
74 if (max_chars < 1)
75 return -EINVAL;
76
77 /* We must leave room for the NUL character at the end of the WMI string */
78 ret = utf8s_to_utf16s(src, src_length, UTF16_LITTLE_ENDIAN, (__force wchar_t *)str->chars,
79 max_chars - 1);
80 if (ret < 0)
81 return ret;
82
83 str_length = (ret + 1) * sizeof(u16);
84 if (str_length > U16_MAX)
85 return -EOVERFLOW;
86
87 str->length = cpu_to_le16(str_length);
88 str->chars[ret] = '\0';
89
90 return ret;
91}
92EXPORT_SYMBOL_GPL(wmi_string_from_utf8s);
drivers/platform/wmi/tests/Kconfig+27−0
@@ -0,0 +1,27 @@
1# SPDX-License-Identifier: GPL-2.0-or-later
2#
3# ACPI WMI KUnit tests
4#
5
6config ACPI_WMI_MARSHALLING_KUNIT_TEST
7 tristate "KUnit Test for ACPI-WMI marshalling" if !KUNIT_ALL_TESTS
8 depends on KUNIT
9 default KUNIT_ALL_TESTS
10 help
11 This builds unit tests for the ACPI-WMI marshalling code.
12
13 For more information on KUnit and unit tests in general, please refer
14 to the KUnit documentation in Documentation/dev-tools/kunit/.
15
16 If unsure, say N.
17
18config ACPI_WMI_STRING_KUNIT_TEST
19 tristate "KUnit Test for ACPI-WMI string conversion" if !KUNIT_ALL_TESTS
20 depends on KUNIT
21 default KUNIT_ALL_TESTS
22 help
23 This builds unit tests for the ACPI-WMI string conversion code.
24 For more information on KUnit and unit tests in general, please refer
25 to the KUnit documentation in Documentation/dev-tools/kunit/.
26
27 If unsure, say N.
drivers/platform/wmi/tests/Makefile+11−0
@@ -0,0 +1,11 @@
1# SPDX-License-Identifier: GPL-2.0-or-later
2#
3# Makefile for linux/drivers/platform/x86/wmi/tests
4# ACPI WMI KUnit tests
5#
6
7wmi_marshalling_kunit-y := marshalling_kunit.o
8obj-$(CONFIG_ACPI_WMI_MARSHALLING_KUNIT_TEST) += wmi_marshalling_kunit.o
9
10wmi_string_kunit-y := string_kunit.o
11obj-$(CONFIG_ACPI_WMI_STRING_KUNIT_TEST) += wmi_string_kunit.o
drivers/platform/wmi/tests/marshalling_kunit.c+452−0
@@ -0,0 +1,452 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * KUnit test for the ACPI-WMI marshalling code.
4 *
5 * Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de>
6 */
7
8#include <linux/acpi.h>
9#include <linux/align.h>
10#include <linux/module.h>
11#include <linux/slab.h>
12#include <linux/string.h>
13#include <linux/types.h>
14#include <linux/wmi.h>
15
16#include <kunit/resource.h>
17#include <kunit/test.h>
18
19#include "../internal.h"
20
21struct wmi_acpi_param {
22 const char *name;
23 const union acpi_object obj;
24 const struct wmi_buffer buffer;
25};
26
27struct wmi_string_param {
28 const char *name;
29 const char *string;
30 const struct wmi_buffer buffer;
31};
32
33struct wmi_invalid_acpi_param {
34 const char *name;
35 const union acpi_object obj;
36};
37
38struct wmi_invalid_string_param {
39 const char *name;
40 const struct wmi_buffer buffer;
41};
42
43/* 0xdeadbeef */
44static u8 expected_single_integer[] = {
45 0xef, 0xbe, 0xad, 0xde,
46};
47
48/* "TEST" */
49static u8 expected_single_string[] = {
50 0x0a, 0x00, 0x54, 0x00, 0x45, 0x00, 0x53, 0x00, 0x54, 0x00, 0x00, 0x00,
51};
52
53static u8 test_buffer[] = {
54 0xab, 0xcd,
55};
56
57static u8 expected_single_buffer[] = {
58 0xab, 0xcd,
59};
60
61static union acpi_object simple_package_elements[] = {
62 {
63 .buffer = {
64 .type = ACPI_TYPE_BUFFER,
65 .length = sizeof(test_buffer),
66 .pointer = test_buffer,
67 },
68 },
69 {
70 .integer = {
71 .type = ACPI_TYPE_INTEGER,
72 .value = 0x01020304,
73 },
74 },
75};
76
77static u8 expected_simple_package[] = {
78 0xab, 0xcd,
79 0x00, 0x00,
80 0x04, 0x03, 0x02, 0x01,
81};
82
83static u8 test_small_buffer[] = {
84 0xde,
85};
86
87static union acpi_object complex_package_elements[] = {
88 {
89 .integer = {
90 .type = ACPI_TYPE_INTEGER,
91 .value = 0xdeadbeef,
92 },
93 },
94 {
95 .buffer = {
96 .type = ACPI_TYPE_BUFFER,
97 .length = sizeof(test_small_buffer),
98 .pointer = test_small_buffer,
99 },
100 },
101 {
102 .string = {
103 .type = ACPI_TYPE_STRING,
104 .length = sizeof("TEST") - 1,
105 .pointer = "TEST",
106 },
107 },
108 {
109 .buffer = {
110 .type = ACPI_TYPE_BUFFER,
111 .length = sizeof(test_small_buffer),
112 .pointer = test_small_buffer,
113 },
114 },
115 {
116 .integer = {
117 .type = ACPI_TYPE_INTEGER,
118 .value = 0x01020304,
119 },
120 }
121};
122
123static u8 expected_complex_package[] = {
124 0xef, 0xbe, 0xad, 0xde,
125 0xde,
126 0x00,
127 0x0a, 0x00, 0x54, 0x00, 0x45, 0x00, 0x53, 0x00, 0x54, 0x00, 0x00, 0x00,
128 0xde,
129 0x00,
130 0x04, 0x03, 0x02, 0x01,
131};
132
133static const struct wmi_acpi_param wmi_acpi_params_array[] = {
134 {
135 .name = "single_integer",
136 .obj = {
137 .integer = {
138 .type = ACPI_TYPE_INTEGER,
139 .value = 0xdeadbeef,
140 },
141 },
142 .buffer = {
143 .data = expected_single_integer,
144 .length = sizeof(expected_single_integer),
145 },
146 },
147 {
148 .name = "single_string",
149 .obj = {
150 .string = {
151 .type = ACPI_TYPE_STRING,
152 .length = sizeof("TEST") - 1,
153 .pointer = "TEST",
154 },
155 },
156 .buffer = {
157 .data = expected_single_string,
158 .length = sizeof(expected_single_string),
159 },
160 },
161 {
162 .name = "single_buffer",
163 .obj = {
164 .buffer = {
165 .type = ACPI_TYPE_BUFFER,
166 .length = sizeof(test_buffer),
167 .pointer = test_buffer,
168 },
169 },
170 .buffer = {
171 .data = expected_single_buffer,
172 .length = sizeof(expected_single_buffer),
173 },
174 },
175 {
176 .name = "simple_package",
177 .obj = {
178 .package = {
179 .type = ACPI_TYPE_PACKAGE,
180 .count = ARRAY_SIZE(simple_package_elements),
181 .elements = simple_package_elements,
182 },
183 },
184 .buffer = {
185 .data = expected_simple_package,
186 .length = sizeof(expected_simple_package),
187 },
188 },
189 {
190 .name = "complex_package",
191 .obj = {
192 .package = {
193 .type = ACPI_TYPE_PACKAGE,
194 .count = ARRAY_SIZE(complex_package_elements),
195 .elements = complex_package_elements,
196 },
197 },
198 .buffer = {
199 .data = expected_complex_package,
200 .length = sizeof(expected_complex_package),
201 },
202 },
203};
204
205static void wmi_acpi_param_get_desc(const struct wmi_acpi_param *param, char *desc)
206{
207 strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
208}
209
210KUNIT_ARRAY_PARAM(wmi_unmarshal_acpi_object, wmi_acpi_params_array, wmi_acpi_param_get_desc);
211
212/* "WMI\0" */
213static u8 padded_wmi_string[] = {
214 0x0a, 0x00,
215 0x57, 0x00,
216 0x4D, 0x00,
217 0x49, 0x00,
218 0x00, 0x00,
219 0x00, 0x00,
220};
221
222static const struct wmi_string_param wmi_string_params_array[] = {
223 {
224 .name = "test",
225 .string = "TEST",
226 .buffer = {
227 .length = sizeof(expected_single_string),
228 .data = expected_single_string,
229 },
230 },
231 {
232 .name = "padded",
233 .string = "WMI",
234 .buffer = {
235 .length = sizeof(padded_wmi_string),
236 .data = padded_wmi_string,
237 },
238 },
239};
240
241static void wmi_string_param_get_desc(const struct wmi_string_param *param, char *desc)
242{
243 strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
244}
245
246KUNIT_ARRAY_PARAM(wmi_marshal_string, wmi_string_params_array, wmi_string_param_get_desc);
247
248static union acpi_object nested_package_elements[] = {
249 {
250 .package = {
251 .type = ACPI_TYPE_PACKAGE,
252 .count = ARRAY_SIZE(simple_package_elements),
253 .elements = simple_package_elements,
254 },
255 }
256};
257
258static const struct wmi_invalid_acpi_param wmi_invalid_acpi_params_array[] = {
259 {
260 .name = "nested_package",
261 .obj = {
262 .package = {
263 .type = ACPI_TYPE_PACKAGE,
264 .count = ARRAY_SIZE(nested_package_elements),
265 .elements = nested_package_elements,
266 },
267 },
268 },
269 {
270 .name = "reference",
271 .obj = {
272 .reference = {
273 .type = ACPI_TYPE_LOCAL_REFERENCE,
274 .actual_type = ACPI_TYPE_ANY,
275 .handle = NULL,
276 },
277 },
278 },
279 {
280 .name = "processor",
281 .obj = {
282 .processor = {
283 .type = ACPI_TYPE_PROCESSOR,
284 .proc_id = 0,
285 .pblk_address = 0,
286 .pblk_length = 0,
287 },
288 },
289 },
290 {
291 .name = "power_resource",
292 .obj = {
293 .power_resource = {
294 .type = ACPI_TYPE_POWER,
295 .system_level = 0,
296 .resource_order = 0,
297 },
298 },
299 },
300};
301
302static void wmi_invalid_acpi_param_get_desc(const struct wmi_invalid_acpi_param *param, char *desc)
303{
304 strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
305}
306
307KUNIT_ARRAY_PARAM(wmi_unmarshal_acpi_object_failure, wmi_invalid_acpi_params_array,
308 wmi_invalid_acpi_param_get_desc);
309
310static u8 oversized_wmi_string[] = {
311 0x04, 0x00, 0x00, 0x00,
312};
313
314/*
315 * The error is that 3 bytes can not hold UTF-16 characters
316 * without cutting of the last one.
317 */
318static u8 undersized_wmi_string[] = {
319 0x03, 0x00, 0x00, 0x00, 0x00,
320};
321
322static u8 non_ascii_wmi_string[] = {
323 0x04, 0x00, 0xC4, 0x00, 0x00, 0x00,
324};
325
326static const struct wmi_invalid_string_param wmi_invalid_string_params_array[] = {
327 {
328 .name = "empty_buffer",
329 .buffer = {
330 .length = 0,
331 .data = ZERO_SIZE_PTR,
332 },
333
334 },
335 {
336 .name = "oversized",
337 .buffer = {
338 .length = sizeof(oversized_wmi_string),
339 .data = oversized_wmi_string,
340 },
341 },
342 {
343 .name = "undersized",
344 .buffer = {
345 .length = sizeof(undersized_wmi_string),
346 .data = undersized_wmi_string,
347 },
348 },
349 {
350 .name = "non_ascii",
351 .buffer = {
352 .length = sizeof(non_ascii_wmi_string),
353 .data = non_ascii_wmi_string,
354 },
355 },
356};
357
358static void wmi_invalid_string_param_get_desc(const struct wmi_invalid_string_param *param,
359 char *desc)
360{
361 strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
362}
363
364KUNIT_ARRAY_PARAM(wmi_marshal_string_failure, wmi_invalid_string_params_array,
365 wmi_invalid_string_param_get_desc);
366
367KUNIT_DEFINE_ACTION_WRAPPER(kfree_wrapper, kfree, const void *);
368
369static void wmi_unmarshal_acpi_object_test(struct kunit *test)
370{
371 const struct wmi_acpi_param *param = test->param_value;
372 struct wmi_buffer result;
373 int ret;
374
375 ret = wmi_unmarshal_acpi_object(&param->obj, &result);
376 if (ret < 0)
377 KUNIT_FAIL_AND_ABORT(test, "Unmarshalling of ACPI object failed\n");
378
379 kunit_add_action(test, kfree_wrapper, result.data);
380
381 KUNIT_EXPECT_TRUE(test, IS_ALIGNED((uintptr_t)result.data, 8));
382 KUNIT_EXPECT_EQ(test, result.length, param->buffer.length);
383 KUNIT_EXPECT_MEMEQ(test, result.data, param->buffer.data, result.length);
384}
385
386static void wmi_unmarshal_acpi_object_failure_test(struct kunit *test)
387{
388 const struct wmi_invalid_acpi_param *param = test->param_value;
389 struct wmi_buffer result;
390 int ret;
391
392 ret = wmi_unmarshal_acpi_object(&param->obj, &result);
393 if (ret < 0)
394 return;
395
396 kfree(result.data);
397 KUNIT_FAIL(test, "Invalid ACPI object was not rejected\n");
398}
399
400static void wmi_marshal_string_test(struct kunit *test)
401{
402 const struct wmi_string_param *param = test->param_value;
403 struct acpi_buffer result;
404 int ret;
405
406 ret = wmi_marshal_string(&param->buffer, &result);
407 if (ret < 0)
408 KUNIT_FAIL_AND_ABORT(test, "Marshalling of WMI string failed\n");
409
410 kunit_add_action(test, kfree_wrapper, result.pointer);
411
412 KUNIT_EXPECT_EQ(test, result.length, strlen(param->string));
413 KUNIT_EXPECT_STREQ(test, result.pointer, param->string);
414}
415
416static void wmi_marshal_string_failure_test(struct kunit *test)
417{
418 const struct wmi_invalid_string_param *param = test->param_value;
419 struct acpi_buffer result;
420 int ret;
421
422 ret = wmi_marshal_string(&param->buffer, &result);
423 if (ret < 0)
424 return;
425
426 kfree(result.pointer);
427 KUNIT_FAIL(test, "Invalid string was not rejected\n");
428}
429
430static struct kunit_case wmi_marshalling_test_cases[] = {
431 KUNIT_CASE_PARAM(wmi_unmarshal_acpi_object_test,
432 wmi_unmarshal_acpi_object_gen_params),
433 KUNIT_CASE_PARAM(wmi_marshal_string_test,
434 wmi_marshal_string_gen_params),
435 KUNIT_CASE_PARAM(wmi_unmarshal_acpi_object_failure_test,
436 wmi_unmarshal_acpi_object_failure_gen_params),
437 KUNIT_CASE_PARAM(wmi_marshal_string_failure_test,
438 wmi_marshal_string_failure_gen_params),
439 {}
440};
441
442static struct kunit_suite wmi_marshalling_test_suite = {
443 .name = "wmi_marshalling",
444 .test_cases = wmi_marshalling_test_cases,
445};
446
447kunit_test_suite(wmi_marshalling_test_suite);
448
449MODULE_AUTHOR("Armin Wolf <W_Armin@gmx.de>");
450MODULE_DESCRIPTION("KUnit test for the ACPI-WMI marshalling code");
451MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
452MODULE_LICENSE("GPL");
drivers/platform/wmi/tests/string_kunit.c+296−0
@@ -0,0 +1,296 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * KUnit test for the ACPI-WMI string conversion code.
4 *
5 * Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de>
6 */
7
8#include <linux/module.h>
9#include <linux/slab.h>
10#include <linux/string.h>
11#include <linux/wmi.h>
12
13#include <kunit/resource.h>
14#include <kunit/test.h>
15
16#include <asm/byteorder.h>
17
18struct wmi_string_param {
19 const char *name;
20 const struct wmi_string *wmi_string;
21 /*
22 * Remember that using sizeof() on a struct wmi_string will
23 * always return a size of two bytes due to the flexible
24 * array member!
25 */
26 size_t wmi_string_length;
27 const u8 *utf8_string;
28 size_t utf8_string_length;
29};
30
31#define TEST_WMI_STRING_LENGTH 12
32
33static const struct wmi_string test_wmi_string = {
34 .length = cpu_to_le16(10),
35 .chars = {
36 cpu_to_le16(u'T'),
37 cpu_to_le16(u'E'),
38 cpu_to_le16(u'S'),
39 cpu_to_le16(u'T'),
40 cpu_to_le16(u'\0'),
41 },
42};
43
44static const u8 test_utf8_string[] = "TEST";
45
46#define SPECIAL_WMI_STRING_LENGTH 14
47
48static const struct wmi_string special_wmi_string = {
49 .length = cpu_to_le16(12),
50 .chars = {
51 cpu_to_le16(u'Ä'),
52 cpu_to_le16(u'Ö'),
53 cpu_to_le16(u'Ü'),
54 cpu_to_le16(u'ß'),
55 cpu_to_le16(u'€'),
56 cpu_to_le16(u'\0'),
57 },
58};
59
60static const u8 special_utf8_string[] = "ÄÖÜ߀";
61
62#define MULTI_POINT_WMI_STRING_LENGTH 12
63
64static const struct wmi_string multi_point_wmi_string = {
65 .length = cpu_to_le16(10),
66 .chars = {
67 cpu_to_le16(u'K'),
68 /* 🐧 */
69 cpu_to_le16(0xD83D),
70 cpu_to_le16(0xDC27),
71 cpu_to_le16(u'!'),
72 cpu_to_le16(u'\0'),
73 },
74};
75
76static const u8 multi_point_utf8_string[] = "K🐧!";
77
78#define PADDED_TEST_WMI_STRING_LENGTH 14
79
80static const struct wmi_string padded_test_wmi_string = {
81 .length = cpu_to_le16(12),
82 .chars = {
83 cpu_to_le16(u'T'),
84 cpu_to_le16(u'E'),
85 cpu_to_le16(u'S'),
86 cpu_to_le16(u'T'),
87 cpu_to_le16(u'\0'),
88 cpu_to_le16(u'\0'),
89 },
90};
91
92static const u8 padded_test_utf8_string[] = "TEST\0";
93
94#define OVERSIZED_TEST_WMI_STRING_LENGTH 14
95
96static const struct wmi_string oversized_test_wmi_string = {
97 .length = cpu_to_le16(8),
98 .chars = {
99 cpu_to_le16(u'T'),
100 cpu_to_le16(u'E'),
101 cpu_to_le16(u'S'),
102 cpu_to_le16(u'T'),
103 cpu_to_le16(u'!'),
104 cpu_to_le16(u'\0'),
105 },
106};
107
108static const u8 oversized_test_utf8_string[] = "TEST!";
109
110#define INVALID_TEST_WMI_STRING_LENGTH 14
111
112static const struct wmi_string invalid_test_wmi_string = {
113 .length = cpu_to_le16(12),
114 .chars = {
115 cpu_to_le16(u'T'),
116 /* 🐧, with low surrogate missing */
117 cpu_to_le16(0xD83D),
118 cpu_to_le16(u'E'),
119 cpu_to_le16(u'S'),
120 cpu_to_le16(u'T'),
121 cpu_to_le16(u'\0'),
122 },
123};
124
125/* We have to split the string here to end the hex escape sequence */
126static const u8 invalid_test_utf8_string[] = "T" "\xF0\x9F" "EST";
127
128static const struct wmi_string_param wmi_string_params_array[] = {
129 {
130 .name = "ascii_string",
131 .wmi_string = &test_wmi_string,
132 .wmi_string_length = TEST_WMI_STRING_LENGTH,
133 .utf8_string = test_utf8_string,
134 .utf8_string_length = sizeof(test_utf8_string),
135 },
136 {
137 .name = "special_string",
138 .wmi_string = &special_wmi_string,
139 .wmi_string_length = SPECIAL_WMI_STRING_LENGTH,
140 .utf8_string = special_utf8_string,
141 .utf8_string_length = sizeof(special_utf8_string),
142 },
143 {
144 .name = "multi_point_string",
145 .wmi_string = &multi_point_wmi_string,
146 .wmi_string_length = MULTI_POINT_WMI_STRING_LENGTH,
147 .utf8_string = multi_point_utf8_string,
148 .utf8_string_length = sizeof(multi_point_utf8_string),
149 },
150};
151
152static void wmi_string_param_get_desc(const struct wmi_string_param *param, char *desc)
153{
154 strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
155}
156
157KUNIT_ARRAY_PARAM(wmi_string, wmi_string_params_array, wmi_string_param_get_desc);
158
159static void wmi_string_to_utf8s_test(struct kunit *test)
160{
161 const struct wmi_string_param *param = test->param_value;
162 ssize_t ret;
163 u8 *result;
164
165 result = kunit_kzalloc(test, param->utf8_string_length, GFP_KERNEL);
166 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
167
168 ret = wmi_string_to_utf8s(param->wmi_string, result, param->utf8_string_length);
169
170 KUNIT_EXPECT_EQ(test, ret, param->utf8_string_length - 1);
171 KUNIT_EXPECT_MEMEQ(test, result, param->utf8_string, param->utf8_string_length);
172}
173
174static void wmi_string_from_utf8s_test(struct kunit *test)
175{
176 const struct wmi_string_param *param = test->param_value;
177 struct wmi_string *result;
178 size_t max_chars;
179 ssize_t ret;
180
181 max_chars = (param->wmi_string_length - sizeof(*result)) / 2;
182 result = kunit_kzalloc(test, param->wmi_string_length, GFP_KERNEL);
183 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
184
185 ret = wmi_string_from_utf8s(result, max_chars, param->utf8_string,
186 param->utf8_string_length);
187
188 KUNIT_EXPECT_EQ(test, ret, max_chars - 1);
189 KUNIT_EXPECT_MEMEQ(test, result, param->wmi_string, param->wmi_string_length);
190}
191
192static void wmi_string_to_utf8s_padded_test(struct kunit *test)
193{
194 u8 result[sizeof(padded_test_utf8_string)];
195 ssize_t ret;
196
197 ret = wmi_string_to_utf8s(&padded_test_wmi_string, result, sizeof(result));
198
199 KUNIT_EXPECT_EQ(test, ret, sizeof(test_utf8_string) - 1);
200 KUNIT_EXPECT_MEMEQ(test, result, test_utf8_string, sizeof(test_utf8_string));
201}
202
203static void wmi_string_from_utf8s_padded_test(struct kunit *test)
204{
205 struct wmi_string *result;
206 size_t max_chars;
207 ssize_t ret;
208
209 max_chars = (PADDED_TEST_WMI_STRING_LENGTH - sizeof(*result)) / 2;
210 result = kunit_kzalloc(test, PADDED_TEST_WMI_STRING_LENGTH, GFP_KERNEL);
211 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
212
213 ret = wmi_string_from_utf8s(result, max_chars, padded_test_utf8_string,
214 sizeof(padded_test_utf8_string));
215
216 KUNIT_EXPECT_EQ(test, ret, sizeof(test_utf8_string) - 1);
217 KUNIT_EXPECT_MEMEQ(test, result, &test_wmi_string, sizeof(test_wmi_string));
218}
219
220static void wmi_string_to_utf8s_oversized_test(struct kunit *test)
221{
222 u8 result[sizeof(oversized_test_utf8_string)];
223 ssize_t ret;
224
225 ret = wmi_string_to_utf8s(&oversized_test_wmi_string, result, sizeof(result));
226
227 KUNIT_EXPECT_EQ(test, ret, sizeof(test_utf8_string) - 1);
228 KUNIT_EXPECT_MEMEQ(test, result, test_utf8_string, sizeof(test_utf8_string));
229}
230
231static void wmi_string_from_utf8s_oversized_test(struct kunit *test)
232{
233 struct wmi_string *result;
234 size_t max_chars;
235 ssize_t ret;
236
237 max_chars = (TEST_WMI_STRING_LENGTH - sizeof(*result)) / 2;
238 result = kunit_kzalloc(test, TEST_WMI_STRING_LENGTH, GFP_KERNEL);
239 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
240
241 ret = wmi_string_from_utf8s(result, max_chars, oversized_test_utf8_string,
242 sizeof(oversized_test_utf8_string));
243
244 KUNIT_EXPECT_EQ(test, ret, sizeof(test_utf8_string) - 1);
245 KUNIT_EXPECT_MEMEQ(test, result, &test_wmi_string, sizeof(test_wmi_string));
246}
247
248static void wmi_string_to_utf8s_invalid_test(struct kunit *test)
249{
250 u8 result[sizeof(invalid_test_utf8_string)];
251 ssize_t ret;
252
253 ret = wmi_string_to_utf8s(&invalid_test_wmi_string, result, sizeof(result));
254
255 KUNIT_EXPECT_EQ(test, ret, sizeof(test_utf8_string) - 1);
256 KUNIT_EXPECT_MEMEQ(test, result, test_utf8_string, sizeof(test_utf8_string));
257}
258
259static void wmi_string_from_utf8s_invalid_test(struct kunit *test)
260{
261 struct wmi_string *result;
262 size_t max_chars;
263 ssize_t ret;
264
265 max_chars = (INVALID_TEST_WMI_STRING_LENGTH - sizeof(*result)) / 2;
266 result = kunit_kzalloc(test, INVALID_TEST_WMI_STRING_LENGTH, GFP_KERNEL);
267 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
268
269 ret = wmi_string_from_utf8s(result, max_chars, invalid_test_utf8_string,
270 sizeof(invalid_test_utf8_string));
271
272 KUNIT_EXPECT_EQ(test, ret, -EINVAL);
273}
274
275static struct kunit_case wmi_string_test_cases[] = {
276 KUNIT_CASE_PARAM(wmi_string_to_utf8s_test, wmi_string_gen_params),
277 KUNIT_CASE_PARAM(wmi_string_from_utf8s_test, wmi_string_gen_params),
278 KUNIT_CASE(wmi_string_to_utf8s_padded_test),
279 KUNIT_CASE(wmi_string_from_utf8s_padded_test),
280 KUNIT_CASE(wmi_string_to_utf8s_oversized_test),
281 KUNIT_CASE(wmi_string_from_utf8s_oversized_test),
282 KUNIT_CASE(wmi_string_to_utf8s_invalid_test),
283 KUNIT_CASE(wmi_string_from_utf8s_invalid_test),
284 {}
285};
286
287static struct kunit_suite wmi_string_test_suite = {
288 .name = "wmi_string",
289 .test_cases = wmi_string_test_cases,
290};
291
292kunit_test_suite(wmi_string_test_suite);
293
294MODULE_AUTHOR("Armin Wolf <W_Armin@gmx.de>");
295MODULE_DESCRIPTION("KUnit test for the ACPI-WMI string conversion code");
296MODULE_LICENSE("GPL");
drivers/platform/x86/amd/pmf/acpi.c+40−0
@@ -9,6 +9,9 @@ · 8 unmodified lines
9 */9 */
10 10
11#include <linux/acpi.h>11#include <linux/acpi.h>
12#include <linux/array_size.h>
13#include <linux/cleanup.h>
14#include <linux/dev_printk.h>
12#include "pmf.h"15#include "pmf.h"
13 16
14#define APMF_CQL_NOTIFICATION 217#define APMF_CQL_NOTIFICATION 2
@@ -331,6 +334,39 @@ int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req · 316 unmodified lines
331 req, sizeof(*req));334 req, sizeof(*req));
332}335}
333 336
337/* Store custom BIOS inputs data in ring buffer */
338static void amd_pmf_custom_bios_inputs_rb(struct amd_pmf_dev *pmf_dev)
339{
340 struct pmf_cbi_ring_buffer *rb = &pmf_dev->cbi_buf;
341 int i;
342
343 guard(mutex)(&pmf_dev->cbi_mutex);
344
345 switch (pmf_dev->cpu_id) {
346 case AMD_CPU_ID_PS:
347 for (i = 0; i < ARRAY_SIZE(custom_bios_inputs_v1); i++)
348 rb->data[rb->head].val[i] = pmf_dev->req1.custom_policy[i];
349 rb->data[rb->head].preq = pmf_dev->req1.pending_req;
350 break;
351 case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
352 case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
353 for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++)
354 rb->data[rb->head].val[i] = pmf_dev->req.custom_policy[i];
355 rb->data[rb->head].preq = pmf_dev->req.pending_req;
356 break;
357 default:
358 return;
359 }
360
361 if (CIRC_SPACE(rb->head, rb->tail, CUSTOM_BIOS_INPUT_RING_ENTRIES) == 0) {
362 /* Rare case: ensures the newest BIOS input value is kept */
363 dev_warn(pmf_dev->dev, "Overwriting BIOS input value, data may be lost\n");
364 rb->tail = (rb->tail + 1) & (CUSTOM_BIOS_INPUT_RING_ENTRIES - 1);
365 }
366
367 rb->head = (rb->head + 1) & (CUSTOM_BIOS_INPUT_RING_ENTRIES - 1);
368}
369
334static void amd_pmf_handle_early_preq(struct amd_pmf_dev *pdev)370static void amd_pmf_handle_early_preq(struct amd_pmf_dev *pdev)
335{371{
336 if (!pdev->cb_flag)372 if (!pdev->cb_flag)
@@ -356,6 +392,8 @@ static void apmf_event_handler_v2(acpi_handle handle, u32 event, void *data) · 19 unmodified lines
356 dev_dbg(pmf_dev->dev, "Pending request (preq): 0x%x\n", pmf_dev->req.pending_req);392 dev_dbg(pmf_dev->dev, "Pending request (preq): 0x%x\n", pmf_dev->req.pending_req);
357 393
358 amd_pmf_handle_early_preq(pmf_dev);394 amd_pmf_handle_early_preq(pmf_dev);
395
396 amd_pmf_custom_bios_inputs_rb(pmf_dev);
359}397}
360 398
361static void apmf_event_handler_v1(acpi_handle handle, u32 event, void *data)399static void apmf_event_handler_v1(acpi_handle handle, u32 event, void *data)
@@ -374,6 +412,8 @@ static void apmf_event_handler_v1(acpi_handle handle, u32 event, void *data) · 12 unmodified lines
374 dev_dbg(pmf_dev->dev, "Pending request (preq1): 0x%x\n", pmf_dev->req1.pending_req);412 dev_dbg(pmf_dev->dev, "Pending request (preq1): 0x%x\n", pmf_dev->req1.pending_req);
375 413
376 amd_pmf_handle_early_preq(pmf_dev);414 amd_pmf_handle_early_preq(pmf_dev);
415
416 amd_pmf_custom_bios_inputs_rb(pmf_dev);
377}417}
378 418
379static void apmf_event_handler(acpi_handle handle, u32 event, void *data)419static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
drivers/platform/x86/amd/pmf/core.c+155−6
@@ -8,12 +8,16 @@ · 7 unmodified lines
8 * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>8 * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
9 */9 */
10 10
11#include <linux/array_size.h>
12#include <linux/cleanup.h>
11#include <linux/debugfs.h>13#include <linux/debugfs.h>
12#include <linux/iopoll.h>14#include <linux/iopoll.h>
13#include <linux/module.h>15#include <linux/module.h>
16#include <linux/mutex.h>
14#include <linux/pci.h>17#include <linux/pci.h>
15#include <linux/platform_device.h>18#include <linux/platform_device.h>
16#include <linux/power_supply.h>19#include <linux/power_supply.h>
20#include <linux/string.h>
17#include <asm/amd/node.h>21#include <asm/amd/node.h>
18#include "pmf.h"22#include "pmf.h"
19 23
@@ -53,6 +57,12 @@ static bool force_load; · 33 unmodified lines
53module_param(force_load, bool, 0444);57module_param(force_load, bool, 0444);
54MODULE_PARM_DESC(force_load, "Force load this driver on supported older platforms (experimental)");58MODULE_PARM_DESC(force_load, "Force load this driver on supported older platforms (experimental)");
55 59
60static bool smart_pc_support = true;
61module_param(smart_pc_support, bool, 0444);
62MODULE_PARM_DESC(smart_pc_support, "Smart PC Support (default = true)");
63
64static struct device *pmf_device;
65
56static int amd_pmf_pwr_src_notify_call(struct notifier_block *nb, unsigned long event, void *data)66static int amd_pmf_pwr_src_notify_call(struct notifier_block *nb, unsigned long event, void *data)
57{67{
58 struct amd_pmf_dev *pmf = container_of(nb, struct amd_pmf_dev, pwr_src_notifier);68 struct amd_pmf_dev *pmf = container_of(nb, struct amd_pmf_dev, pwr_src_notifier);
@@ -314,6 +324,126 @@ int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev) · 255 unmodified lines
314 return 0;324 return 0;
315}325}
316 326
327static int is_npu_metrics_supported(struct amd_pmf_dev *pdev)
328{
329 switch (pdev->cpu_id) {
330 case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
331 case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
332 return 0;
333 default:
334 return -EOPNOTSUPP;
335 }
336}
337
338static int amd_pmf_get_smu_metrics(struct amd_pmf_dev *dev, struct amd_pmf_npu_metrics *data)
339{
340 int ret, i;
341
342 guard(mutex)(&dev->metrics_mutex);
343
344 ret = is_npu_metrics_supported(dev);
345 if (ret)
346 return ret;
347
348 ret = amd_pmf_set_dram_addr(dev, true);
349 if (ret)
350 return ret;
351
352 memset(dev->buf, 0, dev->mtable_size);
353
354 /* Send SMU command to get NPU metrics */
355 ret = amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL);
356 if (ret) {
357 dev_err(dev->dev, "SMU command failed to get NPU metrics: %d\n", ret);
358 return ret;
359 }
360
361 memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size);
362
363 data->npuclk_freq = dev->m_table_v2.npuclk_freq;
364 for (i = 0; i < ARRAY_SIZE(data->npu_busy); i++)
365 data->npu_busy[i] = dev->m_table_v2.npu_busy[i];
366 data->npu_power = dev->m_table_v2.npu_power;
367 data->mpnpuclk_freq = dev->m_table_v2.mpnpuclk_freq;
368 data->npu_reads = dev->m_table_v2.npu_reads;
369 data->npu_writes = dev->m_table_v2.npu_writes;
370
371 return 0;
372}
373
374int amd_pmf_get_npu_data(struct amd_pmf_npu_metrics *info)
375{
376 struct amd_pmf_dev *pdev;
377
378 if (!info)
379 return -EINVAL;
380
381 if (!pmf_device)
382 return -ENODEV;
383
384 pdev = dev_get_drvdata(pmf_device);
385 if (!pdev)
386 return -ENODEV;
387
388 return amd_pmf_get_smu_metrics(pdev, info);
389}
390EXPORT_SYMBOL_NS_GPL(amd_pmf_get_npu_data, "AMD_PMF");
391
392static int amd_pmf_reinit_ta(struct amd_pmf_dev *pdev)
393{
394 bool status;
395 int ret, i;
396
397 for (i = 0; i < ARRAY_SIZE(amd_pmf_ta_uuid); i++) {
398 ret = amd_pmf_tee_init(pdev, &amd_pmf_ta_uuid[i]);
399 if (ret) {
400 dev_err(pdev->dev, "TEE init failed for UUID[%d] ret: %d\n", i, ret);
401 return ret;
402 }
403
404 ret = amd_pmf_start_policy_engine(pdev);
405 dev_dbg(pdev->dev, "start policy engine ret: %d (UUID idx: %d)\n", ret, i);
406 status = ret == TA_PMF_TYPE_SUCCESS;
407 if (status)
408 break;
409 amd_pmf_tee_deinit(pdev);
410 }
411
412 return 0;
413}
414
415static int amd_pmf_restore_handler(struct device *dev)
416{
417 struct amd_pmf_dev *pdev = dev_get_drvdata(dev);
418 int ret;
419
420 if (pdev->buf) {
421 ret = amd_pmf_set_dram_addr(pdev, false);
422 if (ret)
423 return ret;
424 }
425
426 if (pdev->smart_pc_enabled)
427 amd_pmf_reinit_ta(pdev);
428
429 return 0;
430}
431
432static int amd_pmf_freeze_handler(struct device *dev)
433{
434 struct amd_pmf_dev *pdev = dev_get_drvdata(dev);
435
436 if (!pdev->smart_pc_enabled)
437 return 0;
438
439 cancel_delayed_work_sync(&pdev->pb_work);
440 /* Clear all TEE resources */
441 amd_pmf_tee_deinit(pdev);
442 pdev->session_id = 0;
443
444 return 0;
445}
446
317static int amd_pmf_suspend_handler(struct device *dev)447static int amd_pmf_suspend_handler(struct device *dev)
318{448{
319 struct amd_pmf_dev *pdev = dev_get_drvdata(dev);449 struct amd_pmf_dev *pdev = dev_get_drvdata(dev);
@@ -347,7 +477,12 @@ static int amd_pmf_resume_handler(struct device *dev) · 27 unmodified lines
347 return 0;477 return 0;
348}478}
349 479
350static DEFINE_SIMPLE_DEV_PM_OPS(amd_pmf_pm, amd_pmf_suspend_handler, amd_pmf_resume_handler);480static const struct dev_pm_ops amd_pmf_pm = {
481 .suspend = amd_pmf_suspend_handler,
482 .resume = amd_pmf_resume_handler,
483 .freeze = amd_pmf_freeze_handler,
484 .restore = amd_pmf_restore_handler,
485};
351 486
352static void amd_pmf_init_features(struct amd_pmf_dev *dev)487static void amd_pmf_init_features(struct amd_pmf_dev *dev)
353{488{
@@ -362,11 +497,15 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev) · 8 unmodified lines
362 dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");497 dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");
363 }498 }
364 499
365 amd_pmf_init_smart_pc(dev);500 if (smart_pc_support) {
366 if (dev->smart_pc_enabled) {501 amd_pmf_init_smart_pc(dev);
367 dev_dbg(dev->dev, "Smart PC Solution Enabled\n");502 if (dev->smart_pc_enabled) {
368 /* If Smart PC is enabled, no need to check for other features */503 dev_dbg(dev->dev, "Smart PC Solution Enabled\n");
369 return;504 /* If Smart PC is enabled, no need to check for other features */
505 return;
506 }
507 } else {
508 dev->smart_pc_enabled = false;
370 }509 }
371 510
372 if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) {511 if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) {
@@ -477,6 +616,14 @@ static int amd_pmf_probe(struct platform_device *pdev) · 104 unmodified lines
477 if (err)616 if (err)
478 return err;617 return err;
479 618
619 err = devm_mutex_init(dev->dev, &dev->cbi_mutex);
620 if (err)
621 return err;
622
623 err = devm_mutex_init(dev->dev, &dev->metrics_mutex);
624 if (err)
625 return err;
626
480 apmf_acpi_init(dev);627 apmf_acpi_init(dev);
481 platform_set_drvdata(pdev, dev);628 platform_set_drvdata(pdev, dev);
482 amd_pmf_dbgfs_register(dev);629 amd_pmf_dbgfs_register(dev);
@@ -485,6 +632,8 @@ static int amd_pmf_probe(struct platform_device *pdev) · 2 unmodified lines
485 if (is_apmf_func_supported(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2))632 if (is_apmf_func_supported(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
486 amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_LOAD);633 amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_LOAD);
487 634
635 pmf_device = dev->dev;
636
488 dev_info(dev->dev, "registered PMF device successfully\n");637 dev_info(dev->dev, "registered PMF device successfully\n");
489 638
490 return 0;639 return 0;
drivers/platform/x86/amd/pmf/pmf.h+33−0
@@ -12,7 +12,10 @@ · 11 unmodified lines
12#define PMF_H12#define PMF_H
13 13
14#include <linux/acpi.h>14#include <linux/acpi.h>
15#include <linux/amd-pmf-io.h>
16#include <linux/circ_buf.h>
15#include <linux/input.h>17#include <linux/input.h>
18#include <linux/mutex_types.h>
16#include <linux/platform_device.h>19#include <linux/platform_device.h>
17#include <linux/platform_profile.h>20#include <linux/platform_profile.h>
18 21
@@ -120,6 +123,7 @@ struct cookie_header { · 101 unmodified lines
120#define APTS_MAX_STATES 16123#define APTS_MAX_STATES 16
121#define CUSTOM_BIOS_INPUT_BITS GENMASK(16, 7)124#define CUSTOM_BIOS_INPUT_BITS GENMASK(16, 7)
122#define BIOS_INPUTS_MAX 10125#define BIOS_INPUTS_MAX 10
126#define CUSTOM_BIOS_INPUT_RING_ENTRIES 64 /* Must be power of two for CIRC_* macros */
123 127
124/* amd_pmf_send_cmd() set/get */128/* amd_pmf_send_cmd() set/get */
125#define SET_CMD false129#define SET_CMD false
@@ -129,6 +133,12 @@ struct cookie_header { · 3 unmodified lines
129 133
130typedef void (*apmf_event_handler_t)(acpi_handle handle, u32 event, void *data);134typedef void (*apmf_event_handler_t)(acpi_handle handle, u32 event, void *data);
131 135
136static const uuid_t amd_pmf_ta_uuid[] __used = { UUID_INIT(0xd9b39bf2, 0x66bd, 0x4154, 0xaf, 0xb8,
137 0x8a, 0xcc, 0x2b, 0x2b, 0x60, 0xd6),
138 UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d, 0xb1, 0x2d,
139 0xc5, 0x29, 0xb1, 0x3d, 0x85, 0x43),
140 };
141
132/* APTS PMF BIOS Interface */142/* APTS PMF BIOS Interface */
133struct amd_pmf_apts_output {143struct amd_pmf_apts_output {
134 u16 table_version;144 u16 table_version;
@@ -365,6 +375,22 @@ struct pmf_bios_inputs_prev { · 230 unmodified lines
365 u32 custom_bios_inputs[BIOS_INPUTS_MAX];375 u32 custom_bios_inputs[BIOS_INPUTS_MAX];
366};376};
367 377
378/**
379 * struct pmf_bios_input_entry - Snapshot of custom BIOS input event
380 * @val: Array of custom BIOS input values
381 * @preq: Pending request value associated with this event
382 */
383struct pmf_bios_input_entry {
384 u32 val[BIOS_INPUTS_MAX];
385 u32 preq;
386};
387
388struct pmf_cbi_ring_buffer {
389 struct pmf_bios_input_entry data[CUSTOM_BIOS_INPUT_RING_ENTRIES];
390 int head;
391 int tail;
392};
393
368struct amd_pmf_dev {394struct amd_pmf_dev {
369 void __iomem *regbase;395 void __iomem *regbase;
370 void __iomem *smu_virt_addr;396 void __iomem *smu_virt_addr;
@@ -413,6 +439,9 @@ struct amd_pmf_dev { · 42 unmodified lines
413 struct apmf_sbios_req_v1 req1;439 struct apmf_sbios_req_v1 req1;
414 struct pmf_bios_inputs_prev cb_prev; /* To preserve custom BIOS inputs */440 struct pmf_bios_inputs_prev cb_prev; /* To preserve custom BIOS inputs */
415 bool cb_flag; /* To handle first custom BIOS input */441 bool cb_flag; /* To handle first custom BIOS input */
442 struct pmf_cbi_ring_buffer cbi_buf;
443 struct mutex cbi_mutex; /* Protects ring buffer access */
444 struct mutex metrics_mutex;
416};445};
417 446
418struct apmf_sps_prop_granular_v2 {447struct apmf_sps_prop_granular_v2 {
@@ -895,4 +924,8 @@ void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_tab · 476 unmodified lines
895void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in);924void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in);
896int amd_pmf_invoke_cmd_enact(struct amd_pmf_dev *dev);925int amd_pmf_invoke_cmd_enact(struct amd_pmf_dev *dev);
897 926
927int amd_pmf_tee_init(struct amd_pmf_dev *dev, const uuid_t *uuid);
928void amd_pmf_tee_deinit(struct amd_pmf_dev *dev);
929int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev);
930
898#endif /* PMF_H */931#endif /* PMF_H */
drivers/platform/x86/amd/pmf/spc.c+19−14
@@ -11,6 +11,7 @@ · 10 unmodified lines
11 11
12#include <acpi/button.h>12#include <acpi/button.h>
13#include <linux/amd-pmf-io.h>13#include <linux/amd-pmf-io.h>
14#include <linux/cleanup.h>
14#include <linux/power_supply.h>15#include <linux/power_supply.h>
15#include <linux/units.h>16#include <linux/units.h>
16#include "pmf.h"17#include "pmf.h"
@@ -132,32 +133,39 @@ static void amd_pmf_set_ta_custom_bios_input(struct ta_pmf_enact_table *in, int · 115 unmodified lines
132 }133 }
133}134}
134 135
135static void amd_pmf_update_bios_inputs(struct amd_pmf_dev *pdev, u32 pending_req,136static void amd_pmf_update_bios_inputs(struct amd_pmf_dev *pdev, struct pmf_bios_input_entry *data,
136 const struct amd_pmf_pb_bitmap *inputs,137 const struct amd_pmf_pb_bitmap *inputs,
137 const u32 *custom_policy, struct ta_pmf_enact_table *in)138 struct ta_pmf_enact_table *in)
138{139{
139 unsigned int i;140 unsigned int i;
140 141
141 for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++) {142 for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++) {
142 if (!(pending_req & inputs[i].bit_mask))143 if (!(data->preq & inputs[i].bit_mask))
143 continue;144 continue;
144 amd_pmf_set_ta_custom_bios_input(in, i, custom_policy[i]);145 amd_pmf_set_ta_custom_bios_input(in, i, data->val[i]);
145 pdev->cb_prev.custom_bios_inputs[i] = custom_policy[i];146 pdev->cb_prev.custom_bios_inputs[i] = data->val[i];
146 dev_dbg(pdev->dev, "Custom BIOS Input[%d]: %u\n", i, custom_policy[i]);147 dev_dbg(pdev->dev, "Custom BIOS Input[%d]: %u\n", i, data->val[i]);
147 }148 }
148}149}
149 150
150static void amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev *pdev,151static void amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev *pdev,
151 struct ta_pmf_enact_table *in)152 struct ta_pmf_enact_table *in)
152{153{
154 struct pmf_cbi_ring_buffer *rb = &pdev->cbi_buf;
153 unsigned int i;155 unsigned int i;
154 156
157 guard(mutex)(&pdev->cbi_mutex);
158
155 for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++)159 for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++)
156 amd_pmf_set_ta_custom_bios_input(in, i, pdev->cb_prev.custom_bios_inputs[i]);160 amd_pmf_set_ta_custom_bios_input(in, i, pdev->cb_prev.custom_bios_inputs[i]);
157 161
158 if (!(pdev->req.pending_req || pdev->req1.pending_req))162 if (CIRC_CNT(rb->head, rb->tail, CUSTOM_BIOS_INPUT_RING_ENTRIES) == 0)
159 return;163 return;
160 164
165 /* If no active custom BIOS input pending request, do not consume further work */
166 if (!rb->data[rb->tail].preq)
167 goto out_rbadvance;
168
161 if (!pdev->smart_pc_enabled)169 if (!pdev->smart_pc_enabled)
162 return;170 return;
163 171
@@ -165,20 +173,17 @@ static void amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev *pdev, · 1 unmodified line
165 case PMF_IF_V1:173 case PMF_IF_V1:
166 if (!is_apmf_bios_input_notifications_supported(pdev))174 if (!is_apmf_bios_input_notifications_supported(pdev))
167 return;175 return;
168 amd_pmf_update_bios_inputs(pdev, pdev->req1.pending_req, custom_bios_inputs_v1,176 amd_pmf_update_bios_inputs(pdev, &rb->data[rb->tail], custom_bios_inputs_v1, in);
169 pdev->req1.custom_policy, in);
170 break;177 break;
171 case PMF_IF_V2:178 case PMF_IF_V2:
172 amd_pmf_update_bios_inputs(pdev, pdev->req.pending_req, custom_bios_inputs,179 amd_pmf_update_bios_inputs(pdev, &rb->data[rb->tail], custom_bios_inputs, in);
173 pdev->req.custom_policy, in);
174 break;180 break;
175 default:181 default:
176 break;182 break;
177 }183 }
178 184
179 /* Clear pending requests after handling */185out_rbadvance:
180 memset(&pdev->req, 0, sizeof(pdev->req));186 rb->tail = (rb->tail + 1) & (CUSTOM_BIOS_INPUT_RING_ENTRIES - 1);
181 memset(&pdev->req1, 0, sizeof(pdev->req1));
182}187}
183 188
184static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in)189static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in)
drivers/platform/x86/amd/pmf/tee-if.c+5−9
@@ -27,12 +27,6 @@ module_param(pb_side_load, bool, 0444); · 26 unmodified lines
27MODULE_PARM_DESC(pb_side_load, "Sideload policy binaries debug policy failures");27MODULE_PARM_DESC(pb_side_load, "Sideload policy binaries debug policy failures");
28#endif28#endif
29 29
30static const uuid_t amd_pmf_ta_uuid[] = { UUID_INIT(0xd9b39bf2, 0x66bd, 0x4154, 0xaf, 0xb8, 0x8a,
31 0xcc, 0x2b, 0x2b, 0x60, 0xd6),
32 UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d, 0xb1, 0x2d, 0xc5,
33 0x29, 0xb1, 0x3d, 0x85, 0x43),
34 };
35
36static const char *amd_pmf_uevent_as_str(unsigned int state)30static const char *amd_pmf_uevent_as_str(unsigned int state)
37{31{
38 switch (state) {32 switch (state) {
@@ -324,7 +318,7 @@ static void amd_pmf_invoke_cmd(struct work_struct *work) · 285 unmodified lines
324 schedule_delayed_work(&dev->pb_work, msecs_to_jiffies(pb_actions_ms));318 schedule_delayed_work(&dev->pb_work, msecs_to_jiffies(pb_actions_ms));
325}319}
326 320
327static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev)321int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev)
328{322{
329 struct cookie_header *header;323 struct cookie_header *header;
330 int res;324 int res;
@@ -480,7 +474,7 @@ static int amd_pmf_register_input_device(struct amd_pmf_dev *dev) · 149 unmodified lines
480 return 0;474 return 0;
481}475}
482 476
483static int amd_pmf_tee_init(struct amd_pmf_dev *dev, const uuid_t *uuid)477int amd_pmf_tee_init(struct amd_pmf_dev *dev, const uuid_t *uuid)
484{478{
485 u32 size;479 u32 size;
486 int ret;480 int ret;
@@ -528,7 +522,7 @@ out_ctx: · 41 unmodified lines
528 return ret;522 return ret;
529}523}
530 524
531static void amd_pmf_tee_deinit(struct amd_pmf_dev *dev)525void amd_pmf_tee_deinit(struct amd_pmf_dev *dev)
532{526{
533 if (!dev->tee_ctx)527 if (!dev->tee_ctx)
534 return;528 return;
@@ -591,6 +585,8 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev) · 56 unmodified lines
591 status = ret == TA_PMF_TYPE_SUCCESS;585 status = ret == TA_PMF_TYPE_SUCCESS;
592 if (status) {586 if (status) {
593 dev->cb_flag = true;587 dev->cb_flag = true;
588 dev->cbi_buf.head = 0;
589 dev->cbi_buf.tail = 0;
594 break;590 break;
595 }591 }
596 amd_pmf_tee_deinit(dev);592 amd_pmf_tee_deinit(dev);
drivers/platform/x86/amd/wbrf.c+8−17
@@ -42,8 +42,6 @@ static BLOCKING_NOTIFIER_HEAD(wbrf_chain_head); · 41 unmodified lines
42static int wbrf_record(struct acpi_device *adev, uint8_t action, struct wbrf_ranges_in_out *in)42static int wbrf_record(struct acpi_device *adev, uint8_t action, struct wbrf_ranges_in_out *in)
43{43{
44 union acpi_object argv4;44 union acpi_object argv4;
45 union acpi_object *tmp;
46 union acpi_object *obj;
47 u32 num_of_ranges = 0;45 u32 num_of_ranges = 0;
48 u32 num_of_elements;46 u32 num_of_elements;
49 u32 arg_idx = 0;47 u32 arg_idx = 0;
@@ -74,7 +72,7 @@ static int wbrf_record(struct acpi_device *adev, uint8_t action, struct wbrf_ran · 24 unmodified lines
74 */72 */
75 num_of_elements = 2 * num_of_ranges + 2;73 num_of_elements = 2 * num_of_ranges + 2;
76 74
77 tmp = kcalloc(num_of_elements, sizeof(*tmp), GFP_KERNEL);75 union acpi_object *tmp __free(kfree) = kcalloc(num_of_elements, sizeof(*tmp), GFP_KERNEL);
78 if (!tmp)76 if (!tmp)
79 return -ENOMEM;77 return -ENOMEM;
80 78
@@ -101,26 +99,19 @@ static int wbrf_record(struct acpi_device *adev, uint8_t action, struct wbrf_ran · 20 unmodified lines
101 tmp[arg_idx++].integer.value = in->band_list[i].end;99 tmp[arg_idx++].integer.value = in->band_list[i].end;
102 }100 }
103 101
104 obj = acpi_evaluate_dsm(adev->handle, &wifi_acpi_dsm_guid,102 union acpi_object *obj __free(kfree) =
105 WBRF_REVISION, WBRF_RECORD, &argv4);103 acpi_evaluate_dsm(adev->handle, &wifi_acpi_dsm_guid,
104 WBRF_REVISION, WBRF_RECORD, &argv4);
106 105
107 if (!obj) {106 if (!obj)
108 kfree(tmp);
109 return -EINVAL;107 return -EINVAL;
110 }
111 108
112 if (obj->type != ACPI_TYPE_INTEGER) {109 if (obj->type != ACPI_TYPE_INTEGER)
113 ret = -EINVAL;110 return -EINVAL;
114 goto out;
115 }
116 111
117 ret = obj->integer.value;112 ret = obj->integer.value;
118 if (ret)113 if (ret)
119 ret = -EINVAL;114 return -EINVAL;
120
121out:
122 ACPI_FREE(obj);
123 kfree(tmp);
124 115
125 return ret;116 return ret;
126}117}
drivers/platform/x86/asus-wmi.c+196−27
@@ -31,13 +31,13 @@ · 30 unmodified lines
31#include <linux/pci.h>31#include <linux/pci.h>
32#include <linux/pci_hotplug.h>32#include <linux/pci_hotplug.h>
33#include <linux/platform_data/x86/asus-wmi.h>33#include <linux/platform_data/x86/asus-wmi.h>
34#include <linux/platform_data/x86/asus-wmi-leds-ids.h>
35#include <linux/platform_device.h>34#include <linux/platform_device.h>
36#include <linux/platform_profile.h>35#include <linux/platform_profile.h>
37#include <linux/power_supply.h>36#include <linux/power_supply.h>
38#include <linux/rfkill.h>37#include <linux/rfkill.h>
39#include <linux/seq_file.h>38#include <linux/seq_file.h>
40#include <linux/slab.h>39#include <linux/slab.h>
40#include <linux/spinlock.h>
41#include <linux/types.h>41#include <linux/types.h>
42#include <linux/units.h>42#include <linux/units.h>
43 43
@@ -256,6 +256,9 @@ struct asus_wmi { · 212 unmodified lines
256 int tpd_led_wk;256 int tpd_led_wk;
257 struct led_classdev kbd_led;257 struct led_classdev kbd_led;
258 int kbd_led_wk;258 int kbd_led_wk;
259 bool kbd_led_notify;
260 bool kbd_led_avail;
261 bool kbd_led_registered;
259 struct led_classdev lightbar_led;262 struct led_classdev lightbar_led;
260 int lightbar_led_wk;263 int lightbar_led_wk;
261 struct led_classdev micmute_led;264 struct led_classdev micmute_led;
@@ -264,6 +267,7 @@ struct asus_wmi { · 2 unmodified lines
264 struct work_struct tpd_led_work;267 struct work_struct tpd_led_work;
265 struct work_struct wlan_led_work;268 struct work_struct wlan_led_work;
266 struct work_struct lightbar_led_work;269 struct work_struct lightbar_led_work;
270 struct work_struct kbd_led_work;
267 271
268 struct asus_rfkill wlan;272 struct asus_rfkill wlan;
269 struct asus_rfkill bluetooth;273 struct asus_rfkill bluetooth;
@@ -1615,6 +1619,144 @@ static void asus_wmi_battery_exit(struct asus_wmi *asus) · 1345 unmodified lines
1615 1619
1616/* LEDs ***********************************************************************/1620/* LEDs ***********************************************************************/
1617 1621
1622struct asus_hid_ref {
1623 struct list_head listeners;
1624 struct asus_wmi *asus;
1625 /* Protects concurrent access from hid-asus and asus-wmi to leds */
1626 spinlock_t lock;
1627};
1628
1629static struct asus_hid_ref asus_ref = {
1630 .listeners = LIST_HEAD_INIT(asus_ref.listeners),
1631 .asus = NULL,
1632 /*
1633 * Protects .asus, .asus.kbd_led_{wk,notify}, and .listener refs. Other
1634 * asus variables are read-only after .asus is set.
1635 *
1636 * The led cdev device is not protected because it calls backlight_get
1637 * during initialization, which would result in a nested lock attempt.
1638 *
1639 * The led cdev is safe to access without a lock because if
1640 * kbd_led_avail is true it is initialized before .asus is set and never
1641 * changed until .asus is dropped. If kbd_led_avail is false, the led
1642 * cdev is registered by the workqueue, which is single-threaded and
1643 * cancelled before asus-wmi would access the led cdev to unregister it.
1644 *
1645 * A spinlock is used, because the protected variables can be accessed
1646 * from an IRQ context from asus-hid.
1647 */
1648 .lock = __SPIN_LOCK_UNLOCKED(asus_ref.lock),
1649};
1650
1651/*
1652 * Allows registering hid-asus listeners that want to be notified of
1653 * keyboard backlight changes.
1654 */
1655int asus_hid_register_listener(struct asus_hid_listener *bdev)
1656{
1657 struct asus_wmi *asus;
1658
1659 guard(spinlock_irqsave)(&asus_ref.lock);
1660 list_add_tail(&bdev->list, &asus_ref.listeners);
1661 asus = asus_ref.asus;
1662 if (asus)
1663 queue_work(asus->led_workqueue, &asus->kbd_led_work);
1664 return 0;
1665}
1666EXPORT_SYMBOL_GPL(asus_hid_register_listener);
1667
1668/*
1669 * Allows unregistering hid-asus listeners that were added with
1670 * asus_hid_register_listener().
1671 */
1672void asus_hid_unregister_listener(struct asus_hid_listener *bdev)
1673{
1674 guard(spinlock_irqsave)(&asus_ref.lock);
1675 list_del(&bdev->list);
1676}
1677EXPORT_SYMBOL_GPL(asus_hid_unregister_listener);
1678
1679static void do_kbd_led_set(struct led_classdev *led_cdev, int value);
1680
1681static void kbd_led_update_all(struct work_struct *work)
1682{
1683 struct asus_wmi *asus;
1684 bool registered, notify;
1685 int ret, value;
1686
1687 asus = container_of(work, struct asus_wmi, kbd_led_work);
1688
1689 scoped_guard(spinlock_irqsave, &asus_ref.lock) {
1690 registered = asus->kbd_led_registered;
1691 value = asus->kbd_led_wk;
1692 notify = asus->kbd_led_notify;
1693 }
1694
1695 if (!registered) {
1696 /*
1697 * This workqueue runs under asus-wmi, which means probe has
1698 * completed and asus-wmi will keep running until it finishes.
1699 * Therefore, we can safely register the LED without holding
1700 * a spinlock.
1701 */
1702 ret = devm_led_classdev_register(&asus->platform_device->dev,
1703 &asus->kbd_led);
1704 if (!ret) {
1705 scoped_guard(spinlock_irqsave, &asus_ref.lock)
1706 asus->kbd_led_registered = true;
1707 } else {
1708 pr_warn("Failed to register keyboard backlight LED: %d\n", ret);
1709 return;
1710 }
1711 }
1712
1713 if (value >= 0)
1714 do_kbd_led_set(&asus->kbd_led, value);
1715 if (notify) {
1716 scoped_guard(spinlock_irqsave, &asus_ref.lock)
1717 asus->kbd_led_notify = false;
1718 led_classdev_notify_brightness_hw_changed(&asus->kbd_led, value);
1719 }
1720}
1721
1722/*
1723 * This function is called from hid-asus to inform asus-wmi of brightness
1724 * changes initiated by the keyboard backlight keys.
1725 */
1726int asus_hid_event(enum asus_hid_event event)
1727{
1728 struct asus_wmi *asus;
1729 int brightness;
1730
1731 guard(spinlock_irqsave)(&asus_ref.lock);
1732 asus = asus_ref.asus;
1733 if (!asus || !asus->kbd_led_registered)
1734 return -EBUSY;
1735
1736 brightness = asus->kbd_led_wk;
1737
1738 switch (event) {
1739 case ASUS_EV_BRTUP:
1740 brightness += 1;
1741 break;
1742 case ASUS_EV_BRTDOWN:
1743 brightness -= 1;
1744 break;
1745 case ASUS_EV_BRTTOGGLE:
1746 if (brightness >= ASUS_EV_MAX_BRIGHTNESS)
1747 brightness = 0;
1748 else
1749 brightness += 1;
1750 break;
1751 }
1752
1753 asus->kbd_led_wk = clamp_val(brightness, 0, ASUS_EV_MAX_BRIGHTNESS);
1754 asus->kbd_led_notify = true;
1755 queue_work(asus->led_workqueue, &asus->kbd_led_work);
1756 return 0;
1757}
1758EXPORT_SYMBOL_GPL(asus_hid_event);
1759
1618/*1760/*
1619 * These functions actually update the LED's, and are called from a1761 * These functions actually update the LED's, and are called from a
1620 * workqueue. By doing this as separate work rather than when the LED1762 * workqueue. By doing this as separate work rather than when the LED
@@ -1661,7 +1803,8 @@ static void kbd_led_update(struct asus_wmi *asus) · 40 unmodified lines
1661{1803{
1662 int ctrl_param = 0;1804 int ctrl_param = 0;
1663 1805
1664 ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);1806 scoped_guard(spinlock_irqsave, &asus_ref.lock)
1807 ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
1665 asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);1808 asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);
1666}1809}
1667 1810
@@ -1694,14 +1837,21 @@ static int kbd_led_read(struct asus_wmi *asus, int *level, int *env) · 26 unmodified lines
1694 1837
1695static void do_kbd_led_set(struct led_classdev *led_cdev, int value)1838static void do_kbd_led_set(struct led_classdev *led_cdev, int value)
1696{1839{
1840 struct asus_hid_listener *listener;
1697 struct asus_wmi *asus;1841 struct asus_wmi *asus;
1698 int max_level;
1699 1842
1700 asus = container_of(led_cdev, struct asus_wmi, kbd_led);1843 asus = container_of(led_cdev, struct asus_wmi, kbd_led);
1701 max_level = asus->kbd_led.max_brightness;
1702 1844
1703 asus->kbd_led_wk = clamp_val(value, 0, max_level);1845 scoped_guard(spinlock_irqsave, &asus_ref.lock)
1704 kbd_led_update(asus);1846 asus->kbd_led_wk = clamp_val(value, 0, ASUS_EV_MAX_BRIGHTNESS);
1847
1848 if (asus->kbd_led_avail)
1849 kbd_led_update(asus);
1850
1851 scoped_guard(spinlock_irqsave, &asus_ref.lock) {
1852 list_for_each_entry(listener, &asus_ref.listeners, list)
1853 listener->brightness_set(listener, asus->kbd_led_wk);
1854 }
1705}1855}
1706 1856
1707static int kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value)1857static int kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value)
@@ -1716,10 +1866,11 @@ static int kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value) · 8 unmodified lines
1716 1866
1717static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value)1867static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value)
1718{1868{
1719 struct led_classdev *led_cdev = &asus->kbd_led;1869 scoped_guard(spinlock_irqsave, &asus_ref.lock) {
1720 1870 asus->kbd_led_wk = value;
1721 do_kbd_led_set(led_cdev, value);1871 asus->kbd_led_notify = true;
1722 led_classdev_notify_brightness_hw_changed(led_cdev, asus->kbd_led_wk);1872 }
1873 queue_work(asus->led_workqueue, &asus->kbd_led_work);
1723}1874}
1724 1875
1725static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)1876static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
@@ -1729,10 +1880,18 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) · 3 unmodified lines
1729 1880
1730 asus = container_of(led_cdev, struct asus_wmi, kbd_led);1881 asus = container_of(led_cdev, struct asus_wmi, kbd_led);
1731 1882
1883 scoped_guard(spinlock_irqsave, &asus_ref.lock) {
1884 if (!asus->kbd_led_avail)
1885 return asus->kbd_led_wk;
1886 }
1887
1732 retval = kbd_led_read(asus, &value, NULL);1888 retval = kbd_led_read(asus, &value, NULL);
1733 if (retval < 0)1889 if (retval < 0)
1734 return retval;1890 return retval;
1735 1891
1892 scoped_guard(spinlock_irqsave, &asus_ref.lock)
1893 asus->kbd_led_wk = value;
1894
1736 return value;1895 return value;
1737}1896}
1738 1897
@@ -1844,7 +2003,9 @@ static int camera_led_set(struct led_classdev *led_cdev, · 105 unmodified lines
1844 2003
1845static void asus_wmi_led_exit(struct asus_wmi *asus)2004static void asus_wmi_led_exit(struct asus_wmi *asus)
1846{2005{
1847 led_classdev_unregister(&asus->kbd_led);2006 scoped_guard(spinlock_irqsave, &asus_ref.lock)
2007 asus_ref.asus = NULL;
2008
1848 led_classdev_unregister(&asus->tpd_led);2009 led_classdev_unregister(&asus->tpd_led);
1849 led_classdev_unregister(&asus->wlan_led);2010 led_classdev_unregister(&asus->wlan_led);
1850 led_classdev_unregister(&asus->lightbar_led);2011 led_classdev_unregister(&asus->lightbar_led);
@@ -1882,22 +2043,26 @@ static int asus_wmi_led_init(struct asus_wmi *asus) · 31 unmodified lines
1882 goto error;2043 goto error;
1883 }2044 }
1884 2045
1885 if (!kbd_led_read(asus, &led_val, NULL) && !dmi_check_system(asus_use_hid_led_dmi_ids)) {2046 asus->kbd_led.name = "asus::kbd_backlight";
1886 pr_info("using asus-wmi for asus::kbd_backlight\n");2047 asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
1887 asus->kbd_led_wk = led_val;2048 asus->kbd_led.brightness_set_blocking = kbd_led_set;
1888 asus->kbd_led.name = "asus::kbd_backlight";2049 asus->kbd_led.brightness_get = kbd_led_get;
1889 asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;2050 asus->kbd_led.max_brightness = ASUS_EV_MAX_BRIGHTNESS;
1890 asus->kbd_led.brightness_set_blocking = kbd_led_set;2051 asus->kbd_led_avail = !kbd_led_read(asus, &led_val, NULL);
1891 asus->kbd_led.brightness_get = kbd_led_get;2052 INIT_WORK(&asus->kbd_led_work, kbd_led_update_all);
1892 asus->kbd_led.max_brightness = 3;
1893 2053
2054 if (asus->kbd_led_avail) {
2055 asus->kbd_led_wk = led_val;
1894 if (num_rgb_groups != 0)2056 if (num_rgb_groups != 0)
1895 asus->kbd_led.groups = kbd_rgb_mode_groups;2057 asus->kbd_led.groups = kbd_rgb_mode_groups;
2058 } else {
2059 asus->kbd_led_wk = -1;
2060 }
1896 2061
1897 rv = led_classdev_register(&asus->platform_device->dev,2062 scoped_guard(spinlock_irqsave, &asus_ref.lock) {
1898 &asus->kbd_led);2063 asus_ref.asus = asus;
1899 if (rv)2064 if (asus->kbd_led_avail || !list_empty(&asus_ref.listeners))
1900 goto error;2065 queue_work(asus->led_workqueue, &asus->kbd_led_work);
1901 }2066 }
1902 2067
1903 if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_WIRELESS_LED)2068 if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_WIRELESS_LED)
@@ -4372,6 +4537,7 @@ static int asus_wmi_get_event_code(union acpi_object *obj) · 2468 unmodified lines
4372 4537
4373static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)4538static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
4374{4539{
4540 enum led_brightness led_value;
4375 unsigned int key_value = 1;4541 unsigned int key_value = 1;
4376 bool autorelease = 1;4542 bool autorelease = 1;
4377 4543
@@ -4388,19 +4554,22 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) · 10 unmodified lines
4388 return;4554 return;
4389 }4555 }
4390 4556
4557 scoped_guard(spinlock_irqsave, &asus_ref.lock)
4558 led_value = asus->kbd_led_wk;
4559
4391 if (code == NOTIFY_KBD_BRTUP) {4560 if (code == NOTIFY_KBD_BRTUP) {
4392 kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);4561 kbd_led_set_by_kbd(asus, led_value + 1);
4393 return;4562 return;
4394 }4563 }
4395 if (code == NOTIFY_KBD_BRTDWN) {4564 if (code == NOTIFY_KBD_BRTDWN) {
4396 kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1);4565 kbd_led_set_by_kbd(asus, led_value - 1);
4397 return;4566 return;
4398 }4567 }
4399 if (code == NOTIFY_KBD_BRTTOGGLE) {4568 if (code == NOTIFY_KBD_BRTTOGGLE) {
4400 if (asus->kbd_led_wk == asus->kbd_led.max_brightness)4569 if (led_value >= ASUS_EV_MAX_BRIGHTNESS)
4401 kbd_led_set_by_kbd(asus, 0);4570 kbd_led_set_by_kbd(asus, 0);
4402 else4571 else
4403 kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);4572 kbd_led_set_by_kbd(asus, led_value + 1);
4404 return;4573 return;
4405 }4574 }
4406 4575
drivers/platform/x86/hp/hp-wmi.c+489−114
@@ -13,23 +13,28 @@ · 12 unmodified lines
13 13
14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15 15
16#include <linux/kernel.h>16#include <linux/acpi.h>
17#include <linux/module.h>17#include <linux/cleanup.h>
18#include <linux/compiler_attributes.h>
19#include <linux/dmi.h>
20#include <linux/fixp-arith.h>
21#include <linux/hwmon.h>
18#include <linux/init.h>22#include <linux/init.h>
19#include <linux/slab.h>
20#include <linux/types.h>
21#include <linux/input.h>23#include <linux/input.h>
22#include <linux/input/sparse-keymap.h>24#include <linux/input/sparse-keymap.h>
25#include <linux/kernel.h>
26#include <linux/limits.h>
27#include <linux/minmax.h>
28#include <linux/module.h>
29#include <linux/mutex.h>
23#include <linux/platform_device.h>30#include <linux/platform_device.h>
24#include <linux/platform_profile.h>31#include <linux/platform_profile.h>
25#include <linux/hwmon.h>
26#include <linux/acpi.h>
27#include <linux/mutex.h>
28#include <linux/cleanup.h>
29#include <linux/power_supply.h>32#include <linux/power_supply.h>
30#include <linux/rfkill.h>33#include <linux/rfkill.h>
34#include <linux/slab.h>
31#include <linux/string.h>35#include <linux/string.h>
32#include <linux/dmi.h>36#include <linux/types.h>
37#include <linux/workqueue.h>
33 38
34MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");39MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
35MODULE_DESCRIPTION("HP laptop WMI driver");40MODULE_DESCRIPTION("HP laptop WMI driver");
@@ -41,9 +46,13 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4"); · 5 unmodified lines
41#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"46#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
42#define HPWMI_BIOS_GUID "5FB7F034-2C63-45E9-BE91-3D44E2C707E4"47#define HPWMI_BIOS_GUID "5FB7F034-2C63-45E9-BE91-3D44E2C707E4"
43 48
44#define HP_OMEN_EC_THERMAL_PROFILE_FLAGS_OFFSET 0x6249enum hp_ec_offsets {
45#define HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET 0x6350 HP_EC_OFFSET_UNKNOWN = 0x00,
46#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x9551 HP_VICTUS_S_EC_THERMAL_PROFILE_OFFSET = 0x59,
52 HP_OMEN_EC_THERMAL_PROFILE_FLAGS_OFFSET = 0x62,
53 HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET = 0x63,
54 HP_OMEN_EC_THERMAL_PROFILE_OFFSET = 0x95,
55};
47 56
48#define HP_FAN_SPEED_AUTOMATIC 0x0057#define HP_FAN_SPEED_AUTOMATIC 0x00
49#define HP_POWER_LIMIT_DEFAULT 0x0058#define HP_POWER_LIMIT_DEFAULT 0x00
@@ -53,6 +62,70 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4"); · 3 unmodified lines
53 62
54#define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required63#define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required
55 64
65enum hp_thermal_profile_omen_v0 {
66 HP_OMEN_V0_THERMAL_PROFILE_DEFAULT = 0x00,
67 HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE = 0x01,
68 HP_OMEN_V0_THERMAL_PROFILE_COOL = 0x02,
69};
70
71enum hp_thermal_profile_omen_v1 {
72 HP_OMEN_V1_THERMAL_PROFILE_DEFAULT = 0x30,
73 HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE = 0x31,
74 HP_OMEN_V1_THERMAL_PROFILE_COOL = 0x50,
75};
76
77enum hp_thermal_profile_omen_flags {
78 HP_OMEN_EC_FLAGS_TURBO = 0x04,
79 HP_OMEN_EC_FLAGS_NOTIMER = 0x02,
80 HP_OMEN_EC_FLAGS_JUSTSET = 0x01,
81};
82
83enum hp_thermal_profile_victus {
84 HP_VICTUS_THERMAL_PROFILE_DEFAULT = 0x00,
85 HP_VICTUS_THERMAL_PROFILE_PERFORMANCE = 0x01,
86 HP_VICTUS_THERMAL_PROFILE_QUIET = 0x03,
87};
88
89enum hp_thermal_profile_victus_s {
90 HP_VICTUS_S_THERMAL_PROFILE_DEFAULT = 0x00,
91 HP_VICTUS_S_THERMAL_PROFILE_PERFORMANCE = 0x01,
92};
93
94enum hp_thermal_profile {
95 HP_THERMAL_PROFILE_PERFORMANCE = 0x00,
96 HP_THERMAL_PROFILE_DEFAULT = 0x01,
97 HP_THERMAL_PROFILE_COOL = 0x02,
98 HP_THERMAL_PROFILE_QUIET = 0x03,
99};
100
101
102struct thermal_profile_params {
103 u8 performance;
104 u8 balanced;
105 u8 low_power;
106 u8 ec_tp_offset;
107};
108
109static const struct thermal_profile_params victus_s_thermal_params = {
110 .performance = HP_VICTUS_S_THERMAL_PROFILE_PERFORMANCE,
111 .balanced = HP_VICTUS_S_THERMAL_PROFILE_DEFAULT,
112 .low_power = HP_VICTUS_S_THERMAL_PROFILE_DEFAULT,
113 .ec_tp_offset = HP_EC_OFFSET_UNKNOWN,
114};
115
116static const struct thermal_profile_params omen_v1_thermal_params = {
117 .performance = HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE,
118 .balanced = HP_OMEN_V1_THERMAL_PROFILE_DEFAULT,
119 .low_power = HP_OMEN_V1_THERMAL_PROFILE_DEFAULT,
120 .ec_tp_offset = HP_VICTUS_S_EC_THERMAL_PROFILE_OFFSET,
121};
122
123/*
124 * A generic pointer for the currently-active board's thermal profile
125 * parameters.
126 */
127static struct thermal_profile_params *active_thermal_profile_params;
128
56/* DMI board names of devices that should use the omen specific path for129/* DMI board names of devices that should use the omen specific path for
57 * thermal profiles.130 * thermal profiles.
58 * This was obtained by taking a look in the windows omen command center131 * This was obtained by taking a look in the windows omen command center
@@ -99,12 +172,40 @@ static const char * const victus_thermal_profile_boards[] = { · 40 unmodified lines
99};172};
100 173
101/* DMI Board names of Victus 16-r and Victus 16-s laptops */174/* DMI Board names of Victus 16-r and Victus 16-s laptops */
102static const char * const victus_s_thermal_profile_boards[] = {175static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst = {
103 "8BBE", "8BD4", "8BD5",176 {
104 "8C78", "8C99", "8C9C",177 .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BBE") },
105 "8D41",178 .driver_data = (void *)&victus_s_thermal_params,
179 },
180 {
181 .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BD4") },
182 .driver_data = (void *)&victus_s_thermal_params,
183 },
184 {
185 .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BD5") },
186 .driver_data = (void *)&victus_s_thermal_params,
187 },
188 {
189 .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C78") },
190 .driver_data = (void *)&omen_v1_thermal_params,
191 },
192 {
193 .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C99") },
194 .driver_data = (void *)&victus_s_thermal_params,
195 },
196 {
197 .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C9C") },
198 .driver_data = (void *)&victus_s_thermal_params,
199 },
200 {
201 .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D41") },
202 .driver_data = (void *)&victus_s_thermal_params,
203 },
204 {},
106};205};
107 206
207static bool is_victus_s_board;
208
108enum hp_wmi_radio {209enum hp_wmi_radio {
109 HPWMI_WIFI = 0x0,210 HPWMI_WIFI = 0x0,
110 HPWMI_BLUETOOTH = 0x1,211 HPWMI_BLUETOOTH = 0x1,
@@ -190,7 +291,8 @@ enum hp_wmi_gm_commandtype { · 79 unmodified lines
190 HPWMI_SET_GPU_THERMAL_MODES_QUERY = 0x22,291 HPWMI_SET_GPU_THERMAL_MODES_QUERY = 0x22,
191 HPWMI_SET_POWER_LIMITS_QUERY = 0x29,292 HPWMI_SET_POWER_LIMITS_QUERY = 0x29,
192 HPWMI_VICTUS_S_FAN_SPEED_GET_QUERY = 0x2D,293 HPWMI_VICTUS_S_FAN_SPEED_GET_QUERY = 0x2D,
193 HPWMI_FAN_SPEED_SET_QUERY = 0x2E,294 HPWMI_VICTUS_S_FAN_SPEED_SET_QUERY = 0x2E,
295 HPWMI_VICTUS_S_GET_FAN_TABLE_QUERY = 0x2F,
194};296};
195 297
196enum hp_wmi_command {298enum hp_wmi_command {
@@ -225,42 +327,6 @@ enum hp_wireless2_bits { · 28 unmodified lines
225 HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD,327 HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD,
226};328};
227 329
228enum hp_thermal_profile_omen_v0 {
229 HP_OMEN_V0_THERMAL_PROFILE_DEFAULT = 0x00,
230 HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE = 0x01,
231 HP_OMEN_V0_THERMAL_PROFILE_COOL = 0x02,
232};
233
234enum hp_thermal_profile_omen_v1 {
235 HP_OMEN_V1_THERMAL_PROFILE_DEFAULT = 0x30,
236 HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE = 0x31,
237 HP_OMEN_V1_THERMAL_PROFILE_COOL = 0x50,
238};
239
240enum hp_thermal_profile_omen_flags {
241 HP_OMEN_EC_FLAGS_TURBO = 0x04,
242 HP_OMEN_EC_FLAGS_NOTIMER = 0x02,
243 HP_OMEN_EC_FLAGS_JUSTSET = 0x01,
244};
245
246enum hp_thermal_profile_victus {
247 HP_VICTUS_THERMAL_PROFILE_DEFAULT = 0x00,
248 HP_VICTUS_THERMAL_PROFILE_PERFORMANCE = 0x01,
249 HP_VICTUS_THERMAL_PROFILE_QUIET = 0x03,
250};
251
252enum hp_thermal_profile_victus_s {
253 HP_VICTUS_S_THERMAL_PROFILE_DEFAULT = 0x00,
254 HP_VICTUS_S_THERMAL_PROFILE_PERFORMANCE = 0x01,
255};
256
257enum hp_thermal_profile {
258 HP_THERMAL_PROFILE_PERFORMANCE = 0x00,
259 HP_THERMAL_PROFILE_DEFAULT = 0x01,
260 HP_THERMAL_PROFILE_COOL = 0x02,
261 HP_THERMAL_PROFILE_QUIET = 0x03,
262};
263
264#define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW)330#define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW)
265#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)331#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)
266 332
@@ -348,6 +414,58 @@ static const char * const tablet_chassis_types[] = { · 81 unmodified lines
348 414
349#define DEVICE_MODE_TABLET 0x06415#define DEVICE_MODE_TABLET 0x06
350 416
417#define CPU_FAN 0
418#define GPU_FAN 1
419
420enum pwm_modes {
421 PWM_MODE_MAX = 0,
422 PWM_MODE_MANUAL = 1,
423 PWM_MODE_AUTO = 2,
424};
425
426struct hp_wmi_hwmon_priv {
427 u8 min_rpm;
428 u8 max_rpm;
429 u8 gpu_delta;
430 u8 mode;
431 u8 pwm;
432 struct delayed_work keep_alive_dwork;
433};
434
435struct victus_s_fan_table_header {
436 u8 unknown;
437 u8 num_entries;
438} __packed;
439
440struct victus_s_fan_table_entry {
441 u8 cpu_rpm;
442 u8 gpu_rpm;
443 u8 unknown;
444} __packed;
445
446struct victus_s_fan_table {
447 struct victus_s_fan_table_header header;
448 struct victus_s_fan_table_entry entries[];
449} __packed;
450
451/*
452 * 90s delay to prevent the firmware from resetting fan mode after fixed
453 * 120s timeout
454 */
455#define KEEP_ALIVE_DELAY_SECS 90
456
457static inline u8 rpm_to_pwm(u8 rpm, struct hp_wmi_hwmon_priv *priv)
458{
459 return fixp_linear_interpolate(0, 0, priv->max_rpm, U8_MAX,
460 clamp_val(rpm, 0, priv->max_rpm));
461}
462
463static inline u8 pwm_to_rpm(u8 pwm, struct hp_wmi_hwmon_priv *priv)
464{
465 return fixp_linear_interpolate(0, 0, U8_MAX, priv->max_rpm,
466 clamp_val(pwm, 0, U8_MAX));
467}
468
351/* map output size to the corresponding WMI method id */469/* map output size to the corresponding WMI method id */
352static inline int encode_outsize_for_pvsz(int outsize)470static inline int encode_outsize_for_pvsz(int outsize)
353{471{
@@ -637,41 +755,53 @@ static int hp_wmi_fan_speed_max_set(int enabled) · 283 unmodified lines
637 return enabled;755 return enabled;
638}756}
639 757
640static int hp_wmi_fan_speed_reset(void)758static int hp_wmi_fan_speed_set(struct hp_wmi_hwmon_priv *priv, u8 speed)
641{759{
642 u8 fan_speed[2] = { HP_FAN_SPEED_AUTOMATIC, HP_FAN_SPEED_AUTOMATIC };760 u8 fan_speed[2];
643 int ret;761 int gpu_speed, ret;
644 762
645 ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_SET_QUERY, HPWMI_GM,763 fan_speed[CPU_FAN] = speed;
646 &fan_speed, sizeof(fan_speed), 0);764 fan_speed[GPU_FAN] = speed;
647 765
648 return ret;766 /*
649}767 * GPU fan speed is always a little higher than CPU fan speed, we fetch
650 768 * this delta value from the fan table during hwmon init.
651static int hp_wmi_fan_speed_max_reset(void)769 * Exception: Speed is set to HP_FAN_SPEED_AUTOMATIC, to revert to
652{770 * automatic mode.
653 int ret;771 */
772 if (speed != HP_FAN_SPEED_AUTOMATIC) {
773 gpu_speed = speed + priv->gpu_delta;
774 fan_speed[GPU_FAN] = clamp_val(gpu_speed, 0, U8_MAX);
775 }
654 776
777 ret = hp_wmi_get_fan_count_userdefine_trigger();
778 if (ret < 0)
779 return ret;
780 /* Max fans need to be explicitly disabled */
655 ret = hp_wmi_fan_speed_max_set(0);781 ret = hp_wmi_fan_speed_max_set(0);
656 if (ret)782 if (ret < 0)
657 return ret;783 return ret;
784 ret = hp_wmi_perform_query(HPWMI_VICTUS_S_FAN_SPEED_SET_QUERY, HPWMI_GM,
785 &fan_speed, sizeof(fan_speed), 0);
658 786
659 /* Disabling max fan speed on Victus s1xxx laptops needs a 2nd step: */
660 ret = hp_wmi_fan_speed_reset();
661 return ret;787 return ret;
662}788}
663 789
664static int hp_wmi_fan_speed_max_get(void)790static int hp_wmi_fan_speed_reset(struct hp_wmi_hwmon_priv *priv)
665{791{
666 int val = 0, ret;792 return hp_wmi_fan_speed_set(priv, HP_FAN_SPEED_AUTOMATIC);
793}
667 794
668 ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_GET_QUERY, HPWMI_GM,795static int hp_wmi_fan_speed_max_reset(struct hp_wmi_hwmon_priv *priv)
669 &val, zero_if_sup(val), sizeof(val));796{
797 int ret;
670 798
799 ret = hp_wmi_fan_speed_max_set(0);
671 if (ret)800 if (ret)
672 return ret < 0 ? ret : -EINVAL;801 return ret;
673 802
674 return val;803 /* Disabling max fan speed on Victus s1xxx laptops needs a 2nd step: */
804 return hp_wmi_fan_speed_reset(priv);
675}805}
676 806
677static int __init hp_wmi_bios_2008_later(void)807static int __init hp_wmi_bios_2008_later(void)
@@ -1581,15 +1711,8 @@ static int platform_profile_victus_set_ec(enum platform_profile_option profile) · 903 unmodified lines
1581 1711
1582static bool is_victus_s_thermal_profile(void)1712static bool is_victus_s_thermal_profile(void)
1583{1713{
1584 const char *board_name;1714 /* Initialised in driver init, hence safe to use here */
1585 1715 return is_victus_s_board;
1586 board_name = dmi_get_system_info(DMI_BOARD_NAME);
1587 if (!board_name)
1588 return false;
1589
1590 return match_string(victus_s_thermal_profile_boards,
1591 ARRAY_SIZE(victus_s_thermal_profile_boards),
1592 board_name) >= 0;
1593}1716}
1594 1717
1595static int victus_s_gpu_thermal_profile_get(bool *ctgp_enable,1718static int victus_s_gpu_thermal_profile_get(bool *ctgp_enable,
@@ -1670,27 +1793,86 @@ static int victus_s_set_cpu_pl1_pl2(u8 pl1, u8 pl2) · 74 unmodified lines
1670 return ret;1793 return ret;
1671}1794}
1672 1795
1796static int platform_profile_victus_s_get_ec(enum platform_profile_option *profile)
1797{
1798 int ret = 0;
1799 bool current_ctgp_state, current_ppab_state;
1800 u8 current_dstate, current_gpu_slowdown_temp, tp;
1801 const struct thermal_profile_params *params;
1802
1803 params = active_thermal_profile_params;
1804 if (params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN) {
1805 *profile = active_platform_profile;
1806 return 0;
1807 }
1808
1809 ret = ec_read(params->ec_tp_offset, &tp);
1810 if (ret)
1811 return ret;
1812
1813 /*
1814 * We cannot use active_thermal_profile_params here, because boards
1815 * like 8C78 have tp == 0x0 || tp == 0x1 after cold boot, but logically
1816 * it should have tp == 0x30 || tp == 0x31, as corrected by the Omen
1817 * Gaming Hub on windows. Hence accept both of these values.
1818 */
1819 if (tp == victus_s_thermal_params.performance ||
1820 tp == omen_v1_thermal_params.performance) {
1821 *profile = PLATFORM_PROFILE_PERFORMANCE;
1822 } else if (tp == victus_s_thermal_params.balanced ||
1823 tp == omen_v1_thermal_params.balanced) {
1824 /*
1825 * Since both PLATFORM_PROFILE_LOW_POWER and
1826 * PLATFORM_PROFILE_BALANCED share the same thermal profile
1827 * parameter value, hence to differentiate between them, we
1828 * query the GPU CTGP and PPAB states and compare based off of
1829 * that.
1830 */
1831 ret = victus_s_gpu_thermal_profile_get(&current_ctgp_state,
1832 &current_ppab_state,
1833 &current_dstate,
1834 &current_gpu_slowdown_temp);
1835 if (ret < 0)
1836 return ret;
1837 if (current_ctgp_state == 0 && current_ppab_state == 0)
1838 *profile = PLATFORM_PROFILE_LOW_POWER;
1839 else if (current_ctgp_state == 0 && current_ppab_state == 1)
1840 *profile = PLATFORM_PROFILE_BALANCED;
1841 else
1842 return -EINVAL;
1843 } else {
1844 return -EINVAL;
1845 }
1846
1847 return 0;
1848}
1849
1673static int platform_profile_victus_s_set_ec(enum platform_profile_option profile)1850static int platform_profile_victus_s_set_ec(enum platform_profile_option profile)
1674{1851{
1852 struct thermal_profile_params *params;
1675 bool gpu_ctgp_enable, gpu_ppab_enable;1853 bool gpu_ctgp_enable, gpu_ppab_enable;
1676 u8 gpu_dstate; /* Test shows 1 = 100%, 2 = 50%, 3 = 25%, 4 = 12.5% */1854 u8 gpu_dstate; /* Test shows 1 = 100%, 2 = 50%, 3 = 25%, 4 = 12.5% */
1677 int err, tp;1855 int err, tp;
1678 1856
1857 params = active_thermal_profile_params;
1858 if (!params)
1859 return -ENODEV;
1860
1679 switch (profile) {1861 switch (profile) {
1680 case PLATFORM_PROFILE_PERFORMANCE:1862 case PLATFORM_PROFILE_PERFORMANCE:
1681 tp = HP_VICTUS_S_THERMAL_PROFILE_PERFORMANCE;1863 tp = params->performance;
1682 gpu_ctgp_enable = true;1864 gpu_ctgp_enable = true;
1683 gpu_ppab_enable = true;1865 gpu_ppab_enable = true;
1684 gpu_dstate = 1;1866 gpu_dstate = 1;
1685 break;1867 break;
1686 case PLATFORM_PROFILE_BALANCED:1868 case PLATFORM_PROFILE_BALANCED:
1687 tp = HP_VICTUS_S_THERMAL_PROFILE_DEFAULT;1869 tp = params->balanced;
1688 gpu_ctgp_enable = false;1870 gpu_ctgp_enable = false;
1689 gpu_ppab_enable = true;1871 gpu_ppab_enable = true;
1690 gpu_dstate = 1;1872 gpu_dstate = 1;
1691 break;1873 break;
1692 case PLATFORM_PROFILE_LOW_POWER:1874 case PLATFORM_PROFILE_LOW_POWER:
1693 tp = HP_VICTUS_S_THERMAL_PROFILE_DEFAULT;1875 tp = params->low_power;
1694 gpu_ctgp_enable = false;1876 gpu_ctgp_enable = false;
1695 gpu_ppab_enable = false;1877 gpu_ppab_enable = false;
1696 gpu_dstate = 1;1878 gpu_dstate = 1;
@@ -1832,6 +2014,7 @@ static int victus_s_powersource_event(struct notifier_block *nb, · 135 unmodified lines
1832 void *data)2014 void *data)
1833{2015{
1834 struct acpi_bus_event *event_entry = data;2016 struct acpi_bus_event *event_entry = data;
2017 enum platform_profile_option actual_profile;
1835 int err;2018 int err;
1836 2019
1837 if (strcmp(event_entry->device_class, ACPI_AC_CLASS) != 0)2020 if (strcmp(event_entry->device_class, ACPI_AC_CLASS) != 0)
@@ -1839,6 +2022,17 @@ static int victus_s_powersource_event(struct notifier_block *nb, · 1 unmodified line
1839 2022
1840 pr_debug("Received power source device event\n");2023 pr_debug("Received power source device event\n");
1841 2024
2025 guard(mutex)(&active_platform_profile_lock);
2026 err = platform_profile_victus_s_get_ec(&actual_profile);
2027 if (err < 0) {
2028 /*
2029 * Although we failed to get the current platform profile, we
2030 * still want the other event consumers to process it.
2031 */
2032 pr_warn("Failed to read current platform profile (%d)\n", err);
2033 return NOTIFY_DONE;
2034 }
2035
1842 /*2036 /*
1843 * Switching to battery power source while Performance mode is active2037 * Switching to battery power source while Performance mode is active
1844 * needs manual triggering of CPU power limits. Same goes when switching2038 * needs manual triggering of CPU power limits. Same goes when switching
@@ -1847,7 +2041,7 @@ static int victus_s_powersource_event(struct notifier_block *nb, · 2 unmodified lines
1847 * Seen on HP 16-s1034nf (board 8C9C) with F.11 and F.13 BIOS versions.2041 * Seen on HP 16-s1034nf (board 8C9C) with F.11 and F.13 BIOS versions.
1848 */2042 */
1849 2043
1850 if (active_platform_profile == PLATFORM_PROFILE_PERFORMANCE) {2044 if (actual_profile == PLATFORM_PROFILE_PERFORMANCE) {
1851 pr_debug("Triggering CPU PL1/PL2 actualization\n");2045 pr_debug("Triggering CPU PL1/PL2 actualization\n");
1852 err = victus_s_set_cpu_pl1_pl2(HP_POWER_LIMIT_DEFAULT,2046 err = victus_s_set_cpu_pl1_pl2(HP_POWER_LIMIT_DEFAULT,
1853 HP_POWER_LIMIT_DEFAULT);2047 HP_POWER_LIMIT_DEFAULT);
@@ -1958,11 +2152,22 @@ static int thermal_profile_setup(struct platform_device *device) · 104 unmodified lines
1958 ops = &platform_profile_victus_ops;2152 ops = &platform_profile_victus_ops;
1959 } else if (is_victus_s_thermal_profile()) {2153 } else if (is_victus_s_thermal_profile()) {
1960 /*2154 /*
1961 * Being unable to retrieve laptop's current thermal profile,2155 * For an unknown EC layout board, platform_profile_victus_s_get_ec(),
1962 * during this setup, we set it to Balanced by default.2156 * behaves like a wrapper around active_platform_profile, to avoid using
2157 * uninitialized data, we default to PLATFORM_PROFILE_BALANCED.
1963 */2158 */
1964 active_platform_profile = PLATFORM_PROFILE_BALANCED;2159 if (active_thermal_profile_params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN) {
2160 active_platform_profile = PLATFORM_PROFILE_BALANCED;
2161 } else {
2162 err = platform_profile_victus_s_get_ec(&active_platform_profile);
2163 if (err < 0)
2164 return err;
2165 }
1965 2166
2167 /*
2168 * call thermal profile write command to ensure that the
2169 * firmware correctly sets the OEM variables
2170 */
1966 err = platform_profile_victus_s_set_ec(active_platform_profile);2171 err = platform_profile_victus_s_set_ec(active_platform_profile);
1967 if (err < 0)2172 if (err < 0)
1968 return err;2173 return err;
@@ -2031,6 +2236,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) · 62 unmodified lines
2031static void __exit hp_wmi_bios_remove(struct platform_device *device)2236static void __exit hp_wmi_bios_remove(struct platform_device *device)
2032{2237{
2033 int i;2238 int i;
2239 struct hp_wmi_hwmon_priv *priv;
2034 2240
2035 for (i = 0; i < rfkill2_count; i++) {2241 for (i = 0; i < rfkill2_count; i++) {
2036 rfkill_unregister(rfkill2[i].rfkill);2242 rfkill_unregister(rfkill2[i].rfkill);
@@ -2049,6 +2255,10 @@ static void __exit hp_wmi_bios_remove(struct platform_device *device) · 12 unmodified lines
2049 rfkill_unregister(wwan_rfkill);2255 rfkill_unregister(wwan_rfkill);
2050 rfkill_destroy(wwan_rfkill);2256 rfkill_destroy(wwan_rfkill);
2051 }2257 }
2258
2259 priv = platform_get_drvdata(device);
2260 if (priv)
2261 cancel_delayed_work_sync(&priv->keep_alive_dwork);
2052}2262}
2053 2263
2054static int hp_wmi_resume_handler(struct device *device)2264static int hp_wmi_resume_handler(struct device *device)
@@ -2108,12 +2318,56 @@ static struct platform_driver hp_wmi_driver __refdata = { · 53 unmodified lines
2108 .remove = __exit_p(hp_wmi_bios_remove),2318 .remove = __exit_p(hp_wmi_bios_remove),
2109};2319};
2110 2320
2321static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
2322{
2323 int ret;
2324
2325 switch (priv->mode) {
2326 case PWM_MODE_MAX:
2327 if (is_victus_s_thermal_profile())
2328 hp_wmi_get_fan_count_userdefine_trigger();
2329 ret = hp_wmi_fan_speed_max_set(1);
2330 if (ret < 0)
2331 return ret;
2332 schedule_delayed_work(&priv->keep_alive_dwork,
2333 secs_to_jiffies(KEEP_ALIVE_DELAY_SECS));
2334 return 0;
2335 case PWM_MODE_MANUAL:
2336 if (!is_victus_s_thermal_profile())
2337 return -EOPNOTSUPP;
2338 ret = hp_wmi_fan_speed_set(priv, pwm_to_rpm(priv->pwm, priv));
2339 if (ret < 0)
2340 return ret;
2341 schedule_delayed_work(&priv->keep_alive_dwork,
2342 secs_to_jiffies(KEEP_ALIVE_DELAY_SECS));
2343 return 0;
2344 case PWM_MODE_AUTO:
2345 if (is_victus_s_thermal_profile()) {
2346 hp_wmi_get_fan_count_userdefine_trigger();
2347 ret = hp_wmi_fan_speed_max_reset(priv);
2348 } else {
2349 ret = hp_wmi_fan_speed_max_set(0);
2350 }
2351 if (ret < 0)
2352 return ret;
2353 cancel_delayed_work_sync(&priv->keep_alive_dwork);
2354 return 0;
2355 default:
2356 /* shouldn't happen */
2357 return -EINVAL;
2358 }
2359
2360 return 0;
2361}
2362
2111static umode_t hp_wmi_hwmon_is_visible(const void *data,2363static umode_t hp_wmi_hwmon_is_visible(const void *data,
2112 enum hwmon_sensor_types type,2364 enum hwmon_sensor_types type,
2113 u32 attr, int channel)2365 u32 attr, int channel)
2114{2366{
2115 switch (type) {2367 switch (type) {
2116 case hwmon_pwm:2368 case hwmon_pwm:
2369 if (attr == hwmon_pwm_input && !is_victus_s_thermal_profile())
2370 return 0;
2117 return 0644;2371 return 0644;
2118 case hwmon_fan:2372 case hwmon_fan:
2119 if (is_victus_s_thermal_profile()) {2373 if (is_victus_s_thermal_profile()) {
@@ -2134,8 +2388,10 @@ static umode_t hp_wmi_hwmon_is_visible(const void *data, · 14 unmodified lines
2134static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,2388static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
2135 u32 attr, int channel, long *val)2389 u32 attr, int channel, long *val)
2136{2390{
2137 int ret;2391 struct hp_wmi_hwmon_priv *priv;
2392 int rpm, ret;
2138 2393
2394 priv = dev_get_drvdata(dev);
2139 switch (type) {2395 switch (type) {
2140 case hwmon_fan:2396 case hwmon_fan:
2141 if (is_victus_s_thermal_profile())2397 if (is_victus_s_thermal_profile())
@@ -2147,16 +2403,21 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, · 5 unmodified lines
2147 *val = ret;2403 *val = ret;
2148 return 0;2404 return 0;
2149 case hwmon_pwm:2405 case hwmon_pwm:
2150 switch (hp_wmi_fan_speed_max_get()) {2406 if (attr == hwmon_pwm_input) {
2151 case 0:2407 if (!is_victus_s_thermal_profile())
2152 /* 0 is automatic fan, which is 2 for hwmon */2408 return -EOPNOTSUPP;
2153 *val = 2;2409
2410 rpm = hp_wmi_get_fan_speed_victus_s(channel);
2411 if (rpm < 0)
2412 return rpm;
2413 *val = rpm_to_pwm(rpm / 100, priv);
2154 return 0;2414 return 0;
2155 case 1:2415 }
2156 /* 1 is max fan, which is 02416 switch (priv->mode) {
2157 * (no fan speed control) for hwmon2417 case PWM_MODE_MAX:
2158 */2418 case PWM_MODE_MANUAL:
2159 *val = 0;2419 case PWM_MODE_AUTO:
2420 *val = priv->mode;
2160 return 0;2421 return 0;
2161 default:2422 default:
2162 /* shouldn't happen */2423 /* shouldn't happen */
@@ -2170,23 +2431,46 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, · 7 unmodified lines
2170static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,2431static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
2171 u32 attr, int channel, long val)2432 u32 attr, int channel, long val)
2172{2433{
2434 struct hp_wmi_hwmon_priv *priv;
2435 int rpm;
2436
2437 priv = dev_get_drvdata(dev);
2173 switch (type) {2438 switch (type) {
2174 case hwmon_pwm:2439 case hwmon_pwm:
2440 if (attr == hwmon_pwm_input) {
2441 if (!is_victus_s_thermal_profile())
2442 return -EOPNOTSUPP;
2443 /* PWM input is invalid when not in manual mode */
2444 if (priv->mode != PWM_MODE_MANUAL)
2445 return -EINVAL;
2446
2447 /* ensure PWM input is within valid fan speeds */
2448 rpm = pwm_to_rpm(val, priv);
2449 rpm = clamp_val(rpm, priv->min_rpm, priv->max_rpm);
2450 priv->pwm = rpm_to_pwm(rpm, priv);
2451 return hp_wmi_apply_fan_settings(priv);
2452 }
2175 switch (val) {2453 switch (val) {
2176 case 0:2454 case PWM_MODE_MAX:
2177 if (is_victus_s_thermal_profile())2455 priv->mode = PWM_MODE_MAX;
2178 hp_wmi_get_fan_count_userdefine_trigger();2456 return hp_wmi_apply_fan_settings(priv);
2179 /* 0 is no fan speed control (max), which is 1 for us */2457 case PWM_MODE_MANUAL:
2180 return hp_wmi_fan_speed_max_set(1);2458 if (!is_victus_s_thermal_profile())
2181 case 2:2459 return -EOPNOTSUPP;
2182 /* 2 is automatic speed control, which is 0 for us */2460 /*
2183 if (is_victus_s_thermal_profile()) {2461 * When switching to manual mode, set fan speed to
2184 hp_wmi_get_fan_count_userdefine_trigger();2462 * current RPM values to ensure a smooth transition.
2185 return hp_wmi_fan_speed_max_reset();2463 */
2186 } else2464 rpm = hp_wmi_get_fan_speed_victus_s(channel);
2187 return hp_wmi_fan_speed_max_set(0);2465 if (rpm < 0)
2466 return rpm;
2467 priv->pwm = rpm_to_pwm(rpm / 100, priv);
2468 priv->mode = PWM_MODE_MANUAL;
2469 return hp_wmi_apply_fan_settings(priv);
2470 case PWM_MODE_AUTO:
2471 priv->mode = PWM_MODE_AUTO;
2472 return hp_wmi_apply_fan_settings(priv);
2188 default:2473 default:
2189 /* we don't support manual fan speed control */
2190 return -EINVAL;2474 return -EINVAL;
2191 }2475 }
2192 default:2476 default:
@@ -2196,7 +2480,7 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, · 3 unmodified lines
2196 2480
2197static const struct hwmon_channel_info * const info[] = {2481static const struct hwmon_channel_info * const info[] = {
2198 HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT),2482 HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT),
2199 HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE),2483 HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE | HWMON_PWM_INPUT),
2200 NULL2484 NULL
2201};2485};
2202 2486
@@ -2211,12 +2495,70 @@ static const struct hwmon_chip_info chip_info = { · 8 unmodified lines
2211 .info = info,2495 .info = info,
2212};2496};
2213 2497
2498static void hp_wmi_hwmon_keep_alive_handler(struct work_struct *work)
2499{
2500 struct delayed_work *dwork;
2501 struct hp_wmi_hwmon_priv *priv;
2502
2503 dwork = to_delayed_work(work);
2504 priv = container_of(dwork, struct hp_wmi_hwmon_priv, keep_alive_dwork);
2505 /*
2506 * Re-apply the current hwmon context settings.
2507 * NOTE: hp_wmi_apply_fan_settings will handle the re-scheduling.
2508 */
2509 hp_wmi_apply_fan_settings(priv);
2510}
2511
2512static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv)
2513{
2514 u8 fan_data[128] = { 0 };
2515 struct victus_s_fan_table *fan_table;
2516 u8 min_rpm, max_rpm, gpu_delta;
2517 int ret;
2518
2519 /* Default behaviour on hwmon init is automatic mode */
2520 priv->mode = PWM_MODE_AUTO;
2521
2522 /* Bypass all non-Victus S devices */
2523 if (!is_victus_s_thermal_profile())
2524 return 0;
2525
2526 ret = hp_wmi_perform_query(HPWMI_VICTUS_S_GET_FAN_TABLE_QUERY,
2527 HPWMI_GM, &fan_data, 4, sizeof(fan_data));
2528 if (ret)
2529 return ret;
2530
2531 fan_table = (struct victus_s_fan_table *)fan_data;
2532 if (fan_table->header.num_entries == 0 ||
2533 sizeof(struct victus_s_fan_table_header) +
2534 sizeof(struct victus_s_fan_table_entry) * fan_table->header.num_entries > sizeof(fan_data))
2535 return -EINVAL;
2536
2537 min_rpm = fan_table->entries[0].cpu_rpm;
2538 max_rpm = fan_table->entries[fan_table->header.num_entries - 1].cpu_rpm;
2539 gpu_delta = fan_table->entries[0].gpu_rpm - fan_table->entries[0].cpu_rpm;
2540 priv->min_rpm = min_rpm;
2541 priv->max_rpm = max_rpm;
2542 priv->gpu_delta = gpu_delta;
2543
2544 return 0;
2545}
2546
2214static int hp_wmi_hwmon_init(void)2547static int hp_wmi_hwmon_init(void)
2215{2548{
2216 struct device *dev = &hp_wmi_platform_dev->dev;2549 struct device *dev = &hp_wmi_platform_dev->dev;
2550 struct hp_wmi_hwmon_priv *priv;
2217 struct device *hwmon;2551 struct device *hwmon;
2552 int ret;
2553
2554 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
2555 if (!priv)
2556 return -ENOMEM;
2218 2557
2219 hwmon = devm_hwmon_device_register_with_info(dev, "hp", &hp_wmi_driver,2558 ret = hp_wmi_setup_fan_settings(priv);
2559 if (ret)
2560 return ret;
2561 hwmon = devm_hwmon_device_register_with_info(dev, "hp", priv,
2220 &chip_info, NULL);2562 &chip_info, NULL);
2221 2563
2222 if (IS_ERR(hwmon)) {2564 if (IS_ERR(hwmon)) {
@@ -2224,9 +2566,37 @@ static int hp_wmi_hwmon_init(void) · 1 unmodified line
2224 return PTR_ERR(hwmon);2566 return PTR_ERR(hwmon);
2225 }2567 }
2226 2568
2569 INIT_DELAYED_WORK(&priv->keep_alive_dwork, hp_wmi_hwmon_keep_alive_handler);
2570 platform_set_drvdata(hp_wmi_platform_dev, priv);
2571 hp_wmi_apply_fan_settings(priv);
2572
2227 return 0;2573 return 0;
2228}2574}
2229 2575
2576static void __init setup_active_thermal_profile_params(void)
2577{
2578 const struct dmi_system_id *id;
2579
2580 /*
2581 * Currently only victus_s devices use the
2582 * active_thermal_profile_params
2583 */
2584 id = dmi_first_match(victus_s_thermal_profile_boards);
2585 if (id) {
2586 /*
2587 * Marking this boolean is required to ensure that
2588 * is_victus_s_thermal_profile() behaves like a valid
2589 * wrapper.
2590 */
2591 is_victus_s_board = true;
2592 active_thermal_profile_params = id->driver_data;
2593 if (active_thermal_profile_params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN) {
2594 pr_warn("Unknown EC layout for board %s. Thermal profile readback will be disabled. Please report this to platform-driver-x86@vger.kernel.org\n",
2595 dmi_get_system_info(DMI_BOARD_NAME));
2596 }
2597 }
2598}
2599
2230static int __init hp_wmi_init(void)2600static int __init hp_wmi_init(void)
2231{2601{
2232 int event_capable = wmi_has_guid(HPWMI_EVENT_GUID);2602 int event_capable = wmi_has_guid(HPWMI_EVENT_GUID);
@@ -2254,6 +2624,11 @@ static int __init hp_wmi_init(void) · 21 unmodified lines
2254 goto err_destroy_input;2624 goto err_destroy_input;
2255 }2625 }
2256 2626
2627 /*
2628 * Setup active board's thermal profile parameters before
2629 * starting platform driver probe.
2630 */
2631 setup_active_thermal_profile_params();
2257 err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup);2632 err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup);
2258 if (err)2633 if (err)
2259 goto err_unregister_device;2634 goto err_unregister_device;
drivers/platform/x86/intel/pmc/core.c+56−29
@@ -776,16 +776,26 @@ static inline u64 adjust_lpm_residency(struct pmc *pmc, u32 offset, · 775 unmodified lines
776static int pmc_core_substate_res_show(struct seq_file *s, void *unused)776static int pmc_core_substate_res_show(struct seq_file *s, void *unused)
777{777{
778 struct pmc_dev *pmcdev = s->private;778 struct pmc_dev *pmcdev = s->private;
779 struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];779 unsigned int pmc_idx;
780 const int lpm_adj_x2 = pmc->map->lpm_res_counter_step_x2;780
781 u32 offset = pmc->map->lpm_residency_offset;781 for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) {
782 int mode;782 int lpm_adj_x2;
783 struct pmc *pmc;
784 u32 offset;
785 u8 mode;
783 786
784 seq_printf(s, "%-10s %-15s\n", "Substate", "Residency");787 pmc = pmcdev->pmcs[pmc_idx];
788 if (!pmc)
789 continue;
790
791 lpm_adj_x2 = pmc->map->lpm_res_counter_step_x2;
792 offset = pmc->map->lpm_residency_offset;
785 793
786 pmc_for_each_mode(mode, pmcdev) {794 seq_printf(s, "pmc%u %10s %15s\n", pmc_idx, "Substate", "Residency");
787 seq_printf(s, "%-10s %-15llu\n", pmc_lpm_modes[mode],795 pmc_for_each_mode(mode, pmc) {
788 adjust_lpm_residency(pmc, offset + (4 * mode), lpm_adj_x2));796 seq_printf(s, "%15s %15llu\n", pmc_lpm_modes[mode],
797 adjust_lpm_residency(pmc, offset + (4 * mode), lpm_adj_x2));
798 }
789 }799 }
790 800
791 return 0;801 return 0;
@@ -838,10 +848,11 @@ static void pmc_core_substate_req_header_show(struct seq_file *s, int pmc_index, · 46 unmodified lines
838 enum header_type type)848 enum header_type type)
839{849{
840 struct pmc_dev *pmcdev = s->private;850 struct pmc_dev *pmcdev = s->private;
841 int mode;851 struct pmc *pmc = pmcdev->pmcs[pmc_index];
852 u8 mode;
842 853
843 seq_printf(s, "%40s |", "Element");854 seq_printf(s, "%40s |", "Element");
844 pmc_for_each_mode(mode, pmcdev)855 pmc_for_each_mode(mode, pmc)
845 seq_printf(s, " %9s |", pmc_lpm_modes[mode]);856 seq_printf(s, " %9s |", pmc_lpm_modes[mode]);
846 857
847 if (type == HEADER_STATUS) {858 if (type == HEADER_STATUS) {
@@ -880,14 +891,14 @@ static int pmc_core_substate_blk_req_show(struct seq_file *s, void *unused) · 32 unmodified lines
880 const struct pmc_bit_map *map;891 const struct pmc_bit_map *map;
881 892
882 for (map = maps[r_idx]; map->name; map++) {893 for (map = maps[r_idx]; map->name; map++) {
883 int mode;894 u8 mode;
884 895
885 if (!map->blk)896 if (!map->blk)
886 continue;897 continue;
887 898
888 counter = pmc_core_reg_read(pmc, offset);899 counter = pmc_core_reg_read(pmc, offset);
889 seq_printf(s, "pmc%u: %34s |", pmc_idx, map->name);900 seq_printf(s, "pmc%u: %34s |", pmc_idx, map->name);
890 pmc_for_each_mode(mode, pmcdev) {901 pmc_for_each_mode(mode, pmc) {
891 bool required = *lpm_req_regs & BIT(mode);902 bool required = *lpm_req_regs & BIT(mode);
892 903
893 seq_printf(s, " %9s |", required ? "Required" : " ");904 seq_printf(s, " %9s |", required ? "Required" : " ");
@@ -953,14 +964,15 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) · 59 unmodified lines
953 u32 lpm_status;964 u32 lpm_status;
954 u32 lpm_status_live;965 u32 lpm_status_live;
955 const struct pmc_bit_map *map;966 const struct pmc_bit_map *map;
956 int mode, i, len = 32;967 int i, len = 32;
968 u8 mode;
957 969
958 /*970 /*
959 * Capture the requirements and create a mask so that we only971 * Capture the requirements and create a mask so that we only
960 * show an element if it's required for at least one of the972 * show an element if it's required for at least one of the
961 * enabled low power modes973 * enabled low power modes
962 */974 */
963 pmc_for_each_mode(mode, pmcdev)975 pmc_for_each_mode(mode, pmc)
964 req_mask |= lpm_req_regs[mp + (mode * num_maps)];976 req_mask |= lpm_req_regs[mp + (mode * num_maps)];
965 977
966 /* Get the last latched status for this map */978 /* Get the last latched status for this map */
@@ -986,7 +998,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) · 19 unmodified lines
986 seq_printf(s, "pmc%d: %34s |", pmc_idx, map[i].name);998 seq_printf(s, "pmc%d: %34s |", pmc_idx, map[i].name);
987 999
988 /* Loop over the enabled states and display if required */1000 /* Loop over the enabled states and display if required */
989 pmc_for_each_mode(mode, pmcdev) {1001 pmc_for_each_mode(mode, pmc) {
990 bool required = lpm_req_regs[mp + (mode * num_maps)] &1002 bool required = lpm_req_regs[mp + (mode * num_maps)] &
991 bit_mask;1003 bit_mask;
992 seq_printf(s, " %9s |", required ? "Required" : " ");1004 seq_printf(s, " %9s |", required ? "Required" : " ");
@@ -1065,7 +1077,7 @@ static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused) · 72 unmodified lines
1065 struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];1077 struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
1066 bool c10;1078 bool c10;
1067 u32 reg;1079 u32 reg;
1068 int mode;1080 u8 mode;
1069 1081
1070 reg = pmc_core_reg_read(pmc, pmc->map->lpm_sts_latch_en_offset);1082 reg = pmc_core_reg_read(pmc, pmc->map->lpm_sts_latch_en_offset);
1071 if (reg & LPM_STS_LATCH_MODE) {1083 if (reg & LPM_STS_LATCH_MODE) {
@@ -1076,7 +1088,7 @@ static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused) · 4 unmodified lines
1076 c10 = true;1088 c10 = true;
1077 }1089 }
1078 1090
1079 pmc_for_each_mode(mode, pmcdev) {1091 pmc_for_each_mode(mode, pmc) {
1080 if ((BIT(mode) & reg) && !c10)1092 if ((BIT(mode) & reg) && !c10)
1081 seq_printf(s, " [%s]", pmc_lpm_modes[mode]);1093 seq_printf(s, " [%s]", pmc_lpm_modes[mode]);
1082 else1094 else
@@ -1097,8 +1109,9 @@ static ssize_t pmc_core_lpm_latch_mode_write(struct file *file, · 14 unmodified lines
1097 struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];1109 struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
1098 bool clear = false, c10 = false;1110 bool clear = false, c10 = false;
1099 unsigned char buf[8];1111 unsigned char buf[8];
1100 int m, mode;1112 int mode;
1101 u32 reg;1113 u32 reg;
1114 u8 m;
1102 1115
1103 if (count > sizeof(buf) - 1)1116 if (count > sizeof(buf) - 1)
1104 return -EINVAL;1117 return -EINVAL;
@@ -1115,7 +1128,7 @@ static ssize_t pmc_core_lpm_latch_mode_write(struct file *file, · 10 unmodified lines
1115 mode = sysfs_match_string(pmc_lpm_modes, buf);1128 mode = sysfs_match_string(pmc_lpm_modes, buf);
1116 1129
1117 /* Check string matches enabled mode */1130 /* Check string matches enabled mode */
1118 pmc_for_each_mode(m, pmcdev)1131 pmc_for_each_mode(m, pmc)
1119 if (mode == m)1132 if (mode == m)
1120 break;1133 break;
1121 1134
@@ -1211,15 +1224,15 @@ static bool pmc_core_pri_verify(u32 lpm_pri, u8 *mode_order) · 89 unmodified lines
1211 return true;1224 return true;
1212}1225}
1213 1226
1214void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev)1227static void pmc_core_pmc_get_low_power_modes(struct pmc_dev *pmcdev, struct pmc *pmc)
1215{1228{
1216 struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
1217 u8 pri_order[LPM_MAX_NUM_MODES] = LPM_DEFAULT_PRI;1229 u8 pri_order[LPM_MAX_NUM_MODES] = LPM_DEFAULT_PRI;
1218 u8 mode_order[LPM_MAX_NUM_MODES];1230 u8 mode_order[LPM_MAX_NUM_MODES];
1219 u32 lpm_pri;1231 u32 lpm_pri;
1220 u32 lpm_en;1232 u32 lpm_en;
1233 u8 mode;
1221 unsigned int i;1234 unsigned int i;
1222 int mode, p;1235 int p;
1223 1236
1224 /* Use LPM Maps to indicate support for substates */1237 /* Use LPM Maps to indicate support for substates */
1225 if (!pmc->map->lpm_num_maps)1238 if (!pmc->map->lpm_num_maps)
@@ -1230,12 +1243,11 @@ void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev) · 4 unmodified lines
1230 * Lower byte is enough to cover the number of lpm modes for all1243 * Lower byte is enough to cover the number of lpm modes for all
1231 * platforms and hence mask the upper 3 bytes.1244 * platforms and hence mask the upper 3 bytes.
1232 */1245 */
1233 pmcdev->num_lpm_modes = hweight32(lpm_en & 0xFF);1246 pmc->num_lpm_modes = hweight32(lpm_en & 0xFF);
1234 1247
1235 /* Read 32 bit LPM_PRI register */1248 /* Read 32 bit LPM_PRI register */
1236 lpm_pri = pmc_core_reg_read(pmc, pmc->map->lpm_priority_offset);1249 lpm_pri = pmc_core_reg_read(pmc, pmc->map->lpm_priority_offset);
1237 1250
1238
1239 /*1251 /*
1240 * If lpm_pri value passes verification, then override the default1252 * If lpm_pri value passes verification, then override the default
1241 * modes here. Otherwise stick with the default.1253 * modes here. Otherwise stick with the default.
@@ -1254,12 +1266,27 @@ void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev) · 12 unmodified lines
1254 */1266 */
1255 i = 0;1267 i = 0;
1256 for (p = LPM_MAX_NUM_MODES - 1; p >= 0; p--) {1268 for (p = LPM_MAX_NUM_MODES - 1; p >= 0; p--) {
1257 int mode = pri_order[p];1269 u8 mode = pri_order[p];
1258 1270
1259 if (!(BIT(mode) & lpm_en))1271 if (!(BIT(mode) & lpm_en))
1260 continue;1272 continue;
1261 1273
1262 pmcdev->lpm_en_modes[i++] = mode;1274 pmc->lpm_en_modes[i++] = mode;
1275 }
1276}
1277
1278static void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev)
1279{
1280 unsigned int pmc_idx;
1281
1282 for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) {
1283 struct pmc *pmc;
1284
1285 pmc = pmcdev->pmcs[pmc_idx];
1286 if (!pmc)
1287 continue;
1288
1289 pmc_core_pmc_get_low_power_modes(pmcdev, pmc);
1263 }1290 }
1264}1291}
1265 1292
@@ -1490,8 +1517,8 @@ int pmc_core_pmt_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc, struct tel · 224 unmodified lines
1490{1517{
1491 const u8 *lpm_indices;1518 const u8 *lpm_indices;
1492 int num_maps, mode_offset = 0;1519 int num_maps, mode_offset = 0;
1493 int ret, mode;1520 int ret, lpm_size;
1494 int lpm_size;1521 u8 mode;
1495 1522
1496 lpm_indices = pmc->map->lpm_reg_index;1523 lpm_indices = pmc->map->lpm_reg_index;
1497 num_maps = pmc->map->lpm_num_maps;1524 num_maps = pmc->map->lpm_num_maps;
@@ -1504,7 +1531,7 @@ int pmc_core_pmt_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc, struct tel · 6 unmodified lines
1504 return -ENOMEM;1531 return -ENOMEM;
1505 1532
1506 mode_offset = LPM_HEADER_OFFSET + LPM_MODE_OFFSET;1533 mode_offset = LPM_HEADER_OFFSET + LPM_MODE_OFFSET;
1507 pmc_for_each_mode(mode, pmcdev) {1534 pmc_for_each_mode(mode, pmc) {
1508 u32 *req_offset = pmc->lpm_req_regs + (mode * num_maps);1535 u32 *req_offset = pmc->lpm_req_regs + (mode * num_maps);
1509 int m;1536 int m;
1510 1537
drivers/platform/x86/intel/pmc/core.h+7−8
@@ -423,6 +423,8 @@ struct pmc_info { · 422 unmodified lines
423 * specific attributes423 * specific attributes
424 * @lpm_req_regs: List of substate requirements424 * @lpm_req_regs: List of substate requirements
425 * @ltr_ign: Holds LTR ignore data while suspended425 * @ltr_ign: Holds LTR ignore data while suspended
426 * @num_lpm_modes: Count of enabled modes
427 * @lpm_en_modes: Array of enabled modes from lowest to highest priority
426 *428 *
427 * pmc contains info about one power management controller device.429 * pmc contains info about one power management controller device.
428 */430 */
@@ -432,6 +434,8 @@ struct pmc { · 3 unmodified lines
432 const struct pmc_reg_map *map;434 const struct pmc_reg_map *map;
433 u32 *lpm_req_regs;435 u32 *lpm_req_regs;
434 u32 ltr_ign;436 u32 ltr_ign;
437 u8 num_lpm_modes;
438 u8 lpm_en_modes[LPM_MAX_NUM_MODES];
435};439};
436 440
437/**441/**
@@ -446,8 +450,6 @@ struct pmc { · 8 unmodified lines
446 * @pkgc_res_cnt: Array of PKGC residency counters450 * @pkgc_res_cnt: Array of PKGC residency counters
447 * @num_of_pkgc: Number of PKGC451 * @num_of_pkgc: Number of PKGC
448 * @s0ix_counter: S0ix residency (step adjusted)452 * @s0ix_counter: S0ix residency (step adjusted)
449 * @num_lpm_modes: Count of enabled modes
450 * @lpm_en_modes: Array of enabled modes from lowest to highest priority
451 * @suspend: Function to perform platform specific suspend453 * @suspend: Function to perform platform specific suspend
452 * @resume: Function to perform platform specific resume454 * @resume: Function to perform platform specific resume
453 *455 *
@@ -462,8 +464,6 @@ struct pmc_dev { · 8 unmodified lines
462 struct mutex lock; /* generic mutex lock for PMC Core */464 struct mutex lock; /* generic mutex lock for PMC Core */
463 465
464 u64 s0ix_counter;466 u64 s0ix_counter;
465 int num_lpm_modes;
466 int lpm_en_modes[LPM_MAX_NUM_MODES];
467 void (*suspend)(struct pmc_dev *pmcdev);467 void (*suspend)(struct pmc_dev *pmcdev);
468 int (*resume)(struct pmc_dev *pmcdev);468 int (*resume)(struct pmc_dev *pmcdev);
469 469
@@ -535,7 +535,6 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore); · 65 unmodified lines
535 535
536int pmc_core_resume_common(struct pmc_dev *pmcdev);536int pmc_core_resume_common(struct pmc_dev *pmcdev);
537int get_primary_reg_base(struct pmc *pmc);537int get_primary_reg_base(struct pmc *pmc);
538void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev);
539void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids);538void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids);
540void pmc_core_set_device_d3(unsigned int device);539void pmc_core_set_device_d3(unsigned int device);
541 540
@@ -563,10 +562,10 @@ int pmc_core_pmt_get_blk_sub_req(struct pmc_dev *pmcdev, struct pmc *pmc, · 21 unmodified lines
563extern const struct file_operations pmc_core_substate_req_regs_fops;562extern const struct file_operations pmc_core_substate_req_regs_fops;
564extern const struct file_operations pmc_core_substate_blk_req_fops;563extern const struct file_operations pmc_core_substate_blk_req_fops;
565 564
566#define pmc_for_each_mode(mode, pmcdev) \565#define pmc_for_each_mode(mode, pmc) \
567 for (unsigned int __i = 0, __cond; \566 for (unsigned int __i = 0, __cond; \
568 __cond = __i < (pmcdev)->num_lpm_modes, \567 __cond = __i < (pmc)->num_lpm_modes, \
569 __cond && ((mode) = (pmcdev)->lpm_en_modes[__i]), \568 __cond && ((mode) = (pmc)->lpm_en_modes[__i]), \
570 __cond; \569 __cond; \
571 __i++)570 __i++)
572 571
drivers/platform/x86/intel/pmt/class.c+3−3
@@ -140,7 +140,7 @@ guid_show(struct device *dev, struct device_attribute *attr, char *buf) · 139 unmodified lines
140{140{
141 struct intel_pmt_entry *entry = dev_get_drvdata(dev);141 struct intel_pmt_entry *entry = dev_get_drvdata(dev);
142 142
143 return sprintf(buf, "0x%x\n", entry->guid);143 return sysfs_emit(buf, "0x%x\n", entry->guid);
144}144}
145static DEVICE_ATTR_RO(guid);145static DEVICE_ATTR_RO(guid);
146 146
@@ -149,7 +149,7 @@ static ssize_t size_show(struct device *dev, struct device_attribute *attr, · 2 unmodified lines
149{149{
150 struct intel_pmt_entry *entry = dev_get_drvdata(dev);150 struct intel_pmt_entry *entry = dev_get_drvdata(dev);
151 151
152 return sprintf(buf, "%zu\n", entry->size);152 return sysfs_emit(buf, "%zu\n", entry->size);
153}153}
154static DEVICE_ATTR_RO(size);154static DEVICE_ATTR_RO(size);
155 155
@@ -158,7 +158,7 @@ offset_show(struct device *dev, struct device_attribute *attr, char *buf) · 2 unmodified lines
158{158{
159 struct intel_pmt_entry *entry = dev_get_drvdata(dev);159 struct intel_pmt_entry *entry = dev_get_drvdata(dev);
160 160
161 return sprintf(buf, "%lu\n", offset_in_page(entry->base_addr));161 return sysfs_emit(buf, "%lu\n", offset_in_page(entry->base_addr));
162}162}
163static DEVICE_ATTR_RO(offset);163static DEVICE_ATTR_RO(offset);
164 164
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c+56−23
@@ -612,6 +612,9 @@ static long isst_if_core_power_state(void __user *argp) · 611 unmodified lines
612 return -EINVAL;612 return -EINVAL;
613 613
614 if (core_power.get_set) {614 if (core_power.get_set) {
615 if (power_domain_info->write_blocked || !capable(CAP_SYS_ADMIN))
616 return -EPERM;
617
615 _write_cp_info("cp_enable", core_power.enable, SST_CP_CONTROL_OFFSET,618 _write_cp_info("cp_enable", core_power.enable, SST_CP_CONTROL_OFFSET,
616 SST_CP_ENABLE_START, SST_CP_ENABLE_WIDTH, SST_MUL_FACTOR_NONE)619 SST_CP_ENABLE_START, SST_CP_ENABLE_WIDTH, SST_MUL_FACTOR_NONE)
617 _write_cp_info("cp_prio_type", core_power.priority_type, SST_CP_CONTROL_OFFSET,620 _write_cp_info("cp_prio_type", core_power.priority_type, SST_CP_CONTROL_OFFSET,
@@ -656,7 +659,7 @@ static long isst_if_clos_param(void __user *argp) · 38 unmodified lines
656 return -EINVAL;659 return -EINVAL;
657 660
658 if (clos_param.get_set) {661 if (clos_param.get_set) {
659 if (power_domain_info->write_blocked)662 if (power_domain_info->write_blocked || !capable(CAP_SYS_ADMIN))
660 return -EPERM;663 return -EPERM;
661 664
662 _write_cp_info("clos.min_freq", clos_param.min_freq_mhz,665 _write_cp_info("clos.min_freq", clos_param.min_freq_mhz,
@@ -748,7 +751,8 @@ static long isst_if_clos_assoc(void __user *argp) · 85 unmodified lines
748 751
749 power_domain_info = &sst_inst->power_domain_info[part][punit_id];752 power_domain_info = &sst_inst->power_domain_info[part][punit_id];
750 753
751 if (assoc_cmds.get_set && power_domain_info->write_blocked)754 if (assoc_cmds.get_set && (power_domain_info->write_blocked ||
755 !capable(CAP_SYS_ADMIN)))
752 return -EPERM;756 return -EPERM;
753 757
754 offset = SST_CLOS_ASSOC_0_OFFSET +758 offset = SST_CLOS_ASSOC_0_OFFSET +
@@ -925,7 +929,7 @@ static int isst_if_set_perf_level(void __user *argp) · 170 unmodified lines
925 if (!power_domain_info)929 if (!power_domain_info)
926 return -EINVAL;930 return -EINVAL;
927 931
928 if (power_domain_info->write_blocked)932 if (power_domain_info->write_blocked || !capable(CAP_SYS_ADMIN))
929 return -EPERM;933 return -EPERM;
930 934
931 if (!(power_domain_info->pp_header.allowed_level_mask & BIT(perf_level.level)))935 if (!(power_domain_info->pp_header.allowed_level_mask & BIT(perf_level.level)))
@@ -985,7 +989,7 @@ static int isst_if_set_perf_feature(void __user *argp) · 53 unmodified lines
985 if (!power_domain_info)989 if (!power_domain_info)
986 return -EINVAL;990 return -EINVAL;
987 991
988 if (power_domain_info->write_blocked)992 if (power_domain_info->write_blocked || !capable(CAP_SYS_ADMIN))
989 return -EPERM;993 return -EPERM;
990 994
991 _write_pp_info("perf_feature", perf_feature.feature, SST_PP_CONTROL_OFFSET,995 _write_pp_info("perf_feature", perf_feature.feature, SST_PP_CONTROL_OFFSET,
@@ -1717,58 +1721,87 @@ void tpmi_sst_dev_remove(struct auxiliary_device *auxdev) · 725 unmodified lines
1717}1721}
1718EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_remove, "INTEL_TPMI_SST");1722EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_remove, "INTEL_TPMI_SST");
1719 1723
1724#define SST_PP_CAP_CP_ENABLE BIT(0)
1725#define SST_PP_CAP_PP_ENABLE BIT(1)
1726
1720void tpmi_sst_dev_suspend(struct auxiliary_device *auxdev)1727void tpmi_sst_dev_suspend(struct auxiliary_device *auxdev)
1721{1728{
1722 struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev);1729 struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev);
1723 struct tpmi_per_power_domain_info *power_domain_info;1730 struct tpmi_per_power_domain_info *power_domain_info, *pd_info;
1724 struct oobmsm_plat_info *plat_info;1731 struct oobmsm_plat_info *plat_info;
1725 void __iomem *cp_base;1732 void __iomem *cp_base;
1733 int num_resources, i;
1726 1734
1727 plat_info = tpmi_get_platform_data(auxdev);1735 plat_info = tpmi_get_platform_data(auxdev);
1728 if (!plat_info)1736 if (!plat_info)
1729 return;1737 return;
1730 1738
1731 power_domain_info = tpmi_sst->power_domain_info[plat_info->partition];1739 power_domain_info = tpmi_sst->power_domain_info[plat_info->partition];
1740 num_resources = tpmi_sst->number_of_power_domains[plat_info->partition];
1732 1741
1733 cp_base = power_domain_info->sst_base + power_domain_info->sst_header.cp_offset;1742 for (i = 0; i < num_resources; i++) {
1734 power_domain_info->saved_sst_cp_control = readq(cp_base + SST_CP_CONTROL_OFFSET);1743 pd_info = &power_domain_info[i];
1744 if (!pd_info || !pd_info->sst_base)
1745 continue;
1746
1747 if (!(pd_info->sst_header.cap_mask & SST_PP_CAP_CP_ENABLE))
1748 goto process_pp_suspend;
1735 1749
1736 memcpy_fromio(power_domain_info->saved_clos_configs, cp_base + SST_CLOS_CONFIG_0_OFFSET,1750 cp_base = pd_info->sst_base + pd_info->sst_header.cp_offset;
1737 sizeof(power_domain_info->saved_clos_configs));1751 pd_info->saved_sst_cp_control = readq(cp_base + SST_CP_CONTROL_OFFSET);
1752 memcpy_fromio(pd_info->saved_clos_configs, cp_base + SST_CLOS_CONFIG_0_OFFSET,
1753 sizeof(pd_info->saved_clos_configs));
1754 memcpy_fromio(pd_info->saved_clos_assocs, cp_base + SST_CLOS_ASSOC_0_OFFSET,
1755 sizeof(pd_info->saved_clos_assocs));
1738 1756
1739 memcpy_fromio(power_domain_info->saved_clos_assocs, cp_base + SST_CLOS_ASSOC_0_OFFSET,1757process_pp_suspend:
1740 sizeof(power_domain_info->saved_clos_assocs));1758 if (!(pd_info->sst_header.cap_mask & SST_PP_CAP_PP_ENABLE))
1759 continue;
1741 1760
1742 power_domain_info->saved_pp_control = readq(power_domain_info->sst_base +1761 pd_info->saved_pp_control = readq(pd_info->sst_base +
1743 power_domain_info->sst_header.pp_offset +1762 pd_info->sst_header.pp_offset +
1744 SST_PP_CONTROL_OFFSET);1763 SST_PP_CONTROL_OFFSET);
1764 }
1745}1765}
1746EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_suspend, "INTEL_TPMI_SST");1766EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_suspend, "INTEL_TPMI_SST");
1747 1767
1748void tpmi_sst_dev_resume(struct auxiliary_device *auxdev)1768void tpmi_sst_dev_resume(struct auxiliary_device *auxdev)
1749{1769{
1750 struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev);1770 struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev);
1751 struct tpmi_per_power_domain_info *power_domain_info;1771 struct tpmi_per_power_domain_info *power_domain_info, *pd_info;
1752 struct oobmsm_plat_info *plat_info;1772 struct oobmsm_plat_info *plat_info;
1753 void __iomem *cp_base;1773 void __iomem *cp_base;
1774 int num_resources, i;
1754 1775
1755 plat_info = tpmi_get_platform_data(auxdev);1776 plat_info = tpmi_get_platform_data(auxdev);
1756 if (!plat_info)1777 if (!plat_info)
1757 return;1778 return;
1758 1779
1759 power_domain_info = tpmi_sst->power_domain_info[plat_info->partition];1780 power_domain_info = tpmi_sst->power_domain_info[plat_info->partition];
1781 num_resources = tpmi_sst->number_of_power_domains[plat_info->partition];
1782
1783 for (i = 0; i < num_resources; i++) {
1784 pd_info = &power_domain_info[i];
1785 if (!pd_info || !pd_info->sst_base)
1786 continue;
1760 1787
1761 cp_base = power_domain_info->sst_base + power_domain_info->sst_header.cp_offset;1788 if (!(pd_info->sst_header.cap_mask & SST_PP_CAP_CP_ENABLE))
1762 writeq(power_domain_info->saved_sst_cp_control, cp_base + SST_CP_CONTROL_OFFSET);1789 goto process_pp_resume;
1763 1790
1764 memcpy_toio(cp_base + SST_CLOS_CONFIG_0_OFFSET, power_domain_info->saved_clos_configs,1791 cp_base = pd_info->sst_base + pd_info->sst_header.cp_offset;
1765 sizeof(power_domain_info->saved_clos_configs));1792 writeq(pd_info->saved_sst_cp_control, cp_base + SST_CP_CONTROL_OFFSET);
1793 memcpy_toio(cp_base + SST_CLOS_CONFIG_0_OFFSET, pd_info->saved_clos_configs,
1794 sizeof(pd_info->saved_clos_configs));
1795 memcpy_toio(cp_base + SST_CLOS_ASSOC_0_OFFSET, pd_info->saved_clos_assocs,
1796 sizeof(pd_info->saved_clos_assocs));
1766 1797
1767 memcpy_toio(cp_base + SST_CLOS_ASSOC_0_OFFSET, power_domain_info->saved_clos_assocs,1798process_pp_resume:
1768 sizeof(power_domain_info->saved_clos_assocs));1799 if (!(pd_info->sst_header.cap_mask & SST_PP_CAP_PP_ENABLE))
1800 continue;
1769 1801
1770 writeq(power_domain_info->saved_pp_control, power_domain_info->sst_base +1802 writeq(pd_info->saved_pp_control, power_domain_info->sst_base +
1771 power_domain_info->sst_header.pp_offset + SST_PP_CONTROL_OFFSET);1803 pd_info->sst_header.pp_offset + SST_PP_CONTROL_OFFSET);
1804 }
1772}1805}
1773EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_resume, "INTEL_TPMI_SST");1806EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_resume, "INTEL_TPMI_SST");
1774 1807
drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c+7−6
@@ -26,21 +26,21 @@ static ssize_t show_domain_id(struct kobject *kobj, struct kobj_attribute *attr, · 25 unmodified lines
26{26{
27 struct uncore_data *data = container_of(attr, struct uncore_data, domain_id_kobj_attr);27 struct uncore_data *data = container_of(attr, struct uncore_data, domain_id_kobj_attr);
28 28
29 return sprintf(buf, "%u\n", data->domain_id);29 return sysfs_emit(buf, "%u\n", data->domain_id);
30}30}
31 31
32static ssize_t show_fabric_cluster_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)32static ssize_t show_fabric_cluster_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
33{33{
34 struct uncore_data *data = container_of(attr, struct uncore_data, fabric_cluster_id_kobj_attr);34 struct uncore_data *data = container_of(attr, struct uncore_data, fabric_cluster_id_kobj_attr);
35 35
36 return sprintf(buf, "%u\n", data->cluster_id);36 return sysfs_emit(buf, "%u\n", data->cluster_id);
37}37}
38 38
39static ssize_t show_package_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)39static ssize_t show_package_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
40{40{
41 struct uncore_data *data = container_of(attr, struct uncore_data, package_id_kobj_attr);41 struct uncore_data *data = container_of(attr, struct uncore_data, package_id_kobj_attr);
42 42
43 return sprintf(buf, "%u\n", data->package_id);43 return sysfs_emit(buf, "%u\n", data->package_id);
44}44}
45 45
46#define MAX_UNCORE_AGENT_TYPES 446#define MAX_UNCORE_AGENT_TYPES 4
@@ -77,7 +77,7 @@ static ssize_t show_attr(struct uncore_data *data, char *buf, enum uncore_index · 30 unmodified lines
77 if (ret)77 if (ret)
78 return ret;78 return ret;
79 79
80 return sprintf(buf, "%u\n", value);80 return sysfs_emit(buf, "%u\n", value);
81}81}
82 82
83static ssize_t store_attr(struct uncore_data *data, const char *buf, ssize_t count,83static ssize_t store_attr(struct uncore_data *data, const char *buf, ssize_t count,
@@ -269,9 +269,10 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu) · 185 unmodified lines
269 goto uncore_unlock;269 goto uncore_unlock;
270 270
271 data->instance_id = ret;271 data->instance_id = ret;
272 sprintf(data->name, "uncore%02d", ret);272 scnprintf(data->name, sizeof(data->name), "uncore%02d", ret);
273 } else {273 } else {
274 sprintf(data->name, "package_%02d_die_%02d", data->package_id, data->die_id);274 scnprintf(data->name, sizeof(data->name), "package_%02d_die_%02d",
275 data->package_id, data->die_id);
275 }276 }
276 277
277 uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ);278 uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ);
drivers/platform/x86/intel/wmi/sbl-fw-update.c+18−25
@@ -14,7 +14,6 @@ · 13 unmodified lines
14 * https://slimbootloader.github.io/security/firmware-update.html14 * https://slimbootloader.github.io/security/firmware-update.html
15 */15 */
16 16
17#include <linux/acpi.h>
18#include <linux/device.h>17#include <linux/device.h>
19#include <linux/module.h>18#include <linux/module.h>
20#include <linux/slab.h>19#include <linux/slab.h>
@@ -25,41 +24,35 @@ · 4 unmodified lines
25 24
26static int get_fwu_request(struct device *dev, u32 *out)25static int get_fwu_request(struct device *dev, u32 *out)
27{26{
28 union acpi_object *obj;27 struct wmi_buffer buffer;
28 __le32 *result;
29 int ret;
29 30
30 obj = wmidev_block_query(to_wmi_device(dev), 0);31 ret = wmidev_query_block(to_wmi_device(dev), 0, &buffer);
31 if (!obj)32 if (ret < 0)
32 return -ENODEV;33 return ret;
33 34
34 if (obj->type != ACPI_TYPE_INTEGER) {35 if (buffer.length < sizeof(*result)) {
35 dev_warn(dev, "wmidev_block_query returned invalid value\n");36 kfree(buffer.data);
36 kfree(obj);37 return -ENODATA;
37 return -EINVAL;
38 }38 }
39 39
40 *out = obj->integer.value;40 result = buffer.data;
41 kfree(obj);41 *out = le32_to_cpu(*result);
42 kfree(result);
42 43
43 return 0;44 return 0;
44}45}
45 46
46static int set_fwu_request(struct device *dev, u32 in)47static int set_fwu_request(struct device *dev, u32 in)
47{48{
48 struct acpi_buffer input;49 __le32 value = cpu_to_le32(in);
49 acpi_status status;50 struct wmi_buffer buffer = {
50 u32 value;51 .length = sizeof(value),
51 52 .data = &value,
52 value = in;53 };
53 input.length = sizeof(u32);
54 input.pointer = &value;
55
56 status = wmidev_block_set(to_wmi_device(dev), 0, &input);
57 if (ACPI_FAILURE(status)) {
58 dev_err(dev, "wmidev_block_set failed\n");
59 return -ENODEV;
60 }
61 54
62 return 0;55 return wmidev_set_block(to_wmi_device(dev), 0, &buffer);
63}56}
64 57
65static ssize_t firmware_update_request_show(struct device *dev,58static ssize_t firmware_update_request_show(struct device *dev,
drivers/platform/x86/intel/wmi/thunderbolt.c+11−15
@@ -7,7 +7,6 @@ · 6 unmodified lines
7 7
8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9 9
10#include <linux/acpi.h>
11#include <linux/device.h>10#include <linux/device.h>
12#include <linux/fs.h>11#include <linux/fs.h>
13#include <linux/hex.h>12#include <linux/hex.h>
@@ -24,24 +23,21 @@ static ssize_t force_power_store(struct device *dev, · 10 unmodified lines
24 struct device_attribute *attr,23 struct device_attribute *attr,
25 const char *buf, size_t count)24 const char *buf, size_t count)
26{25{
27 struct acpi_buffer input;26 struct wmi_buffer buffer;
28 acpi_status status;27 int ret;
29 u8 mode;28 u8 mode;
30 29
31 input.length = sizeof(u8);30 buffer.length = sizeof(mode);
32 input.pointer = &mode;31 buffer.data = &mode;
32
33 mode = hex_to_bin(buf[0]);33 mode = hex_to_bin(buf[0]);
34 dev_dbg(dev, "force_power: storing %#x\n", mode);34 if (mode > 1)
35 if (mode == 0 || mode == 1) {
36 status = wmidev_evaluate_method(to_wmi_device(dev), 0, 1, &input, NULL);
37 if (ACPI_FAILURE(status)) {
38 dev_dbg(dev, "force_power: failed to evaluate ACPI method\n");
39 return -ENODEV;
40 }
41 } else {
42 dev_dbg(dev, "force_power: unsupported mode\n");
43 return -EINVAL;35 return -EINVAL;
44 }36
37 ret = wmidev_invoke_method(to_wmi_device(dev), 0, 1, &buffer, NULL);
38 if (ret < 0)
39 return ret;
40
45 return count;41 return count;
46}42}
47 43
drivers/platform/x86/lenovo/Kconfig+3−2
@@ -233,7 +233,7 @@ config YT2_1380 · 232 unmodified lines
233 To compile this driver as a module, choose M here: the module will233 To compile this driver as a module, choose M here: the module will
234 be called lenovo-yogabook.234 be called lenovo-yogabook.
235 235
236config LENOVO_WMI_DATA01236config LENOVO_WMI_CAPDATA
237 tristate237 tristate
238 depends on ACPI_WMI238 depends on ACPI_WMI
239 239
@@ -263,8 +263,9 @@ config LENOVO_WMI_GAMEZONE · 23 unmodified lines
263config LENOVO_WMI_TUNING263config LENOVO_WMI_TUNING
264 tristate "Lenovo Other Mode WMI Driver"264 tristate "Lenovo Other Mode WMI Driver"
265 depends on ACPI_WMI265 depends on ACPI_WMI
266 select HWMON
266 select FW_ATTR_CLASS267 select FW_ATTR_CLASS
267 select LENOVO_WMI_DATA01268 select LENOVO_WMI_CAPDATA
268 select LENOVO_WMI_EVENTS269 select LENOVO_WMI_EVENTS
269 select LENOVO_WMI_HELPERS270 select LENOVO_WMI_HELPERS
270 help271 help
drivers/platform/x86/lenovo/Makefile+1−1
@@ -12,7 +12,7 @@ lenovo-target-$(CONFIG_LENOVO_YMC) += ymc.o · 11 unmodified lines
12lenovo-target-$(CONFIG_YOGABOOK) += yogabook.o12lenovo-target-$(CONFIG_YOGABOOK) += yogabook.o
13lenovo-target-$(CONFIG_YT2_1380) += yoga-tab2-pro-1380-fastcharger.o13lenovo-target-$(CONFIG_YT2_1380) += yoga-tab2-pro-1380-fastcharger.o
14lenovo-target-$(CONFIG_LENOVO_WMI_CAMERA) += wmi-camera.o14lenovo-target-$(CONFIG_LENOVO_WMI_CAMERA) += wmi-camera.o
15lenovo-target-$(CONFIG_LENOVO_WMI_DATA01) += wmi-capdata01.o15lenovo-target-$(CONFIG_LENOVO_WMI_CAPDATA) += wmi-capdata.o
16lenovo-target-$(CONFIG_LENOVO_WMI_EVENTS) += wmi-events.o16lenovo-target-$(CONFIG_LENOVO_WMI_EVENTS) += wmi-events.o
17lenovo-target-$(CONFIG_LENOVO_WMI_HELPERS) += wmi-helpers.o17lenovo-target-$(CONFIG_LENOVO_WMI_HELPERS) += wmi-helpers.o
18lenovo-target-$(CONFIG_LENOVO_WMI_GAMEZONE) += wmi-gamezone.o18lenovo-target-$(CONFIG_LENOVO_WMI_GAMEZONE) += wmi-gamezone.o
drivers/platform/x86/lenovo/ideapad-laptop.c+16−23
@@ -219,38 +219,32 @@ MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); · 218 unmodified lines
219static bool allow_v4_dytc;219static bool allow_v4_dytc;
220module_param(allow_v4_dytc, bool, 0444);220module_param(allow_v4_dytc, bool, 0444);
221MODULE_PARM_DESC(allow_v4_dytc,221MODULE_PARM_DESC(allow_v4_dytc,
222 "Enable DYTC version 4 platform-profile support. "222 "Enable DYTC version 4 platform-profile support. If you need this please report this to: platform-driver-x86@vger.kernel.org");
223 "If you need this please report this to: platform-driver-x86@vger.kernel.org");
224 223
225static bool hw_rfkill_switch;224static bool hw_rfkill_switch;
226module_param(hw_rfkill_switch, bool, 0444);225module_param(hw_rfkill_switch, bool, 0444);
227MODULE_PARM_DESC(hw_rfkill_switch,226MODULE_PARM_DESC(hw_rfkill_switch,
228 "Enable rfkill support for laptops with a hw on/off wifi switch/slider. "227 "Enable rfkill support for laptops with a hw on/off wifi switch/slider. If you need this please report this to: platform-driver-x86@vger.kernel.org");
229 "If you need this please report this to: platform-driver-x86@vger.kernel.org");
230 228
231static bool set_fn_lock_led;229static bool set_fn_lock_led;
232module_param(set_fn_lock_led, bool, 0444);230module_param(set_fn_lock_led, bool, 0444);
233MODULE_PARM_DESC(set_fn_lock_led,231MODULE_PARM_DESC(set_fn_lock_led,
234 "Enable driver based updates of the fn-lock LED on fn-lock changes. "232 "Enable driver based updates of the fn-lock LED on fn-lock changes. If you need this please report this to: platform-driver-x86@vger.kernel.org");
235 "If you need this please report this to: platform-driver-x86@vger.kernel.org");
236 233
237static bool ctrl_ps2_aux_port;234static bool ctrl_ps2_aux_port;
238module_param(ctrl_ps2_aux_port, bool, 0444);235module_param(ctrl_ps2_aux_port, bool, 0444);
239MODULE_PARM_DESC(ctrl_ps2_aux_port,236MODULE_PARM_DESC(ctrl_ps2_aux_port,
240 "Enable driver based PS/2 aux port en-/dis-abling on touchpad on/off toggle. "237 "Enable driver based PS/2 aux port en-/dis-abling on touchpad on/off toggle. If you need this please report this to: platform-driver-x86@vger.kernel.org");
241 "If you need this please report this to: platform-driver-x86@vger.kernel.org");
242 238
243static bool touchpad_ctrl_via_ec;239static bool touchpad_ctrl_via_ec;
244module_param(touchpad_ctrl_via_ec, bool, 0444);240module_param(touchpad_ctrl_via_ec, bool, 0444);
245MODULE_PARM_DESC(touchpad_ctrl_via_ec,241MODULE_PARM_DESC(touchpad_ctrl_via_ec,
246 "Enable registering a 'touchpad' sysfs-attribute which can be used to manually "242 "Enable registering a 'touchpad' sysfs-attribute which can be used to manually tell the EC to enable/disable the touchpad. This may not work on all models.");
247 "tell the EC to enable/disable the touchpad. This may not work on all models.");
248 243
249static bool ymc_ec_trigger __read_mostly;244static bool ymc_ec_trigger __read_mostly;
250module_param(ymc_ec_trigger, bool, 0444);245module_param(ymc_ec_trigger, bool, 0444);
251MODULE_PARM_DESC(ymc_ec_trigger,246MODULE_PARM_DESC(ymc_ec_trigger,
252 "Enable EC triggering work-around to force emitting tablet mode events. "247 "Enable EC triggering work-around to force emitting tablet mode events. If you need this please report this to: platform-driver-x86@vger.kernel.org");
253 "If you need this please report this to: platform-driver-x86@vger.kernel.org");
254 248
255/*249/*
256 * shared data250 * shared data
@@ -1446,7 +1440,7 @@ static void ideapad_check_special_buttons(struct ideapad_private *priv) · 1189 unmodified lines
1446 if (read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value))1440 if (read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value))
1447 return;1441 return;
1448 1442
1449 for_each_set_bit (bit, &value, 16) {1443 for_each_set_bit(bit, &value, 16) {
1450 switch (bit) {1444 switch (bit) {
1451 case 6: /* Z570 */1445 case 6: /* Z570 */
1452 case 0: /* Z580 */1446 case 0: /* Z580 */
@@ -1706,11 +1700,10 @@ static int ideapad_kbd_bl_init(struct ideapad_private *priv) · 253 unmodified lines
1706 if (WARN_ON(priv->kbd_bl.initialized))1700 if (WARN_ON(priv->kbd_bl.initialized))
1707 return -EEXIST;1701 return -EEXIST;
1708 1702
1709 if (ideapad_kbd_bl_check_tristate(priv->kbd_bl.type)) {1703 if (ideapad_kbd_bl_check_tristate(priv->kbd_bl.type))
1710 priv->kbd_bl.led.max_brightness = 2;1704 priv->kbd_bl.led.max_brightness = 2;
1711 } else {1705 else
1712 priv->kbd_bl.led.max_brightness = 1;1706 priv->kbd_bl.led.max_brightness = 1;
1713 }
1714 1707
1715 brightness = ideapad_kbd_bl_brightness_get(priv);1708 brightness = ideapad_kbd_bl_brightness_get(priv);
1716 if (brightness < 0)1709 if (brightness < 0)
@@ -1752,7 +1745,7 @@ static enum led_brightness ideapad_fn_lock_led_cdev_get(struct led_classdev *led · 35 unmodified lines
1752}1745}
1753 1746
1754static int ideapad_fn_lock_led_cdev_set(struct led_classdev *led_cdev,1747static int ideapad_fn_lock_led_cdev_set(struct led_classdev *led_cdev,
1755 enum led_brightness brightness)1748 enum led_brightness brightness)
1756{1749{
1757 struct ideapad_private *priv = container_of(led_cdev, struct ideapad_private, fn_lock.led);1750 struct ideapad_private *priv = container_of(led_cdev, struct ideapad_private, fn_lock.led);
1758 1751
@@ -1928,7 +1921,7 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) · 169 unmodified lines
1928 1921
1929 vpc1 = (vpc2 << 8) | vpc1;1922 vpc1 = (vpc2 << 8) | vpc1;
1930 1923
1931 for_each_set_bit (bit, &vpc1, 16) {1924 for_each_set_bit(bit, &vpc1, 16) {
1932 switch (bit) {1925 switch (bit) {
1933 case 13:1926 case 13:
1934 case 11:1927 case 11:
@@ -2142,14 +2135,14 @@ static const enum power_supply_property ideapad_power_supply_props[] = { · 207 unmodified lines
2142 }2135 }
2143 2136
2144DEFINE_IDEAPAD_POWER_SUPPLY_EXTENSION(ideapad_battery_ext_v1,2137DEFINE_IDEAPAD_POWER_SUPPLY_EXTENSION(ideapad_battery_ext_v1,
2145 (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |2138 (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |
2146 BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE))2139 BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE))
2147);2140);
2148 2141
2149DEFINE_IDEAPAD_POWER_SUPPLY_EXTENSION(ideapad_battery_ext_v2,2142DEFINE_IDEAPAD_POWER_SUPPLY_EXTENSION(ideapad_battery_ext_v2,
2150 (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |2143 (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |
2151 BIT(POWER_SUPPLY_CHARGE_TYPE_FAST) |2144 BIT(POWER_SUPPLY_CHARGE_TYPE_FAST) |
2152 BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE))2145 BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE))
2153);2146);
2154 2147
2155static int ideapad_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)2148static int ideapad_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)
drivers/platform/x86/lenovo/thinkpad_acpi.c+206−0
@@ -36,6 +36,7 @@ · 35 unmodified lines
36 36
37#include <linux/acpi.h>37#include <linux/acpi.h>
38#include <linux/backlight.h>38#include <linux/backlight.h>
39#include <linux/bitfield.h>
39#include <linux/bitops.h>40#include <linux/bitops.h>
40#include <linux/delay.h>41#include <linux/delay.h>
41#include <linux/dmi.h>42#include <linux/dmi.h>
@@ -11080,6 +11081,206 @@ static const struct attribute_group auxmac_attr_group = { · 11038 unmodified lines
11080 .attrs = auxmac_attributes,11081 .attrs = auxmac_attributes,
11081};11082};
11082 11083
11084/*************************************************************************
11085 * HWDD subdriver, for the Lenovo Hardware Damage Detection feature.
11086 */
11087
11088#define HWDD_GET_DMG_USBC 0x80000001
11089#define HWDD_GET_CAP 0
11090#define HWDD_NOT_SUPPORTED BIT(31)
11091#define HWDD_SUPPORT_USBC BIT(0)
11092
11093#define PORT_STATUS GENMASK(7, 4)
11094#define LID_STATUS GENMASK(11, 8)
11095#define BASE_STATUS GENMASK(15, 12)
11096#define POS_STATUS GENMASK(3, 2)
11097#define PANEL_STATUS GENMASK(1, 0)
11098
11099#define PORT_DETAIL_OFFSET 16
11100
11101#define PANEL_TOP 0
11102#define PANEL_BASE 1
11103#define PANEL_LEFT 2
11104#define PANEL_RIGHT 3
11105
11106#define POS_LEFT 0
11107#define POS_CENTER 1
11108#define POS_RIGHT 2
11109
11110#define NUM_PORTS 4
11111
11112static bool hwdd_support_available;
11113static bool ucdd_supported;
11114
11115static int hwdd_command(int command, int *output)
11116{
11117 acpi_handle hwdd_handle;
11118
11119 if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "HWDD", &hwdd_handle)))
11120 return -ENODEV;
11121
11122 if (!acpi_evalf(hwdd_handle, output, NULL, "dd", command))
11123 return -EIO;
11124
11125 return 0;
11126}
11127
11128static bool display_damage(char *buf, int *count, char *type, unsigned int dmg_status)
11129{
11130 unsigned char lid_status, base_status, port_status;
11131 unsigned char loc_status, pos_status, panel_status;
11132 bool damage_detected = false;
11133 int i;
11134
11135 port_status = FIELD_GET(PORT_STATUS, dmg_status);
11136 lid_status = FIELD_GET(LID_STATUS, dmg_status);
11137 base_status = FIELD_GET(BASE_STATUS, dmg_status);
11138 for (i = 0; i < NUM_PORTS; i++) {
11139 if (!(dmg_status & BIT(i)) || !(port_status & BIT(i)))
11140 continue;
11141
11142 *count += sysfs_emit_at(buf, *count, "%s: ", type);
11143 loc_status = (dmg_status >> (PORT_DETAIL_OFFSET + (4 * i))) & 0xF;
11144 pos_status = FIELD_GET(POS_STATUS, loc_status);
11145 panel_status = FIELD_GET(PANEL_STATUS, loc_status);
11146
11147 if (lid_status & BIT(i))
11148 *count += sysfs_emit_at(buf, *count, "Lid, ");
11149 if (base_status & BIT(i))
11150 *count += sysfs_emit_at(buf, *count, "Base, ");
11151
11152 switch (pos_status) {
11153 case PANEL_TOP:
11154 *count += sysfs_emit_at(buf, *count, "Top, ");
11155 break;
11156 case PANEL_BASE:
11157 *count += sysfs_emit_at(buf, *count, "Bottom, ");
11158 break;
11159 case PANEL_LEFT:
11160 *count += sysfs_emit_at(buf, *count, "Left, ");
11161 break;
11162 case PANEL_RIGHT:
11163 *count += sysfs_emit_at(buf, *count, "Right, ");
11164 break;
11165 default:
11166 pr_err("Unexpected value %d in switch statement\n", pos_status);
11167 }
11168
11169 switch (panel_status) {
11170 case POS_LEFT:
11171 *count += sysfs_emit_at(buf, *count, "Left port\n");
11172 break;
11173 case POS_CENTER:
11174 *count += sysfs_emit_at(buf, *count, "Center port\n");
11175 break;
11176 case POS_RIGHT:
11177 *count += sysfs_emit_at(buf, *count, "Right port\n");
11178 break;
11179 default:
11180 *count += sysfs_emit_at(buf, *count, "Undefined\n");
11181 break;
11182 }
11183 damage_detected = true;
11184 }
11185 return damage_detected;
11186}
11187
11188/* sysfs type-c damage detection detail */
11189static ssize_t hwdd_detail_show(struct device *dev,
11190 struct device_attribute *attr,
11191 char *buf)
11192{
11193 unsigned int damage_status;
11194 int err, count = 0;
11195
11196 if (!ucdd_supported)
11197 return -ENODEV;
11198
11199 /* Get USB TYPE-C damage status */
11200 err = hwdd_command(HWDD_GET_DMG_USBC, &damage_status);
11201 if (err)
11202 return err;
11203
11204 if (!display_damage(buf, &count, "Type-C", damage_status))
11205 count += sysfs_emit_at(buf, count, "No damage detected\n");
11206
11207 return count;
11208}
11209
11210/* sysfs type-c damage detection capability */
11211static ssize_t hwdd_status_show(struct device *dev,
11212 struct device_attribute *attr,
11213 char *buf)
11214{
11215 unsigned int damage_status, port_status;
11216 int err, i;
11217
11218 if (!ucdd_supported)
11219 return -ENODEV;
11220
11221 /* Get USB TYPE-C damage status */
11222 err = hwdd_command(HWDD_GET_DMG_USBC, &damage_status);
11223 if (err)
11224 return err;
11225
11226 port_status = FIELD_GET(PORT_STATUS, damage_status);
11227 for (i = 0; i < NUM_PORTS; i++) {
11228 if (!(damage_status & BIT(i)))
11229 continue;
11230 if (port_status & BIT(i))
11231 return sysfs_emit(buf, "1\n");
11232 }
11233
11234 return sysfs_emit(buf, "0\n");
11235}
11236static DEVICE_ATTR_RO(hwdd_status);
11237static DEVICE_ATTR_RO(hwdd_detail);
11238
11239static struct attribute *hwdd_attributes[] = {
11240 &dev_attr_hwdd_status.attr,
11241 &dev_attr_hwdd_detail.attr,
11242 NULL
11243};
11244
11245static umode_t hwdd_attr_is_visible(struct kobject *kobj,
11246 struct attribute *attr, int n)
11247{
11248 return hwdd_support_available ? attr->mode : 0;
11249}
11250
11251static const struct attribute_group hwdd_attr_group = {
11252 .is_visible = hwdd_attr_is_visible,
11253 .attrs = hwdd_attributes,
11254};
11255
11256static int tpacpi_hwdd_init(struct ibm_init_struct *iibm)
11257{
11258 int err, output;
11259
11260 /* Below command checks the HWDD damage capability */
11261 err = hwdd_command(HWDD_GET_CAP, &output);
11262 if (err)
11263 return err;
11264
11265 if (!(output & HWDD_NOT_SUPPORTED))
11266 return -ENODEV;
11267
11268 hwdd_support_available = true;
11269
11270 /*
11271 * BIT(0) is assigned to check capability of damage detection is
11272 * supported for USB Type-C port or not.
11273 */
11274 if (output & HWDD_SUPPORT_USBC)
11275 ucdd_supported = true;
11276
11277 return err;
11278}
11279
11280static struct ibm_struct hwdd_driver_data = {
11281 .name = "hwdd",
11282};
11283
11083/* --------------------------------------------------------------------- */11284/* --------------------------------------------------------------------- */
11084 11285
11085static struct attribute *tpacpi_driver_attributes[] = {11286static struct attribute *tpacpi_driver_attributes[] = {
@@ -11139,6 +11340,7 @@ static const struct attribute_group *tpacpi_groups[] = { · 53 unmodified lines
11139 &kbdlang_attr_group,11340 &kbdlang_attr_group,
11140 &dprc_attr_group,11341 &dprc_attr_group,
11141 &auxmac_attr_group,11342 &auxmac_attr_group,
11343 &hwdd_attr_group,
11142 NULL,11344 NULL,
11143};11345};
11144 11346
@@ -11752,6 +11954,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { · 607 unmodified lines
11752 .init = auxmac_init,11954 .init = auxmac_init,
11753 .data = &auxmac_data,11955 .data = &auxmac_data,
11754 },11956 },
11957 {
11958 .init = tpacpi_hwdd_init,
11959 .data = &hwdd_driver_data,
11960 },
11755};11961};
11756 11962
11757static int __init set_ibm_param(const char *val, const struct kernel_param *kp)11963static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
drivers/platform/x86/lenovo/wmi-capdata.c+829−0
@@ -0,0 +1,829 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Lenovo Capability Data WMI Data Block driver.
4 *
5 * Lenovo Capability Data provides information on tunable attributes used by
6 * the "Other Mode" WMI interface.
7 *
8 * Capability Data 00 includes if the attribute is supported by the hardware,
9 * and the default_value. All attributes are independent of thermal modes.
10 *
11 * Capability Data 01 includes if the attribute is supported by the hardware,
12 * and the default_value, max_value, min_value, and step increment. Each
13 * attribute has multiple pages, one for each of the thermal modes managed by
14 * the Gamezone interface.
15 *
16 * Fan Test Data includes the max/min fan speed RPM for each fan. This is
17 * reference data for self-test. If the fan is in good condition, it is capable
18 * to spin faster than max RPM or slower than min RPM.
19 *
20 * Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com>
21 * - Initial implementation (formerly named lenovo-wmi-capdata01)
22 *
23 * Copyright (C) 2025 Rong Zhang <i@rong.moe>
24 * - Unified implementation
25 */
26
27#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
28
29#include <linux/acpi.h>
30#include <linux/bitfield.h>
31#include <linux/bug.h>
32#include <linux/cleanup.h>
33#include <linux/component.h>
34#include <linux/container_of.h>
35#include <linux/device.h>
36#include <linux/dev_printk.h>
37#include <linux/err.h>
38#include <linux/export.h>
39#include <linux/gfp_types.h>
40#include <linux/limits.h>
41#include <linux/module.h>
42#include <linux/mutex.h>
43#include <linux/mutex_types.h>
44#include <linux/notifier.h>
45#include <linux/overflow.h>
46#include <linux/stddef.h>
47#include <linux/types.h>
48#include <linux/wmi.h>
49
50#include "wmi-capdata.h"
51
52#define LENOVO_CAPABILITY_DATA_00_GUID "362A3AFE-3D96-4665-8530-96DAD5BB300E"
53#define LENOVO_CAPABILITY_DATA_01_GUID "7A8F5407-CB67-4D6E-B547-39B3BE018154"
54#define LENOVO_FAN_TEST_DATA_GUID "B642801B-3D21-45DE-90AE-6E86F164FB21"
55
56#define ACPI_AC_CLASS "ac_adapter"
57#define ACPI_AC_NOTIFY_STATUS 0x80
58
59#define LWMI_FEATURE_ID_FAN_TEST 0x05
60
61#define LWMI_ATTR_ID_FAN_TEST \
62 (FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, LWMI_DEVICE_ID_FAN) | \
63 FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, LWMI_FEATURE_ID_FAN_TEST))
64
65enum lwmi_cd_type {
66 LENOVO_CAPABILITY_DATA_00,
67 LENOVO_CAPABILITY_DATA_01,
68 LENOVO_FAN_TEST_DATA,
69 CD_TYPE_NONE = -1,
70};
71
72#define LWMI_CD_TABLE_ITEM(_type) \
73 [_type] = { \
74 .name = #_type, \
75 .type = _type, \
76 }
77
78static const struct lwmi_cd_info {
79 const char *name;
80 enum lwmi_cd_type type;
81} lwmi_cd_table[] = {
82 LWMI_CD_TABLE_ITEM(LENOVO_CAPABILITY_DATA_00),
83 LWMI_CD_TABLE_ITEM(LENOVO_CAPABILITY_DATA_01),
84 LWMI_CD_TABLE_ITEM(LENOVO_FAN_TEST_DATA),
85};
86
87struct lwmi_cd_priv {
88 struct notifier_block acpi_nb; /* ACPI events */
89 struct wmi_device *wdev;
90 struct cd_list *list;
91
92 /*
93 * A capdata device may be a component master of another capdata device.
94 * E.g., lenovo-wmi-other <-> capdata00 <-> capdata_fan
95 * |- master |- component
96 * |- sub-master
97 * |- sub-component
98 */
99 struct lwmi_cd_sub_master_priv {
100 struct device *master_dev;
101 cd_list_cb_t master_cb;
102 struct cd_list *sub_component_list; /* ERR_PTR(-ENODEV) implies no sub-component. */
103 bool registered; /* Has the sub-master been registered? */
104 } *sub_master;
105};
106
107struct cd_list {
108 struct mutex list_mutex; /* list R/W mutex */
109 enum lwmi_cd_type type;
110 u8 count;
111
112 union {
113 DECLARE_FLEX_ARRAY(struct capdata00, cd00);
114 DECLARE_FLEX_ARRAY(struct capdata01, cd01);
115 DECLARE_FLEX_ARRAY(struct capdata_fan, cd_fan);
116 };
117};
118
119static struct wmi_driver lwmi_cd_driver;
120
121/**
122 * lwmi_cd_match() - Match rule for the master driver.
123 * @dev: Pointer to the capability data parent device.
124 * @type: Pointer to capability data type (enum lwmi_cd_type *) to match.
125 *
126 * Return: int.
127 */
128static int lwmi_cd_match(struct device *dev, void *type)
129{
130 struct lwmi_cd_priv *priv;
131
132 if (dev->driver != &lwmi_cd_driver.driver)
133 return false;
134
135 priv = dev_get_drvdata(dev);
136 return priv->list->type == *(enum lwmi_cd_type *)type;
137}
138
139/**
140 * lwmi_cd_match_add_all() - Add all match rule for the master driver.
141 * @master: Pointer to the master device.
142 * @matchptr: Pointer to the returned component_match pointer.
143 *
144 * Adds all component matches to the list stored in @matchptr for the @master
145 * device. @matchptr must be initialized to NULL.
146 */
147void lwmi_cd_match_add_all(struct device *master, struct component_match **matchptr)
148{
149 int i;
150
151 if (WARN_ON(*matchptr))
152 return;
153
154 for (i = 0; i < ARRAY_SIZE(lwmi_cd_table); i++) {
155 /* Skip sub-components. */
156 if (lwmi_cd_table[i].type == LENOVO_FAN_TEST_DATA)
157 continue;
158
159 component_match_add(master, matchptr, lwmi_cd_match,
160 (void *)&lwmi_cd_table[i].type);
161 if (IS_ERR(*matchptr))
162 return;
163 }
164}
165EXPORT_SYMBOL_NS_GPL(lwmi_cd_match_add_all, "LENOVO_WMI_CAPDATA");
166
167/**
168 * lwmi_cd_call_master_cb() - Call the master callback for the sub-component.
169 * @priv: Pointer to the capability data private data.
170 *
171 * Call the master callback and pass the sub-component list to it if the
172 * dependency chain (master <-> sub-master <-> sub-component) is complete.
173 */
174static void lwmi_cd_call_master_cb(struct lwmi_cd_priv *priv)
175{
176 struct cd_list *sub_component_list = priv->sub_master->sub_component_list;
177
178 /*
179 * Call the callback only if the dependency chain is ready:
180 * - Binding between master and sub-master: fills master_dev and master_cb
181 * - Binding between sub-master and sub-component: fills sub_component_list
182 *
183 * If a binding has been unbound before the other binding is bound, the
184 * corresponding members filled by the former are guaranteed to be cleared.
185 *
186 * This function is only called in bind callbacks, and the component
187 * framework guarantees bind/unbind callbacks may never execute
188 * simultaneously, which implies that it's impossible to have a race
189 * condition.
190 *
191 * Hence, this check is sufficient to ensure that the callback is called
192 * at most once and with the correct state, without relying on a specific
193 * sequence of binding establishment.
194 */
195 if (!sub_component_list ||
196 !priv->sub_master->master_dev ||
197 !priv->sub_master->master_cb)
198 return;
199
200 if (PTR_ERR(sub_component_list) == -ENODEV)
201 sub_component_list = NULL;
202 else if (WARN_ON(IS_ERR(sub_component_list)))
203 return;
204
205 priv->sub_master->master_cb(priv->sub_master->master_dev,
206 sub_component_list);
207
208 /*
209 * Userspace may unbind a device from its driver and bind it again
210 * through sysfs. Let's call this operation "reprobe" to distinguish it
211 * from component "rebind".
212 *
213 * When reprobing capdata00/01 or the master device, the master device
214 * is unbound from us with appropriate cleanup before we bind to it and
215 * call master_cb. Everything is fine in this case.
216 *
217 * When reprobing capdata_fan, the master device has never been unbound
218 * from us (hence no cleanup is done)[1], but we call master_cb the
219 * second time. To solve this issue, we clear master_cb and master_dev
220 * so we won't call master_cb twice while a binding is still complete.
221 *
222 * Note that we can't clear sub_component_list, otherwise reprobing
223 * capdata01 or the master device causes master_cb to be never called
224 * after we rebind to the master device.
225 *
226 * [1]: The master device does not need capdata_fan in run time, so
227 * losing capdata_fan will not break the binding to the master device.
228 */
229 priv->sub_master->master_cb = NULL;
230 priv->sub_master->master_dev = NULL;
231}
232
233/**
234 * lwmi_cd_component_bind() - Bind component to master device.
235 * @cd_dev: Pointer to the lenovo-wmi-capdata driver parent device.
236 * @om_dev: Pointer to the lenovo-wmi-other driver parent device.
237 * @data: lwmi_cd_binder object pointer used to return the capability data.
238 *
239 * On lenovo-wmi-other's master bind, provide a pointer to the local capdata
240 * list. This is used to call lwmi_cd*_get_data to look up attribute data
241 * from the lenovo-wmi-other driver.
242 *
243 * If cd_dev is a sub-master, try to call the master callback.
244 *
245 * Return: 0
246 */
247static int lwmi_cd_component_bind(struct device *cd_dev,
248 struct device *om_dev, void *data)
249{
250 struct lwmi_cd_priv *priv = dev_get_drvdata(cd_dev);
251 struct lwmi_cd_binder *binder = data;
252
253 switch (priv->list->type) {
254 case LENOVO_CAPABILITY_DATA_00:
255 binder->cd00_list = priv->list;
256
257 priv->sub_master->master_dev = om_dev;
258 priv->sub_master->master_cb = binder->cd_fan_list_cb;
259 lwmi_cd_call_master_cb(priv);
260
261 break;
262 case LENOVO_CAPABILITY_DATA_01:
263 binder->cd01_list = priv->list;
264 break;
265 default:
266 return -EINVAL;
267 }
268
269 return 0;
270}
271
272/**
273 * lwmi_cd_component_unbind() - Unbind component to master device.
274 * @cd_dev: Pointer to the lenovo-wmi-capdata driver parent device.
275 * @om_dev: Pointer to the lenovo-wmi-other driver parent device.
276 * @data: Unused.
277 *
278 * If cd_dev is a sub-master, clear the collected data from the master device to
279 * prevent the binding establishment between the sub-master and the sub-
280 * component (if it's about to happen) from calling the master callback.
281 */
282static void lwmi_cd_component_unbind(struct device *cd_dev,
283 struct device *om_dev, void *data)
284{
285 struct lwmi_cd_priv *priv = dev_get_drvdata(cd_dev);
286
287 switch (priv->list->type) {
288 case LENOVO_CAPABILITY_DATA_00:
289 priv->sub_master->master_dev = NULL;
290 priv->sub_master->master_cb = NULL;
291 return;
292 default:
293 return;
294 }
295}
296
297static const struct component_ops lwmi_cd_component_ops = {
298 .bind = lwmi_cd_component_bind,
299 .unbind = lwmi_cd_component_unbind,
300};
301
302/**
303 * lwmi_cd_sub_master_bind() - Bind sub-component of sub-master device
304 * @dev: The sub-master capdata basic device.
305 *
306 * Call component_bind_all to bind the sub-component device to the sub-master
307 * device. On success, collect the pointer to the sub-component list and try
308 * to call the master callback.
309 *
310 * Return: 0 on success, or an error code.
311 */
312static int lwmi_cd_sub_master_bind(struct device *dev)
313{
314 struct lwmi_cd_priv *priv = dev_get_drvdata(dev);
315 struct cd_list *sub_component_list;
316 int ret;
317
318 ret = component_bind_all(dev, &sub_component_list);
319 if (ret)
320 return ret;
321
322 priv->sub_master->sub_component_list = sub_component_list;
323 lwmi_cd_call_master_cb(priv);
324
325 return 0;
326}
327
328/**
329 * lwmi_cd_sub_master_unbind() - Unbind sub-component of sub-master device
330 * @dev: The sub-master capdata basic device
331 *
332 * Clear the collected pointer to the sub-component list to prevent the binding
333 * establishment between the sub-master and the sub-component (if it's about to
334 * happen) from calling the master callback. Then, call component_unbind_all to
335 * unbind the sub-component device from the sub-master device.
336 */
337static void lwmi_cd_sub_master_unbind(struct device *dev)
338{
339 struct lwmi_cd_priv *priv = dev_get_drvdata(dev);
340
341 priv->sub_master->sub_component_list = NULL;
342
343 component_unbind_all(dev, NULL);
344}
345
346static const struct component_master_ops lwmi_cd_sub_master_ops = {
347 .bind = lwmi_cd_sub_master_bind,
348 .unbind = lwmi_cd_sub_master_unbind,
349};
350
351/**
352 * lwmi_cd_sub_master_add() - Register a sub-master with its sub-component
353 * @priv: Pointer to the sub-master capdata device private data.
354 * @sub_component_type: Type of the sub-component.
355 *
356 * Match the sub-component type and register the current capdata device as a
357 * sub-master. If the given sub-component type is CD_TYPE_NONE, mark the sub-
358 * component as non-existent without registering sub-master.
359 *
360 * Return: 0 on success, or an error code.
361 */
362static int lwmi_cd_sub_master_add(struct lwmi_cd_priv *priv,
363 enum lwmi_cd_type sub_component_type)
364{
365 struct component_match *master_match = NULL;
366 int ret;
367
368 priv->sub_master = devm_kzalloc(&priv->wdev->dev, sizeof(*priv->sub_master), GFP_KERNEL);
369 if (!priv->sub_master)
370 return -ENOMEM;
371
372 if (sub_component_type == CD_TYPE_NONE) {
373 /* The master callback will be called with NULL on bind. */
374 priv->sub_master->sub_component_list = ERR_PTR(-ENODEV);
375 priv->sub_master->registered = false;
376 return 0;
377 }
378
379 /*
380 * lwmi_cd_match() needs a pointer to enum lwmi_cd_type, but on-stack
381 * data cannot be used here. Steal one from lwmi_cd_table.
382 */
383 component_match_add(&priv->wdev->dev, &master_match, lwmi_cd_match,
384 (void *)&lwmi_cd_table[sub_component_type].type);
385 if (IS_ERR(master_match))
386 return PTR_ERR(master_match);
387
388 ret = component_master_add_with_match(&priv->wdev->dev, &lwmi_cd_sub_master_ops,
389 master_match);
390 if (ret)
391 return ret;
392
393 priv->sub_master->registered = true;
394 return 0;
395}
396
397/**
398 * lwmi_cd_sub_master_del() - Unregister a sub-master if it's registered
399 * @priv: Pointer to the sub-master capdata device private data.
400 */
401static void lwmi_cd_sub_master_del(struct lwmi_cd_priv *priv)
402{
403 if (!priv->sub_master->registered)
404 return;
405
406 component_master_del(&priv->wdev->dev, &lwmi_cd_sub_master_ops);
407 priv->sub_master->registered = false;
408}
409
410/**
411 * lwmi_cd_sub_component_bind() - Bind sub-component to sub-master device.
412 * @sc_dev: Pointer to the sub-component capdata parent device.
413 * @sm_dev: Pointer to the sub-master capdata parent device.
414 * @data: Pointer used to return the capability data list pointer.
415 *
416 * On sub-master's bind, provide a pointer to the local capdata list.
417 * This is used by the sub-master to call the master callback.
418 *
419 * Return: 0
420 */
421static int lwmi_cd_sub_component_bind(struct device *sc_dev,
422 struct device *sm_dev, void *data)
423{
424 struct lwmi_cd_priv *priv = dev_get_drvdata(sc_dev);
425 struct cd_list **listp = data;
426
427 *listp = priv->list;
428
429 return 0;
430}
431
432static const struct component_ops lwmi_cd_sub_component_ops = {
433 .bind = lwmi_cd_sub_component_bind,
434};
435
436/*
437 * lwmi_cd*_get_data - Get the data of the specified attribute
438 * @list: The lenovo-wmi-capdata pointer to its cd_list struct.
439 * @attribute_id: The capdata attribute ID to be found.
440 * @output: Pointer to a capdata* struct to return the data.
441 *
442 * Retrieves the capability data struct pointer for the given
443 * attribute.
444 *
445 * Return: 0 on success, or -EINVAL.
446 */
447#define DEF_LWMI_CDXX_GET_DATA(_cdxx, _cd_type, _output_t) \
448 int lwmi_##_cdxx##_get_data(struct cd_list *list, u32 attribute_id, _output_t *output) \
449 { \
450 u8 idx; \
451 \
452 if (WARN_ON(list->type != _cd_type)) \
453 return -EINVAL; \
454 \
455 guard(mutex)(&list->list_mutex); \
456 for (idx = 0; idx < list->count; idx++) { \
457 if (list->_cdxx[idx].id != attribute_id) \
458 continue; \
459 memcpy(output, &list->_cdxx[idx], sizeof(list->_cdxx[idx])); \
460 return 0; \
461 } \
462 return -EINVAL; \
463 }
464
465DEF_LWMI_CDXX_GET_DATA(cd00, LENOVO_CAPABILITY_DATA_00, struct capdata00);
466EXPORT_SYMBOL_NS_GPL(lwmi_cd00_get_data, "LENOVO_WMI_CAPDATA");
467
468DEF_LWMI_CDXX_GET_DATA(cd01, LENOVO_CAPABILITY_DATA_01, struct capdata01);
469EXPORT_SYMBOL_NS_GPL(lwmi_cd01_get_data, "LENOVO_WMI_CAPDATA");
470
471DEF_LWMI_CDXX_GET_DATA(cd_fan, LENOVO_FAN_TEST_DATA, struct capdata_fan);
472EXPORT_SYMBOL_NS_GPL(lwmi_cd_fan_get_data, "LENOVO_WMI_CAPDATA");
473
474/**
475 * lwmi_cd_cache() - Cache all WMI data block information
476 * @priv: lenovo-wmi-capdata driver data.
477 *
478 * Loop through each WMI data block and cache the data.
479 *
480 * Return: 0 on success, or an error.
481 */
482static int lwmi_cd_cache(struct lwmi_cd_priv *priv)
483{
484 size_t size;
485 int idx;
486 void *p;
487
488 switch (priv->list->type) {
489 case LENOVO_CAPABILITY_DATA_00:
490 p = &priv->list->cd00[0];
491 size = sizeof(priv->list->cd00[0]);
492 break;
493 case LENOVO_CAPABILITY_DATA_01:
494 p = &priv->list->cd01[0];
495 size = sizeof(priv->list->cd01[0]);
496 break;
497 case LENOVO_FAN_TEST_DATA:
498 /* Done by lwmi_cd_alloc() => lwmi_cd_fan_list_alloc_cache(). */
499 return 0;
500 default:
501 return -EINVAL;
502 }
503
504 guard(mutex)(&priv->list->list_mutex);
505 for (idx = 0; idx < priv->list->count; idx++, p += size) {
506 union acpi_object *ret_obj __free(kfree) = NULL;
507
508 ret_obj = wmidev_block_query(priv->wdev, idx);
509 if (!ret_obj)
510 return -ENODEV;
511
512 if (ret_obj->type != ACPI_TYPE_BUFFER ||
513 ret_obj->buffer.length < size)
514 continue;
515
516 memcpy(p, ret_obj->buffer.pointer, size);
517 }
518
519 return 0;
520}
521
522/**
523 * lwmi_cd_fan_list_alloc_cache() - Alloc and cache Fan Test Data list
524 * @priv: lenovo-wmi-capdata driver data.
525 * @listptr: Pointer to returned cd_list pointer.
526 *
527 * Return: count of fans found, or an error.
528 */
529static int lwmi_cd_fan_list_alloc_cache(struct lwmi_cd_priv *priv, struct cd_list **listptr)
530{
531 struct cd_list *list;
532 size_t size;
533 u32 count;
534 int idx;
535
536 /* Emit unaligned access to u8 buffer with __packed. */
537 struct cd_fan_block {
538 u32 nr;
539 u32 data[]; /* id[nr], max_rpm[nr], min_rpm[nr] */
540 } __packed * block;
541
542 union acpi_object *ret_obj __free(kfree) = wmidev_block_query(priv->wdev, 0);
543 if (!ret_obj)
544 return -ENODEV;
545
546 if (ret_obj->type == ACPI_TYPE_BUFFER) {
547 block = (struct cd_fan_block *)ret_obj->buffer.pointer;
548 size = ret_obj->buffer.length;
549
550 count = size >= sizeof(*block) ? block->nr : 0;
551 if (size < struct_size(block, data, count * 3)) {
552 dev_warn(&priv->wdev->dev,
553 "incomplete fan test data block: %zu < %zu, ignoring\n",
554 size, struct_size(block, data, count * 3));
555 count = 0;
556 } else if (count > U8_MAX) {
557 dev_warn(&priv->wdev->dev,
558 "too many fans reported: %u > %u, truncating\n",
559 count, U8_MAX);
560 count = U8_MAX;
561 }
562 } else {
563 /*
564 * This is usually caused by a dummy ACPI method. Do not return an error
565 * as failing to probe this device will result in sub-master device being
566 * unbound. This behavior aligns with lwmi_cd_cache().
567 */
568 count = 0;
569 }
570
571 list = devm_kzalloc(&priv->wdev->dev, struct_size(list, cd_fan, count), GFP_KERNEL);
572 if (!list)
573 return -ENOMEM;
574
575 for (idx = 0; idx < count; idx++) {
576 /* Do not calculate array index using count, as it may be truncated. */
577 list->cd_fan[idx] = (struct capdata_fan) {
578 .id = block->data[idx],
579 .max_rpm = block->data[idx + block->nr],
580 .min_rpm = block->data[idx + (2 * block->nr)],
581 };
582 }
583
584 *listptr = list;
585 return count;
586}
587
588/**
589 * lwmi_cd_alloc() - Allocate a cd_list struct in drvdata
590 * @priv: lenovo-wmi-capdata driver data.
591 * @type: The type of capability data.
592 *
593 * Allocate a cd_list struct large enough to contain data from all WMI data
594 * blocks provided by the interface.
595 *
596 * Return: 0 on success, or an error.
597 */
598static int lwmi_cd_alloc(struct lwmi_cd_priv *priv, enum lwmi_cd_type type)
599{
600 struct cd_list *list;
601 size_t list_size;
602 int count, ret;
603
604 count = wmidev_instance_count(priv->wdev);
605
606 switch (type) {
607 case LENOVO_CAPABILITY_DATA_00:
608 list_size = struct_size(list, cd00, count);
609 break;
610 case LENOVO_CAPABILITY_DATA_01:
611 list_size = struct_size(list, cd01, count);
612 break;
613 case LENOVO_FAN_TEST_DATA:
614 count = lwmi_cd_fan_list_alloc_cache(priv, &list);
615 if (count < 0)
616 return count;
617
618 goto got_list;
619 default:
620 return -EINVAL;
621 }
622
623 list = devm_kzalloc(&priv->wdev->dev, list_size, GFP_KERNEL);
624 if (!list)
625 return -ENOMEM;
626
627got_list:
628 ret = devm_mutex_init(&priv->wdev->dev, &list->list_mutex);
629 if (ret)
630 return ret;
631
632 list->type = type;
633 list->count = count;
634 priv->list = list;
635
636 return 0;
637}
638
639/**
640 * lwmi_cd_setup() - Cache all WMI data block information
641 * @priv: lenovo-wmi-capdata driver data.
642 * @type: The type of capability data.
643 *
644 * Allocate a cd_list struct large enough to contain data from all WMI data
645 * blocks provided by the interface. Then loop through each data block and
646 * cache the data.
647 *
648 * Return: 0 on success, or an error code.
649 */
650static int lwmi_cd_setup(struct lwmi_cd_priv *priv, enum lwmi_cd_type type)
651{
652 int ret;
653
654 ret = lwmi_cd_alloc(priv, type);
655 if (ret)
656 return ret;
657
658 return lwmi_cd_cache(priv);
659}
660
661/**
662 * lwmi_cd01_notifier_call() - Call method for cd01 notifier.
663 * block call chain.
664 * @nb: The notifier_block registered to lenovo-wmi-events driver.
665 * @action: Unused.
666 * @data: The ACPI event.
667 *
668 * For LWMI_EVENT_THERMAL_MODE, set current_mode and notify platform_profile
669 * of a change.
670 *
671 * Return: notifier_block status.
672 */
673static int lwmi_cd01_notifier_call(struct notifier_block *nb, unsigned long action,
674 void *data)
675{
676 struct acpi_bus_event *event = data;
677 struct lwmi_cd_priv *priv;
678 int ret;
679
680 if (strcmp(event->device_class, ACPI_AC_CLASS) != 0)
681 return NOTIFY_DONE;
682
683 priv = container_of(nb, struct lwmi_cd_priv, acpi_nb);
684
685 switch (event->type) {
686 case ACPI_AC_NOTIFY_STATUS:
687 ret = lwmi_cd_cache(priv);
688 if (ret)
689 return NOTIFY_BAD;
690
691 return NOTIFY_OK;
692 default:
693 return NOTIFY_DONE;
694 }
695}
696
697/**
698 * lwmi_cd01_unregister() - Unregister the cd01 ACPI notifier_block.
699 * @data: The ACPI event notifier_block to unregister.
700 */
701static void lwmi_cd01_unregister(void *data)
702{
703 struct notifier_block *acpi_nb = data;
704
705 unregister_acpi_notifier(acpi_nb);
706}
707
708static int lwmi_cd_probe(struct wmi_device *wdev, const void *context)
709{
710 const struct lwmi_cd_info *info = context;
711 struct lwmi_cd_priv *priv;
712 int ret;
713
714 if (!info)
715 return -EINVAL;
716
717 priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
718 if (!priv)
719 return -ENOMEM;
720
721 priv->wdev = wdev;
722 dev_set_drvdata(&wdev->dev, priv);
723
724 ret = lwmi_cd_setup(priv, info->type);
725 if (ret)
726 goto out;
727
728 switch (info->type) {
729 case LENOVO_CAPABILITY_DATA_00: {
730 enum lwmi_cd_type sub_component_type = LENOVO_FAN_TEST_DATA;
731 struct capdata00 capdata00;
732
733 ret = lwmi_cd00_get_data(priv->list, LWMI_ATTR_ID_FAN_TEST, &capdata00);
734 if (ret || !(capdata00.supported & LWMI_SUPP_VALID)) {
735 dev_dbg(&wdev->dev, "capdata00 declares no fan test support\n");
736 sub_component_type = CD_TYPE_NONE;
737 }
738
739 /* Sub-master (capdata00) <-> sub-component (capdata_fan) */
740 ret = lwmi_cd_sub_master_add(priv, sub_component_type);
741 if (ret)
742 goto out;
743
744 /* Master (lenovo-wmi-other) <-> sub-master (capdata00) */
745 ret = component_add(&wdev->dev, &lwmi_cd_component_ops);
746 if (ret)
747 lwmi_cd_sub_master_del(priv);
748
749 goto out;
750 }
751 case LENOVO_CAPABILITY_DATA_01:
752 priv->acpi_nb.notifier_call = lwmi_cd01_notifier_call;
753
754 ret = register_acpi_notifier(&priv->acpi_nb);
755 if (ret)
756 goto out;
757
758 ret = devm_add_action_or_reset(&wdev->dev, lwmi_cd01_unregister,
759 &priv->acpi_nb);
760 if (ret)
761 goto out;
762
763 ret = component_add(&wdev->dev, &lwmi_cd_component_ops);
764 goto out;
765 case LENOVO_FAN_TEST_DATA:
766 ret = component_add(&wdev->dev, &lwmi_cd_sub_component_ops);
767 goto out;
768 default:
769 return -EINVAL;
770 }
771out:
772 if (ret) {
773 dev_err(&wdev->dev, "failed to register %s: %d\n",
774 info->name, ret);
775 } else {
776 dev_dbg(&wdev->dev, "registered %s with %u items\n",
777 info->name, priv->list->count);
778 }
779 return ret;
780}
781
782static void lwmi_cd_remove(struct wmi_device *wdev)
783{
784 struct lwmi_cd_priv *priv = dev_get_drvdata(&wdev->dev);
785
786 switch (priv->list->type) {
787 case LENOVO_CAPABILITY_DATA_00:
788 lwmi_cd_sub_master_del(priv);
789 fallthrough;
790 case LENOVO_CAPABILITY_DATA_01:
791 component_del(&wdev->dev, &lwmi_cd_component_ops);
792 break;
793 case LENOVO_FAN_TEST_DATA:
794 component_del(&wdev->dev, &lwmi_cd_sub_component_ops);
795 break;
796 default:
797 WARN_ON(1);
798 }
799}
800
801#define LWMI_CD_WDEV_ID(_type) \
802 .guid_string = _type##_GUID, \
803 .context = &lwmi_cd_table[_type],
804
805static const struct wmi_device_id lwmi_cd_id_table[] = {
806 { LWMI_CD_WDEV_ID(LENOVO_CAPABILITY_DATA_00) },
807 { LWMI_CD_WDEV_ID(LENOVO_CAPABILITY_DATA_01) },
808 { LWMI_CD_WDEV_ID(LENOVO_FAN_TEST_DATA) },
809 {}
810};
811
812static struct wmi_driver lwmi_cd_driver = {
813 .driver = {
814 .name = "lenovo_wmi_capdata",
815 .probe_type = PROBE_PREFER_ASYNCHRONOUS,
816 },
817 .id_table = lwmi_cd_id_table,
818 .probe = lwmi_cd_probe,
819 .remove = lwmi_cd_remove,
820 .no_singleton = true,
821};
822
823module_wmi_driver(lwmi_cd_driver);
824
825MODULE_DEVICE_TABLE(wmi, lwmi_cd_id_table);
826MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
827MODULE_AUTHOR("Rong Zhang <i@rong.moe>");
828MODULE_DESCRIPTION("Lenovo Capability Data WMI Driver");
829MODULE_LICENSE("GPL");
drivers/platform/x86/lenovo/wmi-capdata.h+65−0
@@ -0,0 +1,65 @@
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2
3/* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */
4
5#ifndef _LENOVO_WMI_CAPDATA_H_
6#define _LENOVO_WMI_CAPDATA_H_
7
8#include <linux/bits.h>
9#include <linux/types.h>
10
11#define LWMI_SUPP_VALID BIT(0)
12#define LWMI_SUPP_GET BIT(1)
13#define LWMI_SUPP_SET BIT(2)
14
15#define LWMI_ATTR_DEV_ID_MASK GENMASK(31, 24)
16#define LWMI_ATTR_FEAT_ID_MASK GENMASK(23, 16)
17#define LWMI_ATTR_MODE_ID_MASK GENMASK(15, 8)
18#define LWMI_ATTR_TYPE_ID_MASK GENMASK(7, 0)
19
20#define LWMI_DEVICE_ID_FAN 0x04
21
22struct component_match;
23struct device;
24struct cd_list;
25
26struct capdata00 {
27 u32 id;
28 u32 supported;
29 u32 default_value;
30};
31
32struct capdata01 {
33 u32 id;
34 u32 supported;
35 u32 default_value;
36 u32 step;
37 u32 min_value;
38 u32 max_value;
39};
40
41struct capdata_fan {
42 u32 id;
43 u32 min_rpm;
44 u32 max_rpm;
45};
46
47typedef void (*cd_list_cb_t)(struct device *master_dev, struct cd_list *cd_list);
48
49struct lwmi_cd_binder {
50 struct cd_list *cd00_list;
51 struct cd_list *cd01_list;
52 /*
53 * May be called during or after the bind callback.
54 * Will be called with NULL if capdata_fan does not exist.
55 * The pointer is only valid in the callback; never keep it for later use!
56 */
57 cd_list_cb_t cd_fan_list_cb;
58};
59
60void lwmi_cd_match_add_all(struct device *master, struct component_match **matchptr);
61int lwmi_cd00_get_data(struct cd_list *list, u32 attribute_id, struct capdata00 *output);
62int lwmi_cd01_get_data(struct cd_list *list, u32 attribute_id, struct capdata01 *output);
63int lwmi_cd_fan_get_data(struct cd_list *list, u32 attribute_id, struct capdata_fan *output);
64
65#endif /* !_LENOVO_WMI_CAPDATA_H_ */
drivers/platform/x86/lenovo/wmi-capdata01.c+0−302
@@ -1,302 +0,0 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Lenovo Capability Data 01 WMI Data Block driver.
4 *
5 * Lenovo Capability Data 01 provides information on tunable attributes used by
6 * the "Other Mode" WMI interface. The data includes if the attribute is
7 * supported by the hardware, the default_value, max_value, min_value, and step
8 * increment. Each attribute has multiple pages, one for each of the thermal
9 * modes managed by the Gamezone interface.
10 *
11 * Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com>
12 */
13
14#include <linux/acpi.h>
15#include <linux/cleanup.h>
16#include <linux/component.h>
17#include <linux/container_of.h>
18#include <linux/device.h>
19#include <linux/export.h>
20#include <linux/gfp_types.h>
21#include <linux/module.h>
22#include <linux/mutex.h>
23#include <linux/mutex_types.h>
24#include <linux/notifier.h>
25#include <linux/overflow.h>
26#include <linux/types.h>
27#include <linux/wmi.h>
28
29#include "wmi-capdata01.h"
30
31#define LENOVO_CAPABILITY_DATA_01_GUID "7A8F5407-CB67-4D6E-B547-39B3BE018154"
32
33#define ACPI_AC_CLASS "ac_adapter"
34#define ACPI_AC_NOTIFY_STATUS 0x80
35
36struct lwmi_cd01_priv {
37 struct notifier_block acpi_nb; /* ACPI events */
38 struct wmi_device *wdev;
39 struct cd01_list *list;
40};
41
42struct cd01_list {
43 struct mutex list_mutex; /* list R/W mutex */
44 u8 count;
45 struct capdata01 data[];
46};
47
48/**
49 * lwmi_cd01_component_bind() - Bind component to master device.
50 * @cd01_dev: Pointer to the lenovo-wmi-capdata01 driver parent device.
51 * @om_dev: Pointer to the lenovo-wmi-other driver parent device.
52 * @data: capdata01_list object pointer used to return the capability data.
53 *
54 * On lenovo-wmi-other's master bind, provide a pointer to the local capdata01
55 * list. This is used to call lwmi_cd01_get_data to look up attribute data
56 * from the lenovo-wmi-other driver.
57 *
58 * Return: 0
59 */
60static int lwmi_cd01_component_bind(struct device *cd01_dev,
61 struct device *om_dev, void *data)
62{
63 struct lwmi_cd01_priv *priv = dev_get_drvdata(cd01_dev);
64 struct cd01_list **cd01_list = data;
65
66 *cd01_list = priv->list;
67
68 return 0;
69}
70
71static const struct component_ops lwmi_cd01_component_ops = {
72 .bind = lwmi_cd01_component_bind,
73};
74
75/**
76 * lwmi_cd01_get_data - Get the data of the specified attribute
77 * @list: The lenovo-wmi-capdata01 pointer to its cd01_list struct.
78 * @attribute_id: The capdata attribute ID to be found.
79 * @output: Pointer to a capdata01 struct to return the data.
80 *
81 * Retrieves the capability data 01 struct pointer for the given
82 * attribute for its specified thermal mode.
83 *
84 * Return: 0 on success, or -EINVAL.
85 */
86int lwmi_cd01_get_data(struct cd01_list *list, u32 attribute_id, struct capdata01 *output)
87{
88 u8 idx;
89
90 guard(mutex)(&list->list_mutex);
91 for (idx = 0; idx < list->count; idx++) {
92 if (list->data[idx].id != attribute_id)
93 continue;
94 memcpy(output, &list->data[idx], sizeof(list->data[idx]));
95 return 0;
96 }
97
98 return -EINVAL;
99}
100EXPORT_SYMBOL_NS_GPL(lwmi_cd01_get_data, "LENOVO_WMI_CD01");
101
102/**
103 * lwmi_cd01_cache() - Cache all WMI data block information
104 * @priv: lenovo-wmi-capdata01 driver data.
105 *
106 * Loop through each WMI data block and cache the data.
107 *
108 * Return: 0 on success, or an error.
109 */
110static int lwmi_cd01_cache(struct lwmi_cd01_priv *priv)
111{
112 int idx;
113
114 guard(mutex)(&priv->list->list_mutex);
115 for (idx = 0; idx < priv->list->count; idx++) {
116 union acpi_object *ret_obj __free(kfree) = NULL;
117
118 ret_obj = wmidev_block_query(priv->wdev, idx);
119 if (!ret_obj)
120 return -ENODEV;
121
122 if (ret_obj->type != ACPI_TYPE_BUFFER ||
123 ret_obj->buffer.length < sizeof(priv->list->data[idx]))
124 continue;
125
126 memcpy(&priv->list->data[idx], ret_obj->buffer.pointer,
127 ret_obj->buffer.length);
128 }
129
130 return 0;
131}
132
133/**
134 * lwmi_cd01_alloc() - Allocate a cd01_list struct in drvdata
135 * @priv: lenovo-wmi-capdata01 driver data.
136 *
137 * Allocate a cd01_list struct large enough to contain data from all WMI data
138 * blocks provided by the interface.
139 *
140 * Return: 0 on success, or an error.
141 */
142static int lwmi_cd01_alloc(struct lwmi_cd01_priv *priv)
143{
144 struct cd01_list *list;
145 size_t list_size;
146 int count, ret;
147
148 count = wmidev_instance_count(priv->wdev);
149 list_size = struct_size(list, data, count);
150
151 list = devm_kzalloc(&priv->wdev->dev, list_size, GFP_KERNEL);
152 if (!list)
153 return -ENOMEM;
154
155 ret = devm_mutex_init(&priv->wdev->dev, &list->list_mutex);
156 if (ret)
157 return ret;
158
159 list->count = count;
160 priv->list = list;
161
162 return 0;
163}
164
165/**
166 * lwmi_cd01_setup() - Cache all WMI data block information
167 * @priv: lenovo-wmi-capdata01 driver data.
168 *
169 * Allocate a cd01_list struct large enough to contain data from all WMI data
170 * blocks provided by the interface. Then loop through each data block and
171 * cache the data.
172 *
173 * Return: 0 on success, or an error code.
174 */
175static int lwmi_cd01_setup(struct lwmi_cd01_priv *priv)
176{
177 int ret;
178
179 ret = lwmi_cd01_alloc(priv);
180 if (ret)
181 return ret;
182
183 return lwmi_cd01_cache(priv);
184}
185
186/**
187 * lwmi_cd01_notifier_call() - Call method for lenovo-wmi-capdata01 driver notifier.
188 * block call chain.
189 * @nb: The notifier_block registered to lenovo-wmi-events driver.
190 * @action: Unused.
191 * @data: The ACPI event.
192 *
193 * For LWMI_EVENT_THERMAL_MODE, set current_mode and notify platform_profile
194 * of a change.
195 *
196 * Return: notifier_block status.
197 */
198static int lwmi_cd01_notifier_call(struct notifier_block *nb, unsigned long action,
199 void *data)
200{
201 struct acpi_bus_event *event = data;
202 struct lwmi_cd01_priv *priv;
203 int ret;
204
205 if (strcmp(event->device_class, ACPI_AC_CLASS) != 0)
206 return NOTIFY_DONE;
207
208 priv = container_of(nb, struct lwmi_cd01_priv, acpi_nb);
209
210 switch (event->type) {
211 case ACPI_AC_NOTIFY_STATUS:
212 ret = lwmi_cd01_cache(priv);
213 if (ret)
214 return NOTIFY_BAD;
215
216 return NOTIFY_OK;
217 default:
218 return NOTIFY_DONE;
219 }
220}
221
222/**
223 * lwmi_cd01_unregister() - Unregister the cd01 ACPI notifier_block.
224 * @data: The ACPI event notifier_block to unregister.
225 */
226static void lwmi_cd01_unregister(void *data)
227{
228 struct notifier_block *acpi_nb = data;
229
230 unregister_acpi_notifier(acpi_nb);
231}
232
233static int lwmi_cd01_probe(struct wmi_device *wdev, const void *context)
234
235{
236 struct lwmi_cd01_priv *priv;
237 int ret;
238
239 priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
240 if (!priv)
241 return -ENOMEM;
242
243 priv->wdev = wdev;
244 dev_set_drvdata(&wdev->dev, priv);
245
246 ret = lwmi_cd01_setup(priv);
247 if (ret)
248 return ret;
249
250 priv->acpi_nb.notifier_call = lwmi_cd01_notifier_call;
251
252 ret = register_acpi_notifier(&priv->acpi_nb);
253 if (ret)
254 return ret;
255
256 ret = devm_add_action_or_reset(&wdev->dev, lwmi_cd01_unregister, &priv->acpi_nb);
257 if (ret)
258 return ret;
259
260 return component_add(&wdev->dev, &lwmi_cd01_component_ops);
261}
262
263static void lwmi_cd01_remove(struct wmi_device *wdev)
264{
265 component_del(&wdev->dev, &lwmi_cd01_component_ops);
266}
267
268static const struct wmi_device_id lwmi_cd01_id_table[] = {
269 { LENOVO_CAPABILITY_DATA_01_GUID, NULL },
270 {}
271};
272
273static struct wmi_driver lwmi_cd01_driver = {
274 .driver = {
275 .name = "lenovo_wmi_cd01",
276 .probe_type = PROBE_PREFER_ASYNCHRONOUS,
277 },
278 .id_table = lwmi_cd01_id_table,
279 .probe = lwmi_cd01_probe,
280 .remove = lwmi_cd01_remove,
281 .no_singleton = true,
282};
283
284/**
285 * lwmi_cd01_match() - Match rule for the master driver.
286 * @dev: Pointer to the capability data 01 parent device.
287 * @data: Unused void pointer for passing match criteria.
288 *
289 * Return: int.
290 */
291int lwmi_cd01_match(struct device *dev, void *data)
292{
293 return dev->driver == &lwmi_cd01_driver.driver;
294}
295EXPORT_SYMBOL_NS_GPL(lwmi_cd01_match, "LENOVO_WMI_CD01");
296
297module_wmi_driver(lwmi_cd01_driver);
298
299MODULE_DEVICE_TABLE(wmi, lwmi_cd01_id_table);
300MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
301MODULE_DESCRIPTION("Lenovo Capability Data 01 WMI Driver");
302MODULE_LICENSE("GPL");
drivers/platform/x86/lenovo/wmi-capdata01.h+0−25
@@ -1,25 +0,0 @@
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2
3/* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */
4
5#ifndef _LENOVO_WMI_CAPDATA01_H_
6#define _LENOVO_WMI_CAPDATA01_H_
7
8#include <linux/types.h>
9
10struct device;
11struct cd01_list;
12
13struct capdata01 {
14 u32 id;
15 u32 supported;
16 u32 default_value;
17 u32 step;
18 u32 min_value;
19 u32 max_value;
20};
21
22int lwmi_cd01_get_data(struct cd01_list *list, u32 attribute_id, struct capdata01 *output);
23int lwmi_cd01_match(struct device *dev, void *data);
24
25#endif /* !_LENOVO_WMI_CAPDATA01_H_ */
drivers/platform/x86/lenovo/wmi-helpers.c+18−3
@@ -21,6 +21,7 @@ · 20 unmodified lines
21#include <linux/errno.h>21#include <linux/errno.h>
22#include <linux/export.h>22#include <linux/export.h>
23#include <linux/module.h>23#include <linux/module.h>
24#include <linux/unaligned.h>
24#include <linux/wmi.h>25#include <linux/wmi.h>
25 26
26#include "wmi-helpers.h"27#include "wmi-helpers.h"
@@ -59,10 +60,24 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id, · 32 unmodified lines
59 if (!ret_obj)60 if (!ret_obj)
60 return -ENODATA;61 return -ENODATA;
61 62
62 if (ret_obj->type != ACPI_TYPE_INTEGER)63 switch (ret_obj->type) {
63 return -ENXIO;64 /*
65 * The ACPI method may simply return a buffer when a u32
66 * is expected. This is valid on Windows as its WMI-ACPI
67 * driver converts everything to a common buffer.
68 */
69 case ACPI_TYPE_BUFFER:
70 if (ret_obj->buffer.length < sizeof(u32))
71 return -ENXIO;
64 72
65 *retval = (u32)ret_obj->integer.value;73 *retval = get_unaligned_le32(ret_obj->buffer.pointer);
74 return 0;
75 case ACPI_TYPE_INTEGER:
76 *retval = (u32)ret_obj->integer.value;
77 return 0;
78 default:
79 return -ENXIO;
80 }
66 }81 }
67 82
68 return 0;83 return 0;
drivers/platform/x86/lenovo/wmi-other.c+507−21
@@ -14,7 +14,16 @@ · 13 unmodified lines
14 * These attributes typically don't fit anywhere else in the sysfs and are set14 * These attributes typically don't fit anywhere else in the sysfs and are set
15 * in Windows using one of Lenovo's multiple user applications.15 * in Windows using one of Lenovo's multiple user applications.
16 *16 *
17 * Additionally, this driver also exports tunable fan speed RPM to HWMON.
18 * Min/max RPM are also provided for reference.
19 *
17 * Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com>20 * Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com>
21 * - fw_attributes
22 * - binding to Capability Data 01
23 *
24 * Copyright (C) 2025 Rong Zhang <i@rong.moe>
25 * - HWMON
26 * - binding to Capability Data 00 and Fan
18 */27 */
19 28
20#include <linux/acpi.h>29#include <linux/acpi.h>
@@ -25,16 +34,18 @@ · 4 unmodified lines
25#include <linux/device.h>34#include <linux/device.h>
26#include <linux/export.h>35#include <linux/export.h>
27#include <linux/gfp_types.h>36#include <linux/gfp_types.h>
37#include <linux/hwmon.h>
28#include <linux/idr.h>38#include <linux/idr.h>
29#include <linux/kdev_t.h>39#include <linux/kdev_t.h>
30#include <linux/kobject.h>40#include <linux/kobject.h>
41#include <linux/limits.h>
31#include <linux/module.h>42#include <linux/module.h>
32#include <linux/notifier.h>43#include <linux/notifier.h>
33#include <linux/platform_profile.h>44#include <linux/platform_profile.h>
34#include <linux/types.h>45#include <linux/types.h>
35#include <linux/wmi.h>46#include <linux/wmi.h>
36 47
37#include "wmi-capdata01.h"48#include "wmi-capdata.h"
38#include "wmi-events.h"49#include "wmi-events.h"
39#include "wmi-gamezone.h"50#include "wmi-gamezone.h"
40#include "wmi-helpers.h"51#include "wmi-helpers.h"
@@ -49,17 +60,26 @@ · 8 unmodified lines
49#define LWMI_FEATURE_ID_CPU_SPL 0x0260#define LWMI_FEATURE_ID_CPU_SPL 0x02
50#define LWMI_FEATURE_ID_CPU_FPPT 0x0361#define LWMI_FEATURE_ID_CPU_FPPT 0x03
51 62
63#define LWMI_FEATURE_ID_FAN_RPM 0x03
64
52#define LWMI_TYPE_ID_NONE 0x0065#define LWMI_TYPE_ID_NONE 0x00
53 66
54#define LWMI_FEATURE_VALUE_GET 1767#define LWMI_FEATURE_VALUE_GET 17
55#define LWMI_FEATURE_VALUE_SET 1868#define LWMI_FEATURE_VALUE_SET 18
56 69
57#define LWMI_ATTR_DEV_ID_MASK GENMASK(31, 24)70#define LWMI_FAN_ID_BASE 1
58#define LWMI_ATTR_FEAT_ID_MASK GENMASK(23, 16)71#define LWMI_FAN_NR 4
59#define LWMI_ATTR_MODE_ID_MASK GENMASK(15, 8)72#define LWMI_FAN_ID(x) ((x) + LWMI_FAN_ID_BASE)
60#define LWMI_ATTR_TYPE_ID_MASK GENMASK(7, 0)73
74#define LWMI_ATTR_ID_FAN_RPM(x) \
75 (FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, LWMI_DEVICE_ID_FAN) | \
76 FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, LWMI_FEATURE_ID_FAN_RPM) | \
77 FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, LWMI_FAN_ID(x)))
78
79#define LWMI_FAN_DIV 100
61 80
62#define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other"81#define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other"
82#define LWMI_OM_HWMON_NAME "lenovo_wmi_other"
63 83
64static BLOCKING_NOTIFIER_HEAD(om_chain_head);84static BLOCKING_NOTIFIER_HEAD(om_chain_head);
65static DEFINE_IDA(lwmi_om_ida);85static DEFINE_IDA(lwmi_om_ida);
@@ -72,16 +92,459 @@ enum attribute_property { · 6 unmodified lines
72 SUPPORTED,92 SUPPORTED,
73};93};
74 94
95struct lwmi_fan_info {
96 u32 supported;
97 u32 last_target;
98 long min_rpm;
99 long max_rpm;
100};
101
75struct lwmi_om_priv {102struct lwmi_om_priv {
76 struct component_master_ops *ops;103 struct component_master_ops *ops;
77 struct cd01_list *cd01_list; /* only valid after capdata01 bind */104
105 /* only valid after capdata bind */
106 struct cd_list *cd00_list;
107 struct cd_list *cd01_list;
108
109 struct device *hwmon_dev;
78 struct device *fw_attr_dev;110 struct device *fw_attr_dev;
79 struct kset *fw_attr_kset;111 struct kset *fw_attr_kset;
80 struct notifier_block nb;112 struct notifier_block nb;
81 struct wmi_device *wdev;113 struct wmi_device *wdev;
82 int ida_id;114 int ida_id;
115
116 struct lwmi_fan_info fan_info[LWMI_FAN_NR];
117
118 struct {
119 bool capdata00_collected : 1;
120 bool capdata_fan_collected : 1;
121 } fan_flags;
83};122};
84 123
124/*
125 * Visibility of fan channels:
126 *
127 * +-------------------+---------+------------------+-----------------------+------------+
128 * | | default | +expose_all_fans | +relax_fan_constraint | +both |
129 * +-------------------+---------+------------------+-----------------------+------------+
130 * | canonical | RW | RW | RW+relaxed | RW+relaxed |
131 * +-------------------+---------+------------------+-----------------------+------------+
132 * | -capdata_fan[idx] | N | RO | N | RW+relaxed |
133 * +-------------------+---------+------------------+-----------------------+------------+
134 *
135 * Note:
136 * 1. LWMI_ATTR_ID_FAN_RPM[idx].supported is always checked before exposing a channel.
137 * 2. -capdata_fan implies -capdata_fan[idx].
138 */
139static bool expose_all_fans;
140module_param(expose_all_fans, bool, 0444);
141MODULE_PARM_DESC(expose_all_fans,
142 "This option skips some capability checks and solely relies on per-channel ones "
143 "to expose fan attributes. Use with caution.");
144
145static bool relax_fan_constraint;
146module_param(relax_fan_constraint, bool, 0444);
147MODULE_PARM_DESC(relax_fan_constraint,
148 "Do not enforce fan RPM constraint (div/min/max) "
149 "and enables fan tuning when such data is missing. "
150 "Enabling this may results in HWMON attributes being out-of-sync, "
151 "and setting a too low RPM stops the fan. Use with caution.");
152
153/* ======== HWMON (component: lenovo-wmi-capdata 00 & fan) ======== */
154
155/**
156 * lwmi_om_fan_get_set() - Get or set fan RPM value of specified fan
157 * @priv: Driver private data structure
158 * @channel: Fan channel index (0-based)
159 * @val: Pointer to value (input for set, output for get)
160 * @set: True to set value, false to get value
161 *
162 * Communicates with WMI interface to either retrieve current fan RPM
163 * or set target fan RPM.
164 *
165 * Return: 0 on success, or an error code.
166 */
167static int lwmi_om_fan_get_set(struct lwmi_om_priv *priv, int channel, u32 *val, bool set)
168{
169 struct wmi_method_args_32 args;
170 u32 method_id, retval;
171 int err;
172
173 method_id = set ? LWMI_FEATURE_VALUE_SET : LWMI_FEATURE_VALUE_GET;
174 args.arg0 = LWMI_ATTR_ID_FAN_RPM(channel);
175 args.arg1 = set ? *val : 0;
176
177 err = lwmi_dev_evaluate_int(priv->wdev, 0x0, method_id,
178 (unsigned char *)&args, sizeof(args), &retval);
179 if (err)
180 return err;
181
182 if (!set) {
183 *val = retval;
184 return 0;
185 }
186
187 /*
188 * It seems that 0 means "no error" and 1 means "done". Apparently
189 * different firmware teams have different thoughts on indicating
190 * success, so we accepts both.
191 */
192 return (retval == 0 || retval == 1) ? 0 : -EIO;
193}
194
195/**
196 * lwmi_om_hwmon_is_visible() - Determine visibility of HWMON attributes
197 * @drvdata: Driver private data
198 * @type: Sensor type
199 * @attr: Attribute identifier
200 * @channel: Channel index
201 *
202 * Determines whether an HWMON attribute should be visible in sysfs
203 * based on hardware capabilities and current configuration.
204 *
205 * Return: permission mode, or 0 if invisible.
206 */
207static umode_t lwmi_om_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
208 u32 attr, int channel)
209{
210 struct lwmi_om_priv *priv = (struct lwmi_om_priv *)drvdata;
211 bool visible = false;
212
213 if (type == hwmon_fan) {
214 if (!(priv->fan_info[channel].supported & LWMI_SUPP_VALID))
215 return 0;
216
217 switch (attr) {
218 case hwmon_fan_target:
219 if (!(priv->fan_info[channel].supported & LWMI_SUPP_SET))
220 return 0;
221
222 if (relax_fan_constraint ||
223 (priv->fan_info[channel].min_rpm >= 0 &&
224 priv->fan_info[channel].max_rpm >= 0))
225 return 0644;
226
227 /*
228 * Reaching here implies expose_all_fans is set.
229 * See lwmi_om_hwmon_add().
230 */
231 dev_warn_once(&priv->wdev->dev,
232 "fan tuning disabled due to missing RPM constraint\n");
233 return 0;
234 case hwmon_fan_div:
235 case hwmon_fan_input:
236 visible = priv->fan_info[channel].supported & LWMI_SUPP_GET;
237 break;
238 case hwmon_fan_min:
239 visible = priv->fan_info[channel].min_rpm >= 0;
240 break;
241 case hwmon_fan_max:
242 visible = priv->fan_info[channel].max_rpm >= 0;
243 break;
244 }
245 }
246
247 return visible ? 0444 : 0;
248}
249
250/**
251 * lwmi_om_hwmon_read() - Read HWMON sensor data
252 * @dev: Device pointer
253 * @type: Sensor type
254 * @attr: Attribute identifier
255 * @channel: Channel index
256 * @val: Pointer to store value
257 *
258 * Reads current sensor values from hardware through WMI interface.
259 *
260 * Return: 0 on success, or an error code.
261 */
262static int lwmi_om_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
263 u32 attr, int channel, long *val)
264{
265 struct lwmi_om_priv *priv = dev_get_drvdata(dev);
266 u32 retval = 0;
267 int err;
268
269 if (type == hwmon_fan) {
270 switch (attr) {
271 /*
272 * The EC has an internal RPM divisor (i.e., the raw register value is
273 * RPM / fanY_div). For fanY_input, the WMI method reads the register
274 * value and returns raw * fanY_div. For fanY_target, the WMI method
275 * divides the written value by fanY_div before writing it to the EC.
276 *
277 * As a result, reading fanY_input always returns a multiple of fanY_div,
278 * while writing to fanY_target loses the remainder.
279 */
280 case hwmon_fan_div:
281 *val = LWMI_FAN_DIV;
282 return 0;
283 case hwmon_fan_input:
284 err = lwmi_om_fan_get_set(priv, channel, &retval, false);
285 if (err)
286 return err;
287
288 *val = retval;
289 return 0;
290 case hwmon_fan_target:
291 *val = priv->fan_info[channel].last_target;
292 return 0;
293 case hwmon_fan_min:
294 *val = priv->fan_info[channel].min_rpm;
295 return 0;
296 case hwmon_fan_max:
297 *val = priv->fan_info[channel].max_rpm;
298 return 0;
299 }
300 }
301
302 return -EOPNOTSUPP;
303}
304
305/**
306 * lwmi_om_hwmon_write() - Write HWMON sensor data
307 * @dev: Device pointer
308 * @type: Sensor type
309 * @attr: Attribute identifier
310 * @channel: Channel index
311 * @val: Value to write
312 *
313 * Writes configuration values to hardware through WMI interface.
314 *
315 * Return: 0 on success, or an error code.
316 */
317static int lwmi_om_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
318 u32 attr, int channel, long val)
319{
320 struct lwmi_om_priv *priv = dev_get_drvdata(dev);
321 u32 raw, min_rpm, max_rpm;
322 int err;
323
324 if (type == hwmon_fan) {
325 switch (attr) {
326 case hwmon_fan_target:
327 if (relax_fan_constraint) {
328 min_rpm = 1;
329 max_rpm = U16_MAX;
330 } else {
331 min_rpm = priv->fan_info[channel].min_rpm;
332 max_rpm = priv->fan_info[channel].max_rpm;
333 }
334
335 /* 0 means "auto". */
336 if (val != 0 && (val < min_rpm || val > max_rpm))
337 return -EINVAL;
338
339 /*
340 * The effective fanY_target is always a multiple of fanY_div
341 * due to the EC's internal RPM divisor (see lwmi_om_hwmon_read).
342 *
343 * Round down the written value to the nearest multiple of fanY_div
344 * to prevent mismatch between the effective value and last_target.
345 *
346 * For relax_fan_constraint, skip this conversion as setting a
347 * sub-fanY_div value is necessary to completely stop the fan on
348 * some devices.
349 */
350 if (!relax_fan_constraint)
351 raw = val / LWMI_FAN_DIV * LWMI_FAN_DIV;
352
353 err = lwmi_om_fan_get_set(priv, channel, &raw, true);
354 if (err)
355 return err;
356
357 priv->fan_info[channel].last_target = raw;
358 return 0;
359 }
360 }
361
362 return -EOPNOTSUPP;
363}
364
365static const struct hwmon_channel_info * const lwmi_om_hwmon_info[] = {
366 /* Must match LWMI_FAN_NR. */
367 HWMON_CHANNEL_INFO(fan,
368 HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV |
369 HWMON_F_MIN | HWMON_F_MAX,
370 HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV |
371 HWMON_F_MIN | HWMON_F_MAX,
372 HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV |
373 HWMON_F_MIN | HWMON_F_MAX,
374 HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV |
375 HWMON_F_MIN | HWMON_F_MAX),
376 NULL
377};
378
379static const struct hwmon_ops lwmi_om_hwmon_ops = {
380 .is_visible = lwmi_om_hwmon_is_visible,
381 .read = lwmi_om_hwmon_read,
382 .write = lwmi_om_hwmon_write,
383};
384
385static const struct hwmon_chip_info lwmi_om_hwmon_chip_info = {
386 .ops = &lwmi_om_hwmon_ops,
387 .info = lwmi_om_hwmon_info,
388};
389
390/**
391 * lwmi_om_hwmon_add() - Register HWMON device if all info is collected
392 * @priv: Driver private data
393 */
394static void lwmi_om_hwmon_add(struct lwmi_om_priv *priv)
395{
396 int i, valid;
397
398 if (WARN_ON(priv->hwmon_dev))
399 return;
400
401 if (!priv->fan_flags.capdata00_collected || !priv->fan_flags.capdata_fan_collected) {
402 dev_dbg(&priv->wdev->dev, "HWMON registration pending (00: %d, fan: %d)\n",
403 priv->fan_flags.capdata00_collected,
404 priv->fan_flags.capdata_fan_collected);
405 return;
406 }
407
408 if (expose_all_fans)
409 dev_warn(&priv->wdev->dev, "all fans exposed. Use with caution\n");
410
411 if (relax_fan_constraint)
412 dev_warn(&priv->wdev->dev, "fan RPM constraint relaxed. Use with caution\n");
413
414 valid = 0;
415 for (i = 0; i < LWMI_FAN_NR; i++) {
416 if (!(priv->fan_info[i].supported & LWMI_SUPP_VALID))
417 continue;
418
419 valid++;
420
421 if (!expose_all_fans &&
422 (priv->fan_info[i].min_rpm < 0 || priv->fan_info[i].max_rpm < 0)) {
423 dev_dbg(&priv->wdev->dev, "missing RPM constraint for fan%d, hiding\n",
424 LWMI_FAN_ID(i));
425 priv->fan_info[i].supported = 0;
426 valid--;
427 }
428 }
429
430 if (valid == 0) {
431 dev_warn(&priv->wdev->dev,
432 "fan reporting/tuning is unsupported on this device\n");
433 return;
434 }
435
436 priv->hwmon_dev = hwmon_device_register_with_info(&priv->wdev->dev,
437 LWMI_OM_HWMON_NAME, priv,
438 &lwmi_om_hwmon_chip_info,
439 NULL);
440 if (IS_ERR(priv->hwmon_dev)) {
441 dev_warn(&priv->wdev->dev, "failed to register HWMON device: %ld\n",
442 PTR_ERR(priv->hwmon_dev));
443 priv->hwmon_dev = NULL;
444 return;
445 }
446
447 dev_dbg(&priv->wdev->dev, "registered HWMON device\n");
448}
449
450/**
451 * lwmi_om_hwmon_remove() - Unregister HWMON device
452 * @priv: Driver private data
453 *
454 * Unregisters the HWMON device if applicable.
455 */
456static void lwmi_om_hwmon_remove(struct lwmi_om_priv *priv)
457{
458 if (!priv->hwmon_dev)
459 return;
460
461 hwmon_device_unregister(priv->hwmon_dev);
462 priv->hwmon_dev = NULL;
463}
464
465/**
466 * lwmi_om_fan_info_init() - Initialzie fan info
467 * @priv: Driver private data
468 *
469 * lwmi_om_fan_info_collect_cd00() and lwmi_om_fan_info_collect_cd_fan() may be
470 * called in an arbitrary order. Hence, initializion must be done before.
471 */
472static void lwmi_om_fan_info_init(struct lwmi_om_priv *priv)
473{
474 int i;
475
476 for (i = 0; i < LWMI_FAN_NR; i++) {
477 priv->fan_info[i] = (struct lwmi_fan_info) {
478 .supported = 0,
479 /*
480 * Assume 0 on probe as the EC resets all fans to auto mode on (re)boot.
481 *
482 * Note that S0ix (s2idle) preserves the RPM target, so we don't need
483 * suspend/resume callbacks. This behavior has not been tested on S3-
484 * capable devices, but I doubt if such devices even have this interface.
485 */
486 .last_target = 0,
487 .min_rpm = -ENODATA,
488 .max_rpm = -ENODATA,
489 };
490 }
491
492 priv->fan_flags.capdata00_collected = false;
493 priv->fan_flags.capdata_fan_collected = false;
494}
495
496/**
497 * lwmi_om_fan_info_collect_cd00() - Collect fan info from capdata 00
498 * @priv: Driver private data
499 */
500static void lwmi_om_fan_info_collect_cd00(struct lwmi_om_priv *priv)
501{
502 struct capdata00 capdata00;
503 int i, err;
504
505 dev_dbg(&priv->wdev->dev, "Collecting fan info from capdata00\n");
506
507 for (i = 0; i < LWMI_FAN_NR; i++) {
508 err = lwmi_cd00_get_data(priv->cd00_list, LWMI_ATTR_ID_FAN_RPM(i), &capdata00);
509 priv->fan_info[i].supported = err ? 0 : capdata00.supported;
510 }
511
512 priv->fan_flags.capdata00_collected = true;
513 lwmi_om_hwmon_add(priv);
514}
515
516/**
517 * lwmi_om_fan_info_collect_cd_fan() - Collect fan info from capdata fan
518 * @dev: Pointer to the lenovo-wmi-other device
519 * @cd_fan_list: Pointer to the capdata fan list
520 */
521static void lwmi_om_fan_info_collect_cd_fan(struct device *dev, struct cd_list *cd_fan_list)
522{
523 struct lwmi_om_priv *priv = dev_get_drvdata(dev);
524 struct capdata_fan capdata_fan;
525 int i, err;
526
527 dev_dbg(dev, "Collecting fan info from capdata_fan\n");
528
529 if (!cd_fan_list)
530 goto out;
531
532 for (i = 0; i < LWMI_FAN_NR; i++) {
533 err = lwmi_cd_fan_get_data(cd_fan_list, LWMI_FAN_ID(i), &capdata_fan);
534 if (err)
535 continue;
536
537 priv->fan_info[i].min_rpm = capdata_fan.min_rpm;
538 priv->fan_info[i].max_rpm = capdata_fan.max_rpm;
539 }
540
541out:
542 priv->fan_flags.capdata_fan_collected = true;
543 lwmi_om_hwmon_add(priv);
544}
545
546/* ======== fw_attributes (component: lenovo-wmi-capdata 01) ======== */
547
85struct tunable_attr_01 {548struct tunable_attr_01 {
86 struct capdata01 *capdata;549 struct capdata01 *capdata;
87 struct device *dev;550 struct device *dev;
@@ -561,32 +1024,45 @@ static void lwmi_om_fw_attr_remove(struct lwmi_om_priv *priv) · 473 unmodified lines
561 device_unregister(priv->fw_attr_dev);1024 device_unregister(priv->fw_attr_dev);
562}1025}
563 1026
1027/* ======== Self (master: lenovo-wmi-other) ======== */
1028
564/**1029/**
565 * lwmi_om_master_bind() - Bind all components of the other mode driver1030 * lwmi_om_master_bind() - Bind all components of the other mode driver
566 * @dev: The lenovo-wmi-other driver basic device.1031 * @dev: The lenovo-wmi-other driver basic device.
567 *1032 *
568 * Call component_bind_all to bind the lenovo-wmi-capdata01 driver to the1033 * Call component_bind_all to bind the lenovo-wmi-capdata devices to the
569 * lenovo-wmi-other master driver. On success, assign the capability data 011034 * lenovo-wmi-other master driver, with a callback to collect fan info from
570 * list pointer to the driver data struct for later access. This pointer1035 * capdata_fan. On success, assign the capability data list pointers to the
571 * is only valid while the capdata01 interface exists. Finally, register all1036 * driver data struct for later access. These pointers are only valid while the
572 * firmware attribute groups.1037 * capdata interfaces exist. Finally, collect fan info from capdata00 and
1038 * register all firmware attribute groups. Note that the HWMON device is
1039 * registered only if all fan info is collected. Hence, it is not registered
1040 * here. See lwmi_om_fan_info_collect_cd00() and
1041 * lwmi_om_fan_info_collect_cd_fan().
573 *1042 *
574 * Return: 0 on success, or an error code.1043 * Return: 0 on success, or an error code.
575 */1044 */
576static int lwmi_om_master_bind(struct device *dev)1045static int lwmi_om_master_bind(struct device *dev)
577{1046{
578 struct lwmi_om_priv *priv = dev_get_drvdata(dev);1047 struct lwmi_om_priv *priv = dev_get_drvdata(dev);
579 struct cd01_list *tmp_list;1048 struct lwmi_cd_binder binder = {
1049 .cd_fan_list_cb = lwmi_om_fan_info_collect_cd_fan,
1050 };
580 int ret;1051 int ret;
581 1052
582 ret = component_bind_all(dev, &tmp_list);1053 lwmi_om_fan_info_init(priv);
1054
1055 ret = component_bind_all(dev, &binder);
583 if (ret)1056 if (ret)
584 return ret;1057 return ret;
585 1058
586 priv->cd01_list = tmp_list;1059 priv->cd00_list = binder.cd00_list;
587 if (!priv->cd01_list)1060 priv->cd01_list = binder.cd01_list;
1061 if (!priv->cd00_list || !priv->cd01_list)
588 return -ENODEV;1062 return -ENODEV;
589 1063
1064 lwmi_om_fan_info_collect_cd00(priv);
1065
590 return lwmi_om_fw_attr_add(priv);1066 return lwmi_om_fw_attr_add(priv);
591}1067}
592 1068
@@ -594,15 +1070,18 @@ static int lwmi_om_master_bind(struct device *dev) · 1 unmodified line
594 * lwmi_om_master_unbind() - Unbind all components of the other mode driver1070 * lwmi_om_master_unbind() - Unbind all components of the other mode driver
595 * @dev: The lenovo-wmi-other driver basic device1071 * @dev: The lenovo-wmi-other driver basic device
596 *1072 *
597 * Unregister all capability data attribute groups. Then call1073 * Unregister all firmware attribute groups and the HWMON device. Then call
598 * component_unbind_all to unbind the lenovo-wmi-capdata01 driver from the1074 * component_unbind_all to unbind lenovo-wmi-capdata devices from the
599 * lenovo-wmi-other master driver. Finally, free the IDA for this device.1075 * lenovo-wmi-other master driver.
600 */1076 */
601static void lwmi_om_master_unbind(struct device *dev)1077static void lwmi_om_master_unbind(struct device *dev)
602{1078{
603 struct lwmi_om_priv *priv = dev_get_drvdata(dev);1079 struct lwmi_om_priv *priv = dev_get_drvdata(dev);
604 1080
605 lwmi_om_fw_attr_remove(priv);1081 lwmi_om_fw_attr_remove(priv);
1082
1083 lwmi_om_hwmon_remove(priv);
1084
606 component_unbind_all(dev, NULL);1085 component_unbind_all(dev, NULL);
607}1086}
608 1087
@@ -620,10 +1099,13 @@ static int lwmi_other_probe(struct wmi_device *wdev, const void *context) · 11 unmodified lines
620 if (!priv)1099 if (!priv)
621 return -ENOMEM;1100 return -ENOMEM;
622 1101
1102 /* Sentinel for on-demand ida_free(). */
1103 priv->ida_id = -EIDRM;
1104
623 priv->wdev = wdev;1105 priv->wdev = wdev;
624 dev_set_drvdata(&wdev->dev, priv);1106 dev_set_drvdata(&wdev->dev, priv);
625 1107
626 component_match_add(&wdev->dev, &master_match, lwmi_cd01_match, NULL);1108 lwmi_cd_match_add_all(&wdev->dev, &master_match);
627 if (IS_ERR(master_match))1109 if (IS_ERR(master_match))
628 return PTR_ERR(master_match);1110 return PTR_ERR(master_match);
629 1111
@@ -636,7 +1118,10 @@ static void lwmi_other_remove(struct wmi_device *wdev) · 6 unmodified lines
636 struct lwmi_om_priv *priv = dev_get_drvdata(&wdev->dev);1118 struct lwmi_om_priv *priv = dev_get_drvdata(&wdev->dev);
637 1119
638 component_master_del(&wdev->dev, &lwmi_om_master_ops);1120 component_master_del(&wdev->dev, &lwmi_om_master_ops);
639 ida_free(&lwmi_om_ida, priv->ida_id);1121
1122 /* No IDA to free if the driver is never bound to its components. */
1123 if (priv->ida_id >= 0)
1124 ida_free(&lwmi_om_ida, priv->ida_id);
640}1125}
641 1126
642static const struct wmi_device_id lwmi_other_id_table[] = {1127static const struct wmi_device_id lwmi_other_id_table[] = {
@@ -657,9 +1142,10 @@ static struct wmi_driver lwmi_other_driver = { · 14 unmodified lines
657 1142
658module_wmi_driver(lwmi_other_driver);1143module_wmi_driver(lwmi_other_driver);
659 1144
660MODULE_IMPORT_NS("LENOVO_WMI_CD01");1145MODULE_IMPORT_NS("LENOVO_WMI_CAPDATA");
661MODULE_IMPORT_NS("LENOVO_WMI_HELPERS");1146MODULE_IMPORT_NS("LENOVO_WMI_HELPERS");
662MODULE_DEVICE_TABLE(wmi, lwmi_other_id_table);1147MODULE_DEVICE_TABLE(wmi, lwmi_other_id_table);
663MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");1148MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
1149MODULE_AUTHOR("Rong Zhang <i@rong.moe>");
664MODULE_DESCRIPTION("Lenovo Other Mode WMI Driver");1150MODULE_DESCRIPTION("Lenovo Other Mode WMI Driver");
665MODULE_LICENSE("GPL");1151MODULE_LICENSE("GPL");
drivers/platform/x86/lenovo/yogabook.c+7−5
@@ -57,7 +57,7 @@ struct yogabook_data { · 56 unmodified lines
57 struct work_struct work;57 struct work_struct work;
58 struct led_classdev kbd_bl_led;58 struct led_classdev kbd_bl_led;
59 unsigned long flags;59 unsigned long flags;
60 uint8_t brightness;60 u8 brightness;
61};61};
62 62
63static void yogabook_work(struct work_struct *work)63static void yogabook_work(struct work_struct *work)
@@ -338,16 +338,18 @@ static int yogabook_wmi_probe(struct wmi_device *wdev, const void *context) · 274 unmodified lines
338 int r;338 int r;
339 339
340 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);340 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
341 if (data == NULL)341 if (!data)
342 return -ENOMEM;342 return -ENOMEM;
343 343
344 data->kbd_adev = acpi_dev_get_first_match_dev("GDIX1001", NULL, -1);344 data->kbd_adev = acpi_dev_get_first_match_dev("GDIX1001", NULL, -1);
345 if (!data->kbd_adev)345 if (!data->kbd_adev)
346 return dev_err_probe(dev, -ENODEV, "Cannot find the touchpad device in ACPI tables\n");346 return dev_err_probe(dev, -ENODEV,
347 "Cannot find the touchpad device in ACPI tables\n");
347 348
348 data->dig_adev = acpi_dev_get_first_match_dev("WCOM0019", NULL, -1);349 data->dig_adev = acpi_dev_get_first_match_dev("WCOM0019", NULL, -1);
349 if (!data->dig_adev) {350 if (!data->dig_adev) {
350 r = dev_err_probe(dev, -ENODEV, "Cannot find the digitizer device in ACPI tables\n");351 r = dev_err_probe(dev, -ENODEV,
352 "Cannot find the digitizer device in ACPI tables\n");
351 goto error_put_devs;353 goto error_put_devs;
352 }354 }
353 355
@@ -453,7 +455,7 @@ static int yogabook_pdev_probe(struct platform_device *pdev) · 99 unmodified lines
453 int r;455 int r;
454 456
455 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);457 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
456 if (data == NULL)458 if (!data)
457 return -ENOMEM;459 return -ENOMEM;
458 460
459 data->kbd_dev = bus_find_device_by_name(&i2c_bus_type, NULL, "i2c-goodix_ts");461 data->kbd_dev = bus_find_device_by_name(&i2c_bus_type, NULL, "i2c-goodix_ts");
drivers/platform/x86/uniwill/uniwill-acpi.c+272−30
@@ -88,6 +88,9 @@ · 87 unmodified lines
88 88
89#define EC_ADDR_GPU_TEMP 0x044F89#define EC_ADDR_GPU_TEMP 0x044F
90 90
91#define EC_ADDR_SYSTEM_ID 0x0456
92#define HAS_GPU BIT(7)
93
91#define EC_ADDR_MAIN_FAN_RPM_1 0x046494#define EC_ADDR_MAIN_FAN_RPM_1 0x0464
92 95
93#define EC_ADDR_MAIN_FAN_RPM_2 0x046596#define EC_ADDR_MAIN_FAN_RPM_2 0x0465
@@ -122,11 +125,11 @@ · 28 unmodified lines
122#define CTGP_DB_DB_ENABLE BIT(1)125#define CTGP_DB_DB_ENABLE BIT(1)
123#define CTGP_DB_CTGP_ENABLE BIT(2)126#define CTGP_DB_CTGP_ENABLE BIT(2)
124 127
125#define EC_ADDR_CTGP_OFFSET 0x0744128#define EC_ADDR_CTGP_DB_CTGP_OFFSET 0x0744
126 129
127#define EC_ADDR_TPP_OFFSET 0x0745130#define EC_ADDR_CTGP_DB_TPP_OFFSET 0x0745
128 131
129#define EC_ADDR_MAX_TGP 0x0746132#define EC_ADDR_CTGP_DB_DB_OFFSET 0x0746
130 133
131#define EC_ADDR_LIGHTBAR_AC_CTRL 0x0748134#define EC_ADDR_LIGHTBAR_AC_CTRL 0x0748
132#define LIGHTBAR_APP_EXISTS BIT(0)135#define LIGHTBAR_APP_EXISTS BIT(0)
@@ -317,11 +320,13 @@ · 184 unmodified lines
317#define UNIWILL_FEATURE_LIGHTBAR BIT(3)320#define UNIWILL_FEATURE_LIGHTBAR BIT(3)
318#define UNIWILL_FEATURE_BATTERY BIT(4)321#define UNIWILL_FEATURE_BATTERY BIT(4)
319#define UNIWILL_FEATURE_HWMON BIT(5)322#define UNIWILL_FEATURE_HWMON BIT(5)
323#define UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL BIT(6)
320 324
321struct uniwill_data {325struct uniwill_data {
322 struct device *dev;326 struct device *dev;
323 acpi_handle handle;327 acpi_handle handle;
324 struct regmap *regmap;328 struct regmap *regmap;
329 unsigned int features;
325 struct acpi_battery_hook hook;330 struct acpi_battery_hook hook;
326 unsigned int last_charge_ctrl;331 unsigned int last_charge_ctrl;
327 struct mutex battery_lock; /* Protects the list of currently registered batteries */332 struct mutex battery_lock; /* Protects the list of currently registered batteries */
@@ -341,12 +346,21 @@ struct uniwill_battery_entry { · 13 unmodified lines
341 struct power_supply *battery;346 struct power_supply *battery;
342};347};
343 348
349struct uniwill_device_descriptor {
350 unsigned int features;
351 /* Executed during driver probing */
352 int (*probe)(struct uniwill_data *data);
353};
354
344static bool force;355static bool force;
345module_param_unsafe(force, bool, 0);356module_param_unsafe(force, bool, 0);
346MODULE_PARM_DESC(force, "Force loading without checking for supported devices\n");357MODULE_PARM_DESC(force, "Force loading without checking for supported devices\n");
347 358
348/* Feature bitmask since the associated registers are not reliable */359/*
349static unsigned int supported_features;360 * Contains device specific data like the feature bitmap since
361 * the associated registers are not always reliable.
362 */
363static struct uniwill_device_descriptor device_descriptor __ro_after_init;
350 364
351static const char * const uniwill_temp_labels[] = {365static const char * const uniwill_temp_labels[] = {
352 "CPU",366 "CPU",
@@ -411,6 +425,12 @@ static const struct key_entry uniwill_keymap[] = { · 58 unmodified lines
411 { KE_END }425 { KE_END }
412};426};
413 427
428static inline bool uniwill_device_supports(struct uniwill_data *data,
429 unsigned int features)
430{
431 return (data->features & features) == features;
432}
433
414static int uniwill_ec_reg_write(void *context, unsigned int reg, unsigned int val)434static int uniwill_ec_reg_write(void *context, unsigned int reg, unsigned int val)
415{435{
416 union acpi_object params[2] = {436 union acpi_object params[2] = {
@@ -498,6 +518,10 @@ static bool uniwill_writeable_reg(struct device *dev, unsigned int reg) · 81 unmodified lines
498 case EC_ADDR_LIGHTBAR_BAT_RED:518 case EC_ADDR_LIGHTBAR_BAT_RED:
499 case EC_ADDR_LIGHTBAR_BAT_GREEN:519 case EC_ADDR_LIGHTBAR_BAT_GREEN:
500 case EC_ADDR_LIGHTBAR_BAT_BLUE:520 case EC_ADDR_LIGHTBAR_BAT_BLUE:
521 case EC_ADDR_CTGP_DB_CTRL:
522 case EC_ADDR_CTGP_DB_CTGP_OFFSET:
523 case EC_ADDR_CTGP_DB_TPP_OFFSET:
524 case EC_ADDR_CTGP_DB_DB_OFFSET:
501 return true;525 return true;
502 default:526 default:
503 return false;527 return false;
@@ -531,6 +555,11 @@ static bool uniwill_readable_reg(struct device *dev, unsigned int reg) · 27 unmodified lines
531 case EC_ADDR_LIGHTBAR_BAT_RED:555 case EC_ADDR_LIGHTBAR_BAT_RED:
532 case EC_ADDR_LIGHTBAR_BAT_GREEN:556 case EC_ADDR_LIGHTBAR_BAT_GREEN:
533 case EC_ADDR_LIGHTBAR_BAT_BLUE:557 case EC_ADDR_LIGHTBAR_BAT_BLUE:
558 case EC_ADDR_SYSTEM_ID:
559 case EC_ADDR_CTGP_DB_CTRL:
560 case EC_ADDR_CTGP_DB_CTGP_OFFSET:
561 case EC_ADDR_CTGP_DB_TPP_OFFSET:
562 case EC_ADDR_CTGP_DB_DB_OFFSET:
534 return true;563 return true;
535 default:564 default:
536 return false;565 return false;
@@ -786,6 +815,70 @@ static ssize_t breathing_in_suspend_show(struct device *dev, struct device_attri · 249 unmodified lines
786 815
787static DEVICE_ATTR_RW(breathing_in_suspend);816static DEVICE_ATTR_RW(breathing_in_suspend);
788 817
818static ssize_t ctgp_offset_store(struct device *dev, struct device_attribute *attr,
819 const char *buf, size_t count)
820{
821 struct uniwill_data *data = dev_get_drvdata(dev);
822 unsigned int value;
823 int ret;
824
825 ret = kstrtouint(buf, 0, &value);
826 if (ret < 0)
827 return ret;
828
829 if (value > U8_MAX)
830 return -EINVAL;
831
832 ret = regmap_write(data->regmap, EC_ADDR_CTGP_DB_CTGP_OFFSET, value);
833 if (ret < 0)
834 return ret;
835
836 return count;
837}
838
839static ssize_t ctgp_offset_show(struct device *dev, struct device_attribute *attr,
840 char *buf)
841{
842 struct uniwill_data *data = dev_get_drvdata(dev);
843 unsigned int value;
844 int ret;
845
846 ret = regmap_read(data->regmap, EC_ADDR_CTGP_DB_CTGP_OFFSET, &value);
847 if (ret < 0)
848 return ret;
849
850 return sysfs_emit(buf, "%u\n", value);
851}
852
853static DEVICE_ATTR_RW(ctgp_offset);
854
855static int uniwill_nvidia_ctgp_init(struct uniwill_data *data)
856{
857 int ret;
858
859 if (!uniwill_device_supports(data, UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL))
860 return 0;
861
862 ret = regmap_write(data->regmap, EC_ADDR_CTGP_DB_CTGP_OFFSET, 0);
863 if (ret < 0)
864 return ret;
865
866 ret = regmap_write(data->regmap, EC_ADDR_CTGP_DB_TPP_OFFSET, 255);
867 if (ret < 0)
868 return ret;
869
870 ret = regmap_write(data->regmap, EC_ADDR_CTGP_DB_DB_OFFSET, 25);
871 if (ret < 0)
872 return ret;
873
874 ret = regmap_set_bits(data->regmap, EC_ADDR_CTGP_DB_CTRL,
875 CTGP_DB_GENERAL_ENABLE | CTGP_DB_DB_ENABLE | CTGP_DB_CTGP_ENABLE);
876 if (ret < 0)
877 return ret;
878
879 return 0;
880}
881
789static struct attribute *uniwill_attrs[] = {882static struct attribute *uniwill_attrs[] = {
790 /* Keyboard-related */883 /* Keyboard-related */
791 &dev_attr_fn_lock_toggle_enable.attr,884 &dev_attr_fn_lock_toggle_enable.attr,
@@ -794,29 +887,39 @@ static struct attribute *uniwill_attrs[] = { · 2 unmodified lines
794 /* Lightbar-related */887 /* Lightbar-related */
795 &dev_attr_rainbow_animation.attr,888 &dev_attr_rainbow_animation.attr,
796 &dev_attr_breathing_in_suspend.attr,889 &dev_attr_breathing_in_suspend.attr,
890 /* Power-management-related */
891 &dev_attr_ctgp_offset.attr,
797 NULL892 NULL
798};893};
799 894
800static umode_t uniwill_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)895static umode_t uniwill_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)
801{896{
897 struct device *dev = kobj_to_dev(kobj);
898 struct uniwill_data *data = dev_get_drvdata(dev);
899
802 if (attr == &dev_attr_fn_lock_toggle_enable.attr) {900 if (attr == &dev_attr_fn_lock_toggle_enable.attr) {
803 if (supported_features & UNIWILL_FEATURE_FN_LOCK_TOGGLE)901 if (uniwill_device_supports(data, UNIWILL_FEATURE_FN_LOCK_TOGGLE))
804 return attr->mode;902 return attr->mode;
805 }903 }
806 904
807 if (attr == &dev_attr_super_key_toggle_enable.attr) {905 if (attr == &dev_attr_super_key_toggle_enable.attr) {
808 if (supported_features & UNIWILL_FEATURE_SUPER_KEY_TOGGLE)906 if (uniwill_device_supports(data, UNIWILL_FEATURE_SUPER_KEY_TOGGLE))
809 return attr->mode;907 return attr->mode;
810 }908 }
811 909
812 if (attr == &dev_attr_touchpad_toggle_enable.attr) {910 if (attr == &dev_attr_touchpad_toggle_enable.attr) {
813 if (supported_features & UNIWILL_FEATURE_TOUCHPAD_TOGGLE)911 if (uniwill_device_supports(data, UNIWILL_FEATURE_TOUCHPAD_TOGGLE))
814 return attr->mode;912 return attr->mode;
815 }913 }
816 914
817 if (attr == &dev_attr_rainbow_animation.attr ||915 if (attr == &dev_attr_rainbow_animation.attr ||
818 attr == &dev_attr_breathing_in_suspend.attr) {916 attr == &dev_attr_breathing_in_suspend.attr) {
819 if (supported_features & UNIWILL_FEATURE_LIGHTBAR)917 if (uniwill_device_supports(data, UNIWILL_FEATURE_LIGHTBAR))
918 return attr->mode;
919 }
920
921 if (attr == &dev_attr_ctgp_offset.attr) {
922 if (uniwill_device_supports(data, UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL))
820 return attr->mode;923 return attr->mode;
821 }924 }
822 925
@@ -944,7 +1047,7 @@ static int uniwill_hwmon_init(struct uniwill_data *data) · 121 unmodified lines
944{1047{
945 struct device *hdev;1048 struct device *hdev;
946 1049
947 if (!(supported_features & UNIWILL_FEATURE_HWMON))1050 if (!uniwill_device_supports(data, UNIWILL_FEATURE_HWMON))
948 return 0;1051 return 0;
949 1052
950 hdev = devm_hwmon_device_register_with_info(data->dev, "uniwill", data,1053 hdev = devm_hwmon_device_register_with_info(data->dev, "uniwill", data,
@@ -1019,7 +1122,7 @@ static int uniwill_led_init(struct uniwill_data *data) · 68 unmodified lines
1019 unsigned int value;1122 unsigned int value;
1020 int ret;1123 int ret;
1021 1124
1022 if (!(supported_features & UNIWILL_FEATURE_LIGHTBAR))1125 if (!uniwill_device_supports(data, UNIWILL_FEATURE_LIGHTBAR))
1023 return 0;1126 return 0;
1024 1127
1025 ret = devm_mutex_init(data->dev, &data->led_lock);1128 ret = devm_mutex_init(data->dev, &data->led_lock);
@@ -1232,7 +1335,7 @@ static int uniwill_battery_init(struct uniwill_data *data) · 206 unmodified lines
1232{1335{
1233 int ret;1336 int ret;
1234 1337
1235 if (!(supported_features & UNIWILL_FEATURE_BATTERY))1338 if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY))
1236 return 0;1339 return 0;
1237 1340
1238 ret = devm_mutex_init(data->dev, &data->battery_lock);1341 ret = devm_mutex_init(data->dev, &data->battery_lock);
@@ -1361,6 +1464,19 @@ static int uniwill_probe(struct platform_device *pdev) · 122 unmodified lines
1361 if (ret < 0)1464 if (ret < 0)
1362 return ret;1465 return ret;
1363 1466
1467 data->features = device_descriptor.features;
1468
1469 /*
1470 * Some devices might need to perform some device-specific initialization steps
1471 * before the supported features are initialized. Because of this we have to call
1472 * this callback just after the EC itself was initialized.
1473 */
1474 if (device_descriptor.probe) {
1475 ret = device_descriptor.probe(data);
1476 if (ret < 0)
1477 return ret;
1478 }
1479
1364 ret = uniwill_battery_init(data);1480 ret = uniwill_battery_init(data);
1365 if (ret < 0)1481 if (ret < 0)
1366 return ret;1482 return ret;
@@ -1373,6 +1489,10 @@ static int uniwill_probe(struct platform_device *pdev) · 6 unmodified lines
1373 if (ret < 0)1489 if (ret < 0)
1374 return ret;1490 return ret;
1375 1491
1492 ret = uniwill_nvidia_ctgp_init(data);
1493 if (ret < 0)
1494 return ret;
1495
1376 return uniwill_input_init(data);1496 return uniwill_input_init(data);
1377}1497}
1378 1498
@@ -1385,7 +1505,7 @@ static void uniwill_shutdown(struct platform_device *pdev) · 6 unmodified lines
1385 1505
1386static int uniwill_suspend_keyboard(struct uniwill_data *data)1506static int uniwill_suspend_keyboard(struct uniwill_data *data)
1387{1507{
1388 if (!(supported_features & UNIWILL_FEATURE_SUPER_KEY_TOGGLE))1508 if (!uniwill_device_supports(data, UNIWILL_FEATURE_SUPER_KEY_TOGGLE))
1389 return 0;1509 return 0;
1390 1510
1391 /*1511 /*
@@ -1397,7 +1517,7 @@ static int uniwill_suspend_keyboard(struct uniwill_data *data) · 5 unmodified lines
1397 1517
1398static int uniwill_suspend_battery(struct uniwill_data *data)1518static int uniwill_suspend_battery(struct uniwill_data *data)
1399{1519{
1400 if (!(supported_features & UNIWILL_FEATURE_BATTERY))1520 if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY))
1401 return 0;1521 return 0;
1402 1522
1403 /*1523 /*
@@ -1408,6 +1528,15 @@ static int uniwill_suspend_battery(struct uniwill_data *data) · 4 unmodified lines
1408 return regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &data->last_charge_ctrl);1528 return regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &data->last_charge_ctrl);
1409}1529}
1410 1530
1531static int uniwill_suspend_nvidia_ctgp(struct uniwill_data *data)
1532{
1533 if (!uniwill_device_supports(data, UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL))
1534 return 0;
1535
1536 return regmap_clear_bits(data->regmap, EC_ADDR_CTGP_DB_CTRL,
1537 CTGP_DB_DB_ENABLE | CTGP_DB_CTGP_ENABLE);
1538}
1539
1411static int uniwill_suspend(struct device *dev)1540static int uniwill_suspend(struct device *dev)
1412{1541{
1413 struct uniwill_data *data = dev_get_drvdata(dev);1542 struct uniwill_data *data = dev_get_drvdata(dev);
@@ -1421,6 +1550,10 @@ static int uniwill_suspend(struct device *dev) · 7 unmodified lines
1421 if (ret < 0)1550 if (ret < 0)
1422 return ret;1551 return ret;
1423 1552
1553 ret = uniwill_suspend_nvidia_ctgp(data);
1554 if (ret < 0)
1555 return ret;
1556
1424 regcache_cache_only(data->regmap, true);1557 regcache_cache_only(data->regmap, true);
1425 regcache_mark_dirty(data->regmap);1558 regcache_mark_dirty(data->regmap);
1426 1559
@@ -1432,7 +1565,7 @@ static int uniwill_resume_keyboard(struct uniwill_data *data) · 5 unmodified lines
1432 unsigned int value;1565 unsigned int value;
1433 int ret;1566 int ret;
1434 1567
1435 if (!(supported_features & UNIWILL_FEATURE_SUPER_KEY_TOGGLE))1568 if (!uniwill_device_supports(data, UNIWILL_FEATURE_SUPER_KEY_TOGGLE))
1436 return 0;1569 return 0;
1437 1570
1438 ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value);1571 ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value);
@@ -1448,13 +1581,22 @@ static int uniwill_resume_keyboard(struct uniwill_data *data) · 9 unmodified lines
1448 1581
1449static int uniwill_resume_battery(struct uniwill_data *data)1582static int uniwill_resume_battery(struct uniwill_data *data)
1450{1583{
1451 if (!(supported_features & UNIWILL_FEATURE_BATTERY))1584 if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY))
1452 return 0;1585 return 0;
1453 1586
1454 return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, CHARGE_CTRL_MASK,1587 return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, CHARGE_CTRL_MASK,
1455 data->last_charge_ctrl);1588 data->last_charge_ctrl);
1456}1589}
1457 1590
1591static int uniwill_resume_nvidia_ctgp(struct uniwill_data *data)
1592{
1593 if (!uniwill_device_supports(data, UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL))
1594 return 0;
1595
1596 return regmap_set_bits(data->regmap, EC_ADDR_CTGP_DB_CTRL,
1597 CTGP_DB_DB_ENABLE | CTGP_DB_CTGP_ENABLE);
1598}
1599
1458static int uniwill_resume(struct device *dev)1600static int uniwill_resume(struct device *dev)
1459{1601{
1460 struct uniwill_data *data = dev_get_drvdata(dev);1602 struct uniwill_data *data = dev_get_drvdata(dev);
@@ -1470,7 +1612,11 @@ static int uniwill_resume(struct device *dev) · 9 unmodified lines
1470 if (ret < 0)1612 if (ret < 0)
1471 return ret;1613 return ret;
1472 1614
1473 return uniwill_resume_battery(data);1615 ret = uniwill_resume_battery(data);
1616 if (ret < 0)
1617 return ret;
1618
1619 return uniwill_resume_nvidia_ctgp(data);
1474}1620}
1475 1621
1476static DEFINE_SIMPLE_DEV_PM_OPS(uniwill_pm_ops, uniwill_suspend, uniwill_resume);1622static DEFINE_SIMPLE_DEV_PM_OPS(uniwill_pm_ops, uniwill_suspend, uniwill_resume);
@@ -1496,6 +1642,48 @@ static struct platform_driver uniwill_driver = { · 19 unmodified lines
1496 .shutdown = uniwill_shutdown,1642 .shutdown = uniwill_shutdown,
1497};1643};
1498 1644
1645static struct uniwill_device_descriptor lapac71h_descriptor __initdata = {
1646 .features = UNIWILL_FEATURE_FN_LOCK_TOGGLE |
1647 UNIWILL_FEATURE_SUPER_KEY_TOGGLE |
1648 UNIWILL_FEATURE_TOUCHPAD_TOGGLE |
1649 UNIWILL_FEATURE_BATTERY |
1650 UNIWILL_FEATURE_HWMON,
1651};
1652
1653static struct uniwill_device_descriptor lapkc71f_descriptor __initdata = {
1654 .features = UNIWILL_FEATURE_FN_LOCK_TOGGLE |
1655 UNIWILL_FEATURE_SUPER_KEY_TOGGLE |
1656 UNIWILL_FEATURE_TOUCHPAD_TOGGLE |
1657 UNIWILL_FEATURE_LIGHTBAR |
1658 UNIWILL_FEATURE_BATTERY |
1659 UNIWILL_FEATURE_HWMON,
1660};
1661
1662static int phxarx1_phxaqf1_probe(struct uniwill_data *data)
1663{
1664 unsigned int value;
1665 int ret;
1666
1667 ret = regmap_read(data->regmap, EC_ADDR_SYSTEM_ID, &value);
1668 if (ret < 0)
1669 return ret;
1670
1671 if (value & HAS_GPU)
1672 data->features |= UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL;
1673
1674 return 0;
1675};
1676
1677static struct uniwill_device_descriptor phxarx1_phxaqf1_descriptor __initdata = {
1678 .probe = phxarx1_phxaqf1_probe,
1679};
1680
1681static struct uniwill_device_descriptor tux_featureset_1_descriptor __initdata = {
1682 .features = UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL,
1683};
1684
1685static struct uniwill_device_descriptor empty_descriptor __initdata = {};
1686
1499static const struct dmi_system_id uniwill_dmi_table[] __initconst = {1687static const struct dmi_system_id uniwill_dmi_table[] __initconst = {
1500 {1688 {
1501 .ident = "XMG FUSION 15",1689 .ident = "XMG FUSION 15",
@@ -1503,6 +1691,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1503 DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"),1691 DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"),
1504 DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71A"),1692 DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71A"),
1505 },1693 },
1694 .driver_data = &empty_descriptor,
1506 },1695 },
1507 {1696 {
1508 .ident = "XMG FUSION 15",1697 .ident = "XMG FUSION 15",
@@ -1510,6 +1699,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1510 DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"),1699 DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"),
1511 DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71B"),1700 DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71B"),
1512 },1701 },
1702 .driver_data = &empty_descriptor,
1513 },1703 },
1514 {1704 {
1515 .ident = "Intel NUC x15",1705 .ident = "Intel NUC x15",
@@ -1517,11 +1707,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1517 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"),1707 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"),
1518 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LAPAC71H"),1708 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LAPAC71H"),
1519 },1709 },
1520 .driver_data = (void *)(UNIWILL_FEATURE_FN_LOCK_TOGGLE |1710 .driver_data = &lapac71h_descriptor,
1521 UNIWILL_FEATURE_SUPER_KEY_TOGGLE |
1522 UNIWILL_FEATURE_TOUCHPAD_TOGGLE |
1523 UNIWILL_FEATURE_BATTERY |
1524 UNIWILL_FEATURE_HWMON),
1525 },1711 },
1526 {1712 {
1527 .ident = "Intel NUC x15",1713 .ident = "Intel NUC x15",
@@ -1529,12 +1715,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1529 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"),1715 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"),
1530 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LAPKC71F"),1716 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LAPKC71F"),
1531 },1717 },
1532 .driver_data = (void *)(UNIWILL_FEATURE_FN_LOCK_TOGGLE |1718 .driver_data = &lapkc71f_descriptor,
1533 UNIWILL_FEATURE_SUPER_KEY_TOGGLE |
1534 UNIWILL_FEATURE_TOUCHPAD_TOGGLE |
1535 UNIWILL_FEATURE_LIGHTBAR |
1536 UNIWILL_FEATURE_BATTERY |
1537 UNIWILL_FEATURE_HWMON),
1538 },1719 },
1539 {1720 {
1540 .ident = "TUXEDO InfinityBook Pro 14 Gen6 Intel",1721 .ident = "TUXEDO InfinityBook Pro 14 Gen6 Intel",
@@ -1542,6 +1723,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1542 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1723 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1543 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxTxX1"),1724 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxTxX1"),
1544 },1725 },
1726 .driver_data = &empty_descriptor,
1545 },1727 },
1546 {1728 {
1547 .ident = "TUXEDO InfinityBook Pro 14 Gen6 Intel",1729 .ident = "TUXEDO InfinityBook Pro 14 Gen6 Intel",
@@ -1549,6 +1731,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1549 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1731 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1550 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxTQx1"),1732 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxTQx1"),
1551 },1733 },
1734 .driver_data = &tux_featureset_1_descriptor,
1552 },1735 },
1553 {1736 {
1554 .ident = "TUXEDO InfinityBook Pro 14/16 Gen7 Intel",1737 .ident = "TUXEDO InfinityBook Pro 14/16 Gen7 Intel",
@@ -1556,6 +1739,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1556 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1739 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1557 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxARX1_PHxAQF1"),1740 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxARX1_PHxAQF1"),
1558 },1741 },
1742 .driver_data = &phxarx1_phxaqf1_descriptor,
1559 },1743 },
1560 {1744 {
1561 .ident = "TUXEDO InfinityBook Pro 16 Gen7 Intel/Commodore Omnia-Book Pro Gen 7",1745 .ident = "TUXEDO InfinityBook Pro 16 Gen7 Intel/Commodore Omnia-Book Pro Gen 7",
@@ -1563,6 +1747,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1563 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1747 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1564 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH6AG01_PH6AQ71_PH6AQI1"),1748 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH6AG01_PH6AQ71_PH6AQI1"),
1565 },1749 },
1750 .driver_data = &tux_featureset_1_descriptor,
1566 },1751 },
1567 {1752 {
1568 .ident = "TUXEDO InfinityBook Pro 14/16 Gen8 Intel/Commodore Omnia-Book Pro Gen 8",1753 .ident = "TUXEDO InfinityBook Pro 14/16 Gen8 Intel/Commodore Omnia-Book Pro Gen 8",
@@ -1570,6 +1755,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1570 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1755 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1571 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH4PRX1_PH6PRX1"),1756 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH4PRX1_PH6PRX1"),
1572 },1757 },
1758 .driver_data = &empty_descriptor,
1573 },1759 },
1574 {1760 {
1575 .ident = "TUXEDO InfinityBook Pro 14 Gen8 Intel/Commodore Omnia-Book Pro Gen 8",1761 .ident = "TUXEDO InfinityBook Pro 14 Gen8 Intel/Commodore Omnia-Book Pro Gen 8",
@@ -1577,6 +1763,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1577 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1763 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1578 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH4PG31"),1764 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH4PG31"),
1579 },1765 },
1766 .driver_data = &tux_featureset_1_descriptor,
1580 },1767 },
1581 {1768 {
1582 .ident = "TUXEDO InfinityBook Pro 16 Gen8 Intel",1769 .ident = "TUXEDO InfinityBook Pro 16 Gen8 Intel",
@@ -1584,6 +1771,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1584 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1771 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1585 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH6PG01_PH6PG71"),1772 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH6PG01_PH6PG71"),
1586 },1773 },
1774 .driver_data = &tux_featureset_1_descriptor,
1587 },1775 },
1588 {1776 {
1589 .ident = "TUXEDO InfinityBook Pro 14/15 Gen9 AMD",1777 .ident = "TUXEDO InfinityBook Pro 14/15 Gen9 AMD",
@@ -1591,6 +1779,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1591 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1779 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1592 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GXxHRXx"),1780 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GXxHRXx"),
1593 },1781 },
1782 .driver_data = &empty_descriptor,
1594 },1783 },
1595 {1784 {
1596 .ident = "TUXEDO InfinityBook Pro 14/15 Gen9 Intel/Commodore Omnia-Book 15 Gen9",1785 .ident = "TUXEDO InfinityBook Pro 14/15 Gen9 Intel/Commodore Omnia-Book 15 Gen9",
@@ -1598,6 +1787,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1598 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1787 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1599 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GXxMRXx"),1788 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GXxMRXx"),
1600 },1789 },
1790 .driver_data = &empty_descriptor,
1601 },1791 },
1602 {1792 {
1603 .ident = "TUXEDO InfinityBook Pro 14/15 Gen10 AMD",1793 .ident = "TUXEDO InfinityBook Pro 14/15 Gen10 AMD",
@@ -1605,6 +1795,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1605 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1795 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1606 DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxHP4NAx"),1796 DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxHP4NAx"),
1607 },1797 },
1798 .driver_data = &empty_descriptor,
1608 },1799 },
1609 {1800 {
1610 .ident = "TUXEDO InfinityBook Pro 14/15 Gen10 AMD",1801 .ident = "TUXEDO InfinityBook Pro 14/15 Gen10 AMD",
@@ -1612,6 +1803,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1612 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1803 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1613 DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxKK4NAx_XxSP4NAx"),1804 DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxKK4NAx_XxSP4NAx"),
1614 },1805 },
1806 .driver_data = &empty_descriptor,
1615 },1807 },
1616 {1808 {
1617 .ident = "TUXEDO InfinityBook Pro 15 Gen10 Intel",1809 .ident = "TUXEDO InfinityBook Pro 15 Gen10 Intel",
@@ -1619,6 +1811,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1619 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1811 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1620 DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxAR4NAx"),1812 DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxAR4NAx"),
1621 },1813 },
1814 .driver_data = &empty_descriptor,
1622 },1815 },
1623 {1816 {
1624 .ident = "TUXEDO InfinityBook Max 15 Gen10 AMD",1817 .ident = "TUXEDO InfinityBook Max 15 Gen10 AMD",
@@ -1626,6 +1819,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1626 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1819 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1627 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X5KK45xS_X5SP45xS"),1820 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X5KK45xS_X5SP45xS"),
1628 },1821 },
1822 .driver_data = &empty_descriptor,
1629 },1823 },
1630 {1824 {
1631 .ident = "TUXEDO InfinityBook Max 16 Gen10 AMD",1825 .ident = "TUXEDO InfinityBook Max 16 Gen10 AMD",
@@ -1633,6 +1827,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1633 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1827 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1634 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6HP45xU"),1828 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6HP45xU"),
1635 },1829 },
1830 .driver_data = &empty_descriptor,
1636 },1831 },
1637 {1832 {
1638 .ident = "TUXEDO InfinityBook Max 16 Gen10 AMD",1833 .ident = "TUXEDO InfinityBook Max 16 Gen10 AMD",
@@ -1640,6 +1835,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1640 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1835 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1641 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6KK45xU_X6SP45xU"),1836 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6KK45xU_X6SP45xU"),
1642 },1837 },
1838 .driver_data = &empty_descriptor,
1643 },1839 },
1644 {1840 {
1645 .ident = "TUXEDO InfinityBook Max 15 Gen10 Intel",1841 .ident = "TUXEDO InfinityBook Max 15 Gen10 Intel",
@@ -1647,6 +1843,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1647 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1843 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1648 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X5AR45xS"),1844 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X5AR45xS"),
1649 },1845 },
1846 .driver_data = &empty_descriptor,
1650 },1847 },
1651 {1848 {
1652 .ident = "TUXEDO InfinityBook Max 16 Gen10 Intel",1849 .ident = "TUXEDO InfinityBook Max 16 Gen10 Intel",
@@ -1654,6 +1851,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1654 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1851 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1655 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR55xU"),1852 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR55xU"),
1656 },1853 },
1854 .driver_data = &empty_descriptor,
1657 },1855 },
1658 {1856 {
1659 .ident = "TUXEDO Polaris 15 Gen1 AMD",1857 .ident = "TUXEDO Polaris 15 Gen1 AMD",
@@ -1661,6 +1859,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1661 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1859 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1662 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501A1650TI"),1860 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501A1650TI"),
1663 },1861 },
1862 .driver_data = &empty_descriptor,
1664 },1863 },
1665 {1864 {
1666 .ident = "TUXEDO Polaris 15 Gen1 AMD",1865 .ident = "TUXEDO Polaris 15 Gen1 AMD",
@@ -1668,6 +1867,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1668 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1867 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1669 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501A2060"),1868 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501A2060"),
1670 },1869 },
1870 .driver_data = &empty_descriptor,
1671 },1871 },
1672 {1872 {
1673 .ident = "TUXEDO Polaris 17 Gen1 AMD",1873 .ident = "TUXEDO Polaris 17 Gen1 AMD",
@@ -1675,6 +1875,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1675 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1875 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1676 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701A1650TI"),1876 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701A1650TI"),
1677 },1877 },
1878 .driver_data = &empty_descriptor,
1678 },1879 },
1679 {1880 {
1680 .ident = "TUXEDO Polaris 17 Gen1 AMD",1881 .ident = "TUXEDO Polaris 17 Gen1 AMD",
@@ -1682,6 +1883,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1682 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1883 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1683 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701A2060"),1884 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701A2060"),
1684 },1885 },
1886 .driver_data = &empty_descriptor,
1685 },1887 },
1686 {1888 {
1687 .ident = "TUXEDO Polaris 15 Gen1 Intel",1889 .ident = "TUXEDO Polaris 15 Gen1 Intel",
@@ -1689,6 +1891,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1689 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1891 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1690 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501I1650TI"),1892 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501I1650TI"),
1691 },1893 },
1894 .driver_data = &empty_descriptor,
1692 },1895 },
1693 {1896 {
1694 .ident = "TUXEDO Polaris 15 Gen1 Intel",1897 .ident = "TUXEDO Polaris 15 Gen1 Intel",
@@ -1696,6 +1899,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1696 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1899 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1697 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501I2060"),1900 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501I2060"),
1698 },1901 },
1902 .driver_data = &empty_descriptor,
1699 },1903 },
1700 {1904 {
1701 .ident = "TUXEDO Polaris 17 Gen1 Intel",1905 .ident = "TUXEDO Polaris 17 Gen1 Intel",
@@ -1703,6 +1907,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1703 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1907 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1704 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701I1650TI"),1908 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701I1650TI"),
1705 },1909 },
1910 .driver_data = &empty_descriptor,
1706 },1911 },
1707 {1912 {
1708 .ident = "TUXEDO Polaris 17 Gen1 Intel",1913 .ident = "TUXEDO Polaris 17 Gen1 Intel",
@@ -1710,6 +1915,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1710 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1915 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1711 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701I2060"),1916 DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701I2060"),
1712 },1917 },
1918 .driver_data = &empty_descriptor,
1713 },1919 },
1714 {1920 {
1715 .ident = "TUXEDO Trinity 15 Intel Gen1",1921 .ident = "TUXEDO Trinity 15 Intel Gen1",
@@ -1717,6 +1923,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1717 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1923 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1718 DMI_EXACT_MATCH(DMI_BOARD_NAME, "TRINITY1501I"),1924 DMI_EXACT_MATCH(DMI_BOARD_NAME, "TRINITY1501I"),
1719 },1925 },
1926 .driver_data = &empty_descriptor,
1720 },1927 },
1721 {1928 {
1722 .ident = "TUXEDO Trinity 17 Intel Gen1",1929 .ident = "TUXEDO Trinity 17 Intel Gen1",
@@ -1724,6 +1931,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1724 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1931 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1725 DMI_EXACT_MATCH(DMI_BOARD_NAME, "TRINITY1701I"),1932 DMI_EXACT_MATCH(DMI_BOARD_NAME, "TRINITY1701I"),
1726 },1933 },
1934 .driver_data = &empty_descriptor,
1727 },1935 },
1728 {1936 {
1729 .ident = "TUXEDO Polaris 15/17 Gen2 AMD",1937 .ident = "TUXEDO Polaris 15/17 Gen2 AMD",
@@ -1731,6 +1939,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1731 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1939 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1732 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxMGxx"),1940 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxMGxx"),
1733 },1941 },
1942 .driver_data = &tux_featureset_1_descriptor,
1734 },1943 },
1735 {1944 {
1736 .ident = "TUXEDO Polaris 15/17 Gen2 Intel",1945 .ident = "TUXEDO Polaris 15/17 Gen2 Intel",
@@ -1738,6 +1947,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1738 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1947 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1739 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxNGxx"),1948 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxNGxx"),
1740 },1949 },
1950 .driver_data = &tux_featureset_1_descriptor,
1741 },1951 },
1742 {1952 {
1743 .ident = "TUXEDO Stellaris/Polaris 15/17 Gen3 AMD",1953 .ident = "TUXEDO Stellaris/Polaris 15/17 Gen3 AMD",
@@ -1745,6 +1955,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1745 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1955 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1746 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxZGxx"),1956 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxZGxx"),
1747 },1957 },
1958 .driver_data = &tux_featureset_1_descriptor,
1748 },1959 },
1749 {1960 {
1750 .ident = "TUXEDO Stellaris/Polaris 15/17 Gen3 Intel",1961 .ident = "TUXEDO Stellaris/Polaris 15/17 Gen3 Intel",
@@ -1752,6 +1963,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1752 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1963 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1753 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxTGxx"),1964 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxTGxx"),
1754 },1965 },
1966 .driver_data = &tux_featureset_1_descriptor,
1755 },1967 },
1756 {1968 {
1757 .ident = "TUXEDO Stellaris/Polaris 15/17 Gen4 AMD",1969 .ident = "TUXEDO Stellaris/Polaris 15/17 Gen4 AMD",
@@ -1759,6 +1971,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1759 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1971 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1760 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxRGxx"),1972 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxRGxx"),
1761 },1973 },
1974 .driver_data = &tux_featureset_1_descriptor,
1762 },1975 },
1763 {1976 {
1764 .ident = "TUXEDO Stellaris 15 Gen4 Intel",1977 .ident = "TUXEDO Stellaris 15 Gen4 Intel",
@@ -1766,6 +1979,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1766 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1979 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1767 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxAGxx"),1980 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxAGxx"),
1768 },1981 },
1982 .driver_data = &tux_featureset_1_descriptor,
1769 },1983 },
1770 {1984 {
1771 .ident = "TUXEDO Polaris 15/17 Gen5 AMD",1985 .ident = "TUXEDO Polaris 15/17 Gen5 AMD",
@@ -1773,6 +1987,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1773 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1987 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1774 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxXGxx"),1988 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxXGxx"),
1775 },1989 },
1990 .driver_data = &tux_featureset_1_descriptor,
1776 },1991 },
1777 {1992 {
1778 .ident = "TUXEDO Stellaris 16 Gen5 AMD",1993 .ident = "TUXEDO Stellaris 16 Gen5 AMD",
@@ -1780,6 +1995,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1780 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),1995 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1781 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6XGxX"),1996 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6XGxX"),
1782 },1997 },
1998 .driver_data = &tux_featureset_1_descriptor,
1783 },1999 },
1784 {2000 {
1785 .ident = "TUXEDO Stellaris 16/17 Gen5 Intel/Commodore ORION Gen 5",2001 .ident = "TUXEDO Stellaris 16/17 Gen5 Intel/Commodore ORION Gen 5",
@@ -1787,6 +2003,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1787 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),2003 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1788 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxPXxx"),2004 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxPXxx"),
1789 },2005 },
2006 .driver_data = &tux_featureset_1_descriptor,
1790 },2007 },
1791 {2008 {
1792 .ident = "TUXEDO Stellaris Slim 15 Gen6 AMD",2009 .ident = "TUXEDO Stellaris Slim 15 Gen6 AMD",
@@ -1794,6 +2011,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1794 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),2011 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1795 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxHGxx"),2012 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxHGxx"),
1796 },2013 },
2014 .driver_data = &tux_featureset_1_descriptor,
1797 },2015 },
1798 {2016 {
1799 .ident = "TUXEDO Stellaris Slim 15 Gen6 Intel/Commodore ORION Slim 15 Gen6",2017 .ident = "TUXEDO Stellaris Slim 15 Gen6 Intel/Commodore ORION Slim 15 Gen6",
@@ -1801,6 +2019,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1801 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),2019 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1802 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM5IXxA"),2020 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM5IXxA"),
1803 },2021 },
2022 .driver_data = &tux_featureset_1_descriptor,
1804 },2023 },
1805 {2024 {
1806 .ident = "TUXEDO Stellaris 16 Gen6 Intel/Commodore ORION 16 Gen6",2025 .ident = "TUXEDO Stellaris 16 Gen6 Intel/Commodore ORION 16 Gen6",
@@ -1808,6 +2027,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1808 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),2027 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1809 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6IXxB_MB1"),2028 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6IXxB_MB1"),
1810 },2029 },
2030 .driver_data = &tux_featureset_1_descriptor,
1811 },2031 },
1812 {2032 {
1813 .ident = "TUXEDO Stellaris 16 Gen6 Intel/Commodore ORION 16 Gen6",2033 .ident = "TUXEDO Stellaris 16 Gen6 Intel/Commodore ORION 16 Gen6",
@@ -1815,6 +2035,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1815 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),2035 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1816 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6IXxB_MB2"),2036 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6IXxB_MB2"),
1817 },2037 },
2038 .driver_data = &tux_featureset_1_descriptor,
1818 },2039 },
1819 {2040 {
1820 .ident = "TUXEDO Stellaris 17 Gen6 Intel/Commodore ORION 17 Gen6",2041 .ident = "TUXEDO Stellaris 17 Gen6 Intel/Commodore ORION 17 Gen6",
@@ -1822,6 +2043,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1822 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),2043 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1823 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM7IXxN"),2044 DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM7IXxN"),
1824 },2045 },
2046 .driver_data = &tux_featureset_1_descriptor,
1825 },2047 },
1826 {2048 {
1827 .ident = "TUXEDO Stellaris 16 Gen7 AMD",2049 .ident = "TUXEDO Stellaris 16 Gen7 AMD",
@@ -1829,6 +2051,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1829 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),2051 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1830 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6FR5xxY"),2052 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6FR5xxY"),
1831 },2053 },
2054 .driver_data = &tux_featureset_1_descriptor,
1832 },2055 },
1833 {2056 {
1834 .ident = "TUXEDO Stellaris 16 Gen7 Intel",2057 .ident = "TUXEDO Stellaris 16 Gen7 Intel",
@@ -1836,6 +2059,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1836 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),2059 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1837 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR5xxY"),2060 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR5xxY"),
1838 },2061 },
2062 .driver_data = &tux_featureset_1_descriptor,
1839 },2063 },
1840 {2064 {
1841 .ident = "TUXEDO Stellaris 16 Gen7 Intel",2065 .ident = "TUXEDO Stellaris 16 Gen7 Intel",
@@ -1843,6 +2067,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1843 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),2067 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1844 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR5xxY_mLED"),2068 DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR5xxY_mLED"),
1845 },2069 },
2070 .driver_data = &tux_featureset_1_descriptor,
1846 },2071 },
1847 {2072 {
1848 .ident = "TUXEDO Book BA15 Gen10 AMD",2073 .ident = "TUXEDO Book BA15 Gen10 AMD",
@@ -1850,6 +2075,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1850 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),2075 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1851 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PF5PU1G"),2076 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PF5PU1G"),
1852 },2077 },
2078 .driver_data = &empty_descriptor,
1853 },2079 },
1854 {2080 {
1855 .ident = "TUXEDO Pulse 14 Gen1 AMD",2081 .ident = "TUXEDO Pulse 14 Gen1 AMD",
@@ -1857,6 +2083,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1857 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),2083 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1858 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PULSE1401"),2084 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PULSE1401"),
1859 },2085 },
2086 .driver_data = &empty_descriptor,
1860 },2087 },
1861 {2088 {
1862 .ident = "TUXEDO Pulse 15 Gen1 AMD",2089 .ident = "TUXEDO Pulse 15 Gen1 AMD",
@@ -1864,6 +2091,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1864 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),2091 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1865 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PULSE1501"),2092 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PULSE1501"),
1866 },2093 },
2094 .driver_data = &empty_descriptor,
1867 },2095 },
1868 {2096 {
1869 .ident = "TUXEDO Pulse 15 Gen2 AMD",2097 .ident = "TUXEDO Pulse 15 Gen2 AMD",
@@ -1871,6 +2099,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { · 1 unmodified line
1871 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),2099 DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
1872 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PF5LUXG"),2100 DMI_EXACT_MATCH(DMI_BOARD_NAME, "PF5LUXG"),
1873 },2101 },
2102 .driver_data = &empty_descriptor,
1874 },2103 },
1875 { }2104 { }
1876};2105};
@@ -1878,6 +2107,7 @@ MODULE_DEVICE_TABLE(dmi, uniwill_dmi_table); · 1 unmodified line
1878 2107
1879static int __init uniwill_init(void)2108static int __init uniwill_init(void)
1880{2109{
2110 const struct uniwill_device_descriptor *descriptor;
1881 const struct dmi_system_id *id;2111 const struct dmi_system_id *id;
1882 int ret;2112 int ret;
1883 2113
@@ -1887,10 +2117,22 @@ static int __init uniwill_init(void) · 3 unmodified lines
1887 return -ENODEV;2117 return -ENODEV;
1888 2118
1889 /* Assume that the device supports all features */2119 /* Assume that the device supports all features */
1890 supported_features = UINT_MAX;2120 device_descriptor.features = UINT_MAX;
1891 pr_warn("Loading on a potentially unsupported device\n");2121 pr_warn("Loading on a potentially unsupported device\n");
1892 } else {2122 } else {
1893 supported_features = (uintptr_t)id->driver_data;2123 /*
2124 * Some devices might support additional features depending on
2125 * the BIOS version/date, so we call this callback to let them
2126 * modify their device descriptor accordingly.
2127 */
2128 if (id->callback) {
2129 ret = id->callback(id);
2130 if (ret < 0)
2131 return ret;
2132 }
2133
2134 descriptor = id->driver_data;
2135 device_descriptor = *descriptor;
1894 }2136 }
1895 2137
1896 ret = platform_driver_register(&uniwill_driver);2138 ret = platform_driver_register(&uniwill_driver);
drivers/platform/x86/wmi-bmof.c+15−19
@@ -8,7 +8,6 @@ · 7 unmodified lines
8 8
9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10 10
11#include <linux/acpi.h>
12#include <linux/device.h>11#include <linux/device.h>
13#include <linux/fs.h>12#include <linux/fs.h>
14#include <linux/kernel.h>13#include <linux/kernel.h>
@@ -24,9 +23,9 @@ static ssize_t bmof_read(struct file *filp, struct kobject *kobj, const struct b · 9 unmodified lines
24 char *buf, loff_t off, size_t count)23 char *buf, loff_t off, size_t count)
25{24{
26 struct device *dev = kobj_to_dev(kobj);25 struct device *dev = kobj_to_dev(kobj);
27 union acpi_object *obj = dev_get_drvdata(dev);26 struct wmi_buffer *buffer = dev_get_drvdata(dev);
28 27
29 return memory_read_from_buffer(buf, count, &off, obj->buffer.pointer, obj->buffer.length);28 return memory_read_from_buffer(buf, count, &off, buffer->data, buffer->length);
30}29}
31 30
32static const BIN_ATTR_ADMIN_RO(bmof, 0);31static const BIN_ATTR_ADMIN_RO(bmof, 0);
@@ -39,9 +38,9 @@ static const struct bin_attribute * const bmof_attrs[] = { · 6 unmodified lines
39static size_t bmof_bin_size(struct kobject *kobj, const struct bin_attribute *attr, int n)38static size_t bmof_bin_size(struct kobject *kobj, const struct bin_attribute *attr, int n)
40{39{
41 struct device *dev = kobj_to_dev(kobj);40 struct device *dev = kobj_to_dev(kobj);
42 union acpi_object *obj = dev_get_drvdata(dev);41 struct wmi_buffer *buffer = dev_get_drvdata(dev);
43 42
44 return obj->buffer.length;43 return buffer->length;
45}44}
46 45
47static const struct attribute_group bmof_group = {46static const struct attribute_group bmof_group = {
@@ -56,30 +55,27 @@ static const struct attribute_group *bmof_groups[] = { · 8 unmodified lines
56 55
57static int wmi_bmof_probe(struct wmi_device *wdev, const void *context)56static int wmi_bmof_probe(struct wmi_device *wdev, const void *context)
58{57{
59 union acpi_object *obj;58 struct wmi_buffer *buffer;
59 int ret;
60 60
61 obj = wmidev_block_query(wdev, 0);61 buffer = devm_kzalloc(&wdev->dev, sizeof(*buffer), GFP_KERNEL);
62 if (!obj) {62 if (!buffer)
63 dev_err(&wdev->dev, "failed to read Binary MOF\n");63 return -ENOMEM;
64 return -EIO;
65 }
66 64
67 if (obj->type != ACPI_TYPE_BUFFER) {65 ret = wmidev_query_block(wdev, 0, buffer);
68 dev_err(&wdev->dev, "Binary MOF is not a buffer\n");66 if (ret < 0)
69 kfree(obj);67 return ret;
70 return -EIO;
71 }
72 68
73 dev_set_drvdata(&wdev->dev, obj);69 dev_set_drvdata(&wdev->dev, buffer);
74 70
75 return 0;71 return 0;
76}72}
77 73
78static void wmi_bmof_remove(struct wmi_device *wdev)74static void wmi_bmof_remove(struct wmi_device *wdev)
79{75{
80 union acpi_object *obj = dev_get_drvdata(&wdev->dev);76 struct wmi_buffer *buffer = dev_get_drvdata(&wdev->dev);
81 77
82 kfree(obj);78 kfree(buffer->data);
83}79}
84 80
85static const struct wmi_device_id wmi_bmof_id_table[] = {81static const struct wmi_device_id wmi_bmof_id_table[] = {
drivers/platform/x86/xiaomi-wmi.c+2−3
@@ -1,7 +1,6 @@
1// SPDX-License-Identifier: GPL-2.01// SPDX-License-Identifier: GPL-2.0
2/* WMI driver for Xiaomi Laptops */2/* WMI driver for Xiaomi Laptops */
3 3
4#include <linux/acpi.h>
5#include <linux/device.h>4#include <linux/device.h>
6#include <linux/input.h>5#include <linux/input.h>
7#include <linux/module.h>6#include <linux/module.h>
@@ -56,7 +55,7 @@ static int xiaomi_wmi_probe(struct wmi_device *wdev, const void *context) · 48 unmodified lines
56 return input_register_device(data->input_dev);55 return input_register_device(data->input_dev);
57}56}
58 57
59static void xiaomi_wmi_notify(struct wmi_device *wdev, union acpi_object *dummy)58static void xiaomi_wmi_notify(struct wmi_device *wdev, const struct wmi_buffer *dummy)
60{59{
61 struct xiaomi_wmi *data = dev_get_drvdata(&wdev->dev);60 struct xiaomi_wmi *data = dev_get_drvdata(&wdev->dev);
62 61
@@ -85,7 +84,7 @@ static struct wmi_driver xiaomi_wmi_driver = { · 22 unmodified lines
85 },84 },
86 .id_table = xiaomi_wmi_id_table,85 .id_table = xiaomi_wmi_id_table,
87 .probe = xiaomi_wmi_probe,86 .probe = xiaomi_wmi_probe,
88 .notify = xiaomi_wmi_notify,87 .notify_new = xiaomi_wmi_notify,
89 .no_singleton = true,88 .no_singleton = true,
90};89};
91module_wmi_driver(xiaomi_wmi_driver);90module_wmi_driver(xiaomi_wmi_driver);
include/linux/amd-pmf-io.h+21−0
@@ -61,5 +61,26 @@ enum laptop_placement { · 60 unmodified lines
61 LP_UNDEFINED,61 LP_UNDEFINED,
62};62};
63 63
64/**
65 * struct amd_pmf_npu_metrics: Get NPU metrics data from PMF driver
66 * @npuclk_freq: NPU clock frequency [MHz]
67 * @npu_busy: NPU busy % [0-100]
68 * @npu_power: NPU power [mW]
69 * @mpnpuclk_freq: MPNPU [MHz]
70 * @npu_reads: NPU read bandwidth [MB/sec]
71 * @npu_writes: NPU write bandwidth [MB/sec]
72 */
73struct amd_pmf_npu_metrics {
74 u16 npuclk_freq;
75 u16 npu_busy[8];
76 u16 npu_power;
77 u16 mpnpuclk_freq;
78 u16 npu_reads;
79 u16 npu_writes;
80};
81
64int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op);82int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op);
83
84/* AMD PMF and NPU interface */
85int amd_pmf_get_npu_data(struct amd_pmf_npu_metrics *info);
65#endif86#endif
include/linux/platform_data/x86/asus-wmi-leds-ids.h+0−50
@@ -1,50 +0,0 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2#ifndef __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H
3#define __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H
4
5#include <linux/dmi.h>
6#include <linux/types.h>
7
8/* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */
9#if IS_REACHABLE(CONFIG_ASUS_WMI) || IS_REACHABLE(CONFIG_HID_ASUS)
10static const struct dmi_system_id asus_use_hid_led_dmi_ids[] = {
11 {
12 .matches = {
13 DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Zephyrus"),
14 },
15 },
16 {
17 .matches = {
18 DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Strix"),
19 },
20 },
21 {
22 .matches = {
23 DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Flow"),
24 },
25 },
26 {
27 .matches = {
28 DMI_MATCH(DMI_PRODUCT_FAMILY, "ProArt P16"),
29 },
30 },
31 {
32 .matches = {
33 DMI_MATCH(DMI_BOARD_NAME, "GA403U"),
34 },
35 },
36 {
37 .matches = {
38 DMI_MATCH(DMI_BOARD_NAME, "GU605M"),
39 },
40 },
41 {
42 .matches = {
43 DMI_MATCH(DMI_BOARD_NAME, "RC71L"),
44 },
45 },
46 { },
47};
48#endif
49
50#endif /* __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H */
include/linux/platform_data/x86/asus-wmi.h+28−0
@@ -173,12 +173,29 @@ enum asus_ally_mcu_hack { · 172 unmodified lines
173 ASUS_WMI_ALLY_MCU_HACK_DISABLED,173 ASUS_WMI_ALLY_MCU_HACK_DISABLED,
174};174};
175 175
176/* Used to notify hid-asus when asus-wmi changes keyboard backlight */
177struct asus_hid_listener {
178 struct list_head list;
179 void (*brightness_set)(struct asus_hid_listener *listener, int brightness);
180};
181
182enum asus_hid_event {
183 ASUS_EV_BRTUP,
184 ASUS_EV_BRTDOWN,
185 ASUS_EV_BRTTOGGLE,
186};
187
188#define ASUS_EV_MAX_BRIGHTNESS 3
189
176#if IS_REACHABLE(CONFIG_ASUS_WMI)190#if IS_REACHABLE(CONFIG_ASUS_WMI)
177void set_ally_mcu_hack(enum asus_ally_mcu_hack status);191void set_ally_mcu_hack(enum asus_ally_mcu_hack status);
178void set_ally_mcu_powersave(bool enabled);192void set_ally_mcu_powersave(bool enabled);
179int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval);193int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval);
180int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval);194int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval);
181int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval);195int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval);
196int asus_hid_register_listener(struct asus_hid_listener *cdev);
197void asus_hid_unregister_listener(struct asus_hid_listener *cdev);
198int asus_hid_event(enum asus_hid_event event);
182#else199#else
183static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status)200static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status)
184{201{
@@ -199,6 +216,17 @@ static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, · 14 unmodified lines
199{216{
200 return -ENODEV;217 return -ENODEV;
201}218}
219static inline int asus_hid_register_listener(struct asus_hid_listener *bdev)
220{
221 return -ENODEV;
222}
223static inline void asus_hid_unregister_listener(struct asus_hid_listener *bdev)
224{
225}
226static inline int asus_hid_event(enum asus_hid_event event)
227{
228 return -ENODEV;
229}
202#endif230#endif
203 231
204#endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */232#endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */
include/linux/psp.h+1−0
@@ -18,6 +18,7 @@ · 17 unmodified lines
18 * and should include an appropriate local definition in their source file.18 * and should include an appropriate local definition in their source file.
19 */19 */
20#define PSP_CMDRESP_STS GENMASK(15, 0)20#define PSP_CMDRESP_STS GENMASK(15, 0)
21#define PSP_TEE_STS_RING_BUSY 0x0000000d /* Ring already initialized */
21#define PSP_CMDRESP_CMD GENMASK(23, 16)22#define PSP_CMDRESP_CMD GENMASK(23, 16)
22#define PSP_CMDRESP_RESERVED GENMASK(29, 24)23#define PSP_CMDRESP_RESERVED GENMASK(29, 24)
23#define PSP_CMDRESP_RECOVERY BIT(30)24#define PSP_CMDRESP_RECOVERY BIT(30)
include/linux/wmi.h+43−2
@@ -8,9 +8,11 @@ · 7 unmodified lines
8#ifndef _LINUX_WMI_H8#ifndef _LINUX_WMI_H
9#define _LINUX_WMI_H9#define _LINUX_WMI_H
10 10
11#include <linux/compiler_attributes.h>
11#include <linux/device.h>12#include <linux/device.h>
12#include <linux/acpi.h>13#include <linux/acpi.h>
13#include <linux/mod_devicetable.h>14#include <linux/mod_devicetable.h>
15#include <linux/types.h>
14 16
15/**17/**
16 * struct wmi_device - WMI device structure18 * struct wmi_device - WMI device structure
@@ -36,6 +38,42 @@ struct wmi_device { · 19 unmodified lines
36 */38 */
37#define to_wmi_device(device) container_of_const(device, struct wmi_device, dev)39#define to_wmi_device(device) container_of_const(device, struct wmi_device, dev)
38 40
41/**
42 * struct wmi_buffer - WMI data buffer
43 * @length: Buffer length in bytes
44 * @data: Pointer to the buffer content
45 *
46 * This structure is used to exchange data with the WMI driver core.
47 */
48struct wmi_buffer {
49 size_t length;
50 void *data;
51};
52
53/**
54 * struct wmi_string - WMI string representation
55 * @length: Size of @chars in bytes
56 * @chars: UTF16-LE characters with optional nul termination and padding
57 *
58 * This structure is used when exchanging string data over the WMI interface.
59 */
60struct wmi_string {
61 __le16 length;
62 __le16 chars[];
63} __packed;
64
65ssize_t wmi_string_to_utf8s(const struct wmi_string *str, u8 *dst, size_t length);
66
67ssize_t wmi_string_from_utf8s(struct wmi_string *str, size_t max_chars, const u8 *src,
68 size_t src_length);
69
70int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id,
71 const struct wmi_buffer *in, struct wmi_buffer *out);
72
73int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out);
74
75int wmidev_set_block(struct wmi_device *wdev, u8 instance, const struct wmi_buffer *in);
76
39acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 method_id,77acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 method_id,
40 const struct acpi_buffer *in, struct acpi_buffer *out);78 const struct acpi_buffer *in, struct acpi_buffer *out);
41 79
@@ -54,9 +92,11 @@ u8 wmidev_instance_count(struct wmi_device *wdev); · 12 unmodified lines
54 * @probe: Callback for device binding92 * @probe: Callback for device binding
55 * @remove: Callback for device unbinding93 * @remove: Callback for device unbinding
56 * @shutdown: Callback for device shutdown94 * @shutdown: Callback for device shutdown
57 * @notify: Callback for receiving WMI events95 * @notify: Callback for receiving WMI events (deprecated)
96 * @notify_new: Callback for receiving WMI events
58 *97 *
59 * This represents WMI drivers which handle WMI devices.98 * This represents WMI drivers which handle WMI devices. The data inside the buffer
99 * passed to the @notify_new callback is guaranteed to be aligned on a 8-byte boundary.
60 */100 */
61struct wmi_driver {101struct wmi_driver {
62 struct device_driver driver;102 struct device_driver driver;
@@ -68,6 +108,7 @@ struct wmi_driver { · 5 unmodified lines
68 void (*remove)(struct wmi_device *wdev);108 void (*remove)(struct wmi_device *wdev);
69 void (*shutdown)(struct wmi_device *wdev);109 void (*shutdown)(struct wmi_device *wdev);
70 void (*notify)(struct wmi_device *device, union acpi_object *data);110 void (*notify)(struct wmi_device *device, union acpi_object *data);
111 void (*notify_new)(struct wmi_device *device, const struct wmi_buffer *data);
71};112};
72 113
73/**114/**
tools/power/x86/intel-speed-select/Makefile+7−1
@@ -13,7 +13,13 @@ endif · 12 unmodified lines
13# Do not use make's built-in rules13# Do not use make's built-in rules
14# (this improves performance and avoids hard-to-debug behaviour);14# (this improves performance and avoids hard-to-debug behaviour);
15MAKEFLAGS += -r15MAKEFLAGS += -r
16override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include -I$(shell $(CC) -print-sysroot)/usr/include/libnl316
17NL3_CFLAGS = $(shell pkg-config --cflags libnl-3.0 2>/dev/null)
18ifeq ($(NL3_CFLAGS),)
19NL3_CFLAGS = -I/usr/include/libnl3
20endif
21
22override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include $(NL3_CFLAGS)
17override LDFLAGS += -lnl-genl-3 -lnl-323override LDFLAGS += -lnl-genl-3 -lnl-3
18 24
19ALL_TARGETS := intel-speed-select25ALL_TARGETS := intel-speed-select
tools/power/x86/intel-speed-select/isst-config.c+40−3
@@ -16,7 +16,7 @@ struct process_cmd_struct { · 15 unmodified lines
16 int arg;16 int arg;
17};17};
18 18
19static const char *version_str = "v1.24";19static const char *version_str = "v1.25";
20 20
21static const int supported_api_ver = 3;21static const int supported_api_ver = 3;
22static struct isst_if_platform_info isst_platform_info;22static struct isst_if_platform_info isst_platform_info;
@@ -80,6 +80,18 @@ struct cpu_topology { · 57 unmodified lines
80 short die_id;80 short die_id;
81};81};
82 82
83static int read_only;
84
85static void check_privilege(void)
86{
87 if (!read_only)
88 return;
89
90 isst_display_error_info_message(1, "Insufficient privileges", 0, 0);
91 isst_ctdp_display_information_end(outf);
92 exit(1);
93}
94
83FILE *get_output_file(void)95FILE *get_output_file(void)
84{96{
85 return outf;97 return outf;
@@ -950,9 +962,11 @@ int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int lev · 864 unmodified lines
950 ret = write(fd, "member", strlen("member"));962 ret = write(fd, "member", strlen("member"));
951 if (ret == -1) {963 if (ret == -1) {
952 printf("Can't update to member\n");964 printf("Can't update to member\n");
965 close(fd);
953 return ret;966 return ret;
954 }967 }
955 968
969 close(fd);
956 return 0;970 return 0;
957 }971 }
958 972
@@ -1578,6 +1592,8 @@ free_mask: · 619 unmodified lines
1578 1592
1579static void set_tdp_level(int arg)1593static void set_tdp_level(int arg)
1580{1594{
1595 check_privilege();
1596
1581 if (cmd_help) {1597 if (cmd_help) {
1582 fprintf(stderr, "Set Config TDP level\n");1598 fprintf(stderr, "Set Config TDP level\n");
1583 fprintf(stderr,1599 fprintf(stderr,
@@ -2046,6 +2062,8 @@ static void set_pbf_enable(int arg) · 462 unmodified lines
2046{2062{
2047 int enable = arg;2063 int enable = arg;
2048 2064
2065 check_privilege();
2066
2049 if (cmd_help) {2067 if (cmd_help) {
2050 if (enable) {2068 if (enable) {
2051 fprintf(stderr,2069 fprintf(stderr,
@@ -2212,6 +2230,8 @@ static void set_fact_enable(int arg) · 160 unmodified lines
2212 int i, ret, enable = arg;2230 int i, ret, enable = arg;
2213 struct isst_id id;2231 struct isst_id id;
2214 2232
2233 check_privilege();
2234
2215 if (cmd_help) {2235 if (cmd_help) {
2216 if (enable) {2236 if (enable) {
2217 fprintf(stderr,2237 fprintf(stderr,
@@ -2361,6 +2381,8 @@ static void set_clos_enable(int arg) · 143 unmodified lines
2361{2381{
2362 int enable = arg;2382 int enable = arg;
2363 2383
2384 check_privilege();
2385
2364 if (cmd_help) {2386 if (cmd_help) {
2365 if (enable) {2387 if (enable) {
2366 fprintf(stderr,2388 fprintf(stderr,
@@ -2491,6 +2513,8 @@ static void set_clos_config_for_cpu(struct isst_id *id, void *arg1, void *arg2, · 124 unmodified lines
2491 2513
2492static void set_clos_config(int arg)2514static void set_clos_config(int arg)
2493{2515{
2516 check_privilege();
2517
2494 if (cmd_help) {2518 if (cmd_help) {
2495 fprintf(stderr,2519 fprintf(stderr,
2496 "Set core-power configuration for one of the four clos ids\n");2520 "Set core-power configuration for one of the four clos ids\n");
@@ -2556,6 +2580,8 @@ static void set_clos_assoc_for_cpu(struct isst_id *id, void *arg1, void *arg2, v · 59 unmodified lines
2556 2580
2557static void set_clos_assoc(int arg)2581static void set_clos_assoc(int arg)
2558{2582{
2583 check_privilege();
2584
2559 if (cmd_help) {2585 if (cmd_help) {
2560 fprintf(stderr, "Associate a clos id to a CPU\n");2586 fprintf(stderr, "Associate a clos id to a CPU\n");
2561 fprintf(stderr,2587 fprintf(stderr,
@@ -2637,6 +2663,8 @@ static void set_turbo_mode(int arg) · 75 unmodified lines
2637 int i, disable = arg;2663 int i, disable = arg;
2638 struct isst_id id;2664 struct isst_id id;
2639 2665
2666 check_privilege();
2667
2640 if (cmd_help) {2668 if (cmd_help) {
2641 if (disable)2669 if (disable)
2642 fprintf(stderr, "Set turbo mode disable\n");2670 fprintf(stderr, "Set turbo mode disable\n");
@@ -2682,6 +2710,7 @@ static void get_set_trl(struct isst_id *id, void *arg1, void *arg2, void *arg3, · 39 unmodified lines
2682 }2710 }
2683 2711
2684 if (set) {2712 if (set) {
2713 check_privilege();
2685 ret = isst_set_trl(id, fact_trl);2714 ret = isst_set_trl(id, fact_trl);
2686 isst_display_result(id, outf, "turbo-mode", "set-trl", ret);2715 isst_display_result(id, outf, "turbo-mode", "set-trl", ret);
2687 return;2716 return;
@@ -3204,8 +3233,16 @@ static void cmdline(int argc, char **argv) · 516 unmodified lines
3204 };3233 };
3205 3234
3206 if (geteuid() != 0) {3235 if (geteuid() != 0) {
3207 fprintf(stderr, "Must run as root\n");3236 int fd;
3208 exit(0);3237
3238 fd = open(pathname, O_RDWR);
3239 if (fd < 0) {
3240 fprintf(stderr, "Must run as root\n");
3241 exit(0);
3242 }
3243 fprintf(stderr, "\nNot running as root, Only read only operations are supported\n");
3244 close(fd);
3245 read_only = 1;
3209 }3246 }
3210 3247
3211 ret = update_cpu_model();3248 ret = update_cpu_model();